@nicia-ai/typegraph 0.20.0 → 0.22.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 (101) 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 +22 -7
  11. package/dist/backend/sqlite/index.d.ts +22 -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 +27 -10
  18. package/dist/backend/sqlite/local.cjs.map +1 -1
  19. package/dist/backend/sqlite/local.d.cts +4 -5
  20. package/dist/backend/sqlite/local.d.ts +4 -5
  21. package/dist/backend/sqlite/local.js +22 -6
  22. package/dist/backend/sqlite/local.js.map +1 -1
  23. package/dist/{chunk-MME3H4ZF.cjs → chunk-3JK66E3I.cjs} +387 -9
  24. package/dist/chunk-3JK66E3I.cjs.map +1 -0
  25. package/dist/{chunk-6GWJH6AR.js → chunk-3PPEHLGK.js} +4 -4
  26. package/dist/{chunk-6GWJH6AR.js.map → chunk-3PPEHLGK.js.map} +1 -1
  27. package/dist/{chunk-QFZQGEQ3.js → chunk-5ZMMHUWR.js} +199 -38
  28. package/dist/chunk-5ZMMHUWR.js.map +1 -0
  29. package/dist/{chunk-GNIYZKBI.js → chunk-753F33CV.js} +378 -8
  30. package/dist/chunk-753F33CV.js.map +1 -0
  31. package/dist/{chunk-K7SQ3SWP.js → chunk-EYELZHKY.js} +120 -12
  32. package/dist/chunk-EYELZHKY.js.map +1 -0
  33. package/dist/{chunk-HXJXTOXR.cjs → chunk-GUYI6VAS.cjs} +101 -21
  34. package/dist/chunk-GUYI6VAS.cjs.map +1 -0
  35. package/dist/{chunk-X33WV2UF.cjs → chunk-HRNQ3DVJ.cjs} +24 -21
  36. package/dist/chunk-HRNQ3DVJ.cjs.map +1 -0
  37. package/dist/{chunk-W6M7XWKX.js → chunk-JYUQA77I.js} +21 -18
  38. package/dist/chunk-JYUQA77I.js.map +1 -0
  39. package/dist/{chunk-LLHFS53Q.js → chunk-NSLTDXVH.js} +91 -11
  40. package/dist/chunk-NSLTDXVH.js.map +1 -0
  41. package/dist/{chunk-LELLOHJK.cjs → chunk-PEE4EWXZ.cjs} +35 -35
  42. package/dist/{chunk-LELLOHJK.cjs.map → chunk-PEE4EWXZ.cjs.map} +1 -1
  43. package/dist/{chunk-G5CYWKRF.js → chunk-RS5OLGEW.js} +2 -2
  44. package/dist/{chunk-G5CYWKRF.js.map → chunk-RS5OLGEW.js.map} +1 -1
  45. package/dist/{chunk-IYWWXOVK.cjs → chunk-SCXOGKLD.cjs} +252 -91
  46. package/dist/chunk-SCXOGKLD.cjs.map +1 -0
  47. package/dist/{chunk-P5CNM325.cjs → chunk-UVZHTBZ5.cjs} +123 -11
  48. package/dist/chunk-UVZHTBZ5.cjs.map +1 -0
  49. package/dist/{chunk-5GZMPJOP.cjs → chunk-WRWVWKQL.cjs} +2 -2
  50. package/dist/{chunk-5GZMPJOP.cjs.map → chunk-WRWVWKQL.cjs.map} +1 -1
  51. package/dist/{ddl-BBo2EcFc.d.cts → ddl-BqkTShcR.d.ts} +28 -6
  52. package/dist/{ddl-D4jlcOH_.d.ts → ddl-Dk1NDN3M.d.cts} +28 -6
  53. package/dist/index.cjs +1809 -513
  54. package/dist/index.cjs.map +1 -1
  55. package/dist/index.d.cts +53 -11
  56. package/dist/index.d.ts +53 -11
  57. package/dist/index.js +1668 -398
  58. package/dist/index.js.map +1 -1
  59. package/dist/indexes/index.cjs +17 -17
  60. package/dist/indexes/index.d.cts +5 -5
  61. package/dist/indexes/index.d.ts +5 -5
  62. package/dist/indexes/index.js +3 -3
  63. package/dist/interchange/index.cjs +6 -6
  64. package/dist/interchange/index.d.cts +4 -5
  65. package/dist/interchange/index.d.ts +4 -5
  66. package/dist/interchange/index.js +1 -1
  67. package/dist/{manager-Chhrq1vl.d.ts → manager-0jVZtiE8.d.ts} +1 -2
  68. package/dist/{manager-Bj1UEnhE.d.cts → manager-BPK-2PUI.d.cts} +1 -2
  69. package/dist/profiler/index.cjs +15 -8
  70. package/dist/profiler/index.cjs.map +1 -1
  71. package/dist/profiler/index.d.cts +7 -8
  72. package/dist/profiler/index.d.ts +7 -8
  73. package/dist/profiler/index.js +8 -1
  74. package/dist/profiler/index.js.map +1 -1
  75. package/dist/schema/index.cjs +21 -21
  76. package/dist/schema/index.d.cts +4 -5
  77. package/dist/schema/index.d.ts +4 -5
  78. package/dist/schema/index.js +3 -3
  79. package/dist/{sqlite-DE-6NWtC.d.cts → sqlite-D1rrwcir.d.cts} +14 -1
  80. package/dist/{sqlite-Blq-AhmK.d.ts → sqlite-W3rmJMlc.d.ts} +14 -1
  81. package/dist/{store-BMrRi_mY.d.ts → store-2QW2SIRk.d.cts} +404 -5
  82. package/dist/{store-DRRvALu8.d.cts → store-C0q13mtJ.d.ts} +404 -5
  83. package/dist/{types-B3mmOMJV.d.cts → types-CQF88McG.d.cts} +475 -2
  84. package/dist/{types-ThB4cFLp.d.ts → types-CQF88McG.d.ts} +475 -2
  85. package/dist/{types-COPePE8_.d.ts → types-CglvhPKz.d.ts} +1 -1
  86. package/dist/{types-CVtGFpB9.d.cts → types-D23slCOA.d.cts} +1 -1
  87. package/dist/{types-Ceb01czq.d.ts → types-IMQCuser.d.ts} +2 -2
  88. package/dist/{types-CZd2PEOc.d.cts → types-vgjar7Ch.d.cts} +2 -2
  89. package/package.json +9 -9
  90. package/dist/chunk-GNIYZKBI.js.map +0 -1
  91. package/dist/chunk-HXJXTOXR.cjs.map +0 -1
  92. package/dist/chunk-IYWWXOVK.cjs.map +0 -1
  93. package/dist/chunk-K7SQ3SWP.js.map +0 -1
  94. package/dist/chunk-LLHFS53Q.js.map +0 -1
  95. package/dist/chunk-MME3H4ZF.cjs.map +0 -1
  96. package/dist/chunk-P5CNM325.cjs.map +0 -1
  97. package/dist/chunk-QFZQGEQ3.js.map +0 -1
  98. package/dist/chunk-W6M7XWKX.js.map +0 -1
  99. package/dist/chunk-X33WV2UF.cjs.map +0 -1
  100. package/dist/types-DMzKq0d5.d.cts +0 -181
  101. package/dist/types-DMzKq0d5.d.ts +0 -181
