edinburgh 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -553,7 +553,7 @@ await E.transact(() => {
553
553
  });
554
554
  ```
555
555
 
556
- ### setMaxRetryCount · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L226)
556
+ ### setMaxRetryCount · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L232)
557
557
 
558
558
  Set the maximum number of retries for a transaction in case of conflicts.
559
559
  The default value is 6. Setting it to 0 will disable retries and cause transactions to fail immediately on conflict.
@@ -564,7 +564,7 @@ The default value is 6. Setting it to 0 will disable retries and cause transacti
564
564
 
565
565
  - `count: number` - The maximum number of retries for a transaction.
566
566
 
567
- ### setOnSaveCallback · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L240)
567
+ ### setOnSaveCallback · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L249)
568
568
 
569
569
  Set a callback function to be called after a model is saved and committed.
570
570
 
@@ -577,11 +577,14 @@ Set a callback function to be called after a model is saved and committed.
577
577
  - A sequential number. Higher numbers have been committed after lower numbers.
578
578
  - A map of model instances to their changes. The change can be "created", "deleted", or an object containing the old values.
579
579
 
580
- ### Model · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
580
+ The callback is called within a new transaction context, allowing lazy-loads to happen. However, any
581
+ changes made to Edinburgh models will not be saved.
582
+
583
+ ### Model · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
581
584
 
582
585
  **Type:** `typeof ModelBase`
583
586
 
584
- ### ModelClass · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
587
+ ### ModelClass · [class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
585
588
 
586
589
  Runtime base constructor for model classes returned by `defineModel()`.
587
590
 
@@ -598,7 +601,7 @@ Useful when accepting or storing arbitrary registered model classes.
598
601
 
599
602
  **Type:** `ModelClass<new () => any, readonly any[], any, any>`
600
603
 
601
- ### ModelBase · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
604
+ ### ModelBase · [abstract class](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
602
605
 
603
606
  Base class for all database models in the Edinburgh ORM.
604
607
 
@@ -651,7 +654,7 @@ const User = E.defineModel("User", class {
651
654
  type User = InstanceType<typeof User>;
652
655
  ```
653
656
 
654
- #### ModelBase.migrate · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
657
+ #### ModelBase.migrate · [static method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
655
658
 
656
659
  Optional migration function called when deserializing rows written with an older schema version.
657
660
  Receives a plain record with all fields and should mutate it in-place to match the current schema.
@@ -678,7 +681,7 @@ const User = E.defineModel("User", class {
678
681
  }, { pk: "id" });
679
682
  ```
680
683
 
681
- #### modelBase.preCommit · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
684
+ #### modelBase.preCommit · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
682
685
 
683
686
  Optional hook called on each modified instance right before the transaction commits.
684
687
  Runs before data is written to disk, so changes made here are included in the commit.
@@ -705,19 +708,19 @@ const Post = E.defineModel("Post", class {
705
708
  }, { pk: "id" });
706
709
  ```
707
710
 
708
- #### modelBase.getPrimaryKey · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
711
+ #### modelBase.getPrimaryKey · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
709
712
 
710
713
  **Signature:** `() => Uint8Array<ArrayBufferLike>`
711
714
 
712
715
  **Returns:** The primary key for this instance.
713
716
 
714
- #### modelBase.getPrimaryKeyHash · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
717
+ #### modelBase.getPrimaryKeyHash · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
715
718
 
716
719
  **Signature:** `() => number`
717
720
 
718
721
  **Returns:** A 53-bit positive integer non-cryptographic hash of the primary key, or undefined if not yet saved.
719
722
 
720
- #### modelBase.isLazyField · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
723
+ #### modelBase.isLazyField · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
721
724
 
722
725
  **Signature:** `(field: keyof this) => boolean`
723
726
 
