ai-database 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +5 -0
- package/.turbo/turbo-test.log +102 -0
- package/README.md +381 -68
- package/TESTING.md +410 -0
- package/TEST_SUMMARY.md +250 -0
- package/TODO.md +128 -0
- package/dist/ai-promise-db.d.ts +370 -0
- package/dist/ai-promise-db.d.ts.map +1 -0
- package/dist/ai-promise-db.js +839 -0
- package/dist/ai-promise-db.js.map +1 -0
- package/dist/authorization.d.ts +531 -0
- package/dist/authorization.d.ts.map +1 -0
- package/dist/authorization.js +632 -0
- package/dist/authorization.js.map +1 -0
- package/dist/durable-clickhouse.d.ts +193 -0
- package/dist/durable-clickhouse.d.ts.map +1 -0
- package/dist/durable-clickhouse.js +422 -0
- package/dist/durable-clickhouse.js.map +1 -0
- package/dist/durable-promise.d.ts +182 -0
- package/dist/durable-promise.d.ts.map +1 -0
- package/dist/durable-promise.js +409 -0
- package/dist/durable-promise.js.map +1 -0
- package/dist/execution-queue.d.ts +239 -0
- package/dist/execution-queue.d.ts.map +1 -0
- package/dist/execution-queue.js +400 -0
- package/dist/execution-queue.js.map +1 -0
- package/dist/index.d.ts +50 -191
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -462
- package/dist/index.js.map +1 -0
- package/dist/linguistic.d.ts +115 -0
- package/dist/linguistic.d.ts.map +1 -0
- package/dist/linguistic.js +379 -0
- package/dist/linguistic.js.map +1 -0
- package/dist/memory-provider.d.ts +304 -0
- package/dist/memory-provider.d.ts.map +1 -0
- package/dist/memory-provider.js +785 -0
- package/dist/memory-provider.js.map +1 -0
- package/dist/schema.d.ts +899 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +1165 -0
- package/dist/schema.js.map +1 -0
- package/dist/tests.d.ts +107 -0
- package/dist/tests.d.ts.map +1 -0
- package/dist/tests.js +568 -0
- package/dist/tests.js.map +1 -0
- package/dist/types.d.ts +972 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +126 -0
- package/dist/types.js.map +1 -0
- package/package.json +37 -37
- package/src/ai-promise-db.ts +1243 -0
- package/src/authorization.ts +1102 -0
- package/src/durable-clickhouse.ts +596 -0
- package/src/durable-promise.ts +582 -0
- package/src/execution-queue.ts +608 -0
- package/src/index.test.ts +868 -0
- package/src/index.ts +337 -0
- package/src/linguistic.ts +404 -0
- package/src/memory-provider.test.ts +1036 -0
- package/src/memory-provider.ts +1119 -0
- package/src/schema.test.ts +1254 -0
- package/src/schema.ts +2296 -0
- package/src/tests.ts +725 -0
- package/src/types.ts +1177 -0
- package/test/README.md +153 -0
- package/test/edge-cases.test.ts +646 -0
- package/test/provider-resolution.test.ts +402 -0
- package/tsconfig.json +9 -0
- package/vitest.config.ts +19 -0
- package/dist/index.d.mts +0 -195
- package/dist/index.mjs +0 -430
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
|
|
2
|
+
> ai-database@0.0.1 test /Users/nathanclevenger/projects/mdx.org.ai/primitives/packages/ai-database
|
|
3
|
+
> vitest
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
DEV v2.1.9 /Users/nathanclevenger/projects/mdx.org.ai/primitives/packages/ai-database
|
|
7
|
+
|
|
8
|
+
✓ src/schema.test.ts (83 tests) 13ms
|
|
9
|
+
✓ src/memory-provider.test.ts (81 tests) 197ms
|
|
10
|
+
❯ src/index.test.ts (47 tests | 1 failed) 13ms
|
|
11
|
+
× DB integration tests > list and query operations > iterates with options 5ms
|
|
12
|
+
→ expected [] to deeply equal [ 'John' ]
|
|
13
|
+
✓ test/edge-cases.test.ts (43 tests) 9ms
|
|
14
|
+
✓ test/provider-resolution.test.ts (38 tests) 2ms
|
|
15
|
+
|
|
16
|
+
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
|
|
17
|
+
|
|
18
|
+
FAIL src/index.test.ts > DB integration tests > list and query operations > iterates with options
|
|
19
|
+
AssertionError: expected [] to deeply equal [ 'John' ]
|
|
20
|
+
|
|
21
|
+
- Expected
|
|
22
|
+
+ Received
|
|
23
|
+
|
|
24
|
+
- Array [
|
|
25
|
+
- "John",
|
|
26
|
+
- ]
|
|
27
|
+
+ Array []
|
|
28
|
+
|
|
29
|
+
❯ src/index.test.ts:251:21
|
|
30
|
+
249| })
|
|
31
|
+
250|
|
|
32
|
+
251| expect(names).toEqual(['John'])
|
|
33
|
+
| ^
|
|
34
|
+
252| })
|
|
35
|
+
253| })
|
|
36
|
+
|
|
37
|
+
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
|
|
38
|
+
|
|
39
|
+
Test Files 1 failed | 4 passed (5)
|
|
40
|
+
Tests 1 failed | 291 passed (292)
|
|
41
|
+
Start at 14:14:37
|
|
42
|
+
Duration 673ms (transform 184ms, setup 0ms, collect 232ms, tests 235ms, environment 0ms, prepare 43ms)
|
|
43
|
+
|
|
44
|
+
FAIL Tests failed. Watching for file changes...
|
|
45
|
+
press h to show help, press q to quit
|
|
46
|
+
c[3J RERUN src/ai-promise-db.ts
|
|
47
|
+
|
|
48
|
+
✓ src/index.test.ts (47 tests) 9ms
|
|
49
|
+
✓ src/schema.test.ts (83 tests) 7ms
|
|
50
|
+
✓ test/edge-cases.test.ts (43 tests) 9ms
|
|
51
|
+
✓ test/provider-resolution.test.ts (38 tests) 2ms
|
|
52
|
+
|
|
53
|
+
Test Files 4 passed (4)
|
|
54
|
+
Tests 211 passed (211)
|
|
55
|
+
Start at 14:35:16
|
|
56
|
+
Duration 79ms
|
|
57
|
+
|
|
58
|
+
PASS Waiting for file changes...
|
|
59
|
+
press h to show help, press q to quit
|
|
60
|
+
c[3J RERUN src/ai-promise-db.ts
|
|
61
|
+
|
|
62
|
+
✓ src/index.test.ts (47 tests) 10ms
|
|
63
|
+
✓ test/edge-cases.test.ts (43 tests) 9ms
|
|
64
|
+
✓ src/schema.test.ts (83 tests) 7ms
|
|
65
|
+
✓ test/provider-resolution.test.ts (38 tests) 2ms
|
|
66
|
+
|
|
67
|
+
Test Files 4 passed (4)
|
|
68
|
+
Tests 211 passed (211)
|
|
69
|
+
Start at 14:35:40
|
|
70
|
+
Duration 70ms
|
|
71
|
+
|
|
72
|
+
PASS Waiting for file changes...
|
|
73
|
+
press h to show help, press q to quit
|
|
74
|
+
c[3J RERUN src/ai-promise-db.ts
|
|
75
|
+
|
|
76
|
+
✓ src/index.test.ts (47 tests) 10ms
|
|
77
|
+
✓ test/edge-cases.test.ts (43 tests) 8ms
|
|
78
|
+
✓ src/schema.test.ts (83 tests) 6ms
|
|
79
|
+
✓ test/provider-resolution.test.ts (38 tests) 2ms
|
|
80
|
+
|
|
81
|
+
Test Files 4 passed (4)
|
|
82
|
+
Tests 211 passed (211)
|
|
83
|
+
Start at 14:35:53
|
|
84
|
+
Duration 65ms
|
|
85
|
+
|
|
86
|
+
PASS Waiting for file changes...
|
|
87
|
+
press h to show help, press q to quit
|
|
88
|
+
c[3J RERUN src/ai-promise-db.ts
|
|
89
|
+
|
|
90
|
+
✓ src/index.test.ts (47 tests) 15ms
|
|
91
|
+
✓ test/edge-cases.test.ts (43 tests) 11ms
|
|
92
|
+
✓ src/schema.test.ts (83 tests) 9ms
|
|
93
|
+
✓ test/provider-resolution.test.ts (38 tests) 2ms
|
|
94
|
+
|
|
95
|
+
Test Files 4 passed (4)
|
|
96
|
+
Tests 211 passed (211)
|
|
97
|
+
Start at 01:20:37
|
|
98
|
+
Duration 115ms
|
|
99
|
+
|
|
100
|
+
PASS Waiting for file changes...
|
|
101
|
+
press h to show help, press q to quit
|
|
102
|
+
ELIFECYCLE Test failed. See above for more details.
|
package/README.md
CHANGED
|
@@ -1,114 +1,427 @@
|
|
|
1
1
|
# ai-database
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Your data, flowing like conversation.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```typescript
|
|
6
|
+
import { DB } from 'ai-database'
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
const { db } = DB({
|
|
9
|
+
Lead: { name: 'string', company: 'Company.leads' },
|
|
10
|
+
Company: { name: 'string' }
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
// Chain without await
|
|
14
|
+
const leads = db.Lead.list()
|
|
15
|
+
const qualified = await leads.filter(l => l.score > 80)
|
|
16
|
+
|
|
17
|
+
// Batch relationship loading
|
|
18
|
+
const enriched = await leads.map(lead => ({
|
|
19
|
+
name: lead.name,
|
|
20
|
+
company: lead.company, // Batch loaded!
|
|
21
|
+
}))
|
|
13
22
|
```
|
|
14
23
|
|
|
15
|
-
##
|
|
24
|
+
## Promise Pipelining
|
|
16
25
|
|
|
17
|
-
|
|
26
|
+
Chain database operations without `await`:
|
|
18
27
|
|
|
19
28
|
```typescript
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
const leads = db.Lead.list()
|
|
30
|
+
const topLeads = leads.filter(l => l.score > 80)
|
|
31
|
+
const names = topLeads.map(l => l.name)
|
|
23
32
|
|
|
24
|
-
//
|
|
25
|
-
const
|
|
26
|
-
|
|
33
|
+
// Only await when you need the result
|
|
34
|
+
const result = await names
|
|
35
|
+
```
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
## Batch Relationship Loading
|
|
38
|
+
|
|
39
|
+
Eliminate N+1 queries automatically:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// Old way - N+1 queries
|
|
43
|
+
const leads = await db.Lead.list()
|
|
44
|
+
for (const lead of leads) {
|
|
45
|
+
const company = await db.Company.get(lead.companyId) // N queries!
|
|
46
|
+
}
|
|
33
47
|
|
|
34
|
-
|
|
48
|
+
// New way - batch loaded
|
|
49
|
+
const enriched = await db.Lead.list().map(lead => ({
|
|
50
|
+
lead,
|
|
51
|
+
company: lead.company, // All companies loaded in ONE query
|
|
52
|
+
}))
|
|
35
53
|
```
|
|
36
54
|
|
|
37
|
-
|
|
55
|
+
## Natural Language Queries
|
|
56
|
+
|
|
57
|
+
Ask your database questions:
|
|
38
58
|
|
|
39
59
|
```typescript
|
|
40
|
-
|
|
60
|
+
const results = await db.Lead`who closed deals this month?`
|
|
61
|
+
const pending = await db.Order`what's stuck in processing?`
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Real-World Examples
|
|
67
|
+
|
|
68
|
+
### Sales Pipeline
|
|
41
69
|
|
|
42
|
-
|
|
43
|
-
const db = DB({
|
|
44
|
-
|
|
45
|
-
|
|
70
|
+
```typescript
|
|
71
|
+
const { db } = DB({
|
|
72
|
+
Lead: {
|
|
73
|
+
name: 'string',
|
|
74
|
+
email: 'string',
|
|
75
|
+
score: 'number',
|
|
76
|
+
company: 'Company.leads',
|
|
77
|
+
},
|
|
78
|
+
Company: {
|
|
79
|
+
name: 'string',
|
|
80
|
+
industry: 'string',
|
|
81
|
+
}
|
|
46
82
|
})
|
|
47
83
|
|
|
48
|
-
//
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
84
|
+
// Find high-value leads with their companies
|
|
85
|
+
const qualified = await db.Lead.list()
|
|
86
|
+
.filter(lead => lead.score > 80)
|
|
87
|
+
.map(lead => ({
|
|
88
|
+
lead,
|
|
89
|
+
company: lead.company,
|
|
90
|
+
}))
|
|
91
|
+
|
|
92
|
+
// Ask questions naturally
|
|
93
|
+
const results = await db.Lead`who hasn't responded in 2 weeks?`
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Customer Success
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const { db } = DB({
|
|
100
|
+
Customer: {
|
|
101
|
+
name: 'string',
|
|
102
|
+
healthScore: 'number',
|
|
103
|
+
mrr: 'number',
|
|
104
|
+
csm: 'User.customers',
|
|
105
|
+
},
|
|
106
|
+
User: { name: 'string' }
|
|
52
107
|
})
|
|
53
108
|
|
|
54
|
-
|
|
109
|
+
// At-risk customers with their CSMs
|
|
110
|
+
const atRisk = await db.Customer.list()
|
|
111
|
+
.filter(c => c.healthScore < 50)
|
|
112
|
+
.map(c => ({
|
|
113
|
+
customer: c,
|
|
114
|
+
csm: c.csm,
|
|
115
|
+
mrr: c.mrr,
|
|
116
|
+
}))
|
|
55
117
|
```
|
|
56
118
|
|
|
57
|
-
###
|
|
119
|
+
### Order Management
|
|
58
120
|
|
|
59
121
|
```typescript
|
|
60
|
-
|
|
122
|
+
const { db } = DB({
|
|
123
|
+
Order: {
|
|
124
|
+
status: 'string',
|
|
125
|
+
total: 'number',
|
|
126
|
+
customer: 'Customer.orders',
|
|
127
|
+
items: ['OrderItem.order'],
|
|
128
|
+
},
|
|
129
|
+
OrderItem: { product: 'string', quantity: 'number' },
|
|
130
|
+
Customer: { name: 'string' }
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
// Pending orders with all details
|
|
134
|
+
const pending = await db.Order
|
|
135
|
+
.find({ status: 'pending' })
|
|
136
|
+
.map(order => ({
|
|
137
|
+
order,
|
|
138
|
+
customer: order.customer,
|
|
139
|
+
items: order.items,
|
|
140
|
+
}))
|
|
141
|
+
```
|
|
61
142
|
|
|
62
|
-
|
|
63
|
-
const result = await generateEmbedding('Text to embed')
|
|
143
|
+
---
|
|
64
144
|
|
|
65
|
-
|
|
66
|
-
console.log('Embedding:', result.embedding)
|
|
67
|
-
console.log('Model used:', result.model)
|
|
145
|
+
## Schema Definition
|
|
68
146
|
|
|
69
|
-
|
|
70
|
-
const embedding1 = result.embedding[0]
|
|
71
|
-
const embedding2 = await generateEmbedding('Similar text').then((r) => r.embedding?.[0])
|
|
147
|
+
Define once, get typed operations everywhere:
|
|
72
148
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
149
|
+
```typescript
|
|
150
|
+
const { db, events, actions, nouns, verbs } = DB({
|
|
151
|
+
Post: {
|
|
152
|
+
title: 'string',
|
|
153
|
+
content: 'markdown',
|
|
154
|
+
author: 'Author.posts', // Creates both directions
|
|
155
|
+
},
|
|
156
|
+
Author: {
|
|
157
|
+
name: 'string',
|
|
158
|
+
// posts: Post[] auto-created from backref
|
|
76
159
|
}
|
|
77
|
-
}
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Field Types
|
|
164
|
+
|
|
165
|
+
| Type | Description |
|
|
166
|
+
|------|-------------|
|
|
167
|
+
| `string` | Text |
|
|
168
|
+
| `number` | Numeric |
|
|
169
|
+
| `boolean` | True/false |
|
|
170
|
+
| `date` | Date only |
|
|
171
|
+
| `datetime` | Date and time |
|
|
172
|
+
| `markdown` | Rich text |
|
|
173
|
+
| `json` | Structured data |
|
|
174
|
+
| `url` | URL string |
|
|
175
|
+
|
|
176
|
+
### Relationships
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// One-to-many: Post has one Author, Author has many Posts
|
|
180
|
+
Post: { author: 'Author.posts' }
|
|
181
|
+
|
|
182
|
+
// Many-to-many: Post has many Tags, Tag has many Posts
|
|
183
|
+
Post: { tags: ['Tag.posts'] }
|
|
78
184
|
```
|
|
79
185
|
|
|
80
|
-
|
|
186
|
+
---
|
|
81
187
|
|
|
82
|
-
|
|
188
|
+
## CRUD Operations
|
|
83
189
|
|
|
84
|
-
|
|
85
|
-
- `findOne(id)`: Find a single document by ID
|
|
86
|
-
- `create(data)`: Create a new document
|
|
87
|
-
- `update(id, data)`: Update an existing document
|
|
88
|
-
- `delete(id)`: Delete a document
|
|
89
|
-
- `search(query, options?)`: Search for documents
|
|
190
|
+
All operations return `DBPromise` for chaining:
|
|
90
191
|
|
|
91
|
-
|
|
192
|
+
```typescript
|
|
193
|
+
// Read
|
|
194
|
+
const lead = await db.Lead.get('lead-123')
|
|
195
|
+
const leads = await db.Lead.list()
|
|
196
|
+
const first = await db.Lead.first()
|
|
197
|
+
const found = await db.Lead.find({ status: 'active' })
|
|
198
|
+
|
|
199
|
+
// Search
|
|
200
|
+
const results = await db.Lead.search('enterprise SaaS')
|
|
201
|
+
|
|
202
|
+
// Write (returns regular Promise)
|
|
203
|
+
const lead = await db.Lead.create({ name: 'Acme Corp' })
|
|
204
|
+
await db.Lead.update(lead.$id, { score: 90 })
|
|
205
|
+
await db.Lead.delete(lead.$id)
|
|
206
|
+
```
|
|
92
207
|
|
|
93
|
-
|
|
208
|
+
### Chainable Methods
|
|
94
209
|
|
|
95
210
|
```typescript
|
|
96
|
-
|
|
211
|
+
db.Lead.list()
|
|
212
|
+
.filter(l => l.score > 50)
|
|
213
|
+
.sort((a, b) => b.score - a.score)
|
|
214
|
+
.limit(10)
|
|
215
|
+
.map(l => ({ name: l.name, score: l.score }))
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Events
|
|
97
221
|
|
|
98
|
-
|
|
99
|
-
|
|
222
|
+
React to changes in real-time:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
events.on('Lead.created', event => {
|
|
226
|
+
notifySlack(`New lead: ${event.data.name}`)
|
|
227
|
+
})
|
|
100
228
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
apiUrl: 'https://your-payload-api.com/api',
|
|
229
|
+
events.on('*.updated', event => {
|
|
230
|
+
logChange(event)
|
|
104
231
|
})
|
|
105
232
|
```
|
|
106
233
|
|
|
107
|
-
##
|
|
234
|
+
## forEach - Large-Scale Processing
|
|
108
235
|
|
|
109
|
-
|
|
110
|
-
|
|
236
|
+
Process thousands of items with concurrency, progress tracking, and error handling:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// Simple iteration
|
|
240
|
+
await db.Lead.forEach(lead => {
|
|
241
|
+
console.log(lead.name)
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
// With AI and concurrency
|
|
245
|
+
const result = await db.Lead.forEach(async lead => {
|
|
246
|
+
const analysis = await ai`analyze ${lead}`
|
|
247
|
+
await db.Lead.update(lead.$id, { analysis })
|
|
248
|
+
}, {
|
|
249
|
+
concurrency: 10,
|
|
250
|
+
onProgress: p => console.log(`${p.completed}/${p.total} (${p.rate.toFixed(1)}/s)`),
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
// With error handling and retries
|
|
254
|
+
await db.Order.forEach(async order => {
|
|
255
|
+
await sendInvoice(order)
|
|
256
|
+
}, {
|
|
257
|
+
concurrency: 5,
|
|
258
|
+
maxRetries: 3,
|
|
259
|
+
retryDelay: attempt => 1000 * Math.pow(2, attempt), // Exponential backoff
|
|
260
|
+
onError: (err, order) => err.code === 'RATE_LIMIT' ? 'retry' : 'continue',
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
console.log(`Completed: ${result.completed}, Failed: ${result.failed}`)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### forEach Options
|
|
267
|
+
|
|
268
|
+
| Option | Type | Description |
|
|
269
|
+
|--------|------|-------------|
|
|
270
|
+
| `concurrency` | `number` | Max parallel operations (default: 1) |
|
|
271
|
+
| `maxRetries` | `number` | Retries per item (default: 0) |
|
|
272
|
+
| `retryDelay` | `number \| fn` | Delay between retries |
|
|
273
|
+
| `onProgress` | `fn` | Progress callback |
|
|
274
|
+
| `onError` | `'continue' \| 'retry' \| 'skip' \| 'stop' \| fn` | Error handling |
|
|
275
|
+
| `timeout` | `number` | Timeout per item in ms |
|
|
276
|
+
| `persist` | `boolean \| string` | Enable durability (string = custom action name) |
|
|
277
|
+
| `resume` | `string` | Resume from action ID |
|
|
278
|
+
|
|
279
|
+
### Durable forEach
|
|
280
|
+
|
|
281
|
+
Persist progress to survive crashes:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// Enable persistence - auto-names action as "Lead.forEach"
|
|
285
|
+
const result = await db.Lead.forEach(processLead, {
|
|
286
|
+
concurrency: 10,
|
|
287
|
+
persist: true,
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
console.log(`Action ID: ${result.actionId}`)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Custom action name:
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
await db.Lead.forEach(processLead, {
|
|
297
|
+
persist: 'analyze-leads', // Custom action name
|
|
298
|
+
})
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Resume after a crash:
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
await db.Lead.forEach(processLead, {
|
|
305
|
+
resume: 'action-123', // Skips already-processed items
|
|
306
|
+
})
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Actions
|
|
312
|
+
|
|
313
|
+
Track long-running operations:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
const action = await actions.create({
|
|
317
|
+
type: 'import-leads',
|
|
318
|
+
data: { file: 'leads.csv' },
|
|
319
|
+
total: 1000,
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
await actions.update(action.id, { progress: 500 })
|
|
323
|
+
await actions.update(action.id, { status: 'completed' })
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## Installation
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
pnpm add ai-database
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## Configuration
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
DATABASE_URL=./content # filesystem (default)
|
|
338
|
+
DATABASE_URL=sqlite://./data # SQLite
|
|
339
|
+
DATABASE_URL=:memory: # in-memory
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Documentation
|
|
343
|
+
|
|
344
|
+
- [Full Documentation](https://primitives.org.ai/database)
|
|
345
|
+
- [CRUD Operations](https://primitives.org.ai/database/create)
|
|
346
|
+
- [Schema Types](https://primitives.org.ai/database/schema)
|
|
347
|
+
- [Events](https://primitives.org.ai/database/events)
|
|
348
|
+
|
|
349
|
+
## Document Database Interface
|
|
350
|
+
|
|
351
|
+
In addition to the schema-first graph model, `ai-database` also exports environment-agnostic types for document-based storage (MDX files with frontmatter). These types are used by `@mdxdb/*` adapters and work in any JavaScript runtime (Node.js, Bun, Deno, Workers, Browser).
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
import type {
|
|
355
|
+
DocumentDatabase,
|
|
356
|
+
DocListOptions,
|
|
357
|
+
DocSearchOptions,
|
|
358
|
+
Document,
|
|
359
|
+
} from 'ai-database'
|
|
360
|
+
|
|
361
|
+
// The DocumentDatabase interface
|
|
362
|
+
interface DocumentDatabase<TData> {
|
|
363
|
+
list(options?: DocListOptions): Promise<DocListResult<TData>>
|
|
364
|
+
search(options: DocSearchOptions): Promise<DocSearchResult<TData>>
|
|
365
|
+
get(id: string, options?: DocGetOptions): Promise<Document<TData> | null>
|
|
366
|
+
set(id: string, doc: Document<TData>, options?: DocSetOptions): Promise<DocSetResult>
|
|
367
|
+
delete(id: string, options?: DocDeleteOptions): Promise<DocDeleteResult>
|
|
368
|
+
close?(): Promise<void>
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Document Types
|
|
373
|
+
|
|
374
|
+
| Type | Description |
|
|
375
|
+
|------|-------------|
|
|
376
|
+
| `Document<TData>` | MDX document with id, type, context, data, and content |
|
|
377
|
+
| `DocumentDatabase<TData>` | Interface for document storage adapters |
|
|
378
|
+
| `DocListOptions` | Options for listing documents (limit, offset, sortBy, type, prefix) |
|
|
379
|
+
| `DocListResult<TData>` | List result with documents, total, hasMore |
|
|
380
|
+
| `DocSearchOptions` | Search options (query, fields, semantic) |
|
|
381
|
+
| `DocSearchResult<TData>` | Search result with scores |
|
|
382
|
+
| `DocGetOptions` | Get options (includeAst, includeCode) |
|
|
383
|
+
| `DocSetOptions` | Set options (createOnly, updateOnly, version) |
|
|
384
|
+
| `DocSetResult` | Set result (id, version, created) |
|
|
385
|
+
| `DocDeleteOptions` | Delete options (soft, version) |
|
|
386
|
+
| `DocDeleteResult` | Delete result (id, deleted) |
|
|
387
|
+
|
|
388
|
+
### View Types
|
|
389
|
+
|
|
390
|
+
For bi-directional relationship rendering:
|
|
391
|
+
|
|
392
|
+
| Type | Description |
|
|
393
|
+
|------|-------------|
|
|
394
|
+
| `ViewManager` | Interface for managing views |
|
|
395
|
+
| `ViewDocument` | View template definition |
|
|
396
|
+
| `ViewContext` | Context for rendering a view |
|
|
397
|
+
| `ViewRenderResult` | Rendered markdown and entities |
|
|
398
|
+
| `ViewSyncResult` | Mutations from extracting edited markdown |
|
|
399
|
+
| `DocumentDatabaseWithViews` | Database with view support |
|
|
400
|
+
|
|
401
|
+
### Usage with @mdxdb adapters
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
// Filesystem adapter
|
|
405
|
+
import { createFsDatabase } from '@mdxdb/fs'
|
|
406
|
+
const db = createFsDatabase({ root: './content' })
|
|
407
|
+
|
|
408
|
+
// API adapter
|
|
409
|
+
import { createApiDatabase } from '@mdxdb/api'
|
|
410
|
+
const db = createApiDatabase({ baseUrl: 'https://api.example.com' })
|
|
411
|
+
|
|
412
|
+
// SQLite adapter
|
|
413
|
+
import { createSqliteDatabase } from '@mdxdb/sqlite'
|
|
414
|
+
const db = createSqliteDatabase({ path: './data.db' })
|
|
415
|
+
|
|
416
|
+
// Same DocumentDatabase interface regardless of backend
|
|
417
|
+
const doc = await db.get('posts/hello-world')
|
|
418
|
+
await db.set('posts/new', { data: { title: 'New Post' }, content: '# Hello' })
|
|
419
|
+
```
|
|
111
420
|
|
|
112
|
-
##
|
|
421
|
+
## Related
|
|
113
422
|
|
|
114
|
-
|
|
423
|
+
- [ai-functions](https://github.com/ai-primitives/ai-primitives/tree/main/packages/ai-functions) - AI-powered functions
|
|
424
|
+
- [ai-workflows](https://github.com/ai-primitives/ai-primitives/tree/main/packages/ai-workflows) - Event-driven workflows
|
|
425
|
+
- [@mdxdb/fs](https://github.com/ai-primitives/mdx.org.ai/tree/main/packages/@mdxdb/fs) - Filesystem adapter
|
|
426
|
+
- [@mdxdb/sqlite](https://github.com/ai-primitives/mdx.org.ai/tree/main/packages/@mdxdb/sqlite) - SQLite adapter
|
|
427
|
+
- [@mdxdb/api](https://github.com/ai-primitives/mdx.org.ai/tree/main/packages/@mdxdb/api) - HTTP API client
|