@nicia-ai/typegraph 0.28.1 → 0.30.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 (178) hide show
  1. package/dist/backend/postgres/index.cjs +24 -762
  2. package/dist/backend/postgres/index.cjs.map +1 -1
  3. package/dist/backend/postgres/index.d.cts +24 -5
  4. package/dist/backend/postgres/index.d.ts +24 -5
  5. package/dist/backend/postgres/index.js +11 -753
  6. package/dist/backend/postgres/index.js.map +1 -1
  7. package/dist/backend/postgres/pglite.cjs +52 -0
  8. package/dist/backend/postgres/pglite.cjs.map +1 -0
  9. package/dist/backend/postgres/pglite.d.cts +116 -0
  10. package/dist/backend/postgres/pglite.d.ts +116 -0
  11. package/dist/backend/postgres/pglite.js +50 -0
  12. package/dist/backend/postgres/pglite.js.map +1 -0
  13. package/dist/backend/sqlite/index.cjs +23 -27
  14. package/dist/backend/sqlite/index.d.cts +22 -15
  15. package/dist/backend/sqlite/index.d.ts +22 -15
  16. package/dist/backend/sqlite/index.js +10 -10
  17. package/dist/backend/sqlite/libsql.cjs +18 -14
  18. package/dist/backend/sqlite/libsql.cjs.map +1 -1
  19. package/dist/backend/sqlite/libsql.d.cts +3 -3
  20. package/dist/backend/sqlite/libsql.d.ts +3 -3
  21. package/dist/backend/sqlite/libsql.js +17 -13
  22. package/dist/backend/sqlite/libsql.js.map +1 -1
  23. package/dist/backend/sqlite/local.cjs +19 -17
  24. package/dist/backend/sqlite/local.cjs.map +1 -1
  25. package/dist/backend/sqlite/local.d.cts +9 -3
  26. package/dist/backend/sqlite/local.d.ts +9 -3
  27. package/dist/backend/sqlite/local.js +17 -15
  28. package/dist/backend/sqlite/local.js.map +1 -1
  29. package/dist/chunk-26HWIIVS.cjs +740 -0
  30. package/dist/chunk-26HWIIVS.cjs.map +1 -0
  31. package/dist/chunk-2QA5EHEN.js +181 -0
  32. package/dist/chunk-2QA5EHEN.js.map +1 -0
  33. package/dist/chunk-3IRMLJEK.js +206 -0
  34. package/dist/chunk-3IRMLJEK.js.map +1 -0
  35. package/dist/{chunk-5MTOKWDA.js → chunk-3JLRHQ67.js} +9 -9
  36. package/dist/{chunk-5MTOKWDA.js.map → chunk-3JLRHQ67.js.map} +1 -1
  37. package/dist/{chunk-WVQXAFP3.js → chunk-454I6GDF.js} +46 -5
  38. package/dist/chunk-454I6GDF.js.map +1 -0
  39. package/dist/{chunk-CTHNVU6K.cjs → chunk-4BXHVEFB.cjs} +16 -16
  40. package/dist/{chunk-CTHNVU6K.cjs.map → chunk-4BXHVEFB.cjs.map} +1 -1
  41. package/dist/{chunk-NMOE2ZEU.cjs → chunk-4LBO6KIF.cjs} +139 -128
  42. package/dist/chunk-4LBO6KIF.cjs.map +1 -0
  43. package/dist/chunk-633N7DSE.js +258 -0
  44. package/dist/chunk-633N7DSE.js.map +1 -0
  45. package/dist/{chunk-WFUN6DCM.cjs → chunk-7JGLPAFV.cjs} +11 -11
  46. package/dist/chunk-7JGLPAFV.cjs.map +1 -0
  47. package/dist/{chunk-ZTREJOGB.js → chunk-AI3RNREM.js} +140 -69
  48. package/dist/chunk-AI3RNREM.js.map +1 -0
  49. package/dist/{chunk-TSGBRPNE.js → chunk-ENDUY2CD.js} +46 -46
  50. package/dist/chunk-ENDUY2CD.js.map +1 -0
  51. package/dist/{chunk-7V335GHG.cjs → chunk-FLRCVI2G.cjs} +12 -2
  52. package/dist/chunk-FLRCVI2G.cjs.map +1 -0
  53. package/dist/chunk-FVIGJTS7.js +305 -0
  54. package/dist/chunk-FVIGJTS7.js.map +1 -0
  55. package/dist/chunk-FWBVPATK.cjs +208 -0
  56. package/dist/chunk-FWBVPATK.cjs.map +1 -0
  57. package/dist/chunk-IE6GN7L3.cjs +319 -0
  58. package/dist/chunk-IE6GN7L3.cjs.map +1 -0
  59. package/dist/{chunk-PGU2XKMA.js → chunk-IUIJJYPI.js} +117 -106
  60. package/dist/chunk-IUIJJYPI.js.map +1 -0
  61. package/dist/{chunk-E5ROA3MN.js → chunk-IX7RE7CG.js} +3 -3
  62. package/dist/{chunk-E5ROA3MN.js.map → chunk-IX7RE7CG.js.map} +1 -1
  63. package/dist/{chunk-YEDQLNVE.cjs → chunk-JIEJCTDS.cjs} +30 -2
  64. package/dist/chunk-JIEJCTDS.cjs.map +1 -0
  65. package/dist/{chunk-WZH2MKHW.cjs → chunk-K4N35E7B.cjs} +101 -101
  66. package/dist/{chunk-WZH2MKHW.cjs.map → chunk-K4N35E7B.cjs.map} +1 -1
  67. package/dist/{chunk-NZ5PMBTG.js → chunk-K5P5OZXS.js} +12 -3
  68. package/dist/chunk-K5P5OZXS.js.map +1 -0
  69. package/dist/chunk-K5VREXMZ.js +738 -0
  70. package/dist/chunk-K5VREXMZ.js.map +1 -0
  71. package/dist/chunk-LUHU4T2F.cjs +12 -0
  72. package/dist/{chunk-4FJVSF3K.cjs.map → chunk-LUHU4T2F.cjs.map} +1 -1
  73. package/dist/{chunk-O57GYGYK.js → chunk-M6OC455F.js} +32 -18
  74. package/dist/chunk-M6OC455F.js.map +1 -0
  75. package/dist/{chunk-OYLF3IC7.js → chunk-MAQJYNJR.js} +4 -4
  76. package/dist/chunk-MAQJYNJR.js.map +1 -0
  77. package/dist/{chunk-OOS2RKRH.js → chunk-MXE2U3EX.js} +4 -4
  78. package/dist/{chunk-OOS2RKRH.js.map → chunk-MXE2U3EX.js.map} +1 -1
  79. package/dist/{chunk-TKM2WDT4.js → chunk-N2OEOGBB.js} +386 -1093
  80. package/dist/chunk-N2OEOGBB.js.map +1 -0
  81. package/dist/{chunk-EJCDOA37.js → chunk-R4JOBEJU.js} +5 -5
  82. package/dist/{chunk-EJCDOA37.js.map → chunk-R4JOBEJU.js.map} +1 -1
  83. package/dist/{chunk-FFHW2JEK.cjs → chunk-RP7YITHX.cjs} +32 -17
  84. package/dist/chunk-RP7YITHX.cjs.map +1 -0
  85. package/dist/{chunk-PYJOBZR3.cjs → chunk-SGPXKJ4A.cjs} +60 -60
  86. package/dist/chunk-SGPXKJ4A.cjs.map +1 -0
  87. package/dist/{chunk-U5ROKVRF.js → chunk-TQOYM4LN.js} +29 -3
  88. package/dist/chunk-TQOYM4LN.js.map +1 -0
  89. package/dist/{chunk-PN37HVAV.cjs → chunk-UESTHN47.cjs} +153 -71
  90. package/dist/chunk-UESTHN47.cjs.map +1 -0
  91. package/dist/chunk-VFX3LC76.cjs +183 -0
  92. package/dist/chunk-VFX3LC76.cjs.map +1 -0
  93. package/dist/chunk-WEMZM7J7.cjs +270 -0
  94. package/dist/chunk-WEMZM7J7.cjs.map +1 -0
  95. package/dist/{chunk-NYQFPGYP.cjs → chunk-WFCECD6Q.cjs} +423 -1139
  96. package/dist/chunk-WFCECD6Q.cjs.map +1 -0
  97. package/dist/{chunk-EK6RJ373.cjs → chunk-YQGLVWB2.cjs} +7 -7
  98. package/dist/{chunk-EK6RJ373.cjs.map → chunk-YQGLVWB2.cjs.map} +1 -1
  99. package/dist/{chunk-X3244EKP.cjs → chunk-ZFWNO3GE.cjs} +48 -4
  100. package/dist/chunk-ZFWNO3GE.cjs.map +1 -0
  101. package/dist/ddl-BSFlbGQF.d.cts +38 -0
  102. package/dist/ddl-EdkIiFVU.d.ts +38 -0
  103. package/dist/{extension-types-B78CZfA5.d.cts → extension-types-CwHV-Uk4.d.cts} +4 -4
  104. package/dist/{extension-types-B78CZfA5.d.ts → extension-types-CwHV-Uk4.d.ts} +4 -4
  105. package/dist/graph-extension/index.cjs +19 -19
  106. package/dist/graph-extension/index.d.cts +4 -4
  107. package/dist/graph-extension/index.d.ts +4 -4
  108. package/dist/graph-extension/index.js +4 -4
  109. package/dist/{index-CWdBFWbI.d.ts → index-DzeIiMO3.d.ts} +30 -3
  110. package/dist/{index-Bilw0pYm.d.cts → index-Ln_KHTGA.d.cts} +30 -3
  111. package/dist/index.cjs +1542 -1011
  112. package/dist/index.cjs.map +1 -1
  113. package/dist/index.d.cts +142 -12
  114. package/dist/index.d.ts +142 -12
  115. package/dist/index.js +1138 -643
  116. package/dist/index.js.map +1 -1
  117. package/dist/indexes/index.cjs +19 -19
  118. package/dist/indexes/index.d.cts +9 -9
  119. package/dist/indexes/index.d.ts +9 -9
  120. package/dist/indexes/index.js +5 -5
  121. package/dist/interchange/index.cjs +8 -14
  122. package/dist/interchange/index.cjs.map +1 -1
  123. package/dist/interchange/index.d.cts +5 -16
  124. package/dist/interchange/index.d.ts +5 -16
  125. package/dist/interchange/index.js +3 -9
  126. package/dist/interchange/index.js.map +1 -1
  127. package/dist/{manager-ehLTTNq0.d.ts → manager-BbdyNhkv.d.ts} +2 -2
  128. package/dist/{manager-c1MGaVVR.d.cts → manager-wTDgjoXG.d.cts} +2 -2
  129. package/dist/{materialize-indexes-DFavFQah.d.ts → materialize-indexes-lkawaDor.d.ts} +1 -1
  130. package/dist/{materialize-indexes-BQfaAZKA.d.cts → materialize-indexes-oanE3RU0.d.cts} +1 -1
  131. package/dist/{ddl-0Ooc2dpL.d.ts → postgres-CK2ByP1q.d.cts} +2 -471
  132. package/dist/{ddl-Dza3kDrI.d.cts → postgres-ymXYC6Rr.d.ts} +2 -471
  133. package/dist/profiler/index.cjs +10 -10
  134. package/dist/profiler/index.d.cts +7 -7
  135. package/dist/profiler/index.d.ts +7 -7
  136. package/dist/profiler/index.js +3 -3
  137. package/dist/schema/index.cjs +33 -33
  138. package/dist/schema/index.d.cts +7 -7
  139. package/dist/schema/index.d.ts +7 -7
  140. package/dist/schema/index.js +10 -10
  141. package/dist/{sqlite-BxZUPbyD.d.ts → sqlite-DxH1lrwD.d.cts} +2 -468
  142. package/dist/{sqlite-mvpXjEeB.d.cts → sqlite-R8YGOqVQ.d.ts} +2 -468
  143. package/dist/{store-Ufc1Eoey.d.ts → store--z1Tmk7U.d.cts} +124 -5
  144. package/dist/{store-D7YEMg2v.d.cts → store-CNKM334w.d.ts} +124 -5
  145. package/dist/{types-CMQymcwk.d.cts → types-BBiDqJCU.d.ts} +252 -46
  146. package/dist/{types-B-sJJ4GQ.d.ts → types-CMvxx2kd.d.ts} +2 -2
  147. package/dist/{types-CmUA9sal.d.cts → types-D5B6Be5c.d.cts} +2 -2
  148. package/dist/{types-CQ7xXJMP.d.ts → types-dL-clxO3.d.cts} +252 -46
  149. package/dist/{where-CtrLu1Zf.d.ts → where-CAo6wsBZ.d.ts} +2 -2
  150. package/dist/{where-5zf15aFZ.d.cts → where-bJjuOCeV.d.cts} +2 -2
  151. package/package.json +20 -5
  152. package/dist/chunk-3EUKJEQ3.js +0 -77
  153. package/dist/chunk-3EUKJEQ3.js.map +0 -1
  154. package/dist/chunk-4FJVSF3K.cjs +0 -12
  155. package/dist/chunk-7V335GHG.cjs.map +0 -1
  156. package/dist/chunk-FFHW2JEK.cjs.map +0 -1
  157. package/dist/chunk-HTGDWIDE.cjs +0 -89
  158. package/dist/chunk-HTGDWIDE.cjs.map +0 -1
  159. package/dist/chunk-LYRUNG6E.cjs +0 -52
  160. package/dist/chunk-LYRUNG6E.cjs.map +0 -1
  161. package/dist/chunk-NMOE2ZEU.cjs.map +0 -1
  162. package/dist/chunk-NYQFPGYP.cjs.map +0 -1
  163. package/dist/chunk-NZ5PMBTG.js.map +0 -1
  164. package/dist/chunk-O57GYGYK.js.map +0 -1
  165. package/dist/chunk-OYLF3IC7.js.map +0 -1
  166. package/dist/chunk-PCHTTG42.js +0 -47
  167. package/dist/chunk-PCHTTG42.js.map +0 -1
  168. package/dist/chunk-PGU2XKMA.js.map +0 -1
  169. package/dist/chunk-PN37HVAV.cjs.map +0 -1
  170. package/dist/chunk-PYJOBZR3.cjs.map +0 -1
  171. package/dist/chunk-TKM2WDT4.js.map +0 -1
  172. package/dist/chunk-TSGBRPNE.js.map +0 -1
  173. package/dist/chunk-U5ROKVRF.js.map +0 -1
  174. package/dist/chunk-WFUN6DCM.cjs.map +0 -1
  175. package/dist/chunk-WVQXAFP3.js.map +0 -1
  176. package/dist/chunk-X3244EKP.cjs.map +0 -1
  177. package/dist/chunk-YEDQLNVE.cjs.map +0 -1
  178. package/dist/chunk-ZTREJOGB.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { N as NodeType, e as EdgeType, o as UniquenessScope, g as Collation, D as DeleteBehavior, d as KindAnnotations, f as Cardinality, k as EndpointExistence, T as TemporalMode, G as GraphExtension, I as IndexEntity, K as KindEntity, a as NullCheckOp, A as AnyEdgeType } from './extension-types-B78CZfA5.js';
