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

@statedelta/chain

v0.1.1

Published

Tail resolver for StateDelta - navigates inheritance chains via partial headers

Readme

@statedelta/chain

Tail resolver for StateDelta - navigates inheritance chains via partial headers.

TypeScript Node License

Overview

@statedelta/chain is a focused, high-performance library for resolving inheritance chains (tails) in document hierarchies. It navigates through extends references using partial header fetches (~4KB), enabling maximum parallelism for orchestrators.

manifest.json ──extends──▶ step-1.json ──extends──▶ genesis.json
     │                          │                        │
     ▼                          ▼                        ▼
   HEAD                       ...                      ROOT

Features

  • Fast - Uses partial headers (~4KB) instead of full files
  • Agnostic - Doesn't know your DSL, receives extractRefs function
  • Event-Driven - Emits events for each node (enables parallel processing)
  • Type-Safe - Full TypeScript with strict mode
  • Cycle Detection - Detects circular dependencies with visualization
  • Cancellable - Supports AbortSignal for timeout/cancellation
  • Disposable - One instance per resolution (stateless)
  • Relative Paths - Passes referrer context for relative path resolution

Installation

pnpm add @statedelta/chain

Peer Dependency:

pnpm add @statedelta/gateway

Quick Start

import { createChain, defaultExtractRefs } from '@statedelta/chain';
import { createGateway, FileProvider } from '@statedelta/gateway';

// 1. Create Gateway
const gateway = createGateway({
  providers: [new FileProvider()],
});

// 2. Create Chain
const chain = createChain({
  gateway,
  extractRefs: defaultExtractRefs, // extracts `extends` field
});

// 3. Listen to events (optional but powerful)
chain.on('node:loaded', (node) => {
  console.log(`Loaded: ${node.header.name} (depth: ${node.depth})`);
});

// 4. Resolve
const result = await chain.resolve('./manifest.json');

console.log(`Tail: ${result.depth} nodes`);
console.log(`Root: ${result.root.header.name}`);
console.log(`Head: ${result.head.header.name}`);

API

createChain(options)

Creates a new Chain instance.

const chain = createChain({
  // Required
  gateway: GatewayLike,           // Gateway with loadHeader()
  extractRefs: ExtractRefs,       // Function to extract parent ref

  // Optional
  maxDepth?: number,              // Default: 100
  validateHeader?: HeaderValidator,
});

chain.resolve(source, options?)

Resolves the inheritance chain.

const result = await chain.resolve('./manifest.json', {
  signal?: AbortSignal,  // For cancellation
});

Returns: Promise<ChainResult>

interface ChainResult {
  tail: ChainNode[];     // [root, ..., head]
  root: ChainNode;       // First node (no parent)
  head: ChainNode;       // Last node (entry point)
  depth: number;         // Number of nodes
  source: string;        // Original source
  duration: number;      // Resolution time (ms)
}

interface ChainNode {
  source: string;        // URL/path
  depth: number;         // 0 = head, increases toward root
  isRoot: boolean;       // true if no parent
  header: CollectedHeader;
}

Events

chain.on('node:loaded', (node: ChainNode) => {
  // Called for each node as it's discovered
  // Perfect for starting parallel work!
});

chain.on('tail:complete', (result: ChainResult) => {
  // Called when resolution completes
});

chain.on('error', (error: Error) => {
  // Called on any error
});

defaultExtractRefs

Default extractor for StateDelta Document Protocol:

import { defaultExtractRefs } from '@statedelta/chain';

// Extracts parent from `extends` field
const refs = defaultExtractRefs(header);
// { parent: header.extends }

createMockGateway(headers)

Creates a mock gateway for testing:

import { createMockGateway } from '@statedelta/chain';

const gateway = createMockGateway({
  './a.json': { name: 'a', extends: './b.json' },
  './b.json': { name: 'b' },
});

Usage Examples

With Orchestrator (Maximum Parallelism)

const chain = createChain({ gateway, extractRefs });

// React to each node immediately
chain.on('node:loaded', (node) => {
  // Start full content download in parallel
  orchestrator.hydrate(node.source);

  // Start deps download in parallel
  if (node.header.deps) {
    orchestrator.loadDeps(node.header.deps);
  }
});

const result = await chain.resolve('./manifest.json');
// By now, orchestrator may have already downloaded everything!

Multiple Chains in Parallel

// Gateway is shared - cache benefits all chains
const [result1, result2, result3] = await Promise.all([
  createChain({ gateway, extractRefs }).resolve('./a.json'),
  createChain({ gateway, extractRefs }).resolve('./b.json'),
  createChain({ gateway, extractRefs }).resolve('./c.json'),
]);

Custom Validation

