npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@authomata/core

v0.0.0

Published

Async, configuration-driven authorization for Authomata.

Readme

@authomata/core

@authomata/core is an asynchronous, storage-agnostic authorization library. For most applications, create a ConfigAuthorizer from a JSON, TOML, or YAML policy document. It parses the document once and uses Map indexes for authorization lookups. Extend Engine only when policies must be loaded from a database, cache, or API.

Installation

npm install @authomata/core

Guides

  • Getting Started: first authorization walkthrough
  • Concepts: evaluation order and policy design
  • Core: policy documents, precedence, groups, and JEL
  • Custom Engines: asynchronous storage adapters and batch hooks
  • Redis: Redis-backed policy stores

Quick Start

createAuthorizer() is the standard in-memory implementation. Policy decisions are strings in configuration documents; the API returns Decision values.

import { createAuthorizer } from "@authomata/core";

const authorizer = createAuthorizer(`
defaultDecision = "deny"

[users.alice]
groups = ["editor"]

[users.alice.decisions]
"document.read" = "allow"

[users.bob]
groups = ["editor", "reviewer"]

[users.bob.decisions]
"document.write" = "deny"

[groups.editor]
priority = 100

[groups.editor.decisions]
"document.write" = "allow"

[groups.reviewer]
priority = 200

[groups.reviewer.decisions]
"document.read" = "allow"
`, { format: "toml" });

const [aliceCanWrite, bobCanRead, bobCanWrite] = await Promise.all([
	authorizer.isAllowed({
		user: "alice",
		action: "document.write",
		context: {},
	}),
	authorizer.isAllowed({
		user: "bob",
		action: "document.read",
		context: {},
	}),
	authorizer.isAllowed({
		user: "bob",
		action: "document.write",
		context: {},
	}),
]);

aliceCanWrite and bobCanRead are true. bobCanWrite is false because the user-specific decision takes precedence over the Editor group's decision. authorize() and isAllowed() are always asynchronous; isAllowed() resolves to true only when the final decision is Decision.ALLOW.

Policy Documents

Pass an object directly, or pass a string with an explicit format of "json", "toml", or "yaml".

const authorizer = createAuthorizer(
	`
defaultDecision: deny
users:
  user-123:
    decisions:
      document.read: allow
`,
	{ format: "yaml" },
);

The following YAML shows the complete document shape. The same fields are available in JSON and TOML.

defaultDecision: deny
contextRules:
  - action: document.read
    condition: [eq, [get, request.ip], 203.0.113.10]
    decision: deny
users:
  alice:
    groups: [editor]
    decisions:
      document.read: allow
  bob:
    groups: [editor, reviewer]
    decisions:
      document.write: deny
groups:
  editor:
    priority: 100
    decisions:
      document.write: allow
  reviewer:
    priority: 200
    decisions:
      document.read: allow

| Field | Description | | --- | --- | | defaultDecision | Optional fallback: allow, deny, or none. none ultimately denies the request. | | contextRules | Optional rules with a JEL condition, decision, and optional action. Omit action to apply a rule to every action. | | users | A map of user identifier to groups and an optional action-to-decision map. | | groups | A map of group identifier to priority and an action-to-decision map. |

ConfigAuthorizer creates Map indexes by action, user, and group ID. It resolves each user's group IDs when loading the document and does not mutate the supplied configuration. Call replaceConfig() to rebuild the complete cache from a new document.

Decisions and Evaluation Order

Every policy produces one of three values.

| Decision | Meaning | | --- | --- | | Decision.ALLOW | Permit the request | | Decision.DENY | Reject the request | | Decision.NONE | Make no decision at this stage |

authorize() evaluates stages in the following order. Once a stage resolves to ALLOW or DENY, later stages are not evaluated.

  1. Context rules whose JEL conditions match
  2. A user-specific decision
  3. Group decisions in priority order
  4. The default decision
  5. DENY when all preceding stages return NONE or undefined

Group priority is an integer from 0 through 65535; 0 is highest. 0..63 and 65472..65535 are reserved by convention, but remain valid.

Context Rules and JEL

