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

@quarry-systems/drift-contracts

v0.0.2-alpha.6

Published

Zod validation schemas and contracts for Drift (Managed Cyclic Graph)

Readme

@quarry-systems/drift-contracts

TypeScript type definitions and Zod validation schemas for the Drift (Managed Cyclic Graph) ecosystem.

npm version License

Overview

drift-contracts provides the foundational type system and validation schemas used across all Drift packages. It defines interfaces for graphs, nodes, edges, plugins, services, and infrastructure adapters, ensuring type safety and consistency throughout the ecosystem.

Installation

npm install @quarry-systems/drift-contracts

Features

  • Comprehensive Type System: Complete TypeScript definitions for all Drift concepts
  • Zod Validation: Runtime validation schemas for configuration and data
  • Plugin Contracts: Interfaces for execution and infrastructure plugins
  • Service Contracts: Type-safe service definitions and injection
  • AI/LLM Types: Complete types for LLM adapters, tools, and streaming
  • Infrastructure Adapters: Contracts for queues, stores, secrets, retrieval, and vectors
  • Zero Runtime Dependencies: Pure types with minimal overhead (only Zod)

Core Concepts

Graph Structure

import type { GraphDef, NodeDef, EdgeDef } from '@quarry-systems/drift-contracts';

const graph: GraphDef = {
  id: 'my-graph',
  nodes: {
    start: { id: 'start', label: 'Start Node' },
    end:   { id: 'end',   label: 'End Node' },
  },
  edges: [
    { id: 'start->end', source: 'start', target: 'end', guards: ['any'] },
  ],
  startNodes: ['start'],
};

Context and Execution

import type { Ctx, Action, Guard } from '@quarry-systems/drift-contracts';

// Context structure
const ctx: Ctx = {
  runId: 'run-123',
  data: { userId: '456' },
  global: { apiKey: 'secret' },
  injected: { timestamp: Date.now() },
  errors: [],
  events: []
};

// Guard function
const guard: Guard = {
  id: 'hasUserId',
  // prevResult is the previous node's handler output (if any), so guards
  // can route on handler results without threading state through ctx.data.
  evaluate: (ctx, prevResult) => {
    if (ctx.data.userId === undefined) {
      return { ok: false, reason: 'missing userId' };
    }
    return { ok: true };
  }
};

// Action function
const action: Action = async (ctx) => {
  return {
    ...ctx,
    data: {
      ...ctx.data,
      processed: true
    }
  };
};

Guard Interface

