@evolu/common 1.0.13 → 1.0.15

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
@@ -1,6 +1,7 @@
1
1
  import * as S from "@effect/schema/Schema";
2
2
  import { Brand, Context, Effect, ReadonlyRecord } from "effect";
3
3
  import * as Kysely from "kysely";
4
+ import { Simplify } from "kysely";
4
5
  import { Bip39, Mnemonic, NanoId } from "./Crypto.js";
5
6
  import { Id, SqliteBoolean, SqliteDate } from "./Model.js";
6
7
  import { Query, Row, Sqlite, Value } from "./Sqlite.js";
@@ -9,9 +10,9 @@ export type TableSchema = ReadonlyRecord.ReadonlyRecord<Value> & {
9
10
  };
10
11
  export type Schema = ReadonlyRecord.ReadonlyRecord<TableSchema>;
11
12
  export type CreateQuery<S extends Schema> = (queryCallback: QueryCallback<S, Row>) => Query;
12
- export type QueryCallback<S extends Schema, QueryRow> = (db: KyselyWithoutMutation<SchemaForQuery<S>>) => Kysely.SelectQueryBuilder<any, any, QueryRow>;
13
+ export type QueryCallback<S extends Schema, QueryRow> = (db: KyselyWithoutMutation<QuerySchema<S>>) => Kysely.SelectQueryBuilder<any, any, QueryRow>;
13
14
  type KyselyWithoutMutation<DB> = Pick<Kysely.Kysely<DB>, "selectFrom" | "fn">;