2
+ import { N as NodeType, d as EdgeType, U as UniquenessScope, C as Collation, D as DeleteBehavior, e as KindAnnotations, f as Cardinality, g as EndpointExistence, T as TemporalMode, G as GraphExtension, I as IndexEntity, K as KindEntity, a as NullCheckOp, A as AnyEdgeType } from './extension-types-CwHV-Uk4.cjs';
3
3
  import { SQL } from 'drizzle-orm';
4
4
  import { PgDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core';
5
5
  import { BaseSQLiteDatabase } from 'drizzle-orm/sqlite-core';
@@ -120,8 +120,6 @@ type SqlTableNames = Readonly<{
120
120
  nodes: string;
121
121
  /** Edges table name (default: "typegraph_edges") */
122
122
  edges: string;
123
- /** Node embeddings table name (default: "typegraph_node_embeddings") */
124
- embeddings: string;
125
123
  /** Node fulltext table name (default: "typegraph_node_fulltext") */
126
124
  fulltext: string;
127
125
  /** Node uniques table name (default: "typegraph_node_uniques") */
@@ -138,8 +136,6 @@ type SqlSchema = Readonly<{
138
136
  nodesTable: SQL;
139
137
  /** Get a SQL reference to the edges table */
140
138
  edgesTable: SQL;
141
- /** Get a SQL reference to the embeddings table */
142
- embeddingsTable: SQL;
143
139
  /** Get a SQL reference to the fulltext table */
144
140
  fulltextTable: SQL;
145
141
  }>;
@@ -162,7 +158,7 @@ type SqlSchema = Readonly<{
162
158
  * const schema = createSqlSchema({
163
159
  * nodes: "myapp_nodes",
164
160
  * edges: "myapp_edges",
165
- * embeddings: "myapp_embeddings",
161
+ * fulltext: "myapp_fulltext",
166
162
  * });
167
163
  * ```
168
164
  */
@@ -171,6 +167,27 @@ declare function createSqlSchema(names?: Partial<SqlTableNames>): SqlSchema;
171
167
  * Default SqlSchema using standard TypeGraph table names.
172
168
  */
173
169
  declare const DEFAULT_SQL_SCHEMA: SqlSchema;
170
+ /**
171
+ * The compiler's resolved view of one declared embedding field — the
172
+ * `(dimensions, metric, indexType)` a {@link VectorStrategy} needs to
173
+ * name and scan the field's typed per-`(kind, field)` storage. Sourced
174
+ * from the registered node schema's `embedding()` declaration when the
175
+ * store builds its compile options.
176
+ */
177
+ type VectorSlotDescriptor = Readonly<{
178
+ dimensions: number;
179
+ metric: VectorMetric;
180
+ indexType: VectorIndexType;
181
+ }>;
182
+ /**
183
+ * Map of declared embedding slots keyed by {@link vectorSlotKey} -
184
+ * `"<nodeKind>\0<fieldPath>"` (NUL-separated). Carries every `(concrete kind,
185
+ * fieldPath)` that declares an embedding field, so the compiler's
186
+ * `field.similarTo(...)` CTE can UNION ALL the per-field tables for the
187
+ * kinds in an alias that actually declare the field (only
188
+ * `includeSubClasses` yields more than one).
189
+ */
190
+ type VectorSlotMap = ReadonlyMap<string, VectorSlotDescriptor>;
174
191
 
175
192
  /**
176
193
  * A single table TypeGraph owns.
@@ -348,6 +365,181 @@ interface FulltextStrategy {
348
365
  declare const tsvectorStrategy: FulltextStrategy;
349
366
  declare const fts5Strategy: FulltextStrategy;
350
367
 
368
+ /**
369
+ * Vector Strategy — pluggable storage + SQL generation for a backend's
370
+ * vector stack. The sibling of {@link FulltextStrategy}: a strategy owns
371
+ * every statement that touches its embedding storage — the per-field DDL,
372
+ * upsert, delete, similarity search, and (optional) ANN index lifecycle —
373
+ * plus the capability advertisement and the distance/score expressions the
374
+ * query compiler splices into its relevance CTE.
375
+ *
376
+ * ## Why a strategy, and why per-(kind, field) storage
377
+ *
378
+ * The spike behind #157 established that "real" ANN on every engine we
379
+ * support converges on a
380
+ * **typed, fixed-dimension structure per `(nodeKind, fieldPath)`**:
381
+ *
382
+ * - pgvector: a `vector(N)` column + HNSW/IVFFlat;
383
+ * - libSQL native: an `F32_BLOB(N)` column + `libsql_vector_idx` +
384
+ * `vector_top_k` (a plain `BLOB` or dimensionless `F32_BLOB` is rejected
385
+ * by the index — the dimension must live in the column type);
386
+ * - sqlite-vec: a `vec0(embedding float[N])` virtual table.
387
+ *
388
+ * The legacy single shared `typegraph_node_embeddings` table (one generic
389
+ * column holding mixed-dimension vectors) can only ever be brute-forced;
390
+ * pgvector was the sole engine that hid this, because its index supplies the
391
+ * dimension via a `::vector(N)` cast expression. So storage is the
392
+ * strategy's to own, slot by slot, rather than a fixed global table.
393
+ *
394
+ * Brute force remains a legitimate, capability-advertised mode (libSQL's
395
+ * `vector_distance_cos` over a column with no index, sqlite-vec's
396
+ * `vec_distance_cosine`): a strategy whose `capabilities.indexTypes` is
397
+ * `["none"]` simply never emits an ANN index and `buildSearch` always scans.
398
+ */
399
+
400
+ /**
401
+ * The resolved identity of one embedding field's storage in a graph —
402
+ * everything a strategy needs to DDL, address, and index it.
403
+ *
404
+ * Graph-scoped: each `(graphId, nodeKind, fieldPath)` gets its own physical
405
+ * table. TypeGraph supports many graphs per physical database, and the same
406
+ * `(kind, field)` can carry different embedding dimensions across graphs — a
407
+ * shared per-`(kind, field)` table would collide on the fixed column type
408
+ * (`vector(N)` / `F32_BLOB(N)`). Graph-scoping also makes libSQL's
409
+ * table-global `vector_top_k` per-graph-exact (no cross-graph recall bleed).
410
+ */
411
+ type VectorSlot = Readonly<{
412
+ /** Graph the embedding belongs to — scopes the physical table. */
413
+ graphId: string;
414
+ /** Node kind owning the embedding field (e.g. `"Document"`). */
415
+ nodeKind: string;
416
+ /** Dot-path of the embedding field within the node props (e.g. `"embedding"`). */
417
+ fieldPath: string;
418
+ /** Fixed vector dimension `N` for this field — carried into the column type. */
419
+ dimensions: number;
420
+ /** Distance metric the field's index is built for. */
421
+ metric: VectorMetric;
422
+ /**
423
+ * Index type to materialize. `"none"` means brute-force only (no ANN
424
+ * index emitted); the strategy still stores and searches the field.
425
+ */
426
+ indexType: VectorIndexType;
427
+ /**
428
+ * Optional ANN index tuning carried into the index DDL (pgvector
429
+ * `m`/`ef_construction`/`lists`). Present on the create-index / re-embed
430
+ * paths (resolved from the field's `embedding()` declaration); omitted on
431
+ * the write/search ensure paths, where the strategy falls back to defaults.
432
+ */
433
+ indexParams?: Readonly<{
434
+ m?: number;
435
+ efConstruction?: number;
436
+ lists?: number;
437
+ }>;
438
+ }>;
439
+ /**
440
+ * Derives the `VectorCapabilities` a backend advertises from its active
441
+ * strategy, so the two never drift (mirrors `buildFulltextCapabilities`).
442
+ * The strategy is the single source of truth; there are no per-call-site
443
+ * `SQLITE_VECTOR_*` constants.
444
+ */
445
+ declare function buildVectorCapabilities(strategy: VectorStrategy): VectorCapabilities;
446
+ /**
447
+ * A pluggable vector implementation. Each strategy is self-contained:
448
+ * given a {@link VectorSlot}, it emits every statement the backend and
449
+ * compiler need — per-field storage DDL, writes, similarity search, ANN
450
+ * index lifecycle — and advertises exactly the metrics and index types it
451
+ * can honor. Adding the Nth backend is one of these objects; no core edits.
452
+ */
453
+ interface VectorStrategy {
454
+ /** Human-readable identifier used in error messages and telemetry. */
455
+ readonly name: string;
456
+ /**
457
+ * The metrics, index types, and dimension ceiling this strategy honors —
458
+ * advertised verbatim as `backend.capabilities.vector`. Asymmetry across
459
+ * engines is legitimate and explicit here (pgvector has `inner_product`;
460
+ * libSQL/sqlite-vec do not), never a silent runtime failure.
461
+ */
462
+ readonly capabilities: VectorCapabilities;
463
+ /**
464
+ * Deterministic physical table (or virtual-table) name backing a field in
465
+ * a graph. The compiler references this to scan the right per-field storage,
466
+ * and the backend uses it to route upserts/deletes. Must be a stable,
467
+ * collision-safe SQL identifier derived from `(graphId, nodeKind, fieldPath)`.
468
+ */
469
+ tableName(graphId: string, nodeKind: string, fieldPath: string): string;
470
+ /**
471
+ * The per-field storage this strategy owns for `slot`, as Drizzle-free
472
+ * `StrategyTableContribution`s (resolved `tableName`, deterministic
473
+ * idempotent `createDdl` for the table **and** its ANN index when the
474
+ * slot's `indexType` warrants one, `runtimeEnsure`). Rides the #129/#135
475
+ * table-contribution + durable-materialization machinery exactly as the
476
+ * FTS5 / tsvector virtual tables do — these are materialized per graph by
477
+ * `materializeIndexes()`, not by global `bootstrapTables`.
478
+ */
479
+ ownedTables(slot: VectorSlot): readonly StrategyTableContribution[];
480
+ /**
481
+ * Emits the statement(s) that upsert a single embedding into the slot's
482
+ * storage. Multiple statements are allowed for engines that cannot upsert
483
+ * a vector in one statement (e.g. a `vec0` virtual table → DELETE+INSERT).
484
+ */
485
+ buildUpsert(slot: VectorSlot, params: UpsertEmbeddingParams, timestamp: string): readonly SQL[];
486
+ /** Emits the statement(s) that delete a single embedding from the slot. */
487
+ buildDelete(slot: VectorSlot, params: DeleteEmbeddingParams): readonly SQL[];
488
+ /**
489
+ * Raw DDL statement(s) that drop the slot's entire physical storage
490
+ * (table + ANN index, and any engine-managed shadow tables). Used by the
491
+ * destructive `store.reembedVectorField()` path to recreate a field's
492
+ * storage at a new dimension. Returned as raw strings (like
493
+ * `ownedTables(...).createDdl`) for `backend.executeDdl`; must be idempotent
494
+ * (`IF EXISTS`).
495
+ */
496
+ buildDropStorage(slot: VectorSlot): readonly string[];
497
+ /**
498
+ * Emits the similarity-search query for the `backend.vectorSearch` path,
499
+ * returning rows shaped `{ node_id, score }` ordered best-first. The
500
+ * strategy picks brute-force vs ANN based on whether `slot.indexType`
501
+ * materialized an index — the caller never branches. `score` follows the
502
+ * shared convention (cosine → similarity `1 - distance`; l2 /
503
+ * inner_product → raw distance), so `coerceVectorScore` / fusion stay
504
+ * dialect-neutral.
505
+ */
506
+ buildSearch(slot: VectorSlot, params: VectorSearchParams): SQL;
507
+ /**
508
+ * The distance expression over the slot's embedding column, used by the
509
+ * **query compiler** to splice vector relevance into its CTE. This is the
510
+ * one genuinely engine-specific fragment (`vec_distance_cosine` vs
511
+ * `<=>` vs `vector_distance_cos`); the surrounding score / minScore /
512
+ * ORDER BY math is shared (see {@link vectorScoreExpression} etc.).
513
+ *
514
+ * `embeddingColumn` is the already-qualified column SQL; `queryEmbedding`
515
+ * is formatted by the strategy into its engine's literal form.
516
+ */
517
+ distanceExpression(embeddingColumn: SQL, queryEmbedding: readonly number[], metric: VectorMetric): SQL;
518
+ /**
519
+ * Emits the ANN index creation statement for a slot, or `undefined` when
520
+ * indexing is inline (vec0) or unsupported (brute-force-only strategies).
521
+ * Invoked through `backend.createVectorIndex` during `materializeIndexes`.
522
+ */
523
+ buildCreateIndex?(slot: VectorSlot, options?: Readonly<{
524
+ concurrent?: boolean;
525
+ }>): SQL | undefined;
526
+ /** Emits the ANN index drop statement, or `undefined` when not applicable. */
527
+ buildDropIndex?(slot: VectorSlot): SQL | undefined;
528
+ }
529
+
530
+ /**
531
+ * SQL Dialect Abstraction Layer
532
+ *
533
+ * Provides a unified interface for dialect-specific SQL generation.
534
+ * Implementing a new dialect (MySQL, SQL Server, etc.) requires
535
+ * implementing this interface.
536
+ */
537
+
538
+ /**
539
+ * Supported SQL dialects.
540
+ */
541
+ type SqlDialect = "sqlite" | "postgres";
542
+
351
543
  /**
352
544
  * JSON Schema type (subset used by Zod toJSONSchema).
353
545
  *
@@ -541,19 +733,6 @@ type SqliteExecutionProfileHints = Readonly<{
541
733
  }>;
542
734
  type AnySqliteDatabase = BaseSQLiteDatabase<"sync" | "async", unknown>;
543
735
 
544
- /**
545
- * SQL Dialect Abstraction Layer
546
- *
547
- * Provides a unified interface for dialect-specific SQL generation.
548
- * Implementing a new dialect (MySQL, SQL Server, etc.) requires
549
- * implementing this interface.
550
- */
551
-
552
- /**
553
- * Supported SQL dialects.
554
- */
555
- type SqlDialect = "sqlite" | "postgres";
556
-
557
736
  /**
558
737
  * Backend interface types for TypeGraph storage.
559
738
  *
@@ -618,18 +797,10 @@ type FulltextCapabilities = Readonly<{
618
797
  * Backend capabilities that vary by dialect.
619
798
  */
620
799
  type BackendCapabilities = Readonly<{
621
- /** Whether the backend supports JSONB type (vs TEXT for JSON) */
622
- jsonb: boolean;
623
- /** Whether the backend supports partial indexes */
624
- partialIndexes: boolean;
625
- /** Whether the backend supports GIN indexes for JSON */
626
- ginIndexes: boolean;
627
- /** Whether the backend supports CTE (WITH) queries */
628
- cte: boolean;
629
- /** Whether the backend supports RETURNING clause */
630
- returning: boolean;
631
800
  /** Whether the backend supports atomic transactions (D1 does not) */
632
801
  transactions: boolean;
802
+ /** Whether the backend supports SQL window functions such as ROW_NUMBER() */
803
+ windowFunctions: boolean;
633
804
  /** Vector search capabilities (undefined if not configured) */
634
805
  vector?: VectorCapabilities | undefined;
635
806
  /** Fulltext search capabilities (undefined if not configured) */
@@ -691,19 +862,6 @@ type SchemaVersionRow = Readonly<{
691
862
  created_at: string;
692
863
  is_active: boolean;
693
864
  }>;
694
- /**
695
- * A row from the typegraph_node_embeddings table.
696
- */
697
- type EmbeddingRow = Readonly<{
698
- graph_id: string;
699
- node_kind: string;
700
- node_id: string;
701
- field_path: string;
702
- embedding: readonly number[];
703
- dimensions: number;
704
- created_at: string;
705
- updated_at: string;
706
- }>;
707
865
  /**
708
866
  * Parameters for inserting a node.
709
867
  */
@@ -785,6 +943,14 @@ type HardDeleteEdgeParams = Readonly<{
785
943
  }>;
786
944
  /**
787
945
  * Parameters for inserting or updating an embedding.
946
+ *
947
+ * `dimensions`, `metric`, and `indexType` together resolve the slot's
948
+ * typed per-`(nodeKind, fieldPath)` storage (the strategy needs the fixed
949
+ * dimension for the column type, and the metric/index type to address the
950
+ * right ANN structure). The store populates them from the schema's
951
+ * `embedding()` declaration via `getEmbeddingDimensions()` /
952
+ * `getEmbeddingIndex()`; the backend constructs a `VectorSlot` from them
953
+ * and stays graph-agnostic.
788
954
  */
789
955
  type UpsertEmbeddingParams = Readonly<{
790
956
  graphId: string;
@@ -793,15 +959,29 @@ type UpsertEmbeddingParams = Readonly<{
793
959
  fieldPath: string;
794
960
  embedding: readonly number[];
795
961
  dimensions: number;
962
+ metric: VectorMetric;
963
+ indexType: VectorIndexType;
796
964
  }>;
797
965
  /**
798
966
  * Parameters for deleting an embedding.
967
+ *
968
+ * `dimensions` / `metric` / `indexType` mirror {@link UpsertEmbeddingParams}
969
+ * so the backend can resolve the slot's typed per-`(nodeKind, fieldPath)`
970
+ * storage and idempotently ensure it exists before the DELETE. That
971
+ * matters because a delete can run before any embedding was ever written
972
+ * (e.g. a node hard-deleted having never carried one), and on Postgres a
973
+ * DELETE against a missing relation inside a transaction aborts the whole
974
+ * transaction. The store populates them from the schema's `embedding()`
975
+ * declaration, exactly as for upserts.
799
976
  */
800
977
  type DeleteEmbeddingParams = Readonly<{
801
978
  graphId: string;
802
979
  nodeKind: string;
803
980
  nodeId: string;
804
981
  fieldPath: string;
982
+ dimensions: number;
983
+ metric: VectorMetric;
984
+ indexType: VectorIndexType;
805
985
  }>;
806
986
  /**
807
987
  * Parameters for vector similarity search.
@@ -812,6 +992,19 @@ type VectorSearchParams = Readonly<{
812
992
  fieldPath: string;
813
993
  queryEmbedding: readonly number[];
814
994
  metric: VectorMetric;
995
+ /**
996
+ * Fixed vector dimension `N` for the searched `(nodeKind, fieldPath)`
997
+ * slot. The store populates it from the schema's `embedding()`
998
+ * declaration via `getEmbeddingDimensions()`; the backend uses it to
999
+ * construct the `VectorSlot` whose typed storage the strategy scans.
1000
+ */
1001
+ dimensions: number;
1002
+ /**
1003
+ * Index type materialized for this slot. `"none"` means brute-force
1004
+ * only; otherwise the strategy may route through its ANN structure.
1005
+ * The store populates it from `getEmbeddingIndex()`.
1006
+ */
1007
+ indexType: VectorIndexType;
815
1008
  limit: number;
816
1009
  minScore?: number;
817
1010
  /**
@@ -1170,6 +1363,16 @@ type GraphBackend = Readonly<{
1170
1363
  * When absent, the dialect's default strategy is used.
1171
1364
  */
1172
1365
  fulltextStrategy?: FulltextStrategy | undefined;
1366
+ /**
1367
+ * Optional vector strategy this backend is wired with. The query
1368
+ * compiler reads it (via `compileQuery` options) to emit per-`(kind,
1369
+ * field)` relevance scans for `field.similarTo(...)` predicates, and
1370
+ * the index-materialization / removal paths read its deterministic
1371
+ * `tableName(...)` to address the right physical per-field storage.
1372
+ * `undefined` when the backend has no vector support (e.g. a generic
1373
+ * SQLite backend without sqlite-vec).
1374
+ */
1375
+ vectorStrategy?: VectorStrategy | undefined;
1173
1376
  insertNode: (params: InsertNodeParams) => Promise<NodeRow>;
1174
1377
  insertNodeNoReturn?: (params: InsertNodeParams) => Promise<void>;
1175
1378
  insertNodesBatch?: (params: readonly InsertNodeParams[]) => Promise<void>;
@@ -1234,7 +1437,6 @@ type GraphBackend = Readonly<{
1234
1437
  setActiveVersion: (params: SetActiveVersionParams) => Promise<void>;
1235
1438
  upsertEmbedding?: (params: UpsertEmbeddingParams) => Promise<void>;
1236
1439
  deleteEmbedding?: (params: DeleteEmbeddingParams) => Promise<void>;
1237
- getEmbedding?: (graphId: string, nodeKind: string, nodeId: string, fieldPath: string) => Promise<EmbeddingRow | undefined>;
1238
1440
  vectorSearch?: (params: VectorSearchParams) => Promise<readonly VectorSearchResult[]>;
1239
1441
  createVectorIndex?: (params: CreateVectorIndexParams) => Promise<void>;
1240
1442
  dropVectorIndex?: (params: DropVectorIndexParams) => Promise<void>;
@@ -1879,8 +2081,12 @@ type VectorSimilarityPredicate = Readonly<{
1879
2081
  field: FieldRef;
1880
2082
  /** The query embedding to compare against */
1881
2083
  queryEmbedding: readonly number[];
1882
- /** Similarity metric to use */
1883
- metric: VectorMetricType;
2084
+ /**
2085
+ * Similarity metric. Omitted when the caller didn't pass one — the compiler
2086
+ * then uses the field's DECLARED `embedding()` metric (resolved per kind),
2087
+ * falling back to "cosine" only when no declaration is available.
2088
+ */
2089
+ metric?: VectorMetricType;
1884
2090
  /** Maximum number of results to return */
1885
2091
  limit: number;
1886
2092
  /** Optional minimum similarity score (0-1 for cosine) */
@@ -2398,4 +2604,4 @@ type RelationalIndexDeclaration = NodeIndexDeclaration | EdgeIndexDeclaration;
2398
2604
  type IndexDeclaration = RelationalIndexDeclaration | VectorIndexDeclaration;
2399
2605
  type SystemColumnName = "graph_id" | "kind" | "id" | "from_kind" | "from_id" | "to_kind" | "to_id" | "deleted_at" | "valid_from" | "valid_to" | "created_at" | "updated_at" | "version";
2400
2606
 
2401
- export { type FulltextCapabilities as $, type AnySqliteDatabase as A, type BackendCapabilities as B, type VectorIndexParams as C, type SchemaVersionRow as D, type EdgeIndexConfig as E, type FulltextStrategy as F, type GraphBackend as G, type InferenceType as H, type IndexDeclaration as I, type JsonPointer as J, type AggregateExpr as K, type FieldRef as L, type MetaEdge as M, type NodeIndexConfig as N, type OntologyRelation as O, type ComparisonOp as P, type QueryAst as Q, type RelationalIndexDeclaration as R, type SqliteExecutionProfileHints as S, type AggregateComparisonPredicate as T, type AdoptedTransaction as U, type VectorIndexDeclaration as V, type CommitSchemaVersionExpected as W, type CommitSchemaVersionParams as X, DEFAULT_SQL_SCHEMA as Y, type DeleteFulltextBatchParams as Z, type FulltextBatchRow as _, type SqliteTransactionMode as a, type FulltextQueryMode as a0, type FulltextSearchParams as a1, type FulltextSearchResult as a2, type HybridFusionOptions as a3, type JsonPointerFor as a4, type JsonPointerInput as a5, type JsonPointerSegment as a6, type JsonPointerSegments as a7, type JsonPointerSegmentsFor as a8, MAX_JSON_POINTER_DEPTH as a9, type Traversal as aA, type NodePredicate as aB, type ProjectedField as aC, type GroupBySpec as aD, type RecursiveCyclePolicy as aE, type TraversalDirection as aF, type EdgeRow as aG, type NodeRow as aH, type SelectiveField as aI, type ComposableQuery as aJ, type SetOperationType as aK, type SetOperation as aL, type VectorMetric as aM, type MetaEdgeProperties as aa, type OrderSpec as ab, type ParameterRef as ac, type ResolveJsonPointer as ad, type ResolveJsonPointerSegments as ae, type SetActiveVersionParams as af, type SortDirection as ag, type SqlSchema as ah, type SqlTableNames as ai, type StrategyTableContribution as aj, type TableContribution as ak, type TransactionBackend as al, type TraversalExpansion as am, type UpsertFulltextBatchParams as an, type VectorCapabilities as ao, createSqlSchema as ap, fts5Strategy as aq, isMetaEdge as ar, joinJsonPointers as as, jsonPointer as at, normalizeJsonPointer as au, parseJsonPointer as av, tsvectorStrategy as aw, type ValueType as ax, type PredicateExpression as ay, type VectorMetricType as az, type AnyPgDatabase as b, type EdgeIndexDeclaration as c, type NodeIndexDeclaration as d, type IndexWhereExpression as e, type SerializedNodeDef as f, type SerializedEdgeDef as g, type SerializedMetaEdge as h, type SerializedOntologyRelation as i, type SerializedClosures as j, type SerializedSchema as k, type SchemaHash as l, type JsonSchema as m, type SerializedOntology as n, type SerializedUniqueConstraint as o, type SqlDialect as p, type EdgeIndexDirection as q, type EdgeIndexWhereBuilder as r, type IndexOrigin as s, type IndexScope as t, type IndexWhereFieldBuilder as u, type IndexWhereInput as v, type NodeIndexWhereBuilder as w, type SystemColumnName as x, type VectorIndexImplementation as y, type VectorIndexMetric as z };
2607
+ export { type FulltextBatchRow as $, type AnySqliteDatabase as A, type BackendCapabilities as B, type VectorIndexMetric as C, type VectorIndexParams as D, type EdgeIndexConfig as E, type FulltextStrategy as F, type GraphBackend as G, type SchemaVersionRow as H, type IndexDeclaration as I, type JsonPointer as J, type InferenceType as K, type AggregateExpr as L, type MetaEdge as M, type NodeIndexConfig as N, type OntologyRelation as O, type FieldRef as P, type QueryAst as Q, type RelationalIndexDeclaration as R, type SqliteExecutionProfileHints as S, type ComparisonOp as T, type AggregateComparisonPredicate as U, type VectorStrategy as V, type AdoptedTransaction as W, type CommitSchemaVersionExpected as X, type CommitSchemaVersionParams as Y, DEFAULT_SQL_SCHEMA as Z, type DeleteFulltextBatchParams as _, type SqliteTransactionMode as a, type FulltextCapabilities as a0, type FulltextQueryMode as a1, type FulltextSearchParams as a2, type FulltextSearchResult as a3, type HybridFusionOptions as a4, type JsonPointerFor as a5, type JsonPointerInput as a6, type JsonPointerSegment as a7, type JsonPointerSegments as a8, type JsonPointerSegmentsFor as a9, type VectorSlotMap as aA, type ValueType as aB, type PredicateExpression as aC, type VectorMetricType as aD, type Traversal as aE, type NodePredicate as aF, type ProjectedField as aG, type GroupBySpec as aH, type RecursiveCyclePolicy as aI, type TraversalDirection as aJ, type EdgeRow as aK, type NodeRow as aL, type SelectiveField as aM, type ComposableQuery as aN, type SetOperationType as aO, type SetOperation as aP, type VectorMetric as aQ, MAX_JSON_POINTER_DEPTH as aa, type MetaEdgeProperties as ab, type OrderSpec as ac, type ParameterRef as ad, type ResolveJsonPointer as ae, type ResolveJsonPointerSegments as af, type SetActiveVersionParams as ag, type SortDirection as ah, type SqlSchema as ai, type SqlTableNames as aj, type StrategyTableContribution as ak, type TableContribution as al, type TransactionBackend as am, type TraversalExpansion as an, type UpsertFulltextBatchParams as ao, type VectorCapabilities as ap, type VectorSlot as aq, buildVectorCapabilities as ar, createSqlSchema as as, fts5Strategy as at, isMetaEdge as au, joinJsonPointers as av, jsonPointer as aw, normalizeJsonPointer as ax, parseJsonPointer as ay, tsvectorStrategy as az, type AnyPgDatabase as b, type EdgeIndexDeclaration as c, type NodeIndexDeclaration as d, type IndexWhereExpression as e, type SerializedNodeDef as f, type SerializedEdgeDef as g, type SerializedMetaEdge as h, type SerializedOntologyRelation as i, type SerializedClosures as j, type SerializedSchema as k, type SchemaHash as l, type JsonSchema as m, type SerializedOntology as n, type SerializedUniqueConstraint as o, type SqlDialect as p, type EdgeIndexDirection as q, type EdgeIndexWhereBuilder as r, type IndexOrigin as s, type IndexScope as t, type IndexWhereFieldBuilder as u, type IndexWhereInput as v, type NodeIndexWhereBuilder as w, type SystemColumnName as x, type VectorIndexDeclaration as y, type VectorIndexImplementation as z };
@@ -1,5 +1,5 @@
1
- import { A as AnyEdgeType, N as NodeType } from './extension-types-B78CZfA5.js';
2
- import { E as EdgeIndexConfig, c as EdgeIndexDeclaration, N as NodeIndexConfig, d as NodeIndexDeclaration, e as IndexWhereExpression } from './types-CQ7xXJMP.js';
1
+ import { A as AnyEdgeType, N as NodeType } from './extension-types-CwHV-Uk4.js';
2
+ import { E as EdgeIndexConfig, c as EdgeIndexDeclaration, N as NodeIndexConfig, d as NodeIndexDeclaration, e as IndexWhereExpression } from './types-BBiDqJCU.js';
3
3
 
4
4
  declare function defineNodeIndex<N extends NodeType>(node: N, config: NodeIndexConfig<N>): NodeIndexDeclaration;
5
5
  declare function defineEdgeIndex<E extends AnyEdgeType>(edge: E, config: EdgeIndexConfig<E>): EdgeIndexDeclaration;
@@ -1,5 +1,5 @@
1
- import { A as AnyEdgeType, N as NodeType } from './extension-types-B78CZfA5.cjs';
2
- import { E as EdgeIndexConfig, c as EdgeIndexDeclaration, N as NodeIndexConfig, d as NodeIndexDeclaration, e as IndexWhereExpression } from './types-CMQymcwk.cjs';
1
+ import { A as AnyEdgeType, N as NodeType } from './extension-types-CwHV-Uk4.cjs';
2
+ import { E as EdgeIndexConfig, c as EdgeIndexDeclaration, N as NodeIndexConfig, d as NodeIndexDeclaration, e as IndexWhereExpression } from './types-dL-clxO3.cjs';
3
3
 
4
4
  declare function defineNodeIndex<N extends NodeType>(node: N, config: NodeIndexConfig<N>): NodeIndexDeclaration;
5
5
  declare function defineEdgeIndex<E extends AnyEdgeType>(edge: E, config: EdgeIndexConfig<E>): EdgeIndexDeclaration;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nicia-ai/typegraph",
3
- "version": "0.28.1",
3
+ "version": "0.30.0",
4
4
  "description": "TypeScript-first embedded knowledge graph library with ontological reasoning",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -47,6 +47,11 @@
47
47
  "import": "./dist/backend/postgres/index.js",
48
48
  "require": "./dist/backend/postgres/index.cjs"
49
49
  },
50
+ "./postgres/pglite": {
51
+ "types": "./dist/backend/postgres/pglite.d.ts",
52
+ "import": "./dist/backend/postgres/pglite.js",
53
+ "require": "./dist/backend/postgres/pglite.cjs"
54
+ },
50
55
  "./sqlite/local": {
51
56
  "types": "./dist/backend/sqlite/local.d.ts",
52
57
  "import": "./dist/backend/sqlite/local.js",
@@ -92,6 +97,8 @@
92
97
  "node": ">=22"
93
98
  },
94
99
  "peerDependencies": {
100
+ "@electric-sql/pglite": ">=0.5.0 <0.6.0",
101
+ "@electric-sql/pglite-pgvector": ">=0.0.2 <1.0.0",
95
102
  "@libsql/client": ">=0.6.0",
96
103
  "@neondatabase/serverless": ">=0.9.0 <2.0.0",
97
104
  "better-sqlite3": ">=9.0.0 <13.0.0",
@@ -101,6 +108,12 @@
101
108
  "zod": "^4.0.0"
102
109
  },
103
110
  "peerDependenciesMeta": {
111
+ "@electric-sql/pglite": {
112
+ "optional": true
113
+ },
114
+ "@electric-sql/pglite-pgvector": {
115
+ "optional": true
116
+ },
104
117
  "@libsql/client": {
105
118
  "optional": true
106
119
  },
@@ -121,7 +134,9 @@
121
134
  "nanoid": "^5.1.11"
122
135
  },
123
136
  "devDependencies": {
124
- "@cloudflare/vitest-pool-workers": "^0.16.7",
137
+ "@cloudflare/vitest-pool-workers": "^0.16.12",
138
+ "@electric-sql/pglite": "^0.5.1",
139
+ "@electric-sql/pglite-pgvector": "^0.0.2",
125
140
  "@libsql/client": "^0.17.3",
126
141
  "@neondatabase/serverless": "^1.1.0",
127
142
  "@stryker-mutator/core": "^9.6.1",
@@ -129,10 +144,10 @@
129
144
  "@types/better-sqlite3": "^7.6.13",
130
145
  "@types/node": "^24.12.0",
131
146
  "@types/pg": "^8.20.0",
132
- "@vitest/coverage-v8": "^4.1.7",
147
+ "@vitest/coverage-v8": "^4.1.8",
133
148
  "better-sqlite3": "^12.10.0",
134
149
  "drizzle-orm": "^0.45.2",
135
- "eslint": "^10.4.0",
150
+ "eslint": "^10.4.1",
136
151
  "fast-check": "^4.8.0",
137
152
  "pg": "^8.21.0",
138
153
  "postgres": "^3.4.9",
@@ -140,7 +155,7 @@
140
155
  "tsd": "^0.33.0",
141
156
  "tsup": "^8.5.1",
142
157
  "typescript": "^5.9.3",
143
- "vitest": "^4.1.7",
158
+ "vitest": "^4.1.8",
144
159
  "zod": "^4.4.3",
145
160
  "@typegraph/eslint-config": "0.1.0"
146
161
  },
@@ -1,77 +0,0 @@
1
- import { sql } from 'drizzle-orm';
2
-
3
- // src/query/compiler/utils.ts
4
- var NODE_COLUMNS = [
5
- "id",
6
- "kind",
7
- "props",
8
- "version",
9
- "valid_from",
10
- "valid_to",
11
- "created_at",
12
- "updated_at",
13
- "deleted_at"
14
- ];
15
- var EDGE_COLUMNS = [
16
- "id",
17
- "kind",
18
- "from_id",
19
- "to_id",
20
- "props",
21
- "valid_from",
22
- "valid_to",
23
- "created_at",
24
- "updated_at",
25
- "deleted_at"
26
- ];
27
- var EMPTY_REQUIRED_COLUMNS = /* @__PURE__ */ new Set();
28
- function quoteIdentifier(identifier) {
29
- return sql.raw(`"${identifier.replaceAll('"', '""')}"`);
30
- }
31
- function shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns) {
32
- if (alwaysRequiredColumns?.has(column)) return true;
33
- if (requiredColumns === void 0) return true;
34
- return requiredColumns.has(column);
35
- }
36
- function addRequiredColumn(requiredColumnsByAlias, alias, column) {
37
- const existing = requiredColumnsByAlias.get(alias);
38
- if (existing) {
39
- existing.add(column);
40
- return;
41
- }
42
- requiredColumnsByAlias.set(alias, /* @__PURE__ */ new Set([column]));
43
- }
44
- function markFieldRefAsRequired(requiredColumnsByAlias, field) {
45
- const column = field.path[0];
46
- if (column === void 0) return;
47
- addRequiredColumn(requiredColumnsByAlias, field.alias, column);
48
- }
49
- function mapSelectiveSystemFieldToColumn(field) {
50
- if (field === "fromId") return "from_id";
51
- if (field === "toId") return "to_id";
52
- if (field.startsWith("meta.")) {
53
- return field.slice(5).replaceAll(/([A-Z])/g, "_$1").toLowerCase();
54
- }
55
- return field;
56
- }
57
- function markSelectiveFieldAsRequired(requiredColumnsByAlias, field) {
58
- if (field.isSystemField) {
59
- addRequiredColumn(
60
- requiredColumnsByAlias,
61
- field.alias,
62
- mapSelectiveSystemFieldToColumn(field.field)
63
- );
64
- return;
65
- }
66
- addRequiredColumn(requiredColumnsByAlias, field.alias, "props");
67
- }
68
- function isIdFieldRef(field) {
69
- return field.path.length === 1 && field.path[0] === "id" && field.jsonPointer === void 0;
70
- }
71
- function isAggregateExpr(source) {
72
- return "__type" in source && source.__type === "aggregate";
73
- }
74
-
75
- export { EDGE_COLUMNS, EMPTY_REQUIRED_COLUMNS, NODE_COLUMNS, addRequiredColumn, isAggregateExpr, isIdFieldRef, mapSelectiveSystemFieldToColumn, markFieldRefAsRequired, markSelectiveFieldAsRequired, quoteIdentifier, shouldProjectColumn };
76
- //# sourceMappingURL=chunk-3EUKJEQ3.js.map
77
- //# sourceMappingURL=chunk-3EUKJEQ3.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/query/compiler/utils.ts"],"names":[],"mappings":";;;AAaO,IAAM,YAAA,GAAe;AAAA,EAC1B,IAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA;AAAA,EACA,UAAA;AAAA,EACA,YAAA;AAAA,EACA,YAAA;AAAA,EACA;AACF;AAEO,IAAM,YAAA,GAAe;AAAA,EAC1B,IAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,YAAA;AAAA,EACA,UAAA;AAAA,EACA,YAAA;AAAA,EACA,YAAA;AAAA,EACA;AACF;AAGO,IAAM,sBAAA,uBAA6B,GAAA;AAMnC,SAAS,gBAAgB,UAAA,EAAyB;AACvD,EAAA,OAAO,GAAA,CAAI,IAAI,CAAA,CAAA,EAAI,UAAA,CAAW,WAAW,GAAA,EAAK,IAAI,CAAC,CAAA,CAAA,CAAG,CAAA;AACxD;AAcO,SAAS,mBAAA,CACd,eAAA,EACA,MAAA,EACA,qBAAA,EACS;AACT,EAAA,IAAI,qBAAA,EAAuB,GAAA,CAAI,MAAM,CAAA,EAAG,OAAO,IAAA;AAC/C,EAAA,IAAI,eAAA,KAAoB,QAAW,OAAO,IAAA;AAC1C,EAAA,OAAO,eAAA,CAAgB,IAAI,MAAM,CAAA;AACnC;AAMO,SAAS,iBAAA,CACd,sBAAA,EACA,KAAA,EACA,MAAA,EACM;AACN,EAAA,MAAM,QAAA,GAAW,sBAAA,CAAuB,GAAA,CAAI,KAAK,CAAA;AACjD,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,QAAA,CAAS,IAAI,MAAM,CAAA;AACnB,IAAA;AAAA,EACF;AACA,EAAA,sBAAA,CAAuB,IAAI,KAAA,kBAAO,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;AACrD;AAEO,SAAS,sBAAA,CACd,wBACA,KAAA,EACM;AACN,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA;AAC3B,EAAA,IAAI,WAAW,MAAA,EAAW;AAC1B,EAAA,iBAAA,CAAkB,sBAAA,EAAwB,KAAA,CAAM,KAAA,EAAO,MAAM,CAAA;AAC/D;AAEO,SAAS,gCAAgC,KAAA,EAAuB;AACrE,EAAA,IAAI,KAAA,KAAU,UAAU,OAAO,SAAA;AAC/B,EAAA,IAAI,KAAA,KAAU,QAAQ,OAAO,OAAA;AAC7B,EAAA,IAAI,KAAA,CAAM,UAAA,CAAW,OAAO,CAAA,EAAG;AAC7B,IAAA,OAAO,KAAA,CACJ,MAAM,CAAC,CAAA,CACP,WAAW,UAAA,EAAY,KAAK,EAC5B,WAAA,EAAY;AAAA,EACjB;AACA,EAAA,OAAO,KAAA;AACT;AAEO,SAAS,4BAAA,CACd,wBACA,KAAA,EACM;AACN,EAAA,IAAI,MAAM,aAAA,EAAe;AACvB,IAAA,iBAAA;AAAA,MACE,sBAAA;AAAA,MACA,KAAA,CAAM,KAAA;AAAA,MACN,+BAAA,CAAgC,MAAM,KAAK;AAAA,KAC7C;AACA,IAAA;AAAA,EACF;AACA,EAAA,iBAAA,CAAkB,sBAAA,EAAwB,KAAA,CAAM,KAAA,EAAO,OAAO,CAAA;AAChE;AAMO,SAAS,aAAa,KAAA,EAA0B;AACrD,EAAA,OACE,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,CAAA,IACtB,KAAA,CAAM,KAAK,CAAC,CAAA,KAAM,IAAA,IAClB,KAAA,CAAM,WAAA,KAAgB,MAAA;AAE1B;AAEO,SAAS,gBACd,MAAA,EACyB;AACzB,EAAA,OAAO,QAAA,IAAY,MAAA,IAAU,MAAA,CAAO,MAAA,KAAW,WAAA;AACjD","file":"chunk-3EUKJEQ3.js","sourcesContent":["/**\n * Shared Compiler Utilities\n *\n * Functions and constants shared between the standard and recursive compilers.\n */\nimport { type SQL, sql } from \"drizzle-orm\";\n\nimport { type AggregateExpr, type FieldRef, type SelectiveField } from \"../ast\";\n\n// ============================================================\n// Constants\n// ============================================================\n\nexport const NODE_COLUMNS = [\n \"id\",\n \"kind\",\n \"props\",\n \"version\",\n \"valid_from\",\n \"valid_to\",\n \"created_at\",\n \"updated_at\",\n \"deleted_at\",\n] as const;\n\nexport const EDGE_COLUMNS = [\n \"id\",\n \"kind\",\n \"from_id\",\n \"to_id\",\n \"props\",\n \"valid_from\",\n \"valid_to\",\n \"created_at\",\n \"updated_at\",\n \"deleted_at\",\n] as const;\n\nexport type RequiredColumnsByAlias = ReadonlyMap<string, ReadonlySet<string>>;\nexport const EMPTY_REQUIRED_COLUMNS = new Set<string>();\n\n// ============================================================\n// SQL Helpers\n// ============================================================\n\nexport function quoteIdentifier(identifier: string): SQL {\n return sql.raw(`\"${identifier.replaceAll('\"', '\"\"')}\"`);\n}\n\n// ============================================================\n// Column Projection\n// ============================================================\n\n/**\n * Determines whether a column should be included in the projection.\n *\n * @param requiredColumns - Set of columns required by the query, or undefined to include all.\n * @param column - The column name to check.\n * @param alwaysRequiredColumns - Optional set of columns that are always projected\n * (e.g. join keys in recursive CTEs). Pass only from recursive compiler callers.\n */\nexport function shouldProjectColumn(\n requiredColumns: ReadonlySet<string> | undefined,\n column: string,\n alwaysRequiredColumns?: ReadonlySet<string>,\n): boolean {\n if (alwaysRequiredColumns?.has(column)) return true;\n if (requiredColumns === undefined) return true;\n return requiredColumns.has(column);\n}\n\n// ============================================================\n// Required Column Tracking\n// ============================================================\n\nexport function addRequiredColumn(\n requiredColumnsByAlias: Map<string, Set<string>>,\n alias: string,\n column: string,\n): void {\n const existing = requiredColumnsByAlias.get(alias);\n if (existing) {\n existing.add(column);\n return;\n }\n requiredColumnsByAlias.set(alias, new Set([column]));\n}\n\nexport function markFieldRefAsRequired(\n requiredColumnsByAlias: Map<string, Set<string>>,\n field: FieldRef,\n): void {\n const column = field.path[0];\n if (column === undefined) return;\n addRequiredColumn(requiredColumnsByAlias, field.alias, column);\n}\n\nexport function mapSelectiveSystemFieldToColumn(field: string): string {\n if (field === \"fromId\") return \"from_id\";\n if (field === \"toId\") return \"to_id\";\n if (field.startsWith(\"meta.\")) {\n return field\n .slice(5)\n .replaceAll(/([A-Z])/g, \"_$1\")\n .toLowerCase();\n }\n return field;\n}\n\nexport function markSelectiveFieldAsRequired(\n requiredColumnsByAlias: Map<string, Set<string>>,\n field: SelectiveField,\n): void {\n if (field.isSystemField) {\n addRequiredColumn(\n requiredColumnsByAlias,\n field.alias,\n mapSelectiveSystemFieldToColumn(field.field),\n );\n return;\n }\n addRequiredColumn(requiredColumnsByAlias, field.alias, \"props\");\n}\n\n// ============================================================\n// AST Type Guards\n// ============================================================\n\nexport function isIdFieldRef(field: FieldRef): boolean {\n return (\n field.path.length === 1 &&\n field.path[0] === \"id\" &&\n field.jsonPointer === undefined\n );\n}\n\nexport function isAggregateExpr(\n source: FieldRef | AggregateExpr,\n): source is AggregateExpr {\n return \"__type\" in source && source.__type === \"aggregate\";\n}\n"]}
@@ -1,12 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkPYJOBZR3_cjs = require('./chunk-PYJOBZR3.cjs');
4
-
5
- // src/graph-extension/define-graph-extension.ts
6
- function defineGraphExtension(input) {
7
- return chunkPYJOBZR3_cjs.unwrap(chunkPYJOBZR3_cjs.validateGraphExtension(input, { strict: true }));
8
- }
9
-
10
- exports.defineGraphExtension = defineGraphExtension;
11
- //# sourceMappingURL=chunk-4FJVSF3K.cjs.map
12
- //# sourceMappingURL=chunk-4FJVSF3K.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/query/json-pointer.ts"],"names":[],"mappings":";;;AAEO,IAAM,sBAAA,GAAyB;AAoI/B,SAAS,YAAY,QAAA,EAA4C;AACtE,EAAA,IAAI,QAAA,CAAS,SAAS,sBAAA,EAAwB;AAC5C,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,+BAAA,EAAkC,sBAAsB,CAAA,EAAA,EAAK,QAAA,CAAS,MAAM,CAAA;AAAA,KAC9E;AAAA,EACF;AAEA,EAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AACzB,IAAA,OAAO,EAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,KAAY;AACxC,IAAA,IAAI,YAAY,GAAA,EAAK;AACnB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,MAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,OAAO,CAAA,IAAK,UAAU,CAAA,EAAG;AAC7C,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,sDAAsD,OAAO,CAAA;AAAA,SAC/D;AAAA,MACF;AACA,MAAA,OAAO,QAAQ,QAAA,EAAS;AAAA,IAC1B;AACA,IAAA,OAAO,yBAAyB,OAAO,CAAA;AAAA,EACzC,CAAC,CAAA;AAED,EAAA,OAAO,CAAA,CAAA,EAAI,OAAA,CAAQ,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA;AAC9B;AAEA,SAAS,sBACP,KAAA,EACwC;AACxC,EAAA,OAAO,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5B;AAEO,SAAS,qBACd,KAAA,EACa;AACb,EAAA,IAAI,qBAAA,CAAsB,KAAK,CAAA,EAAG;AAChC,IAAA,OAAO,YAAY,KAAK,CAAA;AAAA,EAC1B;AAEA,EAAA,OAAO,kBAAkB,KAAK,CAAA;AAChC;AAEO,SAAS,iBAAiB,OAAA,EAAyC;AACxE,EAAA,IAAI,YAAY,EAAA,EAAI;AAClB,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,MAAM,cAAc,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA,CAAE,MAAM,GAAG,CAAA;AAC9C,EAAA,OAAO,YAAY,GAAA,CAAI,CAAC,OAAA,KAAY,wBAAA,CAAyB,OAAO,CAAC,CAAA;AACvE;AAEO,SAAS,gBAAA,CACd,MACA,QAAA,EACa;AACb,EAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,KAAS,EAAA,EAAI;AACxB,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,IAAI,aAAa,EAAA,EAAI;AACnB,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,gBAAA,GAAmB;AAAA,IACvB,GAAG,iBAAiB,IAAI,CAAA;AAAA,IACxB,GAAG,iBAAiB,QAAQ;AAAA,GAC9B;AACA,EAAA,OAAO,YAAY,gBAAgB,CAAA;AACrC;AAEA,SAAS,kBAAkB,OAAA,EAA8B;AACvD,EAAA,IAAI,YAAY,EAAA,IAAM,CAAC,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,EAAG;AAC9C,IAAA,MAAM,IAAI,MAAM,8CAA8C,CAAA;AAAA,EAChE;AAEA,EAAA,MAAM,QAAA,GAAW,OAAA,KAAY,EAAA,GAAK,EAAC,GAAI,QAAQ,KAAA,CAAM,CAAC,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA;AACjE,EAAA,IAAI,QAAA,CAAS,SAAS,sBAAA,EAAwB;AAC5C,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,+BAAA,EAAkC,sBAAsB,CAAA,EAAA,EAAK,QAAA,CAAS,MAAM,CAAA;AAAA,KAC9E;AAAA,EACF;AAEA,EAAA,KAAA,MAAW,WAAW,QAAA,EAAU;AAC9B,IAAA,IAAI,YAAY,GAAA,EAAK;AACnB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,IAAI,CAAC,sBAAA,CAAuB,OAAO,CAAA,EAAG;AACpC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,oDAAoD,OAAO,CAAA;AAAA,OAC7D;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,OAAA;AACT;AAMO,SAAS,yBAAyB,OAAA,EAAyB;AAChE,EAAA,OAAO,QAAQ,UAAA,CAAW,GAAA,EAAK,IAAI,CAAA,CAAE,UAAA,CAAW,KAAK,IAAI,CAAA;AAC3D;AAEA,SAAS,yBAAyB,OAAA,EAAyB;AACzD,EAAA,OAAO,QAAQ,UAAA,CAAW,IAAA,EAAM,GAAG,CAAA,CAAE,UAAA,CAAW,MAAM,GAAG,CAAA;AAC3D;AAEA,SAAS,uBAAuB,OAAA,EAA0B;AACxD,EAAA,KAAA,IAAS,QAAQ,CAAA,EAAG,KAAA,GAAQ,OAAA,CAAQ,MAAA,EAAQ,SAAS,CAAA,EAAG;AACtD,IAAA,IAAI,OAAA,CAAQ,KAAK,CAAA,KAAM,GAAA,EAAK;AAC1B,MAAA;AAAA,IACF;AACA,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,KAAA,GAAQ,CAAC,CAAA;AAC9B,IAAA,IAAI,IAAA,KAAS,GAAA,IAAO,IAAA,KAAS,GAAA,EAAK;AAChC,MAAA,OAAO,KAAA;AAAA,IACT;AACA,IAAA,KAAA,IAAS,CAAA;AAAA,EACX;AACA,EAAA,OAAO,IAAA;AACT","file":"chunk-7V335GHG.cjs","sourcesContent":["// JSON Pointer utilities and types.\n\nexport const MAX_JSON_POINTER_DEPTH = 5 as const;\n\nexport type JsonPointer = string & { readonly __jsonPointer: unique symbol };\nexport type JsonPointerSegment = string | number;\nexport type JsonPointerSegments = readonly JsonPointerSegment[];\n\ntype Depth = 0 | 1 | 2 | 3 | 4 | 5;\n\ninterface DepthDecrementMap {\n 0: 0;\n 1: 0;\n 2: 1;\n 3: 2;\n 4: 3;\n 5: 4;\n}\n\ntype Decrement<Current extends Depth> = DepthDecrementMap[Current];\n\ntype NonNegativeIntegerString = Exclude<`${bigint}`, `-${string}`>;\ntype ObjectPointerKey<T> = Exclude<\n Extract<keyof T, string>,\n NonNegativeIntegerString\n>;\n\ntype EncodeTilde<S extends string> =\n S extends `${infer Head}~${infer Tail}` ?\n `${EncodeTilde<Head>}~0${EncodeTilde<Tail>}`\n : S;\n\ntype EncodeSlash<S extends string> =\n S extends `${infer Head}/${infer Tail}` ?\n `${EncodeSlash<Head>}~1${EncodeSlash<Tail>}`\n : S;\n\ntype EncodePointerSegment<S extends string> = EncodeSlash<EncodeTilde<S>>;\n\ntype DecodePointerSegment<S extends string> =\n S extends `${infer Head}~1${infer Tail}` ?\n `${DecodePointerSegment<Head>}/${DecodePointerSegment<Tail>}`\n : S extends `${infer Head}~0${infer Tail}` ?\n `${DecodePointerSegment<Head>}~${DecodePointerSegment<Tail>}`\n : S;\n\ntype PointerForArray<T, Current extends Depth> =\n | `/${NonNegativeIntegerString}`\n | (Current extends 1 ? never\n : `/${NonNegativeIntegerString}${JsonPointerFor<T, Decrement<Current>>}`);\n\ntype PointerForObject<T, Current extends Depth> = {\n [K in ObjectPointerKey<T>]:\n | `/${EncodePointerSegment<K>}`\n | (Current extends 1 ? never\n : `/${EncodePointerSegment<K>}${JsonPointerFor<T[K], Decrement<Current>>}`);\n}[ObjectPointerKey<T>];\n\nexport type JsonPointerFor<T, Current extends Depth = 5> =\n | \"\"\n | (Current extends 0 ? \"\"\n : T extends readonly (infer U)[] ? PointerForArray<U, Current>\n : never)\n | (Current extends 0 ? \"\"\n : T extends Record<string, unknown> ? PointerForObject<T, Current>\n : never);\n\ntype PointerSegmentsForArray<T, Current extends Depth> =\n | readonly [number]\n | (Current extends 1 ? readonly [number]\n : readonly [number, ...JsonPointerSegmentsFor<T, Decrement<Current>>]);\n\ntype PointerSegmentsForObject<T, Current extends Depth> = {\n [K in ObjectPointerKey<T>]:\n | readonly [K]\n | (Current extends 1 ? readonly [K]\n : readonly [K, ...JsonPointerSegmentsFor<T[K], Decrement<Current>>]);\n}[ObjectPointerKey<T>];\n\nexport type JsonPointerSegmentsFor<T, Current extends Depth = 5> =\n | readonly []\n | (Current extends 0 ? readonly []\n : T extends readonly (infer U)[] ? PointerSegmentsForArray<U, Current>\n : never)\n | (Current extends 0 ? readonly []\n : T extends Record<string, unknown> ? PointerSegmentsForObject<T, Current>\n : never);\n\nexport type JsonPointerInput<T> =\n | JsonPointerFor<T>\n | JsonPointerSegmentsFor<T>\n | JsonPointer;\n\nexport type ResolveJsonPointer<T, Pointer extends string> =\n Pointer extends \"\" ? T\n : Pointer extends `/${infer Head}/${infer Tail}` ?\n ResolveJsonPointer<\n ResolvePointerSegment<T, DecodePointerSegment<Head>>,\n `/${Tail}`\n >\n : Pointer extends `/${infer Head}` ?\n ResolvePointerSegment<T, DecodePointerSegment<Head>>\n : unknown;\n\ntype ResolvePointerSegment<T, Segment extends string> =\n T extends readonly (infer U)[] ?\n Segment extends NonNegativeIntegerString ?\n U\n : unknown\n : T extends Record<string, unknown> ?\n Segment extends keyof T ?\n Segment extends NonNegativeIntegerString ?\n unknown\n : T[Segment]\n : unknown\n : unknown;\n\nexport type ResolveJsonPointerSegments<\n T,\n Segments extends readonly JsonPointerSegment[],\n> =\n Segments extends readonly [] ? T\n : Segments extends readonly [infer Head, ...infer Tail] ?\n ResolveJsonPointerSegments<\n ResolvePointerSegment<T, SegmentToString<Head>>,\n Extract<Tail, readonly JsonPointerSegment[]>\n >\n : unknown;\n\ntype SegmentToString<Segment> =\n Segment extends number ? `${Segment}`\n : Segment extends string ? Segment\n : string;\n\nexport function jsonPointer(segments: JsonPointerSegments): JsonPointer {\n if (segments.length > MAX_JSON_POINTER_DEPTH) {\n throw new Error(\n `JSON Pointer exceeds max depth ${MAX_JSON_POINTER_DEPTH}: ${segments.length}`,\n );\n }\n\n if (segments.length === 0) {\n return \"\" as JsonPointer;\n }\n\n const encoded = segments.map((segment) => {\n if (segment === \"-\") {\n throw new Error(\n \"JSON Pointer '-' segment is not allowed for query access\",\n );\n }\n if (typeof segment === \"number\") {\n if (!Number.isInteger(segment) || segment < 0) {\n throw new Error(\n `JSON Pointer index must be a non-negative integer: ${segment}`,\n );\n }\n return segment.toString();\n }\n return encodeJsonPointerSegment(segment);\n });\n\n return `/${encoded.join(\"/\")}` as JsonPointer;\n}\n\nfunction isJsonPointerSegments(\n input: unknown,\n): input is readonly JsonPointerSegment[] {\n return Array.isArray(input);\n}\n\nexport function normalizeJsonPointer<T>(\n input: JsonPointerInput<T>,\n): JsonPointer {\n if (isJsonPointerSegments(input)) {\n return jsonPointer(input);\n }\n\n return assertJsonPointer(input);\n}\n\nexport function parseJsonPointer(pointer: JsonPointer): readonly string[] {\n if (pointer === \"\") {\n return [];\n }\n\n const rawSegments = pointer.slice(1).split(\"/\");\n return rawSegments.map((segment) => decodeJsonPointerSegment(segment));\n}\n\nexport function joinJsonPointers(\n base: JsonPointer | undefined,\n relative: JsonPointer,\n): JsonPointer {\n if (!base || base === \"\") {\n return relative;\n }\n if (relative === \"\") {\n return base;\n }\n\n const combinedSegments = [\n ...parseJsonPointer(base),\n ...parseJsonPointer(relative),\n ];\n return jsonPointer(combinedSegments);\n}\n\nfunction assertJsonPointer(pointer: string): JsonPointer {\n if (pointer !== \"\" && !pointer.startsWith(\"/\")) {\n throw new Error(\"JSON Pointer must be empty or start with '/'\");\n }\n\n const segments = pointer === \"\" ? [] : pointer.slice(1).split(\"/\");\n if (segments.length > MAX_JSON_POINTER_DEPTH) {\n throw new Error(\n `JSON Pointer exceeds max depth ${MAX_JSON_POINTER_DEPTH}: ${segments.length}`,\n );\n }\n\n for (const segment of segments) {\n if (segment === \"-\") {\n throw new Error(\n \"JSON Pointer '-' segment is not allowed for query access\",\n );\n }\n if (!isValidPointerEncoding(segment)) {\n throw new Error(\n `Invalid JSON Pointer escape sequence in segment: ${segment}`,\n );\n }\n }\n\n return pointer as JsonPointer;\n}\n\n/**\n * Escape a single JSON Pointer reference segment per RFC 6901: `~`\n * becomes `~0`, `/` becomes `~1`. Callers join the result with `/`.\n */\nexport function encodeJsonPointerSegment(segment: string): string {\n return segment.replaceAll(\"~\", \"~0\").replaceAll(\"/\", \"~1\");\n}\n\nfunction decodeJsonPointerSegment(segment: string): string {\n return segment.replaceAll(\"~1\", \"/\").replaceAll(\"~0\", \"~\");\n}\n\nfunction isValidPointerEncoding(segment: string): boolean {\n for (let index = 0; index < segment.length; index += 1) {\n if (segment[index] !== \"~\") {\n continue;\n }\n const next = segment[index + 1];\n if (next !== \"0\" && next !== \"1\") {\n return false;\n }\n index += 1;\n }\n return true;\n}\n"]}