@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.
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/dist/src/Config.d.ts +20 -0
- package/dist/src/Config.d.ts.map +1 -0
- package/dist/src/Config.js +8 -0
- package/dist/src/Crypto.d.ts +42 -0
- package/dist/src/Crypto.d.ts.map +1 -0
- package/dist/src/Crypto.js +44 -0
- package/dist/src/Db.d.ts +60 -0
- package/dist/src/Db.d.ts.map +1 -0
- package/dist/src/Db.js +118 -0
- package/dist/src/DbWorker.d.ts +78 -0
- package/dist/src/DbWorker.d.ts.map +1 -0
- package/dist/src/DbWorker.js +282 -0
- package/dist/src/Diff.d.ts +19 -0
- package/dist/src/Diff.d.ts.map +1 -0
- package/dist/src/Diff.js +86 -0
- package/dist/src/Errors.d.ts +19 -0
- package/dist/src/Errors.d.ts.map +1 -0
- package/dist/src/Errors.js +11 -0
- package/dist/src/Evolu.d.ts +71 -0
- package/dist/src/Evolu.d.ts.map +1 -0
- package/dist/src/Evolu.js +235 -0
- package/dist/src/MerkleTree.d.ts +15 -0
- package/dist/src/MerkleTree.d.ts.map +1 -0
- package/dist/src/MerkleTree.js +49 -0
- package/dist/src/Model.d.ts +143 -0
- package/dist/src/Model.d.ts.map +1 -0
- package/dist/src/Model.js +103 -0
- package/dist/src/Murmurhash.d.ts +2 -0
- package/dist/src/Murmurhash.d.ts.map +1 -0
- package/dist/src/Murmurhash.js +60 -0
- package/dist/src/Platform.d.ts +29 -0
- package/dist/src/Platform.d.ts.map +1 -0
- package/dist/src/Platform.js +17 -0
- package/dist/src/Protobuf.d.ts +130 -0
- package/dist/src/Protobuf.d.ts.map +1 -0
- package/dist/src/Protobuf.js +100 -0
- package/dist/src/Public.d.ts +7 -0
- package/dist/src/Public.d.ts.map +1 -0
- package/dist/src/Public.js +2 -0
- package/dist/src/Sql.d.ts +12 -0
- package/dist/src/Sql.d.ts.map +1 -0
- package/dist/src/Sql.js +100 -0
- package/dist/src/Sqlite.d.ts +30 -0
- package/dist/src/Sqlite.d.ts.map +1 -0
- package/dist/src/Sqlite.js +41 -0
- package/dist/src/Store.d.ts +9 -0
- package/dist/src/Store.d.ts.map +1 -0
- package/dist/src/Store.js +18 -0
- package/dist/src/SyncWorker.d.ts +73 -0
- package/dist/src/SyncWorker.d.ts.map +1 -0
- package/dist/src/SyncWorker.js +136 -0
- package/dist/src/Timestamp.d.ts +44 -0
- package/dist/src/Timestamp.d.ts.map +1 -0
- package/dist/src/Timestamp.js +76 -0
- package/dist/src/index.d.ts +19 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +18 -0
- package/package.json +76 -0
- package/src/Config.ts +33 -0
- package/src/Crypto.ts +113 -0
- package/src/Db.ts +323 -0
- package/src/DbWorker.ts +683 -0
- package/src/Diff.ts +113 -0
- package/src/Errors.ts +33 -0
- package/src/Evolu.ts +527 -0
- package/src/MerkleTree.ts +83 -0
- package/src/Model.ts +212 -0
- package/src/Murmurhash.ts +70 -0
- package/src/Platform.ts +63 -0
- package/src/Protobuf.ts +204 -0
- package/src/Public.ts +6 -0
- package/src/Sql.ts +114 -0
- package/src/Sqlite.ts +92 -0
- package/src/Store.ts +32 -0
- package/src/SyncWorker.ts +344 -0
- package/src/Timestamp.ts +192 -0
- 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 @@
|
|
|
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"}
|