@evolu/common 8.0.0-next.3 → 8.0.0-next.5
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/src/Assert.d.ts +10 -5
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +10 -5
- package/dist/src/Error.d.ts +5 -3
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +16 -0
- package/dist/src/Identicon.js +4 -4
- package/dist/src/LockManager.d.ts +74 -0
- package/dist/src/LockManager.d.ts.map +1 -0
- package/dist/src/LockManager.js +103 -0
- package/dist/src/Microtask.d.ts.map +1 -1
- package/dist/src/Microtask.js +90 -26
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/RefCount.d.ts.map +1 -1
- package/dist/src/RefCount.js +153 -68
- package/dist/src/Resource.d.ts +6 -6
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +211 -162
- package/dist/src/Schedule.d.ts +5 -5
- package/dist/src/Schedule.d.ts.map +1 -1
- package/dist/src/Schedule.js +53 -44
- package/dist/src/Sqlite.d.ts +2 -0
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +66 -41
- package/dist/src/Task.d.ts +99 -123
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +273 -256
- package/dist/src/Test.d.ts +14 -15
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Time.d.ts +5 -0
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +5 -0
- package/dist/src/Type.d.ts +7 -0
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +7 -0
- package/dist/src/Types.d.ts +0 -8
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +7 -6
- package/dist/src/Worker.d.ts +42 -8
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +292 -112
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/local-first/Db.d.ts +5 -4
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +43 -37
- package/dist/src/local-first/Evolu.d.ts +3 -2
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +90 -80
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Shared.d.ts +65 -54
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +390 -376
- package/package.json +1 -1
- package/src/Array.ts +8 -8
- package/src/Assert.ts +11 -6
- package/src/Error.ts +5 -4
- package/src/Identicon.ts +4 -4
- package/src/LockManager.ts +181 -0
- package/src/Microtask.ts +17 -11
- package/src/Object.ts +3 -3
- package/src/RefCount.ts +25 -21
- package/src/Resource.ts +63 -56
- package/src/Schedule.ts +70 -48
- package/src/Set.ts +4 -4
- package/src/Sqlite.ts +43 -31
- package/src/Task.ts +392 -430
- package/src/Test.ts +14 -15
- package/src/Time.ts +7 -0
- package/src/Type.ts +9 -0
- package/src/Types.ts +0 -9
- package/src/WebSocket.ts +7 -6
- package/src/Worker.ts +195 -53
- package/src/index.ts +1 -0
- package/src/local-first/Db.ts +86 -61
- package/src/local-first/Evolu.ts +74 -68
- package/src/local-first/Owner.ts +1 -1
- package/src/local-first/Query.ts +1 -1
- package/src/local-first/Shared.ts +485 -476
package/dist/src/Task.js
CHANGED
|
@@ -56,7 +56,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
56
56
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
57
57
|
});
|
|
58
58
|
import { arrayFrom, emptyArray, isNonEmptyArray, mapArray, } from "./Array.js";
|
|
59
|
-
import { assert } from "./Assert.js";
|
|
59
|
+
import { assert, assertNotDisposed } from "./Assert.js";
|
|
60
60
|
import { createConsole } from "./Console.js";
|
|
61
61
|
import { createRandomBytes } from "./Crypto.js";
|
|
62
62
|
import { eqArrayStrict } from "./Eq.js";
|
|
@@ -154,7 +154,7 @@ export const RunEvent = /*#__PURE__*/ object({
|
|
|
154
154
|
// TODO:
|
|
155
155
|
// Partial<TracerConfigDep> & // TODO:
|
|
156
156
|
// Partial<TracerDep>; // TODO:
|
|
157
|
-
const
|
|
157
|
+
const runDefaultDeps = {
|
|
158
158
|
console: createConsole(),
|
|
159
159
|
randomBytes: createRandomBytes(),
|
|
160
160
|
random: createRandom(),
|
|
@@ -175,7 +175,7 @@ const runDeps = {
|
|
|
175
175
|
* Node.js `uncaughtException`, `unhandledRejection`, and graceful shutdown
|
|
176
176
|
* handling, and `@evolu/react-native` adds React Native global error handling.
|
|
177
177
|
*
|
|
178
|
-
* {@link
|
|
178
|
+
* {@link RunDefaultDeps} provides default dependencies:
|
|
179
179
|
*
|
|
180
180
|
* - {@link Time}
|
|
181
181
|
* - {@link Console}
|
|
@@ -213,7 +213,7 @@ const runDeps = {
|
|
|
213
213
|
* };
|
|
214
214
|
*
|
|
215
215
|
* // Composition root: create a Run with custom deps
|
|
216
|
-
* type AppDeps =
|
|
216
|
+
* type AppDeps = RunDefaultDeps & ConfigDep;
|
|
217
217
|
*
|
|
218
218
|
* const appDeps: AppDeps = {
|
|
219
219
|
* ...testCreateDeps(), // or spread individual deps
|
|
@@ -226,17 +226,14 @@ const runDeps = {
|
|
|
226
226
|
* const result = await run(fetchUser("123"));
|
|
227
227
|
*
|
|
228
228
|
* // TypeScript catches missing deps at compile time:
|
|
229
|
-
* // await using run2 = createRun(); // Run<
|
|
229
|
+
* // await using run2 = createRun(); // Run<RunDefaultDeps>
|
|
230
230
|
* // run2(fetchUser("123")); // Error: Property 'config' is missing
|
|
231
231
|
* ```
|
|
232
232
|
*
|
|
233
233
|
* @group Creating Run
|
|
234
234
|
*/
|
|
235
|
-
export const createRun = (deps) => {
|
|
236
|
-
|
|
237
|
-
return createRunInternal(createRef(mergedDeps))();
|
|
238
|
-
};
|
|
239
|
-
const createRunInternal = (depsRef) => (parent, daemon, abortBehavior, concurrencyBehavior) => {
|
|
235
|
+
export const createRun = (deps) => createRunInternal({ ...runDefaultDeps, ...deps });
|
|
236
|
+
const createRunInternal = (deps, parent, daemon, abortBehavior, concurrencyBehavior) => {
|
|
240
237
|
const parentMask = parent?.abortMask ?? isAbortable;
|
|
241
238
|
let abortMask;
|
|
242
239
|
switch (abortBehavior) {
|
|
@@ -256,8 +253,7 @@ const createRunInternal = (depsRef) => (parent, daemon, abortBehavior, concurren
|
|
|
256
253
|
let result;
|
|
257
254
|
let outcome;
|
|
258
255
|
let children = emptySet;
|
|
259
|
-
const requestAbort = (
|
|
260
|
-
const abortError = reason;
|
|
256
|
+
const requestAbort = (abortError) => {
|
|
261
257
|
if (abortMask === isAbortable)
|
|
262
258
|
signalController.abort(abortError);
|
|
263
259
|
requestController.abort(abortError);
|
|
@@ -266,7 +262,6 @@ const createRunInternal = (depsRef) => (parent, daemon, abortBehavior, concurren
|
|
|
266
262
|
subscribeToAbort(parent.requestSignal, () => requestAbort(parent.requestSignal.reason), { signal: requestController.signal });
|
|
267
263
|
}
|
|
268
264
|
const emitEvent = (data) => {
|
|
269
|
-
const deps = depsRef.get();
|
|
270
265
|
if (!deps.runConfig?.eventsEnabled.get())
|
|
271
266
|
return;
|
|
272
267
|
const e = { id: self.id, timestamp: deps.time.now(), data };
|
|
@@ -274,16 +269,28 @@ const createRunInternal = (depsRef) => (parent, daemon, abortBehavior, concurren
|
|
|
274
269
|
node.onEvent?.(e);
|
|
275
270
|
}
|
|
276
271
|
};
|
|
277
|
-
const run = (task) => {
|
|
278
|
-
const childRun = createRunInternal(
|
|
272
|
+
const run = (task, taskDeps) => {
|
|
273
|
+
const childRun = createRunInternal(taskDeps === undefined
|
|
274
|
+
? deps
|
|
275
|
+
: {
|
|
276
|
+
console: deps.console,
|
|
277
|
+
randomBytes: deps.randomBytes,
|
|
278
|
+
random: deps.random,
|
|
279
|
+
time: deps.time,
|
|
280
|
+
...(deps.runConfig && {
|
|
281
|
+
runConfig: deps.runConfig,
|
|
282
|
+
}),
|
|
283
|
+
...taskDeps,
|
|
284
|
+
}, self, daemon ?? self, getAbortBehavior(task), getConcurrencyBehavior(task));
|
|
279
285
|
if (state !== running) {
|
|
280
286
|
childRun.requestAbort(runStoppedAbortError);
|
|
281
287
|
task = () => err(runStoppedAbortError);
|
|
282
288
|
}
|
|
283
289
|
else if (signalController.signal.aborted &&
|
|
284
290
|
childRun.abortMask === isAbortable) {
|
|
285
|
-
|
|
286
|
-
|
|
291
|
+
const abortError = signalController.signal.reason;
|
|
292
|
+
childRun.requestAbort(abortError);
|
|
293
|
+
task = () => err(abortError);
|
|
287
294
|
}
|
|
288
295
|
const childFiber = Object.assign(Promise.try(task, childRun)
|
|
289
296
|
.then(childRun.handleTaskFulfilled)
|
|
@@ -306,13 +313,17 @@ const createRunInternal = (depsRef) => (parent, daemon, abortBehavior, concurren
|
|
|
306
313
|
emitEvent({ type: "ChildAdded", childId: childRun.id });
|
|
307
314
|
return childFiber;
|
|
308
315
|
};
|
|
316
|
+
let snapshot = null;
|
|
317
|
+
let disposingPromise = null;
|
|
309
318
|
const self = run;
|
|
310
319
|
{
|
|
311
320
|
const run = self;
|
|
312
|
-
const id = createId(
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
321
|
+
const id = createId(deps);
|
|
322
|
+
async function orThrow(task, taskDeps) {
|
|
323
|
+
const result = taskDeps === undefined ? await self(task) : await self(task, taskDeps);
|
|
324
|
+
return getOrThrow(result);
|
|
325
|
+
}
|
|
326
|
+
run.orThrow = orThrow;
|
|
316
327
|
run.id = id;
|
|
317
328
|
run.parent = parent ?? null;
|
|
318
329
|
run.signal = signalController.signal;
|
|
@@ -330,45 +341,38 @@ const createRunInternal = (depsRef) => (parent, daemon, abortBehavior, concurren
|
|
|
330
341
|
!eqArrayStrict(snapshot.children, childSnapshots)) {
|
|
331
342
|
snapshot = {
|
|
332
343
|
id,
|
|
333
|
-
state
|
|
344
|
+
state,
|
|
334
345
|
children: childSnapshots,
|
|
335
346
|
abortMask,
|
|
336
347
|
};
|
|
337
348
|
}
|
|
338
349
|
return snapshot;
|
|
339
350
|
};
|
|
351
|
+
run.onEvent = undefined;
|
|
340
352
|
run.daemon = daemon ?? self;
|
|
341
|
-
run.create = () =>
|
|
342
|
-
|
|
353
|
+
run.create = (runDeps = deps) => {
|
|
354
|
+
const task = createDeferred().task;
|
|
355
|
+
return run.daemon(task, runDeps).run;
|
|
356
|
+
};
|
|
357
|
+
run.deps = deps;
|
|
343
358
|
run.concurrency =
|
|
344
359
|
concurrencyBehavior ?? parent?.concurrency ?? defaultConcurrency;
|
|
345
|
-
run.addDeps = (newDeps) => {
|
|
346
|
-
depsRef.modify((currentDeps) => {
|
|
347
|
-
const duplicate = Object.keys(newDeps).find((k) => k in currentDeps);
|
|
348
|
-
assert(!duplicate, `Dependency '${duplicate}' already added. ` +
|
|
349
|
-
`This assert ensures dependencies are created once. ` +
|
|
350
|
-
`Automatic deduplication would mask bugs.`);
|
|
351
|
-
return [undefined, { ...currentDeps, ...newDeps }];
|
|
352
|
-
});
|
|
353
|
-
return self;
|
|
354
|
-
};
|
|
355
360
|
run[Symbol.asyncDispose] = () => {
|
|
356
361
|
if (disposingPromise)
|
|
357
362
|
return disposingPromise;
|
|
358
363
|
state = { type: "Disposing" };
|
|
359
364
|
emitEvent({ type: "StateChanged", state });
|
|
360
365
|
requestAbort(runStoppedAbortError);
|
|
361
|
-
disposingPromise = Promise.allSettled((
|
|
362
|
-
? [
|
|
366
|
+
disposingPromise = Promise.allSettled((self.ownTaskSettled
|
|
367
|
+
? [self.ownTaskSettled.promise, ...children]
|
|
363
368
|
: children))
|
|
364
369
|
.then(lazyVoid)
|
|
365
370
|
.finally(() => {
|
|
366
371
|
/**
|
|
367
|
-
* Root and daemon Runs have no own Task, so
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
* values already set.
|
|
372
|
+
* Root and daemon Runs have no own Task, so `run.handleTaskFulfilled`
|
|
373
|
+
* never populates their terminal values. In that case disposal
|
|
374
|
+
* publishes `ok()` for both `result` and `outcome`. Task-backed Runs
|
|
375
|
+
* normally reach this point with both values already set.
|
|
372
376
|
*/
|
|
373
377
|
[result, outcome] = [result ?? ok(), outcome ?? ok()];
|
|
374
378
|
state = { type: "Settled", result, outcome };
|
|
@@ -381,15 +385,15 @@ const createRunInternal = (depsRef) => (parent, daemon, abortBehavior, concurren
|
|
|
381
385
|
run.requestAbort = requestAbort;
|
|
382
386
|
run.requestSignal = requestController.signal;
|
|
383
387
|
run.handleTaskFulfilled = (taskOutcome) => {
|
|
384
|
-
const taskResult =
|
|
385
|
-
? err(
|
|
388
|
+
const taskResult = self.signal.aborted
|
|
389
|
+
? err(self.signal.reason)
|
|
386
390
|
: taskOutcome;
|
|
387
391
|
result = taskResult;
|
|
388
392
|
outcome = taskOutcome;
|
|
389
393
|
return taskResult;
|
|
390
394
|
};
|
|
391
395
|
run.handleTaskSettled = () => {
|
|
392
|
-
|
|
396
|
+
self.ownTaskSettled?.resolve();
|
|
393
397
|
};
|
|
394
398
|
}
|
|
395
399
|
return self;
|
|
@@ -432,16 +436,19 @@ const abortBehavior = (behavior) => (task) => Object.assign((run) => run(task),
|
|
|
432
436
|
/**
|
|
433
437
|
* Makes a {@link Task} unabortable.
|
|
434
438
|
*
|
|
435
|
-
* Once started, an unabortable Task always completes
|
|
436
|
-
*
|
|
439
|
+
* Once started, an unabortable Task always completes. Abort requests are masked
|
|
440
|
+
* while it runs, and `signal.aborted` remains `false` inside the Task.
|
|
437
441
|
*
|
|
438
|
-
*
|
|
442
|
+
* `unabortable` controls abort signal propagation; it does not force work to
|
|
443
|
+
* start. If the parent {@link Run} is already disposing or settled, `run(task)`
|
|
439
444
|
* short-circuits before task execution and returns `err(AbortError)` with
|
|
440
445
|
* {@link runStoppedError} as reason. So `unabortable` means “do not interrupt
|
|
441
446
|
* this Task once it has started”, not “remove AbortError from its type”.
|
|
442
447
|
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
448
|
+
* Most callers should still propagate or ignore {@link AbortError} according to
|
|
449
|
+
* ordinary structured-concurrency ownership. When abort would violate a
|
|
450
|
+
* lifecycle invariant, await the unabortable Task and use
|
|
451
|
+
* {@link assertNotAborted} to fail fast if it could not even start.
|
|
445
452
|
*
|
|
446
453
|
* ### Example
|
|
447
454
|
*
|
|
@@ -804,7 +811,7 @@ export const timeoutError = { type: "TimeoutError" };
|
|
|
804
811
|
* @group Composition
|
|
805
812
|
* @see {@link RetryOptions}
|
|
806
813
|
*/
|
|
807
|
-
export const retry = (task, schedule, { retryable = lazyTrue, onRetry
|
|
814
|
+
export const retry = (task, schedule, { retryable = lazyTrue, onRetry } = {}) => async (run) => {
|
|
808
815
|
const step = schedule(run.deps);
|
|
809
816
|
let attempt = onePositiveInt;
|
|
810
817
|
let error;
|
|
@@ -887,7 +894,7 @@ export const retry = (task, schedule, { retryable = lazyTrue, onRetry, } = {}) =
|
|
|
887
894
|
*
|
|
888
895
|
* @group Composition
|
|
889
896
|
*/
|
|
890
|
-
export const repeat = (task, schedule, { repeatable = lazyTrue, onRepeat
|
|
897
|
+
export const repeat = (task, schedule, { repeatable = lazyTrue, onRepeat } = {}) => async (run) => {
|
|
891
898
|
const step = schedule(run.deps);
|
|
892
899
|
let lastResult;
|
|
893
900
|
let attempt = onePositiveInt;
|
|
@@ -998,37 +1005,51 @@ export const deferredDisposedError = {
|
|
|
998
1005
|
* @group Concurrency primitives
|
|
999
1006
|
*/
|
|
1000
1007
|
export const createGate = () => {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
return err(deferredDisposedError);
|
|
1008
|
-
if (isOpen)
|
|
1009
|
-
return ok();
|
|
1010
|
-
return run(deferred.task);
|
|
1011
|
-
},
|
|
1012
|
-
open: () => {
|
|
1013
|
-
if (disposed || isOpen)
|
|
1014
|
-
return;
|
|
1015
|
-
isOpen = true;
|
|
1016
|
-
deferred.resolve(ok());
|
|
1017
|
-
},
|
|
1018
|
-
close: () => {
|
|
1019
|
-
if (disposed || !isOpen)
|
|
1020
|
-
return;
|
|
1021
|
-
isOpen = false;
|
|
1022
|
-
deferred = createDeferred();
|
|
1023
|
-
},
|
|
1024
|
-
isOpen: () => isOpen,
|
|
1025
|
-
[Symbol.dispose]: () => {
|
|
1026
|
-
if (disposed)
|
|
1027
|
-
return;
|
|
1028
|
-
disposed = true;
|
|
1008
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
1009
|
+
try {
|
|
1010
|
+
const disposer = __addDisposableResource(env_1, new DisposableStack(), false);
|
|
1011
|
+
let isOpen = false;
|
|
1012
|
+
let deferred = createDeferred();
|
|
1013
|
+
disposer.defer(() => {
|
|
1029
1014
|
deferred[Symbol.dispose]();
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1015
|
+
});
|
|
1016
|
+
const disposables = disposer.move();
|
|
1017
|
+
return {
|
|
1018
|
+
wait: (run) => {
|
|
1019
|
+
if (disposables.disposed)
|
|
1020
|
+
return err(deferredDisposedError);
|
|
1021
|
+
if (isOpen)
|
|
1022
|
+
return ok();
|
|
1023
|
+
return run(deferred.task);
|
|
1024
|
+
},
|
|
1025
|
+
open: () => {
|
|
1026
|
+
assertNotDisposed(disposables);
|
|
1027
|
+
if (isOpen)
|
|
1028
|
+
return;
|
|
1029
|
+
isOpen = true;
|
|
1030
|
+
deferred.resolve(ok());
|
|
1031
|
+
},
|
|
1032
|
+
close: () => {
|
|
1033
|
+
assertNotDisposed(disposables);
|
|
1034
|
+
if (!isOpen)
|
|
1035
|
+
return;
|
|
1036
|
+
isOpen = false;
|
|
1037
|
+
deferred = createDeferred();
|
|
1038
|
+
},
|
|
1039
|
+
isOpen: () => {
|
|
1040
|
+
assertNotDisposed(disposables);
|
|
1041
|
+
return isOpen;
|
|
1042
|
+
},
|
|
1043
|
+
[Symbol.dispose]: () => disposables.dispose(),
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
catch (e_1) {
|
|
1047
|
+
env_1.error = e_1;
|
|
1048
|
+
env_1.hasError = true;
|
|
1049
|
+
}
|
|
1050
|
+
finally {
|
|
1051
|
+
__disposeResources(env_1);
|
|
1052
|
+
}
|
|
1032
1053
|
};
|
|
1033
1054
|
/**
|
|
1034
1055
|
* Creates a {@link Semaphore} that limits concurrent {@link Task}s.
|
|
@@ -1071,85 +1092,18 @@ export const createGate = () => {
|
|
|
1071
1092
|
* @group Concurrency primitives
|
|
1072
1093
|
*/
|
|
1073
1094
|
export const createSemaphore = (permits) => {
|
|
1074
|
-
const
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
const
|
|
1082
|
-
assert(requested <= permits, "Requested permits must not exceed semaphore capacity.");
|
|
1083
|
-
if (disposed)
|
|
1084
|
-
return err(semaphoreDisposedAbortError);
|
|
1085
|
-
if (waiters.length > 0 || taken + requested > permits) {
|
|
1086
|
-
const waiter = Promise.withResolvers();
|
|
1087
|
-
const waiting = {
|
|
1088
|
-
permits: requested,
|
|
1089
|
-
resolve: waiter.resolve,
|
|
1090
|
-
};
|
|
1091
|
-
waiters.push(waiting);
|
|
1092
|
-
run.onAbort((reason) => {
|
|
1093
|
-
const i = waiters.indexOf(waiting);
|
|
1094
|
-
if (i >= 0)
|
|
1095
|
-
waiters.splice(i, 1);
|
|
1096
|
-
waiter.resolve(err(createAbortError(reason)));
|
|
1097
|
-
});
|
|
1098
|
-
const permit = await waiter.promise;
|
|
1099
|
-
if (!permit.ok)
|
|
1100
|
-
return permit;
|
|
1101
|
-
}
|
|
1102
|
-
else {
|
|
1103
|
-
taken = NonNegativeInt.orThrow(taken + requested);
|
|
1104
|
-
}
|
|
1105
|
-
let fiber = null;
|
|
1106
|
-
const _ = __addDisposableResource(env_1, {
|
|
1107
|
-
[Symbol.dispose]: () => {
|
|
1108
|
-
if (fiber)
|
|
1109
|
-
fibers.delete(fiber);
|
|
1110
|
-
taken = NonNegativeInt.orThrow(taken - requested);
|
|
1111
|
-
while (waiters.length > 0) {
|
|
1112
|
-
const waiter = waiters[0];
|
|
1113
|
-
if (taken + waiter.permits > permits)
|
|
1114
|
-
break;
|
|
1115
|
-
waiters.shift();
|
|
1116
|
-
taken = NonNegativeInt.orThrow(taken + waiter.permits);
|
|
1117
|
-
waiter.resolve(ok());
|
|
1118
|
-
}
|
|
1119
|
-
},
|
|
1120
|
-
}, false);
|
|
1121
|
-
fiber = run(task);
|
|
1122
|
-
fibers.add(fiber);
|
|
1123
|
-
return await fiber;
|
|
1124
|
-
}
|
|
1125
|
-
catch (e_1) {
|
|
1126
|
-
env_1.error = e_1;
|
|
1127
|
-
env_1.hasError = true;
|
|
1128
|
-
}
|
|
1129
|
-
finally {
|
|
1130
|
-
__disposeResources(env_1);
|
|
1131
|
-
}
|
|
1132
|
-
};
|
|
1133
|
-
return {
|
|
1134
|
-
withPermits,
|
|
1135
|
-
withPermit: (task) => withPermits(1)(task),
|
|
1136
|
-
snapshot: () => ({
|
|
1137
|
-
permits,
|
|
1138
|
-
taken,
|
|
1139
|
-
waiting: NonNegativeInt.orThrow(waiters.length),
|
|
1140
|
-
available: NonNegativeInt.orThrow(permits - taken),
|
|
1141
|
-
isIdle: taken === 0 && waiters.length === 0,
|
|
1142
|
-
disposed,
|
|
1143
|
-
}),
|
|
1144
|
-
[Symbol.dispose]: () => {
|
|
1145
|
-
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
1095
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
1096
|
+
try {
|
|
1097
|
+
const disposer = __addDisposableResource(env_2, new DisposableStack(), false);
|
|
1098
|
+
const fibers = new Set();
|
|
1099
|
+
const waiters = [];
|
|
1100
|
+
let taken = zeroNonNegativeInt;
|
|
1101
|
+
disposer.defer(() => {
|
|
1102
|
+
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
1146
1103
|
try {
|
|
1147
|
-
|
|
1148
|
-
return;
|
|
1149
|
-
disposed = true;
|
|
1150
|
-
const stack = __addDisposableResource(env_2, new DisposableStack(), false);
|
|
1104
|
+
const disposer = __addDisposableResource(env_3, new DisposableStack(), false);
|
|
1151
1105
|
for (const fiber of fibers) {
|
|
1152
|
-
|
|
1106
|
+
disposer.adopt(fiber, (fiber) => {
|
|
1153
1107
|
fiber.abort(semaphoreDisposedError);
|
|
1154
1108
|
});
|
|
1155
1109
|
}
|
|
@@ -1158,15 +1112,94 @@ export const createSemaphore = (permits) => {
|
|
|
1158
1112
|
}
|
|
1159
1113
|
waiters.length = 0;
|
|
1160
1114
|
}
|
|
1161
|
-
catch (
|
|
1162
|
-
|
|
1163
|
-
|
|
1115
|
+
catch (e_3) {
|
|
1116
|
+
env_3.error = e_3;
|
|
1117
|
+
env_3.hasError = true;
|
|
1164
1118
|
}
|
|
1165
1119
|
finally {
|
|
1166
|
-
__disposeResources(
|
|
1120
|
+
__disposeResources(env_3);
|
|
1167
1121
|
}
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1122
|
+
});
|
|
1123
|
+
const disposables = disposer.move();
|
|
1124
|
+
const withPermits = (requestedPermits) => (task) => async (run) => {
|
|
1125
|
+
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
1126
|
+
try {
|
|
1127
|
+
const requested = PositiveInt.orThrow(requestedPermits);
|
|
1128
|
+
assert(requested <= permits, "Requested permits must not exceed semaphore capacity.");
|
|
1129
|
+
if (disposables.disposed)
|
|
1130
|
+
return err(semaphoreDisposedAbortError);
|
|
1131
|
+
if (waiters.length > 0 || taken + requested > permits) {
|
|
1132
|
+
const waiter = Promise.withResolvers();
|
|
1133
|
+
const waiting = {
|
|
1134
|
+
permits: requested,
|
|
1135
|
+
resolve: waiter.resolve,
|
|
1136
|
+
};
|
|
1137
|
+
waiters.push(waiting);
|
|
1138
|
+
run.onAbort((reason) => {
|
|
1139
|
+
const i = waiters.indexOf(waiting);
|
|
1140
|
+
if (i >= 0)
|
|
1141
|
+
waiters.splice(i, 1);
|
|
1142
|
+
waiter.resolve(err(createAbortError(reason)));
|
|
1143
|
+
});
|
|
1144
|
+
const permit = await waiter.promise;
|
|
1145
|
+
if (!permit.ok)
|
|
1146
|
+
return permit;
|
|
1147
|
+
}
|
|
1148
|
+
else {
|
|
1149
|
+
taken = NonNegativeInt.orThrow(taken + requested);
|
|
1150
|
+
}
|
|
1151
|
+
let fiber = null;
|
|
1152
|
+
const _ = __addDisposableResource(env_4, {
|
|
1153
|
+
[Symbol.dispose]: () => {
|
|
1154
|
+
if (fiber)
|
|
1155
|
+
fibers.delete(fiber);
|
|
1156
|
+
taken = NonNegativeInt.orThrow(taken - requested);
|
|
1157
|
+
while (waiters.length > 0) {
|
|
1158
|
+
const waiter = waiters[0];
|
|
1159
|
+
if (taken + waiter.permits > permits)
|
|
1160
|
+
break;
|
|
1161
|
+
waiters.shift();
|
|
1162
|
+
taken = NonNegativeInt.orThrow(taken + waiter.permits);
|
|
1163
|
+
waiter.resolve(ok());
|
|
1164
|
+
}
|
|
1165
|
+
},
|
|
1166
|
+
}, false);
|
|
1167
|
+
fiber = run(task);
|
|
1168
|
+
fibers.add(fiber);
|
|
1169
|
+
return await fiber;
|
|
1170
|
+
}
|
|
1171
|
+
catch (e_4) {
|
|
1172
|
+
env_4.error = e_4;
|
|
1173
|
+
env_4.hasError = true;
|
|
1174
|
+
}
|
|
1175
|
+
finally {
|
|
1176
|
+
__disposeResources(env_4);
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
return {
|
|
1180
|
+
withPermits,
|
|
1181
|
+
withPermit: (task) => withPermits(1)(task),
|
|
1182
|
+
snapshot: () => {
|
|
1183
|
+
assertNotDisposed(disposables);
|
|
1184
|
+
return {
|
|
1185
|
+
permits,
|
|
1186
|
+
taken,
|
|
1187
|
+
waiting: NonNegativeInt.orThrow(waiters.length),
|
|
1188
|
+
available: NonNegativeInt.orThrow(permits - taken),
|
|
1189
|
+
isIdle: taken === 0 && waiters.length === 0,
|
|
1190
|
+
disposed: disposables.disposed,
|
|
1191
|
+
};
|
|
1192
|
+
},
|
|
1193
|
+
[Symbol.dispose]: () => disposables.dispose(),
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
catch (e_2) {
|
|
1197
|
+
env_2.error = e_2;
|
|
1198
|
+
env_2.hasError = true;
|
|
1199
|
+
}
|
|
1200
|
+
finally {
|
|
1201
|
+
__disposeResources(env_2);
|
|
1202
|
+
}
|
|
1170
1203
|
};
|
|
1171
1204
|
/**
|
|
1172
1205
|
* Abort reason used when a {@link Semaphore} is disposed.
|
|
@@ -1184,64 +1217,80 @@ export const semaphoreDisposedError = {
|
|
|
1184
1217
|
};
|
|
1185
1218
|
const semaphoreDisposedAbortError = createAbortError(semaphoreDisposedError);
|
|
1186
1219
|
export function createSemaphoreByKey(permits, { lookup = identity } = {}) {
|
|
1187
|
-
const
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
return err(semaphoreDisposedAbortError);
|
|
1196
|
-
let semaphore = semaphoresByKey.get(key);
|
|
1197
|
-
if (!semaphore) {
|
|
1198
|
-
semaphore = createSemaphore(permits);
|
|
1199
|
-
semaphoresByKey.set(key, semaphore);
|
|
1200
|
-
}
|
|
1201
|
-
const _ = __addDisposableResource(env_3, {
|
|
1202
|
-
[Symbol.dispose]: () => {
|
|
1203
|
-
const snapshot = semaphore.snapshot();
|
|
1204
|
-
if (snapshot.isIdle) {
|
|
1205
|
-
semaphoresByKey.delete(key);
|
|
1206
|
-
semaphore[Symbol.dispose]();
|
|
1207
|
-
}
|
|
1208
|
-
},
|
|
1209
|
-
}, false);
|
|
1210
|
-
return await run(semaphore.withPermits(requestedPermits)(task));
|
|
1211
|
-
}
|
|
1212
|
-
catch (e_3) {
|
|
1213
|
-
env_3.error = e_3;
|
|
1214
|
-
env_3.hasError = true;
|
|
1215
|
-
}
|
|
1216
|
-
finally {
|
|
1217
|
-
__disposeResources(env_3);
|
|
1218
|
-
}
|
|
1219
|
-
};
|
|
1220
|
-
return {
|
|
1221
|
-
withPermit: (key, task) => withPermits(key, 1)(task),
|
|
1222
|
-
withPermits,
|
|
1223
|
-
snapshot: (key) => semaphoresByKey.get(key)?.snapshot() ?? null,
|
|
1224
|
-
[Symbol.dispose]: () => {
|
|
1225
|
-
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
1220
|
+
const env_5 = { stack: [], error: void 0, hasError: false };
|
|
1221
|
+
try {
|
|
1222
|
+
const disposer = __addDisposableResource(env_5, new DisposableStack(), false);
|
|
1223
|
+
const semaphoresByKey = createLookupMap({
|
|
1224
|
+
lookup,
|
|
1225
|
+
});
|
|
1226
|
+
disposer.defer(() => {
|
|
1227
|
+
const env_6 = { stack: [], error: void 0, hasError: false };
|
|
1226
1228
|
try {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1229
|
+
const disposer = __addDisposableResource(env_6, new DisposableStack(), false);
|
|
1230
|
+
disposer.defer(() => {
|
|
1231
|
+
semaphoresByKey.clear();
|
|
1232
|
+
});
|
|
1231
1233
|
for (const semaphore of semaphoresByKey.values()) {
|
|
1232
|
-
|
|
1234
|
+
disposer.use(semaphore);
|
|
1233
1235
|
}
|
|
1234
|
-
semaphoresByKey.clear();
|
|
1235
1236
|
}
|
|
1236
|
-
catch (
|
|
1237
|
-
|
|
1238
|
-
|
|
1237
|
+
catch (e_6) {
|
|
1238
|
+
env_6.error = e_6;
|
|
1239
|
+
env_6.hasError = true;
|
|
1239
1240
|
}
|
|
1240
1241
|
finally {
|
|
1241
|
-
__disposeResources(
|
|
1242
|
+
__disposeResources(env_6);
|
|
1242
1243
|
}
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1244
|
+
});
|
|
1245
|
+
const disposables = disposer.move();
|
|
1246
|
+
const withPermits = (key, requestedPermits) => (task) => async (run) => {
|
|
1247
|
+
const env_7 = { stack: [], error: void 0, hasError: false };
|
|
1248
|
+
try {
|
|
1249
|
+
if (disposables.disposed)
|
|
1250
|
+
return err(semaphoreDisposedAbortError);
|
|
1251
|
+
let semaphore = semaphoresByKey.get(key);
|
|
1252
|
+
if (!semaphore) {
|
|
1253
|
+
semaphore = createSemaphore(permits);
|
|
1254
|
+
semaphoresByKey.set(key, semaphore);
|
|
1255
|
+
}
|
|
1256
|
+
const _ = __addDisposableResource(env_7, {
|
|
1257
|
+
[Symbol.dispose]: () => {
|
|
1258
|
+
if (semaphoresByKey.get(key) !== semaphore)
|
|
1259
|
+
return;
|
|
1260
|
+
const snapshot = semaphore.snapshot();
|
|
1261
|
+
if (snapshot.isIdle) {
|
|
1262
|
+
semaphoresByKey.delete(key);
|
|
1263
|
+
semaphore[Symbol.dispose]();
|
|
1264
|
+
}
|
|
1265
|
+
},
|
|
1266
|
+
}, false);
|
|
1267
|
+
return await run(semaphore.withPermits(requestedPermits)(task));
|
|
1268
|
+
}
|
|
1269
|
+
catch (e_7) {
|
|
1270
|
+
env_7.error = e_7;
|
|
1271
|
+
env_7.hasError = true;
|
|
1272
|
+
}
|
|
1273
|
+
finally {
|
|
1274
|
+
__disposeResources(env_7);
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
return {
|
|
1278
|
+
withPermit: (key, task) => withPermits(key, 1)(task),
|
|
1279
|
+
withPermits,
|
|
1280
|
+
snapshot: (key) => {
|
|
1281
|
+
assertNotDisposed(disposables);
|
|
1282
|
+
return semaphoresByKey.get(key)?.snapshot() ?? null;
|
|
1283
|
+
},
|
|
1284
|
+
[Symbol.dispose]: () => disposables.dispose(),
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
catch (e_5) {
|
|
1288
|
+
env_5.error = e_5;
|
|
1289
|
+
env_5.hasError = true;
|
|
1290
|
+
}
|
|
1291
|
+
finally {
|
|
1292
|
+
__disposeResources(env_5);
|
|
1293
|
+
}
|
|
1245
1294
|
}
|
|
1246
1295
|
/**
|
|
1247
1296
|
* Creates a {@link Mutex}.
|
|
@@ -1316,39 +1365,6 @@ export const createMutexRef = (initialValue) => {
|
|
|
1316
1365
|
[Symbol.dispose]: mutex[Symbol.dispose],
|
|
1317
1366
|
};
|
|
1318
1367
|
};
|
|
1319
|
-
/**
|
|
1320
|
-
* Creates an in-process {@link LeaderLock}.
|
|
1321
|
-
*
|
|
1322
|
-
* Uses one {@link Mutex} per {@link Name}. Suitable for runtimes without a
|
|
1323
|
-
* cross-process lock manager (for example in-memory worker tests or React
|
|
1324
|
-
* Native).
|
|
1325
|
-
*
|
|
1326
|
-
* @group Concurrency primitives
|
|
1327
|
-
*/
|
|
1328
|
-
export const createInMemoryLeaderLock = () => {
|
|
1329
|
-
const mutexByName = createMutexByKey();
|
|
1330
|
-
return {
|
|
1331
|
-
lock: (name) => async (run) => {
|
|
1332
|
-
const leaseRun = run.create();
|
|
1333
|
-
const released = createDeferred();
|
|
1334
|
-
const acquired = createDeferred();
|
|
1335
|
-
void leaseRun(mutexByName.withLock(name, async (run) => {
|
|
1336
|
-
acquired.resolve(ok());
|
|
1337
|
-
await run(released.task);
|
|
1338
|
-
return ok();
|
|
1339
|
-
}));
|
|
1340
|
-
const acquiredResult = await run(acquired.task);
|
|
1341
|
-
if (!acquiredResult.ok) {
|
|
1342
|
-
assert(AbortError.is(acquiredResult.error), "Leader lock acquisition deferred must not be disposed.");
|
|
1343
|
-
void leaseRun[Symbol.asyncDispose]();
|
|
1344
|
-
return err(acquiredResult.error);
|
|
1345
|
-
}
|
|
1346
|
-
return ok({
|
|
1347
|
-
[Symbol.asyncDispose]: leaseRun[Symbol.asyncDispose],
|
|
1348
|
-
});
|
|
1349
|
-
},
|
|
1350
|
-
};
|
|
1351
|
-
};
|
|
1352
1368
|
export function all(input, options) {
|
|
1353
1369
|
return collect("all", input, options);
|
|
1354
1370
|
}
|
|
@@ -1433,7 +1449,8 @@ export const anyAbortError = { type: "AnyAbortError" };
|
|
|
1433
1449
|
const collect = (type, input, { collect = true, abortReason = type === "all" ? allAbortError : allSettledAbortError, } = {}) => {
|
|
1434
1450
|
const stopOn = type === "all" ? "error" : null;
|
|
1435
1451
|
if (isIterable(input)) {
|
|
1436
|
-
const
|
|
1452
|
+
const tasks = input;
|
|
1453
|
+
const array = arrayFrom(tasks);
|
|
1437
1454
|
if (!isNonEmptyArray(array))
|
|
1438
1455
|
return () => ok(collect ? emptyArray : undefined);
|
|
1439
1456
|
return pool(array, {
|
|
@@ -1470,7 +1487,7 @@ function pool(tasksIterable, { stopOn = null, collect, abortReason, allFailed, }
|
|
|
1470
1487
|
if (length === 0)
|
|
1471
1488
|
return () => ok(emptyArray);
|
|
1472
1489
|
return async (run) => {
|
|
1473
|
-
const
|
|
1490
|
+
const env_8 = { stack: [], error: void 0, hasError: false };
|
|
1474
1491
|
try {
|
|
1475
1492
|
const results = collect ? new Array(length) : null;
|
|
1476
1493
|
const aborted = Promise.withResolvers();
|
|
@@ -1519,8 +1536,8 @@ function pool(tasksIterable, { stopOn = null, collect, abortReason, allFailed, }
|
|
|
1519
1536
|
worker.abort(reason);
|
|
1520
1537
|
};
|
|
1521
1538
|
const workerCount = Math.min(run.concurrency, length);
|
|
1522
|
-
const workers = arrayFrom(workerCount, () => run.daemon(worker));
|
|
1523
|
-
const _ = __addDisposableResource(
|
|
1539
|
+
const workers = arrayFrom(workerCount, () => run.daemon(worker, run.deps));
|
|
1540
|
+
const _ = __addDisposableResource(env_8, new DisposableStack(), false);
|
|
1524
1541
|
_.defer(() => {
|
|
1525
1542
|
abortWorkers(abortReason);
|
|
1526
1543
|
});
|
|
@@ -1546,12 +1563,12 @@ function pool(tasksIterable, { stopOn = null, collect, abortReason, allFailed, }
|
|
|
1546
1563
|
return ok();
|
|
1547
1564
|
return allFailed === "completion" ? lastResult : lastIndexResult;
|
|
1548
1565
|
}
|
|
1549
|
-
catch (
|
|
1550
|
-
|
|
1551
|
-
|
|
1566
|
+
catch (e_8) {
|
|
1567
|
+
env_8.error = e_8;
|
|
1568
|
+
env_8.hasError = true;
|
|
1552
1569
|
}
|
|
1553
1570
|
finally {
|
|
1554
|
-
__disposeResources(
|
|
1571
|
+
__disposeResources(env_8);
|
|
1555
1572
|
}
|
|
1556
1573
|
};
|
|
1557
1574
|
}
|