edinburgh 0.4.2 → 0.4.5
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 +251 -170
- package/build/src/datapack.d.ts +17 -1
- package/build/src/datapack.js +44 -5
- package/build/src/datapack.js.map +1 -1
- package/build/src/edinburgh.d.ts +1 -1
- package/build/src/edinburgh.js +1 -1
- package/build/src/edinburgh.js.map +1 -1
- package/build/src/indexes.d.ts +34 -17
- package/build/src/indexes.js +126 -55
- package/build/src/indexes.js.map +1 -1
- package/build/src/migrate-cli.d.ts +1 -16
- package/build/src/migrate-cli.js +56 -42
- package/build/src/migrate-cli.js.map +1 -1
- package/build/src/migrate.d.ts +15 -11
- package/build/src/migrate.js +62 -32
- package/build/src/migrate.js.map +1 -1
- package/build/src/models.d.ts +1 -1
- package/build/src/models.js +20 -7
- package/build/src/models.js.map +1 -1
- package/build/src/types.d.ts +13 -1
- package/build/src/types.js +89 -1
- package/build/src/types.js.map +1 -1
- package/build/src/utils.d.ts +2 -0
- package/build/src/utils.js +12 -0
- package/build/src/utils.js.map +1 -1
- package/package.json +7 -4
- package/skill/BaseIndex.md +16 -0
- package/skill/BaseIndex_batchProcess.md +10 -0
- package/skill/BaseIndex_find.md +7 -0
- package/skill/DatabaseError.md +9 -0
- package/skill/Model.md +22 -0
- package/skill/Model_delete.md +14 -0
- package/skill/Model_findAll.md +12 -0
- package/skill/Model_getPrimaryKeyHash.md +5 -0
- package/skill/Model_isValid.md +14 -0
- package/skill/Model_migrate.md +34 -0
- package/skill/Model_preCommit.md +28 -0
- package/skill/Model_preventPersist.md +15 -0
- package/skill/Model_replaceInto.md +16 -0
- package/skill/Model_validate.md +21 -0
- package/skill/PrimaryIndex.md +8 -0
- package/skill/PrimaryIndex_get.md +17 -0
- package/skill/PrimaryIndex_getLazy.md +13 -0
- package/skill/SKILL.md +158 -664
- package/skill/SecondaryIndex.md +9 -0
- package/skill/UniqueIndex.md +9 -0
- package/skill/UniqueIndex_get.md +17 -0
- package/skill/array.md +23 -0
- package/skill/dump.md +8 -0
- package/skill/field.md +29 -0
- package/skill/index.md +32 -0
- package/skill/init.md +17 -0
- package/skill/link.md +27 -0
- package/skill/literal.md +22 -0
- package/skill/opt.md +22 -0
- package/skill/or.md +22 -0
- package/skill/primary.md +26 -0
- package/skill/record.md +21 -0
- package/skill/registerModel.md +26 -0
- package/skill/runMigration.md +10 -0
- package/skill/set.md +23 -0
- package/skill/setMaxRetryCount.md +10 -0
- package/skill/setOnSaveCallback.md +12 -0
- package/skill/transact.md +49 -0
- package/skill/unique.md +32 -0
- package/src/datapack.ts +49 -7
- package/src/edinburgh.ts +2 -0
- package/src/indexes.ts +143 -71
- package/src/migrate-cli.ts +44 -46
- package/src/migrate.ts +71 -39
- package/src/models.ts +19 -7
- package/src/types.ts +97 -1
- package/src/utils.ts +12 -0
package/skill/SKILL.md
CHANGED
|
@@ -121,6 +121,8 @@ Instance fields are declared with `E.field(type, options?)`. Available types:
|
|
|
121
121
|
| `E.or(A, B, ...)` | `A \| B \| ...` | Union type; args can be types or literal values |
|
|
122
122
|
| `E.literal(v)` | literal type | Constant value; defaults to that value |
|
|
123
123
|
| `E.array(T)` | `T[]` | Optional `{min, max}` constraints |
|
|
124
|
+
| `E.set(T)` | `Set<T>` | Optional `{min, max}` constraints |
|
|
125
|
+
| `E.record(T)` | `Record<string \| number, T>` | Key-value object with string/number keys |
|
|
124
126
|
| `E.link(Model)` | `Model` | Foreign key, lazy-loaded on access |
|
|
125
127
|
|
|
126
128
|
#### Defaults
|
|
@@ -254,6 +256,67 @@ await E.transact(() => {
|
|
|
254
256
|
});
|
|
255
257
|
```
|
|
256
258
|
|
|
259
|
+
#### Non-Persistent Properties
|
|
260
|
+
|
|
261
|
+
You can freely add regular methods, getters, and other non-persistent properties to model classes. These work normally in JavaScript but are **not stored in the database** and **not synchronized** across transactions or processes.
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
@E.registerModel
|
|
265
|
+
class User extends E.Model<User> {
|
|
266
|
+
static pk = E.primary(User, "id");
|
|
267
|
+
id = E.field(E.identifier);
|
|
268
|
+
firstName = E.field(E.string);
|
|
269
|
+
lastName = E.field(E.string);
|
|
270
|
+
|
|
271
|
+
// Non-persisted property
|
|
272
|
+
cachedFullName?: string;
|
|
273
|
+
|
|
274
|
+
get fullName(): string {
|
|
275
|
+
this.cachedFullName ??= `${this.firstName} ${this.lastName}`;
|
|
276
|
+
return this.cachedFullName;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
greet(): string {
|
|
280
|
+
return `Hello, ${this.fullName}!`;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
#### Computed Indexes
|
|
286
|
+
|
|
287
|
+
Instead of naming fields, you can pass a **function** to `E.unique()` or `E.index()`. The function receives a model instance and returns an **array** of index key values. Each element creates a separate index entry, enabling multi-value indexes. Return `[]` to skip indexing for that instance (partial index).
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
@E.registerModel
|
|
291
|
+
class Article extends E.Model<Article> {
|
|
292
|
+
static pk = E.primary(Article, "id");
|
|
293
|
+
static byFullName = E.unique(Article, (a: Article) => [`${a.firstName} ${a.lastName}`]);
|
|
294
|
+
static byWord = E.index(Article, (a: Article) => a.title.toLowerCase().split(" "));
|
|
295
|
+
static byDomain = E.index(Article, (a: Article) => a.email ? [a.email.split("@")[1]] : []);
|
|
296
|
+
|
|
297
|
+
id = E.field(E.identifier);
|
|
298
|
+
firstName = E.field(E.string);
|
|
299
|
+
lastName = E.field(E.string);
|
|
300
|
+
title = E.field(E.string);
|
|
301
|
+
email = E.field(E.opt(E.string));
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
await E.transact(() => {
|
|
305
|
+
new Article({ firstName: "Jane", lastName: "Doe", title: "Hello World", email: "jane@acme.com" });
|
|
306
|
+
|
|
307
|
+
// Lookup via computed unique index
|
|
308
|
+
const jane = Article.byFullName.get("Jane Doe");
|
|
309
|
+
|
|
310
|
+
// Multi-value: each word in the title is indexed separately
|
|
311
|
+
for (const a of Article.byWord.find({is: "hello"})) { ... }
|
|
312
|
+
|
|
313
|
+
// Partial index: articles without email are skipped
|
|
314
|
+
for (const a of Article.byDomain.find({is: "acme.com"})) { ... }
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Computed indexes also support `find()` range queries and `batchProcess()`, just like field-based indexes.
|
|
319
|
+
|
|
257
320
|
### Relationships (Links)
|
|
258
321
|
|
|
259
322
|
Use `E.link(Model)` for foreign keys:
|
|
@@ -329,13 +392,9 @@ await Product.byCategory.batchProcess({is: "old"}, (product) => {
|
|
|
329
392
|
// Commits every ~1 second or 4096 rows (configurable via limitSeconds, limitRows)
|
|
330
393
|
```
|
|
331
394
|
|
|
332
|
-
### Schema
|
|
395
|
+
### Lazy Schema Migrations
|
|
333
396
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
- **Adding/removing fields**: Old rows are lazily migrated on read. New fields use their default value.
|
|
337
|
-
- **Changing field types**: Requires a `static migrate()` function.
|
|
338
|
-
- **Adding/removing indexes**: Requires running `npx migrate-edinburgh`.
|
|
397
|
+
When you change a model's schema, Edinburgh will lazily try to migrate old records on access. This allows you to deploy code changes without downtime or a separate migration step. Optionally, you may provide a `static migrate(record: Record<string, any>)` function on the model to transform old records during lazy migration. If there is a migration error (like a new field without a default value, or an incompatible type change), a run-time error is thrown when loading the affected model instance.
|
|
339
398
|
|
|
340
399
|
```typescript
|
|
341
400
|
@E.registerModel
|
|
@@ -343,15 +402,40 @@ class User extends E.Model<User> {
|
|
|
343
402
|
static pk = E.primary(User, "id");
|
|
344
403
|
id = E.field(E.identifier);
|
|
345
404
|
name = E.field(E.string);
|
|
346
|
-
role = E.field(E.string
|
|
405
|
+
role = E.field(E.string); // newly added field
|
|
347
406
|
|
|
348
407
|
static migrate(record: Record<string, any>) {
|
|
349
|
-
record.role ??= "user";
|
|
408
|
+
record.role ??= record.name.indexOf("admin") >= 0 ? "admin" : "user"; // set role based on name for old records
|
|
350
409
|
}
|
|
351
410
|
}
|
|
352
411
|
```
|
|
353
412
|
|
|
354
|
-
|
|
413
|
+
Edinburgh will lazily (re)run the `migrate` function on an instance whenever its implementation (the literal function code) has changed. For robustness, make sure that your `migrate` function...
|
|
414
|
+
- Is idempotent (meaning it can be safely run multiple times on the same row without changing the result after the first run), and
|
|
415
|
+
- Should perform *all* transformation steps starting from the oldest version that could possibly still be in the database. (See the next section.)
|
|
416
|
+
|
|
417
|
+
While lazy migration is convenient and often sufficient, in some cases you need migrations to happen immediately...
|
|
418
|
+
|
|
419
|
+
### Forced Schema Migrations
|
|
420
|
+
|
|
421
|
+
The `migrate-edinburgh` CLI tool will scan the entire database, pro-actively performing the following migrations:
|
|
422
|
+
- **Populate secondary indexes**: If you added or changed secondary indexes, it will build them. Until you do, the indexes will be empty (or only contain instances that have been saved since the index was created).
|
|
423
|
+
- **Migrate primary indexes**: In case you changed the primary key fields or field types (not recommended!) of a model, it will build the new primary index, as well as all secondary indexes (to point at the new primary keys). Until you do, all of your old data will appear to be missing! Note that this may fail on duplicates.
|
|
424
|
+
- **Remove orphaned indexes**: If you removed or changed an index, the stale data will be deleted from the database.
|
|
425
|
+
- **Rewrite primary data**: These are the types of migrations that would normally be done lazily on instance access. As there's usually not much benefit to doing this forcibly, and it can be very time-consuming (and generates a lot of I/O), this is *not* done by default. It may however be useful if you want to clean up the contents of your `migrate()` function, if you have control over all application deployments. Use the `--rewrite-data` flag to enable this.
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
npx migrate-edinburgh ./src/models.ts
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
Run `npx migrate-edinburgh` without arguments to see all options. You can also call `runMigration()` programmatically:
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
import { runMigration } from "edinburgh";
|
|
435
|
+
|
|
436
|
+
const result = await runMigration({ tables: ["User"] });
|
|
437
|
+
console.log(result.secondaries); // { User: 1500 }
|
|
438
|
+
```
|
|
355
439
|
|
|
356
440
|
### preCommit Hook
|
|
357
441
|
|
|
@@ -413,125 +497,33 @@ The following is auto-generated from `src/edinburgh.ts`:
|
|
|
413
497
|
|
|
414
498
|
**Signature:** `() => void`
|
|
415
499
|
|
|
416
|
-
### init · function
|
|
500
|
+
### [init](init.md) · function
|
|
417
501
|
|
|
418
502
|
Initialize the database with the specified directory path.
|
|
419
503
|
This function may be called multiple times with the same parameters. If it is not called before the first transact(),
|
|
420
504
|
the database will be automatically initialized with the default directory.
|
|
421
505
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
**Parameters:**
|
|
425
|
-
|
|
426
|
-
- `dbDir: string`
|
|
427
|
-
|
|
428
|
-
**Examples:**
|
|
429
|
-
|
|
430
|
-
```typescript
|
|
431
|
-
init("./my-database");
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
### transact · function
|
|
506
|
+
### [transact](transact.md) · function
|
|
435
507
|
|
|
436
508
|
Executes a function within a database transaction context.
|
|
437
509
|
|
|
438
|
-
|
|
439
|
-
within a transaction.
|
|
440
|
-
|
|
441
|
-
Transactions have a consistent view of the database, and changes made within a transaction are
|
|
442
|
-
isolated from other transactions until they are committed. In case a commit clashes with changes
|
|
443
|
-
made by another transaction, the transaction function will automatically be re-executed up to 6
|
|
444
|
-
times.
|
|
445
|
-
|
|
446
|
-
**Signature:** `<T>(fn: () => T) => Promise<T>`
|
|
447
|
-
|
|
448
|
-
**Type Parameters:**
|
|
449
|
-
|
|
450
|
-
- `T` - The return type of the transaction function.
|
|
451
|
-
|
|
452
|
-
**Parameters:**
|
|
453
|
-
|
|
454
|
-
- `fn: () => T` - - The function to execute within the transaction context. Receives a Transaction instance.
|
|
455
|
-
|
|
456
|
-
**Returns:** A promise that resolves with the function's return value.
|
|
457
|
-
|
|
458
|
-
**Throws:**
|
|
459
|
-
|
|
460
|
-
- With code "RACING_TRANSACTION" if the transaction fails after retries due to conflicts.
|
|
461
|
-
- With code "TXN_LIMIT" if maximum number of transactions is reached.
|
|
462
|
-
- With code "LMDB-{code}" for LMDB-specific errors.
|
|
463
|
-
|
|
464
|
-
**Examples:**
|
|
465
|
-
|
|
466
|
-
```typescript
|
|
467
|
-
const paid = await E.transact(() => {
|
|
468
|
-
const user = User.pk.get("john_doe");
|
|
469
|
-
if (user.credits > 0) {
|
|
470
|
-
user.credits--;
|
|
471
|
-
return true;
|
|
472
|
-
}
|
|
473
|
-
return false;
|
|
474
|
-
});
|
|
475
|
-
```
|
|
476
|
-
```typescript
|
|
477
|
-
// Transaction with automatic retry on conflicts
|
|
478
|
-
await E.transact(() => {
|
|
479
|
-
const counter = Counter.pk.get("global") || new Counter({id: "global", value: 0});
|
|
480
|
-
counter.value++;
|
|
481
|
-
});
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
### setMaxRetryCount · function
|
|
510
|
+
### [setMaxRetryCount](setMaxRetryCount.md) · function
|
|
485
511
|
|
|
486
512
|
Set the maximum number of retries for a transaction in case of conflicts.
|
|
487
513
|
The default value is 6. Setting it to 0 will disable retries and cause transactions to fail immediately on conflict.
|
|
488
514
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
**Parameters:**
|
|
492
|
-
|
|
493
|
-
- `count: number` - The maximum number of retries for a transaction.
|
|
494
|
-
|
|
495
|
-
### setOnSaveCallback · function
|
|
515
|
+
### [setOnSaveCallback](setOnSaveCallback.md) · function
|
|
496
516
|
|
|
497
517
|
Set a callback function to be called after a model is saved and committed.
|
|
498
518
|
|
|
499
|
-
**Signature:** `(callback: (commitId: number, items: Map<Model<any>, Change>) => void) => void`
|
|
500
|
-
|
|
501
|
-
**Parameters:**
|
|
502
|
-
|
|
503
|
-
- `callback: ((commitId: number, items: Map<Model<any>, Change>) => void) | undefined` - The callback function to set. It gets called after each successful
|
|
504
|
-
`transact()` commit that has changes, with the following arguments:
|
|
505
|
-
- A sequential number. Higher numbers have been committed after lower numbers.
|
|
506
|
-
- A map of model instances to their changes. The change can be "created", "deleted", or an object containing the old values.
|
|
507
|
-
|
|
508
519
|
### deleteEverything · function
|
|
509
520
|
|
|
510
521
|
**Signature:** `() => Promise<void>`
|
|
511
522
|
|
|
512
|
-
### Model · abstract class
|
|
523
|
+
### [Model](Model.md) · abstract class
|
|
513
524
|
|
|
514
525
|
[object Object],[object Object],[object Object],[object Object],[object Object]
|
|
515
526
|
|
|
516
|
-
**Type Parameters:**
|
|
517
|
-
|
|
518
|
-
- `SUB` - The concrete model subclass (for proper typing).
|
|
519
|
-
|
|
520
|
-
**Examples:**
|
|
521
|
-
|
|
522
|
-
```typescript
|
|
523
|
-
@E.registerModel
|
|
524
|
-
class User extends E.Model<User> {
|
|
525
|
-
static pk = E.primary(User, "id");
|
|
526
|
-
|
|
527
|
-
id = E.field(E.identifier);
|
|
528
|
-
name = E.field(E.string);
|
|
529
|
-
email = E.field(E.string);
|
|
530
|
-
|
|
531
|
-
static byEmail = E.unique(User, "email");
|
|
532
|
-
}
|
|
533
|
-
```
|
|
534
|
-
|
|
535
527
|
#### Model.tableName · static property
|
|
536
528
|
|
|
537
529
|
The database table name (defaults to class name).
|
|
@@ -550,120 +542,32 @@ Field configuration metadata.
|
|
|
550
542
|
|
|
551
543
|
**Type:** `Record<string | number | symbol, FieldConfig<unknown>>`
|
|
552
544
|
|
|
553
|
-
#### Model.migrate · static method
|
|
545
|
+
#### [Model.migrate](Model_migrate.md) · static method
|
|
554
546
|
|
|
555
547
|
Optional migration function called when deserializing rows written with an older schema version.
|
|
556
548
|
Receives a plain record with all fields (primary key fields + value fields) and should mutate it
|
|
557
549
|
in-place to match the current schema.
|
|
558
550
|
|
|
559
|
-
|
|
560
|
-
migration (via `runMigration()` / `npx migrate-edinburgh`). The function's source code is hashed
|
|
561
|
-
to detect changes. Modifying `migrate()` triggers a new schema version.
|
|
562
|
-
|
|
563
|
-
If `migrate()` changes values of fields used in secondary or unique indexes, those indexes
|
|
564
|
-
will only be updated when `runMigration()` is run (not during lazy loading).
|
|
565
|
-
|
|
566
|
-
**Signature:** `(record: Record<string, any>) => void`
|
|
567
|
-
|
|
568
|
-
**Parameters:**
|
|
569
|
-
|
|
570
|
-
- `record: Record<string, any>` - - A plain object with all field values from the old schema version.
|
|
571
|
-
|
|
572
|
-
**Examples:**
|
|
573
|
-
|
|
574
|
-
```typescript
|
|
575
|
-
@E.registerModel
|
|
576
|
-
class User extends E.Model<User> {
|
|
577
|
-
static pk = E.primary(User, "id");
|
|
578
|
-
id = E.field(E.identifier);
|
|
579
|
-
name = E.field(E.string);
|
|
580
|
-
role = E.field(E.string); // new field
|
|
581
|
-
|
|
582
|
-
static migrate(record: Record<string, any>) {
|
|
583
|
-
record.role ??= "user"; // default for rows that predate the 'role' field
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
```
|
|
587
|
-
|
|
588
|
-
#### Model.findAll · static method
|
|
551
|
+
#### [Model.findAll](Model_findAll.md) · static method
|
|
589
552
|
|
|
590
553
|
Find all instances of this model in the database, ordered by primary key.
|
|
591
554
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
**Parameters:**
|
|
595
|
-
|
|
596
|
-
- `this: T`
|
|
597
|
-
- `opts?: {reverse?: boolean}` - - Optional parameters.
|
|
598
|
-
|
|
599
|
-
**Returns:** An iterator.
|
|
600
|
-
|
|
601
|
-
#### Model.replaceInto · static method
|
|
555
|
+
#### [Model.replaceInto](Model_replaceInto.md) · static method
|
|
602
556
|
|
|
603
557
|
Load an existing instance by primary key and update it, or create a new one.
|
|
604
558
|
|
|
605
|
-
|
|
606
|
-
the remaining properties from `obj` are set on the loaded instance. Otherwise a
|
|
607
|
-
new instance is created with `obj` as its initial properties.
|
|
608
|
-
|
|
609
|
-
**Signature:** `<T extends typeof Model<any>>(this: T, obj: Partial<Omit<InstanceType<T>, "constructor">>) => InstanceType<T>`
|
|
610
|
-
|
|
611
|
-
**Parameters:**
|
|
612
|
-
|
|
613
|
-
- `this: T`
|
|
614
|
-
- `obj: Partial<Omit<InstanceType<T>, "constructor">>` - - Partial model data that **must** include every primary key field.
|
|
615
|
-
|
|
616
|
-
**Returns:** The loaded-and-updated or newly created instance.
|
|
617
|
-
|
|
618
|
-
#### model.preCommit · method
|
|
559
|
+
#### [model.preCommit](Model_preCommit.md) · method
|
|
619
560
|
|
|
620
561
|
Optional hook called on each modified instance right before the transaction commits.
|
|
621
562
|
Runs before data is written to disk, so changes made here are included in the commit.
|
|
622
563
|
|
|
623
|
-
Common use cases:
|
|
624
|
-
- Computing derived or denormalized fields
|
|
625
|
-
- Enforcing cross-field validation rules
|
|
626
|
-
- Creating or updating related model instances (newly created instances will also
|
|
627
|
-
have their `preCommit()` called)
|
|
628
|
-
|
|
629
|
-
**Signature:** `() => void`
|
|
630
|
-
|
|
631
|
-
**Parameters:**
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
**Examples:**
|
|
635
|
-
|
|
636
|
-
```typescript
|
|
637
|
-
@E.registerModel
|
|
638
|
-
class Post extends E.Model<Post> {
|
|
639
|
-
static pk = E.primary(Post, "id");
|
|
640
|
-
id = E.field(E.identifier);
|
|
641
|
-
title = E.field(E.string);
|
|
642
|
-
slug = E.field(E.string);
|
|
643
|
-
|
|
644
|
-
preCommit() {
|
|
645
|
-
this.slug = this.title.toLowerCase().replace(/\s+/g, "-");
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
```
|
|
649
|
-
|
|
650
564
|
#### model.getPrimaryKey · method
|
|
651
565
|
|
|
652
566
|
**Signature:** `() => Uint8Array<ArrayBufferLike>`
|
|
653
567
|
|
|
654
|
-
**Parameters:**
|
|
655
|
-
|
|
656
|
-
|
|
657
568
|
**Returns:** The primary key for this instance.
|
|
658
569
|
|
|
659
|
-
#### model.getPrimaryKeyHash · method
|
|
660
|
-
|
|
661
|
-
**Signature:** `() => number`
|
|
662
|
-
|
|
663
|
-
**Parameters:**
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
**Returns:** A 53-bit positive integer non-cryptographic hash of the primary key, or undefined if not yet saved.
|
|
570
|
+
#### [model.getPrimaryKeyHash](Model_getPrimaryKeyHash.md) · method
|
|
667
571
|
|
|
668
572
|
#### model.isLazyField · method
|
|
669
573
|
|
|
@@ -673,161 +577,42 @@ class Post extends E.Model<Post> {
|
|
|
673
577
|
|
|
674
578
|
- `field: keyof this`
|
|
675
579
|
|
|
676
|
-
#### model.preventPersist · method
|
|
580
|
+
#### [model.preventPersist](Model_preventPersist.md) · method
|
|
677
581
|
|
|
678
582
|
Prevent this instance from being persisted to the database.
|
|
679
583
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
**Parameters:**
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
**Returns:** This model instance for chaining.
|
|
686
|
-
|
|
687
|
-
**Examples:**
|
|
688
|
-
|
|
689
|
-
```typescript
|
|
690
|
-
const user = User.load("user123");
|
|
691
|
-
user.name = "New Name";
|
|
692
|
-
user.preventPersist(); // Changes won't be saved
|
|
693
|
-
```
|
|
694
|
-
|
|
695
|
-
#### model.delete · method
|
|
584
|
+
#### [model.delete](Model_delete.md) · method
|
|
696
585
|
|
|
697
586
|
Delete this model instance from the database.
|
|
698
587
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
**Signature:** `() => void`
|
|
702
|
-
|
|
703
|
-
**Parameters:**
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
**Examples:**
|
|
707
|
-
|
|
708
|
-
```typescript
|
|
709
|
-
const user = User.load("user123");
|
|
710
|
-
user.delete(); // Removes from database
|
|
711
|
-
```
|
|
712
|
-
|
|
713
|
-
#### model.validate · method
|
|
588
|
+
#### [model.validate](Model_validate.md) · method
|
|
714
589
|
|
|
715
590
|
Validate all fields in this model instance.
|
|
716
591
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
**Parameters:**
|
|
720
|
-
|
|
721
|
-
- `raise: boolean` (optional) - - If true, throw on first validation error.
|
|
722
|
-
|
|
723
|
-
**Returns:** Array of validation errors (empty if valid).
|
|
724
|
-
|
|
725
|
-
**Examples:**
|
|
726
|
-
|
|
727
|
-
```typescript
|
|
728
|
-
const user = new User();
|
|
729
|
-
const errors = user.validate();
|
|
730
|
-
if (errors.length > 0) {
|
|
731
|
-
console.log("Validation failed:", errors);
|
|
732
|
-
}
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
#### model.isValid · method
|
|
592
|
+
#### [model.isValid](Model_isValid.md) · method
|
|
736
593
|
|
|
737
594
|
Check if this model instance is valid.
|
|
738
595
|
|
|
739
|
-
**Signature:** `() => boolean`
|
|
740
|
-
|
|
741
|
-
**Parameters:**
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
**Returns:** true if all validations pass.
|
|
745
|
-
|
|
746
|
-
**Examples:**
|
|
747
|
-
|
|
748
|
-
```typescript
|
|
749
|
-
const user = new User({name: "John"});
|
|
750
|
-
if (!user.isValid()) shoutAtTheUser();
|
|
751
|
-
```
|
|
752
|
-
|
|
753
596
|
#### model.getState · method
|
|
754
597
|
|
|
755
598
|
**Signature:** `() => "created" | "deleted" | "loaded" | "lazy"`
|
|
756
599
|
|
|
757
|
-
**Parameters:**
|
|
758
|
-
|
|
759
|
-
|
|
760
600
|
#### model.toString · method
|
|
761
601
|
|
|
762
602
|
**Signature:** `() => string`
|
|
763
603
|
|
|
764
|
-
**Parameters:**
|
|
765
|
-
|
|
766
|
-
|
|
767
604
|
#### model.[Symbol.for('nodejs.util.inspect.custom')] · method
|
|
768
605
|
|
|
769
606
|
**Signature:** `() => string`
|
|
770
607
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
### registerModel · function
|
|
608
|
+
### [registerModel](registerModel.md) · function
|
|
775
609
|
|
|
776
610
|
Register a model class with the Edinburgh ORM system.
|
|
777
611
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
**Type Parameters:**
|
|
781
|
-
|
|
782
|
-
- `T extends typeof Model<unknown>` - The model class type.
|
|
783
|
-
|
|
784
|
-
**Parameters:**
|
|
785
|
-
|
|
786
|
-
- `MyModel: T` - - The model class to register.
|
|
787
|
-
|
|
788
|
-
**Returns:** The enhanced model class with ORM capabilities.
|
|
789
|
-
|
|
790
|
-
**Examples:**
|
|
791
|
-
|
|
792
|
-
```typescript
|
|
793
|
-
@E.registerModel
|
|
794
|
-
class User extends E.Model<User> {
|
|
795
|
-
static pk = E.index(User, ["id"], "primary");
|
|
796
|
-
id = E.field(E.identifier);
|
|
797
|
-
name = E.field(E.string);
|
|
798
|
-
}
|
|
799
|
-
```
|
|
800
|
-
|
|
801
|
-
### field · function
|
|
612
|
+
### [field](field.md) · function
|
|
802
613
|
|
|
803
614
|
Create a field definition for a model property.
|
|
804
615
|
|
|
805
|
-
This function uses TypeScript magic to return the field configuration object
|
|
806
|
-
while appearing to return the actual field value type to the type system.
|
|
807
|
-
This allows for both runtime introspection and compile-time type safety.
|
|
808
|
-
|
|
809
|
-
**Signature:** `<T>(type: TypeWrapper<T>, options?: Partial<FieldConfig<T>>) => T`
|
|
810
|
-
|
|
811
|
-
**Type Parameters:**
|
|
812
|
-
|
|
813
|
-
- `T` - The field type.
|
|
814
|
-
|
|
815
|
-
**Parameters:**
|
|
816
|
-
|
|
817
|
-
- `type: TypeWrapper<T>` - - The type wrapper for this field.
|
|
818
|
-
- `options: Partial<FieldConfig<T>>` (optional) - - Additional field configuration options.
|
|
819
|
-
|
|
820
|
-
**Returns:** The field value (typed as T, but actually returns FieldConfig<T>).
|
|
821
|
-
|
|
822
|
-
**Examples:**
|
|
823
|
-
|
|
824
|
-
```typescript
|
|
825
|
-
class User extends E.Model<User> {
|
|
826
|
-
name = E.field(E.string, {description: "User's full name"});
|
|
827
|
-
age = E.field(E.opt(E.number), {description: "User's age", default: 25});
|
|
828
|
-
}
|
|
829
|
-
```
|
|
830
|
-
|
|
831
616
|
### string · constant
|
|
832
617
|
|
|
833
618
|
Type wrapper instance for the string type.
|
|
@@ -852,7 +637,7 @@ Type wrapper instance for the number type.
|
|
|
852
637
|
|
|
853
638
|
### dateTime · constant
|
|
854
639
|
|
|
855
|
-
Type wrapper instance for the date/time type.
|
|
640
|
+
Type wrapper instance for the date/time type. Stored without timezone info, rounded to whole seconds.
|
|
856
641
|
|
|
857
642
|
**Value:** `TypeWrapper<Date>`
|
|
858
643
|
|
|
@@ -874,336 +659,90 @@ Type wrapper instance for the 'undefined' type.
|
|
|
874
659
|
|
|
875
660
|
**Value:** `TypeWrapper<undefined>`
|
|
876
661
|
|
|
877
|
-
### opt · function
|
|
662
|
+
### [opt](opt.md) · function
|
|
878
663
|
|
|
879
664
|
Create an optional type wrapper (allows undefined).
|
|
880
665
|
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
**Type Parameters:**
|
|
884
|
-
|
|
885
|
-
- `T extends TypeWrapper<unknown>|BasicType` - Type wrapper or basic type to make optional.
|
|
886
|
-
|
|
887
|
-
**Parameters:**
|
|
888
|
-
|
|
889
|
-
- `inner: T` - - The inner type to make optional.
|
|
890
|
-
|
|
891
|
-
**Returns:** A union type that accepts the inner type or undefined.
|
|
892
|
-
|
|
893
|
-
**Examples:**
|
|
894
|
-
|
|
895
|
-
```typescript
|
|
896
|
-
const optionalString = E.opt(E.string);
|
|
897
|
-
const optionalNumber = E.opt(E.number);
|
|
898
|
-
```
|
|
899
|
-
|
|
900
|
-
### or · function
|
|
666
|
+
### [or](or.md) · function
|
|
901
667
|
|
|
902
668
|
Create a union type wrapper from multiple type choices.
|
|
903
669
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
**Type Parameters:**
|
|
907
|
-
|
|
908
|
-
- `T extends (TypeWrapper<unknown>|BasicType)[]` - Array of type wrapper or basic types.
|
|
909
|
-
|
|
910
|
-
**Parameters:**
|
|
911
|
-
|
|
912
|
-
- `choices: T` - - The type choices for the union.
|
|
913
|
-
|
|
914
|
-
**Returns:** A union type instance.
|
|
915
|
-
|
|
916
|
-
**Examples:**
|
|
917
|
-
|
|
918
|
-
```typescript
|
|
919
|
-
const stringOrNumber = E.or(E.string, E.number);
|
|
920
|
-
const status = E.or("active", "inactive", "pending");
|
|
921
|
-
```
|
|
922
|
-
|
|
923
|
-
### array · function
|
|
670
|
+
### [array](array.md) · function
|
|
924
671
|
|
|
925
672
|
Create an array type wrapper with optional length constraints.
|
|
926
673
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
**Type Parameters:**
|
|
674
|
+
### [set](set.md) · function
|
|
930
675
|
|
|
931
|
-
|
|
676
|
+
Create a Set type wrapper with optional length constraints.
|
|
932
677
|
|
|
933
|
-
|
|
678
|
+
### [record](record.md) · function
|
|
934
679
|
|
|
935
|
-
|
|
936
|
-
- `opts: {min?: number, max?: number}` (optional) - - Optional constraints (min/max length).
|
|
680
|
+
Create a Record type wrapper for key-value objects with string or number keys.
|
|
937
681
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
**Examples:**
|
|
941
|
-
|
|
942
|
-
```typescript
|
|
943
|
-
const stringArray = E.array(E.string);
|
|
944
|
-
const boundedArray = E.array(E.number, {min: 1, max: 10});
|
|
945
|
-
```
|
|
946
|
-
|
|
947
|
-
### literal · function
|
|
682
|
+
### [literal](literal.md) · function
|
|
948
683
|
|
|
949
684
|
Create a literal type wrapper for a constant value.
|
|
950
685
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
**Type Parameters:**
|
|
954
|
-
|
|
955
|
-
- `T` - The literal type.
|
|
956
|
-
|
|
957
|
-
**Parameters:**
|
|
958
|
-
|
|
959
|
-
- `value: T` - - The literal value.
|
|
960
|
-
|
|
961
|
-
**Returns:** A literal type instance.
|
|
962
|
-
|
|
963
|
-
**Examples:**
|
|
964
|
-
|
|
965
|
-
```typescript
|
|
966
|
-
const statusType = E.literal("active");
|
|
967
|
-
const countType = E.literal(42);
|
|
968
|
-
```
|
|
969
|
-
|
|
970
|
-
### link · function
|
|
686
|
+
### [link](link.md) · function
|
|
971
687
|
|
|
972
688
|
Create a link type wrapper for model relationships.
|
|
973
689
|
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
**Type Parameters:**
|
|
977
|
-
|
|
978
|
-
- `T extends typeof Model<any>` - The target model class.
|
|
979
|
-
|
|
980
|
-
**Parameters:**
|
|
981
|
-
|
|
982
|
-
- `TargetModel: T` - - The model class this link points to.
|
|
983
|
-
|
|
984
|
-
**Returns:** A link type instance.
|
|
985
|
-
|
|
986
|
-
**Examples:**
|
|
987
|
-
|
|
988
|
-
```typescript
|
|
989
|
-
class User extends E.Model<User> {
|
|
990
|
-
posts = E.field(E.array(E.link(Post, 'author')));
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
class Post extends E.Model<Post> {
|
|
994
|
-
author = E.field(E.link(User));
|
|
995
|
-
}
|
|
996
|
-
```
|
|
997
|
-
|
|
998
|
-
### index · function
|
|
690
|
+
### [index](index.md) · function
|
|
999
691
|
|
|
1000
|
-
Create a secondary index on model fields.
|
|
692
|
+
Create a secondary index on model fields, or a computed secondary index using a function.
|
|
1001
693
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
**Type Parameters:**
|
|
1005
|
-
|
|
1006
|
-
- `M extends typeof Model` - The model class.
|
|
1007
|
-
- `F extends (keyof InstanceType<M> & string)` - The field name (for single field index).
|
|
1008
|
-
|
|
1009
|
-
**Parameters:**
|
|
1010
|
-
|
|
1011
|
-
- `MyModel: M` - - The model class to create the index for.
|
|
1012
|
-
- `field: F` - - Single field name for simple indexes.
|
|
1013
|
-
|
|
1014
|
-
**Returns:** A new SecondaryIndex instance.
|
|
1015
|
-
|
|
1016
|
-
**Examples:**
|
|
1017
|
-
|
|
1018
|
-
```typescript
|
|
1019
|
-
class User extends E.Model<User> {
|
|
1020
|
-
static byAge = E.index(User, "age");
|
|
1021
|
-
static byTagsDate = E.index(User, ["tags", "createdAt"]);
|
|
1022
|
-
}
|
|
1023
|
-
```
|
|
1024
|
-
|
|
1025
|
-
### primary · function
|
|
694
|
+
### [primary](primary.md) · function
|
|
1026
695
|
|
|
1027
696
|
Create a primary index on model fields.
|
|
1028
697
|
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
**Type Parameters:**
|
|
1032
|
-
|
|
1033
|
-
- `M extends typeof Model` - The model class.
|
|
1034
|
-
- `F extends (keyof InstanceType<M> & string)` - The field name (for single field index).
|
|
1035
|
-
|
|
1036
|
-
**Parameters:**
|
|
1037
|
-
|
|
1038
|
-
- `MyModel: M` - - The model class to create the index for.
|
|
1039
|
-
- `field: F` - - Single field name for simple indexes.
|
|
1040
|
-
|
|
1041
|
-
**Returns:** A new PrimaryIndex instance.
|
|
1042
|
-
|
|
1043
|
-
**Examples:**
|
|
1044
|
-
|
|
1045
|
-
```typescript
|
|
1046
|
-
class User extends E.Model<User> {
|
|
1047
|
-
static pk = E.primary(User, ["id"]);
|
|
1048
|
-
static pkSingle = E.primary(User, "id");
|
|
1049
|
-
}
|
|
1050
|
-
```
|
|
1051
|
-
|
|
1052
|
-
### unique · function
|
|
698
|
+
### [unique](unique.md) · function
|
|
1053
699
|
|
|
1054
|
-
Create a unique index on model fields.
|
|
700
|
+
Create a unique index on model fields, or a computed unique index using a function.
|
|
1055
701
|
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
**Type Parameters:**
|
|
1059
|
-
|
|
1060
|
-
- `M extends typeof Model` - The model class.
|
|
1061
|
-
- `F extends (keyof InstanceType<M> & string)` - The field name (for single field index).
|
|
1062
|
-
|
|
1063
|
-
**Parameters:**
|
|
1064
|
-
|
|
1065
|
-
- `MyModel: M` - - The model class to create the index for.
|
|
1066
|
-
- `field: F` - - Single field name for simple indexes.
|
|
1067
|
-
|
|
1068
|
-
**Returns:** A new UniqueIndex instance.
|
|
1069
|
-
|
|
1070
|
-
**Examples:**
|
|
1071
|
-
|
|
1072
|
-
```typescript
|
|
1073
|
-
class User extends E.Model<User> {
|
|
1074
|
-
static byEmail = E.unique(User, "email");
|
|
1075
|
-
static byNameAge = E.unique(User, ["name", "age"]);
|
|
1076
|
-
}
|
|
1077
|
-
```
|
|
1078
|
-
|
|
1079
|
-
### dump · function
|
|
702
|
+
### [dump](dump.md) · function
|
|
1080
703
|
|
|
1081
704
|
Dump database contents for debugging.
|
|
1082
705
|
|
|
1083
|
-
|
|
1084
|
-
This is primarily useful for development and debugging purposes.
|
|
1085
|
-
|
|
1086
|
-
**Signature:** `() => void`
|
|
1087
|
-
|
|
1088
|
-
### BaseIndex · abstract class
|
|
706
|
+
### [BaseIndex](BaseIndex.md) · abstract class
|
|
1089
707
|
|
|
1090
708
|
Base class for database indexes for efficient lookups on model fields.
|
|
1091
709
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
**Type Parameters:**
|
|
1095
|
-
|
|
1096
|
-
- `M extends typeof Model` - The model class this index belongs to.
|
|
1097
|
-
- `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
|
|
1098
|
-
|
|
1099
|
-
**Constructor Parameters:**
|
|
1100
|
-
|
|
1101
|
-
- `MyModel`: - The model class this index belongs to.
|
|
1102
|
-
- `_fieldNames`: - Array of field names that make up this index.
|
|
710
|
+
#### [baseIndex.find](BaseIndex_find.md) · method
|
|
1103
711
|
|
|
1104
|
-
#### baseIndex.
|
|
1105
|
-
|
|
1106
|
-
**Signature:** `(opts?: FindOptions<IndexArgTypes<M, F>>) => IndexRangeIterator<M>`
|
|
1107
|
-
|
|
1108
|
-
**Parameters:**
|
|
1109
|
-
|
|
1110
|
-
- `opts: FindOptions<IndexArgTypes<M, F>>` (optional)
|
|
1111
|
-
|
|
1112
|
-
#### baseIndex.batchProcess · method
|
|
712
|
+
#### [baseIndex.batchProcess](BaseIndex_batchProcess.md) · method
|
|
1113
713
|
|
|
1114
714
|
[object Object],[object Object],[object Object]
|
|
1115
715
|
|
|
1116
|
-
**Signature:** `(opts: FindOptions<IndexArgTypes<M, F>> & { limitSeconds?: number; limitRows?: number; }, callback: (row: InstanceType<M>) => void | Promise<...>) => Promise<...>`
|
|
1117
|
-
|
|
1118
|
-
**Parameters:**
|
|
1119
|
-
|
|
1120
|
-
- `opts: FindOptions<IndexArgTypes<M, F>> & { limitSeconds?: number; limitRows?: number }` (optional) - - Query options (same as `find()`), plus:
|
|
1121
|
-
- `callback: (row: InstanceType<M>) => void | Promise<void>` - - Called for each matching row within a transaction
|
|
1122
|
-
|
|
1123
716
|
#### baseIndex.toString · method
|
|
1124
717
|
|
|
1125
718
|
**Signature:** `() => string`
|
|
1126
719
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
### UniqueIndex · class
|
|
720
|
+
### [UniqueIndex](UniqueIndex.md) · class
|
|
1131
721
|
|
|
1132
722
|
Unique index that stores references to the primary key.
|
|
1133
723
|
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
- `M extends typeof Model` - The model class this index belongs to.
|
|
1137
|
-
- `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
|
|
1138
|
-
|
|
1139
|
-
#### uniqueIndex.get · method
|
|
724
|
+
#### [uniqueIndex.get](UniqueIndex_get.md) · method
|
|
1140
725
|
|
|
1141
726
|
Get a model instance by unique index key values.
|
|
1142
727
|
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
**Parameters:**
|
|
1146
|
-
|
|
1147
|
-
- `args: IndexArgTypes<M, F>` - - The unique index key values.
|
|
1148
|
-
|
|
1149
|
-
**Returns:** The model instance if found, undefined otherwise.
|
|
1150
|
-
|
|
1151
|
-
**Examples:**
|
|
1152
|
-
|
|
1153
|
-
```typescript
|
|
1154
|
-
const userByEmail = User.byEmail.get("john@example.com");
|
|
1155
|
-
```
|
|
1156
|
-
|
|
1157
|
-
### PrimaryIndex · class
|
|
728
|
+
### [PrimaryIndex](PrimaryIndex.md) · class
|
|
1158
729
|
|
|
1159
730
|
Primary index that stores the actual model data.
|
|
1160
731
|
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
- `M extends typeof Model` - The model class this index belongs to.
|
|
1164
|
-
- `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
|
|
1165
|
-
|
|
1166
|
-
#### primaryIndex.get · method
|
|
732
|
+
#### [primaryIndex.get](PrimaryIndex_get.md) · method
|
|
1167
733
|
|
|
1168
734
|
Get a model instance by primary key values.
|
|
1169
735
|
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
**Parameters:**
|
|
1173
|
-
|
|
1174
|
-
- `args: IndexArgTypes<M, F>` - - The primary key values.
|
|
1175
|
-
|
|
1176
|
-
**Returns:** The model instance if found, undefined otherwise.
|
|
1177
|
-
|
|
1178
|
-
**Examples:**
|
|
1179
|
-
|
|
1180
|
-
```typescript
|
|
1181
|
-
const user = User.pk.get("john_doe");
|
|
1182
|
-
```
|
|
1183
|
-
|
|
1184
|
-
#### primaryIndex.getLazy · method
|
|
736
|
+
#### [primaryIndex.getLazy](PrimaryIndex_getLazy.md) · method
|
|
1185
737
|
|
|
1186
738
|
Does the same as as `get()`, but will delay loading the instance from disk until the first
|
|
1187
739
|
property access. In case it turns out the instance doesn't exist, an error will be thrown
|
|
1188
740
|
at that time.
|
|
1189
741
|
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
**Parameters:**
|
|
1193
|
-
|
|
1194
|
-
- `args: IndexArgTypes<M, F>` - Primary key field values. (Or a single Uint8Array containing the key.)
|
|
1195
|
-
|
|
1196
|
-
**Returns:** The (lazily loaded) model instance.
|
|
1197
|
-
|
|
1198
|
-
### SecondaryIndex · class
|
|
742
|
+
### [SecondaryIndex](SecondaryIndex.md) · class
|
|
1199
743
|
|
|
1200
744
|
Secondary index for non-unique lookups.
|
|
1201
745
|
|
|
1202
|
-
**Type Parameters:**
|
|
1203
|
-
|
|
1204
|
-
- `M extends typeof Model` - The model class this index belongs to.
|
|
1205
|
-
- `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
|
|
1206
|
-
|
|
1207
746
|
### Change · type
|
|
1208
747
|
|
|
1209
748
|
**Type:** `Record<any, any> | "created" | "deleted"`
|
|
@@ -1222,26 +761,15 @@ Secondary index for non-unique lookups.
|
|
|
1222
761
|
|
|
1223
762
|
**Type:** `Map<number, Model<unknown>>`
|
|
1224
763
|
|
|
1225
|
-
### DatabaseError · constant
|
|
764
|
+
### [DatabaseError](DatabaseError.md) · constant
|
|
1226
765
|
|
|
1227
766
|
The DatabaseError class is used to represent errors that occur during database operations.
|
|
1228
767
|
It extends the built-in Error class and has a machine readable error code string property.
|
|
1229
768
|
|
|
1230
|
-
|
|
1231
|
-
Invalid function arguments will throw TypeError.
|
|
1232
|
-
|
|
1233
|
-
**Value:** `DatabaseErrorConstructor`
|
|
1234
|
-
|
|
1235
|
-
### runMigration · function
|
|
1236
|
-
|
|
1237
|
-
Run database migration: upgrade all rows to the latest schema version,
|
|
1238
|
-
convert old primary indices, and clean up orphaned secondary indices.
|
|
1239
|
-
|
|
1240
|
-
**Signature:** `(options?: MigrationOptions) => Promise<MigrationResult>`
|
|
769
|
+
### [runMigration](runMigration.md) · function
|
|
1241
770
|
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
- `options: MigrationOptions` (optional)
|
|
771
|
+
Run database migration: populate secondary indexes for old-version rows,
|
|
772
|
+
convert old primary indices, rewrite row data, and clean up orphaned indices.
|
|
1245
773
|
|
|
1246
774
|
### MigrationOptions · interface
|
|
1247
775
|
|
|
@@ -1251,21 +779,27 @@ Limit migration to specific table names.
|
|
|
1251
779
|
|
|
1252
780
|
**Type:** `string[]`
|
|
1253
781
|
|
|
1254
|
-
#### migrationOptions.
|
|
782
|
+
#### migrationOptions.populateSecondaries · member
|
|
1255
783
|
|
|
1256
|
-
|
|
784
|
+
Populate secondary indexes for rows at old schema versions (default: true).
|
|
1257
785
|
|
|
1258
786
|
**Type:** `boolean`
|
|
1259
787
|
|
|
1260
|
-
#### migrationOptions.
|
|
788
|
+
#### migrationOptions.migratePrimaries · member
|
|
1261
789
|
|
|
1262
|
-
|
|
790
|
+
Convert old primary indices when primary key fields changed (default: true).
|
|
1263
791
|
|
|
1264
792
|
**Type:** `boolean`
|
|
1265
793
|
|
|
1266
|
-
#### migrationOptions.
|
|
794
|
+
#### migrationOptions.rewriteData · member
|
|
1267
795
|
|
|
1268
|
-
|
|
796
|
+
Rewrite all row data to the latest schema version (default: false).
|
|
797
|
+
|
|
798
|
+
**Type:** `boolean`
|
|
799
|
+
|
|
800
|
+
#### migrationOptions.removeOrphans · member
|
|
801
|
+
|
|
802
|
+
Delete orphaned secondary/unique index entries (default: true).
|
|
1269
803
|
|
|
1270
804
|
**Type:** `boolean`
|
|
1271
805
|
|
|
@@ -1279,13 +813,13 @@ Progress callback.
|
|
|
1279
813
|
|
|
1280
814
|
#### migrationResult.secondaries · member
|
|
1281
815
|
|
|
1282
|
-
Per-table
|
|
816
|
+
Per-table counts of secondary index entries populated.
|
|
1283
817
|
|
|
1284
818
|
**Type:** `Record<string, number>`
|
|
1285
819
|
|
|
1286
820
|
#### migrationResult.primaries · member
|
|
1287
821
|
|
|
1288
|
-
Per-table
|
|
822
|
+
Per-table counts of old primary rows migrated.
|
|
1289
823
|
|
|
1290
824
|
**Type:** `Record<string, number>`
|
|
1291
825
|
|
|
@@ -1295,55 +829,15 @@ Per-table conversion failure counts by reason.
|
|
|
1295
829
|
|
|
1296
830
|
**Type:** `Record<string, Record<string, number>>`
|
|
1297
831
|
|
|
1298
|
-
#### migrationResult.
|
|
1299
|
-
|
|
1300
|
-
Number of orphaned index entries deleted.
|
|
1301
|
-
|
|
1302
|
-
**Type:** `number`
|
|
1303
|
-
|
|
1304
|
-
## Schema Migrations
|
|
1305
|
-
|
|
1306
|
-
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.
|
|
1307
|
-
|
|
1308
|
-
### What happens automatically (lazy migration)
|
|
832
|
+
#### migrationResult.rewritten · member
|
|
1309
833
|
|
|
1310
|
-
|
|
834
|
+
Per-table counts of rows rewritten to latest version.
|
|
1311
835
|
|
|
1312
|
-
|
|
1313
|
-
@E.registerModel
|
|
1314
|
-
class User extends E.Model<User> {
|
|
1315
|
-
static pk = E.primary(User, "id");
|
|
1316
|
-
id = E.field(E.identifier);
|
|
1317
|
-
name = E.field(E.string);
|
|
1318
|
-
role = E.field(E.string); // newly added field
|
|
1319
|
-
|
|
1320
|
-
static migrate(record: Record<string, any>) {
|
|
1321
|
-
record.role ??= "user"; // provide a default for old rows
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
```
|
|
1325
|
-
|
|
1326
|
-
### What requires `migrate-edinburgh`
|
|
1327
|
-
|
|
1328
|
-
The `migrate-edinburgh` CLI tool (or the `runMigration()` API) must be run when:
|
|
1329
|
-
|
|
1330
|
-
- **Adding or removing** secondary or unique indexes
|
|
1331
|
-
- **Changing the fields or types** of an existing index
|
|
1332
|
-
- A **`migrate()` function changes values** that are used in index fields
|
|
1333
|
-
|
|
1334
|
-
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.
|
|
1335
|
-
|
|
1336
|
-
```bash
|
|
1337
|
-
npx migrate-edinburgh --import ./src/models.ts
|
|
1338
|
-
```
|
|
836
|
+
**Type:** `Record<string, number>`
|
|
1339
837
|
|
|
1340
|
-
|
|
838
|
+
#### migrationResult.orphans · member
|
|
1341
839
|
|
|
1342
|
-
|
|
840
|
+
Number of orphaned index entries deleted.
|
|
1343
841
|
|
|
1344
|
-
|
|
1345
|
-
import { runMigration } from "edinburgh";
|
|
842
|
+
**Type:** `number`
|
|
1346
843
|
|
|
1347
|
-
const result = await runMigration({ tables: ["User"] });
|
|
1348
|
-
console.log(result.upgraded); // { User: 1500 }
|
|
1349
|
-
```
|