ai-database 2.0.2 → 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 +36 -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
@@ -1,582 +0,0 @@
1
- /**
2
- * Durable Promise - Promise-like wrapper around database Actions
3
- *
4
- * Time is an implementation detail. Whether an operation takes 10ms or 10 hours,
5
- * the same code works. The DurablePromise persists its state as an Action,
6
- * allowing crash recovery, observability, and time-agnostic execution.
7
- *
8
- * @packageDocumentation
9
- */
10
-
11
- import type { Action, MemoryProvider } from './memory-provider.js'
12
-
13
- // =============================================================================
14
- // Types
15
- // =============================================================================
16
-
17
- /**
18
- * Execution priority tiers
19
- *
20
- * - priority: Pay more for immediate execution (fastest, highest cost)
21
- * - standard: Normal price/latency tradeoff (default)
22
- * - flex: Discount for variable latency (cost savings)
23
- * - batch: Maximum discount, 24h SLA (50% savings)
24
- */
25
- export type ExecutionPriority = 'priority' | 'standard' | 'flex' | 'batch'
26
-
27
- /**
28
- * Options for creating a DurablePromise
29
- */
30
- export interface DurablePromiseOptions<T = unknown> {
31
- /** Method identifier (e.g., 'ai.generate', 'db.get', 'api.fetch') */
32
- method: string
33
-
34
- /** Arguments passed to the method */
35
- args?: unknown[]
36
-
37
- /** The executor function that performs the actual work */
38
- executor: () => Promise<T>
39
-
40
- /** Execution priority tier (defaults to context or 'standard') */
41
- priority?: ExecutionPriority
42
-
43
- /** Concurrency key for queue grouping */
44
- concurrencyKey?: string
45
-
46
- /** Defer execution until this time (for batch windows) */
47
- deferUntil?: Date
48
-
49
- /** Action IDs this promise depends on (must complete first) */
50
- dependsOn?: string[]
51
-
52
- /** Actor identifier (who initiated this operation) */
53
- actor?: string
54
-
55
- /** Additional metadata */
56
- meta?: Record<string, unknown>
57
-
58
- /** Provider instance (if not using context) */
59
- provider?: MemoryProvider
60
- }
61
-
62
- /**
63
- * Result of DurablePromise resolution
64
- */
65
- export interface DurablePromiseResult<T> {
66
- /** The resolved value */
67
- value: T
68
- /** The underlying Action record */
69
- action: Action
70
- /** Total execution time in ms */
71
- duration: number
72
- }
73
-
74
- /**
75
- * Internal state for promise resolution
76
- */
77
- interface PromiseState<T> {
78
- status: 'pending' | 'resolved' | 'rejected'
79
- value?: T
80
- error?: Error
81
- resolvers: Array<{
82
- resolve: (value: T | PromiseLike<T>) => void
83
- reject: (reason?: unknown) => void
84
- }>
85
- }
86
-
87
- // =============================================================================
88
- // Context Stack (for inheriting priority from workflow)
89
- // =============================================================================
90
-
91
- interface ExecutionContext {
92
- priority: ExecutionPriority
93
- provider?: MemoryProvider
94
- concurrencyKey?: string
95
- actor?: string
96
- batchWindow?: number
97
- onFlush?: () => Promise<void>
98
- }
99
-
100
- const contextStack: ExecutionContext[] = []
101
-
102
- /**
103
- * Get the current execution context
104
- */
105
- export function getCurrentContext(): ExecutionContext | undefined {
106
- return contextStack[contextStack.length - 1]
107
- }
108
-
109
- /**
110
- * Run code within an execution context
111
- */
112
- export async function withContext<T>(
113
- context: Partial<ExecutionContext>,
114
- fn: () => Promise<T>
115
- ): Promise<T> {
116
- const parent = getCurrentContext()
117
- const merged: ExecutionContext = {
118
- priority: context.priority ?? parent?.priority ?? 'standard',
119
- provider: context.provider ?? parent?.provider,
120
- concurrencyKey: context.concurrencyKey ?? parent?.concurrencyKey,
121
- actor: context.actor ?? parent?.actor,
122
- batchWindow: context.batchWindow ?? parent?.batchWindow,
123
- onFlush: context.onFlush ?? parent?.onFlush,
124
- }
125
-
126
- contextStack.push(merged)
127
- try {
128
- return await fn()
129
- } finally {
130
- contextStack.pop()
131
- }
132
- }
133
-
134
- /**
135
- * Set global default context
136
- */
137
- export function setDefaultContext(context: Partial<ExecutionContext>): void {
138
- if (contextStack.length === 0) {
139
- contextStack.push({
140
- priority: context.priority ?? 'standard',
141
- provider: context.provider,
142
- concurrencyKey: context.concurrencyKey,
143
- actor: context.actor,
144
- batchWindow: context.batchWindow,
145
- onFlush: context.onFlush,
146
- })
147
- } else {
148
- Object.assign(contextStack[0]!, context)
149
- }
150
- }
151
-
152
- // =============================================================================
153
- // DurablePromise Class
154
- // =============================================================================
155
-
156
- /** Symbol to identify DurablePromise instances */
157
- export const DURABLE_PROMISE_SYMBOL = Symbol.for('ai-database.DurablePromise')
158
-
159
- /**
160
- * A Promise-like class that persists its state as an Action
161
- *
162
- * @example
163
- * ```ts
164
- * const promise = new DurablePromise({
165
- * method: 'ai.generate',
166
- * args: [{ prompt: 'Hello' }],
167
- * executor: async () => await ai.generate({ prompt: 'Hello' }),
168
- * priority: 'batch',
169
- * })
170
- *
171
- * // Access the underlying action
172
- * console.log(promise.actionId)
173
- *
174
- * // Await like a normal promise
175
- * const result = await promise
176
- * ```
177
- */
178
- export class DurablePromise<T> implements PromiseLike<T> {
179
- readonly [DURABLE_PROMISE_SYMBOL] = true
180
-
181
- /** The Action ID backing this promise */
182
- readonly actionId: string
183
-
184
- /** The method being executed */
185
- readonly method: string
186
-
187
- /** The execution priority */
188
- readonly priority: ExecutionPriority
189
-
190
- /** Dependencies that must complete first */
191
- readonly dependsOn: string[]
192
-
193
- private readonly state: PromiseState<T> = {
194
- status: 'pending',
195
- resolvers: [],
196
- }
197
-
198
- private action: Action | null = null
199
- private provider: MemoryProvider | null = null
200
- private executor: (() => Promise<T>) | null = null
201
- private startTime: number = Date.now()
202
-
203
- constructor(options: DurablePromiseOptions<T>) {
204
- const context = getCurrentContext()
205
-
206
- this.method = options.method
207
- this.priority = options.priority ?? context?.priority ?? 'standard'
208
- this.dependsOn = options.dependsOn ?? []
209
- this.provider = options.provider ?? context?.provider ?? null
210
- this.executor = options.executor
211
- this.actionId = crypto.randomUUID()
212
-
213
- // Create the action immediately if we have a provider
214
- if (this.provider) {
215
- this.initializeAction(options)
216
- } else {
217
- // Defer action creation but start execution
218
- this.executeDirectly()
219
- }
220
- }
221
-
222
- private async initializeAction(options: DurablePromiseOptions<T>): Promise<void> {
223
- if (!this.provider) return
224
-
225
- try {
226
- // Create the action record
227
- this.action = await this.provider.createAction({
228
- actor: options.actor ?? getCurrentContext()?.actor ?? 'system',
229
- action: this.parseActionVerb(this.method),
230
- object: this.method,
231
- objectData: {
232
- method: this.method,
233
- args: options.args,
234
- priority: this.priority,
235
- concurrencyKey: options.concurrencyKey,
236
- deferUntil: options.deferUntil?.toISOString(),
237
- dependsOn: this.dependsOn,
238
- },
239
- meta: options.meta,
240
- })
241
-
242
- // Override the generated ID with our pre-generated one
243
- // (This allows us to return actionId synchronously)
244
- ;(this.action as { id: string }).id = this.actionId
245
-
246
- // Check if we should defer execution
247
- if (this.priority === 'batch') {
248
- // Register with the batch scheduler instead of executing immediately
249
- const scheduler = getBatchScheduler()
250
- if (scheduler) {
251
- scheduler.enqueue(this as DurablePromise<unknown>)
252
- return
253
- }
254
- }
255
-
256
- // Check dependencies
257
- if (this.dependsOn.length > 0) {
258
- await this.waitForDependencies()
259
- }
260
-
261
- // Execute
262
- await this.execute()
263
- } catch (error) {
264
- this.reject(error as Error)
265
- }
266
- }
267
-
268
- private parseActionVerb(method: string): string {
269
- // Extract verb from method like 'ai.generate' -> 'generate'
270
- const parts = method.split('.')
271
- return parts[parts.length - 1] || 'process'
272
- }
273
-
274
- private async waitForDependencies(): Promise<void> {
275
- if (!this.provider || this.dependsOn.length === 0) return
276
-
277
- // Poll for dependency completion
278
- const checkInterval = 100 // ms
279
- const maxWait = 24 * 60 * 60 * 1000 // 24 hours
280
-
281
- const startWait = Date.now()
282
-
283
- while (Date.now() - startWait < maxWait) {
284
- const pending = await this.provider.listActions({
285
- status: 'pending',
286
- })
287
-
288
- const active = await this.provider.listActions({
289
- status: 'active',
290
- })
291
-
292
- const blockedBy = [...pending, ...active]
293
- .filter((a) => this.dependsOn.includes(a.id))
294
- .map((a) => a.id)
295
-
296
- if (blockedBy.length === 0) {
297
- // All dependencies resolved
298
- return
299
- }
300
-
301
- // Wait before checking again
302
- await new Promise((resolve) => setTimeout(resolve, checkInterval))
303
- }
304
-
305
- throw new Error(`Timeout waiting for dependencies: ${this.dependsOn.join(', ')}`)
306
- }
307
-
308
- private async execute(): Promise<void> {
309
- if (!this.executor) {
310
- throw new Error('No executor provided')
311
- }
312
-
313
- try {
314
- // Mark as active
315
- if (this.provider && this.action) {
316
- await this.provider.updateAction(this.actionId, { status: 'active' })
317
- }
318
-
319
- // Execute the actual work
320
- const result = await this.executor()
321
-
322
- // Mark as completed
323
- if (this.provider && this.action) {
324
- await this.provider.updateAction(this.actionId, {
325
- status: 'completed',
326
- result: { value: result as unknown as Record<string, unknown> },
327
- })
328
- }
329
-
330
- this.resolve(result)
331
- } catch (error) {
332
- // Mark as failed
333
- if (this.provider && this.action) {
334
- await this.provider.updateAction(this.actionId, {
335
- status: 'failed',
336
- error: (error as Error).message,
337
- })
338
- }
339
-
340
- this.reject(error as Error)
341
- }
342
- }
343
-
344
- private async executeDirectly(): Promise<void> {
345
- if (!this.executor) {
346
- throw new Error('No executor provided')
347
- }
348
-
349
- try {
350
- const result = await this.executor()
351
- this.resolve(result)
352
- } catch (error) {
353
- this.reject(error as Error)
354
- }
355
- }
356
-
357
- private resolve(value: T): void {
358
- if (this.state.status !== 'pending') return
359
-
360
- this.state.status = 'resolved'
361
- this.state.value = value
362
-
363
- for (const { resolve } of this.state.resolvers) {
364
- resolve(value)
365
- }
366
- this.state.resolvers = []
367
- }
368
-
369
- private reject(error: Error): void {
370
- if (this.state.status !== 'pending') return
371
-
372
- this.state.status = 'rejected'
373
- this.state.error = error
374
-
375
- for (const { reject } of this.state.resolvers) {
376
- reject(error)
377
- }
378
- this.state.resolvers = []
379
- }
380
-
381
- /**
382
- * Implement PromiseLike interface
383
- */
384
- then<TResult1 = T, TResult2 = never>(
385
- onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
386
- onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null
387
- ): Promise<TResult1 | TResult2> {
388
- return new Promise<TResult1 | TResult2>((resolve, reject) => {
389
- const handleResolve = (value: T) => {
390
- if (onfulfilled) {
391
- try {
392
- resolve(onfulfilled(value))
393
- } catch (e) {
394
- reject(e)
395
- }
396
- } else {
397
- resolve(value as unknown as TResult1)
398
- }
399
- }
400
-
401
- const handleReject = (error: unknown) => {
402
- if (onrejected) {
403
- try {
404
- resolve(onrejected(error))
405
- } catch (e) {
406
- reject(e)
407
- }
408
- } else {
409
- reject(error)
410
- }
411
- }
412
-
413
- if (this.state.status === 'resolved') {
414
- handleResolve(this.state.value!)
415
- } else if (this.state.status === 'rejected') {
416
- handleReject(this.state.error)
417
- } else {
418
- this.state.resolvers.push({
419
- resolve: handleResolve as (value: T | PromiseLike<T>) => void,
420
- reject: handleReject,
421
- })
422
- }
423
- })
424
- }
425
-
426
- /**
427
- * Catch handler
428
- */
429
- catch<TResult = never>(
430
- onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null
431
- ): Promise<T | TResult> {
432
- return this.then(null, onrejected)
433
- }
434
-
435
- /**
436
- * Finally handler
437
- */
438
- finally(onfinally?: (() => void) | null): Promise<T> {
439
- return this.then(
440
- (value) => {
441
- onfinally?.()
442
- return value
443
- },
444
- (reason) => {
445
- onfinally?.()
446
- throw reason
447
- }
448
- )
449
- }
450
-
451
- /**
452
- * Get the current status
453
- */
454
- get status(): Action['status'] {
455
- if (this.action) return this.action.status
456
- if (this.state.status === 'resolved') return 'completed'
457
- if (this.state.status === 'rejected') return 'failed'
458
- return 'pending'
459
- }
460
-
461
- /**
462
- * Get the underlying Action (if available)
463
- */
464
- async getAction(): Promise<Action | null> {
465
- if (this.action) return this.action
466
- if (!this.provider) return null
467
- return this.provider.getAction(this.actionId)
468
- }
469
-
470
- /**
471
- * Get the result with full metadata
472
- */
473
- async getResult(): Promise<DurablePromiseResult<T>> {
474
- const value = await this
475
- const action = await this.getAction()
476
-
477
- return {
478
- value,
479
- action: action!,
480
- duration: Date.now() - this.startTime,
481
- }
482
- }
483
-
484
- /**
485
- * Cancel the promise if still pending
486
- */
487
- async cancel(): Promise<void> {
488
- if (this.state.status !== 'pending') {
489
- throw new Error('Cannot cancel a resolved or rejected promise')
490
- }
491
-
492
- if (this.provider) {
493
- await this.provider.cancelAction(this.actionId)
494
- }
495
-
496
- this.reject(new Error('Promise cancelled'))
497
- }
498
-
499
- /**
500
- * Retry a failed promise
501
- */
502
- async retry(): Promise<this> {
503
- if (this.state.status !== 'rejected') {
504
- throw new Error('Can only retry failed promises')
505
- }
506
-
507
- if (this.provider) {
508
- await this.provider.retryAction(this.actionId)
509
- }
510
-
511
- // Re-execute
512
- this.state.status = 'pending'
513
- await this.execute()
514
- return this
515
- }
516
- }
517
-
518
- /**
519
- * Check if a value is a DurablePromise
520
- */
521
- export function isDurablePromise(value: unknown): value is DurablePromise<unknown> {
522
- return (
523
- typeof value === 'object' &&
524
- value !== null &&
525
- DURABLE_PROMISE_SYMBOL in value &&
526
- (value as Record<symbol, unknown>)[DURABLE_PROMISE_SYMBOL] === true
527
- )
528
- }
529
-
530
- /**
531
- * Create a durable promise from an executor function
532
- */
533
- export function durable<T>(
534
- method: string,
535
- executor: () => Promise<T>,
536
- options?: Omit<DurablePromiseOptions<T>, 'method' | 'executor'>
537
- ): DurablePromise<T> {
538
- return new DurablePromise({
539
- method,
540
- executor,
541
- ...options,
542
- })
543
- }
544
-
545
- // =============================================================================
546
- // Batch Scheduler Singleton
547
- // =============================================================================
548
-
549
- let batchScheduler: BatchScheduler | null = null
550
-
551
- /**
552
- * Get the global batch scheduler
553
- */
554
- export function getBatchScheduler(): BatchScheduler | null {
555
- return batchScheduler
556
- }
557
-
558
- /**
559
- * Set the global batch scheduler
560
- */
561
- export function setBatchScheduler(scheduler: BatchScheduler | null): void {
562
- batchScheduler = scheduler
563
- }
564
-
565
- // =============================================================================
566
- // BatchScheduler Interface (implemented in execution-queue.ts)
567
- // =============================================================================
568
-
569
- /**
570
- * Interface for batch scheduling
571
- * Full implementation is in execution-queue.ts
572
- */
573
- export interface BatchScheduler {
574
- /** Add a promise to the batch queue */
575
- enqueue(promise: DurablePromise<unknown>): void
576
-
577
- /** Flush all pending batches */
578
- flush(): Promise<void>
579
-
580
- /** Get count of pending promises */
581
- readonly pending: number
582
- }