@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/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 owner: AppOwner;
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
- OwnerRowRefDep &
205
+ OwnersDep &
206
+ ClockDep &
213
207
  GetQueryRowsCacheDep &
214
208
  ClientStorageDep;
215
209
 
216
210
  type PostMessageDep = WorkerPostMessageDep<DbWorkerOutput>;
217
211
 
218
- // TODO: More owners (the whole table with ad-hoc added)
219
- export interface OwnerRowRefDep {
220
- readonly ownerRowRef: Ref<OwnerRow>;
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 ownerExists = currentDbSchema.value.tables.some(
266
- (table) => table.name === "evolu_owner",
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
- ownerRowRef: createRef(ownerRow),
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
- if (!ownerExists && isNonEmptyReadonlyArray(initMessage.initialData)) {
301
- const result = applyChanges(depsWithoutSync)(initMessage.initialData);
302
- if (!result.ok) return result;
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
- const owner = deps.ownerRowRef.get();
401
- // TODO: Check owner whether it's allowed to write, return an
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 as OwnerWithWriteAccess,
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 initializeDbResult = initializeDb(deps)(
464
- message.restore.mnemonic,
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
- deps: SqliteDep &
748
- NanoIdLibDep &
749
- CreateMnemonicDep &
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
- "key" text not null primary key,
758
- "value" any not null
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 ("key", "value")
765
- values ('protocolVersion', ${protocolVersion});
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
- const appOwner = createAppOwner(deps)(mnemonic);
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
- OwnerRowRefDep &
858
- ClientStorageDep,
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 lastTimestamp = timestampStringToTimestamp(
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)(lastTimestamp);
913
+ const nextTimestamp = sendTimestamp(deps)(clockTimestamp);
878
914
  if (!nextTimestamp.ok) return nextTimestamp;
879
- lastTimestamp = nextTimestamp.value;
880
- messages.push({ timestamp: lastTimestamp, change });
915
+ clockTimestamp = nextTimestamp.value;
916
+ messages.push({ timestamp: clockTimestamp, change });
881
917
  }
882
918
 
883
- const apply = applyMessages(deps)(messages, lastTimestamp);
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 & OwnerRowRefDep & ClientStorageDep) =>
929
+ (deps: SqliteDep & RandomDep & ClientStorageDep & ClockDep & OwnersDep) =>
894
930
  (
895
931
  messages: ReadonlyArray<CrdtMessage>,
896
- lastTimestamp: Timestamp,
932
+ clockTimestamp: Timestamp,
897
933
  ): Result<void, SqliteError> => {
898
- const ownerId = ownerIdToBinaryOwnerId(deps.ownerRowRef.get().id);
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
- const timestamp = timestampToTimestampString(lastTimestamp);
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 & OwnerRowRefDep) =>
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: OwnerRowRefDep & StorageDep & ConsoleDep): SyncConfig["onOpen"] =>
1080
+ (deps: StorageDep & ConsoleDep & OwnersDep): SyncConfig["onOpen"] =>
1050
1081
  (send) => {
1051
- const ownerId = deps.ownerRowRef.get().id;
1052
- const message = createProtocolMessageForSync(deps)(ownerId);
1053
- if (message) {
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 & OwnerRowRefDep,
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: (_ownerId) => writeKey,
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: (_ownerId, messages) => {
1112
- // TODO: Get owner by _ownerId when we support more.
1113
- const owner = deps.ownerRowRef.get();
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 timestamp = timestampStringToTimestamp(owner.timestamp);
1168
+ let clockTimestamp = deps.clock.get();
1137
1169
 
1138
1170
  for (const message of messages) {
1139
- const receive = receiveTimestamp(deps)(timestamp, message.timestamp);
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
- timestamp = receive.value;
1179
+ clockTimestamp = receive.value;
1145
1180
  }
1146
1181
 
1147
1182
  const applyMessagesResult = applyMessages({ ...deps, storage })(
1148
1183
  decodedAndDecryptedMessages,
1149
- timestamp,
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