@evolu/common 8.0.0-next.5 → 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 +39 -44
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +38 -31
- 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 +3 -3
- package/dist/src/Error.d.ts.map +1 -1
- 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 +39 -45
- 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 +3428 -1629
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +1876 -1279
- package/dist/src/Test.d.ts +17 -124
- 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 -100
- 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 +107 -130
- 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 +85 -42
- 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 +48 -68
- 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 +5 -2
- 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 +142 -137
- package/src/StackTrace.ts +230 -0
- package/src/Store.ts +8 -7
- package/src/String.ts +22 -5
- package/src/Task.ts +5109 -2845
- package/src/Test.ts +20 -167
- package/src/Time.ts +293 -90
- package/src/Type.ts +10034 -4117
- package/src/Types.ts +227 -101
- 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 +161 -190
- 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 +253 -214
- 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,91 +244,81 @@ 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
|
);
|
|
328
323
|
const runWithSqlite = disposer.use(run.create({ ...run.deps, sqlite }));
|
|
329
324
|
const disposables = disposer.move();
|
|
@@ -394,23 +389,21 @@ export const createPreparedStatementsCache = <P>(
|
|
|
394
389
|
}
|
|
395
390
|
});
|
|
396
391
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
}
|
|
409
|
-
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
|
+
},
|
|
410
404
|
},
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
};
|
|
405
|
+
disposer,
|
|
406
|
+
);
|
|
414
407
|
};
|
|
415
408
|
|
|
416
409
|
/** A double-quoted SQL identifier for safe column or table name interpolation. */
|
|
@@ -439,27 +432,32 @@ export type SqlTemplateParam = SqliteValue | SqlIdentifier | RawSql;
|
|
|
439
432
|
* ### Example
|
|
440
433
|
*
|
|
441
434
|
* ```ts
|
|
435
|
+
* import { sql } from "@evolu/common";
|
|
436
|
+
*
|
|
442
437
|
* const id = 42;
|
|
443
438
|
* const name = "Alice";
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
* from users
|
|
448
|
-
* where id = ${id} and name = ${name};
|
|
449
|
-
* `);
|
|
450
|
-
*
|
|
451
|
-
* // For identifiers
|
|
439
|
+
* const select = sql`
|
|
440
|
+
* select * from users where id = ${id} and name = ${name};
|
|
441
|
+
* `;
|
|
452
442
|
* const tableName = "users";
|
|
453
|
-
*
|
|
443
|
+
* const createTable = sql`
|
|
454
444
|
* create table ${sql.identifier(tableName)} (
|
|
455
445
|
* "id" text primary key,
|
|
456
446
|
* "name" text not null
|
|
457
447
|
* );
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
* // For raw SQL (use with caution)
|
|
448
|
+
* `;
|
|
449
|
+
* // Use sql.raw only with trusted SQL fragments.
|
|
461
450
|
* const orderBy = "created_at desc";
|
|
462
|
-
*
|
|
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
|
+
* );
|
|
463
461
|
* ```
|
|
464
462
|
*
|
|
465
463
|
* ## TIP
|
|
@@ -515,7 +513,10 @@ sql.prepared = (
|
|
|
515
513
|
};
|
|
516
514
|
|
|
517
515
|
/** Index metadata stored in `sqlite_master` for a {@link Sqlite} database. */
|
|
518
|
-
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 });
|
|
519
520
|
export interface SqliteIndex extends InferType<typeof SqliteIndex> {}
|
|
520
521
|
|
|
521
522
|
/** {@link Eq} instance for {@link SqliteIndex}. */
|
|
@@ -529,7 +530,10 @@ export const eqSqliteIndex: Eq<SqliteIndex> = /*#__PURE__*/ createEqObject({
|
|
|
529
530
|
*
|
|
530
531
|
* Includes table-column mappings and user-visible indexes.
|
|
531
532
|
*/
|
|
532
|
-
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({
|
|
533
537
|
tables: /*#__PURE__*/ record(String, /*#__PURE__*/ set(String)),
|
|
534
538
|
indexes: /*#__PURE__*/ array(SqliteIndex),
|
|
535
539
|
});
|
|
@@ -547,7 +551,7 @@ export const getSqliteSchema =
|
|
|
547
551
|
*/
|
|
548
552
|
excludeIndexNamePrefix?: string;
|
|
549
553
|
} = {}): SqliteSchema => {
|
|
550
|
-
const tables =
|
|
554
|
+
const tables = createMutableRecord<string, Set<string>>();
|
|
551
555
|
|
|
552
556
|
const tableAndColumnInfoRows = deps.sqlite.exec<{
|
|
553
557
|
tableName: string;
|
|
@@ -570,6 +574,7 @@ export const getSqliteSchema =
|
|
|
570
574
|
? ` and name not like '${excludeIndexNamePrefix.replaceAll("'", "''")}%'`
|
|
571
575
|
: "";
|
|
572
576
|
|
|
577
|
+
// prettier-ignore
|
|
573
578
|
const indexesRows = deps.sqlite.exec<{ name: string; sql: string }>(
|
|
574
579
|
sql`
|
|
575
580
|
select name, sql
|
|
@@ -581,19 +586,17 @@ export const getSqliteSchema =
|
|
|
581
586
|
`,
|
|
582
587
|
);
|
|
583
588
|
|
|
584
|
-
const indexes = indexesRows.rows.map(
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
}),
|
|
596
|
-
);
|
|
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
|
+
}));
|
|
597
600
|
|
|
598
601
|
return { tables, indexes };
|
|
599
602
|
};
|
|
@@ -649,7 +652,7 @@ export const getSqliteSnapshot = (deps: SqliteDep): SqliteSnapshot => {
|
|
|
649
652
|
* converting between JavaScript booleans and SQLite boolean values.
|
|
650
653
|
*/
|
|
651
654
|
export const SqliteBoolean = /*#__PURE__*/ union(0, 1);
|
|
652
|
-
export type SqliteBoolean = typeof SqliteBoolean.
|
|
655
|
+
export type SqliteBoolean = typeof SqliteBoolean.Output;
|
|
653
656
|
|
|
654
657
|
/**
|
|
655
658
|
* Represents the {@link SqliteBoolean} value for `true`.
|
|
@@ -671,8 +674,9 @@ export const sqliteFalse = 0;
|
|
|
671
674
|
* ### Example
|
|
672
675
|
*
|
|
673
676
|
* ```ts
|
|
674
|
-
*
|
|
675
|
-
*
|
|
677
|
+
* import { booleanToSqliteBoolean } from "@evolu/common";
|
|
678
|
+
*
|
|
679
|
+
* expect(booleanToSqliteBoolean(true)).toBe(1);
|
|
676
680
|
* ```
|
|
677
681
|
*/
|
|
678
682
|
export const booleanToSqliteBoolean = (value: boolean): SqliteBoolean =>
|
|
@@ -684,8 +688,9 @@ export const booleanToSqliteBoolean = (value: boolean): SqliteBoolean =>
|
|
|
684
688
|
* ### Example
|
|
685
689
|
*
|
|
686
690
|
* ```ts
|
|
687
|
-
*
|
|
688
|
-
*
|
|
691
|
+
* import { sqliteBooleanToBoolean } from "@evolu/common";
|
|
692
|
+
*
|
|
693
|
+
* expect(sqliteBooleanToBoolean(1)).toBe(true);
|
|
689
694
|
* ```
|
|
690
695
|
*/
|
|
691
696
|
export const sqliteBooleanToBoolean = (value: SqliteBoolean): boolean =>
|