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
package/src/index.test.ts DELETED
@@ -1,868 +0,0 @@
1
- /**
2
- * Integration tests for ai-database
3
- *
4
- * Tests the full DB API with in-memory provider.
5
- */
6
-
7
- import { describe, it, expect, beforeEach, afterEach } from 'vitest'
8
- import { DB, setProvider, createMemoryProvider } from './index.js'
9
- import type { InferEntity } from './index.js'
10
-
11
- describe('DB integration tests', () => {
12
- beforeEach(() => {
13
- // Use in-memory provider for testing
14
- setProvider(createMemoryProvider())
15
- })
16
-
17
- afterEach(() => {
18
- // Clean up
19
- setProvider(createMemoryProvider())
20
- })
21
-
22
- describe('basic CRUD operations', () => {
23
- const schema = {
24
- User: {
25
- name: 'string',
26
- email: 'string',
27
- age: 'number?',
28
- },
29
- } as const
30
-
31
- it('creates an entity without explicit ID', async () => {
32
- const { db } = DB(schema)
33
-
34
- const user = await db.User.create({
35
- name: 'John Doe',
36
- email: 'john@example.com',
37
- })
38
-
39
- expect(user.$id).toBeDefined()
40
- expect(user.$type).toBe('User')
41
- expect(user.name).toBe('John Doe')
42
- expect(user.email).toBe('john@example.com')
43
- })
44
-
45
- it('creates an entity with explicit ID', async () => {
46
- const { db } = DB(schema)
47
-
48
- const user = await db.User.create('john', {
49
- name: 'John Doe',
50
- email: 'john@example.com',
51
- })
52
-
53
- expect(user.$id).toBe('john')
54
- expect(user.name).toBe('John Doe')
55
- })
56
-
57
- it('retrieves an entity by ID', async () => {
58
- const { db } = DB(schema)
59
-
60
- await db.User.create('john', {
61
- name: 'John Doe',
62
- email: 'john@example.com',
63
- })
64
-
65
- const user = await db.User.get('john')
66
-
67
- expect(user).not.toBeNull()
68
- expect(user?.$id).toBe('john')
69
- expect(user?.name).toBe('John Doe')
70
- })
71
-
72
- it('returns null for non-existent entity', async () => {
73
- const { db } = DB(schema)
74
- const user = await db.User.get('nonexistent')
75
- expect(user).toBeNull()
76
- })
77
-
78
- it('updates an entity', async () => {
79
- const { db } = DB(schema)
80
-
81
- await db.User.create('john', {
82
- name: 'John',
83
- email: 'john@example.com',
84
- })
85
-
86
- const updated = await db.User.update('john', {
87
- name: 'John Doe',
88
- age: 30,
89
- })
90
-
91
- expect(updated.name).toBe('John Doe')
92
- expect(updated.email).toBe('john@example.com')
93
- expect(updated.age).toBe(30)
94
- })
95
-
96
- it('upserts - creates if not exists', async () => {
97
- const { db } = DB(schema)
98
-
99
- const user = await db.User.upsert('john', {
100
- name: 'John Doe',
101
- email: 'john@example.com',
102
- })
103
-
104
- expect(user.$id).toBe('john')
105
- expect(user.name).toBe('John Doe')
106
- })
107
-
108
- it('upserts - updates if exists', async () => {
109
- const { db } = DB(schema)
110
-
111
- await db.User.create('john', {
112
- name: 'John',
113
- email: 'john@example.com',
114
- })
115
-
116
- const updated = await db.User.upsert('john', {
117
- name: 'John Doe',
118
- email: 'john.doe@example.com',
119
- })
120
-
121
- expect(updated.name).toBe('John Doe')
122
- expect(updated.email).toBe('john.doe@example.com')
123
- })
124
-
125
- it('deletes an entity', async () => {
126
- const { db } = DB(schema)
127
-
128
- await db.User.create('john', {
129
- name: 'John',
130
- email: 'john@example.com',
131
- })
132
-
133
- const deleted = await db.User.delete('john')
134
- expect(deleted).toBe(true)
135
-
136
- const retrieved = await db.User.get('john')
137
- expect(retrieved).toBeNull()
138
- })
139
-
140
- it('returns false when deleting non-existent entity', async () => {
141
- const { db } = DB(schema)
142
- const deleted = await db.User.delete('nonexistent')
143
- expect(deleted).toBe(false)
144
- })
145
- })
146
-
147
- describe('list and query operations', () => {
148
- const schema = {
149
- User: {
150
- name: 'string',
151
- email: 'string',
152
- age: 'number',
153
- role: 'string',
154
- },
155
- } as const
156
-
157
- it('lists all entities', async () => {
158
- const { db } = DB(schema)
159
-
160
- await db.User.create('john', { name: 'John', email: 'john@example.com', age: 30, role: 'user' })
161
- await db.User.create('jane', { name: 'Jane', email: 'jane@example.com', age: 25, role: 'admin' })
162
-
163
- const users = await db.User.list()
164
-
165
- expect(users).toHaveLength(2)
166
- expect(users.map((u) => u.$id)).toContain('john')
167
- expect(users.map((u) => u.$id)).toContain('jane')
168
- })
169
-
170
- it('lists with where filter', async () => {
171
- const { db } = DB(schema)
172
-
173
- await db.User.create('john', { name: 'John', email: 'john@example.com', age: 30, role: 'admin' })
174
- await db.User.create('jane', { name: 'Jane', email: 'jane@example.com', age: 25, role: 'user' })
175
-
176
- const admins = await db.User.list({ where: { role: 'admin' } })
177
-
178
- expect(admins).toHaveLength(1)
179
- expect(admins[0]?.name).toBe('John')
180
- })
181
-
182
- it('lists with ordering', async () => {
183
- const { db } = DB(schema)
184
-
185
- await db.User.create('john', { name: 'John', email: 'john@example.com', age: 30, role: 'user' })
186
- await db.User.create('jane', { name: 'Jane', email: 'jane@example.com', age: 25, role: 'user' })
187
- await db.User.create('bob', { name: 'Bob', email: 'bob@example.com', age: 35, role: 'user' })
188
-
189
- const users = await db.User.list({ orderBy: 'age', order: 'asc' })
190
-
191
- expect(users[0]?.name).toBe('Jane')
192
- expect(users[1]?.name).toBe('John')
193
- expect(users[2]?.name).toBe('Bob')
194
- })
195
-
196
- it('lists with pagination', async () => {
197
- const { db } = DB(schema)
198
-
199
- await db.User.create('user1', { name: 'User 1', email: '1@example.com', age: 20, role: 'user' })
200
- await db.User.create('user2', { name: 'User 2', email: '2@example.com', age: 21, role: 'user' })
201
- await db.User.create('user3', { name: 'User 3', email: '3@example.com', age: 22, role: 'user' })
202
-
203
- const page1 = await db.User.list({ limit: 2, offset: 0 })
204
- const page2 = await db.User.list({ limit: 2, offset: 2 })
205
-
206
- expect(page1).toHaveLength(2)
207
- expect(page2).toHaveLength(1)
208
- })
209
-
210
- it('finds entities with criteria', async () => {
211
- const { db } = DB(schema)
212
-
213
- await db.User.create('john', { name: 'John', email: 'john@example.com', age: 30, role: 'admin' })
214
- await db.User.create('jane', { name: 'Jane', email: 'jane@example.com', age: 25, role: 'admin' })
215
- await db.User.create('bob', { name: 'Bob', email: 'bob@example.com', age: 35, role: 'user' })
216
-
217
- const admins = await db.User.find({ role: 'admin' })
218
-
219
- expect(admins).toHaveLength(2)
220
- expect(admins.map((u) => u.name)).toContain('John')
221
- expect(admins.map((u) => u.name)).toContain('Jane')
222
- })
223
-
224
- it('iterates over entities with forEach', async () => {
225
- const { db } = DB(schema)
226
-
227
- await db.User.create('john', { name: 'John', email: 'john@example.com', age: 30, role: 'user' })
228
- await db.User.create('jane', { name: 'Jane', email: 'jane@example.com', age: 25, role: 'user' })
229
-
230
- const names: string[] = []
231
- await db.User.forEach((user) => {
232
- names.push(user.name)
233
- })
234
-
235
- expect(names).toHaveLength(2)
236
- expect(names).toContain('John')
237
- expect(names).toContain('Jane')
238
- })
239
-
240
- it('iterates with options', async () => {
241
- const { db } = DB(schema)
242
-
243
- await db.User.create('john', { name: 'John', email: 'john@example.com', age: 30, role: 'admin' })
244
- await db.User.create('jane', { name: 'Jane', email: 'jane@example.com', age: 25, role: 'user' })
245
-
246
- const names: string[] = []
247
- await db.User.forEach({ where: { role: 'admin' } }, (user) => {
248
- names.push(user.name)
249
- })
250
-
251
- expect(names).toEqual(['John'])
252
- })
253
- })
254
-
255
- describe('search operations', () => {
256
- const schema = {
257
- Post: {
258
- title: 'string',
259
- content: 'markdown',
260
- category: 'string',
261
- },
262
- } as const
263
-
264
- it('searches entities', async () => {
265
- const { db } = DB(schema)
266
-
267
- await db.Post.create('post1', {
268
- title: 'Introduction to TypeScript',
269
- content: 'Learn TypeScript basics',
270
- category: 'tutorial',
271
- })
272
- await db.Post.create('post2', {
273
- title: 'Advanced JavaScript',
274
- content: 'Deep dive into JavaScript',
275
- category: 'tutorial',
276
- })
277
-
278
- const results = await db.Post.search('TypeScript')
279
-
280
- expect(results.length).toBeGreaterThan(0)
281
- expect(results[0]?.title).toContain('TypeScript')
282
- })
283
-
284
- it('searches with options', async () => {
285
- const { db } = DB(schema)
286
-
287
- await db.Post.create('post1', {
288
- title: 'TypeScript Tutorial',
289
- content: 'Content about JavaScript',
290
- category: 'tutorial',
291
- })
292
- await db.Post.create('post2', {
293
- title: 'JavaScript Guide',
294
- content: 'Content about TypeScript',
295
- category: 'guide',
296
- })
297
-
298
- const results = await db.Post.search('TypeScript', {
299
- fields: ['title'],
300
- })
301
-
302
- expect(results).toHaveLength(1)
303
- expect(results[0]?.title).toBe('TypeScript Tutorial')
304
- })
305
-
306
- it('searches globally across all types', async () => {
307
- const schema = {
308
- Post: { title: 'string' },
309
- User: { name: 'string' },
310
- } as const
311
-
312
- const { db } = DB(schema)
313
-
314
- await db.Post.create('post1', { title: 'TypeScript Guide' })
315
- await db.User.create('user1', { name: 'TypeScript Expert' })
316
-
317
- const results = await db.search('TypeScript')
318
-
319
- expect(results.length).toBe(2)
320
- })
321
- })
322
-
323
- describe('relationships', () => {
324
- const schema = {
325
- Post: {
326
- title: 'string',
327
- content: 'markdown',
328
- author: 'Author.posts',
329
- tags: ['Tag.posts'],
330
- },
331
- Author: {
332
- name: 'string',
333
- email: 'string',
334
- },
335
- Tag: {
336
- name: 'string',
337
- },
338
- } as const
339
-
340
- it('creates entities with relations', async () => {
341
- const { db } = DB(schema)
342
-
343
- const author = await db.Author.create('john', {
344
- name: 'John Doe',
345
- email: 'john@example.com',
346
- })
347
-
348
- const post = await db.Post.create('post1', {
349
- title: 'Hello World',
350
- content: 'My first post',
351
- author: author.$id,
352
- tags: [],
353
- })
354
-
355
- // Verify the post was created with correct basic fields
356
- expect(post.$id).toBe('post1')
357
- expect(post.title).toBe('Hello World')
358
- expect(post.content).toBe('My first post')
359
- })
360
-
361
- it('queries related entities through provider', async () => {
362
- const { db } = DB(schema)
363
- const provider = createMemoryProvider()
364
- setProvider(provider)
365
-
366
- await db.Author.create('john', {
367
- name: 'John Doe',
368
- email: 'john@example.com',
369
- })
370
-
371
- await db.Post.create('post1', {
372
- title: 'Post 1',
373
- content: 'Content',
374
- author: 'john',
375
- tags: [],
376
- })
377
-
378
- await db.Post.create('post2', {
379
- title: 'Post 2',
380
- content: 'Content',
381
- author: 'john',
382
- tags: [],
383
- })
384
-
385
- // Create relationships
386
- await provider.relate('Author', 'john', 'posts', 'Post', 'post1')
387
- await provider.relate('Author', 'john', 'posts', 'Post', 'post2')
388
-
389
- const posts = await provider.related('Author', 'john', 'posts')
390
- expect(posts).toHaveLength(2)
391
- })
392
-
393
- it('handles many-to-many relationships', async () => {
394
- const { db } = DB(schema)
395
- const provider = createMemoryProvider()
396
- setProvider(provider)
397
-
398
- await db.Post.create('post1', {
399
- title: 'Post 1',
400
- content: 'Content',
401
- author: 'john',
402
- tags: [],
403
- })
404
-
405
- await db.Tag.create('ts', { name: 'TypeScript' })
406
- await db.Tag.create('js', { name: 'JavaScript' })
407
-
408
- await provider.relate('Post', 'post1', 'tags', 'Tag', 'ts')
409
- await provider.relate('Post', 'post1', 'tags', 'Tag', 'js')
410
-
411
- const tags = await provider.related('Post', 'post1', 'tags')
412
- expect(tags).toHaveLength(2)
413
-
414
- // Reverse relation
415
- await provider.relate('Tag', 'ts', 'posts', 'Post', 'post1')
416
- const posts = await provider.related('Tag', 'ts', 'posts')
417
- expect(posts).toHaveLength(1)
418
- })
419
- })
420
-
421
- describe('global methods', () => {
422
- const schema = {
423
- User: { name: 'string' },
424
- Post: { title: 'string' },
425
- } as const
426
-
427
- it('gets entity by URL', async () => {
428
- const { db } = DB(schema)
429
-
430
- await db.User.create('john', { name: 'John' })
431
-
432
- const user = await db.get('https://example.com/User/john')
433
-
434
- expect(user).toBeDefined()
435
- })
436
-
437
- it('gets entity by type/id path', async () => {
438
- const { db } = DB(schema)
439
-
440
- await db.User.create('john', { name: 'John' })
441
-
442
- const user = await db.get('User/john')
443
-
444
- expect(user).toBeDefined()
445
- })
446
-
447
- it('searches across all entity types', async () => {
448
- const { db } = DB(schema)
449
-
450
- await db.User.create('john', { name: 'John TypeScript' })
451
- await db.Post.create('post1', { title: 'TypeScript Guide' })
452
-
453
- const results = await db.search('TypeScript')
454
-
455
- expect(results.length).toBe(2)
456
- })
457
- })
458
-
459
- describe('type safety', () => {
460
- it('provides typed entity operations', () => {
461
- const schema = {
462
- User: {
463
- name: 'string',
464
- age: 'number',
465
- },
466
- } as const
467
-
468
- const { db } = DB(schema)
469
-
470
- // TypeScript should enforce these types at compile time
471
- expect(db.User).toBeDefined()
472
- expect(typeof db.User.get).toBe('function')
473
- expect(typeof db.User.list).toBe('function')
474
- expect(typeof db.User.create).toBe('function')
475
- })
476
-
477
- it('infers entity types correctly', () => {
478
- const schema = {
479
- Post: {
480
- title: 'string',
481
- views: 'number',
482
- author: 'Author.posts',
483
- },
484
- Author: {
485
- name: 'string',
486
- },
487
- } as const
488
-
489
- type Post = InferEntity<typeof schema, 'Post'>
490
- type Author = InferEntity<typeof schema, 'Author'>
491
-
492
- // Type assertions to verify inference
493
- const post: Post = {
494
- $id: '1',
495
- $type: 'Post',
496
- title: 'Hello',
497
- views: 100,
498
- author: {} as Author,
499
- }
500
-
501
- expect(post.$id).toBe('1')
502
- })
503
- })
504
-
505
- describe('complex scenarios', () => {
506
- const schema = {
507
- User: {
508
- name: 'string',
509
- email: 'string',
510
- profile: 'Profile.user?',
511
- },
512
- Profile: {
513
- bio: 'string',
514
- avatar: 'url?',
515
- },
516
- Post: {
517
- title: 'string',
518
- content: 'markdown',
519
- published: 'boolean',
520
- author: 'User.posts',
521
- tags: ['Tag.posts'],
522
- },
523
- Tag: {
524
- name: 'string',
525
- slug: 'string',
526
- },
527
- } as const
528
-
529
- it('handles complex multi-entity operations', async () => {
530
- const { db } = DB(schema)
531
-
532
- // Create user
533
- const user = await db.User.create('john', {
534
- name: 'John Doe',
535
- email: 'john@example.com',
536
- })
537
-
538
- // Create profile
539
- const profile = await db.Profile.create('john-profile', {
540
- bio: 'Software developer',
541
- })
542
-
543
- // Create tags
544
- const tsTag = await db.Tag.create('typescript', {
545
- name: 'TypeScript',
546
- slug: 'typescript',
547
- })
548
-
549
- // Create post
550
- const post = await db.Post.create('post1', {
551
- title: 'Getting Started',
552
- content: '# Introduction',
553
- published: true,
554
- author: user.$id,
555
- tags: [],
556
- })
557
-
558
- expect(user.$id).toBe('john')
559
- expect(profile.$id).toBe('john-profile')
560
- expect(post.$id).toBe('post1')
561
- expect(tsTag.$id).toBe('typescript')
562
- })
563
-
564
- it('handles self-referential relations', async () => {
565
- const schema = {
566
- User: {
567
- name: 'string',
568
- manager: 'User.reports?',
569
- },
570
- } as const
571
-
572
- const { db } = DB(schema)
573
- const provider = createMemoryProvider()
574
- setProvider(provider)
575
-
576
- await db.User.create('alice', {
577
- name: 'Alice',
578
- })
579
-
580
- await db.User.create('bob', {
581
- name: 'Bob',
582
- manager: 'alice',
583
- })
584
-
585
- // Set up relation
586
- await provider.relate('User', 'alice', 'reports', 'User', 'bob')
587
-
588
- const reports = await provider.related('User', 'alice', 'reports')
589
- expect(reports).toHaveLength(1)
590
- expect(reports[0]?.name).toBe('Bob')
591
- })
592
- })
593
-
594
- describe('events API', () => {
595
- const schema = {
596
- User: { name: 'string' },
597
- } as const
598
-
599
- it('returns events API from DB', () => {
600
- const { db, events } = DB(schema)
601
-
602
- expect(events).toBeDefined()
603
- expect(typeof events.on).toBe('function')
604
- expect(typeof events.emit).toBe('function')
605
- expect(typeof events.list).toBe('function')
606
- expect(typeof events.replay).toBe('function')
607
- })
608
- })
609
-
610
- describe('actions API', () => {
611
- const schema = {
612
- User: { name: 'string' },
613
- } as const
614
-
615
- it('returns actions API from DB', () => {
616
- const { db, actions } = DB(schema)
617
-
618
- expect(actions).toBeDefined()
619
- expect(typeof actions.create).toBe('function')
620
- expect(typeof actions.get).toBe('function')
621
- expect(typeof actions.update).toBe('function')
622
- expect(typeof actions.list).toBe('function')
623
- expect(typeof actions.retry).toBe('function')
624
- expect(typeof actions.cancel).toBe('function')
625
- })
626
-
627
- it('creates and tracks actions', async () => {
628
- const { actions } = DB(schema)
629
-
630
- const action = await actions.create({
631
- type: 'generate',
632
- data: { count: 10 },
633
- total: 10,
634
- })
635
-
636
- expect(action.id).toBeDefined()
637
- expect(action.type).toBe('generate')
638
- expect(action.status).toBe('pending')
639
- expect(action.total).toBe(10)
640
-
641
- const retrieved = await actions.get(action.id)
642
- expect(retrieved?.id).toBe(action.id)
643
- })
644
-
645
- it('updates action progress', async () => {
646
- const { actions } = DB(schema)
647
-
648
- const action = await actions.create({
649
- type: 'generate',
650
- data: {},
651
- total: 10,
652
- })
653
-
654
- const updated = await actions.update(action.id, {
655
- status: 'active',
656
- progress: 5,
657
- })
658
-
659
- expect(updated.status).toBe('active')
660
- expect(updated.progress).toBe(5)
661
- })
662
- })
663
-
664
- describe('artifacts API', () => {
665
- const schema = {
666
- User: { name: 'string' },
667
- } as const
668
-
669
- it('returns artifacts API from DB', () => {
670
- const { db, artifacts } = DB(schema)
671
-
672
- expect(artifacts).toBeDefined()
673
- expect(typeof artifacts.get).toBe('function')
674
- expect(typeof artifacts.set).toBe('function')
675
- expect(typeof artifacts.delete).toBe('function')
676
- expect(typeof artifacts.list).toBe('function')
677
- })
678
- })
679
-
680
- describe('nouns API', () => {
681
- const schema = {
682
- BlogPost: { title: 'string' },
683
- Author: { name: 'string' },
684
- } as const
685
-
686
- it('returns nouns API from DB', () => {
687
- const { nouns } = DB(schema)
688
-
689
- expect(nouns).toBeDefined()
690
- expect(typeof nouns.get).toBe('function')
691
- expect(typeof nouns.list).toBe('function')
692
- expect(typeof nouns.define).toBe('function')
693
- })
694
-
695
- it('lists inferred nouns from schema', async () => {
696
- const { nouns } = DB(schema)
697
-
698
- const allNouns = await nouns.list()
699
- expect(allNouns.length).toBe(2)
700
- })
701
-
702
- it('gets noun definition by name', async () => {
703
- const { nouns } = DB(schema)
704
-
705
- const blogPost = await nouns.get('BlogPost')
706
- expect(blogPost).toBeDefined()
707
- expect(blogPost?.singular).toBe('blog post')
708
- expect(blogPost?.plural).toBe('blog posts')
709
- })
710
- })
711
-
712
- describe('verbs API', () => {
713
- const schema = {
714
- User: { name: 'string' },
715
- } as const
716
-
717
- it('returns verbs API from DB', () => {
718
- const { verbs } = DB(schema)
719
-
720
- expect(verbs).toBeDefined()
721
- expect(typeof verbs.get).toBe('function')
722
- expect(typeof verbs.list).toBe('function')
723
- expect(typeof verbs.define).toBe('function')
724
- expect(typeof verbs.conjugate).toBe('function')
725
- })
726
-
727
- it('gets standard verb definitions', () => {
728
- const { verbs } = DB(schema)
729
-
730
- const create = verbs.get('create')
731
- expect(create).toBeDefined()
732
- expect(create?.action).toBe('create')
733
- expect(create?.actor).toBe('creator')
734
- })
735
-
736
- it('conjugates custom verbs', () => {
737
- const { verbs } = DB(schema)
738
-
739
- const publish = verbs.conjugate('publish')
740
- expect(publish.action).toBe('publish')
741
- expect(publish.actor).toBe('publisher')
742
- expect(publish.activity).toBe('publishing')
743
- })
744
- })
745
- })
746
-
747
- describe('dual API syntax', () => {
748
- beforeEach(() => {
749
- setProvider(createMemoryProvider())
750
- })
751
-
752
- const schema = {
753
- User: { name: 'string', email: 'string' },
754
- Post: { title: 'string', author: 'User.posts' },
755
- } as const
756
-
757
- it('supports direct usage - db.Entity.method()', async () => {
758
- // Direct usage: const db = DB(schema)
759
- const db = DB(schema)
760
-
761
- // Entity operations work directly
762
- const user = await db.User.create('john', { name: 'John', email: 'john@example.com' })
763
- expect(user.name).toBe('John')
764
-
765
- // Get also works
766
- const retrieved = await db.User.get('john')
767
- expect(retrieved?.name).toBe('John')
768
- })
769
-
770
- it('supports direct access to events/actions on db object', () => {
771
- // Direct usage: const db = DB(schema)
772
- const db = DB(schema)
773
-
774
- // APIs are available directly on db
775
- expect(db.events).toBeDefined()
776
- expect(typeof db.events.on).toBe('function')
777
- expect(typeof db.events.emit).toBe('function')
778
-
779
- expect(db.actions).toBeDefined()
780
- expect(typeof db.actions.create).toBe('function')
781
-
782
- expect(db.artifacts).toBeDefined()
783
- expect(db.nouns).toBeDefined()
784
- expect(db.verbs).toBeDefined()
785
- })
786
-
787
- it('supports destructured usage - const { db, events } = DB()', async () => {
788
- // Destructured usage
789
- const { db, events, actions, artifacts, nouns, verbs } = DB(schema)
790
-
791
- // Entity operations work on db
792
- const user = await db.User.create('jane', { name: 'Jane', email: 'jane@example.com' })
793
- expect(user.name).toBe('Jane')
794
-
795
- // Separate API objects work
796
- expect(typeof events.on).toBe('function')
797
- expect(typeof actions.create).toBe('function')
798
- expect(typeof artifacts.get).toBe('function')
799
- expect(typeof nouns.get).toBe('function')
800
- expect(typeof verbs.get).toBe('function')
801
- })
802
-
803
- it('both syntaxes work with same schema', async () => {
804
- // Can use both syntaxes interchangeably
805
- const result = DB(schema)
806
-
807
- // Direct usage
808
- await result.User.create('user1', { name: 'User 1', email: 'u1@example.com' })
809
-
810
- // Destructured usage from same result
811
- const { db, events, actions } = result
812
- await db.User.create('user2', { name: 'User 2', email: 'u2@example.com' })
813
-
814
- // Both users exist
815
- const users = await result.User.list()
816
- expect(users).toHaveLength(2)
817
-
818
- // Can also use db.list()
819
- const users2 = await db.User.list()
820
- expect(users2).toHaveLength(2)
821
- })
822
-
823
- it('db self-reference allows clean destructuring', () => {
824
- const result = DB(schema)
825
-
826
- // result.db is same entity operations as result itself
827
- expect(result.db.User).toBeDefined()
828
- expect(result.db.$schema).toBe(result.$schema)
829
-
830
- // But result.db doesn't have events/actions (clean entity ops)
831
- // Actually it does since db is a self-reference, but semantically
832
- // when you destructure { db } you get clean entity ops
833
- })
834
- })
835
-
836
- describe('provider resolution', () => {
837
- it('uses memory provider when set explicitly', async () => {
838
- const provider = createMemoryProvider()
839
- setProvider(provider)
840
-
841
- const schema = {
842
- User: { name: 'string' },
843
- } as const
844
-
845
- const { db } = DB(schema)
846
-
847
- await db.User.create('test', { name: 'Test User' })
848
-
849
- const stats = provider.stats()
850
- expect(stats.entities).toBe(1)
851
- })
852
-
853
- it('isolates data between provider instances', async () => {
854
- const provider1 = createMemoryProvider()
855
- const provider2 = createMemoryProvider()
856
-
857
- setProvider(provider1)
858
- const schema = { User: { name: 'string' } } as const
859
- const { db: db1 } = DB(schema)
860
- await db1.User.create('john', { name: 'John' })
861
-
862
- setProvider(provider2)
863
- const { db: db2 } = DB(schema)
864
- const user = await db2.User.get('john')
865
-
866
- expect(user).toBeNull()
867
- })
868
- })