@nicia-ai/typegraph 0.35.0 → 0.36.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.
Files changed (116) hide show
  1. package/dist/backend/postgres/index.cjs +19 -19
  2. package/dist/backend/postgres/index.js +6 -6
  3. package/dist/backend/postgres/pglite.cjs +11 -11
  4. package/dist/backend/postgres/pglite.js +6 -6
  5. package/dist/backend/sqlite/index.cjs +9 -9
  6. package/dist/backend/sqlite/index.js +6 -6
  7. package/dist/backend/sqlite/libsql.cjs +8 -8
  8. package/dist/backend/sqlite/libsql.js +6 -6
  9. package/dist/backend/sqlite/local.cjs +16 -16
  10. package/dist/backend/sqlite/local.js +6 -6
  11. package/dist/{chunk-VPYHY3LJ.cjs → chunk-2THZ3TQI.cjs} +11 -11
  12. package/dist/{chunk-VPYHY3LJ.cjs.map → chunk-2THZ3TQI.cjs.map} +1 -1
  13. package/dist/{chunk-EM3CWPND.cjs → chunk-456Z2U7Y.cjs} +51 -51
  14. package/dist/{chunk-EM3CWPND.cjs.map → chunk-456Z2U7Y.cjs.map} +1 -1
  15. package/dist/{chunk-GSQ6NPH6.cjs → chunk-4BCU6VFZ.cjs} +7 -7
  16. package/dist/{chunk-GSQ6NPH6.cjs.map → chunk-4BCU6VFZ.cjs.map} +1 -1
  17. package/dist/chunk-4BMPGT35.cjs +12 -0
  18. package/dist/{chunk-GUR2FNB5.cjs.map → chunk-4BMPGT35.cjs.map} +1 -1
  19. package/dist/{chunk-WJT7OU7E.cjs → chunk-4CAXEB2F.cjs} +124 -92
  20. package/dist/chunk-4CAXEB2F.cjs.map +1 -0
  21. package/dist/{chunk-WDBCBWC4.js → chunk-4GX2JJXS.js} +6 -6
  22. package/dist/{chunk-WDBCBWC4.js.map → chunk-4GX2JJXS.js.map} +1 -1
  23. package/dist/{chunk-UES55P7G.js → chunk-5DIPR3L3.js} +284 -154
  24. package/dist/chunk-5DIPR3L3.js.map +1 -0
  25. package/dist/{chunk-CZQ3FO2Q.js → chunk-5VTR2F2K.js} +49 -16
  26. package/dist/chunk-5VTR2F2K.js.map +1 -0
  27. package/dist/{chunk-CTTSDJLY.cjs → chunk-5W4L6DLF.cjs} +11 -11
  28. package/dist/{chunk-CTTSDJLY.cjs.map → chunk-5W4L6DLF.cjs.map} +1 -1
  29. package/dist/{chunk-J7WFGBJR.cjs → chunk-7BQLPXK6.cjs} +865 -735
  30. package/dist/chunk-7BQLPXK6.cjs.map +1 -0
  31. package/dist/{chunk-3TWF3SH4.js → chunk-BJVEU3ZL.js} +7 -7
  32. package/dist/{chunk-3TWF3SH4.js.map → chunk-BJVEU3ZL.js.map} +1 -1
  33. package/dist/{chunk-LGEALRC6.cjs → chunk-DIK74MXX.cjs} +18 -2
  34. package/dist/chunk-DIK74MXX.cjs.map +1 -0
  35. package/dist/{chunk-QNKSQTKQ.js → chunk-DN5VP4JC.js} +48 -17
  36. package/dist/chunk-DN5VP4JC.js.map +1 -0
  37. package/dist/{chunk-4JEZZJ6S.cjs → chunk-HW3LW7DH.cjs} +39 -39
  38. package/dist/{chunk-4JEZZJ6S.cjs.map → chunk-HW3LW7DH.cjs.map} +1 -1
  39. package/dist/{chunk-JBTOYDMN.js → chunk-IU46OHVW.js} +4 -4
  40. package/dist/{chunk-JBTOYDMN.js.map → chunk-IU46OHVW.js.map} +1 -1
  41. package/dist/{chunk-FL472AMI.cjs → chunk-J3LEGNQB.cjs} +8 -8
  42. package/dist/{chunk-FL472AMI.cjs.map → chunk-J3LEGNQB.cjs.map} +1 -1
  43. package/dist/{chunk-O7UN7OXA.cjs → chunk-JFMEONLG.cjs} +80 -80
  44. package/dist/{chunk-O7UN7OXA.cjs.map → chunk-JFMEONLG.cjs.map} +1 -1
  45. package/dist/{chunk-I7ZUZKFD.js → chunk-JFUPDJXD.js} +3 -3
  46. package/dist/{chunk-I7ZUZKFD.js.map → chunk-JFUPDJXD.js.map} +1 -1
  47. package/dist/{chunk-K3PPLHY7.js → chunk-M75SUBFG.js} +7 -7
  48. package/dist/{chunk-K3PPLHY7.js.map → chunk-M75SUBFG.js.map} +1 -1
  49. package/dist/{chunk-CK5BVQOW.cjs → chunk-PWGX5DF5.cjs} +79 -79
  50. package/dist/{chunk-CK5BVQOW.cjs.map → chunk-PWGX5DF5.cjs.map} +1 -1
  51. package/dist/{chunk-KMI7LYAF.js → chunk-PZKNKG5M.js} +8 -8
  52. package/dist/{chunk-KMI7LYAF.js.map → chunk-PZKNKG5M.js.map} +1 -1
  53. package/dist/{chunk-55MZDDZV.js → chunk-RAXU3RMB.js} +4 -4
  54. package/dist/{chunk-55MZDDZV.js.map → chunk-RAXU3RMB.js.map} +1 -1
  55. package/dist/{chunk-BLZDPRXT.cjs → chunk-RSSQKPSZ.cjs} +5 -5
  56. package/dist/{chunk-BLZDPRXT.cjs.map → chunk-RSSQKPSZ.cjs.map} +1 -1
  57. package/dist/{chunk-RGPG7EOJ.js → chunk-S5F7ZLP2.js} +3 -3
  58. package/dist/{chunk-RGPG7EOJ.js.map → chunk-S5F7ZLP2.js.map} +1 -1
  59. package/dist/{chunk-QWEUYILR.js → chunk-SKCOUWAX.js} +3 -3
  60. package/dist/{chunk-QWEUYILR.js.map → chunk-SKCOUWAX.js.map} +1 -1
  61. package/dist/{chunk-QOTI55H7.js → chunk-SXGE7ITL.js} +4 -4
  62. package/dist/{chunk-QOTI55H7.js.map → chunk-SXGE7ITL.js.map} +1 -1
  63. package/dist/{chunk-RGP33ZJ5.js → chunk-VQEXF5D4.js} +7 -7
  64. package/dist/{chunk-RGP33ZJ5.js.map → chunk-VQEXF5D4.js.map} +1 -1
  65. package/dist/{chunk-MP2JQKSG.js → chunk-WI7ISNQF.js} +17 -3
  66. package/dist/chunk-WI7ISNQF.js.map +1 -0
  67. package/dist/{chunk-4TOD4LBZ.cjs → chunk-X4JBT642.cjs} +29 -29
  68. package/dist/{chunk-4TOD4LBZ.cjs.map → chunk-X4JBT642.cjs.map} +1 -1
  69. package/dist/{chunk-EMF62TZG.cjs → chunk-ZAMFOJ5P.cjs} +86 -86
  70. package/dist/{chunk-EMF62TZG.cjs.map → chunk-ZAMFOJ5P.cjs.map} +1 -1
  71. package/dist/{chunk-VSPCZAXF.js → chunk-ZIGXMKQQ.js} +3 -3
  72. package/dist/{chunk-VSPCZAXF.js.map → chunk-ZIGXMKQQ.js.map} +1 -1
  73. package/dist/{chunk-XOX22WHL.cjs → chunk-ZUNQ5BEP.cjs} +104 -71
  74. package/dist/chunk-ZUNQ5BEP.cjs.map +1 -0
  75. package/dist/graph-extension/index.cjs +19 -19
  76. package/dist/graph-extension/index.d.cts +1 -1
  77. package/dist/graph-extension/index.d.ts +1 -1
  78. package/dist/graph-extension/index.js +4 -4
  79. package/dist/graph-merge/index.cjs +82 -82
  80. package/dist/graph-merge/index.cjs.map +1 -1
  81. package/dist/graph-merge/index.d.cts +2 -2
  82. package/dist/graph-merge/index.d.ts +2 -2
  83. package/dist/graph-merge/index.js +13 -13
  84. package/dist/graph-merge/index.js.map +1 -1
  85. package/dist/{index-6CSUAq-x.d.ts → index-BFvdmBPV.d.ts} +68 -1
  86. package/dist/{index-Bks_3HHK.d.cts → index-BLtQWKRj.d.cts} +68 -1
  87. package/dist/index.cjs +144 -136
  88. package/dist/index.d.cts +3 -3
  89. package/dist/index.d.ts +3 -3
  90. package/dist/index.js +14 -14
  91. package/dist/interchange/index.cjs +27 -26
  92. package/dist/interchange/index.d.cts +1 -1
  93. package/dist/interchange/index.d.ts +1 -1
  94. package/dist/interchange/index.js +9 -8
  95. package/dist/profiler/index.d.cts +1 -1
  96. package/dist/profiler/index.d.ts +1 -1
  97. package/dist/provenance/index.cjs +31 -31
  98. package/dist/provenance/index.d.cts +1 -1
  99. package/dist/provenance/index.d.ts +1 -1
  100. package/dist/provenance/index.js +4 -4
  101. package/dist/schema/index.cjs +31 -31
  102. package/dist/schema/index.d.cts +1 -1
  103. package/dist/schema/index.d.ts +1 -1
  104. package/dist/schema/index.js +7 -7
  105. package/dist/{store-view-BSpqvAxK.d.cts → store-view-9KDOpZzF.d.cts} +250 -36
  106. package/dist/{store-view-CKbHFRnn.d.ts → store-view-DplHC3Nt.d.ts} +250 -36
  107. package/package.json +1 -1
  108. package/dist/chunk-CZQ3FO2Q.js.map +0 -1
  109. package/dist/chunk-GUR2FNB5.cjs +0 -12
  110. package/dist/chunk-J7WFGBJR.cjs.map +0 -1
  111. package/dist/chunk-LGEALRC6.cjs.map +0 -1
  112. package/dist/chunk-MP2JQKSG.js.map +0 -1
  113. package/dist/chunk-QNKSQTKQ.js.map +0 -1
  114. package/dist/chunk-UES55P7G.js.map +0 -1
  115. package/dist/chunk-WJT7OU7E.cjs.map +0 -1
  116. package/dist/chunk-XOX22WHL.cjs.map +0 -1
