@evolu/common 6.0.1-preview.32 → 6.0.1-preview.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/src/Array.d.ts +193 -41
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +155 -48
- package/dist/src/Crypto.d.ts +6 -8
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +9 -9
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +4 -4
- package/dist/src/Evolu/Evolu.d.ts +9 -4
- package/dist/src/Evolu/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu/Evolu.js +11 -11
- package/dist/src/Evolu/Protocol.d.ts +24 -1
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +59 -30
- package/dist/src/Evolu/Query.d.ts +1 -1
- package/dist/src/Evolu/Query.d.ts.map +1 -1
- package/dist/src/Evolu/Query.js +1 -1
- package/dist/src/Evolu/Schema.d.ts +24 -20
- package/dist/src/Evolu/Schema.d.ts.map +1 -1
- package/dist/src/Evolu/Schema.js +36 -27
- package/dist/src/Evolu/Storage.d.ts +10 -2
- package/dist/src/Evolu/Storage.d.ts.map +1 -1
- package/dist/src/Evolu/Storage.js +18 -4
- package/dist/src/Evolu/Sync.d.ts +2 -1
- package/dist/src/Evolu/Sync.d.ts.map +1 -1
- package/dist/src/Evolu/Sync.js +45 -33
- package/dist/src/Evolu/Timestamp.d.ts +12 -17
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.js +5 -19
- package/dist/src/Object.d.ts +10 -4
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +9 -3
- package/dist/src/Type.d.ts +57 -11
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +58 -28
- package/package.json +2 -2
- package/src/Array.ts +213 -55
- package/src/Crypto.ts +11 -9
- package/src/Evolu/Db.ts +7 -7
- package/src/Evolu/Evolu.ts +21 -20
- package/src/Evolu/Protocol.ts +70 -35
- package/src/Evolu/Query.ts +2 -2
- package/src/Evolu/Schema.ts +44 -32
- package/src/Evolu/Storage.ts +40 -2
- package/src/Evolu/Sync.ts +54 -32
- package/src/Evolu/Timestamp.ts +7 -28
- package/src/Object.ts +13 -5
- package/src/Type.ts +58 -32
package/src/Array.ts
CHANGED
|
@@ -1,44 +1,121 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 🔒 Immutable, type-safe array
|
|
2
|
+
* 🔒 Immutable, type-safe array helpers
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* methods return mutable arrays even when called on readonly arrays. These
|
|
6
|
-
* helpers ensure transformations return readonly types.
|
|
4
|
+
* Array types, guards, operations, transformations, accessors, and mutations.
|
|
7
5
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* Prepared for TC39 Hack pipes:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* // Problem: nested functions can be hard to follow
|
|
10
|
+
* const result = firstInArray(
|
|
11
|
+
* mapArray(dedupeArray(appendToArray(value, 2)), (x) => x * 2),
|
|
12
|
+
* );
|
|
13
|
+
*
|
|
14
|
+
* // Ideal solution: TC39 Hack pipes (when available)
|
|
15
|
+
* // const result = value
|
|
16
|
+
* // |> appendToArray(%, 2)
|
|
17
|
+
* // |> dedupeArray(%)
|
|
18
|
+
* // |> mapArray(%, (x) => x * 2)
|
|
19
|
+
* // |> firstInArray(%);
|
|
20
|
+
*
|
|
21
|
+
* // Current solution: name each step (or use p1, p2 if lazy)
|
|
22
|
+
* const p1 = appendToArray(value, 2);
|
|
23
|
+
* const p2 = dedupeArray(p1);
|
|
24
|
+
* const p3 = mapArray(p2, (x) => x * 2);
|
|
25
|
+
* const p4 = firstInArray(p3);
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Of course it's possible to use array instance methods, but they mutate and do
|
|
29
|
+
* not preserve {@link NonEmptyArray} and {@link NonEmptyReadonlyArray} types.
|
|
11
30
|
*
|
|
12
31
|
* ### Example
|
|
13
32
|
*
|
|
14
33
|
* ```ts
|
|
15
|
-
* //
|
|
34
|
+
* // Types - compile-time guarantee of at least one element
|
|
35
|
+
* const _valid: NonEmptyReadonlyArray<number> = [1, 2, 3];
|
|
36
|
+
* // ts-expect-error - empty array is not a valid NonEmptyReadonlyArray
|
|
37
|
+
* const _invalid: NonEmptyReadonlyArray<number> = [];
|
|
38
|
+
*
|
|
39
|
+
* // Guards
|
|
40
|
+
* const arr: ReadonlyArray<number> = [1, 2, 3];
|
|
41
|
+
* if (isNonEmptyReadonlyArray(arr)) {
|
|
42
|
+
* firstInArray(arr);
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* // Operations
|
|
46
|
+
* const appended = appendToArray([1, 2, 3], 4); // [1, 2, 3, 4]
|
|
47
|
+
* const prepended = prependToArray([2, 3], 1); // [1, 2, 3]
|
|
48
|
+
*
|
|
49
|
+
* // Transformations
|
|
16
50
|
* const readonly: ReadonlyArray<number> = [1, 2, 3];
|
|
17
|
-
* const mapped = readonly
|
|
51
|
+
* const mapped = mapArray(readonly, (x) => x * 2); // [2, 4, 6]
|
|
52
|
+
* const filtered = filterArray(readonly, (x) => x > 1); // [2, 3]
|
|
53
|
+
* const deduped = dedupeArray([1, 2, 1, 3, 2]); // [1, 2, 3]
|
|
18
54
|
*
|
|
19
|
-
* //
|
|
20
|
-
* const
|
|
55
|
+
* // Accessors
|
|
56
|
+
* const first = firstInArray(["a", "b", "c"]); // "a"
|
|
57
|
+
* const last = lastInArray(["a", "b", "c"]); // "c"
|
|
21
58
|
*
|
|
22
|
-
* //
|
|
23
|
-
* const
|
|
24
|
-
*
|
|
59
|
+
* // Mutations
|
|
60
|
+
* const mutable: NonEmptyArray<number> = [1, 2, 3];
|
|
61
|
+
* shiftArray(mutable); // 1 (guaranteed to exist)
|
|
62
|
+
* mutable; // [2, 3]
|
|
25
63
|
* ```
|
|
26
64
|
*
|
|
27
65
|
* @module
|
|
28
66
|
*/
|
|
29
67
|
|
|
30
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* An array with at least one element.
|
|
70
|
+
*
|
|
71
|
+
* @category Types
|
|
72
|
+
*/
|
|
31
73
|
export type NonEmptyArray<T> = [T, ...Array<T>];
|
|
32
74
|
|
|
33
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* A readonly array with at least one element.
|
|
77
|
+
*
|
|
78
|
+
* @category Types
|
|
79
|
+
*/
|
|
80
|
+
export type NonEmptyReadonlyArray<T> = readonly [T, ...ReadonlyArray<T>];
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Checks if an array is non-empty and narrows its type to {@link NonEmptyArray}.
|
|
84
|
+
*
|
|
85
|
+
* Use `if (!isNonEmptyArray(arr))` for empty checks.
|
|
86
|
+
*
|
|
87
|
+
* ### Example
|
|
88
|
+
*
|
|
89
|
+
* ```ts
|
|
90
|
+
* const arr: Array<number> = [1, 2, 3];
|
|
91
|
+
* if (isNonEmptyArray(arr)) {
|
|
92
|
+
* firstInArray(arr); // arr is NonEmptyArray<number>
|
|
93
|
+
* }
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @category Type Guards
|
|
97
|
+
*/
|
|
34
98
|
export const isNonEmptyArray = <T>(
|
|
35
99
|
array: Array<T>,
|
|
36
100
|
): array is NonEmptyArray<T> => array.length > 0;
|
|
37
101
|
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Checks if a readonly array is non-empty and narrows its type to
|
|
104
|
+
* {@link NonEmptyReadonlyArray}.
|
|
105
|
+
*
|
|
106
|
+
* Use `if (!isNonEmptyReadonlyArray(arr))` for empty checks.
|
|
107
|
+
*
|
|
108
|
+
* ### Example
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* const arr: ReadonlyArray<number> = [1, 2, 3];
|
|
112
|
+
* if (isNonEmptyReadonlyArray(arr)) {
|
|
113
|
+
* firstInArray(arr); // arr is NonEmptyReadonlyArray<number>
|
|
114
|
+
* }
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* @category Type Guards
|
|
118
|
+
*/
|
|
42
119
|
export const isNonEmptyReadonlyArray = <T>(
|
|
43
120
|
array: ReadonlyArray<T>,
|
|
44
121
|
): array is NonEmptyReadonlyArray<T> => array.length > 0;
|
|
@@ -47,10 +124,18 @@ export const isNonEmptyReadonlyArray = <T>(
|
|
|
47
124
|
* Appends an item to an array, returning a new non-empty readonly array.
|
|
48
125
|
*
|
|
49
126
|
* Accepts both mutable and readonly arrays. Does not mutate the original array.
|
|
127
|
+
*
|
|
128
|
+
* ### Example
|
|
129
|
+
*
|
|
130
|
+
* ```ts
|
|
131
|
+
* appendToArray([1, 2, 3], 4); // [1, 2, 3, 4]
|
|
132
|
+
* ```
|
|
133
|
+
*
|
|
134
|
+
* @category Operations
|
|
50
135
|
*/
|
|
51
136
|
export const appendToArray = <T>(
|
|
52
|
-
item: T,
|
|
53
137
|
array: ReadonlyArray<T>,
|
|
138
|
+
item: T,
|
|
54
139
|
): NonEmptyReadonlyArray<T> =>
|
|
55
140
|
[...array, item] as ReadonlyArray<T> as NonEmptyReadonlyArray<T>;
|
|
56
141
|
|
|
@@ -58,17 +143,33 @@ export const appendToArray = <T>(
|
|
|
58
143
|
* Prepends an item to an array, returning a new non-empty readonly array.
|
|
59
144
|
*
|
|
60
145
|
* Accepts both mutable and readonly arrays. Does not mutate the original array.
|
|
146
|
+
*
|
|
147
|
+
* ### Example
|
|
148
|
+
*
|
|
149
|
+
* ```ts
|
|
150
|
+
* prependToArray([2, 3], 1); // [1, 2, 3]
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* @category Operations
|
|
61
154
|
*/
|
|
62
155
|
export const prependToArray = <T>(
|
|
63
|
-
item: T,
|
|
64
156
|
array: ReadonlyArray<T>,
|
|
157
|
+
item: T,
|
|
65
158
|
): NonEmptyReadonlyArray<T> => [item, ...array] as NonEmptyReadonlyArray<T>;
|
|
66
159
|
|
|
67
160
|
/**
|
|
68
|
-
* Maps an array using a mapper function
|
|
69
|
-
* applicable.
|
|
161
|
+
* Maps an array using a mapper function.
|
|
70
162
|
*
|
|
71
163
|
* Accepts both mutable and readonly arrays. Does not mutate the original array.
|
|
164
|
+
* Preserves non-empty type.
|
|
165
|
+
*
|
|
166
|
+
* ### Example
|
|
167
|
+
*
|
|
168
|
+
* ```ts
|
|
169
|
+
* mapArray([1, 2, 3], (x) => x * 2); // [2, 4, 6]
|
|
170
|
+
* ```
|
|
171
|
+
*
|
|
172
|
+
* @category Transformations
|
|
72
173
|
*/
|
|
73
174
|
export function mapArray<T, U>(
|
|
74
175
|
array: NonEmptyReadonlyArray<T> | NonEmptyArray<T>,
|
|
@@ -89,6 +190,14 @@ export function mapArray<T, U>(
|
|
|
89
190
|
* Filters an array using a predicate function, returning a new readonly array.
|
|
90
191
|
*
|
|
91
192
|
* Accepts both mutable and readonly arrays. Does not mutate the original array.
|
|
193
|
+
*
|
|
194
|
+
* ### Example
|
|
195
|
+
*
|
|
196
|
+
* ```ts
|
|
197
|
+
* filterArray([1, 2, 3, 4, 5], (x) => x % 2 === 0); // [2, 4]
|
|
198
|
+
* ```
|
|
199
|
+
*
|
|
200
|
+
* @category Transformations
|
|
92
201
|
*/
|
|
93
202
|
export const filterArray = <T>(
|
|
94
203
|
array: ReadonlyArray<T>,
|
|
@@ -96,44 +205,45 @@ export const filterArray = <T>(
|
|
|
96
205
|
): ReadonlyArray<T> => array.filter(predicate) as ReadonlyArray<T>;
|
|
97
206
|
|
|
98
207
|
/**
|
|
99
|
-
*
|
|
208
|
+
* Returns a new readonly array with duplicate items removed. If `by` is
|
|
209
|
+
* provided, it will be used to derive the key for uniqueness; otherwise values
|
|
210
|
+
* are used directly. Dedupes by reference equality of values (or extracted keys
|
|
211
|
+
* when `by` is used).
|
|
100
212
|
*
|
|
101
|
-
*
|
|
102
|
-
|
|
103
|
-
export const shiftArray = <T>(array: NonEmptyArray<T>): T => array.shift() as T;
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Returns the first element of a non-empty readonly array.
|
|
213
|
+
* Accepts both mutable and readonly arrays. Does not mutate the original array.
|
|
214
|
+
* Preserves non-empty type.
|
|
107
215
|
*
|
|
108
|
-
*
|
|
109
|
-
*/
|
|
110
|
-
export const firstInArray = <T>(array: NonEmptyReadonlyArray<T>): T => array[0];
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Returns the last element of a non-empty readonly array.
|
|
216
|
+
* ### Example
|
|
114
217
|
*
|
|
115
|
-
*
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
array[array.length - 1];
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Returns a new readonly array with duplicate items removed based on a key
|
|
122
|
-
* extractor function. Preserves the first occurrence of each distinct key.
|
|
218
|
+
* ```ts
|
|
219
|
+
* // Dedupe primitives by value
|
|
220
|
+
* dedupeArray([1, 2, 1, 3, 2]); // [1, 2, 3]
|
|
123
221
|
*
|
|
124
|
-
*
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
*
|
|
129
|
-
*
|
|
222
|
+
* // Dedupe objects by property
|
|
223
|
+
* dedupeArray(
|
|
224
|
+
* [
|
|
225
|
+
* { id: 1, name: "Alice" },
|
|
226
|
+
* { id: 2, name: "Bob" },
|
|
227
|
+
* { id: 1, name: "Alice 2" },
|
|
228
|
+
* ],
|
|
229
|
+
* (item) => item.id,
|
|
230
|
+
* ); // [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]
|
|
231
|
+
* ```
|
|
130
232
|
*
|
|
131
|
-
*
|
|
233
|
+
* @category Transformations
|
|
132
234
|
*/
|
|
133
|
-
export
|
|
235
|
+
export function dedupeArray<T>(
|
|
236
|
+
array: NonEmptyReadonlyArray<T> | NonEmptyArray<T>,
|
|
237
|
+
by?: (item: T) => unknown,
|
|
238
|
+
): NonEmptyReadonlyArray<T>;
|
|
239
|
+
export function dedupeArray<T>(
|
|
240
|
+
array: ReadonlyArray<T> | Array<T>,
|
|
241
|
+
by?: (item: T) => unknown,
|
|
242
|
+
): ReadonlyArray<T>;
|
|
243
|
+
export function dedupeArray<T>(
|
|
134
244
|
array: ReadonlyArray<T>,
|
|
135
245
|
by?: (item: T) => unknown,
|
|
136
|
-
): ReadonlyArray<T>
|
|
246
|
+
): ReadonlyArray<T> {
|
|
137
247
|
if (by == null) {
|
|
138
248
|
return Array.from(new Set(array)) as ReadonlyArray<T>;
|
|
139
249
|
}
|
|
@@ -145,4 +255,52 @@ export const dedupeArray = <T>(
|
|
|
145
255
|
seen.add(key);
|
|
146
256
|
return true;
|
|
147
257
|
}) as ReadonlyArray<T>;
|
|
148
|
-
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Returns the first element of a non-empty array.
|
|
262
|
+
*
|
|
263
|
+
* Accepts both mutable and readonly arrays. Does not mutate the original array.
|
|
264
|
+
*
|
|
265
|
+
* ### Example
|
|
266
|
+
*
|
|
267
|
+
* ```ts
|
|
268
|
+
* firstInArray(["a", "b", "c"]); // "a"
|
|
269
|
+
* ```
|
|
270
|
+
*
|
|
271
|
+
* @category Accessors
|
|
272
|
+
*/
|
|
273
|
+
export const firstInArray = <T>(array: NonEmptyReadonlyArray<T>): T => array[0];
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Returns the last element of a non-empty array.
|
|
277
|
+
*
|
|
278
|
+
* Accepts both mutable and readonly arrays. Does not mutate the original array.
|
|
279
|
+
*
|
|
280
|
+
* ### Example
|
|
281
|
+
*
|
|
282
|
+
* ```ts
|
|
283
|
+
* lastInArray(["a", "b", "c"]); // "c"
|
|
284
|
+
* ```
|
|
285
|
+
*
|
|
286
|
+
* @category Accessors
|
|
287
|
+
*/
|
|
288
|
+
export const lastInArray = <T>(array: NonEmptyReadonlyArray<T>): T =>
|
|
289
|
+
array[array.length - 1];
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Shifts an item from a non-empty mutable array, guaranteed to return T.
|
|
293
|
+
*
|
|
294
|
+
* **Mutates** the original array.
|
|
295
|
+
*
|
|
296
|
+
* ### Example
|
|
297
|
+
*
|
|
298
|
+
* ```ts
|
|
299
|
+
* const arr: NonEmptyArray<number> = [1, 2, 3];
|
|
300
|
+
* shiftArray(arr); // 1
|
|
301
|
+
* arr; // [2, 3]
|
|
302
|
+
* ```
|
|
303
|
+
*
|
|
304
|
+
* @category Mutations
|
|
305
|
+
*/
|
|
306
|
+
export const shiftArray = <T>(array: NonEmptyArray<T>): T => array.shift() as T;
|
package/src/Crypto.ts
CHANGED
|
@@ -172,10 +172,13 @@ export const createSymmetricCrypto = (
|
|
|
172
172
|
* Returns the PADMÉ padded length for a given input length.
|
|
173
173
|
*
|
|
174
174
|
* PADMÉ limits information leakage about the length of the plain-text for a
|
|
175
|
-
* wide range of encrypted data sizes.
|
|
176
|
-
*
|
|
175
|
+
* wide range of encrypted data sizes.
|
|
176
|
+
*
|
|
177
|
+
* See the PURBs paper for details: https://bford.info/pub/sec/purb.pdf
|
|
177
178
|
*/
|
|
178
|
-
export const
|
|
179
|
+
export const createPadmePaddedLength = (
|
|
180
|
+
length: NonNegativeInt,
|
|
181
|
+
): NonNegativeInt => {
|
|
179
182
|
if (length <= 0) return NonNegativeInt.orThrow(0);
|
|
180
183
|
const e = 31 - Math.clz32(length >>> 0);
|
|
181
184
|
const s = 32 - Math.clz32(e >>> 0);
|
|
@@ -184,12 +187,11 @@ export const padmePaddedLength = (length: NonNegativeInt): NonNegativeInt => {
|
|
|
184
187
|
return NonNegativeInt.orThrow((length + mask) & ~mask);
|
|
185
188
|
};
|
|
186
189
|
|
|
187
|
-
/**
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
return NonNegativeInt.orThrow(padmePaddedLength(length) - length);
|
|
190
|
+
/** Creates a PADMÉ padding array of zeros for the given input length. */
|
|
191
|
+
export const createPadmePadding = (length: NonNegativeInt): Uint8Array => {
|
|
192
|
+
const paddedLength = createPadmePaddedLength(length);
|
|
193
|
+
const paddingLength = NonNegativeInt.orThrow(paddedLength - length);
|
|
194
|
+
return new globalThis.Uint8Array(paddingLength);
|
|
193
195
|
};
|
|
194
196
|
|
|
195
197
|
/**
|
package/src/Evolu/Db.ts
CHANGED
|
@@ -63,11 +63,11 @@ import {
|
|
|
63
63
|
} from "./Sync.js";
|
|
64
64
|
import {
|
|
65
65
|
Timestamp,
|
|
66
|
+
TimestampBytes,
|
|
67
|
+
timestampBytesToTimestamp,
|
|
66
68
|
TimestampConfig,
|
|
67
69
|
TimestampError,
|
|
68
|
-
|
|
69
|
-
timestampStringToTimestamp,
|
|
70
|
-
timestampToTimestampString,
|
|
70
|
+
timestampToTimestampBytes,
|
|
71
71
|
} from "./Timestamp.js";
|
|
72
72
|
|
|
73
73
|
export interface DbConfig extends ConsoleConfig, TimestampConfig {
|
|
@@ -387,7 +387,7 @@ const createDbWorkerDeps =
|
|
|
387
387
|
// }
|
|
388
388
|
|
|
389
389
|
const configResult = sqlite.exec<{
|
|
390
|
-
clock:
|
|
390
|
+
clock: TimestampBytes;
|
|
391
391
|
appOwnerId: OwnerId;
|
|
392
392
|
appOwnerEncryptionKey: OwnerEncryptionKey;
|
|
393
393
|
appOwnerWriteKey: OwnerWriteKey;
|
|
@@ -415,7 +415,7 @@ const createDbWorkerDeps =
|
|
|
415
415
|
};
|
|
416
416
|
|
|
417
417
|
clock = createClock({ ...platformDeps, sqlite })(
|
|
418
|
-
|
|
418
|
+
timestampBytesToTimestamp(config.clock),
|
|
419
419
|
);
|
|
420
420
|
} else {
|
|
421
421
|
appOwner =
|
|
@@ -507,7 +507,7 @@ const initializeDb =
|
|
|
507
507
|
|
|
508
508
|
sql`
|
|
509
509
|
create table evolu_config (
|
|
510
|
-
"clock"
|
|
510
|
+
"clock" blob not null,
|
|
511
511
|
"appOwnerId" text not null,
|
|
512
512
|
"appOwnerEncryptionKey" blob not null,
|
|
513
513
|
"appOwnerWriteKey" blob not null,
|
|
@@ -527,7 +527,7 @@ const initializeDb =
|
|
|
527
527
|
)
|
|
528
528
|
values
|
|
529
529
|
(
|
|
530
|
-
${
|
|
530
|
+
${timestampToTimestampBytes(initialClock)},
|
|
531
531
|
${initialAppOwner.id},
|
|
532
532
|
${initialAppOwner.encryptionKey},
|
|
533
533
|
${initialAppOwner.writeKey},
|
package/src/Evolu/Evolu.ts
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
isNonEmptyArray,
|
|
5
5
|
isNonEmptyReadonlyArray,
|
|
6
6
|
} from "../Array.js";
|
|
7
|
-
import {
|
|
7
|
+
import { assertNonEmptyReadonlyArray } from "../Assert.js";
|
|
8
8
|
import { createCallbacks } from "../Callbacks.js";
|
|
9
9
|
import { ConsoleDep } from "../Console.js";
|
|
10
10
|
import { RandomBytesDep, SymmetricCryptoDecryptError } from "../Crypto.js";
|
|
@@ -48,7 +48,6 @@ import {
|
|
|
48
48
|
} from "./Query.js";
|
|
49
49
|
import {
|
|
50
50
|
CreateQuery,
|
|
51
|
-
DefaultColumns,
|
|
52
51
|
EvoluSchema,
|
|
53
52
|
evoluSchemaToDbSchema,
|
|
54
53
|
IndexesConfig,
|
|
@@ -59,6 +58,7 @@ import {
|
|
|
59
58
|
MutationKind,
|
|
60
59
|
MutationMapping,
|
|
61
60
|
MutationOptions,
|
|
61
|
+
SystemColumns,
|
|
62
62
|
updateable,
|
|
63
63
|
upsertable,
|
|
64
64
|
ValidateSchema,
|
|
@@ -239,7 +239,7 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends Disposable {
|
|
|
239
239
|
*
|
|
240
240
|
* Evolu does not use SQL for mutations to ensure data can be safely and
|
|
241
241
|
* predictably merged without conflicts. Explicit mutations also allow Evolu
|
|
242
|
-
* to automatically add and update {@link
|
|
242
|
+
* to automatically add and update {@link SystemColumns}.
|
|
243
243
|
*
|
|
244
244
|
* ### Example
|
|
245
245
|
*
|
|
@@ -284,7 +284,7 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends Disposable {
|
|
|
284
284
|
*
|
|
285
285
|
* Evolu does not use SQL for mutations to ensure data can be safely and
|
|
286
286
|
* predictably merged without conflicts. Explicit mutations also allow Evolu
|
|
287
|
-
* to automatically add and update {@link
|
|
287
|
+
* to automatically add and update {@link SystemColumns}.
|
|
288
288
|
*
|
|
289
289
|
* ### Example
|
|
290
290
|
*
|
|
@@ -337,7 +337,7 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends Disposable {
|
|
|
337
337
|
*
|
|
338
338
|
* Evolu does not use SQL for mutations to ensure data can be safely and
|
|
339
339
|
* predictably merged without conflicts. Explicit mutations also allow Evolu
|
|
340
|
-
* to automatically add and update {@link
|
|
340
|
+
* to automatically add and update {@link SystemColumns}.
|
|
341
341
|
*
|
|
342
342
|
* ### Example
|
|
343
343
|
*
|
|
@@ -408,7 +408,12 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends Disposable {
|
|
|
408
408
|
*/
|
|
409
409
|
readonly reloadApp: () => void;
|
|
410
410
|
|
|
411
|
-
/**
|
|
411
|
+
/**
|
|
412
|
+
* Export SQLite database file as Uint8Array.
|
|
413
|
+
*
|
|
414
|
+
* In the future, it will be possible to import a database and export/import
|
|
415
|
+
* history for 1:1 migrations across owners.
|
|
416
|
+
*/
|
|
412
417
|
readonly exportDatabase: () => Promise<Uint8Array<ArrayBuffer>>;
|
|
413
418
|
|
|
414
419
|
/**
|
|
@@ -716,21 +721,17 @@ const createEvoluInstance =
|
|
|
716
721
|
const values = { ...result.value };
|
|
717
722
|
delete values.id;
|
|
718
723
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
const dbChange = { table, id, values };
|
|
727
|
-
assert(
|
|
728
|
-
DbChange.is(dbChange),
|
|
729
|
-
`Failed to create DbChange for table "${dbChange.table}"`,
|
|
730
|
-
);
|
|
724
|
+
const dbChange = DbChange.orThrow({
|
|
725
|
+
table,
|
|
726
|
+
id,
|
|
727
|
+
values,
|
|
728
|
+
isInsert: kind === "insert" || kind === "upsert",
|
|
729
|
+
});
|
|
731
730
|
|
|
732
|
-
|
|
733
|
-
|
|
731
|
+
mutateMicrotaskQueue.push([
|
|
732
|
+
{ ...dbChange, ownerId: options?.ownerId },
|
|
733
|
+
options?.onComplete,
|
|
734
|
+
]);
|
|
734
735
|
}
|
|
735
736
|
|
|
736
737
|
if (mutateMicrotaskQueue.length === 1) {
|