@evolu/common 7.4.1 → 8.0.0-next.1
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/README.md +4 -61
- package/dist/src/Array.d.ts +343 -102
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +181 -85
- package/dist/src/Assert.d.ts +57 -11
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +47 -11
- package/dist/src/BigInt.d.ts +6 -1
- package/dist/src/BigInt.d.ts.map +1 -1
- package/dist/src/BigInt.js +5 -0
- package/dist/src/Brand.d.ts +40 -12
- package/dist/src/Brand.d.ts.map +1 -1
- package/dist/src/Brand.js +5 -0
- package/dist/src/Buffer.d.ts +6 -1
- package/dist/src/Buffer.d.ts.map +1 -1
- package/dist/src/Buffer.js +7 -2
- package/dist/src/Cache.d.ts +10 -2
- package/dist/src/Cache.d.ts.map +1 -1
- package/dist/src/Cache.js +6 -0
- package/dist/src/Callbacks.d.ts +10 -7
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Callbacks.js +23 -13
- package/dist/src/Console.d.ts +361 -69
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +217 -117
- package/dist/src/Crypto.d.ts +70 -21
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +72 -27
- package/dist/src/Eq.d.ts +22 -2
- package/dist/src/Eq.d.ts.map +1 -1
- package/dist/src/Eq.js +21 -1
- package/dist/src/Error.d.ts +23 -12
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +27 -11
- package/dist/src/Function.d.ts +128 -56
- package/dist/src/Function.d.ts.map +1 -1
- package/dist/src/Function.js +118 -14
- package/dist/src/Identicon.d.ts +5 -0
- package/dist/src/Identicon.d.ts.map +1 -1
- package/dist/src/Identicon.js +6 -1
- package/dist/src/Lookup.d.ts +162 -0
- package/dist/src/Lookup.d.ts.map +1 -0
- package/dist/src/Lookup.js +200 -0
- package/dist/src/Microtask.d.ts +21 -0
- package/dist/src/Microtask.d.ts.map +1 -0
- package/dist/src/Microtask.js +37 -0
- package/dist/src/Number.d.ts +18 -4
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +23 -1
- package/dist/src/Object.d.ts +110 -5
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +108 -5
- package/dist/src/Option.d.ts +58 -0
- package/dist/src/Option.d.ts.map +1 -0
- package/dist/src/Option.js +43 -0
- package/dist/src/Order.d.ts +5 -0
- package/dist/src/Order.d.ts.map +1 -1
- package/dist/src/Order.js +8 -3
- package/dist/src/Platform.d.ts +36 -2
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +10 -6
- package/dist/src/Polyfills.d.ts +29 -0
- package/dist/src/Polyfills.d.ts.map +1 -0
- package/dist/src/Polyfills.js +329 -0
- package/dist/src/Random.d.ts +18 -7
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +9 -7
- package/dist/src/Redacted.d.ts +7 -4
- package/dist/src/Redacted.d.ts.map +1 -1
- package/dist/src/Redacted.js +5 -0
- package/dist/src/Ref.d.ts +31 -16
- package/dist/src/Ref.d.ts.map +1 -1
- package/dist/src/Ref.js +35 -7
- package/dist/src/RefCount.d.ts +62 -0
- package/dist/src/RefCount.d.ts.map +1 -0
- package/dist/src/RefCount.js +83 -0
- package/dist/src/Relation.d.ts +84 -29
- package/dist/src/Relation.d.ts.map +1 -1
- package/dist/src/Relation.js +83 -105
- package/dist/src/Resource.d.ts +263 -0
- package/dist/src/Resource.d.ts.map +1 -0
- package/dist/src/Resource.js +389 -0
- package/dist/src/Result.d.ts +390 -374
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +142 -70
- package/dist/src/Schedule.d.ts +953 -0
- package/dist/src/Schedule.d.ts.map +1 -0
- package/dist/src/Schedule.js +1199 -0
- package/dist/src/Set.d.ts +181 -0
- package/dist/src/Set.d.ts.map +1 -0
- package/dist/src/Set.js +137 -0
- package/dist/src/Skiplist.d.ts +6 -1
- package/dist/src/Skiplist.d.ts.map +1 -1
- package/dist/src/Skiplist.js +5 -0
- package/dist/src/Sqlite.d.ts +160 -60
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +290 -190
- package/dist/src/Store.d.ts +36 -28
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +55 -15
- package/dist/src/String.d.ts +5 -0
- package/dist/src/String.d.ts.map +1 -1
- package/dist/src/String.js +5 -0
- package/dist/src/Task.d.ts +2274 -423
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +1499 -348
- package/dist/src/Test.d.ts +150 -0
- package/dist/src/Test.d.ts.map +1 -0
- package/dist/src/Test.js +97 -0
- package/dist/src/Time.d.ts +202 -48
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +199 -88
- package/dist/src/Tracer.d.ts +48 -0
- package/dist/src/Tracer.d.ts.map +1 -0
- package/dist/src/Tracer.js +6 -0
- package/dist/src/Type.d.ts +587 -209
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +370 -240
- package/dist/src/Types.d.ts +160 -6
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/Types.js +22 -2
- package/dist/src/WebSocket.d.ts +130 -58
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +274 -113
- package/dist/src/Worker.d.ts +245 -60
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +220 -64
- package/dist/src/index.d.ts +25 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +19 -3
- package/dist/src/local-first/Db.d.ts +24 -213
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +621 -293
- package/dist/src/local-first/Error.d.ts +12 -0
- package/dist/src/local-first/Error.d.ts.map +1 -0
- package/dist/src/local-first/Error.js +6 -0
- package/dist/src/local-first/Evolu.d.ts +330 -285
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +472 -402
- package/dist/src/local-first/LocalAuth.d.ts +10 -10
- package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
- package/dist/src/local-first/LocalAuth.js +6 -1
- package/dist/src/local-first/Owner.d.ts +70 -27
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +27 -14
- package/dist/src/local-first/Protocol.d.ts +58 -50
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +253 -155
- package/dist/src/local-first/Query.d.ts +133 -43
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Query.js +139 -90
- package/dist/src/local-first/Relay.d.ts +34 -49
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Relay.js +59 -177
- package/dist/src/local-first/Schema.d.ts +139 -144
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +78 -163
- package/dist/src/local-first/Shared.d.ts +125 -0
- package/dist/src/local-first/Shared.d.ts.map +1 -0
- package/dist/src/local-first/Shared.js +569 -0
- package/dist/src/local-first/Storage.d.ts +54 -58
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +127 -179
- package/dist/src/local-first/Timestamp.d.ts +28 -35
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +24 -25
- package/dist/src/local-first/index.d.ts +2 -15
- package/dist/src/local-first/index.d.ts.map +1 -1
- package/dist/src/local-first/index.js +2 -15
- package/package.json +37 -24
- package/src/Array.ts +467 -106
- package/src/Assert.ts +87 -12
- package/src/BigInt.ts +7 -1
- package/src/Brand.ts +41 -12
- package/src/Buffer.ts +10 -4
- package/src/Cache.ts +11 -2
- package/src/Callbacks.ts +33 -23
- package/src/Console.ts +567 -178
- package/src/Crypto.ts +111 -62
- package/src/Eq.ts +30 -3
- package/src/Error.ts +50 -34
- package/src/Function.ts +140 -68
- package/src/Identicon.ts +7 -1
- package/src/Lookup.ts +429 -0
- package/src/Microtask.ts +58 -0
- package/src/Number.ts +40 -7
- package/src/Object.ts +155 -16
- package/src/Option.ts +74 -0
- package/src/Order.ts +11 -3
- package/src/Platform.ts +43 -7
- package/src/Polyfills.ts +520 -0
- package/src/Random.ts +25 -11
- package/src/Redacted.ts +8 -4
- package/src/Ref.ts +72 -21
- package/src/RefCount.ts +170 -0
- package/src/Relation.ts +188 -136
- package/src/Resource.ts +864 -0
- package/src/Result.ts +548 -381
- package/src/Schedule.ts +1471 -0
- package/src/Set.ts +247 -0
- package/src/Skiplist.ts +7 -1
- package/src/Sqlite.ts +409 -304
- package/src/Store.ts +94 -48
- package/src/String.ts +6 -0
- package/src/Task.ts +3696 -740
- package/src/Test.ts +202 -0
- package/src/Time.ts +357 -147
- package/src/Tracer.ts +54 -0
- package/src/Type.ts +866 -430
- package/src/Types.ts +202 -7
- package/src/WebSocket.ts +404 -167
- package/src/Worker.ts +527 -147
- package/src/index.ts +74 -3
- package/src/local-first/Db.ts +867 -597
- package/src/local-first/Error.ts +17 -0
- package/src/local-first/Evolu.ts +884 -824
- package/src/local-first/LocalAuth.ts +10 -10
- package/src/local-first/Owner.ts +94 -42
- package/src/local-first/Protocol.ts +441 -307
- package/src/local-first/Query.ts +243 -194
- package/src/local-first/Relay.ts +119 -273
- package/src/local-first/Schema.ts +284 -394
- package/src/local-first/Shared.ts +932 -0
- package/src/local-first/Storage.ts +211 -283
- package/src/local-first/Timestamp.ts +38 -45
- package/src/local-first/index.ts +2 -16
- package/dist/src/Instances.d.ts +0 -34
- package/dist/src/Instances.d.ts.map +0 -1
- package/dist/src/Instances.js +0 -44
- package/dist/src/Resources.d.ts +0 -118
- package/dist/src/Resources.d.ts.map +0 -1
- package/dist/src/Resources.js +0 -197
- package/dist/src/local-first/Platform.d.ts +0 -25
- package/dist/src/local-first/Platform.d.ts.map +0 -1
- package/dist/src/local-first/Platform.js +0 -1
- package/dist/src/local-first/Public.d.ts +0 -22
- package/dist/src/local-first/Public.d.ts.map +0 -1
- package/dist/src/local-first/Public.js +0 -15
- package/dist/src/local-first/PublicKysely.d.ts +0 -148
- package/dist/src/local-first/PublicKysely.d.ts.map +0 -1
- package/dist/src/local-first/PublicKysely.js +0 -184
- package/dist/src/local-first/Sync.d.ts +0 -112
- package/dist/src/local-first/Sync.d.ts.map +0 -1
- package/dist/src/local-first/Sync.js +0 -526
- package/src/Instances.ts +0 -92
- package/src/Resources.ts +0 -367
- package/src/local-first/Platform.ts +0 -27
- package/src/local-first/Public.ts +0 -42
- package/src/local-first/PublicKysely.ts +0 -239
- package/src/local-first/Sync.ts +0 -949
package/src/Polyfills.ts
ADDED
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polyfills.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Installs polyfills required by `@evolu/common`.
|
|
9
|
+
*
|
|
10
|
+
* Installs:
|
|
11
|
+
*
|
|
12
|
+
* - Resource-management polyfills (`Symbol.dispose`, `Symbol.asyncDispose`,
|
|
13
|
+
* `DisposableStack`, `AsyncDisposableStack`, and `SuppressedError`) for
|
|
14
|
+
* Safari and React Native
|
|
15
|
+
* - `Map` and `WeakMap` upsert polyfills (`Map.prototype.getOrInsert`,
|
|
16
|
+
* `Map.prototype.getOrInsertComputed`, `WeakMap.prototype.getOrInsert`, and
|
|
17
|
+
* `WeakMap.prototype.getOrInsertComputed`) for Node 24 and React Native
|
|
18
|
+
*
|
|
19
|
+
* `@evolu/react-native` has its own `Polyfills` module and its
|
|
20
|
+
* `installPolyfills` calls this function first, then installs React Native
|
|
21
|
+
* specific polyfills.
|
|
22
|
+
*
|
|
23
|
+
* Call this explicitly from the app entry point.
|
|
24
|
+
*
|
|
25
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Resource_management
|
|
26
|
+
* @see https://github.com/es-shims/DisposableStack
|
|
27
|
+
* @see https://github.com/es-shims/DisposableStack/issues/9
|
|
28
|
+
*/
|
|
29
|
+
export const installPolyfills = (): void => {
|
|
30
|
+
/**
|
|
31
|
+
* This module intentionally owns `DisposableStack` and `AsyncDisposableStack`
|
|
32
|
+
* polyfills instead of depending on `es-shims/DisposableStack` at runtime.
|
|
33
|
+
*
|
|
34
|
+
* Evolu originally used the upstream package, but WebKit hit a known async
|
|
35
|
+
* disposal completion bug (`completion["?"]` crash, see issue #9). The local
|
|
36
|
+
* implementation applies the fix and keeps behavior deterministic across
|
|
37
|
+
* runtimes used by Evolu.
|
|
38
|
+
*
|
|
39
|
+
* Conformance is validated by tests that combine upstream-style and test262
|
|
40
|
+
* parity cases with Evolu-specific regressions. Those tests run against
|
|
41
|
+
* native Node.js behavior and against this polyfill path in browser projects,
|
|
42
|
+
* including WebKit.
|
|
43
|
+
*/
|
|
44
|
+
installDisposableStack();
|
|
45
|
+
installMapAndWeakMapUpsertPolyfills();
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
interface DisposableResource {
|
|
49
|
+
readonly dispose: () => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface AsyncDisposableResource {
|
|
53
|
+
readonly dispose: () => Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Installs `DisposableStack`-related polyfills missing from the runtime. */
|
|
57
|
+
const installDisposableStack = (): void => {
|
|
58
|
+
installSuppressedError();
|
|
59
|
+
|
|
60
|
+
const symbolDispose = getOrInstallSymbol("dispose", "Symbol.dispose");
|
|
61
|
+
const symbolAsyncDispose = getOrInstallSymbol(
|
|
62
|
+
"asyncDispose",
|
|
63
|
+
"Symbol.asyncDispose",
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
if (typeof globalThis.DisposableStack !== "function") {
|
|
67
|
+
defineGlobalValue(
|
|
68
|
+
globalThis,
|
|
69
|
+
"DisposableStack",
|
|
70
|
+
createDisposableStackPolyfill(symbolDispose),
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (typeof globalThis.AsyncDisposableStack !== "function") {
|
|
75
|
+
defineGlobalValue(
|
|
76
|
+
globalThis,
|
|
77
|
+
"AsyncDisposableStack",
|
|
78
|
+
createAsyncDisposableStackPolyfill(symbolDispose, symbolAsyncDispose),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
type SymbolWithDisposable = SymbolConstructor & {
|
|
84
|
+
dispose?: symbol;
|
|
85
|
+
asyncDispose?: symbol;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const suppressedErrorMessage = "An error was suppressed during disposal.";
|
|
89
|
+
|
|
90
|
+
const disposedMessage = (className: string, method: string): string =>
|
|
91
|
+
`Cannot call ${className}.prototype.${method} on an already-disposed DisposableStack`;
|
|
92
|
+
|
|
93
|
+
const defineGlobalValue = (
|
|
94
|
+
target: object,
|
|
95
|
+
key: PropertyKey,
|
|
96
|
+
value: unknown,
|
|
97
|
+
): void => {
|
|
98
|
+
Object.defineProperty(target, key, {
|
|
99
|
+
configurable: true,
|
|
100
|
+
enumerable: false,
|
|
101
|
+
writable: true,
|
|
102
|
+
value,
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const defineMethodAlias = (
|
|
107
|
+
target: object,
|
|
108
|
+
alias: symbol,
|
|
109
|
+
methodName: string,
|
|
110
|
+
): void => {
|
|
111
|
+
defineGlobalValue(
|
|
112
|
+
target,
|
|
113
|
+
alias,
|
|
114
|
+
Object.getOwnPropertyDescriptor(target, methodName)?.value,
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const throwIfDisposed = (disposed: boolean, message: string): void => {
|
|
119
|
+
if (disposed) {
|
|
120
|
+
throw new ReferenceError(message);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const appendDisposeError = (
|
|
125
|
+
currentError: unknown,
|
|
126
|
+
previousError: unknown,
|
|
127
|
+
): unknown =>
|
|
128
|
+
new globalThis.SuppressedError(
|
|
129
|
+
currentError,
|
|
130
|
+
previousError,
|
|
131
|
+
suppressedErrorMessage,
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const assertObjectOrFunction = (value: unknown): object => {
|
|
135
|
+
if (
|
|
136
|
+
(typeof value !== "object" || value === null) &&
|
|
137
|
+
typeof value !== "function"
|
|
138
|
+
) {
|
|
139
|
+
throw new TypeError("Disposable value must be an object or function.");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return value;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const getMethod = (
|
|
146
|
+
value: object,
|
|
147
|
+
key: symbol,
|
|
148
|
+
): ((this: unknown) => unknown) | undefined => {
|
|
149
|
+
const method = (value as Record<symbol, unknown>)[key];
|
|
150
|
+
if (method === undefined) return undefined;
|
|
151
|
+
if (typeof method !== "function") {
|
|
152
|
+
throw new TypeError("Disposable method must be a function.");
|
|
153
|
+
}
|
|
154
|
+
return method as (this: unknown) => unknown;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const getOrInstallSymbol = (
|
|
158
|
+
key: "dispose" | "asyncDispose",
|
|
159
|
+
description: string,
|
|
160
|
+
): symbol => {
|
|
161
|
+
const SymbolCtor = globalThis.Symbol as SymbolWithDisposable;
|
|
162
|
+
const installedValue = Object.getOwnPropertyDescriptor(SymbolCtor, key)
|
|
163
|
+
?.value as unknown;
|
|
164
|
+
const installed =
|
|
165
|
+
typeof installedValue === "symbol" ? installedValue : undefined;
|
|
166
|
+
if (installed != null) return installed;
|
|
167
|
+
|
|
168
|
+
const symbol = Symbol(description);
|
|
169
|
+
Object.defineProperty(SymbolCtor, key, {
|
|
170
|
+
configurable: false,
|
|
171
|
+
enumerable: false,
|
|
172
|
+
writable: false,
|
|
173
|
+
value: symbol,
|
|
174
|
+
});
|
|
175
|
+
return symbol;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const installSuppressedError = (): void => {
|
|
179
|
+
if (typeof globalThis.SuppressedError === "function") return;
|
|
180
|
+
|
|
181
|
+
class SuppressedErrorPolyfill
|
|
182
|
+
extends Error
|
|
183
|
+
implements globalThis.SuppressedError
|
|
184
|
+
{
|
|
185
|
+
readonly error: unknown;
|
|
186
|
+
readonly suppressed: unknown;
|
|
187
|
+
|
|
188
|
+
constructor(error: unknown, suppressed: unknown, message?: string) {
|
|
189
|
+
super(message ?? suppressedErrorMessage);
|
|
190
|
+
this.name = "SuppressedError";
|
|
191
|
+
this.error = error;
|
|
192
|
+
this.suppressed = suppressed;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
defineGlobalValue(globalThis, "SuppressedError", SuppressedErrorPolyfill);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const createDisposableStackPolyfill = (
|
|
200
|
+
symbolDispose: symbol,
|
|
201
|
+
): new () => DisposableStack => {
|
|
202
|
+
class DisposableStackPolyfill implements DisposableStack {
|
|
203
|
+
#disposed = false;
|
|
204
|
+
#resources: Array<DisposableResource> = [];
|
|
205
|
+
|
|
206
|
+
declare readonly [Symbol.toStringTag]: "DisposableStack";
|
|
207
|
+
declare [Symbol.dispose]: () => void;
|
|
208
|
+
|
|
209
|
+
get disposed(): boolean {
|
|
210
|
+
return this.#disposed;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
use<T extends object | null | undefined>(value: T): T {
|
|
214
|
+
throwIfDisposed(
|
|
215
|
+
this.#disposed,
|
|
216
|
+
disposedMessage("DisposableStack", "use"),
|
|
217
|
+
);
|
|
218
|
+
if (value == null) return value;
|
|
219
|
+
|
|
220
|
+
const target = assertObjectOrFunction(value);
|
|
221
|
+
const method = getMethod(target, symbolDispose);
|
|
222
|
+
if (method == null) {
|
|
223
|
+
throw new TypeError("Resource does not implement Symbol.dispose.");
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
this.#resources.push({
|
|
227
|
+
dispose: () => {
|
|
228
|
+
method.call(target);
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return value;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
adopt<T>(value: T, onDispose: (value: T) => void): T {
|
|
236
|
+
throwIfDisposed(
|
|
237
|
+
this.#disposed,
|
|
238
|
+
disposedMessage("DisposableStack", "adopt"),
|
|
239
|
+
);
|
|
240
|
+
if (typeof onDispose !== "function") {
|
|
241
|
+
throw new TypeError("onDispose must be a function.");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
this.#resources.push({
|
|
245
|
+
dispose: () => {
|
|
246
|
+
onDispose(value);
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
return value;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
defer(onDispose: () => void): void {
|
|
254
|
+
throwIfDisposed(
|
|
255
|
+
this.#disposed,
|
|
256
|
+
disposedMessage("DisposableStack", "defer"),
|
|
257
|
+
);
|
|
258
|
+
if (typeof onDispose !== "function") {
|
|
259
|
+
throw new TypeError("onDispose must be a function.");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
this.#resources.push({
|
|
263
|
+
dispose: () => {
|
|
264
|
+
onDispose();
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
move(): DisposableStack {
|
|
270
|
+
throwIfDisposed(
|
|
271
|
+
this.#disposed,
|
|
272
|
+
disposedMessage("DisposableStack", "move"),
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
const moved = new DisposableStackPolyfill();
|
|
276
|
+
moved.#resources = this.#resources;
|
|
277
|
+
|
|
278
|
+
this.#resources = [];
|
|
279
|
+
this.#disposed = true;
|
|
280
|
+
|
|
281
|
+
return moved;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
dispose(): void {
|
|
285
|
+
if (this.#disposed) return;
|
|
286
|
+
|
|
287
|
+
this.#disposed = true;
|
|
288
|
+
const resources = this.#resources;
|
|
289
|
+
this.#resources = [];
|
|
290
|
+
|
|
291
|
+
let completionError: unknown = undefined;
|
|
292
|
+
let hasCompletionError = false;
|
|
293
|
+
|
|
294
|
+
for (let i = resources.length - 1; i >= 0; i--) {
|
|
295
|
+
try {
|
|
296
|
+
resources[i].dispose();
|
|
297
|
+
} catch (error) {
|
|
298
|
+
completionError = hasCompletionError
|
|
299
|
+
? appendDisposeError(error, completionError)
|
|
300
|
+
: error;
|
|
301
|
+
hasCompletionError = true;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (hasCompletionError) {
|
|
306
|
+
throw completionError;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
defineMethodAlias(
|
|
312
|
+
DisposableStackPolyfill.prototype,
|
|
313
|
+
symbolDispose,
|
|
314
|
+
"dispose",
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
Object.defineProperty(DisposableStackPolyfill.prototype, Symbol.toStringTag, {
|
|
318
|
+
configurable: true,
|
|
319
|
+
enumerable: false,
|
|
320
|
+
writable: false,
|
|
321
|
+
value: "DisposableStack",
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
return DisposableStackPolyfill;
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const createAsyncDisposableStackPolyfill = (
|
|
328
|
+
symbolDispose: symbol,
|
|
329
|
+
symbolAsyncDispose: symbol,
|
|
330
|
+
): new () => AsyncDisposableStack => {
|
|
331
|
+
class AsyncDisposableStackPolyfill implements AsyncDisposableStack {
|
|
332
|
+
#disposed = false;
|
|
333
|
+
#resources: Array<AsyncDisposableResource> = [];
|
|
334
|
+
|
|
335
|
+
declare readonly [Symbol.toStringTag]: "AsyncDisposableStack";
|
|
336
|
+
declare [Symbol.asyncDispose]: () => Promise<void>;
|
|
337
|
+
|
|
338
|
+
get disposed(): boolean {
|
|
339
|
+
return this.#disposed;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
use<T extends object | null | undefined>(value: T): T {
|
|
343
|
+
throwIfDisposed(
|
|
344
|
+
this.#disposed,
|
|
345
|
+
disposedMessage("AsyncDisposableStack", "use"),
|
|
346
|
+
);
|
|
347
|
+
if (value == null) return value;
|
|
348
|
+
|
|
349
|
+
const target = assertObjectOrFunction(value);
|
|
350
|
+
const asyncMethod = getMethod(target, symbolAsyncDispose);
|
|
351
|
+
|
|
352
|
+
if (asyncMethod != null) {
|
|
353
|
+
this.#resources.push({
|
|
354
|
+
dispose: async () => {
|
|
355
|
+
await asyncMethod.call(target);
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
return value;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const syncMethod = getMethod(target, symbolDispose);
|
|
362
|
+
|
|
363
|
+
if (syncMethod != null) {
|
|
364
|
+
this.#resources.push({
|
|
365
|
+
dispose: () =>
|
|
366
|
+
Promise.resolve().then(() => {
|
|
367
|
+
syncMethod.call(target);
|
|
368
|
+
}),
|
|
369
|
+
});
|
|
370
|
+
return value;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
throw new TypeError(
|
|
374
|
+
"Resource does not implement Symbol.asyncDispose or Symbol.dispose.",
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
adopt<T>(value: T, onDisposeAsync: (value: T) => void | Promise<void>): T {
|
|
379
|
+
throwIfDisposed(
|
|
380
|
+
this.#disposed,
|
|
381
|
+
disposedMessage("AsyncDisposableStack", "adopt"),
|
|
382
|
+
);
|
|
383
|
+
if (typeof onDisposeAsync !== "function") {
|
|
384
|
+
throw new TypeError("onDisposeAsync must be a function.");
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
this.#resources.push({
|
|
388
|
+
dispose: () => Promise.resolve().then(() => onDisposeAsync(value)),
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
return value;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
defer(onDisposeAsync: () => void | Promise<void>): void {
|
|
395
|
+
throwIfDisposed(
|
|
396
|
+
this.#disposed,
|
|
397
|
+
disposedMessage("AsyncDisposableStack", "defer"),
|
|
398
|
+
);
|
|
399
|
+
if (typeof onDisposeAsync !== "function") {
|
|
400
|
+
throw new TypeError("onDisposeAsync must be a function.");
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
this.#resources.push({
|
|
404
|
+
dispose: () => Promise.resolve().then(() => onDisposeAsync()),
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
move(): AsyncDisposableStack {
|
|
409
|
+
throwIfDisposed(
|
|
410
|
+
this.#disposed,
|
|
411
|
+
disposedMessage("AsyncDisposableStack", "move"),
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
const moved = new AsyncDisposableStackPolyfill();
|
|
415
|
+
moved.#resources = this.#resources;
|
|
416
|
+
|
|
417
|
+
this.#resources = [];
|
|
418
|
+
this.#disposed = true;
|
|
419
|
+
|
|
420
|
+
return moved;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
async disposeAsync(): Promise<void> {
|
|
424
|
+
if (this.#disposed) return;
|
|
425
|
+
|
|
426
|
+
this.#disposed = true;
|
|
427
|
+
const resources = this.#resources;
|
|
428
|
+
this.#resources = [];
|
|
429
|
+
|
|
430
|
+
let completionError: unknown = undefined;
|
|
431
|
+
let hasCompletionError = false;
|
|
432
|
+
|
|
433
|
+
for (let i = resources.length - 1; i >= 0; i--) {
|
|
434
|
+
try {
|
|
435
|
+
await resources[i].dispose();
|
|
436
|
+
} catch (error) {
|
|
437
|
+
completionError = hasCompletionError
|
|
438
|
+
? appendDisposeError(error, completionError)
|
|
439
|
+
: error;
|
|
440
|
+
hasCompletionError = true;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (hasCompletionError) {
|
|
445
|
+
throw completionError;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
defineMethodAlias(
|
|
451
|
+
AsyncDisposableStackPolyfill.prototype,
|
|
452
|
+
symbolAsyncDispose,
|
|
453
|
+
"disposeAsync",
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
Object.defineProperty(
|
|
457
|
+
AsyncDisposableStackPolyfill.prototype,
|
|
458
|
+
Symbol.toStringTag,
|
|
459
|
+
{
|
|
460
|
+
configurable: true,
|
|
461
|
+
enumerable: false,
|
|
462
|
+
writable: false,
|
|
463
|
+
value: "AsyncDisposableStack",
|
|
464
|
+
},
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
return AsyncDisposableStackPolyfill;
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
/** Installs `Map` and `WeakMap` upsert polyfills missing from the runtime. */
|
|
471
|
+
const installMapAndWeakMapUpsertPolyfills = (): void => {
|
|
472
|
+
const getOrInsert: typeof Map.prototype.getOrInsert = function (
|
|
473
|
+
this: Map<unknown, unknown> | WeakMap<object, unknown>,
|
|
474
|
+
key: unknown,
|
|
475
|
+
defaultValue: unknown,
|
|
476
|
+
): unknown {
|
|
477
|
+
if (!this.has(key as never)) {
|
|
478
|
+
this.set(key as never, defaultValue);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return this.get(key as never);
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
const getOrInsertComputed: typeof Map.prototype.getOrInsertComputed =
|
|
485
|
+
function (
|
|
486
|
+
this: Map<unknown, unknown> | WeakMap<object, unknown>,
|
|
487
|
+
key: unknown,
|
|
488
|
+
callbackFunction: (key: unknown) => unknown,
|
|
489
|
+
): unknown {
|
|
490
|
+
if (!this.has(key as never)) {
|
|
491
|
+
this.set(key as never, callbackFunction(key));
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return this.get(key as never);
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
if (typeof Map.prototype.getOrInsert !== "function") {
|
|
498
|
+
defineGlobalValue(Map.prototype, "getOrInsert", getOrInsert);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (typeof Map.prototype.getOrInsertComputed !== "function") {
|
|
502
|
+
defineGlobalValue(
|
|
503
|
+
Map.prototype,
|
|
504
|
+
"getOrInsertComputed",
|
|
505
|
+
getOrInsertComputed,
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (typeof WeakMap.prototype.getOrInsert !== "function") {
|
|
510
|
+
defineGlobalValue(WeakMap.prototype, "getOrInsert", getOrInsert);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (typeof WeakMap.prototype.getOrInsertComputed !== "function") {
|
|
514
|
+
defineGlobalValue(
|
|
515
|
+
WeakMap.prototype,
|
|
516
|
+
"getOrInsertComputed",
|
|
517
|
+
getOrInsertComputed,
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
};
|
package/src/Random.ts
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeded random number generation.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import { Random as RandomLib } from "random";
|
|
8
|
+
import type { Brand } from "./Brand.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A random floating point number in [0, 1).
|
|
12
|
+
*
|
|
13
|
+
* Branded to distinguish random values from arbitrary numbers.
|
|
14
|
+
*/
|
|
15
|
+
export type RandomNumber = number & Brand<"RandomNumber">;
|
|
2
16
|
|
|
3
17
|
/**
|
|
4
18
|
* A simple wrapper around Math.random().
|
|
@@ -19,7 +33,7 @@ import { Random as RandomLib } from "random";
|
|
|
19
33
|
*/
|
|
20
34
|
export interface Random {
|
|
21
35
|
/** Returns a floating point number in [0, 1). Just like Math.random(). */
|
|
22
|
-
next: () =>
|
|
36
|
+
next: () => RandomNumber;
|
|
23
37
|
}
|
|
24
38
|
|
|
25
39
|
export interface RandomDep {
|
|
@@ -28,9 +42,13 @@ export interface RandomDep {
|
|
|
28
42
|
|
|
29
43
|
/** Creates a {@link Random} using Math.random(). */
|
|
30
44
|
export const createRandom = (): Random => ({
|
|
31
|
-
next: () => Math.random(),
|
|
45
|
+
next: () => Math.random() as RandomNumber,
|
|
32
46
|
});
|
|
33
47
|
|
|
48
|
+
/** Creates a seeded {@link Random} for deterministic tests. Default seed "evolu". */
|
|
49
|
+
export const testCreateRandom = (seed = "evolu"): Random =>
|
|
50
|
+
createRandomWithSeed(seed);
|
|
51
|
+
|
|
34
52
|
/**
|
|
35
53
|
* Creates {@link Random} using {@link RandomLibDep} with a seed which is useful
|
|
36
54
|
* for tests.
|
|
@@ -38,7 +56,7 @@ export const createRandom = (): Random => ({
|
|
|
38
56
|
export const createRandomWithSeed = (seed: string): Random => {
|
|
39
57
|
const random = new RandomLib(seed);
|
|
40
58
|
return {
|
|
41
|
-
next: () => random.next(),
|
|
59
|
+
next: () => random.next() as RandomNumber,
|
|
42
60
|
};
|
|
43
61
|
};
|
|
44
62
|
|
|
@@ -48,16 +66,12 @@ export const createRandomWithSeed = (seed: string): Random => {
|
|
|
48
66
|
* https://github.com/transitive-bullshit/random
|
|
49
67
|
*/
|
|
50
68
|
export interface RandomLibDep {
|
|
51
|
-
|
|
69
|
+
readonly randomLib: RandomLib;
|
|
52
70
|
}
|
|
53
71
|
|
|
54
72
|
/** Creates a `RandomLib` using the NPM `random` package. */
|
|
55
73
|
export const createRandomLib = (): RandomLib => new RandomLib();
|
|
56
74
|
|
|
57
|
-
/**
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
*/
|
|
61
|
-
export const createRandomLibWithSeed = (seed: string): RandomLibDep => ({
|
|
62
|
-
random: new RandomLib(seed),
|
|
63
|
-
});
|
|
75
|
+
/** Creates a seeded `RandomLib` for deterministic tests. Default seed "evolu". */
|
|
76
|
+
export const testCreateRandomLib = (seed = "evolu"): RandomLib =>
|
|
77
|
+
new RandomLib(seed);
|
package/src/Redacted.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sensitive value protection against accidental exposure.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import { assert } from "./Assert.js";
|
|
2
8
|
import type { Brand } from "./Brand.js";
|
|
3
9
|
import type { Eq } from "./Eq.js";
|
|
@@ -44,8 +50,8 @@ import type { Eq } from "./Eq.js";
|
|
|
44
50
|
* // use value...
|
|
45
51
|
* };
|
|
46
52
|
*
|
|
47
|
-
* fetchUser(redactedKey); //
|
|
48
|
-
* // fetchUser(createRedacted("x" as DbPassword)); //
|
|
53
|
+
* fetchUser(redactedKey); // ok
|
|
54
|
+
* // fetchUser(createRedacted("x" as DbPassword)); // type error
|
|
49
55
|
*
|
|
50
56
|
* // Automatic cleanup with `using`
|
|
51
57
|
* {
|
|
@@ -53,8 +59,6 @@ import type { Eq } from "./Eq.js";
|
|
|
53
59
|
* // ... use secret ...
|
|
54
60
|
* } // automatically wiped from memory
|
|
55
61
|
* ```
|
|
56
|
-
*
|
|
57
|
-
* @experimental
|
|
58
62
|
*/
|
|
59
63
|
export interface Redacted<A> extends Brand<"Redacted">, Disposable {
|
|
60
64
|
/** The inner type. Useful for inference via `typeof redacted.Type`. */
|