@@ -1,8 +1,7 @@
1
- import { S as SqlSchema, G as GraphBackend, T as TransactionBackend, E as EdgeRow, N as NodeRow } from './types-ThB4cFLp.js';
2
- import { K as KindRegistry, G as GraphDef, E as EdgeKinds, N as NodeKinds, a as AllNodeTypes, S as SchemaManagerOptions, d as SchemaValidationResult } from './manager-Chhrq1vl.js';
3
- import { S as SqlDialect, N as NodeType, A as AnyEdgeType, T as TemporalMode, E as EdgeType, d as EdgeRegistration, b as EdgeId, g as NodeId, i as NodeRegistration } from './types-DMzKq0d5.js';
1
+ import { S as SqlDialect, s as SqlSchema, m as FulltextStrategy, j as FulltextQueryMode, N as NodeType, G as GraphBackend, u as TransactionBackend, A as AnyEdgeType, T as TemporalMode, E as EdgeType, f as EdgeRegistration, d as EdgeId, p as NodeId, P as EdgeRow, Q as NodeRow, r as NodeRegistration, R as VectorMetric } from './types-CQF88McG.cjs';
2
+ import { K as KindRegistry, G as GraphDef, E as EdgeKinds, N as NodeKinds, a as AllNodeTypes, S as SchemaManagerOptions, d as SchemaValidationResult } from './manager-BPK-2PUI.cjs';
4
3
  import { z } from 'zod';
5
- import { V as ValueType, i as PredicateExpression, k as VectorMetricType, Q as QueryAst, J as JsonPointer, F as FieldRef, P as ParameterRef, c as JsonPointerInput, T as TraversalExpansion, l as Traversal, N as NodePredicate, m as ProjectedField, O as OrderSpec, G as GroupBySpec, o as RecursiveCyclePolicy, q as TraversalDirection$1, A as AggregateExpr, r as SelectiveField, s as ComposableQuery, t as SetOperationType, u as SetOperation, S as SortDirection } from './ast-COMyNeMn.js';
4
+ import { V as ValueType, i as PredicateExpression, k as VectorMetricType, Q as QueryAst, J as JsonPointer, F as FieldRef, P as ParameterRef, c as JsonPointerInput, T as TraversalExpansion, l as Traversal, N as NodePredicate, m as ProjectedField, O as OrderSpec, G as GroupBySpec, H as HybridFusionOptions, o as RecursiveCyclePolicy, q as TraversalDirection$1, A as AggregateExpr, r as SelectiveField, s as ComposableQuery, t as SetOperationType, u as SetOperation, S as SortDirection } from './ast-DogwbBqA.cjs';
6
5
  import { SQL } from 'drizzle-orm';
