@nicia-ai/typegraph 0.20.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 +1705 -490
  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 +1552 -361
  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-BMrRi_mY.d.ts → store-2QW2SIRk.d.cts} +404 -5
  80. package/dist/{store-DRRvALu8.d.cts → store-C0q13mtJ.d.ts} +404 -5
  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 +9 -9
  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
  }
@@ -3335,63 +3750,26 @@ function compileRecursiveOrderBy(ast, dialect) {
3335
3750
  );
3336
3751
  }
3337
3752
  const field2 = compileFieldValue(orderSpec.field, dialect, valueType);
3338
- const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
3339
- const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
3340
- const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
3341
- parts.push(
3342
- drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
3343
- drizzleOrm.sql`${field2} ${direction}`
3344
- );
3345
- }
3346
- return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(parts, drizzleOrm.sql`, `)}`;
3347
- }
3348
- function compileLimitOffset(ast) {
3349
- const parts = [];
3350
- if (ast.limit !== void 0) {
3351
- parts.push(drizzleOrm.sql`LIMIT ${ast.limit}`);
3352
- }
3353
- if (ast.offset !== void 0) {
3354
- parts.push(drizzleOrm.sql`OFFSET ${ast.offset}`);
3355
- }
3356
- return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
3357
- }
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.`
3753
+ const direction = drizzleOrm.sql.raw(orderSpec.direction.toUpperCase());
3754
+ const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
3755
+ const nullsDirection = drizzleOrm.sql.raw(nulls === "first" ? "DESC" : "ASC");
3756
+ parts.push(
3757
+ drizzleOrm.sql`(${field2} IS NULL) ${nullsDirection}`,
3758
+ drizzleOrm.sql`${field2} ${direction}`
3376
3759
  );
3377
3760
  }
3761
+ return drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.join(parts, drizzleOrm.sql`, `)}`;
3378
3762
  }
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
- };
3763
+ function compileLimitOffset(ast) {
3764
+ const parts = [];
3765
+ if (ast.limit !== void 0) {
3766
+ parts.push(drizzleOrm.sql`LIMIT ${ast.limit}`);
3767
+ }
3768
+ if (ast.offset !== void 0) {
3769
+ parts.push(drizzleOrm.sql`OFFSET ${ast.offset}`);
3770
+ }
3771
+ return parts.length > 0 ? drizzleOrm.sql.join(parts, drizzleOrm.sql` `) : void 0;
3393
3772
  }
3394
- var DEFAULT_SQL_SCHEMA = createSqlSchema();
3395
3773
 
3396
3774
  // src/query/execution/value-decoder.ts
3397
3775
  function nullToUndefined(value) {
@@ -3622,9 +4000,9 @@ function truncateToBytes(value, maxBytes) {
3622
4000
  }
3623
4001
  function projectionAlias(entityPrefix, kind, field2) {
3624
4002
  const prefix = entityPrefix === "node" ? "sg_n" : "sg_e";
3625
- const hash = chunkMME3H4ZF_cjs.fnv1aBase36(`${kind}\0${field2}`);
4003
+ const hash = chunkFQCQM3KG_cjs.fnv1aBase36(`${kind}\0${field2}`);
3626
4004
  const fixedBytes = prefix.length + 1 + 1 + hash.length;
3627
- const maxKindBytes = chunkMME3H4ZF_cjs.MAX_PG_IDENTIFIER_LENGTH - fixedBytes;
4005
+ const maxKindBytes = chunkFQCQM3KG_cjs.MAX_PG_IDENTIFIER_LENGTH - fixedBytes;
3628
4006
  const truncatedKind = truncateToBytes(kind, maxKindBytes);
3629
4007
  return `${prefix}_${truncatedKind}_${hash}`;
3630
4008
  }
@@ -3696,12 +4074,7 @@ async function executeSubgraph(params) {
3696
4074
  for (const row of edgeRows) {
3697
4075
  const edge = mapSubgraphEdgeRow(row, edgeProjectionPlan);
3698
4076
  insertAdjacencyEntry(adjacency, edge.fromId, edge.kind, edge);
3699
- insertAdjacencyEntry(
3700
- reverseAdjacency,
3701
- edge.toId,
3702
- edge.kind,
3703
- edge
3704
- );
4077
+ insertAdjacencyEntry(reverseAdjacency, edge.toId, edge.kind, edge);
3705
4078
  }
3706
4079
  const root = nodesMap.get(ctx.rootId);
3707
4080
  return {
@@ -3727,7 +4100,7 @@ function getSubgraphSchemaIntrospector(graph) {
3727
4100
  { schema: definition.type.schema }
3728
4101
  ])
3729
4102
  );
3730
- const introspector = chunkP5CNM325_cjs.createSchemaIntrospector(nodeKinds, edgeKinds);
4103
+ const introspector = chunkUVZHTBZ5_cjs.createSchemaIntrospector(nodeKinds, edgeKinds);
3731
4104
  introspectorCache.set(graph, introspector);
3732
4105
  return introspector;
3733
4106
  }
@@ -3871,11 +4244,11 @@ function buildProjectedPropertyColumns(alias, plan, dialect) {
3871
4244
  const extracted = compileTypedJsonExtract({
3872
4245
  column: drizzleOrm.sql.raw(`${alias}.props`),
3873
4246
  dialect,
3874
- pointer: chunkP5CNM325_cjs.jsonPointer([fieldPlan.field]),
4247
+ pointer: chunkUVZHTBZ5_cjs.jsonPointer([fieldPlan.field]),
3875
4248
  valueType: fieldPlan.typeInfo?.valueType
3876
4249
  });
3877
4250
  columns.push(
3878
- 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)}`
3879
4252
  );
3880
4253
  }
3881
4254
  }
@@ -3942,16 +4315,6 @@ function mapSubgraphEdgeRow(row, projectionPlan) {
3942
4315
  applyProjectedFields(projectedEdge, row, kindPlan);
3943
4316
  return projectedEdge;
3944
4317
  }
