@evolu/common 6.0.1-preview.2 → 6.0.1-preview.21
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/Assert.d.ts +6 -3
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +7 -4
- package/dist/src/Brand.d.ts +75 -0
- package/dist/src/Brand.d.ts.map +1 -0
- package/dist/src/Brand.js +1 -0
- package/dist/src/Buffer.d.ts +1 -1
- package/dist/src/Buffer.d.ts.map +1 -1
- package/dist/src/Buffer.js +1 -1
- package/dist/src/CallbackRegistry.d.ts +53 -0
- package/dist/src/CallbackRegistry.d.ts.map +1 -0
- package/dist/src/CallbackRegistry.js +25 -0
- package/dist/src/Console.d.ts +31 -6
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +72 -9
- package/dist/src/Crypto.d.ts +61 -34
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +29 -42
- package/dist/src/Evolu/Db.d.ts +159 -66
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +300 -705
- package/dist/src/Evolu/Diff.d.ts +3 -3
- package/dist/src/Evolu/Diff.d.ts.map +1 -1
- package/dist/src/Evolu/Diff.js +7 -5
- package/dist/src/Evolu/Evolu.d.ts +200 -115
- package/dist/src/Evolu/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu/Evolu.js +287 -157
- package/dist/src/Evolu/Internal.d.ts +0 -2
- package/dist/src/Evolu/Internal.d.ts.map +1 -1
- package/dist/src/Evolu/Internal.js +0 -2
- package/dist/src/Evolu/LocalAuth.d.ts +144 -0
- package/dist/src/Evolu/LocalAuth.d.ts.map +1 -0
- package/dist/src/Evolu/LocalAuth.js +171 -0
- package/dist/src/Evolu/Owner.d.ts +209 -124
- package/dist/src/Evolu/Owner.d.ts.map +1 -1
- package/dist/src/Evolu/Owner.js +118 -104
- package/dist/src/Evolu/Platform.d.ts +9 -7
- package/dist/src/Evolu/Platform.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.d.ts +256 -211
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +558 -359
- package/dist/src/Evolu/Public.d.ts +6 -8
- package/dist/src/Evolu/Public.d.ts.map +1 -1
- package/dist/src/Evolu/Public.js +2 -3
- package/dist/src/Evolu/PublicKysely.js +3 -3
- package/dist/src/Evolu/Query.d.ts +2 -1
- package/dist/src/Evolu/Query.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.d.ts +72 -4
- package/dist/src/Evolu/Relay.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.js +124 -13
- package/dist/src/Evolu/Schema.d.ts +129 -73
- package/dist/src/Evolu/Schema.d.ts.map +1 -1
- package/dist/src/Evolu/Schema.js +169 -89
- package/dist/src/Evolu/Storage.d.ts +159 -14
- package/dist/src/Evolu/Storage.d.ts.map +1 -1
- package/dist/src/Evolu/Storage.js +42 -32
- package/dist/src/Evolu/Sync.d.ts +68 -13
- package/dist/src/Evolu/Sync.d.ts.map +1 -1
- package/dist/src/Evolu/Sync.js +414 -20
- package/dist/src/Evolu/Timestamp.d.ts +35 -27
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.js +27 -18
- package/dist/src/ManyToManyMap.d.ts +74 -10
- package/dist/src/ManyToManyMap.d.ts.map +1 -1
- package/dist/src/ManyToManyMap.js +41 -6
- package/dist/src/Number.d.ts +2 -1
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Random.d.ts +3 -2
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/RefCountedResourceManager.d.ts +119 -0
- package/dist/src/RefCountedResourceManager.d.ts.map +1 -0
- package/dist/src/RefCountedResourceManager.js +197 -0
- package/dist/src/Result.d.ts +176 -51
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +30 -241
- package/dist/src/Sqlite.d.ts +21 -4
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +50 -8
- package/dist/src/Task.d.ts +511 -0
- package/dist/src/Task.d.ts.map +1 -0
- package/dist/src/Task.js +410 -0
- package/dist/src/Time.d.ts +59 -0
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +87 -4
- package/dist/src/Type.d.ts +474 -327
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +502 -436
- package/dist/src/Types.d.ts +0 -74
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/WebSocket.d.ts +5 -2
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +12 -13
- package/dist/src/Worker.d.ts +39 -11
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +22 -4
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -3
- package/package.json +14 -13
- package/src/Assert.ts +8 -7
- package/src/Brand.ts +75 -0
- package/src/Buffer.ts +1 -1
- package/src/CallbackRegistry.ts +84 -0
- package/src/Console.ts +91 -11
- package/src/Crypto.ts +94 -79
- package/src/Evolu/Db.ts +528 -1027
- package/src/Evolu/Diff.ts +7 -5
- package/src/Evolu/Evolu.ts +682 -329
- package/src/Evolu/Internal.ts +0 -2
- package/src/Evolu/LocalAuth.ts +422 -0
- package/src/Evolu/Owner.ts +282 -227
- package/src/Evolu/Platform.ts +9 -9
- package/src/Evolu/Protocol.ts +788 -640
- package/src/Evolu/Public.ts +7 -14
- package/src/Evolu/PublicKysely.ts +3 -3
- package/src/Evolu/Query.ts +2 -1
- package/src/Evolu/Relay.ts +238 -18
- package/src/Evolu/Schema.ts +391 -191
- package/src/Evolu/Storage.ts +275 -55
- package/src/Evolu/Sync.ts +708 -37
- package/src/Evolu/Timestamp.ts +38 -35
- package/src/ManyToManyMap.ts +127 -24
- package/src/Number.ts +2 -6
- package/src/Random.ts +3 -2
- package/src/RefCountedResourceManager.ts +368 -0
- package/src/Result.ts +183 -53
- package/src/Sqlite.ts +59 -23
- package/src/Task.ts +779 -0
- package/src/Time.ts +168 -4
- package/src/Type.ts +708 -660
- package/src/Types.ts +0 -76
- package/src/WebSocket.ts +23 -17
- package/src/Worker.ts +72 -23
- package/src/index.ts +3 -3
- package/dist/src/Callbacks.d.ts +0 -20
- package/dist/src/Callbacks.d.ts.map +0 -1
- package/dist/src/Callbacks.js +0 -18
- package/dist/src/Evolu/Config.d.ts +0 -69
- package/dist/src/Evolu/Config.d.ts.map +0 -1
- package/dist/src/Evolu/Config.js +0 -9
- package/dist/src/Evolu/Kysely.d.ts +0 -6
- package/dist/src/Evolu/Kysely.d.ts.map +0 -1
- package/dist/src/Evolu/Kysely.js +0 -21
- package/dist/src/NanoId.d.ts +0 -27
- package/dist/src/NanoId.d.ts.map +0 -1
- package/dist/src/NanoId.js +0 -6
- package/dist/src/Promise.d.ts +0 -180
- package/dist/src/Promise.d.ts.map +0 -1
- package/dist/src/Promise.js +0 -176
- package/src/Callbacks.ts +0 -43
- package/src/Evolu/Config.ts +0 -83
- package/src/Evolu/Kysely.ts +0 -38
- package/src/NanoId.ts +0 -39
- package/src/Promise.ts +0 -295
package/dist/src/Evolu/Evolu.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
import { pack } from "msgpackr";
|
|
1
2
|
import { isNonEmptyArray, isNonEmptyReadonlyArray } from "../Array.js";
|
|
2
|
-
import { assertNonEmptyArray } from "../Assert.js";
|
|
3
|
-
import {
|
|
3
|
+
import { assert, assertNonEmptyArray } from "../Assert.js";
|
|
4
|
+
import { createCallbackRegistry } from "../CallbackRegistry.js";
|
|
5
|
+
import { eqArrayNumber } from "../Eq.js";
|
|
4
6
|
import { exhaustiveCheck } from "../Function.js";
|
|
5
7
|
import { err, ok } from "../Result.js";
|
|
6
8
|
import { isSqlMutation } from "../Sqlite.js";
|
|
7
9
|
import { createStore } from "../Store.js";
|
|
8
10
|
import { createId, } from "../Type.js";
|
|
9
|
-
import {
|
|
11
|
+
import { defaultDbConfig } from "./Db.js";
|
|
10
12
|
import { applyPatches } from "./Diff.js";
|
|
11
|
-
import { kysely } from "./Kysely.js";
|
|
12
13
|
import { createSubscribedQueries, emptyRows, serializeQuery, } from "./Query.js";
|
|
13
|
-
import {
|
|
14
|
+
import { evoluSchemaToDbSchema, insertable, kysely, updateable, upsertable, } from "./Schema.js";
|
|
15
|
+
import { DbChange } from "./Storage.js";
|
|
14
16
|
import { initialSyncState } from "./Sync.js";
|
|
15
17
|
// For hot reloading and Evolu multitenancy.
|
|
16
18
|
const evoluInstances = new Map();
|
|
@@ -32,7 +34,8 @@ let tabId = null;
|
|
|
32
34
|
* and merging.
|
|
33
35
|
* - Automatic schema evolution that updates the underlying database with new
|
|
34
36
|
* columns or tables.
|
|
35
|
-
* - Managing owner data with resetAppOwner and
|
|
37
|
+
* - Managing owner data with {@link Evolu#resetAppOwner} and
|
|
38
|
+
* {@link Evolu#restoreAppOwner}.
|
|
36
39
|
*
|
|
37
40
|
* ### Example
|
|
38
41
|
*
|
|
@@ -62,15 +65,12 @@ let tabId = null;
|
|
|
62
65
|
* const evolu = createEvolu(evoluReactDeps)(Schema);
|
|
63
66
|
* ```
|
|
64
67
|
*/
|
|
65
|
-
export const createEvolu = (deps) => (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
schema, partialConfig = {}) => {
|
|
69
|
-
const config = { ...defaultConfig, ...partialConfig };
|
|
70
|
-
let evolu = evoluInstances.get(config.name);
|
|
68
|
+
export const createEvolu = (deps) => (schema, config) => {
|
|
69
|
+
const name = config?.name ?? defaultDbConfig.name;
|
|
70
|
+
let evolu = evoluInstances.get(name);
|
|
71
71
|
if (evolu == null) {
|
|
72
72
|
evolu = createEvoluInstance(deps)(schema, config);
|
|
73
|
-
evoluInstances.set(
|
|
73
|
+
evoluInstances.set(name, evolu);
|
|
74
74
|
}
|
|
75
75
|
else {
|
|
76
76
|
// Hot reloading. Note that indexes are intentionally omitted.
|
|
@@ -78,45 +78,87 @@ schema, partialConfig = {}) => {
|
|
|
78
78
|
}
|
|
79
79
|
return evolu;
|
|
80
80
|
};
|
|
81
|
-
const createEvoluInstance = (deps) => (schema,
|
|
82
|
-
deps.console.enabled =
|
|
83
|
-
|
|
84
|
-
const {
|
|
81
|
+
const createEvoluInstance = (deps) => (schema, config) => {
|
|
82
|
+
deps.console.enabled = config?.enableLogging ?? false;
|
|
83
|
+
const { indexes, reloadUrl = "/", ...partialDbConfig } = config ?? {};
|
|
84
|
+
const dbConfig = { ...defaultDbConfig, ...partialDbConfig };
|
|
85
|
+
deps.console.log("[evolu]", "createEvoluInstance", {
|
|
86
|
+
name: dbConfig.name,
|
|
87
|
+
});
|
|
85
88
|
const errorStore = createStore(null);
|
|
86
89
|
const rowsStore = createStore(new Map());
|
|
87
|
-
const
|
|
88
|
-
|
|
90
|
+
const { promise: appOwner, resolve: resolveAppOwner } = Promise.withResolvers();
|
|
91
|
+
if (config?.externalAppOwner) {
|
|
92
|
+
resolveAppOwner(config.externalAppOwner);
|
|
93
|
+
}
|
|
94
|
+
// TODO: Update it for the owner-api
|
|
95
|
+
const _syncStore = createStore(initialSyncState);
|
|
89
96
|
const subscribedQueries = createSubscribedQueries(rowsStore);
|
|
90
97
|
const loadingPromises = createLoadingPromises(subscribedQueries);
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const dbWorker = deps.createDbWorker(
|
|
98
|
+
const onCompleteRegistry = createCallbackRegistry(deps);
|
|
99
|
+
const exportRegistry = createCallbackRegistry(deps);
|
|
100
|
+
const dbWorker = deps.createDbWorker(dbConfig.name);
|
|
94
101
|
const getTabId = () => {
|
|
95
102
|
tabId ??= createId(deps);
|
|
96
103
|
return tabId;
|
|
97
104
|
};
|
|
105
|
+
// const createLocalOnly = (
|
|
106
|
+
// localMutations: Array<MutationChange>,
|
|
107
|
+
// defaultOwnerId: OwnerId | undefined,
|
|
108
|
+
// ): LocalOnly<EvoluSchema> => ({
|
|
109
|
+
// insert: (table, values) => {
|
|
110
|
+
// const id = createId(deps);
|
|
111
|
+
// localMutations.push({
|
|
112
|
+
// table,
|
|
113
|
+
// id,
|
|
114
|
+
// values,
|
|
115
|
+
// ownerId: defaultOwnerId,
|
|
116
|
+
// });
|
|
117
|
+
// return id;
|
|
118
|
+
// },
|
|
119
|
+
// update: (table, values) => {
|
|
120
|
+
// const { id, ...rest } = values;
|
|
121
|
+
// localMutations.push({
|
|
122
|
+
// table,
|
|
123
|
+
// id: id as Id,
|
|
124
|
+
// values: rest,
|
|
125
|
+
// ownerId: defaultOwnerId,
|
|
126
|
+
// });
|
|
127
|
+
// },
|
|
128
|
+
// upsert: (table, values) => {
|
|
129
|
+
// const { id, ...rest } = values as Record<string, unknown> & { id: Id };
|
|
130
|
+
// localMutations.push({
|
|
131
|
+
// table,
|
|
132
|
+
// id: id,
|
|
133
|
+
// values: rest as MutationChange["values"],
|
|
134
|
+
// ownerId: defaultOwnerId,
|
|
135
|
+
// });
|
|
136
|
+
// },
|
|
137
|
+
// });
|
|
138
|
+
// Worker responses are delivered to all tabs. Each case must handle this
|
|
139
|
+
// properly (e.g., AppOwner promise resolves only once, tabId filtering).
|
|
98
140
|
dbWorker.onMessage((message) => {
|
|
99
141
|
switch (message.type) {
|
|
100
|
-
case "onInit": {
|
|
101
|
-
ownerStore.set(message.owner);
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
142
|
case "onError": {
|
|
105
143
|
errorStore.set(message.error);
|
|
106
144
|
break;
|
|
107
145
|
}
|
|
108
|
-
case "
|
|
146
|
+
case "onGetAppOwner": {
|
|
147
|
+
resolveAppOwner(message.appOwner);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case "onQueryPatches": {
|
|
109
151
|
if (message.tabId !== getTabId())
|
|
110
152
|
return;
|
|
111
153
|
const state = rowsStore.get();
|
|
112
154
|
const nextState = new Map([
|
|
113
155
|
...state,
|
|
114
|
-
...message.
|
|
156
|
+
...message.queryPatches.map(({ query, patches }) => [
|
|
115
157
|
query,
|
|
116
158
|
applyPatches(patches, state.get(query) ?? emptyRows),
|
|
117
159
|
]),
|
|
118
160
|
]);
|
|
119
|
-
for (const { query } of message.
|
|
161
|
+
for (const { query } of message.queryPatches) {
|
|
120
162
|
loadingPromises.resolve(query, nextState.get(query) ?? emptyRows);
|
|
121
163
|
}
|
|
122
164
|
if (deps.flushSync && message.onCompleteIds.length > 0) {
|
|
@@ -128,15 +170,19 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
128
170
|
rowsStore.set(nextState);
|
|
129
171
|
}
|
|
130
172
|
for (const id of message.onCompleteIds) {
|
|
131
|
-
|
|
173
|
+
onCompleteRegistry.execute(id);
|
|
132
174
|
}
|
|
133
175
|
break;
|
|
134
176
|
}
|
|
135
|
-
case "
|
|
177
|
+
case "refreshQueries": {
|
|
136
178
|
if (message.tabId && message.tabId === getTabId())
|
|
137
179
|
return;
|
|
138
|
-
loadingPromises.
|
|
139
|
-
|
|
180
|
+
const loadingPromisesQueries = loadingPromises.getQueries();
|
|
181
|
+
loadingPromises.releaseUnsubscribedOnMutation();
|
|
182
|
+
const queries = [
|
|
183
|
+
// Dedupe
|
|
184
|
+
...new Set([...loadingPromisesQueries, ...subscribedQueries.get()]),
|
|
185
|
+
];
|
|
140
186
|
if (isNonEmptyReadonlyArray(queries)) {
|
|
141
187
|
dbWorker.postMessage({ type: "query", tabId: getTabId(), queries });
|
|
142
188
|
}
|
|
@@ -144,22 +190,82 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
144
190
|
}
|
|
145
191
|
case "onReset": {
|
|
146
192
|
if (message.reload) {
|
|
147
|
-
|
|
193
|
+
deps.reloadApp(reloadUrl);
|
|
148
194
|
}
|
|
149
195
|
else {
|
|
150
|
-
|
|
196
|
+
onCompleteRegistry.execute(message.onCompleteId);
|
|
151
197
|
}
|
|
152
198
|
break;
|
|
153
199
|
}
|
|
200
|
+
// case "processNewMessages": {
|
|
201
|
+
// void requestIdleTask(
|
|
202
|
+
// toTask(async () => {
|
|
203
|
+
// const approved: Array<Timestamp> = [];
|
|
204
|
+
// const invalidChanges: Array<DbChange> = [];
|
|
205
|
+
// const rejectedChanges: Array<DbChange> = [];
|
|
206
|
+
// const localMutations: Array<MutationChange> = [];
|
|
207
|
+
// for (const crdtMessage of message.messages) {
|
|
208
|
+
// let isApproved = true;
|
|
209
|
+
// let isValid = true;
|
|
210
|
+
// const table = crdtMessage.change.table;
|
|
211
|
+
// if (table in schema) {
|
|
212
|
+
// const { createdAt, ...values } = crdtMessage.change.values;
|
|
213
|
+
// isValid =
|
|
214
|
+
// (createdAt ? DateIso.is(createdAt) : true) &&
|
|
215
|
+
// getMutationType(table, "update").is({
|
|
216
|
+
// id: crdtMessage.change.id,
|
|
217
|
+
// ...values,
|
|
218
|
+
// });
|
|
219
|
+
// } else {
|
|
220
|
+
// isValid = false;
|
|
221
|
+
// }
|
|
222
|
+
// if (!isValid) {
|
|
223
|
+
// isApproved = false;
|
|
224
|
+
// invalidChanges.push(crdtMessage.change);
|
|
225
|
+
// } else if (onMessage) {
|
|
226
|
+
// // At this point, we've validated that the message conforms to the
|
|
227
|
+
// // schema, so the typed callback can safely process it.
|
|
228
|
+
// isApproved = await onMessage(crdtMessage.change, {
|
|
229
|
+
// ownerId: message.ownerId,
|
|
230
|
+
// localOnly: createLocalOnly(localMutations, message.ownerId),
|
|
231
|
+
// });
|
|
232
|
+
// if (!isApproved) {
|
|
233
|
+
// rejectedChanges.push(crdtMessage.change);
|
|
234
|
+
// }
|
|
235
|
+
// }
|
|
236
|
+
// if (isApproved) {
|
|
237
|
+
// approved.push(crdtMessage.timestamp);
|
|
238
|
+
// }
|
|
239
|
+
// }
|
|
240
|
+
// // Report OnMessageError if there were any invalid or rejected changes
|
|
241
|
+
// if (invalidChanges.length > 0 || rejectedChanges.length > 0) {
|
|
242
|
+
// const onMessageError: OnMessageError = {
|
|
243
|
+
// type: "OnMessageError",
|
|
244
|
+
// invalidChanges,
|
|
245
|
+
// rejectedChanges,
|
|
246
|
+
// };
|
|
247
|
+
// errorStore.set(onMessageError);
|
|
248
|
+
// }
|
|
249
|
+
// dbWorker.postMessage({
|
|
250
|
+
// type: "onProcessNewMessages",
|
|
251
|
+
// onCompleteId: message.onCompleteId,
|
|
252
|
+
// approved,
|
|
253
|
+
// localMutations,
|
|
254
|
+
// });
|
|
255
|
+
// return ok();
|
|
256
|
+
// }),
|
|
257
|
+
// )();
|
|
258
|
+
// break;
|
|
259
|
+
// }
|
|
154
260
|
case "onExport": {
|
|
155
|
-
|
|
261
|
+
exportRegistry.execute(message.onCompleteId, message.file);
|
|
156
262
|
break;
|
|
157
263
|
}
|
|
158
264
|
default:
|
|
159
265
|
exhaustiveCheck(message);
|
|
160
266
|
}
|
|
161
267
|
});
|
|
162
|
-
const dbSchema =
|
|
268
|
+
const dbSchema = evoluSchemaToDbSchema(schema, indexes);
|
|
163
269
|
const mutationTypesCache = new Map();
|
|
164
270
|
// Lazy create mutation Types like this: `insertable(Schema.todo)`
|
|
165
271
|
const getMutationType = (table, kind) => {
|
|
@@ -175,103 +281,99 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
175
281
|
}
|
|
176
282
|
return type;
|
|
177
283
|
};
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
* (initial data are already in the DB) because we don't want to wait for
|
|
182
|
-
* SQLite's response. Initial data should be small (because they are inlined
|
|
183
|
-
* in the code), so it's ok.
|
|
184
|
-
*/
|
|
185
|
-
if (initialData)
|
|
186
|
-
initialData({
|
|
187
|
-
insert: (table, props) => {
|
|
188
|
-
const Type = getMutationType(table, "insert");
|
|
189
|
-
const id = createId(deps);
|
|
190
|
-
const result = Type.fromUnknown(props);
|
|
191
|
-
if (result.ok) {
|
|
192
|
-
initialDataDbChanges.push({
|
|
193
|
-
id,
|
|
194
|
-
table,
|
|
195
|
-
values: result.value,
|
|
196
|
-
});
|
|
197
|
-
return ok({ id });
|
|
198
|
-
}
|
|
199
|
-
return result;
|
|
200
|
-
},
|
|
201
|
-
});
|
|
202
|
-
dbWorker.postMessage({
|
|
203
|
-
type: "init",
|
|
204
|
-
config,
|
|
205
|
-
dbSchema,
|
|
206
|
-
initialData: initialDataDbChanges,
|
|
207
|
-
});
|
|
284
|
+
dbWorker.postMessage({ type: "init", config: dbConfig, dbSchema });
|
|
285
|
+
// We can't use `init` to get AppOwner because `init` runs only once per n tabs.
|
|
286
|
+
dbWorker.postMessage({ type: "getAppOwner" });
|
|
208
287
|
const loadQueryMicrotaskQueue = [];
|
|
209
288
|
const mutateMicrotaskQueue = [];
|
|
289
|
+
const useOwnerMicrotaskQueue = [];
|
|
210
290
|
const createMutation = (kind) => (table, props, options) => {
|
|
211
|
-
const
|
|
212
|
-
const result = Type.fromUnknown(props);
|
|
291
|
+
const result = getMutationType(table, kind).fromUnknown(props);
|
|
213
292
|
const id = kind === "insert"
|
|
214
293
|
? createId(deps)
|
|
215
294
|
: props.id;
|
|
216
295
|
if (options?.onlyValidate !== true) {
|
|
217
296
|
if (!result.ok) {
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
mutateMicrotaskQueue.push([undefined, undefined]);
|
|
297
|
+
// Mark the transaction as invalid by pushing null
|
|
298
|
+
mutateMicrotaskQueue.push([null, undefined]);
|
|
221
299
|
}
|
|
222
300
|
else {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (mutateMicrotaskQueue.length === 1)
|
|
230
|
-
queueMicrotask(() => {
|
|
231
|
-
const changes = [];
|
|
232
|
-
const onCompletes = [];
|
|
233
|
-
for (const [change, onComplete] of mutateMicrotaskQueue) {
|
|
234
|
-
if (change)
|
|
235
|
-
changes.push(change);
|
|
236
|
-
if (onComplete)
|
|
237
|
-
onCompletes.push(onComplete);
|
|
238
|
-
}
|
|
239
|
-
const mutateMicrotaskQueueLength = mutateMicrotaskQueue.length;
|
|
240
|
-
mutateMicrotaskQueue.length = 0;
|
|
241
|
-
// Don't mutate anything if there was a validation error.
|
|
242
|
-
// All mutations within a queue are considered to be a transaction.
|
|
243
|
-
if (changes.length !== mutateMicrotaskQueueLength) {
|
|
244
|
-
return;
|
|
301
|
+
const values = { ...result.value };
|
|
302
|
+
delete values.id;
|
|
303
|
+
if (kind === "insert" || kind === "upsert") {
|
|
304
|
+
// Only set createdAt if not provided by user
|
|
305
|
+
if (!("createdAt" in values)) {
|
|
306
|
+
values.createdAt = new Date(deps.time.now()).toISOString();
|
|
245
307
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
});
|
|
256
|
-
});
|
|
308
|
+
}
|
|
309
|
+
const dbChange = { table, id, values };
|
|
310
|
+
assert(DbChange.is(dbChange), `Failed to create DbChange for table "${dbChange.table}"`);
|
|
311
|
+
const mutationChange = { ...dbChange, ownerId: options?.ownerId };
|
|
312
|
+
mutateMicrotaskQueue.push([mutationChange, options?.onComplete]);
|
|
313
|
+
}
|
|
314
|
+
if (mutateMicrotaskQueue.length === 1) {
|
|
315
|
+
queueMicrotask(processMutationQueue);
|
|
316
|
+
}
|
|
257
317
|
}
|
|
258
318
|
if (result.ok)
|
|
259
319
|
return ok({ id });
|
|
260
320
|
return err(result.error);
|
|
261
321
|
};
|
|
322
|
+
const processMutationQueue = () => {
|
|
323
|
+
const changes = [];
|
|
324
|
+
const onCompleteCallbacks = [];
|
|
325
|
+
for (const [change, onComplete] of mutateMicrotaskQueue) {
|
|
326
|
+
if (change !== null)
|
|
327
|
+
changes.push(change);
|
|
328
|
+
if (onComplete)
|
|
329
|
+
onCompleteCallbacks.push(onComplete);
|
|
330
|
+
}
|
|
331
|
+
const queueLength = mutateMicrotaskQueue.length;
|
|
332
|
+
mutateMicrotaskQueue.length = 0;
|
|
333
|
+
// Don't process any mutations if there was a validation error.
|
|
334
|
+
// All mutations within a queue run as a single transaction.
|
|
335
|
+
if (changes.length !== queueLength) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const onCompleteIds = onCompleteCallbacks.map(onCompleteRegistry.register);
|
|
339
|
+
loadingPromises.releaseUnsubscribedOnMutation();
|
|
340
|
+
if (!isNonEmptyArray(changes))
|
|
341
|
+
return;
|
|
342
|
+
// if (onMessage) {
|
|
343
|
+
// const rejectedChanges: Array<DbChange> = [];
|
|
344
|
+
// const localMutations: Array<MutationChange> = [];
|
|
345
|
+
// for (const change of changes) {
|
|
346
|
+
// const localOnly = createLocalOnly(localMutations, change.ownerId);
|
|
347
|
+
// const isApproved = await onMessage(change, {
|
|
348
|
+
// ownerId: change.ownerId,
|
|
349
|
+
// localOnly,
|
|
350
|
+
// });
|
|
351
|
+
// if (!isApproved) {
|
|
352
|
+
// rejectedChanges.push(change);
|
|
353
|
+
// }
|
|
354
|
+
// }
|
|
355
|
+
// if (rejectedChanges.length > 0) {
|
|
356
|
+
// errorStore.set({
|
|
357
|
+
// type: "OnMessageError",
|
|
358
|
+
// invalidChanges: [],
|
|
359
|
+
// rejectedChanges,
|
|
360
|
+
// });
|
|
361
|
+
// return;
|
|
362
|
+
// }
|
|
363
|
+
// changes.push(...localMutations);
|
|
364
|
+
// }
|
|
365
|
+
dbWorker.postMessage({
|
|
366
|
+
type: "mutate",
|
|
367
|
+
tabId: getTabId(),
|
|
368
|
+
changes,
|
|
369
|
+
onCompleteIds,
|
|
370
|
+
subscribedQueries: subscribedQueries.get(),
|
|
371
|
+
});
|
|
372
|
+
};
|
|
262
373
|
const evolu = {
|
|
263
374
|
subscribeError: errorStore.subscribe,
|
|
264
375
|
getError: errorStore.get,
|
|
265
|
-
createQuery
|
|
266
|
-
const compiledQuery = queryCallback(kysely).compile();
|
|
267
|
-
if (isSqlMutation(compiledQuery.sql))
|
|
268
|
-
throw new Error("SQL mutation (INSERT, UPDATE, DELETE, etc.) isn't allowed in the Evolu `createQuery` function. Kysely suggests it because there is no read-only Kysely yet, and removing such an API is not possible. For mutations, use Evolu Mutation API.");
|
|
269
|
-
return serializeQuery({
|
|
270
|
-
sql: compiledQuery.sql,
|
|
271
|
-
parameters: compiledQuery.parameters,
|
|
272
|
-
...(options && { options }),
|
|
273
|
-
});
|
|
274
|
-
},
|
|
376
|
+
createQuery,
|
|
275
377
|
loadQuery: (query) => {
|
|
276
378
|
const { promise, isNew } = loadingPromises.get(query);
|
|
277
379
|
if (isNew) {
|
|
@@ -282,6 +384,7 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
282
384
|
const queries = [...new Set(loadQueryMicrotaskQueue)];
|
|
283
385
|
loadQueryMicrotaskQueue.length = 0;
|
|
284
386
|
assertNonEmptyArray(queries);
|
|
387
|
+
deps.console.log("[evolu]", "loadQuery", { queries });
|
|
285
388
|
dbWorker.postMessage({
|
|
286
389
|
type: "query",
|
|
287
390
|
tabId: getTabId(),
|
|
@@ -309,20 +412,16 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
309
412
|
};
|
|
310
413
|
},
|
|
311
414
|
getQueryRows: (query) => (rowsStore.get().get(query) ?? emptyRows),
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
subscribeSyncState: syncStore.subscribe,
|
|
315
|
-
getSyncState: syncStore.get,
|
|
415
|
+
appOwner,
|
|
416
|
+
// TODO: Update it for the owner-api
|
|
417
|
+
// subscribeSyncState: syncStore.subscribe,
|
|
418
|
+
// getSyncState: syncStore.get,
|
|
316
419
|
insert: createMutation("insert"),
|
|
317
420
|
update: createMutation("update"),
|
|
318
421
|
upsert: createMutation("upsert"),
|
|
319
422
|
resetAppOwner: (options) => {
|
|
320
|
-
// Eslint bug, Promise<void> is correct by docs.
|
|
321
|
-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
322
423
|
const { promise, resolve } = Promise.withResolvers();
|
|
323
|
-
const onCompleteId =
|
|
324
|
-
resolve();
|
|
325
|
-
});
|
|
424
|
+
const onCompleteId = onCompleteRegistry.register(resolve);
|
|
326
425
|
dbWorker.postMessage({
|
|
327
426
|
type: "reset",
|
|
328
427
|
onCompleteId,
|
|
@@ -331,12 +430,8 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
331
430
|
return promise;
|
|
332
431
|
},
|
|
333
432
|
restoreAppOwner: (mnemonic, options) => {
|
|
334
|
-
// Eslint bug, Promise<void> is correct by docs.
|
|
335
|
-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
336
433
|
const { promise, resolve } = Promise.withResolvers();
|
|
337
|
-
const onCompleteId =
|
|
338
|
-
resolve();
|
|
339
|
-
});
|
|
434
|
+
const onCompleteId = onCompleteRegistry.register(resolve);
|
|
340
435
|
dbWorker.postMessage({
|
|
341
436
|
type: "reset",
|
|
342
437
|
onCompleteId,
|
|
@@ -346,32 +441,77 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
346
441
|
return promise;
|
|
347
442
|
},
|
|
348
443
|
reloadApp: () => {
|
|
349
|
-
|
|
444
|
+
deps.reloadApp(reloadUrl);
|
|
350
445
|
},
|
|
351
446
|
ensureSchema: (schema) => {
|
|
352
447
|
mutationTypesCache.clear();
|
|
353
|
-
const
|
|
354
|
-
dbWorker.postMessage({
|
|
355
|
-
type: "ensureDbSchema",
|
|
356
|
-
dbSchema: validEvoluSchemaToDbSchema(validSchema),
|
|
357
|
-
});
|
|
448
|
+
const dbSchema = evoluSchemaToDbSchema(schema);
|
|
449
|
+
dbWorker.postMessage({ type: "ensureDbSchema", dbSchema });
|
|
358
450
|
},
|
|
359
451
|
exportDatabase: () => {
|
|
360
452
|
const { promise, resolve } = Promise.withResolvers();
|
|
361
|
-
const onCompleteId =
|
|
362
|
-
if (arg instanceof Uint8Array)
|
|
363
|
-
resolve(arg);
|
|
364
|
-
});
|
|
453
|
+
const onCompleteId = exportRegistry.register(resolve);
|
|
365
454
|
dbWorker.postMessage({ type: "export", onCompleteId });
|
|
366
455
|
return promise;
|
|
367
456
|
},
|
|
457
|
+
useOwner: (owner) => {
|
|
458
|
+
const scheduleOwnerQueueProcessing = () => {
|
|
459
|
+
if (useOwnerMicrotaskQueue.length !== 1)
|
|
460
|
+
return;
|
|
461
|
+
queueMicrotask(() => {
|
|
462
|
+
const queue = [...useOwnerMicrotaskQueue];
|
|
463
|
+
useOwnerMicrotaskQueue.length = 0;
|
|
464
|
+
const result = [];
|
|
465
|
+
const skipIndices = new Set();
|
|
466
|
+
for (let i = 0; i < queue.length; i++) {
|
|
467
|
+
if (skipIndices.has(i))
|
|
468
|
+
continue;
|
|
469
|
+
const [currentOwner, currentUse, currentOwnerSerialized] = queue[i];
|
|
470
|
+
// Look for opposite action with same owner
|
|
471
|
+
for (let j = i + 1; j < queue.length; j++) {
|
|
472
|
+
if (skipIndices.has(j))
|
|
473
|
+
continue;
|
|
474
|
+
const [, otherUse, otherOwnerSerialized] = queue[j];
|
|
475
|
+
if (currentUse !== otherUse &&
|
|
476
|
+
eqArrayNumber(currentOwnerSerialized, otherOwnerSerialized)) {
|
|
477
|
+
// Found cancel-out pair, skip both
|
|
478
|
+
skipIndices.add(i).add(j);
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
if (!skipIndices.has(i)) {
|
|
483
|
+
result.push([currentOwner, currentUse, currentOwnerSerialized]);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
for (const [owner, use] of result) {
|
|
487
|
+
dbWorker.postMessage({ type: "useOwner", owner, use });
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
};
|
|
491
|
+
useOwnerMicrotaskQueue.push([owner, true, pack(owner)]);
|
|
492
|
+
scheduleOwnerQueueProcessing();
|
|
493
|
+
const unuse = () => {
|
|
494
|
+
useOwnerMicrotaskQueue.push([owner, false, pack(owner)]);
|
|
495
|
+
scheduleOwnerQueueProcessing();
|
|
496
|
+
};
|
|
497
|
+
return unuse;
|
|
498
|
+
},
|
|
368
499
|
};
|
|
369
500
|
return evolu;
|
|
370
501
|
};
|
|
371
|
-
export const
|
|
502
|
+
export const createQuery = (queryCallback, options) => {
|
|
503
|
+
const compiledQuery = queryCallback(kysely).compile();
|
|
504
|
+
if (isSqlMutation(compiledQuery.sql))
|
|
505
|
+
throw new Error("SQL mutation (INSERT, UPDATE, DELETE, etc.) isn't allowed in the Evolu `createQuery` function. Kysely suggests it because there is no read-only Kysely yet, and removing such an API is not possible. For mutations, use Evolu Mutation API.");
|
|
506
|
+
return serializeQuery({
|
|
507
|
+
sql: compiledQuery.sql,
|
|
508
|
+
parameters: compiledQuery.parameters,
|
|
509
|
+
...(options && { options }),
|
|
510
|
+
});
|
|
511
|
+
};
|
|
372
512
|
const createLoadingPromises = (subscribedQueries) => {
|
|
373
513
|
const loadingPromiseMap = new Map();
|
|
374
|
-
|
|
514
|
+
return {
|
|
375
515
|
get: (query) => {
|
|
376
516
|
let loadingPromise = loadingPromiseMap.get(query);
|
|
377
517
|
const isNew = !loadingPromise;
|
|
@@ -393,15 +533,12 @@ const createLoadingPromises = (subscribedQueries) => {
|
|
|
393
533
|
loadingPromise.resolve(rows);
|
|
394
534
|
}
|
|
395
535
|
else {
|
|
396
|
-
// A promise can't be fulfilled 2x, so we need a new one.
|
|
397
536
|
loadingPromise.promise = Promise.resolve(rows);
|
|
398
537
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
* https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md
|
|
404
|
-
*/
|
|
538
|
+
// Set status and value fields for React's `use` Hook to unwrap synchronously.
|
|
539
|
+
// While undocumented in React docs, React still uses these properties internally,
|
|
540
|
+
// and Evolu's own promise caching logic depends on checking `promise.status`.
|
|
541
|
+
// https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md
|
|
405
542
|
void Object.assign(loadingPromise.promise, {
|
|
406
543
|
status: "fulfilled",
|
|
407
544
|
value: rows,
|
|
@@ -410,14 +547,7 @@ const createLoadingPromises = (subscribedQueries) => {
|
|
|
410
547
|
loadingPromiseMap.delete(query);
|
|
411
548
|
}
|
|
412
549
|
},
|
|
413
|
-
|
|
414
|
-
* We can't delete loading promises in `resolveLoadingPromises` because they
|
|
415
|
-
* must be cached, so repeated calls to `loadQuery` will always return the
|
|
416
|
-
* same promise until the data changes, and we also can't cache them forever
|
|
417
|
-
* because only subscribed queries are automatically updated (reactivity is
|
|
418
|
-
* expensive) hence this function must be called manually on any mutation.
|
|
419
|
-
*/
|
|
420
|
-
releaseUnsubscribed: () => {
|
|
550
|
+
releaseUnsubscribedOnMutation: () => {
|
|
421
551
|
[...loadingPromiseMap.entries()]
|
|
422
552
|
.filter(([query]) => !subscribedQueries.has(query))
|
|
423
553
|
.forEach(([query, loadingPromise]) => {
|
|
@@ -429,6 +559,6 @@ const createLoadingPromises = (subscribedQueries) => {
|
|
|
429
559
|
}
|
|
430
560
|
});
|
|
431
561
|
},
|
|
562
|
+
getQueries: () => Array.from(loadingPromiseMap.keys()),
|
|
432
563
|
};
|
|
433
|
-
return loadingPromises;
|
|
434
564
|
};
|
|
@@ -9,11 +9,9 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module
|
|
11
11
|
*/
|
|
12
|
-
export * from "./Config.js";
|
|
13
12
|
export * from "./Db.js";
|
|
14
13
|
export * from "./Diff.js";
|
|
15
14
|
export * from "./Evolu.js";
|
|
16
|
-
export * from "./Kysely.js";
|
|
17
15
|
export * from "./Owner.js";
|
|
18
16
|
export * from "./Platform.js";
|
|
19
17
|
export * from "./Protocol.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Internal.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,
|
|
1
|
+
{"version":3,"file":"Internal.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC"}
|
|
@@ -9,11 +9,9 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module
|
|
11
11
|
*/
|
|
12
|
-
export * from "./Config.js";
|
|
13
12
|
export * from "./Db.js";
|
|
14
13
|
export * from "./Diff.js";
|
|
15
14
|
export * from "./Evolu.js";
|
|
16
|
-
export * from "./Kysely.js";
|
|
17
15
|
export * from "./Owner.js";
|
|
18
16
|
export * from "./Platform.js";
|
|
19
17
|
export * from "./Protocol.js";
|