@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/Types.ts
CHANGED
|
@@ -4,19 +4,29 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import * as Kysely from "kysely";
|
|
8
|
-
import type { TypeName } from "./Type.js";
|
|
9
|
-
|
|
10
7
|
/**
|
|
11
8
|
* A function that receives a value and returns nothing.
|
|
12
9
|
*
|
|
13
10
|
* Use for event handlers, observers, and async completion handlers.
|
|
14
11
|
*
|
|
15
|
-
* ###
|
|
12
|
+
* ### Completion callbacks
|
|
16
13
|
*
|
|
17
14
|
* ```ts
|
|
18
|
-
*
|
|
19
|
-
*
|
|
15
|
+
* import { ok, type Callback, type Result } from "@evolu/common";
|
|
16
|
+
*
|
|
17
|
+
* const completedValues: Array<string> = [];
|
|
18
|
+
* const onComplete: Callback<string> = (value) => {
|
|
19
|
+
* completedValues.push(value);
|
|
20
|
+
* };
|
|
21
|
+
* const queue = new Set<Callback<Result<string, Error>>>();
|
|
22
|
+
* queue.add((result) => {
|
|
23
|
+
* if (result.ok) completedValues.push(result.value);
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* onComplete("direct");
|
|
27
|
+
* for (const callback of queue) callback(ok("queued"));
|
|
28
|
+
*
|
|
29
|
+
* expect(completedValues).toEqual(["direct", "queued"]);
|
|
20
30
|
* ```
|
|
21
31
|
*/
|
|
22
32
|
export type Callback<T> = (value: T) => void;
|
|
@@ -26,13 +36,29 @@ export type Callback<T> = (value: T) => void;
|
|
|
26
36
|
*
|
|
27
37
|
* Use for subscriptions or callbacks that need abort-time teardown.
|
|
28
38
|
*
|
|
29
|
-
* ###
|
|
39
|
+
* ### Subscription teardown
|
|
30
40
|
*
|
|
31
41
|
* ```ts
|
|
42
|
+
* import type { CallbackWithTeardown } from "@evolu/common";
|
|
43
|
+
*
|
|
44
|
+
* interface EventSource {
|
|
45
|
+
* readonly start: () => void;
|
|
46
|
+
* readonly stop: () => void;
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* const events: Array<string> = [];
|
|
50
|
+
* const source: EventSource = {
|
|
51
|
+
* start: () => events.push("started"),
|
|
52
|
+
* stop: () => events.push("stopped"),
|
|
53
|
+
* };
|
|
32
54
|
* const subscribe: CallbackWithTeardown<EventSource> = (source) => {
|
|
33
55
|
* source.start();
|
|
34
|
-
* return
|
|
56
|
+
* return source.stop;
|
|
35
57
|
* };
|
|
58
|
+
* const teardown = subscribe(source);
|
|
59
|
+
* if (teardown) teardown();
|
|
60
|
+
*
|
|
61
|
+
* expect(events).toEqual(["started", "stopped"]);
|
|
36
62
|
* ```
|
|
37
63
|
*/
|
|
38
64
|
export type CallbackWithTeardown<T> = (value: T) => void | (() => void);
|
|
@@ -42,13 +68,14 @@ export type CallbackWithTeardown<T> = (value: T) => void | (() => void);
|
|
|
42
68
|
*
|
|
43
69
|
* A predicate starts with an 'is' prefix, e.g., `isEven`.
|
|
44
70
|
*
|
|
45
|
-
* ###
|
|
71
|
+
* ### Filtering values
|
|
46
72
|
*
|
|
47
73
|
* ```ts
|
|
74
|
+
* import type { Predicate } from "@evolu/common";
|
|
75
|
+
*
|
|
48
76
|
* const isEven: Predicate<number> = (n) => n % 2 === 0;
|
|
49
77
|
*
|
|
50
|
-
*
|
|
51
|
-
* const evenNumbers = numbers.filter(isEven); // [2, 4]
|
|
78
|
+
* expect([1, 2, 3, 4].filter(isEven)).toEqual([2, 4]);
|
|
52
79
|
* ```
|
|
53
80
|
*/
|
|
54
81
|
export type Predicate<T> = (value: T) => boolean;
|
|
@@ -58,14 +85,15 @@ export type Predicate<T> = (value: T) => boolean;
|
|
|
58
85
|
*
|
|
59
86
|
* Useful for callbacks that need both the element and its position.
|
|
60
87
|
*
|
|
61
|
-
* ###
|
|
88
|
+
* ### Filtering by position
|
|
62
89
|
*
|
|
63
90
|
* ```ts
|
|
91
|
+
* import type { PredicateWithIndex } from "@evolu/common";
|
|
92
|
+
*
|
|
64
93
|
* const isEvenIndex: PredicateWithIndex<string> = (value, index) =>
|
|
65
94
|
* index % 2 === 0;
|
|
66
95
|
*
|
|
67
|
-
*
|
|
68
|
-
* const evenIndexItems = items.filter(isEvenIndex); // ["a", "c"]
|
|
96
|
+
* expect(["a", "b", "c", "d"].filter(isEvenIndex)).toEqual(["a", "c"]);
|
|
69
97
|
* ```
|
|
70
98
|
*/
|
|
71
99
|
export type PredicateWithIndex<T> = (value: T, index: number) => boolean;
|
|
@@ -73,19 +101,25 @@ export type PredicateWithIndex<T> = (value: T, index: number) => boolean;
|
|
|
73
101
|
/**
|
|
74
102
|
* A type guard function that refines type `A` to a narrower type `B`.
|
|
75
103
|
*
|
|
76
|
-
* ###
|
|
104
|
+
* ### Narrowing a value
|
|
77
105
|
*
|
|
78
106
|
* ```ts
|
|
79
|
-
* type
|
|
80
|
-
*
|
|
107
|
+
* import type { Refinement } from "@evolu/common";
|
|
108
|
+
*
|
|
109
|
+
* interface Animal {
|
|
110
|
+
* readonly name: string;
|
|
111
|
+
* }
|
|
112
|
+
* interface Dog extends Animal {
|
|
113
|
+
* readonly breed: string;
|
|
114
|
+
* }
|
|
81
115
|
*
|
|
82
116
|
* const isDog: Refinement<Animal, Dog> = (animal): animal is Dog =>
|
|
83
117
|
* "breed" in animal;
|
|
118
|
+
* const dog: Dog = { name: "Dog", breed: "Beagle" };
|
|
119
|
+
* const animal: Animal = dog;
|
|
120
|
+
* if (!isDog(animal)) throw new Error("Expected a dog");
|
|
84
121
|
*
|
|
85
|
-
*
|
|
86
|
-
* if (isDog(animal)) {
|
|
87
|
-
* console.log(animal.breed); // Safe access to `breed`
|
|
88
|
-
* }
|
|
122
|
+
* expect(animal.breed).toBe("Beagle");
|
|
89
123
|
* ```
|
|
90
124
|
*/
|
|
91
125
|
export type Refinement<in A, out B extends A> = (a: A) => a is B;
|
|
@@ -97,17 +131,30 @@ export type Refinement<in A, out B extends A> = (a: A) => a is B;
|
|
|
97
131
|
* Useful for callbacks that need both the element and its position while
|
|
98
132
|
* maintaining type narrowing.
|
|
99
133
|
*
|
|
100
|
-
* ###
|
|
134
|
+
* ### Indexed refinement
|
|
101
135
|
*
|
|
102
136
|
* ```ts
|
|
103
|
-
*
|
|
137
|
+
* import { partitionArray, type RefinementWithIndex } from "@evolu/common";
|
|
104
138
|
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
139
|
+
* type Item = {
|
|
140
|
+
* readonly type: "number" | "string";
|
|
141
|
+
* readonly value: unknown;
|
|
142
|
+
* };
|
|
143
|
+
* type NumberItem = Item & { readonly type: "number" };
|
|
144
|
+
*
|
|
145
|
+
* const isNumberItem: RefinementWithIndex<Item, NumberItem> = (
|
|
146
|
+
* item,
|
|
147
|
+
* index,
|
|
148
|
+
* ): item is NumberItem => index > 0 && item.type === "number";
|
|
149
|
+
* const items: ReadonlyArray<Item> = [
|
|
150
|
+
* { type: "number", value: 1 },
|
|
151
|
+
* { type: "number", value: 2 },
|
|
152
|
+
* ];
|
|
110
153
|
* const [numbers, others] = partitionArray(items, isNumberItem);
|
|
154
|
+
*
|
|
155
|
+
* expectTypeOf(numbers).toEqualTypeOf<ReadonlyArray<NumberItem>>();
|
|
156
|
+
* expect(numbers[0]?.value).toBe(2);
|
|
157
|
+
* expect(others[0]?.value).toBe(1);
|
|
111
158
|
* ```
|
|
112
159
|
*/
|
|
113
160
|
export type RefinementWithIndex<in A, out B extends A> = (
|
|
@@ -115,26 +162,96 @@ export type RefinementWithIndex<in A, out B extends A> = (
|
|
|
115
162
|
index: number,
|
|
116
163
|
) => a is B;
|
|
117
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Realm-neutral runtime identity for a TypeScript interface.
|
|
167
|
+
*
|
|
168
|
+
* Extend this interface and add its runtime evidence with {@link instance} when
|
|
169
|
+
* constructing a value. Unlike JavaScript `instanceof`, the identity does not
|
|
170
|
+
* depend on a constructor or prototype and therefore survives realms, package
|
|
171
|
+
* duplication, object spreading, and structured cloning.
|
|
172
|
+
*
|
|
173
|
+
* The marker is intentionally forgeable. It identifies values created by
|
|
174
|
+
* trusted constructors; it is not structural validation or a security
|
|
175
|
+
* boundary.
|
|
176
|
+
*
|
|
177
|
+
* ### Adding runtime identity
|
|
178
|
+
*
|
|
179
|
+
* ```ts
|
|
180
|
+
* import { instance, type Instance } from "@evolu/common";
|
|
181
|
+
*
|
|
182
|
+
* interface Foo extends Instance<"Foo"> {
|
|
183
|
+
* readonly value: string;
|
|
184
|
+
* }
|
|
185
|
+
*
|
|
186
|
+
* const foo: Foo = {
|
|
187
|
+
* ...instance("Foo"),
|
|
188
|
+
* value: "value",
|
|
189
|
+
* };
|
|
190
|
+
*
|
|
191
|
+
* expect(foo["~evolu/instance"]).toBe("Foo");
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
export interface Instance<Name extends string> {
|
|
195
|
+
readonly "~evolu/instance": Name;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Creates the runtime identity property required by {@link Instance}. */
|
|
199
|
+
export const instance = <const Name extends string>(
|
|
200
|
+
name: Name,
|
|
201
|
+
): Instance<Name> => ({ "~evolu/instance": name });
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Creates a realm-neutral predicate for one {@link Instance} name.
|
|
205
|
+
*
|
|
206
|
+
* The identity must be stored directly on the value; inherited markers are
|
|
207
|
+
* ignored.
|
|
208
|
+
*
|
|
209
|
+
* The explicit value type can include the rest of an interface whose trusted
|
|
210
|
+
* constructors attach the matching identity.
|
|
211
|
+
*
|
|
212
|
+
* ### Checking runtime identity
|
|
213
|
+
*
|
|
214
|
+
* ```ts
|
|
215
|
+
* import { instance, isInstance, type Instance } from "@evolu/common";
|
|
216
|
+
*
|
|
217
|
+
* interface Foo extends Instance<"Foo"> {
|
|
218
|
+
* readonly value: string;
|
|
219
|
+
* }
|
|
220
|
+
*
|
|
221
|
+
* const isFoo = isInstance<Foo>("Foo");
|
|
222
|
+
* const value: unknown = { ...instance("Foo"), value: "value" };
|
|
223
|
+
*
|
|
224
|
+
* expect(isFoo(value)).toBe(true);
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
export const isInstance =
|
|
228
|
+
<Value extends Instance<string>>(name: Value["~evolu/instance"]) =>
|
|
229
|
+
(value: unknown): value is Value =>
|
|
230
|
+
value !== null &&
|
|
231
|
+
typeof value === "object" &&
|
|
232
|
+
globalThis.Object.hasOwn(value, "~evolu/instance") &&
|
|
233
|
+
(value as Instance<string>)["~evolu/instance"] === name;
|
|
234
|
+
|
|
118
235
|
/**
|
|
119
236
|
* Makes properties optional if they accept `null` as a value.
|
|
120
237
|
*
|
|
121
238
|
* For each property in `T`, if `null` is a valid value for that property, the
|
|
122
239
|
* property will be made optional in the resulting type.
|
|
123
240
|
*
|
|
124
|
-
* ###
|
|
241
|
+
* ### Optional nullable properties
|
|
125
242
|
*
|
|
126
243
|
* ```ts
|
|
244
|
+
* import type { NullablePartial } from "@evolu/common";
|
|
245
|
+
*
|
|
127
246
|
* type Example = {
|
|
128
247
|
* required: string;
|
|
129
248
|
* optionalWithNull: string | null;
|
|
130
249
|
* };
|
|
131
250
|
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* // optionalWithNull?: string | null;
|
|
137
|
-
* // }
|
|
251
|
+
* expectTypeOf<NullablePartial<Example>>().toEqualTypeOf<{
|
|
252
|
+
* required: string;
|
|
253
|
+
* optionalWithNull?: string | null;
|
|
254
|
+
* }>();
|
|
138
255
|
* ```
|
|
139
256
|
*/
|
|
140
257
|
export type NullablePartial<
|
|
@@ -145,6 +262,11 @@ export type NullablePartial<
|
|
|
145
262
|
NP = Pick<T, Exclude<keyof T, NK>> & Partial<Pick<T, NK>>,
|
|
146
263
|
> = { [K in keyof NP]: NP[K] };
|
|
147
264
|
|
|
265
|
+
/** A value with a numeric length. */
|
|
266
|
+
export interface ValueWithLength {
|
|
267
|
+
readonly length: number;
|
|
268
|
+
}
|
|
269
|
+
|
|
148
270
|
/**
|
|
149
271
|
* String, number, bigint, boolean, undefined, null
|
|
150
272
|
*
|
|
@@ -172,13 +294,8 @@ export type WidenLiteral<T extends Literal> = T extends string
|
|
|
172
294
|
? bigint
|
|
173
295
|
: T;
|
|
174
296
|
|
|
175
|
-
/**
|
|
176
|
-
|
|
177
|
-
*
|
|
178
|
-
* Useful for constructing immutable objects step-by-step (e.g. builder pattern)
|
|
179
|
-
* before casting them back to the readonly type.
|
|
180
|
-
*/
|
|
181
|
-
export type Mutable<T> = {
|
|
297
|
+
/** Removes `readonly` modifier from all properties of a type. */
|
|
298
|
+
export type Writable<T> = {
|
|
182
299
|
-readonly [P in keyof T]: T[P];
|
|
183
300
|
};
|
|
184
301
|
|
|
@@ -188,19 +305,23 @@ export type Mutable<T> = {
|
|
|
188
305
|
* This utility forces TypeScript to "flatten" an intersection type into a
|
|
189
306
|
* single object type so that tooltips and error messages are easier to read.
|
|
190
307
|
*
|
|
191
|
-
* ###
|
|
308
|
+
* ### Flattening an intersection
|
|
192
309
|
*
|
|
193
310
|
* ```ts
|
|
194
|
-
* type
|
|
195
|
-
* // Without Simplify, TypeScript may display A as:
|
|
196
|
-
* // { a: string } & { b: number }
|
|
311
|
+
* import type { Simplify } from "@evolu/common";
|
|
197
312
|
*
|
|
313
|
+
* type A = { a: string } & { b: number };
|
|
198
314
|
* type B = Simplify<A>;
|
|
199
|
-
*
|
|
200
|
-
*
|
|
315
|
+
*
|
|
316
|
+
* expectTypeOf<B>().toEqualTypeOf<{
|
|
317
|
+
* a: string;
|
|
318
|
+
* b: number;
|
|
319
|
+
* }>();
|
|
201
320
|
* ```
|
|
202
321
|
*/
|
|
203
|
-
export type Simplify<T> =
|
|
322
|
+
export type Simplify<T> = {
|
|
323
|
+
[K in keyof T]: T[K];
|
|
324
|
+
} & {};
|
|
204
325
|
|
|
205
326
|
/**
|
|
206
327
|
* Makes a specific property of an object optional while keeping others
|
|
@@ -215,21 +336,21 @@ export type PartialProp<T, K extends keyof T> = Omit<T, K> &
|
|
|
215
336
|
* Use when a function may complete synchronously or asynchronously depending on
|
|
216
337
|
* runtime conditions (e.g., cache hit vs network fetch).
|
|
217
338
|
*
|
|
218
|
-
* ###
|
|
339
|
+
* ### Sync and async completion
|
|
219
340
|
*
|
|
220
341
|
* ```ts
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
* };
|
|
342
|
+
* import { isPromiseLike, type Awaitable } from "@evolu/common";
|
|
343
|
+
*
|
|
344
|
+
* const cache = new Map([["cached", "from cache"]]);
|
|
345
|
+
* const getData = (id: string): Awaitable<string> =>
|
|
346
|
+
* cache.get(id) ?? Promise.resolve(`fetched ${id}`);
|
|
226
347
|
*
|
|
227
|
-
*
|
|
228
|
-
* const
|
|
348
|
+
* const fetched = await getData("missing");
|
|
349
|
+
* const result = getData("cached");
|
|
350
|
+
* const cached = isPromiseLike(result) ? await result : result;
|
|
229
351
|
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
* const data = isPromiseLike(result) ? await result : result;
|
|
352
|
+
* expect(fetched).toBe("fetched missing");
|
|
353
|
+
* expect(cached).toBe("from cache");
|
|
233
354
|
* ```
|
|
234
355
|
*/
|
|
235
356
|
export type Awaitable<T> = T | PromiseLike<T>;
|
|
@@ -240,17 +361,19 @@ export type Awaitable<T> = T | PromiseLike<T>;
|
|
|
240
361
|
* Use with {@link Awaitable} to conditionally `await` only when necessary,
|
|
241
362
|
* avoiding microtask overhead for synchronous values.
|
|
242
363
|
*
|
|
243
|
-
* ###
|
|
364
|
+
* ### Conditional awaiting
|
|
244
365
|
*
|
|
245
366
|
* ```ts
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
367
|
+
* import { isPromiseLike, type Awaitable } from "@evolu/common";
|
|
368
|
+
*
|
|
369
|
+
* const cache = new Map([["cached", true]]);
|
|
370
|
+
* const validate = (id: string): Awaitable<boolean> =>
|
|
371
|
+
* cache.get(id) ?? Promise.resolve(false);
|
|
251
372
|
*
|
|
252
|
-
* const result = validate(
|
|
373
|
+
* const result = validate("cached");
|
|
253
374
|
* const isValid = isPromiseLike(result) ? await result : result;
|
|
375
|
+
*
|
|
376
|
+
* expect(isValid).toBe(true);
|
|
254
377
|
* ```
|
|
255
378
|
*/
|
|
256
379
|
export const isPromiseLike = <T>(
|
|
@@ -258,33 +381,33 @@ export const isPromiseLike = <T>(
|
|
|
258
381
|
): value is PromiseLike<T> =>
|
|
259
382
|
typeof (value as PromiseLike<T> | null | undefined)?.then === "function";
|
|
260
383
|
|
|
261
|
-
/**
|
|
384
|
+
/** Decimal digit from `"0"` to `"9"`. */
|
|
262
385
|
export type Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
|
|
263
386
|
|
|
264
|
-
/**
|
|
387
|
+
/** Decimal digit from `"1"` to `"9"`. */
|
|
265
388
|
export type Digit1To9 = Exclude<Digit, "0">;
|
|
266
389
|
|
|
267
|
-
/**
|
|
390
|
+
/** Decimal string from `"1"` to `"6"`. */
|
|
268
391
|
export type Digit1To6 = "1" | "2" | "3" | "4" | "5" | "6";
|
|
269
392
|
|
|
270
|
-
/**
|
|
393
|
+
/** Decimal string from `"1"` to `"23"`. */
|
|
271
394
|
export type Digit1To23 =
|
|
272
395
|
| Digit1To9 // 1-9
|
|
273
396
|
| `1${Digit}` // 10-19
|
|
274
397
|
| `2${"0" | "1" | "2" | "3"}`; // 20-23
|
|
275
398
|
|
|
276
|
-
/**
|
|
399
|
+
/** Decimal string from `"1"` to `"51"`. */
|
|
277
400
|
export type Digit1To51 =
|
|
278
401
|
| Digit1To9 // 1-9
|
|
279
402
|
| `${"1" | "2" | "3" | "4"}${Digit}` // 10-49
|
|
280
403
|
| `5${"0" | "1"}`; // 50-51
|
|
281
404
|
|
|
282
|
-
/**
|
|
405
|
+
/** Decimal string from `"1"` to `"99"`. */
|
|
283
406
|
export type Digit1To99 =
|
|
284
407
|
| Digit1To9 // 1-9
|
|
285
408
|
| `${Digit1To9}${Digit}`; // 10-99
|
|
286
409
|
|
|
287
|
-
/**
|
|
410
|
+
/** Decimal string from `"1"` to `"59"`. */
|
|
288
411
|
export type Digit1To59 =
|
|
289
412
|
| Digit1To9 // 1-9
|
|
290
413
|
| `1${Digit}` // 10-19
|
|
@@ -307,6 +430,24 @@ export type Int1To100 = Int1To99 | 100;
|
|
|
307
430
|
export type NumberFromString<T extends string> =
|
|
308
431
|
T extends `${infer N extends number}` ? N : never;
|
|
309
432
|
|
|
433
|
+
/** Creates a readable compiler-facing error message. */
|
|
434
|
+
export type CompileTimeError<
|
|
435
|
+
Context extends string,
|
|
436
|
+
Message extends string,
|
|
437
|
+
> = `⛔ ${Context} error: ${Message}`;
|
|
438
|
+
|
|
439
|
+
/** Returns whether a type is a union. */
|
|
440
|
+
export type IsUnion<T, Whole = T> = [T] extends [never]
|
|
441
|
+
? false
|
|
442
|
+
: T extends Whole
|
|
443
|
+
? [Whole] extends [T]
|
|
444
|
+
? false
|
|
445
|
+
: true
|
|
446
|
+
: never;
|
|
447
|
+
|
|
448
|
+
/** Returns every property key present in any member of a union. */
|
|
449
|
+
export type KeysOfUnion<T> = T extends T ? keyof T : never;
|
|
450
|
+
|
|
310
451
|
/** Converts a union to an intersection. */
|
|
311
452
|
export type UnionToIntersection<U> = (
|
|
312
453
|
U extends unknown ? (k: U) => void : never
|
|
@@ -314,57 +455,33 @@ export type UnionToIntersection<U> = (
|
|
|
314
455
|
? I
|
|
315
456
|
: never;
|
|
316
457
|
|
|
458
|
+
/** Intersects the parameter types of a union of unary functions. */
|
|
459
|
+
export type ParameterIntersection<T> = [T] extends [(value: infer I) => void]
|
|
460
|
+
? I
|
|
461
|
+
: unknown;
|
|
462
|
+
|
|
317
463
|
/**
|
|
318
464
|
* Removes keys from each member of a union.
|
|
319
465
|
*
|
|
320
466
|
* Use when {@link Omit} would collapse a discriminated union into a single
|
|
321
467
|
* shared shape.
|
|
322
468
|
*
|
|
323
|
-
* ###
|
|
469
|
+
* ### Preserving discriminated unions
|
|
324
470
|
*
|
|
325
471
|
* ```ts
|
|
472
|
+
* import type { DistributiveOmit } from "@evolu/common";
|
|
473
|
+
*
|
|
326
474
|
* type Event =
|
|
327
475
|
* | { type: "a"; a: string; shared: number }
|
|
328
476
|
* | { type: "b"; b: number; shared: number };
|
|
329
477
|
*
|
|
330
478
|
* type Payload = DistributiveOmit<Event, "shared">;
|
|
331
|
-
*
|
|
479
|
+
*
|
|
480
|
+
* expectTypeOf<Payload>().toEqualTypeOf<
|
|
481
|
+
* { type: "a"; a: string } | { type: "b"; b: number }
|
|
482
|
+
* >();
|
|
332
483
|
* ```
|
|
333
484
|
*/
|
|
334
485
|
export type DistributiveOmit<T, K extends PropertyKey> = T extends unknown
|
|
335
486
|
? Omit<T, K>
|
|
336
487
|
: never;
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Extracts members of a discriminated union by their `type` literal.
|
|
340
|
-
*
|
|
341
|
-
* Constrains `TType` to valid `type` values, so typos fail at the type argument
|
|
342
|
-
* instead of silently producing `never`.
|
|
343
|
-
*
|
|
344
|
-
* ### Example
|
|
345
|
-
*
|
|
346
|
-
* ```ts
|
|
347
|
-
* type Message =
|
|
348
|
-
* | { readonly type: "Create"; readonly id: string }
|
|
349
|
-
* | { readonly type: "Delete"; readonly id: string };
|
|
350
|
-
*
|
|
351
|
-
* type CreateMessage = ExtractType<Message, "Create">;
|
|
352
|
-
* // { readonly type: "Create"; readonly id: string }
|
|
353
|
-
*
|
|
354
|
-
* // Type error: "Cretae" is not a valid Message type
|
|
355
|
-
* type _Typo = ExtractType<Message, "Cretae">;
|
|
356
|
-
* ```
|
|
357
|
-
*/
|
|
358
|
-
export type ExtractType<
|
|
359
|
-
TUnion extends { readonly type: TypeName },
|
|
360
|
-
TType extends TUnion["type"],
|
|
361
|
-
> = Extract<TUnion, { readonly type: TType }>;
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Constrains `T` to only contain keys not present in `Existing`.
|
|
365
|
-
*
|
|
366
|
-
* Use as a generic constraint to prevent overriding existing properties.
|
|
367
|
-
*/
|
|
368
|
-
export type NewKeys<T, Existing> = {
|
|
369
|
-
[K in keyof T]: K extends keyof Existing ? never : T[K];
|
|
370
|
-
};
|