ai-database 2.1.3 → 2.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 (260) hide show
  1. package/CHANGELOG.md +35 -1
  2. package/README.md +880 -669
  3. package/dist/actions.d.ts +2 -2
  4. package/dist/actions.d.ts.map +1 -1
  5. package/dist/actions.js +1 -1
  6. package/dist/actions.js.map +1 -1
  7. package/dist/ai-promise-db.d.ts +49 -23
  8. package/dist/ai-promise-db.d.ts.map +1 -1
  9. package/dist/ai-promise-db.js +91 -63
  10. package/dist/ai-promise-db.js.map +1 -1
  11. package/dist/authorization.d.ts.map +1 -1
  12. package/dist/authorization.js +38 -30
  13. package/dist/authorization.js.map +1 -1
  14. package/dist/cascade-orchestrator.d.ts +404 -0
  15. package/dist/cascade-orchestrator.d.ts.map +1 -0
  16. package/dist/cascade-orchestrator.js +828 -0
  17. package/dist/cascade-orchestrator.js.map +1 -0
  18. package/dist/cascade-write-strategy.d.ts +584 -0
  19. package/dist/cascade-write-strategy.d.ts.map +1 -0
  20. package/dist/cascade-write-strategy.js +590 -0
  21. package/dist/cascade-write-strategy.js.map +1 -0
  22. package/dist/ch-adapter.d.ts +358 -0
  23. package/dist/ch-adapter.d.ts.map +1 -0
  24. package/dist/ch-adapter.js +929 -0
  25. package/dist/ch-adapter.js.map +1 -0
  26. package/dist/client/index.d.ts +42 -0
  27. package/dist/client/index.d.ts.map +1 -0
  28. package/dist/client/index.js +43 -0
  29. package/dist/client/index.js.map +1 -0
  30. package/dist/client.d.ts +266 -0
  31. package/dist/client.d.ts.map +1 -0
  32. package/dist/client.js +81 -0
  33. package/dist/client.js.map +1 -0
  34. package/dist/constants.d.ts +64 -1
  35. package/dist/constants.d.ts.map +1 -1
  36. package/dist/constants.js +52 -2
  37. package/dist/constants.js.map +1 -1
  38. package/dist/dataloader.d.ts +99 -0
  39. package/dist/dataloader.d.ts.map +1 -0
  40. package/dist/dataloader.js +225 -0
  41. package/dist/dataloader.js.map +1 -0
  42. package/dist/db-provider-port.d.ts +501 -0
  43. package/dist/db-provider-port.d.ts.map +1 -0
  44. package/dist/db-provider-port.js +113 -0
  45. package/dist/db-provider-port.js.map +1 -0
  46. package/dist/digital-objects-provider.d.ts +49 -0
  47. package/dist/digital-objects-provider.d.ts.map +1 -0
  48. package/dist/digital-objects-provider.js +55 -0
  49. package/dist/digital-objects-provider.js.map +1 -0
  50. package/dist/do-sqlite-adapter.d.ts +402 -0
  51. package/dist/do-sqlite-adapter.d.ts.map +1 -0
  52. package/dist/do-sqlite-adapter.js +745 -0
  53. package/dist/do-sqlite-adapter.js.map +1 -0
  54. package/dist/docs-rels/custom-types.d.ts +134 -0
  55. package/dist/docs-rels/custom-types.d.ts.map +1 -0
  56. package/dist/docs-rels/custom-types.js +70 -0
  57. package/dist/docs-rels/custom-types.js.map +1 -0
  58. package/dist/docs-rels/index.d.ts +16 -0
  59. package/dist/docs-rels/index.d.ts.map +1 -0
  60. package/dist/docs-rels/index.js +16 -0
  61. package/dist/docs-rels/index.js.map +1 -0
  62. package/dist/docs-rels/migrations/index.d.ts +30 -0
  63. package/dist/docs-rels/migrations/index.d.ts.map +1 -0
  64. package/dist/docs-rels/migrations/index.js +128 -0
  65. package/dist/docs-rels/migrations/index.js.map +1 -0
  66. package/dist/docs-rels/schema.d.ts +2961 -0
  67. package/dist/docs-rels/schema.d.ts.map +1 -0
  68. package/dist/docs-rels/schema.js +244 -0
  69. package/dist/docs-rels/schema.js.map +1 -0
  70. package/dist/durable-clickhouse.d.ts.map +1 -1
  71. package/dist/durable-clickhouse.js +16 -13
  72. package/dist/durable-clickhouse.js.map +1 -1
  73. package/dist/durable-promise.d.ts.map +1 -1
  74. package/dist/durable-promise.js +34 -15
  75. package/dist/durable-promise.js.map +1 -1
  76. package/dist/errors.d.ts +127 -0
  77. package/dist/errors.d.ts.map +1 -0
  78. package/dist/errors.js +210 -0
  79. package/dist/errors.js.map +1 -0
  80. package/dist/eventbridge.d.ts +117 -0
  81. package/dist/eventbridge.d.ts.map +1 -0
  82. package/dist/eventbridge.js +238 -0
  83. package/dist/eventbridge.js.map +1 -0
  84. package/dist/events.d.ts +2 -2
  85. package/dist/events.d.ts.map +1 -1
  86. package/dist/events.js +1 -1
  87. package/dist/events.js.map +1 -1
  88. package/dist/execution-queue.d.ts.map +1 -1
  89. package/dist/execution-queue.js +4 -5
  90. package/dist/execution-queue.js.map +1 -1
  91. package/dist/index.d.ts +35 -8
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +106 -6
  94. package/dist/index.js.map +1 -1
  95. package/dist/linguistic.d.ts +3 -108
  96. package/dist/linguistic.d.ts.map +1 -1
  97. package/dist/linguistic.js +3 -372
  98. package/dist/linguistic.js.map +1 -1
  99. package/dist/logger.d.ts +132 -0
  100. package/dist/logger.d.ts.map +1 -0
  101. package/dist/logger.js +137 -0
  102. package/dist/logger.js.map +1 -0
  103. package/dist/memory-provider.d.ts +128 -0
  104. package/dist/memory-provider.d.ts.map +1 -1
  105. package/dist/memory-provider.js +592 -257
  106. package/dist/memory-provider.js.map +1 -1
  107. package/dist/pg-adapter.d.ts +424 -0
  108. package/dist/pg-adapter.d.ts.map +1 -0
  109. package/dist/pg-adapter.js +921 -0
  110. package/dist/pg-adapter.js.map +1 -0
  111. package/dist/pipelines-iceberg-emitter.d.ts +327 -0
  112. package/dist/pipelines-iceberg-emitter.d.ts.map +1 -0
  113. package/dist/pipelines-iceberg-emitter.js +351 -0
  114. package/dist/pipelines-iceberg-emitter.js.map +1 -0
  115. package/dist/provider-capabilities.d.ts +146 -0
  116. package/dist/provider-capabilities.d.ts.map +1 -0
  117. package/dist/provider-capabilities.js +214 -0
  118. package/dist/provider-capabilities.js.map +1 -0
  119. package/dist/rdb-provider-adapter.d.ts +195 -0
  120. package/dist/rdb-provider-adapter.d.ts.map +1 -0
  121. package/dist/rdb-provider-adapter.js +291 -0
  122. package/dist/rdb-provider-adapter.js.map +1 -0
  123. package/dist/schema/cascade.d.ts +48 -17
  124. package/dist/schema/cascade.d.ts.map +1 -1
  125. package/dist/schema/cascade.js +477 -278
  126. package/dist/schema/cascade.js.map +1 -1
  127. package/dist/schema/definition-caches.d.ts +24 -0
  128. package/dist/schema/definition-caches.d.ts.map +1 -0
  129. package/dist/schema/definition-caches.js +26 -0
  130. package/dist/schema/definition-caches.js.map +1 -0
  131. package/dist/schema/dependency-graph.d.ts +21 -109
  132. package/dist/schema/dependency-graph.d.ts.map +1 -1
  133. package/dist/schema/dependency-graph.js +25 -333
  134. package/dist/schema/dependency-graph.js.map +1 -1
  135. package/dist/schema/diff.d.ts +103 -0
  136. package/dist/schema/diff.d.ts.map +1 -0
  137. package/dist/schema/diff.js +329 -0
  138. package/dist/schema/diff.js.map +1 -0
  139. package/dist/schema/entity-operations.d.ts +99 -0
  140. package/dist/schema/entity-operations.d.ts.map +1 -0
  141. package/dist/schema/entity-operations.js +818 -0
  142. package/dist/schema/entity-operations.js.map +1 -0
  143. package/dist/schema/index.d.ts +28 -34
  144. package/dist/schema/index.d.ts.map +1 -1
  145. package/dist/schema/index.js +454 -521
  146. package/dist/schema/index.js.map +1 -1
  147. package/dist/schema/migration.d.ts +205 -0
  148. package/dist/schema/migration.d.ts.map +1 -0
  149. package/dist/schema/migration.js +327 -0
  150. package/dist/schema/migration.js.map +1 -0
  151. package/dist/schema/nl-query-generator.d.ts +68 -0
  152. package/dist/schema/nl-query-generator.d.ts.map +1 -0
  153. package/dist/schema/nl-query-generator.js +362 -0
  154. package/dist/schema/nl-query-generator.js.map +1 -0
  155. package/dist/schema/nl-query.d.ts +65 -0
  156. package/dist/schema/nl-query.d.ts.map +1 -0
  157. package/dist/schema/nl-query.js +178 -0
  158. package/dist/schema/nl-query.js.map +1 -0
  159. package/dist/schema/parse.d.ts.map +1 -1
  160. package/dist/schema/parse.js +144 -89
  161. package/dist/schema/parse.js.map +1 -1
  162. package/dist/schema/provider.d.ts +37 -0
  163. package/dist/schema/provider.d.ts.map +1 -1
  164. package/dist/schema/provider.js +15 -7
  165. package/dist/schema/provider.js.map +1 -1
  166. package/dist/schema/resolve.d.ts +46 -5
  167. package/dist/schema/resolve.d.ts.map +1 -1
  168. package/dist/schema/resolve.js +237 -95
  169. package/dist/schema/resolve.js.map +1 -1
  170. package/dist/schema/search-utils.d.ts +76 -0
  171. package/dist/schema/search-utils.d.ts.map +1 -0
  172. package/dist/schema/search-utils.js +86 -0
  173. package/dist/schema/search-utils.js.map +1 -0
  174. package/dist/schema/seed.d.ts +53 -0
  175. package/dist/schema/seed.d.ts.map +1 -0
  176. package/dist/schema/seed.js +94 -0
  177. package/dist/schema/seed.js.map +1 -0
  178. package/dist/schema/semantic.d.ts +10 -0
  179. package/dist/schema/semantic.d.ts.map +1 -1
  180. package/dist/schema/semantic.js +192 -86
  181. package/dist/schema/semantic.js.map +1 -1
  182. package/dist/schema/sub-apis.d.ts +52 -0
  183. package/dist/schema/sub-apis.d.ts.map +1 -0
  184. package/dist/schema/sub-apis.js +216 -0
  185. package/dist/schema/sub-apis.js.map +1 -0
  186. package/dist/schema/system-entities.d.ts +42 -0
  187. package/dist/schema/system-entities.d.ts.map +1 -0
  188. package/dist/schema/system-entities.js +101 -0
  189. package/dist/schema/system-entities.js.map +1 -0
  190. package/dist/schema/types.d.ts +91 -9
  191. package/dist/schema/types.d.ts.map +1 -1
  192. package/dist/schema/union-fallback.d.ts.map +1 -1
  193. package/dist/schema/union-fallback.js +21 -15
  194. package/dist/schema/union-fallback.js.map +1 -1
  195. package/dist/schema/value-generators/ai.d.ts +54 -0
  196. package/dist/schema/value-generators/ai.d.ts.map +1 -0
  197. package/dist/schema/value-generators/ai.js +136 -0
  198. package/dist/schema/value-generators/ai.js.map +1 -0
  199. package/dist/schema/value-generators/index.d.ts +126 -0
  200. package/dist/schema/value-generators/index.d.ts.map +1 -0
  201. package/dist/schema/value-generators/index.js +219 -0
  202. package/dist/schema/value-generators/index.js.map +1 -0
  203. package/dist/schema/value-generators/placeholder.d.ts +52 -0
  204. package/dist/schema/value-generators/placeholder.d.ts.map +1 -0
  205. package/dist/schema/value-generators/placeholder.js +328 -0
  206. package/dist/schema/value-generators/placeholder.js.map +1 -0
  207. package/dist/schema/value-generators/types.d.ts +116 -0
  208. package/dist/schema/value-generators/types.d.ts.map +1 -0
  209. package/dist/schema/value-generators/types.js +11 -0
  210. package/dist/schema/value-generators/types.js.map +1 -0
  211. package/dist/schema/version.d.ts +111 -0
  212. package/dist/schema/version.d.ts.map +1 -0
  213. package/dist/schema/version.js +190 -0
  214. package/dist/schema/version.js.map +1 -0
  215. package/dist/schema.d.ts +1095 -24
  216. package/dist/schema.d.ts.map +1 -1
  217. package/dist/schema.js +2852 -40
  218. package/dist/schema.js.map +1 -1
  219. package/dist/semantic-vectors.d.ts +39 -0
  220. package/dist/semantic-vectors.d.ts.map +1 -0
  221. package/dist/semantic-vectors.js +334 -0
  222. package/dist/semantic-vectors.js.map +1 -0
  223. package/dist/semantic.d.ts +29 -1
  224. package/dist/semantic.d.ts.map +1 -1
  225. package/dist/semantic.js +26 -16
  226. package/dist/semantic.js.map +1 -1
  227. package/dist/telemetry.d.ts +128 -0
  228. package/dist/telemetry.d.ts.map +1 -0
  229. package/dist/telemetry.js +305 -0
  230. package/dist/telemetry.js.map +1 -0
  231. package/dist/tests.d.ts.map +1 -1
  232. package/dist/tests.js +30 -22
  233. package/dist/tests.js.map +1 -1
  234. package/dist/type-guards.d.ts +50 -5
  235. package/dist/type-guards.d.ts.map +1 -1
  236. package/dist/type-guards.js +87 -16
  237. package/dist/type-guards.js.map +1 -1
  238. package/dist/types.d.ts +33 -245
  239. package/dist/types.d.ts.map +1 -1
  240. package/dist/types.js +62 -72
  241. package/dist/types.js.map +1 -1
  242. package/dist/validation.d.ts +2 -5
  243. package/dist/validation.d.ts.map +1 -1
  244. package/dist/validation.js +65 -93
  245. package/dist/validation.js.map +1 -1
  246. package/dist/worker/db-provider.d.ts +168 -0
  247. package/dist/worker/db-provider.d.ts.map +1 -0
  248. package/dist/worker/db-provider.js +277 -0
  249. package/dist/worker/db-provider.js.map +1 -0
  250. package/dist/worker/index.d.ts +35 -0
  251. package/dist/worker/index.d.ts.map +1 -0
  252. package/dist/worker/index.js +37 -0
  253. package/dist/worker/index.js.map +1 -0
  254. package/dist/worker.d.ts +779 -0
  255. package/dist/worker.d.ts.map +1 -0
  256. package/dist/worker.js +2786 -0
  257. package/dist/worker.js.map +1 -0
  258. package/package.json +46 -16
  259. package/src/docs-rels/migrations/0001-init.sql +125 -0
  260. package/LICENSE +0 -21