7
6
 
8
7
  /**
@@ -86,6 +85,75 @@ declare function isEmbeddingSchema(value: unknown): value is EmbeddingSchema;
86
85
  */
87
86
  declare function getEmbeddingDimensions(schema: z.ZodType): number | undefined;
88
87
 
88
+ /**
89
+ * Searchable string type for fulltext search.
90
+ *
91
+ * `searchable()` attaches `SearchableMetadata` to a plain Zod string so
92
+ * the schema introspector and fulltext-sync layer can keep the fulltext
93
+ * index in sync with node data.
94
+ *
95
+ * The metadata is preserved by Zod's `.meta()` across refinements
96
+ * (`.min(1)`, `.trim()`, `.regex(...)`) and wrapper types (`.optional()`,
97
+ * `.nullable()`, `.default(...)`, pipes) — `getSearchableMetadata()` walks
98
+ * these variants so runtime indexing works whether or not a user chains
99
+ * additional modifiers.
100
+ *
101
+ * `$fulltext` is exposed on every `NodeAccessor` at the type level; a
102
+ * runtime guard throws a clear error if you call `.matches()` on an
103
+ * alias whose node kind has no `searchable()` fields. This is simpler
104
+ * and more predictable than a type-level brand that silently disappears
105
+ * behind `.min(1)`.
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * const Document = defineNode("Document", {
110
+ * schema: z.object({
111
+ * title: searchable({ language: "english" }),
112
+ * body: searchable().min(1),
113
+ * authorId: z.string(),
114
+ * }),
115
+ * });
116
+ * ```
117
+ */
118
+
119
+ declare const SEARCHABLE_FIELD_KEY: "_searchableField";
120
+ declare const DEFAULT_SEARCHABLE_LANGUAGE: "english";
121
+ /**
122
+ * Searchable field metadata attached to a Zod schema.
123
+ */
124
+ type SearchableMetadata = Readonly<{
125
+ /**
126
+ * Language for stemming / tokenization.
127
+ * Postgres: passed to `to_tsvector(regconfig, ...)`.
128
+ * SQLite FTS5: tokenizer is fixed at table-create time, so the
129
+ * language is recorded but not applied per-row.
130
+ */
131
+ language: string;
132
+ }>;
133
+ /**
134
+ * Branded Zod string schema. The `SEARCHABLE_FIELD_KEY` marker is
135
+ * attached both as a direct property (fast runtime check) and via Zod's
136
+ * `.meta()` (so it survives `.min()`, `.trim()`, etc.).
137
+ */
138
+ type SearchableSchema = z.ZodString & Readonly<{
139
+ [SEARCHABLE_FIELD_KEY]: SearchableMetadata;
140
+ }>;
141
+ type SearchableOptions = Readonly<{
142
+ language?: string;
143
+ }>;
144
+ /**
145
+ * Creates a Zod string schema tagged as fulltext-searchable.
146
+ *
147
+ * The returned schema passes runtime validation unchanged — the tag only
148
+ * affects how TypeGraph treats the field for indexing and search. Pair
149
+ * with `.optional()` / `.nullable()` / `.min()` / `.trim()` exactly like
150
+ * any other Zod string; `getSearchableMetadata()` finds the metadata
151
+ * through all of them.
152
+ */
153
+ declare function searchable(options?: SearchableOptions): SearchableSchema;
154
+ declare function isSearchableSchema(value: unknown): value is SearchableSchema;
155
+ declare function getSearchableMetadata(schema: z.ZodType): SearchableMetadata | undefined;
156
+
89
157
  /**
90
158
  * Query Compiler Module
91
159
  *
@@ -101,6 +169,14 @@ type CompileQueryOptions = Readonly<{
101
169
  dialect?: SqlDialect | undefined;
102
170
  /** SQL schema configuration for table names. Defaults to standard names. */
103
171
  schema?: SqlSchema | undefined;
172
+ /**
173
+ * Fulltext strategy override. When set, overrides the dialect's
174
+ * default fulltext strategy for `$fulltext.matches()` compilation.
175
+ * Callers typically read this from `backend.fulltextStrategy` so a
176
+ * backend-declared strategy (e.g. ParadeDB) wins over the dialect
177
+ * default (tsvector).
178
+ */
179
+ fulltextStrategy?: FulltextStrategy | undefined;
104
180
  }>;
105
181
 
