@evolu/common 4.1.1 → 5.0.0

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 (78) hide show
  1. package/README.md +1 -1
  2. package/dist/src/Config.d.ts +27 -30
  3. package/dist/src/Config.d.ts.map +1 -1
  4. package/dist/src/Config.js +32 -5
  5. package/dist/src/Crdt.d.ts +5 -4
  6. package/dist/src/Crdt.d.ts.map +1 -1
  7. package/dist/src/Crdt.js +27 -24
  8. package/dist/src/Crypto.d.ts +16 -10
  9. package/dist/src/Crypto.d.ts.map +1 -1
  10. package/dist/src/Crypto.js +24 -11
  11. package/dist/src/Db.d.ts +58 -78
  12. package/dist/src/Db.d.ts.map +1 -1
  13. package/dist/src/Db.js +368 -170
  14. package/dist/src/Diff.d.ts +9 -3
  15. package/dist/src/Diff.d.ts.map +1 -1
  16. package/dist/src/Diff.js +12 -11
  17. package/dist/src/Error.d.ts +15 -0
  18. package/dist/src/Error.d.ts.map +1 -0
  19. package/dist/src/Error.js +14 -0
  20. package/dist/src/Evolu.d.ts +139 -91
  21. package/dist/src/Evolu.d.ts.map +1 -1
  22. package/dist/src/Evolu.js +267 -239
  23. package/dist/src/Model.d.ts +52 -0
  24. package/dist/src/Model.d.ts.map +1 -1
  25. package/dist/src/Model.js +47 -5
  26. package/dist/src/Owner.d.ts.map +1 -1
  27. package/dist/src/Owner.js +14 -8
  28. package/dist/src/Platform.d.ts +24 -34
  29. package/dist/src/Platform.d.ts.map +1 -1
  30. package/dist/src/Platform.js +9 -31
  31. package/dist/src/Public.d.ts +3 -5
  32. package/dist/src/Public.d.ts.map +1 -1
  33. package/dist/src/Public.js +2 -3
  34. package/dist/src/Sqlite.d.ts +25 -16
  35. package/dist/src/Sqlite.d.ts.map +1 -1
  36. package/dist/src/Sqlite.js +28 -20
  37. package/dist/src/Store.d.ts.map +1 -1
  38. package/dist/src/Sync.d.ts +70 -0
  39. package/dist/src/Sync.d.ts.map +1 -0
  40. package/dist/src/Sync.js +126 -0
  41. package/dist/src/index.d.ts +2 -5
  42. package/dist/src/index.d.ts.map +1 -1
  43. package/dist/src/index.js +2 -5
  44. package/package.json +12 -10
  45. package/src/Config.ts +77 -36
  46. package/src/Crdt.ts +61 -76
  47. package/src/Crypto.ts +83 -60
  48. package/src/Db.ts +782 -352
  49. package/src/Diff.ts +23 -21
  50. package/src/Error.ts +29 -0
  51. package/src/Evolu.ts +599 -499
  52. package/src/Model.ts +102 -6
  53. package/src/Owner.ts +24 -24
  54. package/src/Platform.ts +33 -81
  55. package/src/Public.ts +3 -5
  56. package/src/Sqlite.ts +81 -44
  57. package/src/Sync.ts +306 -0
  58. package/src/index.ts +2 -5
  59. package/dist/src/DbWorker.d.ts +0 -82
  60. package/dist/src/DbWorker.d.ts.map +0 -1
  61. package/dist/src/DbWorker.js +0 -335
  62. package/dist/src/ErrorStore.d.ts +0 -29
  63. package/dist/src/ErrorStore.d.ts.map +0 -1
  64. package/dist/src/ErrorStore.js +0 -13
  65. package/dist/src/OnCompletes.d.ts +0 -14
  66. package/dist/src/OnCompletes.d.ts.map +0 -1
  67. package/dist/src/OnCompletes.js +0 -25
  68. package/dist/src/SyncWorker.d.ts +0 -80
  69. package/dist/src/SyncWorker.d.ts.map +0 -1
  70. package/dist/src/SyncWorker.js +0 -150
  71. package/dist/src/Types.d.ts +0 -13
  72. package/dist/src/Types.d.ts.map +0 -1
  73. package/dist/src/Types.js +0 -1
  74. package/src/DbWorker.ts +0 -721
  75. package/src/ErrorStore.ts +0 -46
  76. package/src/OnCompletes.ts +0 -51
  77. package/src/SyncWorker.ts +0 -361
  78. package/src/Types.ts +0 -11
@@ -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,150 +1,121 @@
1
- import * as AST from "@effect/schema/AST";
2
1
  import * as S from "@effect/schema/Schema";