@@ -30,22 +30,43 @@
30
30
  *
31
31
  * @packageDocumentation
32
32
  */
33
- import { DBPromise, wrapEntityOperations, setSchemaRelationInfo } from '../ai-promise-db.js';
33
+ import { DBPromise, wrapEntityOperations, setSchemaRelationInfo, } from '../ai-promise-db.js';
34
+ import { logError } from '../logger.js';
34
35
  // Re-export parse functions
35
36
  export { parseOperator, parseField, parseSchema, isPrimitiveType } from './parse.js';
36
- export { setProvider, resolveProvider, hasSemanticSearch, hasHybridSearch, hasEventsAPI, hasActionsAPI, hasArtifactsAPI, hasEmbeddingsConfig, } from './provider.js';
37
+ // Re-export seed functions
38
+ export { loadSeedData, fetchSeedData, parseDelimitedData, mapSeedDataToRecords } from './seed.js';
39
+ export { setProvider, resolveProvider, hasSemanticSearch, hasHybridSearch, hasEventsAPI, hasActionsAPI, hasArtifactsAPI, hasEmbeddingsConfig, hasTransactionSupport, } from './provider.js';
40
+ // Import resolve functions used internally
41
+ import { isPromptField } from './resolve.js';
37
42
  // Re-export resolve functions
38
- export { isEntityId, inferTypeFromField, resolveContextPath, resolveInstructions, prefetchContext, isPromptField, resolveNestedPending, resolveReferenceSpec, hydrateEntity, } from './resolve.js';
43
+ export { isEntityId, inferTypeFromField, resolveContextPath, resolveInstructions, prefetchContext, prefetchContextPaths, isPromptField, resolveNestedPending, resolveReferenceSpec, hydrateEntity, } from './resolve.js';
39
44
  // Re-export cascade functions
40
- export { generateContextAwareValue, generateAIFields, generateEntity, resolveForwardExact, generateNaturalLanguageContent, } from './cascade.js';
45
+ export { generateContextAwareValue, generateAIFields, generateEntity, resolveForwardExact, generateNaturalLanguageContent,
46
+ // AI generation configuration
47
+ configureAIGeneration, getAIGenerationConfig,
48
+ // Value generator configuration
49
+ setValueGenerator, getValueGenerator, } from './cascade.js';
41
50
  // Re-export semantic functions
42
51
  export { resolveBackwardFuzzy, resolveForwardFuzzy } from './semantic.js';
52
+ // Re-export NL query generator functions
53
+ export { createDefaultNLQueryGenerator, matchesFilter, applyFilters } from './nl-query-generator.js';
54
+ // Re-export entity operations
55
+ export { createEntityOperations, createEdgeEntityOperations, createNounEntityOperations, createVerbEntityOperations, } from './entity-operations.js';
56
+ // Re-export NL query functions
57
+ export { buildNLQueryContext, executeNLQuery, createNLQueryFn, setNLQueryGenerator, getNLQueryGenerator, } from './nl-query.js';
43
58
  // Re-export dependency graph functions
