@evolu/common 6.0.1-preview.1 → 6.0.1-preview.11

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.
Files changed (40) hide show
  1. package/dist/src/Assert.d.ts +6 -3
  2. package/dist/src/Assert.d.ts.map +1 -1
  3. package/dist/src/Assert.js +6 -3
  4. package/dist/src/Crypto.d.ts +11 -0
  5. package/dist/src/Crypto.d.ts.map +1 -1
  6. package/dist/src/Evolu/Config.d.ts +17 -4
  7. package/dist/src/Evolu/Config.d.ts.map +1 -1
  8. package/dist/src/Evolu/Config.js +1 -1
  9. package/dist/src/Evolu/Db.d.ts +21 -13
  10. package/dist/src/Evolu/Db.d.ts.map +1 -1
  11. package/dist/src/Evolu/Db.js +81 -64
  12. package/dist/src/Evolu/Evolu.d.ts +6 -6
  13. package/dist/src/Evolu/Evolu.d.ts.map +1 -1
  14. package/dist/src/Evolu/Evolu.js +19 -24
  15. package/dist/src/Evolu/Protocol.d.ts +79 -37
  16. package/dist/src/Evolu/Protocol.d.ts.map +1 -1
  17. package/dist/src/Evolu/Protocol.js +170 -58
  18. package/dist/src/Evolu/Relay.d.ts +3 -1
  19. package/dist/src/Evolu/Relay.d.ts.map +1 -1
  20. package/dist/src/Evolu/Relay.js +39 -3
  21. package/dist/src/Evolu/Schema.d.ts +24 -40
  22. package/dist/src/Evolu/Schema.d.ts.map +1 -1
  23. package/dist/src/Evolu/Schema.js +13 -72
  24. package/dist/src/Evolu/Storage.d.ts +1 -0
  25. package/dist/src/Evolu/Storage.d.ts.map +1 -1
  26. package/dist/src/Evolu/Storage.js +10 -0
  27. package/dist/src/Type.d.ts +42 -2
  28. package/dist/src/Type.d.ts.map +1 -1
  29. package/dist/src/Type.js +58 -1
  30. package/package.json +3 -3
  31. package/src/Assert.ts +6 -3
  32. package/src/Crypto.ts +13 -0
  33. package/src/Evolu/Config.ts +19 -5
  34. package/src/Evolu/Db.ts +92 -76
  35. package/src/Evolu/Evolu.ts +38 -35
  36. package/src/Evolu/Protocol.ts +209 -89
  37. package/src/Evolu/Relay.ts +45 -4
  38. package/src/Evolu/Schema.ts +102 -130
  39. package/src/Evolu/Storage.ts +12 -0
  40. package/src/Type.ts +71 -3
package/src/Evolu/Db.ts CHANGED
@@ -34,7 +34,7 @@ import {
34
34
  SqliteValue,
35
35
  } from "../Sqlite.js";
36
36
  import { TimeDep } from "../Time.js";
