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

@hex-di/guard

v0.1.1

Published

Compile-time-safe authorization for the HexDI ecosystem — permission tokens, role DAG, policy evaluator, guard adapter

Readme

@hex-di/guard

Compile-time-safe authorization for the HexDI ecosystem. Permissions and roles are branded nominal tokens, policies are serializable discriminated unions composed through algebraic combinators, and enforcement integrates directly with the HexDI dependency graph.

Features

  • Permission tokens -- branded nominal tokens created with Symbol.for() + phantom brands
  • Role DAG -- role inheritance with automatic permission flattening and cycle detection
  • Policy combinators -- algebraic composition (allOf, anyOf, not, hasPermission, hasRole, hasAttribute)
  • Synchronous evaluator -- pure evaluate() function returning Decision with full trace
  • Guard adapter -- enforcePolicy() wraps adapters with policy enforcement at resolution time
  • Serialization -- policies are JSON-serializable data, not callbacks
  • Port gate hook -- coarse-grained and fine-grained authorization at the container level
  • GxP compliance -- audit trail, electronic signatures, write-ahead log, circuit breaker

For detailed walkthroughs with architecture diagrams, see the documentation.

Installation

pnpm add @hex-di/guard

Dependencies: @hex-di/core, @hex-di/result

Quick Start

With DI Container

import { GraphBuilder } from "@hex-di/graph";
import { createContainer } from "@hex-di/runtime";
import {
  createPermission,
  createRole,
  hasPermission,
  evaluate,
  enforcePolicy,
} from "@hex-di/guard";

// Define permissions and roles
const ReadUsers = createPermission("ReadUsers");
const WriteUsers = createPermission("WriteUsers");
const AdminRole = createRole("Admin", { permissions: [ReadUsers, WriteUsers] });

// Evaluate a policy
const policy = hasPermission(ReadUsers);
const decision = evaluate(policy, subject);
// decision.granted === true | false

// Guard an adapter in the DI graph
const GuardedUserAdapter = enforcePolicy(UserAdapter, {
  policy: hasPermission(ReadUsers),
  subjectPort: SubjectProviderPort,
});

Standalone

import {
  createPermission,
  createRole,
  hasPermission,
  hasRole,
  allOf,
  evaluate,
} from "@hex-di/guard";

const Read = createPermission("Read");
const Write = createPermission("Write");
const Editor = createRole("Editor", { permissions: [Read, Write] });

const subject = {
  id: "user-1",
  permissions: new Set([Read]),
  roles: new Set([Editor]),
  attributes: {},
};

const policy = allOf(hasPermission(Read), hasRole(Editor));
const decision = evaluate(policy, subject);
// decision.granted === true
// decision.trace contains evaluation path

Permissions

Permission tokens are branded nominal values. Two permissions with the same name are identity-equal across module boundaries via Symbol.for().

import { createPermission, createPermissionGroup } from "@hex-di/guard";

// Single permission
const ReadUsers = createPermission("ReadUsers");
const WriteUsers = createPermission("WriteUsers");
const DeleteUsers = createPermission("DeleteUsers");

// Permission group for convenient bundling
const UserPermissions = createPermissionGroup("UserPermissions", {
  read: ReadUsers,
  write: WriteUsers,
  delete: DeleteUsers,
});

Roles

Roles carry a set of permissions and support DAG-based inheritance with automatic permission flattening. Circular inheritance is detected at construction time.

import { createRole } from "@hex-di/guard";

const ViewerRole = createRole("Viewer", {
  permissions: [ReadUsers],
});

const EditorRole = createRole("Editor", {
  permissions: [WriteUsers],
  inherits: [ViewerRole], // inherits ReadUsers from Viewer
});

const AdminRole = createRole("Admin", {
  permissions: [DeleteUsers],
  inherits: [EditorRole], // inherits ReadUsers + WriteUsers
});

Permissions flatten automatically -- Admin receives all permissions from the entire chain without explicit declaration.

