ai-database 2.0.1 → 2.1.1

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 (88) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/actions.d.ts +247 -0
  3. package/dist/actions.d.ts.map +1 -0
  4. package/dist/actions.js +260 -0
  5. package/dist/actions.js.map +1 -0
  6. package/dist/ai-promise-db.d.ts +34 -2
  7. package/dist/ai-promise-db.d.ts.map +1 -1
  8. package/dist/ai-promise-db.js +511 -66
  9. package/dist/ai-promise-db.js.map +1 -1
  10. package/dist/constants.d.ts +16 -0
  11. package/dist/constants.d.ts.map +1 -0
  12. package/dist/constants.js +16 -0
  13. package/dist/constants.js.map +1 -0
  14. package/dist/events.d.ts +153 -0
  15. package/dist/events.d.ts.map +1 -0
  16. package/dist/events.js +154 -0
  17. package/dist/events.js.map +1 -0
  18. package/dist/index.d.ts +8 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +13 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/memory-provider.d.ts +144 -2
  23. package/dist/memory-provider.d.ts.map +1 -1
  24. package/dist/memory-provider.js +569 -13
  25. package/dist/memory-provider.js.map +1 -1
  26. package/dist/schema/cascade.d.ts +96 -0
  27. package/dist/schema/cascade.d.ts.map +1 -0
  28. package/dist/schema/cascade.js +528 -0
  29. package/dist/schema/cascade.js.map +1 -0
  30. package/dist/schema/index.d.ts +197 -0
  31. package/dist/schema/index.d.ts.map +1 -0
  32. package/dist/schema/index.js +1211 -0
  33. package/dist/schema/index.js.map +1 -0
  34. package/dist/schema/parse.d.ts +225 -0
  35. package/dist/schema/parse.d.ts.map +1 -0
  36. package/dist/schema/parse.js +732 -0
  37. package/dist/schema/parse.js.map +1 -0
  38. package/dist/schema/provider.d.ts +176 -0
  39. package/dist/schema/provider.d.ts.map +1 -0
  40. package/dist/schema/provider.js +258 -0
  41. package/dist/schema/provider.js.map +1 -0
  42. package/dist/schema/resolve.d.ts +87 -0
  43. package/dist/schema/resolve.d.ts.map +1 -0
  44. package/dist/schema/resolve.js +474 -0
  45. package/dist/schema/resolve.js.map +1 -0
  46. package/dist/schema/semantic.d.ts +53 -0
  47. package/dist/schema/semantic.d.ts.map +1 -0
  48. package/dist/schema/semantic.js +247 -0
  49. package/dist/schema/semantic.js.map +1 -0
  50. package/dist/schema/types.d.ts +528 -0
  51. package/dist/schema/types.d.ts.map +1 -0
  52. package/dist/schema/types.js +9 -0
  53. package/dist/schema/types.js.map +1 -0
  54. package/dist/schema.d.ts +24 -867
  55. package/dist/schema.d.ts.map +1 -1
  56. package/dist/schema.js +41 -1124
  57. package/dist/schema.js.map +1 -1
  58. package/dist/semantic.d.ts +175 -0
  59. package/dist/semantic.d.ts.map +1 -0
  60. package/dist/semantic.js +338 -0
  61. package/dist/semantic.js.map +1 -0
  62. package/dist/types.d.ts +14 -0
  63. package/dist/types.d.ts.map +1 -1
  64. package/dist/types.js.map +1 -1
  65. package/package.json +13 -4
  66. package/.turbo/turbo-build.log +0 -5
  67. package/TESTING.md +0 -410
  68. package/TEST_SUMMARY.md +0 -250
  69. package/TODO.md +0 -128
  70. package/src/ai-promise-db.ts +0 -1243
  71. package/src/authorization.ts +0 -1102
  72. package/src/durable-clickhouse.ts +0 -596
  73. package/src/durable-promise.ts +0 -582
  74. package/src/execution-queue.ts +0 -608
  75. package/src/index.test.ts +0 -868
  76. package/src/index.ts +0 -337
  77. package/src/linguistic.ts +0 -404
  78. package/src/memory-provider.test.ts +0 -1036
  79. package/src/memory-provider.ts +0 -1119
  80. package/src/schema.test.ts +0 -1254
  81. package/src/schema.ts +0 -2296
  82. package/src/tests.ts +0 -725
  83. package/src/types.ts +0 -1177
  84. package/test/README.md +0 -153
  85. package/test/edge-cases.test.ts +0 -646
  86. package/test/provider-resolution.test.ts +0 -402
  87. package/tsconfig.json +0 -9
  88. package/vitest.config.ts +0 -19