44
59
  export { buildDependencyGraph, topologicalSort, detectCycles, getParallelGroups, getAllDependencies, hasCycles, visualizeGraph, CircularDependencyError, PRIMITIVE_TYPES, } from './dependency-graph.js';
45
60
  // Re-export union fallback functions
46
61
  export { parseUnionTypes, parseUnionThresholds, searchUnionTypes, createProviderSearcher, } from './union-fallback.js';
47
62
  // Re-export generation context
48
63
  export { GenerationContext, createGenerationContext, ContextOverflowError, } from './generation-context.js';
64
+ // Re-export schema versioning functions
65
+ export { computeSchemaHash, getSchemaVersion, setSchemaVersion, hasSchemaChanged, } from './version.js';
66
+ // Re-export schema diff functions
67
+ export { diffSchemas, describeDiff, } from './diff.js';
68
+ // Re-export migration functions
69
+ export { defineMigration, runMigrations, getPendingMigrations, rollbackLastMigration, } from './migration.js';
49
70
  // Re-export verb derivation functions
50
71
  export { FORWARD_TO_REVERSE, BIDIRECTIONAL_PAIRS, deriveReverseVerb, fieldNameToVerb, isPassiveVerb, registerVerbPair, registerBidirectionalPair, registerFieldVerb, } from './verb-derivation.js';
51
72
  import { Verbs, parseUrl } from '../types.js';
@@ -53,8 +74,11 @@ import { inferNoun, getTypeMeta, conjugate } from '../linguistic.js';
53
74
  import { parseSchema } from './parse.js';
54
75
  import { resolveProvider, hasSemanticSearch, hasHybridSearch, hasEventsAPI, hasActionsAPI, hasArtifactsAPI, hasEmbeddingsConfig, } from './provider.js';
55
76
  import { hydrateEntity, resolveReferenceSpec } from './resolve.js';
56
- import { resolveForwardExact, generateAIFields, generateContextAwareValue, generateNaturalLanguageContent } from './cascade.js';
57
- import { resolveBackwardFuzzy, resolveForwardFuzzy } from './semantic.js';
77
+ import { resolveForwardExact, generateAIFields, generateContextAwareValue, generateNaturalLanguageContent, generateEntity, setValueGenerator, } from './cascade.js';
78
+ import { resolveBackwardFuzzy, resolveForwardFuzzy, getFuzzyThreshold } from './semantic.js';
79
+ // Import from extracted modules for use in DB() factory
80
+ import { createEntityOperations, createEdgeEntityOperations, createNounEntityOperations, createVerbEntityOperations, } from './entity-operations.js';
81
+ import { createNLQueryFn } from './nl-query.js';
58
82
  // =============================================================================
59
83
  // Noun/Verb Helpers
60
84
  // =============================================================================
@@ -232,441 +256,8 @@ function schemaToProperties(schema) {
232
256
  }
233
257
  return properties;
234
258
  }