const chain = createChain({
  gateway,
  extractRefs,
  validateHeader: (header, ctx) => {
    // Reject old versions
    if (header.version && !semver.satisfies(header.version, '^2.0.0')) {
      return { valid: false, error: `Version ${header.version} not supported` };
    }

    // Limit depth for this use case
    if (ctx.depth > 50) {
      return { valid: false, error: 'Chain too deep' };
    }

    return { valid: true };
  },
});

With Timeout

const controller = new AbortController();
setTimeout(() => controller.abort(), 5000); // 5s timeout

try {
  const result = await chain.resolve('./manifest.json', {
    signal: controller.signal,
  });
} catch (error) {
  if (isAbortError(error)) {
    console.log('Resolution timed out');
  }
}

Custom ExtractRefs

// For a different DSL that uses `parent` instead of `extends`
const extractRefs = (header) => ({
  parent: header.parent || header.base || header.inherits,
});

const chain = createChain({ gateway, extractRefs });

Relative Paths

Chain supports relative paths in extends by passing the referrer context to Gateway:

// head.json: { "extends": "./delta.json" }
// delta.json: { "extends": "../base/state.json" }

const result = await chain.resolve('/fixtures/head.json');

// Chain passes referrer to Gateway:
// loadHeader("./delta.json", { referrer: "/fixtures/head.json" })
// Gateway/FileProvider resolves to: "/fixtures/delta.json"

// Then:
// loadHeader("../base/state.json", { referrer: "/fixtures/delta.json" })
// Gateway/FileProvider resolves to: "/fixtures/base/state.json"

Note: Chain does NOT resolve paths itself. It passes the referrer context, and the Gateway/Provider resolves the relative path. This keeps Chain agnostic to filesystem details.

Errors

All errors extend ChainError and include:

  • code - Programmatic error code
  • source - Source that caused the error
  • toJSON() - Serialization support
import {
  ChainError,
  CircularDependencyError,
  MaxDepthError,
  HeaderValidationError,
  ResolutionError,
  AbortError,
  // Type guards
  isChainError,
  isCircularDependencyError,
  isMaxDepthError,
  isHeaderValidationError,
  isResolutionError,
  isAbortError,
} from '@statedelta/chain';

CircularDependencyError

try {
  await chain.resolve('./a.json');
} catch (error) {
  if (isCircularDependencyError(error)) {
    console.log('Cycle:', error.cycle);
    // ['a.json', 'b.json', 'c.json', 'a.json']

    console.log(error.visualize());
    // a.json
    //   ↓
    // b.json
    //   ↓
    // c.json
    //   ↓
    // a.json ← cycle!
  }
}

MaxDepthError

if (isMaxDepthError(error)) {
  console.log(`Depth ${error.depth} exceeded max ${error.maxDepth}`);
  console.log('Path:', error.path);
}

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                         ORCHESTRATOR                             │
│                    (Loader or Assembler)                         │
│                                                                  │
│  - Provides extractRefs (knows DSL)                              │
│  - Listens to Chain events                                       │
│  - Starts parallel work on node:loaded                           │
└─────────────────────────────────────────────────────────────────┘
              │                              │
              │ on('node:loaded')            │ gateway.load()
              ▼                              ▼
┌──────────────────────┐          ┌──────────────────────────────┐
│       CHAIN          │          │         GATEWAY               │
│                      │          │                               │
│  - Navigates tail    │ headers  │  - loadHeader() partial      │
│  - EventEmitter      │─────────▶│  - load() full               │
│  - Cycle detection   │          │  - Cache                     │
│  - One-shot          │          │  - Integrity validation      │
└──────────────────────┘          └──────────────────────────────┘

What Chain Does

  • ✅ Navigates inheritance chain via gateway.loadHeader()
  • ✅ Emits events for each node
  • ✅ Returns Promise<ChainResult> with complete tail
  • ✅ Detects circular dependencies
  • ✅ Supports AbortSignal cancellation
  • ✅ Custom header validation

What Chain Does NOT Do

  • ❌ Know your DSL (receives extractRefs)
  • ❌ Direct I/O (Gateway does it)
  • ❌ Parse content (Gateway does it)
  • ❌ Store content (stateless)
  • ❌ Manage deps (Orchestrator does it)
  • ❌ Cache (Gateway does it)
  • ❌ Resolve paths (passes referrer, Provider resolves)

Performance

| Operation | Without Chain | With Chain | |-----------|---------------|------------| | Discover 10-node tail | ~500ms (full files) | ~50ms (headers only) | | Bytes per node | ~500KB | ~4KB | | Start parallel work | After complete | After 1st node |

Type Guards

import {
  isChainNode,
  isChainResult,
  isCollectedHeader,
  isGatewayLike,
} from '@statedelta/chain';

if (isChainNode(value)) {
  // value is ChainNode
}

Testing

# Run tests
pnpm test

# Watch mode
pnpm test:watch

# Coverage
pnpm test:coverage

# UI
pnpm test:ui

Coverage: 99%+ statements, 97%+ functions

Documentation

Related Packages

License

MIT