@nicia-ai/typegraph 0.19.0 → 0.21.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 (99) hide show
  1. package/dist/{ast-Bh2NDeaK.d.cts → ast-CSLbPIOC.d.ts} +56 -3
  2. package/dist/{ast-COMyNeMn.d.ts → ast-DogwbBqA.d.cts} +56 -3
  3. package/dist/backend/postgres/index.cjs +123 -45
  4. package/dist/backend/postgres/index.cjs.map +1 -1
  5. package/dist/backend/postgres/index.d.cts +15 -7
  6. package/dist/backend/postgres/index.d.ts +15 -7
  7. package/dist/backend/postgres/index.js +101 -23
  8. package/dist/backend/postgres/index.js.map +1 -1
  9. package/dist/backend/sqlite/index.cjs +15 -15
  10. package/dist/backend/sqlite/index.d.cts +11 -7
  11. package/dist/backend/sqlite/index.d.ts +11 -7
  12. package/dist/backend/sqlite/index.js +5 -5
  13. package/dist/backend/sqlite/libsql.cjs +8 -8
  14. package/dist/backend/sqlite/libsql.d.cts +4 -5
  15. package/dist/backend/sqlite/libsql.d.ts +4 -5
  16. package/dist/backend/sqlite/libsql.js +5 -5
  17. package/dist/backend/sqlite/local.cjs +9 -9
  18. package/dist/backend/sqlite/local.d.cts +4 -5
  19. package/dist/backend/sqlite/local.d.ts +4 -5
  20. package/dist/backend/sqlite/local.js +5 -5
  21. package/dist/{chunk-LELLOHJK.cjs → chunk-2X7P7AD5.cjs} +35 -35
  22. package/dist/{chunk-LELLOHJK.cjs.map → chunk-2X7P7AD5.cjs.map} +1 -1
  23. package/dist/{chunk-GNIYZKBI.js → chunk-4XNE2JG3.js} +376 -8
  24. package/dist/chunk-4XNE2JG3.js.map +1 -0
  25. package/dist/{chunk-HXJXTOXR.cjs → chunk-CQL6FOTT.cjs} +82 -21
  26. package/dist/chunk-CQL6FOTT.cjs.map +1 -0
  27. package/dist/{chunk-K7SQ3SWP.js → chunk-EYELZHKY.js} +120 -12
  28. package/dist/chunk-EYELZHKY.js.map +1 -0
  29. package/dist/{chunk-MME3H4ZF.cjs → chunk-FQCQM3KG.cjs} +385 -9
  30. package/dist/chunk-FQCQM3KG.cjs.map +1 -0
  31. package/dist/{chunk-X33WV2UF.cjs → chunk-HRNQ3DVJ.cjs} +24 -21
  32. package/dist/chunk-HRNQ3DVJ.cjs.map +1 -0
  33. package/dist/{chunk-W6M7XWKX.js → chunk-JYUQA77I.js} +21 -18
  34. package/dist/chunk-JYUQA77I.js.map +1 -0
  35. package/dist/{chunk-LLHFS53Q.js → chunk-NRNJ52AA.js} +72 -11
  36. package/dist/chunk-NRNJ52AA.js.map +1 -0
  37. package/dist/{chunk-IYWWXOVK.cjs → chunk-Q73OWTJY.cjs} +204 -89
  38. package/dist/chunk-Q73OWTJY.cjs.map +1 -0
  39. package/dist/{chunk-G5CYWKRF.js → chunk-RS5OLGEW.js} +2 -2
  40. package/dist/{chunk-G5CYWKRF.js.map → chunk-RS5OLGEW.js.map} +1 -1
  41. package/dist/{chunk-6GWJH6AR.js → chunk-SMMOAVJ5.js} +4 -4
  42. package/dist/{chunk-6GWJH6AR.js.map → chunk-SMMOAVJ5.js.map} +1 -1
  43. package/dist/{chunk-QFZQGEQ3.js → chunk-UQVBVBVG.js} +151 -36
  44. package/dist/chunk-UQVBVBVG.js.map +1 -0
  45. package/dist/{chunk-P5CNM325.cjs → chunk-UVZHTBZ5.cjs} +123 -11
  46. package/dist/chunk-UVZHTBZ5.cjs.map +1 -0
  47. package/dist/{chunk-5GZMPJOP.cjs → chunk-WRWVWKQL.cjs} +2 -2
  48. package/dist/{chunk-5GZMPJOP.cjs.map → chunk-WRWVWKQL.cjs.map} +1 -1
  49. package/dist/{ddl-BBo2EcFc.d.cts → ddl-BqkTShcR.d.ts} +28 -6
  50. package/dist/{ddl-D4jlcOH_.d.ts → ddl-Dk1NDN3M.d.cts} +28 -6
  51. package/dist/index.cjs +2102 -556
  52. package/dist/index.cjs.map +1 -1
  53. package/dist/index.d.cts +8 -10
  54. package/dist/index.d.ts +8 -10
  55. package/dist/index.js +1961 -439
  56. package/dist/index.js.map +1 -1
  57. package/dist/indexes/index.cjs +17 -17
  58. package/dist/indexes/index.d.cts +5 -5
  59. package/dist/indexes/index.d.ts +5 -5
  60. package/dist/indexes/index.js +3 -3
  61. package/dist/interchange/index.cjs +6 -6
  62. package/dist/interchange/index.d.cts +4 -5
  63. package/dist/interchange/index.d.ts +4 -5
  64. package/dist/interchange/index.js +1 -1
  65. package/dist/{manager-Chhrq1vl.d.ts → manager-0jVZtiE8.d.ts} +1 -2
  66. package/dist/{manager-Bj1UEnhE.d.cts → manager-BPK-2PUI.d.cts} +1 -2
  67. package/dist/profiler/index.cjs +15 -8
  68. package/dist/profiler/index.cjs.map +1 -1
  69. package/dist/profiler/index.d.cts +7 -8
  70. package/dist/profiler/index.d.ts +7 -8
  71. package/dist/profiler/index.js +8 -1
  72. package/dist/profiler/index.js.map +1 -1
  73. package/dist/schema/index.cjs +21 -21
  74. package/dist/schema/index.d.cts +4 -5
  75. package/dist/schema/index.d.ts +4 -5
  76. package/dist/schema/index.js +3 -3
  77. package/dist/{sqlite-DE-6NWtC.d.cts → sqlite-D1rrwcir.d.cts} +14 -1
  78. package/dist/{sqlite-Blq-AhmK.d.ts → sqlite-W3rmJMlc.d.ts} +14 -1
  79. package/dist/{store-BnUu4qo5.d.cts → store-2QW2SIRk.d.cts} +626 -7
  80. package/dist/{store-CCOwNft_.d.ts → store-C0q13mtJ.d.ts} +626 -7
  81. package/dist/{types-B3mmOMJV.d.cts → types-CQF88McG.d.cts} +475 -2
  82. package/dist/{types-ThB4cFLp.d.ts → types-CQF88McG.d.ts} +475 -2
  83. package/dist/{types-COPePE8_.d.ts → types-CglvhPKz.d.ts} +1 -1
  84. package/dist/{types-CVtGFpB9.d.cts → types-D23slCOA.d.cts} +1 -1
  85. package/dist/{types-Ceb01czq.d.ts → types-IMQCuser.d.ts} +2 -2
  86. package/dist/{types-CZd2PEOc.d.cts → types-vgjar7Ch.d.cts} +2 -2
  87. package/package.json +10 -10
  88. package/dist/chunk-GNIYZKBI.js.map +0 -1
  89. package/dist/chunk-HXJXTOXR.cjs.map +0 -1
  90. package/dist/chunk-IYWWXOVK.cjs.map +0 -1
  91. package/dist/chunk-K7SQ3SWP.js.map +0 -1
  92. package/dist/chunk-LLHFS53Q.js.map +0 -1
  93. package/dist/chunk-MME3H4ZF.cjs.map +0 -1
  94. package/dist/chunk-P5CNM325.cjs.map +0 -1
  95. package/dist/chunk-QFZQGEQ3.js.map +0 -1
  96. package/dist/chunk-W6M7XWKX.js.map +0 -1
  97. package/dist/chunk-X33WV2UF.cjs.map +0 -1
  98. package/dist/types-DMzKq0d5.d.cts +0 -181
  99. package/dist/types-DMzKq0d5.d.ts +0 -181
package/dist/index.cjs CHANGED
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var chunkX33WV2UF_cjs = require('./chunk-X33WV2UF.cjs');
4
- var chunk5GZMPJOP_cjs = require('./chunk-5GZMPJOP.cjs');
3
+ var chunkHRNQ3DVJ_cjs = require('./chunk-HRNQ3DVJ.cjs');
4
+ var chunkWRWVWKQL_cjs = require('./chunk-WRWVWKQL.cjs');
5
5
  var chunkO5PST6TB_cjs = require('./chunk-O5PST6TB.cjs');
6
- var chunkMME3H4ZF_cjs = require('./chunk-MME3H4ZF.cjs');
7
- var chunkP5CNM325_cjs = require('./chunk-P5CNM325.cjs');
6
+ var chunkFQCQM3KG_cjs = require('./chunk-FQCQM3KG.cjs');
7
+ var chunkUVZHTBZ5_cjs = require('./chunk-UVZHTBZ5.cjs');
8
8
  var zod = require('zod');
9
9
  var drizzleOrm = require('drizzle-orm');
10
10
  var nanoid = require('nanoid');
@@ -29,6 +29,44 @@ var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
29
29
  "constructor",
30
30
  "prototype"
31
31
  ]);