235
- // =============================================================================
236
- // Natural Language Query Implementation
237
- // =============================================================================
238
- let nlQueryGenerator = null;
239
- /**
240
- * Set the AI generator for natural language queries
241
- */
242
- export function setNLQueryGenerator(generator) {
243
- nlQueryGenerator = generator;
244
- }
245
- function buildNLQueryContext(schema, targetType) {
246
- const types = [];
247
- for (const [name, entity] of schema.entities) {
248
- const fields = [];
249
- const relationships = [];
250
- for (const [fieldName, field] of entity.fields) {
251
- if (field.isRelation && field.relatedType) {
252
- relationships.push({
253
- name: fieldName,
254
- to: field.relatedType,
255
- cardinality: field.isArray ? 'many' : 'one',
256
- });
257
- }
258
- else {
259
- fields.push(fieldName);
260
- }
261
- }
262
- const meta = getTypeMeta(name);
263
- types.push({
264
- name,
265
- singular: meta.singular,
266
- plural: meta.plural,
267
- fields,
268
- relationships,
269
- });
270
- }
271
- return { types, targetType };
272
- }
273
- async function executeNLQuery(question, schema, targetType) {
274
- if (!nlQueryGenerator) {
275
- const provider = await resolveProvider();
276
- const results = [];
277
- // Simple heuristic for common "list all" patterns in fallback mode
278
- const lowerQuestion = question.toLowerCase().trim();
279
- const isListAllQuery = /^(show|list|get|find|display)\s+(all|every|the)?\s*/i.test(lowerQuestion) ||
280
- lowerQuestion === '' ||
281
- /\ball\b/i.test(lowerQuestion);
282
- if (targetType) {
283
- if (isListAllQuery) {
284
- // For "show all X" queries, just list everything
285
- const listResults = await provider.list(targetType);
286
- results.push(...listResults);
287
- }
288
- else {
289
- const searchResults = await provider.search(targetType, question);
290
- results.push(...searchResults);
291
- }
292
- }
293
- else {
294
- for (const [typeName] of schema.entities) {
295
- if (isListAllQuery) {
296
- const listResults = await provider.list(typeName);
297
- results.push(...listResults);
298
- }
299
- else {
300
- const searchResults = await provider.search(typeName, question);
301
- results.push(...searchResults);
302
- }
303
- }
304
- }
305
- return {
306
- interpretation: `Search for "${question}"`,
307
- confidence: 0.5,
308
- results,
309
- explanation: 'Fallback to keyword search (no AI generator configured)',
310
- };
311
- }
312
- const context = buildNLQueryContext(schema, targetType);
313
- const plan = await nlQueryGenerator(question, context);
314
- const provider = await resolveProvider();
315
- const results = [];
316
- for (const typeName of plan.types) {
317
- let typeResults;
318
- if (plan.search) {
319
- typeResults = await provider.search(typeName, plan.search, {
320
- where: plan.filters,
321
- });
322
- }
323
- else {
324
- typeResults = await provider.list(typeName, {
325
- where: plan.filters,
326
- });
327
- }
328
- results.push(...typeResults);
329
- }
330
- return {
331
- interpretation: plan.interpretation,
332
- confidence: plan.confidence,
333
- results,
334
- query: JSON.stringify({ types: plan.types, filters: plan.filters, search: plan.search }),
335
- };
336
- }
337
- function createNLQueryFn(schema, typeName) {
338
- return async (strings, ...values) => {
339
- const question = strings.reduce((acc, str, i) => {
340
- return acc + str + (values[i] !== undefined ? String(values[i]) : '');
341
- }, '');
342
- return executeNLQuery(question, schema, typeName);
343
- };
344
- }
345
- // =============================================================================
346
- // Edge Entity Operations
347
- // =============================================================================
348
- function createEdgeEntityOperations(schemaEdgeRecords, getProvider) {
349
- async function getRuntimeEdges() {
350
- try {
351
- const provider = await getProvider();
352
- const runtimeEdges = await provider.list('Edge');
353
- return runtimeEdges;
354
- }
355
- catch {
356
- return [];
357
- }
358
- }
359
- async function getAllEdges() {
360
- const runtimeEdges = await getRuntimeEdges();
361
- const runtimeEdgeKeys = new Set(runtimeEdges.map(e => `${e.from}:${e.name}`));
362
- const filteredSchemaEdges = schemaEdgeRecords.filter(e => {
363
- const key = `${e.from}:${e.name}`;
364
- const hasRuntimeVersion = runtimeEdgeKeys.has(key);
365
- if (hasRuntimeVersion && e.matchMode === 'fuzzy') {
366
- return false;
367
- }
368
- return !hasRuntimeVersion;
369
- });
370
- return [...filteredSchemaEdges, ...runtimeEdges];
371
- }
372
- return {
373
- async get(id) {
374
- const runtimeEdges = await getRuntimeEdges();
375
- const runtimeMatch = runtimeEdges.find(e => e.$id === id || `${e.from}:${e.name}` === id);
376
- if (runtimeMatch)
377
- return { ...runtimeMatch, $type: 'Edge' };
378
- return schemaEdgeRecords.find(e => `${e.from}:${e.name}` === id) ?? null;
379
- },
380
- async list(options) {
381
- let results = await getAllEdges();
382
- if (options?.where) {
383
- for (const [key, value] of Object.entries(options.where)) {
384
- results = results.filter(e => e[key] === value);
385
- }
386
- }
387
- return results.map(e => ({
388
- ...e,
389
- $id: e.$id || `${e.from}:${e.name}`,
390
- $type: 'Edge',
391
- }));
392
- },
393
- async find(where) {
394
- let results = await getAllEdges();
395
- for (const [key, value] of Object.entries(where)) {
396
- results = results.filter(e => e[key] === value);
397
- }
398
- return results.map(e => ({
399
- ...e,
400
- $id: e.$id || `${e.from}:${e.name}`,
401
- $type: 'Edge',
402
- }));
403
- },
404
- async search(query) {
405
- const allEdges = await getAllEdges();
406
- const queryLower = query.toLowerCase();
407
- return allEdges
408
- .filter(e => String(e.from).toLowerCase().includes(queryLower) ||
409
- String(e.name).toLowerCase().includes(queryLower) ||
410
- String(e.to).toLowerCase().includes(queryLower))
411
- .map(e => ({
412
- ...e,
413
- $id: e.$id || `${e.from}:${e.name}`,
414
- $type: 'Edge',
415
- }));
416
- },
417
- async create() {
418
- throw new Error('Cannot manually create Edge records - they are auto-generated');
419
- },
420
- async update() {
421
- throw new Error('Cannot manually update Edge records - they are auto-generated');
422
- },
423
- async upsert() {
424
- throw new Error('Cannot manually upsert Edge records - they are auto-generated');
425
- },
426
- async delete() {
427
- throw new Error('Cannot manually delete Edge records - they are auto-generated');
428
- },
429
- async forEach(optionsOrCallback, maybeCallback) {
430
- const options = typeof optionsOrCallback === 'function' ? undefined : optionsOrCallback;
431
- const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : maybeCallback;
432
- const items = await this.list(options);
433
- for (const item of items) {
434
- await callback(item);
435
- }
436
- },
437
- async semanticSearch() {
438
- return [];
439
- },
440
- async hybridSearch() {
441
- return [];
442
- },
443
- };
444
- }
445
- // =============================================================================
446
- // Entity Operations Factory
447
- // =============================================================================
448
- function createEntityOperations(typeName, entity, schema) {
449
- return {
450
- async get(id) {
451
- const provider = await resolveProvider();
452
- const result = await provider.get(typeName, id);
453
- if (!result)
454
- return null;
455
- return hydrateEntity(result, entity, schema, resolveProvider);
456
- },
457
- async list(options) {
458
- const provider = await resolveProvider();
459
- const results = await provider.list(typeName, options);
460
- return Promise.all(results.map((r) => hydrateEntity(r, entity, schema, resolveProvider)));
461
- },
462
- async find(where) {
463
- const provider = await resolveProvider();
464
- const results = await provider.list(typeName, {
465
- where: where,
466
- });
467
- return Promise.all(results.map((r) => hydrateEntity(r, entity, schema, resolveProvider)));
468
- },
469
- async search(query, options) {
470
- const provider = await resolveProvider();
471
- const results = await provider.search(typeName, query, options);
472
- return Promise.all(results.map((r) => hydrateEntity(r, entity, schema, resolveProvider)));
473
- },
474
- async create(idOrData, maybeData) {
475
- const provider = await resolveProvider();
476
- const providedId = typeof idOrData === 'string' ? idOrData : undefined;
477
- const data = typeof idOrData === 'string'
478
- ? maybeData
479
- : idOrData;
480
- const entityId = providedId || crypto.randomUUID();
481
- const { data: resolvedData, pendingRelations } = await resolveForwardExact(typeName, data, entity, schema, provider, entityId);
482
- const { data: fuzzyResolvedData, pendingRelations: fuzzyPendingRelations } = await resolveForwardFuzzy(typeName, resolvedData, entity, schema, provider, entityId);
483
- const backwardResolvedData = await resolveBackwardFuzzy(typeName, fuzzyResolvedData, entity, schema, provider);
484
- const finalData = await generateAIFields(backwardResolvedData, typeName, entity, schema, provider);
485
- const result = await provider.create(typeName, entityId, finalData);
486
- for (const rel of pendingRelations) {
487
- await provider.relate(typeName, entityId, rel.fieldName, rel.targetType, rel.targetId);
488
- }
489
- const createdEdgeIds = new Set();
490
- for (const rel of fuzzyPendingRelations) {
491
- await provider.relate(typeName, entityId, rel.fieldName, rel.targetType, rel.targetId, {
492
- matchMode: 'fuzzy',
493
- similarity: rel.similarity,
494
- matchedType: rel.matchedType
495
- });
496
- const edgeId = `${typeName}:${rel.fieldName}:${entityId}:${rel.targetId}`;
497
- if (!createdEdgeIds.has(edgeId)) {
498
- createdEdgeIds.add(edgeId);
499
- try {
500
- await provider.create('Edge', edgeId, {
501
- from: typeName,
502
- name: rel.fieldName,
503
- to: rel.targetType,
504
- direction: 'forward',
505
- matchMode: 'fuzzy',
506
- similarity: rel.similarity,
507
- matchedType: rel.matchedType,
508
- fromId: entityId,
509
- toId: rel.targetId,
510
- });
511
- }
512
- catch {
513
- // Edge already exists
514
- }
515
- }
516
- }
517
- return hydrateEntity(result, entity, schema, resolveProvider);
518
- },
519
- async update(id, data) {
520
- const provider = await resolveProvider();
521
- const result = await provider.update(typeName, id, data);
522
- return hydrateEntity(result, entity, schema, resolveProvider);
523
- },
524
- async upsert(id, data) {
525
- const provider = await resolveProvider();
526
- const existing = await provider.get(typeName, id);
527
- if (existing) {
528
- const result = await provider.update(typeName, id, data);
529
- return hydrateEntity(result, entity, schema, resolveProvider);
530
- }
531
- const result = await provider.create(typeName, id, data);
532
- return hydrateEntity(result, entity, schema, resolveProvider);
533
- },
534
- async delete(id) {
535
- const provider = await resolveProvider();
536
- return provider.delete(typeName, id);
537
- },
538
- async forEach(optionsOrCallback, maybeCallback) {
539
- const options = typeof optionsOrCallback === 'function' ? undefined : optionsOrCallback;
540
- const callback = typeof optionsOrCallback === 'function'
541
- ? optionsOrCallback
542
- : maybeCallback;
543
- const items = await this.list(options);
544
- for (const item of items) {
545
- await callback(item);
546
- }
547
- },
548
- async semanticSearch(query, options) {
549
- const provider = await resolveProvider();
550
- if (hasSemanticSearch(provider)) {
551
- const results = await provider.semanticSearch(typeName, query, options);
552
- return Promise.all(results.map((r) => ({
553
- ...hydrateEntity(r, entity, schema, resolveProvider),
554
- $score: r.$score,
555
- })));
556
- }
557
- return [];
558
- },
559
- async hybridSearch(query, options) {
560
- const provider = await resolveProvider();
561
- if (hasHybridSearch(provider)) {
562
- const results = await provider.hybridSearch(typeName, query, options);
563
- return Promise.all(results.map((r) => ({
564
- ...hydrateEntity(r, entity, schema, resolveProvider),
565
- $rrfScore: r.$rrfScore,
566
- $ftsRank: r.$ftsRank,
567
- $semanticRank: r.$semanticRank,
568
- $score: r.$score,
569
- })));
570
- }
571
- return [];
572
- },
573
- async draft(data, options) {
574
- const draftData = { ...data, $phase: 'draft' };
575
- const refs = {};
576
- for (const [fieldName, field] of entity.fields) {
577
- if (draftData[fieldName] !== undefined && draftData[fieldName] !== null) {
578
- continue;
579
- }
580
- if (field.operator && field.relatedType) {
581
- const matchMode = field.matchMode ?? (field.operator.includes('~') ? 'fuzzy' : 'exact');
582
- if (field.isArray) {
583
- const generatedText = generateNaturalLanguageContent(fieldName, field.prompt, field.relatedType, data);
584
- draftData[fieldName] = generatedText;
585
- const refSpec = {
586
- field: fieldName,
587
- operator: field.operator,
588
- type: field.relatedType,
589
- matchMode,
590
- resolved: false,
591
- prompt: field.prompt,
592
- generatedText,
593
- };
594
- refs[fieldName] = [refSpec];
595
- if (options?.stream && options.onChunk) {
596
- options.onChunk(generatedText);
597
- }
598
- }
599
- else {
600
- const generatedText = generateNaturalLanguageContent(fieldName, field.prompt, field.relatedType, data);
601
- draftData[fieldName] = generatedText;
602
- refs[fieldName] = {
603
- field: fieldName,
604
- operator: field.operator,
605
- type: field.relatedType,
606
- matchMode,
607
- resolved: false,
608
- prompt: field.prompt,
609
- generatedText,
610
- };
611
- if (options?.stream && options.onChunk) {
612
- options.onChunk(generatedText);
613
- }
614
- }
615
- }
616
- }
617
- draftData.$refs = refs;
618
- return draftData;
619
- },
620
- async resolve(draft, options) {
621
- // Draft<T> interface requires $phase: 'draft', so we can access it directly
622
- if (draft.$phase !== 'draft') {
623
- throw new Error('Cannot resolve entity: not a draft (missing $phase: "draft")');
624
- }
625
- const provider = await resolveProvider();
626
- const resolved = { ...draft };
627
- const errors = [];
628
- delete resolved.$refs;
629
- resolved.$phase = 'resolved';
630
- const refs = draft.$refs;
631
- for (const [fieldName, refSpec] of Object.entries(refs)) {
632
- try {
633
- if (Array.isArray(refSpec)) {
634
- const resolvedIds = [];
635
- for (const spec of refSpec) {
636
- const resolvedId = await resolveReferenceSpec(spec, resolved, schema, provider, generateContextAwareValue);
637
- if (resolvedId) {
638
- resolvedIds.push(resolvedId);
639
- options?.onResolved?.(fieldName, resolvedId);
640
- }
641
- }
642
- resolved[fieldName] = resolvedIds;
643
- }
644
- else {
645
- const resolvedId = await resolveReferenceSpec(refSpec, resolved, schema, provider, generateContextAwareValue);
646
- if (resolvedId) {
647
- resolved[fieldName] = resolvedId;
648
- options?.onResolved?.(fieldName, resolvedId);
649
- }
650
- }
651
- }
652
- catch (err) {
653
- const errorMsg = err instanceof Error ? err.message : String(err);
654
- if (options?.onError === 'skip') {
655
- errors.push({ field: fieldName, error: errorMsg });
656
- }
657
- else {
658
- throw err;
659
- }
660
- }
661
- }
662
- if (errors.length > 0 || options?.onError === 'skip') {
663
- // resolved is typed as Record<string, unknown>, so we can assign $errors directly
664
- resolved.$errors = errors;
665
- }
666
- return resolved;
667
- },
668
- };
669
- }
259
+ // Note: NL Query functions extracted to nl-query.ts
260
+ // Note: Edge Entity Operations extracted to entity-operations.ts
670
261
  // =============================================================================
