@evolu/common 1.0.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/LICENSE +21 -0
- package/README.md +205 -0
- package/dist/src/Config.d.ts +20 -0
- package/dist/src/Config.d.ts.map +1 -0
- package/dist/src/Config.js +8 -0
- package/dist/src/Crypto.d.ts +42 -0
- package/dist/src/Crypto.d.ts.map +1 -0
- package/dist/src/Crypto.js +44 -0
- package/dist/src/Db.d.ts +60 -0
- package/dist/src/Db.d.ts.map +1 -0
- package/dist/src/Db.js +118 -0
- package/dist/src/DbWorker.d.ts +78 -0
- package/dist/src/DbWorker.d.ts.map +1 -0
- package/dist/src/DbWorker.js +282 -0
- package/dist/src/Diff.d.ts +19 -0
- package/dist/src/Diff.d.ts.map +1 -0
- package/dist/src/Diff.js +86 -0
- package/dist/src/Errors.d.ts +19 -0
- package/dist/src/Errors.d.ts.map +1 -0
- package/dist/src/Errors.js +11 -0
- package/dist/src/Evolu.d.ts +71 -0
- package/dist/src/Evolu.d.ts.map +1 -0
- package/dist/src/Evolu.js +235 -0
- package/dist/src/MerkleTree.d.ts +15 -0
- package/dist/src/MerkleTree.d.ts.map +1 -0
- package/dist/src/MerkleTree.js +49 -0
- package/dist/src/Model.d.ts +143 -0
- package/dist/src/Model.d.ts.map +1 -0
- package/dist/src/Model.js +103 -0
- package/dist/src/Murmurhash.d.ts +2 -0
- package/dist/src/Murmurhash.d.ts.map +1 -0
- package/dist/src/Murmurhash.js +60 -0
- package/dist/src/Platform.d.ts +29 -0
- package/dist/src/Platform.d.ts.map +1 -0
- package/dist/src/Platform.js +17 -0
- package/dist/src/Protobuf.d.ts +130 -0
- package/dist/src/Protobuf.d.ts.map +1 -0
- package/dist/src/Protobuf.js +100 -0
- package/dist/src/Public.d.ts +7 -0
- package/dist/src/Public.d.ts.map +1 -0
- package/dist/src/Public.js +2 -0
- package/dist/src/Sql.d.ts +12 -0
- package/dist/src/Sql.d.ts.map +1 -0
- package/dist/src/Sql.js +100 -0
- package/dist/src/Sqlite.d.ts +30 -0
- package/dist/src/Sqlite.d.ts.map +1 -0
- package/dist/src/Sqlite.js +41 -0
- package/dist/src/Store.d.ts +9 -0
- package/dist/src/Store.d.ts.map +1 -0
- package/dist/src/Store.js +18 -0
- package/dist/src/SyncWorker.d.ts +73 -0
- package/dist/src/SyncWorker.d.ts.map +1 -0
- package/dist/src/SyncWorker.js +136 -0
- package/dist/src/Timestamp.d.ts +44 -0
- package/dist/src/Timestamp.d.ts.map +1 -0
- package/dist/src/Timestamp.js +76 -0
- package/dist/src/index.d.ts +19 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +18 -0
- package/package.json +76 -0
- package/src/Config.ts +33 -0
- package/src/Crypto.ts +113 -0
- package/src/Db.ts +323 -0
- package/src/DbWorker.ts +683 -0
- package/src/Diff.ts +113 -0
- package/src/Errors.ts +33 -0
- package/src/Evolu.ts +527 -0
- package/src/MerkleTree.ts +83 -0
- package/src/Model.ts +212 -0
- package/src/Murmurhash.ts +70 -0
- package/src/Platform.ts +63 -0
- package/src/Protobuf.ts +204 -0
- package/src/Public.ts +6 -0
- package/src/Sql.ts +114 -0
- package/src/Sqlite.ts +92 -0
- package/src/Store.ts +32 -0
- package/src/SyncWorker.ts +344 -0
- package/src/Timestamp.ts +192 -0
- package/src/index.ts +18 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Brand, Option } from "effect";
|
|
2
|
+
import {
|
|
3
|
+
Millis,
|
|
4
|
+
Timestamp,
|
|
5
|
+
TimestampHash,
|
|
6
|
+
timestampToHash,
|
|
7
|
+
} from "./Timestamp.js";
|
|
8
|
+
|
|
9
|
+
// Technically, it's not Merkle Tree but “merkleized” prefix tree (trie).
|
|
10
|
+
// https://decomposition.al/blog/2019/05/31/how-i-learned-about-merklix-trees-without-having-to-become-a-cryptocurrency-enthusiast/#fnref:1
|
|
11
|
+
// TODO: Add Schema and use it in Evolu Server.
|
|
12
|
+
export interface MerkleTree {
|
|
13
|
+
readonly hash?: TimestampHash;
|
|
14
|
+
readonly "0"?: MerkleTree;
|
|
15
|
+
readonly "1"?: MerkleTree;
|
|
16
|
+
readonly "2"?: MerkleTree;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type MerkleTreeString = string & Brand.Brand<"MerkleTreeString">;
|
|
20
|
+
|
|
21
|
+
export const initialMerkleTree = Object.create(null) as MerkleTree;
|
|
22
|
+
|
|
23
|
+
const timestampToKey = (timestamp: Timestamp): string =>
|
|
24
|
+
Math.floor(timestamp.millis / 1000 / 60).toString(3);
|
|
25
|
+
|
|
26
|
+
const insertKey = (
|
|
27
|
+
tree: MerkleTree,
|
|
28
|
+
key: string,
|
|
29
|
+
hash: TimestampHash,
|
|
30
|
+
): MerkleTree => {
|
|
31
|
+
if (key.length === 0) return tree;
|
|
32
|
+
const childKey = key[0] as "0" | "1" | "2";
|
|
33
|
+
const child = tree[childKey] || {};
|
|
34
|
+
return {
|
|
35
|
+
...tree,
|
|
36
|
+
[childKey]: {
|
|
37
|
+
...child,
|
|
38
|
+
...insertKey(child, key.slice(1), hash),
|
|
39
|
+
// @ts-expect-error undefined is OK
|
|
40
|
+
hash: child.hash ^ hash,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const insertIntoMerkleTree =
|
|
46
|
+
(timestamp: Timestamp) =>
|
|
47
|
+
(tree: MerkleTree): MerkleTree => {
|
|
48
|
+
const key = timestampToKey(timestamp);
|
|
49
|
+
const hash = timestampToHash(timestamp);
|
|
50
|
+
// @ts-expect-error undefined is OK
|
|
51
|
+
return insertKey({ ...tree, hash: tree.hash ^ hash }, key, hash);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const keyToTimestamp = (key: string): Millis =>
|
|
55
|
+
(parseInt(key.length > 0 ? key : "0", 3) * 1000 * 60) as Millis;
|
|
56
|
+
|
|
57
|
+
export const diffMerkleTrees = (
|
|
58
|
+
tree1: MerkleTree,
|
|
59
|
+
tree2: MerkleTree,
|
|
60
|
+
): Option.Option<Millis> => {
|
|
61
|
+
if (tree1.hash === tree2.hash) return Option.none();
|
|
62
|
+
for1: for (let node1 = tree1, node2 = tree2, key = ""; ; ) {
|
|
63
|
+
for (const k of ["0", "1", "2"] as const) {
|
|
64
|
+
const next1 = node1[k];
|
|
65
|
+
const next2 = node2[k];
|
|
66
|
+
if (!next1 && !next2) continue;
|
|
67
|
+
if (!next1 || !next2) break;
|
|
68
|
+
if (next1.hash !== next2.hash) {
|
|
69
|
+
key += k;
|
|
70
|
+
node1 = next1;
|
|
71
|
+
node2 = next2;
|
|
72
|
+
continue for1;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return Option.some(keyToTimestamp(key));
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const merkleTreeToString = (m: MerkleTree): MerkleTreeString =>
|
|
80
|
+
JSON.stringify(m) as MerkleTreeString;
|
|
81
|
+
|
|
82
|
+
export const unsafeMerkleTreeFromString = (m: MerkleTreeString): MerkleTree =>
|
|
83
|
+
JSON.parse(m) as MerkleTree;
|
package/src/Model.ts
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import * as Schema from "@effect/schema/Schema";
|
|
2
|
+
import { Brand } from "effect";
|
|
3
|
+
import { Row, maybeJson } from "./Sqlite.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Branded Id Schema for any table Id.
|
|
7
|
+
* To create Id Schema for a specific table, use {@link id}.
|
|
8
|
+
*/
|
|
9
|
+
export const Id = Schema.string.pipe(
|
|
10
|
+
Schema.pattern(/^[\w-]{21}$/),
|
|
11
|
+
Schema.brand("Id"),
|
|
12
|
+
);
|
|
13
|
+
export type Id = Schema.Schema.To<typeof Id>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A factory function to create {@link Id} Schema for a specific table.
|
|
17
|
+
*
|
|
18
|
+
* ### Example
|
|
19
|
+
*
|
|
20
|
+
* ```
|
|
21
|
+
* import * as Schema from "@effect/schema/Schema";
|
|
22
|
+
* import * as Evolu from "@evolu/react";
|
|
23
|
+
*
|
|
24
|
+
* const TodoId = Evolu.id("Todo");
|
|
25
|
+
* type TodoId = Schema.Schema.To<typeof TodoId>;
|
|
26
|
+
*
|
|
27
|
+
* if (!Schema.is(TodoId)(value)) return;
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export const id = <T extends string>(
|
|
31
|
+
table: T,
|
|
32
|
+
): Schema.BrandSchema<string, string & Brand.Brand<"Id"> & Brand.Brand<T>> =>
|
|
33
|
+
Id.pipe(Schema.brand(table));
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* SQLite doesn't support the Date type, so Evolu uses SqliteDate instead.
|
|
37
|
+
* Use the {@link cast} helper to cast SqliteDate from Date and back.
|
|
38
|
+
* https://www.sqlite.org/quirks.html#no_separate_datetime_datatype
|
|
39
|
+
*/
|
|
40
|
+
export const SqliteDate = Schema.string.pipe(
|
|
41
|
+
Schema.filter((s) => !isNaN(Date.parse(s))),
|
|
42
|
+
Schema.brand("SqliteDate"),
|
|
43
|
+
);
|
|
44
|
+
export type SqliteDate = Schema.Schema.To<typeof SqliteDate>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* SQLite doesn't support the boolean type, so Evolu uses SqliteBoolean instead.
|
|
48
|
+
* Use the {@link cast} helper to cast SqliteBoolean from boolean and back.
|
|
49
|
+
* https://www.sqlite.org/quirks.html#no_separate_boolean_datatype
|
|
50
|
+
*/
|
|
51
|
+
export const SqliteBoolean = Schema.number.pipe(
|
|
52
|
+
Schema.int(),
|
|
53
|
+
Schema.filter((s) => s === 0 || s === 1),
|
|
54
|
+
Schema.brand("SqliteBoolean"),
|
|
55
|
+
);
|
|
56
|
+
export type SqliteBoolean = Schema.Schema.To<typeof SqliteBoolean>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* SQLite doesn't support Date nor Boolean types, so Evolu emulates them
|
|
60
|
+
* with {@link SqliteBoolean} and {@link SqliteDate}.
|
|
61
|
+
*
|
|
62
|
+
* For {@link SqliteBoolean}, you can use JavaScript boolean.
|
|
63
|
+
* For {@link SqliteDate}, you can use JavaScript Date.
|
|
64
|
+
*/
|
|
65
|
+
export type CastableForMutate<T> = {
|
|
66
|
+
readonly [K in keyof T]: T[K] extends SqliteBoolean
|
|
67
|
+
? boolean | SqliteBoolean
|
|
68
|
+
: T[K] extends null | SqliteBoolean
|
|
69
|
+
? null | boolean | SqliteBoolean
|
|
70
|
+
: T[K] extends SqliteDate
|
|
71
|
+
? Date | SqliteDate
|
|
72
|
+
: T[K] extends null | SqliteDate
|
|
73
|
+
? null | Date | SqliteDate
|
|
74
|
+
: T[K];
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A helper for casting types not supported by SQLite.
|
|
79
|
+
* SQLite doesn't support Date nor Boolean types, so Evolu emulates them
|
|
80
|
+
* with {@link SqliteBoolean} and {@link SqliteDate}.
|
|
81
|
+
*
|
|
82
|
+
* ### Example
|
|
83
|
+
*
|
|
84
|
+
* ```
|
|
85
|
+
* // isDeleted is SqliteBoolean
|
|
86
|
+
* .where("isDeleted", "is not", cast(true))
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export function cast(value: boolean): SqliteBoolean;
|
|
90
|
+
export function cast(value: SqliteBoolean): boolean;
|
|
91
|
+
export function cast(value: Date): SqliteDate;
|
|
92
|
+
export function cast(value: SqliteDate): Date;
|
|
93
|
+
export function cast(
|
|
94
|
+
value: boolean | SqliteBoolean | Date | SqliteDate,
|
|
95
|
+
): boolean | SqliteBoolean | Date | SqliteDate {
|
|
96
|
+
if (typeof value === "boolean")
|
|
97
|
+
return (value === true ? 1 : 0) as SqliteBoolean;
|
|
98
|
+
if (typeof value === "number") return value === 1;
|
|
99
|
+
if (value instanceof Date) return value.toISOString() as SqliteDate;
|
|
100
|
+
return new Date(value);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* String schema represents a string that is not stringified JSON. Using
|
|
105
|
+
* String schema for strings stored in SQLite is crucial to ensure a stored
|
|
106
|
+
* string is not automatically parsed to a JSON object or array when
|
|
107
|
+
* retrieved. Use String schema for all string-based schemas.
|
|
108
|
+
*/
|
|
109
|
+
export const String = Schema.string.pipe(
|
|
110
|
+
Schema.filter(
|
|
111
|
+
(s) => {
|
|
112
|
+
if (!maybeJson(s)) return true;
|
|
113
|
+
try {
|
|
114
|
+
JSON.parse(s);
|
|
115
|
+
} catch (e) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
},
|
|
120
|
+
{ message: () => "a string that is not stringified JSON" },
|
|
121
|
+
),
|
|
122
|
+
Schema.brand("String"),
|
|
123
|
+
);
|
|
124
|
+
export type String = Schema.Schema.To<typeof String>;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* A string with a maximum length of 1000 characters.
|
|
128
|
+
*
|
|
129
|
+
* ### Example
|
|
130
|
+
*
|
|
131
|
+
* ```
|
|
132
|
+
* import * as Schema from "@effect/schema/Schema";
|
|
133
|
+
* import * as Evolu from "@evolu/react";
|
|
134
|
+
*
|
|
135
|
+
* if (!Schema.is(Evolu.String1000)(value)) return;
|
|
136
|
+
* function foo(value: Evolu.String1000) {}
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
export const String1000 = String.pipe(
|
|
140
|
+
Schema.maxLength(1000),
|
|
141
|
+
Schema.brand("String1000"),
|
|
142
|
+
);
|
|
143
|
+
export type String1000 = Schema.Schema.To<typeof String1000>;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* A nonempty string with a maximum length of 1000 characters.
|
|
147
|
+
*
|
|
148
|
+
* ### Example
|
|
149
|
+
*
|
|
150
|
+
* ```
|
|
151
|
+
* import * as Schema from "@effect/schema/Schema";
|
|
152
|
+
* import * as Evolu from "@evolu/react";
|
|
153
|
+
*
|
|
154
|
+
* if (!Schema.is(Evolu.NonEmptyString1000)(value)) return;
|
|
155
|
+
* function foo(value: Evolu.NonEmptyString1000) {}
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
export const NonEmptyString1000 = String.pipe(
|
|
159
|
+
Schema.minLength(1),
|
|
160
|
+
Schema.maxLength(1000),
|
|
161
|
+
Schema.brand("NonEmptyString1000"),
|
|
162
|
+
);
|
|
163
|
+
export type NonEmptyString1000 = Schema.Schema.To<typeof NonEmptyString1000>;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* A positive integer.
|
|
167
|
+
*
|
|
168
|
+
* ### Example
|
|
169
|
+
*
|
|
170
|
+
* ```
|
|
171
|
+
* import * as Schema from "@effect/schema/Schema";
|
|
172
|
+
* import * as Evolu from "@evolu/react";
|
|
173
|
+
*
|
|
174
|
+
* if (!Schema.is(Evolu.PositiveInt)(value)) return;
|
|
175
|
+
* function foo(value: Evolu.PositiveInt) {}
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
export const PositiveInt = Schema.number.pipe(
|
|
179
|
+
Schema.int(),
|
|
180
|
+
Schema.positive(),
|
|
181
|
+
Schema.brand("PositiveInt"),
|
|
182
|
+
);
|
|
183
|
+
export type PositiveInt = Schema.Schema.To<typeof PositiveInt>;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Filter and map array items in one step with the correct return type and
|
|
187
|
+
* without unreliable TypeScript type guards.
|
|
188
|
+
*
|
|
189
|
+
* ### Examples
|
|
190
|
+
*
|
|
191
|
+
* ```
|
|
192
|
+
* useQuery(
|
|
193
|
+
* (db) => db.selectFrom("todo").selectAll(),
|
|
194
|
+
* // Filter and map nothing.
|
|
195
|
+
* (row) => row,
|
|
196
|
+
* );
|
|
197
|
+
*
|
|
198
|
+
* useQuery(
|
|
199
|
+
* (db) => db.selectFrom("todo").selectAll(),
|
|
200
|
+
* // Filter items with title != null.
|
|
201
|
+
* // Note the title type isn't nullable anymore in rows.
|
|
202
|
+
* ({ title, ...rest }) => title != null && { title, ...rest },
|
|
203
|
+
* );
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
export type FilterMap<QueryRow extends Row, FilterMapRow extends Row> = (
|
|
207
|
+
row: QueryRow,
|
|
208
|
+
) => OrNullOrFalse<FilterMapRow>;
|
|
209
|
+
|
|
210
|
+
export type OrNullOrFalse<T> = T | null | false;
|
|
211
|
+
|
|
212
|
+
export type ExcludeNullAndFalse<T> = Exclude<T, null | false>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// https://github.com/garycourt/murmurhash-js/blob/master/murmurhash3_gc.js
|
|
2
|
+
export const murmurhash = (key: string, seed = 0): number => {
|
|
3
|
+
let h1, h1b, k1, i;
|
|
4
|
+
|
|
5
|
+
const remainder = key.length & 3; // key.length % 4
|
|
6
|
+
const bytes = key.length - remainder;
|
|
7
|
+
h1 = seed;
|
|
8
|
+
const c1 = 0xcc9e2d51;
|
|
9
|
+
const c2 = 0x1b873593;
|
|
10
|
+
i = 0;
|
|
11
|
+
|
|
12
|
+
while (i < bytes) {
|
|
13
|
+
k1 =
|
|
14
|
+
(key.charCodeAt(i) & 0xff) |
|
|
15
|
+
((key.charCodeAt(++i) & 0xff) << 8) |
|
|
16
|
+
((key.charCodeAt(++i) & 0xff) << 16) |
|
|
17
|
+
((key.charCodeAt(++i) & 0xff) << 24);
|
|
18
|
+
++i;
|
|
19
|
+
|
|
20
|
+
k1 =
|
|
21
|
+
((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
|
|
22
|
+
k1 = (k1 << 15) | (k1 >>> 17);
|
|
23
|
+
k1 =
|
|
24
|
+
((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
|
|
25
|
+
|
|
26
|
+
h1 ^= k1;
|
|
27
|
+
h1 = (h1 << 13) | (h1 >>> 19);
|
|
28
|
+
h1b =
|
|
29
|
+
((h1 & 0xffff) * 5 + ((((h1 >>> 16) * 5) & 0xffff) << 16)) & 0xffffffff;
|
|
30
|
+
h1 = (h1b & 0xffff) + 0x6b64 + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
k1 = 0;
|
|
34
|
+
|
|
35
|
+
switch (remainder) {
|
|
36
|
+
case 3:
|
|
37
|
+
k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
|
|
38
|
+
// eslint-disable-next-line no-fallthrough
|
|
39
|
+
case 2:
|
|
40
|
+
k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
|
|
41
|
+
// eslint-disable-next-line no-fallthrough
|
|
42
|
+
case 1:
|
|
43
|
+
k1 ^= key.charCodeAt(i) & 0xff;
|
|
44
|
+
|
|
45
|
+
k1 =
|
|
46
|
+
((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) &
|
|
47
|
+
0xffffffff;
|
|
48
|
+
k1 = (k1 << 15) | (k1 >>> 17);
|
|
49
|
+
k1 =
|
|
50
|
+
((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) &
|
|
51
|
+
0xffffffff;
|
|
52
|
+
h1 ^= k1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
h1 ^= key.length;
|
|
56
|
+
|
|
57
|
+
h1 ^= h1 >>> 16;
|
|
58
|
+
h1 =
|
|
59
|
+
((h1 & 0xffff) * 0x85ebca6b +
|
|
60
|
+
((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) &
|
|
61
|
+
0xffffffff;
|
|
62
|
+
h1 ^= h1 >>> 13;
|
|
63
|
+
h1 =
|
|
64
|
+
((h1 & 0xffff) * 0xc2b2ae35 +
|
|
65
|
+
((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16)) &
|
|
66
|
+
0xffffffff;
|
|
67
|
+
h1 ^= h1 >>> 16;
|
|
68
|
+
|
|
69
|
+
return h1 >>> 0;
|
|
70
|
+
};
|
package/src/Platform.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
|
|
3
|
+
export interface Platform {
|
|
4
|
+
readonly name:
|
|
5
|
+
| "server"
|
|
6
|
+
| "web-with-opfs"
|
|
7
|
+
| "web-without-opfs"
|
|
8
|
+
| "react-native";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const Platform = Context.Tag<Platform>("evolu/Platform");
|
|
12
|
+
|
|
13
|
+
export type FlushSync = (callback: () => void) => void;
|
|
14
|
+
|
|
15
|
+
export const FlushSync = Context.Tag<FlushSync>("evolu/FlushSync");
|
|
16
|
+
|
|
17
|
+
export interface SyncLock {
|
|
18
|
+
readonly acquire: Effect.Effect<never, never, boolean>;
|
|
19
|
+
readonly release: Effect.Effect<never, never, void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const SyncLock = Context.Tag<SyncLock>("evolu/SyncLock");
|
|
23
|
+
|
|
24
|
+
export type Fetch = (
|
|
25
|
+
url: string,
|
|
26
|
+
body: Uint8Array,
|
|
27
|
+
) => Effect.Effect<never, FetchError, Response>;
|
|
28
|
+
|
|
29
|
+
export const Fetch = Context.Tag<Fetch>("evolu/Fetch");
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* This error occurs when there is a problem with the network connection,
|
|
33
|
+
* or the server cannot be reached.
|
|
34
|
+
*/
|
|
35
|
+
export interface FetchError {
|
|
36
|
+
readonly _tag: "FetchError";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const FetchLive = Layer.succeed(
|
|
40
|
+
Fetch,
|
|
41
|
+
Fetch.of((url, body) =>
|
|
42
|
+
Effect.tryPromise({
|
|
43
|
+
try: () =>
|
|
44
|
+
fetch(url, {
|
|
45
|
+
method: "POST",
|
|
46
|
+
body,
|
|
47
|
+
headers: {
|
|
48
|
+
"Content-Type": "application/octet-stream",
|
|
49
|
+
"Content-Length": body.length.toString(),
|
|
50
|
+
},
|
|
51
|
+
}),
|
|
52
|
+
catch: (): FetchError => ({ _tag: "FetchError" }),
|
|
53
|
+
}),
|
|
54
|
+
),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
export interface AppState {
|
|
58
|
+
readonly onFocus: (callback: () => void) => void;
|
|
59
|
+
readonly onReconnect: (callback: () => void) => void;
|
|
60
|
+
readonly reset: Effect.Effect<never, never, void>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const AppState = Context.Tag<AppState>("evolu/AppState");
|
package/src/Protobuf.ts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// @generated by protobuf-ts 2.9.1 with parameter eslint_disable,optimize_code_size
|
|
3
|
+
// @generated from protobuf file "Protobuf.proto" (syntax proto3)
|
|
4
|
+
// tslint:disable
|
|
5
|
+
import { MessageType } from "@protobuf-ts/runtime";
|
|
6
|
+
import { MerkleTreeString } from "./MerkleTree.js";
|
|
7
|
+
import { OwnerId } from "./Db.js";
|
|
8
|
+
import { NodeId } from "./Crypto.js";
|
|
9
|
+
import { TimestampString } from "./Timestamp.js";
|
|
10
|
+
import { Id } from "./Model.js";
|
|
11
|
+
/**
|
|
12
|
+
* @generated from protobuf message SyncRequest
|
|
13
|
+
*/
|
|
14
|
+
export interface SyncRequest {
|
|
15
|
+
/**
|
|
16
|
+
* @generated from protobuf field: repeated EncryptedMessage messages = 1;
|
|
17
|
+
*/
|
|
18
|
+
messages: ReadonlyArray<EncryptedMessage>;
|
|
19
|
+
/**
|
|
20
|
+
* @generated from protobuf field: string userId = 2;
|
|
21
|
+
*/
|
|
22
|
+
userId: OwnerId;
|
|
23
|
+
/**
|
|
24
|
+
* @generated from protobuf field: string nodeId = 3;
|
|
25
|
+
*/
|
|
26
|
+
nodeId: NodeId;
|
|
27
|
+
/**
|
|
28
|
+
* @generated from protobuf field: string merkleTree = 4;
|
|
29
|
+
*/
|
|
30
|
+
merkleTree: MerkleTreeString;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* @generated from protobuf message SyncResponse
|
|
34
|
+
*/
|
|
35
|
+
export interface SyncResponse {
|
|
36
|
+
/**
|
|
37
|
+
* @generated from protobuf field: repeated EncryptedMessage messages = 1;
|
|
38
|
+
*/
|
|
39
|
+
messages: ReadonlyArray<EncryptedMessage>;
|
|
40
|
+
/**
|
|
41
|
+
* @generated from protobuf field: string merkleTree = 2;
|
|
42
|
+
*/
|
|
43
|
+
merkleTree: MerkleTreeString;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @generated from protobuf message EncryptedMessage
|
|
47
|
+
*/
|
|
48
|
+
export interface EncryptedMessage {
|
|
49
|
+
/**
|
|
50
|
+
* @generated from protobuf field: string timestamp = 1;
|
|
51
|
+
*/
|
|
52
|
+
timestamp: TimestampString;
|
|
53
|
+
/**
|
|
54
|
+
* @generated from protobuf field: bytes content = 2;
|
|
55
|
+
*/
|
|
56
|
+
content: Uint8Array;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* @generated from protobuf message MessageContent
|
|
60
|
+
*/
|
|
61
|
+
export interface MessageContent {
|
|
62
|
+
/**
|
|
63
|
+
* @generated from protobuf field: string table = 1;
|
|
64
|
+
*/
|
|
65
|
+
table: string;
|
|
66
|
+
/**
|
|
67
|
+
* @generated from protobuf field: string row = 2;
|
|
68
|
+
*/
|
|
69
|
+
row: Id;
|
|
70
|
+
/**
|
|
71
|
+
* @generated from protobuf field: string column = 3;
|
|
72
|
+
*/
|
|
73
|
+
column: string;
|
|
74
|
+
/**
|
|
75
|
+
* @generated from protobuf oneof: value
|
|
76
|
+
*/
|
|
77
|
+
value:
|
|
78
|
+
| {
|
|
79
|
+
oneofKind: "stringValue";
|
|
80
|
+
/**
|
|
81
|
+
* @generated from protobuf field: string stringValue = 4;
|
|
82
|
+
*/
|
|
83
|
+
stringValue: string;
|
|
84
|
+
}
|
|
85
|
+
| {
|
|
86
|
+
oneofKind: "numberValue";
|
|
87
|
+
/**
|
|
88
|
+
* @generated from protobuf field: int32 numberValue = 5;
|
|
89
|
+
*/
|
|
90
|
+
numberValue: number;
|
|
91
|
+
}
|
|
92
|
+
| {
|
|
93
|
+
oneofKind: "bytesValue";
|
|
94
|
+
/**
|
|
95
|
+
* @generated from protobuf field: bytes bytesValue = 6;
|
|
96
|
+
*/
|
|
97
|
+
bytesValue: Uint8Array;
|
|
98
|
+
}
|
|
99
|
+
| {
|
|
100
|
+
oneofKind: "jsonValue";
|
|
101
|
+
/**
|
|
102
|
+
* @generated from protobuf field: string jsonValue = 7;
|
|
103
|
+
*/
|
|
104
|
+
jsonValue: string;
|
|
105
|
+
}
|
|
106
|
+
| {
|
|
107
|
+
oneofKind: undefined;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
111
|
+
class SyncRequest$Type extends MessageType<SyncRequest> {
|
|
112
|
+
constructor() {
|
|
113
|
+
super("SyncRequest", [
|
|
114
|
+
{
|
|
115
|
+
no: 1,
|
|
116
|
+
name: "messages",
|
|
117
|
+
kind: "message",
|
|
118
|
+
repeat: 1 /*RepeatType.PACKED*/,
|
|
119
|
+
T: () => EncryptedMessage,
|
|
120
|
+
},
|
|
121
|
+
{ no: 2, name: "userId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
|
122
|
+
{ no: 3, name: "nodeId", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
|
123
|
+
{ no: 4, name: "merkleTree", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
|
124
|
+
]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* @generated MessageType for protobuf message SyncRequest
|
|
129
|
+
*/
|
|
130
|
+
export const SyncRequest = new SyncRequest$Type();
|
|
131
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
132
|
+
class SyncResponse$Type extends MessageType<SyncResponse> {
|
|
133
|
+
constructor() {
|
|
134
|
+
super("SyncResponse", [
|
|
135
|
+
{
|
|
136
|
+
no: 1,
|
|
137
|
+
name: "messages",
|
|
138
|
+
kind: "message",
|
|
139
|
+
repeat: 1 /*RepeatType.PACKED*/,
|
|
140
|
+
T: () => EncryptedMessage,
|
|
141
|
+
},
|
|
142
|
+
{ no: 2, name: "merkleTree", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
|
143
|
+
]);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* @generated MessageType for protobuf message SyncResponse
|
|
148
|
+
*/
|
|
149
|
+
export const SyncResponse = new SyncResponse$Type();
|
|
150
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
151
|
+
class EncryptedMessage$Type extends MessageType<EncryptedMessage> {
|
|
152
|
+
constructor() {
|
|
153
|
+
super("EncryptedMessage", [
|
|
154
|
+
{ no: 1, name: "timestamp", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
|
155
|
+
{ no: 2, name: "content", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
|
|
156
|
+
]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* @generated MessageType for protobuf message EncryptedMessage
|
|
161
|
+
*/
|
|
162
|
+
export const EncryptedMessage = new EncryptedMessage$Type();
|
|
163
|
+
// @generated message type with reflection information, may provide speed optimized methods
|
|
164
|
+
class MessageContent$Type extends MessageType<MessageContent> {
|
|
165
|
+
constructor() {
|
|
166
|
+
super("MessageContent", [
|
|
167
|
+
{ no: 1, name: "table", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
|
168
|
+
{ no: 2, name: "row", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
|
169
|
+
{ no: 3, name: "column", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
|
|
170
|
+
{
|
|
171
|
+
no: 4,
|
|
172
|
+
name: "stringValue",
|
|
173
|
+
kind: "scalar",
|
|
174
|
+
oneof: "value",
|
|
175
|
+
T: 9 /*ScalarType.STRING*/,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
no: 5,
|
|
179
|
+
name: "numberValue",
|
|
180
|
+
kind: "scalar",
|
|
181
|
+
oneof: "value",
|
|
182
|
+
T: 5 /*ScalarType.INT32*/,
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
no: 6,
|
|
186
|
+
name: "bytesValue",
|
|
187
|
+
kind: "scalar",
|
|
188
|
+
oneof: "value",
|
|
189
|
+
T: 12 /*ScalarType.BYTES*/,
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
no: 7,
|
|
193
|
+
name: "jsonValue",
|
|
194
|
+
kind: "scalar",
|
|
195
|
+
oneof: "value",
|
|
196
|
+
T: 9 /*ScalarType.STRING*/,
|
|
197
|
+
},
|
|
198
|
+
]);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* @generated MessageType for protobuf message MessageContent
|
|
203
|
+
*/
|
|
204
|
+
export const MessageContent = new MessageContent$Type();
|
package/src/Public.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { jsonArrayFrom, jsonObjectFrom } from "kysely/helpers/sqlite";
|
|
2
|
+
export type { Mnemonic } from "./Crypto.js";
|
|
3
|
+
export type { Owner, OwnerId } from "./Db.js";
|
|
4
|
+
export type { EvoluError } from "./Errors.js";
|
|
5
|
+
export * from "./Model.js";
|
|
6
|
+
export type { SyncState } from "./SyncWorker.js";
|