3945
-
3946
- // src/query/ast.ts
3947
- function mergeEdgeKinds(traversal) {
3948
- const inverse = traversal.inverseEdgeKinds;
3949
- if (inverse === void 0 || inverse.length === 0) return traversal.edgeKinds;
3950
- return [
3951
- ...traversal.edgeKinds,
3952
- ...inverse.filter((kind) => !traversal.edgeKinds.includes(kind))
3953
- ];
3954
- }
3955
4318
  var OPERATOR_MAP = {
3956
4319
  union: "UNION",
3957
4320
  unionAll: "UNION ALL",
@@ -4108,6 +4471,9 @@ function validateSqliteSetOpLeaf(ast) {
4108
4471
  if (hasVectorSimilarityPredicates(ast)) {
4109
4472
  unsupported.push("vector similarity predicates");
4110
4473
  }
4474
+ if (hasFulltextMatchPredicates(ast)) {
4475
+ unsupported.push("fulltext match predicates");
4476
+ }
4111
4477
  if (ast.groupBy !== void 0) {
4112
4478
  unsupported.push("GROUP BY");
4113
4479
  }
@@ -4157,7 +4523,42 @@ function hasVectorSimilarityInExpression(expr) {
4157
4523
  case "between":
4158
4524
  case "array_op":
4159
4525
  case "object_op":
4160
- 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": {
4161
4562
  return false;
4162
4563
  }
4163
4564
  }
@@ -4193,7 +4594,8 @@ function hasSubqueryInExpression(expr) {
4193
4594
  case "array_op":
4194
4595
  case "object_op":
4195
4596
  case "aggregate_comparison":
4196
- case "vector_similarity": {
4597
+ case "vector_similarity":
4598
+ case "fulltext_match": {
4197
4599
  return false;
4198
4600
  }
4199
4601
  }
@@ -4359,7 +4761,7 @@ function getLeftmostProjection(op) {
4359
4761
  function normalizeFieldRefKey(field2) {
4360
4762
  let pointer = field2.jsonPointer;
4361
4763
  if (pointer === void 0 && field2.path.length > 1 && field2.path[0] === "props") {
4362
- pointer = chunkP5CNM325_cjs.jsonPointer(field2.path.slice(1));
4764
+ pointer = chunkUVZHTBZ5_cjs.jsonPointer(field2.path.slice(1));
4363
4765
  }
4364
4766
  const basePath = field2.path.length > 0 && field2.path[0] === "props" ? "props" : field2.path.join(".");
4365
4767
  return `${field2.alias}:${basePath}:${pointer ?? ""}`;
@@ -4471,6 +4873,10 @@ function markPredicateFieldsAsRequired(requiredColumnsByAlias, expression) {
4471
4873
  markFieldRefAsRequired(requiredColumnsByAlias, expression.field);
4472
4874
  return;
4473
4875
  }
4876
+ case "fulltext_match": {
4877
+ markFieldRefAsRequired(requiredColumnsByAlias, expression.field);
4878
+ return;
4879
+ }
4474
4880
  case "exists": {
4475
4881
  return;
4476
4882
  }
@@ -4535,7 +4941,8 @@ function hasIdEqualityPredicate(expression, alias) {
4535
4941
  case "aggregate_comparison":
4536
4942
  case "exists":
4537
4943
  case "in_subquery":
4538
- case "vector_similarity": {
4944
+ case "vector_similarity":
4945
+ case "fulltext_match": {
4539
4946
  return false;
4540
4947
  }
4541
4948
  }
@@ -4576,10 +4983,13 @@ function resolveTraversalCteLimit(ast, predicateIndex) {
4576
4983
  );
4577
4984
  return Math.max(ast.limit, pushdownLimit);
4578
4985
  }
4579
- function canCollapseSelectiveTraversalRowset(ast, vectorPredicate) {
4986
+ function canCollapseSelectiveTraversalRowset(ast, vectorPredicate, fulltextPredicate) {
4580
4987
  if (vectorPredicate !== void 0) {
4581
4988
  return false;
4582
4989
  }
4990
+ if (fulltextPredicate !== void 0) {
4991
+ return false;
4992
+ }
4583
4993
  if (ast.traversals.length === 0) {
4584
4994
  return false;
4585
4995
  }
@@ -4619,6 +5029,8 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
4619
5029
  collapsedTraversalCteAlias: void 0,
4620
5030
  ctx,
4621
5031
  effectiveLimit: void 0,
5032
+ fulltextPredicate: void 0,
5033
+ fusion: void 0,
4622
5034
  logicalPlan: void 0,
4623
5035
  predicateIndex: buildPredicateIndex(ast),
4624
5036
  requiredColumnsByAlias: void 0,
@@ -4640,6 +5052,39 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
4640
5052
  }
4641
5053
  });
4642
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;
4643
5088
  const temporalPass = runCompilerPass(state, {
4644
5089
  name: "temporal_filters",
4645
5090
  execute(currentState) {
@@ -4674,7 +5119,8 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
4674
5119
  execute(currentState) {
4675
5120
  const shouldCollapseSelectiveTraversalRowset = canCollapseSelectiveTraversalRowset(
4676
5121
  currentState.ast,
4677
- currentState.vectorPredicate
5122
+ currentState.vectorPredicate,
5123
+ currentState.fulltextPredicate
4678
5124
  );
4679
5125
  const lastTraversal = currentState.ast.traversals.at(-1);
4680
5126
  const collapsedTraversalCteAlias = shouldCollapseSelectiveTraversalRowset && lastTraversal !== void 0 ? `cte_${lastTraversal.nodeAlias}` : void 0;
@@ -4710,9 +5156,9 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
4710
5156
  state = traversalLimitPass.state;
4711
5157
  state = {
4712
5158
  ...state,
4713
- effectiveLimit: resolveVectorAwareLimit(
4714
- state.ast.limit,
4715
- state.vectorPredicate
5159
+ effectiveLimit: resolveFulltextAwareLimit(
5160
+ resolveVectorAwareLimit(state.ast.limit, state.vectorPredicate),
5161
+ state.fulltextPredicate
4716
5162
  )
4717
5163
  };
4718
5164
  const logicalPlanPass = runCompilerPass(state, {
@@ -4729,7 +5175,8 @@ function runStandardQueryPassPipeline(ast, graphId, ctx) {
4729
5175
  collapsedTraversalCteAlias: currentState.collapsedTraversalCteAlias
4730
5176
  },
4731
5177
  ...currentState.effectiveLimit === void 0 ? {} : { effectiveLimit: currentState.effectiveLimit },
4732
- ...currentState.vectorPredicate === void 0 ? {} : { vectorPredicate: currentState.vectorPredicate }
5178
+ ...currentState.vectorPredicate === void 0 ? {} : { vectorPredicate: currentState.vectorPredicate },
5179
+ ...currentState.fulltextPredicate === void 0 ? {} : { fulltextPredicate: currentState.fulltextPredicate }
4733
5180
  });
4734
5181
  },
4735
5182
  update(currentState, logicalPlan) {
@@ -4748,11 +5195,11 @@ function compileQuery(ast, graphId, options = "sqlite") {
4748
5195
  const options_ = typeof options === "string" ? { dialect: options } : options;
4749
5196
  const dialect = options_.dialect ?? "sqlite";
4750
5197
  const schema = options_.schema ?? DEFAULT_SQL_SCHEMA;
4751
- const adapter = chunkMME3H4ZF_cjs.getDialect(dialect);
5198
+ const adapter = resolveDialectAdapter(dialect, options_.fulltextStrategy);
4752
5199
  const ctx = {
4753
5200
  dialect: adapter,
4754
5201
  schema,
4755
- compileQuery: (subAst, subGraphId) => compileQuery(subAst, subGraphId, { dialect, schema })
5202
+ compileQuery: (subAst, subGraphId) => compileQuery(subAst, subGraphId, propagateOptions(options_))
4756
5203
  };
4757
5204
  if (hasVariableLengthTraversal(ast)) {
4758
5205
  const lowered = tryLowerSingleHopRecursiveTraversal(ast);
@@ -4788,15 +5235,29 @@ function compileSetOperation2(op, graphId, options = "sqlite") {
4788
5235
  const options_ = typeof options === "string" ? { dialect: options } : options;
4789
5236
  const dialect = options_.dialect ?? "sqlite";
4790
5237
  const schema = options_.schema ?? DEFAULT_SQL_SCHEMA;
4791
- const adapter = chunkMME3H4ZF_cjs.getDialect(dialect);
5238
+ const adapter = resolveDialectAdapter(dialect, options_.fulltextStrategy);
4792
5239
  return compileSetOperation(
4793
5240
  op,
4794
5241
  graphId,
4795
5242
  adapter,
4796
5243
  schema,
4797
- (ast, gid) => compileQuery(ast, gid, { dialect, schema })
5244
+ (ast, gid) => compileQuery(ast, gid, propagateOptions(options_))
4798
5245
  );
4799
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
+ }
4800
5261
  function resolveCountAggregateFastPath(ast) {
4801
5262
  if (ast.traversals.length !== 1) {
4802
5263
  return void 0;
@@ -4945,12 +5406,12 @@ function compileCountAggregateFastPath(ast, graphId, ctx, logicalPlan, requiredC
4945
5406
  source.valueType,
4946
5407
  `cte_${source.alias}`
4947
5408
  );
4948
- return drizzleOrm.sql`${value} AS ${quoteIdentifier(projectedField.outputName)}`;
5409
+ return drizzleOrm.sql`${value} AS ${quoteIdentifier2(projectedField.outputName)}`;
4949
5410
  }
4950
5411
  const projectedCountColumn = source.function === "countDistinct" ? countDistinctColumn : countColumn;
4951
5412
  const countValue = drizzleOrm.sql`${drizzleOrm.sql.raw(countCteAlias)}.${drizzleOrm.sql.raw(projectedCountColumn)}`;
4952
5413
  const aggregateValue = traversal.optional ? drizzleOrm.sql`COALESCE(${countValue}, 0)` : countValue;
4953
- return drizzleOrm.sql`${aggregateValue} AS ${quoteIdentifier(projectedField.outputName)}`;
5414
+ return drizzleOrm.sql`${aggregateValue} AS ${quoteIdentifier2(projectedField.outputName)}`;
4954
5415
  }),
4955
5416
  drizzleOrm.sql`, `
4956
5417
  );
@@ -4983,11 +5444,37 @@ function compileStandardQuery(ast, graphId, ctx) {
4983
5444
  const handler = STANDARD_QUERY_STRATEGY_HANDLERS[strategy];
4984
5445
  return handler(ast, graphId, ctx);
4985
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
+ }
4986
5471
  function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
4987
5472
  const { dialect } = ctx;
4988
5473
  const {
4989
5474
  collapsedTraversalCteAlias,
4990
5475
  effectiveLimit,
5476
+ fulltextPredicate,
5477
+ fusion,
4991
5478
  logicalPlan,
4992
5479
  predicateIndex,
4993
5480
  requiredColumnsByAlias,
@@ -5008,7 +5495,7 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
5008
5495
  { phase: "standard-pass-pipeline" }
5009
5496
  );
5010
5497
  }
5011
- if (!vectorPredicate) {
5498
+ if (!vectorPredicate && !fulltextPredicate) {
5012
5499
  const fastPathSql = compileCountAggregateFastPath(
5013
5500
  ast,
5014
5501
  graphId,
@@ -5054,7 +5541,29 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
5054
5541
  );
5055
5542
  }
5056
5543
  if (vectorPredicate) {
5057
- 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());
5058
5567
  }
5059
5568
  const projection = buildStandardProjection({
5060
5569
  ast,
@@ -5064,14 +5573,18 @@ function compileStandardQueryWithCteStrategy(ast, graphId, ctx) {
5064
5573
  const fromClause = buildStandardFromClause({
5065
5574
  ast,
5066
5575
  ...collapsedTraversalCteAlias === void 0 ? {} : { collapsedTraversalCteAlias },
5067
- ...vectorPredicate === void 0 ? {} : { vectorPredicate }
5576
+ ...vectorPredicate === void 0 ? {} : { vectorPredicate },
5577
+ ...fulltextPredicate === void 0 ? {} : { fulltextPredicate }
5068
5578
  });
5069
5579
  const groupBy = buildStandardGroupBy({ ast, dialect });
5070
5580
  const having2 = buildStandardHaving({ ast, ctx });
5071
- const orderBy = vectorPredicate ? buildStandardVectorOrderBy({ ast, dialect }) : buildStandardOrderBy({
5581
+ const orderBy = selectStandardOrderBy({
5072
5582
  ast,
5073
- ...collapsedTraversalCteAlias === void 0 ? {} : { collapsedTraversalCteAlias },
5074
- dialect
5583
+ collapsedTraversalCteAlias,
5584
+ dialect,
5585
+ fulltextPredicate,
5586
+ fusion,
5587
+ vectorPredicate
5075
5588
  });
5076
5589
  const limitOffset = buildLimitOffsetClause({
5077
5590
  limit: effectiveLimit,
@@ -5193,7 +5706,7 @@ function extractCursorValue(row, spec) {
5193
5706
  current = record[segment];
5194
5707
  }
5195
5708
  if (jsonPointer2) {
5196
- const segments = chunkP5CNM325_cjs.parseJsonPointer(jsonPointer2);
5709
+ const segments = chunkUVZHTBZ5_cjs.parseJsonPointer(jsonPointer2);
5197
5710
  for (const segment of segments) {
5198
5711
  if (current === null || current === void 0) return void 0;
5199
5712
  if (typeof current !== "object") return void 0;
@@ -5263,14 +5776,14 @@ function generateId() {
5263
5776
 
5264
5777
  // src/utils/path.ts
5265
5778
  function parseSqlitePath(path) {
5266
- 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}`;
5267
5780
  if (!path || path === emptyPath) return [];
5268
5781
  const trimmed = path.slice(1, -1);
5269
5782
  if (trimmed === "") return [];
5270
- return trimmed.split(chunkMME3H4ZF_cjs.SQLITE_PATH_DELIMITER);
5783
+ return trimmed.split(chunkFQCQM3KG_cjs.SQLITE_PATH_DELIMITER);
5271
5784
  }
5272
5785
  function isSqlitePath(value) {
5273
- 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);
5274
5787
  }
5275
5788
  function normalizePath(value) {
5276
5789
  if (Array.isArray(value)) {
@@ -5285,12 +5798,12 @@ function normalizePath(value) {
5285
5798
  return [];
5286
5799
  }
5287
5800
  function isPostgresTextArray(value) {
5288
- 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);
5289
5802
  }
5290
5803
  function parsePostgresTextArray(value) {
5291
5804
  const inner = value.slice(1, -1);
5292
5805
  if (inner === "") return [];
5293
- return inner.split(chunkMME3H4ZF_cjs.PG_PATH_ELEMENT_SEPARATOR);
5806
+ return inner.split(chunkFQCQM3KG_cjs.PG_PATH_ELEMENT_SEPARATOR);
5294
5807
  }
5295
5808
 
5296
5809
  // src/query/execution/result-mapper.ts
@@ -5492,11 +6005,7 @@ function buildComparisonPredicate(field2, op, value) {
5492
6005
  };
5493
6006
  }
5494
6007
  function buildCursorFromContext(context, orderBy, direction) {
5495
- return buildCursorFromRow(
5496
- context,
5497
- orderBy,
5498
- direction
5499
- );
6008
+ return buildCursorFromRow(context, orderBy, direction);
5500
6009
  }
5501
6010
  function buildPaginatedResult(data, orderedRows, orderBy, limit, hasMore, isBackward, cursor, buildContext) {
5502
6011
  let nextCursor;
@@ -5531,7 +6040,7 @@ async function* createStreamIterable(batchSize, paginate) {
5531
6040
  }
5532
6041
  }
5533
6042
  function getStreamBatchSize(options) {
5534
- return options?.batchSize ?? chunkMME3H4ZF_cjs.DEFAULT_STREAM_BATCH_SIZE;
6043
+ return options?.batchSize ?? chunkFQCQM3KG_cjs.DEFAULT_STREAM_BATCH_SIZE;
5535
6044
  }
5536
6045
 
5537
6046
  // src/query/execution/field-tracker.ts
@@ -5613,7 +6122,7 @@ function createNodeTrackingProxy(alias, kindNames, tracker, options) {
5613
6122
  return getPlaceholderForSystemField(property, options.mode);
5614
6123
  }
5615
6124
  if (property === "meta") {
5616
- for (const key of chunkMME3H4ZF_cjs.NODE_META_KEYS) {
6125
+ for (const key of chunkFQCQM3KG_cjs.NODE_META_KEYS) {
5617
6126
  tracker.record(alias, `meta.${key}`, true);
5618
6127
  }
5619
6128
  return buildNodeMetaPlaceholder(options.mode);
@@ -5646,7 +6155,7 @@ function createEdgeTrackingProxy(alias, edgeKindNames, tracker, options) {
5646
6155
  return getPlaceholderForSystemField(property, options.mode);
5647
6156
  }
5648
6157
  if (property === "meta") {
5649
- for (const key of chunkMME3H4ZF_cjs.EDGE_META_KEYS) {
6158
+ for (const key of chunkFQCQM3KG_cjs.EDGE_META_KEYS) {
5650
6159
  tracker.record(alias, `meta.${key}`, true);
5651
6160
  }
5652
6161
  return buildEdgeMetaPlaceholder(options.mode);
@@ -6089,59 +6598,41 @@ function validateSqlIdentifier(alias) {
6089
6598
  );
6090
6599
  }
6091
6600
  }
6092
- function throwInvalidVectorPredicatePlacement(path) {
6093
- throw new chunkO5PST6TB_cjs.ValidationError(
6094
- "Vector similarity predicates cannot be nested under OR or NOT. Use top-level AND combinations instead.",
6095
- {
6096
- issues: [
6097
- {
6098
- path,
6099
- message: "Vector similarity predicates are only supported at top-level or inside AND groups."
6100
- }
6101
- ]
6102
- },
6103
- {
6104
- suggestion: "Rewrite the predicate to keep vector similarity at top-level or combine with additional filters using AND."
6105
- }
6106
- );
6107
- }
6108
- 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
+ }
6109
6606
  switch (expression.__type) {
6110
- case "vector_similarity": {
6111
- if (inDisallowedBranch) {
6112
- throwInvalidVectorPredicatePlacement(path);
6113
- }
6114
- return;
6115
- }
6116
6607
  case "and": {
6117
- for (const [
6118
- predicateIndex,
6119
- predicate2
6120
- ] of expression.predicates.entries()) {
6121
- validateVectorPredicateExpression(
6122
- predicate2,
6608
+ for (const [index, child] of expression.predicates.entries()) {
6609
+ validateStructuralPredicatePlacement(
6610
+ child,
6611
+ matchType,
6612
+ buildError,
6123
6613
  inDisallowedBranch,
6124
- `${path}.predicates[${predicateIndex}]`
6614
+ `${path}.predicates[${index}]`
6125
6615
  );
6126
6616
  }
6127
6617
  return;
6128
6618
  }
6129
6619
  case "or": {
6130
- for (const [
6131
- predicateIndex,
6132
- predicate2
6133
- ] of expression.predicates.entries()) {
6134
- validateVectorPredicateExpression(
6135
- predicate2,
6620
+ for (const [index, child] of expression.predicates.entries()) {
6621
+ validateStructuralPredicatePlacement(
6622
+ child,
6623
+ matchType,
6624
+ buildError,
6136
6625
  true,
6137
- `${path}.predicates[${predicateIndex}]`
6626
+ `${path}.predicates[${index}]`
6138
6627
  );
6139
6628
  }
6140
6629
  return;
6141
6630
  }
6142
6631
  case "not": {
6143
- validateVectorPredicateExpression(
6632
+ validateStructuralPredicatePlacement(
6144
6633
  expression.predicate,
6634
+ matchType,
6635
+ buildError,
6145
6636
  true,
6146
6637
  `${path}.predicate`
6147
6638
  );
@@ -6155,24 +6646,108 @@ function validateVectorPredicateExpression(expression, inDisallowedBranch, path)
6155
6646
  case "object_op":
6156
6647
  case "aggregate_comparison":
6157
6648
  case "exists":
6158
- case "in_subquery": {
6649
+ case "in_subquery":
6650
+ case "vector_similarity":
6651
+ case "fulltext_match": {
6159
6652
  return;
6160
6653
  }
6161
6654
  }
6162
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
+ }
6163
6688
  function validateVectorPredicatePlacement(predicates) {
6164
- for (const [predicateIndex, predicate2] of predicates.entries()) {
6165
- 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(
6166
6702
  predicate2.expression,
6703
+ "fulltext_match",
6704
+ throwInvalidFulltextPredicatePlacement,
6167
6705
  false,
6168
- `predicates[${predicateIndex}].expression`
6706
+ `predicates[${index}].expression`
6169
6707
  );
6170
6708
  }
6171
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
+ }
6724
+ );
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
+ }
6745
+ }
6172
6746
 
6173
6747
  // src/query/builder/ast-builder.ts
6174
6748
  function buildQueryAst(config, state) {
6175
6749
  validateVectorPredicatePlacement(state.predicates);
6750
+ validateFulltextPredicatePlacement(state.predicates);
6176
6751
  const temporalMode = {
6177
6752
  mode: state.temporalMode
6178
6753
  };
@@ -6196,7 +6771,18 @@ function buildQueryAst(config, state) {
6196
6771
  ...state.limit !== void 0 && { limit: state.limit },
6197
6772
  ...state.offset !== void 0 && { offset: state.offset },
6198
6773
  ...state.groupBy !== void 0 && { groupBy: state.groupBy },
6199
- ...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 }
6200
6786
  };
6201
6787
  }
6202
6788
  function toLiteral(value) {
@@ -6298,7 +6884,8 @@ function substitutePredicateExpression(expr, bindings) {
6298
6884
  case "array_op":
6299
6885
  case "object_op":
6300
6886
  case "aggregate_comparison":
6301
- case "vector_similarity": {
6887
+ case "vector_similarity":
6888
+ case "fulltext_match": {
6302
6889
  return expr;
6303
6890
  }
6304
6891
  case "exists": {
@@ -6337,7 +6924,7 @@ function substituteParameters(ast, bindings) {
6337
6924
  };
6338
6925
  }
6339
6926
  function fillPlaceholders(params, bindings, dialect) {
6340
- const adapter = chunkMME3H4ZF_cjs.getDialect(dialect);
6927
+ const adapter = chunkFQCQM3KG_cjs.getDialect(dialect);
6341
6928
  return params.map((parameter) => {
6342
6929
  if (parameter instanceof drizzleOrm.Placeholder) {
6343
6930
  const name = parameter.name;
@@ -6550,7 +7137,8 @@ function collectParameterMetadataFromExpression(expression, names, stringOpParam
6550
7137
  case "array_op":
6551
7138
  case "object_op":
6552
7139
  case "aggregate_comparison":
6553
- case "vector_similarity": {
7140
+ case "vector_similarity":
7141
+ case "fulltext_match": {
6554
7142
  return;
6555
7143
  }
6556
7144
  case "exists": {
@@ -6681,7 +7269,7 @@ var ExecutableQuery = class _ExecutableQuery {
6681
7269
  direction
6682
7270
  } : {
6683
7271
  field: fieldRef(alias, ["props"], {
6684
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
7272
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
6685
7273
  valueType: typeInfo?.valueType,
6686
7274
  elementType: typeInfo?.elementType
6687
7275
  }),
@@ -6905,10 +7493,7 @@ var ExecutableQuery = class _ExecutableQuery {
6905
7493
  * Builds compile options from the config.
6906
7494
  */
6907
7495
  #compileOptions() {
6908
- return {
6909
- dialect: this.#config.dialect ?? "sqlite",
6910
- schema: this.#config.schema
6911
- };
7496
+ return buildCompileOptions(this.#config);
6912
7497
  }
6913
7498
  #requireBackend() {
6914
7499
  const { backend } = this.#config;
@@ -7290,7 +7875,7 @@ var ExecutableQuery = class _ExecutableQuery {
7290
7875
  if (field2.jsonPointer === void 0) {
7291
7876
  return false;
7292
7877
  }
7293
- const segments = chunkP5CNM325_cjs.parseJsonPointer(field2.jsonPointer);
7878
+ const segments = chunkUVZHTBZ5_cjs.parseJsonPointer(field2.jsonPointer);
7294
7879
  if (segments.length !== 1) {
7295
7880
  return false;
7296
7881
  }
@@ -7338,7 +7923,7 @@ var ExecutableQuery = class _ExecutableQuery {
7338
7923
  aliasObject2[fieldName] = nullToUndefined(row[outputName2]);
7339
7924
  continue;
7340
7925
  }
7341
- const segments = chunkP5CNM325_cjs.parseJsonPointer(jsonPointer2);
7926
+ const segments = chunkUVZHTBZ5_cjs.parseJsonPointer(jsonPointer2);
7342
7927
  if (segments.length === 0) {
7343
7928
  throw new MissingSelectiveFieldError(alias, "orderBy");
7344
7929
  }
@@ -7434,7 +8019,7 @@ var ExecutableQuery = class _ExecutableQuery {
7434
8019
  );
7435
8020
  }
7436
8021
  const isBackward = options.last !== void 0 || options.before !== void 0;
7437
- const limit = options.first ?? options.last ?? chunkMME3H4ZF_cjs.DEFAULT_PAGINATION_LIMIT;
8022
+ const limit = options.first ?? options.last ?? chunkFQCQM3KG_cjs.DEFAULT_PAGINATION_LIMIT;
7438
8023
  const cursor = options.after ?? options.before;
7439
8024
  let cursorData;
7440
8025
  if (cursor) {
@@ -7623,14 +8208,8 @@ var ExecutableAggregateQuery = class _ExecutableAggregateQuery {
7623
8208
  this.#cachedCompiled = compiled;
7624
8209
  return compiled;
7625
8210
  }
7626
- /**
7627
- * Builds compile options from the config.
7628
- */
7629
8211
  #compileOptions() {
7630
- return {
7631
- dialect: this.#config.dialect ?? "sqlite",
7632
- schema: this.#config.schema
7633
- };
8212
+ return buildCompileOptions(this.#config);
7634
8213
  }
7635
8214
  /**
7636
8215
  * Executes the query and returns typed results.
@@ -7861,7 +8440,7 @@ var TraversalBuilder = class _TraversalBuilder {
7861
8440
  const valueType = typeInfo?.valueType;
7862
8441
  const elementType = typeInfo?.elementType;
7863
8442
  const ref = fieldRef(alias, ["props"], {
7864
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([propertyName]),
8443
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([propertyName]),
7865
8444
  valueType,
7866
8445
  elementType
7867
8446
  });
@@ -8181,7 +8760,7 @@ var QueryBuilder = class _QueryBuilder {
8181
8760
  if (source.valueType !== void 0 || source.path.length !== 1 || source.path[0] !== "props" || source.jsonPointer === void 0) {
8182
8761
  return [outputName, source];
8183
8762
  }
8184
- const segments = chunkP5CNM325_cjs.parseJsonPointer(source.jsonPointer);
8763
+ const segments = chunkUVZHTBZ5_cjs.parseJsonPointer(source.jsonPointer);
8185
8764
  if (segments.length !== 1) {
8186
8765
  return [outputName, source];
8187
8766
  }
@@ -8235,7 +8814,7 @@ var QueryBuilder = class _QueryBuilder {
8235
8814
  direction
8236
8815
  } : {
8237
8816
  field: fieldRef(alias, ["props"], {
8238
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
8817
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
8239
8818
  valueType: typeInfo?.valueType,
8240
8819
  elementType: typeInfo?.elementType
8241
8820
  }),
@@ -8310,7 +8889,7 @@ var QueryBuilder = class _QueryBuilder {
8310
8889
  __type: "field_ref",
8311
8890
  alias,
8312
8891
  path: ["props"],
8313
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
8892
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
8314
8893
  valueType: typeInfo?.valueType,
8315
8894
  elementType: typeInfo?.elementType
8316
8895
  };
@@ -8357,6 +8936,36 @@ var QueryBuilder = class _QueryBuilder {
8357
8936
  having: predicate2
8358
8937
  });
8359
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
+ }
8360
8969
  /**
8361
8970
  * Applies a query fragment to transform this builder.
8362
8971
  *
@@ -8408,42 +9017,14 @@ var QueryBuilder = class _QueryBuilder {
8408
9017
  property
8409
9018
  ) : void 0;
8410
9019
  const ref = fieldRef(alias, ["props"], {
8411
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([property]),
9020
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([property]),
8412
9021
  valueType: typeInfo?.valueType,
8413
9022
  elementType: typeInfo?.elementType
8414
9023
  });
8415
9024
  return this.#buildFieldBuilderForTypeInfo(ref, typeInfo);
8416
9025
  }
8417
9026
  #buildFieldBuilderForTypeInfo(ref, typeInfo) {
8418
- if (!typeInfo) {
8419
- return baseField(ref);
8420
- }
8421
- switch (typeInfo.valueType) {
8422
- case "string": {
8423
- return stringField(ref);
8424
- }
8425
- case "number": {
8426
- return numberField(ref);
8427
- }
8428
- case "boolean": {
8429
- return baseField(ref);
8430
- }
8431
- case "date": {
8432
- return dateField(ref);
8433
- }
8434
- case "array": {
8435
- return arrayField(ref);
8436
- }
8437
- case "object": {
8438
- return objectField(ref, { typeInfo });
8439
- }
8440
- case "embedding": {
8441
- return embeddingField(ref);
8442
- }
8443
- case "unknown": {
8444
- return baseField(ref);
8445
- }
8446
- }
9027
+ return buildFieldBuilderForTypeInfo(ref, typeInfo);
8447
9028
  }
8448
9029
  #createNodeAccessor(alias) {
8449
9030
  const kindNames = this.#getKindNamesForAlias(alias);
@@ -8453,6 +9034,10 @@ var QueryBuilder = class _QueryBuilder {
8453
9034
  const kindAccessor = stringField(
8454
9035
  fieldRef(alias, ["kind"], { valueType: "string" })
8455
9036
  );
9037
+ const fulltextAccessor = createFulltextAccessor(
9038
+ alias,
9039
+ () => this.#hasSearchableField(kindNames)
9040
+ );
8456
9041
  return new Proxy({}, {
8457
9042
  get: (_, property) => {
8458
9043
  if (typeof property === "symbol") return;
@@ -8460,10 +9045,15 @@ var QueryBuilder = class _QueryBuilder {
8460
9045
  if (property === "toJSON") return;
8461
9046
  if (property === "id") return idAccessor;
8462
9047
  if (property === "kind") return kindAccessor;
9048
+ if (property === "$fulltext") return fulltextAccessor;
8463
9049
  return this.#getFieldBuilderForProperty(kindNames, property, alias);
8464
9050
  }
8465
9051
  });
8466
9052
  }
9053
+ #hasSearchableField(kindNames) {
9054
+ if (!kindNames) return false;
9055
+ return this.#config.schemaIntrospector.hasSearchableField(kindNames);
9056
+ }
8467
9057
  #expandTraversalEdgeKinds(edgeKind, includeImplyingEdges) {
8468
9058
  return includeImplyingEdges ? this.#config.registry.expandImplyingEdges(edgeKind) : [edgeKind];
8469
9059
  }
@@ -8506,7 +9096,7 @@ var QueryBuilder = class _QueryBuilder {
8506
9096
  property
8507
9097
  ) : void 0;
8508
9098
  const ref = fieldRef(alias, ["props"], {
8509
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([property]),
9099
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([property]),
8510
9100
  valueType: typeInfo?.valueType,
8511
9101
  elementType: typeInfo?.elementType
8512
9102
  });
@@ -8689,14 +9279,8 @@ var UnionableQuery = class _UnionableQuery {
8689
9279
  this.#cachedCompiled = compiled;
8690
9280
  return compiled;
8691
9281
  }
8692
- /**
8693
- * Builds compile options from the config.
8694
- */
8695
9282
  #compileOptions() {
8696
- return {
8697
- dialect: this.#config.dialect ?? "sqlite",
8698
- schema: this.#config.schema
8699
- };
9283
+ return buildCompileOptions(this.#config);
8700
9284
  }
8701
9285
  /**
8702
9286
  * Executes the combined query.
@@ -8758,7 +9342,7 @@ function count(alias, field2) {
8758
9342
  __type: "field_ref",
8759
9343
  alias,
8760
9344
  path: field2 ? ["props"] : ["id"],
8761
- jsonPointer: field2 ? chunkP5CNM325_cjs.jsonPointer([field2]) : void 0,
9345
+ jsonPointer: field2 ? chunkUVZHTBZ5_cjs.jsonPointer([field2]) : void 0,
8762
9346
  valueType: field2 ? void 0 : "string"
8763
9347
  }
8764
9348
  };
@@ -8771,7 +9355,7 @@ function countDistinct(alias, field2) {
8771
9355
  __type: "field_ref",
8772
9356
  alias,
8773
9357
  path: field2 ? ["props"] : ["id"],
8774
- jsonPointer: field2 ? chunkP5CNM325_cjs.jsonPointer([field2]) : void 0,
9358
+ jsonPointer: field2 ? chunkUVZHTBZ5_cjs.jsonPointer([field2]) : void 0,
8775
9359
  valueType: field2 ? void 0 : "string"
8776
9360
  }
8777
9361
  };
@@ -8784,7 +9368,7 @@ function sum(alias, field2) {
8784
9368
  __type: "field_ref",
8785
9369
  alias,
8786
9370
  path: ["props"],
8787
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
9371
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
8788
9372
  valueType: "number"
8789
9373
  }
8790
9374
  };
@@ -8797,7 +9381,7 @@ function avg(alias, field2) {
8797
9381
  __type: "field_ref",
8798
9382
  alias,
8799
9383
  path: ["props"],
8800
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
9384
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
8801
9385
  valueType: "number"
8802
9386
  }
8803
9387
  };
@@ -8810,7 +9394,7 @@ function min(alias, field2) {
8810
9394
  __type: "field_ref",
8811
9395
  alias,
8812
9396
  path: ["props"],
8813
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
9397
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
8814
9398
  valueType: "number"
8815
9399
  }
8816
9400
  };
@@ -8823,7 +9407,7 @@ function max(alias, field2) {
8823
9407
  __type: "field_ref",
8824
9408
  alias,
8825
9409
  path: ["props"],
8826
- jsonPointer: chunkP5CNM325_cjs.jsonPointer([field2]),
9410
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer([field2]),
8827
9411
  valueType: "number"
8828
9412
  }
8829
9413
  };
@@ -8854,7 +9438,7 @@ function field(alias, ...path) {
8854
9438
  __type: "field_ref",
8855
9439
  alias,
8856
9440
  path: ["props"],
8857
- jsonPointer: chunkP5CNM325_cjs.jsonPointer(path)
9441
+ jsonPointer: chunkUVZHTBZ5_cjs.jsonPointer(path)
8858
9442
  };
8859
9443
  }
8860
9444
  function having(aggregate, op, value) {
@@ -8912,16 +9496,12 @@ function offsetFragment(n) {
8912
9496
  }
8913
9497
 
8914
9498
  // src/query/builder/index.ts
8915
- setQueryBuilderClass(
8916
- QueryBuilder
8917
- );
8918
- setUnionableQueryClass(
8919
- UnionableQuery
8920
- );
9499
+ setQueryBuilderClass(QueryBuilder);
9500
+ setUnionableQueryClass(UnionableQuery);
8921
9501
 
8922
9502
  // src/query/builder.ts
8923
9503
  function createQueryBuilder(graphId, registry, options) {
8924
- const schemaIntrospector = chunkP5CNM325_cjs.createSchemaIntrospector(
9504
+ const schemaIntrospector = chunkUVZHTBZ5_cjs.createSchemaIntrospector(
8925
9505
  registry.nodeKinds,
8926
9506
  registry.edgeKinds
8927
9507
  );
@@ -8948,7 +9528,8 @@ function createQueryBuilder(graphId, registry, options) {
8948
9528
  temporalMode: "current",
8949
9529
  asOf: void 0,
8950
9530
  groupBy: void 0,
8951
- having: void 0
9531
+ having: void 0,
9532
+ fusion: void 0
8952
9533
  };
8953
9534
  return new QueryBuilder(config, initialState);
8954
9535
  }
@@ -8957,12 +9538,12 @@ function createQueryBuilder(graphId, registry, options) {
8957
9538
  function buildKindRegistry(graph) {
8958
9539
  const nodeTypes = extractNodeTypes(graph);
8959
9540
  const edgeTypes = extractEdgeTypes(graph);
8960
- const closures = graph.ontology.length > 0 ? chunkX33WV2UF_cjs.computeClosuresFromOntology(graph.ontology) : chunkX33WV2UF_cjs.createEmptyClosures();
8961
- 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);
8962
9543
  }
8963
9544
  function extractNodeTypes(graph) {
8964
9545
  const result = /* @__PURE__ */ new Map();
8965
- for (const typeName of chunk5GZMPJOP_cjs.getNodeKinds(graph)) {
9546
+ for (const typeName of chunkWRWVWKQL_cjs.getNodeKinds(graph)) {
8966
9547
  const registration = graph.nodes[typeName];
8967
9548
  result.set(typeName, registration.type);
8968
9549
  }
@@ -8970,7 +9551,7 @@ function extractNodeTypes(graph) {
8970
9551
  }
8971
9552
  function extractEdgeTypes(graph) {
8972
9553
  const result = /* @__PURE__ */ new Map();
8973
- for (const typeName of chunk5GZMPJOP_cjs.getEdgeKinds(graph)) {
9554
+ for (const typeName of chunkWRWVWKQL_cjs.getEdgeKinds(graph)) {
8974
9555
  const registration = graph.edges[typeName];
8975
9556
  result.set(typeName, registration.type);
8976
9557
  }
@@ -9184,7 +9765,7 @@ function createGraphAlgorithms(params) {
9184
9765
  const ctx = {
9185
9766
  graphId: params.graphId,
9186
9767
  backend: params.backend,
9187
- dialect: chunkMME3H4ZF_cjs.getDialect(params.backend.dialect),
9768
+ dialect: chunkFQCQM3KG_cjs.getDialect(params.backend.dialect),
9188
9769
  schema: params.schema ?? (params.backend.tableNames ? createSqlSchema(params.backend.tableNames) : createSqlSchema()),
9189
9770
  defaultTemporalMode: params.defaultTemporalMode
9190
9771
  };
@@ -9289,20 +9870,13 @@ function createEdgeCollection(config) {
9289
9870
  const result = {};
9290
9871
  if (options?.matchOn !== void 0)
9291
9872
  result.matchOn = options.matchOn;
9292
- if (options?.props !== void 0)
9293
- result.props = options.props;
9873
+ if (options?.props !== void 0) result.props = options.props;
9294
9874
  return result;
9295
9875
  }
9296
9876
  return {
9297
9877
  async create(from, to, props, options) {
9298
9878
  const result = await executeEdgeCreate2(
9299
- buildCreateEdgeInput(
9300
- kind,
9301
- from,
9302
- to,
9303
- props ?? {},
9304
- options
9305
- ),
9879
+ buildCreateEdgeInput(kind, from, to, props ?? {}, options),
9306
9880
  backend
9307
9881
  );
9308
9882
  return narrowEdge(result);
@@ -9397,7 +9971,7 @@ function createEdgeCollection(config) {
9397
9971
  temporalMode: mode
9398
9972
  };
9399
9973
  if (mode === "current" || mode === "asOf") {
9400
- params.asOf = options?.asOf ?? chunk5GZMPJOP_cjs.nowIso();
9974
+ params.asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
9401
9975
  }
9402
9976
  if (options?.from?.kind !== void 0)
9403
9977
  params.fromKind = options.from.kind;
@@ -9418,7 +9992,7 @@ function createEdgeCollection(config) {
9418
9992
  temporalMode: mode
9419
9993
  };
9420
9994
  if (mode === "current" || mode === "asOf") {
9421
- params.asOf = options?.asOf ?? chunk5GZMPJOP_cjs.nowIso();
9995
+ params.asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
9422
9996
  }
9423
9997
  if (options?.from?.kind !== void 0)
9424
9998
  params.fromKind = options.from.kind;
@@ -9428,10 +10002,7 @@ function createEdgeCollection(config) {
9428
10002
  return backend.countEdgesByKind(params);
9429
10003
  },
9430
10004
  async bulkCreate(items) {
9431
- const batchInputs = mapBulkEdgeInputs(
9432
- kind,
9433
- items
9434
- );
10005
+ const batchInputs = mapBulkEdgeInputs(kind, items);
9435
10006
  if (backend.capabilities.transactions && "transaction" in backend) {
9436
10007
  const results2 = await backend.transaction(
9437
10008
  async (txBackend) => executeEdgeCreateBatch2(batchInputs, txBackend)
@@ -9467,11 +10038,7 @@ function createEdgeCollection(config) {
9467
10038
  if (existing) {
9468
10039
  toUpdate.push({
9469
10040
  index: itemIndex,
9470
- input: buildUpdateEdgeInput(
9471
- item.id,
9472
- item.props ?? {},
9473
- item
9474
- ),
10041
+ input: buildUpdateEdgeInput(item.id, item.props ?? {}, item),
9475
10042
  clearDeleted: existing.deleted_at !== void 0
9476
10043
  });
9477
10044
  } else {
@@ -9510,10 +10077,7 @@ function createEdgeCollection(config) {
9510
10077
  return upsertAll(backend);
9511
10078
  },
9512
10079
  async bulkInsert(items) {
9513
- const batchInputs = mapBulkEdgeInputs(
9514
- kind,
9515
- items
9516
- );
10080
+ const batchInputs = mapBulkEdgeInputs(kind, items);
9517
10081
  if (backend.capabilities.transactions && "transaction" in backend) {
9518
10082
  await backend.transaction(async (txBackend) => {
9519
10083
  await executeEdgeCreateNoReturnBatch2(batchInputs, txBackend);
@@ -9664,11 +10228,7 @@ function createNodeCollection(config) {
9664
10228
  async getByIds(ids, options) {
9665
10229
  if (ids.length === 0) return [];
9666
10230
  if (backend.getNodes !== void 0) {
9667
- const rows = await backend.getNodes(
9668
- graphId,
9669
- kind,
9670
- ids
9671
- );
10231
+ const rows = await backend.getNodes(graphId, kind, ids);
9672
10232
  const rowMap = /* @__PURE__ */ new Map();
9673
10233
  for (const row of rows) {
9674
10234
  rowMap.set(row.id, row);
@@ -9713,7 +10273,7 @@ function createNodeCollection(config) {
9713
10273
  const mode2 = options.temporalMode ?? defaultTemporalMode;
9714
10274
  let query = createQuery().from(kind, "_n").temporal(
9715
10275
  mode2,
9716
- mode2 === "asOf" ? options.asOf ?? chunk5GZMPJOP_cjs.nowIso() : void 0
10276
+ mode2 === "asOf" ? options.asOf ?? chunkWRWVWKQL_cjs.nowIso() : void 0
9717
10277
  ).whereNode("_n", options.where).select((ctx) => ctx._n);
9718
10278
  if (options.limit !== void 0) query = query.limit(options.limit);
9719
10279
  if (options.offset !== void 0) query = query.offset(options.offset);
@@ -9728,7 +10288,7 @@ function createNodeCollection(config) {
9728
10288
  temporalMode: mode
9729
10289
  };
9730
10290
  if (mode === "current" || mode === "asOf") {
9731
- params.asOf = options?.asOf ?? chunk5GZMPJOP_cjs.nowIso();
10291
+ params.asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
9732
10292
  }
9733
10293
  if (options?.limit !== void 0) params.limit = options.limit;
9734
10294
  if (options?.offset !== void 0) params.offset = options.offset;
@@ -9744,16 +10304,12 @@ function createNodeCollection(config) {
9744
10304
  temporalMode: mode
9745
10305
  };
9746
10306
  if (mode === "current" || mode === "asOf") {
9747
- params.asOf = options?.asOf ?? chunk5GZMPJOP_cjs.nowIso();
10307
+ params.asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
9748
10308
  }
9749
10309
  return backend.countNodesByKind(params);
9750
10310
  },
9751
10311
  async upsertById(id, props, options) {
9752
- return this.upsertByIdFromRecord(
9753
- id,
9754
- props,
9755
- options
9756
- );
10312
+ return this.upsertByIdFromRecord(id, props, options);
9757
10313
  },
9758
10314
  async upsertByIdFromRecord(id, data, options) {
9759
10315
  const existing = await backend.getNode(graphId, kind, id);
@@ -9773,10 +10329,7 @@ function createNodeCollection(config) {
9773
10329
  return narrowNode(result);
9774
10330
  },
9775
10331
  async bulkCreate(items) {
9776
- const batchInputs = mapBulkNodeInputs(
9777
- kind,
9778
- items
9779
- );
10332
+ const batchInputs = mapBulkNodeInputs(kind, items);
9780
10333
  if (backend.capabilities.transactions && "transaction" in backend) {
9781
10334
  const results2 = await backend.transaction(
9782
10335
  async (txBackend) => executeNodeCreateBatch2(batchInputs, txBackend)
@@ -9799,11 +10352,7 @@ function createNodeCollection(config) {
9799
10352
  if (row !== void 0) existingMap.set(row.id, row);
9800
10353
  }
9801
10354
  } else {
9802
- const rows = await target.getNodes(
9803
- graphId,
9804
- kind,
9805
- ids
9806
- );
10355
+ const rows = await target.getNodes(graphId, kind, ids);
9807
10356
  for (const row of rows) {
9808
10357
  existingMap.set(row.id, row);
9809
10358
  }
@@ -9816,22 +10365,13 @@ function createNodeCollection(config) {
9816
10365
  if (existing) {
9817
10366
  toUpdate.push({
9818
10367
  index: itemIndex,
9819
- input: buildUpdateInput(
9820
- kind,
9821
- item.id,
9822
- item.props,
9823
- item
9824
- ),
10368
+ input: buildUpdateInput(kind, item.id, item.props, item),
9825
10369
  clearDeleted: existing.deleted_at !== void 0
9826
10370
  });
9827
10371
  } else {
9828
10372
  toCreate.push({
9829
10373
  index: itemIndex,
9830
- input: buildCreateInput(
9831
- kind,
9832
- item.props,
9833
- item
9834
- )
10374
+ input: buildCreateInput(kind, item.props, item)
9835
10375
  });
9836
10376
  }
9837
10377
  itemIndex++;
@@ -9858,10 +10398,7 @@ function createNodeCollection(config) {
9858
10398
  return upsertAll(backend);
9859
10399
  },
9860
10400
  async bulkInsert(items) {
9861
- const batchInputs = mapBulkNodeInputs(
9862
- kind,
9863
- items
9864
- );
10401
+ const batchInputs = mapBulkNodeInputs(kind, items);
9865
10402
  if (backend.capabilities.transactions && "transaction" in backend) {
9866
10403
  await backend.transaction(async (txBackend) => {
9867
10404
  await executeNodeCreateNoReturnBatch2(batchInputs, txBackend);
@@ -10394,12 +10931,12 @@ async function validateAndPrepareEdgeCreate(ctx, input, id, backend) {
10394
10931
  nodeId: input.toId
10395
10932
  });
10396
10933
  }
10397
- const validatedProps = chunkX33WV2UF_cjs.validateEdgeProps(edgeKind.schema, input.props, {
10934
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, input.props, {
10398
10935
  kind,
10399
10936
  operation: "create"
10400
10937
  });
10401
- const validFrom = chunk5GZMPJOP_cjs.validateOptionalIsoDate(input.validFrom, "validFrom");
10402
- 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");
10403
10940
  const cardinality = registration.cardinality ?? "many";
10404
10941
  const constraintContext = {
10405
10942
  graphId: ctx.graphId,
@@ -10544,12 +11081,12 @@ async function executeEdgeUpdate(ctx, input, backend) {
10544
11081
  const edgeKind = registration.type;
10545
11082
  const existingProps = JSON.parse(existing.props);
10546
11083
  const mergedProps = { ...existingProps, ...input.props };
10547
- const validatedProps = chunkX33WV2UF_cjs.validateEdgeProps(edgeKind.schema, mergedProps, {
11084
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, mergedProps, {
10548
11085
  kind: existing.kind,
10549
11086
  operation: "update",
10550
11087
  id
10551
11088
  });
10552
- const validTo = chunk5GZMPJOP_cjs.validateOptionalIsoDate(input.validTo, "validTo");
11089
+ const validTo = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validTo, "validTo");
10553
11090
  const updateParams = {
10554
11091
  graphId: ctx.graphId,
10555
11092
  id,
@@ -10570,12 +11107,12 @@ async function executeEdgeUpsertUpdate(ctx, input, backend, options) {
10570
11107
  const edgeKind = registration.type;
10571
11108
  const existingProps = JSON.parse(existing.props);
10572
11109
  const mergedProps = { ...existingProps, ...input.props };
10573
- const validatedProps = chunkX33WV2UF_cjs.validateEdgeProps(edgeKind.schema, mergedProps, {
11110
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, mergedProps, {
10574
11111
  kind: existing.kind,
10575
11112
  operation: "update",
10576
11113
  id
10577
11114
  });
10578
- const validTo = chunk5GZMPJOP_cjs.validateOptionalIsoDate(input.validTo, "validTo");
11115
+ const validTo = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validTo, "validTo");
10579
11116
  const updateParams = {
10580
11117
  graphId: ctx.graphId,
10581
11118
  id,
@@ -10727,7 +11264,7 @@ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, to
10727
11264
  const matchOn = options?.matchOn ?? [];
10728
11265
  const registration = getEdgeRegistration(ctx.graph, kind);
10729
11266
  const edgeKind = registration.type;
10730
- const validatedProps = chunkX33WV2UF_cjs.validateEdgeProps(edgeKind.schema, props, {
11267
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, props, {
10731
11268
  kind,
10732
11269
  operation: "create"
10733
11270
  });
@@ -10809,7 +11346,7 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
10809
11346
  validateMatchOnFields(edgeKind.schema, matchOn, kind);
10810
11347
  const validated = [];
10811
11348
  for (const item of items) {
10812
- const validatedProps = chunkX33WV2UF_cjs.validateEdgeProps(edgeKind.schema, item.props, {
11349
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateEdgeProps(edgeKind.schema, item.props, {
10813
11350
  kind,
10814
11351
  operation: "create"
10815
11352
  });
@@ -10967,15 +11504,19 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
10967
11504
  }
10968
11505
 
10969
11506
  // src/store/embedding-sync.ts
11507
+ var embeddingFieldsCache = /* @__PURE__ */ new WeakMap();
10970
11508
  function getEmbeddingFields(schema) {
10971
- if (schema.type !== "object") {
10972
- return [];
10973
- }
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 [];
10974
11517
  const def = schema.def;
10975
11518
  const shape = def.shape;
10976
- if (!shape) {
10977
- return [];
10978
- }
11519
+ if (!shape) return [];
10979
11520
  const fields = [];
10980
11521
  for (const [fieldPath, fieldSchema] of Object.entries(shape)) {
10981
11522
  const dimensions = getEmbeddingDimensionsFromField(fieldSchema);
@@ -10986,13 +11527,13 @@ function getEmbeddingFields(schema) {
10986
11527
  return fields;
10987
11528
  }
10988
11529
  function getEmbeddingDimensionsFromField(schema) {
10989
- const directDimensions = chunkP5CNM325_cjs.getEmbeddingDimensions(schema);
11530
+ const directDimensions = chunkUVZHTBZ5_cjs.getEmbeddingDimensions(schema);
10990
11531
  if (directDimensions !== void 0) {
10991
11532
  return directDimensions;
10992
11533
  }
10993
11534
  const unwrapped = unwrapToEmbedding(schema);
10994
11535
  if (unwrapped) {
10995
- return chunkP5CNM325_cjs.getEmbeddingDimensions(unwrapped);
11536
+ return chunkUVZHTBZ5_cjs.getEmbeddingDimensions(unwrapped);
10996
11537
  }
10997
11538
  return void 0;
10998
11539
  }
@@ -11000,7 +11541,7 @@ function unwrapToEmbedding(schema) {
11000
11541
  const type = schema.type;
11001
11542
  const def = schema.def;
11002
11543
  if ((type === "optional" || type === "nullable" || type === "default" || type === "readonly") && def.innerType) {
11003
- if (chunkP5CNM325_cjs.isEmbeddingSchema(def.innerType)) {
11544
+ if (chunkUVZHTBZ5_cjs.isEmbeddingSchema(def.innerType)) {
11004
11545
  return def.innerType;
11005
11546
  }
11006
11547
  return unwrapToEmbedding(def.innerType);
@@ -11062,6 +11603,101 @@ function isValidEmbeddingValue(value) {
11062
11603
  return value.every((n) => typeof n === "number" && Number.isFinite(n));
11063
11604
  }
11064
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
+
11065
11701
  // src/store/uniqueness.ts
11066
11702
  async function checkUniquenessConstraints(ctx, kind, id, props, constraints) {
11067
11703
  for (const constraint of constraints) {
@@ -11255,6 +11891,9 @@ function createUniquenessContext(graphId, registry, backend) {
11255
11891
  function createEmbeddingSyncContext(graphId, nodeKind, nodeId, backend) {
11256
11892
  return { graphId, nodeKind, nodeId, backend };
11257
11893
  }
11894
+ function createFulltextSyncContext(graphId, nodeKind, nodeId, backend) {
11895
+ return { graphId, nodeKind, nodeId, backend };
11896
+ }
11258
11897
  function createNodeBatchValidationBackend(graphId, registry, backend) {
11259
11898
  const nodeCache = /* @__PURE__ */ new Map();
11260
11899
  const pendingNodes = /* @__PURE__ */ new Map();
@@ -11354,12 +11993,12 @@ async function validateAndPrepareNodeCreate(ctx, input, id, backend) {
11354
11993
  const kind = input.kind;
11355
11994
  const registration = getNodeRegistration(ctx.graph, kind);
11356
11995
  const nodeKind = registration.type;
11357
- const validatedProps = chunkX33WV2UF_cjs.validateNodeProps(nodeKind.schema, input.props, {
11996
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, input.props, {
11358
11997
  kind,
11359
11998
  operation: "create"
11360
11999
  });
11361
- const validFrom = chunk5GZMPJOP_cjs.validateOptionalIsoDate(input.validFrom, "validFrom");
11362
- 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");
11363
12002
  const existingNode = await backend.getNode(ctx.graphId, kind, id);
11364
12003
  if (existingNode && !existingNode.deleted_at) {
11365
12004
  throw createNodeAlreadyExistsError(kind, id);
@@ -11412,6 +12051,11 @@ async function finalizeNodeCreate(ctx, prepared, backend) {
11412
12051
  prepared.nodeKind.schema,
11413
12052
  prepared.validatedProps
11414
12053
  );
12054
+ await syncFulltext(
12055
+ createFulltextSyncContext(ctx.graphId, prepared.kind, prepared.id, backend),
12056
+ prepared.nodeKind.schema,
12057
+ prepared.validatedProps
12058
+ );
11415
12059
  }
11416
12060
  async function performNodeUpdate(ctx, input, backend, options) {
11417
12061
  const { kind, id } = input;
@@ -11423,12 +12067,12 @@ async function performNodeUpdate(ctx, input, backend, options) {
11423
12067
  const existingProps = JSON.parse(existing.props);
11424
12068
  const mergedProps = { ...existingProps, ...input.props };
11425
12069
  const nodeKind = registration.type;
11426
- const validatedProps = chunkX33WV2UF_cjs.validateNodeProps(nodeKind.schema, mergedProps, {
12070
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, mergedProps, {
11427
12071
  kind,
11428
12072
  operation: "update",
11429
12073
  id
11430
12074
  });
11431
- const validTo = chunk5GZMPJOP_cjs.validateOptionalIsoDate(input.validTo, "validTo");
12075
+ const validTo = chunkWRWVWKQL_cjs.validateOptionalIsoDate(input.validTo, "validTo");
11432
12076
  await updateUniquenessEntries(
11433
12077
  createUniquenessContext(ctx.graphId, ctx.registry, backend),
11434
12078
  kind,
@@ -11452,6 +12096,11 @@ async function performNodeUpdate(ctx, input, backend, options) {
11452
12096
  nodeKind.schema,
11453
12097
  validatedProps
11454
12098
  );
12099
+ await syncFulltext(
12100
+ createFulltextSyncContext(ctx.graphId, kind, id, backend),
12101
+ nodeKind.schema,
12102
+ validatedProps
12103
+ );
11455
12104
  return rowToNode(row);
11456
12105
  }
11457
12106
  async function enforceDeleteBehavior(ctx, kind, id, mode, backend, registration) {
@@ -11668,6 +12317,10 @@ async function executeNodeDelete(ctx, kind, id, backend) {
11668
12317
  createEmbeddingSyncContext(ctx.graphId, kind, id, backend),
11669
12318
  nodeKind.schema
11670
12319
  );
12320
+ await deleteNodeFulltext(
12321
+ createFulltextSyncContext(ctx.graphId, kind, id, backend),
12322
+ nodeKind.schema
12323
+ );
11671
12324
  });
11672
12325
  }
11673
12326
  async function executeNodeHardDelete(ctx, kind, id, backend) {
@@ -11687,7 +12340,7 @@ async function executeNodeGetOrCreateByConstraint(ctx, kind, constraintName, pro
11687
12340
  const ifExists = options?.ifExists ?? "return";
11688
12341
  const registration = getNodeRegistration(ctx.graph, kind);
11689
12342
  const nodeKind = registration.type;
11690
- const validatedProps = chunkX33WV2UF_cjs.validateNodeProps(nodeKind.schema, props, {
12343
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, props, {
11691
12344
  kind,
11692
12345
  operation: "create"
11693
12346
  });
@@ -11759,7 +12412,7 @@ async function executeNodeGetOrCreateByConstraint(ctx, kind, constraintName, pro
11759
12412
  async function executeNodeFindByConstraint(ctx, kind, constraintName, props, backend) {
11760
12413
  const registration = getNodeRegistration(ctx.graph, kind);
11761
12414
  const nodeKind = registration.type;
11762
- const validatedProps = chunkX33WV2UF_cjs.validateNodeProps(nodeKind.schema, props, {
12415
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, props, {
11763
12416
  kind,
11764
12417
  operation: "create"
11765
12418
  });
@@ -11796,7 +12449,7 @@ async function executeNodeFindByConstraint(ctx, kind, constraintName, props, bac
11796
12449
  function validateAndComputeKeys(nodeKind, kind, constraint, items) {
11797
12450
  const validated = [];
11798
12451
  for (const item of items) {
11799
- const validatedProps = chunkX33WV2UF_cjs.validateNodeProps(nodeKind.schema, item.props, {
12452
+ const validatedProps = chunkHRNQ3DVJ_cjs.validateNodeProps(nodeKind.schema, item.props, {
11800
12453
  kind,
11801
12454
  operation: "create"
11802
12455
  });
@@ -11974,6 +12627,503 @@ async function executeNodeBulkGetOrCreateByConstraint(ctx, kind, constraintName,
11974
12627
  return results;
11975
12628
  }
11976
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
+
11977
13127
  // src/store/store.ts
11978
13128
  var Store = class {
11979
13129
  #graph;
@@ -11985,6 +13135,7 @@ var Store = class {
11985
13135
  #nodeCollections;
11986
13136
  #edgeCollections;
11987
13137
  #algorithms;
13138
+ #search;
11988
13139
  constructor(graph, backend, options) {
11989
13140
  this.#graph = graph;
11990
13141
  this.#backend = backend;
@@ -12233,6 +13384,44 @@ var Store = class {
12233
13384
  query() {
12234
13385
  return this.#createQueryForBackend(this.#backend);
12235
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
+ }
12236
13425
  // === Batch Query Execution ===
12237
13426
  /**
12238
13427
  * Executes multiple queries over a single connection with snapshot consistency.
@@ -12306,7 +13495,7 @@ var Store = class {
12306
13495
  graphId: this.graphId,
12307
13496
  rootId,
12308
13497
  backend: this.#backend,
12309
- dialect: chunkMME3H4ZF_cjs.getDialect(this.#backend.dialect),
13498
+ dialect: chunkFQCQM3KG_cjs.getDialect(this.#backend.dialect),
12310
13499
  schema: this.#schema,
12311
13500
  options
12312
13501
  });
@@ -12377,6 +13566,7 @@ var Store = class {
12377
13566
  this.#nodeCollections = void 0;
12378
13567
  this.#edgeCollections = void 0;
12379
13568
  this.#algorithms = void 0;
13569
+ this.#search = void 0;
12380
13570
  }
12381
13571
  // === Lifecycle ===
12382
13572
  /**
@@ -12446,7 +13636,7 @@ var Store = class {
12446
13636
  // === Internal: Temporal Filtering ===
12447
13637
  #matchesTemporalMode(row, options) {
12448
13638
  const mode = options?.temporalMode ?? this.#graph.defaults.temporalMode;
12449
- const asOf = options?.asOf ?? chunk5GZMPJOP_cjs.nowIso();
13639
+ const asOf = options?.asOf ?? chunkWRWVWKQL_cjs.nowIso();
12450
13640
  switch (mode) {
12451
13641
  case "current":
12452
13642
  case "asOf": {
@@ -12479,41 +13669,41 @@ function createStore(graph, backend, options) {
12479
13669
  }
12480
13670
  async function createStoreWithSchema(graph, backend, options) {
12481
13671
  const store = createStore(graph, backend, options);
12482
- const result = await chunkX33WV2UF_cjs.ensureSchema(backend, graph, options);
13672
+ const result = await chunkHRNQ3DVJ_cjs.ensureSchema(backend, graph, options);
12483
13673
  return [store, result];
12484
13674
  }
12485
13675
 
12486
13676
  Object.defineProperty(exports, "isMetaEdge", {
12487
13677
  enumerable: true,
12488
- get: function () { return chunkX33WV2UF_cjs.isMetaEdge; }
13678
+ get: function () { return chunkHRNQ3DVJ_cjs.isMetaEdge; }
12489
13679
  });
12490
13680
  Object.defineProperty(exports, "defineGraph", {
12491
13681
  enumerable: true,
12492
- get: function () { return chunk5GZMPJOP_cjs.defineGraph; }
13682
+ get: function () { return chunkWRWVWKQL_cjs.defineGraph; }
12493
13683
  });
12494
13684
  Object.defineProperty(exports, "getEdgeKinds", {
12495
13685
  enumerable: true,
12496
- get: function () { return chunk5GZMPJOP_cjs.getEdgeKinds; }
13686
+ get: function () { return chunkWRWVWKQL_cjs.getEdgeKinds; }
12497
13687
  });
12498
13688
  Object.defineProperty(exports, "getNodeKinds", {
12499
13689
  enumerable: true,
12500
- get: function () { return chunk5GZMPJOP_cjs.getNodeKinds; }
13690
+ get: function () { return chunkWRWVWKQL_cjs.getNodeKinds; }
12501
13691
  });
12502
13692
  Object.defineProperty(exports, "isEdgeType", {
12503
13693
  enumerable: true,
12504
- get: function () { return chunk5GZMPJOP_cjs.isEdgeType; }
13694
+ get: function () { return chunkWRWVWKQL_cjs.isEdgeType; }
12505
13695
  });
12506
13696
  Object.defineProperty(exports, "isEdgeTypeWithEndpoints", {
12507
13697
  enumerable: true,
12508
- get: function () { return chunk5GZMPJOP_cjs.isEdgeTypeWithEndpoints; }
13698
+ get: function () { return chunkWRWVWKQL_cjs.isEdgeTypeWithEndpoints; }
12509
13699
  });
12510
13700
  Object.defineProperty(exports, "isGraphDef", {
12511
13701
  enumerable: true,
12512
- get: function () { return chunk5GZMPJOP_cjs.isGraphDef; }
13702
+ get: function () { return chunkWRWVWKQL_cjs.isGraphDef; }
12513
13703
  });
12514
13704
  Object.defineProperty(exports, "isNodeType", {
12515
13705
  enumerable: true,
12516
- get: function () { return chunk5GZMPJOP_cjs.isNodeType; }
13706
+ get: function () { return chunkWRWVWKQL_cjs.isNodeType; }
12517
13707
  });
12518
13708
  Object.defineProperty(exports, "BackendDisposedError", {
12519
13709
  enumerable: true,
@@ -12615,37 +13805,61 @@ Object.defineProperty(exports, "isUserRecoverable", {
12615
13805
  enumerable: true,
12616
13806
  get: function () { return chunkO5PST6TB_cjs.isUserRecoverable; }
12617
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
+ });
12618
13820
  Object.defineProperty(exports, "MAX_JSON_POINTER_DEPTH", {
12619
13821
  enumerable: true,
12620
- get: function () { return chunkP5CNM325_cjs.MAX_JSON_POINTER_DEPTH; }
13822
+ get: function () { return chunkUVZHTBZ5_cjs.MAX_JSON_POINTER_DEPTH; }
12621
13823
  });
12622
13824
  Object.defineProperty(exports, "embedding", {
12623
13825
  enumerable: true,
12624
- get: function () { return chunkP5CNM325_cjs.embedding; }
13826
+ get: function () { return chunkUVZHTBZ5_cjs.embedding; }
12625
13827
  });
12626
13828
  Object.defineProperty(exports, "getEmbeddingDimensions", {
12627
13829
  enumerable: true,
12628
- 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; }
12629
13835
  });
12630
13836
  Object.defineProperty(exports, "isEmbeddingSchema", {
12631
13837
  enumerable: true,
12632
- 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; }
12633
13843
  });
12634
13844
  Object.defineProperty(exports, "joinJsonPointers", {
12635
13845
  enumerable: true,
12636
- get: function () { return chunkP5CNM325_cjs.joinJsonPointers; }
13846
+ get: function () { return chunkUVZHTBZ5_cjs.joinJsonPointers; }
12637
13847
  });
12638
13848
  Object.defineProperty(exports, "jsonPointer", {
12639
13849
  enumerable: true,
12640
- get: function () { return chunkP5CNM325_cjs.jsonPointer; }
13850
+ get: function () { return chunkUVZHTBZ5_cjs.jsonPointer; }
12641
13851
  });
12642
13852
  Object.defineProperty(exports, "normalizeJsonPointer", {
12643
13853
  enumerable: true,
12644
- get: function () { return chunkP5CNM325_cjs.normalizeJsonPointer; }
13854
+ get: function () { return chunkUVZHTBZ5_cjs.normalizeJsonPointer; }
12645
13855
  });
12646
13856
  Object.defineProperty(exports, "parseJsonPointer", {
12647
13857
  enumerable: true,
12648
- 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; }
12649
13863
  });
12650
13864
  exports.DEFAULT_SQL_SCHEMA = DEFAULT_SQL_SCHEMA;
12651
13865
  exports.ExecutableAggregateQuery = ExecutableAggregateQuery;
@@ -12654,6 +13868,7 @@ exports.MAX_EXPLICIT_RECURSIVE_DEPTH = MAX_EXPLICIT_RECURSIVE_DEPTH;
12654
13868
  exports.MAX_RECURSIVE_DEPTH = MAX_RECURSIVE_DEPTH;
12655
13869
  exports.PreparedQuery = PreparedQuery;
12656
13870
  exports.QueryBuilder = QueryBuilder;
13871
+ exports.StoreSearch = StoreSearch;
12657
13872
  exports.UnionableQuery = UnionableQuery;
12658
13873
  exports.avg = avg;
12659
13874
  exports.broader = broader;