@evolu/common 6.0.1-preview.16 → 6.0.1-preview.18
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 +1 -1
- package/dist/src/Evolu/Db.d.ts +5 -5
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +8 -10
- package/dist/src/Evolu/Evolu.d.ts +21 -19
- package/dist/src/Evolu/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu/Evolu.js +5 -28
- package/dist/src/Evolu/Owner.d.ts +1 -1
- package/dist/src/Evolu/Owner.d.ts.map +1 -1
- package/dist/src/Evolu/Owner.js +1 -0
- package/dist/src/Evolu/Protocol.d.ts +1 -1
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +1 -0
- package/dist/src/Evolu/Public.d.ts +1 -1
- package/dist/src/Evolu/Public.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/Schema.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 -240
- 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/Type.js +1 -0
- 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 +1 -1
- package/src/Evolu/Db.ts +9 -17
- package/src/Evolu/Evolu.ts +26 -54
- package/src/Evolu/Owner.ts +1 -0
- package/src/Evolu/Protocol.ts +3 -1
- package/src/Evolu/Public.ts +1 -6
- package/src/Evolu/Query.ts +2 -1
- package/src/Evolu/Schema.ts +1 -8
- 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 -39
- package/src/Sqlite.ts +2 -1
- package/src/Type.ts +3 -1
- package/src/Types.ts +0 -76
- package/src/index.ts +1 -0
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
|
package/dist/src/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evolu/common",
|
|
3
|
-
"version": "6.0.1-preview.
|
|
3
|
+
"version": "6.0.1-preview.18",
|
|
4
4
|
"description": "TypeScript library and local-first framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"evolu",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"@noble/ciphers": "^1.3.0",
|
|
46
46
|
"@noble/hashes": "^1.8.0",
|
|
47
47
|
"@scure/bip39": "^1.6.0",
|
|
48
|
-
"kysely": "^0.28.
|
|
49
|
-
"msgpackr": "^1.11.
|
|
48
|
+
"kysely": "^0.28.4",
|
|
49
|
+
"msgpackr": "^1.11.5",
|
|
50
50
|
"nanoid": "^3.3.11",
|
|
51
|
-
"random": "^5.4.
|
|
51
|
+
"random": "^5.4.1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@bokuweb/zstd-wasm": "0.0.27",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@types/ws": "^8.18.1",
|
|
57
57
|
"better-sqlite3": "^12.1.1",
|
|
58
58
|
"shx": "^0.3.4",
|
|
59
|
-
"typescript": "^5.
|
|
59
|
+
"typescript": "^5.9.2",
|
|
60
60
|
"vitest": "^3.2.3",
|
|
61
61
|
"ws": "^8.18.2",
|
|
62
62
|
"@evolu/tsconfig": "0.0.2"
|
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
package/src/Evolu/Db.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isNonEmptyArray,
|
|
3
|
-
isNonEmptyReadonlyArray,
|
|
4
|
-
NonEmptyReadonlyArray,
|
|
5
|
-
} from "../Array.js";
|
|
1
|
+
import { isNonEmptyArray, NonEmptyReadonlyArray } from "../Array.js";
|
|
6
2
|
import { assert, assertNonEmptyReadonlyArray } from "../Assert.js";
|
|
7
3
|
import { CallbackId } from "../Callbacks.js";
|
|
8
4
|
import { ConsoleDep } from "../Console.js";
|
|
@@ -121,7 +117,6 @@ export type DbWorkerInput =
|
|
|
121
117
|
readonly type: "init";
|
|
122
118
|
readonly config: Config;
|
|
123
119
|
readonly dbSchema: DbSchema;
|
|
124
|
-
readonly initialData: ReadonlyArray<DbChange>;
|
|
125
120
|
}
|
|
126
121
|
| {
|
|
127
122
|
readonly type: "mutate";
|
|
@@ -157,6 +152,7 @@ export type DbWorkerOutput =
|
|
|
157
152
|
| {
|
|
158
153
|
readonly type: "onInit";
|
|
159
154
|
readonly appOwner: AppOwner;
|
|
155
|
+
readonly isFirst: boolean;
|
|
160
156
|
}
|
|
161
157
|
| {
|
|
162
158
|
readonly type: "onError";
|
|
@@ -289,11 +285,11 @@ export const createDbWorkerForPlatform = (
|
|
|
289
285
|
let appOwner: AppOwner;
|
|
290
286
|
let clock: Clock;
|
|
291
287
|
|
|
292
|
-
const
|
|
288
|
+
const dbIsInitialized = currentDbSchema.value.tables.some(
|
|
293
289
|
(table) => table.name === "evolu_version",
|
|
294
290
|
);
|
|
295
291
|
|
|
296
|
-
if (
|
|
292
|
+
if (dbIsInitialized) {
|
|
297
293
|
const versionResult = sqlite.exec<{
|
|
298
294
|
protocolVersion: number;
|
|
299
295
|
}>(sql`select protocolVersion from evolu_version limit 1;`);
|
|
@@ -383,15 +379,11 @@ export const createDbWorkerForPlatform = (
|
|
|
383
379
|
storage: storage.value,
|
|
384
380
|
};
|
|
385
381
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
if (!result.ok) return result;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
postMessage({ type: "onInit", appOwner });
|
|
382
|
+
postMessage({
|
|
383
|
+
type: "onInit",
|
|
384
|
+
appOwner,
|
|
385
|
+
isFirst: !dbIsInitialized,
|
|
386
|
+
});
|
|
395
387
|
|
|
396
388
|
const sync = platformDeps.createSync(platformDeps)({
|
|
397
389
|
...initMessage.config,
|
package/src/Evolu/Evolu.ts
CHANGED
|
@@ -465,36 +465,36 @@ export type EvoluDeps = CreateDbWorkerDep &
|
|
|
465
465
|
ConsoleDep &
|
|
466
466
|
CreateAppStateDep;
|
|
467
467
|
|
|
468
|
-
export interface
|
|
469
|
-
extends Config {
|
|
468
|
+
export interface EvoluConfigWithFunctions extends Config {
|
|
470
469
|
/**
|
|
471
|
-
*
|
|
470
|
+
* Callback invoked when the database is initialized. Use `isFirst` to perform
|
|
471
|
+
* one-time setup like initial data seeding.
|
|
472
472
|
*
|
|
473
473
|
* ### Example
|
|
474
474
|
*
|
|
475
475
|
* ```ts
|
|
476
476
|
* const evolu = createEvolu(evoluReactWebDeps)(Schema, {
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
*
|
|
477
|
+
* onInit: ({ appOwner, isFirst }) => {
|
|
478
|
+
* if (isFirst) {
|
|
479
|
+
* const todoCategoryId = getOrThrow(
|
|
480
|
+
* evolu.insert("todoCategory", {
|
|
481
|
+
* name: "Not Urgent",
|
|
482
|
+
* }),
|
|
483
|
+
* );
|
|
481
484
|
*
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
* categoryId: todoCategory.value.id,
|
|
488
|
-
* });
|
|
485
|
+
* evolu.insert("todo", {
|
|
486
|
+
* title: "Try React Suspense",
|
|
487
|
+
* categoryId: todoCategoryId.id,
|
|
488
|
+
* });
|
|
489
|
+
* }
|
|
489
490
|
* },
|
|
490
491
|
* });
|
|
491
492
|
* ```
|
|
492
493
|
*/
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
insert: Mutation<S, "insert">;
|
|
494
|
+
readonly onInit?: (params: {
|
|
495
|
+
readonly appOwner: AppOwner;
|
|
496
|
+
readonly isFirst: boolean;
|
|
497
|
+
}) => void;
|
|
498
498
|
}
|
|
499
499
|
|
|
500
500
|
// For hot reloading and Evolu multitenancy.
|
|
@@ -553,7 +553,7 @@ export const createEvolu =
|
|
|
553
553
|
(deps: EvoluDeps) =>
|
|
554
554
|
<S extends EvoluSchema>(
|
|
555
555
|
schema: ValidateSchema<S> extends never ? S : ValidateSchema<S>,
|
|
556
|
-
partialConfig: Partial<
|
|
556
|
+
partialConfig: Partial<EvoluConfigWithFunctions> = {},
|
|
557
557
|
): Evolu<S> => {
|
|
558
558
|
const config = { ...defaultConfig, ...partialConfig };
|
|
559
559
|
|
|
@@ -577,15 +577,13 @@ const createEvoluInstance =
|
|
|
577
577
|
(deps: EvoluDeps) =>
|
|
578
578
|
(
|
|
579
579
|
schema: EvoluSchema,
|
|
580
|
-
evoluConfig:
|
|
580
|
+
evoluConfig: EvoluConfigWithFunctions,
|
|
581
581
|
): InternalEvoluInstance => {
|
|
582
582
|
deps.console.enabled = evoluConfig.enableLogging ?? false;
|
|
583
583
|
|
|
584
584
|
deps.console.log("[evolu]", "createEvoluInstance");
|
|
585
585
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
const { initialData, indexes, ...config } = evoluConfig;
|
|
586
|
+
const { onInit, indexes, ...config } = evoluConfig;
|
|
589
587
|
|
|
590
588
|
const errorStore = createStore<EvoluError | null>(null);
|
|
591
589
|
const rowsStore = createStore<QueryRowsMap>(new Map());
|
|
@@ -608,6 +606,10 @@ const createEvoluInstance =
|
|
|
608
606
|
switch (message.type) {
|
|
609
607
|
case "onInit": {
|
|
610
608
|
appOwnerStore.set(message.appOwner);
|
|
609
|
+
onInit?.({
|
|
610
|
+
appOwner: message.appOwner,
|
|
611
|
+
isFirst: message.isFirst,
|
|
612
|
+
});
|
|
611
613
|
break;
|
|
612
614
|
}
|
|
613
615
|
|
|
@@ -702,40 +704,10 @@ const createEvoluInstance =
|
|
|
702
704
|
return type;
|
|
703
705
|
};
|
|
704
706
|
|
|
705
|
-
const initialDataDbChanges: Array<DbChange> = [];
|
|
706
|
-
|
|
707
|
-
/**
|
|
708
|
-
* Note that the initial data function is called even if it is unnecessary
|
|
709
|
-
* (initial data are already in the DB) because we don't want to wait for
|
|
710
|
-
* SQLite's response. Initial data should be small (because they are inlined
|
|
711
|
-
* in the code), so it's ok.
|
|
712
|
-
*/
|
|
713
|
-
if (initialData)
|
|
714
|
-
initialData({
|
|
715
|
-
insert: (table, props) => {
|
|
716
|
-
const id = createId(deps);
|
|
717
|
-
const values = getMutationType(table, "insert").fromUnknown(props);
|
|
718
|
-
|
|
719
|
-
if (values.ok) {
|
|
720
|
-
const valuesWithCreatedAt = {
|
|
721
|
-
...values.value,
|
|
722
|
-
createdAt: new Date(deps.time.now()).toISOString(),
|
|
723
|
-
};
|
|
724
|
-
const dbChange = { table, id, values: valuesWithCreatedAt };
|
|
725
|
-
assertValidDbChange(dbChange);
|
|
726
|
-
initialDataDbChanges.push(dbChange);
|
|
727
|
-
return ok({ id });
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
return values;
|
|
731
|
-
},
|
|
732
|
-
});
|
|
733
|
-
|
|
734
707
|
dbWorker.postMessage({
|
|
735
708
|
type: "init",
|
|
736
709
|
config,
|
|
737
710
|
dbSchema,
|
|
738
|
-
initialData: initialDataDbChanges,
|
|
739
711
|
});
|
|
740
712
|
|
|
741
713
|
const loadQueryMicrotaskQueue: Array<Query> = [];
|
package/src/Evolu/Owner.ts
CHANGED
package/src/Evolu/Protocol.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable jsdoc/no-undefined-types */
|
|
1
2
|
/**
|
|
2
3
|
* Evolu Protocol
|
|
3
4
|
*
|
|
@@ -182,7 +183,8 @@ import {
|
|
|
182
183
|
PositiveInt,
|
|
183
184
|
record,
|
|
184
185
|
} from "../Type.js";
|
|
185
|
-
import {
|
|
186
|
+
import { Predicate } from "../Types.js";
|
|
187
|
+
import { Brand } from "../Brand.js";
|
|
186
188
|
import { Owner, OwnerId, WriteKey, writeKeyLength } from "./Owner.js";
|
|
187
189
|
import {
|
|
188
190
|
BinaryTimestamp,
|
package/src/Evolu/Public.ts
CHANGED
|
@@ -5,12 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export { createEvolu } from "./Evolu.js";
|
|
8
|
-
export type {
|
|
9
|
-
Evolu,
|
|
10
|
-
EvoluConfigWithInitialData,
|
|
11
|
-
EvoluDeps,
|
|
12
|
-
EvoluError,
|
|
13
|
-
} from "./Evolu.js";
|
|
8
|
+
export type { Evolu, EvoluDeps, EvoluError } from "./Evolu.js";
|
|
14
9
|
export * from "./Owner.js";
|
|
15
10
|
export { binaryIdToId, idToBinaryId } from "./Protocol.js";
|
|
16
11
|
export type { BinaryId } from "./Protocol.js";
|
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/Schema.ts
CHANGED
|
@@ -32,8 +32,8 @@ import { DbSchema } from "./Db.js";
|
|
|
32
32
|
import { createIndexes, DbIndexesBuilder } from "./Kysely.js";
|
|
33
33
|
import {
|
|
34
34
|
BinaryId,
|
|
35
|
-
maxProtocolMessageRangesSize,
|
|
36
35
|
CrdtMessage,
|
|
36
|
+
maxProtocolMessageRangesSize,
|
|
37
37
|
} from "./Protocol.js";
|
|
38
38
|
import { Query, Row } from "./Query.js";
|
|
39
39
|
import { BinaryTimestamp } from "./Timestamp.js";
|
|
@@ -251,13 +251,6 @@ export interface MutationOptions {
|
|
|
251
251
|
* `onlyValidate: true`.
|
|
252
252
|
*/
|
|
253
253
|
readonly onlyValidate?: boolean;
|
|
254
|
-
|
|
255
|
-
// /**
|
|
256
|
-
// * The owner to use for this mutation. Can be a {@link ShardOwner} for sharding
|
|
257
|
-
// * app data or a {@link SharedOwner} for collaborative write access. If
|
|
258
|
-
// * omitted, defaults to the app's {@link AppOwner}.
|
|
259
|
-
// */
|
|
260
|
-
// readonly owner?: ShardOwner | SharedOwner;
|
|
261
254
|
}
|
|
262
255
|
|
|
263
256
|
/**
|
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,36 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 🛡️ Type-safe errors
|
|
3
3
|
*
|
|
4
|
-
* ## Intro
|
|
5
|
-
*
|
|
6
4
|
* The problem with throwing an exception in JavaScript is that the caught error
|
|
7
5
|
* is always of an unknown type. The unknown type is a problem because we can't
|
|
8
6
|
* be sure all errors have been handled because the TypeScript compiler can't
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* type, such as Result or Either, allowing the developer to handle all errors
|
|
14
|
-
* safely. ✅
|
|
15
|
-
*
|
|
16
|
-
* ✨ Evolu uses {@link Result}, and it looks like this:
|
|
17
|
-
*
|
|
18
|
-
* ```ts
|
|
19
|
-
* type Result<T, E> = Ok<T> | Err<E>;
|
|
20
|
-
*
|
|
21
|
-
* interface Ok<T> {
|
|
22
|
-
* readonly ok: true;
|
|
23
|
-
* readonly value: T;
|
|
24
|
-
* }
|
|
25
|
-
*
|
|
26
|
-
* interface Err<E> {
|
|
27
|
-
* readonly ok: false;
|
|
28
|
-
* readonly error: E;
|
|
29
|
-
* }
|
|
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. ✅
|
|
30
11
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
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.
|
|
34
15
|
*
|
|
35
16
|
* Now let's look at how `Result` can be used for safe JSON parsing:
|
|
36
17
|
*
|
|
@@ -197,9 +178,8 @@
|
|
|
197
178
|
* ### How do I handle an array of operations and short-circuit on the first error?
|
|
198
179
|
*
|
|
199
180
|
* If you have an array of operations (not results), you should make them
|
|
200
|
-
* _lazy_—that is, represent each operation as a function
|
|
201
|
-
*
|
|
202
|
-
* 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:
|
|
203
183
|
*
|
|
204
184
|
* ```ts
|
|
205
185
|
* import type { LazyValue } from "./Function";
|
|
@@ -234,15 +214,6 @@
|
|
|
234
214
|
* developers. While monads and functional helpers can be powerful, they often
|
|
235
215
|
* obscure control flow and make debugging harder. Evolu's approach keeps error
|
|
236
216
|
* handling explicit and straightforward.
|
|
237
|
-
*
|
|
238
|
-
* @module
|
|
239
|
-
*/
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* A `Result` can be either {@link Ok} (success) or {@link Err} (error).
|
|
243
|
-
*
|
|
244
|
-
* Use {@link ok} to create a successful result and {@link err} to create an error
|
|
245
|
-
* result.
|
|
246
217
|
*/
|
|
247
218
|
export type Result<T, E> = Ok<T> | Err<E>;
|
|
248
219
|
|
|
@@ -355,12 +326,14 @@ export const err = <E>(error: E): Err<E> => ({ ok: false, error });
|
|
|
355
326
|
* const config = getOrThrow(loadConfig());
|
|
356
327
|
* // Safe to use config here
|
|
357
328
|
* ```
|
|
329
|
+
*
|
|
330
|
+
* Throws: `Error` with the original error attached as `cause`.
|
|
358
331
|
*/
|
|
359
332
|
export const getOrThrow = <T, E>(result: Result<T, E>): T => {
|
|
360
333
|
if (result.ok) {
|
|
361
334
|
return result.value;
|
|
362
335
|
} else {
|
|
363
|
-
throw new Error(
|
|
336
|
+
throw new Error("getOrThrow failed", { cause: result.error });
|
|
364
337
|
}
|
|
365
338
|
};
|
|
366
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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable jsdoc/no-undefined-types */
|
|
1
2
|
/**
|
|
2
3
|
* 🧩 Validation, Parsing, and Transformation
|
|
3
4
|
*
|
|
@@ -79,7 +80,8 @@ import { NanoIdLibDep } from "./NanoId.js";
|
|
|
79
80
|
import { isPlainObject } from "./Object.js";
|
|
80
81
|
import { Err, err, Ok, ok, Result, trySync } from "./Result.js";
|
|
81
82
|
import { safelyStringifyUnknownValue } from "./String.js";
|
|
82
|
-
import type {
|
|
83
|
+
import type { Literal, Simplify, WidenLiteral } from "./Types.js";
|
|
84
|
+
import type { Brand } from "./Brand.js";
|
|
83
85
|
import { IntentionalNever } from "./Types.js";
|
|
84
86
|
|
|
85
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
|
*
|