edinburgh 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +387 -217
- package/build/src/datapack.d.ts +22 -3
- package/build/src/datapack.js +105 -41
- package/build/src/datapack.js.map +1 -1
- package/build/src/edinburgh.d.ts +31 -13
- package/build/src/edinburgh.js +148 -62
- package/build/src/edinburgh.js.map +1 -1
- package/build/src/indexes.d.ts +78 -56
- package/build/src/indexes.js +519 -284
- package/build/src/indexes.js.map +1 -1
- package/build/src/migrate-cli.d.ts +20 -0
- package/build/src/migrate-cli.js +122 -0
- package/build/src/migrate-cli.js.map +1 -0
- package/build/src/migrate.d.ts +33 -0
- package/build/src/migrate.js +225 -0
- package/build/src/migrate.js.map +1 -0
- package/build/src/models.d.ts +130 -25
- package/build/src/models.js +271 -169
- package/build/src/models.js.map +1 -1
- package/build/src/types.d.ts +24 -7
- package/build/src/types.js +49 -15
- package/build/src/types.js.map +1 -1
- package/build/src/utils.d.ts +6 -10
- package/build/src/utils.js +26 -32
- package/build/src/utils.js.map +1 -1
- package/package.json +5 -4
- package/src/datapack.ts +117 -46
- package/src/edinburgh.ts +156 -64
- package/src/indexes.ts +550 -287
- package/src/migrate-cli.ts +138 -0
- package/src/migrate.ts +267 -0
- package/src/models.ts +352 -184
- package/src/types.ts +59 -16
- package/src/utils.ts +32 -32
package/README.md
CHANGED
|
@@ -1,63 +1,124 @@
|
|
|
1
1
|
# Edinburgh
|
|
2
|
-
**
|
|
2
|
+
**TypeScript objects that live in the database.**
|
|
3
3
|
|
|
4
|
-
Edinburgh
|
|
4
|
+
Edinburgh blurs the line between in-memory objects and database records. Define a model class, and its instances *are* the database rows. They are read directly from a memory-mapped [LMDB](http://www.lmdb.tech/doc/) store on first access, and mutations are written back in an ACID transaction on commit. There is no SQL layer, no query builder, no network round-trip, and no result-set marshalling. A primary-key lookup completes in about 1 µs.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
This makes problems like n+1 queries irrelevant: traversing `post.author.department.manager` is just a chain of microsecond memory-mapped reads, not a cascade of network calls.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
8
|
+
Built on [OLMDB](https://github.com/vanviegen/olmdb) (an optimistic-locking wrapper around LMDB).
|
|
9
|
+
|
|
10
|
+
- **Objects are records**: model fields are backed by memory-mapped storage; no serialization boundary between your code and the database
|
|
11
|
+
- **Sub-microsecond reads**: embedded B+ tree in the same process, no network hop, no query parsing
|
|
12
|
+
- **Type-safe at every layer**: TypeScript inference at compile time, runtime validation at write time
|
|
13
|
+
- **First-class relationships**: `E.link(OtherModel)` fields load lazily and transparently on access
|
|
14
|
+
- **Indexes**: primary, unique, and secondary indexes with efficient range queries
|
|
15
|
+
- **ACID transactions**: optimistic locking with automatic retry on conflict (up to 6 attempts)
|
|
16
|
+
- **Zero-downtime schema evolution**: old rows are lazily migrated on read; no batch DDL required
|
|
13
17
|
|
|
14
18
|
## Quick Demo
|
|
15
19
|
```typescript
|
|
16
20
|
import * as E from "edinburgh";
|
|
17
21
|
|
|
18
|
-
// Initialize the database (optional, defaults to "
|
|
22
|
+
// Initialize the database (optional, defaults to ".edinburgh")
|
|
19
23
|
E.init("./my-database");
|
|
20
24
|
|
|
21
25
|
// Define a model
|
|
22
26
|
@E.registerModel
|
|
23
27
|
class User extends E.Model<User> {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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.
|
|
34
|
+
id = E.field(E.identifier);
|
|
35
|
+
name = E.field(E.string);
|
|
36
|
+
age = E.field(E.number);
|
|
37
|
+
email = E.field(E.opt(E.string)); // TypeScript: undefined | string
|
|
38
|
+
|
|
39
|
+
// Link to another instance of this model
|
|
40
|
+
supervisor = E.field(E.opt(E.link(User)));
|
|
41
|
+
|
|
42
|
+
// A field with a more elaborate type. In TypeScript: `User | User[] | "unknown" | "whatever"`
|
|
43
|
+
something = E.field(E.or(E.link(User), E.array(E.link(User)), E.literal("unknown"), E.literal("whatever")), { default: "unknown" });
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
// Use in transactions
|
|
40
47
|
await E.transact(() => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
const boss = new User({
|
|
49
|
+
name: "Big Boss",
|
|
50
|
+
age: 50,
|
|
51
|
+
});
|
|
52
|
+
const john = new User({ // Unique 'id' is automatically generated if not provided
|
|
53
|
+
name: "John Doe",
|
|
54
|
+
age: 41,
|
|
55
|
+
email: "john@example.com",
|
|
56
|
+
supervisor: boss, // Link to another model instance
|
|
57
|
+
});
|
|
45
58
|
});
|
|
46
59
|
|
|
47
60
|
await E.transact(() => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
// Query by unique index
|
|
62
|
+
const john = User.byEmail.get("john@example.com")!;
|
|
63
|
+
|
|
64
|
+
// The transaction will retry if there's a conflict, such as another transaction
|
|
65
|
+
// modifying the same user (from another async function or another process)
|
|
66
|
+
john.age++;
|
|
67
|
+
|
|
68
|
+
// The supervisor object is lazy loaded on first access
|
|
69
|
+
console.log(`${john.supervisor!.name} is ${john.name}'s supervisor`);
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## TypeScript Configuration
|
|
74
|
+
|
|
75
|
+
When using TypeScript to transpile to JavaScript, make sure to enable the following options in your `tsconfig.json`:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"compilerOptions": {
|
|
80
|
+
"target": "es2022",
|
|
81
|
+
"experimentalDecorators": true
|
|
82
|
+
}
|
|
83
|
+
}
|
|
54
84
|
```
|
|
55
85
|
|
|
86
|
+
## Logging
|
|
87
|
+
|
|
88
|
+
You can enable debug logging to stdout by setting the `EDINBURGH_LOG_LEVEL` environment variable to a number from 0 to 3. Higher numbers produce more verbose logs, including model-level operations, updates, and reads.
|
|
89
|
+
|
|
90
|
+
- 0: no logging (default)
|
|
91
|
+
- 1: model-level logs
|
|
92
|
+
- 2: + update logs
|
|
93
|
+
- 3: + read logs
|
|
94
|
+
|
|
56
95
|
## API Reference
|
|
57
96
|
|
|
58
97
|
The following is auto-generated from `src/edinburgh.ts`:
|
|
59
98
|
|
|
60
|
-
###
|
|
99
|
+
### scheduleInit · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L6)
|
|
100
|
+
|
|
101
|
+
**Signature:** `() => void`
|
|
102
|
+
|
|
103
|
+
### init · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L65)
|
|
104
|
+
|
|
105
|
+
Initialize the database with the specified directory path.
|
|
106
|
+
This function may be called multiple times with the same parameters. If it is not called before the first transact(),
|
|
107
|
+
the database will be automatically initialized with the default directory.
|
|
108
|
+
|
|
109
|
+
**Signature:** `(dbDir: string) => void`
|
|
110
|
+
|
|
111
|
+
**Parameters:**
|
|
112
|
+
|
|
113
|
+
- `dbDir: string`
|
|
114
|
+
|
|
115
|
+
**Examples:**
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
init("./my-database");
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### transact · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L116)
|
|
61
122
|
|
|
62
123
|
Executes a function within a database transaction context.
|
|
63
124
|
|
|
@@ -66,7 +127,7 @@ within a transaction.
|
|
|
66
127
|
|
|
67
128
|
Transactions have a consistent view of the database, and changes made within a transaction are
|
|
68
129
|
isolated from other transactions until they are committed. In case a commit clashes with changes
|
|
69
|
-
made by another transaction, the transaction function will automatically be re-executed up to
|
|
130
|
+
made by another transaction, the transaction function will automatically be re-executed up to 6
|
|
70
131
|
times.
|
|
71
132
|
|
|
72
133
|
**Signature:** `<T>(fn: () => T) => Promise<T>`
|
|
@@ -77,15 +138,13 @@ times.
|
|
|
77
138
|
|
|
78
139
|
**Parameters:**
|
|
79
140
|
|
|
80
|
-
- `fn: () => T` - - The function to execute within the transaction context.
|
|
141
|
+
- `fn: () => T` - - The function to execute within the transaction context. Receives a Transaction instance.
|
|
81
142
|
|
|
82
143
|
**Returns:** A promise that resolves with the function's return value.
|
|
83
144
|
|
|
84
145
|
**Throws:**
|
|
85
146
|
|
|
86
|
-
- If nested transactions are attempted.
|
|
87
147
|
- With code "RACING_TRANSACTION" if the transaction fails after retries due to conflicts.
|
|
88
|
-
- With code "TRANSACTION_FAILED" if the transaction fails for other reasons.
|
|
89
148
|
- With code "TXN_LIMIT" if maximum number of transactions is reached.
|
|
90
149
|
- With code "LMDB-{code}" for LMDB-specific errors.
|
|
91
150
|
|
|
@@ -94,7 +153,6 @@ times.
|
|
|
94
153
|
```typescript
|
|
95
154
|
const paid = await E.transact(() => {
|
|
96
155
|
const user = User.pk.get("john_doe");
|
|
97
|
-
// This is concurrency-safe - the function will rerun if it is raced by another transaction
|
|
98
156
|
if (user.credits > 0) {
|
|
99
157
|
user.credits--;
|
|
100
158
|
return true;
|
|
@@ -105,32 +163,42 @@ const paid = await E.transact(() => {
|
|
|
105
163
|
```typescript
|
|
106
164
|
// Transaction with automatic retry on conflicts
|
|
107
165
|
await E.transact(() => {
|
|
108
|
-
const counter = Counter.
|
|
166
|
+
const counter = Counter.pk.get("global") || new Counter({id: "global", value: 0});
|
|
109
167
|
counter.value++;
|
|
110
168
|
});
|
|
111
169
|
```
|
|
112
170
|
|
|
113
|
-
###
|
|
171
|
+
### setMaxRetryCount · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L206)
|
|
172
|
+
|
|
173
|
+
Set the maximum number of retries for a transaction in case of conflicts.
|
|
174
|
+
The default value is 6. Setting it to 0 will disable retries and cause transactions to fail immediately on conflict.
|
|
175
|
+
|
|
176
|
+
**Signature:** `(count: number) => void`
|
|
177
|
+
|
|
178
|
+
**Parameters:**
|
|
179
|
+
|
|
180
|
+
- `count: number` - The maximum number of retries for a transaction.
|
|
181
|
+
|
|
182
|
+
### setOnSaveCallback · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L220)
|
|
114
183
|
|
|
115
184
|
Set a callback function to be called after a model is saved and committed.
|
|
116
185
|
|
|
117
|
-
**Signature:** `(callback: (commitId: number, items:
|
|
186
|
+
**Signature:** `(callback: (commitId: number, items: Map<Model<any>, Change>) => void) => void`
|
|
118
187
|
|
|
119
188
|
**Parameters:**
|
|
120
189
|
|
|
121
|
-
- `callback: ((commitId: number, items:
|
|
190
|
+
- `callback: ((commitId: number, items: Map<Model<any>, Change>) => void) | undefined` - The callback function to set. It gets called after each successful
|
|
191
|
+
`transact()` commit that has changes, with the following arguments:
|
|
192
|
+
- A sequential number. Higher numbers have been committed after lower numbers.
|
|
193
|
+
- A map of model instances to their changes. The change can be "created", "deleted", or an object containing the old values.
|
|
122
194
|
|
|
123
|
-
### deleteEverything · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
195
|
+
### deleteEverything · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L225)
|
|
124
196
|
|
|
125
197
|
**Signature:** `() => Promise<void>`
|
|
126
198
|
|
|
127
|
-
### Model · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
128
|
-
|
|
129
|
-
Base class for all database models in the Edinburgh ORM.
|
|
199
|
+
### Model · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L218)
|
|
130
200
|
|
|
131
|
-
|
|
132
|
-
change tracking, and relationship management. All model classes should extend
|
|
133
|
-
this base class and be decorated with `@registerModel`.
|
|
201
|
+
[object Object],[object Object],[object Object],[object Object],[object Object]
|
|
134
202
|
|
|
135
203
|
**Type Parameters:**
|
|
136
204
|
|
|
@@ -141,29 +209,70 @@ this base class and be decorated with `@registerModel`.
|
|
|
141
209
|
```typescript
|
|
142
210
|
@E.registerModel
|
|
143
211
|
class User extends E.Model<User> {
|
|
144
|
-
static pk = E.
|
|
212
|
+
static pk = E.primary(User, "id");
|
|
145
213
|
|
|
146
214
|
id = E.field(E.identifier);
|
|
147
215
|
name = E.field(E.string);
|
|
148
216
|
email = E.field(E.string);
|
|
149
217
|
|
|
150
|
-
static byEmail = E.
|
|
218
|
+
static byEmail = E.unique(User, "email");
|
|
151
219
|
}
|
|
152
220
|
```
|
|
153
221
|
|
|
154
|
-
#### Model.tableName · [static property](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
222
|
+
#### Model.tableName · [static property](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L226)
|
|
155
223
|
|
|
156
224
|
The database table name (defaults to class name).
|
|
157
225
|
|
|
158
226
|
**Type:** `string`
|
|
159
227
|
|
|
160
|
-
#### Model.
|
|
228
|
+
#### Model.override · [static property](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L230)
|
|
229
|
+
|
|
230
|
+
When true, registerModel replaces an existing model with the same tableName.
|
|
231
|
+
|
|
232
|
+
**Type:** `boolean`
|
|
233
|
+
|
|
234
|
+
#### Model.fields · [static property](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L231)
|
|
161
235
|
|
|
162
236
|
Field configuration metadata.
|
|
163
237
|
|
|
164
238
|
**Type:** `Record<string | number | symbol, FieldConfig<unknown>>`
|
|
165
239
|
|
|
166
|
-
#### Model.
|
|
240
|
+
#### Model.migrate · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
241
|
+
|
|
242
|
+
Optional migration function called when deserializing rows written with an older schema version.
|
|
243
|
+
Receives a plain record with all fields (primary key fields + value fields) and should mutate it
|
|
244
|
+
in-place to match the current schema.
|
|
245
|
+
|
|
246
|
+
This is called both during lazy loading (when a row is read from disk) and during batch
|
|
247
|
+
migration (via `runMigration()` / `npx migrate-edinburgh`). The function's source code is hashed
|
|
248
|
+
to detect changes. Modifying `migrate()` triggers a new schema version.
|
|
249
|
+
|
|
250
|
+
If `migrate()` changes values of fields used in secondary or unique indexes, those indexes
|
|
251
|
+
will only be updated when `runMigration()` is run (not during lazy loading).
|
|
252
|
+
|
|
253
|
+
**Signature:** `(record: Record<string, any>) => void`
|
|
254
|
+
|
|
255
|
+
**Parameters:**
|
|
256
|
+
|
|
257
|
+
- `record: Record<string, any>` - - A plain object with all field values from the old schema version.
|
|
258
|
+
|
|
259
|
+
**Examples:**
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
@E.registerModel
|
|
263
|
+
class User extends E.Model<User> {
|
|
264
|
+
static pk = E.primary(User, "id");
|
|
265
|
+
id = E.field(E.identifier);
|
|
266
|
+
name = E.field(E.string);
|
|
267
|
+
role = E.field(E.string); // new field
|
|
268
|
+
|
|
269
|
+
static migrate(record: Record<string, any>) {
|
|
270
|
+
record.role ??= "user"; // default for rows that predate the 'role' field
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
#### Model.findAll · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
167
276
|
|
|
168
277
|
Find all instances of this model in the database, ordered by primary key.
|
|
169
278
|
|
|
@@ -176,27 +285,74 @@ Find all instances of this model in the database, ordered by primary key.
|
|
|
176
285
|
|
|
177
286
|
**Returns:** An iterator.
|
|
178
287
|
|
|
179
|
-
####
|
|
288
|
+
#### Model.replaceInto · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
289
|
+
|
|
290
|
+
Load an existing instance by primary key and update it, or create a new one.
|
|
291
|
+
|
|
292
|
+
The provided object must contain all primary key fields. If a matching row exists,
|
|
293
|
+
the remaining properties from `obj` are set on the loaded instance. Otherwise a
|
|
294
|
+
new instance is created with `obj` as its initial properties.
|
|
295
|
+
|
|
296
|
+
**Signature:** `<T extends typeof Model<any>>(this: T, obj: Partial<Omit<InstanceType<T>, "constructor">>) => InstanceType<T>`
|
|
297
|
+
|
|
298
|
+
**Parameters:**
|
|
299
|
+
|
|
300
|
+
- `this: T`
|
|
301
|
+
- `obj: Partial<Omit<InstanceType<T>, "constructor">>` - - Partial model data that **must** include every primary key field.
|
|
302
|
+
|
|
303
|
+
**Returns:** The loaded-and-updated or newly created instance.
|
|
304
|
+
|
|
305
|
+
#### model.preCommit · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
180
306
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
been deleted. If its an object, the instance has been modified and the object contains the old values.
|
|
307
|
+
Optional hook called on each modified instance right before the transaction commits.
|
|
308
|
+
Runs before data is written to disk, so changes made here are included in the commit.
|
|
184
309
|
|
|
185
|
-
|
|
186
|
-
|
|
310
|
+
Common use cases:
|
|
311
|
+
- Computing derived or denormalized fields
|
|
312
|
+
- Enforcing cross-field validation rules
|
|
313
|
+
- Creating or updating related model instances (newly created instances will also
|
|
314
|
+
have their `preCommit()` called)
|
|
187
315
|
|
|
188
|
-
**
|
|
316
|
+
**Signature:** `() => void`
|
|
317
|
+
|
|
318
|
+
**Parameters:**
|
|
189
319
|
|
|
190
|
-
|
|
320
|
+
|
|
321
|
+
**Examples:**
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
@E.registerModel
|
|
325
|
+
class Post extends E.Model<Post> {
|
|
326
|
+
static pk = E.primary(Post, "id");
|
|
327
|
+
id = E.field(E.identifier);
|
|
328
|
+
title = E.field(E.string);
|
|
329
|
+
slug = E.field(E.string);
|
|
330
|
+
|
|
331
|
+
preCommit() {
|
|
332
|
+
this.slug = this.title.toLowerCase().replace(/\s+/g, "-");
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
#### model.getPrimaryKey · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
191
338
|
|
|
192
339
|
**Signature:** `() => Uint8Array<ArrayBufferLike>`
|
|
193
340
|
|
|
194
341
|
**Parameters:**
|
|
195
342
|
|
|
196
343
|
|
|
197
|
-
**Returns:** The primary key for this instance
|
|
344
|
+
**Returns:** The primary key for this instance.
|
|
345
|
+
|
|
346
|
+
#### model.getPrimaryKeyHash · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
347
|
+
|
|
348
|
+
**Signature:** `() => number`
|
|
349
|
+
|
|
350
|
+
**Parameters:**
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
**Returns:** A 53-bit positive integer non-cryptographic hash of the primary key, or undefined if not yet saved.
|
|
198
354
|
|
|
199
|
-
#### model.isLazyField · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
355
|
+
#### model.isLazyField · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
200
356
|
|
|
201
357
|
**Signature:** `(field: keyof this) => boolean`
|
|
202
358
|
|
|
@@ -204,7 +360,7 @@ detail that may change semantics at any minor release.
|
|
|
204
360
|
|
|
205
361
|
- `field: keyof this`
|
|
206
362
|
|
|
207
|
-
#### model.preventPersist · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
363
|
+
#### model.preventPersist · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
208
364
|
|
|
209
365
|
Prevent this instance from being persisted to the database.
|
|
210
366
|
|
|
@@ -223,7 +379,7 @@ user.name = "New Name";
|
|
|
223
379
|
user.preventPersist(); // Changes won't be saved
|
|
224
380
|
```
|
|
225
381
|
|
|
226
|
-
#### model.delete · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
382
|
+
#### model.delete · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
227
383
|
|
|
228
384
|
Delete this model instance from the database.
|
|
229
385
|
|
|
@@ -241,7 +397,7 @@ const user = User.load("user123");
|
|
|
241
397
|
user.delete(); // Removes from database
|
|
242
398
|
```
|
|
243
399
|
|
|
244
|
-
#### model.validate · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
400
|
+
#### model.validate · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
245
401
|
|
|
246
402
|
Validate all fields in this model instance.
|
|
247
403
|
|
|
@@ -263,7 +419,7 @@ if (errors.length > 0) {
|
|
|
263
419
|
}
|
|
264
420
|
```
|
|
265
421
|
|
|
266
|
-
#### model.isValid · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
422
|
+
#### model.isValid · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
267
423
|
|
|
268
424
|
Check if this model instance is valid.
|
|
269
425
|
|
|
@@ -281,7 +437,28 @@ const user = new User({name: "John"});
|
|
|
281
437
|
if (!user.isValid()) shoutAtTheUser();
|
|
282
438
|
```
|
|
283
439
|
|
|
284
|
-
|
|
440
|
+
#### model.getState · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
441
|
+
|
|
442
|
+
**Signature:** `() => "created" | "deleted" | "loaded" | "lazy"`
|
|
443
|
+
|
|
444
|
+
**Parameters:**
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
#### model.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
448
|
+
|
|
449
|
+
**Signature:** `() => string`
|
|
450
|
+
|
|
451
|
+
**Parameters:**
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
#### model.[Symbol.for('nodejs.util.inspect.custom')] · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
455
|
+
|
|
456
|
+
**Signature:** `() => string`
|
|
457
|
+
|
|
458
|
+
**Parameters:**
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
### registerModel · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L111)
|
|
285
462
|
|
|
286
463
|
Register a model class with the Edinburgh ORM system.
|
|
287
464
|
|
|
@@ -308,7 +485,7 @@ class User extends E.Model<User> {
|
|
|
308
485
|
}
|
|
309
486
|
```
|
|
310
487
|
|
|
311
|
-
### field · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
488
|
+
### field · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L87)
|
|
312
489
|
|
|
313
490
|
Create a field definition for a model property.
|
|
314
491
|
|
|
@@ -338,13 +515,13 @@ class User extends E.Model<User> {
|
|
|
338
515
|
}
|
|
339
516
|
```
|
|
340
517
|
|
|
341
|
-
### string · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
518
|
+
### string · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
342
519
|
|
|
343
520
|
Type wrapper instance for the string type.
|
|
344
521
|
|
|
345
522
|
**Value:** `TypeWrapper<string>`
|
|
346
523
|
|
|
347
|
-
### orderedString · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
524
|
+
### orderedString · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
348
525
|
|
|
349
526
|
Type wrapper instance for the ordered string type, which is just like a string
|
|
350
527
|
except that it sorts lexicographically in the database (instead of by incrementing
|
|
@@ -354,37 +531,37 @@ may not contain null characters.
|
|
|
354
531
|
|
|
355
532
|
**Value:** `TypeWrapper<string>`
|
|
356
533
|
|
|
357
|
-
### number · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
534
|
+
### number · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
358
535
|
|
|
359
536
|
Type wrapper instance for the number type.
|
|
360
537
|
|
|
361
538
|
**Value:** `TypeWrapper<number>`
|
|
362
539
|
|
|
363
|
-
### dateTime · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
540
|
+
### dateTime · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
364
541
|
|
|
365
542
|
Type wrapper instance for the date/time type.
|
|
366
543
|
|
|
367
544
|
**Value:** `TypeWrapper<Date>`
|
|
368
545
|
|
|
369
|
-
### boolean · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
546
|
+
### boolean · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
370
547
|
|
|
371
548
|
Type wrapper instance for the boolean type.
|
|
372
549
|
|
|
373
550
|
**Value:** `TypeWrapper<boolean>`
|
|
374
551
|
|
|
375
|
-
### identifier · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
552
|
+
### identifier · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
376
553
|
|
|
377
554
|
Type wrapper instance for the identifier type.
|
|
378
555
|
|
|
379
556
|
**Value:** `TypeWrapper<string>`
|
|
380
557
|
|
|
381
|
-
### undef · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
558
|
+
### undef · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
382
559
|
|
|
383
560
|
Type wrapper instance for the 'undefined' type.
|
|
384
561
|
|
|
385
562
|
**Value:** `TypeWrapper<undefined>`
|
|
386
563
|
|
|
387
|
-
### opt · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
564
|
+
### opt · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
388
565
|
|
|
389
566
|
Create an optional type wrapper (allows undefined).
|
|
390
567
|
|
|
@@ -407,7 +584,7 @@ const optionalString = E.opt(E.string);
|
|
|
407
584
|
const optionalNumber = E.opt(E.number);
|
|
408
585
|
```
|
|
409
586
|
|
|
410
|
-
### or · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
587
|
+
### or · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
411
588
|
|
|
412
589
|
Create a union type wrapper from multiple type choices.
|
|
413
590
|
|
|
@@ -430,7 +607,7 @@ const stringOrNumber = E.or(E.string, E.number);
|
|
|
430
607
|
const status = E.or("active", "inactive", "pending");
|
|
431
608
|
```
|
|
432
609
|
|
|
433
|
-
### array · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
610
|
+
### array · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
434
611
|
|
|
435
612
|
Create an array type wrapper with optional length constraints.
|
|
436
613
|
|
|
@@ -454,7 +631,7 @@ const stringArray = E.array(E.string);
|
|
|
454
631
|
const boundedArray = E.array(E.number, {min: 1, max: 10});
|
|
455
632
|
```
|
|
456
633
|
|
|
457
|
-
### literal · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
634
|
+
### literal · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
458
635
|
|
|
459
636
|
Create a literal type wrapper for a constant value.
|
|
460
637
|
|
|
@@ -477,7 +654,7 @@ const statusType = E.literal("active");
|
|
|
477
654
|
const countType = E.literal(42);
|
|
478
655
|
```
|
|
479
656
|
|
|
480
|
-
### link · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
657
|
+
### link · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
481
658
|
|
|
482
659
|
Create a link type wrapper for model relationships.
|
|
483
660
|
|
|
@@ -505,7 +682,7 @@ class Post extends E.Model<Post> {
|
|
|
505
682
|
}
|
|
506
683
|
```
|
|
507
684
|
|
|
508
|
-
### index · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
685
|
+
### index · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
509
686
|
|
|
510
687
|
Create a secondary index on model fields.
|
|
511
688
|
|
|
@@ -532,7 +709,7 @@ class User extends E.Model<User> {
|
|
|
532
709
|
}
|
|
533
710
|
```
|
|
534
711
|
|
|
535
|
-
### primary · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
712
|
+
### primary · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
536
713
|
|
|
537
714
|
Create a primary index on model fields.
|
|
538
715
|
|
|
@@ -559,7 +736,7 @@ class User extends E.Model<User> {
|
|
|
559
736
|
}
|
|
560
737
|
```
|
|
561
738
|
|
|
562
|
-
### unique · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
739
|
+
### unique · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
563
740
|
|
|
564
741
|
Create a unique index on model fields.
|
|
565
742
|
|
|
@@ -586,7 +763,7 @@ class User extends E.Model<User> {
|
|
|
586
763
|
}
|
|
587
764
|
```
|
|
588
765
|
|
|
589
|
-
### dump · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
766
|
+
### dump · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
590
767
|
|
|
591
768
|
Dump database contents for debugging.
|
|
592
769
|
|
|
@@ -595,18 +772,7 @@ This is primarily useful for development and debugging purposes.
|
|
|
595
772
|
|
|
596
773
|
**Signature:** `() => void`
|
|
597
774
|
|
|
598
|
-
###
|
|
599
|
-
|
|
600
|
-
Global log level for debugging output.
|
|
601
|
-
0 = no logging, 1 = model-level logs, 2 = update logs, 3 = read logs.
|
|
602
|
-
|
|
603
|
-
**Signature:** `(level: number) => void`
|
|
604
|
-
|
|
605
|
-
**Parameters:**
|
|
606
|
-
|
|
607
|
-
- `level: number`
|
|
608
|
-
|
|
609
|
-
### BaseIndex · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L129)
|
|
775
|
+
### BaseIndex · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L128)
|
|
610
776
|
|
|
611
777
|
Base class for database indexes for efficient lookups on model fields.
|
|
612
778
|
|
|
@@ -622,73 +788,33 @@ Indexes enable fast queries on specific field combinations and enforce uniquenes
|
|
|
622
788
|
- `MyModel`: - The model class this index belongs to.
|
|
623
789
|
- `_fieldNames`: - Array of field names that make up this index.
|
|
624
790
|
|
|
625
|
-
#### baseIndex.find · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
626
|
-
|
|
627
|
-
Find model instances using flexible range query options.
|
|
628
|
-
|
|
629
|
-
Supports exact matches, inclusive/exclusive range queries, and reverse iteration.
|
|
630
|
-
For single-field indexes, you can pass values directly or in arrays.
|
|
631
|
-
For multi-field indexes, pass arrays or partial arrays for prefix matching.
|
|
791
|
+
#### baseIndex.find · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
632
792
|
|
|
633
793
|
**Signature:** `(opts?: FindOptions<IndexArgTypes<M, F>>) => IndexRangeIterator<M>`
|
|
634
794
|
|
|
635
795
|
**Parameters:**
|
|
636
796
|
|
|
637
|
-
- `opts: FindOptions<IndexArgTypes<M, F>>` (optional)
|
|
638
|
-
|
|
639
|
-
**Returns:** An iterable of model instances matching the query
|
|
797
|
+
- `opts: FindOptions<IndexArgTypes<M, F>>` (optional)
|
|
640
798
|
|
|
641
|
-
|
|
799
|
+
#### baseIndex.batchProcess · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
642
800
|
|
|
643
|
-
|
|
644
|
-
// Exact match
|
|
645
|
-
for (const user of User.byEmail.find({is: "john@example.com"})) {
|
|
646
|
-
console.log(user.name);
|
|
647
|
-
}
|
|
801
|
+
[object Object],[object Object],[object Object]
|
|
648
802
|
|
|
649
|
-
|
|
650
|
-
for (const user of User.byEmail.find({from: "a@", to: "m@"})) {
|
|
651
|
-
console.log(user.email);
|
|
652
|
-
}
|
|
803
|
+
**Signature:** `(opts: FindOptions<IndexArgTypes<M, F>> & { limitSeconds?: number; limitRows?: number; }, callback: (row: InstanceType<M>) => void | Promise<...>) => Promise<...>`
|
|
653
804
|
|
|
654
|
-
|
|
655
|
-
for (const user of User.byEmail.find({after: "a@", before: "m@"})) {
|
|
656
|
-
console.log(user.email);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
// Open-ended ranges
|
|
660
|
-
for (const user of User.byEmail.find({from: "m@"})) { // m@ and later
|
|
661
|
-
console.log(user.email);
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
for (const user of User.byEmail.find({to: "m@"})) { // up to and including m@
|
|
665
|
-
console.log(user.email);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
// Reverse iteration
|
|
669
|
-
for (const user of User.byEmail.find({reverse: true})) {
|
|
670
|
-
console.log(user.email); // Z to A order
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
// Multi-field index prefix matching
|
|
674
|
-
for (const item of CompositeModel.pk.find({from: ["electronics", "phones"]})) {
|
|
675
|
-
console.log(item.name); // All electronics/phones items
|
|
676
|
-
}
|
|
805
|
+
**Parameters:**
|
|
677
806
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
console.log(user.name);
|
|
681
|
-
}
|
|
682
|
-
```
|
|
807
|
+
- `opts: FindOptions<IndexArgTypes<M, F>> & { limitSeconds?: number; limitRows?: number }` (optional) - - Query options (same as `find()`), plus:
|
|
808
|
+
- `callback: (row: InstanceType<M>) => void | Promise<void>` - - Called for each matching row within a transaction
|
|
683
809
|
|
|
684
|
-
#### baseIndex.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
810
|
+
#### baseIndex.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
685
811
|
|
|
686
812
|
**Signature:** `() => string`
|
|
687
813
|
|
|
688
814
|
**Parameters:**
|
|
689
815
|
|
|
690
816
|
|
|
691
|
-
### UniqueIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
817
|
+
### UniqueIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
692
818
|
|
|
693
819
|
Unique index that stores references to the primary key.
|
|
694
820
|
|
|
@@ -697,7 +823,7 @@ Unique index that stores references to the primary key.
|
|
|
697
823
|
- `M extends typeof Model` - The model class this index belongs to.
|
|
698
824
|
- `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
|
|
699
825
|
|
|
700
|
-
#### uniqueIndex.get · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
826
|
+
#### uniqueIndex.get · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
701
827
|
|
|
702
828
|
Get a model instance by unique index key values.
|
|
703
829
|
|
|
@@ -715,7 +841,7 @@ Get a model instance by unique index key values.
|
|
|
715
841
|
const userByEmail = User.byEmail.get("john@example.com");
|
|
716
842
|
```
|
|
717
843
|
|
|
718
|
-
### PrimaryIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
844
|
+
### PrimaryIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
719
845
|
|
|
720
846
|
Primary index that stores the actual model data.
|
|
721
847
|
|
|
@@ -724,15 +850,15 @@ Primary index that stores the actual model data.
|
|
|
724
850
|
- `M extends typeof Model` - The model class this index belongs to.
|
|
725
851
|
- `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
|
|
726
852
|
|
|
727
|
-
#### primaryIndex.get · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
853
|
+
#### primaryIndex.get · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
728
854
|
|
|
729
855
|
Get a model instance by primary key values.
|
|
730
856
|
|
|
731
|
-
**Signature:** `(...args: IndexArgTypes<M, F>
|
|
857
|
+
**Signature:** `(...args: IndexArgTypes<M, F>) => InstanceType<M>`
|
|
732
858
|
|
|
733
859
|
**Parameters:**
|
|
734
860
|
|
|
735
|
-
- `args: IndexArgTypes<M, F
|
|
861
|
+
- `args: IndexArgTypes<M, F>` - - The primary key values.
|
|
736
862
|
|
|
737
863
|
**Returns:** The model instance if found, undefined otherwise.
|
|
738
864
|
|
|
@@ -742,125 +868,169 @@ Get a model instance by primary key values.
|
|
|
742
868
|
const user = User.pk.get("john_doe");
|
|
743
869
|
```
|
|
744
870
|
|
|
745
|
-
#### primaryIndex.getLazy · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
871
|
+
#### primaryIndex.getLazy · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
746
872
|
|
|
747
873
|
Does the same as as `get()`, but will delay loading the instance from disk until the first
|
|
748
874
|
property access. In case it turns out the instance doesn't exist, an error will be thrown
|
|
749
875
|
at that time.
|
|
750
876
|
|
|
751
|
-
**Signature:** `(...args: IndexArgTypes<M, F>
|
|
877
|
+
**Signature:** `(...args: IndexArgTypes<M, F>) => InstanceType<M>`
|
|
752
878
|
|
|
753
879
|
**Parameters:**
|
|
754
880
|
|
|
755
|
-
- `args: IndexArgTypes<M, F
|
|
881
|
+
- `args: IndexArgTypes<M, F>` - Primary key field values. (Or a single Uint8Array containing the key.)
|
|
756
882
|
|
|
757
883
|
**Returns:** The (lazily loaded) model instance.
|
|
758
884
|
|
|
759
|
-
###
|
|
885
|
+
### SecondaryIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
760
886
|
|
|
761
|
-
|
|
762
|
-
This function may only be called once. If it is not called before the first transact(),
|
|
763
|
-
the database will be automatically initialized with the default directory.
|
|
887
|
+
Secondary index for non-unique lookups.
|
|
764
888
|
|
|
765
|
-
**
|
|
889
|
+
**Type Parameters:**
|
|
766
890
|
|
|
767
|
-
|
|
891
|
+
- `M extends typeof Model` - The model class this index belongs to.
|
|
892
|
+
- `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
|
|
768
893
|
|
|
769
|
-
|
|
894
|
+
### Change · [type](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L94)
|
|
770
895
|
|
|
771
|
-
**
|
|
896
|
+
**Type:** `Record<any, any> | "created" | "deleted"`
|
|
772
897
|
|
|
773
|
-
|
|
774
|
-
- With code "CREATE_DIR_FAILED" if directory creation fails.
|
|
775
|
-
- With code "LMDB-{code}" for LMDB-specific errors.
|
|
898
|
+
### Transaction · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L38)
|
|
776
899
|
|
|
777
|
-
|
|
900
|
+
#### transaction.id · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L41)
|
|
778
901
|
|
|
779
|
-
|
|
780
|
-
init("./my-database");
|
|
781
|
-
```
|
|
902
|
+
**Type:** `number`
|
|
782
903
|
|
|
783
|
-
|
|
904
|
+
#### transaction.instances · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L42)
|
|
784
905
|
|
|
785
|
-
|
|
786
|
-
The callback will be executed outside of transaction context.
|
|
906
|
+
**Type:** `Set<Model<unknown>>`
|
|
787
907
|
|
|
788
|
-
|
|
908
|
+
#### transaction.instancesByPk · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L44)
|
|
789
909
|
|
|
790
|
-
**
|
|
910
|
+
**Type:** `Map<number, Model<unknown>>`
|
|
791
911
|
|
|
792
|
-
|
|
912
|
+
### DatabaseError · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L156)
|
|
793
913
|
|
|
794
|
-
|
|
914
|
+
The DatabaseError class is used to represent errors that occur during database operations.
|
|
915
|
+
It extends the built-in Error class and has a machine readable error code string property.
|
|
795
916
|
|
|
796
|
-
|
|
917
|
+
The lowlevel API will throw DatabaseError instances for all database-related errors.
|
|
918
|
+
Invalid function arguments will throw TypeError.
|
|
797
919
|
|
|
798
|
-
|
|
920
|
+
**Value:** `DatabaseErrorConstructor`
|
|
921
|
+
|
|
922
|
+
### runMigration · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L116)
|
|
799
923
|
|
|
800
|
-
|
|
801
|
-
|
|
924
|
+
Run database migration: upgrade all rows to the latest schema version,
|
|
925
|
+
convert old primary indices, and clean up orphaned secondary indices.
|
|
802
926
|
|
|
803
|
-
**Signature:** `(
|
|
927
|
+
**Signature:** `(options?: MigrationOptions) => Promise<MigrationResult>`
|
|
804
928
|
|
|
805
929
|
**Parameters:**
|
|
806
930
|
|
|
807
|
-
- `
|
|
931
|
+
- `options: MigrationOptions` (optional)
|
|
808
932
|
|
|
809
|
-
|
|
933
|
+
### MigrationOptions · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L16)
|
|
810
934
|
|
|
811
|
-
|
|
935
|
+
#### migrationOptions.tables · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L18)
|
|
812
936
|
|
|
813
|
-
|
|
937
|
+
Limit migration to specific table names.
|
|
814
938
|
|
|
815
|
-
|
|
939
|
+
**Type:** `string[]`
|
|
816
940
|
|
|
817
|
-
|
|
941
|
+
#### migrationOptions.convertOldPrimaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L22)
|
|
818
942
|
|
|
819
|
-
|
|
943
|
+
Whether to convert old primary indices for known tables (default: true).
|
|
820
944
|
|
|
821
|
-
|
|
945
|
+
**Type:** `boolean`
|
|
822
946
|
|
|
823
|
-
|
|
947
|
+
#### migrationOptions.deleteOrphanedIndexes · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L30)
|
|
824
948
|
|
|
825
|
-
|
|
949
|
+
Whether to delete orphaned secondary/unique indices (default: true).
|
|
826
950
|
|
|
827
|
-
|
|
951
|
+
**Type:** `boolean`
|
|
828
952
|
|
|
829
|
-
|
|
953
|
+
#### migrationOptions.upgradeVersions · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L38)
|
|
830
954
|
|
|
831
|
-
|
|
832
|
-
attached to the currently running (async) task.
|
|
955
|
+
Whether to upgrade rows to the latest version (default: true).
|
|
833
956
|
|
|
834
|
-
**
|
|
957
|
+
**Type:** `boolean`
|
|
835
958
|
|
|
836
|
-
|
|
959
|
+
#### migrationOptions.onProgress · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L42)
|
|
837
960
|
|
|
838
|
-
|
|
839
|
-
- `value: any` - - The value to store.
|
|
961
|
+
Progress callback.
|
|
840
962
|
|
|
841
|
-
**
|
|
963
|
+
**Type:** `(info: ProgressInfo) => void`
|
|
842
964
|
|
|
843
|
-
|
|
965
|
+
### MigrationResult · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L47)
|
|
844
966
|
|
|
845
|
-
|
|
967
|
+
#### migrationResult.secondaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L49)
|
|
968
|
+
|
|
969
|
+
Per-table stats for row upgrades.
|
|
970
|
+
|
|
971
|
+
**Type:** `Record<string, number>`
|
|
972
|
+
|
|
973
|
+
#### migrationResult.primaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L50)
|
|
974
|
+
|
|
975
|
+
Per-table stats for old primary conversions.
|
|
976
|
+
|
|
977
|
+
**Type:** `Record<string, number>`
|
|
978
|
+
|
|
979
|
+
#### migrationResult.conversionFailures · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L56)
|
|
980
|
+
|
|
981
|
+
Per-table conversion failure counts by reason.
|
|
982
|
+
|
|
983
|
+
**Type:** `Record<string, Record<string, number>>`
|
|
984
|
+
|
|
985
|
+
#### migrationResult.orphaned · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L57)
|
|
986
|
+
|
|
987
|
+
Number of orphaned index entries deleted.
|
|
988
|
+
|
|
989
|
+
**Type:** `number`
|
|
990
|
+
|
|
991
|
+
## Schema Migrations
|
|
992
|
+
|
|
993
|
+
Edinburgh automatically tracks the schema version of each model. When you change fields, field types, indexes, or the `migrate()` function, Edinburgh detects a new schema version.
|
|
994
|
+
|
|
995
|
+
### What happens automatically (lazy migration)
|
|
996
|
+
|
|
997
|
+
Changes to regular (non-index) field values are migrated lazily. When a row with an old schema version is loaded from disk, it is deserialized using the old field types and transformed by the optional static `migrate()` function. This is transparent and requires no downtime.
|
|
846
998
|
|
|
847
999
|
```typescript
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
1000
|
+
@E.registerModel
|
|
1001
|
+
class User extends E.Model<User> {
|
|
1002
|
+
static pk = E.primary(User, "id");
|
|
1003
|
+
id = E.field(E.identifier);
|
|
1004
|
+
name = E.field(E.string);
|
|
1005
|
+
role = E.field(E.string); // newly added field
|
|
1006
|
+
|
|
1007
|
+
static migrate(record: Record<string, any>) {
|
|
1008
|
+
record.role ??= "user"; // provide a default for old rows
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
855
1011
|
```
|
|
856
1012
|
|
|
857
|
-
###
|
|
1013
|
+
### What requires `migrate-edinburgh`
|
|
858
1014
|
|
|
859
|
-
The
|
|
860
|
-
It extends the built-in Error class and has a machine readable error code string property.
|
|
1015
|
+
The `migrate-edinburgh` CLI tool (or the `runMigration()` API) must be run when:
|
|
861
1016
|
|
|
862
|
-
|
|
863
|
-
|
|
1017
|
+
- **Adding or removing** secondary or unique indexes
|
|
1018
|
+
- **Changing the fields or types** of an existing index
|
|
1019
|
+
- A **`migrate()` function changes values** that are used in index fields
|
|
864
1020
|
|
|
865
|
-
|
|
1021
|
+
The tool populates new indexes, removes orphaned ones, and updates index entries whose values were changed by `migrate()`. It does *not* rewrite primary data rows - lazy migration handles that on read.
|
|
1022
|
+
|
|
1023
|
+
```bash
|
|
1024
|
+
npx migrate-edinburgh --import ./src/models.ts
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
Run `npx migrate-edinburgh` to see all of its options.
|
|
1028
|
+
|
|
1029
|
+
You can also call `runMigration()` programmatically:
|
|
866
1030
|
|
|
1031
|
+
```typescript
|
|
1032
|
+
import { runMigration } from "edinburgh";
|
|
1033
|
+
|
|
1034
|
+
const result = await runMigration({ tables: ["User"] });
|
|
1035
|
+
console.log(result.upgraded); // { User: 1500 }
|
|
1036
|
+
```
|