edinburgh 0.1.3 → 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 +450 -218
- package/build/src/datapack.d.ts +138 -0
- package/build/src/datapack.js +684 -0
- package/build/src/datapack.js.map +1 -0
- package/build/src/edinburgh.d.ts +41 -11
- package/build/src/edinburgh.js +163 -43
- package/build/src/edinburgh.js.map +1 -1
- package/build/src/indexes.d.ts +100 -111
- package/build/src/indexes.js +679 -369
- 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 +147 -46
- package/build/src/models.js +322 -268
- package/build/src/models.js.map +1 -1
- package/build/src/types.d.ts +209 -260
- package/build/src/types.js +423 -324
- package/build/src/types.js.map +1 -1
- package/build/src/utils.d.ts +9 -9
- package/build/src/utils.js +32 -9
- package/build/src/utils.js.map +1 -1
- package/package.json +14 -11
- package/src/datapack.ts +726 -0
- package/src/edinburgh.ts +174 -43
- package/src/indexes.ts +722 -380
- package/src/migrate-cli.ts +138 -0
- package/src/migrate.ts +267 -0
- package/src/models.ts +415 -285
- package/src/types.ts +510 -391
- package/src/utils.ts +40 -12
- package/build/src/bytes.d.ts +0 -155
- package/build/src/bytes.js +0 -455
- package/build/src/bytes.js.map +0 -1
- package/src/bytes.ts +0 -500
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
|
|
|
@@ -93,8 +152,7 @@ times.
|
|
|
93
152
|
|
|
94
153
|
```typescript
|
|
95
154
|
const paid = await E.transact(() => {
|
|
96
|
-
const user = User.
|
|
97
|
-
// This is concurrency-safe - the function will rerun if it is raced by another transaction
|
|
155
|
+
const user = User.pk.get("john_doe");
|
|
98
156
|
if (user.credits > 0) {
|
|
99
157
|
user.credits--;
|
|
100
158
|
return true;
|
|
@@ -105,22 +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)
|
|
114
172
|
|
|
115
|
-
|
|
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`
|
|
116
177
|
|
|
117
|
-
|
|
178
|
+
**Parameters:**
|
|
118
179
|
|
|
119
|
-
|
|
180
|
+
- `count: number` - The maximum number of retries for a transaction.
|
|
120
181
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
182
|
+
### setOnSaveCallback · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L220)
|
|
183
|
+
|
|
184
|
+
Set a callback function to be called after a model is saved and committed.
|
|
185
|
+
|
|
186
|
+
**Signature:** `(callback: (commitId: number, items: Map<Model<any>, Change>) => void) => void`
|
|
187
|
+
|
|
188
|
+
**Parameters:**
|
|
189
|
+
|
|
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.
|
|
194
|
+
|
|
195
|
+
### deleteEverything · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L225)
|
|
196
|
+
|
|
197
|
+
**Signature:** `() => Promise<void>`
|
|
198
|
+
|
|
199
|
+
### Model · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L218)
|
|
200
|
+
|
|
201
|
+
[object Object],[object Object],[object Object],[object Object],[object Object]
|
|
124
202
|
|
|
125
203
|
**Type Parameters:**
|
|
126
204
|
|
|
@@ -131,54 +209,160 @@ this base class and be decorated with `@registerModel`.
|
|
|
131
209
|
```typescript
|
|
132
210
|
@E.registerModel
|
|
133
211
|
class User extends E.Model<User> {
|
|
134
|
-
static pk = E.
|
|
212
|
+
static pk = E.primary(User, "id");
|
|
135
213
|
|
|
136
214
|
id = E.field(E.identifier);
|
|
137
215
|
name = E.field(E.string);
|
|
138
216
|
email = E.field(E.string);
|
|
139
217
|
|
|
140
|
-
static byEmail = E.
|
|
218
|
+
static byEmail = E.unique(User, "email");
|
|
141
219
|
}
|
|
142
220
|
```
|
|
143
221
|
|
|
144
|
-
#### 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)
|
|
145
223
|
|
|
146
224
|
The database table name (defaults to class name).
|
|
147
225
|
|
|
148
226
|
**Type:** `string`
|
|
149
227
|
|
|
150
|
-
#### 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)
|
|
151
235
|
|
|
152
236
|
Field configuration metadata.
|
|
153
237
|
|
|
154
|
-
**Type:** `Record<string, FieldConfig<unknown>>`
|
|
238
|
+
**Type:** `Record<string | number | symbol, FieldConfig<unknown>>`
|
|
239
|
+
|
|
240
|
+
#### Model.migrate · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
155
241
|
|
|
156
|
-
|
|
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.
|
|
157
245
|
|
|
158
|
-
|
|
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.
|
|
159
249
|
|
|
160
|
-
|
|
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`
|
|
161
254
|
|
|
162
255
|
**Parameters:**
|
|
163
256
|
|
|
164
|
-
- `
|
|
165
|
-
|
|
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)
|
|
276
|
+
|
|
277
|
+
Find all instances of this model in the database, ordered by primary key.
|
|
278
|
+
|
|
279
|
+
**Signature:** `<T extends typeof Model<unknown>>(this: T, opts?: { reverse?: boolean; }) => IndexRangeIterator<T>`
|
|
280
|
+
|
|
281
|
+
**Parameters:**
|
|
282
|
+
|
|
283
|
+
- `this: T`
|
|
284
|
+
- `opts?: {reverse?: boolean}` - - Optional parameters.
|
|
285
|
+
|
|
286
|
+
**Returns:** An iterator.
|
|
287
|
+
|
|
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)
|
|
306
|
+
|
|
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.
|
|
309
|
+
|
|
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)
|
|
315
|
+
|
|
316
|
+
**Signature:** `() => void`
|
|
317
|
+
|
|
318
|
+
**Parameters:**
|
|
166
319
|
|
|
167
|
-
**Returns:** The model instance if found, undefined otherwise.
|
|
168
320
|
|
|
169
321
|
**Examples:**
|
|
170
322
|
|
|
171
323
|
```typescript
|
|
172
|
-
|
|
173
|
-
|
|
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
|
+
}
|
|
174
335
|
```
|
|
175
336
|
|
|
176
|
-
#### model.
|
|
337
|
+
#### model.getPrimaryKey · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
177
338
|
|
|
178
|
-
|
|
339
|
+
**Signature:** `() => Uint8Array<ArrayBufferLike>`
|
|
340
|
+
|
|
341
|
+
**Parameters:**
|
|
342
|
+
|
|
343
|
+
|
|
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.
|
|
179
354
|
|
|
180
|
-
|
|
181
|
-
|
|
355
|
+
#### model.isLazyField · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
356
|
+
|
|
357
|
+
**Signature:** `(field: keyof this) => boolean`
|
|
358
|
+
|
|
359
|
+
**Parameters:**
|
|
360
|
+
|
|
361
|
+
- `field: keyof this`
|
|
362
|
+
|
|
363
|
+
#### model.preventPersist · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
364
|
+
|
|
365
|
+
Prevent this instance from being persisted to the database.
|
|
182
366
|
|
|
183
367
|
**Signature:** `() => this`
|
|
184
368
|
|
|
@@ -195,7 +379,7 @@ user.name = "New Name";
|
|
|
195
379
|
user.preventPersist(); // Changes won't be saved
|
|
196
380
|
```
|
|
197
381
|
|
|
198
|
-
#### 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)
|
|
199
383
|
|
|
200
384
|
Delete this model instance from the database.
|
|
201
385
|
|
|
@@ -213,11 +397,11 @@ const user = User.load("user123");
|
|
|
213
397
|
user.delete(); // Removes from database
|
|
214
398
|
```
|
|
215
399
|
|
|
216
|
-
#### 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)
|
|
217
401
|
|
|
218
402
|
Validate all fields in this model instance.
|
|
219
403
|
|
|
220
|
-
**Signature:** `(raise?: boolean) =>
|
|
404
|
+
**Signature:** `(raise?: boolean) => Error[]`
|
|
221
405
|
|
|
222
406
|
**Parameters:**
|
|
223
407
|
|
|
@@ -235,7 +419,7 @@ if (errors.length > 0) {
|
|
|
235
419
|
}
|
|
236
420
|
```
|
|
237
421
|
|
|
238
|
-
#### 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)
|
|
239
423
|
|
|
240
424
|
Check if this model instance is valid.
|
|
241
425
|
|
|
@@ -253,13 +437,30 @@ const user = new User({name: "John"});
|
|
|
253
437
|
if (!user.isValid()) shoutAtTheUser();
|
|
254
438
|
```
|
|
255
439
|
|
|
256
|
-
|
|
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:**
|
|
257
459
|
|
|
258
|
-
Register a model class with the Edinburgh ORM system.
|
|
259
460
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
461
|
+
### registerModel · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L111)
|
|
462
|
+
|
|
463
|
+
Register a model class with the Edinburgh ORM system.
|
|
263
464
|
|
|
264
465
|
**Signature:** `<T extends typeof Model<unknown>>(MyModel: T) => T`
|
|
265
466
|
|
|
@@ -284,7 +485,7 @@ class User extends E.Model<User> {
|
|
|
284
485
|
}
|
|
285
486
|
```
|
|
286
487
|
|
|
287
|
-
### 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)
|
|
288
489
|
|
|
289
490
|
Create a field definition for a model property.
|
|
290
491
|
|
|
@@ -314,51 +515,57 @@ class User extends E.Model<User> {
|
|
|
314
515
|
}
|
|
315
516
|
```
|
|
316
517
|
|
|
317
|
-
###
|
|
518
|
+
### string · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
318
519
|
|
|
319
|
-
|
|
520
|
+
Type wrapper instance for the string type.
|
|
320
521
|
|
|
321
|
-
**
|
|
522
|
+
**Value:** `TypeWrapper<string>`
|
|
322
523
|
|
|
323
|
-
|
|
524
|
+
### orderedString · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
324
525
|
|
|
325
|
-
|
|
526
|
+
Type wrapper instance for the ordered string type, which is just like a string
|
|
527
|
+
except that it sorts lexicographically in the database (instead of by incrementing
|
|
528
|
+
length first), making it suitable for index fields that want lexicographic range
|
|
529
|
+
scans. Ordered strings are implemented as null-terminated UTF-8 strings, so they
|
|
530
|
+
may not contain null characters.
|
|
326
531
|
|
|
327
|
-
|
|
532
|
+
**Value:** `TypeWrapper<string>`
|
|
328
533
|
|
|
329
|
-
|
|
534
|
+
### number · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
330
535
|
|
|
331
|
-
|
|
536
|
+
Type wrapper instance for the number type.
|
|
332
537
|
|
|
333
|
-
|
|
538
|
+
**Value:** `TypeWrapper<number>`
|
|
334
539
|
|
|
335
|
-
|
|
540
|
+
### dateTime · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
336
541
|
|
|
337
|
-
|
|
542
|
+
Type wrapper instance for the date/time type.
|
|
338
543
|
|
|
339
|
-
|
|
544
|
+
**Value:** `TypeWrapper<Date>`
|
|
340
545
|
|
|
341
|
-
|
|
546
|
+
### boolean · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
342
547
|
|
|
343
|
-
|
|
548
|
+
Type wrapper instance for the boolean type.
|
|
344
549
|
|
|
345
|
-
|
|
550
|
+
**Value:** `TypeWrapper<boolean>`
|
|
346
551
|
|
|
347
|
-
|
|
552
|
+
### identifier · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
348
553
|
|
|
349
|
-
|
|
554
|
+
Type wrapper instance for the identifier type.
|
|
350
555
|
|
|
351
|
-
|
|
556
|
+
**Value:** `TypeWrapper<string>`
|
|
352
557
|
|
|
353
|
-
|
|
558
|
+
### undef · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
354
559
|
|
|
355
|
-
|
|
560
|
+
Type wrapper instance for the 'undefined' type.
|
|
356
561
|
|
|
357
|
-
|
|
562
|
+
**Value:** `TypeWrapper<undefined>`
|
|
563
|
+
|
|
564
|
+
### opt · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
358
565
|
|
|
359
566
|
Create an optional type wrapper (allows undefined).
|
|
360
567
|
|
|
361
|
-
**Signature:** `<const T extends TypeWrapper<unknown> | BasicType>(inner: T) =>
|
|
568
|
+
**Signature:** `<const T extends TypeWrapper<unknown> | BasicType>(inner: T) => TypeWrapper<T extends TypeWrapper<infer U> ? U : T>`
|
|
362
569
|
|
|
363
570
|
**Type Parameters:**
|
|
364
571
|
|
|
@@ -377,11 +584,11 @@ const optionalString = E.opt(E.string);
|
|
|
377
584
|
const optionalNumber = E.opt(E.number);
|
|
378
585
|
```
|
|
379
586
|
|
|
380
|
-
### 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)
|
|
381
588
|
|
|
382
589
|
Create a union type wrapper from multiple type choices.
|
|
383
590
|
|
|
384
|
-
**Signature:** `<const T extends (TypeWrapper<unknown> | BasicType)[]>(...choices: T) =>
|
|
591
|
+
**Signature:** `<const T extends (TypeWrapper<unknown> | BasicType)[]>(...choices: T) => TypeWrapper<UnwrapTypes<T>>`
|
|
385
592
|
|
|
386
593
|
**Type Parameters:**
|
|
387
594
|
|
|
@@ -400,11 +607,11 @@ const stringOrNumber = E.or(E.string, E.number);
|
|
|
400
607
|
const status = E.or("active", "inactive", "pending");
|
|
401
608
|
```
|
|
402
609
|
|
|
403
|
-
### 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)
|
|
404
611
|
|
|
405
612
|
Create an array type wrapper with optional length constraints.
|
|
406
613
|
|
|
407
|
-
**Signature:** `<const T>(inner: TypeWrapper<T>, opts?: { min?: number; max?: number; }) =>
|
|
614
|
+
**Signature:** `<const T>(inner: TypeWrapper<T>, opts?: { min?: number; max?: number; }) => TypeWrapper<T[]>`
|
|
408
615
|
|
|
409
616
|
**Type Parameters:**
|
|
410
617
|
|
|
@@ -424,11 +631,11 @@ const stringArray = E.array(E.string);
|
|
|
424
631
|
const boundedArray = E.array(E.number, {min: 1, max: 10});
|
|
425
632
|
```
|
|
426
633
|
|
|
427
|
-
### 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)
|
|
428
635
|
|
|
429
636
|
Create a literal type wrapper for a constant value.
|
|
430
637
|
|
|
431
|
-
**Signature:** `<const T>(value: T) =>
|
|
638
|
+
**Signature:** `<const T>(value: T) => TypeWrapper<T>`
|
|
432
639
|
|
|
433
640
|
**Type Parameters:**
|
|
434
641
|
|
|
@@ -447,11 +654,11 @@ const statusType = E.literal("active");
|
|
|
447
654
|
const countType = E.literal(42);
|
|
448
655
|
```
|
|
449
656
|
|
|
450
|
-
### 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)
|
|
451
658
|
|
|
452
659
|
Create a link type wrapper for model relationships.
|
|
453
660
|
|
|
454
|
-
**Signature:** `<const T extends typeof Model<any>>(TargetModel: T) =>
|
|
661
|
+
**Signature:** `<const T extends typeof Model<any>>(TargetModel: T) => TypeWrapper<InstanceType<T>>`
|
|
455
662
|
|
|
456
663
|
**Type Parameters:**
|
|
457
664
|
|
|
@@ -475,7 +682,7 @@ class Post extends E.Model<Post> {
|
|
|
475
682
|
}
|
|
476
683
|
```
|
|
477
684
|
|
|
478
|
-
### 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)
|
|
479
686
|
|
|
480
687
|
Create a secondary index on model fields.
|
|
481
688
|
|
|
@@ -502,7 +709,7 @@ class User extends E.Model<User> {
|
|
|
502
709
|
}
|
|
503
710
|
```
|
|
504
711
|
|
|
505
|
-
### 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)
|
|
506
713
|
|
|
507
714
|
Create a primary index on model fields.
|
|
508
715
|
|
|
@@ -529,7 +736,7 @@ class User extends E.Model<User> {
|
|
|
529
736
|
}
|
|
530
737
|
```
|
|
531
738
|
|
|
532
|
-
### 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)
|
|
533
740
|
|
|
534
741
|
Create a unique index on model fields.
|
|
535
742
|
|
|
@@ -556,7 +763,7 @@ class User extends E.Model<User> {
|
|
|
556
763
|
}
|
|
557
764
|
```
|
|
558
765
|
|
|
559
|
-
### 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)
|
|
560
767
|
|
|
561
768
|
Dump database contents for debugging.
|
|
562
769
|
|
|
@@ -565,7 +772,7 @@ This is primarily useful for development and debugging purposes.
|
|
|
565
772
|
|
|
566
773
|
**Signature:** `() => void`
|
|
567
774
|
|
|
568
|
-
### BaseIndex · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
775
|
+
### BaseIndex · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L128)
|
|
569
776
|
|
|
570
777
|
Base class for database indexes for efficient lookups on model fields.
|
|
571
778
|
|
|
@@ -581,66 +788,33 @@ Indexes enable fast queries on specific field combinations and enforce uniquenes
|
|
|
581
788
|
- `MyModel`: - The model class this index belongs to.
|
|
582
789
|
- `_fieldNames`: - Array of field names that make up this index.
|
|
583
790
|
|
|
584
|
-
#### baseIndex.find · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#
|
|
585
|
-
|
|
586
|
-
Find model instances using flexible range query options.
|
|
587
|
-
|
|
588
|
-
Supports exact matches, inclusive/exclusive range queries, and reverse iteration.
|
|
589
|
-
For single-field indexes, you can pass values directly or in arrays.
|
|
590
|
-
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)
|
|
591
792
|
|
|
592
|
-
**Signature:** `(opts?: FindOptions<IndexArgTypes<M, F>>) => IndexRangeIterator<M
|
|
793
|
+
**Signature:** `(opts?: FindOptions<IndexArgTypes<M, F>>) => IndexRangeIterator<M>`
|
|
593
794
|
|
|
594
795
|
**Parameters:**
|
|
595
796
|
|
|
596
|
-
- `opts: FindOptions<IndexArgTypes<M, F>>` (optional)
|
|
797
|
+
- `opts: FindOptions<IndexArgTypes<M, F>>` (optional)
|
|
597
798
|
|
|
598
|
-
|
|
799
|
+
#### baseIndex.batchProcess · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
599
800
|
|
|
600
|
-
|
|
801
|
+
[object Object],[object Object],[object Object]
|
|
601
802
|
|
|
602
|
-
|
|
603
|
-
// Exact match
|
|
604
|
-
for (const user of User.byEmail.find({is: "john@example.com"})) {
|
|
605
|
-
console.log(user.name);
|
|
606
|
-
}
|
|
803
|
+
**Signature:** `(opts: FindOptions<IndexArgTypes<M, F>> & { limitSeconds?: number; limitRows?: number; }, callback: (row: InstanceType<M>) => void | Promise<...>) => Promise<...>`
|
|
607
804
|
|
|
608
|
-
|
|
609
|
-
for (const user of User.byEmail.find({from: "a@", to: "m@"})) {
|
|
610
|
-
console.log(user.email);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
// Range query (exclusive)
|
|
614
|
-
for (const user of User.byEmail.find({after: "a@", before: "m@"})) {
|
|
615
|
-
console.log(user.email);
|
|
616
|
-
}
|
|
805
|
+
**Parameters:**
|
|
617
806
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
console.log(user.email);
|
|
621
|
-
}
|
|
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
|
|
622
809
|
|
|
623
|
-
|
|
624
|
-
console.log(user.email);
|
|
625
|
-
}
|
|
810
|
+
#### baseIndex.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
626
811
|
|
|
627
|
-
|
|
628
|
-
for (const user of User.byEmail.find({reverse: true})) {
|
|
629
|
-
console.log(user.email); // Z to A order
|
|
630
|
-
}
|
|
812
|
+
**Signature:** `() => string`
|
|
631
813
|
|
|
632
|
-
|
|
633
|
-
for (const item of CompositeModel.pk.find({from: ["electronics", "phones"]})) {
|
|
634
|
-
console.log(item.name); // All electronics/phones items
|
|
635
|
-
}
|
|
814
|
+
**Parameters:**
|
|
636
815
|
|
|
637
|
-
// For single-field indexes, you can use the value directly
|
|
638
|
-
for (const user of User.byEmail.find({is: "john@example.com"})) {
|
|
639
|
-
console.log(user.name);
|
|
640
|
-
}
|
|
641
|
-
```
|
|
642
816
|
|
|
643
|
-
### 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)
|
|
644
818
|
|
|
645
819
|
Unique index that stores references to the primary key.
|
|
646
820
|
|
|
@@ -649,7 +823,7 @@ Unique index that stores references to the primary key.
|
|
|
649
823
|
- `M extends typeof Model` - The model class this index belongs to.
|
|
650
824
|
- `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
|
|
651
825
|
|
|
652
|
-
#### 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)
|
|
653
827
|
|
|
654
828
|
Get a model instance by unique index key values.
|
|
655
829
|
|
|
@@ -667,7 +841,7 @@ Get a model instance by unique index key values.
|
|
|
667
841
|
const userByEmail = User.byEmail.get("john@example.com");
|
|
668
842
|
```
|
|
669
843
|
|
|
670
|
-
### 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)
|
|
671
845
|
|
|
672
846
|
Primary index that stores the actual model data.
|
|
673
847
|
|
|
@@ -676,7 +850,7 @@ Primary index that stores the actual model data.
|
|
|
676
850
|
- `M extends typeof Model` - The model class this index belongs to.
|
|
677
851
|
- `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
|
|
678
852
|
|
|
679
|
-
#### 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)
|
|
680
854
|
|
|
681
855
|
Get a model instance by primary key values.
|
|
682
856
|
|
|
@@ -694,111 +868,169 @@ Get a model instance by primary key values.
|
|
|
694
868
|
const user = User.pk.get("john_doe");
|
|
695
869
|
```
|
|
696
870
|
|
|
697
|
-
|
|
871
|
+
#### primaryIndex.getLazy · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
698
872
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
873
|
+
Does the same as as `get()`, but will delay loading the instance from disk until the first
|
|
874
|
+
property access. In case it turns out the instance doesn't exist, an error will be thrown
|
|
875
|
+
at that time.
|
|
702
876
|
|
|
703
|
-
**Signature:** `(
|
|
877
|
+
**Signature:** `(...args: IndexArgTypes<M, F>) => InstanceType<M>`
|
|
704
878
|
|
|
705
879
|
**Parameters:**
|
|
706
880
|
|
|
707
|
-
- `
|
|
881
|
+
- `args: IndexArgTypes<M, F>` - Primary key field values. (Or a single Uint8Array containing the key.)
|
|
708
882
|
|
|
709
|
-
**
|
|
883
|
+
**Returns:** The (lazily loaded) model instance.
|
|
710
884
|
|
|
711
|
-
|
|
712
|
-
- With code "CREATE_DIR_FAILED" if directory creation fails.
|
|
713
|
-
- With code "LMDB-{code}" for LMDB-specific errors.
|
|
885
|
+
### SecondaryIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L251)
|
|
714
886
|
|
|
715
|
-
|
|
887
|
+
Secondary index for non-unique lookups.
|
|
716
888
|
|
|
717
|
-
|
|
718
|
-
init("./my-database");
|
|
719
|
-
```
|
|
889
|
+
**Type Parameters:**
|
|
720
890
|
|
|
721
|
-
|
|
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.
|
|
722
893
|
|
|
723
|
-
|
|
724
|
-
The callback will be executed outside of transaction context.
|
|
894
|
+
### Change · [type](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L94)
|
|
725
895
|
|
|
726
|
-
**
|
|
896
|
+
**Type:** `Record<any, any> | "created" | "deleted"`
|
|
727
897
|
|
|
728
|
-
|
|
898
|
+
### Transaction · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L38)
|
|
729
899
|
|
|
730
|
-
|
|
900
|
+
#### transaction.id · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L41)
|
|
731
901
|
|
|
732
|
-
**
|
|
902
|
+
**Type:** `number`
|
|
733
903
|
|
|
734
|
-
|
|
904
|
+
#### transaction.instances · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L42)
|
|
735
905
|
|
|
736
|
-
|
|
906
|
+
**Type:** `Set<Model<unknown>>`
|
|
737
907
|
|
|
738
|
-
|
|
739
|
-
The callback will be executed outside of transaction context.
|
|
908
|
+
#### transaction.instancesByPk · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L44)
|
|
740
909
|
|
|
741
|
-
**
|
|
910
|
+
**Type:** `Map<number, Model<unknown>>`
|
|
742
911
|
|
|
743
|
-
|
|
912
|
+
### DatabaseError · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L156)
|
|
744
913
|
|
|
745
|
-
|
|
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.
|
|
746
916
|
|
|
747
|
-
|
|
917
|
+
The lowlevel API will throw DatabaseError instances for all database-related errors.
|
|
918
|
+
Invalid function arguments will throw TypeError.
|
|
748
919
|
|
|
749
|
-
|
|
920
|
+
**Value:** `DatabaseErrorConstructor`
|
|
750
921
|
|
|
751
|
-
###
|
|
922
|
+
### runMigration · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L116)
|
|
752
923
|
|
|
753
|
-
|
|
924
|
+
Run database migration: upgrade all rows to the latest schema version,
|
|
925
|
+
convert old primary indices, and clean up orphaned secondary indices.
|
|
754
926
|
|
|
755
|
-
**Signature:** `(
|
|
927
|
+
**Signature:** `(options?: MigrationOptions) => Promise<MigrationResult>`
|
|
756
928
|
|
|
757
929
|
**Parameters:**
|
|
758
930
|
|
|
759
|
-
- `
|
|
931
|
+
- `options: MigrationOptions` (optional)
|
|
760
932
|
|
|
761
|
-
|
|
933
|
+
### MigrationOptions · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L16)
|
|
762
934
|
|
|
763
|
-
|
|
935
|
+
#### migrationOptions.tables · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L18)
|
|
764
936
|
|
|
765
|
-
|
|
937
|
+
Limit migration to specific table names.
|
|
766
938
|
|
|
767
|
-
|
|
939
|
+
**Type:** `string[]`
|
|
768
940
|
|
|
769
|
-
|
|
770
|
-
attached to the currently running (async) task.
|
|
941
|
+
#### migrationOptions.convertOldPrimaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L22)
|
|
771
942
|
|
|
772
|
-
|
|
943
|
+
Whether to convert old primary indices for known tables (default: true).
|
|
773
944
|
|
|
774
|
-
**
|
|
945
|
+
**Type:** `boolean`
|
|
775
946
|
|
|
776
|
-
|
|
777
|
-
- `value: any` - - The value to store.
|
|
947
|
+
#### migrationOptions.deleteOrphanedIndexes · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L30)
|
|
778
948
|
|
|
779
|
-
|
|
949
|
+
Whether to delete orphaned secondary/unique indices (default: true).
|
|
780
950
|
|
|
781
|
-
|
|
951
|
+
**Type:** `boolean`
|
|
782
952
|
|
|
783
|
-
|
|
953
|
+
#### migrationOptions.upgradeVersions · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L38)
|
|
954
|
+
|
|
955
|
+
Whether to upgrade rows to the latest version (default: true).
|
|
956
|
+
|
|
957
|
+
**Type:** `boolean`
|
|
958
|
+
|
|
959
|
+
#### migrationOptions.onProgress · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L42)
|
|
960
|
+
|
|
961
|
+
Progress callback.
|
|
962
|
+
|
|
963
|
+
**Type:** `(info: ProgressInfo) => void`
|
|
964
|
+
|
|
965
|
+
### MigrationResult · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L47)
|
|
966
|
+
|
|
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.
|
|
784
998
|
|
|
785
999
|
```typescript
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
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
|
+
}
|
|
793
1011
|
```
|
|
794
1012
|
|
|
795
|
-
###
|
|
1013
|
+
### What requires `migrate-edinburgh`
|
|
796
1014
|
|
|
797
|
-
The
|
|
798
|
-
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:
|
|
799
1016
|
|
|
800
|
-
|
|
801
|
-
|
|
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
|
|
802
1020
|
|
|
803
|
-
|
|
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.
|
|
804
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:
|
|
1030
|
+
|
|
1031
|
+
```typescript
|
|
1032
|
+
import { runMigration } from "edinburgh";
|
|
1033
|
+
|
|
1034
|
+
const result = await runMigration({ tables: ["User"] });
|
|
1035
|
+
console.log(result.upgraded); // { User: 1500 }
|
|
1036
|
+
```
|