@opendaw/lib-box 0.0.79 → 0.0.81

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.
@@ -1 +1 @@
1
- {"version":3,"file":"editing.d.ts","sourceRoot":"","sources":["../src/editing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAA;AAChC,OAAO,EAGH,OAAO,EAEP,KAAK,EAEL,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,YAAY,EAEf,MAAM,kBAAkB,CAAA;AAiDzB,MAAM,WAAW,mBAAmB;IAChC,OAAO,IAAI,IAAI,CAAA;IACf,MAAM,IAAI,IAAI,CAAA;CACjB;AAED,qBAAa,UAAW,YAAW,OAAO;;gBAY1B,KAAK,EAAE,QAAQ;IAM3B,IAAI,KAAK,IAAI,QAAQ,CAAqB;IAE1C,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY;IAIjD,SAAS,IAAI,IAAI;IAKjB,iBAAiB,IAAI,OAAO;IAM5B,YAAY,IAAI,OAAO;IAEvB,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,OAAO;IAWf,IAAI,IAAI,OAAO;IASf,OAAO,IAAI,OAAO;IAKlB,OAAO,IAAI,OAAO;IAMlB,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,GAAE,OAAc,GAAG,MAAM,CAAC,CAAC,CAAC;IA4B5E,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IA8CtD,iBAAiB,IAAI,mBAAmB;IAoCxC,IAAI,IAAI,IAAI;IAYZ,aAAa,IAAI,IAAI;IAOrB,OAAO,IAAI,IAAI;CAGlB"}
1
+ {"version":3,"file":"editing.d.ts","sourceRoot":"","sources":["../src/editing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAA;AAChC,OAAO,EAGH,OAAO,EAEP,KAAK,EAEL,QAAQ,EACR,MAAM,EAEN,YAAY,EACZ,YAAY,EAGf,MAAM,kBAAkB,CAAA;AAiDzB,MAAM,WAAW,mBAAmB;IAChC,OAAO,IAAI,IAAI,CAAA;IACf,MAAM,IAAI,IAAI,CAAA;CACjB;AAED,qBAAa,UAAW,YAAW,OAAO;;gBAY1B,KAAK,EAAE,QAAQ;IAM3B,IAAI,KAAK,IAAI,QAAQ,CAAqB;IAE1C,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY;IAIjD,SAAS,IAAI,IAAI;IAKjB,iBAAiB,IAAI,OAAO;IAM5B,YAAY,IAAI,OAAO;IAEvB,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,OAAO;IAsBf,IAAI,IAAI,OAAO;IAoBf,OAAO,IAAI,OAAO;IAKlB,OAAO,IAAI,OAAO;IAMlB,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,GAAE,OAAc,GAAG,MAAM,CAAC,CAAC,CAAC;IAiC5E,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IAoDtD,iBAAiB,IAAI,mBAAmB;IAoCxC,IAAI,IAAI,IAAI;IAYZ,aAAa,IAAI,IAAI;IAOrB,OAAO,IAAI,IAAI;CAGlB"}
package/dist/editing.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Arrays, assert, Notifier, Option, UUID } from "@opendaw/lib-std";
1
+ import { Arrays, assert, Notifier, Option, RuntimeNotifier, tryCatch, UUID } from "@opendaw/lib-std";
2
2
  import { DeleteUpdate, NewUpdate, PointerUpdate, PrimitiveUpdate } from "./updates";
3
3
  // Removes updates for boxes that were created AND deleted in the same transaction.
4
4
  const optimizeUpdates = (updates) => {
@@ -96,8 +96,21 @@ export class BoxEditing {
96
96
  }
97
97
  console.debug("undo");
98
98
  const modifications = this.#marked[--this.#historyIndex];
99
- modifications.toReversed().forEach(step => step.inverse(this.#graph));
100
- this.#graph.edges().validateRequirements();
99
+ const reversed = modifications.toReversed();
100
+ const applied = [];
101
+ for (const step of reversed) {
102
+ const result = tryCatch(() => step.inverse(this.#graph));
103
+ if (result.status === "failure") {
104
+ if (this.#graph.inTransaction()) {
105
+ this.#graph.abortTransaction();
106
+ }
107
+ applied.toReversed().forEach(completed => completed.forward(this.#graph));
108
+ this.#historyIndex++;
109
+ RuntimeNotifier.info({ headline: "Undo Failed", message: "This history step is no longer valid due to changes from other participants." });
110
+ return false;
111
+ }
112
+ applied.push(step);
113
+ }
101
114
  this.#notifier.notify();
102
115
  return true;
103
116
  }
@@ -106,8 +119,21 @@ export class BoxEditing {
106
119
  return false;
107
120
  }
108
121
  console.debug("redo");
109
- this.#marked[this.#historyIndex++].forEach(step => step.forward(this.#graph));
110
- this.#graph.edges().validateRequirements();
122
+ const modifications = this.#marked[this.#historyIndex++];
123
+ const applied = [];
124
+ for (const step of modifications) {
125
+ const result = tryCatch(() => step.forward(this.#graph));
126
+ if (result.status === "failure") {
127
+ if (this.#graph.inTransaction()) {
128
+ this.#graph.abortTransaction();
129
+ }
130
+ applied.toReversed().forEach(completed => completed.inverse(this.#graph));
131
+ this.#historyIndex--;
132
+ RuntimeNotifier.info({ headline: "Redo Failed", message: "This history step is no longer valid due to changes from other participants." });
133
+ return false;
134
+ }
135
+ applied.push(step);
136
+ }
111
137
  this.#notifier.notify();
112
138
  return true;
113
139
  }
@@ -129,7 +155,6 @@ export class BoxEditing {
129
155
  modify(modifier, mark = true) {
130
156
  assert(!this.#inProcess, "Cannot call modify while a modification process is running");
131
157
  if (this.#modifying || this.#graph.inTransaction()) {
132
- // Nested modify call or reactive call during undo/redo — just execute without separate recording
133
158
  this.#notifier.notify();
134
159
  return Option.wrap(modifier());
135
160
  }
@@ -141,21 +166,29 @@ export class BoxEditing {
141
166
  const subscription = this.#graph.subscribeToAllUpdates({
142
167
  onUpdate: (update) => updates.push(update)
143
168
  });
144
- this.#graph.beginTransaction();
145
- const result = modifier();
146
- this.#graph.endTransaction();
169
+ const result = tryCatch(() => {
170
+ this.#graph.beginTransaction();
171
+ const result = modifier();
172
+ this.#graph.endTransaction();
173
+ return result;
174
+ });
147
175
  subscription.terminate();
176
+ this.#modifying = false;
177
+ if (result.status === "failure") {
178
+ if (this.#graph.inTransaction()) {
179
+ this.#graph.abortTransaction();
180
+ }
181
+ throw result.error;
182
+ }
148
183
  const optimized = optimizeUpdates(updates);
149
184
  if (optimized.length > 0) {
150
185
  this.#pending.push(new Modification(optimized));
151
186
  }
152
- this.#modifying = false;
153
- this.#graph.edges().validateRequirements();
154
187
  if (mark) {
155
188
  this.mark();
156
189
  }
157
190
  this.#notifier.notify();
158
- return Option.wrap(result);
191
+ return Option.wrap(result.value);
159
192
  }
160
193
  append(modifier) {
161
194
  assert(!this.#inProcess, "Cannot call append while a modification process is running");
@@ -177,10 +210,20 @@ export class BoxEditing {
177
210
  const subscription = this.#graph.subscribeToAllUpdates({
178
211
  onUpdate: (update) => updates.push(update)
179
212
  });
180
- this.#graph.beginTransaction();
181
- const result = modifier();
182
- this.#graph.endTransaction();
213
+ const result = tryCatch(() => {
214
+ this.#graph.beginTransaction();
215
+ const result = modifier();
216
+ this.#graph.endTransaction();
217
+ return result;
218
+ });
183
219
  subscription.terminate();
220
+ this.#modifying = false;
221
+ if (result.status === "failure") {
222
+ if (this.#graph.inTransaction()) {
223
+ this.#graph.abortTransaction();
224
+ }
225
+ throw result.error;
226
+ }
184
227
  const optimized = optimizeUpdates(updates);
185
228
  if (optimized.length > 0) {
186
229
  const modification = new Modification(optimized);
@@ -199,10 +242,8 @@ export class BoxEditing {
199
242
  this.#historyIndex = this.#marked.length;
200
243
  }
201
244
  }
202
- this.#modifying = false;
203
- this.#graph.edges().validateRequirements();
204
245
  this.#notifier.notify();
205
- return Option.wrap(result);
246
+ return Option.wrap(result.value);
206
247
  }
207
248
  beginModification() {
208
249
  assert(!this.#modifying && !this.#inProcess, "Cannot begin modification while another is in progress");
@@ -213,27 +254,29 @@ export class BoxEditing {
213
254
  onUpdate: (update) => updates.push(update)
214
255
  });
215
256
  this.#graph.beginTransaction();
257
+ const cleanup = () => {
258
+ subscription.terminate();
259
+ this.#modifying = false;
260
+ this.#inProcess = false;
261
+ };
216
262
  return {
217
263
  approve: () => {
218
- this.#graph.endTransaction();
219
- subscription.terminate();
264
+ const result = tryCatch(() => this.#graph.endTransaction());
265
+ cleanup();
266
+ if (result.status === "failure") {
267
+ throw result.error;
268
+ }
220
269
  const optimized = optimizeUpdates(updates);
221
270
  if (optimized.length > 0) {
222
271
  this.#pending.push(new Modification(optimized));
223
272
  }
224
- this.#modifying = false;
225
- this.#inProcess = false;
226
- this.#graph.edges().validateRequirements();
227
273
  this.mark();
228
274
  this.#notifier.notify();
229
275
  },
230
276
  revert: () => {
231
- this.#graph.endTransaction();
232
- subscription.terminate();
233
- this.#modifying = false;
234
- this.#inProcess = false;
235
- this.#graph.edges().validateRequirements();
236
- if (updates.length > 0) {
277
+ const result = tryCatch(() => this.#graph.endTransaction());
278
+ cleanup();
279
+ if (result.status === "success" && updates.length > 0) {
237
280
  new Modification(updates).inverse(this.#graph);
238
281
  }
239
282
  }
@@ -1,3 +1,4 @@
1
+ import { Option } from "@opendaw/lib-std";
1
2
  import { Address } from "./address";
2
3
  import { PointerField } from "./pointer";
3
4
  import { Vertex } from "./vertex";
@@ -12,6 +13,8 @@ export declare class GraphEdges {
12
13
  isConnected(source: PointerField, target: Address): boolean;
13
14
  outgoingEdgesOf(box: Box): ReadonlyArray<[PointerField, Address]>;
14
15
  incomingEdgesOf(vertex: Box | Vertex): ReadonlyArray<PointerField>;
16
+ clearAffected(): void;
17
+ tryValidateAffected(): Option<Error>;
15
18
  validateRequirements(): void;
16
19
  }
17
20
  //# sourceMappingURL=graph-edges.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph-edges.d.ts","sourceRoot":"","sources":["../src/graph-edges.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AACjC,OAAO,EAAC,YAAY,EAAC,MAAM,WAAW,CAAA;AACtC,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAC,GAAG,EAAC,MAAM,OAAO,CAAA;AAEzB,qBAAa,UAAU;;;IAenB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAoBhD,iBAAiB,CAAC,GAAG,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;IAqBrD,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAQpD,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAOtC,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO;IAI3D,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,aAAa,CAAC,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAIjE,eAAe,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,GAAG,aAAa,CAAC,YAAY,CAAC;IASlE,oBAAoB,IAAI,IAAI;CAgD/B"}
1
+ {"version":3,"file":"graph-edges.d.ts","sourceRoot":"","sources":["../src/graph-edges.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwC,MAAM,EAAyB,MAAM,kBAAkB,CAAA;AACtG,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AACjC,OAAO,EAAC,YAAY,EAAC,MAAM,WAAW,CAAA;AACtC,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAC,GAAG,EAAC,MAAM,OAAO,CAAA;AAEzB,qBAAa,UAAU;;;IAiBnB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAqBhD,iBAAiB,CAAC,GAAG,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;IAqBrD,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAUpD,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAStC,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO;IAI3D,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,aAAa,CAAC,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAIjE,eAAe,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,GAAG,aAAa,CAAC,YAAY,CAAC;IASlE,aAAa,IAAI,IAAI;IAErB,mBAAmB,IAAI,MAAM,CAAC,KAAK,CAAC;IAsCpC,oBAAoB,IAAI,IAAI;CAgD/B"}
@@ -1,4 +1,4 @@
1
- import { Arrays, isDefined, isInstanceOf, panic } from "@opendaw/lib-std";
1
+ import { Arrays, isDefined, isInstanceOf, Option, panic, UUID } from "@opendaw/lib-std";
2
2
  import { Address } from "./address";
3
3
  import { PointerField } from "./pointer";
4
4
  export class GraphEdges {
@@ -7,12 +7,14 @@ export class GraphEdges {
7
7
  #requiresExclusive;
8
8
  #incoming;
9
9
  #outgoing;
10
+ #affected;
10
11
  constructor() {
11
12
  this.#requiresTarget = Address.newSet(source => source.address);
12
13
  this.#requiresPointer = Address.newSet(vertex => vertex.address);
13
14
  this.#requiresExclusive = Address.newSet(vertex => vertex.address);
14
15
  this.#incoming = Address.newSet(([address]) => address);
15
16
  this.#outgoing = Address.newSet(([source]) => source.address);
17
+ this.#affected = UUID.newSet(uuid => uuid);
16
18
  }
17
19
  watchVertex(vertex) {
18
20
  if (isInstanceOf(vertex, PointerField)) {
@@ -33,6 +35,7 @@ export class GraphEdges {
33
35
  this.#requiresExclusive.add(vertex);
34
36
  }
35
37
  }
38
+ this.#affected.add(vertex.address.uuid);
36
39
  }
37
40
  unwatchVerticesOf(...boxes) {
38
41
  const map = ({ box: { address: { uuid } } }) => uuid;
@@ -58,6 +61,8 @@ export class GraphEdges {
58
61
  none: () => this.#incoming.add([target, [source]]),
59
62
  some: ([, sources]) => sources.push(source)
60
63
  });
64
+ this.#affected.add(source.address.uuid);
65
+ this.#affected.add(target.uuid);
61
66
  }
62
67
  disconnect(source) {
63
68
  const [, target] = this.#outgoing.removeByKey(source.address);
@@ -66,6 +71,8 @@ export class GraphEdges {
66
71
  if (sources.length === 0) {
67
72
  this.#incoming.removeByKey(target);
68
73
  }
74
+ this.#affected.add(source.address.uuid);
75
+ this.#affected.add(target.uuid);
69
76
  }
70
77
  isConnected(source, target) {
71
78
  return this.#outgoing.opt(source.address).mapOr(([, actualTarget]) => actualTarget.equals(target), false);
@@ -82,6 +89,46 @@ export class GraphEdges {
82
89
  return this.#incoming.opt(vertex.address).mapOr(([_, pointers]) => pointers, Arrays.empty());
83
90
  }
84
91
  }
92
+ clearAffected() { this.#affected.clear(); }
93
+ tryValidateAffected() {
94
+ const map = ({ box: { address: { uuid } } }) => uuid;
95
+ for (const uuid of this.#affected.values()) {
96
+ const pointers = this.#collectSameBox(this.#requiresTarget, uuid, map);
97
+ for (const pointer of pointers) {
98
+ if (pointer.isEmpty()) {
99
+ this.#affected.clear();
100
+ if (pointer.mandatory) {
101
+ return Option.wrap(new Error(`Pointer ${pointer.toString()} requires an edge.`));
102
+ }
103
+ else {
104
+ return Option.wrap(new Error(`Illegal state: ${pointer} has no edge requirements.`));
105
+ }
106
+ }
107
+ }
108
+ const targets = this.#collectSameBox(this.#requiresPointer, uuid, map);
109
+ for (const target of targets) {
110
+ if (target.pointerHub.isEmpty()) {
111
+ this.#affected.clear();
112
+ if (target.pointerRules.mandatory) {
113
+ return Option.wrap(new Error(`Target ${target.toString()} requires an edge.`));
114
+ }
115
+ else {
116
+ return Option.wrap(new Error(`Illegal state: ${target} has no edge requirements.`));
117
+ }
118
+ }
119
+ }
120
+ const exclusives = this.#collectSameBox(this.#requiresExclusive, uuid, map);
121
+ for (const target of exclusives) {
122
+ const count = target.pointerHub.size();
123
+ if (count > 1) {
124
+ this.#affected.clear();
125
+ return Option.wrap(new Error(`Target ${target.toString()} is exclusive but has ${count} incoming pointers.`));
126
+ }
127
+ }
128
+ }
129
+ this.#affected.clear();
130
+ return Option.None;
131
+ }
85
132
  validateRequirements() {
86
133
  // TODO I removed the assertions because they were too slow in busy graphs.
87
134
  // I tried to use a Set<Box> in BoxGraph, but that wasn't faster than the SortedSet.
package/dist/graph.d.ts CHANGED
@@ -10,7 +10,7 @@ import { GraphEdges } from "./graph-edges";
10
10
  export type BoxFactory<BoxMap> = (name: keyof BoxMap, graph: BoxGraph<BoxMap>, uuid: UUID.Bytes, constructor: Procedure<Box>) => Box;
11
11
  export interface TransactionListener {
12
12
  onBeginTransaction(): void;
13
- onEndTransaction(): void;
13
+ onEndTransaction(rolledBack: boolean): void;
14
14
  }
15
15
  export interface UpdateListener {
16
16
  onUpdate(update: Update): void;
@@ -23,6 +23,7 @@ export declare class BoxGraph<BoxMap = any> {
23
23
  #private;
24
24
  constructor(boxFactory?: Option<BoxFactory<BoxMap>>);
25
25
  beginTransaction(): void;
26
+ abortTransaction(): void;
26
27
  endTransaction(): void;
27
28
  inTransaction(): boolean;
28
29
  constructingBox(): boolean;
@@ -55,8 +56,8 @@ export declare class BoxGraph<BoxMap = any> {
55
56
  debugDependencies(): void;
56
57
  addressToDebugPath(address: Option<Address>): Option<string>;
57
58
  toArrayBuffer(): ArrayBufferLike;
58
- fromArrayBuffer(arrayBuffer: ArrayBufferLike): void;
59
+ fromArrayBuffer(arrayBuffer: ArrayBufferLike, validate?: boolean): void;
59
60
  toJSON(): JSONValue;
60
- fromJSON(value: JSONValue): void;
61
+ fromJSON(value: JSONValue, validate?: boolean): void;
61
62
  }
62
63
  //# sourceMappingURL=graph.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"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"}
package/dist/graph.js CHANGED
@@ -1,4 +1,4 @@
1
- import { asDefined, assert, ByteArrayInput, ByteArrayOutput, Checksum, isDefined, Listeners, Option, panic, Predicates, UUID } from "@opendaw/lib-std";
1
+ import { asDefined, assert, ByteArrayInput, ByteArrayOutput, Checksum, isDefined, Listeners, Option, panic, Predicates, tryCatch, UUID } from "@opendaw/lib-std";
2
2
  import { Address } from "./address";
3
3
  import { DeleteUpdate, NewUpdate, PointerUpdate, PrimitiveUpdate } from "./updates";
4
4
  import { Dispatchers } from "./dispatchers";
@@ -7,6 +7,8 @@ export class BoxGraph {
7
7
  #boxFactory;
8
8
  #boxes;
9
9
  #deferredPointerUpdates;
10
+ #pendingDeferredNotifications;
11
+ #transactionUpdates;
10
12
  #updateListeners;
11
13
  #immediateUpdateListeners;
12
14
  #transactionListeners;
@@ -17,10 +19,13 @@ export class BoxGraph {
17
19
  #deletionListeners;
18
20
  #inTransaction = false;
19
21
  #constructingBox = false;
22
+ #rollingBack = false;
20
23
  constructor(boxFactory = Option.None) {
21
24
  this.#boxFactory = boxFactory;
22
25
  this.#boxes = UUID.newSet(box => box.address.uuid);
23
26
  this.#deferredPointerUpdates = [];
27
+ this.#pendingDeferredNotifications = [];
28
+ this.#transactionUpdates = [];
24
29
  this.#dispatchers = Dispatchers.create();
25
30
  this.#updateListeners = new Listeners();
26
31
  this.#immediateUpdateListeners = new Listeners();
@@ -33,32 +38,33 @@ export class BoxGraph {
33
38
  beginTransaction() {
34
39
  assert(!this.#inTransaction, "Transaction already in progress");
35
40
  this.#inTransaction = true;
41
+ this.#transactionUpdates.length = 0;
36
42
  this.#transactionListeners.proxy.onBeginTransaction();
37
43
  }
44
+ abortTransaction() {
45
+ assert(this.#inTransaction, "No transaction in progress");
46
+ this.#rollback();
47
+ this.#deferredPointerUpdates.length = 0;
48
+ this.#finalizeTransaction();
49
+ }
38
50
  endTransaction() {
39
51
  assert(this.#inTransaction, "No transaction in progress");
40
52
  if (this.#deferredPointerUpdates.length > 0) {
41
- this.#deferredPointerUpdates.forEach(({ pointerField, update }) => this.#processPointerVertexUpdate(pointerField, update));
53
+ this.#deferredPointerUpdates.forEach(({ pointerField, update }) => {
54
+ this.#transactionUpdates.push(update);
55
+ this.#prepareDeferredPointerUpdate(pointerField, update);
56
+ });
42
57
  this.#deferredPointerUpdates.length = 0;
43
58
  }
44
- this.#pointerTransactionState.values()
45
- .toSorted((a, b) => a.index - b.index)
46
- .forEach(({ pointer, initial, final }) => {
47
- if (!initial.equals(final)) {
48
- initial.ifSome(address => this.findVertex(address).unwrapOrUndefined()?.pointerHub.onRemoved(pointer));
49
- final.ifSome(address => this.findVertex(address).unwrapOrUndefined()?.pointerHub.onAdded(pointer));
50
- }
51
- });
52
- this.#pointerTransactionState.clear();
53
- this.#inTransaction = false;
54
- // it is possible that new observers will be added while executing
55
- while (this.#finalizeTransactionObservers.length > 0) {
56
- this.#finalizeTransactionObservers.splice(0).forEach(observer => observer());
57
- if (this.#finalizeTransactionObservers.length > 0) {
58
- console.debug(`${this.#finalizeTransactionObservers.length} new observers while notifying`);
59
- }
59
+ if (!this.#rollingBack && this.#boxFactory.nonEmpty()) {
60
+ this.#edges.tryValidateAffected().ifSome(error => {
61
+ this.#rollback();
62
+ this.#finalizeTransaction();
63
+ throw error;
64
+ });
60
65
  }
61
- this.#transactionListeners.proxy.onEndTransaction();
66
+ this.#dispatchDeferredNotifications();
67
+ this.#finalizeTransaction();
62
68
  }
63
69
  inTransaction() { return this.#inTransaction; }
64
70
  constructingBox() { return this.#constructingBox; }
@@ -76,6 +82,9 @@ export class BoxGraph {
76
82
  const added = this.#boxes.add(box);
77
83
  assert(added, () => `${box.name} ${box.address.toString()} already staged`);
78
84
  const update = new NewUpdate(box.address.uuid, box.name, box.toArrayBuffer());
85
+ if (!this.#rollingBack) {
86
+ this.#transactionUpdates.push(update);
87
+ }
79
88
  this.#updateListeners.proxy.onUpdate(update);
80
89
  this.#immediateUpdateListeners.proxy.onUpdate(update);
81
90
  return box;
@@ -111,6 +120,9 @@ export class BoxGraph {
111
120
  assert(deleted === box, `${box} could not be found to unstage`);
112
121
  this.#edges.unwatchVerticesOf(box);
113
122
  const update = new DeleteUpdate(box.address.uuid, box.name, box.toArrayBuffer());
123
+ if (!this.#rollingBack) {
124
+ this.#transactionUpdates.push(update);
125
+ }
114
126
  this.#deletionListeners.removeByKeyIfExist(box.address.uuid)?.listeners.forEach(listener => listener());
115
127
  this.#updateListeners.proxy.onUpdate(update);
116
128
  this.#immediateUpdateListeners.proxy.onUpdate(update);
@@ -132,6 +144,9 @@ export class BoxGraph {
132
144
  this.#assertTransaction();
133
145
  if (field.isAttached() && !this.#constructingBox) {
134
146
  const update = new PrimitiveUpdate(field.address, field.serialization(), oldValue, newValue);
147
+ if (!this.#rollingBack) {
148
+ this.#transactionUpdates.push(update);
149
+ }
135
150
  this.#dispatchers.dispatch(update);
136
151
  this.#updateListeners.proxy.onUpdate(update);
137
152
  this.#immediateUpdateListeners.proxy.onUpdate(update);
@@ -150,6 +165,9 @@ export class BoxGraph {
150
165
  this.#deferredPointerUpdates.push({ pointerField, update });
151
166
  }
152
167
  else {
168
+ if (!this.#rollingBack) {
169
+ this.#transactionUpdates.push(update);
170
+ }
153
171
  this.#processPointerVertexUpdate(pointerField, update);
154
172
  this.#immediateUpdateListeners.proxy.onUpdate(update);
155
173
  }
@@ -170,6 +188,60 @@ export class BoxGraph {
170
188
  this.#dispatchers.dispatch(update);
171
189
  this.#updateListeners.proxy.onUpdate(update);
172
190
  }
191
+ #prepareDeferredPointerUpdate(pointerField, update) {
192
+ const { oldAddress, newAddress } = update;
193
+ pointerField.resolvedTo(newAddress.flatMap(address => this.findVertex(address)));
194
+ const optState = this.#pointerTransactionState.opt(pointerField.address);
195
+ optState.match({
196
+ none: () => this.#pointerTransactionState.add({
197
+ pointer: pointerField,
198
+ initial: oldAddress,
199
+ final: newAddress,
200
+ index: this.#pointerTransactionState.size()
201
+ }),
202
+ some: state => state.final = newAddress
203
+ });
204
+ this.#pendingDeferredNotifications.push({ pointerField, update });
205
+ }
206
+ #dispatchDeferredNotifications() {
207
+ for (const { update } of this.#pendingDeferredNotifications) {
208
+ this.#dispatchers.dispatch(update);
209
+ this.#updateListeners.proxy.onUpdate(update);
210
+ }
211
+ this.#pendingDeferredNotifications.length = 0;
212
+ }
213
+ #finalizeTransaction() {
214
+ this.#pointerTransactionState.values()
215
+ .toSorted((a, b) => a.index - b.index)
216
+ .forEach(({ pointer, initial, final }) => {
217
+ if (!initial.equals(final)) {
218
+ initial.ifSome(address => this.findVertex(address).unwrapOrUndefined()?.pointerHub.onRemoved(pointer));
219
+ final.ifSome(address => this.findVertex(address).unwrapOrUndefined()?.pointerHub.onAdded(pointer));
220
+ }
221
+ });
222
+ this.#pointerTransactionState.clear();
223
+ this.#inTransaction = false;
224
+ while (this.#finalizeTransactionObservers.length > 0) {
225
+ this.#finalizeTransactionObservers.splice(0).forEach(observer => observer());
226
+ if (this.#finalizeTransactionObservers.length > 0) {
227
+ console.debug(`${this.#finalizeTransactionObservers.length} new observers while notifying`);
228
+ }
229
+ }
230
+ this.#transactionListeners.proxy.onEndTransaction(this.#rollingBack);
231
+ this.#rollingBack = false;
232
+ }
233
+ #rollback() {
234
+ this.#rollingBack = true;
235
+ const updates = this.#transactionUpdates.splice(0);
236
+ for (let i = updates.length - 1; i >= 0; i--) {
237
+ updates[i].inverse(this);
238
+ }
239
+ this.#deferredPointerUpdates.length = 0;
240
+ this.#pendingDeferredNotifications.length = 0;
241
+ this.#edges.clearAffected();
242
+ this.#pointerTransactionState.clear();
243
+ this.#transactionUpdates.length = 0;
244
+ }
173
245
  findOrphans(rootBox) {
174
246
  const connectedBoxes = this.#collectAllConnectedBoxes(rootBox);
175
247
  return this.boxes().filter(box => !connectedBoxes.has(box));
@@ -331,7 +403,7 @@ export class BoxGraph {
331
403
  });
332
404
  return output.toArrayBuffer();
333
405
  }
334
- fromArrayBuffer(arrayBuffer) {
406
+ fromArrayBuffer(arrayBuffer, validate = true) {
335
407
  assert(this.#boxes.isEmpty(), "Cannot call fromArrayBuffer if boxes is not empty");
336
408
  const input = new ByteArrayInput(arrayBuffer);
337
409
  const numBoxes = input.readInt();
@@ -350,7 +422,17 @@ export class BoxGraph {
350
422
  boxes
351
423
  .sort((a, b) => a.creationIndex - b.creationIndex)
352
424
  .forEach(({ name, uuid, boxStream }) => this.createBox(name, uuid, box => box.read(boxStream)));
353
- this.endTransaction();
425
+ if (validate) {
426
+ this.endTransaction();
427
+ }
428
+ else {
429
+ if (this.#deferredPointerUpdates.length > 0) {
430
+ this.#deferredPointerUpdates.forEach(({ pointerField, update }) => this.#processPointerVertexUpdate(pointerField, update));
431
+ this.#deferredPointerUpdates.length = 0;
432
+ }
433
+ this.#edges.clearAffected();
434
+ this.#finalizeTransaction();
435
+ }
354
436
  }
355
437
  toJSON() {
356
438
  const result = {};
@@ -362,7 +444,7 @@ export class BoxGraph {
362
444
  }
363
445
  return result;
364
446
  }
365
- fromJSON(value) {
447
+ fromJSON(value, validate = true) {
366
448
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
367
449
  return panic("Expected object");
368
450
  }
@@ -370,15 +452,23 @@ export class BoxGraph {
370
452
  const entries = Object.entries(value);
371
453
  for (const [uuid, { name, fields }] of entries) {
372
454
  this.createBox(name, UUID.parse(uuid), box => {
373
- try {
374
- box.fromJSON(fields);
375
- }
376
- catch (reason) {
377
- console.warn(reason);
455
+ const result = tryCatch(() => box.fromJSON(fields));
456
+ if (result.status === "failure") {
457
+ console.warn(result.error);
378
458
  }
379
459
  });
380
460
  }
381
- this.endTransaction();
461
+ if (validate) {
462
+ this.endTransaction();
463
+ }
464
+ else {
465
+ if (this.#deferredPointerUpdates.length > 0) {
466
+ this.#deferredPointerUpdates.forEach(({ pointerField, update }) => this.#processPointerVertexUpdate(pointerField, update));
467
+ this.#deferredPointerUpdates.length = 0;
468
+ }
469
+ this.#edges.clearAffected();
470
+ this.#finalizeTransaction();
471
+ }
382
472
  }
383
473
  #assertTransaction() {
384
474
  assert(this.#inTransaction, () => "Modification only prohibited in transaction mode.");
@@ -1 +1 @@
1
- {"version":3,"file":"sync-source.d.ts","sourceRoot":"","sources":["../src/sync-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,UAAU,EAAa,MAAM,kBAAkB,CAAA;AACjF,OAAO,EAAe,SAAS,EAAC,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAA;AAIhC,qBAAa,UAAU,CAAC,CAAC,CAAE,YAAW,UAAU;;IAC5C,MAAM,CAAC,QAAQ,CAAC,cAAc,SAAQ;gBAK1B,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,OAAO;IAkE1E,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IACzC,SAAS;CACZ"}
1
+ {"version":3,"file":"sync-source.d.ts","sourceRoot":"","sources":["../src/sync-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,UAAU,EAAa,MAAM,kBAAkB,CAAA;AACjF,OAAO,EAAe,SAAS,EAAC,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAA;AAIhC,qBAAa,UAAU,CAAC,CAAC,CAAE,YAAW,UAAU;;IAC5C,MAAM,CAAC,QAAQ,CAAC,cAAc,SAAQ;gBAK1B,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,OAAO;IAsE1E,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IACzC,SAAS;CACZ"}
@@ -23,7 +23,11 @@ export class SyncSource {
23
23
  const updates = [];
24
24
  this.#terminator.own(graph.subscribeTransaction({
25
25
  onBeginTransaction: EmptyExec,
26
- onEndTransaction: () => {
26
+ onEndTransaction: (rolledBack) => {
27
+ if (rolledBack) {
28
+ Arrays.clear(updates);
29
+ return;
30
+ }
27
31
  this.#caller.sendUpdates(updates);
28
32
  if (SyncSource.DEBUG_CHECKSUM) {
29
33
  this.#caller.checksum(graph.checksum()).then(EmptyExec, (reason) => panic(reason));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendaw/lib-box",
3
- "version": "0.0.79",
3
+ "version": "0.0.81",
4
4
  "sideEffects": false,
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -23,12 +23,12 @@
23
23
  "test": "vitest run"
24
24
  },
25
25
  "dependencies": {
26
- "@opendaw/lib-runtime": "^0.0.74",
27
- "@opendaw/lib-std": "^0.0.73"
26
+ "@opendaw/lib-runtime": "^0.0.76",
27
+ "@opendaw/lib-std": "^0.0.75"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@opendaw/eslint-config": "^0.0.27",
31
31
  "@opendaw/typescript-config": "^0.0.29"
32
32
  },
33
- "gitHead": "0de255fcdacefee9ab663ceeb0d4424a278c5dc4"
33
+ "gitHead": "ff6efcb5332559bb0ba064be829ab0d18a6ba226"
34
34
  }