edinburgh 0.4.4 → 0.4.6

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.
Files changed (61) hide show
  1. package/README.md +119 -150
  2. package/build/src/edinburgh.js +4 -2
  3. package/build/src/edinburgh.js.map +1 -1
  4. package/build/src/indexes.d.ts +2 -3
  5. package/build/src/indexes.js +3 -5
  6. package/build/src/indexes.js.map +1 -1
  7. package/build/src/migrate-cli.d.ts +1 -16
  8. package/build/src/migrate-cli.js +56 -42
  9. package/build/src/migrate-cli.js.map +1 -1
  10. package/build/src/migrate.d.ts +15 -11
  11. package/build/src/migrate.js +53 -39
  12. package/build/src/migrate.js.map +1 -1
  13. package/build/src/models.d.ts +8 -2
  14. package/build/src/models.js +59 -51
  15. package/build/src/models.js.map +1 -1
  16. package/build/src/types.d.ts +2 -1
  17. package/package.json +6 -4
  18. package/skill/BaseIndex.md +16 -0
  19. package/skill/BaseIndex_batchProcess.md +10 -0
  20. package/skill/BaseIndex_find.md +7 -0
  21. package/skill/DatabaseError.md +9 -0
  22. package/skill/Model.md +22 -0
  23. package/skill/Model_delete.md +14 -0
  24. package/skill/Model_findAll.md +12 -0
  25. package/skill/Model_getPrimaryKeyHash.md +5 -0
  26. package/skill/Model_isValid.md +14 -0
  27. package/skill/Model_migrate.md +34 -0
  28. package/skill/Model_preCommit.md +28 -0
  29. package/skill/Model_preventPersist.md +15 -0
  30. package/skill/Model_replaceInto.md +16 -0
  31. package/skill/Model_validate.md +21 -0
  32. package/skill/PrimaryIndex.md +8 -0
  33. package/skill/PrimaryIndex_get.md +17 -0
  34. package/skill/PrimaryIndex_getLazy.md +13 -0
  35. package/skill/SKILL.md +96 -714
  36. package/skill/SecondaryIndex.md +9 -0
  37. package/skill/UniqueIndex.md +9 -0
  38. package/skill/UniqueIndex_get.md +17 -0
  39. package/skill/array.md +23 -0
  40. package/skill/dump.md +8 -0
  41. package/skill/field.md +29 -0
  42. package/skill/index.md +32 -0
  43. package/skill/init.md +17 -0
  44. package/skill/link.md +27 -0
  45. package/skill/literal.md +22 -0
  46. package/skill/opt.md +22 -0
  47. package/skill/or.md +22 -0
  48. package/skill/primary.md +26 -0
  49. package/skill/record.md +21 -0
  50. package/skill/registerModel.md +26 -0
  51. package/skill/runMigration.md +10 -0
  52. package/skill/set.md +23 -0
  53. package/skill/setMaxRetryCount.md +10 -0
  54. package/skill/setOnSaveCallback.md +12 -0
  55. package/skill/transact.md +49 -0
  56. package/skill/unique.md +32 -0
  57. package/src/edinburgh.ts +4 -2
  58. package/src/indexes.ts +3 -6
  59. package/src/migrate-cli.ts +44 -46
  60. package/src/migrate.ts +64 -46
  61. package/src/models.ts +59 -51