@@ -725,7 +728,7 @@ const Post = E.defineModel("Post", class {
725
728
 
726
729
  - `field: keyof this`
727
730
 
728
- #### modelBase.preventPersist · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
731
+ #### modelBase.preventPersist · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
729
732
 
730
733
  Prevent this instance from being persisted to the database.
731
734
 
@@ -741,7 +744,7 @@ user.name = "New Name";
741
744
  user.preventPersist(); // Changes won't be saved
742
745
  ```
743
746
 
744
- #### modelBase.delete · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
747
+ #### modelBase.delete · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
745
748
 
746
749
  Delete this model instance from the database.
747
750
 
@@ -756,7 +759,7 @@ const user = User.get("user123");
756
759
  user.delete(); // Removes from database
757
760
  ```
758
761
 
759
- #### modelBase.validate · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
762
+ #### modelBase.validate · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
760
763
 
761
764
  Validate all fields in this model instance.
762
765
 
@@ -778,7 +781,7 @@ if (errors.length > 0) {
778
781
  }
779
782
  ```
780
783
 
781
- #### modelBase.isValid · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
784
+ #### modelBase.isValid · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
782
785
 
783
786
  Check if this model instance is valid.
784
787
 
@@ -793,19 +796,19 @@ const user = new User({name: "John"});
793
796
  if (!user.isValid()) shoutAtTheUser();
794
797
  ```
795
798
 
796
- #### modelBase.getState · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
799
+ #### modelBase.getState · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
797
800
 
798
801
  **Signature:** `() => "created" | "deleted" | "loaded" | "lazy"`
799
802
 
800
- #### modelBase.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
803
+ #### modelBase.toString · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
801
804
 
802
805
  **Signature:** `() => string`
803
806
 
804
- #### modelBase.[Symbol.for('nodejs.util.inspect.custom')] · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
807
+ #### modelBase.[Symbol.for('nodejs.util.inspect.custom')] · [method](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
805
808
 
806
809
  **Signature:** `() => string`
807
810
 
808
- ### defineModel · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
811
+ ### defineModel · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
809
812
 
810
813
  Register a model class with the Edinburgh ORM system.
811
814
 
@@ -829,7 +832,7 @@ typed fields, primary key access, and optional secondary and unique indexes.
829
832
 
830
833
  **Returns:** The enhanced model constructor.
831
834
 
832
- ### deleteEverything · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
835
+ ### deleteEverything · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
833
836
 
834
837
  Delete every key/value entry in the database and reinitialize all registered models.
835
838
 
@@ -868,13 +871,13 @@ const User = E.defineModel("User", class {
868
871
  });
869
872
  ```
870
873
 
871
- ### string · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
874
+ ### string · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
872
875
 
873
876
  Type wrapper instance for the string type.
874
877
 
875
878
  **Value:** `TypeWrapper<string>`
876
879
 
877
- ### orderedString · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
880
+ ### orderedString · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
878
881
 
879
882
  Type wrapper instance for the ordered string type, which is just like a string
880
883
  except that it sorts lexicographically in the database (instead of by incrementing
@@ -884,37 +887,37 @@ may not contain null characters.
884
887
 
885
888
  **Value:** `TypeWrapper<string>`
886
889
 
887
- ### number · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
890
+ ### number · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
888
891
 
889
892
  Type wrapper instance for the number type.
890
893
 
891
894
  **Value:** `TypeWrapper<number>`
892
895
 
893
- ### dateTime · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
896
+ ### dateTime · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
894
897
 
895
898
  Type wrapper instance for the date/time type. Stored without timezone info, rounded to whole seconds.
896
899
 
897
900
  **Value:** `TypeWrapper<Date>`
898
901
 
899
- ### boolean · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
902
+ ### boolean · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
900
903
 
901
904
  Type wrapper instance for the boolean type.
902
905
 
903
906
  **Value:** `TypeWrapper<boolean>`
904
907
 
905
- ### identifier · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
908
+ ### identifier · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
906
909
 
907
910
  Type wrapper instance for the identifier type.
908
911
 
909
912
  **Value:** `TypeWrapper<string>`
910
913
 
911
- ### undef · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
914
+ ### undef · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
912
915
 
913
916
  Type wrapper instance for the 'undefined' type.
914
917
 
915
918
  **Value:** `TypeWrapper<undefined>`
916
919
 
917
- ### opt · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
920
+ ### opt · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
918
921
 
919
922
  Create an optional type wrapper (allows undefined).
920
923
 
@@ -937,7 +940,7 @@ const optionalString = E.opt(E.string);
937
940
  const optionalNumber = E.opt(E.number);
938
941
  ```
939
942
 
940
- ### or · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
943
+ ### or · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
941
944
 
942
945
  Create a union type wrapper from multiple type choices.
943
946
 
@@ -960,7 +963,7 @@ const stringOrNumber = E.or(E.string, E.number);
960
963
  const status = E.or("active", "inactive", "pending");
961
964
  ```
962
965
 
963
- ### array · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
966
+ ### array · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
964
967
 
965
968
  Create an array type wrapper with optional length constraints.
966
969
 
@@ -984,7 +987,7 @@ const stringArray = E.array(E.string);
984
987
  const boundedArray = E.array(E.number, {min: 1, max: 10});
985
988
  ```
986
989
 
987
- ### set · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
990
+ ### set · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
988
991
 
989
992
  Create a Set type wrapper with optional length constraints.
990
993
 
@@ -1008,7 +1011,7 @@ const stringSet = E.set(E.string);
1008
1011
  const boundedSet = E.set(E.number, {min: 1, max: 10});
1009
1012
  ```
1010
1013
 
1011
- ### record · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
1014
+ ### record · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
1012
1015
 
1013
1016
  Create a Record type wrapper for key-value objects with string or number keys.
1014
1017
 
@@ -1030,7 +1033,7 @@ Create a Record type wrapper for key-value objects with string or number keys.
1030
1033
  const scores = E.record(E.number); // Record<string | number, number>
1031
1034
  ```
1032
1035
 
1033
- ### literal · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
1036
+ ### literal · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
1034
1037
 
1035
1038
  Create a literal type wrapper for a constant value.
1036
1039
 
@@ -1053,7 +1056,7 @@ const statusType = E.literal("active");
1053
1056
  const countType = E.literal(42);
1054
1057
  ```
1055
1058
 
1056
- ### link · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
1059
+ ### link · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
1057
1060
 
1058
1061
  Create a link type wrapper for model relationships.
1059
1062
 
@@ -1083,7 +1086,7 @@ const Book = E.defineModel("Book", class {
1083
1086
  }, { pk: "id" });
1084
1087
  ```
1085
1088
 
1086
- ### dump · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L243)
1089
+ ### dump · [function](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L252)
1087
1090
 
1088
1091
  **Signature:** `() => void`
1089
1092
 
@@ -1273,7 +1276,7 @@ Check if indexing should be skipped for this field value.
1273
1276
 
1274
1277
  **Signature:** `() => AnyModelClass`
1275
1278
 
1276
- ### DatabaseError · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L166)
1279
+ ### DatabaseError · [constant](https://github.com/vanviegen/edinburgh/blob/main/src/edinburgh.ts#L174)
1277
1280
 
1278
1281
  The DatabaseError class is used to represent errors that occur during database operations.
1279
1282
  It extends the built-in Error class and has a machine readable error code string property.
@@ -84,5 +84,8 @@ export declare function setMaxRetryCount(count: number): void;
84
84
  * `transact()` commit that has changes, with the following arguments:
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
+ *
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.
87
90
  */
88
91
  export declare function setOnSaveCallback(callback: ((commitId: number, items: Map<Model<unknown>, Change>) => void) | undefined): void;
@@ -140,27 +140,31 @@ export async function transact(fn) {
140
140
  catch { }
141
141
  throw e;
142
142
  }
143
- finally {
144
- // Make the instances read-only to make it clear that their transaction has ended.
145
- for (const instance of txn.instances.values()) {
146
- delete instance._oldValues;
147
- Object.defineProperty(instance, "_txn", STALE_INSTANCE_DESCRIPTOR);
148
- Object.freeze(instance);
149
- }
150
- // Destroy the transaction object, to make sure things crash if they are used after
151
- // this point, and to help the GC reclaim memory.
152
- txn.id = txn.instances = undefined;
153
- }
154
- const commitResult = lowlevel.commitTransaction(txnId);
155
- const commitSeq = typeof commitResult === 'number' ? commitResult : await commitResult;
156
- if (commitSeq > 0) {
157
- // Success
158
- if (onSaveItems?.size) {
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
159
151
  onSaveCallback(commitSeq, onSaveItems);
160
152
  }
161
- return result;
153
+ // else: only reads
162
154
  }
163
- // Race condition - retry
155
+ // Make the instances read-only to make it clear that their transaction has ended.
156
+ for (const instance of txn.instances.values()) {
157
+ delete instance._oldValues;
158
+ Object.defineProperty(instance, "_txn", STALE_INSTANCE_DESCRIPTOR);
159
+ Object.freeze(instance);
160
+ }
161
+ // Destroy the transaction object, to make sure things crash if they are used after
162
+ // this point, and to help the GC reclaim memory.
163
+ txn.id = txn.instances = undefined;
164
+ // Commit the transaction and actually end it
165
+ if ((await lowlevel.commitTransaction(txnId)) <= 0)
166
+ continue; // Race condition - retry
167
+ return result;
164
168
  }
165
169
  throw new DatabaseError("Transaction keeps getting raced", "RACING_TRANSACTION");
166
170
  // } catch (e: Error | any) {
@@ -187,6 +191,9 @@ let onSaveCallback;
187
191
  * `transact()` commit that has changes, with the following arguments:
188
192
  * - A sequential number. Higher numbers have been committed after lower numbers.
189
193
  * - A map of model instances to their changes. The change can be "created", "deleted", or an object containing the old values.
194
+ *
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.
190
197
  */
191
198
  export function setOnSaveCallback(callback) {
192
199
  onSaveCallback = callback;
@@ -1 +1 @@
1
- {"version":3,"file":"edinburgh.js","sourceRoot":"","sources":["../../src/edinburgh.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"edinburgh.js","sourceRoot":"","sources":["../../src/edinburgh.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edinburgh",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "author": "Frank van Viegen",
5
5
  "devDependencies": {
6
6
  "@types/bun": "^1.2.16",
@@ -56,6 +56,6 @@
56
56
  ],
57
57
  "type": "module",
58
58
  "dependencies": {
59
- "olmdb": "^0.4.1"
59
+ "olmdb": "^0.4.2"
60
60
  }
61
61
  }
@@ -10,3 +10,6 @@ Set a callback function to be called after a model is saved and committed.
10
10
  `transact()` commit that has changes, with the following arguments:
11
11
  - A sequential number. Higher numbers have been committed after lower numbers.
12
12
  - A map of model instances to their changes. The change can be "created", "deleted", or an object containing the old values.
13
+
14
+ The callback is called within a new transaction context, allowing lazy-loads to happen. However, any
15
+ changes made to Edinburgh models will not be saved.
package/src/edinburgh.ts CHANGED
@@ -183,30 +183,36 @@ export async function transact<T>(fn: () => T): Promise<T> {
183
183
  } catch (e: any) {
184
184
  try { lowlevel.abortTransaction(txnId); } catch {}
185
185
  throw e;
186
- } finally {
187
- // Make the instances read-only to make it clear that their transaction has ended.
188
- for (const instance of txn.instances.values()) {
189
- delete instance._oldValues;
190
- Object.defineProperty(instance, "_txn", STALE_INSTANCE_DESCRIPTOR);
191
- Object.freeze(instance);
192
- }
193
- // Destroy the transaction object, to make sure things crash if they are used after
194
- // this point, and to help the GC reclaim memory.
195
- txn.id = txn.instances = undefined as any;
196
186
  }
197
187
 
198
- const commitResult = lowlevel.commitTransaction(txnId);
199
- const commitSeq = typeof commitResult === 'number' ? commitResult : await commitResult;
200
-
201
- if (commitSeq > 0) {
202
- // Success
203
- if (onSaveItems?.size) {
188
+ if (onSaveItems?.size) {
189
+ // Perform writes, and start a new transaction at or past the point-in-time of our commit
190
+ const commitResult = lowlevel.commitTransaction(txnId, true);
191
+ if (typeof commitResult === 'object') {
192
+ const commitSeq = await commitResult;
193
+ if (commitSeq <= 0) continue; // Race condition - retry
194
+ // Run the callback within our new transaction context, so it can fetch linked lazy fields if needed
204
195
  onSaveCallback!(commitSeq, onSaveItems);
205
196
  }
206
- return result as T;
197
+ // else: only reads
198
+ }
199
+
200
+ // Make the instances read-only to make it clear that their transaction has ended.
201
+ for (const instance of txn.instances.values()) {
202
+ delete instance._oldValues;
203
+ Object.defineProperty(instance, "_txn", STALE_INSTANCE_DESCRIPTOR);
204
+ Object.freeze(instance);
207
205
  }
208
206
 
209
- // Race condition - retry
207
+ // Destroy the transaction object, to make sure things crash if they are used after
208
+ // this point, and to help the GC reclaim memory.
209
+ txn.id = txn.instances = undefined as any;
210
+
211
+
212
+ // Commit the transaction and actually end it
213
+ if ((await lowlevel.commitTransaction(txnId)) <= 0) continue; // Race condition - retry
214
+
215
+ return result as T;
210
216
  }
211
217
  throw new DatabaseError("Transaction keeps getting raced", "RACING_TRANSACTION");
212
218
  // } catch (e: Error | any) {
@@ -236,6 +242,9 @@ let onSaveCallback: ((commitId: number, items: Map<Model<unknown>, Change>) => v
236
242
  * `transact()` commit that has changes, with the following arguments:
237
243
  * - A sequential number. Higher numbers have been committed after lower numbers.
238
244
  * - A map of model instances to their changes. The change can be "created", "deleted", or an object containing the old values.
245
+ *
246
+ * The callback is called within a new transaction context, allowing lazy-loads to happen. However, any
247
+ * changes made to Edinburgh models will not be saved.
239
248
  */
240
249
  export function setOnSaveCallback(callback: ((commitId: number, items: Map<Model<unknown>, Change>) => void) | undefined) {
241
250
  onSaveCallback = callback;