671
262
  // DB Factory
672
263
  // =============================================================================
@@ -675,23 +266,199 @@ function createEntityOperations(typeName, entity, schema) {
675
266
  */
676
267
  export function DB(schema, options) {
677
268
  const parsedSchema = parseSchema(schema);
678
- // Add Edge entity to the parsed schema
269
+ // Add system entities to the parsed schema (Noun, Verb, Edge)
270
+ // Noun entity - represents type definitions
271
+ const nounEntity = {
272
+ name: 'Noun',
273
+ fields: new Map([
274
+ [
275
+ 'name',
276
+ { name: 'name', type: 'string', isArray: false, isOptional: false, isRelation: false },
277
+ ],
278
+ [
279
+ 'singular',
280
+ { name: 'singular', type: 'string', isArray: false, isOptional: false, isRelation: false },
281
+ ],
282
+ [
283
+ 'plural',
284
+ { name: 'plural', type: 'string', isArray: false, isOptional: false, isRelation: false },
285
+ ],
286
+ [
287
+ 'slug',
288
+ { name: 'slug', type: 'string', isArray: false, isOptional: false, isRelation: false },
289
+ ],
290
+ [
291
+ 'slugPlural',
292
+ {
293
+ name: 'slugPlural',
294
+ type: 'string',
295
+ isArray: false,
296
+ isOptional: false,
297
+ isRelation: false,
298
+ },
299
+ ],
300
+ [
301
+ 'description',
302
+ {
303
+ name: 'description',
304
+ type: 'string',
305
+ isArray: false,
306
+ isOptional: true,
307
+ isRelation: false,
308
+ },
309
+ ],
310
+ [
311
+ 'properties',
312
+ { name: 'properties', type: 'json', isArray: false, isOptional: true, isRelation: false },
313
+ ],
314
+ [
315
+ 'relationships',
316
+ {
317
+ name: 'relationships',
318
+ type: 'json',
319
+ isArray: false,
320
+ isOptional: true,
321
+ isRelation: false,
322
+ },
323
+ ],
324
+ [
325
+ 'actions',
326
+ { name: 'actions', type: 'json', isArray: false, isOptional: true, isRelation: false },
327
+ ],
328
+ [
329
+ 'events',
330
+ { name: 'events', type: 'json', isArray: false, isOptional: true, isRelation: false },
331
+ ],
332
+ [
333
+ 'metadata',
334
+ { name: 'metadata', type: 'json', isArray: false, isOptional: true, isRelation: false },
335
+ ],
336
+ ]),
337
+ };
338
+ parsedSchema.entities.set('Noun', nounEntity);
339
+ // Verb entity - represents action definitions
340
+ const verbEntity = {
341
+ name: 'Verb',
342
+ fields: new Map([
343
+ [
344
+ 'action',
345
+ { name: 'action', type: 'string', isArray: false, isOptional: false, isRelation: false },
346
+ ],
347
+ [
348
+ 'actor',
349
+ { name: 'actor', type: 'string', isArray: false, isOptional: true, isRelation: false },
350
+ ],
351
+ ['act', { name: 'act', type: 'string', isArray: false, isOptional: true, isRelation: false }],
352
+ [
353
+ 'activity',
354
+ { name: 'activity', type: 'string', isArray: false, isOptional: true, isRelation: false },
355
+ ],
356
+ [
357
+ 'result',
358
+ { name: 'result', type: 'string', isArray: false, isOptional: true, isRelation: false },
359
+ ],
360
+ [
361
+ 'reverse',
362
+ { name: 'reverse', type: 'json', isArray: false, isOptional: true, isRelation: false },
363
+ ],
364
+ [
365
+ 'inverse',
366
+ { name: 'inverse', type: 'string', isArray: false, isOptional: true, isRelation: false },
367
+ ],
368
+ [
369
+ 'description',
370
+ {
371
+ name: 'description',
372
+ type: 'string',
373
+ isArray: false,
374
+ isOptional: true,
375
+ isRelation: false,
376
+ },
377
+ ],
378
+ ]),
379
+ };
380
+ parsedSchema.entities.set('Verb', verbEntity);
381
+ // Edge entity - represents relationships
679
382
  const edgeEntity = {
680
383
  name: 'Edge',
681
384
  fields: new Map([
682
- ['from', { name: 'from', type: 'string', isArray: false, isOptional: false, isRelation: false }],
683
- ['name', { name: 'name', type: 'string', isArray: false, isOptional: false, isRelation: false }],
385
+ [
386
+ 'from',
387
+ { name: 'from', type: 'string', isArray: false, isOptional: false, isRelation: false },
388
+ ],
389
+ [
390
+ 'name',
391
+ { name: 'name', type: 'string', isArray: false, isOptional: false, isRelation: false },
392
+ ],
684
393
  ['to', { name: 'to', type: 'string', isArray: false, isOptional: false, isRelation: false }],
685
- ['backref', { name: 'backref', type: 'string', isArray: false, isOptional: true, isRelation: false }],
686
- ['cardinality', { name: 'cardinality', type: 'string', isArray: false, isOptional: false, isRelation: false }],
687
- ['direction', { name: 'direction', type: 'string', isArray: false, isOptional: false, isRelation: false }],
688
- ['matchMode', { name: 'matchMode', type: 'string', isArray: false, isOptional: true, isRelation: false }],
394
+ [
395
+ 'backref',
396
+ { name: 'backref', type: 'string', isArray: false, isOptional: true, isRelation: false },
397
+ ],
398
+ [
399
+ 'cardinality',
400
+ {
401
+ name: 'cardinality',
402
+ type: 'string',
403
+ isArray: false,
404
+ isOptional: false,
405
+ isRelation: false,
406
+ },
407
+ ],
408
+ [
409
+ 'direction',
410
+ { name: 'direction', type: 'string', isArray: false, isOptional: false, isRelation: false },
411
+ ],
412
+ [
413
+ 'matchMode',
414
+ { name: 'matchMode', type: 'string', isArray: false, isOptional: true, isRelation: false },
415
+ ],
689
416
  ]),
690
417
  };
691
418
  parsedSchema.entities.set('Edge', edgeEntity);
419
+ // Create local getProvider function for dependency injection
420
+ // If options.provider is provided, use it; otherwise fall back to global resolveProvider()
421
+ let cachedProvider = null;
422
+ let providerPromise = null;
423
+ async function getProvider() {
424
+ // Return cached provider if available
425
+ if (cachedProvider)
426
+ return cachedProvider;
427
+ // Return pending promise if resolution is in progress
428
+ if (providerPromise)
429
+ return providerPromise;
430
+ // Check if options.provider is provided (dependency injection)
431
+ if (options?.provider) {
432
+ providerPromise = (async () => {
433
+ const providerOption = options.provider;
434
+ if (typeof providerOption === 'function') {
435
+ // It's a factory function - call it
436
+ const result = providerOption();
437
+ // Handle both sync and async factory functions
438
+ cachedProvider = result instanceof Promise ? await result : result;
439
+ }
440
+ else {
441
+ // It's a direct provider instance
442
+ cachedProvider = providerOption;
443
+ }
444
+ return cachedProvider;
445
+ })();
446
+ return providerPromise;
447
+ }
448
+ // Fall back to global resolveProvider()
449
+ providerPromise = resolveProvider().then((p) => {
450
+ cachedProvider = p;
451
+ return p;
452
+ });
453
+ return providerPromise;
454
+ }
455
+ // Configure value generator if provided
456
+ if (options?.valueGenerator) {
457
+ setValueGenerator(options.valueGenerator);
458
+ }
692
459
  // Configure provider with embeddings settings if provided
693
460
  if (options?.embeddings) {
694
- resolveProvider().then(provider => {
461
+ getProvider().then((provider) => {
695
462
  if (hasEmbeddingsConfig(provider)) {
696
463
  provider.setEmbeddingsConfig(options.embeddings);
697
464
  }
@@ -700,11 +467,27 @@ export function DB(schema, options) {
700
467
  // Collect all edge records from the schema
701
468
  const allEdgeRecords = [];
702
469
  for (const [entityName, entity] of parsedSchema.entities) {
703
- if (entityName !== 'Edge') {
470
+ // Only create edge records for user-defined entities (not system entities)
471
+ if (entityName !== 'Edge' && entityName !== 'Noun' && entityName !== 'Verb') {
704
472
  const edgeRecords = createEdgeRecords(entityName, schema[entityName] ?? {}, entity);
705
473
  allEdgeRecords.push(...edgeRecords);
706
474
  }
707
475
  }
476
+ // Collect all noun records from the schema (user-defined entities only)
477
+ const allNounRecords = [];
478
+ for (const [entityName, entity] of parsedSchema.entities) {
479
+ // Only create noun records for user-defined entities (not system entities)
480
+ if (entityName !== 'Edge' && entityName !== 'Noun' && entityName !== 'Verb') {
481
+ const nounRecord = createNounRecord(entityName, schema[entityName]);
482
+ allNounRecords.push(nounRecord);
483
+ }
484
+ }
485
+ // Collect all verb records from the standard verbs
486
+ const allVerbRecords = Object.values(Verbs).map((verb) => ({
487
+ ...verb,
488
+ $id: verb.action,
489
+ $type: 'Verb',
490
+ }));
708
491
  // Build and set schema relation info for batch loading
709
492
  // Maps entityType -> fieldName -> relatedType
710
493
  const relationInfo = new Map();
@@ -724,7 +507,7 @@ export function DB(schema, options) {
724
507
  // This API adapts DBAction to ForEachActionsAPI interface
725
508
  const actionsAPI = {
726
509
  async create(data) {
727
- const provider = await resolveProvider();
510
+ const provider = await getProvider();
728
511
  if (hasActionsAPI(provider)) {
729
512
  const action = await provider.createAction(data);
730
513
  return { id: action.id };
@@ -732,35 +515,45 @@ export function DB(schema, options) {
732
515
  throw new Error('Provider does not support actions');
733
516
  },
734
517
  async get(id) {
735
- const provider = await resolveProvider();
518
+ const provider = await getProvider();
736
519
  if (hasActionsAPI(provider)) {
737
520
  const action = await provider.getAction(id);
738
521
  if (!action)
739
522
  return null;
740
523
  // Adapt DBAction to ForEachActionState
741
- return {
524
+ const state = {
742
525
  id: action.id,
743
526
  type: action.action ?? action.type ?? 'unknown',
744
527
  status: action.status,
745
- progress: action.progress,
746
- total: action.total,
747
528
  data: action.objectData ?? {},
748
- result: action.result,
749
- error: action.error,
750
529
  };
530
+ if (action.progress !== undefined)
531
+ state.progress = action.progress;
532
+ if (action.total !== undefined)
533
+ state.total = action.total;
534
+ if (action.result !== undefined)
535
+ state.result = action.result;
536
+ if (action.error !== undefined)
537
+ state.error = action.error;
538
+ return state;
751
539
  }
752
540
  return null;
753
541
  },
754
542
  async update(id, updates) {
755
- const provider = await resolveProvider();
543
+ const provider = await getProvider();
756
544
  if (hasActionsAPI(provider)) {
757
- await provider.updateAction(id, {
758
- status: updates.status,
759
- progress: updates.progress,
760
- // ForEachResult needs to be converted to Record<string, unknown> for DBAction.result
761
- result: updates.result,
762
- error: updates.error,
763
- });
545
+ const updatePayload = {};
546
+ if (updates.status !== undefined)
547
+ updatePayload.status = updates.status;
548
+ if (updates.progress !== undefined)
549
+ updatePayload.progress = updates.progress;
550
+ if (updates.error !== undefined)
551
+ updatePayload.error = updates.error;
552
+ // ForEachResult needs to be converted to Record<string, unknown> for DBAction.result
553
+ if (updates.result !== undefined) {
554
+ updatePayload.result = updates.result;
555
+ }
556
+ await provider.updateAction(id, updatePayload);
764
557
  return;
765
558
  }
766
559
  throw new Error('Provider does not support actions');
@@ -781,12 +574,13 @@ export function DB(schema, options) {
781
574
  function emitInternalEventForOps(eventType, data) {
782
575
  const handlers = eventHandlersForOps.get(eventType);
783
576
  if (handlers) {
784
- for (const handler of handlers) {
577
+ const snapshot = [...handlers];
578
+ for (const handler of snapshot) {
785
579
  try {
786
580
  handler(data);
787
581
  }
788
582
  catch (e) {
789
- console.error(`Error in event handler for ${eventType}:`, e);
583
+ logError(`Error in event handler for ${eventType}:`, e);
790
584
  }
791
585
  }
792
586
  }
@@ -808,10 +602,23 @@ export function DB(schema, options) {
808
602
  }
809
603
  for (const [entityName, entity] of parsedSchema.entities) {
810
604
  if (entityName === 'Edge') {
811
- const edgeOps = createEdgeEntityOperations(allEdgeRecords, resolveProvider);
605
+ // Edge entity - auto-generated from schema relationships
606
+ const edgeOps = createEdgeEntityOperations(allEdgeRecords, getProvider);
812
607
  const wrappedEdgeOps = wrapEntityOperations(entityName, edgeOps, actionsAPI);
813
608
  entityOperations[entityName] = makeCallableEntityOps(wrappedEdgeOps, entityName);
814
609
  }
610
+ else if (entityName === 'Noun') {
611
+ // Noun entity - auto-generated from schema entity types
612
+ const nounOps = createNounEntityOperations(allNounRecords);
613
+ const wrappedNounOps = wrapEntityOperations(entityName, nounOps, actionsAPI);
614
+ entityOperations[entityName] = makeCallableEntityOps(wrappedNounOps, entityName);
615
+ }
616
+ else if (entityName === 'Verb') {
617
+ // Verb entity - standard verbs with conjugation forms
618
+ const verbOps = createVerbEntityOperations(allVerbRecords);
619
+ const wrappedVerbOps = wrapEntityOperations(entityName, verbOps, actionsAPI);
620
+ entityOperations[entityName] = makeCallableEntityOps(wrappedVerbOps, entityName);
621
+ }
815
622
  else {
816
623
  const baseOps = createEntityOperations(entityName, entity, parsedSchema);
817
624
  const wrappedOps = wrapEntityOperations(entityName, baseOps, actionsAPI);
@@ -823,7 +630,7 @@ export function DB(schema, options) {
823
630
  throw new Error(`Draft method not available for ${entityName}`);
824
631
  }
825
632
  const draft = await draftMethod(data, options);
826
- draft.$type = entityName;
633
+ draft['$type'] = entityName;
827
634
  emitInternalEventForOps('draft', draft);
828
635
  return draft;
829
636
  };
@@ -834,7 +641,8 @@ export function DB(schema, options) {
834
641
  }
835
642
  const resolved = await resolveMethod(draft, options);
836
643
  if (resolved && typeof resolved === 'object') {
837
- resolved.$type = entityName;
644
+ ;
645
+ resolved['$type'] = entityName;
838
646
  }
839
647
  emitInternalEventForOps('resolve', resolved);
840
648
  return resolved;
@@ -858,27 +666,57 @@ export function DB(schema, options) {
858
666
  const draft = await draftFn(data);
859
667
  return draft;
860
668
  }
669
+ // Check if entity has reference fields that need two-phase processing
670
+ const entityDef = parsedSchema.entities.get(entityName);
861
671
  const effectiveMaxDepth = options?.maxDepth ?? (options?.cascade ? 3 : 0);
672
+ // Disable auto-generation when:
673
+ // 1. cascade is explicitly set to false
674
+ // 2. cascade is true but maxDepth is 0 (depth exhausted)
675
+ // When cascade is undefined, single-level array generation is allowed
676
+ const cascadeExplicitlyDisabled = options?.cascade === false || (options?.cascade === true && effectiveMaxDepth === 0);
677
+ const hasReferenceFields = entityDef &&
678
+ Array.from(entityDef.fields.values()).some((field) => {
679
+ if (!field.operator || !field.relatedType)
680
+ return false;
681
+ // Exclude forward fuzzy (~>) - handled by resolveForwardFuzzy in the direct create path
682
+ if (field.operator === '~>')
683
+ return false;
684
+ // For forward exact (->) array fields when cascade is explicitly disabled
685
+ if (field.operator === '->' && field.isArray && cascadeExplicitlyDisabled) {
686
+ return false;
687
+ }
688
+ return true;
689
+ });
690
+ // Cascade takes priority over two-phase processing when enabled
691
+ // Cascade handles generating related entities through the graph
862
692
  if (options?.cascade && effectiveMaxDepth > 0) {
863
- const provider = await resolveProvider();
693
+ const provider = await getProvider();
864
694
  const entityDef = parsedSchema.entities.get(entityName);
865
695
  if (entityDef) {
866
696
  // CreateEntityOptions now includes _cascadeState as an optional property
867
697
  const cascadeState = options._cascadeState ?? {
868
698
  totalEntitiesCreated: 0,
869
699
  initialMaxDepth: effectiveMaxDepth,
870
- rootOnProgress: options.onProgress,
871
- rootOnError: options.onError,
872
- stopOnError: options.stopOnError,
873
- cascadeTypes: options.cascadeTypes,
700
+ ...(options.onProgress !== undefined && { rootOnProgress: options.onProgress }),
701
+ ...(options.onError !== undefined && { rootOnError: options.onError }),
702
+ ...(options.stopOnError !== undefined && { stopOnError: options.stopOnError }),
703
+ ...(options.cascadeTypes !== undefined && { cascadeTypes: options.cascadeTypes }),
874
704
  };
875
705
  const currentDepth = cascadeState.initialMaxDepth - effectiveMaxDepth;
876
706
  const cascadeData = { ...data };
707
+ // Emit progress for current entity being generated
708
+ cascadeState.rootOnProgress?.({
709
+ phase: 'generating',
710
+ depth: currentDepth,
711
+ currentType: entityName,
712
+ totalEntitiesCreated: cascadeState.totalEntitiesCreated,
713
+ });
877
714
  for (const [fieldName, field] of entityDef.fields) {
878
715
  if (cascadeData[fieldName] !== undefined)
879
716
  continue;
880
717
  if (field.operator === '->' && field.relatedType) {
881
- if (cascadeState.cascadeTypes && !cascadeState.cascadeTypes.includes(field.relatedType)) {
718
+ if (cascadeState.cascadeTypes &&
719
+ !cascadeState.cascadeTypes.includes(field.relatedType)) {
882
720
  continue;
883
721
  }
884
722
  const relatedEntity = parsedSchema.entities.get(field.relatedType);
@@ -892,39 +730,48 @@ export function DB(schema, options) {
892
730
  totalEntitiesCreated: cascadeState.totalEntitiesCreated,
893
731
  });
894
732
  const childEntityData = {};
895
- const parentInstructions = entityDef.schema?.$instructions;
896
- const childInstructions = relatedEntity.schema?.$instructions;
733
+ const parentInstructions = entityDef.schema?.['$instructions'];
734
+ const childInstructions = relatedEntity.schema?.['$instructions'];
897
735
  const contextParts = [];
898
736
  if (parentInstructions)
899
737
  contextParts.push(parentInstructions);
900
738
  if (childInstructions)
901
739
  contextParts.push(childInstructions);
902
740
  for (const [key, value] of Object.entries(cascadeData)) {
903
- if (!key.startsWith('$') && !key.startsWith('_') && typeof value === 'string' && value) {
741
+ if (!key.startsWith('$') &&
742
+ !key.startsWith('_') &&
743
+ typeof value === 'string' &&
744
+ value) {
904
745
  contextParts.push(`${key}: ${value}`);
905
746
  }
906
747
  }
907
748
  const fullContext = contextParts.join(' | ');
908
749
  for (const [childFieldName, childField] of relatedEntity.fields) {
909
- if (!childField.isRelation && childField.type === 'string') {
910
- childEntityData[childFieldName] = generateContextAwareValue(childFieldName, field.relatedType, fullContext, undefined, cascadeData);
750
+ const isPrompt = isPromptField(childField);
751
+ if (!childField.isRelation && (childField.type === 'string' || isPrompt)) {
752
+ // Use field type as hint for prompt fields
753
+ const fieldHint = isPrompt ? childField.type : undefined;
754
+ childEntityData[childFieldName] = generateContextAwareValue(childFieldName, field.relatedType, fullContext, fieldHint, cascadeData);
911
755
  }
912
756
  }
913
757
  const childOptions = {
914
758
  ...options,
759
+ cascade: true, // Ensure cascade continues for child entities
915
760
  maxDepth: effectiveMaxDepth - 1,
916
761
  _cascadeState: cascadeState,
917
762
  };
918
763
  const relatedOps = entityOperations[field.relatedType];
919
- const createFn = relatedOps?.create;
920
- const childEntity = createFn ? await createFn(childEntityData, childOptions) : undefined;
921
- if (childEntity?.$id) {
764
+ const createFn = relatedOps?.['create'];
765
+ const childEntity = createFn
766
+ ? await createFn(childEntityData, childOptions)
767
+ : undefined;
768
+ if (childEntity?.['$id']) {
922
769
  cascadeState.totalEntitiesCreated++;
923
770
  if (field.isArray) {
924
- cascadeData[fieldName] = [childEntity.$id];
771
+ cascadeData[fieldName] = [childEntity['$id']];
925
772
  }
926
773
  else {
927
- cascadeData[fieldName] = childEntity.$id;
774
+ cascadeData[fieldName] = childEntity['$id'];
928
775
  }
929
776
  }
930
777
  }
@@ -936,12 +783,13 @@ export function DB(schema, options) {
936
783
  }
937
784
  }
938
785
  }
786
+ const createFn = originalCreate;
939
787
  let result;
940
788
  if (id) {
941
- result = await originalCreate.call(wrappedOps, id, cascadeData);
789
+ result = await createFn.call(wrappedOps, id, cascadeData);
942
790
  }
943
791
  else {
944
- result = await originalCreate.call(wrappedOps, cascadeData);
792
+ result = await createFn.call(wrappedOps, cascadeData);
945
793
  }
946
794
  cascadeState.totalEntitiesCreated++;
947
795
  if (currentDepth === 0) {
@@ -954,8 +802,78 @@ export function DB(schema, options) {
954
802
  return result;
955
803
  }
956
804
  }
957
- return originalCreate.call(wrappedOps, ...args);
805
+ // Use two-phase draft/resolve pipeline when entity has reference fields (but cascade not enabled)
806
+ if (hasReferenceFields) {
807
+ // Phase 1: Draft - create placeholder with natural language refs
808
+ // Skip promptless refs when cascade is not explicitly enabled (default behavior = no auto-generation)
809
+ const skipPromptless = options?.cascade !== true;
810
+ const draft = await draftFn(data, { _skipPromptlessRefs: skipPromptless });
811
+ // Phase 2: Resolve - convert refs to actual entity IDs
812
+ const resolved = await resolveFn(draft);
813
+ // Extract clean data for persistence (remove $phase, $refs, $errors)
814
+ const { $phase, $refs, $errors, $type, ...cleanData } = resolved;
815
+ // Phase 3: Generate AI fields for entities with $context dependencies
816
+ // The draft phase skips prompt fields for entities with $context because
817
+ // those fields need pre-fetched context data to generate properly.
818
+ // generateAIFields handles this context pre-fetching and field generation.
819
+ const provider = await getProvider();
820
+ const entityDefForAI = parsedSchema.entities.get(entityName);
821
+ let finalCleanData = cleanData;
822
+ if (entityDefForAI) {
823
+ finalCleanData = await generateAIFields(cleanData, entityName, entityDefForAI, parsedSchema, provider);
824
+ }
825
+ const createFn = originalCreate;
826
+ let result;
827
+ if (id) {
828
+ result = await createFn.call(wrappedOps, id, finalCleanData);
829
+ }
830
+ else {
831
+ result = await createFn.call(wrappedOps, finalCleanData);
832
+ }
833
+ return result;
834
+ }
835
+ // Pre-initialize empty arrays for ->Type[] fields when cascade is explicitly disabled
836
+ // This prevents resolveForwardExact from auto-generating entities for array fields
837
+ let processedData = data;
838
+ if (cascadeExplicitlyDisabled && entityDef) {
839
+ processedData = { ...data };
840
+ for (const [fieldName, field] of entityDef.fields) {
841
+ if (processedData[fieldName] !== undefined)
842
+ continue;
843
+ if (field.isArray && field.operator === '->' && field.relatedType) {
844
+ processedData[fieldName] = [];
845
+ }
846
+ }
847
+ }
848
+ // Call with properly typed arguments (already extracted above)
849
+ // Use type assertion for internal overload dispatch via .call()
850
+ const createFnFinal = originalCreate;
851
+ if (id) {
852
+ return createFnFinal.call(wrappedOps, id, processedData, options);
853
+ }
854
+ return createFnFinal.call(wrappedOps, processedData, options);
958
855
  };
856
+ // Add seed method if entity has seed configuration
857
+ if (entity.seedConfig) {
858
+ const seedConfig = entity.seedConfig;
859
+ wrappedOps['seed'] = async () => {
860
+ const { loadSeedData } = await import('./seed.js');
861
+ const records = await loadSeedData(seedConfig);
862
+ const provider = await getProvider();
863
+ for (const record of records) {
864
+ const { $id, ...data } = record;
865
+ // Upsert: check if exists, then update or create
866
+ const existing = await provider.get(entityName, $id);
867
+ if (existing) {
868
+ await provider.update(entityName, $id, data);
869
+ }
870
+ else {
871
+ await provider.create(entityName, $id, data);
872
+ }
873
+ }
874
+ return { count: records.length };
875
+ };
876
+ }
959
877
  // Make the entity operations callable as a tagged template literal
960
878
  entityOperations[entityName] = makeCallableEntityOps(wrappedOps, entityName);
961
879
  }
@@ -980,12 +898,12 @@ export function DB(schema, options) {
980
898
  const db = {
981
899
  $schema: parsedSchema,
982
900
  async get(url) {
983
- const provider = await resolveProvider();
901
+ const provider = await getProvider();
984
902
  const parsed = parseUrl(url);
985
903
  return provider.get(parsed.type, parsed.id);
986
904
  },
987
905
  async search(query, options) {
988
- const provider = await resolveProvider();
906
+ const provider = await getProvider();
989
907
  const results = [];
990
908
  for (const [typeName] of parsedSchema.entities) {
991
909
  const typeResults = await provider.search(typeName, query, options);
@@ -994,7 +912,7 @@ export function DB(schema, options) {
994
912
  return results;
995
913
  },
996
914
  async semanticSearch(query, options) {
997
- const provider = await resolveProvider();
915
+ const provider = await getProvider();
998
916
  const results = [];
999
917
  if (hasSemanticSearch(provider)) {
1000
918
  for (const [typeName] of parsedSchema.entities) {
@@ -1007,13 +925,19 @@ export function DB(schema, options) {
1007
925
  return results.slice(0, limit);
1008
926
  },
1009
927
  async count(type, where) {
1010
- const provider = await resolveProvider();
1011
- const results = await provider.list(type, { where });
928
+ const provider = await getProvider();
929
+ const listOpts = {};
930
+ if (where !== undefined)
931
+ listOpts.where = where;
932
+ const results = await provider.list(type, listOpts);
1012
933
  return results.length;
1013
934
  },
1014
935
  async forEach(options, callback) {
1015
- const provider = await resolveProvider();
1016
- const results = await provider.list(options.type, { where: options.where });
936
+ const provider = await getProvider();
937
+ const listOpts = {};
938
+ if (options.where !== undefined)
939
+ listOpts.where = options.where;
940
+ const results = await provider.list(options.type, listOpts);
1017
941
  const concurrency = options.concurrency ?? 1;
1018
942
  if (concurrency === 1) {
1019
943
  for (const entity of results) {
@@ -1027,7 +951,7 @@ export function DB(schema, options) {
1027
951
  }
1028
952
  },
1029
953
  async set(type, id, data) {
1030
- const provider = await resolveProvider();
954
+ const provider = await getProvider();
1031
955
  const existing = await provider.get(type, id);
1032
956
  if (existing) {
1033
957
  return provider.update(type, id, data);
@@ -1035,7 +959,7 @@ export function DB(schema, options) {
1035
959
  return provider.create(type, id, data);
1036
960
  },
1037
961
  async generate(options) {
1038
- const provider = await resolveProvider();
962
+ const provider = await getProvider();
1039
963
  if (options.mode === 'background') {
1040
964
  const { createMemoryProvider } = await import('../memory-provider.js');
1041
965
  const memProvider = provider;
@@ -1057,7 +981,7 @@ export function DB(schema, options) {
1057
981
  const events = {
1058
982
  on(pattern, handler) {
1059
983
  let unsubscribe = () => { };
1060
- resolveProvider().then((provider) => {
984
+ getProvider().then((provider) => {
1061
985
  if (hasEventsAPI(provider)) {
1062
986
  unsubscribe = provider.on(pattern, handler);
1063
987
  }
@@ -1065,7 +989,7 @@ export function DB(schema, options) {
1065
989
  return () => unsubscribe();
1066
990
  },
1067
991
  async emit(optionsOrType, data) {
1068
- const provider = await resolveProvider();
992
+ const provider = await getProvider();
1069
993
  if (hasEventsAPI(provider)) {
1070
994
  // The provider.emit has overloads: (options: CreateEventOptions) or (type: string, data: unknown)
1071
995
  if (typeof optionsOrType === 'string') {
@@ -1075,36 +999,45 @@ export function DB(schema, options) {
1075
999
  }
1076
1000
  const now = new Date();
1077
1001
  if (typeof optionsOrType === 'string') {
1078
- return {
1002
+ const baseEvent = {
1079
1003
  id: crypto.randomUUID(),
1080
1004
  actor: 'system',
1081
1005
  event: optionsOrType,
1082
- objectData: data,
1083
1006
  timestamp: now,
1084
1007
  };
1008
+ if (data !== undefined)
1009
+ baseEvent.objectData = data;
1010
+ return baseEvent;
1085
1011
  }
1086
- return {
1012
+ const baseEvent = {
1087
1013
  id: crypto.randomUUID(),
1088
1014
  actor: optionsOrType.actor,
1089
- actorData: optionsOrType.actorData,
1090
1015
  event: optionsOrType.event,
1091
- object: optionsOrType.object,
1092
- objectData: optionsOrType.objectData,
1093
- result: optionsOrType.result,
1094
- resultData: optionsOrType.resultData,
1095
- meta: optionsOrType.meta,
1096
1016
  timestamp: now,
1097
1017
  };
1018
+ if (optionsOrType.actorData !== undefined)
1019
+ baseEvent.actorData = optionsOrType.actorData;
1020
+ if (optionsOrType.object !== undefined)
1021
+ baseEvent.object = optionsOrType.object;
1022
+ if (optionsOrType.objectData !== undefined)
1023
+ baseEvent.objectData = optionsOrType.objectData;
1024
+ if (optionsOrType.result !== undefined)
1025
+ baseEvent.result = optionsOrType.result;
1026
+ if (optionsOrType.resultData !== undefined)
1027
+ baseEvent.resultData = optionsOrType.resultData;
1028
+ if (optionsOrType.meta !== undefined)
1029
+ baseEvent.meta = optionsOrType.meta;
1030
+ return baseEvent;
1098
1031
  },
1099
1032
  async list(options) {
1100
- const provider = await resolveProvider();
1033
+ const provider = await getProvider();
1101
1034
  if (hasEventsAPI(provider)) {
1102
1035
  return provider.listEvents(options);
1103
1036
  }
1104
1037
  return [];
1105
1038
  },
1106
1039
  async replay(options) {
1107
- const provider = await resolveProvider();
1040
+ const provider = await getProvider();
1108
1041
  if (hasEventsAPI(provider)) {
1109
1042
  await provider.replayEvents(options);
1110
1043
  }
@@ -1113,42 +1046,42 @@ export function DB(schema, options) {
1113
1046
  // Create Actions API (public version with full DBAction types)
1114
1047
  const actions = {
1115
1048
  async create(options) {
1116
- const provider = await resolveProvider();
1049
+ const provider = await getProvider();
1117
1050
  if (hasActionsAPI(provider)) {
1118
1051
  return provider.createAction(options);
1119
1052
  }
1120
1053
  throw new Error('Provider does not support actions');
1121
1054
  },
1122
1055
  async get(id) {
1123
- const provider = await resolveProvider();
1056
+ const provider = await getProvider();
1124
1057
  if (hasActionsAPI(provider)) {
1125
1058
  return provider.getAction(id);
1126
1059
  }
1127
1060
  return null;
1128
1061
  },
1129
1062
  async update(id, updates) {
1130
- const provider = await resolveProvider();
1063
+ const provider = await getProvider();
1131
1064
  if (hasActionsAPI(provider)) {
1132
1065
  return provider.updateAction(id, updates);
1133
1066
  }
1134
1067
  throw new Error('Provider does not support actions');
1135
1068
  },
1136
1069
  async list(options) {
1137
- const provider = await resolveProvider();
1070
+ const provider = await getProvider();
1138
1071
  if (hasActionsAPI(provider)) {
1139
1072
  return provider.listActions(options);
1140
1073
  }
1141
1074
  return [];
1142
1075
  },
1143
1076
  async retry(id) {
1144
- const provider = await resolveProvider();
1077
+ const provider = await getProvider();
1145
1078
  if (hasActionsAPI(provider)) {
1146
1079
  return provider.retryAction(id);
1147
1080
  }
1148
1081
  throw new Error('Provider does not support actions');
1149
1082
  },
1150
1083
  async cancel(id) {
1151
- const provider = await resolveProvider();
1084
+ const provider = await getProvider();
1152
1085
  if (hasActionsAPI(provider)) {
1153
1086
  await provider.cancelAction(id);
1154
1087
  }
@@ -1158,26 +1091,26 @@ export function DB(schema, options) {
1158
1091
  // Create Artifacts API
1159
1092
  const artifacts = {
1160
1093
  async get(url, type) {
1161
- const provider = await resolveProvider();
1094
+ const provider = await getProvider();
1162
1095
  if (hasArtifactsAPI(provider)) {
1163
1096
  return provider.getArtifact(url, type);
1164
1097
  }
1165
1098
  return null;
1166
1099
  },
1167
1100
  async set(url, type, data) {
1168
- const provider = await resolveProvider();
1101
+ const provider = await getProvider();
1169
1102
  if (hasArtifactsAPI(provider)) {
1170
1103
  await provider.setArtifact(url, type, data);
1171
1104
  }
1172
1105
  },
1173
1106
  async delete(url, type) {
1174
- const provider = await resolveProvider();
1107
+ const provider = await getProvider();
1175
1108
  if (hasArtifactsAPI(provider)) {
1176
1109
  await provider.deleteArtifact(url, type);
1177
1110
  }
1178
1111
  },
1179
1112
  async list(url) {
1180
- const provider = await resolveProvider();
1113
+ const provider = await getProvider();
1181
1114
  if (hasArtifactsAPI(provider)) {
1182
1115
  return provider.listArtifacts(url);
1183
1116
  }