@nicia-ai/typegraph 0.2.0 → 0.3.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 (146) hide show
  1. package/README.md +23 -0
  2. package/dist/{ast-BVyihVbP.d.cts → ast-CXFx6bF6.d.ts} +45 -165
  3. package/dist/{ast-BVyihVbP.d.ts → ast-D-3bOanX.d.cts} +45 -165
  4. package/dist/backend/drizzle/index.cjs +20 -20
  5. package/dist/backend/drizzle/index.d.cts +6 -5
  6. package/dist/backend/drizzle/index.d.ts +6 -5
  7. package/dist/backend/drizzle/index.js +8 -8
  8. package/dist/backend/drizzle/postgres.cjs +9 -9
  9. package/dist/backend/drizzle/postgres.d.cts +9 -11
  10. package/dist/backend/drizzle/postgres.d.ts +9 -11
  11. package/dist/backend/drizzle/postgres.js +6 -6
  12. package/dist/backend/drizzle/schema/postgres.cjs +10 -10
  13. package/dist/backend/drizzle/schema/postgres.d.cts +6 -5
  14. package/dist/backend/drizzle/schema/postgres.d.ts +6 -5
  15. package/dist/backend/drizzle/schema/postgres.js +3 -3
  16. package/dist/backend/drizzle/schema/sqlite.cjs +10 -10
  17. package/dist/backend/drizzle/schema/sqlite.d.cts +6 -5
  18. package/dist/backend/drizzle/schema/sqlite.d.ts +6 -5
  19. package/dist/backend/drizzle/schema/sqlite.js +3 -3
  20. package/dist/backend/drizzle/sqlite.cjs +9 -9
  21. package/dist/backend/drizzle/sqlite.d.cts +17 -18
  22. package/dist/backend/drizzle/sqlite.d.ts +17 -18
  23. package/dist/backend/drizzle/sqlite.js +6 -6
  24. package/dist/backend/postgres/index.cjs +21 -17
  25. package/dist/backend/postgres/index.d.cts +8 -7
  26. package/dist/backend/postgres/index.d.ts +8 -7
  27. package/dist/backend/postgres/index.js +8 -8
  28. package/dist/backend/sqlite/index.cjs +27 -21
  29. package/dist/backend/sqlite/index.cjs.map +1 -1
  30. package/dist/backend/sqlite/index.d.cts +7 -6
  31. package/dist/backend/sqlite/index.d.ts +7 -6
  32. package/dist/backend/sqlite/index.js +18 -12
  33. package/dist/backend/sqlite/index.js.map +1 -1
  34. package/dist/{chunk-YM5AL65Y.cjs → chunk-2WVFEIHR.cjs} +29 -3
  35. package/dist/chunk-2WVFEIHR.cjs.map +1 -0
  36. package/dist/{chunk-4PIEL2VO.js → chunk-3PURVEA4.js} +36 -5
  37. package/dist/chunk-3PURVEA4.js.map +1 -0
  38. package/dist/chunk-4HARSV2G.js +1448 -0
  39. package/dist/chunk-4HARSV2G.js.map +1 -0
  40. package/dist/{chunk-IIAT36MI.js → chunk-54WJF3DW.js} +29 -3
  41. package/dist/chunk-54WJF3DW.js.map +1 -0
  42. package/dist/chunk-CMHFS34N.cjs +390 -0
  43. package/dist/chunk-CMHFS34N.cjs.map +1 -0
  44. package/dist/chunk-DD6ONEBN.cjs +1264 -0
  45. package/dist/chunk-DD6ONEBN.cjs.map +1 -0
  46. package/dist/chunk-F2BZSEFE.js +388 -0
  47. package/dist/chunk-F2BZSEFE.js.map +1 -0
  48. package/dist/{chunk-DDM2FZRJ.cjs → chunk-JQDWEX6V.cjs} +24 -24
  49. package/dist/{chunk-DDM2FZRJ.cjs.map → chunk-JQDWEX6V.cjs.map} +1 -1
  50. package/dist/chunk-NP4G4ZKM.js +1228 -0
  51. package/dist/chunk-NP4G4ZKM.js.map +1 -0
  52. package/dist/{chunk-UJAGXJDG.cjs → chunk-NU2XNMVI.cjs} +38 -7
  53. package/dist/chunk-NU2XNMVI.cjs.map +1 -0
  54. package/dist/{chunk-JKTO7TW3.js → chunk-O5XPCJLF.js} +25 -3
  55. package/dist/chunk-O5XPCJLF.js.map +1 -0
  56. package/dist/{chunk-2QHQ2C4P.js → chunk-OGGLFYFA.js} +36 -5
  57. package/dist/chunk-OGGLFYFA.js.map +1 -0
  58. package/dist/{chunk-SV5H3XM5.cjs → chunk-OYL2SGBD.cjs} +26 -2
  59. package/dist/chunk-OYL2SGBD.cjs.map +1 -0
  60. package/dist/chunk-SFY2PPOY.cjs +1469 -0
  61. package/dist/chunk-SFY2PPOY.cjs.map +1 -0
  62. package/dist/{chunk-JDAET5LO.js → chunk-SMLIWLS7.js} +9 -9
  63. package/dist/chunk-SMLIWLS7.js.map +1 -0
  64. package/dist/{chunk-VXRVGFCI.js → chunk-U3452TEU.js} +17 -17
  65. package/dist/{chunk-VXRVGFCI.js.map → chunk-U3452TEU.js.map} +1 -1
  66. package/dist/{chunk-MNO33ASC.cjs → chunk-UYMT4LO2.cjs} +9 -8
  67. package/dist/chunk-UYMT4LO2.cjs.map +1 -0
  68. package/dist/chunk-V7CS2MDB.cjs +289 -0
  69. package/dist/chunk-V7CS2MDB.cjs.map +1 -0
  70. package/dist/chunk-WE5BKYNB.js +287 -0
  71. package/dist/chunk-WE5BKYNB.js.map +1 -0
  72. package/dist/{chunk-L642L24T.js → chunk-XDTYTNYL.js} +14 -21
  73. package/dist/chunk-XDTYTNYL.js.map +1 -0
  74. package/dist/{chunk-N4AOJ3VF.cjs → chunk-XZL6MCZJ.cjs} +38 -7
  75. package/dist/chunk-XZL6MCZJ.cjs.map +1 -0
  76. package/dist/{chunk-DBFCKELK.cjs → chunk-ZJHQZZT2.cjs} +18 -27
  77. package/dist/chunk-ZJHQZZT2.cjs.map +1 -0
  78. package/dist/index-Dkicw49A.d.cts +373 -0
  79. package/dist/index-Dkicw49A.d.ts +373 -0
  80. package/dist/index.cjs +5915 -3377
  81. package/dist/index.cjs.map +1 -1
  82. package/dist/index.d.cts +24 -664
  83. package/dist/index.d.ts +24 -664
  84. package/dist/index.js +5781 -3223
  85. package/dist/index.js.map +1 -1
  86. package/dist/indexes/index.cjs +16 -16
  87. package/dist/indexes/index.d.cts +5 -4
  88. package/dist/indexes/index.d.ts +5 -4
  89. package/dist/indexes/index.js +2 -2
  90. package/dist/interchange/index.cjs +9 -9
  91. package/dist/interchange/index.cjs.map +1 -1
  92. package/dist/interchange/index.d.cts +5 -3
  93. package/dist/interchange/index.d.ts +5 -3
  94. package/dist/interchange/index.js +8 -8
  95. package/dist/interchange/index.js.map +1 -1
  96. package/dist/manager-Jc5Btay9.d.cts +493 -0
  97. package/dist/manager-e9LXthrx.d.ts +493 -0
  98. package/dist/profiler/index.cjs +2 -8
  99. package/dist/profiler/index.cjs.map +1 -1
  100. package/dist/profiler/index.d.cts +9 -161
  101. package/dist/profiler/index.d.ts +9 -161
  102. package/dist/profiler/index.js +4 -4
  103. package/dist/profiler/index.js.map +1 -1
  104. package/dist/schema/index.cjs +145 -0
  105. package/dist/schema/index.cjs.map +1 -0
  106. package/dist/schema/index.d.cts +237 -0
  107. package/dist/schema/index.d.ts +237 -0
  108. package/dist/schema/index.js +72 -0
  109. package/dist/schema/index.js.map +1 -0
  110. package/dist/{store-BPhjw5S8.d.ts → store-DM3Tk3Pw.d.ts} +874 -1283
  111. package/dist/{store-DNOOQEm8.d.cts → store-nbBybLWP.d.cts} +874 -1283
  112. package/dist/{test-helpers-BjyRYJZX.d.ts → test-helpers-CIq1Hhj1.d.ts} +5 -1
  113. package/dist/{test-helpers-NoQXhleQ.d.cts → test-helpers-DPRFVky4.d.cts} +5 -1
  114. package/dist/{types-D_3mEv2y.d.ts → types-BL1GyVku.d.cts} +2 -2
  115. package/dist/{types-DsRfx0yk.d.ts → types-Cdbi4hcx.d.ts} +228 -4
  116. package/dist/{types-BrSfFSpW.d.cts → types-DCGa53O2.d.ts} +2 -2
  117. package/dist/{types-aapj0GLz.d.cts → types-DDP0MGBF.d.cts} +228 -4
  118. package/dist/{types-CX4cLd7M.d.ts → types-DHRsi6j9.d.cts} +4 -3
  119. package/dist/types-DTJEu_-h.d.cts +158 -0
  120. package/dist/types-DTJEu_-h.d.ts +158 -0
  121. package/dist/{types-a5rAxC92.d.cts → types-ZT5mlism.d.ts} +4 -3
  122. package/package.json +17 -2
  123. package/dist/chunk-2FURVVAX.cjs +0 -350
  124. package/dist/chunk-2FURVVAX.cjs.map +0 -1
  125. package/dist/chunk-2QHQ2C4P.js.map +0 -1
  126. package/dist/chunk-4PIEL2VO.js.map +0 -1
  127. package/dist/chunk-DBFCKELK.cjs.map +0 -1
  128. package/dist/chunk-H7THXVH6.cjs +0 -314
  129. package/dist/chunk-H7THXVH6.cjs.map +0 -1
  130. package/dist/chunk-HXAPXPZH.cjs +0 -680
  131. package/dist/chunk-HXAPXPZH.cjs.map +0 -1
  132. package/dist/chunk-IIAT36MI.js.map +0 -1
  133. package/dist/chunk-JDAET5LO.js.map +0 -1
  134. package/dist/chunk-JKTO7TW3.js.map +0 -1
  135. package/dist/chunk-L642L24T.js.map +0 -1
  136. package/dist/chunk-MNO33ASC.cjs.map +0 -1
  137. package/dist/chunk-N4AOJ3VF.cjs.map +0 -1
  138. package/dist/chunk-QB3WBMDT.js +0 -646
  139. package/dist/chunk-QB3WBMDT.js.map +0 -1
  140. package/dist/chunk-SV5H3XM5.cjs.map +0 -1
  141. package/dist/chunk-UJAGXJDG.cjs.map +0 -1
  142. package/dist/chunk-X4EVMBON.js +0 -312
  143. package/dist/chunk-X4EVMBON.js.map +0 -1
  144. package/dist/chunk-XZL4NLV6.js +0 -348
  145. package/dist/chunk-XZL4NLV6.js.map +0 -1
  146. package/dist/chunk-YM5AL65Y.cjs.map +0 -1
@@ -1,59 +1,11 @@
1
- import { S as SqlSchema, G as GraphBackend, b as SchemaVersionRow } from './types-DsRfx0yk.js';
2
- import { N as NodeType, E as EdgeType, p as NodeRegistration, h as EdgeRegistration, D as DeleteBehavior, T as TemporalMode, i as EdgeTypeWithEndpoints, G as GraphDefaults, A as AnyEdgeType, n as NodeId, V as ValueType, P as PredicateExpression, z as VectorMetricType, Q as QueryAst, J as JsonPointer, F as FieldRef, k as JsonPointerInput, B as Traversal, H as NodePredicate, I as ProjectedField, O as OrderSpec, K as GroupBySpec, L as TraversalDirection, c as AggregateExpr, W as ComposableQuery, X as SetOperationType, Y as SetOperation, S as SortDirection, r as UniquenessScope, f as Collation, e as Cardinality, j as EndpointExistence } from './ast-BVyihVbP.js';
1
+ import { k as SqlSchema, G as GraphBackend } from './types-Cdbi4hcx.js';
2
+ import { K as KindRegistry, G as GraphDef, b as SchemaManagerOptions, c as SchemaValidationResult } from './manager-e9LXthrx.js';
3
+ import { N as NodeType, A as AnyEdgeType, T as TemporalMode, E as EdgeType, b as EdgeRegistration, g as NodeId } from './types-DTJEu_-h.js';
4
+ import { V as ValueType, i as PredicateExpression, k as VectorMetricType, Q as QueryAst, J as JsonPointer, F as FieldRef, P as ParameterRef, d 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, A as AggregateExpr, r as SelectiveField, s as ComposableQuery, t as SetOperationType, u as SetOperation, S as SortDirection } from './ast-CXFx6bF6.js';
3
5
  import { SQL } from 'drizzle-orm';
4
6
  import { z } from 'zod';
5
7
  import { S as SqlDialect } from './types-BRzHlhKC.js';
6
8
 
7
- /** Brand key for MetaEdge */
8
- declare const META_EDGE_BRAND: "__metaEdge";
9
- /**
10
- * How a meta-edge affects queries and validation.
11
- */
12
- type InferenceType = "subsumption" | "hierarchy" | "substitution" | "constraint" | "composition" | "association" | "none";
13
- /**
14
- * Properties of a meta-edge.
15
- */
16
- type MetaEdgeProperties = Readonly<{
17
- transitive: boolean;
18
- symmetric: boolean;
19
- reflexive: boolean;
20
- inverse: string | undefined;
21
- inference: InferenceType;
22
- description: string | undefined;
23
- }>;
24
- /**
25
- * A meta-edge definition.
26
- *
27
- * Meta-edges represent type-level relationships (between kinds),
28
- * not instance-level relationships (between nodes).
29
- */
30
- type MetaEdge<K extends string = string> = Readonly<{
31
- [META_EDGE_BRAND]: true;
32
- name: K;
33
- properties: MetaEdgeProperties;
34
- }>;
35
- /**
36
- * A relation in the ontology (instance of meta-edge between types).
37
- *
38
- * @example
39
- * ```typescript
40
- * // Podcast subClassOf Media
41
- * subClassOf(Podcast, Media)
42
- *
43
- * // Person equivalentTo schema:Person
44
- * equivalentTo(Person, "https://schema.org/Person")
45
- * ```
46
- */
47
- type OntologyRelation = Readonly<{
48
- metaEdge: MetaEdge;
49
- from: NodeType | EdgeType | string;
50
- to: NodeType | EdgeType | string;
51
- }>;
52
- /**
53
- * Checks if a value is a MetaEdge.
54
- */
55
- declare function isMetaEdge(value: unknown): value is MetaEdge;
56
-
57
9
  /**
58
10
  * Embedding type for vector search.
59
11
  *
@@ -135,479 +87,711 @@ declare function isEmbeddingSchema(value: unknown): value is EmbeddingSchema;
135
87
  */
136
88
  declare function getEmbeddingDimensions(schema: z.ZodType): number | undefined;
137
89
 
138
- /** Brand key for GraphDef */
139
- declare const GRAPH_DEF_BRAND: "__graphDef";
140
90
  /**
141
- * An edge entry in the graph definition.
142
- * Can be:
143
- * - EdgeType directly (if it has from/to defined)
144
- * - EdgeRegistration object (always works, can override/narrow defaults)
145
- */
146
- type EdgeEntry = EdgeRegistration | EdgeTypeWithEndpoints;
147
- /**
148
- * Normalized edge map type - all entries become EdgeRegistration.
91
+ * Query Compiler Module
92
+ *
93
+ * Main entry point for compiling query ASTs to SQL.
94
+ * Re-exports individual compiler modules and provides the main compile functions.
149
95
  */
150
- type NormalizedEdges<TEdges extends Record<string, EdgeEntry>> = {
151
- [K in keyof TEdges]: TEdges[K] extends EdgeRegistration ? TEdges[K] : TEdges[K] extends EdgeTypeWithEndpoints ? EdgeRegistration<TEdges[K], TEdges[K]["from"][number], TEdges[K]["to"][number]> : never;
152
- };
96
+
153
97
  /**
154
- * Configuration for defineGraph.
98
+ * Options for query compilation.
155
99
  */
