@evolu/common 1.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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +205 -0
  3. package/dist/src/Config.d.ts +20 -0
  4. package/dist/src/Config.d.ts.map +1 -0
  5. package/dist/src/Config.js +8 -0
  6. package/dist/src/Crypto.d.ts +42 -0
  7. package/dist/src/Crypto.d.ts.map +1 -0
  8. package/dist/src/Crypto.js +44 -0
  9. package/dist/src/Db.d.ts +60 -0
  10. package/dist/src/Db.d.ts.map +1 -0
  11. package/dist/src/Db.js +118 -0
  12. package/dist/src/DbWorker.d.ts +78 -0
  13. package/dist/src/DbWorker.d.ts.map +1 -0
  14. package/dist/src/DbWorker.js +282 -0
  15. package/dist/src/Diff.d.ts +19 -0
  16. package/dist/src/Diff.d.ts.map +1 -0
  17. package/dist/src/Diff.js +86 -0
  18. package/dist/src/Errors.d.ts +19 -0
  19. package/dist/src/Errors.d.ts.map +1 -0
  20. package/dist/src/Errors.js +11 -0
  21. package/dist/src/Evolu.d.ts +71 -0
  22. package/dist/src/Evolu.d.ts.map +1 -0
  23. package/dist/src/Evolu.js +235 -0
  24. package/dist/src/MerkleTree.d.ts +15 -0
  25. package/dist/src/MerkleTree.d.ts.map +1 -0
  26. package/dist/src/MerkleTree.js +49 -0
  27. package/dist/src/Model.d.ts +143 -0
  28. package/dist/src/Model.d.ts.map +1 -0
  29. package/dist/src/Model.js +103 -0
  30. package/dist/src/Murmurhash.d.ts +2 -0
  31. package/dist/src/Murmurhash.d.ts.map +1 -0
  32. package/dist/src/Murmurhash.js +60 -0
  33. package/dist/src/Platform.d.ts +29 -0
  34. package/dist/src/Platform.d.ts.map +1 -0
  35. package/dist/src/Platform.js +17 -0
  36. package/dist/src/Protobuf.d.ts +130 -0
  37. package/dist/src/Protobuf.d.ts.map +1 -0
  38. package/dist/src/Protobuf.js +100 -0
  39. package/dist/src/Public.d.ts +7 -0
  40. package/dist/src/Public.d.ts.map +1 -0
  41. package/dist/src/Public.js +2 -0
  42. package/dist/src/Sql.d.ts +12 -0
  43. package/dist/src/Sql.d.ts.map +1 -0
  44. package/dist/src/Sql.js +100 -0
  45. package/dist/src/Sqlite.d.ts +30 -0
  46. package/dist/src/Sqlite.d.ts.map +1 -0
  47. package/dist/src/Sqlite.js +41 -0
  48. package/dist/src/Store.d.ts +9 -0
  49. package/dist/src/Store.d.ts.map +1 -0
  50. package/dist/src/Store.js +18 -0
  51. package/dist/src/SyncWorker.d.ts +73 -0
  52. package/dist/src/SyncWorker.d.ts.map +1 -0
  53. package/dist/src/SyncWorker.js +136 -0
  54. package/dist/src/Timestamp.d.ts +44 -0
  55. package/dist/src/Timestamp.d.ts.map +1 -0
  56. package/dist/src/Timestamp.js +76 -0
  57. package/dist/src/index.d.ts +19 -0
  58. package/dist/src/index.d.ts.map +1 -0
  59. package/dist/src/index.js +18 -0
  60. package/package.json +76 -0
  61. package/src/Config.ts +33 -0
  62. package/src/Crypto.ts +113 -0
  63. package/src/Db.ts +323 -0
  64. package/src/DbWorker.ts +683 -0
  65. package/src/Diff.ts +113 -0
  66. package/src/Errors.ts +33 -0
  67. package/src/Evolu.ts +527 -0
  68. package/src/MerkleTree.ts +83 -0
  69. package/src/Model.ts +212 -0
  70. package/src/Murmurhash.ts +70 -0
  71. package/src/Platform.ts +63 -0
  72. package/src/Protobuf.ts +204 -0
  73. package/src/Public.ts +6 -0
  74. package/src/Sql.ts +114 -0
  75. package/src/Sqlite.ts +92 -0
  76. package/src/Store.ts +32 -0
  77. package/src/SyncWorker.ts +344 -0
  78. package/src/Timestamp.ts +192 -0
  79. package/src/index.ts +18 -0