106
182
  type FieldTypeInfo = Readonly<{
@@ -111,14 +187,29 @@ type FieldTypeInfo = Readonly<{
111
187
  recordValueType?: FieldTypeInfo | undefined;
112
188
  /** For embedding types: the number of dimensions */
113
189
  dimensions?: number | undefined;
190
+ /** For fulltext-searchable string types: field-level metadata */
191
+ searchable?: SearchableMetadata | undefined;
114
192
  }>;
115
193
  type SchemaIntrospector = Readonly<{
116
194
  getFieldTypeInfo: (kindName: string, fieldName: string) => FieldTypeInfo | undefined;
117
195
  getSharedFieldTypeInfo: (kindNames: readonly string[], fieldName: string) => FieldTypeInfo | undefined;
118
196
  getEdgeFieldTypeInfo: (edgeKindName: string, fieldName: string) => FieldTypeInfo | undefined;
119
197
  getSharedEdgeFieldTypeInfo: (edgeKindNames: readonly string[], fieldName: string) => FieldTypeInfo | undefined;
198
+ /**
199
+ * True iff every kind in `kindNames` has at least one `searchable()`
200
+ * field. For polymorphic aliases, `$fulltext` is available only when
201
+ * every resolved kind has searchable content — otherwise `.matches()`
202
+ * would silently miss some kinds.
203
+ */
204
+ hasSearchableField: (kindNames: readonly string[]) => boolean;
120
205
  }>;
121
206
 
207
+ /**
208
+ * Predicate builders for TypeGraph queries.
209
+ *
210
+ * Provides a fluent API for building type-safe predicates.
211
+ */
212
+
122
213
  /**
123
214
  * A chainable predicate that can be combined with AND/OR.
124
215
  */
@@ -151,6 +242,34 @@ declare function param(name: string): ParameterRef;
151
242
  * Type guard for ParameterRef values.
152
243
  */
153
244
  declare function isParameterRef(value: unknown): value is ParameterRef;
245
+ /**
246
+ * Options for the `$fulltext.matches()` predicate.
247
+ *
248
+ * Note the term glossary used throughout TypeGraph search:
249
+ * - **`k`** (the positional argument on `.matches(query, k)`) is the
250
+ * per-predicate top-k cap applied inside the fulltext CTE.
251
+ * - **`limit`** (on store-level `search.fulltext` / `search.hybrid` and
252
+ * on the query builder) is the final result count returned to the
253
+ * caller after any fusion.
254
+ */
255
+ type MatchesOptions = Readonly<{
256
+ /**
257
+ * Parse mode for the query string. Default: "websearch".
258
+ *
259
+ * - "websearch": Google-style syntax (quoted phrases, `-excluded`, `OR`).
260
+ * Postgres: `websearch_to_tsquery`. SQLite: translated to FTS5 MATCH.
261
+ * - "phrase": treats the whole query as a phrase.
262
+ * - "plain": splits on whitespace and ANDs terms.
263
+ * - "raw": dialect-native syntax passed through unchanged.
264
+ */
265
+ mode?: FulltextQueryMode;
266
+ /**
267
+ * Language override. Default: per-row language as stored at insert time.
268
+ */
269
+ language?: string;
270
+ /** Minimum relevance score to include. */
271
+ minScore?: number;
272
+ }>;
154
273
  /**
155
274
  * Options for the similarTo method.
156
275
  */
@@ -174,6 +293,17 @@ type FieldRefOptions = Readonly<{
174
293
  elementType?: ValueType | undefined;
175
294
  }>;
176
295
  declare function fieldRef(alias: string, path: readonly string[], options?: FieldRefOptions): FieldRef;
296
+ /**
297
+ * Node-level fulltext accessor. Always present on the `NodeAccessor`
298
+ * type — a runtime check throws a clear error if the node kind has no
299
+ * `searchable()` fields. `k` is the top-k cap applied inside the
300
+ * fulltext CTE; defaults to `DEFAULT_FULLTEXT_MATCH_K`. Use a larger
301
+ * value when feeding the result into RRF (`.fuseWith()` /
302
+ * `store.search.hybrid()`).
303
+ */
304
+ type FulltextAccessor = Readonly<{
305
+ matches: (query: string, k?: number, options?: MatchesOptions) => Predicate;
306
+ }>;
177
307
  /**
178
308
  * Creates an EXISTS subquery predicate.
179
309
  * Returns true if the subquery returns at least one row.
@@ -421,10 +551,15 @@ type ObjectFieldAccessor<T> = BaseFieldAccessor & Readonly<{
421
551
  * Properties are available at the top level for ergonomic access:
422
552
  * - `n.name` instead of `n.props.name`
423
553
  * - System fields: `n.id`, `n.kind`
554
+ * - Fulltext: `n.$fulltext.matches(...)` — throws at query build time if
555
+ * the node kind has no `searchable()` fields. Exposed at the type
556
+ * level on every accessor so refinements like
557
+ * `searchable().min(1)` do not make it disappear.
424
558
  */
425
559
  type NodeAccessor<N extends NodeType> = Readonly<{
426
560
  id: StringFieldAccessor;
427
561
  kind: StringFieldAccessor;
562
+ $fulltext: FulltextAccessor;
428
563
  }> & PropsAccessor<N>;
429
564
  /**
430
565
  * Creates typed field accessors for an edge kind's properties.
@@ -591,6 +726,7 @@ type QueryBuilderState = Readonly<{
591
726
  asOf: string | undefined;
592
727
  groupBy: GroupBySpec | undefined;
593
728
  having: PredicateExpression | undefined;
729
+ fusion: HybridFusionOptions | undefined;
594
730
  }>;
595
731
  /**
596
732
  * Options for creating a query builder.
@@ -2052,6 +2188,30 @@ declare class QueryBuilder<G extends GraphDef, Aliases extends AliasMap = {}, Ed
2052
2188
  * @param predicate - A predicate expression to filter groups
2053
2189
  */
2054
2190
  having(predicate: PredicateExpression): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases>;
2191
+ /**
2192
+ * Sets fusion parameters for hybrid (vector + fulltext) queries.
2193
+ *
2194
+ * Applies only when the query contains both a `.similarTo()` and a
2195
+ * `.$fulltext.matches()` predicate. Without this call, the default is
2196
+ * RRF with k=60 and equal weights. A mismatch between `.fuseWith()`
2197
+ * configuration and the predicates on the query is caught during
2198
+ * compilation, not here.
2199
+ *
2200
+ * @example
2201
+ * ```typescript
2202
+ * store.query()
2203
+ * .from("Document", "d")
2204
+ * .whereNode("d", d =>
2205
+ * d.$fulltext.matches("renewable energy", 50)
2206
+ * .and(d.embedding.similarTo(vec, 50))
2207
+ * .and(d.tenantId.eq(tenant))
2208
+ * )
2209
+ * .fuseWith({ k: 60, weights: { fulltext: 1.5 } })
2210
+ * .limit(10)
2211
+ * .execute();
2212
+ * ```
2213
+ */
2214
+ fuseWith(options: HybridFusionOptions): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases>;
2055
2215
  /**
2056
2216
  * Applies a query fragment to transform this builder.
2057
2217
  *
@@ -2272,6 +2432,217 @@ type GraphAlgorithms<G extends GraphDef> = Readonly<{
2272
2432
  degree: (node: NodeIdentifier, options?: DegreeOptions<G>) => Promise<number>;
2273
2433
  }>;
2274
2434
 
2435
+ type RebuildFulltextOptions = Readonly<{
2436
+ /** Page size. Must be a positive integer. Default: 500. */
2437
+ pageSize?: number;
2438
+ /**
2439
+ * Maximum number of skipped node IDs to include in the `skippedIds`
2440
+ * array. Default: 10,000. Set higher to collect the full list when
2441
+ * investigating systemic corruption; set lower when `processed` is
2442
+ * all you care about. The `skipped` total is always accurate.
2443
+ */
2444
+ maxSkippedIds?: number;
2445
+ }>;
2446
+ type RebuildFulltextResult = Readonly<{
2447
+ /** Node kinds that were rebuilt (those with at least one searchable field). */
2448
+ kinds: readonly string[];
2449
+ /** Total nodes scanned. */
2450
+ processed: number;
2451
+ /** Fulltext upsert operations executed. */
2452
+ upserted: number;
2453
+ /** Fulltext delete operations executed (soft-deleted or all-empty nodes). */
2454
+ cleared: number;
2455
+ /**
2456
+ * Nodes skipped due to corrupt or non-object `props` (not counted in
2457
+ * upserted/cleared).
2458
+ */
2459
+ skipped: number;
2460
+ /**
2461
+ * IDs of skipped nodes, capped at 10,000 entries so pathological
2462
+ * corruption doesn't turn rebuild into an OOM. See `skippedTruncated`
2463
+ * to tell whether the cap was hit. Empty when `skipped === 0`.
2464
+ */
2465
+ skippedIds: readonly string[];
2466
+ /** True when `skipped > skippedIds.length` (the cap was reached). */
2467
+ skippedTruncated: boolean;
2468
+ }>;
2469
+
2470
+ /**
2471
+ * Store-level search helpers.
2472
+ *
2473
+ * `fulltextSearch` is a thin typed wrapper over `backend.fulltextSearch`.
2474
+ * `hybridSearch` runs a vector and a fulltext query in parallel and
2475
+ * fuses the two ranked lists with Reciprocal Rank Fusion. RRF is
2476
+ * rank-based, so it papers over score-scale differences between
2477
+ * pgvector/sqlite-vec (distance-derived) and tsvector/FTS5 (BM25-style).
2478
+ */
2479
+
2480
+ /**
2481
+ * A fulltext search hit. `N` defaults to the generic `Node`; the
2482
+ * `store.search.fulltext<K>(kind)` facade narrows it to the concrete
2483
+ * typed node for that kind so callers get `hit.node.title` without a cast.
2484
+ */
2485
+ type FulltextSearchHit<N = Node> = Readonly<{
2486
+ node: N;
2487
+ /** Backend-native relevance score; higher is better. */
2488
+ score: number;
2489
+ /** 1-based rank within the result set. */
2490
+ rank: number;
2491
+ /** Highlighted snippet (only present when `includeSnippets: true`). */
2492
+ snippet?: string;
2493
+ }>;
2494
+ type VectorSearchHit<N = Node> = Readonly<{
2495
+ node: N;
2496
+ score: number;
2497
+ rank: number;
2498
+ }>;
2499
+ /**
2500
+ * A hybrid search hit. Both sub-results (`vector`, `fulltext`) carry the
2501
+ * same narrowed node type as the top-level `node` for ergonomic access.
2502
+ */
2503
+ type HybridSearchHit<N = Node> = Readonly<{
2504
+ node: N;
2505
+ /** Fused RRF score (higher is better). */
2506
+ score: number;
2507
+ /** 1-based rank in the fused list. */
2508
+ rank: number;
2509
+ /** Sub-result from the vector half, if it ranked this node. */
2510
+ vector?: VectorSearchHit<N>;
2511
+ /** Sub-result from the fulltext half, if it ranked this node. */
2512
+ fulltext?: FulltextSearchHit<N>;
2513
+ }>;
2514
+ type FulltextSearchOptions = Readonly<{
2515
+ /** The user-supplied query string. */
2516
+ query: string;
2517
+ /** Max results. Required. */
2518
+ limit: number;
2519
+ /** Query parser mode. Default: "websearch". */
2520
+ mode?: FulltextQueryMode;
2521
+ /**
2522
+ * Language override for query parsing. Default: per-row language as
2523
+ * stored at insert time.
2524
+ */
2525
+ language?: string;
2526
+ /** Minimum relevance score to include in results. */
2527
+ minScore?: number;
2528
+ /** Return a highlighted snippet alongside each hit. */
2529
+ includeSnippets?: boolean;
2530
+ }>;
2531
+ type HybridVectorOptions = Readonly<{
2532
+ /** Field path of the embedding column on the node kind. */
2533
+ fieldPath: string;
2534
+ /** Query embedding to compare against. */
2535
+ queryEmbedding: readonly number[];
2536
+ /** Distance metric. Default: "cosine". */
2537
+ metric?: VectorMetric;
2538
+ /** How many candidates to retrieve from the vector side. Default: 4 * limit. */
2539
+ k?: number;
2540
+ /** Minimum similarity to include (units depend on metric). */
2541
+ minScore?: number;
2542
+ }>;
2543
+ type HybridFulltextOptions = Readonly<{
2544
+ query: string;
2545
+ /** How many candidates to retrieve from the fulltext side. Default: 4 * limit. */
2546
+ k?: number;
2547
+ mode?: FulltextQueryMode;
2548
+ language?: string;
2549
+ minScore?: number;
2550
+ includeSnippets?: boolean;
2551
+ }>;
2552
+ type HybridSearchOptions = Readonly<{
2553
+ vector: HybridVectorOptions;
2554
+ fulltext: HybridFulltextOptions;
2555
+ fusion?: HybridFusionOptions;
2556
+ /** Final number of fused results to return. Required. */
2557
+ limit: number;
2558
+ }>;
2559
+
2560
+ /**
2561
+ * StoreSearch — store.search facade.
2562
+ *
2563
+ * Groups fulltext, hybrid, and maintenance operations under one
2564
+ * namespace so the top-level Store API stays focused on CRUD + graph
2565
+ * traversal. The three methods delegate to their respective execution
2566
+ * modules; this class exists purely to shape the surface, not to add
2567
+ * behavior.
2568
+ */
2569
+
2570
+ /**
2571
+ * Narrows `Node` to the concrete typed node for a given kind in the graph.
2572
+ */
2573
+ type NodeOfKind<G extends GraphDef, K extends NodeKinds<G>> = G["nodes"][K] extends NodeRegistration<infer N extends NodeType> ? Node<N> : Node;
2574
+ type StoreSearchContext = Readonly<{
2575
+ graphId: string;
2576
+ backend: GraphBackend;
2577
+ registry: KindRegistry;
2578
+ }>;
2579
+ /**
2580
+ * Search-related operations exposed via `store.search`.
2581
+ *
2582
+ * @example
2583
+ * ```typescript
2584
+ * // Fulltext only
2585
+ * const hits = await store.search.fulltext("Document", {
2586
+ * query: "climate change",
2587
+ * limit: 10,
2588
+ * includeSnippets: true,
2589
+ * });
2590
+ *
2591
+ * // Hybrid: vector + fulltext, fused with RRF
2592
+ * const ranked = await store.search.hybrid("Document", {
2593
+ * limit: 10,
2594
+ * vector: { fieldPath: "embedding", queryEmbedding: vec },
2595
+ * fulltext: { query: "climate change" },
2596
+ * });
2597
+ *
2598
+ * // Rebuild after backfill / schema change
2599
+ * const stats = await store.search.rebuildFulltext();
2600
+ * ```
2601
+ */
2602
+ declare class StoreSearch<G extends GraphDef> {
2603
+ #private;
2604
+ constructor(context: StoreSearchContext);
2605
+ /**
2606
+ * Runs a fulltext search against nodes of the given kind.
2607
+ *
2608
+ * Requires fields on the node schema declared with `searchable()`.
2609
+ * The search hits the backend's fulltext index (tsvector + GIN on
2610
+ * Postgres, FTS5 on SQLite — or whatever strategy the backend is
2611
+ * configured with) and resolves the matching node IDs back to typed
2612
+ * `Node` objects.
2613
+ */
2614
+ fulltext<K extends NodeKinds<G>>(nodeKind: K, options: FulltextSearchOptions): Promise<readonly FulltextSearchHit<NodeOfKind<G, K>>[]>;
2615
+ /**
2616
+ * Runs a vector + fulltext hybrid search and fuses the results with
2617
+ * Reciprocal Rank Fusion.
2618
+ *
2619
+ * RRF is rank-based, so it composes well across heterogeneous score
2620
+ * scales (cosine similarity vs ts_rank_cd vs FTS5 BM25). Default
2621
+ * over-fetch is 4× `limit` from each source — tune via `vector.k` /
2622
+ * `fulltext.k` for higher-recall corpora.
2623
+ */
2624
+ hybrid<K extends NodeKinds<G>>(nodeKind: K, options: HybridSearchOptions): Promise<readonly HybridSearchHit<NodeOfKind<G, K>>[]>;
2625
+ /**
2626
+ * Rebuilds the fulltext index from existing node data.
2627
+ *
2628
+ * Use when:
2629
+ * - A node kind gained a `searchable()` field after data was already
2630
+ * written and existing rows were never indexed.
2631
+ * - The fulltext table was dropped / truncated.
2632
+ * - `language` was changed on a `searchable()` field.
2633
+ *
2634
+ * Iterates nodes with keyset pagination (stable under shared
2635
+ * timestamps and light concurrent writes), transacts per page, skips
2636
+ * kinds with no searchable fields, and cleans up stale rows for
2637
+ * soft-deleted nodes. Corrupt or non-object `props` are counted in
2638
+ * `skipped` with their node IDs surfaced via `skippedIds` so operators
2639
+ * can investigate. Concurrent hard-deletes between page fetches can
2640
+ * be missed by a single pass — run during a maintenance window for
2641
+ * full consistency.
2642
+ */
2643
+ rebuildFulltext<K extends NodeKinds<G>>(nodeKind?: K, options?: RebuildFulltextOptions): Promise<RebuildFulltextResult>;
2644
+ }
2645
+
2275
2646
  /**
2276
2647
  * Main Store implementation for TypeGraph.
2277
2648
  *
@@ -2411,6 +2782,34 @@ declare class Store<G extends GraphDef> {
2411
2782
  * ```
2412
2783
  */
