@nicia-ai/typegraph 0.32.0 → 0.33.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.
- package/README.md +31 -0
- package/dist/backend/postgres/index.cjs +28 -15
- package/dist/backend/postgres/index.d.cts +6 -6
- package/dist/backend/postgres/index.d.ts +6 -6
- package/dist/backend/postgres/index.js +6 -5
- package/dist/backend/postgres/pglite.cjs +10 -9
- package/dist/backend/postgres/pglite.cjs.map +1 -1
- package/dist/backend/postgres/pglite.d.cts +3 -3
- package/dist/backend/postgres/pglite.d.ts +3 -3
- package/dist/backend/postgres/pglite.js +6 -5
- package/dist/backend/postgres/pglite.js.map +1 -1
- package/dist/backend/sqlite/index.cjs +27 -14
- package/dist/backend/sqlite/index.d.cts +7 -7
- package/dist/backend/sqlite/index.d.ts +7 -7
- package/dist/backend/sqlite/index.js +6 -5
- package/dist/backend/sqlite/libsql.cjs +9 -8
- package/dist/backend/sqlite/libsql.cjs.map +1 -1
- package/dist/backend/sqlite/libsql.d.cts +3 -3
- package/dist/backend/sqlite/libsql.d.ts +3 -3
- package/dist/backend/sqlite/libsql.js +6 -5
- package/dist/backend/sqlite/libsql.js.map +1 -1
- package/dist/backend/sqlite/local.cjs +10 -9
- package/dist/backend/sqlite/local.cjs.map +1 -1
- package/dist/backend/sqlite/local.d.cts +3 -3
- package/dist/backend/sqlite/local.d.ts +3 -3
- package/dist/backend/sqlite/local.js +6 -5
- package/dist/backend/sqlite/local.js.map +1 -1
- package/dist/{chunk-CAMMC3E4.cjs → chunk-3642VBVC.cjs} +65 -63
- package/dist/chunk-3642VBVC.cjs.map +1 -0
- package/dist/chunk-3E7SNSIA.cjs +55 -0
- package/dist/chunk-3E7SNSIA.cjs.map +1 -0
- package/dist/{chunk-6RMLO5O4.cjs → chunk-3IR2GEMJ.cjs} +8 -7
- package/dist/chunk-3IR2GEMJ.cjs.map +1 -0
- package/dist/{chunk-JKRKIEVA.cjs → chunk-4RBAJVEC.cjs} +125 -3
- package/dist/chunk-4RBAJVEC.cjs.map +1 -0
- package/dist/{chunk-IJORSAC3.js → chunk-6NRFUHMS.js} +4 -3
- package/dist/chunk-6NRFUHMS.js.map +1 -0
- package/dist/{chunk-YL4F3CMD.cjs → chunk-BF3ZDEUE.cjs} +96 -64
- package/dist/chunk-BF3ZDEUE.cjs.map +1 -0
- package/dist/{chunk-2LAT5BIK.cjs → chunk-D3EWZ67S.cjs} +2820 -1947
- package/dist/chunk-D3EWZ67S.cjs.map +1 -0
- package/dist/{chunk-XQFAJVYW.js → chunk-DJYRRDHA.js} +123 -4
- package/dist/chunk-DJYRRDHA.js.map +1 -0
- package/dist/{chunk-YQGLVWB2.cjs → chunk-EF3Z645U.cjs} +2 -52
- package/dist/chunk-EF3Z645U.cjs.map +1 -0
- package/dist/chunk-GYVBEZAO.js +120 -0
- package/dist/chunk-GYVBEZAO.js.map +1 -0
- package/dist/chunk-I4W7MKVG.cjs +1526 -0
- package/dist/chunk-I4W7MKVG.cjs.map +1 -0
- package/dist/{chunk-MXE2U3EX.js → chunk-IZ4PCL47.js} +3 -52
- package/dist/chunk-IZ4PCL47.js.map +1 -0
- package/dist/{chunk-7WW3WL6E.js → chunk-JHOBXD7O.js} +9 -7
- package/dist/chunk-JHOBXD7O.js.map +1 -0
- package/dist/{chunk-3HQDMOBK.cjs → chunk-KY5SDVA7.cjs} +619 -578
- package/dist/chunk-KY5SDVA7.cjs.map +1 -0
- package/dist/{chunk-EEYV6UOE.cjs → chunk-MBLXCV2Y.cjs} +2 -2
- package/dist/{chunk-EEYV6UOE.cjs.map → chunk-MBLXCV2Y.cjs.map} +1 -1
- package/dist/{chunk-LAZGHOZW.cjs → chunk-NG5N7R45.cjs} +28 -28
- package/dist/{chunk-LAZGHOZW.cjs.map → chunk-NG5N7R45.cjs.map} +1 -1
- package/dist/chunk-OIE2UPVG.cjs +126 -0
- package/dist/chunk-OIE2UPVG.cjs.map +1 -0
- package/dist/{chunk-NDM5PXEH.js → chunk-OS6KS3HV.js} +41 -9
- package/dist/chunk-OS6KS3HV.js.map +1 -0
- package/dist/chunk-PYD3UCDU.js +1480 -0
- package/dist/chunk-PYD3UCDU.js.map +1 -0
- package/dist/{chunk-W4ZX2GCP.js → chunk-QBYTJSAY.js} +2 -2
- package/dist/{chunk-W4ZX2GCP.js.map → chunk-QBYTJSAY.js.map} +1 -1
- package/dist/{chunk-HFBRAOF7.js → chunk-SPT5JMME.js} +3 -3
- package/dist/{chunk-HFBRAOF7.js.map → chunk-SPT5JMME.js.map} +1 -1
- package/dist/{chunk-SB65GJNY.cjs → chunk-SST7XDY6.cjs} +11 -11
- package/dist/{chunk-SB65GJNY.cjs.map → chunk-SST7XDY6.cjs.map} +1 -1
- package/dist/{chunk-TUA3MEJJ.js → chunk-UPDMJEW6.js} +606 -561
- package/dist/chunk-UPDMJEW6.js.map +1 -0
- package/dist/{chunk-WNORITK6.js → chunk-WLP6TKFB.js} +2691 -1817
- package/dist/chunk-WLP6TKFB.js.map +1 -0
- package/dist/chunk-XLUNB2TZ.js +53 -0
- package/dist/chunk-XLUNB2TZ.js.map +1 -0
- package/dist/{chunk-2BVHPOIG.js → chunk-XVUJY72M.js} +6 -6
- package/dist/{chunk-2BVHPOIG.js.map → chunk-XVUJY72M.js.map} +1 -1
- package/dist/{ddl-C8e8zDAE.d.ts → ddl-CVnHJcar.d.ts} +3 -3
- package/dist/{ddl-CwyG4HoQ.d.cts → ddl-Dxt_fBHr.d.cts} +3 -3
- package/dist/{extension-types-D1k0inWU.d.cts → extension-types-YGFJPrzK.d.cts} +1 -1
- package/dist/{extension-types-D1k0inWU.d.ts → extension-types-YGFJPrzK.d.ts} +1 -1
- package/dist/graph-extension/index.d.cts +4 -4
- package/dist/graph-extension/index.d.ts +4 -4
- package/dist/graph-merge/index.cjs +24 -23
- package/dist/graph-merge/index.cjs.map +1 -1
- package/dist/graph-merge/index.d.cts +6 -6
- package/dist/graph-merge/index.d.ts +6 -6
- package/dist/graph-merge/index.js +9 -8
- package/dist/graph-merge/index.js.map +1 -1
- package/dist/{index-BEK0VtNB.d.ts → index-DGX6D9j1.d.ts} +2 -2
- package/dist/{index-C8sSTErx.d.cts → index-SEvUHDA6.d.cts} +2 -2
- package/dist/index.cjs +129 -104
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -55
- package/dist/index.d.ts +25 -55
- package/dist/index.js +12 -10
- package/dist/index.js.map +1 -1
- package/dist/indexes/index.cjs +5 -4
- package/dist/indexes/index.d.cts +5 -5
- package/dist/indexes/index.d.ts +5 -5
- package/dist/indexes/index.js +3 -2
- package/dist/interchange/index.cjs +15 -15
- package/dist/interchange/index.d.cts +5 -5
- package/dist/interchange/index.d.ts +5 -5
- package/dist/interchange/index.js +2 -2
- package/dist/{manager-aVgxbGt-.d.cts → manager-BYl_zyZR.d.cts} +2 -2
- package/dist/{manager-8htmuGr-.d.ts → manager-C0_r0ALf.d.ts} +2 -2
- package/dist/{materialize-indexes-CAGhqPHQ.d.ts → materialize-indexes-CAkIYFV-.d.ts} +1 -1
- package/dist/{materialize-indexes-DEYkU-wr.d.cts → materialize-indexes-D20xNluI.d.cts} +1 -1
- package/dist/{postgres-jgXmD45C.d.cts → postgres-MzBPkYzq.d.ts} +2651 -596
- package/dist/{postgres-Dhom7iiX.d.ts → postgres-_9psmVGm.d.cts} +2651 -596
- package/dist/profiler/index.cjs +4 -3
- package/dist/profiler/index.cjs.map +1 -1
- package/dist/profiler/index.d.cts +7 -7
- package/dist/profiler/index.d.ts +7 -7
- package/dist/profiler/index.js +2 -1
- package/dist/profiler/index.js.map +1 -1
- package/dist/schema/index.cjs +30 -29
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.cts +7 -7
- package/dist/schema/index.d.ts +7 -7
- package/dist/schema/index.js +7 -6
- package/dist/schema/index.js.map +1 -1
- package/dist/{sqlite-BfLsCEqO.d.ts → sqlite-BfksSb45.d.cts} +2726 -455
- package/dist/{sqlite-DcQ_BCUW.d.cts → sqlite-gm44dShb.d.ts} +2726 -455
- package/dist/{store-view-pBOUrQte.d.ts → store-view-9jQ2rPum.d.cts} +437 -129
- package/dist/{store-view-lTcb_cnp.d.cts → store-view-FfE5_7eo.d.ts} +437 -129
- package/dist/{types-5RQwzWXx.d.cts → types-B6mhrcLv.d.cts} +2 -2
- package/dist/{types-Dk8r09KY.d.ts → types-C_UnQOQp.d.cts} +154 -23
- package/dist/{types-DGDiXAYc.d.ts → types-CdWxK-nD.d.ts} +2 -2
- package/dist/{types-Dvmn-eal.d.cts → types-DGylBhWo.d.ts} +154 -23
- package/dist/{where-Cx_glEEB.d.cts → where-Bpp2ul_z.d.cts} +2 -2
- package/dist/{where-CHZFELX0.d.ts → where-BsEXraKW.d.ts} +2 -2
- package/package.json +1 -1
- package/dist/chunk-2LAT5BIK.cjs.map +0 -1
- package/dist/chunk-3HQDMOBK.cjs.map +0 -1
- package/dist/chunk-6G35I3VW.cjs +0 -40
- package/dist/chunk-6G35I3VW.cjs.map +0 -1
- package/dist/chunk-6RMLO5O4.cjs.map +0 -1
- package/dist/chunk-7WW3WL6E.js.map +0 -1
- package/dist/chunk-CAMMC3E4.cjs.map +0 -1
- package/dist/chunk-IJORSAC3.js.map +0 -1
- package/dist/chunk-JKRKIEVA.cjs.map +0 -1
- package/dist/chunk-M6OC455F.js +0 -65
- package/dist/chunk-M6OC455F.js.map +0 -1
- package/dist/chunk-MXE2U3EX.js.map +0 -1
- package/dist/chunk-NDM5PXEH.js.map +0 -1
- package/dist/chunk-RP7YITHX.cjs +0 -70
- package/dist/chunk-RP7YITHX.cjs.map +0 -1
- package/dist/chunk-SAJSMR2B.js +0 -35
- package/dist/chunk-SAJSMR2B.js.map +0 -1
- package/dist/chunk-TUA3MEJJ.js.map +0 -1
- package/dist/chunk-WNORITK6.js.map +0 -1
- package/dist/chunk-XQFAJVYW.js.map +0 -1
- package/dist/chunk-YL4F3CMD.cjs.map +0 -1
- package/dist/chunk-YQGLVWB2.cjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/backend/drizzle/execution/postgres-execution.ts","../src/backend/drizzle/postgres.ts"],"names":["sql","tables","options"],"mappings":"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","file":"chunk-NDM5PXEH.js","sourcesContent":["import { type SQL } from \"drizzle-orm\";\nimport { type PgDatabase, type PgQueryResultHKT } from \"drizzle-orm/pg-core\";\n\nimport { getOrCreateLru } from \"./lru\";\nimport {\n type CompiledSqlQuery,\n compileQueryWithDialect,\n type PreparedSqlStatement,\n type SqlExecutionAdapter,\n} from \"./types\";\n\n/**\n * Edge-safe: this module is loaded on Cloudflare Workers / Vercel Edge\n * via `@neondatabase/serverless`, so it must not statically import any\n * `node:*` module. Use only platform-neutral primitives.\n */\n\ntype PgQueryResult = Readonly<{\n rows: readonly unknown[];\n}>;\n\n/**\n * The wire-protocol-shaped client the fast path calls. Both the wrapped\n * node-postgres and postgres-js clients normalize to this signature; what\n * differs is whether the underlying driver gets a server-side prepared\n * statement (node-postgres path) or relies on its own internal preparation\n * (postgres-js).\n */\ntype PgQueryClient = Readonly<{\n query: (sqlText: string, params: readonly unknown[]) => Promise<PgQueryResult>;\n}>;\n\n/**\n * Minimal shape of the node-postgres / @neondatabase/serverless client\n * we rely on. Both accept either positional `query(text, values)`\n * (unnamed) or the configuration-object form `query({name, text, values})`\n * which uses a server-side prepared statement keyed by `name`.\n */\ntype NodePgQueryConfig = Readonly<{\n name: string;\n text: string;\n values: readonly unknown[];\n}>;\n\ntype NodePgClient = Readonly<{\n query: ((\n sqlText: string,\n params?: readonly unknown[],\n ) => Promise<PgQueryResult>) &\n ((config: NodePgQueryConfig) => Promise<PgQueryResult>);\n}>;\n\n/**\n * Minimal shape of the postgres-js tagged-template `Sql` client we rely on\n * for the raw fast path. `unsafe(sqlText, params)` is postgres-js's\n * parameterized-raw-SQL entry point and resolves to a row array.\n */\ntype PostgresJsClient = ((...args: readonly unknown[]) => unknown) &\n Readonly<{\n unsafe: (\n sqlText: string,\n params?: readonly unknown[],\n ) => PromiseLike<readonly unknown[]>;\n }>;\n\ntype PgClientCarrier = Readonly<{\n $client?: unknown;\n}>;\n\nexport type AnyPgDatabase = PgDatabase<PgQueryResultHKT, Record<string, unknown>>;\n\nexport type PostgresExecutionAdapter = SqlExecutionAdapter;\n\nfunction hasFunctionProperty<K extends string>(\n value: unknown,\n property: K,\n): value is Readonly<Record<K, (...args: readonly unknown[]) => unknown>> {\n if (value === undefined || value === null) {\n return false;\n }\n const candidate = value as Readonly<Record<K, unknown>>;\n return typeof candidate[property] === \"function\";\n}\n\nfunction isPgNativeClient(candidate: unknown): candidate is NodePgClient {\n // pg `Pool` / `Client` and @neondatabase/serverless `Pool` / `Client`\n // are object instances with a `.query` method. We require non-callable\n // here so we don't accidentally swallow the postgres-js or neon-http\n // tagged-template clients (both callable, both also expose `.query`).\n return typeof candidate === \"object\" && hasFunctionProperty(candidate, \"query\");\n}\n\nfunction isPgliteClient(candidate: unknown): candidate is NodePgClient {\n // PGlite (Postgres-in-WASM) exposes `.query(sql, params, options)` like\n // node-postgres and so also satisfies `isPgNativeClient` — but it does NOT\n // accept the `{name, text, values}` named-statement config form. Passing\n // that object doesn't merely fail the call: it desyncs PGlite's single\n // wire connection, so every subsequent query errors with `08P01`. Detect\n // PGlite structurally via `dumpDataDir` (a PGlite-specific method absent on\n // pg `Pool`/`Client`) so `resolvePgClient` can route it to the unnamed\n // positional wrapper regardless of `prepareStatements`. Duck-typed with no\n // static import, keeping this module dependency-free and edge-safe.\n return (\n typeof candidate === \"object\" &&\n hasFunctionProperty(candidate, \"query\") &&\n hasFunctionProperty(candidate, \"dumpDataDir\")\n );\n}\n\nfunction isPostgresJsClient(candidate: unknown): candidate is PostgresJsClient {\n // postgres-js's tagged-template Sql is callable, has `.unsafe` (raw\n // parameterized executor), and has `.begin` (transaction starter).\n // Neon HTTP is also callable + has `.unsafe`, but that `.unsafe` is a\n // fragment builder rather than a query executor, so we discriminate\n // on `.begin` (postgres-js only).\n return (\n typeof candidate === \"function\" &&\n hasFunctionProperty(candidate, \"unsafe\") &&\n hasFunctionProperty(candidate, \"begin\")\n );\n}\n\n/**\n * @neondatabase/serverless's `neon(url)` HTTP-only tagged-template\n * function. Distinguishing markers vs postgres-js: `.transaction`\n * (an HTTP-batch transaction submitter) instead of `.begin`. Its\n * `.query`/`.unsafe` methods have different signatures from pg's, so\n * the fast path can't drive them safely — Drizzle's neon-http session\n * handles them correctly via the `db.execute` slow path.\n *\n * Exported so the backend factory can auto-disable the `transactions`\n * capability when this driver is detected (HTTP can't hold a session,\n * so multi-statement transactions are not available regardless).\n */\nexport function isNeonHttpClient(db: AnyPgDatabase): boolean {\n const candidate = (db as PgClientCarrier).$client;\n return (\n typeof candidate === \"function\" &&\n hasFunctionProperty(candidate, \"transaction\") &&\n !hasFunctionProperty(candidate, \"begin\")\n );\n}\n\n/**\n * Default cap on the number of distinct SQL strings tracked for\n * server-side prepared statements. Mirrors the SQLite adapter's\n * `DEFAULT_PREPARED_STATEMENT_CACHE_MAX`. High-cardinality SQL text —\n * variable-length IN-list expansions, generated aliases, custom\n * `backend.execute()` calls — would otherwise grow this map (and the\n * matching per-session prepared-statement memory inside PostgreSQL)\n * without bound.\n */\nconst DEFAULT_POSTGRES_STATEMENT_CACHE_MAX = 256;\n\n/**\n * Module-scope cache mapping each unique SQL text to a stable\n * prepared-statement name. PostgreSQL only requires statement names to\n * be unique per session, so a monotonic counter is sufficient — and\n * avoids depending on a hash primitive (`node:crypto` doesn't exist on\n * Cloudflare Workers, Web Crypto's digest is async).\n *\n * The cache is module-scope on purpose: pg `Pool` reuses connections,\n * and the same SQL text needs the same statement name across every\n * adapter that might end up driving the same connection. Without\n * shared naming, two adapters could try to register two different\n * statements under the same name on a recycled connection.\n *\n * Bounded by `cacheMax` per call (LRU eviction). The counter is\n * deliberately independent of the cache size so eviction never\n * recycles a name — a recycled name could collide with a still-cached\n * statement on a long-lived pg connection (\"prepared statement\n * already exists\" error with different text).\n */\nconst STATEMENT_NAME_CACHE = new Map<string, string>();\nlet nextStatementId = 0;\n\nfunction statementNameFor(sqlText: string, cacheMax: number): string {\n return getOrCreateLru(STATEMENT_NAME_CACHE, sqlText, cacheMax, () => {\n const name = `tg_${nextStatementId}`;\n nextStatementId += 1;\n return name;\n });\n}\n\n/**\n * Test-only: reset the module-scope statement-name state. Used to\n * isolate tests that assert on counter values or eviction behavior.\n */\nexport function resetStatementNameCacheForTests(): void {\n STATEMENT_NAME_CACHE.clear();\n nextStatementId = 0;\n}\n\n/**\n * Walks a row and replaces any `Date` instance with its ISO-string\n * equivalent. Used to normalize node-postgres / neon-serverless output\n * when we bypass Drizzle's session: pg's default type parsers return\n * `Date` objects for `timestamptz` / `timestamp` / `date` columns, but\n * TypeGraph's row contract everywhere downstream is \"timestamps come\n * back as ISO strings.\" Drizzle's session installs a per-query type\n * override that does the same thing; this is the dependency-free,\n * edge-safe equivalent.\n *\n * O(columns × rows) — negligible for the row counts TypeGraph queries\n * return (typical: 1–100 rows × 5–30 columns).\n */\nfunction normalizeRow(row: unknown): unknown {\n if (row === null || typeof row !== \"object\") return row;\n let mutated: Record<string, unknown> | undefined;\n for (const key of Object.keys(row)) {\n const value = (row as Record<string, unknown>)[key];\n if (value instanceof Date) {\n mutated ??= { ...(row as Record<string, unknown>) };\n mutated[key] = value.toISOString();\n }\n }\n return mutated ?? row;\n}\n\nfunction normalizeRows(\n rows: readonly unknown[],\n): readonly unknown[] {\n // Single-pass scan; only allocate a new array if any row contained a\n // Date that needed normalizing. Most rows on most queries don't.\n let mutated: unknown[] | undefined;\n for (let index = 0; index < rows.length; index += 1) {\n const original = rows[index];\n const normalized = normalizeRow(original);\n if (normalized !== original) {\n mutated ??= [...rows] as unknown[];\n mutated[index] = normalized;\n }\n }\n return mutated ?? rows;\n}\n\n/**\n * Wraps a node-postgres / neon-serverless client so every call goes\n * through a server-side prepared statement keyed by a stable name. The\n * first call on each connection parses + plans + executes; subsequent\n * calls with the same statement name reuse the cached plan and skip\n * both parse and plan phases. Measurable on multi-CTE TypeGraph\n * queries: 3-hop drops from ~7-12ms (parse + plan + execute every call)\n * to ~0.8ms median (execute only).\n *\n * Returned rows are normalized so that `Date` objects from default pg\n * type parsers become ISO strings, matching the row shape Drizzle's\n * session would produce. Without this, `ctx.<alias>.meta.createdAt`\n * style accessors in the SELECT-result path return Date instances,\n * breaking user code that expects strings.\n */\nfunction wrapNodePgClient(\n client: NodePgClient,\n cacheMax: number,\n): PgQueryClient {\n return {\n async query(\n sqlText: string,\n params: readonly unknown[],\n ): Promise<PgQueryResult> {\n const result = await client.query({\n name: statementNameFor(sqlText, cacheMax),\n text: sqlText,\n values: params,\n });\n return { rows: normalizeRows(result.rows) };\n },\n };\n}\n\n/**\n * Variant that bypasses server-side prepared statements entirely.\n * Required for pgbouncer in transaction-pool mode (named statements\n * registered on one backend connection aren't visible on the next),\n * and useful when memory-sensitive workloads can't afford to keep\n * prepared statements warm across the pool.\n *\n * Same Date→ISO normalization as `wrapNodePgClient` so the row contract\n * downstream stays identical.\n */\nfunction wrapNodePgClientUnnamed(client: NodePgClient): PgQueryClient {\n return {\n async query(\n sqlText: string,\n params: readonly unknown[],\n ): Promise<PgQueryResult> {\n const result = await client.query(sqlText, params);\n return { rows: normalizeRows(result.rows) };\n },\n };\n}\n\nfunction adaptPostgresJsClient(sql: PostgresJsClient): PgQueryClient {\n return {\n async query(\n sqlText: string,\n params: readonly unknown[],\n ): Promise<PgQueryResult> {\n // postgres-js handles its own statement preparation internally\n // (controlled by the `prepare` connection option, default true),\n // so we don't need to name statements here ourselves.\n const rows = await sql.unsafe(sqlText, params);\n return { rows };\n },\n };\n}\n\n/**\n * Resolves a Drizzle-wrapped PostgreSQL client to a uniform `{query}` shape\n * that the fast path can call. Supports:\n *\n * - `drizzle-orm/node-postgres` (pg Pool / Client) — wrapped to use\n * server-side prepared statements with stable counter-derived names\n * keyed by the SQL text (see `statementNameFor`). When\n * `prepareStatements` is `false`, falls back to unnamed\n * `client.query(text, values)` for pgbouncer transaction-pool\n * compatibility.\n * - `drizzle-orm/neon-serverless` (@neondatabase/serverless Pool) —\n * pg-Pool-compatible, takes the same wrapper as node-postgres\n * - `drizzle-orm/postgres-js` (postgres-js tagged-template Sql) — adapted\n * via `.unsafe(sql, params)`. Safe because Drizzle installs transparent\n * parsers on the same client instance at `drizzle()` time, so direct\n * calls and Drizzle-routed calls produce identical row shapes.\n * - `drizzle-orm/pglite` (PGlite, Postgres-in-WASM) — routed to the unnamed\n * wrapper unconditionally. PGlite's `.query` has no named-statement config\n * form (and server-side prepared statements buy nothing for an in-process\n * engine), so `prepareStatements` is ignored for it.\n *\n * Returns `undefined` for `drizzle-orm/neon-http` and any other driver we\n * don't recognize; callers fall back to `db.execute(sql)`, which goes\n * through the Drizzle session and is always correct (just without the\n * server-side prepared-statement perf win).\n */\nfunction resolvePgClient(\n db: AnyPgDatabase,\n prepareStatements: boolean,\n cacheMax: number,\n): PgQueryClient | undefined {\n // Order matters: neon-http and postgres-js are both callable + have\n // `.unsafe`, but neon-http's `.unsafe` is a fragment builder (not a\n // query executor) and its `.query` doesn't accept the {name, text,\n // values} config form. Skip the fast path entirely for neon-http and\n // let Drizzle's session route the call.\n if (isNeonHttpClient(db)) {\n return undefined;\n }\n const client = (db as PgClientCarrier).$client;\n // Must precede `isPgNativeClient`: PGlite matches it too (object with\n // `.query`), but only the unnamed wrapper is safe for it (see\n // `isPgliteClient`).\n if (isPgliteClient(client)) {\n return wrapNodePgClientUnnamed(client);\n }\n if (isPgNativeClient(client)) {\n return prepareStatements\n ? wrapNodePgClient(client, cacheMax)\n : wrapNodePgClientUnnamed(client);\n }\n if (isPostgresJsClient(client)) {\n // postgres-js handles its own preparation internally (controlled\n // by the driver's own `prepare` connection option) — the\n // `prepareStatements` flag here only affects the node-postgres\n // path.\n return adaptPostgresJsClient(client);\n }\n return undefined;\n}\n\n/**\n * Normalizes the result shape of `db.execute(sql)` across drivers.\n *\n * - `drizzle-orm/node-postgres` / `drizzle-orm/neon-serverless` return a\n * pg-style `{ rows, rowCount, ... }` object.\n * - `drizzle-orm/postgres-js` returns the raw postgres-js result, which is\n * a plain array of rows (with extra non-enumerable properties like\n * `count` / `command`).\n *\n * The backend contract is \"a row array.\" Normalize here so every caller\n * downstream can assume the same shape.\n */\nasync function executeDrizzleQuery<TRow>(\n db: AnyPgDatabase,\n query: SQL,\n): Promise<readonly TRow[]> {\n const result = await db.execute(query);\n if (Array.isArray(result)) {\n return result as readonly TRow[];\n }\n return (result as Readonly<{ rows: readonly TRow[] }>).rows;\n}\n\nfunction createPgPreparedStatement(\n pgClient: PgQueryClient,\n sqlText: string,\n): PreparedSqlStatement {\n return {\n async execute<TRow>(params: readonly unknown[]): Promise<readonly TRow[]> {\n const result = await pgClient.query(sqlText, params);\n return result.rows as readonly TRow[];\n },\n };\n}\n\n/**\n * Tuning options for the PostgreSQL execution fast path.\n *\n * Defaults are correct for `drizzle-orm/node-postgres` against a\n * standalone PostgreSQL or session-pooled deployment. Override when:\n *\n * - **Pooling through pgbouncer in transaction-pool mode.** Set\n * `prepareStatements: false`. Named statements registered on one\n * pooled backend connection aren't visible to the next, so the\n * server-side prepared-statement cache must be off.\n * - **High-cardinality SQL text.** If the application emits many\n * distinct compiled SQL strings (variable-length IN-list expansions,\n * custom `backend.execute()` calls, generated aliases), tune\n * `preparedStatementCacheMax` down to bound per-session memory more\n * aggressively, or up if memory is plentiful and you want fewer\n * re-PREPAREs.\n */\nexport type PostgresExecutionAdapterOptions = Readonly<{\n /**\n * When `true` (default), node-postgres / neon-serverless calls go\n * through `client.query({name, text, values})` so PostgreSQL caches\n * the parsed/planned statement per session. Set to `false` to issue\n * unnamed `client.query(text, values)` calls — required for\n * pgbouncer transaction-pool mode.\n */\n prepareStatements?: boolean;\n /**\n * Maximum number of distinct SQL strings tracked for prepared-\n * statement naming. Defaults to `DEFAULT_POSTGRES_STATEMENT_CACHE_MAX`\n * (256). Ignored when `prepareStatements` is `false`.\n */\n preparedStatementCacheMax?: number;\n}>;\n\nexport function createPostgresExecutionAdapter(\n db: AnyPgDatabase,\n options: PostgresExecutionAdapterOptions = {},\n): PostgresExecutionAdapter {\n const prepareStatements = options.prepareStatements ?? true;\n const cacheMax =\n options.preparedStatementCacheMax ?? DEFAULT_POSTGRES_STATEMENT_CACHE_MAX;\n const pgClient = resolvePgClient(db, prepareStatements, cacheMax);\n\n function compile(query: SQL): CompiledSqlQuery {\n return compileQueryWithDialect(db, query, \"PostgreSQL\");\n }\n\n if (pgClient === undefined) {\n return {\n compile,\n async execute<TRow>(query: SQL): Promise<readonly TRow[]> {\n return executeDrizzleQuery<TRow>(db, query);\n },\n };\n }\n\n const pgQueryClient = pgClient;\n\n async function executeCompiled<TRow>(\n compiledQuery: CompiledSqlQuery,\n ): Promise<readonly TRow[]> {\n const result = await pgQueryClient.query(\n compiledQuery.sql,\n compiledQuery.params,\n );\n return result.rows as readonly TRow[];\n }\n\n return {\n compile,\n async execute<TRow>(query: SQL): Promise<readonly TRow[]> {\n // Fast path: compile via Drizzle's dialect, then execute through\n // the wrapped client directly. Bypasses Drizzle's session\n // overhead (logging spans, plan-cache bookkeeping for typed\n // queries we don't use), and on node-postgres this also enables\n // the server-side prepared-statement path because the wrapped\n // client assigns each unique SQL a stable statement name.\n const compiled = compile(query);\n return executeCompiled<TRow>(compiled);\n },\n executeCompiled,\n prepare(sqlText: string): PreparedSqlStatement {\n return createPgPreparedStatement(pgQueryClient, sqlText);\n },\n };\n}\n","/**\n * PostgreSQL backend adapter for TypeGraph.\n *\n * Works with any Drizzle PostgreSQL database instance. Tested against:\n * - `drizzle-orm/node-postgres` (pg Pool / Client)\n * - `drizzle-orm/postgres-js` (postgres-js tagged-template client)\n * - `drizzle-orm/neon-serverless` (@neondatabase/serverless Pool / Client)\n * - `drizzle-orm/neon-http` (@neondatabase/serverless `neon(url)`) —\n * transactions are auto-disabled because HTTP can't hold a session;\n * use `drizzle-orm/neon-serverless` if you need transactional writes.\n *\n * - `drizzle-orm/pglite` (PGlite, Postgres-in-WASM) — the execution\n * fast path detects PGlite and routes it correctly (its `.query` has no\n * named-statement form). For a batteries-included in-process setup, see\n * `createLocalPgliteBackend` in `@nicia-ai/typegraph/postgres/pglite`.\n *\n * Other pg-protocol Drizzle adapters (Vercel Postgres, Supabase via pg)\n * work unchanged because they all expose a compatible `db.execute()` /\n * `db.transaction()` surface.\n *\n * @example\n * ```typescript\n * import { drizzle } from \"drizzle-orm/node-postgres\";\n * import { Pool } from \"pg\";\n * import { createPostgresBackend, tables } from \"@nicia-ai/typegraph/postgres\";\n *\n * const pool = new Pool({ connectionString: process.env.DATABASE_URL });\n * const db = drizzle(pool);\n * const backend = createPostgresBackend(db, { tables });\n * ```\n */\nimport {\n and,\n eq,\n getTableName,\n inArray,\n isNull,\n type SQL,\n sql,\n} from \"drizzle-orm\";\nimport { PgDatabase, PgTransaction } from \"drizzle-orm/pg-core\";\n\nimport { ConfigurationError } from \"../../errors\";\nimport type { SqlTableNames } from \"../../query/compiler/schema\";\nimport { quoteIdentifier } from \"../../query/compiler/utils\";\nimport {\n buildFulltextCapabilities,\n type FulltextStrategy,\n tsvectorStrategy,\n} from \"../../query/dialect/fulltext-strategy\";\nimport {\n assertPgvectorEfSearch,\n pgvectorStrategy,\n} from \"../../query/dialect/vector/pgvector-strategy\";\nimport {\n assertVectorSearchLimit,\n buildVectorCapabilities,\n type VectorSlot,\n type VectorStrategy,\n} from \"../../query/dialect/vector-strategy\";\nimport { isMissingTableError } from \"../../utils/sql-errors\";\nimport {\n type AdoptedTransaction,\n type BackendCapabilities,\n type CommitSchemaVersionParams,\n type ContributionMaterializationIdentity,\n type ContributionMaterializationRow,\n type CreateVectorIndexParams,\n type DeleteEmbeddingParams,\n type DeleteFulltextBatchParams,\n type DeleteFulltextParams,\n type DropVectorIndexParams,\n type FulltextSearchParams,\n type FulltextSearchResult,\n type GraphBackend,\n type IndexMaterializationRow,\n type KindRemovalRow,\n POSTGRES_CAPABILITIES,\n type RecordContributionMaterializationParams,\n type RecordIndexMaterializationParams,\n type RecordKindRemovalParams,\n type SchemaVersionRow,\n type SetActiveVersionParams,\n type TransactionBackend,\n type TransactionOptions,\n type UpsertEmbeddingParams,\n type UpsertFulltextBatchParams,\n type UpsertFulltextParams,\n type VectorSearchParams,\n type VectorSearchResult,\n} from \"../types\";\nimport {\n buildContributionInsertValues,\n buildContributionOnConflictSet,\n createContributionMaterializer,\n gateFulltext,\n gateFulltextMethods,\n mapContributionMaterializationRow,\n POSTGRES_CONTRIBUTION_MAT_TIMESTAMPS,\n} from \"./contribution-materializations\";\nimport { generatePgCreateTableSQL, generatePostgresDDL } from \"./ddl\";\nimport {\n type AnyPgDatabase,\n createPostgresExecutionAdapter,\n isNeonHttpClient,\n type PostgresExecutionAdapter,\n type PostgresExecutionAdapterOptions,\n} from \"./execution/postgres-execution\";\nimport {\n buildMaterializationInsertValues,\n buildMaterializationOnConflictSet,\n mapMaterializationRow,\n POSTGRES_INDEX_MAT_TIMESTAMPS,\n} from \"./index-materializations\";\nimport {\n buildKindRemovalInsertValues,\n buildKindRemovalOnConflictSet,\n mapKindRemovalRow,\n POSTGRES_KIND_REMOVAL_TIMESTAMPS,\n} from \"./kind-removals\";\nimport {\n assertAdoptedDialect,\n type CommonOperationBackend,\n createCommonOperationBackend,\n type InternalOperationBackend,\n} from \"./operation-backend-core\";\nimport { createPostgresOperationStrategy } from \"./operations/strategy\";\nimport {\n coerceNumericScore,\n createEdgeRowMapper,\n createNodeRowMapper,\n createSchemaVersionRowMapper,\n createUniqueRowMapper,\n nowIso,\n POSTGRES_ROW_MAPPER_CONFIG,\n} from \"./row-mappers\";\nimport {\n type PostgresTables,\n tables as defaultTables,\n} from \"./schema/postgres\";\nimport {\n createVectorSlotLatch,\n mapVectorWriteError,\n vectorSlotFromCreateIndexParams,\n vectorSlotFromDropIndexParams,\n vectorSlotFromParams,\n type VectorSlotLatch,\n} from \"./vector-runtime\";\n\n// ============================================================\n// Types\n// ============================================================\n\n/**\n * Options for creating a PostgreSQL backend.\n */\nexport type PostgresBackendOptions = Readonly<{\n /**\n * Custom table definitions. Use createPostgresTables() to customize table names.\n * Defaults to standard TypeGraph table names.\n */\n tables?: PostgresTables;\n /**\n * Fulltext strategy override. Defaults to `tsvectorStrategy`\n * (Postgres built-in `tsvector` + GIN). Pass a custom strategy here to\n * swap the entire fulltext stack — DDL, MATCH condition, rank\n * expression, and snippet generation — for alternate Postgres\n * backends like ParadeDB (`pg_search`), pg_trgm similarity, or\n * pgroonga without forking TypeGraph.\n */\n fulltext?: FulltextStrategy;\n /**\n * Vector strategy override. Defaults to `pgvectorStrategy` (pgvector's\n * `vector(N)` columns + HNSW/IVFFlat). The strategy owns per-`(kind,\n * field)` typed storage — DDL, upsert, delete, similarity search, and\n * ANN index lifecycle — and advertises `strategy.capabilities` as\n * `capabilities.vector`. Pass a custom strategy to swap the entire\n * vector stack for an alternate Postgres extension without forking\n * TypeGraph.\n *\n * Pass `false` to disable vector support entirely. The backend then\n * advertises no `capabilities.vector` and omits the embedding/search\n * methods, mirroring a SQLite connection without sqlite-vec. Required\n * for an in-process Postgres (e.g. PGlite) built without the pgvector\n * extension: the default `pgvectorStrategy` assumes `vector(N)` exists,\n * so any embedding write or `CREATE EXTENSION vector` would otherwise\n * hard-fail at runtime.\n */\n vector?: VectorStrategy | false;\n /**\n * Override specific backend capabilities. Useful when the underlying\n * driver doesn't support a feature TypeGraph would otherwise assume —\n * for example, an HTTP-only Postgres driver that can't hold a session\n * across statements would need `{ transactions: false }` so\n * TypeGraph falls through to non-transactional execution paths.\n *\n * `drizzle-orm/neon-http` is auto-detected and has `transactions`\n * disabled without an explicit override; this option exists for\n * other HTTP-style drivers and for tests that need to simulate a\n * capability gap.\n */\n capabilities?: Partial<BackendCapabilities>;\n /**\n * Use server-side prepared statements (named statements cached per\n * pg connection) on the node-postgres / neon-serverless fast path.\n * Defaults to `true`. Set to `false` when pooling through pgbouncer\n * in transaction-pool mode — pgbouncer routes successive statements\n * over different backend connections, and a `name` registered on one\n * is invisible on the next.\n *\n * No effect on `drizzle-orm/postgres-js` (handles preparation\n * internally) or `drizzle-orm/neon-http` (no fast path).\n */\n prepareStatements?: boolean;\n /**\n * Cap on the number of distinct SQL strings tracked for\n * prepared-statement naming. Defaults to 256. The cache is LRU-\n * bounded so high-cardinality SQL text (variable-length IN-lists,\n * generated aliases, `backend.execute()` calls with one-off SQL)\n * doesn't grow unbounded in either the Node process or in\n * PostgreSQL's per-session prepared-statement memory. Worst-case\n * server-side footprint is roughly `cap × pool size` statements\n * across all pooled connections. Ignored when\n * `prepareStatements` is `false`.\n */\n preparedStatementCacheMax?: number;\n}>;\n\nconst POSTGRES_MAX_BIND_PARAMETERS = 65_535;\nconst NODE_INSERT_PARAM_COUNT = 9;\nconst EDGE_INSERT_PARAM_COUNT = 12;\nconst POSTGRES_NODE_INSERT_BATCH_SIZE = Math.max(\n 1,\n Math.floor(POSTGRES_MAX_BIND_PARAMETERS / NODE_INSERT_PARAM_COUNT),\n);\nconst POSTGRES_EDGE_INSERT_BATCH_SIZE = Math.max(\n 1,\n Math.floor(POSTGRES_MAX_BIND_PARAMETERS / EDGE_INSERT_PARAM_COUNT),\n);\nconst POSTGRES_GET_NODES_ID_CHUNK_SIZE = Math.max(\n 1,\n POSTGRES_MAX_BIND_PARAMETERS - 2,\n);\nconst POSTGRES_GET_EDGES_ID_CHUNK_SIZE = Math.max(\n 1,\n POSTGRES_MAX_BIND_PARAMETERS - 1,\n);\nconst CHECK_UNIQUE_BATCH_FIXED_PARAM_COUNT = 3;\nconst POSTGRES_CHECK_UNIQUE_BATCH_CHUNK_SIZE = Math.max(\n 1,\n POSTGRES_MAX_BIND_PARAMETERS - CHECK_UNIQUE_BATCH_FIXED_PARAM_COUNT,\n);\n\n// ============================================================\n// Utilities\n// ============================================================\n\nconst toNodeRow = createNodeRowMapper(POSTGRES_ROW_MAPPER_CONFIG);\nconst toEdgeRow = createEdgeRowMapper(POSTGRES_ROW_MAPPER_CONFIG);\nconst toUniqueRow = createUniqueRowMapper(POSTGRES_ROW_MAPPER_CONFIG);\nconst toSchemaVersionRow = createSchemaVersionRowMapper(\n POSTGRES_ROW_MAPPER_CONFIG,\n);\n\nfunction buildPostgresCapabilities(\n fulltextStrategy: FulltextStrategy,\n vectorStrategy: VectorStrategy | undefined,\n): BackendCapabilities {\n return {\n ...POSTGRES_CAPABILITIES,\n ...(vectorStrategy === undefined ?\n {}\n : { vector: buildVectorCapabilities(vectorStrategy) }),\n fulltext: buildFulltextCapabilities(fulltextStrategy),\n };\n}\n\n// ============================================================\n// Backend Factory\n// ============================================================\n\n/**\n * Creates a TypeGraph backend for PostgreSQL databases.\n *\n * Works with any Drizzle PostgreSQL instance regardless of the underlying driver.\n *\n * @param db - A Drizzle PostgreSQL database instance\n * @param options - Backend configuration\n * @returns A GraphBackend implementation\n */\nexport function createPostgresBackend(\n db: AnyPgDatabase,\n options: PostgresBackendOptions = {},\n): GraphBackend {\n const tables = options.tables ?? defaultTables;\n const fulltextStrategy = options.fulltext ?? tsvectorStrategy;\n // pgvector is compiled into a standalone Postgres server, so it is wired\n // unconditionally by default (overridable for alternate Postgres vector\n // stacks). `vector: false` disables it — required for an in-process\n // Postgres (PGlite) built without the pgvector extension, where the\n // default strategy's `vector(N)` DDL would hard-fail.\n const vectorStrategy =\n options.vector === false ? undefined : (options.vector ?? pgvectorStrategy);\n // One latch per backend instance, shared with every transaction-scoped\n // backend so a slot's per-field table is created at most once per process.\n // Absent when vector support is disabled.\n const vectorSlotLatch =\n vectorStrategy === undefined ? undefined : createVectorSlotLatch();\n const baseCapabilities = buildPostgresCapabilities(\n fulltextStrategy,\n vectorStrategy,\n );\n // HTTP-only drivers (notably `drizzle-orm/neon-http`) can't hold a\n // session across statements, so multi-statement transactions are\n // unavailable regardless of what we declare. Auto-detect and downgrade\n // the capability so callers get correct fallback behavior without\n // having to remember to override it themselves.\n const httpOnlyOverrides = isNeonHttpClient(db) ? { transactions: false } : {};\n const capabilities: BackendCapabilities = {\n ...baseCapabilities,\n ...httpOnlyOverrides,\n ...options.capabilities,\n };\n const adapterOptions: PostgresExecutionAdapterOptions = {\n ...(options.prepareStatements === undefined ?\n {}\n : { prepareStatements: options.prepareStatements }),\n ...(options.preparedStatementCacheMax === undefined ?\n {}\n : { preparedStatementCacheMax: options.preparedStatementCacheMax }),\n };\n const executionAdapter = createPostgresExecutionAdapter(db, adapterOptions);\n const tableNames: SqlTableNames = {\n nodes: getTableName(tables.nodes),\n edges: getTableName(tables.edges),\n fulltext: tables.fulltextTableName,\n uniques: getTableName(tables.uniques),\n };\n // Pre-quote identifiers so refreshStatistics() doesn't rebuild the\n // ANALYZE statement on every call. Per-field vector tables are created\n // lazily and live outside this base set, so they are not ANALYZEd here.\n const analyzeStatement = sql`ANALYZE ${sql.join(\n [\n tableNames.nodes,\n tableNames.edges,\n getTableName(tables.uniques),\n tableNames.fulltext,\n ].map((name) => quoteIdentifier(name)),\n sql`, `,\n )}`;\n const operationStrategy = createPostgresOperationStrategy(\n tables,\n fulltextStrategy,\n );\n const operations = createPostgresOperationBackend({\n db,\n executionAdapter,\n adapterOptions,\n operationStrategy,\n tableNames,\n capabilities,\n fulltextStrategy,\n vectorStrategy,\n vectorSlotLatch,\n });\n\n /**\n * Runs `fn` inside a Postgres transaction, holding an\n * `pg_advisory_xact_lock` keyed on the graph id. The advisory lock\n * serializes all schema commits per-graph: the read-then-write CAS in\n * `commitSchemaVersion` is safe even for the initial-commit case\n * where there is no row yet to `SELECT ... FOR UPDATE`.\n *\n * Refuses on backends that don't support transactions\n * (`drizzle-orm/neon-http`). The orphan-row crash window cannot be\n * eliminated without atomicity, so silent best-effort degradation is\n * worse than a typed error.\n */\n function runSchemaWriteTransaction<T>(\n graphId: string,\n fn: (tx: CommonOperationBackend) => Promise<T>,\n ): Promise<T> {\n if (!capabilities.transactions) {\n throw new ConfigurationError(\n \"commitSchemaVersion and setActiveVersion require atomic transactions, \" +\n \"but this Postgres backend does not provide them. The drizzle-orm/neon-http \" +\n \"driver communicates over HTTP and cannot hold a session across statements; \" +\n \"use drizzle-orm/neon-serverless (websocket) for transactional writes.\",\n {\n backend: \"postgres\",\n capability: \"transactions\",\n supportsTransactions: false,\n },\n );\n }\n\n return db.transaction(async (tx) => {\n // Advisory lock: hashtext($graphId) is collision-tolerant for the\n // size of an active graph set; collisions just serialize unrelated\n // graphs which is harmless. Held until the transaction commits.\n await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${graphId}))`);\n // Advisory lock is held here, so the schema-write-capable\n // InternalOperationBackend is used intentionally (see its type).\n const txBackend = createTransactionBackend({\n db: tx,\n adapterOptions,\n operationStrategy,\n tableNames,\n capabilities,\n fulltextStrategy,\n vectorStrategy,\n vectorSlotLatch,\n });\n return fn(txBackend);\n });\n }\n\n // Durable fulltext materialization (#135): the dialect-specific\n // marker-table primitives. Orchestration (materialize / assert /\n // per-instance cache) lives once in `createContributionMaterializer`.\n const matTable = tables.contributionMaterializations;\n\n async function ensureContributionMaterializationsTableImpl(): Promise<void> {\n await db.execute(sql.raw(generatePgCreateTableSQL(matTable)));\n }\n\n async function getContributionMaterializationRow(\n identity: ContributionMaterializationIdentity,\n ): Promise<ContributionMaterializationRow | undefined> {\n const rows = await db\n .select()\n .from(matTable)\n .where(\n and(\n eq(matTable.graphId, identity.graphId),\n eq(matTable.logicalName, identity.logicalName),\n eq(matTable.owner, identity.owner),\n eq(matTable.tableName, identity.tableName),\n ),\n );\n const row = rows[0];\n if (row === undefined) return undefined;\n return mapContributionMaterializationRow(\n row,\n POSTGRES_CONTRIBUTION_MAT_TIMESTAMPS.decode,\n );\n }\n\n async function recordContributionMaterializationRow(\n params: RecordContributionMaterializationParams,\n ): Promise<void> {\n await db\n .insert(matTable)\n .values(\n buildContributionInsertValues(\n params,\n POSTGRES_CONTRIBUTION_MAT_TIMESTAMPS.encode,\n ),\n )\n .onConflictDoUpdate({\n target: [\n matTable.graphId,\n matTable.logicalName,\n matTable.owner,\n matTable.tableName,\n ],\n set: buildContributionOnConflictSet(\n matTable.materializedAt,\n params.materializedAt,\n ),\n });\n }\n\n const contributionMaterializer = createContributionMaterializer({\n dialect: \"postgres\",\n fulltextStrategy,\n fulltextTableName: tables.fulltextTableName,\n execDdl: async (statement) => {\n await db.execute(sql.raw(statement));\n },\n ensureMarkerTable: ensureContributionMaterializationsTableImpl,\n getMarker: getContributionMaterializationRow,\n recordMarker: recordContributionMaterializationRow,\n });\n\n // Shared by `transaction()` (TypeGraph opens the tx) and\n // `adoptTransaction()` (#134 — the caller already opened it): bind a\n // tx-scoped backend to the *literal* `tx` client and gate fulltext on\n // the durable marker (a cached SELECT, never DDL).\n function bindTransactionBackend(tx: AnyPgDatabase): TransactionBackend {\n const txBackend = createTransactionBackend({\n db: tx,\n adapterOptions,\n operationStrategy,\n tableNames,\n capabilities,\n fulltextStrategy,\n vectorStrategy,\n vectorSlotLatch,\n });\n return gateFulltext(txBackend, contributionMaterializer.assertInitialized);\n }\n\n const backend: GraphBackend = {\n ...operations,\n\n async bootstrapTables(): Promise<void> {\n const statements = generatePostgresDDL(tables, fulltextStrategy);\n for (const statement of statements) {\n await db.execute(sql.raw(statement));\n }\n },\n\n // Every fulltext-touching method asserts the durable marker instead\n // of lazily emitting DDL. Steady state performs zero ensure; an\n // uninitialized database throws `StoreNotInitializedError` loudly\n // rather than self-healing on a read/write path (#135). Shared\n // verbatim with the tx-scoped gate via `gateFulltextMethods`.\n ...gateFulltextMethods(\n operations,\n contributionMaterializer.assertInitialized,\n ),\n\n async executeDdl(ddl: string): Promise<void> {\n await db.execute(sql.raw(ddl));\n },\n\n async ensureIndexMaterializationsTable(): Promise<void> {\n await db.execute(\n sql.raw(generatePgCreateTableSQL(tables.indexMaterializations)),\n );\n },\n\n async getIndexMaterialization(\n indexName: string,\n ): Promise<IndexMaterializationRow | undefined> {\n const t = tables.indexMaterializations;\n const rows = await db.select().from(t).where(eq(t.indexName, indexName));\n const row = rows[0];\n if (row === undefined) return undefined;\n return mapMaterializationRow(row, POSTGRES_INDEX_MAT_TIMESTAMPS.decode);\n },\n\n async getIndexMaterializations(\n statusKeys: readonly string[],\n ): Promise<readonly IndexMaterializationRow[]> {\n if (statusKeys.length === 0) return [];\n const t = tables.indexMaterializations;\n const rows = await db\n .select()\n .from(t)\n .where(inArray(t.indexName, [...statusKeys]));\n return rows.map((row) =>\n mapMaterializationRow(row, POSTGRES_INDEX_MAT_TIMESTAMPS.decode),\n );\n },\n\n async recordIndexMaterialization(\n params: RecordIndexMaterializationParams,\n ): Promise<void> {\n const t = tables.indexMaterializations;\n await db\n .insert(t)\n .values(\n buildMaterializationInsertValues(\n params,\n POSTGRES_INDEX_MAT_TIMESTAMPS.encode,\n ),\n )\n .onConflictDoUpdate({\n target: t.indexName,\n set: buildMaterializationOnConflictSet(\n t.materializedAt,\n params.materializedAt,\n ),\n });\n },\n\n async ensureContributionMaterializationsTable(): Promise<void> {\n await ensureContributionMaterializationsTableImpl();\n },\n\n async getContributionMaterialization(\n identity: ContributionMaterializationIdentity,\n ): Promise<ContributionMaterializationRow | undefined> {\n return getContributionMaterializationRow(identity);\n },\n\n async recordContributionMaterialization(\n params: RecordContributionMaterializationParams,\n ): Promise<void> {\n await recordContributionMaterializationRow(params);\n },\n\n async assertRuntimeContributionsInitialized(\n graphId: string,\n ): Promise<void> {\n await contributionMaterializer.assertInitialized(graphId);\n },\n\n async ensureKindRemovalsTable(): Promise<void> {\n await db.execute(sql.raw(generatePgCreateTableSQL(tables.kindRemovals)));\n },\n\n async getPendingKindRemovals(\n graphId: string,\n ): Promise<readonly KindRemovalRow[]> {\n const t = tables.kindRemovals;\n const rows = await db\n .select()\n .from(t)\n .where(and(eq(t.graphId, graphId), isNull(t.removedAt)));\n return rows.map((row) =>\n mapKindRemovalRow(row, POSTGRES_KIND_REMOVAL_TIMESTAMPS.decode),\n );\n },\n\n async getAllKindRemovals(\n graphId: string,\n ): Promise<readonly KindRemovalRow[]> {\n const t = tables.kindRemovals;\n const rows = await db.select().from(t).where(eq(t.graphId, graphId));\n return rows.map((row) =>\n mapKindRemovalRow(row, POSTGRES_KIND_REMOVAL_TIMESTAMPS.decode),\n );\n },\n\n async recordKindRemoval(params: RecordKindRemovalParams): Promise<void> {\n const t = tables.kindRemovals;\n await db\n .insert(t)\n .values(\n buildKindRemovalInsertValues(\n params,\n POSTGRES_KIND_REMOVAL_TIMESTAMPS.encode,\n ),\n )\n .onConflictDoUpdate({\n target: [t.graphId, t.kindName, t.entity, t.schemaVersion],\n set: buildKindRemovalOnConflictSet(t.removedAt, params.removedAt),\n });\n },\n\n async ensureReconciliationMarkersTable(): Promise<void> {\n await db.execute(\n sql.raw(generatePgCreateTableSQL(tables.reconciliationMarkers)),\n );\n },\n\n async ensureRuntimeContributions(graphId: string): Promise<void> {\n await contributionMaterializer.ensureRuntimeContributions(graphId);\n },\n\n /**\n * Superseded by `ensureRuntimeContributions(graphId)` (#129).\n * Retained as a thin back-compat wrapper for callers predating\n * #129; #135 routed it through the durable-marker writer.\n */\n async ensureFulltextTable(graphId: string): Promise<void> {\n await contributionMaterializer.ensureRuntimeContributions(graphId);\n },\n\n async getReconciliationMarker(\n graphId: string,\n ): Promise<number | undefined> {\n const t = tables.reconciliationMarkers;\n const rows = await db.select().from(t).where(eq(t.graphId, graphId));\n return rows[0]?.reconciledToVersion;\n },\n\n async setReconciliationMarker(\n graphId: string,\n version: number,\n ): Promise<void> {\n const t = tables.reconciliationMarkers;\n await db\n .insert(t)\n .values({ graphId, reconciledToVersion: version })\n .onConflictDoUpdate({\n target: t.graphId,\n set: { reconciledToVersion: version },\n });\n },\n\n async refreshStatistics(): Promise<void> {\n // Scoped to TypeGraph-managed tables only — we don't touch\n // unrelated tables in the same database. Without fresh stats\n // after a bulk load the planner can pick a reverse-index scan\n // with a filter (5ms forward traversal instead of 0.5ms) until\n // autovacuum catches up.\n await db.execute(analyzeStatement);\n },\n\n async commitSchemaVersion(\n params: CommitSchemaVersionParams,\n ): Promise<SchemaVersionRow> {\n return runSchemaWriteTransaction(params.graphId, (target) =>\n target.commitSchemaVersion(params),\n );\n },\n\n async setActiveVersion(params: SetActiveVersionParams): Promise<void> {\n await runSchemaWriteTransaction(params.graphId, (target) =>\n target.setActiveVersion(params),\n );\n },\n\n async transaction<T>(\n fn: (tx: TransactionBackend, sql: AdoptedTransaction) => Promise<T>,\n options?: TransactionOptions,\n ): Promise<T> {\n // #134/#135: NO DDL or ensure here. The tx-scoped backend's\n // fulltext-touching methods assert the durable contribution\n // marker (one cached SELECT — never DDL) at point of use, exactly\n // like the non-tx wrappers. A transaction that never touches\n // fulltext never asserts; one that does runs pure DML against an\n // already-materialized table, with the \"no DDL in the business\n // transaction\" guarantee backed by the durable fact.\n const txConfig =\n options?.isolationLevel ?\n {\n isolationLevel: options.isolationLevel.replace(\"_\", \" \") as\n | \"read uncommitted\"\n | \"read committed\"\n | \"repeatable read\"\n | \"serializable\",\n }\n : undefined;\n\n return db.transaction(\n async (tx) => fn(bindTransactionBackend(tx), tx),\n txConfig,\n );\n },\n\n adoptTransaction(externalTx: AdoptedTransaction): TransactionBackend {\n // #134: cross-store atomicity is unsafe without real rollback —\n // the caller's relational write on `externalTx` *would* still\n // commit even though the graph write could not be undone. Refuse\n // loudly rather than silently degrade.\n if (!capabilities.transactions) {\n throw new ConfigurationError(\n \"Cross-store atomicity is unavailable on this Postgres backend: \" +\n \"its driver does not support transactions (drizzle-orm/neon-http, \" +\n \"Cloudflare D1). Adopting an external transaction here would let \" +\n \"the caller's relational write commit with no way to roll back \" +\n \"the graph write. Use a node-postgres or neon-serverless \" +\n \"(Pool/WebSocket) connection for cross-store transactions.\",\n {\n backend: \"postgres\",\n capability: \"transactions\",\n supportsTransactions: false,\n },\n );\n }\n assertAdoptedDialect<AnyPgDatabase>(externalTx, PgDatabase, \"postgres\");\n // The caller owns BEGIN/COMMIT/ROLLBACK via its own\n // `db.transaction(...)`. We adopt the literal `tx` client and run\n // pure DML on it — no transaction is opened or closed here, and no\n // DDL is emitted inside the caller's business transaction.\n return bindTransactionBackend(externalTx);\n },\n\n async close(): Promise<void> {\n // Drizzle doesn't expose a close method\n // Users manage connection lifecycle themselves\n },\n };\n\n return backend;\n}\n\ntype CreatePostgresOperationBackendOptions = Readonly<{\n db: AnyPgDatabase;\n executionAdapter: PostgresExecutionAdapter;\n /**\n * Adapter tuning (prepared-statement cache settings). Used to bind a\n * fresh, equivalently-configured adapter to a transaction client when\n * a per-search `efSearch` override opens its own transaction.\n */\n adapterOptions?: PostgresExecutionAdapterOptions | undefined;\n operationStrategy: ReturnType<typeof createPostgresOperationStrategy>;\n tableNames: SqlTableNames;\n capabilities: BackendCapabilities;\n fulltextStrategy: FulltextStrategy;\n /**\n * Active vector strategy (`pgvectorStrategy` unless overridden), or\n * `undefined` when vector support is disabled (`vector: false`).\n */\n vectorStrategy: VectorStrategy | undefined;\n /**\n * Shared per-`(kind, field)` storage-ensure latch. Paired with\n * `vectorStrategy`: both present, or both `undefined`.\n */\n vectorSlotLatch: VectorSlotLatch | undefined;\n}>;\n\ntype CreatePostgresTransactionBackendOptions = Readonly<{\n db: AnyPgDatabase;\n executionAdapter?: PostgresExecutionAdapter;\n adapterOptions?: PostgresExecutionAdapterOptions;\n operationStrategy: ReturnType<typeof createPostgresOperationStrategy>;\n tableNames: SqlTableNames;\n capabilities: BackendCapabilities;\n fulltextStrategy: FulltextStrategy;\n /** Active vector strategy. See {@link CreatePostgresOperationBackendOptions}. */\n vectorStrategy: VectorStrategy | undefined;\n /** Shared storage-ensure latch. See {@link CreatePostgresOperationBackendOptions}. */\n vectorSlotLatch: VectorSlotLatch | undefined;\n}>;\n\nfunction createPostgresOperationBackend(\n options: CreatePostgresOperationBackendOptions,\n): InternalOperationBackend {\n const {\n db,\n executionAdapter,\n adapterOptions,\n operationStrategy,\n tableNames,\n capabilities,\n fulltextStrategy,\n vectorStrategy,\n vectorSlotLatch,\n } = options;\n\n // Route through the execution adapter so driver-specific result shapes\n // (`{rows}` for node-postgres / neon-serverless; bare array for\n // postgres-js) are normalized in one place.\n async function execAll<T>(query: SQL): Promise<readonly T[]> {\n return executionAdapter.execute<T>(query);\n }\n\n async function execGet<T>(query: SQL): Promise<T | undefined> {\n const rows = await executionAdapter.execute<T>(query);\n return rows[0];\n }\n\n async function execRun(query: SQL): Promise<void> {\n await executionAdapter.execute(query);\n }\n\n type VectorSearchRow = Readonly<{ node_id: string; score: number }>;\n\n // One warning per backend instance when `efSearch` is supplied but the\n // driver can't hold a transaction to scope `SET LOCAL` to.\n let efSearchUnsupportedWarned = false;\n function warnEfSearchUnsupported(): void {\n if (efSearchUnsupportedWarned) return;\n efSearchUnsupportedWarned = true;\n if (typeof console === \"undefined\" || typeof console.warn !== \"function\") {\n return;\n }\n console.warn(\n \"[typegraph] efSearch (hnsw.ef_search override) was ignored: this \" +\n \"Postgres backend has transactions disabled (e.g. drizzle-orm/neon-http), \" +\n \"and SET LOCAL needs a transaction to scope the override. Use a \" +\n \"transactional driver (node-postgres / neon-serverless / postgres-js) \" +\n \"to apply efSearch.\",\n );\n }\n\n /**\n * Runs the vector SELECT, applying `hnsw.ef_search` transaction-locally\n * when requested. `SET LOCAL` only takes effect inside an explicit\n * transaction — issued in autocommit it rolls off with the statement\n * and the next pooled query (notably under transaction-mode pgbouncer)\n * sees the session default again. So when `efSearch` is set we bundle\n * `BEGIN; SET LOCAL …; SELECT …; COMMIT;` onto one connection. When\n * absent we take the unchanged single-statement fast path and open no\n * transaction.\n */\n async function runVectorSearch(\n efSearch: number | undefined,\n query: SQL,\n ): Promise<readonly VectorSearchRow[]> {\n if (efSearch === undefined) {\n return execAll<VectorSearchRow>(query);\n }\n if (!capabilities.transactions) {\n warnEfSearchUnsupported();\n return execAll<VectorSearchRow>(query);\n }\n // `SET` cannot be parameterized; `efSearch` is validated as an\n // integer in 1..MAX_HNSW_EF_SEARCH before we get here, so inlining\n // it is injection-safe.\n const setEfSearch = sql.raw(`SET LOCAL hnsw.ef_search = ${efSearch}`);\n if (db instanceof PgTransaction) {\n // Already inside the caller's transaction (low-level\n // backend.transaction / adoptTransaction). `executionAdapter` is\n // bound to this tx client, but SET LOCAL persists to the end of the\n // caller's transaction — leaking the override into their later\n // vector searches and breaking the per-search contract. Snapshot\n // the current frontier, apply the override, then restore it once\n // the SELECT has materialized so the override stays scoped to this\n // one search.\n const [setting] = await execAll<{ ef_search: string }>(\n sql`SELECT current_setting('hnsw.ef_search') AS ef_search`,\n );\n await execRun(setEfSearch);\n const rows = await execAll<VectorSearchRow>(query);\n // Restore only on success: a failed SELECT aborts the caller's\n // transaction, so its rollback discards the SET LOCAL anyway and a\n // restore here would just fail against the aborted tx, masking the\n // real error. `set_config(_, _, true)` is the parameterizable form\n // of SET LOCAL.\n if (setting !== undefined) {\n await execRun(\n sql`SELECT set_config('hnsw.ef_search', ${setting.ef_search}, true)`,\n );\n }\n return rows;\n }\n return db.transaction(async (tx) => {\n // Bind an equivalently-configured adapter to the tx client so the\n // SELECT keeps the server-side prepared-statement fast path and the\n // driver result-shape normalization, rather than a bespoke execute.\n const txAdapter = createPostgresExecutionAdapter(tx, adapterOptions);\n await txAdapter.execute(setEfSearch);\n return txAdapter.execute<VectorSearchRow>(query);\n });\n }\n\n // Runs the strategy's per-field DDL on this backend's connection (the tx\n // client for a transaction-scoped backend) so a slot's table exists\n // before the first write/search hits it.\n async function ensureVectorSlotStorage(slot: VectorSlot): Promise<void> {\n if (vectorStrategy === undefined || vectorSlotLatch === undefined) return;\n await vectorSlotLatch.ensure(vectorStrategy, slot, async (statement) => {\n await execRun(sql.raw(statement));\n });\n }\n\n const commonBackend = createCommonOperationBackend({\n batchConfig: {\n checkUniqueBatchChunkSize: POSTGRES_CHECK_UNIQUE_BATCH_CHUNK_SIZE,\n edgeInsertBatchSize: POSTGRES_EDGE_INSERT_BATCH_SIZE,\n getEdgesChunkSize: POSTGRES_GET_EDGES_ID_CHUNK_SIZE,\n getNodesChunkSize: POSTGRES_GET_NODES_ID_CHUNK_SIZE,\n nodeInsertBatchSize: POSTGRES_NODE_INSERT_BATCH_SIZE,\n },\n execution: {\n execAll,\n execGet,\n execRun,\n },\n nowIso,\n operationStrategy,\n rowMappers: {\n toEdgeRow,\n toNodeRow,\n toSchemaVersionRow,\n toUniqueRow,\n },\n });\n\n const executeCompiled = executionAdapter.executeCompiled;\n const executeRawMethod: Pick<TransactionBackend, \"executeRaw\"> =\n executeCompiled === undefined ?\n {}\n : {\n async executeRaw<T>(\n sqlText: string,\n params: readonly unknown[],\n ): Promise<readonly T[]> {\n return executeCompiled<T>({ params, sql: sqlText });\n },\n };\n\n // Embedding write/search methods are present only when a vector strategy\n // is wired. With `vector: false` (e.g. PGlite without pgvector) they are\n // omitted, so `capabilities.vector` is absent and the store never routes\n // embedding work here — mirroring a SQLite connection without sqlite-vec.\n const vectorEmbeddingMethods =\n vectorStrategy === undefined ?\n {}\n : {\n async upsertEmbedding(params: UpsertEmbeddingParams): Promise<void> {\n const slot = vectorSlotFromParams(params);\n await ensureVectorSlotStorage(slot);\n const statements = vectorStrategy.buildUpsert(slot, params, nowIso());\n try {\n for (const statement of statements) {\n await execRun(statement);\n }\n } catch (error) {\n throw mapVectorWriteError(error, params);\n }\n },\n\n async deleteEmbedding(params: DeleteEmbeddingParams): Promise<void> {\n // Ensure the per-field table exists before deleting. A delete can\n // run before any embedding was ever written for the field (e.g. a\n // node hard-deleted having never carried one), and on Postgres a\n // DELETE against a missing relation INSIDE a transaction aborts the\n // whole transaction — swallowing the JS error can't un-abort it.\n // The idempotent ensure makes the DELETE target an existing\n // (possibly empty) table, so it's always a clean no-op.\n const slot = vectorSlotFromParams(params);\n await ensureVectorSlotStorage(slot);\n const statements = vectorStrategy.buildDelete(slot, params);\n for (const statement of statements) {\n await execRun(statement);\n }\n },\n\n async vectorSearch(\n params: VectorSearchParams,\n ): Promise<readonly VectorSearchResult[]> {\n assertVectorSearchLimit(params.limit);\n // Validate `efSearch` against pgvector's `hnsw.ef_search` ceiling\n // before `runVectorSearch` inlines it into `SET LOCAL`.\n assertPgvectorEfSearch(params.efSearch);\n const slot = vectorSlotFromParams(params);\n await ensureVectorSlotStorage(slot);\n const query = vectorStrategy.buildSearch(slot, params);\n let rows: readonly { node_id: string; score: number }[];\n try {\n rows = await runVectorSearch(params.efSearch, query);\n } catch (error) {\n // A query vector whose dimension no longer matches the stored\n // column surfaces the same typed error as the write path.\n throw mapVectorWriteError(error, params);\n }\n return rows.map((row) => ({\n nodeId: row.node_id,\n score: row.score,\n }));\n },\n };\n\n const operationBackend: InternalOperationBackend = {\n ...commonBackend,\n ...executeRawMethod,\n ...vectorEmbeddingMethods,\n capabilities,\n fulltextStrategy,\n ...(vectorStrategy === undefined ? {} : { vectorStrategy }),\n dialect: \"postgres\",\n tableNames,\n\n // === Vector Index Operations ===\n\n async createVectorIndex(params: CreateVectorIndexParams): Promise<void> {\n if (vectorStrategy === undefined) return;\n const slot = vectorSlotFromCreateIndexParams(params);\n // Ensure the per-field table exists first (idempotent), then create its\n // ANN index. pgvector's `ownedTables` builds the table only — the HNSW/\n // IVFFlat index is created here (and only here) so it picks up the\n // declared `m`/`ef_construction`/`lists` from `slot.indexParams` rather\n // than defaults.\n await ensureVectorSlotStorage(slot);\n // Honor the `concurrent` flag materializeIndexes passes on Postgres so the\n // ANN build doesn't take a write-blocking lock on a live table. execRun is\n // autocommit, which CONCURRENTLY requires.\n const indexStatement = vectorStrategy.buildCreateIndex?.(slot, {\n concurrent: params.concurrent === true,\n });\n if (indexStatement !== undefined) {\n await execRun(indexStatement);\n }\n },\n\n // === Fulltext Operations ===\n\n async upsertFulltext(params: UpsertFulltextParams): Promise<void> {\n const timestamp = nowIso();\n const statements = operationStrategy.buildUpsertFulltext(\n params,\n timestamp,\n );\n for (const stmt of statements) {\n await execRun(stmt);\n }\n },\n\n async deleteFulltext(params: DeleteFulltextParams): Promise<void> {\n const statements = operationStrategy.buildDeleteFulltext(params);\n for (const stmt of statements) {\n await execRun(stmt);\n }\n },\n\n async upsertFulltextBatch(\n params: UpsertFulltextBatchParams,\n ): Promise<void> {\n if (params.rows.length === 0) return;\n const timestamp = nowIso();\n const statements = operationStrategy.buildUpsertFulltextBatch(\n params,\n timestamp,\n );\n for (const stmt of statements) {\n await execRun(stmt);\n }\n },\n\n async deleteFulltextBatch(\n params: DeleteFulltextBatchParams,\n ): Promise<void> {\n if (params.nodeIds.length === 0) return;\n const statements = operationStrategy.buildDeleteFulltextBatch(params);\n for (const stmt of statements) {\n await execRun(stmt);\n }\n },\n\n async fulltextSearch(\n params: FulltextSearchParams,\n ): Promise<readonly FulltextSearchResult[]> {\n const query = operationStrategy.buildFulltextSearch(params);\n // pg returns `numeric` as a string to preserve precision; coerce at the\n // backend boundary so FulltextSearchResult.score is always `number`.\n const rows = await execAll<{\n node_id: string;\n score: number | string;\n snippet: string | null;\n }>(query);\n return rows.map((row, index) => ({\n nodeId: row.node_id,\n score: coerceNumericScore(row.score),\n rank: index + 1,\n ...(row.snippet === null ? {} : { snippet: row.snippet }),\n }));\n },\n\n async dropVectorIndex(params: DropVectorIndexParams): Promise<void> {\n if (vectorStrategy === undefined) return;\n const slot = vectorSlotFromDropIndexParams(params);\n const dropStatement = vectorStrategy.buildDropIndex?.(slot);\n if (dropStatement === undefined) return;\n try {\n await execRun(dropStatement);\n } catch (error) {\n // The per-field table (and thus its index) may never have been\n // materialized; treat a missing relation as already-dropped.\n if (!isMissingTableError(error)) throw error;\n }\n },\n\n // === Query Execution ===\n\n async execute<T>(query: SQL): Promise<readonly T[]> {\n return executionAdapter.execute<T>(query);\n },\n\n compileSql(\n query: SQL,\n ): Readonly<{ sql: string; params: readonly unknown[] }> {\n return executionAdapter.compile(query);\n },\n };\n\n return operationBackend;\n}\n\nfunction createTransactionBackend(\n options: CreatePostgresTransactionBackendOptions,\n): InternalOperationBackend {\n const txExecutionAdapter =\n options.executionAdapter ??\n createPostgresExecutionAdapter(options.db, options.adapterOptions);\n\n // A transaction-scoped backend gets its OWN per-field ensure-latch, never\n // the outer process-global one: a `CREATE TABLE/INDEX` that runs inside the\n // caller's transaction and then rolls back must not leave the shared latch\n // marking the slot \"ensured\" (which would skip the re-CREATE and make every\n // later write fail with \"relation does not exist\"). The fresh latch is\n // discarded with the transaction, so the next write re-ensures idempotently.\n // No latch when vector support is disabled (`vector: false`).\n return createPostgresOperationBackend({\n db: options.db,\n executionAdapter: txExecutionAdapter,\n adapterOptions: options.adapterOptions,\n operationStrategy: options.operationStrategy,\n tableNames: options.tableNames,\n capabilities: options.capabilities,\n fulltextStrategy: options.fulltextStrategy,\n vectorStrategy: options.vectorStrategy,\n vectorSlotLatch:\n options.vectorStrategy === undefined ? undefined : createVectorSlotLatch(),\n });\n}\n\n// Re-export schema utilities\nexport type { PostgresTableNames, PostgresTables } from \"./schema/postgres\";\nexport { createPostgresTables, tables } from \"./schema/postgres\";\n"]}
|
package/dist/chunk-RP7YITHX.cjs
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// src/utils/sql-errors.ts
|
|
4
|
-
var MISSING_TABLE_PATTERNS = [
|
|
5
|
-
"no such table",
|
|
6
|
-
// SQLite
|
|
7
|
-
"does not exist",
|
|
8
|
-
// PostgreSQL ("relation ... does not exist")
|
|
9
|
-
"SQLITE_ERROR"
|
|
10
|
-
// D1 / Durable Objects error code
|
|
11
|
-
];
|
|
12
|
-
var POSTGRES_UNDEFINED_TABLE_CODE = "42P01";
|
|
13
|
-
function* errorChain(error) {
|
|
14
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15
|
-
let current = error;
|
|
16
|
-
while (current !== void 0 && current !== null && !seen.has(current)) {
|
|
17
|
-
seen.add(current);
|
|
18
|
-
yield current;
|
|
19
|
-
current = current instanceof Error ? current.cause : void 0;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
function isPostgresUndefinedTable(link) {
|
|
23
|
-
return typeof link === "object" && link !== null && "code" in link && link.code === POSTGRES_UNDEFINED_TABLE_CODE;
|
|
24
|
-
}
|
|
25
|
-
function isMissingTableError(error) {
|
|
26
|
-
for (const link of errorChain(error)) {
|
|
27
|
-
if (isPostgresUndefinedTable(link)) return true;
|
|
28
|
-
const message = link instanceof Error ? link.message : String(link);
|
|
29
|
-
if (MISSING_TABLE_PATTERNS.some((pattern) => message.includes(pattern))) {
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
var DIMENSION_MISMATCH_PATTERN = /expected (\d+) dimensions(?:[,\s]+(?:not|got|but got|but)\s*(\d+))?/i;
|
|
36
|
-
function parseDimensionMismatch(error) {
|
|
37
|
-
for (const link of errorChain(error)) {
|
|
38
|
-
const message = link instanceof Error ? link.message : String(link);
|
|
39
|
-
const match = DIMENSION_MISMATCH_PATTERN.exec(message);
|
|
40
|
-
if (match) {
|
|
41
|
-
return {
|
|
42
|
-
expected: Number(match[1]),
|
|
43
|
-
actual: match[2] === void 0 ? void 0 : Number(match[2])
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return void 0;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// src/schema/canonical.ts
|
|
51
|
-
function sortedReplacer(_key, value) {
|
|
52
|
-
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
53
|
-
const sorted = {};
|
|
54
|
-
for (const key of Object.keys(value).toSorted()) {
|
|
55
|
-
sorted[key] = value[key];
|
|
56
|
-
}
|
|
57
|
-
return sorted;
|
|
58
|
-
}
|
|
59
|
-
return value;
|
|
60
|
-
}
|
|
61
|
-
function canonicalEqual(a, b) {
|
|
62
|
-
return JSON.stringify(a, sortedReplacer) === JSON.stringify(b, sortedReplacer);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
exports.canonicalEqual = canonicalEqual;
|
|
66
|
-
exports.isMissingTableError = isMissingTableError;
|
|
67
|
-
exports.parseDimensionMismatch = parseDimensionMismatch;
|
|
68
|
-
exports.sortedReplacer = sortedReplacer;
|
|
69
|
-
//# sourceMappingURL=chunk-RP7YITHX.cjs.map
|
|
70
|
-
//# sourceMappingURL=chunk-RP7YITHX.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/sql-errors.ts","../src/schema/canonical.ts"],"names":[],"mappings":";;;AAqBA,IAAM,sBAAA,GAAyB;AAAA,EAC7B,eAAA;AAAA;AAAA,EACA,gBAAA;AAAA;AAAA,EACA;AAAA;AACF,CAAA;AASA,IAAM,6BAAA,GAAgC,OAAA;AAOtC,UAAU,WAAW,KAAA,EAAgD;AACnE,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAa;AAC9B,EAAA,IAAI,OAAA,GAAmB,KAAA;AACvB,EAAA,OAAO,OAAA,KAAY,UAAa,OAAA,KAAY,IAAA,IAAQ,CAAC,IAAA,CAAK,GAAA,CAAI,OAAO,CAAA,EAAG;AACtE,IAAA,IAAA,CAAK,IAAI,OAAO,CAAA;AAChB,IAAA,MAAM,OAAA;AACN,IAAA,OAAA,GAAU,OAAA,YAAmB,KAAA,GAAQ,OAAA,CAAQ,KAAA,GAAQ,MAAA;AAAA,EACvD;AACF;AAMA,SAAS,yBAAyB,IAAA,EAAwB;AACxD,EAAA,OACE,OAAO,SAAS,QAAA,IAChB,IAAA,KAAS,QACT,MAAA,IAAU,IAAA,IACT,KAAsC,IAAA,KACrC,6BAAA;AAEN;AAEO,SAAS,oBAAoB,KAAA,EAAyB;AAC3D,EAAA,KAAA,MAAW,IAAA,IAAQ,UAAA,CAAW,KAAK,CAAA,EAAG;AACpC,IAAA,IAAI,wBAAA,CAAyB,IAAI,CAAA,EAAG,OAAO,IAAA;AAC3C,IAAA,MAAM,UAAU,IAAA,YAAgB,KAAA,GAAQ,IAAA,CAAK,OAAA,GAAU,OAAO,IAAI,CAAA;AAClE,IAAA,IAAI,sBAAA,CAAuB,KAAK,CAAC,OAAA,KAAY,QAAQ,QAAA,CAAS,OAAO,CAAC,CAAA,EAAG;AACvE,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAQA,IAAM,0BAAA,GACJ,sEAAA;AAQK,SAAS,uBACd,KAAA,EAC8D;AAC9D,EAAA,KAAA,MAAW,IAAA,IAAQ,UAAA,CAAW,KAAK,CAAA,EAAG;AACpC,IAAA,MAAM,UAAU,IAAA,YAAgB,KAAA,GAAQ,IAAA,CAAK,OAAA,GAAU,OAAO,IAAI,CAAA;AAClE,IAAA,MAAM,KAAA,GAAQ,0BAAA,CAA2B,IAAA,CAAK,OAAO,CAAA;AACrD,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,OAAO;AAAA,QACL,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA;AAAA,QACzB,MAAA,EAAQ,MAAM,CAAC,CAAA,KAAM,SAAY,MAAA,GAAY,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC;AAAA,OAC9D;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;;;ACxFO,SAAS,cAAA,CAAe,MAAc,KAAA,EAAyB;AACpE,EAAA,IAAI,KAAA,KAAU,QAAQ,OAAO,KAAA,KAAU,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxE,IAAA,MAAM,SAAkC,EAAC;AACzC,IAAA,KAAA,MAAW,OAAO,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA,CAAE,UAAS,EAAG;AAC/C,MAAA,MAAA,CAAO,GAAG,CAAA,GAAK,KAAA,CAAkC,GAAG,CAAA;AAAA,IACtD;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA;AACT;AAaO,SAAS,cAAA,CAAe,GAAY,CAAA,EAAqB;AAC9D,EAAA,OACE,IAAA,CAAK,UAAU,CAAA,EAAG,cAAc,MAAM,IAAA,CAAK,SAAA,CAAU,GAAG,cAAc,CAAA;AAE1E","file":"chunk-RP7YITHX.cjs","sourcesContent":["/**\n * Cross-dialect detection of \"this relation does not exist yet\" errors.\n *\n * Shared so the schema bootstrap (`loadActiveSchemaWithBootstrap`) and\n * the durable contribution-materialization gate (#135) agree on what a\n * missing-table failure looks like, and — critically — so neither one\n * swallows a genuine system fault (connection/permission/driver error)\n * as a benign \"not bootstrapped yet\".\n *\n * Detection walks the error's `cause` chain because Drizzle (>= the\n * `DrizzleQueryError` era, drizzle-orm ≥ 0.36) wraps every failure from a\n * query-builder call (`db.select()`, `db.insert()`, …): the wrapper's\n * `.message` becomes the failed query text and the real driver error —\n * which carries both the missing-relation text and the SQLSTATE — is\n * preserved on `.cause`. node-postgres / postgres-js nest the pg error\n * one link deep; better-sqlite3 throws it unwrapped; raw `client.query()`\n * fast paths surface it directly. Walking the chain makes the check\n * wrapper- and driver-agnostic instead of only matching the outermost\n * `.message`, which on Postgres is just the SQL string.\n */\n\nconst MISSING_TABLE_PATTERNS = [\n \"no such table\", // SQLite\n \"does not exist\", // PostgreSQL (\"relation ... does not exist\")\n \"SQLITE_ERROR\", // D1 / Durable Objects error code\n] as const;\n\n/**\n * SQLSTATE for PostgreSQL `undefined_table`. Preferred over the\n * human-readable message: it is locale-independent (the \"... does not\n * exist\" text is translated under a non-English `lc_messages`) and is\n * preserved on the underlying driver error even when Drizzle overwrites\n * `.message` with the query text.\n */\nconst POSTGRES_UNDEFINED_TABLE_CODE = \"42P01\";\n\n/**\n * Yields an error and each error reachable by following `.cause`,\n * outermost first. `seen` guards the pathological cyclic-cause case so a\n * self-referential chain can't spin forever.\n */\nfunction* errorChain(error: unknown): Generator<unknown, void, void> {\n const seen = new Set<unknown>();\n let current: unknown = error;\n while (current !== undefined && current !== null && !seen.has(current)) {\n seen.add(current);\n yield current;\n current = current instanceof Error ? current.cause : undefined;\n }\n}\n\n/**\n * Whether a single chain link is a PostgreSQL `undefined_table` failure,\n * identified by its SQLSTATE rather than a message substring.\n */\nfunction isPostgresUndefinedTable(link: unknown): boolean {\n return (\n typeof link === \"object\" &&\n link !== null &&\n \"code\" in link &&\n (link as Readonly<{ code?: unknown }>).code ===\n POSTGRES_UNDEFINED_TABLE_CODE\n );\n}\n\nexport function isMissingTableError(error: unknown): boolean {\n for (const link of errorChain(error)) {\n if (isPostgresUndefinedTable(link)) return true;\n const message = link instanceof Error ? link.message : String(link);\n if (MISSING_TABLE_PATTERNS.some((pattern) => message.includes(pattern))) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Engine \"vector dimension mismatch\" message shapes. pgvector:\n * `expected 384 dimensions, not 512`; libSQL / sqlite-vec surface a similar\n * `expected N … got/not M`. The first capture is the dimension the *stored*\n * column expects; the optional second is the dimension that was *attempted*.\n */\nconst DIMENSION_MISMATCH_PATTERN =\n /expected (\\d+) dimensions(?:[,\\s]+(?:not|got|but got|but)\\s*(\\d+))?/i;\n\n/**\n * Parses an engine vector-dimension-mismatch error into `{ expected, actual }`\n * by walking the `.cause` chain (drivers wrap the real error). `expected` is\n * the stored column's dimension; `actual` (when the message includes it) is the\n * attempted vector's dimension. Returns `undefined` for unrelated errors.\n */\nexport function parseDimensionMismatch(\n error: unknown,\n): { expected: number; actual: number | undefined } | undefined {\n for (const link of errorChain(error)) {\n const message = link instanceof Error ? link.message : String(link);\n const match = DIMENSION_MISMATCH_PATTERN.exec(message);\n if (match) {\n return {\n expected: Number(match[1]),\n actual: match[2] === undefined ? undefined : Number(match[2]),\n };\n }\n }\n return undefined;\n}\n","/**\n * Canonical-form helpers for deterministic schema serialization.\n *\n * Used by both content-hashing (`computeSchemaHash`) and structural diffing\n * (`computeSchemaDiff`) to ensure that semantically-equivalent objects with\n * differently-ordered keys produce identical canonical strings.\n */\n\n/**\n * `JSON.stringify` replacer that sorts object keys recursively.\n *\n * Apply via `JSON.stringify(value, sortedReplacer)` to obtain output in\n * which sibling keys at every depth appear in lexicographic order.\n *\n * Arrays are passed through unchanged — array order is semantically\n * meaningful and must not be normalized.\n */\nexport function sortedReplacer(_key: string, value: unknown): unknown {\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(value).toSorted()) {\n sorted[key] = (value as Record<string, unknown>)[key];\n }\n return sorted;\n }\n return value;\n}\n\n/**\n * Compare two values for canonical-form equality.\n *\n * Returns `true` when both values produce identical JSON under `sortedReplacer`\n * — i.e., they have the same JSON structure regardless of object key order.\n * Used by diff machinery to detect semantic changes in JSON-shaped fields.\n *\n * Note: callers must handle `undefined` themselves — `JSON.stringify(undefined)`\n * returns `undefined`, so two `undefined` inputs would compare equal here, which\n * may or may not be the desired semantics depending on context.\n */\nexport function canonicalEqual(a: unknown, b: unknown): boolean {\n return (\n JSON.stringify(a, sortedReplacer) === JSON.stringify(b, sortedReplacer)\n );\n}\n"]}
|
package/dist/chunk-SAJSMR2B.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// src/backend/types.ts
|
|
2
|
-
function wrapWithManagedClose(backend, teardown) {
|
|
3
|
-
let isClosed = false;
|
|
4
|
-
return {
|
|
5
|
-
...backend,
|
|
6
|
-
async close() {
|
|
7
|
-
if (isClosed) return;
|
|
8
|
-
isClosed = true;
|
|
9
|
-
await backend.close();
|
|
10
|
-
await teardown();
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
async function runOptionallyInTransaction(backend, fn, fallback) {
|
|
15
|
-
if (!backend.capabilities.transactions) {
|
|
16
|
-
return fn(fallback ?? backend);
|
|
17
|
-
}
|
|
18
|
-
return backend.transaction((tx) => fn(tx));
|
|
19
|
-
}
|
|
20
|
-
var SQLITE_CAPABILITIES = {
|
|
21
|
-
transactions: true,
|
|
22
|
-
// SQLite supports transactions
|
|
23
|
-
windowFunctions: true
|
|
24
|
-
// SQLite has supported window functions since 3.25.0
|
|
25
|
-
};
|
|
26
|
-
var POSTGRES_CAPABILITIES = {
|
|
27
|
-
transactions: true,
|
|
28
|
-
// PostgreSQL supports transactions
|
|
29
|
-
windowFunctions: true
|
|
30
|
-
// PostgreSQL supports ROW_NUMBER() and related windows
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export { POSTGRES_CAPABILITIES, SQLITE_CAPABILITIES, runOptionallyInTransaction, wrapWithManagedClose };
|
|
34
|
-
//# sourceMappingURL=chunk-SAJSMR2B.js.map
|
|
35
|
-
//# sourceMappingURL=chunk-SAJSMR2B.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/backend/types.ts"],"names":[],"mappings":";AAkrCO,SAAS,oBAAA,CACd,SACA,QAAA,EACc;AACd,EAAA,IAAI,QAAA,GAAW,KAAA;AACf,EAAA,OAAO;AAAA,IACL,GAAG,OAAA;AAAA,IACH,MAAM,KAAA,GAAuB;AAC3B,MAAA,IAAI,QAAA,EAAU;AACd,MAAA,QAAA,GAAW,IAAA;AACX,MAAA,MAAM,QAAQ,KAAA,EAAM;AACpB,MAAA,MAAM,QAAA,EAAS;AAAA,IACjB;AAAA,GACF;AACF;AAeA,eAAsB,0BAAA,CACpB,OAAA,EACA,EAAA,EACA,QAAA,EACY;AACZ,EAAA,IAAI,CAAC,OAAA,CAAQ,YAAA,CAAa,YAAA,EAAc;AACtC,IAAA,OAAO,EAAA,CAAG,YAAY,OAAO,CAAA;AAAA,EAC/B;AACA,EAAA,OAAO,QAAQ,WAAA,CAAY,CAAC,EAAA,KAAO,EAAA,CAAG,EAAE,CAAC,CAAA;AAC3C;AA0PO,IAAM,mBAAA,GAA2C;AAAA,EACtD,YAAA,EAAc,IAAA;AAAA;AAAA,EACd,eAAA,EAAiB;AAAA;AACnB;AAKO,IAAM,qBAAA,GAA6C;AAAA,EACxD,YAAA,EAAc,IAAA;AAAA;AAAA,EACd,eAAA,EAAiB;AAAA;AACnB","file":"chunk-SAJSMR2B.js","sourcesContent":["/**\n * Backend interface types for TypeGraph storage.\n *\n * The backend abstracts database operations, allowing different\n * SQL implementations (SQLite, PostgreSQL) behind a common interface.\n */\nimport { type SQL } from \"drizzle-orm\";\n\nimport {\n type IndexEntity,\n type KindEntity,\n type TemporalMode,\n} from \"../core/types\";\nimport { type SqlTableNames } from \"../query/compiler/schema\";\nimport { type FulltextStrategy } from \"../query/dialect/fulltext-strategy\";\nimport { type VectorStrategy } from \"../query/dialect/vector-strategy\";\nimport { type SerializedSchema } from \"../schema/types\";\nimport {\n type AnyPgDatabase,\n type AnySqliteDatabase,\n} from \"./drizzle/execution\";\n\n// ============================================================\n// Vector Search Types\n// ============================================================\n\n/**\n * Supported vector similarity metrics.\n */\nexport type VectorMetric = \"cosine\" | \"l2\" | \"inner_product\";\n\n/**\n * Supported vector index types.\n */\nexport type VectorIndexType = \"hnsw\" | \"ivfflat\" | \"none\";\n\n/**\n * Vector search capabilities.\n */\nexport type VectorCapabilities = Readonly<{\n /** Whether the backend supports vector operations */\n supported: boolean;\n /** Supported similarity metrics */\n metrics: readonly VectorMetric[];\n /** Supported index types */\n indexTypes: readonly VectorIndexType[];\n /** Maximum dimensions supported */\n maxDimensions: number;\n}>;\n\n// ============================================================\n// Fulltext Search Types\n// ============================================================\n\n/**\n * Query modes for fulltext search.\n *\n * - \"websearch\": Google-style syntax (quoted phrases, +required, -excluded).\n * Postgres: `websearch_to_tsquery`. SQLite: translated to FTS5 MATCH.\n * - \"phrase\": treats the whole query as a phrase.\n * Postgres: `phraseto_tsquery`. SQLite: FTS5 `\"...\"` phrase.\n * - \"plain\": splits on whitespace and ANDs terms.\n * Postgres: `plainto_tsquery`. SQLite: default FTS5 AND.\n * - \"raw\": dialect-native syntax passed through unchanged.\n */\nexport type FulltextQueryMode = \"websearch\" | \"phrase\" | \"plain\" | \"raw\";\n\n/**\n * Fulltext search capabilities declared by a backend.\n */\nexport type FulltextCapabilities = Readonly<{\n /** Whether the backend supports fulltext operations */\n supported: boolean;\n /**\n * Language / tokenizer names understood by this backend.\n * Postgres: installed regconfigs (english, simple, ...).\n * SQLite FTS5: tokenizer names (unicode61, porter, trigram).\n */\n languages: readonly string[];\n /** Whether phrase queries are supported. */\n phraseQueries: boolean;\n /** Whether prefix (`foo*`) queries are supported. */\n prefixQueries: boolean;\n /** Whether highlighting / snippets are supported. */\n highlighting: boolean;\n}>;\n\n// ============================================================\n// SQL Dialect & Capabilities\n// ============================================================\n\nimport { type SqlDialect } from \"../query/dialect/types\";\n\nexport type { SqlDialect } from \"../query/dialect/types\";\n\n/**\n * Backend capabilities that vary by dialect.\n */\nexport type BackendCapabilities = Readonly<{\n /** Whether the backend supports atomic transactions (D1 does not) */\n transactions: boolean;\n /** Whether the backend supports SQL window functions such as ROW_NUMBER() */\n windowFunctions: boolean;\n /** Vector search capabilities (undefined if not configured) */\n vector?: VectorCapabilities | undefined;\n /** Fulltext search capabilities (undefined if not configured) */\n fulltext?: FulltextCapabilities | undefined;\n}>;\n\n// ============================================================\n// Row Types (Database Records)\n// ============================================================\n\n/**\n * A row from the typegraph_nodes table.\n */\nexport type NodeRow = Readonly<{\n graph_id: string;\n kind: string;\n id: string;\n props: string; // JSON string\n version: number;\n valid_from: string | undefined;\n valid_to: string | undefined;\n created_at: string;\n updated_at: string;\n deleted_at: string | undefined;\n}>;\n\n/**\n * A row from the typegraph_edges table.\n */\nexport type EdgeRow = Readonly<{\n graph_id: string;\n id: string;\n kind: string;\n from_kind: string;\n from_id: string;\n to_kind: string;\n to_id: string;\n props: string; // JSON string\n valid_from: string | undefined;\n valid_to: string | undefined;\n created_at: string;\n updated_at: string;\n deleted_at: string | undefined;\n}>;\n\n/**\n * A row from the typegraph_node_uniques table.\n */\nexport type UniqueRow = Readonly<{\n graph_id: string;\n node_kind: string;\n constraint_name: string;\n key: string;\n node_id: string;\n concrete_kind: string;\n deleted_at: string | undefined;\n}>;\n\n/**\n * A row from the typegraph_schema_versions table.\n */\nexport type SchemaVersionRow = Readonly<{\n graph_id: string;\n version: number;\n schema_hash: string;\n schema_doc: string; // JSON string\n created_at: string;\n is_active: boolean;\n}>;\n\n// ============================================================\n// Insert Parameters\n// ============================================================\n\n/**\n * Parameters for inserting a node.\n */\nexport type InsertNodeParams = Readonly<{\n graphId: string;\n kind: string;\n id: string;\n props: Readonly<Record<string, unknown>>;\n validFrom?: string;\n validTo?: string;\n}>;\n\n/**\n * Parameters for updating a node.\n */\nexport type UpdateNodeParams = Readonly<{\n graphId: string;\n kind: string;\n id: string;\n props: Readonly<Record<string, unknown>>;\n validTo?: string;\n incrementVersion?: boolean;\n /** If true, clears deleted_at (un-deletes the node). Used by upsert. */\n clearDeleted?: boolean;\n}>;\n\n/**\n * Parameters for deleting a node (soft delete).\n */\nexport type DeleteNodeParams = Readonly<{\n graphId: string;\n kind: string;\n id: string;\n}>;\n\n/**\n * Parameters for inserting an edge.\n */\nexport type InsertEdgeParams = Readonly<{\n graphId: string;\n id: string;\n kind: string;\n fromKind: string;\n fromId: string;\n toKind: string;\n toId: string;\n props: Readonly<Record<string, unknown>>;\n validFrom?: string;\n validTo?: string;\n}>;\n\n/**\n * Parameters for updating an edge.\n */\nexport type UpdateEdgeParams = Readonly<{\n graphId: string;\n id: string;\n props: Readonly<Record<string, unknown>>;\n validTo?: string;\n clearDeleted?: boolean;\n}>;\n\n/**\n * Parameters for deleting an edge (soft delete).\n */\nexport type DeleteEdgeParams = Readonly<{\n graphId: string;\n id: string;\n}>;\n\n/**\n * Parameters for hard deleting a node (permanent removal).\n */\nexport type HardDeleteNodeParams = Readonly<{\n graphId: string;\n kind: string;\n id: string;\n}>;\n\n/**\n * Parameters for hard deleting an edge (permanent removal).\n */\nexport type HardDeleteEdgeParams = Readonly<{\n graphId: string;\n id: string;\n}>;\n\n// ============================================================\n// Embedding Parameters\n// ============================================================\n\n/**\n * Parameters for inserting or updating an embedding.\n *\n * `dimensions`, `metric`, and `indexType` together resolve the slot's\n * typed per-`(nodeKind, fieldPath)` storage (the strategy needs the fixed\n * dimension for the column type, and the metric/index type to address the\n * right ANN structure). The store populates them from the schema's\n * `embedding()` declaration via `getEmbeddingDimensions()` /\n * `getEmbeddingIndex()`; the backend constructs a `VectorSlot` from them\n * and stays graph-agnostic.\n */\nexport type UpsertEmbeddingParams = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeId: string;\n fieldPath: string;\n embedding: readonly number[];\n dimensions: number;\n metric: VectorMetric;\n indexType: VectorIndexType;\n}>;\n\n/**\n * Parameters for deleting an embedding.\n *\n * `dimensions` / `metric` / `indexType` mirror {@link UpsertEmbeddingParams}\n * so the backend can resolve the slot's typed per-`(nodeKind, fieldPath)`\n * storage and idempotently ensure it exists before the DELETE. That\n * matters because a delete can run before any embedding was ever written\n * (e.g. a node hard-deleted having never carried one), and on Postgres a\n * DELETE against a missing relation inside a transaction aborts the whole\n * transaction. The store populates them from the schema's `embedding()`\n * declaration, exactly as for upserts.\n */\nexport type DeleteEmbeddingParams = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeId: string;\n fieldPath: string;\n dimensions: number;\n metric: VectorMetric;\n indexType: VectorIndexType;\n}>;\n\n/**\n * Parameters for vector similarity search.\n */\nexport type VectorSearchParams = Readonly<{\n graphId: string;\n nodeKind: string;\n fieldPath: string;\n queryEmbedding: readonly number[];\n metric: VectorMetric;\n /**\n * Fixed vector dimension `N` for the searched `(nodeKind, fieldPath)`\n * slot. The store populates it from the schema's `embedding()`\n * declaration via `getEmbeddingDimensions()`; the backend uses it to\n * construct the `VectorSlot` whose typed storage the strategy scans.\n */\n dimensions: number;\n /**\n * Index type materialized for this slot. `\"none\"` means brute-force\n * only; otherwise the strategy may route through its ANN structure.\n * The store populates it from `getEmbeddingIndex()`.\n */\n indexType: VectorIndexType;\n limit: number;\n minScore?: number;\n /**\n * HNSW search frontier for this query (pgvector `hnsw.ef_search`).\n * Sizes the dynamic candidate list the index scan maintains: higher\n * trades latency for recall. The floor for the over-fetch to fill its\n * candidate set is `efSearch >= limit`; ~2–4× is the high-recall\n * target. Applied transaction-locally (`SET LOCAL`) on the Postgres\n * HNSW path only. No-op on backends without an HNSW frontier knob\n * (sqlite-vec) and ignored — with a one-time warning — on Postgres\n * backends without transactions (e.g. `drizzle-orm/neon-http`).\n */\n efSearch?: number;\n}>;\n\n/**\n * Result from a vector similarity search.\n */\nexport type VectorSearchResult = Readonly<{\n nodeId: string;\n /**\n * Cosine metric returns similarity score (higher is better).\n * L2 and inner_product return raw distance (lower is better).\n */\n score: number;\n}>;\n\n/**\n * Parameters for creating a vector index.\n */\nexport type CreateVectorIndexParams = Readonly<{\n graphId: string;\n nodeKind: string;\n fieldPath: string;\n dimensions: number;\n metric: VectorMetric;\n indexType: VectorIndexType;\n /** Index-specific parameters */\n indexParams?: Readonly<{\n /** HNSW: max connections per layer */\n m?: number;\n /** HNSW: construction search depth */\n efConstruction?: number;\n /** IVFFlat: number of lists */\n lists?: number;\n }>;\n /**\n * Build the index without taking an `AccessExclusiveLock` on live\n * tables (Postgres `CREATE INDEX CONCURRENTLY`). Mirrors the\n * `concurrent` flag the relational DDL path uses inside\n * `materializeIndexes()`. Cannot be set inside a transaction —\n * callers (`materializeIndexes()`) run at top level. Backends that\n * don't have a CONCURRENTLY equivalent (SQLite) ignore this flag.\n */\n concurrent?: boolean;\n}>;\n\n/**\n * Parameters for dropping a vector index.\n */\nexport type DropVectorIndexParams = Readonly<{\n graphId: string;\n nodeKind: string;\n fieldPath: string;\n}>;\n\n// ============================================================\n// Fulltext Parameters\n// ============================================================\n\n/**\n * Parameters for inserting or updating a fulltext entry.\n *\n * One row per node. Callers concatenate the searchable fields into\n * `content` so a single MATCH query can find terms spread across fields.\n *\n * Note: `createFulltextIndex` / `dropFulltextIndex` were removed as\n * dead code in #PR_E. The fulltext table's canonical index (Postgres\n * GIN on `tsv`, SQLite FTS5 virtual table) is created with the table\n * itself by `bootstrapTables` per the active `FulltextStrategy`;\n * per-kind fulltext indexes are an \"advanced strategy\" surface that\n * doesn't fit the relational-style declaration model and is reserved\n * for future work.\n */\nexport type UpsertFulltextParams = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeId: string;\n content: string;\n language: string;\n}>;\n\n/**\n * Parameters for deleting a single fulltext entry.\n */\nexport type DeleteFulltextParams = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeId: string;\n}>;\n\n/**\n * A single row in a fulltext batch upsert.\n */\nexport type FulltextBatchRow = Readonly<{\n nodeId: string;\n content: string;\n language: string;\n}>;\n\n/**\n * Parameters for a batched fulltext upsert.\n *\n * Homogeneous: one graph, one node kind, many nodes. Duplicate `nodeId`\n * values within a single batch are deduplicated last-write-wins by the\n * builders before SQL generation — Postgres `ON CONFLICT` errors on\n * repeated conflict keys in one statement, and SQLite `DELETE + INSERT`\n * would create duplicate virtual-table rows otherwise.\n */\nexport type UpsertFulltextBatchParams = Readonly<{\n graphId: string;\n nodeKind: string;\n rows: readonly FulltextBatchRow[];\n}>;\n\n/**\n * Parameters for a batched fulltext delete.\n */\nexport type DeleteFulltextBatchParams = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeIds: readonly string[];\n}>;\n\n/**\n * Parameters for fulltext search.\n */\nexport type FulltextSearchParams = Readonly<{\n graphId: string;\n nodeKind: string;\n /** The user-supplied query string. */\n query: string;\n /** How to parse `query`. Default: \"websearch\". */\n mode?: FulltextQueryMode;\n /**\n * Language override. Default: per-row language (as stored at insert time).\n * Postgres: passed as the regconfig to `to_tsquery` / `websearch_to_tsquery`.\n * SQLite: informational only (FTS5 tokenizer is fixed at table-create time).\n */\n language?: string;\n /** Max rows to return. */\n limit: number;\n /** Minimum rank to include (backend-dependent units). */\n minScore?: number;\n /** Whether to return a highlighted snippet per match. */\n includeSnippets?: boolean;\n}>;\n\n/**\n * Result from a fulltext search.\n *\n * Score semantics differ by backend; prefer `rank` (1-based) when fusing\n * with another source via RRF.\n */\nexport type FulltextSearchResult = Readonly<{\n nodeId: string;\n /**\n * Backend-native relevance score.\n * Postgres: `ts_rank_cd` (higher is better).\n * SQLite FTS5: negated `bm25()` (higher is better; FTS5 returns lower-is-better).\n */\n score: number;\n /** 1-based rank within the result set, suitable for RRF. */\n rank: number;\n /** Highlighted snippet of the content (if `includeSnippets` was set). */\n snippet?: string;\n}>;\n\n/**\n * Parameters for creating a fulltext index.\n *\n * The canonical index (Postgres GIN on `tsv`, SQLite FTS5 virtual table)\n * is created when the fulltext table itself is created. This is reserved\n * for advanced per-kind specializations.\n */\n\n// ============================================================\n// Index Materialization Status\n// ============================================================\n\n/**\n * Per-deployment record for a single declared index.\n *\n * Identified by `indexName` because SQL index names are physical,\n * database-global identifiers — `graphId` is provenance, not identity.\n * `materializedAt` is null until the first successful CREATE INDEX\n * completes; `lastAttemptedAt` is always set, even on failure.\n */\nexport type IndexMaterializationRow = Readonly<{\n indexName: string;\n graphId: string;\n entity: IndexEntity;\n kind: string;\n signature: string;\n schemaVersion: number;\n materializedAt: string | undefined;\n lastAttemptedAt: string;\n lastError: string | undefined;\n}>;\n\n/**\n * Parameters for upserting a materialization attempt.\n *\n * On success: pass `materializedAt` (ISO timestamp) and undefined `error`.\n * On failure: pass undefined `materializedAt` (preserve any existing\n * timestamp from a prior success) and the error message.\n */\nexport type RecordIndexMaterializationParams = Readonly<{\n indexName: string;\n graphId: string;\n entity: IndexEntity;\n kind: string;\n signature: string;\n schemaVersion: number;\n attemptedAt: string;\n /** ISO timestamp on success; undefined on failure (preserves existing). */\n materializedAt: string | undefined;\n /** Error message on failure; undefined on success (clears existing). */\n error: string | undefined;\n}>;\n\n// ============================================================\n// Contribution Materializations (#135 — durable strategy-owned\n// storage marker, sibling of index materializations)\n// ============================================================\n\n/**\n * Durable identity of a strategy-owned table contribution (#129),\n * scoped to a graph. This is the primary key of\n * `typegraph_contribution_materializations`.\n *\n * `logicalName` is the stable slot (\"fulltext\"); `owner` is the\n * producing strategy (\"tsvector\" / \"fts5\"); `tableName` is the resolved\n * physical name (custom per-deployment names must be distinguishable).\n * `graphId` is part of identity here — unlike the index status table\n * where the physical index name is database-global, two graphs can each\n * own a logically-identical fulltext contribution.\n */\nexport type ContributionMaterializationIdentity = Readonly<{\n graphId: string;\n logicalName: string;\n owner: string;\n tableName: string;\n}>;\n\n/**\n * Per-deployment record that a strategy-owned contribution has been\n * durably materialized against this database.\n *\n * `signature` is intentionally NOT part of the identity: a row with the\n * same identity but a different signature means \"materialized artifact\n * is stale/drifted\" — a loud error on the hot path, never a silent\n * re-materialize. `materializedAt` is undefined until the first\n * successful materialization; `lastAttemptedAt` is always set.\n */\nexport type ContributionMaterializationRow = Readonly<{\n graphId: string;\n logicalName: string;\n owner: string;\n tableName: string;\n signature: string;\n materializedAt: string | undefined;\n lastAttemptedAt: string;\n lastError: string | undefined;\n}>;\n\n/**\n * Parameters for upserting a contribution-materialization attempt.\n * Same success/failure contract as\n * {@link RecordIndexMaterializationParams}: on failure pass undefined\n * `materializedAt` so a prior successful timestamp is preserved via\n * COALESCE, and the error message.\n */\nexport type RecordContributionMaterializationParams = Readonly<{\n graphId: string;\n logicalName: string;\n owner: string;\n tableName: string;\n signature: string;\n attemptedAt: string;\n /** ISO timestamp on success; undefined on failure (preserves existing). */\n materializedAt: string | undefined;\n /** Error message on failure; undefined on success (clears existing). */\n error: string | undefined;\n}>;\n\n// ============================================================\n// Kind Removals (data-cleanup status)\n// ============================================================\n\n/**\n * One row of the per-deployment `typegraph_kind_removals` table:\n * a graph-extension kind that has been removed from the schema and whose\n * data may or may not have been cleaned up yet.\n */\nexport type KindRemovalRow = Readonly<{\n graphId: string;\n kindName: string;\n entity: KindEntity;\n schemaVersion: number;\n /** ISO timestamp when the data-cleanup pass succeeded; undefined while pending. */\n removedAt: string | undefined;\n lastAttemptedAt: string;\n lastError: string | undefined;\n}>;\n\n/**\n * Upsert payload for a kind-removal status row. On removal-commit:\n * pass `removedAt: undefined` (the pending state). On successful\n * data-cleanup: pass `removedAt` set and `error: undefined`. On\n * cleanup failure: pass `removedAt: undefined` (preserves any prior\n * timestamp from a partial success on a different replica) and the\n * error message.\n */\nexport type RecordKindRemovalParams = Readonly<{\n graphId: string;\n kindName: string;\n entity: KindEntity;\n schemaVersion: number;\n attemptedAt: string;\n /** ISO timestamp on success; undefined while pending or on failure. */\n removedAt: string | undefined;\n /** Error message on failure; undefined on success or while pending. */\n error: string | undefined;\n}>;\n\n// ============================================================\n// Query Types\n// ============================================================\n\n/**\n * Transaction options.\n */\nexport type TransactionOptions = Readonly<{\n /** Transaction isolation level (if supported) */\n isolationLevel?:\n | \"read_uncommitted\"\n | \"read_committed\"\n | \"repeatable_read\"\n | \"serializable\";\n}>;\n\n/**\n * A caller-owned, already-open Drizzle transaction handle that a\n * TypeGraph store can adopt so both layers commit/rollback together.\n *\n * The literal client the caller's transaction runs on — a `PgDatabase`\n * transaction (node-postgres / `neon-serverless` Pool) or a\n * `BaseSQLiteDatabase` connection. Async drivers obtain it from\n * `db.transaction(async (tx) => …)`; synchronous `better-sqlite3` (whose\n * driver rejects an `async` transaction callback) passes the connection\n * itself under an explicit `BEGIN`/`COMMIT`/`ROLLBACK`. Passing it to\n * {@link GraphBackend.adoptTransaction} threads that *literal* client\n * through TypeGraph's SQL, so graph writes and the caller's own\n * relational writes share one Postgres/SQLite transaction (#134).\n */\nexport type AdoptedTransaction = AnyPgDatabase | AnySqliteDatabase;\n\n// ============================================================\n// Backend Interface\n// ============================================================\n\n/**\n * Transaction backend — a backend scoped to a transaction.\n *\n * `commitSchemaVersion` and `setActiveVersion` are intentionally omitted:\n * the atomicity / CAS guarantees of those primitives depend on\n * dialect-specific write-locking (BEGIN IMMEDIATE on SQLite,\n * `pg_advisory_xact_lock` on Postgres) acquired by the top-level\n * backend wrappers, not the transaction itself. Calling them from a\n * user-supplied `backend.transaction(...)` callback would bypass that\n * locking and silently weaken the orphan-row crash window the primitive\n * exists to eliminate. Schema commits go through the top-level backend\n * methods only.\n */\nexport type TransactionBackend = Omit<\n GraphBackend,\n | \"transaction\"\n | \"adoptTransaction\"\n | \"close\"\n | \"refreshStatistics\"\n | \"commitSchemaVersion\"\n | \"setActiveVersion\"\n>;\n\n/**\n * The GraphBackend interface abstracts database operations.\n *\n * Implementations should provide:\n * - SQLite backend via better-sqlite3 or libsql\n * - PostgreSQL backend via pg or postgres\n */\nexport type GraphBackend = Readonly<{\n /** The SQL dialect */\n dialect: SqlDialect;\n /** Backend capabilities */\n capabilities: BackendCapabilities;\n /** Table names used by this backend (for query schema auto-derivation) */\n tableNames?: SqlTableNames | undefined;\n /**\n * Optional fulltext strategy override. When present, both the compiler\n * (for `$fulltext.matches()` in query builder) and backend-direct\n * search paths use this instead of the dialect's default strategy —\n * allowing a Postgres backend to ship pg_trgm, ParadeDB, pgroonga etc.\n * When absent, the dialect's default strategy is used.\n */\n fulltextStrategy?: FulltextStrategy | undefined;\n /**\n * Optional vector strategy this backend is wired with. The query\n * compiler reads it (via `compileQuery` options) to emit per-`(kind,\n * field)` relevance scans for `field.similarTo(...)` predicates, and\n * the index-materialization / removal paths read its deterministic\n * `tableName(...)` to address the right physical per-field storage.\n * `undefined` when the backend has no vector support (e.g. a generic\n * SQLite backend without sqlite-vec).\n */\n vectorStrategy?: VectorStrategy | undefined;\n\n // === Node Operations ===\n insertNode: (params: InsertNodeParams) => Promise<NodeRow>;\n insertNodeNoReturn?: (params: InsertNodeParams) => Promise<void>;\n insertNodesBatch?: (params: readonly InsertNodeParams[]) => Promise<void>;\n insertNodesBatchReturning?: (\n params: readonly InsertNodeParams[],\n ) => Promise<readonly NodeRow[]>;\n updateNode: (params: UpdateNodeParams) => Promise<NodeRow>;\n deleteNode: (params: DeleteNodeParams) => Promise<void>;\n hardDeleteNode: (params: HardDeleteNodeParams) => Promise<void>;\n getNode: (\n graphId: string,\n kind: string,\n id: string,\n ) => Promise<NodeRow | undefined>;\n getNodes?: (\n graphId: string,\n kind: string,\n ids: readonly string[],\n ) => Promise<readonly NodeRow[]>;\n\n // === Edge Operations ===\n insertEdge: (params: InsertEdgeParams) => Promise<EdgeRow>;\n insertEdgeNoReturn?: (params: InsertEdgeParams) => Promise<void>;\n insertEdgesBatch?: (params: readonly InsertEdgeParams[]) => Promise<void>;\n insertEdgesBatchReturning?: (\n params: readonly InsertEdgeParams[],\n ) => Promise<readonly EdgeRow[]>;\n updateEdge: (params: UpdateEdgeParams) => Promise<EdgeRow>;\n deleteEdge: (params: DeleteEdgeParams) => Promise<void>;\n hardDeleteEdge: (params: HardDeleteEdgeParams) => Promise<void>;\n getEdge: (graphId: string, id: string) => Promise<EdgeRow | undefined>;\n getEdges?: (\n graphId: string,\n ids: readonly string[],\n ) => Promise<readonly EdgeRow[]>;\n\n // === Edge Cardinality Operations ===\n countEdgesFrom: (params: CountEdgesFromParams) => Promise<number>;\n edgeExistsBetween: (params: EdgeExistsBetweenParams) => Promise<boolean>;\n\n // === Edge Query Operations ===\n findEdgesConnectedTo: (\n params: FindEdgesConnectedToParams,\n ) => Promise<readonly EdgeRow[]>;\n\n // === Collection Query Operations ===\n findNodesByKind: (\n params: FindNodesByKindParams,\n ) => Promise<readonly NodeRow[]>;\n countNodesByKind: (params: CountNodesByKindParams) => Promise<number>;\n findEdgesByKind: (\n params: FindEdgesByKindParams,\n ) => Promise<readonly EdgeRow[]>;\n countEdgesByKind: (params: CountEdgesByKindParams) => Promise<number>;\n\n // === Unique Constraint Operations ===\n insertUnique: (params: InsertUniqueParams) => Promise<void>;\n deleteUnique: (params: DeleteUniqueParams) => Promise<void>;\n checkUnique: (params: CheckUniqueParams) => Promise<UniqueRow | undefined>;\n checkUniqueBatch?: (\n params: CheckUniqueBatchParams,\n ) => Promise<readonly UniqueRow[]>;\n\n // === Schema Operations ===\n getActiveSchema: (graphId: string) => Promise<SchemaVersionRow | undefined>;\n getSchemaVersion: (\n graphId: string,\n version: number,\n ) => Promise<SchemaVersionRow | undefined>;\n /**\n * Atomically inserts a new schema version and activates it as a single\n * transactional unit, with optimistic compare-and-swap on the currently\n * active version.\n *\n * - If `expected.kind === \"active\"` and the actual active version\n * differs, throws `StaleVersionError` (caller should refetch and\n * retry).\n * - If a row already exists at `params.version` with the same\n * `schemaHash`, returns it idempotently — reactivating it if it was\n * left inactive by an earlier crashed commit.\n * - If a row already exists at `params.version` with a different\n * `schemaHash`, throws `SchemaContentConflictError`.\n *\n * Requires `capabilities.transactions === true`. On non-transactional\n * backends (e.g. Cloudflare D1, drizzle-orm/neon-http) this method\n * throws `ConfigurationError` rather than running with degraded\n * atomicity that would silently re-introduce the orphan-row crash\n * window the primitive exists to eliminate.\n */\n commitSchemaVersion: (\n params: CommitSchemaVersionParams,\n ) => Promise<SchemaVersionRow>;\n /**\n * Atomically flips the active schema pointer to an existing version,\n * with optimistic compare-and-swap on the currently active version.\n * Used by `rollbackSchema` and any other \"promote/demote existing\n * version\" workflow. Throws `StaleVersionError` on CAS mismatch and\n * `MigrationError` if the target version row does not exist.\n *\n * Same transactional requirements as `commitSchemaVersion`.\n */\n setActiveVersion: (params: SetActiveVersionParams) => Promise<void>;\n\n // === Embedding Operations (optional - depends on vector capabilities) ===\n upsertEmbedding?: (params: UpsertEmbeddingParams) => Promise<void>;\n deleteEmbedding?: (params: DeleteEmbeddingParams) => Promise<void>;\n vectorSearch?: (\n params: VectorSearchParams,\n ) => Promise<readonly VectorSearchResult[]>;\n createVectorIndex?: (params: CreateVectorIndexParams) => Promise<void>;\n dropVectorIndex?: (params: DropVectorIndexParams) => Promise<void>;\n\n // === Fulltext Operations (optional - depends on fulltext capabilities) ===\n upsertFulltext?: (params: UpsertFulltextParams) => Promise<void>;\n deleteFulltext?: (params: DeleteFulltextParams) => Promise<void>;\n /**\n * Batched variant of `upsertFulltext`. Optional — callers fall back to\n * per-row `upsertFulltext` when unset.\n */\n upsertFulltextBatch?: (params: UpsertFulltextBatchParams) => Promise<void>;\n /**\n * Batched variant of `deleteFulltext`. Optional — callers fall back to\n * per-row `deleteFulltext` when unset.\n */\n deleteFulltextBatch?: (params: DeleteFulltextBatchParams) => Promise<void>;\n fulltextSearch?: (\n params: FulltextSearchParams,\n ) => Promise<readonly FulltextSearchResult[]>;\n\n // === Index Materialization (used by store.materializeIndexes) ===\n /**\n * Idempotently ensure ONLY the `typegraph_index_materializations`\n * table exists — separate from `bootstrapTables` so that\n * `materializeIndexes` doesn't pull in the full base-table DDL set\n * just to access the status table.\n *\n * Why focused: `bootstrapTables` issues 20+ `CREATE TABLE / CREATE\n * INDEX IF NOT EXISTS` statements covering every base table. Two\n * concurrent calls (e.g. two replicas of the same `schema_doc` both\n * starting up and calling `materializeIndexes`) race on\n * Postgres SHARE locks and DEADLOCK. Restricting the ensure-step to\n * the single status table eliminates the cross-table race entirely\n * — concurrent `CREATE TABLE IF NOT EXISTS` for one specific table\n * is well-behaved on Postgres.\n */\n ensureIndexMaterializationsTable?: () => Promise<void>;\n\n /**\n * Look up a recorded materialization for a declared index by its\n * physical SQL index name. Returns `undefined` if no row exists.\n */\n getIndexMaterialization?: (\n indexName: string,\n ) => Promise<IndexMaterializationRow | undefined>;\n /**\n * Bulk variant of `getIndexMaterialization`: load every recorded\n * materialization whose `indexName` (status key) is in `statusKeys`,\n * in a single round-trip. Returned rows are unordered — callers index\n * by `indexName`. Optional; consumers fall back to per-key\n * `getIndexMaterialization` when unset.\n */\n getIndexMaterializations?: (\n statusKeys: readonly string[],\n ) => Promise<readonly IndexMaterializationRow[]>;\n /**\n * Upsert a materialization attempt — success or failure. Failure rows\n * preserve any prior `materializedAt` so the historical successful\n * timestamp survives across error windows.\n */\n recordIndexMaterialization?: (\n params: RecordIndexMaterializationParams,\n ) => Promise<void>;\n\n // === Contribution Materialization (#135 — durable strategy-owned\n // storage marker, sibling of the index status table) ===\n\n /**\n * Idempotently ensure ONLY the\n * `typegraph_contribution_materializations` table exists. Same\n * focused-bootstrap rationale as `ensureIndexMaterializationsTable`:\n * a single `CREATE TABLE IF NOT EXISTS` is concurrency-safe under\n * replica startup, where the full `bootstrapTables` set risks a\n * Postgres SHARE-lock deadlock.\n */\n ensureContributionMaterializationsTable?: () => Promise<void>;\n\n /**\n * Look up the durable materialization marker for one strategy-owned\n * contribution identity. Returns `undefined` when no row exists\n * (\"never initialized\").\n */\n getContributionMaterialization?: (\n identity: ContributionMaterializationIdentity,\n ) => Promise<ContributionMaterializationRow | undefined>;\n\n /**\n * Upsert a contribution-materialization attempt — success or failure.\n * Failure rows preserve any prior `materializedAt` via COALESCE so a\n * later failed re-attempt doesn't erase the historical success.\n */\n recordContributionMaterialization?: (\n params: RecordContributionMaterializationParams,\n ) => Promise<void>;\n\n /**\n * Resolve (once per backend instance, cached) and assert the durable\n * materialization markers for every `runtimeEnsure` contribution this\n * backend's strategy declares, for `graphId`. Throws\n * `StoreNotInitializedError` when a marker is missing, stale\n * (signature drift), or recorded a failed last attempt.\n *\n * This is the single read-side gate the fulltext hot-path wrappers\n * and `store.transaction()` consult. It performs ZERO DDL and ZERO\n * marker writes — initialization is the exclusive job of the async\n * boot path (`createStoreWithSchema` → `ensureRuntimeContributions`).\n */\n assertRuntimeContributionsInitialized?: (graphId: string) => Promise<void>;\n\n // === Kind Removal Status ===\n\n /**\n * Bootstraps the per-deployment `typegraph_kind_removals` table so\n * `store.removeKinds()` and `store.materializeRemovals()` can persist\n * removal status. Mirrors the focused-bootstrap rationale documented\n * on `ensureIndexMaterializationsTable` — the full `bootstrapTables`\n * touches every base table and risks Postgres SHARE-lock deadlock\n * under concurrent replica startup.\n */\n ensureKindRemovalsTable?: () => Promise<void>;\n\n /**\n * List graph-extension kind removals whose data-cleanup pass has not yet\n * succeeded for this `graphId`. Returns rows with\n * `removedAt: undefined`. Order is unspecified; callers materialize\n * one-at-a-time and don't depend on it.\n */\n getPendingKindRemovals?: (\n graphId: string,\n ) => Promise<readonly KindRemovalRow[]>;\n\n /**\n * List ALL kind-removal rows for a `graphId` — pending and completed.\n * Used by `materializeRemovals()` reconciliation to detect rows that\n * are missing entirely (the `removeKinds()` crash window) versus\n * already completed. Without this distinction the reconciler would\n * have to upsert every expected historical removal on every call,\n * churning `last_attempted_at` on rows that long since succeeded.\n * Order is unspecified.\n */\n getAllKindRemovals?: (graphId: string) => Promise<readonly KindRemovalRow[]>;\n\n /**\n * Upsert a kind-removal status row. `removedAt: undefined` records\n * the pending state at schema-commit time; `removedAt: <iso>`\n * marks the data cleanup successful. The COALESCE rule on `removedAt`\n * mirrors `recordIndexMaterialization` so a later failure doesn't\n * clobber the historical successful timestamp from another replica.\n */\n recordKindRemoval?: (params: RecordKindRemovalParams) => Promise<void>;\n\n // === Reconciliation Watermark ===\n\n /**\n * Bootstraps the per-deployment `typegraph_reconciliation_markers`\n * table so `materializeRemovals()` can persist reconciliation\n * progress. Same focused-bootstrap rationale as the other status\n * tables — full `bootstrapTables` risks Postgres SHARE-lock\n * deadlock under concurrent replica startup.\n */\n ensureReconciliationMarkersTable?: () => Promise<void>;\n\n // === Table Contributions (#129) ===\n\n /**\n * Materializes every contribution flagged `runtimeEnsure` — the\n * strategy-owned runtime tables (fulltext today) that drizzle-kit-\n * managed setups don't create. Called once after a successful schema\n * load. Deliberately scoped: base/drizzle-visible tables are\n * `runtimeEnsure: false`, so this does not regress startup into\n * broad DDL/probing across every table.\n *\n * The canonical durable-marker writer (#135): for each runtime\n * contribution it short-circuits when the marker already records a\n * matching signature, otherwise runs the idempotent `createDdl` and\n * records the marker (success or failure) keyed by `graphId`.\n */\n ensureRuntimeContributions?: (graphId: string) => Promise<void>;\n\n /**\n * Bootstraps the fulltext storage table the active `FulltextStrategy`\n * owns. Same focused-bootstrap rationale as the other `ensure*Table`\n * methods: idempotent and concurrency-safe under replica startup.\n *\n * Superseded by `ensureRuntimeContributions()` (#129); retained as\n * a thin back-compat wrapper for backends/callers predating #129. Not\n * machine-`@deprecated` because the manager still calls it as the\n * pre-#129 fallback. #135 removed the remaining hot-path callers and\n * routed this through the durable-marker writer.\n */\n ensureFulltextTable?: (graphId: string) => Promise<void>;\n\n /**\n * Read the high-water mark schema version for which\n * `materializeRemovals` reconciliation has already verified history\n * for `graphId`. Returns `undefined` when no marker has been\n * recorded yet. Used to skip already-checked transitions in the\n * recovery walk.\n */\n getReconciliationMarker?: (graphId: string) => Promise<number | undefined>;\n\n /**\n * Persist the reconciliation high-water mark for `graphId`. Called\n * after `materializeRemovals` completes a clean walk; subsequent\n * calls walk only versions newer than this marker. Idempotent\n * upsert by `graphId`.\n */\n setReconciliationMarker?: (graphId: string, version: number) => Promise<void>;\n\n // === Graph Lifecycle ===\n /**\n * Hard-deletes all data for a graph (nodes, edges, uniques, embeddings, schema versions).\n * Intended for import-replacement workflows. No hooks, no per-row logic.\n */\n clearGraph: (graphId: string) => Promise<void>;\n\n /**\n * Creates the base TypeGraph tables if they don't already exist.\n *\n * Called automatically by `createStoreWithSchema()` when a fresh database\n * is detected. Users who manage DDL themselves via `createStore()` never\n * hit this path.\n */\n bootstrapTables?: () => Promise<void>;\n\n /**\n * Refreshes the backend's query-planner statistics.\n *\n * Call this once after a large initial import or bulk backfill. Without\n * up-to-date statistics, the planner can pick suboptimal execution plans\n * — on PostgreSQL this is the difference between a 0.5ms and a 5ms\n * forward traversal; on SQLite it's the difference between 0.9ms and\n * 23ms fulltext search. Autovacuum / background statistics collection\n * will catch up eventually, but calling this explicitly after a bulk\n * load gives you correct latencies immediately.\n *\n * Implementations:\n * - SQLite runs `ANALYZE`, which populates `sqlite_stat1`\n * - PostgreSQL runs `ANALYZE` on the TypeGraph-managed tables\n *\n * Safe to call at any time; costs a few tens of milliseconds on the\n * sizes this library is designed for.\n */\n refreshStatistics: () => Promise<void>;\n\n // === Query Execution ===\n execute: <T>(query: SQL) => Promise<readonly T[]>;\n\n /** Execute pre-compiled SQL text with bound parameters. Available on sync SQLite and pg backends. */\n executeRaw?: <T>(\n sqlText: string,\n params: readonly unknown[],\n ) => Promise<readonly T[]>;\n\n /** Compile a Drizzle SQL object to { sql, params } without executing. */\n compileSql?: (\n query: SQL,\n ) => Readonly<{ sql: string; params: readonly unknown[] }>;\n\n /**\n * Execute a DDL statement that returns no rows (CREATE INDEX,\n * CREATE TABLE, ALTER TABLE, etc.). Separate from `executeRaw`\n * because some drivers (better-sqlite3) require `.run()` for DDL\n * and `.all()` for queries — the ambiguity can't be resolved by\n * inspecting the SQL string portably.\n *\n * Postgres path can use this for `CREATE INDEX CONCURRENTLY`, which\n * cannot run inside a transaction. Implementations must execute the\n * statement outside `transaction(...)`.\n */\n executeDdl?: (ddl: string) => Promise<void>;\n\n // === Transaction ===\n /**\n * `fn` receives the tx-scoped backend and the raw Drizzle handle\n * **bound to that same transaction** (`AdoptedTransaction`). The\n * store surfaces the second argument as `TransactionContext.sql` so\n * callers can write their own relational tables inside the\n * graph-owned transaction. Implementations MUST pass the *exact*\n * handle the tx-scoped backend writes through (the Postgres/libsql\n * tx handle; for better-sqlite3 / do-sqlite the bound connection),\n * never a fresh one.\n */\n transaction: <T>(\n fn: (tx: TransactionBackend, sql: AdoptedTransaction) => Promise<T>,\n options?: TransactionOptions,\n ) => Promise<T>;\n\n /**\n * Adopt a caller-owned, already-open transaction (#134).\n *\n * Returns a transaction-scoped backend bound to the *exact*\n * `externalTx` client, reusing this backend's already-resolved\n * schema/strategy config — no `createStoreWithSchema` / `evolve` /\n * `migrateSchema`, and **no DDL inside the caller's business\n * transaction**. The caller owns `BEGIN`/`COMMIT`/`ROLLBACK`; this\n * method neither opens nor closes a transaction. Async drivers\n * (node-postgres, `neon-serverless` Pool, libsql) wrap with\n * `db.transaction(async …)`; synchronous `better-sqlite3` must instead\n * issue explicit `BEGIN`/`COMMIT`/`ROLLBACK` (its driver rejects an\n * `async` transaction callback).\n *\n * Optional and presence-detected like the other capability-scoped\n * members: only the Drizzle Postgres/SQLite backends provide it.\n * Implementations MUST throw (not silently degrade) when\n * `capabilities.transactions` is `false` — a non-atomic fallback is\n * safe for graph-only writes but dangerous for cross-store flows,\n * where the caller's relational write *would* still commit.\n *\n * Fulltext stays safe by construction: the returned backend's\n * fulltext methods assert the durable materialization marker (a\n * cached SELECT, never DDL) at point of use and throw\n * `StoreNotInitializedError` on a missing/stale/failed marker rather\n * than migrating mid-transaction. Prefer booting the parent store via\n * `createStoreWithSchema` so that assertion is a warm-cache no-op.\n */\n adoptTransaction?: (externalTx: AdoptedTransaction) => TransactionBackend;\n\n // === Lifecycle ===\n close: () => Promise<void>;\n}>;\n\n// ============================================================\n// Managed Backend Helper\n// ============================================================\n\n/**\n * Wraps a GraphBackend with idempotent close that also runs a teardown\n * callback (e.g. closing the underlying database connection).\n */\nexport function wrapWithManagedClose(\n backend: GraphBackend,\n teardown: () => void | Promise<void>,\n): GraphBackend {\n let isClosed = false;\n return {\n ...backend,\n async close(): Promise<void> {\n if (isClosed) return;\n isClosed = true;\n await backend.close();\n await teardown();\n },\n };\n}\n\n/**\n * Runs `fn` inside a transaction when the backend supports one, falling\n * through to a direct invocation otherwise. Lets call sites benefit from\n * atomicity on backends that have transactions while staying functional\n * on backends that don't (Cloudflare D1, `drizzle-orm/neon-http` over\n * HTTP). The single-statement race window is already implicit on any\n * backend that reports `transactions: false`; callers that cannot\n * tolerate it must branch on the capability themselves.\n *\n * Pass `fallback` only when the toplevel backend method would recurse\n * — pass the operation-level backend so the no-tx path doesn't loop\n * back through the same toplevel method.\n */\nexport async function runOptionallyInTransaction<T>(\n backend: GraphBackend,\n fn: (target: GraphBackend | TransactionBackend) => Promise<T>,\n fallback?: GraphBackend | TransactionBackend,\n): Promise<T> {\n if (!backend.capabilities.transactions) {\n return fn(fallback ?? backend);\n }\n return backend.transaction((tx) => fn(tx));\n}\n\n// ============================================================\n// Additional Parameter Types\n// ============================================================\n\n/**\n * Parameters for inserting a unique constraint entry.\n */\nexport type InsertUniqueParams = Readonly<{\n graphId: string;\n nodeKind: string;\n constraintName: string;\n key: string;\n nodeId: string;\n concreteKind: string;\n}>;\n\n/**\n * Parameters for deleting a unique constraint entry.\n */\nexport type DeleteUniqueParams = Readonly<{\n graphId: string;\n nodeKind: string;\n constraintName: string;\n key: string;\n}>;\n\n/**\n * Parameters for checking a unique constraint.\n */\nexport type CheckUniqueParams = Readonly<{\n graphId: string;\n nodeKind: string;\n constraintName: string;\n key: string;\n /** If true, also returns soft-deleted entries. Used by get-or-create operations. */\n includeDeleted?: boolean;\n}>;\n\n/**\n * Parameters for batch-checking unique constraints.\n */\nexport type CheckUniqueBatchParams = Readonly<{\n graphId: string;\n nodeKind: string;\n constraintName: string;\n keys: readonly string[];\n /** If true, also returns soft-deleted entries. Used by get-or-create operations. */\n includeDeleted?: boolean;\n}>;\n\n/**\n * Parameters for inserting a schema version.\n *\n * Used internally by backend implementations. Public callers go through\n * `commitSchemaVersion`, which handles the insert + activate atomically\n * with CAS guarantees.\n */\nexport type InsertSchemaParams = Readonly<{\n graphId: string;\n version: number;\n schemaHash: string;\n schemaDoc: SerializedSchema;\n isActive: boolean;\n}>;\n\n/**\n * The caller's claim about the currently-active schema version, used as\n * the optimistic compare-and-swap guard for `commitSchemaVersion`.\n *\n * - `{ kind: \"initial\" }` — caller is committing the first-ever version\n * for this graph and asserts no active version exists yet.\n * - `{ kind: \"active\", version: N }` — caller observed version N as\n * active and is committing version N+1 against that baseline. The\n * commit fails with `StaleVersionError` if some other writer has\n * advanced or rolled back the pointer in the meantime.\n *\n * Tagged-union form (rather than a magic `version: 0` sentinel) because\n * the two cases have materially different semantics: the initial path\n * skips the \"deactivate prior\" UPDATE, and the no-active-row state is\n * a *valid* expected state, not an out-of-band signal.\n */\nexport type CommitSchemaVersionExpected =\n | Readonly<{ kind: \"initial\" }>\n | Readonly<{ kind: \"active\"; version: number }>;\n\n/**\n * Parameters for `commitSchemaVersion`.\n */\nexport type CommitSchemaVersionParams = Readonly<{\n graphId: string;\n /** CAS guard — see `CommitSchemaVersionExpected`. */\n expected: CommitSchemaVersionExpected;\n /** The new version to insert and activate. */\n version: number;\n schemaHash: string;\n schemaDoc: SerializedSchema;\n}>;\n\n/**\n * Parameters for `setActiveVersion`.\n *\n * Flips the active pointer from `expected` to `version` for an existing\n * row. CAS prevents overwriting a concurrent rollback or commit.\n */\nexport type SetActiveVersionParams = Readonly<{\n graphId: string;\n /** CAS guard. Same semantics as `commitSchemaVersion.expected`. */\n expected: CommitSchemaVersionExpected;\n /** The version to mark active. Must already exist. */\n version: number;\n}>;\n\n/**\n * Parameters for counting edges from a source node.\n */\nexport type CountEdgesFromParams = Readonly<{\n graphId: string;\n edgeKind: string;\n fromKind: string;\n fromId: string;\n /** If true, only count edges where valid_to IS NULL */\n activeOnly?: boolean;\n}>;\n\n/**\n * Parameters for checking if an edge exists between two nodes.\n */\nexport type EdgeExistsBetweenParams = Readonly<{\n graphId: string;\n edgeKind: string;\n fromKind: string;\n fromId: string;\n toKind: string;\n toId: string;\n}>;\n\n/**\n * Parameters for finding edges connected to a node.\n */\nexport type FindEdgesConnectedToParams = Readonly<{\n graphId: string;\n nodeKind: string;\n nodeId: string;\n}>;\n\n/**\n * Parameters for finding nodes by kind.\n */\nexport type FindNodesByKindParams = Readonly<{\n graphId: string;\n kind: string;\n /** Max rows to return. */\n limit?: number;\n /** Offset. Present for backward compat; rebuild uses `after` instead. */\n offset?: number;\n /** If true, exclude deleted nodes. Default true. */\n excludeDeleted?: boolean;\n /** Temporal mode for filtering by validity period. */\n temporalMode?: TemporalMode;\n /** Timestamp for \"current\" and \"asOf\" temporal modes. */\n asOf?: string;\n /**\n * Stable ordering for keyset pagination. Default: \"created_at\" (existing\n * behavior). Rebuild should use \"id\" for iteration that is stable under\n * concurrent writes and shared timestamps.\n */\n orderBy?: \"id\" | \"created_at\";\n /**\n * Keyset cursor. Returns rows strictly greater (by `orderBy`) than this\n * value. When `orderBy: \"id\"`, compared lexicographically. Mutually\n * exclusive with `offset` — callers pick one.\n */\n after?: string;\n}>;\n\n/**\n * Parameters for counting nodes by kind.\n */\nexport type CountNodesByKindParams = Readonly<{\n graphId: string;\n kind: string;\n /** If true, exclude deleted nodes. Default true. */\n excludeDeleted?: boolean;\n /** Temporal mode for filtering by validity period. */\n temporalMode?: TemporalMode;\n /** Timestamp for \"current\" and \"asOf\" temporal modes. */\n asOf?: string;\n}>;\n\n/**\n * Parameters for finding edges by kind.\n */\nexport type FindEdgesByKindParams = Readonly<{\n graphId: string;\n kind: string;\n fromKind?: string;\n fromId?: string;\n toKind?: string;\n toId?: string;\n limit?: number;\n offset?: number;\n /** If true, exclude deleted edges. Default true. */\n excludeDeleted?: boolean;\n /** Temporal mode for filtering by validity period. */\n temporalMode?: TemporalMode;\n /** Timestamp for \"current\" and \"asOf\" temporal modes. */\n asOf?: string;\n /**\n * Stable ordering for keyset pagination. Default: \"created_at\" (existing\n * behavior). Use \"id\" for iteration that is stable under shared `created_at`\n * timestamps — the offset path orders by the NON-unique `created_at`, so a\n * full enumeration must page by the unique `id` to avoid skipping a row at a\n * page boundary. Mirrors {@link FindNodesByKindParams.orderBy}.\n */\n orderBy?: \"id\" | \"created_at\";\n /**\n * Keyset cursor. Returns rows strictly greater (by `orderBy`) than this value.\n * When `orderBy: \"id\"`, compared lexicographically. Mutually exclusive with\n * `offset` — callers pick one. Mirrors {@link FindNodesByKindParams.after}.\n */\n after?: string;\n}>;\n\n/**\n * Parameters for counting edges by kind.\n */\nexport type CountEdgesByKindParams = Readonly<{\n graphId: string;\n kind: string;\n fromKind?: string;\n fromId?: string;\n toKind?: string;\n toId?: string;\n /** If true, exclude deleted edges. Default true. */\n excludeDeleted?: boolean;\n /** Temporal mode for filtering by validity period. */\n temporalMode?: TemporalMode;\n /** Timestamp for \"current\" and \"asOf\" temporal modes. */\n asOf?: string;\n}>;\n\n// ============================================================\n// Default Capabilities\n// ============================================================\n\n/**\n * Default capabilities for SQLite.\n */\nexport const SQLITE_CAPABILITIES: BackendCapabilities = {\n transactions: true, // SQLite supports transactions\n windowFunctions: true, // SQLite has supported window functions since 3.25.0\n};\n\n/**\n * Default capabilities for PostgreSQL.\n */\nexport const POSTGRES_CAPABILITIES: BackendCapabilities = {\n transactions: true, // PostgreSQL supports transactions\n windowFunctions: true, // PostgreSQL supports ROW_NUMBER() and related windows\n};\n"]}
|