@thomasfosterau/legation 0.0.0-stage → 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["Collection.filterDeclarationOf","Hash.contentHash","make","Sql.sqlite","Predicate.test","Sql.compileWhere","Predicate.isNullish","Subset.apply","Predicate.resolveExists","Sql.compileSelect","Predicate.isIn","UnknownMutator.make","Backend.transaction","make","InvalidationBus.InvalidationBus"],"sources":["../../src/sql/Sql.ts","../../src/sql/SqlBackend.ts","../../src/sql/PollingHintSource.ts","../../src/sql/PgExecutor.ts"],"sourcesContent":["/**\n * Sql\n *\n * Translate a `Subset`'s predicate into a parameterised SQL `WHERE` clause.\n * The translation is pure and dialect-aware (placeholder, identifier quoting,\n * collation name, boolean encoding), and it is **exact**: every node compiles\n * to a SQL boolean expression whose three-valued result on a row equals\n * `Predicate.evaluate` on that row (ADR-0028 §6), so the `WHERE` returns\n * precisely the rows the JS evaluator returns `Some(true)` for — on Postgres\n * (normative) and on SQLite alike. The differential harness\n * (`__tests__/differential.test.ts`, issue #153) proves it against a real\n * Postgres and SQLite over the generated corpus, set and per-row verdict\n * both. The `SqlBackend` still finalises order/limit/cursor locally with\n * `Subset.apply`; that is redundancy, not correction.\n *\n * Predicate → SQL:\n * - `True` → `1 = 1`\n * - `Compare` → `col <op> ?`; a `null` literal binds as `NULL`, so the\n * comparison is unknown for every row — exactly `Predicate.evaluate`\n * (ADR-0028 §5). `IS NULL` is the `IsNull` node, which\n * `Predicate.eq(f, null)` desugars to.\n * - `In` → `col IN (?, ?, …)` (empty set → `1 = 0`)\n * - `NotIn` → `col NOT IN (?, ?, …)` (empty set → `1 = 1`)\n * - `IsNull` → `col IS NULL` / `col IS NOT NULL`\n * - `And` / `Or` → parenthesised joins (empty And → `1 = 1`, empty Or → `1 = 0`)\n * - `Not` → `NOT (…)`\n * - `Exists` → `localField IN (SELECT foreignField FROM collection WHERE …)`\n *\n * **Literal kinds (`kinds` option).** `compileWhere` and `compileSelect`\n * accept a field → `Predicate.LiteralKind` map (`text`, `integer`, `double`,\n * `boolean`, `instant`), the per-attribute declaration of ADR-0028 §4. It\n * drives two things:\n *\n * - **The collation clause.** A `Compare`, `In` or `NotIn` on a field\n * declared `text` carries an explicit code-point collation on the column\n * side — `\"col\" COLLATE \"C\"` on Postgres, `\"col\" COLLATE BINARY` on\n * SQLite — so text orders by code point even where the column's own\n * collation is not `\"C\"`. The clause is emitted for a declared `text`\n * field only, never inferred from a string literal: a string may be bound\n * against a `uuid`, `date`, enum or `timestamptz` column, none of which\n * accepts a collation, and an `instant` compares chronologically without\n * one. A field without a declaration compiles to a plain comparison and\n * orders by the column's own collation — hence the schema rule, which\n * stands regardless: filterable and sortable text columns are declared\n * `COLLATE \"C\"` (Postgres) or left at `BINARY` (SQLite). Nothing checks\n * DDL.\n * - **Typed binds (rule (d)).** A literal of the declared kind's JS type\n * binds as that type: strings for `text` and `instant`, numbers for\n * `integer` and `double` (SQLite stores an integral number as INTEGER and\n * the rest as REAL; a Postgres driver sends text the server infers against\n * the column), and booleans as the dialect stores them (Postgres\n * `true`/`false`, SQLite `1`/`0`). A literal of another JS type is a\n * cross-type comparison, which the grammar cannot express and the sync\n * tier rejects; the compiler gives it `Predicate.evaluate`'s verdict —\n * unknown — by binding `NULL` in its place, so SQLite's affinity coercion\n * (`1 = '1'`) never sees the raw literal. In an `In`/`NotIn` list such\n * literals are dropped (a mismatch can match nothing), and a non-empty\n * list left with no literal binds one `NULL`, which is unknown.\n *\n * Without a declaration a literal binds as its JS type (booleans still per\n * dialect) and nothing collates. The inner `where` of an `Exists` names the\n * foreign collection's fields, so it takes that collection's entry in\n * `foreignKinds` (collection → kinds); `SqlBackend` passes every mapping's\n * kinds through.\n */\n\nimport type { Predicate } from \"../index.js\";\nimport * as Arr from \"effect/Array\";\nimport * as P from \"effect/Predicate\";\n\n/** A SQL fragment plus its ordered bind parameters. */\nexport interface SqlFragment {\n readonly text: string;\n readonly params: ReadonlyArray<unknown>;\n}\n\n/** Placeholder, identifier-quoting, collation and boolean rules for a SQL dialect. */\nexport interface Dialect {\n /** Bind placeholder for the 1-based parameter index. */\n readonly placeholder: (index: number) => string;\n /** Quote a column/table identifier. */\n readonly quote: (identifier: string) => string;\n /** The code-point collation name, emitted as `COLLATE <name>` after a declared-text operand. */\n readonly textCollation: string;\n /** How a boolean literal binds (Postgres has booleans; SQLite stores `0`/`1`). */\n readonly boolean: (value: boolean) => unknown;\n /** The larger of two SQL expressions: `MAX` on SQLite, `GREATEST` on PostgreSQL. */\n readonly greatest: (left: string, right: string) => string;\n /**\n * The statement that makes the transaction it runs in SERIALIZABLE, run first\n * in each `applyMutations` transaction (#353). It must be the transaction's first\n * statement in a top-level transaction: a nested one refuses it, and the batch\n * then fails. Absent on a dialect whose writers already serialise (SQLite takes\n * its write lock with `BEGIN IMMEDIATE`); a hand-written dialect without it gets\n * no isolation statement, so its executor must provide the isolation itself.\n */\n readonly serializable?: string | undefined;\n /**\n * How each arm of `SqlBackend.evaluateMany`'s one `UNION ALL` statement\n * carries its row (#505), since mappings' columns differ per collection.\n * Absent on a hand-written dialect, `evaluateMany` is too, and a pull\n * evaluates its subsets one statement each. See {@link UnionRows}.\n */\n readonly unionRows?: UnionRows | undefined;\n}\n\n/**\n * How a `UNION ALL` arm carries a row whose columns differ from the other\n * arms' (`Dialect.unionRows`, #505):\n *\n * - `Columns`: the relation's own columns by position, padded with `NULL` to\n * the widest arm. Only for a database whose `UNION ALL` columns take any\n * type per row (SQLite): each value then reaches the driver as `SELECT *`\n * would give it. `columnsOf` lists a relation's columns, one row per column\n * with its name in a `name` column.\n * - `Json`: one column of JSON text, `rowJson(alias)` for the row aliased\n * `alias` (PostgreSQL's `row_to_json`). JSON carries text, booleans, `NULL`\n * and numbers that fit a double as the driver does; other column types reach\n * the backend as JSON gives them.\n */\nexport type UnionRows =\n | {\n readonly _tag: \"Columns\";\n readonly columnsOf: (relation: string) => SqlFragment;\n }\n | {\n readonly _tag: \"Json\";\n readonly rowJson: (alias: string) => string;\n };\n\n/** Field → literal kind, for the collation clause and typed binds. */\nexport type Kinds = Readonly<Record<string, Predicate.LiteralKind>>;\n\n/** Options shared by {@link compileWhere} and {@link compileSelect}. */\nexport interface CompileOptions {\n /** Declared literal kinds of the predicate's own fields (see the module doc). */\n readonly kinds?: Kinds | undefined;\n /** Declared kinds of other collections' fields, keyed by collection, for the inner `where` of an `Exists`. */\n readonly foreignKinds?: Readonly<Record<string, Kinds>> | undefined;\n}\n\nconst quoteDouble = (identifier: string): string => `\"${identifier.replace(/\"/g, '\"\"')}\"`;\n\n/**\n * SQLite: `?` placeholders, `COLLATE BINARY`, booleans as `0`/`1`, a `UNION\n * ALL` arm's row as its own columns by position (`pragma_table_info`).\n */\nexport const sqlite: Dialect = {\n placeholder: () => \"?\",\n quote: quoteDouble,\n textCollation: \"BINARY\",\n boolean: (value) => (value ? 1 : 0),\n greatest: (left, right) => `MAX(${left}, ${right})`,\n unionRows: {\n _tag: \"Columns\",\n columnsOf: (relation) => ({\n text: `SELECT name FROM pragma_table_info(?)`,\n params: [relation],\n }),\n },\n};\n\n/**\n * PostgreSQL: `$1`, `$2`, … placeholders, `COLLATE \"C\"`, native booleans,\n * SERIALIZABLE transactions, a `UNION ALL` arm's row as JSON (`row_to_json`).\n */\nexport const postgres: Dialect = {\n placeholder: (index) => `$${index}`,\n quote: quoteDouble,\n textCollation: `\"C\"`,\n boolean: (value) => value,\n greatest: (left, right) => `GREATEST(${left}, ${right})`,\n serializable: \"SET TRANSACTION ISOLATION LEVEL SERIALIZABLE\",\n unionRows: { _tag: \"Json\", rowJson: (alias) => `row_to_json(${alias})::text` },\n};\n\nconst comparators: Record<Predicate.Comparison, string> = {\n eq: \"=\",\n ne: \"<>\",\n gt: \">\",\n gte: \">=\",\n lt: \"<\",\n lte: \"<=\",\n};\n\n/** Accumulates bind params so placeholders get the right 1-based index. */\nclass Params {\n readonly values: Array<unknown> = [];\n constructor(private readonly dialect: Dialect) {}\n bind(value: unknown): string {\n this.values.push(value);\n return this.dialect.placeholder(this.values.length);\n }\n}\n\n/** One compilation's context: the dialect, the bind accumulator and the declared kinds. */\ninterface Compilation {\n readonly dialect: Dialect;\n readonly params: Params;\n readonly kinds: Kinds | undefined;\n readonly foreignKinds: Readonly<Record<string, Kinds>> | undefined;\n}\n\n/** The JS type a literal of each kind has. */\nconst jsTypeOf: Record<Predicate.LiteralKind, \"string\" | \"number\" | \"boolean\"> = {\n text: \"string\",\n instant: \"string\",\n integer: \"number\",\n double: \"number\",\n boolean: \"boolean\",\n};\n\n/** Whether a literal can bind as the declared kind: `null` always, else by JS type. */\nconst conforms = (kind: Predicate.LiteralKind | undefined, value: Predicate.Literal): boolean =>\n value === null || kind === undefined || typeof value === jsTypeOf[kind];\n\n/** The column operand: quoted, with the collation clause when declared `text`. */\nconst operand = (compilation: Compilation, field: string): string => {\n const column = compilation.dialect.quote(field);\n return compilation.kinds?.[field] === \"text\"\n ? `${column} COLLATE ${compilation.dialect.textCollation}`\n : column;\n};\n\n/**\n * Bind one literal under the field's declared kind: a conforming literal as\n * itself (booleans as the dialect stores them), a cross-type literal as\n * `NULL` (unknown, rule (d)).\n */\nconst bindLiteral = (compilation: Compilation, field: string, value: Predicate.Literal): string => {\n const { params, dialect, kinds } = compilation;\n if (!conforms(kinds?.[field], value)) return params.bind(null);\n return params.bind(P.isBoolean(value) ? dialect.boolean(value) : value);\n};\n\n/**\n * The literals of an `In`/`NotIn` list that can bind under the declared\n * kind. A cross-type literal matches nothing, so it is dropped; a non-empty\n * list left with nothing binds one `NULL`, which makes the membership\n * unknown — `Predicate.evaluate`'s verdict when no listed literal has the\n * field's type.\n */\nconst bindableList = (\n compilation: Compilation,\n field: string,\n values: ReadonlyArray<Predicate.Literal>\n): ReadonlyArray<Predicate.Literal> => {\n const kind = compilation.kinds?.[field];\n const kept = values.filter((value) => conforms(kind, value));\n return Arr.isReadonlyArrayEmpty(kept) ? [null] : kept;\n};\n\ntype CompareNode = Extract<Predicate.Predicate, { readonly _tag: \"Compare\" }>;\ntype ListNode = Extract<Predicate.Predicate, { readonly _tag: \"In\" | \"NotIn\" }>;\ntype ExistsNode = Extract<Predicate.Predicate, { readonly _tag: \"Exists\" }>;\n\nconst compileCompare = (node: CompareNode, compilation: Compilation): string =>\n `${operand(compilation, node.field)} ${comparators[node.op]} ${bindLiteral(compilation, node.field, node.value)}`;\n\n/** `In` / `NotIn`: an empty list is the constant `FALSE` / `TRUE` (ADR-0028 §3). */\nconst compileList = (node: ListNode, compilation: Compilation): string => {\n if (Arr.isReadonlyArrayEmpty(node.values)) return node._tag === \"In\" ? \"1 = 0\" : \"1 = 1\";\n const placeholders = bindableList(compilation, node.field, node.values)\n .map((value) => bindLiteral(compilation, node.field, value))\n .join(\", \");\n return `${operand(compilation, node.field)} ${node._tag === \"In\" ? \"IN\" : \"NOT IN\"} (${placeholders})`;\n};\n\n/** Correlated set membership: `localField IN (SELECT foreignField FROM … WHERE …)`. */\nconst compileExists = (node: ExistsNode, compilation: Compilation): string => {\n // The inner `where` names the foreign collection's fields: its kinds come\n // from `foreignKinds`, never from the outer collection's `kinds`.\n const kinds = compilation.foreignKinds?.[node.collection];\n const inner = compileNode(node.where, { ...compilation, kinds });\n const { quote } = compilation.dialect;\n return `${quote(node.localField)} IN (SELECT ${quote(node.foreignField)} FROM ${quote(node.collection)} WHERE ${inner})`;\n};\n\nconst compileNode = (predicate: Predicate.Predicate, compilation: Compilation): string => {\n switch (predicate._tag) {\n case \"True\":\n return \"1 = 1\";\n case \"Compare\":\n return compileCompare(predicate, compilation);\n case \"In\":\n case \"NotIn\":\n return compileList(predicate, compilation);\n case \"IsNull\":\n return `${compilation.dialect.quote(predicate.field)} IS ${predicate.negated === true ? \"NOT \" : \"\"}NULL`;\n case \"And\":\n if (Arr.isReadonlyArrayEmpty(predicate.predicates)) return \"1 = 1\";\n return predicate.predicates.map((inner) => compileNode(inner, compilation)).join(\" AND \");\n case \"Or\": {\n if (Arr.isReadonlyArrayEmpty(predicate.predicates)) return \"1 = 0\";\n return `(${predicate.predicates.map((inner) => compileNode(inner, compilation)).join(\" OR \")})`;\n }\n case \"Not\":\n return `NOT (${compileNode(predicate.predicate, compilation)})`;\n case \"Exists\":\n return compileExists(predicate, compilation);\n case \"InSet\":\n // An unbound named set is the empty set (#205): the sync server binds\n // every `InSet` before a subset reaches a backend, so this is the\n // fail-closed fallback, like `IN ()`.\n return \"1 = 0\";\n }\n};\n\n/**\n * Compile a predicate into a `WHERE`-ready fragment for a dialect. Pass\n * `options.kinds` (field → literal kind) so declared-text fields get their\n * collation clause and literals bind by the declared kind; without a\n * declaration nothing collates and a literal binds as its JS type (see the\n * module doc).\n */\nexport const compileWhere = (\n predicate: Predicate.Predicate,\n dialect: Dialect = sqlite,\n options: CompileOptions = {}\n): SqlFragment => {\n const params = new Params(dialect);\n const text = compileNode(predicate, {\n dialect,\n params,\n kinds: options.kinds,\n foreignKinds: options.foreignKinds,\n });\n return { text, params: params.values };\n};\n\n/**\n * Compile a `SELECT * FROM <table> WHERE <predicate>` for a subset. The\n * `WHERE` is exact (see the module doc). Order, limit and cursor are not\n * pushed down: this statement may later grow an `ORDER BY … LIMIT` for\n * backends that want it, but today the backend's `Subset.apply` finalises\n * them locally, so this stays a single, safe statement.\n */\nexport const compileSelect = (\n options: {\n readonly table: string;\n readonly where: Predicate.Predicate;\n readonly dialect?: Dialect;\n } & CompileOptions\n): SqlFragment => {\n const dialect = options.dialect ?? sqlite;\n const { text, params } = compileWhere(options.where, dialect, {\n kinds: options.kinds,\n foreignKinds: options.foreignKinds,\n });\n return { text: `SELECT * FROM ${dialect.quote(options.table)} WHERE ${text}`, params };\n};\n","/**\n * SqlBackend\n *\n * A reference implementation of the core `Backend` contract (ADR-0009) over a\n * SQL database, addressing the audit's \"turnkey backend\" gap with a\n * driver-agnostic adapter: the app provides a tiny `SqlExecutor` (wrapping\n * `better-sqlite3`, `effect/sql`, `pg`, …) and per-collection table\n * mappings, and this backend supplies subset evaluation, row fetching, and the\n * transactional mutation-id ledger.\n *\n * Reads go through the `Sql` translator (`Subset` predicate → `WHERE`, exact\n * under ADR-0028 on both dialects), then `Subset.apply` finalises\n * order/limit/cursor locally. A table mapping may declare its columns'\n * literal kinds (`TableMapping.kinds`) so the compiler collates declared-text\n * columns and binds literals by kind (booleans per dialect); the row decoder\n * maps SQLite's `0`/`1` back to booleans for columns declared `boolean`, and\n * otherwise leaves rows as the driver returns them (values stay opaque,\n * ADR-0014). Writes run app-provided handlers inside one transaction (on an\n * executor that has transactions), and each mutation is first claimed in the\n * client's ledger row: a conditional UPDATE\n * from `id - 1` to `id` that runs before its handler. The database evaluates\n * that WHERE against the latest committed row, so of two calls racing for one\n * mutation only one claim matches and the other is a replay the ledger covers.\n * The watermark only ever moves from `id - 1` to `id`, so it cannot fall. This\n * gives the contract's exactly-once, in-order guarantee for one client group,\n * with the claim alone needing no stronger isolation than READ COMMITTED.\n * An executor without transactions (`transactional: false`) cannot commit a\n * claim together with its handler's writes, so it keeps the handler-then-bump\n * order instead (see D1 below).\n *\n * A permanent rejection is remembered in the client's ledger row, in the same\n * transaction as the claim (#366), in a `rejections` column that holds a JSON\n * object of mutation id → reason. A replayed copy lists its mutation in\n * `rejected` with the stored reason, so every copy reports the committed outcome\n * (guarantee 1). A copy that a concurrent call committed first is dropped on\n * each dialect: on SQLite it lists its mutation in `rejected` directly; on\n * PostgreSQL it is refused with a transient `BackendError` (SQLSTATE 40001), and\n * its retry, a replay, lists it. A push forgets the rejections below its lowest\n * id per client. The memory is bounded per client: a client that never pushes\n * again keeps its last push's rejections. It assumes one journal per `clientId`,\n * which backend contiguity already requires.\n *\n * Mutations from different client groups are serialisable (guarantee 1, #353),\n * per dialect:\n *\n * - SQLite serialises transactions on its one connection, and the client takes\n * the write lock with `BEGIN IMMEDIATE`, so a handler's reads and its writes\n * never interleave with another group's transaction. It conforms.\n * - PostgreSQL runs each `applyMutations` transaction at SERIALIZABLE: the\n * dialect's `serializable` statement (`SET TRANSACTION ISOLATION LEVEL\n * SERIALIZABLE`) runs first, before any read. It must be the transaction's\n * first statement in a top-level transaction: PostgreSQL refuses it inside a\n * subtransaction (a nested `transaction`, which a savepoint runs), so\n * `applyMutations` then fails with a `BackendError` rather than running at\n * READ COMMITTED. A transaction that read a row another transaction changed\n * and committed first is refused at its write with SQLSTATE 40001, and rolls\n * back. The executor reports that as a\n * `BackendError`, which the client treats as transient and retries, and the\n * retry runs the handler on the committed state. A handler therefore never\n * commits a write computed from a stale read. This is not retried inside the\n * backend: the client already re-sends an unacknowledged batch, so the\n * transient path covers it without a second retry policy. A PostgreSQL\n * executor that sets `transactional: false` loses this guarantee, since the\n * ordering then has no transaction to isolate.\n * - D1 is non-conforming (#342, #353). It has no interactive transactions, so its\n * executor sets `transactional: false` and each statement commits on its own.\n * `applyMutations` then keeps trunk's order: the handler runs, then the\n * ledger is bumped to the larger of the stored and the new id, so an\n * overlapping call that finishes late cannot move the watermark back. The\n * handler's writes commit before the bump, so a pull between the two carries\n * the effect without the watermark; a crash after the writes and before the\n * bump makes the retry run the handler again; and a transient failure leaves\n * the writes committed for the retry to repeat. A rejection recorded before a\n * failed bump is forgotten when the retry applies the id (#425). Nothing\n * isolates a handler's reads from another group's writes, so a lost update is\n * possible. The claim order is not used on D1 because a claim committed on its\n * own would leave a crash between the claim and the handler's commit claimed\n * and unapplied, so a retry would skip the mutation (#359).\n *\n * An executor on another database must provide the same isolation, or the\n * handlers' reads must be version-checked, before it can claim guarantee 1.\n *\n * A mapping's reads run against `readRelation` when given (issue #204): a\n * real app's current-state relation is often a view — one that resolves each\n * node's current version and joins the leaf at exactly that version — never\n * the table mutator handlers write through. A mapping's `tombstone`\n * predicate (the engine's own `Predicate` vocabulary, evaluated with\n * `Predicate.test`) marks a row a soft delete rather than a live one: such a\n * row is dropped from `evaluate`'s returned entries, so it simply isn't in\n * the next CVR view and `Cvr.diff` reports it as a delete for that id,\n * exactly as a row that vanished from the relation outright would (fail-open\n * would be a bug: a soft-deleted row must never keep syncing as a put).\n *\n * A pull's subsets are evaluated together by `evaluateMany` (#505): one\n * `UNION ALL` statement whose arms each select their subset's index and their\n * row, plus one statement per `Exists` node, as in `evaluate`. On SQLite, whose\n * `UNION ALL` columns take any type per row, an arm carries the relation's own\n * columns by position, so each value reaches the backend exactly as `evaluate`\n * reads it. On PostgreSQL an arm carries its row as JSON (`row_to_json`): the\n * two agree wherever JSON carries a column's value as the driver does\n * (`BackendContract` rule 14 checks it over the generated corpus), the README\n * lists the column types it does not, and a pull whose rows hold a number\n * JSON cannot carry exactly (an integer beyond ±(2^53 − 1), a `numeric` with\n * more digits than a double, such a number inside a `jsonb` column) is\n * answered by `evaluate` per subset instead, with an {@link InexactJsonNumber}\n * warning naming the column. Integer, double and text values belong in typed\n * columns (`integer`, `double precision`, `text`) rather than in JSON. The rows read back are finished\n * by the same code as `evaluate`'s.\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport * as MutableHashMap from \"effect/MutableHashMap\";\nimport * as MutableHashSet from \"effect/MutableHashSet\";\nimport * as Option from \"effect/Option\";\nimport * as P from \"effect/Predicate\";\nimport * as R from \"effect/Record\";\nimport * as Schema from \"effect/Schema\";\nimport {\n Backend,\n BackendError,\n Collection,\n MutationRejected,\n Predicate,\n type Mutator,\n} from \"../index.js\";\nimport { Hash, Subset, UnknownMutator } from \"../internal.js\";\nimport * as Sql from \"./Sql.js\";\n\n// Decode pushed args against the mutator's schema (untrusted input).\nconst decodeArgs = (\n definition: Mutator.AnyMutator,\n args: unknown,\n name: string\n): Effect.Effect<unknown, BackendError> =>\n Schema.decodeUnknownEffect(definition.args)(args).pipe(\n Effect.mapError(\n (cause) => new BackendError({ message: `Invalid args for mutator \"${name}\"`, cause })\n )\n );\n\n/** One pushed mutation, as `applyMutations` receives it. */\ntype Mutation = Parameters<Backend.Backend[\"applyMutations\"]>[1][number];\n\n/** A push's state: its context, and the counter that names each handler run's savepoint. */\ninterface SqlBatch {\n readonly context: Backend.SyncContext;\n readonly savepoints: { next: number };\n}\n\n/**\n * One opened mutation: its context, its savepoint, and, without transactions,\n * whether the ledger remembers a rejection for its id that a failed bump left\n * behind (#425).\n */\ninterface SqlUnit {\n readonly context: Backend.SyncContext;\n readonly mutation: Mutation;\n readonly savepoint: string;\n readonly staleRejection: boolean;\n}\n\n/** The `rejections` column's value: a JSON object of mutation id → reason, or `null` when none is remembered. */\nconst encodeRejections = (rejections: ReadonlyMap<number, string>): string | null =>\n rejections.size === 0 ? null : JSON.stringify(Object.fromEntries(rejections));\n\n/** Decode the `rejections` column (`null` and absent both mean none remembered). */\nconst decodeRejections = (\n text: unknown,\n mutationsTable: string\n): Effect.Effect<ReadonlyMap<number, string>, BackendError> => {\n if (text === null || text === undefined) return Effect.succeed(new Map());\n return Effect.try({\n try: () => {\n if (!P.isString(text)) throw new TypeError(\"not text\");\n return new Map(\n Object.entries(JSON.parse(text) as Record<string, string>).map(\n ([id, reason]) => [Number(id), String(reason)] as const\n )\n );\n },\n catch: (cause) =>\n new BackendError({\n message: `The \"rejections\" column of the mutation ledger \"${mutationsTable}\" holds a value that is not a rejection memory`,\n cause,\n }),\n });\n};\n\n/** A row as returned by the driver: a column → value record. */\nexport type SqlRow = Record<string, unknown>;\n\n/**\n * The minimal database surface the backend needs.\n *\n * **Isolation (guarantee 1, #353):** `transaction` must run its effect as one\n * database transaction whose reads a concurrent transaction cannot change under\n * them before it commits, so a handler never writes from a stale read. That is\n * SERIALIZABLE isolation, or an equivalent check, and the reference executors\n * meet it: SQLite serialises writers (`BEGIN IMMEDIATE`), and the PostgreSQL\n * dialect's `serializable` statement sets SERIALIZABLE on each transaction. On\n * PostgreSQL that transaction must be top-level, not a nested one run as a\n * savepoint: the statement is refused there, and `applyMutations` fails with a\n * `BackendError`. An executor whose database cannot do this does not conform. A\n * serialization failure (PostgreSQL SQLSTATE 40001 or 40P01) must surface as a\n * `BackendError`, so the client retries the batch; the D1 executor does not meet\n * the requirement (`transactional: false`, #342).\n */\nexport interface SqlExecutor {\n /** Run a query and return its rows. */\n readonly query: (fragment: Sql.SqlFragment) => Effect.Effect<ReadonlyArray<SqlRow>, BackendError>;\n /** Run a statement for its effect (insert/update/delete). */\n readonly run: (fragment: Sql.SqlFragment) => Effect.Effect<void, BackendError>;\n /**\n * Run an effect inside a transaction (atomic commit / rollback on error). On\n * PostgreSQL it must open a top-level transaction, not a savepoint nested in\n * another one, so SERIALIZABLE can be set first (see the `dialect` option of\n * `SqlBackendConfig`).\n */\n readonly transaction: <A, R>(\n effect: Effect.Effect<A, BackendError, R>\n ) => Effect.Effect<A, BackendError, R>;\n /**\n * Whether `transaction` commits and rolls back atomically; defaults to `true`.\n *\n * Set `false` on an executor without transactions (D1). `applyMutations` then\n * bumps each id after its handler instead of claiming it first, because a\n * claim committed on its own would be skipped by a retry after a crash.\n */\n readonly transactional?: boolean | undefined;\n}\n\n/** How a collection maps onto a table. */\nexport interface TableMapping {\n /** The write target: what mutator handlers address. Also the read default. */\n readonly table: string;\n /**\n * The relation reads evaluate against, when it differs from `table`\n * (issue #204) — typically a view joining a root version table to a leaf\n * at its current version. Read-only: `SqlBackend` never writes rows\n * itself, only the mutation-id ledger, so nothing here needs `table` to be\n * writable. Defaults to `table`.\n */\n readonly readRelation?: string;\n /** Primary-key column. Defaults to `\"id\"`. */\n readonly idColumn?: string;\n /** Column holding the row version; if absent, a content hash is used. */\n readonly versionColumn?: string;\n /**\n * Declared literal kinds of the filterable columns (field → kind), passed\n * to the `Sql` compiler: a column declared `text` gets the code-point\n * collation clause (the clause needs the declaration; a string literal\n * alone never collates), `instant` columns do not, literals bind by the\n * declared kind, and `boolean` columns bind and decode as the dialect\n * stores them (`0`/`1` on SQLite). A column without a kind compiles to a\n * plain comparison with the literal bound as its JS type (see `Sql`). See\n * {@link fromCollection} to derive this instead of writing it by hand.\n */\n readonly kinds?: Sql.Kinds;\n /**\n * A row matching this predicate is a tombstone, not a live row (issue\n * #204): `evaluate` drops it from the entries it returns, so it is simply\n * absent from the next CVR view and diffs as a delete, the same as a row\n * that vanished from the relation outright. Expressed in the engine's own\n * `Predicate` vocabulary (evaluated with `Predicate.test`) rather than a\n * bespoke shape, so it composes with everything else that already\n * understands a `Predicate` (the `Sql` compiler, the differential\n * harness). Typically an `IsNull`/`eq` test on a soft-delete column\n * (`Predicate.isNotNull(\"deletedAt\")`'s negation, an `action` enum check).\n */\n readonly tombstone?: Predicate.Predicate;\n}\n\n/**\n * Derive a `TableMapping` from a `Collection`: column kinds come from its\n * declared filterable set (`Collection.filterDeclarationOf`, #154) instead of\n * being restated by hand — the common case. `table`, and optionally\n * `readRelation` / `idColumn` / `versionColumn` / `tombstone`, are still\n * given explicitly; the hand-written record shape (this function's return\n * type) remains the override path for a mapping derivation can't cover.\n */\nexport const fromCollection = (\n collection: Collection.AnyCollection,\n options: {\n readonly table: string;\n readonly readRelation?: string;\n readonly idColumn?: string;\n readonly versionColumn?: string;\n readonly tombstone?: Predicate.Predicate;\n }\n): TableMapping => {\n const { filterable } = Collection.filterDeclarationOf(collection);\n const kinds: Sql.Kinds = Object.fromEntries(\n Object.entries(filterable).map(([field, declared]) => [field, declared.kind])\n );\n return { ...options, kinds };\n};\n\n/**\n * An app-authored write, running its own SQL inside the push transaction. Fail\n * with `BackendError` for a transient error (aborts the whole push) or\n * `MutationRejected` for a permanent one (this mutation's writes are rolled back\n * to a savepoint, its id still bumped, the batch continues).\n */\nexport type SqlHandler = (\n args: unknown,\n tx: SqlExecutor,\n context: Backend.SyncContext\n) => Effect.Effect<ReadonlyArray<Backend.ChangeHint>, BackendError | MutationRejected>;\n\n/** Configuration for {@link make}. */\nexport interface SqlBackendConfig {\n readonly executor: SqlExecutor;\n readonly tables: Readonly<Record<string, TableMapping>>;\n /** Mutator definitions, for decoding/validating pushed args. */\n readonly mutators: ReadonlyMap<string, Mutator.AnyMutator>;\n readonly handlers: Readonly<Record<string, SqlHandler>>;\n /**\n * The SQL dialect. Defaults to SQLite (`Sql.sqlite`). A PostgreSQL app must pass\n * `Sql.postgres`: that sets SERIALIZABLE on each transaction, and omitting it runs\n * the batches at READ COMMITTED with no error. A hand-written dialect without a\n * `serializable` statement gets no isolation statement, so its executor must\n * provide the isolation itself (see {@link SqlExecutor}).\n */\n readonly dialect?: Sql.Dialect | undefined;\n /**\n * Per-client mutation-id ledger table. Defaults to `\"sync_mutations\"`. It must\n * have the `rejections` column that #366 added: an older table fails the push\n * with a `BackendError` naming the migration (see the sql README).\n */\n readonly mutationsTable?: string;\n}\n\n/**\n * The alias each arm of `evaluateMany`'s statement gives its relation: a\n * `Json` dialect's `rowJson` names the row by it, so it must not be a column\n * of any mapping.\n */\nconst MANY_ROW_ALIAS = \"__legation_row\";\n\n/** The subset index each row of `evaluateMany`'s statement carries. */\nconst ManyIndex = Schema.Struct({ subset: Schema.Int });\n\n/** A row a `Json` dialect's arm carries: JSON text holding an object. */\nconst ManyJsonRow = Schema.fromJsonString(Schema.Record(Schema.String, Schema.Unknown));\n\n/** A failure to decode a row of `evaluateMany`'s statement, as a `BackendError`. */\nconst undecodable = (cause: unknown) =>\n new BackendError({ message: \"evaluateMany read a row it could not decode\", cause });\n\n/** The subset index of a row of `evaluateMany`'s statement. */\nconst decodeManyIndex = (row: SqlRow) =>\n Schema.decodeUnknownEffect(ManyIndex)(row).pipe(Effect.mapError(undecodable));\n\n/** The row a `Json` dialect's arm carried, from its JSON text. */\nconst decodeManyJson = (text: string) =>\n Schema.decodeEffect(ManyJsonRow)(text).pipe(Effect.mapError(undecodable));\n\n/**\n * Why a pull fell back from its batched statement to one `evaluate` per\n * subset: a number in a JSON row (PostgreSQL's `row_to_json`) that JSON\n * cannot carry exactly (#505). `JSON.parse` reads every JSON number as a\n * double, so one with more significant digits than a double holds (a `bigint`\n * past ±(2^53 − 1), a `numeric` of high precision, such a number inside a\n * `json` / `jsonb` column) would read back as a different value than the\n * database holds, and an integer past ±(2^53 − 1) is read by the driver as\n * text, not a number. `evaluateMany` then answers the pull with `evaluate`\n * per subset, whose values are the driver's, and logs this once per\n * collection and column at `Warning` level, as the log's cause: declare the\n * column `integer`, `double precision` or `text` in the read relation (a view\n * can cast it) so the pull stays one statement.\n *\n * Logged, never raised: the pull still succeeds.\n */\nexport class InexactJsonNumber extends Schema.TaggedError<\n InexactJsonNumber,\n { readonly brand: unique symbol }\n>()(\"InexactJsonNumber\", {\n message: Schema.String,\n /** The collection whose read relation holds the column. */\n collection: Schema.String,\n /** The read relation's column the number sits in (top-level key of the row's JSON). */\n column: Schema.String,\n}) {}\n\n/**\n * A JSON number's decimal value, as `<sign><digits>e<scale>` with the digits'\n * leading and trailing zeros dropped (the value is `0.<digits> × 10^scale`),\n * so two spellings of one value (`1.50`, `15e-1`) give the same key.\n */\nconst decimalKey = (token: string): string => {\n const match = /^(-?)(\\d+)(?:\\.(\\d+))?(?:[eE]([+-]?\\d+))?$/.exec(token);\n if (match === null) return token;\n const [, sign = \"\", whole = \"\", fraction = \"\", exponent = \"0\"] = match;\n const digits = `${whole}${fraction}`;\n const significant = digits.replace(/^0+/, \"\");\n const trimmed = significant.replace(/0+$/, \"\");\n if (trimmed === \"\") return \"0\";\n const scale = whole.length - (digits.length - significant.length) + Number(exponent);\n return `${sign}${trimmed}e${scale}`;\n};\n\n/**\n * Whether `JSON.parse` reads a JSON number token as exactly the value it\n * spells, and as a number the driver would also give: its decimal value equals\n * the double it parses to, and that double is not an integer past\n * ±(2^53 − 1) (which the driver reads as text).\n */\nconst isExactJsonNumber = (token: string): boolean => {\n const value = Number(token);\n return (\n (!Number.isInteger(value) || Number.isSafeInteger(value)) &&\n decimalKey(token) === decimalKey(String(value))\n );\n};\n\n/** JSON's tokens, for {@link inexactColumns}: strings, numbers and punctuation. */\nconst JSON_TOKEN = /\"(?:[^\"\\\\]|\\\\.)*\"|-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?|[{}[\\]:,]/g;\n\n/** How a JSON token changes the nesting depth: `{` / `[` open, `}` / `]` close. */\nconst nestingOf = (token: string): number => {\n if (token === \"{\" || token === \"[\") return 1;\n return token === \"}\" || token === \"]\" ? -1 : 0;\n};\n\n/** Whether a JSON token is a number `JSON.parse` cannot carry exactly. */\nconst isInexactNumberToken = (token: string): boolean =>\n /^-?\\d/.test(token) && !isExactJsonNumber(token);\n\n/** A JSON string token's value (a column name, escapes decoded). */\nconst decodeJsonString = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.String));\n\n/**\n * The columns of a JSON row whose values hold a number JSON cannot carry\n * exactly (see {@link InexactJsonNumber}), nested values included: each\n * distinct top-level key such a number sits under, in order.\n */\nconst inexactColumns = (text: string): ReadonlyArray<string> => {\n const tokens = Array.from(text.matchAll(JSON_TOKEN), (match) => match[0]);\n const start = { depth: 0, key: \"\", previous: \"\" };\n const [, found] = Arr.mapAccum(tokens, start, (state, token) => {\n const depth = state.depth + nestingOf(token);\n // A string followed by `:` directly inside the row's object is a column name.\n const key =\n token === \":\" && state.depth === 1\n ? Option.getOrElse(decodeJsonString(state.previous), () => state.previous)\n : state.key;\n const inexact = isInexactNumberToken(token) ? Option.some(key) : Option.none<string>();\n return [{ depth, key, previous: token }, inexact] as const;\n });\n return Arr.dedupe(Arr.getSomes(found));\n};\n\n/** Content-hash fallback version when no version column is mapped. */\nexport const versionOf = Hash.contentHash;\n\n/** One client's ledger row: its watermark and the rejections it still remembers (#366). */\ninterface LedgerRow {\n readonly last: number;\n readonly rejections: ReadonlyMap<number, string>;\n}\n\n/**\n * A {@link Backend.Backend} over a {@link SqlExecutor}: subset evaluation, row fetch and the\n * transactional, idempotent mutation-id ledger.\n */\nexport const make = (config: SqlBackendConfig): Backend.Backend => {\n const dialect = config.dialect ?? Sql.sqlite;\n const executor = config.executor;\n const transactional = executor.transactional ?? true;\n const mutationsTable = config.mutationsTable ?? \"sync_mutations\";\n const ph = (index: number) => dialect.placeholder(index);\n const q = (id: string) => dialect.quote(id);\n\n const idOf = (collection: string) => config.tables[collection]?.idColumn ?? \"id\";\n const versionFor = (collection: string, row: SqlRow): string | number => {\n const column = config.tables[collection]?.versionColumn;\n return column !== undefined ? (row[column] as string | number) : versionOf(row);\n };\n // What a read queries: the mapping's `readRelation` when it names one\n // (typically a view), else the write target `table` (#204).\n const readTableOf = (mapping: TableMapping) => mapping.readRelation ?? mapping.table;\n // Whether a decoded row is a live row, not a tombstone: no mapping or no\n // declared predicate means every row is live.\n const isLive = (collection: string, value: SqlRow): boolean => {\n const tombstone = config.tables[collection]?.tombstone;\n return tombstone === undefined || !Predicate.test(tombstone, value);\n };\n\n // Every mapping's declared kinds, keyed by collection, for the inner\n // `where` of an `Exists` (the compiler picks the foreign collection's).\n const foreignKinds: Record<string, Sql.Kinds> = Object.fromEntries(\n Object.entries(config.tables).flatMap(([collection, mapping]) =>\n mapping.kinds === undefined ? [] : [[collection, mapping.kinds] as const]\n )\n );\n // The columns declared `boolean` per collection, fixed once so decoding a\n // row costs nothing for a collection with none.\n const booleanColumns: Record<string, ReadonlyArray<string>> = Object.fromEntries(\n Object.entries(config.tables).map(([collection, mapping]) => [\n collection,\n Object.entries(mapping.kinds ?? {})\n .filter(([, kind]) => kind === \"boolean\")\n .map(([field]) => field),\n ])\n );\n\n // Map a driver row back to the engine's values: a column declared\n // `boolean` comes back as `0`/`1` from SQLite and is decoded to a boolean;\n // every other column stays exactly as the driver returned it, and a row\n // with nothing to decode is returned as is (no copy).\n const decodeRow = (collection: string, row: SqlRow): SqlRow => {\n let decoded: Record<string, unknown> | undefined;\n Arr.forEach(booleanColumns[collection] ?? [], (field) => {\n const value = row[field];\n if (value !== 0 && value !== 1) return;\n decoded ??= { ...row };\n decoded[field] = value === 1;\n });\n return decoded ?? row;\n };\n\n // Resolve a correlated Exists to its allow-set via a DISTINCT subquery.\n // NULL foreign values are left out and the rest decoded by kind, exactly\n // as `Predicate.existsValues` does for MemoryBackend (ADR-0028 rule (b)):\n // NULL equals nothing, so it must not make `Not(Exists)` unknown. A\n // tombstoned foreign row is excluded the same way: a soft-deleted row\n // never satisfies a correlation.\n const resolveExistsNode = (\n node: Predicate.ExistsNode\n ): Effect.Effect<ReadonlyArray<Predicate.Literal>, BackendError> => {\n const mapping = config.tables[node.collection];\n if (mapping === undefined) return Effect.succeed([]);\n const where = Sql.compileWhere(node.where, dialect, { kinds: mapping.kinds, foreignKinds });\n return executor\n .query({\n text: `SELECT DISTINCT ${q(node.foreignField)} FROM ${q(readTableOf(mapping))} WHERE ${where.text}`,\n params: where.params,\n })\n .pipe(\n Effect.map((rows) =>\n rows.flatMap((row) => {\n const decoded = decodeRow(node.collection, row);\n if (!isLive(node.collection, decoded)) return [];\n const value = decoded[node.foreignField];\n return Predicate.isNullish(value) ? [] : [value as Predicate.Literal];\n })\n )\n );\n };\n\n // A subset's entries from the rows its `WHERE` selected (`where` is the\n // subset's predicate with its `Exists` nodes resolved).\n const entriesOf = (\n subset: Subset.Subset,\n where: Predicate.Predicate,\n rows: ReadonlyArray<SqlRow>\n ): ReadonlyArray<Backend.SubsetEntry> => {\n const idColumn = idOf(subset.collection);\n // A tombstoned row is dropped here, before ordering/limit/cursor: it is\n // not a live row to page over, and its absence from the returned\n // entries is exactly what makes `Cvr.diff` report it as a delete\n // (#204) — the same mechanism a row vanishing from the relation\n // outright already relies on.\n const values = rows\n .map((row) => ({ id: String(row[idColumn]), value: decodeRow(subset.collection, row) }))\n .filter((row) => isLive(subset.collection, row.value));\n return Subset.apply(values, { ...subset, where }).map((row) => ({\n id: row.id,\n version: versionFor(subset.collection, row.value),\n }));\n };\n\n const evaluate: Backend.Backend[\"evaluate\"] = (_context, subset) =>\n Effect.gen(function* () {\n const mapping = config.tables[subset.collection];\n if (mapping === undefined) return [];\n const where = yield* Predicate.resolveExists(subset.where, resolveExistsNode);\n const rows = yield* executor.query(\n Sql.compileSelect({\n table: readTableOf(mapping),\n where,\n dialect,\n kinds: mapping.kinds,\n foreignKinds,\n })\n );\n return entriesOf(subset, where, rows);\n });\n\n // A read relation's column names, for a `Columns` dialect (SQLite).\n // Looked up once per relation and kept for the backend's life: a read\n // relation whose columns change needs a new backend. An empty answer (no\n // such relation yet) is not kept.\n const columnCache = MutableHashMap.empty<string, ReadonlyArray<string>>();\n const columnsOf = (\n union: Sql.UnionRows,\n relation: string\n ): Effect.Effect<ReadonlyArray<string>, BackendError> => {\n if (P.isTagged(union, \"Json\")) return Effect.succeed([]);\n return Option.match(MutableHashMap.get(columnCache, relation), {\n onSome: Effect.succeed,\n onNone: () =>\n executor.query(union.columnsOf(relation)).pipe(\n Effect.map((rows) => rows.map((row) => String(row[\"name\"]))),\n Effect.tap((columns) =>\n Effect.sync(() => {\n if (Arr.isReadonlyArrayNonEmpty(columns)) {\n MutableHashMap.set(columnCache, relation, columns);\n }\n })\n )\n ),\n });\n };\n\n // One subset's arm of `evaluateMany`'s statement, before its binds are numbered.\n interface ManyArm {\n readonly index: number;\n readonly subset: Subset.Subset;\n readonly mapping: TableMapping;\n readonly where: Predicate.Predicate;\n /** The relation's columns, in order, for a `Columns` dialect; empty for `Json`. */\n readonly columns: ReadonlyArray<string>;\n }\n\n // One subset's arm, or none for a collection without a mapping (which\n // `evaluate` answers with no entries). Resolves the `Exists` nodes as\n // `evaluate` does.\n const manyArm =\n (union: Sql.UnionRows) =>\n (subset: Subset.Subset, index: number): Effect.Effect<Option.Option<ManyArm>, BackendError> => {\n const mapping = config.tables[subset.collection];\n if (mapping === undefined) return Effect.succeedNone;\n return Effect.all(\n {\n where: Predicate.resolveExists(subset.where, resolveExistsNode),\n columns: columnsOf(union, readTableOf(mapping)),\n },\n { concurrency: 1 }\n ).pipe(\n Effect.map(({ where, columns }) => Option.some({ index, subset, mapping, where, columns }))\n );\n };\n\n // What an arm selects after its subset index: its row as JSON, or its own\n // columns by position (`c0`, `c1`, …) padded with NULL to `width`.\n const projection = (union: Sql.UnionRows, arm: ManyArm, width: number): string => {\n const alias = q(MANY_ROW_ALIAS);\n if (P.isTagged(union, \"Json\")) return `${union.rowJson(alias)} AS ${q(\"row\")}`;\n return Arr.makeBy(width, (position) => {\n const column = arm.columns[position];\n const value = column === undefined ? \"NULL\" : `${alias}.${q(column)}`;\n return `${value} AS ${q(`c${position}`)}`;\n }).join(\", \");\n };\n\n // A statement row back as the subset index and the row `SELECT *` would give.\n const readManyRow =\n (union: Sql.UnionRows, arms: ReadonlyArray<Option.Option<ManyArm>>) => (row: SqlRow) =>\n Effect.gen(function* () {\n const { subset } = yield* decodeManyIndex(row);\n if (P.isTagged(union, \"Json\")) {\n const text = String(row[\"row\"]);\n return { subset, row: yield* decodeManyJson(text), inexact: inexactColumns(text) };\n }\n const columns = Option.match(arms[subset] ?? Option.none(), {\n onNone: () => [],\n onSome: (arm) => arm.columns,\n });\n return {\n subset,\n row: R.fromEntries(columns.map((name, position) => [name, row[`c${position}`]])),\n inexact: Arr.empty<string>(),\n };\n });\n\n // Warn once per collection and column for the backend's life that a pull\n // fell back to `evaluate` for a number JSON cannot carry exactly.\n const warnedInexact = MutableHashSet.empty<string>();\n const warnInexact = ({\n collection,\n column,\n }: {\n readonly collection: string;\n readonly column: string;\n }) =>\n Effect.suspend(() => {\n const key = `${collection}\\u0000${column}`;\n if (MutableHashSet.has(warnedInexact, key)) return Effect.void;\n MutableHashSet.add(warnedInexact, key);\n const message =\n `evaluateMany: column \"${column}\" of collection \"${collection}\" holds a number JSON cannot ` +\n \"carry exactly, so the pull falls back to one evaluate per subset; declare it integer, \" +\n \"double precision or text in the read relation\";\n return Effect.logWarning(message, new InexactJsonNumber({ message, collection, column }));\n });\n\n // One `UNION ALL` statement for every subset (#505): each arm selects its\n // subset's index and its row (`Dialect.unionRows`: its own columns by\n // position on SQLite, JSON on PostgreSQL), since mappings' columns differ per\n // collection. The arms' `WHERE`s are the ones `evaluate` compiles, numbered\n // on from the previous arm's binds, and the rows are finished per subset\n // exactly as `evaluate` finishes them. Round trips: one statement, plus one\n // per `Exists` node (resolved first, as in `evaluate`), plus on SQLite one\n // column lookup per read relation the first time it is read.\n const evaluateMany = (union: Sql.UnionRows) =>\n ((context, subsets) =>\n Effect.gen(function* () {\n const arms = yield* Effect.forEach(subsets, manyArm(union), { concurrency: 1 });\n const present = Arr.getSomes(arms);\n const width = Math.max(0, ...present.map((arm) => arm.columns.length));\n const noBinds: ReadonlyArray<unknown> = [];\n const [params, texts] = Arr.mapAccum(present, noBinds, (bound, arm) => {\n const offset = bound.length;\n const compiled = Sql.compileWhere(\n arm.where,\n { ...dialect, placeholder: (bind) => dialect.placeholder(offset + bind) },\n { kinds: arm.mapping.kinds, foreignKinds }\n );\n const relation = q(readTableOf(arm.mapping));\n return [\n [...bound, ...compiled.params],\n `SELECT ${arm.index} AS ${q(\"subset\")}, ${projection(union, arm, width)} FROM ${relation} AS ${q(MANY_ROW_ALIAS)} WHERE ${compiled.text}`,\n ] as const;\n });\n const rows = Arr.isReadonlyArrayNonEmpty(texts)\n ? yield* executor.query({ text: texts.join(\" UNION ALL \"), params })\n : [];\n const read = yield* Effect.forEach(rows, readManyRow(union, arms), {\n concurrency: 1,\n });\n // JSON carries a number with more digits than a double holds (a wide\n // `bigint` or `numeric`, also inside a `jsonb` column) as one\n // `JSON.parse` rounds, and an integer past ±(2^53 − 1) as a number where\n // the driver gives text, so such a row would read back differently from\n // `evaluate`'s. Such a pull is answered by `evaluate` per subset\n // instead, with a warning naming each such column (#505). (A `Columns` arm\n // hands the driver its values as `evaluate`'s statement does.)\n const inexact = read.flatMap((row) =>\n row.inexact.map((column) => ({\n collection: subsets[row.subset]?.collection ?? \"\",\n column,\n }))\n );\n if (Arr.isReadonlyArrayNonEmpty(inexact)) {\n yield* Effect.forEach(inexact, warnInexact, { concurrency: 1, discard: true });\n return yield* Effect.forEach(subsets, (subset) => evaluate(context, subset), {\n concurrency: 1,\n });\n }\n const bySubset = Arr.groupBy(read, (row) => String(row.subset));\n return arms.map((arm) =>\n Option.match(arm, {\n onNone: () => [],\n onSome: ({ index, subset, where }) =>\n entriesOf(\n subset,\n where,\n (bySubset[String(index)] ?? []).map((row) => row.row)\n ),\n })\n );\n })) satisfies NonNullable<Backend.Backend[\"evaluateMany\"]>;\n\n // Fetch the raw rows for explicit ids (shared by getRows and hint\n // enrichment). Reads see the push transaction's own writes. A tombstoned\n // row is omitted, exactly like an id no longer in the relation (#204).\n const queryRowsById = (\n collection: string,\n ids: ReadonlyArray<string>\n ): Effect.Effect<ReadonlyArray<SqlRow>, BackendError> => {\n const mapping = config.tables[collection];\n if (mapping === undefined || Arr.isReadonlyArrayEmpty(ids)) return Effect.succeed([]);\n return executor\n .query(\n Sql.compileSelect({\n table: readTableOf(mapping),\n where: Predicate.isIn(idOf(collection), [...ids]),\n dialect,\n kinds: mapping.kinds,\n })\n )\n .pipe(\n Effect.map((rows) =>\n rows.map((row) => decodeRow(collection, row)).filter((row) => isLive(collection, row))\n )\n );\n };\n\n const getRows: Backend.Backend[\"getRows\"] = (_context, collection, ids) =>\n queryRowsById(collection, ids).pipe(\n Effect.map((rows) =>\n rows.map((row) => ({\n id: String(row[idOf(collection)]),\n version: versionFor(collection, row),\n value: row,\n }))\n )\n );\n\n // Current (id, value) pairs for explicit ids, for hint enrichment.\n const rowValuesFor = (collection: string, ids: ReadonlyArray<string>) =>\n queryRowsById(collection, ids).pipe(\n Effect.map((rows) => rows.map((row) => ({ id: String(row[idOf(collection)]), value: row })))\n );\n\n // Attach the current row values to a handler-emitted hint (best-effort, for\n // predicate-level fan-out). The rows still present are PUTs; ids the handler\n // named but that the read no longer returns were deleted (absent from\n // `values`). A collection without a table mapping keeps the hint value-less,\n // so the server falls back to a collection-level match.\n const enrichHint = (hint: Backend.ChangeHint): Effect.Effect<Backend.ChangeHint, BackendError> =>\n config.tables[hint.collection] === undefined\n ? Effect.succeed(hint)\n : rowValuesFor(hint.collection, hint.ids).pipe(Effect.map((values) => ({ ...hint, values })));\n\n const readLastIds = (\n clientGroupId: string\n ): Effect.Effect<Record<string, number>, BackendError> =>\n executor\n .query({\n text: `SELECT ${q(\"client_id\")}, ${q(\"last_mutation_id\")} FROM ${q(mutationsTable)} WHERE ${q(\"client_group_id\")} = ${ph(1)}`,\n params: [clientGroupId],\n })\n .pipe(\n Effect.map((rows) =>\n Object.fromEntries(\n rows.map((row) => [String(row.client_id), Number(row.last_mutation_id)])\n )\n )\n );\n\n // The group's ledger rows with their rejection memory (#366). Every push reads\n // it, so a ledger without the `rejections` column fails here, on the first\n // push after an upgrade, naming the migration instead of failing silently.\n const readLedger = (\n clientGroupId: string\n ): Effect.Effect<ReadonlyMap<string, LedgerRow>, BackendError> =>\n executor\n .query({\n text: `SELECT ${q(\"client_id\")}, ${q(\"last_mutation_id\")}, ${q(\"rejections\")} FROM ${q(mutationsTable)} WHERE ${q(\"client_group_id\")} = ${ph(1)}`,\n params: [clientGroupId],\n })\n .pipe(\n Effect.mapError(\n (cause) =>\n new BackendError({\n message:\n `The mutation ledger \"${mutationsTable}\" could not be read with its \"rejections\" column. ` +\n `If the column is missing, check for it, then run \\`ALTER TABLE ${mutationsTable} ADD COLUMN rejections TEXT\\` ` +\n `(the sql README's \"Ledger without the \\`rejections\\` column\" section).`,\n cause,\n })\n ),\n Effect.flatMap((rows) =>\n Effect.forEach(rows, (row) =>\n decodeRejections(row.rejections, mutationsTable).pipe(\n Effect.map(\n (rejections) =>\n [\n String(row.client_id),\n { last: Number(row.last_mutation_id), rejections },\n ] as const\n )\n )\n )\n ),\n Effect.map((entries) => new Map(entries))\n );\n\n // Replace one client's remembered rejections (`null` when the set is empty).\n const writeRejections = (\n clientGroupId: string,\n clientId: string,\n rejections: ReadonlyMap<number, string>\n ) =>\n executor.run({\n text: `UPDATE ${q(mutationsTable)} SET ${q(\"rejections\")} = ${ph(1)} WHERE ${q(\"client_group_id\")} = ${ph(2)} AND ${q(\"client_id\")} = ${ph(3)}`,\n params: [encodeRejections(rejections), clientGroupId, clientId],\n });\n\n // Remember a permanent rejection in the client's ledger row (#366). It runs in\n // the same transaction as the claim (or, without transactions, before the\n // bump), so a copy that reads the row afterwards sees the outcome with the id.\n // The row is created at 0 first if it does not exist yet: a D1 push records the\n // rejection before the bump that would otherwise create the row.\n const recordRejection = (\n clientGroupId: string,\n clientId: string,\n mutationId: number,\n reason: string\n ) =>\n Effect.gen(function* () {\n yield* ensureLedgerRow(clientGroupId, clientId);\n const row = (yield* readLedger(clientGroupId)).get(clientId);\n const rejections = new Map(row?.rejections ?? []);\n rejections.set(mutationId, reason);\n yield* writeRejections(clientGroupId, clientId, rejections);\n });\n\n // Forget the rejections a push has moved past: for each client in the push, the\n // remembered rejections below its lowest id in the push (#355, as MemoryBackend).\n const forgetRejections = (clientGroupId: string, floors: ReadonlyMap<string, number>) =>\n Effect.gen(function* () {\n if (floors.size === 0) return;\n const ledger = yield* readLedger(clientGroupId);\n yield* Effect.forEach(\n floors,\n ([clientId, floor]) =>\n Effect.gen(function* () {\n const row = ledger.get(clientId);\n if (row === undefined || row.rejections.size === 0) return;\n const kept = new Map([...row.rejections].filter(([mutationId]) => mutationId >= floor));\n if (kept.size !== row.rejections.size) {\n yield* writeRejections(clientGroupId, clientId, kept);\n }\n }),\n { concurrency: 1, discard: true }\n );\n });\n\n // Create the client's ledger row at 0 when it has none. `DO NOTHING` leaves a\n // row another transaction inserted alone (on PostgreSQL the insert waits for\n // that transaction to commit or roll back first), so the claim below finds\n // exactly one row either way.\n const ensureLedgerRow = (clientGroupId: string, clientId: string) =>\n executor.run({\n text:\n `INSERT INTO ${q(mutationsTable)} (${q(\"client_group_id\")}, ${q(\"client_id\")}, ${q(\"last_mutation_id\")}) ` +\n `VALUES (${ph(1)}, ${ph(2)}, 0) ` +\n `ON CONFLICT (${q(\"client_group_id\")}, ${q(\"client_id\")}) DO NOTHING`,\n params: [clientGroupId, clientId],\n });\n\n // The claim: one conditional UPDATE from `mutationId - 1` to `mutationId`. The\n // database evaluates the WHERE against the latest committed row, so two calls\n // racing for one mutation cannot both match (READ COMMITTED re-checks the\n // WHERE once a concurrent writer commits). RETURNING says whether this call\n // moved the row. Only the claim decides; no read is trusted to decide it.\n const claimLedger = (\n clientGroupId: string,\n clientId: string,\n mutationId: number\n ): Effect.Effect<boolean, BackendError> =>\n executor\n .query({\n text:\n `UPDATE ${q(mutationsTable)} SET ${q(\"last_mutation_id\")} = ${ph(1)} ` +\n `WHERE ${q(\"client_group_id\")} = ${ph(2)} AND ${q(\"client_id\")} = ${ph(3)} ` +\n `AND ${q(\"last_mutation_id\")} = ${ph(4)} RETURNING ${q(\"last_mutation_id\")}`,\n params: [mutationId, clientGroupId, clientId, mutationId - 1],\n })\n .pipe(Effect.map((rows) => Arr.isReadonlyArrayNonEmpty(rows)));\n\n // A unit for an opened mutation, with the next savepoint name of the push.\n const unitOf = (batch: SqlBatch, mutation: Mutation, staleRejection: boolean): SqlUnit => {\n const savepoint = q(`legation_sp_${batch.savepoints.next}`);\n batch.savepoints.next += 1;\n return { context: batch.context, mutation, savepoint, staleRejection };\n };\n\n // Open `mutationId` claim-first: claim it before its handler runs. A won claim\n // opens the unit. A zero-row claim re-reads the ledger, which only ever rises,\n // so a stale claim cannot move the watermark back, and reports the watermark\n // with the committed rejection, if there is one (#366): `Backend.transaction`\n // classifies it as a replay or a gap.\n const openClaimFirst = (\n batch: SqlBatch,\n mutation: Mutation\n ): Effect.Effect<Backend.Opened<SqlUnit>, BackendError> =>\n Effect.gen(function* () {\n const { clientGroupId } = batch.context;\n yield* ensureLedgerRow(clientGroupId, mutation.clientId);\n if (yield* claimLedger(clientGroupId, mutation.clientId, mutation.mutationId)) {\n return { _tag: \"Opened\", unit: unitOf(batch, mutation, false) };\n }\n const row = (yield* readLedger(clientGroupId)).get(mutation.clientId);\n return {\n _tag: \"Watermark\",\n lastMutationId: row?.last ?? 0,\n rejection: Option.fromUndefinedOr(row?.rejections.get(mutation.mutationId)),\n };\n });\n\n // Open `mutationId` handler-then-bump (executors without transactions, D1):\n // the ledger is read and nothing is claimed, so a crash cannot leave an id\n // claimed and unapplied. The bump comes at commit, after the handler.\n const openBumpAfter = (\n batch: SqlBatch,\n mutation: Mutation\n ): Effect.Effect<Backend.Opened<SqlUnit>, BackendError> =>\n readLedger(batch.context.clientGroupId).pipe(\n Effect.map((ledger): Backend.Opened<SqlUnit> => {\n const row = ledger.get(mutation.clientId);\n const last = row?.last ?? 0;\n return mutation.mutationId === last + 1\n ? {\n _tag: \"Opened\",\n unit: unitOf(batch, mutation, row?.rejections.has(mutation.mutationId) === true),\n }\n : {\n _tag: \"Watermark\",\n lastMutationId: last,\n rejection: Option.fromUndefinedOr(row?.rejections.get(mutation.mutationId)),\n };\n })\n );\n\n // The bump of the handler-then-bump order (executors without transactions).\n // It runs after the handler's writes, and it takes the larger of the stored and\n // the new id, so a call that parked before a later bump cannot move the\n // watermark back when it finishes (#352). It is not atomic with those writes\n // (#342).\n const bumpLastId = (clientGroupId: string, clientId: string, mutationId: number) =>\n executor.run({\n text:\n `INSERT INTO ${q(mutationsTable)} (${q(\"client_group_id\")}, ${q(\"client_id\")}, ${q(\"last_mutation_id\")}) ` +\n `VALUES (${ph(1)}, ${ph(2)}, ${ph(3)}) ` +\n // `excluded.` instead of re-binding ph(3): with `?` placeholders a\n // reused index renders an extra placeholder with no matching param\n // (found by the commerce example's contract run over node:sqlite).\n // The stored row's column is qualified with the table name: PostgreSQL\n // reads an unqualified name here as ambiguous with `excluded` (#390).\n `ON CONFLICT (${q(\"client_group_id\")}, ${q(\"client_id\")}) DO UPDATE SET ${q(\"last_mutation_id\")} = ` +\n dialect.greatest(\n `${q(mutationsTable)}.${q(\"last_mutation_id\")}`,\n `excluded.${q(\"last_mutation_id\")}`\n ),\n params: [clientGroupId, clientId, mutationId],\n });\n\n // Give a claim back after its handler failed transiently. A transaction rolls\n // the claim back with the handler's effects. An executor without transactions\n // (D1, see D1Executor) keeps it, so this restores the id and the client's\n // retry applies the mutation again. Best effort: the handler's error is what\n // the caller sees, and inside a failed PostgreSQL transaction this statement\n // is refused and ignored, the rollback doing the work instead.\n const releaseClaim = (clientGroupId: string, clientId: string, mutationId: number) =>\n executor\n .run({\n text:\n `UPDATE ${q(mutationsTable)} SET ${q(\"last_mutation_id\")} = ${ph(1)} ` +\n `WHERE ${q(\"client_group_id\")} = ${ph(2)} AND ${q(\"client_id\")} = ${ph(3)} ` +\n `AND ${q(\"last_mutation_id\")} = ${ph(4)}`,\n params: [mutationId - 1, clientGroupId, clientId, mutationId],\n })\n .pipe(Effect.ignore);\n\n // Run one mutation's handler inside a savepoint, so a permanent rejection\n // drops its writes (at commit) while the id's ledger write (the claim, or the\n // bump after it) stays. A transient BackendError propagates and aborts the batch.\n const runHandler = (\n unit: SqlUnit,\n handler: SqlHandler,\n mutation: Mutation\n ): Effect.Effect<ReadonlyArray<Backend.ChangeHint>, BackendError | MutationRejected> =>\n Effect.gen(function* () {\n const definition = config.mutators.get(mutation.mutator);\n if (definition === undefined) return yield* UnknownMutator.make(mutation.mutator);\n const args = yield* decodeArgs(definition, mutation.args, mutation.mutator);\n yield* executor.run({ text: `SAVEPOINT ${unit.savepoint}`, params: [] });\n return yield* handler(args, executor, unit.context);\n });\n\n // Commit one mutation's outcome with its ledger write. An applied mutation\n // keeps its savepoint's writes; a rejected one rolls them back, and its\n // rejection is remembered in the same transaction as the claim (#366), or,\n // without transactions, before the bump, so a copy that sees the id as covered\n // also sees its rejection. Without transactions the bump comes last and takes\n // the larger id (`bumpLastId`); it is not atomic with the handler's writes (#342).\n const commitOutcome = (\n unit: SqlUnit,\n outcome: Backend.Outcome<ReadonlyArray<Backend.ChangeHint>>\n ): Effect.Effect<Backend.Commit, BackendError> =>\n Effect.gen(function* () {\n const { clientGroupId } = unit.context;\n const { clientId, mutationId } = unit.mutation;\n if (outcome._tag === \"Rejected\") {\n yield* executor.run({ text: `ROLLBACK TO SAVEPOINT ${unit.savepoint}`, params: [] });\n yield* executor.run({ text: `RELEASE SAVEPOINT ${unit.savepoint}`, params: [] });\n yield* recordRejection(clientGroupId, clientId, mutationId, outcome.reason);\n } else {\n yield* executor.run({ text: `RELEASE SAVEPOINT ${unit.savepoint}`, params: [] });\n if (!transactional && unit.staleRejection) {\n // An earlier copy was rejected and recorded it, but its bump failed, so\n // the id was never acknowledged and this copy applies it. The stale\n // rejection goes before the bump: a bump that then fails leaves the id\n // unacknowledged with no rejection to report, and a replay after the\n // bump must not report one for an applied mutation (#425).\n const row = (yield* readLedger(clientGroupId)).get(clientId);\n const rejections = new Map(row?.rejections ?? []);\n rejections.delete(mutationId);\n yield* writeRejections(clientGroupId, clientId, rejections);\n }\n }\n if (!transactional) yield* bumpLastId(clientGroupId, clientId, mutationId);\n return { _tag: \"Committed\" } as const;\n });\n\n // The transaction's isolation, set before any read so every read in it sees\n // the snapshot SERIALIZABLE gives (#353). An executor without transactions has\n // nothing to isolate, and SQLite has no such statement (its writers serialise).\n const serializable = transactional ? dialect.serializable : undefined;\n\n // The whole push is one executor transaction, with SERIALIZABLE set first\n // where the dialect has it (and the executor has transactions).\n const atomically = <A>(\n _context: Backend.SyncContext,\n effect: Effect.Effect<A, BackendError>\n ): Effect.Effect<A, BackendError> =>\n executor.transaction(\n serializable === undefined\n ? effect\n : executor.run({ text: serializable, params: [] }).pipe(\n // PostgreSQL refuses this statement inside a subtransaction (a nested\n // transaction, which the executor runs as a savepoint) or after another\n // statement. A refusal fails the batch: the batch must never run at READ\n // COMMITTED without saying so (#353). The client retries it.\n Effect.mapError(\n (cause) =>\n new BackendError({\n message:\n \"SERIALIZABLE isolation could not be set for this mutation batch. On PostgreSQL the \" +\n \"executor's transaction must be top-level, with this statement first: SET TRANSACTION \" +\n \"fails inside a nested transaction (a savepoint) or after another statement.\",\n cause,\n })\n ),\n Effect.andThen(effect)\n )\n );\n\n // `Backend.transaction` owns the ledger rules (#177): order, replay and gap\n // classification, the bump with each outcome, and reporting. This store\n // supplies the claim (or the read, on D1), the savepoint, and the commit.\n const applyMutations = Backend.transaction<\n SqlHandler,\n SqlBatch,\n SqlUnit,\n ReadonlyArray<Backend.ChangeHint>\n >({\n handlers: config.handlers,\n // Always the executor's transaction, as before #177: an executor without\n // transactions (D1) defines what its `transaction` does.\n atomically,\n // The database isolates a batch (or, on D1, nothing does): a commit never\n // reports a conflict, so a handler runs once per opened unit.\n maxAttempts: 1,\n // The rejections this push has moved past are forgotten first (#366).\n begin: (context, floors) =>\n forgetRejections(context.clientGroupId, floors).pipe(\n Effect.as({ context, savepoints: { next: 0 } })\n ),\n open: transactional ? openClaimFirst : openBumpAfter,\n run: runHandler,\n commit: commitOutcome,\n // A transaction rolls a claim back with the handler's effects; this gives it\n // back as well (best effort). Without transactions nothing was claimed.\n release: transactional\n ? (unit) =>\n releaseClaim(unit.context.clientGroupId, unit.mutation.clientId, unit.mutation.mutationId)\n : () => Effect.void,\n // Enrich once, after all writes, so the reads see the final tx state\n // (a row a later mutation deleted is correctly absent from `values`).\n finish: ({ context }, applied) =>\n Effect.all(\n {\n lastMutationIds: readLastIds(context.clientGroupId),\n hints: Effect.forEach(applied.flat(), enrichHint),\n },\n { concurrency: 1 }\n ),\n });\n\n // The group's remembered rejections, from every client's ledger row (#365). A pull\n // reports the ones its watermark covers.\n const rememberedRejections = (\n clientGroupId: string\n ): Effect.Effect<ReadonlyArray<Backend.RejectedMutation>, BackendError> =>\n readLedger(clientGroupId).pipe(\n Effect.map((ledger) =>\n [...ledger].flatMap(([clientId, row]) =>\n [...row.rejections].map(([mutationId, reason]) => ({ clientId, mutationId, reason }))\n )\n )\n );\n\n return Backend.Backend.of({\n applyMutations,\n lastMutationIds: (context) => readLastIds(context.clientGroupId),\n rejections: (context) => rememberedRejections(context.clientGroupId),\n evaluate,\n evaluateMany: dialect.unionRows === undefined ? undefined : evaluateMany(dialect.unionRows),\n getRows,\n });\n};\n","/**\n * PollingHintSource\n *\n * A tier-2 change-data-capture hint source (ADR-0019): a scoped fibre polls\n * each configured table on an interval for a cheap change-detector and, when a\n * table's detector moves between polls, publishes a value-less `ChangeHint` for\n * that collection to the InvalidationBus. Out-of-band writes (cron jobs, admin\n * tools, another service, psql) that never went through a mutator therefore\n * still poke subscribed clients, at a bounded staleness of one poll interval.\n *\n * It uses only the existing `SqlExecutor` seam — no new driver dependency\n * (ADR-0006) — so any backend that can already run the `SqlBackend` can also run\n * this.\n *\n * The published hint is value-less (`ids: []`), so the #42 fan-out matcher falls\n * back to a collection-level match: every group subscribed to the collection is\n * poked and the next pull reconciles. That is sound — over-matching a poke costs\n * an empty pull; poke is never a correctness dependency (ADR-0009).\n *\n * Detector coverage (be honest about it): the detector is\n * `SELECT COUNT(*) AS n, MAX(version) AS v` when a `versionColumn` is mapped,\n * else `SELECT COUNT(*) AS n`. `COUNT(*) + MAX(version)` catches inserts,\n * deletes, and version-bumping updates. A table with NO monotonic\n * `versionColumn` can only detect row-count changes: an in-place update that\n * keeps the row count and has no version column is missed. Map a `versionColumn`\n * (or an `updated_at`) for full coverage.\n */\n\nimport * as Effect from \"effect/Effect\";\nimport * as Ref from \"effect/Ref\";\nimport * as Schedule from \"effect/Schedule\";\nimport type * as Duration from \"effect/Duration\";\nimport { Backend, BackendError } from \"../index.js\";\nimport { HintSource, InvalidationBus } from \"../internal.js\";\nimport * as Sql from \"./Sql.js\";\nimport type * as SqlBackend from \"./SqlBackend.js\";\n\n/** Configuration for {@link make}. */\nexport interface PollingHintSourceConfig {\n readonly executor: SqlBackend.SqlExecutor;\n /** Collection → table mapping (the `SqlBackend` mapping, reused). */\n readonly tables: Readonly<Record<string, SqlBackend.TableMapping>>;\n /** Poll cadence — the bounded staleness of an out-of-band write. */\n readonly interval: Duration.Input;\n /** Collections to poll. Defaults to every key of `tables`. */\n readonly collections?: ReadonlyArray<string>;\n}\n\n// Identifier quoting only (no placeholders in the detector), and both dialects\n// quote identically, so the sqlite dialect's quoter suffices.\nconst quote = Sql.sqlite.quote;\n\n/**\n * The cheap change-detector query for a table: count plus (if a version column\n * is mapped) its running maximum.\n */\nconst detectorQuery = (mapping: SqlBackend.TableMapping): Sql.SqlFragment => {\n const table = quote(mapping.table);\n return mapping.versionColumn !== undefined\n ? {\n text: `SELECT COUNT(*) AS n, MAX(${quote(mapping.versionColumn)}) AS v FROM ${table}`,\n params: [],\n }\n : { text: `SELECT COUNT(*) AS n FROM ${table}`, params: [] };\n};\n\n/** Collapse a detector row to a comparable signature `${count}|${maxVersion}`. */\nconst signatureOf = (rows: ReadonlyArray<SqlBackend.SqlRow>): string => {\n const row = rows[0] ?? {};\n const version = row.v === undefined || row.v === null ? \"\" : String(row.v);\n return `${String(row.n ?? 0)}|${version}`;\n};\n\n/**\n * A `HintSource` that polls each mapped table for a cheap change detector and publishes a hint\n * when a collection changes out of band.\n */\nexport const make = (config: PollingHintSourceConfig): HintSource.HintSource => {\n const collections = config.collections ?? Object.keys(config.tables);\n\n // Current detector signature for a collection, or undefined when it has no\n // table mapping (nothing to poll).\n const signatureFor = (collection: string): Effect.Effect<string | undefined, BackendError> => {\n const mapping = config.tables[collection];\n if (mapping === undefined) return Effect.succeed(undefined);\n return config.executor.query(detectorQuery(mapping)).pipe(Effect.map(signatureOf));\n };\n\n // One poll: read every collection's detector, publish a value-less hint for\n // each whose signature moved since the last poll (batched into one publish),\n // and remember the new signatures. The first poll only establishes the\n // baseline — a collection not yet in `state` never emits a hint.\n const tick = (\n bus: InvalidationBus.InvalidationBus,\n state: Ref.Ref<Map<string, string>>\n ): Effect.Effect<void, BackendError> =>\n Effect.gen(function* () {\n const previous = yield* Ref.get(state);\n const next = new Map(previous);\n const changed: Array<Backend.ChangeHint> = [];\n yield* Effect.forEach(\n collections,\n (collection) =>\n Effect.gen(function* () {\n const signature = yield* signatureFor(collection);\n if (signature === undefined) return;\n if (previous.has(collection) && previous.get(collection) !== signature) {\n changed.push({ collection, ids: [] });\n }\n next.set(collection, signature);\n }),\n { concurrency: 1, discard: true }\n );\n yield* Ref.set(state, next);\n yield* bus.publish(changed);\n });\n\n return {\n run: Effect.flatMap(InvalidationBus.InvalidationBus, (bus) =>\n Effect.gen(function* () {\n // Per-run baseline: each fork starts fresh, so no spurious hint fires\n // for the state a previous run already saw.\n const state = yield* Ref.make(new Map<string, string>());\n yield* Effect.forkScoped(\n // A transient query error just skips this tick — the next poll (and\n // the interval pull) recovers; poke is never a correctness path.\n tick(bus, state).pipe(\n Effect.catch(() => Effect.void),\n Effect.repeat(Schedule.spaced(config.interval))\n )\n );\n })\n ),\n };\n};\n","/**\n * PgExecutor\n *\n * A `SqlBackend` executor over a PostgreSQL `SqlClient` from `effect/sql`\n * (#203). Pair it with `dialect: Sql.postgres`:\n *\n * ```ts\n * const sql = yield* SqlClient; // from `PgClient.layer(...)` (`@effect/sql-pg`)\n * const backend = SqlBackend.make({\n * executor: PgExecutor.make(sql),\n * dialect: Sql.postgres,\n * tables,\n * mutators,\n * handlers,\n * });\n * ```\n *\n * The executor needs only the `SqlClient` service, which ships in `effect`\n * itself, so this package gains no dependency (ADR-0006): the driver\n * (`@effect/sql-pg`) and its connection settings are the app's choice, and it\n * provides the `SqlClient` layer.\n *\n * **Transactions:** `transaction` is the client's `withTransaction`, so each\n * mutation batch is one real PostgreSQL transaction on one connection, and\n * `SqlBackend` claims each mutation id in the same transaction as its handler's\n * writes. `Sql.postgres` sets SERIALIZABLE as the transaction's first\n * statement, so a handler that read a row another transaction then changed is\n * refused with SQLSTATE 40001 and rolled back; that surfaces as a\n * `BackendError`, which the client retries. The transaction must be top-level:\n * calling the backend inside an outer `withTransaction` makes this a savepoint,\n * where PostgreSQL refuses `SET TRANSACTION` and the batch fails instead.\n *\n * **Statements run unprepared** (`.unprepared`): each is parsed per call rather\n * than cached by name on the connection. A transaction-mode pooler in front of\n * the database (PgBouncer, a managed pooler) can hand consecutive statements to\n * different server connections, where a named statement prepared on one does not\n * exist on the next. The cost is a parse per statement.\n */\n\nimport * as Effect from \"effect/Effect\";\nimport type { SqlClient } from \"effect/sql/SqlClient\";\nimport { BackendError } from \"../index.js\";\nimport type * as Sql from \"./Sql.js\";\nimport type * as SqlBackend from \"./SqlBackend.js\";\n\nconst asBackendError = (message: string) => (cause: unknown) =>\n new BackendError({ message, cause });\n\n/**\n * The executor over `sql`: unprepared statements, and a real transaction per `transaction` call.\n *\n * A failed statement or transaction fails as a `BackendError` that carries the driver's `SqlError`\n * as its `cause` (a serialization refusal included), with the statement text, never its parameters,\n * in the message.\n */\nexport const make = (sql: SqlClient): SqlBackend.SqlExecutor => ({\n query: (fragment: Sql.SqlFragment) =>\n sql\n .unsafe<SqlBackend.SqlRow>(fragment.text, fragment.params)\n .unprepared.pipe(Effect.mapError(asBackendError(`query failed: ${fragment.text}`))),\n run: (fragment: Sql.SqlFragment) =>\n sql\n .unsafe(fragment.text, fragment.params)\n .unprepared.pipe(\n Effect.asVoid,\n Effect.mapError(asBackendError(`statement failed: ${fragment.text}`))\n ),\n // `BEGIN`, `COMMIT` and `ROLLBACK` fail as a `SqlError`; the effect's own failures are already\n // `BackendError`s. A serialization failure can surface at `COMMIT`, so it is mapped here too.\n transaction: (effect) =>\n sql\n .withTransaction(effect)\n .pipe(\n Effect.catchTag(\"SqlError\", (error) =>\n Effect.fail(asBackendError(\"transaction failed\")(error))\n )\n ),\n});\n"],"mappings":"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"}