32
+ var RESERVED_PROPERTY_PREFIX = "$";
33
+ function isReservedPropertyName(name) {
34
+ return name.startsWith(RESERVED_PROPERTY_PREFIX);
35
+ }
36
+ function assertSchemaKeysAreFree(entityKind, name, keys, reservedStructuralKeys) {
37
+ const structuralConflicts = keys.filter(
38
+ (key) => reservedStructuralKeys.has(key)
39
+ );
40
+ if (structuralConflicts.length > 0) {
41
+ const label = entityKind.toLowerCase();
42
+ throw new chunkO5PST6TB_cjs.ConfigurationError(
43
+ `${entityKind} "${name}" schema contains reserved property names: ${structuralConflicts.join(", ")}`,
44
+ {
45
+ [`${label}Type`]: name,
46
+ conflicts: structuralConflicts,
47
+ reservedKeys: [...reservedStructuralKeys]
48
+ },
49
+ {
50
+ suggestion: `Rename the conflicting properties. Reserved names (${[...reservedStructuralKeys].join(", ")}) are added automatically to all ${label}s.`
51
+ }
52
+ );
53
+ }
54
+ const prefixConflicts = keys.filter((key) => isReservedPropertyName(key));
55
+ if (prefixConflicts.length > 0) {
56
+ const label = entityKind.toLowerCase();
57
+ throw new chunkO5PST6TB_cjs.ConfigurationError(
58
+ `${entityKind} "${name}" schema uses the reserved "${RESERVED_PROPERTY_PREFIX}" prefix on: ${prefixConflicts.join(", ")}`,
59
+ {
60
+ [`${label}Type`]: name,
61
+ conflicts: prefixConflicts,
62
+ reservedPrefix: RESERVED_PROPERTY_PREFIX
63
+ },
64
+ {
65
+ suggestion: `Property names starting with "${RESERVED_PROPERTY_PREFIX}" are reserved for TypeGraph accessors (e.g. $fulltext). Rename each field.`
66
+ }
67
+ );
68
+ }
69
+ }
32
70
  function validateProjectionField(field2, entityType, kind) {
33
71
  const reserved = entityType === "node" ? RESERVED_NODE_KEYS : RESERVED_EDGE_KEYS;
34
72
  if (reserved.has(field2)) {
@@ -62,24 +100,17 @@ function filterReservedKeys(props, reservedKeys) {
62
100
 
63
101
  // src/core/node.ts
64
102
  function validateSchemaKeys(schema, name) {
65
- const shape = schema.shape;
66
- const conflicts = Object.keys(shape).filter(
67
- (key) => RESERVED_NODE_KEYS.has(key)
103
+ assertSchemaKeysAreFree(
104
+ "Node",
105
+ name,
106
+ Object.keys(schema.shape),
107
+ RESERVED_NODE_KEYS
68
108
  );
69
- if (conflicts.length > 0) {
70
- throw new chunkO5PST6TB_cjs.ConfigurationError(
71
- `Node "${name}" schema contains reserved property names: ${conflicts.join(", ")}`,
72
- { nodeType: name, conflicts, reservedKeys: [...RESERVED_NODE_KEYS] },
73
- {
74
- suggestion: `Rename the conflicting properties. Reserved names (id, kind, meta) are added automatically to all nodes.`
75
- }
76
- );
77
- }
78
109
  }
79
110
  function defineNode(name, options) {
80
111
  validateSchemaKeys(options.schema, name);
81
112
  return Object.freeze({
82
- [chunk5GZMPJOP_cjs.NODE_TYPE_BRAND]: true,
113
+ [chunkWRWVWKQL_cjs.NODE_TYPE_BRAND]: true,
83
114
  kind: name,
84
115
  schema: options.schema,
85
116
  description: options.description
@@ -87,25 +118,18 @@ function defineNode(name, options) {
87
118
  }
88
119
  var EMPTY_SCHEMA = zod.z.object({});
89
120
  function validateSchemaKeys2(schema, name) {
90
- const shape = schema.shape;
91
- const conflicts = Object.keys(shape).filter(
92
- (key) => RESERVED_EDGE_KEYS.has(key)
121
+ assertSchemaKeysAreFree(
122
+ "Edge",
123
+ name,
124
+ Object.keys(schema.shape),
125
+ RESERVED_EDGE_KEYS
93
126
  );
94
- if (conflicts.length > 0) {
95
- throw new chunkO5PST6TB_cjs.ConfigurationError(
96
- `Edge "${name}" schema contains reserved property names: ${conflicts.join(", ")}`,
97
- { edgeType: name, conflicts, reservedKeys: [...RESERVED_EDGE_KEYS] },
98
- {
99
- suggestion: `Rename the conflicting properties. Reserved names are added automatically to all edges.`
100
- }
101
- );
102
- }
103
127
  }
104
128
  function defineEdge(name, options) {
105
129
  const schema = options?.schema ?? EMPTY_SCHEMA;
106
130
  validateSchemaKeys2(schema, name);
107
131
  return Object.freeze({
108
- [chunk5GZMPJOP_cjs.EDGE_TYPE_BRAND]: true,
132
+ [chunkWRWVWKQL_cjs.EDGE_TYPE_BRAND]: true,
109
133
  kind: name,
110
134
  schema,
111
135
  description: options?.description,
@@ -125,7 +149,7 @@ function metaEdge(name, options = {}) {
125
149
  description: options.description
126
150
  };
127
151
  return Object.freeze({
128
- [chunkX33WV2UF_cjs.META_EDGE_BRAND]: true,
152
+ [chunkHRNQ3DVJ_cjs.META_EDGE_BRAND]: true,
129
153
  name,
130
154
  properties
131
155
  });
@@ -161,7 +185,7 @@ function createExternalRef(table) {
161
185
  // src/ontology/core-meta-edges.ts
162
186
  function createMetaEdge(name, properties) {
163
187
  return Object.freeze({
164
- [chunkX33WV2UF_cjs.META_EDGE_BRAND]: true,
188
+ [chunkHRNQ3DVJ_cjs.META_EDGE_BRAND]: true,
165
189
  name,
166
190
  properties: {
167
191
  transitive: properties.transitive ?? false,
@@ -173,7 +197,7 @@ function createMetaEdge(name, properties) {
173
197
  }
174
198
  });
175
199
  }
176
- var subClassOfMetaEdge = createMetaEdge(chunkX33WV2UF_cjs.META_EDGE_SUB_CLASS_OF, {
200
+ var subClassOfMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_SUB_CLASS_OF, {
177
201
  transitive: true,
178
202
  inference: "subsumption",
179
203
  description: "Type inheritance (Podcast subClassOf Media)"
@@ -185,9 +209,9 @@ function subClassOf(child, parent) {
185
209
  to: parent
186
210
  };
187
211
  }
188
- var broaderMetaEdge = createMetaEdge(chunkX33WV2UF_cjs.META_EDGE_BROADER, {
212
+ var broaderMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_BROADER, {
189
213
  transitive: true,
190
- inverse: chunkX33WV2UF_cjs.META_EDGE_NARROWER,
214
+ inverse: chunkHRNQ3DVJ_cjs.META_EDGE_NARROWER,
191
215
  inference: "hierarchy",
192
216
  description: "Broader concept (ML broader AI)"
193
217
  });
@@ -198,9 +222,9 @@ function broader(narrowerConcept, broaderConcept) {
198
222
  to: broaderConcept
199
223
  };
200
224
  }
201
- var narrowerMetaEdge = createMetaEdge(chunkX33WV2UF_cjs.META_EDGE_NARROWER, {
225
+ var narrowerMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_NARROWER, {
202
226
  transitive: true,
203
- inverse: chunkX33WV2UF_cjs.META_EDGE_BROADER,
227
+ inverse: chunkHRNQ3DVJ_cjs.META_EDGE_BROADER,
204
228
  inference: "hierarchy",
205
229
  description: "Narrower concept (AI narrower ML)"
206
230
  });
@@ -211,7 +235,7 @@ function narrower(broaderConcept, narrowerConcept) {
211
235
  to: narrowerConcept
212
236
  };
213
237
  }
214
- var relatedToMetaEdge = createMetaEdge(chunkX33WV2UF_cjs.META_EDGE_RELATED_TO, {
238
+ var relatedToMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_RELATED_TO, {
215
239
  symmetric: true,
216
240
  inference: "association",
217
241
  description: "Non-hierarchical association"
@@ -223,7 +247,7 @@ function relatedTo(conceptA, conceptB) {
223
247
  to: conceptB
224
248
  };
225
249
  }
226
- var equivalentToMetaEdge = createMetaEdge(chunkX33WV2UF_cjs.META_EDGE_EQUIVALENT_TO, {
250
+ var equivalentToMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_EQUIVALENT_TO, {
227
251
  symmetric: true,
228
252
  transitive: true,
229
253
  inference: "substitution",
@@ -236,7 +260,7 @@ function equivalentTo(kindA, kindBOrIri) {
236
260
  to: kindBOrIri
237
261
  };
238
262
  }
239
- var sameAsMetaEdge = createMetaEdge(chunkX33WV2UF_cjs.META_EDGE_SAME_AS, {
263
+ var sameAsMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_SAME_AS, {
240
264
  symmetric: true,
241
265
  transitive: true,
242
266
  inference: "substitution",
@@ -249,7 +273,7 @@ function sameAs(kindA, kindBOrIri) {
249
273
  to: kindBOrIri
250
274
  };
251
275
  }
252
- var differentFromMetaEdge = createMetaEdge(chunkX33WV2UF_cjs.META_EDGE_DIFFERENT_FROM, {
276
+ var differentFromMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_DIFFERENT_FROM, {
253
277
  symmetric: true,
254
278
  inference: "constraint",
255
279
  description: "Explicitly different individuals"
@@ -261,7 +285,7 @@ function differentFrom(kindA, kindB) {
261
285
  to: kindB
262
286
  };
263
287
  }
264
- var disjointWithMetaEdge = createMetaEdge(chunkX33WV2UF_cjs.META_EDGE_DISJOINT_WITH, {
288
+ var disjointWithMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_DISJOINT_WITH, {
265
289
  symmetric: true,
266
290
  inference: "constraint",
267
291
  description: "Mutually exclusive types"
@@ -273,9 +297,9 @@ function disjointWith(kindA, kindB) {
273
297
  to: kindB
274
298
  };
275
299
  }
276
- var partOfMetaEdge = createMetaEdge(chunkX33WV2UF_cjs.META_EDGE_PART_OF, {
300
+ var partOfMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_PART_OF, {
277
301
  transitive: true,
278
- inverse: chunkX33WV2UF_cjs.META_EDGE_HAS_PART,
302
+ inverse: chunkHRNQ3DVJ_cjs.META_EDGE_HAS_PART,
279
303
  inference: "composition",
280
304
  description: "X is part of Y"
281
305
  });
@@ -286,9 +310,9 @@ function partOf(part, whole) {
286
310
  to: whole
287
311
  };
288
312
  }
289
- var hasPartMetaEdge = createMetaEdge(chunkX33WV2UF_cjs.META_EDGE_HAS_PART, {
313
+ var hasPartMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_HAS_PART, {
290
314
  transitive: true,
291
- inverse: chunkX33WV2UF_cjs.META_EDGE_PART_OF,
315
+ inverse: chunkHRNQ3DVJ_cjs.META_EDGE_PART_OF,
292
316
  inference: "composition",
293
317
  description: "Y has part X"
294
318
  });
@@ -299,12 +323,12 @@ function hasPart(whole, part) {
299
323
  to: part
300
324
  };
301
325
  }
302
- var inverseOfMetaEdge = createMetaEdge(chunkX33WV2UF_cjs.META_EDGE_INVERSE_OF, {
326
+ var inverseOfMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_INVERSE_OF, {
303
327
  symmetric: true,
304
328
  inference: "none",
305
329
  description: "Edge A is inverse of edge B"
306
330
  });
307
- var impliesMetaEdge = createMetaEdge(chunkX33WV2UF_cjs.META_EDGE_IMPLIES, {
331
+ var impliesMetaEdge = createMetaEdge(chunkHRNQ3DVJ_cjs.META_EDGE_IMPLIES, {
308
332
  transitive: true,
309
333
  inference: "none",
310
334
  description: "Edge A implies edge B exists"
@@ -565,6 +589,41 @@ function stringField(field2) {
565
589
  ilike: (pattern) => stringOp("ilike", field2, pattern)
566
590
  };
567
591
  }
592
+ var DEFAULT_FULLTEXT_MATCH_K = 50;
593
+ function createFulltextAccessor(alias, hasSearchableField) {
594
+ return {
595
+ matches: (query, k, options) => {
596
+ if (!hasSearchableField()) {
597
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
598
+ `Cannot call .$fulltext.matches() on alias "${alias}" \u2014 its node kind has no fields declared with searchable(). Add at least one: \`title: searchable({ language: "english" })\`.`
599
+ );
600
+ }
601
+ return fulltextMatch(
602
+ fieldRef(alias, ["$fulltext"]),
603
+ query,
604
+ k ?? DEFAULT_FULLTEXT_MATCH_K,
605
+ options
606
+ );
607
+ }
608
+ };
609
+ }
610
+ function fulltextMatch(field2, query, limit, options) {
611
+ if (!Number.isFinite(limit) || limit <= 0) {
612
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
613
+ `matches() limit must be a positive finite number, got ${String(limit)}`
614
+ );
615
+ }
616
+ const expr = {
617
+ __type: "fulltext_match",
618
+ field: field2,
619
+ query,
620
+ mode: options?.mode ?? "websearch",
621
+ limit,
622
+ ...options?.language === void 0 ? {} : { language: options.language },
623
+ ...options?.minScore === void 0 ? {} : { minScore: options.minScore }
624
+ };
625
+ return predicate(expr);
626
+ }
568
627
  function numberField(field2) {
569
628
  return {
570
629
  ...baseFieldBuilder(field2),
@@ -664,7 +723,7 @@ function buildFieldBuilderForTypeInfo(field2, typeInfo) {
664
723
  }
665
724
  }
666
725
  function resolvePointerTypeInfo(typeInfo, pointer) {
667
- return chunkP5CNM325_cjs.resolveFieldTypeInfoAtJsonPointer(typeInfo, pointer);
726
+ return chunkUVZHTBZ5_cjs.resolveFieldTypeInfoAtJsonPointer(typeInfo, pointer);
668
727
  }
669
728
  function objectOp(op, field2, pointer, value, options) {
670
729
  const expr = {
@@ -687,8 +746,8 @@ function objectField(field2, options) {
687
746
  return {
688
747
  ...baseFieldBuilder(field2),
689
748
  get: (key) => {
690
- const pointer = chunkP5CNM325_cjs.jsonPointer([key]);
691
- const nestedPointer = chunkP5CNM325_cjs.joinJsonPointers(basePointer, pointer);
749
+ const pointer = chunkUVZHTBZ5_cjs.jsonPointer([key]);
750
+ const nestedPointer = chunkUVZHTBZ5_cjs.joinJsonPointers(basePointer, pointer);
692
751
  const resolved = resolvePointerTypeInfo(options?.typeInfo, pointer);
693
752
  const nestedRef = fieldRef(field2.alias, field2.path, {
694
753
  jsonPointer: nestedPointer,
@@ -702,17 +761,17 @@ function objectField(field2, options) {
702
761
  return buildFieldBuilderForTypeInfo(nestedRef, resolved);
703
762
  },
704
763
  hasKey: (key) => {
705
- const pointer = chunkP5CNM325_cjs.jsonPointer([key]);
764
+ const pointer = chunkUVZHTBZ5_cjs.jsonPointer([key]);
706
765
  resolvePointerTypeInfo(options?.typeInfo, pointer);
707
766
  return objectOp("hasKey", field2, pointer);
708
767
  },
709
768
  hasPath: (pointer) => {
710
- const normalized = chunkP5CNM325_cjs.normalizeJsonPointer(pointer);
769
+ const normalized = chunkUVZHTBZ5_cjs.normalizeJsonPointer(pointer);
711
770
  resolvePointerTypeInfo(options?.typeInfo, normalized);
712
771
  return objectOp("hasPath", field2, normalized);
713
772
  },
714
773
  pathEquals: (pointer, value) => {
715
- const normalized = chunkP5CNM325_cjs.normalizeJsonPointer(pointer);
774
+ const normalized = chunkUVZHTBZ5_cjs.normalizeJsonPointer(pointer);
716
775
  const resolved = resolvePointerTypeInfo(options?.typeInfo, normalized);
717
776
  if (resolved && (resolved.valueType === "array" || resolved.valueType === "object")) {
718
777
  throw new Error("pathEquals is only supported for scalar JSON values");
@@ -721,7 +780,7 @@ function objectField(field2, options) {
721
780
  return objectOp("pathEquals", field2, normalized, value, predicateOptions);
722
781
  },
723
782
  pathContains: (pointer, value) => {
724
- const normalized = chunkP5CNM325_cjs.normalizeJsonPointer(pointer);
783
+ const normalized = chunkUVZHTBZ5_cjs.normalizeJsonPointer(pointer);
725
784
  const resolved = resolvePointerTypeInfo(options?.typeInfo, normalized);
726
785
  if (resolved && resolved.valueType !== "array") {
727
786
  throw new Error("pathContains is only supported for JSON array values");
@@ -736,7 +795,7 @@ function objectField(field2, options) {
736
795
  );
737
796
  },
738
797
  pathIsNull: (pointer) => {
739
- const normalized = chunkP5CNM325_cjs.normalizeJsonPointer(pointer);
798
+ const normalized = chunkUVZHTBZ5_cjs.normalizeJsonPointer(pointer);
740
799
  const resolved = resolvePointerTypeInfo(options?.typeInfo, normalized);
741
800
  const predicateOptions = resolved?.valueType === void 0 ? void 0 : { valueType: resolved.valueType };
742
801
  return objectOp(
@@ -748,7 +807,7 @@ function objectField(field2, options) {
748
807
  );
749
808
  },
750
809
  pathIsNotNull: (pointer) => {
751
- const normalized = chunkP5CNM325_cjs.normalizeJsonPointer(pointer);
810
+ const normalized = chunkUVZHTBZ5_cjs.normalizeJsonPointer(pointer);
752
811
  const resolved = resolvePointerTypeInfo(options?.typeInfo, normalized);
753
812
  const predicateOptions = resolved?.valueType === void 0 ? void 0 : { valueType: resolved.valueType };
754
813
  return objectOp(
@@ -760,8 +819,8 @@ function objectField(field2, options) {
760
819
  );
761
820
  },
762
821
  field: (pointer) => {
763
- const normalized = chunkP5CNM325_cjs.normalizeJsonPointer(pointer);
764
- const nestedPointer = chunkP5CNM325_cjs.joinJsonPointers(basePointer, normalized);
822
+ const normalized = chunkUVZHTBZ5_cjs.normalizeJsonPointer(pointer);
823
+ const nestedPointer = chunkUVZHTBZ5_cjs.joinJsonPointers(basePointer, normalized);
765
824
  const resolved = resolvePointerTypeInfo(options?.typeInfo, normalized);
766
825
  const nestedRef = fieldRef(field2.alias, field2.path, {
767
826
  jsonPointer: nestedPointer,
@@ -891,7 +950,7 @@ function getFieldPointer(field2) {
891
950
  return field2.jsonPointer;
892
951
  }
893
952
  if (field2.path.length > 1 && field2.path[0] === "props") {
894
- return chunkP5CNM325_cjs.jsonPointer(field2.path.slice(1));
953
+ return chunkUVZHTBZ5_cjs.jsonPointer(field2.path.slice(1));
895
954
  }
896
955
  return void 0;
897
956
  }
@@ -1140,11 +1199,13 @@ function compilePredicateExpression(expr, ctx) {
1140
1199
  case "in_subquery": {
1141
1200
  return compileInSubquery(expr, ctx);
1142
1201
  }
1143
- case "vector_similarity": {
1144
- return compileVectorSimilarityPredicate();
1202
+ case "vector_similarity":
1203
+ case "fulltext_match": {
1204
+ return STRUCTURAL_PREDICATE_NOOP;
1145
1205
  }
1146
1206
  }
1147
1207
  }
1208
+ var STRUCTURAL_PREDICATE_NOOP = drizzleOrm.sql.raw("1=1");
1148
1209
  function compileComparisonPredicate(expr, dialect, cteColumnPrefix) {
1149
1210
  if (isParameterRef(expr.right)) {
1150
1211
  const parameterValueType = normalizeValueType2(expr.right.valueType) ?? normalizeValueType2(expr.left.valueType);
@@ -1248,7 +1309,7 @@ function compileArrayPredicate(expr, dialect, cteColumnPrefix) {
1248
1309
  }
1249
1310
  function compileObjectPredicate(expr, dialect, cteColumnPrefix) {
1250
1311
  const basePointer = getFieldPointer(expr.field);
1251
- const pointer = chunkP5CNM325_cjs.joinJsonPointers(basePointer, expr.pointer);
1312
+ const pointer = chunkUVZHTBZ5_cjs.joinJsonPointers(basePointer, expr.pointer);
1252
1313
  const column = compileFieldColumn(expr.field, void 0, cteColumnPrefix);
1253
1314
  switch (expr.op) {
1254
1315
  case "hasKey":
@@ -1398,45 +1459,61 @@ function compileInSubquery(expr, ctx) {
1398
1459
  const subSql = ctx.compileQuery(expr.subquery, expr.subquery.graphId);
1399
1460
  return expr.negated ? drizzleOrm.sql`${fieldSql} NOT IN (${subSql})` : drizzleOrm.sql`${fieldSql} IN (${subSql})`;
1400
1461
  }
1401
- function compileVectorSimilarityPredicate(_expr, _ctx) {
1402
- return drizzleOrm.sql.raw("1=1");
1403
- }
1404
- function extractVectorSimilarityPredicates(predicates) {
1462
+ function extractStructuralPredicates(predicates, isMatch, rejectionMessage) {
1405
1463
  const results = [];
1406
1464
  function visit(expr, inDisallowedBranch) {
1407
- switch (expr.__type) {
1408
- case "vector_similarity": {
1409
- if (inDisallowedBranch) {
1410
- throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
1411
- "Vector similarity predicates cannot be nested under OR or NOT. Use top-level AND combinations instead."
1412
- );
1413
- }
1414
- results.push(expr);
1415
- break;
1465
+ if (isMatch(expr)) {
1466
+ if (inDisallowedBranch) {
1467
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(rejectionMessage);
1416
1468
  }
1469
+ results.push(expr);
1470
+ return;
1471
+ }
1472
+ switch (expr.__type) {
1417
1473
  case "and": {
1418
- for (const predicate2 of expr.predicates) {
1419
- visit(predicate2, inDisallowedBranch);
1420
- }
1421
- break;
1474
+ for (const child of expr.predicates) visit(child, inDisallowedBranch);
1475
+ return;
1422
1476
  }
1423
1477
  case "or": {
1424
- for (const predicate2 of expr.predicates) {
1425
- visit(predicate2, true);
1426
- }
1427
- break;
1478
+ for (const child of expr.predicates) visit(child, true);
1479
+ return;
1428
1480
  }
1429
1481
  case "not": {
1430
1482
  visit(expr.predicate, true);
1431
- break;
1483
+ return;
1484
+ }
1485
+ case "comparison":
1486
+ case "string_op":
1487
+ case "null_check":
1488
+ case "between":
1489
+ case "array_op":
1490
+ case "object_op":
1491
+ case "aggregate_comparison":
1492
+ case "exists":
1493
+ case "in_subquery":
1494
+ case "vector_similarity":
1495
+ case "fulltext_match": {
1496
+ return;
1432
1497
  }
1433
1498
  }
1434
1499
  }
1435
- for (const pred of predicates) {
1436
- visit(pred.expression, false);
1437
- }
1500
+ for (const pred of predicates) visit(pred.expression, false);
1438
1501
  return results;
1439
1502
  }
1503
+ function extractVectorSimilarityPredicates(predicates) {
1504
+ return extractStructuralPredicates(
1505
+ predicates,
1506
+ (expr) => expr.__type === "vector_similarity",
1507
+ "Vector similarity predicates cannot be nested under OR or NOT. Use top-level AND combinations instead."
1508
+ );
1509
+ }
1510
+ function extractFulltextMatchPredicates(predicates) {
1511
+ return extractStructuralPredicates(
1512
+ predicates,
1513
+ (expr) => expr.__type === "fulltext_match",
1514
+ "Fulltext match predicates cannot be nested under OR or NOT. Use top-level AND combinations instead."
1515
+ );
1516
+ }
1440
1517
 
1441
1518
  // src/query/compiler/predicate-utils.ts
1442
1519
  var EMPTY_PREDICATES = [];
@@ -1495,6 +1572,10 @@ function getNodeKindsForAlias(ast, alias) {
1495
1572
  }
1496
1573
  throw new chunkO5PST6TB_cjs.CompilerInvariantError(`Unknown traversal source alias: ${alias}`);
1497
1574
  }
1575
+ function getHybridTargetAlias(vectorPredicate, fulltextPredicate) {
1576
+ if (!vectorPredicate || !fulltextPredicate) return void 0;
1577
+ return vectorPredicate.field.alias === fulltextPredicate.field.alias ? vectorPredicate.field.alias : void 0;
1578
+ }
1498
1579
 
1499
1580
  // src/query/compiler/emitter/plan-inspector.ts
1500
1581
  function collectPlanOperations(node, ops) {
@@ -1502,6 +1583,7 @@ function collectPlanOperations(node, ops) {
1502
1583
  switch (node.op) {
1503
1584
  case "aggregate":
1504
1585
  case "filter":
1586
+ case "fulltext_match":
1505
1587
  case "join":
1506
1588
  case "limit_offset":
1507
1589
  case "project":
@@ -1530,6 +1612,7 @@ function findUnaryNodeInProjectChain(rootNode, op) {
1530
1612
  switch (currentNode.op) {
1531
1613
  case "aggregate":
1532
1614
  case "filter":
1615
+ case "fulltext_match":
1533
1616
  case "join":
1534
1617
  case "limit_offset":
1535
1618
  case "recursive_expand":
@@ -1565,6 +1648,7 @@ function inspectProjectPlan(logicalPlan) {
1565
1648
  );
1566
1649
  return {
1567
1650
  hasAggregate: operations.has("aggregate"),
1651
+ hasFulltextMatch: operations.has("fulltext_match"),
1568
1652
  hasLimitOffset: operations.has("limit_offset"),
1569
1653
  hasRecursiveExpand: operations.has("recursive_expand"),
1570
1654
  hasSetOperation: operations.has("set_op"),
@@ -1616,6 +1700,7 @@ function findTopLevelLimitOffsetNode(rootNode) {
1616
1700
  switch (currentNode.op) {
1617
1701
  case "aggregate":
1618
1702
  case "filter":
1703
+ case "fulltext_match":
1619
1704
  case "join":
1620
1705
  case "project":
1621
1706
  case "recursive_expand":
@@ -1640,6 +1725,7 @@ function findTopLevelSortNode(rootNode) {
1640
1725
  switch (currentNode.op) {
1641
1726
  case "aggregate":
1642
1727
  case "filter":
1728
+ case "fulltext_match":
1643
1729
  case "join":
1644
1730
  case "limit_offset":
1645
1731
  case "project":
@@ -1774,16 +1860,16 @@ function assertStandardEmitterClauseAlignment(logicalPlan, input) {
1774
1860
  { component: "standard-emitter" }
1775
1861
  );
1776
1862
  }
1777
- const expectsOrderBy = planShape.hasSort || planShape.hasVectorKnn;
1863
+ const expectsOrderBy = planShape.hasSort || planShape.hasVectorKnn || planShape.hasFulltextMatch;
1778
1864
  if (expectsOrderBy && input.orderBy === void 0) {
1779
1865
  throw new chunkO5PST6TB_cjs.CompilerInvariantError(
1780
- "Standard SQL emitter expected ORDER BY clause for plan containing a sort or vector_knn node",
1866
+ "Standard SQL emitter expected ORDER BY clause for plan containing a sort, vector_knn, or fulltext_match node",
1781
1867
  { component: "standard-emitter" }
1782
1868
  );
1783
1869
  }
1784
1870
  if (!expectsOrderBy && input.orderBy !== void 0) {
1785
1871
  throw new chunkO5PST6TB_cjs.CompilerInvariantError(
1786
- "Standard SQL emitter received ORDER BY clause for a plan without sort or vector_knn nodes",
1872
+ "Standard SQL emitter received ORDER BY clause for a plan without sort, vector_knn, or fulltext_match nodes",
1787
1873
  { component: "standard-emitter" }
1788
1874
  );
1789
1875
  }
@@ -1822,6 +1908,62 @@ function emitStandardQuerySql(input) {
1822
1908
  return drizzleOrm.sql.join(parts, drizzleOrm.sql` `);
1823
1909
  }
1824
1910
 
1911
+ // src/query/ast.ts
1912
+ var DEFAULT_RRF_K = 60;
1913
+ var DEFAULT_RRF_WEIGHT = 1;
1914
+ function mergeEdgeKinds(traversal) {
1915
+ const inverse = traversal.inverseEdgeKinds;
1916
+ if (inverse === void 0 || inverse.length === 0) return traversal.edgeKinds;
1917
+ return [
1918
+ ...traversal.edgeKinds,
1919
+ ...inverse.filter((kind) => !traversal.edgeKinds.includes(kind))
1920
+ ];
1921
+ }
1922
+ var DEFAULT_TABLE_NAMES = {
1923
+ nodes: "typegraph_nodes",
1924
+ edges: "typegraph_edges",
1925
+ embeddings: "typegraph_node_embeddings",
1926
+ fulltext: "typegraph_node_fulltext"
1927
+ };
1928
+ var VALID_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_$]*$/i;
1929
+ function validateTableName(name, label) {
1930
+ if (!name || name.length === 0) {
1931
+ throw new chunkO5PST6TB_cjs.ConfigurationError(`${label} table name cannot be empty`);
1932
+ }
1933
+ if (name.length > chunkFQCQM3KG_cjs.MAX_PG_IDENTIFIER_LENGTH) {
1934
+ throw new chunkO5PST6TB_cjs.ConfigurationError(
1935
+ `${label} table name exceeds maximum length of ${chunkFQCQM3KG_cjs.MAX_PG_IDENTIFIER_LENGTH} characters`
1936
+ );
1937
+ }
1938
+ if (!VALID_IDENTIFIER_PATTERN.test(name)) {
1939
+ throw new chunkO5PST6TB_cjs.ConfigurationError(
1940
+ `${label} table name "${name}" is not a valid SQL identifier. Table names must start with a letter or underscore and contain only letters, digits, underscores, or dollar signs.`
1941
+ );
1942
+ }
1943
+ }
1944
+ function quoteIdentifier(name) {
1945
+ return `"${name.replaceAll('"', '""')}"`;
1946
+ }
1947
+ function createSqlSchema(names = {}) {
1948
+ const tables = { ...DEFAULT_TABLE_NAMES, ...names };
1949
+ validateTableName(tables.nodes, "nodes");
1950
+ validateTableName(tables.edges, "edges");
1951
+ validateTableName(tables.embeddings, "embeddings");
1952
+ validateTableName(tables.fulltext, "fulltext");
1953
+ return {
1954
+ tables,
1955
+ nodesTable: drizzleOrm.sql.raw(quoteIdentifier(tables.nodes)),
1956
+ edgesTable: drizzleOrm.sql.raw(quoteIdentifier(tables.edges)),
1957
+ embeddingsTable: drizzleOrm.sql.raw(quoteIdentifier(tables.embeddings)),
1958
+ fulltextTable: drizzleOrm.sql.raw(quoteIdentifier(tables.fulltext))
1959
+ };
1960
+ }
1961
+ var DEFAULT_SQL_SCHEMA = createSqlSchema();
1962
+ var ALIAS_CTE_PREFIX = "cte_";
1963
+ var EMBEDDINGS_CTE_ALIAS = "cte_embeddings";
1964
+ var FULLTEXT_CTE_ALIAS = "cte_fulltext";
1965
+ var HYBRID_CANDIDATES_CTE_ALIAS = "cte_relevance_candidates";
1966
+
1825
1967
  // src/query/compiler/typed-json-extract.ts
1826
1968
  function compileTypedJsonExtract(input) {
1827
1969
  const { column, dialect, pointer, valueType } = input;
@@ -1875,7 +2017,7 @@ var EDGE_COLUMNS = [
1875
2017
  "deleted_at"
1876
2018
  ];
1877
2019
  var EMPTY_REQUIRED_COLUMNS = /* @__PURE__ */ new Set();
1878
- function quoteIdentifier(identifier) {
2020
+ function quoteIdentifier2(identifier) {
1879
2021
  return drizzleOrm.sql.raw(`"${identifier.replaceAll('"', '""')}"`);
1880
2022
  }
1881
2023
  function shouldProjectColumn(requiredColumns, column, alwaysRequiredColumns) {
@@ -1929,6 +2071,21 @@ function compileColumnReference(tableAlias, column) {
1929
2071
  }
1930
2072
  return drizzleOrm.sql`${drizzleOrm.sql.raw(tableAlias)}.${drizzleOrm.sql.raw(column)}`;
1931
2073
  }
2074
+ function qualifyColumn(owner, name) {
2075
+ return drizzleOrm.sql`${quoteIdentifier2(owner)}.${quoteIdentifier2(name)}`;
2076
+ }
2077
+ var SCOPED_RELEVANCE_NODES_ALIAS = "scoped_relevance_nodes";
2078
+ function buildScopedNodeIdsSubquery(nodeAlias) {
2079
+ const cteAlias = `${ALIAS_CTE_PREFIX}${nodeAlias}`;
2080
+ return drizzleOrm.sql`
2081
+ (
2082
+ SELECT DISTINCT
2083
+ ${qualifyColumn(cteAlias, `${nodeAlias}_id`)} AS node_id,
2084
+ ${qualifyColumn(cteAlias, `${nodeAlias}_kind`)} AS node_kind
2085
+ FROM ${quoteIdentifier2(cteAlias)}
2086
+ ) AS ${drizzleOrm.sql.raw(SCOPED_RELEVANCE_NODES_ALIAS)}
2087
+ `;
2088
+ }
1932
2089
  function compileNodeSelectColumns(tableAlias, alias, requiredColumns) {
1933
2090
  return NODE_COLUMNS.filter(
1934
2091
  (column) => column === "id" || column === "kind" || shouldProjectColumn(requiredColumns, column)
@@ -2181,7 +2338,7 @@ function buildStandardProjection(input) {
2181
2338
  }
2182
2339
  const projectedFields = fields.map((field2) => {
2183
2340
  const source = compileProjectedSource(field2, dialect);
2184
- return drizzleOrm.sql`${source} AS ${quoteIdentifier(field2.outputName)}`;
2341
+ return drizzleOrm.sql`${source} AS ${quoteIdentifier2(field2.outputName)}`;
2185
2342
  });
2186
2343
  return drizzleOrm.sql.join(projectedFields, drizzleOrm.sql`, `);
2187
2344
  }
@@ -2201,23 +2358,67 @@ function compileSelectiveProjection(fields, dialect, ast, collapsedTraversalCteA
2201
2358
  const cteAlias = collapsedTraversalCteAlias ?? aliasToCte.get(field2.alias) ?? `cte_${field2.alias}`;
2202
2359
  if (field2.isSystemField) {
2203
2360
  const dbColumn = mapSelectiveSystemFieldToColumn(field2.field);
2204
- return drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier(field2.outputName)}`;
2361
+ return drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier2(field2.outputName)}`;
2205
2362
  }
2206
2363
  const propsColumn = `${field2.alias}_props`;
2207
2364
  const column = drizzleOrm.sql`${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(propsColumn)}`;
2208
- const pointer = chunkP5CNM325_cjs.jsonPointer([field2.field]);
2365
+ const pointer = chunkUVZHTBZ5_cjs.jsonPointer([field2.field]);
2209
2366
  const extracted = compileTypedJsonExtract({
2210
2367
  column,
2211
2368
  dialect,
2212
2369
  pointer,
2213
2370
  valueType: field2.valueType
2214
2371
  });
2215
- return drizzleOrm.sql`${extracted} AS ${quoteIdentifier(field2.outputName)}`;
2372
+ return drizzleOrm.sql`${extracted} AS ${quoteIdentifier2(field2.outputName)}`;
2216
2373
  });
2217
2374
  return drizzleOrm.sql.join(columns, drizzleOrm.sql`, `);
2218
2375
  }
2376
+ function buildRelevanceJoins(vectorPredicate, fulltextPredicate) {
2377
+ const hybridTargetAlias = getHybridTargetAlias(
2378
+ vectorPredicate,
2379
+ fulltextPredicate
2380
+ );
2381
+ if (hybridTargetAlias !== void 0) {
2382
+ return [
2383
+ buildHybridCandidateJoin(hybridTargetAlias),
2384
+ buildRelevanceJoin(EMBEDDINGS_CTE_ALIAS, hybridTargetAlias, "LEFT JOIN"),
2385
+ buildRelevanceJoin(FULLTEXT_CTE_ALIAS, hybridTargetAlias, "LEFT JOIN")
2386
+ ];
2387
+ }
2388
+ const joins = [];
2389
+ if (vectorPredicate) {
2390
+ joins.push(
2391
+ buildRelevanceJoin(EMBEDDINGS_CTE_ALIAS, vectorPredicate.field.alias)
2392
+ );
2393
+ }
2394
+ if (fulltextPredicate) {
2395
+ joins.push(
2396
+ buildRelevanceJoin(FULLTEXT_CTE_ALIAS, fulltextPredicate.field.alias)
2397
+ );
2398
+ }
2399
+ return joins;
2400
+ }
2401
+ function buildHybridCandidateJoin(nodeAlias) {
2402
+ const candidateCte = drizzleOrm.sql.raw(HYBRID_CANDIDATES_CTE_ALIAS);
2403
+ const node = drizzleOrm.sql.raw(`${ALIAS_CTE_PREFIX}${nodeAlias}`);
2404
+ const idColumn = drizzleOrm.sql.raw(`${nodeAlias}_id`);
2405
+ const kindColumn = drizzleOrm.sql.raw(`${nodeAlias}_kind`);
2406
+ return drizzleOrm.sql`INNER JOIN ${candidateCte} ON ${candidateCte}.node_id = ${node}.${idColumn} AND ${candidateCte}.node_kind = ${node}.${kindColumn}`;
2407
+ }
2408
+ function buildRelevanceJoin(cteAlias, nodeAlias, joinType = "INNER JOIN") {
2409
+ const cte = drizzleOrm.sql.raw(cteAlias);
2410
+ const node = drizzleOrm.sql.raw(`${ALIAS_CTE_PREFIX}${nodeAlias}`);
2411
+ const idColumn = drizzleOrm.sql.raw(`${nodeAlias}_id`);
2412
+ const kindColumn = drizzleOrm.sql.raw(`${nodeAlias}_kind`);
2413
+ return drizzleOrm.sql`${drizzleOrm.sql.raw(joinType)} ${cte} ON ${cte}.node_id = ${node}.${idColumn} AND ${cte}.node_kind = ${node}.${kindColumn}`;
2414
+ }
2219
2415
  function buildStandardFromClause(input) {
2220
- const { ast, collapsedTraversalCteAlias, vectorPredicate } = input;
2416
+ const {
2417
+ ast,
2418
+ collapsedTraversalCteAlias,
2419
+ fulltextPredicate,
2420
+ vectorPredicate
2421
+ } = input;
2221
2422
  if (collapsedTraversalCteAlias !== void 0) {
2222
2423
  return drizzleOrm.sql`FROM ${drizzleOrm.sql.raw(collapsedTraversalCteAlias)}`;
2223
2424
  }
@@ -2232,11 +2433,8 @@ function buildStandardFromClause(input) {
2232
2433
  drizzleOrm.sql`${drizzleOrm.sql.raw(joinType)} ${drizzleOrm.sql.raw(cteAlias)} ON ${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id = cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_id AND ${drizzleOrm.sql.raw(cteAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind = cte_${drizzleOrm.sql.raw(previousAlias)}.${drizzleOrm.sql.raw(previousAlias)}_kind`
2233
2434
  );
2234
2435
  }
2235
- if (vectorPredicate) {
2236
- const nodeAlias = vectorPredicate.field.alias;
2237
- joins.push(
2238
- drizzleOrm.sql`INNER JOIN cte_embeddings ON cte_embeddings.node_id = cte_${drizzleOrm.sql.raw(nodeAlias)}.${drizzleOrm.sql.raw(nodeAlias)}_id`
2239
- );
2436
+ for (const join of buildRelevanceJoins(vectorPredicate, fulltextPredicate)) {
2437
+ joins.push(join);
2240
2438
  }
2241
2439
  return joins.length === 0 ? fromClause : drizzleOrm.sql`${fromClause} ${drizzleOrm.sql.join(joins, drizzleOrm.sql` `)}`;
2242
2440
  }
@@ -2275,6 +2473,15 @@ function fieldRefKey(field2) {
2275
2473
  const pointer = field2.jsonPointer ?? "";
2276
2474
  return `${field2.alias}:${field2.path.join(".")}:${pointer}`;
2277
2475
  }
2476
+ function resolveEmbeddingFieldPath(field2) {
2477
+ if (field2.jsonPointer !== void 0 && field2.jsonPointer !== "" && field2.jsonPointer !== "/") {
2478
+ return field2.jsonPointer.replace(/^\//u, "").replaceAll("/", ".");
2479
+ }
2480
+ if (field2.path[0] === "props") {
2481
+ return field2.path.slice(1).join(".");
2482
+ }
2483
+ return field2.path.join(".");
2484
+ }
2278
2485
  function buildStandardGroupBy(input) {
2279
2486
  const { ast, dialect } = input;
2280
2487
  if (!ast.groupBy || ast.groupBy.fields.length === 0) {
@@ -2321,18 +2528,29 @@ function buildStandardHaving(input) {
2321
2528
  return drizzleOrm.sql`HAVING ${condition}`;
2322
2529
  }
2323
2530
  function buildStandardEmbeddingsCte(input) {
2324
- const { ctx, graphId, vectorPredicate } = input;
2531
+ const { ctx, graphId, nodeKinds, vectorPredicate } = input;
2325
2532
  const { dialect } = ctx;
2326
2533
  const { field: field2, metric, minScore, queryEmbedding } = vectorPredicate;
2327
- const fieldPath = field2.jsonPointer ?? (field2.path.length > 1 && field2.path[0] === "props" ? `/${field2.path.slice(1).join("/")}` : `/${field2.path.join("/")}`);
2534
+ const embeddingsTableName = ctx.schema.tables.embeddings;
2535
+ if (nodeKinds.length === 0) {
2536
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
2537
+ "Vector predicate must resolve to at least one node kind"
2538
+ );
2539
+ }
2540
+ const fieldPath = resolveEmbeddingFieldPath(field2);
2328
2541
  const distanceExpr = dialect.vectorDistance(
2329
- drizzleOrm.sql.raw("embedding"),
2542
+ qualifyColumn(embeddingsTableName, "embedding"),
2330
2543
  queryEmbedding,
2331
2544
  metric
2332
2545
  );
2546
+ const scopedNodes = buildScopedNodeIdsSubquery(field2.alias);
2333
2547
  const conditions = [
2334
- drizzleOrm.sql`graph_id = ${graphId}`,
2335
- drizzleOrm.sql`field_path = ${fieldPath}`
2548
+ drizzleOrm.sql`${qualifyColumn(embeddingsTableName, "graph_id")} = ${graphId}`,
2549
+ compileKindFilter(
2550
+ qualifyColumn(embeddingsTableName, "node_kind"),
2551
+ nodeKinds
2552
+ ),
2553
+ drizzleOrm.sql`${qualifyColumn(embeddingsTableName, "field_path")} = ${fieldPath}`
2336
2554
  ];
2337
2555
  if (minScore !== void 0) {
2338
2556
  conditions.push(
@@ -2341,14 +2559,29 @@ function buildStandardEmbeddingsCte(input) {
2341
2559
  }
2342
2560
  const scoreExpr = compileVectorScoreExpression(distanceExpr, metric);
2343
2561
  return drizzleOrm.sql`
2344
- cte_embeddings AS (
2562
+ ${drizzleOrm.sql.raw(EMBEDDINGS_CTE_ALIAS)} AS (
2345
2563
  SELECT
2346
2564
  node_id,
2347
- ${distanceExpr} AS distance,
2348
- ${scoreExpr} AS score
2349
- FROM ${ctx.schema.embeddingsTable}
2350
- WHERE ${drizzleOrm.sql.join(conditions, drizzleOrm.sql` AND `)}
2351
- ORDER BY ${distanceExpr} ASC
2565
+ node_kind,
2566
+ distance,
2567
+ score,
2568
+ ROW_NUMBER() OVER (ORDER BY distance ASC) AS ord
2569
+ FROM (
2570
+ SELECT
2571
+ ${qualifyColumn(embeddingsTableName, "node_id")} AS node_id,
2572
+ ${qualifyColumn(embeddingsTableName, "node_kind")} AS node_kind,
2573
+ ${distanceExpr} AS distance,
2574
+ ${scoreExpr} AS score
2575
+ FROM ${ctx.schema.embeddingsTable}
2576
+ INNER JOIN ${scopedNodes}
2577
+ ON ${drizzleOrm.sql.raw(`${SCOPED_RELEVANCE_NODES_ALIAS}.node_id`)} =
2578
+ ${qualifyColumn(embeddingsTableName, "node_id")}
2579
+ AND ${drizzleOrm.sql.raw(`${SCOPED_RELEVANCE_NODES_ALIAS}.node_kind`)} =
2580
+ ${qualifyColumn(embeddingsTableName, "node_kind")}
2581
+ WHERE ${drizzleOrm.sql.join(conditions, drizzleOrm.sql` AND `)}
2582
+ ORDER BY distance ASC
2583
+ LIMIT ${vectorPredicate.limit}
2584
+ ) AS vec_inner
2352
2585
  )
2353
2586
  `;
2354
2587
  }
@@ -2378,37 +2611,130 @@ function compileVectorMinScoreCondition(distanceExpr, metric, minScore) {
2378
2611
  }
2379
2612
  }
2380
2613
  }
2381
- function buildStandardVectorOrderBy(input) {
2382
- const { ast, dialect } = input;
2383
- const distanceOrder = drizzleOrm.sql`cte_embeddings.distance ASC`;
2384
- const additionalOrders = [];
2385
- if (ast.orderBy && ast.orderBy.length > 0) {
2386
- const aliasToCte = buildAliasToCteMap(ast);
2387
- for (const orderSpec of ast.orderBy) {
2388
- const valueType = orderSpec.field.valueType;
2389
- if (valueType === "array" || valueType === "object") {
2390
- throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
2391
- "Ordering by JSON arrays or objects is not supported"
2392
- );
2393
- }
2394
- const cteAlias = aliasToCte.get(orderSpec.field.alias) ?? `cte_${orderSpec.field.alias}`;
2395
- const field2 = compileFieldValue(
2396
- orderSpec.field,
2397
- dialect,
2398
- valueType,
2399
- cteAlias
2400
- );
2401
- const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
2402
- const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
2403
- const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
2404
- additionalOrders.push(
2405
- drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
2406
- drizzleOrm.sql`${field2} ${direction}`
2614
+ function buildStandardFulltextCte(input) {
2615
+ const { ctx, fulltextPredicate, graphId, nodeKinds } = input;
2616
+ const { dialect, schema } = ctx;
2617
+ const { query, mode, language, limit, minScore } = fulltextPredicate;
2618
+ if (nodeKinds.length === 0) {
2619
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
2620
+ "Fulltext predicate must resolve to at least one node kind"
2621
+ );
2622
+ }
2623
+ const tableName = schema.tables.fulltext;
2624
+ const scopedNodes = buildScopedNodeIdsSubquery(fulltextPredicate.field.alias);
2625
+ const fulltextStrategy = dialect.fulltext;
2626
+ if (fulltextStrategy === void 0) {
2627
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
2628
+ `Fulltext match predicates are not supported for dialect "${dialect.name}"`
2629
+ );
2630
+ }
2631
+ const matchCondition = fulltextStrategy.matchCondition(
2632
+ tableName,
2633
+ query,
2634
+ mode,
2635
+ language
2636
+ );
2637
+ const rankExpression = fulltextStrategy.rankExpression(
2638
+ tableName,
2639
+ query,
2640
+ mode,
2641
+ language
2642
+ );
2643
+ const conditions = [
2644
+ drizzleOrm.sql`${qualifyColumn(tableName, "graph_id")} = ${graphId}`,
2645
+ compileKindFilter(qualifyColumn(tableName, "node_kind"), nodeKinds),
2646
+ matchCondition
2647
+ ];
2648
+ if (minScore !== void 0) {
2649
+ conditions.push(drizzleOrm.sql`${rankExpression} >= ${minScore}`);
2650
+ }
2651
+ return drizzleOrm.sql`
2652
+ ${drizzleOrm.sql.raw(FULLTEXT_CTE_ALIAS)} AS (
2653
+ SELECT
2654
+ node_id,
2655
+ node_kind,
2656
+ rank,
2657
+ ROW_NUMBER() OVER (ORDER BY rank DESC, node_id ASC) AS ord
2658
+ FROM (
2659
+ SELECT
2660
+ ${qualifyColumn(tableName, "node_id")} AS node_id,
2661
+ ${qualifyColumn(tableName, "node_kind")} AS node_kind,
2662
+ ${rankExpression} AS rank
2663
+ FROM ${schema.fulltextTable}
2664
+ INNER JOIN ${scopedNodes}
2665
+ ON ${drizzleOrm.sql.raw(`${SCOPED_RELEVANCE_NODES_ALIAS}.node_id`)} =
2666
+ ${qualifyColumn(tableName, "node_id")}
2667
+ AND ${drizzleOrm.sql.raw(`${SCOPED_RELEVANCE_NODES_ALIAS}.node_kind`)} =
2668
+ ${qualifyColumn(tableName, "node_kind")}
2669
+ WHERE ${drizzleOrm.sql.join(conditions, drizzleOrm.sql` AND `)}
2670
+ ORDER BY rank DESC, ${qualifyColumn(tableName, "node_id")} ASC
2671
+ LIMIT ${limit}
2672
+ ) AS fts_inner
2673
+ )
2674
+ `;
2675
+ }
2676
+ function buildStandardHybridCandidateCte() {
2677
+ return drizzleOrm.sql`
2678
+ ${drizzleOrm.sql.raw(HYBRID_CANDIDATES_CTE_ALIAS)} AS (
2679
+ SELECT node_id, node_kind FROM ${drizzleOrm.sql.raw(EMBEDDINGS_CTE_ALIAS)}
2680
+ UNION
2681
+ SELECT node_id, node_kind FROM ${drizzleOrm.sql.raw(FULLTEXT_CTE_ALIAS)}
2682
+ )
2683
+ `;
2684
+ }
2685
+ function compileUserOrderBy(ast, dialect) {
2686
+ if (!ast.orderBy || ast.orderBy.length === 0) return [];
2687
+ const aliasToCte = buildAliasToCteMap(ast);
2688
+ const fragments = [];
2689
+ for (const orderSpec of ast.orderBy) {
2690
+ const valueType = orderSpec.field.valueType;
2691
+ if (valueType === "array" || valueType === "object") {
2692
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
2693
+ "Ordering by JSON arrays or objects is not supported"
2407
2694
  );
2408
2695
  }
2696
+ const cteAlias = aliasToCte.get(orderSpec.field.alias) ?? `${ALIAS_CTE_PREFIX}${orderSpec.field.alias}`;
2697
+ const field2 = compileFieldValue(
2698
+ orderSpec.field,
2699
+ dialect,
2700
+ valueType,
2701
+ cteAlias
2702
+ );
2703
+ const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
2704
+ const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
2705
+ const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
2706
+ fragments.push(
2707
+ drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
2708
+ drizzleOrm.sql`${field2} ${direction}`
2709
+ );
2409
2710
  }
2410
- const allOrders = [distanceOrder, ...additionalOrders];
2411
- return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(allOrders, drizzleOrm.sql`, `)}`;
2711
+ return fragments;
2712
+ }
2713
+ function buildStandardFulltextOrderBy(input) {
2714
+ const { ast, dialect } = input;
2715
+ const rankOrder = drizzleOrm.sql.raw(`${FULLTEXT_CTE_ALIAS}.rank DESC`);
2716
+ const userOrders = compileUserOrderBy(ast, dialect);
2717
+ return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join([rankOrder, ...userOrders], drizzleOrm.sql`, `)}`;
2718
+ }
2719
+ function buildStandardHybridRrfOrderBy(input) {
2720
+ const { ast, dialect, fusion } = input;
2721
+ const k = fusion?.k ?? DEFAULT_RRF_K;
2722
+ const vectorWeight = fusion?.weights?.vector ?? DEFAULT_RRF_WEIGHT;
2723
+ const fulltextWeight = fusion?.weights?.fulltext ?? DEFAULT_RRF_WEIGHT;
2724
+ const rrfOrder = drizzleOrm.sql.raw(
2725
+ `(COALESCE(${vectorWeight} / (${k} + ${EMBEDDINGS_CTE_ALIAS}.ord), 0) + COALESCE(${fulltextWeight} / (${k} + ${FULLTEXT_CTE_ALIAS}.ord), 0)) DESC`
2726
+ );
2727
+ const idTiebreak = drizzleOrm.sql.raw(
2728
+ `COALESCE(${FULLTEXT_CTE_ALIAS}.node_id, ${EMBEDDINGS_CTE_ALIAS}.node_id) ASC`
2729
+ );
2730
+ const userOrders = compileUserOrderBy(ast, dialect);
2731
+ return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join([rrfOrder, ...userOrders, idTiebreak], drizzleOrm.sql`, `)}`;
2732
+ }
2733
+ function buildStandardVectorOrderBy(input) {
2734
+ const { ast, dialect } = input;
2735
+ const distanceOrder = drizzleOrm.sql.raw(`${EMBEDDINGS_CTE_ALIAS}.distance ASC`);
2736
+ const userOrders = compileUserOrderBy(ast, dialect);
2737
+ return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join([distanceOrder, ...userOrders], drizzleOrm.sql`, `)}`;
2412
2738
  }
2413
2739
  function buildLimitOffsetClause(input) {
2414
2740
  const { limit, offset } = input;
@@ -2422,6 +2748,74 @@ function buildLimitOffsetClause(input) {
2422
2748
  return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
2423
2749
  }
2424
2750
 
2751
+ // src/query/compiler/passes/fulltext.ts
2752
+ function runFulltextPredicatePass(ast, dialect) {
2753
+ const fulltextPredicates = extractFulltextMatchPredicates(ast.predicates);
2754
+ if (fulltextPredicates.length > 1) {
2755
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
2756
+ "Multiple fulltext match predicates in a single query are not supported"
2757
+ );
2758
+ }
2759
+ const fulltextPredicate = fulltextPredicates[0];
2760
+ if (fulltextPredicate === void 0) {
2761
+ return { fulltextPredicate: void 0 };
2762
+ }
2763
+ if (!dialect.capabilities.supportsFulltext) {
2764
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
2765
+ `Fulltext match predicates are not supported for dialect "${dialect.name}"`
2766
+ );
2767
+ }
2768
+ const strategy = dialect.fulltext;
2769
+ if (strategy === void 0) {
2770
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
2771
+ `Dialect "${dialect.name}" advertises fulltext support but has no strategy configured`
2772
+ );
2773
+ }
2774
+ if (!strategy.supportedModes.includes(fulltextPredicate.mode)) {
2775
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
2776
+ `Fulltext query mode "${fulltextPredicate.mode}" is not supported by the "${strategy.name}" strategy. Supported modes: ${strategy.supportedModes.join(", ")}.`
2777
+ );
2778
+ }
2779
+ if (!Number.isFinite(fulltextPredicate.limit) || fulltextPredicate.limit <= 0) {
2780
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
2781
+ `Fulltext match limit must be a positive finite number, got ${String(fulltextPredicate.limit)}`
2782
+ );
2783
+ }
2784
+ const { minScore } = fulltextPredicate;
2785
+ if (minScore !== void 0 && !Number.isFinite(minScore)) {
2786
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
2787
+ `Fulltext minScore must be a finite number, got ${String(minScore)}`
2788
+ );
2789
+ }
2790
+ return { fulltextPredicate };
2791
+ }
2792
+ function resolveFulltextAwareLimit(astLimit, fulltextPredicate) {
2793
+ if (fulltextPredicate === void 0) {
2794
+ return astLimit;
2795
+ }
2796
+ if (astLimit === void 0) {
2797
+ return fulltextPredicate.limit;
2798
+ }
2799
+ return Math.min(astLimit, fulltextPredicate.limit);
2800
+ }
2801
+
2802
+ // src/query/compiler/passes/fusion.ts
2803
+ function runFusionConfigPass(ast, vectorPredicate, fulltextPredicate) {
2804
+ const fusion = ast.fusion;
2805
+ if (fusion === void 0) return { fusion: void 0 };
2806
+ if (vectorPredicate === void 0 || fulltextPredicate === void 0) {
2807
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
2808
+ `.fuseWith() requires both a .similarTo() and a .$fulltext.matches() predicate in the same query. Remove .fuseWith(), or add the missing predicate.`
2809
+ );
2810
+ }
2811
+ if (vectorPredicate.field.alias !== fulltextPredicate.field.alias) {
2812
+ throw new chunkO5PST6TB_cjs.UnsupportedPredicateError(
2813
+ `.fuseWith() requires .similarTo() and .$fulltext.matches() to target the same node alias. Got vector=${vectorPredicate.field.alias}, fulltext=${fulltextPredicate.field.alias}.`
2814
+ );
2815
+ }
2816
+ return { fusion };
2817
+ }
2818
+
2425
2819
  // src/query/compiler/passes/recursive.ts
2426
2820
  function runRecursiveTraversalSelectionPass(ast) {
2427
2821
  const variableLengthTraversal = ast.traversals.find(
@@ -2691,6 +3085,14 @@ function lowerStandardQueryToLogicalPlanNode(input) {
2691
3085
  predicate: input.vectorPredicate
2692
3086
  };
2693
3087
  }
3088
+ if (input.fulltextPredicate !== void 0) {
3089
+ currentNode = {
3090
+ id: nextPlanNodeId(),
3091
+ input: currentNode,
3092
+ op: "fulltext_match",
3093
+ predicate: input.fulltextPredicate
3094
+ };
3095
+ }
2694
3096
  return appendAggregateSortLimitAndProjectNodes(
2695
3097
  currentNode,
2696
3098
  ast,
@@ -2768,17 +3170,30 @@ function lowerComposableQueryToLogicalPlanNode(query, dialect, graphId, nextPlan
2768
3170
  nextPlanNodeId
2769
3171
  });
2770
3172
  }
3173
+ const dialectAdapter = chunkFQCQM3KG_cjs.getDialect(dialect);
2771
3174
  const vectorPredicate = runVectorPredicatePass(
2772
3175
  query,
2773
- chunkMME3H4ZF_cjs.getDialect(dialect)
3176
+ dialectAdapter
2774
3177
  ).vectorPredicate;
2775
- const effectiveLimit = resolveVectorAwareLimit(query.limit, vectorPredicate);
3178
+ const fulltextPredicate = runFulltextPredicatePass(
3179
+ query,
3180
+ dialectAdapter
3181
+ ).fulltextPredicate;
3182
+ const limitAfterVector = resolveVectorAwareLimit(
3183
+ query.limit,
3184
+ vectorPredicate
3185
+ );
3186
+ const effectiveLimit = resolveFulltextAwareLimit(
3187
+ limitAfterVector,
3188
+ fulltextPredicate
3189
+ );
2776
3190
  const loweringInput = {
2777
3191
  ast: query,
2778
3192
  graphId,
2779
3193
  nextPlanNodeId,
2780
3194
  ...effectiveLimit === void 0 ? {} : { effectiveLimit },
2781
- ...vectorPredicate === void 0 ? {} : { vectorPredicate }
3195
+ ...vectorPredicate === void 0 ? {} : { vectorPredicate },
3196
+ ...fulltextPredicate === void 0 ? {} : { fulltextPredicate }
2782
3197
  };
2783
3198
  return lowerStandardQueryToLogicalPlanNode(loweringInput);
2784
3199
  }
@@ -3285,10 +3700,10 @@ function compileRecursiveProjection(ast, traversal, dialect) {
3285
3700
  drizzleOrm.sql`${drizzleOrm.sql.raw(nodeAlias)}_deleted_at`
3286
3701
  ];
3287
3702
  if (vl.depthAlias !== void 0) {
3288
- fields.push(drizzleOrm.sql`depth AS ${quoteIdentifier(vl.depthAlias)}`);
3703
+ fields.push(drizzleOrm.sql`depth AS ${quoteIdentifier2(vl.depthAlias)}`);
3289
3704
  }
3290
3705
  if (vl.pathAlias !== void 0) {
3291
- fields.push(drizzleOrm.sql`path AS ${quoteIdentifier(vl.pathAlias)}`);
3706
+ fields.push(drizzleOrm.sql`path AS ${quoteIdentifier2(vl.pathAlias)}`);
3292
3707
  }
3293
3708
  return drizzleOrm.sql.join(fields, drizzleOrm.sql`, `);
3294
3709
  }
@@ -3302,23 +3717,23 @@ function compileRecursiveSelectiveProjection(fields, ast, traversal, dialect) {
3302
3717
  }
3303
3718
  if (field2.isSystemField) {
3304
3719
  const dbColumn = mapSelectiveSystemFieldToColumn(field2.field);
3305
- return drizzleOrm.sql`${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier(field2.outputName)}`;
3720
+ return drizzleOrm.sql`${drizzleOrm.sql.raw(`${field2.alias}_${dbColumn}`)} AS ${quoteIdentifier2(field2.outputName)}`;
3306
3721
  }
3307
3722
  const column = drizzleOrm.sql.raw(`${field2.alias}_props`);
3308
3723
  const extracted = compileTypedJsonExtract({
3309
3724
  column,
3310
3725
  dialect,
3311
- pointer: chunkP5CNM325_cjs.jsonPointer([field2.field]),
3726
+ pointer: chunkUVZHTBZ5_cjs.jsonPointer([field2.field]),
3312
3727
  valueType: field2.valueType
3313
3728
  });
3314
- return drizzleOrm.sql`${extracted} AS ${quoteIdentifier(field2.outputName)}`;
3729
+ return drizzleOrm.sql`${extracted} AS ${quoteIdentifier2(field2.outputName)}`;
3315
3730
  });
3316
3731
  const vl = traversal.variableLength;
3317
3732
  if (vl.depthAlias !== void 0) {
3318
- columns.push(drizzleOrm.sql`depth AS ${quoteIdentifier(vl.depthAlias)}`);
3733
+ columns.push(drizzleOrm.sql`depth AS ${quoteIdentifier2(vl.depthAlias)}`);
3319
3734
  }
3320
3735
  if (vl.pathAlias !== void 0) {
3321
- columns.push(drizzleOrm.sql`path AS ${quoteIdentifier(vl.pathAlias)}`);
3736
+ columns.push(drizzleOrm.sql`path AS ${quoteIdentifier2(vl.pathAlias)}`);
3322
3737
  }
3323
3738
  return drizzleOrm.sql.join(columns, drizzleOrm.sql`, `);
3324
3739
  }
@@ -3355,43 +3770,6 @@ function compileLimitOffset(ast) {
3355
3770
  }
3356
3771
  return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
3357
3772
  }
3358
- var DEFAULT_TABLE_NAMES = {
3359
- nodes: "typegraph_nodes",
3360
- edges: "typegraph_edges",
3361
- embeddings: "typegraph_node_embeddings"
3362
- };
3363
- var VALID_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_$]*$/i;
3364
- function validateTableName(name, label) {
3365
- if (!name || name.length === 0) {
3366
- throw new chunkO5PST6TB_cjs.ConfigurationError(`${label} table name cannot be empty`);
3367
- }
3368
- if (name.length > chunkMME3H4ZF_cjs.MAX_PG_IDENTIFIER_LENGTH) {
3369
- throw new chunkO5PST6TB_cjs.ConfigurationError(
3370
- `${label} table name exceeds maximum length of ${chunkMME3H4ZF_cjs.MAX_PG_IDENTIFIER_LENGTH} characters`
3371
- );
3372
- }
3373
- if (!VALID_IDENTIFIER_PATTERN.test(name)) {
3374
- throw new chunkO5PST6TB_cjs.ConfigurationError(
3375
- `${label} table name "${name}" is not a valid SQL identifier. Table names must start with a letter or underscore and contain only letters, digits, underscores, or dollar signs.`
3376
- );
3377
- }
3378
- }
3379
- function quoteIdentifier2(name) {
3380
- return `"${name.replaceAll('"', '""')}"`;
3381
- }
3382
- function createSqlSchema(names = {}) {
3383
- const tables = { ...DEFAULT_TABLE_NAMES, ...names };
3384
- validateTableName(tables.nodes, "nodes");
3385
- validateTableName(tables.edges, "edges");
3386
- validateTableName(tables.embeddings, "embeddings");
3387
- return {
3388
- tables,
3389
- nodesTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.nodes)),
3390
- edgesTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.edges)),
3391
- embeddingsTable: drizzleOrm.sql.raw(quoteIdentifier2(tables.embeddings))
3392
- };
3393
- }
3394
- var DEFAULT_SQL_SCHEMA = createSqlSchema();
3395
3773
 
3396
3774
  // src/query/execution/value-decoder.ts
3397
3775
  function nullToUndefined(value) {
@@ -3449,6 +3827,116 @@ function decodeByValueType(value, valueType) {
3449
3827
  }
3450
3828
  }
3451
3829
  }
3830
+ function buildReachableCte(options) {
3831
+ const trackPath = options.cyclePolicy === "prevent" || options.includePath;
3832
+ const edgeKindFilter = compileKindFilter(
3833
+ drizzleOrm.sql.raw("e.kind"),
3834
+ options.edgeKinds
3835
+ );
3836
+ const currentTimestamp = options.dialect.currentTimestamp();
3837
+ const nodeTemporalFilter = compileTemporalFilter({
3838
+ mode: options.temporalMode,
3839
+ asOf: options.asOf,
3840
+ tableAlias: "n",
3841
+ currentTimestamp
3842
+ });
3843
+ const edgeTemporalFilter = compileTemporalFilter({
3844
+ mode: options.temporalMode,
3845
+ asOf: options.asOf,
3846
+ tableAlias: "e",
3847
+ currentTimestamp
3848
+ });
3849
+ const initialPath = trackPath ? options.dialect.initializePath(drizzleOrm.sql.raw("n.id")) : void 0;
3850
+ const pathExtension = trackPath ? options.dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
3851
+ const cycleCheck = options.cyclePolicy === "prevent" ? options.dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
3852
+ const baseColumns = [drizzleOrm.sql`n.id`, drizzleOrm.sql`n.kind`, drizzleOrm.sql`0 AS depth`];
3853
+ if (initialPath !== void 0) {
3854
+ baseColumns.push(drizzleOrm.sql`${initialPath} AS path`);
3855
+ }
3856
+ const baseCase = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(baseColumns, drizzleOrm.sql`, `)} FROM ${options.schema.nodesTable} n WHERE n.graph_id = ${options.graphId} AND n.id = ${options.sourceId} AND ${nodeTemporalFilter}`;
3857
+ const recursiveColumns = [
3858
+ drizzleOrm.sql`n.id`,
3859
+ drizzleOrm.sql`n.kind`,
3860
+ drizzleOrm.sql`r.depth + 1 AS depth`
3861
+ ];
3862
+ if (pathExtension !== void 0) {
3863
+ recursiveColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
3864
+ }
3865
+ const recursiveWhere = [
3866
+ drizzleOrm.sql`e.graph_id = ${options.graphId}`,
3867
+ edgeKindFilter,
3868
+ edgeTemporalFilter,
3869
+ nodeTemporalFilter,
3870
+ drizzleOrm.sql`r.depth < ${options.maxHops}`
3871
+ ];
3872
+ if (cycleCheck !== void 0) recursiveWhere.push(cycleCheck);
3873
+ const forceWorktableOuterJoinOrder = options.dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
3874
+ const recursiveCase = compileRecursiveBranch({
3875
+ recursiveColumns,
3876
+ whereClauses: recursiveWhere,
3877
+ direction: options.direction,
3878
+ forceWorktableOuterJoinOrder,
3879
+ schema: options.schema
3880
+ });
3881
+ return drizzleOrm.sql`WITH RECURSIVE reachable AS (${baseCase} UNION ALL ${recursiveCase})`;
3882
+ }
3883
+ function compileRecursiveBranch(options) {
3884
+ const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join([...options.recursiveColumns], drizzleOrm.sql`, `)}`;
3885
+ switch (options.direction) {
3886
+ case "out": {
3887
+ return buildDirectionalBranch({
3888
+ selectClause,
3889
+ whereClauses: options.whereClauses,
3890
+ joinField: "from_id",
3891
+ targetField: "to_id",
3892
+ targetKindField: "to_kind",
3893
+ forceWorktableOuterJoinOrder: options.forceWorktableOuterJoinOrder,
3894
+ schema: options.schema
3895
+ });
3896
+ }
3897
+ case "in": {
3898
+ return buildDirectionalBranch({
3899
+ selectClause,
3900
+ whereClauses: options.whereClauses,
3901
+ joinField: "to_id",
3902
+ targetField: "from_id",
3903
+ targetKindField: "from_kind",
3904
+ forceWorktableOuterJoinOrder: options.forceWorktableOuterJoinOrder,
3905
+ schema: options.schema
3906
+ });
3907
+ }
3908
+ case "both": {
3909
+ return buildBidirectionalBranch({
3910
+ selectClause,
3911
+ whereClauses: options.whereClauses,
3912
+ forceWorktableOuterJoinOrder: options.forceWorktableOuterJoinOrder,
3913
+ schema: options.schema
3914
+ });
3915
+ }
3916
+ }
3917
+ }
3918
+ function buildDirectionalBranch(options) {
3919
+ const nodeJoin = drizzleOrm.sql`JOIN ${options.schema.nodesTable} n ON n.graph_id = e.graph_id AND n.id = e.${drizzleOrm.sql.raw(options.targetField)} AND n.kind = e.${drizzleOrm.sql.raw(options.targetKindField)}`;
3920
+ if (options.forceWorktableOuterJoinOrder) {
3921
+ const allWhere = [
3922
+ ...options.whereClauses,
3923
+ drizzleOrm.sql`e.${drizzleOrm.sql.raw(options.joinField)} = r.id`
3924
+ ];
3925
+ return drizzleOrm.sql`${options.selectClause} FROM reachable r CROSS JOIN ${options.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
3926
+ }
3927
+ return drizzleOrm.sql`${options.selectClause} FROM reachable r JOIN ${options.schema.edgesTable} e ON e.${drizzleOrm.sql.raw(options.joinField)} = r.id ${nodeJoin} WHERE ${drizzleOrm.sql.join([...options.whereClauses], drizzleOrm.sql` AND `)}`;
3928
+ }
3929
+ function buildBidirectionalBranch(options) {
3930
+ const nodeJoin = drizzleOrm.sql`JOIN ${options.schema.nodesTable} n ON n.graph_id = e.graph_id AND ((e.to_id = r.id AND n.id = e.from_id AND n.kind = e.from_kind) OR (e.from_id = r.id AND n.id = e.to_id AND n.kind = e.to_kind))`;
3931
+ if (options.forceWorktableOuterJoinOrder) {
3932
+ const allWhere = [
3933
+ ...options.whereClauses,
3934
+ drizzleOrm.sql`(e.from_id = r.id OR e.to_id = r.id)`
3935
+ ];
3936
+ return drizzleOrm.sql`${options.selectClause} FROM reachable r CROSS JOIN ${options.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
3937
+ }
3938
+ return drizzleOrm.sql`${options.selectClause} FROM reachable r JOIN ${options.schema.edgesTable} e ON (e.from_id = r.id OR e.to_id = r.id) ${nodeJoin} WHERE ${drizzleOrm.sql.join([...options.whereClauses], drizzleOrm.sql` AND `)}`;
3939
+ }
3452
3940
 
3453
3941
  // src/store/row-mappers.ts
3454
3942
  function nullToUndefined2(value) {
@@ -3512,9 +4000,9 @@ function truncateToBytes(value, maxBytes) {
3512
4000
  }
3513
4001
  function projectionAlias(entityPrefix, kind, field2) {
3514
4002
  const prefix = entityPrefix === "node" ? "sg_n" : "sg_e";
3515
- const hash = chunkMME3H4ZF_cjs.fnv1aBase36(`${kind}\0${field2}`);
4003
+ const hash = chunkFQCQM3KG_cjs.fnv1aBase36(`${kind}\0${field2}`);
3516
4004
  const fixedBytes = prefix.length + 1 + 1 + hash.length;
3517
- const maxKindBytes = chunkMME3H4ZF_cjs.MAX_PG_IDENTIFIER_LENGTH - fixedBytes;
4005
+ const maxKindBytes = chunkFQCQM3KG_cjs.MAX_PG_IDENTIFIER_LENGTH - fixedBytes;
3518
4006
  const truncatedKind = truncateToBytes(kind, maxKindBytes);
3519
4007
  return `${prefix}_${truncatedKind}_${hash}`;
3520
4008
  }
@@ -3539,6 +4027,8 @@ async function executeSubgraph(params) {
3539
4027
  excludeRoot: options.excludeRoot ?? false,
3540
4028
  direction: options.direction ?? "out",
3541
4029
  cyclePolicy: options.cyclePolicy ?? "prevent",
4030
+ temporalMode: options.temporalMode ?? params.graph.defaults.temporalMode,
4031
+ asOf: options.asOf,
3542
4032
  dialect: params.dialect,
3543
4033
  schema: params.schema ?? DEFAULT_SQL_SCHEMA,
3544
4034
  backend: params.backend
@@ -3556,7 +4046,19 @@ async function executeSubgraph(params) {
3556
4046
  (kind, field2) => schemaIntrospector.getEdgeFieldTypeInfo(kind, field2),
3557
4047
  "edge"
3558
4048
  );
3559
- const reachableCte = buildReachableCte(ctx);
4049
+ const reachableCte = buildReachableCte({
4050
+ graphId: ctx.graphId,
4051
+ sourceId: ctx.rootId,
4052
+ edgeKinds: ctx.edgeKinds,
4053
+ maxHops: ctx.maxDepth,
4054
+ direction: ctx.direction,
4055
+ cyclePolicy: ctx.cyclePolicy,
4056
+ includePath: false,
4057
+ temporalMode: ctx.temporalMode,
4058
+ ...ctx.asOf !== void 0 && { asOf: ctx.asOf },
4059
+ dialect: ctx.dialect,
4060
+ schema: ctx.schema
4061
+ });
3560
4062
  const includedIdsCte = buildIncludedIdsCte(ctx);
3561
4063
  const [nodeRows, edgeRows] = await Promise.all([
3562
4064
  fetchSubgraphNodes(ctx, reachableCte, includedIdsCte, nodeProjectionPlan),
@@ -3572,12 +4074,7 @@ async function executeSubgraph(params) {
3572
4074
  for (const row of edgeRows) {
3573
4075
  const edge = mapSubgraphEdgeRow(row, edgeProjectionPlan);
3574
4076
  insertAdjacencyEntry(adjacency, edge.fromId, edge.kind, edge);
3575
- insertAdjacencyEntry(
3576
- reverseAdjacency,
3577
- edge.toId,
3578
- edge.kind,
3579
- edge
3580
- );
4077
+ insertAdjacencyEntry(reverseAdjacency, edge.toId, edge.kind, edge);
3581
4078
  }
3582
4079
  const root = nodesMap.get(ctx.rootId);
3583
4080
  return {
@@ -3603,7 +4100,7 @@ function getSubgraphSchemaIntrospector(graph) {
3603
4100
  { schema: definition.type.schema }
3604
4101
  ])
3605
4102
  );
3606
- const introspector = chunkP5CNM325_cjs.createSchemaIntrospector(nodeKinds, edgeKinds);
4103
+ const introspector = chunkUVZHTBZ5_cjs.createSchemaIntrospector(nodeKinds, edgeKinds);
3607
4104
  introspectorCache.set(graph, introspector);
3608
4105
  return introspector;
3609
4106
  }
@@ -3654,79 +4151,6 @@ function getIncludedNodeKinds(graph, includeKinds) {
3654
4151
  function dedupeStrings(values) {
3655
4152
  return [...new Set(values)];
3656
4153
  }
3657
- function buildReachableCte(ctx) {
3658
- const shouldTrackPath = ctx.cyclePolicy === "prevent";
3659
- const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
3660
- const initialPath = shouldTrackPath ? ctx.dialect.initializePath(drizzleOrm.sql.raw("n.id")) : void 0;
3661
- const pathExtension = shouldTrackPath ? ctx.dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
3662
- const cycleCheck = shouldTrackPath ? ctx.dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
3663
- const baseColumns = [drizzleOrm.sql`n.id`, drizzleOrm.sql`n.kind`, drizzleOrm.sql`0 AS depth`];
3664
- if (initialPath !== void 0) {
3665
- baseColumns.push(drizzleOrm.sql`${initialPath} AS path`);
3666
- }
3667
- const baseCase = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(baseColumns, drizzleOrm.sql`, `)} FROM ${ctx.schema.nodesTable} n WHERE n.graph_id = ${ctx.graphId} AND n.id = ${ctx.rootId} AND n.deleted_at IS NULL`;
3668
- const recursiveColumns = [
3669
- drizzleOrm.sql`n.id`,
3670
- drizzleOrm.sql`n.kind`,
3671
- drizzleOrm.sql`r.depth + 1 AS depth`
3672
- ];
3673
- if (pathExtension !== void 0) {
3674
- recursiveColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
3675
- }
3676
- const recursiveWhereClauses = [
3677
- drizzleOrm.sql`e.graph_id = ${ctx.graphId}`,
3678
- edgeKindFilter,
3679
- drizzleOrm.sql`e.deleted_at IS NULL`,
3680
- drizzleOrm.sql`n.deleted_at IS NULL`,
3681
- drizzleOrm.sql`r.depth < ${ctx.maxDepth}`
3682
- ];
3683
- if (cycleCheck !== void 0) {
3684
- recursiveWhereClauses.push(cycleCheck);
3685
- }
3686
- const forceWorktableOuterJoinOrder = ctx.dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
3687
- const recursiveCase = ctx.direction === "both" ? compileBidirectionalBranch({
3688
- recursiveColumns,
3689
- whereClauses: recursiveWhereClauses,
3690
- forceWorktableOuterJoinOrder,
3691
- schema: ctx.schema
3692
- }) : compileRecursiveBranch({
3693
- recursiveColumns,
3694
- whereClauses: recursiveWhereClauses,
3695
- joinField: "from_id",
3696
- targetField: "to_id",
3697
- targetKindField: "to_kind",
3698
- forceWorktableOuterJoinOrder,
3699
- schema: ctx.schema
3700
- });
3701
- return drizzleOrm.sql`WITH RECURSIVE reachable AS (${baseCase} UNION ALL ${recursiveCase})`;
3702
- }
3703
- function compileRecursiveBranch(params) {
3704
- const columns = [...params.recursiveColumns];
3705
- const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
3706
- const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND n.id = e.${drizzleOrm.sql.raw(params.targetField)} AND n.kind = e.${drizzleOrm.sql.raw(params.targetKindField)}`;
3707
- if (params.forceWorktableOuterJoinOrder) {
3708
- const allWhere = [
3709
- ...params.whereClauses,
3710
- drizzleOrm.sql`e.${drizzleOrm.sql.raw(params.joinField)} = r.id`
3711
- ];
3712
- return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
3713
- }
3714
- const where = [...params.whereClauses];
3715
- return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON e.${drizzleOrm.sql.raw(params.joinField)} = r.id ${nodeJoin} WHERE ${drizzleOrm.sql.join(where, drizzleOrm.sql` AND `)}`;
3716
- }
3717
- function compileBidirectionalBranch(params) {
3718
- const columns = [...params.recursiveColumns];
3719
- const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
3720
- const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND ((e.to_id = r.id AND n.id = e.from_id AND n.kind = e.from_kind) OR (e.from_id = r.id AND n.id = e.to_id AND n.kind = e.to_kind))`;
3721
- if (params.forceWorktableOuterJoinOrder) {
3722
- const allWhere = [
3723
- ...params.whereClauses,
3724
- drizzleOrm.sql`(e.from_id = r.id OR e.to_id = r.id)`
3725
- ];
3726
- return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
3727
- }
3728
- return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON (e.from_id = r.id OR e.to_id = r.id) ${nodeJoin} WHERE ${drizzleOrm.sql.join([...params.whereClauses], drizzleOrm.sql` AND `)}`;
3729
- }
3730
4154
  function buildIncludedIdsCte(ctx) {
3731
4155
  const filters = [];
3732
4156
  if (ctx.includeKinds !== void 0 && ctx.includeKinds.length > 0) {
@@ -3758,6 +4182,12 @@ async function fetchSubgraphNodes(ctx, reachableCte, includedIdsCte, projectionP
3758
4182
  }
3759
4183
  async function fetchSubgraphEdges(ctx, reachableCte, includedIdsCte, projectionPlan) {
3760
4184
  const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
4185
+ const edgeTemporalFilter = compileTemporalFilter({
4186
+ mode: ctx.temporalMode,
4187
+ asOf: ctx.asOf,
4188
+ tableAlias: "e",
4189
+ currentTimestamp: ctx.dialect.currentTimestamp()
4190
+ });
3761
4191
  const columns = [
3762
4192
  drizzleOrm.sql`e.id`,
3763
4193
  drizzleOrm.sql`e.kind`,
@@ -3775,7 +4205,7 @@ async function fetchSubgraphEdges(ctx, reachableCte, includedIdsCte, projectionP
3775
4205
  ]),
3776
4206
  ...buildProjectedPropertyColumns("e", projectionPlan, ctx.dialect)
3777
4207
  ];
3778
- const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)} FROM ${ctx.schema.edgesTable} e WHERE e.graph_id = ${ctx.graphId} AND ${edgeKindFilter} AND e.deleted_at IS NULL AND e.from_id IN (SELECT id FROM included_ids) AND e.to_id IN (SELECT id FROM included_ids)`;
4208
+ const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)} FROM ${ctx.schema.edgesTable} e WHERE e.graph_id = ${ctx.graphId} AND ${edgeKindFilter} AND ${edgeTemporalFilter} AND e.from_id IN (SELECT id FROM included_ids) AND e.to_id IN (SELECT id FROM included_ids)`;
3779
4209
  return ctx.backend.execute(query);
3780
4210
  }
3781
4211
  function buildMetadataColumns(alias, plan, columns) {
@@ -3814,11 +4244,11 @@ function buildProjectedPropertyColumns(alias, plan, dialect) {
3814
4244
  const extracted = compileTypedJsonExtract({
3815
4245
  column: drizzleOrm.sql.raw(`${alias}.props`),
3816
4246
  dialect,
3817
- pointer: chunkP5CNM325_cjs.jsonPointer([fieldPlan.field]),
4247
+ pointer: chunkUVZHTBZ5_cjs.jsonPointer([fieldPlan.field]),
3818
4248
  valueType: fieldPlan.typeInfo?.valueType
3819
4249
  });
3820
4250
  columns.push(
3821
- drizzleOrm.sql`CASE WHEN ${drizzleOrm.sql.raw(alias)}.kind = ${kind} THEN ${extracted} ELSE NULL END AS ${quoteIdentifier(fieldPlan.outputName)}`
4251
+ drizzleOrm.sql`CASE WHEN ${drizzleOrm.sql.raw(alias)}.kind = ${kind} THEN ${extracted} ELSE NULL END AS ${quoteIdentifier2(fieldPlan.outputName)}`
3822
4252
  );
3823
4253
  }
3824
4254
  }
@@ -3885,16 +4315,6 @@ function mapSubgraphEdgeRow(row, projectionPlan) {
3885
4315
  applyProjectedFields(projectedEdge, row, kindPlan);
3886
4316
  return projectedEdge;
3887
4317
  }
3888
-
3889
- // src/query/ast.ts
3890
- function mergeEdgeKinds(traversal) {
3891
- const inverse = traversal.inverseEdgeKinds;
3892
- if (inverse === void 0 || inverse.length === 0) return traversal.edgeKinds;
3893
- return [
3894
- ...traversal.edgeKinds,
3895
- ...inverse.filter((kind) => !traversal.edgeKinds.includes(kind))
3896
- ];
3897
- }
3898
4318
  var OPERATOR_MAP = {
3899
4319
  union: "UNION",
3900
4320
  unionAll: "UNION ALL",
@@ -4051,6 +4471,9 @@ function validateSqliteSetOpLeaf(ast) {
4051
4471
  if (hasVectorSimilarityPredicates(ast)) {
4052
4472
  unsupported.push("vector similarity predicates");
4053
4473
  }
4474
+ if (hasFulltextMatchPredicates(ast)) {
4475
+ unsupported.push("fulltext match predicates");
4476
+ }
4054
4477
  if (ast.groupBy !== void 0) {
4055
4478
  unsupported.push("GROUP BY");
4056
4479
  }
@@ -4100,7 +4523,42 @@ function hasVectorSimilarityInExpression(expr) {
4100
4523
  case "between":
4101
4524
  case "array_op":
4102
4525
  case "object_op":
4103
- case "aggregate_comparison": {
4526
+ case "aggregate_comparison":
4527
+ case "fulltext_match": {
4528
+ return false;
4529
+ }
4530
+ }
4531
+ }
4532
+ return false;
4533
+ }
4534
+ function hasFulltextMatchPredicates(ast) {
4535
+ return ast.predicates.some(
4536
+ (predicate2) => hasFulltextMatchInExpression(predicate2.expression)
4537
+ );
4538
+ }
4539
+ function hasFulltextMatchInExpression(expr) {
4540
+ if ("__type" in expr) {
4541
+ switch (expr.__type) {
4542
+ case "and":
4543
+ case "or": {
4544
+ return expr.predicates.some((p) => hasFulltextMatchInExpression(p));
4545
+ }
4546
+ case "not": {
4547
+ return hasFulltextMatchInExpression(expr.predicate);
4548
+ }
4549
+ case "fulltext_match": {
4550
+ return true;
4551
+ }
4552
+ case "exists":
4553
+ case "in_subquery":
4554
+ case "comparison":
4555
+ case "string_op":
4556
+ case "null_check":
4557
+ case "between":
4558
+ case "array_op":
4559
+ case "object_op":
4560
+ case "aggregate_comparison":
4561
+ case "vector_similarity": {
4104
4562
  return false;
4105
4563
  }
4106
4564
  }
@@ -4136,7 +4594,8 @@ function hasSubqueryInExpression(expr) {
4136
4594
  case "array_op":
4137
4595
  case "object_op":
4138
4596
  case "aggregate_comparison":
4139
- case "vector_similarity": {
4597
+ case "vector_similarity":
4598
+ case "fulltext_match": {
4140
4599
  return false;
4141
4600
  }
4142
4601
  }
@@ -4302,7 +4761,7 @@ function getLeftmostProjection(op) {
4302
4761
  function normalizeFieldRefKey(field2) {
4303
4762
  let pointer = field2.jsonPointer;
4304
4763
  if (pointer === void 0 && field2.path.length > 1 && field2.path[0] === "props") {
4305
- pointer = chunkP5CNM325_cjs.jsonPointer(field2.path.slice(1));
4764
+ pointer = chunkUVZHTBZ5_cjs.jsonPointer(field2.path.slice(1));
4306
4765
  }
4307
4766
  const basePath = field2.path.length > 0 && field2.path[0] === "props" ? "props" : field2.path.join(".");
4308
4767
  return `${field2.alias}:${basePath}:${pointer ?? ""}`;
@@ -4414,6 +4873,10 @@ function markPredicateFieldsAsRequired(requiredColumnsByAlias, expression) {
4414
4873
  markFieldRefAsRequired(requiredColumnsByAlias, expression.field);
4415
4874
  return;
4416
4875
  }
4876
+ case "fulltext_match": {
4877
+ markFieldRefAsRequired(requiredColumnsByAlias, expression.field);
4878
+ return;
4879
+ }
4417
4880
  case "exists": {
4418
4881
  return;
4419
4882
  }
@@ -4478,7 +4941,8 @@ function hasIdEqualityPredicate(expression, alias) {
4478
4941
  case "aggregate_comparison":
4479
4942
  case "exists":
4480
4943
  case "in_subquery":
4481
- case "vector_similarity": {
4944
+ case "vector_similarity":
4945
+ case "fulltext_match": {
4482
4946
  return false;
4483
4947
  }
4484
4948
  }
@@ -4519,10 +4983,13 @@ function resolveTraversalCteLimit(ast, predicateIndex) {
4519
4983
  );
4520
4984
  return Math.max(ast.limit, pushdownLimit);
4521
4985
  }
4522
- function canCollapseSelectiveTraversalRowset(ast, vectorPredicate) {
4986
+ function canCollapseSelectiveTraversalRowset(ast, vectorPredicate, fulltextPredicate) {
4523
4987
  if (vectorPredicate !== void 0) {
4524
4988
  return false;
4525
4989
  }
4990
+ if (fulltextPredicate !== void 0) {
4991
+ return false;
4992
+ }
4526
4993
  if (ast.traversals.length === 0) {
4527
4994
  return false;
4528
4995
  }
@@ -4562,6 +5029,8 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
4562
5029
  collapsedTraversalCteAlias: void 0,
4563
5030
  ctx,
4564
5031
  effectiveLimit: void 0,
5032
+ fulltextPredicate: void 0,
5033
+ fusion: void 0,
4565
5034
  logicalPlan: void 0,
4566
5035
  predicateIndex: buildPredicateIndex(ast),
4567
5036
  requiredColumnsByAlias: void 0,
@@ -4583,6 +5052,39 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
4583
5052
  }
4584
5053
  });
4585
5054
  state = vectorPass.state;
5055
+ const fulltextPass = runCompilerPass(state, {
5056
+ name: "fulltext_predicate",
5057
+ execute(currentState) {
5058
+ return runFulltextPredicatePass(
5059
+ currentState.ast,
5060
+ currentState.ctx.dialect
5061
+ ).fulltextPredicate;
5062
+ },
5063
+ update(currentState, fulltextPredicate) {
5064
+ return {
5065
+ ...currentState,
5066
+ fulltextPredicate
5067
+ };
5068
+ }
5069
+ });
5070
+ state = fulltextPass.state;
5071
+ const fusionPass = runCompilerPass(state, {
5072
+ name: "fusion_config",
5073
+ execute(currentState) {
5074
+ return runFusionConfigPass(
5075
+ currentState.ast,
5076
+ currentState.vectorPredicate,
5077
+ currentState.fulltextPredicate
5078
+ ).fusion;
5079
+ },
5080
+ update(currentState, fusion) {
5081
+ return {
5082
+ ...currentState,
5083
+ fusion
5084
+ };
5085
+ }
5086
+ });
5087
+ state = fusionPass.state;
4586
5088
  const temporalPass = runCompilerPass(state, {
4587
5089
  name: "temporal_filters",
4588
5090
  execute(currentState) {
@@ -4617,7 +5119,8 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
4617
5119
  execute(currentState) {
4618
5120
  const shouldCollapseSelectiveTraversalRowset = canCollapseSelectiveTraversalRowset(
4619
5121
  currentState.ast,
4620
- currentState.vectorPredicate
5122
+ currentState.vectorPredicate,
5123
+ currentState.fulltextPredicate
4621
5124
  );
4622
5125
  const lastTraversal = currentState.ast.traversals.at(-1);
4623
5126
  const collapsedTraversalCteAlias = shouldCollapseSelectiveTraversalRowset && lastTraversal !== void 0 ? `cte_${lastTraversal.nodeAlias}` : void 0;
@@ -4653,9 +5156,9 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
4653
5156
  state = traversalLimitPass.state;
4654
5157
  state = {
4655
5158
  ...state,
4656
- effectiveLimit: resolveVectorAwareLimit(
4657
- state.ast.limit,
4658
- state.vectorPredicate
5159
+ effectiveLimit: resolveFulltextAwareLimit(
5160
+ resolveVectorAwareLimit(state.ast.limit, state.vectorPredicate),
5161
+ state.fulltextPredicate
4659
5162
  )
4660
5163
  };
4661
5164
  const logicalPlanPass = runCompilerPass(state, {
@@ -4672,7 +5175,8 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
4672
5175
  collapsedTraversalCteAlias: currentState.collapsedTraversalCteAlias
4673
5176
  },
4674
5177
  ...currentState.effectiveLimit === void 0 ? {} : { effectiveLimit: currentState.effectiveLimit },
4675
- ...currentState.vectorPredicate === void 0 ? {} : { vectorPredicate: currentState.vectorPredicate }
5178
+ ...currentState.vectorPredicate === void 0 ? {} : { vectorPredicate: currentState.vectorPredicate },
5179
+ ...currentState.fulltextPredicate === void 0 ? {} : { fulltextPredicate: currentState.fulltextPredicate }
4676
5180
  });
4677
5181
  },
4678
5182
  update(currentState, logicalPlan) {
@@ -4691,11 +5195,11 @@ function compileQuery(ast, graphId, options = "sqlite") {
4691
5195
  const options_ = typeof options === "string" ? { dialect: options } : options;
4692
5196
  const dialect = options_.dialect ?? "sqlite";
4693
5197
  const schema = options_.schema ?? DEFAULT_SQL_SCHEMA;
4694
- const adapter = chunkMME3H4ZF_cjs.getDialect(dialect);
5198
+ const adapter = resolveDialectAdapter(dialect, options_.fulltextStrategy);
4695
5199
  const ctx = {
4696
5200
  dialect: adapter,
4697
5201
  schema,
4698
- compileQuery: (subAst, subGraphId) => compileQuery(subAst, subGraphId, { dialect, schema })
5202
+ compileQuery: (subAst, subGraphId) => compileQuery(subAst, subGraphId, propagateOptions(options_))
4699
5203
  };
4700
5204
  if (hasVariableLengthTraversal(ast)) {
4701
5205
  const lowered = tryLowerSingleHopRecursiveTraversal(ast);
@@ -4731,15 +5235,29 @@ function compileSetOperation2(op, graphId, options = "sqlite") {
4731
5235
  const options_ = typeof options === "string" ? { dialect: options } : options;
4732
5236
  const dialect = options_.dialect ?? "sqlite";
4733
5237
  const schema = options_.schema ?? DEFAULT_SQL_SCHEMA;
4734
- const adapter = chunkMME3H4ZF_cjs.getDialect(dialect);
5238
+ const adapter = resolveDialectAdapter(dialect, options_.fulltextStrategy);
4735
5239
  return compileSetOperation(
4736
5240
  op,
4737
5241
  graphId,
4738
5242
  adapter,
4739
5243
  schema,
4740
- (ast, gid) => compileQuery(ast, gid, { dialect, schema })
5244
+ (ast, gid) => compileQuery(ast, gid, propagateOptions(options_))
4741
5245
  );
4742
5246
  }
5247
+ function resolveDialectAdapter(dialect, fulltextStrategy) {
5248
+ const baseAdapter = chunkFQCQM3KG_cjs.getDialect(dialect);
5249
+ if (fulltextStrategy === void 0 || fulltextStrategy === baseAdapter.fulltext) {
5250
+ return baseAdapter;
5251
+ }
5252
+ return { ...baseAdapter, fulltext: fulltextStrategy };
5253
+ }
5254
+ function propagateOptions(options_) {
5255
+ return {
5256
+ dialect: options_.dialect ?? "sqlite",
5257
+ ...options_.schema === void 0 ? {} : { schema: options_.schema },
5258
+ ...options_.fulltextStrategy === void 0 ? {} : { fulltextStrategy: options_.fulltextStrategy }
5259
+ };
5260
+ }
4743
5261
  function resolveCountAggregateFastPath(ast) {
4744
5262
  if (ast.traversals.length !== 1) {
4745
5263
  return void 0;
@@ -4888,12 +5406,12 @@ function compileCountAggregateFastPath(ast, graphId, ctx, logicalPlan, requiredC
4888
5406
  source.valueType,
4889
5407
  `cte_${source.alias}`
4890
5408
  );
4891
- return drizzleOrm.sql`${value} AS ${quoteIdentifier(projectedField.outputName)}`;
5409
+ return drizzleOrm.sql`${value} AS ${quoteIdentifier2(projectedField.outputName)}`;
4892
5410
  }
4893
5411
  const projectedCountColumn = source.function === "countDistinct" ? countDistinctColumn : countColumn;
4894
5412
  const countValue = drizzleOrm.sql`${drizzleOrm.sql.raw(countCteAlias)}.${drizzleOrm.sql.raw(projectedCountColumn)}`;
4895
5413
  const aggregateValue = traversal.optional ? drizzleOrm.sql`COALESCE(${countValue}, 0)` : countValue;
4896
- return drizzleOrm.sql`${aggregateValue} AS ${quoteIdentifier(projectedField.outputName)}`;
5414
+ return drizzleOrm.sql`${aggregateValue} AS ${quoteIdentifier2(projectedField.outputName)}`;
4897
5415
  }),
4898
5416
  drizzleOrm.sql`, `
4899
5417
  );
@@ -4918,19 +5436,45 @@ function compileCountAggregateFastPath(ast, graphId, ctx, logicalPlan, requiredC
4918
5436
  projection
4919
5437
  });
4920
5438
  }
4921
- var STANDARD_QUERY_STRATEGY_HANDLERS = {
4922
- cte_project: compileStandardQueryWithCteStrategy
4923
- };
4924
- function compileStandardQuery(ast, graphId, ctx) {
4925
- const strategy = ctx.dialect.capabilities.standardQueryStrategy;
4926
- const handler = STANDARD_QUERY_STRATEGY_HANDLERS[strategy];
4927
- return handler(ast, graphId, ctx);
4928
- }
5439
+ var STANDARD_QUERY_STRATEGY_HANDLERS = {
5440
+ cte_project: compileStandardQueryWithCteStrategy
5441
+ };
5442
+ function compileStandardQuery(ast, graphId, ctx) {
5443
+ const strategy = ctx.dialect.capabilities.standardQueryStrategy;
5444
+ const handler = STANDARD_QUERY_STRATEGY_HANDLERS[strategy];
5445
+ return handler(ast, graphId, ctx);
5446
+ }
5447
+ function selectStandardOrderBy(input) {
5448
+ const {
5449
+ ast,
5450
+ collapsedTraversalCteAlias,
5451
+ dialect,
5452
+ fulltextPredicate,
5453
+ fusion,
5454
+ vectorPredicate
5455
+ } = input;
5456
+ if (vectorPredicate && fulltextPredicate) {
5457
+ return buildStandardHybridRrfOrderBy({ ast, dialect, fusion });
5458
+ }
5459
+ if (vectorPredicate) {
5460
+ return buildStandardVectorOrderBy({ ast, dialect });
5461
+ }
5462
+ if (fulltextPredicate) {
5463
+ return buildStandardFulltextOrderBy({ ast, dialect });
5464
+ }
5465
+ return buildStandardOrderBy({
5466
+ ast,
5467
+ ...collapsedTraversalCteAlias === void 0 ? {} : { collapsedTraversalCteAlias },
5468
+ dialect
5469
+ });
5470
+ }
4929
5471
  function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
4930
5472
  const { dialect } = ctx;
4931
5473
  const {
4932
5474
  collapsedTraversalCteAlias,
4933
5475
  effectiveLimit,
5476
+ fulltextPredicate,
5477
+ fusion,
4934
5478
  logicalPlan,
4935
5479
  predicateIndex,
4936
5480
  requiredColumnsByAlias,
@@ -4951,7 +5495,7 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
4951
5495
  { phase: "standard-pass-pipeline" }
4952
5496
  );
4953
5497
  }
4954
- if (!vectorPredicate) {
5498
+ if (!vectorPredicate && !fulltextPredicate) {
4955
5499
  const fastPathSql = compileCountAggregateFastPath(
4956
5500
  ast,
4957
5501
  graphId,
@@ -4997,7 +5541,29 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
4997
5541
  );
4998
5542
  }
4999
5543
  if (vectorPredicate) {
5000
- ctes.push(buildStandardEmbeddingsCte({ ctx, graphId, vectorPredicate }));
5544
+ const nodeKinds = getNodeKindsForAlias(ast, vectorPredicate.field.alias);
5545
+ ctes.push(
5546
+ buildStandardEmbeddingsCte({
5547
+ ctx,
5548
+ graphId,
5549
+ nodeKinds,
5550
+ vectorPredicate
5551
+ })
5552
+ );
5553
+ }
5554
+ if (fulltextPredicate) {
5555
+ const nodeKinds = getNodeKindsForAlias(ast, fulltextPredicate.field.alias);
5556
+ ctes.push(
5557
+ buildStandardFulltextCte({
5558
+ ctx,
5559
+ fulltextPredicate,
5560
+ graphId,
5561
+ nodeKinds
5562
+ })
5563
+ );
5564
+ }
5565
+ if (getHybridTargetAlias(vectorPredicate, fulltextPredicate) !== void 0) {
5566
+ ctes.push(buildStandardHybridCandidateCte());
5001
5567
  }
5002
5568
  const projection = buildStandardProjection({
5003
5569
  ast,
@@ -5007,14 +5573,18 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
5007
5573
  const fromClause = buildStandardFromClause({
5008
5574
  ast,
5009
5575
  ...collapsedTraversalCteAlias === void 0 ? {} : { collapsedTraversalCteAlias },
5010
- ...vectorPredicate === void 0 ? {} : { vectorPredicate }
5576
+ ...vectorPredicate === void 0 ? {} : { vectorPredicate },
5577
+ ...fulltextPredicate === void 0 ? {} : { fulltextPredicate }
5011
5578
  });
5012
5579
  const groupBy = buildStandardGroupBy({ ast, dialect });
5013
5580
  const having2 = buildStandardHaving({ ast, ctx });
5014
- const orderBy = vectorPredicate ? buildStandardVectorOrderBy({ ast, dialect }) : buildStandardOrderBy({
5581
+ const orderBy = selectStandardOrderBy({
5015
5582
  ast,
5016
- ...collapsedTraversalCteAlias === void 0 ? {} : { collapsedTraversalCteAlias },
5017
- dialect
5583
+ collapsedTraversalCteAlias,
5584
+ dialect,
5585
+ fulltextPredicate,
5586
+ fusion,
5587
+ vectorPredicate
5018
5588
  });
5019
5589
  const limitOffset = buildLimitOffsetClause({
5020
5590
  limit: effectiveLimit,
@@ -5136,7 +5706,7 @@ function extractCursorValue(row, spec) {
5136
5706
  current = record[segment];
5137
5707
  }
5138
5708
  if (jsonPointer2) {
5139
- const segments = chunkP5CNM325_cjs.parseJsonPointer(jsonPointer2);
5709
+ const segments = chunkUVZHTBZ5_cjs.parseJsonPointer(jsonPointer2);
5140
5710
  for (const segment of segments) {
5141
5711
  if (current === null || current === void 0) return void 0;
5142
5712
  if (typeof current !== "object") return void 0;
@@ -5206,14 +5776,14 @@ function generateId() {
5206
5776
 
5207
5777
  // src/utils/path.ts
5208
5778
  function parseSqlitePath(path) {
5209
- const emptyPath = `${chunkMME3H4ZF_cjs.SQLITE_PATH_DELIMITER}${chunkMME3H4ZF_cjs.SQLITE_PATH_DELIMITER}`;
5779
+ const emptyPath = `${chunkFQCQM3KG_cjs.SQLITE_PATH_DELIMITER}${chunkFQCQM3KG_cjs.SQLITE_PATH_DELIMITER}`;
5210
5780
  if (!path || path === emptyPath) return [];
5211
5781
  const trimmed = path.slice(1, -1);
5212
5782
  if (trimmed === "") return [];
5213
- return trimmed.split(chunkMME3H4ZF_cjs.SQLITE_PATH_DELIMITER);
5783
+ return trimmed.split(chunkFQCQM3KG_cjs.SQLITE_PATH_DELIMITER);
5214
5784
  }
5215
5785
  function isSqlitePath(value) {
5216
- return typeof value === "string" && value.startsWith(chunkMME3H4ZF_cjs.SQLITE_PATH_DELIMITER) && value.endsWith(chunkMME3H4ZF_cjs.SQLITE_PATH_DELIMITER);
5786
+ return typeof value === "string" && value.startsWith(chunkFQCQM3KG_cjs.SQLITE_PATH_DELIMITER) && value.endsWith(chunkFQCQM3KG_cjs.SQLITE_PATH_DELIMITER);
5217
5787
  }
5218
5788
  function normalizePath(value) {
5219
5789
  if (Array.isArray(value)) {
@@ -5228,12 +5798,12 @@ function normalizePath(value) {
5228
5798
  return [];
5229
5799
  }
5230
5800
  function isPostgresTextArray(value) {
5231
- return typeof value === "string" && value.startsWith(chunkMME3H4ZF_cjs.PG_ARRAY_START) && value.endsWith(chunkMME3H4ZF_cjs.PG_ARRAY_END);
5801
+ return typeof value === "string" && value.startsWith(chunkFQCQM3KG_cjs.PG_ARRAY_START) && value.endsWith(chunkFQCQM3KG_cjs.PG_ARRAY_END);
5232
5802
  }
5233
5803
  function parsePostgresTextArray(value) {
5234
5804
  const inner = value.slice(1, -1);
5235
5805
  if (inner === "") return [];
5236
- return inner.split(chunkMME3H4ZF_cjs.PG_PATH_ELEMENT_SEPARATOR);
5806
+ return inner.split(chunkFQCQM3KG_cjs.PG_PATH_ELEMENT_SEPARATOR);
5237
5807
  }
5238
5808
 
5239
5809
  // src/query/execution/result-mapper.ts
@@ -5435,11 +6005,7 @@ function buildComparisonPredicate(field2, op, value) {
5435
6005
  };
5436
6006
  }
5437
6007
  function buildCursorFromContext(context, orderBy, direction) {
5438
- return buildCursorFromRow(
5439
- context,
5440
- orderBy,
5441
- direction
5442
- );
6008
+ return buildCursorFromRow(context, orderBy, direction);
5443
6009
  }
5444
6010
  function buildPaginatedResult(data, orderedRows, orderBy, limit, hasMore, isBackward, cursor, buildContext) {
5445
6011
  let nextCursor;
@@ -5474,7 +6040,7 @@ async function* createStreamIterable(batchSize, paginate) {
5474
6040
  }
5475
6041
  }
5476
6042
  function getStreamBatchSize(options) {
5477
- return options?.batchSize ?? chunkMME3H4ZF_cjs.DEFAULT_STREAM_BATCH_SIZE;
6043
+ return options?.batchSize ?? chunkFQCQM3KG_cjs.DEFAULT_STREAM_BATCH_SIZE;
5478
6044
  }
5479
6045
 
5480
6046
  // src/query/execution/field-tracker.ts
@@ -5556,7 +6122,7 @@ function createNodeTrackingProxy(alias, kindNames, tracker, options) {
5556
6122
  return getPlaceholderForSystemField(property, options.mode);
5557
6123
  }
5558
6124
  if (property === "meta") {
5559
- for (const key of chunkMME3H4ZF_cjs.NODE_META_KEYS) {
6125
+ for (const key of chunkFQCQM3KG_cjs.NODE_META_KEYS) {
5560
6126
  tracker.record(alias, `meta.${key}`, true);
5561
6127
  }
5562
6128
  return buildNodeMetaPlaceholder(options.mode);
@@ -5589,7 +6155,7 @@ function createEdgeTrackingProxy(alias, edgeKindNames, tracker, options) {
5589
6155
  return getPlaceholderForSystemField(property, options.mode);
5590
6156
  }
5591
6157
  if (property === "meta") {
5592
- for (const key of chunkMME3H4ZF_cjs.EDGE_META_KEYS) {
6158
+ for (const key of chunkFQCQM3KG_cjs.EDGE_META_KEYS) {
5593
6159
  tracker.record(alias, `meta.${key}`, true);
5594
6160
  }
5595
6161
  return buildEdgeMetaPlaceholder(options.mode);
@@ -6032,59 +6598,41 @@ function validateSqlIdentifier(alias) {
6032
6598
  );
6033
6599
  }
6034
6600
  }
6035
- function throwInvalidVectorPredicatePlacement(path) {
6036
- throw new chunkO5PST6TB_cjs.ValidationError(
6037
- "Vector similarity predicates cannot be nested under OR or NOT. Use top-level AND combinations instead.",
6038
- {
6039
- issues: [
6040
- {
6041
- path,
6042
- message: "Vector similarity predicates are only supported at top-level or inside AND groups."
6043
- }
6044
- ]
6045
- },
6046
- {
6047
- suggestion: "Rewrite the predicate to keep vector similarity at top-level or combine with additional filters using AND."
6048
- }
6049
- );
6050
- }
6051
- function validateVectorPredicateExpression(expression, inDisallowedBranch, path) {
6601
+ function validateStructuralPredicatePlacement(expression, matchType, buildError, inDisallowedBranch, path) {
6602
+ if (expression.__type === matchType) {
6603
+ if (inDisallowedBranch) buildError(path);
6604
+ return;
6605
+ }
6052
6606
  switch (expression.__type) {
6053
- case "vector_similarity": {
6054
- if (inDisallowedBranch) {
6055
- throwInvalidVectorPredicatePlacement(path);
6056
- }
6057
- return;
6058
- }
6059
6607
  case "and": {
6060
- for (const [
6061
- predicateIndex,
6062
- predicate2
6063
- ] of expression.predicates.entries()) {
6064
- validateVectorPredicateExpression(
6065
- predicate2,
6608
+ for (const [index, child] of expression.predicates.entries()) {
6609
+ validateStructuralPredicatePlacement(
6610
+ child,
6611
+ matchType,
6612
+ buildError,
6066
6613
  inDisallowedBranch,
6067
- `${path}.predicates[${predicateIndex}]`
6614
+ `${path}.predicates[${index}]`
6068
6615
  );
6069
6616
  }
6070
6617
  return;
6071
6618
  }
6072
6619
  case "or": {
6073
- for (const [
6074
- predicateIndex,
6075
- predicate2
6076
- ] of expression.predicates.entries()) {
6077
- validateVectorPredicateExpression(
6078
- predicate2,
6620
+ for (const [index, child] of expression.predicates.entries()) {
6621
+ validateStructuralPredicatePlacement(
6622
+ child,
6623
+ matchType,
6624
+ buildError,
6079
6625
  true,
6080
- `${path}.predicates[${predicateIndex}]`
6626
+ `${path}.predicates[${index}]`
6081
6627
  );
6082
6628
  }
6083
6629
  return;
6084
6630
  }
6085
6631
  case "not": {
6086
- validateVectorPredicateExpression(
6632
+ validateStructuralPredicatePlacement(
6087
6633
  expression.predicate,
6634
+ matchType,
6635
+ buildError,
6088
6636
  true,
6089
6637
  `${path}.predicate`
6090
6638
  );
@@ -6098,24 +6646,108 @@ function validateVectorPredicateExpression(expression, inDisallowedBranch, path)
6098
6646
  case "object_op":
6099
6647
  case "aggregate_comparison":
6100
6648
  case "exists":
6101
- case "in_subquery": {
6649
+ case "in_subquery":
6650
+ case "vector_similarity":
6651
+ case "fulltext_match": {
6102
6652
  return;
6103
6653
  }
6104
6654
  }
6105
6655
  }
6656
+ function throwInvalidVectorPredicatePlacement(path) {
6657
+ throw new chunkO5PST6TB_cjs.ValidationError(
6658
+ "Vector similarity predicates cannot be nested under OR or NOT. Use top-level AND combinations instead.",
6659
+ {
6660
+ issues: [
6661
+ {
6662
+ path,
6663
+ message: "Vector similarity predicates are only supported at top-level or inside AND groups."
6664
+ }
6665
+ ]
6666
+ },
6667
+ {
6668
+ suggestion: "Rewrite the predicate to keep vector similarity at top-level or combine with additional filters using AND."
6669
+ }
6670
+ );
6671
+ }
6672
+ function throwInvalidFulltextPredicatePlacement(path) {
6673
+ throw new chunkO5PST6TB_cjs.ValidationError(
6674
+ "Fulltext match predicates (.matches()) cannot be nested under OR or NOT. Use top-level AND combinations instead.",
6675
+ {
6676
+ issues: [
6677
+ {
6678
+ path,
6679
+ message: "Fulltext match predicates are only supported at top-level or inside AND groups."
6680
+ }
6681
+ ]
6682
+ },
6683
+ {
6684
+ suggestion: "Restructure your query so the fulltext match appears at the top level of a whereNode() or inside an .and() combination."
6685
+ }
6686
+ );
6687
+ }
6106
6688
  function validateVectorPredicatePlacement(predicates) {
6107
- for (const [predicateIndex, predicate2] of predicates.entries()) {
6108
- validateVectorPredicateExpression(
6689
+ for (const [index, predicate2] of predicates.entries()) {
6690
+ validateStructuralPredicatePlacement(
6691
+ predicate2.expression,
6692
+ "vector_similarity",
6693
+ throwInvalidVectorPredicatePlacement,
6694
+ false,
6695
+ `predicates[${index}].expression`
6696
+ );
6697
+ }
6698
+ }
6699
+ function validateFulltextPredicatePlacement(predicates) {
6700
+ for (const [index, predicate2] of predicates.entries()) {
6701
+ validateStructuralPredicatePlacement(
6109
6702
  predicate2.expression,
6703
+ "fulltext_match",
6704
+ throwInvalidFulltextPredicatePlacement,
6110
6705
  false,
6111
- `predicates[${predicateIndex}].expression`
6706
+ `predicates[${index}].expression`
6707
+ );
6708
+ }
6709
+ }
6710
+ function validateHybridFusionOptions(options) {
6711
+ const method = options.method;
6712
+ if (method !== void 0 && method !== "rrf") {
6713
+ throw new chunkO5PST6TB_cjs.ValidationError(`Unsupported fusion method: ${method}`, {
6714
+ issues: [{ path: "fusion.method", message: `Only "rrf" is supported.` }]
6715
+ });
6716
+ }
6717
+ const { k } = options;
6718
+ if (k !== void 0 && (!Number.isFinite(k) || k <= 0)) {
6719
+ throw new chunkO5PST6TB_cjs.ValidationError(
6720
+ `Fusion k must be a positive finite number, got: ${String(k)}`,
6721
+ {
6722
+ issues: [{ path: "fusion.k", message: "Must be a positive number." }]
6723
+ }
6112
6724
  );
6113
6725
  }
6726
+ const weights = options.weights;
6727
+ if (weights !== void 0) {
6728
+ for (const key of ["vector", "fulltext"]) {
6729
+ const weight = weights[key];
6730
+ if (weight !== void 0 && (!Number.isFinite(weight) || weight < 0)) {
6731
+ throw new chunkO5PST6TB_cjs.ValidationError(
6732
+ `Fusion weight for "${key}" must be a non-negative finite number, got: ${String(weight)}`,
6733
+ {
6734
+ issues: [
6735
+ {
6736
+ path: `fusion.weights.${key}`,
6737
+ message: "Must be a non-negative number."
6738
+ }
6739
+ ]
6740
+ }
6741
+ );
6742
+ }
6743
+ }
6744
+ }
6114
6745
  }
6115
6746
 
6116
6747
  // src/query/builder/ast-builder.ts
6117
6748
  function buildQueryAst(config, state) {
6118
6749
  validateVectorPredicatePlacement(state.predicates);
6750
+ validateFulltextPredicatePlacement(state.predicates);
6119
6751
  const temporalMode = {
6120
6752
  mode: state.temporalMode
6121
6753
  };
@@ -6139,7 +6771,18 @@ function buildQueryAst(config, state) {
6139
6771
  ...state.limit !== void 0 && { limit: state.limit },
6140
6772
  ...state.offset !== void 0 && { offset: state.offset },
6141
6773
  ...state.groupBy !== void 0 && { groupBy: state.groupBy },
6142
- ...state.having !== void 0 && { having: state.having }
6774
+ ...state.having !== void 0 && { having: state.having },
6775
+ ...state.fusion !== void 0 && { fusion: state.fusion }
6776
+ };
6777
+ }
6778
+
6779
+ // src/query/builder/compile-options.ts
6780
+ function buildCompileOptions(config) {
6781
+ const fulltextStrategy = config.backend?.fulltextStrategy;
6782
+ return {
6783
+ dialect: config.dialect ?? "sqlite",
6784
+ schema: config.schema,
6785
+ ...fulltextStrategy === void 0 ? {} : { fulltextStrategy }
6143
6786
  };
6144
6787
  }
6145
6788
  function toLiteral(value) {
@@ -6241,7 +6884,8 @@ function substitutePredicateExpression(expr, bindings) {
6241
6884
  case "array_op":
6242
6885
  case "object_op":
6243
6886
  case "aggregate_comparison":
6244
- case "vector_similarity": {
6887
+ case "vector_similarity":
6888
+ case "fulltext_match": {
6245
6889
  return expr;
6246
6890
  }
6247
6891
  case "exists": {
@@ -6280,7 +6924,7 @@ function substituteParameters(ast, bindings) {
6280
6924
  };
6281
6925
  }
6282
6926
  function fillPlaceholders(params, bindings, dialect) {
6283
- const adapter = chunkMME3H4ZF_cjs.getDialect(dialect);
6927
+ const adapter = chunkFQCQM3KG_cjs.getDialect(dialect);
6284
6928
  return params.map((parameter) => {
6285
6929
  if (parameter instanceof drizzleOrm.Placeholder) {
6286
6930
  const name = parameter.name;
@@ -6493,7 +7137,8 @@ function collectParameterMetadataFromExpression(expression, names, stringOpParam
6493
7137
  case "array_op":
6494
7138
  case "object_op":
6495
7139
  case "aggregate_comparison":
6496
- case "vector_similarity": {
7140
+ case "vector_similarity":
7141
+ case "fulltext_match": {
6497
7142
  return;
6498
7143
  }
6499
7144
  case "exists": {
@@ -6624,7 +7269,7 @@ var ExecutableQuery = class _ExecutableQuery {
6624
7269
  direction
6625
7270
  } : {
6626
7271
  field: fieldRef(alias, ["props"], {
6627
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
7272
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
6628
7273
  valueType: typeInfo?.valueType,
6629
7274
  elementType: typeInfo?.elementType
6630
7275
  }),
@@ -6848,10 +7493,7 @@ var ExecutableQuery = class _ExecutableQuery {
6848
7493
  * Builds compile options from the config.
6849
7494
  */
6850
7495
  #compileOptions() {
6851
- return {
6852
- dialect: this.#config.dialect ?? "sqlite",
6853
- schema: this.#config.schema
6854
- };
7496
+ return buildCompileOptions(this.#config);
6855
7497
  }
6856
7498
  #requireBackend() {
6857
7499
  const { backend } = this.#config;
@@ -7233,7 +7875,7 @@ var ExecutableQuery = class _ExecutableQuery {
7233
7875
  if (field2.jsonPointer === void 0) {
7234
7876
  return false;
7235
7877
  }
7236
- const segments = chunkP5CNM325_cjs.parseJsonPointer(field2.jsonPointer);
7878
+ const segments = chunkUVZHTBZ5_cjs.parseJsonPointer(field2.jsonPointer);
7237
7879
  if (segments.length !== 1) {
7238
7880
  return false;
7239
7881
  }
@@ -7281,7 +7923,7 @@ var ExecutableQuery = class _ExecutableQuery {
7281
7923
  aliasObject2[fieldName] = nullToUndefined(row[outputName2]);
7282
7924
  continue;
7283
7925
  }
7284
- const segments = chunkP5CNM325_cjs.parseJsonPointer(jsonPointer2);
7926
+ const segments = chunkUVZHTBZ5_cjs.parseJsonPointer(jsonPointer2);
7285
7927
  if (segments.length === 0) {
7286
7928
  throw new MissingSelectiveFieldError(alias, "orderBy");
7287
7929
  }
@@ -7377,7 +8019,7 @@ var ExecutableQuery = class _ExecutableQuery {
7377
8019
  );
7378
8020
  }
7379
8021
  const isBackward = options.last !== void 0 || options.before !== void 0;
7380
- const limit = options.first ?? options.last ?? chunkMME3H4ZF_cjs.DEFAULT_PAGINATION_LIMIT;
8022
+ const limit = options.first ?? options.last ?? chunkFQCQM3KG_cjs.DEFAULT_PAGINATION_LIMIT;
7381
8023
  const cursor = options.after ?? options.before;
7382
8024
  let cursorData;
7383
8025
  if (cursor) {
@@ -7566,14 +8208,8 @@ var ExecutableAggregateQuery = class _ExecutableAggregateQuery {
7566
8208
  this.#cachedCompiled = compiled;
7567
8209
  return compiled;
7568
8210
  }
7569
- /**
7570
- * Builds compile options from the config.
7571
- */
7572
8211
  #compileOptions() {
7573
- return {
7574
- dialect: this.#config.dialect ?? "sqlite",
7575
- schema: this.#config.schema
7576
- };
8212
+ return buildCompileOptions(this.#config);
7577
8213
  }
7578
8214
  /**
7579
8215
  * Executes the query and returns typed results.
@@ -7804,7 +8440,7 @@ var TraversalBuilder = class _TraversalBuilder {
7804
8440
  const valueType = typeInfo?.valueType;
7805
8441
  const elementType = typeInfo?.elementType;
7806
8442
  const ref = fieldRef(alias, ["props"], {
7807
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([propertyName]),
8443
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([propertyName]),
7808
8444
  valueType,
7809
8445
  elementType
7810
8446
  });
@@ -8124,7 +8760,7 @@ var QueryBuilder = class _QueryBuilder {
8124
8760
  if (source.valueType !== void 0 || source.path.length !== 1 || source.path[0] !== "props" || source.jsonPointer === void 0) {
8125
8761
  return [outputName, source];
8126
8762
  }
8127
- const segments = chunkP5CNM325_cjs.parseJsonPointer(source.jsonPointer);
8763
+ const segments = chunkUVZHTBZ5_cjs.parseJsonPointer(source.jsonPointer);
8128
8764
  if (segments.length !== 1) {
8129
8765
  return [outputName, source];
8130
8766
  }
@@ -8178,7 +8814,7 @@ var QueryBuilder = class _QueryBuilder {
8178
8814
  direction
8179
8815
  } : {
8180
8816
  field: fieldRef(alias, ["props"], {
8181
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
8817
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
8182
8818
  valueType: typeInfo?.valueType,
8183
8819
  elementType: typeInfo?.elementType
8184
8820
  }),
@@ -8253,7 +8889,7 @@ var QueryBuilder = class _QueryBuilder {
8253
8889
  __type: "field_ref",
8254
8890
  alias,
8255
8891
  path: ["props"],
8256
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
8892
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
8257
8893
  valueType: typeInfo?.valueType,
8258
8894
  elementType: typeInfo?.elementType
8259
8895
  };
@@ -8300,6 +8936,36 @@ var QueryBuilder = class _QueryBuilder {
8300
8936
  having: predicate2
8301
8937
  });
8302
8938
  }
8939
+ /**
8940
+ * Sets fusion parameters for hybrid (vector + fulltext) queries.
8941
+ *
8942
+ * Applies only when the query contains both a `.similarTo()` and a
8943
+ * `.$fulltext.matches()` predicate. Without this call, the default is
8944
+ * RRF with k=60 and equal weights. A mismatch between `.fuseWith()`
8945
+ * configuration and the predicates on the query is caught during
8946
+ * compilation, not here.
8947
+ *
8948
+ * @example
8949
+ * ```typescript
8950
+ * store.query()
8951
+ * .from("Document", "d")
8952
+ * .whereNode("d", d =>
8953
+ * d.$fulltext.matches("renewable energy", 50)
8954
+ * .and(d.embedding.similarTo(vec, 50))
8955
+ * .and(d.tenantId.eq(tenant))
8956
+ * )
8957
+ * .fuseWith({ k: 60, weights: { fulltext: 1.5 } })
8958
+ * .limit(10)
8959
+ * .execute();
8960
+ * ```
8961
+ */
8962
+ fuseWith(options) {
8963
+ validateHybridFusionOptions(options);
8964
+ return new _QueryBuilder(this.#config, {
8965
+ ...this.#state,
8966
+ fusion: options
8967
+ });
8968
+ }
8303
8969
  /**
8304
8970
  * Applies a query fragment to transform this builder.
8305
8971
  *
@@ -8351,42 +9017,14 @@ var QueryBuilder = class _QueryBuilder {
8351
9017
  property
8352
9018
  ) : void 0;
8353
9019
  const ref = fieldRef(alias, ["props"], {
8354
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([property]),
9020
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([property]),
8355
9021
  valueType: typeInfo?.valueType,
8356
9022
  elementType: typeInfo?.elementType
8357
9023
  });
8358
9024
  return this.#buildFieldBuilderForTypeInfo(ref, typeInfo);
8359
9025
  }
8360
9026
  #buildFieldBuilderForTypeInfo(ref, typeInfo) {
8361
- if (!typeInfo) {
8362
- return baseField(ref);
8363
- }
8364
- switch (typeInfo.valueType) {
8365
- case "string": {
8366
- return stringField(ref);
8367
- }
8368
- case "number": {
8369
- return numberField(ref);
8370
- }
8371
- case "boolean": {
8372
- return baseField(ref);
8373
- }
8374
- case "date": {
8375
- return dateField(ref);
8376
- }
8377
- case "array": {
8378
- return arrayField(ref);
8379
- }
8380
- case "object": {
8381
- return objectField(ref, { typeInfo });
8382
- }
8383
- case "embedding": {
8384
- return embeddingField(ref);
8385
- }
8386
- case "unknown": {
8387
- return baseField(ref);
8388
- }
8389
- }
9027
+ return buildFieldBuilderForTypeInfo(ref, typeInfo);
8390
9028
  }
8391
9029
  #createNodeAccessor(alias) {
8392
9030
  const kindNames = this.#getKindNamesForAlias(alias);
@@ -8396,6 +9034,10 @@ var QueryBuilder = class _QueryBuilder {
8396
9034
  const kindAccessor = stringField(
8397
9035
  fieldRef(alias, ["kind"], { valueType: "string" })
8398
9036
  );
9037
+ const fulltextAccessor = createFulltextAccessor(
9038
+ alias,
9039
+ () => this.#hasSearchableField(kindNames)
9040
+ );
8399
9041
  return new Proxy({}, {
8400
9042
  get: (_, property) => {
8401
9043
  if (typeof property === "symbol") return;
@@ -8403,10 +9045,15 @@ var QueryBuilder = class _QueryBuilder {
8403
9045
  if (property === "toJSON") return;
8404
9046
  if (property === "id") return idAccessor;
8405
9047
  if (property === "kind") return kindAccessor;
9048
+ if (property === "$fulltext") return fulltextAccessor;
8406
9049
  return this.#getFieldBuilderForProperty(kindNames, property, alias);
8407
9050
  }
8408
9051
  });
8409
9052
  }
9053
+ #hasSearchableField(kindNames) {
9054
+ if (!kindNames) return false;
9055
+ return this.#config.schemaIntrospector.hasSearchableField(kindNames);
9056
+ }
8410
9057
  #expandTraversalEdgeKinds(edgeKind, includeImplyingEdges) {
8411
9058
  return includeImplyingEdges ? this.#config.registry.expandImplyingEdges(edgeKind) : [edgeKind];
8412
9059
  }
@@ -8449,7 +9096,7 @@ var QueryBuilder = class _QueryBuilder {
8449
9096
  property
8450
9097
  ) : void 0;
8451
9098
  const ref = fieldRef(alias, ["props"], {
8452
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([property]),
9099
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([property]),
8453
9100
  valueType: typeInfo?.valueType,
8454
9101
  elementType: typeInfo?.elementType
8455
9102
  });
@@ -8632,14 +9279,8 @@ var UnionableQuery = class _UnionableQuery {
8632
9279
  this.#cachedCompiled = compiled;
8633
9280
  return compiled;
8634
9281
  }
8635
- /**
8636
- * Builds compile options from the config.
8637
- */
8638
9282
  #compileOptions() {
8639
- return {
8640
- dialect: this.#config.dialect ?? "sqlite",
8641
- schema: this.#config.schema
8642
- };
9283
+ return buildCompileOptions(this.#config);
8643
9284
  }
8644
9285
  /**
8645
9286
  * Executes the combined query.
@@ -8701,7 +9342,7 @@ function count(alias, field2) {
8701
9342
  __type: "field_ref",
8702
9343
  alias,
8703
9344
  path: field2 ? ["props"] : ["id"],
8704
- jsonPointer: field2 ? chunkP5CNM325_cjs.jsonPointer([field2]) : void 0,
9345
+ jsonPointer: field2 ? chunkUVZHTBZ5_cjs.jsonPointer([field2]) : void 0,
8705
9346
  valueType: field2 ? void 0 : "string"
8706
9347
  }
8707
9348
  };
@@ -8714,7 +9355,7 @@ function countDistinct(alias, field2) {
8714
9355
  __type: "field_ref",
8715
9356
  alias,
8716
9357
  path: field2 ? ["props"] : ["id"],
8717
- jsonPointer: field2 ? chunkP5CNM325_cjs.jsonPointer([field2]) : void 0,
9358
+ jsonPointer: field2 ? chunkUVZHTBZ5_cjs.jsonPointer([field2]) : void 0,
8718
9359
  valueType: field2 ? void 0 : "string"
8719
9360
  }
8720
9361
  };
@@ -8727,7 +9368,7 @@ function sum(alias, field2) {
8727
9368
  __type: "field_ref",
8728
9369
  alias,
8729
9370
  path: ["props"],
8730
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
9371
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
8731
9372
  valueType: "number"
8732
9373
  }
8733
9374
  };
@@ -8740,7 +9381,7 @@ function avg(alias, field2) {
8740
9381
  __type: "field_ref",
8741
9382
  alias,
8742
9383
  path: ["props"],
8743
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
9384
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
8744
9385
  valueType: "number"
8745
9386
  }
8746
9387
  };
@@ -8753,7 +9394,7 @@ function min(alias, field2) {
8753
9394
  __type: "field_ref",
8754
9395
  alias,
8755
9396
  path: ["props"],
8756
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
9397
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
8757
9398
  valueType: "number"
8758
9399
  }
8759
9400
  };
@@ -8766,7 +9407,7 @@ function max(alias, field2) {
8766
9407
  __type: "field_ref",
8767
9408
  alias,
8768
9409
  path: ["props"],
8769
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
9410
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
8770
9411
  valueType: "number"
8771
9412
  }
8772
9413
  };
@@ -8797,7 +9438,7 @@ function field(alias, ...path) {
8797
9438
  __type: "field_ref",
8798
9439
  alias,
8799
9440
  path: ["props"],
8800
- jsonPointer: chunkP5CNM325_cjs.jsonPointer(path)
9441
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer(path)
8801
9442
  };
8802
9443
  }
8803
9444
  function having(aggregate, op, value) {
@@ -8855,16 +9496,12 @@ function offsetFragment(n) {
8855
9496
  }
8856
9497
 
8857
9498
  // src/query/builder/index.ts
8858
- setQueryBuilderClass(
8859
- QueryBuilder
8860
- );
8861
- setUnionableQueryClass(
8862
- UnionableQuery
8863
- );
9499
+ setQueryBuilderClass(QueryBuilder);
9500
+ setUnionableQueryClass(UnionableQuery);
8864
9501
 
8865
9502
  // src/query/builder.ts
8866
9503
  function createQueryBuilder(graphId, registry, options) {
8867
- const schemaIntrospector = chunkP5CNM325_cjs.createSchemaIntrospector(
9504
+ const schemaIntrospector = chunkUVZHTBZ5_cjs.createSchemaIntrospector(
8868
9505
  registry.nodeKinds,
8869
9506
  registry.edgeKinds
8870
9507
  );
@@ -8891,7 +9528,8 @@ function createQueryBuilder(graphId, registry, options) {
8891
9528
  temporalMode: "current",
8892
9529
  asOf: void 0,
8893
9530
  groupBy: void 0,
8894
- having: void 0
9531
+ having: void 0,
9532
+ fusion: void 0
8895
9533
  };
8896
9534
  return new QueryBuilder(config, initialState);
8897
9535
  }
@@ -8900,12 +9538,12 @@ function createQueryBuilder(graphId, registry, options) {
8900
9538
  function buildKindRegistry(graph) {
8901
9539
  const nodeTypes = extractNodeTypes(graph);
8902
9540
  const edgeTypes = extractEdgeTypes(graph);
8903
- const closures = graph.ontology.length > 0 ? chunkX33WV2UF_cjs.computeClosuresFromOntology(graph.ontology) : chunkX33WV2UF_cjs.createEmptyClosures();
8904
- return new chunkX33WV2UF_cjs.KindRegistry(nodeTypes, edgeTypes, closures);
9541
+ const closures = graph.ontology.length > 0 ? chunkHRNQ3DVJ_cjs.computeClosuresFromOntology(graph.ontology) : chunkHRNQ3DVJ_cjs.createEmptyClosures();
9542
+ return new chunkHRNQ3DVJ_cjs.KindRegistry(nodeTypes, edgeTypes, closures);
8905
9543
  }
8906
9544
  function extractNodeTypes(graph) {
8907
9545
  const result = /* @__PURE__ */ new Map();
8908
- for (const typeName of chunk5GZMPJOP_cjs.getNodeKinds(graph)) {
9546
+ for (const typeName of chunkWRWVWKQL_cjs.getNodeKinds(graph)) {
8909
9547
  const registration = graph.nodes[typeName];
8910
9548
  result.set(typeName, registration.type);
8911
9549
  }
@@ -8913,13 +9551,253 @@ function extractNodeTypes(graph) {
8913
9551
  }
8914
9552
  function extractEdgeTypes(graph) {
8915
9553
  const result = /* @__PURE__ */ new Map();
8916
- for (const typeName of chunk5GZMPJOP_cjs.getEdgeKinds(graph)) {
9554
+ for (const typeName of chunkWRWVWKQL_cjs.getEdgeKinds(graph)) {
8917
9555
  const registration = graph.edges[typeName];
8918
9556
  result.set(typeName, registration.type);
8919
9557
  }
8920
9558
  return result;
8921
9559
  }
8922
9560
 
9561
+ // src/store/algorithms/context.ts
9562
+ var DEFAULT_ALGORITHM_MAX_HOPS = 10;
9563
+ var DEFAULT_NEIGHBOR_DEPTH = 1;
9564
+ function resolveTemporalOptions(ctx, options) {
9565
+ return {
9566
+ temporalMode: options.temporalMode ?? ctx.defaultTemporalMode,
9567
+ ...options.asOf !== void 0 && { asOf: options.asOf }
9568
+ };
9569
+ }
9570
+ function resolveTemporalFilter(ctx, options, tableAlias) {
9571
+ const resolved = resolveTemporalOptions(ctx, options);
9572
+ const filterOptions = {
9573
+ mode: resolved.temporalMode,
9574
+ asOf: resolved.asOf,
9575
+ tableAlias,
9576
+ currentTimestamp: ctx.dialect.currentTimestamp()
9577
+ };
9578
+ return compileTemporalFilter(filterOptions);
9579
+ }
9580
+ function resolveMaxHops(rawMaxHops, fallback, optionName) {
9581
+ const value = rawMaxHops ?? fallback;
9582
+ if (!Number.isFinite(value) || !Number.isInteger(value)) {
9583
+ throw new chunkO5PST6TB_cjs.ConfigurationError(
9584
+ `Graph algorithm ${optionName} must be a finite integer, got ${String(value)}.`,
9585
+ { option: optionName, value }
9586
+ );
9587
+ }
9588
+ if (value < 1) {
9589
+ throw new chunkO5PST6TB_cjs.ConfigurationError(
9590
+ `Graph algorithm ${optionName} must be at least 1, got ${value}.`,
9591
+ { option: optionName, value }
9592
+ );
9593
+ }
9594
+ if (value > MAX_EXPLICIT_RECURSIVE_DEPTH) {
9595
+ throw new chunkO5PST6TB_cjs.ConfigurationError(
9596
+ `Graph algorithm ${optionName} (${value}) exceeds the maximum of ${MAX_EXPLICIT_RECURSIVE_DEPTH}.`,
9597
+ { option: optionName, value, limit: MAX_EXPLICIT_RECURSIVE_DEPTH }
9598
+ );
9599
+ }
9600
+ return value;
9601
+ }
9602
+ function assertEdgeKinds(edges) {
9603
+ if (edges.length === 0) {
9604
+ throw new chunkO5PST6TB_cjs.ConfigurationError(
9605
+ `Graph algorithms require at least one edge kind in 'edges'.`,
9606
+ { edges }
9607
+ );
9608
+ }
9609
+ }
9610
+
9611
+ // src/store/algorithms/degree.ts
9612
+ async function executeDegree(ctx, nodeId, options = {}) {
9613
+ const direction = options.direction ?? "both";
9614
+ const whereClauses = [
9615
+ drizzleOrm.sql`graph_id = ${ctx.graphId}`,
9616
+ resolveTemporalFilter(ctx, options),
9617
+ compileDirectionFilter(direction, nodeId)
9618
+ ];
9619
+ if (options.edges !== void 0) {
9620
+ whereClauses.push(compileKindFilter(drizzleOrm.sql.raw("kind"), options.edges));
9621
+ }
9622
+ const countExpr = direction === "both" ? drizzleOrm.sql`COUNT(DISTINCT id)` : drizzleOrm.sql`COUNT(*)`;
9623
+ const query = drizzleOrm.sql`SELECT ${countExpr} AS count FROM ${ctx.schema.edgesTable} WHERE ${drizzleOrm.sql.join(whereClauses, drizzleOrm.sql` AND `)}`;
9624
+ const rows = await ctx.backend.execute(query);
9625
+ return Number(rows[0]?.count ?? 0);
9626
+ }
9627
+ function compileDirectionFilter(direction, nodeId) {
9628
+ switch (direction) {
9629
+ case "out": {
9630
+ return drizzleOrm.sql`from_id = ${nodeId}`;
9631
+ }
9632
+ case "in": {
9633
+ return drizzleOrm.sql`to_id = ${nodeId}`;
9634
+ }
9635
+ case "both": {
9636
+ return drizzleOrm.sql`(from_id = ${nodeId} OR to_id = ${nodeId})`;
9637
+ }
9638
+ }
9639
+ }
9640
+ async function executeReachable(ctx, sourceId, options) {
9641
+ assertEdgeKinds(options.edges);
9642
+ const maxHops = resolveMaxHops(
9643
+ options.maxHops,
9644
+ DEFAULT_ALGORITHM_MAX_HOPS,
9645
+ "maxHops"
9646
+ );
9647
+ const cte = buildReachableCte({
9648
+ graphId: ctx.graphId,
9649
+ sourceId,
9650
+ edgeKinds: options.edges,
9651
+ maxHops,
9652
+ direction: options.direction ?? "out",
9653
+ cyclePolicy: options.cyclePolicy ?? "prevent",
9654
+ includePath: false,
9655
+ ...resolveTemporalOptions(ctx, options),
9656
+ dialect: ctx.dialect,
9657
+ schema: ctx.schema
9658
+ });
9659
+ const sourceFilter = options.excludeSource === true ? drizzleOrm.sql` WHERE id != ${sourceId}` : drizzleOrm.sql``;
9660
+ const query = drizzleOrm.sql`${cte} SELECT id, kind, MIN(depth) AS depth FROM reachable${sourceFilter} GROUP BY id, kind ORDER BY depth ASC, id ASC`;
9661
+ const rows = await ctx.backend.execute(query);
9662
+ return rows.map((row) => ({
9663
+ id: row.id,
9664
+ kind: row.kind,
9665
+ depth: Number(row.depth)
9666
+ }));
9667
+ }
9668
+ async function executeCanReach(ctx, sourceId, targetId, options) {
9669
+ assertEdgeKinds(options.edges);
9670
+ const maxHops = resolveMaxHops(
9671
+ options.maxHops,
9672
+ DEFAULT_ALGORITHM_MAX_HOPS,
9673
+ "maxHops"
9674
+ );
9675
+ const cte = buildReachableCte({
9676
+ graphId: ctx.graphId,
9677
+ sourceId,
9678
+ edgeKinds: options.edges,
9679
+ maxHops,
9680
+ direction: options.direction ?? "out",
9681
+ cyclePolicy: options.cyclePolicy ?? "prevent",
9682
+ includePath: false,
9683
+ ...resolveTemporalOptions(ctx, options),
9684
+ dialect: ctx.dialect,
9685
+ schema: ctx.schema
9686
+ });
9687
+ const query = drizzleOrm.sql`${cte} SELECT 1 AS hit FROM reachable WHERE id = ${targetId} LIMIT 1`;
9688
+ const rows = await ctx.backend.execute(query);
9689
+ return rows.length > 0;
9690
+ }
9691
+ async function executeNeighbors(ctx, sourceId, options) {
9692
+ const depth = resolveMaxHops(options.depth, DEFAULT_NEIGHBOR_DEPTH, "depth");
9693
+ return executeReachable(ctx, sourceId, {
9694
+ edges: options.edges,
9695
+ maxHops: depth,
9696
+ direction: options.direction ?? "out",
9697
+ cyclePolicy: options.cyclePolicy ?? "prevent",
9698
+ ...options.temporalMode !== void 0 && {
9699
+ temporalMode: options.temporalMode
9700
+ },
9701
+ ...options.asOf !== void 0 && { asOf: options.asOf },
9702
+ excludeSource: true
9703
+ });
9704
+ }
9705
+ async function executeShortestPath(ctx, sourceId, targetId, options) {
9706
+ assertEdgeKinds(options.edges);
9707
+ const maxHops = resolveMaxHops(
9708
+ options.maxHops,
9709
+ DEFAULT_ALGORITHM_MAX_HOPS,
9710
+ "maxHops"
9711
+ );
9712
+ if (sourceId === targetId) {
9713
+ const source = await fetchNodeKind(ctx, sourceId, options);
9714
+ if (source === void 0) return void 0;
9715
+ return { nodes: [source], depth: 0 };
9716
+ }
9717
+ const cte = buildReachableCte({
9718
+ graphId: ctx.graphId,
9719
+ sourceId,
9720
+ edgeKinds: options.edges,
9721
+ maxHops,
9722
+ direction: options.direction ?? "out",
9723
+ cyclePolicy: options.cyclePolicy ?? "prevent",
9724
+ includePath: true,
9725
+ ...resolveTemporalOptions(ctx, options),
9726
+ dialect: ctx.dialect,
9727
+ schema: ctx.schema
9728
+ });
9729
+ const pathContainsCheck = drizzleOrm.sql`NOT (${ctx.dialect.cycleCheck(drizzleOrm.sql.raw("r.id"), drizzleOrm.sql.raw("h.path"))})`;
9730
+ const query = drizzleOrm.sql`${cte}, hit AS (SELECT id, kind, depth, path FROM reachable WHERE id = ${targetId} ORDER BY depth ASC LIMIT 1) SELECT h.id AS hit_id, h.kind AS hit_kind, h.depth AS hit_depth, h.path AS hit_path, r.id AS node_id, r.kind AS node_kind FROM hit h LEFT JOIN reachable r ON ${pathContainsCheck}`;
9731
+ const rows = await ctx.backend.execute(query);
9732
+ const first = rows[0];
9733
+ if (first === void 0) return void 0;
9734
+ const pathIds = normalizePath(first.hit_path);
9735
+ const kindById = buildKindIndex(rows);
9736
+ const pathNodes = pathIds.map((id) => ({
9737
+ id,
9738
+ kind: kindById.get(id) ?? ""
9739
+ }));
9740
+ return { nodes: pathNodes, depth: Number(first.hit_depth) };
9741
+ }
9742
+ function buildKindIndex(rows) {
9743
+ const map = /* @__PURE__ */ new Map();
9744
+ for (const row of rows) {
9745
+ if (row.node_id !== void 0 && row.node_kind !== void 0) {
9746
+ map.set(row.node_id, row.node_kind);
9747
+ }
9748
+ }
9749
+ return map;
9750
+ }
9751
+ async function fetchNodeKind(ctx, nodeId, options) {
9752
+ const temporalFilter = resolveTemporalFilter(ctx, options);
9753
+ const query = drizzleOrm.sql`SELECT id, kind FROM ${ctx.schema.nodesTable} WHERE graph_id = ${ctx.graphId} AND id = ${nodeId} AND ${temporalFilter} LIMIT 1`;
9754
+ const rows = await ctx.backend.execute(query);
9755
+ const row = rows[0];
9756
+ if (row === void 0) return void 0;
9757
+ return { id: row.id, kind: row.kind };
9758
+ }
9759
+
9760
+ // src/store/algorithms/index.ts
9761
+ function resolveNodeId(value) {
9762
+ return typeof value === "string" ? value : value.id;
9763
+ }
9764
+ function createGraphAlgorithms(params) {
9765
+ const ctx = {
9766
+ graphId: params.graphId,
9767
+ backend: params.backend,
9768
+ dialect: chunkFQCQM3KG_cjs.getDialect(params.backend.dialect),
9769
+ schema: params.schema ?? (params.backend.tableNames ? createSqlSchema(params.backend.tableNames) : createSqlSchema()),
9770
+ defaultTemporalMode: params.defaultTemporalMode
9771
+ };
9772
+ return {
9773
+ shortestPath(from, to, options) {
9774
+ return executeShortestPath(
9775
+ ctx,
9776
+ resolveNodeId(from),
9777
+ resolveNodeId(to),
9778
+ options
9779
+ );
9780
+ },
9781
+ reachable(from, options) {
9782
+ return executeReachable(ctx, resolveNodeId(from), options);
9783
+ },
9784
+ canReach(from, to, options) {
9785
+ return executeCanReach(
9786
+ ctx,
9787
+ resolveNodeId(from),
9788
+ resolveNodeId(to),
9789
+ options
9790
+ );
9791
+ },
9792
+ neighbors(node, options) {
9793
+ return executeNeighbors(ctx, resolveNodeId(node), options);
9794
+ },
9795
+ degree(node, options) {
9796
+ return executeDegree(ctx, resolveNodeId(node), options ?? {});
9797
+ }
9798
+ };
9799
+ }
9800
+
8923
9801
  // src/store/collections/edge-collection.ts
8924
9802
  function narrowEdge(edge) {
8925
9803
  return edge;
@@ -8992,20 +9870,13 @@ function createEdgeCollection(config) {
8992
9870
  const result = {};
8993
9871
  if (options?.matchOn !== void 0)
8994
9872
  result.matchOn = options.matchOn;
8995
- if (options?.props !== void 0)
8996
- result.props = options.props;
9873
+ if (options?.props !== void 0) result.props = options.props;
8997
9874
  return result;
8998
9875
  }
8999
9876
  return {
9000
9877
  async create(from, to, props, options) {
9001
9878
  const result = await executeEdgeCreate2(
9002
- buildCreateEdgeInput(
9003
- kind,
9004
- from,
9005
- to,
9006
- props ?? {},
9007
- options
9008
- ),
9879
+ buildCreateEdgeInput(kind, from, to, props ?? {}, options),
9009
9880
  backend
9010
9881
  );
9011
9882
  return narrowEdge(result);
@@ -9100,7 +9971,7 @@ function createEdgeCollection(config) {
9100
9971
  temporalMode: mode
9101
9972
  };
9102
9973
  if (mode === "current" || mode === "asOf") {
9103
- params.asOf = options?.asOf ?? chunk5GZMPJOP_cjs.nowIso();
9974
+ params.asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
9104
9975
  }
9105
9976
  if (options?.from?.kind !== void 0)
9106
9977
  params.fromKind = options.from.kind;
@@ -9121,7 +9992,7 @@ function createEdgeCollection(config) {
9121
9992
  temporalMode: mode
9122
9993
  };
9123
9994
  if (mode === "current" || mode === "asOf") {
9124
- params.asOf = options?.asOf ?? chunk5GZMPJOP_cjs.nowIso();
9995
+ params.asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
9125
9996
  }
9126
9997
  if (options?.from?.kind !== void 0)
9127
9998
  params.fromKind = options.from.kind;
@@ -9131,10 +10002,7 @@ function createEdgeCollection(config) {
9131
10002
  return backend.countEdgesByKind(params);
9132
10003
  },
9133
10004
  async bulkCreate(items) {
9134
- const batchInputs = mapBulkEdgeInputs(
9135
- kind,
9136
- items
9137
- );
10005
+ const batchInputs = mapBulkEdgeInputs(kind, items);
9138
10006
  if (backend.capabilities.transactions && "transaction" in backend) {
9139
10007
  const results2 = await backend.transaction(
9140
10008
  async (txBackend) => executeEdgeCreateBatch2(batchInputs, txBackend)
@@ -9170,11 +10038,7 @@ function createEdgeCollection(config) {
9170
10038
  if (existing) {
9171
10039
  toUpdate.push({
9172
10040
  index: itemIndex,
9173
- input: buildUpdateEdgeInput(
9174
- item.id,
9175
- item.props ?? {},
9176
- item
9177
- ),
10041
+ input: buildUpdateEdgeInput(item.id, item.props ?? {}, item),
9178
10042
  clearDeleted: existing.deleted_at !== void 0
9179
10043
  });
9180
10044
  } else {
@@ -9213,10 +10077,7 @@ function createEdgeCollection(config) {
9213
10077
  return upsertAll(backend);
9214
10078
  },
9215
10079
  async bulkInsert(items) {
9216
- const batchInputs = mapBulkEdgeInputs(
9217
- kind,
9218
- items
9219
- );
10080
+ const batchInputs = mapBulkEdgeInputs(kind, items);
9220
10081
  if (backend.capabilities.transactions && "transaction" in backend) {
9221
10082
  await backend.transaction(async (txBackend) => {
9222
10083
  await executeEdgeCreateNoReturnBatch2(batchInputs, txBackend);
@@ -9367,11 +10228,7 @@ function createNodeCollection(config) {
9367
10228
  async getByIds(ids, options) {
9368
10229
  if (ids.length === 0) return [];
9369
10230
  if (backend.getNodes !== void 0) {
9370
- const rows = await backend.getNodes(
9371
- graphId,
9372
- kind,
9373
- ids
9374
- );
10231
+ const rows = await backend.getNodes(graphId, kind, ids);
9375
10232
  const rowMap = /* @__PURE__ */ new Map();
9376
10233
  for (const row of rows) {
9377
10234
  rowMap.set(row.id, row);
@@ -9416,7 +10273,7 @@ function createNodeCollection(config) {
9416
10273
  const mode2 = options.temporalMode ?? defaultTemporalMode;
9417
10274
  let query = createQuery().from(kind, "_n").temporal(
9418
10275
  mode2,
9419
- mode2 === "asOf" ? options.asOf ?? chunk5GZMPJOP_cjs.nowIso() : void 0
10276
+ mode2 === "asOf" ? options.asOf ?? chunkWRWVWKQL_cjs.nowIso() : void 0
9420
10277
  ).whereNode("_n", options.where).select((ctx) => ctx._n);
9421
10278
  if (options.limit !== void 0) query = query.limit(options.limit);
9422
10279
  if (options.offset !== void 0) query = query.offset(options.offset);
@@ -9431,7 +10288,7 @@ function createNodeCollection(config) {
9431
10288
  temporalMode: mode
9432
10289
  };
9433
10290
  if (mode === "current" || mode === "asOf") {
9434
- params.asOf = options?.asOf ?? chunk5GZMPJOP_cjs.nowIso();
10291
+ params.asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
9435
10292
  }
9436
10293
  if (options?.limit !== void 0) params.limit = options.limit;
9437
10294
  if (options?.offset !== void 0) params.offset = options.offset;
@@ -9447,16 +10304,12 @@ function createNodeCollection(config) {
9447
10304
  temporalMode: mode
9448
10305
  };
9449
10306
  if (mode === "current" || mode === "asOf") {
9450
- params.asOf = options?.asOf ?? chunk5GZMPJOP_cjs.nowIso();
10307
+ params.asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
9451
10308
  }
9452
10309
  return backend.countNodesByKind(params);
9453
10310
  },
9454
10311
  async upsertById(id, props, options) {
9455
- return this.upsertByIdFromRecord(
9456
- id,
9457
- props,
9458
- options
9459
- );
10312
+ return this.upsertByIdFromRecord(id, props, options);
9460
10313
  },
9461
10314
  async upsertByIdFromRecord(id, data, options) {
9462
10315
  const existing = await backend.getNode(graphId, kind, id);
@@ -9476,10 +10329,7 @@ function createNodeCollection(config) {
9476
10329
  return narrowNode(result);
9477
10330
  },
9478
10331
  async bulkCreate(items) {
9479
- const batchInputs = mapBulkNodeInputs(
9480
- kind,
9481
- items
9482
- );
10332
+ const batchInputs = mapBulkNodeInputs(kind, items);
9483
10333
  if (backend.capabilities.transactions && "transaction" in backend) {
9484
10334
  const results2 = await backend.transaction(
9485
10335
  async (txBackend) => executeNodeCreateBatch2(batchInputs, txBackend)
@@ -9502,11 +10352,7 @@ function createNodeCollection(config) {
9502
10352
  if (row !== void 0) existingMap.set(row.id, row);
9503
10353
  }
9504
10354
  } else {
9505
- const rows = await target.getNodes(
9506
- graphId,
9507
- kind,
9508
- ids
9509
- );
10355
+ const rows = await target.getNodes(graphId, kind, ids);
9510
10356
  for (const row of rows) {
9511
10357
  existingMap.set(row.id, row);
9512
10358
  }
@@ -9519,22 +10365,13 @@ function createNodeCollection(config) {
9519
10365
  if (existing) {
9520
10366
  toUpdate.push({
9521
10367
  index: itemIndex,
9522
- input: buildUpdateInput(
9523
- kind,
9524
- item.id,
9525
- item.props,
9526
- item
9527
- ),
10368
+ input: buildUpdateInput(kind, item.id, item.props, item),
9528
10369
  clearDeleted: existing.deleted_at !== void 0
9529
10370
  });
9530
10371
  } else {
9531
10372
  toCreate.push({
9532
10373
  index: itemIndex,
9533
- input: buildCreateInput(
9534
- kind,
9535
- item.props,
9536
- item
9537
- )
10374
+ input: buildCreateInput(kind, item.props, item)
9538
10375
  });
9539
10376
  }
9540
10377
  itemIndex++;
@@ -9561,10 +10398,7 @@ function createNodeCollection(config) {
9561
10398
  return upsertAll(backend);
9562
10399
  },
9563
10400
  async bulkInsert(items) {
9564
- const batchInputs = mapBulkNodeInputs(
9565
- kind,
9566
- items
9567
- );
10401
+ const batchInputs = mapBulkNodeInputs(kind, items);
9568
10402
  if (backend.capabilities.transactions && "transaction" in backend) {
9569
10403
  await backend.transaction(async (txBackend) => {
9570
10404
  await executeNodeCreateNoReturnBatch2(batchInputs, txBackend);
@@ -10097,12 +10931,12 @@ async function validateAndPrepareEdgeCreate(ctx, input, id, backend) {
10097
10931
  nodeId: input.toId
10098
10932
  });
10099
10933
  }
10100
- const validatedProps = chunkX33WV2UF_cjs.validateEdgeProps(edgeKind.schema, input.props, {
10934
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, input.props, {
10101
10935
  kind,
10102
10936
  operation: "create"
10103
10937
  });
10104
- const validFrom = chunk5GZMPJOP_cjs.validateOptionalIsoDate(input.validFrom, "validFrom");
10105
- const validTo = chunk5GZMPJOP_cjs.validateOptionalIsoDate(input.validTo, "validTo");
10938
+ const validFrom = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validFrom, "validFrom");
10939
+ const validTo = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validTo, "validTo");
10106
10940
  const cardinality = registration.cardinality ?? "many";
10107
10941
  const constraintContext = {
10108
10942
  graphId: ctx.graphId,
@@ -10247,12 +11081,12 @@ async function executeEdgeUpdate(ctx, input, backend) {
10247
11081
  const edgeKind = registration.type;
10248
11082
  const existingProps = JSON.parse(existing.props);
10249
11083
  const mergedProps = { ...existingProps, ...input.props };
10250
- const validatedProps = chunkX33WV2UF_cjs.validateEdgeProps(edgeKind.schema, mergedProps, {
11084
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, mergedProps, {
10251
11085
  kind: existing.kind,
10252
11086
  operation: "update",
10253
11087
  id
10254
11088
  });
10255
- const validTo = chunk5GZMPJOP_cjs.validateOptionalIsoDate(input.validTo, "validTo");
11089
+ const validTo = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validTo, "validTo");
10256
11090
  const updateParams = {
10257
11091
  graphId: ctx.graphId,
10258
11092
  id,
@@ -10273,12 +11107,12 @@ async function executeEdgeUpsertUpdate(ctx, input, backend, options) {
10273
11107
  const edgeKind = registration.type;
10274
11108
  const existingProps = JSON.parse(existing.props);
10275
11109
  const mergedProps = { ...existingProps, ...input.props };
10276
- const validatedProps = chunkX33WV2UF_cjs.validateEdgeProps(edgeKind.schema, mergedProps, {
11110
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, mergedProps, {
10277
11111
  kind: existing.kind,
10278
11112
  operation: "update",
10279
11113
  id
10280
11114
  });
10281
- const validTo = chunk5GZMPJOP_cjs.validateOptionalIsoDate(input.validTo, "validTo");
11115
+ const validTo = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validTo, "validTo");
10282
11116
  const updateParams = {
10283
11117
  graphId: ctx.graphId,
10284
11118
  id,
@@ -10430,7 +11264,7 @@ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, to
10430
11264
  const matchOn = options?.matchOn ?? [];
10431
11265
  const registration = getEdgeRegistration(ctx.graph, kind);
10432
11266
  const edgeKind = registration.type;
10433
- const validatedProps = chunkX33WV2UF_cjs.validateEdgeProps(edgeKind.schema, props, {
11267
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, props, {
10434
11268
  kind,
10435
11269
  operation: "create"
10436
11270
  });
@@ -10512,7 +11346,7 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
10512
11346
  validateMatchOnFields(edgeKind.schema, matchOn, kind);
10513
11347
  const validated = [];
10514
11348
  for (const item of items) {
10515
- const validatedProps = chunkX33WV2UF_cjs.validateEdgeProps(edgeKind.schema, item.props, {
11349
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, item.props, {
10516
11350
  kind,
10517
11351
  operation: "create"
10518
11352
  });
@@ -10670,15 +11504,19 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
10670
11504
  }
10671
11505
 
10672
11506
  // src/store/embedding-sync.ts
11507
+ var embeddingFieldsCache = /* @__PURE__ */ new WeakMap();
10673
11508
  function getEmbeddingFields(schema) {
10674
- if (schema.type !== "object") {
10675
- return [];
10676
- }
11509
+ const cached = embeddingFieldsCache.get(schema);
11510
+ if (cached) return cached;
11511
+ const fields = computeEmbeddingFields(schema);
11512
+ embeddingFieldsCache.set(schema, fields);
11513
+ return fields;
11514
+ }
11515
+ function computeEmbeddingFields(schema) {
11516
+ if (schema.type !== "object") return [];
10677
11517
  const def = schema.def;
10678
11518
  const shape = def.shape;
10679
- if (!shape) {
10680
- return [];
10681
- }
11519
+ if (!shape) return [];
10682
11520
  const fields = [];
10683
11521
  for (const [fieldPath, fieldSchema] of Object.entries(shape)) {
10684
11522
  const dimensions = getEmbeddingDimensionsFromField(fieldSchema);
@@ -10689,13 +11527,13 @@ function getEmbeddingFields(schema) {
10689
11527
  return fields;
10690
11528
  }
10691
11529
  function getEmbeddingDimensionsFromField(schema) {
10692
- const directDimensions = chunkP5CNM325_cjs.getEmbeddingDimensions(schema);
11530
+ const directDimensions = chunkUVZHTBZ5_cjs.getEmbeddingDimensions(schema);
10693
11531
  if (directDimensions !== void 0) {
10694
11532
  return directDimensions;
10695
11533
  }
10696
11534
  const unwrapped = unwrapToEmbedding(schema);
10697
11535
  if (unwrapped) {
10698
- return chunkP5CNM325_cjs.getEmbeddingDimensions(unwrapped);
11536
+ return chunkUVZHTBZ5_cjs.getEmbeddingDimensions(unwrapped);
10699
11537
  }
10700
11538
  return void 0;
10701
11539
  }
@@ -10703,7 +11541,7 @@ function unwrapToEmbedding(schema) {
10703
11541
  const type = schema.type;
10704
11542
  const def = schema.def;
10705
11543
  if ((type === "optional" || type === "nullable" || type === "default" || type === "readonly") && def.innerType) {
10706
- if (chunkP5CNM325_cjs.isEmbeddingSchema(def.innerType)) {
11544
+ if (chunkUVZHTBZ5_cjs.isEmbeddingSchema(def.innerType)) {
10707
11545
  return def.innerType;
10708
11546
  }
10709
11547
  return unwrapToEmbedding(def.innerType);
@@ -10765,6 +11603,101 @@ function isValidEmbeddingValue(value) {
10765
11603
  return value.every((n) => typeof n === "number" && Number.isFinite(n));
10766
11604
  }
10767
11605
 
11606
+ // src/store/fulltext-sync.ts
11607
+ var searchableFieldsCache = /* @__PURE__ */ new WeakMap();
11608
+ function getSearchableFields(schema) {
11609
+ const cached = searchableFieldsCache.get(schema);
11610
+ if (cached) return cached;
11611
+ const fields = computeSearchableFields(schema);
11612
+ searchableFieldsCache.set(schema, fields);
11613
+ return fields;
11614
+ }
11615
+ function computeSearchableFields(schema) {
11616
+ if (schema.type !== "object") return [];
11617
+ const def = schema.def;
11618
+ const shape = def.shape;
11619
+ if (!shape) return [];
11620
+ const fields = [];
11621
+ for (const [fieldPath, fieldSchema] of Object.entries(shape)) {
11622
+ const metadata = chunkUVZHTBZ5_cjs.getSearchableMetadata(fieldSchema);
11623
+ if (metadata !== void 0) {
11624
+ fields.push({ fieldPath, metadata });
11625
+ }
11626
+ }
11627
+ warnIfConflictingLanguages(fields);
11628
+ return fields;
11629
+ }
11630
+ function warnIfConflictingLanguages(fields) {
11631
+ if (fields.length < 2) return;
11632
+ const languages = new Set(fields.map((field2) => field2.metadata.language));
11633
+ if (languages.size < 2) return;
11634
+ if (typeof console === "undefined" || typeof console.warn !== "function") {
11635
+ return;
11636
+ }
11637
+ const fieldSummary = fields.map((field2) => `${field2.fieldPath}=${field2.metadata.language}`).join(", ");
11638
+ const winning = fields[0]?.metadata.language ?? chunkUVZHTBZ5_cjs.DEFAULT_SEARCHABLE_LANGUAGE;
11639
+ console.warn(
11640
+ `[typegraph] searchable() fields declare conflicting languages (${fieldSummary}). The first field's language ("${winning}") is recorded on the fulltext row; true multilingual indexing requires a dedicated node kind per language.`
11641
+ );
11642
+ }
11643
+ var FIELD_SEPARATOR = "\n";
11644
+ function resolveCombinedLanguage(fields) {
11645
+ return fields[0]?.metadata.language ?? chunkUVZHTBZ5_cjs.DEFAULT_SEARCHABLE_LANGUAGE;
11646
+ }
11647
+ function computeFulltextContent(schema, props) {
11648
+ const searchableFields = getSearchableFields(schema);
11649
+ if (searchableFields.length === 0) return void 0;
11650
+ const parts = [];
11651
+ for (const field2 of searchableFields) {
11652
+ const value = props[field2.fieldPath];
11653
+ if (typeof value === "string" && value.length > 0) {
11654
+ parts.push(value);
11655
+ }
11656
+ }
11657
+ if (parts.length === 0) return void 0;
11658
+ return {
11659
+ content: parts.join(FIELD_SEPARATOR),
11660
+ language: resolveCombinedLanguage(searchableFields)
11661
+ };
11662
+ }
11663
+ async function syncFulltext(ctx, schema, props) {
11664
+ const { backend } = ctx;
11665
+ if (!backend.upsertFulltext || !backend.deleteFulltext) {
11666
+ return;
11667
+ }
11668
+ const computed = computeFulltextContent(schema, props);
11669
+ if (computed !== void 0) {
11670
+ await backend.upsertFulltext({
11671
+ graphId: ctx.graphId,
11672
+ nodeKind: ctx.nodeKind,
11673
+ nodeId: ctx.nodeId,
11674
+ content: computed.content,
11675
+ language: computed.language
11676
+ });
11677
+ return;
11678
+ }
11679
+ if (getSearchableFields(schema).length === 0) return;
11680
+ await backend.deleteFulltext({
11681
+ graphId: ctx.graphId,
11682
+ nodeKind: ctx.nodeKind,
11683
+ nodeId: ctx.nodeId
11684
+ });
11685
+ }
11686
+ async function deleteNodeFulltext(ctx, schema) {
11687
+ const { backend } = ctx;
11688
+ if (!backend.deleteFulltext) {
11689
+ return;
11690
+ }
11691
+ if (getSearchableFields(schema).length === 0) {
11692
+ return;
11693
+ }
11694
+ await backend.deleteFulltext({
11695
+ graphId: ctx.graphId,
11696
+ nodeKind: ctx.nodeKind,
11697
+ nodeId: ctx.nodeId
11698
+ });
11699
+ }
11700
+
10768
11701
  // src/store/uniqueness.ts
10769
11702
  async function checkUniquenessConstraints(ctx, kind, id, props, constraints) {
10770
11703
  for (const constraint of constraints) {
@@ -10958,6 +11891,9 @@ function createUniquenessContext(graphId, registry, backend) {
10958
11891
  function createEmbeddingSyncContext(graphId, nodeKind, nodeId, backend) {
10959
11892
  return { graphId, nodeKind, nodeId, backend };
10960
11893
  }
11894
+ function createFulltextSyncContext(graphId, nodeKind, nodeId, backend) {
11895
+ return { graphId, nodeKind, nodeId, backend };
11896
+ }
10961
11897
  function createNodeBatchValidationBackend(graphId, registry, backend) {
10962
11898
  const nodeCache = /* @__PURE__ */ new Map();
10963
11899
  const pendingNodes = /* @__PURE__ */ new Map();
@@ -11057,12 +11993,12 @@ async function validateAndPrepareNodeCreate(ctx, input, id, backend) {
11057
11993
  const kind = input.kind;
11058
11994
  const registration = getNodeRegistration(ctx.graph, kind);
11059
11995
  const nodeKind = registration.type;
11060
- const validatedProps = chunkX33WV2UF_cjs.validateNodeProps(nodeKind.schema, input.props, {
11996
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, input.props, {
11061
11997
  kind,
11062
11998
  operation: "create"
11063
11999
  });
11064
- const validFrom = chunk5GZMPJOP_cjs.validateOptionalIsoDate(input.validFrom, "validFrom");
11065
- const validTo = chunk5GZMPJOP_cjs.validateOptionalIsoDate(input.validTo, "validTo");
12000
+ const validFrom = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validFrom, "validFrom");
12001
+ const validTo = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validTo, "validTo");
11066
12002
  const existingNode = await backend.getNode(ctx.graphId, kind, id);
11067
12003
  if (existingNode && !existingNode.deleted_at) {
11068
12004
  throw createNodeAlreadyExistsError(kind, id);
@@ -11115,6 +12051,11 @@ async function finalizeNodeCreate(ctx, prepared, backend) {
11115
12051
  prepared.nodeKind.schema,
11116
12052
  prepared.validatedProps
11117
12053
  );
12054
+ await syncFulltext(
12055
+ createFulltextSyncContext(ctx.graphId, prepared.kind, prepared.id, backend),
12056
+ prepared.nodeKind.schema,
12057
+ prepared.validatedProps
12058
+ );
11118
12059
  }
11119
12060
  async function performNodeUpdate(ctx, input, backend, options) {
11120
12061
  const { kind, id } = input;
@@ -11126,12 +12067,12 @@ async function performNodeUpdate(ctx, input, backend, options) {
11126
12067
  const existingProps = JSON.parse(existing.props);
11127
12068
  const mergedProps = { ...existingProps, ...input.props };
11128
12069
  const nodeKind = registration.type;
11129
- const validatedProps = chunkX33WV2UF_cjs.validateNodeProps(nodeKind.schema, mergedProps, {
12070
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, mergedProps, {
11130
12071
  kind,
11131
12072
  operation: "update",
11132
12073
  id
11133
12074
  });
11134
- const validTo = chunk5GZMPJOP_cjs.validateOptionalIsoDate(input.validTo, "validTo");
12075
+ const validTo = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validTo, "validTo");
11135
12076
  await updateUniquenessEntries(
11136
12077
  createUniquenessContext(ctx.graphId, ctx.registry, backend),
11137
12078
  kind,
@@ -11155,6 +12096,11 @@ async function performNodeUpdate(ctx, input, backend, options) {
11155
12096
  nodeKind.schema,
11156
12097
  validatedProps
11157
12098
  );
12099
+ await syncFulltext(
12100
+ createFulltextSyncContext(ctx.graphId, kind, id, backend),
12101
+ nodeKind.schema,
12102
+ validatedProps
12103
+ );
11158
12104
  return rowToNode(row);
11159
12105
  }
11160
12106
  async function enforceDeleteBehavior(ctx, kind, id, mode, backend, registration) {
@@ -11371,6 +12317,10 @@ async function executeNodeDelete(ctx, kind, id, backend) {
11371
12317
  createEmbeddingSyncContext(ctx.graphId, kind, id, backend),
11372
12318
  nodeKind.schema
11373
12319
  );
12320
+ await deleteNodeFulltext(
12321
+ createFulltextSyncContext(ctx.graphId, kind, id, backend),
12322
+ nodeKind.schema
12323
+ );
11374
12324
  });
11375
12325
  }
11376
12326
  async function executeNodeHardDelete(ctx, kind, id, backend) {
@@ -11390,7 +12340,7 @@ async function executeNodeGetOrCreateByConstraint(ctx, kind, constraintName, pro
11390
12340
  const ifExists = options?.ifExists ?? "return";
11391
12341
  const registration = getNodeRegistration(ctx.graph, kind);
11392
12342
  const nodeKind = registration.type;
11393
- const validatedProps = chunkX33WV2UF_cjs.validateNodeProps(nodeKind.schema, props, {
12343
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, props, {
11394
12344
  kind,
11395
12345
  operation: "create"
11396
12346
  });
@@ -11462,7 +12412,7 @@ async function executeNodeGetOrCreateByConstraint(ctx, kind, constraintName, pro
11462
12412
  async function executeNodeFindByConstraint(ctx, kind, constraintName, props, backend) {
11463
12413
  const registration = getNodeRegistration(ctx.graph, kind);
11464
12414
  const nodeKind = registration.type;
11465
- const validatedProps = chunkX33WV2UF_cjs.validateNodeProps(nodeKind.schema, props, {
12415
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, props, {
11466
12416
  kind,
11467
12417
  operation: "create"
11468
12418
  });
@@ -11499,7 +12449,7 @@ async function executeNodeFindByConstraint(ctx, kind, constraintName, props, bac
11499
12449
  function validateAndComputeKeys(nodeKind, kind, constraint, items) {
11500
12450
  const validated = [];
11501
12451
  for (const item of items) {
11502
- const validatedProps = chunkX33WV2UF_cjs.validateNodeProps(nodeKind.schema, item.props, {
12452
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, item.props, {
11503
12453
  kind,
11504
12454
  operation: "create"
11505
12455
  });
@@ -11677,6 +12627,503 @@ async function executeNodeBulkGetOrCreateByConstraint(ctx, kind, constraintName,
11677
12627
  return results;
11678
12628
  }
11679
12629
 
12630
+ // src/store/fulltext-rebuild.ts
12631
+ var DEFAULT_PAGE_SIZE = 500;
12632
+ var DEFAULT_MAX_SKIPPED_IDS = 1e4;
12633
+ function validatePageSize(value) {
12634
+ if (value === void 0) return DEFAULT_PAGE_SIZE;
12635
+ if (!Number.isInteger(value) || value <= 0) {
12636
+ throw new chunkO5PST6TB_cjs.ValidationError(
12637
+ `pageSize must be a positive integer, got: ${String(value)}`,
12638
+ {
12639
+ issues: [{ path: "pageSize", message: "Must be a positive integer." }]
12640
+ }
12641
+ );
12642
+ }
12643
+ return value;
12644
+ }
12645
+ function validateMaxSkippedIds(value) {
12646
+ if (value === void 0) return DEFAULT_MAX_SKIPPED_IDS;
12647
+ if (!Number.isInteger(value) || value < 0) {
12648
+ throw new chunkO5PST6TB_cjs.ValidationError(
12649
+ `maxSkippedIds must be a non-negative integer, got: ${String(value)}`,
12650
+ {
12651
+ issues: [
12652
+ {
12653
+ path: "maxSkippedIds",
12654
+ message: "Must be a non-negative integer."
12655
+ }
12656
+ ]
12657
+ }
12658
+ );
12659
+ }
12660
+ return value;
12661
+ }
12662
+ function isPropsObject(value) {
12663
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12664
+ }
12665
+ async function rebuildFulltextIndex(ctx, nodeKind, options) {
12666
+ const { backend, registry } = ctx;
12667
+ if (!backend.upsertFulltext || !backend.deleteFulltext) {
12668
+ throw new chunkO5PST6TB_cjs.ConfigurationError(
12669
+ "Backend does not support fulltext; cannot rebuild index",
12670
+ { backend: backend.dialect, capability: "fulltext" }
12671
+ );
12672
+ }
12673
+ const pageSize = validatePageSize(options.pageSize);
12674
+ const maxSkippedIds = validateMaxSkippedIds(options.maxSkippedIds);
12675
+ const targetKinds = nodeKind === void 0 ? [...registry.nodeKinds.keys()] : [nodeKind];
12676
+ const rebuiltKinds = [];
12677
+ let processed = 0;
12678
+ let upserted = 0;
12679
+ let cleared = 0;
12680
+ let skipped = 0;
12681
+ const skippedIds = [];
12682
+ for (const kind of targetKinds) {
12683
+ const nodeType = registry.getNodeType(kind);
12684
+ if (!nodeType) {
12685
+ throw new chunkO5PST6TB_cjs.ConfigurationError(`Unknown node kind: ${kind}`, { kind });
12686
+ }
12687
+ if (getSearchableFields(nodeType.schema).length === 0) {
12688
+ continue;
12689
+ }
12690
+ rebuiltKinds.push(kind);
12691
+ let cursor;
12692
+ for (; ; ) {
12693
+ const rows = await backend.findNodesByKind({
12694
+ graphId: ctx.graphId,
12695
+ kind,
12696
+ limit: pageSize,
12697
+ excludeDeleted: false,
12698
+ orderBy: "id",
12699
+ ...cursor === void 0 ? {} : { after: cursor }
12700
+ });
12701
+ if (rows.length === 0) break;
12702
+ const pageResult = processPage(nodeType.schema, rows);
12703
+ skipped += pageResult.skipped;
12704
+ const remaining = maxSkippedIds - skippedIds.length;
12705
+ if (remaining > 0 && pageResult.skippedIds.length > 0) {
12706
+ skippedIds.push(...pageResult.skippedIds.slice(0, remaining));
12707
+ }
12708
+ await backend.transaction(async (tx) => {
12709
+ if (pageResult.toUpsert.length > 0) {
12710
+ if (tx.upsertFulltextBatch) {
12711
+ await tx.upsertFulltextBatch({
12712
+ graphId: ctx.graphId,
12713
+ nodeKind: kind,
12714
+ rows: pageResult.toUpsert
12715
+ });
12716
+ } else if (tx.upsertFulltext) {
12717
+ for (const item of pageResult.toUpsert) {
12718
+ await tx.upsertFulltext({
12719
+ graphId: ctx.graphId,
12720
+ nodeKind: kind,
12721
+ nodeId: item.nodeId,
12722
+ content: item.content,
12723
+ language: item.language
12724
+ });
12725
+ }
12726
+ }
12727
+ }
12728
+ if (pageResult.toDelete.length > 0) {
12729
+ if (tx.deleteFulltextBatch) {
12730
+ await tx.deleteFulltextBatch({
12731
+ graphId: ctx.graphId,
12732
+ nodeKind: kind,
12733
+ nodeIds: pageResult.toDelete
12734
+ });
12735
+ } else if (tx.deleteFulltext) {
12736
+ for (const nodeId of pageResult.toDelete) {
12737
+ await tx.deleteFulltext({
12738
+ graphId: ctx.graphId,
12739
+ nodeKind: kind,
12740
+ nodeId
12741
+ });
12742
+ }
12743
+ }
12744
+ }
12745
+ });
12746
+ processed += rows.length;
12747
+ upserted += pageResult.toUpsert.length;
12748
+ cleared += pageResult.toDelete.length;
12749
+ const lastRow = rows.at(-1);
12750
+ if (!lastRow) break;
12751
+ cursor = lastRow.id;
12752
+ if (rows.length < pageSize) break;
12753
+ }
12754
+ }
12755
+ return {
12756
+ kinds: rebuiltKinds,
12757
+ processed,
12758
+ upserted,
12759
+ cleared,
12760
+ skipped,
12761
+ skippedIds,
12762
+ skippedTruncated: skipped > skippedIds.length
12763
+ };
12764
+ }
12765
+ function processPage(schema, rows) {
12766
+ const toUpsert = [];
12767
+ const toDelete = [];
12768
+ const skippedIds = [];
12769
+ let skipped = 0;
12770
+ for (const row of rows) {
12771
+ if (row.deleted_at !== void 0) {
12772
+ toDelete.push(row.id);
12773
+ continue;
12774
+ }
12775
+ let parsed;
12776
+ try {
12777
+ parsed = JSON.parse(row.props);
12778
+ } catch {
12779
+ skipped += 1;
12780
+ skippedIds.push(row.id);
12781
+ continue;
12782
+ }
12783
+ if (!isPropsObject(parsed)) {
12784
+ skipped += 1;
12785
+ skippedIds.push(row.id);
12786
+ continue;
12787
+ }
12788
+ const computed = computeFulltextContent(schema, parsed);
12789
+ if (computed === void 0) {
12790
+ toDelete.push(row.id);
12791
+ } else {
12792
+ toUpsert.push({
12793
+ nodeId: row.id,
12794
+ content: computed.content,
12795
+ language: computed.language
12796
+ });
12797
+ }
12798
+ }
12799
+ return { toUpsert, toDelete, skipped, skippedIds };
12800
+ }
12801
+
12802
+ // src/store/search.ts
12803
+ async function executeFulltextSearch(ctx, nodeKind, options) {
12804
+ const { backend, graphId } = ctx;
12805
+ if (!backend.fulltextSearch) {
12806
+ throw new chunkO5PST6TB_cjs.ConfigurationError("Backend does not support fulltext search", {
12807
+ backend: backend.dialect,
12808
+ capability: "fulltext"
12809
+ });
12810
+ }
12811
+ if (!Number.isInteger(options.limit) || options.limit <= 0) {
12812
+ throw new RangeError(
12813
+ `fulltextSearch.limit must be a positive integer, got: ${options.limit}`
12814
+ );
12815
+ }
12816
+ validateFulltextCallOptions(backend, {
12817
+ mode: options.mode,
12818
+ includeSnippets: options.includeSnippets,
12819
+ language: options.language
12820
+ });
12821
+ const params = {
12822
+ graphId,
12823
+ nodeKind,
12824
+ query: options.query,
12825
+ limit: options.limit,
12826
+ ...options.mode ? { mode: options.mode } : {},
12827
+ ...options.language ? { language: options.language } : {},
12828
+ ...options.minScore === void 0 ? {} : { minScore: options.minScore },
12829
+ ...options.includeSnippets === void 0 ? {} : { includeSnippets: options.includeSnippets }
12830
+ };
12831
+ const rows = await backend.fulltextSearch(params);
12832
+ if (rows.length === 0) return [];
12833
+ const ids = rows.map((r) => r.nodeId);
12834
+ const nodeMap = await fetchNodesByIds(backend, graphId, nodeKind, ids);
12835
+ const hits = [];
12836
+ let rank = 1;
12837
+ for (const row of rows) {
12838
+ const node = nodeMap.get(row.nodeId);
12839
+ if (!node) continue;
12840
+ hits.push({
12841
+ node,
12842
+ score: row.score,
12843
+ rank,
12844
+ ...row.snippet === void 0 ? {} : { snippet: row.snippet }
12845
+ });
12846
+ rank += 1;
12847
+ }
12848
+ return hits;
12849
+ }
12850
+ async function executeHybridSearch(ctx, nodeKind, options) {
12851
+ const { backend, graphId } = ctx;
12852
+ if (!backend.vectorSearch) {
12853
+ throw new chunkO5PST6TB_cjs.ConfigurationError("Backend does not support vector search", {
12854
+ backend: backend.dialect,
12855
+ capability: "vector"
12856
+ });
12857
+ }
12858
+ if (!backend.fulltextSearch) {
12859
+ throw new chunkO5PST6TB_cjs.ConfigurationError("Backend does not support fulltext search", {
12860
+ backend: backend.dialect,
12861
+ capability: "fulltext"
12862
+ });
12863
+ }
12864
+ if (!Number.isInteger(options.limit) || options.limit <= 0) {
12865
+ throw new RangeError(
12866
+ `hybridSearch.limit must be a positive integer, got: ${options.limit}`
12867
+ );
12868
+ }
12869
+ if (options.fusion !== void 0) {
12870
+ validateHybridFusionOptions(options.fusion);
12871
+ }
12872
+ validateFulltextCallOptions(backend, {
12873
+ mode: options.fulltext.mode,
12874
+ includeSnippets: options.fulltext.includeSnippets,
12875
+ language: options.fulltext.language
12876
+ });
12877
+ const fusionK = options.fusion?.k ?? DEFAULT_RRF_K;
12878
+ const vectorWeight = options.fusion?.weights?.vector ?? DEFAULT_RRF_WEIGHT;
12879
+ const fulltextWeight = options.fusion?.weights?.fulltext ?? DEFAULT_RRF_WEIGHT;
12880
+ const overFetchMultiplier = 4;
12881
+ const vectorK = options.vector.k ?? options.limit * overFetchMultiplier;
12882
+ const fulltextK = options.fulltext.k ?? options.limit * overFetchMultiplier;
12883
+ const vectorPromise = backend.vectorSearch({
12884
+ graphId,
12885
+ nodeKind,
12886
+ fieldPath: options.vector.fieldPath,
12887
+ queryEmbedding: options.vector.queryEmbedding,
12888
+ metric: options.vector.metric ?? "cosine",
12889
+ limit: vectorK,
12890
+ ...options.vector.minScore === void 0 ? {} : { minScore: options.vector.minScore }
12891
+ });
12892
+ const fulltextPromise = backend.fulltextSearch({
12893
+ graphId,
12894
+ nodeKind,
12895
+ query: options.fulltext.query,
12896
+ limit: fulltextK,
12897
+ ...options.fulltext.mode ? { mode: options.fulltext.mode } : {},
12898
+ ...options.fulltext.language ? { language: options.fulltext.language } : {},
12899
+ ...options.fulltext.minScore === void 0 ? {} : { minScore: options.fulltext.minScore },
12900
+ ...options.fulltext.includeSnippets === void 0 ? {} : { includeSnippets: options.fulltext.includeSnippets }
12901
+ });
12902
+ const [vectorRows, fulltextRows] = await Promise.all([
12903
+ vectorPromise,
12904
+ fulltextPromise
12905
+ ]);
12906
+ const fused = /* @__PURE__ */ new Map();
12907
+ for (const [index, row] of vectorRows.entries()) {
12908
+ const rank2 = index + 1;
12909
+ const contribution = vectorWeight / (fusionK + rank2);
12910
+ const entry = fused.get(row.nodeId) ?? {
12911
+ nodeId: row.nodeId,
12912
+ fusedScore: 0
12913
+ };
12914
+ entry.fusedScore += contribution;
12915
+ entry.vectorRank = rank2;
12916
+ entry.vectorScore = row.score;
12917
+ fused.set(row.nodeId, entry);
12918
+ }
12919
+ for (const [index, row] of fulltextRows.entries()) {
12920
+ const rank2 = index + 1;
12921
+ const contribution = fulltextWeight / (fusionK + rank2);
12922
+ const entry = fused.get(row.nodeId) ?? {
12923
+ nodeId: row.nodeId,
12924
+ fusedScore: 0
12925
+ };
12926
+ entry.fusedScore += contribution;
12927
+ entry.fulltextRank = rank2;
12928
+ entry.fulltextScore = row.score;
12929
+ if (row.snippet !== void 0) {
12930
+ entry.fulltextSnippet = row.snippet;
12931
+ }
12932
+ fused.set(row.nodeId, entry);
12933
+ }
12934
+ const ranked = [...fused.values()].toSorted(
12935
+ (a, b) => b.fusedScore - a.fusedScore || a.nodeId.localeCompare(b.nodeId)
12936
+ ).slice(0, options.limit);
12937
+ const ids = ranked.map((entry) => entry.nodeId);
12938
+ const nodeMap = await fetchNodesByIds(backend, graphId, nodeKind, ids);
12939
+ const hits = [];
12940
+ let rank = 1;
12941
+ for (const entry of ranked) {
12942
+ const node = nodeMap.get(entry.nodeId);
12943
+ if (!node) continue;
12944
+ const typedNode = node;
12945
+ const hit = {
12946
+ node: typedNode,
12947
+ score: entry.fusedScore,
12948
+ rank,
12949
+ ...entry.vectorRank !== void 0 && entry.vectorScore !== void 0 ? {
12950
+ vector: {
12951
+ node: typedNode,
12952
+ score: entry.vectorScore,
12953
+ rank: entry.vectorRank
12954
+ }
12955
+ } : {},
12956
+ ...entry.fulltextRank !== void 0 && entry.fulltextScore !== void 0 ? {
12957
+ fulltext: {
12958
+ node: typedNode,
12959
+ score: entry.fulltextScore,
12960
+ rank: entry.fulltextRank,
12961
+ ...entry.fulltextSnippet === void 0 ? {} : { snippet: entry.fulltextSnippet }
12962
+ }
12963
+ } : {}
12964
+ };
12965
+ hits.push(hit);
12966
+ rank += 1;
12967
+ }
12968
+ return hits;
12969
+ }
12970
+ function validateFulltextCallOptions(backend, options) {
12971
+ const strategy = backend.fulltextStrategy;
12972
+ const capabilities = backend.capabilities.fulltext;
12973
+ if (options.mode !== void 0) {
12974
+ validateFulltextMode(backend, strategy, capabilities, options.mode);
12975
+ }
12976
+ if (options.includeSnippets === true) {
12977
+ validateFulltextSnippets(backend, strategy, capabilities);
12978
+ }
12979
+ if (options.language !== void 0) {
12980
+ validateFulltextLanguageOption(
12981
+ backend,
12982
+ strategy,
12983
+ capabilities,
12984
+ options.language
12985
+ );
12986
+ }
12987
+ }
12988
+ function validateFulltextMode(backend, strategy, capabilities, mode) {
12989
+ if (strategy !== void 0) {
12990
+ if (strategy.supportedModes.includes(mode)) return;
12991
+ throw new chunkO5PST6TB_cjs.ConfigurationError(
12992
+ `Backend "${backend.dialect}" fulltext strategy "${strategy.name}" does not support mode "${mode}". Supported modes: ${strategy.supportedModes.join(", ")}`,
12993
+ {
12994
+ backend: backend.dialect,
12995
+ strategy: strategy.name,
12996
+ capability: `fulltext.modes.${mode}`
12997
+ }
12998
+ );
12999
+ }
13000
+ if (mode === "phrase" && capabilities && !capabilities.phraseQueries) {
13001
+ throw new chunkO5PST6TB_cjs.ConfigurationError(
13002
+ `Backend "${backend.dialect}" does not support phrase queries`,
13003
+ { backend: backend.dialect, capability: "fulltext.phraseQueries" }
13004
+ );
13005
+ }
13006
+ }
13007
+ function validateFulltextSnippets(backend, strategy, capabilities) {
13008
+ const supportsSnippets = strategy === void 0 ? capabilities?.highlighting !== false : strategy.supportsSnippets;
13009
+ if (supportsSnippets) return;
13010
+ throw new chunkO5PST6TB_cjs.ConfigurationError(
13011
+ `Backend "${backend.dialect}" does not support snippet highlighting; drop includeSnippets or switch to a backend where highlighting is supported`,
13012
+ { backend: backend.dialect, capability: "fulltext.highlighting" }
13013
+ );
13014
+ }
13015
+ function validateFulltextLanguageOption(backend, strategy, capabilities, language) {
13016
+ if (strategy !== void 0 && !strategy.supportsLanguageOverride) {
13017
+ throw new chunkO5PST6TB_cjs.ConfigurationError(
13018
+ `Backend "${backend.dialect}" fulltext strategy "${strategy.name}" does not honor a per-query \`language\` override (its tokenizer is fixed at table-create time). Drop the option, or pick a strategy that advertises \`supportsLanguageOverride: true\`.`,
13019
+ {
13020
+ backend: backend.dialect,
13021
+ strategy: strategy.name,
13022
+ capability: "fulltext.languageOverride"
13023
+ }
13024
+ );
13025
+ }
13026
+ if (capabilities !== void 0) {
13027
+ warnIfLanguageNotAdvertised(backend.dialect, capabilities, language);
13028
+ }
13029
+ }
13030
+ function warnIfLanguageNotAdvertised(dialect, capabilities, language) {
13031
+ if (capabilities.languages.length === 0) return;
13032
+ if (capabilities.languages.includes(language)) return;
13033
+ if (typeof console === "undefined" || typeof console.warn !== "function") {
13034
+ return;
13035
+ }
13036
+ console.warn(
13037
+ `[typegraph] fulltext language "${language}" is not in the advertised list for dialect "${dialect}" (${capabilities.languages.join(", ")}). If your backend has this language installed this warning is safe to ignore.`
13038
+ );
13039
+ }
13040
+ async function fetchNodesByIds(backend, graphId, nodeKind, ids) {
13041
+ if (ids.length === 0) return /* @__PURE__ */ new Map();
13042
+ const map = /* @__PURE__ */ new Map();
13043
+ if (backend.getNodes) {
13044
+ const rows = await backend.getNodes(graphId, nodeKind, ids);
13045
+ for (const row of rows) {
13046
+ if (row.deleted_at !== void 0) continue;
13047
+ map.set(row.id, rowToNode(row));
13048
+ }
13049
+ return map;
13050
+ }
13051
+ for (const id of ids) {
13052
+ const row = await backend.getNode(graphId, nodeKind, id);
13053
+ if (row && row.deleted_at === void 0) map.set(row.id, rowToNode(row));
13054
+ }
13055
+ return map;
13056
+ }
13057
+
13058
+ // src/store/search-facade.ts
13059
+ var StoreSearch = class {
13060
+ #context;
13061
+ constructor(context) {
13062
+ this.#context = context;
13063
+ }
13064
+ /**
13065
+ * Runs a fulltext search against nodes of the given kind.
13066
+ *
13067
+ * Requires fields on the node schema declared with `searchable()`.
13068
+ * The search hits the backend's fulltext index (tsvector + GIN on
13069
+ * Postgres, FTS5 on SQLite — or whatever strategy the backend is
13070
+ * configured with) and resolves the matching node IDs back to typed
13071
+ * `Node` objects.
13072
+ */
13073
+ async fulltext(nodeKind, options) {
13074
+ return executeFulltextSearch(
13075
+ { graphId: this.#context.graphId, backend: this.#context.backend },
13076
+ nodeKind,
13077
+ options
13078
+ );
13079
+ }
13080
+ /**
13081
+ * Runs a vector + fulltext hybrid search and fuses the results with
13082
+ * Reciprocal Rank Fusion.
13083
+ *
13084
+ * RRF is rank-based, so it composes well across heterogeneous score
13085
+ * scales (cosine similarity vs ts_rank_cd vs FTS5 BM25). Default
13086
+ * over-fetch is 4× `limit` from each source — tune via `vector.k` /
13087
+ * `fulltext.k` for higher-recall corpora.
13088
+ */
13089
+ async hybrid(nodeKind, options) {
13090
+ return executeHybridSearch(
13091
+ { graphId: this.#context.graphId, backend: this.#context.backend },
13092
+ nodeKind,
13093
+ options
13094
+ );
13095
+ }
13096
+ /**
13097
+ * Rebuilds the fulltext index from existing node data.
13098
+ *
13099
+ * Use when:
13100
+ * - A node kind gained a `searchable()` field after data was already
13101
+ * written and existing rows were never indexed.
13102
+ * - The fulltext table was dropped / truncated.
13103
+ * - `language` was changed on a `searchable()` field.
13104
+ *
13105
+ * Iterates nodes with keyset pagination (stable under shared
13106
+ * timestamps and light concurrent writes), transacts per page, skips
13107
+ * kinds with no searchable fields, and cleans up stale rows for
13108
+ * soft-deleted nodes. Corrupt or non-object `props` are counted in
13109
+ * `skipped` with their node IDs surfaced via `skippedIds` so operators
13110
+ * can investigate. Concurrent hard-deletes between page fetches can
13111
+ * be missed by a single pass — run during a maintenance window for
13112
+ * full consistency.
13113
+ */
13114
+ async rebuildFulltext(nodeKind, options = {}) {
13115
+ return rebuildFulltextIndex(
13116
+ {
13117
+ graphId: this.#context.graphId,
13118
+ backend: this.#context.backend,
13119
+ registry: this.#context.registry
13120
+ },
13121
+ nodeKind,
13122
+ options
13123
+ );
13124
+ }
13125
+ };
13126
+
11680
13127
  // src/store/store.ts
11681
13128
  var Store = class {
11682
13129
  #graph;
@@ -11687,6 +13134,8 @@ var Store = class {
11687
13134
  #defaultTraversalExpansion;
11688
13135
  #nodeCollections;
11689
13136
  #edgeCollections;
13137
+ #algorithms;
13138
+ #search;
11690
13139
  constructor(graph, backend, options) {
11691
13140
  this.#graph = graph;
11692
13141
  this.#backend = backend;
@@ -11768,6 +13217,38 @@ var Store = class {
11768
13217
  }
11769
13218
  return this.#edgeCollections;
11770
13219
  }
13220
+ // === Graph Algorithms ===
13221
+ /**
13222
+ * Tier 1 graph algorithms: shortest path, reachability, neighborhoods, and
13223
+ * degree centrality.
13224
+ *
13225
+ * Each call compiles to a single recursive-CTE query against the backend.
13226
+ * The facade is built lazily on first access and cached for the lifetime
13227
+ * of the store.
13228
+ *
13229
+ * @example
13230
+ * ```typescript
13231
+ * const path = await store.algorithms.shortestPath(alice, bob, {
13232
+ * edges: ["knows"],
13233
+ * });
13234
+ *
13235
+ * const friends = await store.algorithms.neighbors(alice, {
13236
+ * edges: ["knows"],
13237
+ * depth: 2,
13238
+ * });
13239
+ * ```
13240
+ */
13241
+ get algorithms() {
13242
+ if (this.#algorithms === void 0) {
13243
+ this.#algorithms = createGraphAlgorithms({
13244
+ graphId: this.graphId,
13245
+ backend: this.#backend,
13246
+ schema: this.#schema,
13247
+ defaultTemporalMode: this.#graph.defaults.temporalMode
13248
+ });
13249
+ }
13250
+ return this.#algorithms;
13251
+ }
11771
13252
  // === Dynamic Collection Access ===
11772
13253
  /**
11773
13254
  * Returns the node collection for the given kind, or undefined if the kind
@@ -11903,6 +13384,44 @@ var Store = class {
11903
13384
  query() {
11904
13385
  return this.#createQueryForBackend(this.#backend);
11905
13386
  }
13387
+ // === Search ===
13388
+ /**
13389
+ * Search-related operations (fulltext, hybrid, and rebuild).
13390
+ *
13391
+ * All search methods live under this facade to keep the top-level
13392
+ * Store API focused on CRUD + graph traversal. The facade is
13393
+ * lazy-initialized and cached for the lifetime of the store.
13394
+ *
13395
+ * @example
13396
+ * ```typescript
13397
+ * // Fulltext
13398
+ * const hits = await store.search.fulltext("Document", {
13399
+ * query: "climate warming",
13400
+ * limit: 10,
13401
+ * includeSnippets: true,
13402
+ * });
13403
+ *
13404
+ * // Hybrid (vector + fulltext fused with RRF)
13405
+ * const ranked = await store.search.hybrid("Document", {
13406
+ * limit: 10,
13407
+ * vector: { fieldPath: "embedding", queryEmbedding: vec },
13408
+ * fulltext: { query: "climate warming" },
13409
+ * });
13410
+ *
13411
+ * // Rebuild index after schema change
13412
+ * const stats = await store.search.rebuildFulltext();
13413
+ * ```
13414
+ */
13415
+ get search() {
13416
+ if (this.#search === void 0) {
13417
+ this.#search = new StoreSearch({
13418
+ graphId: this.graphId,
13419
+ backend: this.#backend,
13420
+ registry: this.#registry
13421
+ });
13422
+ }
13423
+ return this.#search;
13424
+ }
11906
13425
  // === Batch Query Execution ===
11907
13426
  /**
11908
13427
  * Executes multiple queries over a single connection with snapshot consistency.
@@ -11976,7 +13495,7 @@ var Store = class {
11976
13495
  graphId: this.graphId,
11977
13496
  rootId,
11978
13497
  backend: this.#backend,
11979
- dialect: chunkMME3H4ZF_cjs.getDialect(this.#backend.dialect),
13498
+ dialect: chunkFQCQM3KG_cjs.getDialect(this.#backend.dialect),
11980
13499
  schema: this.#schema,
11981
13500
  options
11982
13501
  });
@@ -12046,6 +13565,8 @@ var Store = class {
12046
13565
  await (this.#backend.capabilities.transactions ? this.#backend.transaction(async (tx) => doClear(tx)) : doClear(this.#backend));
12047
13566
  this.#nodeCollections = void 0;
12048
13567
  this.#edgeCollections = void 0;
13568
+ this.#algorithms = void 0;
13569
+ this.#search = void 0;
12049
13570
  }
12050
13571
  // === Lifecycle ===
12051
13572
  /**
@@ -12115,7 +13636,7 @@ var Store = class {
12115
13636
  // === Internal: Temporal Filtering ===
12116
13637
  #matchesTemporalMode(row, options) {
12117
13638
  const mode = options?.temporalMode ?? this.#graph.defaults.temporalMode;
12118
- const asOf = options?.asOf ?? chunk5GZMPJOP_cjs.nowIso();
13639
+ const asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
12119
13640
  switch (mode) {
12120
13641
  case "current":
12121
13642
  case "asOf": {
@@ -12148,41 +13669,41 @@ function createStore(graph, backend, options) {
12148
13669
  }
12149
13670
  async function createStoreWithSchema(graph, backend, options) {
12150
13671
  const store = createStore(graph, backend, options);
12151
- const result = await chunkX33WV2UF_cjs.ensureSchema(backend, graph, options);
13672
+ const result = await chunkHRNQ3DVJ_cjs.ensureSchema(backend, graph, options);
12152
13673
  return [store, result];
12153
13674
  }
12154
13675
 
12155
13676
  Object.defineProperty(exports, "isMetaEdge", {
12156
13677
  enumerable: true,
12157
- get: function () { return chunkX33WV2UF_cjs.isMetaEdge; }
13678
+ get: function () { return chunkHRNQ3DVJ_cjs.isMetaEdge; }
12158
13679
  });
12159
13680
  Object.defineProperty(exports, "defineGraph", {
12160
13681
  enumerable: true,
12161
- get: function () { return chunk5GZMPJOP_cjs.defineGraph; }
13682
+ get: function () { return chunkWRWVWKQL_cjs.defineGraph; }
12162
13683
  });
12163
13684
  Object.defineProperty(exports, "getEdgeKinds", {
12164
13685
  enumerable: true,
12165
- get: function () { return chunk5GZMPJOP_cjs.getEdgeKinds; }
13686
+ get: function () { return chunkWRWVWKQL_cjs.getEdgeKinds; }
12166
13687
  });
12167
13688
  Object.defineProperty(exports, "getNodeKinds", {
12168
13689
  enumerable: true,
12169
- get: function () { return chunk5GZMPJOP_cjs.getNodeKinds; }
13690
+ get: function () { return chunkWRWVWKQL_cjs.getNodeKinds; }
12170
13691
  });
12171
13692
  Object.defineProperty(exports, "isEdgeType", {
12172
13693
  enumerable: true,
12173
- get: function () { return chunk5GZMPJOP_cjs.isEdgeType; }
13694
+ get: function () { return chunkWRWVWKQL_cjs.isEdgeType; }
12174
13695
  });
12175
13696
  Object.defineProperty(exports, "isEdgeTypeWithEndpoints", {
12176
13697
  enumerable: true,
12177
- get: function () { return chunk5GZMPJOP_cjs.isEdgeTypeWithEndpoints; }
13698
+ get: function () { return chunkWRWVWKQL_cjs.isEdgeTypeWithEndpoints; }
12178
13699
  });
12179
13700
  Object.defineProperty(exports, "isGraphDef", {
12180
13701
  enumerable: true,
12181
- get: function () { return chunk5GZMPJOP_cjs.isGraphDef; }
13702
+ get: function () { return chunkWRWVWKQL_cjs.isGraphDef; }
12182
13703
  });
12183
13704
  Object.defineProperty(exports, "isNodeType", {
12184
13705
  enumerable: true,
12185
- get: function () { return chunk5GZMPJOP_cjs.isNodeType; }
13706
+ get: function () { return chunkWRWVWKQL_cjs.isNodeType; }
12186
13707
  });
12187
13708
  Object.defineProperty(exports, "BackendDisposedError", {
12188
13709
  enumerable: true,
@@ -12284,37 +13805,61 @@ Object.defineProperty(exports, "isUserRecoverable", {
12284
13805
  enumerable: true,
12285
13806
  get: function () { return chunkO5PST6TB_cjs.isUserRecoverable; }
12286
13807
  });
13808
+ Object.defineProperty(exports, "fts5Strategy", {
13809
+ enumerable: true,
13810
+ get: function () { return chunkFQCQM3KG_cjs.fts5Strategy; }
13811
+ });
13812
+ Object.defineProperty(exports, "tsvectorStrategy", {
13813
+ enumerable: true,
13814
+ get: function () { return chunkFQCQM3KG_cjs.tsvectorStrategy; }
13815
+ });
13816
+ Object.defineProperty(exports, "DEFAULT_SEARCHABLE_LANGUAGE", {
13817
+ enumerable: true,
13818
+ get: function () { return chunkUVZHTBZ5_cjs.DEFAULT_SEARCHABLE_LANGUAGE; }
13819
+ });
12287
13820
  Object.defineProperty(exports, "MAX_JSON_POINTER_DEPTH", {
12288
13821
  enumerable: true,
12289
- get: function () { return chunkP5CNM325_cjs.MAX_JSON_POINTER_DEPTH; }
13822
+ get: function () { return chunkUVZHTBZ5_cjs.MAX_JSON_POINTER_DEPTH; }
12290
13823
  });
12291
13824
  Object.defineProperty(exports, "embedding", {
12292
13825
  enumerable: true,
12293
- get: function () { return chunkP5CNM325_cjs.embedding; }
13826
+ get: function () { return chunkUVZHTBZ5_cjs.embedding; }
12294
13827
  });
12295
13828
  Object.defineProperty(exports, "getEmbeddingDimensions", {
12296
13829
  enumerable: true,
12297
- get: function () { return chunkP5CNM325_cjs.getEmbeddingDimensions; }
13830
+ get: function () { return chunkUVZHTBZ5_cjs.getEmbeddingDimensions; }
13831
+ });
13832
+ Object.defineProperty(exports, "getSearchableMetadata", {
13833
+ enumerable: true,
13834
+ get: function () { return chunkUVZHTBZ5_cjs.getSearchableMetadata; }
12298
13835
  });
12299
13836
  Object.defineProperty(exports, "isEmbeddingSchema", {
12300
13837
  enumerable: true,
12301
- get: function () { return chunkP5CNM325_cjs.isEmbeddingSchema; }
13838
+ get: function () { return chunkUVZHTBZ5_cjs.isEmbeddingSchema; }
13839
+ });
13840
+ Object.defineProperty(exports, "isSearchableSchema", {
13841
+ enumerable: true,
13842
+ get: function () { return chunkUVZHTBZ5_cjs.isSearchableSchema; }
12302
13843
  });
12303
13844
  Object.defineProperty(exports, "joinJsonPointers", {
12304
13845
  enumerable: true,
12305
- get: function () { return chunkP5CNM325_cjs.joinJsonPointers; }
13846
+ get: function () { return chunkUVZHTBZ5_cjs.joinJsonPointers; }
12306
13847
  });
12307
13848
  Object.defineProperty(exports, "jsonPointer", {
12308
13849
  enumerable: true,
12309
- get: function () { return chunkP5CNM325_cjs.jsonPointer; }
13850
+ get: function () { return chunkUVZHTBZ5_cjs.jsonPointer; }
12310
13851
  });
12311
13852
  Object.defineProperty(exports, "normalizeJsonPointer", {
12312
13853
  enumerable: true,
12313
- get: function () { return chunkP5CNM325_cjs.normalizeJsonPointer; }
13854
+ get: function () { return chunkUVZHTBZ5_cjs.normalizeJsonPointer; }
12314
13855
  });
12315
13856
  Object.defineProperty(exports, "parseJsonPointer", {
12316
13857
  enumerable: true,
12317
- get: function () { return chunkP5CNM325_cjs.parseJsonPointer; }
13858
+ get: function () { return chunkUVZHTBZ5_cjs.parseJsonPointer; }
13859
+ });
13860
+ Object.defineProperty(exports, "searchable", {
13861
+ enumerable: true,
13862
+ get: function () { return chunkUVZHTBZ5_cjs.searchable; }
12318
13863
  });
12319
13864
  exports.DEFAULT_SQL_SCHEMA = DEFAULT_SQL_SCHEMA;
12320
13865
  exports.ExecutableAggregateQuery = ExecutableAggregateQuery;
@@ -12323,6 +13868,7 @@ exports.MAX_EXPLICIT_RECURSIVE_DEPTH = MAX_EXPLICIT_RECURSIVE_DEPTH;
12323
13868
  exports.MAX_RECURSIVE_DEPTH = MAX_RECURSIVE_DEPTH;
12324
13869
  exports.PreparedQuery = PreparedQuery;
12325
13870
  exports.QueryBuilder = QueryBuilder;
13871
+ exports.StoreSearch = StoreSearch;
12326
13872
  exports.UnionableQuery = UnionableQuery;
12327
13873
  exports.avg = avg;
12328
13874
  exports.broader = broader;