@matter/general 0.13.1-alpha.0-20250517-99a1e848a → 0.14.0-alpha.0-20250521-979eda05d
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/dist/cjs/crypto/Crypto.d.ts +1 -1
- package/dist/cjs/polyfills/index.d.ts +1 -0
- package/dist/cjs/polyfills/index.d.ts.map +1 -1
- package/dist/cjs/polyfills/index.js +1 -0
- package/dist/cjs/polyfills/index.js.map +1 -1
- package/dist/cjs/polyfills/suppressed-error.d.ts +7 -0
- package/dist/cjs/polyfills/suppressed-error.d.ts.map +1 -0
- package/dist/cjs/polyfills/suppressed-error.js +21 -0
- package/dist/cjs/polyfills/suppressed-error.js.map +6 -0
- package/dist/cjs/transaction/Transaction.d.ts +49 -22
- package/dist/cjs/transaction/Transaction.d.ts.map +1 -1
- package/dist/cjs/transaction/Transaction.js +22 -3
- package/dist/cjs/transaction/Transaction.js.map +1 -1
- package/dist/cjs/transaction/Tx.d.ts +9 -4
- package/dist/cjs/transaction/Tx.d.ts.map +1 -1
- package/dist/cjs/transaction/Tx.js +152 -122
- package/dist/cjs/transaction/Tx.js.map +2 -2
- package/dist/cjs/util/Observable.js +1 -1
- package/dist/cjs/util/Observable.js.map +1 -1
- package/dist/esm/crypto/Crypto.d.ts +1 -1
- package/dist/esm/polyfills/index.d.ts +1 -0
- package/dist/esm/polyfills/index.d.ts.map +1 -1
- package/dist/esm/polyfills/index.js +1 -0
- package/dist/esm/polyfills/index.js.map +1 -1
- package/dist/esm/polyfills/suppressed-error.d.ts +7 -0
- package/dist/esm/polyfills/suppressed-error.d.ts.map +1 -0
- package/dist/esm/polyfills/suppressed-error.js +20 -0
- package/dist/esm/polyfills/suppressed-error.js.map +6 -0
- package/dist/esm/transaction/Transaction.d.ts +49 -22
- package/dist/esm/transaction/Transaction.d.ts.map +1 -1
- package/dist/esm/transaction/Transaction.js +23 -4
- package/dist/esm/transaction/Transaction.js.map +1 -1
- package/dist/esm/transaction/Tx.d.ts +9 -4
- package/dist/esm/transaction/Tx.d.ts.map +1 -1
- package/dist/esm/transaction/Tx.js +152 -122
- package/dist/esm/transaction/Tx.js.map +2 -2
- package/dist/esm/util/Observable.js +1 -1
- package/dist/esm/util/Observable.js.map +1 -1
- package/package.json +3 -3
- package/src/polyfills/index.ts +1 -0
- package/src/polyfills/suppressed-error.ts +27 -0
- package/src/transaction/Transaction.ts +42 -4
- package/src/transaction/Tx.ts +202 -165
- package/src/util/Observable.ts +1 -1
package/src/transaction/Tx.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { Diagnostic } from "#log/Diagnostic.js";
|
|
|
8
8
|
import { Logger } from "#log/Logger.js";
|
|
9
9
|
import { ImplementationError, ReadOnlyError } from "#MatterError.js";
|
|
10
10
|
import { Time, Timer } from "#time/Time.js";
|
|
11
|
+
import { asError } from "#util/Error.js";
|
|
11
12
|
import { Observable } from "#util/Observable.js";
|
|
12
13
|
import { MaybePromise } from "#util/Promises.js";
|
|
13
14
|
import { describeList } from "#util/String.js";
|
|
@@ -29,106 +30,14 @@ const MAX_CHAINED_COMMITS = 5;
|
|
|
29
30
|
/**
|
|
30
31
|
* This is the only public interface to this file.
|
|
31
32
|
*/
|
|
32
|
-
export function
|
|
33
|
-
|
|
34
|
-
let commits = 0;
|
|
35
|
-
|
|
36
|
-
// Post-commit logic may result in the transaction requiring commit again so commit iteratively up to
|
|
37
|
-
// MAX_CHAINED_COMMITS times
|
|
38
|
-
function commitTransaction(finalResult: T): MaybePromise<T> {
|
|
39
|
-
commits++;
|
|
40
|
-
|
|
41
|
-
if (commits > MAX_CHAINED_COMMITS) {
|
|
42
|
-
throw new TransactionFlowError(
|
|
43
|
-
`Transaction commits have cascaded ${MAX_CHAINED_COMMITS} times which likely indicates an infinite loop`,
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Avoid MaybePromise.then to shorten stack traces
|
|
48
|
-
const result = tx.commit();
|
|
49
|
-
if (MaybePromise.is(result)) {
|
|
50
|
-
return result.then(() => {
|
|
51
|
-
if (tx.status === Status.Exclusive) {
|
|
52
|
-
return commitTransaction(finalResult);
|
|
53
|
-
}
|
|
54
|
-
return finalResult;
|
|
55
|
-
});
|
|
56
|
-
} else if (tx.status === Status.Exclusive) {
|
|
57
|
-
return commitTransaction(finalResult);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return finalResult;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const handleTransactionError = ((error: any) => {
|
|
64
|
-
// If we've committed, error happened during commit and we've already logged and cleaned up
|
|
65
|
-
if (commits) {
|
|
66
|
-
throw error;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
logger.error("Rolling back", tx.via, "due to error:", Diagnostic.weak(error?.message || `${error}`));
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
const result = tx.rollback();
|
|
73
|
-
if (MaybePromise.is(result)) {
|
|
74
|
-
return Promise.resolve(result).catch(error2 => {
|
|
75
|
-
if (error2 !== error) {
|
|
76
|
-
logger.error("Secondary error in", tx.via, "rollback:", error2);
|
|
77
|
-
}
|
|
78
|
-
throw error;
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
} catch (error2) {
|
|
82
|
-
if (error2 !== error) {
|
|
83
|
-
logger.error("Secondary error in", tx.via, "rollback:", error2);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
throw error;
|
|
88
|
-
}) as (error: any) => MaybePromise<T>; // Cast because otherwise type is MaybePromise<void>
|
|
89
|
-
|
|
90
|
-
const closeTransaction = tx.close.bind(tx);
|
|
91
|
-
|
|
92
|
-
let isAsync = false;
|
|
93
|
-
try {
|
|
94
|
-
// Execute the actor
|
|
95
|
-
const actorResult = actor(tx);
|
|
96
|
-
|
|
97
|
-
// If actor is async, chain commit and close asynchronously
|
|
98
|
-
if (MaybePromise.is(actorResult)) {
|
|
99
|
-
// If the actor is async mark the transaction as async; this will enable reporting on lock changes
|
|
100
|
-
isAsync = tx.isAsync = true;
|
|
101
|
-
return Promise.resolve(actorResult)
|
|
102
|
-
.then(commitTransaction, handleTransactionError)
|
|
103
|
-
.finally(closeTransaction) as T;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Actor is not async but if commit is, chain closeTransaction
|
|
107
|
-
const commitResult = commitTransaction(actorResult);
|
|
108
|
-
if (MaybePromise.is(commitResult)) {
|
|
109
|
-
isAsync = true;
|
|
110
|
-
return Promise.resolve(commitResult).catch(handleTransactionError).finally(closeTransaction) as T;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Fully synchronous action
|
|
114
|
-
return commitResult;
|
|
115
|
-
} catch (e) {
|
|
116
|
-
const result = handleTransactionError(e);
|
|
117
|
-
|
|
118
|
-
// Above throws if synchronous so this is async code path
|
|
119
|
-
isAsync = true;
|
|
120
|
-
return Promise.resolve(result).finally(closeTransaction) as T;
|
|
121
|
-
} finally {
|
|
122
|
-
if (!isAsync) {
|
|
123
|
-
tx.close();
|
|
124
|
-
}
|
|
125
|
-
}
|
|
33
|
+
export function open(via: string): Transaction & Transaction.Finalization {
|
|
34
|
+
return new Tx(via);
|
|
126
35
|
}
|
|
127
36
|
|
|
128
37
|
/**
|
|
129
38
|
* The concrete implementation of the Transaction interface.
|
|
130
39
|
*/
|
|
131
|
-
class Tx implements Transaction {
|
|
40
|
+
class Tx implements Transaction, Transaction.Finalization {
|
|
132
41
|
#participants = new Set<Participant>();
|
|
133
42
|
#roles = new Map<{}, Participant>();
|
|
134
43
|
#resources = new Set<Resource>();
|
|
@@ -149,12 +58,9 @@ class Tx implements Transaction {
|
|
|
149
58
|
}
|
|
150
59
|
}
|
|
151
60
|
|
|
152
|
-
|
|
153
|
-
|
|
61
|
+
[Symbol.dispose]() {
|
|
62
|
+
this.#reset("dropped");
|
|
154
63
|
this.#status = Status.Destroyed;
|
|
155
|
-
this.#resources.clear();
|
|
156
|
-
this.#roles.clear();
|
|
157
|
-
this.#participants.clear();
|
|
158
64
|
this.#closed?.emit();
|
|
159
65
|
}
|
|
160
66
|
|
|
@@ -182,6 +88,9 @@ class Tx implements Transaction {
|
|
|
182
88
|
return this.#isAsync;
|
|
183
89
|
}
|
|
184
90
|
|
|
91
|
+
/**
|
|
92
|
+
* We set this during async processing. This enables the lock reporting when too much time ellapses.
|
|
93
|
+
*/
|
|
185
94
|
set isAsync(isAsync: true) {
|
|
186
95
|
// When the transaction is noted as async we start reporting locks. A further optimization would be to not even
|
|
187
96
|
// acquire locks for synchronous transactions
|
|
@@ -318,28 +227,51 @@ class Tx implements Transaction {
|
|
|
318
227
|
}
|
|
319
228
|
|
|
320
229
|
commit() {
|
|
230
|
+
if (this.status === Status.Shared) {
|
|
231
|
+
return this.rollback();
|
|
232
|
+
}
|
|
233
|
+
|
|
321
234
|
this.#assertAvailable();
|
|
322
235
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
236
|
+
const result = this.#executeCommitCycle(0);
|
|
237
|
+
if (result) {
|
|
238
|
+
this.isAsync = true;
|
|
326
239
|
}
|
|
327
240
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const participants = [...this.#participants];
|
|
331
|
-
const result = this.#finalize(Status.CommittingPhaseOne, "committed", this.#executeCommit.bind(this));
|
|
332
|
-
if (MaybePromise.is(result)) {
|
|
333
|
-
return result.then(() => this.#executePostCommit(participants));
|
|
334
|
-
}
|
|
335
|
-
return this.#executePostCommit(participants);
|
|
336
|
-
};
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
337
243
|
|
|
338
|
-
|
|
244
|
+
resolve<T>(result: T): MaybePromise<Awaited<T>> {
|
|
245
|
+
// If result is a promise, we wait for resolution and then commit (success) or roll back (error)
|
|
339
246
|
if (MaybePromise.is(result)) {
|
|
340
|
-
|
|
247
|
+
this.isAsync = true;
|
|
248
|
+
return result.then(this.resolve.bind(this), this.reject.bind(this));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Result is not a promise. Commit immediately
|
|
252
|
+
let promise;
|
|
253
|
+
try {
|
|
254
|
+
promise = this.commit();
|
|
255
|
+
} catch (e) {
|
|
256
|
+
// Commit failed synchronously
|
|
257
|
+
return this.reject(e);
|
|
341
258
|
}
|
|
342
|
-
|
|
259
|
+
|
|
260
|
+
// If commit is async then wait for commit before destruction
|
|
261
|
+
if (MaybePromise.is(promise)) {
|
|
262
|
+
this.isAsync = true;
|
|
263
|
+
|
|
264
|
+
return Promise.resolve(promise)
|
|
265
|
+
.then(() => {
|
|
266
|
+
this[Symbol.dispose]();
|
|
267
|
+
return result as Awaited<T>;
|
|
268
|
+
}, this.reject.bind(this))
|
|
269
|
+
.finally(this[Symbol.dispose].bind(this));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Result and commit succeeded synchronously
|
|
273
|
+
this[Symbol.dispose]();
|
|
274
|
+
return result as Awaited<T>;
|
|
343
275
|
}
|
|
344
276
|
|
|
345
277
|
rollback() {
|
|
@@ -348,6 +280,79 @@ class Tx implements Transaction {
|
|
|
348
280
|
return this.#finalize(Status.RollingBack, "rolled back", () => this.#executeRollback());
|
|
349
281
|
}
|
|
350
282
|
|
|
283
|
+
reject(cause: unknown): MaybePromise<never> {
|
|
284
|
+
if (this.#status === Status.Shared) {
|
|
285
|
+
this.#reset("released");
|
|
286
|
+
throw cause;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
logger.error("Rolling back", this.via, "due to error:", Diagnostic.weak(asError(cause).message));
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
const result = this.rollback();
|
|
293
|
+
if (MaybePromise.is(result)) {
|
|
294
|
+
return Promise.resolve(result)
|
|
295
|
+
.catch(cause2 => {
|
|
296
|
+
if (cause2 === cause) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// TODO - once SuppressedError support is confirmed solid, consider using it here
|
|
301
|
+
logger.error("Secondary error in", this.via, "rollback:", cause2);
|
|
302
|
+
})
|
|
303
|
+
.finally(() => {
|
|
304
|
+
this[Symbol.dispose]();
|
|
305
|
+
throw cause;
|
|
306
|
+
}) as Promise<never>;
|
|
307
|
+
}
|
|
308
|
+
} catch (cause2) {
|
|
309
|
+
if (cause2 !== cause) {
|
|
310
|
+
logger.error("Secondary error in", this.via, "rollback:", cause2);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
this[Symbol.dispose]();
|
|
315
|
+
|
|
316
|
+
throw cause;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Execute commit logic for a single commit cycle.
|
|
321
|
+
*
|
|
322
|
+
* A "cycle" performs all commit logic and normally brings us back to shared state. But we allow post-commit
|
|
323
|
+
* handlers to re-enter exclusive state. If that happens, we trigger another commit cycle.
|
|
324
|
+
*/
|
|
325
|
+
#executeCommitCycle(count: number): MaybePromise<void> {
|
|
326
|
+
count++;
|
|
327
|
+
|
|
328
|
+
if (count > MAX_CHAINED_COMMITS) {
|
|
329
|
+
throw new TransactionFlowError(
|
|
330
|
+
`Transaction commits have cascaded ${count} times which likely indicates an infinite loop`,
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Precommit first
|
|
335
|
+
let result = this.#createPreCommitExecutor()();
|
|
336
|
+
|
|
337
|
+
// Then rest of normal commit
|
|
338
|
+
if (MaybePromise.is(result)) {
|
|
339
|
+
result = result.then(this.#executeCommit.bind(this));
|
|
340
|
+
} else {
|
|
341
|
+
result = this.#executeCommit();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Then, if transaction is once again exclusive, recurse
|
|
345
|
+
if (MaybePromise.is(result)) {
|
|
346
|
+
return result.then(() => {
|
|
347
|
+
if (this.#status === Status.Exclusive) {
|
|
348
|
+
return this.#executeCommitCycle(count);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
} else if (this.#status === Status.Exclusive) {
|
|
352
|
+
return this.#executeCommitCycle(count);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
351
356
|
waitFor(others: Set<Transaction>) {
|
|
352
357
|
this.#assertAvailable();
|
|
353
358
|
|
|
@@ -401,27 +406,6 @@ class Tx implements Transaction {
|
|
|
401
406
|
);
|
|
402
407
|
}
|
|
403
408
|
|
|
404
|
-
// Post-finalization state reset
|
|
405
|
-
const cleanup = () => {
|
|
406
|
-
// Release locks
|
|
407
|
-
const set = new ResourceSet(this, this.#resources);
|
|
408
|
-
const unlocked = set.releaseLocks();
|
|
409
|
-
this.#locksChanged(unlocked, `${why} and unlocked`);
|
|
410
|
-
|
|
411
|
-
// Reset "slow" transaction state
|
|
412
|
-
Monitor.delete(this);
|
|
413
|
-
this.#reportingLocks = false;
|
|
414
|
-
|
|
415
|
-
// Release participants
|
|
416
|
-
this.#participants.clear();
|
|
417
|
-
|
|
418
|
-
// Revert to shared
|
|
419
|
-
this.#status = Status.Shared;
|
|
420
|
-
|
|
421
|
-
// Notify listeners
|
|
422
|
-
this.#shared?.emit();
|
|
423
|
-
};
|
|
424
|
-
|
|
425
409
|
// Perform the commit or rollback
|
|
426
410
|
let isAsync = false;
|
|
427
411
|
try {
|
|
@@ -429,19 +413,47 @@ class Tx implements Transaction {
|
|
|
429
413
|
const result = finalizer();
|
|
430
414
|
if (MaybePromise.is(result)) {
|
|
431
415
|
isAsync = true;
|
|
432
|
-
return Promise.resolve(result).finally(
|
|
416
|
+
return Promise.resolve(result).finally(() => this.#reset(why));
|
|
433
417
|
}
|
|
434
418
|
} finally {
|
|
435
419
|
if (!isAsync) {
|
|
436
|
-
|
|
420
|
+
this.#reset(why);
|
|
437
421
|
}
|
|
438
422
|
}
|
|
439
423
|
}
|
|
440
424
|
|
|
425
|
+
/**
|
|
426
|
+
* Reset state to shared with no resources or participants.
|
|
427
|
+
*/
|
|
428
|
+
#reset(why: string) {
|
|
429
|
+
// Post-finalization state reset
|
|
430
|
+
// Release locks
|
|
431
|
+
const set = new ResourceSet(this, this.#resources);
|
|
432
|
+
const unlocked = set.releaseLocks();
|
|
433
|
+
this.#locksChanged(unlocked, `${why} and unlocked`);
|
|
434
|
+
|
|
435
|
+
// Remove resources
|
|
436
|
+
this.#resources.clear();
|
|
437
|
+
|
|
438
|
+
// Reset "slow" transaction state
|
|
439
|
+
Monitor.delete(this);
|
|
440
|
+
this.#reportingLocks = false;
|
|
441
|
+
|
|
442
|
+
// Release participants
|
|
443
|
+
this.#participants.clear();
|
|
444
|
+
this.#roles.clear();
|
|
445
|
+
|
|
446
|
+
// Revert to shared
|
|
447
|
+
this.#status = Status.Shared;
|
|
448
|
+
|
|
449
|
+
// Notify listeners
|
|
450
|
+
this.#shared?.emit();
|
|
451
|
+
}
|
|
452
|
+
|
|
441
453
|
/**
|
|
442
454
|
* Iteratively execute pre-commit until all participants "settle" and report no possible mutation.
|
|
443
455
|
*/
|
|
444
|
-
#
|
|
456
|
+
#createPreCommitExecutor(): () => MaybePromise<void> {
|
|
445
457
|
let mayHaveMutated = false;
|
|
446
458
|
let abortedDueToError = false;
|
|
447
459
|
let iterator = this.participants[Symbol.iterator]();
|
|
@@ -482,7 +494,7 @@ class Tx implements Transaction {
|
|
|
482
494
|
iterator = this.participants[Symbol.iterator]();
|
|
483
495
|
};
|
|
484
496
|
|
|
485
|
-
const
|
|
497
|
+
const executePreCommit = (previousResult?: boolean): MaybePromise<void> => {
|
|
486
498
|
// If an error occurred
|
|
487
499
|
if (abortedDueToError) {
|
|
488
500
|
return;
|
|
@@ -526,7 +538,7 @@ class Tx implements Transaction {
|
|
|
526
538
|
try {
|
|
527
539
|
const result = participant.preCommit?.();
|
|
528
540
|
if (MaybePromise.is(result)) {
|
|
529
|
-
return Promise.resolve(result).catch(handleError).then(
|
|
541
|
+
return Promise.resolve(result).catch(handleError).then(executePreCommit);
|
|
530
542
|
}
|
|
531
543
|
if (result) {
|
|
532
544
|
mayHaveMutated = true;
|
|
@@ -537,19 +549,33 @@ class Tx implements Transaction {
|
|
|
537
549
|
}
|
|
538
550
|
};
|
|
539
551
|
|
|
540
|
-
return
|
|
552
|
+
return executePreCommit;
|
|
541
553
|
}
|
|
542
554
|
|
|
543
555
|
/**
|
|
544
|
-
*
|
|
556
|
+
* Handle actual commit and post-commit.
|
|
545
557
|
*/
|
|
546
|
-
#executeCommit()
|
|
547
|
-
//
|
|
548
|
-
const
|
|
558
|
+
#executeCommit() {
|
|
559
|
+
// Ensure participants are immutable
|
|
560
|
+
const participants = [...this.#participants];
|
|
561
|
+
|
|
562
|
+
// Commit phases 1 & 2
|
|
563
|
+
const executeCommit = (): MaybePromise => {
|
|
564
|
+
const result = this.#executeCommit1();
|
|
565
|
+
|
|
566
|
+
if (MaybePromise.is(result)) {
|
|
567
|
+
return Promise.resolve(result).then(this.#executeCommit2.bind(this));
|
|
568
|
+
}
|
|
569
|
+
return this.#executeCommit2();
|
|
570
|
+
};
|
|
571
|
+
const result = this.#finalize(Status.CommittingPhaseOne, "committed", executeCommit);
|
|
572
|
+
|
|
573
|
+
// Post commit
|
|
574
|
+
const executePostCommit = this.#createPostCommitExecutor(participants);
|
|
549
575
|
if (MaybePromise.is(result)) {
|
|
550
|
-
return
|
|
576
|
+
return result.then(executePostCommit);
|
|
551
577
|
}
|
|
552
|
-
return
|
|
578
|
+
return executePostCommit();
|
|
553
579
|
}
|
|
554
580
|
|
|
555
581
|
#executeCommit1(): MaybePromise {
|
|
@@ -636,28 +662,39 @@ class Tx implements Transaction {
|
|
|
636
662
|
}
|
|
637
663
|
}
|
|
638
664
|
|
|
639
|
-
|
|
640
|
-
|
|
665
|
+
/**
|
|
666
|
+
* Execute post-commit phase.
|
|
667
|
+
*
|
|
668
|
+
* We notify each participant sequentially. If a participant throws, we log the error and move on to the next
|
|
669
|
+
* participant.
|
|
670
|
+
*/
|
|
671
|
+
#createPostCommitExecutor(participants: Participant[]): () => MaybePromise {
|
|
672
|
+
let i = 0;
|
|
641
673
|
|
|
642
|
-
const
|
|
643
|
-
|
|
674
|
+
const executePostCommit = (): MaybePromise => {
|
|
675
|
+
for (; i < participants.length; i++) {
|
|
676
|
+
const participant = participants[i];
|
|
644
677
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
678
|
+
try {
|
|
679
|
+
const promise = participant.postCommit?.();
|
|
648
680
|
|
|
649
|
-
|
|
681
|
+
if (MaybePromise.is(promise)) {
|
|
682
|
+
return Promise.resolve(promise).then(executePostCommit, e => {
|
|
683
|
+
reportParticipantError(e);
|
|
684
|
+
executePostCommit();
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
} catch (e) {
|
|
688
|
+
reportParticipantError(e);
|
|
689
|
+
}
|
|
650
690
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
logger.error(`Error post-commit of ${participant}:`, error);
|
|
656
|
-
},
|
|
657
|
-
);
|
|
691
|
+
function reportParticipantError(e: unknown) {
|
|
692
|
+
logger.error(`Error post-commit of ${participant}:`, e);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
658
695
|
};
|
|
659
696
|
|
|
660
|
-
return
|
|
697
|
+
return executePostCommit;
|
|
661
698
|
}
|
|
662
699
|
|
|
663
700
|
/**
|
|
@@ -684,7 +721,7 @@ class Tx implements Transaction {
|
|
|
684
721
|
},
|
|
685
722
|
);
|
|
686
723
|
|
|
687
|
-
// If
|
|
724
|
+
// If rollback is asynchronous, collect the promise
|
|
688
725
|
if (MaybePromise.is(promise)) {
|
|
689
726
|
if (ongoing) {
|
|
690
727
|
ongoing.push(promise as Promise<void>);
|
package/src/util/Observable.ts
CHANGED
|
@@ -755,7 +755,7 @@ export class QuietObservable<T extends any[] = any[]> extends BasicObservable<T>
|
|
|
755
755
|
if (this.#source && this.#sourceObserver) {
|
|
756
756
|
this.#source.off(this.#sourceObserver);
|
|
757
757
|
} else if (this.#sourceObserver === undefined) {
|
|
758
|
-
this.#sourceObserver =
|
|
758
|
+
this.#sourceObserver = this.emit.bind(this);
|
|
759
759
|
}
|
|
760
760
|
this.#source = source;
|
|
761
761
|
if (source) {
|