@evolu/common 6.0.1-preview.15 → 6.0.1-preview.16

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.
@@ -1,7 +1,8 @@
1
1
  import { ConsoleConfig } from "../Console.js";
2
2
  import { getOrThrow } from "../Result.js";
3
- import { Mnemonic, SimpleName } from "../Type.js";
3
+ import { SimpleName } from "../Type.js";
4
4
  import type { DbIndexesBuilder } from "./Kysely.js";
5
+ import type { AppOwner } from "./Owner.js";
5
6
 
6
7
  export interface Config extends ConsoleConfig {
7
8
  /**
@@ -66,18 +67,17 @@ export interface Config extends ConsoleConfig {
66
67
  readonly indexes?: DbIndexesBuilder;
67
68
 
68
69
  /**
69
- * Use this option to create Evolu with the specified mnemonic. If omitted,
70
- * the mnemonic will be autogenerated. That should be the default behavior
71
- * until special UX requirements are needed (e.g., multitenancy).
70
+ * Initial AppOwner to use when creating Evolu instance. If omitted, a new
71
+ * AppOwner will be generated automatically.
72
72
  */
73
- readonly mnemonic?: Mnemonic;
73
+ readonly initialAppOwner?: AppOwner;
74
74
 
75
75
  /**
76
76
  * Use in-memory SQLite database instead of persistent storage. Useful for
77
77
  * testing or temporary data that doesn't need persistence.
78
78
  *
79
- * In-memory databases exist only in RAM and are completely destroyed when
80
- * the process ends, making them forensically safe for sensitive data.
79
+ * In-memory databases exist only in RAM and are completely destroyed when the
80
+ * process ends, making them forensically safe for sensitive data.
81
81
  *
82
82
  * The default value is: `false`.
83
83
  */
package/src/Evolu/Db.ts CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  CreateMnemonicDep,
11
11
  CreateRandomBytesDep,
12
12
  createSymmetricCrypto,
13
+ EncryptionKey,
13
14
  SymmetricCryptoDecryptError,
14
15
  SymmetricCryptoDep,
15
16
  } from "../Crypto.js";
@@ -19,7 +20,6 @@ import { constFalse, exhaustiveCheck } from "../Function.js";
19
20
  import { NanoIdLibDep } from "../NanoId.js";
20
21
  import { objectToEntries } from "../Object.js";
21
22
  import { RandomDep } from "../Random.js";
22
- import { createRef, Ref } from "../Ref.js";
23
23
  import { ok, Result } from "../Result.js";
