@evolu/common 1.0.12 → 1.0.14

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/Db.d.ts CHANGED
@@ -52,6 +52,12 @@ export declare const transaction: <R, E, A>(effect: Effect.Effect<R, E, A>) => E
52
52
  export interface NoSuchTableOrColumnError {
53
53
  readonly _tag: "NoSuchTableOrColumnError";
54
54
  }
55
+ export declare const SqliteNoSuchTableOrColumnError: S.Schema<{
56
+ readonly message: string;
57
+ }, {
58
+ readonly message: string;
59
+ }>;
60
+ export type SqliteNoSuchTableOrColumnError = S.Schema.To<typeof SqliteNoSuchTableOrColumnError>;
55
61
  export declare const someDefectToNoSuchTableOrColumnError: <R, E, A>(self: Effect.Effect<R, E, A>) => Effect.Effect<R, NoSuchTableOrColumnError | E, A>;
56
62
  export declare const makeOwner: (mnemonic?: Mnemonic) => Effect.Effect<Bip39, never, Owner>;
57
63
  export declare const lazyInit: (mnemonic?: Mnemonic) => Effect.Effect<Sqlite | Bip39 | NanoId, never, Owner>;
@@ -1 +1 @@
1
- {"version":3,"file":"Db.d.ts","sourceRoot":"","sources":["../../src/Db.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Db.d.ts","sourceRoot":"","sources":["../../src/Db.ts"],"names":[],"mappings":"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"}
package/dist/src/Db.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as AST from "@effect/schema/AST";
2
+ import * as S from "@effect/schema/Schema";
2
3
  import { make } from "@effect/schema/Schema";
3
4
  import { bytesToHex } from "@noble/ciphers/utils";
4
5
  import { Context, Effect, Exit, Option, Predicate, ReadonlyArray, ReadonlyRecord, String, pipe, } from "effect";
@@ -41,20 +42,14 @@ schema) => pipe(getPropertySignatures(schema), ReadonlyRecord.toEntries, Readonl
41
42
  })));
42
43
  export const Owner = Context.Tag("evolu/Owner");
43
44
  export const transaction = (effect) => Effect.flatMap(Sqlite, (sqlite) => Effect.acquireUseRelease(sqlite.exec("BEGIN"), () => effect, (_, exit) => Exit.isFailure(exit) ? sqlite.exec("ROLLBACK") : sqlite.exec("END")));