@@ -0,0 +1,1211 @@
1
+ /**
2
+ * Schema-first Database Definition
3
+ *
4
+ * Declarative schema with automatic bi-directional relationships.
5
+ * Uses mdxld conventions for entity structure.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const { db } = DB({
10
+ * Post: {
11
+ * title: 'string',
12
+ * author: 'Author.posts', // one-to-many: Post.author -> Author, Author.posts -> Post[]
13
+ * tags: ['Tag.posts'], // many-to-many: Post.tags -> Tag[], Tag.posts -> Post[]
14
+ * },
15
+ * Author: {
16
+ * name: 'string',
17
+ * // posts: Post[] auto-created from backref
18
+ * },
19
+ * Tag: {
20
+ * name: 'string',
21
+ * // posts: Post[] auto-created from backref
22
+ * }
23
+ * })
24
+ *
25
+ * // Typed access
26
+ * const post = await db.Post.get('123')
27
+ * post.author // Author (single)
28
+ * post.tags // Tag[] (array)
29
+ * ```
30
+ *
31
+ * @packageDocumentation
32
+ */
33
+ import { DBPromise, wrapEntityOperations, setSchemaRelationInfo } from '../ai-promise-db.js';
34
+ // Re-export parse functions
35
+ export { parseOperator, parseField, parseSchema, isPrimitiveType } from './parse.js';
36
+ export { setProvider, resolveProvider, hasSemanticSearch, hasHybridSearch, hasEventsAPI, hasActionsAPI, hasArtifactsAPI, hasEmbeddingsConfig, } from './provider.js';
37
+ // Re-export resolve functions
38
+ export { isEntityId, inferTypeFromField, resolveContextPath, resolveInstructions, prefetchContext, isPromptField, resolveNestedPending, resolveReferenceSpec, hydrateEntity, } from './resolve.js';
39
+ // Re-export cascade functions
40
+ export { generateContextAwareValue, generateAIFields, generateEntity, resolveForwardExact, generateNaturalLanguageContent, } from './cascade.js';
41
+ // Re-export semantic functions
42
+ export { resolveBackwardFuzzy, resolveForwardFuzzy } from './semantic.js';
43
+ import { Verbs, parseUrl } from '../types.js';
44
+ import { inferNoun, getTypeMeta, conjugate } from '../linguistic.js';
45
+ import { parseSchema } from './parse.js';
46
+ import { resolveProvider, hasSemanticSearch, hasHybridSearch, hasEventsAPI, hasActionsAPI, hasArtifactsAPI, hasEmbeddingsConfig, } from './provider.js';
47
+ import { hydrateEntity, resolveReferenceSpec } from './resolve.js';
48
+ import { resolveForwardExact, generateAIFields, generateContextAwareValue, generateNaturalLanguageContent } from './cascade.js';
49
+ import { resolveBackwardFuzzy, resolveForwardFuzzy } from './semantic.js';
50
+ // =============================================================================
51
+ // Noun/Verb Helpers
52
+ // =============================================================================
53
+ /**
54
+ * Create a Noun definition with type inference
55
+ */
56
+ export function defineNoun(noun) {
57
+ return noun;
58
+ }
59
+ /**
60
+ * Create a Verb definition with type inference
61
+ */
62
+ export function defineVerb(verb) {
63
+ return verb;
64
+ }
65
+ /**
66
+ * Convert a Noun to an EntitySchema for use with DB()
67
+ */
68
+ export function nounToSchema(noun) {
69
+ const schema = {};
70
+ // Add properties
71
+ if (noun.properties) {
72
+ for (const [name, prop] of Object.entries(noun.properties)) {
73
+ let type = prop.type;
74
+ if (prop.array)
75
+ type = `${type}[]`;
76
+ if (prop.optional)
77
+ type = `${type}?`;
78
+ schema[name] = type;
79
+ }
80
+ }
81
+ // Add relationships
82
+ if (noun.relationships) {
83
+ for (const [name, rel] of Object.entries(noun.relationships)) {
84
+ const baseType = rel.type.replace('[]', '');
85
+ const isArray = rel.type.endsWith('[]');
86
+ if (rel.backref) {
87
+ schema[name] = isArray ? [`${baseType}.${rel.backref}`] : `${baseType}.${rel.backref}`;
88
+ }
89
+ else {
90
+ schema[name] = rel.type;
91
+ }
92
+ }
93
+ }
94
+ return schema;
95
+ }
96
+ // =============================================================================
97
+ // Built-in Schema Types
98
+ // =============================================================================
99
+ /**
100
+ * Built-in Thing schema - base type for all entities
101
+ */
102
+ export const ThingSchema = {
103
+ type: 'Noun.things',
104
+ };
105
+ /**
106
+ * Built-in Noun schema for storing type definitions
107
+ */
108
+ export const NounSchema = {
109
+ name: 'string',
110
+ singular: 'string',
111
+ plural: 'string',
112
+ slug: 'string',
113
+ slugPlural: 'string',
114
+ description: 'string?',
115
+ properties: 'json?',
116
+ relationships: 'json?',
117
+ actions: 'json?',
118
+ events: 'json?',
119
+ metadata: 'json?',
120
+ };
121
+ /**
122
+ * Built-in Verb schema for storing action definitions
123
+ */
124
+ export const VerbSchema = {
125
+ action: 'string',
126
+ actor: 'string?',
127
+ act: 'string?',
128
+ activity: 'string?',
129
+ result: 'string?',
130
+ reverse: 'json?',
131
+ inverse: 'string?',
132
+ description: 'string?',
133
+ };
134
+ /**
135
+ * Built-in Edge schema for relationships between types
136
+ */
137
+ export const EdgeSchema = {
138
+ from: 'string',
139
+ name: 'string',
140
+ to: 'string',
141
+ backref: 'string?',
142
+ cardinality: 'string',
143
+ direction: 'string',
144
+ matchMode: 'string?',
145
+ required: 'boolean?',
146
+ description: 'string?',
147
+ };
148
+ /**
149
+ * System types that are auto-created in every database
150
+ */
151
+ export const SystemSchema = {
152
+ Thing: ThingSchema,
153
+ Noun: NounSchema,
154
+ Verb: VerbSchema,
155
+ Edge: EdgeSchema,
156
+ };
157
+ /**
158
+ * Create Edge records from schema relationships
159
+ */
160
+ export function createEdgeRecords(typeName, schema, parsedEntity) {
161
+ const edges = [];
162
+ for (const [fieldName, field] of parsedEntity.fields) {
163
+ if (field.isRelation && field.relatedType) {
164
+ const direction = field.direction ?? 'forward';
165
+ const matchMode = field.matchMode ?? 'exact';
166
+ const isBackward = direction === 'backward';
167
+ const from = isBackward ? field.relatedType : typeName;
168
+ const to = isBackward ? typeName : field.relatedType;
169
+ let cardinality;
170
+ if (field.isArray) {
171
+ cardinality = field.backref ? 'many-to-many' : 'one-to-many';
172
+ }
173
+ else {
174
+ cardinality = 'many-to-one';
175
+ }
176
+ edges.push({
177
+ from,
178
+ name: fieldName,
179
+ to,
180
+ backref: field.backref,
181
+ cardinality,
182
+ direction,
183
+ matchMode,
184
+ });
185
+ }
186
+ }
187
+ return edges;
188
+ }
189
+ /**
190
+ * Create a Noun record from a type name and optional schema
191
+ */
192
+ export function createNounRecord(typeName, schema, nounDef) {
193
+ const meta = getTypeMeta(typeName);
194
+ const inferred = inferNoun(typeName);
195
+ return {
196
+ name: typeName,
197
+ singular: nounDef?.singular ?? meta.singular,
198
+ plural: nounDef?.plural ?? meta.plural,
199
+ slug: meta.slug,
200
+ slugPlural: meta.slugPlural,
201
+ description: nounDef?.description,
202
+ properties: nounDef?.properties ?? (schema ? schemaToProperties(schema) : undefined),
203
+ relationships: nounDef?.relationships,
204
+ actions: nounDef?.actions ?? inferred.actions,
205
+ events: nounDef?.events ?? inferred.events,
206
+ metadata: nounDef?.metadata,
207
+ };
208
+ }
209
+ /**
210
+ * Convert EntitySchema to NounProperty format
211
+ */
212
+ function schemaToProperties(schema) {
213
+ const properties = {};
214
+ for (const [name, def] of Object.entries(schema)) {
215
+ const defStr = Array.isArray(def) ? def[0] : def;
216
+ const isOptional = defStr.endsWith('?');
217
+ const isArray = defStr.endsWith('[]') || Array.isArray(def);
218
+ const baseType = defStr.replace(/[\?\[\]]/g, '').split('.')[0];
219
+ properties[name] = {
220
+ type: baseType,
221
+ optional: isOptional,
222
+ array: isArray,
223
+ };
224
+ }
225
+ return properties;
226
+ }
227
+ // =============================================================================
228
+ // Natural Language Query Implementation
229
+ // =============================================================================
230
+ let nlQueryGenerator = null;
231
+ /**
232
+ * Set the AI generator for natural language queries
233
+ */
234
+ export function setNLQueryGenerator(generator) {
235
+ nlQueryGenerator = generator;
236
+ }
237
+ function buildNLQueryContext(schema, targetType) {
238
+ const types = [];
239
+ for (const [name, entity] of schema.entities) {
240
+ const fields = [];
241
+ const relationships = [];
242
+ for (const [fieldName, field] of entity.fields) {
243
+ if (field.isRelation && field.relatedType) {
244
+ relationships.push({
245
+ name: fieldName,
246
+ to: field.relatedType,
247
+ cardinality: field.isArray ? 'many' : 'one',
248
+ });
249
+ }
250
+ else {
251
+ fields.push(fieldName);
252
+ }
253
+ }
254
+ const meta = getTypeMeta(name);
255
+ types.push({
256
+ name,
257
+ singular: meta.singular,
258
+ plural: meta.plural,
259
+ fields,
260
+ relationships,
261
+ });
262
+ }
263
+ return { types, targetType };
264
+ }
265
+ async function executeNLQuery(question, schema, targetType) {
266
+ if (!nlQueryGenerator) {
267
+ const provider = await resolveProvider();
268
+ const results = [];
269
+ // Simple heuristic for common "list all" patterns in fallback mode
270
+ const lowerQuestion = question.toLowerCase().trim();
271
+ const isListAllQuery = /^(show|list|get|find|display)\s+(all|every|the)?\s*/i.test(lowerQuestion) ||
272
+ lowerQuestion === '' ||
273
+ /\ball\b/i.test(lowerQuestion);
274
+ if (targetType) {
275
+ if (isListAllQuery) {
276
+ // For "show all X" queries, just list everything
277
+ const listResults = await provider.list(targetType);
278
+ results.push(...listResults);
279
+ }
280
+ else {
281
+ const searchResults = await provider.search(targetType, question);
282
+ results.push(...searchResults);
283
+ }
284
+ }
285
+ else {
286
+ for (const [typeName] of schema.entities) {
287
+ if (isListAllQuery) {
288
+ const listResults = await provider.list(typeName);
289
+ results.push(...listResults);
290
+ }
291
+ else {
292
+ const searchResults = await provider.search(typeName, question);
293
+ results.push(...searchResults);
294
+ }
295
+ }
296
+ }
297
+ return {
298
+ interpretation: `Search for "${question}"`,
299
+ confidence: 0.5,
300
+ results,
301
+ explanation: 'Fallback to keyword search (no AI generator configured)',
302
+ };
303
+ }
304
+ const context = buildNLQueryContext(schema, targetType);
305
+ const plan = await nlQueryGenerator(question, context);
306
+ const provider = await resolveProvider();
307
+ const results = [];
308
+ for (const typeName of plan.types) {
309
+ let typeResults;
310
+ if (plan.search) {
311
+ typeResults = await provider.search(typeName, plan.search, {
312
+ where: plan.filters,
313
+ });
314
+ }
315
+ else {
316
+ typeResults = await provider.list(typeName, {
317
+ where: plan.filters,
318
+ });
319
+ }
320
+ results.push(...typeResults);
321
+ }
322
+ return {
323
+ interpretation: plan.interpretation,
324
+ confidence: plan.confidence,
325
+ results,
326
+ query: JSON.stringify({ types: plan.types, filters: plan.filters, search: plan.search }),
327
+ };
328
+ }
329
+ function createNLQueryFn(schema, typeName) {
330
+ return async (strings, ...values) => {
331
+ const question = strings.reduce((acc, str, i) => {
332
+ return acc + str + (values[i] !== undefined ? String(values[i]) : '');
333
+ }, '');
334
+ return executeNLQuery(question, schema, typeName);
335
+ };
336
+ }
337
+ // =============================================================================
338
+ // Edge Entity Operations
339
+ // =============================================================================
340
+ function createEdgeEntityOperations(schemaEdgeRecords, getProvider) {
341
+ async function getRuntimeEdges() {
342
+ try {
343
+ const provider = await getProvider();
344
+ const runtimeEdges = await provider.list('Edge');
345
+ return runtimeEdges;
346
+ }
347
+ catch {
348
+ return [];
349
+ }
350
+ }
351
+ async function getAllEdges() {
352
+ const runtimeEdges = await getRuntimeEdges();
353
+ const runtimeEdgeKeys = new Set(runtimeEdges.map(e => `${e.from}:${e.name}`));
354
+ const filteredSchemaEdges = schemaEdgeRecords.filter(e => {
355
+ const key = `${e.from}:${e.name}`;
356
+ const hasRuntimeVersion = runtimeEdgeKeys.has(key);
357
+ if (hasRuntimeVersion && e.matchMode === 'fuzzy') {
358
+ return false;
359
+ }
360
+ return !hasRuntimeVersion;
361
+ });
362
+ return [...filteredSchemaEdges, ...runtimeEdges];
363
+ }
364
+ return {
365
+ async get(id) {
366
+ const runtimeEdges = await getRuntimeEdges();
367
+ const runtimeMatch = runtimeEdges.find(e => e.$id === id || `${e.from}:${e.name}` === id);
368
+ if (runtimeMatch)
369
+ return { ...runtimeMatch, $type: 'Edge' };
370
+ return schemaEdgeRecords.find(e => `${e.from}:${e.name}` === id) ?? null;
371
+ },
372
+ async list(options) {
373
+ let results = await getAllEdges();
374
+ if (options?.where) {
375
+ for (const [key, value] of Object.entries(options.where)) {
376
+ results = results.filter(e => e[key] === value);
377
+ }
378
+ }
379
+ return results.map(e => ({
380
+ ...e,
381
+ $id: e.$id || `${e.from}:${e.name}`,
382
+ $type: 'Edge',
383
+ }));
384
+ },
385
+ async find(where) {
386
+ let results = await getAllEdges();
387
+ for (const [key, value] of Object.entries(where)) {
388
+ results = results.filter(e => e[key] === value);
389
+ }
390
+ return results.map(e => ({
391
+ ...e,
392
+ $id: e.$id || `${e.from}:${e.name}`,
393
+ $type: 'Edge',
394
+ }));
395
+ },
396
+ async search(query) {
397
+ const allEdges = await getAllEdges();
398
+ const queryLower = query.toLowerCase();
399
+ return allEdges
400
+ .filter(e => String(e.from).toLowerCase().includes(queryLower) ||
401
+ String(e.name).toLowerCase().includes(queryLower) ||
402
+ String(e.to).toLowerCase().includes(queryLower))
403
+ .map(e => ({
404
+ ...e,
405
+ $id: e.$id || `${e.from}:${e.name}`,
406
+ $type: 'Edge',
407
+ }));
408
+ },
409
+ async create() {
410
+ throw new Error('Cannot manually create Edge records - they are auto-generated');
411
+ },
412
+ async update() {
413
+ throw new Error('Cannot manually update Edge records - they are auto-generated');
414
+ },
415
+ async upsert() {
416
+ throw new Error('Cannot manually upsert Edge records - they are auto-generated');
417
+ },
418
+ async delete() {
419
+ throw new Error('Cannot manually delete Edge records - they are auto-generated');
420
+ },
421
+ async forEach(optionsOrCallback, maybeCallback) {
422
+ const options = typeof optionsOrCallback === 'function' ? undefined : optionsOrCallback;
423
+ const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : maybeCallback;
424
+ const items = await this.list(options);
425
+ for (const item of items) {
426
+ await callback(item);
427
+ }
428
+ },
429
+ async semanticSearch() {
430
+ return [];
431
+ },
432
+ async hybridSearch() {
433
+ return [];
434
+ },
435
+ };
436
+ }
437
+ // =============================================================================
438
+ // Entity Operations Factory
439
+ // =============================================================================
440
+ function createEntityOperations(typeName, entity, schema) {
441
+ return {
442
+ async get(id) {
443
+ const provider = await resolveProvider();
444
+ const result = await provider.get(typeName, id);
445
+ if (!result)
446
+ return null;
447
+ return hydrateEntity(result, entity, schema, resolveProvider);
448
+ },
449
+ async list(options) {
450
+ const provider = await resolveProvider();
451
+ const results = await provider.list(typeName, options);
452
+ return Promise.all(results.map((r) => hydrateEntity(r, entity, schema, resolveProvider)));
453
+ },
454
+ async find(where) {
455
+ const provider = await resolveProvider();
456
+ const results = await provider.list(typeName, {
457
+ where: where,
458
+ });
459
+ return Promise.all(results.map((r) => hydrateEntity(r, entity, schema, resolveProvider)));
460
+ },
461
+ async search(query, options) {
462
+ const provider = await resolveProvider();
463
+ const results = await provider.search(typeName, query, options);
464
+ return Promise.all(results.map((r) => hydrateEntity(r, entity, schema, resolveProvider)));
465
+ },
466
+ async create(idOrData, maybeData) {
467
+ const provider = await resolveProvider();
468
+ const providedId = typeof idOrData === 'string' ? idOrData : undefined;
469
+ const data = typeof idOrData === 'string'
470
+ ? maybeData
471
+ : idOrData;
472
+ const entityId = providedId || crypto.randomUUID();
473
+ const { data: resolvedData, pendingRelations } = await resolveForwardExact(typeName, data, entity, schema, provider, entityId);
474
+ const { data: fuzzyResolvedData, pendingRelations: fuzzyPendingRelations } = await resolveForwardFuzzy(typeName, resolvedData, entity, schema, provider, entityId);
475
+ const backwardResolvedData = await resolveBackwardFuzzy(typeName, fuzzyResolvedData, entity, schema, provider);
476
+ const finalData = await generateAIFields(backwardResolvedData, typeName, entity, schema, provider);
477
+ const result = await provider.create(typeName, entityId, finalData);
478
+ for (const rel of pendingRelations) {
479
+ await provider.relate(typeName, entityId, rel.fieldName, rel.targetType, rel.targetId);
480
+ }
481
+ const createdEdgeIds = new Set();
482
+ for (const rel of fuzzyPendingRelations) {
483
+ await provider.relate(typeName, entityId, rel.fieldName, rel.targetType, rel.targetId, {
484
+ matchMode: 'fuzzy',
485
+ similarity: rel.similarity
486
+ });
487
+ const edgeId = `${typeName}:${rel.fieldName}:${entityId}:${rel.targetId}`;
488
+ if (!createdEdgeIds.has(edgeId)) {
489
+ createdEdgeIds.add(edgeId);
490
+ try {
491
+ await provider.create('Edge', edgeId, {
492
+ from: typeName,
493
+ name: rel.fieldName,
494
+ to: rel.targetType,
495
+ direction: 'forward',
496
+ matchMode: 'fuzzy',
497
+ similarity: rel.similarity,
498
+ fromId: entityId,
499
+ toId: rel.targetId,
500
+ });
501
+ }
502
+ catch {
503
+ // Edge already exists
504
+ }
505
+ }
506
+ }
507
+ return hydrateEntity(result, entity, schema, resolveProvider);
508
+ },
509
+ async update(id, data) {
510
+ const provider = await resolveProvider();
511
+ const result = await provider.update(typeName, id, data);
512
+ return hydrateEntity(result, entity, schema, resolveProvider);
513
+ },
514
+ async upsert(id, data) {
515
+ const provider = await resolveProvider();
516
+ const existing = await provider.get(typeName, id);
517
+ if (existing) {
518
+ const result = await provider.update(typeName, id, data);
519
+ return hydrateEntity(result, entity, schema, resolveProvider);
520
+ }
521
+ const result = await provider.create(typeName, id, data);
522
+ return hydrateEntity(result, entity, schema, resolveProvider);
523
+ },
524
+ async delete(id) {
525
+ const provider = await resolveProvider();
526
+ return provider.delete(typeName, id);
527
+ },
528
+ async forEach(optionsOrCallback, maybeCallback) {
529
+ const options = typeof optionsOrCallback === 'function' ? undefined : optionsOrCallback;
530
+ const callback = typeof optionsOrCallback === 'function'
531
+ ? optionsOrCallback
532
+ : maybeCallback;
533
+ const items = await this.list(options);
534
+ for (const item of items) {
535
+ await callback(item);
536
+ }
537
+ },
538
+ async semanticSearch(query, options) {
539
+ const provider = await resolveProvider();
540
+ if (hasSemanticSearch(provider)) {
541
+ const results = await provider.semanticSearch(typeName, query, options);
542
+ return Promise.all(results.map((r) => ({
543
+ ...hydrateEntity(r, entity, schema, resolveProvider),
544
+ $score: r.$score,
545
+ })));
546
+ }
547
+ return [];
548
+ },
549
+ async hybridSearch(query, options) {
550
+ const provider = await resolveProvider();
551
+ if (hasHybridSearch(provider)) {
552
+ const results = await provider.hybridSearch(typeName, query, options);
553
+ return Promise.all(results.map((r) => ({
554
+ ...hydrateEntity(r, entity, schema, resolveProvider),
555
+ $rrfScore: r.$rrfScore,
556
+ $ftsRank: r.$ftsRank,
557
+ $semanticRank: r.$semanticRank,
558
+ $score: r.$score,
559
+ })));
560
+ }
561
+ return [];
562
+ },
563
+ async draft(data, options) {
564
+ const draftData = { ...data, $phase: 'draft' };
565
+ const refs = {};
566
+ for (const [fieldName, field] of entity.fields) {
567
+ if (draftData[fieldName] !== undefined && draftData[fieldName] !== null) {
568
+ continue;
569
+ }
570
+ if (field.operator && field.relatedType) {
571
+ const matchMode = field.matchMode ?? (field.operator.includes('~') ? 'fuzzy' : 'exact');
572
+ if (field.isArray) {
573
+ const generatedText = generateNaturalLanguageContent(fieldName, field.prompt, field.relatedType, data);
574
+ draftData[fieldName] = generatedText;
575
+ const refSpec = {
576
+ field: fieldName,
577
+ operator: field.operator,
578
+ type: field.relatedType,
579
+ matchMode,
580
+ resolved: false,
581
+ prompt: field.prompt,
582
+ generatedText,
583
+ };
584
+ refs[fieldName] = [refSpec];
585
+ if (options?.stream && options.onChunk) {
586
+ options.onChunk(generatedText);
587
+ }
588
+ }
589
+ else {
590
+ const generatedText = generateNaturalLanguageContent(fieldName, field.prompt, field.relatedType, data);
591
+ draftData[fieldName] = generatedText;
592
+ refs[fieldName] = {
593
+ field: fieldName,
594
+ operator: field.operator,
595
+ type: field.relatedType,
596
+ matchMode,
597
+ resolved: false,
598
+ prompt: field.prompt,
599
+ generatedText,
600
+ };
601
+ if (options?.stream && options.onChunk) {
602
+ options.onChunk(generatedText);
603
+ }
604
+ }
605
+ }
606
+ }
607
+ draftData.$refs = refs;
608
+ return draftData;
609
+ },
610
+ async resolve(draft, options) {
611
+ // Draft<T> interface requires $phase: 'draft', so we can access it directly
612
+ if (draft.$phase !== 'draft') {
613
+ throw new Error('Cannot resolve entity: not a draft (missing $phase: "draft")');
614
+ }
615
+ const provider = await resolveProvider();
616
+ const resolved = { ...draft };
617
+ const errors = [];
618
+ delete resolved.$refs;
619
+ resolved.$phase = 'resolved';
620
+ const refs = draft.$refs;
621
+ for (const [fieldName, refSpec] of Object.entries(refs)) {
622
+ try {
623
+ if (Array.isArray(refSpec)) {
624
+ const resolvedIds = [];
625
+ for (const spec of refSpec) {
626
+ const resolvedId = await resolveReferenceSpec(spec, resolved, schema, provider, generateContextAwareValue);
627
+ if (resolvedId) {
628
+ resolvedIds.push(resolvedId);
629
+ options?.onResolved?.(fieldName, resolvedId);
630
+ }
631
+ }
632
+ resolved[fieldName] = resolvedIds;
633
+ }
634
+ else {
635
+ const resolvedId = await resolveReferenceSpec(refSpec, resolved, schema, provider, generateContextAwareValue);
636
+ if (resolvedId) {
637
+ resolved[fieldName] = resolvedId;
638
+ options?.onResolved?.(fieldName, resolvedId);
639
+ }
640
+ }
641
+ }
642
+ catch (err) {
643
+ const errorMsg = err instanceof Error ? err.message : String(err);
644
+ if (options?.onError === 'skip') {
645
+ errors.push({ field: fieldName, error: errorMsg });
646
+ }
647
+ else {
648
+ throw err;
649
+ }
650
+ }
651
+ }
652
+ if (errors.length > 0 || options?.onError === 'skip') {
653
+ // resolved is typed as Record<string, unknown>, so we can assign $errors directly
654
+ resolved.$errors = errors;
655
+ }
656
+ return resolved;
657
+ },
658
+ };
659
+ }
660
+ // =============================================================================
661
+ // DB Factory
662
+ // =============================================================================
663
+ /**
664
+ * Create a typed database from a schema definition
665
+ */
666
+ export function DB(schema, options) {
667
+ const parsedSchema = parseSchema(schema);
668
+ // Add Edge entity to the parsed schema
669
+ const edgeEntity = {
670
+ name: 'Edge',
671
+ fields: new Map([
672
+ ['from', { name: 'from', type: 'string', isArray: false, isOptional: false, isRelation: false }],
673
+ ['name', { name: 'name', type: 'string', isArray: false, isOptional: false, isRelation: false }],
674
+ ['to', { name: 'to', type: 'string', isArray: false, isOptional: false, isRelation: false }],
675
+ ['backref', { name: 'backref', type: 'string', isArray: false, isOptional: true, isRelation: false }],
676
+ ['cardinality', { name: 'cardinality', type: 'string', isArray: false, isOptional: false, isRelation: false }],
677
+ ['direction', { name: 'direction', type: 'string', isArray: false, isOptional: false, isRelation: false }],
678
+ ['matchMode', { name: 'matchMode', type: 'string', isArray: false, isOptional: true, isRelation: false }],
679
+ ]),
680
+ };
681
+ parsedSchema.entities.set('Edge', edgeEntity);
682
+ // Configure provider with embeddings settings if provided
683
+ if (options?.embeddings) {
684
+ resolveProvider().then(provider => {
685
+ if (hasEmbeddingsConfig(provider)) {
686
+ provider.setEmbeddingsConfig(options.embeddings);
687
+ }
688
+ });
689
+ }
690
+ // Collect all edge records from the schema
691
+ const allEdgeRecords = [];
692
+ for (const [entityName, entity] of parsedSchema.entities) {
693
+ if (entityName !== 'Edge') {
694
+ const edgeRecords = createEdgeRecords(entityName, schema[entityName] ?? {}, entity);
695
+ allEdgeRecords.push(...edgeRecords);
696
+ }
697
+ }
698
+ // Build and set schema relation info for batch loading
699
+ // Maps entityType -> fieldName -> relatedType
700
+ const relationInfo = new Map();
701
+ for (const [entityName, entity] of parsedSchema.entities) {
702
+ const fieldRelations = new Map();
703
+ for (const [fieldName, field] of entity.fields) {
704
+ if (field.isRelation && field.relatedType) {
705
+ fieldRelations.set(fieldName, field.relatedType);
706
+ }
707
+ }
708
+ if (fieldRelations.size > 0) {
709
+ relationInfo.set(entityName, fieldRelations);
710
+ }
711
+ }
712
+ setSchemaRelationInfo(relationInfo);
713
+ // Create Actions API early for internal use by wrapEntityOperations
714
+ // This API adapts DBAction to ForEachActionsAPI interface
715
+ const actionsAPI = {
716
+ async create(data) {
717
+ const provider = await resolveProvider();
718
+ if (hasActionsAPI(provider)) {
719
+ const action = await provider.createAction(data);
720
+ return { id: action.id };
721
+ }
722
+ throw new Error('Provider does not support actions');
723
+ },
724
+ async get(id) {
725
+ const provider = await resolveProvider();
726
+ if (hasActionsAPI(provider)) {
727
+ const action = await provider.getAction(id);
728
+ if (!action)
729
+ return null;
730
+ // Adapt DBAction to ForEachActionState
731
+ return {
732
+ id: action.id,
733
+ type: action.action ?? action.type ?? 'unknown',
734
+ status: action.status,
735
+ progress: action.progress,
736
+ total: action.total,
737
+ data: action.objectData ?? {},
738
+ result: action.result,
739
+ error: action.error,
740
+ };
741
+ }
742
+ return null;
743
+ },
744
+ async update(id, updates) {
745
+ const provider = await resolveProvider();
746
+ if (hasActionsAPI(provider)) {
747
+ await provider.updateAction(id, {
748
+ status: updates.status,
749
+ progress: updates.progress,
750
+ // ForEachResult needs to be converted to Record<string, unknown> for DBAction.result
751
+ result: updates.result,
752
+ error: updates.error,
753
+ });
754
+ return;
755
+ }
756
+ throw new Error('Provider does not support actions');
757
+ },
758
+ };
759
+ // Create entity operations
760
+ // Using Record<string, unknown> with [key: string]: unknown allows flexible method access
761
+ // while being safer than `any`. The actual operations implement EntityOperations<T>
762
+ // but we lose the generic when storing by entity name.
763
+ //
764
+ // IMPORTANT: Each entity must be both:
765
+ // 1. Callable as a tagged template literal: db.Lead`query`
766
+ // 2. An object with methods: db.Lead.get(), db.Lead.list(), etc.
767
+ //
768
+ // We achieve this by creating a function and attaching methods to it.
769
+ const entityOperations = {};
770
+ const eventHandlersForOps = new Map();
771
+ function emitInternalEventForOps(eventType, data) {
772
+ const handlers = eventHandlersForOps.get(eventType);
773
+ if (handlers) {
774
+ for (const handler of handlers) {
775
+ try {
776
+ handler(data);
777
+ }
778
+ catch (e) {
779
+ console.error(`Error in event handler for ${eventType}:`, e);
780
+ }
781
+ }
782
+ }
783
+ }
784
+ /**
785
+ * Make entity operations callable as a tagged template literal
786
+ * This allows both: db.Lead.get('id') and db.Lead`natural language query`
787
+ */
788
+ function makeCallableEntityOps(ops, entityName) {
789
+ // Create the NL query function for this entity type
790
+ const nlQueryFn = createNLQueryFn(parsedSchema, entityName);
791
+ // Create a function that acts as the tagged template literal handler
792
+ const callableOps = function (strings, ...values) {
793
+ return nlQueryFn(strings, ...values);
794
+ };
795
+ // Copy all methods from the wrapped operations to the function
796
+ Object.assign(callableOps, ops);
797
+ return callableOps;
798
+ }
799
+ for (const [entityName, entity] of parsedSchema.entities) {
800
+ if (entityName === 'Edge') {
801
+ const edgeOps = createEdgeEntityOperations(allEdgeRecords, resolveProvider);
802
+ const wrappedEdgeOps = wrapEntityOperations(entityName, edgeOps, actionsAPI);
803
+ entityOperations[entityName] = makeCallableEntityOps(wrappedEdgeOps, entityName);
804
+ }
805
+ else {
806
+ const baseOps = createEntityOperations(entityName, entity, parsedSchema);
807
+ const wrappedOps = wrapEntityOperations(entityName, baseOps, actionsAPI);
808
+ // baseOps has optional draft/resolve methods - EntityOperations<T> defines them as optional
809
+ const draftMethod = baseOps.draft;
810
+ const resolveMethod = baseOps.resolve;
811
+ const draftFn = async (data, options) => {
812
+ if (!draftMethod) {
813
+ throw new Error(`Draft method not available for ${entityName}`);
814
+ }
815
+ const draft = await draftMethod(data, options);
816
+ draft.$type = entityName;
817
+ emitInternalEventForOps('draft', draft);
818
+ return draft;
819
+ };
820
+ wrappedOps.draft = draftFn;
821
+ const resolveFn = async (draft, options) => {
822
+ if (!resolveMethod) {
823
+ throw new Error(`Resolve method not available for ${entityName}`);
824
+ }
825
+ const resolved = await resolveMethod(draft, options);
826
+ if (resolved && typeof resolved === 'object') {
827
+ resolved.$type = entityName;
828
+ }
829
+ emitInternalEventForOps('resolve', resolved);
830
+ return resolved;
831
+ };
832
+ wrappedOps.resolve = resolveFn;
833
+ const originalCreate = wrappedOps.create;
834
+ wrappedOps.create = async (...args) => {
835
+ let id;
836
+ let data;
837
+ let options;
838
+ if (typeof args[0] === 'string') {
839
+ id = args[0];
840
+ data = args[1];
841
+ options = args[2];
842
+ }
843
+ else {
844
+ data = args[0];
845
+ options = args[1];
846
+ }
847
+ if (options?.draftOnly) {
848
+ const draft = await draftFn(data);
849
+ return draft;
850
+ }
851
+ const effectiveMaxDepth = options?.maxDepth ?? (options?.cascade ? 3 : 0);
852
+ if (options?.cascade && effectiveMaxDepth > 0) {
853
+ const provider = await resolveProvider();
854
+ const entityDef = parsedSchema.entities.get(entityName);
855
+ if (entityDef) {
856
+ // CreateEntityOptions now includes _cascadeState as an optional property
857
+ const cascadeState = options._cascadeState ?? {
858
+ totalEntitiesCreated: 0,
859
+ initialMaxDepth: effectiveMaxDepth,
860
+ rootOnProgress: options.onProgress,
861
+ rootOnError: options.onError,
862
+ stopOnError: options.stopOnError,
863
+ cascadeTypes: options.cascadeTypes,
864
+ };
865
+ const currentDepth = cascadeState.initialMaxDepth - effectiveMaxDepth;
866
+ const cascadeData = { ...data };
867
+ for (const [fieldName, field] of entityDef.fields) {
868
+ if (cascadeData[fieldName] !== undefined)
869
+ continue;
870
+ if (field.operator === '->' && field.relatedType) {
871
+ if (cascadeState.cascadeTypes && !cascadeState.cascadeTypes.includes(field.relatedType)) {
872
+ continue;
873
+ }
874
+ const relatedEntity = parsedSchema.entities.get(field.relatedType);
875
+ if (!relatedEntity)
876
+ continue;
877
+ try {
878
+ cascadeState.rootOnProgress?.({
879
+ phase: 'generating',
880
+ depth: currentDepth + 1,
881
+ currentType: field.relatedType,
882
+ totalEntitiesCreated: cascadeState.totalEntitiesCreated,
883
+ });
884
+ const childEntityData = {};
885
+ const parentInstructions = entityDef.schema?.$instructions;
886
+ const childInstructions = relatedEntity.schema?.$instructions;
887
+ const contextParts = [];
888
+ if (parentInstructions)
889
+ contextParts.push(parentInstructions);
890
+ if (childInstructions)
891
+ contextParts.push(childInstructions);
892
+ for (const [key, value] of Object.entries(cascadeData)) {
893
+ if (!key.startsWith('$') && !key.startsWith('_') && typeof value === 'string' && value) {
894
+ contextParts.push(`${key}: ${value}`);
895
+ }
896
+ }
897
+ const fullContext = contextParts.join(' | ');
898
+ for (const [childFieldName, childField] of relatedEntity.fields) {
899
+ if (!childField.isRelation && childField.type === 'string') {
900
+ childEntityData[childFieldName] = generateContextAwareValue(childFieldName, field.relatedType, fullContext, undefined, cascadeData);
901
+ }
902
+ }
903
+ const childOptions = {
904
+ ...options,
905
+ maxDepth: effectiveMaxDepth - 1,
906
+ _cascadeState: cascadeState,
907
+ };
908
+ const relatedOps = entityOperations[field.relatedType];
909
+ const createFn = relatedOps?.create;
910
+ const childEntity = createFn ? await createFn(childEntityData, childOptions) : undefined;
911
+ if (childEntity?.$id) {
912
+ cascadeState.totalEntitiesCreated++;
913
+ if (field.isArray) {
914
+ cascadeData[fieldName] = [childEntity.$id];
915
+ }
916
+ else {
917
+ cascadeData[fieldName] = childEntity.$id;
918
+ }
919
+ }
920
+ }
921
+ catch (error) {
922
+ cascadeState.rootOnError?.(error);
923
+ if (cascadeState.stopOnError) {
924
+ throw error;
925
+ }
926
+ }
927
+ }
928
+ }
929
+ let result;
930
+ if (id) {
931
+ result = await originalCreate.call(wrappedOps, id, cascadeData);
932
+ }
933
+ else {
934
+ result = await originalCreate.call(wrappedOps, cascadeData);
935
+ }
936
+ cascadeState.totalEntitiesCreated++;
937
+ if (currentDepth === 0) {
938
+ cascadeState.rootOnProgress?.({
939
+ phase: 'complete',
940
+ depth: currentDepth,
941
+ totalEntitiesCreated: cascadeState.totalEntitiesCreated,
942
+ });
943
+ }
944
+ return result;
945
+ }
946
+ }
947
+ return originalCreate.call(wrappedOps, ...args);
948
+ };
949
+ // Make the entity operations callable as a tagged template literal
950
+ entityOperations[entityName] = makeCallableEntityOps(wrappedOps, entityName);
951
+ }
952
+ }
953
+ // Noun definitions cache
954
+ const nounDefinitions = new Map();
955
+ for (const [entityName] of parsedSchema.entities) {
956
+ const noun = inferNoun(entityName);
957
+ nounDefinitions.set(entityName, noun);
958
+ }
959
+ // Verb definitions cache
960
+ const verbDefinitions = new Map(Object.entries(Verbs).map(([k, v]) => [k, v]));
961
+ function onInternal(eventType, handler) {
962
+ if (!eventHandlersForOps.has(eventType)) {
963
+ eventHandlersForOps.set(eventType, new Set());
964
+ }
965
+ eventHandlersForOps.get(eventType).add(handler);
966
+ return () => {
967
+ eventHandlersForOps.get(eventType)?.delete(handler);
968
+ };
969
+ }
970
+ const db = {
971
+ $schema: parsedSchema,
972
+ async get(url) {
973
+ const provider = await resolveProvider();
974
+ const parsed = parseUrl(url);
975
+ return provider.get(parsed.type, parsed.id);
976
+ },
977
+ async search(query, options) {
978
+ const provider = await resolveProvider();
979
+ const results = [];
980
+ for (const [typeName] of parsedSchema.entities) {
981
+ const typeResults = await provider.search(typeName, query, options);
982
+ results.push(...typeResults);
983
+ }
984
+ return results;
985
+ },
986
+ async semanticSearch(query, options) {
987
+ const provider = await resolveProvider();
988
+ const results = [];
989
+ if (hasSemanticSearch(provider)) {
990
+ for (const [typeName] of parsedSchema.entities) {
991
+ const typeResults = await provider.semanticSearch(typeName, query, options);
992
+ results.push(...typeResults);
993
+ }
994
+ }
995
+ results.sort((a, b) => b.$score - a.$score);
996
+ const limit = options?.limit ?? results.length;
997
+ return results.slice(0, limit);
998
+ },
999
+ async count(type, where) {
1000
+ const provider = await resolveProvider();
1001
+ const results = await provider.list(type, { where });
1002
+ return results.length;
1003
+ },
1004
+ async forEach(options, callback) {
1005
+ const provider = await resolveProvider();
1006
+ const results = await provider.list(options.type, { where: options.where });
1007
+ const concurrency = options.concurrency ?? 1;
1008
+ if (concurrency === 1) {
1009
+ for (const entity of results) {
1010
+ await callback(entity);
1011
+ }
1012
+ }
1013
+ else {
1014
+ const { Semaphore } = await import('../memory-provider.js');
1015
+ const semaphore = new Semaphore(concurrency);
1016
+ await semaphore.map(results, callback);
1017
+ }
1018
+ },
1019
+ async set(type, id, data) {
1020
+ const provider = await resolveProvider();
1021
+ const existing = await provider.get(type, id);
1022
+ if (existing) {
1023
+ return provider.update(type, id, data);
1024
+ }
1025
+ return provider.create(type, id, data);
1026
+ },
1027
+ async generate(options) {
1028
+ const provider = await resolveProvider();
1029
+ if (options.mode === 'background') {
1030
+ const { createMemoryProvider } = await import('../memory-provider.js');
1031
+ const memProvider = provider;
1032
+ if ('createAction' in memProvider) {
1033
+ return memProvider.createAction({
1034
+ type: 'generate',
1035
+ data: options,
1036
+ total: options.count ?? 1,
1037
+ });
1038
+ }
1039
+ }
1040
+ return provider.create(options.type, undefined, options.data ?? {});
1041
+ },
1042
+ ask: createNLQueryFn(parsedSchema),
1043
+ on: onInternal,
1044
+ ...entityOperations,
1045
+ };
1046
+ // Create Events API
1047
+ const events = {
1048
+ on(pattern, handler) {
1049
+ let unsubscribe = () => { };
1050
+ resolveProvider().then((provider) => {
1051
+ if (hasEventsAPI(provider)) {
1052
+ unsubscribe = provider.on(pattern, handler);
1053
+ }
1054
+ });
1055
+ return () => unsubscribe();
1056
+ },
1057
+ async emit(optionsOrType, data) {
1058
+ const provider = await resolveProvider();
1059
+ if (hasEventsAPI(provider)) {
1060
+ // The provider.emit has overloads: (options: CreateEventOptions) or (type: string, data: unknown)
1061
+ if (typeof optionsOrType === 'string') {
1062
+ return provider.emit(optionsOrType, data);
1063
+ }
1064
+ return provider.emit(optionsOrType);
1065
+ }
1066
+ const now = new Date();
1067
+ if (typeof optionsOrType === 'string') {
1068
+ return {
1069
+ id: crypto.randomUUID(),
1070
+ actor: 'system',
1071
+ event: optionsOrType,
1072
+ objectData: data,
1073
+ timestamp: now,
1074
+ };
1075
+ }
1076
+ return {
1077
+ id: crypto.randomUUID(),
1078
+ actor: optionsOrType.actor,
1079
+ actorData: optionsOrType.actorData,
1080
+ event: optionsOrType.event,
1081
+ object: optionsOrType.object,
1082
+ objectData: optionsOrType.objectData,
1083
+ result: optionsOrType.result,
1084
+ resultData: optionsOrType.resultData,
1085
+ meta: optionsOrType.meta,
1086
+ timestamp: now,
1087
+ };
1088
+ },
1089
+ async list(options) {
1090
+ const provider = await resolveProvider();
1091
+ if (hasEventsAPI(provider)) {
1092
+ return provider.listEvents(options);
1093
+ }
1094
+ return [];
1095
+ },
1096
+ async replay(options) {
1097
+ const provider = await resolveProvider();
1098
+ if (hasEventsAPI(provider)) {
1099
+ await provider.replayEvents(options);
1100
+ }
1101
+ },
1102
+ };
1103
+ // Create Actions API (public version with full DBAction types)
1104
+ const actions = {
1105
+ async create(options) {
1106
+ const provider = await resolveProvider();
1107
+ if (hasActionsAPI(provider)) {
1108
+ return provider.createAction(options);
1109
+ }
1110
+ throw new Error('Provider does not support actions');
1111
+ },
1112
+ async get(id) {
1113
+ const provider = await resolveProvider();
1114
+ if (hasActionsAPI(provider)) {
1115
+ return provider.getAction(id);
1116
+ }
1117
+ return null;
1118
+ },
1119
+ async update(id, updates) {
1120
+ const provider = await resolveProvider();
1121
+ if (hasActionsAPI(provider)) {
1122
+ return provider.updateAction(id, updates);
1123
+ }
1124
+ throw new Error('Provider does not support actions');
1125
+ },
1126
+ async list(options) {
1127
+ const provider = await resolveProvider();
1128
+ if (hasActionsAPI(provider)) {
1129
+ return provider.listActions(options);
1130
+ }
1131
+ return [];
1132
+ },
1133
+ async retry(id) {
1134
+ const provider = await resolveProvider();
1135
+ if (hasActionsAPI(provider)) {
1136
+ return provider.retryAction(id);
1137
+ }
1138
+ throw new Error('Provider does not support actions');
1139
+ },
1140
+ async cancel(id) {
1141
+ const provider = await resolveProvider();
1142
+ if (hasActionsAPI(provider)) {
1143
+ await provider.cancelAction(id);
1144
+ }
1145
+ },
1146
+ conjugate,
1147
+ };
1148
+ // Create Artifacts API
1149
+ const artifacts = {
1150
+ async get(url, type) {
1151
+ const provider = await resolveProvider();
1152
+ if (hasArtifactsAPI(provider)) {
1153
+ return provider.getArtifact(url, type);
1154
+ }
1155
+ return null;
1156
+ },
1157
+ async set(url, type, data) {
1158
+ const provider = await resolveProvider();
1159
+ if (hasArtifactsAPI(provider)) {
1160
+ await provider.setArtifact(url, type, data);
1161
+ }
1162
+ },
1163
+ async delete(url, type) {
1164
+ const provider = await resolveProvider();
1165
+ if (hasArtifactsAPI(provider)) {
1166
+ await provider.deleteArtifact(url, type);
1167
+ }
1168
+ },
1169
+ async list(url) {
1170
+ const provider = await resolveProvider();
1171
+ if (hasArtifactsAPI(provider)) {
1172
+ return provider.listArtifacts(url);
1173
+ }
1174
+ return [];
1175
+ },
1176
+ };
1177
+ // Create Nouns API
1178
+ const nouns = {
1179
+ async get(name) {
1180
+ return nounDefinitions.get(name) ?? null;
1181
+ },
1182
+ async list() {
1183
+ return Array.from(nounDefinitions.values());
1184
+ },
1185
+ async define(noun) {
1186
+ nounDefinitions.set(noun.singular, noun);
1187
+ },
1188
+ };
1189
+ // Create Verbs API
1190
+ const verbs = {
1191
+ get(action) {
1192
+ return verbDefinitions.get(action) ?? null;
1193
+ },
1194
+ list() {
1195
+ return Array.from(verbDefinitions.values());
1196
+ },
1197
+ define(verb) {
1198
+ verbDefinitions.set(verb.action, verb);
1199
+ },
1200
+ conjugate,
1201
+ };
1202
+ return Object.assign(db, {
1203
+ db,
1204
+ events,
1205
+ actions,
1206
+ artifacts,
1207
+ nouns,
1208
+ verbs,
1209
+ });
1210
+ }
1211
+ //# sourceMappingURL=index.js.map