@evolu/common 6.0.1-preview.17 → 6.0.1-preview.19
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/Brand.d.ts +75 -0
- package/dist/src/Brand.d.ts.map +1 -0
- package/dist/src/Brand.js +1 -0
- package/dist/src/Callbacks.d.ts +1 -1
- package/dist/src/Crypto.d.ts +8 -2
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +15 -6
- package/dist/src/Evolu/Db.d.ts +4 -4
- package/dist/src/Evolu/Owner.d.ts +1 -1
- package/dist/src/Evolu/Protocol.d.ts +1 -1
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Query.d.ts +2 -1
- package/dist/src/Evolu/Query.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.d.ts +1 -1
- package/dist/src/Number.d.ts +2 -1
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Result.d.ts +11 -37
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +3 -241
- package/dist/src/Sqlite.d.ts +2 -1
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Type.d.ts +2 -1
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Types.d.ts +0 -74
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/package.json +5 -5
- package/src/Brand.ts +75 -0
- package/src/Callbacks.ts +1 -1
- package/src/Crypto.ts +21 -8
- package/src/Evolu/Protocol.ts +2 -1
- package/src/Evolu/Query.ts +2 -1
- package/src/Evolu/Storage.ts +1 -1
- package/src/Evolu/Timestamp.ts +1 -1
- package/src/Number.ts +2 -6
- package/src/Result.ts +12 -40
- package/src/Sqlite.ts +2 -1
- package/src/Type.ts +2 -1
- package/src/Types.ts +0 -76
- package/src/index.ts +1 -0
package/src/Brand.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A utility interface for creating branded types.
|
|
3
|
+
*
|
|
4
|
+
* Branded types enhance type safety by differentiating otherwise identical base
|
|
5
|
+
* types, such as `number` or `string`, to enforce stricter type checks.
|
|
6
|
+
*
|
|
7
|
+
* Supports multiple brands, allowing types to act like flags.
|
|
8
|
+
*
|
|
9
|
+
* ### Example 1: Single Brand
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* // A branded type definition
|
|
13
|
+
* type UserId = number & Brand<"UserId">;
|
|
14
|
+
*
|
|
15
|
+
* // A function that creates `UserId` values.
|
|
16
|
+
* // Casting with `as UserId` is unsafe, so `createUserId` must be unit-tested.
|
|
17
|
+
* const createUserId = (): UserId => {
|
|
18
|
+
* return 123 as UserId; // Unsafe casting
|
|
19
|
+
* };
|
|
20
|
+
*
|
|
21
|
+
* const userId = createUserId();
|
|
22
|
+
*
|
|
23
|
+
* // A function that accepts only `UserId`.
|
|
24
|
+
* const getUser = (id: UserId) => {
|
|
25
|
+
* // Implementation
|
|
26
|
+
* };
|
|
27
|
+
*
|
|
28
|
+
* getUser(userId); // ✅ Valid
|
|
29
|
+
* getUser(123); // ❌ TypeScript error
|
|
30
|
+
* getUser("123"); // ❌ TypeScript error
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* ### Example 2: Multiple Brands
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* // Define branded types
|
|
37
|
+
* type Min1 = string & Brand<"Min1">;
|
|
38
|
+
* type Max100 = string & Brand<"Max100">;
|
|
39
|
+
* type Min1Max100 = string & Brand<"Min1" | "Max100">;
|
|
40
|
+
*
|
|
41
|
+
* // Functions requiring specific brands
|
|
42
|
+
* const requiresMin1 = (value: Min1): void => {};
|
|
43
|
+
* const requiresMax100 = (value: Max100): void => {};
|
|
44
|
+
*
|
|
45
|
+
* // Values with single brands
|
|
46
|
+
* const min1Value: Min1 = "hello" as Min1;
|
|
47
|
+
* const max100Value: Max100 = "world" as Max100;
|
|
48
|
+
*
|
|
49
|
+
* // Value with multiple brands
|
|
50
|
+
* const min1Max100Value: Min1Max100 = "typescript" as Min1Max100;
|
|
51
|
+
*
|
|
52
|
+
* // Valid cases
|
|
53
|
+
* requiresMin1(min1Value); // ✅ Valid
|
|
54
|
+
* requiresMax100(max100Value); // ✅ Valid
|
|
55
|
+
* requiresMin1(min1Max100Value); // ✅ Valid: Min1Max100 satisfies Min1
|
|
56
|
+
* requiresMax100(min1Max100Value); // ✅ Valid: Min1Max100 satisfies Max100
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export interface Brand<B extends string> {
|
|
60
|
+
readonly [__brand]: Readonly<Record<B, true>>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
declare const __brand: unique symbol;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Determines whether a type `T` is a branded type.
|
|
67
|
+
*
|
|
68
|
+
* Works with any base type intersected with a `Brand`.
|
|
69
|
+
*
|
|
70
|
+
* ### Examples
|
|
71
|
+
*
|
|
72
|
+
* - `IsBranded<string>` -> false
|
|
73
|
+
* - `IsBranded<string & Brand<"X">>` -> true
|
|
74
|
+
*/
|
|
75
|
+
export type IsBranded<T> = T extends Brand<string> ? true : false;
|
package/src/Callbacks.ts
CHANGED
package/src/Crypto.ts
CHANGED
|
@@ -20,8 +20,9 @@ import {
|
|
|
20
20
|
NonNegativeInt,
|
|
21
21
|
Uint8Array,
|
|
22
22
|
} from "./Type.js";
|
|
23
|
-
import { Brand } from "./
|
|
23
|
+
import { Brand } from "./Brand.js";
|
|
24
24
|
import { assert } from "./Assert.js";
|
|
25
|
+
import { utf8ToBytes } from "./Buffer.js";
|
|
25
26
|
|
|
26
27
|
/** `Uint8Array` created by {@link createRandomBytes}. */
|
|
27
28
|
export type RandomBytes = Uint8Array & Brand<"RandomBytes">;
|
|
@@ -56,7 +57,7 @@ export const mnemonicToMnemonicSeed = (mnemonic: Mnemonic): MnemonicSeed =>
|
|
|
56
57
|
* https://github.com/satoshilabs/slips/blob/master/slip-0021.md
|
|
57
58
|
*/
|
|
58
59
|
export const createSlip21 = (
|
|
59
|
-
seed:
|
|
60
|
+
seed: Uint8Array,
|
|
60
61
|
path: ReadonlyArray<string>,
|
|
61
62
|
): Uint8Array => {
|
|
62
63
|
assert(
|
|
@@ -64,17 +65,29 @@ export const createSlip21 = (
|
|
|
64
65
|
`Unusual SLIP-0021 seed length: ${seed.length} bytes`,
|
|
65
66
|
);
|
|
66
67
|
|
|
67
|
-
let m = hmac(sha512, "Symmetric key seed", seed);
|
|
68
|
+
let m = hmac(sha512, utf8ToBytes("Symmetric key seed"), seed);
|
|
68
69
|
for (const component of path) {
|
|
69
|
-
|
|
70
|
-
const e = new globalThis.Uint8Array(p.byteLength + 1);
|
|
71
|
-
e[0] = 0;
|
|
72
|
-
e.set(p, 1);
|
|
73
|
-
m = hmac(sha512, m.slice(0, 32), e);
|
|
70
|
+
m = deriveSlip21Node(component, m);
|
|
74
71
|
}
|
|
75
72
|
return m.slice(32, 64);
|
|
76
73
|
};
|
|
77
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Derives a single node in the SLIP-21 hierarchical key derivation.
|
|
77
|
+
*
|
|
78
|
+
* @see {@link createSlip21}
|
|
79
|
+
*/
|
|
80
|
+
export const deriveSlip21Node = (
|
|
81
|
+
component: string,
|
|
82
|
+
m: Uint8Array,
|
|
83
|
+
): Uint8Array => {
|
|
84
|
+
const p = utf8ToBytes(component);
|
|
85
|
+
const e = new globalThis.Uint8Array(p.byteLength + 1);
|
|
86
|
+
e[0] = 0;
|
|
87
|
+
e.set(p, 1);
|
|
88
|
+
return hmac(sha512, m.slice(0, 32), e);
|
|
89
|
+
};
|
|
90
|
+
|
|
78
91
|
/**
|
|
79
92
|
* Creates a 21-character Base64URL ID (also known as nanoid) from a SLIP-21
|
|
80
93
|
* derived key.
|
package/src/Evolu/Protocol.ts
CHANGED
|
@@ -183,7 +183,8 @@ import {
|
|
|
183
183
|
PositiveInt,
|
|
184
184
|
record,
|
|
185
185
|
} from "../Type.js";
|
|
186
|
-
import {
|
|
186
|
+
import { Predicate } from "../Types.js";
|
|
187
|
+
import { Brand } from "../Brand.js";
|
|
187
188
|
import { Owner, OwnerId, WriteKey, writeKeyLength } from "./Owner.js";
|
|
188
189
|
import {
|
|
189
190
|
BinaryTimestamp,
|
package/src/Evolu/Query.ts
CHANGED
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
SqliteValue,
|
|
9
9
|
} from "../Sqlite.js";
|
|
10
10
|
import { Store, StoreSubscribe } from "../Store.js";
|
|
11
|
-
import {
|
|
11
|
+
import { Simplify } from "../Types.js";
|
|
12
|
+
import { Brand } from "../Brand.js";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* A type-safe SQL query.
|
package/src/Evolu/Storage.ts
CHANGED
|
@@ -29,7 +29,7 @@ import { RandomDep } from "../Random.js";
|
|
|
29
29
|
import { ok, Result } from "../Result.js";
|
|
30
30
|
import { sql, SqliteDep, SqliteError } from "../Sqlite.js";
|
|
31
31
|
import { Int64String, NonNegativeInt, PositiveInt } from "../Type.js";
|
|
32
|
-
import { Brand } from "../
|
|
32
|
+
import { Brand } from "../Brand.js";
|
|
33
33
|
import { OwnerId } from "./Owner.js";
|
|
34
34
|
import {
|
|
35
35
|
BinaryOwnerId,
|
package/src/Evolu/Timestamp.ts
CHANGED
package/src/Number.ts
CHANGED
|
@@ -2,12 +2,8 @@ import { NonEmptyReadonlyArray } from "./Array.js";
|
|
|
2
2
|
import { assertNonEmptyReadonlyArray } from "./Assert.js";
|
|
3
3
|
import { err, ok, Result } from "./Result.js";
|
|
4
4
|
import { NonNegativeInt, PositiveInt } from "./Type.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
IsBranded,
|
|
8
|
-
Predicate,
|
|
9
|
-
WidenLiteral,
|
|
10
|
-
} from "./Types.js";
|
|
5
|
+
import { IntentionalNever, Predicate, WidenLiteral } from "./Types.js";
|
|
6
|
+
import { IsBranded } from "./Brand.js";
|
|
11
7
|
|
|
12
8
|
export const increment = (n: number): number => n + 1;
|
|
13
9
|
|
package/src/Result.ts
CHANGED
|
@@ -1,37 +1,17 @@
|
|
|
1
|
-
/* eslint-disable jsdoc/no-undefined-types */
|
|
2
1
|
/**
|
|
3
2
|
* 🛡️ Type-safe errors
|
|
4
3
|
*
|
|
5
|
-
* ## Intro
|
|
6
|
-
*
|
|
7
4
|
* The problem with throwing an exception in JavaScript is that the caught error
|
|
8
5
|
* is always of an unknown type. The unknown type is a problem because we can't
|
|
9
6
|
* be sure all errors have been handled because the TypeScript compiler can't
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* type, such as Result or Either, allowing the developer to handle all errors
|
|
15
|
-
* safely. ✅
|
|
16
|
-
*
|
|
17
|
-
* ✨ Evolu uses {@link Result}, and it looks like this:
|
|
18
|
-
*
|
|
19
|
-
* ```ts
|
|
20
|
-
* type Result<T, E> = Ok<T> | Err<E>;
|
|
21
|
-
*
|
|
22
|
-
* interface Ok<T> {
|
|
23
|
-
* readonly ok: true;
|
|
24
|
-
* readonly value: T;
|
|
25
|
-
* }
|
|
26
|
-
*
|
|
27
|
-
* interface Err<E> {
|
|
28
|
-
* readonly ok: false;
|
|
29
|
-
* readonly error: E;
|
|
30
|
-
* }
|
|
7
|
+
* tell us. Some other languages like Rust 🦀 or Haskell 📚 use a type-safe
|
|
8
|
+
* approach to error handling, where errors are explicitly represented as part
|
|
9
|
+
* of the return type, such as Result or Either, allowing the developer to
|
|
10
|
+
* handle all errors safely. ✅
|
|
31
11
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
12
|
+
* This type models that approach in TypeScript. A `Result` can be either
|
|
13
|
+
* {@link Ok} (success) or {@link Err} (error). Use {@link ok} to create a
|
|
14
|
+
* successful result and {@link err} to create an error result.
|
|
35
15
|
*
|
|
36
16
|
* Now let's look at how `Result` can be used for safe JSON parsing:
|
|
37
17
|
*
|
|
@@ -198,9 +178,8 @@
|
|
|
198
178
|
* ### How do I handle an array of operations and short-circuit on the first error?
|
|
199
179
|
*
|
|
200
180
|
* If you have an array of operations (not results), you should make them
|
|
201
|
-
* _lazy_—that is, represent each operation as a function
|
|
202
|
-
*
|
|
203
|
-
* stop on the first error:
|
|
181
|
+
* _lazy_—that is, represent each operation as a function. This way, you only
|
|
182
|
+
* execute each operation as needed, and can stop on the first error:
|
|
204
183
|
*
|
|
205
184
|
* ```ts
|
|
206
185
|
* import type { LazyValue } from "./Function";
|
|
@@ -235,15 +214,6 @@
|
|
|
235
214
|
* developers. While monads and functional helpers can be powerful, they often
|
|
236
215
|
* obscure control flow and make debugging harder. Evolu's approach keeps error
|
|
237
216
|
* handling explicit and straightforward.
|
|
238
|
-
*
|
|
239
|
-
* @module
|
|
240
|
-
*/
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* A `Result` can be either {@link Ok} (success) or {@link Err} (error).
|
|
244
|
-
*
|
|
245
|
-
* Use {@link ok} to create a successful result and {@link err} to create an error
|
|
246
|
-
* result.
|
|
247
217
|
*/
|
|
248
218
|
export type Result<T, E> = Ok<T> | Err<E>;
|
|
249
219
|
|
|
@@ -356,12 +326,14 @@ export const err = <E>(error: E): Err<E> => ({ ok: false, error });
|
|
|
356
326
|
* const config = getOrThrow(loadConfig());
|
|
357
327
|
* // Safe to use config here
|
|
358
328
|
* ```
|
|
329
|
+
*
|
|
330
|
+
* Throws: `Error` with the original error attached as `cause`.
|
|
359
331
|
*/
|
|
360
332
|
export const getOrThrow = <T, E>(result: Result<T, E>): T => {
|
|
361
333
|
if (result.ok) {
|
|
362
334
|
return result.value;
|
|
363
335
|
} else {
|
|
364
|
-
throw new Error(
|
|
336
|
+
throw new Error("getOrThrow failed", { cause: result.error });
|
|
365
337
|
}
|
|
366
338
|
};
|
|
367
339
|
|
package/src/Sqlite.ts
CHANGED
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
Uint8Array,
|
|
12
12
|
union,
|
|
13
13
|
} from "./Type.js";
|
|
14
|
-
import {
|
|
14
|
+
import { Predicate, IntentionalNever } from "./Types.js";
|
|
15
|
+
import { Brand } from "./Brand.js";
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* SQLite driver interface. This is the minimal interface that platform-specific
|
package/src/Type.ts
CHANGED
|
@@ -80,7 +80,8 @@ import { NanoIdLibDep } from "./NanoId.js";
|
|
|
80
80
|
import { isPlainObject } from "./Object.js";
|
|
81
81
|
import { Err, err, Ok, ok, Result, trySync } from "./Result.js";
|
|
82
82
|
import { safelyStringifyUnknownValue } from "./String.js";
|
|
83
|
-
import type {
|
|
83
|
+
import type { Literal, Simplify, WidenLiteral } from "./Types.js";
|
|
84
|
+
import type { Brand } from "./Brand.js";
|
|
84
85
|
import { IntentionalNever } from "./Types.js";
|
|
85
86
|
|
|
86
87
|
export interface Type<
|
package/src/Types.ts
CHANGED
|
@@ -78,82 +78,6 @@ export type NullablePartial<
|
|
|
78
78
|
*/
|
|
79
79
|
export type IntentionalNever = never;
|
|
80
80
|
|
|
81
|
-
/**
|
|
82
|
-
* A utility interface for creating branded types.
|
|
83
|
-
*
|
|
84
|
-
* Branded types enhance type safety by differentiating otherwise identical base
|
|
85
|
-
* types, such as `number` or `string`, to enforce stricter type checks.
|
|
86
|
-
*
|
|
87
|
-
* Supports multiple brands, allowing types to act like flags.
|
|
88
|
-
*
|
|
89
|
-
* ### Example 1: Single Brand
|
|
90
|
-
*
|
|
91
|
-
* ```ts
|
|
92
|
-
* // A branded type definition
|
|
93
|
-
* type UserId = number & Brand<"UserId">;
|
|
94
|
-
*
|
|
95
|
-
* // A function that creates `UserId` values.
|
|
96
|
-
* // Casting with `as UserId` is unsafe, so `createUserId` must be unit-tested.
|
|
97
|
-
* const createUserId = (): UserId => {
|
|
98
|
-
* return 123 as UserId; // Unsafe casting
|
|
99
|
-
* };
|
|
100
|
-
*
|
|
101
|
-
* const userId = createUserId();
|
|
102
|
-
*
|
|
103
|
-
* // A function that accepts only `UserId`.
|
|
104
|
-
* const getUser = (id: UserId) => {
|
|
105
|
-
* // Implementation
|
|
106
|
-
* };
|
|
107
|
-
*
|
|
108
|
-
* getUser(userId); // ✅ Valid
|
|
109
|
-
* getUser(123); // ❌ TypeScript error
|
|
110
|
-
* getUser("123"); // ❌ TypeScript error
|
|
111
|
-
* ```
|
|
112
|
-
*
|
|
113
|
-
* ### Example 2: Multiple Brands
|
|
114
|
-
*
|
|
115
|
-
* ```ts
|
|
116
|
-
* // Define branded types
|
|
117
|
-
* type Min1 = string & Brand<"Min1">;
|
|
118
|
-
* type Max100 = string & Brand<"Max100">;
|
|
119
|
-
* type Min1Max100 = string & Brand<"Min1" | "Max100">;
|
|
120
|
-
*
|
|
121
|
-
* // Functions requiring specific brands
|
|
122
|
-
* const requiresMin1 = (value: Min1): void => {};
|
|
123
|
-
* const requiresMax100 = (value: Max100): void => {};
|
|
124
|
-
*
|
|
125
|
-
* // Values with single brands
|
|
126
|
-
* const min1Value: Min1 = "hello" as Min1;
|
|
127
|
-
* const max100Value: Max100 = "world" as Max100;
|
|
128
|
-
*
|
|
129
|
-
* // Value with multiple brands
|
|
130
|
-
* const min1Max100Value: Min1Max100 = "typescript" as Min1Max100;
|
|
131
|
-
*
|
|
132
|
-
* // Valid cases
|
|
133
|
-
* requiresMin1(min1Value); // ✅ Valid
|
|
134
|
-
* requiresMax100(max100Value); // ✅ Valid
|
|
135
|
-
* requiresMin1(min1Max100Value); // ✅ Valid: Min1Max100 satisfies Min1
|
|
136
|
-
* requiresMax100(min1Max100Value); // ✅ Valid: Min1Max100 satisfies Max100
|
|
137
|
-
* ```
|
|
138
|
-
*/
|
|
139
|
-
export interface Brand<B extends string> {
|
|
140
|
-
readonly [__brand]: Readonly<Record<B, true>>;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
declare const __brand: unique symbol;
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Determines whether a type `T` is a branded type.
|
|
147
|
-
*
|
|
148
|
-
* Works with any base type intersected with a `Brand`.
|
|
149
|
-
*
|
|
150
|
-
* ### Examples
|
|
151
|
-
*
|
|
152
|
-
* - `IsBranded<string>` -> false
|
|
153
|
-
* - `IsBranded<string & Brand<"X">>` -> true
|
|
154
|
-
*/
|
|
155
|
-
export type IsBranded<T> = T extends Brand<string> ? true : false;
|
|
156
|
-
|
|
157
81
|
/**
|
|
158
82
|
* String | number | bigint | boolean | undefined | null
|
|
159
83
|
*
|