44
- export const someDefectToNoSuchTableOrColumnError = Effect.catchSomeDefect((error) => {
45
- if (typeof error === "object" &&
46
- error != null &&
47
- "message" in error &&
48
- typeof error.message === "string" &&
49
- error.message.includes("code 1") &&
50
- (error.message.includes("no such table") ||
51
- error.message.includes("no such column") ||
52
- error.message.includes("has no column")))
53
- return Option.some(Effect.fail({
54
- _tag: "NoSuchTableOrColumnError",
55
- }));
56
- return Option.none();
45
+ export const SqliteNoSuchTableOrColumnError = S.struct({
46
+ message: S.union(S.string.pipe(S.includes("no such table")), S.string.pipe(S.includes("no such column")), S.string.pipe(S.includes("has no column"))),
57
47
  });
48
+ export const someDefectToNoSuchTableOrColumnError = Effect.catchSomeDefect((error) => S.is(SqliteNoSuchTableOrColumnError)(error)
49
+ ? Option.some(Effect.fail({
50
+ _tag: "NoSuchTableOrColumnError",
51
+ }))
52
+ : Option.none());
58
53
  export const makeOwner = (mnemonic) => Effect.gen(function* (_) {
59
54
  const bip39 = yield* _(Bip39);
60
55
  if (mnemonic == null)
@@ -1,4 +1,4 @@
1
- import { Brand, Context, Layer, ReadonlyArray, ReadonlyRecord } from "effect";
1
+ import { Brand, Context, Effect, Layer, ReadonlyArray, ReadonlyRecord } from "effect";
2
2
  import { Config } from "./Config.js";
3
3
  import { Bip39, Mnemonic, NanoId } from "./Crypto.js";
4
4
  import { Owner, Tables } from "./Db.js";
@@ -73,7 +73,8 @@ export interface MutateItem {
73
73
  readonly onCompleteId: OnCompleteId | null;
74
74
  }
75
75
  export type RowsCacheMap = ReadonlyMap<Query, ReadonlyArray<Row>>;
76
- export declare const mutateItemsToNewMessages: (items: readonly [MutateItem, ...MutateItem[]]) => readonly [NewMessage, ...NewMessage[]];
76
+ export declare const mutateItemsToNewMessages: (items: ReadonlyArray<MutateItem>) => ReadonlyArray<NewMessage>;
77
+ export declare const upsertValueIntoTableRowColumn: (message: NewMessage, messages: ReadonlyArray<NewMessage>) => Effect.Effect<Sqlite, never, void>;
77
78
  export declare const DbWorkerLive: Layer.Layer<Bip39 | NanoId | Sqlite | SyncWorker, never, DbWorker>;
78
79
  export {};
79
80
  //# sourceMappingURL=DbWorker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DbWorker.d.ts","sourceRoot":"","sources":["../../src/DbWorker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DbWorker.d.ts","sourceRoot":"","sources":["../../src/DbWorker.ts"],"names":[],"mappings":"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"}
@@ -7,14 +7,14 @@ import { Owner, ensureSchema, lazyInit, someDefectToNoSuchTableOrColumnError, tr
7
7
  import { makePatches } from "./Diff.js";
8
8
  import { makeUnexpectedError } from "./Errors.js";
9
9
  import { cast } from "./Model.js";
10
- import { insertIntoMessagesIfNew, insertValueIntoTableRowColumn, selectLastTimestampForTableRowColumn, selectMessagesToSync, selectOwner, selectOwnerTimestampAndMerkleTree, updateOwnerTimestampAndMerkleTree, } from "./Sql.js";
10
+ import * as Sql from "./Sql.js";
11
11
  import { Sqlite, queryObjectFromQuery } from "./Sqlite.js";
12
12
  import { NewMessageEquivalence, SyncWorker, SyncWorkerPostMessage, } from "./SyncWorker.js";
13
13
  export const DbWorker = Context.Tag("evolu/DbWorker");
14
14
  const DbWorkerOnMessage = Context.Tag("evolu/DbWorkerOnMessage");
15
15
  const init = (input) => Effect.gen(function* (_) {
16
16
  const sqlite = yield* _(Sqlite);
17
- return yield* _(sqlite.exec(selectOwner), Effect.map(({ rows: [row] }) => ({
17
+ return yield* _(sqlite.exec(Sql.selectOwner), Effect.map(({ rows: [row] }) => ({
18
18
  id: row.id,
19
19
  mnemonic: row.mnemonic,
20
20
  // expo-sqlite 11.3.2 doesn't support Uint8Array
@@ -37,11 +37,11 @@ const query = ({ queries, onCompleteIds = [], }) => Effect.gen(function* (_) {
37
37
  }));
38
38
  dbWorkerOnMessage({ _tag: "onQuery", queriesPatches, onCompleteIds });
39
39
  });
40
- const readTimestampAndMerkleTree = Sqlite.pipe(Effect.flatMap((sqlite) => sqlite.exec(selectOwnerTimestampAndMerkleTree)), Effect.map((result) => result.rows), Effect.map(([{ timestamp, merkleTree }]) => ({
40
+ const readTimestampAndMerkleTree = Sqlite.pipe(Effect.flatMap((sqlite) => sqlite.exec(Sql.selectOwnerTimestampAndMerkleTree)), Effect.map((result) => result.rows), Effect.map(([{ timestamp, merkleTree }]) => ({
41
41
  timestamp: unsafeTimestampFromString(timestamp),
42
42
  merkleTree: merkleTree,
43
43
  })));
44
- export const mutateItemsToNewMessages = (items) => pipe(items, ReadonlyArray.mapNonEmpty(({ id, isInsert, now, table, values }) => pipe(Object.entries(values), ReadonlyArray.filterMap(([key, value]) =>
44
+ export const mutateItemsToNewMessages = (items) => pipe(items, ReadonlyArray.map(({ id, isInsert, now, table, values }) => pipe(Object.entries(values), ReadonlyArray.filterMap(([key, value]) =>
45
45
  // The value can be undefined if exactOptionalPropertyTypes isn't true.
46
46
  // Don't insert nulls because null is the default value.
47
47
  value === undefined || (isInsert && value == null)
@@ -53,13 +53,13 @@ value === undefined || (isInsert && value == null)
53
53
  : value instanceof Date
54
54
  ? cast(value)
55
55
  : value,
56
- ]), ReadonlyArray.append([isInsert ? "createdAt" : "updatedAt", now]), ReadonlyArray.mapNonEmpty(([key, value]) => ({
56
+ ]), ReadonlyArray.append([isInsert ? "createdAt" : "updatedAt", now]), ReadonlyArray.map(([key, value]) => ({
57
57
  table,
58
58
  row: id,
59
59
  column: key,
60
60
  value,
61
- })))), ReadonlyArray.flattenNonEmpty, ReadonlyArray.dedupeNonEmptyWith(NewMessageEquivalence));
62
- const ensureSchemaByMessages = (messages) => Effect.gen(function* (_) {
61
+ })))), ReadonlyArray.flatten, ReadonlyArray.dedupeWith(NewMessageEquivalence));
62
+ const ensureSchemaByNewMessages = (messages) => Effect.gen(function* (_) {
63
63
  const tablesMap = new Map();
64
64
  messages.forEach((message) => {
65
65
  const table = tablesMap.get(message.table);
@@ -79,23 +79,29 @@ const ensureSchemaByMessages = (messages) => Effect.gen(function* (_) {
79
79
  });
80
80
  yield* _(ensureSchema(Array.from(tablesMap.values())));
81
81
  });
82
+ export const upsertValueIntoTableRowColumn = (message, messages) => Effect.gen(function* (_) {
83
+ const sqlite = yield* _(Sqlite);
84
+ const insert = sqlite.exec({
85
+ sql: Sql.upsertValueIntoTableRowColumn(message.table, message.column),
86
+ parameters: [message.row, message.value, message.value],
87
+ });
88
+ yield* _(insert, someDefectToNoSuchTableOrColumnError, Effect.catchTag("NoSuchTableOrColumnError", () =>
89
+ // If one message fails, we ensure schema for all messages.
90
+ ensureSchemaByNewMessages(messages).pipe(Effect.zipRight(insert))));
91
+ });
82
92
  const applyMessages = ({ merkleTree, messages, }) => Effect.gen(function* (_) {
83
93
  const sqlite = yield* _(Sqlite);
84
94
  for (const message of messages) {
85
95
  const timestamp = yield* _(sqlite.exec({
86
- sql: selectLastTimestampForTableRowColumn,
96
+ sql: Sql.selectLastTimestampForTableRowColumn,
87
97
  parameters: [message.table, message.row, message.column],
88
98
  }), Effect.map((result) => result.rows), Effect.flatMap(ReadonlyArray.head), Effect.map((row) => row.timestamp), Effect.catchTag("NoSuchElementException", () => Effect.succeed(null)));
89
99
  if (timestamp == null || timestamp < message.timestamp) {
90
- const insert = sqlite.exec({
91
- sql: insertValueIntoTableRowColumn(message.table, message.column),
92
- parameters: [message.row, message.value, message.value],
93
- });
94
- yield* _(insert, someDefectToNoSuchTableOrColumnError, Effect.catchTag("NoSuchTableOrColumnError", () => ensureSchemaByMessages(messages).pipe(Effect.flatMap(() => insert))));
100
+ yield* _(upsertValueIntoTableRowColumn(message, messages));
95
101
  }
96
102
  if (timestamp == null || timestamp !== message.timestamp) {
97
103
  const { changes } = yield* _(sqlite.exec({
98
- sql: insertIntoMessagesIfNew,
104
+ sql: Sql.insertIntoMessagesIfNew,
99
105
  parameters: [
100
106
  message.timestamp,
101
107
  message.table,
@@ -104,50 +110,52 @@ const applyMessages = ({ merkleTree, messages, }) => Effect.gen(function* (_) {
104
110
  message.value,
105
111
  ],
106
112
  }));
107
- if (changes > 0)
108
- merkleTree = insertIntoMerkleTree(unsafeTimestampFromString(message.timestamp))(merkleTree);
113
+ if (changes > 0) {
114
+ const timestamp = unsafeTimestampFromString(message.timestamp);
115
+ merkleTree = insertIntoMerkleTree(timestamp)(merkleTree);
116
+ }
109
117
  }
110
118
  }
111
119
  return merkleTree;
112
120
  });
113
121
  const writeTimestampAndMerkleTree = ({ timestamp, merkleTree, }) => Effect.flatMap(Sqlite, (sqlite) => sqlite.exec({
114
- sql: updateOwnerTimestampAndMerkleTree,
122
+ sql: Sql.updateOwnerTimestampAndMerkleTree,
115
123
  parameters: [
116
124
  timestampToString(timestamp),
117
125
  merkleTreeToString(merkleTree),
118
126
  ],
119
127
  }));
120
128
  const mutate = ({ items, queries, }) => Effect.gen(function* (_) {
121
- const owner = yield* _(Owner);
122
- let { timestamp, merkleTree } = yield* _(readTimestampAndMerkleTree);
123
- const messages = yield* _(mutateItemsToNewMessages(items), Effect.forEach((newMessage) => Effect.map(sendTimestamp(timestamp), (nextTimestamp) => {
124
- timestamp = nextTimestamp;
125
- return {
126
- ...newMessage,
127
- timestamp: timestampToString(timestamp),
128
- };
129
- })));
130
- if (ReadonlyArray.isNonEmptyReadonlyArray(messages)) {
129
+ const [toSync, localOnlyItems] = ReadonlyArray.partition(items, (item) => item.table.startsWith("_"));
130
+ const [toUpsert, toDelete] = ReadonlyArray.partition(localOnlyItems, (item) => mutateItemsToNewMessages([item]).some((message) => message.column === "isDeleted" && message.value === 1)).map(mutateItemsToNewMessages);
131
+ yield* _(Effect.forEach(toUpsert, (message) => upsertValueIntoTableRowColumn(message, toUpsert)));
132
+ const { exec } = yield* _(Sqlite);
133
+ yield* _(Effect.forEach(toDelete, ({ table, row }) => exec({ sql: Sql.deleteTableRow(table), parameters: [row] })));
134
+ if (toSync.length > 0) {
135
+ let { timestamp, merkleTree } = yield* _(readTimestampAndMerkleTree);
136
+ const messages = yield* _(mutateItemsToNewMessages(toSync), Effect.forEach((message) => Effect.map(sendTimestamp(timestamp), (nextTimestamp) => {
137
+ timestamp = nextTimestamp;
138
+ return { ...message, timestamp: timestampToString(timestamp) };
139
+ })));
131
140
  merkleTree = yield* _(applyMessages({ merkleTree, messages }));
132
141
  yield* _(writeTimestampAndMerkleTree({ timestamp, merkleTree }));
142
+ (yield* _(SyncWorkerPostMessage))({
143
+ _tag: "sync",
144
+ syncUrl: (yield* _(Config)).syncUrl,
145
+ messages,
146
+ timestamp,
147
+ merkleTree,
148
+ owner: yield* _(Owner),
149
+ syncLoopCount: 0,
150
+ });
133
151
  }
134
152
  const onCompleteIds = ReadonlyArray.filterMap(items, (item) => Option.fromNullable(item.onCompleteId));
135
153
  if (queries.length > 0 || onCompleteIds.length > 0)
136
154
  yield* _(query({ queries, onCompleteIds }));
137
- const [config, syncWorkerPostMessage] = yield* _(Effect.all([Config, SyncWorkerPostMessage]));
138
- syncWorkerPostMessage({
139
- _tag: "sync",
140
- syncUrl: config.syncUrl,
141
- messages,
142
- timestamp,
143
- merkleTree,
144
- owner,
145
- syncLoopCount: 0,
146
- });
147
155
  });
148
156
  const handleSyncResponse = ({ messages, ...response }) => Effect.gen(function* (_) {
149
157
  let { timestamp, merkleTree } = yield* _(readTimestampAndMerkleTree);
150
- if (ReadonlyArray.isNonEmptyReadonlyArray(messages)) {
158
+ if (messages.length > 0) {
151
159
  for (const message of messages)
152
160
  timestamp = yield* _(unsafeTimestampFromString(message.timestamp), (remote) => receiveTimestamp({ local: timestamp, remote }));
153
161
  merkleTree = yield* _(applyMessages({ merkleTree, messages }));
@@ -170,7 +178,7 @@ const handleSyncResponse = ({ messages, ...response }) => Effect.gen(function* (
170
178
  }
171
179
  const [sqlite, config, owner] = yield* _(Effect.all([Sqlite, Config, Owner]));
172
180
  const messagesToSync = yield* _(sqlite.exec({
173
- sql: selectMessagesToSync,
181
+ sql: Sql.selectMessagesToSync,
174
182
  parameters: [timestampToString(makeSyncTimestamp(diff.value))],
175
183
  }), Effect.map(({ rows }) => rows));
176
184
  syncWorkerPostMessage({
@@ -30,5 +30,5 @@ export declare const AppState: Context.Tag<AppState, AppState>;
30
30
  * To detect whether DOM can be used.
31
31
  * https://github.com/facebook/fbjs/blob/main/packages/fbjs/src/core/ExecutionEnvironment.js
32
32
  */
33
- export declare const canUseDOM: boolean;
33
+ export declare const canUseDom: boolean;
34
34
  //# sourceMappingURL=Platform.d.ts.map
@@ -19,6 +19,6 @@ export const AppState = Context.Tag("evolu/AppState");
19
19
  * To detect whether DOM can be used.
20
20
  * https://github.com/facebook/fbjs/blob/main/packages/fbjs/src/core/ExecutionEnvironment.js
21
21
  */
22
- export const canUseDOM = !!(typeof window !== "undefined" &&
22
+ export const canUseDom = !!(typeof window !== "undefined" &&
23
23
  window.document &&
24
24
  window.document.createElement);
package/dist/src/Sql.d.ts CHANGED
@@ -5,7 +5,8 @@ export declare const createOwnerTable = "\nCREATE TABLE\n \"evolu_owner\" (\n
5
5
  export declare const insertOwner = "\nINSERT INTO\n \"evolu_owner\" (\n \"id\",\n \"mnemonic\",\n \"encryptionKey\",\n \"timestamp\",\n \"merkleTree\"\n )\nVALUES\n (?, ?, ?, ?, ?);\n";
6
6
  export declare const selectOwnerTimestampAndMerkleTree = "\nSELECT\n \"timestamp\",\n \"merkleTree\"\nFROM\n \"evolu_owner\"\n";
7
7
  export declare const selectLastTimestampForTableRowColumn = "\nSELECT\n \"timestamp\"\nFROM\n \"evolu_message\"\nWHERE\n \"table\" = ?\n AND \"row\" = ?\n AND \"column\" = ?\nORDER BY\n \"timestamp\" DESC\nLIMIT\n 1\n";
8
- export declare const insertValueIntoTableRowColumn: (table: string, column: string) => string;
8
+ export declare const upsertValueIntoTableRowColumn: (table: string, column: string) => string;
9
+ export declare const deleteTableRow: (table: string) => string;
9
10
  export declare const insertIntoMessagesIfNew = "\nINSERT INTO\n \"evolu_message\" (\"timestamp\", \"table\", \"row\", \"column\", \"value\")\nVALUES\n (?, ?, ?, ?, ?)\nON CONFLICT DO NOTHING\n";
10
11
  export declare const updateOwnerTimestampAndMerkleTree = "\nUPDATE \"evolu_owner\"\nSET\n \"timestamp\" = ?,\n \"merkleTree\" = ?\n";
11
12
  export declare const selectMessagesToSync = "\nSELECT\n *\nFROM\n \"evolu_message\"\nWHERE\n \"timestamp\" >= ?\nORDER BY\n \"timestamp\"\n";
@@ -1 +1 @@
1
- {"version":3,"file":"Sql.d.ts","sourceRoot":"","sources":["../../src/Sql.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,yFAOvB,CAAC;AAEF,eAAO,MAAM,kBAAkB,yKAS9B,CAAC;AAEF,eAAO,MAAM,uBAAuB,yHAInC,CAAC;AAEF,eAAO,MAAM,gBAAgB,yKAS5B,CAAC;AAEF,eAAO,MAAM,WAAW,0KAWvB,CAAC;AAEF,eAAO,MAAM,iCAAiC,4EAM7C,CAAC;AAEF,eAAO,MAAM,oCAAoC,wKAahD,CAAC;AAEF,eAAO,MAAM,6BAA6B,UACjC,MAAM,UACL,MAAM,KACb,MAOF,CAAC;AAEF,eAAO,MAAM,uBAAuB,uJAMnC,CAAC;AAEF,eAAO,MAAM,iCAAiC,gFAK7C,CAAC;AAEF,eAAO,MAAM,oBAAoB,uGAShC,CAAC"}
1
+ {"version":3,"file":"Sql.d.ts","sourceRoot":"","sources":["../../src/Sql.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,yFAOvB,CAAC;AAEF,eAAO,MAAM,kBAAkB,yKAS9B,CAAC;AAEF,eAAO,MAAM,uBAAuB,yHAInC,CAAC;AAEF,eAAO,MAAM,gBAAgB,yKAS5B,CAAC;AAEF,eAAO,MAAM,WAAW,0KAWvB,CAAC;AAEF,eAAO,MAAM,iCAAiC,4EAM7C,CAAC;AAEF,eAAO,MAAM,oCAAoC,wKAahD,CAAC;AAEF,eAAO,MAAM,6BAA6B,UACjC,MAAM,UACL,MAAM,KACb,MAOF,CAAC;AAEF,eAAO,MAAM,cAAc,UAAW,MAAM,KAAG,MAI9C,CAAC;AAEF,eAAO,MAAM,uBAAuB,uJAMnC,CAAC;AAEF,eAAO,MAAM,iCAAiC,gFAK7C,CAAC;AAEF,eAAO,MAAM,oBAAoB,uGAShC,CAAC"}
package/dist/src/Sql.js CHANGED
@@ -67,7 +67,7 @@ ORDER BY
67
67
  LIMIT
68
68
  1
69
69
  `;
70
- export const insertValueIntoTableRowColumn = (table, column) => `
70
+ export const upsertValueIntoTableRowColumn = (table, column) => `
71
71
  INSERT INTO
72
72
  "${table}" ("id", "${column}")
73
73
  VALUES
@@ -75,6 +75,11 @@ VALUES
75
75
  ON CONFLICT DO UPDATE SET
76
76
  "${column}" = ?
77
77
  `;
78
+ export const deleteTableRow = (table) => `
79
+ DELETE FROM "${table}"
80
+ WHERE
81
+ "id" = ?;
82
+ `;
78
83
  export const insertIntoMessagesIfNew = `
79
84
  INSERT INTO
80
85
  "evolu_message" ("timestamp", "table", "row", "column", "value")
@@ -4,14 +4,11 @@ export * from "./Crdt.js";
4
4
  export * from "./Crypto.js";
5
5
  export * from "./Db.js";
6
6
  export * from "./DbWorker.js";
7
- export * from "./Diff.js";
8
7
  export * from "./Errors.js";
9
8
  export * from "./Evolu.js";
10
9
  export * from "./Model.js";
11
- export * from "./Murmurhash.js";
12
10
  export * from "./Platform.js";
13
11
  export * from "./Protobuf.js";
14
- export * from "./Sql.js";
15
12
  export * from "./Sqlite.js";
16
13
  export * from "./Store.js";
17
14
  export * from "./SyncWorker.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACtE,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACtE,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC"}
package/dist/src/index.js CHANGED
@@ -4,14 +4,11 @@ export * from "./Crdt.js";
4
4
  export * from "./Crypto.js";
5
5
  export * from "./Db.js";
6
6
  export * from "./DbWorker.js";
7
- export * from "./Diff.js";
8
7
  export * from "./Errors.js";
9
8
  export * from "./Evolu.js";
10
9
  export * from "./Model.js";
11
- export * from "./Murmurhash.js";
12
10
  export * from "./Platform.js";
13
11
  export * from "./Protobuf.js";
14
- export * from "./Sql.js";
15
12
  export * from "./Sqlite.js";
16
13
  export * from "./Store.js";
17
14
  export * from "./SyncWorker.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evolu/common",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Local-first platform designed for privacy, ease of use, and no vendor lock-in to sync and backup people's lifetime data",
5
5
  "keywords": [
6
6
  "evolu",
@@ -46,6 +46,7 @@
46
46
  "@protobuf-ts/plugin": "^2.9.1",
47
47
  "@protobuf-ts/protoc": "^2.9.1",
48
48
  "array-shuffle": "^3.0.0",
49
+ "better-sqlite3": "^9.0.0",
49
50
  "effect": "2.0.0-next.50",
50
51
  "eslint": "^8.52.0",
51
52
  "typescript": "^5.2.2",
package/src/Db.ts CHANGED
@@ -169,26 +169,26 @@ export interface NoSuchTableOrColumnError {
169
169
  readonly _tag: "NoSuchTableOrColumnError";
170
170
  }
171
171
 
172
- export const someDefectToNoSuchTableOrColumnError = Effect.catchSomeDefect(
173
- (error) => {
174
- if (
175
- typeof error === "object" &&
176
- error != null &&
177
- "message" in error &&
178
- typeof error.message === "string" &&
179
- error.message.includes("code 1") &&
180
- (error.message.includes("no such table") ||
181
- error.message.includes("no such column") ||
182
- error.message.includes("has no column"))
183
- )
184
- return Option.some(
185
- Effect.fail<NoSuchTableOrColumnError>({
186
- _tag: "NoSuchTableOrColumnError",
187
- }),
188
- );
172
+ export const SqliteNoSuchTableOrColumnError = S.struct({
173
+ message: S.union(
174
+ S.string.pipe(S.includes("no such table")),
175
+ S.string.pipe(S.includes("no such column")),
176
+ S.string.pipe(S.includes("has no column")),
177
+ ),
178
+ });
179
+ export type SqliteNoSuchTableOrColumnError = S.Schema.To<
180
+ typeof SqliteNoSuchTableOrColumnError
181
+ >;
189
182
 
190
- return Option.none();
191
- },
183
+ export const someDefectToNoSuchTableOrColumnError = Effect.catchSomeDefect(
184
+ (error) =>
185
+ S.is(SqliteNoSuchTableOrColumnError)(error)
186
+ ? Option.some(
187
+ Effect.fail<NoSuchTableOrColumnError>({
188
+ _tag: "NoSuchTableOrColumnError",
189
+ }),
190
+ )
191
+ : Option.none(),
192
192
  );
193
193
 
194
194
  export const makeOwner = (
package/src/DbWorker.ts CHANGED
@@ -45,15 +45,7 @@ import {
45
45
  import { QueryPatches, makePatches } from "./Diff.js";
46
46
  import { EvoluError, UnexpectedError, makeUnexpectedError } from "./Errors.js";
47
47
  import { Id, SqliteDate, cast } from "./Model.js";
48
- import {
49
- insertIntoMessagesIfNew,
50
- insertValueIntoTableRowColumn,
51
- selectLastTimestampForTableRowColumn,
52
- selectMessagesToSync,
53
- selectOwner,
54
- selectOwnerTimestampAndMerkleTree,
55
- updateOwnerTimestampAndMerkleTree,
56
- } from "./Sql.js";
48
+ import * as Sql from "./Sql.js";
57
49
  import { Query, Row, Sqlite, Value, queryObjectFromQuery } from "./Sqlite.js";
58
50
  import {
59
51
  Message,
@@ -179,7 +171,7 @@ const init = (
179
171
  const sqlite = yield* _(Sqlite);
180
172
 
181
173
  return yield* _(
182
- sqlite.exec(selectOwner),
174
+ sqlite.exec(Sql.selectOwner),
183
175
  Effect.map(
184
176
  ({ rows: [row] }): Owner => ({
185
177
  id: row.id as OwnerId,
@@ -237,7 +229,9 @@ interface TimestampAndMerkleTree {
237
229
  }
238
230
 
239
231
  const readTimestampAndMerkleTree = Sqlite.pipe(
240
- Effect.flatMap((sqlite) => sqlite.exec(selectOwnerTimestampAndMerkleTree)),
232
+ Effect.flatMap((sqlite) =>
233
+ sqlite.exec(Sql.selectOwnerTimestampAndMerkleTree),
234
+ ),
241
235
  Effect.map((result) => result.rows),
242
236
  Effect.map(
243
237
  ([{ timestamp, merkleTree }]): TimestampAndMerkleTree => ({
@@ -248,11 +242,11 @@ const readTimestampAndMerkleTree = Sqlite.pipe(
248
242
  );
249
243
 
250
244
  export const mutateItemsToNewMessages = (
251
- items: ReadonlyArray.NonEmptyReadonlyArray<MutateItem>,
252
- ): ReadonlyArray.NonEmptyReadonlyArray<NewMessage> =>
245
+ items: ReadonlyArray<MutateItem>,
246
+ ): ReadonlyArray<NewMessage> =>
253
247
  pipe(
254
248
  items,
255
- ReadonlyArray.mapNonEmpty(({ id, isInsert, now, table, values }) =>
249
+ ReadonlyArray.map(({ id, isInsert, now, table, values }) =>
256
250
  pipe(
257
251
  Object.entries(values),
258
252
  ReadonlyArray.filterMap(([key, value]) =>
@@ -274,7 +268,7 @@ export const mutateItemsToNewMessages = (
274
268
  ] as const,
275
269
  ),
276
270
  ReadonlyArray.append([isInsert ? "createdAt" : "updatedAt", now]),
277
- ReadonlyArray.mapNonEmpty(
271
+ ReadonlyArray.map(
278
272
  ([key, value]): NewMessage => ({
279
273
  table,
280
274
  row: id,
@@ -284,12 +278,12 @@ export const mutateItemsToNewMessages = (
284
278
  ),
285
279
  ),
286
280
  ),
287
- ReadonlyArray.flattenNonEmpty,
288
- ReadonlyArray.dedupeNonEmptyWith(NewMessageEquivalence),
281
+ ReadonlyArray.flatten,
282
+ ReadonlyArray.dedupeWith(NewMessageEquivalence),
289
283
  );
290
284
 
291
- const ensureSchemaByMessages = (
292
- messages: ReadonlyArray.NonEmptyReadonlyArray<Message>,
285
+ const ensureSchemaByNewMessages = (
286
+ messages: ReadonlyArray<NewMessage>,
293
287
  ): Effect.Effect<Sqlite, never, void> =>
294
288
  Effect.gen(function* (_) {
295
289
  const tablesMap = new Map<string, Table>();
@@ -311,12 +305,34 @@ const ensureSchemaByMessages = (
311
305
  yield* _(ensureSchema(Array.from(tablesMap.values())));
312
306
  });
313
307
 
308
+ export const upsertValueIntoTableRowColumn = (
309
+ message: NewMessage,
310
+ messages: ReadonlyArray<NewMessage>,
311
+ ): Effect.Effect<Sqlite, never, void> =>
312
+ Effect.gen(function* (_) {
313
+ const sqlite = yield* _(Sqlite);
314
+
315
+ const insert = sqlite.exec({
316
+ sql: Sql.upsertValueIntoTableRowColumn(message.table, message.column),
317
+ parameters: [message.row, message.value, message.value],
318
+ });
319
+
320
+ yield* _(
321
+ insert,
322
+ someDefectToNoSuchTableOrColumnError,
323
+ Effect.catchTag("NoSuchTableOrColumnError", () =>
324
+ // If one message fails, we ensure schema for all messages.
325
+ ensureSchemaByNewMessages(messages).pipe(Effect.zipRight(insert)),
326
+ ),
327
+ );
328
+ });
329
+
314
330
  const applyMessages = ({
315
331
  merkleTree,
316
332
  messages,
317
333
  }: {
318
334
  merkleTree: MerkleTree;
319
- messages: ReadonlyArray.NonEmptyReadonlyArray<Message>;
335
+ messages: ReadonlyArray<Message>;
320
336
  }): Effect.Effect<Sqlite, never, MerkleTree> =>
321
337
  Effect.gen(function* (_) {
322
338
  const sqlite = yield* _(Sqlite);
@@ -324,7 +340,7 @@ const applyMessages = ({
324
340
  for (const message of messages) {
325
341
  const timestamp: TimestampString | null = yield* _(
326
342
  sqlite.exec({
327
- sql: selectLastTimestampForTableRowColumn,
343
+ sql: Sql.selectLastTimestampForTableRowColumn,
328
344
  parameters: [message.table, message.row, message.column],
329
345
  }),
330
346
  Effect.map((result) => result.rows),
@@ -334,23 +350,13 @@ const applyMessages = ({
334
350
  );
335
351
 
336
352
  if (timestamp == null || timestamp < message.timestamp) {
337
- const insert = sqlite.exec({
338
- sql: insertValueIntoTableRowColumn(message.table, message.column),
339
- parameters: [message.row, message.value, message.value],
340
- });
341
- yield* _(
342
- insert,
343
- someDefectToNoSuchTableOrColumnError,
344
- Effect.catchTag("NoSuchTableOrColumnError", () =>
345
- ensureSchemaByMessages(messages).pipe(Effect.flatMap(() => insert)),
346
- ),
347
- );
353
+ yield* _(upsertValueIntoTableRowColumn(message, messages));
348
354
  }
349
355
 
350
356
  if (timestamp == null || timestamp !== message.timestamp) {
351
357
  const { changes } = yield* _(
352
358
  sqlite.exec({
353
- sql: insertIntoMessagesIfNew,
359
+ sql: Sql.insertIntoMessagesIfNew,
354
360
  parameters: [
355
361
  message.timestamp,
356
362
  message.table,
@@ -360,11 +366,10 @@ const applyMessages = ({
360
366
  ],
361
367
  }),
362
368
  );
363
-
364
- if (changes > 0)
365
- merkleTree = insertIntoMerkleTree(
366
- unsafeTimestampFromString(message.timestamp),
367
- )(merkleTree);
369
+ if (changes > 0) {
370
+ const timestamp = unsafeTimestampFromString(message.timestamp);
371
+ merkleTree = insertIntoMerkleTree(timestamp)(merkleTree);
372
+ }
368
373
  }
369
374
  }
370
375
 
@@ -377,7 +382,7 @@ const writeTimestampAndMerkleTree = ({
377
382
  }: TimestampAndMerkleTree): Effect.Effect<Sqlite, never, void> =>
378
383
  Effect.flatMap(Sqlite, (sqlite) =>
379
384
  sqlite.exec({
380
- sql: updateOwnerTimestampAndMerkleTree,
385
+ sql: Sql.updateOwnerTimestampAndMerkleTree,
381
386
  parameters: [
382
387
  timestampToString(timestamp),
383
388
  merkleTreeToString(merkleTree),
@@ -402,47 +407,63 @@ const mutate = ({
402
407
  void
403
408
  > =>
404
409
  Effect.gen(function* (_) {
405
- const owner = yield* _(Owner);
406
- let { timestamp, merkleTree } = yield* _(readTimestampAndMerkleTree);
410
+ const [toSync, localOnlyItems] = ReadonlyArray.partition(items, (item) =>
411
+ item.table.startsWith("_"),
412
+ );
413
+ const [toUpsert, toDelete] = ReadonlyArray.partition(
414
+ localOnlyItems,
415
+ (item) =>
416
+ mutateItemsToNewMessages([item]).some(
417
+ (message) => message.column === "isDeleted" && message.value === 1,
418
+ ),
419
+ ).map(mutateItemsToNewMessages);
407
420
 
408
- const messages = yield* _(
409
- mutateItemsToNewMessages(items),
410
- Effect.forEach((newMessage) =>
411
- Effect.map(sendTimestamp(timestamp), (nextTimestamp): Message => {
412
- timestamp = nextTimestamp;
413
- return {
414
- ...newMessage,
415
- timestamp: timestampToString(timestamp),
416
- };
417
- }),
421
+ yield* _(
422
+ Effect.forEach(toUpsert, (message) =>
423
+ upsertValueIntoTableRowColumn(message, toUpsert),
418
424
  ),
419
425
  );
420
426
 
421
- if (ReadonlyArray.isNonEmptyReadonlyArray(messages)) {
427
+ const { exec } = yield* _(Sqlite);
428
+ yield* _(
429
+ Effect.forEach(toDelete, ({ table, row }) =>
430
+ exec({ sql: Sql.deleteTableRow(table), parameters: [row] }),
431
+ ),
432
+ );
433
+
434
+ if (toSync.length > 0) {
435
+ let { timestamp, merkleTree } = yield* _(readTimestampAndMerkleTree);
436
+
437
+ const messages = yield* _(
438
+ mutateItemsToNewMessages(toSync),
439
+ Effect.forEach((message) =>
440
+ Effect.map(sendTimestamp(timestamp), (nextTimestamp): Message => {
441
+ timestamp = nextTimestamp;
442
+ return { ...message, timestamp: timestampToString(timestamp) };
443
+ }),
444
+ ),
445
+ );
446
+
422
447
  merkleTree = yield* _(applyMessages({ merkleTree, messages }));
448
+
423
449
  yield* _(writeTimestampAndMerkleTree({ timestamp, merkleTree }));
450
+
451
+ (yield* _(SyncWorkerPostMessage))({
452
+ _tag: "sync",
453
+ syncUrl: (yield* _(Config)).syncUrl,
454
+ messages,
455
+ timestamp,
456
+ merkleTree,
457
+ owner: yield* _(Owner),
458
+ syncLoopCount: 0,
459
+ });
424
460
  }
425
461
 
426
462
  const onCompleteIds = ReadonlyArray.filterMap(items, (item) =>
427
463
  Option.fromNullable(item.onCompleteId),
428
464
  );
429
-
430
465
  if (queries.length > 0 || onCompleteIds.length > 0)
431
466
  yield* _(query({ queries, onCompleteIds }));
432
-
433
- const [config, syncWorkerPostMessage] = yield* _(
434
- Effect.all([Config, SyncWorkerPostMessage]),
435
- );
436
-
437
- syncWorkerPostMessage({
438
- _tag: "sync",
439
- syncUrl: config.syncUrl,
440
- messages,
441
- timestamp,
442
- merkleTree,
443
- owner,
444
- syncLoopCount: 0,
445
- });
446
467
  });
447
468
 
448
469
  const handleSyncResponse = ({
@@ -456,7 +477,7 @@ const handleSyncResponse = ({
456
477
  Effect.gen(function* (_) {
457
478
  let { timestamp, merkleTree } = yield* _(readTimestampAndMerkleTree);
458
479
 
459
- if (ReadonlyArray.isNonEmptyReadonlyArray(messages)) {
480
+ if (messages.length > 0) {
460
481
  for (const message of messages)
461
482
  timestamp = yield* _(
462
483
  unsafeTimestampFromString(message.timestamp),
@@ -490,7 +511,7 @@ const handleSyncResponse = ({
490
511
  );
491
512
  const messagesToSync = yield* _(
492
513
  sqlite.exec({
493
- sql: selectMessagesToSync,
514
+ sql: Sql.selectMessagesToSync,
494
515
  parameters: [timestampToString(makeSyncTimestamp(diff.value))],
495
516
  }),
496
517
  Effect.map(({ rows }) => rows as unknown as ReadonlyArray<Message>),
package/src/Platform.ts CHANGED
@@ -66,7 +66,7 @@ export const AppState = Context.Tag<AppState>("evolu/AppState");
66
66
  * To detect whether DOM can be used.
67
67
  * https://github.com/facebook/fbjs/blob/main/packages/fbjs/src/core/ExecutionEnvironment.js
68
68
  */
69
- export const canUseDOM = !!(
69
+ export const canUseDom = !!(
70
70
  typeof window !== "undefined" &&
71
71
  window.document &&
72
72
  window.document.createElement
package/src/Sql.ts CHANGED
@@ -75,7 +75,7 @@ LIMIT
75
75
  1
76
76
  `;
77
77
 
78
- export const insertValueIntoTableRowColumn = (
78
+ export const upsertValueIntoTableRowColumn = (
79
79
  table: string,
80
80
  column: string,
81
81
  ): string => `
@@ -87,6 +87,12 @@ ON CONFLICT DO UPDATE SET
87
87
  "${column}" = ?
88
88
  `;
89
89
 
90
+ export const deleteTableRow = (table: string): string => `
91
+ DELETE FROM "${table}"
92
+ WHERE
93
+ "id" = ?;
94
+ `;
95
+
90
96
  export const insertIntoMessagesIfNew = `
91
97
  INSERT INTO
92
98
  "evolu_message" ("timestamp", "table", "row", "column", "value")
package/src/index.ts CHANGED
@@ -4,14 +4,11 @@ export * from "./Crdt.js";
4
4
  export * from "./Crypto.js";
5
5
  export * from "./Db.js";
6
6
  export * from "./DbWorker.js";
7
- export * from "./Diff.js";
8
7
  export * from "./Errors.js";
9
8
  export * from "./Evolu.js";
10
9
  export * from "./Model.js";
11
- export * from "./Murmurhash.js";
12
10
  export * from "./Platform.js";
13
11
  export * from "./Protobuf.js";
14
- export * from "./Sql.js";
15
12
  export * from "./Sqlite.js";
16
13
  export * from "./Store.js";
17
14
  export * from "./SyncWorker.js";