@@ -1290,6 +1290,49 @@ type BaseStoreOptions = Readonly<{
1290
1290
  * manually after commit. `importGraph` handles its own refresh.
1291
1291
  */
1292
1292
  autoRefreshStatistics?: false | number;
1293
+ /**
1294
+ * Skip the write for an `upsertById` (or `bulkUpsertById` item) whose
1295
+ * validated props are value-identical to the existing live row. Default
1296
+ * off.
1297
+ *
1298
+ * Enable this for at-least-once / replay materializers. An event log that
1299
+ * re-delivers a byte-identical change, or a full replay that reprocesses an
1300
+ * already-materialized log, would otherwise rewrite every row it touches:
1301
+ * today an `upsertById` on an existing id calls `updateNode`
1302
+ * unconditionally, allocating a fresh recorded instant and a new history row
1303
+ * per re-delivery. A rebuild — the workload replay is recommended for — is
1304
+ * the one that inflates recorded history most, with a dense band of
1305
+ * "changes" that changed nothing.
1306
+ *
1307
+ * When enabled, an upsert that would not change the stored value performs
1308
+ * **no write at all**: no `updateNode`, no recorded-time capture, no history
1309
+ * row, no revision-anchor advance, and no `update` operation hooks (nothing
1310
+ * happened, so nothing is reported). It resolves with the **existing** node,
1311
+ * preserving its original `validFrom` / `updatedAt` / `version`.
1312
+ *
1313
+ * Receipt shape is unchanged and needs no new signal: a coalesced upsert
1314
+ * still counts as one write intent (`writes.total` includes it), but
1315
+ * captures nothing (`receipt.recorded` stays `undefined` when it is the only
1316
+ * write) — the same shape a no-op delete already produces, so a consumer
1317
+ * that carries the prior anchor forward on `recorded === undefined` handles
1318
+ * it unchanged.
1319
+ *
1320
+ * A write is coalesced only when **all** of the following hold; otherwise the
1321
+ * normal write happens:
1322
+ * 1. An existing row is found for the id (else it is a create).
1323
+ * 2. That row is not soft-deleted (a deleted row resurrects — a real
1324
+ * change — and is never coalesced).
1325
+ * 3. The caller passed no explicit `validFrom` / `validTo` (an explicit
1326
+ * temporal override is a deliberate request and is never coalesced;
1327
+ * applied per item in the bulk path).
1328
+ * 4. The new props, merged over the stored props and run through the
1329
+ * kind's Zod schema (defaults applied, values normalized), are deeply
1330
+ * value-identical to the stored props (key order aside).
1331
+ *
1332
+ * Default-off because some consumers *want* an audit row per re-delivery as
1333
+ * proof the event was reprocessed; coalescing removes that signal.
1334
+ */
1335
+ coalesceUnchangedUpserts?: boolean;
1293
1336
  /** SQL schema configuration from createSqlSchema(...) for custom table names */
1294
1337
  schema?: SqlSchema;
1295
1338
  /** Query default behaviors. */
@@ -1350,7 +1393,9 @@ interface StoreRef<T> {
1350
1393
  current: T;
1351
1394
  }
1352
1395
  /**
1353
- * Summary returned by `store.transactionWithReceipt(fn)`.
1396
+ * Result plus write summary. Returned by `store.transactionWithReceipt(fn)`,
1397
+ * `store.withRecordedTransaction(externalTx, fn)` (the adopted-commit path), and
1398
+ * `tx.measure(fn)` (a scoped sub-receipt). See {@link TransactionReceipt}.
1354
1399
  */
1355
1400
  type TransactionOutcome<T> = Readonly<{
1356
1401
  result: T;
@@ -1394,7 +1439,10 @@ type TransactionReceipt = Readonly<{
1394
1439
  /**
1395
1440
  * The recorded commit instant allocated for this store's graph by this
1396
1441
  * transaction. Undefined when history capture is off, the transaction is
1397
- * read-only, or no captured writes were flushed.
1442
+ * read-only, or no captured writes were flushed. **Always undefined on a
1443
+ * scoped receipt from {@link ScopedMeasure}** (`tx.measure`) — the recorded
1444
+ * instant is a per-transaction flush concern allocated once when the whole
1445
+ * transaction's capture flushes, unknowable mid-transaction.
1398
1446
  */
1399
1447
  recorded?: RecordedInstant;
1400
1448
  }>;
@@ -2032,6 +2080,14 @@ type RecordedStoreViewNodeCollections<G extends GraphDef> = {
2032
2080
  type RecordedStoreViewEdgeCollections<G extends GraphDef> = {
2033
2081
  [K in keyof G["edges"] & string]-?: TypedRecordedStoreViewEdgeCollection<G["edges"][K]>;
2034
2082
  };
2083
+ /**
2084
+ * Whether — and why — `tx.sql` can be used on a transaction context. A single
2085
+ * required discriminant covering exactly the four states raw-SQL access can be
2086
+ * in, so a portable caller branches on capability instead of truthiness-testing
2087
+ * `tx.sql` (which is present-but-throwing under history / revision tracking).
2088
+ * See {@link TransactionContext.sqlAvailability} for the per-value semantics.
2089
+ */
2090
+ type SqlAvailability = "available" | "history" | "revisionTracking" | "unavailable";
2035
2091
  /**
2036
2092
  * A typed transaction context with collection API.
2037
2093
  *
@@ -2074,11 +2130,43 @@ type RecordedStoreViewEdgeCollections<G extends GraphDef> = {
2074
2130
  * Drizzle database type at the call site.
2075
2131
  *
2076
2132
  * When the store was created with `{ history: true }` or
2077
- * `{ revisionTracking: true }`, `tx.sql` is present but replaced by a fail-loud
2078
- * guard because raw SQL would bypass recorded-time capture or the revision
2079
- * anchor. Use the typed `tx.nodes` / `tx.edges` collections inside
2080
- * `transaction()`, or use `store.withRecordedTransaction(externalTx, fn)` when
2081
- * the relational layer owns the transaction boundary.
2133
+ * `{ revisionTracking: true }`, `tx.sql` is **present but throwing**: every
2134
+ * access raises {@link ConfigurationError} because raw SQL would bypass
2135
+ * recorded-time capture or the revision anchor. The static type marks it
2136
+ * `sql?: never` so a mistaken `tx.sql.run(...)` fails to typecheck; at runtime
2137
+ * it is a fail-loud guard, not `undefined`. Use the typed `tx.nodes` /
2138
+ * `tx.edges` collections inside `transaction()`, or
2139
+ * `store.withRecordedTransaction(externalTx, fn)` when the relational layer
2140
+ * owns the transaction boundary.
2141
+ *
2142
+ * **Feature detection — branch on {@link sqlAvailability}, not on `tx.sql`'s
2143
+ * truthiness.** Because `tx.sql` is present-but-throwing under history or
2144
+ * revision tracking, `if (tx.sql)` reads truthy and then throws. The honest
2145
+ * discriminant is `tx.sqlAvailability`:
2146
+ *
2147
+ * ```typescript
2148
+ * await store.transaction(async (tx) => {
2149
+ * switch (tx.sqlAvailability) {
2150
+ * case "available": {
2151
+ * const sqlTx = tx.sql as NodePgDatabase; // usable raw handle
2152
+ * await sqlTx.insert(rows).values(row);
2153
+ * break;
2154
+ * }
2155
+ * case "unavailable":
2156
+ * // non-transactional backend: tx.sql === undefined, no atomicity
2157
+ * await fallbackWithoutAtomicity();
2158
+ * break;
2159
+ * case "history":
2160
+ * case "revisionTracking":
2161
+ * // raw SQL disabled here — write your own tables through the external
2162
+ * // handle passed to store.withRecordedTransaction instead
2163
+ * break;
2164
+ * }
2165
+ * });
2166
+ * ```
2167
+ *
2168
+ * `tx.sql === undefined` still means exactly "non-transactional fallback"
2169
+ * (`sqlAvailability === "unavailable"`).
2082
2170
  *
2083
2171
  * @example
2084
2172
  * ```typescript
@@ -2093,6 +2181,26 @@ type TransactionContext<G extends GraphDef> = Readonly<{
2093
2181
  nodes: GraphNodeCollections<G>;
2094
2182
  edges: GraphEdgeCollections<G>;
2095
2183
  sql?: AdoptedTransaction;
2184
+ /**
2185
+ * Honest capability discriminant for `tx.sql` on this transaction. Prefer
2186
+ * this over truthiness-testing `tx.sql`, which is present-but-throwing under
2187
+ * history capture or revision tracking.
2188
+ *
2189
+ * - `"available"` — `tx.sql` is the usable raw handle bound to this
2190
+ * transaction.
2191
+ * - `"history"` — history capture is on; `tx.sql` is present but every access
2192
+ * throws {@link ConfigurationError} (`details.code`
2193
+ * `RECORDED_CAPTURE_RAW_SQL_DISABLED`). Use `tx.nodes` / `tx.edges`, or
2194
+ * `store.withRecordedTransaction` with your own external handle for
2195
+ * cross-store atomicity.
2196
+ * - `"revisionTracking"` — same shape as `"history"` (`details.code`
2197
+ * `REVISION_TRACKING_RAW_SQL_DISABLED`).
2198
+ * - `"unavailable"` — non-transactional fallback
2199
+ * (`backend.capabilities.transactions === false`, e.g. Cloudflare D1,
2200
+ * `drizzle-orm/neon-http`); `tx.sql` is `undefined` and there is no
2201
+ * atomicity.
2202
+ */
2203
+ sqlAvailability: SqlAvailability;
2096
2204
  /**
2097
2205
  * The transaction-scoped backend the collections are bound to, for advanced
2098
2206
  * raw reads that must observe the transaction's snapshot (e.g. graph-merge's
@@ -2116,6 +2224,40 @@ type TransactionContext<G extends GraphDef> = Readonly<{
2116
2224
  */
2117
2225
  runNodeOperationHooks<T>(operation: "create" | "update" | "delete", kind: string, id: string, fn: () => Promise<T>): Promise<T>;
2118
2226
  }>;
2227
+ /**
2228
+ * Scoped write measurement, available only on the receipt-enabled transaction
2229
+ * contexts (`transactionWithReceipt`, `withRecordedTransaction`). Runs `fn`,
2230
+ * passing it a **scoped context** — a second view over the same transaction — and
2231
+ * returns a {@link TransactionOutcome} whose receipt counts exactly the writes
2232
+ * made *through that scoped context* (`scoped.nodes` / `scoped.edges`).
2233
+ *
2234
+ * Attribution is by **which context you write through**, not by timing. A write
2235
+ * through the scoped context counts in both the scope and the outer receipt (it
2236
+ * happened in the transaction); a write through the outer `tx` during the scope
2237
+ * counts only in the outer receipt. This makes overlapping and concurrent
2238
+ * measures safe by construction — two scopes running under `Promise.all`, each
2239
+ * writing through its own scoped context, never cross-count. Nesting composes:
2240
+ * `scoped.measure(...)` opens a child scope that counts in itself, every
2241
+ * ancestor scope, and the outer receipt.
2242
+ *
2243
+ * Counts otherwise inherit {@link TransactionReceipt} (bulk by input length, a
2244
+ * rejected write counts 0). The returned receipt's `recorded` is **always
2245
+ * `undefined`**: the recorded commit instant is a per-transaction flush concern,
2246
+ * unknowable mid-transaction.
2247
+ */
2248
+ type ScopedMeasure<Context> = <T>(fn: (scoped: Context) => Promise<T>) => Promise<TransactionOutcome<T>>;
2249
+ /**
2250
+ * A {@link TransactionContext} that also exposes {@link ScopedMeasure}. Only the
2251
+ * receipt-enabled entry points (`transactionWithReceipt`,
2252
+ * `withRecordedTransaction`) hand a callback this type; plain `transaction()`
2253
+ * contexts have no recorder and therefore no `measure`, keeping that path
2254
+ * zero-overhead. Assignable to {@link TransactionContext}, so a projector helper
2255
+ * typed `(tx: TransactionContext<G>) => ...` accepts a measurable context. The
2256
+ * scoped context handed to `measure` is itself measurable, so scopes nest.
2257
+ */
2258
+ type MeasurableTransactionContext<G extends GraphDef> = TransactionContext<G> & Readonly<{
2259
+ measure: ScopedMeasure<MeasurableTransactionContext<G>>;
2260
+ }>;
2119
2261
  /**
2120
2262
  * Replace branded `NodeId` / `EdgeId` with plain `string` in each
2121
2263
  * method's parameter list. Return types are preserved unchanged.
@@ -4277,13 +4419,16 @@ declare class Store<G extends GraphDef> {
4277
4419
  * transaction**, for writing the caller's own relational tables in
4278
4420
  * the same atomic boundary (the graph-owned counterpart of
4279
4421
  * {@link Store.withTransaction}). See {@link TransactionContext}
4280
- * for per-backend semantics; it is `undefined` only on the
4281
- * non-transactional fallback.
4282
- * When the store was created with `{ history: true }` or
4283
- * `{ revisionTracking: true }`, `tx.sql` is replaced by a fail-loud guard
4284
- * because raw SQL would bypass recorded-time capture or revision tracking;
4285
- * use the typed collections inside
4286
- * `transaction()` or {@link Store.withRecordedTransaction}.
4422
+ * for per-backend semantics. To decide at runtime whether `tx.sql` is
4423
+ * usable, branch on `tx.sqlAvailability` rather than truthiness-testing
4424
+ * `tx.sql` — under history capture or revision tracking `tx.sql` is
4425
+ * present-but-throwing (`sqlAvailability` is `"history"` /
4426
+ * `"revisionTracking"`), and it is `undefined` only on the non-transactional
4427
+ * fallback (`sqlAvailability === "unavailable"`). When it reports
4428
+ * `"history"` / `"revisionTracking"`, raw SQL is disabled because it would
4429
+ * bypass recorded-time capture or the revision anchor; use the typed
4430
+ * collections inside `transaction()` or
4431
+ * {@link Store.withRecordedTransaction}.
4287
4432
  *
4288
4433
  * @example
4289
4434
  * ```typescript
@@ -4355,11 +4500,12 @@ declare class Store<G extends GraphDef> {
4355
4500
  *
4356
4501
  * The receipt does not count writes that bypass the `tx.nodes.*` /
4357
4502
  * `tx.edges.*` collections, such as direct backend writes, raw SQL, or
4358
- * import helpers. Adopted transactions (`withTransaction` /
4359
- * `withRecordedTransaction`) do not produce receipts in this version
4360
- * because their commit belongs to the caller. On non-transactional
4361
- * backends, the receipt is still returned, but it describes operations
4362
- * that individually committed rather than one atomic commit; if the
4503
+ * import helpers. The adopted-commit sibling
4504
+ * {@link Store.withRecordedTransaction} also returns a
4505
+ * {@link TransactionOutcome}; only `withTransaction` (whose commit belongs
4506
+ * entirely to the caller with no flush point) produces no receipt. On
4507
+ * non-transactional backends, the receipt is still returned, but it describes
4508
+ * operations that individually committed rather than one atomic commit; if the
4363
4509
  * callback rejects on such a backend, no receipt is returned even though
4364
4510
  * earlier operations committed individually.
4365
4511
  *
@@ -4367,6 +4513,12 @@ declare class Store<G extends GraphDef> {
4367
4513
  * recorded commit instant this transaction allocated for the store's
4368
4514
  * graph, or `undefined` when nothing was captured.
4369
4515
  *
4516
+ * The callback receives a {@link MeasurableTransactionContext}: call
4517
+ * `tx.measure((scoped) => ...)` to scope a sub-receipt to the writes made
4518
+ * through the `scoped` context (e.g. to attribute writes to user code the
4519
+ * caller invokes, excluding its own bookkeeping written through `tx`). See
4520
+ * {@link MeasurableTransactionContext} for semantics.
4521
+ *
4370
4522
  * @example
4371
4523
  * ```typescript
4372
4524
  * const outcome = await store.transactionWithReceipt(async (tx) => {
@@ -4377,8 +4529,8 @@ declare class Store<G extends GraphDef> {
4377
4529
  * outcome.receipt.writes; // { nodes: { Person: 1 }, edges: {}, total: 1 }
4378
4530
  * ```
4379
4531
  */
4380
- transactionWithReceipt<T>(this: HistoryStore<G>, fn: (tx: HistoryTransactionContext<G>) => Promise<T>, options?: TransactionOptions): Promise<TransactionOutcome<T>>;
4381
- transactionWithReceipt<T>(fn: (tx: TransactionContext<G>) => Promise<T>, options?: TransactionOptions): Promise<TransactionOutcome<T>>;
4532
+ transactionWithReceipt<T>(this: HistoryStore<G>, fn: (tx: MeasurableHistoryTransactionContext<G>) => Promise<T>, options?: TransactionOptions): Promise<TransactionOutcome<T>>;
4533
+ transactionWithReceipt<T>(fn: (tx: MeasurableTransactionContext<G>) => Promise<T>, options?: TransactionOptions): Promise<TransactionOutcome<T>>;
4382
4534
  /**
4383
4535
  * Adopts a caller-owned, already-open Drizzle transaction so the graph
4384
4536
  * store and the caller's relational writes commit or roll back as one
@@ -4431,11 +4583,14 @@ declare class Store<G extends GraphDef> {
4431
4583
  *
4432
4584
  * Not available when the store was created with `{ history: true }`: a
4433
4585
  * caller-owned transaction context would let writes happen after capture has
4434
- * lost its flush point, so this throws `ConfigurationError`
4435
- * (`RECORDED_CAPTURE_REQUIRES_CALLBACK_TRANSACTION`). Use
4436
- * {@link Store.withRecordedTransaction} on a history-enabled store, which
4437
- * adopts the same external transaction but flushes recorded-time capture
4438
- * before the caller commits.
4586
+ * lost its flush point. On a {@link HistoryStore} this is a **compile error**
4587
+ * at the call site (the `externalTx` argument is rejected against a message
4588
+ * type naming {@link Store.withRecordedTransaction}); the runtime guard still
4589
+ * throws `ConfigurationError`
4590
+ * (`RECORDED_CAPTURE_REQUIRES_CALLBACK_TRANSACTION`) if the check is
4591
+ * suppressed. Use {@link Store.withRecordedTransaction} on a history-enabled
4592
+ * store, which adopts the same external transaction but flushes recorded-time
4593
+ * capture before the caller commits.
4439
4594
  *
4440
4595
  * @throws {ConfigurationError} when the store has history capture enabled
4441
4596
  * (use {@link Store.withRecordedTransaction} instead), or when the backend
@@ -4451,7 +4606,7 @@ declare class Store<G extends GraphDef> {
4451
4606
  * `createStoreWithSchema`). Rolls back the caller's transaction
4452
4607
  * without emitting any DDL.
4453
4608
  */
4454
- withTransaction(externalTx: AdoptedTransaction): TransactionContext<G>;
4609
+ withTransaction<This extends Store<G>>(this: This, externalTx: This extends HistoryStore<G> ? HistoryWithTransactionUnavailable : AdoptedTransaction): This extends HistoryStore<G> ? never : TransactionContext<G>;
4455
4610
  /**
4456
4611
  * Adopts a caller-owned transaction while giving recorded-time capture a
4457
4612
  * flush point before the caller commits. Required when `history: true` is
@@ -4462,16 +4617,55 @@ declare class Store<G extends GraphDef> {
4462
4617
  * session, so a graph write made through the transaction context *after* `fn`
4463
4618
  * returns (e.g. retaining `tx` and writing once more before the caller's
4464
4619
  * COMMIT) throws rather than committing uncaptured — the post-flush write
4465
- * cannot silently diverge history from live state.
4620
+ * cannot silently diverge history from live state. This sealing behavior is
4621
+ * unchanged by the receipt return.
4622
+ *
4623
+ * Returns a {@link TransactionOutcome} — the adopted path is the only way to
4624
+ * get exactly-once cursors and graph writes atomically on a history store, so
4625
+ * it surfaces the same receipt `transactionWithReceipt` does: `receipt.writes`
4626
+ * for drop detection (a non-delete change that wrote nothing) and
4627
+ * `receipt.recorded` as the per-transaction replay anchor. Destructure
4628
+ * `{ result, receipt }`:
4629
+ *
4630
+ * ```typescript
4631
+ * const { result, receipt } = await store.withRecordedTransaction(
4632
+ * externalTx,
4633
+ * async (tx) => tx.nodes.Document.update(documentId, props),
4634
+ * );
4635
+ * ```
4636
+ *
4637
+ * `receipt.recorded` is the recorded commit instant this transaction allocated
4638
+ * for the store's graph at the flush point (when `fn` resolved), and is
4639
+ * `undefined` when nothing was captured — a read-only `fn`, or a non-history
4640
+ * store (where the receipt still counts write intents but there is no recorded
4641
+ * time). To attribute writes when `fn` invokes user code that also
4642
+ * bookkeeps, use `tx.measure((scoped) => ...)` and have that code write
4643
+ * through the `scoped` context (see {@link MeasurableTransactionContext}).
4644
+ *
4645
+ * Write your own relational tables through the **external transaction handle
4646
+ * you passed in** (`externalTx`) — it *is* the pinned connection. `tx.sql` is
4647
+ * unavailable here (it is a fail-loud guard under history capture, since raw
4648
+ * SQL bypasses recorded-time capture, and `HistoryTransactionContext` types it
4649
+ * `sql?: never`). Writing through `externalTx` is the sanctioned way to get
4650
+ * cross-store atomicity on a history-enabled store:
4466
4651
  *
4467
- * Like {@link Store.withTransaction}, the graph writes and any raw `tx.sql`
4468
- * statements share the caller's one pinned connection. TypeGraph serializes
4469
- * the statements its collections issue; sequence your own raw statements (and
4470
- * don't `Promise.all` them with graph writes) so two queries never race on
4471
- * that connection.
4652
+ * ```typescript
4653
+ * // Async driver (Postgres / libsql):
4654
+ * await db.transaction(async (pgTx) => {
4655
+ * const { receipt } = await store.withRecordedTransaction(pgTx, async (tx) => {
4656
+ * await tx.nodes.Document.update(documentId, props); // graph write
4657
+ * });
4658
+ * await pgTx.insert(streamCursors).values(cursorRow); // your own table
4659
+ * }); // one COMMIT / ROLLBACK across both layers
4660
+ * ```
4661
+ *
4662
+ * The graph writes and your `externalTx` statements share the caller's one
4663
+ * pinned connection. TypeGraph serializes the statements its collections
4664
+ * issue; sequence your own raw statements (and don't `Promise.all` them with
4665
+ * graph writes) so two queries never race on that connection.
4472
4666
  */
4473
- withRecordedTransaction<T>(this: HistoryStore<G>, externalTx: AdoptedTransaction, fn: (tx: HistoryTransactionContext<G>) => Promise<T>): Promise<T>;
4474
- withRecordedTransaction<T>(externalTx: AdoptedTransaction, fn: (tx: TransactionContext<G>) => Promise<T>): Promise<T>;
4667
+ withRecordedTransaction<T>(this: HistoryStore<G>, externalTx: AdoptedTransaction, fn: (tx: MeasurableHistoryTransactionContext<G>) => Promise<T>): Promise<TransactionOutcome<T>>;
4668
+ withRecordedTransaction<T>(externalTx: AdoptedTransaction, fn: (tx: MeasurableTransactionContext<G>) => Promise<T>): Promise<TransactionOutcome<T>>;
4475
4669
  /**
4476
4670
  * Hard-deletes all data for this graph from the database.
4477
4671
  *
@@ -4729,6 +4923,17 @@ type HistoryTransactionContext<G extends GraphDef> = Omit<TransactionContext<G>,
4729
4923
  backend: HistorySafeTransactionBackend;
4730
4924
  sql?: never;
4731
4925
  }>;
4926
+ /**
4927
+ * The {@link HistoryTransactionContext} handed to a `withRecordedTransaction`
4928
+ * callback, extended with {@link ScopedMeasure}. Its `measure` scopes to a child
4929
+ * {@link MeasurableHistoryTransactionContext} (so nested scopes keep the
4930
+ * history-safe `sql?: never` / backend typing). Assignable to
4931
+ * {@link MeasurableTransactionContext} (and hence to {@link TransactionContext}),
4932
+ * so a projector helper typed against either still accepts it.
4933
+ */
4934
+ type MeasurableHistoryTransactionContext<G extends GraphDef> = HistoryTransactionContext<G> & Readonly<{
4935
+ measure: ScopedMeasure<MeasurableHistoryTransactionContext<G>>;
4936
+ }>;
4732
4937
  type RecordedReadStore<G extends GraphDef> = Store<G> & Readonly<{
4733
4938
  recordedReadBound: true;
4734
4939
  }>;
@@ -4737,6 +4942,15 @@ type HistoryStore<G extends GraphDef> = Store<G> & Readonly<{
4737
4942
  historyEnabled: true;
4738
4943
  recordedReadBound: true;
4739
4944
  }>;
4945
+ /**
4946
+ * The `externalTx` parameter type {@link Store.withTransaction} resolves to when
4947
+ * called on a {@link HistoryStore}. A real {@link AdoptedTransaction} is not
4948
+ * assignable to this string literal, so the call is a compile error whose
4949
+ * message names the working alternative. `HistoryStore` cannot be excluded from
4950
+ * the general signature by a `this` constraint alone — it is a subtype of
4951
+ * `Store<G>` — so the block is expressed on the argument instead.
4952
+ */
4953
+ type HistoryWithTransactionUnavailable = "withTransaction() is unavailable on a history-enabled store — use store.withRecordedTransaction() so recorded-time capture can flush before the caller commits";
4740
4954
  type ExplicitRecordedReadLiveOptions = LiveStoreOptions & Readonly<{
4741
4955
  recordedRead: NonNullable<LiveStoreOptions["recordedRead"]>;
4742
4956
  }>;
@@ -4987,4 +5201,4 @@ declare class RecordedStoreView<G extends GraphDef> extends CoordinatePinnedView
4987
5201
  get edges(): RecordedStoreViewEdgeCollections<G>;
4988
5202
  }
4989
5203
 
4990
- export { type GetOrCreateAction as $, type AliasMap as A, type BaseTraversalOptions as B, type ConstraintNames as C, DEFAULT_SEARCHABLE_LANGUAGE as D, type EmbeddingMetric as E, type EdgeFindByEndpointsOptions as F, type EdgeGetOrCreateByEndpointsOptions as G, type HistoryStore as H, type EdgeGetOrCreateByEndpointsResult as I, type EdgeIntrospection as J, type EdgeTemporalReads as K, type EdgeWrites as L, type EmbeddingSchema as M, type Node as N, type EmbeddingValue as O, type EmptyAliasMap as P, type QueryCoordinateState as Q, type RecursiveAliasMap as R, Store as S, TraversalBuilder as T, type UniqueIntrospection as U, type EmptyEdgeAliasMap as V, ExecutableAggregateQuery as W, ExecutableQuery as X, type FieldAccessor as Y, type FulltextSearchHit as Z, type FulltextSearchOptions as _, type EmbeddingIndexType as a, type SelectContext as a$, type GraphAlgorithms as a0, type GraphEdgeCollections as a1, type GraphNodeCollections as a2, type HistorySafeBackend as a3, type HistorySafeTransactionBackend as a4, type HistoryStoreOptions as a5, type HistoryTransactionContext as a6, type HookContext as a7, type HybridFulltextOptions as a8, type HybridSearchHit as a9, type PathNode as aA, type Predicate as aB, PreparedQuery as aC, type PropsAccessor as aD, type QueryHookContext as aE, type QueryOptions as aF, type ReachableNode as aG, type ReachableOptions as aH, type RebuildFulltextOptions as aI, type RebuildFulltextResult as aJ, type ReclaimedVectorFieldEntry as aK, type RecordedInstant as aL, type RecordedReadStore as aM, RecordedStoreView as aN, type RecordedStoreViewEdgeCollection as aO, type RecordedStoreViewEdgeCollections as aP, type RecordedStoreViewNodeCollection as aQ, type RecordedStoreViewNodeCollections as aR, type RecursiveTraversalOptions as aS, type ReembedFunction as aT, type ReembedVectorFieldOptions as aU, type ReembedVectorFieldResult as aV, type SchemaIntrospection as aW, type SearchScopeOptions as aX, type SearchableMetadata as aY, type SearchableOptions as aZ, type SearchableSchema as a_, type HybridSearchOptions as aa, type HybridVectorOptions as ab, type IfExistsMode as ac, type InitialQueryBuilder as ad, type KindIntrospection as ae, type LiveStoreOptions as af, type MaterializeRemovalsEntry as ag, type MaterializeRemovalsOptions as ah, type MaterializeRemovalsResult as ai, type NeighborsOptions as aj, type NoRecordedCoordinate as ak, type NodeAccessor as al, type NodeAlias as am, type NodeBulkFindByIndexOptions as an, type NodeCollection as ao, type NodeCurrentReads as ap, type NodeGetOrCreateByConstraintOptions as aq, type NodeGetOrCreateByConstraintResult as ar, type NodeIdentifier as as, type NodeRef as at, type NodeTemporalReads as au, type NodeWrites as av, type OntologyIntrospection as aw, type OperationHookContext as ax, type PaginateOptions as ay, type PaginatedResult as az, type EdgeAliasMap as b, type SelectableEdge as b0, type SelectableNode as b1, type ShortestPathOptions as b2, type ShortestPathResult as b3, type StoreHooks as b4, type StoreOptions as b5, type StoreProjection as b6, type StoreRef as b7, StoreSearch as b8, StoreView as b9, UnionableQuery as bA, type UpdateEdgeInput as bB, type UpdateNodeInput as bC, type VectorSearchHit as bD, type VectorSearchOptions as bE, asRecordedInstant as bF, createQueryBuilder as bG, createStore as bH, createStoreWithSchema as bI, createVerifiedStore as bJ, defineSubgraphProject as bK, embedding as bL, exists as bM, fieldRef as bN, getEmbeddingDimensions as bO, getSearchableMetadata as bP, inSubquery as bQ, isEmbeddingSchema as bR, isParameterRef as bS, isSearchableSchema as bT, notExists as bU, notInSubquery as bV, param as bW, searchable as bX, type StoreViewCanReachOptions as ba, type StoreViewCoordinate as bb, type StoreViewDegreeOptions as bc, type StoreViewEdgeCollection as bd, type StoreViewEdgeCollections as be, type StoreViewNeighborsOptions as bf, type StoreViewNodeCollection as bg, type StoreViewNodeCollections as bh, type StoreViewReachableOptions as bi, type StoreViewShortestPathOptions as bj, type StoreViewSubgraphOptions as bk, type StreamOptions as bl, type SubgraphEdgeResult as bm, type SubgraphNodeResult as bn, type SubgraphOptions as bo, type SubgraphResult as bp, type SubsetEdge as bq, type SubsetNode as br, type TemporalAlgorithmOptions as bs, type TransactionContext as bt, type TransactionOutcome as bu, type TransactionReceipt as bv, type TraversalDirection as bw, type TypedEdgeCollection as bx, type TypedRecordedStoreViewEdgeCollection as by, type TypedStoreViewEdgeCollection as bz, QueryBuilder as c, type EmptyRecursiveAliasMap as d, type AggregateResult as e, type AlgorithmCyclePolicy as f, type AnyEdge as g, type AnyNode as h, type BatchResults as i, type BatchableQuery as j, type CreateEdgeInput as k, type CreateNodeInput as l, type DegreeOptions as m, type DynamicEdgeAccessor as n, type DynamicEdgeCollection as o, type DynamicEdgeType as p, type DynamicFieldBuilder as q, type DynamicNodeAccessor as r, type DynamicNodeCollection as s, type DynamicNodeType as t, type DynamicSelectableEdge as u, type DynamicSelectableNode as v, type Edge as w, type EdgeAccessor as x, type EdgeBatchReads as y, type EdgeCollection as z };
5204
+ export { type GetOrCreateAction as $, type AliasMap as A, type BaseTraversalOptions as B, type ConstraintNames as C, DEFAULT_SEARCHABLE_LANGUAGE as D, type EmbeddingMetric as E, type EdgeFindByEndpointsOptions as F, type EdgeGetOrCreateByEndpointsOptions as G, type HistoryStore as H, type EdgeGetOrCreateByEndpointsResult as I, type EdgeIntrospection as J, type EdgeTemporalReads as K, type EdgeWrites as L, type EmbeddingSchema as M, type Node as N, type EmbeddingValue as O, type EmptyAliasMap as P, type QueryCoordinateState as Q, type RecursiveAliasMap as R, Store as S, TraversalBuilder as T, type UniqueIntrospection as U, type EmptyEdgeAliasMap as V, ExecutableAggregateQuery as W, ExecutableQuery as X, type FieldAccessor as Y, type FulltextSearchHit as Z, type FulltextSearchOptions as _, type EmbeddingIndexType as a, type SearchScopeOptions as a$, type GraphAlgorithms as a0, type GraphEdgeCollections as a1, type GraphNodeCollections as a2, type HistorySafeBackend as a3, type HistorySafeTransactionBackend as a4, type HistoryStoreOptions as a5, type HistoryTransactionContext as a6, type HistoryWithTransactionUnavailable as a7, type HookContext as a8, type HybridFulltextOptions as a9, type OperationHookContext as aA, type PaginateOptions as aB, type PaginatedResult as aC, type PathNode as aD, type Predicate as aE, PreparedQuery as aF, type PropsAccessor as aG, type QueryHookContext as aH, type QueryOptions as aI, type ReachableNode as aJ, type ReachableOptions as aK, type RebuildFulltextOptions as aL, type RebuildFulltextResult as aM, type ReclaimedVectorFieldEntry as aN, type RecordedInstant as aO, type RecordedReadStore as aP, RecordedStoreView as aQ, type RecordedStoreViewEdgeCollection as aR, type RecordedStoreViewEdgeCollections as aS, type RecordedStoreViewNodeCollection as aT, type RecordedStoreViewNodeCollections as aU, type RecursiveTraversalOptions as aV, type ReembedFunction as aW, type ReembedVectorFieldOptions as aX, type ReembedVectorFieldResult as aY, type SchemaIntrospection as aZ, type ScopedMeasure as a_, type HybridSearchHit as aa, type HybridSearchOptions as ab, type HybridVectorOptions as ac, type IfExistsMode as ad, type InitialQueryBuilder as ae, type KindIntrospection as af, type LiveStoreOptions as ag, type MaterializeRemovalsEntry as ah, type MaterializeRemovalsOptions as ai, type MaterializeRemovalsResult as aj, type MeasurableHistoryTransactionContext as ak, type MeasurableTransactionContext as al, type NeighborsOptions as am, type NoRecordedCoordinate as an, type NodeAccessor as ao, type NodeAlias as ap, type NodeBulkFindByIndexOptions as aq, type NodeCollection as ar, type NodeCurrentReads as as, type NodeGetOrCreateByConstraintOptions as at, type NodeGetOrCreateByConstraintResult as au, type NodeIdentifier as av, type NodeRef as aw, type NodeTemporalReads as ax, type NodeWrites as ay, type OntologyIntrospection as az, type EdgeAliasMap as b, param as b$, type SearchableMetadata as b0, type SearchableOptions as b1, type SearchableSchema as b2, type SelectContext as b3, type SelectableEdge as b4, type SelectableNode as b5, type ShortestPathOptions as b6, type ShortestPathResult as b7, type SqlAvailability as b8, type StoreHooks as b9, type TransactionReceipt as bA, type TraversalDirection as bB, type TypedEdgeCollection as bC, type TypedRecordedStoreViewEdgeCollection as bD, type TypedStoreViewEdgeCollection as bE, UnionableQuery as bF, type UpdateEdgeInput as bG, type UpdateNodeInput as bH, type VectorSearchHit as bI, type VectorSearchOptions as bJ, asRecordedInstant as bK, createQueryBuilder as bL, createStore as bM, createStoreWithSchema as bN, createVerifiedStore as bO, defineSubgraphProject as bP, embedding as bQ, exists as bR, fieldRef as bS, getEmbeddingDimensions as bT, getSearchableMetadata as bU, inSubquery as bV, isEmbeddingSchema as bW, isParameterRef as bX, isSearchableSchema as bY, notExists as bZ, notInSubquery as b_, type StoreOptions as ba, type StoreProjection as bb, type StoreRef as bc, StoreSearch as bd, StoreView as be, type StoreViewCanReachOptions as bf, type StoreViewCoordinate as bg, type StoreViewDegreeOptions as bh, type StoreViewEdgeCollection as bi, type StoreViewEdgeCollections as bj, type StoreViewNeighborsOptions as bk, type StoreViewNodeCollection as bl, type StoreViewNodeCollections as bm, type StoreViewReachableOptions as bn, type StoreViewShortestPathOptions as bo, type StoreViewSubgraphOptions as bp, type StreamOptions as bq, type SubgraphEdgeResult as br, type SubgraphNodeResult as bs, type SubgraphOptions as bt, type SubgraphResult as bu, type SubsetEdge as bv, type SubsetNode as bw, type TemporalAlgorithmOptions as bx, type TransactionContext as by, type TransactionOutcome as bz, QueryBuilder as c, searchable as c0, type EmptyRecursiveAliasMap as d, type AggregateResult as e, type AlgorithmCyclePolicy as f, type AnyEdge as g, type AnyNode as h, type BatchResults as i, type BatchableQuery as j, type CreateEdgeInput as k, type CreateNodeInput as l, type DegreeOptions as m, type DynamicEdgeAccessor as n, type DynamicEdgeCollection as o, type DynamicEdgeType as p, type DynamicFieldBuilder as q, type DynamicNodeAccessor as r, type DynamicNodeCollection as s, type DynamicNodeType as t, type DynamicSelectableEdge as u, type DynamicSelectableNode as v, type Edge as w, type EdgeAccessor as x, type EdgeBatchReads as y, type EdgeCollection as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nicia-ai/typegraph",
3
- "version": "0.35.0",
3
+ "version": "0.36.0",
4
4
  "description": "TypeScript-first embedded knowledge graph library with ontological reasoning",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",