@rotorsoft/act 1.11.0 → 1.12.1

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/adapters/console-logger.ts","../src/internal/lru-map.ts","../src/adapters/in-memory-cache.ts","../src/config.ts","../src/ports.ts","../src/utils.ts","../src/adapters/in-memory-store.ts"],"sourcesContent":["/**\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 * @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 * @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 { AsyncLocalStorage } from \"node:async_hooks\";\nimport { 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 type {\n Cache,\n Disposable,\n Disposer,\n Logger,\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/** AsyncLocalStorage carrying the active Act's ports. Internal — not re-exported. */\nexport const scoped = new AsyncLocalStorage<Scoped>();\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 scoped.getStore()?.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 scoped.getStore()?.cache ?? _cache(adapter);\n}) as (adapter?: Cache) => Cache;\n\n// ---------------------------------------------------------------------------\n// Disposal\n// ---------------------------------------------------------------------------\n\n/** Registered cleanup functions, executed in reverse order during shutdown. */\nconst disposers: Disposer[] = [];\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 for (const disposer of [...disposers].reverse()) {\n await disposer();\n }\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 && disposers.push(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","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 * @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 */\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} 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\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 /**\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 );\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 /**\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 * Acknowledge completion of processing for this stream.\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 this._at = lease.at;\n this._retry = -1;\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 if (this._leased_by === lease.by) {\n this._blocked = true;\n this._error = error;\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 }\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 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 // stored stream positions and other metadata\n private _streams: Map<string, InMemoryStream> = new Map();\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 source-pattern probe in claim()\n // without scanning 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._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 // 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 if (query?.backward) {\n let i = (query?.before || 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 && e.id <= query.after) break;\n if (query.created_after && e.created <= query.created_after) break;\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 = (query?.after ?? -1) + 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 && e.id >= query.before) break;\n if (query?.created_before && e.created >= query.created_before) break;\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._events.length,\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 this._events.length, so any\n // 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 // Cache compiled regexes — multiple subscribed streams typically share the\n // same source pattern, and the inner loop can run thousands of times per claim.\n const source_regex = new Map<string, RegExp>();\n const get_regex = (source: string) => {\n let re = source_regex.get(source);\n if (!re) {\n re = new RegExp(source);\n source_regex.set(source, re);\n }\n return re;\n };\n const has_work = (s: InMemoryStream): boolean => {\n if (s.at < 0) return true;\n if (!s.source) return s.at < this._max_non_snap_event_id;\n const re = get_regex(s.source);\n for (const [stream_name, maxId] of this._max_event_id_by_stream) {\n if (maxId > s.at && re.test(stream_name)) return true;\n }\n return false;\n };\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.\n const lag = available\n .sort((a, b) => b.priority - a.priority || a.at - b.at)\n .slice(0, lagging)\n .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: Array<{\n stream: string;\n source?: string;\n priority?: number;\n lane?: string;\n }>\n ) {\n await sleep();\n let subscribed = 0;\n for (const {\n stream,\n source,\n priority = 0,\n lane = DEFAULT_LANE,\n } of streams) {\n const existing = this._streams.get(stream);\n if (existing) {\n existing.bump_priority(priority);\n existing.lane = lane;\n } else {\n this._streams.set(\n stream,\n new InMemoryStream(stream, source, priority, lane)\n );\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 { subscribed, watermark };\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 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 * 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 for (const name of 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 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\n const sorted = [...this._streams.values()].sort((a, b) =>\n a.stream.localeCompare(b.stream)\n );\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 (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 });\n count++;\n if (count >= limit) break;\n }\n return { maxEventId: this._events.length - 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\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 : // biome-ignore lint/style/noNonNullAssertion: 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 // biome-ignore lint/style/noNonNullAssertion: a.names initialized above when want_names\n a.names![n] = (a.names![n] ?? 0) + 1;\n }\n }\n\n const out = new Map<string, StreamStats<E>>();\n for (const [stream, a] of acc) {\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 }\n return out;\n }\n\n /**\n * Atomically truncates streams and seeds each with a snapshot or tombstone.\n * @param targets - Streams to truncate with optional snapshot state and meta.\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 }>\n ) {\n await sleep();\n // Count per-stream deletions\n const deleted_counts = new Map<string, number>();\n const stream_set = new Set(targets.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 for (const stream of stream_set) {\n this._streams.delete(stream);\n this._stream_versions.delete(stream);\n this._max_event_id_by_stream.delete(stream);\n }\n const result = new Map<\n string,\n { deleted: number; committed: Committed<Schemas, keyof Schemas> }\n >();\n for (const { stream, snapshot, meta } of targets) {\n const event: Committed<Schemas, keyof Schemas> = {\n id: this._events.length,\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 (matching the\n * adapter's commit-id convention — InMemory uses `_events.length`).\n * `created` is 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_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 // Swap in fresh state for the duration of the rebuild.\n this._events = [];\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 try {\n await driver(async (event) => {\n const id = this._events.length;\n const committed: Committed<Schemas, keyof Schemas> = { ...event, id };\n this._events.push(committed);\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._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 throw err;\n }\n }\n}\n"],"mappings":";;;;;;;;AAYA,IAAM,eAAuC;AAAA,EAC3C,OAAO;AAAA,EACP,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AACT;AAEA,IAAM,eAAuC;AAAA,EAC3C,OAAO;AAAA;AAAA,EACP,OAAO;AAAA;AAAA,EACP,MAAM;AAAA;AAAA,EACN,MAAM;AAAA;AAAA,EACN,OAAO;AAAA;AAAA,EACP,OAAO;AAAA;AACT;AAEA,IAAM,QAAQ;AAEd,IAAM,OAAO,MAAM;AAAC;AAUb,IAAM,gBAAN,MAAM,eAAgC;AAAA,EAC3C;AAAA,EACiB;AAAA,EAER;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,YACE,UAII,CAAC,GACL;AACA,UAAM;AAAA,MACJ,QAAQ;AAAA,MACR,SAAS,QAAQ,IAAI,aAAa;AAAA,MAClC;AAAA,IACF,IAAI;AACJ,SAAK,UAAU;AACf,SAAK,QAAQ;AAEb,UAAM,YAAY,aAAa,KAAK,KAAK;AACzC,UAAM,QAAQ,SACV,KAAK,cAAc,KAAK,MAAM,QAAQ,IACtC,KAAK,YAAY,KAAK,MAAM,QAAQ;AAGxC,SAAK,QAAQ,MAAM,KAAK,MAAM,SAAS,EAAE;AACzC,SAAK,QAAQ,aAAa,KAAK,MAAM,KAAK,MAAM,SAAS,EAAE,IAAI;AAC/D,SAAK,OAAO,aAAa,KAAK,MAAM,KAAK,MAAM,QAAQ,EAAE,IAAI;AAC7D,SAAK,OAAO,aAAa,KAAK,MAAM,KAAK,MAAM,QAAQ,EAAE,IAAI;AAC7D,SAAK,QAAQ,aAAa,KAAK,MAAM,KAAK,MAAM,SAAS,EAAE,IAAI;AAC/D,SAAK,QAAQ,aAAa,KAAK,MAAM,KAAK,MAAM,SAAS,EAAE,IAAI;AAAA,EACjE;AAAA;AAAA,EAGA,MAAM,UAAyB;AAAA,EAAC;AAAA;AAAA,EAGhC,MAAM,UAA2C;AAC/C,WAAO,IAAI,eAAc;AAAA,MACvB,OAAO,KAAK;AAAA,MACZ,QAAQ,KAAK;AAAA,MACb;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,YACN,UACA,OACA,MACA,YACA,KACM;AACN,QAAI;AACJ,QAAI;AAEJ,QAAI,OAAO,eAAe,UAAU;AAClC,gBAAU;AACV,YAAM,CAAC;AAAA,IACT,WAAW,sBAAsB,OAAO;AAGtC,gBAAU,OAAO,WAAW;AAC5B,YAAM;AAAA,QACJ,OAAO,EAAE,SAAS,WAAW,SAAS,MAAM,WAAW,KAAK;AAAA,QAC5D,OAAO,WAAW;AAAA,MACpB;AAAA,IACF,WAAW,eAAe,QAAQ,OAAO,eAAe,UAAU;AAChE,gBAAU;AACV,YAAM,EAAE,GAAI,WAAuC;AAAA,IACrD,OAAO;AACL,gBAAU;AACV,YAAM,EAAE,OAAO,WAAW;AAAA,IAC5B;AAEA,UAAM,QAAQ,OAAO,OAAO,EAAE,OAAO,MAAM,KAAK,IAAI,EAAE,GAAG,UAAU,GAAG;AACtE,QAAI,QAAS,OAAM,MAAM;AAEzB,QAAI;AACJ,QAAI;AACF,aAAO,KAAK,UAAU,KAAK;AAAA,IAC7B,QAAQ;AAGN,aAAO,KAAK,UAAU;AAAA,QACpB;AAAA,QACA,MAAM,MAAM;AAAA,QACZ,KAAK,WAAW;AAAA,QAChB,gBAAgB;AAAA,MAClB,CAAC;AAAA,IACH;AACA,YAAQ,OAAO,MAAM,OAAO,IAAI;AAAA,EAClC;AAAA,EAEQ,cACN,UACA,OACA,MACA,YACA,KACM;AACN,UAAM,QAAQ,aAAa,KAAK;AAChC,UAAM,MAAM,GAAG,KAAK,GAAG,MAAM,YAAY,EAAE,OAAO,CAAC,CAAC,GAAG,KAAK;AAC5D,UAAM,MAAK,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,IAAI,EAAE;AAEhD,QAAI;AACJ,QAAI;AAEJ,QAAI,OAAO,eAAe,UAAU;AAClC,gBAAU;AAAA,IACZ,WAAW,sBAAsB,OAAO;AAKtC,gBAAU,OAAO,WAAW;AAC5B,aAAO,WAAW;AAAA,IACpB,OAAO;AACL,gBAAU,OAAO;AACjB,UAAI,eAAe,UAAa,eAAe,MAAM;AACnD,YAAI;AACF,iBAAO,KAAK,UAAU,UAAU;AAAA,QAClC,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WACJ,YAAY,OAAO,KAAK,QAAQ,EAAE,SAC9B,IAAI,KAAK,UAAU,QAAQ,CAAC,KAC5B;AAEN,UAAM,QAAQ,CAAC,IAAI,KAAK,SAAS,MAAM,QAAQ,EAAE,OAAO,OAAO;AAC/D,YAAQ,OAAO,MAAM,MAAM,KAAK,GAAG,IAAI,IAAI;AAAA,EAC7C;AACF;;;ACjKO,IAAM,SAAN,MAAmB;AAAA,EACP,WAAW,oBAAI,IAAU;AAAA,EACzB;AAAA,EAEjB,YAAY,SAAiB;AAC3B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,IAAI,KAAuB;AACzB,UAAM,IAAI,KAAK,SAAS,IAAI,GAAG;AAC/B,QAAI,MAAM,OAAW,QAAO;AAE5B,SAAK,SAAS,OAAO,GAAG;AACxB,SAAK,SAAS,IAAI,KAAK,CAAC;AACxB,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,KAAiB;AACnB,WAAO,KAAK,SAAS,IAAI,GAAG;AAAA,EAC9B;AAAA,EAEA,IAAI,KAAQ,OAAgB;AAC1B,SAAK,SAAS,OAAO,GAAG;AACxB,QAAI,KAAK,SAAS,QAAQ,KAAK,WAAW;AAGxC,YAAM,SAAS,KAAK,SAAS,KAAK,EAAE,KAAK,EAAE;AAC3C,WAAK,SAAS,OAAO,MAAM;AAAA,IAC7B;AACA,SAAK,SAAS,IAAI,KAAK,KAAK;AAAA,EAC9B;AAAA,EAEA,OAAO,KAAiB;AACtB,WAAO,KAAK,SAAS,OAAO,GAAG;AAAA,EACjC;AAAA,EAEA,QAAc;AACZ,SAAK,SAAS,MAAM;AAAA,EACtB;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,SAAS;AAAA,EACvB;AACF;AASO,IAAM,SAAN,MAAgB;AAAA,EACJ;AAAA,EAEjB,YAAY,SAAiB;AAC3B,SAAK,OAAO,IAAI,OAAO,OAAO;AAAA,EAChC;AAAA,EAEA,IAAI,OAAmB;AACrB,WAAO,KAAK,KAAK,IAAI,KAAK;AAAA,EAC5B;AAAA,EAEA,IAAI,OAAgB;AAClB,SAAK,KAAK,IAAI,OAAO,IAAI;AAAA,EAC3B;AAAA,EAEA,OAAO,OAAmB;AACxB,WAAO,KAAK,KAAK,OAAO,KAAK;AAAA,EAC/B;AAAA,EAEA,QAAc;AACZ,SAAK,KAAK,MAAM;AAAA,EAClB;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,KAAK;AAAA,EACnB;AACF;;;ACnFO,IAAM,gBAAN,MAAqC;AAAA;AAAA;AAAA;AAAA,EAIzB;AAAA,EAEjB,YAAY,SAAgC;AAC1C,SAAK,WAAW,IAAI,OAAO,SAAS,WAAW,GAAI;AAAA,EACrD;AAAA;AAAA,EAGA,MAAM,IACJ,QACyC;AACzC,WAAO,KAAK,SAAS,IAAI,MAAM;AAAA,EACjC;AAAA;AAAA,EAGA,MAAM,IACJ,QACA,OACe;AACf,SAAK,SAAS,IAAI,QAAQ,KAAK;AAAA,EACjC;AAAA;AAAA,EAGA,MAAM,WAAW,QAA+B;AAC9C,SAAK,SAAS,OAAO,MAAM;AAAA,EAC7B;AAAA;AAAA,EAGA,MAAM,QAAuB;AAC3B,SAAK,SAAS,MAAM;AAAA,EACtB;AAAA;AAAA,EAGA,MAAM,UAAyB;AAC7B,SAAK,SAAS,MAAM;AAAA,EACtB;AAAA;AAAA,EAGA,IAAI,OAAe;AACjB,WAAO,KAAK,SAAS;AAAA,EACvB;AACF;;;ACtDA,YAAY,QAAQ;AACpB,SAAS,SAAS;;;ACTlB,SAAS,yBAAyB;;;ACAlC,SAAS,eAAe,gBAA8B;AA6B/C,IAAM,WAAW,CACtB,QACA,SACA,WACgB;AAChB,MAAI;AACF,WAAO,SAAS,OAAO,MAAM,OAAO,IAAI;AAAA,EAC1C,SAAS,OAAO;AACd,QAAI,iBAAiB,UAAU;AAC7B,YAAM,IAAI,gBAAgB,QAAQ,SAAS,cAAc,KAAK,CAAC;AAAA,IACjE;AACA,UAAM,IAAI,gBAAgB,QAAQ,SAAS,KAAK;AAAA,EAClD;AACF;AAiBO,IAAM,SAAS,CAIpB,QACA,QACA,WACoB;AACpB,QAAM,QAAQ,SAAS,UAAU,QAAQ,MAAM;AAC/C,SAAO,EAAE,GAAG,QAAQ,GAAG,MAAM;AAC/B;AAYA,eAAsB,MAAM,IAAa;AACvC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,MAAM,OAAO,EAAE,OAAO,CAAC;AAC7E;;;AChDA,IAAM,iBAAN,MAAqB;AAAA,EACV;AAAA,EACA;AAAA,EACD,MAAM;AAAA,EACN,SAAS;AAAA,EACT,WAAW;AAAA,EACX,SAAS;AAAA,EACT,aAAiC;AAAA,EACjC,gBAAkC;AAAA,EAClC,YAAY;AAAA,EACZ,QAAgB;AAAA,EAExB,YACE,QACA,QACA,WAAW,GACX,OAAe,cACf;AACA,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,YAAY;AACjB,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,IAAI,WAAW;AACb,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,OAAO;AACT,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,KAAK,OAAe;AACtB,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAc,UAAkB;AAC9B,QAAI,WAAW,KAAK,UAAW,MAAK,YAAY;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,UAAkB;AAC7B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,IAAI,eAAe;AACjB,WACE,CAAC,KAAK,aACL,CAAC,KAAK,iBAAiB,KAAK,iBAAiB,oBAAI,KAAK;AAAA,EAE3D;AAAA,EAEA,IAAI,KAAK;AACP,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,QAAQ;AACV,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,UAAU;AACZ,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,QAAQ;AACV,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,YAAY;AACd,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,eAAe;AACjB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAc,QAAuB;AACzC,QAAI,SAAS,GAAG;AACd,WAAK,aAAa,MAAM;AACxB,WAAK,gBAAgB,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM;AAAA,IACnD;AACA,SAAK,SAAS,KAAK,SAAS;AAC5B,WAAO;AAAA,MACL,QAAQ,KAAK;AAAA,MACb,QAAQ,KAAK;AAAA,MACb,IAAI,MAAM;AAAA,MACV,IAAI,MAAM;AAAA,MACV,OAAO,KAAK;AAAA,MACZ,SAAS,MAAM;AAAA,MACf,MAAM,KAAK;AAAA,IACb;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,OAAc;AAChB,QAAI,KAAK,eAAe,MAAM,IAAI;AAChC,WAAK,aAAa;AAClB,WAAK,gBAAgB;AACrB,WAAK,MAAM,MAAM;AACjB,WAAK,SAAS;AACd,aAAO;AAAA,QACL,QAAQ,KAAK;AAAA,QACb,QAAQ,KAAK;AAAA,QACb,IAAI,KAAK;AAAA,QACT,IAAI,MAAM;AAAA,QACV,OAAO,KAAK;AAAA,QACZ,SAAS,MAAM;AAAA,QACf,MAAM,KAAK;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAc,OAAe;AACjC,QAAI,KAAK,eAAe,MAAM,IAAI;AAChC,WAAK,WAAW;AAChB,WAAK,SAAS;AACd,aAAO;AAAA,QACL,QAAQ,KAAK;AAAA,QACb,QAAQ,KAAK;AAAA,QACb,IAAI,KAAK;AAAA,QACT,IAAI,KAAK;AAAA,QACT,OAAO,KAAK;AAAA,QACZ,OAAO,KAAK;AAAA,QACZ,SAAS,MAAM;AAAA,QACf,MAAM,KAAK;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ;AACN,SAAK,MAAM;AACX,SAAK,SAAS;AACd,SAAK,WAAW;AAChB,SAAK,SAAS;AACd,SAAK,aAAa;AAClB,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAmB;AACjB,QAAI,CAAC,KAAK,SAAU,QAAO;AAC3B,SAAK,WAAW;AAChB,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,aAAa;AAClB,SAAK,gBAAgB;AACrB,WAAO;AAAA,EACT;AACF;AAwEO,IAAM,gBAAN,MAAqC;AAAA;AAAA,EAElC,UAA+C,CAAC;AAAA;AAAA,EAEhD,WAAwC,oBAAI,IAAI;AAAA;AAAA,EAEhD,mBAAwC,oBAAI,IAAI;AAAA;AAAA;AAAA,EAGhD,0BAA+C,oBAAI,IAAI;AAAA;AAAA,EAEvD,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMzB,OAA0D,oBAAI,IAAI;AAAA,EAElE,iBAAiB;AACvB,SAAK,QAAQ,SAAS;AACtB,SAAK,iBAAiB,MAAM;AAC5B,SAAK,wBAAwB,MAAM;AACnC,SAAK,yBAAyB;AAC9B,SAAK,KAAK,MAAM;AAAA,EAClB;AAAA;AAAA;AAAA,EAIQ,UACN,GACuB;AACvB,UAAM,MAAM,KAAK,KAAK,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE;AAC7C,WAAO,MAAO,EAAE,GAAG,GAAG,IAAI,IAA8B;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAU;AACd,UAAM,MAAM;AACZ,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO;AACX,UAAM,MAAM;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO;AACX,UAAM,MAAM;AACZ,SAAK,eAAe;AACpB,SAAK,WAAW,oBAAI,IAAI;AAAA,EAC1B;AAAA,EAEQ,SAA4B,OAAc,GAA0B;AAC1E,QAAI,MAAM,QAAQ;AAChB,UAAI,MAAM,cAAc;AACtB,YAAI,EAAE,WAAW,MAAM,OAAQ,QAAO;AAAA,MACxC,WAAW,CAAC,OAAO,MAAM,MAAM,EAAE,KAAK,EAAE,MAAM,EAAG,QAAO;AAAA,IAC1D;AACA,QAAI,MAAM,SAAS,CAAC,MAAM,MAAM,SAAS,EAAE,IAAc,EAAG,QAAO;AACnE,QAAI,MAAM,eAAe,EAAE,MAAM,gBAAgB,MAAM;AACrD,aAAO;AACT,QAAI,EAAE,SAAS,cAAc,CAAC,MAAM,WAAY,QAAO;AACvD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,MACJ,UACA,OACA;AACA,UAAM,MAAM;AACZ,QAAI,QAAQ;AACZ,QAAI,OAAO,UAAU;AACnB,UAAI,KAAK,OAAO,UAAU,KAAK,QAAQ,UAAU;AACjD,aAAO,KAAK,GAAG;AACb,cAAM,IAAI,KAAK,QAAQ,GAAG;AAC1B,YAAI,SAAS,CAAC,KAAK,SAAS,OAAO,CAAC,EAAG;AACvC,YAAI,OAAO,kBAAkB,EAAE,WAAW,MAAM;AAC9C;AACF,YAAI,MAAM,SAAS,EAAE,MAAM,MAAM,MAAO;AACxC,YAAI,MAAM,iBAAiB,EAAE,WAAW,MAAM,cAAe;AAC7D,cAAM,QAAQ;AAAA,UACZ,SAAS,KAAK,UAAU,CAA0B,CAAC;AAAA,QACrD;AACA;AACA,YAAI,OAAO,SAAS,SAAS,MAAM,MAAO;AAAA,MAC5C;AAAA,IACF,OAAO;AACL,UAAI,KAAK,OAAO,SAAS,MAAM;AAC/B,aAAO,IAAI,KAAK,QAAQ,QAAQ;AAC9B,cAAM,IAAI,KAAK,QAAQ,GAAG;AAC1B,YAAI,SAAS,CAAC,KAAK,SAAS,OAAO,CAAC,EAAG;AACvC,YAAI,OAAO,iBAAiB,EAAE,WAAW,MAAM,cAAe;AAC9D,YAAI,OAAO,UAAU,EAAE,MAAM,MAAM,OAAQ;AAC3C,YAAI,OAAO,kBAAkB,EAAE,WAAW,MAAM,eAAgB;AAChE,cAAM,QAAQ;AAAA,UACZ,SAAS,KAAK,UAAU,CAA0B,CAAC;AAAA,QACrD;AACA;AACA,YAAI,OAAO,SAAS,SAAS,MAAM,MAAO;AAAA,MAC5C;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,OACJ,QACA,MACA,MACA,iBACA;AACA,UAAM,MAAM;AACZ,UAAM,kBAAkB,KAAK,iBAAiB,IAAI,MAAM,KAAK;AAC7D,QACE,OAAO,oBAAoB,YAC3B,oBAAoB,iBACpB;AACA,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,QAAI,UAAU,kBAAkB;AAChC,QAAI,mBAAmB;AACvB,UAAM,YAAY,KAAK,IAAI,CAAC,EAAE,MAAM,MAAM,IAAI,MAAM;AAClD,YAAM,IAA2B;AAAA,QAC/B,IAAI,KAAK,QAAQ;AAAA,QACjB;AAAA,QACA;AAAA,QACA,SAAS,oBAAI,KAAK;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAIA,WAAK,QAAQ,KAAK,CAAsC;AACxD,UAAI,OAAO,MAAM;AACf,YAAI,aAAa,KAAK,KAAK,IAAI,MAAM;AACrC,YAAI,CAAC,YAAY;AACf,uBAAa,oBAAI,IAAI;AACrB,eAAK,KAAK,IAAI,QAAQ,UAAU;AAAA,QAClC;AACA,mBAAW,IAAI,EAAE,IAAI,gBAAgB,GAAG,CAA4B;AAAA,MACtE;AACA,UAAI,SAAS,WAAY,oBAAmB,EAAE;AAC9C;AACA,aAAO,KAAK,UAAU,CAAC;AAAA,IACzB,CAAC;AACD,SAAK,iBAAiB,IAAI,QAAQ,UAAU,CAAC;AAC7C,QAAI,oBAAoB,GAAG;AACzB,WAAK,wBAAwB,IAAI,QAAQ,gBAAgB;AAGzD,WAAK,yBAAyB;AAAA,IAChC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,MACJ,SACA,SACA,IACA,QACA,MACA;AACA,UAAM,MAAM;AAGZ,UAAM,eAAe,oBAAI,IAAoB;AAC7C,UAAM,YAAY,CAAC,WAAmB;AACpC,UAAI,KAAK,aAAa,IAAI,MAAM;AAChC,UAAI,CAAC,IAAI;AACP,aAAK,IAAI,OAAO,MAAM;AACtB,qBAAa,IAAI,QAAQ,EAAE;AAAA,MAC7B;AACA,aAAO;AAAA,IACT;AACA,UAAM,WAAW,CAAC,MAA+B;AAC/C,UAAI,EAAE,KAAK,EAAG,QAAO;AACrB,UAAI,CAAC,EAAE,OAAQ,QAAO,EAAE,KAAK,KAAK;AAClC,YAAM,KAAK,UAAU,EAAE,MAAM;AAC7B,iBAAW,CAAC,aAAa,KAAK,KAAK,KAAK,yBAAyB;AAC/D,YAAI,QAAQ,EAAE,MAAM,GAAG,KAAK,WAAW,EAAG,QAAO;AAAA,MACnD;AACA,aAAO;AAAA,IACT;AACA,UAAM,YAAY,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE;AAAA,MAC5C,CAAC,MACC,EAAE,gBAAgB,SAAS,CAAC,MAAM,SAAS,UAAa,EAAE,SAAS;AAAA,IACvE;AAKA,UAAM,MAAM,UACT,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,EACrD,MAAM,GAAG,OAAO,EAChB,IAAI,CAAC,OAAO;AAAA,MACX,QAAQ,EAAE;AAAA,MACV,QAAQ,EAAE;AAAA,MACV,IAAI,EAAE;AAAA,MACN,SAAS;AAAA,IACX,EAAE;AACJ,UAAM,OAAO,UACV,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE,EAC1B,MAAM,GAAG,OAAO,EAChB,IAAI,CAAC,OAAO;AAAA,MACX,QAAQ,EAAE;AAAA,MACV,QAAQ,EAAE;AAAA,MACV,IAAI,EAAE;AAAA,MACN,SAAS;AAAA,IACX,EAAE;AAEJ,UAAM,OAAO,oBAAI,IAAY;AAC7B,UAAM,WAAW,CAAC,GAAG,KAAK,GAAG,IAAI,EAAE,OAAO,CAAC,MAAM;AAC/C,UAAI,KAAK,IAAI,EAAE,MAAM,EAAG,QAAO;AAC/B,WAAK,IAAI,EAAE,MAAM;AACjB,aAAO;AAAA,IACT,CAAC;AAED,WAAO,SACJ;AAAA,MAAI,CAAC,MACJ,KAAK,SAAS,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,IAAI,OAAO,EAAE,GAAG,MAAM;AAAA,IACnE,EACC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,UACJ,SAMA;AACA,UAAM,MAAM;AACZ,QAAI,aAAa;AACjB,eAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX,OAAO;AAAA,IACT,KAAK,SAAS;AACZ,YAAM,WAAW,KAAK,SAAS,IAAI,MAAM;AACzC,UAAI,UAAU;AACZ,iBAAS,cAAc,QAAQ;AAC/B,iBAAS,OAAO;AAAA,MAClB,OAAO;AACL,aAAK,SAAS;AAAA,UACZ;AAAA,UACA,IAAI,eAAe,QAAQ,QAAQ,UAAU,IAAI;AAAA,QACnD;AACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,YAAY;AAChB,eAAW,KAAK,KAAK,SAAS,OAAO,GAAG;AACtC,UAAI,EAAE,KAAK,UAAW,aAAY,EAAE;AAAA,IACtC;AACA,WAAO,EAAE,YAAY,UAAU;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,IAAI,QAAiB;AACzB,UAAM,MAAM;AACZ,WAAO,OACJ,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,EAC9C,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,MAAM,QAAwB;AAClC,UAAM,MAAM;AACZ,WAAO,OACJ,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,EAAE,KAAK,CAAC,EACzD,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,kBACN,QACgC;AAChC,UAAM,YACJ,OAAO,UAAU,CAAC,OAAO,eACrB,IAAI,OAAO,OAAO,MAAM,IACxB;AACN,UAAM,YACJ,OAAO,UAAU,CAAC,OAAO,eACrB,IAAI,OAAO,OAAO,MAAM,IACxB;AACN,WAAO,CAAC,MAAM;AACZ,UAAI,OAAO,WAAW,QAAW;AAC/B,YACE,OAAO,eACH,EAAE,WAAW,OAAO,SACpB,CAAC,UAAW,KAAK,EAAE,MAAM;AAE7B,iBAAO;AAAA,MACX;AACA,UAAI,OAAO,WAAW,QAAW;AAC/B,YAAI,EAAE,WAAW,OAAW,QAAO;AACnC,YACE,OAAO,eACH,EAAE,WAAW,OAAO,SACpB,CAAC,UAAW,KAAK,EAAE,MAAM;AAE7B,iBAAO;AAAA,MACX;AACA,UAAI,OAAO,YAAY,UAAa,EAAE,YAAY,OAAO;AACvD,eAAO;AACT,UAAI,OAAO,SAAS,UAAa,EAAE,SAAS,OAAO,KAAM,QAAO;AAChE,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,MAAM,OAAgC;AAC1C,UAAM,MAAM;AACZ,QAAI,QAAQ;AACZ,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,QAAQ,OAAO;AACxB,cAAM,IAAI,KAAK,SAAS,IAAI,IAAI;AAChC,YAAI,GAAG;AACL,YAAE,MAAM;AACR;AAAA,QACF;AAAA,MACF;AAAA,IACF,OAAO;AACL,YAAM,UAAU,KAAK,kBAAkB,KAAK;AAC5C,iBAAW,KAAK,KAAK,SAAS,OAAO,GAAG;AACtC,YAAI,QAAQ,CAAC,GAAG;AACd,YAAE,MAAM;AACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,MAAM,WAAW,QAAiC;AAChD,UAAM,MAAM;AACZ,UAAM,QAAQ,KAAK,KAAK,IAAI,MAAM,GAAG,QAAQ;AAC7C,SAAK,KAAK,OAAO,MAAM;AACvB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAQ,OAAgC;AAC5C,UAAM,MAAM;AACZ,QAAI,QAAQ;AACZ,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,QAAQ,OAAO;AACxB,cAAM,IAAI,KAAK,SAAS,IAAI,IAAI;AAChC,YAAI,GAAG,QAAQ,EAAG;AAAA,MACpB;AAAA,IACF,OAAO;AAIL,YAAM,UAAU,KAAK,kBAAkB,EAAE,GAAG,OAAO,SAAS,KAAK,CAAC;AAClE,iBAAW,KAAK,KAAK,SAAS,OAAO,GAAG;AACtC,YAAI,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAG;AAAA,MACjC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,WAAW,QAAsB,UAAkB;AACvD,UAAM,MAAM;AACZ,UAAM,UAAU,KAAK,kBAAkB,MAAM;AAC7C,QAAI,QAAQ;AACZ,eAAW,KAAK,KAAK,SAAS,OAAO,GAAG;AACtC,UAAI,CAAC,QAAQ,CAAC,EAAG;AACjB,UAAI,EAAE,aAAa,UAAU;AAC3B,UAAE,aAAa,QAAQ;AACvB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,cACJ,UACA,OAC6B;AAC7B,UAAM,MAAM;AACZ,UAAM,QAAQ,OAAO,SAAS;AAC9B,UAAM,QAAQ,OAAO;AACrB,UAAM,UAAU,OAAO;AACvB,UAAM,YACJ,OAAO,UAAU,CAAC,MAAM,eACpB,IAAI,OAAO,MAAM,MAAM,IACvB;AACN,UAAM,YACJ,OAAO,UAAU,CAAC,MAAM,eACpB,IAAI,OAAO,MAAM,MAAM,IACvB;AAEN,UAAM,SAAS,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE;AAAA,MAAK,CAAC,GAAG,MAClD,EAAE,OAAO,cAAc,EAAE,MAAM;AAAA,IACjC;AAEA,QAAI,QAAQ;AACZ,eAAW,KAAK,QAAQ;AACtB,UAAI,UAAU,UAAa,EAAE,UAAU,MAAO;AAC9C,UAAI,OAAO,WAAW,QAAW;AAC/B,YACE,MAAM,eACF,EAAE,WAAW,MAAM,SACnB,CAAC,UAAW,KAAK,EAAE,MAAM;AAE7B;AAAA,MACJ;AACA,UAAI,OAAO,WAAW,QAAW;AAC/B,YAAI,EAAE,WAAW,OAAW;AAC5B,YACE,MAAM,eACF,EAAE,WAAW,MAAM,SACnB,CAAC,UAAW,KAAK,EAAE,MAAM;AAE7B;AAAA,MACJ;AACA,UAAI,YAAY,UAAa,EAAE,YAAY,QAAS;AACpD,UAAI,OAAO,SAAS,UAAa,EAAE,SAAS,MAAM,KAAM;AACxD,eAAS;AAAA,QACP,QAAQ,EAAE;AAAA,QACV,QAAQ,EAAE;AAAA,QACV,IAAI,EAAE;AAAA,QACN,OAAO,EAAE;AAAA,QACT,SAAS,EAAE;AAAA,QACX,OAAO,EAAE;AAAA,QACT,UAAU,EAAE;AAAA,QACZ,WAAW,EAAE;AAAA,QACb,cAAc,EAAE;AAAA,QAChB,MAAM,EAAE;AAAA,MACV,CAAC;AACD;AACA,UAAI,SAAS,MAAO;AAAA,IACtB;AACA,WAAO,EAAE,YAAY,KAAK,QAAQ,SAAS,GAAG,MAAM;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,YACJ,OACA,SACsC;AACtC,UAAM,MAAM;AACZ,UAAM,UAAU,IAAI,IAAY,SAAS,WAAW,CAAC,CAAC;AACtD,UAAM,YAAY,SAAS,QAAQ;AACnC,UAAM,aAAa,SAAS,SAAS;AACrC,UAAM,aAAa,SAAS,SAAS;AACrC,UAAM,SAAS,SAAS;AAIxB,UAAM,gBAAgB,MAAM,QAAQ,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI;AAC9D,UAAM,SAAS,MAAM,QAAQ,KAAK,IAAI,OAAO;AAC7C,UAAM,YACJ,QAAQ,UAAU,CAAC,OAAO,eACtB,IAAI,OAAO,OAAO,MAAM,IACxB;AAEN,UAAM,cAAc,oBAAI,IAAqB;AAC7C,UAAM,WAAW,CAAC,WAA4B;AAC5C,YAAM,SAAS,YAAY,IAAI,MAAM;AACrC,UAAI,WAAW,OAAW,QAAO;AACjC,UAAI,KAAK;AACT,UAAI,eAAe;AACjB,aAAK,cAAc,IAAI,MAAM;AAAA,MAC/B,WAAW,QAAQ,WAAW,QAAW;AACvC,aAAK,OAAO,eACR,WAAW,OAAO;AAAA;AAAA,UAElB,UAAW,KAAK,MAAM;AAAA;AAAA,MAC5B;AACA,kBAAY,IAAI,QAAQ,EAAE;AAC1B,aAAO;AAAA,IACT;AAQA,UAAM,MAAM,oBAAI,IAAiB;AACjC,eAAW,KAAK,KAAK,SAAS;AAC5B,UAAI,WAAW,UAAa,EAAE,MAAM,OAAQ;AAC5C,UAAI,CAAC,SAAS,EAAE,MAAM,EAAG;AACzB,UAAI,QAAQ,IAAI,EAAE,IAAc,EAAG;AACnC,UAAI,IAAI,IAAI,IAAI,EAAE,MAAM;AACxB,UAAI,CAAC,GAAG;AACN,YAAI,EAAE,MAAM,GAAG,OAAO,EAAE;AACxB,YAAI,UAAW,GAAE,OAAO;AACxB,YAAI,WAAY,GAAE,QAAQ,CAAC;AAC3B,YAAI,IAAI,EAAE,QAAQ,CAAC;AAAA,MACrB;AACA,QAAE,OAAO;AACT,QAAE;AACF,UAAI,YAAY;AACd,cAAM,IAAI,OAAO,EAAE,IAAI;AAEvB,UAAE,MAAO,CAAC,KAAK,EAAE,MAAO,CAAC,KAAK,KAAK;AAAA,MACrC;AAAA,IACF;AAEA,UAAM,MAAM,oBAAI,IAA4B;AAC5C,eAAW,CAAC,QAAQ,CAAC,KAAK,KAAK;AAC7B,YAAM,QAKF,EAAE,MAAM,EAAE,KAAK;AACnB,UAAI,UAAW,OAAM,OAAO,EAAE;AAC9B,UAAI,WAAY,OAAM,QAAQ,EAAE;AAChC,UAAI,WAAY,OAAM,QAAQ,EAAE;AAChC,UAAI,IAAI,QAAQ,KAAuB;AAAA,IACzC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SACJ,SAKA;AACA,UAAM,MAAM;AAEZ,UAAM,iBAAiB,oBAAI,IAAoB;AAC/C,UAAM,aAAa,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AACvD,eAAW,KAAK,KAAK,SAAS;AAC5B,UAAI,WAAW,IAAI,EAAE,MAAM,GAAG;AAC5B,uBAAe,IAAI,EAAE,SAAS,eAAe,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;AAAA,MACtE;AAAA,IACF;AACA,SAAK,UAAU,KAAK,QAAQ,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,MAAM,CAAC;AACnE,eAAW,UAAU,YAAY;AAC/B,WAAK,SAAS,OAAO,MAAM;AAC3B,WAAK,iBAAiB,OAAO,MAAM;AACnC,WAAK,wBAAwB,OAAO,MAAM;AAAA,IAC5C;AACA,UAAM,SAAS,oBAAI,IAGjB;AACF,eAAW,EAAE,QAAQ,UAAU,KAAK,KAAK,SAAS;AAChD,YAAM,QAA2C;AAAA,QAC/C,IAAI,KAAK,QAAQ;AAAA,QACjB;AAAA,QACA,SAAS;AAAA,QACT,SAAS,oBAAI,KAAK;AAAA,QAClB,MAAM,aAAa,SAAY,aAAa;AAAA,QAC5C,MAAM,YAAY,CAAC;AAAA,QACnB,MAAM,QAAQ,EAAE,aAAa,IAAI,WAAW,CAAC,EAAE;AAAA,MACjD;AACA,WAAK,QAAQ,KAAK,KAAK;AACvB,WAAK,iBAAiB,IAAI,QAAQ,CAAC;AACnC,UAAI,MAAM,SAAS,YAAY;AAC7B,aAAK,wBAAwB,IAAI,QAAQ,MAAM,EAAE;AAAA,MACnD;AACA,aAAO,IAAI,QAAQ;AAAA,QACjB,SAAS,eAAe,IAAI,MAAM,KAAK;AAAA,QACvC,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAGA,QAAI,MAAM;AACV,eAAW,MAAM,KAAK,wBAAwB,OAAO;AACnD,UAAI,KAAK,IAAK,OAAM;AACtB,SAAK,yBAAyB;AAC9B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,QACJ,QAGe;AACf,UAAM,MAAM;AAEZ,UAAM,cAAc,KAAK;AACzB,UAAM,eAAe,KAAK;AAC1B,UAAM,uBAAuB,KAAK;AAClC,UAAM,8BAA8B,KAAK;AACzC,UAAM,6BAA6B,KAAK;AAExC,SAAK,UAAU,CAAC;AAChB,SAAK,WAAW,oBAAI,IAAI;AACxB,SAAK,mBAAmB,oBAAI,IAAI;AAChC,SAAK,0BAA0B,oBAAI,IAAI;AACvC,SAAK,yBAAyB;AAC9B,QAAI;AACF,YAAM,OAAO,OAAO,UAAU;AAC5B,cAAM,KAAK,KAAK,QAAQ;AACxB,cAAM,YAA+C,EAAE,GAAG,OAAO,GAAG;AACpE,aAAK,QAAQ,KAAK,SAAS;AAK3B,aAAK,iBAAiB,IAAI,MAAM,QAAQ,MAAM,OAAO;AACrD,YAAI,MAAM,SAAS,YAAY;AAC7B,eAAK,wBAAwB,IAAI,MAAM,QAAQ,EAAE;AACjD,eAAK,yBAAyB;AAAA,QAChC;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH,SAAS,KAAK;AAGZ,WAAK,UAAU;AACf,WAAK,WAAW;AAChB,WAAK,mBAAmB;AACxB,WAAK,0BAA0B;AAC/B,WAAK,yBAAyB;AAC9B,YAAM;AAAA,IACR;AAAA,EACF;AACF;;;AF5/BO,IAAM,SAAS,IAAI,kBAA0B;AAuB7C,IAAM,YAAY,CAAC,SAAS,MAAM;AAuBzC,IAAM,WAAW,oBAAI,IAAwB;AAqBtC,SAAS,KAA8B,UAA0B;AACtE,SAAO,CAAC,YAAyB;AAC/B,QAAI,CAAC,SAAS,IAAI,SAAS,IAAI,GAAG;AAChC,YAAM,WAAW,SAAS,OAAO;AACjC,eAAS,IAAI,SAAS,MAAM,QAAQ;AAIpC,UAAI,EAAE,KAAK,WAAW,SAAS,IAAI,IAAI,SAAS,YAAY,IAAI,EAAE;AAAA,IACpE;AACA,WAAO,SAAS,IAAI,SAAS,IAAI;AAAA,EACnC;AACF;AAoCO,IAAM,MAAM,KAAK,SAASA,KAAI,SAAkB;AACrD,QAAM,MAAM,OAAO;AACnB,SACE,WACA,IAAI,cAAc;AAAA,IAChB,OAAO,IAAI;AAAA,IACX,QAAQ,IAAI,QAAQ;AAAA,EACtB,CAAC;AAEL,CAAC;AAwCD,IAAM,SAAS,KAAK,SAAS,MAAM,SAAwB;AACzD,SAAO,WAAW,IAAI,cAAc;AACtC,CAAC;AAEM,IAAMC,UAAS,CAAC,YAA2B;AAChD,SAAO,OAAO,SAAS,GAAG,SAAS,OAAO,OAAO;AACnD;AAeA,IAAM,SAAS,KAAK,SAAS,MAAM,SAAiB;AAClD,SAAO,WAAW,IAAI,cAAc;AACtC,CAAC;AAEM,IAAMC,UAAS,CAAC,YAA2B;AAChD,SAAO,OAAO,SAAS,GAAG,SAAS,OAAO,OAAO;AACnD;AAOA,IAAM,YAAwB,CAAC;AAiB/B,eAAsB,eAAe,OAAiB,QAAuB;AAC3E,MAAI,SAAS,WAAW,OAAO,EAAE,QAAQ,cAAc;AAIrD,QAAI,EAAE;AAAA,MACJ;AAAA,IACF;AACA;AAAA,EACF;AAKA,aAAW,YAAY,CAAC,GAAG,SAAS,EAAE,QAAQ,GAAG;AAC/C,UAAM,SAAS;AAAA,EACjB;AACA,aAAW,WAAW,CAAC,GAAG,SAAS,OAAO,CAAC,EAAE,QAAQ,GAAG;AACtD,UAAM,QAAQ,QAAQ;AACtB,QAAI,EAAE,KAAK,WAAW,QAAQ,YAAY,IAAI,EAAE;AAAA,EAClD;AACA,WAAS,MAAM;AACf,SAAO,EAAE,QAAQ,UAAU,QAAQ,KAAK,SAAS,UAAU,IAAI,CAAC;AAClE;AA0BO,SAAS,QACd,UACoC;AACpC,cAAY,UAAU,KAAK,QAAQ;AACnC,SAAO;AACT;AAWO,IAAM,aAAa;AASnB,IAAM,kBAAkB;AAWxB,IAAM,eAAe;;;ADzSrB,IAAM,gBAAgB,EAAE,OAAO;AAAA,EACpC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACxC,QAAQ,EACL,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,EAChE,SAAS,EACT,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EACpB,SAAS;AAAA,EACZ,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACpC,cAAc,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,EAAE,SAAS;AAC1D,CAAC;AAqBD,IAAM,mBAA4B;AAAA,EAChC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,aAAa;AACf;AAWA,IAAM,cAAc,MAAe;AACjC,MAAI;AACF,UAAM,MAAS,gBAAa,cAAc;AAC1C,WAAO,KAAK,MAAM,IAAI,SAAS,CAAC;AAAA,EAClC,SAAS,KAAK;AACZ,qBAAiB;AACjB,WAAO;AAAA,EACT;AACF;AAGA,IAAI;AAOJ,IAAM,aAAa,cAAc,OAAO;AAAA,EACtC,KAAK,EAAE,KAAK,YAAY;AAAA,EACxB,UAAU,EAAE,KAAK,SAAS;AAAA,EAC1B,eAAe,EAAE,QAAQ;AAAA,EACzB,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI;AAC3C,CAAC;AAOD,IAAM,EAAE,UAAU,WAAW,iBAAiB,SAAS,IAAI,QAAQ;AAEnE,IAAM,MAAO,YAAY;AACzB,IAAM,WAAY,cACf,aAAa,SACV,UACA,aAAa,eACX,SACA;AACR,IAAM,iBAAiB,mBAAmB,YAAY;AACtD,IAAM,UAAU,SAAS,aAAa,SAAS,MAAO,YAAY,OAAQ,EAAE;AAE5E,IAAM,MAAM,YAAY;AAMxB,IAAI;AA4DG,IAAM,SAAS,MAAc;AAClC,MAAI,CAAC,YAAY;AACf,iBAAa;AAAA,MACX,EAAE,GAAG,KAAK,KAAK,UAAU,eAAe,QAAQ;AAAA,MAChD;AAAA,IACF;AACA,QAAI,gBAAgB;AAIlB,YAAM,MACJ,0BAA0B,QACtB,eAAe,UACf,OAAO,mBAAmB,WACxB,iBACA;AACR,UAAI,EAAE;AAAA,QACJ,sCAAsC,GAAG,4BAC9B,iBAAiB,IAAI,cAAc,iBAAiB,OAAO;AAAA,MACxE;AACA,uBAAiB;AAAA,IACnB;AAAA,EACF;AACA,SAAO;AACT;","names":["log","store","cache"]}
1
+ {"version":3,"sources":["../src/adapters/console-logger.ts","../src/internal/lru-map.ts","../src/adapters/in-memory-cache.ts","../src/config.ts","../src/ports.ts","../src/utils.ts","../src/adapters/in-memory-store.ts"],"sourcesContent":["/**\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 * @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 * @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 { AsyncLocalStorage } from \"node:async_hooks\";\nimport { 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 type {\n Cache,\n Disposable,\n Disposer,\n Logger,\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/** AsyncLocalStorage carrying the active Act's ports. Internal — not re-exported. */\nexport const scoped = new AsyncLocalStorage<Scoped>();\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 scoped.getStore()?.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 scoped.getStore()?.cache ?? _cache(adapter);\n}) as (adapter?: Cache) => Cache;\n\n// ---------------------------------------------------------------------------\n// Disposal\n// ---------------------------------------------------------------------------\n\n/** Registered cleanup functions, executed in reverse order during shutdown. */\nconst disposers: Disposer[] = [];\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 for (const disposer of [...disposers].reverse()) {\n await disposer();\n }\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 && disposers.push(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","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 * @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 */\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} 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\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 /**\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 );\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 /**\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 * Acknowledge completion of processing for this stream.\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 this._at = lease.at;\n this._retry = -1;\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 if (this._leased_by === lease.by) {\n this._blocked = true;\n this._error = error;\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 }\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 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 // stored stream positions and other metadata\n private _streams: Map<string, InMemoryStream> = new Map();\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 source-pattern probe in claim()\n // without scanning 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._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 // 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 if (query?.backward) {\n let i = (query?.before || 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 && e.id <= query.after) break;\n if (query.created_after && e.created <= query.created_after) break;\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 = (query?.after ?? -1) + 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 && e.id >= query.before) break;\n if (query?.created_before && e.created >= query.created_before) break;\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._events.length,\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 this._events.length, so any\n // 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 // Cache compiled regexes — multiple subscribed streams typically share the\n // same source pattern, and the inner loop can run thousands of times per claim.\n const source_regex = new Map<string, RegExp>();\n const get_regex = (source: string) => {\n let re = source_regex.get(source);\n if (!re) {\n re = new RegExp(source);\n source_regex.set(source, re);\n }\n return re;\n };\n const has_work = (s: InMemoryStream): boolean => {\n if (s.at < 0) return true;\n if (!s.source) return s.at < this._max_non_snap_event_id;\n const re = get_regex(s.source);\n for (const [stream_name, maxId] of this._max_event_id_by_stream) {\n if (maxId > s.at && re.test(stream_name)) return true;\n }\n return false;\n };\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.\n const lag = available\n .sort((a, b) => b.priority - a.priority || a.at - b.at)\n .slice(0, lagging)\n .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: Array<{\n stream: string;\n source?: string;\n priority?: number;\n lane?: string;\n }>\n ) {\n await sleep();\n let subscribed = 0;\n for (const {\n stream,\n source,\n priority = 0,\n lane = DEFAULT_LANE,\n } of streams) {\n const existing = this._streams.get(stream);\n if (existing) {\n existing.bump_priority(priority);\n existing.lane = lane;\n } else {\n this._streams.set(\n stream,\n new InMemoryStream(stream, source, priority, lane)\n );\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 { subscribed, watermark };\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 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 * 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 for (const name of 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 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\n const sorted = [...this._streams.values()].sort((a, b) =>\n a.stream.localeCompare(b.stream)\n );\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 (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 });\n count++;\n if (count >= limit) break;\n }\n return { maxEventId: this._events.length - 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\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 const out = new Map<string, StreamStats<E>>();\n for (const [stream, a] of acc) {\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 }\n return out;\n }\n\n /**\n * Atomically truncates streams and seeds each with a snapshot or tombstone.\n * @param targets - Streams to truncate with optional snapshot state and meta.\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 }>\n ) {\n await sleep();\n // Count per-stream deletions\n const deleted_counts = new Map<string, number>();\n const stream_set = new Set(targets.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 for (const stream of stream_set) {\n this._streams.delete(stream);\n this._stream_versions.delete(stream);\n this._max_event_id_by_stream.delete(stream);\n }\n const result = new Map<\n string,\n { deleted: number; committed: Committed<Schemas, keyof Schemas> }\n >();\n for (const { stream, snapshot, meta } of targets) {\n const event: Committed<Schemas, keyof Schemas> = {\n id: this._events.length,\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 (matching the\n * adapter's commit-id convention — InMemory uses `_events.length`).\n * `created` is 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_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 // Swap in fresh state for the duration of the rebuild.\n this._events = [];\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 try {\n await driver(async (event) => {\n const id = this._events.length;\n const committed: Committed<Schemas, keyof Schemas> = { ...event, id };\n this._events.push(committed);\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._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 throw err;\n }\n }\n}\n"],"mappings":"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","names":["log","store","cache"]}
package/dist/index.cjs CHANGED
@@ -1229,7 +1229,7 @@ var InMemoryStore = class {
1229
1229
  ok = array_targets.has(stream);
1230
1230
  } else if (filter?.stream !== void 0) {
1231
1231
  ok = filter.stream_exact ? stream === filter.stream : (
1232
- // biome-ignore lint/style/noNonNullAssertion: stream_re set when stream is regex
1232
+ // stream_re set when stream is regex
1233
1233
  stream_re.test(stream)
1234
1234
  );
1235
1235
  }