24
24
  import {
25
25
  createSqlite,
@@ -42,19 +42,14 @@ import {
42
42
  } from "../Worker.js";
43
43
  import { Config } from "./Config.js";
44
44
  import { makePatches, QueryPatches } from "./Diff.js";
45
- import {
46
- AppOwner,
47
- createAppOwner,
48
- createOwnerRow,
49
- OwnerRow,
50
- OwnerWithWriteAccess,
51
- } from "./Owner.js";
45
+ import { AppOwner, createAppOwner, OwnerId, WriteKey } from "./Owner.js";
52
46
  import {
53
47
  applyProtocolMessageAsClient,
54
48
  Base64Url256,
55
49
  BinaryId,
56
50
  binaryIdToId,
57
51
  BinaryOwnerId,
52
+ binaryOwnerIdToOwnerId,
58
53
  CrdtMessage,
59
54
  createProtocolMessageForSync,
60
55
  createProtocolMessageFromCrdtMessages,
@@ -83,6 +78,7 @@ import {
83
78
  import { CreateSyncDep, SyncConfig, SyncDep } from "./Sync.js";
84
79
  import {
85
80
  binaryTimestampToTimestamp,
81
+ createInitialTimestamp,
86
82
  receiveTimestamp,
87
83
  sendTimestamp,
88
84
  Timestamp,
@@ -90,6 +86,7 @@ import {
90
86
  TimestampCounterOverflowError,
91
87
  TimestampDriftError,
92
88
  TimestampError,
89
+ TimestampString,
93
90
  timestampStringToTimestamp,
94
91
  TimestampTimeOutOfRangeError,
95
92
  timestampToBinaryTimestamp,
@@ -159,7 +156,7 @@ export type DbWorkerInput =
159
156
  export type DbWorkerOutput =
160
157
  | {
161
158
  readonly type: "onInit";
162
- readonly owner: AppOwner;
159
+ readonly appOwner: AppOwner;
163
160
  }
164
161
  | {
165
162
  readonly type: "onError";
@@ -209,21 +206,53 @@ type DbWorkerDeps = Omit<
209
206
  TimestampConfigDep &
210
207
  SymmetricCryptoDep &
211
208
  PostMessageDep &
212
- OwnerRowRefDep &
209
+ OwnersDep &
210
+ ClockDep &
213
211
  GetQueryRowsCacheDep &
214
212
  ClientStorageDep;
215
213
 
216
214
  type PostMessageDep = WorkerPostMessageDep<DbWorkerOutput>;
217
215
 
218
- // TODO: More owners (the whole table with ad-hoc added)
219
- export interface OwnerRowRefDep {
220
- readonly ownerRowRef: Ref<OwnerRow>;
216
+ interface OwnersDep {
217
+ readonly owners: Owners;
218
+ }
219
+
220
+ type Owners = Map<OwnerId, AppOwner>;
221
+
222
+ interface ClockDep {
223
+ readonly clock: Clock;
224
+ }
225
+
226
+ interface Clock {
227
+ readonly get: () => Timestamp;
228
+ readonly save: (timestamp: Timestamp) => Result<void, SqliteError>;
221
229
  }
222
230
 
223
231
  interface GetQueryRowsCacheDep {
224
232
  readonly getQueryRowsCache: (tabId: Id) => QueryRowsCache;
225
233
  }
226
234
 
235
+ const createClock =
236
+ (deps: NanoIdLibDep & SqliteDep) =>
237
+ (initialTimestamp = createInitialTimestamp(deps)): Clock => {
238
+ let currentTimestamp = initialTimestamp;
239
+
240
+ return {
241
+ get: () => currentTimestamp,
242
+ save: (timestamp) => {
243
+ currentTimestamp = timestamp;
244
+
245
+ const timestampString = timestampToTimestampString(timestamp);
246
+ const saveTimestamp = deps.sqlite.exec(sql.prepared`
247
+ update evolu_config set "clock" = ${timestampString};
248
+ `);
249
+ if (!saveTimestamp.ok) return saveTimestamp;
250
+
251
+ return ok();
252
+ },
253
+ };
254
+ };
255
+
227
256
  export const createDbWorkerForPlatform = (
228
257
  platformDeps: DbWorkerPlatformDeps,
229
258
  ): DbWorker => {
@@ -245,35 +274,91 @@ export const createDbWorkerForPlatform = (
245
274
  initMessage.config.name,
246
275
  { memory: initMessage.config.inMemory ?? false },
247
276
  );
248
-
249
277
  if (!sqliteResult.ok) {
250
278
  postMessage({ type: "onError", error: sqliteResult.error });
251
279
  return null;
252
280
  }
281
+
253
282
  const sqlite = sqliteResult.value;
283
+ const platformDepsWithSqlite = { ...platformDeps, sqlite };
254
284
 
255
285
  const deps = sqlite.transaction(() => {
256
286
  const currentDbSchema = getDbSchema({ sqlite })();
257
287
  if (!currentDbSchema.ok) return currentDbSchema;
258
288
 
289
+ let appOwner: AppOwner;
290
+ let clock: Clock;
291
+
292
+ const versionTableExists = currentDbSchema.value.tables.some(
293
+ (table) => table.name === "evolu_version",
294
+ );
295
+
296
+ if (versionTableExists) {
297
+ const versionResult = sqlite.exec<{
298
+ protocolVersion: number;
299
+ }>(sql`select protocolVersion from evolu_version limit 1;`);
300
+ if (!versionResult.ok) return versionResult;
301
+
302
+ // TODO: Handle version migrations here if needed
303
+ // const [{ protocolVersion }] = protocolVersionResult.value.rows;
304
+ // if (protocolVersion < currentProtocolVersion) {
305
+ // const migrateResult = migrateDatabase({ sqlite })(
306
+ // protocolVersion,
307
+ // currentProtocolVersion
308
+ // );
309
+ // if (!migrateResult.ok) return migrateResult;
310
+ // }
311
+
312
+ const configResult = sqlite.exec<{
313
+ clock: TimestampString;
314
+ appOwnerId: OwnerId;
315
+ appOwnerEncryptionKey: EncryptionKey;
316
+ appOwnerWriteKey: WriteKey;
317
+ appOwnerMnemonic: Mnemonic | null;
318
+ }>(sql`
319
+ select
320
+ clock,
321
+ appOwnerId,
322
+ appOwnerEncryptionKey,
323
+ appOwnerWriteKey,
324
+ appOwnerMnemonic
325
+ from evolu_config
326
+ limit 1;
327
+ `);
328
+ if (!configResult.ok) return configResult;
329
+
330
+ const [config] = configResult.value.rows;
331
+
332
+ appOwner = {
333
+ type: "AppOwner",
334
+ id: config.appOwnerId,
335
+ encryptionKey: config.appOwnerEncryptionKey,
336
+ writeKey: config.appOwnerWriteKey,
337
+ mnemonic: config.appOwnerMnemonic,
338
+ };
339
+ clock = createClock(platformDepsWithSqlite)(
340
+ timestampStringToTimestamp(config.clock),
341
+ );
342
+ } else {
343
+ appOwner =
344
+ initMessage.config.initialAppOwner ??
345
+ createAppOwner(platformDeps.createMnemonic());
346
+ clock = createClock(platformDepsWithSqlite)();
347
+ const initializeDbResult = initializeDb(platformDepsWithSqlite)(
348
+ appOwner,
349
+ clock,
350
+ );
351
+ if (!initializeDbResult.ok) return initializeDbResult;
352
+ }
353
+
259
354
  const ensureDbSchemaResult = ensureDbSchema({ sqlite })(
260
355
  initMessage.dbSchema,
261
356
  currentDbSchema.value,
262
357
  );
263
358
  if (!ensureDbSchemaResult.ok) return ensureDbSchemaResult;
264
359
 
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;
360
+ const owners: Owners = new Map();
361
+ owners.set(appOwner.id, appOwner);
277
362
 
278
363
  const depsWithoutSyncAndStorage = {
279
364
  ...platformDeps,
@@ -282,7 +367,8 @@ export const createDbWorkerForPlatform = (
282
367
  timestampConfig: initMessage.config,
283
368
  symmetricCrypto: createSymmetricCrypto(platformDeps),
284
369
  getQueryRowsCache,
285
- ownerRowRef: createRef(ownerRow),
370
+ clock,
371
+ owners,
286
372
  };
287
373
 
288
374
  const storage = createClientStorage(depsWithoutSyncAndStorage)({
@@ -297,12 +383,15 @@ export const createDbWorkerForPlatform = (
297
383
  storage: storage.value,
298
384
  };
299
385
 
300
- if (!ownerExists && isNonEmptyReadonlyArray(initMessage.initialData)) {
386
+ if (
387
+ !versionTableExists &&
388
+ isNonEmptyReadonlyArray(initMessage.initialData)
389
+ ) {
301
390
  const result = applyChanges(depsWithoutSync)(initMessage.initialData);
302
391
  if (!result.ok) return result;
303
392
  }
304
393
 
305
- postMessage({ type: "onInit", owner: appOwner });
394
+ postMessage({ type: "onInit", appOwner });
306
395
 
307
396
  const sync = platformDeps.createSync(platformDeps)({
308
397
  ...initMessage.config,
@@ -397,15 +486,11 @@ export const createDbWorkerForPlatform = (
397
486
  const messages = applyChanges(deps)(toSyncChanges, onChange);
398
487
  if (!messages.ok) return messages;
399
488
 
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
- }
489
+ // TODO: Use owner from db change or AppOwner
490
+ const owner = Array.from(deps.owners.values())[0];
406
491
 
407
492
  const protocolMessage = createProtocolMessageFromCrdtMessages(deps)(
408
- owner as OwnerWithWriteAccess,
493
+ owner,
409
494
  messages.value,
410
495
  );
411
496
 
@@ -460,9 +545,10 @@ export const createDbWorkerForPlatform = (
460
545
  );
461
546
  if (!ensureDbSchemaResult.ok) return ensureDbSchemaResult;
462
547
 
463
- const initializeDbResult = initializeDb(deps)(
464
- message.restore.mnemonic,
465
- );
548
+ const appOwner = createAppOwner(message.restore.mnemonic);
549
+ const clock = createClock(deps)();
550
+
551
+ const initializeDbResult = initializeDb(deps)(appOwner, clock);
466
552
  if (!initializeDbResult.ok) return initializeDbResult;
467
553
  }
468
554
  return ok();
@@ -712,57 +798,54 @@ export const createAppTable = (
712
798
  );
713
799
  ` as SafeSql;
714
800
 
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
801
  const initializeDb =
802
+ (deps: SqliteDep & CreateMnemonicDep & TimeDep & CreateRandomBytesDep) =>
746
803
  (
747
- deps: SqliteDep &
748
- NanoIdLibDep &
749
- CreateMnemonicDep &
750
- TimeDep &
751
- CreateRandomBytesDep,
752
- ) =>
753
- (mnemonic?: Mnemonic): Result<[AppOwner, OwnerRow], SqliteError> => {
804
+ initialAppOwner: AppOwner,
805
+ initialClock: Clock,
806
+ ): Result<void, SqliteError> => {
754
807
  for (const query of [
808
+ // Never change structure to ensure all versions can read it.
809
+ sql`
810
+ create table evolu_version (
811
+ "protocolVersion" integer not null
812
+ )
813
+ strict;
814
+ `,
815
+
816
+ sql`
817
+ insert into evolu_version ("protocolVersion")
818
+ values (${protocolVersion});
819
+ `,
820
+
755
821
  sql`
756
822
  create table evolu_config (
757
- "key" text not null primary key,
758
- "value" any not null
823
+ "clock" text not null,
824
+ "appOwnerId" text not null,
825
+ "appOwnerEncryptionKey" blob not null,
826
+ "appOwnerWriteKey" blob not null,
827
+ "appOwnerMnemonic" text
759
828
  )
760
829
  strict;
761
830
  `,
762
831
 
763
832
  sql`
764
- insert into evolu_config ("key", "value")
765
- values ('protocolVersion', ${protocolVersion});
833
+ insert into evolu_config
834
+ (
835
+ "clock",
836
+ "appOwnerId",
837
+ "appOwnerEncryptionKey",
838
+ "appOwnerWriteKey",
839
+ "appOwnerMnemonic"
840
+ )
841
+ values
842
+ (
843
+ ${timestampToTimestampString(initialClock.get())},
844
+ ${initialAppOwner.id},
845
+ ${initialAppOwner.encryptionKey},
846
+ ${initialAppOwner.writeKey},
847
+ ${initialAppOwner.mnemonic ?? null}
848
+ );
766
849
  `,
767
850
 
768
851
  /**
@@ -802,50 +885,12 @@ const initializeDb =
802
885
  "timestamp" desc
803
886
  );
804
887
  `,
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
888
  ]) {
818
889
  const result = deps.sqlite.exec(query);
819
890
  if (!result.ok) return result;
820
891
  }
821
892
 
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]);
893
+ return ok();
849
894
  };
850
895
 
851
896
  const applyChanges =
@@ -854,8 +899,9 @@ const applyChanges =
854
899
  TimeDep &
855
900
  TimestampConfigDep &
856
901
  RandomDep &
857
- OwnerRowRefDep &
858
- ClientStorageDep,
902
+ ClientStorageDep &
903
+ OwnersDep &
904
+ ClockDep,
859
905
  ) =>
860
906
  (
861
907
  changes: NonEmptyReadonlyArray<DbChange>,
@@ -867,20 +913,18 @@ const applyChanges =
867
913
  | TimestampCounterOverflowError
868
914
  | SqliteError
869
915
  > => {
870
- let lastTimestamp = timestampStringToTimestamp(
871
- deps.ownerRowRef.get().timestamp,
872
- );
916
+ let clockTimestamp = deps.clock.get();
873
917
 
874
918
  const messages: Array<CrdtMessage> = [];
875
919
 
876
920
  for (const change of changes) {
877
- const nextTimestamp = sendTimestamp(deps)(lastTimestamp);
921
+ const nextTimestamp = sendTimestamp(deps)(clockTimestamp);
878
922
  if (!nextTimestamp.ok) return nextTimestamp;
879
- lastTimestamp = nextTimestamp.value;
880
- messages.push({ timestamp: lastTimestamp, change });
923
+ clockTimestamp = nextTimestamp.value;
924
+ messages.push({ timestamp: clockTimestamp, change });
881
925
  }
882
926
 
883
- const apply = applyMessages(deps)(messages, lastTimestamp);
927
+ const apply = applyMessages(deps)(messages, clockTimestamp);
884
928
  if (!apply.ok) return apply;
885
929
 
886
930
  if (onChange) onChange();
@@ -890,12 +934,14 @@ const applyChanges =
890
934
  };
891
935
 
892
936
  const applyMessages =
893
- (deps: SqliteDep & RandomDep & OwnerRowRefDep & ClientStorageDep) =>
937
+ (deps: SqliteDep & RandomDep & ClientStorageDep & ClockDep & OwnersDep) =>
894
938
  (
895
939
  messages: ReadonlyArray<CrdtMessage>,
896
- lastTimestamp: Timestamp,
940
+ clockTimestamp: Timestamp,
897
941
  ): Result<void, SqliteError> => {
898
- const ownerId = ownerIdToBinaryOwnerId(deps.ownerRowRef.get().id);
942
+ const ownerId = ownerIdToBinaryOwnerId(
943
+ Array.from(deps.owners.values())[0].id,
944
+ );
899
945
 
900
946
  for (const message of messages) {
901
947
  const result1 = applyMessageToAppTable(deps)(ownerId, message);
@@ -908,18 +954,11 @@ const applyMessages =
908
954
  if (!result2.ok) return result2;
909
955
  }
910
956
 
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();
957
+ return deps.clock.save(clockTimestamp);
919
958
  };
920
959
 
921
960
  const applyMessageToAppTable =
922
- (deps: SqliteDep & OwnerRowRefDep) =>
961
+ (deps: SqliteDep) =>
923
962
  (ownerId: BinaryOwnerId, message: CrdtMessage): Result<void, SqliteError> => {
924
963
  const timestamp = timestampToBinaryTimestamp(message.timestamp);
925
964
  const updatedAt = new Date(message.timestamp.millis).toISOString();
@@ -1046,11 +1085,11 @@ const dropAllTables = (deps: SqliteDep): Result<void, SqliteError> => {
1046
1085
  };
1047
1086
 
1048
1087
  const handleSyncOpen =
1049
- (deps: OwnerRowRefDep & StorageDep & ConsoleDep): SyncConfig["onOpen"] =>
1088
+ (deps: StorageDep & ConsoleDep & OwnersDep): SyncConfig["onOpen"] =>
1050
1089
  (send) => {
1051
- const ownerId = deps.ownerRowRef.get().id;
1052
- const message = createProtocolMessageForSync(deps)(ownerId);
1053
- if (message) {
1090
+ for (const [id] of deps.owners) {
1091
+ const message = createProtocolMessageForSync(deps)(id);
1092
+ if (!message) return;
1054
1093
  deps.console.log("[db]", "send initial sync message", message);
1055
1094
  send(message);
1056
1095
  }
@@ -1058,15 +1097,14 @@ const handleSyncOpen =
1058
1097
 
1059
1098
  const createHandleSyncMessage =
1060
1099
  (
1061
- deps: PostMessageDep & StorageDep & SqliteDep & ConsoleDep & OwnerRowRefDep,
1100
+ deps: PostMessageDep & StorageDep & SqliteDep & ConsoleDep & OwnersDep,
1062
1101
  ): SyncConfig["onMessage"] =>
1063
1102
  (input, send) => {
1064
1103
  deps.console.log("[db]", "receive sync message", input);
1065
- const { writeKey } = deps.ownerRowRef.get();
1066
1104
 
1067
1105
  const output = deps.sqlite.transaction(() =>
1068
1106
  applyProtocolMessageAsClient(deps)(input, {
1069
- getWriteKey: (_ownerId) => writeKey,
1107
+ getWriteKey: (ownerId) => deps.owners.get(ownerId)?.writeKey ?? null,
1070
1108
  }),
1071
1109
  );
1072
1110
  if (!output.ok) {
@@ -1080,20 +1118,21 @@ const createHandleSyncMessage =
1080
1118
  }
1081
1119
  };
1082
1120
 
1083
- export interface ClientStorage extends SqliteStorageBase, Storage {}
1084
-
1085
1121
  export interface ClientStorageDep {
1086
1122
  readonly storage: ClientStorage;
1087
1123
  }
1088
1124
 
1125
+ export interface ClientStorage extends SqliteStorageBase, Storage {}
1126
+
1089
1127
  const createClientStorage =
1090
1128
  (
1091
1129
  deps: SqliteDep &
1092
1130
  PostMessageDep &
1093
1131
  SymmetricCryptoDep &
1094
- OwnerRowRefDep &
1095
1132
  RandomDep &
1096
1133
  TimeDep &
1134
+ OwnersDep &
1135
+ ClockDep &
1097
1136
  TimestampConfigDep,
1098
1137
  ) =>
1099
1138
  (
@@ -1108,9 +1147,10 @@ const createClientStorage =
1108
1147
  validateWriteKey: constFalse,
1109
1148
  setWriteKey: constFalse,
1110
1149
 
1111
- writeMessages: (_ownerId, messages) => {
1112
- // TODO: Get owner by _ownerId when we support more.
1113
- const owner = deps.ownerRowRef.get();
1150
+ writeMessages: (ownerId, messages) => {
1151
+ const owner = deps.owners.get(binaryOwnerIdToOwnerId(ownerId));
1152
+ assert(owner, "Missing owner");
1153
+
1114
1154
  const decodedAndDecryptedMessages: Array<CrdtMessage> = [];
1115
1155
 
1116
1156
  for (const message of messages) {
@@ -1133,20 +1173,23 @@ const createClientStorage =
1133
1173
  });
1134
1174
  }
1135
1175
 
1136
- let timestamp = timestampStringToTimestamp(owner.timestamp);
1176
+ let clockTimestamp = deps.clock.get();
1137
1177
 
1138
1178
  for (const message of messages) {
1139
- const receive = receiveTimestamp(deps)(timestamp, message.timestamp);
1179
+ const receive = receiveTimestamp(deps)(
1180
+ clockTimestamp,
1181
+ message.timestamp,
1182
+ );
1140
1183
  if (!receive.ok) {
1141
1184
  deps.postMessage({ type: "onError", error: receive.error });
1142
1185
  return false;
1143
1186
  }
1144
- timestamp = receive.value;
1187
+ clockTimestamp = receive.value;
1145
1188
  }
1146
1189
 
1147
1190
  const applyMessagesResult = applyMessages({ ...deps, storage })(
1148
1191
  decodedAndDecryptedMessages,
1149
- timestamp,
1192
+ clockTimestamp,
1150
1193
  );
1151
1194
 
1152
1195
  if (!applyMessagesResult.ok) {
@@ -1163,6 +1206,9 @@ const createClientStorage =
1163
1206
  },
1164
1207
 
1165
1208
  readDbChange: (ownerId, timestamp) => {
1209
+ const owner = deps.owners.get(binaryOwnerIdToOwnerId(ownerId));
1210
+ assert(owner, "Missing owner");
1211
+
1166
1212
  const result = deps.sqlite.exec<{
1167
1213
  table: Base64Url256;
1168
1214
  id: BinaryId;
@@ -1198,9 +1244,8 @@ const createClientStorage =
1198
1244
  values,
1199
1245
  },
1200
1246
  };
1201
- const { encryptionKey } = deps.ownerRowRef.get();
1202
1247
 
1203
- return encodeAndEncryptDbChange(deps)(message, encryptionKey);
1248
+ return encodeAndEncryptDbChange(deps)(message, owner.encryptionKey);
1204
1249
  },
1205
1250
  };
1206
1251
 
@@ -583,11 +583,13 @@ const createEvoluInstance =
583
583
 
584
584
  deps.console.log("[evolu]", "createEvoluInstance");
585
585
 
586
+ // evoluConfig.mnemonic
587
+
586
588
  const { initialData, indexes, ...config } = evoluConfig;
587
589
 
588
590
  const errorStore = createStore<EvoluError | null>(null);
589
591
  const rowsStore = createStore<QueryRowsMap>(new Map());
590
- const ownerStore = createStore<AppOwner | null>(null);
592
+ const appOwnerStore = createStore<AppOwner | null>(null);
591
593
  const syncStore = createStore<SyncState>(initialSyncState);
592
594
 
593
595
  const subscribedQueries = createSubscribedQueries(rowsStore);
@@ -605,7 +607,7 @@ const createEvoluInstance =
605
607
  dbWorker.onMessage((message) => {
606
608
  switch (message.type) {
607
609
  case "onInit": {
608
- ownerStore.set(message.owner);
610
+ appOwnerStore.set(message.appOwner);
609
611
  break;
610
612
  }
611
613
 
@@ -907,8 +909,8 @@ const createEvoluInstance =
907
909
  getQueryRows: <R extends Row>(query: Query<R>): QueryRows<R> =>
908
910
  (rowsStore.get().get(query) ?? emptyRows) as QueryRows<R>,
909
911
 
910
- subscribeAppOwner: ownerStore.subscribe,
911
- getAppOwner: ownerStore.get,
912
+ subscribeAppOwner: appOwnerStore.subscribe,
913
+ getAppOwner: appOwnerStore.get,
912
914
 
913
915
  subscribeSyncState: syncStore.subscribe,
914
916
  getSyncState: syncStore.get,