Guards are pure predicates evaluated against the context (and optionally the previous node's result) to decide whether an edge can be traversed.

import type { Guard, EvalResult, MaybePromise } from '@quarry-systems/drift-contracts';

interface Guard<TCtx = Ctx> {
  id: string;
  description?: string;
  priority?: number;
  timeoutMs?: number;
  evaluate(ctx: TCtx, prevResult?: unknown): MaybePromise<EvalResult>;
}

prevResult parameter — Optional second argument to evaluate. When the graph edge being evaluated follows a node execution, the runtime passes that node's handler output as prevResult. This lets guards make routing decisions directly from handler results without having to merge them into ctx.data first.

// Route on handler output without polluting ctx.data
const isSuccess: Guard = {
  id: 'isSuccess',
  evaluate: (_ctx, prevResult) => {
    const r = prevResult as { status?: string } | undefined;
    return r?.status === 'ok'
      ? { ok: true }
      : { ok: false, reason: 'handler did not return ok' };
  }
};

prevResult is unknown — cast it to the shape your handlers actually return. When no previous result exists (e.g., the first edge of a run), prevResult is undefined.

Edge Definition

import type { EdgeDef } from '@quarry-systems/drift-contracts';

interface EdgeDef<TCtx extends Ctx = Ctx> {
  id: string;
  source: string;
  target: string;
  guards?: Guard<TCtx>[];
  onTransit?: (Action<TCtx> | string)[];
  priority?: number;

  /**
   * Max times this edge can be traversed per run.
   * Omit or `0` = unlimited.
   */
  maxTraversals?: number;

  /**
   * Intrinsic meter memberships stamped at `.meter()` declaration time.
   * Each entry is a meter id that claims this edge as a member; read by the
   * per-meter enforcement guard's wrap-predicate.
   */
  intrinsicMeters?: string[];
}

maxTraversals field — Hard cap on how often a given edge is allowed to fire within a single run. The runtime increments a per-edge counter each time the edge is traversed and skips the edge during selection once the counter reaches maxTraversals. Omit the field (or set it to 0) for unlimited traversals. Useful for bounding remediation loops or retry cycles without writing a manual counter into ctx.data.

// Retry at most 3 times, then fall through to the failure edge
const retryEdge: EdgeDef = {
  id: 'verify->execute',
  source: 'verify',
  target: 'execute',
  guards: [notSuccessGuard],
  maxTraversals: 3
};

Plugin System

import type { Plugin, NodeHandler } from '@quarry-systems/drift-contracts';

// Node handler
const myHandler: NodeHandler = async (node, ctx, meta) => {
  // Process the node
  return {
    ...ctx,
    data: {
      ...ctx.data,
      result: 'processed'
    }
  };
};

// Plugin definition
const myPlugin: Plugin = {
  name: 'my-plugin',
  version: '1.0.0',
  description: 'My custom plugin',
  nodes: {
    'my-node-type': myHandler
  }
};

Service Contracts

import type { ServiceContract, ServiceDefinition } from '@quarry-systems/drift-contracts';

// Define a service contract
const MyServiceContract: ServiceContract = {
  name: 'my-service',
  version: '1.0.0',
  capabilities: ['read', 'write'],
  methods: {
    getData: {
      input: { id: 'string' },
      output: { data: 'object' }
    }
  }
};

// Service definition
const myService: ServiceDefinition = {
  contract: MyServiceContract,
  scope: 'run',
  factory: (ctx) => ({
    getData: async (id: string) => ({ data: { id } })
  })
};

Type Categories

Core Types

  • Context: Ctx, AnyCtx, MaybePromise, EvalResult
  • Graph: GraphDef, NodeDef, EdgeDef, GraphLimits
  • Behaviors: Guard, Action (Rule / GraphRule are still exported for back-compat but no longer wired by drift-core)
  • Events: GraphEvent, NodeEvent, GraphEventHandler, NodeEventHandler

Graph version pinning (alpha.3+):

import type {
  GraphVersion,
  GraphVersionResolution,
  GraphVersionResult,
  GraphVersionComparator,
} from '@quarry-systems/drift-contracts';
  • GraphVersion = string. Opaque; may be an explicit consumer-supplied value (semver/date/SHA/build-id) or a structural-hash fallback (sha256-v1:<hex>).
  • GraphVersionResolution = { version: GraphVersion; source: 'explicit' | 'structural-hash'; structuralHash: string }. Captured at Manager construction; persisted on every snapshot. When source==='structural-hash', version === structuralHash.
  • GraphVersionResult = 'match' | 'compatible' | 'mismatch'. Comparator outcome. Adding outcomes is a SemVer-major change.
  • GraphVersionComparator = (snapshot: GraphVersionResolution, current: GraphVersionResolution) => GraphVersionResult. Pure, sync, side-effect-free. Argument order reads "is this snapshot compatible with current?"

Two new event variants on the RunEvent union: RunResumeGraphVersionMissing (legacy snapshots) and RunResumeGraphVersionWarning ('compatible' outcomes). Both carry full GraphVersionResolution payloads for triage.

AI/LLM Types

  • Messages: ChatMessage, MessageRole, ResponseFormat
  • Tools: ToolDefinition, ToolCall
  • Requests: LLMRequest, LLMResponse, LLMUsage, LLMError
  • Streaming: LLMStreamEvent
  • Adapters: LLMAdapter, EmbeddingAdapter
  • Schema: MCGJsonSchema

Plugin Types

  • Core: Plugin, NodeHandler, PluginMetadata
  • Manifest: PluginManifest, PluginType, PluginCapabilities
  • Validation: PluginValidationResult

Infrastructure Adapters

Queue Adapter

import type { QueueAdapter, QueueJob, JobProcessor } from '@quarry-systems/drift-contracts';

Store Adapter

import type { 
  RunStoreAdapter, 
  ArtifactStore, 
  RunSnapshot, 
  RunMetadata 
} from '@quarry-systems/drift-contracts';

Secrets Adapter

import type { SecretsAdapter, SecretRef } from '@quarry-systems/drift-contracts';

Retrieval Adapter

import type { 
  RetrievalAdapter, 
  Document, 
  Chunk, 
  ChunkMatch 
} from '@quarry-systems/drift-contracts';

Vector Adapter

import type { 
  VectorAdapter, 
  VectorItem, 
  VectorMatch 
} from '@quarry-systems/drift-contracts';

Service System

  • Contracts: ServiceContract, ServiceCapabilities, VersionCompatibility
  • Injection: ServiceDefinition, ServiceRegistry, ServiceScope
  • Validation: ServiceValidationResult

Middleware

import type { 
  Middleware, 
  NodeStartEvent, 
  NodeEndEvent, 
  ServiceCallStartEvent 
} from '@quarry-systems/drift-contracts';

Error Handling

import type { 
  ExecutionError, 
  TraceEntry, 
  ExecutionTrace, 
  DebugOptions 
} from '@quarry-systems/drift-contracts';

Licensing

import type { 
  LicenseTier, 
  LicenseStatus, 
  LicenseFile, 
  LicenseOptions 
} from '@quarry-systems/drift-contracts';

Usage Examples

Building Type-Safe Graphs

import type { GraphDef, NodeDef, Ctx } from '@quarry-systems/drift-contracts';

function createWorkflow(): GraphDef<Ctx, { userId: string }> {
  return {
    id: 'user-workflow',
    nodes: {
      fetch: {
        id: 'fetch',
        label: 'Fetch User',
        meta: { http: { url: '/api/users/${injected.userId}' } },
      },
      process: {
        id: 'process',
        label: 'Process Data',
      },
    },
    edges: [
      { id: 'fetch->process', source: 'fetch', target: 'process', guards: ['any'] },
    ],
    startNodes: ['fetch'],
  };
}

Creating Custom Plugins

import type { Plugin, NodeHandler, Ctx } from '@quarry-systems/drift-contracts';

const delayHandler: NodeHandler = async (node, ctx, meta) => {
  const ms = node.meta?.delay || 1000;
  await new Promise(resolve => setTimeout(resolve, ms));
  return ctx;
};

export const delayPlugin: Plugin = {
  name: 'delay-plugin',
  version: '1.0.0',
  description: 'Adds delay nodes',
  nodes: {
    'delay': delayHandler
  }
};

Implementing Adapters

import type { SecretsAdapter } from '@quarry-systems/drift-contracts';

class EnvSecretsAdapter implements SecretsAdapter {
  async get(key: string): Promise<string | null> {
    return process.env[key] || null;
  }
  
  async set(key: string, value: string): Promise<void> {
    process.env[key] = value;
  }
  
  async delete(key: string): Promise<void> {
    delete process.env[key];
  }
  
  async list(): Promise<string[]> {
    return Object.keys(process.env);
  }
}

Package Structure

drift-contracts/
├── src/
│   ├── ai.ts              # AI/LLM types
│   ├── behaviors.ts       # Guard, Action (Rule still exported for back-compat)
│   ├── context.ts         # Context and state types
│   ├── errors.ts          # Error and debugging types
│   ├── events.ts          # Event system types
│   ├── graph.ts           # Graph structure types
│   ├── license.ts         # Licensing types
│   ├── manifest.ts        # Plugin manifest types
│   ├── middleware.ts      # Middleware types
│   ├── plugin.ts          # Plugin system types
│   ├── queue.ts           # Queue adapter types
│   ├── retrieval.ts       # Retrieval adapter types
│   ├── secrets.ts         # Secrets adapter types
│   ├── service-contract.ts # Service contract types
│   ├── service-injection.ts # Service injection types
│   ├── store.ts           # Store adapter types
│   ├── vector.ts          # Vector adapter types
│   └── index.ts           # Main exports

Execute-Phase Middleware Hooks

Two additional hooks on the Middleware interface fire during the execute phase of a node, giving middleware the ability to propose context patches in addition to observing execution.

import type {
  NodeBeforeExecuteEvent,
  NodeAfterExecuteEvent,
  Middleware,
} from '@quarry-systems/drift-contracts';

NodeBeforeExecuteEvent

Fired after onEnter is committed but before the node's execute actions run.

| Field | Type | Description | |-------|------|-------------| | nodeId | string | Node identifier | | runId | string | Run identifier | | graphId | string | Graph identifier | | context | TCtx | Current context state | | timestamp | number | Event timestamp (ms since epoch) | | propose | (patch) => void | Emit a patch into the Tier-2 patch buffer |

NodeAfterExecuteEvent

Fired after the node's execute actions run but before edge selection.

| Field | Type | Description | |-------|------|-------------| | nodeId | string | Node identifier | | runId | string | Run identifier | | graphId | string | Graph identifier | | context | TCtx | Context state after execute actions | | timestamp | number | Event timestamp (ms since epoch) | | prevResult | unknown | Last execute action's return value; undefined if no execute actions ran | | propose | (patch) => void | Emit a patch into the Tier-2 patch buffer |

propose() signature

propose(patch: Omit<Patch, 'ts' | 'by'> & { by?: string }): void

propose() enqueues a patch into the runtime's Tier-2 patch buffer. Patches are committed into ctx at phase boundaries — they are not immediately visible to later middleware in the same hook chain.

Observer shape preserved

Both hooks return void | Promise<void>. Mutation is done exclusively through propose(); the hook return value is ignored by the runtime. This keeps the Middleware interface consistent with its observer-only contract: hooks observe, and propose() is the explicit opt-in for mutation.

Both hooks are optional on the Middleware interface.

const auditMiddleware: Middleware = {
  name: 'audit',
  version: '1.0.0',

  async onBeforeNodeExecute(event) {
    // Inject a timestamp patch before execute runs
    event.propose({ op: 'set', path: ['_auditStart', event.nodeId], value: event.timestamp });
  },

  async onAfterNodeExecute(event) {
    // Log or propose a patch based on the execute result
    console.log(`${event.nodeId} prevResult:`, event.prevResult);
  },
};

Runtime Events

RuntimeEvent is part of the DriftEvent union emitted on the graph's unified event bus. Subscribe via graph.event() or graph.__eventBus.on().

import type { RuntimeEvent, DriftEvent } from '@quarry-systems/drift-contracts';

RuntimeEvent variants

| Type | Fields | |------|--------| | MeterExceeded | runId, nodeId, meter: string, current: number, limit: number, timestamp: number | | WaitSuspended | runId, nodeId, keys: string[], timestamp: number, timeoutAt?: number | | WaitResolved | runId, key: string, timedOut: boolean, timestamp: number | | ServiceRebuilt | runId, key: string, timestamp: number | | RunSuspended | runId, nodeId, keys: string[] | | RunResumed | runId, nodeId |

Subscription examples

// Via graph.event() — typed DriftEvent handler
graph.event('MeterExceeded', (e) => {
  console.warn(`Meter "${e.meter}" exceeded: ${e.current}/${e.limit} at node ${e.nodeId}`);
});

// Via graph.__eventBus.on() — lower-level subscription
graph.__eventBus.on('WaitSuspended', (e) => {
  console.log(`Run ${e.runId} suspended at ${e.nodeId}, waiting for keys: ${e.keys.join(', ')}`);
  if (e.timeoutAt) {
    console.log(`  Timeout at: ${new Date(e.timeoutAt).toISOString()}`);
  }
});

graph.__eventBus.on('WaitResolved', (e) => {
  if (e.timedOut) {
    console.warn(`Wait key "${e.key}" timed out in run ${e.runId}`);
  }
});

Meter Types

Resource meters are declarative accumulators with limits. The runtime auto-composes a metersOk guard onto every non-essential edge and fires MeterExceeded when any meter breaches its limit.

import type { MeterSource, MeterDef, MetersPolicy } from '@quarry-systems/drift-contracts';

MeterSource

Discriminated union describing where the meter reads its current value.

type MeterSource =
  | { kind: 'metrics'; key: string }   // reads metrics plugin's total(key)
  | { kind: 'timer' }                  // reads ctx.timer.elapsed (requires timer: true)
  | { kind: 'builtin'; name: 'steps' } // reads ctx.data.__currentStep

MeterDef

interface MeterDef {
  limit: number;          // Maximum allowed value; breach fires MeterExceeded
  source: MeterSource;    // Where the current value comes from
  description?: string;   // Human description for logs/errors
}

MetersPolicy

type MetersPolicy = Record<string, MeterDef>;

Example

const meters: MetersPolicy = {
  // Limit to 50 LLM token calls (tracked via metrics plugin)
  llmCalls: {
    limit: 50,
    source: { kind: 'metrics', key: 'llm.calls' },
    description: 'Maximum LLM calls per run',
  },
  // Limit wall-clock time to 5 minutes (requires Manager option timer: true)
  wallTime: {
    limit: 5 * 60 * 1000,
    source: { kind: 'timer' },
    description: '5-minute wall-clock budget',
  },
  // Limit traversal steps
  steps: {
    limit: 200,
    source: { kind: 'builtin', name: 'steps' },
    description: 'Maximum graph steps',
  },
};

Context Extensions

CtxTimer

A checkpoint-aware timer for tracking wall-clock time minus paused intervals. Enabled via the Manager option timer: true and available as ctx.timer.

interface CtxTimer {
  readonly elapsed: number;  // Total elapsed ms excluding paused intervals
  pause(): void;             // Pause the timer (e.g., waiting for user input)
  resume(): void;            // Resume the timer
  readonly paused: boolean;  // Whether currently paused
}
// Inside a node action
const budget = 5 * 60 * 1000; // 5 minutes
if (ctx.timer && ctx.timer.elapsed > budget) {
  throw new Error('Run exceeded time budget');
}

WaitOptions

Options passed to ctx.waitForInjection().

interface WaitOptions {
  timeoutMs?: number; // Wall-clock timeout in ms; on expiry, ctx.injected.__waits[key] = { __timedOut: true }
  mode?: 'all';       // Reserved; v1 only supports all-satisfied resume
}

InjectedWaits

Reserved sub-namespace on ctx.injected written by Manager.inject(runId, key, value).

interface InjectedWaits {
  [key: string]: unknown | { __timedOut: true };
}

Keys use a flat, dot-safe convention: 'foo.bar' is a literal key, not a nested path. A timed-out key has the shape { __timedOut: true }.

ctx.waitForInjection and ctx.injected.__waits

// Inside a node action — register a wait for an external event
ctx.waitForInjection?.('approval', { timeoutMs: 60_000 });

// The NEXT node reads the resolved value (or timeout sentinel)
const result = (ctx.injected as any).__waits?.['approval'];
if (result?.__timedOut) {
  // Handle timeout
} else {
  // Use result
}

waitForInjection uses mark-and-return semantics: the call registers the wait and returns immediately. Suspension happens at the next onAfterNodeExecute phase. The injected value is consumed by the next node, not the node that called waitForInjection.


Related Packages

Official Plugins

  • @quarry-systems/drift-http - HTTP client
  • @quarry-systems/drift-timer - Timers and scheduling
  • @quarry-systems/drift-openai - OpenAI integration
  • @quarry-systems/drift-secrets - Secrets management
  • @quarry-systems/drift-store-sqlite - SQLite storage
  • @quarry-systems/drift-vector-chroma - ChromaDB vectors

TypeScript Configuration

For best results, use these TypeScript compiler options:

{
  "compilerOptions": {
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "moduleResolution": "node",
    "resolveJsonModule": true
  }
}

Contributing

This package is part of the Drift monorepo. For contributions, please see the main repository.

License

Dual-licensed under:

  • AGPL-3.0 for open source projects
  • Commercial License for proprietary use

See LICENSE.md for details.

For commercial licensing inquiries:

Support