@evolu/common 1.0.13 → 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 +6 -0
- package/dist/src/Db.d.ts.map +1 -1
- package/dist/src/Db.js +8 -13
- package/dist/src/DbWorker.d.ts +3 -2
- package/dist/src/DbWorker.d.ts.map +1 -1
- package/dist/src/DbWorker.js +47 -39
- package/dist/src/Sql.d.ts +2 -1
- package/dist/src/Sql.d.ts.map +1 -1
- package/dist/src/Sql.js +6 -1
- package/dist/src/index.d.ts +0 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +0 -3
- package/package.json +2 -1
- package/src/Db.ts +19 -19
- package/src/DbWorker.ts +90 -69
- package/src/Sql.ts +7 -1
- package/src/index.ts +0 -3
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>;
|
package/dist/src/Db.d.ts.map
CHANGED
|
@@ -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":"AACA,OAAO,KAAK,CAAC,MAAM,uBAAuB,CAAC;AAG3C,OAAO,EACL,KAAK,EACL,OAAO,EACP,MAAM,EAKN,cAAc,EAGf,MAAM,QAAQ,CAAC;AAChB,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAQjC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAgB,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAO3D,OAAO,EACL,KAAK,EAEL,GAAG,EACH,MAAM,EACN,KAAK,EAEN,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,WAAW,GAAG,cAAc,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAC/D,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG,cAAc,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;AAEhE,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,IAAI,CAC1C,aAAa,EAAE,aAAa,CAAC,CAAC,EAAE,GAAG,CAAC,KACjC,KAAK,CAAC;AAEX,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,IAAI,CACtD,EAAE,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,KAEzC,MAAM,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;AAEnD,KAAK,qBAAqB,CAAC,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC,CAAC;AAE9E,KAAK,cAAc,CAAC,CAAC,SAAS,MAAM,IAAI;IACtC,QAAQ,EAAE,KAAK,IAAI,MAAM,CAAC,GAAG,kBAAkB,CAC7C;QACE,QAAQ,EAAE,MAAM,IAAI,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;KACtD,GAAG,aAAa,CAClB;CACF,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI;IAClC,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;CAC7D,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;CACnC;AAED,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACzC;AAED,MAAM,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;AAe1C,eAAO,MAAM,eAAe,wCAGmD,CAAC;AAkBhF,eAAO,MAAM,cAAc,WAEjB,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,WAazB,CAAC;AAEJ;;;;GAIG;AACH,MAAM,WAAW,KAAK;IACpB,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IAErB,QAAQ,CAAC,aAAa,EAAE,UAAU,CAAC;CACpC;AAED,eAAO,MAAM,KAAK,2BAAoC,CAAC;AAEvD;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAEhD,eAAO,MAAM,WAAW,8EAUrB,CAAC;AAEJ,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAC;CAC3C;AAED,eAAO,MAAM,8BAA8B;;;;EAMzC,CAAC;AACH,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CACtD,OAAO,8BAA8B,CACtC,CAAC;AAEF,eAAO,MAAM,oCAAoC,8FAShD,CAAC;AAEF,eAAO,MAAM,SAAS,cACT,QAAQ,KAClB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CA0BhC,CAAC;AAEL,eAAO,MAAM,QAAQ,cACR,QAAQ,KAClB,aAAa,CAAC,MAAM,GAAG,KAAK,GAAG,MAAM,EAAE,KAAK,EAAE,KAAK,CA4BlD,CAAC;AA6DL,eAAO,MAAM,YAAY,sBAEtB,aAAa,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAUjC,CAAC"}
|
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
|
|
45
|
-
|
|
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)
|
package/dist/src/DbWorker.d.ts
CHANGED
|
@@ -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:
|
|
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":"AACA,OAAO,EACL,KAAK,EACL,OAAO,
|
|
1
|
+
{"version":3,"file":"DbWorker.d.ts","sourceRoot":"","sources":["../../src/DbWorker.ts"],"names":[],"mappings":"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"}
|
package/dist/src/DbWorker.js
CHANGED
|
@@ -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
|
|
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.
|
|
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.
|
|
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.
|
|
62
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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 (
|
|
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({
|
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
|
|
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";
|
package/dist/src/Sql.d.ts.map
CHANGED
|
@@ -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
|
|
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")
|
package/dist/src/index.d.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";
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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.
|
|
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
|
|
173
|
-
(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
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) =>
|
|
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
|
|
252
|
-
): ReadonlyArray
|
|
245
|
+
items: ReadonlyArray<MutateItem>,
|
|
246
|
+
): ReadonlyArray<NewMessage> =>
|
|
253
247
|
pipe(
|
|
254
248
|
items,
|
|
255
|
-
ReadonlyArray.
|
|
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.
|
|
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.
|
|
288
|
-
ReadonlyArray.
|
|
281
|
+
ReadonlyArray.flatten,
|
|
282
|
+
ReadonlyArray.dedupeWith(NewMessageEquivalence),
|
|
289
283
|
);
|
|
290
284
|
|
|
291
|
-
const
|
|
292
|
-
messages: ReadonlyArray
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
365
|
-
merkleTree = insertIntoMerkleTree(
|
|
366
|
-
|
|
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
|
|
406
|
-
|
|
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
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
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 (
|
|
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/Sql.ts
CHANGED
|
@@ -75,7 +75,7 @@ LIMIT
|
|
|
75
75
|
1
|
|
76
76
|
`;
|
|
77
77
|
|
|
78
|
-
export const
|
|
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";
|