| Role | Direct Permissions | Flattened Permissions | | ------ | ------------------ | ---------------------------------------- | | Viewer | ReadUsers | ReadUsers | | Editor | WriteUsers | WriteUsers, ReadUsers | | Admin | DeleteUsers | DeleteUsers, WriteUsers, ReadUsers |

Policies

Policies are discriminated unions composed through algebraic combinators. Every policy is serializable JSON data.

Combinators

import { hasPermission, hasRole, hasAttribute, allOf, anyOf, not } from "@hex-di/guard";

// Leaf policies
const canRead = hasPermission(ReadUsers);
const isAdmin = hasRole(AdminRole);
const isActive = hasAttribute("status", "active");

// Composed policies
const canEdit = allOf(hasPermission(WriteUsers), isActive);
const canAccess = anyOf(isAdmin, canRead);
const notSuspended = not(hasAttribute("status", "suspended"));

| Combinator | Description | | -------------------------- | ------------------------------ | | hasPermission(p) | Subject has permission p | | hasRole(r) | Subject has role r | | hasAttribute(key, value) | Subject attribute matches | | allOf(...policies) | All policies must grant | | anyOf(...policies) | At least one policy must grant | | not(policy) | Inverts the decision |

Evaluation

The evaluate() function is pure and synchronous. It returns a Decision with the grant/deny result and a full EvaluationTrace for debugging and audit.

import { evaluate } from "@hex-di/guard";

const decision = evaluate(policy, subject);

if (decision.granted) {
  // Access allowed
} else {
  // decision.trace describes why access was denied
}

For policies that require async attribute resolution:

import { evaluateAsync } from "@hex-di/guard";

const decision = await evaluateAsync(policy, subject, {
  attributeResolver: async key => fetchAttribute(key),
});

Guard Adapter

enforcePolicy() wraps an existing adapter with policy enforcement. When the guarded adapter is resolved from the container, the subject is resolved from a scoped adapter, the policy is evaluated, and denial produces an AccessDeniedError.

import { enforcePolicy } from "@hex-di/guard";

const GuardedUserAdapter = enforcePolicy(UserAdapter, {
  policy: hasPermission(ReadUsers),
  subjectPort: SubjectProviderPort,
});

// Register in graph instead of UserAdapter
const graph = GraphBuilder.create().add(GuardedUserAdapter).build();

Port Gate Hook

For coarse-grained authorization at the container level, createPortGateHook and createRoleGate restrict access to entire ports.

import { createPortGateHook, createRoleGate } from "@hex-di/guard";

// Block resolution of AdminPort unless subject has AdminRole
const gateHook = createPortGateHook({
  gates: [{ port: AdminPort, policy: hasRole(AdminRole) }],
  subjectPort: SubjectProviderPort,
});

// Or use the role-based shorthand
const roleGate = createRoleGate({
  gates: [{ port: AdminPort, role: AdminRole }],
  subjectPort: SubjectProviderPort,
});

Serialization

Policies are plain data and can be serialized to JSON, deserialized back, and explained as human-readable strings.

import { serializePolicy, deserializePolicy, explainPolicy } from "@hex-di/guard";

const json = serializePolicy(policy);
// Store in database, send over network, etc.

const restored = deserializePolicy(json);
// Structurally identical to original

const explanation = explainPolicy(policy);
// "all of: has permission 'ReadUsers', has role 'Admin'"

GxP Infrastructure

For regulated environments, the guard package includes audit trail, write-ahead log, circuit breaker, meta-audit, and decommissioning utilities.

import {
  createWriteAheadLog,
  createCircuitBreaker,
  createScopeDisposalVerifier,
  detectClockDrift,
  enforceRetention,
  createMetaAuditEntry,
  archiveAuditTrail,
  createDecommissioningChecklist,
} from "@hex-di/guard";

API Reference

Tokens