14
- type SchemaForQuery<S extends Schema> = {
15
+ type QuerySchema<S extends Schema> = {
15
16
  readonly [Table in keyof S]: NullableExceptOfId<{
16
17
  readonly [Column in keyof S[Table]]: S[Table][Column];
17
18
  } & CommonColumns>;
@@ -24,12 +25,45 @@ export interface CommonColumns {
24
25
  readonly updatedAt: SqliteDate;
25
26
  readonly isDeleted: SqliteBoolean;
26
27
  }
28
+ /**
29
+ * Filter and map array items in one step with the correct return type and
30
+ * without unreliable TypeScript type guards.
31
+ *
32
+ * ### Examples
33
+ *
34
+ * ```
35
+ * useQuery(
36
+ * (db) => db.selectFrom("todo").selectAll(),
37
+ * // Filter and map nothing.
38
+ * (row) => row,
39
+ * );
40
+ *
41
+ * useQuery(
42
+ * (db) => db.selectFrom("todo").selectAll(),
43
+ * // Filter items with title != null.
44
+ * // Note the title type isn't nullable anymore in rows.
45
+ * ({ title, ...rest }) => title != null && { title, ...rest },
46
+ * );
47
+ * ```
48
+ */
49
+ export type FilterMap<QueryRow extends Row, FilterMapRow extends Row> = (row: QueryRow) => FilterMapRow | null | false;
50
+ export interface QueryResult<FilterMapRow extends Row> {
51
+ /**
52
+ * Rows from the database. They can be filtered and mapped by `filterMap`.
53
+ */
54
+ readonly rows: ReadonlyArray<Readonly<Simplify<ExcludeNullAndFalse<FilterMapRow>>>>;
55
+ /**
56
+ * The first row from `rows`. For empty rows, it's null.
57
+ */
58
+ readonly firstRow: Readonly<Simplify<ExcludeNullAndFalse<FilterMapRow>>> | null;
59
+ }
60
+ type ExcludeNullAndFalse<T> = Exclude<T, null | false>;
27
61
  export interface Table {
28
62
  readonly name: string;
29
63
  readonly columns: ReadonlyArray<string>;
30
64
  }
31
65
  export type Tables = ReadonlyArray<Table>;
32
- export declare const makeCreateQuery: <T extends Schema>() => CreateQuery<T>;
66
+ export declare const makeCreateQuery: <S extends Schema>() => CreateQuery<S>;
33
67
  export declare const schemaToTables: (schema: S.Schema<any, any>) => Tables;
34
68
  /**
35
69
  * `Owner` represents the Evolu database owner. Evolu auto-generates `Owner`
@@ -52,9 +86,16 @@ export declare const transaction: <R, E, A>(effect: Effect.Effect<R, E, A>) => E
52
86
  export interface NoSuchTableOrColumnError {
53
87
  readonly _tag: "NoSuchTableOrColumnError";
54
88
  }
89
+ export declare const SqliteNoSuchTableOrColumnError: S.Schema<{
90
+ readonly message: string;
91
+ }, {
92
+ readonly message: string;
93
+ }>;
94
+ export type SqliteNoSuchTableOrColumnError = S.Schema.To<typeof SqliteNoSuchTableOrColumnError>;
55
95
  export declare const someDefectToNoSuchTableOrColumnError: <R, E, A>(self: Effect.Effect<R, E, A>) => Effect.Effect<R, NoSuchTableOrColumnError | E, A>;
56
96
  export declare const makeOwner: (mnemonic?: Mnemonic) => Effect.Effect<Bip39, never, Owner>;
57
97
  export declare const lazyInit: (mnemonic?: Mnemonic) => Effect.Effect<Sqlite | Bip39 | NanoId, never, Owner>;
58
98
  export declare const ensureSchema: (tables: Tables) => Effect.Effect<Sqlite, never, void>;
99
+ export declare const makeCacheFilterMap: () => <QueryRow extends Row, FilterMapRow extends Row>(filterMap: FilterMap<QueryRow, FilterMapRow>) => FilterMap<QueryRow, FilterMapRow>;
59
100
  export {};
60
101
  //# sourceMappingURL=Db.d.ts.map
@@ -1 +1 @@
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,oCAAoC,8FAoBhD,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"}
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)
@@ -115,3 +110,14 @@ const createTable = ({ name, columns, }) => Effect.flatMap(Sqlite, (sqlite) => s
115
110
  export const ensureSchema = (tables) => Effect.flatMap(getTables, (existingTables) => Effect.forEach(tables, (tableDefinition) => existingTables.includes(tableDefinition.name)
116
111
  ? updateTable(tableDefinition)
117
112
  : createTable(tableDefinition), { discard: true }));
113
+ export const makeCacheFilterMap = () => {
114
+ const cache = new WeakMap();
115
+ return (filterMap) => (row) => {
116
+ let cachedRow = cache.get(row);
117
+ if (cachedRow === undefined) {
118
+ cachedRow = filterMap(row);
119
+ cache.set(row, cachedRow);
120
+ }
121
+ return cachedRow;
122
+ };
123
+ };
@@ -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({
@@ -27,7 +27,7 @@ export interface Evolu<S extends Schema> {
27
27
  readonly ownerActions: OwnerActions;
28
28
  readonly ensureSchema: (tables: Tables) => void;
29
29
  }
30
- export declare const Evolu: <T extends Schema>() => Context.Tag<Evolu<T>, Evolu<T>>;
30
+ export declare const Evolu: <S extends Schema>() => Context.Tag<Evolu<S>, Evolu<S>>;
31
31
  type ErrorStore = Store<EvoluError | null>;
32
32
  type OwnerStore = Store<Owner | null>;
33
33
  interface QueryStore {
@@ -65,7 +65,7 @@ interface RestoreOwnerError {
65
65
  readonly _tag: "RestoreOwnerError";
66
66
  }
67
67
  export declare const loadingPromisesPromiseProp = "rows";
68
- export declare const EvoluLive: <T extends Schema>(tables: Tables) => Layer.Layer<Config | Bip39 | NanoId | Time | FlushSync | AppState | DbWorker, never, Evolu<T>>;
69
- export declare const makeEvoluForPlatform: <T extends Schema>(PlatformLayer: Layer.Layer<never, never, DbWorker | Bip39 | NanoId | FlushSync | AppState>, tables: Tables, config?: Partial<Config>) => Evolu<T>;
68
+ export declare const EvoluLive: <S extends Schema>(tables: Tables) => Layer.Layer<Config | Bip39 | NanoId | Time | FlushSync | AppState | DbWorker, never, Evolu<S>>;
69
+ export declare const makeEvoluForPlatform: <S extends Schema>(PlatformLayer: Layer.Layer<never, never, DbWorker | Bip39 | NanoId | FlushSync | AppState>, tables: Tables, config?: Partial<Config>) => Evolu<S>;
70
70
  export {};
71
71
  //# sourceMappingURL=Evolu.d.ts.map
@@ -1,6 +1,5 @@
1
1
  import * as Schema from "@effect/schema/Schema";
2
2
  import { Brand } from "effect";
3
- import { Row } from "./Sqlite.js";
4
3
  /**
5
4
  * Branded Id Schema for any table Id.
6
5
  * To create Id Schema for a specific table, use {@link id}.
@@ -116,28 +115,4 @@ export type NonEmptyString1000 = Schema.Schema.To<typeof NonEmptyString1000>;
116
115
  */
117
116
  export declare const PositiveInt: Schema.BrandSchema<number, number & Brand.Brand<"PositiveInt">>;
118
117
  export type PositiveInt = Schema.Schema.To<typeof PositiveInt>;
119
- /**
120
- * Filter and map array items in one step with the correct return type and
121
- * without unreliable TypeScript type guards.
122
- *
123
- * ### Examples
124
- *
125
- * ```
126
- * useQuery(
127
- * (db) => db.selectFrom("todo").selectAll(),
128
- * // Filter and map nothing.
129
- * (row) => row,
130
- * );
131
- *
132
- * useQuery(
133
- * (db) => db.selectFrom("todo").selectAll(),
134
- * // Filter items with title != null.
135
- * // Note the title type isn't nullable anymore in rows.
136
- * ({ title, ...rest }) => title != null && { title, ...rest },
137
- * );
138
- * ```
139
- */
140
- export type FilterMap<QueryRow extends Row, FilterMapRow extends Row> = (row: QueryRow) => OrNullOrFalse<FilterMapRow>;
141
- export type OrNullOrFalse<T> = T | null | false;
142
- export type ExcludeNullAndFalse<T> = Exclude<T, null | false>;
143
118
  //# sourceMappingURL=Model.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Model.d.ts","sourceRoot":"","sources":["../../src/Model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,GAAG,EAAa,MAAM,aAAa,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,EAAE,wDAGd,CAAC;AACF,MAAM,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;AAE7C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,EAAE,yGAGe,CAAC;AAE/B;;;;GAIG;AACH,eAAO,MAAM,UAAU,gEAGtB,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,UAAU,CAAC,CAAC;AAE7D;;;;GAIG;AACH,eAAO,MAAM,aAAa,mEAIzB,CAAC;AACF,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,aAAa,CAAC,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;IACjC,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,aAAa,GAC/C,OAAO,GAAG,aAAa,GACvB,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,aAAa,GACjC,IAAI,GAAG,OAAO,GAAG,aAAa,GAC9B,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,GACvB,IAAI,GAAG,UAAU,GACjB,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,UAAU,GAC9B,IAAI,GAAG,IAAI,GAAG,UAAU,GACxB,CAAC,CAAC,CAAC,CAAC;CACT,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAAC;AACpD,wBAAgB,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC;AACpD,wBAAgB,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,UAAU,CAAC;AAC9C,wBAAgB,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;AAW9C;;;;;GAKG;AACH,eAAO,MAAM,MAAM,4DAclB,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC;AAErD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU,wFAGtB,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,UAAU,CAAC,CAAC;AAE7D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,gGAI9B,CAAC;AACF,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE7E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,iEAIvB,CAAC;AACF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,WAAW,CAAC,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,SAAS,CAAC,QAAQ,SAAS,GAAG,EAAE,YAAY,SAAS,GAAG,IAAI,CACtE,GAAG,EAAE,QAAQ,KACV,aAAa,CAAC,YAAY,CAAC,CAAC;AAEjC,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC;AAEhD,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,EAAE,IAAI,GAAG,KAAK,CAAC,CAAC"}
1
+ {"version":3,"file":"Model.d.ts","sourceRoot":"","sources":["../../src/Model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAG/B;;;GAGG;AACH,eAAO,MAAM,EAAE,wDAGd,CAAC;AACF,MAAM,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;AAE7C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,EAAE,yGAGe,CAAC;AAE/B;;;;GAIG;AACH,eAAO,MAAM,UAAU,gEAGtB,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,UAAU,CAAC,CAAC;AAE7D;;;;GAIG;AACH,eAAO,MAAM,aAAa,mEAIzB,CAAC;AACF,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,aAAa,CAAC,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI;IACjC,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,aAAa,GAC/C,OAAO,GAAG,aAAa,GACvB,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,aAAa,GACjC,IAAI,GAAG,OAAO,GAAG,aAAa,GAC9B,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,GACvB,IAAI,GAAG,UAAU,GACjB,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,UAAU,GAC9B,IAAI,GAAG,IAAI,GAAG,UAAU,GACxB,CAAC,CAAC,CAAC,CAAC;CACT,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAAC;AACpD,wBAAgB,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC;AACpD,wBAAgB,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,UAAU,CAAC;AAC9C,wBAAgB,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;AAW9C;;;;;GAKG;AACH,eAAO,MAAM,MAAM,4DAclB,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC;AAErD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU,wFAGtB,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,UAAU,CAAC,CAAC;AAE7D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,gGAI9B,CAAC;AACF,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE7E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,iEAIvB,CAAC;AACF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,WAAW,CAAC,CAAC"}
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.13",
3
+ "version": "1.0.15",
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",
@@ -42,11 +42,12 @@
42
42
  "nanoid": "^5.0.2"
43
43
  },
44
44
  "devDependencies": {
45
- "@effect/schema": "0.45.5",
45
+ "@effect/schema": "0.46.1",
46
46
  "@protobuf-ts/plugin": "^2.9.1",
47
47
  "@protobuf-ts/protoc": "^2.9.1",
48
48
  "array-shuffle": "^3.0.0",
49
- "effect": "2.0.0-next.50",
49
+ "better-sqlite3": "^9.0.0",
50
+ "effect": "2.0.0-next.52",
50
51
  "eslint": "^8.52.0",
51
52
  "typescript": "^5.2.2",
52
53
  "vitest": "^0.34.6",
@@ -54,8 +55,8 @@
54
55
  "eslint-config-evolu": "0.0.2"
55
56
  },
56
57
  "peerDependencies": {
57
- "@effect/schema": "0.45.5",
58
- "effect": "2.0.0-next.50"
58
+ "@effect/schema": "0.46.1",
59
+ "effect": "2.0.0-next.52"
59
60
  },
60
61
  "publishConfig": {
61
62
  "access": "public"
package/src/Db.ts CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  pipe,
16
16
  } from "effect";
17
17
  import * as Kysely from "kysely";
18
+ import { Simplify } from "kysely";
18
19
  import { urlAlphabet } from "nanoid";
19
20
  import {
20
21
  initialMerkleTree,
@@ -50,13 +51,13 @@ export type CreateQuery<S extends Schema> = (
50
51
  ) => Query;
51
52
 
52
53
  export type QueryCallback<S extends Schema, QueryRow> = (
53
- db: KyselyWithoutMutation<SchemaForQuery<S>>,
54
+ db: KyselyWithoutMutation<QuerySchema<S>>,
54
55
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
56
  ) => Kysely.SelectQueryBuilder<any, any, QueryRow>;
56
57
 
57
58
  type KyselyWithoutMutation<DB> = Pick<Kysely.Kysely<DB>, "selectFrom" | "fn">;
58
59
 
59
- type SchemaForQuery<S extends Schema> = {
60
+ type QuerySchema<S extends Schema> = {
60
61
  readonly [Table in keyof S]: NullableExceptOfId<
61
62
  {
62
63
  readonly [Column in keyof S[Table]]: S[Table][Column];
@@ -74,6 +75,48 @@ export interface CommonColumns {
74
75
  readonly isDeleted: SqliteBoolean;
75
76
  }
76
77
 
78
+ /**
79
+ * Filter and map array items in one step with the correct return type and
80
+ * without unreliable TypeScript type guards.
81
+ *
82
+ * ### Examples
83
+ *
84
+ * ```
85
+ * useQuery(
86
+ * (db) => db.selectFrom("todo").selectAll(),
87
+ * // Filter and map nothing.
88
+ * (row) => row,
89
+ * );
90
+ *
91
+ * useQuery(
92
+ * (db) => db.selectFrom("todo").selectAll(),
93
+ * // Filter items with title != null.
94
+ * // Note the title type isn't nullable anymore in rows.
95
+ * ({ title, ...rest }) => title != null && { title, ...rest },
96
+ * );
97
+ * ```
98
+ */
99
+ export type FilterMap<QueryRow extends Row, FilterMapRow extends Row> = (
100
+ row: QueryRow,
101
+ ) => FilterMapRow | null | false;
102
+
103
+ export interface QueryResult<FilterMapRow extends Row> {
104
+ /**
105
+ * Rows from the database. They can be filtered and mapped by `filterMap`.
106
+ */
107
+ readonly rows: ReadonlyArray<
108
+ Readonly<Simplify<ExcludeNullAndFalse<FilterMapRow>>>
109
+ >;
110
+ /**
111
+ * The first row from `rows`. For empty rows, it's null.
112
+ */
113
+ readonly firstRow: Readonly<
114
+ Simplify<ExcludeNullAndFalse<FilterMapRow>>
115
+ > | null;
116
+ }
117
+
118
+ type ExcludeNullAndFalse<T> = Exclude<T, null | false>;
119
+
77
120
  export interface Table {
78
121
  readonly name: string;
79
122
  readonly columns: ReadonlyArray<string>;
@@ -83,7 +126,7 @@ export type Tables = ReadonlyArray<Table>;
83
126
 
84
127
  const commonColumns = ["createdAt", "updatedAt", "isDeleted"];
85
128
 
86
- const kysely: Kysely.Kysely<SchemaForQuery<Schema>> = new Kysely.Kysely({
129
+ const kysely: Kysely.Kysely<QuerySchema<Schema>> = new Kysely.Kysely({
87
130
  dialect: {
88
131
  createAdapter: () => new Kysely.SqliteAdapter(),
89
132
  createDriver: () => new Kysely.DummyDriver(),
@@ -95,7 +138,7 @@ const kysely: Kysely.Kysely<SchemaForQuery<Schema>> = new Kysely.Kysely({
95
138
  });
96
139
 
97
140
  export const makeCreateQuery =
98
- <T extends Schema>(): CreateQuery<T> =>
141
+ <S extends Schema>(): CreateQuery<S> =>
99
142
  (queryCallback) =>
100
143
  queryObjectToQuery(queryCallback(kysely as never).compile() as QueryObject);
101
144
 
@@ -169,26 +212,26 @@ export interface NoSuchTableOrColumnError {
169
212
  readonly _tag: "NoSuchTableOrColumnError";
170
213
  }
171
214
 
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
- );
215
+ export const SqliteNoSuchTableOrColumnError = S.struct({
216
+ message: S.union(
217
+ S.string.pipe(S.includes("no such table")),
218
+ S.string.pipe(S.includes("no such column")),
219
+ S.string.pipe(S.includes("has no column")),
220
+ ),
221
+ });
222
+ export type SqliteNoSuchTableOrColumnError = S.Schema.To<
223
+ typeof SqliteNoSuchTableOrColumnError
224
+ >;
189
225
 
190
- return Option.none();
191
- },
226
+ export const someDefectToNoSuchTableOrColumnError = Effect.catchSomeDefect(
227
+ (error) =>
228
+ S.is(SqliteNoSuchTableOrColumnError)(error)
229
+ ? Option.some(
230
+ Effect.fail<NoSuchTableOrColumnError>({
231
+ _tag: "NoSuchTableOrColumnError",
232
+ }),
233
+ )
234
+ : Option.none(),
192
235
  );
193
236
 
194
237
  export const makeOwner = (
@@ -325,3 +368,24 @@ export const ensureSchema = (
325
368
  { discard: true },
326
369
  ),
327
370
  );
371
+
372
+ export const makeCacheFilterMap = (): (<
373
+ QueryRow extends Row,
374
+ FilterMapRow extends Row,
375
+ >(
376
+ filterMap: FilterMap<QueryRow, FilterMapRow>,
377
+ ) => FilterMap<QueryRow, FilterMapRow>) => {
378
+ const cache = new WeakMap<Row, Row | null | false>();
379
+
380
+ return <QueryRow extends Row, FilterMapRow extends Row>(
381
+ filterMap: FilterMap<QueryRow, FilterMapRow>,
382
+ ): FilterMap<QueryRow, FilterMapRow> =>
383
+ (row: QueryRow) => {
384
+ let cachedRow = cache.get(row);
385
+ if (cachedRow === undefined) {
386
+ cachedRow = filterMap(row);
387
+ cache.set(row, cachedRow);
388
+ }
389
+ return cachedRow as FilterMapRow | null | false;
390
+ };
391
+ };
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/Evolu.ts CHANGED
@@ -60,8 +60,8 @@ export interface Evolu<S extends Schema> {
60
60
  readonly ensureSchema: (tables: Tables) => void;
61
61
  }
62
62
 
63
- export const Evolu = <T extends Schema>(): Context.Tag<Evolu<T>, Evolu<T>> =>
64
- Context.Tag<Evolu<T>>("evolu/Evolu");
63
+ export const Evolu = <S extends Schema>(): Context.Tag<Evolu<S>, Evolu<S>> =>
64
+ Context.Tag<Evolu<S>>("evolu/Evolu");
65
65
 
66
66
  type ErrorStore = Store<EvoluError | null>;
67
67
 
@@ -118,8 +118,8 @@ type Mutate<S extends Schema> = <
118
118
  readonly id: U[T]["id"];
119
119
  };
120
120
 
121
- const Mutate = <T extends Schema>(): Context.Tag<Mutate<T>, Mutate<T>> =>
122
- Context.Tag<Mutate<T>>("evolu/Mutate");
121
+ const Mutate = <S extends Schema>(): Context.Tag<Mutate<S>, Mutate<S>> =>
122
+ Context.Tag<Mutate<S>>("evolu/Mutate");
123
123
 
124
124
  type SchemaForMutate<S extends Schema> = {
125
125
  readonly [Table in keyof S]: NullableExceptOfId<
@@ -297,13 +297,13 @@ const QueryStoreLive = Layer.effect(
297
297
  }),
298
298
  );
299
299
 
300
- const MutateLive = <T extends Schema>(): Layer.Layer<
300
+ const MutateLive = <S extends Schema>(): Layer.Layer<
301
301
  NanoId | OnCompletes | Time | SubscribedQueries | LoadingPromises | DbWorker,
302
302
  never,
303
- Mutate<T>
303
+ Mutate<S>
304
304
  > =>
305
305
  Layer.effect(
306
- Mutate<T>(),
306
+ Mutate<S>(),
307
307
  Effect.gen(function* (_) {
308
308
  const nanoid = yield* _(NanoId);
309
309
  const onCompletes = yield* _(OnCompletes);
@@ -314,7 +314,7 @@ const MutateLive = <T extends Schema>(): Layer.Layer<
314
314
 
315
315
  const queue: Array<MutateItem> = [];
316
316
 
317
- return Mutate<T>().of((table, { id, ...values }, onComplete) => {
317
+ return Mutate<S>().of((table, { id, ...values }, onComplete) => {
318
318
  const isInsert = id == null;
319
319
  if (isInsert) id = Effect.runSync(nanoid.nanoid) as never;
320
320
 
@@ -381,15 +381,15 @@ const OwnerActionsLive = Layer.effect(
381
381
  }),
382
382
  );
383
383
 
384
- export const EvoluLive = <T extends Schema>(
384
+ export const EvoluLive = <S extends Schema>(
385
385
  tables: Tables,
386
386
  ): Layer.Layer<
387
387
  DbWorker | Bip39 | Config | FlushSync | NanoId | Time | AppState,
388
388
  never,
389
- Evolu<T>
389
+ Evolu<S>
390
390
  > =>
391
391
  Layer.effect(
392
- Evolu<T>(),
392
+ Evolu<S>(),
393
393
  Effect.gen(function* (_) {
394
394
  const dbWorker = yield* _(DbWorker);
395
395
  const appState = yield* _(AppState);
@@ -425,8 +425,8 @@ export const EvoluLive = <T extends Schema>(
425
425
  ).pipe(Effect.runSync);
426
426
 
427
427
  const mutate = Effect.provide(
428
- Mutate<T>(),
429
- Layer.use(MutateLive<T>(), Layers),
428
+ Mutate<S>(),
429
+ Layer.use(MutateLive<S>(), Layers),
430
430
  ).pipe(Effect.runSync);
431
431
 
432
432
  const ownerActions = Effect.provide(
@@ -434,7 +434,7 @@ export const EvoluLive = <T extends Schema>(
434
434
  Layer.use(OwnerActionsLive, Layers),
435
435
  ).pipe(Effect.runSync);
436
436
 
437
- const ensureSchema: Evolu<T>["ensureSchema"] = (tables) => {
437
+ const ensureSchema: Evolu<S>["ensureSchema"] = (tables) => {
438
438
  dbWorker.postMessage({ _tag: "ensureSchema", tables });
439
439
  };
440
440
 
@@ -486,14 +486,14 @@ export const EvoluLive = <T extends Schema>(
486
486
  appState.onReconnect(sync);
487
487
  sync();
488
488
 
489
- return Evolu<T>().of({
489
+ return Evolu<S>().of({
490
490
  subscribeError: errorStore.subscribe,
491
491
  getError: errorStore.getState,
492
492
 
493
493
  subscribeOwner: ownerStore.subscribe,
494
494
  getOwner: ownerStore.getState,
495
495
 
496
- createQuery: makeCreateQuery<T>(),
496
+ createQuery: makeCreateQuery<S>(),
497
497
  subscribeQuery: queryStore.subscribe,
498
498
  getQuery: queryStore.getState,
499
499
  loadQuery: queryStore.loadQuery,
@@ -501,15 +501,15 @@ export const EvoluLive = <T extends Schema>(
501
501
  subscribeSyncState: syncStateStore.subscribe,
502
502
  getSyncState: syncStateStore.getState,
503
503
 
504
- create: mutate as Create<T>,
505
- update: mutate as Update<T>,
504
+ create: mutate as Create<S>,
505
+ update: mutate as Update<S>,
506
506
  ownerActions,
507
507
  ensureSchema,
508
508
  });
509
509
  }),
510
510
  );
511
511
 
512
- export const makeEvoluForPlatform = <T extends Schema>(
512
+ export const makeEvoluForPlatform = <S extends Schema>(
513
513
  PlatformLayer: Layer.Layer<
514
514
  never,
515
515
  never,
@@ -517,11 +517,11 @@ export const makeEvoluForPlatform = <T extends Schema>(
517
517
  >,
518
518
  tables: Tables,
519
519
  config?: Partial<Config>,
520
- ): Evolu<T> =>
520
+ ): Evolu<S> =>
521
521
  Effect.provide(
522
- Evolu<T>(),
522
+ Evolu<S>(),
523
523
  Layer.use(
524
- EvoluLive<T>(tables),
524
+ EvoluLive<S>(tables),
525
525
  Layer.mergeAll(PlatformLayer, ConfigLive(config), TimeLive),
526
526
  ),
527
527
  ).pipe(Effect.runSync);
package/src/Model.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as Schema from "@effect/schema/Schema";
2
2
  import { Brand } from "effect";
3
- import { Row, maybeJson } from "./Sqlite.js";
3
+ import { maybeJson } from "./Sqlite.js";
4
4
 
5
5
  /**
6
6
  * Branded Id Schema for any table Id.
@@ -181,32 +181,3 @@ export const PositiveInt = Schema.number.pipe(
181
181
  Schema.brand("PositiveInt"),
182
182
  );
183
183
  export type PositiveInt = Schema.Schema.To<typeof PositiveInt>;
184
-
185
- /**
186
- * Filter and map array items in one step with the correct return type and
187
- * without unreliable TypeScript type guards.
188
- *
189
- * ### Examples
190
- *
191
- * ```
192
- * useQuery(
193
- * (db) => db.selectFrom("todo").selectAll(),
194
- * // Filter and map nothing.
195
- * (row) => row,
196
- * );
197
- *
198
- * useQuery(
199
- * (db) => db.selectFrom("todo").selectAll(),
200
- * // Filter items with title != null.
201
- * // Note the title type isn't nullable anymore in rows.
202
- * ({ title, ...rest }) => title != null && { title, ...rest },
203
- * );
204
- * ```
205
- */
206
- export type FilterMap<QueryRow extends Row, FilterMapRow extends Row> = (
207
- row: QueryRow,
208
- ) => OrNullOrFalse<FilterMapRow>;
209
-
210
- export type OrNullOrFalse<T> = T | null | false;
211
-
212
- export type ExcludeNullAndFalse<T> = Exclude<T, null | false>;
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";