3
- import { make } from "@effect/schema/Schema";
2
+ import * as Arr from "effect/Array";
3
+ import * as Console from "effect/Console";
4
4
  import * as Context from "effect/Context";
5
+ import * as Deferred from "effect/Deferred";
5
6
  import * as Effect from "effect/Effect";
6
- import * as Exit from "effect/Exit";
7
- import { pipe } from "effect/Function";
8
- import * as Layer from "effect/Layer";
7
+ import { constVoid, pipe } from "effect/Function";
9
8
  import * as Option from "effect/Option";
10
9
  import * as Predicate from "effect/Predicate";
11
- import * as ReadonlyArray from "effect/ReadonlyArray";
12
- import * as ReadonlyRecord from "effect/ReadonlyRecord";
10
+ import * as Ref from "effect/Ref";
11
+ import * as Schedule from "effect/Schedule";
13
12
  import * as String from "effect/String";
14
- import { initialMerkleTree, makeInitialTimestamp, merkleTreeToString, timestampToString, } from "./Crdt.js";
15
- import { SqliteBoolean, SqliteDate } from "./Model.js";
16
- import { makeOwner } from "./Owner.js";
17
- import { createMessageTable, createMessageTableIndex, createOwnerTable, insertOwner, } from "./Sql.js";
18
- import { indexEquivalence, Sqlite, isJsonObjectOrArray, } from "./Sqlite.js";
19
- import { makeStore } from "./Store.js";
20
- /**
21
- * Create table schema.
22
- *
23
- * Supported types are null, string, number, Uint8Array, JSON Object, and JSON
24
- * Array. Use SqliteDate for dates and SqliteBoolean for booleans.
25
- *
26
- * Reserved columns are createdAt, updatedAt, isDeleted. Those columns are added
27
- * by default.
28
- *
29
- * @example
30
- * const TodoId = id("Todo");
31
- * type TodoId = S.Schema.Type<typeof TodoId>;
32
- *
33
- * const TodoTable = table({
34
- * id: TodoId,
35
- * title: NonEmptyString1000,
36
- * isCompleted: S.nullable(SqliteBoolean),
37
- * });
38
- * type TodoTable = S.Schema.Type<typeof TodoTable>;
39
- */
40
- export const table = (fields) => S.struct(fields).pipe(S.extend(ReservedColumns));
41
- const ReservedColumns = S.struct({
42
- createdAt: SqliteDate,
43
- updatedAt: SqliteDate,
44
- isDeleted: SqliteBoolean,
13
+ import * as SynchronizedRef from "effect/SynchronizedRef";
14
+ import { Time, diffMerkleTrees, initialMerkleTree, insertIntoMerkleTree, makeInitialTimestamp, makeSyncTimestamp, merkleTreeToString, receiveTimestamp, sendTimestamp, timestampToString, unsafeTimestampFromString, } from "./Crdt.js";
15
+ import { Bip39, NanoIdGenerator } from "./Crypto.js";
16
+ import { makePatches } from "./Diff.js";
17
+ import { ensureTransferableError, makeUnexpectedError, } from "./Error.js";
18
+ import { cast } from "./Model.js";
19
+ import { Owner, makeOwner } from "./Owner.js";
20
+ import { SyncLock } from "./Platform.js";
21
+ import * as Sql from "./Sql.js";
22
+ import { Sqlite, SqliteFactory, drawSqliteQueryPlan, isJsonObjectOrArray, } from "./Sqlite.js";
23
+ import { Sync, SyncFactory, } from "./Sync.js";
24
+ export const Index = S.Struct({
25
+ name: S.String,
26
+ sql: S.String,
45
27
  });
46
- /**
47
- * Create database schema.
48
- *
49
- * Tables with a name prefixed with _ are local-only, which means they are not
50
- * synced. Local-only tables are useful for device-specific or temporal data.
51
- *
52
- * @example
53
- * const Database = database({
54
- * // A local-only table.
55
- * _todo: TodoTable,
56
- * todo: TodoTable,
57
- * todoCategory: TodoCategoryTable,
58
- * });
59
- * type Database = S.Schema.Type<typeof Database>;
60
- */
61
- export const database = S.struct;
62
- // We use queries as keys, hence JSON.stringify.
63
- export const serializeQuery = ({ sql, parameters = [], options, }) => {
64
- const query = {
65
- sql,
66
- parameters: parameters.map((p) => Predicate.isUint8Array(p)
67
- ? Array.from(p)
68
- : isJsonObjectOrArray(p)
69
- ? { json: p }
70
- : p),
71
- ...(options && { options }),
72
- };
73
- return JSON.stringify(query);
74
- };
75
- export const deserializeQuery = (query) => {
76
- const serializedSqliteQuery = JSON.parse(query);
77
- return {
78
- ...serializedSqliteQuery,
79
- parameters: serializedSqliteQuery.parameters.map((p) => Array.isArray(p)
80
- ? new Uint8Array(p)
81
- : typeof p === "object" && p != null
82
- ? p.json
83
- : p),
28
+ export class DbFactory extends Context.Tag("DbFactory")() {
29
+ }
30
+ export const createDb = Effect.gen(function* () {
31
+ const { createSqlite } = yield* SqliteFactory;
32
+ const { createSync } = yield* SyncFactory;
33
+ const initContext = Context.empty().pipe(Context.add(Bip39, yield* Bip39), Context.add(NanoIdGenerator, yield* NanoIdGenerator), Context.add(Time, yield* Time), Context.add(SyncLock, yield* SyncLock));
34
+ const afterInitContext = yield* Deferred.make();
35
+ const afterInit = (options) => (effect) => Effect.flatMap(Deferred.await(afterInitContext), (context) => Effect.flatMap(Sqlite, (sqlite) => sqlite.transaction(options.transaction)(effect)).pipe(Effect.provide(context)));
36
+ const queryRowsRef = yield* SynchronizedRef.make(new Map());
37
+ const db = {
38
+ init: (schema, initialData, onError, onSyncStateChange, onReceive) => Effect.gen(function* () {
39
+ yield* Effect.logDebug(["Db init", { schema }]);
40
+ const sqlite = yield* createSqlite;
41
+ const contextWithSqlite = Context.add(initContext, Sqlite, sqlite);
42
+ const owner = yield* getSchema.pipe(Effect.tap(ensureSchema(schema)), Effect.flatMap((currentSchema) => {
43
+ if (currentSchema.tables.map((t) => t.name).includes("evolu_owner"))
44
+ return readOwner;
45
+ return createOwner().pipe(Effect.tap(applyMutations(initialData)));
46
+ }), sqlite.transaction("exclusive"), Effect.provide(contextWithSqlite));
47
+ const sync = yield* createSync.pipe(Effect.provide(initContext));
48
+ yield* sync.init(owner);
49
+ Deferred.unsafeDone(afterInitContext, Effect.succeed(contextWithSqlite.pipe(Context.add(Owner, owner), Context.add(Sync, sync), Context.add(Callbacks, { onError, onSyncStateChange, onReceive }))));
50
+ return owner;
51
+ }),
52
+ loadQueries: (queries) => Effect.logDebug(["Db loadQueries", { queries }]).pipe(Effect.zipRight(loadQueries(queries, queryRowsRef)), afterInit({ transaction: "shared" })),
53
+ mutate: (mutations, queriesToRefresh) => Effect.gen(function* () {
54
+ yield* Effect.logDebug(["Db mutate", { mutations, queriesToRefresh }]);
55
+ const time = yield* Time;
56
+ const sqlite = yield* Sqlite;
57
+ const [toSyncMutations, localOnlyMutations] = Arr.partition(mutations,
58
+ /** Table name starting with '_' is local only (not synced). */
59
+ (mutation) => mutation.table.startsWith("_"));
60
+ for (const mutation of localOnlyMutations) {
61
+ const isDeleteMutation = mutationToNewMessages(mutation).some(({ column, value }) => column === "isDeleted" && value === 1);
62
+ if (isDeleteMutation) {
63
+ yield* sqlite.exec({
64
+ sql: `delete from "${mutation.table}" where "id" = ?;`,
65
+ parameters: [mutation.id],
66
+ });
67
+ }
68
+ else {
69
+ const messages = mutationToNewMessages(mutation);
70
+ for (const message of messages) {
71
+ const now = yield* time.now;
72
+ yield* upsertValueIntoTableRowColumn(message, messages, now);
73
+ }
74
+ }
75
+ }
76
+ if (toSyncMutations.length > 0) {
77
+ yield* Effect.tap(applyMutations(toSyncMutations), forkSync);
78
+ }
79
+ return yield* loadQueries(queriesToRefresh, queryRowsRef);
80
+ }).pipe(afterInit({ transaction: "exclusive" })),
81
+ resetOwner: () => Effect.logTrace("Db resetOwner").pipe(Effect.tap(dropAllTables), afterInit({ transaction: "last" })),
82
+ restoreOwner: (schema, mnemonic) => Effect.logTrace("Db restoreOwner").pipe(Effect.tap(dropAllTables), Effect.tap(Effect.flatMap(getSchema, ensureSchema(schema))), Effect.tap(createOwner(mnemonic)), afterInit({ transaction: "last" })),
83
+ ensureSchema: (schema) => getSchema.pipe(Effect.flatMap(ensureSchema(schema)), afterInit({ transaction: "exclusive" })),
84
+ sync: (queriesToRefresh) => Effect.logDebug(["Db sync", { queriesToRefresh }]).pipe(Effect.zipRight(forkSync()), Effect.zipRight(loadQueries(queriesToRefresh, queryRowsRef)), afterInit({ transaction: "shared" })),
85
+ // TODO:
86
+ // dispose: () =>
87
+ // Effect.logTrace("Db dispose").pipe(
88
+ // Effect.tap(Scope.close(scope, Exit.succeed("Db disposed"))),
89
+ // afterInit({ transaction: "last" }),
90
+ // ),
84
91
  };
85
- };
86
- const _emptyRows = [];
87
- export const emptyRows = () => _emptyRows;
88
- const queryResultCache = new WeakMap();
89
- export const queryResultFromRows = (rows) => {
90
- let queryResult = queryResultCache.get(rows);
91
- if (queryResult == null) {
92
- queryResult = { rows, row: rows[0] };
93
- queryResultCache.set(rows, queryResult);
94
- }
95
- return queryResult;
96
- };
97
- // TODO: https://discord.com/channels/795981131316985866/1218626687546294386/1218796529725476935
98
- // https://github.com/Effect-TS/schema/releases/tag/v0.18.0
99
- const getPropertySignatures = (schema) => {
100
- const out = {};
101
- const propertySignatures = AST.getPropertySignatures(schema.ast);
102
- for (let i = 0; i < propertySignatures.length; i++) {
103
- const propertySignature = propertySignatures[i];
104
- out[propertySignature.name] = make(propertySignature.type);
105
- }
106
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
107
- return out;
108
- };
109
- export const schemaToTables = (schema) => pipe(getPropertySignatures(schema), ReadonlyRecord.toEntries, ReadonlyArray.map(([name, schema]) => ({
110
- name,
111
- columns: Object.keys(getPropertySignatures(schema)),
112
- })));
113
- export const transaction = (effect) => Effect.flatMap(Sqlite, (sqlite) => Effect.acquireUseRelease(sqlite.exec({ sql: "begin" }), () => effect, (_, exit) => Exit.isFailure(exit)
114
- ? sqlite.exec({ sql: "rollback" })
115
- : sqlite.exec({ sql: "end" })));
116
- export const SqliteNoSuchTableOrColumnError = S.struct({
117
- 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"))),
92
+ return db;
118
93
  });
119
- export const someDefectToNoSuchTableOrColumnError = Effect.catchSomeDefect((error) => S.is(SqliteNoSuchTableOrColumnError)(error)
120
- ? Option.some(Effect.fail({
121
- _tag: "NoSuchTableOrColumnError",
122
- }))
123
- : Option.none());
124
- export const lazyInit = (mnemonic) => Effect.gen(function* (_) {
125
- const [owner, sqlite, initialTimestampString] = yield* _(Effect.all([makeOwner(mnemonic), Sqlite, makeInitialTimestamp], {
126
- concurrency: "unbounded",
94
+ export const Callbacks = Context.GenericTag("Callbacks");
95
+ const loadQueries = (queries, queryRowsRef) => Effect.gen(function* () {
96
+ const sqlite = yield* Sqlite;
97
+ const previousState = yield* SynchronizedRef.getAndUpdateEffect(queryRowsRef, (previousState) => Effect.map(Effect.forEach(queries, (query) => {
98
+ const sqliteQuery = deserializeQuery(query);
99
+ return sqlite.exec(sqliteQuery).pipe(Effect.tap(maybeExplainQueryPlan(sqliteQuery)), Effect.map(({ rows }) => [query, rows]));
100
+ }), (queriesRows) => new Map([...previousState, ...queriesRows])));
101
+ const currentState = yield* SynchronizedRef.get(queryRowsRef);
102
+ return queries.map((query) => ({
103
+ query,
104
+ patches: makePatches(previousState.get(query), currentState.get(query) || []),
127
105
  }));
128
- yield* _(Effect.all([
129
- sqlite.exec(createMessageTable),
130
- sqlite.exec(createMessageTableIndex),
131
- sqlite.exec(createOwnerTable),
132
- sqlite.exec({
133
- ...insertOwner,
134
- parameters: [
135
- owner.id,
136
- owner.mnemonic,
137
- owner.encryptionKey,
138
- timestampToString(initialTimestampString),
139
- merkleTreeToString(initialMerkleTree),
140
- ],
141
- }),
142
- ]));
143
- return owner;
144
106
  });
107
+ const maybeExplainQueryPlan = (sqliteQuery) => {
108
+ if (!sqliteQuery.options?.logExplainQueryPlan)
109
+ return Effect.void;
110
+ return Sqlite.pipe(Effect.flatMap((sqlite) => sqlite.exec({
111
+ ...sqliteQuery,
112
+ sql: `EXPLAIN QUERY PLAN ${sqliteQuery.sql}`,
113
+ })), Effect.tap(Console.log("ExplainQueryPlan", sqliteQuery)), Effect.tap(({ rows }) => Console.log(drawSqliteQueryPlan(rows))), Effect.map(constVoid));
114
+ };
145
115
  const getSchema = Effect.gen(function* (_) {
146
- const sqlite = yield* _(Sqlite);
147
- const tables = yield* _(sqlite.exec({
116
+ yield* Effect.logTrace("Db getSchema");
117
+ const sqlite = yield* Sqlite;
118
+ const tables = yield* Effect.map(sqlite.exec({
148
119
  // https://til.simonwillison.net/sqlite/list-all-columns-in-a-database
149
120
  sql: `
150
121
  select
@@ -153,8 +124,8 @@ select
153
124
  from
154
125
  sqlite_master
155
126
  join pragma_table_info(sqlite_master.name) as table_info
156
- `.trim(),
157
- }), Effect.map(({ rows }) => {
127
+ `.trim(),
128
+ }), ({ rows }) => {
158
129
  const map = new Map();
159
130
  rows.forEach((row) => {
160
131
  const { tableName, columnName } = row;
@@ -162,12 +133,12 @@ from
162
133
  map.set(tableName, []);
163
134
  map.get(tableName)?.push(columnName);
164
135
  });
165
- return Array.from(map, ([name, columns]) => ({
136
+ return globalThis.Array.from(map, ([name, columns]) => ({
166
137
  name,
167
138
  columns,
168
139
  }));
169
- }));
170
- const indexes = yield* _(sqlite.exec({
140
+ });
141
+ const indexes = yield* Effect.map(sqlite.exec({
171
142
  sql: `
172
143
  select
173
144
  name, sql
@@ -178,7 +149,7 @@ where
178
149
  name not like 'sqlite_%' and
179
150
  name not like 'index_evolu_%'
180
151
  `.trim(),
181
- }), Effect.map((result) => ReadonlyArray.map(result.rows, (row) => ({
152
+ }), (result) => Arr.map(result.rows, (row) => ({
182
153
  name: row.name,
183
154
  /**
184
155
  * SQLite returns "CREATE INDEX" for "create index" for some reason.
@@ -186,58 +157,285 @@ where
186
157
  * for `indexEquivalence` manually.
187
158
  */
188
159
  sql: row.sql.replace("CREATE INDEX", "create index"),
189
- }))));
160
+ })));
190
161
  return { tables, indexes };
191
162
  });
192
- export const ensureSchema = (schema) => Effect.gen(function* (_) {
193
- const sqlite = yield* _(Sqlite);
194
- const currentSchema = yield* _(getSchema);
163
+ const ensureSchema = (newSchema) => (currentSchema) => Effect.gen(function* () {
164
+ yield* Effect.logTrace("Db ensureSchema");
195
165
  const sql = [];
196
- schema.tables.forEach((table) => {
166
+ newSchema.tables.forEach((table) => {
197
167
  const currentTable = currentSchema.tables.find((t) => t.name === table.name);
198
168
  if (!currentTable) {
199
169
  sql.push(`
200
- create table ${table.name} (
201
- "id" text primary key,
202
- ${table.columns
170
+ create table ${table.name} (
171
+ "id" text primary key,
172
+ ${table.columns
203
173
  .filter((c) => c !== "id")
204
174
  // "A column with affinity BLOB does not prefer one storage class over another
205
175
  // and no attempt is made to coerce data from one storage class into another."
206
176
  // https://www.sqlite.org/datatype3.html
207
177
  .map((name) => `"${name}" blob`)
208
178
  .join(", ")}
209
- );`.trim());
179
+ );`.trim());
210
180
  }
211
181
  else {
212
- ReadonlyArray.differenceWith(String.Equivalence)(table.columns, currentTable.columns).forEach((newColumn) => {
182
+ Arr.differenceWith(String.Equivalence)(table.columns, currentTable.columns).forEach((newColumn) => {
213
183
  sql.push(`alter table "${table.name}" add column "${newColumn}" blob;`);
214
184
  });
215
185
  }
216
186
  });
217
187
  // Remove old indexes.
218
- ReadonlyArray.differenceWith(indexEquivalence)(currentSchema.indexes, ReadonlyArray.intersectionWith(indexEquivalence)(currentSchema.indexes, schema.indexes)).forEach((indexToDrop) => {
188
+ Arr.differenceWith(indexEquivalence)(currentSchema.indexes, Arr.intersectionWith(indexEquivalence)(currentSchema.indexes, newSchema.indexes)).forEach((indexToDrop) => {
219
189
  sql.push(`drop index "${indexToDrop.name}";`);
220
190
  });
221
191
  // Add new indexes.
222
- ReadonlyArray.differenceWith(indexEquivalence)(schema.indexes, currentSchema.indexes).forEach((newIndex) => {
192
+ Arr.differenceWith(indexEquivalence)(newSchema.indexes, currentSchema.indexes).forEach((newIndex) => {
223
193
  sql.push(`${newIndex.sql};`);
224
194
  });
225
- if (sql.length > 0)
226
- yield* _(sqlite.exec({
227
- sql: sql.join("\n"),
228
- }));
195
+ if (sql.length > 0) {
196
+ const sqlite = yield* Sqlite;
197
+ yield* sqlite.exec({ sql: sql.join("\n") });
198
+ }
229
199
  });
230
- export const dropAllTables = Effect.gen(function* (_) {
231
- const sqlite = yield* _(Sqlite);
232
- const schema = yield* _(getSchema);
233
- const sql = schema.tables
234
- // The dropped table is completely removed from the database schema and
235
- // the disk file. The table can not be recovered.
236
- // All indices and triggers associated with the table are also deleted.
237
- // https://sqlite.org/lang_droptable.html
238
- .map((table) => `drop table "${table.name}";`)
239
- .join("");
240
- yield* _(sqlite.exec({ sql }));
200
+ const indexEquivalence = (self, that) => self.name === that.name && self.sql === that.sql;
201
+ const readOwner = Effect.logTrace("Db readOwner").pipe(Effect.zipRight(Sqlite), Effect.flatMap((sqlite) => sqlite.exec(Sql.selectOwner)), Effect.map(({ rows: [row] }) => ({
202
+ id: row.id,
203
+ mnemonic: row.mnemonic,
204
+ encryptionKey: row.encryptionKey,
205
+ })));
206
+ const createOwner = (mnemonic) => Effect.logTrace("Db createOwner").pipe(Effect.zipRight(Effect.all([makeOwner(mnemonic), Sqlite, makeInitialTimestamp])), Effect.tap(([owner, sqlite, initialTimestampString]) => Effect.all([
207
+ sqlite.exec(Sql.createMessageTable),
208
+ sqlite.exec(Sql.createMessageTableIndex),
209
+ sqlite.exec(Sql.createOwnerTable),
210
+ sqlite.exec({
211
+ ...Sql.insertOwner,
212
+ parameters: [
213
+ owner.id,
214
+ owner.mnemonic,
215
+ owner.encryptionKey,
216
+ timestampToString(initialTimestampString),
217
+ merkleTreeToString(initialMerkleTree),
218
+ ],
219
+ }),
220
+ ])), Effect.map(([owner]) => owner));
221
+ const applyMutations = (mutations) => Effect.gen(function* (_) {
222
+ const { timestamp, merkleTree } = yield* readTimestampAndMerkleTree;
223
+ const [nextTimestamp, messages] = yield* Effect.mapAccum(mutations.flatMap(mutationToNewMessages), timestamp, (currentTimestamp, newMessage) => Effect.map(sendTimestamp(currentTimestamp), (nextTimestamp) => {
224
+ const message = {
225
+ ...newMessage,
226
+ timestamp: timestampToString(nextTimestamp),
227
+ };
228
+ return [nextTimestamp, message];
229
+ }));
230
+ const nextMerkleTree = yield* applyMessages(merkleTree, messages);
231
+ yield* writeTimestampAndMerkleTree(nextTimestamp, nextMerkleTree);
232
+ return messages;
241
233
  });
242
- export const RowsStore = Context.GenericTag("@services/RowsStore");
243
- export const RowsStoreLive = Layer.effect(RowsStore, makeStore(new Map()));
234
+ const readTimestampAndMerkleTree = Sqlite.pipe(Effect.flatMap((sqlite) => sqlite.exec(Sql.selectOwnerTimestampAndMerkleTree)), Effect.map(({ rows: [{ timestamp, merkleTree }] }) => ({
235
+ timestamp: unsafeTimestampFromString(timestamp),
236
+ merkleTree: merkleTree,
237
+ })));
238
+ const mutationToNewMessages = (mutation) => pipe(Object.entries(mutation.values), Arr.filterMap(([column, value]) =>
239
+ // The value can be undefined if exactOptionalPropertyTypes isn't true.
240
+ // Don't insert nulls because null is the default value.
241
+ value === undefined || (mutation.isInsert && value == null)
242
+ ? Option.none()
243
+ : Option.some([column, value])), Arr.map(([column, value]) => ({
244
+ table: mutation.table,
245
+ row: mutation.id,
246
+ column,
247
+ value: typeof value === "boolean"
248
+ ? cast(value)
249
+ : value instanceof Date
250
+ ? cast(value)
251
+ : value,
252
+ })));
253
+ const applyMessages = (merkleTree, messages) => Effect.gen(function* () {
254
+ const sqlite = yield* Sqlite;
255
+ for (const message of messages) {
256
+ const messageTimestamp = unsafeTimestampFromString(message.timestamp);
257
+ const lastTimestamp = yield* Effect.map(sqlite.exec({
258
+ ...Sql.selectLastTimestampForTableRowColumn,
259
+ parameters: [message.table, message.row, message.column, 1],
260
+ }), ({ rows }) => rows.length > 0 ? rows[0].timestamp : null);
261
+ if (lastTimestamp == null || lastTimestamp < message.timestamp) {
262
+ yield* upsertValueIntoTableRowColumn(message, messages, messageTimestamp.millis);
263
+ }
264
+ if (lastTimestamp == null || lastTimestamp !== message.timestamp) {
265
+ const { changes } = yield* sqlite.exec({
266
+ ...Sql.insertIntoMessagesIfNew,
267
+ parameters: [
268
+ message.timestamp,
269
+ message.table,
270
+ message.row,
271
+ message.column,
272
+ message.value,
273
+ ],
274
+ });
275
+ if (changes === 1)
276
+ merkleTree = insertIntoMerkleTree(merkleTree, messageTimestamp);
277
+ }
278
+ }
279
+ return merkleTree;
280
+ });
281
+ export const upsertValueIntoTableRowColumn = (message, messages, millis) => Sqlite.pipe(Effect.map((sqlite) => {
282
+ const now = cast(new Date(millis));
283
+ return sqlite.exec({
284
+ sql: `
285
+ insert into
286
+ "${message.table}" ("id", "${message.column}", "createdAt", "updatedAt")
287
+ values
288
+ (?, ?, ?, ?)
289
+ on conflict do update set
290
+ "${message.column}" = ?,
291
+ "updatedAt" = ?
292
+ `.trim(),
293
+ parameters: [message.row, message.value, now, now, message.value, now],
294
+ });
295
+ }), Effect.flatMap((insert) => Effect.catchSomeDefect(insert, (error) => S.is(SqliteNoSuchTableOrColumnError)(error)
296
+ ? Option.some(
297
+ // If one message fails, we ensure schema for all messages.
298
+ ensureSchemaByNewMessages(messages).pipe(Effect.zipRight(insert)))
299
+ : Option.none())));
300
+ const SqliteNoSuchTableOrColumnError = S.Struct({
301
+ 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"))),
302
+ });
303
+ const ensureSchemaByNewMessages = (messages) => Effect.gen(function* () {
304
+ const tablesMap = new Map();
305
+ messages.forEach((message) => {
306
+ const table = tablesMap.get(message.table);
307
+ if (table == null) {
308
+ tablesMap.set(message.table, {
309
+ name: message.table,
310
+ columns: [message.column, "createdAt", "updatedAt"],
311
+ });
312
+ return;
313
+ }
314
+ if (table.columns.includes(message.column))
315
+ return;
316
+ tablesMap.set(message.table, {
317
+ name: message.table,
318
+ columns: table.columns.concat(message.column),
319
+ });
320
+ });
321
+ const tables = Arr.fromIterable(tablesMap.values());
322
+ yield* Effect.flatMap(getSchema, ensureSchema({ tables, indexes: [] }));
323
+ });
324
+ const writeTimestampAndMerkleTree = (timestamp, merkleTree) => Effect.flatMap(Sqlite, (sqlite) => sqlite.exec({
325
+ ...Sql.updateOwnerTimestampAndMerkleTree,
326
+ parameters: [
327
+ merkleTreeToString(merkleTree),
328
+ timestampToString(timestamp),
329
+ ],
330
+ }));
331
+ const dropAllTables = Effect.gen(function* () {
332
+ yield* Effect.logTrace("Db dropAllTables");
333
+ const sqlite = yield* Sqlite;
334
+ const schema = yield* getSchema;
335
+ yield* Effect.forEach(schema.tables, (table) =>
336
+ // The dropped table is completely removed from the database schema and
337
+ // the disk file. The table can not be recovered.
338
+ // All indices and triggers associated with the table are also deleted.
339
+ // https://sqlite.org/lang_droptable.html
340
+ sqlite.exec({ sql: `drop table "${table.name}"` }));
341
+ });
342
+ const forkSync = (messages = []) => SyncLock.pipe(Effect.flatMap((syncLock) => syncLock.tryAcquire), Effect.matchEffect({
343
+ onFailure: () => Effect.void,
344
+ onSuccess: () => syncLoop(messages),
345
+ }), Effect.scoped, Effect.forkDaemon);
346
+ const syncLoop = (messages = []) => Effect.gen(function* (_) {
347
+ const sqlite = yield* Sqlite;
348
+ const sync = yield* Sync;
349
+ const callbacks = yield* Callbacks;
350
+ const time = yield* Time;
351
+ const syncDataRef = yield* readTimestampAndMerkleTree.pipe(sqlite.transaction("shared"), Effect.map((a) => ({ ...a, messages })), Effect.flatMap(Ref.make));
352
+ callbacks.onSyncStateChange({ _tag: "SyncStateIsSyncing" });
353
+ yield* Ref.get(syncDataRef).pipe(Effect.flatMap(sync.sync), Effect.flatMap(handleSyncResult), Effect.repeat({
354
+ // TODO: Schedule.jittered(Schedule.exponential("10 millis"))
355
+ schedule: Schedule.recurs(50),
356
+ until: Option.match({
357
+ onNone: () => Effect.gen(function* () {
358
+ callbacks.onSyncStateChange({
359
+ _tag: "SyncStateIsSynced",
360
+ time: yield* time.now,
361
+ });
362
+ return true;
363
+ }),
364
+ onSome: (syncData) => Effect.gen(function* () {
365
+ yield* Ref.set(syncDataRef, syncData);
366
+ return false;
367
+ }),
368
+ }),
369
+ }), Effect.catchAllDefect((error) =>
370
+ // Db can run in a Web Worker, so we must ensure transferable error.
371
+ Effect.fail(makeUnexpectedError(ensureTransferableError(error)))), Effect.catchTag("SyncStateIsNotSynced", (state) => Effect.succeed(callbacks.onSyncStateChange(state))), Effect.catchAll((error) => Effect.succeed(callbacks.onError(error))));
372
+ });
373
+ const handleSyncResult = (result) => Effect.flatMap(Sqlite, (sqlite) => Effect.gen(function* (_) {
374
+ const { onReceive } = yield* Callbacks;
375
+ const current = yield* readTimestampAndMerkleTree;
376
+ const nextTimestamp = yield* Effect.reduce(result.messages, current.timestamp, (local, message) => receiveTimestamp({
377
+ local,
378
+ remote: unsafeTimestampFromString(message.timestamp),
379
+ }));
380
+ const nextMerkleTree = yield* applyMessages(current.merkleTree, result.messages);
381
+ if (result.messages.length > 0) {
382
+ yield* writeTimestampAndMerkleTree(nextTimestamp, nextMerkleTree);
383
+ onReceive();
384
+ }
385
+ return yield* diffMerkleTrees(result.merkleTree, nextMerkleTree).pipe(Effect.flatMap((diff) => sqlite.exec({
386
+ ...Sql.selectMessagesToSync,
387
+ parameters: [timestampToString(makeSyncTimestamp(diff))],
388
+ })), Effect.map(({ rows }) => ({
389
+ messages: rows,
390
+ merkleTree: nextMerkleTree,
391
+ timestamp: nextTimestamp,
392
+ })), Effect.option);
393
+ }).pipe(sqlite.transaction("exclusive")));
394
+ // We use queries as keys, hence JSON.stringify.
395
+ export const serializeQuery = ({ sql, parameters = [], options, }) => {
396
+ const query = {
397
+ sql,
398
+ parameters: parameters.map((p) => Predicate.isUint8Array(p)
399
+ ? Arr.fromIterable(p)
400
+ : isJsonObjectOrArray(p)
401
+ ? { json: p }
402
+ : p),
403
+ ...(options && { options }),
404
+ };
405
+ return JSON.stringify(query);
406
+ };
407
+ export const deserializeQuery = (query) => {
408
+ const serializedSqliteQuery = JSON.parse(query);
409
+ return {
410
+ ...serializedSqliteQuery,
411
+ parameters: serializedSqliteQuery.parameters.map((p) => Arr.isArray(p)
412
+ ? new Uint8Array(p)
413
+ : typeof p === "object" && p != null
414
+ ? p.json
415
+ : p),
416
+ };
417
+ };
418
+ // To preserve identity.
419
+ const _emptyRows = [];
420
+ export const emptyRows = () => _emptyRows;
421
+ // To preserve identity.
422
+ const queryResultCache = new WeakMap();
423
+ export const queryResultFromRows = (rows) => {
424
+ let queryResult = queryResultCache.get(rows);
425
+ if (queryResult == null) {
426
+ queryResult = { rows, row: rows[0] };
427
+ queryResultCache.set(rows, queryResult);
428
+ }
429
+ return queryResult;
430
+ };
431
+ export const notSupportedPlatformWorker = {
432
+ init: () => Effect.fail({
433
+ _tag: "NotSupportedPlatformError",
434
+ }),
435
+ loadQueries: () => Effect.succeed([]),
436
+ mutate: () => Effect.succeed([]),
437
+ resetOwner: () => Effect.void,
438
+ restoreOwner: () => Effect.void,
439
+ ensureSchema: () => Effect.void,
440
+ sync: () => Effect.succeed([]),
441
+ };