edinburgh 0.1.3 → 0.3.0

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/src/edinburgh.ts CHANGED
@@ -1,20 +1,23 @@
1
1
  import * as olmdb from "olmdb";
2
- import { Model, MODIFIED_INSTANCES_SYMBOL, resetModelCaches } from "./models.js";
2
+ import { Model, INSTANCES_SYMBOL, resetModelCaches } from "./models.js";
3
+ import { INSTANCES_BY_PK_SYMBOL } from "./indexes.js";
3
4
 
4
5
  // Re-export public API from models
5
6
  export {
6
7
  Model,
7
8
  registerModel,
8
9
  field,
9
- setOnSaveCallback
10
10
  } from "./models.js";
11
11
 
12
+ import type { ChangedModel } from "./models.js";
12
13
 
13
14
  // Re-export public API from types (only factory functions and instances)
14
15
  export {
15
16
  // Pre-defined type instances
16
17
  string,
18
+ orderedString,
17
19
  number,
20
+ dateTime,
18
21
  boolean,
19
22
  identifier,
20
23
  undef,
@@ -34,12 +37,15 @@ export {
34
37
  dump,
35
38
  } from "./indexes.js";
36
39
 
40
+ export {
41
+ setLogLevel
42
+ } from "./utils.js";
43
+
37
44
  export { BaseIndex, UniqueIndex, PrimaryIndex } from './indexes.js';
38
45
 
39
46
  // Re-export from OLMDB
40
47
  export { init, onCommit, onRevert, getTransactionData, setTransactionData, DatabaseError } from "olmdb";
41
48
 
42
-
43
49
  /**
44
50
  * Executes a function within a database transaction context.
45
51
  *
@@ -63,7 +69,7 @@ export { init, onCommit, onRevert, getTransactionData, setTransactionData, Datab
63
69
  * @example
64
70
  * ```typescript
65
71
  * const paid = await E.transact(() => {
66
- * const user = User.load("john_doe");
72
+ * const user = User.pk.get("john_doe");
67
73
  * // This is concurrency-safe - the function will rerun if it is raced by another transaction
68
74
  * if (user.credits > 0) {
69
75
  * user.credits--;
@@ -80,35 +86,68 @@ export { init, onCommit, onRevert, getTransactionData, setTransactionData, Datab
80
86
  * });
81
87
  * ```
82
88
  */
83
- export function transact<T>(fn: () => T): Promise<T> {
84
- return olmdb.transact(() => {
85
- const modifiedInstances = new Set<Model<any>>();
86
- olmdb.setTransactionData(MODIFIED_INSTANCES_SYMBOL, modifiedInstances);
87
-
88
- const savedInstances: Set<Model<any>> = new Set();
89
- try {
90
- const result = fn();
91
- // Save all modified instances before committing.
92
- while(modifiedInstances.size > 0) {
93
- // Back referencing can cause models to be scheduled for save() a second time,
94
- // which is why we require the outer loop.
95
- for (const instance of modifiedInstances) {
96
- instance._save();
97
- savedInstances.add(instance);
98
- modifiedInstances.delete(instance);
89
+ export async function transact<T>(fn: () => T): Promise<T> {
90
+ try {
91
+ const onSaveQueue: ChangedModel[] | undefined = onSaveCallback ? [] : undefined;
92
+ return await olmdb.transact(async (): Promise<T> => {
93
+ const instances = new Set<Model<any>>();
94
+ olmdb.setTransactionData(INSTANCES_SYMBOL, instances);
95
+ olmdb.setTransactionData(INSTANCES_BY_PK_SYMBOL, new Map());
96
+
97
+ const savedInstances: Set<Model<any>> = new Set();
98
+ try {
99
+ const result = await fn();
100
+ // Save all modified instances before committing.
101
+ while(instances.size > 0) {
102
+ // Back referencing can cause models to be scheduled for save() a second time,
103
+ // which is why we require the outer loop.
104
+ for (const instance of instances) {
105
+ instance._onCommit(onSaveQueue);
106
+ savedInstances.add(instance);
107
+ instances.delete(instance);
108
+ }
99
109
  }
110
+ if (onSaveQueue?.length) {
111
+ olmdb.onCommit((commitId: number) => {
112
+ if (onSaveCallback) onSaveCallback(commitId, onSaveQueue);
113
+ });
114
+ }
115
+
116
+ return result;
117
+ } catch (error) {
118
+ // Discard changes on all saved and still unsaved instances
119
+ for (const instance of savedInstances) instance.preventPersist();
120
+ for (const instance of instances) instance.preventPersist();
121
+ throw error;
100
122
  }
101
-
102
- return result;
103
- } catch (error) {
104
- // Discard changes on all saved and still unsaved instances
105
- for (const instance of savedInstances) instance.preventPersist();
106
- for (const instance of modifiedInstances) instance.preventPersist();
107
- throw error;
108
- }
109
- });
123
+ });
124
+ } catch (e: Error | any) {
125
+ // This hackery is required to provide useful stack traces. Without this,
126
+ // both Bun and Node (even with --async-stack-traces) don't show which
127
+ // line called the transact(), which is pretty important info when validation
128
+ // fails, for instance. Though the line numbers in Bun still don't really
129
+ // make sense. Probably this bug: https://github.com/oven-sh/bun/issues/15859
130
+ e.stack += "\nat async:\n" + new Error().stack?.replace(/^.*?\n/, '');
131
+ throw e;
132
+ }
133
+ }
134
+
135
+ let onSaveCallback: ((commitId: number, items: ChangedModel[]) => void) | undefined;
136
+
137
+ /**
138
+ * Set a callback function to be called after a model is saved and committed.
139
+ *
140
+ * @param callback The callback function to set. It gets called after each successful
141
+ * `transact()` commit that has changes, with the following arguments:
142
+ * - A sequential number. Higher numbers have been committed after lower numbers.
143
+ * - An array of model instances that have been modified, created, or deleted.
144
+ * You can used its {@link Model.changed} property to figure out what changed.
145
+ */
146
+ export function setOnSaveCallback(callback: ((commitId: number, items: ChangedModel[]) => void) | undefined) {
147
+ onSaveCallback = callback;
110
148
  }
111
149
 
150
+
112
151
  export async function deleteEverything(): Promise<void> {
113
152
  await olmdb.transact(() => {
114
153
  for (const {key} of olmdb.scan()) {