| Export | Kind | Description | | ---------------------------------- | -------- | ------------------------------------------------------- | | createPermission(name) | function | Create a branded permission token | | createPermissionGroup(name, map) | function | Bundle permissions into a named group | | createRole(name, config) | function | Create a role with permissions and optional inheritance |

Policy Combinators

| Export | Kind | Description | | -------------------------- | -------- | -------------------------------------- | | hasPermission(p) | function | Leaf policy: subject has permission | | hasRole(r) | function | Leaf policy: subject has role | | hasAttribute(key, value) | function | Leaf policy: subject attribute matches | | allOf(...policies) | function | All sub-policies must grant | | anyOf(...policies) | function | At least one sub-policy must grant | | not(policy) | function | Invert a policy decision |

Evaluation

| Export | Kind | Description | | -------------------------------------- | -------- | ------------------------------------------ | | evaluate(policy, subject) | function | Synchronous policy evaluation | | evaluateAsync(policy, subject, opts) | function | Async evaluation with attribute resolution | | Decision | type | Evaluation result (granted, trace) | | EvaluationTrace | type | Detailed evaluation path |

Guard

| Export | Kind | Description | | -------------------------------- | -------- | ------------------------------------- | | enforcePolicy(adapter, config) | function | Wrap adapter with policy enforcement | | createGuardGraph(config) | function | Create guard-specific graph fragment | | createGuardHealthCheck() | function | Health check for guard infrastructure | | AccessDeniedError | class | Thrown when policy denies access | | createNoopAuditTrailAdapter() | function | No-op audit trail for non-GxP use |

Hooks

| Export | Kind | Description | | ---------------------------- | -------- | --------------------------------------- | | createPortGateHook(config) | function | Coarse-grained port-level authorization | | createRoleGate(config) | function | Role-based port gate shorthand |

Serialization

| Export | Kind | Description | | ------------------------- | -------- | --------------------------------- | | serializePolicy(policy) | function | Policy to JSON | | deserializePolicy(json) | function | JSON to policy | | explainPolicy(policy) | function | Human-readable policy description |

Subject

| Export | Kind | Description | | --------------------- | ---- | ------------------------------------------------------ | | AuthSubject | type | Subject interface (id, permissions, roles, attributes) | | SubjectProviderPort | port | Port for resolving current subject |

GxP Infrastructure

| Export | Kind | Description | | ----------------------------------- | -------- | -------------------------------------- | | createWriteAheadLog() | function | WAL for audit durability | | createCircuitBreaker(opts) | function | Circuit breaker for audit trail writes | | createScopeDisposalVerifier() | function | Verify scope cleanup | | detectClockDrift(a, b) | function | Clock drift detection | | enforceRetention(entries, policy) | function | Apply retention policy | | createMetaAuditEntry(data) | function | Meta-audit entry creation | | archiveAuditTrail(entries, opts) | function | Archive audit trail data | | createDecommissioningChecklist() | function | System decommissioning checklist |

Inspection

| Export | Kind | Description | | ---------------- | ----- | --------------------------------- | | GuardInspector | class | Runtime inspection of guard state |

Error Types

| Export | Kind | Description | | ------------------------------ | ----- | -------------------------- | | AccessDeniedError | class | Policy denied access | | CircularRoleInheritanceError | type | Circular role DAG detected | | PolicyEvaluationError | type | Evaluation failed | | PolicyDeserializationError | type | Invalid serialized policy | | AuditTrailWriteError | type | Audit write failed | | SignatureError | type | Electronic signature error |

Related Packages

| Package | Description | | -------------------------- | --------------------------------------------------------------------------------------- | | @hex-di/guard-testing | Test utilities: createTestSubject, testPolicy, custom matchers, memory adapters | | @hex-di/guard-react | React integration: SubjectProvider, Can/Cannot, useCan/usePolicy/useSubject | | @hex-di/guard-validation | Programmatic IQ/OQ/PQ runners and traceability matrix generation |

License

MIT