@rotorsoft/act-sqlite 1.15.0 → 1.16.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,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/sqlite-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/sqlite-store.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -751,7 +751,7 @@ var SqliteStore = class {
751
751
  // --- query_streams: read-only introspection with filters ---
752
752
  async query_streams(callback, query) {
753
753
  const limit = query?.limit ?? 100;
754
- let sql = "SELECT stream, source, at, retry, blocked, error, leased_by, leased_until, priority, lane, deferred_at FROM streams WHERE 1=1";
754
+ let sql = "SELECT stream, source, at, retry, blocked, error, leased_by, leased_until, priority, lane, deferred_at, correlated_at FROM streams WHERE 1=1";
755
755
  const args = [];
756
756
  if (query?.stream !== void 0) {
757
757
  if (query.stream_exact) {
@@ -794,6 +794,7 @@ var SqliteStore = class {
794
794
  for (const row of streamsResult.rows) {
795
795
  const leased_until = row.leased_until;
796
796
  const deferred_at = row.deferred_at;
797
+ const correlated_at = row.correlated_at;
797
798
  callback({
798
799
  stream: row.stream,
799
800
  source: row.source ?? void 0,
@@ -805,7 +806,8 @@ var SqliteStore = class {
805
806
  leased_by: row.leased_by ?? void 0,
806
807
  leased_until: leased_until ? new Date(leased_until) : void 0,
807
808
  lane: row.lane,
808
- deferred_at: deferred_at ? new Date(deferred_at).getTime() : void 0
809
+ deferred_at: deferred_at ? new Date(deferred_at).getTime() : void 0,
810
+ correlated_at: correlated_at ?? void 0
809
811
  });
810
812
  count++;
811
813
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/sqlite-store.ts"],"sourcesContent":["/**\n * @packageDocumentation\n * @module act-sqlite\n * Main entry point for the Act-SQLite adapter. Re-exports all core APIs\n */\nexport * from \"./sqlite-store.js\";\n","import { type Client, createClient } from \"@libsql/client\";\nimport type {\n BlockedLease,\n Committed,\n EventMeta,\n Lease,\n Message,\n Query,\n QueryStatsOptions,\n QueryStreams,\n QueryStreamsResult,\n Schemas,\n Store,\n StreamFilter,\n StreamPosition,\n StreamStats,\n SubscribeInput,\n} from \"@rotorsoft/act\";\nimport {\n ConcurrencyError,\n dateReviver,\n is_literal_source,\n StoreError,\n ValidationError,\n} from \"@rotorsoft/act\";\nimport {\n decrypt,\n type Encryption,\n encrypt,\n makeKeyResolver,\n} from \"@rotorsoft/act-crypto\";\n\n/**\n * SQLite store configuration\n */\nexport interface SqliteConfig {\n /**\n * libSQL connection URL. **Required — there is no default.**\n *\n * Accepted forms:\n *\n * - `file:myapp.db` — a local database file, relative or absolute.\n * This is the shape an embedded deployment wants.\n * - `libsql://…` / `https://…` — a remote libSQL server, paired with\n * {@link SqliteConfig.authToken}.\n * - `:memory:` / `file::memory:` — an in-memory database. Normalized\n * to `file::memory:?cache=shared`, because libSQL hands every\n * *connection* its own private database otherwise and the client\n * does not pin statements to one connection — DDL would land in a\n * database later statements cannot see, so writes would be accepted\n * and then silently lost. Shared cache is the only in-memory mode\n * that round-trips, and it comes with a caveat worth knowing: it is\n * **one database per process**, shared by every store pointed at\n * it, and it outlives {@link SqliteStore.dispose}. For isolated\n * throwaway state prefer `InMemoryStore` from `@rotorsoft/act`, or\n * give each store its own `file:` path.\n *\n * `file::memory:?cache=private` is rejected at construction — it is\n * the failure mode above, spelled out explicitly.\n */\n url: string;\n /** Auth token for libSQL server connections (optional) */\n authToken?: string;\n /**\n * Adapter-layer envelope encryption for the `events.pii` column.\n * Optional — when present, every non-null PII payload is encrypted\n * before INSERT and decrypted on every read; when absent, the\n * column is stored and read as plaintext (the framework's default\n * behavior).\n *\n * Cipher and wire format come from `@rotorsoft/act-crypto`:\n * AES-256-GCM with a versioned base64-framed envelope. The TEXT\n * column stores `JSON.stringify(...)`-ed values either way, so\n * encrypted writes land as a JSON-stringified base64 string and\n * plaintext writes as a JSON-stringified object. The read path\n * discriminates by `typeof` after `JSON.parse` — strings get\n * decrypted, objects pass through — which makes mixed-data\n * rollouts transparent.\n *\n * `forget_pii` semantics are unchanged: the column is set to\n * `NULL` regardless of whether the prior value was plaintext or\n * ciphertext.\n *\n * Encryption at rest at the **storage** layer (SQLite SEE, an\n * encrypted volume, OS-level FDE) composes orthogonally with\n * adapter-layer encryption. See\n * `docs/docs/guides/pii-encryption-at-rest.md` for the decision\n * matrix.\n */\n pii_encryption?: Encryption;\n}\n\n/**\n * The only in-memory URL libSQL serves from a single database. A bare\n * `:memory:` / `file::memory:` gives every connection a *private*\n * database, and the client is free to run consecutive statements on\n * different connections — `seed()`'s DDL then lands somewhere the next\n * `commit` cannot see, which surfaces as a successful commit followed\n * by \"no such table: events\" on readback.\n * @internal\n */\nconst SHARED_MEMORY_URL = \"file::memory:?cache=shared\";\n\n/**\n * Recognize libSQL's in-memory URL forms: the bare `:memory:` alias and\n * the `file::memory:` URI it expands to, with or without a query string.\n * Mirrors `isInMemoryConfig` in `@libsql/core`.\n * @internal\n */\nconst is_in_memory_url = (url: string): boolean =>\n url === \":memory:\" ||\n url === \"file::memory:\" ||\n url.startsWith(\"file::memory:?\");\n\n/**\n * Validate and normalize the configured URL.\n *\n * Two failures are turned into loud construction-time errors rather\n * than silent data loss: a missing URL (there is no safe default — a\n * store has to be told where to write) and an explicitly private\n * in-memory database (unusable, for the reason on\n * {@link SHARED_MEMORY_URL}). Every other in-memory spelling is\n * normalized to the shared-cache form so it actually round-trips.\n * @internal\n */\nconst resolve_url = (url: unknown): string => {\n if (typeof url !== \"string\" || url.trim().length === 0)\n throw new ValidationError(\n 'SqliteStore config — \"url\" is required and has no default. Use a file database (\"file:myapp.db\"), a remote libSQL server (\"libsql://…\" plus authToken), or \":memory:\" for a process-wide shared in-memory database. For isolated throwaway state, prefer InMemoryStore from @rotorsoft/act',\n { url },\n \"missing url\"\n );\n if (url.startsWith(\"file::memory:?\") && url.includes(\"cache=private\"))\n throw new ValidationError(\n 'SqliteStore config — a private in-memory database (\"cache=private\") cannot back a store: libSQL gives each connection its own database, so writes are accepted and then lost on the next statement. Use \":memory:\" for the shared-cache database, a \"file:\" path, or InMemoryStore from @rotorsoft/act',\n { url },\n \"private in-memory url\"\n );\n return is_in_memory_url(url) ? SHARED_MEMORY_URL : url;\n};\n\n/**\n * Parse a JSON-encoded TEXT column, reviving ISO-date strings to `Date`\n * for cross-adapter payload parity (#1198).\n * @internal\n */\nconst parse_json = (raw: string): unknown => JSON.parse(raw, dateReviver);\n\n/**\n * SQLite extended result code for a UNIQUE constraint violation\n * (`SQLITE_CONSTRAINT_UNIQUE`). libsql surfaces it on the error's\n * `rawCode`.\n * @internal\n */\nconst SQLITE_CONSTRAINT_UNIQUE = 2067;\n\n/**\n * Recognize a `(stream, version)` unique-constraint violation from a\n * libsql commit error (#1202). Detects via the extended result code when\n * present, falling back to the message text (`UNIQUE constraint failed`)\n * for drivers that surface only the message.\n * @internal\n */\nconst is_unique_violation = (e: unknown): boolean => {\n const err = e as { rawCode?: number; message?: unknown };\n return (\n err?.rawCode === SQLITE_CONSTRAINT_UNIQUE ||\n (typeof err?.message === \"string\" &&\n err.message.includes(\"UNIQUE constraint failed\"))\n );\n};\n\n/** Portable stream-filter body grammar: literal characters, `.` (any\n * single character), and `.*` (any run). Everything else — escapes\n * (`\\.`), character classes (`[0-9]`), grouping/alternation (`(a|b)`),\n * quantifiers (`+ ? { }`), and mid-pattern anchors — has no faithful\n * `GLOB` translation and must throw instead of silently mis-matching.\n * @internal\n */\nconst PORTABLE_BODY = /^(?:\\.\\*|\\.|[^\\\\^$.()[\\]{}|+?*])*$/;\n\n/** Translate a stream filter (regex-shaped or plain substring) into a\n * SQLite `GLOB` pattern.\n *\n * #1197: `GLOB` — not `LIKE` — because `GLOB` is **case-sensitive**,\n * matching PG's `~` and InMemory's `RegExp`. SQLite's `LIKE` is ASCII\n * case-insensitive, and its per-connection `PRAGMA case_sensitive_like`\n * override is unreliable under libsql's pooled connections (a\n * transaction and a follow-up read can land on different connections).\n * `GLOB` carries its case-sensitivity in the operator itself, so it is\n * correct regardless of which pooled connection runs the statement.\n *\n * Honors `^` / `$` anchors and converts `.*` → `*`, `.` → `?`.\n * Unanchored input gets `*` wildcards on both sides. `GLOB` has no\n * ESCAPE clause and `%` / `_` are ordinary literals under it — so no\n * escaping is needed. The portable grammar rejects `*`, `?`, and `[`\n * as literals (they are metacharacters), so a literal can never collide\n * with a `GLOB` wildcard.\n *\n * Only the portable filter grammar documented on\n * {@link QueryStreams.stream} is convertible: `^` / `$` anchors, `.`,\n * `.*`, and literal characters. Any richer regex syntax throws\n * {@link ValidationError} — a lossy approximation would silently\n * return wrong rows, the worst available failure mode when the filter\n * drives `reset` / `unblock`.\n *\n * Examples:\n * - `^abc$` → `abc` (exact)\n * - `^abc.*` → `abc*` (starts-with)\n * - `.*abc$` → `*abc` (ends-with)\n * - `abc` → `*abc*` (contains)\n * - `a.c` → `*a?c*` (single-char wildcard, contains)\n * - `^a_c$` → `a_c` (literal underscore — ordinary under GLOB)\n * - `(a|b)` → throws {@link ValidationError}\n *\n * @internal exported for testing\n */\nexport function streamPatternToGlob(input: string): string {\n let s = input;\n const start = s.startsWith(\"^\");\n const end = s.endsWith(\"$\");\n if (start) s = s.slice(1);\n if (end) s = s.slice(0, -1);\n if (!PORTABLE_BODY.test(s))\n throw new ValidationError(\n `stream filter pattern \"${input}\" — the SQLite adapter supports only the portable subset: \"^\" and \"$\" anchors, \".\" (any single character), \".*\" (any run), and literal characters. Escapes, character classes, grouping, alternation, and quantifiers cannot be converted to GLOB; use stream_exact/source_exact for literal names`,\n input,\n \"non-portable stream filter pattern\"\n );\n const tokens: string[] = [];\n if (!start) tokens.push(\"*\");\n for (let i = 0; i < s.length; i++) {\n const c = s[i];\n if (c === \".\") {\n if (s[i + 1] === \"*\") {\n tokens.push(\"*\");\n i++;\n } else tokens.push(\"?\");\n } else tokens.push(c);\n }\n if (!end) tokens.push(\"*\");\n // Collapse adjacent `*` wildcards — e.g. `^a.*` would otherwise yield\n // `a**`. GLOB treats `**` as `*`, but collapsing keeps the output\n // minimal and matches the pre-#1197 LIKE behavior.\n let out = \"\";\n let prev = \"\";\n for (const t of tokens) {\n if (t === \"*\" && prev === \"*\") continue;\n out += t;\n prev = t;\n }\n return out;\n}\n\n/**\n * SQLite event store adapter for [@rotorsoft/act](https://www.npmjs.com/package/@rotorsoft/act).\n *\n * Provides persistent event storage using SQLite via `@libsql/client`.\n * All write operations use transactions for ACID guarantees.\n *\n * SQLite is a single-writer store: concurrent write transactions raise\n * `SQLITE_BUSY` rather than serializing transparently, so it does not\n * support competing consumers the way PostgreSQL's `FOR UPDATE SKIP LOCKED`\n * does (the TCK leaves `concurrent_claim` off for this reason). The intended\n * model is a single drain worker per database file.\n *\n * **`Store.notify` is intentionally not implemented.** The notify hook is\n * a cross-process wake-up signal that lets a horizontally-scaled Act\n * deployment wake `settle()` immediately on remote commits. SQLite is\n * single-node by design — there is no remote writer to be notified of —\n * so the {@link Act} orchestrator falls back to the existing\n * debounce/poll path, which is correct for this topology.\n *\n * @example\n * ```typescript\n * import { store } from \"@rotorsoft/act\";\n * import { SqliteStore } from \"@rotorsoft/act-sqlite\";\n *\n * store(new SqliteStore({ url: \"file:myapp.db\" }));\n * await store().seed();\n * ```\n */\nexport class SqliteStore implements Store {\n private client: Client;\n /**\n * Memoized key resolver for the optional `pii_encryption` envelope.\n * Initialized in the constructor when encryption is configured;\n * `undefined` otherwise. The resolver caches the operator's key on\n * first use — rotation means restarting the store with a fresh\n * provider.\n */\n private readonly _resolve_pii_key: (() => Promise<Buffer>) | undefined;\n\n constructor(config: SqliteConfig) {\n this.client = createClient({\n url: resolve_url(config?.url),\n authToken: config.authToken,\n });\n this._resolve_pii_key = config.pii_encryption\n ? makeKeyResolver(config.pii_encryption)\n : undefined;\n }\n\n /**\n * Write-side pii encoder: encrypts when `pii_encryption` is configured,\n * stringifies for the TEXT column either way. `null` passes through so\n * `forget_pii` semantics survive intact (the row format skips the\n * column entirely on a `NULL` write).\n *\n * @internal\n */\n private async _stringify_pii_for_write(\n pii: Readonly<Record<string, unknown>> | null | undefined\n ): Promise<string | null> {\n if (pii == null) return null;\n if (this._resolve_pii_key) {\n return JSON.stringify(await encrypt(pii, this._resolve_pii_key));\n }\n return JSON.stringify(pii);\n }\n\n /**\n * Read-side pii decoder: parses the TEXT column, and when\n * `pii_encryption` is configured, transparently decrypts any value\n * that came back as a JSON string. Plaintext writes deserialize to\n * objects and pass through — which is what makes mixed-data\n * rollouts (some pre-encryption rows, some post-) read cleanly.\n *\n * @internal\n */\n private async _parse_pii_from_read(\n raw: unknown\n ): Promise<Record<string, unknown> | null> {\n if (raw == null) return null;\n // Revive dates like `data`/`meta` do (#1198/#1365). Base64 ciphertext\n // never matches the ISO-8601 pattern, so the encrypted branch below is\n // unaffected by the reviver here — it gets its own on the plaintext.\n const parsed = parse_json(raw as string);\n if (this._resolve_pii_key && typeof parsed === \"string\") {\n return (await decrypt(\n parsed,\n this._resolve_pii_key,\n dateReviver\n )) as Record<string, unknown>;\n }\n return parsed as Record<string, unknown>;\n }\n\n async seed() {\n await this.client.execute(\"PRAGMA journal_mode=WAL\");\n await this.client.execute(`\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream TEXT NOT NULL,\n version INTEGER NOT NULL,\n name TEXT NOT NULL,\n data TEXT NOT NULL,\n meta TEXT NOT NULL,\n created TEXT NOT NULL,\n pii TEXT,\n UNIQUE(stream, version)\n )\n `);\n // Migration for tables created before pii_isolation (#871).\n // libSQL surfaces \"duplicate column\" as an error, hence the\n // try/swallow — mirrors PG's `ADD COLUMN IF NOT EXISTS`. SQLite's\n // row format skips NULL columns, so events without sensitive\n // declarations pay zero extra bytes.\n try {\n await this.client.execute(\"ALTER TABLE events ADD COLUMN pii TEXT\");\n } catch {\n // already present\n }\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_events_stream ON events(stream)\"\n );\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_events_name ON events(name)\"\n );\n // Partial index over snapshot rows only, so the with_snaps \"resume at\n // the latest snapshot\" floor (MAX(id) WHERE stream=? AND\n // name='__snapshot__') is an O(log) lookup and costs nothing for\n // streams with no snapshot (the index has no rows for them).\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_events_snapshot ON events(stream, id) WHERE name = '__snapshot__'\"\n );\n // Complement of the snapshot index, and the one claim's has-work probe\n // seeks on: \"does this source stream have a non-snapshot event past the\n // watermark?\" (#1448). SQLite already probes per candidate with a\n // sargable `stream = ? AND id > ?`, so it only lacked the index — the\n // existing idx_events_stream is stream-only and still has to walk every\n // event of that stream. Partial over non-snapshot rows so the two\n // indexes partition the table rather than overlapping.\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_events_stream_id ON events(stream, id) WHERE name <> '__snapshot__'\"\n );\n await this.client.execute(`\n CREATE TABLE IF NOT EXISTS streams (\n stream TEXT PRIMARY KEY,\n source TEXT,\n at INTEGER NOT NULL DEFAULT -1,\n retry INTEGER NOT NULL DEFAULT -1,\n blocked INTEGER NOT NULL DEFAULT 0,\n error TEXT NOT NULL DEFAULT '',\n leased_by TEXT,\n leased_until TEXT,\n priority INTEGER NOT NULL DEFAULT 0,\n lane TEXT NOT NULL DEFAULT 'default',\n deferred_at TEXT,\n correlated_at INTEGER\n )\n `);\n // Migration for tables created before priority lanes (ACT-102).\n // libSQL surfaces \"duplicate column\" as an error, hence the\n // try/swallow — this mirrors PG's `ADD COLUMN IF NOT EXISTS`.\n try {\n await this.client.execute(\n \"ALTER TABLE streams ADD COLUMN priority INTEGER NOT NULL DEFAULT 0\"\n );\n } catch {\n // already present\n }\n // Migration for tables created before deferred reactions (#1090).\n try {\n await this.client.execute(\n \"ALTER TABLE streams ADD COLUMN deferred_at TEXT\"\n );\n } catch {\n // already present\n }\n // Migration for tables created before drain lanes (ACT-1103).\n try {\n await this.client.execute(\n \"ALTER TABLE streams ADD COLUMN lane TEXT NOT NULL DEFAULT 'default'\"\n );\n } catch {\n // already present\n }\n // Migration for tables created before the work set (#1485). Nullable on\n // purpose: NULL means \"unknown\", which routes the row to the legacy\n // has-work probe, so an upgraded install behaves exactly as it did and\n // migrates row by row as correlate marks each target.\n try {\n await this.client.execute(\n \"ALTER TABLE streams ADD COLUMN correlated_at INTEGER\"\n );\n } catch {\n // already present\n }\n // Correlate checkpoint (#1484). Its own single-row table rather than a\n // reserved subscription: a subscription row is counted by every\n // stream-scoped operator surface (prioritize / reset / unblock /\n // query_streams / blocked_streams) and would inflate those counts.\n await this.client.execute(`\n CREATE TABLE IF NOT EXISTS correlated (\n id INTEGER PRIMARY KEY CHECK (id = 0),\n at INTEGER NOT NULL DEFAULT -1\n )\n `);\n await this.client.execute(\n \"INSERT OR IGNORE INTO correlated (id) VALUES (0)\"\n );\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_streams_claim ON streams(blocked, priority DESC, at)\"\n );\n // The correlated set IS the index (#1485): it holds only streams with\n // work, so `claim` scans candidates instead of every subscription. A\n // stream leaves when `ack` advances `at` to `correlated_at`, and re-enters\n // when correlate raises the mark.\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_streams_correlated_at ON streams(lane, priority DESC, at) WHERE blocked = 0 AND at < correlated_at\"\n );\n // Lane filter index (ACT-1103).\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_streams_lane ON streams(lane)\"\n );\n }\n\n async drop() {\n await this.client.execute(\"DROP TABLE IF EXISTS events\");\n await this.client.execute(\"DROP TABLE IF EXISTS streams\");\n await this.client.execute(\"DROP TABLE IF EXISTS correlated\");\n }\n\n async dispose() {\n await Promise.resolve();\n this.client.close();\n }\n\n // --- commit: transaction + optimistic concurrency ---\n async commit<E extends Schemas>(\n stream: string,\n msgs: Message<E, keyof E>[],\n meta: EventMeta,\n expectedVersion?: number\n ): Promise<Committed<E, keyof E>[]> {\n const tx = await this.client.transaction(\"write\");\n // Hoisted so the catch can build a ConcurrencyError with the head\n // version on a (stream, version) unique collision (#1202).\n let current_version = -1;\n try {\n const version_row = await tx.execute({\n sql: \"SELECT COALESCE(MAX(version), -1) as v FROM events WHERE stream = ?\",\n args: [stream],\n });\n current_version = Number(version_row.rows[0].v);\n\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 const now = new Date().toISOString();\n const committed: Committed<E, keyof E>[] = [];\n let version = current_version + 1;\n\n for (const { name, data, pii } of msgs) {\n const pii_for_write = await this._stringify_pii_for_write(pii);\n const result = await tx.execute({\n sql: \"INSERT INTO events (stream, version, name, data, meta, created, pii) VALUES (?, ?, ?, ?, ?, ?, ?)\",\n args: [\n stream,\n version,\n name as string,\n JSON.stringify(data),\n JSON.stringify(meta),\n now,\n pii_for_write,\n ],\n });\n committed.push({\n id: Number(result.lastInsertRowid),\n stream,\n version,\n created: new Date(now),\n name,\n data,\n meta,\n ...(pii == null ? {} : { pii }),\n });\n version++;\n }\n\n await tx.commit();\n return committed;\n } catch (e) {\n await tx.rollback();\n // #1202: map the (stream, version) unique collision to\n // ConcurrencyError — the same signal PG raises from 23505 — so a\n // caller retries on the framework contract, not a raw libsql error.\n // This is the race where the version probe passed but a concurrent\n // INSERT (a second store on the same file, tooling, the act CLI)\n // beat this one to the row. Every other driver error is wrapped in\n // StoreError('commit') for parity with the adapter's other methods.\n if (e instanceof ConcurrencyError) throw e;\n if (is_unique_violation(e))\n throw new ConcurrencyError(\n stream,\n current_version,\n msgs as Message<Schemas, keyof Schemas>[],\n expectedVersion ?? -1\n );\n throw new StoreError(\"commit\", { cause: e });\n }\n }\n\n // --- query: read-only, no transaction needed ---\n async query<E extends Schemas>(\n callback: (event: Committed<E, keyof E>) => void,\n query?: Query\n ): Promise<number> {\n let sql = \"SELECT * FROM events WHERE 1=1\";\n const args: unknown[] = [];\n\n if (query?.stream) {\n if (query.stream_exact) {\n sql += \" AND stream = ?\";\n args.push(query.stream);\n } else {\n sql += \" AND stream GLOB ?\";\n args.push(streamPatternToGlob(query.stream));\n }\n }\n if (query?.names !== undefined) {\n // #1199: `names: []` means \"match no event names\". An empty\n // placeholder list yields `name IN ()` which SQLite evaluates as\n // always-false — matching the PG (`= ANY('{}')`) / InMemory\n // semantics. `!== undefined` (not truthiness) keeps that explicit.\n sql += ` AND name IN (${query.names.map(() => \"?\").join(\",\")})`;\n args.push(...query.names);\n }\n if ((query as any)?.correlation) {\n sql += \" AND json_extract(meta, '$.correlation') = ?\";\n args.push((query as any).correlation);\n }\n if (query?.after !== undefined) {\n sql += \" AND id > ?\";\n args.push(query.after);\n } else if (query?.with_snaps && query.stream_exact && query.stream) {\n // Resume at the latest snapshot for this stream so pre-snapshot\n // events aren't scanned. No snapshot → MAX is NULL → -1 → full\n // stream. An explicit `after` (above) wins. The orchestrator only sets\n // `with_snaps` for an unbounded current-state load — it suppresses the\n // flag under any `asOf` bound (RFC 1274) — so the floor never needs to\n // re-check `before`/`created_*`/`limit` here.\n sql +=\n \" AND id >= (SELECT COALESCE(MAX(id), -1) FROM events WHERE stream = ? AND name = '__snapshot__')\";\n args.push(query.stream);\n }\n if (query?.before !== undefined) {\n sql += \" AND id < ?\";\n args.push(query.before);\n }\n if (query?.created_after) {\n sql += \" AND created > ?\";\n args.push(query.created_after.toISOString());\n }\n if (query?.created_before) {\n sql += \" AND created < ?\";\n args.push(query.created_before.toISOString());\n }\n if (!query?.with_snaps) {\n sql += \" AND name != '__snapshot__'\";\n }\n\n sql += query?.backward ? \" ORDER BY id DESC\" : \" ORDER BY id ASC\";\n\n if (query?.limit) {\n sql += \" LIMIT ?\";\n args.push(query.limit);\n }\n\n const result = await this.client.execute({ sql, args: args as any[] });\n let count = 0;\n\n for (const row of result.rows) {\n const pii_value = await this._parse_pii_from_read(row.pii);\n await Promise.resolve(\n callback({\n id: Number(row.id),\n stream: row.stream as string,\n version: Number(row.version),\n created: new Date(row.created as string),\n name: row.name as string,\n data: parse_json(row.data as string) as any,\n meta: parse_json(row.meta as string) as any,\n pii: pii_value,\n })\n );\n count++;\n }\n\n return count;\n }\n\n // --- subscribe: idempotent INSERT OR IGNORE (= PG ON CONFLICT DO NOTHING)\n // plus a UPDATE pass to keep the *max* priority across reactions\n // targeting the same stream (ACT-102). Operator overrides go\n // through `prioritize()` instead.\n async subscribe(streams: SubscribeInput[], correlated_at?: number) {\n // Fail loud at registration for a claim source this adapter cannot\n // match: libsql has no `REGEXP`, and the portable GLOB grammar cannot\n // express alternation/grouping. A pattern source outside the portable\n // subset (e.g. `^(A|B)$`) would otherwise silently never claim its\n // stream, so `streamPatternToGlob` throws `ValidationError` here —\n // pointing operators to InMemory/PG for full regex claim sources —\n // before any row is written. Literal sources skip the check entirely.\n for (const { source } of streams) {\n if (source !== undefined && !is_literal_source(source))\n streamPatternToGlob(source);\n }\n const tx = await this.client.transaction(\"write\");\n try {\n let subscribed = 0;\n for (const {\n stream,\n source,\n priority = 0,\n lane = \"default\",\n correlated_at,\n } of streams) {\n const inserted = await tx.execute({\n sql: \"INSERT OR IGNORE INTO streams (stream, source, priority, lane, retry) VALUES (?, ?, ?, ?, -1)\",\n args: [stream, source ?? null, priority, lane],\n });\n if (inserted.rowsAffected > 0) {\n subscribed++;\n } else {\n // `WHERE priority < ?` is the whole max rule, for every value on\n // the number line. An extra `priority > 0` gate around this used to\n // skip the merge for non-positive priorities, which made a stored\n // negative priority unraisable: an operator's `prioritize(-5)`\n // survived every subsequent boot instead of being restored to the\n // declared priority by the restart-driven re-subscribe (#1445).\n await tx.execute({\n sql: \"UPDATE streams SET priority = ? WHERE stream = ? AND priority < ?\",\n args: [priority, stream, priority],\n });\n // ACT-1103: current subscribe wins on lane.\n await tx.execute({\n sql: \"UPDATE streams SET lane = ? WHERE stream = ? AND lane <> ?\",\n args: [lane, stream, lane],\n });\n }\n // The work mark never regresses (#1485). `WHERE correlated_at IS\n // NULL OR correlated_at < ?` is the whole rule: the NULL arm is first\n // mark (a comparison against NULL is unknown, so the predicate alone\n // would skip it), and it holds for every value including zero and\n // negatives. Written here rather than in the INSERT above so an\n // unmarked subscribe never names the column — a table that predates\n // it, and that `seed()` has not migrated, keeps working.\n if (correlated_at !== undefined)\n await tx.execute({\n sql: \"UPDATE streams SET correlated_at = ? WHERE stream = ? AND (correlated_at IS NULL OR correlated_at < ?)\",\n args: [correlated_at, stream, correlated_at],\n });\n }\n const wm = await tx.execute(\n \"SELECT COALESCE(MAX(at), -1) as w FROM streams\"\n );\n // The correlate checkpoint is written by its own producer, in the call\n // correlate already makes (#1484). MAX keeps it monotonic.\n if (correlated_at !== undefined)\n await tx.execute({\n sql: \"UPDATE correlated SET at = MAX(at, ?) WHERE id = 0\",\n args: [correlated_at],\n });\n // Watermark and checkpoint together — correlate needs both.\n const cp = await tx.execute(\"SELECT at FROM correlated WHERE id = 0\");\n await tx.commit();\n return {\n subscribed,\n watermark: Number(wm.rows[0].w),\n // `seed()` always inserts the singleton row, so this read cannot\n // come back empty — no defensive fallback to leave untested.\n correlated_at: Number(cp.rows[0].at),\n };\n } catch (e) {\n await tx.rollback();\n throw new StoreError(\"subscribe\", { cause: e });\n }\n }\n\n // --- claim: write transaction. Single-writer: concurrent claims from\n // separate connections raise SQLITE_BUSY, so this assumes one drain\n // worker per database file (see class doc; TCK `concurrent_claim` off).\n async claim(\n lagging: number,\n leading: number,\n by: string,\n millis: number,\n lane?: string\n ) {\n const tx = await this.client.transaction(\"write\");\n try {\n const now = new Date().toISOString();\n\n const lane_clause = lane !== undefined ? \" AND lane = ?\" : \"\";\n // `correlated_at IS NULL OR at < correlated_at` (#1485) drops marked\n // whose watermark has caught up before they ever reach the probe loop\n // below. NULL means \"unknown\", so those rows pass through to the legacy\n // probe — an install that predates the column behaves exactly as it did.\n const result = await tx.execute({\n sql: `SELECT stream, source, at, priority, lane, correlated_at FROM streams\n WHERE blocked = 0 AND (leased_until IS NULL OR leased_until <= ?)\n AND (deferred_at IS NULL OR deferred_at <= ?)${lane_clause}\n AND (correlated_at IS NULL OR at < correlated_at)\n ORDER BY priority DESC, at ASC`,\n args: lane !== undefined ? [now, now, lane] : [now, now],\n });\n\n const candidates: {\n stream: string;\n source: string | undefined;\n at: number;\n priority: number;\n lane: string;\n }[] = [];\n for (const row of result.rows) {\n const stream = row.stream as string;\n const source = row.source as string | null;\n const at = Number(row.at);\n\n // A marked row is already known to have work — the SQL above kept\n // only `at < correlated_at` — so it skips the probe entirely. This is\n // the N+1 the work set exists to remove; step 5 deletes the arm below.\n if (row.correlated_at !== null) {\n candidates.push({\n stream,\n source: source ?? undefined,\n at,\n priority: Number(row.priority),\n lane: row.lane as string,\n });\n continue;\n }\n\n let has_events: boolean;\n if (source) {\n // Has-work probe. A literal `source` (no regex metacharacter)\n // matches by equality — index-friendly, and exact so \"s1\" never\n // claims \"s12\". A portable pattern (anchors, `.`, `.*`) matches\n // via the same GLOB translation the StreamFilter surfaces use.\n // Non-portable patterns (alternation/grouping) never reach here:\n // `subscribe` rejects them up front, so `streamPatternToGlob`\n // below only sees the portable subset.\n const check = is_literal_source(source)\n ? await tx.execute({\n sql: `SELECT 1 FROM events WHERE id > ? AND name != '__snapshot__' AND stream = ? LIMIT 1`,\n args: [at, source],\n })\n : await tx.execute({\n sql: `SELECT 1 FROM events WHERE id > ? AND name != '__snapshot__' AND stream GLOB ? LIMIT 1`,\n args: [at, streamPatternToGlob(source)],\n });\n has_events = check.rows.length > 0;\n } else {\n const check = await tx.execute({\n sql: `SELECT 1 FROM events WHERE id > ? AND name != '__snapshot__' LIMIT 1`,\n args: [at],\n });\n has_events = check.rows.length > 0;\n }\n\n if (has_events) {\n candidates.push({\n stream,\n source: source ?? undefined,\n at,\n priority: Number(row.priority),\n lane: row.lane as string,\n });\n }\n }\n\n // Dual frontier: lagging (priority DESC, watermark ASC — ACT-102)\n // + leading (newest first). The candidates list arrives sorted\n // by `priority DESC, at ASC` from the SELECT above. The lagging\n // budget is split (ACT-1223): the priority portion takes the first\n // `lagging - fair` candidates in that order; a fairness reserve then\n // fills `fair` more by pure `at ASC` (priority ignored), excluding\n // the ones already picked, so a default-priority lagging stream is\n // never starved out by sustained higher-priority load. With all\n // priorities equal both portions order by `at`, a no-op split.\n const fair = lagging >= 2 ? Math.max(1, Math.floor(lagging / 4)) : 0;\n const priority_slice = candidates.slice(0, lagging - fair);\n const picked = new Set(priority_slice.map((c) => c.stream));\n const fair_slice = [...candidates]\n .sort((a, b) => a.at - b.at)\n .filter((c) => !picked.has(c.stream))\n .slice(0, fair);\n const lag = [...priority_slice, ...fair_slice].map((c) => ({\n ...c,\n lagging: true,\n }));\n const lead = [...candidates]\n .sort((a, b) => b.at - a.at)\n .slice(0, leading)\n .map((c) => ({ ...c, lagging: false }));\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\n const leases: Lease[] = [];\n const until = new Date(Date.now() + millis).toISOString();\n for (const row of combined) {\n const updated = await tx.execute({\n sql: \"UPDATE streams SET leased_by = ?, leased_until = ?, retry = retry + 1 WHERE stream = ? RETURNING retry\",\n args: [by, until, row.stream],\n });\n leases.push({\n stream: row.stream,\n source: row.source,\n at: row.at,\n by,\n retry: Number(updated.rows[0].retry),\n lagging: row.lagging,\n lane: row.lane,\n });\n }\n\n await tx.commit();\n return leases;\n } catch (e) {\n await tx.rollback();\n throw new StoreError(\"claim\", { cause: e });\n }\n }\n\n // --- ack: transaction + ownership check (= PG WHERE leased_by) ---\n async ack(leases: Lease[]) {\n const tx = await this.client.transaction(\"write\");\n try {\n // The whole batch finalizes in one transaction, so acks and defer\n // schedules land all-or-nothing per the Store.ack contract. Every\n // entry advances the watermark to its `at` (the last event handled\n // this cycle); an entry without `due` also clears retry + schedule,\n // while an entry with `due` additionally sets the schedule and the\n // entry's own `retry` — advance and defer are independent legs\n // (#1278), so a partial-progress defer keeps the handled prefix. The\n // bound schedule doubles as the ack-vs-defer discriminator — NULL\n // means ack. An explicit defer passes retry -1 (not a failure); a\n // backoff retry passes the climbing counter so the budget keeps\n // accruing across windows (#1262). Only acked entries are returned.\n const result: Lease[] = [];\n for (const l of leases) {\n const due = l.due !== undefined ? new Date(l.due).toISOString() : null;\n const r = await tx.execute({\n // RETURNING the post-ack `retry`/`source`/`lane` from the row so the\n // returned lease reflects the authoritative state, not the caller's\n // pre-ack echo (#1347). A non-due ack sets `retry = -1`; pushing the\n // input lease verbatim leaked the claim-incremented value and\n // diverged from PG (RETURNING s.retry) and InMemory (which return the\n // reset -1). `at`/`by`/`lagging` still come from the input lease,\n // matching both other adapters.\n sql: `UPDATE streams\n SET at = ?,\n deferred_at = ?,\n leased_by = NULL, leased_until = NULL,\n retry = CASE WHEN ? IS NULL THEN -1 ELSE ? END\n WHERE stream = ? AND leased_by = ?\n RETURNING source, retry, lane`,\n args: [l.at, due, due, l.retry, l.stream, l.by],\n });\n if (due === null && r.rows.length > 0) {\n const row = r.rows[0];\n result.push({\n stream: l.stream,\n source: (row.source as string | null) ?? undefined,\n at: l.at,\n by: l.by,\n retry: Number(row.retry),\n lagging: l.lagging,\n lane: row.lane as string,\n });\n }\n }\n await tx.commit();\n return result;\n } catch (e) {\n await tx.rollback();\n throw new StoreError(\"ack\", { cause: e });\n }\n }\n\n // --- block: transaction + ownership + idempotent (= PG) ---\n async block(leases: BlockedLease[]) {\n const tx = await this.client.transaction(\"write\");\n try {\n const result: BlockedLease[] = [];\n for (const l of leases) {\n const r = await tx.execute({\n // RETURNING the post-block row so the returned lease reflects the\n // authoritative state, not the caller's echo (#1382, the `block`\n // twin of #1347). A block leaves the watermark alone — the failing\n // event must be retried, not skipped — but `run_drain_cycle` hands\n // us a lease whose `at` was fast-forwarded to the fetch ceiling, so\n // pushing the input verbatim reported a position the row never\n // held. `by`/`error`/`lagging` still come from the input lease,\n // matching PG (`RETURNING s.stream, s.source, s.at, i.by, ...`).\n sql: `UPDATE streams SET blocked = 1, error = ?, deferred_at = NULL\n WHERE stream = ? AND leased_by = ? AND blocked = 0\n RETURNING source, at, retry, lane`,\n args: [l.error, l.stream, l.by],\n });\n if (r.rows.length > 0) {\n const row = r.rows[0];\n result.push({\n stream: l.stream,\n source: (row.source as string | null) ?? undefined,\n at: Number(row.at),\n by: l.by,\n retry: Number(row.retry),\n error: l.error,\n lagging: l.lagging,\n lane: row.lane as string,\n });\n }\n }\n await tx.commit();\n return result;\n } catch (e) {\n await tx.rollback();\n throw new StoreError(\"block\", { cause: e });\n }\n }\n\n // --- defer: hold streams out of claim until deferred_at (#1090) ---\n // Persisted as an ISO string (like leased_until) so claim's\n // `deferred_at <= now` comparison is a lexicographic string compare.\n // Cleared by ack/block/reset/unblock. See {@link Store.defer}.\n async defer(input: string[] | StreamFilter, deferred_at: number) {\n const at_iso = new Date(deferred_at).toISOString();\n const tx = await this.client.transaction(\"write\");\n try {\n let count = 0;\n if (Array.isArray(input)) {\n // De-dup so a repeated name counts once, matching PG's set-based\n // `WHERE stream = ANY(...)` (#1360).\n for (const stream of new Set(input)) {\n const r = await tx.execute({\n sql: `UPDATE streams SET deferred_at = ?, retry = -1 WHERE stream = ?`,\n args: [at_iso, stream],\n });\n count += r.rowsAffected;\n }\n } else {\n const { clause, args } = this._filter_clause(input);\n const r = await tx.execute({\n sql: `UPDATE streams SET deferred_at = ?, retry = -1 WHERE ${clause}`,\n args: [at_iso, ...args] as any[],\n });\n count = r.rowsAffected;\n }\n await tx.commit();\n return count;\n } catch (e) {\n await tx.rollback();\n throw new StoreError(\"defer\", { cause: e });\n }\n }\n\n /**\n * Translate a {@link StreamFilter} to a SQLite `WHERE` clause fragment\n * plus positional args. Returns `\"1\"` (always true) when empty so\n * callers can compose it unconditionally.\n */\n private _filter_clause(filter: StreamFilter): {\n clause: string;\n args: unknown[];\n } {\n const conditions: string[] = [];\n const args: unknown[] = [];\n if (filter.stream !== undefined) {\n if (filter.stream_exact) {\n conditions.push(\"stream = ?\");\n args.push(filter.stream);\n } else {\n conditions.push(\"stream GLOB ?\");\n args.push(streamPatternToGlob(filter.stream));\n }\n }\n if (filter.source !== undefined) {\n conditions.push(\"source IS NOT NULL\");\n if (filter.source_exact) {\n conditions.push(\"source = ?\");\n args.push(filter.source);\n } else {\n conditions.push(\"source GLOB ?\");\n args.push(streamPatternToGlob(filter.source));\n }\n }\n if (filter.blocked !== undefined) {\n conditions.push(\"blocked = ?\");\n args.push(filter.blocked ? 1 : 0);\n }\n if (filter.lane !== undefined) {\n conditions.push(\"lane = ?\");\n args.push(filter.lane);\n }\n return { clause: conditions.length ? conditions.join(\" AND \") : \"1\", args };\n }\n\n // --- reset: transactional, accepts names or filter ---\n async reset(input: string[] | StreamFilter) {\n const set_clause = `SET at = -1, retry = -1, blocked = 0, error = '',\n leased_by = NULL, leased_until = NULL, deferred_at = NULL`;\n const tx = await this.client.transaction(\"write\");\n try {\n let count = 0;\n if (Array.isArray(input)) {\n // De-dup so a repeated name counts once, matching PG's set-based\n // `WHERE stream = ANY(...)` (#1360).\n for (const stream of new Set(input)) {\n const r = await tx.execute({\n sql: `UPDATE streams ${set_clause} WHERE stream = ?`,\n args: [stream],\n });\n count += r.rowsAffected;\n }\n } else {\n const { clause, args } = this._filter_clause(input);\n const r = await tx.execute({\n sql: `UPDATE streams ${set_clause} WHERE ${clause}`,\n args: args as any[],\n });\n count = r.rowsAffected;\n }\n await tx.commit();\n return count;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- unblock: clear blocked + retry + lease without touching watermark ---\n // `retry = -1` so claim's post-bump returns retry=0 (first attempt),\n // matching the InMemoryStore convention.\n async unblock(input: string[] | StreamFilter) {\n const set_clause = `SET retry = -1, blocked = 0, error = '',\n leased_by = NULL, leased_until = NULL, deferred_at = NULL`;\n const tx = await this.client.transaction(\"write\");\n try {\n let count = 0;\n if (Array.isArray(input)) {\n for (const stream of input) {\n const r = await tx.execute({\n sql: `UPDATE streams ${set_clause}\n WHERE stream = ? AND blocked = 1`,\n args: [stream],\n });\n count += r.rowsAffected;\n }\n } else {\n // Filter form: force blocked = true regardless of what the\n // caller passed.\n const { clause, args } = this._filter_clause({\n ...input,\n blocked: true,\n });\n const r = await tx.execute({\n sql: `UPDATE streams ${set_clause} WHERE ${clause}`,\n args: args as any[],\n });\n count = r.rowsAffected;\n }\n await tx.commit();\n return count;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- query_streams: read-only introspection with filters ---\n async query_streams(\n callback: (position: StreamPosition) => void,\n query?: QueryStreams\n ): Promise<QueryStreamsResult> {\n const limit = query?.limit ?? 100;\n let sql =\n \"SELECT stream, source, at, retry, blocked, error, leased_by, leased_until, priority, lane, deferred_at FROM streams WHERE 1=1\";\n const args: unknown[] = [];\n\n if (query?.stream !== undefined) {\n if (query.stream_exact) {\n sql += \" AND stream = ?\";\n args.push(query.stream);\n } else {\n sql += \" AND stream GLOB ?\";\n args.push(streamPatternToGlob(query.stream));\n }\n }\n if (query?.source !== undefined) {\n sql += \" AND source IS NOT NULL\";\n if (query.source_exact) {\n sql += \" AND source = ?\";\n args.push(query.source);\n } else {\n sql += \" AND source GLOB ?\";\n args.push(streamPatternToGlob(query.source));\n }\n }\n if (query?.blocked !== undefined) {\n sql += \" AND blocked = ?\";\n args.push(query.blocked ? 1 : 0);\n }\n if (query?.lane !== undefined) {\n sql += \" AND lane = ?\";\n args.push(query.lane);\n }\n if (query?.after !== undefined) {\n sql += \" AND stream > ?\";\n args.push(query.after);\n }\n sql += \" ORDER BY stream LIMIT ?\";\n args.push(limit);\n\n const [streamsResult, maxResult] = await Promise.all([\n this.client.execute({ sql, args: args as any[] }),\n this.client.execute(\"SELECT COALESCE(MAX(id), -1) AS m FROM events\"),\n ]);\n\n let count = 0;\n for (const row of streamsResult.rows) {\n const leased_until = row.leased_until as string | null;\n // Persisted as an ISO string (like leased_until); surface as ms since\n // epoch (#1221) so the cold-start re-seed re-arms at the due-time.\n const deferred_at = row.deferred_at as string | null;\n callback({\n stream: row.stream as string,\n source: (row.source as string | null) ?? undefined,\n at: Number(row.at),\n retry: Number(row.retry),\n blocked: Number(row.blocked) === 1,\n error: row.error as string,\n priority: Number(row.priority),\n leased_by: (row.leased_by as string | null) ?? undefined,\n leased_until: leased_until ? new Date(leased_until) : undefined,\n lane: row.lane as string,\n deferred_at: deferred_at ? new Date(deferred_at).getTime() : undefined,\n });\n count++;\n }\n\n return { maxEventId: Number(maxResult.rows[0].m), count };\n }\n\n /**\n * Per-stream aggregated stats — see {@link Store.query_stats}.\n *\n * Two code paths (mirrors the PostgresStore strategy):\n *\n * - **Heads-only path** (no `count`, no `names`): one or two queries\n * using `ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version\n * DESC|ASC)` (SQLite lacks PG's `DISTINCT ON`). Window function +\n * `WHERE rn = 1` materializes the head (or tail) per stream from\n * the `(stream, version)` unique index. Parallel `Promise.all` when\n * tail is requested.\n *\n * - **Full-scan path** (`count` or `names` set): one CTE materializes\n * the filtered events, then `GROUP BY stream, name` →\n * `json_group_object(name, n)` for the names map plus `SUM(n)` for\n * count. Heads (and tails when requested) come from the same scan.\n *\n * SQLite specifics:\n * - `data` and `meta` are stored as TEXT (JSON-encoded); the reader\n * JSON-parses them when materializing the {@link Committed} rows.\n * - `blocked` is stored as 0/1 integer; the filter converts.\n * - Array input expands to a placeholder list (`IN (?, ?, ...)`)\n * since SQLite has no native array type.\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 const exclude = options?.exclude ?? [];\n const want_tail = options?.tail ?? false;\n const want_count = options?.count ?? false;\n const want_names = options?.names ?? false;\n const before = options?.before;\n const after = options?.after;\n const limit = options?.limit;\n const full_scan = want_count || want_names;\n\n if (Array.isArray(input) && input.length === 0) {\n return new Map<string, StreamStats<E>>();\n }\n\n // Build WHERE clause + positional args. Subscription-level filters\n // (source, blocked) are intentionally not accepted — events live in\n // the events table; subscription state in the streams table. For\n // \"stats for blocked subscriptions\" callers compose with\n // query_streams. So no JOIN here.\n const where: string[] = [];\n const args: unknown[] = [];\n\n if (Array.isArray(input)) {\n const placeholders = input.map(() => \"?\").join(\",\");\n where.push(`e.stream IN (${placeholders})`);\n args.push(...input);\n } else if (input.stream !== undefined) {\n if (input.stream_exact) {\n where.push(`e.stream = ?`);\n args.push(input.stream);\n } else {\n where.push(`e.stream GLOB ?`);\n args.push(streamPatternToGlob(input.stream));\n }\n }\n if (exclude.length) {\n const placeholders = exclude.map(() => \"?\").join(\",\");\n where.push(`e.name NOT IN (${placeholders})`);\n args.push(...exclude);\n }\n if (before !== undefined) {\n where.push(`e.id < ?`);\n args.push(before);\n }\n // Keyset pagination cursor (#1010): exclusive — return only streams\n // whose name sorts strictly after `after`. Applied at the event level\n // so the per-stream head/agg rows downstream only cover the page.\n if (after !== undefined) {\n where.push(`e.stream > ?`);\n args.push(after);\n }\n\n const from_clause = `events e`;\n // Always emit a WHERE clause — `WHERE 1=1` short-circuits the\n // empty-filter case without a conditional branch on the generation\n // side. SQLite optimizes the trivial predicate out.\n const where_clause = `WHERE ${where.length ? where.join(\" AND \") : \"1=1\"}`;\n\n return full_scan\n ? this._query_stats_full_scan<E>(\n from_clause,\n where_clause,\n args,\n want_tail,\n want_count,\n want_names,\n limit\n )\n : this._query_stats_heads_only<E>(\n from_clause,\n where_clause,\n args,\n want_tail,\n limit\n );\n }\n\n /**\n * Cheap path — head (and optional tail) via ROW_NUMBER() over the\n * `(stream, version)` unique index. Parallel queries when tail set.\n */\n private async _query_stats_heads_only<E extends Schemas>(\n from_clause: string,\n where_clause: string,\n args: unknown[],\n want_tail: boolean,\n limit?: number\n ): Promise<Map<string, StreamStats<E>>> {\n // `query_stats` is an operator-introspection surface with no actor\n // context and no disclosure gate, so it never carries pii — heads/tails\n // return the event shape without the pii sidecar, matching PostgresStore\n // and InMemoryStore (#1294).\n const cols = `e.id, e.stream, e.version, e.name, e.data, e.created, e.meta`;\n // Keyset pagination (#1010): order heads by stream ascending so the\n // caller can use the last key as the next `after` cursor; cap with\n // LIMIT when set (unbounded otherwise).\n const limit_clause = limit !== undefined ? \" LIMIT ?\" : \"\";\n const head_sql = `SELECT * FROM (\n SELECT ${cols}, ROW_NUMBER() OVER (PARTITION BY e.stream ORDER BY e.version DESC) AS rn\n FROM ${from_clause}\n ${where_clause}\n ) WHERE rn = 1 ORDER BY stream${limit_clause}`;\n const head_args = limit !== undefined ? [...args, limit] : args;\n // The tail query shares the same WHERE; when paginating it must cover\n // exactly the limited head stream set, so it joins against the same\n // ordered+limited heads subquery rather than re-scanning all streams.\n const tail_sql = want_tail\n ? `SELECT t.* FROM (\n SELECT ${cols}, ROW_NUMBER() OVER (PARTITION BY e.stream ORDER BY e.version ASC) AS rn\n FROM ${from_clause}\n ${where_clause}\n ) t\n WHERE t.rn = 1 AND t.stream IN (\n SELECT stream FROM (\n SELECT e.stream, ROW_NUMBER() OVER (PARTITION BY e.stream ORDER BY e.version DESC) AS rn\n FROM ${from_clause}\n ${where_clause}\n ) WHERE rn = 1 ORDER BY stream${limit_clause}\n )`\n : null;\n const tail_args =\n limit !== undefined ? [...args, ...args, limit] : [...args, ...args];\n\n const [headRes, tailRes] = await Promise.all([\n this.client.execute({ sql: head_sql, args: head_args as any[] }),\n tail_sql\n ? this.client.execute({ sql: tail_sql, args: tail_args as any[] })\n : null,\n ]);\n\n const to_committed = (\n row: Record<string, unknown>\n ): Committed<E, keyof E> =>\n ({\n id: Number(row.id),\n stream: row.stream as string,\n version: Number(row.version),\n name: row.name as string,\n data: parse_json(row.data as string) as any,\n meta: parse_json(row.meta as string) as any,\n created: new Date(row.created as string),\n }) as Committed<E, keyof E>;\n\n const out = new Map<string, StreamStats<E>>();\n for (const row of headRes.rows) {\n out.set(row.stream as string, {\n head: to_committed(row as Record<string, unknown>),\n });\n }\n if (tailRes) {\n for (const row of tailRes.rows) {\n // Head and tail share the same WHERE, so any stream returning\n // a tail must also have returned a head — no null check needed.\n (\n out.get(row.stream as string) as {\n head: Committed<E, keyof E>;\n tail?: Committed<E, keyof E>;\n }\n ).tail = to_committed(row as Record<string, unknown>);\n }\n }\n return out;\n }\n\n /**\n * Full-scan path — one CTE-based query with per-stream `COUNT(*)` and\n * `json_group_object(name, n)`. Heads (and optional tails) ride free\n * on the same scan.\n */\n private async _query_stats_full_scan<E extends Schemas>(\n from_clause: string,\n where_clause: string,\n args: unknown[],\n want_tail: boolean,\n want_count: boolean,\n want_names: boolean,\n limit?: number\n ): Promise<Map<string, StreamStats<E>>> {\n const tail_cte = want_tail\n ? `, tails AS (\n SELECT * FROM (\n SELECT *, ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version ASC) AS rn FROM ef\n ) WHERE rn = 1\n )`\n : \"\";\n const tail_join = want_tail\n ? `LEFT JOIN tails t ON t.stream = h.stream`\n : \"\";\n const tail_cols = want_tail\n ? `, t.id AS t_id, t.stream AS t_stream, t.version AS t_version,\n t.name AS t_name, t.data AS t_data, t.created AS t_created, t.meta AS t_meta`\n : \"\";\n\n // No pii column: `query_stats` heads/tails are pii-free (#1294 — see\n // `_query_stats_heads_only`).\n const sql = `\n WITH ef AS (\n SELECT e.id, e.stream, e.version, e.name, e.data, e.created, e.meta\n FROM ${from_clause}\n ${where_clause}\n ),\n agg AS (\n SELECT stream,\n SUM(n) AS cnt,\n json_group_object(name, n) AS names\n FROM (\n SELECT stream, name, COUNT(*) AS n\n FROM ef\n GROUP BY stream, name\n )\n GROUP BY stream\n ),\n heads AS (\n SELECT * FROM (\n SELECT *, ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version DESC) AS rn FROM ef\n ) WHERE rn = 1\n ORDER BY stream${limit !== undefined ? \" LIMIT ?\" : \"\"}\n )\n ${tail_cte}\n SELECT\n h.id, h.stream, h.version, h.name, h.data, h.created, h.meta,\n a.cnt AS agg_count,\n a.names AS agg_names\n ${tail_cols}\n FROM heads h\n LEFT JOIN agg a ON a.stream = h.stream\n ${tail_join}\n ORDER BY h.stream\n `;\n\n // Keyset pagination (#1010): the `heads` CTE is ordered by stream and\n // capped with LIMIT (when set); the final SELECT re-asserts stream\n // order so the returned Map's key order is the next cursor. The LIMIT\n // arg binds last when present (unbounded otherwise).\n const full_args = limit !== undefined ? [...args, limit] : args;\n const res = await this.client.execute({ sql, args: full_args as any[] });\n\n const to_committed = (\n id: unknown,\n stream: unknown,\n version: unknown,\n name: unknown,\n data: unknown,\n meta: unknown,\n created: unknown\n ): Committed<E, keyof E> =>\n ({\n id: Number(id),\n stream: stream as string,\n version: Number(version),\n name: name as string,\n data: parse_json(data as string) as any,\n meta: parse_json(meta as string) as any,\n created: new Date(created as string),\n }) as Committed<E, keyof E>;\n\n const out = new Map<string, StreamStats<E>>();\n for (const row of res.rows) {\n const r = row as unknown as Record<string, unknown>;\n const stats: {\n head: Committed<E, keyof E>;\n tail?: Committed<E, keyof E>;\n count?: number;\n names?: Record<string, number>;\n } = {\n head: to_committed(\n r.id,\n r.stream,\n r.version,\n r.name,\n r.data,\n r.meta,\n r.created\n ),\n };\n if (want_tail && r.t_id !== null && r.t_id !== undefined) {\n stats.tail = to_committed(\n r.t_id,\n r.t_stream,\n r.t_version,\n r.t_name,\n r.t_data,\n r.t_meta,\n r.t_created\n );\n }\n if (want_count) stats.count = Number(r.agg_count);\n // `agg_names` is non-null when this row exists: heads and agg are\n // both built from the same `ef` CTE, so any stream in heads has\n // at least one matching event and `json_group_object` returns a\n // JSON string (never null) for that group.\n if (want_names) stats.names = JSON.parse(r.agg_names as string);\n out.set(r.stream as string, stats as StreamStats<E>);\n }\n return out;\n }\n\n // --- prioritize: bulk priority update with filter (ACT-102) ---\n async prioritize(filter: StreamFilter, priority: number): Promise<number> {\n const { clause, args: filterArgs } = this._filter_clause(filter);\n // libSQL `?` placeholders are positional and NOT reusable, so we\n // bind `priority` twice: once for SET, once for the no-op skip\n // in WHERE.\n const sql = `UPDATE streams SET priority = ?\n WHERE priority <> ? AND ${clause}`;\n const result = await this.client.execute({\n sql,\n args: [priority, priority, ...filterArgs] as any[],\n });\n return result.rowsAffected;\n }\n\n // --- truncate: transactional delete + seed ---\n // Windowed targets (`before` set) prune the prefix below the closest safe\n // `__snapshot__` instead — no seed, subscriptions untouched, no-op when no\n // snapshot qualifies.\n async truncate(\n targets: Array<{\n stream: string;\n snapshot?: Record<string, unknown>;\n meta?: EventMeta;\n before?: Date;\n max_id?: number;\n }>\n ) {\n const result = new Map<\n string,\n {\n deleted: number;\n committed: Committed<Schemas, keyof Schemas>;\n before?: Date;\n }\n >();\n\n const tx = await this.client.transaction(\"write\");\n try {\n for (const { stream, before, max_id } of targets) {\n if (before === undefined) continue;\n // Closest safe boundary: latest snapshot older than the cutoff\n // and at/below the consumer watermark cap. ISO-8601 strings\n // compare lexicographically, so `created < ?` works on the text\n // column. No qualifying snapshot → no-op, absent from the result.\n const snap_row = await tx.execute({\n sql: `SELECT id, stream, version, name, data, meta, created\n FROM events\n WHERE stream = ? AND name = '__snapshot__' AND created < ?\n AND (? IS NULL OR id <= ?)\n ORDER BY id DESC LIMIT 1`,\n args: [stream, before.toISOString(), max_id ?? null, max_id ?? null],\n });\n if (!snap_row.rows.length) continue;\n const row = snap_row.rows[0];\n const boundary_id = Number(row.id);\n const del = await tx.execute({\n sql: \"DELETE FROM events WHERE stream = ? AND id < ?\",\n args: [stream, boundary_id],\n });\n result.set(stream, {\n deleted: del.rowsAffected,\n committed: {\n id: boundary_id,\n stream,\n version: Number(row.version),\n created: new Date(row.created as string),\n name: row.name as string,\n data: parse_json(row.data as string) as any,\n meta: parse_json(row.meta as string) as any,\n },\n before,\n });\n }\n for (const { stream, snapshot, meta, before } of targets) {\n if (before !== undefined) continue;\n const count_row = await tx.execute({\n sql: \"SELECT COUNT(*) as c FROM events WHERE stream = ?\",\n args: [stream],\n });\n const deleted = Number(count_row.rows[0].c);\n await tx.execute({\n sql: \"DELETE FROM events WHERE stream = ?\",\n args: [stream],\n });\n // A restart target carries a seed snapshot: the stream lives on, so\n // its subscription row must survive. Deleting it silently stops\n // reactions whose target is named after the stream — the documented\n // per-aggregate shape `.to(e => ({target: e.stream}))` (#1398).\n if (snapshot === undefined)\n await tx.execute({\n sql: \"DELETE FROM streams WHERE stream = ?\",\n args: [stream],\n });\n\n const event_name =\n snapshot !== undefined ? \"__snapshot__\" : \"__tombstone__\";\n const event_meta = meta ?? { correlation: \"\", causation: {} };\n const now = new Date().toISOString();\n const ins = await tx.execute({\n sql: \"INSERT INTO events (stream, version, name, data, meta, created) VALUES (?, 0, ?, ?, ?, ?)\",\n args: [\n stream,\n event_name,\n JSON.stringify(snapshot ?? {}),\n JSON.stringify(event_meta),\n now,\n ],\n });\n\n result.set(stream, {\n deleted,\n committed: {\n id: Number(ins.lastInsertRowid),\n stream,\n version: 0,\n created: new Date(now),\n name: event_name,\n data: snapshot ?? {},\n meta: event_meta,\n },\n });\n }\n await tx.commit();\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n\n return result;\n }\n\n /**\n * Atomically wipe-and-rebuild the store inside a single libsql\n * `write` transaction.\n *\n * On any throw inside the driver the transaction rolls back and the\n * store is byte-for-byte unchanged. `DELETE FROM events` + `DELETE\n * FROM streams` wipe both tables; `DELETE FROM sqlite_sequence\n * WHERE name = 'events'` resets the autoincrement counter so the\n * new sequence is dense from 1. `created` is preserved verbatim\n * 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 const tx = await this.client.transaction(\"write\");\n try {\n await tx.execute(\"DELETE FROM events\");\n await tx.execute(\"DELETE FROM streams\");\n // Reset the autoincrement counter so the new sequence is dense\n // from 1. `DELETE FROM sqlite_sequence WHERE name = '?'` is the\n // canonical SQLite reset; safe even if the row doesn't exist.\n await tx.execute(\"DELETE FROM sqlite_sequence WHERE name = 'events'\");\n await driver(async (event) => {\n const pii_for_write = await this._stringify_pii_for_write(event.pii);\n const ins = await tx.execute({\n sql: \"INSERT INTO events (stream, version, name, data, meta, created, pii) VALUES (?, ?, ?, ?, ?, ?, ?)\",\n args: [\n event.stream,\n event.version,\n event.name,\n JSON.stringify(event.data),\n JSON.stringify(event.meta),\n event.created.toISOString(),\n pii_for_write,\n ],\n });\n return Number(ins.lastInsertRowid);\n });\n await tx.commit();\n } catch (error) {\n await tx.rollback();\n throw error;\n }\n }\n\n /**\n * Wipe the sensitive-data payload for every event on the stream — the\n * physical-erasure side of the sensitive-data epic (#566). Sets\n * `events.pii` to `NULL` for the stream's events; `events.data` and\n * the rest of the row are never touched.\n *\n * Single `UPDATE` under SQLite's writer lock, bounded by events-per-\n * stream. Idempotent — the `pii IS NOT NULL` predicate filters out\n * already-wiped rows so a second call returns `0`.\n *\n * SQLite doesn't auto-reclaim space; freed pages stay in the file\n * until an operator-scheduled `PRAGMA incremental_vacuum` or a full\n * `VACUUM`. The production checklist documents the cadence.\n *\n * @param stream Target stream\n * @returns Count of events whose `pii` was set to `NULL`\n */\n async forget_pii(stream: string): Promise<number> {\n const r = await this.client.execute({\n sql: \"UPDATE events SET pii = NULL WHERE stream = ? AND pii IS NOT NULL\",\n args: [stream],\n });\n return r.rowsAffected ?? 0;\n }\n}\n"],"mappings":"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","names":["correlated_at"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/sqlite-store.ts"],"sourcesContent":["/**\n * @packageDocumentation\n * @module act-sqlite\n * Main entry point for the Act-SQLite adapter. Re-exports all core APIs\n */\nexport * from \"./sqlite-store.js\";\n","import { type Client, createClient } from \"@libsql/client\";\nimport type {\n BlockedLease,\n Committed,\n EventMeta,\n Lease,\n Message,\n Query,\n QueryStatsOptions,\n QueryStreams,\n QueryStreamsResult,\n Schemas,\n Store,\n StreamFilter,\n StreamPosition,\n StreamStats,\n SubscribeInput,\n} from \"@rotorsoft/act\";\nimport {\n ConcurrencyError,\n dateReviver,\n is_literal_source,\n StoreError,\n ValidationError,\n} from \"@rotorsoft/act\";\nimport {\n decrypt,\n type Encryption,\n encrypt,\n makeKeyResolver,\n} from \"@rotorsoft/act-crypto\";\n\n/**\n * SQLite store configuration\n */\nexport interface SqliteConfig {\n /**\n * libSQL connection URL. **Required — there is no default.**\n *\n * Accepted forms:\n *\n * - `file:myapp.db` — a local database file, relative or absolute.\n * This is the shape an embedded deployment wants.\n * - `libsql://…` / `https://…` — a remote libSQL server, paired with\n * {@link SqliteConfig.authToken}.\n * - `:memory:` / `file::memory:` — an in-memory database. Normalized\n * to `file::memory:?cache=shared`, because libSQL hands every\n * *connection* its own private database otherwise and the client\n * does not pin statements to one connection — DDL would land in a\n * database later statements cannot see, so writes would be accepted\n * and then silently lost. Shared cache is the only in-memory mode\n * that round-trips, and it comes with a caveat worth knowing: it is\n * **one database per process**, shared by every store pointed at\n * it, and it outlives {@link SqliteStore.dispose}. For isolated\n * throwaway state prefer `InMemoryStore` from `@rotorsoft/act`, or\n * give each store its own `file:` path.\n *\n * `file::memory:?cache=private` is rejected at construction — it is\n * the failure mode above, spelled out explicitly.\n */\n url: string;\n /** Auth token for libSQL server connections (optional) */\n authToken?: string;\n /**\n * Adapter-layer envelope encryption for the `events.pii` column.\n * Optional — when present, every non-null PII payload is encrypted\n * before INSERT and decrypted on every read; when absent, the\n * column is stored and read as plaintext (the framework's default\n * behavior).\n *\n * Cipher and wire format come from `@rotorsoft/act-crypto`:\n * AES-256-GCM with a versioned base64-framed envelope. The TEXT\n * column stores `JSON.stringify(...)`-ed values either way, so\n * encrypted writes land as a JSON-stringified base64 string and\n * plaintext writes as a JSON-stringified object. The read path\n * discriminates by `typeof` after `JSON.parse` — strings get\n * decrypted, objects pass through — which makes mixed-data\n * rollouts transparent.\n *\n * `forget_pii` semantics are unchanged: the column is set to\n * `NULL` regardless of whether the prior value was plaintext or\n * ciphertext.\n *\n * Encryption at rest at the **storage** layer (SQLite SEE, an\n * encrypted volume, OS-level FDE) composes orthogonally with\n * adapter-layer encryption. See\n * `docs/docs/guides/pii-encryption-at-rest.md` for the decision\n * matrix.\n */\n pii_encryption?: Encryption;\n}\n\n/**\n * The only in-memory URL libSQL serves from a single database. A bare\n * `:memory:` / `file::memory:` gives every connection a *private*\n * database, and the client is free to run consecutive statements on\n * different connections — `seed()`'s DDL then lands somewhere the next\n * `commit` cannot see, which surfaces as a successful commit followed\n * by \"no such table: events\" on readback.\n * @internal\n */\nconst SHARED_MEMORY_URL = \"file::memory:?cache=shared\";\n\n/**\n * Recognize libSQL's in-memory URL forms: the bare `:memory:` alias and\n * the `file::memory:` URI it expands to, with or without a query string.\n * Mirrors `isInMemoryConfig` in `@libsql/core`.\n * @internal\n */\nconst is_in_memory_url = (url: string): boolean =>\n url === \":memory:\" ||\n url === \"file::memory:\" ||\n url.startsWith(\"file::memory:?\");\n\n/**\n * Validate and normalize the configured URL.\n *\n * Two failures are turned into loud construction-time errors rather\n * than silent data loss: a missing URL (there is no safe default — a\n * store has to be told where to write) and an explicitly private\n * in-memory database (unusable, for the reason on\n * {@link SHARED_MEMORY_URL}). Every other in-memory spelling is\n * normalized to the shared-cache form so it actually round-trips.\n * @internal\n */\nconst resolve_url = (url: unknown): string => {\n if (typeof url !== \"string\" || url.trim().length === 0)\n throw new ValidationError(\n 'SqliteStore config — \"url\" is required and has no default. Use a file database (\"file:myapp.db\"), a remote libSQL server (\"libsql://…\" plus authToken), or \":memory:\" for a process-wide shared in-memory database. For isolated throwaway state, prefer InMemoryStore from @rotorsoft/act',\n { url },\n \"missing url\"\n );\n if (url.startsWith(\"file::memory:?\") && url.includes(\"cache=private\"))\n throw new ValidationError(\n 'SqliteStore config — a private in-memory database (\"cache=private\") cannot back a store: libSQL gives each connection its own database, so writes are accepted and then lost on the next statement. Use \":memory:\" for the shared-cache database, a \"file:\" path, or InMemoryStore from @rotorsoft/act',\n { url },\n \"private in-memory url\"\n );\n return is_in_memory_url(url) ? SHARED_MEMORY_URL : url;\n};\n\n/**\n * Parse a JSON-encoded TEXT column, reviving ISO-date strings to `Date`\n * for cross-adapter payload parity (#1198).\n * @internal\n */\nconst parse_json = (raw: string): unknown => JSON.parse(raw, dateReviver);\n\n/**\n * SQLite extended result code for a UNIQUE constraint violation\n * (`SQLITE_CONSTRAINT_UNIQUE`). libsql surfaces it on the error's\n * `rawCode`.\n * @internal\n */\nconst SQLITE_CONSTRAINT_UNIQUE = 2067;\n\n/**\n * Recognize a `(stream, version)` unique-constraint violation from a\n * libsql commit error (#1202). Detects via the extended result code when\n * present, falling back to the message text (`UNIQUE constraint failed`)\n * for drivers that surface only the message.\n * @internal\n */\nconst is_unique_violation = (e: unknown): boolean => {\n const err = e as { rawCode?: number; message?: unknown };\n return (\n err?.rawCode === SQLITE_CONSTRAINT_UNIQUE ||\n (typeof err?.message === \"string\" &&\n err.message.includes(\"UNIQUE constraint failed\"))\n );\n};\n\n/** Portable stream-filter body grammar: literal characters, `.` (any\n * single character), and `.*` (any run). Everything else — escapes\n * (`\\.`), character classes (`[0-9]`), grouping/alternation (`(a|b)`),\n * quantifiers (`+ ? { }`), and mid-pattern anchors — has no faithful\n * `GLOB` translation and must throw instead of silently mis-matching.\n * @internal\n */\nconst PORTABLE_BODY = /^(?:\\.\\*|\\.|[^\\\\^$.()[\\]{}|+?*])*$/;\n\n/** Translate a stream filter (regex-shaped or plain substring) into a\n * SQLite `GLOB` pattern.\n *\n * #1197: `GLOB` — not `LIKE` — because `GLOB` is **case-sensitive**,\n * matching PG's `~` and InMemory's `RegExp`. SQLite's `LIKE` is ASCII\n * case-insensitive, and its per-connection `PRAGMA case_sensitive_like`\n * override is unreliable under libsql's pooled connections (a\n * transaction and a follow-up read can land on different connections).\n * `GLOB` carries its case-sensitivity in the operator itself, so it is\n * correct regardless of which pooled connection runs the statement.\n *\n * Honors `^` / `$` anchors and converts `.*` → `*`, `.` → `?`.\n * Unanchored input gets `*` wildcards on both sides. `GLOB` has no\n * ESCAPE clause and `%` / `_` are ordinary literals under it — so no\n * escaping is needed. The portable grammar rejects `*`, `?`, and `[`\n * as literals (they are metacharacters), so a literal can never collide\n * with a `GLOB` wildcard.\n *\n * Only the portable filter grammar documented on\n * {@link QueryStreams.stream} is convertible: `^` / `$` anchors, `.`,\n * `.*`, and literal characters. Any richer regex syntax throws\n * {@link ValidationError} — a lossy approximation would silently\n * return wrong rows, the worst available failure mode when the filter\n * drives `reset` / `unblock`.\n *\n * Examples:\n * - `^abc$` → `abc` (exact)\n * - `^abc.*` → `abc*` (starts-with)\n * - `.*abc$` → `*abc` (ends-with)\n * - `abc` → `*abc*` (contains)\n * - `a.c` → `*a?c*` (single-char wildcard, contains)\n * - `^a_c$` → `a_c` (literal underscore — ordinary under GLOB)\n * - `(a|b)` → throws {@link ValidationError}\n *\n * @internal exported for testing\n */\nexport function streamPatternToGlob(input: string): string {\n let s = input;\n const start = s.startsWith(\"^\");\n const end = s.endsWith(\"$\");\n if (start) s = s.slice(1);\n if (end) s = s.slice(0, -1);\n if (!PORTABLE_BODY.test(s))\n throw new ValidationError(\n `stream filter pattern \"${input}\" — the SQLite adapter supports only the portable subset: \"^\" and \"$\" anchors, \".\" (any single character), \".*\" (any run), and literal characters. Escapes, character classes, grouping, alternation, and quantifiers cannot be converted to GLOB; use stream_exact/source_exact for literal names`,\n input,\n \"non-portable stream filter pattern\"\n );\n const tokens: string[] = [];\n if (!start) tokens.push(\"*\");\n for (let i = 0; i < s.length; i++) {\n const c = s[i];\n if (c === \".\") {\n if (s[i + 1] === \"*\") {\n tokens.push(\"*\");\n i++;\n } else tokens.push(\"?\");\n } else tokens.push(c);\n }\n if (!end) tokens.push(\"*\");\n // Collapse adjacent `*` wildcards — e.g. `^a.*` would otherwise yield\n // `a**`. GLOB treats `**` as `*`, but collapsing keeps the output\n // minimal and matches the pre-#1197 LIKE behavior.\n let out = \"\";\n let prev = \"\";\n for (const t of tokens) {\n if (t === \"*\" && prev === \"*\") continue;\n out += t;\n prev = t;\n }\n return out;\n}\n\n/**\n * SQLite event store adapter for [@rotorsoft/act](https://www.npmjs.com/package/@rotorsoft/act).\n *\n * Provides persistent event storage using SQLite via `@libsql/client`.\n * All write operations use transactions for ACID guarantees.\n *\n * SQLite is a single-writer store: concurrent write transactions raise\n * `SQLITE_BUSY` rather than serializing transparently, so it does not\n * support competing consumers the way PostgreSQL's `FOR UPDATE SKIP LOCKED`\n * does (the TCK leaves `concurrent_claim` off for this reason). The intended\n * model is a single drain worker per database file.\n *\n * **`Store.notify` is intentionally not implemented.** The notify hook is\n * a cross-process wake-up signal that lets a horizontally-scaled Act\n * deployment wake `settle()` immediately on remote commits. SQLite is\n * single-node by design — there is no remote writer to be notified of —\n * so the {@link Act} orchestrator falls back to the existing\n * debounce/poll path, which is correct for this topology.\n *\n * @example\n * ```typescript\n * import { store } from \"@rotorsoft/act\";\n * import { SqliteStore } from \"@rotorsoft/act-sqlite\";\n *\n * store(new SqliteStore({ url: \"file:myapp.db\" }));\n * await store().seed();\n * ```\n */\nexport class SqliteStore implements Store {\n private client: Client;\n /**\n * Memoized key resolver for the optional `pii_encryption` envelope.\n * Initialized in the constructor when encryption is configured;\n * `undefined` otherwise. The resolver caches the operator's key on\n * first use — rotation means restarting the store with a fresh\n * provider.\n */\n private readonly _resolve_pii_key: (() => Promise<Buffer>) | undefined;\n\n constructor(config: SqliteConfig) {\n this.client = createClient({\n url: resolve_url(config?.url),\n authToken: config.authToken,\n });\n this._resolve_pii_key = config.pii_encryption\n ? makeKeyResolver(config.pii_encryption)\n : undefined;\n }\n\n /**\n * Write-side pii encoder: encrypts when `pii_encryption` is configured,\n * stringifies for the TEXT column either way. `null` passes through so\n * `forget_pii` semantics survive intact (the row format skips the\n * column entirely on a `NULL` write).\n *\n * @internal\n */\n private async _stringify_pii_for_write(\n pii: Readonly<Record<string, unknown>> | null | undefined\n ): Promise<string | null> {\n if (pii == null) return null;\n if (this._resolve_pii_key) {\n return JSON.stringify(await encrypt(pii, this._resolve_pii_key));\n }\n return JSON.stringify(pii);\n }\n\n /**\n * Read-side pii decoder: parses the TEXT column, and when\n * `pii_encryption` is configured, transparently decrypts any value\n * that came back as a JSON string. Plaintext writes deserialize to\n * objects and pass through — which is what makes mixed-data\n * rollouts (some pre-encryption rows, some post-) read cleanly.\n *\n * @internal\n */\n private async _parse_pii_from_read(\n raw: unknown\n ): Promise<Record<string, unknown> | null> {\n if (raw == null) return null;\n // Revive dates like `data`/`meta` do (#1198/#1365). Base64 ciphertext\n // never matches the ISO-8601 pattern, so the encrypted branch below is\n // unaffected by the reviver here — it gets its own on the plaintext.\n const parsed = parse_json(raw as string);\n if (this._resolve_pii_key && typeof parsed === \"string\") {\n return (await decrypt(\n parsed,\n this._resolve_pii_key,\n dateReviver\n )) as Record<string, unknown>;\n }\n return parsed as Record<string, unknown>;\n }\n\n async seed() {\n await this.client.execute(\"PRAGMA journal_mode=WAL\");\n await this.client.execute(`\n CREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n stream TEXT NOT NULL,\n version INTEGER NOT NULL,\n name TEXT NOT NULL,\n data TEXT NOT NULL,\n meta TEXT NOT NULL,\n created TEXT NOT NULL,\n pii TEXT,\n UNIQUE(stream, version)\n )\n `);\n // Migration for tables created before pii_isolation (#871).\n // libSQL surfaces \"duplicate column\" as an error, hence the\n // try/swallow — mirrors PG's `ADD COLUMN IF NOT EXISTS`. SQLite's\n // row format skips NULL columns, so events without sensitive\n // declarations pay zero extra bytes.\n try {\n await this.client.execute(\"ALTER TABLE events ADD COLUMN pii TEXT\");\n } catch {\n // already present\n }\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_events_stream ON events(stream)\"\n );\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_events_name ON events(name)\"\n );\n // Partial index over snapshot rows only, so the with_snaps \"resume at\n // the latest snapshot\" floor (MAX(id) WHERE stream=? AND\n // name='__snapshot__') is an O(log) lookup and costs nothing for\n // streams with no snapshot (the index has no rows for them).\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_events_snapshot ON events(stream, id) WHERE name = '__snapshot__'\"\n );\n // Complement of the snapshot index, and the one claim's has-work probe\n // seeks on: \"does this source stream have a non-snapshot event past the\n // watermark?\" (#1448). SQLite already probes per candidate with a\n // sargable `stream = ? AND id > ?`, so it only lacked the index — the\n // existing idx_events_stream is stream-only and still has to walk every\n // event of that stream. Partial over non-snapshot rows so the two\n // indexes partition the table rather than overlapping.\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_events_stream_id ON events(stream, id) WHERE name <> '__snapshot__'\"\n );\n await this.client.execute(`\n CREATE TABLE IF NOT EXISTS streams (\n stream TEXT PRIMARY KEY,\n source TEXT,\n at INTEGER NOT NULL DEFAULT -1,\n retry INTEGER NOT NULL DEFAULT -1,\n blocked INTEGER NOT NULL DEFAULT 0,\n error TEXT NOT NULL DEFAULT '',\n leased_by TEXT,\n leased_until TEXT,\n priority INTEGER NOT NULL DEFAULT 0,\n lane TEXT NOT NULL DEFAULT 'default',\n deferred_at TEXT,\n correlated_at INTEGER\n )\n `);\n // Migration for tables created before priority lanes (ACT-102).\n // libSQL surfaces \"duplicate column\" as an error, hence the\n // try/swallow — this mirrors PG's `ADD COLUMN IF NOT EXISTS`.\n try {\n await this.client.execute(\n \"ALTER TABLE streams ADD COLUMN priority INTEGER NOT NULL DEFAULT 0\"\n );\n } catch {\n // already present\n }\n // Migration for tables created before deferred reactions (#1090).\n try {\n await this.client.execute(\n \"ALTER TABLE streams ADD COLUMN deferred_at TEXT\"\n );\n } catch {\n // already present\n }\n // Migration for tables created before drain lanes (ACT-1103).\n try {\n await this.client.execute(\n \"ALTER TABLE streams ADD COLUMN lane TEXT NOT NULL DEFAULT 'default'\"\n );\n } catch {\n // already present\n }\n // Migration for tables created before the work set (#1485). Nullable on\n // purpose: NULL means \"unknown\", which routes the row to the legacy\n // has-work probe, so an upgraded install behaves exactly as it did and\n // migrates row by row as correlate marks each target.\n try {\n await this.client.execute(\n \"ALTER TABLE streams ADD COLUMN correlated_at INTEGER\"\n );\n } catch {\n // already present\n }\n // Correlate checkpoint (#1484). Its own single-row table rather than a\n // reserved subscription: a subscription row is counted by every\n // stream-scoped operator surface (prioritize / reset / unblock /\n // query_streams / blocked_streams) and would inflate those counts.\n await this.client.execute(`\n CREATE TABLE IF NOT EXISTS correlated (\n id INTEGER PRIMARY KEY CHECK (id = 0),\n at INTEGER NOT NULL DEFAULT -1\n )\n `);\n await this.client.execute(\n \"INSERT OR IGNORE INTO correlated (id) VALUES (0)\"\n );\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_streams_claim ON streams(blocked, priority DESC, at)\"\n );\n // The correlated set IS the index (#1485): it holds only streams with\n // work, so `claim` scans candidates instead of every subscription. A\n // stream leaves when `ack` advances `at` to `correlated_at`, and re-enters\n // when correlate raises the mark.\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_streams_correlated_at ON streams(lane, priority DESC, at) WHERE blocked = 0 AND at < correlated_at\"\n );\n // Lane filter index (ACT-1103).\n await this.client.execute(\n \"CREATE INDEX IF NOT EXISTS idx_streams_lane ON streams(lane)\"\n );\n }\n\n async drop() {\n await this.client.execute(\"DROP TABLE IF EXISTS events\");\n await this.client.execute(\"DROP TABLE IF EXISTS streams\");\n await this.client.execute(\"DROP TABLE IF EXISTS correlated\");\n }\n\n async dispose() {\n await Promise.resolve();\n this.client.close();\n }\n\n // --- commit: transaction + optimistic concurrency ---\n async commit<E extends Schemas>(\n stream: string,\n msgs: Message<E, keyof E>[],\n meta: EventMeta,\n expectedVersion?: number\n ): Promise<Committed<E, keyof E>[]> {\n const tx = await this.client.transaction(\"write\");\n // Hoisted so the catch can build a ConcurrencyError with the head\n // version on a (stream, version) unique collision (#1202).\n let current_version = -1;\n try {\n const version_row = await tx.execute({\n sql: \"SELECT COALESCE(MAX(version), -1) as v FROM events WHERE stream = ?\",\n args: [stream],\n });\n current_version = Number(version_row.rows[0].v);\n\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 const now = new Date().toISOString();\n const committed: Committed<E, keyof E>[] = [];\n let version = current_version + 1;\n\n for (const { name, data, pii } of msgs) {\n const pii_for_write = await this._stringify_pii_for_write(pii);\n const result = await tx.execute({\n sql: \"INSERT INTO events (stream, version, name, data, meta, created, pii) VALUES (?, ?, ?, ?, ?, ?, ?)\",\n args: [\n stream,\n version,\n name as string,\n JSON.stringify(data),\n JSON.stringify(meta),\n now,\n pii_for_write,\n ],\n });\n committed.push({\n id: Number(result.lastInsertRowid),\n stream,\n version,\n created: new Date(now),\n name,\n data,\n meta,\n ...(pii == null ? {} : { pii }),\n });\n version++;\n }\n\n await tx.commit();\n return committed;\n } catch (e) {\n await tx.rollback();\n // #1202: map the (stream, version) unique collision to\n // ConcurrencyError — the same signal PG raises from 23505 — so a\n // caller retries on the framework contract, not a raw libsql error.\n // This is the race where the version probe passed but a concurrent\n // INSERT (a second store on the same file, tooling, the act CLI)\n // beat this one to the row. Every other driver error is wrapped in\n // StoreError('commit') for parity with the adapter's other methods.\n if (e instanceof ConcurrencyError) throw e;\n if (is_unique_violation(e))\n throw new ConcurrencyError(\n stream,\n current_version,\n msgs as Message<Schemas, keyof Schemas>[],\n expectedVersion ?? -1\n );\n throw new StoreError(\"commit\", { cause: e });\n }\n }\n\n // --- query: read-only, no transaction needed ---\n async query<E extends Schemas>(\n callback: (event: Committed<E, keyof E>) => void,\n query?: Query\n ): Promise<number> {\n let sql = \"SELECT * FROM events WHERE 1=1\";\n const args: unknown[] = [];\n\n if (query?.stream) {\n if (query.stream_exact) {\n sql += \" AND stream = ?\";\n args.push(query.stream);\n } else {\n sql += \" AND stream GLOB ?\";\n args.push(streamPatternToGlob(query.stream));\n }\n }\n if (query?.names !== undefined) {\n // #1199: `names: []` means \"match no event names\". An empty\n // placeholder list yields `name IN ()` which SQLite evaluates as\n // always-false — matching the PG (`= ANY('{}')`) / InMemory\n // semantics. `!== undefined` (not truthiness) keeps that explicit.\n sql += ` AND name IN (${query.names.map(() => \"?\").join(\",\")})`;\n args.push(...query.names);\n }\n if ((query as any)?.correlation) {\n sql += \" AND json_extract(meta, '$.correlation') = ?\";\n args.push((query as any).correlation);\n }\n if (query?.after !== undefined) {\n sql += \" AND id > ?\";\n args.push(query.after);\n } else if (query?.with_snaps && query.stream_exact && query.stream) {\n // Resume at the latest snapshot for this stream so pre-snapshot\n // events aren't scanned. No snapshot → MAX is NULL → -1 → full\n // stream. An explicit `after` (above) wins. The orchestrator only sets\n // `with_snaps` for an unbounded current-state load — it suppresses the\n // flag under any `asOf` bound (RFC 1274) — so the floor never needs to\n // re-check `before`/`created_*`/`limit` here.\n sql +=\n \" AND id >= (SELECT COALESCE(MAX(id), -1) FROM events WHERE stream = ? AND name = '__snapshot__')\";\n args.push(query.stream);\n }\n if (query?.before !== undefined) {\n sql += \" AND id < ?\";\n args.push(query.before);\n }\n if (query?.created_after) {\n sql += \" AND created > ?\";\n args.push(query.created_after.toISOString());\n }\n if (query?.created_before) {\n sql += \" AND created < ?\";\n args.push(query.created_before.toISOString());\n }\n if (!query?.with_snaps) {\n sql += \" AND name != '__snapshot__'\";\n }\n\n sql += query?.backward ? \" ORDER BY id DESC\" : \" ORDER BY id ASC\";\n\n if (query?.limit) {\n sql += \" LIMIT ?\";\n args.push(query.limit);\n }\n\n const result = await this.client.execute({ sql, args: args as any[] });\n let count = 0;\n\n for (const row of result.rows) {\n const pii_value = await this._parse_pii_from_read(row.pii);\n await Promise.resolve(\n callback({\n id: Number(row.id),\n stream: row.stream as string,\n version: Number(row.version),\n created: new Date(row.created as string),\n name: row.name as string,\n data: parse_json(row.data as string) as any,\n meta: parse_json(row.meta as string) as any,\n pii: pii_value,\n })\n );\n count++;\n }\n\n return count;\n }\n\n // --- subscribe: idempotent INSERT OR IGNORE (= PG ON CONFLICT DO NOTHING)\n // plus a UPDATE pass to keep the *max* priority across reactions\n // targeting the same stream (ACT-102). Operator overrides go\n // through `prioritize()` instead.\n async subscribe(streams: SubscribeInput[], correlated_at?: number) {\n // Fail loud at registration for a claim source this adapter cannot\n // match: libsql has no `REGEXP`, and the portable GLOB grammar cannot\n // express alternation/grouping. A pattern source outside the portable\n // subset (e.g. `^(A|B)$`) would otherwise silently never claim its\n // stream, so `streamPatternToGlob` throws `ValidationError` here —\n // pointing operators to InMemory/PG for full regex claim sources —\n // before any row is written. Literal sources skip the check entirely.\n for (const { source } of streams) {\n if (source !== undefined && !is_literal_source(source))\n streamPatternToGlob(source);\n }\n const tx = await this.client.transaction(\"write\");\n try {\n let subscribed = 0;\n for (const {\n stream,\n source,\n priority = 0,\n lane = \"default\",\n correlated_at,\n } of streams) {\n const inserted = await tx.execute({\n sql: \"INSERT OR IGNORE INTO streams (stream, source, priority, lane, retry) VALUES (?, ?, ?, ?, -1)\",\n args: [stream, source ?? null, priority, lane],\n });\n if (inserted.rowsAffected > 0) {\n subscribed++;\n } else {\n // `WHERE priority < ?` is the whole max rule, for every value on\n // the number line. An extra `priority > 0` gate around this used to\n // skip the merge for non-positive priorities, which made a stored\n // negative priority unraisable: an operator's `prioritize(-5)`\n // survived every subsequent boot instead of being restored to the\n // declared priority by the restart-driven re-subscribe (#1445).\n await tx.execute({\n sql: \"UPDATE streams SET priority = ? WHERE stream = ? AND priority < ?\",\n args: [priority, stream, priority],\n });\n // ACT-1103: current subscribe wins on lane.\n await tx.execute({\n sql: \"UPDATE streams SET lane = ? WHERE stream = ? AND lane <> ?\",\n args: [lane, stream, lane],\n });\n }\n // The work mark never regresses (#1485). `WHERE correlated_at IS\n // NULL OR correlated_at < ?` is the whole rule: the NULL arm is first\n // mark (a comparison against NULL is unknown, so the predicate alone\n // would skip it), and it holds for every value including zero and\n // negatives. Written here rather than in the INSERT above so an\n // unmarked subscribe never names the column — a table that predates\n // it, and that `seed()` has not migrated, keeps working.\n if (correlated_at !== undefined)\n await tx.execute({\n sql: \"UPDATE streams SET correlated_at = ? WHERE stream = ? AND (correlated_at IS NULL OR correlated_at < ?)\",\n args: [correlated_at, stream, correlated_at],\n });\n }\n const wm = await tx.execute(\n \"SELECT COALESCE(MAX(at), -1) as w FROM streams\"\n );\n // The correlate checkpoint is written by its own producer, in the call\n // correlate already makes (#1484). MAX keeps it monotonic.\n if (correlated_at !== undefined)\n await tx.execute({\n sql: \"UPDATE correlated SET at = MAX(at, ?) WHERE id = 0\",\n args: [correlated_at],\n });\n // Watermark and checkpoint together — correlate needs both.\n const cp = await tx.execute(\"SELECT at FROM correlated WHERE id = 0\");\n await tx.commit();\n return {\n subscribed,\n watermark: Number(wm.rows[0].w),\n // `seed()` always inserts the singleton row, so this read cannot\n // come back empty — no defensive fallback to leave untested.\n correlated_at: Number(cp.rows[0].at),\n };\n } catch (e) {\n await tx.rollback();\n throw new StoreError(\"subscribe\", { cause: e });\n }\n }\n\n // --- claim: write transaction. Single-writer: concurrent claims from\n // separate connections raise SQLITE_BUSY, so this assumes one drain\n // worker per database file (see class doc; TCK `concurrent_claim` off).\n async claim(\n lagging: number,\n leading: number,\n by: string,\n millis: number,\n lane?: string\n ) {\n const tx = await this.client.transaction(\"write\");\n try {\n const now = new Date().toISOString();\n\n const lane_clause = lane !== undefined ? \" AND lane = ?\" : \"\";\n // `correlated_at IS NULL OR at < correlated_at` (#1485) drops marked\n // whose watermark has caught up before they ever reach the probe loop\n // below. NULL means \"unknown\", so those rows pass through to the legacy\n // probe — an install that predates the column behaves exactly as it did.\n const result = await tx.execute({\n sql: `SELECT stream, source, at, priority, lane, correlated_at FROM streams\n WHERE blocked = 0 AND (leased_until IS NULL OR leased_until <= ?)\n AND (deferred_at IS NULL OR deferred_at <= ?)${lane_clause}\n AND (correlated_at IS NULL OR at < correlated_at)\n ORDER BY priority DESC, at ASC`,\n args: lane !== undefined ? [now, now, lane] : [now, now],\n });\n\n const candidates: {\n stream: string;\n source: string | undefined;\n at: number;\n priority: number;\n lane: string;\n }[] = [];\n for (const row of result.rows) {\n const stream = row.stream as string;\n const source = row.source as string | null;\n const at = Number(row.at);\n\n // A marked row is already known to have work — the SQL above kept\n // only `at < correlated_at` — so it skips the probe entirely. This is\n // the N+1 the work set exists to remove; step 5 deletes the arm below.\n if (row.correlated_at !== null) {\n candidates.push({\n stream,\n source: source ?? undefined,\n at,\n priority: Number(row.priority),\n lane: row.lane as string,\n });\n continue;\n }\n\n let has_events: boolean;\n if (source) {\n // Has-work probe. A literal `source` (no regex metacharacter)\n // matches by equality — index-friendly, and exact so \"s1\" never\n // claims \"s12\". A portable pattern (anchors, `.`, `.*`) matches\n // via the same GLOB translation the StreamFilter surfaces use.\n // Non-portable patterns (alternation/grouping) never reach here:\n // `subscribe` rejects them up front, so `streamPatternToGlob`\n // below only sees the portable subset.\n const check = is_literal_source(source)\n ? await tx.execute({\n sql: `SELECT 1 FROM events WHERE id > ? AND name != '__snapshot__' AND stream = ? LIMIT 1`,\n args: [at, source],\n })\n : await tx.execute({\n sql: `SELECT 1 FROM events WHERE id > ? AND name != '__snapshot__' AND stream GLOB ? LIMIT 1`,\n args: [at, streamPatternToGlob(source)],\n });\n has_events = check.rows.length > 0;\n } else {\n const check = await tx.execute({\n sql: `SELECT 1 FROM events WHERE id > ? AND name != '__snapshot__' LIMIT 1`,\n args: [at],\n });\n has_events = check.rows.length > 0;\n }\n\n if (has_events) {\n candidates.push({\n stream,\n source: source ?? undefined,\n at,\n priority: Number(row.priority),\n lane: row.lane as string,\n });\n }\n }\n\n // Dual frontier: lagging (priority DESC, watermark ASC — ACT-102)\n // + leading (newest first). The candidates list arrives sorted\n // by `priority DESC, at ASC` from the SELECT above. The lagging\n // budget is split (ACT-1223): the priority portion takes the first\n // `lagging - fair` candidates in that order; a fairness reserve then\n // fills `fair` more by pure `at ASC` (priority ignored), excluding\n // the ones already picked, so a default-priority lagging stream is\n // never starved out by sustained higher-priority load. With all\n // priorities equal both portions order by `at`, a no-op split.\n const fair = lagging >= 2 ? Math.max(1, Math.floor(lagging / 4)) : 0;\n const priority_slice = candidates.slice(0, lagging - fair);\n const picked = new Set(priority_slice.map((c) => c.stream));\n const fair_slice = [...candidates]\n .sort((a, b) => a.at - b.at)\n .filter((c) => !picked.has(c.stream))\n .slice(0, fair);\n const lag = [...priority_slice, ...fair_slice].map((c) => ({\n ...c,\n lagging: true,\n }));\n const lead = [...candidates]\n .sort((a, b) => b.at - a.at)\n .slice(0, leading)\n .map((c) => ({ ...c, lagging: false }));\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\n const leases: Lease[] = [];\n const until = new Date(Date.now() + millis).toISOString();\n for (const row of combined) {\n const updated = await tx.execute({\n sql: \"UPDATE streams SET leased_by = ?, leased_until = ?, retry = retry + 1 WHERE stream = ? RETURNING retry\",\n args: [by, until, row.stream],\n });\n leases.push({\n stream: row.stream,\n source: row.source,\n at: row.at,\n by,\n retry: Number(updated.rows[0].retry),\n lagging: row.lagging,\n lane: row.lane,\n });\n }\n\n await tx.commit();\n return leases;\n } catch (e) {\n await tx.rollback();\n throw new StoreError(\"claim\", { cause: e });\n }\n }\n\n // --- ack: transaction + ownership check (= PG WHERE leased_by) ---\n async ack(leases: Lease[]) {\n const tx = await this.client.transaction(\"write\");\n try {\n // The whole batch finalizes in one transaction, so acks and defer\n // schedules land all-or-nothing per the Store.ack contract. Every\n // entry advances the watermark to its `at` (the last event handled\n // this cycle); an entry without `due` also clears retry + schedule,\n // while an entry with `due` additionally sets the schedule and the\n // entry's own `retry` — advance and defer are independent legs\n // (#1278), so a partial-progress defer keeps the handled prefix. The\n // bound schedule doubles as the ack-vs-defer discriminator — NULL\n // means ack. An explicit defer passes retry -1 (not a failure); a\n // backoff retry passes the climbing counter so the budget keeps\n // accruing across windows (#1262). Only acked entries are returned.\n const result: Lease[] = [];\n for (const l of leases) {\n const due = l.due !== undefined ? new Date(l.due).toISOString() : null;\n const r = await tx.execute({\n // RETURNING the post-ack `retry`/`source`/`lane` from the row so the\n // returned lease reflects the authoritative state, not the caller's\n // pre-ack echo (#1347). A non-due ack sets `retry = -1`; pushing the\n // input lease verbatim leaked the claim-incremented value and\n // diverged from PG (RETURNING s.retry) and InMemory (which return the\n // reset -1). `at`/`by`/`lagging` still come from the input lease,\n // matching both other adapters.\n sql: `UPDATE streams\n SET at = ?,\n deferred_at = ?,\n leased_by = NULL, leased_until = NULL,\n retry = CASE WHEN ? IS NULL THEN -1 ELSE ? END\n WHERE stream = ? AND leased_by = ?\n RETURNING source, retry, lane`,\n args: [l.at, due, due, l.retry, l.stream, l.by],\n });\n if (due === null && r.rows.length > 0) {\n const row = r.rows[0];\n result.push({\n stream: l.stream,\n source: (row.source as string | null) ?? undefined,\n at: l.at,\n by: l.by,\n retry: Number(row.retry),\n lagging: l.lagging,\n lane: row.lane as string,\n });\n }\n }\n await tx.commit();\n return result;\n } catch (e) {\n await tx.rollback();\n throw new StoreError(\"ack\", { cause: e });\n }\n }\n\n // --- block: transaction + ownership + idempotent (= PG) ---\n async block(leases: BlockedLease[]) {\n const tx = await this.client.transaction(\"write\");\n try {\n const result: BlockedLease[] = [];\n for (const l of leases) {\n const r = await tx.execute({\n // RETURNING the post-block row so the returned lease reflects the\n // authoritative state, not the caller's echo (#1382, the `block`\n // twin of #1347). A block leaves the watermark alone — the failing\n // event must be retried, not skipped — but `run_drain_cycle` hands\n // us a lease whose `at` was fast-forwarded to the fetch ceiling, so\n // pushing the input verbatim reported a position the row never\n // held. `by`/`error`/`lagging` still come from the input lease,\n // matching PG (`RETURNING s.stream, s.source, s.at, i.by, ...`).\n sql: `UPDATE streams SET blocked = 1, error = ?, deferred_at = NULL\n WHERE stream = ? AND leased_by = ? AND blocked = 0\n RETURNING source, at, retry, lane`,\n args: [l.error, l.stream, l.by],\n });\n if (r.rows.length > 0) {\n const row = r.rows[0];\n result.push({\n stream: l.stream,\n source: (row.source as string | null) ?? undefined,\n at: Number(row.at),\n by: l.by,\n retry: Number(row.retry),\n error: l.error,\n lagging: l.lagging,\n lane: row.lane as string,\n });\n }\n }\n await tx.commit();\n return result;\n } catch (e) {\n await tx.rollback();\n throw new StoreError(\"block\", { cause: e });\n }\n }\n\n // --- defer: hold streams out of claim until deferred_at (#1090) ---\n // Persisted as an ISO string (like leased_until) so claim's\n // `deferred_at <= now` comparison is a lexicographic string compare.\n // Cleared by ack/block/reset/unblock. See {@link Store.defer}.\n async defer(input: string[] | StreamFilter, deferred_at: number) {\n const at_iso = new Date(deferred_at).toISOString();\n const tx = await this.client.transaction(\"write\");\n try {\n let count = 0;\n if (Array.isArray(input)) {\n // De-dup so a repeated name counts once, matching PG's set-based\n // `WHERE stream = ANY(...)` (#1360).\n for (const stream of new Set(input)) {\n const r = await tx.execute({\n sql: `UPDATE streams SET deferred_at = ?, retry = -1 WHERE stream = ?`,\n args: [at_iso, stream],\n });\n count += r.rowsAffected;\n }\n } else {\n const { clause, args } = this._filter_clause(input);\n const r = await tx.execute({\n sql: `UPDATE streams SET deferred_at = ?, retry = -1 WHERE ${clause}`,\n args: [at_iso, ...args] as any[],\n });\n count = r.rowsAffected;\n }\n await tx.commit();\n return count;\n } catch (e) {\n await tx.rollback();\n throw new StoreError(\"defer\", { cause: e });\n }\n }\n\n /**\n * Translate a {@link StreamFilter} to a SQLite `WHERE` clause fragment\n * plus positional args. Returns `\"1\"` (always true) when empty so\n * callers can compose it unconditionally.\n */\n private _filter_clause(filter: StreamFilter): {\n clause: string;\n args: unknown[];\n } {\n const conditions: string[] = [];\n const args: unknown[] = [];\n if (filter.stream !== undefined) {\n if (filter.stream_exact) {\n conditions.push(\"stream = ?\");\n args.push(filter.stream);\n } else {\n conditions.push(\"stream GLOB ?\");\n args.push(streamPatternToGlob(filter.stream));\n }\n }\n if (filter.source !== undefined) {\n conditions.push(\"source IS NOT NULL\");\n if (filter.source_exact) {\n conditions.push(\"source = ?\");\n args.push(filter.source);\n } else {\n conditions.push(\"source GLOB ?\");\n args.push(streamPatternToGlob(filter.source));\n }\n }\n if (filter.blocked !== undefined) {\n conditions.push(\"blocked = ?\");\n args.push(filter.blocked ? 1 : 0);\n }\n if (filter.lane !== undefined) {\n conditions.push(\"lane = ?\");\n args.push(filter.lane);\n }\n return { clause: conditions.length ? conditions.join(\" AND \") : \"1\", args };\n }\n\n // --- reset: transactional, accepts names or filter ---\n async reset(input: string[] | StreamFilter) {\n const set_clause = `SET at = -1, retry = -1, blocked = 0, error = '',\n leased_by = NULL, leased_until = NULL, deferred_at = NULL`;\n const tx = await this.client.transaction(\"write\");\n try {\n let count = 0;\n if (Array.isArray(input)) {\n // De-dup so a repeated name counts once, matching PG's set-based\n // `WHERE stream = ANY(...)` (#1360).\n for (const stream of new Set(input)) {\n const r = await tx.execute({\n sql: `UPDATE streams ${set_clause} WHERE stream = ?`,\n args: [stream],\n });\n count += r.rowsAffected;\n }\n } else {\n const { clause, args } = this._filter_clause(input);\n const r = await tx.execute({\n sql: `UPDATE streams ${set_clause} WHERE ${clause}`,\n args: args as any[],\n });\n count = r.rowsAffected;\n }\n await tx.commit();\n return count;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- unblock: clear blocked + retry + lease without touching watermark ---\n // `retry = -1` so claim's post-bump returns retry=0 (first attempt),\n // matching the InMemoryStore convention.\n async unblock(input: string[] | StreamFilter) {\n const set_clause = `SET retry = -1, blocked = 0, error = '',\n leased_by = NULL, leased_until = NULL, deferred_at = NULL`;\n const tx = await this.client.transaction(\"write\");\n try {\n let count = 0;\n if (Array.isArray(input)) {\n for (const stream of input) {\n const r = await tx.execute({\n sql: `UPDATE streams ${set_clause}\n WHERE stream = ? AND blocked = 1`,\n args: [stream],\n });\n count += r.rowsAffected;\n }\n } else {\n // Filter form: force blocked = true regardless of what the\n // caller passed.\n const { clause, args } = this._filter_clause({\n ...input,\n blocked: true,\n });\n const r = await tx.execute({\n sql: `UPDATE streams ${set_clause} WHERE ${clause}`,\n args: args as any[],\n });\n count = r.rowsAffected;\n }\n await tx.commit();\n return count;\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n }\n\n // --- query_streams: read-only introspection with filters ---\n async query_streams(\n callback: (position: StreamPosition) => void,\n query?: QueryStreams\n ): Promise<QueryStreamsResult> {\n const limit = query?.limit ?? 100;\n let sql =\n \"SELECT stream, source, at, retry, blocked, error, leased_by, leased_until, priority, lane, deferred_at, correlated_at FROM streams WHERE 1=1\";\n const args: unknown[] = [];\n\n if (query?.stream !== undefined) {\n if (query.stream_exact) {\n sql += \" AND stream = ?\";\n args.push(query.stream);\n } else {\n sql += \" AND stream GLOB ?\";\n args.push(streamPatternToGlob(query.stream));\n }\n }\n if (query?.source !== undefined) {\n sql += \" AND source IS NOT NULL\";\n if (query.source_exact) {\n sql += \" AND source = ?\";\n args.push(query.source);\n } else {\n sql += \" AND source GLOB ?\";\n args.push(streamPatternToGlob(query.source));\n }\n }\n if (query?.blocked !== undefined) {\n sql += \" AND blocked = ?\";\n args.push(query.blocked ? 1 : 0);\n }\n if (query?.lane !== undefined) {\n sql += \" AND lane = ?\";\n args.push(query.lane);\n }\n if (query?.after !== undefined) {\n sql += \" AND stream > ?\";\n args.push(query.after);\n }\n sql += \" ORDER BY stream LIMIT ?\";\n args.push(limit);\n\n const [streamsResult, maxResult] = await Promise.all([\n this.client.execute({ sql, args: args as any[] }),\n this.client.execute(\"SELECT COALESCE(MAX(id), -1) AS m FROM events\"),\n ]);\n\n let count = 0;\n for (const row of streamsResult.rows) {\n const leased_until = row.leased_until as string | null;\n // Persisted as an ISO string (like leased_until); surface as ms since\n // epoch (#1221) so the cold-start re-seed re-arms at the due-time.\n const deferred_at = row.deferred_at as string | null;\n // NULL means \"no mark yet\" — unknown, not \"no work\" (#1485).\n const correlated_at = row.correlated_at as number | null;\n callback({\n stream: row.stream as string,\n source: (row.source as string | null) ?? undefined,\n at: Number(row.at),\n retry: Number(row.retry),\n blocked: Number(row.blocked) === 1,\n error: row.error as string,\n priority: Number(row.priority),\n leased_by: (row.leased_by as string | null) ?? undefined,\n leased_until: leased_until ? new Date(leased_until) : undefined,\n lane: row.lane as string,\n deferred_at: deferred_at ? new Date(deferred_at).getTime() : undefined,\n correlated_at: correlated_at ?? undefined,\n });\n count++;\n }\n\n return { maxEventId: Number(maxResult.rows[0].m), count };\n }\n\n /**\n * Per-stream aggregated stats — see {@link Store.query_stats}.\n *\n * Two code paths (mirrors the PostgresStore strategy):\n *\n * - **Heads-only path** (no `count`, no `names`): one or two queries\n * using `ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version\n * DESC|ASC)` (SQLite lacks PG's `DISTINCT ON`). Window function +\n * `WHERE rn = 1` materializes the head (or tail) per stream from\n * the `(stream, version)` unique index. Parallel `Promise.all` when\n * tail is requested.\n *\n * - **Full-scan path** (`count` or `names` set): one CTE materializes\n * the filtered events, then `GROUP BY stream, name` →\n * `json_group_object(name, n)` for the names map plus `SUM(n)` for\n * count. Heads (and tails when requested) come from the same scan.\n *\n * SQLite specifics:\n * - `data` and `meta` are stored as TEXT (JSON-encoded); the reader\n * JSON-parses them when materializing the {@link Committed} rows.\n * - `blocked` is stored as 0/1 integer; the filter converts.\n * - Array input expands to a placeholder list (`IN (?, ?, ...)`)\n * since SQLite has no native array type.\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 const exclude = options?.exclude ?? [];\n const want_tail = options?.tail ?? false;\n const want_count = options?.count ?? false;\n const want_names = options?.names ?? false;\n const before = options?.before;\n const after = options?.after;\n const limit = options?.limit;\n const full_scan = want_count || want_names;\n\n if (Array.isArray(input) && input.length === 0) {\n return new Map<string, StreamStats<E>>();\n }\n\n // Build WHERE clause + positional args. Subscription-level filters\n // (source, blocked) are intentionally not accepted — events live in\n // the events table; subscription state in the streams table. For\n // \"stats for blocked subscriptions\" callers compose with\n // query_streams. So no JOIN here.\n const where: string[] = [];\n const args: unknown[] = [];\n\n if (Array.isArray(input)) {\n const placeholders = input.map(() => \"?\").join(\",\");\n where.push(`e.stream IN (${placeholders})`);\n args.push(...input);\n } else if (input.stream !== undefined) {\n if (input.stream_exact) {\n where.push(`e.stream = ?`);\n args.push(input.stream);\n } else {\n where.push(`e.stream GLOB ?`);\n args.push(streamPatternToGlob(input.stream));\n }\n }\n if (exclude.length) {\n const placeholders = exclude.map(() => \"?\").join(\",\");\n where.push(`e.name NOT IN (${placeholders})`);\n args.push(...exclude);\n }\n if (before !== undefined) {\n where.push(`e.id < ?`);\n args.push(before);\n }\n // Keyset pagination cursor (#1010): exclusive — return only streams\n // whose name sorts strictly after `after`. Applied at the event level\n // so the per-stream head/agg rows downstream only cover the page.\n if (after !== undefined) {\n where.push(`e.stream > ?`);\n args.push(after);\n }\n\n const from_clause = `events e`;\n // Always emit a WHERE clause — `WHERE 1=1` short-circuits the\n // empty-filter case without a conditional branch on the generation\n // side. SQLite optimizes the trivial predicate out.\n const where_clause = `WHERE ${where.length ? where.join(\" AND \") : \"1=1\"}`;\n\n return full_scan\n ? this._query_stats_full_scan<E>(\n from_clause,\n where_clause,\n args,\n want_tail,\n want_count,\n want_names,\n limit\n )\n : this._query_stats_heads_only<E>(\n from_clause,\n where_clause,\n args,\n want_tail,\n limit\n );\n }\n\n /**\n * Cheap path — head (and optional tail) via ROW_NUMBER() over the\n * `(stream, version)` unique index. Parallel queries when tail set.\n */\n private async _query_stats_heads_only<E extends Schemas>(\n from_clause: string,\n where_clause: string,\n args: unknown[],\n want_tail: boolean,\n limit?: number\n ): Promise<Map<string, StreamStats<E>>> {\n // `query_stats` is an operator-introspection surface with no actor\n // context and no disclosure gate, so it never carries pii — heads/tails\n // return the event shape without the pii sidecar, matching PostgresStore\n // and InMemoryStore (#1294).\n const cols = `e.id, e.stream, e.version, e.name, e.data, e.created, e.meta`;\n // Keyset pagination (#1010): order heads by stream ascending so the\n // caller can use the last key as the next `after` cursor; cap with\n // LIMIT when set (unbounded otherwise).\n const limit_clause = limit !== undefined ? \" LIMIT ?\" : \"\";\n const head_sql = `SELECT * FROM (\n SELECT ${cols}, ROW_NUMBER() OVER (PARTITION BY e.stream ORDER BY e.version DESC) AS rn\n FROM ${from_clause}\n ${where_clause}\n ) WHERE rn = 1 ORDER BY stream${limit_clause}`;\n const head_args = limit !== undefined ? [...args, limit] : args;\n // The tail query shares the same WHERE; when paginating it must cover\n // exactly the limited head stream set, so it joins against the same\n // ordered+limited heads subquery rather than re-scanning all streams.\n const tail_sql = want_tail\n ? `SELECT t.* FROM (\n SELECT ${cols}, ROW_NUMBER() OVER (PARTITION BY e.stream ORDER BY e.version ASC) AS rn\n FROM ${from_clause}\n ${where_clause}\n ) t\n WHERE t.rn = 1 AND t.stream IN (\n SELECT stream FROM (\n SELECT e.stream, ROW_NUMBER() OVER (PARTITION BY e.stream ORDER BY e.version DESC) AS rn\n FROM ${from_clause}\n ${where_clause}\n ) WHERE rn = 1 ORDER BY stream${limit_clause}\n )`\n : null;\n const tail_args =\n limit !== undefined ? [...args, ...args, limit] : [...args, ...args];\n\n const [headRes, tailRes] = await Promise.all([\n this.client.execute({ sql: head_sql, args: head_args as any[] }),\n tail_sql\n ? this.client.execute({ sql: tail_sql, args: tail_args as any[] })\n : null,\n ]);\n\n const to_committed = (\n row: Record<string, unknown>\n ): Committed<E, keyof E> =>\n ({\n id: Number(row.id),\n stream: row.stream as string,\n version: Number(row.version),\n name: row.name as string,\n data: parse_json(row.data as string) as any,\n meta: parse_json(row.meta as string) as any,\n created: new Date(row.created as string),\n }) as Committed<E, keyof E>;\n\n const out = new Map<string, StreamStats<E>>();\n for (const row of headRes.rows) {\n out.set(row.stream as string, {\n head: to_committed(row as Record<string, unknown>),\n });\n }\n if (tailRes) {\n for (const row of tailRes.rows) {\n // Head and tail share the same WHERE, so any stream returning\n // a tail must also have returned a head — no null check needed.\n (\n out.get(row.stream as string) as {\n head: Committed<E, keyof E>;\n tail?: Committed<E, keyof E>;\n }\n ).tail = to_committed(row as Record<string, unknown>);\n }\n }\n return out;\n }\n\n /**\n * Full-scan path — one CTE-based query with per-stream `COUNT(*)` and\n * `json_group_object(name, n)`. Heads (and optional tails) ride free\n * on the same scan.\n */\n private async _query_stats_full_scan<E extends Schemas>(\n from_clause: string,\n where_clause: string,\n args: unknown[],\n want_tail: boolean,\n want_count: boolean,\n want_names: boolean,\n limit?: number\n ): Promise<Map<string, StreamStats<E>>> {\n const tail_cte = want_tail\n ? `, tails AS (\n SELECT * FROM (\n SELECT *, ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version ASC) AS rn FROM ef\n ) WHERE rn = 1\n )`\n : \"\";\n const tail_join = want_tail\n ? `LEFT JOIN tails t ON t.stream = h.stream`\n : \"\";\n const tail_cols = want_tail\n ? `, t.id AS t_id, t.stream AS t_stream, t.version AS t_version,\n t.name AS t_name, t.data AS t_data, t.created AS t_created, t.meta AS t_meta`\n : \"\";\n\n // No pii column: `query_stats` heads/tails are pii-free (#1294 — see\n // `_query_stats_heads_only`).\n const sql = `\n WITH ef AS (\n SELECT e.id, e.stream, e.version, e.name, e.data, e.created, e.meta\n FROM ${from_clause}\n ${where_clause}\n ),\n agg AS (\n SELECT stream,\n SUM(n) AS cnt,\n json_group_object(name, n) AS names\n FROM (\n SELECT stream, name, COUNT(*) AS n\n FROM ef\n GROUP BY stream, name\n )\n GROUP BY stream\n ),\n heads AS (\n SELECT * FROM (\n SELECT *, ROW_NUMBER() OVER (PARTITION BY stream ORDER BY version DESC) AS rn FROM ef\n ) WHERE rn = 1\n ORDER BY stream${limit !== undefined ? \" LIMIT ?\" : \"\"}\n )\n ${tail_cte}\n SELECT\n h.id, h.stream, h.version, h.name, h.data, h.created, h.meta,\n a.cnt AS agg_count,\n a.names AS agg_names\n ${tail_cols}\n FROM heads h\n LEFT JOIN agg a ON a.stream = h.stream\n ${tail_join}\n ORDER BY h.stream\n `;\n\n // Keyset pagination (#1010): the `heads` CTE is ordered by stream and\n // capped with LIMIT (when set); the final SELECT re-asserts stream\n // order so the returned Map's key order is the next cursor. The LIMIT\n // arg binds last when present (unbounded otherwise).\n const full_args = limit !== undefined ? [...args, limit] : args;\n const res = await this.client.execute({ sql, args: full_args as any[] });\n\n const to_committed = (\n id: unknown,\n stream: unknown,\n version: unknown,\n name: unknown,\n data: unknown,\n meta: unknown,\n created: unknown\n ): Committed<E, keyof E> =>\n ({\n id: Number(id),\n stream: stream as string,\n version: Number(version),\n name: name as string,\n data: parse_json(data as string) as any,\n meta: parse_json(meta as string) as any,\n created: new Date(created as string),\n }) as Committed<E, keyof E>;\n\n const out = new Map<string, StreamStats<E>>();\n for (const row of res.rows) {\n const r = row as unknown as Record<string, unknown>;\n const stats: {\n head: Committed<E, keyof E>;\n tail?: Committed<E, keyof E>;\n count?: number;\n names?: Record<string, number>;\n } = {\n head: to_committed(\n r.id,\n r.stream,\n r.version,\n r.name,\n r.data,\n r.meta,\n r.created\n ),\n };\n if (want_tail && r.t_id !== null && r.t_id !== undefined) {\n stats.tail = to_committed(\n r.t_id,\n r.t_stream,\n r.t_version,\n r.t_name,\n r.t_data,\n r.t_meta,\n r.t_created\n );\n }\n if (want_count) stats.count = Number(r.agg_count);\n // `agg_names` is non-null when this row exists: heads and agg are\n // both built from the same `ef` CTE, so any stream in heads has\n // at least one matching event and `json_group_object` returns a\n // JSON string (never null) for that group.\n if (want_names) stats.names = JSON.parse(r.agg_names as string);\n out.set(r.stream as string, stats as StreamStats<E>);\n }\n return out;\n }\n\n // --- prioritize: bulk priority update with filter (ACT-102) ---\n async prioritize(filter: StreamFilter, priority: number): Promise<number> {\n const { clause, args: filterArgs } = this._filter_clause(filter);\n // libSQL `?` placeholders are positional and NOT reusable, so we\n // bind `priority` twice: once for SET, once for the no-op skip\n // in WHERE.\n const sql = `UPDATE streams SET priority = ?\n WHERE priority <> ? AND ${clause}`;\n const result = await this.client.execute({\n sql,\n args: [priority, priority, ...filterArgs] as any[],\n });\n return result.rowsAffected;\n }\n\n // --- truncate: transactional delete + seed ---\n // Windowed targets (`before` set) prune the prefix below the closest safe\n // `__snapshot__` instead — no seed, subscriptions untouched, no-op when no\n // snapshot qualifies.\n async truncate(\n targets: Array<{\n stream: string;\n snapshot?: Record<string, unknown>;\n meta?: EventMeta;\n before?: Date;\n max_id?: number;\n }>\n ) {\n const result = new Map<\n string,\n {\n deleted: number;\n committed: Committed<Schemas, keyof Schemas>;\n before?: Date;\n }\n >();\n\n const tx = await this.client.transaction(\"write\");\n try {\n for (const { stream, before, max_id } of targets) {\n if (before === undefined) continue;\n // Closest safe boundary: latest snapshot older than the cutoff\n // and at/below the consumer watermark cap. ISO-8601 strings\n // compare lexicographically, so `created < ?` works on the text\n // column. No qualifying snapshot → no-op, absent from the result.\n const snap_row = await tx.execute({\n sql: `SELECT id, stream, version, name, data, meta, created\n FROM events\n WHERE stream = ? AND name = '__snapshot__' AND created < ?\n AND (? IS NULL OR id <= ?)\n ORDER BY id DESC LIMIT 1`,\n args: [stream, before.toISOString(), max_id ?? null, max_id ?? null],\n });\n if (!snap_row.rows.length) continue;\n const row = snap_row.rows[0];\n const boundary_id = Number(row.id);\n const del = await tx.execute({\n sql: \"DELETE FROM events WHERE stream = ? AND id < ?\",\n args: [stream, boundary_id],\n });\n result.set(stream, {\n deleted: del.rowsAffected,\n committed: {\n id: boundary_id,\n stream,\n version: Number(row.version),\n created: new Date(row.created as string),\n name: row.name as string,\n data: parse_json(row.data as string) as any,\n meta: parse_json(row.meta as string) as any,\n },\n before,\n });\n }\n for (const { stream, snapshot, meta, before } of targets) {\n if (before !== undefined) continue;\n const count_row = await tx.execute({\n sql: \"SELECT COUNT(*) as c FROM events WHERE stream = ?\",\n args: [stream],\n });\n const deleted = Number(count_row.rows[0].c);\n await tx.execute({\n sql: \"DELETE FROM events WHERE stream = ?\",\n args: [stream],\n });\n // A restart target carries a seed snapshot: the stream lives on, so\n // its subscription row must survive. Deleting it silently stops\n // reactions whose target is named after the stream — the documented\n // per-aggregate shape `.to(e => ({target: e.stream}))` (#1398).\n if (snapshot === undefined)\n await tx.execute({\n sql: \"DELETE FROM streams WHERE stream = ?\",\n args: [stream],\n });\n\n const event_name =\n snapshot !== undefined ? \"__snapshot__\" : \"__tombstone__\";\n const event_meta = meta ?? { correlation: \"\", causation: {} };\n const now = new Date().toISOString();\n const ins = await tx.execute({\n sql: \"INSERT INTO events (stream, version, name, data, meta, created) VALUES (?, 0, ?, ?, ?, ?)\",\n args: [\n stream,\n event_name,\n JSON.stringify(snapshot ?? {}),\n JSON.stringify(event_meta),\n now,\n ],\n });\n\n result.set(stream, {\n deleted,\n committed: {\n id: Number(ins.lastInsertRowid),\n stream,\n version: 0,\n created: new Date(now),\n name: event_name,\n data: snapshot ?? {},\n meta: event_meta,\n },\n });\n }\n await tx.commit();\n } catch (e) {\n await tx.rollback();\n throw e;\n }\n\n return result;\n }\n\n /**\n * Atomically wipe-and-rebuild the store inside a single libsql\n * `write` transaction.\n *\n * On any throw inside the driver the transaction rolls back and the\n * store is byte-for-byte unchanged. `DELETE FROM events` + `DELETE\n * FROM streams` wipe both tables; `DELETE FROM sqlite_sequence\n * WHERE name = 'events'` resets the autoincrement counter so the\n * new sequence is dense from 1. `created` is preserved verbatim\n * 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 const tx = await this.client.transaction(\"write\");\n try {\n await tx.execute(\"DELETE FROM events\");\n await tx.execute(\"DELETE FROM streams\");\n // Reset the autoincrement counter so the new sequence is dense\n // from 1. `DELETE FROM sqlite_sequence WHERE name = '?'` is the\n // canonical SQLite reset; safe even if the row doesn't exist.\n await tx.execute(\"DELETE FROM sqlite_sequence WHERE name = 'events'\");\n await driver(async (event) => {\n const pii_for_write = await this._stringify_pii_for_write(event.pii);\n const ins = await tx.execute({\n sql: \"INSERT INTO events (stream, version, name, data, meta, created, pii) VALUES (?, ?, ?, ?, ?, ?, ?)\",\n args: [\n event.stream,\n event.version,\n event.name,\n JSON.stringify(event.data),\n JSON.stringify(event.meta),\n event.created.toISOString(),\n pii_for_write,\n ],\n });\n return Number(ins.lastInsertRowid);\n });\n await tx.commit();\n } catch (error) {\n await tx.rollback();\n throw error;\n }\n }\n\n /**\n * Wipe the sensitive-data payload for every event on the stream — the\n * physical-erasure side of the sensitive-data epic (#566). Sets\n * `events.pii` to `NULL` for the stream's events; `events.data` and\n * the rest of the row are never touched.\n *\n * Single `UPDATE` under SQLite's writer lock, bounded by events-per-\n * stream. Idempotent — the `pii IS NOT NULL` predicate filters out\n * already-wiped rows so a second call returns `0`.\n *\n * SQLite doesn't auto-reclaim space; freed pages stay in the file\n * until an operator-scheduled `PRAGMA incremental_vacuum` or a full\n * `VACUUM`. The production checklist documents the cadence.\n *\n * @param stream Target stream\n * @returns Count of events whose `pii` was set to `NULL`\n */\n async forget_pii(stream: string): Promise<number> {\n const r = await this.client.execute({\n sql: \"UPDATE events SET pii = NULL WHERE stream = ? AND pii IS NOT NULL\",\n args: [stream],\n });\n return r.rowsAffected ?? 0;\n }\n}\n"],"mappings":"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","names":["correlated_at"]}
package/dist/index.js CHANGED
@@ -734,7 +734,7 @@ var SqliteStore = class {
734
734
  // --- query_streams: read-only introspection with filters ---
735
735
  async query_streams(callback, query) {
736
736
  const limit = query?.limit ?? 100;
737
- let sql = "SELECT stream, source, at, retry, blocked, error, leased_by, leased_until, priority, lane, deferred_at FROM streams WHERE 1=1";
737
+ let sql = "SELECT stream, source, at, retry, blocked, error, leased_by, leased_until, priority, lane, deferred_at, correlated_at FROM streams WHERE 1=1";
738
738
  const args = [];
739
739
  if (query?.stream !== void 0) {
740
740
  if (query.stream_exact) {
@@ -777,6 +777,7 @@ var SqliteStore = class {
777
777
  for (const row of streamsResult.rows) {
778
778
  const leased_until = row.leased_until;
779
779
  const deferred_at = row.deferred_at;
780
+ const correlated_at = row.correlated_at;
780
781
  callback({
781
782
  stream: row.stream,
782
783
  source: row.source ?? void 0,
@@ -788,7 +789,8 @@ var SqliteStore = class {
788
789
  leased_by: row.leased_by ?? void 0,
789
790
  leased_until: leased_until ? new Date(leased_until) : void 0,
790
791
  lane: row.lane,
791
- deferred_at: deferred_at ? new Date(deferred_at).getTime() : void 0
792
+ deferred_at: deferred_at ? new Date(deferred_at).getTime() : void 0,
793
+ correlated_at: correlated_at ?? void 0
792
794
  });
793
795
  count++;
794
796
  }