@evolu/common 8.3.3 → 8.4.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/Assert.d.ts +62 -19
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +63 -17
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +4 -4
- package/dist/src/local-first/Protocol.js +1 -1
- package/package.json +1 -1
- package/src/Assert.ts +80 -19
- package/src/local-first/Db.ts +9 -6
- package/src/local-first/Protocol.ts +1 -1
package/dist/src/Assert.d.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
+
import type { NonEmptyArray, NonEmptyReadonlyArray } from "./Array.ts";
|
|
6
7
|
/**
|
|
7
8
|
* Ensures a condition is true, throwing an error with the provided message if
|
|
8
9
|
* not.
|
|
@@ -10,9 +11,9 @@
|
|
|
10
11
|
* Prevents invalid states from propagating through the system by halting
|
|
11
12
|
* execution when a condition fails, improving reliability and debuggability.
|
|
12
13
|
*
|
|
13
|
-
* Do not use this instead of {@link Type}. Assertions are intended
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Do not use this instead of {@link Type}. Assertions are intended when a
|
|
15
|
+
* condition is logically guaranteed to be true but TypeScript cannot prove it,
|
|
16
|
+
* or for catching and signaling developer mistakes eagerly.
|
|
16
17
|
*
|
|
17
18
|
* ### Example
|
|
18
19
|
*
|
|
@@ -29,52 +30,94 @@ export declare const assert: (condition: unknown, message: string) => asserts co
|
|
|
29
30
|
/**
|
|
30
31
|
* Asserts that a value is non-nullable.
|
|
31
32
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
33
|
+
* Following TypeScript's {@link NonNullable}, non-nullable here means neither
|
|
34
|
+
* null nor undefined. Use this when a value is logically guaranteed to be
|
|
35
|
+
* non-nullable but TypeScript cannot prove it.
|
|
34
36
|
*/
|
|
35
37
|
export declare const assertNonNullable: <T>(value: T, message?: string) => asserts value is NonNullable<T>;
|
|
38
|
+
/**
|
|
39
|
+
* Asserts that a value is not null while preserving undefined.
|
|
40
|
+
*
|
|
41
|
+
* Use this when a value is logically guaranteed not to be null but TypeScript
|
|
42
|
+
* cannot prove it.
|
|
43
|
+
*
|
|
44
|
+
* ### Example
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* import { assertNotNull } from "@evolu/common";
|
|
48
|
+
*
|
|
49
|
+
* const value = undefined as string | null | undefined;
|
|
50
|
+
* assertNotNull(value);
|
|
51
|
+
* expectTypeOf(value).toEqualTypeOf<string | undefined>();
|
|
52
|
+
* expect(value).toBeUndefined();
|
|
53
|
+
* expect(() => assertNotNull(null)).toThrow(
|
|
54
|
+
* "Expected value not to be null.",
|
|
55
|
+
* );
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare const assertNotNull: <T>(value: T, message?: string) => asserts value is T & ({} | undefined);
|
|
59
|
+
/**
|
|
60
|
+
* Asserts that a value is not undefined while preserving null.
|
|
61
|
+
*
|
|
62
|
+
* Use this when a value is logically guaranteed not to be undefined but
|
|
63
|
+
* TypeScript cannot prove it.
|
|
64
|
+
*
|
|
65
|
+
* ### Example
|
|
66
|
+
*
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { assertNotUndefined } from "@evolu/common";
|
|
69
|
+
*
|
|
70
|
+
* const value = null as string | null | undefined;
|
|
71
|
+
* assertNotUndefined(value);
|
|
72
|
+
* expectTypeOf(value).toEqualTypeOf<string | null>();
|
|
73
|
+
* expect(value).toBeNull();
|
|
74
|
+
* expect(() => assertNotUndefined(undefined)).toThrow(
|
|
75
|
+
* "Expected value not to be undefined.",
|
|
76
|
+
* );
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare const assertNotUndefined: <T>(value: T, message?: string) => asserts value is T & ({} | null);
|
|
36
80
|
/**
|
|
37
81
|
* Asserts that an array is non-empty.
|
|
38
82
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* known.
|
|
83
|
+
* Use this when an array is logically guaranteed to be non-empty but TypeScript
|
|
84
|
+
* cannot prove it.
|
|
42
85
|
*
|
|
43
86
|
* ### Example
|
|
44
87
|
*
|
|
45
88
|
* ```ts
|
|
46
|
-
* import { assertNonEmptyArray } from "@evolu/common";
|
|
89
|
+
* import { assertNonEmptyArray, type NonEmptyArray } from "@evolu/common";
|
|
47
90
|
*
|
|
48
91
|
* const values = [1, 2, 3];
|
|
49
92
|
* assertNonEmptyArray(values);
|
|
50
|
-
* expectTypeOf(values).toEqualTypeOf<
|
|
93
|
+
* expectTypeOf(values).toEqualTypeOf<NonEmptyArray<number>>();
|
|
51
94
|
* expect(values[0]).toBe(1);
|
|
52
95
|
* expect(() => assertNonEmptyArray([])).toThrow();
|
|
53
96
|
* ```
|
|
54
97
|
*/
|
|
55
|
-
export declare const assertNonEmptyArray: <T>(arr: Array<T>, message?: string) => asserts arr is
|
|
98
|
+
export declare const assertNonEmptyArray: <T>(arr: Array<T>, message?: string) => asserts arr is NonEmptyArray<T>;
|
|
56
99
|
/**
|
|
57
100
|
* Asserts that a readonly array is non-empty.
|
|
58
101
|
*
|
|
59
|
-
*
|
|
60
|
-
* TypeScript
|
|
61
|
-
* statically known.
|
|
102
|
+
* Use this when a readonly array is logically guaranteed to be non-empty but
|
|
103
|
+
* TypeScript cannot prove it.
|
|
62
104
|
*
|
|
63
105
|
* ### Example
|
|
64
106
|
*
|
|
65
107
|
* ```ts
|
|
66
|
-
* import {
|
|
108
|
+
* import {
|
|
109
|
+
* assertNonEmptyReadonlyArray,
|
|
110
|
+
* type NonEmptyReadonlyArray,
|
|
111
|
+
* } from "@evolu/common";
|
|
67
112
|
*
|
|
68
113
|
* const values: ReadonlyArray<number> = [1, 2, 3];
|
|
69
114
|
* assertNonEmptyReadonlyArray(values);
|
|
70
|
-
* expectTypeOf(values).toEqualTypeOf<
|
|
71
|
-
* readonly [number, ...Array<number>]
|
|
72
|
-
* >();
|
|
115
|
+
* expectTypeOf(values).toEqualTypeOf<NonEmptyReadonlyArray<number>>();
|
|
73
116
|
* expect(values[0]).toBe(1);
|
|
74
117
|
* expect(() => assertNonEmptyReadonlyArray([])).toThrow();
|
|
75
118
|
* ```
|
|
76
119
|
*/
|
|
77
|
-
export declare const assertNonEmptyReadonlyArray: <T>(arr: ReadonlyArray<T>, message?: string) => asserts arr is
|
|
120
|
+
export declare const assertNonEmptyReadonlyArray: <T>(arr: ReadonlyArray<T>, message?: string) => asserts arr is NonEmptyReadonlyArray<T>;
|
|
78
121
|
/**
|
|
79
122
|
* Guards synchronous methods on objects that may be called after disposal.
|
|
80
123
|
*
|
package/dist/src/Assert.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Assert.d.ts","sourceRoot":"","sources":["../../src/Assert.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"Assert.d.ts","sourceRoot":"","sources":["../../src/Assert.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAGvE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,MAAM,EAAE,CACnB,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC,SAIZ,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,EAAE,CAAC,CAAC,EAChC,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC,KAAK,IAAI,WAAW,CAAC,CAAC,CAKlC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,aAAa,EAAE,CAAC,CAAC,EAC5B,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,SAAS,CAKxC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,kBAAkB,EAAE,CAAC,CAAC,EACjC,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAKnC,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,mBAAmB,EAAE,CAAC,CAAC,EAClC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,EACb,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC,GAAG,IAAI,aAAa,CAAC,CAAC,CAKlC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,2BAA2B,EAAE,CAAC,CAAC,EAC1C,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,EACrB,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC,GAAG,IAAI,qBAAqB,CAAC,CAAC,CAK1C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,iBAAiB,UAE1B,eAAe,GAAG,oBAAoB,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,KACxE,IAEF,CAAC"}
|
package/dist/src/Assert.js
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* Prevents invalid states from propagating through the system by halting
|
|
11
11
|
* execution when a condition fails, improving reliability and debuggability.
|
|
12
12
|
*
|
|
13
|
-
* Do not use this instead of {@link Type}. Assertions are intended
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Do not use this instead of {@link Type}. Assertions are intended when a
|
|
14
|
+
* condition is logically guaranteed to be true but TypeScript cannot prove it,
|
|
15
|
+
* or for catching and signaling developer mistakes eagerly.
|
|
16
16
|
*
|
|
17
17
|
* ### Example
|
|
18
18
|
*
|
|
@@ -33,27 +33,73 @@ export const assert = (condition, message) => {
|
|
|
33
33
|
/**
|
|
34
34
|
* Asserts that a value is non-nullable.
|
|
35
35
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* Following TypeScript's {@link NonNullable}, non-nullable here means neither
|
|
37
|
+
* null nor undefined. Use this when a value is logically guaranteed to be
|
|
38
|
+
* non-nullable but TypeScript cannot prove it.
|
|
38
39
|
*/
|
|
39
40
|
export const assertNonNullable = (value, message = "Expected value to be non-nullable.") => {
|
|
40
41
|
assert(value != null, message);
|
|
41
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* Asserts that a value is not null while preserving undefined.
|
|
45
|
+
*
|
|
46
|
+
* Use this when a value is logically guaranteed not to be null but TypeScript
|
|
47
|
+
* cannot prove it.
|
|
48
|
+
*
|
|
49
|
+
* ### Example
|
|
50
|
+
*
|
|
51
|
+
* ```ts
|
|
52
|
+
* import { assertNotNull } from "@evolu/common";
|
|
53
|
+
*
|
|
54
|
+
* const value = undefined as string | null | undefined;
|
|
55
|
+
* assertNotNull(value);
|
|
56
|
+
* expectTypeOf(value).toEqualTypeOf<string | undefined>();
|
|
57
|
+
* expect(value).toBeUndefined();
|
|
58
|
+
* expect(() => assertNotNull(null)).toThrow(
|
|
59
|
+
* "Expected value not to be null.",
|
|
60
|
+
* );
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export const assertNotNull = (value, message = "Expected value not to be null.") => {
|
|
64
|
+
assert(value !== null, message);
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Asserts that a value is not undefined while preserving null.
|
|
68
|
+
*
|
|
69
|
+
* Use this when a value is logically guaranteed not to be undefined but
|
|
70
|
+
* TypeScript cannot prove it.
|
|
71
|
+
*
|
|
72
|
+
* ### Example
|
|
73
|
+
*
|
|
74
|
+
* ```ts
|
|
75
|
+
* import { assertNotUndefined } from "@evolu/common";
|
|
76
|
+
*
|
|
77
|
+
* const value = null as string | null | undefined;
|
|
78
|
+
* assertNotUndefined(value);
|
|
79
|
+
* expectTypeOf(value).toEqualTypeOf<string | null>();
|
|
80
|
+
* expect(value).toBeNull();
|
|
81
|
+
* expect(() => assertNotUndefined(undefined)).toThrow(
|
|
82
|
+
* "Expected value not to be undefined.",
|
|
83
|
+
* );
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export const assertNotUndefined = (value, message = "Expected value not to be undefined.") => {
|
|
87
|
+
assert(value !== undefined, message);
|
|
88
|
+
};
|
|
42
89
|
/**
|
|
43
90
|
* Asserts that an array is non-empty.
|
|
44
91
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* known.
|
|
92
|
+
* Use this when an array is logically guaranteed to be non-empty but TypeScript
|
|
93
|
+
* cannot prove it.
|
|
48
94
|
*
|
|
49
95
|
* ### Example
|
|
50
96
|
*
|
|
51
97
|
* ```ts
|
|
52
|
-
* import { assertNonEmptyArray } from "@evolu/common";
|
|
98
|
+
* import { assertNonEmptyArray, type NonEmptyArray } from "@evolu/common";
|
|
53
99
|
*
|
|
54
100
|
* const values = [1, 2, 3];
|
|
55
101
|
* assertNonEmptyArray(values);
|
|
56
|
-
* expectTypeOf(values).toEqualTypeOf<
|
|
102
|
+
* expectTypeOf(values).toEqualTypeOf<NonEmptyArray<number>>();
|
|
57
103
|
* expect(values[0]).toBe(1);
|
|
58
104
|
* expect(() => assertNonEmptyArray([])).toThrow();
|
|
59
105
|
* ```
|
|
@@ -64,20 +110,20 @@ export const assertNonEmptyArray = (arr, message = "Expected a non-empty array."
|
|
|
64
110
|
/**
|
|
65
111
|
* Asserts that a readonly array is non-empty.
|
|
66
112
|
*
|
|
67
|
-
*
|
|
68
|
-
* TypeScript
|
|
69
|
-
* statically known.
|
|
113
|
+
* Use this when a readonly array is logically guaranteed to be non-empty but
|
|
114
|
+
* TypeScript cannot prove it.
|
|
70
115
|
*
|
|
71
116
|
* ### Example
|
|
72
117
|
*
|
|
73
118
|
* ```ts
|
|
74
|
-
* import {
|
|
119
|
+
* import {
|
|
120
|
+
* assertNonEmptyReadonlyArray,
|
|
121
|
+
* type NonEmptyReadonlyArray,
|
|
122
|
+
* } from "@evolu/common";
|
|
75
123
|
*
|
|
76
124
|
* const values: ReadonlyArray<number> = [1, 2, 3];
|
|
77
125
|
* assertNonEmptyReadonlyArray(values);
|
|
78
|
-
* expectTypeOf(values).toEqualTypeOf<
|
|
79
|
-
* readonly [number, ...Array<number>]
|
|
80
|
-
* >();
|
|
126
|
+
* expectTypeOf(values).toEqualTypeOf<NonEmptyReadonlyArray<number>>();
|
|
81
127
|
* expect(values[0]).toBe(1);
|
|
82
128
|
* expect(() => assertNonEmptyReadonlyArray([])).toThrow();
|
|
83
129
|
* ```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Db.d.ts","sourceRoot":"","sources":["../../../src/local-first/Db.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EACL,aAAa,EAGd,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAIxD,OAAO,KAAK,EACV,qBAAqB,EAGrB,YAAY,EACb,MAAM,cAAc,CAAC;AAUtB,OAAO,EAAsB,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,
|
|
1
|
+
{"version":3,"file":"Db.d.ts","sourceRoot":"","sources":["../../../src/local-first/Db.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EACL,aAAa,EAGd,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAIxD,OAAO,KAAK,EACV,qBAAqB,EAGrB,YAAY,EACb,MAAM,cAAc,CAAC;AAUtB,OAAO,EAAsB,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,EAQL,KAAK,IAAI,EACV,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EACV,yBAAyB,EACzB,iBAAiB,EACjB,MAAM,EACN,UAAU,EACV,UAAU,EACX,MAAM,cAAc,CAAC;AAqBtB,OAAO,KAAK,EAEV,aAAa,EACb,cAAc,EAGf,MAAM,aAAa,CAAC;AA+BrB,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;AAE5C,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;CACjE;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,QAAQ,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;CACzC;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,GACnC,yBAAyB,GACzB,cAAc,GACd,qBAAqB,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,aAAa,SACjB,UAAU,CAAC,YAAY,CAAC,KAAG,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,YAAY,CAwM/D,CAAC"}
|
|
@@ -56,7 +56,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
56
56
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
57
57
|
});
|
|
58
58
|
import { appendToArray, firstInArray, } from "../Array.js";
|
|
59
|
-
import {
|
|
59
|
+
import { assertNonEmptyReadonlyArray, assertNonNullable, assertNotUndefined, } from "../Assert.js";
|
|
60
60
|
import { EncryptionKey, } from "../Crypto.js";
|
|
61
61
|
import { constFalse, constVoid } from "../Function.js";
|
|
62
62
|
import { acquireLeaderLock } from "../LockManager.js";
|
|
@@ -414,7 +414,7 @@ const createClientStorage = (deps) => ({ onError, }) => {
|
|
|
414
414
|
let encryptionKey = null;
|
|
415
415
|
let didWriteMessages = false;
|
|
416
416
|
const getEncryptionKey = () => {
|
|
417
|
-
|
|
417
|
+
assertNonNullable(encryptionKey, "ClientStorage encryption key must be set");
|
|
418
418
|
return encryptionKey;
|
|
419
419
|
};
|
|
420
420
|
return {
|
|
@@ -557,7 +557,7 @@ const applyLocalOnlyChange = (deps) => (change) => {
|
|
|
557
557
|
const ownerId = change.ownerId;
|
|
558
558
|
const columns = dbChangeToColumns(change, deps.time.now());
|
|
559
559
|
for (const [column, value] of columns) {
|
|
560
|
-
|
|
560
|
+
assertNotUndefined(value);
|
|
561
561
|
deps.sqlite.exec(sql.prepared `
|
|
562
562
|
insert into ${sql.identifier(change.table)}
|
|
563
563
|
("ownerId", "id", ${sql.identifier(column)})
|
|
@@ -577,7 +577,7 @@ const applyMessages = (deps) => (ownerId, messages) => {
|
|
|
577
577
|
const idBytes = idToIdBytes(change.id);
|
|
578
578
|
const timestampBytes = timestampToTimestampBytes(timestamp);
|
|
579
579
|
for (const [column, value] of columns) {
|
|
580
|
-
|
|
580
|
+
assertNotUndefined(value);
|
|
581
581
|
if (validateColumnValue(deps)(change.table, column, value)) {
|
|
582
582
|
applyColumnChange(deps)(ownerIdBytes, ownerId, change.table, idBytes, change.id, column, value, timestampBytes);
|
|
583
583
|
}
|
|
@@ -884,7 +884,7 @@ const sync = (deps) => (ranges, output, ownerIdBytes) => {
|
|
|
884
884
|
nonSkipRangeAdded = true;
|
|
885
885
|
if (skip) {
|
|
886
886
|
skip = false;
|
|
887
|
-
|
|
887
|
+
assertNonNullable(prevUpperBound, "prevUpperBound is null");
|
|
888
888
|
// There is always a space for a skip range before adding.
|
|
889
889
|
output.addRange({
|
|
890
890
|
type: RangeType.Skip,
|
package/package.json
CHANGED
package/src/Assert.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { NonEmptyArray, NonEmptyReadonlyArray } from "./Array.ts";
|
|
7
8
|
import type { Type } from "./Type.ts";
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -13,9 +14,9 @@ import type { Type } from "./Type.ts";
|
|
|
13
14
|
* Prevents invalid states from propagating through the system by halting
|
|
14
15
|
* execution when a condition fails, improving reliability and debuggability.
|
|
15
16
|
*
|
|
16
|
-
* Do not use this instead of {@link Type}. Assertions are intended
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Do not use this instead of {@link Type}. Assertions are intended when a
|
|
18
|
+
* condition is logically guaranteed to be true but TypeScript cannot prove it,
|
|
19
|
+
* or for catching and signaling developer mistakes eagerly.
|
|
19
20
|
*
|
|
20
21
|
* ### Example
|
|
21
22
|
*
|
|
@@ -40,8 +41,9 @@ export const assert: (
|
|
|
40
41
|
/**
|
|
41
42
|
* Asserts that a value is non-nullable.
|
|
42
43
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
44
|
+
* Following TypeScript's {@link NonNullable}, non-nullable here means neither
|
|
45
|
+
* null nor undefined. Use this when a value is logically guaranteed to be
|
|
46
|
+
* non-nullable but TypeScript cannot prove it.
|
|
45
47
|
*/
|
|
46
48
|
export const assertNonNullable: <T>(
|
|
47
49
|
value: T,
|
|
@@ -53,21 +55,80 @@ export const assertNonNullable: <T>(
|
|
|
53
55
|
assert(value != null, message);
|
|
54
56
|
};
|
|
55
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Asserts that a value is not null while preserving undefined.
|
|
60
|
+
*
|
|
61
|
+
* Use this when a value is logically guaranteed not to be null but TypeScript
|
|
62
|
+
* cannot prove it.
|
|
63
|
+
*
|
|
64
|
+
* ### Example
|
|
65
|
+
*
|
|
66
|
+
* ```ts
|
|
67
|
+
* import { assertNotNull } from "@evolu/common";
|
|
68
|
+
*
|
|
69
|
+
* const value = undefined as string | null | undefined;
|
|
70
|
+
* assertNotNull(value);
|
|
71
|
+
* expectTypeOf(value).toEqualTypeOf<string | undefined>();
|
|
72
|
+
* expect(value).toBeUndefined();
|
|
73
|
+
* expect(() => assertNotNull(null)).toThrow(
|
|
74
|
+
* "Expected value not to be null.",
|
|
75
|
+
* );
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export const assertNotNull: <T>(
|
|
79
|
+
value: T,
|
|
80
|
+
message?: string,
|
|
81
|
+
) => asserts value is T & ({} | undefined) = (
|
|
82
|
+
value,
|
|
83
|
+
message = "Expected value not to be null.",
|
|
84
|
+
) => {
|
|
85
|
+
assert(value !== null, message);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Asserts that a value is not undefined while preserving null.
|
|
90
|
+
*
|
|
91
|
+
* Use this when a value is logically guaranteed not to be undefined but
|
|
92
|
+
* TypeScript cannot prove it.
|
|
93
|
+
*
|
|
94
|
+
* ### Example
|
|
95
|
+
*
|
|
96
|
+
* ```ts
|
|
97
|
+
* import { assertNotUndefined } from "@evolu/common";
|
|
98
|
+
*
|
|
99
|
+
* const value = null as string | null | undefined;
|
|
100
|
+
* assertNotUndefined(value);
|
|
101
|
+
* expectTypeOf(value).toEqualTypeOf<string | null>();
|
|
102
|
+
* expect(value).toBeNull();
|
|
103
|
+
* expect(() => assertNotUndefined(undefined)).toThrow(
|
|
104
|
+
* "Expected value not to be undefined.",
|
|
105
|
+
* );
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export const assertNotUndefined: <T>(
|
|
109
|
+
value: T,
|
|
110
|
+
message?: string,
|
|
111
|
+
) => asserts value is T & ({} | null) = (
|
|
112
|
+
value,
|
|
113
|
+
message = "Expected value not to be undefined.",
|
|
114
|
+
) => {
|
|
115
|
+
assert(value !== undefined, message);
|
|
116
|
+
};
|
|
117
|
+
|
|
56
118
|
/**
|
|
57
119
|
* Asserts that an array is non-empty.
|
|
58
120
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* known.
|
|
121
|
+
* Use this when an array is logically guaranteed to be non-empty but TypeScript
|
|
122
|
+
* cannot prove it.
|
|
62
123
|
*
|
|
63
124
|
* ### Example
|
|
64
125
|
*
|
|
65
126
|
* ```ts
|
|
66
|
-
* import { assertNonEmptyArray } from "@evolu/common";
|
|
127
|
+
* import { assertNonEmptyArray, type NonEmptyArray } from "@evolu/common";
|
|
67
128
|
*
|
|
68
129
|
* const values = [1, 2, 3];
|
|
69
130
|
* assertNonEmptyArray(values);
|
|
70
|
-
* expectTypeOf(values).toEqualTypeOf<
|
|
131
|
+
* expectTypeOf(values).toEqualTypeOf<NonEmptyArray<number>>();
|
|
71
132
|
* expect(values[0]).toBe(1);
|
|
72
133
|
* expect(() => assertNonEmptyArray([])).toThrow();
|
|
73
134
|
* ```
|
|
@@ -75,7 +136,7 @@ export const assertNonNullable: <T>(
|
|
|
75
136
|
export const assertNonEmptyArray: <T>(
|
|
76
137
|
arr: Array<T>,
|
|
77
138
|
message?: string,
|
|
78
|
-
) => asserts arr is
|
|
139
|
+
) => asserts arr is NonEmptyArray<T> = (
|
|
79
140
|
arr,
|
|
80
141
|
message = "Expected a non-empty array.",
|
|
81
142
|
) => {
|
|
@@ -85,20 +146,20 @@ export const assertNonEmptyArray: <T>(
|
|
|
85
146
|
/**
|
|
86
147
|
* Asserts that a readonly array is non-empty.
|
|
87
148
|
*
|
|
88
|
-
*
|
|
89
|
-
* TypeScript
|
|
90
|
-
* statically known.
|
|
149
|
+
* Use this when a readonly array is logically guaranteed to be non-empty but
|
|
150
|
+
* TypeScript cannot prove it.
|
|
91
151
|
*
|
|
92
152
|
* ### Example
|
|
93
153
|
*
|
|
94
154
|
* ```ts
|
|
95
|
-
* import {
|
|
155
|
+
* import {
|
|
156
|
+
* assertNonEmptyReadonlyArray,
|
|
157
|
+
* type NonEmptyReadonlyArray,
|
|
158
|
+
* } from "@evolu/common";
|
|
96
159
|
*
|
|
97
160
|
* const values: ReadonlyArray<number> = [1, 2, 3];
|
|
98
161
|
* assertNonEmptyReadonlyArray(values);
|
|
99
|
-
* expectTypeOf(values).toEqualTypeOf<
|
|
100
|
-
* readonly [number, ...Array<number>]
|
|
101
|
-
* >();
|
|
162
|
+
* expectTypeOf(values).toEqualTypeOf<NonEmptyReadonlyArray<number>>();
|
|
102
163
|
* expect(values[0]).toBe(1);
|
|
103
164
|
* expect(() => assertNonEmptyReadonlyArray([])).toThrow();
|
|
104
165
|
* ```
|
|
@@ -106,7 +167,7 @@ export const assertNonEmptyArray: <T>(
|
|
|
106
167
|
export const assertNonEmptyReadonlyArray: <T>(
|
|
107
168
|
arr: ReadonlyArray<T>,
|
|
108
169
|
message?: string,
|
|
109
|
-
) => asserts arr is
|
|
170
|
+
) => asserts arr is NonEmptyReadonlyArray<T> = (
|
|
110
171
|
arr,
|
|
111
172
|
message = "Expected a non-empty readonly array.",
|
|
112
173
|
) => {
|
package/src/local-first/Db.ts
CHANGED
|
@@ -11,9 +11,9 @@ import {
|
|
|
11
11
|
type NonEmptyReadonlyArray,
|
|
12
12
|
} from "../Array.ts";
|
|
13
13
|
import {
|
|
14
|
-
assert,
|
|
15
14
|
assertNonEmptyReadonlyArray,
|
|
16
15
|
assertNonNullable,
|
|
16
|
+
assertNotUndefined,
|
|
17
17
|
} from "../Assert.ts";
|
|
18
18
|
import type { ConsoleLevel } from "../Console.ts";
|
|
19
19
|
import {
|
|
@@ -45,13 +45,13 @@ import { callback, type Run, type Task } from "../Task.ts";
|
|
|
45
45
|
import { Millis, millisToDateIso, type TimeDep } from "../Time.ts";
|
|
46
46
|
import {
|
|
47
47
|
assertType,
|
|
48
|
-
type ExtractTyped,
|
|
49
48
|
Id,
|
|
50
49
|
IdBytes,
|
|
51
50
|
idBytesToId,
|
|
52
51
|
idToIdBytes,
|
|
53
|
-
type Name,
|
|
54
52
|
onePositiveInt,
|
|
53
|
+
type ExtractTyped,
|
|
54
|
+
type Name,
|
|
55
55
|
} from "../Type.ts";
|
|
56
56
|
import type {
|
|
57
57
|
CreateBroadcastChannelDep,
|
|
@@ -634,7 +634,10 @@ const createClientStorage =
|
|
|
634
634
|
let didWriteMessages = false;
|
|
635
635
|
|
|
636
636
|
const getEncryptionKey = (): EncryptionKey => {
|
|
637
|
-
|
|
637
|
+
assertNonNullable(
|
|
638
|
+
encryptionKey,
|
|
639
|
+
"ClientStorage encryption key must be set",
|
|
640
|
+
);
|
|
638
641
|
return encryptionKey;
|
|
639
642
|
};
|
|
640
643
|
|
|
@@ -841,7 +844,7 @@ const applyLocalOnlyChange =
|
|
|
841
844
|
const columns = dbChangeToColumns(change, deps.time.now());
|
|
842
845
|
|
|
843
846
|
for (const [column, value] of columns) {
|
|
844
|
-
|
|
847
|
+
assertNotUndefined(value);
|
|
845
848
|
deps.sqlite.exec(sql.prepared`
|
|
846
849
|
insert into ${sql.identifier(change.table)}
|
|
847
850
|
("ownerId", "id", ${sql.identifier(column)})
|
|
@@ -871,7 +874,7 @@ const applyMessages =
|
|
|
871
874
|
const timestampBytes = timestampToTimestampBytes(timestamp);
|
|
872
875
|
|
|
873
876
|
for (const [column, value] of columns) {
|
|
874
|
-
|
|
877
|
+
assertNotUndefined(value);
|
|
875
878
|
if (validateColumnValue(deps)(change.table, column, value)) {
|
|
876
879
|
applyColumnChange(deps)(
|
|
877
880
|
ownerIdBytes,
|
|
@@ -1370,7 +1370,7 @@ const sync =
|
|
|
1370
1370
|
nonSkipRangeAdded = true;
|
|
1371
1371
|
if (skip) {
|
|
1372
1372
|
skip = false;
|
|
1373
|
-
|
|
1373
|
+
assertNonNullable(prevUpperBound, "prevUpperBound is null");
|
|
1374
1374
|
// There is always a space for a skip range before adding.
|
|
1375
1375
|
output.addRange({
|
|
1376
1376
|
type: RangeType.Skip,
|