@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/src/DbWorker.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { hexToBytes } from "@noble/ciphers/utils";
|
|
2
2
|
import {
|
|
3
|
-
Brand,
|
|
4
3
|
Context,
|
|
5
4
|
Effect,
|
|
6
5
|
Function,
|
|
7
6
|
Layer,
|
|
7
|
+
Match,
|
|
8
8
|
Option,
|
|
9
9
|
ReadonlyArray,
|
|
10
10
|
ReadonlyRecord,
|
|
11
|
-
Ref,
|
|
12
11
|
pipe,
|
|
13
12
|
} from "effect";
|
|
14
13
|
import { Config, ConfigLive } from "./Config.js";
|
|
@@ -33,20 +32,25 @@ import {
|
|
|
33
32
|
} from "./Crdt.js";
|
|
34
33
|
import { Bip39, Mnemonic, NanoId } from "./Crypto.js";
|
|
35
34
|
import {
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
Queries,
|
|
36
|
+
Query,
|
|
37
|
+
RowsStore,
|
|
38
|
+
RowsStoreLive,
|
|
38
39
|
Table,
|
|
39
40
|
Tables,
|
|
41
|
+
deserializeQuery,
|
|
40
42
|
ensureSchema,
|
|
41
43
|
lazyInit,
|
|
42
44
|
someDefectToNoSuchTableOrColumnError,
|
|
43
45
|
transaction,
|
|
44
46
|
} from "./Db.js";
|
|
45
47
|
import { QueryPatches, makePatches } from "./Diff.js";
|
|
46
|
-
import { EvoluError,
|
|
48
|
+
import { EvoluError, makeUnexpectedError } from "./ErrorStore.js";
|
|
47
49
|
import { Id, SqliteDate, cast } from "./Model.js";
|
|
50
|
+
import { OnCompleteId } from "./OnCompletes.js";
|
|
51
|
+
import { Owner, OwnerId } from "./Owner.js";
|
|
48
52
|
import * as Sql from "./Sql.js";
|
|
49
|
-
import {
|
|
53
|
+
import { Sqlite, Value } from "./Sqlite.js";
|
|
50
54
|
import {
|
|
51
55
|
Message,
|
|
52
56
|
NewMessage,
|
|
@@ -57,13 +61,12 @@ import {
|
|
|
57
61
|
SyncWorkerPostMessage,
|
|
58
62
|
} from "./SyncWorker.js";
|
|
59
63
|
|
|
60
|
-
// TODO: Refactor to Effect.
|
|
61
64
|
export interface DbWorker {
|
|
62
65
|
readonly postMessage: (input: DbWorkerInput) => void;
|
|
63
66
|
onMessage: (output: DbWorkerOutput) => void;
|
|
64
67
|
}
|
|
65
68
|
|
|
66
|
-
export const DbWorker = Context.Tag<DbWorker>(
|
|
69
|
+
export const DbWorker = Context.Tag<DbWorker>();
|
|
67
70
|
|
|
68
71
|
export type DbWorkerInput =
|
|
69
72
|
| DbWorkerInputInit
|
|
@@ -77,7 +80,6 @@ export type DbWorkerInput =
|
|
|
77
80
|
interface DbWorkerInputInit {
|
|
78
81
|
readonly _tag: "init";
|
|
79
82
|
readonly config: Config;
|
|
80
|
-
readonly tables: Tables;
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
interface DbWorkerInputQuery {
|
|
@@ -87,13 +89,13 @@ interface DbWorkerInputQuery {
|
|
|
87
89
|
|
|
88
90
|
interface DbWorkerInputMutate {
|
|
89
91
|
readonly _tag: "mutate";
|
|
90
|
-
readonly
|
|
91
|
-
readonly queries:
|
|
92
|
+
readonly mutations: ReadonlyArray.NonEmptyReadonlyArray<Mutation>;
|
|
93
|
+
readonly queries: Queries;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
interface DbWorkerInputSync {
|
|
95
97
|
readonly _tag: "sync";
|
|
96
|
-
readonly queries:
|
|
98
|
+
readonly queries: Queries;
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
interface DbWorkerInputReset {
|
|
@@ -108,9 +110,7 @@ interface DbWorkerInputEnsureSchema {
|
|
|
108
110
|
|
|
109
111
|
type DbWorkerOnMessage = DbWorker["onMessage"];
|
|
110
112
|
|
|
111
|
-
const DbWorkerOnMessage = Context.Tag<DbWorkerOnMessage>(
|
|
112
|
-
"evolu/DbWorkerOnMessage",
|
|
113
|
-
);
|
|
113
|
+
const DbWorkerOnMessage = Context.Tag<DbWorkerOnMessage>();
|
|
114
114
|
|
|
115
115
|
export type DbWorkerOutput =
|
|
116
116
|
| DbWorkerOutputOnError
|
|
@@ -120,12 +120,12 @@ export type DbWorkerOutput =
|
|
|
120
120
|
| DbWorkerOutputOnResetOrRestore
|
|
121
121
|
| DbWorkerOutputOnSyncState;
|
|
122
122
|
|
|
123
|
-
interface DbWorkerOutputOnError {
|
|
123
|
+
export interface DbWorkerOutputOnError {
|
|
124
124
|
readonly _tag: "onError";
|
|
125
125
|
readonly error: EvoluError;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
interface DbWorkerOutputOnOwner {
|
|
128
|
+
export interface DbWorkerOutputOnOwner {
|
|
129
129
|
readonly _tag: "onOwner";
|
|
130
130
|
readonly owner: Owner;
|
|
131
131
|
}
|
|
@@ -136,10 +136,6 @@ export interface DbWorkerOutputOnQuery {
|
|
|
136
136
|
readonly onCompleteIds: ReadonlyArray<OnCompleteId>;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
export type OnCompleteId = string &
|
|
140
|
-
Brand.Brand<"Id"> &
|
|
141
|
-
Brand.Brand<"OnComplete">;
|
|
142
|
-
|
|
143
139
|
interface DbWorkerOutputOnReceive {
|
|
144
140
|
readonly _tag: "onReceive";
|
|
145
141
|
}
|
|
@@ -153,7 +149,7 @@ interface DbWorkerOutputOnSyncState {
|
|
|
153
149
|
readonly state: SyncState;
|
|
154
150
|
}
|
|
155
151
|
|
|
156
|
-
export interface
|
|
152
|
+
export interface Mutation {
|
|
157
153
|
readonly table: string;
|
|
158
154
|
readonly id: Id;
|
|
159
155
|
readonly values: ReadonlyRecord.ReadonlyRecord<
|
|
@@ -164,54 +160,47 @@ export interface MutateItem {
|
|
|
164
160
|
readonly onCompleteId: OnCompleteId | null;
|
|
165
161
|
}
|
|
166
162
|
|
|
167
|
-
const init = (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
Effect.catchTag("NoSuchTableOrColumnError", () => lazyInit()),
|
|
185
|
-
Effect.tap(() => ensureSchema(input.tables)),
|
|
186
|
-
);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
export type RowsCacheMap = ReadonlyMap<Query, ReadonlyArray<Row>>;
|
|
190
|
-
|
|
191
|
-
type RowsCacheRef = Ref.Ref<RowsCacheMap>;
|
|
192
|
-
const RowsCacheRef = Context.Tag<RowsCacheRef>("evolu/RowsCacheRef");
|
|
163
|
+
const init = Effect.gen(function* (_) {
|
|
164
|
+
const sqlite = yield* _(Sqlite);
|
|
165
|
+
|
|
166
|
+
return yield* _(
|
|
167
|
+
sqlite.exec(Sql.selectOwner),
|
|
168
|
+
Effect.map(
|
|
169
|
+
({ rows: [row] }): Owner => ({
|
|
170
|
+
id: row.id as OwnerId,
|
|
171
|
+
mnemonic: row.mnemonic as Mnemonic,
|
|
172
|
+
// expo-sqlite 11.3.2 doesn't support Uint8Array
|
|
173
|
+
encryptionKey: hexToBytes(row.encryptionKey as string),
|
|
174
|
+
}),
|
|
175
|
+
),
|
|
176
|
+
someDefectToNoSuchTableOrColumnError,
|
|
177
|
+
Effect.catchTag("NoSuchTableOrColumnError", () => lazyInit()),
|
|
178
|
+
);
|
|
179
|
+
});
|
|
193
180
|
|
|
194
181
|
const query = ({
|
|
195
182
|
queries,
|
|
196
183
|
onCompleteIds = [],
|
|
197
184
|
}: {
|
|
198
|
-
readonly queries:
|
|
185
|
+
readonly queries: Queries;
|
|
199
186
|
readonly onCompleteIds?: ReadonlyArray<OnCompleteId>;
|
|
200
|
-
}): Effect.Effect<Sqlite |
|
|
187
|
+
}): Effect.Effect<Sqlite | RowsStore | DbWorkerOnMessage, never, void> =>
|
|
201
188
|
Effect.gen(function* (_) {
|
|
202
189
|
const sqlite = yield* _(Sqlite);
|
|
203
|
-
const
|
|
190
|
+
const rowsStore = yield* _(RowsStore);
|
|
204
191
|
const dbWorkerOnMessage = yield* _(DbWorkerOnMessage);
|
|
205
192
|
|
|
206
193
|
const queriesRows = yield* _(
|
|
207
|
-
|
|
194
|
+
ReadonlyArray.dedupe(queries),
|
|
195
|
+
Effect.forEach((query) =>
|
|
208
196
|
sqlite
|
|
209
|
-
.exec(
|
|
197
|
+
.exec(deserializeQuery(query))
|
|
210
198
|
.pipe(Effect.map((result) => [query, result.rows] as const)),
|
|
211
199
|
),
|
|
212
200
|
);
|
|
213
|
-
|
|
214
|
-
|
|
201
|
+
|
|
202
|
+
const previous = rowsStore.getState();
|
|
203
|
+
yield* _(rowsStore.setState(new Map([...previous, ...queriesRows])));
|
|
215
204
|
|
|
216
205
|
const queriesPatches = queriesRows.map(
|
|
217
206
|
([query, rows]): QueryPatches => ({
|
|
@@ -241,11 +230,11 @@ const readTimestampAndMerkleTree = Sqlite.pipe(
|
|
|
241
230
|
),
|
|
242
231
|
);
|
|
243
232
|
|
|
244
|
-
export const
|
|
245
|
-
|
|
233
|
+
export const mutationsToNewMessages = (
|
|
234
|
+
mutations: ReadonlyArray<Mutation>,
|
|
246
235
|
): ReadonlyArray<NewMessage> =>
|
|
247
236
|
pipe(
|
|
248
|
-
|
|
237
|
+
mutations,
|
|
249
238
|
ReadonlyArray.map(({ id, isInsert, now, table, values }) =>
|
|
250
239
|
pipe(
|
|
251
240
|
Object.entries(values),
|
|
@@ -263,8 +252,8 @@ export const mutateItemsToNewMessages = (
|
|
|
263
252
|
typeof value === "boolean"
|
|
264
253
|
? cast(value)
|
|
265
254
|
: value instanceof Date
|
|
266
|
-
|
|
267
|
-
|
|
255
|
+
? cast(value)
|
|
256
|
+
: value,
|
|
268
257
|
] as const,
|
|
269
258
|
),
|
|
270
259
|
ReadonlyArray.append([isInsert ? "createdAt" : "updatedAt", now]),
|
|
@@ -391,14 +380,14 @@ const writeTimestampAndMerkleTree = ({
|
|
|
391
380
|
);
|
|
392
381
|
|
|
393
382
|
const mutate = ({
|
|
394
|
-
|
|
383
|
+
mutations,
|
|
395
384
|
queries,
|
|
396
385
|
}: DbWorkerInputMutate): Effect.Effect<
|
|
397
386
|
| Sqlite
|
|
398
387
|
| Owner
|
|
399
388
|
| Time
|
|
400
389
|
| Config
|
|
401
|
-
|
|
|
390
|
+
| RowsStore
|
|
402
391
|
| DbWorkerOnMessage
|
|
403
392
|
| SyncWorkerPostMessage,
|
|
404
393
|
| TimestampDriftError
|
|
@@ -407,16 +396,14 @@ const mutate = ({
|
|
|
407
396
|
void
|
|
408
397
|
> =>
|
|
409
398
|
Effect.gen(function* (_) {
|
|
410
|
-
const [toSync,
|
|
399
|
+
const [toSync, localOnly] = ReadonlyArray.partition(mutations, (item) =>
|
|
411
400
|
item.table.startsWith("_"),
|
|
412
401
|
);
|
|
413
|
-
const [toUpsert, toDelete] = ReadonlyArray.partition(
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
),
|
|
419
|
-
).map(mutateItemsToNewMessages);
|
|
402
|
+
const [toUpsert, toDelete] = ReadonlyArray.partition(localOnly, (item) =>
|
|
403
|
+
mutationsToNewMessages([item]).some(
|
|
404
|
+
(message) => message.column === "isDeleted" && message.value === 1,
|
|
405
|
+
),
|
|
406
|
+
).map(mutationsToNewMessages);
|
|
420
407
|
|
|
421
408
|
yield* _(
|
|
422
409
|
Effect.forEach(toUpsert, (message) =>
|
|
@@ -435,7 +422,7 @@ const mutate = ({
|
|
|
435
422
|
let { timestamp, merkleTree } = yield* _(readTimestampAndMerkleTree);
|
|
436
423
|
|
|
437
424
|
const messages = yield* _(
|
|
438
|
-
|
|
425
|
+
mutationsToNewMessages(toSync),
|
|
439
426
|
Effect.forEach((message) =>
|
|
440
427
|
Effect.map(sendTimestamp(timestamp), (nextTimestamp): Message => {
|
|
441
428
|
timestamp = nextTimestamp;
|
|
@@ -459,7 +446,7 @@ const mutate = ({
|
|
|
459
446
|
});
|
|
460
447
|
}
|
|
461
448
|
|
|
462
|
-
const onCompleteIds = ReadonlyArray.filterMap(
|
|
449
|
+
const onCompleteIds = ReadonlyArray.filterMap(mutations, (item) =>
|
|
463
450
|
Option.fromNullable(item.onCompleteId),
|
|
464
451
|
);
|
|
465
452
|
if (queries.length > 0 || onCompleteIds.length > 0)
|
|
@@ -477,6 +464,7 @@ const handleSyncResponse = ({
|
|
|
477
464
|
Effect.gen(function* (_) {
|
|
478
465
|
let { timestamp, merkleTree } = yield* _(readTimestampAndMerkleTree);
|
|
479
466
|
|
|
467
|
+
const dbWorkerOnMessage = yield* _(DbWorkerOnMessage);
|
|
480
468
|
if (messages.length > 0) {
|
|
481
469
|
for (const message of messages)
|
|
482
470
|
timestamp = yield* _(
|
|
@@ -485,11 +473,9 @@ const handleSyncResponse = ({
|
|
|
485
473
|
);
|
|
486
474
|
merkleTree = yield* _(applyMessages({ merkleTree, messages }));
|
|
487
475
|
yield* _(writeTimestampAndMerkleTree({ timestamp, merkleTree }));
|
|
476
|
+
dbWorkerOnMessage({ _tag: "onReceive" });
|
|
488
477
|
}
|
|
489
478
|
|
|
490
|
-
const dbWorkerOnMessage = yield* _(DbWorkerOnMessage);
|
|
491
|
-
dbWorkerOnMessage({ _tag: "onReceive" });
|
|
492
|
-
|
|
493
479
|
const diff = diffMerkleTrees(response.merkleTree, merkleTree);
|
|
494
480
|
|
|
495
481
|
const syncWorkerPostMessage = yield* _(SyncWorkerPostMessage);
|
|
@@ -506,9 +492,10 @@ const handleSyncResponse = ({
|
|
|
506
492
|
return;
|
|
507
493
|
}
|
|
508
494
|
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
);
|
|
495
|
+
const sqlite = yield* _(Sqlite);
|
|
496
|
+
const config = yield* _(Config);
|
|
497
|
+
const owner = yield* _(Owner);
|
|
498
|
+
|
|
512
499
|
const messagesToSync = yield* _(
|
|
513
500
|
sqlite.exec({
|
|
514
501
|
sql: Sql.selectMessagesToSync,
|
|
@@ -516,6 +503,14 @@ const handleSyncResponse = ({
|
|
|
516
503
|
}),
|
|
517
504
|
Effect.map(({ rows }) => rows as unknown as ReadonlyArray<Message>),
|
|
518
505
|
);
|
|
506
|
+
|
|
507
|
+
if (response.syncLoopCount > 100) {
|
|
508
|
+
// TODO: dbWorkerOnMessage({ _tag: "onError" });
|
|
509
|
+
// eslint-disable-next-line no-console
|
|
510
|
+
console.error("Evolu: syncLoopCount > 100");
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
519
514
|
syncWorkerPostMessage({
|
|
520
515
|
_tag: "sync",
|
|
521
516
|
syncUrl: config.syncUrl,
|
|
@@ -535,22 +530,17 @@ const sync = ({
|
|
|
535
530
|
| DbWorkerOnMessage
|
|
536
531
|
| SyncWorkerPostMessage
|
|
537
532
|
| Owner
|
|
538
|
-
|
|
|
533
|
+
| RowsStore,
|
|
539
534
|
never,
|
|
540
535
|
void
|
|
541
536
|
> =>
|
|
542
537
|
Effect.gen(function* (_) {
|
|
543
538
|
if (queries.length > 0) yield* _(query({ queries }));
|
|
544
539
|
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
Config,
|
|
550
|
-
Owner,
|
|
551
|
-
readTimestampAndMerkleTree,
|
|
552
|
-
]),
|
|
553
|
-
);
|
|
540
|
+
const syncWorkerPostMessage = yield* _(SyncWorkerPostMessage);
|
|
541
|
+
const config = yield* _(Config);
|
|
542
|
+
const owner = yield* _(Owner);
|
|
543
|
+
const timestampAndMerkleTree = yield* _(readTimestampAndMerkleTree);
|
|
554
544
|
|
|
555
545
|
syncWorkerPostMessage({
|
|
556
546
|
...timestampAndMerkleTree,
|
|
@@ -593,7 +583,6 @@ export const DbWorkerLive = Layer.effect(
|
|
|
593
583
|
DbWorker,
|
|
594
584
|
Effect.gen(function* (_) {
|
|
595
585
|
const syncWorker = yield* _(SyncWorker);
|
|
596
|
-
const rowsCacheRef = yield* _(Ref.make<RowsCacheMap>(new Map()));
|
|
597
586
|
|
|
598
587
|
const onError = (error: EvoluError): Effect.Effect<never, never, void> =>
|
|
599
588
|
Effect.sync(() => {
|
|
@@ -613,20 +602,27 @@ export const DbWorkerLive = Layer.effect(
|
|
|
613
602
|
}
|
|
614
603
|
};
|
|
615
604
|
|
|
616
|
-
const
|
|
605
|
+
const runContext = Context.empty().pipe(
|
|
617
606
|
Context.add(Sqlite, yield* _(Sqlite)),
|
|
618
607
|
Context.add(Bip39, yield* _(Bip39)),
|
|
619
608
|
Context.add(NanoId, yield* _(NanoId)),
|
|
609
|
+
Context.add(DbWorkerOnMessage, (output) => {
|
|
610
|
+
dbWorker.onMessage(output);
|
|
611
|
+
}),
|
|
620
612
|
);
|
|
621
613
|
|
|
622
614
|
const run = (
|
|
623
|
-
effect: Effect.Effect<
|
|
615
|
+
effect: Effect.Effect<
|
|
616
|
+
Sqlite | Bip39 | NanoId | DbWorkerOnMessage,
|
|
617
|
+
EvoluError,
|
|
618
|
+
void
|
|
619
|
+
>,
|
|
624
620
|
): Promise<void> =>
|
|
625
621
|
effect.pipe(
|
|
626
622
|
Effect.catchAllDefect(makeUnexpectedError),
|
|
627
623
|
transaction,
|
|
628
624
|
Effect.catchAll(onError),
|
|
629
|
-
Effect.provide(
|
|
625
|
+
Effect.provide(runContext),
|
|
630
626
|
Effect.runPromise,
|
|
631
627
|
);
|
|
632
628
|
|
|
@@ -635,65 +631,39 @@ export const DbWorkerLive = Layer.effect(
|
|
|
635
631
|
// Write for reset only in case init fails.
|
|
636
632
|
const writeForInitFail: Write = (input): Promise<void> => {
|
|
637
633
|
if (input._tag !== "reset") return Promise.resolve(undefined);
|
|
638
|
-
return reset(input).pipe(
|
|
639
|
-
Effect.provideService(DbWorkerOnMessage, dbWorker.onMessage),
|
|
640
|
-
Effect.provide(context),
|
|
641
|
-
run,
|
|
642
|
-
);
|
|
634
|
+
return reset(input).pipe(run);
|
|
643
635
|
};
|
|
644
636
|
|
|
645
|
-
const makeWriteForInitSuccess = (
|
|
637
|
+
const makeWriteForInitSuccess = (config: Config, owner: Owner): Write => {
|
|
646
638
|
let skipAllBecauseOfReset = false;
|
|
647
639
|
|
|
648
640
|
const layer = Layer.mergeAll(
|
|
649
641
|
ConfigLive(config),
|
|
650
|
-
Layer.succeed(DbWorkerOnMessage, dbWorker.onMessage),
|
|
651
642
|
Layer.succeed(Owner, owner),
|
|
652
643
|
Layer.succeed(SyncWorkerPostMessage, syncWorker.postMessage),
|
|
653
|
-
|
|
644
|
+
RowsStoreLive,
|
|
654
645
|
TimeLive,
|
|
655
646
|
);
|
|
656
647
|
|
|
657
|
-
const mapInputToEffect = (
|
|
658
|
-
input: DbWorkerInput,
|
|
659
|
-
): Effect.Effect<
|
|
660
|
-
| Sqlite
|
|
661
|
-
| RowsCacheRef
|
|
662
|
-
| DbWorkerOnMessage
|
|
663
|
-
| Owner
|
|
664
|
-
| Time
|
|
665
|
-
| Config
|
|
666
|
-
| SyncWorkerPostMessage
|
|
667
|
-
| Bip39
|
|
668
|
-
| NanoId,
|
|
669
|
-
| UnexpectedError
|
|
670
|
-
| TimestampDriftError
|
|
671
|
-
| TimestampCounterOverflowError
|
|
672
|
-
| TimestampError,
|
|
673
|
-
void
|
|
674
|
-
> => {
|
|
675
|
-
switch (input._tag) {
|
|
676
|
-
case "init":
|
|
677
|
-
return makeUnexpectedError(new Error("init must be called once"));
|
|
678
|
-
case "query":
|
|
679
|
-
return query(input);
|
|
680
|
-
case "mutate":
|
|
681
|
-
return mutate(input);
|
|
682
|
-
case "sync":
|
|
683
|
-
return sync(input);
|
|
684
|
-
case "reset":
|
|
685
|
-
skipAllBecauseOfReset = true;
|
|
686
|
-
return reset(input);
|
|
687
|
-
case "ensureSchema":
|
|
688
|
-
return ensureSchema(input.tables);
|
|
689
|
-
case "SyncWorkerOutputSyncResponse":
|
|
690
|
-
return handleSyncResponse(input);
|
|
691
|
-
}
|
|
692
|
-
};
|
|
693
|
-
|
|
694
648
|
return (input) => {
|
|
695
649
|
if (skipAllBecauseOfReset) return Promise.resolve(undefined);
|
|
696
|
-
return
|
|
650
|
+
return Match.value(input).pipe(
|
|
651
|
+
Match.tagsExhaustive({
|
|
652
|
+
init: () =>
|
|
653
|
+
makeUnexpectedError(new Error("init must be called once")),
|
|
654
|
+
query,
|
|
655
|
+
mutate,
|
|
656
|
+
sync,
|
|
657
|
+
reset: (input) => {
|
|
658
|
+
skipAllBecauseOfReset = true;
|
|
659
|
+
return reset(input);
|
|
660
|
+
},
|
|
661
|
+
ensureSchema: ({ tables }) => ensureSchema(tables),
|
|
662
|
+
SyncWorkerOutputSyncResponse: handleSyncResponse,
|
|
663
|
+
}),
|
|
664
|
+
Effect.provide(layer),
|
|
665
|
+
run,
|
|
666
|
+
);
|
|
697
667
|
};
|
|
698
668
|
};
|
|
699
669
|
|
|
@@ -701,10 +671,10 @@ export const DbWorkerLive = Layer.effect(
|
|
|
701
671
|
if (input._tag !== "init")
|
|
702
672
|
return run(makeUnexpectedError(new Error("init must be called first")));
|
|
703
673
|
write = writeForInitFail;
|
|
704
|
-
return init
|
|
674
|
+
return init.pipe(
|
|
705
675
|
Effect.map((owner) => {
|
|
706
676
|
dbWorker.onMessage({ _tag: "onOwner", owner });
|
|
707
|
-
write = makeWriteForInitSuccess(
|
|
677
|
+
write = makeWriteForInitSuccess(input.config, owner);
|
|
708
678
|
}),
|
|
709
679
|
run,
|
|
710
680
|
);
|
package/src/Diff.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Predicate } from "effect";
|
|
2
|
-
import { Query, Row
|
|
1
|
+
import { Predicate, ReadonlyArray } from "effect";
|
|
2
|
+
import { Query, Row } from "./Db.js";
|
|
3
|
+
import { Value } from "./Sqlite.js";
|
|
3
4
|
|
|
4
5
|
export interface QueryPatches {
|
|
5
6
|
readonly query: Query;
|
|
@@ -22,15 +23,12 @@ export interface ReplaceAtPatch {
|
|
|
22
23
|
export const applyPatches =
|
|
23
24
|
(patches: ReadonlyArray<Patch>) =>
|
|
24
25
|
(current: ReadonlyArray<Row>): ReadonlyArray<Row> =>
|
|
25
|
-
patches.reduce((
|
|
26
|
+
patches.reduce((next, patch) => {
|
|
26
27
|
switch (patch.op) {
|
|
27
28
|
case "replaceAll":
|
|
28
29
|
return patch.value;
|
|
29
30
|
case "replaceAt": {
|
|
30
|
-
|
|
31
|
-
const next = [...a];
|
|
32
|
-
next[patch.index] = patch.value;
|
|
33
|
-
return next;
|
|
31
|
+
return ReadonlyArray.replace(next, patch.index, patch.value);
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
34
|
}, current);
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { TimestampError } from "./Crdt.js";
|
|
3
|
+
import { makeStore } from "./Store.js";
|
|
3
4
|
|
|
5
|
+
export const makeErrorStore = makeStore<EvoluError | null>(null);
|
|
6
|
+
|
|
7
|
+
/** The EvoluError type is used to represent errors that can occur in Evolu. */
|
|
4
8
|
export type EvoluError = UnexpectedError | TimestampError;
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
|
-
*
|
|
8
|
-
* in
|
|
9
|
-
*
|
|
11
|
+
* The UnexpectedError interface is designed to represent errors that can occur
|
|
12
|
+
* unexpectedly anywhere, even in third-party libraries, because Evolu uses
|
|
13
|
+
* Effect to track all errors.
|
|
10
14
|
*/
|
|
11
|
-
interface TransferableError {
|
|
12
|
-
readonly message: string;
|
|
13
|
-
readonly stack: string | undefined;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
15
|
export interface UnexpectedError {
|
|
17
16
|
readonly _tag: "UnexpectedError";
|
|
18
17
|
readonly error: TransferableError;
|
|
@@ -31,3 +30,13 @@ export const makeUnexpectedError = (
|
|
|
31
30
|
},
|
|
32
31
|
});
|
|
33
32
|
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* We can't use the whole error because of WebWorker postMessage DataCloneError
|
|
36
|
+
* in Safari and Firefox. TODO:
|
|
37
|
+
* https://discord.com/channels/795981131316985866/795983589644304396/1096736473396564079
|
|
38
|
+
*/
|
|
39
|
+
interface TransferableError {
|
|
40
|
+
readonly message: string;
|
|
41
|
+
readonly stack: string | undefined;
|
|
42
|
+
}
|