@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/Set.ts
CHANGED
|
@@ -8,33 +8,38 @@
|
|
|
8
8
|
* Use {@link isNonEmptySet} to narrow to {@link NonEmptyReadonlySet} before
|
|
9
9
|
* calling functions like {@link firstInSet} that require a non-empty set.
|
|
10
10
|
*
|
|
11
|
-
* ###
|
|
11
|
+
* ### Composing immutable and native operations
|
|
12
12
|
*
|
|
13
13
|
* ```ts
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* const
|
|
24
|
-
* const
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* const
|
|
14
|
+
* import {
|
|
15
|
+
* addToSet,
|
|
16
|
+
* deleteFromSet,
|
|
17
|
+
* filterSet,
|
|
18
|
+
* firstInSet,
|
|
19
|
+
* isNonEmptySet,
|
|
20
|
+
* mapSet,
|
|
21
|
+
* } from "@evolu/common";
|
|
22
|
+
*
|
|
23
|
+
* const values = addToSet(new Set([1, 2]), 3);
|
|
24
|
+
* const withoutOne = deleteFromSet(values, 1);
|
|
25
|
+
* const doubled = mapSet(withoutOne, (x) => x * 2);
|
|
26
|
+
* const atLeastFour = filterSet(doubled, (x) => x >= 4);
|
|
27
|
+
*
|
|
28
|
+
* // Evolu's readonly results compose with native Set operations.
|
|
29
|
+
* const union = atLeastFour.union(new Set([6, 8]));
|
|
30
|
+
* const intersection = union.intersection(new Set([4, 6, 8, 10]));
|
|
31
|
+
* const difference = intersection.difference(new Set([4]));
|
|
32
|
+
*
|
|
33
|
+
* if (!isNonEmptySet(difference)) throw new Error("Expected values");
|
|
34
|
+
* expect(firstInSet(difference)).toBe(6);
|
|
30
35
|
* ```
|
|
31
36
|
*
|
|
32
37
|
* @module
|
|
33
38
|
*/
|
|
34
39
|
|
|
35
|
-
import type { NonEmptyReadonlyArray } from "./Array.
|
|
36
|
-
import type { Brand } from "./Brand.
|
|
37
|
-
import type { PredicateWithIndex, RefinementWithIndex } from "./Types.
|
|
40
|
+
import type { NonEmptyReadonlyArray } from "./Array.ts";
|
|
41
|
+
import type { Brand } from "./Brand.ts";
|
|
42
|
+
import type { PredicateWithIndex, RefinementWithIndex } from "./Types.ts";
|
|
38
43
|
|
|
39
44
|
/**
|
|
40
45
|
* An empty readonly set.
|
|
@@ -50,11 +55,17 @@ export const emptySet: ReadonlySet<never> = /*#__PURE__*/ new Set();
|
|
|
50
55
|
*
|
|
51
56
|
* Preserves non-empty type when the input array is non-empty.
|
|
52
57
|
*
|
|
53
|
-
* ###
|
|
58
|
+
* ### Preserving non-empty inputs
|
|
54
59
|
*
|
|
55
60
|
* ```ts
|
|
56
|
-
* createSet
|
|
57
|
-
*
|
|
61
|
+
* import { createSet, type NonEmptyReadonlySet } from "@evolu/common";
|
|
62
|
+
*
|
|
63
|
+
* const values = createSet([1, 2, 3]);
|
|
64
|
+
* const empty = createSet([] as ReadonlyArray<number>);
|
|
65
|
+
*
|
|
66
|
+
* expectTypeOf(values).toEqualTypeOf<NonEmptyReadonlySet<number>>();
|
|
67
|
+
* expectTypeOf(empty).toEqualTypeOf<ReadonlySet<number>>();
|
|
68
|
+
* expect(values).toEqual(new Set([1, 2, 3]));
|
|
58
69
|
* ```
|
|
59
70
|
*
|
|
60
71
|
* @group Constructors
|
|
@@ -94,13 +105,16 @@ export type NonEmptyReadonlySet<T> = ReadonlySet<T> & Brand<"NonEmpty">;
|
|
|
94
105
|
* negated guard is better than `.size === 0` for early returns because
|
|
95
106
|
* TypeScript narrows the type after the check.
|
|
96
107
|
*
|
|
97
|
-
* ###
|
|
108
|
+
* ### Narrowing before access
|
|
98
109
|
*
|
|
99
110
|
* ```ts
|
|
111
|
+
* import { isNonEmptySet, type NonEmptyReadonlySet } from "@evolu/common";
|
|
112
|
+
*
|
|
100
113
|
* const set: ReadonlySet<number> = new Set([1, 2, 3]);
|
|
101
|
-
* if (isNonEmptySet(set))
|
|
102
|
-
*
|
|
103
|
-
*
|
|
114
|
+
* if (!isNonEmptySet(set)) throw new Error("Expected a non-empty set");
|
|
115
|
+
*
|
|
116
|
+
* expectTypeOf(set).toEqualTypeOf<NonEmptyReadonlySet<number>>();
|
|
117
|
+
* expect(set.size).toBe(3);
|
|
104
118
|
* ```
|
|
105
119
|
*
|
|
106
120
|
* @group Type guards
|
|
@@ -115,11 +129,18 @@ export const isNonEmptySet = <T>(
|
|
|
115
129
|
* If the item already exists, returns a new set with the same elements (still a
|
|
116
130
|
* new reference for change detection).
|
|
117
131
|
*
|
|
118
|
-
* ###
|
|
132
|
+
* ### Adding without mutation
|
|
119
133
|
*
|
|
120
134
|
* ```ts
|
|
121
|
-
*
|
|
122
|
-
*
|
|
135
|
+
* import { addToSet, type NonEmptyReadonlySet } from "@evolu/common";
|
|
136
|
+
*
|
|
137
|
+
* const original: ReadonlySet<number> = new Set([1, 2]);
|
|
138
|
+
* const added = addToSet(original, 3);
|
|
139
|
+
* const unchanged = addToSet(original, 2);
|
|
140
|
+
*
|
|
141
|
+
* expectTypeOf(added).toEqualTypeOf<NonEmptyReadonlySet<number>>();
|
|
142
|
+
* expect(added).toEqual(new Set([1, 2, 3]));
|
|
143
|
+
* expect(unchanged).not.toBe(original);
|
|
123
144
|
* ```
|
|
124
145
|
*
|
|
125
146
|
* @group Transformations
|
|
@@ -139,11 +160,14 @@ export const addToSet = <T>(
|
|
|
139
160
|
* If the item doesn't exist, returns a new set with the same elements (still a
|
|
140
161
|
* new reference for change detection).
|
|
141
162
|
*
|
|
142
|
-
* ###
|
|
163
|
+
* ### Deleting without mutation
|
|
143
164
|
*
|
|
144
165
|
* ```ts
|
|
145
|
-
* deleteFromSet
|
|
146
|
-
*
|
|
166
|
+
* import { deleteFromSet } from "@evolu/common";
|
|
167
|
+
*
|
|
168
|
+
* const original = new Set([1, 2, 3]);
|
|
169
|
+
* expect(deleteFromSet(original, 2)).toEqual(new Set([1, 3]));
|
|
170
|
+
* expect(deleteFromSet(original, 5)).not.toBe(original);
|
|
147
171
|
* ```
|
|
148
172
|
*
|
|
149
173
|
* @group Transformations
|
|
@@ -165,11 +189,22 @@ export const deleteFromSet = <T>(
|
|
|
165
189
|
* Note: If the mapper produces duplicate values, the resulting set will have
|
|
166
190
|
* fewer elements.
|
|
167
191
|
*
|
|
168
|
-
* ###
|
|
192
|
+
* ### Mapping non-empty sets
|
|
169
193
|
*
|
|
170
194
|
* ```ts
|
|
171
|
-
*
|
|
172
|
-
*
|
|
195
|
+
* import {
|
|
196
|
+
* createSet,
|
|
197
|
+
* mapSet,
|
|
198
|
+
* type NonEmptyReadonlySet,
|
|
199
|
+
* } from "@evolu/common";
|
|
200
|
+
*
|
|
201
|
+
* const original = createSet([1, 2, 3]);
|
|
202
|
+
* const doubled = mapSet(original, (x) => x * 2);
|
|
203
|
+
* const parity = mapSet(original, (x) => x % 2);
|
|
204
|
+
*
|
|
205
|
+
* expectTypeOf(doubled).toEqualTypeOf<NonEmptyReadonlySet<number>>();
|
|
206
|
+
* expect(doubled).toEqual(new Set([2, 4, 6]));
|
|
207
|
+
* expect(parity).toEqual(new Set([1, 0]));
|
|
173
208
|
* ```
|
|
174
209
|
*
|
|
175
210
|
* @group Transformations
|
|
@@ -201,10 +236,21 @@ export function mapSet<T, U>(
|
|
|
201
236
|
* When used with a refinement function (with `value is Type` syntax),
|
|
202
237
|
* TypeScript will narrow the result type.
|
|
203
238
|
*
|
|
204
|
-
* ###
|
|
239
|
+
* ### Filtering and refining
|
|
205
240
|
*
|
|
206
241
|
* ```ts
|
|
207
|
-
*
|
|
242
|
+
* import { filterSet } from "@evolu/common";
|
|
243
|
+
*
|
|
244
|
+
* const evens = filterSet(new Set([1, 2, 3, 4, 5]), (x) => x % 2 === 0);
|
|
245
|
+
* expect(evens).toEqual(new Set([2, 4]));
|
|
246
|
+
*
|
|
247
|
+
* const mixed: ReadonlySet<string | number> = new Set([1, "a", 2, "b"]);
|
|
248
|
+
* const strings = filterSet(
|
|
249
|
+
* mixed,
|
|
250
|
+
* (value): value is string => typeof value === "string",
|
|
251
|
+
* );
|
|
252
|
+
* expectTypeOf(strings).toEqualTypeOf<ReadonlySet<string>>();
|
|
253
|
+
* expect(strings).toEqual(new Set(["a", "b"]));
|
|
208
254
|
* ```
|
|
209
255
|
*
|
|
210
256
|
* @group Transformations
|
|
@@ -235,10 +281,13 @@ export function filterSet<T>(
|
|
|
235
281
|
/**
|
|
236
282
|
* Returns the first element of a non-empty set (by insertion order).
|
|
237
283
|
*
|
|
238
|
-
* ###
|
|
284
|
+
* ### Reading the first value
|
|
239
285
|
*
|
|
240
286
|
* ```ts
|
|
241
|
-
*
|
|
287
|
+
* import { createSet, firstInSet } from "@evolu/common";
|
|
288
|
+
*
|
|
289
|
+
* const set = createSet(["a", "b", "c"]);
|
|
290
|
+
* expect(firstInSet(set)).toBe("a");
|
|
242
291
|
* ```
|
|
243
292
|
*
|
|
244
293
|
* @group Accessors
|
package/src/Sqlite.ts
CHANGED
|
@@ -4,30 +4,35 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import type {
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import type { Result } from "./Result.
|
|
15
|
-
import { ok } from "./Result.
|
|
16
|
-
import
|
|
17
|
-
import { testCreateRun, type TestDeps } from "./Test.js";
|
|
18
|
-
import type { InferType, Name, Typed } from "./Type.js";
|
|
7
|
+
import type { Brand } from "./Brand.ts";
|
|
8
|
+
import { bytesToHex, hexToBytes } from "./Buffer.ts";
|
|
9
|
+
import type { EncryptionKey } from "./Crypto.ts";
|
|
10
|
+
import type { Eq } from "./Eq.ts";
|
|
11
|
+
import { createEqObject, eqArrayNumber, eqString } from "./Eq.ts";
|
|
12
|
+
import { disposable } from "./Function.ts";
|
|
13
|
+
import { createMutableRecord, objectToEntries } from "./Object.ts";
|
|
14
|
+
import type { Result } from "./Result.ts";
|
|
15
|
+
import { ok } from "./Result.ts";
|
|
16
|
+
import { testCreateRun, type Task, type TestRunDep } from "./Task.ts";
|
|
19
17
|
import {
|
|
20
18
|
array,
|
|
19
|
+
type ArrayType,
|
|
20
|
+
type InferType,
|
|
21
|
+
type Name,
|
|
21
22
|
Null,
|
|
22
23
|
Number,
|
|
23
24
|
object,
|
|
25
|
+
type ObjectType,
|
|
24
26
|
record,
|
|
27
|
+
type RecordType,
|
|
25
28
|
set,
|
|
29
|
+
type SetType,
|
|
26
30
|
String,
|
|
27
31
|
testName,
|
|
32
|
+
type Typed,
|
|
28
33
|
Uint8Array,
|
|
29
34
|
union,
|
|
30
|
-
} from "./Type.
|
|
35
|
+
} from "./Type.ts";
|
|
31
36
|
|
|
32
37
|
/**
|
|
33
38
|
* Platform-agnostic SQLite wrapping a {@link SqliteDriver}.
|
|
@@ -96,7 +101,7 @@ export const SqliteValue = /*#__PURE__*/ union(
|
|
|
96
101
|
Number,
|
|
97
102
|
Uint8Array,
|
|
98
103
|
);
|
|
99
|
-
export type SqliteValue = typeof SqliteValue.
|
|
104
|
+
export type SqliteValue = typeof SqliteValue.Output;
|
|
100
105
|
|
|
101
106
|
/** Equality comparison for {@link SqliteValue}. */
|
|
102
107
|
export const eqSqliteValue: Eq<SqliteValue> = (x, y) =>
|
|
@@ -239,96 +244,87 @@ export const createSqlite =
|
|
|
239
244
|
const console = run.deps.console.child("sql");
|
|
240
245
|
await using disposer = new AsyncDisposableStack();
|
|
241
246
|
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
247
|
+
const driver = disposer.use(
|
|
248
|
+
await run.ok(createSqliteDriver(name, options)),
|
|
249
|
+
);
|
|
245
250
|
console.debug("SQLite driver created");
|
|
246
251
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
},
|
|
306
|
-
|
|
307
|
-
[Symbol.asyncDispose]: () => disposables.disposeAsync(),
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
return ok(sqlite);
|
|
252
|
+
return ok(
|
|
253
|
+
disposable<Sqlite>(
|
|
254
|
+
{
|
|
255
|
+
exec: <R extends SqliteRow = SqliteRow>(query: SqliteQuery) => {
|
|
256
|
+
console.debug({ query });
|
|
257
|
+
|
|
258
|
+
const label =
|
|
259
|
+
query.options?.logQueryExecutionTime &&
|
|
260
|
+
`SqliteQueryExecutionTime ${query.sql}`;
|
|
261
|
+
|
|
262
|
+
if (label) console.time(label);
|
|
263
|
+
const result = driver.exec(query);
|
|
264
|
+
if (label) console.timeEnd(label);
|
|
265
|
+
|
|
266
|
+
if (query.options?.logExplainQueryPlan) {
|
|
267
|
+
const result = driver.exec({
|
|
268
|
+
...query,
|
|
269
|
+
sql: `EXPLAIN QUERY PLAN ${query.sql}` as SafeSql,
|
|
270
|
+
});
|
|
271
|
+
console.log("[logExplainQueryPlan]", query);
|
|
272
|
+
console.log(
|
|
273
|
+
drawSqliteQueryPlan(
|
|
274
|
+
result.rows as unknown as Array<SqliteQueryPlanRow>,
|
|
275
|
+
),
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
console.debug({ result });
|
|
280
|
+
return result as SqliteExecResult<R>;
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
transaction: ((callback: () => Result<unknown, unknown> | void) => {
|
|
284
|
+
console.debug("begin");
|
|
285
|
+
driver.exec(sql`begin;`);
|
|
286
|
+
|
|
287
|
+
using rollback = new DisposableStack();
|
|
288
|
+
let shouldRollback = true;
|
|
289
|
+
rollback.defer(() => {
|
|
290
|
+
if (!shouldRollback) return;
|
|
291
|
+
console.debug("rollback");
|
|
292
|
+
driver.exec(sql`rollback;`);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const result = callback();
|
|
296
|
+
if (result != null && !result.ok) return result;
|
|
297
|
+
|
|
298
|
+
console.debug("commit");
|
|
299
|
+
driver.exec(sql`commit;`);
|
|
300
|
+
shouldRollback = false;
|
|
301
|
+
|
|
302
|
+
return result;
|
|
303
|
+
}) as SqliteTransaction,
|
|
304
|
+
|
|
305
|
+
export: () => driver.export(),
|
|
306
|
+
},
|
|
307
|
+
disposer,
|
|
308
|
+
),
|
|
309
|
+
);
|
|
311
310
|
};
|
|
312
311
|
|
|
313
312
|
/** Creates a test setup with a in-memory {@link Sqlite}. */
|
|
314
313
|
export const testSetupSqlite = async (
|
|
315
314
|
deps: CreateSqliteDriverDep,
|
|
316
315
|
): Promise<
|
|
317
|
-
AsyncDisposable &
|
|
318
|
-
SqliteDep & {
|
|
319
|
-
readonly run: Run<TestDeps & CreateSqliteDriverDep & SqliteDep>;
|
|
320
|
-
readonly sqlite: Sqlite;
|
|
321
|
-
}
|
|
316
|
+
AsyncDisposable & SqliteDep & TestRunDep<CreateSqliteDriverDep & SqliteDep>
|
|
322
317
|
> => {
|
|
323
318
|
await using disposer = new AsyncDisposableStack();
|
|
324
319
|
const run = disposer.use(testCreateRun(deps));
|
|
325
320
|
const sqlite = disposer.use(
|
|
326
|
-
await run.
|
|
321
|
+
await run.ok(createSqlite(testName, { mode: "memory" })),
|
|
327
322
|
);
|
|
323
|
+
const runWithSqlite = disposer.use(run.create({ ...run.deps, sqlite }));
|
|
328
324
|
const disposables = disposer.move();
|
|
329
325
|
|
|
330
326
|
return {
|
|
331
|
-
run:
|
|
327
|
+
run: runWithSqlite,
|
|
332
328
|
sqlite,
|
|
333
329
|
[Symbol.asyncDispose]: () => disposables.disposeAsync(),
|
|
334
330
|
};
|
|
@@ -393,23 +389,21 @@ export const createPreparedStatementsCache = <P>(
|
|
|
393
389
|
}
|
|
394
390
|
});
|
|
395
391
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
return statement;
|
|
392
|
+
return disposable<PreparedStatements<P>>(
|
|
393
|
+
{
|
|
394
|
+
get: (query, alwaysPrepare) => {
|
|
395
|
+
if (alwaysPrepare !== true && !query.options?.prepare)
|
|
396
|
+
return null as never;
|
|
397
|
+
let statement = statementsBySql.get(query.sql);
|
|
398
|
+
if (!statement) {
|
|
399
|
+
statement = factory(query.sql);
|
|
400
|
+
statementsBySql.set(query.sql, statement);
|
|
401
|
+
}
|
|
402
|
+
return statement;
|
|
403
|
+
},
|
|
409
404
|
},
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
};
|
|
405
|
+
disposer,
|
|
406
|
+
);
|
|
413
407
|
};
|
|
414
408
|
|
|
415
409
|
/** A double-quoted SQL identifier for safe column or table name interpolation. */
|
|
@@ -438,27 +432,32 @@ export type SqlTemplateParam = SqliteValue | SqlIdentifier | RawSql;
|
|
|
438
432
|
* ### Example
|
|
439
433
|
*
|
|
440
434
|
* ```ts
|
|
435
|
+
* import { sql } from "@evolu/common";
|
|
436
|
+
*
|
|
441
437
|
* const id = 42;
|
|
442
438
|
* const name = "Alice";
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
* from users
|
|
447
|
-
* where id = ${id} and name = ${name};
|
|
448
|
-
* `);
|
|
449
|
-
*
|
|
450
|
-
* // For identifiers
|
|
439
|
+
* const select = sql`
|
|
440
|
+
* select * from users where id = ${id} and name = ${name};
|
|
441
|
+
* `;
|
|
451
442
|
* const tableName = "users";
|
|
452
|
-
*
|
|
443
|
+
* const createTable = sql`
|
|
453
444
|
* create table ${sql.identifier(tableName)} (
|
|
454
445
|
* "id" text primary key,
|
|
455
446
|
* "name" text not null
|
|
456
447
|
* );
|
|
457
|
-
*
|
|
458
|
-
*
|
|
459
|
-
* // For raw SQL (use with caution)
|
|
448
|
+
* `;
|
|
449
|
+
* // Use sql.raw only with trusted SQL fragments.
|
|
460
450
|
* const orderBy = "created_at desc";
|
|
461
|
-
*
|
|
451
|
+
* const ordered = sql`select * from users order by ${sql.raw(orderBy)};`;
|
|
452
|
+
*
|
|
453
|
+
* expect(select).toEqual({
|
|
454
|
+
* sql: "select * from users where id = ? and name = ?;",
|
|
455
|
+
* parameters: [42, "Alice"],
|
|
456
|
+
* });
|
|
457
|
+
* expect(createTable.sql).toContain('create table "users"');
|
|
458
|
+
* expect(ordered.sql).toBe(
|
|
459
|
+
* "select * from users order by created_at desc;",
|
|
460
|
+
* );
|
|
462
461
|
* ```
|
|
463
462
|
*
|
|
464
463
|
* ## TIP
|
|
@@ -514,7 +513,10 @@ sql.prepared = (
|
|
|
514
513
|
};
|
|
515
514
|
|
|
516
515
|
/** Index metadata stored in `sqlite_master` for a {@link Sqlite} database. */
|
|
517
|
-
export const SqliteIndex
|
|
516
|
+
export const SqliteIndex: ObjectType<{
|
|
517
|
+
readonly name: typeof String;
|
|
518
|
+
readonly sql: typeof String;
|
|
519
|
+
}> = /*#__PURE__*/ object({ name: String, sql: String });
|
|
518
520
|
export interface SqliteIndex extends InferType<typeof SqliteIndex> {}
|
|
519
521
|
|
|
520
522
|
/** {@link Eq} instance for {@link SqliteIndex}. */
|
|
@@ -528,7 +530,10 @@ export const eqSqliteIndex: Eq<SqliteIndex> = /*#__PURE__*/ createEqObject({
|
|
|
528
530
|
*
|
|
529
531
|
* Includes table-column mappings and user-visible indexes.
|
|
530
532
|
*/
|
|
531
|
-
export const SqliteSchema
|
|
533
|
+
export const SqliteSchema: ObjectType<{
|
|
534
|
+
readonly tables: RecordType<typeof String, SetType<typeof String>>;
|
|
535
|
+
readonly indexes: ArrayType<typeof SqliteIndex>;
|
|
536
|
+
}> = /*#__PURE__*/ object({
|
|
532
537
|
tables: /*#__PURE__*/ record(String, /*#__PURE__*/ set(String)),
|
|
533
538
|
indexes: /*#__PURE__*/ array(SqliteIndex),
|
|
534
539
|
});
|
|
@@ -546,7 +551,7 @@ export const getSqliteSchema =
|
|
|
546
551
|
*/
|
|
547
552
|
excludeIndexNamePrefix?: string;
|
|
548
553
|
} = {}): SqliteSchema => {
|
|
549
|
-
const tables =
|
|
554
|
+
const tables = createMutableRecord<string, Set<string>>();
|
|
550
555
|
|
|
551
556
|
const tableAndColumnInfoRows = deps.sqlite.exec<{
|
|
552
557
|
tableName: string;
|
|
@@ -569,6 +574,7 @@ export const getSqliteSchema =
|
|
|
569
574
|
? ` and name not like '${excludeIndexNamePrefix.replaceAll("'", "''")}%'`
|
|
570
575
|
: "";
|
|
571
576
|
|
|
577
|
+
// prettier-ignore
|
|
572
578
|
const indexesRows = deps.sqlite.exec<{ name: string; sql: string }>(
|
|
573
579
|
sql`
|
|
574
580
|
select name, sql
|
|
@@ -580,19 +586,17 @@ export const getSqliteSchema =
|
|
|
580
586
|
`,
|
|
581
587
|
);
|
|
582
588
|
|
|
583
|
-
const indexes = indexesRows.rows.map(
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}),
|
|
595
|
-
);
|
|
589
|
+
const indexes = indexesRows.rows.map((row): SqliteIndex => ({
|
|
590
|
+
name: row.name,
|
|
591
|
+
/**
|
|
592
|
+
* SQLite returns "CREATE INDEX" for "create index" for some reason. Other
|
|
593
|
+
* keywords remain unchanged. We have to normalize the casing for schema
|
|
594
|
+
* comparison manually.
|
|
595
|
+
*/
|
|
596
|
+
sql: row.sql
|
|
597
|
+
.replace("CREATE INDEX", "create index")
|
|
598
|
+
.replace("CREATE UNIQUE INDEX", "create unique index"),
|
|
599
|
+
}));
|
|
596
600
|
|
|
597
601
|
return { tables, indexes };
|
|
598
602
|
};
|
|
@@ -648,7 +652,7 @@ export const getSqliteSnapshot = (deps: SqliteDep): SqliteSnapshot => {
|
|
|
648
652
|
* converting between JavaScript booleans and SQLite boolean values.
|
|
649
653
|
*/
|
|
650
654
|
export const SqliteBoolean = /*#__PURE__*/ union(0, 1);
|
|
651
|
-
export type SqliteBoolean = typeof SqliteBoolean.
|
|
655
|
+
export type SqliteBoolean = typeof SqliteBoolean.Output;
|
|
652
656
|
|
|
653
657
|
/**
|
|
654
658
|
* Represents the {@link SqliteBoolean} value for `true`.
|
|
@@ -670,8 +674,9 @@ export const sqliteFalse = 0;
|
|
|
670
674
|
* ### Example
|
|
671
675
|
*
|
|
672
676
|
* ```ts
|
|
673
|
-
*
|
|
674
|
-
*
|
|
677
|
+
* import { booleanToSqliteBoolean } from "@evolu/common";
|
|
678
|
+
*
|
|
679
|
+
* expect(booleanToSqliteBoolean(true)).toBe(1);
|
|
675
680
|
* ```
|
|
676
681
|
*/
|
|
677
682
|
export const booleanToSqliteBoolean = (value: boolean): SqliteBoolean =>
|
|
@@ -683,8 +688,9 @@ export const booleanToSqliteBoolean = (value: boolean): SqliteBoolean =>
|
|
|
683
688
|
* ### Example
|
|
684
689
|
*
|
|
685
690
|
* ```ts
|
|
686
|
-
*
|
|
687
|
-
*
|
|
691
|
+
* import { sqliteBooleanToBoolean } from "@evolu/common";
|
|
692
|
+
*
|
|
693
|
+
* expect(sqliteBooleanToBoolean(1)).toBe(true);
|
|
688
694
|
* ```
|
|
689
695
|
*/
|
|
690
696
|
export const sqliteBooleanToBoolean = (value: SqliteBoolean): boolean =>
|