edinburgh 0.6.3 → 0.6.4

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 CHANGED
@@ -494,14 +494,14 @@ ln -s ../../node_modules/edinburgh/skill .claude/skills/edinburgh
494
494
 
495
495
  The following is auto-generated from `src/edinburgh.ts`:
496
496
 
497
- ### currentTxn · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L66)
497
+ ### currentTxn · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L67)
498
498
 
499
499
  Returns the current transaction from AsyncLocalStorage.
500
500
  Throws if called outside a transact() callback.
501
501
 
502
502
  **Signature:** `() => Transaction`
503
503
 
504
- ### init · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L85)
504
+ ### init · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L86)
505
505
 
506
506
  Initialize the database with the specified directory path.
507
507
  This function may be called multiple times with the same parameters. If it is not called before the first transact(),
@@ -519,7 +519,7 @@ the database will be automatically initialized with the default directory.
519
519
  init("./my-database");
520
520
  ```
521
521
 
522
- ### transact · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L136)
522
+ ### transact · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L137)
523
523
 
524
524
  Executes a function within a database transaction context.
525
525
 
@@ -569,7 +569,7 @@ await E.transact(() => {
569
569
  });
570
570
  ```
571
571
 
572
- ### setMaxRetryCount · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L232)
572
+ ### setMaxRetryCount · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L244)
573
573
 
574
574
  Set the maximum number of retries for a transaction in case of conflicts.
575
575
  The default value is 6. Setting it to 0 will disable retries and cause transactions to fail immediately on conflict.
@@ -580,11 +580,11 @@ The default value is 6. Setting it to 0 will disable retries and cause transacti
580
580
 
581
581
  - `count: number` - The maximum number of retries for a transaction.
582
582
 
583
- ### setOnSaveCallback · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L249)
583
+ ### setOnSaveCallback · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L261)
584
584
 
585
585
  Set a callback function to be called after a model is saved and committed.
586
586
 
587
- **Signature:** `(callback: (commitId: number, items: Map<ModelBase, Change>) => void) => void`
587
+ **Signature:** `(callback: (commitId: number, items: Map<ModelBase<ModelLookup<readonly any[]>>, Change>) => void) => void`
588
588
 
589
589
  **Parameters:**
590
590
 
@@ -593,14 +593,14 @@ Set a callback function to be called after a model is saved and committed.
593
593
  - A sequential number. Higher numbers have been committed after lower numbers.
594
594
  - A map of model instances to their changes. The change can be "created", "deleted", or an object containing the old values.
595
595
 
596
- The callback is called within a new transaction context, allowing lazy-loads to happen. However, any
597
- changes made to Edinburgh models will not be saved.
596
+ The callback is called within a new transaction context at or after the committed state, so lazy-loads
597
+ and additional writes are allowed.
598
598
 
599
- ### Model · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
599
+ ### Model · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
600
600
 
601
601
  **Type:** `typeof ModelBase`
602
602
 
603
- ### ModelClass · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
603
+ ### ModelClass · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
604
604
 
605
605
  Runtime base constructor for model classes returned by `defineModel()`.
606
606
 
@@ -609,15 +609,15 @@ Prefer the `ModelClass` type alias for annotations and the result of
609
609
 
610
610
  **Type:** `typeof ModelClassRuntime`
611
611
 
612
- ### AnyModelClass · [type](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L132)
612
+ ### AnyModelClass · [type](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L137)
613
613
 
614
614
  A model constructor with its generic information erased.
615
615
 
616
616
  Useful when accepting or storing arbitrary registered model classes.
617
617
 
618
- **Type:** `ModelClass<object, any, any, readonly any[], any, any>`
618
+ **Type:** `ModelClass<object, any, readonly any[], any, any>`
619
619
 
620
- ### ModelBase · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
620
+ ### ModelBase · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
621
621
 
622
622
  Base class for all database models in the Edinburgh ORM.
623
623
 