2413
2784
  query(): QueryBuilder<G>;
2785
+ /**
2786
+ * Search-related operations (fulltext, hybrid, and rebuild).
2787
+ *
2788
+ * All search methods live under this facade to keep the top-level
2789
+ * Store API focused on CRUD + graph traversal. The facade is
2790
+ * lazy-initialized and cached for the lifetime of the store.
2791
+ *
2792
+ * @example
2793
+ * ```typescript
2794
+ * // Fulltext
2795
+ * const hits = await store.search.fulltext("Document", {
2796
+ * query: "climate warming",
2797
+ * limit: 10,
2798
+ * includeSnippets: true,
2799
+ * });
2800
+ *
2801
+ * // Hybrid (vector + fulltext fused with RRF)
2802
+ * const ranked = await store.search.hybrid("Document", {
2803
+ * limit: 10,
2804
+ * vector: { fieldPath: "embedding", queryEmbedding: vec },
2805
+ * fulltext: { query: "climate warming" },
2806
+ * });
2807
+ *
2808
+ * // Rebuild index after schema change
2809
+ * const stats = await store.search.rebuildFulltext();
2810
+ * ```
2811
+ */
2812
+ get search(): StoreSearch<G>;
2414
2813
  /**
2415
2814
  * Executes multiple queries over a single connection with snapshot consistency.
2416
2815
  *
@@ -2574,4 +2973,4 @@ declare function createStore<G extends GraphDef>(graph: G, backend: GraphBackend
2574
2973
  */
