@rotorsoft/act 1.31.14 → 1.31.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/test/index.ts","../../src/test/sandbox.ts","../../src/internal/lru-map.ts","../../src/adapters/in-memory-cache.ts","../../src/adapters/console-logger.ts","../../src/config.ts","../../src/types/errors.ts","../../src/types/schemas.ts","../../src/internal/sensitive.ts","../../src/types/index.ts","../../src/utils.ts","../../src/scoped.ts","../../src/ports.ts","../../src/adapters/in-memory-store.ts"],"sourcesContent":["export * from \"./sandbox.js\";\n","import { test } from \"vitest\";\nimport type { Act, ActOptions } from \"../act.js\";\nimport { InMemoryCache } from \"../adapters/in-memory-cache.js\";\nimport { InMemoryStore } from \"../adapters/in-memory-store.js\";\nimport type { Cache, Store } from \"../types/index.js\";\n\n/**\n * Minimal structural shape — anything with a `.build()` that returns an\n * Act. Avoids the variance pitfalls of constraining on the full\n * `ActBuilder<TSchemaReg, TEvents, TActions, TStateMap, TActor>`\n * generic when called across package boundaries.\n */\ntype AnyActBuilder<TApp> = {\n // `ActOptions<any>` rather than `ActOptions<string>` (the default) — a\n // builder narrowed to `ActOptions<\"default\">` would otherwise trip\n // function-parameter contravariance against `ActOptions<string>`. The\n // `any` only widens `onlyLanes`; the rest of the option shape\n // (`scoped`, `correlator`, `settleDebounceMs`, etc.) still type-checks\n // at the runtime call site below.\n build: (options?: ActOptions<any>) => TApp;\n};\n\n/**\n * Options for {@link sandbox} / {@link fixture}.\n *\n * Defaults to `new InMemoryStore() + new InMemoryCache()` per call.\n * Override `store` / `cache` to point at PG, SQLite, or any other\n * adapter — factories run once per call so each test gets a fresh\n * instance.\n */\nexport type SandboxOptions = {\n /** Factory for the per-test store. Defaults to `new InMemoryStore()`. */\n readonly store?: () => Store | Promise<Store>;\n /** Factory for the per-test cache. Defaults to `new InMemoryCache()`. */\n readonly cache?: () => Cache | Promise<Cache>;\n /** Pass-through ActOptions. `scoped` is filled in by the helper. */\n readonly actOptions?: Omit<ActOptions, \"scoped\">;\n};\n\n/** Return shape of {@link sandbox}. */\nexport type Sandbox<TApp> = {\n readonly app: TApp;\n readonly store: Store;\n readonly cache: Cache;\n /** Tears down the Act, store, and cache. Idempotent. */\n readonly dispose: () => Promise<void>;\n};\n\n/**\n * Build a scoped Act bound to a fresh `{ store, cache }` bag.\n *\n * Intended for parallel-safe test isolation. Each call constructs new\n * ports (defaults to InMemoryStore + InMemoryCache), seeds the store,\n * builds the Act with `ActOptions.scoped`, and returns `{ app, store,\n * cache, dispose }`. The caller owns the lifecycle: `dispose()` performs\n * `app.shutdown()` followed by `store.dispose()` + `cache.dispose()`.\n *\n * Prefer {@link fixture} when fixture-style ergonomics fit; reach for\n * `sandbox` when you need explicit control (e.g., wiring inside\n * `beforeAll` rather than per-test, or two scoped Acts in one test, or\n * direct access to the store/cache handles).\n *\n * @example Per-test isolated Act with explicit dispose\n * ```ts\n * import { sandbox } from \"@rotorsoft/act/test\";\n *\n * const counterBuilder = act().withState(Counter);\n *\n * it(\"increments\", async () => {\n * const { app, dispose } = await sandbox(counterBuilder);\n * await app.do(\"increment\", { stream: \"c-1\", actor }, { by: 1 });\n * expect((await app.load(\"Counter\", \"c-1\")).state.count).toBe(1);\n * await dispose();\n * });\n * ```\n *\n * @example Custom store factory (PG per-test schema)\n * ```ts\n * const { app, dispose } = await sandbox(builder, {\n * store: () => new PostgresStore({ schema: `t_${nanoid()}` }),\n * });\n * ```\n */\nexport async function sandbox<TApp>(\n builder: AnyActBuilder<TApp>,\n options: SandboxOptions = {}\n): Promise<Sandbox<TApp>> {\n const store = options.store ? await options.store() : new InMemoryStore();\n const cache = options.cache ? await options.cache() : new InMemoryCache();\n await store.seed();\n\n const app = builder.build({\n ...options.actOptions,\n scoped: { store, cache },\n });\n\n let _disposed: Promise<void> | undefined;\n const dispose = (): Promise<void> => {\n if (!_disposed) {\n _disposed = (async () => {\n await (app as unknown as Act<any, any, any, any, any>).shutdown();\n await store.dispose();\n await cache.dispose();\n })();\n }\n return _disposed;\n };\n\n return { app, store, cache, dispose };\n}\n\n/**\n * Vitest fixture wrapper around {@link sandbox}.\n *\n * Returns a `test` instance with an `app` fixture — each test gets a\n * fresh, isolated Act and vitest's fixture lifecycle runs the cleanup\n * automatically when the test completes.\n *\n * Works with `test.concurrent(...)` — every concurrent invocation\n * receives its own bag, so tests don't race on the singleton.\n *\n * For tests that also need direct access to the underlying `store` /\n * `cache` handles, use {@link sandbox} explicitly.\n *\n * @example Fixture-style — auto cleanup, parallel-safe\n * ```ts\n * import { fixture } from \"@rotorsoft/act/test\";\n *\n * const test = fixture(act().withState(Counter));\n *\n * test(\"increments\", async ({ app }) => {\n * await app.do(\"increment\", { stream: \"c-1\", actor }, { by: 1 });\n * expect((await app.load(\"Counter\", \"c-1\")).state.count).toBe(1);\n * });\n *\n * test.concurrent(\"isolated from concurrent peers\", async ({ app }) => {\n * // Each concurrent invocation gets its own store + cache — no\n * // singleton contention, no flake.\n * });\n * ```\n *\n * @example Custom store factory\n * ```ts\n * const test = fixture(builder, {\n * store: () => new PostgresStore({ schema: `t_${nanoid()}` }),\n * });\n * ```\n */\nexport function fixture<TApp>(\n builder: AnyActBuilder<TApp>,\n defaults: SandboxOptions = {}\n) {\n return test.extend<{ app: TApp }>({\n app: async (\n // biome-ignore lint/correctness/noEmptyPattern: vitest fixture API requires a destructured deps parameter\n {},\n use: (value: TApp) => Promise<void>\n ) => {\n const ctx = await sandbox(builder, defaults);\n try {\n await use(ctx.app);\n } finally {\n await ctx.dispose();\n }\n },\n });\n}\n","/**\n * @module lru-map\n * @category Internal\n *\n * Tiny bounded LRU map / set built on insertion-ordered `Map`. Used to cap\n * memory in long-running orchestrators that mint large numbers of keys —\n * notably:\n *\n * - {@link InMemoryCache}: stream → state checkpoint\n * - `Act._subscribed_streams`: stream → presence (LruSet)\n *\n * Apps with millions of dynamic streams (one target per aggregate) can't\n * afford an unbounded `Set<string>` — eviction is required.\n *\n * @internal\n */\n\n/**\n * Bounded LRU map. `get()` promotes; `has()` does not. `set()` always\n * promotes and evicts the oldest entry when at capacity.\n *\n * @internal\n */\nexport class LruMap<K, V> {\n private readonly _entries = new Map<K, V>();\n private readonly _max_size: number;\n\n constructor(maxSize: number) {\n this._max_size = maxSize;\n }\n\n get(key: K): V | undefined {\n const v = this._entries.get(key);\n if (v === undefined) return undefined;\n // promote: delete + re-insert moves to most-recent position\n this._entries.delete(key);\n this._entries.set(key, v);\n return v;\n }\n\n has(key: K): boolean {\n return this._entries.has(key);\n }\n\n set(key: K, value: V): void {\n this._entries.delete(key);\n if (this._entries.size >= this._max_size) {\n // size >= maxSize ≥ 1 → at least one entry exists → next().value\n // is the oldest key (asserted with `!`).\n const oldest = this._entries.keys().next().value!;\n this._entries.delete(oldest);\n }\n this._entries.set(key, value);\n }\n\n delete(key: K): boolean {\n return this._entries.delete(key);\n }\n\n clear(): void {\n this._entries.clear();\n }\n\n get size(): number {\n return this._entries.size;\n }\n}\n\n/**\n * Bounded LRU set built on top of {@link LruMap}. `has()` does not promote;\n * `add()` does (re-inserting if already present, evicting the oldest at\n * capacity).\n *\n * @internal\n */\nexport class LruSet<T> {\n private readonly _map: LruMap<T, true>;\n\n constructor(maxSize: number) {\n this._map = new LruMap(maxSize);\n }\n\n has(value: T): boolean {\n return this._map.has(value);\n }\n\n add(value: T): void {\n this._map.set(value, true);\n }\n\n delete(value: T): boolean {\n return this._map.delete(value);\n }\n\n clear(): void {\n this._map.clear();\n }\n\n get size(): number {\n return this._map.size;\n }\n}\n","import { LruMap } from \"../internal/lru-map.js\";\nimport type { Cache, CacheEntry, Schema } from \"../types/index.js\";\n\n/**\n * In-memory LRU cache for stream snapshots.\n *\n * Backed by an internal `LruMap` for O(1) get/set with LRU eviction.\n * Configurable `maxSize` bounds memory usage.\n *\n * @example\n * ```typescript\n * import { cache } from \"@rotorsoft/act\";\n * import { InMemoryCache } from \"@rotorsoft/act\";\n *\n * cache(new InMemoryCache({ maxSize: 500 }));\n * ```\n */\n/* eslint-disable @typescript-eslint/require-await -- async interface for Redis-compatibility */\nexport class InMemoryCache implements Cache {\n // CacheEntry<any> lets `get<TState>` and `set<TState>` flow without casts:\n // any is bidirectionally compatible with the per-call TState binding, while\n // the public Cache interface still presents a typed surface to callers.\n private readonly _entries: LruMap<string, CacheEntry<any>>;\n\n constructor(options?: { maxSize?: number }) {\n this._entries = new LruMap(options?.maxSize ?? 1000);\n }\n\n /** @inheritDoc */\n async get<TState extends Schema>(\n stream: string\n ): Promise<CacheEntry<TState> | undefined> {\n return this._entries.get(stream);\n }\n\n /** @inheritDoc */\n async set<TState extends Schema>(\n stream: string,\n entry: CacheEntry<TState>\n ): Promise<void> {\n this._entries.set(stream, entry);\n }\n\n /** @inheritDoc */\n async invalidate(stream: string): Promise<void> {\n this._entries.delete(stream);\n }\n\n /** @inheritDoc */\n async clear(): Promise<void> {\n this._entries.clear();\n }\n\n /** @inheritDoc */\n async dispose(): Promise<void> {\n this._entries.clear();\n }\n\n /** Current number of entries held by the LRU. */\n get size(): number {\n return this._entries.size;\n }\n}\n","/**\n * @module adapters/console-logger\n *\n * High-performance console logger inspired by pino's design:\n * - Numeric level comparison for O(1) gating\n * - stdout.write() in production for raw JSON lines (no console overhead)\n * - Colorized single-line output in development\n * - No-op method replacement when level is above threshold\n * - Child logger support with merged bindings\n */\nimport type { Logger } from \"../types/index.js\";\n\nconst LEVEL_VALUES: Record<string, number> = {\n fatal: 60,\n error: 50,\n warn: 40,\n info: 30,\n debug: 20,\n trace: 10,\n};\n\nconst LEVEL_COLORS: Record<string, string> = {\n fatal: \"\\x1b[41m\\x1b[37m\", // white on red bg\n error: \"\\x1b[31m\", // red\n warn: \"\\x1b[33m\", // yellow\n info: \"\\x1b[32m\", // green\n debug: \"\\x1b[36m\", // cyan\n trace: \"\\x1b[90m\", // gray\n};\n\nconst RESET = \"\\x1b[0m\";\n\nconst noop = () => {};\n\n/**\n * Default console logger for the Act framework.\n *\n * Production mode emits newline-delimited JSON (compatible with GCP, AWS\n * CloudWatch, Datadog, and other structured log ingestion systems).\n *\n * Development mode emits colorized, human-readable output.\n */\nexport class ConsoleLogger implements Logger {\n level: string;\n private readonly _pretty: boolean;\n\n readonly fatal: Logger[\"fatal\"];\n readonly error: Logger[\"error\"];\n readonly warn: Logger[\"warn\"];\n readonly info: Logger[\"info\"];\n readonly debug: Logger[\"debug\"];\n readonly trace: Logger[\"trace\"];\n\n constructor(\n options: {\n level?: string;\n pretty?: boolean;\n bindings?: Record<string, unknown>;\n } = {}\n ) {\n const {\n level = \"info\",\n pretty = process.env.NODE_ENV !== \"production\",\n bindings,\n } = options;\n this._pretty = pretty;\n this.level = level;\n\n const threshold = LEVEL_VALUES[level] ?? 30;\n const write = pretty\n ? this._pretty_write.bind(this, bindings)\n : this._json_write.bind(this, bindings);\n\n // Assign methods — noop when level is gated (like pino's level-based replacement)\n this.fatal = write.bind(this, \"fatal\", 60); // fatal is always enabled\n this.error = threshold <= 50 ? write.bind(this, \"error\", 50) : noop;\n this.warn = threshold <= 40 ? write.bind(this, \"warn\", 40) : noop;\n this.info = threshold <= 30 ? write.bind(this, \"info\", 30) : noop;\n this.debug = threshold <= 20 ? write.bind(this, \"debug\", 20) : noop;\n this.trace = threshold <= 10 ? write.bind(this, \"trace\", 10) : noop;\n }\n\n /** No-op — `console.log` has no resources to release. */\n async dispose(): Promise<void> {}\n\n /** @inheritDoc */\n child(bindings: Record<string, unknown>): Logger {\n return new ConsoleLogger({\n level: this.level,\n pretty: this._pretty,\n bindings,\n });\n }\n\n private _json_write(\n bindings: Record<string, unknown> | undefined,\n level: string,\n _num: number,\n obj_or_msg: unknown,\n msg?: string\n ): void {\n let obj: Record<string, unknown>;\n let message: string | undefined;\n\n if (typeof obj_or_msg === \"string\") {\n message = obj_or_msg;\n obj = {};\n } else if (obj_or_msg instanceof Error) {\n // Error instances spread to `{}` — capture the salient fields\n // explicitly so structured log aggregators see them.\n message = msg ?? obj_or_msg.message;\n obj = {\n error: { message: obj_or_msg.message, name: obj_or_msg.name },\n stack: obj_or_msg.stack,\n };\n } else if (obj_or_msg !== null && typeof obj_or_msg === \"object\") {\n message = msg;\n obj = { ...(obj_or_msg as Record<string, unknown>) };\n } else {\n message = msg;\n obj = { value: obj_or_msg };\n }\n\n const entry = Object.assign({ level, time: Date.now() }, bindings, obj);\n if (message) entry.msg = message;\n\n let line: string;\n try {\n line = JSON.stringify(entry);\n } catch {\n // Cyclic or unserializable payload — emit a minimal line rather\n // than crash the log call site.\n line = JSON.stringify({\n level,\n time: entry.time,\n msg: message ?? \"[unserializable]\",\n unserializable: true,\n });\n }\n process.stdout.write(line + \"\\n\");\n }\n\n private _pretty_write(\n bindings: Record<string, unknown> | undefined,\n level: string,\n _num: number,\n obj_or_msg: unknown,\n msg?: string\n ): void {\n const color = LEVEL_COLORS[level];\n const tag = `${color}${level.toUpperCase().padEnd(5)}${RESET}`;\n const ts = new Date().toISOString().slice(11, 23); // HH:mm:ss.SSS\n\n let message: string;\n let data: string | undefined;\n\n if (typeof obj_or_msg === \"string\") {\n message = obj_or_msg;\n } else if (obj_or_msg instanceof Error) {\n // Error instances don't serialize their `message`/`stack` via\n // JSON.stringify — `JSON.stringify(err) === \"{}\"`. Render the\n // message inline and the stack as the data payload so operators\n // see something useful instead of an empty object.\n message = msg ?? obj_or_msg.message;\n data = obj_or_msg.stack;\n } else {\n message = msg ?? \"\";\n if (obj_or_msg !== undefined && obj_or_msg !== null) {\n try {\n data = JSON.stringify(obj_or_msg);\n } catch {\n data = \"[unserializable]\";\n }\n }\n }\n\n const bind_str =\n bindings && Object.keys(bindings).length\n ? ` ${JSON.stringify(bindings)}`\n : \"\";\n\n const parts = [ts, tag, message, data, bind_str].filter(Boolean);\n process.stdout.write(parts.join(\" \") + \"\\n\");\n }\n}\n","/**\n * @packageDocumentation\n * Configuration utilities for Act Framework environment, logging, and package metadata.\n *\n * Provides type-safe configuration loading and validation using Zod schemas.\n *\n * @module config\n */\nimport * as fs from \"node:fs\";\nimport { z } from \"zod\";\nimport { log } from \"./ports.js\";\nimport {\n type Environment,\n Environments,\n type LogLevel,\n LogLevels,\n} from \"./types/index.js\";\nimport { extend } from \"./utils.js\";\n\n/**\n * Zod schema for validating package.json metadata.\n * @internal\n */\nexport const PackageSchema = z.object({\n name: z.string().min(1),\n version: z.string().min(1),\n description: z.string().min(1).optional(),\n author: z\n .object({ name: z.string().min(1), email: z.string().optional() })\n .optional()\n .or(z.string().min(1))\n .optional(),\n license: z.string().min(1).optional(),\n dependencies: z.record(z.string(), z.string()).optional(),\n});\n\n/**\n * Type representing the validated package.json metadata.\n *\n * @internal\n */\nexport type Package = z.infer<typeof PackageSchema>;\n\n/**\n * Fallback package metadata when `package.json` can't be read at module\n * load — happens when the framework is consumed from a CWD that doesn't\n * have one (bundled CLIs, Lambda layers, embedded scripts) or when the\n * file exists but is malformed.\n *\n * The values are deliberately synthetic so callers spot them immediately:\n * `config().name === \"act-fallback\"` is a runtime signal that the framework\n * couldn't load the host project's package.json.\n *\n * @internal\n */\nconst FALLBACK_PACKAGE: Package = {\n name: \"act-fallback\",\n version: \"0.0.0-fallback\",\n description: \"Synthetic fallback — package.json could not be loaded\",\n};\n\n/**\n * Loads and parses the local package.json file as a Package object. On\n * any read or parse failure, falls back to {@link FALLBACK_PACKAGE} and\n * stashes the error so {@link config} can surface it on first access —\n * we can't call `log()` here because the logger port memoizes on first\n * call and locking it at module load defeats user injection.\n *\n * @internal\n */\nconst get_package = (): Package => {\n try {\n const raw = fs.readFileSync(\"package.json\");\n return JSON.parse(raw.toString()) as Package;\n } catch (err) {\n pkg_load_error = err;\n return FALLBACK_PACKAGE;\n }\n};\n\n/** Stashed read/parse error from {@link get_package}, surfaced by config(). */\nlet pkg_load_error: unknown;\n\n/**\n * Zod schema for the full Act Framework configuration object.\n * Includes package metadata, environment, logging, and timing options.\n * @internal\n */\nconst BaseSchema = PackageSchema.extend({\n env: z.enum(Environments),\n logLevel: z.enum(LogLevels),\n logSingleLine: z.boolean(),\n sleepMs: z.number().int().min(0).max(5000),\n});\n\n/**\n * Type representing the validated Act Framework configuration object.\n */\nexport type Config = z.infer<typeof BaseSchema>;\n\nconst { NODE_ENV, LOG_LEVEL, LOG_SINGLE_LINE, SLEEP_MS } = process.env;\n\nconst env = (NODE_ENV || \"development\") as Environment;\nconst logLevel = (LOG_LEVEL ||\n (NODE_ENV === \"test\"\n ? \"fatal\"\n : NODE_ENV === \"production\"\n ? \"info\"\n : \"trace\")) as LogLevel;\nconst logSingleLine = (LOG_SINGLE_LINE || \"true\") === \"true\";\nconst sleepMs = parseInt(NODE_ENV === \"test\" ? \"0\" : (SLEEP_MS ?? \"100\"), 10);\n\nconst pkg = get_package();\n\n// Lazily validated on first call. Cannot run extend() at module load\n// because of a utils.ts <-> config.ts cycle (utils imports config for\n// sleep()'s default). Inputs are frozen after import, so the cached\n// result is stable for the life of the process.\nlet _validated: Config | undefined;\n\n/**\n * Gets the current Act Framework configuration.\n *\n * Configuration is loaded from package.json and environment variables, providing\n * type-safe access to application metadata and runtime settings.\n *\n * **Environment Variables:**\n * - `NODE_ENV`: \"development\" | \"test\" | \"staging\" | \"production\" (default: \"development\")\n * - `LOG_LEVEL`: \"fatal\" | \"error\" | \"warn\" | \"info\" | \"debug\" | \"trace\"\n * - `LOG_SINGLE_LINE`: \"true\" | \"false\" (default: \"true\")\n * - `SLEEP_MS`: Milliseconds for sleep utility (default: 100, 0 for tests)\n *\n * **Defaults by environment:**\n * - test: logLevel=\"error\", sleepMs=0\n * - production: logLevel=\"info\"\n * - development: logLevel=\"trace\"\n *\n * @returns The validated configuration object\n *\n * @example Basic usage\n * ```typescript\n * import { config } from \"@rotorsoft/act\";\n *\n * const cfg = config();\n * console.log(`App: ${cfg.name} v${cfg.version}`);\n * console.log(`Environment: ${cfg.env}`);\n * console.log(`Log level: ${cfg.logLevel}`);\n * ```\n *\n * @example Environment-specific behavior\n * ```typescript\n * import { config } from \"@rotorsoft/act\";\n *\n * const cfg = config();\n *\n * if (cfg.env === \"production\") {\n * // Use PostgreSQL in production\n * store(new PostgresStore(prodConfig));\n * } else {\n * // Use in-memory store for dev/test\n * store(new InMemoryStore());\n * }\n * ```\n *\n * @example Adjusting log levels\n * ```typescript\n * // Set via environment variable:\n * // LOG_LEVEL=debug npm start\n *\n * // Or check in code:\n * const cfg = config();\n * if (cfg.logLevel === \"trace\") {\n * logger.trace(\"Detailed debugging enabled\");\n * }\n * ```\n *\n * @see {@link Config} for configuration type\n */\nexport const config = (): Config => {\n if (!_validated) {\n _validated = extend(\n { ...pkg, env, logLevel, logSingleLine, sleepMs },\n BaseSchema\n );\n if (pkg_load_error) {\n // Surface the fallback once, after _validated is set so the\n // recursive log() → config() call short-circuits. log() resolves\n // through the port singleton — respects user injection and level.\n const msg =\n pkg_load_error instanceof Error\n ? pkg_load_error.message\n : typeof pkg_load_error === \"string\"\n ? pkg_load_error\n : \"unknown error\";\n log().warn(\n `[act] Could not read package.json (${msg}); using synthetic ` +\n `name=\"${FALLBACK_PACKAGE.name}\" version=\"${FALLBACK_PACKAGE.version}\".`\n );\n pkg_load_error = undefined;\n }\n }\n return _validated;\n};\n","import type {\n Actor,\n Message,\n Schema,\n Schemas,\n Snapshot,\n Target,\n} from \"./action.js\";\n\n/**\n * @packageDocumentation\n * @module act/types\n * @category Types\n * Application error type constants and error classes for the Act Framework.\n *\n * - `ERR_VALIDATION`: Schema validation error\n * - `ERR_INVARIANT`: Invariant validation error\n * - `ERR_CONCURRENCY`: Optimistic concurrency validation error on commits\n */\nexport const Errors = {\n ValidationError: \"ERR_VALIDATION\",\n InvariantError: \"ERR_INVARIANT\",\n ConcurrencyError: \"ERR_CONCURRENCY\",\n StreamClosedError: \"ERR_STREAM_CLOSED\",\n NonRetryableError: \"ERR_NON_RETRYABLE\",\n StoreError: \"ERR_STORE\",\n} as const;\n\n/**\n * Thrown when an action or event payload fails Zod schema validation.\n *\n * This error indicates that data doesn't match the expected schema defined\n * for an action or event. The `details` property contains the Zod validation\n * error with specific information about what failed.\n *\n * @example Catching validation errors\n * ```typescript\n * import { ValidationError } from \"@rotorsoft/act\";\n *\n * try {\n * await app.do(\"createUser\", target, {\n * email: \"invalid-email\", // Missing @ symbol\n * age: -5 // Negative age\n * });\n * } catch (error) {\n * if (error instanceof ValidationError) {\n * console.error(\"Validation failed for:\", error.target);\n * console.error(\"Invalid payload:\", error.payload);\n * console.error(\"Validation details:\", error.details);\n * // details contains Zod error with field-level info\n * }\n * }\n * ```\n *\n * @example Logging validation details\n * ```typescript\n * try {\n * await app.do(\"updateProfile\", target, payload);\n * } catch (error) {\n * if (error instanceof ValidationError) {\n * error.details.errors.forEach((err) => {\n * console.error(`Field ${err.path.join(\".\")}: ${err.message}`);\n * });\n * }\n * }\n * ```\n *\n * @see {@link https://zod.dev | Zod documentation} for validation details\n */\nexport class ValidationError extends Error {\n /** The type of target being validated (e.g., \"action\", \"event\") */\n public readonly target: string;\n /** The invalid payload that failed validation */\n public readonly payload: any;\n /** Zod validation error details */\n public readonly details: any;\n\n constructor(target: string, payload: any, details: any) {\n super(`Invalid ${target} payload`);\n this.name = Errors.ValidationError;\n this.target = target;\n this.payload = payload;\n this.details = details;\n }\n}\n\n/**\n * Thrown when a business rule (invariant) is violated during action execution.\n *\n * Invariants are conditions that must hold true for an action to succeed.\n * They're checked after loading the current state but before emitting events.\n * This error provides complete context about what action was attempted and\n * why it was rejected.\n *\n * @template TState - State schema type\n * @template TEvents - Event schemas type\n * @template TActions - Action schemas type\n * @template TKey - Action name\n * @template TActor - Actor type extending base Actor\n *\n * @example Catching invariant violations\n * ```typescript\n * import { InvariantError } from \"@rotorsoft/act\";\n *\n * try {\n * await app.do(\"withdraw\",\n * { stream: \"account-123\", actor: { id: \"user1\", name: \"Alice\" } },\n * { amount: 1000 }\n * );\n * } catch (error) {\n * if (error instanceof InvariantError) {\n * console.error(\"Action:\", error.action);\n * console.error(\"Reason:\", error.description);\n * console.error(\"Current state:\", error.snapshot.state);\n * console.error(\"Attempted payload:\", error.payload);\n * }\n * }\n * ```\n *\n * @example User-friendly error messages\n * ```typescript\n * try {\n * await app.do(\"closeTicket\", target, payload);\n * } catch (error) {\n * if (error instanceof InvariantError) {\n * // Present friendly message to user\n * if (error.description === \"Ticket must be open\") {\n * return { error: \"This ticket is already closed\" };\n * } else if (error.description === \"Not authorized\") {\n * return { error: \"You don't have permission to close this ticket\" };\n * }\n * }\n * }\n * ```\n *\n * @example Logging with context\n * ```typescript\n * try {\n * await app.do(\"transfer\", target, { to: \"account2\", amount: 500 });\n * } catch (error) {\n * if (error instanceof InvariantError) {\n * logger.error({\n * action: error.action,\n * stream: error.target.stream,\n * actor: error.target.actor,\n * reason: error.description,\n * balance: error.snapshot.state.balance,\n * attempted: error.payload.amount\n * }, \"Invariant violation\");\n * }\n * }\n * ```\n *\n * @see {@link Invariant} for defining business rules\n */\nexport class InvariantError<\n TState extends Schema,\n TEvents extends Schemas,\n TActions extends Schemas,\n TKey extends keyof TActions,\n TActor extends Actor = Actor,\n> extends Error {\n /** The action that was attempted */\n readonly action: TKey;\n /** The action payload that was provided */\n readonly payload: Readonly<TActions[TKey]>;\n /** The target stream and actor context */\n readonly target: Target<TActor>;\n /** The current state snapshot when invariant was checked */\n readonly snapshot: Snapshot<TState, TEvents>;\n /** Human-readable description of why the invariant failed */\n readonly description: string;\n\n constructor(\n action: TKey,\n payload: Readonly<TActions[TKey]>,\n target: Target<TActor>,\n snapshot: Snapshot<TState, TEvents>,\n description: string\n ) {\n super(`${action as string} failed invariant: ${description}`);\n this.name = Errors.InvariantError;\n this.action = action;\n this.payload = payload;\n this.target = target;\n this.snapshot = snapshot;\n this.description = description;\n }\n}\n\n/**\n * Thrown when optimistic concurrency control detects a conflict.\n *\n * This error occurs when trying to commit events to a stream that has been\n * modified by another process since it was last loaded. The version number\n * doesn't match expectations, indicating a concurrent modification.\n *\n * This is a normal occurrence in distributed systems and should be handled\n * by reloading the current state and retrying the action.\n *\n * @example Handling concurrency conflicts with retry\n * ```typescript\n * import { ConcurrencyError } from \"@rotorsoft/act\";\n *\n * async function transferWithRetry(from, to, amount, maxRetries = 3) {\n * for (let attempt = 0; attempt < maxRetries; attempt++) {\n * try {\n * await app.do(\"transfer\",\n * { stream: from, actor: currentUser },\n * { to, amount }\n * );\n * return { success: true };\n * } catch (error) {\n * if (error instanceof ConcurrencyError) {\n * if (attempt < maxRetries - 1) {\n * console.log(`Concurrent modification detected, retrying... (${attempt + 1}/${maxRetries})`);\n * await sleep(100 * Math.pow(2, attempt)); // Exponential backoff\n * continue;\n * }\n * }\n * throw error;\n * }\n * }\n * return { success: false, reason: \"Too many concurrent modifications\" };\n * }\n * ```\n *\n * @example Logging concurrency conflicts\n * ```typescript\n * try {\n * await app.do(\"updateInventory\", target, payload);\n * } catch (error) {\n * if (error instanceof ConcurrencyError) {\n * logger.warn({\n * stream: error.stream,\n * expectedVersion: error.expectedVersion,\n * actualVersion: error.lastVersion,\n * events: error.events.map(e => e.name)\n * }, \"Concurrent modification detected\");\n * }\n * }\n * ```\n *\n * @example User feedback for conflicts\n * ```typescript\n * try {\n * await app.do(\"editDocument\", target, { content: newContent });\n * } catch (error) {\n * if (error instanceof ConcurrencyError) {\n * return {\n * error: \"This document was modified by another user. Please refresh and try again.\",\n * code: \"CONCURRENT_MODIFICATION\"\n * };\n * }\n * }\n * ```\n *\n * @see {@link Store.commit} for version checking details\n */\nexport class ConcurrencyError extends Error {\n /** The stream that had the concurrent modification */\n public readonly stream: string;\n /** The actual current version in the store */\n public readonly lastVersion: number;\n /** The events that were being committed */\n public readonly events: Message<Schemas, keyof Schemas>[];\n /** The version number that was expected */\n public readonly expectedVersion: number;\n\n constructor(\n stream: string,\n lastVersion: number,\n events: Message<Schemas, keyof Schemas>[],\n expectedVersion: number\n ) {\n // Message lists stream + event names only. Payloads remain accessible\n // via `error.events` for callers who need them — keeping them out of\n // the message avoids MB-scale strings on contended writes and keeps\n // potentially-sensitive data out of log streams.\n super(\n `Concurrency error committing \"${events\n .map((e) => `${stream}.${e.name}`)\n .join(\n \", \"\n )}\". Expected version ${expectedVersion} but found version ${lastVersion}.`\n );\n this.name = Errors.ConcurrencyError;\n this.stream = stream;\n this.lastVersion = lastVersion;\n this.events = events;\n this.expectedVersion = expectedVersion;\n }\n}\n\n/**\n * Thrown when attempting to write to a stream that has been closed\n * with a tombstone event.\n *\n * A tombstoned stream is permanently closed — no further actions can\n * be executed against it. The only way to reopen a tombstoned stream\n * is through `Act.close()` with a `restart` callback.\n *\n * @example\n * ```typescript\n * import { StreamClosedError } from \"@rotorsoft/act\";\n *\n * try {\n * await app.do(\"updateTicket\", target, payload);\n * } catch (error) {\n * if (error instanceof StreamClosedError) {\n * console.error(`Stream ${error.stream} is closed`);\n * }\n * }\n * ```\n *\n * @see {@link Act.close} for closing streams\n */\nexport class StreamClosedError extends Error {\n /** The stream that is closed */\n public readonly stream: string;\n\n constructor(stream: string) {\n super(`Stream \"${stream}\" is closed (tombstoned)`);\n this.name = Errors.StreamClosedError;\n this.stream = stream;\n }\n}\n\n/**\n * Thrown by a {@link Store} adapter when an infrastructure operation fails\n * for a reason that is *not* a domain condition — a dropped connection, a\n * transaction rollback, a query timeout. It is the typed boundary between\n * \"the store is unavailable/degraded\" and the domain errors above\n * ({@link ConcurrencyError}, {@link StreamClosedError}), which describe\n * legitimate outcomes the caller should branch on.\n *\n * Adapters wrap their driver errors in `StoreError` (preserving the\n * original via `cause`) so the orchestrator can distinguish a degraded\n * backend from \"no work\" and react accordingly — see the drain circuit\n * breaker, which trips on repeated `StoreError`s and surfaces an\n * `error` lifecycle event instead of silently spinning on a down\n * database.\n *\n * @example\n * ```typescript\n * app.on(\"error\", ({ error, circuit }) => {\n * if (error instanceof StoreError)\n * alert(`store ${error.operation} failing; circuit=${circuit}`);\n * });\n * ```\n */\nexport class StoreError extends Error {\n /** The store operation that failed (e.g. `\"claim\"`, `\"ack\"`, `\"commit\"`). */\n public readonly operation: string;\n\n constructor(operation: string, options?: { cause?: unknown }) {\n super(`Store operation \"${operation}\" failed`, options);\n this.name = Errors.StoreError;\n this.operation = operation;\n }\n}\n\n/**\n * Thrown by a reaction handler to signal that the failure is permanent\n * and the drain pipeline should block the stream immediately, without\n * consuming the rest of the `maxRetries` budget.\n *\n * The drain finalizer detects `instanceof NonRetryableError` and forces\n * `block = options.blockOnError` regardless of `lease.retry`. When\n * `blockOnError` is `false`, behavior is unchanged (drain keeps retrying\n * forever) — the class never overrides the operator's explicit \"never\n * block\" choice.\n *\n * Use this for failures the handler *knows* won't get better on retry:\n * a 4xx from a webhook, a `ZodError` on malformed input, a \"user\n * deleted\" 404 from a downstream API. Use regular `Error` (or a\n * subclass) for transient failures so the existing retry-with-backoff\n * loop applies.\n *\n * @example Wrapping a permanent downstream error\n * ```typescript\n * import { NonRetryableError } from \"@rotorsoft/act\";\n *\n * .on(\"OrderConfirmed\")\n * .do(async (event) => {\n * const res = await fetch(url, ...);\n * if (res.status >= 400 && res.status < 500) {\n * throw new NonRetryableError(\n * `webhook ${url} responded ${res.status}`,\n * { cause: await res.text() }\n * );\n * }\n * if (!res.ok) throw new Error(`webhook ${url} responded ${res.status}`);\n * })\n * ```\n *\n * @example Marking validation failures as non-retryable\n * ```typescript\n * .on(\"PaymentReceived\")\n * .do(async (event) => {\n * const parsed = Schema.safeParse(event.data);\n * if (!parsed.success) {\n * throw new NonRetryableError(\"payment payload failed validation\", {\n * cause: parsed.error,\n * });\n * }\n * // ... handle parsed payload\n * })\n * ```\n */\nexport class NonRetryableError extends Error {\n /** The original failure, if any. Mirrors the standard `Error.cause` shape. */\n public override readonly cause?: unknown;\n\n constructor(message: string, options?: { cause?: unknown }) {\n super(message);\n this.name = Errors.NonRetryableError;\n this.cause = options?.cause;\n }\n}\n","import { type ZodObject, type ZodRawShape, z } from \"zod\";\n// Deep-path import (vs `../internal/index.js`) is deliberate — `_registry` is\n// a side-effect-free leaf, and going through the internal barrel would pull\n// tracing.ts → config.ts in at type-schema load time and crash on TDZ when a\n// test imports a public schema before config is initialized.\nimport { _mark_sensitive, _registry } from \"../internal/sensitive.js\";\n\n/**\n * @packageDocumentation\n * @module act/types\n * @category Types\n * Zod schemas and helpers for the Act Framework.\n */\n\n/**\n * An empty Zod schema (no properties).\n */\nexport const ZodEmpty = z.record(z.string(), z.never());\n\n/**\n * Sensitive-data foundation re-exports (#855 / epic #566).\n *\n * - `REDACTED` / `SHREDDED` — sentinels placed in `event.data[field]`\n * when the caller isn't authorized to see a sensitive field\n * (`.discloses(predicate)` returned `false` or none was declared —\n * recoverable) or the underlying PII was wiped (`Store.forget_pii` —\n * irrecoverable).\n * top-level field names marked via {@link sensitive}. A pure,\n * read-only helper that inspects the out-of-band sensitive registry (a\n * process-global `WeakMap`) `sensitive()` populates, otherwise\n * unreachable from outside the package. Surfaced for adapters and\n * tooling that must reflect input sensitivity on their own wire\n * surface — e.g. the `@rotorsoft/act-http/openapi` emitter marks these\n * fields `writeOnly` + `format: password` so generated clients and\n * Swagger UI don't echo PII freely. Returns `[]` for non-object\n * schemas or objects with no sensitive fields; top-level shape only.\n */\nexport { pii_fields, REDACTED, SHREDDED } from \"../internal/sensitive.js\";\n\n/**\n * Mark a Zod schema as sensitive. Returns the same schema instance — the\n * marker is registered out-of-band so the static type is preserved and the\n * call site reads as a pure annotation.\n *\n * Idempotent: re-wrapping an already-sensitive schema is a no-op.\n *\n * The marker is what the orchestrator inspects to split event payloads into\n * `data` + `pii` on commit, gate reads via `.discloses`, and strip handler\n * payloads. Part of the sensitive-data foundation (#855 / epic #566).\n *\n * @example\n * ```ts\n * import { z } from \"zod\";\n * import { state, sensitive } from \"@rotorsoft/act\";\n *\n * const UserRegistered = z.object({\n * email: sensitive(z.string()),\n * name: sensitive(z.string()),\n * plan: z.enum([\"free\", \"pro\"]), // not sensitive — stays in events.data\n * });\n * ```\n *\n * @param schema - The Zod schema to mark sensitive.\n * @returns The same schema instance, unmodified at the type level.\n */\nexport function sensitive<T extends z.ZodType>(schema: T): T {\n _registry.add(schema, { sensitive: true });\n // Also stamp the def, so the marker survives the clone Zod produces for\n // any refinement chained AFTER this call — `sensitive(z.string()).min(1)`\n // used to lose it silently and write plaintext into `events.data` (#1417).\n _mark_sensitive(schema);\n return schema;\n}\n\n/**\n * Zod schema for an actor (user, system, etc.).\n */\nexport const ActorSchema = z\n .object({\n id: z.string(),\n name: z.string(),\n })\n .loose()\n .readonly();\n\n/**\n * Zod schema for a target (stream and actor info).\n */\nexport const TargetSchema = z\n .object({\n stream: z.string(),\n actor: ActorSchema,\n expectedVersion: z.number().optional(),\n })\n .loose()\n .readonly();\n\n/**\n * Zod schema for causation event metadata.\n */\nexport const CausationEventSchema = z.object({\n id: z.number(),\n name: z.string(),\n stream: z.string(),\n});\n\n/**\n * Zod schema for event metadata (correlation and causation).\n */\nexport const EventMetaSchema = z\n .object({\n correlation: z.string(),\n causation: z.object({\n action: TargetSchema.and(z.object({ name: z.string() })).optional(),\n event: CausationEventSchema.optional(),\n }),\n })\n .readonly();\n\n/**\n * Zod schema for committed event metadata (id, stream, version, created, meta).\n */\nexport const CommittedMetaSchema = z\n .object({\n id: z.number(),\n stream: z.string(),\n version: z.number(),\n created: z.date(),\n meta: EventMetaSchema,\n })\n .readonly();\n\n/**\n * Type representing the full state schema for a domain.\n * @property events - Map of event names to Zod schemas.\n * @property actions - Map of action names to Zod schemas.\n * @property state - Zod schema for the state object.\n */\nexport type StateSchema = Readonly<{\n events: Record<string, ZodObject<ZodRawShape> | typeof ZodEmpty>;\n actions: Record<string, ZodObject<ZodRawShape> | typeof ZodEmpty>;\n state: ZodObject<ZodRawShape>;\n}>;\n\n/**\n * Query options for event store queries.\n */\nexport const QuerySchema = z\n .object({\n stream: z.string().optional(),\n names: z.string().array().optional(),\n before: z.number().optional(),\n after: z.number().optional(),\n limit: z.number().optional(),\n created_before: z.date().optional(),\n created_after: z.date().optional(),\n backward: z.boolean().optional(),\n correlation: z.string().optional(),\n with_snaps: z.boolean().optional(),\n stream_exact: z.boolean().optional(),\n })\n .readonly();\n","/**\n * @module sensitive\n * @category Internal\n *\n * Internal mechanics for the sensitive-data foundation (#855 / epic #566).\n * The public surface (`sensitive(zodType)`) lives at `libs/act/src/sensitive.ts`\n * and re-exports `REDACTED` / `SHREDDED` from here; this module holds the\n * registry plus the helpers the orchestrator calls during commit, load, and\n * handler dispatch.\n *\n * - `_registry` — process-global `z.registry<{ sensitive: true }>()`. Public\n * `sensitive()` adds to it; the helpers in this module read it.\n * - `pii_fields(schema)` — walk a Zod schema's top-level shape, return the\n * keys marked via `sensitive(...)`.\n * - `pii_gate(event, fields, predicate, actor)` — produce the external\n * view: plaintext when authorized, `[REDACTED]` when not, `[SHREDDED]`\n * when the underlying pii column is null.\n * - `pii_strip(event, fields)` — remove sensitive keys entirely\n * before invoking projection / reaction handlers.\n *\n * @internal\n */\n\nimport { z } from \"zod\";\nimport type { Actor, Committed, Schemas } from \"../types/index.js\";\n\n/**\n * Sentinel placed in `event.data[field]` when the caller isn't authorized to\n * see the sensitive field — either `.discloses(predicate)` returned `false`,\n * or no predicate was declared (framework default-deny). Recoverable: a\n * properly-authorized read returns the plaintext.\n *\n * Re-exported from `libs/act/src/sensitive.ts` as part of the public surface.\n */\nexport const REDACTED = \"[REDACTED]\" as const;\n\n/**\n * Sentinel placed in `event.data[field]` when the underlying PII payload has\n * been wiped via `Store.forget_pii(stream)` — the row's pii column is `NULL`\n * and the original plaintext is gone forever. Irrecoverable.\n *\n * Re-exported from `libs/act/src/sensitive.ts` as part of the public surface.\n */\nexport const SHREDDED = \"[SHREDDED]\" as const;\n\n/**\n * Process-global registry holding every Zod schema marked sensitive. Backed\n * by a `WeakMap`, so wrapper-created instances (`.optional()`, `.nullable()`,\n * `.default()`) that chain off a marked schema produce *new* schema instances\n * the registry doesn't track; the field walker handles those via unwrap.\n *\n * Exported so the public `sensitive(zodType)` wrapper can call `_registry.add`.\n * Underscore prefix marks \"framework-private, don't touch from user code.\"\n *\n * @internal\n */\nexport const _registry = z.registry<{ sensitive: true }>();\n\n/**\n * Marker key stamped onto a schema's own `def`. Zod clones a schema on every\n * refinement (`.min()`, `.email()`, `.trim()`, `.describe()`, `.refine()`,\n * `.transform()`, …) via `{...def}`, which copies own symbol keys — so a\n * marker on the def survives the whole chain, while the `_registry` WeakMap\n * (keyed on the *instance*) does not (#1417).\n *\n * The registry is still populated and still consulted: it covers schemas\n * marked before this key existed, and it is the mechanism the public\n * `sensitive()` doc-comment describes.\n *\n * @internal\n */\nexport const _SENSITIVE = Symbol.for(\"act.sensitive\");\n\n/**\n * Stamp the def-level marker. Called by the public `sensitive()` alongside\n * `_registry.add`.\n *\n * @internal\n */\nexport function _mark_sensitive(schema: z.ZodType): void {\n const def = (\n schema as unknown as {\n _zod?: { def?: Record<PropertyKey, unknown> };\n }\n )._zod?.def;\n if (def) def[_SENSITIVE] = true;\n}\n\n/**\n * True when the given schema was marked via `sensitive(...)`.\n *\n * Walks through Zod wrapper layers (`.optional()`, `.nullable()`,\n * `.default()`, `.readonly()`) by following `_def.innerType` until it reaches\n * a non-wrapper schema, then checks the registry. Wrappers create new schema\n * instances; the marker lives on the *inner* schema the user wrapped, so we\n * test that one.\n *\n * @internal\n */\nexport function is_pii(schema: z.ZodType): boolean {\n let cur: z.ZodType = schema;\n while (true) {\n if (_registry.has(cur)) return true;\n // Def-level marker: survives the clone a refinement produces, which the\n // instance-keyed registry above cannot (#1417).\n const def = (\n cur as unknown as {\n _zod?: { def?: Record<PropertyKey, unknown> };\n }\n )._zod?.def;\n if (def?.[_SENSITIVE] === true) return true;\n const inner = (cur as { _def?: { innerType?: z.ZodType } })._def?.innerType;\n if (!inner || inner === cur) return false;\n cur = inner;\n }\n}\n\n/**\n * Derive the list of sensitive field names from an event's Zod schema.\n *\n * Walks the top-level shape of a `z.object({...})` and returns the keys whose\n * schema (after unwrapping optional/nullable/default wrappers) was marked via\n * `sensitive(...)`. Returns an empty array for non-object schemas or events\n * with no sensitive fields — the common-case zero-cost path.\n *\n * Only the top-level shape is walked. Sensitive fields nested inside a\n * `z.object` declared inside the event payload would require recursive\n * descent; that's deferred until a real callsite needs it.\n *\n * @internal — consumed by the registry's `sensitive_fields(event_name)` lookup.\n */\nexport function pii_fields(schema: z.ZodType): readonly string[] {\n const shape = (schema as { shape?: Record<string, z.ZodType> }).shape;\n if (shape && typeof shape === \"object\") {\n const fields: string[] = [];\n for (const key of Object.keys(shape)) {\n if (is_pii(shape[key])) fields.push(key);\n }\n return fields;\n }\n // A union event has no top-level shape, so reading `.shape` alone returned\n // [] and dropped EVERY marker in every variant (#1417). Take the union of\n // the options' field sets: a key that is sensitive in any variant must be\n // split, because the stored payload could be that variant. This is not the\n // documented nested-object carve-out below — here the union IS the top\n // level.\n const options = (schema as { options?: unknown }).options;\n if (Array.isArray(options)) {\n const fields = new Set<string>();\n for (const option of options)\n for (const key of pii_fields(option as z.ZodType)) fields.add(key);\n return [...fields];\n }\n return [];\n}\n\n/**\n * Split an emitted event's `data` into `data` (non-sensitive) + `pii`\n * (sensitive) using the field list precomputed at build time. Used by the\n * State's `_pii_split` decorator just before `Store.commit`.\n *\n * Single forward pass over `Object.keys(validated)` — same shape as the\n * spread-and-delete-free implementation in slice 3, just hoisted out of the\n * orchestrator hot path so it's only invoked when the State actually has a\n * sensitive event.\n *\n * @internal\n */\nexport function pii_split<TName, TData extends Record<string, unknown>>(\n emitted: { name: TName; data: TData },\n fields: readonly string[]\n): { name: TName; data: TData; pii: Record<string, unknown> } {\n const data = { ...emitted.data };\n const pii: Record<string, unknown> = {};\n for (const f of fields) {\n if (f in data) {\n pii[f] = data[f];\n delete data[f];\n }\n }\n return { name: emitted.name, data, pii };\n}\n\n/**\n * Build the **external view** of a committed event — the form returned by\n * `load()`, `query()`, `query_array()`, and the snapshot in `do()`'s reply.\n *\n * Only ever reached for events that declare sensitive fields — the sole caller\n * is {@link make_gate}, which the builder invokes exclusively for sensitive\n * events; non-sensitive events short-circuit to {@link IDENTITY_GATE} before\n * they get here. `fields` is therefore guaranteed non-empty (same contract as\n * {@link pii_strip}).\n *\n * - Event whose `pii` payload is null/undefined → substitute {@link SHREDDED}\n * for each declared field. Irrecoverable, so no predicate check.\n * - Event with a `pii` payload, predicate returns `true` → merge `pii` into\n * `data` (plaintext).\n * - Event with a `pii` payload, predicate returns `false` OR no predicate\n * declared (framework default-deny) → substitute {@link REDACTED} for each\n * declared field.\n *\n * @internal\n */\nexport function pii_gate<TEvents extends Schemas, TKey extends keyof TEvents>(\n event: Committed<TEvents, TKey>,\n fields: readonly string[],\n predicate: ((event: any, actor: Actor) => boolean) | null,\n actor: Actor | undefined\n): Committed<TEvents, TKey> {\n const data = event.data as Record<string, unknown>;\n // The external view NEVER carries the isolated `pii` sidecar — dropping it\n // is the whole point of the gate. Keeping it (an earlier `...event` spread)\n // leaked plaintext PII on every gated read surface (`load`, `query`,\n // `query_array`) even while `data` was correctly redacted (#1277). Strip it\n // once here; the plaintext lives in `data` only on the authorized path.\n const { pii, ...rest } = event as Committed<TEvents, TKey> & {\n pii?: Record<string, unknown> | null;\n };\n if (pii == null) {\n const shredded: Record<string, unknown> = { ...data };\n for (const f of fields) shredded[f] = SHREDDED;\n return { ...rest, data: shredded as Committed<TEvents, TKey>[\"data\"] };\n }\n // Plaintext path requires both an actor AND a predicate that allows. Missing\n // either → default-deny → REDACTED.\n const allowed = !!actor && !!predicate && predicate(event, actor);\n if (allowed) {\n return {\n ...rest,\n data: { ...data, ...pii } as Committed<TEvents, TKey>[\"data\"],\n };\n }\n const redacted: Record<string, unknown> = { ...data };\n for (const f of fields) redacted[f] = REDACTED;\n return { ...rest, data: redacted as Committed<TEvents, TKey>[\"data\"] };\n}\n\n/**\n * A prebuilt per-event read gate: given a committed event and the reading\n * actor, return the caller-visible form. This is the single gating primitive\n * the builder prebuilds for **every** read surface — both the actor-less\n * `query` / `query_array` (which pass no actor → default-deny) and the\n * actor-aware `load` / `do`-return view (which pass the reader). Non-sensitive\n * events use the shared {@link IDENTITY_GATE}; sensitive events use a redactor\n * built by {@link make_gate} that closes over the field list and the state's\n * disclosure predicate, so the read path never recomputes the sensitive-field\n * lookup nor allocates per event.\n *\n * The `actor` is optional so the actor-less surfaces can call `gate(event)`.\n *\n * @internal\n */\nexport type EventGate = <TEvents extends Schemas, TKey extends keyof TEvents>(\n event: Committed<TEvents, TKey>,\n actor?: Actor\n) => Committed<TEvents, TKey>;\n\n/**\n * Shared zero-cost gate for every event with no `sensitive(...)` fields — a\n * single frozen reference the builder hands back for non-sensitive events, so\n * the common path is one `Map` miss and an identity call, no allocation. This\n * is the \"by default, return the event\" half of the prebuilt per-event gate.\n *\n * @internal\n */\nexport const IDENTITY_GATE: EventGate = (event) => event;\n\n/**\n * Prebuild a read gate for a sensitive event, capturing its field list and the\n * disclosure predicate once at build time. The returned closure defers to\n * {@link pii_gate} with the reading actor supplied per call:\n *\n * - `predicate = null` (the actor-less `query` surfaces, or a state that never\n * declared `.discloses`) → default-deny: declared fields come back\n * {@link REDACTED} (or {@link SHREDDED} once the pii column is forgotten).\n * - `predicate` set + an authorized actor → plaintext merged into `data`.\n *\n * Either way the isolated `pii` sidecar is dropped. The builder stores one gate\n * per sensitive event (per state for the load path; predicate-less for the\n * query path); non-sensitive events fall back to {@link IDENTITY_GATE}.\n *\n * @internal\n */\nexport function make_gate(\n fields: readonly string[],\n predicate: ((event: any, actor: Actor) => boolean) | null\n): EventGate {\n return (event, actor) => pii_gate(event, fields, predicate, actor);\n}\n\n/**\n * Build the **handler view** — sensitive keys removed entirely from `data`\n * and the `pii` field dropped from the event. Used before invoking projection\n * handlers and reaction handlers, which never see PII by framework rule.\n *\n * Different from {@link pii_gate} (which substitutes {@link REDACTED} or\n * {@link SHREDDED}) — projection tables and reaction sinks shouldn't even\n * structurally observe the keys, so a handler that mistakenly writes\n * `event.data.email` into a column would get `undefined`, not a sentinel\n * string that looks like real data. The strictness is deliberate.\n *\n * Reactions that genuinely need PII (e.g. a welcome-email reaction reading\n * `email`) opt back in by explicitly calling `app.load(stream, { actor:\n * system_actor })` inside the handler — pulling PII through the gate at the\n * call site makes the security-relevant path visible in code review.\n *\n * @internal\n */\nexport function pii_strip<\n TEvents extends Schemas,\n TKey extends keyof TEvents & string,\n>(\n event: Committed<TEvents, TKey>,\n fields: readonly string[]\n): Committed<TEvents, TKey> {\n // Contract: `fields` is non-empty. `build_handle` / `build_handle_batch`\n // filter on `fields.length > 0` before invocation.\n const data = event.data as Record<string, unknown>;\n const stripped: Record<string, unknown> = {};\n for (const k of Object.keys(data)) {\n if (!fields.includes(k)) stripped[k] = data[k];\n }\n const { pii: _drop_pii, ...rest } = event as Committed<TEvents, TKey> & {\n pii?: unknown;\n };\n return {\n ...rest,\n data: stripped as Committed<TEvents, TKey>[\"data\"],\n } as Committed<TEvents, TKey>;\n}\n","/**\n * @packageDocumentation\n * @module act/types\n * Barrel file for Act Framework core types.\n *\n * Re-exports all major type definitions for actions, errors, ports, reactions, registries, and schemas.\n * Also defines common environment and log level types/constants for configuration and logging.\n *\n * @remarks\n * Import from this module to access all core framework types in one place.\n */\nexport type * from \"./action.js\";\nexport type * from \"./audit.js\";\nexport * from \"./errors.js\";\nexport type * from \"./ports.js\";\nexport type * from \"./reaction.js\";\nexport type * from \"./registry.js\";\nexport * from \"./schemas.js\";\n\n/**\n * Supported runtime environments for the framework.\n * - `development`: Local development\n * - `test`: Automated testing\n * - `staging`: Pre-production\n * - `production`: Live/production\n */\nexport const Environments = [\n \"development\",\n \"test\",\n \"staging\",\n \"production\",\n] as const;\n\n/**\n * Type representing a valid environment string.\n */\nexport type Environment = (typeof Environments)[number];\n\n/**\n * Supported log levels for framework logging.\n * - `fatal`, `error`, `warn`, `info`, `debug`, `trace`\n */\nexport const LogLevels = [\n \"fatal\",\n \"error\",\n \"warn\",\n \"info\",\n \"debug\",\n \"trace\",\n] as const;\n\n/**\n * Type representing a valid log level string.\n */\nexport type LogLevel = (typeof LogLevels)[number];\n","import { prettifyError, ZodError, type ZodType } from \"zod\";\nimport { config } from \"./config.js\";\nimport { ValidationError } from \"./types/index.js\";\n\n/**\n * @module utils\n * @category Utilities\n *\n * Small utilities used across the framework:\n * - {@link validate} — parse a payload against a Zod schema, throwing\n * {@link ValidationError} on failure.\n * - {@link extend} — validate a source object and merge into defaults.\n * - {@link sleep} — async delay (default duration from `config().sleepMs`).\n */\n\n/**\n * Parse `payload` against `schema`, returning the validated value or throwing\n * a {@link ValidationError} with prettified Zod details. When `schema` is\n * omitted, returns `payload` unchanged. The framework calls this for every\n * `app.do()` action, every emitted event, and every state init.\n *\n * @example\n * ```typescript\n * const UserSchema = z.object({ email: z.string().email() });\n * const user = validate(\"User\", { email: \"alice@example.com\" }, UserSchema);\n * ```\n *\n * @see {@link ValidationError}\n */\nexport const validate = <S>(\n target: string,\n payload: Readonly<S>,\n schema?: ZodType<S>\n): Readonly<S> => {\n try {\n return schema ? schema.parse(payload) : payload;\n } catch (error) {\n if (error instanceof ZodError) {\n throw new ValidationError(target, payload, prettifyError(error));\n }\n throw new ValidationError(target, payload, error);\n }\n};\n\n/**\n * Validate `source` against `schema` and return a new object that merges\n * `source` over the optional `target` defaults. Used by {@link config} for\n * env-var-overrides-defaults patterns; safe to call elsewhere — it never\n * mutates `target`.\n *\n * @example\n * ```typescript\n * const schema = z.object({ host: z.string(), port: z.number() });\n * const cfg = extend({ port: 8080 }, schema, { host: \"localhost\", port: 80 });\n * // → { host: \"localhost\", port: 8080 }\n * ```\n *\n * @throws {@link ValidationError} if `source` fails the schema.\n */\nexport const extend = <\n S extends Record<string, unknown>,\n T extends Record<string, unknown>,\n>(\n source: Readonly<S>,\n schema: ZodType<S>,\n target?: Readonly<T>\n): Readonly<S & T> => {\n const value = validate(\"config\", source, schema);\n return { ...target, ...value } as Readonly<S & T>;\n};\n\n/**\n * Pause for `ms` milliseconds (or `config().sleepMs` when omitted — `100ms`\n * in dev, `0ms` in tests). Used by adapters to simulate async I/O.\n *\n * @example\n * ```typescript\n * await sleep(); // default delay from config\n * await sleep(500); // explicit 500ms\n * ```\n */\nexport async function sleep(ms?: number) {\n return new Promise((resolve) => setTimeout(resolve, ms ?? config().sleepMs));\n}\n\n/**\n * Regex metacharacters that, when present in a reaction `source`, make it a\n * pattern rather than a literal stream name. A source containing none of\n * these is a bare stream name — the common case — and every claim/fetch\n * site matches it by string equality on the store's stream index. A source\n * containing any of them is compiled as a RegExp and matched against\n * candidate streams with the caller's own anchoring (e.g. `^(A|B)$`).\n */\nconst SOURCE_METACHARACTERS = /[\\^$.*+?()[\\]{}|\\\\]/;\n\n/**\n * True when `source` is a **literal** stream name — it carries no regex\n * metacharacter, so every adapter treats it as an exact match. This is the\n * fast, index-friendly path and covers every autoclose/dynamic-resolver\n * source (bare stream names). A `false` return means the source is a\n * **pattern** (contains `^ $ . * + ? ( ) [ ] { } | \\`) and must be compiled\n * as a RegExp before matching — the shape the calculator's static\n * `source: \"^(A|B)$\"` reaction relies on.\n *\n * The single source of truth for literal-vs-pattern classification across\n * the InMemory has-work probe, the drain fetch path, and the SQL adapters,\n * so all three agree on which sources take the exact path.\n *\n * @example\n * ```typescript\n * is_literal_source(\"Board\"); // → true (exact lookup)\n * is_literal_source(\"^(A|B)$\"); // → false (compile as RegExp)\n * ```\n */\nexport function is_literal_source(source: string): boolean {\n return !SOURCE_METACHARACTERS.test(source);\n}\n\n/**\n * Matches an ISO-8601 timestamp, the shape `JSON.stringify` produces for a\n * `Date`.\n *\n * @internal\n */\nconst ISO_8601 =\n /^(\\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(\\.\\d+)?(Z|[+-][0-2][0-9]:[0-5][0-9])?$/;\n\n/**\n * Revive ISO-8601-shaped strings into `Date`s during `JSON.parse`.\n *\n * **The framework no longer uses this.** Dates are resolved from the declared\n * `z.date()` paths at build time and converted on read, so a field declared\n * `z.string()` keeps its string even when the value looks like a timestamp\n * ([#1556](https://github.com/Rotorsoft/act-root/issues/1556)). Adapters\n * return what they stored; typing is the orchestrator's job.\n *\n * Kept exported for host applications that parse Act JSON themselves and want\n * the old shape-based behaviour. New code should let the schema decide.\n *\n */\nexport const dateReviver = (_key: string, value: unknown): unknown =>\n typeof value === \"string\" && ISO_8601.test(value) ? new Date(value) : value;\n","/**\n * @module scoped\n * @category Internal\n *\n * Ambient execution context — every `AsyncLocalStorage` the framework owns\n * lives here, and so does every operation on one. No other module calls\n * `.run()` or `.getStore()`; they ask for a runner or a reader instead, so\n * this file is the single place to look at the abstraction.\n *\n * Two contexts:\n *\n * - **ports** — the active Act's store/cache bag, so `store()` / `cache()`\n * resolve per-Act rather than per-process (ACT-501). Installed by the\n * orchestrator via {@link make_run_scoped}, read by the port singletons\n * via {@link current_ports}.\n * - **reaction** — the event a handler is processing, so a dispatch made\n * anywhere inside that handler can thread `reactingTo` whichever `IAct`\n * reference made the call (#1541). Installed via {@link run_reacting},\n * read at the orchestrator boundary via {@link current_reacting}.\n *\n * Keeping the mechanics here keeps ambient state out of `internal/`, whose\n * modules are stateless implementations that receive what they need.\n *\n * @internal\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport type {\n Actor,\n Cache,\n Committed,\n IAct,\n Schemas,\n Store,\n} from \"./types/index.js\";\n\n/** Per-Act ports bag (ACT-501). Both required together — a shared cache across stores would collide on stream keys. */\nexport type Scoped = {\n readonly store: Store;\n readonly cache: Cache;\n};\n\n/**\n * AsyncLocalStorage carrying the active Act's ports.\n *\n * Exported for in-repo tooling that measures the context itself (the\n * scope-overhead bench). NOT public: `ports.ts` no longer re-exports it and\n * `index.ts` reaches this module only for the `Scoped` type, so it is not\n * importable from the package. Everything else uses the helpers below.\n */\nexport const scoped = new AsyncLocalStorage<Scoped>();\n\n/**\n * The reaction currently running, as a box the handler can empty.\n *\n * A frame captured by work the handler started and did not await outlives the\n * handler and can never be unbound. The *frame* is unreclaimable, but the\n * *box inside it* is not: clearing `event` as the handler settles turns every\n * later read through that frame into \"no reaction\", without anyone having to\n * ask a second question (#1562).\n *\n * Not reachable from `index.ts`.\n */\ntype Reacting = { event: Committed<Schemas, string> | undefined };\n\nconst reacting = new AsyncLocalStorage<Reacting>();\n\n/**\n * Builds the runner an Act uses to enter its own port scope.\n *\n * A scoped Act wraps every entry point so `store()`/`cache()` resolve to its\n * bag; a singleton Act needs no frame at all, so the runner collapses to\n * calling `fn` — the non-scoped path pays nothing.\n *\n * @internal\n */\nexport function make_run_scoped(\n bag: Scoped | undefined\n): <T>(fn: () => Promise<T>) => Promise<T> {\n return bag ? (fn) => scoped.run(bag, fn) : (fn) => fn();\n}\n\n/** The active Act's ports, or `undefined` on the singleton path. @internal */\nexport function current_ports(): Scoped | undefined {\n return scoped.getStore();\n}\n\n/**\n * Runs `fn` with `event` installed as the triggering-event context.\n *\n * Entered per payload rather than per lease: the context has to unwind with\n * the handler so it never reaches the drain cycle, and work a handler started\n * without awaiting has to resume into its own event's frame.\n *\n * @internal\n */\nexport function run_reacting<T>(\n event: Committed<Schemas, string>,\n fn: () => Promise<T>\n): Promise<T> {\n const box: Reacting = { event };\n return reacting.run(box, async () => {\n try {\n return await fn();\n } finally {\n // Anything still running keeps this frame; from here it reads empty.\n box.event = undefined;\n }\n });\n}\n\n/**\n * The event being reacted to, or `undefined` outside a running handler —\n * including inside work that outlived one.\n *\n * @internal\n */\nexport function current_reacting(): Committed<Schemas, string> | undefined {\n return reacting.getStore()?.event;\n}\n\n/**\n * Everything a reaction handler runs inside: the `IAct` facade it is handed\n * as its third argument, and the triggering-event context that every dispatch\n * made within it resolves — including one through a captured `app`.\n *\n * Built here rather than in the dispatcher so the dispatcher never has to\n * know how either half works; it receives this whole and calls it.\n *\n * @internal\n */\nexport type ReactionScope<\n TEvents extends Schemas,\n TActions extends Schemas,\n TActor extends Actor = Actor,\n> = {\n readonly app: IAct<TEvents, TActions, TActor>;\n readonly run: <T>(\n event: Committed<Schemas, string>,\n fn: () => Promise<T>\n ) => Promise<T>;\n};\n\n/**\n * Assembles the reaction scope from the orchestrator's bound `IAct` methods.\n *\n * @internal\n */\nexport function make_reaction_scope<\n TEvents extends Schemas,\n TActions extends Schemas,\n TActor extends Actor = Actor,\n>(\n app: IAct<TEvents, TActions, TActor>\n): ReactionScope<TEvents, TActions, TActor> {\n return { app, run: run_reacting };\n}\n","import { ConsoleLogger } from \"./adapters/console-logger.js\";\nimport { InMemoryCache } from \"./adapters/in-memory-cache.js\";\nimport { InMemoryStore } from \"./adapters/in-memory-store.js\";\nimport { config } from \"./config.js\";\nimport { register_disposer, run_disposers } from \"./disposers.js\";\nimport { current_ports } from \"./scoped.js\";\nimport type {\n Cache,\n Disposable,\n Disposer,\n Logger,\n Store,\n} from \"./types/index.js\";\n\n// `Scoped` is the type of the public `ActOptions.scoped` bag, so it stays\n// exported. The AsyncLocalStorage carrying it does not: it is a mechanism,\n// it was only ever reachable because `index.ts` star-exports this module,\n// and its own doc-comment already declared it internal.\nexport type { Scoped } from \"./scoped.js\";\n\n/**\n * Port/adapter infrastructure for the Act framework.\n *\n * All infrastructure concerns (logging, storage, caching) are managed as\n * singleton adapters injected via port functions. Each port follows the same\n * pattern: first call wins with a sensible default, optional adapter injection.\n *\n * - `log()` — structured logging (default: ConsoleLogger)\n * - `store()` — event persistence (default: InMemoryStore)\n * - `cache()` — state checkpoints (default: InMemoryCache)\n * - `dispose()` — register cleanup functions for graceful shutdown\n *\n * @module ports\n */\n\n/**\n * List of exit codes for process termination. Consumed by signal handlers\n * and {@link disposeAndExit}; not part of the user-facing surface.\n *\n * @internal\n */\nexport const ExitCodes = [\"ERROR\", \"EXIT\"] as const;\n\n/**\n * Type for allowed exit codes.\n *\n * - `\"ERROR\"` — abnormal termination (uncaught exception, unhandled rejection)\n * - `\"EXIT\"` — clean shutdown (SIGINT, SIGTERM, or manual trigger)\n *\n * @internal\n */\nexport type ExitCode = (typeof ExitCodes)[number];\n\n// ---------------------------------------------------------------------------\n// Port factory\n// ---------------------------------------------------------------------------\n\n/**\n * Factory function that creates or returns the injected adapter.\n * @internal\n */\ntype Injector<Port extends Disposable> = (adapter?: Port) => Port;\n\n/** Singleton adapter registry, keyed by injector function name. */\nconst adapters = new Map<string, Disposable>();\n\n/**\n * Creates a singleton port with optional adapter injection.\n *\n * The first call initializes the adapter (using the provided adapter or the\n * injector's default). Subsequent calls return the cached singleton. Adapters\n * are disposed in reverse registration order during {@link disposeAndExit}.\n *\n * @param injector - Named function that creates the default adapter\n * @returns Port function: call with no args to get the singleton, or pass an\n * adapter on the first call to override the default\n *\n * @example\n * ```typescript\n * const store = port(function store(adapter?: Store) {\n * return adapter || new InMemoryStore();\n * });\n * const s = store(); // InMemoryStore\n * ```\n */\nexport function port<Port extends Disposable>(injector: Injector<Port>) {\n return (adapter?: Port): Port => {\n if (!adapters.has(injector.name)) {\n const injected = injector(adapter);\n adapters.set(injector.name, injected);\n // log() is now in adapters (or this IS the log port we just registered),\n // so the recursive call resolves immediately. Routing through the logger\n // means level gating (e.g. silenced in tests at fatal) takes effect.\n log().info(`[act] + ${injector.name}:${injected.constructor.name}`);\n }\n return adapters.get(injector.name) as Port;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Ports: log, store, cache\n// ---------------------------------------------------------------------------\n\n/**\n * Gets or injects the singleton logger.\n *\n * By default, Act uses a built-in {@link ConsoleLogger} that emits JSON lines\n * in production (compatible with GCP, AWS CloudWatch, Datadog) and colorized\n * output in development — zero external dependencies.\n *\n * For pino, inject a `PinoLogger` from `@rotorsoft/act-pino` before building\n * your application.\n *\n * @param adapter - Optional logger implementation to inject\n * @returns The singleton logger instance\n *\n * @example Default console logger\n * ```typescript\n * import { log } from \"@rotorsoft/act\";\n * const logger = log();\n * logger.info(\"Application started\");\n * ```\n *\n * @example Injecting pino\n * ```typescript\n * import { log } from \"@rotorsoft/act\";\n * import { PinoLogger } from \"@rotorsoft/act-pino\";\n * log(new PinoLogger({ level: \"debug\", pretty: true }));\n * ```\n *\n * @see {@link Logger} for the interface contract\n * @see {@link ConsoleLogger} for the default implementation\n */\nexport const log = port(function log(adapter?: Logger) {\n const cfg = config();\n return (\n adapter ||\n new ConsoleLogger({\n level: cfg.logLevel,\n pretty: cfg.env !== \"production\",\n })\n );\n});\n\n/**\n * Gets or injects the singleton event store.\n *\n * By default, Act uses an {@link InMemoryStore} suitable for development and\n * testing. For production, inject a persistent store like `PostgresStore` from\n * `@rotorsoft/act-pg` before building your application.\n *\n * **Important:** Store injection must happen before creating any Act instances.\n * Once set, the store cannot be changed without restarting the application.\n *\n * @param adapter - Optional store implementation to inject\n * @returns The singleton store instance\n *\n * @example Default in-memory store\n * ```typescript\n * import { store } from \"@rotorsoft/act\";\n * const s = store();\n * ```\n *\n * @example Injecting PostgreSQL\n * ```typescript\n * import { store } from \"@rotorsoft/act\";\n * import { PostgresStore } from \"@rotorsoft/act-pg\";\n *\n * store(new PostgresStore({\n * host: \"localhost\",\n * port: 5432,\n * database: \"myapp\",\n * user: \"postgres\",\n * password: \"secret\",\n * }));\n * ```\n *\n * @see {@link Store} for the interface contract\n * @see {@link InMemoryStore} for the default implementation\n */\n// ALS check lives outside `port()` — its cache fires only once, so the\n// per-call branch on a scoped Act has to be in the public wrapper.\nconst _store = port(function store(adapter?: Store): Store {\n return adapter ?? new InMemoryStore();\n});\n\nexport const store = ((adapter?: Store): Store => {\n return current_ports()?.store ?? _store(adapter);\n}) as (adapter?: Store) => Store;\n\n/**\n * Gets or injects the singleton cache.\n *\n * By default, Act uses an {@link InMemoryCache} (LRU, maxSize 1000). For\n * distributed deployments, inject a Redis-backed cache before building your\n * application.\n *\n * @param adapter - Optional cache implementation to inject\n * @returns The singleton cache instance\n *\n * @see {@link Cache} for the interface contract\n * @see {@link InMemoryCache} for the default implementation\n */\nconst _cache = port(function cache(adapter?: Cache) {\n return adapter ?? new InMemoryCache();\n});\n\nexport const cache = ((adapter?: Cache): Cache => {\n return current_ports()?.cache ?? _cache(adapter);\n}) as (adapter?: Cache) => Cache;\n\n// ---------------------------------------------------------------------------\n// Disposal\n// ---------------------------------------------------------------------------\n\n/**\n * Registered cleanup functions live in `disposers.ts`, which holds\n * lifetime-bound entries weakly so registering never pins its target for the\n * process lifetime (#1441). The public surface here is unchanged.\n */\n\n/**\n * Disposes all registered adapters and disposers, then exits the process.\n *\n * Execution order:\n * 1. Custom disposers (registered via {@link dispose}) — in reverse order\n * 2. Port adapters (log, store, cache) — in reverse registration order\n * 3. Adapter registry is cleared\n * 4. Process exits (skipped in test environment)\n *\n * In production, `\"ERROR\"` exits are silently ignored to avoid crashing on\n * transient failures (e.g. an uncaught promise in a non-critical path).\n *\n * @param code - Exit code: `\"EXIT\"` for clean shutdown (exit 0),\n * `\"ERROR\"` for abnormal termination (exit 1)\n */\nexport async function disposeAndExit(code: ExitCode = \"EXIT\"): Promise<void> {\n if (code === \"ERROR\" && config().env === \"production\") {\n // Surface the swallow so incident triage can see it. Without this\n // log the framework looks unresponsive after an uncaught exception\n // in prod — exactly when operators most need a breadcrumb.\n log().warn(\n \"disposeAndExit('ERROR') ignored in production — process kept alive\"\n );\n return;\n }\n\n // Run sequentially in reverse registration order so a disposer can rely on\n // later-registered disposers (and adapters on later-registered adapters)\n // having already finished — Promise.all would race them.\n await run_disposers();\n for (const adapter of [...adapters.values()].reverse()) {\n await adapter.dispose();\n log().info(`[act] - ${adapter.constructor.name}`);\n }\n adapters.clear();\n config().env !== \"test\" && process.exit(code === \"ERROR\" ? 1 : 0);\n}\n\n/**\n * Registers a cleanup function for graceful shutdown.\n *\n * Disposers are called automatically on SIGINT, SIGTERM, uncaught exceptions,\n * and unhandled rejections. They execute in reverse registration order before\n * port adapters are disposed.\n *\n * @param disposer - Async function to call during cleanup. Omit to get a\n * reference to {@link disposeAndExit} without registering.\n * @returns Function to manually trigger disposal and exit\n *\n * @example\n * ```typescript\n * import { dispose } from \"@rotorsoft/act\";\n *\n * const db = connectDatabase();\n * dispose(async () => await db.close());\n *\n * // In tests\n * afterAll(async () => await dispose()());\n * ```\n *\n * @see {@link disposeAndExit} for the full shutdown sequence\n */\nexport function dispose(\n disposer?: Disposer\n): (code?: ExitCode) => Promise<void> {\n disposer && register_disposer(disposer);\n return disposeAndExit;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Event name used internally for snapshot events in the event store.\n * Snapshot events store a full state checkpoint, enabling efficient cold-start\n * recovery without replaying the entire event stream.\n */\nexport const SNAP_EVENT = \"__snapshot__\";\n\n/**\n * Event name used internally for tombstone events in the event store.\n * A tombstone marks a stream as permanently closed — no further writes\n * are permitted until the stream is explicitly restarted via `close()`.\n *\n * @see {@link Act.close} for the close-the-books API\n */\nexport const TOMBSTONE_EVENT = \"__tombstone__\";\n\n/**\n * Name of the implicit lane every reaction lands in unless its `.to({lane})`\n * declaration says otherwise (ACT-1103). Acts that don't call\n * `.withLane(...)` see only this lane, and behavior is identical to\n * pre-1103 single-controller drain.\n *\n * Persisted on `streams.lane` and threaded as the strict-typed default in\n * builder generics — `lane?: TLanes` always includes `\"default\"`.\n */\nexport const DEFAULT_LANE = \"default\";\n","/**\n * @packageDocumentation\n * @module act/adapters\n * In-memory event store adapter for the Act Framework.\n *\n * This adapter implements the Store interface and is suitable for development, testing, and demonstration purposes.\n * All data is stored in memory and lost on process exit.\n *\n * @category Adapters\n */\n\nimport { DEFAULT_LANE, SNAP_EVENT, TOMBSTONE_EVENT } from \"../ports.js\";\nimport { ConcurrencyError } from \"../types/errors.js\";\nimport type {\n BlockedLease,\n Committed,\n EventMeta,\n Lease,\n Message,\n Query,\n QueryStatsOptions,\n QueryStreams,\n QueryStreamsResult,\n Schema,\n Schemas,\n Store,\n StreamFilter,\n StreamPosition,\n StreamStats,\n SubscribeInput,\n} from \"../types/index.js\";\nimport { sleep } from \"../utils.js\";\n\n/**\n * @internal\n * Represents an in-memory stream for event processing and leasing.\n */\nclass InMemoryStream {\n readonly stream: string;\n readonly source: string | undefined;\n private _at = -1;\n private _retry = -1;\n private _blocked = false;\n private _error = \"\";\n private _leased_by: string | undefined = undefined;\n private _leased_until: Date | undefined = undefined;\n private _priority = 0;\n private _lane: string = DEFAULT_LANE;\n // Persisted next-visit time (#1090). When set and still in the future, the\n // stream is held out of `claim` entirely — so a deferred reaction is not\n // re-claimed (and `retry` is never bumped) until its due-time passes. Unlike\n // in-process backoff, this is durable store state shared across workers.\n private _deferred_at: number | undefined = undefined;\n // Work mark (#1485): highest event id observed to resolve to this target.\n // `undefined` means UNKNOWN — the row predates the mark, so `claim` falls\n // back to the legacy has-work probe rather than treating it as \"no work\".\n private _correlated_at: number | undefined = undefined;\n\n constructor(\n stream: string,\n source: string | undefined,\n priority = 0,\n lane: string = DEFAULT_LANE\n ) {\n this.stream = stream;\n this.source = source;\n this._priority = priority;\n this._lane = lane;\n }\n\n get priority() {\n return this._priority;\n }\n\n get lane() {\n return this._lane;\n }\n\n /** Replace on every subscribe — current builder config wins on restart. */\n set lane(value: string) {\n this._lane = value;\n }\n\n /**\n * Bump the priority via {@link subscribe}: keeps the maximum across\n * reactions so the highest-priority registrant wins.\n */\n bump_priority(priority: number) {\n if (priority > this._priority) this._priority = priority;\n }\n\n get correlated_at() {\n return this._correlated_at;\n }\n\n /**\n * Raise the work mark via {@link subscribe}: keeps the maximum, for every\n * value including zero and negatives, so a mark never regresses.\n */\n mark(correlated_at: number) {\n if (\n this._correlated_at === undefined ||\n correlated_at > this._correlated_at\n )\n this._correlated_at = correlated_at;\n }\n\n /**\n * Set the priority outright via {@link prioritize}: operator\n * runtime override that ignores the build-time `max()` invariant.\n */\n set_priority(priority: number) {\n this._priority = priority;\n }\n\n get is_available() {\n return (\n !this._blocked &&\n (!this._leased_until || this._leased_until <= new Date()) &&\n // A stream deferred to a future time is not claimable until due (#1090).\n (!this._deferred_at || this._deferred_at <= Date.now())\n );\n }\n\n /**\n * Hold this stream out of `claim` until `deferred_at` (ms since epoch).\n * Set by a deliberate `defer` outcome — not a failure, so retry/blocked\n * state is untouched. Cleared by ack/block/reset/unblock.\n */\n defer(deferred_at: number) {\n this._deferred_at = deferred_at;\n // A defer is not a failure: reset retry so the redelivery after the\n // due-time is a fresh attempt, never accumulating toward maxRetries.\n this._retry = -1;\n }\n\n get at() {\n return this._at;\n }\n\n get retry() {\n return this._retry;\n }\n\n get blocked() {\n return this._blocked;\n }\n\n get error() {\n return this._error;\n }\n\n get leased_by() {\n return this._leased_by;\n }\n\n get leased_until() {\n return this._leased_until;\n }\n\n /** Persisted next-visit time (#1090/#1221), or undefined when no active defer. */\n get deferred_at() {\n return this._deferred_at;\n }\n\n /**\n * Attempt to lease this stream for processing.\n * @param lease - The lease request.\n * @param millis - Lease duration in milliseconds.\n * @returns The granted lease or undefined if blocked.\n */\n lease(lease: Lease, millis: number): Lease {\n if (millis > 0) {\n this._leased_by = lease.by;\n this._leased_until = new Date(Date.now() + millis);\n }\n this._retry = this._retry + 1;\n return {\n stream: this.stream,\n source: this.source,\n at: lease.at,\n by: lease.by,\n retry: this._retry,\n lagging: lease.lagging,\n lane: this._lane,\n };\n }\n\n /**\n * Finalize this stream's lease: ack (advance the watermark) or, when the\n * lease carries a `due` marker, defer (persist the schedule, hold the\n * watermark) — see {@link Store.ack}.\n * @param lease - The lease request.\n */\n ack(lease: Lease) {\n if (this._leased_by === lease.by) {\n this._leased_by = undefined;\n this._leased_until = undefined;\n if (lease.due !== undefined) {\n // Due marker: advance the watermark past the events handled this cycle\n // (`lease.at`) AND schedule the re-visit, persisting the caller's\n // retry — advance and defer are independent legs (#1278). Advancing\n // means the succeeded prefix never re-runs on redelivery. An explicit\n // defer passes `retry: -1` (a defer is not a failure); a backoff retry\n // passes the climbing counter so it keeps accruing toward the block\n // threshold across windows (#1262). Deferred entries are not part of\n // ack's return value.\n this._at = lease.at;\n this._retry = lease.retry;\n this._deferred_at = lease.due;\n return undefined;\n }\n // A successful ack clears the retry budget and any pending defer.\n this._retry = -1;\n this._at = lease.at;\n this._deferred_at = undefined;\n return {\n stream: this.stream,\n source: this.source,\n at: this._at,\n by: lease.by,\n retry: this._retry,\n lagging: lease.lagging,\n lane: this._lane,\n };\n }\n }\n\n /**\n * Block a stream for processing after failing to process and reaching max retries with blocking enabled.\n * @param lease - The lease request.\n * @param error Blocked error message.\n */\n block(lease: Lease, error: string) {\n // Skip already-blocked streams so a redundant block is a no-op, mirroring\n // the SQL adapters' `WHERE ... AND blocked = false` guard (#1263). Without\n // this, re-blocking returns the lease again and emits a spurious duplicate\n // `blocked` lifecycle event that the durable stores suppress.\n if (this._leased_by === lease.by && !this._blocked) {\n this._blocked = true;\n this._error = error;\n // A blocked stream is poison; clear any pending defer (#1090).\n this._deferred_at = undefined;\n return {\n stream: this.stream,\n source: this.source,\n at: this._at,\n by: this._leased_by,\n retry: this._retry,\n error: this._error,\n lagging: lease.lagging,\n lane: this._lane,\n };\n }\n }\n\n /**\n * Reset this stream's watermark and state for replay. The retry counter\n * resets to -1 to match the constructor + ack() invariant (\"released\n * stream\"); the next claim() bumps it to 0 (first attempt).\n */\n reset() {\n this._at = -1;\n this._retry = -1;\n this._blocked = false;\n this._error = \"\";\n this._leased_by = undefined;\n this._leased_until = undefined;\n this._deferred_at = undefined;\n }\n\n /**\n * Clear the blocked flag and lease bookkeeping without touching the\n * watermark. Returns true if the stream was actually blocked (and is\n * now flipped); false otherwise.\n */\n unblock(): boolean {\n if (!this._blocked) return false;\n this._blocked = false;\n this._retry = -1;\n this._error = \"\";\n this._leased_by = undefined;\n this._leased_until = undefined;\n this._deferred_at = undefined;\n return true;\n }\n}\n\n/**\n * In-memory event store implementation.\n *\n * This is the default store used by Act when no other store is injected.\n * It stores all events in memory and is suitable for:\n * - Development and prototyping\n * - Unit and integration testing\n * - Demonstrations and examples\n *\n * **Not suitable for production** - all data is lost when the process exits.\n * Use {@link PostgresStore} for production deployments.\n *\n * The in-memory store provides:\n * - Full {@link Store} interface implementation\n * - Optimistic concurrency control\n * - Stream leasing for distributed processing simulation\n * - Snapshot support\n * - Fast performance (no I/O overhead)\n *\n * **`Store.notify` is intentionally not implemented.** The notify hook is a\n * cross-process wake-up signal — local commits already arm the drain via\n * `do()`. An in-memory store is single-process by definition, so there is\n * no remote writer to be notified of. The {@link Act} orchestrator\n * detects the absence and falls back to the existing debounce/poll path.\n *\n * @example Using in tests\n * ```typescript\n * import { store } from \"@rotorsoft/act\";\n *\n * describe(\"Counter\", () => {\n * beforeEach(async () => {\n * // Reset store between tests\n * await store().seed();\n * });\n *\n * it(\"increments\", async () => {\n * await app.do(\"increment\", target, { by: 5 });\n * const snapshot = await app.load(Counter, \"counter-1\");\n * expect(snapshot.state.count).toBe(5);\n * });\n * });\n * ```\n *\n * @example Explicit instantiation\n * ```typescript\n * import { InMemoryStore } from \"@rotorsoft/act\";\n *\n * const testStore = new InMemoryStore();\n * await testStore.seed();\n *\n * // Use for specific test scenarios\n * await testStore.commit(\"test-stream\", events, meta);\n * ```\n *\n * @example Querying events\n * ```typescript\n * const events: any[] = [];\n * await store().query(\n * (event) => events.push(event),\n * { stream: \"test-stream\" }\n * );\n * console.log(`Found ${events.length} events`);\n * ```\n *\n * @see {@link Store} for the interface definition\n * @see {@link PostgresStore} for production use\n * @see {@link store} for injecting stores\n *\n * @category Adapters\n */\nexport class InMemoryStore implements Store {\n // stored events\n private _events: Committed<Schemas, keyof Schemas>[] = [];\n // next event id — monotonic, never reused. Deletions (truncate, windowed\n // prune) punch holes in the id sequence, so ids are NOT array indexes and\n // NOT `_events.length`; they only stay sorted ascending in `_events`.\n private _next_id = 0;\n // stored stream positions and other metadata\n private _streams: Map<string, InMemoryStream> = new Map();\n /** Correlate checkpoint (#1484): how far the log has been READ. */\n private _correlated_at = -1;\n /**\n * Per-correlator checkpoint and lease (#1532), keyed by correlator.\n *\n * Keyed rather than singular because correlators that look for different\n * things read the log for different reasons: sharing one position lets a\n * partial-behaviour worker inherit another's, and sharing one lease lets\n * one starve the other. Callers that supply no correlator use\n * `_correlated_at` instead, which is the pre-#1532 behaviour.\n */\n private _correlators = new Map<\n string,\n { at: number; by: string; until: number }\n >();\n // last committed version per stream — O(1) replacement for filter-on-commit\n private _stream_versions: Map<string, number> = new Map();\n // max non-snapshot event id per stream — drives the has-work probe in\n // claim(): an O(1) lookup for a literal source, and the per-stream max\n // that a pattern source scans, without touching the full event log.\n private _max_event_id_by_stream: Map<string, number> = new Map();\n // global max non-snapshot event id — fast pre-check for source-less streams in claim()\n private _max_non_snap_event_id = -1;\n // stream → (event_id → cloned sensitive payload). Two-level so `forget_pii`\n // is O(1) — drop the inner Map for the stream and the wipe is done — mirroring\n // the `DELETE WHERE stream = ?` scope that durable adapters get from their\n // stream index. Entries exist only for events committed with a non-null\n // `pii` field; absence means \"no PII\" (returned as `null` on load).\n private _pii: Map<string, Map<number, Record<string, unknown>>> = new Map();\n\n private _reset_indexes() {\n this._events.length = 0;\n this._next_id = 0;\n this._correlated_at = -1;\n this._stream_versions.clear();\n this._max_event_id_by_stream.clear();\n this._max_non_snap_event_id = -1;\n this._pii.clear();\n }\n\n // First index whose event id is greater than `after`. `_events` stays\n // sorted ascending by id (append-only commits; deletions preserve\n // order), so id-bounded scans binary-search their start instead of\n // assuming id === index — an invariant that truncation breaks.\n private _first_index_after(after: number): number {\n let lo = 0;\n let hi = this._events.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (this._events[mid].id > after) hi = mid;\n else lo = mid + 1;\n }\n return lo;\n }\n\n // Attach the isolated PII payload (or null) to an event before handing it to\n // a caller. Allocation-free for events without PII — by far the common case.\n private _with_pii<E extends Schemas>(\n e: Committed<E, keyof E>\n ): Committed<E, keyof E> {\n const pii = this._pii.get(e.stream)?.get(e.id);\n return pii ? ({ ...e, pii } as Committed<E, keyof E>) : e;\n }\n\n /**\n * Dispose of the store and clear all events.\n * @returns Promise that resolves when disposal is complete.\n */\n async dispose() {\n await sleep();\n this._reset_indexes();\n }\n\n /**\n * Seed the store with initial data (no-op for in-memory).\n * @returns Promise that resolves when seeding is complete.\n */\n async seed() {\n await sleep();\n }\n\n /**\n * Drop all data from the store.\n * @returns Promise that resolves when the store is cleared.\n */\n async drop() {\n await sleep();\n this._reset_indexes();\n this._streams = new Map();\n }\n\n private in_query<E extends Schemas>(query: Query, e: Committed<E, keyof E>) {\n if (query.stream) {\n if (query.stream_exact) {\n if (e.stream !== query.stream) return false;\n } else if (!RegExp(query.stream).test(e.stream)) return false;\n }\n if (query.names && !query.names.includes(e.name as string)) return false;\n if (query.correlation && e.meta?.correlation !== query.correlation)\n return false;\n if (e.name === SNAP_EVENT && !query.with_snaps) return false;\n return true;\n }\n\n /**\n * Query events in the store, optionally filtered by query options.\n * @param callback - Function to call for each event.\n * @param query - Optional query options.\n * @returns The number of events processed.\n */\n async query<E extends Schemas>(\n callback: (event: Committed<E, keyof E>) => void,\n query?: Query\n ) {\n await sleep();\n let count = 0;\n // Snapshot resume floor: `with_snaps` requests a resume at the latest\n // snapshot for an exact single stream, so pre-snapshot events aren't read.\n // The orchestrator sets `with_snaps` only for an unbounded current-state\n // load — it suppresses the flag under any `asOf` bound (RFC 1274) — so the\n // store applies the floor whenever asked and never re-checks bounds. An\n // explicit `after` is a separate resume point that wins. No snapshot → -1,\n // i.e. a full scan. Forward starts at the snapshot; backward stops at it.\n let floor_index = -1;\n if (\n query?.with_snaps &&\n query.stream_exact &&\n query.stream !== undefined &&\n query.after === undefined\n ) {\n for (let j = this._events.length - 1; j >= 0; j--) {\n const e = this._events[j];\n if (e.stream === query.stream && e.name === SNAP_EVENT) {\n floor_index = j;\n break;\n }\n }\n }\n if (query?.backward) {\n const floor_id = floor_index >= 0 ? this._events[floor_index].id : -1;\n let i =\n (query?.before !== undefined\n ? this._first_index_after(query.before - 1)\n : this._events.length) - 1;\n while (i >= 0) {\n const e = this._events[i--];\n if (query && !this.in_query(query, e)) continue;\n if (query?.created_before && e.created >= query.created_before)\n continue;\n if (query.after !== undefined && e.id <= query.after) break;\n // Below the resume floor → every remaining (lower-id) event is too,\n // so stop the DESC scan.\n if (floor_id >= 0 && e.id < floor_id) break;\n // `created` is not monotonic with `id` (restore preserves the\n // source timestamps verbatim), so a failing time bound skips the\n // event rather than terminating the scan — matching PG/SQLite,\n // which treat `created` bounds as pure WHERE filters. Only the\n // id-ordered `after` bound above may short-circuit.\n if (query.created_after && e.created <= query.created_after) continue;\n await Promise.resolve(\n callback(this._with_pii(e as Committed<E, keyof E>))\n );\n count++;\n if (query?.limit && count >= query.limit) break;\n }\n } else {\n let i =\n floor_index >= 0\n ? floor_index\n : this._first_index_after(query?.after ?? -1);\n while (i < this._events.length) {\n const e = this._events[i++];\n if (query && !this.in_query(query, e)) continue;\n if (query?.created_after && e.created <= query.created_after) continue;\n if (query?.before !== undefined && e.id >= query.before) break;\n // `created` is not monotonic with `id`, so a failing time bound\n // skips the event rather than terminating the scan — matching\n // PG/SQLite. Only the id-ordered `before` bound above may\n // short-circuit.\n if (query?.created_before && e.created >= query.created_before)\n continue;\n await Promise.resolve(\n callback(this._with_pii(e as Committed<E, keyof E>))\n );\n count++;\n if (query?.limit && count >= query.limit) break;\n }\n }\n return count;\n }\n\n /**\n * Commit one or more events to a stream.\n * @param stream - The stream name.\n * @param msgs - The events/messages to commit.\n * @param meta - Event metadata.\n * @param expectedVersion - Optional optimistic concurrency check.\n * @returns The committed events with metadata.\n * @throws ConcurrencyError if expectedVersion does not match.\n */\n async commit<E extends Schemas>(\n stream: string,\n msgs: Message<E, keyof E>[],\n meta: EventMeta,\n expectedVersion?: number\n ) {\n await sleep();\n const current_version = this._stream_versions.get(stream) ?? -1;\n if (\n typeof expectedVersion === \"number\" &&\n current_version !== expectedVersion\n ) {\n throw new ConcurrencyError(\n stream,\n current_version,\n msgs as Message<Schemas, keyof Schemas>[],\n expectedVersion\n );\n }\n\n let version = current_version + 1;\n let last_non_snap_id = -1;\n const committed = msgs.map(({ name, data, pii }) => {\n const c: Committed<E, keyof E> = {\n id: this._next_id++,\n stream,\n version,\n created: new Date(),\n name,\n data,\n meta,\n };\n // The stored event is the pii-less view — `forget_pii` only has to\n // drop the inner Map for the stream, never the event row. Mandatory\n // clone on the pii payload defends against caller-side mutation.\n this._events.push(c as Committed<Schemas, keyof Schemas>);\n if (pii != null) {\n let per_stream = this._pii.get(stream);\n if (!per_stream) {\n per_stream = new Map();\n this._pii.set(stream, per_stream);\n }\n per_stream.set(c.id, structuredClone(pii) as Record<string, unknown>);\n }\n if (name !== SNAP_EVENT) last_non_snap_id = c.id;\n version++;\n return this._with_pii(c);\n });\n this._stream_versions.set(stream, version - 1);\n if (last_non_snap_id >= 0) {\n this._max_event_id_by_stream.set(stream, last_non_snap_id);\n // commit always assigns a fresh id from the monotonic _next_id, so\n // any non-snap commit strictly raises the global max.\n this._max_non_snap_event_id = last_non_snap_id;\n }\n return committed;\n }\n\n /**\n * Atomically discovers and leases streams for processing.\n * Fuses poll + lease into a single operation.\n * @param lagging - Max streams from lagging frontier.\n * @param leading - Max streams from leading frontier.\n * @param by - Lease holder identifier.\n * @param millis - Lease duration in milliseconds.\n * @returns Granted leases.\n */\n async claim(\n lagging: number,\n leading: number,\n by: string,\n millis: number,\n lane?: string\n ) {\n await sleep();\n // Eligibility is a pure subscription-row predicate (#1488). `claim`\n // never looks at the event log: `correlate` records the highest event id\n // that resolves to a target, and `at < correlated_at` is the whole\n // question. The probe this replaced walked the event index once per\n // eligible subscription, matching `source` literally or as a pattern —\n // matching that now happens in correlate, when it decides what to mark.\n //\n // An unmarked row is not claimable, by definition (#1446): `undefined`\n // means the row has never been correlated, not that it has no work.\n const has_work = (s: InMemoryStream): boolean =>\n s.correlated_at !== undefined && s.at < s.correlated_at;\n const available = [...this._streams.values()].filter(\n (s) =>\n s.is_available && has_work(s) && (lane === undefined || s.lane === lane)\n );\n // Lagging frontier orders by priority DESC (higher first), then by\n // watermark ASC (most-behind first). Mirrors the PG `claim()` SQL\n // — see `libs/act-pg/PERFORMANCE.md` for the benchmark that\n // motivated the priority dimension. A fairness reserve (ACT-1223)\n // carves `fair` slots off the budget and fills them by pure watermark\n // order (priority ignored) so a default-priority lagging stream can\n // never be starved out of the frontier by sustained higher-priority\n // load. With everyone at the same priority both slices order by `at`,\n // so this is a behavioral no-op for existing workloads.\n const fair = lagging >= 2 ? Math.max(1, Math.floor(lagging / 4)) : 0;\n const by_priority = [...available].sort(\n (a, b) => b.priority - a.priority || a.at - b.at\n );\n const priority_slice = by_priority.slice(0, lagging - fair);\n const picked = new Set(priority_slice.map((s) => s.stream));\n const fair_slice = [...available]\n .sort((a, b) => a.at - b.at)\n .filter((s) => !picked.has(s.stream))\n .slice(0, fair);\n const lag = [...priority_slice, ...fair_slice].map((s) => ({\n stream: s.stream,\n source: s.source,\n at: s.at,\n lagging: true,\n }));\n const lead = available\n .sort((a, b) => b.at - a.at)\n .slice(0, leading)\n .map((s) => ({\n stream: s.stream,\n source: s.source,\n at: s.at,\n lagging: false,\n }));\n // deduplicate (a stream can appear in both frontiers)\n const seen = new Set<string>();\n const combined = [...lag, ...lead].filter((p) => {\n if (seen.has(p.stream)) return false;\n seen.add(p.stream);\n return true;\n });\n // lease each atomically\n return combined\n .map((p) =>\n this._streams.get(p.stream)?.lease({ ...p, by, retry: 0 }, millis)\n )\n .filter((l) => !!l);\n }\n\n /**\n * Registers streams for event processing. When the same stream is\n * resubscribed with a different priority, the **maximum** wins — so\n * the highest-priority registered reaction sets the scheduling lane.\n * Use {@link prioritize} for operator runtime overrides.\n *\n * @param streams - Streams to register with optional source + priority.\n * @returns subscribed count and current max watermark.\n */\n async subscribe(\n streams: SubscribeInput[],\n correlated_at?: number,\n correlator?: { key: string; by: string; millis: number }\n ) {\n await sleep();\n\n // The correlate checkpoint is written by its own producer, in the call\n // correlate already makes (#1484). Monotonic: a lower value is ignored.\n //\n // With a correlator the position and the lease are per-key (#1532); a key\n // with no row yet inherits the shared value, so an upgrade does not\n // re-read history.\n let correlating: boolean | undefined;\n /** This correlator's position, once known — always set when keyed. */\n let keyed_at: number | undefined;\n if (correlator) {\n const now = Date.now();\n const held = this._correlators.get(correlator.key);\n // A key with no row yet inherits the shared position, so a new\n // correlator does not re-read history.\n const at = Math.max(held?.at ?? this._correlated_at, correlated_at ?? -1);\n keyed_at = at;\n if (!held || held.until < now || held.by === correlator.by) {\n correlating = true;\n this._correlators.set(correlator.key, {\n at,\n by: correlator.by,\n // A non-positive `millis` releases outright rather than setting an\n // expiry a hair in the future: a successor asking inside the same\n // millisecond would otherwise still be refused.\n until: correlator.millis > 0 ? now + correlator.millis : 0,\n });\n } else {\n // Refused, which is only possible when a live holder exists — so the\n // row is here to keep. The position still advances: this caller may\n // already have scanned, and dropping how far it read would leave its\n // marks ahead of its recorded position.\n correlating = false;\n held.at = at;\n }\n // The shared position tracks how far *any* correlator has read: the\n // floor a brand-new key inherits, and what a caller reading without a\n // correlator still sees.\n if (correlated_at !== undefined && correlated_at > this._correlated_at)\n this._correlated_at = correlated_at;\n } else if (\n correlated_at !== undefined &&\n correlated_at > this._correlated_at\n )\n this._correlated_at = correlated_at;\n\n let subscribed = 0;\n for (const {\n stream,\n source,\n priority = 0,\n lane = DEFAULT_LANE,\n correlated_at,\n } of streams) {\n const existing = this._streams.get(stream);\n if (existing) {\n existing.bump_priority(priority);\n existing.lane = lane;\n if (correlated_at !== undefined) existing.mark(correlated_at);\n } else {\n const created = new InMemoryStream(stream, source, priority, lane);\n if (correlated_at !== undefined) created.mark(correlated_at);\n this._streams.set(stream, created);\n subscribed++;\n }\n }\n let watermark = -1;\n for (const s of this._streams.values()) {\n if (s.at > watermark) watermark = s.at;\n }\n return {\n subscribed,\n watermark,\n correlated_at: keyed_at ?? this._correlated_at,\n ...(correlating === undefined ? {} : { correlating }),\n };\n }\n\n /**\n * Acknowledge completion of processing for leased streams.\n * @param leases - Leases to acknowledge, including last processed watermark and lease holder.\n */\n async ack(leases: Lease[]) {\n await sleep();\n // Acks and defer schedules land in one synchronous pass — the\n // in-memory equivalent of the single-transaction contract on\n // {@link Store.ack}: no await between entries, so a caller\n // never observes a cycle's acks without its schedules. `due`-carrying\n // entries defer (and return undefined), the rest ack.\n return leases\n .map((l) => this._streams.get(l.stream)?.ack(l))\n .filter((l) => !!l);\n }\n\n /**\n * Block a stream for processing after failing to process and reaching max retries with blocking enabled.\n * @param leases - Leases to block, including lease holder and last error message.\n * @returns Blocked leases.\n */\n async block(leases: BlockedLease[]) {\n await sleep();\n return leases\n .map((l) => this._streams.get(l.stream)?.block(l, l.error))\n .filter((l) => !!l);\n }\n\n /**\n * Hold the matched streams out of {@link claim} until `deferred_at`\n * (ms since epoch) — see {@link Store.defer}. Accepts an explicit list\n * of names or a {@link StreamFilter}, mirroring {@link reset}/{@link unblock}.\n * Persisted store state (unlike in-process backoff), so the skip is honored\n * by every competing worker. Unknown names are silently skipped.\n *\n * @returns Count of streams whose `deferred_at` was set.\n */\n async defer(input: string[] | StreamFilter, deferred_at: number) {\n await sleep();\n let count = 0;\n if (Array.isArray(input)) {\n // De-dup the array so a repeated name counts once, matching PG's\n // set-based `WHERE stream = ANY(...)` (#1360).\n for (const name of new Set(input)) {\n const s = this._streams.get(name);\n if (s) {\n s.defer(deferred_at);\n count++;\n }\n }\n } else {\n const matches = this._filter_predicate(input);\n for (const s of this._streams.values()) {\n if (matches(s)) {\n s.defer(deferred_at);\n count++;\n }\n }\n }\n return count;\n }\n\n /**\n * Build a predicate from a {@link StreamFilter}. Compiled regexes are\n * cached in the closure so callers can apply it across the streams\n * map without re-compiling per iteration.\n */\n private _filter_predicate(\n filter: StreamFilter\n ): (s: InMemoryStream) => boolean {\n const stream_re =\n filter.stream && !filter.stream_exact\n ? new RegExp(filter.stream)\n : undefined;\n const source_re =\n filter.source && !filter.source_exact\n ? new RegExp(filter.source)\n : undefined;\n return (s) => {\n if (filter.stream !== undefined) {\n if (\n filter.stream_exact\n ? s.stream !== filter.stream\n : !stream_re!.test(s.stream)\n )\n return false;\n }\n if (filter.source !== undefined) {\n if (s.source === undefined) return false;\n if (\n filter.source_exact\n ? s.source !== filter.source\n : !source_re!.test(s.source)\n )\n return false;\n }\n if (filter.blocked !== undefined && s.blocked !== filter.blocked)\n return false;\n if (filter.lane !== undefined && s.lane !== filter.lane) return false;\n return true;\n };\n }\n\n /**\n * Reset watermarks to -1, clearing retry, blocked, error, and lease\n * state so the matched streams can be replayed from the beginning.\n * Accepts either an explicit list of names or a {@link StreamFilter}.\n *\n * @param input - Stream names or a filter selecting the streams to reset.\n * @returns Count of streams that were actually reset.\n */\n async reset(input: string[] | StreamFilter) {\n await sleep();\n let count = 0;\n if (Array.isArray(input)) {\n // De-dup the array so a repeated name counts once, matching PG's\n // set-based `WHERE stream = ANY(...)` (#1360).\n for (const name of new Set(input)) {\n const s = this._streams.get(name);\n if (s) {\n s.reset();\n count++;\n }\n }\n } else {\n const matches = this._filter_predicate(input);\n for (const s of this._streams.values()) {\n if (matches(s)) {\n s.reset();\n count++;\n }\n }\n }\n return count;\n }\n\n /**\n * Clear the blocked flag (and retry / error / lease) on the matched\n * streams without touching the watermark. Streams that aren't blocked\n * at call time are silently skipped. Accepts either an explicit list\n * of names or a {@link StreamFilter}. The filter form always restricts\n * to blocked streams — passing `blocked: false` matches nothing.\n * See {@link Store.unblock}.\n *\n * @param input - Stream names or a filter selecting the streams to unblock.\n * @returns Count of streams that were actually flipped (were blocked).\n */\n /**\n * Wipe the sensitive-data payload for every event on the stream — see\n * {@link Store.forget_pii}. O(1) drop of the stream's inner Map; the size of\n * that Map is the count of events that had PII. Idempotent: a second call\n * finds no inner Map and returns `0`.\n *\n * @param stream - Target stream.\n * @returns Count of events whose isolated PII payload was deleted.\n */\n async forget_pii(stream: string): Promise<number> {\n await sleep();\n const count = this._pii.get(stream)?.size ?? 0;\n this._pii.delete(stream);\n return count;\n }\n\n async unblock(input: string[] | StreamFilter) {\n await sleep();\n let count = 0;\n if (Array.isArray(input)) {\n for (const name of input) {\n const s = this._streams.get(name);\n if (s?.unblock()) count++;\n }\n } else {\n // Filter form: always restrict to blocked streams. An explicit\n // `blocked: false` in the filter is silently overridden — there\n // is no use case for \"unblock unblocked streams.\"\n const matches = this._filter_predicate({ ...input, blocked: true });\n for (const s of this._streams.values()) {\n if (matches(s) && s.unblock()) count++;\n }\n }\n return count;\n }\n\n /**\n * Bulk-update priority of streams matching `filter`. Mirrors\n * {@link query_streams}'s filter semantics — see {@link Store.prioritize}.\n * Unlike {@link subscribe} (which keeps `max()` of registered\n * priorities), this sets the priority outright — operator override\n * for the build-time scheduling policy.\n *\n * @returns Count of streams whose priority changed.\n */\n async prioritize(filter: StreamFilter, priority: number) {\n await sleep();\n const matches = this._filter_predicate(filter);\n let count = 0;\n for (const s of this._streams.values()) {\n if (!matches(s)) continue;\n if (s.priority !== priority) {\n s.set_priority(priority);\n count++;\n }\n }\n return count;\n }\n\n /**\n * Streams registered subscription positions to the callback, ordered by\n * stream name. Returns the highest event id in the store and the count\n * of positions emitted.\n */\n async query_streams(\n callback: (position: StreamPosition) => void,\n query?: QueryStreams\n ): Promise<QueryStreamsResult> {\n await sleep();\n const limit = query?.limit ?? 100;\n const after = query?.after;\n const blocked = query?.blocked;\n const source_matches = query?.source_matches;\n const stream_re =\n query?.stream && !query.stream_exact\n ? new RegExp(query.stream)\n : undefined;\n const source_re =\n query?.source && !query.source_exact\n ? new RegExp(query.source)\n : undefined;\n // Reverse-match: a stream qualifies when its stored `source` pattern\n // matches at least one of the requested names. Patterns are compiled\n // once and cached — many subscriptions share one source pattern.\n const reverse_cache = new Map<string, RegExp>();\n const reverse_match = (source: string): boolean => {\n let re = reverse_cache.get(source);\n if (!re) {\n re = new RegExp(source);\n reverse_cache.set(source, re);\n }\n return source_matches!.some((name) => re!.test(name));\n };\n\n // Order by stream name so `after`/`limit` keyset-paginate\n // deterministically. The sort MUST agree with the `after` cursor below\n // (JS `<=`, i.e. UTF-16 code-unit order) — `localeCompare` disagrees\n // with `<=` on mixed-case/accented names (`B`=66 < `a`=97 by code unit,\n // but `a` < `B` by locale), which let the cursor skip streams the sort\n // placed after it (#1375, the `query_streams` twin of #1357).\n // The default `Array.sort()` comparator IS code-unit order, so it\n // matches `<=` exactly — and PG/SQLite, which paginate under their\n // binary collation. Sorting the keys (rather than comparing values)\n // keeps that default in play.\n const sorted = [...this._streams.keys()]\n .sort()\n .map((stream) => this._streams.get(stream) as InMemoryStream);\n\n let count = 0;\n for (const s of sorted) {\n if (after !== undefined && s.stream <= after) continue;\n if (query?.stream !== undefined) {\n if (\n query.stream_exact\n ? s.stream !== query.stream\n : !stream_re!.test(s.stream)\n )\n continue;\n }\n if (query?.source !== undefined) {\n if (s.source === undefined) continue;\n if (\n query.source_exact\n ? s.source !== query.source\n : !source_re!.test(s.source)\n )\n continue;\n }\n if (source_matches !== undefined) {\n // Absent/empty source = no source constraint = consumes from\n // every stream, so it matches any requested name. Only a\n // present source that matches none of them is excluded.\n if (s.source && !reverse_match(s.source)) continue;\n }\n if (blocked !== undefined && s.blocked !== blocked) continue;\n if (query?.lane !== undefined && s.lane !== query.lane) continue;\n callback({\n stream: s.stream,\n source: s.source,\n at: s.at,\n retry: s.retry,\n blocked: s.blocked,\n error: s.error,\n priority: s.priority,\n leased_by: s.leased_by,\n leased_until: s.leased_until,\n lane: s.lane,\n deferred_at: s.deferred_at,\n correlated_at: s.correlated_at,\n });\n count++;\n if (count >= limit) break;\n }\n return { maxEventId: this._events.at(-1)?.id ?? -1, count };\n }\n\n /**\n * Per-stream aggregated stats — see {@link Store.query_stats}.\n *\n * Single forward scan over the in-memory event list, accumulating per\n * stream. The \"cheap heads\" cost tier from durable adapters doesn't\n * apply here (InMemory has no indexes); correctness is the goal, perf\n * is a non-issue.\n *\n * Scope rules:\n * - Array `input` — explicit stream names, regardless of subscription.\n * - Filter `input` — `stream`/`stream_exact` match against event-bearing\n * stream names; `source`/`source_exact`/`blocked` require a\n * corresponding subscription in `_streams` (those are subscription\n * concepts, not event concepts). Empty filter `{}` matches every\n * event-bearing stream.\n */\n async query_stats<E extends Schemas>(\n input: string[] | Pick<StreamFilter, \"stream\" | \"stream_exact\">,\n options?: QueryStatsOptions<E>\n ): Promise<Map<string, StreamStats<E>>> {\n await sleep();\n const exclude = new Set<string>(options?.exclude ?? []);\n const want_tail = options?.tail ?? false;\n const want_count = options?.count ?? false;\n const want_names = options?.names ?? false;\n const before = options?.before;\n const after = options?.after;\n const limit = options?.limit;\n\n // Pre-compile per-stream scope predicate, cached as we go so each\n // distinct stream evaluates the regex once.\n const array_targets = Array.isArray(input) ? new Set(input) : null;\n const filter = Array.isArray(input) ? null : input;\n const stream_re =\n filter?.stream && !filter.stream_exact\n ? new RegExp(filter.stream)\n : undefined;\n\n const scope_cache = new Map<string, boolean>();\n const in_scope = (stream: string): boolean => {\n const cached = scope_cache.get(stream);\n if (cached !== undefined) return cached;\n let ok = true;\n if (array_targets) {\n ok = array_targets.has(stream);\n } else if (filter?.stream !== undefined) {\n ok = filter.stream_exact\n ? stream === filter.stream\n : // stream_re set when stream is regex\n stream_re!.test(stream);\n }\n scope_cache.set(stream, ok);\n return ok;\n };\n\n type Acc = {\n head: Committed<Schemas, keyof Schemas>;\n tail?: Committed<Schemas, keyof Schemas>;\n count: number;\n names?: Record<string, number>;\n };\n const acc = new Map<string, Acc>();\n for (const e of this._events) {\n if (before !== undefined && e.id >= before) continue;\n if (!in_scope(e.stream)) continue;\n if (exclude.has(e.name as string)) continue;\n let a = acc.get(e.stream);\n if (!a) {\n a = { head: e, count: 0 };\n if (want_tail) a.tail = e;\n if (want_names) a.names = {};\n acc.set(e.stream, a);\n }\n a.head = e;\n a.count++;\n if (want_names) {\n const n = String(e.name);\n // a.names initialized above when want_names\n a.names![n] = (a.names![n] ?? 0) + 1;\n }\n }\n\n // Order by stream name so `after`/`limit` keyset-paginate\n // deterministically. The sort MUST agree with the `after` cursor below\n // (JS `<=`, i.e. UTF-16 code-unit order) — `localeCompare` disagrees with\n // `<=` on mixed-case/accented names (`B`=66 < `a`=97 by code unit, but\n // `a` < `B` by locale), which would let the cursor skip streams the sort\n // placed after it (#1357). The default `Array.sort()` comparator IS\n // code-unit order, so it matches `<=` exactly — and PG/SQLite, which\n // paginate `query_stats` under their binary collation.\n const ordered = [...acc.keys()].sort();\n const out = new Map<string, StreamStats<E>>();\n for (const stream of ordered) {\n if (after !== undefined && stream <= after) continue;\n const a = acc.get(stream)!;\n const stats: {\n head: Committed<Schemas, keyof Schemas>;\n tail?: Committed<Schemas, keyof Schemas>;\n count?: number;\n names?: Record<string, number>;\n } = { head: a.head };\n if (want_tail) stats.tail = a.tail;\n if (want_count) stats.count = a.count;\n if (want_names) stats.names = a.names;\n out.set(stream, stats as StreamStats<E>);\n if (limit !== undefined && out.size >= limit) break;\n }\n return out;\n }\n\n /**\n * Atomically truncates streams and seeds each with a snapshot or tombstone.\n * Windowed targets (`before` set) prune the prefix below the closest safe\n * `__snapshot__` instead — no seed, subscriptions untouched, no-op when no\n * snapshot qualifies.\n * @param targets - Streams to truncate with optional snapshot state and meta,\n * or a `before`/`max_id` boundary for a windowed prefix delete.\n * @returns Map keyed by stream name, each entry with `deleted` count and `committed` event.\n */\n async truncate(\n targets: Array<{\n stream: string;\n snapshot?: Schema;\n meta?: EventMeta;\n before?: Date;\n max_id?: number;\n }>\n ) {\n await sleep();\n const result = new Map<\n string,\n {\n deleted: number;\n committed: Committed<Schemas, keyof Schemas>;\n before?: Date;\n }\n >();\n\n // Windowed targets: pure prefix delete behind the closest safe snapshot.\n const windowed = targets.filter((t) => t.before !== undefined);\n if (windowed.length) {\n const drop = new Set<number>();\n for (const { stream, before, max_id } of windowed) {\n let boundary: Committed<Schemas, keyof Schemas> | undefined;\n for (const e of this._events) {\n if (\n e.stream === stream &&\n e.name === SNAP_EVENT &&\n e.created < before! &&\n (max_id === undefined || e.id <= max_id) &&\n (!boundary || e.id > boundary.id)\n )\n boundary = e;\n }\n if (!boundary) continue; // no qualifying snapshot → no-op\n let deleted = 0;\n for (const e of this._events) {\n if (e.stream === stream && e.id < boundary.id) {\n drop.add(e.id);\n this._pii.get(stream)?.delete(e.id);\n deleted++;\n }\n }\n result.set(stream, { deleted, committed: boundary, before });\n }\n if (drop.size) this._events = this._events.filter((e) => !drop.has(e.id));\n }\n\n const full = targets.filter((t) => t.before === undefined);\n // Count per-stream deletions\n const deleted_counts = new Map<string, number>();\n const stream_set = new Set(full.map((t) => t.stream));\n for (const e of this._events) {\n if (stream_set.has(e.stream)) {\n deleted_counts.set(e.stream, (deleted_counts.get(e.stream) ?? 0) + 1);\n }\n }\n this._events = this._events.filter((e) => !stream_set.has(e.stream));\n // Subscriptions are deliberately untouched, for restart *and* retire\n // targets alike — see the note in truncate's contract. A tombstoned\n // stream's subscription is inert, and reaping it is maintenance that\n // `seed()` performs (#1527).\n for (const stream of stream_set) {\n this._stream_versions.delete(stream);\n this._max_event_id_by_stream.delete(stream);\n // The pii payloads die with the event rows, matching the durable\n // adapters' `DELETE WHERE stream = ?` scope.\n this._pii.delete(stream);\n }\n for (const { stream, snapshot, meta } of full) {\n const event: Committed<Schemas, keyof Schemas> = {\n id: this._next_id++,\n stream,\n version: 0,\n created: new Date(),\n name: snapshot !== undefined ? SNAP_EVENT : TOMBSTONE_EVENT,\n data: snapshot ?? {},\n meta: meta ?? { correlation: \"\", causation: {} },\n };\n this._events.push(event);\n this._stream_versions.set(stream, 0);\n if (event.name !== SNAP_EVENT) {\n this._max_event_id_by_stream.set(stream, event.id);\n }\n result.set(stream, {\n deleted: deleted_counts.get(stream) ?? 0,\n committed: event,\n });\n }\n // Recompute global max from the per-stream index — deletions may have\n // dropped the previous max, while new tombstones may have raised it.\n let max = -1;\n for (const id of this._max_event_id_by_stream.values())\n if (id > max) max = id;\n this._max_non_snap_event_id = max;\n return result;\n }\n\n /**\n * Atomically wipe-and-rebuild the store under an in-process snapshot.\n *\n * Captures every index state up front, clears it, then hands the\n * orchestrator a per-event insert `callback` via the driver. Any\n * throw inside the driver restores the snapshot, leaving the store\n * byte-for-byte unchanged from the operator's perspective.\n *\n * `id`s are reassigned `0..N-1` as events arrive (dense — the\n * monotonic id counter restarts at 0 for the rebuild). `created` is\n * preserved verbatim from the source.\n */\n async restore(\n driver: (\n callback: (event: Committed<Schemas, keyof Schemas>) => Promise<number>\n ) => Promise<void>\n ): Promise<void> {\n await sleep();\n // Snapshot every index so we can roll back on throw.\n const prev_events = this._events;\n const prev_next_id = this._next_id;\n const prev_streams = this._streams;\n const prev_stream_versions = this._stream_versions;\n const prev_max_event_id_by_stream = this._max_event_id_by_stream;\n const prev_max_non_snap_event_id = this._max_non_snap_event_id;\n const prev_pii = this._pii;\n // Swap in fresh state for the duration of the rebuild.\n this._events = [];\n this._next_id = 0;\n this._streams = new Map();\n this._stream_versions = new Map();\n this._max_event_id_by_stream = new Map();\n this._max_non_snap_event_id = -1;\n this._pii = new Map();\n try {\n await driver(async (event) => {\n const id = this._next_id++;\n // Split `pii` out of the stored row into the isolated `_pii` map,\n // mirroring `commit`. The stored event is the pii-less view so\n // `forget_pii` — which only drops the `_pii` entry — actually\n // erases restored PII (durable adapters restore into their\n // isolated column for the same reason).\n const { pii, ...rest } = event;\n const committed: Committed<Schemas, keyof Schemas> = { ...rest, id };\n this._events.push(committed);\n if (pii != null) {\n let per_stream = this._pii.get(event.stream);\n if (!per_stream) {\n per_stream = new Map();\n this._pii.set(event.stream, per_stream);\n }\n per_stream.set(id, structuredClone(pii) as Record<string, unknown>);\n }\n // Last event per stream wins for the version watermark — the\n // source is expected to be in commit order, so this is also\n // the highest version. Out-of-order sources get last-wins,\n // matching the legacy raw-SQL restore.\n this._stream_versions.set(event.stream, event.version);\n if (event.name !== SNAP_EVENT) {\n this._max_event_id_by_stream.set(event.stream, id);\n this._max_non_snap_event_id = id;\n }\n return id;\n });\n } catch (err) {\n // Roll back to the captured snapshot — every index restored\n // exactly as it was before the call started.\n this._events = prev_events;\n this._next_id = prev_next_id;\n this._streams = prev_streams;\n this._stream_versions = prev_stream_versions;\n this._max_event_id_by_stream = prev_max_event_id_by_stream;\n this._max_non_snap_event_id = prev_max_non_snap_event_id;\n this._pii = prev_pii;\n throw err;\n }\n }\n}\n"],"mappings":"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","names":["import_zod","import_zod","import_zod","log","correlated_at","store","cache"]}
1
+ {"version":3,"sources":["../../src/test/index.ts","../../src/test/sandbox.ts","../../src/internal/lru-map.ts","../../src/adapters/in-memory-cache.ts","../../src/adapters/console-logger.ts","../../src/config.ts","../../src/types/errors.ts","../../src/types/schemas.ts","../../src/internal/sensitive.ts","../../src/types/index.ts","../../src/utils.ts","../../src/scoped.ts","../../src/ports.ts","../../src/adapters/in-memory-store.ts"],"sourcesContent":["export * from \"./sandbox.js\";\n","import { test } from \"vitest\";\nimport type { Act, ActOptions } from \"../act.js\";\nimport { InMemoryCache } from \"../adapters/in-memory-cache.js\";\nimport { InMemoryStore } from \"../adapters/in-memory-store.js\";\nimport type { Cache, Store } from \"../types/index.js\";\n\n/**\n * Minimal structural shape — anything with a `.build()` that returns an\n * Act. Avoids the variance pitfalls of constraining on the full\n * `ActBuilder<TSchemaReg, TEvents, TActions, TStateMap, TActor>`\n * generic when called across package boundaries.\n */\ntype AnyActBuilder<TApp> = {\n // `ActOptions<any>` rather than `ActOptions<string>` (the default) — a\n // builder narrowed to `ActOptions<\"default\">` would otherwise trip\n // function-parameter contravariance against `ActOptions<string>`. The\n // `any` only widens `onlyLanes`; the rest of the option shape\n // (`scoped`, `correlator`, `settleDebounceMs`, etc.) still type-checks\n // at the runtime call site below.\n build: (options?: ActOptions<any>) => TApp;\n};\n\n/**\n * Options for {@link sandbox} / {@link fixture}.\n *\n * Defaults to `new InMemoryStore() + new InMemoryCache()` per call.\n * Override `store` / `cache` to point at PG, SQLite, or any other\n * adapter — factories run once per call so each test gets a fresh\n * instance.\n */\nexport type SandboxOptions = {\n /** Factory for the per-test store. Defaults to `new InMemoryStore()`. */\n readonly store?: () => Store | Promise<Store>;\n /** Factory for the per-test cache. Defaults to `new InMemoryCache()`. */\n readonly cache?: () => Cache | Promise<Cache>;\n /** Pass-through ActOptions. `scoped` is filled in by the helper. */\n readonly actOptions?: Omit<ActOptions, \"scoped\">;\n};\n\n/** Return shape of {@link sandbox}. */\nexport type Sandbox<TApp> = {\n readonly app: TApp;\n readonly store: Store;\n readonly cache: Cache;\n /** Tears down the Act, store, and cache. Idempotent. */\n readonly dispose: () => Promise<void>;\n};\n\n/**\n * Build a scoped Act bound to a fresh `{ store, cache }` bag.\n *\n * Intended for parallel-safe test isolation. Each call constructs new\n * ports (defaults to InMemoryStore + InMemoryCache), seeds the store,\n * builds the Act with `ActOptions.scoped`, and returns `{ app, store,\n * cache, dispose }`. The caller owns the lifecycle: `dispose()` performs\n * `app.shutdown()` followed by `store.dispose()` + `cache.dispose()`.\n *\n * Prefer {@link fixture} when fixture-style ergonomics fit; reach for\n * `sandbox` when you need explicit control (e.g., wiring inside\n * `beforeAll` rather than per-test, or two scoped Acts in one test, or\n * direct access to the store/cache handles).\n *\n * @example Per-test isolated Act with explicit dispose\n * ```ts\n * import { sandbox } from \"@rotorsoft/act/test\";\n *\n * const counterBuilder = act().withState(Counter);\n *\n * it(\"increments\", async () => {\n * const { app, dispose } = await sandbox(counterBuilder);\n * await app.do(\"increment\", { stream: \"c-1\", actor }, { by: 1 });\n * expect((await app.load(\"Counter\", \"c-1\")).state.count).toBe(1);\n * await dispose();\n * });\n * ```\n *\n * @example Custom store factory (PG per-test schema)\n * ```ts\n * const { app, dispose } = await sandbox(builder, {\n * store: () => new PostgresStore({ schema: `t_${nanoid()}` }),\n * });\n * ```\n */\nexport async function sandbox<TApp>(\n builder: AnyActBuilder<TApp>,\n options: SandboxOptions = {}\n): Promise<Sandbox<TApp>> {\n const store = options.store ? await options.store() : new InMemoryStore();\n const cache = options.cache ? await options.cache() : new InMemoryCache();\n await store.seed();\n\n const app = builder.build({\n ...options.actOptions,\n scoped: { store, cache },\n });\n\n let _disposed: Promise<void> | undefined;\n const dispose = (): Promise<void> => {\n if (!_disposed) {\n _disposed = (async () => {\n await (app as unknown as Act<any, any, any, any, any>).shutdown();\n await store.dispose();\n await cache.dispose();\n })();\n }\n return _disposed;\n };\n\n return { app, store, cache, dispose };\n}\n\n/**\n * Vitest fixture wrapper around {@link sandbox}.\n *\n * Returns a `test` instance with an `app` fixture — each test gets a\n * fresh, isolated Act and vitest's fixture lifecycle runs the cleanup\n * automatically when the test completes.\n *\n * Works with `test.concurrent(...)` — every concurrent invocation\n * receives its own bag, so tests don't race on the singleton.\n *\n * For tests that also need direct access to the underlying `store` /\n * `cache` handles, use {@link sandbox} explicitly.\n *\n * @example Fixture-style — auto cleanup, parallel-safe\n * ```ts\n * import { fixture } from \"@rotorsoft/act/test\";\n *\n * const test = fixture(act().withState(Counter));\n *\n * test(\"increments\", async ({ app }) => {\n * await app.do(\"increment\", { stream: \"c-1\", actor }, { by: 1 });\n * expect((await app.load(\"Counter\", \"c-1\")).state.count).toBe(1);\n * });\n *\n * test.concurrent(\"isolated from concurrent peers\", async ({ app }) => {\n * // Each concurrent invocation gets its own store + cache — no\n * // singleton contention, no flake.\n * });\n * ```\n *\n * @example Custom store factory\n * ```ts\n * const test = fixture(builder, {\n * store: () => new PostgresStore({ schema: `t_${nanoid()}` }),\n * });\n * ```\n */\nexport function fixture<TApp>(\n builder: AnyActBuilder<TApp>,\n defaults: SandboxOptions = {}\n) {\n return test.extend<{ app: TApp }>({\n app: async (\n // biome-ignore lint/correctness/noEmptyPattern: vitest fixture API requires a destructured deps parameter\n {},\n use: (value: TApp) => Promise<void>\n ) => {\n const ctx = await sandbox(builder, defaults);\n try {\n await use(ctx.app);\n } finally {\n await ctx.dispose();\n }\n },\n });\n}\n","/**\n * @module lru-map\n * @category Internal\n *\n * Tiny bounded LRU map / set built on insertion-ordered `Map`. Used to cap\n * memory in long-running orchestrators that mint large numbers of keys —\n * notably:\n *\n * - {@link InMemoryCache}: stream → state checkpoint\n * - `Act._subscribed_streams`: stream → presence (LruSet)\n *\n * Apps with millions of dynamic streams (one target per aggregate) can't\n * afford an unbounded `Set<string>` — eviction is required.\n *\n * @internal\n */\n\n/**\n * Bounded LRU map. `get()` promotes; `has()` does not. `set()` always\n * promotes and evicts the oldest entry when at capacity.\n *\n * @internal\n */\nexport class LruMap<K, V> {\n private readonly _entries = new Map<K, V>();\n private readonly _max_size: number;\n\n constructor(maxSize: number) {\n this._max_size = maxSize;\n }\n\n get(key: K): V | undefined {\n const v = this._entries.get(key);\n if (v === undefined) return undefined;\n // promote: delete + re-insert moves to most-recent position\n this._entries.delete(key);\n this._entries.set(key, v);\n return v;\n }\n\n has(key: K): boolean {\n return this._entries.has(key);\n }\n\n set(key: K, value: V): void {\n this._entries.delete(key);\n if (this._entries.size >= this._max_size) {\n // size >= maxSize ≥ 1 → at least one entry exists → next().value\n // is the oldest key (asserted with `!`).\n const oldest = this._entries.keys().next().value!;\n this._entries.delete(oldest);\n }\n this._entries.set(key, value);\n }\n\n delete(key: K): boolean {\n return this._entries.delete(key);\n }\n\n clear(): void {\n this._entries.clear();\n }\n\n get size(): number {\n return this._entries.size;\n }\n}\n\n/**\n * Bounded LRU set built on top of {@link LruMap}. `has()` does not promote;\n * `add()` does (re-inserting if already present, evicting the oldest at\n * capacity).\n *\n * @internal\n */\nexport class LruSet<T> {\n private readonly _map: LruMap<T, true>;\n\n constructor(maxSize: number) {\n this._map = new LruMap(maxSize);\n }\n\n has(value: T): boolean {\n return this._map.has(value);\n }\n\n add(value: T): void {\n this._map.set(value, true);\n }\n\n delete(value: T): boolean {\n return this._map.delete(value);\n }\n\n clear(): void {\n this._map.clear();\n }\n\n get size(): number {\n return this._map.size;\n }\n}\n","import { LruMap } from \"../internal/lru-map.js\";\nimport type { Cache, CacheEntry, Schema } from \"../types/index.js\";\n\n/**\n * In-memory LRU cache for stream snapshots.\n *\n * Backed by an internal `LruMap` for O(1) get/set with LRU eviction.\n * Configurable `maxSize` bounds memory usage.\n *\n * @example\n * ```typescript\n * import { cache } from \"@rotorsoft/act\";\n * import { InMemoryCache } from \"@rotorsoft/act\";\n *\n * cache(new InMemoryCache({ maxSize: 500 }));\n * ```\n */\n/* eslint-disable @typescript-eslint/require-await -- async interface for Redis-compatibility */\nexport class InMemoryCache implements Cache {\n // CacheEntry<any> lets `get<TState>` and `set<TState>` flow without casts:\n // any is bidirectionally compatible with the per-call TState binding, while\n // the public Cache interface still presents a typed surface to callers.\n private readonly _entries: LruMap<string, CacheEntry<any>>;\n\n constructor(options?: { maxSize?: number }) {\n this._entries = new LruMap(options?.maxSize ?? 1000);\n }\n\n /** @inheritDoc */\n async get<TState extends Schema>(\n stream: string\n ): Promise<CacheEntry<TState> | undefined> {\n return this._entries.get(stream);\n }\n\n /** @inheritDoc */\n async set<TState extends Schema>(\n stream: string,\n entry: CacheEntry<TState>\n ): Promise<void> {\n this._entries.set(stream, entry);\n }\n\n /** @inheritDoc */\n async invalidate(stream: string): Promise<void> {\n this._entries.delete(stream);\n }\n\n /** @inheritDoc */\n async clear(): Promise<void> {\n this._entries.clear();\n }\n\n /** @inheritDoc */\n async dispose(): Promise<void> {\n this._entries.clear();\n }\n\n /** Current number of entries held by the LRU. */\n get size(): number {\n return this._entries.size;\n }\n}\n","/**\n * @module adapters/console-logger\n *\n * High-performance console logger inspired by pino's design:\n * - Numeric level comparison for O(1) gating\n * - stdout.write() in production for raw JSON lines (no console overhead)\n * - Colorized single-line output in development\n * - No-op method replacement when level is above threshold\n * - Child logger support with merged bindings\n */\nimport type { Logger } from \"../types/index.js\";\n\nconst LEVEL_VALUES: Record<string, number> = {\n fatal: 60,\n error: 50,\n warn: 40,\n info: 30,\n debug: 20,\n trace: 10,\n};\n\nconst LEVEL_COLORS: Record<string, string> = {\n fatal: \"\\x1b[41m\\x1b[37m\", // white on red bg\n error: \"\\x1b[31m\", // red\n warn: \"\\x1b[33m\", // yellow\n info: \"\\x1b[32m\", // green\n debug: \"\\x1b[36m\", // cyan\n trace: \"\\x1b[90m\", // gray\n};\n\nconst RESET = \"\\x1b[0m\";\n\nconst noop = () => {};\n\n/**\n * Default console logger for the Act framework.\n *\n * Production mode emits newline-delimited JSON (compatible with GCP, AWS\n * CloudWatch, Datadog, and other structured log ingestion systems).\n *\n * Development mode emits colorized, human-readable output.\n */\nexport class ConsoleLogger implements Logger {\n level: string;\n private readonly _pretty: boolean;\n\n readonly fatal: Logger[\"fatal\"];\n readonly error: Logger[\"error\"];\n readonly warn: Logger[\"warn\"];\n readonly info: Logger[\"info\"];\n readonly debug: Logger[\"debug\"];\n readonly trace: Logger[\"trace\"];\n\n constructor(\n options: {\n level?: string;\n pretty?: boolean;\n bindings?: Record<string, unknown>;\n } = {}\n ) {\n const {\n level = \"info\",\n pretty = process.env.NODE_ENV !== \"production\",\n bindings,\n } = options;\n this._pretty = pretty;\n this.level = level;\n\n const threshold = LEVEL_VALUES[level] ?? 30;\n const write = pretty\n ? this._pretty_write.bind(this, bindings)\n : this._json_write.bind(this, bindings);\n\n // Assign methods — noop when level is gated (like pino's level-based replacement)\n this.fatal = write.bind(this, \"fatal\", 60); // fatal is always enabled\n this.error = threshold <= 50 ? write.bind(this, \"error\", 50) : noop;\n this.warn = threshold <= 40 ? write.bind(this, \"warn\", 40) : noop;\n this.info = threshold <= 30 ? write.bind(this, \"info\", 30) : noop;\n this.debug = threshold <= 20 ? write.bind(this, \"debug\", 20) : noop;\n this.trace = threshold <= 10 ? write.bind(this, \"trace\", 10) : noop;\n }\n\n /** No-op — `console.log` has no resources to release. */\n async dispose(): Promise<void> {}\n\n /** @inheritDoc */\n child(bindings: Record<string, unknown>): Logger {\n return new ConsoleLogger({\n level: this.level,\n pretty: this._pretty,\n bindings,\n });\n }\n\n private _json_write(\n bindings: Record<string, unknown> | undefined,\n level: string,\n _num: number,\n obj_or_msg: unknown,\n msg?: string\n ): void {\n let obj: Record<string, unknown>;\n let message: string | undefined;\n\n if (typeof obj_or_msg === \"string\") {\n message = obj_or_msg;\n obj = {};\n } else if (obj_or_msg instanceof Error) {\n // Error instances spread to `{}` — capture the salient fields\n // explicitly so structured log aggregators see them.\n message = msg ?? obj_or_msg.message;\n obj = {\n error: { message: obj_or_msg.message, name: obj_or_msg.name },\n stack: obj_or_msg.stack,\n };\n } else if (obj_or_msg !== null && typeof obj_or_msg === \"object\") {\n message = msg;\n obj = { ...(obj_or_msg as Record<string, unknown>) };\n } else {\n message = msg;\n obj = { value: obj_or_msg };\n }\n\n const entry = Object.assign({ level, time: Date.now() }, bindings, obj);\n if (message) entry.msg = message;\n\n let line: string;\n try {\n line = JSON.stringify(entry);\n } catch {\n // Cyclic or unserializable payload — emit a minimal line rather\n // than crash the log call site.\n line = JSON.stringify({\n level,\n time: entry.time,\n msg: message ?? \"[unserializable]\",\n unserializable: true,\n });\n }\n process.stdout.write(line + \"\\n\");\n }\n\n private _pretty_write(\n bindings: Record<string, unknown> | undefined,\n level: string,\n _num: number,\n obj_or_msg: unknown,\n msg?: string\n ): void {\n const color = LEVEL_COLORS[level];\n const tag = `${color}${level.toUpperCase().padEnd(5)}${RESET}`;\n const ts = new Date().toISOString().slice(11, 23); // HH:mm:ss.SSS\n\n let message: string;\n let data: string | undefined;\n\n if (typeof obj_or_msg === \"string\") {\n message = obj_or_msg;\n } else if (obj_or_msg instanceof Error) {\n // Error instances don't serialize their `message`/`stack` via\n // JSON.stringify — `JSON.stringify(err) === \"{}\"`. Render the\n // message inline and the stack as the data payload so operators\n // see something useful instead of an empty object.\n message = msg ?? obj_or_msg.message;\n data = obj_or_msg.stack;\n } else {\n message = msg ?? \"\";\n if (obj_or_msg !== undefined && obj_or_msg !== null) {\n try {\n data = JSON.stringify(obj_or_msg);\n } catch {\n data = \"[unserializable]\";\n }\n }\n }\n\n const bind_str =\n bindings && Object.keys(bindings).length\n ? ` ${JSON.stringify(bindings)}`\n : \"\";\n\n const parts = [ts, tag, message, data, bind_str].filter(Boolean);\n process.stdout.write(parts.join(\" \") + \"\\n\");\n }\n}\n","/**\n * @packageDocumentation\n * Configuration utilities for Act Framework environment, logging, and package metadata.\n *\n * Provides type-safe configuration loading and validation using Zod schemas.\n *\n * @module config\n */\nimport * as fs from \"node:fs\";\nimport { z } from \"zod\";\nimport { log } from \"./ports.js\";\nimport {\n type Environment,\n Environments,\n type LogLevel,\n LogLevels,\n} from \"./types/index.js\";\nimport { extend } from \"./utils.js\";\n\n/**\n * Zod schema for validating package.json metadata.\n * @internal\n */\nexport const PackageSchema = z.object({\n name: z.string().min(1),\n version: z.string().min(1),\n description: z.string().min(1).optional(),\n author: z\n .object({ name: z.string().min(1), email: z.string().optional() })\n .optional()\n .or(z.string().min(1))\n .optional(),\n license: z.string().min(1).optional(),\n dependencies: z.record(z.string(), z.string()).optional(),\n});\n\n/**\n * Type representing the validated package.json metadata.\n *\n * @internal\n */\nexport type Package = z.infer<typeof PackageSchema>;\n\n/**\n * Fallback package metadata when `package.json` can't be read at module\n * load — happens when the framework is consumed from a CWD that doesn't\n * have one (bundled CLIs, Lambda layers, embedded scripts) or when the\n * file exists but is malformed.\n *\n * The values are deliberately synthetic so callers spot them immediately:\n * `config().name === \"act-fallback\"` is a runtime signal that the framework\n * couldn't load the host project's package.json.\n *\n * @internal\n */\nconst FALLBACK_PACKAGE: Package = {\n name: \"act-fallback\",\n version: \"0.0.0-fallback\",\n description: \"Synthetic fallback — package.json could not be loaded\",\n};\n\n/**\n * Loads and parses the local package.json file as a Package object. On\n * any read or parse failure, falls back to {@link FALLBACK_PACKAGE} and\n * stashes the error so {@link config} can surface it on first access —\n * we can't call `log()` here because the logger port memoizes on first\n * call and locking it at module load defeats user injection.\n *\n * @internal\n */\nconst get_package = (): Package => {\n try {\n const raw = fs.readFileSync(\"package.json\");\n return JSON.parse(raw.toString()) as Package;\n } catch (err) {\n pkg_load_error = err;\n return FALLBACK_PACKAGE;\n }\n};\n\n/** Stashed read/parse error from {@link get_package}, surfaced by config(). */\nlet pkg_load_error: unknown;\n\n/**\n * Zod schema for the full Act Framework configuration object.\n * Includes package metadata, environment, logging, and timing options.\n * @internal\n */\nconst BaseSchema = PackageSchema.extend({\n env: z.enum(Environments),\n logLevel: z.enum(LogLevels),\n logSingleLine: z.boolean(),\n sleepMs: z.number().int().min(0).max(5000),\n});\n\n/**\n * Type representing the validated Act Framework configuration object.\n */\nexport type Config = z.infer<typeof BaseSchema>;\n\nconst { NODE_ENV, LOG_LEVEL, LOG_SINGLE_LINE, SLEEP_MS } = process.env;\n\nconst env = (NODE_ENV || \"development\") as Environment;\nconst logLevel = (LOG_LEVEL ||\n (NODE_ENV === \"test\"\n ? \"fatal\"\n : NODE_ENV === \"production\"\n ? \"info\"\n : \"trace\")) as LogLevel;\nconst logSingleLine = (LOG_SINGLE_LINE || \"true\") === \"true\";\nconst sleepMs = parseInt(NODE_ENV === \"test\" ? \"0\" : (SLEEP_MS ?? \"100\"), 10);\n\nconst pkg = get_package();\n\n// Lazily validated on first call. Cannot run extend() at module load\n// because of a utils.ts <-> config.ts cycle (utils imports config for\n// sleep()'s default). Inputs are frozen after import, so the cached\n// result is stable for the life of the process.\nlet _validated: Config | undefined;\n\n/**\n * Gets the current Act Framework configuration.\n *\n * Configuration is loaded from package.json and environment variables, providing\n * type-safe access to application metadata and runtime settings.\n *\n * **Environment Variables:**\n * - `NODE_ENV`: \"development\" | \"test\" | \"staging\" | \"production\" (default: \"development\")\n * - `LOG_LEVEL`: \"fatal\" | \"error\" | \"warn\" | \"info\" | \"debug\" | \"trace\"\n * - `LOG_SINGLE_LINE`: \"true\" | \"false\" (default: \"true\")\n * - `SLEEP_MS`: Milliseconds for sleep utility (default: 100, 0 for tests)\n *\n * **Defaults by environment:**\n * - test: logLevel=\"error\", sleepMs=0\n * - production: logLevel=\"info\"\n * - development: logLevel=\"trace\"\n *\n * @returns The validated configuration object\n *\n * @example Basic usage\n * ```typescript\n * import { config } from \"@rotorsoft/act\";\n *\n * const cfg = config();\n * console.log(`App: ${cfg.name} v${cfg.version}`);\n * console.log(`Environment: ${cfg.env}`);\n * console.log(`Log level: ${cfg.logLevel}`);\n * ```\n *\n * @example Environment-specific behavior\n * ```typescript\n * import { config } from \"@rotorsoft/act\";\n *\n * const cfg = config();\n *\n * if (cfg.env === \"production\") {\n * // Use PostgreSQL in production\n * store(new PostgresStore(prodConfig));\n * } else {\n * // Use in-memory store for dev/test\n * store(new InMemoryStore());\n * }\n * ```\n *\n * @example Adjusting log levels\n * ```typescript\n * // Set via environment variable:\n * // LOG_LEVEL=debug npm start\n *\n * // Or check in code:\n * const cfg = config();\n * if (cfg.logLevel === \"trace\") {\n * logger.trace(\"Detailed debugging enabled\");\n * }\n * ```\n *\n * @see {@link Config} for configuration type\n */\nexport const config = (): Config => {\n if (!_validated) {\n _validated = extend(\n { ...pkg, env, logLevel, logSingleLine, sleepMs },\n BaseSchema\n );\n if (pkg_load_error) {\n // Surface the fallback once, after _validated is set so the\n // recursive log() → config() call short-circuits. log() resolves\n // through the port singleton — respects user injection and level.\n const msg =\n pkg_load_error instanceof Error\n ? pkg_load_error.message\n : typeof pkg_load_error === \"string\"\n ? pkg_load_error\n : \"unknown error\";\n log().warn(\n `[act] Could not read package.json (${msg}); using synthetic ` +\n `name=\"${FALLBACK_PACKAGE.name}\" version=\"${FALLBACK_PACKAGE.version}\".`\n );\n pkg_load_error = undefined;\n }\n }\n return _validated;\n};\n","import type {\n Actor,\n Message,\n Schema,\n Schemas,\n Snapshot,\n Target,\n} from \"./action.js\";\n\n/**\n * @packageDocumentation\n * @module act/types\n * @category Types\n * Application error type constants and error classes for the Act Framework.\n *\n * - `ERR_VALIDATION`: Schema validation error\n * - `ERR_INVARIANT`: Invariant validation error\n * - `ERR_CONCURRENCY`: Optimistic concurrency validation error on commits\n */\nexport const Errors = {\n ValidationError: \"ERR_VALIDATION\",\n InvariantError: \"ERR_INVARIANT\",\n ConcurrencyError: \"ERR_CONCURRENCY\",\n StreamClosedError: \"ERR_STREAM_CLOSED\",\n NonRetryableError: \"ERR_NON_RETRYABLE\",\n StoreError: \"ERR_STORE\",\n} as const;\n\n/**\n * Thrown when an action or event payload fails Zod schema validation.\n *\n * This error indicates that data doesn't match the expected schema defined\n * for an action or event. The `details` property contains the Zod validation\n * error with specific information about what failed.\n *\n * @example Catching validation errors\n * ```typescript\n * import { ValidationError } from \"@rotorsoft/act\";\n *\n * try {\n * await app.do(\"createUser\", target, {\n * email: \"invalid-email\", // Missing @ symbol\n * age: -5 // Negative age\n * });\n * } catch (error) {\n * if (error instanceof ValidationError) {\n * console.error(\"Validation failed for:\", error.target);\n * console.error(\"Invalid payload:\", error.payload);\n * console.error(\"Validation details:\", error.details);\n * // details contains Zod error with field-level info\n * }\n * }\n * ```\n *\n * @example Logging validation details\n * ```typescript\n * try {\n * await app.do(\"updateProfile\", target, payload);\n * } catch (error) {\n * if (error instanceof ValidationError) {\n * error.details.errors.forEach((err) => {\n * console.error(`Field ${err.path.join(\".\")}: ${err.message}`);\n * });\n * }\n * }\n * ```\n *\n * @see {@link https://zod.dev | Zod documentation} for validation details\n */\nexport class ValidationError extends Error {\n /** The type of target being validated (e.g., \"action\", \"event\") */\n public readonly target: string;\n /** The invalid payload that failed validation */\n public readonly payload: any;\n /** Zod validation error details */\n public readonly details: any;\n\n constructor(target: string, payload: any, details: any) {\n super(`Invalid ${target} payload`);\n this.name = Errors.ValidationError;\n this.target = target;\n this.payload = payload;\n this.details = details;\n }\n}\n\n/**\n * Thrown when a business rule (invariant) is violated during action execution.\n *\n * Invariants are conditions that must hold true for an action to succeed.\n * They're checked after loading the current state but before emitting events.\n * This error provides complete context about what action was attempted and\n * why it was rejected.\n *\n * @template TState - State schema type\n * @template TEvents - Event schemas type\n * @template TActions - Action schemas type\n * @template TKey - Action name\n * @template TActor - Actor type extending base Actor\n *\n * @example Catching invariant violations\n * ```typescript\n * import { InvariantError } from \"@rotorsoft/act\";\n *\n * try {\n * await app.do(\"withdraw\",\n * { stream: \"account-123\", actor: { id: \"user1\", name: \"Alice\" } },\n * { amount: 1000 }\n * );\n * } catch (error) {\n * if (error instanceof InvariantError) {\n * console.error(\"Action:\", error.action);\n * console.error(\"Reason:\", error.description);\n * console.error(\"Current state:\", error.snapshot.state);\n * console.error(\"Attempted payload:\", error.payload);\n * }\n * }\n * ```\n *\n * @example User-friendly error messages\n * ```typescript\n * try {\n * await app.do(\"closeTicket\", target, payload);\n * } catch (error) {\n * if (error instanceof InvariantError) {\n * // Present friendly message to user\n * if (error.description === \"Ticket must be open\") {\n * return { error: \"This ticket is already closed\" };\n * } else if (error.description === \"Not authorized\") {\n * return { error: \"You don't have permission to close this ticket\" };\n * }\n * }\n * }\n * ```\n *\n * @example Logging with context\n * ```typescript\n * try {\n * await app.do(\"transfer\", target, { to: \"account2\", amount: 500 });\n * } catch (error) {\n * if (error instanceof InvariantError) {\n * logger.error({\n * action: error.action,\n * stream: error.target.stream,\n * actor: error.target.actor,\n * reason: error.description,\n * balance: error.snapshot.state.balance,\n * attempted: error.payload.amount\n * }, \"Invariant violation\");\n * }\n * }\n * ```\n *\n * @see {@link Invariant} for defining business rules\n */\nexport class InvariantError<\n TState extends Schema,\n TEvents extends Schemas,\n TActions extends Schemas,\n TKey extends keyof TActions,\n TActor extends Actor = Actor,\n> extends Error {\n /** The action that was attempted */\n readonly action: TKey;\n /** The action payload that was provided */\n readonly payload: Readonly<TActions[TKey]>;\n /** The target stream and actor context */\n readonly target: Target<TActor>;\n /** The current state snapshot when invariant was checked */\n readonly snapshot: Snapshot<TState, TEvents>;\n /** Human-readable description of why the invariant failed */\n readonly description: string;\n\n constructor(\n action: TKey,\n payload: Readonly<TActions[TKey]>,\n target: Target<TActor>,\n snapshot: Snapshot<TState, TEvents>,\n description: string\n ) {\n super(`${action as string} failed invariant: ${description}`);\n this.name = Errors.InvariantError;\n this.action = action;\n this.payload = payload;\n this.target = target;\n this.snapshot = snapshot;\n this.description = description;\n }\n}\n\n/**\n * Thrown when optimistic concurrency control detects a conflict.\n *\n * This error occurs when trying to commit events to a stream that has been\n * modified by another process since it was last loaded. The version number\n * doesn't match expectations, indicating a concurrent modification.\n *\n * This is a normal occurrence in distributed systems and should be handled\n * by reloading the current state and retrying the action.\n *\n * @example Handling concurrency conflicts with retry\n * ```typescript\n * import { ConcurrencyError } from \"@rotorsoft/act\";\n *\n * async function transferWithRetry(from, to, amount, maxRetries = 3) {\n * for (let attempt = 0; attempt < maxRetries; attempt++) {\n * try {\n * await app.do(\"transfer\",\n * { stream: from, actor: currentUser },\n * { to, amount }\n * );\n * return { success: true };\n * } catch (error) {\n * if (error instanceof ConcurrencyError) {\n * if (attempt < maxRetries - 1) {\n * console.log(`Concurrent modification detected, retrying... (${attempt + 1}/${maxRetries})`);\n * await sleep(100 * Math.pow(2, attempt)); // Exponential backoff\n * continue;\n * }\n * }\n * throw error;\n * }\n * }\n * return { success: false, reason: \"Too many concurrent modifications\" };\n * }\n * ```\n *\n * @example Logging concurrency conflicts\n * ```typescript\n * try {\n * await app.do(\"updateInventory\", target, payload);\n * } catch (error) {\n * if (error instanceof ConcurrencyError) {\n * logger.warn({\n * stream: error.stream,\n * expectedVersion: error.expectedVersion,\n * actualVersion: error.lastVersion,\n * events: error.events.map(e => e.name)\n * }, \"Concurrent modification detected\");\n * }\n * }\n * ```\n *\n * @example User feedback for conflicts\n * ```typescript\n * try {\n * await app.do(\"editDocument\", target, { content: newContent });\n * } catch (error) {\n * if (error instanceof ConcurrencyError) {\n * return {\n * error: \"This document was modified by another user. Please refresh and try again.\",\n * code: \"CONCURRENT_MODIFICATION\"\n * };\n * }\n * }\n * ```\n *\n * @see {@link Store.commit} for version checking details\n */\nexport class ConcurrencyError extends Error {\n /** The stream that had the concurrent modification */\n public readonly stream: string;\n /** The actual current version in the store */\n public readonly lastVersion: number;\n /** The events that were being committed */\n public readonly events: Message<Schemas, keyof Schemas>[];\n /** The version number that was expected */\n public readonly expectedVersion: number;\n\n constructor(\n stream: string,\n lastVersion: number,\n events: Message<Schemas, keyof Schemas>[],\n expectedVersion: number\n ) {\n // Message lists stream + event names only. Payloads remain accessible\n // via `error.events` for callers who need them — keeping them out of\n // the message avoids MB-scale strings on contended writes and keeps\n // potentially-sensitive data out of log streams.\n super(\n `Concurrency error committing \"${events\n .map((e) => `${stream}.${e.name}`)\n .join(\n \", \"\n )}\". Expected version ${expectedVersion} but found version ${lastVersion}.`\n );\n this.name = Errors.ConcurrencyError;\n this.stream = stream;\n this.lastVersion = lastVersion;\n this.events = events;\n this.expectedVersion = expectedVersion;\n }\n}\n\n/**\n * Thrown when attempting to write to a stream that has been closed\n * with a tombstone event.\n *\n * A tombstoned stream is permanently closed — no further actions can\n * be executed against it. The only way to reopen a tombstoned stream\n * is through `Act.close()` with a `restart` callback.\n *\n * @example\n * ```typescript\n * import { StreamClosedError } from \"@rotorsoft/act\";\n *\n * try {\n * await app.do(\"updateTicket\", target, payload);\n * } catch (error) {\n * if (error instanceof StreamClosedError) {\n * console.error(`Stream ${error.stream} is closed`);\n * }\n * }\n * ```\n *\n * @see {@link Act.close} for closing streams\n */\nexport class StreamClosedError extends Error {\n /** The stream that is closed */\n public readonly stream: string;\n\n constructor(stream: string) {\n super(`Stream \"${stream}\" is closed (tombstoned)`);\n this.name = Errors.StreamClosedError;\n this.stream = stream;\n }\n}\n\n/**\n * Thrown by a {@link Store} adapter when an infrastructure operation fails\n * for a reason that is *not* a domain condition — a dropped connection, a\n * transaction rollback, a query timeout. It is the typed boundary between\n * \"the store is unavailable/degraded\" and the domain errors above\n * ({@link ConcurrencyError}, {@link StreamClosedError}), which describe\n * legitimate outcomes the caller should branch on.\n *\n * Adapters wrap their driver errors in `StoreError` (preserving the\n * original via `cause`) so the orchestrator can distinguish a degraded\n * backend from \"no work\" and react accordingly — see the drain circuit\n * breaker, which trips on repeated `StoreError`s and surfaces an\n * `error` lifecycle event instead of silently spinning on a down\n * database.\n *\n * @example\n * ```typescript\n * app.on(\"error\", ({ error, circuit }) => {\n * if (error instanceof StoreError)\n * alert(`store ${error.operation} failing; circuit=${circuit}`);\n * });\n * ```\n */\nexport class StoreError extends Error {\n /** The store operation that failed (e.g. `\"claim\"`, `\"ack\"`, `\"commit\"`). */\n public readonly operation: string;\n\n constructor(operation: string, options?: { cause?: unknown }) {\n super(`Store operation \"${operation}\" failed`, options);\n this.name = Errors.StoreError;\n this.operation = operation;\n }\n}\n\n/**\n * Thrown by a reaction handler to signal that the failure is permanent\n * and the drain pipeline should block the stream immediately, without\n * consuming the rest of the `maxRetries` budget.\n *\n * The drain finalizer detects `instanceof NonRetryableError` and forces\n * `block = options.blockOnError` regardless of `lease.retry`. When\n * `blockOnError` is `false`, behavior is unchanged (drain keeps retrying\n * forever) — the class never overrides the operator's explicit \"never\n * block\" choice.\n *\n * Use this for failures the handler *knows* won't get better on retry:\n * a 4xx from a webhook, a `ZodError` on malformed input, a \"user\n * deleted\" 404 from a downstream API. Use regular `Error` (or a\n * subclass) for transient failures so the existing retry-with-backoff\n * loop applies.\n *\n * @example Wrapping a permanent downstream error\n * ```typescript\n * import { NonRetryableError } from \"@rotorsoft/act\";\n *\n * .on(\"OrderConfirmed\")\n * .do(async (event) => {\n * const res = await fetch(url, ...);\n * if (res.status >= 400 && res.status < 500) {\n * throw new NonRetryableError(\n * `webhook ${url} responded ${res.status}`,\n * { cause: await res.text() }\n * );\n * }\n * if (!res.ok) throw new Error(`webhook ${url} responded ${res.status}`);\n * })\n * ```\n *\n * @example Marking validation failures as non-retryable\n * ```typescript\n * .on(\"PaymentReceived\")\n * .do(async (event) => {\n * const parsed = Schema.safeParse(event.data);\n * if (!parsed.success) {\n * throw new NonRetryableError(\"payment payload failed validation\", {\n * cause: parsed.error,\n * });\n * }\n * // ... handle parsed payload\n * })\n * ```\n */\nexport class NonRetryableError extends Error {\n /** The original failure, if any. Mirrors the standard `Error.cause` shape. */\n public override readonly cause?: unknown;\n\n constructor(message: string, options?: { cause?: unknown }) {\n super(message);\n this.name = Errors.NonRetryableError;\n this.cause = options?.cause;\n }\n}\n","import { type ZodObject, type ZodRawShape, z } from \"zod\";\n// Deep-path import (vs `../internal/index.js`) is deliberate — `_registry` is\n// a side-effect-free leaf, and going through the internal barrel would pull\n// tracing.ts → config.ts in at type-schema load time and crash on TDZ when a\n// test imports a public schema before config is initialized.\nimport { _mark_sensitive, _registry } from \"../internal/sensitive.js\";\n\n/**\n * @packageDocumentation\n * @module act/types\n * @category Types\n * Zod schemas and helpers for the Act Framework.\n */\n\n/**\n * An empty Zod schema (no properties).\n */\nexport const ZodEmpty = z.record(z.string(), z.never());\n\n/**\n * Sensitive-data foundation re-exports (#855 / epic #566).\n *\n * - `REDACTED` / `SHREDDED` — sentinels placed in `event.data[field]`\n * when the caller isn't authorized to see a sensitive field\n * (`.discloses(predicate)` returned `false` or none was declared —\n * recoverable) or the underlying PII was wiped (`Store.forget_pii` —\n * irrecoverable).\n * top-level field names marked via {@link sensitive}. A pure,\n * read-only helper that inspects the out-of-band sensitive registry (a\n * process-global `WeakMap`) `sensitive()` populates, otherwise\n * unreachable from outside the package. Surfaced for adapters and\n * tooling that must reflect input sensitivity on their own wire\n * surface — e.g. the `@rotorsoft/act-http/openapi` emitter marks these\n * fields `writeOnly` + `format: password` so generated clients and\n * Swagger UI don't echo PII freely. Returns `[]` for non-object\n * schemas or objects with no sensitive fields; top-level shape only.\n */\nexport { pii_fields, REDACTED, SHREDDED } from \"../internal/sensitive.js\";\n\n/**\n * Mark a Zod schema as sensitive. Returns the same schema instance — the\n * marker is registered out-of-band so the static type is preserved and the\n * call site reads as a pure annotation.\n *\n * Idempotent: re-wrapping an already-sensitive schema is a no-op.\n *\n * The marker is what the orchestrator inspects to split event payloads into\n * `data` + `pii` on commit, gate reads via `.discloses`, and strip handler\n * payloads. Part of the sensitive-data foundation (#855 / epic #566).\n *\n * @example\n * ```ts\n * import { z } from \"zod\";\n * import { state, sensitive } from \"@rotorsoft/act\";\n *\n * const UserRegistered = z.object({\n * email: sensitive(z.string()),\n * name: sensitive(z.string()),\n * plan: z.enum([\"free\", \"pro\"]), // not sensitive — stays in events.data\n * });\n * ```\n *\n * @param schema - The Zod schema to mark sensitive.\n * @returns The same schema instance, unmodified at the type level.\n */\nexport function sensitive<T extends z.ZodType>(schema: T): T {\n _registry.add(schema, { sensitive: true });\n // Also stamp the def, so the marker survives the clone Zod produces for\n // any refinement chained AFTER this call — `sensitive(z.string()).min(1)`\n // used to lose it silently and write plaintext into `events.data` (#1417).\n _mark_sensitive(schema);\n return schema;\n}\n\n/**\n * Zod schema for an actor (user, system, etc.).\n */\nexport const ActorSchema = z\n .object({\n id: z.string(),\n name: z.string(),\n })\n .loose()\n .readonly();\n\n/**\n * Zod schema for a target (stream and actor info).\n */\nexport const TargetSchema = z\n .object({\n stream: z.string(),\n actor: ActorSchema,\n expectedVersion: z.number().optional(),\n })\n .loose()\n .readonly();\n\n/**\n * Zod schema for causation event metadata.\n */\nexport const CausationEventSchema = z.object({\n id: z.number(),\n name: z.string(),\n stream: z.string(),\n});\n\n/**\n * Zod schema for event metadata (correlation and causation).\n */\nexport const EventMetaSchema = z\n .object({\n correlation: z.string(),\n causation: z.object({\n action: TargetSchema.and(z.object({ name: z.string() })).optional(),\n event: CausationEventSchema.optional(),\n }),\n })\n .readonly();\n\n/**\n * Zod schema for committed event metadata (id, stream, version, created, meta).\n */\nexport const CommittedMetaSchema = z\n .object({\n id: z.number(),\n stream: z.string(),\n version: z.number(),\n created: z.date(),\n meta: EventMetaSchema,\n })\n .readonly();\n\n/**\n * Type representing the full state schema for a domain.\n * @property events - Map of event names to Zod schemas.\n * @property actions - Map of action names to Zod schemas.\n * @property state - Zod schema for the state object.\n */\nexport type StateSchema = Readonly<{\n events: Record<string, ZodObject<ZodRawShape> | typeof ZodEmpty>;\n actions: Record<string, ZodObject<ZodRawShape> | typeof ZodEmpty>;\n state: ZodObject<ZodRawShape>;\n}>;\n\n/**\n * Query options for event store queries.\n */\nexport const QuerySchema = z\n .object({\n stream: z.string().optional(),\n names: z.string().array().optional(),\n before: z.number().optional(),\n after: z.number().optional(),\n limit: z.number().optional(),\n created_before: z.date().optional(),\n created_after: z.date().optional(),\n backward: z.boolean().optional(),\n correlation: z.string().optional(),\n with_snaps: z.boolean().optional(),\n stream_exact: z.boolean().optional(),\n })\n .readonly();\n","/**\n * @module sensitive\n * @category Internal\n *\n * Internal mechanics for the sensitive-data foundation (#855 / epic #566).\n * The public surface (`sensitive(zodType)`) lives at `libs/act/src/sensitive.ts`\n * and re-exports `REDACTED` / `SHREDDED` from here; this module holds the\n * registry plus the helpers the orchestrator calls during commit, load, and\n * handler dispatch.\n *\n * - `_registry` — process-global `z.registry<{ sensitive: true }>()`. Public\n * `sensitive()` adds to it; the helpers in this module read it.\n * - `pii_fields(schema)` — walk a Zod schema's top-level shape, return the\n * keys marked via `sensitive(...)`.\n * - `pii_gate(event, fields, predicate, actor)` — produce the external\n * view: plaintext when authorized, `[REDACTED]` when not, `[SHREDDED]`\n * when the underlying pii column is null.\n * - `pii_strip(event, fields)` — remove sensitive keys entirely\n * before invoking projection / reaction handlers.\n *\n * @internal\n */\n\nimport { z } from \"zod\";\nimport type { Actor, Committed, Schemas } from \"../types/index.js\";\n\n/**\n * Sentinel placed in `event.data[field]` when the caller isn't authorized to\n * see the sensitive field — either `.discloses(predicate)` returned `false`,\n * or no predicate was declared (framework default-deny). Recoverable: a\n * properly-authorized read returns the plaintext.\n *\n * Re-exported from `libs/act/src/sensitive.ts` as part of the public surface.\n */\nexport const REDACTED = \"[REDACTED]\" as const;\n\n/**\n * Sentinel placed in `event.data[field]` when the underlying PII payload has\n * been wiped via `Store.forget_pii(stream)` — the row's pii column is `NULL`\n * and the original plaintext is gone forever. Irrecoverable.\n *\n * Re-exported from `libs/act/src/sensitive.ts` as part of the public surface.\n */\nexport const SHREDDED = \"[SHREDDED]\" as const;\n\n/**\n * Process-global registry holding every Zod schema marked sensitive. Backed\n * by a `WeakMap`, so wrapper-created instances (`.optional()`, `.nullable()`,\n * `.default()`) that chain off a marked schema produce *new* schema instances\n * the registry doesn't track; the field walker handles those via unwrap.\n *\n * Exported so the public `sensitive(zodType)` wrapper can call `_registry.add`.\n * Underscore prefix marks \"framework-private, don't touch from user code.\"\n *\n * @internal\n */\nexport const _registry = z.registry<{ sensitive: true }>();\n\n/**\n * Marker key stamped onto a schema's own `def`. Zod clones a schema on every\n * refinement (`.min()`, `.email()`, `.trim()`, `.describe()`, `.refine()`,\n * `.transform()`, …) via `{...def}`, which copies own symbol keys — so a\n * marker on the def survives the whole chain, while the `_registry` WeakMap\n * (keyed on the *instance*) does not (#1417).\n *\n * The registry is still populated and still consulted: it covers schemas\n * marked before this key existed, and it is the mechanism the public\n * `sensitive()` doc-comment describes.\n *\n * @internal\n */\nexport const _SENSITIVE = Symbol.for(\"act.sensitive\");\n\n/**\n * Stamp the def-level marker. Called by the public `sensitive()` alongside\n * `_registry.add`.\n *\n * @internal\n */\nexport function _mark_sensitive(schema: z.ZodType): void {\n const def = (\n schema as unknown as {\n _zod?: { def?: Record<PropertyKey, unknown> };\n }\n )._zod?.def;\n if (def) def[_SENSITIVE] = true;\n}\n\n/**\n * True when the given schema was marked via `sensitive(...)`.\n *\n * Walks through Zod wrapper layers (`.optional()`, `.nullable()`,\n * `.default()`, `.readonly()`) by following `_def.innerType` until it reaches\n * a non-wrapper schema, then checks the registry. Wrappers create new schema\n * instances; the marker lives on the *inner* schema the user wrapped, so we\n * test that one.\n *\n * @internal\n */\nexport function is_pii(schema: z.ZodType): boolean {\n let cur: z.ZodType = schema;\n while (true) {\n if (_registry.has(cur)) return true;\n // Def-level marker: survives the clone a refinement produces, which the\n // instance-keyed registry above cannot (#1417).\n const def = (\n cur as unknown as {\n _zod?: { def?: Record<PropertyKey, unknown> };\n }\n )._zod?.def;\n if (def?.[_SENSITIVE] === true) return true;\n const inner = (cur as { _def?: { innerType?: z.ZodType } })._def?.innerType;\n if (!inner || inner === cur) return false;\n cur = inner;\n }\n}\n\n/**\n * Derive an event's sensitive fields, as the declared schema of each.\n *\n * Walks the top-level shape of a `z.object({...})` and keeps the keys whose\n * schema (after unwrapping optional/nullable/default wrappers) was marked via\n * `sensitive(...)`. Returns an empty object for non-object schemas or events\n * with no sensitive fields — the common-case zero-cost path.\n *\n * Only the top-level shape is walked. Sensitive fields nested inside a\n * `z.object` declared inside the event payload would require recursive\n * descent; that's deferred until a real callsite needs it.\n *\n * A union event has no top-level shape, so the options are walked and merged:\n * a key sensitive in any variant must be split, because the stored payload\n * could be that variant ([#1417](https://github.com/Rotorsoft/act-root/issues/1417)).\n * The first variant to declare a key wins, which only matters to a caller that\n * wants the schema rather than the name.\n *\n * Returning the schemas rather than just the names is what lets a caller do\n * something per field — the event builder asks each one whether it holds a\n * date, so the `pii` sidecar's dates can be revived like any other.\n *\n * @internal\n */\nexport function pii_schemas(schema: z.ZodType): Record<string, z.ZodType> {\n const shape = (schema as { shape?: Record<string, z.ZodType> }).shape;\n if (shape && typeof shape === \"object\") {\n const fields: Record<string, z.ZodType> = {};\n for (const key of Object.keys(shape))\n if (is_pii(shape[key])) fields[key] = shape[key];\n return fields;\n }\n const options = (schema as { options?: unknown }).options;\n if (Array.isArray(options)) {\n const fields: Record<string, z.ZodType> = {};\n for (const option of options)\n for (const [key, field] of Object.entries(\n pii_schemas(option as z.ZodType)\n ))\n fields[key] ??= field;\n return fields;\n }\n return {};\n}\n\n/**\n * The names of an event's sensitive fields — {@link pii_schemas} keyed.\n *\n * @internal — consumed by the registry's `sensitive_fields(event_name)` lookup,\n * and public through `types/schemas.ts`, where act-http's OpenAPI emitter uses\n * it to mark request-body properties `writeOnly`.\n */\nexport function pii_fields(schema: z.ZodType): readonly string[] {\n return Object.keys(pii_schemas(schema));\n}\n\n/**\n * Split an emitted event's `data` into `data` (non-sensitive) + `pii`\n * (sensitive) using the field list precomputed at build time. Used by the\n * State's `_pii_split` decorator just before `Store.commit`.\n *\n * Single forward pass over `Object.keys(validated)` — same shape as the\n * spread-and-delete-free implementation in slice 3, just hoisted out of the\n * orchestrator hot path so it's only invoked when the State actually has a\n * sensitive event.\n *\n * @internal\n */\nexport function pii_split<TName, TData extends Record<string, unknown>>(\n emitted: { name: TName; data: TData },\n fields: readonly string[]\n): { name: TName; data: TData; pii: Record<string, unknown> } {\n const data = { ...emitted.data };\n const pii: Record<string, unknown> = {};\n for (const f of fields) {\n if (f in data) {\n pii[f] = data[f];\n delete data[f];\n }\n }\n return { name: emitted.name, data, pii };\n}\n\n/**\n * Build the **external view** of a committed event — the form returned by\n * `load()`, `query()`, `query_array()`, and the snapshot in `do()`'s reply.\n *\n * Only ever reached for events that declare sensitive fields — the sole caller\n * is {@link make_gate}, which the builder invokes exclusively for sensitive\n * events; non-sensitive events short-circuit to {@link IDENTITY_GATE} before\n * they get here. `fields` is therefore guaranteed non-empty (same contract as\n * {@link pii_strip}).\n *\n * - Event whose `pii` payload is null/undefined → substitute {@link SHREDDED}\n * for each declared field. Irrecoverable, so no predicate check.\n * - Event with a `pii` payload, predicate returns `true` → merge `pii` into\n * `data` (plaintext).\n * - Event with a `pii` payload, predicate returns `false` OR no predicate\n * declared (framework default-deny) → substitute {@link REDACTED} for each\n * declared field.\n *\n * @internal\n */\nexport function pii_gate<TEvents extends Schemas, TKey extends keyof TEvents>(\n event: Committed<TEvents, TKey>,\n fields: readonly string[],\n predicate: ((event: any, actor: Actor) => boolean) | null,\n actor: Actor | undefined\n): Committed<TEvents, TKey> {\n const data = event.data as Record<string, unknown>;\n // The external view NEVER carries the isolated `pii` sidecar — dropping it\n // is the whole point of the gate. Keeping it (an earlier `...event` spread)\n // leaked plaintext PII on every gated read surface (`load`, `query`,\n // `query_array`) even while `data` was correctly redacted (#1277). Strip it\n // once here; the plaintext lives in `data` only on the authorized path.\n const { pii, ...rest } = event as Committed<TEvents, TKey> & {\n pii?: Record<string, unknown> | null;\n };\n if (pii == null) {\n const shredded: Record<string, unknown> = { ...data };\n for (const f of fields) shredded[f] = SHREDDED;\n return { ...rest, data: shredded as Committed<TEvents, TKey>[\"data\"] };\n }\n // Plaintext path requires both an actor AND a predicate that allows. Missing\n // either → default-deny → REDACTED.\n const allowed = !!actor && !!predicate && predicate(event, actor);\n if (allowed) {\n return {\n ...rest,\n data: { ...data, ...pii } as Committed<TEvents, TKey>[\"data\"],\n };\n }\n const redacted: Record<string, unknown> = { ...data };\n for (const f of fields) redacted[f] = REDACTED;\n return { ...rest, data: redacted as Committed<TEvents, TKey>[\"data\"] };\n}\n\n/**\n * A prebuilt per-event read gate: given a committed event and the reading\n * actor, return the caller-visible form. This is the single gating primitive\n * the builder prebuilds for **every** read surface — both the actor-less\n * `query` / `query_array` (which pass no actor → default-deny) and the\n * actor-aware `load` / `do`-return view (which pass the reader). Non-sensitive\n * events use the shared {@link IDENTITY_GATE}; sensitive events use a redactor\n * built by {@link make_gate} that closes over the field list and the state's\n * disclosure predicate, so the read path never recomputes the sensitive-field\n * lookup nor allocates per event.\n *\n * The `actor` is optional so the actor-less surfaces can call `gate(event)`.\n *\n * @internal\n */\nexport type EventGate = <TEvents extends Schemas, TKey extends keyof TEvents>(\n event: Committed<TEvents, TKey>,\n actor?: Actor\n) => Committed<TEvents, TKey>;\n\n/**\n * Shared zero-cost gate for every event with no `sensitive(...)` fields — a\n * single frozen reference the builder hands back for non-sensitive events, so\n * the common path is one `Map` miss and an identity call, no allocation. This\n * is the \"by default, return the event\" half of the prebuilt per-event gate.\n *\n * @internal\n */\nexport const IDENTITY_GATE: EventGate = (event) => event;\n\n/**\n * Prebuild a read gate for a sensitive event, capturing its field list and the\n * disclosure predicate once at build time. The returned closure defers to\n * {@link pii_gate} with the reading actor supplied per call:\n *\n * - `predicate = null` (the actor-less `query` surfaces, or a state that never\n * declared `.discloses`) → default-deny: declared fields come back\n * {@link REDACTED} (or {@link SHREDDED} once the pii column is forgotten).\n * - `predicate` set + an authorized actor → plaintext merged into `data`.\n *\n * Either way the isolated `pii` sidecar is dropped. The builder stores one gate\n * per sensitive event (per state for the load path; predicate-less for the\n * query path); non-sensitive events fall back to {@link IDENTITY_GATE}.\n *\n * @internal\n */\nexport function make_gate(\n fields: readonly string[],\n predicate: ((event: any, actor: Actor) => boolean) | null\n): EventGate {\n return (event, actor) => pii_gate(event, fields, predicate, actor);\n}\n\n/**\n * Build the **handler view** — sensitive keys removed entirely from `data`\n * and the `pii` field dropped from the event. Used before invoking projection\n * handlers and reaction handlers, which never see PII by framework rule.\n *\n * Different from {@link pii_gate} (which substitutes {@link REDACTED} or\n * {@link SHREDDED}) — projection tables and reaction sinks shouldn't even\n * structurally observe the keys, so a handler that mistakenly writes\n * `event.data.email` into a column would get `undefined`, not a sentinel\n * string that looks like real data. The strictness is deliberate.\n *\n * Reactions that genuinely need PII (e.g. a welcome-email reaction reading\n * `email`) opt back in by explicitly calling `app.load(stream, { actor:\n * system_actor })` inside the handler — pulling PII through the gate at the\n * call site makes the security-relevant path visible in code review.\n *\n * @internal\n */\nexport function pii_strip<\n TEvents extends Schemas,\n TKey extends keyof TEvents & string,\n>(\n event: Committed<TEvents, TKey>,\n fields: readonly string[]\n): Committed<TEvents, TKey> {\n // Contract: `fields` is non-empty. `build_handle` / `build_handle_batch`\n // filter on `fields.length > 0` before invocation.\n const data = event.data as Record<string, unknown>;\n const stripped: Record<string, unknown> = {};\n for (const k of Object.keys(data)) {\n if (!fields.includes(k)) stripped[k] = data[k];\n }\n const { pii: _drop_pii, ...rest } = event as Committed<TEvents, TKey> & {\n pii?: unknown;\n };\n return {\n ...rest,\n data: stripped as Committed<TEvents, TKey>[\"data\"],\n } as Committed<TEvents, TKey>;\n}\n","/**\n * @packageDocumentation\n * @module act/types\n * Barrel file for Act Framework core types.\n *\n * Re-exports all major type definitions for actions, errors, ports, reactions, registries, and schemas.\n * Also defines common environment and log level types/constants for configuration and logging.\n *\n * @remarks\n * Import from this module to access all core framework types in one place.\n */\nexport type * from \"./action.js\";\nexport type * from \"./audit.js\";\nexport * from \"./errors.js\";\nexport type * from \"./ports.js\";\nexport type * from \"./reaction.js\";\nexport type * from \"./registry.js\";\nexport * from \"./schemas.js\";\n\n/**\n * Supported runtime environments for the framework.\n * - `development`: Local development\n * - `test`: Automated testing\n * - `staging`: Pre-production\n * - `production`: Live/production\n */\nexport const Environments = [\n \"development\",\n \"test\",\n \"staging\",\n \"production\",\n] as const;\n\n/**\n * Type representing a valid environment string.\n */\nexport type Environment = (typeof Environments)[number];\n\n/**\n * Supported log levels for framework logging.\n * - `fatal`, `error`, `warn`, `info`, `debug`, `trace`\n */\nexport const LogLevels = [\n \"fatal\",\n \"error\",\n \"warn\",\n \"info\",\n \"debug\",\n \"trace\",\n] as const;\n\n/**\n * Type representing a valid log level string.\n */\nexport type LogLevel = (typeof LogLevels)[number];\n","import { prettifyError, ZodError, type ZodType } from \"zod\";\nimport { config } from \"./config.js\";\nimport { ValidationError } from \"./types/index.js\";\n\n/**\n * @module utils\n * @category Utilities\n *\n * Small utilities used across the framework:\n * - {@link validate} — parse a payload against a Zod schema, throwing\n * {@link ValidationError} on failure.\n * - {@link extend} — validate a source object and merge into defaults.\n * - {@link sleep} — async delay (default duration from `config().sleepMs`).\n */\n\n/**\n * Parse `payload` against `schema`, returning the validated value or throwing\n * a {@link ValidationError} with prettified Zod details. When `schema` is\n * omitted, returns `payload` unchanged. The framework calls this for every\n * `app.do()` action, every emitted event, and every state init.\n *\n * @example\n * ```typescript\n * const UserSchema = z.object({ email: z.string().email() });\n * const user = validate(\"User\", { email: \"alice@example.com\" }, UserSchema);\n * ```\n *\n * @see {@link ValidationError}\n */\nexport const validate = <S>(\n target: string,\n payload: Readonly<S>,\n schema?: ZodType<S>\n): Readonly<S> => {\n try {\n return schema ? schema.parse(payload) : payload;\n } catch (error) {\n if (error instanceof ZodError) {\n throw new ValidationError(target, payload, prettifyError(error));\n }\n throw new ValidationError(target, payload, error);\n }\n};\n\n/**\n * Validate `source` against `schema` and return a new object that merges\n * `source` over the optional `target` defaults. Used by {@link config} for\n * env-var-overrides-defaults patterns; safe to call elsewhere — it never\n * mutates `target`.\n *\n * @example\n * ```typescript\n * const schema = z.object({ host: z.string(), port: z.number() });\n * const cfg = extend({ port: 8080 }, schema, { host: \"localhost\", port: 80 });\n * // → { host: \"localhost\", port: 8080 }\n * ```\n *\n * @throws {@link ValidationError} if `source` fails the schema.\n */\nexport const extend = <\n S extends Record<string, unknown>,\n T extends Record<string, unknown>,\n>(\n source: Readonly<S>,\n schema: ZodType<S>,\n target?: Readonly<T>\n): Readonly<S & T> => {\n const value = validate(\"config\", source, schema);\n return { ...target, ...value } as Readonly<S & T>;\n};\n\n/**\n * Pause for `ms` milliseconds (or `config().sleepMs` when omitted — `100ms`\n * in dev, `0ms` in tests). Used by adapters to simulate async I/O.\n *\n * @example\n * ```typescript\n * await sleep(); // default delay from config\n * await sleep(500); // explicit 500ms\n * ```\n */\nexport async function sleep(ms?: number) {\n return new Promise((resolve) => setTimeout(resolve, ms ?? config().sleepMs));\n}\n\n/**\n * Regex metacharacters that, when present in a reaction `source`, make it a\n * pattern rather than a literal stream name. A source containing none of\n * these is a bare stream name — the common case — and every claim/fetch\n * site matches it by string equality on the store's stream index. A source\n * containing any of them is compiled as a RegExp and matched against\n * candidate streams with the caller's own anchoring (e.g. `^(A|B)$`).\n */\nconst SOURCE_METACHARACTERS = /[\\^$.*+?()[\\]{}|\\\\]/;\n\n/**\n * True when `source` is a **literal** stream name — it carries no regex\n * metacharacter, so every adapter treats it as an exact match. This is the\n * fast, index-friendly path and covers every autoclose/dynamic-resolver\n * source (bare stream names). A `false` return means the source is a\n * **pattern** (contains `^ $ . * + ? ( ) [ ] { } | \\`) and must be compiled\n * as a RegExp before matching — the shape the calculator's static\n * `source: \"^(A|B)$\"` reaction relies on.\n *\n * The single source of truth for literal-vs-pattern classification across\n * the InMemory has-work probe, the drain fetch path, and the SQL adapters,\n * so all three agree on which sources take the exact path.\n *\n * @example\n * ```typescript\n * is_literal_source(\"Board\"); // → true (exact lookup)\n * is_literal_source(\"^(A|B)$\"); // → false (compile as RegExp)\n * ```\n */\nexport function is_literal_source(source: string): boolean {\n return !SOURCE_METACHARACTERS.test(source);\n}\n\n/**\n * Matches an ISO-8601 timestamp, the shape `JSON.stringify` produces for a\n * `Date`.\n *\n * @internal\n */\nconst ISO_8601 =\n /^(\\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(\\.\\d+)?(Z|[+-][0-2][0-9]:[0-5][0-9])?$/;\n\n/**\n * Revive ISO-8601-shaped strings into `Date`s during `JSON.parse`.\n *\n * **The framework no longer uses this.** Dates are resolved from the declared\n * `z.date()` paths at build time and converted on read, so a field declared\n * `z.string()` keeps its string even when the value looks like a timestamp\n * ([#1556](https://github.com/Rotorsoft/act-root/issues/1556)). Adapters\n * return what they stored; typing is the orchestrator's job.\n *\n * Kept exported for host applications that parse Act JSON themselves and want\n * the old shape-based behaviour. New code should let the schema decide.\n *\n */\nexport const dateReviver = (_key: string, value: unknown): unknown =>\n typeof value === \"string\" && ISO_8601.test(value) ? new Date(value) : value;\n","/**\n * @module scoped\n * @category Internal\n *\n * Ambient execution context — every `AsyncLocalStorage` the framework owns\n * lives here, and so does every operation on one. No other module calls\n * `.run()` or `.getStore()`; they ask for a runner or a reader instead, so\n * this file is the single place to look at the abstraction.\n *\n * Two contexts:\n *\n * - **ports** — the active Act's store/cache bag, so `store()` / `cache()`\n * resolve per-Act rather than per-process (ACT-501). Installed by the\n * orchestrator via {@link make_run_scoped}, read by the port singletons\n * via {@link current_ports}.\n * - **reaction** — the event a handler is processing, so a dispatch made\n * anywhere inside that handler can thread `reactingTo` whichever `IAct`\n * reference made the call (#1541). Installed via {@link run_reacting},\n * read at the orchestrator boundary via {@link current_reacting}.\n *\n * Keeping the mechanics here keeps ambient state out of `internal/`, whose\n * modules are stateless implementations that receive what they need.\n *\n * @internal\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport type {\n Actor,\n Cache,\n Committed,\n IAct,\n Schemas,\n Store,\n} from \"./types/index.js\";\n\n/** Per-Act ports bag (ACT-501). Both required together — a shared cache across stores would collide on stream keys. */\nexport type Scoped = {\n readonly store: Store;\n readonly cache: Cache;\n};\n\n/**\n * AsyncLocalStorage carrying the active Act's ports.\n *\n * Exported for in-repo tooling that measures the context itself (the\n * scope-overhead bench). NOT public: `ports.ts` no longer re-exports it and\n * `index.ts` reaches this module only for the `Scoped` type, so it is not\n * importable from the package. Everything else uses the helpers below.\n */\nexport const scoped = new AsyncLocalStorage<Scoped>();\n\n/**\n * The reaction currently running, as a box the handler can empty.\n *\n * A frame captured by work the handler started and did not await outlives the\n * handler and can never be unbound. The *frame* is unreclaimable, but the\n * *box inside it* is not: clearing `event` as the handler settles turns every\n * later read through that frame into \"no reaction\", without anyone having to\n * ask a second question (#1562).\n *\n * Not reachable from `index.ts`.\n */\ntype Reacting = { event: Committed<Schemas, string> | undefined };\n\nconst reacting = new AsyncLocalStorage<Reacting>();\n\n/**\n * Builds the runner an Act uses to enter its own port scope.\n *\n * Every Act gets a frame, including one built without `ActOptions.scoped` —\n * that one carries the singleton adapters (see `default_scope` in `ports.ts`).\n * Entering unconditionally is what makes an Act's ports its own: a runner that\n * collapsed to `fn()` for a singleton Act did not leave whatever frame it was\n * called from, so dispatching into a shared Act from inside a tenant's handler\n * resolved `store()` to that tenant and wrote the shared Act's events into the\n * tenant's log ([#1597](https://github.com/Rotorsoft/act-root/issues/1597)).\n *\n * @internal\n */\nexport function make_run_scoped(\n bag: Scoped\n): <T>(fn: () => Promise<T>) => Promise<T> {\n return (fn) => scoped.run(bag, fn);\n}\n\n/**\n * The active Act's ports, or `undefined` outside any Act.\n *\n * Every Act runs in a frame, so this is `undefined` only for a call made\n * outside one — `store()` and `cache()` fall back to the singleton adapters\n * there, which is what a bare `store()` in application setup expects.\n *\n * @internal\n */\nexport function current_ports(): Scoped | undefined {\n return scoped.getStore();\n}\n\n/**\n * Runs `fn` with `event` installed as the triggering-event context.\n *\n * Entered per payload rather than per lease: the context has to unwind with\n * the handler so it never reaches the drain cycle, and work a handler started\n * without awaiting has to resume into its own event's frame.\n *\n * @internal\n */\nexport function run_reacting<T>(\n event: Committed<Schemas, string>,\n fn: () => Promise<T>\n): Promise<T> {\n const box: Reacting = { event };\n return reacting.run(box, async () => {\n try {\n return await fn();\n } finally {\n // Anything still running keeps this frame; from here it reads empty.\n box.event = undefined;\n }\n });\n}\n\n/**\n * The event being reacted to, or `undefined` outside a running handler —\n * including inside work that outlived one.\n *\n * @internal\n */\nexport function current_reacting(): Committed<Schemas, string> | undefined {\n return reacting.getStore()?.event;\n}\n\n/**\n * Everything a reaction handler runs inside: the `IAct` facade it is handed\n * as its third argument, and the triggering-event context that every dispatch\n * made within it resolves — including one through a captured `app`.\n *\n * Built here rather than in the dispatcher so the dispatcher never has to\n * know how either half works; it receives this whole and calls it.\n *\n * @internal\n */\nexport type ReactionScope<\n TEvents extends Schemas,\n TActions extends Schemas,\n TActor extends Actor = Actor,\n> = {\n readonly app: IAct<TEvents, TActions, TActor>;\n readonly run: <T>(\n event: Committed<Schemas, string>,\n fn: () => Promise<T>\n ) => Promise<T>;\n};\n\n/**\n * Assembles the reaction scope from the orchestrator's bound `IAct` methods.\n *\n * @internal\n */\nexport function make_reaction_scope<\n TEvents extends Schemas,\n TActions extends Schemas,\n TActor extends Actor = Actor,\n>(\n app: IAct<TEvents, TActions, TActor>\n): ReactionScope<TEvents, TActions, TActor> {\n return { app, run: run_reacting };\n}\n","import { ConsoleLogger } from \"./adapters/console-logger.js\";\nimport { InMemoryCache } from \"./adapters/in-memory-cache.js\";\nimport { InMemoryStore } from \"./adapters/in-memory-store.js\";\nimport { config } from \"./config.js\";\nimport { register_disposer, run_disposers } from \"./disposers.js\";\nimport { current_ports, type Scoped } from \"./scoped.js\";\nimport type {\n Cache,\n Disposable,\n Disposer,\n Logger,\n Store,\n} from \"./types/index.js\";\n\n// `Scoped` is the type of the public `ActOptions.scoped` bag, so it stays\n// exported. The AsyncLocalStorage carrying it does not: it is a mechanism,\n// it was only ever reachable because `index.ts` star-exports this module,\n// and its own doc-comment already declared it internal.\nexport type { Scoped } from \"./scoped.js\";\n\n/**\n * Port/adapter infrastructure for the Act framework.\n *\n * All infrastructure concerns (logging, storage, caching) are managed as\n * singleton adapters injected via port functions. Each port follows the same\n * pattern: first call wins with a sensible default, optional adapter injection.\n *\n * - `log()` — structured logging (default: ConsoleLogger)\n * - `store()` — event persistence (default: InMemoryStore)\n * - `cache()` — state checkpoints (default: InMemoryCache)\n * - `dispose()` — register cleanup functions for graceful shutdown\n *\n * @module ports\n */\n\n/**\n * List of exit codes for process termination. Consumed by signal handlers\n * and {@link disposeAndExit}; not part of the user-facing surface.\n *\n * @internal\n */\nexport const ExitCodes = [\"ERROR\", \"EXIT\"] as const;\n\n/**\n * Type for allowed exit codes.\n *\n * - `\"ERROR\"` — abnormal termination (uncaught exception, unhandled rejection)\n * - `\"EXIT\"` — clean shutdown (SIGINT, SIGTERM, or manual trigger)\n *\n * @internal\n */\nexport type ExitCode = (typeof ExitCodes)[number];\n\n// ---------------------------------------------------------------------------\n// Port factory\n// ---------------------------------------------------------------------------\n\n/**\n * Factory function that creates or returns the injected adapter.\n * @internal\n */\ntype Injector<Port extends Disposable> = (adapter?: Port) => Port;\n\n/** Singleton adapter registry, keyed by injector function name. */\nconst adapters = new Map<string, Disposable>();\n\n/**\n * Creates a singleton port with optional adapter injection.\n *\n * The first call initializes the adapter (using the provided adapter or the\n * injector's default). Subsequent calls return the cached singleton. Adapters\n * are disposed in reverse registration order during {@link disposeAndExit}.\n *\n * @param injector - Named function that creates the default adapter\n * @returns Port function: call with no args to get the singleton, or pass an\n * adapter on the first call to override the default\n *\n * @example\n * ```typescript\n * const store = port(function store(adapter?: Store) {\n * return adapter || new InMemoryStore();\n * });\n * const s = store(); // InMemoryStore\n * ```\n */\nexport function port<Port extends Disposable>(injector: Injector<Port>) {\n return (adapter?: Port): Port => {\n if (!adapters.has(injector.name)) {\n const injected = injector(adapter);\n adapters.set(injector.name, injected);\n // log() is now in adapters (or this IS the log port we just registered),\n // so the recursive call resolves immediately. Routing through the logger\n // means level gating (e.g. silenced in tests at fatal) takes effect.\n log().info(`[act] + ${injector.name}:${injected.constructor.name}`);\n }\n return adapters.get(injector.name) as Port;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Ports: log, store, cache\n// ---------------------------------------------------------------------------\n\n/**\n * Gets or injects the singleton logger.\n *\n * By default, Act uses a built-in {@link ConsoleLogger} that emits JSON lines\n * in production (compatible with GCP, AWS CloudWatch, Datadog) and colorized\n * output in development — zero external dependencies.\n *\n * For pino, inject a `PinoLogger` from `@rotorsoft/act-pino` before building\n * your application.\n *\n * @param adapter - Optional logger implementation to inject\n * @returns The singleton logger instance\n *\n * @example Default console logger\n * ```typescript\n * import { log } from \"@rotorsoft/act\";\n * const logger = log();\n * logger.info(\"Application started\");\n * ```\n *\n * @example Injecting pino\n * ```typescript\n * import { log } from \"@rotorsoft/act\";\n * import { PinoLogger } from \"@rotorsoft/act-pino\";\n * log(new PinoLogger({ level: \"debug\", pretty: true }));\n * ```\n *\n * @see {@link Logger} for the interface contract\n * @see {@link ConsoleLogger} for the default implementation\n */\nexport const log = port(function log(adapter?: Logger) {\n const cfg = config();\n return (\n adapter ||\n new ConsoleLogger({\n level: cfg.logLevel,\n pretty: cfg.env !== \"production\",\n })\n );\n});\n\n/**\n * Gets or injects the singleton event store.\n *\n * By default, Act uses an {@link InMemoryStore} suitable for development and\n * testing. For production, inject a persistent store like `PostgresStore` from\n * `@rotorsoft/act-pg` before building your application.\n *\n * **Important:** Store injection must happen before creating any Act instances.\n * Once set, the store cannot be changed without restarting the application.\n *\n * @param adapter - Optional store implementation to inject\n * @returns The singleton store instance\n *\n * @example Default in-memory store\n * ```typescript\n * import { store } from \"@rotorsoft/act\";\n * const s = store();\n * ```\n *\n * @example Injecting PostgreSQL\n * ```typescript\n * import { store } from \"@rotorsoft/act\";\n * import { PostgresStore } from \"@rotorsoft/act-pg\";\n *\n * store(new PostgresStore({\n * host: \"localhost\",\n * port: 5432,\n * database: \"myapp\",\n * user: \"postgres\",\n * password: \"secret\",\n * }));\n * ```\n *\n * @see {@link Store} for the interface contract\n * @see {@link InMemoryStore} for the default implementation\n */\n// ALS check lives outside `port()` — its cache fires only once, so the\n// per-call branch on a scoped Act has to be in the public wrapper.\nconst _store = port(function store(adapter?: Store): Store {\n return adapter ?? new InMemoryStore();\n});\n\nexport const store = ((adapter?: Store): Store => {\n return current_ports()?.store ?? _store(adapter);\n}) as (adapter?: Store) => Store;\n\n/**\n * Gets or injects the singleton cache.\n *\n * By default, Act uses an {@link InMemoryCache} (LRU, maxSize 1000). For\n * distributed deployments, inject a Redis-backed cache before building your\n * application.\n *\n * @param adapter - Optional cache implementation to inject\n * @returns The singleton cache instance\n *\n * @see {@link Cache} for the interface contract\n * @see {@link InMemoryCache} for the default implementation\n */\nconst _cache = port(function cache(adapter?: Cache) {\n return adapter ?? new InMemoryCache();\n});\n\nexport const cache = ((adapter?: Cache): Cache => {\n return current_ports()?.cache ?? _cache(adapter);\n}) as (adapter?: Cache) => Cache;\n\n/**\n * The ports bag an Act without `ActOptions.scoped` runs in: the singleton\n * adapters, read through the same frame a scoped Act uses.\n *\n * Every Act entering a frame is what keeps its ports its own. Without one, a\n * shared Act called from inside a tenant's handler inherited that tenant's\n * frame and committed to the tenant's store (#1597).\n *\n * The properties are getters on purpose. The adapters are resolved lazily and\n * injected after `act().build()` in the normal case — `store(new PgStore())`\n * in application setup, or a test's `beforeEach` — so capturing them when the\n * bag is made would pin whatever existed at build time. They read the raw\n * resolvers rather than the public `store()`/`cache()`, which consult the\n * frame this bag *is* and would recurse.\n *\n * @internal\n */\nexport const default_scope = (): Scoped => DEFAULT_SCOPE;\n\nconst DEFAULT_SCOPE: Scoped = {\n get store() {\n return _store();\n },\n get cache() {\n return _cache();\n },\n};\n\n// ---------------------------------------------------------------------------\n// Disposal\n// ---------------------------------------------------------------------------\n\n/**\n * Registered cleanup functions live in `disposers.ts`, which holds\n * lifetime-bound entries weakly so registering never pins its target for the\n * process lifetime (#1441). The public surface here is unchanged.\n */\n\n/**\n * Disposes all registered adapters and disposers, then exits the process.\n *\n * Execution order:\n * 1. Custom disposers (registered via {@link dispose}) — in reverse order\n * 2. Port adapters (log, store, cache) — in reverse registration order\n * 3. Adapter registry is cleared\n * 4. Process exits (skipped in test environment)\n *\n * In production, `\"ERROR\"` exits are silently ignored to avoid crashing on\n * transient failures (e.g. an uncaught promise in a non-critical path).\n *\n * @param code - Exit code: `\"EXIT\"` for clean shutdown (exit 0),\n * `\"ERROR\"` for abnormal termination (exit 1)\n */\nexport async function disposeAndExit(code: ExitCode = \"EXIT\"): Promise<void> {\n if (code === \"ERROR\" && config().env === \"production\") {\n // Surface the swallow so incident triage can see it. Without this\n // log the framework looks unresponsive after an uncaught exception\n // in prod — exactly when operators most need a breadcrumb.\n log().warn(\n \"disposeAndExit('ERROR') ignored in production — process kept alive\"\n );\n return;\n }\n\n // Run sequentially in reverse registration order so a disposer can rely on\n // later-registered disposers (and adapters on later-registered adapters)\n // having already finished — Promise.all would race them.\n await run_disposers();\n for (const adapter of [...adapters.values()].reverse()) {\n await adapter.dispose();\n log().info(`[act] - ${adapter.constructor.name}`);\n }\n adapters.clear();\n config().env !== \"test\" && process.exit(code === \"ERROR\" ? 1 : 0);\n}\n\n/**\n * Registers a cleanup function for graceful shutdown.\n *\n * Disposers are called automatically on SIGINT, SIGTERM, uncaught exceptions,\n * and unhandled rejections. They execute in reverse registration order before\n * port adapters are disposed.\n *\n * @param disposer - Async function to call during cleanup. Omit to get a\n * reference to {@link disposeAndExit} without registering.\n * @returns Function to manually trigger disposal and exit\n *\n * @example\n * ```typescript\n * import { dispose } from \"@rotorsoft/act\";\n *\n * const db = connectDatabase();\n * dispose(async () => await db.close());\n *\n * // In tests\n * afterAll(async () => await dispose()());\n * ```\n *\n * @see {@link disposeAndExit} for the full shutdown sequence\n */\nexport function dispose(\n disposer?: Disposer\n): (code?: ExitCode) => Promise<void> {\n disposer && register_disposer(disposer);\n return disposeAndExit;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Event name used internally for snapshot events in the event store.\n * Snapshot events store a full state checkpoint, enabling efficient cold-start\n * recovery without replaying the entire event stream.\n */\nexport const SNAP_EVENT = \"__snapshot__\";\n\n/**\n * Event name used internally for tombstone events in the event store.\n * A tombstone marks a stream as permanently closed — no further writes\n * are permitted until the stream is explicitly restarted via `close()`.\n *\n * @see {@link Act.close} for the close-the-books API\n */\nexport const TOMBSTONE_EVENT = \"__tombstone__\";\n\n/**\n * Name of the implicit lane every reaction lands in unless its `.to({lane})`\n * declaration says otherwise (ACT-1103). Acts that don't call\n * `.withLane(...)` see only this lane, and behavior is identical to\n * pre-1103 single-controller drain.\n *\n * Persisted on `streams.lane` and threaded as the strict-typed default in\n * builder generics — `lane?: TLanes` always includes `\"default\"`.\n */\nexport const DEFAULT_LANE = \"default\";\n","/**\n * @packageDocumentation\n * @module act/adapters\n * In-memory event store adapter for the Act Framework.\n *\n * This adapter implements the Store interface and is suitable for development, testing, and demonstration purposes.\n * All data is stored in memory and lost on process exit.\n *\n * @category Adapters\n */\n\nimport { DEFAULT_LANE, SNAP_EVENT, TOMBSTONE_EVENT } from \"../ports.js\";\nimport { ConcurrencyError } from \"../types/errors.js\";\nimport type {\n BlockedLease,\n Committed,\n EventMeta,\n Lease,\n Message,\n Query,\n QueryStatsOptions,\n QueryStreams,\n QueryStreamsResult,\n Schema,\n Schemas,\n Store,\n StreamFilter,\n StreamPosition,\n StreamStats,\n SubscribeInput,\n} from \"../types/index.js\";\nimport { sleep } from \"../utils.js\";\n\n/**\n * @internal\n * Represents an in-memory stream for event processing and leasing.\n */\nclass InMemoryStream {\n readonly stream: string;\n readonly source: string | undefined;\n private _at = -1;\n private _retry = -1;\n private _blocked = false;\n private _error = \"\";\n private _leased_by: string | undefined = undefined;\n private _leased_until: Date | undefined = undefined;\n private _priority = 0;\n private _lane: string = DEFAULT_LANE;\n // Persisted next-visit time (#1090). When set and still in the future, the\n // stream is held out of `claim` entirely — so a deferred reaction is not\n // re-claimed (and `retry` is never bumped) until its due-time passes. Unlike\n // in-process backoff, this is durable store state shared across workers.\n private _deferred_at: number | undefined = undefined;\n // Work mark (#1485): highest event id observed to resolve to this target.\n // `undefined` means UNKNOWN — the row predates the mark, so `claim` falls\n // back to the legacy has-work probe rather than treating it as \"no work\".\n private _correlated_at: number | undefined = undefined;\n\n constructor(\n stream: string,\n source: string | undefined,\n priority = 0,\n lane: string = DEFAULT_LANE\n ) {\n this.stream = stream;\n this.source = source;\n this._priority = priority;\n this._lane = lane;\n }\n\n get priority() {\n return this._priority;\n }\n\n get lane() {\n return this._lane;\n }\n\n /** Replace on every subscribe — current builder config wins on restart. */\n set lane(value: string) {\n this._lane = value;\n }\n\n /**\n * Bump the priority via {@link subscribe}: keeps the maximum across\n * reactions so the highest-priority registrant wins.\n */\n bump_priority(priority: number) {\n if (priority > this._priority) this._priority = priority;\n }\n\n get correlated_at() {\n return this._correlated_at;\n }\n\n /**\n * Raise the work mark via {@link subscribe}: keeps the maximum, for every\n * value including zero and negatives, so a mark never regresses.\n */\n mark(correlated_at: number) {\n if (\n this._correlated_at === undefined ||\n correlated_at > this._correlated_at\n )\n this._correlated_at = correlated_at;\n }\n\n /**\n * Set the priority outright via {@link prioritize}: operator\n * runtime override that ignores the build-time `max()` invariant.\n */\n set_priority(priority: number) {\n this._priority = priority;\n }\n\n get is_available() {\n return (\n !this._blocked &&\n (!this._leased_until || this._leased_until <= new Date()) &&\n // A stream deferred to a future time is not claimable until due (#1090).\n (!this._deferred_at || this._deferred_at <= Date.now())\n );\n }\n\n /**\n * Hold this stream out of `claim` until `deferred_at` (ms since epoch).\n * Set by a deliberate `defer` outcome — not a failure, so retry/blocked\n * state is untouched. Cleared by ack/block/reset/unblock.\n */\n defer(deferred_at: number) {\n this._deferred_at = deferred_at;\n // A defer is not a failure: reset retry so the redelivery after the\n // due-time is a fresh attempt, never accumulating toward maxRetries.\n this._retry = -1;\n }\n\n get at() {\n return this._at;\n }\n\n get retry() {\n return this._retry;\n }\n\n get blocked() {\n return this._blocked;\n }\n\n get error() {\n return this._error;\n }\n\n get leased_by() {\n return this._leased_by;\n }\n\n get leased_until() {\n return this._leased_until;\n }\n\n /** Persisted next-visit time (#1090/#1221), or undefined when no active defer. */\n get deferred_at() {\n return this._deferred_at;\n }\n\n /**\n * Attempt to lease this stream for processing.\n * @param lease - The lease request.\n * @param millis - Lease duration in milliseconds.\n * @returns The granted lease or undefined if blocked.\n */\n lease(lease: Lease, millis: number): Lease {\n if (millis > 0) {\n this._leased_by = lease.by;\n this._leased_until = new Date(Date.now() + millis);\n }\n this._retry = this._retry + 1;\n return {\n stream: this.stream,\n source: this.source,\n at: lease.at,\n by: lease.by,\n retry: this._retry,\n lagging: lease.lagging,\n lane: this._lane,\n };\n }\n\n /**\n * Finalize this stream's lease: ack (advance the watermark) or, when the\n * lease carries a `due` marker, defer (persist the schedule, hold the\n * watermark) — see {@link Store.ack}.\n * @param lease - The lease request.\n */\n ack(lease: Lease) {\n if (this._leased_by === lease.by) {\n this._leased_by = undefined;\n this._leased_until = undefined;\n if (lease.due !== undefined) {\n // Due marker: advance the watermark past the events handled this cycle\n // (`lease.at`) AND schedule the re-visit, persisting the caller's\n // retry — advance and defer are independent legs (#1278). Advancing\n // means the succeeded prefix never re-runs on redelivery. An explicit\n // defer passes `retry: -1` (a defer is not a failure); a backoff retry\n // passes the climbing counter so it keeps accruing toward the block\n // threshold across windows (#1262). Deferred entries are not part of\n // ack's return value.\n this._at = lease.at;\n this._retry = lease.retry;\n this._deferred_at = lease.due;\n return undefined;\n }\n // A successful ack clears the retry budget and any pending defer.\n this._retry = -1;\n this._at = lease.at;\n this._deferred_at = undefined;\n return {\n stream: this.stream,\n source: this.source,\n at: this._at,\n by: lease.by,\n retry: this._retry,\n lagging: lease.lagging,\n lane: this._lane,\n };\n }\n }\n\n /**\n * Block a stream for processing after failing to process and reaching max retries with blocking enabled.\n * @param lease - The lease request.\n * @param error Blocked error message.\n */\n block(lease: Lease, error: string) {\n // Skip already-blocked streams so a redundant block is a no-op, mirroring\n // the SQL adapters' `WHERE ... AND blocked = false` guard (#1263). Without\n // this, re-blocking returns the lease again and emits a spurious duplicate\n // `blocked` lifecycle event that the durable stores suppress.\n if (this._leased_by === lease.by && !this._blocked) {\n this._blocked = true;\n this._error = error;\n // A blocked stream is poison; clear any pending defer (#1090).\n this._deferred_at = undefined;\n return {\n stream: this.stream,\n source: this.source,\n at: this._at,\n by: this._leased_by,\n retry: this._retry,\n error: this._error,\n lagging: lease.lagging,\n lane: this._lane,\n };\n }\n }\n\n /**\n * Reset this stream's watermark and state for replay. The retry counter\n * resets to -1 to match the constructor + ack() invariant (\"released\n * stream\"); the next claim() bumps it to 0 (first attempt).\n */\n reset() {\n this._at = -1;\n this._retry = -1;\n this._blocked = false;\n this._error = \"\";\n this._leased_by = undefined;\n this._leased_until = undefined;\n this._deferred_at = undefined;\n }\n\n /**\n * Clear the blocked flag and lease bookkeeping without touching the\n * watermark. Returns true if the stream was actually blocked (and is\n * now flipped); false otherwise.\n */\n unblock(): boolean {\n if (!this._blocked) return false;\n this._blocked = false;\n this._retry = -1;\n this._error = \"\";\n this._leased_by = undefined;\n this._leased_until = undefined;\n this._deferred_at = undefined;\n return true;\n }\n}\n\n/**\n * In-memory event store implementation.\n *\n * This is the default store used by Act when no other store is injected.\n * It stores all events in memory and is suitable for:\n * - Development and prototyping\n * - Unit and integration testing\n * - Demonstrations and examples\n *\n * **Not suitable for production** - all data is lost when the process exits.\n * Use {@link PostgresStore} for production deployments.\n *\n * The in-memory store provides:\n * - Full {@link Store} interface implementation\n * - Optimistic concurrency control\n * - Stream leasing for distributed processing simulation\n * - Snapshot support\n * - Fast performance (no I/O overhead)\n *\n * **`Store.notify` is intentionally not implemented.** The notify hook is a\n * cross-process wake-up signal — local commits already arm the drain via\n * `do()`. An in-memory store is single-process by definition, so there is\n * no remote writer to be notified of. The {@link Act} orchestrator\n * detects the absence and falls back to the existing debounce/poll path.\n *\n * @example Using in tests\n * ```typescript\n * import { store } from \"@rotorsoft/act\";\n *\n * describe(\"Counter\", () => {\n * beforeEach(async () => {\n * // Reset store between tests\n * await store().seed();\n * });\n *\n * it(\"increments\", async () => {\n * await app.do(\"increment\", target, { by: 5 });\n * const snapshot = await app.load(Counter, \"counter-1\");\n * expect(snapshot.state.count).toBe(5);\n * });\n * });\n * ```\n *\n * @example Explicit instantiation\n * ```typescript\n * import { InMemoryStore } from \"@rotorsoft/act\";\n *\n * const testStore = new InMemoryStore();\n * await testStore.seed();\n *\n * // Use for specific test scenarios\n * await testStore.commit(\"test-stream\", events, meta);\n * ```\n *\n * @example Querying events\n * ```typescript\n * const events: any[] = [];\n * await store().query(\n * (event) => events.push(event),\n * { stream: \"test-stream\" }\n * );\n * console.log(`Found ${events.length} events`);\n * ```\n *\n * @see {@link Store} for the interface definition\n * @see {@link PostgresStore} for production use\n * @see {@link store} for injecting stores\n *\n * @category Adapters\n */\nexport class InMemoryStore implements Store {\n // stored events\n private _events: Committed<Schemas, keyof Schemas>[] = [];\n // next event id — monotonic, never reused. Deletions (truncate, windowed\n // prune) punch holes in the id sequence, so ids are NOT array indexes and\n // NOT `_events.length`; they only stay sorted ascending in `_events`.\n private _next_id = 0;\n // stored stream positions and other metadata\n private _streams: Map<string, InMemoryStream> = new Map();\n /** Correlate checkpoint (#1484): how far the log has been READ. */\n private _correlated_at = -1;\n /**\n * Per-correlator checkpoint and lease (#1532), keyed by correlator.\n *\n * Keyed rather than singular because correlators that look for different\n * things read the log for different reasons: sharing one position lets a\n * partial-behaviour worker inherit another's, and sharing one lease lets\n * one starve the other. Callers that supply no correlator use\n * `_correlated_at` instead, which is the pre-#1532 behaviour.\n */\n private _correlators = new Map<\n string,\n { at: number; by: string; until: number }\n >();\n // last committed version per stream — O(1) replacement for filter-on-commit\n private _stream_versions: Map<string, number> = new Map();\n // max non-snapshot event id per stream — drives the has-work probe in\n // claim(): an O(1) lookup for a literal source, and the per-stream max\n // that a pattern source scans, without touching the full event log.\n private _max_event_id_by_stream: Map<string, number> = new Map();\n // global max non-snapshot event id — fast pre-check for source-less streams in claim()\n private _max_non_snap_event_id = -1;\n // stream → (event_id → cloned sensitive payload). Two-level so `forget_pii`\n // is O(1) — drop the inner Map for the stream and the wipe is done — mirroring\n // the `DELETE WHERE stream = ?` scope that durable adapters get from their\n // stream index. Entries exist only for events committed with a non-null\n // `pii` field; absence means \"no PII\" (returned as `null` on load).\n private _pii: Map<string, Map<number, Record<string, unknown>>> = new Map();\n\n private _reset_indexes() {\n this._events.length = 0;\n this._next_id = 0;\n this._correlated_at = -1;\n this._stream_versions.clear();\n this._max_event_id_by_stream.clear();\n this._max_non_snap_event_id = -1;\n this._pii.clear();\n }\n\n // First index whose event id is greater than `after`. `_events` stays\n // sorted ascending by id (append-only commits; deletions preserve\n // order), so id-bounded scans binary-search their start instead of\n // assuming id === index — an invariant that truncation breaks.\n private _first_index_after(after: number): number {\n let lo = 0;\n let hi = this._events.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (this._events[mid].id > after) hi = mid;\n else lo = mid + 1;\n }\n return lo;\n }\n\n // Attach the isolated PII payload (or null) to an event before handing it to\n // a caller. Allocation-free for events without PII — by far the common case.\n private _with_pii<E extends Schemas>(\n e: Committed<E, keyof E>\n ): Committed<E, keyof E> {\n const pii = this._pii.get(e.stream)?.get(e.id);\n return pii ? ({ ...e, pii } as Committed<E, keyof E>) : e;\n }\n\n /**\n * Dispose of the store and clear all events.\n * @returns Promise that resolves when disposal is complete.\n */\n async dispose() {\n await sleep();\n this._reset_indexes();\n }\n\n /**\n * Seed the store with initial data (no-op for in-memory).\n * @returns Promise that resolves when seeding is complete.\n */\n async seed() {\n await sleep();\n }\n\n /**\n * Drop all data from the store.\n * @returns Promise that resolves when the store is cleared.\n */\n async drop() {\n await sleep();\n this._reset_indexes();\n this._streams = new Map();\n }\n\n private in_query<E extends Schemas>(query: Query, e: Committed<E, keyof E>) {\n if (query.stream) {\n if (query.stream_exact) {\n if (e.stream !== query.stream) return false;\n } else if (!RegExp(query.stream).test(e.stream)) return false;\n }\n if (query.names && !query.names.includes(e.name as string)) return false;\n if (query.correlation && e.meta?.correlation !== query.correlation)\n return false;\n if (e.name === SNAP_EVENT && !query.with_snaps) return false;\n return true;\n }\n\n /**\n * Query events in the store, optionally filtered by query options.\n * @param callback - Function to call for each event.\n * @param query - Optional query options.\n * @returns The number of events processed.\n */\n async query<E extends Schemas>(\n callback: (event: Committed<E, keyof E>) => void,\n query?: Query\n ) {\n await sleep();\n let count = 0;\n // Snapshot resume floor: `with_snaps` requests a resume at the latest\n // snapshot for an exact single stream, so pre-snapshot events aren't read.\n // The orchestrator sets `with_snaps` only for an unbounded current-state\n // load — it suppresses the flag under any `asOf` bound (RFC 1274) — so the\n // store applies the floor whenever asked and never re-checks bounds. An\n // explicit `after` is a separate resume point that wins. No snapshot → -1,\n // i.e. a full scan. Forward starts at the snapshot; backward stops at it.\n let floor_index = -1;\n if (\n query?.with_snaps &&\n query.stream_exact &&\n query.stream !== undefined &&\n query.after === undefined\n ) {\n for (let j = this._events.length - 1; j >= 0; j--) {\n const e = this._events[j];\n if (e.stream === query.stream && e.name === SNAP_EVENT) {\n floor_index = j;\n break;\n }\n }\n }\n if (query?.backward) {\n const floor_id = floor_index >= 0 ? this._events[floor_index].id : -1;\n let i =\n (query?.before !== undefined\n ? this._first_index_after(query.before - 1)\n : this._events.length) - 1;\n while (i >= 0) {\n const e = this._events[i--];\n if (query && !this.in_query(query, e)) continue;\n if (query?.created_before && e.created >= query.created_before)\n continue;\n if (query.after !== undefined && e.id <= query.after) break;\n // Below the resume floor → every remaining (lower-id) event is too,\n // so stop the DESC scan.\n if (floor_id >= 0 && e.id < floor_id) break;\n // `created` is not monotonic with `id` (restore preserves the\n // source timestamps verbatim), so a failing time bound skips the\n // event rather than terminating the scan — matching PG/SQLite,\n // which treat `created` bounds as pure WHERE filters. Only the\n // id-ordered `after` bound above may short-circuit.\n if (query.created_after && e.created <= query.created_after) continue;\n await Promise.resolve(\n callback(this._with_pii(e as Committed<E, keyof E>))\n );\n count++;\n if (query?.limit && count >= query.limit) break;\n }\n } else {\n let i =\n floor_index >= 0\n ? floor_index\n : this._first_index_after(query?.after ?? -1);\n while (i < this._events.length) {\n const e = this._events[i++];\n if (query && !this.in_query(query, e)) continue;\n if (query?.created_after && e.created <= query.created_after) continue;\n if (query?.before !== undefined && e.id >= query.before) break;\n // `created` is not monotonic with `id`, so a failing time bound\n // skips the event rather than terminating the scan — matching\n // PG/SQLite. Only the id-ordered `before` bound above may\n // short-circuit.\n if (query?.created_before && e.created >= query.created_before)\n continue;\n await Promise.resolve(\n callback(this._with_pii(e as Committed<E, keyof E>))\n );\n count++;\n if (query?.limit && count >= query.limit) break;\n }\n }\n return count;\n }\n\n /**\n * Commit one or more events to a stream.\n * @param stream - The stream name.\n * @param msgs - The events/messages to commit.\n * @param meta - Event metadata.\n * @param expectedVersion - Optional optimistic concurrency check.\n * @returns The committed events with metadata.\n * @throws ConcurrencyError if expectedVersion does not match.\n */\n async commit<E extends Schemas>(\n stream: string,\n msgs: Message<E, keyof E>[],\n meta: EventMeta,\n expectedVersion?: number\n ) {\n await sleep();\n const current_version = this._stream_versions.get(stream) ?? -1;\n if (\n typeof expectedVersion === \"number\" &&\n current_version !== expectedVersion\n ) {\n throw new ConcurrencyError(\n stream,\n current_version,\n msgs as Message<Schemas, keyof Schemas>[],\n expectedVersion\n );\n }\n\n let version = current_version + 1;\n let last_non_snap_id = -1;\n const committed = msgs.map(({ name, data, pii }) => {\n const c: Committed<E, keyof E> = {\n id: this._next_id++,\n stream,\n version,\n created: new Date(),\n name,\n data,\n meta,\n };\n // The stored event is the pii-less view — `forget_pii` only has to\n // drop the inner Map for the stream, never the event row. Mandatory\n // clone on the pii payload defends against caller-side mutation.\n this._events.push(c as Committed<Schemas, keyof Schemas>);\n if (pii != null) {\n let per_stream = this._pii.get(stream);\n if (!per_stream) {\n per_stream = new Map();\n this._pii.set(stream, per_stream);\n }\n per_stream.set(c.id, structuredClone(pii) as Record<string, unknown>);\n }\n if (name !== SNAP_EVENT) last_non_snap_id = c.id;\n version++;\n return this._with_pii(c);\n });\n this._stream_versions.set(stream, version - 1);\n if (last_non_snap_id >= 0) {\n this._max_event_id_by_stream.set(stream, last_non_snap_id);\n // commit always assigns a fresh id from the monotonic _next_id, so\n // any non-snap commit strictly raises the global max.\n this._max_non_snap_event_id = last_non_snap_id;\n }\n return committed;\n }\n\n /**\n * Atomically discovers and leases streams for processing.\n * Fuses poll + lease into a single operation.\n * @param lagging - Max streams from lagging frontier.\n * @param leading - Max streams from leading frontier.\n * @param by - Lease holder identifier.\n * @param millis - Lease duration in milliseconds.\n * @returns Granted leases.\n */\n async claim(\n lagging: number,\n leading: number,\n by: string,\n millis: number,\n lane?: string\n ) {\n await sleep();\n // Eligibility is a pure subscription-row predicate (#1488). `claim`\n // never looks at the event log: `correlate` records the highest event id\n // that resolves to a target, and `at < correlated_at` is the whole\n // question. The probe this replaced walked the event index once per\n // eligible subscription, matching `source` literally or as a pattern —\n // matching that now happens in correlate, when it decides what to mark.\n //\n // An unmarked row is not claimable, by definition (#1446): `undefined`\n // means the row has never been correlated, not that it has no work.\n const has_work = (s: InMemoryStream): boolean =>\n s.correlated_at !== undefined && s.at < s.correlated_at;\n const available = [...this._streams.values()].filter(\n (s) =>\n s.is_available && has_work(s) && (lane === undefined || s.lane === lane)\n );\n // Lagging frontier orders by priority DESC (higher first), then by\n // watermark ASC (most-behind first). Mirrors the PG `claim()` SQL\n // — see `libs/act-pg/PERFORMANCE.md` for the benchmark that\n // motivated the priority dimension. A fairness reserve (ACT-1223)\n // carves `fair` slots off the budget and fills them by pure watermark\n // order (priority ignored) so a default-priority lagging stream can\n // never be starved out of the frontier by sustained higher-priority\n // load. With everyone at the same priority both slices order by `at`,\n // so this is a behavioral no-op for existing workloads.\n const fair = lagging >= 2 ? Math.max(1, Math.floor(lagging / 4)) : 0;\n const by_priority = [...available].sort(\n (a, b) => b.priority - a.priority || a.at - b.at\n );\n const priority_slice = by_priority.slice(0, lagging - fair);\n const picked = new Set(priority_slice.map((s) => s.stream));\n const fair_slice = [...available]\n .sort((a, b) => a.at - b.at)\n .filter((s) => !picked.has(s.stream))\n .slice(0, fair);\n const lag = [...priority_slice, ...fair_slice].map((s) => ({\n stream: s.stream,\n source: s.source,\n at: s.at,\n lagging: true,\n }));\n const lead = available\n .sort((a, b) => b.at - a.at)\n .slice(0, leading)\n .map((s) => ({\n stream: s.stream,\n source: s.source,\n at: s.at,\n lagging: false,\n }));\n // deduplicate (a stream can appear in both frontiers)\n const seen = new Set<string>();\n const combined = [...lag, ...lead].filter((p) => {\n if (seen.has(p.stream)) return false;\n seen.add(p.stream);\n return true;\n });\n // lease each atomically\n return combined\n .map((p) =>\n this._streams.get(p.stream)?.lease({ ...p, by, retry: 0 }, millis)\n )\n .filter((l) => !!l);\n }\n\n /**\n * Registers streams for event processing. When the same stream is\n * resubscribed with a different priority, the **maximum** wins — so\n * the highest-priority registered reaction sets the scheduling lane.\n * Use {@link prioritize} for operator runtime overrides.\n *\n * @param streams - Streams to register with optional source + priority.\n * @returns subscribed count and current max watermark.\n */\n async subscribe(\n streams: SubscribeInput[],\n correlated_at?: number,\n correlator?: { key: string; by: string; millis: number }\n ) {\n await sleep();\n\n // The correlate checkpoint is written by its own producer, in the call\n // correlate already makes (#1484). Monotonic: a lower value is ignored.\n //\n // With a correlator the position and the lease are per-key (#1532); a key\n // with no row yet inherits the shared value, so an upgrade does not\n // re-read history.\n let correlating: boolean | undefined;\n /** This correlator's position, once known — always set when keyed. */\n let keyed_at: number | undefined;\n if (correlator) {\n const now = Date.now();\n const held = this._correlators.get(correlator.key);\n // A key with no row yet inherits the shared position, so a new\n // correlator does not re-read history.\n const at = Math.max(held?.at ?? this._correlated_at, correlated_at ?? -1);\n keyed_at = at;\n if (!held || held.until < now || held.by === correlator.by) {\n correlating = true;\n this._correlators.set(correlator.key, {\n at,\n by: correlator.by,\n // A non-positive `millis` releases outright rather than setting an\n // expiry a hair in the future: a successor asking inside the same\n // millisecond would otherwise still be refused.\n until: correlator.millis > 0 ? now + correlator.millis : 0,\n });\n } else {\n // Refused, which is only possible when a live holder exists — so the\n // row is here to keep. The position still advances: this caller may\n // already have scanned, and dropping how far it read would leave its\n // marks ahead of its recorded position.\n correlating = false;\n held.at = at;\n }\n // The shared position tracks how far *any* correlator has read: the\n // floor a brand-new key inherits, and what a caller reading without a\n // correlator still sees.\n if (correlated_at !== undefined && correlated_at > this._correlated_at)\n this._correlated_at = correlated_at;\n } else if (\n correlated_at !== undefined &&\n correlated_at > this._correlated_at\n )\n this._correlated_at = correlated_at;\n\n let subscribed = 0;\n for (const {\n stream,\n source,\n priority = 0,\n lane = DEFAULT_LANE,\n correlated_at,\n } of streams) {\n const existing = this._streams.get(stream);\n if (existing) {\n existing.bump_priority(priority);\n existing.lane = lane;\n if (correlated_at !== undefined) existing.mark(correlated_at);\n } else {\n const created = new InMemoryStream(stream, source, priority, lane);\n if (correlated_at !== undefined) created.mark(correlated_at);\n this._streams.set(stream, created);\n subscribed++;\n }\n }\n let watermark = -1;\n for (const s of this._streams.values()) {\n if (s.at > watermark) watermark = s.at;\n }\n return {\n subscribed,\n watermark,\n correlated_at: keyed_at ?? this._correlated_at,\n ...(correlating === undefined ? {} : { correlating }),\n };\n }\n\n /**\n * Acknowledge completion of processing for leased streams.\n * @param leases - Leases to acknowledge, including last processed watermark and lease holder.\n */\n async ack(leases: Lease[]) {\n await sleep();\n // Acks and defer schedules land in one synchronous pass — the\n // in-memory equivalent of the single-transaction contract on\n // {@link Store.ack}: no await between entries, so a caller\n // never observes a cycle's acks without its schedules. `due`-carrying\n // entries defer (and return undefined), the rest ack.\n return leases\n .map((l) => this._streams.get(l.stream)?.ack(l))\n .filter((l) => !!l);\n }\n\n /**\n * Block a stream for processing after failing to process and reaching max retries with blocking enabled.\n * @param leases - Leases to block, including lease holder and last error message.\n * @returns Blocked leases.\n */\n async block(leases: BlockedLease[]) {\n await sleep();\n return leases\n .map((l) => this._streams.get(l.stream)?.block(l, l.error))\n .filter((l) => !!l);\n }\n\n /**\n * Hold the matched streams out of {@link claim} until `deferred_at`\n * (ms since epoch) — see {@link Store.defer}. Accepts an explicit list\n * of names or a {@link StreamFilter}, mirroring {@link reset}/{@link unblock}.\n * Persisted store state (unlike in-process backoff), so the skip is honored\n * by every competing worker. Unknown names are silently skipped.\n *\n * @returns Count of streams whose `deferred_at` was set.\n */\n async defer(input: string[] | StreamFilter, deferred_at: number) {\n await sleep();\n let count = 0;\n if (Array.isArray(input)) {\n // De-dup the array so a repeated name counts once, matching PG's\n // set-based `WHERE stream = ANY(...)` (#1360).\n for (const name of new Set(input)) {\n const s = this._streams.get(name);\n if (s) {\n s.defer(deferred_at);\n count++;\n }\n }\n } else {\n const matches = this._filter_predicate(input);\n for (const s of this._streams.values()) {\n if (matches(s)) {\n s.defer(deferred_at);\n count++;\n }\n }\n }\n return count;\n }\n\n /**\n * Build a predicate from a {@link StreamFilter}. Compiled regexes are\n * cached in the closure so callers can apply it across the streams\n * map without re-compiling per iteration.\n */\n private _filter_predicate(\n filter: StreamFilter\n ): (s: InMemoryStream) => boolean {\n const stream_re =\n filter.stream && !filter.stream_exact\n ? new RegExp(filter.stream)\n : undefined;\n const source_re =\n filter.source && !filter.source_exact\n ? new RegExp(filter.source)\n : undefined;\n return (s) => {\n if (filter.stream !== undefined) {\n if (\n filter.stream_exact\n ? s.stream !== filter.stream\n : !stream_re!.test(s.stream)\n )\n return false;\n }\n if (filter.source !== undefined) {\n if (s.source === undefined) return false;\n if (\n filter.source_exact\n ? s.source !== filter.source\n : !source_re!.test(s.source)\n )\n return false;\n }\n if (filter.blocked !== undefined && s.blocked !== filter.blocked)\n return false;\n if (filter.lane !== undefined && s.lane !== filter.lane) return false;\n return true;\n };\n }\n\n /**\n * Reset watermarks to -1, clearing retry, blocked, error, and lease\n * state so the matched streams can be replayed from the beginning.\n * Accepts either an explicit list of names or a {@link StreamFilter}.\n *\n * @param input - Stream names or a filter selecting the streams to reset.\n * @returns Count of streams that were actually reset.\n */\n async reset(input: string[] | StreamFilter) {\n await sleep();\n let count = 0;\n if (Array.isArray(input)) {\n // De-dup the array so a repeated name counts once, matching PG's\n // set-based `WHERE stream = ANY(...)` (#1360).\n for (const name of new Set(input)) {\n const s = this._streams.get(name);\n if (s) {\n s.reset();\n count++;\n }\n }\n } else {\n const matches = this._filter_predicate(input);\n for (const s of this._streams.values()) {\n if (matches(s)) {\n s.reset();\n count++;\n }\n }\n }\n return count;\n }\n\n /**\n * Clear the blocked flag (and retry / error / lease) on the matched\n * streams without touching the watermark. Streams that aren't blocked\n * at call time are silently skipped. Accepts either an explicit list\n * of names or a {@link StreamFilter}. The filter form always restricts\n * to blocked streams — passing `blocked: false` matches nothing.\n * See {@link Store.unblock}.\n *\n * @param input - Stream names or a filter selecting the streams to unblock.\n * @returns Count of streams that were actually flipped (were blocked).\n */\n /**\n * Wipe the sensitive-data payload for every event on the stream — see\n * {@link Store.forget_pii}. O(1) drop of the stream's inner Map; the size of\n * that Map is the count of events that had PII. Idempotent: a second call\n * finds no inner Map and returns `0`.\n *\n * @param stream - Target stream.\n * @returns Count of events whose isolated PII payload was deleted.\n */\n async forget_pii(stream: string): Promise<number> {\n await sleep();\n const count = this._pii.get(stream)?.size ?? 0;\n this._pii.delete(stream);\n return count;\n }\n\n async unblock(input: string[] | StreamFilter) {\n await sleep();\n let count = 0;\n if (Array.isArray(input)) {\n for (const name of input) {\n const s = this._streams.get(name);\n if (s?.unblock()) count++;\n }\n } else {\n // Filter form: always restrict to blocked streams. An explicit\n // `blocked: false` in the filter is silently overridden — there\n // is no use case for \"unblock unblocked streams.\"\n const matches = this._filter_predicate({ ...input, blocked: true });\n for (const s of this._streams.values()) {\n if (matches(s) && s.unblock()) count++;\n }\n }\n return count;\n }\n\n /**\n * Bulk-update priority of streams matching `filter`. Mirrors\n * {@link query_streams}'s filter semantics — see {@link Store.prioritize}.\n * Unlike {@link subscribe} (which keeps `max()` of registered\n * priorities), this sets the priority outright — operator override\n * for the build-time scheduling policy.\n *\n * @returns Count of streams whose priority changed.\n */\n async prioritize(filter: StreamFilter, priority: number) {\n await sleep();\n const matches = this._filter_predicate(filter);\n let count = 0;\n for (const s of this._streams.values()) {\n if (!matches(s)) continue;\n if (s.priority !== priority) {\n s.set_priority(priority);\n count++;\n }\n }\n return count;\n }\n\n /**\n * Streams registered subscription positions to the callback, ordered by\n * stream name. Returns the highest event id in the store and the count\n * of positions emitted.\n */\n async query_streams(\n callback: (position: StreamPosition) => void,\n query?: QueryStreams\n ): Promise<QueryStreamsResult> {\n await sleep();\n const limit = query?.limit ?? 100;\n const after = query?.after;\n const blocked = query?.blocked;\n const source_matches = query?.source_matches;\n const stream_re =\n query?.stream && !query.stream_exact\n ? new RegExp(query.stream)\n : undefined;\n const source_re =\n query?.source && !query.source_exact\n ? new RegExp(query.source)\n : undefined;\n // Reverse-match: a stream qualifies when its stored `source` pattern\n // matches at least one of the requested names. Patterns are compiled\n // once and cached — many subscriptions share one source pattern.\n const reverse_cache = new Map<string, RegExp>();\n const reverse_match = (source: string): boolean => {\n let re = reverse_cache.get(source);\n if (!re) {\n re = new RegExp(source);\n reverse_cache.set(source, re);\n }\n return source_matches!.some((name) => re!.test(name));\n };\n\n // Order by stream name so `after`/`limit` keyset-paginate\n // deterministically. The sort MUST agree with the `after` cursor below\n // (JS `<=`, i.e. UTF-16 code-unit order) — `localeCompare` disagrees\n // with `<=` on mixed-case/accented names (`B`=66 < `a`=97 by code unit,\n // but `a` < `B` by locale), which let the cursor skip streams the sort\n // placed after it (#1375, the `query_streams` twin of #1357).\n // The default `Array.sort()` comparator IS code-unit order, so it\n // matches `<=` exactly — and PG/SQLite, which paginate under their\n // binary collation. Sorting the keys (rather than comparing values)\n // keeps that default in play.\n const sorted = [...this._streams.keys()]\n .sort()\n .map((stream) => this._streams.get(stream) as InMemoryStream);\n\n let count = 0;\n for (const s of sorted) {\n if (after !== undefined && s.stream <= after) continue;\n if (query?.stream !== undefined) {\n if (\n query.stream_exact\n ? s.stream !== query.stream\n : !stream_re!.test(s.stream)\n )\n continue;\n }\n if (query?.source !== undefined) {\n if (s.source === undefined) continue;\n if (\n query.source_exact\n ? s.source !== query.source\n : !source_re!.test(s.source)\n )\n continue;\n }\n if (source_matches !== undefined) {\n // Absent/empty source = no source constraint = consumes from\n // every stream, so it matches any requested name. Only a\n // present source that matches none of them is excluded.\n if (s.source && !reverse_match(s.source)) continue;\n }\n if (blocked !== undefined && s.blocked !== blocked) continue;\n if (query?.lane !== undefined && s.lane !== query.lane) continue;\n callback({\n stream: s.stream,\n source: s.source,\n at: s.at,\n retry: s.retry,\n blocked: s.blocked,\n error: s.error,\n priority: s.priority,\n leased_by: s.leased_by,\n leased_until: s.leased_until,\n lane: s.lane,\n deferred_at: s.deferred_at,\n correlated_at: s.correlated_at,\n });\n count++;\n if (count >= limit) break;\n }\n return { maxEventId: this._events.at(-1)?.id ?? -1, count };\n }\n\n /**\n * Per-stream aggregated stats — see {@link Store.query_stats}.\n *\n * Single forward scan over the in-memory event list, accumulating per\n * stream. The \"cheap heads\" cost tier from durable adapters doesn't\n * apply here (InMemory has no indexes); correctness is the goal, perf\n * is a non-issue.\n *\n * Scope rules:\n * - Array `input` — explicit stream names, regardless of subscription.\n * - Filter `input` — `stream`/`stream_exact` match against event-bearing\n * stream names; `source`/`source_exact`/`blocked` require a\n * corresponding subscription in `_streams` (those are subscription\n * concepts, not event concepts). Empty filter `{}` matches every\n * event-bearing stream.\n */\n async query_stats<E extends Schemas>(\n input: string[] | Pick<StreamFilter, \"stream\" | \"stream_exact\">,\n options?: QueryStatsOptions<E>\n ): Promise<Map<string, StreamStats<E>>> {\n await sleep();\n const exclude = new Set<string>(options?.exclude ?? []);\n const want_tail = options?.tail ?? false;\n const want_count = options?.count ?? false;\n const want_names = options?.names ?? false;\n const before = options?.before;\n const after = options?.after;\n const limit = options?.limit;\n\n // Pre-compile per-stream scope predicate, cached as we go so each\n // distinct stream evaluates the regex once.\n const array_targets = Array.isArray(input) ? new Set(input) : null;\n const filter = Array.isArray(input) ? null : input;\n const stream_re =\n filter?.stream && !filter.stream_exact\n ? new RegExp(filter.stream)\n : undefined;\n\n const scope_cache = new Map<string, boolean>();\n const in_scope = (stream: string): boolean => {\n const cached = scope_cache.get(stream);\n if (cached !== undefined) return cached;\n let ok = true;\n if (array_targets) {\n ok = array_targets.has(stream);\n } else if (filter?.stream !== undefined) {\n ok = filter.stream_exact\n ? stream === filter.stream\n : // stream_re set when stream is regex\n stream_re!.test(stream);\n }\n scope_cache.set(stream, ok);\n return ok;\n };\n\n type Acc = {\n head: Committed<Schemas, keyof Schemas>;\n tail?: Committed<Schemas, keyof Schemas>;\n count: number;\n names?: Record<string, number>;\n };\n const acc = new Map<string, Acc>();\n for (const e of this._events) {\n if (before !== undefined && e.id >= before) continue;\n if (!in_scope(e.stream)) continue;\n if (exclude.has(e.name as string)) continue;\n let a = acc.get(e.stream);\n if (!a) {\n a = { head: e, count: 0 };\n if (want_tail) a.tail = e;\n if (want_names) a.names = {};\n acc.set(e.stream, a);\n }\n a.head = e;\n a.count++;\n if (want_names) {\n const n = String(e.name);\n // a.names initialized above when want_names\n a.names![n] = (a.names![n] ?? 0) + 1;\n }\n }\n\n // Order by stream name so `after`/`limit` keyset-paginate\n // deterministically. The sort MUST agree with the `after` cursor below\n // (JS `<=`, i.e. UTF-16 code-unit order) — `localeCompare` disagrees with\n // `<=` on mixed-case/accented names (`B`=66 < `a`=97 by code unit, but\n // `a` < `B` by locale), which would let the cursor skip streams the sort\n // placed after it (#1357). The default `Array.sort()` comparator IS\n // code-unit order, so it matches `<=` exactly — and PG/SQLite, which\n // paginate `query_stats` under their binary collation.\n const ordered = [...acc.keys()].sort();\n const out = new Map<string, StreamStats<E>>();\n for (const stream of ordered) {\n if (after !== undefined && stream <= after) continue;\n const a = acc.get(stream)!;\n const stats: {\n head: Committed<Schemas, keyof Schemas>;\n tail?: Committed<Schemas, keyof Schemas>;\n count?: number;\n names?: Record<string, number>;\n } = { head: a.head };\n if (want_tail) stats.tail = a.tail;\n if (want_count) stats.count = a.count;\n if (want_names) stats.names = a.names;\n out.set(stream, stats as StreamStats<E>);\n if (limit !== undefined && out.size >= limit) break;\n }\n return out;\n }\n\n /**\n * Atomically truncates streams and seeds each with a snapshot or tombstone.\n * Windowed targets (`before` set) prune the prefix below the closest safe\n * `__snapshot__` instead — no seed, subscriptions untouched, no-op when no\n * snapshot qualifies.\n * @param targets - Streams to truncate with optional snapshot state and meta,\n * or a `before`/`max_id` boundary for a windowed prefix delete.\n * @returns Map keyed by stream name, each entry with `deleted` count and `committed` event.\n */\n async truncate(\n targets: Array<{\n stream: string;\n snapshot?: Schema;\n meta?: EventMeta;\n before?: Date;\n max_id?: number;\n }>\n ) {\n await sleep();\n const result = new Map<\n string,\n {\n deleted: number;\n committed: Committed<Schemas, keyof Schemas>;\n before?: Date;\n }\n >();\n\n // Windowed targets: pure prefix delete behind the closest safe snapshot.\n const windowed = targets.filter((t) => t.before !== undefined);\n if (windowed.length) {\n const drop = new Set<number>();\n for (const { stream, before, max_id } of windowed) {\n let boundary: Committed<Schemas, keyof Schemas> | undefined;\n for (const e of this._events) {\n if (\n e.stream === stream &&\n e.name === SNAP_EVENT &&\n e.created < before! &&\n (max_id === undefined || e.id <= max_id) &&\n (!boundary || e.id > boundary.id)\n )\n boundary = e;\n }\n if (!boundary) continue; // no qualifying snapshot → no-op\n let deleted = 0;\n for (const e of this._events) {\n if (e.stream === stream && e.id < boundary.id) {\n drop.add(e.id);\n this._pii.get(stream)?.delete(e.id);\n deleted++;\n }\n }\n result.set(stream, { deleted, committed: boundary, before });\n }\n if (drop.size) this._events = this._events.filter((e) => !drop.has(e.id));\n }\n\n const full = targets.filter((t) => t.before === undefined);\n // Count per-stream deletions\n const deleted_counts = new Map<string, number>();\n const stream_set = new Set(full.map((t) => t.stream));\n for (const e of this._events) {\n if (stream_set.has(e.stream)) {\n deleted_counts.set(e.stream, (deleted_counts.get(e.stream) ?? 0) + 1);\n }\n }\n this._events = this._events.filter((e) => !stream_set.has(e.stream));\n // Subscriptions are deliberately untouched, for restart *and* retire\n // targets alike — see the note in truncate's contract. A tombstoned\n // stream's subscription is inert, and reaping it is maintenance that\n // `seed()` performs (#1527).\n for (const stream of stream_set) {\n this._stream_versions.delete(stream);\n this._max_event_id_by_stream.delete(stream);\n // The pii payloads die with the event rows, matching the durable\n // adapters' `DELETE WHERE stream = ?` scope.\n this._pii.delete(stream);\n }\n for (const { stream, snapshot, meta } of full) {\n const event: Committed<Schemas, keyof Schemas> = {\n id: this._next_id++,\n stream,\n version: 0,\n created: new Date(),\n name: snapshot !== undefined ? SNAP_EVENT : TOMBSTONE_EVENT,\n data: snapshot ?? {},\n meta: meta ?? { correlation: \"\", causation: {} },\n };\n this._events.push(event);\n this._stream_versions.set(stream, 0);\n if (event.name !== SNAP_EVENT) {\n this._max_event_id_by_stream.set(stream, event.id);\n }\n result.set(stream, {\n deleted: deleted_counts.get(stream) ?? 0,\n committed: event,\n });\n }\n // Recompute global max from the per-stream index — deletions may have\n // dropped the previous max, while new tombstones may have raised it.\n let max = -1;\n for (const id of this._max_event_id_by_stream.values())\n if (id > max) max = id;\n this._max_non_snap_event_id = max;\n return result;\n }\n\n /**\n * Atomically wipe-and-rebuild the store under an in-process snapshot.\n *\n * Captures every index state up front, clears it, then hands the\n * orchestrator a per-event insert `callback` via the driver. Any\n * throw inside the driver restores the snapshot, leaving the store\n * byte-for-byte unchanged from the operator's perspective.\n *\n * `id`s are reassigned `0..N-1` as events arrive (dense — the\n * monotonic id counter restarts at 0 for the rebuild). `created` is\n * preserved verbatim from the source.\n */\n async restore(\n driver: (\n callback: (event: Committed<Schemas, keyof Schemas>) => Promise<number>\n ) => Promise<void>\n ): Promise<void> {\n await sleep();\n // Snapshot every index so we can roll back on throw.\n const prev_events = this._events;\n const prev_next_id = this._next_id;\n const prev_streams = this._streams;\n const prev_stream_versions = this._stream_versions;\n const prev_max_event_id_by_stream = this._max_event_id_by_stream;\n const prev_max_non_snap_event_id = this._max_non_snap_event_id;\n const prev_pii = this._pii;\n // Swap in fresh state for the duration of the rebuild.\n this._events = [];\n this._next_id = 0;\n this._streams = new Map();\n this._stream_versions = new Map();\n this._max_event_id_by_stream = new Map();\n this._max_non_snap_event_id = -1;\n this._pii = new Map();\n try {\n await driver(async (event) => {\n const id = this._next_id++;\n // Split `pii` out of the stored row into the isolated `_pii` map,\n // mirroring `commit`. The stored event is the pii-less view so\n // `forget_pii` — which only drops the `_pii` entry — actually\n // erases restored PII (durable adapters restore into their\n // isolated column for the same reason).\n const { pii, ...rest } = event;\n const committed: Committed<Schemas, keyof Schemas> = { ...rest, id };\n this._events.push(committed);\n if (pii != null) {\n let per_stream = this._pii.get(event.stream);\n if (!per_stream) {\n per_stream = new Map();\n this._pii.set(event.stream, per_stream);\n }\n per_stream.set(id, structuredClone(pii) as Record<string, unknown>);\n }\n // Last event per stream wins for the version watermark — the\n // source is expected to be in commit order, so this is also\n // the highest version. Out-of-order sources get last-wins,\n // matching the legacy raw-SQL restore.\n this._stream_versions.set(event.stream, event.version);\n if (event.name !== SNAP_EVENT) {\n this._max_event_id_by_stream.set(event.stream, id);\n this._max_non_snap_event_id = id;\n }\n return id;\n });\n } catch (err) {\n // Roll back to the captured snapshot — every index restored\n // exactly as it was before the call started.\n this._events = prev_events;\n this._next_id = prev_next_id;\n this._streams = prev_streams;\n this._stream_versions = prev_stream_versions;\n this._max_event_id_by_stream = prev_max_event_id_by_stream;\n this._max_non_snap_event_id = prev_max_non_snap_event_id;\n this._pii = prev_pii;\n throw err;\n }\n }\n}\n"],"mappings":"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","names":["import_zod","import_zod","import_zod","log","correlated_at","store","cache"]}