The condition of a context rule is a JEL Bool and only matches when it evaluates to true. Include every value referenced by the expression in request.context.

const result = await authorizer.authorize({
	user: "user-123",
	action: "document.read",
	context: {
		request: { ip: "203.0.113.10" },
	},
});

JEL supports property access (get), logical operators, comparisons, collection checks, conditional expressions, and wildcard path segments. Validate or evaluate an expression independently with JEL.returnsBoolean() and JEL.evaluate().

Merging Same-Priority Decisions

Matching context rules and group decisions at the same priority are combined by a merge function.

| Function | Result | | --- | --- | | Merge.hasAllow | Returns ALLOW if any input is ALLOW. This is the default. | | Merge.hasDeny | Returns DENY if any input is DENY. | | Merge.majority | Returns the majority of ALLOW and DENY; ties return NONE. |

Provide a custom synchronous or asynchronous merge function through createAuthorizer or an Engine constructor.

const authorizer = createAuthorizer(policy, {
	merge: async (decisions) =>
		decisions.includes(Decision.DENY)
			? Decision.DENY
			: Decision.NONE,
});

Custom Asynchronous Storage

Extend Engine when policies do not fit the configuration document. Each retrieval hook accepts either an immediate value or a promise, while the public evaluation API always returns a promise.

Backends that can reduce round trips may override getUserAndDefaultDecisions(request), getGroupPriorities(groups, request), and getGroupDecisions(groups, request). The user/default hook may return both scopes together; its default returns undefined, preserving lazy default evaluation for existing engines. The group hooks receive a collection of groups and return a ReadonlyMap keyed by those group instances. Their default implementations call the corresponding single-scope hook concurrently. Decisions are fetched one priority band at a time to preserve short-circuit evaluation. For one-request storage snapshots, override createAuthorizationState(request); its result is passed as the optional final argument to every retrieval hook.

import {
	Decision,
	Engine,
	type AuthorizationRequest,
	type ContextRule,
} from "@authomata/core";

type User = { id: string };
type Group = { id: string; priority: number };
type Action = "document.read" | "document.write";
type RequestContext = Record<string, unknown>;

class ApplicationEngine extends Engine<User, Group, Action, RequestContext> {
	protected async getContextRules(
		_request: AuthorizationRequest<User, Action, RequestContext>,
	): Promise<Iterable<ContextRule>> {
		return [];
	}

	protected async getUserDecision(
		request: AuthorizationRequest<User, Action, RequestContext>,
	): Promise<Decision | undefined> {
		return policyStore.findUserDecision(request.user.id, request.action);
	}

	protected async getGroups(
		request: AuthorizationRequest<User, Action, RequestContext>,
	): Promise<Iterable<Group>> {
		return groupStore.findByUser(request.user.id);
	}

	protected getGroupPriority(
		group: Group,
		_request: AuthorizationRequest<User, Action, RequestContext>,
	): number {
		return group.priority;
	}

	protected async getGroupDecision(
		group: Group,
		request: AuthorizationRequest<User, Action, RequestContext>,
	): Promise<Decision | undefined> {
		return policyStore.findGroupDecision(group.id, request.action);
	}

	protected getDefaultDecision(
		_request: AuthorizationRequest<User, Action, RequestContext>,
	): Decision {
		return Decision.DENY;
	}
}

const decision = await new ApplicationEngine().authorize({
	user: { id: "user-123" },
	action: "document.read",
	context: {},
});

API

| Export | Purpose | | --- | --- | | createAuthorizer / ConfigAuthorizer | Default JSON, TOML, YAML, and Map-cached authorizer | | parseAuthorizationConfig | Parse a JSON, TOML, or YAML policy string without filesystem access | | Engine | Abstract asynchronous engine for custom storage adapters | | Decision | The ALLOW, DENY, and NONE values | | Merge / MergeFunction | Synchronous or asynchronous functions for same-priority decisions | | JEL / Bool | Context-condition validation and evaluation | | AuthorizationConfig | Typed shape for the policy document | | GROUP_PRIORITY_* | Constants for valid and reserved group-priority ranges |