@@ -1643,7 +1643,7 @@ var make_restart_candidate_pass = (deps, options) => {
1643
1643
  // names map is sparse — `__snapshot__` key absent when the
1644
1644
  // stream has never been snapshotted (a common case for the
1645
1645
  // restart-candidate signal).
1646
- snaps: names["__snapshot__"] ?? 0
1646
+ snaps: names.__snapshot__ ?? 0
1647
1647
  });
1648
1648
  },
1649
1649
  drain: () => findings
@@ -1705,7 +1705,7 @@ var make_snapshot_drift_pass = (deps, options) => {
1705
1705
  candidates.push({
1706
1706
  stream,
1707
1707
  total: count,
1708
- snaps: names["__snapshot__"] ?? 0
1708
+ snaps: names.__snapshot__ ?? 0
1709
1709
  });
1710
1710
  },
1711
1711
  async finalize(deps2) {
@@ -2199,6 +2199,76 @@ var AutocloseController = class {
2199
2199
  }
2200
2200
  };
2201
2201
 
2202
+ // src/internal/autoclose-policy.ts
2203
+ var import_zod6 = require("zod");
2204
+ var MIN_AFTER_MS = 6e4;
2205
+ var AfterSchema = import_zod6.z.object({
2206
+ days: import_zod6.z.number({ message: "autocloses: after.days must be a number" }).positive("autocloses: after.days must be > 0")
2207
+ }).refine((o) => o.days * 864e5 >= MIN_AFTER_MS, {
2208
+ message: `autocloses: after resolves to < ${MIN_AFTER_MS}ms (one minute), which is too short to be a meaningful retention window`
2209
+ });
2210
+ var IsSchema = import_zod6.z.union([
2211
+ import_zod6.z.string().min(1, "autocloses: is must be a non-empty event name"),
2212
+ import_zod6.z.array(
2213
+ import_zod6.z.string().min(1, "autocloses: is entries must be non-empty event names")
2214
+ ).min(1, "autocloses: is must include at least one event name").readonly()
2215
+ ]);
2216
+ var ReachesSchema = import_zod6.z.number({ message: "autocloses: reaches must be a number" }).int("autocloses: reaches must be an integer").min(1, "autocloses: reaches must be >= 1");
2217
+ var OrBlockSchema = import_zod6.z.object({
2218
+ after: AfterSchema.optional(),
2219
+ is: IsSchema.optional(),
2220
+ reaches: ReachesSchema.optional()
2221
+ }).strict().refine(
2222
+ (o) => o.after !== void 0 || o.is !== void 0 || o.reaches !== void 0,
2223
+ {
2224
+ message: "autocloses: `or` block must include at least one of after / is / reaches"
2225
+ }
2226
+ );
2227
+ var AutoclosePolicySchema = import_zod6.z.object({
2228
+ after: AfterSchema.optional(),
2229
+ is: IsSchema.optional(),
2230
+ reaches: ReachesSchema.optional(),
2231
+ or: OrBlockSchema.optional()
2232
+ }).strict().refine(
2233
+ (o) => o.after !== void 0 || o.is !== void 0 || o.reaches !== void 0 || o.or !== void 0,
2234
+ {
2235
+ message: "autocloses: at least one of after / is / reaches / or must be specified \u2014 empty `{}` is a misconfiguration"
2236
+ }
2237
+ );
2238
+ function compile_after(after) {
2239
+ const after_ms = after.days * 864e5;
2240
+ return (_stream, head) => Date.now() - head.created.getTime() >= after_ms;
2241
+ }
2242
+ function compile_is(is) {
2243
+ const set = new Set(typeof is === "string" ? [is] : is);
2244
+ return (_stream, head) => set.has(head.name);
2245
+ }
2246
+ function compile_reaches(reaches) {
2247
+ return (_stream, _head, count) => count >= reaches;
2248
+ }
2249
+ function compile_autoclose_policy(options) {
2250
+ const parsed = AutoclosePolicySchema.parse(options);
2251
+ const and_preds = [];
2252
+ if (parsed.after) and_preds.push(compile_after(parsed.after));
2253
+ if (parsed.is) and_preds.push(compile_is(parsed.is));
2254
+ if (parsed.reaches) and_preds.push(compile_reaches(parsed.reaches));
2255
+ const or_preds = [];
2256
+ if (parsed.or) {
2257
+ if (parsed.or.after) or_preds.push(compile_after(parsed.or.after));
2258
+ if (parsed.or.is) or_preds.push(compile_is(parsed.or.is));
2259
+ if (parsed.or.reaches) or_preds.push(compile_reaches(parsed.or.reaches));
2260
+ }
2261
+ return (stream, head, count) => {
2262
+ if (and_preds.length > 0 && and_preds.every((p) => p(stream, head, count))) {
2263
+ return true;
2264
+ }
2265
+ if (or_preds.some((p) => p(stream, head, count))) {
2266
+ return true;
2267
+ }
2268
+ return false;
2269
+ };
2270
+ }
2271
+
2202
2272
  // src/internal/build-classify.ts
