edinburgh 0.4.6 → 0.6.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 +403 -461
- package/build/src/datapack.d.ts +9 -9
- package/build/src/datapack.js +10 -10
- package/build/src/datapack.js.map +1 -1
- package/build/src/edinburgh.d.ts +21 -10
- package/build/src/edinburgh.js +33 -55
- package/build/src/edinburgh.js.map +1 -1
- package/build/src/indexes.d.ts +99 -288
- package/build/src/indexes.js +253 -636
- package/build/src/indexes.js.map +1 -1
- package/build/src/migrate.js +17 -39
- package/build/src/migrate.js.map +1 -1
- package/build/src/models.d.ts +177 -113
- package/build/src/models.js +487 -259
- package/build/src/models.js.map +1 -1
- package/build/src/types.d.ts +41 -51
- package/build/src/types.js +39 -52
- package/build/src/types.js.map +1 -1
- package/build/src/utils.d.ts +4 -4
- package/build/src/utils.js +4 -4
- package/package.json +1 -3
- package/skill/AnyModelClass.md +7 -0
- package/skill/FindOptions.md +37 -0
- package/skill/Lifecycle Hooks.md +24 -0
- package/skill/{Model_delete.md → Lifecycle Hooks_delete.md } +2 -2
- package/skill/{Model_getPrimaryKeyHash.md → Lifecycle Hooks_getPrimaryKeyHash.md } +1 -1
- package/skill/{Model_isValid.md → Lifecycle Hooks_isValid.md } +1 -1
- package/skill/Lifecycle Hooks_migrate.md +26 -0
- package/skill/{Model_preCommit.md → Lifecycle Hooks_preCommit.md } +3 -5
- package/skill/{Model_preventPersist.md → Lifecycle Hooks_preventPersist.md } +2 -2
- package/skill/{Model_validate.md → Lifecycle Hooks_validate.md } +2 -2
- package/skill/ModelBase.md +7 -0
- package/skill/ModelClass.md +8 -0
- package/skill/SKILL.md +253 -215
- package/skill/Schema Evolution.md +19 -0
- package/skill/TypeWrapper_containsNull.md +11 -0
- package/skill/TypeWrapper_deserialize.md +9 -0
- package/skill/TypeWrapper_getError.md +11 -0
- package/skill/TypeWrapper_serialize.md +10 -0
- package/skill/TypeWrapper_serializeType.md +9 -0
- package/skill/array.md +2 -2
- package/skill/defineModel.md +23 -0
- package/skill/deleteEverything.md +8 -0
- package/skill/field.md +4 -4
- package/skill/link.md +12 -10
- package/skill/literal.md +1 -1
- package/skill/opt.md +1 -1
- package/skill/or.md +1 -1
- package/skill/record.md +1 -1
- package/skill/set.md +2 -2
- package/skill/setOnSaveCallback.md +2 -2
- package/skill/transact.md +3 -3
- package/src/datapack.ts +10 -10
- package/src/edinburgh.ts +46 -58
- package/src/indexes.ts +338 -802
- package/src/migrate.ts +15 -37
- package/src/models.ts +617 -314
- package/src/types.ts +61 -54
- package/src/utils.ts +4 -4
- package/skill/BaseIndex.md +0 -16
- package/skill/BaseIndex_batchProcess.md +0 -10
- package/skill/BaseIndex_find.md +0 -7
- package/skill/Model.md +0 -22
- package/skill/Model_findAll.md +0 -12
- package/skill/Model_migrate.md +0 -34
- package/skill/Model_replaceInto.md +0 -16
- package/skill/PrimaryIndex.md +0 -8
- package/skill/PrimaryIndex_get.md +0 -17
- package/skill/PrimaryIndex_getLazy.md +0 -13
- package/skill/SecondaryIndex.md +0 -9
- package/skill/UniqueIndex.md +0 -9
- package/skill/UniqueIndex_get.md +0 -17
- package/skill/dump.md +0 -8
- 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/README.md
CHANGED
|
@@ -22,85 +22,82 @@ import * as E from "edinburgh";
|
|
|
22
22
|
// Initialize the database (optional, defaults to ".edinburgh")
|
|
23
23
|
E.init("./my-database");
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
@E.registerModel
|
|
27
|
-
class User extends E.Model<User> {
|
|
28
|
-
// Define a primary key (optional, defaults to using the "id" field)
|
|
29
|
-
static pk = E.primary(User, "id");
|
|
30
|
-
// Define a unique index on the email field
|
|
31
|
-
static byEmail = E.unique(User, "email");
|
|
32
|
-
|
|
33
|
-
// Define fields with simple types -- they will be type-checked at compile time and validated at runtime.
|
|
25
|
+
const User = E.defineModel("User", class {
|
|
34
26
|
id = E.field(E.identifier);
|
|
35
27
|
name = E.field(E.string);
|
|
36
28
|
age = E.field(E.number);
|
|
37
|
-
email = E.field(E.opt(E.string));
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
29
|
+
email = E.field(E.opt(E.string));
|
|
30
|
+
// Optional link to another instance of this model (needs a function as `User` is not defined yet at this point)
|
|
31
|
+
supervisor = E.field(E.opt(E.link(() => User)));
|
|
32
|
+
// A field with a more elaborate type. In TypeScript: `User | User[] | "unknown" | "whatever"`, defaulting to "unknown".
|
|
33
|
+
something = E.field(
|
|
34
|
+
E.or(
|
|
35
|
+
E.link(() => User),
|
|
36
|
+
E.array(E.link(() => User)),
|
|
37
|
+
E.literal("unknown"),
|
|
38
|
+
E.literal("whatever")
|
|
39
|
+
),
|
|
40
|
+
{ default: "unknown" }
|
|
41
|
+
);
|
|
42
|
+
}, {
|
|
43
|
+
pk: "id",
|
|
44
|
+
unique: {
|
|
45
|
+
email: "email",
|
|
46
|
+
},
|
|
47
|
+
});
|
|
45
48
|
|
|
46
|
-
// Use in transactions
|
|
47
49
|
await E.transact(() => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const john = new User({ // Unique 'id' is automatically generated if not provided
|
|
53
|
-
name: "John Doe",
|
|
50
|
+
// Unique 'id' values are auto-generated if not provided
|
|
51
|
+
const boss = new User({ name: "Big Boss", age: 50 });
|
|
52
|
+
new User({
|
|
53
|
+
name: "John Doe",
|
|
54
54
|
age: 41,
|
|
55
55
|
email: "john@example.com",
|
|
56
56
|
supervisor: boss, // Link to another model instance
|
|
57
57
|
});
|
|
58
|
+
// Newly instantiated models are automatically saved to the database on transaction commit
|
|
58
59
|
});
|
|
59
60
|
|
|
60
61
|
await E.transact(() => {
|
|
61
62
|
// Query by unique index
|
|
62
|
-
|
|
63
|
+
const john = User.getBy("email", "john@example.com")!;
|
|
63
64
|
|
|
64
65
|
// The transaction will retry if there's a conflict, such as another transaction
|
|
65
66
|
// modifying the same user (from another async function or another process)
|
|
66
67
|
john.age++;
|
|
67
68
|
|
|
68
69
|
// The supervisor object is lazy loaded on first access
|
|
69
|
-
console.log(`${john.supervisor!.name} is ${john.name}'s supervisor`);
|
|
70
|
+
console.log(`${john.supervisor!.name} is ${john.name}'s supervisor`);
|
|
70
71
|
});
|
|
71
72
|
```
|
|
72
73
|
|
|
73
74
|
## Tutorial
|
|
74
75
|
|
|
75
|
-
### TypeScript Configuration
|
|
76
|
-
|
|
77
|
-
When using TypeScript to transpile to JavaScript, make sure to enable the following options in your `tsconfig.json`:
|
|
78
|
-
|
|
79
|
-
```json
|
|
80
|
-
{
|
|
81
|
-
"compilerOptions": {
|
|
82
|
-
"target": "es2022",
|
|
83
|
-
"experimentalDecorators": true
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
76
|
|
|
88
77
|
### Defining Models
|
|
89
78
|
|
|
90
|
-
|
|
79
|
+
A model is defined using the `E.defineModel()` function by passing it..
|
|
80
|
+
- a consistent table name,
|
|
81
|
+
- an (anonymous) class containing `E.field` database properties and optionally regular properties/methods, and
|
|
82
|
+
- optional key/index configuration.
|
|
91
83
|
|
|
92
84
|
```typescript
|
|
93
85
|
import * as E from "edinburgh";
|
|
94
86
|
|
|
95
|
-
|
|
96
|
-
class User extends E.Model<User> {
|
|
97
|
-
static pk = E.primary(User, "id");
|
|
98
|
-
|
|
87
|
+
const User = E.defineModel("User", class {
|
|
99
88
|
id = E.field(E.identifier);
|
|
100
89
|
name = E.field(E.string);
|
|
101
90
|
email = E.field(E.string);
|
|
102
91
|
age = E.field(E.number);
|
|
103
|
-
}
|
|
92
|
+
}, {
|
|
93
|
+
pk: "id",
|
|
94
|
+
unique: {
|
|
95
|
+
email: "email",
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
// Add this if you want to use User as a type annotation (e.g. `let u: User`).
|
|
99
|
+
// Not needed just to call User.get(), User.find(), new User(), etc.
|
|
100
|
+
type User = InstanceType<typeof User>;
|
|
104
101
|
```
|
|
105
102
|
|
|
106
103
|
Instance fields are declared with `E.field(type, options?)`. Available types:
|
|
@@ -124,16 +121,13 @@ Instance fields are declared with `E.field(type, options?)`. Available types:
|
|
|
124
121
|
#### Defaults
|
|
125
122
|
|
|
126
123
|
```typescript
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
static pk = E.primary(Post, "id");
|
|
130
|
-
|
|
131
|
-
id = E.field(E.identifier); // auto-generated
|
|
124
|
+
const Post = E.defineModel("Post", class {
|
|
125
|
+
id = E.field(E.identifier); // auto-generated
|
|
132
126
|
title = E.field(E.string);
|
|
133
127
|
status = E.field(E.or("draft", "published"), {default: "draft"});
|
|
134
|
-
tags = E.field(E.array(E.string), {default: () => []});
|
|
135
|
-
createdAt = E.field(E.dateTime);
|
|
136
|
-
}
|
|
128
|
+
tags = E.field(E.array(E.string), {default: () => []}); // use function for mutable defaults
|
|
129
|
+
createdAt = E.field(E.dateTime); // dateTime defaults to new Date()
|
|
130
|
+
}, { pk: "id" });
|
|
137
131
|
```
|
|
138
132
|
|
|
139
133
|
### Transactions
|
|
@@ -146,19 +140,19 @@ E.init("./my-database");
|
|
|
146
140
|
|
|
147
141
|
// Create
|
|
148
142
|
await E.transact(() => {
|
|
149
|
-
|
|
150
|
-
|
|
143
|
+
// User.id is auto-generated
|
|
144
|
+
new User({name: "Alice", email: "alice@example.com", age: 30});
|
|
151
145
|
});
|
|
152
146
|
|
|
153
147
|
// Read + Update
|
|
154
148
|
await E.transact(() => {
|
|
155
|
-
const user = User.
|
|
149
|
+
const user = User.getBy("email", "alice@example.com");
|
|
156
150
|
if (user) user.age++;
|
|
157
151
|
});
|
|
158
152
|
|
|
159
153
|
// Return values from transactions
|
|
160
154
|
const name = await E.transact(() => {
|
|
161
|
-
const user = User.
|
|
155
|
+
const user = User.getBy("email", "alice@example.com");
|
|
162
156
|
return user?.name;
|
|
163
157
|
});
|
|
164
158
|
```
|
|
@@ -170,30 +164,29 @@ Transactions auto-retry on conflict (up to 6 times by default). Keep transaction
|
|
|
170
164
|
Edinburgh supports three index types:
|
|
171
165
|
|
|
172
166
|
```typescript
|
|
173
|
-
|
|
174
|
-
class Product extends E.Model<Product> {
|
|
175
|
-
static pk = E.primary(Product, "sku"); // primary: one per model, stores data
|
|
176
|
-
static byName = E.unique(Product, "name"); // unique: enforces uniqueness + fast lookup
|
|
177
|
-
static byCategory = E.index(Product, "category");// secondary: non-unique, for queries
|
|
178
|
-
|
|
167
|
+
const Product = E.defineModel("Product", class {
|
|
179
168
|
sku = E.field(E.string);
|
|
180
169
|
name = E.field(E.string);
|
|
181
170
|
category = E.field(E.string);
|
|
182
171
|
price = E.field(E.number);
|
|
183
|
-
}
|
|
172
|
+
}, {
|
|
173
|
+
pk: "sku",
|
|
174
|
+
unique: { name: "name" },
|
|
175
|
+
index: { category: "category" },
|
|
176
|
+
});
|
|
184
177
|
```
|
|
185
178
|
|
|
186
|
-
If no `
|
|
179
|
+
If no `pk` is provided, Edinburgh auto-creates one on an `id` field, adding it as an `E.identifier` field if needed.
|
|
187
180
|
|
|
188
181
|
#### Lookups
|
|
189
182
|
|
|
190
183
|
```typescript
|
|
191
184
|
await E.transact(() => {
|
|
192
185
|
// Primary key lookup
|
|
193
|
-
const
|
|
186
|
+
const p1 = Product.get("SKU-001");
|
|
194
187
|
|
|
195
188
|
// Unique index lookup
|
|
196
|
-
const p2 = Product.
|
|
189
|
+
const p2 = Product.getBy("name", "Widget");
|
|
197
190
|
|
|
198
191
|
// All return undefined if not found
|
|
199
192
|
});
|
|
@@ -201,54 +194,53 @@ await E.transact(() => {
|
|
|
201
194
|
|
|
202
195
|
#### Range Queries
|
|
203
196
|
|
|
204
|
-
|
|
197
|
+
Primary-key queries use `.find()`. Named unique and secondary indexes use `.findBy(name, ...)`:
|
|
205
198
|
|
|
206
199
|
```typescript
|
|
207
200
|
await E.transact(() => {
|
|
208
201
|
// Exact match
|
|
209
|
-
for (const p of Product.
|
|
202
|
+
for (const p of Product.findBy("category", {is: "electronics"})) {
|
|
210
203
|
console.log(p.name);
|
|
211
204
|
}
|
|
212
205
|
|
|
213
206
|
// Range (inclusive)
|
|
214
|
-
for (const p of Product.
|
|
207
|
+
for (const p of Product.find({from: "A", to: "M"})) {
|
|
215
208
|
console.log(p.sku);
|
|
216
209
|
}
|
|
217
210
|
|
|
218
211
|
// Exclusive bounds
|
|
219
|
-
for (const p of Product.
|
|
212
|
+
for (const p of Product.find({after: "A", before: "M"})) { ... }
|
|
220
213
|
|
|
221
214
|
// Open-ended
|
|
222
|
-
for (const p of Product.
|
|
215
|
+
for (const p of Product.find({from: "M"})) { ... }
|
|
223
216
|
|
|
224
217
|
// Reverse
|
|
225
|
-
for (const p of Product.
|
|
218
|
+
for (const p of Product.find({reverse: true})) { ... }
|
|
226
219
|
|
|
227
220
|
// Count and fetch helpers
|
|
228
|
-
const count = Product.
|
|
229
|
-
const first = Product.
|
|
221
|
+
const count = Product.findBy("category", {is: "electronics"}).count();
|
|
222
|
+
const first = Product.findBy("category", {is: "electronics"}).fetch(); // first match or undefined
|
|
230
223
|
});
|
|
231
224
|
```
|
|
232
225
|
|
|
233
|
-
#### Composite
|
|
226
|
+
#### Composite Primary Keys
|
|
234
227
|
|
|
235
228
|
```typescript
|
|
236
|
-
|
|
237
|
-
class Event extends E.Model<Event> {
|
|
238
|
-
static pk = E.primary(Event, ["year", "month", "id"]);
|
|
239
|
-
|
|
229
|
+
const Event = E.defineModel("Event", class {
|
|
240
230
|
year = E.field(E.number);
|
|
241
231
|
month = E.field(E.number);
|
|
242
232
|
id = E.field(E.identifier);
|
|
243
233
|
title = E.field(E.string);
|
|
244
|
-
}
|
|
234
|
+
}, {
|
|
235
|
+
pk: ["year", "month", "id"] as const,
|
|
236
|
+
});
|
|
245
237
|
|
|
246
238
|
await E.transact(() => {
|
|
247
239
|
// Prefix matching — find all events in 2025
|
|
248
|
-
for (const e of Event.
|
|
240
|
+
for (const e of Event.find({is: [2025]})) { ... }
|
|
249
241
|
|
|
250
242
|
// Find events in March 2025
|
|
251
|
-
for (const e of Event.
|
|
243
|
+
for (const e of Event.find({is: [2025, 3]})) { ... }
|
|
252
244
|
});
|
|
253
245
|
```
|
|
254
246
|
|
|
@@ -257,10 +249,7 @@ await E.transact(() => {
|
|
|
257
249
|
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.
|
|
258
250
|
|
|
259
251
|
```typescript
|
|
260
|
-
|
|
261
|
-
class User extends E.Model<User> {
|
|
262
|
-
static pk = E.primary(User, "id");
|
|
263
|
-
id = E.field(E.identifier);
|
|
252
|
+
const User = E.defineModel("User", class {
|
|
264
253
|
firstName = E.field(E.string);
|
|
265
254
|
lastName = E.field(E.string);
|
|
266
255
|
|
|
@@ -275,70 +264,68 @@ class User extends E.Model<User> {
|
|
|
275
264
|
greet(): string {
|
|
276
265
|
return `Hello, ${this.fullName}!`;
|
|
277
266
|
}
|
|
278
|
-
}
|
|
267
|
+
});
|
|
279
268
|
```
|
|
280
269
|
|
|
281
270
|
#### Computed Indexes
|
|
282
271
|
|
|
283
|
-
Instead of naming fields, you can pass a
|
|
272
|
+
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).
|
|
284
273
|
|
|
285
274
|
```typescript
|
|
286
|
-
|
|
287
|
-
class Article extends E.Model<Article> {
|
|
288
|
-
static pk = E.primary(Article, "id");
|
|
289
|
-
static byFullName = E.unique(Article, (a: Article) => [`${a.firstName} ${a.lastName}`]);
|
|
290
|
-
static byWord = E.index(Article, (a: Article) => a.title.toLowerCase().split(" "));
|
|
291
|
-
static byDomain = E.index(Article, (a: Article) => a.email ? [a.email.split("@")[1]] : []);
|
|
292
|
-
|
|
275
|
+
const Article = E.defineModel("Article", class {
|
|
293
276
|
id = E.field(E.identifier);
|
|
294
277
|
firstName = E.field(E.string);
|
|
295
278
|
lastName = E.field(E.string);
|
|
296
279
|
title = E.field(E.string);
|
|
297
280
|
email = E.field(E.opt(E.string));
|
|
298
|
-
}
|
|
281
|
+
}, {
|
|
282
|
+
pk: "id",
|
|
283
|
+
unique: {
|
|
284
|
+
fullName: (a: any) => [`${a.firstName} ${a.lastName}`], // computed covering unique index
|
|
285
|
+
},
|
|
286
|
+
index: {
|
|
287
|
+
domain: (a: any) => a.email ? [a.email.split("@")[1]] : [], // computed partial index
|
|
288
|
+
word: (a: any) => a.title.toLowerCase().split(" "), // computed multi-index
|
|
289
|
+
},
|
|
290
|
+
});
|
|
299
291
|
|
|
300
292
|
await E.transact(() => {
|
|
301
293
|
new Article({ firstName: "Jane", lastName: "Doe", title: "Hello World", email: "jane@acme.com" });
|
|
302
294
|
|
|
303
295
|
// Lookup via computed unique index
|
|
304
|
-
const jane = Article.
|
|
296
|
+
const jane = Article.getBy("fullName", "Jane Doe");
|
|
305
297
|
|
|
306
298
|
// Multi-value: each word in the title is indexed separately
|
|
307
|
-
for (const a of Article.
|
|
299
|
+
for (const a of Article.findBy("word", {is: "hello"})) { ... }
|
|
308
300
|
|
|
309
301
|
// Partial index: articles without email are skipped
|
|
310
|
-
for (const a of Article.
|
|
302
|
+
for (const a of Article.findBy("domain", {is: "acme.com"})) { ... }
|
|
311
303
|
});
|
|
312
304
|
```
|
|
313
305
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
### Relationships (Links)
|
|
306
|
+
### Relationships
|
|
317
307
|
|
|
318
|
-
Use `E.link(Model)` for foreign keys:
|
|
308
|
+
Use `E.link(Model)` for foreign keys. Use a thunk (a function that just returns a value) for forward references when needed:
|
|
319
309
|
|
|
320
310
|
```typescript
|
|
321
|
-
|
|
322
|
-
class Author extends E.Model<Author> {
|
|
323
|
-
static pk = E.primary(Author, "id");
|
|
311
|
+
const Author = E.defineModel("Author", class {
|
|
324
312
|
id = E.field(E.identifier);
|
|
325
313
|
name = E.field(E.string);
|
|
326
|
-
}
|
|
314
|
+
}, { pk: "id" });
|
|
327
315
|
|
|
328
|
-
|
|
329
|
-
class Book extends E.Model<Book> {
|
|
330
|
-
static pk = E.primary(Book, "id");
|
|
316
|
+
const Book = E.defineModel("Book", class {
|
|
331
317
|
id = E.field(E.identifier);
|
|
332
318
|
title = E.field(E.string);
|
|
333
319
|
author = E.field(E.link(Author));
|
|
334
|
-
}
|
|
320
|
+
}, { pk: "id" });
|
|
335
321
|
|
|
336
322
|
await E.transact(() => {
|
|
337
323
|
const author = new Author({name: "Tolkien"});
|
|
338
324
|
const book = new Book({title: "The Hobbit", author});
|
|
339
325
|
|
|
340
326
|
// Later: linked models are lazy-loaded on property access
|
|
341
|
-
const b = Book.
|
|
327
|
+
const b = Book.get(book.id)!;
|
|
328
|
+
console.log(b.author.id); // no need to load yet..
|
|
342
329
|
console.log(b.author.name); // loads Author automatically (~1µs)
|
|
343
330
|
});
|
|
344
331
|
```
|
|
@@ -347,7 +334,7 @@ await E.transact(() => {
|
|
|
347
334
|
|
|
348
335
|
```typescript
|
|
349
336
|
await E.transact(() => {
|
|
350
|
-
const user = User.
|
|
337
|
+
const user = User.get(someId);
|
|
351
338
|
if (user) user.delete();
|
|
352
339
|
});
|
|
353
340
|
```
|
|
@@ -365,10 +352,16 @@ await E.transact(() => {
|
|
|
365
352
|
user.preventPersist(); // exclude from commit
|
|
366
353
|
});
|
|
367
354
|
|
|
368
|
-
//
|
|
355
|
+
// find() iterates all instances (or use range options)
|
|
369
356
|
await E.transact(() => {
|
|
370
|
-
for (const user of User.
|
|
371
|
-
for (const user of User.
|
|
357
|
+
for (const user of User.find()) { ... }
|
|
358
|
+
for (const user of User.find({reverse: true})) { ... }
|
|
359
|
+
|
|
360
|
+
// {fetch: 'first'} returns a single instance or undefined
|
|
361
|
+
const first = User.find({fetch: 'first'});
|
|
362
|
+
|
|
363
|
+
// {fetch: 'single'} returns a single instance, or throws if there are none or more than one
|
|
364
|
+
const only = User.find({fetch: 'single'});
|
|
372
365
|
});
|
|
373
366
|
|
|
374
367
|
// replaceInto: upsert by primary key
|
|
@@ -382,7 +375,7 @@ await E.transact(() => {
|
|
|
382
375
|
For large datasets, `batchProcess` auto-commits in batches:
|
|
383
376
|
|
|
384
377
|
```typescript
|
|
385
|
-
await Product.
|
|
378
|
+
await Product.batchProcess({ limitRows: 1000 }, (product) => {
|
|
386
379
|
product.category = "archived";
|
|
387
380
|
});
|
|
388
381
|
// Commits every ~1 second or 4096 rows (configurable via limitSeconds, limitRows)
|
|
@@ -390,12 +383,10 @@ await Product.byCategory.batchProcess({is: "old"}, (product) => {
|
|
|
390
383
|
|
|
391
384
|
### Lazy Schema Migrations
|
|
392
385
|
|
|
393
|
-
When you change a model's schema, Edinburgh
|
|
386
|
+
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:
|
|
394
387
|
|
|
395
388
|
```typescript
|
|
396
|
-
|
|
397
|
-
class User extends E.Model<User> {
|
|
398
|
-
static pk = E.primary(User, "id");
|
|
389
|
+
const UserV2 = E.defineModel("User", class {
|
|
399
390
|
id = E.field(E.identifier);
|
|
400
391
|
name = E.field(E.string);
|
|
401
392
|
role = E.field(E.string); // newly added field
|
|
@@ -403,7 +394,7 @@ class User extends E.Model<User> {
|
|
|
403
394
|
static migrate(record: Record<string, any>) {
|
|
404
395
|
record.role ??= record.name.indexOf("admin") >= 0 ? "admin" : "user"; // set role based on name for old records
|
|
405
396
|
}
|
|
406
|
-
}
|
|
397
|
+
})
|
|
407
398
|
```
|
|
408
399
|
|
|
409
400
|
Edinburgh will lazily (re)run the `migrate` function on an instance whenever its implementation (the literal function code) has changed. For robustness, make sure that your `migrate` function...
|
|
@@ -438,9 +429,7 @@ console.log(result.secondaries); // { User: 1500 }
|
|
|
438
429
|
Compute derived fields before data is written:
|
|
439
430
|
|
|
440
431
|
```typescript
|
|
441
|
-
|
|
442
|
-
class Article extends E.Model<Article> {
|
|
443
|
-
static pk = E.primary(Article, "id");
|
|
432
|
+
const Article = E.defineModel("Article", class {
|
|
444
433
|
id = E.field(E.identifier);
|
|
445
434
|
title = E.field(E.string);
|
|
446
435
|
slug = E.field(E.string);
|
|
@@ -448,7 +437,7 @@ class Article extends E.Model<Article> {
|
|
|
448
437
|
preCommit() {
|
|
449
438
|
this.slug = this.title.toLowerCase().replace(/\s+/g, "-");
|
|
450
439
|
}
|
|
451
|
-
}
|
|
440
|
+
});
|
|
452
441
|
```
|
|
453
442
|
|
|
454
443
|
### Change Tracking
|
|
@@ -489,11 +478,14 @@ ln -s ../../node_modules/edinburgh/skill .claude/skills/edinburgh
|
|
|
489
478
|
|
|
490
479
|
The following is auto-generated from `src/edinburgh.ts`:
|
|
491
480
|
|
|
492
|
-
###
|
|
481
|
+
### currentTxn · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L66)
|
|
493
482
|
|
|
494
|
-
|
|
483
|
+
Returns the current transaction from AsyncLocalStorage.
|
|
484
|
+
Throws if called outside a transact() callback.
|
|
495
485
|
|
|
496
|
-
|
|
486
|
+
**Signature:** `() => Transaction`
|
|
487
|
+
|
|
488
|
+
### init · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L85)
|
|
497
489
|
|
|
498
490
|
Initialize the database with the specified directory path.
|
|
499
491
|
This function may be called multiple times with the same parameters. If it is not called before the first transact(),
|
|
@@ -511,7 +503,7 @@ the database will be automatically initialized with the default directory.
|
|
|
511
503
|
init("./my-database");
|
|
512
504
|
```
|
|
513
505
|
|
|
514
|
-
### transact · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
506
|
+
### transact · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L136)
|
|
515
507
|
|
|
516
508
|
Executes a function within a database transaction context.
|
|
517
509
|
|
|
@@ -531,7 +523,7 @@ times.
|
|
|
531
523
|
|
|
532
524
|
**Parameters:**
|
|
533
525
|
|
|
534
|
-
- `fn: () => T` -
|
|
526
|
+
- `fn: () => T` - The function to execute within the transaction context. Receives a Transaction instance.
|
|
535
527
|
|
|
536
528
|
**Returns:** A promise that resolves with the function's return value.
|
|
537
529
|
|
|
@@ -545,7 +537,7 @@ times.
|
|
|
545
537
|
|
|
546
538
|
```typescript
|
|
547
539
|
const paid = await E.transact(() => {
|
|
548
|
-
const user = User.
|
|
540
|
+
const user = User.get("john_doe");
|
|
549
541
|
if (user.credits > 0) {
|
|
550
542
|
user.credits--;
|
|
551
543
|
return true;
|
|
@@ -556,12 +548,12 @@ const paid = await E.transact(() => {
|
|
|
556
548
|
```typescript
|
|
557
549
|
// Transaction with automatic retry on conflicts
|
|
558
550
|
await E.transact(() => {
|
|
559
|
-
const counter = Counter.
|
|
551
|
+
const counter = Counter.get("global") || new Counter({id: "global", value: 0});
|
|
560
552
|
counter.value++;
|
|
561
553
|
});
|
|
562
554
|
```
|
|
563
555
|
|
|
564
|
-
### setMaxRetryCount · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
556
|
+
### setMaxRetryCount · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L226)
|
|
565
557
|
|
|
566
558
|
Set the maximum number of retries for a transaction in case of conflicts.
|
|
567
559
|
The default value is 6. Setting it to 0 will disable retries and cause transactions to fail immediately on conflict.
|
|
@@ -572,142 +564,121 @@ The default value is 6. Setting it to 0 will disable retries and cause transacti
|
|
|
572
564
|
|
|
573
565
|
- `count: number` - The maximum number of retries for a transaction.
|
|
574
566
|
|
|
575
|
-
### setOnSaveCallback · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
567
|
+
### setOnSaveCallback · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L240)
|
|
576
568
|
|
|
577
569
|
Set a callback function to be called after a model is saved and committed.
|
|
578
570
|
|
|
579
|
-
**Signature:** `(callback: (commitId: number, items: Map<
|
|
571
|
+
**Signature:** `(callback: (commitId: number, items: Map<ModelBase, Change>) => void) => void`
|
|
580
572
|
|
|
581
573
|
**Parameters:**
|
|
582
574
|
|
|
583
|
-
- `callback: ((commitId: number, items: Map<Model<
|
|
575
|
+
- `callback: ((commitId: number, items: Map<Model<unknown>, Change>) => void) | undefined` - The callback function to set. It gets called after each successful
|
|
584
576
|
`transact()` commit that has changes, with the following arguments:
|
|
585
577
|
- A sequential number. Higher numbers have been committed after lower numbers.
|
|
586
578
|
- A map of model instances to their changes. The change can be "created", "deleted", or an object containing the old values.
|
|
587
579
|
|
|
588
|
-
###
|
|
580
|
+
### Model · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
589
581
|
|
|
590
|
-
**
|
|
582
|
+
**Type:** `typeof ModelBase`
|
|
591
583
|
|
|
592
|
-
###
|
|
584
|
+
### ModelClass · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
593
585
|
|
|
594
|
-
|
|
586
|
+
Runtime base constructor for model classes returned by `defineModel()`.
|
|
595
587
|
|
|
596
|
-
|
|
588
|
+
Prefer the `ModelClass` type alias for annotations and the result of
|
|
589
|
+
`defineModel()` for concrete model classes.
|
|
597
590
|
|
|
598
|
-
|
|
591
|
+
**Type:** `typeof ModelClassRuntime`
|
|
599
592
|
|
|
600
|
-
|
|
593
|
+
### AnyModelClass · [type](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L111)
|
|
601
594
|
|
|
602
|
-
|
|
603
|
-
@E.registerModel
|
|
604
|
-
class User extends E.Model<User> {
|
|
605
|
-
static pk = E.primary(User, "id");
|
|
606
|
-
|
|
607
|
-
id = E.field(E.identifier);
|
|
608
|
-
name = E.field(E.string);
|
|
609
|
-
email = E.field(E.string);
|
|
610
|
-
|
|
611
|
-
static byEmail = E.unique(User, "email");
|
|
612
|
-
}
|
|
613
|
-
```
|
|
595
|
+
A model constructor with its generic information erased.
|
|
614
596
|
|
|
615
|
-
|
|
597
|
+
Useful when accepting or storing arbitrary registered model classes.
|
|
616
598
|
|
|
617
|
-
|
|
599
|
+
**Type:** `ModelClass<new () => any, readonly any[], any, any>`
|
|
618
600
|
|
|
619
|
-
|
|
601
|
+
### ModelBase · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
620
602
|
|
|
621
|
-
|
|
603
|
+
Base class for all database models in the Edinburgh ORM.
|
|
622
604
|
|
|
623
|
-
|
|
605
|
+
Models represent database entities with typed fields, automatic serialization,
|
|
606
|
+
change tracking, and relationship management. Model classes are created using
|
|
607
|
+
`E.defineModel()`.
|
|
624
608
|
|
|
625
|
-
|
|
609
|
+
### Schema Evolution
|
|
626
610
|
|
|
627
|
-
|
|
611
|
+
Edinburgh tracks the schema version of each model automatically. When you add, remove, or
|
|
612
|
+
change the types of fields, or add/remove indexes, Edinburgh detects the new schema version.
|
|
628
613
|
|
|
629
|
-
|
|
614
|
+
**Lazy migration:** Changes to non-key field values are migrated lazily, when a row with an
|
|
615
|
+
old schema version is read from disk, it is deserialized using the old schema and optionally
|
|
616
|
+
transformed by the static `migrate()` function. This happens transparently on every read
|
|
617
|
+
and requires no downtime or batch processing.
|
|
630
618
|
|
|
631
|
-
**
|
|
619
|
+
**Batch migration (via `npx migrate-edinburgh` or `runMigration()`):** Certain schema changes
|
|
620
|
+
require an explicit migration run:
|
|
621
|
+
- Adding or removing secondary/unique indexes
|
|
622
|
+
- Changing the fields or types of an existing index
|
|
623
|
+
- A `migrate()` function that changes values used in secondary index fields
|
|
632
624
|
|
|
633
|
-
|
|
625
|
+
The batch migration tool populates new indexes, deletes orphaned ones, and updates index
|
|
626
|
+
entries whose values were changed by `migrate()`. It does *not* rewrite primary data rows
|
|
627
|
+
(lazy migration handles that).
|
|
634
628
|
|
|
635
|
-
|
|
636
|
-
Receives a plain record with all fields (primary key fields + value fields) and should mutate it
|
|
637
|
-
in-place to match the current schema.
|
|
629
|
+
### Lifecycle Hooks
|
|
638
630
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
to detect changes. Modifying `migrate()` triggers a new schema version.
|
|
631
|
+
- **`static migrate(record)`**: Called when deserializing rows written with an older schema
|
|
632
|
+
version. Receives a plain record object; mutate it in-place to match the current schema.
|
|
642
633
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
**Signature:** `(record: Record<string, any>) => void`
|
|
647
|
-
|
|
648
|
-
**Parameters:**
|
|
649
|
-
|
|
650
|
-
- `record: Record<string, any>` - - A plain object with all field values from the old schema version.
|
|
634
|
+
- **`preCommit()`**: Called on each modified instance right before the transaction commits.
|
|
635
|
+
Useful for computing derived fields, enforcing cross-field invariants, or creating related
|
|
636
|
+
instances.
|
|
651
637
|
|
|
652
638
|
**Examples:**
|
|
653
639
|
|
|
654
640
|
```typescript
|
|
655
|
-
|
|
656
|
-
class User extends E.Model<User> {
|
|
657
|
-
static pk = E.primary(User, "id");
|
|
641
|
+
const User = E.defineModel("User", class {
|
|
658
642
|
id = E.field(E.identifier);
|
|
659
643
|
name = E.field(E.string);
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
644
|
+
email = E.field(E.string);
|
|
645
|
+
}, {
|
|
646
|
+
pk: "id",
|
|
647
|
+
unique: { email: "email" },
|
|
648
|
+
});
|
|
649
|
+
// Optional: declare a companion type so `let u: User` works.
|
|
650
|
+
// Not needed if you only use `new User()`, `User.find()`, etc.
|
|
651
|
+
type User = InstanceType<typeof User>;
|
|
666
652
|
```
|
|
667
653
|
|
|
668
|
-
####
|
|
669
|
-
|
|
670
|
-
Transform the model's `E.field` properties into the appropriate JavaScript properties. Normally this is done
|
|
671
|
-
automatically when using `transact()`, but in case you need to access `Model.fields` directly before the first
|
|
672
|
-
transaction, you can call this method manually.
|
|
673
|
-
|
|
674
|
-
**Signature:** `(reset?: boolean) => void`
|
|
675
|
-
|
|
676
|
-
**Parameters:**
|
|
677
|
-
|
|
678
|
-
- `reset?: boolean`
|
|
679
|
-
|
|
680
|
-
#### Model.findAll · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
|
|
654
|
+
#### ModelBase.migrate · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
681
655
|
|
|
682
|
-
|
|
656
|
+
Optional migration function called when deserializing rows written with an older schema version.
|
|
657
|
+
Receives a plain record with all fields and should mutate it in-place to match the current schema.
|
|
658
|
+
It runs during lazy loading and during `runMigration()`. Changing this method creates a new schema version.
|
|
659
|
+
If it updates values used by secondary or unique indexes, those index entries are refreshed only by `runMigration()`.
|
|
683
660
|
|
|
684
|
-
**Signature:**
|
|
661
|
+
**Signature:** `(record: Record<string, any>) => void`
|
|
685
662
|
|
|
686
663
|
**Parameters:**
|
|
687
664
|
|
|
688
|
-
- `
|
|
689
|
-
- `opts?: {reverse?: boolean}` - - Optional parameters.
|
|
690
|
-
|
|
691
|
-
**Returns:** An iterator.
|
|
692
|
-
|
|
693
|
-
#### Model.replaceInto · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
|
|
694
|
-
|
|
695
|
-
Load an existing instance by primary key and update it, or create a new one.
|
|
696
|
-
|
|
697
|
-
The provided object must contain all primary key fields. If a matching row exists,
|
|
698
|
-
the remaining properties from `obj` are set on the loaded instance. Otherwise a
|
|
699
|
-
new instance is created with `obj` as its initial properties.
|
|
665
|
+
- `record: Record<string, any>` - A plain object containing the row's field values from the older schema version.
|
|
700
666
|
|
|
701
|
-
**
|
|
702
|
-
|
|
703
|
-
**Parameters:**
|
|
667
|
+
**Examples:**
|
|
704
668
|
|
|
705
|
-
|
|
706
|
-
|
|
669
|
+
```typescript
|
|
670
|
+
const User = E.defineModel("User", class {
|
|
671
|
+
id = E.field(E.identifier);
|
|
672
|
+
name = E.field(E.string);
|
|
673
|
+
role = E.field(E.string);
|
|
707
674
|
|
|
708
|
-
|
|
675
|
+
static migrate(record: Record<string, any>) {
|
|
676
|
+
record.role ??= "user";
|
|
677
|
+
}
|
|
678
|
+
}, { pk: "id" });
|
|
679
|
+
```
|
|
709
680
|
|
|
710
|
-
####
|
|
681
|
+
#### modelBase.preCommit · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
711
682
|
|
|
712
683
|
Optional hook called on each modified instance right before the transaction commits.
|
|
713
684
|
Runs before data is written to disk, so changes made here are included in the commit.
|
|
@@ -723,9 +694,7 @@ Common use cases:
|
|
|
723
694
|
**Examples:**
|
|
724
695
|
|
|
725
696
|
```typescript
|
|
726
|
-
|
|
727
|
-
class Post extends E.Model<Post> {
|
|
728
|
-
static pk = E.primary(Post, "id");
|
|
697
|
+
const Post = E.defineModel("Post", class {
|
|
729
698
|
id = E.field(E.identifier);
|
|
730
699
|
title = E.field(E.string);
|
|
731
700
|
slug = E.field(E.string);
|
|
@@ -733,22 +702,22 @@ class Post extends E.Model<Post> {
|
|
|
733
702
|
preCommit() {
|
|
734
703
|
this.slug = this.title.toLowerCase().replace(/\s+/g, "-");
|
|
735
704
|
}
|
|
736
|
-
}
|
|
705
|
+
}, { pk: "id" });
|
|
737
706
|
```
|
|
738
707
|
|
|
739
|
-
####
|
|
708
|
+
#### modelBase.getPrimaryKey · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
740
709
|
|
|
741
710
|
**Signature:** `() => Uint8Array<ArrayBufferLike>`
|
|
742
711
|
|
|
743
712
|
**Returns:** The primary key for this instance.
|
|
744
713
|
|
|
745
|
-
####
|
|
714
|
+
#### modelBase.getPrimaryKeyHash · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
746
715
|
|
|
747
716
|
**Signature:** `() => number`
|
|
748
717
|
|
|
749
718
|
**Returns:** A 53-bit positive integer non-cryptographic hash of the primary key, or undefined if not yet saved.
|
|
750
719
|
|
|
751
|
-
####
|
|
720
|
+
#### modelBase.isLazyField · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
752
721
|
|
|
753
722
|
**Signature:** `(field: keyof this) => boolean`
|
|
754
723
|
|
|
@@ -756,7 +725,7 @@ class Post extends E.Model<Post> {
|
|
|
756
725
|
|
|
757
726
|
- `field: keyof this`
|
|
758
727
|
|
|
759
|
-
####
|
|
728
|
+
#### modelBase.preventPersist · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
760
729
|
|
|
761
730
|
Prevent this instance from being persisted to the database.
|
|
762
731
|
|
|
@@ -767,12 +736,12 @@ Prevent this instance from being persisted to the database.
|
|
|
767
736
|
**Examples:**
|
|
768
737
|
|
|
769
738
|
```typescript
|
|
770
|
-
const user = User.
|
|
739
|
+
const user = User.get("user123");
|
|
771
740
|
user.name = "New Name";
|
|
772
741
|
user.preventPersist(); // Changes won't be saved
|
|
773
742
|
```
|
|
774
743
|
|
|
775
|
-
####
|
|
744
|
+
#### modelBase.delete · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
776
745
|
|
|
777
746
|
Delete this model instance from the database.
|
|
778
747
|
|
|
@@ -783,11 +752,11 @@ Removes the instance and all its index entries from the database and prevents fu
|
|
|
783
752
|
**Examples:**
|
|
784
753
|
|
|
785
754
|
```typescript
|
|
786
|
-
const user = User.
|
|
755
|
+
const user = User.get("user123");
|
|
787
756
|
user.delete(); // Removes from database
|
|
788
757
|
```
|
|
789
758
|
|
|
790
|
-
####
|
|
759
|
+
#### modelBase.validate · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
791
760
|
|
|
792
761
|
Validate all fields in this model instance.
|
|
793
762
|
|
|
@@ -795,7 +764,7 @@ Validate all fields in this model instance.
|
|
|
795
764
|
|
|
796
765
|
**Parameters:**
|
|
797
766
|
|
|
798
|
-
- `raise: boolean` (optional) -
|
|
767
|
+
- `raise: boolean` (optional) - If true, throw on first validation error.
|
|
799
768
|
|
|
800
769
|
**Returns:** Array of validation errors (empty if valid).
|
|
801
770
|
|
|
@@ -809,7 +778,7 @@ if (errors.length > 0) {
|
|
|
809
778
|
}
|
|
810
779
|
```
|
|
811
780
|
|
|
812
|
-
####
|
|
781
|
+
#### modelBase.isValid · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
813
782
|
|
|
814
783
|
Check if this model instance is valid.
|
|
815
784
|
|
|
@@ -824,46 +793,52 @@ const user = new User({name: "John"});
|
|
|
824
793
|
if (!user.isValid()) shoutAtTheUser();
|
|
825
794
|
```
|
|
826
795
|
|
|
827
|
-
####
|
|
796
|
+
#### modelBase.getState · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
828
797
|
|
|
829
798
|
**Signature:** `() => "created" | "deleted" | "loaded" | "lazy"`
|
|
830
799
|
|
|
831
|
-
####
|
|
800
|
+
#### modelBase.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
832
801
|
|
|
833
802
|
**Signature:** `() => string`
|
|
834
803
|
|
|
835
|
-
####
|
|
804
|
+
#### modelBase.[Symbol.for('nodejs.util.inspect.custom')] · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
836
805
|
|
|
837
806
|
**Signature:** `() => string`
|
|
838
807
|
|
|
839
|
-
###
|
|
808
|
+
### defineModel · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
840
809
|
|
|
841
810
|
Register a model class with the Edinburgh ORM system.
|
|
842
811
|
|
|
843
|
-
|
|
812
|
+
Converts a plain class into a fully-featured model with database persistence,
|
|
813
|
+
typed fields, primary key access, and optional secondary and unique indexes.
|
|
814
|
+
|
|
815
|
+
**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) | ...`
|
|
844
816
|
|
|
845
817
|
**Type Parameters:**
|
|
846
818
|
|
|
847
|
-
- `T extends
|
|
819
|
+
- `T extends new () => any`
|
|
820
|
+
- `PK extends (keyof FieldsOf<T> & string) | readonly (keyof FieldsOf<T> & string)[]`
|
|
821
|
+
- `UNIQUE extends Record<string, (keyof FieldsOf<T> & string) | readonly (keyof FieldsOf<T> & string)[] | ((instance: any) => any)>`
|
|
822
|
+
- `INDEX extends Record<string, (keyof FieldsOf<T> & string) | readonly (keyof FieldsOf<T> & string)[] | ((instance: any) => any)>`
|
|
848
823
|
|
|
849
824
|
**Parameters:**
|
|
850
825
|
|
|
851
|
-
- `
|
|
826
|
+
- `tableName: string` - The database table name for this model.
|
|
827
|
+
- `cls: T` - A plain class whose properties use E.field().
|
|
828
|
+
- `opts?: { pk?: PK, unique?: UNIQUE, index?: INDEX, override?: boolean }` - Registration options.
|
|
852
829
|
|
|
853
|
-
**Returns:** The enhanced model
|
|
830
|
+
**Returns:** The enhanced model constructor.
|
|
854
831
|
|
|
855
|
-
|
|
832
|
+
### deleteEverything · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
856
833
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
id = E.field(E.identifier);
|
|
862
|
-
name = E.field(E.string);
|
|
863
|
-
}
|
|
864
|
-
```
|
|
834
|
+
Delete every key/value entry in the database and reinitialize all registered models.
|
|
835
|
+
|
|
836
|
+
This clears rows, index metadata, and schema-version records. It is mainly useful
|
|
837
|
+
for tests, local resets, or tooling that needs a completely empty database.
|
|
865
838
|
|
|
866
|
-
|
|
839
|
+
**Signature:** `() => Promise<void>`
|
|
840
|
+
|
|
841
|
+
### field · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L76)
|
|
867
842
|
|
|
868
843
|
Create a field definition for a model property.
|
|
869
844
|
|
|
@@ -879,27 +854,27 @@ This allows for both runtime introspection and compile-time type safety.
|
|
|
879
854
|
|
|
880
855
|
**Parameters:**
|
|
881
856
|
|
|
882
|
-
- `type: TypeWrapper<T>` -
|
|
883
|
-
- `options: Partial<FieldConfig<T>>` (optional) -
|
|
857
|
+
- `type: TypeWrapper<T>` - The type wrapper for this field.
|
|
858
|
+
- `options: Partial<FieldConfig<T>>` (optional) - Additional field configuration options.
|
|
884
859
|
|
|
885
860
|
**Returns:** The field value (typed as T, but actually returns FieldConfig<T>).
|
|
886
861
|
|
|
887
862
|
**Examples:**
|
|
888
863
|
|
|
889
864
|
```typescript
|
|
890
|
-
|
|
865
|
+
const User = E.defineModel("User", class {
|
|
891
866
|
name = E.field(E.string, {description: "User's full name"});
|
|
892
867
|
age = E.field(E.opt(E.number), {description: "User's age", default: 25});
|
|
893
|
-
}
|
|
868
|
+
});
|
|
894
869
|
```
|
|
895
870
|
|
|
896
|
-
### string · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
871
|
+
### string · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
897
872
|
|
|
898
873
|
Type wrapper instance for the string type.
|
|
899
874
|
|
|
900
875
|
**Value:** `TypeWrapper<string>`
|
|
901
876
|
|
|
902
|
-
### orderedString · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
877
|
+
### orderedString · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
903
878
|
|
|
904
879
|
Type wrapper instance for the ordered string type, which is just like a string
|
|
905
880
|
except that it sorts lexicographically in the database (instead of by incrementing
|
|
@@ -909,37 +884,37 @@ may not contain null characters.
|
|
|
909
884
|
|
|
910
885
|
**Value:** `TypeWrapper<string>`
|
|
911
886
|
|
|
912
|
-
### number · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
887
|
+
### number · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
913
888
|
|
|
914
889
|
Type wrapper instance for the number type.
|
|
915
890
|
|
|
916
891
|
**Value:** `TypeWrapper<number>`
|
|
917
892
|
|
|
918
|
-
### dateTime · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
893
|
+
### dateTime · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
919
894
|
|
|
920
895
|
Type wrapper instance for the date/time type. Stored without timezone info, rounded to whole seconds.
|
|
921
896
|
|
|
922
897
|
**Value:** `TypeWrapper<Date>`
|
|
923
898
|
|
|
924
|
-
### boolean · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
899
|
+
### boolean · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
925
900
|
|
|
926
901
|
Type wrapper instance for the boolean type.
|
|
927
902
|
|
|
928
903
|
**Value:** `TypeWrapper<boolean>`
|
|
929
904
|
|
|
930
|
-
### identifier · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
905
|
+
### identifier · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
931
906
|
|
|
932
907
|
Type wrapper instance for the identifier type.
|
|
933
908
|
|
|
934
909
|
**Value:** `TypeWrapper<string>`
|
|
935
910
|
|
|
936
|
-
### undef · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
911
|
+
### undef · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
937
912
|
|
|
938
913
|
Type wrapper instance for the 'undefined' type.
|
|
939
914
|
|
|
940
915
|
**Value:** `TypeWrapper<undefined>`
|
|
941
916
|
|
|
942
|
-
### opt · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
917
|
+
### opt · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
943
918
|
|
|
944
919
|
Create an optional type wrapper (allows undefined).
|
|
945
920
|
|
|
@@ -951,7 +926,7 @@ Create an optional type wrapper (allows undefined).
|
|
|
951
926
|
|
|
952
927
|
**Parameters:**
|
|
953
928
|
|
|
954
|
-
- `inner: T` -
|
|
929
|
+
- `inner: T` - The inner type to make optional.
|
|
955
930
|
|
|
956
931
|
**Returns:** A union type that accepts the inner type or undefined.
|
|
957
932
|
|
|
@@ -962,7 +937,7 @@ const optionalString = E.opt(E.string);
|
|
|
962
937
|
const optionalNumber = E.opt(E.number);
|
|
963
938
|
```
|
|
964
939
|
|
|
965
|
-
### or · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
940
|
+
### or · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
966
941
|
|
|
967
942
|
Create a union type wrapper from multiple type choices.
|
|
968
943
|
|
|
@@ -974,7 +949,7 @@ Create a union type wrapper from multiple type choices.
|
|
|
974
949
|
|
|
975
950
|
**Parameters:**
|
|
976
951
|
|
|
977
|
-
- `choices: T` -
|
|
952
|
+
- `choices: T` - The type choices for the union.
|
|
978
953
|
|
|
979
954
|
**Returns:** A union type instance.
|
|
980
955
|
|
|
@@ -985,7 +960,7 @@ const stringOrNumber = E.or(E.string, E.number);
|
|
|
985
960
|
const status = E.or("active", "inactive", "pending");
|
|
986
961
|
```
|
|
987
962
|
|
|
988
|
-
### array · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
963
|
+
### array · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
989
964
|
|
|
990
965
|
Create an array type wrapper with optional length constraints.
|
|
991
966
|
|
|
@@ -997,8 +972,8 @@ Create an array type wrapper with optional length constraints.
|
|
|
997
972
|
|
|
998
973
|
**Parameters:**
|
|
999
974
|
|
|
1000
|
-
- `inner: TypeWrapper<T>` -
|
|
1001
|
-
- `opts: {min?: number, max?: number}` (optional) -
|
|
975
|
+
- `inner: TypeWrapper<T>` - Type wrapper for array elements.
|
|
976
|
+
- `opts: {min?: number, max?: number}` (optional) - Optional constraints (min/max length).
|
|
1002
977
|
|
|
1003
978
|
**Returns:** An array type instance.
|
|
1004
979
|
|
|
@@ -1009,7 +984,7 @@ const stringArray = E.array(E.string);
|
|
|
1009
984
|
const boundedArray = E.array(E.number, {min: 1, max: 10});
|
|
1010
985
|
```
|
|
1011
986
|
|
|
1012
|
-
### set · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
987
|
+
### set · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
1013
988
|
|
|
1014
989
|
Create a Set type wrapper with optional length constraints.
|
|
1015
990
|
|
|
@@ -1021,8 +996,8 @@ Create a Set type wrapper with optional length constraints.
|
|
|
1021
996
|
|
|
1022
997
|
**Parameters:**
|
|
1023
998
|
|
|
1024
|
-
- `inner: TypeWrapper<T>` -
|
|
1025
|
-
- `opts: {min?: number, max?: number}` (optional) -
|
|
999
|
+
- `inner: TypeWrapper<T>` - Type wrapper for set elements.
|
|
1000
|
+
- `opts: {min?: number, max?: number}` (optional) - Optional constraints (min/max length).
|
|
1026
1001
|
|
|
1027
1002
|
**Returns:** A set type instance.
|
|
1028
1003
|
|
|
@@ -1033,7 +1008,7 @@ const stringSet = E.set(E.string);
|
|
|
1033
1008
|
const boundedSet = E.set(E.number, {min: 1, max: 10});
|
|
1034
1009
|
```
|
|
1035
1010
|
|
|
1036
|
-
### record · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1011
|
+
### record · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
1037
1012
|
|
|
1038
1013
|
Create a Record type wrapper for key-value objects with string or number keys.
|
|
1039
1014
|
|
|
@@ -1045,7 +1020,7 @@ Create a Record type wrapper for key-value objects with string or number keys.
|
|
|
1045
1020
|
|
|
1046
1021
|
**Parameters:**
|
|
1047
1022
|
|
|
1048
|
-
- `inner: TypeWrapper<T>` -
|
|
1023
|
+
- `inner: TypeWrapper<T>` - Type wrapper for record values.
|
|
1049
1024
|
|
|
1050
1025
|
**Returns:** A record type instance.
|
|
1051
1026
|
|
|
@@ -1055,7 +1030,7 @@ Create a Record type wrapper for key-value objects with string or number keys.
|
|
|
1055
1030
|
const scores = E.record(E.number); // Record<string | number, number>
|
|
1056
1031
|
```
|
|
1057
1032
|
|
|
1058
|
-
### literal · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1033
|
+
### literal · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
1059
1034
|
|
|
1060
1035
|
Create a literal type wrapper for a constant value.
|
|
1061
1036
|
|
|
@@ -1067,7 +1042,7 @@ Create a literal type wrapper for a constant value.
|
|
|
1067
1042
|
|
|
1068
1043
|
**Parameters:**
|
|
1069
1044
|
|
|
1070
|
-
- `value: T` -
|
|
1045
|
+
- `value: T` - The literal value.
|
|
1071
1046
|
|
|
1072
1047
|
**Returns:** A literal type instance.
|
|
1073
1048
|
|
|
@@ -1078,274 +1053,227 @@ const statusType = E.literal("active");
|
|
|
1078
1053
|
const countType = E.literal(42);
|
|
1079
1054
|
```
|
|
1080
1055
|
|
|
1081
|
-
### link · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1056
|
+
### link · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
1082
1057
|
|
|
1083
1058
|
Create a link type wrapper for model relationships.
|
|
1084
1059
|
|
|
1085
|
-
**Signature:**
|
|
1060
|
+
**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<...>; }`
|
|
1086
1061
|
|
|
1087
1062
|
**Type Parameters:**
|
|
1088
1063
|
|
|
1089
|
-
- `T extends
|
|
1064
|
+
- `T extends new (...args: any[]) => Model<any>` - The target model class.
|
|
1090
1065
|
|
|
1091
1066
|
**Parameters:**
|
|
1092
1067
|
|
|
1093
|
-
- `TargetModel: T` -
|
|
1068
|
+
- `TargetModel: T` - The model class this link points to.
|
|
1094
1069
|
|
|
1095
1070
|
**Returns:** A link type instance.
|
|
1096
1071
|
|
|
1097
1072
|
**Examples:**
|
|
1098
1073
|
|
|
1099
1074
|
```typescript
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1075
|
+
const Author = E.defineModel("Author", class {
|
|
1076
|
+
id = E.field(E.identifier);
|
|
1077
|
+
posts = E.field(E.array(E.link(() => Book)));
|
|
1078
|
+
}, { pk: "id" });
|
|
1103
1079
|
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1080
|
+
const Book = E.defineModel("Book", class {
|
|
1081
|
+
id = E.field(E.identifier);
|
|
1082
|
+
author = E.field(E.link(Author));
|
|
1083
|
+
}, { pk: "id" });
|
|
1107
1084
|
```
|
|
1108
1085
|
|
|
1109
|
-
###
|
|
1110
|
-
|
|
1111
|
-
Create a secondary index on model fields, or a computed secondary index using a function.
|
|
1112
|
-
|
|
1113
|
-
For field-based indexes, pass a field name or array of field names.
|
|
1114
|
-
For computed indexes, pass a function that takes a model instance and returns an array of
|
|
1115
|
-
index keys. Return `[]` to skip indexing for that instance. Each array element creates a
|
|
1116
|
-
separate index entry, enabling multi-value indexes (e.g., indexing by each word in a name).
|
|
1117
|
-
|
|
1118
|
-
**Signature:** `{ <M extends typeof Model, V>(MyModel: M, fn: (instance: InstanceType<M>) => V[]): SecondaryIndex<M, [], [V]>; <M extends typeof Model, const F extends (keyof InstanceType<M> & string)>(MyModel: M, field: F): SecondaryIndex<...>; <M extends typeof Model, const FS extends readonly (keyof InstanceType<M> & string)[]>(...`
|
|
1119
|
-
|
|
1120
|
-
**Type Parameters:**
|
|
1086
|
+
### dump · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
|
|
1121
1087
|
|
|
1122
|
-
|
|
1123
|
-
- `V` - The computed index value type (for function-based indexes).
|
|
1124
|
-
|
|
1125
|
-
**Parameters:**
|
|
1088
|
+
**Signature:** `() => void`
|
|
1126
1089
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1090
|
+
### FindOptions · [type](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L115)
|
|
1091
|
+
|
|
1092
|
+
Range-query options accepted by `find()`, `findBy()`, `batchProcess()`, and `batchProcessBy()`.
|
|
1093
|
+
|
|
1094
|
+
Supports exact-match lookups via `is`, inclusive bounds via `from` / `to`,
|
|
1095
|
+
exclusive bounds via `after` / `before`, and reverse scans.
|
|
1096
|
+
|
|
1097
|
+
For single-field indexes, values can be passed directly. For composite indexes,
|
|
1098
|
+
pass tuples or partial tuples for prefix matching.
|
|
1099
|
+
|
|
1100
|
+
**Type:** `(
|
|
1101
|
+
(
|
|
1102
|
+
{is: ArrayOrOnlyItem<ARG_TYPES>;} // Shortcut for setting `from` and `to` to the same value
|
|
1103
|
+
|
|
|
1104
|
+
(
|
|
1105
|
+
(
|
|
1106
|
+
{from: ArrayOrOnlyItem<ARG_TYPES>;}
|
|
1107
|
+
|
|
|
1108
|
+
{after: ArrayOrOnlyItem<ARG_TYPES>;}
|
|
1109
|
+
|
|
|
1110
|
+
{}
|
|
1111
|
+
)
|
|
1112
|
+
&
|
|
1113
|
+
(
|
|
1114
|
+
{to: ArrayOrOnlyItem<ARG_TYPES>;}
|
|
1115
|
+
|
|
|
1116
|
+
{before: ArrayOrOnlyItem<ARG_TYPES>;}
|
|
1117
|
+
|
|
|
1118
|
+
{}
|
|
1119
|
+
)
|
|
1120
|
+
)
|
|
1121
|
+
) &
|
|
1122
|
+
{
|
|
1123
|
+
reverse?: boolean;
|
|
1124
|
+
}
|
|
1125
|
+
& (FETCH extends undefined ? { fetch?: undefined } : { fetch: FETCH })
|
|
1126
|
+
)`
|
|
1129
1127
|
|
|
1130
|
-
|
|
1128
|
+
### IndexRangeIterator · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L73)
|
|
1131
1129
|
|
|
1132
|
-
|
|
1130
|
+
Iterator for range queries on indexes.
|
|
1131
|
+
Handles common iteration logic for both primary and unique indexes.
|
|
1132
|
+
Extends built-in Iterator to provide map/filter/reduce/toArray/etc.
|
|
1133
1133
|
|
|
1134
|
-
|
|
1135
|
-
class User extends E.Model<User> {
|
|
1136
|
-
static byAge = E.index(User, "age");
|
|
1137
|
-
static byTagsDate = E.index(User, ["tags", "createdAt"]);
|
|
1138
|
-
static byWord = E.index(User, (u: User) => u.name.split(" "));
|
|
1139
|
-
}
|
|
1140
|
-
```
|
|
1134
|
+
**Type Parameters:**
|
|
1141
1135
|
|
|
1142
|
-
|
|
1136
|
+
- `ITEM`
|
|
1143
1137
|
|
|
1144
|
-
|
|
1138
|
+
#### indexRangeIterator.[Symbol.iterator] · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L78)
|
|
1145
1139
|
|
|
1146
|
-
**Signature:** `
|
|
1140
|
+
**Signature:** `() => this`
|
|
1147
1141
|
|
|
1148
|
-
|
|
1142
|
+
#### indexRangeIterator.next · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L80)
|
|
1149
1143
|
|
|
1150
|
-
|
|
1151
|
-
- `F extends (keyof InstanceType<M> & string)` - The field name (for single field index).
|
|
1144
|
+
**Signature:** `() => IteratorResult<ITEM, any>`
|
|
1152
1145
|
|
|
1153
|
-
|
|
1146
|
+
#### indexRangeIterator.count · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L102)
|
|
1154
1147
|
|
|
1155
|
-
|
|
1156
|
-
- `field: F` - - Single field name for simple indexes.
|
|
1148
|
+
**Signature:** `() => number`
|
|
1157
1149
|
|
|
1158
|
-
|
|
1150
|
+
#### indexRangeIterator.fetch · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L105)
|
|
1159
1151
|
|
|
1160
|
-
**
|
|
1152
|
+
**Signature:** `() => ITEM`
|
|
1161
1153
|
|
|
1162
|
-
|
|
1163
|
-
class User extends E.Model<User> {
|
|
1164
|
-
static pk = E.primary(User, ["id"]);
|
|
1165
|
-
static pkSingle = E.primary(User, "id");
|
|
1166
|
-
}
|
|
1167
|
-
```
|
|
1168
|
-
|
|
1169
|
-
### unique · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
|
|
1154
|
+
### Change · [type](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L85)
|
|
1170
1155
|
|
|
1171
|
-
|
|
1156
|
+
**Type:** `Record<any, any> | "created" | "deleted"`
|
|
1172
1157
|
|
|
1173
|
-
|
|
1174
|
-
For computed indexes, pass a function that takes a model instance and returns an array of
|
|
1175
|
-
index keys. Return `[]` to skip indexing for that instance. Each array element creates a
|
|
1176
|
-
separate index entry, enabling multi-value indexes (e.g., indexing by each word in a name).
|
|
1158
|
+
### FieldConfig · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L37)
|
|
1177
1159
|
|
|
1178
|
-
|
|
1160
|
+
Configuration interface for model fields.
|
|
1179
1161
|
|
|
1180
1162
|
**Type Parameters:**
|
|
1181
1163
|
|
|
1182
|
-
- `
|
|
1183
|
-
- `V` - The computed index value type (for function-based indexes).
|
|
1184
|
-
|
|
1185
|
-
**Parameters:**
|
|
1186
|
-
|
|
1187
|
-
- `MyModel: M` - - The model class to create the index for.
|
|
1188
|
-
- `fn: (instance: InstanceType<M>) => V[]`
|
|
1164
|
+
- `T` - The field type.
|
|
1189
1165
|
|
|
1190
|
-
|
|
1166
|
+
#### fieldConfig.type · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L45)
|
|
1191
1167
|
|
|
1192
|
-
|
|
1168
|
+
The type wrapper that defines how this field is serialized/validated.
|
|
1193
1169
|
|
|
1194
|
-
|
|
1195
|
-
class User extends E.Model<User> {
|
|
1196
|
-
static byEmail = E.unique(User, "email");
|
|
1197
|
-
static byNameAge = E.unique(User, ["name", "age"]);
|
|
1198
|
-
static byFullName = E.unique(User, (u: User) => [`${u.firstName} ${u.lastName}`]);
|
|
1199
|
-
}
|
|
1200
|
-
```
|
|
1170
|
+
**Type:** `TypeWrapper<T>`
|
|
1201
1171
|
|
|
1202
|
-
|
|
1172
|
+
#### fieldConfig.description · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L47)
|
|
1203
1173
|
|
|
1204
|
-
|
|
1174
|
+
Optional human-readable description of the field.
|
|
1205
1175
|
|
|
1206
|
-
|
|
1207
|
-
This is primarily useful for development and debugging purposes.
|
|
1176
|
+
**Type:** `string`
|
|
1208
1177
|
|
|
1209
|
-
|
|
1178
|
+
#### fieldConfig.default · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L49)
|
|
1210
1179
|
|
|
1211
|
-
|
|
1180
|
+
Optional default value or function that generates default values.
|
|
1212
1181
|
|
|
1213
|
-
|
|
1182
|
+
**Type:** `T | ((model: Record<string, any>) => T)`
|
|
1214
1183
|
|
|
1215
|
-
|
|
1184
|
+
### TypeWrapper · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L33)
|
|
1216
1185
|
|
|
1217
1186
|
**Type Parameters:**
|
|
1218
1187
|
|
|
1219
|
-
- `
|
|
1220
|
-
- `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
|
|
1221
|
-
- `ARGS extends readonly any[] = IndexArgTypes<M, F>`
|
|
1188
|
+
- `T` - The TypeScript type this wrapper represents.
|
|
1222
1189
|
|
|
1223
|
-
|
|
1190
|
+
#### typeWrapper.kind · [abstract property](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L47)
|
|
1224
1191
|
|
|
1225
|
-
|
|
1226
|
-
- `_fieldNames`: - Array of field names that make up this index.
|
|
1192
|
+
A string identifier for this type, used during serialization
|
|
1227
1193
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
**Signature:** `(opts?: FindOptions<ARGS>) => IndexRangeIterator<M>`
|
|
1231
|
-
|
|
1232
|
-
**Parameters:**
|
|
1233
|
-
|
|
1234
|
-
- `opts: FindOptions<ARGS>` (optional)
|
|
1194
|
+
**Type:** `string`
|
|
1235
1195
|
|
|
1236
|
-
####
|
|
1196
|
+
#### typeWrapper.serialize · [abstract method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L52)
|
|
1237
1197
|
|
|
1238
|
-
|
|
1198
|
+
Serialize a value from an object property to a Pack.
|
|
1239
1199
|
|
|
1240
|
-
**Signature:** `(
|
|
1200
|
+
**Signature:** `(value: T, pack: DataPack) => void`
|
|
1241
1201
|
|
|
1242
1202
|
**Parameters:**
|
|
1243
1203
|
|
|
1244
|
-
- `
|
|
1245
|
-
- `
|
|
1246
|
-
|
|
1247
|
-
#### baseIndex.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
|
|
1248
|
-
|
|
1249
|
-
**Signature:** `() => string`
|
|
1250
|
-
|
|
1251
|
-
### UniqueIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
|
|
1252
|
-
|
|
1253
|
-
Unique index that stores references to the primary key.
|
|
1254
|
-
|
|
1255
|
-
**Type Parameters:**
|
|
1256
|
-
|
|
1257
|
-
- `M extends typeof Model` - The model class this index belongs to.
|
|
1258
|
-
- `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
|
|
1259
|
-
- `ARGS extends readonly any[] = IndexArgTypes<M, F>`
|
|
1204
|
+
- `value: T` - The value to serialize.
|
|
1205
|
+
- `pack: DataPack` - The Pack instance to write to.
|
|
1260
1206
|
|
|
1261
|
-
####
|
|
1207
|
+
#### typeWrapper.deserialize · [abstract method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L59)
|
|
1262
1208
|
|
|
1263
|
-
|
|
1209
|
+
Deserialize a value from a Pack into an object property.
|
|
1264
1210
|
|
|
1265
|
-
**Signature:** `(
|
|
1211
|
+
**Signature:** `(pack: DataPack) => T`
|
|
1266
1212
|
|
|
1267
1213
|
**Parameters:**
|
|
1268
1214
|
|
|
1269
|
-
- `
|
|
1215
|
+
- `pack: DataPack` - The Pack instance to read from.
|
|
1270
1216
|
|
|
1271
|
-
|
|
1217
|
+
#### typeWrapper.getError · [abstract method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L64)
|
|
1272
1218
|
|
|
1273
|
-
|
|
1219
|
+
Validate a value.
|
|
1274
1220
|
|
|
1275
|
-
|
|
1276
|
-
const userByEmail = User.byEmail.get("john@example.com");
|
|
1277
|
-
```
|
|
1278
|
-
|
|
1279
|
-
### PrimaryIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
|
|
1221
|
+
**Signature:** `(value: T) => void | DatabaseError`
|
|
1280
1222
|
|
|
1281
|
-
|
|
1223
|
+
**Parameters:**
|
|
1282
1224
|
|
|
1283
|
-
|
|
1225
|
+
- `value: T` - The value to validate.
|
|
1284
1226
|
|
|
1285
|
-
|
|
1286
|
-
- `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
|
|
1227
|
+
**Returns:** - A DatabaseError if validation fails.
|
|
1287
1228
|
|
|
1288
|
-
####
|
|
1229
|
+
#### typeWrapper.serializeType · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L68)
|
|
1289
1230
|
|
|
1290
|
-
|
|
1231
|
+
Serialize type metadata to a Pack (for schema serialization).
|
|
1291
1232
|
|
|
1292
|
-
**Signature:** `(
|
|
1233
|
+
**Signature:** `(pack: DataPack) => void`
|
|
1293
1234
|
|
|
1294
1235
|
**Parameters:**
|
|
1295
1236
|
|
|
1296
|
-
- `
|
|
1237
|
+
- `pack: DataPack` - The Pack instance to write to.
|
|
1297
1238
|
|
|
1298
|
-
|
|
1239
|
+
#### typeWrapper.containsNull · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L77)
|
|
1299
1240
|
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
```typescript
|
|
1303
|
-
const user = User.pk.get("john_doe");
|
|
1304
|
-
```
|
|
1305
|
-
|
|
1306
|
-
#### primaryIndex.getLazy · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
|
|
1307
|
-
|
|
1308
|
-
Does the same as as `get()`, but will delay loading the instance from disk until the first
|
|
1309
|
-
property access. In case it turns out the instance doesn't exist, an error will be thrown
|
|
1310
|
-
at that time.
|
|
1241
|
+
Check if indexing should be skipped for this field value.
|
|
1311
1242
|
|
|
1312
|
-
**Signature:** `(
|
|
1243
|
+
**Signature:** `(value: T) => boolean`
|
|
1313
1244
|
|
|
1314
1245
|
**Parameters:**
|
|
1315
1246
|
|
|
1316
|
-
- `
|
|
1247
|
+
- `value: T`
|
|
1317
1248
|
|
|
1318
|
-
**Returns:**
|
|
1249
|
+
**Returns:** true if indexing should be skipped.
|
|
1319
1250
|
|
|
1320
|
-
|
|
1251
|
+
#### typeWrapper.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L78)
|
|
1321
1252
|
|
|
1322
|
-
|
|
1253
|
+
**Signature:** `() => string`
|
|
1323
1254
|
|
|
1324
|
-
|
|
1255
|
+
#### typeWrapper.clone · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L81)
|
|
1325
1256
|
|
|
1326
|
-
|
|
1327
|
-
- `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
|
|
1328
|
-
- `ARGS extends readonly any[] = IndexArgTypes<M, F>`
|
|
1257
|
+
**Signature:** `(value: T) => T`
|
|
1329
1258
|
|
|
1330
|
-
|
|
1259
|
+
**Parameters:**
|
|
1331
1260
|
|
|
1332
|
-
|
|
1261
|
+
- `value: T`
|
|
1333
1262
|
|
|
1334
|
-
|
|
1263
|
+
#### typeWrapper.equals · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L85)
|
|
1335
1264
|
|
|
1336
|
-
|
|
1265
|
+
**Signature:** `(value1: T, value2: T) => boolean`
|
|
1337
1266
|
|
|
1338
|
-
**
|
|
1339
|
-
|
|
1340
|
-
#### transaction.instances · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L42)
|
|
1267
|
+
**Parameters:**
|
|
1341
1268
|
|
|
1342
|
-
|
|
1269
|
+
- `value1: T`
|
|
1270
|
+
- `value2: T`
|
|
1343
1271
|
|
|
1344
|
-
####
|
|
1272
|
+
#### typeWrapper.getLinkedModel · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L90)
|
|
1345
1273
|
|
|
1346
|
-
**
|
|
1274
|
+
**Signature:** `() => AnyModelClass`
|
|
1347
1275
|
|
|
1348
|
-
### DatabaseError · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1276
|
+
### DatabaseError · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L166)
|
|
1349
1277
|
|
|
1350
1278
|
The DatabaseError class is used to represent errors that occur during database operations.
|
|
1351
1279
|
It extends the built-in Error class and has a machine readable error code string property.
|
|
@@ -1355,7 +1283,7 @@ Invalid function arguments will throw TypeError.
|
|
|
1355
1283
|
|
|
1356
1284
|
**Value:** `DatabaseErrorConstructor`
|
|
1357
1285
|
|
|
1358
|
-
### runMigration · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1286
|
+
### runMigration · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L121)
|
|
1359
1287
|
|
|
1360
1288
|
Run database migration: populate secondary indexes for old-version rows,
|
|
1361
1289
|
convert old primary indices, rewrite row data, and clean up orphaned indices.
|
|
@@ -1366,7 +1294,7 @@ convert old primary indices, rewrite row data, and clean up orphaned indices.
|
|
|
1366
1294
|
|
|
1367
1295
|
- `options: MigrationOptions` (optional)
|
|
1368
1296
|
|
|
1369
|
-
### MigrationOptions · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1297
|
+
### MigrationOptions · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L13)
|
|
1370
1298
|
|
|
1371
1299
|
#### migrationOptions.tables · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L18)
|
|
1372
1300
|
|
|
@@ -1374,45 +1302,45 @@ Limit migration to specific table names.
|
|
|
1374
1302
|
|
|
1375
1303
|
**Type:** `string[]`
|
|
1376
1304
|
|
|
1377
|
-
#### migrationOptions.populateSecondaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1305
|
+
#### migrationOptions.populateSecondaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L22)
|
|
1378
1306
|
|
|
1379
1307
|
Populate secondary indexes for rows at old schema versions (default: true).
|
|
1380
1308
|
|
|
1381
1309
|
**Type:** `boolean`
|
|
1382
1310
|
|
|
1383
|
-
#### migrationOptions.migratePrimaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1311
|
+
#### migrationOptions.migratePrimaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L29)
|
|
1384
1312
|
|
|
1385
1313
|
Convert old primary indices when primary key fields changed (default: true).
|
|
1386
1314
|
|
|
1387
1315
|
**Type:** `boolean`
|
|
1388
1316
|
|
|
1389
|
-
#### migrationOptions.rewriteData · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1317
|
+
#### migrationOptions.rewriteData · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L38)
|
|
1390
1318
|
|
|
1391
1319
|
Rewrite all row data to the latest schema version (default: false).
|
|
1392
1320
|
|
|
1393
1321
|
**Type:** `boolean`
|
|
1394
1322
|
|
|
1395
|
-
#### migrationOptions.removeOrphans · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1323
|
+
#### migrationOptions.removeOrphans · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L45)
|
|
1396
1324
|
|
|
1397
1325
|
Delete orphaned secondary/unique index entries (default: true).
|
|
1398
1326
|
|
|
1399
1327
|
**Type:** `boolean`
|
|
1400
1328
|
|
|
1401
|
-
#### migrationOptions.onProgress · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1329
|
+
#### migrationOptions.onProgress · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L45)
|
|
1402
1330
|
|
|
1403
1331
|
Progress callback.
|
|
1404
1332
|
|
|
1405
1333
|
**Type:** `(info: ProgressInfo) => void`
|
|
1406
1334
|
|
|
1407
|
-
### MigrationResult · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1335
|
+
### MigrationResult · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L50)
|
|
1408
1336
|
|
|
1409
|
-
#### migrationResult.secondaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1337
|
+
#### migrationResult.secondaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L52)
|
|
1410
1338
|
|
|
1411
1339
|
Per-table counts of secondary index entries populated.
|
|
1412
1340
|
|
|
1413
1341
|
**Type:** `Record<string, number>`
|
|
1414
1342
|
|
|
1415
|
-
#### migrationResult.primaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1343
|
+
#### migrationResult.primaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L56)
|
|
1416
1344
|
|
|
1417
1345
|
Per-table counts of old primary rows migrated.
|
|
1418
1346
|
|
|
@@ -1424,15 +1352,29 @@ Per-table conversion failure counts by reason.
|
|
|
1424
1352
|
|
|
1425
1353
|
**Type:** `Record<string, Record<string, number>>`
|
|
1426
1354
|
|
|
1427
|
-
#### migrationResult.rewritten · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1355
|
+
#### migrationResult.rewritten · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L63)
|
|
1428
1356
|
|
|
1429
1357
|
Per-table counts of rows rewritten to latest version.
|
|
1430
1358
|
|
|
1431
1359
|
**Type:** `Record<string, number>`
|
|
1432
1360
|
|
|
1433
|
-
#### migrationResult.orphans · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
1361
|
+
#### migrationResult.orphans · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L67)
|
|
1434
1362
|
|
|
1435
1363
|
Number of orphaned index entries deleted.
|
|
1436
1364
|
|
|
1437
1365
|
**Type:** `number`
|
|
1438
1366
|
|
|
1367
|
+
### Transaction · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L54)
|
|
1368
|
+
|
|
1369
|
+
#### transaction.id · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L55)
|
|
1370
|
+
|
|
1371
|
+
**Type:** `number`
|
|
1372
|
+
|
|
1373
|
+
#### transaction.instances · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L56)
|
|
1374
|
+
|
|
1375
|
+
**Type:** `Map<number, ModelBase>`
|
|
1376
|
+
|
|
1377
|
+
### txnStorage · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L59)
|
|
1378
|
+
|
|
1379
|
+
**Value:** `AsyncLocalStorage<Transaction>`
|
|
1380
|
+
|