@evolu/common 1.0.17 → 2.0.1
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 +7 -7
- package/dist/src/Config.d.ts +4 -6
- package/dist/src/Config.d.ts.map +1 -1
- package/dist/src/Config.js +1 -1
- package/dist/src/Crdt.d.ts +15 -9
- package/dist/src/Crdt.d.ts.map +1 -1
- package/dist/src/Crdt.js +15 -14
- package/dist/src/Crypto.d.ts +7 -13
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +7 -9
- package/dist/src/Db.d.ts +36 -70
- package/dist/src/Db.d.ts.map +1 -1
- package/dist/src/Db.js +45 -51
- package/dist/src/DbWorker.d.ts +13 -14
- package/dist/src/DbWorker.d.ts.map +1 -1
- package/dist/src/DbWorker.js +56 -57
- package/dist/src/Diff.d.ts +3 -1
- package/dist/src/Diff.d.ts.map +1 -1
- package/dist/src/Diff.js +3 -7
- package/dist/src/{Errors.d.ts → ErrorStore.d.ts} +14 -7
- package/dist/src/ErrorStore.d.ts.map +1 -0
- package/dist/src/{Errors.js → ErrorStore.js} +2 -0
- package/dist/src/Evolu.d.ts +316 -58
- package/dist/src/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu.js +199 -185
- package/dist/src/Model.d.ts +47 -57
- package/dist/src/Model.d.ts.map +1 -1
- package/dist/src/Model.js +30 -35
- package/dist/src/OnCompletes.d.ts +11 -0
- package/dist/src/OnCompletes.d.ts.map +1 -0
- package/dist/src/OnCompletes.js +21 -0
- package/dist/src/Owner.d.ts +31 -0
- package/dist/src/Owner.d.ts.map +1 -0
- package/dist/src/Owner.js +20 -0
- package/dist/src/Platform.d.ts +16 -12
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +18 -12
- package/dist/src/Protobuf.d.ts +25 -73
- package/dist/src/Protobuf.d.ts.map +1 -1
- package/dist/src/Protobuf.js +4 -12
- package/dist/src/Public.d.ts +5 -3
- package/dist/src/Public.d.ts.map +1 -1
- package/dist/src/Public.js +1 -0
- package/dist/src/Sqlite.d.ts +8 -11
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +7 -7
- package/dist/src/Store.d.ts +6 -5
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +21 -16
- package/dist/src/SyncWorker.d.ts +8 -2
- package/dist/src/SyncWorker.d.ts.map +1 -1
- package/dist/src/SyncWorker.js +12 -19
- package/dist/src/index.d.ts +6 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -2
- package/package.json +23 -13
- package/src/Config.ts +7 -7
- package/src/Crdt.ts +29 -26
- package/src/Crypto.ts +14 -19
- package/src/Db.ts +113 -166
- package/src/DbWorker.ts +110 -140
- package/src/Diff.ts +5 -7
- package/src/{Errors.ts → ErrorStore.ts} +17 -8
- package/src/Evolu.ts +682 -442
- package/src/Model.ts +60 -85
- package/src/OnCompletes.ts +46 -0
- package/src/Owner.ts +65 -0
- package/src/Platform.ts +38 -25
- package/src/Protobuf.ts +25 -73
- package/src/Public.ts +5 -3
- package/src/Sqlite.ts +13 -24
- package/src/Store.ts +36 -24
- package/src/SyncWorker.ts +30 -38
- package/src/index.ts +6 -2
- package/dist/src/Errors.d.ts.map +0 -1
package/dist/src/Store.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
export type
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
export type Listener = () => void;
|
|
3
|
+
export type Unsubscribe = () => void;
|
|
3
4
|
export interface Store<T> {
|
|
4
|
-
readonly subscribe: (listener:
|
|
5
|
-
readonly setState: (state: T) => void;
|
|
5
|
+
readonly subscribe: (listener: Listener) => Unsubscribe;
|
|
6
6
|
readonly getState: () => T;
|
|
7
|
+
readonly setState: (state: T) => Effect.Effect<never, never, void>;
|
|
7
8
|
}
|
|
8
|
-
export declare const makeStore: <T>(initialState: T) => Store<T
|
|
9
|
+
export declare const makeStore: <T>(initialState: T) => Effect.Effect<never, never, Store<T>>;
|
|
9
10
|
//# sourceMappingURL=Store.d.ts.map
|
package/dist/src/Store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Store.d.ts","sourceRoot":"","sources":["../../src/Store.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"Store.d.ts","sourceRoot":"","sources":["../../src/Store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,MAAM,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC;AAElC,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AAErC,MAAM,WAAW,KAAK,CAAC,CAAC;IAEtB,QAAQ,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,WAAW,CAAC;IACxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAG3B,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;CACpE;AAED,eAAO,MAAM,SAAS,+DA4BlB,CAAC"}
|
package/dist/src/Store.js
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
export const makeStore = (initialState) => Effect.sync(() => {
|
|
3
3
|
const listeners = new Set();
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
listeners.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
4
|
+
let currentState = initialState;
|
|
5
|
+
const store = {
|
|
6
|
+
subscribe(listener) {
|
|
7
|
+
listeners.add(listener);
|
|
8
|
+
return () => {
|
|
9
|
+
listeners.delete(listener);
|
|
10
|
+
};
|
|
11
|
+
},
|
|
12
|
+
getState() {
|
|
13
|
+
return currentState;
|
|
14
|
+
},
|
|
15
|
+
setState: (state) => Effect.sync(() => {
|
|
16
|
+
if (state === currentState)
|
|
17
|
+
return;
|
|
18
|
+
currentState = state;
|
|
19
|
+
listeners.forEach((listener) => listener());
|
|
20
|
+
}),
|
|
15
21
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
22
|
+
return store;
|
|
23
|
+
});
|
package/dist/src/SyncWorker.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Context, Equivalence, Layer } from "effect";
|
|
2
2
|
import { MerkleTree, Millis, Timestamp, TimestampString } from "./Crdt.js";
|
|
3
3
|
import { SecretBox } from "./Crypto.js";
|
|
4
|
-
import {
|
|
5
|
-
import { UnexpectedError } from "./Errors.js";
|
|
4
|
+
import { UnexpectedError } from "./ErrorStore.js";
|
|
6
5
|
import { Id } from "./Model.js";
|
|
6
|
+
import { Owner } from "./Owner.js";
|
|
7
7
|
import { Fetch, SyncLock } from "./Platform.js";
|
|
8
8
|
import { Value } from "./Sqlite.js";
|
|
9
9
|
export interface SyncWorker {
|
|
@@ -43,6 +43,12 @@ export type SyncWorkerOutputSyncResponse = {
|
|
|
43
43
|
readonly merkleTree: MerkleTree;
|
|
44
44
|
readonly syncLoopCount: number;
|
|
45
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* The SyncState type represents the various states that a synchronization
|
|
48
|
+
* process can be in. Evolu apps should not bother users with an "unsynced"
|
|
49
|
+
* state. Instead, they should warn users if data have not been synced for too
|
|
50
|
+
* long (a week, for example).
|
|
51
|
+
*/
|
|
46
52
|
export type SyncState = SyncStateInitial | SyncStateIsSyncing | SyncStateIsSynced | SyncStateIsNotSyncedError;
|
|
47
53
|
export interface SyncStateInitial {
|
|
48
54
|
readonly _tag: "SyncStateInitial";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SyncWorker.d.ts","sourceRoot":"","sources":["../../src/SyncWorker.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,OAAO,EAEP,WAAW,EAEX,KAAK,
|
|
1
|
+
{"version":3,"file":"SyncWorker.d.ts","sourceRoot":"","sources":["../../src/SyncWorker.ts"],"names":[],"mappings":"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"}
|
package/dist/src/SyncWorker.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { concatBytes } from "@noble/ciphers/utils";
|
|
2
2
|
import { BinaryReader, BinaryWriter } from "@protobuf-ts/runtime";
|
|
3
|
-
import { Context, Effect, Function, Layer, Option, Predicate, ReadonlyArray, absurd, identity, } from "effect";
|
|
3
|
+
import { Context, Effect, Function, Layer, Match, Option, Predicate, ReadonlyArray, absurd, identity, } from "effect";
|
|
4
4
|
import { merkleTreeToString, unsafeMerkleTreeFromString, } from "./Crdt.js";
|
|
5
5
|
import { SecretBox } from "./Crypto.js";
|
|
6
|
-
import { makeUnexpectedError } from "./
|
|
6
|
+
import { makeUnexpectedError } from "./ErrorStore.js";
|
|
7
7
|
import { Fetch, SyncLock } from "./Platform.js";
|
|
8
8
|
import { MessageContent, SyncRequest, SyncResponse, } from "./Protobuf.js";
|
|
9
|
-
export const SyncWorker = Context.Tag(
|
|
10
|
-
export const SyncWorkerPostMessage = Context.Tag(
|
|
9
|
+
export const SyncWorker = Context.Tag();
|
|
10
|
+
export const SyncWorkerPostMessage = Context.Tag();
|
|
11
11
|
export const NewMessageEquivalence = (a, b) => a.table === b.table &&
|
|
12
12
|
a.row === b.row &&
|
|
13
13
|
a.column === b.column &&
|
|
14
14
|
JSON.stringify(a.value) === JSON.stringify(b.value);
|
|
15
|
-
const SyncWorkerOnMessage = Context.Tag(
|
|
15
|
+
const SyncWorkerOnMessage = Context.Tag();
|
|
16
16
|
const version1 = new Uint8Array([0, 1]);
|
|
17
17
|
const valueToProtobuf = (value) => {
|
|
18
18
|
switch (typeof value) {
|
|
@@ -121,24 +121,17 @@ const sync = (input) => Effect.gen(function* (_) {
|
|
|
121
121
|
});
|
|
122
122
|
export const SyncWorkerLive = Layer.effect(SyncWorker, Effect.gen(function* (_) {
|
|
123
123
|
const syncLock = yield* _(SyncLock);
|
|
124
|
-
const fetch = yield* _(Fetch);
|
|
125
|
-
const secretBox = yield* _(SecretBox);
|
|
126
124
|
const onError = (error) => Effect.sync(() => {
|
|
127
125
|
syncWorker.onMessage(error);
|
|
128
126
|
});
|
|
129
|
-
const
|
|
130
|
-
switch (input._tag) {
|
|
131
|
-
case "sync":
|
|
132
|
-
return sync(input);
|
|
133
|
-
case "syncCompleted":
|
|
134
|
-
return syncLock.release;
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
const postMessage = (input) => {
|
|
138
|
-
void mapInputToEffect(input).pipe(Effect.catchAllDefect(makeUnexpectedError), Effect.catchAll(onError), Effect.provideService(SyncLock, syncLock), Effect.provideService(Fetch, fetch), Effect.provideService(SecretBox, secretBox), Effect.provideService(SyncWorkerOnMessage, syncWorker.onMessage), Effect.runPromise);
|
|
139
|
-
};
|
|
127
|
+
const context = Context.empty().pipe(Context.add(SyncLock, syncLock), Context.add(Fetch, yield* _(Fetch)), Context.add(SecretBox, yield* _(SecretBox)));
|
|
140
128
|
const syncWorker = {
|
|
141
|
-
postMessage
|
|
129
|
+
postMessage: (input) => {
|
|
130
|
+
Match.value(input).pipe(Match.tagsExhaustive({
|
|
131
|
+
sync,
|
|
132
|
+
syncCompleted: () => syncLock.release,
|
|
133
|
+
}), Effect.catchAllDefect(makeUnexpectedError), Effect.catchAll(onError), Effect.provide(context), Effect.provideService(SyncWorkerOnMessage, syncWorker.onMessage), Effect.runPromise);
|
|
134
|
+
},
|
|
142
135
|
onMessage: Function.constVoid,
|
|
143
136
|
};
|
|
144
137
|
return syncWorker;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
export { jsonArrayFrom, jsonObjectFrom } from "kysely/helpers/sqlite";
|
|
2
1
|
export * from "./Config.js";
|
|
3
2
|
export * from "./Crdt.js";
|
|
4
3
|
export * from "./Crypto.js";
|
|
5
4
|
export * from "./Db.js";
|
|
6
5
|
export * from "./DbWorker.js";
|
|
7
|
-
export * from "./
|
|
6
|
+
export * from "./Diff.js";
|
|
7
|
+
export * from "./ErrorStore.js";
|
|
8
8
|
export * from "./Evolu.js";
|
|
9
9
|
export * from "./Model.js";
|
|
10
|
+
export * from "./Murmurhash.js";
|
|
11
|
+
export * from "./OnCompletes.js";
|
|
12
|
+
export * from "./Owner.js";
|
|
10
13
|
export * from "./Platform.js";
|
|
11
14
|
export * from "./Protobuf.js";
|
|
15
|
+
export * from "./Public.js";
|
|
12
16
|
export * from "./Sqlite.js";
|
|
13
17
|
export * from "./Store.js";
|
|
14
18
|
export * from "./SyncWorker.js";
|
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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC"}
|
package/dist/src/index.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
export { jsonArrayFrom, jsonObjectFrom } from "kysely/helpers/sqlite";
|
|
2
1
|
export * from "./Config.js";
|
|
3
2
|
export * from "./Crdt.js";
|
|
4
3
|
export * from "./Crypto.js";
|
|
5
4
|
export * from "./Db.js";
|
|
6
5
|
export * from "./DbWorker.js";
|
|
7
|
-
export * from "./
|
|
6
|
+
export * from "./Diff.js";
|
|
7
|
+
export * from "./ErrorStore.js";
|
|
8
8
|
export * from "./Evolu.js";
|
|
9
9
|
export * from "./Model.js";
|
|
10
|
+
export * from "./Murmurhash.js";
|
|
11
|
+
export * from "./OnCompletes.js";
|
|
12
|
+
export * from "./Owner.js";
|
|
10
13
|
export * from "./Platform.js";
|
|
11
14
|
export * from "./Protobuf.js";
|
|
15
|
+
export * from "./Public.js";
|
|
12
16
|
export * from "./Sqlite.js";
|
|
13
17
|
export * from "./Store.js";
|
|
14
18
|
export * from "./SyncWorker.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evolu/common",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Local-first platform designed for privacy, ease of use, and no vendor lock-in to sync and backup people's lifetime data",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"evolu",
|
|
@@ -23,11 +23,21 @@
|
|
|
23
23
|
"types": "./dist/src/index.d.ts",
|
|
24
24
|
"import": "./dist/src/index.js",
|
|
25
25
|
"browser": "./dist/src/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./public": {
|
|
28
|
+
"import": "./dist/src/Public.js"
|
|
26
29
|
}
|
|
27
30
|
},
|
|
28
31
|
"react-native": {
|
|
29
32
|
"./index.js": "./dist/src/index.js"
|
|
30
33
|
},
|
|
34
|
+
"typesVersions": {
|
|
35
|
+
"*": {
|
|
36
|
+
"public": [
|
|
37
|
+
"./dist/src/Public.d.ts"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
},
|
|
31
41
|
"files": [
|
|
32
42
|
"dist/src/**",
|
|
33
43
|
"src/**",
|
|
@@ -39,24 +49,24 @@
|
|
|
39
49
|
"@protobuf-ts/runtime": "^2.9.1",
|
|
40
50
|
"@scure/bip39": "^1.2.1",
|
|
41
51
|
"kysely": "^0.26.3",
|
|
42
|
-
"nanoid": "^5.0.
|
|
52
|
+
"nanoid": "^5.0.3"
|
|
43
53
|
},
|
|
44
54
|
"devDependencies": {
|
|
45
|
-
"@effect/schema": "0.
|
|
55
|
+
"@effect/schema": "0.50.0",
|
|
46
56
|
"@protobuf-ts/plugin": "^2.9.1",
|
|
47
57
|
"@protobuf-ts/protoc": "^2.9.1",
|
|
48
58
|
"array-shuffle": "^3.0.0",
|
|
49
|
-
"better-sqlite3": "^9.
|
|
50
|
-
"effect": "2.0.0-next.
|
|
51
|
-
"eslint": "^8.
|
|
52
|
-
"typescript": "^5.
|
|
59
|
+
"better-sqlite3": "^9.1.1",
|
|
60
|
+
"effect": "2.0.0-next.56",
|
|
61
|
+
"eslint": "^8.54.0",
|
|
62
|
+
"typescript": "^5.3.2",
|
|
53
63
|
"vitest": "^0.34.6",
|
|
54
64
|
"@evolu/tsconfig": "0.0.2",
|
|
55
|
-
"eslint-config-evolu": "0.0
|
|
65
|
+
"eslint-config-evolu": "1.0.0"
|
|
56
66
|
},
|
|
57
67
|
"peerDependencies": {
|
|
58
|
-
"@effect/schema": "0.
|
|
59
|
-
"effect": "2.0.0-next.
|
|
68
|
+
"@effect/schema": "0.50.0",
|
|
69
|
+
"effect": "2.0.0-next.56"
|
|
60
70
|
},
|
|
61
71
|
"publishConfig": {
|
|
62
72
|
"access": "public"
|
|
@@ -64,13 +74,13 @@
|
|
|
64
74
|
"engines": {
|
|
65
75
|
"node": ">=18.16"
|
|
66
76
|
},
|
|
67
|
-
"sideEffects":
|
|
77
|
+
"sideEffects": [],
|
|
68
78
|
"scripts": {
|
|
69
79
|
"dev": "tsc --watch",
|
|
70
80
|
"build": "rm -rf dist ./README.md && tsc && cp ../../README.md ./",
|
|
71
|
-
"lint": "
|
|
81
|
+
"lint": "eslint src --ext .ts,.tsx",
|
|
72
82
|
"test": "vitest run",
|
|
73
|
-
"clean": "rm -rf .turbo
|
|
83
|
+
"clean": "rm -rf .turbo node_modules dist",
|
|
74
84
|
"format": "prettier --write \"src/*.{ts,tsx,md}\"",
|
|
75
85
|
"protobuf": "pnpm protoc --ts_out ./src --proto_path protobuf protobuf/Protobuf.proto --ts_opt eslint_disable --ts_opt optimize_code_size && pnpm format"
|
|
76
86
|
}
|
package/src/Config.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { Context, Layer } from "effect";
|
|
2
2
|
|
|
3
3
|
export interface Config {
|
|
4
|
-
/**
|
|
5
|
-
* Alternate URL to Evolu sync&backup server.
|
|
6
|
-
*/
|
|
4
|
+
/** Alternate URL to Evolu sync and backup server. */
|
|
7
5
|
readonly syncUrl: string;
|
|
6
|
+
|
|
8
7
|
/**
|
|
9
|
-
* Alternate URL to reload browser tabs after
|
|
8
|
+
* Alternate URL to reload browser tabs after {@link Owner} reset or restore.
|
|
10
9
|
* The default value is `/`.
|
|
11
10
|
*/
|
|
12
11
|
readonly reloadUrl: string;
|
|
12
|
+
|
|
13
13
|
/**
|
|
14
|
-
* Maximum physical clock drift allowed in ms.
|
|
15
|
-
*
|
|
14
|
+
* Maximum physical clock drift allowed in ms. The default value is 5 * 60 *
|
|
15
|
+
* 1000 (5 minutes).
|
|
16
16
|
*/
|
|
17
17
|
readonly maxDrift: number;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export const Config = Context.Tag<Config>(
|
|
20
|
+
export const Config = Context.Tag<Config>();
|
|
21
21
|
|
|
22
22
|
export const ConfigLive = (
|
|
23
23
|
config?: Partial<Config>,
|
package/src/Crdt.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as S from "@effect/schema/Schema";
|
|
2
2
|
import {
|
|
3
3
|
Brand,
|
|
4
4
|
Context,
|
|
@@ -32,30 +32,28 @@ export const AllowedTimeRange = {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* Millis represents a time that is valid for usage with the Merkle tree.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
35
|
+
* Millis represents a time that is valid for usage with the Merkle tree. It
|
|
36
|
+
* must be between Apr 13, 1997, and Nov 05, 2051, to ensure MinutesBase3 length
|
|
37
|
+
* equals 16. We can find diff for two Merkle trees only within this range. If
|
|
38
|
+
* the device clock is out of range, Evolu will not store data until it's
|
|
39
|
+
* fixed.
|
|
39
40
|
*/
|
|
40
|
-
export const Millis =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
export const Millis = S.number.pipe(
|
|
42
|
+
S.greaterThan(AllowedTimeRange.greaterThan),
|
|
43
|
+
S.lessThan(AllowedTimeRange.lessThan),
|
|
44
|
+
S.brand("Millis"),
|
|
44
45
|
);
|
|
45
46
|
|
|
46
|
-
export type Millis =
|
|
47
|
+
export type Millis = S.Schema.To<typeof Millis>;
|
|
47
48
|
|
|
48
|
-
export const initialMillis =
|
|
49
|
+
export const initialMillis = S.parseSync(Millis)(
|
|
49
50
|
AllowedTimeRange.greaterThan + 1,
|
|
50
51
|
);
|
|
51
52
|
|
|
52
|
-
export const Counter =
|
|
53
|
-
|
|
54
|
-
Schema.brand("Counter"),
|
|
55
|
-
);
|
|
56
|
-
export type Counter = Schema.Schema.To<typeof Counter>;
|
|
53
|
+
export const Counter = S.number.pipe(S.between(0, 65535), S.brand("Counter"));
|
|
54
|
+
export type Counter = S.Schema.To<typeof Counter>;
|
|
57
55
|
|
|
58
|
-
const initialCounter =
|
|
56
|
+
const initialCounter = S.parseSync(Counter)(0);
|
|
59
57
|
|
|
60
58
|
export type TimestampHash = number & Brand.Brand<"TimestampHash">;
|
|
61
59
|
|
|
@@ -80,7 +78,7 @@ export const unsafeTimestampFromString = (s: TimestampString): Timestamp => {
|
|
|
80
78
|
export const timestampToHash = (t: Timestamp): TimestampHash =>
|
|
81
79
|
murmurhash(timestampToString(t)) as TimestampHash;
|
|
82
80
|
|
|
83
|
-
const syncNodeId =
|
|
81
|
+
const syncNodeId = S.parseSync(NodeId)("0000000000000000");
|
|
84
82
|
|
|
85
83
|
export const makeSyncTimestamp = (
|
|
86
84
|
millis: Millis = initialMillis,
|
|
@@ -105,12 +103,12 @@ export interface Time {
|
|
|
105
103
|
readonly now: Effect.Effect<never, TimestampTimeOutOfRangeError, Millis>;
|
|
106
104
|
}
|
|
107
105
|
|
|
108
|
-
export const Time = Context.Tag<Time>(
|
|
106
|
+
export const Time = Context.Tag<Time>();
|
|
109
107
|
|
|
110
108
|
export const TimeLive = Layer.succeed(
|
|
111
109
|
Time,
|
|
112
110
|
Time.of({
|
|
113
|
-
now: Effect.suspend(() =>
|
|
111
|
+
now: Effect.suspend(() => S.parse(Millis)(Date.now())).pipe(
|
|
114
112
|
Effect.catchTag("ParseError", () =>
|
|
115
113
|
Effect.fail<TimestampTimeOutOfRangeError>({
|
|
116
114
|
_tag: "TimestampTimeOutOfRangeError",
|
|
@@ -120,6 +118,11 @@ export const TimeLive = Layer.succeed(
|
|
|
120
118
|
}),
|
|
121
119
|
);
|
|
122
120
|
|
|
121
|
+
/**
|
|
122
|
+
* The TimestampError type represents all Timestamp-related errors. If such an
|
|
123
|
+
* error happens, the device clock is skewed and should be set to the current
|
|
124
|
+
* time.
|
|
125
|
+
*/
|
|
123
126
|
export type TimestampError =
|
|
124
127
|
| TimestampDriftError
|
|
125
128
|
| TimestampCounterOverflowError
|
|
@@ -176,11 +179,11 @@ const incrementCounter = (
|
|
|
176
179
|
): Either.Either<TimestampCounterOverflowError, Counter> =>
|
|
177
180
|
pipe(
|
|
178
181
|
Number.increment(counter),
|
|
179
|
-
|
|
182
|
+
S.parseEither(Counter),
|
|
180
183
|
Either.mapLeft(() => ({ _tag: "TimestampCounterOverflowError" })),
|
|
181
184
|
);
|
|
182
185
|
|
|
183
|
-
const counterMin =
|
|
186
|
+
const counterMin = S.parseSync(Counter)(0);
|
|
184
187
|
|
|
185
188
|
export const sendTimestamp = (
|
|
186
189
|
timestamp: Timestamp,
|
|
@@ -228,10 +231,10 @@ export const receiveTimestamp = ({
|
|
|
228
231
|
millis === local.millis && millis === remote.millis
|
|
229
232
|
? incrementCounter(Math.max(local.counter, remote.counter) as Counter)
|
|
230
233
|
: millis === local.millis
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
234
|
+
? incrementCounter(local.counter)
|
|
235
|
+
: millis === remote.millis
|
|
236
|
+
? incrementCounter(remote.counter)
|
|
237
|
+
: Either.right(counterMin),
|
|
235
238
|
);
|
|
236
239
|
|
|
237
240
|
return { ...local, millis, counter };
|
package/src/Crypto.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as S from "@effect/schema/Schema";
|
|
2
2
|
import { secretbox } from "@noble/ciphers/salsa";
|
|
3
3
|
import { concatBytes } from "@noble/ciphers/utils";
|
|
4
4
|
import { hmac } from "@noble/hashes/hmac";
|
|
@@ -7,6 +7,7 @@ import { randomBytes } from "@noble/hashes/utils";
|
|
|
7
7
|
import { Brand, Context, Effect, Layer } from "effect";
|
|
8
8
|
import { customAlphabet, nanoid } from "nanoid";
|
|
9
9
|
|
|
10
|
+
// TODO: Add dynamic import error.
|
|
10
11
|
export interface Bip39 {
|
|
11
12
|
readonly make: Effect.Effect<never, never, Mnemonic>;
|
|
12
13
|
|
|
@@ -19,7 +20,7 @@ export interface Bip39 {
|
|
|
19
20
|
) => Effect.Effect<never, InvalidMnemonicError, Mnemonic>;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
export const Bip39 = Context.Tag<Bip39>(
|
|
23
|
+
export const Bip39 = Context.Tag<Bip39>();
|
|
23
24
|
|
|
24
25
|
export interface InvalidMnemonicError {
|
|
25
26
|
readonly _tag: "InvalidMnemonicError";
|
|
@@ -28,9 +29,9 @@ export interface InvalidMnemonicError {
|
|
|
28
29
|
/**
|
|
29
30
|
* Mnemonic is a password generated by Evolu in BIP39 format.
|
|
30
31
|
*
|
|
31
|
-
* A mnemonic, also known as a "seed phrase," is a set of 12 words in a
|
|
32
|
-
*
|
|
33
|
-
*
|
|
32
|
+
* A mnemonic, also known as a "seed phrase," is a set of 12 words in a specific
|
|
33
|
+
* order chosen from a predefined list. The purpose of the BIP39 mnemonic is to
|
|
34
|
+
* provide a human-readable way of storing a private key.
|
|
34
35
|
*/
|
|
35
36
|
export type Mnemonic = string & Brand.Brand<"Mnemonic">;
|
|
36
37
|
|
|
@@ -39,13 +40,11 @@ export interface NanoId {
|
|
|
39
40
|
readonly nanoidAsNodeId: Effect.Effect<never, never, NodeId>;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
export const NanoId = Context.Tag<NanoId>(
|
|
43
|
+
export const NanoId = Context.Tag<NanoId>();
|
|
43
44
|
|
|
44
|
-
export const NodeId:
|
|
45
|
-
string,
|
|
46
|
-
|
|
47
|
-
> = Schema.string.pipe(Schema.pattern(/^[\w-]{16}$/), Schema.brand("NodeId"));
|
|
48
|
-
export type NodeId = Schema.Schema.To<typeof NodeId>;
|
|
45
|
+
export const NodeId: S.BrandSchema<string, string & Brand.Brand<"NodeId">> =
|
|
46
|
+
S.string.pipe(S.pattern(/^[\w-]{16}$/), S.brand("NodeId"));
|
|
47
|
+
export type NodeId = S.Schema.To<typeof NodeId>;
|
|
49
48
|
|
|
50
49
|
const nanoidForNodeId = customAlphabet("0123456789abcdef", 16);
|
|
51
50
|
|
|
@@ -57,10 +56,8 @@ export const NanoIdLive = Layer.succeed(
|
|
|
57
56
|
}),
|
|
58
57
|
);
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
* https://github.com/satoshilabs/slips/blob/master/slip-0021.md
|
|
63
|
-
*/
|
|
59
|
+
// SLIP-21 implementation
|
|
60
|
+
// https://github.com/satoshilabs/slips/blob/master/slip-0021.md
|
|
64
61
|
export const slip21Derive = (
|
|
65
62
|
seed: Uint8Array,
|
|
66
63
|
path: ReadonlyArray<string>,
|
|
@@ -77,9 +74,7 @@ export const slip21Derive = (
|
|
|
77
74
|
return m.slice(32, 64);
|
|
78
75
|
});
|
|
79
76
|
|
|
80
|
-
/**
|
|
81
|
-
* Alias to xsalsa20poly1305, for compatibility with libsodium / nacl
|
|
82
|
-
*/
|
|
77
|
+
/** Alias to xsalsa20poly1305, for compatibility with libsodium/nacl */
|
|
83
78
|
export interface SecretBox {
|
|
84
79
|
readonly seal: (
|
|
85
80
|
key: Uint8Array,
|
|
@@ -92,7 +87,7 @@ export interface SecretBox {
|
|
|
92
87
|
) => Effect.Effect<never, never, Uint8Array>;
|
|
93
88
|
}
|
|
94
89
|
|
|
95
|
-
export const SecretBox = Context.Tag<SecretBox>(
|
|
90
|
+
export const SecretBox = Context.Tag<SecretBox>();
|
|
96
91
|
|
|
97
92
|
export const SecretBoxLive = Layer.succeed(
|
|
98
93
|
SecretBox,
|