@@ -670,7 +670,7 @@ const User = E.defineModel("User", class {
670
670
  type User = InstanceType<typeof User>;
671
671
  ```
672
672
 
673
- #### ModelBase.migrate · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
673
+ #### ModelBase.migrate · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
674
674
 
675
675
  Optional migration function called when deserializing rows written with an older schema version.
676
676
  Receives a plain record with all fields and should mutate it in-place to match the current schema.
@@ -697,7 +697,7 @@ const User = E.defineModel("User", class {
697
697
  }, { pk: "id" });
698
698
  ```
699
699
 
700
- #### modelBase.preCommit · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
700
+ #### modelBase.preCommit · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
701
701
 
702
702
  Optional hook called on each modified instance right before the transaction commits.
703
703
  Runs before data is written to disk, so changes made here are included in the commit.
@@ -724,19 +724,19 @@ const Post = E.defineModel("Post", class {
724
724
  }, { pk: "id" });
725
725
  ```
726
726
 
727
- #### modelBase.getPrimaryKey · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
727
+ #### modelBase.getPrimaryKey · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
728
728
 
729
729
  **Signature:** `() => Uint8Array<ArrayBufferLike>`
730
730
 
731
731
  **Returns:** The primary key for this instance.
732
732
 
733
- #### modelBase.getPrimaryKeyHash · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
733
+ #### modelBase.getPrimaryKeyHash · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
734
734
 
735
735
  **Signature:** `() => number`
736
736
 
737
737
  **Returns:** A 53-bit positive integer non-cryptographic hash of the primary key, or undefined if not yet saved.
738
738
 
739
- #### modelBase.isLazyField · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
739
+ #### modelBase.isLazyField · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
740
740
 
741
741
  **Signature:** `(field: keyof this) => boolean`
742
742
 
@@ -744,7 +744,7 @@ const Post = E.defineModel("Post", class {
744
744
 
745
745
  - `field: keyof this`
746
746
 
747
- #### modelBase.preventPersist · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
747
+ #### modelBase.preventPersist · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
748
748
 
749
749
  Prevent this instance from being persisted to the database.
750
750
 
@@ -760,7 +760,7 @@ user.name = "New Name";
760
760
  user.preventPersist(); // Changes won't be saved
761
761
  ```
762
762
 
763
- #### modelBase.delete · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
763
+ #### modelBase.delete · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
764
764
 
765
765
  Delete this model instance from the database.
766
766
 
@@ -775,7 +775,7 @@ const user = User.get("user123");
775
775
  user.delete(); // Removes from database
776
776
  ```
777
777
 
778
- #### modelBase.validate · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
778
+ #### modelBase.validate · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
779
779
 
780
780
  Validate all fields in this model instance.
781
781
 
@@ -797,7 +797,7 @@ if (errors.length > 0) {
797
797
  }
798
798
  ```
799
799
 
800
- #### modelBase.isValid · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
800
+ #### modelBase.isValid · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
801
801
 
802
802
  Check if this model instance is valid.
803
803
 
@@ -812,31 +812,41 @@ const user = new User({name: "John"});
812
812
  if (!user.isValid()) shoutAtTheUser();
813
813
  ```
814
814
 
815
- #### modelBase.getState · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
815
+ #### modelBase.getState · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
816
816
 
817
817
  **Signature:** `() => "created" | "deleted" | "loaded" | "lazy"`
818
818
 
819
- #### modelBase.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
819
+ #### modelBase.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
820
820
 
821
821
  **Signature:** `() => string`
822
822
 
823
- #### modelBase.[Symbol.for('nodejs.util.inspect.custom')] · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
823
+ #### modelBase.[Symbol.for('nodejs.util.inspect.custom')] · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
824
824
 
825
825
  **Signature:** `() => string`
826
826
 
827
- ### defineModel · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
827
+ ### ModelLookup · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L96)
828
+
829
+ **Type Parameters:**
830
+
831
+ - `PKA extends readonly any[] = readonly any[]`
832
+
833
+ #### modelLookup.get · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L101)
834
+
835
+ **Type:** `(...args: PKA) => any`
836
+
837
+ ### defineModel · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
828
838
 
829
839
  Register a model class with the Edinburgh ORM system.
830
840
 
831
841
  Converts a plain class into a fully-featured model with database persistence,
832
842
  typed fields, primary key access, and optional secondary and unique indexes.
833
843
 
834
- **Signature:** `<T extends new () => any, const PK extends (keyof FieldsOf<T> & string) | readonly (keyof FieldsOf<T> & string)[], const UNIQUE extends Record<string, IndexSpec<T>>, const INDEX extends Record<string, IndexSpec<T>>>(tableName: string, cls: T, opts?: { ...; }) => ModelClass<...>`
844
+ **Signature:** `<T extends new () => any, const PK extends (keyof ModelFields<T> & string) | readonly (keyof ModelFields<T> & string)[], const UNIQUE extends Record<string, IndexSpec<T>>, const INDEX extends Record<string, IndexSpec<T>>>(tableName: string, cls: T, opts?: { ...; }) => ModelClass<...>`
835
845
 
836
846
  **Type Parameters:**
837
847
 
838
848
  - `T extends new () => any`
839
- - `PK extends (keyof FieldsOf<T> & string) | readonly (keyof FieldsOf<T> & string)[]`
849
+ - `PK extends (keyof ModelFields<T> & string) | readonly (keyof ModelFields<T> & string)[]`
840
850
  - `UNIQUE extends Record<string, IndexSpec<T>>`
841
851
  - `INDEX extends Record<string, IndexSpec<T>>`
842
852
 
@@ -848,7 +858,7 @@ typed fields, primary key access, and optional secondary and unique indexes.
848
858
 
849
859
  **Returns:** The enhanced model constructor.
850
860
 
851
- ### deleteEverything · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
861
+ ### deleteEverything · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
852
862
 
853
863
  Delete every key/value entry in the database and reinitialize all registered models.
854
864
 
@@ -887,13 +897,13 @@ const User = E.defineModel("User", class {
887
897
  });
888
898
  ```
889
899
 
890
- ### string · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
900
+ ### string · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
891
901
 
892
902
  Type wrapper instance for the string type.
893
903
 
894
904
  **Value:** `TypeWrapper<string>`
895
905
 
896
- ### orderedString · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
906
+ ### orderedString · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
897
907
 
898
908
  Type wrapper instance for the ordered string type, which is just like a string
899
909
  except that it sorts lexicographically in the database (instead of by incrementing
@@ -903,37 +913,37 @@ may not contain null characters.
903
913
 
904
914
  **Value:** `TypeWrapper<string>`
905
915
 
906
- ### number · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
916
+ ### number · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
907
917
 
908
918
  Type wrapper instance for the number type.
909
919
 
910
920
  **Value:** `TypeWrapper<number>`
911
921
 
912
- ### dateTime · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
922
+ ### dateTime · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
913
923
 
914
924
  Type wrapper instance for the date/time type. Stored without timezone info, rounded to whole seconds.
915
925
 
916
926
  **Value:** `TypeWrapper<Date>`
917
927
 
918
- ### boolean · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
928
+ ### boolean · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
919
929
 
920
930
  Type wrapper instance for the boolean type.
921
931
 
922
932
  **Value:** `TypeWrapper<boolean>`
923
933
 
924
- ### identifier · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
934
+ ### identifier · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
925
935
 
926
936
  Type wrapper instance for the identifier type.
927
937
 
928
938
  **Value:** `TypeWrapper<string>`
929
939
 
930
- ### undef · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
940
+ ### undef · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
931
941
 
932
942
  Type wrapper instance for the 'undefined' type.
933
943
 
934
944
  **Value:** `TypeWrapper<undefined>`
935
945
 
936
- ### opt · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
946
+ ### opt · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
937
947
 
938
948
  Create an optional type wrapper (allows undefined).
939
949
 
@@ -956,7 +966,7 @@ const optionalString = E.opt(E.string);
956
966
  const optionalNumber = E.opt(E.number);
957
967
  ```
958
968
 
959
- ### or · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
969
+ ### or · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
960
970
 
961
971
  Create a union type wrapper from multiple type choices.
962
972
 
@@ -979,7 +989,7 @@ const stringOrNumber = E.or(E.string, E.number);
979
989
  const status = E.or("active", "inactive", "pending");
980
990
  ```
981
991
 
982
- ### array · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
992
+ ### array · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
983
993
 
984
994
  Create an array type wrapper with optional length constraints.
985
995
 
@@ -1003,7 +1013,7 @@ const stringArray = E.array(E.string);
1003
1013
  const boundedArray = E.array(E.number, {min: 1, max: 10});
1004
1014
  ```
1005
1015
 
1006
- ### set · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
1016
+ ### set · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
1007
1017
 
1008
1018
  Create a Set type wrapper with optional length constraints.
1009
1019
 
@@ -1027,7 +1037,7 @@ const stringSet = E.set(E.string);
1027
1037
  const boundedSet = E.set(E.number, {min: 1, max: 10});
1028
1038
  ```
1029
1039
 
1030
- ### record · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
1040
+ ### record · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
1031
1041
 
1032
1042
  Create a Record type wrapper for key-value objects with string or number keys.
1033
1043
 
@@ -1049,7 +1059,7 @@ Create a Record type wrapper for key-value objects with string or number keys.
1049
1059
  const scores = E.record(E.number); // Record<string | number, number>
1050
1060
  ```
1051
1061
 
1052
- ### literal · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
1062
+ ### literal · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
1053
1063
 
1054
1064
  Create a literal type wrapper for a constant value.
1055
1065
 
@@ -1072,11 +1082,11 @@ const statusType = E.literal("active");
1072
1082
  const countType = E.literal(42);
1073
1083
  ```
1074
1084
 
1075
- ### link · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
1085
+ ### link · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
1076
1086
 
1077
1087
  Create a link type wrapper for model relationships.
1078
1088
 
1079
- **Signature:** `{ <const T extends new (...args: any[]) => Model<any>>(TargetModel: T): TypeWrapper<InstanceType<T>> & QueryArgCarrier<InstanceType<T> | LinkPrimaryKeyInput<LinkTargetPKArgs<T>>>; <const T extends new (...args: any[]) => Model<any>>(TargetModel: () => T): TypeWrapper<...>; }`
1089
+ **Signature:** `{ <const T extends new (...args: any[]) => Model<any>>(TargetModel: T): TypeWrapper<InstanceType<T>>; <const T extends new (...args: any[]) => Model<any>>(TargetModel: () => T): TypeWrapper<...>; }`
1080
1090
 
1081
1091
  **Type Parameters:**
1082
1092
 
@@ -1102,7 +1112,7 @@ const Book = E.defineModel("Book", class {
1102
1112
  }, { pk: "id" });
1103
1113
  ```
1104
1114
 
1105
- ### dump · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
1115
+ ### dump · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L264)
1106
1116
 
1107
1117
  **Signature:** `() => void`
1108
1118
 
@@ -1147,7 +1157,7 @@ that slot.
1147
1157
  & (FETCH extends undefined ? { fetch?: undefined } : { fetch: FETCH })
1148
1158
  )`
1149
1159
 
1150
- ### IndexRangeIterator · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L90)
1160
+ ### IndexRangeIterator · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L88)
1151
1161
 
1152
1162
  Iterator for range queries on indexes.
1153
1163
  Handles common iteration logic for both primary and unique indexes.
@@ -1157,15 +1167,15 @@ Extends built-in Iterator to provide map/filter/reduce/toArray/etc.
1157
1167
 
1158
1168
  - `ITEM`
1159
1169
 
1160
- #### indexRangeIterator.[Symbol.iterator] · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L100)
1170
+ #### indexRangeIterator.[Symbol.iterator] · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L101)
1161
1171
 
1162
1172
  **Signature:** `() => this`
1163
1173
 
1164
- #### indexRangeIterator.next · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L102)
1174
+ #### indexRangeIterator.next · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L101)
1165
1175
 
1166
1176
  **Signature:** `() => IteratorResult<ITEM, any>`
1167
1177
 
1168
- #### indexRangeIterator.count · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L110)
1178
+ #### indexRangeIterator.count · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L111)
1169
1179
 
1170
1180
  **Signature:** `() => number`
1171
1181
 
@@ -1173,11 +1183,11 @@ Extends built-in Iterator to provide map/filter/reduce/toArray/etc.
1173
1183
 
1174
1184
  **Signature:** `() => ITEM`
1175
1185
 
1176
- ### Change · [type](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L93)
1186
+ ### Change · [type](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L91)
1177
1187
 
1178
1188
  **Type:** `Record<any, any> | "created" | "deleted"`
1179
1189
 
1180
- ### FieldConfig · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L40)
1190
+ ### FieldConfig · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L39)
1181
1191
 
1182
1192
  Configuration interface for model fields.
1183
1193
 
@@ -1185,7 +1195,7 @@ Configuration interface for model fields.
1185
1195
 
1186
1196
  - `T` - The field type.
1187
1197
 
1188
- #### fieldConfig.type · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L45)
1198
+ #### fieldConfig.type · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L46)
1189
1199
 
1190
1200
  The type wrapper that defines how this field is serialized/validated.
1191
1201
 
@@ -1203,7 +1213,7 @@ Optional default value or function that generates default values.
1203
1213
 
1204
1214
  **Type:** `T | ((model: Record<string, any>) => T)`
1205
1215
 
1206
- ### TypeWrapper · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L36)
1216
+ ### TypeWrapper · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L35)
1207
1217
 
1208
1218
  **Type Parameters:**
1209
1219
 
@@ -1215,7 +1225,7 @@ A string identifier for this type, used during serialization
1215
1225
 
1216
1226
  **Type:** `string`
1217
1227
 
1218
- #### typeWrapper.serialize · [abstract method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L52)
1228
+ #### typeWrapper.serialize · [abstract method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L53)
1219
1229
 
1220
1230
  Serialize a value from an object property to a Pack.
1221
1231
 
@@ -1226,7 +1236,7 @@ Serialize a value from an object property to a Pack.
1226
1236
  - `value: T` - The value to serialize.
1227
1237
  - `pack: DataPack` - The Pack instance to write to.
1228
1238
 
1229
- #### typeWrapper.deserialize · [abstract method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L59)
1239
+ #### typeWrapper.deserialize · [abstract method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L60)
1230
1240
 
1231
1241
  Deserialize a value from a Pack into an object property.
1232
1242
 
@@ -1236,7 +1246,7 @@ Deserialize a value from a Pack into an object property.
1236
1246
 
1237
1247
  - `pack: DataPack` - The Pack instance to read from.
1238
1248
 
1239
- #### typeWrapper.getError · [abstract method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L66)
1249
+ #### typeWrapper.getError · [abstract method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L67)
1240
1250
 
1241
1251
  Validate a value.
1242
1252
 
@@ -1248,7 +1258,7 @@ Validate a value.
1248
1258
 
1249
1259
  **Returns:** - A DatabaseError if validation fails.
1250
1260
 
1251
- #### typeWrapper.serializeType · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L68)
1261
+ #### typeWrapper.serializeType · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L69)
1252
1262
 
1253
1263
  Serialize type metadata to a Pack (for schema serialization).
1254
1264
 
@@ -1291,11 +1301,11 @@ Check if indexing should be skipped for this field value.
1291
1301
  - `value1: T`
1292
1302
  - `value2: T`
1293
1303
 
1294
- #### typeWrapper.getLinkedModel · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L90)
1304
+ #### typeWrapper.getLinkedModel · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L91)
1295
1305
 
1296
1306
  **Signature:** `() => AnyModelClass`
1297
1307
 
1298
- ### DatabaseError · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L182)
1308
+ ### DatabaseError · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L181)
1299
1309
 
1300
1310
  The DatabaseError class is used to represent errors that occur during database operations.
1301
1311
  It extends the built-in Error class and has a machine readable error code string property.
@@ -1318,13 +1328,13 @@ convert old primary indices, rewrite row data, and clean up orphaned indices.
1318
1328
 
1319
1329
  ### MigrationOptions · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L13)
1320
1330
 
1321
- #### migrationOptions.tables · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L18)
1331
+ #### migrationOptions.tables · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L19)
1322
1332
 
1323
1333
  Limit migration to specific table names.
1324
1334
 
1325
1335
  **Type:** `string[]`
1326
1336
 
1327
- #### migrationOptions.populateSecondaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L22)
1337
+ #### migrationOptions.populateSecondaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L21)
1328
1338
 
1329
1339
  Populate secondary indexes for rows at old schema versions (default: true).
1330
1340
 
@@ -1336,27 +1346,27 @@ Convert old primary indices when primary key fields changed (default: true).
1336
1346
 
1337
1347
  **Type:** `boolean`
1338
1348
 
1339
- #### migrationOptions.rewriteData · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L38)
1349
+ #### migrationOptions.rewriteData · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L37)
1340
1350
 
1341
1351
  Rewrite all row data to the latest schema version (default: false).
1342
1352
 
1343
1353
  **Type:** `boolean`
1344
1354
 
1345
- #### migrationOptions.removeOrphans · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L45)
1355
+ #### migrationOptions.removeOrphans · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L44)
1346
1356
 
1347
1357
  Delete orphaned secondary/unique index entries (default: true).
1348
1358
 
1349
1359
  **Type:** `boolean`
1350
1360
 
1351
- #### migrationOptions.onProgress · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L45)
1361
+ #### migrationOptions.onProgress · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L46)
1352
1362
 
1353
1363
  Progress callback.
1354
1364
 
1355
1365
  **Type:** `(info: ProgressInfo) => void`
1356
1366
 
1357
- ### MigrationResult · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L50)
1367
+ ### MigrationResult · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L51)
1358
1368
 
1359
- #### migrationResult.secondaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L52)
1369
+ #### migrationResult.secondaries · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L53)
1360
1370
 
1361
1371
  Per-table counts of secondary index entries populated.
1362
1372
 
@@ -1368,35 +1378,35 @@ Per-table counts of old primary rows migrated.
1368
1378
 
1369
1379
  **Type:** `Record<string, number>`
1370
1380
 
1371
- #### migrationResult.conversionFailures · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L59)
1381
+ #### migrationResult.conversionFailures · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L60)
1372
1382
 
1373
1383
  Per-table conversion failure counts by reason.
1374
1384
 
1375
1385
  **Type:** `Record<string, Record<string, number>>`
1376
1386
 
1377
- #### migrationResult.rewritten · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L63)
1387
+ #### migrationResult.rewritten · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L64)
1378
1388
 
1379
1389
  Per-table counts of rows rewritten to latest version.
1380
1390
 
1381
1391
  **Type:** `Record<string, number>`
1382
1392
 
1383
- #### migrationResult.orphans · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L67)
1393
+ #### migrationResult.orphans · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L68)
1384
1394
 
1385
1395
  Number of orphaned index entries deleted.
1386
1396
 
1387
1397
  **Type:** `number`
1388
1398
 
1389
- ### Transaction · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L54)
1399
+ ### Transaction · [interface](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L55)
1390
1400
 
1391
- #### transaction.id · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L55)
1401
+ #### transaction.id · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L56)
1392
1402
 
1393
1403
  **Type:** `number`
1394
1404
 
1395
- #### transaction.instances · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L56)
1405
+ #### transaction.instances · [member](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L57)
1396
1406
 
1397
- **Type:** `Map<number, ModelBase>`
1407
+ **Type:** `Map<number, ModelBase<ModelLookup<readonly any[]>>>`
1398
1408
 
1399
- ### txnStorage · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L59)
1409
+ ### txnStorage · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L60)
1400
1410
 
1401
1411
  **Value:** `AsyncLocalStorage<Transaction>`
1402
1412
 
@@ -1,5 +1,5 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
- export { Model, type ModelClass, type AnyModelClass, type ModelBase, defineModel, deleteEverything, field, } from "./models.js";
2
+ export { Model, type ModelClass, type AnyModelClass, type ModelBase, type ModelLookup, defineModel, deleteEverything, field, } from "./models.js";
3
3
  import type { Change, Model } from "./models.js";
4
4
  export { string, orderedString, number, dateTime, boolean, identifier, undef, opt, or, array, set, record, literal, link, } from "./types.js";
5
5
  export { dump, } from "./indexes.js";
@@ -85,7 +85,7 @@ export declare function setMaxRetryCount(count: number): void;
85
85
  * - A sequential number. Higher numbers have been committed after lower numbers.
86
86
  * - A map of model instances to their changes. The change can be "created", "deleted", or an object containing the old values.
87
87
  *
88
- * The callback is called within a new transaction context, allowing lazy-loads to happen. However, any
89
- * changes made to Edinburgh models will not be saved.
88
+ * The callback is called within a new transaction context at or after the committed state, so lazy-loads
89
+ * and additional writes are allowed.
90
90
  */
91
91
  export declare function setOnSaveCallback(callback: ((commitId: number, items: Map<Model<unknown>, Change>) => void) | undefined): void;
@@ -111,6 +111,7 @@ export async function transact(fn) {
111
111
  const txnId = lowlevel.startTransaction();
112
112
  const txn = { id: txnId, instances: new Map() };
113
113
  const onSaveItems = onSaveCallback ? new Map() : undefined;
114
+ let retry = false;
114
115
  let result;
115
116
  try {
116
117
  await txnStorage.run(txn, async function () {
@@ -131,6 +132,29 @@ export async function transact(fn) {
131
132
  onSaveItems.set(instance, change);
132
133
  }
133
134
  }
135
+ if (onSaveItems?.size) {
136
+ // Perform writes, and start a new transaction at or past the point-in-time of our commit
137
+ const commitResult = lowlevel.commitTransaction(txnId, true);
138
+ if (typeof commitResult === 'object') {
139
+ const commitSeq = await commitResult;
140
+ if (commitSeq <= 0) {
141
+ try {
142
+ lowlevel.abortTransaction(txnId);
143
+ }
144
+ catch { }
145
+ retry = true;
146
+ return;
147
+ }
148
+ // Run the callback within our renewed transaction context, so it can fetch linked lazy fields if needed
149
+ try {
150
+ onSaveCallback(commitSeq, onSaveItems);
151
+ }
152
+ catch (e) {
153
+ throw e;
154
+ }
155
+ }
156
+ // else: only reads
157
+ }
134
158
  });
135
159
  }
136
160
  catch (e) {
@@ -140,18 +164,8 @@ export async function transact(fn) {
140
164
  catch { }
141
165
  throw e;
142
166
  }
143
- if (onSaveItems?.size) {
144
- // Perform writes, and start a new transaction at or past the point-in-time of our commit
145
- const commitResult = lowlevel.commitTransaction(txnId, true);
146
- if (typeof commitResult === 'object') {
147
- const commitSeq = await commitResult;
148
- if (commitSeq <= 0)
149
- continue; // Race condition - retry
150
- // Run the callback within our new transaction context, so it can fetch linked lazy fields if needed
151
- onSaveCallback(commitSeq, onSaveItems);
152
- }
153
- // else: only reads
154
- }
167
+ if (retry)
168
+ continue;
155
169
  // Make the instances read-only to make it clear that their transaction has ended.
156
170
  for (const instance of txn.instances.values()) {
157
171
  delete instance._oldValues;
@@ -192,8 +206,8 @@ let onSaveCallback;
192
206
  * - A sequential number. Higher numbers have been committed after lower numbers.
193
207
  * - A map of model instances to their changes. The change can be "created", "deleted", or an object containing the old values.
194
208
  *
195
- * The callback is called within a new transaction context, allowing lazy-loads to happen. However, any
196
- * changes made to Edinburgh models will not be saved.
209
+ * The callback is called within a new transaction context at or after the committed state, so lazy-loads
210
+ * and additional writes are allowed.
197
211
  */
198
212
  export function setOnSaveCallback(callback) {
199
213
  onSaveCallback = callback;