2575
2974
  declare function createStoreWithSchema<G extends GraphDef>(graph: G, backend: GraphBackend, options?: StoreOptions & SchemaManagerOptions): Promise<[Store<G>, SchemaValidationResult]>;
2576
2975
 
2577
- export { type ReachableNode as $, type AliasMap as A, type BaseTraversalOptions as B, type CreateQueryBuilderOptions as C, type DegreeOptions as D, type EdgeAliasMap as E, type FieldAccessor as F, type GetOrCreateAction as G, type HookContext as H, type IfExistsMode as I, type NodeAlias as J, type NodeCollection as K, type NodeGetOrCreateByConstraintOptions as L, type NodeGetOrCreateByConstraintResult as M, type NeighborsOptions as N, type NodeIdentifier as O, type NodeRef as P, QueryBuilder as Q, type OperationHookContext as R, type PaginateOptions as S, TraversalBuilder as T, type PaginatedResult as U, type PathNode as V, type Predicate as W, PreparedQuery as X, type PropsAccessor as Y, type QueryHookContext as Z, type QueryOptions as _, type AggregateResult as a, type ReachableOptions as a0, type RecursiveTraversalOptions as a1, type SelectContext as a2, type SelectableEdge as a3, type SelectableNode as a4, type ShortestPathOptions as a5, type ShortestPathResult as a6, Store as a7, type StoreHooks as a8, type StoreOptions as a9, notInSubquery as aA, param as aB, type StoreProjection as aa, type StreamOptions as ab, type SubgraphEdgeResult as ac, type SubgraphNodeResult as ad, type SubgraphOptions as ae, type SubgraphResult as af, type SubsetEdge as ag, type SubsetNode as ah, type TemporalAlgorithmOptions as ai, type TransactionContext as aj, type TraversalDirection as ak, type TypedEdgeCollection as al, UnionableQuery as am, type UpdateEdgeInput as an, type UpdateNodeInput as ao, createStore as ap, createStoreWithSchema as aq, defineSubgraphProject as ar, embedding as as, exists as at, fieldRef as au, getEmbeddingDimensions as av, inSubquery as aw, isEmbeddingSchema as ax, isParameterRef as ay, notExists as az, type AlgorithmCyclePolicy as b, type AnyEdge as c, type AnyNode as d, type BatchResults as e, type BatchableQuery as f, type ConstraintNames as g, type CreateEdgeInput as h, type CreateNodeInput as i, type DynamicEdgeCollection as j, type DynamicNodeCollection as k, type Edge as l, type EdgeAccessor as m, type EdgeCollection as n, type EdgeFindByEndpointsOptions as o, type EdgeGetOrCreateByEndpointsOptions as p, type EdgeGetOrCreateByEndpointsResult as q, type EmbeddingSchema as r, type EmbeddingValue as s, ExecutableAggregateQuery as t, ExecutableQuery as u, type GraphAlgorithms as v, type GraphEdgeCollections as w, type GraphNodeCollections as x, type Node as y, type NodeAccessor as z };
2976
+ export { type PaginatedResult as $, type AliasMap as A, type BaseTraversalOptions as B, type CreateQueryBuilderOptions as C, DEFAULT_SEARCHABLE_LANGUAGE as D, type EdgeAliasMap as E, type FieldAccessor as F, type GetOrCreateAction as G, type GraphNodeCollections as H, type HookContext as I, type HybridFulltextOptions as J, type HybridSearchHit as K, type HybridSearchOptions as L, type HybridVectorOptions as M, type IfExistsMode as N, type NeighborsOptions as O, type Node as P, QueryBuilder as Q, type NodeAccessor as R, type NodeAlias as S, TraversalBuilder as T, type NodeCollection as U, type NodeGetOrCreateByConstraintOptions as V, type NodeGetOrCreateByConstraintResult as W, type NodeIdentifier as X, type NodeRef as Y, type OperationHookContext as Z, type PaginateOptions as _, type AggregateResult as a, type PathNode as a0, type Predicate as a1, PreparedQuery as a2, type PropsAccessor as a3, type QueryHookContext as a4, type QueryOptions as a5, type ReachableNode as a6, type ReachableOptions as a7, type RebuildFulltextOptions as a8, type RebuildFulltextResult as a9, type UpdateEdgeInput as aA, type UpdateNodeInput as aB, type VectorSearchHit as aC, createStore as aD, createStoreWithSchema as aE, defineSubgraphProject as aF, embedding as aG, exists as aH, fieldRef as aI, getEmbeddingDimensions as aJ, getSearchableMetadata as aK, inSubquery as aL, isEmbeddingSchema as aM, isParameterRef as aN, isSearchableSchema as aO, notExists as aP, notInSubquery as aQ, param as aR, searchable as aS, type RecursiveTraversalOptions as aa, type SearchableMetadata as ab, type SearchableOptions as ac, type SearchableSchema as ad, type SelectContext as ae, type SelectableEdge as af, type SelectableNode as ag, type ShortestPathOptions as ah, type ShortestPathResult as ai, Store as aj, type StoreHooks as ak, type StoreOptions as al, type StoreProjection as am, StoreSearch as an, type StreamOptions as ao, type SubgraphEdgeResult as ap, type SubgraphNodeResult as aq, type SubgraphOptions as ar, type SubgraphResult as as, type SubsetEdge as at, type SubsetNode as au, type TemporalAlgorithmOptions as av, type TransactionContext as aw, type TraversalDirection as ax, type TypedEdgeCollection as ay, UnionableQuery as az, type AlgorithmCyclePolicy as b, type AnyEdge as c, type AnyNode as d, type BatchResults as e, type BatchableQuery as f, type ConstraintNames as g, type CreateEdgeInput as h, type CreateNodeInput as i, type DegreeOptions as j, type DynamicEdgeCollection as k, type DynamicNodeCollection as l, type Edge as m, type EdgeAccessor as n, type EdgeCollection as o, type EdgeFindByEndpointsOptions as p, type EdgeGetOrCreateByEndpointsOptions as q, type EdgeGetOrCreateByEndpointsResult as r, type EmbeddingSchema as s, type EmbeddingValue as t, ExecutableAggregateQuery as u, ExecutableQuery as v, type FulltextSearchHit as w, type FulltextSearchOptions as x, type GraphAlgorithms as y, type GraphEdgeCollections as z };