156
- type GraphDefConfig<TNodes extends Record<string, NodeRegistration>, TEdges extends Record<string, EdgeEntry>, TOntology extends readonly OntologyRelation[]> = Readonly<{
157
- /** Unique identifier for this graph */
158
- id: string;
159
- /** Node registrations */
160
- nodes: TNodes;
161
- /** Edge registrations or EdgeTypes with built-in domain/range */
162
- edges: TEdges;
163
- /** Ontology relations */
164
- ontology?: TOntology;
165
- /** Graph-wide defaults */
166
- defaults?: GraphDefaults;
100
+ type CompileQueryOptions = Readonly<{
101
+ /** SQL dialect ("sqlite" or "postgres"). Defaults to "sqlite". */
102
+ dialect?: SqlDialect | undefined;
103
+ /** SQL schema configuration for table names. Defaults to standard names. */
104
+ schema?: SqlSchema | undefined;
167
105
  }>;
168
- /**
169
- * A graph definition.
170
- *
171
- * This is a compile-time artifact that describes the structure of a graph.
172
- * Use `createStore()` to create a runtime store from this definition.
173
- */
174
- type GraphDef<TNodes extends Record<string, NodeRegistration> = Record<string, NodeRegistration>, TEdges extends Record<string, EdgeRegistration> = Record<string, EdgeRegistration>, TOntology extends readonly OntologyRelation[] = readonly OntologyRelation[]> = Readonly<{
175
- [GRAPH_DEF_BRAND]: true;
176
- id: string;
177
- nodes: TNodes;
178
- edges: TEdges;
179
- ontology: TOntology;
180
- defaults: Readonly<{
181
- onNodeDelete: DeleteBehavior;
182
- temporalMode: TemporalMode;
183
- }>;
106
+
107
+ type FieldTypeInfo = Readonly<{
108
+ valueType: ValueType;
109
+ elementType?: ValueType | undefined;
110
+ elementTypeInfo?: FieldTypeInfo | undefined;
111
+ shape?: Readonly<Record<string, FieldTypeInfo>> | undefined;
112
+ recordValueType?: FieldTypeInfo | undefined;
113
+ /** For embedding types: the number of dimensions */
114
+ dimensions?: number | undefined;
184
115
  }>;
116
+ type SchemaIntrospector = Readonly<{
117
+ getFieldTypeInfo: (kindName: string, fieldName: string) => FieldTypeInfo | undefined;
118
+ getSharedFieldTypeInfo: (kindNames: readonly string[], fieldName: string) => FieldTypeInfo | undefined;
119
+ getEdgeFieldTypeInfo: (edgeKindName: string, fieldName: string) => FieldTypeInfo | undefined;
120
+ getSharedEdgeFieldTypeInfo: (edgeKindNames: readonly string[], fieldName: string) => FieldTypeInfo | undefined;
121
+ }>;
122
+
185
123
  /**
186
- * Extract node type names from a GraphDef.
187
- */
188
- type NodeTypeNames<G extends GraphDef> = keyof G["nodes"] & string;
189
- /**
190
- * Extract edge type names from a GraphDef.
124
+ * A chainable predicate that can be combined with AND/OR.
191
125
  */
192
- type EdgeTypeNames<G extends GraphDef> = keyof G["edges"] & string;
126
+ type Predicate = Readonly<{
127
+ __expr: PredicateExpression;
128
+ and: (other: Predicate) => Predicate;
129
+ or: (other: Predicate) => Predicate;
130
+ not: () => Predicate;
131
+ }>;
193
132
  /**
194
- * Get a NodeType from a GraphDef by name.
133
+ * Creates a named parameter reference for prepared queries.
134
+ *
135
+ * Use with `query.prepare()` to create reusable parameterized queries.
136
+ * Only supported in scalar comparison positions (eq, neq, gt, etc.),
137
+ * string operations, and between bounds. Not supported in `in()`/`notIn()`.
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * const prepared = store.query()
142
+ * .from("Person", "p")
143
+ * .whereNode("p", (p) => p.name.eq(param("name")))
144
+ * .select((ctx) => ctx.p)
145
+ * .prepare();
146
+ *
147
+ * const results = await prepared.execute({ name: "Alice" });
148
+ * ```
195
149
  */
196
- type GetNodeType<G extends GraphDef, K extends NodeTypeNames<G>> = G["nodes"][K]["type"];
150
+ declare function param(name: string): ParameterRef;
197
151
  /**
198
- * Get an EdgeType from a GraphDef by name.
152
+ * Type guard for ParameterRef values.
199
153
  */
200
- type GetEdgeType<G extends GraphDef, K extends EdgeTypeNames<G>> = G["edges"][K]["type"];
154
+ declare function isParameterRef(value: unknown): value is ParameterRef;
201
155
  /**
202
- * Get all NodeTypes from a GraphDef.
156
+ * Options for the similarTo method.
203
157
  */
204
- type AllNodeTypes<G extends GraphDef> = {
205
- [K in NodeTypeNames<G>]: G["nodes"][K]["type"];
206
- }[NodeTypeNames<G>];
158
+ type SimilarToOptions = Readonly<{
159
+ /** Similarity metric to use. Default: "cosine" */
160
+ metric?: VectorMetricType;
161
+ /**
162
+ * Minimum similarity score to include results.
163
+ * For cosine: 0-1 where 1 is identical.
164
+ * For L2: maximum distance to include.
165
+ * For inner_product: minimum inner product value.
166
+ */
167
+ minScore?: number;
168
+ }>;
207
169
  /**
208
- * Get all EdgeTypes from a GraphDef.
170
+ * Creates a field reference.
209
171
  */
210
- type AllEdgeTypes<G extends GraphDef> = {
211
- [K in EdgeTypeNames<G>]: G["edges"][K]["type"];
212
- }[EdgeTypeNames<G>];
172
+ type FieldRefOptions = Readonly<{
173
+ jsonPointer?: JsonPointer | undefined;
174
+ valueType?: ValueType | undefined;
175
+ elementType?: ValueType | undefined;
176
+ }>;
177
+ declare function fieldRef(alias: string, path: readonly string[], options?: FieldRefOptions): FieldRef;
213
178
  /**
214
- * Creates a graph definition.
179
+ * Creates an EXISTS subquery predicate.
180
+ * Returns true if the subquery returns at least one row.
181
+ *
182
+ * @param subquery - The subquery AST to check for existence
215
183
  *
216
184
  * @example
217
185
  * ```typescript
218
- * const graph = defineGraph({
219
- * id: "my_graph",
220
- * nodes: {
221
- * Person: { type: Person },
222
- * Company: { type: Company },
223
- * },
224
- * edges: {
225
- * // Traditional EdgeRegistration syntax
226
- * worksAt: {
227
- * type: worksAt,
228
- * from: [Person],
229
- * to: [Company],
230
- * cardinality: "many",
231
- * },
232
- * // Or use EdgeType directly if it has from/to defined
233
- * knows, // EdgeType with built-in domain/range
234
- * },
235
- * ontology: [
236
- * subClassOf(Company, Organization),
237
- * disjointWith(Person, Organization),
238
- * ],
239
- * defaults: {
240
- * onNodeDelete: "restrict",
241
- * temporalMode: "current",
242
- * },
243
- * });
186
+ * // Find persons who have at least one order
187
+ * query
188
+ * .from("Person", "p")
189
+ * .whereNode("p", () =>
190
+ * exists(
191
+ * query.from("Order", "o")
192
+ * .whereNode("o", (o) => o.customerId.eq(field("p.id")))
193
+ * .select((ctx) => ({ id: ctx.o.id }))
194
+ * .toAst()
195
+ * )
196
+ * )
244
197
  * ```
245
198
  */
246
- declare function defineGraph<TNodes extends Record<string, NodeRegistration<NodeType>>, TEdges extends Record<string, EdgeEntry>, TOntology extends readonly OntologyRelation[]>(config: GraphDefConfig<TNodes, TEdges, TOntology>): GraphDef<TNodes, NormalizedEdges<TEdges>, TOntology>;
199
+ declare function exists(subquery: QueryAst): Predicate;
247
200
  /**
248
- * Checks if a value is a GraphDef.
201
+ * Creates a NOT EXISTS subquery predicate.
202
+ * Returns true if the subquery returns no rows.
203
+ *
204
+ * @param subquery - The subquery AST to check for non-existence
249
205
  */
250
- declare function isGraphDef(value: unknown): value is GraphDef;
206
+ declare function notExists(subquery: QueryAst): Predicate;
251
207
  /**
252
- * Gets all node type names from a GraphDef.
208
+ * Creates an IN subquery predicate.
209
+ * Returns true if the field value is in the subquery results.
210
+ *
211
+ * @param field - The field to check
212
+ * @param subquery - The subquery AST that returns a single column
213
+ *
214
+ * @example
215
+ * ```typescript
216
+ * // Find persons whose ID is in the VIP list
217
+ * query
218
+ * .from("Person", "p")
219
+ * .where(() =>
220
+ * inSubquery(
221
+ * fieldRef("p", ["id"]),
222
+ * query.from("VIPMember", "v")
223
+ * .aggregate({
224
+ * id: fieldRef("v", ["props", "personId"], { valueType: "string" }),
225
+ * })
226
+ * .toAst()
227
+ * )
228
+ * )
229
+ * ```
253
230
  */
254
- declare function getNodeTypeNames<G extends GraphDef>(graph: G): readonly string[];
231
+ declare function inSubquery(field: FieldRef, subquery: QueryAst): Predicate;
255
232
  /**
256
- * Gets all edge type names from a GraphDef.
233
+ * Creates a NOT IN subquery predicate.
234
+ * Returns true if the field value is not in the subquery results.
235
+ *
236
+ * @param field - The field to check
237
+ * @param subquery - The subquery AST that returns a single column
257
238
  */
258
- declare function getEdgeTypeNames<G extends GraphDef>(graph: G): readonly string[];
239
+ declare function notInSubquery(field: FieldRef, subquery: QueryAst): Predicate;
259
240
 
260
241
  /**
261
- * KindRegistry holds precomputed closures for ontological reasoning.
242
+ * Shared type definitions for the query builder.
262
243
  *
263
- * Computed at store initialization and cached for fast query-time lookups.
244
+ * Contains type definitions used across QueryBuilder, TraversalBuilder,
245
+ * and ExecutableQuery classes.
264
246
  */
265
- declare class KindRegistry {
266
- readonly nodeKinds: ReadonlyMap<string, NodeType>;
267
- readonly edgeKinds: ReadonlyMap<string, AnyEdgeType>;
268
- readonly subClassAncestors: ReadonlyMap<string, ReadonlySet<string>>;
269
- readonly subClassDescendants: ReadonlyMap<string, ReadonlySet<string>>;
270
- readonly broaderClosure: ReadonlyMap<string, ReadonlySet<string>>;
271
- readonly narrowerClosure: ReadonlyMap<string, ReadonlySet<string>>;
272
- readonly equivalenceSets: ReadonlyMap<string, ReadonlySet<string>>;
273
- readonly iriToKind: ReadonlyMap<string, string>;
274
- readonly disjointPairs: ReadonlySet<string>;
275
- readonly partOfClosure: ReadonlyMap<string, ReadonlySet<string>>;
276
- readonly hasPartClosure: ReadonlyMap<string, ReadonlySet<string>>;
277
- readonly edgeInverses: ReadonlyMap<string, string>;
278
- readonly edgeImplicationsClosure: ReadonlyMap<string, ReadonlySet<string>>;
279
- readonly edgeImplyingClosure: ReadonlyMap<string, ReadonlySet<string>>;
280
- constructor(nodeKinds: ReadonlyMap<string, NodeType>, edgeKinds: ReadonlyMap<string, AnyEdgeType>, closures: {
281
- subClassAncestors: ReadonlyMap<string, ReadonlySet<string>>;
282
- subClassDescendants: ReadonlyMap<string, ReadonlySet<string>>;
283
- broaderClosure: ReadonlyMap<string, ReadonlySet<string>>;
284
- narrowerClosure: ReadonlyMap<string, ReadonlySet<string>>;
285
- equivalenceSets: ReadonlyMap<string, ReadonlySet<string>>;
286
- iriToKind: ReadonlyMap<string, string>;
287
- disjointPairs: ReadonlySet<string>;
288
- partOfClosure: ReadonlyMap<string, ReadonlySet<string>>;
289
- hasPartClosure: ReadonlyMap<string, ReadonlySet<string>>;
290
- edgeInverses: ReadonlyMap<string, string>;
291
- edgeImplicationsClosure: ReadonlyMap<string, ReadonlySet<string>>;
292
- edgeImplyingClosure: ReadonlyMap<string, ReadonlySet<string>>;
293
- });
294
- /**
295
- * Checks if child is a subclass of parent (directly or transitively).
296
- */
297
- isSubClassOf(child: string, parent: string): boolean;
298
- /**
299
- * Expands a kind to include all its subclasses.
300
- * Returns [kind, ...subclasses].
301
- */
302
- expandSubClasses(kind: string): readonly string[];
303
- /**
304
- * Gets all ancestors of a kind (via subClassOf).
305
- */
306
- getAncestors(kind: string): ReadonlySet<string>;
307
- /**
308
- * Gets all descendants of a kind (via subClassOf).
309
- */
310
- getDescendants(kind: string): ReadonlySet<string>;
311
- /**
312
- * Checks if narrowerConcept is narrower than broaderConcept.
313
- */
314
- isNarrowerThan(narrowerConcept: string, broaderConcept: string): boolean;
315
- /**
316
- * Checks if broaderConcept is broader than narrowerConcept.
317
- */
318
- isBroaderThan(broaderConcept: string, narrowerConcept: string): boolean;
319
- /**
320
- * Expands to include all narrower concepts.
321
- */
322
- expandNarrower(kind: string): readonly string[];
323
- /**
324
- * Expands to include all broader concepts.
325
- */
326
- expandBroader(kind: string): readonly string[];
327
- /**
328
- * Checks if two kinds are equivalent.
329
- */
330
- areEquivalent(a: string, b: string): boolean;
331
- /**
332
- * Gets all equivalents of a kind (including external IRIs).
333
- */
334
- getEquivalents(kind: string): readonly string[];
335
- /**
336
- * Resolves an external IRI to an internal kind name.
337
- */
338
- resolveIri(iri: string): string | undefined;
339
- /**
340
- * Checks if two kinds are disjoint.
341
- */
342
- areDisjoint(a: string, b: string): boolean;
343
- /**
344
- * Gets all kinds that are disjoint with the given kind.
345
- */
346
- getDisjointKinds(kind: string): readonly string[];
347
- /**
348
- * Checks if part is part of whole (directly or transitively).
349
- */
350
- isPartOf(part: string, whole: string): boolean;
351
- /**
352
- * Gets all wholes that contain this part.
353
- */
354
- getWholes(part: string): readonly string[];
355
- /**
356
- * Gets all parts of this whole.
357
- */
358
- getParts(whole: string): readonly string[];
359
- /**
360
- * Gets the inverse edge kind for a given edge kind.
361
- * If edgeA inverseOf edgeB, then getInverseEdge("edgeA") returns "edgeB".
362
- */
363
- getInverseEdge(edgeKind: string): string | undefined;
364
- /**
365
- * Gets all edges implied by a given edge (transitively).
366
- * If A implies B and B implies C, then getImpliedEdges("A") returns ["B", "C"].
367
- */
368
- getImpliedEdges(edgeKind: string): readonly string[];
369
- /**
370
- * Gets all edges that imply a given edge (transitively).
371
- * If A implies B and B implies C, then getImplyingEdges("C") returns ["A", "B"].
372
- * Used for query-time expansion: when querying for C, also include A and B edges.
373
- */
374
- getImplyingEdges(edgeKind: string): readonly string[];
375
- /**
376
- * Expands an edge kind to include all edges that imply it.
377
- * Returns [edgeKind, ...implyingEdges].
378
- */
379
- expandImplyingEdges(edgeKind: string): readonly string[];
380
- /**
381
- * Checks if a concrete kind is assignable to a target kind.
382
- * Uses subsumption: Company is assignable to Organization if Company subClassOf Organization.
383
- */
384
- isAssignableTo(concreteKind: string, targetKind: string): boolean;
385
- /**
386
- * Validates that a kind exists in the registry.
387
- */
388
- hasNodeType(name: string): boolean;
389
- /**
390
- * Validates that an edge kind exists in the registry.
391
- */
392
- hasEdgeType(name: string): boolean;
393
- /**
394
- * Gets a node kind by name.
395
- */
396
- getNodeType(name: string): NodeType | undefined;
397
- /**
398
- * Gets an edge kind by name.
399
- */
400
- getEdgeType(name: string): AnyEdgeType | undefined;
401
- }
402
247
 
403
248
  /**
404
- * Store types for TypeGraph operations.
249
+ * Extracts the names of valid target node kinds for an edge traversal.
250
+ *
251
+ * For "out" direction: returns the names of node kinds in the edge's "to" array.
252
+ * For "in" direction: returns the names of node kinds in the edge's "from" array.
253
+ *
254
+ * @example
255
+ * // Given: worksAt: { from: [Person], to: [Company, Organization] }
256
+ * // ValidEdgeTargets<G, "worksAt", "out"> = "Company" | "Organization"
257
+ * // ValidEdgeTargets<G, "worksAt", "in"> = "Person"
405
258
  */
406
-
259
+ type ValidEdgeTargets<G extends GraphDef, EK extends keyof G["edges"] & string, Dir extends TraversalDirection> = G["edges"][EK] extends EdgeRegistration ? Dir extends "out" ? G["edges"][EK]["to"][number]["kind"] : G["edges"][EK]["from"][number]["kind"] : never;
407
260
  /**
408
- * Metadata for a node instance.
261
+ * A node alias with its associated type.
409
262
  */
410
- type NodeMeta = Readonly<{
411
- version: number;
412
- validFrom: string | undefined;
413
- validTo: string | undefined;
414
- createdAt: string;
415
- updatedAt: string;
416
- deletedAt: string | undefined;
263
+ type NodeAlias<K extends NodeType = NodeType, Optional extends boolean = false> = Readonly<{
264
+ type: K;
265
+ alias: string;
266
+ optional: Optional;
417
267
  }>;
418
268
  /**
419
- * A node instance in the graph.
420
- *
421
- * Properties from the schema are spread at the top level for ergonomic access:
422
- * - `node.name` instead of `node.props.name`
423
- * - System metadata is under `node.meta.*`
269
+ * A map of alias names to their node aliases.
424
270
  */
425
- type Node<N extends NodeType = NodeType> = Readonly<{
426
- kind: N["name"];
427
- id: NodeId<N>;
428
- meta: NodeMeta;
429
- }> & Readonly<z.infer<N["schema"]>>;
271
+ type AliasMap = Readonly<Record<string, NodeAlias<NodeType, boolean>>>;
430
272
  /**
431
- * Input for creating a node.
273
+ * An edge alias with its associated type and optional flag.
432
274
  */
433
- type CreateNodeInput<N extends NodeType = NodeType> = Readonly<{
434
- kind: N["name"];
435
- id?: string;
436
- props: z.infer<N["schema"]>;
437
- validFrom?: string;
438
- validTo?: string;
275
+ type EdgeAlias<E extends AnyEdgeType = EdgeType, Optional extends boolean = false> = Readonly<{
276
+ type: E;
277
+ alias: string;
278
+ optional: Optional;
439
279
  }>;
440
280
  /**
441
- * Input for updating a node.
281
+ * A map of alias names to their edge aliases.
442
282
  */
443
- type UpdateNodeInput<N extends NodeType = NodeType> = Readonly<{
444
- kind: N["name"];
445
- id: NodeId<N>;
446
- props: Partial<z.infer<N["schema"]>>;
447
- validTo?: string;
448
- }>;
283
+ type EdgeAliasMap = Readonly<Record<string, EdgeAlias<EdgeType, boolean>>>;
449
284
  /**
450
- * Metadata for an edge instance.
285
+ * A recursive alias marker with its associated type (depth or path).
451
286
  */
452
- type EdgeMeta = Readonly<{
453
- validFrom: string | undefined;
454
- validTo: string | undefined;
455
- createdAt: string;
456
- updatedAt: string;
457
- deletedAt: string | undefined;
287
+ type RecursiveAlias<T extends "depth" | "path"> = Readonly<{
288
+ type: T;
458
289
  }>;
459
290
  /**
460
- * An edge instance in the graph.
461
- *
462
- * Properties from the schema are spread at the top level for ergonomic access:
463
- * - `edge.role` instead of `edge.props.role`
464
- * - System metadata is under `edge.meta.*`
291
+ * A map of recursive alias names to their types.
465
292
  */
466
- type Edge<E extends AnyEdgeType = EdgeType> = Readonly<{
467
- id: string;
468
- kind: E["name"];
469
- fromKind: string;
470
- fromId: string;
471
- toKind: string;
472
- toId: string;
473
- meta: EdgeMeta;
474
- }> & Readonly<z.infer<E["schema"]>>;
293
+ type RecursiveAliasMap = Readonly<Record<string, RecursiveAlias<"depth" | "path">>>;
475
294
  /**
476
- * Input for creating an edge.
295
+ * Resolves a recursive alias marker to its runtime value type.
477
296
  */
478
- type CreateEdgeInput<E extends AnyEdgeType = EdgeType> = Readonly<{
479
- kind: E["name"];
480
- id?: string;
481
- fromKind: string;
482
- fromId: string;
483
- toKind: string;
484
- toId: string;
485
- props: z.infer<E["schema"]>;
486
- validFrom?: string;
487
- validTo?: string;
488
- }>;
297
+ type RecursiveAliasValue<RA> = RA extends RecursiveAlias<"depth"> ? number : RA extends RecursiveAlias<"path"> ? readonly string[] : never;
489
298
  /**
490
- * Input for updating an edge.
299
+ * Resolves the depth alias name from the recursive config.
300
+ * If a string is provided, uses it directly. If `true`, defaults to `${A}_depth`.
491
301
  */
492
- type UpdateEdgeInput<E extends AnyEdgeType = EdgeType> = Readonly<{
493
- id: string;
494
- props: Partial<z.infer<E["schema"]>>;
495
- validTo?: string;
302
+ type ResolveDepthAlias<DC, A extends string> = DC extends string ? DC : DC extends true ? `${A}_depth` : never;
303
+ /**
304
+ * Resolves the path alias name from the recursive config.
305
+ * If a string is provided, uses it directly. If `true`, defaults to `${A}_path`.
306
+ */
307
+ type ResolvePathAlias<PC, A extends string> = PC extends string ? PC : PC extends true ? `${A}_path` : never;
308
+ /**
309
+ * Builds the recursive alias map from depth/path config and target node alias.
310
+ */
311
+ type BuildRecursiveAliases<DC, PC, A extends string> = ([DC] extends ([
312
+ false
313
+ ]) ? {} : Record<ResolveDepthAlias<DC, A>, RecursiveAlias<"depth">>) & ([PC] extends [false] ? {} : Record<ResolvePathAlias<PC, A>, RecursiveAlias<"path">>);
314
+ /**
315
+ * Type utility for compile-time alias collision detection.
316
+ *
317
+ * When A already exists in Aliases, this resolves to an error message type
318
+ * that will cause a type error with a descriptive message.
319
+ */
320
+ type UniqueAlias<A extends string, Aliases extends AliasMap> = A extends keyof Aliases ? `Error: Alias '${A}' is already in use` : A;
321
+ /**
322
+ * Creates typed field accessors for a node kind's properties.
323
+ */
324
+ type PropsAccessor<N extends NodeType> = Readonly<{
325
+ [K in keyof z.infer<N["schema"]>]-?: FieldAccessor<z.infer<N["schema"]>[K]>;
496
326
  }>;
497
327
  /**
498
- * Options for node and edge queries.
328
+ * A field accessor with type-appropriate predicate methods.
329
+ * Uses NonNullable to handle optional fields correctly.
499
330
  */
500
- type QueryOptions = Readonly<{
501
- /** Temporal mode for the query */
502
- temporalMode?: TemporalMode;
503
- /** Specific timestamp for asOf queries */
504
- asOf?: string;
331
+ type FieldAccessor<T> = FieldAccessorForType<NonNullable<T>>;
332
+ type FieldAccessorForType<T> = [
333
+ T
334
+ ] extends [EmbeddingValue] ? EmbeddingFieldAccessor : [T] extends [string] ? StringFieldAccessor : [T] extends [number] ? NumberFieldAccessor : [T] extends [boolean] ? BooleanFieldAccessor : [T] extends [Date] ? DateFieldAccessor : [T] extends [readonly (infer U)[]] ? ArrayFieldAccessor<U> : [T] extends [Record<string, unknown>] ? ObjectFieldAccessor<T> : BaseFieldAccessor;
335
+ type BaseFieldAccessor = Readonly<{
336
+ eq: (value: unknown) => Predicate;
337
+ neq: (value: unknown) => Predicate;
338
+ isNull: () => Predicate;
339
+ isNotNull: () => Predicate;
340
+ in: (values: readonly unknown[]) => Predicate;
341
+ notIn: (values: readonly unknown[]) => Predicate;
342
+ }>;
343
+ type StringFieldAccessor = BaseFieldAccessor & Readonly<{
344
+ contains: (pattern: string | ParameterRef) => Predicate;
345
+ startsWith: (pattern: string | ParameterRef) => Predicate;
346
+ endsWith: (pattern: string | ParameterRef) => Predicate;
347
+ like: (pattern: string | ParameterRef) => Predicate;
348
+ ilike: (pattern: string | ParameterRef) => Predicate;
349
+ }>;
350
+ type NumberFieldAccessor = BaseFieldAccessor & Readonly<{
351
+ gt: (value: number | ParameterRef) => Predicate;
352
+ gte: (value: number | ParameterRef) => Predicate;
353
+ lt: (value: number | ParameterRef) => Predicate;
354
+ lte: (value: number | ParameterRef) => Predicate;
355
+ between: (lower: number | ParameterRef, upper: number | ParameterRef) => Predicate;
356
+ }>;
357
+ type BooleanFieldAccessor = BaseFieldAccessor;
358
+ type DateFieldAccessor = BaseFieldAccessor & Readonly<{
359
+ gt: (value: Date | string | ParameterRef) => Predicate;
360
+ gte: (value: Date | string | ParameterRef) => Predicate;
361
+ lt: (value: Date | string | ParameterRef) => Predicate;
362
+ lte: (value: Date | string | ParameterRef) => Predicate;
363
+ between: (lower: Date | string | ParameterRef, upper: Date | string | ParameterRef) => Predicate;
364
+ }>;
365
+ type ArrayFieldAccessor<U> = BaseFieldAccessor & Readonly<{
366
+ contains: (value: U) => Predicate;
367
+ containsAny: (values: readonly U[]) => Predicate;
368
+ containsAll: (values: readonly U[]) => Predicate;
369
+ length: NumberFieldAccessor;
370
+ isEmpty: () => Predicate;
371
+ isNotEmpty: () => Predicate;
372
+ lengthEq: (length: number) => Predicate;
373
+ lengthGt: (length: number) => Predicate;
374
+ lengthGte: (length: number) => Predicate;
375
+ lengthLt: (length: number) => Predicate;
376
+ lengthLte: (length: number) => Predicate;
377
+ }>;
378
+ type EmbeddingFieldAccessor = BaseFieldAccessor & Readonly<{
379
+ /**
380
+ * Finds the k most similar items using vector similarity.
381
+ *
382
+ * @param queryEmbedding - The query vector to compare against
383
+ * @param k - Maximum number of results to return
384
+ * @param options - Optional metric and minimum score filter
385
+ */
386
+ similarTo: (queryEmbedding: readonly number[], k: number, options?: SimilarToOptions) => Predicate;
387
+ }>;
388
+ type ObjectFieldAccessor<T> = BaseFieldAccessor & Readonly<{
389
+ get: <K extends keyof T & string>(key: K) => T[K] extends Record<string, unknown> ? ObjectFieldAccessor<T[K]> : FieldAccessor<T[K]>;
390
+ hasKey: (key: string) => Predicate;
391
+ hasPath: <P extends JsonPointerInput<T>>(pointer: P) => Predicate;
392
+ pathEquals: <P extends JsonPointerInput<T>>(pointer: P, value: string | number | boolean | Date) => Predicate;
393
+ pathContains: <P extends JsonPointerInput<T>>(pointer: P, value: string | number | boolean | Date) => Predicate;
394
+ pathIsNull: <P extends JsonPointerInput<T>>(pointer: P) => Predicate;
395
+ pathIsNotNull: <P extends JsonPointerInput<T>>(pointer: P) => Predicate;
505
396
  }>;
506
397
  /**
507
- * Context passed to observability hooks.
398
+ * Node accessor for predicate building.
399
+ *
400
+ * Properties are available at the top level for ergonomic access:
401
+ * - `n.name` instead of `n.props.name`
402
+ * - System fields: `n.id`, `n.kind`
508
403
  */
509
- type HookContext = Readonly<{
510
- /** Unique ID for this operation */
511
- operationId: string;
512
- /** Graph ID */
513
- graphId: string;
514
- /** Timestamp when operation started */
515
- startedAt: Date;
404
+ type NodeAccessor<N extends NodeType> = Readonly<{
405
+ id: StringFieldAccessor;
406
+ kind: StringFieldAccessor;
407
+ }> & PropsAccessor<N>;
408
+ /**
409
+ * Creates typed field accessors for an edge kind's properties.
410
+ */
411
+ type EdgePropsAccessor<E extends AnyEdgeType> = Readonly<{
412
+ [K in keyof z.infer<E["schema"]>]-?: FieldAccessor<z.infer<E["schema"]>[K]>;
516
413
  }>;
517
414
  /**
518
- * Query hook context with SQL information.
415
+ * Edge accessor for predicate building.
416
+ *
417
+ * Properties are available at the top level for ergonomic access:
418
+ * - `e.role` instead of `e.props.role`
419
+ * - System fields: `e.id`, `e.kind`, `e.fromId`, `e.toId`
519
420
  */
520
- type QueryHookContext = HookContext & Readonly<{
521
- /** The SQL query being executed */
522
- sql: string;
523
- /** Query parameters */
524
- params: readonly unknown[];
421
+ type EdgeAccessor<E extends AnyEdgeType> = Readonly<{
422
+ id: StringFieldAccessor;
423
+ kind: StringFieldAccessor;
424
+ fromId: StringFieldAccessor;
425
+ toId: StringFieldAccessor;
426
+ }> & EdgePropsAccessor<E>;
427
+ /**
428
+ * Metadata for a selectable node result.
429
+ */
430
+ type SelectableNodeMeta = Readonly<{
431
+ version: number;
432
+ validFrom: string | undefined;
433
+ validTo: string | undefined;
434
+ createdAt: string;
435
+ updatedAt: string;
436
+ deletedAt: string | undefined;
525
437
  }>;
526
438
  /**
527
- * Operation hook context for CRUD operations.
439
+ * A selectable node result.
440
+ *
441
+ * Properties from the schema are spread at the top level for ergonomic access:
442
+ * - `node.name` instead of `node.props.name`
443
+ * - System metadata is under `node.meta.*`
528
444
  */
529
- type OperationHookContext = HookContext & Readonly<{
530
- /** Operation type */
531
- operation: "create" | "update" | "delete";
532
- /** Entity type */
533
- entity: "node" | "edge";
534
- /** Kind of node or edge */
535
- kind: string;
536
- /** Entity ID */
445
+ type SelectableNode<N extends NodeType> = Readonly<{
537
446
  id: string;
447
+ kind: N["kind"];
448
+ meta: SelectableNodeMeta;
449
+ }> & Readonly<z.infer<N["schema"]>>;
450
+ /**
451
+ * Metadata for a selectable edge result.
452
+ */
453
+ type SelectableEdgeMeta = Readonly<{
454
+ validFrom: string | undefined;
455
+ validTo: string | undefined;
456
+ createdAt: string;
457
+ updatedAt: string;
458
+ deletedAt: string | undefined;
538
459
  }>;
539
460
  /**
540
- * Observability hooks for monitoring store operations.
461
+ * A selectable edge result.
541
462
  *
542
- * @example
543
- * ```typescript
544
- * const hooks: StoreHooks = {
545
- * onQueryStart: (ctx) => {
546
- * console.log(`[${ctx.operationId}] Query: ${ctx.sql}`);
547
- * },
548
- * onQueryEnd: (ctx, result) => {
549
- * const duration = Date.now() - ctx.startedAt.getTime();
550
- * console.log(`[${ctx.operationId}] Completed in ${duration}ms`);
551
- * },
552
- * onError: (ctx, error) => {
553
- * console.error(`[${ctx.operationId}] Error:`, error);
554
- * },
555
- * };
463
+ * Properties from the schema are spread at the top level for ergonomic access:
464
+ * - `edge.role` instead of `edge.props.role`
465
+ * - System metadata is under `edge.meta.*`
466
+ */
467
+ type SelectableEdge<E extends AnyEdgeType = EdgeType> = Readonly<{
468
+ id: string;
469
+ kind: E["kind"];
470
+ fromId: string;
471
+ toId: string;
472
+ meta: SelectableEdgeMeta;
473
+ }> & Readonly<z.infer<E["schema"]>>;
474
+ /**
475
+ * Selection context passed to select callback.
556
476
  *
557
- * const store = createStore(graph, backend, { hooks });
558
- * ```
477
+ * Includes node aliases, edge aliases, and recursive metadata aliases
478
+ * (depth/path from variable-length traversals). Edge aliases from optional
479
+ * traversals are nullable.
559
480
  */
560
- type StoreHooks = Readonly<{
561
- /** Called before a query is executed */
562
- onQueryStart?: (ctx: QueryHookContext) => void;
563
- /** Called after a query completes successfully */
564
- onQueryEnd?: (ctx: QueryHookContext, result: Readonly<{
565
- rowCount: number;
566
- durationMs: number;
567
- }>) => void;
568
- /** Called before a CRUD operation starts */
569
- onOperationStart?: (ctx: OperationHookContext) => void;
570
- /** Called after a CRUD operation completes */
571
- onOperationEnd?: (ctx: OperationHookContext, result: Readonly<{
572
- durationMs: number;
573
- }>) => void;
574
- /** Called when an error occurs */
575
- onError?: (ctx: HookContext, error: Error) => void;
481
+ type SelectContext<Aliases extends AliasMap, EdgeAliases extends EdgeAliasMap = Record<string, never>, RecursiveAliases extends RecursiveAliasMap = {}> = Readonly<{
482
+ [A in keyof Aliases]: Aliases[A]["optional"] extends true ? SelectableNode<Aliases[A]["type"]> | undefined : SelectableNode<Aliases[A]["type"]>;
483
+ }> & Readonly<{
484
+ [EA in keyof EdgeAliases]: EdgeAliases[EA]["optional"] extends true ? SelectableEdge<EdgeAliases[EA]["type"]> | undefined : SelectableEdge<EdgeAliases[EA]["type"]>;
485
+ }> & Readonly<{
486
+ [RA in keyof RecursiveAliases]: RecursiveAliasValue<RecursiveAliases[RA]>;
576
487
  }>;
577
488
  /**
578
- * Options for creating a store.
489
+ * Result of a paginated query.
579
490
  */
580
- type StoreOptions = Readonly<{
581
- /** Observability hooks for monitoring */
582
- hooks?: StoreHooks;
583
- /** SQL schema configuration for custom table names */
584
- schema?: SqlSchema;
491
+ type PaginatedResult<R> = Readonly<{
492
+ /** The data items for this page */
493
+ data: readonly R[];
494
+ /** Cursor to fetch the next page (undefined if no more pages) */
495
+ nextCursor: string | undefined;
496
+ /** Cursor to fetch the previous page (undefined if on first page) */
497
+ prevCursor: string | undefined;
498
+ /** Whether there are more items after this page */
499
+ hasNextPage: boolean;
500
+ /** Whether there are items before this page */
501
+ hasPrevPage: boolean;
585
502
  }>;
586
503
  /**
587
- * Configuration for creating a store.
504
+ * Options for cursor-based pagination.
505
+ *
506
+ * Use `first`/`after` for forward pagination, `last`/`before` for backward.
588
507
  */
589
- type StoreConfig<G extends GraphDef> = Readonly<{
590
- /** The graph definition */
591
- graph: G;
592
- /** Database connection string or path */
593
- connection?: string;
594
- /** Auto-run migrations */
595
- autoMigrate?: boolean;
508
+ type PaginateOptions = Readonly<{
509
+ /** Number of items to fetch (forward pagination) */
510
+ first?: number;
511
+ /** Cursor to start after (forward pagination) */
512
+ after?: string;
513
+ /** Number of items to fetch (backward pagination) */
514
+ last?: number;
515
+ /** Cursor to start before (backward pagination) */
516
+ before?: string;
596
517
  }>;
597
518
  /**
598
- * A collection of nodes of a specific type.
599
- *
600
- * Provides ergonomic CRUD operations for a single node type.
519
+ * Options for streaming results.
601
520
  */
602
- type NodeCollection<N extends NodeType> = Readonly<{
603
- /** Create a new node */
604
- create: (props: z.input<N["schema"]>, options?: Readonly<{
605
- id?: string;
521
+ type StreamOptions = Readonly<{
522
+ /** Number of items to fetch per batch (default: 1000) */
523
+ batchSize?: number;
524
+ }>;
525
+ /**
526
+ * Options for recursive traversals.
527
+ */
528
+ type RecursiveTraversalOptions = Readonly<{
529
+ /** Minimum number of hops before including results (default: 1) */
530
+ minHops?: number;
531
+ /** Maximum number of hops (-1 means unlimited) */
532
+ maxHops?: number;
533
+ /** Cycle handling policy (default: "prevent") */
534
+ cyclePolicy?: RecursiveCyclePolicy;
535
+ /** Include path in output. Pass a string to customize alias. */
536
+ path?: boolean | string;
537
+ /** Include depth in output. Pass a string to customize alias. */
538
+ depth?: boolean | string;
539
+ }>;
540
+ /**
541
+ * Configuration for the query builder.
542
+ */
543
+ type QueryBuilderConfig = Readonly<{
544
+ graphId: string;
545
+ registry: KindRegistry;
546
+ schemaIntrospector: SchemaIntrospector;
547
+ /** Default traversal ontology expansion mode. */
548
+ defaultTraversalExpansion: TraversalExpansion;
549
+ backend?: GraphBackend;
550
+ dialect?: SqlDialect;
551
+ /** SQL schema configuration for custom table names. */
552
+ schema?: SqlSchema;
553
+ }>;
554
+ /**
555
+ * Internal state of the query builder.
556
+ */
557
+ type QueryBuilderState = Readonly<{
558
+ startAlias: string;
559
+ startKinds: readonly string[];
560
+ /** The current alias (last traversal target, or startAlias if no traversals) */
561
+ currentAlias: string;
562
+ includeSubClasses: boolean;
563
+ traversals: readonly Traversal[];
564
+ predicates: readonly NodePredicate[];
565
+ projection: readonly ProjectedField[];
566
+ orderBy: readonly OrderSpec[];
567
+ limit: number | undefined;
568
+ offset: number | undefined;
569
+ temporalMode: TemporalMode;
570
+ asOf: string | undefined;
571
+ groupBy: GroupBySpec | undefined;
572
+ having: PredicateExpression | undefined;
573
+ }>;
574
+ /**
575
+ * Options for creating a query builder.
576
+ */
577
+ type CreateQueryBuilderOptions = Readonly<{
578
+ /** Backend for query execution */
579
+ backend?: GraphBackend;
580
+ /** SQL dialect for compilation */
581
+ dialect?: SqlDialect;
582
+ /** SQL schema configuration for custom table names */
583
+ schema?: SqlSchema;
584
+ /** Default traversal ontology expansion mode (default: "inverse"). */
585
+ defaultTraversalExpansion?: TraversalExpansion;
586
+ }>;
587
+
588
+ /**
589
+ * Store types for TypeGraph operations.
590
+ */
591
+
592
+ /**
593
+ * Metadata for a node instance.
594
+ */
595
+ type NodeMeta = Readonly<{
596
+ version: number;
597
+ validFrom: string | undefined;
598
+ validTo: string | undefined;
599
+ createdAt: string;
600
+ updatedAt: string;
601
+ deletedAt: string | undefined;
602
+ }>;
603
+ /**
604
+ * A node instance in the graph.
605
+ *
606
+ * Properties from the schema are spread at the top level for ergonomic access:
607
+ * - `node.name` instead of `node.props.name`
608
+ * - System metadata is under `node.meta.*`
609
+ */
610
+ type Node<N extends NodeType = NodeType> = Readonly<{
611
+ kind: N["kind"];
612
+ id: NodeId<N>;
613
+ meta: NodeMeta;
614
+ }> & Readonly<z.infer<N["schema"]>>;
615
+ /**
616
+ * Input for creating a node.
617
+ */
618
+ type CreateNodeInput<N extends NodeType = NodeType> = Readonly<{
619
+ kind: N["kind"];
620
+ id?: string;
621
+ props: z.infer<N["schema"]>;
622
+ validFrom?: string;
623
+ validTo?: string;
624
+ }>;
625
+ /**
626
+ * Input for updating a node.
627
+ */
628
+ type UpdateNodeInput<N extends NodeType = NodeType> = Readonly<{
629
+ kind: N["kind"];
630
+ id: NodeId<N>;
631
+ props: Partial<z.infer<N["schema"]>>;
632
+ validTo?: string;
633
+ }>;
634
+ /**
635
+ * Metadata for an edge instance.
636
+ */
637
+ type EdgeMeta = Readonly<{
638
+ validFrom: string | undefined;
639
+ validTo: string | undefined;
640
+ createdAt: string;
641
+ updatedAt: string;
642
+ deletedAt: string | undefined;
643
+ }>;
644
+ /**
645
+ * An edge instance in the graph.
646
+ *
647
+ * Properties from the schema are spread at the top level for ergonomic access:
648
+ * - `edge.role` instead of `edge.props.role`
649
+ * - System metadata is under `edge.meta.*`
650
+ */
651
+ type Edge<E extends AnyEdgeType = EdgeType> = Readonly<{
652
+ id: string;
653
+ kind: E["kind"];
654
+ fromKind: string;
655
+ fromId: string;
656
+ toKind: string;
657
+ toId: string;
658
+ meta: EdgeMeta;
659
+ }> & Readonly<z.infer<E["schema"]>>;
660
+ /**
661
+ * Input for creating an edge.
662
+ */
663
+ type CreateEdgeInput<E extends AnyEdgeType = EdgeType> = Readonly<{
664
+ kind: E["kind"];
665
+ id?: string;
666
+ fromKind: string;
667
+ fromId: string;
668
+ toKind: string;
669
+ toId: string;
670
+ props: z.infer<E["schema"]>;
671
+ validFrom?: string;
672
+ validTo?: string;
673
+ }>;
674
+ /**
675
+ * Input for updating an edge.
676
+ */
677
+ type UpdateEdgeInput<E extends AnyEdgeType = EdgeType> = Readonly<{
678
+ id: string;
679
+ props: Partial<z.infer<E["schema"]>>;
680
+ validTo?: string;
681
+ }>;
682
+ /**
683
+ * Options for node and edge queries.
684
+ */
685
+ type QueryOptions = Readonly<{
686
+ /** Temporal mode for the query */
687
+ temporalMode?: TemporalMode;
688
+ /** Specific timestamp for asOf queries */
689
+ asOf?: string;
690
+ }>;
691
+ /**
692
+ * Context passed to observability hooks.
693
+ */
694
+ type HookContext = Readonly<{
695
+ /** Unique ID for this operation */
696
+ operationId: string;
697
+ /** Graph ID */
698
+ graphId: string;
699
+ /** Timestamp when operation started */
700
+ startedAt: Date;
701
+ }>;
702
+ /**
703
+ * Query hook context with SQL information.
704
+ */
705
+ type QueryHookContext = HookContext & Readonly<{
706
+ /** The SQL query being executed */
707
+ sql: string;
708
+ /** Query parameters */
709
+ params: readonly unknown[];
710
+ }>;
711
+ /**
712
+ * Operation hook context for CRUD operations.
713
+ */
714
+ type OperationHookContext = HookContext & Readonly<{
715
+ /** Operation type */
716
+ operation: "create" | "update" | "delete";
717
+ /** Entity type */
718
+ entity: "node" | "edge";
719
+ /** Kind of node or edge */
720
+ kind: string;
721
+ /** Entity ID */
722
+ id: string;
723
+ }>;
724
+ /**
725
+ * Observability hooks for monitoring store operations.
726
+ *
727
+ * Note: Batch operations (`bulkCreate`, `bulkInsert`, `bulkUpsert`) skip
728
+ * per-item operation hooks for throughput. Query hooks still fire normally.
729
+ *
730
+ * @example
731
+ * ```typescript
732
+ * const hooks: StoreHooks = {
733
+ * onQueryStart: (ctx) => {
734
+ * console.log(`[${ctx.operationId}] Query: ${ctx.sql}`);
735
+ * },
736
+ * onQueryEnd: (ctx, result) => {
737
+ * const duration = Date.now() - ctx.startedAt.getTime();
738
+ * console.log(`[${ctx.operationId}] Completed in ${duration}ms`);
739
+ * },
740
+ * onError: (ctx, error) => {
741
+ * console.error(`[${ctx.operationId}] Error:`, error);
742
+ * },
743
+ * };
744
+ *
745
+ * const store = createStore(graph, backend, { hooks });
746
+ * ```
747
+ */
748
+ type StoreHooks = Readonly<{
749
+ /** Called before a query is executed */
750
+ onQueryStart?: (ctx: QueryHookContext) => void;
751
+ /** Called after a query completes successfully */
752
+ onQueryEnd?: (ctx: QueryHookContext, result: Readonly<{
753
+ rowCount: number;
754
+ durationMs: number;
755
+ }>) => void;
756
+ /** Called before a CRUD operation starts */
757
+ onOperationStart?: (ctx: OperationHookContext) => void;
758
+ /** Called after a CRUD operation completes */
759
+ onOperationEnd?: (ctx: OperationHookContext, result: Readonly<{
760
+ durationMs: number;
761
+ }>) => void;
762
+ /** Called when an error occurs */
763
+ onError?: (ctx: HookContext, error: Error) => void;
764
+ }>;
765
+ /**
766
+ * Options for creating a store.
767
+ */
768
+ type StoreOptions = Readonly<{
769
+ /** Observability hooks for monitoring */
770
+ hooks?: StoreHooks;
771
+ /** SQL schema configuration for custom table names */
772
+ schema?: SqlSchema;
773
+ /** Query default behaviors. */
774
+ queryDefaults?: Readonly<{
775
+ /** Default traversal ontology expansion mode (default: "inverse"). */
776
+ traversalExpansion?: TraversalExpansion;
777
+ }>;
778
+ }>;
779
+ /**
780
+ * A collection of nodes of a specific type.
781
+ *
782
+ * Provides ergonomic CRUD operations for a single node type.
783
+ */
784
+ type NodeCollection<N extends NodeType> = Readonly<{
785
+ /** Create a new node */
786
+ create: (props: z.input<N["schema"]>, options?: Readonly<{
787
+ id?: string;
606
788
  validFrom?: string;
607
789
  validTo?: string;
608
790
  }>) => Promise<Node<N>>;
609
791
  /** Get a node by ID */
610
792
  getById: (id: NodeId<N>, options?: QueryOptions) => Promise<Node<N> | undefined>;
793
+ /** Get multiple nodes by ID, preserving input order (undefined for missing) */
794
+ getByIds: (ids: readonly NodeId<N>[], options?: QueryOptions) => Promise<readonly (Node<N> | undefined)[]>;
611
795
  /** Update a node */
612
796
  update: (id: NodeId<N>, props: Partial<z.input<N["schema"]>>, options?: Readonly<{
613
797
  validTo?: string;
@@ -629,14 +813,18 @@ type NodeCollection<N extends NodeType> = Readonly<{
629
813
  /**
630
814
  * Find nodes matching criteria.
631
815
  *
816
+ * Supports predicate filtering via the `where` option for SQL-level filtering.
632
817
  * For simple queries. Use store.query() for complex traversals.
633
818
  */
634
819
  find: (options?: Readonly<{
820
+ where?: (accessor: NodeAccessor<N>) => Predicate;
635
821
  limit?: number;
636
822
  offset?: number;
823
+ temporalMode?: TemporalMode;
824
+ asOf?: string;
637
825
  }>) => Promise<Node<N>[]>;
638
826
  /** Count nodes matching criteria */
639
- count: () => Promise<number>;
827
+ count: (options?: QueryOptions) => Promise<number>;
640
828
  /**
641
829
  * Create or update a node.
642
830
  *
@@ -651,6 +839,7 @@ type NodeCollection<N extends NodeType> = Readonly<{
651
839
  * Create multiple nodes in a batch.
652
840
  *
653
841
  * More efficient than calling create() multiple times.
842
+ * Use `bulkInsert` for the dedicated fast path that skips returning results.
654
843
  */
655
844
  bulkCreate: (items: readonly Readonly<{
656
845
  props: z.input<N["schema"]>;
@@ -670,10 +859,24 @@ type NodeCollection<N extends NodeType> = Readonly<{
670
859
  validFrom?: string;
671
860
  validTo?: string;
672
861
  }>[]) => Promise<Node<N>[]>;
862
+ /**
863
+ * Insert multiple nodes without returning results.
864
+ *
865
+ * This is the dedicated fast path for bulk inserts. Unlike `bulkCreate`
866
+ * with `returnResults: false`, the intent is unambiguous: no results
867
+ * are returned and the operation is wrapped in a transaction.
868
+ */
869
+ bulkInsert: (items: readonly Readonly<{
870
+ props: z.input<N["schema"]>;
871
+ id?: string;
872
+ validFrom?: string;
873
+ validTo?: string;
874
+ }>[]) => Promise<void>;
673
875
  /**
674
876
  * Delete multiple nodes by ID.
675
877
  *
676
- * Silently ignores IDs that don't exist.
878
+ * Atomic when the backend supports transactions. Silently ignores IDs
879
+ * that don't exist.
677
880
  */
678
881
  bulkDelete: (ids: readonly NodeId<N>[]) => Promise<void>;
679
882
  }>;
@@ -697,7 +900,7 @@ type NodeRef = Readonly<{
697
900
  * allowed node kinds defined in the edge registration.
698
901
  */
699
902
  type TypedNodeRef<N extends NodeType> = Node<N> | Readonly<{
700
- kind: N["name"];
903
+ kind: N["kind"];
701
904
  id: string;
702
905
  }>;
703
906
  /**
@@ -750,6 +953,8 @@ type EdgeCollection<E extends AnyEdgeType, From extends NodeType = NodeType, To
750
953
  create: (from: TypedNodeRef<From>, to: TypedNodeRef<To>, ...args: EdgeCreateArguments<E>) => Promise<Edge<E>>;
751
954
  /** Get an edge by ID */
752
955
  getById: (id: string, options?: QueryOptions) => Promise<Edge<E> | undefined>;
956
+ /** Get multiple edges by ID, preserving input order (undefined for missing) */
957
+ getByIds: (ids: readonly string[], options?: QueryOptions) => Promise<readonly (Edge<E> | undefined)[]>;
753
958
  /** Update an edge's properties */
754
959
  update: (id: string, props: Partial<z.input<E["schema"]>>, options?: Readonly<{
755
960
  validTo?: string;
@@ -770,22 +975,27 @@ type EdgeCollection<E extends AnyEdgeType, From extends NodeType = NodeType, To
770
975
  * Consider using soft delete (`delete()`) for most use cases.
771
976
  */
772
977
  hardDelete: (id: string) => Promise<void>;
773
- /** Find edges matching criteria */
978
+ /** Find edges matching endpoint and pagination criteria */
774
979
  find: (options?: Readonly<{
775
980
  from?: TypedNodeRef<From>;
776
981
  to?: TypedNodeRef<To>;
777
982
  limit?: number;
778
983
  offset?: number;
984
+ temporalMode?: TemporalMode;
985
+ asOf?: string;
779
986
  }>) => Promise<Edge<E>[]>;
780
987
  /** Count edges matching criteria */
781
988
  count: (options?: Readonly<{
782
989
  from?: TypedNodeRef<From>;
783
990
  to?: TypedNodeRef<To>;
991
+ temporalMode?: TemporalMode;
992
+ asOf?: string;
784
993
  }>) => Promise<number>;
785
994
  /**
786
995
  * Create multiple edges in a batch.
787
996
  *
788
997
  * More efficient than calling create() multiple times.
998
+ * Use `bulkInsert` for the dedicated fast path that skips returning results.
789
999
  */
790
1000
  bulkCreate: (items: readonly Readonly<{
791
1001
  from: TypedNodeRef<From>;
@@ -793,463 +1003,83 @@ type EdgeCollection<E extends AnyEdgeType, From extends NodeType = NodeType, To
793
1003
  props?: z.input<E["schema"]>;
794
1004
  id?: string;
795
1005
  validFrom?: string;
796
- validTo?: string;
797
- }>[]) => Promise<Edge<E>[]>;
798
- /**
799
- * Delete multiple edges by ID.
800
- *
801
- * Silently ignores IDs that don't exist.
802
- */
803
- bulkDelete: (ids: readonly string[]) => Promise<void>;
804
- }>;
805
- /**
806
- * Extract the union of 'from' node types from an EdgeRegistration.
807
- */
808
- type EdgeFromTypes<R extends EdgeRegistration> = R["from"] extends readonly (infer N)[] ? N : never;
809
- /**
810
- * Extract the union of 'to' node types from an EdgeRegistration.
811
- */
812
- type EdgeToTypes<R extends EdgeRegistration> = R["to"] extends readonly (infer N)[] ? N : never;
813
- /**
814
- * Create a type-safe EdgeCollection from an EdgeRegistration.
815
- * Extracts the edge type and from/to node types automatically.
816
- */
817
- type TypedEdgeCollection<R extends EdgeRegistration> = EdgeCollection<R["type"], EdgeFromTypes<R> extends NodeType ? EdgeFromTypes<R> : NodeType, EdgeToTypes<R> extends NodeType ? EdgeToTypes<R> : NodeType>;
818
- /**
819
- * A typed transaction context with collection API.
820
- *
821
- * Provides the same `tx.nodes.*` and `tx.edges.*` API as the Store,
822
- * but operations are executed within the transaction scope.
823
- *
824
- * @example
825
- * ```typescript
826
- * await store.transaction(async (tx) => {
827
- * const person = await tx.nodes.Person.create({ name: "Alice" });
828
- * const company = await tx.nodes.Company.create({ name: "Acme" });
829
- * // Pass nodes directly - their kind and id properties are used
830
- * await tx.edges.worksAt.create(person, company, { role: "Engineer" });
831
- * });
832
- * ```
833
- */
834
- type TransactionContext<G extends GraphDef> = Readonly<{
835
- /** Node collections for the transaction */
836
- nodes: {
837
- [K in keyof G["nodes"] & string]-?: NodeCollection<G["nodes"][K]["type"]>;
838
- };
839
- /** Edge collections for the transaction */
840
- edges: {
841
- [K in keyof G["edges"] & string]-?: TypedEdgeCollection<G["edges"][K]>;
842
- };
843
- }>;
844
-
845
- type FieldTypeInfo = Readonly<{
846
- valueType: ValueType;
847
- elementType?: ValueType | undefined;
848
- elementTypeInfo?: FieldTypeInfo | undefined;
849
- shape?: Readonly<Record<string, FieldTypeInfo>> | undefined;
850
- recordValueType?: FieldTypeInfo | undefined;
851
- /** For embedding types: the number of dimensions */
852
- dimensions?: number | undefined;
853
- }>;
854
- type SchemaIntrospector = Readonly<{
855
- getFieldTypeInfo: (kindName: string, fieldName: string) => FieldTypeInfo | undefined;
856
- getSharedFieldTypeInfo: (kindNames: readonly string[], fieldName: string) => FieldTypeInfo | undefined;
857
- getEdgeFieldTypeInfo: (edgeKindName: string, fieldName: string) => FieldTypeInfo | undefined;
858
- getSharedEdgeFieldTypeInfo: (edgeKindNames: readonly string[], fieldName: string) => FieldTypeInfo | undefined;
859
- }>;
860
-
861
- /**
862
- * Predicate builders for TypeGraph queries.
863
- *
864
- * Provides a fluent API for building type-safe predicates.
865
- */
866
-
867
- /**
868
- * A chainable predicate that can be combined with AND/OR.
869
- */
870
- type Predicate = Readonly<{
871
- __expr: PredicateExpression;
872
- and: (other: Predicate) => Predicate;
873
- or: (other: Predicate) => Predicate;
874
- not: () => Predicate;
875
- }>;
876
- /**
877
- * Options for the similarTo method.
878
- */
879
- type SimilarToOptions = Readonly<{
880
- /** Similarity metric to use. Default: "cosine" */
881
- metric?: VectorMetricType;
882
- /**
883
- * Minimum similarity score to include results.
884
- * For cosine: 0-1 where 1 is identical.
885
- * For L2: maximum distance to include.
886
- * For inner_product: minimum inner product value.
887
- */
888
- minScore?: number;
889
- }>;
890
- /**
891
- * Creates a field reference.
892
- */
893
- type FieldRefOptions = Readonly<{
894
- jsonPointer?: JsonPointer | undefined;
895
- valueType?: ValueType | undefined;
896
- elementType?: ValueType | undefined;
897
- }>;
898
- declare function fieldRef(alias: string, path: readonly string[], options?: FieldRefOptions): FieldRef;
899
- /**
900
- * Creates an EXISTS subquery predicate.
901
- * Returns true if the subquery returns at least one row.
902
- *
903
- * @param subquery - The subquery AST to check for existence
904
- *
905
- * @example
906
- * ```typescript
907
- * // Find persons who have at least one order
908
- * query
909
- * .from("Person", "p")
910
- * .whereNode("p", () =>
911
- * exists(
912
- * query.from("Order", "o")
913
- * .whereNode("o", (o) => o.customerId.eq(field("p.id")))
914
- * .select((ctx) => ({ id: ctx.o.id }))
915
- * .toAst()
916
- * )
917
- * )
918
- * ```
919
- */
920
- declare function exists(subquery: QueryAst): Predicate;
921
- /**
922
- * Creates a NOT EXISTS subquery predicate.
923
- * Returns true if the subquery returns no rows.
924
- *
925
- * @param subquery - The subquery AST to check for non-existence
926
- */
927
- declare function notExists(subquery: QueryAst): Predicate;
928
- /**
929
- * Creates an IN subquery predicate.
930
- * Returns true if the field value is in the subquery results.
931
- *
932
- * @param field - The field to check
933
- * @param subquery - The subquery AST that returns a single column
934
- *
935
- * @example
936
- * ```typescript
937
- * // Find persons whose ID is in the VIP list
938
- * query
939
- * .from("Person", "p")
940
- * .where(() =>
941
- * inSubquery(
942
- * fieldRef("p", ["id"]),
943
- * query.from("VIPMember", "v")
944
- * .select({ id: field("v.personId") })
945
- * .toAst()
946
- * )
947
- * )
948
- * ```
949
- */
950
- declare function inSubquery(field: FieldRef, subquery: QueryAst): Predicate;
951
- /**
952
- * Creates a NOT IN subquery predicate.
953
- * Returns true if the field value is not in the subquery results.
954
- *
955
- * @param field - The field to check
956
- * @param subquery - The subquery AST that returns a single column
957
- */
958
- declare function notInSubquery(field: FieldRef, subquery: QueryAst): Predicate;
959
-
960
- /**
961
- * Shared type definitions for the query builder.
962
- *
963
- * Contains type definitions used across QueryBuilder, TraversalBuilder,
964
- * and ExecutableQuery classes.
965
- */
966
-
967
- /**
968
- * Extracts the names of valid target node kinds for an edge traversal.
969
- *
970
- * For "out" direction: returns the names of node kinds in the edge's "to" array.
971
- * For "in" direction: returns the names of node kinds in the edge's "from" array.
972
- *
973
- * @example
974
- * // Given: worksAt: { from: [Person], to: [Company, Organization] }
975
- * // ValidEdgeTargets<G, "worksAt", "out"> = "Company" | "Organization"
976
- * // ValidEdgeTargets<G, "worksAt", "in"> = "Person"
977
- */
978
- type ValidEdgeTargets<G extends GraphDef, EK extends keyof G["edges"] & string, Dir extends TraversalDirection> = G["edges"][EK] extends EdgeRegistration ? Dir extends "out" ? G["edges"][EK]["to"][number]["name"] : G["edges"][EK]["from"][number]["name"] : never;
979
- /**
980
- * A node alias with its associated kind.
981
- */
982
- type NodeAlias<K extends NodeType = NodeType, Optional extends boolean = false> = Readonly<{
983
- kind: K;
984
- alias: string;
985
- optional: Optional;
986
- }>;
987
- /**
988
- * A map of alias names to their node aliases.
989
- */
990
- type AliasMap = Readonly<Record<string, NodeAlias<NodeType, boolean>>>;
991
- /**
992
- * An edge alias with its associated kind and optional flag.
993
- */
994
- type EdgeAlias<E extends AnyEdgeType = EdgeType, Optional extends boolean = false> = Readonly<{
995
- kind: E;
996
- alias: string;
997
- optional: Optional;
998
- }>;
999
- /**
1000
- * A map of alias names to their edge aliases.
1001
- */
1002
- type EdgeAliasMap = Readonly<Record<string, EdgeAlias<EdgeType, boolean>>>;
1003
- /**
1004
- * Type utility for compile-time alias collision detection.
1005
- *
1006
- * When A already exists in Aliases, this resolves to an error message type
1007
- * that will cause a type error with a descriptive message.
1008
- */
1009
- type UniqueAlias<A extends string, Aliases extends AliasMap> = A extends keyof Aliases ? `Error: Alias '${A}' is already in use` : A;
1010
- /**
1011
- * Creates typed field accessors for a node kind's properties.
1012
- */
1013
- type PropsAccessor<N extends NodeType> = Readonly<{
1014
- [K in keyof z.infer<N["schema"]>]-?: FieldAccessor<z.infer<N["schema"]>[K]>;
1015
- }>;
1016
- /**
1017
- * A field accessor with type-appropriate predicate methods.
1018
- * Uses NonNullable to handle optional fields correctly.
1019
- */
1020
- type FieldAccessor<T> = FieldAccessorForType<NonNullable<T>>;
1021
- type FieldAccessorForType<T> = [
1022
- T
1023
- ] extends [EmbeddingValue] ? EmbeddingFieldAccessor : [T] extends [string] ? StringFieldAccessor : [T] extends [number] ? NumberFieldAccessor : [T] extends [boolean] ? BooleanFieldAccessor : [T] extends [Date] ? DateFieldAccessor : [T] extends [readonly (infer U)[]] ? ArrayFieldAccessor<U> : [T] extends [Record<string, unknown>] ? ObjectFieldAccessor<T> : BaseFieldAccessor;
1024
- type BaseFieldAccessor = Readonly<{
1025
- eq: (value: unknown) => Predicate;
1026
- neq: (value: unknown) => Predicate;
1027
- isNull: () => Predicate;
1028
- isNotNull: () => Predicate;
1029
- in: (values: readonly unknown[]) => Predicate;
1030
- notIn: (values: readonly unknown[]) => Predicate;
1031
- }>;
1032
- type StringFieldAccessor = BaseFieldAccessor & Readonly<{
1033
- contains: (pattern: string) => Predicate;
1034
- startsWith: (pattern: string) => Predicate;
1035
- endsWith: (pattern: string) => Predicate;
1036
- like: (pattern: string) => Predicate;
1037
- ilike: (pattern: string) => Predicate;
1038
- }>;
1039
- type NumberFieldAccessor = BaseFieldAccessor & Readonly<{
1040
- gt: (value: number) => Predicate;
1041
- gte: (value: number) => Predicate;
1042
- lt: (value: number) => Predicate;
1043
- lte: (value: number) => Predicate;
1044
- between: (lower: number, upper: number) => Predicate;
1045
- }>;
1046
- type BooleanFieldAccessor = BaseFieldAccessor;
1047
- type DateFieldAccessor = BaseFieldAccessor & Readonly<{
1048
- gt: (value: Date | string) => Predicate;
1049
- gte: (value: Date | string) => Predicate;
1050
- lt: (value: Date | string) => Predicate;
1051
- lte: (value: Date | string) => Predicate;
1052
- between: (lower: Date | string, upper: Date | string) => Predicate;
1053
- }>;
1054
- type ArrayFieldAccessor<U> = BaseFieldAccessor & Readonly<{
1055
- contains: (value: U) => Predicate;
1056
- containsAny: (values: readonly U[]) => Predicate;
1057
- containsAll: (values: readonly U[]) => Predicate;
1058
- length: NumberFieldAccessor;
1059
- isEmpty: () => Predicate;
1060
- isNotEmpty: () => Predicate;
1061
- lengthEq: (length: number) => Predicate;
1062
- lengthGt: (length: number) => Predicate;
1063
- lengthGte: (length: number) => Predicate;
1064
- lengthLt: (length: number) => Predicate;
1065
- lengthLte: (length: number) => Predicate;
1066
- }>;
1067
- type EmbeddingFieldAccessor = BaseFieldAccessor & Readonly<{
1068
- /**
1069
- * Finds the k most similar items using vector similarity.
1070
- *
1071
- * @param queryEmbedding - The query vector to compare against
1072
- * @param k - Maximum number of results to return
1073
- * @param options - Optional metric and minimum score filter
1074
- */
1075
- similarTo: (queryEmbedding: readonly number[], k: number, options?: SimilarToOptions) => Predicate;
1076
- }>;
1077
- type ObjectFieldAccessor<T> = BaseFieldAccessor & Readonly<{
1078
- get: <K extends keyof T & string>(key: K) => T[K] extends Record<string, unknown> ? ObjectFieldAccessor<T[K]> : FieldAccessor<T[K]>;
1079
- hasKey: (key: string) => Predicate;
1080
- hasPath: <P extends JsonPointerInput<T>>(pointer: P) => Predicate;
1081
- pathEquals: <P extends JsonPointerInput<T>>(pointer: P, value: string | number | boolean | Date) => Predicate;
1082
- pathContains: <P extends JsonPointerInput<T>>(pointer: P, value: string | number | boolean | Date) => Predicate;
1083
- pathIsNull: <P extends JsonPointerInput<T>>(pointer: P) => Predicate;
1084
- pathIsNotNull: <P extends JsonPointerInput<T>>(pointer: P) => Predicate;
1085
- }>;
1086
- /**
1087
- * Node accessor for predicate building.
1088
- *
1089
- * Properties are available at the top level for ergonomic access:
1090
- * - `n.name` instead of `n.props.name`
1091
- * - System fields: `n.id`, `n.kind`
1092
- */
1093
- type NodeAccessor<N extends NodeType> = Readonly<{
1094
- id: StringFieldAccessor;
1095
- kind: StringFieldAccessor;
1096
- }> & PropsAccessor<N>;
1097
- /**
1098
- * Creates typed field accessors for an edge kind's properties.
1099
- */
1100
- type EdgePropsAccessor<E extends AnyEdgeType> = Readonly<{
1101
- [K in keyof z.infer<E["schema"]>]-?: FieldAccessor<z.infer<E["schema"]>[K]>;
1102
- }>;
1103
- /**
1104
- * Edge accessor for predicate building.
1105
- *
1106
- * Properties are available at the top level for ergonomic access:
1107
- * - `e.role` instead of `e.props.role`
1108
- * - System fields: `e.id`, `e.kind`, `e.fromId`, `e.toId`
1109
- */
1110
- type EdgeAccessor<E extends AnyEdgeType> = Readonly<{
1111
- id: StringFieldAccessor;
1112
- kind: StringFieldAccessor;
1113
- fromId: StringFieldAccessor;
1114
- toId: StringFieldAccessor;
1115
- }> & EdgePropsAccessor<E>;
1116
- /**
1117
- * Metadata for a selectable node result.
1118
- */
1119
- type SelectableNodeMeta = Readonly<{
1120
- version: number;
1121
- validFrom: string | undefined;
1122
- validTo: string | undefined;
1123
- createdAt: string;
1124
- updatedAt: string;
1125
- deletedAt: string | undefined;
1126
- }>;
1127
- /**
1128
- * A selectable node result.
1129
- *
1130
- * Properties from the schema are spread at the top level for ergonomic access:
1131
- * - `node.name` instead of `node.props.name`
1132
- * - System metadata is under `node.meta.*`
1133
- */
1134
- type SelectableNode<N extends NodeType> = Readonly<{
1135
- id: string;
1136
- kind: N["name"];
1137
- meta: SelectableNodeMeta;
1138
- }> & Readonly<z.infer<N["schema"]>>;
1139
- /**
1140
- * Metadata for a selectable edge result.
1141
- */
1142
- type SelectableEdgeMeta = Readonly<{
1143
- validFrom: string | undefined;
1144
- validTo: string | undefined;
1145
- createdAt: string;
1146
- updatedAt: string;
1147
- deletedAt: string | undefined;
1148
- }>;
1149
- /**
1150
- * A selectable edge result.
1151
- *
1152
- * Properties from the schema are spread at the top level for ergonomic access:
1153
- * - `edge.role` instead of `edge.props.role`
1154
- * - System metadata is under `edge.meta.*`
1155
- */
1156
- type SelectableEdge<E extends AnyEdgeType = EdgeType> = Readonly<{
1157
- id: string;
1158
- kind: E["name"];
1159
- fromId: string;
1160
- toId: string;
1161
- meta: SelectableEdgeMeta;
1162
- }> & Readonly<z.infer<E["schema"]>>;
1163
- /**
1164
- * Selection context passed to select callback.
1165
- *
1166
- * Includes both node aliases and edge aliases. Edge aliases from optional
1167
- * traversals are nullable.
1168
- */
1169
- type SelectContext<Aliases extends AliasMap, EdgeAliases extends EdgeAliasMap = Record<string, never>> = Readonly<{
1170
- [A in keyof Aliases]: Aliases[A]["optional"] extends true ? SelectableNode<Aliases[A]["kind"]> | undefined : SelectableNode<Aliases[A]["kind"]>;
1171
- }> & Readonly<{
1172
- [EA in keyof EdgeAliases]: EdgeAliases[EA]["optional"] extends true ? SelectableEdge<EdgeAliases[EA]["kind"]> | undefined : SelectableEdge<EdgeAliases[EA]["kind"]>;
1173
- }>;
1174
- /**
1175
- * Result of a paginated query.
1176
- */
1177
- type PaginatedResult<R> = Readonly<{
1178
- /** The data items for this page */
1179
- data: readonly R[];
1180
- /** Cursor to fetch the next page (undefined if no more pages) */
1181
- nextCursor: string | undefined;
1182
- /** Cursor to fetch the previous page (undefined if on first page) */
1183
- prevCursor: string | undefined;
1184
- /** Whether there are more items after this page */
1185
- hasNextPage: boolean;
1186
- /** Whether there are items before this page */
1187
- hasPrevPage: boolean;
1188
- }>;
1189
- /**
1190
- * Options for cursor-based pagination.
1191
- *
1192
- * Use `first`/`after` for forward pagination, `last`/`before` for backward.
1193
- */
1194
- type PaginateOptions = Readonly<{
1195
- /** Number of items to fetch (forward pagination) */
1196
- first?: number;
1197
- /** Cursor to start after (forward pagination) */
1198
- after?: string;
1199
- /** Number of items to fetch (backward pagination) */
1200
- last?: number;
1201
- /** Cursor to start before (backward pagination) */
1202
- before?: string;
1006
+ validTo?: string;
1007
+ }>[]) => Promise<Edge<E>[]>;
1008
+ /**
1009
+ * Create or update multiple edges in a batch.
1010
+ *
1011
+ * For each item, if an edge with the given ID exists, updates it.
1012
+ * Otherwise, creates a new edge with that ID.
1013
+ */
1014
+ bulkUpsert: (items: readonly Readonly<{
1015
+ id: string;
1016
+ from: TypedNodeRef<From>;
1017
+ to: TypedNodeRef<To>;
1018
+ props?: z.input<E["schema"]>;
1019
+ validFrom?: string;
1020
+ validTo?: string;
1021
+ }>[]) => Promise<Edge<E>[]>;
1022
+ /**
1023
+ * Insert multiple edges without returning results.
1024
+ *
1025
+ * This is the dedicated fast path for bulk inserts. Unlike `bulkCreate`
1026
+ * with `returnResults: false`, the intent is unambiguous: no results
1027
+ * are returned and the operation is wrapped in a transaction.
1028
+ */
1029
+ bulkInsert: (items: readonly Readonly<{
1030
+ from: TypedNodeRef<From>;
1031
+ to: TypedNodeRef<To>;
1032
+ props?: z.input<E["schema"]>;
1033
+ id?: string;
1034
+ validFrom?: string;
1035
+ validTo?: string;
1036
+ }>[]) => Promise<void>;
1037
+ /**
1038
+ * Delete multiple edges by ID.
1039
+ *
1040
+ * Atomic when the backend supports transactions. Silently ignores IDs
1041
+ * that don't exist.
1042
+ */
1043
+ bulkDelete: (ids: readonly string[]) => Promise<void>;
1203
1044
  }>;
1204
1045
  /**
1205
- * Options for streaming results.
1046
+ * Extract the union of 'from' node types from an EdgeRegistration.
1206
1047
  */
1207
- type StreamOptions = Readonly<{
1208
- /** Number of items to fetch per batch (default: 1000) */
1209
- batchSize?: number;
1210
- }>;
1048
+ type EdgeFromTypes<R extends EdgeRegistration> = R["from"] extends readonly (infer N)[] ? N : never;
1211
1049
  /**
1212
- * Configuration for the query builder.
1050
+ * Extract the union of 'to' node types from an EdgeRegistration.
1213
1051
  */
1214
- type QueryBuilderConfig = Readonly<{
1215
- graphId: string;
1216
- registry: KindRegistry;
1217
- schemaIntrospector: SchemaIntrospector;
1218
- backend?: GraphBackend;
1219
- dialect?: SqlDialect;
1220
- /** SQL schema configuration for custom table names. */
1221
- schema?: SqlSchema;
1222
- }>;
1052
+ type EdgeToTypes<R extends EdgeRegistration> = R["to"] extends readonly (infer N)[] ? N : never;
1223
1053
  /**
1224
- * Internal state of the query builder.
1054
+ * Create a type-safe EdgeCollection from an EdgeRegistration.
1055
+ * Extracts the edge type and from/to node types automatically.
1225
1056
  */
1226
- type QueryBuilderState = Readonly<{
1227
- startAlias: string;
1228
- startKinds: readonly string[];
1229
- /** The current alias (last traversal target, or startAlias if no traversals) */
1230
- currentAlias: string;
1231
- includeSubClasses: boolean;
1232
- traversals: readonly Traversal[];
1233
- predicates: readonly NodePredicate[];
1234
- projection: readonly ProjectedField[];
1235
- orderBy: readonly OrderSpec[];
1236
- limit: number | undefined;
1237
- offset: number | undefined;
1238
- temporalMode: TemporalMode;
1239
- asOf: string | undefined;
1240
- groupBy: GroupBySpec | undefined;
1241
- having: PredicateExpression | undefined;
1242
- }>;
1057
+ type TypedEdgeCollection<R extends EdgeRegistration> = EdgeCollection<R["type"], EdgeFromTypes<R> extends NodeType ? EdgeFromTypes<R> : NodeType, EdgeToTypes<R> extends NodeType ? EdgeToTypes<R> : NodeType>;
1243
1058
  /**
1244
- * Options for creating a query builder.
1059
+ * A typed transaction context with collection API.
1060
+ *
1061
+ * Provides the same `tx.nodes.*` and `tx.edges.*` API as the Store,
1062
+ * but operations are executed within the transaction scope.
1063
+ *
1064
+ * @example
1065
+ * ```typescript
1066
+ * await store.transaction(async (tx) => {
1067
+ * const person = await tx.nodes.Person.create({ name: "Alice" });
1068
+ * const company = await tx.nodes.Company.create({ name: "Acme" });
1069
+ * // Pass nodes directly - their kind and id properties are used
1070
+ * await tx.edges.worksAt.create(person, company, { role: "Engineer" });
1071
+ * });
1072
+ * ```
1245
1073
  */
1246
- type CreateQueryBuilderOptions = Readonly<{
1247
- /** Backend for query execution */
1248
- backend?: GraphBackend;
1249
- /** SQL dialect for compilation */
1250
- dialect?: SqlDialect;
1251
- /** SQL schema configuration for custom table names */
1252
- schema?: SqlSchema;
1074
+ type TransactionContext<G extends GraphDef> = Readonly<{
1075
+ /** Node collections for the transaction */
1076
+ nodes: {
1077
+ [K in keyof G["nodes"] & string]-?: NodeCollection<G["nodes"][K]["type"]>;
1078
+ };
1079
+ /** Edge collections for the transaction */
1080
+ edges: {
1081
+ [K in keyof G["edges"] & string]-?: TypedEdgeCollection<G["edges"][K]>;
1082
+ };
1253
1083
  }>;
1254
1084
 
1255
1085
  /**
@@ -1281,6 +1111,16 @@ declare class ExecutableAggregateQuery<G extends GraphDef, Aliases extends Alias
1281
1111
  * Offsets the results.
1282
1112
  */
1283
1113
  offset(n: number): ExecutableAggregateQuery<G, Aliases, R>;
1114
+ /**
1115
+ * Compiles the query and returns the SQL text and parameters.
1116
+ *
1117
+ * Requires a backend to be configured (the backend determines the SQL dialect).
1118
+ * Use this for debugging, logging, or running the query with a custom executor.
1119
+ */
1120
+ toSQL(): Readonly<{
1121
+ sql: string;
1122
+ params: readonly unknown[];
1123
+ }>;
1284
1124
  /**
1285
1125
  * Compiles the query to a Drizzle SQL object.
1286
1126
  */
@@ -1293,6 +1133,52 @@ declare class ExecutableAggregateQuery<G extends GraphDef, Aliases extends Alias
1293
1133
  execute(): Promise<readonly AggregateResult<R>[]>;
1294
1134
  }
1295
1135
 
1136
+ type PreparedQueryConfig<R> = Readonly<{
1137
+ ast: QueryAst;
1138
+ unoptimizedAst: QueryAst;
1139
+ sqlText: string | undefined;
1140
+ sqlParams: readonly unknown[] | undefined;
1141
+ unoptimizedSqlText: string | undefined;
1142
+ unoptimizedSqlParams: readonly unknown[] | undefined;
1143
+ backend: GraphBackend;
1144
+ dialect: SqlDialect;
1145
+ graphId: string;
1146
+ compileOptions: CompileQueryOptions;
1147
+ state: QueryBuilderState;
1148
+ selectiveFields: readonly SelectiveField[] | undefined;
1149
+ selectFn: (context: SelectContext<AliasMap, EdgeAliasMap>) => R;
1150
+ schemaIntrospector: SchemaIntrospector;
1151
+ }>;
1152
+ /**
1153
+ * A pre-compiled, parameterized query.
1154
+ *
1155
+ * @example
1156
+ * ```typescript
1157
+ * const prepared = store.query()
1158
+ * .from("Person", "p")
1159
+ * .whereNode("p", (p) => p.name.eq(param("name")))
1160
+ * .select((ctx) => ctx.p)
1161
+ * .prepare();
1162
+ *
1163
+ * // Execute with different bindings
1164
+ * const alice = await prepared.execute({ name: "Alice" });
1165
+ * const bob = await prepared.execute({ name: "Bob" });
1166
+ * ```
1167
+ */
1168
+ declare class PreparedQuery<R> {
1169
+ #private;
1170
+ constructor(config: PreparedQueryConfig<R>);
1171
+ /** The set of parameter names required by this prepared query. */
1172
+ get parameterNames(): ReadonlySet<string>;
1173
+ /**
1174
+ * Executes the prepared query with the given parameter bindings.
1175
+ *
1176
+ * @param bindings - A record mapping parameter names to their values
1177
+ * @returns The query results
1178
+ */
1179
+ execute(bindings?: Readonly<Record<string, unknown>>): Promise<readonly R[]>;
1180
+ }
1181
+
1296
1182
  /**
1297
1183
  * UnionableQuery - A query formed by combining multiple queries with set operations.
1298
1184
  */
@@ -1348,6 +1234,16 @@ declare class UnionableQuery<G extends GraphDef, R> {
1348
1234
  * Builds the set operation AST.
1349
1235
  */
1350
1236
  toAst(): SetOperation;
1237
+ /**
1238
+ * Compiles the query and returns the SQL text and parameters.
1239
+ *
1240
+ * Requires a backend to be configured (the backend determines the SQL dialect).
1241
+ * Use this for debugging, logging, or running the query with a custom executor.
1242
+ */
1243
+ toSQL(): Readonly<{
1244
+ sql: string;
1245
+ params: readonly unknown[];
1246
+ }>;
1351
1247
  /**
1352
1248
  * Compiles the set operation to SQL.
1353
1249
  */
@@ -1365,9 +1261,9 @@ declare class UnionableQuery<G extends GraphDef, R> {
1365
1261
  /**
1366
1262
  * A query that can be executed.
1367
1263
  */
1368
- declare class ExecutableQuery<G extends GraphDef, Aliases extends AliasMap, EdgeAliases extends EdgeAliasMap = {}, R = unknown> {
1264
+ declare class ExecutableQuery<G extends GraphDef, Aliases extends AliasMap, EdgeAliases extends EdgeAliasMap = {}, RecursiveAliases extends RecursiveAliasMap = {}, R = unknown> {
1369
1265
  #private;
1370
- constructor(config: QueryBuilderConfig, state: QueryBuilderState, selectFunction: (context: SelectContext<Aliases, EdgeAliases>) => R);
1266
+ constructor(config: QueryBuilderConfig, state: QueryBuilderState, selectFunction: (context: SelectContext<Aliases, EdgeAliases, RecursiveAliases>) => R);
1371
1267
  /**
1372
1268
  * Builds the query AST.
1373
1269
  */
@@ -1375,15 +1271,15 @@ declare class ExecutableQuery<G extends GraphDef, Aliases extends AliasMap, Edge
1375
1271
  /**
1376
1272
  * Orders results.
1377
1273
  */
1378
- orderBy<A extends keyof Aliases & string>(alias: A, field: string, direction?: SortDirection): ExecutableQuery<G, Aliases, EdgeAliases, R>;
1274
+ orderBy<A extends keyof Aliases & string>(alias: A, field: string, direction?: SortDirection): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>;
1379
1275
  /**
1380
1276
  * Limits the number of results.
1381
1277
  */
1382
- limit(n: number): ExecutableQuery<G, Aliases, EdgeAliases, R>;
1278
+ limit(n: number): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>;
1383
1279
  /**
1384
1280
  * Offsets the results.
1385
1281
  */
1386
- offset(n: number): ExecutableQuery<G, Aliases, EdgeAliases, R>;
1282
+ offset(n: number): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>;
1387
1283
  /**
1388
1284
  * Applies a query fragment to transform this executable query.
1389
1285
  *
@@ -1404,23 +1300,33 @@ declare class ExecutableQuery<G extends GraphDef, Aliases extends AliasMap, Edge
1404
1300
  * @param fragment - A function that transforms the executable query
1405
1301
  * @returns The transformed executable query
1406
1302
  */
1407
- pipe<NewR = R>(fragment: (query: ExecutableQuery<G, Aliases, EdgeAliases, R>) => ExecutableQuery<G, Aliases, EdgeAliases, NewR>): ExecutableQuery<G, Aliases, EdgeAliases, NewR>;
1303
+ pipe<NewR = R>(fragment: (query: ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>) => ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, NewR>): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, NewR>;
1408
1304
  /**
1409
1305
  * Combines this query with another using UNION (removes duplicates).
1410
1306
  */
1411
- union(other: ExecutableQuery<G, any, any, R>): UnionableQuery<G, R>;
1307
+ union(other: ExecutableQuery<G, any, any, any, R>): UnionableQuery<G, R>;
1412
1308
  /**
1413
1309
  * Combines this query with another using UNION ALL (keeps duplicates).
1414
1310
  */
1415
- unionAll(other: ExecutableQuery<G, any, any, R>): UnionableQuery<G, R>;
1311
+ unionAll(other: ExecutableQuery<G, any, any, any, R>): UnionableQuery<G, R>;
1416
1312
  /**
1417
1313
  * Combines this query with another using INTERSECT.
1418
1314
  */
1419
- intersect(other: ExecutableQuery<G, any, any, R>): UnionableQuery<G, R>;
1315
+ intersect(other: ExecutableQuery<G, any, any, any, R>): UnionableQuery<G, R>;
1420
1316
  /**
1421
1317
  * Combines this query with another using EXCEPT.
1422
1318
  */
1423
- except(other: ExecutableQuery<G, any, any, R>): UnionableQuery<G, R>;
1319
+ except(other: ExecutableQuery<G, any, any, any, R>): UnionableQuery<G, R>;
1320
+ /**
1321
+ * Compiles the query and returns the SQL text and parameters.
1322
+ *
1323
+ * Requires a backend to be configured (the backend determines the SQL dialect).
1324
+ * Use this for debugging, logging, or running the query with a custom executor.
1325
+ */
1326
+ toSQL(): Readonly<{
1327
+ sql: string;
1328
+ params: readonly unknown[];
1329
+ }>;
1424
1330
  /**
1425
1331
  * Compiles the query to a Drizzle SQL object.
1426
1332
  *
@@ -1428,6 +1334,30 @@ declare class ExecutableQuery<G extends GraphDef, Aliases extends AliasMap, Edge
1428
1334
  * with db.all(), db.get(), etc.
1429
1335
  */
1430
1336
  compile(): SQL;
1337
+ /**
1338
+ * Creates a prepared (pre-compiled) query that can be executed
1339
+ * multiple times with different parameter bindings.
1340
+ *
1341
+ * Use `param("name")` in predicates to create parameterized slots,
1342
+ * then pass values via `prepared.execute({ name: "value" })`.
1343
+ *
1344
+ * @example
1345
+ * ```typescript
1346
+ * import { param } from "@nicia-ai/typegraph";
1347
+ *
1348
+ * const prepared = store.query()
1349
+ * .from("Person", "p")
1350
+ * .whereNode("p", (p) => p.name.eq(param("name")))
1351
+ * .select((ctx) => ctx.p)
1352
+ * .prepare();
1353
+ *
1354
+ * const alice = await prepared.execute({ name: "Alice" });
1355
+ * const bob = await prepared.execute({ name: "Bob" });
1356
+ * ```
1357
+ *
1358
+ * @throws Error if no backend is configured
1359
+ */
1360
+ prepare(): PreparedQuery<R>;
1431
1361
  /**
1432
1362
  * Executes the query and returns typed results.
1433
1363
  *
@@ -1473,8 +1403,10 @@ interface VariableLengthState {
1473
1403
  enabled: boolean;
1474
1404
  minDepth: number;
1475
1405
  maxDepth: number;
1476
- collectPath: boolean;
1406
+ cyclePolicy: RecursiveCyclePolicy;
1407
+ pathEnabled: boolean;
1477
1408
  pathAlias?: string;
1409
+ depthEnabled: boolean;
1478
1410
  depthAlias?: string;
1479
1411
  }
1480
1412
  /**
@@ -1483,41 +1415,25 @@ interface VariableLengthState {
1483
1415
  * Type parameters track the edge kind and direction to constrain
1484
1416
  * which node kinds are valid targets in the `to()` method.
1485
1417
  */
1486
- declare class TraversalBuilder<G extends GraphDef, Aliases extends AliasMap, EdgeAliases extends EdgeAliasMap = {}, EK extends keyof G["edges"] & string = keyof G["edges"] & string, EA extends string = string, Dir extends TraversalDirection = "out", Optional extends boolean = false> {
1418
+ declare class TraversalBuilder<G extends GraphDef, Aliases extends AliasMap, EdgeAliases extends EdgeAliasMap = {}, EK extends keyof G["edges"] & string = keyof G["edges"] & string, EA extends string = string, Dir extends TraversalDirection = "out", Optional extends boolean = false, DC extends boolean | string = false, PC extends boolean | string = false, RecAliases extends RecursiveAliasMap = {}> {
1487
1419
  #private;
1488
- constructor(config: QueryBuilderConfig, state: QueryBuilderState, edgeKinds: readonly string[], edgeAlias: EA, direction: Dir, fromAlias: string, optional?: Optional, variableLength?: VariableLengthState, pendingEdgePredicates?: readonly NodePredicate[]);
1420
+ constructor(config: QueryBuilderConfig, state: QueryBuilderState, edgeKinds: readonly string[], edgeAlias: EA, direction: Dir, fromAlias: string, inverseEdgeKinds?: readonly string[], optional?: Optional, variableLength?: VariableLengthState, pendingEdgePredicates?: readonly NodePredicate[]);
1489
1421
  /**
1490
1422
  * Enables variable-length (recursive) traversal.
1491
- * By default, traverses unlimited depth with cycle detection.
1492
- */
1493
- recursive(): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, Dir, Optional>;
1494
- /**
1495
- * Sets the maximum traversal depth.
1496
- * @param max Maximum number of hops (must be >= 1)
1497
- */
1498
- maxHops(max: number): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, Dir, Optional>;
1499
- /**
1500
- * Sets the minimum traversal depth (skip nodes closer than this).
1501
- * @param min Minimum hops before including results (default: 1)
1502
- */
1503
- minHops(min: number): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, Dir, Optional>;
1504
- /**
1505
- * Includes the traversal path as an array in results.
1506
- * @param alias Column alias for the path array (default: "{nodeAlias}_path")
1507
- */
1508
- collectPath(alias?: string): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, Dir, Optional>;
1509
- /**
1510
- * Includes the traversal depth in results.
1511
- * @param alias Column alias for the depth (default: "{nodeAlias}_depth")
1423
+ * By default, traverses unlimited depth with cycle prevention.
1512
1424
  */
1513
- withDepth(alias?: string): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, Dir, Optional>;
1425
+ recursive<const O extends RecursiveTraversalOptions = Record<string, never>>(options?: O): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, Dir, Optional, O extends {
1426
+ depth: infer D extends boolean | string;
1427
+ } ? D : DC, O extends {
1428
+ path: infer P extends boolean | string;
1429
+ } ? P : PC, RecAliases>;
1514
1430
  /**
1515
1431
  * Adds a WHERE clause for the edge being traversed.
1516
1432
  *
1517
1433
  * @param alias - The edge alias to filter on (must be the current edge alias)
1518
1434
  * @param predicateFunction - A function that builds predicates using the edge accessor
1519
1435
  */
1520
- whereEdge(alias: EA, predicateFunction: (edge: EdgeAccessor<G["edges"][EK]["type"]>) => Predicate): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, Dir, Optional>;
1436
+ whereEdge(alias: EA, predicateFunction: (edge: EdgeAccessor<G["edges"][EK]["type"]>) => Predicate): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, Dir, Optional, DC, PC, RecAliases>;
1521
1437
  /**
1522
1438
  * Specifies the target node kind.
1523
1439
  *
@@ -1530,10 +1446,10 @@ declare class TraversalBuilder<G extends GraphDef, Aliases extends AliasMap, Edg
1530
1446
  */
1531
1447
  to<K extends ValidEdgeTargets<G, EK, Dir>, A extends string>(kind: K, alias: UniqueAlias<A, Aliases>, options?: {
1532
1448
  includeSubClasses?: false;
1533
- }): QueryBuilder<G, Aliases & Record<A, NodeAlias<G["nodes"][K]["type"], Optional>>, EdgeAliases & Record<EA, EdgeAlias<G["edges"][EK]["type"], Optional>>>;
1449
+ }): QueryBuilder<G, Aliases & Record<A, NodeAlias<G["nodes"][K]["type"], Optional>>, EdgeAliases & Record<EA, EdgeAlias<G["edges"][EK]["type"], Optional>>, RecAliases & BuildRecursiveAliases<DC, PC, A>>;
1534
1450
  to<K extends ValidEdgeTargets<G, EK, Dir>, A extends string>(kind: K, alias: UniqueAlias<A, Aliases>, options: {
1535
1451
  includeSubClasses: true;
1536
- }): QueryBuilder<G, Aliases & Record<A, NodeAlias<NodeType, Optional>>, EdgeAliases & Record<EA, EdgeAlias<G["edges"][EK]["type"], Optional>>>;
1452
+ }): QueryBuilder<G, Aliases & Record<A, NodeAlias<NodeType, Optional>>, EdgeAliases & Record<EA, EdgeAlias<G["edges"][EK]["type"], Optional>>, RecAliases & BuildRecursiveAliases<DC, PC, A>>;
1537
1453
  }
1538
1454
 
1539
1455
  /**
@@ -1548,7 +1464,7 @@ declare class TraversalBuilder<G extends GraphDef, Aliases extends AliasMap, Edg
1548
1464
  * - Aliases: Map of alias names to their node kinds
1549
1465
  * - EdgeAliases: Map of alias names to their edge kinds (accumulated during traversals)
1550
1466
  */
1551
- declare class QueryBuilder<G extends GraphDef, Aliases extends AliasMap = {}, EdgeAliases extends EdgeAliasMap = {}> {
1467
+ declare class QueryBuilder<G extends GraphDef, Aliases extends AliasMap = {}, EdgeAliases extends EdgeAliasMap = {}, RecursiveAliases extends RecursiveAliasMap = {}> {
1552
1468
  #private;
1553
1469
  constructor(config: QueryBuilderConfig, state: QueryBuilderState);
1554
1470
  /**
@@ -1559,35 +1475,35 @@ declare class QueryBuilder<G extends GraphDef, Aliases extends AliasMap = {}, Ed
1559
1475
  */
1560
1476
  from<K extends keyof G["nodes"] & string, A extends string>(kind: K, alias: UniqueAlias<A, Aliases>, options?: {
1561
1477
  includeSubClasses?: false;
1562
- }): QueryBuilder<G, Aliases & Record<A, NodeAlias<G["nodes"][K]["type"]>>, EdgeAliases>;
1478
+ }): QueryBuilder<G, Aliases & Record<A, NodeAlias<G["nodes"][K]["type"]>>, EdgeAliases, RecursiveAliases>;
1563
1479
  from<K extends keyof G["nodes"] & string, A extends string>(kind: K, alias: UniqueAlias<A, Aliases>, options: {
1564
1480
  includeSubClasses: true;
1565
- }): QueryBuilder<G, Aliases & Record<A, NodeAlias>, EdgeAliases>;
1481
+ }): QueryBuilder<G, Aliases & Record<A, NodeAlias>, EdgeAliases, RecursiveAliases>;
1566
1482
  /**
1567
1483
  * Adds a WHERE clause for a node.
1568
1484
  */
1569
- whereNode<A extends keyof Aliases & string>(alias: A, predicateFunction: (n: NodeAccessor<Aliases[A]["kind"]>) => Predicate): QueryBuilder<G, Aliases, EdgeAliases>;
1485
+ whereNode<A extends keyof Aliases & string>(alias: A, predicateFunction: (n: NodeAccessor<Aliases[A]["type"]>) => Predicate): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases>;
1570
1486
  /**
1571
1487
  * Adds a WHERE clause for an edge.
1572
1488
  *
1573
1489
  * @param alias - The edge alias to filter on
1574
1490
  * @param predicateFunction - A function that builds predicates using the edge accessor
1575
1491
  */
1576
- whereEdge<EA extends keyof EdgeAliases & string>(alias: EA, predicateFunction: (edge: EdgeAccessor<EdgeAliases[EA]["kind"]>) => Predicate): QueryBuilder<G, Aliases, EdgeAliases>;
1492
+ whereEdge<EA extends keyof EdgeAliases & string>(alias: EA, predicateFunction: (edge: EdgeAccessor<EdgeAliases[EA]["type"]>) => Predicate): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases>;
1577
1493
  /**
1578
1494
  * Traverses an edge to another node (outgoing direction).
1579
1495
  *
1580
1496
  * By default, traverses from the current node (last traversal target, or start node).
1581
1497
  * Use the `from` option to traverse from a different alias (fan-out pattern).
1582
1498
  *
1583
- * @param options.includeImplyingEdges - If true, also match edges that imply this edge kind
1499
+ * @param options.expand - Ontology expansion mode for implying/inverse edges
1584
1500
  * @param options.from - Alias to traverse from (defaults to current/last traversal target)
1585
1501
  */
1586
1502
  traverse<EK extends keyof G["edges"] & string, EA extends string>(edgeKind: EK, edgeAlias: EA, options?: {
1587
1503
  direction?: "out";
1588
- includeImplyingEdges?: boolean;
1504
+ expand?: TraversalExpansion;
1589
1505
  from?: keyof Aliases & string;
1590
- }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA>;
1506
+ }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "out", false, false, false, RecursiveAliases>;
1591
1507
  /**
1592
1508
  * Traverses an edge to another node (incoming direction).
1593
1509
  *
@@ -1595,14 +1511,14 @@ declare class QueryBuilder<G extends GraphDef, Aliases extends AliasMap = {}, Ed
1595
1511
  * Use the `from` option to traverse from a different alias (fan-out pattern).
1596
1512
  *
1597
1513
  * @param options.direction - Set to "in" for incoming edge traversal
1598
- * @param options.includeImplyingEdges - If true, also match edges that imply this edge kind
1514
+ * @param options.expand - Ontology expansion mode for implying/inverse edges
1599
1515
  * @param options.from - Alias to traverse from (defaults to current/last traversal target)
1600
1516
  */
1601
1517
  traverse<EK extends keyof G["edges"] & string, EA extends string>(edgeKind: EK, edgeAlias: EA, options: {
1602
1518
  direction: "in";
1603
- includeImplyingEdges?: boolean;
1519
+ expand?: TraversalExpansion;
1604
1520
  from?: keyof Aliases & string;
1605
- }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "in">;
1521
+ }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "in", false, false, false, RecursiveAliases>;
1606
1522
  /**
1607
1523
  * Optionally traverses an edge to another node (LEFT JOIN semantics).
1608
1524
  * If no matching edge/node exists, the result will include null values.
@@ -1611,42 +1527,42 @@ declare class QueryBuilder<G extends GraphDef, Aliases extends AliasMap = {}, Ed
1611
1527
  * Use the `from` option to traverse from a different alias (fan-out pattern).
1612
1528
  *
1613
1529
  * @param options.direction - Direction of traversal: "out" (default) or "in"
1614
- * @param options.includeImplyingEdges - If true, also match edges that imply this edge kind
1530
+ * @param options.expand - Ontology expansion mode for implying/inverse edges
1615
1531
  * @param options.from - Alias to traverse from (defaults to current/last traversal target)
1616
1532
  */
1617
1533
  optionalTraverse<EK extends keyof G["edges"] & string, EA extends string>(edgeKind: EK, edgeAlias: EA, options?: {
1618
1534
  direction?: "out";
1619
- includeImplyingEdges?: boolean;
1535
+ expand?: TraversalExpansion;
1620
1536
  from?: keyof Aliases & string;
1621
- }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "out", true>;
1537
+ }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "out", true, false, false, RecursiveAliases>;
1622
1538
  optionalTraverse<EK extends keyof G["edges"] & string, EA extends string>(edgeKind: EK, edgeAlias: EA, options: {
1623
1539
  direction: "in";
1624
- includeImplyingEdges?: boolean;
1540
+ expand?: TraversalExpansion;
1625
1541
  from?: keyof Aliases & string;
1626
- }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "in", true>;
1542
+ }): TraversalBuilder<G, Aliases, EdgeAliases, EK, EA, "in", true, false, false, RecursiveAliases>;
1627
1543
  /**
1628
1544
  * Selects fields to return.
1629
1545
  */
1630
- select<R>(selectFunction: (context: SelectContext<Aliases, EdgeAliases>) => R): ExecutableQuery<G, Aliases, EdgeAliases, R>;
1546
+ select<R>(selectFunction: (context: SelectContext<Aliases, EdgeAliases, RecursiveAliases>) => R): ExecutableQuery<G, Aliases, EdgeAliases, RecursiveAliases, R>;
1631
1547
  /**
1632
1548
  * Selects fields including aggregates.
1633
1549
  * Use with groupBy() for aggregate queries.
1634
1550
  *
1635
1551
  * @param fields - Object mapping output names to field refs or aggregate expressions
1636
1552
  */
1637
- selectAggregate<R extends Record<string, FieldRef | AggregateExpr>>(fields: R): ExecutableAggregateQuery<G, Aliases, R>;
1553
+ aggregate<R extends Record<string, FieldRef | AggregateExpr>>(fields: R): ExecutableAggregateQuery<G, Aliases, R>;
1638
1554
  /**
1639
1555
  * Orders results.
1640
1556
  */
1641
- orderBy<A extends keyof Aliases & string>(alias: A, field: string, direction?: SortDirection): QueryBuilder<G, Aliases, EdgeAliases>;
1557
+ orderBy<A extends keyof Aliases & string>(alias: A, field: string, direction?: SortDirection): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases>;
1642
1558
  /**
1643
1559
  * Limits the number of results.
1644
1560
  */
1645
- limit(n: number): QueryBuilder<G, Aliases, EdgeAliases>;
1561
+ limit(n: number): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases>;
1646
1562
  /**
1647
1563
  * Offsets the results.
1648
1564
  */
1649
- offset(n: number): QueryBuilder<G, Aliases, EdgeAliases>;
1565
+ offset(n: number): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases>;
1650
1566
  /**
1651
1567
  * Sets temporal mode.
1652
1568
  *
@@ -1654,7 +1570,7 @@ declare class QueryBuilder<G extends GraphDef, Aliases extends AliasMap = {}, Ed
1654
1570
  * @param asOf - Required timestamp for "asOf" mode (ISO 8601 string)
1655
1571
  * @throws ValidationError if mode is "asOf" but no timestamp is provided
1656
1572
  */
1657
- temporal(mode: TemporalMode, asOf?: string): QueryBuilder<G, Aliases, EdgeAliases>;
1573
+ temporal(mode: TemporalMode, asOf?: string): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases>;
1658
1574
  /**
1659
1575
  * Groups results by the specified field.
1660
1576
  * Use with aggregate functions like COUNT, SUM, AVG in select().
@@ -1662,21 +1578,21 @@ declare class QueryBuilder<G extends GraphDef, Aliases extends AliasMap = {}, Ed
1662
1578
  * @param alias - The node alias to group by
1663
1579
  * @param field - The field name to group by
1664
1580
  */
1665
- groupBy<A extends keyof Aliases & string>(alias: A, field: string): QueryBuilder<G, Aliases, EdgeAliases>;
1581
+ groupBy<A extends keyof Aliases & string>(alias: A, field: string): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases>;
1666
1582
  /**
1667
1583
  * Groups results by the node ID.
1668
1584
  * Use when you want to group by a complete node rather than a specific field.
1669
1585
  *
1670
1586
  * @param alias - The node alias to group by (uses the node's ID)
1671
1587
  */
1672
- groupByNode<A extends keyof Aliases & string>(alias: A): QueryBuilder<G, Aliases, EdgeAliases>;
1588
+ groupByNode<A extends keyof Aliases & string>(alias: A): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases>;
1673
1589
  /**
1674
1590
  * Filters grouped results using aggregate conditions (HAVING clause).
1675
1591
  * Use after groupBy() to filter based on aggregate values.
1676
1592
  *
1677
1593
  * @param predicate - A predicate expression to filter groups
1678
1594
  */
1679
- having(predicate: PredicateExpression): QueryBuilder<G, Aliases, EdgeAliases>;
1595
+ having(predicate: PredicateExpression): QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases>;
1680
1596
  /**
1681
1597
  * Applies a query fragment to transform this builder.
1682
1598
  *
@@ -1702,336 +1618,9 @@ declare class QueryBuilder<G extends GraphDef, Aliases extends AliasMap = {}, Ed
1702
1618
  * @param fragment - A function that transforms the builder
1703
1619
  * @returns The transformed builder
1704
1620
  */
1705
- pipe<OutAliases extends AliasMap, OutEdgeAliases extends EdgeAliasMap = EdgeAliases>(fragment: (builder: QueryBuilder<G, Aliases, EdgeAliases>) => QueryBuilder<G, OutAliases, OutEdgeAliases>): QueryBuilder<G, OutAliases, OutEdgeAliases>;
1621
+ pipe<OutAliases extends AliasMap, OutEdgeAliases extends EdgeAliasMap = EdgeAliases, OutRecAliases extends RecursiveAliasMap = RecursiveAliases>(fragment: (builder: QueryBuilder<G, Aliases, EdgeAliases, RecursiveAliases>) => QueryBuilder<G, OutAliases, OutEdgeAliases, OutRecAliases>): QueryBuilder<G, OutAliases, OutEdgeAliases, OutRecAliases>;
1706
1622
  }
1707
1623
 
1708
- /**
1709
- * Types for serialized schema storage.
1710
- *
1711
- * These types represent the JSON-serializable format used for
1712
- * homoiconic schema storage in the database.
1713
- */
1714
-
1715
- /**
1716
- * JSON Schema type (subset used by Zod toJSONSchema).
1717
- *
1718
- * This is a simplified version - the actual JSON Schema has many more properties.
1719
- */
1720
- type JsonSchema = Readonly<{
1721
- $schema?: string;
1722
- type?: string | readonly string[];
1723
- properties?: Record<string, JsonSchema>;
1724
- required?: readonly string[];
1725
- items?: JsonSchema;
1726
- additionalProperties?: boolean | JsonSchema;
1727
- enum?: readonly unknown[];
1728
- const?: unknown;
1729
- anyOf?: readonly JsonSchema[];
1730
- oneOf?: readonly JsonSchema[];
1731
- allOf?: readonly JsonSchema[];
1732
- not?: JsonSchema;
1733
- description?: string;
1734
- default?: unknown;
1735
- minimum?: number;
1736
- maximum?: number;
1737
- minLength?: number;
1738
- maxLength?: number;
1739
- pattern?: string;
1740
- format?: string;
1741
- [key: string]: unknown;
1742
- }>;
1743
- /**
1744
- * Serialized representation of a meta-edge.
1745
- */
1746
- type SerializedMetaEdge = Readonly<{
1747
- name: string;
1748
- transitive: boolean;
1749
- symmetric: boolean;
1750
- reflexive: boolean;
1751
- inverse: string | undefined;
1752
- inference: InferenceType;
1753
- description: string | undefined;
1754
- }>;
1755
- /**
1756
- * Serialized representation of an ontology relation.
1757
- */
1758
- type SerializedOntologyRelation = Readonly<{
1759
- metaEdge: string;
1760
- from: string;
1761
- to: string;
1762
- }>;
1763
- /**
1764
- * Precomputed closures stored in the schema for fast runtime lookup.
1765
- */
1766
- type SerializedClosures = Readonly<{
1767
- subClassAncestors: Record<string, readonly string[]>;
1768
- subClassDescendants: Record<string, readonly string[]>;
1769
- broaderClosure: Record<string, readonly string[]>;
1770
- narrowerClosure: Record<string, readonly string[]>;
1771
- equivalenceSets: Record<string, readonly string[]>;
1772
- disjointPairs: readonly string[];
1773
- partOfClosure: Record<string, readonly string[]>;
1774
- hasPartClosure: Record<string, readonly string[]>;
1775
- iriToKind: Record<string, string>;
1776
- edgeInverses: Record<string, string>;
1777
- edgeImplicationsClosure: Record<string, readonly string[]>;
1778
- edgeImplyingClosure: Record<string, readonly string[]>;
1779
- }>;
1780
- /**
1781
- * Complete serialized ontology section.
1782
- */
1783
- type SerializedOntology = Readonly<{
1784
- metaEdges: Record<string, SerializedMetaEdge>;
1785
- relations: readonly SerializedOntologyRelation[];
1786
- closures: SerializedClosures;
1787
- }>;
1788
- /**
1789
- * Serialized representation of a uniqueness constraint.
1790
- */
1791
- type SerializedUniqueConstraint = Readonly<{
1792
- name: string;
1793
- fields: readonly string[];
1794
- where: string | undefined;
1795
- scope: UniquenessScope;
1796
- collation: Collation;
1797
- }>;
1798
- /**
1799
- * Serialized representation of a node kind.
1800
- */
1801
- type SerializedNodeDef = Readonly<{
1802
- name: string;
1803
- properties: JsonSchema;
1804
- uniqueConstraints: readonly SerializedUniqueConstraint[];
1805
- onDelete: DeleteBehavior;
1806
- description: string | undefined;
1807
- }>;
1808
- /**
1809
- * Serialized representation of an edge kind.
1810
- */
1811
- type SerializedEdgeDef = Readonly<{
1812
- name: string;
1813
- fromKinds: readonly string[];
1814
- toKinds: readonly string[];
1815
- properties: JsonSchema;
1816
- cardinality: Cardinality;
1817
- endpointExistence: EndpointExistence;
1818
- description: string | undefined;
1819
- }>;
1820
- /**
1821
- * Complete serialized schema document.
1822
- *
1823
- * This is the format stored in the schema_doc column of
1824
- * typegraph_schema_versions.
1825
- */
1826
- type SerializedSchema = Readonly<{
1827
- graphId: string;
1828
- version: number;
1829
- generatedAt: string;
1830
- nodes: Record<string, SerializedNodeDef>;
1831
- edges: Record<string, SerializedEdgeDef>;
1832
- ontology: SerializedOntology;
1833
- defaults: Readonly<{
1834
- onNodeDelete: DeleteBehavior;
1835
- temporalMode: TemporalMode;
1836
- }>;
1837
- }>;
1838
- /**
1839
- * A schema hash for detecting changes.
1840
- *
1841
- * We hash the schema content (excluding version and generatedAt)
1842
- * to detect if the schema has actually changed.
1843
- */
1844
- type SchemaHash = string;
1845
-
1846
- /**
1847
- * Schema migration utilities.
1848
- *
1849
- * Provides diff detection between schema versions to identify
1850
- * what has changed and what migrations might be needed.
1851
- */
1852
-
1853
- /**
1854
- * Types of changes that can occur in a schema.
1855
- */
1856
- type ChangeType = "added" | "removed" | "modified" | "renamed";
1857
- /**
1858
- * Severity of a change for migration purposes.
1859
- */
1860
- type ChangeSeverity = "safe" | "warning" | "breaking";
1861
- /**
1862
- * A change to a node definition.
1863
- */
1864
- type NodeChange = Readonly<{
1865
- type: ChangeType;
1866
- name: string;
1867
- severity: ChangeSeverity;
1868
- details: string;
1869
- before?: SerializedNodeDef | undefined;
1870
- after?: SerializedNodeDef | undefined;
1871
- }>;
1872
- /**
1873
- * A change to an edge definition.
1874
- */
1875
- type EdgeChange = Readonly<{
1876
- type: ChangeType;
1877
- name: string;
1878
- severity: ChangeSeverity;
1879
- details: string;
1880
- before?: SerializedEdgeDef | undefined;
1881
- after?: SerializedEdgeDef | undefined;
1882
- }>;
1883
- /**
1884
- * A change to the ontology.
1885
- */
1886
- type OntologyChange = Readonly<{
1887
- type: ChangeType;
1888
- entity: "metaEdge" | "relation";
1889
- name: string;
1890
- severity: ChangeSeverity;
1891
- details: string;
1892
- }>;
1893
- /**
1894
- * A complete diff between two schema versions.
1895
- */
1896
- type SchemaDiff = Readonly<{
1897
- fromVersion: number;
1898
- toVersion: number;
1899
- /** Changes to node definitions */
1900
- nodes: readonly NodeChange[];
1901
- /** Changes to edge definitions */
1902
- edges: readonly EdgeChange[];
1903
- /** Changes to ontology */
1904
- ontology: readonly OntologyChange[];
1905
- /** Whether any breaking changes exist */
1906
- hasBreakingChanges: boolean;
1907
- /** Whether any changes exist at all */
1908
- hasChanges: boolean;
1909
- /** Summary of changes */
1910
- summary: string;
1911
- }>;
1912
- /**
1913
- * Computes the diff between two schema versions.
1914
- *
1915
- * @param before - The previous schema version
1916
- * @param after - The new schema version
1917
- * @returns A diff describing all changes
1918
- */
1919
- declare function computeSchemaDiff(before: SerializedSchema, after: SerializedSchema): SchemaDiff;
1920
- /**
1921
- * Checks if a schema change is backwards compatible.
1922
- *
1923
- * A change is backwards compatible if:
1924
- * - No nodes or edges were removed
1925
- * - No required properties were added
1926
- * - No existing properties were removed
1927
- */
1928
- declare function isBackwardsCompatible(diff: SchemaDiff): boolean;
1929
- /**
1930
- * Gets a list of actions needed for migration.
1931
- */
1932
- declare function getMigrationActions(diff: SchemaDiff): readonly string[];
1933
-
1934
- /**
1935
- * Schema manager for TypeGraph.
1936
- *
1937
- * Provides schema lifecycle management:
1938
- * - Initialization on first store creation
1939
- * - Validation on store open
1940
- * - Auto-migration for safe changes
1941
- * - Error reporting for breaking changes
1942
- */
1943
-
1944
- /**
1945
- * Result of schema validation.
1946
- */
1947
- type SchemaValidationResult = {
1948
- status: "initialized";
1949
- version: number;
1950
- } | {
1951
- status: "unchanged";
1952
- version: number;
1953
- } | {
1954
- status: "migrated";
1955
- fromVersion: number;
1956
- toVersion: number;
1957
- diff: SchemaDiff;
1958
- } | {
1959
- status: "breaking";
1960
- diff: SchemaDiff;
1961
- actions: readonly string[];
1962
- };
1963
- /**
1964
- * Options for schema management.
1965
- */
1966
- type SchemaManagerOptions = Readonly<{
1967
- /** If true, auto-migrate safe changes. Default: true */
1968
- autoMigrate?: boolean;
1969
- /** If true, throw on breaking changes. Default: true */
1970
- throwOnBreaking?: boolean;
1971
- }>;
1972
- /**
1973
- * Ensures the schema is initialized and up-to-date.
1974
- *
1975
- * This is the main entry point for schema management. It:
1976
- * 1. Initializes the schema if this is the first run (version 1)
1977
- * 2. Returns "unchanged" if the schema matches the current graph
1978
- * 3. Auto-migrates safe changes if autoMigrate is true
1979
- * 4. Throws MigrationError for breaking changes if throwOnBreaking is true
1980
- *
1981
- * @param backend - The database backend
1982
- * @param graph - The current graph definition
1983
- * @param options - Schema management options
1984
- * @returns The result of schema validation
1985
- * @throws MigrationError if breaking changes detected and throwOnBreaking is true
1986
- */
1987
- declare function ensureSchema<G extends GraphDef>(backend: GraphBackend, graph: G, options?: SchemaManagerOptions): Promise<SchemaValidationResult>;
1988
- /**
1989
- * Initializes the schema for a new graph.
1990
- *
1991
- * Creates version 1 of the schema and marks it as active.
1992
- *
1993
- * @param backend - The database backend
1994
- * @param graph - The graph definition
1995
- * @returns The created schema version row
1996
- */
1997
- declare function initializeSchema<G extends GraphDef>(backend: GraphBackend, graph: G): Promise<SchemaVersionRow>;
1998
- /**
1999
- * Migrates the schema to match the current graph definition.
2000
- *
2001
- * This creates a new schema version and marks it as active.
2002
- * The old version is preserved for history/rollback.
2003
- *
2004
- * @param backend - The database backend
2005
- * @param graph - The current graph definition
2006
- * @param currentVersion - The current active schema version
2007
- * @returns The new version number
2008
- */
2009
- declare function migrateSchema<G extends GraphDef>(backend: GraphBackend, graph: G, currentVersion: number): Promise<number>;
2010
- /**
2011
- * Gets the current active schema for a graph.
2012
- *
2013
- * @param backend - The database backend
2014
- * @param graphId - The graph ID
2015
- * @returns The active schema or undefined if not initialized
2016
- */
2017
- declare function getActiveSchema(backend: GraphBackend, graphId: string): Promise<SerializedSchema | undefined>;
2018
- /**
2019
- * Checks if a graph's schema has been initialized.
2020
- *
2021
- * @param backend - The database backend
2022
- * @param graphId - The graph ID
2023
- * @returns True if the schema has been initialized
2024
- */
2025
- declare function isSchemaInitialized(backend: GraphBackend, graphId: string): Promise<boolean>;
2026
- /**
2027
- * Gets the schema diff between the stored schema and current graph.
2028
- *
2029
- * @param backend - The database backend
2030
- * @param graph - The current graph definition
2031
- * @returns The diff, or undefined if schema not initialized
2032
- */
2033
- declare function getSchemaChanges<G extends GraphDef>(backend: GraphBackend, graph: G): Promise<SchemaDiff | undefined>;
2034
-
2035
1624
  /**
2036
1625
  * Main Store implementation for TypeGraph.
2037
1626
  *
@@ -2221,9 +1810,11 @@ declare function createStore<G extends GraphDef>(graph: G, backend: GraphBackend
2221
1810
  * console.log("Schema initialized at version", result.version);
2222
1811
  * } else if (result.status === "migrated") {
2223
1812
  * console.log(`Migrated from v${result.fromVersion} to v${result.toVersion}`);
1813
+ * } else if (result.status === "pending") {
1814
+ * console.log(`Safe changes pending at version ${result.version}`);
2224
1815
  * }
2225
1816
  * ```
2226
1817
  */
2227
1818
  declare function createStoreWithSchema<G extends GraphDef>(graph: G, backend: GraphBackend, options?: StoreOptions & SchemaManagerOptions): Promise<[Store<G>, SchemaValidationResult]>;
2228
1819
 
2229
- export { type QueryOptions as $, type AliasMap as A, type NodeAlias as B, type CreateQueryBuilderOptions as C, type NodeChange as D, type EdgeAliasMap as E, type FieldAccessor as F, type GraphDef as G, type HookContext as H, type InferenceType as I, type JsonSchema as J, KindRegistry as K, type NodeCollection as L, type MetaEdge as M, type Node as N, type OntologyRelation as O, type NodeRef as P, QueryBuilder as Q, type NodeTypeNames as R, Store as S, TraversalBuilder as T, type OntologyChange as U, type OperationHookContext as V, type PaginateOptions as W, type PaginatedResult as X, type Predicate as Y, type PropsAccessor as Z, type QueryHookContext as _, type SerializedNodeDef as a, type SchemaDiff as a0, type SchemaManagerOptions as a1, type SchemaValidationResult as a2, type SelectContext as a3, type SelectableEdge as a4, type SelectableNode as a5, type SerializedOntology as a6, type SerializedUniqueConstraint as a7, type StoreConfig as a8, type StoreHooks as a9, isMetaEdge as aA, isSchemaInitialized as aB, migrateSchema as aC, notExists as aD, notInSubquery as aE, type StoreOptions as aa, type StreamOptions as ab, type TransactionContext as ac, type TypedEdgeCollection as ad, type TypedNodeRef as ae, type UpdateEdgeInput as af, type UpdateNodeInput as ag, computeSchemaDiff as ah, createStore as ai, createStoreWithSchema as aj, defineGraph as ak, embedding as al, ensureSchema as am, exists as an, fieldRef as ao, getActiveSchema as ap, getEdgeTypeNames as aq, getEmbeddingDimensions as ar, getMigrationActions as as, getNodeTypeNames as at, getSchemaChanges as au, inSubquery as av, initializeSchema as aw, isBackwardsCompatible as ax, isEmbeddingSchema as ay, isGraphDef as az, type SerializedEdgeDef as b, type SerializedMetaEdge as c, type SerializedOntologyRelation as d, type SerializedClosures as e, type SerializedSchema as f, type SchemaHash as g, type AllEdgeTypes as h, type AllNodeTypes as i, type ChangeSeverity as j, type ChangeType as k, type CreateEdgeInput as l, type CreateNodeInput as m, type Edge as n, type EdgeAccessor as o, type EdgeChange as p, type EdgeCollection as q, type EdgeTypeNames as r, type EmbeddingSchema as s, type EmbeddingValue as t, ExecutableAggregateQuery as u, ExecutableQuery as v, type GetEdgeType as w, type GetNodeType as x, type MetaEdgeProperties as y, type NodeAccessor as z };
1820
+ export { notExists as $, type AliasMap as A, type TransactionContext as B, type CreateQueryBuilderOptions as C, type TypedEdgeCollection as D, type EdgeAliasMap as E, type FieldAccessor as F, type TypedNodeRef as G, type HookContext as H, type UpdateEdgeInput as I, type UpdateNodeInput as J, createStore as K, createStoreWithSchema as L, embedding as M, type Node as N, type OperationHookContext as O, type PaginateOptions as P, QueryBuilder as Q, type RecursiveTraversalOptions as R, Store as S, TraversalBuilder as T, UnionableQuery as U, exists as V, fieldRef as W, getEmbeddingDimensions as X, inSubquery as Y, isEmbeddingSchema as Z, isParameterRef as _, type AggregateResult as a, notInSubquery as a0, param as a1, type CreateEdgeInput as b, type CreateNodeInput as c, type Edge as d, type EdgeAccessor as e, type EdgeCollection as f, type EmbeddingSchema as g, type EmbeddingValue as h, ExecutableAggregateQuery as i, ExecutableQuery as j, type NodeAccessor as k, type NodeAlias as l, type NodeCollection as m, type NodeRef as n, type PaginatedResult as o, type Predicate as p, PreparedQuery as q, type PropsAccessor as r, type QueryHookContext as s, type QueryOptions as t, type SelectContext as u, type SelectableEdge as v, type SelectableNode as w, type StoreHooks as x, type StoreOptions as y, type StreamOptions as z };