2203
2273
  var ALL_LANES = /* @__PURE__ */ Symbol("act-1103/all-lanes");
2204
2274
  function classify_registry(registry, states) {
@@ -2920,7 +2990,7 @@ function trace_cycle(logger, leased, fetched, handled, acked, blocked) {
2920
2990
  const detail = leased.map(({ stream, at, retry }) => {
2921
2991
  const f = fetch_by_stream.get(stream);
2922
2992
  const key = f?.source ? `${hue(C_STREAM, stream)}${dim(`<-${f.source}`)}` : hue(C_STREAM, stream);
2923
- const events = f && f.events.length ? ` ${dim(
2993
+ const events = f?.events.length ? ` ${dim(
2924
2994
  `[${f.events.map(({ id, name }) => `#${id} ${String(name)}`).join(", ")}]`
2925
2995
  )}` : "";
2926
2996
  const acked_at = acked_by_stream.get(stream);
@@ -3155,7 +3225,7 @@ var DrainController = class {
3155
3225
  };
3156
3226
 
3157
3227
  // src/internal/merge.ts
3158
- var import_zod6 = require("zod");
3228
+ var import_zod7 = require("zod");
3159
3229
  function base_type_name(zodType) {
3160
3230
  let t = zodType;
3161
3231
  while (typeof t.unwrap === "function") {
@@ -3164,7 +3234,7 @@ function base_type_name(zodType) {
3164
3234
  return t.constructor.name;
3165
3235
  }
3166
3236
  function merge_schemas(existing, incoming, state_name) {
3167
- if (existing instanceof import_zod6.ZodObject && incoming instanceof import_zod6.ZodObject) {
3237
+ if (existing instanceof import_zod7.ZodObject && incoming instanceof import_zod7.ZodObject) {
3168
3238
  const existing_shape = existing.shape;
3169
3239
  const incoming_shape = incoming.shape;
3170
3240
  for (const key of Object.keys(incoming_shape)) {
@@ -3270,6 +3340,11 @@ function merge_event_register(target, source) {
3270
3340
  const target_reg = target[event_name];
3271
3341
  if (!target_reg) continue;
3272
3342
  for (const [name, reaction] of source_reg.reactions) {
3343
+ const existing = target_reg.reactions.get(name);
3344
+ if (existing !== void 0 && existing !== reaction)
3345
+ throw new Error(
3346
+ `Duplicate reaction "${name}" for event "${event_name}". Reaction handlers are keyed by function name; rename one of them.`
3347
+ );
3273
3348
  target_reg.reactions.set(name, reaction);
3274
3349
  }
3275
3350
  }
@@ -4720,6 +4795,10 @@ function act() {
4720
4795
  throw new Error(
4721
4796
  `Reaction handler for "${String(event)}" must be a named function`
4722
4797
  );
4798
+ if (registry.events[event].reactions.has(handler.name))
4799
+ throw new Error(
4800
+ `Duplicate reaction "${handler.name}" for event "${String(event)}". Reaction handlers are keyed by function name; rename one of them.`
4801
+ );
4723
4802
  registry.events[event].reactions.set(handler.name, reaction);
4724
4803
  return Object.assign(builder, {
4725
4804
  to(resolver) {
@@ -4836,6 +4915,10 @@ function _projection(target) {
4836
4915
  throw new Error(
4837
4916
  `Projection handler for "${event}" must be a named function`
4838
4917
  );
4918
+ if (register.reactions.has(handler.name))
4919
+ throw new Error(
4920
+ `Duplicate projection handler "${handler.name}" for event "${event}". Projection handlers are keyed by function name; rename one of them.`
4921
+ );
4839
4922
  register.reactions.set(handler.name, reaction);
4840
4923
  const widened = base;
4841
4924
  return Object.assign(widened, {
@@ -4911,6 +4994,10 @@ function slice() {
4911
4994
  throw new Error(
4912
4995
  `Reaction handler for "${String(event)}" must be a named function`
4913
4996
  );
4997
+ if (events[event].reactions.has(handler.name))
4998
+ throw new Error(
4999
+ `Duplicate reaction "${handler.name}" for event "${String(event)}". Reaction handlers are keyed by function name; rename one of them.`
5000
+ );
4914
5001
  events[event].reactions.set(handler.name, reaction);
4915
5002
  return Object.assign(builder, {
4916
5003
  to(resolver) {
@@ -5022,10 +5109,17 @@ function action_builder(state2) {
5022
5109
  internal.disclose = disclose;
5023
5110
  return builder;
5024
5111
  },
5025
- autocloses(predicate) {
5026
- if (typeof predicate !== "function") {
5112
+ autocloses(predicate_or_policy) {
5113
+ let predicate;
5114
+ if (typeof predicate_or_policy === "function") {
5115
+ predicate = predicate_or_policy;
5116
+ } else if (predicate_or_policy !== null && typeof predicate_or_policy === "object") {
5117
+ predicate = compile_autoclose_policy(
5118
+ predicate_or_policy
5119
+ );
5120
+ } else {
5027
5121
  throw new Error(
5028
- ".autocloses(predicate) requires a function; got " + typeof predicate
5122
+ ".autocloses(...) requires a function or a policy object; got " + typeof predicate_or_policy
5029
5123
  );
5030
5124
  }
5031
5125
  internal.autoclose = predicate;