@evolu/common 8.0.0-next.3 → 8.0.0-next.4
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 +5 -5
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +4 -4
- 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 +64 -40
- package/dist/src/Task.d.ts +6 -33
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +213 -204
- package/dist/src/Test.d.ts +4 -4
- 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/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 +44 -38
- 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 +387 -373
- package/package.json +1 -1
- package/src/Array.ts +8 -8
- package/src/Assert.ts +5 -5
- 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 +41 -30
- package/src/Task.ts +77 -120
- package/src/Test.ts +4 -4
- package/src/Time.ts +7 -0
- package/src/Type.ts +9 -0
- package/src/WebSocket.ts +7 -6
- package/src/Worker.ts +195 -53
- package/src/index.ts +1 -0
- package/src/local-first/Db.ts +69 -46
- 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 +472 -464
package/src/Task.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
type NonEmptyReadonlyArray,
|
|
13
13
|
} from "./Array.js";
|
|
14
14
|
import type { assertNotAborted } from "./Assert.js";
|
|
15
|
-
import { assert } from "./Assert.js";
|
|
15
|
+
import { assert, assertNotDisposed } from "./Assert.js";
|
|
16
16
|
import { type Console, type ConsoleDep, createConsole } from "./Console.js";
|
|
17
17
|
import type { RandomBytes, RandomBytesDep } from "./Crypto.js";
|
|
18
18
|
import { createRandomBytes } from "./Crypto.js";
|
|
@@ -43,7 +43,6 @@ import {
|
|
|
43
43
|
Id,
|
|
44
44
|
type InferType,
|
|
45
45
|
maxPositiveInt,
|
|
46
|
-
type Name,
|
|
47
46
|
NonNegativeInt,
|
|
48
47
|
object,
|
|
49
48
|
onePositiveInt,
|
|
@@ -327,13 +326,13 @@ import {
|
|
|
327
326
|
* ### Example
|
|
328
327
|
*
|
|
329
328
|
* ```ts
|
|
330
|
-
* await using
|
|
329
|
+
* await using disposer = new AsyncDisposableStack();
|
|
331
330
|
*
|
|
332
331
|
* const fooResult = await run(createFoo());
|
|
333
332
|
* if (!fooResult.ok) return fooResult;
|
|
334
|
-
* const foo =
|
|
333
|
+
* const foo = disposer.use(fooResult.value);
|
|
335
334
|
*
|
|
336
|
-
*
|
|
335
|
+
* disposer.defer(async () => {
|
|
337
336
|
* await foo.close();
|
|
338
337
|
* });
|
|
339
338
|
* stack.adopt(session, async (session) => {
|
|
@@ -761,9 +760,9 @@ export interface Run<D = unknown> extends AsyncDisposable {
|
|
|
761
760
|
* (config: Config): Task<void, InitError, CreateDbDep> =>
|
|
762
761
|
* async (run) => {
|
|
763
762
|
* const { createDb } = run.deps;
|
|
764
|
-
* await using
|
|
763
|
+
* await using disposer = new AsyncDisposableStack();
|
|
765
764
|
*
|
|
766
|
-
* const db =
|
|
765
|
+
* const db = disposer.use(await run.orThrow(startApp()));
|
|
767
766
|
* if (!db.ok) return db;
|
|
768
767
|
*
|
|
769
768
|
* const runWithDb = run.addDeps({ db: db.value });
|
|
@@ -2025,7 +2024,7 @@ export const retry =
|
|
|
2025
2024
|
task: Task<T, E, D>,
|
|
2026
2025
|
schedule: Schedule<Output, E>,
|
|
2027
2026
|
{
|
|
2028
|
-
retryable = lazyTrue
|
|
2027
|
+
retryable = lazyTrue,
|
|
2029
2028
|
onRetry,
|
|
2030
2029
|
}: RetryOptions<E, Output> = {},
|
|
2031
2030
|
): Task<T, RetryError<E>, D> =>
|
|
@@ -2145,7 +2144,7 @@ export const repeat =
|
|
|
2145
2144
|
task: Task<T, E, D>,
|
|
2146
2145
|
schedule: Schedule<Output, T>,
|
|
2147
2146
|
{
|
|
2148
|
-
repeatable = lazyTrue
|
|
2147
|
+
repeatable = lazyTrue,
|
|
2149
2148
|
onRepeat,
|
|
2150
2149
|
}: RepeatOptions<T, Output> = {},
|
|
2151
2150
|
): Task<T, E, D> =>
|
|
@@ -2340,36 +2339,43 @@ export interface Gate<D = unknown> extends Disposable {
|
|
|
2340
2339
|
* @group Concurrency primitives
|
|
2341
2340
|
*/
|
|
2342
2341
|
export const createGate = <D = unknown>(): Gate<D> => {
|
|
2342
|
+
using disposer = new DisposableStack();
|
|
2343
2343
|
let isOpen = false;
|
|
2344
|
-
let disposed = false;
|
|
2345
2344
|
let deferred = createDeferred<void>();
|
|
2346
2345
|
|
|
2346
|
+
disposer.defer(() => {
|
|
2347
|
+
deferred[Symbol.dispose]();
|
|
2348
|
+
});
|
|
2349
|
+
|
|
2350
|
+
const disposables = disposer.move();
|
|
2351
|
+
|
|
2347
2352
|
return {
|
|
2348
2353
|
wait: (run) => {
|
|
2349
|
-
if (disposed) return err(deferredDisposedError);
|
|
2354
|
+
if (disposables.disposed) return err(deferredDisposedError);
|
|
2350
2355
|
if (isOpen) return ok();
|
|
2351
2356
|
return run(deferred.task);
|
|
2352
2357
|
},
|
|
2353
2358
|
|
|
2354
2359
|
open: () => {
|
|
2355
|
-
|
|
2360
|
+
assertNotDisposed(disposables);
|
|
2361
|
+
if (isOpen) return;
|
|
2356
2362
|
isOpen = true;
|
|
2357
2363
|
deferred.resolve(ok());
|
|
2358
2364
|
},
|
|
2359
2365
|
|
|
2360
2366
|
close: () => {
|
|
2361
|
-
|
|
2367
|
+
assertNotDisposed(disposables);
|
|
2368
|
+
if (!isOpen) return;
|
|
2362
2369
|
isOpen = false;
|
|
2363
2370
|
deferred = createDeferred<void>();
|
|
2364
2371
|
},
|
|
2365
2372
|
|
|
2366
|
-
isOpen: () =>
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
if (disposed) return;
|
|
2370
|
-
disposed = true;
|
|
2371
|
-
deferred[Symbol.dispose]();
|
|
2373
|
+
isOpen: () => {
|
|
2374
|
+
assertNotDisposed(disposables);
|
|
2375
|
+
return isOpen;
|
|
2372
2376
|
},
|
|
2377
|
+
|
|
2378
|
+
[Symbol.dispose]: () => disposables.dispose(),
|
|
2373
2379
|
};
|
|
2374
2380
|
};
|
|
2375
2381
|
|
|
@@ -2483,10 +2489,26 @@ export const createSemaphore = (permits: Concurrency): Semaphore => {
|
|
|
2483
2489
|
readonly resolve: Callback<Result<void, AbortError>>;
|
|
2484
2490
|
}
|
|
2485
2491
|
|
|
2492
|
+
using disposer = new DisposableStack();
|
|
2486
2493
|
const fibers = new Set<Fiber>();
|
|
2487
2494
|
const waiters: Array<Waiter> = [];
|
|
2488
2495
|
let taken = zeroNonNegativeInt;
|
|
2489
|
-
|
|
2496
|
+
|
|
2497
|
+
disposer.defer(() => {
|
|
2498
|
+
using disposer = new DisposableStack();
|
|
2499
|
+
for (const fiber of fibers) {
|
|
2500
|
+
disposer.adopt(fiber, (fiber) => {
|
|
2501
|
+
fiber.abort(semaphoreDisposedError);
|
|
2502
|
+
});
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
for (const waiter of waiters) {
|
|
2506
|
+
waiter.resolve(err(semaphoreDisposedAbortError));
|
|
2507
|
+
}
|
|
2508
|
+
waiters.length = 0;
|
|
2509
|
+
});
|
|
2510
|
+
|
|
2511
|
+
const disposables = disposer.move();
|
|
2490
2512
|
|
|
2491
2513
|
const withPermits =
|
|
2492
2514
|
<T, E, D>(requestedPermits: Concurrency) =>
|
|
@@ -2499,7 +2521,7 @@ export const createSemaphore = (permits: Concurrency): Semaphore => {
|
|
|
2499
2521
|
"Requested permits must not exceed semaphore capacity.",
|
|
2500
2522
|
);
|
|
2501
2523
|
|
|
2502
|
-
if (disposed) return err(semaphoreDisposedAbortError);
|
|
2524
|
+
if (disposables.disposed) return err(semaphoreDisposedAbortError);
|
|
2503
2525
|
|
|
2504
2526
|
if (waiters.length > 0 || taken + requested > permits) {
|
|
2505
2527
|
const waiter = Promise.withResolvers<Result<void, AbortError>>();
|
|
@@ -2548,31 +2570,19 @@ export const createSemaphore = (permits: Concurrency): Semaphore => {
|
|
|
2548
2570
|
withPermit: <T, E, D>(task: Task<T, E, D>): Task<T, E, D> =>
|
|
2549
2571
|
withPermits<T, E, D>(1)(task),
|
|
2550
2572
|
|
|
2551
|
-
snapshot: () =>
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
if (disposed) return;
|
|
2562
|
-
disposed = true;
|
|
2563
|
-
|
|
2564
|
-
using stack = new DisposableStack();
|
|
2565
|
-
for (const fiber of fibers) {
|
|
2566
|
-
stack.adopt(fiber, (fiber) => {
|
|
2567
|
-
fiber.abort(semaphoreDisposedError);
|
|
2568
|
-
});
|
|
2569
|
-
}
|
|
2570
|
-
|
|
2571
|
-
for (const waiter of waiters) {
|
|
2572
|
-
waiter.resolve(err(semaphoreDisposedAbortError));
|
|
2573
|
-
}
|
|
2574
|
-
waiters.length = 0;
|
|
2573
|
+
snapshot: () => {
|
|
2574
|
+
assertNotDisposed(disposables);
|
|
2575
|
+
return {
|
|
2576
|
+
permits,
|
|
2577
|
+
taken,
|
|
2578
|
+
waiting: NonNegativeInt.orThrow(waiters.length),
|
|
2579
|
+
available: NonNegativeInt.orThrow(permits - taken),
|
|
2580
|
+
isIdle: taken === 0 && waiters.length === 0,
|
|
2581
|
+
disposed: disposables.disposed,
|
|
2582
|
+
};
|
|
2575
2583
|
},
|
|
2584
|
+
|
|
2585
|
+
[Symbol.dispose]: () => disposables.dispose(),
|
|
2576
2586
|
};
|
|
2577
2587
|
};
|
|
2578
2588
|
|
|
@@ -2659,16 +2669,29 @@ export function createSemaphoreByKey<K, L = K>(
|
|
|
2659
2669
|
permits: Concurrency,
|
|
2660
2670
|
{ lookup = identity as Lookup<K, L> }: CreateSemaphoreByKeyOptions<K, L> = {},
|
|
2661
2671
|
): SemaphoreByKey<K> {
|
|
2672
|
+
using disposer = new DisposableStack();
|
|
2662
2673
|
const semaphoresByKey = createLookupMap<K, Semaphore, L>({
|
|
2663
2674
|
lookup,
|
|
2664
2675
|
});
|
|
2665
|
-
|
|
2676
|
+
|
|
2677
|
+
disposer.defer(() => {
|
|
2678
|
+
using disposer = new DisposableStack();
|
|
2679
|
+
disposer.defer(() => {
|
|
2680
|
+
semaphoresByKey.clear();
|
|
2681
|
+
});
|
|
2682
|
+
|
|
2683
|
+
for (const semaphore of semaphoresByKey.values()) {
|
|
2684
|
+
disposer.use(semaphore);
|
|
2685
|
+
}
|
|
2686
|
+
});
|
|
2687
|
+
|
|
2688
|
+
const disposables = disposer.move();
|
|
2666
2689
|
|
|
2667
2690
|
const withPermits =
|
|
2668
2691
|
<T, E, D>(key: K, requestedPermits: Concurrency) =>
|
|
2669
2692
|
(task: Task<T, E, D>): Task<T, E, D> =>
|
|
2670
2693
|
async (run: Run<D>) => {
|
|
2671
|
-
if (disposed) return err(semaphoreDisposedAbortError);
|
|
2694
|
+
if (disposables.disposed) return err(semaphoreDisposedAbortError);
|
|
2672
2695
|
|
|
2673
2696
|
let semaphore = semaphoresByKey.get(key);
|
|
2674
2697
|
if (!semaphore) {
|
|
@@ -2678,6 +2701,8 @@ export function createSemaphoreByKey<K, L = K>(
|
|
|
2678
2701
|
|
|
2679
2702
|
using _ = {
|
|
2680
2703
|
[Symbol.dispose]: () => {
|
|
2704
|
+
if (semaphoresByKey.get(key) !== semaphore) return;
|
|
2705
|
+
|
|
2681
2706
|
const snapshot = semaphore.snapshot();
|
|
2682
2707
|
if (snapshot.isIdle) {
|
|
2683
2708
|
semaphoresByKey.delete(key);
|
|
@@ -2695,18 +2720,12 @@ export function createSemaphoreByKey<K, L = K>(
|
|
|
2695
2720
|
|
|
2696
2721
|
withPermits,
|
|
2697
2722
|
|
|
2698
|
-
snapshot: (key) =>
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
if (disposed) return;
|
|
2702
|
-
disposed = true;
|
|
2703
|
-
|
|
2704
|
-
using stack = new DisposableStack();
|
|
2705
|
-
for (const semaphore of semaphoresByKey.values()) {
|
|
2706
|
-
stack.use(semaphore);
|
|
2707
|
-
}
|
|
2708
|
-
semaphoresByKey.clear();
|
|
2723
|
+
snapshot: (key) => {
|
|
2724
|
+
assertNotDisposed(disposables);
|
|
2725
|
+
return semaphoresByKey.get(key)?.snapshot() ?? null;
|
|
2709
2726
|
},
|
|
2727
|
+
|
|
2728
|
+
[Symbol.dispose]: () => disposables.dispose(),
|
|
2710
2729
|
};
|
|
2711
2730
|
}
|
|
2712
2731
|
|
|
@@ -2931,68 +2950,6 @@ export const createMutexRef = <T>(initialValue: T): MutexRef<T> => {
|
|
|
2931
2950
|
};
|
|
2932
2951
|
};
|
|
2933
2952
|
|
|
2934
|
-
/**
|
|
2935
|
-
* Cross-platform leader lock abstraction.
|
|
2936
|
-
*
|
|
2937
|
-
* `lock` returns a {@link Task} that waits until leadership is acquired and
|
|
2938
|
-
* yields a lease.
|
|
2939
|
-
*
|
|
2940
|
-
* Returns {@link AsyncDisposable} lease. Dispose it to release leadership.
|
|
2941
|
-
*
|
|
2942
|
-
* @group Concurrency primitives
|
|
2943
|
-
*/
|
|
2944
|
-
export interface LeaderLock {
|
|
2945
|
-
readonly lock: (name: Name) => Task<AsyncDisposable>;
|
|
2946
|
-
}
|
|
2947
|
-
|
|
2948
|
-
/** @group Concurrency primitives */
|
|
2949
|
-
export interface LeaderLockDep {
|
|
2950
|
-
readonly leaderLock: LeaderLock;
|
|
2951
|
-
}
|
|
2952
|
-
|
|
2953
|
-
/**
|
|
2954
|
-
* Creates an in-process {@link LeaderLock}.
|
|
2955
|
-
*
|
|
2956
|
-
* Uses one {@link Mutex} per {@link Name}. Suitable for runtimes without a
|
|
2957
|
-
* cross-process lock manager (for example in-memory worker tests or React
|
|
2958
|
-
* Native).
|
|
2959
|
-
*
|
|
2960
|
-
* @group Concurrency primitives
|
|
2961
|
-
*/
|
|
2962
|
-
export const createInMemoryLeaderLock = (): LeaderLock => {
|
|
2963
|
-
const mutexByName = createMutexByKey<Name>();
|
|
2964
|
-
|
|
2965
|
-
return {
|
|
2966
|
-
lock: (name) => async (run) => {
|
|
2967
|
-
const leaseRun = run.create();
|
|
2968
|
-
const released = createDeferred<void>();
|
|
2969
|
-
const acquired = createDeferred<void>();
|
|
2970
|
-
|
|
2971
|
-
void leaseRun(
|
|
2972
|
-
mutexByName.withLock(name, async (run) => {
|
|
2973
|
-
acquired.resolve(ok());
|
|
2974
|
-
await run(released.task);
|
|
2975
|
-
return ok();
|
|
2976
|
-
}),
|
|
2977
|
-
);
|
|
2978
|
-
|
|
2979
|
-
const acquiredResult = await run(acquired.task);
|
|
2980
|
-
if (!acquiredResult.ok) {
|
|
2981
|
-
assert(
|
|
2982
|
-
AbortError.is(acquiredResult.error),
|
|
2983
|
-
"Leader lock acquisition deferred must not be disposed.",
|
|
2984
|
-
);
|
|
2985
|
-
void leaseRun[Symbol.asyncDispose]();
|
|
2986
|
-
return err(acquiredResult.error);
|
|
2987
|
-
}
|
|
2988
|
-
|
|
2989
|
-
return ok({
|
|
2990
|
-
[Symbol.asyncDispose]: leaseRun[Symbol.asyncDispose],
|
|
2991
|
-
});
|
|
2992
|
-
},
|
|
2993
|
-
};
|
|
2994
|
-
};
|
|
2995
|
-
|
|
2996
2953
|
/**
|
|
2997
2954
|
* Options for {@link all}, {@link allSettled}, {@link map}, and {@link mapSettled}.
|
|
2998
2955
|
*
|
package/src/Test.ts
CHANGED
|
@@ -119,10 +119,10 @@ export const testCreateDeps = (options?: {
|
|
|
119
119
|
* // If multiple tests need the same setup, create a disposable helper.
|
|
120
120
|
* // Then `await using setup` disposes everything the helper owns.
|
|
121
121
|
* const setupFoo = async () => {
|
|
122
|
-
* await using
|
|
123
|
-
* const run =
|
|
124
|
-
* const foo =
|
|
125
|
-
* const
|
|
122
|
+
* await using disposer = new AsyncDisposableStack();
|
|
123
|
+
* const run = disposer.use(testCreateRun());
|
|
124
|
+
* const foo = disposer.use(await run.orThrow(createFoo()));
|
|
125
|
+
* const disposables = disposer.move();
|
|
126
126
|
*
|
|
127
127
|
* // Return whatever the tests need: a Run with the dependency,
|
|
128
128
|
* // the dependency itself, or both.
|
package/src/Time.ts
CHANGED
|
@@ -177,6 +177,13 @@ export const minMillis = 0 as Millis;
|
|
|
177
177
|
/** Maximum {@link Millis} value. */
|
|
178
178
|
export const maxMillis = (maxMillisWithInfinity - 1) as Millis;
|
|
179
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Converts a number to {@link Millis}, rounding to the nearest millisecond and
|
|
182
|
+
* saturating overflow at {@link maxMillis}.
|
|
183
|
+
*/
|
|
184
|
+
export const saturateMillis = (value: number): Millis =>
|
|
185
|
+
Millis.orNull(Math.max(0, Math.round(value))) ?? maxMillis;
|
|
186
|
+
|
|
180
187
|
/**
|
|
181
188
|
* Converts {@link Millis} to {@link DateIso}.
|
|
182
189
|
*
|
package/src/Type.ts
CHANGED
|
@@ -2334,6 +2334,14 @@ export const formatFiniteError =
|
|
|
2334
2334
|
export const FiniteNumber = /*#__PURE__*/ finite(Number);
|
|
2335
2335
|
export type FiniteNumber = typeof FiniteNumber.Type;
|
|
2336
2336
|
|
|
2337
|
+
/**
|
|
2338
|
+
* Non-negative finite number.
|
|
2339
|
+
*
|
|
2340
|
+
* @group Number
|
|
2341
|
+
*/
|
|
2342
|
+
export const NonNegativeFiniteNumber = /*#__PURE__*/ nonNegative(FiniteNumber);
|
|
2343
|
+
export type NonNegativeFiniteNumber = typeof NonNegativeFiniteNumber.Type;
|
|
2344
|
+
|
|
2337
2345
|
/**
|
|
2338
2346
|
* Number that is a multiple of a divisor.
|
|
2339
2347
|
*
|
|
@@ -4651,6 +4659,7 @@ export const createFormatTypeError = <ExtraErrors extends TypeError = never>(
|
|
|
4651
4659
|
|
|
4652
4660
|
if (extraMessage != null) return extraMessage;
|
|
4653
4661
|
|
|
4662
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
4654
4663
|
error = error as TypeErrors;
|
|
4655
4664
|
|
|
4656
4665
|
switch (error.type) {
|
package/src/WebSocket.ts
CHANGED
|
@@ -200,7 +200,7 @@ export const createWebSocket: CreateWebSocket =
|
|
|
200
200
|
} = {},
|
|
201
201
|
) =>
|
|
202
202
|
async (run) => {
|
|
203
|
-
await using
|
|
203
|
+
await using disposer = new AsyncDisposableStack();
|
|
204
204
|
|
|
205
205
|
let socket: globalThis.WebSocket | null = null;
|
|
206
206
|
|
|
@@ -268,7 +268,7 @@ export const createWebSocket: CreateWebSocket =
|
|
|
268
268
|
return closeSocket;
|
|
269
269
|
});
|
|
270
270
|
|
|
271
|
-
const retryFiber =
|
|
271
|
+
const retryFiber = disposer.use(run.daemon(retry(connect, schedule)));
|
|
272
272
|
|
|
273
273
|
// Report RetryError (schedule exhausted) via onError callback
|
|
274
274
|
void retryFiber.then((result) => {
|
|
@@ -277,7 +277,7 @@ export const createWebSocket: CreateWebSocket =
|
|
|
277
277
|
}
|
|
278
278
|
});
|
|
279
279
|
|
|
280
|
-
const
|
|
280
|
+
const disposables = disposer.move();
|
|
281
281
|
|
|
282
282
|
return ok<WebSocket>({
|
|
283
283
|
send: (data) => {
|
|
@@ -290,14 +290,15 @@ export const createWebSocket: CreateWebSocket =
|
|
|
290
290
|
},
|
|
291
291
|
|
|
292
292
|
getReadyState: () => {
|
|
293
|
-
if (
|
|
293
|
+
if (disposables.disposed) return "closed";
|
|
294
294
|
return socket ? nativeToStringState[socket.readyState] : "connecting";
|
|
295
295
|
},
|
|
296
296
|
|
|
297
297
|
isOpen: () =>
|
|
298
|
-
!
|
|
298
|
+
!disposables.disposed &&
|
|
299
|
+
socket?.readyState === globalThis.WebSocket.OPEN,
|
|
299
300
|
|
|
300
|
-
[Symbol.asyncDispose]: () =>
|
|
301
|
+
[Symbol.asyncDispose]: () => disposables.disposeAsync(),
|
|
301
302
|
});
|
|
302
303
|
};
|
|
303
304
|
|