@@ -0,0 +1,235 @@
1
+ import { Context, Effect, Either, Function, Layer, Number, Option, ReadonlyArray, absurd, pipe, } from "effect";
2
+ import { Config, ConfigLive } from "./Config.js";
3
+ import { Bip39, NanoId } from "./Crypto.js";
4
+ import { makeCreateQuery, } from "./Db.js";
5
+ import { DbWorker, } from "./DbWorker.js";
6
+ import { applyPatches } from "./Diff.js";
7
+ import { cast } from "./Model.js";
8
+ import { AppState, FlushSync } from "./Platform.js";
9
+ import { makeStore } from "./Store.js";
10
+ import { Time, TimeLive } from "./Timestamp.js";
11
+ export const Evolu = () => Context.Tag("evolu/Evolu");
12
+ const QueryStore = Context.Tag("evolu/QueryStore");
13
+ const Mutate = () => Context.Tag("evolu/Mutate");
14
+ const OwnerActions = Context.Tag("evolu/OwnerActions");
15
+ const SubscribedQueries = Context.Tag("evolu/SubscribedQueries");
16
+ const OnCompletes = Context.Tag("evolu/OnCompletes");
17
+ const LoadingPromises = Context.Tag("evolu/LoadingPromises");
18
+ export const loadingPromisesPromiseProp = "rows";
19
+ const LoadingPromisesLive = Layer.effect(LoadingPromises, Effect.sync(() => {
20
+ const promises = new Map();
21
+ const getPromise = (query) => {
22
+ const item = promises.get(query);
23
+ if (item)
24
+ return { promise: item.promise, isNew: false };
25
+ let resolve = Function.constVoid;
26
+ const promise = new Promise((_resolve) => {
27
+ resolve = _resolve;
28
+ });
29
+ promises.set(query, { promise, resolve });
30
+ return { promise, isNew: true };
31
+ };
32
+ const resolvePromise = (query, rows) => {
33
+ const item = promises.get(query);
34
+ if (!item)
35
+ return;
36
+ // It's similar to what React will do.
37
+ void Object.assign(item.promise, { [loadingPromisesPromiseProp]: rows });
38
+ item.resolve(rows);
39
+ };
40
+ const releasePromises = (ignoreQueries) => {
41
+ [...promises.keys()].forEach((query) => {
42
+ if (!ignoreQueries.includes(query))
43
+ promises.delete(query);
44
+ });
45
+ };
46
+ return { getPromise, resolvePromise, releasePromises };
47
+ }));
48
+ const QueryStoreLive = Layer.effect(QueryStore, Effect.gen(function* (_) {
49
+ const subscribedQueries = yield* _(SubscribedQueries);
50
+ const loadingPromises = yield* _(LoadingPromises);
51
+ const dbWorker = yield* _(DbWorker);
52
+ const flushSync = yield* _(FlushSync);
53
+ const onCompletes = yield* _(OnCompletes);
54
+ const rowsCacheStore = makeStore(new Map());
55
+ const queue = new Set();
56
+ const subscribe = (query) => (listen) => {
57
+ if (query == null)
58
+ return Function.constVoid;
59
+ subscribedQueries.set(query, Number.increment(subscribedQueries.get(query) ?? 0));
60
+ const unsubscribe = rowsCacheStore.subscribe(listen);
61
+ return () => {
62
+ // `as number`, because React mount/unmount are symmetric.
63
+ const count = subscribedQueries.get(query);
64
+ if (count > 1)
65
+ subscribedQueries.set(query, Number.decrement(count));
66
+ else
67
+ subscribedQueries.delete(query);
68
+ unsubscribe();
69
+ };
70
+ };
71
+ const getState = (query) => (query && rowsCacheStore.getState().get(query)) || null;
72
+ const loadQuery = (query) => {
73
+ const { promise, isNew } = loadingPromises.getPromise(query);
74
+ if (isNew)
75
+ queue.add(query);
76
+ if (queue.size === 1) {
77
+ queueMicrotask(() => {
78
+ const queries = [...queue];
79
+ queue.clear();
80
+ if (ReadonlyArray.isNonEmptyReadonlyArray(queries))
81
+ dbWorker.postMessage({ _tag: "query", queries });
82
+ });
83
+ }
84
+ return promise;
85
+ };
86
+ const onQuery = ({ queriesPatches, onCompleteIds, }) => {
87
+ const state = rowsCacheStore.getState();
88
+ const nextState = pipe(queriesPatches, ReadonlyArray.map(({ query, patches }) => [query, applyPatches(patches)(state.get(query) || [])]), (a) => new Map([...state, ...a]));
89
+ // Resolve all Promises belonging to queries.
90
+ queriesPatches.forEach(({ query }) => {
91
+ const rows = nextState.get(query) || [];
92
+ loadingPromises.resolvePromise(query, rows);
93
+ });
94
+ // No mutation is using onComplete, so we don't need flushSync.
95
+ if (onCompleteIds.length === 0) {
96
+ rowsCacheStore.setState(nextState);
97
+ return;
98
+ }
99
+ flushSync(() => rowsCacheStore.setState(nextState));
100
+ ReadonlyArray.filterMap(onCompleteIds, (id) => {
101
+ const onComplete = onCompletes.get(id);
102
+ onCompletes.delete(id);
103
+ return Option.fromNullable(onComplete);
104
+ }).forEach((onComplete) => onComplete());
105
+ };
106
+ return { subscribe, getState, loadQuery, onQuery };
107
+ }));
108
+ const MutateLive = () => Layer.effect(Mutate(), Effect.gen(function* (_) {
109
+ const nanoid = yield* _(NanoId);
110
+ const onCompletes = yield* _(OnCompletes);
111
+ const time = yield* _(Time);
112
+ const subscribedQueries = yield* _(SubscribedQueries);
113
+ const loadingPromises = yield* _(LoadingPromises);
114
+ const dbWorker = yield* _(DbWorker);
115
+ const queue = [];
116
+ return Mutate().of((table, { id, ...values }, onComplete) => {
117
+ const isInsert = id == null;
118
+ if (isInsert)
119
+ id = Effect.runSync(nanoid.nanoid);
120
+ let onCompleteId = null;
121
+ if (onComplete) {
122
+ onCompleteId = Effect.runSync(nanoid.nanoid);
123
+ onCompletes.set(onCompleteId, onComplete);
124
+ }
125
+ queue.push({
126
+ table: table.toString(),
127
+ id: id,
128
+ values,
129
+ isInsert,
130
+ now: cast(new Date(Effect.runSync(time.now))),
131
+ onCompleteId,
132
+ });
133
+ if (queue.length === 1)
134
+ queueMicrotask(() => {
135
+ const items = [...queue];
136
+ queue.length = 0;
137
+ const queries = Array.from(subscribedQueries.keys());
138
+ // Just wipe-out LoadingPromises unused queries.
139
+ loadingPromises.releasePromises(queries);
140
+ if (ReadonlyArray.isNonEmptyReadonlyArray(items))
141
+ dbWorker.postMessage({ _tag: "mutate", items, queries });
142
+ });
143
+ return { id: id };
144
+ });
145
+ }));
146
+ const OwnerActionsLive = Layer.effect(OwnerActions, Effect.gen(function* (_) {
147
+ const dbWorker = yield* _(DbWorker);
148
+ const bip39 = yield* _(Bip39);
149
+ const reset = () => {
150
+ dbWorker.postMessage({ _tag: "reset" });
151
+ };
152
+ const restore = (mnemonic) => bip39.parse(mnemonic).pipe(Effect.flatMap((mnemonic) => Effect.sync(() => {
153
+ dbWorker.postMessage({ _tag: "reset", mnemonic });
154
+ return Either.right(undefined);
155
+ })), Effect.catchTag("InvalidMnemonicError", () => Effect.succeed(Either.left({ _tag: "RestoreOwnerError" }))), Effect.runPromise);
156
+ return { reset, restore };
157
+ }));
158
+ export const EvoluLive = (tables) => Layer.effect(Evolu(), Effect.gen(function* (_) {
159
+ const dbWorker = yield* _(DbWorker);
160
+ const appState = yield* _(AppState);
161
+ const config = yield* _(Config);
162
+ const subscribedQueries = new Map();
163
+ const onCompletes = new Map();
164
+ const loadingPromises = Effect.provide(LoadingPromises, LoadingPromisesLive).pipe(Effect.runSync);
165
+ const errorStore = makeStore(null);
166
+ const ownerStore = makeStore(null);
167
+ const syncStateStore = makeStore({
168
+ _tag: "SyncStateInitial",
169
+ });
170
+ const Layers = Layer.mergeAll(Layer.succeed(DbWorker, dbWorker), Layer.succeed(SubscribedQueries, subscribedQueries), Layer.succeed(OnCompletes, onCompletes), Layer.succeed(LoadingPromises, loadingPromises), Layer.succeed(FlushSync, yield* _(FlushSync)), Layer.succeed(NanoId, yield* _(NanoId)), Layer.succeed(Time, yield* _(Time)), Layer.succeed(Bip39, yield* _(Bip39)));
171
+ const queryStore = Effect.provide(QueryStore, Layer.use(QueryStoreLive, Layers)).pipe(Effect.runSync);
172
+ const mutate = Effect.provide(Mutate(), Layer.use(MutateLive(), Layers)).pipe(Effect.runSync);
173
+ const ownerActions = Effect.provide(OwnerActions, Layer.use(OwnerActionsLive, Layers)).pipe(Effect.runSync);
174
+ const ensureSchema = (tables) => {
175
+ dbWorker.postMessage({ _tag: "ensureSchema", tables });
176
+ };
177
+ const getSubscribedQueries = () => Array.from(subscribedQueries.keys());
178
+ dbWorker.onMessage = (output) => {
179
+ // console.log(output);
180
+ switch (output._tag) {
181
+ case "onError":
182
+ if (process.env.NODE_ENV === "development")
183
+ // JSON.stringify, because Expo console needs strings.
184
+ // eslint-disable-next-line no-console
185
+ console.warn(JSON.stringify(output.error, null, 2));
186
+ errorStore.setState(output.error);
187
+ break;
188
+ case "onOwner":
189
+ ownerStore.setState(output.owner);
190
+ break;
191
+ case "onQuery":
192
+ queryStore.onQuery(output);
193
+ break;
194
+ case "onReceive": {
195
+ const queries = getSubscribedQueries();
196
+ if (ReadonlyArray.isNonEmptyReadonlyArray(queries))
197
+ dbWorker.postMessage({ _tag: "query", queries });
198
+ break;
199
+ }
200
+ case "onResetOrRestore":
201
+ Effect.runSync(appState.reset);
202
+ break;
203
+ case "onSyncState":
204
+ syncStateStore.setState(output.state);
205
+ break;
206
+ default:
207
+ absurd(output);
208
+ }
209
+ };
210
+ dbWorker.postMessage({ _tag: "init", config, tables });
211
+ appState.onFocus(() => {
212
+ // `queries` to refresh subscribed queries when a tab is changed.
213
+ dbWorker.postMessage({ _tag: "sync", queries: getSubscribedQueries() });
214
+ });
215
+ const sync = () => dbWorker.postMessage({ _tag: "sync", queries: [] });
216
+ appState.onReconnect(sync);
217
+ sync();
218
+ return Evolu().of({
219
+ subscribeError: errorStore.subscribe,
220
+ getError: errorStore.getState,
221
+ subscribeOwner: ownerStore.subscribe,
222
+ getOwner: ownerStore.getState,
223
+ createQuery: makeCreateQuery(),
224
+ subscribeQuery: queryStore.subscribe,
225
+ getQuery: queryStore.getState,
226
+ loadQuery: queryStore.loadQuery,
227
+ subscribeSyncState: syncStateStore.subscribe,
228
+ getSyncState: syncStateStore.getState,
229
+ create: mutate,
230
+ update: mutate,
231
+ ownerActions,
232
+ ensureSchema,
233
+ });
234
+ }));
235
+ export const makeEvoluForPlatform = (PlatformLayer, tables, config) => Effect.provide(Evolu(), Layer.use(EvoluLive(tables), Layer.mergeAll(PlatformLayer, ConfigLive(config), TimeLive))).pipe(Effect.runSync);
@@ -0,0 +1,15 @@
1
+ import { Brand, Option } from "effect";
2
+ import { Millis, Timestamp, TimestampHash } from "./Timestamp.js";
3
+ export interface MerkleTree {
4
+ readonly hash?: TimestampHash;
5
+ readonly "0"?: MerkleTree;
6
+ readonly "1"?: MerkleTree;
7
+ readonly "2"?: MerkleTree;
8
+ }
9
+ export type MerkleTreeString = string & Brand.Brand<"MerkleTreeString">;
10
+ export declare const initialMerkleTree: MerkleTree;
11
+ export declare const insertIntoMerkleTree: (timestamp: Timestamp) => (tree: MerkleTree) => MerkleTree;
12
+ export declare const diffMerkleTrees: (tree1: MerkleTree, tree2: MerkleTree) => Option.Option<Millis>;
13
+ export declare const merkleTreeToString: (m: MerkleTree) => MerkleTreeString;
14
+ export declare const unsafeMerkleTreeFromString: (m: MerkleTreeString) => MerkleTree;
15
+ //# sourceMappingURL=MerkleTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MerkleTree.d.ts","sourceRoot":"","sources":["../../src/MerkleTree.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EACL,MAAM,EACN,SAAS,EACT,aAAa,EAEd,MAAM,gBAAgB,CAAC;AAKxB,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC;CAC3B;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;AAExE,eAAO,MAAM,iBAAiB,YAAoC,CAAC;AAwBnE,eAAO,MAAM,oBAAoB,cACnB,SAAS,YACd,UAAU,KAAG,UAKnB,CAAC;AAKJ,eAAO,MAAM,eAAe,UACnB,UAAU,SACV,UAAU,KAChB,aAAa,CAAC,MAAM,CAiBtB,CAAC;AAEF,eAAO,MAAM,kBAAkB,MAAO,UAAU,KAAG,gBACZ,CAAC;AAExC,eAAO,MAAM,0BAA0B,MAAO,gBAAgB,KAAG,UACpC,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { Option } from "effect";
2
+ import { timestampToHash, } from "./Timestamp.js";
3
+ export const initialMerkleTree = Object.create(null);
4
+ const timestampToKey = (timestamp) => Math.floor(timestamp.millis / 1000 / 60).toString(3);
5
+ const insertKey = (tree, key, hash) => {
6
+ if (key.length === 0)
7
+ return tree;
8
+ const childKey = key[0];
9
+ const child = tree[childKey] || {};
10
+ return {
11
+ ...tree,
12
+ [childKey]: {
13
+ ...child,
14
+ ...insertKey(child, key.slice(1), hash),
15
+ // @ts-expect-error undefined is OK
16
+ hash: child.hash ^ hash,
17
+ },
18
+ };
19
+ };
20
+ export const insertIntoMerkleTree = (timestamp) => (tree) => {
21
+ const key = timestampToKey(timestamp);
22
+ const hash = timestampToHash(timestamp);
23
+ // @ts-expect-error undefined is OK
24
+ return insertKey({ ...tree, hash: tree.hash ^ hash }, key, hash);
25
+ };
26
+ const keyToTimestamp = (key) => (parseInt(key.length > 0 ? key : "0", 3) * 1000 * 60);
27
+ export const diffMerkleTrees = (tree1, tree2) => {
28
+ if (tree1.hash === tree2.hash)
29
+ return Option.none();
30
+ for1: for (let node1 = tree1, node2 = tree2, key = "";;) {
31
+ for (const k of ["0", "1", "2"]) {
32
+ const next1 = node1[k];
33
+ const next2 = node2[k];
34
+ if (!next1 && !next2)
35
+ continue;
36
+ if (!next1 || !next2)
37
+ break;
38
+ if (next1.hash !== next2.hash) {
39
+ key += k;
40
+ node1 = next1;
41
+ node2 = next2;
42
+ continue for1;
43
+ }
44
+ }
45
+ return Option.some(keyToTimestamp(key));
46
+ }
47
+ };
48
+ export const merkleTreeToString = (m) => JSON.stringify(m);
49
+ export const unsafeMerkleTreeFromString = (m) => JSON.parse(m);
@@ -0,0 +1,143 @@
1
+ import * as Schema from "@effect/schema/Schema";
2
+ import { Brand } from "effect";
3
+ import { Row } from "./Sqlite.js";
4
+ /**
5
+ * Branded Id Schema for any table Id.
6
+ * To create Id Schema for a specific table, use {@link id}.
7
+ */
8
+ export declare const Id: Schema.BrandSchema<string, string & Brand.Brand<"Id">>;
9
+ export type Id = Schema.Schema.To<typeof Id>;
10
+ /**
11
+ * A factory function to create {@link Id} Schema for a specific table.
12
+ *
13
+ * ### Example
14
+ *
15
+ * ```
16
+ * import * as Schema from "@effect/schema/Schema";
17
+ * import * as Evolu from "@evolu/react";
18
+ *
19
+ * const TodoId = Evolu.id("Todo");
20
+ * type TodoId = Schema.Schema.To<typeof TodoId>;
21
+ *
22
+ * if (!Schema.is(TodoId)(value)) return;
23
+ * ```
24
+ */
25
+ export declare const id: <T extends string>(table: T) => Schema.BrandSchema<string, string & Brand.Brand<"Id"> & Brand.Brand<T>>;
26
+ /**
27
+ * SQLite doesn't support the Date type, so Evolu uses SqliteDate instead.
28
+ * Use the {@link cast} helper to cast SqliteDate from Date and back.
29
+ * https://www.sqlite.org/quirks.html#no_separate_datetime_datatype
30
+ */
31
+ export declare const SqliteDate: Schema.BrandSchema<string, string & Brand.Brand<"SqliteDate">>;
32
+ export type SqliteDate = Schema.Schema.To<typeof SqliteDate>;
33
+ /**
34
+ * SQLite doesn't support the boolean type, so Evolu uses SqliteBoolean instead.
35
+ * Use the {@link cast} helper to cast SqliteBoolean from boolean and back.
36
+ * https://www.sqlite.org/quirks.html#no_separate_boolean_datatype
37
+ */
38
+ export declare const SqliteBoolean: Schema.BrandSchema<number, number & Brand.Brand<"SqliteBoolean">>;
39
+ export type SqliteBoolean = Schema.Schema.To<typeof SqliteBoolean>;
40
+ /**
41
+ * SQLite doesn't support Date nor Boolean types, so Evolu emulates them
42
+ * with {@link SqliteBoolean} and {@link SqliteDate}.
43
+ *
44
+ * For {@link SqliteBoolean}, you can use JavaScript boolean.
45
+ * For {@link SqliteDate}, you can use JavaScript Date.
46
+ */
47
+ export type CastableForMutate<T> = {
48
+ readonly [K in keyof T]: T[K] extends SqliteBoolean ? boolean | SqliteBoolean : T[K] extends null | SqliteBoolean ? null | boolean | SqliteBoolean : T[K] extends SqliteDate ? Date | SqliteDate : T[K] extends null | SqliteDate ? null | Date | SqliteDate : T[K];
49
+ };
50
+ /**
51
+ * A helper for casting types not supported by SQLite.
52
+ * SQLite doesn't support Date nor Boolean types, so Evolu emulates them
53
+ * with {@link SqliteBoolean} and {@link SqliteDate}.
54
+ *
55
+ * ### Example
56
+ *
57
+ * ```
58
+ * // isDeleted is SqliteBoolean
59
+ * .where("isDeleted", "is not", cast(true))
60
+ * ```
61
+ */
62
+ export declare function cast(value: boolean): SqliteBoolean;
63
+ export declare function cast(value: SqliteBoolean): boolean;
64
+ export declare function cast(value: Date): SqliteDate;
65
+ export declare function cast(value: SqliteDate): Date;
66
+ /**
67
+ * String schema represents a string that is not stringified JSON. Using
68
+ * String schema for strings stored in SQLite is crucial to ensure a stored
69
+ * string is not automatically parsed to a JSON object or array when
70
+ * retrieved. Use String schema for all string-based schemas.
71
+ */
72
+ export declare const String: Schema.BrandSchema<string, string & Brand.Brand<"String">>;
73
+ export type String = Schema.Schema.To<typeof String>;
74
+ /**
75
+ * A string with a maximum length of 1000 characters.
76
+ *
77
+ * ### Example
78
+ *
79
+ * ```
80
+ * import * as Schema from "@effect/schema/Schema";
81
+ * import * as Evolu from "@evolu/react";
82
+ *
83
+ * if (!Schema.is(Evolu.String1000)(value)) return;
84
+ * function foo(value: Evolu.String1000) {}
85
+ * ```
86
+ */
87
+ export declare const String1000: Schema.BrandSchema<string, string & Brand.Brand<"String"> & Brand.Brand<"String1000">>;
88
+ export type String1000 = Schema.Schema.To<typeof String1000>;
89
+ /**
90
+ * A nonempty string with a maximum length of 1000 characters.
91
+ *
92
+ * ### Example
93
+ *
94
+ * ```
95
+ * import * as Schema from "@effect/schema/Schema";
96
+ * import * as Evolu from "@evolu/react";
97
+ *
98
+ * if (!Schema.is(Evolu.NonEmptyString1000)(value)) return;
99
+ * function foo(value: Evolu.NonEmptyString1000) {}
100
+ * ```
101
+ */
102
+ export declare const NonEmptyString1000: Schema.BrandSchema<string, string & Brand.Brand<"String"> & Brand.Brand<"NonEmptyString1000">>;
103
+ export type NonEmptyString1000 = Schema.Schema.To<typeof NonEmptyString1000>;
104
+ /**
105
+ * A positive integer.
106
+ *
107
+ * ### Example
108
+ *
109
+ * ```
110
+ * import * as Schema from "@effect/schema/Schema";
111
+ * import * as Evolu from "@evolu/react";
112
+ *
113
+ * if (!Schema.is(Evolu.PositiveInt)(value)) return;
114
+ * function foo(value: Evolu.PositiveInt) {}
115
+ * ```
116
+ */
117
+ export declare const PositiveInt: Schema.BrandSchema<number, number & Brand.Brand<"PositiveInt">>;
118
+ 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
+ //# sourceMappingURL=Model.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,103 @@
1
+ import * as Schema from "@effect/schema/Schema";
2
+ import { maybeJson } from "./Sqlite.js";
3
+ /**
4
+ * Branded Id Schema for any table Id.
5
+ * To create Id Schema for a specific table, use {@link id}.
6
+ */
7
+ export const Id = Schema.string.pipe(Schema.pattern(/^[\w-]{21}$/), Schema.brand("Id"));
8
+ /**
9
+ * A factory function to create {@link Id} Schema for a specific table.
10
+ *
11
+ * ### Example
12
+ *
13
+ * ```
14
+ * import * as Schema from "@effect/schema/Schema";
15
+ * import * as Evolu from "@evolu/react";
16
+ *
17
+ * const TodoId = Evolu.id("Todo");
18
+ * type TodoId = Schema.Schema.To<typeof TodoId>;
19
+ *
20
+ * if (!Schema.is(TodoId)(value)) return;
21
+ * ```
22
+ */
23
+ export const id = (table) => Id.pipe(Schema.brand(table));
24
+ /**
25
+ * SQLite doesn't support the Date type, so Evolu uses SqliteDate instead.
26
+ * Use the {@link cast} helper to cast SqliteDate from Date and back.
27
+ * https://www.sqlite.org/quirks.html#no_separate_datetime_datatype
28
+ */
29
+ export const SqliteDate = Schema.string.pipe(Schema.filter((s) => !isNaN(Date.parse(s))), Schema.brand("SqliteDate"));
30
+ /**
31
+ * SQLite doesn't support the boolean type, so Evolu uses SqliteBoolean instead.
32
+ * Use the {@link cast} helper to cast SqliteBoolean from boolean and back.
33
+ * https://www.sqlite.org/quirks.html#no_separate_boolean_datatype
34
+ */
35
+ export const SqliteBoolean = Schema.number.pipe(Schema.int(), Schema.filter((s) => s === 0 || s === 1), Schema.brand("SqliteBoolean"));
36
+ export function cast(value) {
37
+ if (typeof value === "boolean")
38
+ return (value === true ? 1 : 0);
39
+ if (typeof value === "number")
40
+ return value === 1;
41
+ if (value instanceof Date)
42
+ return value.toISOString();
43
+ return new Date(value);
44
+ }
45
+ /**
46
+ * String schema represents a string that is not stringified JSON. Using
47
+ * String schema for strings stored in SQLite is crucial to ensure a stored
48
+ * string is not automatically parsed to a JSON object or array when
49
+ * retrieved. Use String schema for all string-based schemas.
50
+ */
51
+ export const String = Schema.string.pipe(Schema.filter((s) => {
52
+ if (!maybeJson(s))
53
+ return true;
54
+ try {
55
+ JSON.parse(s);
56
+ }
57
+ catch (e) {
58
+ return true;
59
+ }
60
+ return false;
61
+ }, { message: () => "a string that is not stringified JSON" }), Schema.brand("String"));
62
+ /**
63
+ * A string with a maximum length of 1000 characters.
64
+ *
65
+ * ### Example
66
+ *
67
+ * ```
68
+ * import * as Schema from "@effect/schema/Schema";
69
+ * import * as Evolu from "@evolu/react";
70
+ *
71
+ * if (!Schema.is(Evolu.String1000)(value)) return;
72
+ * function foo(value: Evolu.String1000) {}
73
+ * ```
74
+ */
75
+ export const String1000 = String.pipe(Schema.maxLength(1000), Schema.brand("String1000"));
76
+ /**
77
+ * A nonempty string with a maximum length of 1000 characters.
78
+ *
79
+ * ### Example
80
+ *
81
+ * ```
82
+ * import * as Schema from "@effect/schema/Schema";
83
+ * import * as Evolu from "@evolu/react";
84
+ *
85
+ * if (!Schema.is(Evolu.NonEmptyString1000)(value)) return;
86
+ * function foo(value: Evolu.NonEmptyString1000) {}
87
+ * ```
88
+ */
89
+ export const NonEmptyString1000 = String.pipe(Schema.minLength(1), Schema.maxLength(1000), Schema.brand("NonEmptyString1000"));
90
+ /**
91
+ * A positive integer.
92
+ *
93
+ * ### Example
94
+ *
95
+ * ```
96
+ * import * as Schema from "@effect/schema/Schema";
97
+ * import * as Evolu from "@evolu/react";
98
+ *
99
+ * if (!Schema.is(Evolu.PositiveInt)(value)) return;
100
+ * function foo(value: Evolu.PositiveInt) {}
101
+ * ```
102
+ */
103
+ export const PositiveInt = Schema.number.pipe(Schema.int(), Schema.positive(), Schema.brand("PositiveInt"));
@@ -0,0 +1,2 @@
1
+ export declare const murmurhash: (key: string, seed?: number) => number;
2
+ //# sourceMappingURL=Murmurhash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Murmurhash.d.ts","sourceRoot":"","sources":["../../src/Murmurhash.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,UAAU,QAAS,MAAM,oBAAa,MAoElD,CAAC"}
@@ -0,0 +1,60 @@
1
+ // https://github.com/garycourt/murmurhash-js/blob/master/murmurhash3_gc.js
2
+ export const murmurhash = (key, seed = 0) => {
3
+ let h1, h1b, k1, i;
4
+ const remainder = key.length & 3; // key.length % 4
5
+ const bytes = key.length - remainder;
6
+ h1 = seed;
7
+ const c1 = 0xcc9e2d51;
8
+ const c2 = 0x1b873593;
9
+ i = 0;
10
+ while (i < bytes) {
11
+ k1 =
12
+ (key.charCodeAt(i) & 0xff) |
13
+ ((key.charCodeAt(++i) & 0xff) << 8) |
14
+ ((key.charCodeAt(++i) & 0xff) << 16) |
15
+ ((key.charCodeAt(++i) & 0xff) << 24);
16
+ ++i;
17
+ k1 =
18
+ ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
19
+ k1 = (k1 << 15) | (k1 >>> 17);
20
+ k1 =
21
+ ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
22
+ h1 ^= k1;
23
+ h1 = (h1 << 13) | (h1 >>> 19);
24
+ h1b =
25
+ ((h1 & 0xffff) * 5 + ((((h1 >>> 16) * 5) & 0xffff) << 16)) & 0xffffffff;
26
+ h1 = (h1b & 0xffff) + 0x6b64 + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16);
27
+ }
28
+ k1 = 0;
29
+ switch (remainder) {
30
+ case 3:
31
+ k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
32
+ // eslint-disable-next-line no-fallthrough
33
+ case 2:
34
+ k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
35
+ // eslint-disable-next-line no-fallthrough
36
+ case 1:
37
+ k1 ^= key.charCodeAt(i) & 0xff;
38
+ k1 =
39
+ ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) &
40
+ 0xffffffff;
41
+ k1 = (k1 << 15) | (k1 >>> 17);
42
+ k1 =
43
+ ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) &
44
+ 0xffffffff;
45
+ h1 ^= k1;
46
+ }
47
+ h1 ^= key.length;
48
+ h1 ^= h1 >>> 16;
49
+ h1 =
50
+ ((h1 & 0xffff) * 0x85ebca6b +
51
+ ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) &
52
+ 0xffffffff;
53
+ h1 ^= h1 >>> 13;
54
+ h1 =
55
+ ((h1 & 0xffff) * 0xc2b2ae35 +
56
+ ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16)) &
57
+ 0xffffffff;
58
+ h1 ^= h1 >>> 16;
59
+ return h1 >>> 0;
60
+ };
@@ -0,0 +1,29 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ export interface Platform {
3
+ readonly name: "server" | "web-with-opfs" | "web-without-opfs" | "react-native";
4
+ }
5
+ export declare const Platform: Context.Tag<Platform, Platform>;
6
+ export type FlushSync = (callback: () => void) => void;
7
+ export declare const FlushSync: Context.Tag<FlushSync, FlushSync>;
8
+ export interface SyncLock {
9
+ readonly acquire: Effect.Effect<never, never, boolean>;
10
+ readonly release: Effect.Effect<never, never, void>;
11
+ }
12
+ export declare const SyncLock: Context.Tag<SyncLock, SyncLock>;
13
+ export type Fetch = (url: string, body: Uint8Array) => Effect.Effect<never, FetchError, Response>;
14
+ export declare const Fetch: Context.Tag<Fetch, Fetch>;
15
+ /**
16
+ * This error occurs when there is a problem with the network connection,
17
+ * or the server cannot be reached.
18
+ */
19
+ export interface FetchError {
20
+ readonly _tag: "FetchError";
21
+ }
22
+ export declare const FetchLive: Layer.Layer<never, never, Fetch>;
23
+ export interface AppState {
24
+ readonly onFocus: (callback: () => void) => void;
25
+ readonly onReconnect: (callback: () => void) => void;
26
+ readonly reset: Effect.Effect<never, never, void>;
27
+ }
28
+ export declare const AppState: Context.Tag<AppState, AppState>;
29
+ //# sourceMappingURL=Platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Platform.d.ts","sourceRoot":"","sources":["../../src/Platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEhD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EACT,QAAQ,GACR,eAAe,GACf,kBAAkB,GAClB,cAAc,CAAC;CACpB;AAED,eAAO,MAAM,QAAQ,iCAA0C,CAAC;AAEhE,MAAM,MAAM,SAAS,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;AAEvD,eAAO,MAAM,SAAS,mCAA4C,CAAC;AAEnE,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;CACrD;AAED,eAAO,MAAM,QAAQ,iCAA0C,CAAC;AAEhE,MAAM,MAAM,KAAK,GAAG,CAClB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,UAAU,KACb,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;AAEhD,eAAO,MAAM,KAAK,2BAAoC,CAAC;AAEvD;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;CAC7B;AAED,eAAO,MAAM,SAAS,kCAgBrB,CAAC;AAEF,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACjD,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;CACnD;AAED,eAAO,MAAM,QAAQ,iCAA0C,CAAC"}