@matter/general 0.12.4-alpha.0-20250211-56b2c53a0 → 0.12.4-alpha.0-20250213-1187f81eb

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.
Files changed (138) hide show
  1. package/dist/cjs/MatterError.d.ts +12 -0
  2. package/dist/cjs/MatterError.d.ts.map +1 -1
  3. package/dist/cjs/MatterError.js +12 -0
  4. package/dist/cjs/MatterError.js.map +1 -1
  5. package/dist/cjs/index.d.ts +1 -0
  6. package/dist/cjs/index.d.ts.map +1 -1
  7. package/dist/cjs/index.js +1 -0
  8. package/dist/cjs/index.js.map +1 -1
  9. package/dist/cjs/log/Logger.d.ts.map +1 -1
  10. package/dist/cjs/log/Logger.js +2 -0
  11. package/dist/cjs/log/Logger.js.map +1 -1
  12. package/dist/cjs/time/Time.d.ts +1 -1
  13. package/dist/cjs/time/Time.d.ts.map +1 -1
  14. package/dist/cjs/time/Time.js +2 -2
  15. package/dist/cjs/time/Time.js.map +1 -1
  16. package/dist/cjs/transaction/Participant.d.ts +47 -0
  17. package/dist/cjs/transaction/Participant.d.ts.map +1 -0
  18. package/dist/cjs/transaction/Participant.js +22 -0
  19. package/dist/cjs/transaction/Participant.js.map +6 -0
  20. package/dist/cjs/transaction/Resource.d.ts +29 -0
  21. package/dist/cjs/transaction/Resource.d.ts.map +1 -0
  22. package/dist/cjs/transaction/Resource.js +40 -0
  23. package/dist/cjs/transaction/Resource.js.map +6 -0
  24. package/dist/cjs/transaction/ResourceSet.d.ts +36 -0
  25. package/dist/cjs/transaction/ResourceSet.d.ts.map +1 -0
  26. package/dist/cjs/transaction/ResourceSet.js +155 -0
  27. package/dist/cjs/transaction/ResourceSet.js.map +6 -0
  28. package/dist/cjs/transaction/Status.d.ts +49 -0
  29. package/dist/cjs/transaction/Status.d.ts.map +1 -0
  30. package/dist/cjs/transaction/Status.js +55 -0
  31. package/dist/cjs/transaction/Status.js.map +6 -0
  32. package/dist/cjs/transaction/Transaction.d.ts +197 -0
  33. package/dist/cjs/transaction/Transaction.d.ts.map +1 -0
  34. package/dist/cjs/transaction/Transaction.js +50 -0
  35. package/dist/cjs/transaction/Transaction.js.map +6 -0
  36. package/dist/cjs/transaction/Tx.d.ts +47 -0
  37. package/dist/cjs/transaction/Tx.d.ts.map +1 -0
  38. package/dist/cjs/transaction/Tx.js +586 -0
  39. package/dist/cjs/transaction/Tx.js.map +6 -0
  40. package/dist/cjs/transaction/errors.d.ts +52 -0
  41. package/dist/cjs/transaction/errors.d.ts.map +1 -0
  42. package/dist/cjs/transaction/errors.js +47 -0
  43. package/dist/cjs/transaction/errors.js.map +6 -0
  44. package/dist/cjs/transaction/index.d.ts +8 -0
  45. package/dist/cjs/transaction/index.d.ts.map +1 -0
  46. package/dist/cjs/transaction/index.js +25 -0
  47. package/dist/cjs/transaction/index.js.map +6 -0
  48. package/dist/cjs/util/Cancelable.d.ts +101 -0
  49. package/dist/cjs/util/Cancelable.d.ts.map +1 -0
  50. package/dist/cjs/util/Cancelable.js +279 -0
  51. package/dist/cjs/util/Cancelable.js.map +6 -0
  52. package/dist/cjs/util/Observable.js +1 -1
  53. package/dist/cjs/util/Observable.js.map +1 -1
  54. package/dist/cjs/util/Promises.d.ts +0 -15
  55. package/dist/cjs/util/Promises.d.ts.map +1 -1
  56. package/dist/cjs/util/Promises.js +0 -33
  57. package/dist/cjs/util/Promises.js.map +1 -1
  58. package/dist/cjs/util/index.d.ts +1 -0
  59. package/dist/cjs/util/index.d.ts.map +1 -1
  60. package/dist/cjs/util/index.js +1 -0
  61. package/dist/cjs/util/index.js.map +1 -1
  62. package/dist/esm/MatterError.d.ts +12 -0
  63. package/dist/esm/MatterError.d.ts.map +1 -1
  64. package/dist/esm/MatterError.js +12 -0
  65. package/dist/esm/MatterError.js.map +1 -1
  66. package/dist/esm/index.d.ts +1 -0
  67. package/dist/esm/index.d.ts.map +1 -1
  68. package/dist/esm/index.js +1 -0
  69. package/dist/esm/index.js.map +1 -1
  70. package/dist/esm/log/Logger.d.ts.map +1 -1
  71. package/dist/esm/log/Logger.js +2 -0
  72. package/dist/esm/log/Logger.js.map +1 -1
  73. package/dist/esm/time/Time.d.ts +1 -1
  74. package/dist/esm/time/Time.d.ts.map +1 -1
  75. package/dist/esm/time/Time.js +1 -1
  76. package/dist/esm/transaction/Participant.d.ts +47 -0
  77. package/dist/esm/transaction/Participant.d.ts.map +1 -0
  78. package/dist/esm/transaction/Participant.js +6 -0
  79. package/dist/esm/transaction/Participant.js.map +6 -0
  80. package/dist/esm/transaction/Resource.d.ts +29 -0
  81. package/dist/esm/transaction/Resource.d.ts.map +1 -0
  82. package/dist/esm/transaction/Resource.js +20 -0
  83. package/dist/esm/transaction/Resource.js.map +6 -0
  84. package/dist/esm/transaction/ResourceSet.d.ts +36 -0
  85. package/dist/esm/transaction/ResourceSet.d.ts.map +1 -0
  86. package/dist/esm/transaction/ResourceSet.js +135 -0
  87. package/dist/esm/transaction/ResourceSet.js.map +6 -0
  88. package/dist/esm/transaction/Status.d.ts +49 -0
  89. package/dist/esm/transaction/Status.d.ts.map +1 -0
  90. package/dist/esm/transaction/Status.js +35 -0
  91. package/dist/esm/transaction/Status.js.map +6 -0
  92. package/dist/esm/transaction/Transaction.d.ts +197 -0
  93. package/dist/esm/transaction/Transaction.d.ts.map +1 -0
  94. package/dist/esm/transaction/Transaction.js +30 -0
  95. package/dist/esm/transaction/Transaction.js.map +6 -0
  96. package/dist/esm/transaction/Tx.d.ts +47 -0
  97. package/dist/esm/transaction/Tx.d.ts.map +1 -0
  98. package/dist/esm/transaction/Tx.js +566 -0
  99. package/dist/esm/transaction/Tx.js.map +6 -0
  100. package/dist/esm/transaction/errors.d.ts +52 -0
  101. package/dist/esm/transaction/errors.d.ts.map +1 -0
  102. package/dist/esm/transaction/errors.js +27 -0
  103. package/dist/esm/transaction/errors.js.map +6 -0
  104. package/dist/esm/transaction/index.d.ts +8 -0
  105. package/dist/esm/transaction/index.d.ts.map +1 -0
  106. package/dist/esm/transaction/index.js +8 -0
  107. package/dist/esm/transaction/index.js.map +6 -0
  108. package/dist/esm/util/Cancelable.d.ts +101 -0
  109. package/dist/esm/util/Cancelable.d.ts.map +1 -0
  110. package/dist/esm/util/Cancelable.js +259 -0
  111. package/dist/esm/util/Cancelable.js.map +6 -0
  112. package/dist/esm/util/Observable.js +1 -1
  113. package/dist/esm/util/Observable.js.map +1 -1
  114. package/dist/esm/util/Promises.d.ts +0 -15
  115. package/dist/esm/util/Promises.d.ts.map +1 -1
  116. package/dist/esm/util/Promises.js +0 -33
  117. package/dist/esm/util/Promises.js.map +1 -1
  118. package/dist/esm/util/index.d.ts +1 -0
  119. package/dist/esm/util/index.d.ts.map +1 -1
  120. package/dist/esm/util/index.js +1 -0
  121. package/dist/esm/util/index.js.map +1 -1
  122. package/package.json +2 -2
  123. package/src/MatterError.ts +18 -0
  124. package/src/index.ts +1 -0
  125. package/src/log/Logger.ts +3 -0
  126. package/src/time/Time.ts +1 -1
  127. package/src/transaction/Participant.ts +54 -0
  128. package/src/transaction/Resource.ts +39 -0
  129. package/src/transaction/ResourceSet.ts +160 -0
  130. package/src/transaction/Status.ts +68 -0
  131. package/src/transaction/Transaction.ts +190 -0
  132. package/src/transaction/Tx.ts +734 -0
  133. package/src/transaction/errors.ts +53 -0
  134. package/src/transaction/index.ts +8 -0
  135. package/src/util/Cancelable.ts +380 -0
  136. package/src/util/Observable.ts +1 -1
  137. package/src/util/Promises.ts +0 -52
  138. package/src/util/index.ts +1 -0
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { MatterError } from "#MatterError.js";
8
+ import type { Transaction } from "./Transaction.js";
9
+
10
+ /**
11
+ * Thrown when a {@link Transaction} operation cannot complete because the transaction has the wrong status.
12
+ */
13
+ export class TransactionFlowError extends MatterError {}
14
+
15
+ /**
16
+ * Thrown if a {@link Transaction} attempts to lock a {@link Resource} synchronously but the resource is already locked.
17
+ *
18
+ * If you see this error you are probably modifying {@link Behavior} state. You can use
19
+ * {@link Transaction.addResources} and {@link Transaction.begin} to lock the behavior before performing your write.
20
+ */
21
+ export class SynchronousTransactionConflictError extends MatterError {}
22
+
23
+ /**
24
+ * Thrown if a transaction attempts to obtain exclusivity in a manner that would lead to deadlock.
25
+ */
26
+ export class TransactionDeadlockError extends MatterError {}
27
+
28
+ /**
29
+ * Thrown if an error occurs during commit or rollback.
30
+ */
31
+ export class FinalizationError extends MatterError {}
32
+
33
+ /**
34
+ * Thrown if a {@link Transaction} is accessed after it has been destroyed.
35
+ *
36
+ * If you see this error, you have probably kept a reference to a contextual object its exited. You may need to create
37
+ * a new, independent transaction context.
38
+ *
39
+ * A possible cause of this error is forgetting to use await on an async function. The context will remain open so long
40
+ * as there is an unresolved {@link Promise} it can await.
41
+ */
42
+ export class TransactionDestroyedError extends MatterError {}
43
+
44
+ /**
45
+ * Thrown if a {@link Transaction} cannot commit because state has mutated continuously for too many pre-commit cycles.
46
+ *
47
+ * "Pre-commit" is a commit event triggered by {@link Transaction} before stage 1 commit. During pre-commit listeners
48
+ * may mutate state. If state does change, the transaction re-runs pre-commit so all listeners see the same state.
49
+ *
50
+ * If state continues to mutate for too many of these cycles then the transaction will abort. This likely indicates a
51
+ * logic error that will result in an infinite loop.
52
+ */
53
+ export class UnsettledStateError extends FinalizationError {}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ export * from "./errors.js";
8
+ export * from "./Transaction.js";
@@ -0,0 +1,380 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import type { Logger } from "#log/Logger.js";
8
+ import { CanceledError } from "#MatterError.js";
9
+ import { errorOf } from "./Error.js";
10
+ import { createPromise, MaybePromise } from "./Promises.js";
11
+
12
+ /**
13
+ * An operation that may be canceled.
14
+ */
15
+ export interface Cancelable {
16
+ /**
17
+ * Cancel the operation.
18
+ */
19
+ cancel(reason: any): void;
20
+ }
21
+
22
+ /**
23
+ * A {@link PromiseLike} that may be canceled.
24
+ *
25
+ * Behaves like a normal promise but does not actually extend {@link Promise} because that's a huge PITA.
26
+ */
27
+ export class CancelablePromise<T = void> implements Promise<T>, Cancelable {
28
+ #reject!: (cause: any) => void;
29
+ #promise: Promise<T>;
30
+ #isSettled = false;
31
+
32
+ // Cancelable cannot create its own logger because that would create a circular dependency
33
+ static #logger: Logger | Console = console;
34
+
35
+ /**
36
+ * Create a new cancelable promise.
37
+ *
38
+ * If the promise is rejected due to cancelation, the {@link executor} callbacks have no effect.
39
+ *
40
+ * If you supply {@link onCancel} it overwrites the {@link CancelablePromise#onCancel} method.
41
+ *
42
+ * @param executor the normal executor supplied to a {@link Promise} constructor
43
+ * @param onCancel rejection handler supplied with a reason and a callback for optionally rejecting the promise
44
+ */
45
+ constructor(
46
+ executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void,
47
+ onCancel?: (reason: Error) => void,
48
+ ) {
49
+ if (onCancel !== undefined) {
50
+ this.onCancel = onCancel;
51
+ }
52
+
53
+ this.#promise = new Promise((resolve, reject) => {
54
+ this.#reject = (reason?: any) => {
55
+ this.#isSettled = true;
56
+ reject(errorOf(reason));
57
+ };
58
+
59
+ executor(
60
+ (value: T | PromiseLike<T>) => {
61
+ if (this.#isSettled) {
62
+ return;
63
+ }
64
+
65
+ this.#isSettled = true;
66
+ resolve(value);
67
+ },
68
+
69
+ (reason?: any) => {
70
+ if (this.#isSettled) {
71
+ CancelablePromise.logger.warn(`Cancelable promise rejected after settle:`, reason);
72
+ return;
73
+ }
74
+
75
+ this.#reject(reason);
76
+ },
77
+ );
78
+ });
79
+ }
80
+
81
+ /**
82
+ * Cancel the operation.
83
+ */
84
+ cancel(reason: any = new CanceledError()) {
85
+ if (this.#isSettled) {
86
+ return;
87
+ }
88
+
89
+ try {
90
+ this.onCancel(reason);
91
+ } catch (e) {
92
+ this.#reject(e);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Implement cancelation. This is only invoked if the promise has not resolved.
98
+ *
99
+ * Throwing causes the promise to reject with the error thrown. The default implementation rethrows {@link reason}.
100
+ *
101
+ * This is overwritten if there is an "onCancel" argument to the constructor.
102
+ */
103
+ protected onCancel(reason: Error) {
104
+ throw reason;
105
+ }
106
+
107
+ then<TResult1 = T, TResult2 = never>(
108
+ onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
109
+ onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,
110
+ ): CancelablePromise<TResult1 | TResult2> {
111
+ const result = this.#promise.then(onfulfilled, onrejected) as CancelablePromise<TResult1 | TResult2>;
112
+ result.cancel = this.cancel.bind(this);
113
+ return result;
114
+ }
115
+
116
+ catch<TResult = never>(
117
+ onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null,
118
+ ): CancelablePromise<T | TResult> {
119
+ return this.then(onrejected);
120
+ }
121
+
122
+ finally(onfinally?: (() => void) | null): CancelablePromise<T> {
123
+ const handler = (result: any) => {
124
+ onfinally?.();
125
+ return result;
126
+ };
127
+ return this.then(handler, handler);
128
+ }
129
+
130
+ get [Symbol.toStringTag]() {
131
+ return this.#promise[Symbol.toStringTag];
132
+ }
133
+
134
+ static is<T>(value: MaybePromise<T>): value is CancelablePromise<T> {
135
+ return MaybePromise.is(value) && typeof (value as CancelablePromise<T>).cancel === "function";
136
+ }
137
+
138
+ static resolve<T>(value: T): CancelablePromise<T> {
139
+ const result = Promise.resolve(value) as CancelablePromise<T>;
140
+ result.cancel = () => {};
141
+ return result;
142
+ }
143
+
144
+ static reject(cause: any): CancelablePromise<any> {
145
+ const result = Promise.reject(errorOf(cause)) as CancelablePromise<any>;
146
+ result.cancel = () => {};
147
+ return result;
148
+ }
149
+
150
+ static set logger(logger: Logger | Console) {
151
+ this.#logger = logger;
152
+ }
153
+
154
+ static get logger() {
155
+ return this.#logger;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * An {@link AsyncIterator} that may be canceled.
161
+ */
162
+ export class CancelableAsyncIterator<T, TReturn = T, TNext = void>
163
+ implements AsyncIterator<T, TReturn, TNext>, Cancelable
164
+ {
165
+ // The result of the final iteration
166
+ #settled?: boolean;
167
+
168
+ // The input next implementation
169
+ #next: (...[value]: [] | [TNext]) => Promise<IteratorResult<T, TReturn>>;
170
+
171
+ // We race against this promise to detect cancelation during next()
172
+ #canceled: Promise<IteratorResult<T, TReturn>>;
173
+
174
+ // Rejects #canceled
175
+ #reject!: (reason: any) => void;
176
+
177
+ /**
178
+ * Create a new instance.
179
+ *
180
+ * @param next the function that produces results
181
+ * @param onCancel if provided this will overwrite {@link onCancel}
182
+ */
183
+ constructor(
184
+ next: (...[value]: [] | [TNext]) => Promise<IteratorResult<T, TReturn>>,
185
+ onCancel?: (reason: Error) => void,
186
+ ) {
187
+ this.#next = next;
188
+ this.#canceled = new Promise((_resolve, reject) => (this.#reject = reject));
189
+ if (onCancel !== undefined) {
190
+ this.onCancel = onCancel;
191
+ }
192
+ }
193
+
194
+ next(...[value]: [] | [TNext]): Promise<IteratorResult<T, TReturn>> {
195
+ if (this.#settled) {
196
+ // This type is a lie if TReturn does not allow undefined but TS doesn't give us any options here, and
197
+ // calling next after the final iteration doesn't make semantic sense anyway. The spec does say subsequent
198
+ // calls should return done = true
199
+ return Promise.resolve({ done: true } as IteratorResult<T, TReturn>);
200
+ }
201
+
202
+ const next = value === undefined ? this.#next() : this.#next(value);
203
+
204
+ // If next resolves after we've canceled we ignore it, but if we were to do that with an error it would be
205
+ // unhandled. Errors from next will still cause an unhandled error if not caught before we set this.#final
206
+ // because we rethrow from the race below where this.#final cannot be set
207
+ next.catch(reason => {
208
+ if (this.#settled) {
209
+ CancelablePromise.logger.warn(`Cancelable async iterator rejected after return:`, reason);
210
+ }
211
+ });
212
+
213
+ return Promise.race([next, this.#canceled]).then(
214
+ result => {
215
+ // next resolved (this.#canceled can only be rejected)
216
+ if (result.done) {
217
+ this.#settled = true;
218
+ }
219
+ return result;
220
+ },
221
+ reason => {
222
+ // next or this.#canceled rejected; regardless we use this for all subsequent calls to next() since we
223
+ // will never receive TReturn
224
+ this.#settled = true;
225
+ throw reason;
226
+ },
227
+ );
228
+ }
229
+
230
+ cancel(reason: any): void {
231
+ if (this.#settled) {
232
+ return;
233
+ }
234
+
235
+ try {
236
+ this.onCancel(reason);
237
+ } catch (e) {
238
+ this.#reject(e);
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Handle cancelation.
244
+ *
245
+ * If the underlying operation supports cancelation then it is better to use that. Otherwise throwing
246
+ * {@link reason} (the default behavior) will reject the current (or next) iteration regardless of the state of the
247
+ * underyling operation.
248
+ *
249
+ * @param reason the reason provided to {@link cancel}
250
+ */
251
+ protected onCancel(reason: any) {
252
+ throw reason;
253
+ }
254
+
255
+ static is(value: unknown): value is CancelableAsyncIterator<unknown> {
256
+ return (
257
+ typeof value === "object" &&
258
+ value !== null &&
259
+ Symbol.asyncIterator in value &&
260
+ typeof value[Symbol.asyncIterator] === "function" &&
261
+ "cancel" in value &&
262
+ typeof value["cancel"] === "function"
263
+ );
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Create a function that returns a {@link CancelablePromise} and delegates cancelation internally to other async logic.
269
+ *
270
+ * The output function invokes the supplied {@link executor} with an additional "cancelable" argument. This function
271
+ * wraps supported types (currently {@link CancelablePromise}, {@link CancelableAsyncIterator} and {@link Promise}) with
272
+ * cancelation logic.
273
+ *
274
+ * Any such wrapped object behaves normally but will throw with the cancelation reason on cancel.
275
+ */
276
+ export function Cancelable<ThisT, ArgsT extends unknown[], ReturnT>(
277
+ executor: Cancelable.Executor<ThisT, ArgsT, ReturnT>,
278
+ ) {
279
+ // The proxy that invokes the executor with a "canceable" argument used for delegation
280
+ return function cancelable(this: ThisT, ...args: ArgsT): CancelablePromise<ReturnT> {
281
+ // Active delegates register here
282
+ let delegates: undefined | Set<(reason: any) => void>;
283
+
284
+ // The return value from the proxy function
285
+ return new CancelablePromise<ReturnT>(
286
+ (resolve, reject) => {
287
+ // Invoke the executor
288
+ try {
289
+ const result = executor.call(this, cancelable, ...args);
290
+
291
+ if (MaybePromise.is(result)) {
292
+ result.then(resolve, reject);
293
+ return;
294
+ }
295
+
296
+ resolve(result);
297
+ } catch (e) {
298
+ reject(e);
299
+ }
300
+
301
+ // We pass this function to the executor; the executor invokes on supported objects to perform
302
+ // delegation
303
+ function cancelable<T>(value: T): T {
304
+ // Delegation to cancelable promise - just cancel when we are canceled; unregister when the target
305
+ // resolves
306
+ if (CancelablePromise.is(value)) {
307
+ const undelegate = addDelegate(reason => value.cancel(reason));
308
+ return value.finally(undelegate) as T;
309
+ }
310
+
311
+ // Delegation to cancelable async iterators - cancel when we cancel; unregister when iteration
312
+ // completes
313
+ if (CancelableAsyncIterator.is(value)) {
314
+ const undelegate = addDelegate(reason => value.cancel(reason));
315
+ const next = value.next.bind(value);
316
+ value.next = () => {
317
+ return next().then(
318
+ result => {
319
+ if (result.done) {
320
+ undelegate();
321
+ }
322
+ return result;
323
+ },
324
+ reason => {
325
+ undelegate();
326
+ throw reason;
327
+ },
328
+ );
329
+ };
330
+
331
+ return value;
332
+ }
333
+
334
+ // Delegation to non-cancelable promises - use a race to abort the wait but operation will proceed
335
+ if (MaybePromise.is(value)) {
336
+ const { promise, rejecter } = createPromise<T>();
337
+ const undelegate = addDelegate(reason => rejecter(reason));
338
+ return Promise.race([promise, value]).finally(() => undelegate) as T;
339
+ }
340
+
341
+ // No delegation possible; just return the original value
342
+ return value;
343
+ }
344
+ },
345
+
346
+ // Our "onCancel" that delegates to any registered delegators or simply rethrows if no delegation is active
347
+ reason => {
348
+ if (!delegates?.size) {
349
+ throw reason;
350
+ }
351
+
352
+ for (const delegate of delegates) {
353
+ delegate(reason);
354
+ }
355
+ },
356
+ );
357
+
358
+ // Register a delegate
359
+ function addDelegate(delegate: (reason: any) => void) {
360
+ if (!delegates) {
361
+ delegates = new Set();
362
+ }
363
+ delegates.add(delegate);
364
+
365
+ return () => {
366
+ delegates?.delete(delegate);
367
+ };
368
+ }
369
+ };
370
+ }
371
+
372
+ export namespace Cancelable {
373
+ export interface Delegator {
374
+ <T>(value: T): T;
375
+ }
376
+
377
+ export interface Executor<ThisT, ArgsT extends unknown[], ReturnT> {
378
+ (this: ThisT, cancelable: Delegator, ...args: ArgsT): MaybePromise<ReturnT>;
379
+ }
380
+ }
@@ -434,7 +434,7 @@ export class ObservableProxy extends BasicObservable {
434
434
 
435
435
  Object.defineProperty(this.#emitter, observant, {
436
436
  get() {
437
- return this.isObserved;
437
+ return super.isObserved;
438
438
  },
439
439
  });
440
440
 
@@ -291,55 +291,3 @@ export const MaybePromise = {
291
291
  };
292
292
 
293
293
  MaybePromise.toString = () => "MaybePromise";
294
-
295
- /**
296
- * A "promise" that may be canceled.
297
- *
298
- * Behaviors like a normal promise but does not actually extend {@link Promise} because that makes extension a PITA.
299
- */
300
- export class CancelablePromise<T = void> implements Promise<T> {
301
- #promise: Promise<T>;
302
-
303
- constructor(
304
- executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void,
305
- onCancel?: () => void,
306
- ) {
307
- this.#promise = new Promise(executor);
308
- if (onCancel !== undefined) {
309
- this.cancel = onCancel;
310
- }
311
- }
312
-
313
- cancel() {}
314
-
315
- then<TResult1 = T, TResult2 = never>(
316
- onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
317
- onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,
318
- ): CancelablePromise<TResult1 | TResult2> {
319
- const result = this.#promise.then(onfulfilled, onrejected) as CancelablePromise<TResult1 | TResult2>;
320
- result.cancel = this.cancel.bind(this);
321
- return result;
322
- }
323
-
324
- catch<TResult = never>(
325
- onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null,
326
- ): CancelablePromise<T | TResult> {
327
- return this.then(onrejected);
328
- }
329
-
330
- finally(onfinally?: (() => void) | null): CancelablePromise<T> {
331
- const handler = (result: any) => {
332
- onfinally?.();
333
- return result;
334
- };
335
- return this.then(handler, handler);
336
- }
337
-
338
- get [Symbol.toStringTag]() {
339
- return this.#promise[Symbol.toStringTag];
340
- }
341
-
342
- static is<T>(value: MaybePromise<T>): value is CancelablePromise<T> {
343
- return MaybePromise.is(value) && typeof (value as CancelablePromise<T>).cancel === "function";
344
- }
345
- }
package/src/util/index.ts CHANGED
@@ -8,6 +8,7 @@ export * from "./Array.js";
8
8
  export * from "./Boot.js";
9
9
  export * from "./Bytes.js";
10
10
  export * from "./Cache.js";
11
+ export * from "./Cancelable.js";
11
12
  export * from "./Construction.js";
12
13
  export * from "./DataReader.js";
13
14
  export * from "./DataReadQueue.js";