@evolu/common 6.0.1-preview.15 → 6.0.1-preview.17
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/Crypto.d.ts +0 -1
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +2 -1
- package/dist/src/Evolu/Config.d.ts +7 -7
- package/dist/src/Evolu/Config.d.ts.map +1 -1
- package/dist/src/Evolu/Db.d.ts +5 -9
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +139 -117
- 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 +9 -31
- package/dist/src/Evolu/Owner.d.ts +84 -105
- package/dist/src/Evolu/Owner.d.ts.map +1 -1
- package/dist/src/Evolu/Owner.js +79 -89
- package/dist/src/Evolu/Protocol.d.ts +2 -2
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +2 -1
- package/dist/src/Evolu/Public.d.ts +1 -1
- package/dist/src/Evolu/Public.d.ts.map +1 -1
- package/dist/src/Evolu/Schema.d.ts +0 -7
- package/dist/src/Evolu/Schema.d.ts.map +1 -1
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +1 -0
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +1 -0
- package/package.json +1 -1
- package/src/Crypto.ts +6 -4
- package/src/Evolu/Config.ts +7 -7
- package/src/Evolu/Db.ts +206 -169
- package/src/Evolu/Evolu.ts +30 -56
- package/src/Evolu/Owner.ts +129 -203
- package/src/Evolu/Protocol.ts +3 -8
- package/src/Evolu/Public.ts +1 -6
- package/src/Evolu/Schema.ts +1 -9
- package/src/Result.ts +1 -0
- package/src/Type.ts +1 -0
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";
|
|
@@ -10,6 +6,7 @@ import {
|
|
|
10
6
|
CreateMnemonicDep,
|
|
11
7
|
CreateRandomBytesDep,
|
|
12
8
|
createSymmetricCrypto,
|
|
9
|
+
EncryptionKey,
|
|
13
10
|
SymmetricCryptoDecryptError,
|
|
14
11
|
SymmetricCryptoDep,
|
|
15
12
|
} from "../Crypto.js";
|
|
@@ -19,7 +16,6 @@ import { constFalse, exhaustiveCheck } from "../Function.js";
|
|
|
19
16
|
import { NanoIdLibDep } from "../NanoId.js";
|
|
20
17
|
import { objectToEntries } from "../Object.js";
|
|
21
18
|
import { RandomDep } from "../Random.js";
|
|
22
|
-
import { createRef, Ref } from "../Ref.js";
|
|
23
19
|
import { ok, Result } from "../Result.js";
|
|
24
20
|
import {
|
|
25
21
|
createSqlite,
|
|
@@ -42,19 +38,14 @@ import {
|
|
|
42
38
|
} from "../Worker.js";
|
|
43
39
|
import { Config } from "./Config.js";
|
|
44
40
|
import { makePatches, QueryPatches } from "./Diff.js";
|
|
45
|
-
import {
|
|
46
|
-
AppOwner,
|
|
47
|
-
createAppOwner,
|
|
48
|
-
createOwnerRow,
|
|
49
|
-
OwnerRow,
|
|
50
|
-
OwnerWithWriteAccess,
|
|
51
|
-
} from "./Owner.js";
|
|
41
|
+
import { AppOwner, createAppOwner, OwnerId, WriteKey } from "./Owner.js";
|
|
52
42
|
import {
|
|
53
43
|
applyProtocolMessageAsClient,
|
|
54
44
|
Base64Url256,
|
|
55
45
|
BinaryId,
|
|
56
46
|
binaryIdToId,
|
|
57
47
|
BinaryOwnerId,
|
|
48
|
+
binaryOwnerIdToOwnerId,
|
|
58
49
|
CrdtMessage,
|
|
59
50
|
createProtocolMessageForSync,
|
|
60
51
|
createProtocolMessageFromCrdtMessages,
|
|
@@ -83,6 +74,7 @@ import {
|
|
|
83
74
|
import { CreateSyncDep, SyncConfig, SyncDep } from "./Sync.js";
|
|
84
75
|
import {
|
|
85
76
|
binaryTimestampToTimestamp,
|
|
77
|
+
createInitialTimestamp,
|
|
86
78
|
receiveTimestamp,
|
|
87
79
|
sendTimestamp,
|
|
88
80
|
Timestamp,
|
|
@@ -90,6 +82,7 @@ import {
|
|
|
90
82
|
TimestampCounterOverflowError,
|
|
91
83
|
TimestampDriftError,
|
|
92
84
|
TimestampError,
|
|
85
|
+
TimestampString,
|
|
93
86
|
timestampStringToTimestamp,
|
|
94
87
|
TimestampTimeOutOfRangeError,
|
|
95
88
|
timestampToBinaryTimestamp,
|
|
@@ -124,7 +117,6 @@ export type DbWorkerInput =
|
|
|
124
117
|
readonly type: "init";
|
|
125
118
|
readonly config: Config;
|
|
126
119
|
readonly dbSchema: DbSchema;
|
|
127
|
-
readonly initialData: ReadonlyArray<DbChange>;
|
|
128
120
|
}
|
|
129
121
|
| {
|
|
130
122
|
readonly type: "mutate";
|
|
@@ -159,7 +151,8 @@ export type DbWorkerInput =
|
|
|
159
151
|
export type DbWorkerOutput =
|
|
160
152
|
| {
|
|
161
153
|
readonly type: "onInit";
|
|
162
|
-
readonly
|
|
154
|
+
readonly appOwner: AppOwner;
|
|
155
|
+
readonly isFirst: boolean;
|
|
163
156
|
}
|
|
164
157
|
| {
|
|
165
158
|
readonly type: "onError";
|
|
@@ -209,21 +202,53 @@ type DbWorkerDeps = Omit<
|
|
|
209
202
|
TimestampConfigDep &
|
|
210
203
|
SymmetricCryptoDep &
|
|
211
204
|
PostMessageDep &
|
|
212
|
-
|
|
205
|
+
OwnersDep &
|
|
206
|
+
ClockDep &
|
|
213
207
|
GetQueryRowsCacheDep &
|
|
214
208
|
ClientStorageDep;
|
|
215
209
|
|
|
216
210
|
type PostMessageDep = WorkerPostMessageDep<DbWorkerOutput>;
|
|
217
211
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
212
|
+
interface OwnersDep {
|
|
213
|
+
readonly owners: Owners;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
type Owners = Map<OwnerId, AppOwner>;
|
|
217
|
+
|
|
218
|
+
interface ClockDep {
|
|
219
|
+
readonly clock: Clock;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
interface Clock {
|
|
223
|
+
readonly get: () => Timestamp;
|
|
224
|
+
readonly save: (timestamp: Timestamp) => Result<void, SqliteError>;
|
|
221
225
|
}
|
|
222
226
|
|
|
223
227
|
interface GetQueryRowsCacheDep {
|
|
224
228
|
readonly getQueryRowsCache: (tabId: Id) => QueryRowsCache;
|
|
225
229
|
}
|
|
226
230
|
|
|
231
|
+
const createClock =
|
|
232
|
+
(deps: NanoIdLibDep & SqliteDep) =>
|
|
233
|
+
(initialTimestamp = createInitialTimestamp(deps)): Clock => {
|
|
234
|
+
let currentTimestamp = initialTimestamp;
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
get: () => currentTimestamp,
|
|
238
|
+
save: (timestamp) => {
|
|
239
|
+
currentTimestamp = timestamp;
|
|
240
|
+
|
|
241
|
+
const timestampString = timestampToTimestampString(timestamp);
|
|
242
|
+
const saveTimestamp = deps.sqlite.exec(sql.prepared`
|
|
243
|
+
update evolu_config set "clock" = ${timestampString};
|
|
244
|
+
`);
|
|
245
|
+
if (!saveTimestamp.ok) return saveTimestamp;
|
|
246
|
+
|
|
247
|
+
return ok();
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
|
|
227
252
|
export const createDbWorkerForPlatform = (
|
|
228
253
|
platformDeps: DbWorkerPlatformDeps,
|
|
229
254
|
): DbWorker => {
|
|
@@ -245,35 +270,91 @@ export const createDbWorkerForPlatform = (
|
|
|
245
270
|
initMessage.config.name,
|
|
246
271
|
{ memory: initMessage.config.inMemory ?? false },
|
|
247
272
|
);
|
|
248
|
-
|
|
249
273
|
if (!sqliteResult.ok) {
|
|
250
274
|
postMessage({ type: "onError", error: sqliteResult.error });
|
|
251
275
|
return null;
|
|
252
276
|
}
|
|
277
|
+
|
|
253
278
|
const sqlite = sqliteResult.value;
|
|
279
|
+
const platformDepsWithSqlite = { ...platformDeps, sqlite };
|
|
254
280
|
|
|
255
281
|
const deps = sqlite.transaction(() => {
|
|
256
282
|
const currentDbSchema = getDbSchema({ sqlite })();
|
|
257
283
|
if (!currentDbSchema.ok) return currentDbSchema;
|
|
258
284
|
|
|
285
|
+
let appOwner: AppOwner;
|
|
286
|
+
let clock: Clock;
|
|
287
|
+
|
|
288
|
+
const dbIsInitialized = currentDbSchema.value.tables.some(
|
|
289
|
+
(table) => table.name === "evolu_version",
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
if (dbIsInitialized) {
|
|
293
|
+
const versionResult = sqlite.exec<{
|
|
294
|
+
protocolVersion: number;
|
|
295
|
+
}>(sql`select protocolVersion from evolu_version limit 1;`);
|
|
296
|
+
if (!versionResult.ok) return versionResult;
|
|
297
|
+
|
|
298
|
+
// TODO: Handle version migrations here if needed
|
|
299
|
+
// const [{ protocolVersion }] = protocolVersionResult.value.rows;
|
|
300
|
+
// if (protocolVersion < currentProtocolVersion) {
|
|
301
|
+
// const migrateResult = migrateDatabase({ sqlite })(
|
|
302
|
+
// protocolVersion,
|
|
303
|
+
// currentProtocolVersion
|
|
304
|
+
// );
|
|
305
|
+
// if (!migrateResult.ok) return migrateResult;
|
|
306
|
+
// }
|
|
307
|
+
|
|
308
|
+
const configResult = sqlite.exec<{
|
|
309
|
+
clock: TimestampString;
|
|
310
|
+
appOwnerId: OwnerId;
|
|
311
|
+
appOwnerEncryptionKey: EncryptionKey;
|
|
312
|
+
appOwnerWriteKey: WriteKey;
|
|
313
|
+
appOwnerMnemonic: Mnemonic | null;
|
|
314
|
+
}>(sql`
|
|
315
|
+
select
|
|
316
|
+
clock,
|
|
317
|
+
appOwnerId,
|
|
318
|
+
appOwnerEncryptionKey,
|
|
319
|
+
appOwnerWriteKey,
|
|
320
|
+
appOwnerMnemonic
|
|
321
|
+
from evolu_config
|
|
322
|
+
limit 1;
|
|
323
|
+
`);
|
|
324
|
+
if (!configResult.ok) return configResult;
|
|
325
|
+
|
|
326
|
+
const [config] = configResult.value.rows;
|
|
327
|
+
|
|
328
|
+
appOwner = {
|
|
329
|
+
type: "AppOwner",
|
|
330
|
+
id: config.appOwnerId,
|
|
331
|
+
encryptionKey: config.appOwnerEncryptionKey,
|
|
332
|
+
writeKey: config.appOwnerWriteKey,
|
|
333
|
+
mnemonic: config.appOwnerMnemonic,
|
|
334
|
+
};
|
|
335
|
+
clock = createClock(platformDepsWithSqlite)(
|
|
336
|
+
timestampStringToTimestamp(config.clock),
|
|
337
|
+
);
|
|
338
|
+
} else {
|
|
339
|
+
appOwner =
|
|
340
|
+
initMessage.config.initialAppOwner ??
|
|
341
|
+
createAppOwner(platformDeps.createMnemonic());
|
|
342
|
+
clock = createClock(platformDepsWithSqlite)();
|
|
343
|
+
const initializeDbResult = initializeDb(platformDepsWithSqlite)(
|
|
344
|
+
appOwner,
|
|
345
|
+
clock,
|
|
346
|
+
);
|
|
347
|
+
if (!initializeDbResult.ok) return initializeDbResult;
|
|
348
|
+
}
|
|
349
|
+
|
|
259
350
|
const ensureDbSchemaResult = ensureDbSchema({ sqlite })(
|
|
260
351
|
initMessage.dbSchema,
|
|
261
352
|
currentDbSchema.value,
|
|
262
353
|
);
|
|
263
354
|
if (!ensureDbSchemaResult.ok) return ensureDbSchemaResult;
|
|
264
355
|
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
const appOwnerAndOwnerRow = ownerExists
|
|
270
|
-
? selectAppOwner({ sqlite })
|
|
271
|
-
: initializeDb({ ...platformDeps, sqlite })(
|
|
272
|
-
initMessage.config.mnemonic,
|
|
273
|
-
);
|
|
274
|
-
if (!appOwnerAndOwnerRow.ok) return appOwnerAndOwnerRow;
|
|
275
|
-
|
|
276
|
-
const [appOwner, ownerRow] = appOwnerAndOwnerRow.value;
|
|
356
|
+
const owners: Owners = new Map();
|
|
357
|
+
owners.set(appOwner.id, appOwner);
|
|
277
358
|
|
|
278
359
|
const depsWithoutSyncAndStorage = {
|
|
279
360
|
...platformDeps,
|
|
@@ -282,7 +363,8 @@ export const createDbWorkerForPlatform = (
|
|
|
282
363
|
timestampConfig: initMessage.config,
|
|
283
364
|
symmetricCrypto: createSymmetricCrypto(platformDeps),
|
|
284
365
|
getQueryRowsCache,
|
|
285
|
-
|
|
366
|
+
clock,
|
|
367
|
+
owners,
|
|
286
368
|
};
|
|
287
369
|
|
|
288
370
|
const storage = createClientStorage(depsWithoutSyncAndStorage)({
|
|
@@ -297,12 +379,11 @@ export const createDbWorkerForPlatform = (
|
|
|
297
379
|
storage: storage.value,
|
|
298
380
|
};
|
|
299
381
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
postMessage({ type: "onInit", owner: appOwner });
|
|
382
|
+
postMessage({
|
|
383
|
+
type: "onInit",
|
|
384
|
+
appOwner,
|
|
385
|
+
isFirst: !dbIsInitialized,
|
|
386
|
+
});
|
|
306
387
|
|
|
307
388
|
const sync = platformDeps.createSync(platformDeps)({
|
|
308
389
|
...initMessage.config,
|
|
@@ -397,15 +478,11 @@ export const createDbWorkerForPlatform = (
|
|
|
397
478
|
const messages = applyChanges(deps)(toSyncChanges, onChange);
|
|
398
479
|
if (!messages.ok) return messages;
|
|
399
480
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
// error if not.
|
|
403
|
-
if (owner.writeKey == null) {
|
|
404
|
-
return ok();
|
|
405
|
-
}
|
|
481
|
+
// TODO: Use owner from db change or AppOwner
|
|
482
|
+
const owner = Array.from(deps.owners.values())[0];
|
|
406
483
|
|
|
407
484
|
const protocolMessage = createProtocolMessageFromCrdtMessages(deps)(
|
|
408
|
-
owner
|
|
485
|
+
owner,
|
|
409
486
|
messages.value,
|
|
410
487
|
);
|
|
411
488
|
|
|
@@ -460,9 +537,10 @@ export const createDbWorkerForPlatform = (
|
|
|
460
537
|
);
|
|
461
538
|
if (!ensureDbSchemaResult.ok) return ensureDbSchemaResult;
|
|
462
539
|
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
540
|
+
const appOwner = createAppOwner(message.restore.mnemonic);
|
|
541
|
+
const clock = createClock(deps)();
|
|
542
|
+
|
|
543
|
+
const initializeDbResult = initializeDb(deps)(appOwner, clock);
|
|
466
544
|
if (!initializeDbResult.ok) return initializeDbResult;
|
|
467
545
|
}
|
|
468
546
|
return ok();
|
|
@@ -712,57 +790,54 @@ export const createAppTable = (
|
|
|
712
790
|
);
|
|
713
791
|
` as SafeSql;
|
|
714
792
|
|
|
715
|
-
const selectAppOwner = (
|
|
716
|
-
deps: SqliteDep,
|
|
717
|
-
): Result<[AppOwner, OwnerRow], SqliteError> => {
|
|
718
|
-
const result = deps.sqlite.exec<OwnerRow>(sql`
|
|
719
|
-
select mnemonic, id, createdAt, encryptionKey, writeKey, timestamp
|
|
720
|
-
from evolu_owner
|
|
721
|
-
order by createdAt asc
|
|
722
|
-
limit 1;
|
|
723
|
-
`);
|
|
724
|
-
|
|
725
|
-
if (!result.ok) return result;
|
|
726
|
-
|
|
727
|
-
const {
|
|
728
|
-
rows: [ownerRow],
|
|
729
|
-
} = result.value;
|
|
730
|
-
|
|
731
|
-
assert(ownerRow.writeKey != null, "The writeKey is null");
|
|
732
|
-
|
|
733
|
-
const appOwner: AppOwner = {
|
|
734
|
-
type: "AppOwner",
|
|
735
|
-
mnemonic: ownerRow.mnemonic,
|
|
736
|
-
createdAt: ownerRow.createdAt,
|
|
737
|
-
id: ownerRow.id,
|
|
738
|
-
encryptionKey: ownerRow.encryptionKey,
|
|
739
|
-
writeKey: ownerRow.writeKey,
|
|
740
|
-
};
|
|
741
|
-
|
|
742
|
-
return ok([appOwner, ownerRow]);
|
|
743
|
-
};
|
|
744
|
-
|
|
745
793
|
const initializeDb =
|
|
794
|
+
(deps: SqliteDep & CreateMnemonicDep & TimeDep & CreateRandomBytesDep) =>
|
|
746
795
|
(
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
TimeDep &
|
|
751
|
-
CreateRandomBytesDep,
|
|
752
|
-
) =>
|
|
753
|
-
(mnemonic?: Mnemonic): Result<[AppOwner, OwnerRow], SqliteError> => {
|
|
796
|
+
initialAppOwner: AppOwner,
|
|
797
|
+
initialClock: Clock,
|
|
798
|
+
): Result<void, SqliteError> => {
|
|
754
799
|
for (const query of [
|
|
800
|
+
// Never change structure to ensure all versions can read it.
|
|
801
|
+
sql`
|
|
802
|
+
create table evolu_version (
|
|
803
|
+
"protocolVersion" integer not null
|
|
804
|
+
)
|
|
805
|
+
strict;
|
|
806
|
+
`,
|
|
807
|
+
|
|
808
|
+
sql`
|
|
809
|
+
insert into evolu_version ("protocolVersion")
|
|
810
|
+
values (${protocolVersion});
|
|
811
|
+
`,
|
|
812
|
+
|
|
755
813
|
sql`
|
|
756
814
|
create table evolu_config (
|
|
757
|
-
"
|
|
758
|
-
"
|
|
815
|
+
"clock" text not null,
|
|
816
|
+
"appOwnerId" text not null,
|
|
817
|
+
"appOwnerEncryptionKey" blob not null,
|
|
818
|
+
"appOwnerWriteKey" blob not null,
|
|
819
|
+
"appOwnerMnemonic" text
|
|
759
820
|
)
|
|
760
821
|
strict;
|
|
761
822
|
`,
|
|
762
823
|
|
|
763
824
|
sql`
|
|
764
|
-
insert into evolu_config
|
|
765
|
-
|
|
825
|
+
insert into evolu_config
|
|
826
|
+
(
|
|
827
|
+
"clock",
|
|
828
|
+
"appOwnerId",
|
|
829
|
+
"appOwnerEncryptionKey",
|
|
830
|
+
"appOwnerWriteKey",
|
|
831
|
+
"appOwnerMnemonic"
|
|
832
|
+
)
|
|
833
|
+
values
|
|
834
|
+
(
|
|
835
|
+
${timestampToTimestampString(initialClock.get())},
|
|
836
|
+
${initialAppOwner.id},
|
|
837
|
+
${initialAppOwner.encryptionKey},
|
|
838
|
+
${initialAppOwner.writeKey},
|
|
839
|
+
${initialAppOwner.mnemonic ?? null}
|
|
840
|
+
);
|
|
766
841
|
`,
|
|
767
842
|
|
|
768
843
|
/**
|
|
@@ -802,50 +877,12 @@ const initializeDb =
|
|
|
802
877
|
"timestamp" desc
|
|
803
878
|
);
|
|
804
879
|
`,
|
|
805
|
-
|
|
806
|
-
sql`
|
|
807
|
-
create table evolu_owner (
|
|
808
|
-
"mnemonic" text not null primary key,
|
|
809
|
-
"id" text not null,
|
|
810
|
-
"encryptionKey" blob not null,
|
|
811
|
-
"createdAt" text not null,
|
|
812
|
-
"writeKey" blob,
|
|
813
|
-
"timestamp" text not null
|
|
814
|
-
)
|
|
815
|
-
strict;
|
|
816
|
-
`,
|
|
817
880
|
]) {
|
|
818
881
|
const result = deps.sqlite.exec(query);
|
|
819
882
|
if (!result.ok) return result;
|
|
820
883
|
}
|
|
821
884
|
|
|
822
|
-
|
|
823
|
-
const ownerRow = createOwnerRow(deps)(appOwner);
|
|
824
|
-
|
|
825
|
-
const result = deps.sqlite.exec(sql`
|
|
826
|
-
insert into evolu_owner
|
|
827
|
-
(
|
|
828
|
-
"mnemonic",
|
|
829
|
-
"id",
|
|
830
|
-
"encryptionKey",
|
|
831
|
-
"createdAt",
|
|
832
|
-
"writeKey",
|
|
833
|
-
"timestamp"
|
|
834
|
-
)
|
|
835
|
-
values
|
|
836
|
-
(
|
|
837
|
-
${ownerRow.mnemonic},
|
|
838
|
-
${ownerRow.id},
|
|
839
|
-
${ownerRow.encryptionKey},
|
|
840
|
-
${ownerRow.createdAt},
|
|
841
|
-
${ownerRow.writeKey},
|
|
842
|
-
${ownerRow.timestamp}
|
|
843
|
-
);
|
|
844
|
-
`);
|
|
845
|
-
|
|
846
|
-
if (!result.ok) return result;
|
|
847
|
-
|
|
848
|
-
return ok([appOwner, ownerRow]);
|
|
885
|
+
return ok();
|
|
849
886
|
};
|
|
850
887
|
|
|
851
888
|
const applyChanges =
|
|
@@ -854,8 +891,9 @@ const applyChanges =
|
|
|
854
891
|
TimeDep &
|
|
855
892
|
TimestampConfigDep &
|
|
856
893
|
RandomDep &
|
|
857
|
-
|
|
858
|
-
|
|
894
|
+
ClientStorageDep &
|
|
895
|
+
OwnersDep &
|
|
896
|
+
ClockDep,
|
|
859
897
|
) =>
|
|
860
898
|
(
|
|
861
899
|
changes: NonEmptyReadonlyArray<DbChange>,
|
|
@@ -867,20 +905,18 @@ const applyChanges =
|
|
|
867
905
|
| TimestampCounterOverflowError
|
|
868
906
|
| SqliteError
|
|
869
907
|
> => {
|
|
870
|
-
let
|
|
871
|
-
deps.ownerRowRef.get().timestamp,
|
|
872
|
-
);
|
|
908
|
+
let clockTimestamp = deps.clock.get();
|
|
873
909
|
|
|
874
910
|
const messages: Array<CrdtMessage> = [];
|
|
875
911
|
|
|
876
912
|
for (const change of changes) {
|
|
877
|
-
const nextTimestamp = sendTimestamp(deps)(
|
|
913
|
+
const nextTimestamp = sendTimestamp(deps)(clockTimestamp);
|
|
878
914
|
if (!nextTimestamp.ok) return nextTimestamp;
|
|
879
|
-
|
|
880
|
-
messages.push({ timestamp:
|
|
915
|
+
clockTimestamp = nextTimestamp.value;
|
|
916
|
+
messages.push({ timestamp: clockTimestamp, change });
|
|
881
917
|
}
|
|
882
918
|
|
|
883
|
-
const apply = applyMessages(deps)(messages,
|
|
919
|
+
const apply = applyMessages(deps)(messages, clockTimestamp);
|
|
884
920
|
if (!apply.ok) return apply;
|
|
885
921
|
|
|
886
922
|
if (onChange) onChange();
|
|
@@ -890,12 +926,14 @@ const applyChanges =
|
|
|
890
926
|
};
|
|
891
927
|
|
|
892
928
|
const applyMessages =
|
|
893
|
-
(deps: SqliteDep & RandomDep &
|
|
929
|
+
(deps: SqliteDep & RandomDep & ClientStorageDep & ClockDep & OwnersDep) =>
|
|
894
930
|
(
|
|
895
931
|
messages: ReadonlyArray<CrdtMessage>,
|
|
896
|
-
|
|
932
|
+
clockTimestamp: Timestamp,
|
|
897
933
|
): Result<void, SqliteError> => {
|
|
898
|
-
const ownerId = ownerIdToBinaryOwnerId(
|
|
934
|
+
const ownerId = ownerIdToBinaryOwnerId(
|
|
935
|
+
Array.from(deps.owners.values())[0].id,
|
|
936
|
+
);
|
|
899
937
|
|
|
900
938
|
for (const message of messages) {
|
|
901
939
|
const result1 = applyMessageToAppTable(deps)(ownerId, message);
|
|
@@ -908,18 +946,11 @@ const applyMessages =
|
|
|
908
946
|
if (!result2.ok) return result2;
|
|
909
947
|
}
|
|
910
948
|
|
|
911
|
-
|
|
912
|
-
deps.ownerRowRef.modify((owner) => ({ ...owner, timestamp }));
|
|
913
|
-
const saveTimestamp = deps.sqlite.exec(sql.prepared`
|
|
914
|
-
update evolu_owner set "timestamp" = ${timestamp};
|
|
915
|
-
`);
|
|
916
|
-
if (!saveTimestamp.ok) return saveTimestamp;
|
|
917
|
-
|
|
918
|
-
return ok();
|
|
949
|
+
return deps.clock.save(clockTimestamp);
|
|
919
950
|
};
|
|
920
951
|
|
|
921
952
|
const applyMessageToAppTable =
|
|
922
|
-
(deps: SqliteDep
|
|
953
|
+
(deps: SqliteDep) =>
|
|
923
954
|
(ownerId: BinaryOwnerId, message: CrdtMessage): Result<void, SqliteError> => {
|
|
924
955
|
const timestamp = timestampToBinaryTimestamp(message.timestamp);
|
|
925
956
|
const updatedAt = new Date(message.timestamp.millis).toISOString();
|
|
@@ -1046,11 +1077,11 @@ const dropAllTables = (deps: SqliteDep): Result<void, SqliteError> => {
|
|
|
1046
1077
|
};
|
|
1047
1078
|
|
|
1048
1079
|
const handleSyncOpen =
|
|
1049
|
-
(deps:
|
|
1080
|
+
(deps: StorageDep & ConsoleDep & OwnersDep): SyncConfig["onOpen"] =>
|
|
1050
1081
|
(send) => {
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1053
|
-
|
|
1082
|
+
for (const [id] of deps.owners) {
|
|
1083
|
+
const message = createProtocolMessageForSync(deps)(id);
|
|
1084
|
+
if (!message) return;
|
|
1054
1085
|
deps.console.log("[db]", "send initial sync message", message);
|
|
1055
1086
|
send(message);
|
|
1056
1087
|
}
|
|
@@ -1058,15 +1089,14 @@ const handleSyncOpen =
|
|
|
1058
1089
|
|
|
1059
1090
|
const createHandleSyncMessage =
|
|
1060
1091
|
(
|
|
1061
|
-
deps: PostMessageDep & StorageDep & SqliteDep & ConsoleDep &
|
|
1092
|
+
deps: PostMessageDep & StorageDep & SqliteDep & ConsoleDep & OwnersDep,
|
|
1062
1093
|
): SyncConfig["onMessage"] =>
|
|
1063
1094
|
(input, send) => {
|
|
1064
1095
|
deps.console.log("[db]", "receive sync message", input);
|
|
1065
|
-
const { writeKey } = deps.ownerRowRef.get();
|
|
1066
1096
|
|
|
1067
1097
|
const output = deps.sqlite.transaction(() =>
|
|
1068
1098
|
applyProtocolMessageAsClient(deps)(input, {
|
|
1069
|
-
getWriteKey: (
|
|
1099
|
+
getWriteKey: (ownerId) => deps.owners.get(ownerId)?.writeKey ?? null,
|
|
1070
1100
|
}),
|
|
1071
1101
|
);
|
|
1072
1102
|
if (!output.ok) {
|
|
@@ -1080,20 +1110,21 @@ const createHandleSyncMessage =
|
|
|
1080
1110
|
}
|
|
1081
1111
|
};
|
|
1082
1112
|
|
|
1083
|
-
export interface ClientStorage extends SqliteStorageBase, Storage {}
|
|
1084
|
-
|
|
1085
1113
|
export interface ClientStorageDep {
|
|
1086
1114
|
readonly storage: ClientStorage;
|
|
1087
1115
|
}
|
|
1088
1116
|
|
|
1117
|
+
export interface ClientStorage extends SqliteStorageBase, Storage {}
|
|
1118
|
+
|
|
1089
1119
|
const createClientStorage =
|
|
1090
1120
|
(
|
|
1091
1121
|
deps: SqliteDep &
|
|
1092
1122
|
PostMessageDep &
|
|
1093
1123
|
SymmetricCryptoDep &
|
|
1094
|
-
OwnerRowRefDep &
|
|
1095
1124
|
RandomDep &
|
|
1096
1125
|
TimeDep &
|
|
1126
|
+
OwnersDep &
|
|
1127
|
+
ClockDep &
|
|
1097
1128
|
TimestampConfigDep,
|
|
1098
1129
|
) =>
|
|
1099
1130
|
(
|
|
@@ -1108,9 +1139,10 @@ const createClientStorage =
|
|
|
1108
1139
|
validateWriteKey: constFalse,
|
|
1109
1140
|
setWriteKey: constFalse,
|
|
1110
1141
|
|
|
1111
|
-
writeMessages: (
|
|
1112
|
-
|
|
1113
|
-
|
|
1142
|
+
writeMessages: (ownerId, messages) => {
|
|
1143
|
+
const owner = deps.owners.get(binaryOwnerIdToOwnerId(ownerId));
|
|
1144
|
+
assert(owner, "Missing owner");
|
|
1145
|
+
|
|
1114
1146
|
const decodedAndDecryptedMessages: Array<CrdtMessage> = [];
|
|
1115
1147
|
|
|
1116
1148
|
for (const message of messages) {
|
|
@@ -1133,20 +1165,23 @@ const createClientStorage =
|
|
|
1133
1165
|
});
|
|
1134
1166
|
}
|
|
1135
1167
|
|
|
1136
|
-
let
|
|
1168
|
+
let clockTimestamp = deps.clock.get();
|
|
1137
1169
|
|
|
1138
1170
|
for (const message of messages) {
|
|
1139
|
-
const receive = receiveTimestamp(deps)(
|
|
1171
|
+
const receive = receiveTimestamp(deps)(
|
|
1172
|
+
clockTimestamp,
|
|
1173
|
+
message.timestamp,
|
|
1174
|
+
);
|
|
1140
1175
|
if (!receive.ok) {
|
|
1141
1176
|
deps.postMessage({ type: "onError", error: receive.error });
|
|
1142
1177
|
return false;
|
|
1143
1178
|
}
|
|
1144
|
-
|
|
1179
|
+
clockTimestamp = receive.value;
|
|
1145
1180
|
}
|
|
1146
1181
|
|
|
1147
1182
|
const applyMessagesResult = applyMessages({ ...deps, storage })(
|
|
1148
1183
|
decodedAndDecryptedMessages,
|
|
1149
|
-
|
|
1184
|
+
clockTimestamp,
|
|
1150
1185
|
);
|
|
1151
1186
|
|
|
1152
1187
|
if (!applyMessagesResult.ok) {
|
|
@@ -1163,6 +1198,9 @@ const createClientStorage =
|
|
|
1163
1198
|
},
|
|
1164
1199
|
|
|
1165
1200
|
readDbChange: (ownerId, timestamp) => {
|
|
1201
|
+
const owner = deps.owners.get(binaryOwnerIdToOwnerId(ownerId));
|
|
1202
|
+
assert(owner, "Missing owner");
|
|
1203
|
+
|
|
1166
1204
|
const result = deps.sqlite.exec<{
|
|
1167
1205
|
table: Base64Url256;
|
|
1168
1206
|
id: BinaryId;
|
|
@@ -1198,9 +1236,8 @@ const createClientStorage =
|
|
|
1198
1236
|
values,
|
|
1199
1237
|
},
|
|
1200
1238
|
};
|
|
1201
|
-
const { encryptionKey } = deps.ownerRowRef.get();
|
|
1202
1239
|
|
|
1203
|
-
return encodeAndEncryptDbChange(deps)(message, encryptionKey);
|
|
1240
|
+
return encodeAndEncryptDbChange(deps)(message, owner.encryptionKey);
|
|
1204
1241
|
},
|
|
1205
1242
|
};
|
|
1206
1243
|
|