package/README.md CHANGED
@@ -388,13 +388,9 @@ await Product.byCategory.batchProcess({is: "old"}, (product) => {
388
388
  // Commits every ~1 second or 4096 rows (configurable via limitSeconds, limitRows)
389
389
  ```
390
390
 
391
- ### Schema Evolution
391
+ ### Lazy Schema Migrations
392
392
 
393
- Edinburgh handles schema changes automatically:
394
-
395
- - **Adding/removing fields**: Old rows are lazily migrated on read. New fields use their default value.
396
- - **Changing field types**: Requires a `static migrate()` function.
397
- - **Adding/removing indexes**: Requires running `npx migrate-edinburgh`.
393
+ 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.
398
394
 
399
395
  ```typescript
400
396
  @E.registerModel
@@ -402,15 +398,40 @@ class User extends E.Model<User> {
402
398
  static pk = E.primary(User, "id");
403
399
  id = E.field(E.identifier);
404
400
  name = E.field(E.string);
405
- role = E.field(E.string, {default: "user"}); // new field
401
+ role = E.field(E.string); // newly added field
406
402
 
407
403
  static migrate(record: Record<string, any>) {
408
- record.role ??= "user"; // provide value for old rows
404
+ record.role ??= record.name.indexOf("admin") >= 0 ? "admin" : "user"; // set role based on name for old records
409
405
  }
410
406
  }
411
407
  ```
412
408
 
413
- Run `npx migrate-edinburgh` (or call `E.runMigration()`) after adding/removing indexes, changing index field types, or when a `migrate()` function affects indexed fields.
409
+ 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...
410
+ - Is idempotent (meaning it can be safely run multiple times on the same row without changing the result after the first run), and
411
+ - Should perform *all* transformation steps starting from the oldest version that could possibly still be in the database. (See the next section.)
412
+
413
+ While lazy migration is convenient and often sufficient, in some cases you need migrations to happen immediately...
414
+
415
+ ### Forced Schema Migrations
416
+
417
+ The `migrate-edinburgh` CLI tool will scan the entire database, pro-actively performing the following migrations:
418
+ - **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).
419
+ - **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.
420
+ - **Remove orphaned indexes**: If you removed or changed an index, the stale data will be deleted from the database.
421
+ - **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.
422
+
423
+ ```bash
424
+ npx migrate-edinburgh ./src/models.ts
425
+ ```
426
+
427
+ Run `npx migrate-edinburgh` without arguments to see all options. You can also call `runMigration()` programmatically:
428
+
429
+ ```typescript
430
+ import { runMigration } from "edinburgh";
431
+
432
+ const result = await runMigration({ tables: ["User"] });
433
+ console.log(result.secondaries); // { User: 1500 }
434
+ ```
414
435
 
415
436
  ### preCommit Hook
416
437
 
@@ -540,7 +561,7 @@ await E.transact(() => {
540
561
  });
541
562
  ```
542
563
 
543
- ### setMaxRetryCount · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L208)
564
+ ### setMaxRetryCount · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L209)
544
565
 
545
566
  Set the maximum number of retries for a transaction in case of conflicts.
546
567
  The default value is 6. Setting it to 0 will disable retries and cause transactions to fail immediately on conflict.
@@ -551,7 +572,7 @@ The default value is 6. Setting it to 0 will disable retries and cause transacti
551
572
 
552
573
  - `count: number` - The maximum number of retries for a transaction.
553
574
 
554
- ### setOnSaveCallback · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L222)
575
+ ### setOnSaveCallback · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L223)
555
576
 
556
577
  Set a callback function to be called after a model is saved and committed.
557
578
 
@@ -564,11 +585,11 @@ Set a callback function to be called after a model is saved and committed.
564
585
  - A sequential number. Higher numbers have been committed after lower numbers.
565
586
  - A map of model instances to their changes. The change can be "created", "deleted", or an object containing the old values.
566
587
 
567
- ### deleteEverything · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L227)
588
+ ### deleteEverything · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L228)
568
589
 
569
590
  **Signature:** `() => Promise<void>`
570
591
 
571
- ### Model · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L220)
592
+ ### Model · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L221)
572
593
 
573
594
  [object Object],[object Object],[object Object],[object Object],[object Object]
574
595
 
@@ -591,7 +612,7 @@ class User extends E.Model<User> {
591
612
  }
592
613
  ```
593
614
 
594
- #### Model.tableName · [static property](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L227)
615
+ #### Model.tableName · [static property](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L225)
595
616
 
596
617
  The database table name (defaults to class name).
597
618
 
@@ -603,13 +624,13 @@ When true, registerModel replaces an existing model with the same tableName.
603
624
 
604
625
  **Type:** `boolean`
605
626
 
606
- #### Model.fields · [static property](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L232)
627
+ #### Model.fields · [static property](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L233)
607
628
 
608
629
  Field configuration metadata.
609
630
 
610
631
  **Type:** `Record<string | number | symbol, FieldConfig<unknown>>`
611
632
 
612
- #### Model.migrate · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
633
+ #### Model.migrate · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
613
634
 
614
635
  Optional migration function called when deserializing rows written with an older schema version.
615
636
  Receives a plain record with all fields (primary key fields + value fields) and should mutate it
@@ -644,7 +665,19 @@ class User extends E.Model<User> {
644
665
  }
645
666
  ```
646
667
 
647
- #### Model.findAll · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
668
+ #### Model.initFields · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
669
+
670
+ Transform the model's `E.field` properties into the appropriate JavaScript properties. Normally this is done
671
+ automatically when using `transact()`, but in case you need to access `Model.fields` directly before the first
672
+ transaction, you can call this method manually.
673
+
674
+ **Signature:** `(reset?: boolean) => void`
675
+
676
+ **Parameters:**
677
+
678
+ - `reset?: boolean`
679
+
680
+ #### Model.findAll · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
648
681
 
649
682
  Find all instances of this model in the database, ordered by primary key.
650
683
 
@@ -657,7 +690,7 @@ Find all instances of this model in the database, ordered by primary key.
657
690
 
658
691
  **Returns:** An iterator.
659
692
 
660
- #### Model.replaceInto · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
693
+ #### Model.replaceInto · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
661
694
 
662
695
  Load an existing instance by primary key and update it, or create a new one.
663
696
 
@@ -674,7 +707,7 @@ new instance is created with `obj` as its initial properties.
674
707
 
675
708
  **Returns:** The loaded-and-updated or newly created instance.
676
709
 
677
- #### model.preCommit · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
710
+ #### model.preCommit · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
678
711
 
679
712
  Optional hook called on each modified instance right before the transaction commits.
680
713
  Runs before data is written to disk, so changes made here are included in the commit.
@@ -687,9 +720,6 @@ Common use cases:
687
720
 
688
721
  **Signature:** `() => void`
689
722
 
690
- **Parameters:**
691
-
692
-
693
723
  **Examples:**
694
724
 
695
725
  ```typescript
@@ -706,25 +736,19 @@ class Post extends E.Model<Post> {
706
736
  }
707
737
  ```
708
738
 
709
- #### model.getPrimaryKey · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
739
+ #### model.getPrimaryKey · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
710
740
 
711
741
  **Signature:** `() => Uint8Array<ArrayBufferLike>`
712
742
 
713
- **Parameters:**
714
-
715
-
716
743
  **Returns:** The primary key for this instance.
717
744
 
718
- #### model.getPrimaryKeyHash · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
745
+ #### model.getPrimaryKeyHash · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
719
746
 
720
747
  **Signature:** `() => number`
721
748
 
722
- **Parameters:**
723
-
724
-
725
749
  **Returns:** A 53-bit positive integer non-cryptographic hash of the primary key, or undefined if not yet saved.
726
750
 
727
- #### model.isLazyField · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
751
+ #### model.isLazyField · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
728
752
 
729
753
  **Signature:** `(field: keyof this) => boolean`
730
754
 
@@ -732,15 +756,12 @@ class Post extends E.Model<Post> {
732
756
 
733
757
  - `field: keyof this`
734
758
 
735
- #### model.preventPersist · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
759
+ #### model.preventPersist · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
736
760
 
737
761
  Prevent this instance from being persisted to the database.
738
762
 
739
763
  **Signature:** `() => this`
740
764
 
741
- **Parameters:**
742
-
743
-
744
765
  **Returns:** This model instance for chaining.
745
766
 
746
767
  **Examples:**
@@ -751,7 +772,7 @@ user.name = "New Name";
751
772
  user.preventPersist(); // Changes won't be saved
752
773
  ```
753
774
 
754
- #### model.delete · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
775
+ #### model.delete · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
755
776
 
756
777
  Delete this model instance from the database.
757
778
 
@@ -759,9 +780,6 @@ Removes the instance and all its index entries from the database and prevents fu
759
780
 
760
781
  **Signature:** `() => void`
761
782
 
762
- **Parameters:**
763
-
764
-
765
783
  **Examples:**
766
784
 
767
785
  ```typescript
@@ -769,7 +787,7 @@ const user = User.load("user123");
769
787
  user.delete(); // Removes from database
770
788
  ```
771
789
 
772
- #### model.validate · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
790
+ #### model.validate · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
773
791
 
774
792
  Validate all fields in this model instance.
775
793
 
@@ -791,15 +809,12 @@ if (errors.length > 0) {
791
809
  }
792
810
  ```
793
811
 
794
- #### model.isValid · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
812
+ #### model.isValid · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
795
813
 
796
814
  Check if this model instance is valid.
797
815
 
798
816
  **Signature:** `() => boolean`
799
817
 
800
- **Parameters:**
801
-
802
-
803
818
  **Returns:** true if all validations pass.
804
819
 
805
820
  **Examples:**
@@ -809,27 +824,18 @@ const user = new User({name: "John"});
809
824
  if (!user.isValid()) shoutAtTheUser();
810
825
  ```
811
826
 
812
- #### model.getState · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
827
+ #### model.getState · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
813
828
 
814
829
  **Signature:** `() => "created" | "deleted" | "loaded" | "lazy"`
815
830
 
816
- **Parameters:**
817
-
818
-
819
- #### model.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
831
+ #### model.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
820
832
 
821
833
  **Signature:** `() => string`
822
834
 
823
- **Parameters:**
824
-
825
-
826
- #### model.[Symbol.for('nodejs.util.inspect.custom')] · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
835
+ #### model.[Symbol.for('nodejs.util.inspect.custom')] · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
827
836
 
828
837
  **Signature:** `() => string`
829
838
 
830
- **Parameters:**
831
-
832
-
833
839
  ### registerModel · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L113)
834
840
 
835
841
  Register a model class with the Edinburgh ORM system.
@@ -887,13 +893,13 @@ class User extends E.Model<User> {
887
893
  }
888
894
  ```
889
895
 
890
- ### string · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
896
+ ### string · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
891
897
 
892
898
  Type wrapper instance for the string type.
893
899
 
894
900
  **Value:** `TypeWrapper<string>`
895
901
 
896
- ### orderedString · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
902
+ ### orderedString · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
897
903
 
898
904
  Type wrapper instance for the ordered string type, which is just like a string
899
905
  except that it sorts lexicographically in the database (instead of by incrementing
@@ -903,37 +909,37 @@ may not contain null characters.
903
909
 
904
910
  **Value:** `TypeWrapper<string>`
905
911
 
906
- ### number · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
912
+ ### number · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
907
913
 
908
914
  Type wrapper instance for the number type.
909
915
 
910
916
  **Value:** `TypeWrapper<number>`
911
917
 
912
- ### dateTime · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
918
+ ### dateTime · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
913
919
 
914
920
  Type wrapper instance for the date/time type. Stored without timezone info, rounded to whole seconds.
915
921
 
916
922
  **Value:** `TypeWrapper<Date>`
917
923
 
918
- ### boolean · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
924
+ ### boolean · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
919
925
 
920
926
  Type wrapper instance for the boolean type.
921
927
 
922
928
  **Value:** `TypeWrapper<boolean>`
923
929
 
924
- ### identifier · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
930
+ ### identifier · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
925
931
 
926
932
  Type wrapper instance for the identifier type.
927
933
 
928
934
  **Value:** `TypeWrapper<string>`
929
935
 
930
- ### undef · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
936
+ ### undef · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
931
937
 
932
938
  Type wrapper instance for the 'undefined' type.
933
939
 
934
940
  **Value:** `TypeWrapper<undefined>`
935
941
 
936
- ### opt · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
942
+ ### opt · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
937
943
 
938
944
  Create an optional type wrapper (allows undefined).
939
945
 
@@ -956,7 +962,7 @@ const optionalString = E.opt(E.string);
956
962
  const optionalNumber = E.opt(E.number);
957
963
  ```
958
964
 
959
- ### or · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
965
+ ### or · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
960
966
 
961
967
  Create a union type wrapper from multiple type choices.
962
968
 
@@ -979,7 +985,7 @@ const stringOrNumber = E.or(E.string, E.number);
979
985
  const status = E.or("active", "inactive", "pending");
980
986
  ```
981
987
 
982
- ### array · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
988
+ ### array · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
983
989
 
984
990
  Create an array type wrapper with optional length constraints.
985
991
 
@@ -1003,7 +1009,7 @@ const stringArray = E.array(E.string);
1003
1009
  const boundedArray = E.array(E.number, {min: 1, max: 10});
1004
1010
  ```
1005
1011
 
1006
- ### set · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1012
+ ### set · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1007
1013
 
1008
1014
  Create a Set type wrapper with optional length constraints.
1009
1015
 
@@ -1027,7 +1033,7 @@ const stringSet = E.set(E.string);
1027
1033
  const boundedSet = E.set(E.number, {min: 1, max: 10});
1028
1034
  ```
1029
1035
 
1030
- ### record · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1036
+ ### record · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1031
1037
 
1032
1038
  Create a Record type wrapper for key-value objects with string or number keys.
1033
1039
 
@@ -1049,7 +1055,7 @@ Create a Record type wrapper for key-value objects with string or number keys.
1049
1055
  const scores = E.record(E.number); // Record<string | number, number>
1050
1056
  ```
1051
1057
 
1052
- ### literal · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1058
+ ### literal · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1053
1059
 
1054
1060
  Create a literal type wrapper for a constant value.
1055
1061
 
@@ -1072,7 +1078,7 @@ const statusType = E.literal("active");
1072
1078
  const countType = E.literal(42);
1073
1079
  ```
1074
1080
 
1075
- ### link · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1081
+ ### link · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1076
1082
 
1077
1083
  Create a link type wrapper for model relationships.
1078
1084
 
@@ -1100,7 +1106,7 @@ class Post extends E.Model<Post> {
1100
1106
  }
1101
1107
  ```
1102
1108
 
1103
- ### index · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1109
+ ### index · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1104
1110
 
1105
1111
  Create a secondary index on model fields, or a computed secondary index using a function.
1106
1112
 
@@ -1133,7 +1139,7 @@ class User extends E.Model<User> {
1133
1139
  }
1134
1140
  ```
1135
1141
 
1136
- ### primary · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1142
+ ### primary · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1137
1143
 
1138
1144
  Create a primary index on model fields.
1139
1145
 
@@ -1160,7 +1166,7 @@ class User extends E.Model<User> {
1160
1166
  }
1161
1167
  ```
1162
1168
 
1163
- ### unique · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1169
+ ### unique · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1164
1170
 
1165
1171
  Create a unique index on model fields, or a computed unique index using a function.
1166
1172
 
@@ -1193,7 +1199,7 @@ class User extends E.Model<User> {
1193
1199
  }
1194
1200
  ```
1195
1201
 
1196
- ### dump · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1202
+ ### dump · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1197
1203
 
1198
1204
  Dump database contents for debugging.
1199
1205
 
@@ -1202,7 +1208,7 @@ This is primarily useful for development and debugging purposes.
1202
1208
 
1203
1209
  **Signature:** `() => void`
1204
1210
 
1205
- ### BaseIndex · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L125)
1211
+ ### BaseIndex · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L123)
1206
1212
 
1207
1213
  Base class for database indexes for efficient lookups on model fields.
1208
1214
 
@@ -1219,7 +1225,7 @@ Indexes enable fast queries on specific field combinations and enforce uniquenes
1219
1225
  - `MyModel`: - The model class this index belongs to.
1220
1226
  - `_fieldNames`: - Array of field names that make up this index.
1221
1227
 
1222
- #### baseIndex.find · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1228
+ #### baseIndex.find · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1223
1229
 
1224
1230
  **Signature:** `(opts?: FindOptions<ARGS>) => IndexRangeIterator<M>`
1225
1231
 
@@ -1227,7 +1233,7 @@ Indexes enable fast queries on specific field combinations and enforce uniquenes
1227
1233
 
1228
1234
  - `opts: FindOptions<ARGS>` (optional)
1229
1235
 
1230
- #### baseIndex.batchProcess · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1236
+ #### baseIndex.batchProcess · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1231
1237
 
1232
1238
  [object Object],[object Object],[object Object]
1233
1239
 
@@ -1238,14 +1244,11 @@ Indexes enable fast queries on specific field combinations and enforce uniquenes
1238
1244
  - `opts: FindOptions<ARGS> & { limitSeconds?: number; limitRows?: number }` (optional) - - Query options (same as `find()`), plus:
1239
1245
  - `callback: (row: InstanceType<M>) => void | Promise<void>` - - Called for each matching row within a transaction
1240
1246
 
1241
- #### baseIndex.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1247
+ #### baseIndex.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1242
1248
 
1243
1249
  **Signature:** `() => string`
1244
1250
 
1245
- **Parameters:**
1246
-
1247
-
1248
- ### UniqueIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1251
+ ### UniqueIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1249
1252
 
1250
1253
  Unique index that stores references to the primary key.
1251
1254
 
@@ -1255,7 +1258,7 @@ Unique index that stores references to the primary key.
1255
1258
  - `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
1256
1259
  - `ARGS extends readonly any[] = IndexArgTypes<M, F>`
1257
1260
 
1258
- #### uniqueIndex.get · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1261
+ #### uniqueIndex.get · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1259
1262
 
1260
1263
  Get a model instance by unique index key values.
1261
1264
 
@@ -1273,7 +1276,7 @@ Get a model instance by unique index key values.
1273
1276
  const userByEmail = User.byEmail.get("john@example.com");
1274
1277
  ```
1275
1278
 
1276
- ### PrimaryIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1279
+ ### PrimaryIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1277
1280
 
1278
1281
  Primary index that stores the actual model data.
1279
1282
 
@@ -1282,7 +1285,7 @@ Primary index that stores the actual model data.
1282
1285
  - `M extends typeof Model` - The model class this index belongs to.
1283
1286
  - `F extends readonly (keyof InstanceType<M> & string)[]` - The field names that make up this index.
1284
1287
 
1285
- #### primaryIndex.get · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1288
+ #### primaryIndex.get · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1286
1289
 
1287
1290
  Get a model instance by primary key values.
1288
1291
 
@@ -1300,7 +1303,7 @@ Get a model instance by primary key values.
1300
1303
  const user = User.pk.get("john_doe");
1301
1304
  ```
1302
1305
 
1303
- #### primaryIndex.getLazy · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1306
+ #### primaryIndex.getLazy · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1304
1307
 
1305
1308
  Does the same as as `get()`, but will delay loading the instance from disk until the first
1306
1309
  property access. In case it turns out the instance doesn't exist, an error will be thrown
@@ -1314,7 +1317,7 @@ at that time.
1314
1317
 
1315
1318
  **Returns:** The (lazily loaded) model instance.
1316
1319
 
1317
- ### SecondaryIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L253)
1320
+ ### SecondaryIndex · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L255)
1318
1321
 
1319
1322
  Secondary index for non-unique lookups.
1320
1323
 
@@ -1342,7 +1345,7 @@ Secondary index for non-unique lookups.
1342
1345
 
1343
1346
  **Type:** `Map<number, Model<unknown>>`
1344
1347
 
1345
- ### DatabaseError · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L157)
1348
+ ### DatabaseError · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L158)
1346
1349
 
1347
1350
  The DatabaseError class is used to represent errors that occur during database operations.
1348
1351
  It extends the built-in Error class and has a machine readable error code string property.
@@ -1352,10 +1355,10 @@ Invalid function arguments will throw TypeError.
1352
1355
 
1353
1356
  **Value:** `DatabaseErrorConstructor`
1354
1357
 
1355
- ### runMigration · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L118)
1358
+ ### runMigration · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L124)
1356
1359
 
1357
- Run database migration: upgrade all rows to the latest schema version,
1358
- convert old primary indices, and clean up orphaned secondary indices.
1360
+ Run database migration: populate secondary indexes for old-version rows,
1361
+ convert old primary indices, rewrite row data, and clean up orphaned indices.
1359
1362
 
1360
1363
  **Signature:** `(options?: MigrationOptions) => Promise<MigrationResult>`
1361
1364
 
@@ -1371,99 +1374,65 @@ Limit migration to specific table names.
1371
1374
 
1372
1375
  **Type:** `string[]`
1373
1376
 
1374
- #### migrationOptions.convertOldPrimaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L23)
1377
+ #### migrationOptions.populateSecondaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L23)
1378
+
1379
+ Populate secondary indexes for rows at old schema versions (default: true).
1380
+
1381
+ **Type:** `boolean`
1382
+
1383
+ #### migrationOptions.migratePrimaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L32)
1375
1384
 
1376
- Whether to convert old primary indices for known tables (default: true).
1385
+ Convert old primary indices when primary key fields changed (default: true).
1377
1386
 
1378
1387
  **Type:** `boolean`
1379
1388
 
1380
- #### migrationOptions.deleteOrphanedIndexes · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L31)
1389
+ #### migrationOptions.rewriteData · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L40)
1381
1390
 
1382
- Whether to delete orphaned secondary/unique indices (default: true).
1391
+ Rewrite all row data to the latest schema version (default: false).
1383
1392
 
1384
1393
  **Type:** `boolean`
1385
1394
 
1386
- #### migrationOptions.upgradeVersions · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L39)
1395
+ #### migrationOptions.removeOrphans · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L46)
1387
1396
 
1388
- Whether to upgrade rows to the latest version (default: true).
1397
+ Delete orphaned secondary/unique index entries (default: true).
1389
1398
 
1390
1399
  **Type:** `boolean`
1391
1400
 
1392
- #### migrationOptions.onProgress · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L44)
1401
+ #### migrationOptions.onProgress · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L48)
1393
1402
 
1394
1403
  Progress callback.
1395
1404
 
1396
1405
  **Type:** `(info: ProgressInfo) => void`
1397
1406
 
1398
- ### MigrationResult · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L49)
1407
+ ### MigrationResult · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L51)
1399
1408
 
1400
- #### migrationResult.secondaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L51)
1409
+ #### migrationResult.secondaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L54)
1401
1410
 
1402
- Per-table stats for row upgrades.
1411
+ Per-table counts of secondary index entries populated.
1403
1412
 
1404
1413
  **Type:** `Record<string, number>`
1405
1414
 
1406
- #### migrationResult.primaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L52)
1415
+ #### migrationResult.primaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L59)
1407
1416
 
1408
- Per-table stats for old primary conversions.
1417
+ Per-table counts of old primary rows migrated.
1409
1418
 
1410
1419
  **Type:** `Record<string, number>`
1411
1420
 
1412
- #### migrationResult.conversionFailures · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L58)
1421
+ #### migrationResult.conversionFailures · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L59)
1413
1422
 
1414
1423
  Per-table conversion failure counts by reason.
1415
1424
 
1416
1425
  **Type:** `Record<string, Record<string, number>>`
1417
1426
 
1418
- #### migrationResult.orphaned · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L59)
1419
-
1420
- Number of orphaned index entries deleted.
1421
-
1422
- **Type:** `number`
1423
-
1424
- ## Schema Migrations
1425
-
1426
- 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.
1427
-
1428
- ### What happens automatically (lazy migration)
1429
-
1430
- 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.
1431
-
1432
- ```typescript
1433
- @E.registerModel
1434
- class User extends E.Model<User> {
1435
- static pk = E.primary(User, "id");
1436
- id = E.field(E.identifier);
1437
- name = E.field(E.string);
1438
- role = E.field(E.string); // newly added field
1439
-
1440
- static migrate(record: Record<string, any>) {
1441
- record.role ??= "user"; // provide a default for old rows
1442
- }
1443
- }
1444
- ```
1445
-
1446
- ### What requires `migrate-edinburgh`
1427
+ #### migrationResult.rewritten · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L64)
1447
1428
 
1448
- The `migrate-edinburgh` CLI tool (or the `runMigration()` API) must be run when:
1429
+ Per-table counts of rows rewritten to latest version.
1449
1430
 
1450
- - **Adding or removing** secondary or unique indexes
1451
- - **Changing the fields or types** of an existing index
1452
- - A **`migrate()` function changes values** that are used in index fields
1453
-
1454
- 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.
1455
-
1456
- ```bash
1457
- npx migrate-edinburgh --import ./src/models.ts
1458
- ```
1431
+ **Type:** `Record<string, number>`
1459
1432
 
1460
- Run `npx migrate-edinburgh` to see all of its options.
1433
+ #### migrationResult.orphans · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L68)
1461
1434
 
1462
- You can also call `runMigration()` programmatically:
1435
+ Number of orphaned index entries deleted.
1463
1436
 
1464
- ```typescript
1465
- import { runMigration } from "edinburgh";
1437
+ **Type:** `number`
1466
1438
 
1467
- const result = await runMigration({ tables: ["User"] });
1468
- console.log(result.upgraded); // { User: 1500 }
1469
- ```