37
- import { Id, Mnemonic, object, SimpleName, String } from "../Type.js";
37
+ import { array, Id, Mnemonic, object, SimpleName, String } from "../Type.js";
38
38
  import {
39
39
  createInitializedWorker,
40
40
  Worker,
@@ -54,6 +54,7 @@ import {
54
54
  Base64Url256,
55
55
  BinaryId,
56
56
  binaryIdToId,
57
+ BinaryOwnerId,
57
58
  CrdtMessage,
58
59
  createProtocolMessageForSync,
59
60
  createProtocolMessageFromCrdtMessages,
@@ -96,19 +97,21 @@ import {
96
97
  timestampToTimestampString,
97
98
  } from "./Timestamp.js";
98
99
 
99
- export interface DbSchema {
100
- readonly tables: ReadonlyArray<DbTable>;
101
- readonly indexes: ReadonlyArray<DbIndex>;
102
- }
103
-
104
- export interface DbTable {
105
- readonly name: Base64Url256;
106
- readonly columns: ReadonlyArray<Base64Url256>;
107
- }
100
+ export const DbTable = object({
101
+ name: Base64Url256,
102
+ columns: array(Base64Url256),
103
+ });
104
+ export type DbTable = typeof DbTable.Type;
108
105
 
109
106
  export const DbIndex = object({ name: String, sql: String });
110
107
  export type DbIndex = typeof DbIndex.Type;
111
108
 
109
+ export const DbSchema = object({
110
+ tables: array(DbTable),
111
+ indexes: array(DbIndex),
112
+ });
113
+ export type DbSchema = typeof DbSchema.Type;
114
+
112
115
  export type DbWorker = Worker<DbWorkerInput, DbWorkerOutput>;
113
116
 
114
117
  export type CreateDbWorker = (name: SimpleName) => DbWorker;
@@ -241,6 +244,7 @@ export const createDbWorkerForPlatform = (
241
244
 
242
245
  const sqliteResult = await createSqlite(platformDeps)(
243
246
  initMessage.config.name,
247
+ { memory: initMessage.config.inMemory ?? false },
244
248
  );
245
249
 
246
250
  if (!sqliteResult.ok) {
@@ -496,7 +500,6 @@ export const createDbWorkerForPlatform = (
496
500
  onCompleteId: message.onCompleteId,
497
501
  reload: message.reload,
498
502
  });
499
- deps.sqlite[Symbol.dispose]();
500
503
 
501
504
  break;
502
505
  }
@@ -621,34 +624,32 @@ const indexesAreEqual = (self: DbIndex, that: DbIndex): boolean =>
621
624
 
622
625
  export interface DbSnapshot {
623
626
  readonly schema: DbSchema;
624
- readonly rows: Array<{
625
- rows: ReadonlyArray<SqliteRow>;
627
+ readonly tables: Array<{
626
628
  name: string;
629
+ rows: ReadonlyArray<SqliteRow>;
627
630
  }>;
628
631
  }
629
632
 
630
- // TODO: Move it to Sqlite.
631
- export const getDbSnapshot = (
632
- deps: SqliteDep,
633
- ): Result<DbSnapshot, SqliteError> => {
633
+ // TODO: Move to test helpers.
634
+ export const getDbSnapshot = (deps: SqliteDep): DbSnapshot => {
634
635
  const schema = getDbSchema(deps)({ allIndexes: true });
635
- if (!schema.ok) return schema;
636
+ assert(schema.ok, "bug");
636
637
 
637
- const rows = [];
638
+ const tables = [];
638
639
 
639
640
  for (const table of schema.value.tables) {
640
641
  const result = deps.sqlite.exec(sql`
641
642
  select * from ${sql.identifier(table.name)};
642
643
  `);
643
- if (!result.ok) return result;
644
+ assert(result.ok, "bug");
644
645
 
645
- rows.push({
646
- rows: result.value.rows,
646
+ tables.push({
647
647
  name: table.name,
648
+ rows: result.value.rows,
648
649
  });
649
650
  }
650
651
 
651
- return ok({ schema: schema.value, rows });
652
+ return { schema: schema.value, tables };
652
653
  };
653
654
 
654
655
  const ensureDbSchema =
@@ -787,41 +788,39 @@ const initializeDb =
787
788
  `,
788
789
 
789
790
  /**
790
- * The History table stores all values per timestamp, table, row, and
791
- * column. It's required for merging without conflicts. Evolu uses
792
- * last-write-win CRDT. In case of last-write-win value isn't what we
793
- * want, we can use time travel. The current implementation prefers
794
- * performance over storage size. The History table denormalizes Timestamp
795
- * and DbChange to leverage a covering index. Hence, every value change
796
- * has its timestamp, table, row, and column. In the future, we will
797
- * rethink that and store history more efficiently.
798
- *
799
- * There is no need to use `OwnerId` on the client, because timestamps
800
- * (which include a `NodeId`) are globally unique. The relay needs
801
- * `OwnerId` because it hosts multiple apps/owners, but client storage
802
- * represents one DB, and even when it hosts multiple owners, their
803
- * timestamps remain unique.
791
+ * The History table stores all values per ownerId, timestamp, table, id,
792
+ * and column for conflict-free merging using last-write-win CRDT.
793
+ * Denormalizes Timestamp and DbChange for covering index performance.
794
+ * Time travel is available when last-write-win isn't desired. Future
795
+ * optimization will store history more efficiently.
804
796
  */
805
797
  sql`
806
798
  create table evolu_history (
807
- "timestamp" blob not null,
799
+ "ownerId" blob not null,
808
800
  "table" text not null,
809
- "row" blob not null,
801
+ "id" blob not null,
810
802
  "column" text not null,
803
+ "timestamp" blob not null,
811
804
  "value" any
812
805
  )
813
806
  strict;
814
807
  `,
815
808
 
809
+ // Index for reading database changes by owner and timestamp.
810
+ // Timestamp always corresponds to a DbChange.
816
811
  sql`
817
- create index evolu_history_timestamp on evolu_history ("timestamp");
812
+ create index evolu_history_ownerId_timestamp on evolu_history (
813
+ "ownerId",
814
+ "timestamp"
815
+ );
818
816
  `,
819
817
 
820
818
  sql`
821
- create unique index evolu_history_row_column_table_timestampDesc on evolu_history (
822
- "row",
823
- "column",
819
+ create unique index evolu_history_ownerId_table_id_column_timestampDesc on evolu_history (
820
+ "ownerId",
824
821
  "table",
822
+ "id",
823
+ "column",
825
824
  "timestamp" desc
826
825
  );
827
826
  `,
@@ -847,7 +846,14 @@ const initializeDb =
847
846
 
848
847
  const result = deps.sqlite.exec(sql`
849
848
  insert into evolu_owner
850
- (mnemonic, id, encryptionKey, createdAt, writeKey, timestamp)
849
+ (
850
+ "mnemonic",
851
+ "id",
852
+ "encryptionKey",
853
+ "createdAt",
854
+ "writeKey",
855
+ "timestamp"
856
+ )
851
857
  values
852
858
  (
853
859
  ${ownerRow.mnemonic},
@@ -910,16 +916,18 @@ const applyMessages =
910
916
  (
911
917
  messages: ReadonlyArray<CrdtMessage>,
912
918
  lastTimestamp: Timestamp,
913
- options: { isMigrationFromVersion0?: boolean } = {},
914
919
  ): Result<void, SqliteError> => {
920
+ const ownerId = ownerIdToBinaryOwnerId(deps.ownerRowRef.get().id);
921
+
915
922
  for (const message of messages) {
916
- if (!options.isMigrationFromVersion0) {
917
- const apply1 = applyMessageToAppTable(deps)(message);
918
- if (!apply1.ok) return apply1;
919
- }
923
+ const result1 = applyMessageToAppTable(deps)(ownerId, message);
924
+ if (!result1.ok) return result1;
920
925
 
921
- const apply2 = applyMessageToTimestampAndHistoryTables(deps)(message);
922
- if (!apply2.ok) return apply2;
926
+ const result2 = applyMessageToTimestampAndHistoryTables(deps)(
927
+ ownerId,
928
+ message,
929
+ );
930
+ if (!result2.ok) return result2;
923
931
  }
924
932
 
925
933
  const timestamp = timestampToTimestampString(lastTimestamp);
@@ -933,8 +941,8 @@ const applyMessages =
933
941
  };
934
942
 
935
943
  const applyMessageToAppTable =
936
- (deps: SqliteDep) =>
937
- (message: CrdtMessage): Result<void, SqliteError> => {
944
+ (deps: SqliteDep & OwnerRowRefDep) =>
945
+ (ownerId: BinaryOwnerId, message: CrdtMessage): Result<void, SqliteError> => {
938
946
  const date = new Date(message.timestamp.millis).toISOString();
939
947
  const timestamp = timestampToBinaryTimestamp(message.timestamp);
940
948
 
@@ -942,28 +950,29 @@ const applyMessageToAppTable =
942
950
  const result = deps.sqlite.exec(sql.prepared`
943
951
  with
944
952
  lastTimestamp as (
945
- select timestamp
953
+ select "timestamp"
946
954
  from evolu_history
947
955
  where
948
- "row" = ${message.change.id}
949
- and "column" = ${column}
956
+ "ownerId" = ${ownerId}
950
957
  and "table" = ${message.change.table}
951
- order by timestamp desc
958
+ and "id" = ${message.change.id}
959
+ and "column" = ${column}
960
+ order by "timestamp" desc
952
961
  limit 1
953
962
  )
954
963
  insert into ${sql.identifier(message.change.table)}
955
964
  ("id", ${sql.identifier(column)}, createdAt, updatedAt)
956
965
  select ${message.change.id}, ${value}, ${date}, ${date}
957
966
  where
958
- (select timestamp from lastTimestamp) is null
959
- or (select timestamp from lastTimestamp) < ${timestamp}
967
+ (select "timestamp" from lastTimestamp) is null
968
+ or (select "timestamp" from lastTimestamp) < ${timestamp}
960
969
  on conflict ("id") do update
961
970
  set
962
971
  ${sql.identifier(column)} = ${value},
963
972
  updatedAt = ${date}
964
973
  where
965
- (select timestamp from lastTimestamp) is null
966
- or (select timestamp from lastTimestamp) < ${timestamp};
974
+ (select "timestamp" from lastTimestamp) is null
975
+ or (select "timestamp" from lastTimestamp) < ${timestamp};
967
976
  `);
968
977
 
969
978
  if (!result.ok) return result;
@@ -973,10 +982,9 @@ const applyMessageToAppTable =
973
982
  };
974
983
 
975
984
  export const applyMessageToTimestampAndHistoryTables =
976
- (deps: SqliteDep & RandomDep & OwnerRowRefDep & ClientStorageDep) =>
977
- (message: CrdtMessage): Result<void, SqliteError> => {
985
+ (deps: SqliteDep & ClientStorageDep) =>
986
+ (ownerId: BinaryOwnerId, message: CrdtMessage): Result<void, SqliteError> => {
978
987
  const timestamp = timestampToBinaryTimestamp(message.timestamp);
979
- const ownerId = ownerIdToBinaryOwnerId(deps.ownerRowRef.get().id);
980
988
  const id = idToBinaryId(message.change.id);
981
989
 
982
990
  const result = deps.storage.insertTimestamp(ownerId, timestamp);
@@ -985,9 +993,16 @@ export const applyMessageToTimestampAndHistoryTables =
985
993
  for (const [column, value] of Object.entries(message.change.values)) {
986
994
  const result = deps.sqlite.exec(sql.prepared`
987
995
  insert into evolu_history
988
- ("timestamp", "table", "row", "column", "value")
996
+ ("ownerId", "table", "id", "column", "value", "timestamp")
989
997
  values
990
- (${timestamp}, ${message.change.table}, ${id}, ${column}, ${value})
998
+ (
999
+ ${ownerId},
1000
+ ${message.change.table},
1001
+ ${id},
1002
+ ${column},
1003
+ ${value},
1004
+ ${timestamp}
1005
+ )
991
1006
  on conflict do nothing;
992
1007
  `);
993
1008
  if (!result.ok) return result;
@@ -1063,11 +1078,11 @@ export const maybeMigrateToVersion0 =
1063
1078
  const messagesRows = deps.sqlite.exec<{
1064
1079
  timestamp: TimestampString;
1065
1080
  table: Base64Url256;
1066
- row: Id;
1081
+ id: Id;
1067
1082
  column: Base64Url256;
1068
1083
  value: SqliteValue;
1069
1084
  }>(sql`
1070
- select "timestamp", "table", "row", "column", "value" from evolu_message;
1085
+ select "timestamp", "table", "id", "column", "value" from evolu_message;
1071
1086
  `);
1072
1087
 
1073
1088
  if (!messagesRows.ok) return messagesRows;
@@ -1083,7 +1098,7 @@ export const maybeMigrateToVersion0 =
1083
1098
  const messages = messagesRows.value.rows.map((message) => ({
1084
1099
  timestamp: timestampStringToTimestamp(message.timestamp),
1085
1100
  change: {
1086
- id: message.row,
1101
+ id: message.id,
1087
1102
  table: message.table,
1088
1103
  values: { [message.column]: message.value },
1089
1104
  },
@@ -1176,6 +1191,7 @@ const createClientStorage =
1176
1191
  ...sqliteStorageBase.value,
1177
1192
 
1178
1193
  validateWriteKey: constFalse,
1194
+ setWriteKey: constFalse,
1179
1195
 
1180
1196
  writeMessages: (_ownerId, messages) => {
1181
1197
  // TODO: Get owner by _ownerId when we support more.
@@ -1231,16 +1247,16 @@ const createClientStorage =
1231
1247
  return true;
1232
1248
  },
1233
1249
 
1234
- readDbChange: (_ownerId, timestamp) => {
1250
+ readDbChange: (ownerId, timestamp) => {
1235
1251
  const result = deps.sqlite.exec<{
1236
1252
  table: Base64Url256;
1237
- row: BinaryId;
1253
+ id: BinaryId;
1238
1254
  column: Base64Url256;
1239
1255
  value: SqliteValue;
1240
1256
  }>(sql`
1241
- select "table", "row", "column", "value"
1257
+ select "table", "id", "column", "value"
1242
1258
  from evolu_history
1243
- where "timestamp" = ${timestamp};
1259
+ where "ownerId" = ${ownerId} and "timestamp" = ${timestamp};
1244
1260
  `);
1245
1261
  if (!result.ok) {
1246
1262
  deps.postMessage({ type: "onError", error: result.error });
@@ -1250,18 +1266,18 @@ const createClientStorage =
1250
1266
  const { rows } = result.value;
1251
1267
  assert(rows.length > 0, "Rows must not be empty");
1252
1268
 
1253
- const { table, row } = rows[0];
1269
+ const { table, id } = rows[0];
1254
1270
  const values: Record<string, SqliteValue> = {};
1255
1271
 
1256
1272
  for (const r of rows) {
1257
1273
  assert(r.table === table, "All rows must have the same table");
1258
- assert(eqArrayNumber(r.row, row), "All rows must have the same Id");
1274
+ assert(eqArrayNumber(r.id, id), "All rows must have the same Id");
1259
1275
  values[r.column] = r.value;
1260
1276
  }
1261
1277
 
1262
1278
  const change: DbChange = {
1263
1279
  table: rows[0].table,
1264
- id: binaryIdToId(rows[0].row),
1280
+ id: binaryIdToId(rows[0].id),
1265
1281
  values,
1266
1282
  };
1267
1283
 
@@ -1,5 +1,5 @@
1
1
  import { isNonEmptyArray, isNonEmptyReadonlyArray } from "../Array.js";
2
- import { assertNonEmptyArray } from "../Assert.js";
2
+ import { assert, assertNonEmptyArray } from "../Assert.js";
3
3
  import { createCallbacks } from "../Callbacks.js";
4
4
  import { ConsoleDep } from "../Console.js";
5
5
  import { SymmetricCryptoDecryptError } from "../Crypto.js";
@@ -44,9 +44,9 @@ import {
44
44
  SubscribedQueries,
45
45
  } from "./Query.js";
46
46
  import {
47
- assertValidEvoluSchema,
48
47
  CreateQuery,
49
48
  EvoluSchema,
49
+ evoluSchemaToDbSchema,
50
50
  insertable,
51
51
  Mutation,
52
52
  MutationKind,
@@ -54,7 +54,7 @@ import {
54
54
  MutationOptions,
55
55
  updateable,
56
56
  upsertable,
57
- validEvoluSchemaToDbSchema,
57
+ ValidateSchema,
58
58
  ValidMutationSize,
59
59
  ValidMutationSizeError,
60
60
  } from "./Schema.js";
@@ -88,7 +88,7 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> {
88
88
  * All this function does is compile the Kysely query and serialize it into a
89
89
  * unique string. Both operations are fast and cheap.
90
90
  *
91
- * For mutations, use {@link Evolu.insert} and {@link Evolu.update}.
91
+ * For mutations, use {@link Evolu#insert} and {@link Evolu#update}.
92
92
  *
93
93
  * ### Example
94
94
  *
@@ -111,7 +111,7 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> {
111
111
  * A returned promise always resolves successfully because there is no reason
112
112
  * why loading should fail. All data are local, and the query is typed. A
113
113
  * serious unexpected Evolu error shall be handled with
114
- * {@link Evolu.subscribeError}.
114
+ * {@link Evolu#subscribeError}.
115
115
  *
116
116
  * Loading is batched, and returned promises are cached, so there is no need
117
117
  * for an additional cache. Evolu's internal cache is invalidated on
@@ -144,7 +144,7 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> {
144
144
  * If you are curious why Evolu does not do that for all queries by default,
145
145
  * the answer is simple: performance. Tracking changes is costly and
146
146
  * meaningful only for visible (hence subscribed) queries anyway. To subscribe
147
- * to a query, use {@link Evolu.subscribeQuery}.
147
+ * to a query, use {@link Evolu#subscribeQuery}.
148
148
  *
149
149
  * ### Example
150
150
  *
@@ -300,7 +300,7 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> {
300
300
 
301
301
  /**
302
302
  * Restore {@link AppOwner} with all their synced data. It uses
303
- * {@link Evolu.resetAppOwner}, so be careful.
303
+ * {@link Evolu#resetAppOwner}, so be careful.
304
304
  */
305
305
  readonly restoreAppOwner: (
306
306
  mnemonic: Mnemonic,
@@ -431,9 +431,7 @@ let tabId: Id | null = null;
431
431
  export const createEvolu =
432
432
  (deps: EvoluDeps) =>
433
433
  <S extends EvoluSchema>(
434
- // TODO: Validate missing Id, unsupported types, used default types via TS types
435
- // with type errors messages as we had it in the old Evolu.
436
- schema: S,
434
+ schema: ValidateSchema<S> extends never ? S : ValidateSchema<S>,
437
435
  partialConfig: Partial<EvoluConfigWithInitialData<S>> = {},
438
436
  ): Evolu<S> => {
439
437
  const config = { ...defaultConfig, ...partialConfig };
@@ -441,11 +439,14 @@ export const createEvolu =
441
439
  let evolu = evoluInstances.get(config.name);
442
440
 
443
441
  if (evolu == null) {
444
- evolu = createEvoluInstance(deps)(schema, config as IntentionalNever);
442
+ evolu = createEvoluInstance(deps)(
443
+ schema as EvoluSchema,
444
+ config as IntentionalNever,
445
+ );
445
446
  evoluInstances.set(config.name, evolu);
446
447
  } else {
447
448
  // Hot reloading. Note that indexes are intentionally omitted.
448
- evolu.ensureSchema(schema);
449
+ evolu.ensureSchema(schema as EvoluSchema);
449
450
  }
450
451
 
451
452
  return evolu as IntentionalNever;
@@ -554,10 +555,7 @@ const createEvoluInstance =
554
555
  }
555
556
  });
556
557
 
557
- const dbSchema = validEvoluSchemaToDbSchema(
558
- assertValidEvoluSchema(schema),
559
- indexes,
560
- );
558
+ const dbSchema = evoluSchemaToDbSchema(schema, indexes);
561
559
 
562
560
  const mutationTypesCache = new Map<
563
561
  MutationKind,
@@ -592,21 +590,17 @@ const createEvoluInstance =
592
590
  if (initialData)
593
591
  initialData({
594
592
  insert: (table, props) => {
595
- const Type = getMutationType(table, "insert");
596
593
  const id = createId(deps);
594
+ const values = getMutationType(table, "insert").fromUnknown(props);
597
595
 
598
- const result = Type.fromUnknown(props);
599
-
600
- if (result.ok) {
601
- initialDataDbChanges.push({
602
- id,
603
- table,
604
- values: result.value,
605
- } as unknown as DbChange);
596
+ if (values.ok) {
597
+ const dbChange = { table, id, values: values.value };
598
+ assertValidDbChange(dbChange);
599
+ initialDataDbChanges.push(dbChange);
606
600
  return ok({ id });
607
601
  }
608
602
 
609
- return result;
603
+ return values;
610
604
  },
611
605
  });
612
606
 
@@ -654,14 +648,14 @@ const createEvoluInstance =
654
648
  } else {
655
649
  // Remove `id` from values.
656
650
  const { id: _id, ...values } = result.value;
657
- // EvoluSchema Types ensure valid types.
658
- const change = { table, id, values } as unknown as DbChange;
659
- mutateMicrotaskQueue.push([change, options?.onComplete]);
651
+ const dbChange = { table, id, values };
652
+ assertValidDbChange(dbChange);
653
+ mutateMicrotaskQueue.push([dbChange, options?.onComplete]);
660
654
  }
661
655
 
662
656
  if (mutateMicrotaskQueue.length === 1)
663
657
  queueMicrotask(() => {
664
- const changes = [];
658
+ const changes: Array<DbChange> = [];
665
659
  const onCompletes = [];
666
660
 
667
661
  for (const [change, onComplete] of mutateMicrotaskQueue) {
@@ -807,6 +801,7 @@ const createEvoluInstance =
807
801
  const onCompleteId = callbacks.register(() => {
808
802
  resolve();
809
803
  });
804
+
810
805
  dbWorker.postMessage({
811
806
  type: "reset",
812
807
  onCompleteId,
@@ -822,11 +817,8 @@ const createEvoluInstance =
822
817
 
823
818
  ensureSchema: (schema) => {
824
819
  mutationTypesCache.clear();
825
- const validSchema = assertValidEvoluSchema(schema);
826
- dbWorker.postMessage({
827
- type: "ensureDbSchema",
828
- dbSchema: validEvoluSchemaToDbSchema(validSchema),
829
- });
820
+ const dbSchema = evoluSchemaToDbSchema(schema);
821
+ dbWorker.postMessage({ type: "ensureDbSchema", dbSchema });
830
822
  },
831
823
 
832
824
  exportDatabase: () => {
@@ -945,3 +937,14 @@ const createLoadingPromises = (
945
937
 
946
938
  return loadingPromises;
947
939
  };
940
+
941
+ const assertValidDbChange: (dbChange: {
942
+ table: string;
943
+ id: Id;
944
+ values: unknown;
945
+ }) => asserts dbChange is DbChange = (dbChange) => {
946
+ assert(
947
+ DbChange.is(dbChange),
948
+ `Failed to create DbChange for table "${dbChange.table}". If you see this message, you either disabled EvoluSchema validation or Evolu has a bug - please report it.`,
949
+ );
950
+ };