@evolu/common 8.0.0-next.4 → 8.0.0
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/Array.d.ts +279 -173
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +105 -101
- package/dist/src/Assert.d.ts +44 -44
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +44 -32
- package/dist/src/BigInt.d.ts +6 -3
- package/dist/src/BigInt.d.ts.map +1 -1
- package/dist/src/BigInt.js +5 -2
- package/dist/src/Brand.d.ts +27 -40
- package/dist/src/Brand.d.ts.map +1 -1
- package/dist/src/Buffer.d.ts +27 -11
- package/dist/src/Buffer.d.ts.map +1 -1
- package/dist/src/Cache.d.ts +9 -4
- package/dist/src/Cache.d.ts.map +1 -1
- package/dist/src/Cache.js +8 -3
- package/dist/src/Callbacks.d.ts +30 -19
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Console.d.ts +145 -66
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +91 -43
- package/dist/src/Crypto.d.ts +85 -42
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +44 -20
- package/dist/src/Eq.d.ts +87 -21
- package/dist/src/Eq.d.ts.map +1 -1
- package/dist/src/Eq.js +67 -19
- 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/Function.d.ts +119 -58
- package/dist/src/Function.d.ts.map +1 -1
- package/dist/src/Function.js +86 -43
- package/dist/src/Http.d.ts +394 -0
- package/dist/src/Http.d.ts.map +1 -0
- package/dist/src/Http.js +102 -0
- package/dist/src/Identicon.d.ts +16 -7
- package/dist/src/Identicon.d.ts.map +1 -1
- package/dist/src/Identicon.js +14 -5
- package/dist/src/LeakDetector.d.ts +88 -0
- package/dist/src/LeakDetector.d.ts.map +1 -0
- package/dist/src/LeakDetector.js +86 -0
- package/dist/src/LockManager.d.ts +18 -9
- package/dist/src/LockManager.d.ts.map +1 -1
- package/dist/src/LockManager.js +7 -8
- package/dist/src/Lookup.d.ts +43 -13
- package/dist/src/Lookup.d.ts.map +1 -1
- package/dist/src/Lookup.js +28 -8
- package/dist/src/Microtask.d.ts +1 -1
- package/dist/src/Microtask.d.ts.map +1 -1
- package/dist/src/Microtask.js +3 -7
- package/dist/src/Number.d.ts +49 -13
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +34 -5
- package/dist/src/Object.d.ts +160 -40
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +92 -46
- package/dist/src/Option.d.ts +10 -7
- package/dist/src/Option.d.ts.map +1 -1
- package/dist/src/Option.js +9 -6
- package/dist/src/Order.d.ts +21 -21
- package/dist/src/Order.d.ts.map +1 -1
- package/dist/src/Order.js +18 -21
- package/dist/src/Platform.d.ts +31 -0
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +98 -0
- package/dist/src/Random.d.ts +32 -17
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +12 -8
- package/dist/src/Redacted.d.ts +43 -29
- package/dist/src/Redacted.d.ts.map +1 -1
- package/dist/src/Redacted.js +14 -3
- package/dist/src/Ref.d.ts +11 -3
- package/dist/src/Ref.d.ts.map +1 -1
- package/dist/src/Ref.js +2 -2
- package/dist/src/RefCount.d.ts +2 -6
- package/dist/src/RefCount.d.ts.map +1 -1
- package/dist/src/RefCount.js +33 -90
- package/dist/src/Relation.d.ts +70 -5
- package/dist/src/Relation.d.ts.map +1 -1
- package/dist/src/Relation.js +77 -2
- package/dist/src/Resource.d.ts +590 -155
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +592 -343
- package/dist/src/Result.d.ts +838 -191
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +163 -102
- package/dist/src/Schedule.d.ts +684 -231
- package/dist/src/Schedule.d.ts.map +1 -1
- package/dist/src/Schedule.js +766 -295
- package/dist/src/Set.d.ts +89 -40
- package/dist/src/Set.d.ts.map +1 -1
- package/dist/src/Set.js +50 -29
- package/dist/src/Sqlite.d.ts +29 -72
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +41 -46
- package/dist/src/StackTrace.d.ts +40 -0
- package/dist/src/StackTrace.d.ts.map +1 -0
- package/dist/src/StackTrace.js +140 -0
- package/dist/src/Store.d.ts +5 -4
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +5 -4
- package/dist/src/String.d.ts +7 -1
- package/dist/src/String.d.ts.map +1 -1
- package/dist/src/String.js +20 -7
- package/dist/src/Task.d.ts +3572 -1770
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +1879 -1274
- package/dist/src/Test.d.ts +17 -125
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +17 -68
- package/dist/src/Time.d.ts +108 -49
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +172 -73
- package/dist/src/Type.d.ts +3542 -1926
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +3070 -1784
- package/dist/src/Types.d.ts +200 -108
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/Types.js +39 -8
- package/dist/src/WebSocket.d.ts +48 -23
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +15 -6
- package/dist/src/Worker.d.ts +94 -36
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +58 -24
- package/dist/src/index.d.ts +55 -52
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -1
- package/dist/src/intl/cs.d.ts +94 -0
- package/dist/src/intl/cs.d.ts.map +1 -0
- package/dist/src/intl/cs.js +220 -0
- package/dist/src/intl/index.d.ts +7 -0
- package/dist/src/intl/index.d.ts.map +1 -0
- package/dist/src/intl/index.js +6 -0
- package/dist/src/local-first/Db.d.ts +12 -8
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +109 -132
- package/dist/src/local-first/Error.d.ts +4 -4
- package/dist/src/local-first/Evolu.d.ts +338 -92
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +47 -55
- package/dist/src/local-first/LocalAuth.d.ts +4 -4
- package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
- package/dist/src/local-first/Owner.d.ts +61 -24
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +45 -8
- package/dist/src/local-first/Protocol.d.ts +44 -31
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +36 -13
- package/dist/src/local-first/Query.d.ts +147 -62
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Query.js +112 -58
- package/dist/src/local-first/Relay.d.ts +44 -14
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Schema.d.ts +92 -41
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +25 -8
- package/dist/src/local-first/Shared.d.ts +23 -21
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +100 -57
- package/dist/src/local-first/Storage.d.ts +53 -32
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +194 -234
- package/dist/src/local-first/Timestamp.d.ts +21 -21
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +2 -2
- package/dist/src/local-first/index.d.ts +11 -11
- package/package.json +14 -13
- package/src/Array.ts +306 -205
- package/src/Assert.ts +54 -69
- package/src/BigInt.ts +6 -3
- package/src/Brand.ts +27 -40
- package/src/Buffer.ts +28 -12
- package/src/Cache.ts +9 -4
- package/src/Callbacks.ts +30 -19
- package/src/Console.ts +156 -84
- package/src/Crypto.ts +83 -41
- package/src/Eq.ts +95 -22
- package/src/Error.ts +8 -4
- package/src/Function.ts +167 -59
- package/src/Http.ts +549 -0
- package/src/Identicon.ts +16 -7
- package/src/LeakDetector.ts +182 -0
- package/src/LockManager.ts +43 -39
- package/src/Lookup.ts +62 -28
- package/src/Microtask.ts +19 -23
- package/src/Number.ts +64 -12
- package/src/Object.ts +173 -49
- package/src/Option.ts +10 -7
- package/src/Order.ts +23 -21
- package/src/Platform.ts +136 -0
- package/src/Polyfills.ts +6 -12
- package/src/Random.ts +32 -19
- package/src/Redacted.ts +44 -30
- package/src/Ref.ts +14 -6
- package/src/RefCount.ts +38 -80
- package/src/Relation.ts +202 -10
- package/src/Resource.ts +1199 -591
- package/src/Result.ts +930 -271
- package/src/Schedule.ts +859 -343
- package/src/Set.ts +89 -40
- package/src/Sqlite.ts +144 -138
- package/src/StackTrace.ts +230 -0
- package/src/Store.ts +8 -7
- package/src/String.ts +22 -5
- package/src/Task.ts +5101 -2832
- package/src/Test.ts +20 -168
- package/src/Time.ts +293 -90
- package/src/Type.ts +10034 -4117
- package/src/Types.ts +227 -110
- package/src/WebSocket.ts +64 -36
- package/src/Worker.ts +180 -72
- package/src/index.ts +55 -52
- package/src/intl/cs.ts +388 -0
- package/src/intl/index.ts +7 -0
- package/src/local-first/Db.ts +152 -179
- package/src/local-first/Error.ts +4 -4
- package/src/local-first/Evolu.ts +495 -250
- package/src/local-first/LocalAuth.ts +6 -12
- package/src/local-first/Owner.ts +58 -21
- package/src/local-first/Protocol.ts +49 -31
- package/src/local-first/Query.ts +146 -61
- package/src/local-first/Relay.ts +51 -21
- package/src/local-first/Schema.ts +109 -51
- package/src/local-first/Shared.ts +266 -226
- package/src/local-first/Storage.ts +266 -264
- package/src/local-first/Timestamp.ts +25 -18
- package/src/local-first/index.ts +11 -11
- package/dist/src/Skiplist.d.ts +0 -28
- package/dist/src/Skiplist.d.ts.map +0 -1
- package/dist/src/Skiplist.js +0 -64
- package/dist/src/Tracer.d.ts +0 -48
- package/dist/src/Tracer.d.ts.map +0 -1
- package/dist/src/Tracer.js +0 -6
- package/src/Skiplist.ts +0 -108
- package/src/Tracer.ts +0 -54
package/dist/src/Resource.d.ts
CHANGED
|
@@ -3,261 +3,696 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import
|
|
7
|
-
import { type
|
|
8
|
-
import { type
|
|
9
|
-
import {
|
|
10
|
-
|
|
6
|
+
import type { NonEmptyReadonlyArray } from "./Array.ts";
|
|
7
|
+
import { type Lookup, type LookupOption } from "./Lookup.ts";
|
|
8
|
+
import { type SemaphoreSnapshot, type Task } from "./Task.ts";
|
|
9
|
+
import { type PositiveDuration } from "./Time.ts";
|
|
10
|
+
import { NonNegativeInt } from "./Type.ts";
|
|
11
|
+
import { type DistributiveOmit } from "./Types.ts";
|
|
11
12
|
/**
|
|
12
13
|
* Disposable resource.
|
|
13
14
|
*
|
|
14
15
|
* A resource is any object that implements {@link Disposable} or
|
|
15
16
|
* {@link AsyncDisposable}.
|
|
16
17
|
*
|
|
18
|
+
* Successfully returning an owned Resource transfers ownership of a live
|
|
19
|
+
* resource to the caller. Return a typed Result error for a recoverable
|
|
20
|
+
* creation failure; use `undefined` only when absence is a valid success. If
|
|
21
|
+
* creation fails, aborts, or defects, use {@link AsyncDisposableStack} to
|
|
22
|
+
* dispose partially created resources and return no Resource. Never represent
|
|
23
|
+
* failed creation with an already-disposed Resource.
|
|
24
|
+
*
|
|
17
25
|
* Disposal must succeed. A disposer that throws indicates an unrecoverable
|
|
18
26
|
* invariant violation, not a recoverable domain error, so resource lifecycle
|
|
19
|
-
* APIs let that error propagate.
|
|
20
|
-
*
|
|
21
|
-
* resource helpers is to guarantee cleanup and prevent leaks.
|
|
27
|
+
* APIs let that error propagate as a defect. The purpose of resource helpers is
|
|
28
|
+
* to guarantee cleanup and prevent leaks.
|
|
22
29
|
*
|
|
23
|
-
* @see {@link ResourceRef}
|
|
24
|
-
* @see {@link createResourceRef}
|
|
25
30
|
* @see {@link SharedResource}
|
|
26
31
|
* @see {@link createSharedResource}
|
|
27
|
-
* @see {@link SharedResourceByKey}
|
|
28
|
-
* @see {@link createSharedResourceByKey}
|
|
29
32
|
*/
|
|
30
33
|
export type Resource = Disposable | AsyncDisposable;
|
|
31
34
|
/**
|
|
32
35
|
* Borrowed {@link Resource}.
|
|
33
36
|
*
|
|
34
|
-
* A borrowed resource
|
|
37
|
+
* A borrowed resource removes disposal methods from a {@link Resource}'s type.
|
|
38
|
+
* The runtime object is unchanged.
|
|
35
39
|
*
|
|
36
40
|
* Another abstraction owns the resource and controls its lifecycle. Exposing
|
|
37
|
-
* disposal would break that ownership and allow callers to dispose
|
|
38
|
-
* they do not own.
|
|
41
|
+
* disposal in the type would break that ownership and allow callers to dispose
|
|
42
|
+
* a resource they do not own.
|
|
39
43
|
*/
|
|
40
|
-
export type BorrowedResource<T extends Resource> =
|
|
44
|
+
export type BorrowedResource<T extends Resource> = DistributiveOmit<T, typeof Symbol.dispose | typeof Symbol.asyncDispose>;
|
|
41
45
|
/**
|
|
42
|
-
* {@link Resource}
|
|
46
|
+
* An owned lease on a shared {@link Resource}.
|
|
43
47
|
*
|
|
44
|
-
* A
|
|
45
|
-
*
|
|
48
|
+
* A lease keeps the underlying resource alive. Release it with
|
|
49
|
+
* {@link Lease.release} or `using`; releasing the last lease starts the resource
|
|
50
|
+
* disposal path. Release is idempotent, so double release is a safe no-op.
|
|
46
51
|
*
|
|
47
|
-
*
|
|
48
|
-
* the
|
|
52
|
+
* Release is synchronous accounting and never waits for resource disposal.
|
|
53
|
+
* Disposal completion belongs to the owning {@link SharedResource}: it may be
|
|
54
|
+
* delayed by {@link SharedResourceOptions.idleDisposeAfter | idleDisposeAfter}
|
|
55
|
+
* and is awaited by the owner's async disposal.
|
|
49
56
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
57
|
+
* Release does not revoke {@link Lease.resource | resource}; a released lease
|
|
58
|
+
* keeps a plain reference, and using it after the resource is disposed is a
|
|
59
|
+
* programmer error that nothing catches statically. Create resources with the
|
|
60
|
+
* {@link disposable} helper so use-after-dispose throws eagerly instead of
|
|
61
|
+
* operating on disposed state.
|
|
54
62
|
*/
|
|
55
|
-
export interface
|
|
56
|
-
/**
|
|
57
|
-
readonly
|
|
63
|
+
export interface Lease<T extends Resource> extends Disposable {
|
|
64
|
+
/** The leased resource. */
|
|
65
|
+
readonly resource: BorrowedResource<T>;
|
|
66
|
+
/** Whether this acquisition created the leased resource generation. */
|
|
67
|
+
readonly created: boolean;
|
|
58
68
|
/**
|
|
59
|
-
*
|
|
69
|
+
* Releases this lease.
|
|
60
70
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
71
|
+
* Returns whether this call released a still-held lease. Returns `false` if
|
|
72
|
+
* the lease was already released, drained by disposal of its owning
|
|
73
|
+
* {@link SharedResource}, or drained by disposal of the Run tree that owns
|
|
74
|
+
* it.
|
|
63
75
|
*/
|
|
64
|
-
readonly
|
|
76
|
+
readonly release: () => boolean;
|
|
65
77
|
}
|
|
66
|
-
/** Creates {@link ResourceRef}. */
|
|
67
|
-
export declare const createResourceRef: <T extends Resource, D>(create: Task<T, never, D>) => Task<ResourceRef<T, D>, never, D>;
|
|
68
78
|
/**
|
|
69
79
|
* Shared {@link Resource}.
|
|
70
80
|
*
|
|
71
|
-
* Lazily
|
|
72
|
-
* {@link SharedResource.acquire | acquire} call, shares it across callers
|
|
73
|
-
* disposes it when the last
|
|
74
|
-
*
|
|
81
|
+
* Lazily creates the underlying resource on the first
|
|
82
|
+
* {@link SharedResource.acquire | acquire} call, shares it across callers via
|
|
83
|
+
* {@link Lease}s, and disposes it when the last lease is released.
|
|
84
|
+
*
|
|
85
|
+
* ### Example
|
|
86
|
+
*
|
|
87
|
+
* ```ts
|
|
88
|
+
* import {
|
|
89
|
+
* all,
|
|
90
|
+
* createRun,
|
|
91
|
+
* createSharedResource,
|
|
92
|
+
* ok,
|
|
93
|
+
* type Task,
|
|
94
|
+
* } from "@evolu/common";
|
|
95
|
+
*
|
|
96
|
+
* interface Connection extends Disposable {
|
|
97
|
+
* readonly send: (message: string) => void;
|
|
98
|
+
* }
|
|
99
|
+
*
|
|
100
|
+
* let createdCount = 0;
|
|
101
|
+
* const createConnection: Task<Connection> = () => {
|
|
102
|
+
* createdCount += 1;
|
|
103
|
+
* return ok({
|
|
104
|
+
* send: (_message) => {},
|
|
105
|
+
* [Symbol.dispose]: () => {},
|
|
106
|
+
* });
|
|
107
|
+
* };
|
|
108
|
+
*
|
|
109
|
+
* await using run = createRun();
|
|
110
|
+
* await using sharedConnection = await run.ok(
|
|
111
|
+
* createSharedResource(createConnection, { idleDisposeAfter: "5s" }),
|
|
112
|
+
* );
|
|
113
|
+
*
|
|
114
|
+
* // Creating the owner is lazy; no connection is open yet.
|
|
115
|
+
* expect(createdCount).toBe(0);
|
|
116
|
+
*
|
|
117
|
+
* // `use` owns and releases a lease around each operation.
|
|
118
|
+
* const send = (message: string): Task<void> =>
|
|
119
|
+
* sharedConnection.use((connection) => () => {
|
|
120
|
+
* connection.send(message);
|
|
121
|
+
* return ok();
|
|
122
|
+
* });
|
|
123
|
+
*
|
|
124
|
+
* {
|
|
125
|
+
* // An explicit lease can span several operations. Concurrent `use` calls
|
|
126
|
+
* // share the connection kept alive by this lease.
|
|
127
|
+
* using batchLease = await run.ok(sharedConnection.acquire);
|
|
128
|
+
* batchLease.resource.send("first");
|
|
129
|
+
* batchLease.resource.send("second");
|
|
130
|
+
* await run.ok(all([send("hello"), send("world")], { concurrency: 2 }));
|
|
131
|
+
* }
|
|
132
|
+
*
|
|
133
|
+
* // Reacquiring during the idle delay reuses the same generation.
|
|
134
|
+
* using reusedLease = await run.ok(sharedConnection.acquire);
|
|
135
|
+
* expect(reusedLease.created).toBe(false);
|
|
136
|
+
* expect(createdCount).toBe(1);
|
|
137
|
+
* ```
|
|
138
|
+
*
|
|
139
|
+
* ## FAQ
|
|
140
|
+
*
|
|
141
|
+
* ### Why release a lease with `using`?
|
|
142
|
+
*
|
|
143
|
+
* `using` guarantees a {@link Lease} is released on every exit path: normal
|
|
144
|
+
* completion, a thrown error, and abort — abort surfaces as an exception in
|
|
145
|
+
* Task code, so stack unwinding runs disposers. Binding a lease to an ordinary
|
|
146
|
+
* `const` instead is a deliberate ownership transfer; the new owner must
|
|
147
|
+
* guarantee release.
|
|
75
148
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
149
|
+
* ### What happens when a lease leaks?
|
|
150
|
+
*
|
|
151
|
+
* Nothing in JavaScript enforces `using` or release; a lease that is never
|
|
152
|
+
* released compiles silently (see [MDN resource management
|
|
153
|
+
* pitfalls](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Resource_management#pitfalls);
|
|
154
|
+
* a lint rule may eventually close this gap:
|
|
155
|
+
* https://github.com/typescript-eslint/typescript-eslint/issues/8255). Encoding
|
|
156
|
+
* resource lifetime in the type system would not be bulletproof either: types
|
|
157
|
+
* can force a lifetime to exist, but a lifetime scoped too widely leaks just as
|
|
158
|
+
* silently. Evolu therefore stays JS-native — `using` in the language — and
|
|
159
|
+
* bounds the damage structurally. A leaked lease keeps the resource alive but
|
|
160
|
+
* never past its owner: disposing a SharedResource drains all outstanding
|
|
161
|
+
* leases and disposes the resource. A leaked SharedResource is bounded by its
|
|
162
|
+
* Run, whose disposal also drains its leases and disposes its current resource.
|
|
163
|
+
* Leaks are also observable — {@link SharedResource.snapshot | snapshot} exposes
|
|
164
|
+
* a lease count that never returns to zero — and detected: in development
|
|
165
|
+
* builds, a lease that is garbage-collected without release logs a warning with
|
|
166
|
+
* its acquire stack via the LeakDetector dependency.
|
|
79
167
|
*/
|
|
80
|
-
export interface SharedResource<T extends Resource
|
|
168
|
+
export interface SharedResource<T extends Resource> extends AsyncDisposable {
|
|
81
169
|
/**
|
|
82
|
-
* Acquires a shared
|
|
170
|
+
* Acquires a {@link Lease} on the shared resource.
|
|
83
171
|
*
|
|
84
172
|
* The first call lazily creates the resource. Later calls reuse the same
|
|
85
|
-
* resource until the
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
173
|
+
* resource until the last lease is released.
|
|
174
|
+
*
|
|
175
|
+
* Once started, acquire runs to completion even when the caller aborts its
|
|
176
|
+
* Fiber, and the returned lease still counts as held. If resource creation or
|
|
177
|
+
* disposal is in progress, acquire waits for it to complete. Always await the
|
|
178
|
+
* result and release the lease; a caller that stops waiting (for example with
|
|
179
|
+
* the `daemon` helper) abandons a lease that is never released, retaining the
|
|
180
|
+
* resource until the owning {@link SharedResource} is disposed.
|
|
89
181
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
182
|
+
* Owner disposal is different from caller abort: if disposal starts before a
|
|
183
|
+
* lease is transferred, acquire aborts with `runDisposedAbortReason` from the
|
|
184
|
+
* SharedResource's internal Run. A resource returned by `create` after
|
|
185
|
+
* shutdown starts remains owned by this SharedResource and is disposed
|
|
186
|
+
* without a lease escaping. Transfer occurs when the internal acquisition
|
|
187
|
+
* completes, before the caller necessarily resumes. If owner disposal starts
|
|
188
|
+
* in that gap, the caller can receive a lease already drained by disposal.
|
|
189
|
+
* Its `release` returns `false`, and its resource must not be used.
|
|
94
190
|
*/
|
|
95
|
-
readonly acquire: Task<
|
|
191
|
+
readonly acquire: Task<Lease<T>>;
|
|
96
192
|
/**
|
|
97
|
-
*
|
|
193
|
+
* Acquires a {@link Lease} on the current resource without creating one.
|
|
98
194
|
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* disposal
|
|
103
|
-
|
|
195
|
+
* Waits for preceding creation or disposal to finish. Returns `undefined` if
|
|
196
|
+
* no current resource remains. Owner disposal while waiting aborts the
|
|
197
|
+
* acquisition before a lease is transferred. As with `acquire`, owner
|
|
198
|
+
* disposal after transfer can drain the lease before the caller resumes.
|
|
199
|
+
*/
|
|
200
|
+
readonly acquireCurrent: Task<Lease<T> | undefined>;
|
|
201
|
+
/**
|
|
202
|
+
* Acquires a {@link Lease}, runs a Task with the shared resource, and releases
|
|
203
|
+
* the lease after the Task settles.
|
|
104
204
|
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
205
|
+
* Creates the resource when absent. The callback receives whether this use
|
|
206
|
+
* created the resource generation. While the owner remains running, the lease
|
|
207
|
+
* prevents ordinary idle disposal. Disposing this SharedResource is forceful:
|
|
208
|
+
* it drains the lease and may dispose the resource before or while the
|
|
209
|
+
* callback Task runs.
|
|
107
210
|
*/
|
|
108
|
-
readonly
|
|
109
|
-
/** Returns the current acquire count. */
|
|
110
|
-
readonly getCount: Task<NonNegativeInt, never, D>;
|
|
111
|
-
/** Returns the current resource, or `undefined` if absent. */
|
|
112
|
-
readonly get: () => BorrowedResource<T> | undefined;
|
|
211
|
+
readonly use: <R, E, D>(callback: (resource: BorrowedResource<T>, created: boolean) => Task<R, E, D>) => Task<R, E, D>;
|
|
113
212
|
/** Returns the current shared-resource state for monitoring/debugging. */
|
|
114
213
|
readonly snapshot: () => SharedResourceSnapshot;
|
|
115
214
|
}
|
|
116
215
|
/** Snapshot returned by {@link SharedResource.snapshot}. */
|
|
117
216
|
export interface SharedResourceSnapshot {
|
|
118
217
|
/**
|
|
119
|
-
* Whether the resource has no current value, no
|
|
120
|
-
*
|
|
218
|
+
* Whether the resource has no current value, no leases, no pending idle
|
|
219
|
+
* disposal, and no acquisition in progress.
|
|
121
220
|
*/
|
|
122
221
|
readonly isIdle: boolean;
|
|
123
|
-
/** Current
|
|
222
|
+
/** Current active lease count. */
|
|
223
|
+
readonly leaseCount: NonNegativeInt;
|
|
224
|
+
/** Whether a current resource exists. */
|
|
225
|
+
readonly hasResource: boolean;
|
|
226
|
+
/** Whether delayed idle disposal is scheduled and pending. */
|
|
227
|
+
readonly idleDisposePending: boolean;
|
|
228
|
+
/** Current internal mutex state for monitoring/debugging. */
|
|
124
229
|
readonly mutex: SemaphoreSnapshot;
|
|
125
230
|
}
|
|
126
231
|
/** Options for {@link createSharedResource}. */
|
|
127
232
|
export interface SharedResourceOptions {
|
|
128
233
|
/**
|
|
129
|
-
* Keeps the resource alive briefly after the last
|
|
234
|
+
* Keeps the resource alive briefly after the last lease is released.
|
|
130
235
|
*
|
|
131
236
|
* This avoids immediate disposal when the resource is expensive to create and
|
|
132
237
|
* likely to be acquired again soon. A new acquire during this delay cancels
|
|
133
238
|
* the pending disposal and reuses the current resource.
|
|
134
239
|
*/
|
|
135
|
-
readonly idleDisposeAfter?:
|
|
136
|
-
/**
|
|
137
|
-
|
|
240
|
+
readonly idleDisposeAfter?: PositiveDuration | undefined;
|
|
241
|
+
/**
|
|
242
|
+
* Called after each current resource disposal is attempted and the current
|
|
243
|
+
* reference is cleared, including during {@link SharedResource} disposal.
|
|
244
|
+
* Native disposal still calls it if the resource disposer defects. Not called
|
|
245
|
+
* if no resource was created.
|
|
246
|
+
*/
|
|
247
|
+
readonly onDisposed?: (() => void) | undefined;
|
|
138
248
|
}
|
|
139
|
-
/**
|
|
140
|
-
|
|
249
|
+
/**
|
|
250
|
+
* Creates {@link SharedResource}.
|
|
251
|
+
*
|
|
252
|
+
* The `create` Task must not fail. Creation establishes shared resource state
|
|
253
|
+
* and may serve multiple concurrent acquirers, so a recoverable failure does
|
|
254
|
+
* not belong to one lease. Handle recoverable failures inside `create`: retry
|
|
255
|
+
* until creation succeeds (for example with a Schedule), or return a resource
|
|
256
|
+
* whose state models connection and reconnection failures.
|
|
257
|
+
*
|
|
258
|
+
* A successfully returned resource must be live and independently owned. The
|
|
259
|
+
* `create` Task must not bind its disposal to that Task's {@link DisposableRun}
|
|
260
|
+
* with `defer`.
|
|
261
|
+
*
|
|
262
|
+
* Create resources with {@link disposable} when their shape supports it. Its
|
|
263
|
+
* disposal guard makes calls through a lease fail eagerly after resource
|
|
264
|
+
* disposal instead of operating on disposed state.
|
|
265
|
+
*
|
|
266
|
+
* The `create` Task runs while acquisition is locked and must not directly or
|
|
267
|
+
* transitively acquire from the same SharedResource. The lock is non-reentrant,
|
|
268
|
+
* so doing so deadlocks.
|
|
269
|
+
*
|
|
270
|
+
* Internal work runs on a Run created from the Run that executes this Task, so
|
|
271
|
+
* dependencies are captured at creation time. Deps provided to later acquire
|
|
272
|
+
* calls do not change what `create` observes.
|
|
273
|
+
*
|
|
274
|
+
* Lifecycle callbacks must not throw. A throwing
|
|
275
|
+
* {@link SharedResourceOptions.onDisposed | onDisposed} is a defect that panics
|
|
276
|
+
* the Run tree.
|
|
277
|
+
*/
|
|
278
|
+
export declare const createSharedResource: <T extends Resource, D>(create: Task<T, never, D>, { idleDisposeAfter, onDisposed }?: SharedResourceOptions) => Task<SharedResource<T>, never, D>;
|
|
141
279
|
/**
|
|
142
280
|
* Shared {@link Resource}s keyed by logical identity.
|
|
143
281
|
*
|
|
144
282
|
* A map-like registry of {@link SharedResource}s. Each key owns at most one
|
|
145
|
-
* current resource instance.
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
* resource until the final {@link SharedResourceByKey.release | release} starts
|
|
150
|
-
* the final disposal path for that key. Disposal and registry removal happen
|
|
151
|
-
* immediately by default, or after
|
|
152
|
-
* {@link SharedResourceByKeyOptions.idleDisposeAfter | idleDisposeAfter} elapses
|
|
153
|
-
* when configured.
|
|
283
|
+
* current resource instance. The first
|
|
284
|
+
* {@link SharedResourceByKey.acquire | acquire} for a key lazily creates that
|
|
285
|
+
* key's resource; releasing the key's last {@link Lease} disposes it and removes
|
|
286
|
+
* the key from the registry.
|
|
154
287
|
*
|
|
155
288
|
* Different keys are independent and may progress concurrently. Operations for
|
|
156
|
-
* the same key are serialized.
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
* {
|
|
166
|
-
*
|
|
289
|
+
* the same key are serialized.
|
|
290
|
+
*
|
|
291
|
+
* By default, keys use reference identity, matching native `Map`. Callers may
|
|
292
|
+
* instead provide a {@link Lookup lookup} so logical equality is based on a
|
|
293
|
+
* derived stable key.
|
|
294
|
+
*
|
|
295
|
+
* ### Example
|
|
296
|
+
*
|
|
297
|
+
* ```ts
|
|
298
|
+
* import {
|
|
299
|
+
* all,
|
|
300
|
+
* createRun,
|
|
301
|
+
* createSharedResourceByKey,
|
|
302
|
+
* ok,
|
|
303
|
+
* type Task,
|
|
304
|
+
* } from "@evolu/common";
|
|
305
|
+
*
|
|
306
|
+
* interface Connection extends Disposable {
|
|
307
|
+
* readonly ownerId: string;
|
|
308
|
+
* readonly send: (message: string) => void;
|
|
309
|
+
* readonly flush: () => ReadonlyArray<string>;
|
|
310
|
+
* }
|
|
311
|
+
*
|
|
312
|
+
* const createConnection =
|
|
313
|
+
* (ownerId: string): Task<Connection> =>
|
|
314
|
+
* () => {
|
|
315
|
+
* const pendingMessages: Array<string> = [];
|
|
316
|
+
* return ok({
|
|
317
|
+
* ownerId,
|
|
318
|
+
* send: (message) => {
|
|
319
|
+
* pendingMessages.push(message);
|
|
320
|
+
* },
|
|
321
|
+
* flush: () => pendingMessages.splice(0),
|
|
322
|
+
* [Symbol.dispose]: () => {},
|
|
323
|
+
* });
|
|
324
|
+
* };
|
|
325
|
+
*
|
|
326
|
+
* await using run = createRun();
|
|
327
|
+
* await using connections = await run.ok(
|
|
328
|
+
* createSharedResourceByKey(createConnection, {
|
|
329
|
+
* idleDisposeAfter: "30s",
|
|
330
|
+
* }),
|
|
331
|
+
* );
|
|
332
|
+
* const send = (ownerId: string, message: string): Task<void> =>
|
|
333
|
+
* connections.use(ownerId, (connection) => () => {
|
|
334
|
+
* connection.send(message);
|
|
335
|
+
* return ok();
|
|
336
|
+
* });
|
|
337
|
+
*
|
|
338
|
+
* // Same-key calls share one connection. Work for different keys remains
|
|
339
|
+
* // independent, so all three operations can run concurrently.
|
|
340
|
+
* await run.ok(
|
|
341
|
+
* all(
|
|
342
|
+
* [
|
|
343
|
+
* send("owner-1", "first"),
|
|
344
|
+
* send("owner-1", "second"),
|
|
345
|
+
* send("owner-2", "hello"),
|
|
346
|
+
* ],
|
|
347
|
+
* { concurrency: 3 },
|
|
348
|
+
* ),
|
|
349
|
+
* );
|
|
350
|
+
*
|
|
351
|
+
* // `acquireCurrent` does not create absent keys. The idle delay keeps the two
|
|
352
|
+
* // existing connections available to enumerate and flush.
|
|
353
|
+
* using missingLease = await run.ok(connections.acquireCurrent("owner-3"));
|
|
354
|
+
* const messagesByOwnerId = new Map<string, ReadonlyArray<string>>();
|
|
355
|
+
* await run.ok(
|
|
356
|
+
* connections.forEachCurrent((connection) => {
|
|
357
|
+
* messagesByOwnerId.set(connection.ownerId, connection.flush());
|
|
358
|
+
* }),
|
|
359
|
+
* );
|
|
360
|
+
*
|
|
361
|
+
* expect(missingLease).toBeUndefined();
|
|
362
|
+
* expect(messagesByOwnerId).toEqual(
|
|
363
|
+
* new Map([
|
|
364
|
+
* ["owner-1", ["first", "second"]],
|
|
365
|
+
* ["owner-2", ["hello"]],
|
|
366
|
+
* ]),
|
|
367
|
+
* );
|
|
368
|
+
* ```
|
|
167
369
|
*/
|
|
168
|
-
export interface SharedResourceByKey<K, T extends Resource
|
|
370
|
+
export interface SharedResourceByKey<K, T extends Resource> extends AsyncDisposable {
|
|
169
371
|
/**
|
|
170
|
-
* Acquires the shared resource for `key`, creating
|
|
372
|
+
* Acquires a {@link Lease} on the shared resource for `key`, creating the
|
|
373
|
+
* resource on first use.
|
|
171
374
|
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
375
|
+
* The same contract as {@link SharedResource.acquire}: once started, acquire
|
|
376
|
+
* runs to completion even when the caller aborts its Fiber, and the returned
|
|
377
|
+
* lease still counts as held. Registry disposal before lease transfer aborts
|
|
378
|
+
* the acquisition. Always await the result and release the lease.
|
|
176
379
|
*/
|
|
177
|
-
readonly acquire: (key: K) => Task<
|
|
380
|
+
readonly acquire: (key: K) => Task<Lease<T>>;
|
|
178
381
|
/**
|
|
179
|
-
*
|
|
382
|
+
* Acquires a {@link Lease} on the current resource for `key` without creating
|
|
383
|
+
* one.
|
|
180
384
|
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
385
|
+
* Waits for preceding creation or disposal for the same key to finish.
|
|
386
|
+
* Returns `undefined` if no current resource remains. Registry disposal while
|
|
387
|
+
* waiting aborts the acquisition before a lease is transferred. Registry
|
|
388
|
+
* disposal after transfer can drain the lease before the caller resumes,
|
|
389
|
+
* matching {@link SharedResource.acquireCurrent}.
|
|
390
|
+
*/
|
|
391
|
+
readonly acquireCurrent: (key: K) => Task<Lease<T> | undefined>;
|
|
392
|
+
/**
|
|
393
|
+
* Acquires a {@link Lease}, runs a Task with the shared resource for `key`,
|
|
394
|
+
* and releases the lease after the Task settles.
|
|
186
395
|
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
396
|
+
* Creates the resource when absent. The callback receives whether this use
|
|
397
|
+
* created the resource generation. While the registry remains running, the
|
|
398
|
+
* lease prevents ordinary idle disposal. Disposing this registry is forceful:
|
|
399
|
+
* it drains the lease and may dispose the resource before or while the
|
|
400
|
+
* callback Task runs.
|
|
189
401
|
*/
|
|
190
|
-
readonly
|
|
191
|
-
/**
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
402
|
+
readonly use: <R, E, D>(key: K, callback: (resource: BorrowedResource<T>, created: boolean) => Task<R, E, D>) => Task<R, E, D>;
|
|
403
|
+
/**
|
|
404
|
+
* Calls `callback` for each current resource it can lease from keys
|
|
405
|
+
* registered when this Task starts.
|
|
406
|
+
*
|
|
407
|
+
* Never creates resources. A resource whose creation is in progress may be
|
|
408
|
+
* included after creation finishes; one disposed before its temporary lease
|
|
409
|
+
* is acquired is skipped. Each acquired lease stays held while later keys are
|
|
410
|
+
* awaited, and every callback runs while all acquired resources remain
|
|
411
|
+
* leased. Caller abort is observed between keys; already-collected leases are
|
|
412
|
+
* released and no callbacks run.
|
|
413
|
+
*/
|
|
414
|
+
readonly forEachCurrent: (callback: (resource: BorrowedResource<T>, key: K) => void) => Task<void>;
|
|
415
|
+
/** Returns current per-key shared-resource states for monitoring/debugging. */
|
|
416
|
+
readonly snapshot: () => SharedResourceByKeySnapshot<K>;
|
|
197
417
|
}
|
|
198
418
|
/** Snapshot returned by {@link SharedResourceByKey.snapshot}. */
|
|
199
|
-
export interface SharedResourceByKeySnapshot<K
|
|
200
|
-
/**
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
419
|
+
export interface SharedResourceByKeySnapshot<K> {
|
|
420
|
+
/**
|
|
421
|
+
* Current registered {@link SharedResourceSnapshot} for each key.
|
|
422
|
+
*
|
|
423
|
+
* A key is absent during the brief first-acquire interval before its
|
|
424
|
+
* SharedResource is registered, even though that acquire may already hold the
|
|
425
|
+
* key mutex.
|
|
426
|
+
*/
|
|
427
|
+
readonly resourcesByKey: ReadonlyMap<K, SharedResourceSnapshot>;
|
|
204
428
|
}
|
|
205
429
|
/** Options for {@link createSharedResourceByKey}. */
|
|
206
430
|
export interface SharedResourceByKeyOptions<K, L = K> extends Pick<SharedResourceOptions, "idleDisposeAfter">, LookupOption<K, L> {
|
|
207
|
-
/**
|
|
208
|
-
|
|
431
|
+
/**
|
|
432
|
+
* Called with `key` after each current resource for that key is disposed,
|
|
433
|
+
* including during {@link SharedResourceByKey} disposal. Not called for keys
|
|
434
|
+
* whose resources were never created.
|
|
435
|
+
*
|
|
436
|
+
* After ordinary idle disposal, the key is removed before this callback
|
|
437
|
+
* unless a same-key operation is already in progress. A regular acquire can
|
|
438
|
+
* then create the next current resource. An acquireCurrent that finds no
|
|
439
|
+
* resource removes the retained key before returning.
|
|
440
|
+
*/
|
|
441
|
+
readonly onDisposed?: ((key: K) => void) | undefined;
|
|
209
442
|
}
|
|
210
443
|
/**
|
|
211
444
|
* Creates {@link SharedResourceByKey}.
|
|
212
445
|
*
|
|
213
|
-
* The `create` Task
|
|
214
|
-
* {@link createSharedResource}.
|
|
446
|
+
* The `create` callback returns a Task scoped to one key. The returned Task
|
|
447
|
+
* must not fail, matching {@link createSharedResource}.
|
|
448
|
+
*
|
|
449
|
+
* Create each resource with {@link disposable} when its shape supports it. The
|
|
450
|
+
* same disposal-guard recommendation as {@link createSharedResource} applies.
|
|
451
|
+
*
|
|
452
|
+
* The returned Task must not directly or transitively acquire the same logical
|
|
453
|
+
* key or call `forEachCurrent` on this registry. Same-key acquisition is
|
|
454
|
+
* non-reentrant, and `forEachCurrent` attempts to acquire every registered key.
|
|
455
|
+
* Different keys remain independent.
|
|
456
|
+
*
|
|
457
|
+
* Like {@link createSharedResource}, dependencies are captured when the registry
|
|
458
|
+
* is created; dependencies provided to later acquire calls do not change what
|
|
459
|
+
* the returned Task observes.
|
|
215
460
|
*/
|
|
216
|
-
export declare function createSharedResourceByKey<K = unknown, T extends Resource = Resource, D = unknown>(create: (key: K) => Task<T, never, D>): Task<SharedResourceByKey<K, T
|
|
217
|
-
export declare function createSharedResourceByKey<K, T extends Resource, D, L = K>(create: (key: K) => Task<T, never, D>, options: SharedResourceByKeyOptions<K, L>): Task<SharedResourceByKey<K, T
|
|
461
|
+
export declare function createSharedResourceByKey<K = unknown, T extends Resource = Resource, D = unknown>(create: (key: K) => Task<T, never, D>): Task<SharedResourceByKey<K, T>, never, D>;
|
|
462
|
+
export declare function createSharedResourceByKey<K, T extends Resource, D, L = K>(create: (key: K) => Task<T, never, D>, options: SharedResourceByKeyOptions<K, L>): Task<SharedResourceByKey<K, T>, never, D>;
|
|
218
463
|
/**
|
|
219
|
-
* Shared {@link Resource}s
|
|
464
|
+
* Shared keyed {@link Resource}s retained through claims.
|
|
220
465
|
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
466
|
+
* A claim identifies an application-level owner such as an account, tenant, or
|
|
467
|
+
* open document. Resource keys identify the shared resources that owner needs,
|
|
468
|
+
* such as relay URLs, database names, or worker IDs.
|
|
223
469
|
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
470
|
+
* Use this abstraction when each owner needs a set of keyed resources and those
|
|
471
|
+
* sets can overlap. Calling {@link SharedResourceByKeyWithClaims.claim |
|
|
472
|
+
* claim}
|
|
473
|
+
* retains every resource in one owner's set. Overlapping owners share one
|
|
474
|
+
* resource instance per key, and the resource remains alive until every
|
|
475
|
+
* {@link ClaimLease} retaining that key is released.
|
|
476
|
+
*
|
|
477
|
+
* Relation queries reflect active claims, not physical resource liveness. With
|
|
478
|
+
* `idleDisposeAfter`, a resource can outlive its last claim.
|
|
479
|
+
*
|
|
480
|
+
* Use {@link SharedResourceByKey} instead when callers only need independent
|
|
481
|
+
* leases by key and the application does not need to associate those leases
|
|
482
|
+
* with logical owners.
|
|
483
|
+
*
|
|
484
|
+
* Two accounts can share a relay connection while one also uses a local-network
|
|
485
|
+
* transport:
|
|
486
|
+
*
|
|
487
|
+
* ```ts
|
|
488
|
+
* import {
|
|
489
|
+
* createRun,
|
|
490
|
+
* createSharedResourceByKeyWithClaims,
|
|
491
|
+
* ok,
|
|
492
|
+
* type Brand,
|
|
493
|
+
* type Task,
|
|
494
|
+
* } from "@evolu/common";
|
|
495
|
+
*
|
|
496
|
+
* type AccountId = string & Brand<"AccountId">;
|
|
497
|
+
* type TransportUrl = string & Brand<"TransportUrl">;
|
|
498
|
+
*
|
|
499
|
+
* interface Connection extends Disposable {
|
|
500
|
+
* readonly url: TransportUrl;
|
|
501
|
+
* }
|
|
502
|
+
*
|
|
503
|
+
* const createConnection =
|
|
504
|
+
* (url: TransportUrl): Task<Connection> =>
|
|
505
|
+
* () =>
|
|
506
|
+
* ok({
|
|
507
|
+
* url,
|
|
508
|
+
* [Symbol.dispose]: () => {},
|
|
509
|
+
* });
|
|
510
|
+
* // These literals stand in for values validated at application boundaries.
|
|
511
|
+
* const accountA = "account-a" as AccountId;
|
|
512
|
+
* const accountB = "account-b" as AccountId;
|
|
513
|
+
* const relay = "wss://relay.example.com" as TransportUrl;
|
|
514
|
+
* const localNetwork = "ws://local-network" as TransportUrl;
|
|
515
|
+
*
|
|
516
|
+
* await using run = createRun();
|
|
517
|
+
* await using transports = await run.ok(
|
|
518
|
+
* createSharedResourceByKeyWithClaims<
|
|
519
|
+
* TransportUrl,
|
|
520
|
+
* AccountId,
|
|
521
|
+
* Connection
|
|
522
|
+
* >(createConnection),
|
|
523
|
+
* );
|
|
524
|
+
*
|
|
525
|
+
* {
|
|
526
|
+
* // Account A retains both transports.
|
|
527
|
+
* using accountATransports = await run.ok(
|
|
528
|
+
* transports.claim(accountA, [relay, localNetwork]),
|
|
529
|
+
* );
|
|
530
|
+
*
|
|
531
|
+
* {
|
|
532
|
+
* // Account B reuses the relay already retained by account A.
|
|
533
|
+
* using accountBRelay = await run.ok(
|
|
534
|
+
* transports.claim(accountB, [relay]),
|
|
535
|
+
* );
|
|
536
|
+
* expect(transports.getClaimsForResource(relay)).toEqual(
|
|
537
|
+
* new Set([accountA, accountB]),
|
|
538
|
+
* );
|
|
539
|
+
* expect(transports.getResourceKeysForClaim(accountA)).toEqual(
|
|
540
|
+
* new Set([relay, localNetwork]),
|
|
541
|
+
* );
|
|
542
|
+
* }
|
|
543
|
+
*
|
|
544
|
+
* // Releasing account B leaves account A's relay retain intact.
|
|
545
|
+
* expect(transports.getClaimsForResource(relay)).toEqual(
|
|
546
|
+
* new Set([accountA]),
|
|
547
|
+
* );
|
|
548
|
+
* }
|
|
549
|
+
*
|
|
550
|
+
* // Releasing the final claim removes the relation for both transports.
|
|
551
|
+
* expect(transports.getClaimsForResource(relay)).toEqual(new Set());
|
|
552
|
+
* expect(transports.getClaimsForResource(localNetwork)).toEqual(new Set());
|
|
553
|
+
* ```
|
|
228
554
|
*/
|
|
229
|
-
export interface SharedResourceByKeyWithClaims<K, C, T extends Resource
|
|
230
|
-
/**
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
555
|
+
export interface SharedResourceByKeyWithClaims<K, C, T extends Resource> extends AsyncDisposable {
|
|
556
|
+
/**
|
|
557
|
+
* Retains every resource key for `claim`, creating absent resources lazily.
|
|
558
|
+
*
|
|
559
|
+
* Keys are snapshotted when this Task starts and acquired sequentially in
|
|
560
|
+
* input order.
|
|
561
|
+
*
|
|
562
|
+
* `resourceKeys` must not contain logical duplicates according to
|
|
563
|
+
* {@link SharedResourceByKeyWithClaimsOptions.resourceLookup}. A duplicate is
|
|
564
|
+
* a programmer defect that panics the owning Run.
|
|
565
|
+
*
|
|
566
|
+
* The returned {@link ClaimLease} releases all retains added by this call.
|
|
567
|
+
* Resource creation must succeed, so this Task has no recoverable error.
|
|
568
|
+
*/
|
|
569
|
+
readonly claim: (claim: C, resourceKeys: NonEmptyReadonlyArray<K>) => Task<ClaimLease>;
|
|
570
|
+
/**
|
|
571
|
+
* Retains every resource key for `claim`, runs a Task with those resources,
|
|
572
|
+
* and releases the resulting {@link ClaimLease} after the Task settles.
|
|
573
|
+
*
|
|
574
|
+
* The callback receives only the resources retained by this call, in input
|
|
575
|
+
* order. Other active ClaimLeases for the same logical claim are excluded.
|
|
576
|
+
* While this registry remains running, every borrowed resource remains valid
|
|
577
|
+
* until the callback Task settles. Disposing this registry is forceful: it
|
|
578
|
+
* drains the ClaimLease and may dispose resources before or while the
|
|
579
|
+
* callback Task runs.
|
|
580
|
+
*/
|
|
581
|
+
readonly use: <R, E, D>(claim: C, resourceKeys: NonEmptyReadonlyArray<K>, callback: (resources: NonEmptyReadonlyArray<readonly [BorrowedResource<T>, K]>) => Task<R, E, D>) => Task<R, E, D>;
|
|
582
|
+
/**
|
|
583
|
+
* Returns the current unique claims retaining `resourceKey`.
|
|
584
|
+
*
|
|
585
|
+
* Returns an empty snapshot when no claim retains the key, including after
|
|
586
|
+
* this registry is disposed.
|
|
587
|
+
*/
|
|
588
|
+
readonly getClaimsForResource: (resourceKey: K) => ReadonlySet<C>;
|
|
589
|
+
/**
|
|
590
|
+
* Returns the current unique resource keys retained by `claim`.
|
|
591
|
+
*
|
|
592
|
+
* Returns an empty snapshot when the claim retains no keys, including after
|
|
593
|
+
* this registry is disposed.
|
|
594
|
+
*/
|
|
239
595
|
readonly getResourceKeysForClaim: (claim: C) => ReadonlySet<K>;
|
|
240
|
-
/**
|
|
241
|
-
|
|
596
|
+
/**
|
|
597
|
+
* Calls `callback` for each current resource retained by `claim`.
|
|
598
|
+
*
|
|
599
|
+
* The keys and resources are snapshotted before the first callback runs.
|
|
600
|
+
* Resources remain valid until this synchronous iteration returns, even if a
|
|
601
|
+
* callback releases the final retaining ClaimLease. Callbacks must not retain
|
|
602
|
+
* or asynchronously use borrowed resources afterward. Does nothing when the
|
|
603
|
+
* claim retains no resources, including after this registry is disposed.
|
|
604
|
+
*/
|
|
605
|
+
readonly forEachResourceForClaim: (claim: C, callback: (resource: BorrowedResource<T>, resourceKey: K) => void) => void;
|
|
606
|
+
/**
|
|
607
|
+
* Returns current claim-retain and keyed-resource states.
|
|
608
|
+
*
|
|
609
|
+
* While a claim is acquiring keys, `resourcesByKey` can include inner leases
|
|
610
|
+
* not yet reflected by `claimLeaseCount` or
|
|
611
|
+
* `retainCountsByResourceKeyByClaim`.
|
|
612
|
+
*/
|
|
613
|
+
readonly snapshot: () => SharedResourceByKeyWithClaimsSnapshot<K, C>;
|
|
614
|
+
}
|
|
615
|
+
/** Snapshot returned by {@link SharedResourceByKeyWithClaims.snapshot}. */
|
|
616
|
+
export interface SharedResourceByKeyWithClaimsSnapshot<K, C> {
|
|
617
|
+
/** Number of currently held ClaimLeases. */
|
|
618
|
+
readonly claimLeaseCount: NonNegativeInt;
|
|
619
|
+
/** Current retain count for every active logical claim-resource pair. */
|
|
620
|
+
readonly retainCountsByResourceKeyByClaim: ReadonlyMap<C, ReadonlyMap<K, number>>;
|
|
621
|
+
/** Current registered {@link SharedResourceSnapshot} for each key. */
|
|
622
|
+
readonly resourcesByKey: ReadonlyMap<K, SharedResourceSnapshot>;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* An owned lease on the resource-retains added by one claim operation.
|
|
626
|
+
*
|
|
627
|
+
* A ClaimLease is a grouped ownership token. For example, a claim for one
|
|
628
|
+
* account can retain its relay, local-network, and Bluetooth transports. The
|
|
629
|
+
* ClaimLease releases that whole group together; it does not expose or transfer
|
|
630
|
+
* ownership of the resources themselves.
|
|
631
|
+
*
|
|
632
|
+
* Releasing the lease removes every retain added by that claim call. Release is
|
|
633
|
+
* idempotent and synchronous; resource disposal remains owned by the
|
|
634
|
+
* {@link SharedResourceByKeyWithClaims} that created the lease.
|
|
635
|
+
*/
|
|
636
|
+
export interface ClaimLease extends Disposable {
|
|
637
|
+
/**
|
|
638
|
+
* Releases every resource retain owned by this claim lease.
|
|
639
|
+
*
|
|
640
|
+
* Returns whether this call released a still-held lease. Returns `false` if
|
|
641
|
+
* the lease was already released or drained by disposal of its owning
|
|
642
|
+
* {@link SharedResourceByKeyWithClaims} or the Run tree that owns it.
|
|
643
|
+
*/
|
|
644
|
+
readonly release: () => boolean;
|
|
242
645
|
}
|
|
243
646
|
/** Options for {@link createSharedResourceByKeyWithClaims}. */
|
|
244
647
|
export interface SharedResourceByKeyWithClaimsOptions<K, C, T extends Resource, LK = K, LC = C> extends Pick<SharedResourceOptions, "idleDisposeAfter"> {
|
|
245
|
-
/** Derives
|
|
648
|
+
/** Derives the identity used to compare resource keys. */
|
|
246
649
|
readonly resourceLookup?: Lookup<K, LK>;
|
|
247
|
-
/** Derives
|
|
650
|
+
/** Derives the identity used to compare claims. */
|
|
248
651
|
readonly claimLookup?: Lookup<C, LC>;
|
|
249
|
-
/**
|
|
652
|
+
/**
|
|
653
|
+
* Called when a claim-resource pair transitions from zero retains to one.
|
|
654
|
+
*
|
|
655
|
+
* This is a pair-retain transition, not a resource-generation transition: a
|
|
656
|
+
* resource can already be retained by other claims or be idling after an
|
|
657
|
+
* earlier release. Arguments use the first stored representatives for their
|
|
658
|
+
* logical claim and key.
|
|
659
|
+
*/
|
|
250
660
|
readonly onFirstClaimAdded?: (claim: C, resource: BorrowedResource<T>, resourceKey: K) => void;
|
|
251
|
-
/**
|
|
661
|
+
/**
|
|
662
|
+
* Called when a claim-resource pair transitions from one retain to zero.
|
|
663
|
+
*
|
|
664
|
+
* Runs before the ClaimLease's inner resource lease is released. Other claims
|
|
665
|
+
* can still retain the same resource. Arguments use the first stored
|
|
666
|
+
* representatives for their logical claim and key. During normal release, the
|
|
667
|
+
* current pair and earlier keys from the same ClaimLease are already absent
|
|
668
|
+
* from relation reads; later keys remain. During compensation for a failed
|
|
669
|
+
* first-claim callback, the failed claim remains fully visible.
|
|
670
|
+
*/
|
|
252
671
|
readonly onLastClaimRemoved?: (claim: C, resource: BorrowedResource<T>, resourceKey: K) => void;
|
|
253
672
|
}
|
|
254
673
|
/**
|
|
255
674
|
* Creates {@link SharedResourceByKeyWithClaims}.
|
|
256
675
|
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
676
|
+
* The `create` callback is called with a resource key when that resource must
|
|
677
|
+
* be created. The returned Task must not fail. Its dependencies are captured
|
|
678
|
+
* when the registry is created; later claim calls do not require or replace
|
|
679
|
+
* them.
|
|
680
|
+
*
|
|
681
|
+
* Transition callbacks represent semantic claim-resource pair changes and are
|
|
682
|
+
* not called when this registry or its Run tree drains claims during disposal.
|
|
683
|
+
* They must not throw. If a first-claim callback defects after earlier
|
|
684
|
+
* callbacks in the same claim completed, matching last-claim callbacks
|
|
685
|
+
* compensate those completed transitions in reverse order before cleanup.
|
|
686
|
+
* Relation reads during compensation still include the failed claim. Callback
|
|
687
|
+
* defects panic the owner Run after claim metadata and resource ownership are
|
|
688
|
+
* cleaned up.
|
|
689
|
+
*
|
|
690
|
+
* Transition callbacks may synchronously read relation state. They must not
|
|
691
|
+
* call `claim`, release a {@link ClaimLease} owned by this registry, or dispose
|
|
692
|
+
* this registry.
|
|
693
|
+
*
|
|
694
|
+
* Transition callbacks do not report actual resource disposal. Disposal can
|
|
695
|
+
* happen later when `idleDisposeAfter` is configured.
|
|
260
696
|
*/
|
|
261
|
-
export declare
|
|
262
|
-
export declare function createSharedResourceByKeyWithClaims<T extends Resource, K, C, D, LK = K, LC = C>(create: (key: K) => Task<T, never, D>, options: SharedResourceByKeyWithClaimsOptions<K, C, T, LK, LC>): Task<SharedResourceByKeyWithClaims<K, C, T, D>, never, D>;
|
|
697
|
+
export declare const createSharedResourceByKeyWithClaims: <K = unknown, C = unknown, T extends Resource = Resource, D = unknown, LK = K, LC = C>(create: (key: K) => Task<T, never, D>, { idleDisposeAfter, resourceLookup, claimLookup, onFirstClaimAdded, onLastClaimRemoved, }?: SharedResourceByKeyWithClaimsOptions<K, C, T, LK, LC>) => Task<SharedResourceByKeyWithClaims<K, C, T>, never, D>;
|
|
263
698
|
//# sourceMappingURL=Resource.d.ts.map
|