@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/src/Object.ts
CHANGED
|
@@ -22,15 +22,20 @@ export type ReadonlyRecord<K extends keyof any, V> = Readonly<Record<K, V>>;
|
|
|
22
22
|
* Object.prototype` so it also works for plain objects coming from another
|
|
23
23
|
* JavaScript realm.
|
|
24
24
|
*
|
|
25
|
+
* TODO: deprecated Use `Object.is` from Type. Define a dedicated Type when
|
|
26
|
+
* another object domain is required.
|
|
27
|
+
*
|
|
25
28
|
* ### Example
|
|
26
29
|
*
|
|
27
30
|
* ```ts
|
|
28
|
-
* isPlainObject
|
|
29
|
-
*
|
|
30
|
-
* isPlainObject(
|
|
31
|
-
* isPlainObject(
|
|
32
|
-
* isPlainObject(
|
|
33
|
-
* isPlainObject(
|
|
31
|
+
* import { isPlainObject } from "@evolu/common";
|
|
32
|
+
*
|
|
33
|
+
* expect(isPlainObject({})).toBe(true);
|
|
34
|
+
* expect(isPlainObject(Object.create(null))).toBe(true);
|
|
35
|
+
* expect(isPlainObject(new Date())).toBe(false);
|
|
36
|
+
* expect(isPlainObject(new (class Example {})())).toBe(false);
|
|
37
|
+
* expect(isPlainObject([])).toBe(false);
|
|
38
|
+
* expect(isPlainObject(null)).toBe(false);
|
|
34
39
|
* ```
|
|
35
40
|
*/
|
|
36
41
|
export const isPlainObject = (
|
|
@@ -50,9 +55,11 @@ export const isPlainObject = (
|
|
|
50
55
|
* ### Example
|
|
51
56
|
*
|
|
52
57
|
* ```ts
|
|
53
|
-
* isFunction
|
|
54
|
-
*
|
|
55
|
-
* isFunction({});
|
|
58
|
+
* import { isFunction } from "@evolu/common";
|
|
59
|
+
*
|
|
60
|
+
* expect(isFunction(() => {})).toBe(true);
|
|
61
|
+
* expect(isFunction(function () {})).toBe(true);
|
|
62
|
+
* expect(isFunction({})).toBe(false);
|
|
56
63
|
* ```
|
|
57
64
|
*/
|
|
58
65
|
export const isFunction = (value: unknown): value is globalThis.Function =>
|
|
@@ -64,9 +71,11 @@ export const isFunction = (value: unknown): value is globalThis.Function =>
|
|
|
64
71
|
* ### Example
|
|
65
72
|
*
|
|
66
73
|
* ```ts
|
|
67
|
-
* isIterable
|
|
68
|
-
*
|
|
69
|
-
* isIterable(
|
|
74
|
+
* import { isIterable } from "@evolu/common";
|
|
75
|
+
*
|
|
76
|
+
* expect(isIterable([1, 2, 3])).toBe(true);
|
|
77
|
+
* expect(isIterable("abc")).toBe(true);
|
|
78
|
+
* expect(isIterable({})).toBe(false);
|
|
70
79
|
* ```
|
|
71
80
|
*/
|
|
72
81
|
export const isIterable = (value: unknown): value is Iterable<unknown> =>
|
|
@@ -79,17 +88,26 @@ export const isIterable = (value: unknown): value is Iterable<unknown> =>
|
|
|
79
88
|
* ### Example
|
|
80
89
|
*
|
|
81
90
|
* ```ts
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
91
|
+
* import {
|
|
92
|
+
* objectToEntries,
|
|
93
|
+
* type Brand,
|
|
94
|
+
* type ReadonlyRecord,
|
|
95
|
+
* } from "@evolu/common";
|
|
96
|
+
*
|
|
97
|
+
* type UserId = string & Brand<"UserId">;
|
|
98
|
+
*
|
|
99
|
+
* const userId = "u1" as UserId;
|
|
100
|
+
* const users: ReadonlyRecord<UserId, string> = { [userId]: "Alice" };
|
|
101
|
+
* const entries = objectToEntries(users);
|
|
102
|
+
*
|
|
103
|
+
* expectTypeOf(entries).toEqualTypeOf<ReadonlyArray<[UserId, string]>>();
|
|
104
|
+
* expect(entries).toEqual([[userId, "Alice"]]);
|
|
85
105
|
* ```
|
|
86
106
|
*/
|
|
87
107
|
export const objectToEntries = <T extends Record<string, any>>(
|
|
88
108
|
record: T,
|
|
89
109
|
): ReadonlyArray<[StringKeyOf<T>, T[StringKeyOf<T>]]> =>
|
|
90
|
-
Object.entries(record) as Array<
|
|
91
|
-
[StringKeyOf<T>, T[StringKeyOf<T>]]
|
|
92
|
-
>;
|
|
110
|
+
Object.entries(record) as Array<[StringKeyOf<T>, T[StringKeyOf<T>]]>;
|
|
93
111
|
|
|
94
112
|
// A helper type to remove symbol keys (e.g for branded objects).
|
|
95
113
|
type StringKeyOf<T> = Extract<keyof T, string>;
|
|
@@ -103,11 +121,20 @@ type StringKeyOf<T> = Extract<keyof T, string>;
|
|
|
103
121
|
* ### Example
|
|
104
122
|
*
|
|
105
123
|
* ```ts
|
|
106
|
-
*
|
|
124
|
+
* import {
|
|
125
|
+
* objectFromEntries,
|
|
126
|
+
* type Brand,
|
|
127
|
+
* type ReadonlyRecord,
|
|
128
|
+
* } from "@evolu/common";
|
|
129
|
+
*
|
|
130
|
+
* type UserId = string & Brand<"UserId">;
|
|
107
131
|
* const entries: ReadonlyArray<[UserId, string]> = [
|
|
108
132
|
* ["u1" as UserId, "Alice"],
|
|
109
133
|
* ];
|
|
110
|
-
* const users = objectFromEntries(entries);
|
|
134
|
+
* const users = objectFromEntries(entries);
|
|
135
|
+
*
|
|
136
|
+
* expectTypeOf(users).toEqualTypeOf<ReadonlyRecord<UserId, string>>();
|
|
137
|
+
* expect(users).toEqual({ u1: "Alice" });
|
|
111
138
|
* ```
|
|
112
139
|
*/
|
|
113
140
|
export const objectFromEntries = <K extends string, V>(
|
|
@@ -123,11 +150,20 @@ export const objectFromEntries = <K extends string, V>(
|
|
|
123
150
|
* ### Example
|
|
124
151
|
*
|
|
125
152
|
* ```ts
|
|
126
|
-
* objectFrom
|
|
127
|
-
*
|
|
153
|
+
* import { objectFrom, type ReadonlyRecord } from "@evolu/common";
|
|
154
|
+
*
|
|
155
|
+
* const translations = objectFrom(
|
|
156
|
+
* ["en", "fr"] as const,
|
|
157
|
+
* (locale): string => `Hello in ${locale}`,
|
|
158
|
+
* );
|
|
128
159
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
160
|
+
* expectTypeOf(translations).toEqualTypeOf<
|
|
161
|
+
* ReadonlyRecord<"en" | "fr", string>
|
|
162
|
+
* >();
|
|
163
|
+
* expect(translations).toEqual({
|
|
164
|
+
* en: "Hello in en",
|
|
165
|
+
* fr: "Hello in fr",
|
|
166
|
+
* });
|
|
131
167
|
* ```
|
|
132
168
|
*/
|
|
133
169
|
export const objectFrom = <K extends string, V>(
|
|
@@ -168,53 +204,138 @@ export const excludeProp = <T extends object, K extends keyof T>(
|
|
|
168
204
|
};
|
|
169
205
|
|
|
170
206
|
/**
|
|
171
|
-
* Creates a
|
|
207
|
+
* Creates a mutable Record.
|
|
172
208
|
*
|
|
173
|
-
* Use
|
|
174
|
-
*
|
|
175
|
-
*
|
|
209
|
+
* Use it to build a Record locally through mutation, avoiding repeated object
|
|
210
|
+
* spreads. Keep mutation scoped to the constructing function, and treat the
|
|
211
|
+
* completed Record as immutable after it leaves that scope. When a source is
|
|
212
|
+
* provided, its own enumerable properties are shallow-copied. Inherited and
|
|
213
|
+
* non-enumerable properties are not copied.
|
|
176
214
|
*
|
|
177
|
-
*
|
|
215
|
+
* The Record is created with `Object.create(null)`, so any string can be used
|
|
216
|
+
* as a key while it is being built. Missing keys such as `toString` and
|
|
217
|
+
* `constructor` do not resolve to inherited values, and assigning `__proto__`
|
|
218
|
+
* creates an own data property instead of changing the prototype.
|
|
219
|
+
*
|
|
220
|
+
* The null prototype makes dynamic construction safer, but it does not have to
|
|
221
|
+
* be preserved. Spreading the completed Record into an ordinary object is
|
|
222
|
+
* supported. Use {@link getOwnProp} when a lookup must read only own
|
|
223
|
+
* properties.
|
|
224
|
+
*
|
|
225
|
+
* For immutable empty application data, defaults, or placeholders, use
|
|
226
|
+
* {@link emptyRecord}.
|
|
227
|
+
*
|
|
228
|
+
* ### Example
|
|
178
229
|
*
|
|
179
230
|
* ```ts
|
|
180
|
-
*
|
|
181
|
-
*
|
|
231
|
+
* import { createMutableRecord } from "@evolu/common";
|
|
232
|
+
*
|
|
233
|
+
* const createValuesByKey = (
|
|
234
|
+
* entries: ReadonlyArray<readonly [string, number]>,
|
|
235
|
+
* ): Readonly<Record<string, number>> => {
|
|
236
|
+
* const valuesByKey = createMutableRecord<string, number>();
|
|
237
|
+
*
|
|
238
|
+
* for (const [key, value] of entries) {
|
|
239
|
+
* valuesByKey[key] = value;
|
|
240
|
+
* }
|
|
241
|
+
*
|
|
242
|
+
* return valuesByKey;
|
|
243
|
+
* };
|
|
244
|
+
*
|
|
245
|
+
* expect(
|
|
246
|
+
* createValuesByKey([
|
|
247
|
+
* ["a", 1],
|
|
248
|
+
* ["b", 2],
|
|
249
|
+
* ]),
|
|
250
|
+
* ).toEqual({ a: 1, b: 2 });
|
|
182
251
|
* ```
|
|
252
|
+
*
|
|
253
|
+
* Note that TypeScript does not model an object's runtime prototype. A plain
|
|
254
|
+
* TypeScript Record exposes `Object.prototype` members even when the runtime
|
|
255
|
+
* object has a `null` prototype:
|
|
256
|
+
*
|
|
257
|
+
* ```ts
|
|
258
|
+
* const values = Object.create(null) as Record<string, number>;
|
|
259
|
+
*
|
|
260
|
+
* // TypeScript accepts the call, but `toString` is undefined at runtime.
|
|
261
|
+
* expect(() => values.toString()).toThrow(TypeError);
|
|
262
|
+
* ```
|
|
263
|
+
*
|
|
264
|
+
* `createMutableRecord` uses the same TypeScript Record representation.
|
|
265
|
+
*
|
|
266
|
+
* ```ts
|
|
267
|
+
* import { createMutableRecord } from "@evolu/common";
|
|
268
|
+
*
|
|
269
|
+
* const values = createMutableRecord<string, number>();
|
|
270
|
+
*
|
|
271
|
+
* // TypeScript accepts the call, but `toString` is undefined at runtime.
|
|
272
|
+
* expect(() => values.toString()).toThrow(TypeError);
|
|
273
|
+
* ```
|
|
274
|
+
*
|
|
275
|
+
* In other words, treat the returned object as string-keyed data rather than
|
|
276
|
+
* calling inherited object methods through it.
|
|
183
277
|
*/
|
|
184
|
-
export
|
|
185
|
-
K,
|
|
186
|
-
V
|
|
187
|
-
>
|
|
278
|
+
export function createMutableRecord<
|
|
279
|
+
K extends string = string,
|
|
280
|
+
V = unknown,
|
|
281
|
+
>(): Record<K, V>;
|
|
282
|
+
export function createMutableRecord<T extends object>(
|
|
283
|
+
source: T,
|
|
284
|
+
): { -readonly [K in keyof T]: T[K] };
|
|
285
|
+
export function createMutableRecord(source?: object): Record<string, unknown> {
|
|
286
|
+
const record = Object.create(null) as Record<string, unknown>;
|
|
287
|
+
return source === undefined ? record : Object.assign(record, source);
|
|
288
|
+
}
|
|
188
289
|
|
|
189
290
|
/**
|
|
190
|
-
*
|
|
291
|
+
* A shared frozen empty readonly Record.
|
|
191
292
|
*
|
|
192
|
-
* Use as
|
|
293
|
+
* Use it as an immutable empty value for defaults and placeholders instead of
|
|
294
|
+
* allocating a new empty object. Because the instance is shared, it is frozen
|
|
295
|
+
* to prevent accidental mutation.
|
|
296
|
+
*
|
|
297
|
+
* Use {@link createMutableRecord} to build a Record locally through mutation
|
|
298
|
+
* instead of repeated object spreads.
|
|
193
299
|
*
|
|
194
300
|
* @group Constants
|
|
195
301
|
*/
|
|
196
302
|
export const emptyRecord: Readonly<Record<string, never>> =
|
|
197
|
-
/*#__PURE__*/
|
|
303
|
+
/*#__PURE__*/ Object.freeze(
|
|
304
|
+
/*#__PURE__*/ createMutableRecord<string, never>(),
|
|
305
|
+
);
|
|
198
306
|
|
|
199
307
|
/**
|
|
200
|
-
*
|
|
201
|
-
*
|
|
308
|
+
* Gets an own property from a record, returning `undefined` if the key is
|
|
309
|
+
* missing or inherited.
|
|
202
310
|
*
|
|
203
311
|
* TypeScript's `Record<K, V>` type assumes all keys exist, but at runtime
|
|
204
|
-
* accessing a
|
|
205
|
-
* typing for that case without
|
|
312
|
+
* accessing a missing key returns `undefined`. This helper provides proper
|
|
313
|
+
* typing for that case without treating properties inherited from
|
|
314
|
+
* `Object.prototype` as record data.
|
|
206
315
|
*
|
|
207
316
|
* ### Example
|
|
208
317
|
*
|
|
209
318
|
* ```ts
|
|
210
|
-
*
|
|
211
|
-
*
|
|
319
|
+
* import { getOwnProp } from "@evolu/common";
|
|
320
|
+
*
|
|
321
|
+
* interface User {
|
|
322
|
+
* readonly name: string;
|
|
323
|
+
* }
|
|
324
|
+
*
|
|
325
|
+
* const users: Readonly<Record<string, User>> = {
|
|
326
|
+
* alice: { name: "Alice" },
|
|
327
|
+
* };
|
|
328
|
+
* const user = getOwnProp(users, "bob");
|
|
329
|
+
*
|
|
330
|
+
* expectTypeOf(user).toEqualTypeOf<User | undefined>();
|
|
331
|
+
* expect(user).toBeUndefined();
|
|
332
|
+
* expect(getOwnProp(users, "toString")).toBeUndefined();
|
|
212
333
|
* ```
|
|
213
334
|
*/
|
|
214
|
-
export const
|
|
335
|
+
export const getOwnProp = <K extends string, V>(
|
|
215
336
|
record: ReadonlyRecord<K, V>,
|
|
216
|
-
key: K
|
|
217
|
-
): V | undefined => (key
|
|
337
|
+
key: NoInfer<K>,
|
|
338
|
+
): V | undefined => (Object.hasOwn(record, key) ? record[key] : undefined);
|
|
218
339
|
|
|
219
340
|
/**
|
|
220
341
|
* A disposable wrapper around `URL.createObjectURL` that automatically revokes
|
|
@@ -224,10 +345,13 @@ export const getProperty = <K extends string, V>(
|
|
|
224
345
|
* ### Example
|
|
225
346
|
*
|
|
226
347
|
* ```ts
|
|
348
|
+
* import { createObjectURL } from "@evolu/common";
|
|
349
|
+
*
|
|
227
350
|
* const blob = new Blob(["hello"], { type: "text/plain" });
|
|
228
351
|
* using objectUrl = createObjectURL(blob);
|
|
229
|
-
*
|
|
230
|
-
*
|
|
352
|
+
*
|
|
353
|
+
* expect(objectUrl.url).toMatch(/^blob:/);
|
|
354
|
+
* // URL.revokeObjectURL is automatically called when the scope ends.
|
|
231
355
|
* ```
|
|
232
356
|
*
|
|
233
357
|
* This ensures the URL is always revoked when the scope ends, even if an error
|
package/src/Option.ts
CHANGED
|
@@ -9,24 +9,27 @@
|
|
|
9
9
|
* ### Example
|
|
10
10
|
*
|
|
11
11
|
* ```ts
|
|
12
|
-
*
|
|
12
|
+
* import { isNone, isSome, none, type Option, some } from "@evolu/common";
|
|
13
|
+
*
|
|
13
14
|
* const cache = new Map<string, Option<unknown>>();
|
|
14
15
|
*
|
|
15
16
|
* const get = (key: string): Option<unknown> => cache.get(key) ?? none;
|
|
16
17
|
*
|
|
17
|
-
* cache.set("a", some(null));
|
|
18
|
-
* cache.set("b", some(undefined));
|
|
18
|
+
* cache.set("a", some(null));
|
|
19
|
+
* cache.set("b", some(undefined));
|
|
19
20
|
*
|
|
20
|
-
*
|
|
21
|
-
* isSome(
|
|
22
|
-
*
|
|
21
|
+
* const a = get("a");
|
|
22
|
+
* assert(isSome(a));
|
|
23
|
+
* expect(a.value).toBeNull();
|
|
24
|
+
* expect(isSome(get("b"))).toBe(true);
|
|
25
|
+
* expect(isNone(get("c"))).toBe(true);
|
|
23
26
|
* ```
|
|
24
27
|
*
|
|
25
28
|
* @module
|
|
26
29
|
*/
|
|
27
30
|
|
|
28
31
|
/** Optional value. */
|
|
29
|
-
import type { Typed } from "./Type.
|
|
32
|
+
import type { Typed } from "./Type.ts";
|
|
30
33
|
|
|
31
34
|
/** Optional value. */
|
|
32
35
|
export type Option<T> = Some<T> | None;
|
package/src/Order.ts
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { Eq, eqFromOrder } from "./Eq.ts";
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* Compares two values of type `A` and returns their ordering.
|
|
9
11
|
*
|
|
@@ -12,6 +14,9 @@
|
|
|
12
14
|
* - Returns `-1` if `x` is less than `y`.
|
|
13
15
|
* - Returns `0` if `x` is equal to `y`.
|
|
14
16
|
* - Returns `1` if `x` is greater than `y`.
|
|
17
|
+
*
|
|
18
|
+
* An `Order` also defines equality. Use {@link eqFromOrder} to derive an
|
|
19
|
+
* {@link Eq}.
|
|
15
20
|
*/
|
|
16
21
|
export type Order<in A> = (x: A, y: A) => Ordering;
|
|
17
22
|
|
|
@@ -28,10 +33,12 @@ export type Ordering = -1 | 0 | 1;
|
|
|
28
33
|
* ### Example
|
|
29
34
|
*
|
|
30
35
|
* ```ts
|
|
36
|
+
* import { createOrder } from "@evolu/common";
|
|
37
|
+
*
|
|
31
38
|
* const orderNumber = createOrder<number>((x, y) => x < y);
|
|
32
|
-
* expect(orderNumber(1, 2)).
|
|
33
|
-
* expect(orderNumber(2, 1)).
|
|
34
|
-
* expect(orderNumber(1, 1)).
|
|
39
|
+
* expect(orderNumber(1, 2)).toBe(-1);
|
|
40
|
+
* expect(orderNumber(2, 1)).toBe(1);
|
|
41
|
+
* expect(orderNumber(1, 1)).toBe(0);
|
|
35
42
|
* ```
|
|
36
43
|
*/
|
|
37
44
|
export const createOrder =
|
|
@@ -45,9 +52,10 @@ export const createOrder =
|
|
|
45
52
|
* ### Example
|
|
46
53
|
*
|
|
47
54
|
* ```ts
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* reverseOrder(orderNumber)
|
|
55
|
+
* import { orderNumber, reverseOrder } from "@evolu/common";
|
|
56
|
+
*
|
|
57
|
+
* const descendingOrderNumber = reverseOrder(orderNumber);
|
|
58
|
+
* expect([3, 1, 2].toSorted(descendingOrderNumber)).toEqual([3, 2, 1]);
|
|
51
59
|
* ```
|
|
52
60
|
*/
|
|
53
61
|
export const reverseOrder =
|
|
@@ -61,10 +69,9 @@ export const reverseOrder =
|
|
|
61
69
|
* ### Example
|
|
62
70
|
*
|
|
63
71
|
* ```ts
|
|
64
|
-
* orderString
|
|
65
|
-
*
|
|
66
|
-
* orderString("a", "
|
|
67
|
-
* ["c", "b", "a"].toSorted(orderString); // ["a", "b", "c"]
|
|
72
|
+
* import { orderString } from "@evolu/common";
|
|
73
|
+
*
|
|
74
|
+
* expect(["c", "b", "a"].toSorted(orderString)).toEqual(["a", "b", "c"]);
|
|
68
75
|
* ```
|
|
69
76
|
*/
|
|
70
77
|
export const orderString: Order<string> = /*#__PURE__*/ createOrder(
|
|
@@ -77,13 +84,9 @@ export const orderString: Order<string> = /*#__PURE__*/ createOrder(
|
|
|
77
84
|
* ### Example
|
|
78
85
|
*
|
|
79
86
|
* ```ts
|
|
80
|
-
* orderNumber
|
|
81
|
-
*
|
|
82
|
-
* orderNumber(1,
|
|
83
|
-
* [2, 1, 3].toSorted(orderNumber); // [1, 2, 3]
|
|
84
|
-
* reverseOrder(orderNumber)(1, 2); // 1
|
|
85
|
-
* reverseOrder(orderNumber)(2, 1); // -1
|
|
86
|
-
* reverseOrder(orderNumber)(1, 1); // 0
|
|
87
|
+
* import { orderNumber } from "@evolu/common";
|
|
88
|
+
*
|
|
89
|
+
* expect([2, 1, 3].toSorted(orderNumber)).toEqual([1, 2, 3]);
|
|
87
90
|
* ```
|
|
88
91
|
*/
|
|
89
92
|
export const orderNumber = /*#__PURE__*/ createOrder<number>((a, b) => a < b);
|
|
@@ -94,10 +97,9 @@ export const orderNumber = /*#__PURE__*/ createOrder<number>((a, b) => a < b);
|
|
|
94
97
|
* ### Example
|
|
95
98
|
*
|
|
96
99
|
* ```ts
|
|
97
|
-
* orderBigInt
|
|
98
|
-
*
|
|
99
|
-
* orderBigInt(1n,
|
|
100
|
-
* [2n, 1n, 3n].toSorted(orderBigInt); // [1n, 2n, 3n]
|
|
100
|
+
* import { orderBigInt } from "@evolu/common";
|
|
101
|
+
*
|
|
102
|
+
* expect([2n, 1n, 3n].toSorted(orderBigInt)).toEqual([1n, 2n, 3n]);
|
|
101
103
|
* ```
|
|
102
104
|
*/
|
|
103
105
|
export const orderBigInt = /*#__PURE__*/ createOrder<bigint>((a, b) => a < b);
|
package/src/Platform.ts
CHANGED
|
@@ -10,6 +10,22 @@ export const isHermes = "HermesInternal" in globalThis;
|
|
|
10
10
|
/** Returns true if running in a server environment (no DOM). */
|
|
11
11
|
export const isServer = typeof document === "undefined";
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* True outside production builds.
|
|
15
|
+
*
|
|
16
|
+
* Bundlers (webpack, Vite, esbuild, Metro) replace the text
|
|
17
|
+
* `process.env.NODE_ENV` statically at build time, so in production bundles
|
|
18
|
+
* this expression folds to `false` and code behind it is dead-code-eliminated —
|
|
19
|
+
* the same mechanism React uses. The `typeof` guard makes the runtime read safe
|
|
20
|
+
* where no bundler ran, such as un-bundled browser ESM, where it fails closed
|
|
21
|
+
* to production behavior. Node.js reads it natively; React Native polyfills it.
|
|
22
|
+
* A missing `NODE_ENV` counts as development, matching React semantics.
|
|
23
|
+
*/
|
|
24
|
+
export const isDev =
|
|
25
|
+
typeof process === "undefined"
|
|
26
|
+
? false
|
|
27
|
+
: process.env.NODE_ENV !== "production";
|
|
28
|
+
|
|
13
29
|
/**
|
|
14
30
|
* Detects if Node.js Buffer is available and should be used.
|
|
15
31
|
*
|
|
@@ -54,3 +70,123 @@ export type ReloadApp = (url?: string) => void;
|
|
|
54
70
|
export interface ReloadAppDep {
|
|
55
71
|
readonly reloadApp: ReloadApp;
|
|
56
72
|
}
|
|
73
|
+
|
|
74
|
+
export interface TestGlobalErrors extends Disposable {
|
|
75
|
+
readonly errors: ReadonlyArray<unknown>;
|
|
76
|
+
readonly next: () => Promise<unknown>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Waits until every error already reported to the platform has been delivered
|
|
80
|
+
* to this recorder, then returns {@link TestGlobalErrors.errors}.
|
|
81
|
+
*
|
|
82
|
+
* Platforms deliver global errors asynchronously and provide no hook for
|
|
83
|
+
* "nothing was reported", so settle emits a sentinel of the same kind and
|
|
84
|
+
* resolves when it arrives. Platform delivery is ordered, so all earlier
|
|
85
|
+
* errors are recorded by then. Use it to assert absence: `expect(await
|
|
86
|
+
* unhandledRejections.settle()).toEqual([])`.
|
|
87
|
+
*/
|
|
88
|
+
readonly settle: () => Promise<ReadonlyArray<unknown>>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Records platform global uncaught-error reporting until disposed. */
|
|
92
|
+
export const testGlobalUncaughtErrors = (): TestGlobalErrors =>
|
|
93
|
+
createTestGlobalErrors("uncaughtErrors");
|
|
94
|
+
|
|
95
|
+
/** Records platform global unhandled-rejection reporting until disposed. */
|
|
96
|
+
export const testGlobalUnhandledRejections = (): TestGlobalErrors =>
|
|
97
|
+
createTestGlobalErrors("unhandledRejection");
|
|
98
|
+
|
|
99
|
+
const settleSentinel = new Error("TestGlobalErrors.settle sentinel");
|
|
100
|
+
|
|
101
|
+
const createTestGlobalErrors = (
|
|
102
|
+
kind: "uncaughtErrors" | "unhandledRejection",
|
|
103
|
+
): TestGlobalErrors => {
|
|
104
|
+
const nodeEvent =
|
|
105
|
+
kind === "uncaughtErrors" ? "uncaughtException" : "unhandledRejection";
|
|
106
|
+
const webEvent = kind === "uncaughtErrors" ? "error" : "unhandledrejection";
|
|
107
|
+
const webErrorKey = kind === "uncaughtErrors" ? "error" : "reason";
|
|
108
|
+
const errorType =
|
|
109
|
+
kind === "uncaughtErrors" ? "uncaught-error" : "unhandled-rejection";
|
|
110
|
+
const disposableStack = new DisposableStack();
|
|
111
|
+
const errors: Array<unknown> = [];
|
|
112
|
+
const nextWaiters: Array<(error: unknown) => void> = [];
|
|
113
|
+
const settleWaiters: Array<() => void> = [];
|
|
114
|
+
let nextIndex = 0;
|
|
115
|
+
|
|
116
|
+
const next = (): Promise<unknown> => {
|
|
117
|
+
if (nextIndex < errors.length) {
|
|
118
|
+
const error = errors[nextIndex];
|
|
119
|
+
nextIndex += 1;
|
|
120
|
+
return Promise.resolve(error);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const next = Promise.withResolvers<unknown>();
|
|
124
|
+
nextWaiters.push(next.resolve);
|
|
125
|
+
return next.promise;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const record = (error: unknown): void => {
|
|
129
|
+
if (error === settleSentinel) {
|
|
130
|
+
settleWaiters.shift()?.();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
errors.push(error);
|
|
135
|
+
const resolveNext = nextWaiters.shift();
|
|
136
|
+
if (!resolveNext) return;
|
|
137
|
+
|
|
138
|
+
nextIndex += 1;
|
|
139
|
+
resolveNext(error);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const settle = (): Promise<ReadonlyArray<unknown>> => {
|
|
143
|
+
const settled = Promise.withResolvers<ReadonlyArray<unknown>>();
|
|
144
|
+
settleWaiters.push(() => {
|
|
145
|
+
settled.resolve(errors);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (kind === "unhandledRejection") {
|
|
149
|
+
void Promise.reject(settleSentinel);
|
|
150
|
+
} else {
|
|
151
|
+
queueMicrotask(() => {
|
|
152
|
+
throw settleSentinel;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return settled.promise;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
if (
|
|
160
|
+
typeof process === "object" &&
|
|
161
|
+
typeof process.on === "function" &&
|
|
162
|
+
typeof process.off === "function"
|
|
163
|
+
) {
|
|
164
|
+
process.on(nodeEvent, record);
|
|
165
|
+
disposableStack.defer(() => {
|
|
166
|
+
process.off(nodeEvent, record);
|
|
167
|
+
});
|
|
168
|
+
} else if (typeof globalThis.addEventListener === "function") {
|
|
169
|
+
const listener = (event: unknown): void => {
|
|
170
|
+
(event as Event).preventDefault();
|
|
171
|
+
record((event as Record<typeof webErrorKey, unknown>)[webErrorKey]);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
globalThis.addEventListener(webEvent, listener);
|
|
175
|
+
disposableStack.defer(() => {
|
|
176
|
+
globalThis.removeEventListener(webEvent, listener);
|
|
177
|
+
});
|
|
178
|
+
} else {
|
|
179
|
+
throw new Error(`Unsupported platform global ${errorType} reporting.`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
errors,
|
|
184
|
+
next,
|
|
185
|
+
settle,
|
|
186
|
+
[Symbol.dispose]: () => {
|
|
187
|
+
nextWaiters.length = 0;
|
|
188
|
+
settleWaiters.length = 0;
|
|
189
|
+
disposableStack.dispose();
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
};
|
package/src/Polyfills.ts
CHANGED
|
@@ -199,13 +199,10 @@ const installSuppressedError = (): void => {
|
|
|
199
199
|
const createDisposableStackPolyfill = (
|
|
200
200
|
symbolDispose: symbol,
|
|
201
201
|
): new () => DisposableStack => {
|
|
202
|
-
class DisposableStackPolyfill
|
|
202
|
+
class DisposableStackPolyfill {
|
|
203
203
|
#disposed = false;
|
|
204
204
|
#resources: Array<DisposableResource> = [];
|
|
205
205
|
|
|
206
|
-
declare readonly [Symbol.toStringTag]: "DisposableStack";
|
|
207
|
-
declare [Symbol.dispose]: () => void;
|
|
208
|
-
|
|
209
206
|
get disposed(): boolean {
|
|
210
207
|
return this.#disposed;
|
|
211
208
|
}
|
|
@@ -278,7 +275,7 @@ const createDisposableStackPolyfill = (
|
|
|
278
275
|
this.#resources = [];
|
|
279
276
|
this.#disposed = true;
|
|
280
277
|
|
|
281
|
-
return moved;
|
|
278
|
+
return moved as unknown as DisposableStack;
|
|
282
279
|
}
|
|
283
280
|
|
|
284
281
|
dispose(): void {
|
|
@@ -321,20 +318,17 @@ const createDisposableStackPolyfill = (
|
|
|
321
318
|
value: "DisposableStack",
|
|
322
319
|
});
|
|
323
320
|
|
|
324
|
-
return DisposableStackPolyfill;
|
|
321
|
+
return DisposableStackPolyfill as unknown as new () => DisposableStack;
|
|
325
322
|
};
|
|
326
323
|
|
|
327
324
|
const createAsyncDisposableStackPolyfill = (
|
|
328
325
|
symbolDispose: symbol,
|
|
329
326
|
symbolAsyncDispose: symbol,
|
|
330
327
|
): new () => AsyncDisposableStack => {
|
|
331
|
-
class AsyncDisposableStackPolyfill
|
|
328
|
+
class AsyncDisposableStackPolyfill {
|
|
332
329
|
#disposed = false;
|
|
333
330
|
#resources: Array<AsyncDisposableResource> = [];
|
|
334
331
|
|
|
335
|
-
declare readonly [Symbol.toStringTag]: "AsyncDisposableStack";
|
|
336
|
-
declare [Symbol.asyncDispose]: () => Promise<void>;
|
|
337
|
-
|
|
338
332
|
get disposed(): boolean {
|
|
339
333
|
return this.#disposed;
|
|
340
334
|
}
|
|
@@ -417,7 +411,7 @@ const createAsyncDisposableStackPolyfill = (
|
|
|
417
411
|
this.#resources = [];
|
|
418
412
|
this.#disposed = true;
|
|
419
413
|
|
|
420
|
-
return moved;
|
|
414
|
+
return moved as unknown as AsyncDisposableStack;
|
|
421
415
|
}
|
|
422
416
|
|
|
423
417
|
async disposeAsync(): Promise<void> {
|
|
@@ -464,7 +458,7 @@ const createAsyncDisposableStackPolyfill = (
|
|
|
464
458
|
},
|
|
465
459
|
);
|
|
466
460
|
|
|
467
|
-
return AsyncDisposableStackPolyfill;
|
|
461
|
+
return AsyncDisposableStackPolyfill as unknown as new () => AsyncDisposableStack;
|
|
468
462
|
};
|
|
469
463
|
|
|
470
464
|
/** Installs `Map` and `WeakMap` upsert polyfills missing from the runtime. */
|