edinburgh 0.4.5 → 0.5.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/README.md +268 -374
- package/build/src/datapack.js +1 -1
- package/build/src/datapack.js.map +1 -1
- package/build/src/edinburgh.d.ts +5 -5
- package/build/src/edinburgh.js +8 -7
- package/build/src/edinburgh.js.map +1 -1
- package/build/src/indexes.d.ts +46 -116
- package/build/src/indexes.js +148 -180
- package/build/src/indexes.js.map +1 -1
- package/build/src/migrate.js +11 -31
- package/build/src/migrate.js.map +1 -1
- package/build/src/models.d.ts +74 -49
- package/build/src/models.js +112 -165
- package/build/src/models.js.map +1 -1
- package/build/src/types.d.ts +30 -21
- package/build/src/types.js +16 -30
- package/build/src/types.js.map +1 -1
- package/package.json +1 -3
- package/skill/BaseIndex_batchProcess.md +1 -1
- package/skill/BaseIndex_find.md +2 -2
- package/skill/BaseIndex_find_2.md +7 -0
- package/skill/BaseIndex_find_3.md +7 -0
- package/skill/BaseIndex_find_4.md +7 -0
- package/skill/Model.md +5 -7
- package/skill/Model_batchProcess.md +8 -0
- package/skill/Model_delete.md +1 -1
- package/skill/Model_migrate.md +2 -4
- package/skill/Model_preCommit.md +2 -4
- package/skill/Model_preventPersist.md +1 -1
- package/skill/Model_replaceInto.md +2 -2
- package/skill/NonPrimaryIndex.md +10 -0
- package/skill/SKILL.md +146 -144
- package/skill/SecondaryIndex.md +2 -2
- package/skill/UniqueIndex.md +2 -2
- package/skill/defineModel.md +22 -0
- package/skill/field.md +2 -2
- package/skill/link.md +11 -9
- package/skill/transact.md +2 -2
- package/src/datapack.ts +1 -1
- package/src/edinburgh.ts +8 -9
- package/src/indexes.ts +157 -276
- package/src/migrate.ts +9 -30
- package/src/models.ts +188 -174
- package/src/types.ts +31 -26
- package/skill/Model_findAll.md +0 -12
- package/skill/PrimaryIndex.md +0 -8
- package/skill/PrimaryIndex_get.md +0 -17
- package/skill/PrimaryIndex_getLazy.md +0 -13
- package/skill/UniqueIndex_get.md +0 -17
- package/skill/index.md +0 -32
- package/skill/primary.md +0 -26
- package/skill/registerModel.md +0 -26
- package/skill/unique.md +0 -32
package/skill/SKILL.md
CHANGED
|
@@ -26,39 +26,41 @@ import * as E from "edinburgh";
|
|
|
26
26
|
// Initialize the database (optional, defaults to ".edinburgh")
|
|
27
27
|
E.init("./my-database");
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
@E.registerModel
|
|
31
|
-
class User extends E.Model<User> {
|
|
32
|
-
// Define a primary key (optional, defaults to using the "id" field)
|
|
33
|
-
static pk = E.primary(User, "id");
|
|
34
|
-
// Define a unique index on the email field
|
|
35
|
-
static byEmail = E.unique(User, "email");
|
|
36
|
-
|
|
37
|
-
// Define fields with simple types -- they will be type-checked at compile time and validated at runtime.
|
|
29
|
+
const User = E.defineModel(class {
|
|
38
30
|
id = E.field(E.identifier);
|
|
39
31
|
name = E.field(E.string);
|
|
40
32
|
age = E.field(E.number);
|
|
41
|
-
email = E.field(E.opt(E.string));
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
email = E.field(E.opt(E.string));
|
|
34
|
+
// Optional link to another instance of this model (needs a function as `User` is not defined yet at this point)
|
|
35
|
+
supervisor = E.field(E.opt(E.link(() => User)));
|
|
36
|
+
// A field with a more elaborate type. In TypeScript: `User | User[] | "unknown" | "whatever"`, defaulting to "unknown".
|
|
37
|
+
something = E.field(
|
|
38
|
+
E.or(
|
|
39
|
+
E.link(() => User),
|
|
40
|
+
E.array(E.link(() => User)),
|
|
41
|
+
E.literal("unknown"),
|
|
42
|
+
E.literal("whatever")
|
|
43
|
+
),
|
|
44
|
+
{ default: "unknown" }
|
|
45
|
+
);
|
|
46
|
+
}, {
|
|
47
|
+
pk: "id",
|
|
48
|
+
unique: {
|
|
49
|
+
byEmail: "email",
|
|
50
|
+
},
|
|
51
|
+
tableName: "User",
|
|
52
|
+
});
|
|
49
53
|
|
|
50
|
-
// Use in transactions
|
|
51
54
|
await E.transact(() => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const john = new User({ // Unique 'id' is automatically generated if not provided
|
|
57
|
-
name: "John Doe",
|
|
55
|
+
// Unique 'id' values are auto-generated if not provided
|
|
56
|
+
const boss = new User({ name: "Big Boss", age: 50 });
|
|
57
|
+
new User({
|
|
58
|
+
name: "John Doe",
|
|
58
59
|
age: 41,
|
|
59
60
|
email: "john@example.com",
|
|
60
61
|
supervisor: boss, // Link to another model instance
|
|
61
62
|
});
|
|
63
|
+
// Newly instantiated models are automatically saved to the database on transaction commit
|
|
62
64
|
});
|
|
63
65
|
|
|
64
66
|
await E.transact(() => {
|
|
@@ -70,41 +72,31 @@ await E.transact(() => {
|
|
|
70
72
|
john.age++;
|
|
71
73
|
|
|
72
74
|
// The supervisor object is lazy loaded on first access
|
|
73
|
-
console.log(`${john.supervisor!.name} is ${john.name}'s supervisor`);
|
|
75
|
+
console.log(`${john.supervisor!.name} is ${john.name}'s supervisor`);
|
|
74
76
|
});
|
|
75
77
|
```
|
|
76
78
|
|
|
77
79
|
## Tutorial
|
|
78
80
|
|
|
79
|
-
### TypeScript Configuration
|
|
80
|
-
|
|
81
|
-
When using TypeScript to transpile to JavaScript, make sure to enable the following options in your `tsconfig.json`:
|
|
82
|
-
|
|
83
|
-
```json
|
|
84
|
-
{
|
|
85
|
-
"compilerOptions": {
|
|
86
|
-
"target": "es2022",
|
|
87
|
-
"experimentalDecorators": true
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
81
|
|
|
92
82
|
### Defining Models
|
|
93
83
|
|
|
94
|
-
Models are
|
|
84
|
+
Models are plain, usually anonymous, classes passed to `E.defineModel()`:
|
|
95
85
|
|
|
96
86
|
```typescript
|
|
97
87
|
import * as E from "edinburgh";
|
|
98
88
|
|
|
99
|
-
|
|
100
|
-
class User extends E.Model<User> {
|
|
101
|
-
static pk = E.primary(User, "id");
|
|
102
|
-
|
|
89
|
+
const User = E.defineModel(class {
|
|
103
90
|
id = E.field(E.identifier);
|
|
104
91
|
name = E.field(E.string);
|
|
105
92
|
email = E.field(E.string);
|
|
106
93
|
age = E.field(E.number);
|
|
107
|
-
}
|
|
94
|
+
}, {
|
|
95
|
+
pk: "id",
|
|
96
|
+
unique: {
|
|
97
|
+
byEmail: "email",
|
|
98
|
+
},
|
|
99
|
+
});
|
|
108
100
|
```
|
|
109
101
|
|
|
110
102
|
Instance fields are declared with `E.field(type, options?)`. Available types:
|
|
@@ -128,16 +120,13 @@ Instance fields are declared with `E.field(type, options?)`. Available types:
|
|
|
128
120
|
#### Defaults
|
|
129
121
|
|
|
130
122
|
```typescript
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
static pk = E.primary(Post, "id");
|
|
134
|
-
|
|
135
|
-
id = E.field(E.identifier); // auto-generated
|
|
123
|
+
const Post = E.defineModel(class {
|
|
124
|
+
id = E.field(E.identifier); // auto-generated
|
|
136
125
|
title = E.field(E.string);
|
|
137
126
|
status = E.field(E.or("draft", "published"), {default: "draft"});
|
|
138
|
-
tags = E.field(E.array(E.string), {default: () => []});
|
|
139
|
-
createdAt = E.field(E.dateTime);
|
|
140
|
-
}
|
|
127
|
+
tags = E.field(E.array(E.string), {default: () => []}); // use function for mutable defaults
|
|
128
|
+
createdAt = E.field(E.dateTime); // dateTime defaults to new Date()
|
|
129
|
+
}, { pk: "id" });
|
|
141
130
|
```
|
|
142
131
|
|
|
143
132
|
### Transactions
|
|
@@ -150,8 +139,8 @@ E.init("./my-database");
|
|
|
150
139
|
|
|
151
140
|
// Create
|
|
152
141
|
await E.transact(() => {
|
|
153
|
-
|
|
154
|
-
|
|
142
|
+
// User.id is auto-generated
|
|
143
|
+
new User({name: "Alice", email: "alice@example.com", age: 30});
|
|
155
144
|
});
|
|
156
145
|
|
|
157
146
|
// Read + Update
|
|
@@ -174,27 +163,26 @@ Transactions auto-retry on conflict (up to 6 times by default). Keep transaction
|
|
|
174
163
|
Edinburgh supports three index types:
|
|
175
164
|
|
|
176
165
|
```typescript
|
|
177
|
-
|
|
178
|
-
class Product extends E.Model<Product> {
|
|
179
|
-
static pk = E.primary(Product, "sku"); // primary: one per model, stores data
|
|
180
|
-
static byName = E.unique(Product, "name"); // unique: enforces uniqueness + fast lookup
|
|
181
|
-
static byCategory = E.index(Product, "category");// secondary: non-unique, for queries
|
|
182
|
-
|
|
166
|
+
const Product = E.defineModel(class {
|
|
183
167
|
sku = E.field(E.string);
|
|
184
168
|
name = E.field(E.string);
|
|
185
169
|
category = E.field(E.string);
|
|
186
170
|
price = E.field(E.number);
|
|
187
|
-
}
|
|
171
|
+
}, {
|
|
172
|
+
pk: "sku",
|
|
173
|
+
unique: { byName: "name" },
|
|
174
|
+
index: { byCategory: "category" },
|
|
175
|
+
});
|
|
188
176
|
```
|
|
189
177
|
|
|
190
|
-
If no `
|
|
178
|
+
If no `pk` is provided, Edinburgh auto-creates one on an `id` field, adding it as an `E.identifier` field if needed.
|
|
191
179
|
|
|
192
180
|
#### Lookups
|
|
193
181
|
|
|
194
182
|
```typescript
|
|
195
183
|
await E.transact(() => {
|
|
196
184
|
// Primary key lookup
|
|
197
|
-
const
|
|
185
|
+
const p1 = Product.get("SKU-001");
|
|
198
186
|
|
|
199
187
|
// Unique index lookup
|
|
200
188
|
const p2 = Product.byName.get("Widget");
|
|
@@ -215,18 +203,18 @@ await E.transact(() => {
|
|
|
215
203
|
}
|
|
216
204
|
|
|
217
205
|
// Range (inclusive)
|
|
218
|
-
for (const p of Product.
|
|
206
|
+
for (const p of Product.find({from: "A", to: "M"})) {
|
|
219
207
|
console.log(p.sku);
|
|
220
208
|
}
|
|
221
209
|
|
|
222
210
|
// Exclusive bounds
|
|
223
|
-
for (const p of Product.
|
|
211
|
+
for (const p of Product.find({after: "A", before: "M"})) { ... }
|
|
224
212
|
|
|
225
213
|
// Open-ended
|
|
226
|
-
for (const p of Product.
|
|
214
|
+
for (const p of Product.find({from: "M"})) { ... }
|
|
227
215
|
|
|
228
216
|
// Reverse
|
|
229
|
-
for (const p of Product.
|
|
217
|
+
for (const p of Product.find({reverse: true})) { ... }
|
|
230
218
|
|
|
231
219
|
// Count and fetch helpers
|
|
232
220
|
const count = Product.byCategory.find({is: "electronics"}).count();
|
|
@@ -234,25 +222,24 @@ await E.transact(() => {
|
|
|
234
222
|
});
|
|
235
223
|
```
|
|
236
224
|
|
|
237
|
-
#### Composite
|
|
225
|
+
#### Composite Primary Keys
|
|
238
226
|
|
|
239
227
|
```typescript
|
|
240
|
-
|
|
241
|
-
class Event extends E.Model<Event> {
|
|
242
|
-
static pk = E.primary(Event, ["year", "month", "id"]);
|
|
243
|
-
|
|
228
|
+
const Event = E.defineModel(class {
|
|
244
229
|
year = E.field(E.number);
|
|
245
230
|
month = E.field(E.number);
|
|
246
231
|
id = E.field(E.identifier);
|
|
247
232
|
title = E.field(E.string);
|
|
248
|
-
}
|
|
233
|
+
}, {
|
|
234
|
+
pk: ["year", "month", "id"] as const,
|
|
235
|
+
});
|
|
249
236
|
|
|
250
237
|
await E.transact(() => {
|
|
251
238
|
// Prefix matching — find all events in 2025
|
|
252
|
-
for (const e of Event.
|
|
239
|
+
for (const e of Event.find({is: [2025]})) { ... }
|
|
253
240
|
|
|
254
241
|
// Find events in March 2025
|
|
255
|
-
for (const e of Event.
|
|
242
|
+
for (const e of Event.find({is: [2025, 3]})) { ... }
|
|
256
243
|
});
|
|
257
244
|
```
|
|
258
245
|
|
|
@@ -261,10 +248,7 @@ await E.transact(() => {
|
|
|
261
248
|
You can freely add regular methods, getters, and other non-persistent properties to model classes. These work normally in JavaScript but are **not stored in the database** and **not synchronized** across transactions or processes.
|
|
262
249
|
|
|
263
250
|
```typescript
|
|
264
|
-
|
|
265
|
-
class User extends E.Model<User> {
|
|
266
|
-
static pk = E.primary(User, "id");
|
|
267
|
-
id = E.field(E.identifier);
|
|
251
|
+
const User = E.defineModel(class {
|
|
268
252
|
firstName = E.field(E.string);
|
|
269
253
|
lastName = E.field(E.string);
|
|
270
254
|
|
|
@@ -279,27 +263,30 @@ class User extends E.Model<User> {
|
|
|
279
263
|
greet(): string {
|
|
280
264
|
return `Hello, ${this.fullName}!`;
|
|
281
265
|
}
|
|
282
|
-
}
|
|
266
|
+
});
|
|
283
267
|
```
|
|
284
268
|
|
|
285
269
|
#### Computed Indexes
|
|
286
270
|
|
|
287
|
-
Instead of naming fields, you can pass a
|
|
271
|
+
Instead of naming fields, you can pass a function as an index specification. The function receives a model instance and returns an **array** of index key values. Each element creates a separate index entry, enabling multi-value indexes. Return `[]` to skip indexing for that instance (partial index).
|
|
288
272
|
|
|
289
273
|
```typescript
|
|
290
|
-
|
|
291
|
-
class Article extends E.Model<Article> {
|
|
292
|
-
static pk = E.primary(Article, "id");
|
|
293
|
-
static byFullName = E.unique(Article, (a: Article) => [`${a.firstName} ${a.lastName}`]);
|
|
294
|
-
static byWord = E.index(Article, (a: Article) => a.title.toLowerCase().split(" "));
|
|
295
|
-
static byDomain = E.index(Article, (a: Article) => a.email ? [a.email.split("@")[1]] : []);
|
|
296
|
-
|
|
274
|
+
const Article = E.defineModel(class {
|
|
297
275
|
id = E.field(E.identifier);
|
|
298
276
|
firstName = E.field(E.string);
|
|
299
277
|
lastName = E.field(E.string);
|
|
300
278
|
title = E.field(E.string);
|
|
301
279
|
email = E.field(E.opt(E.string));
|
|
302
|
-
}
|
|
280
|
+
}, {
|
|
281
|
+
pk: "id",
|
|
282
|
+
unique: {
|
|
283
|
+
byFullName: (a: any) => [`${a.firstName} ${a.lastName}`], // computed covering unique index
|
|
284
|
+
},
|
|
285
|
+
index: {
|
|
286
|
+
byDomain: (a: any) => a.email ? [a.email.split("@")[1]] : [], // computed partial index
|
|
287
|
+
byWord: (a: any) => a.title.toLowerCase().split(" "), // computed multi-index
|
|
288
|
+
},
|
|
289
|
+
});
|
|
303
290
|
|
|
304
291
|
await E.transact(() => {
|
|
305
292
|
new Article({ firstName: "Jane", lastName: "Doe", title: "Hello World", email: "jane@acme.com" });
|
|
@@ -315,34 +302,29 @@ await E.transact(() => {
|
|
|
315
302
|
});
|
|
316
303
|
```
|
|
317
304
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
### Relationships (Links)
|
|
305
|
+
### Relationships
|
|
321
306
|
|
|
322
|
-
Use `E.link(Model)` for foreign keys:
|
|
307
|
+
Use `E.link(Model)` for foreign keys. Use a thunk (a function that just returns a value) for forward references when needed:
|
|
323
308
|
|
|
324
309
|
```typescript
|
|
325
|
-
|
|
326
|
-
class Author extends E.Model<Author> {
|
|
327
|
-
static pk = E.primary(Author, "id");
|
|
310
|
+
const Author = E.defineModel(class {
|
|
328
311
|
id = E.field(E.identifier);
|
|
329
312
|
name = E.field(E.string);
|
|
330
|
-
}
|
|
313
|
+
}, { pk: "id" });
|
|
331
314
|
|
|
332
|
-
|
|
333
|
-
class Book extends E.Model<Book> {
|
|
334
|
-
static pk = E.primary(Book, "id");
|
|
315
|
+
const Book = E.defineModel(class {
|
|
335
316
|
id = E.field(E.identifier);
|
|
336
317
|
title = E.field(E.string);
|
|
337
318
|
author = E.field(E.link(Author));
|
|
338
|
-
}
|
|
319
|
+
}, { pk: "id" });
|
|
339
320
|
|
|
340
321
|
await E.transact(() => {
|
|
341
322
|
const author = new Author({name: "Tolkien"});
|
|
342
323
|
const book = new Book({title: "The Hobbit", author});
|
|
343
324
|
|
|
344
325
|
// Later: linked models are lazy-loaded on property access
|
|
345
|
-
const b = Book.
|
|
326
|
+
const b = Book.get(book.id)!;
|
|
327
|
+
console.log(b.author.id); // no need to load yet..
|
|
346
328
|
console.log(b.author.name); // loads Author automatically (~1µs)
|
|
347
329
|
});
|
|
348
330
|
```
|
|
@@ -351,7 +333,7 @@ await E.transact(() => {
|
|
|
351
333
|
|
|
352
334
|
```typescript
|
|
353
335
|
await E.transact(() => {
|
|
354
|
-
const user = User.
|
|
336
|
+
const user = User.get(someId);
|
|
355
337
|
if (user) user.delete();
|
|
356
338
|
});
|
|
357
339
|
```
|
|
@@ -369,10 +351,16 @@ await E.transact(() => {
|
|
|
369
351
|
user.preventPersist(); // exclude from commit
|
|
370
352
|
});
|
|
371
353
|
|
|
372
|
-
//
|
|
354
|
+
// find() iterates all instances (or use range options)
|
|
373
355
|
await E.transact(() => {
|
|
374
|
-
for (const user of User.
|
|
375
|
-
for (const user of User.
|
|
356
|
+
for (const user of User.find()) { ... }
|
|
357
|
+
for (const user of User.find({reverse: true})) { ... }
|
|
358
|
+
|
|
359
|
+
// {fetch: 'first'} returns a single instance or undefined
|
|
360
|
+
const first = User.find({fetch: 'first'});
|
|
361
|
+
|
|
362
|
+
// {fetch: 'single'} returns a single instance, or throws if there are none or more than one
|
|
363
|
+
const only = User.find({fetch: 'single'});
|
|
376
364
|
});
|
|
377
365
|
|
|
378
366
|
// replaceInto: upsert by primary key
|
|
@@ -386,7 +374,7 @@ await E.transact(() => {
|
|
|
386
374
|
For large datasets, `batchProcess` auto-commits in batches:
|
|
387
375
|
|
|
388
376
|
```typescript
|
|
389
|
-
await Product.
|
|
377
|
+
await Product.batchProcess({ limitRows: 1000 }, (product) => {
|
|
390
378
|
product.category = "archived";
|
|
391
379
|
});
|
|
392
380
|
// Commits every ~1 second or 4096 rows (configurable via limitSeconds, limitRows)
|
|
@@ -394,12 +382,10 @@ await Product.byCategory.batchProcess({is: "old"}, (product) => {
|
|
|
394
382
|
|
|
395
383
|
### Lazy Schema Migrations
|
|
396
384
|
|
|
397
|
-
When you change a model's schema, Edinburgh
|
|
385
|
+
When you change a model's schema, Edinburgh lazily migrates old records on access. You can provide a `static migrate(record)` function to transform old rows:
|
|
398
386
|
|
|
399
387
|
```typescript
|
|
400
|
-
|
|
401
|
-
class User extends E.Model<User> {
|
|
402
|
-
static pk = E.primary(User, "id");
|
|
388
|
+
const UserV2 = E.defineModel(class {
|
|
403
389
|
id = E.field(E.identifier);
|
|
404
390
|
name = E.field(E.string);
|
|
405
391
|
role = E.field(E.string); // newly added field
|
|
@@ -442,9 +428,7 @@ console.log(result.secondaries); // { User: 1500 }
|
|
|
442
428
|
Compute derived fields before data is written:
|
|
443
429
|
|
|
444
430
|
```typescript
|
|
445
|
-
|
|
446
|
-
class Article extends E.Model<Article> {
|
|
447
|
-
static pk = E.primary(Article, "id");
|
|
431
|
+
const Article = E.defineModel(class {
|
|
448
432
|
id = E.field(E.identifier);
|
|
449
433
|
title = E.field(E.string);
|
|
450
434
|
slug = E.field(E.string);
|
|
@@ -452,7 +436,7 @@ class Article extends E.Model<Article> {
|
|
|
452
436
|
preCommit() {
|
|
453
437
|
this.slug = this.title.toLowerCase().replace(/\s+/g, "-");
|
|
454
438
|
}
|
|
455
|
-
}
|
|
439
|
+
}, { pk: "id" });
|
|
456
440
|
```
|
|
457
441
|
|
|
458
442
|
### Change Tracking
|
|
@@ -532,7 +516,7 @@ The database table name (defaults to class name).
|
|
|
532
516
|
|
|
533
517
|
#### Model.override · static property
|
|
534
518
|
|
|
535
|
-
When true,
|
|
519
|
+
When true, defineModel replaces an existing model with the same tableName.
|
|
536
520
|
|
|
537
521
|
**Type:** `boolean`
|
|
538
522
|
|
|
@@ -548,9 +532,31 @@ Optional migration function called when deserializing rows written with an older
|
|
|
548
532
|
Receives a plain record with all fields (primary key fields + value fields) and should mutate it
|
|
549
533
|
in-place to match the current schema.
|
|
550
534
|
|
|
551
|
-
####
|
|
535
|
+
#### Model.get · static method
|
|
552
536
|
|
|
553
|
-
|
|
537
|
+
**Signature:** `(...args: any[]) => any`
|
|
538
|
+
|
|
539
|
+
**Parameters:**
|
|
540
|
+
|
|
541
|
+
- `args: any[]`
|
|
542
|
+
|
|
543
|
+
#### Model.getLazy · static method
|
|
544
|
+
|
|
545
|
+
**Signature:** `(...args: any[]) => any`
|
|
546
|
+
|
|
547
|
+
**Parameters:**
|
|
548
|
+
|
|
549
|
+
- `args: any[]`
|
|
550
|
+
|
|
551
|
+
#### Model.find · static method
|
|
552
|
+
|
|
553
|
+
**Signature:** `(opts?: any) => any`
|
|
554
|
+
|
|
555
|
+
**Parameters:**
|
|
556
|
+
|
|
557
|
+
- `opts?: any`
|
|
558
|
+
|
|
559
|
+
#### [Model.batchProcess](Model_batchProcess.md) · static method
|
|
554
560
|
|
|
555
561
|
#### [Model.replaceInto](Model_replaceInto.md) · static method
|
|
556
562
|
|
|
@@ -605,7 +611,7 @@ Check if this model instance is valid.
|
|
|
605
611
|
|
|
606
612
|
**Signature:** `() => string`
|
|
607
613
|
|
|
608
|
-
### [
|
|
614
|
+
### [defineModel](defineModel.md) · function
|
|
609
615
|
|
|
610
616
|
Register a model class with the Edinburgh ORM system.
|
|
611
617
|
|
|
@@ -613,6 +619,13 @@ Register a model class with the Edinburgh ORM system.
|
|
|
613
619
|
|
|
614
620
|
Create a field definition for a model property.
|
|
615
621
|
|
|
622
|
+
### currentTxn · function
|
|
623
|
+
|
|
624
|
+
Returns the current transaction from AsyncLocalStorage.
|
|
625
|
+
Throws if called outside a transact() callback.
|
|
626
|
+
|
|
627
|
+
**Signature:** `() => Transaction`
|
|
628
|
+
|
|
616
629
|
### string · constant
|
|
617
630
|
|
|
618
631
|
Type wrapper instance for the string type.
|
|
@@ -687,18 +700,6 @@ Create a literal type wrapper for a constant value.
|
|
|
687
700
|
|
|
688
701
|
Create a link type wrapper for model relationships.
|
|
689
702
|
|
|
690
|
-
### [index](index.md) · function
|
|
691
|
-
|
|
692
|
-
Create a secondary index on model fields, or a computed secondary index using a function.
|
|
693
|
-
|
|
694
|
-
### [primary](primary.md) · function
|
|
695
|
-
|
|
696
|
-
Create a primary index on model fields.
|
|
697
|
-
|
|
698
|
-
### [unique](unique.md) · function
|
|
699
|
-
|
|
700
|
-
Create a unique index on model fields, or a computed unique index using a function.
|
|
701
|
-
|
|
702
703
|
### [dump](dump.md) · function
|
|
703
704
|
|
|
704
705
|
Dump database contents for debugging.
|
|
@@ -709,6 +710,12 @@ Base class for database indexes for efficient lookups on model fields.
|
|
|
709
710
|
|
|
710
711
|
#### [baseIndex.find](BaseIndex_find.md) · method
|
|
711
712
|
|
|
713
|
+
#### [baseIndex.find](BaseIndex_find_2.md) · method
|
|
714
|
+
|
|
715
|
+
#### [baseIndex.find](BaseIndex_find_3.md) · method
|
|
716
|
+
|
|
717
|
+
#### [baseIndex.find](BaseIndex_find_4.md) · method
|
|
718
|
+
|
|
712
719
|
#### [baseIndex.batchProcess](BaseIndex_batchProcess.md) · method
|
|
713
720
|
|
|
714
721
|
[object Object],[object Object],[object Object]
|
|
@@ -717,27 +724,22 @@ Base class for database indexes for efficient lookups on model fields.
|
|
|
717
724
|
|
|
718
725
|
**Signature:** `() => string`
|
|
719
726
|
|
|
720
|
-
### [
|
|
721
|
-
|
|
722
|
-
Unique index that stores references to the primary key.
|
|
723
|
-
|
|
724
|
-
#### [uniqueIndex.get](UniqueIndex_get.md) · method
|
|
727
|
+
### [NonPrimaryIndex](NonPrimaryIndex.md) · abstract class
|
|
725
728
|
|
|
726
|
-
|
|
729
|
+
Abstract base for all non-primary indexes (unique and secondary).
|
|
730
|
+
Provides shared key serialization, write/delete/update logic.
|
|
727
731
|
|
|
728
|
-
### [
|
|
732
|
+
### [UniqueIndex](UniqueIndex.md) · class
|
|
729
733
|
|
|
730
|
-
|
|
734
|
+
Unique index that stores references to the primary key.
|
|
731
735
|
|
|
732
|
-
####
|
|
736
|
+
#### uniqueIndex.get · method
|
|
733
737
|
|
|
734
|
-
|
|
738
|
+
**Signature:** `(...args: ARGS) => InstanceType<M>`
|
|
735
739
|
|
|
736
|
-
|
|
740
|
+
**Parameters:**
|
|
737
741
|
|
|
738
|
-
|
|
739
|
-
property access. In case it turns out the instance doesn't exist, an error will be thrown
|
|
740
|
-
at that time.
|
|
742
|
+
- `args: ARGS`
|
|
741
743
|
|
|
742
744
|
### [SecondaryIndex](SecondaryIndex.md) · class
|
|
743
745
|
|
package/skill/SecondaryIndex.md
CHANGED
|
@@ -4,6 +4,6 @@ Secondary index for non-unique lookups.
|
|
|
4
4
|
|
|
5
5
|
**Type Parameters:**
|
|
6
6
|
|
|
7
|
-
- `M extends typeof Model`
|
|
8
|
-
- `F extends readonly (keyof InstanceType<M> & string)[]`
|
|
7
|
+
- `M extends typeof Model`
|
|
8
|
+
- `F extends readonly (keyof InstanceType<M> & string)[]`
|
|
9
9
|
- `ARGS extends readonly any[] = IndexArgTypes<M, F>`
|
package/skill/UniqueIndex.md
CHANGED
|
@@ -4,6 +4,6 @@ Unique index that stores references to the primary key.
|
|
|
4
4
|
|
|
5
5
|
**Type Parameters:**
|
|
6
6
|
|
|
7
|
-
- `M extends typeof Model`
|
|
8
|
-
- `F extends readonly (keyof InstanceType<M> & string)[]`
|
|
7
|
+
- `M extends typeof Model`
|
|
8
|
+
- `F extends readonly (keyof InstanceType<M> & string)[]`
|
|
9
9
|
- `ARGS extends readonly any[] = IndexArgTypes<M, F>`
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
### defineModel · function
|
|
2
|
+
|
|
3
|
+
Register a model class with the Edinburgh ORM system.
|
|
4
|
+
|
|
5
|
+
Converts a plain class into a fully-featured model with database persistence,
|
|
6
|
+
typed fields, primary key access, and optional secondary and unique indexes.
|
|
7
|
+
|
|
8
|
+
**Signature:** `<T extends new () => any, const PK extends (keyof FieldsOf<T> & string) | readonly (keyof FieldsOf<T> & string)[], const UNIQUE extends Record<string, (keyof FieldsOf<T> & string) | readonly (keyof FieldsOf<T> & string)[] | ((instance: any) => any)>, const INDEX extends Record<string, (keyof FieldsOf<T> & string) | ...`
|
|
9
|
+
|
|
10
|
+
**Type Parameters:**
|
|
11
|
+
|
|
12
|
+
- `T extends new () => any`
|
|
13
|
+
- `PK extends (keyof FieldsOf<T> & string) | readonly (keyof FieldsOf<T> & string)[]`
|
|
14
|
+
- `UNIQUE extends Record<string, (keyof FieldsOf<T> & string) | readonly (keyof FieldsOf<T> & string)[] | ((instance: any) => any)>`
|
|
15
|
+
- `INDEX extends Record<string, (keyof FieldsOf<T> & string) | readonly (keyof FieldsOf<T> & string)[] | ((instance: any) => any)>`
|
|
16
|
+
|
|
17
|
+
**Parameters:**
|
|
18
|
+
|
|
19
|
+
- `cls: T` - - A plain class whose properties use E.field().
|
|
20
|
+
- `opts?: { pk?: PK, unique?: UNIQUE, index?: INDEX, tableName?: string, override?: boolean }` - - Registration options.
|
|
21
|
+
|
|
22
|
+
**Returns:** The enhanced model constructor.
|
package/skill/field.md
CHANGED
|
@@ -22,8 +22,8 @@ This allows for both runtime introspection and compile-time type safety.
|
|
|
22
22
|
**Examples:**
|
|
23
23
|
|
|
24
24
|
```typescript
|
|
25
|
-
|
|
25
|
+
const User = E.defineModel(class {
|
|
26
26
|
name = E.field(E.string, {description: "User's full name"});
|
|
27
27
|
age = E.field(E.opt(E.number), {description: "User's age", default: 25});
|
|
28
|
-
}
|
|
28
|
+
});
|
|
29
29
|
```
|
package/skill/link.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Create a link type wrapper for model relationships.
|
|
4
4
|
|
|
5
|
-
**Signature:**
|
|
5
|
+
**Signature:** `{ <const T extends new (...args: any[]) => Model<any>>(TargetModel: T): TypeWrapper<InstanceType<T>>; <const T extends new (...args: any[]) => Model<any>>(TargetModel: () => T): TypeWrapper<...>; }`
|
|
6
6
|
|
|
7
7
|
**Type Parameters:**
|
|
8
8
|
|
|
9
|
-
- `T extends
|
|
9
|
+
- `T extends new (...args: any[]) => Model<any>` - The target model class.
|
|
10
10
|
|
|
11
11
|
**Parameters:**
|
|
12
12
|
|
|
@@ -17,11 +17,13 @@ Create a link type wrapper for model relationships.
|
|
|
17
17
|
**Examples:**
|
|
18
18
|
|
|
19
19
|
```typescript
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
const Author = E.defineModel(class {
|
|
21
|
+
id = E.field(E.identifier);
|
|
22
|
+
posts = E.field(E.array(E.link(() => Book)));
|
|
23
|
+
}, { pk: "id" });
|
|
24
|
+
|
|
25
|
+
const Book = E.defineModel(class {
|
|
26
|
+
id = E.field(E.identifier);
|
|
27
|
+
author = E.field(E.link(Author));
|
|
28
|
+
}, { pk: "id" });
|
|
27
29
|
```
|
package/skill/transact.md
CHANGED
|
@@ -32,7 +32,7 @@ times.
|
|
|
32
32
|
|
|
33
33
|
```typescript
|
|
34
34
|
const paid = await E.transact(() => {
|
|
35
|
-
const user = User.
|
|
35
|
+
const user = User.get("john_doe");
|
|
36
36
|
if (user.credits > 0) {
|
|
37
37
|
user.credits--;
|
|
38
38
|
return true;
|
|
@@ -43,7 +43,7 @@ const paid = await E.transact(() => {
|
|
|
43
43
|
```typescript
|
|
44
44
|
// Transaction with automatic retry on conflicts
|
|
45
45
|
await E.transact(() => {
|
|
46
|
-
const counter = Counter.
|
|
46
|
+
const counter = Counter.get("global") || new Counter({id: "global", value: 0});
|
|
47
47
|
counter.value++;
|
|
48
48
|
});
|
|
49
49
|
```
|
package/src/datapack.ts
CHANGED
|
@@ -187,7 +187,7 @@ export default class DataPack {
|
|
|
187
187
|
this.ensureCapacity(data.length);
|
|
188
188
|
this.buffer.set(data, this.writePos);
|
|
189
189
|
this.writePos += data.length;
|
|
190
|
-
} else if (Array.isArray(data)) {
|
|
190
|
+
} else if (Array.isArray(data) || (typeof data.length === 'number' && typeof data[Symbol.iterator] === 'function')) {
|
|
191
191
|
// Type 4, subtype 5: array start
|
|
192
192
|
this.buffer[this.writePos++] = (4 << 5) | 5;
|
|
193
193
|
for (const item of data) {
|