@evolu/common 6.0.1-preview.3 → 6.0.1-preview.31
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/Array.d.ts +69 -5
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +64 -5
- package/dist/src/Assert.d.ts +6 -16
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +6 -18
- 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 +8 -7
- package/dist/src/Cache.d.ts +44 -0
- package/dist/src/Cache.d.ts.map +1 -0
- package/dist/src/Cache.js +52 -0
- package/dist/src/Callbacks.d.ts +45 -12
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Callbacks.js +14 -7
- 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 +32 -45
- package/dist/src/Evolu/Db.d.ts +161 -65
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +286 -694
- 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 +208 -133
- package/dist/src/Evolu/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu/Evolu.js +188 -183
- 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 +150 -0
- package/dist/src/Evolu/LocalAuth.d.ts.map +1 -0
- package/dist/src/Evolu/LocalAuth.js +174 -0
- package/dist/src/Evolu/Owner.d.ts +273 -120
- package/dist/src/Evolu/Owner.d.ts.map +1 -1
- package/dist/src/Evolu/Owner.js +130 -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 +277 -232
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +603 -378
- 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 +92 -7
- package/dist/src/Evolu/Relay.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.js +238 -76
- 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 +240 -26
- package/dist/src/Evolu/Storage.d.ts.map +1 -1
- package/dist/src/Evolu/Storage.js +189 -91
- package/dist/src/Evolu/Sync.d.ts +67 -13
- package/dist/src/Evolu/Sync.d.ts.map +1 -1
- package/dist/src/Evolu/Sync.js +441 -20
- package/dist/src/Evolu/Timestamp.d.ts +85 -27
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.js +77 -18
- package/dist/src/Identicon.d.ts +35 -0
- package/dist/src/Identicon.d.ts.map +1 -0
- package/dist/src/Identicon.js +143 -0
- package/dist/src/Instances.d.ts +34 -0
- package/dist/src/Instances.d.ts.map +1 -0
- package/dist/src/Instances.js +44 -0
- package/dist/src/ManyToManyMap.d.ts +71 -10
- package/dist/src/ManyToManyMap.d.ts.map +1 -1
- package/dist/src/ManyToManyMap.js +41 -6
- package/dist/src/Number.d.ts +4 -3
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +5 -4
- package/dist/src/Platform.d.ts +20 -0
- package/dist/src/Platform.d.ts.map +1 -0
- package/dist/src/Platform.js +22 -0
- package/dist/src/Random.d.ts +3 -2
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Resources.d.ts +118 -0
- package/dist/src/Resources.d.ts.map +1 -0
- package/dist/src/Resources.js +197 -0
- package/dist/src/Result.d.ts +184 -52
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +30 -241
- package/dist/src/Skiplist.js +2 -1
- package/dist/src/Sqlite.d.ts +63 -5
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +110 -9
- package/dist/src/Task.d.ts +586 -0
- package/dist/src/Task.d.ts.map +1 -0
- package/dist/src/Task.js +469 -0
- package/dist/src/Time.d.ts +66 -1
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +99 -5
- package/dist/src/Type.d.ts +622 -340
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +666 -464
- package/dist/src/Types.d.ts +1 -75
- 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 -18
- 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 +7 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -2
- package/package.json +14 -13
- package/src/Array.ts +90 -11
- package/src/Assert.ts +6 -24
- package/src/Brand.ts +75 -0
- package/src/Buffer.ts +7 -7
- package/src/Cache.ts +85 -0
- package/src/Callbacks.ts +62 -22
- package/src/Console.ts +91 -11
- package/src/Crypto.ts +97 -82
- package/src/Evolu/Db.ts +517 -1020
- package/src/Evolu/Diff.ts +7 -5
- package/src/Evolu/Evolu.ts +464 -355
- package/src/Evolu/Internal.ts +0 -2
- package/src/Evolu/LocalAuth.ts +463 -0
- package/src/Evolu/Owner.ts +355 -228
- package/src/Evolu/Platform.ts +9 -9
- package/src/Evolu/Protocol.ts +859 -676
- 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 +437 -93
- package/src/Evolu/Schema.ts +391 -191
- package/src/Evolu/Storage.ts +532 -135
- package/src/Evolu/Sync.ts +766 -37
- package/src/Evolu/Timestamp.ts +88 -35
- package/src/Identicon.ts +197 -0
- package/src/Instances.ts +90 -0
- package/src/ManyToManyMap.ts +124 -24
- package/src/Number.ts +6 -10
- package/src/Platform.ts +26 -0
- package/src/Random.ts +3 -2
- package/src/Resources.ts +367 -0
- package/src/Result.ts +191 -54
- package/src/Skiplist.ts +1 -1
- package/src/Sqlite.ts +122 -17
- package/src/Task.ts +901 -0
- package/src/Time.ts +180 -5
- package/src/Type.ts +1084 -727
- package/src/Types.ts +1 -77
- package/src/WebSocket.ts +27 -25
- package/src/Worker.ts +72 -23
- package/src/index.ts +7 -2
- 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/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,38 +1,30 @@
|
|
|
1
|
+
import { pack } from "msgpackr";
|
|
1
2
|
import { isNonEmptyArray, isNonEmptyReadonlyArray } from "../Array.js";
|
|
2
|
-
import { assertNonEmptyArray } from "../Assert.js";
|
|
3
|
+
import { assert, assertNonEmptyArray } from "../Assert.js";
|
|
3
4
|
import { createCallbacks } from "../Callbacks.js";
|
|
5
|
+
import { eqArrayNumber } from "../Eq.js";
|
|
4
6
|
import { exhaustiveCheck } from "../Function.js";
|
|
7
|
+
import { createInstances } from "../Instances.js";
|
|
5
8
|
import { err, ok } from "../Result.js";
|
|
6
9
|
import { isSqlMutation } from "../Sqlite.js";
|
|
7
10
|
import { createStore } from "../Store.js";
|
|
8
11
|
import { createId, } from "../Type.js";
|
|
9
|
-
import {
|
|
12
|
+
import { defaultDbConfig } from "./Db.js";
|
|
10
13
|
import { applyPatches } from "./Diff.js";
|
|
11
|
-
import { kysely } from "./Kysely.js";
|
|
12
14
|
import { createSubscribedQueries, emptyRows, serializeQuery, } from "./Query.js";
|
|
13
|
-
import {
|
|
15
|
+
import { evoluSchemaToDbSchema, insertable, kysely, updateable, upsertable, } from "./Schema.js";
|
|
16
|
+
import { DbChange } from "./Storage.js";
|
|
14
17
|
import { initialSyncState } from "./Sync.js";
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
const evoluInstances = createInstances();
|
|
19
|
+
/**
|
|
20
|
+
* Unique identifier for the current browser tab or app instance, lazily
|
|
21
|
+
* initialized on first use to distinguish between multiple tabs.
|
|
22
|
+
*/
|
|
17
23
|
let tabId = null;
|
|
18
24
|
/**
|
|
19
|
-
* Creates an {@link Evolu} instance configured with the specified
|
|
20
|
-
* {@link EvoluSchema} and optional
|
|
21
|
-
*
|
|
22
|
-
* This function returns a configured Evolu instance, providing a typed
|
|
23
|
-
* interface for querying, mutating, and syncing your application's data. The
|
|
24
|
-
* returned instance includes:
|
|
25
|
-
*
|
|
26
|
-
* - Subscription methods for receiving updates on queries, the owner, errors, and
|
|
27
|
-
* sync state.
|
|
28
|
-
* - Methods for creating, updating, or deleting rows in a type-safe manner.
|
|
29
|
-
* - Methods for querying data using Evolu's typed SQL queries, leveraging Kysely
|
|
30
|
-
* under the hood.
|
|
31
|
-
* - Built-in support for local-first and offline-first data with automatic sync
|
|
32
|
-
* and merging.
|
|
33
|
-
* - Automatic schema evolution that updates the underlying database with new
|
|
34
|
-
* columns or tables.
|
|
35
|
-
* - Managing owner data with resetAppOwner and restoreAppOwner.
|
|
25
|
+
* Creates an {@link Evolu} instance for a platform configured with the specified
|
|
26
|
+
* {@link EvoluSchema} and optional {@link EvoluConfig} providing a typed
|
|
27
|
+
* interface for querying, mutating, and syncing your application's data.
|
|
36
28
|
*
|
|
37
29
|
* ### Example
|
|
38
30
|
*
|
|
@@ -61,62 +53,66 @@ let tabId = null;
|
|
|
61
53
|
*
|
|
62
54
|
* const evolu = createEvolu(evoluReactDeps)(Schema);
|
|
63
55
|
* ```
|
|
56
|
+
*
|
|
57
|
+
* ### Instance Caching
|
|
58
|
+
*
|
|
59
|
+
* `createEvolu` caches instances using {@link Instances} by {@link EvoluConfig}
|
|
60
|
+
* name to enable hot reloading and prevent database corruption from multiple
|
|
61
|
+
* connections. For testing, use unique instance names to ensure proper
|
|
62
|
+
* isolation.
|
|
64
63
|
*/
|
|
65
|
-
export const createEvolu = (deps) => (
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// Hot reloading. Note that indexes are intentionally omitted.
|
|
77
|
-
evolu.ensureSchema(schema);
|
|
78
|
-
}
|
|
79
|
-
return evolu;
|
|
80
|
-
};
|
|
81
|
-
const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
82
|
-
deps.console.enabled = evoluConfig.enableLogging ?? false;
|
|
83
|
-
deps.console.log("[evolu]", "createEvoluInstance");
|
|
84
|
-
const { initialData, indexes, ...config } = evoluConfig;
|
|
64
|
+
export const createEvolu = (deps) => (schema, config) => evoluInstances.ensure(config?.name ?? defaultDbConfig.name, () => createEvoluInstance(deps)(schema, config), (evolu) => {
|
|
65
|
+
// Hot reloading. Note that indexes are intentionally omitted.
|
|
66
|
+
evolu.ensureSchema(schema);
|
|
67
|
+
});
|
|
68
|
+
const createEvoluInstance = (deps) => (schema, config) => {
|
|
69
|
+
deps.console.enabled = config?.enableLogging ?? false;
|
|
70
|
+
const { indexes, reloadUrl = "/", ...partialDbConfig } = config ?? {};
|
|
71
|
+
const dbConfig = { ...defaultDbConfig, ...partialDbConfig };
|
|
72
|
+
deps.console.log("[evolu]", "createEvoluInstance", {
|
|
73
|
+
name: dbConfig.name,
|
|
74
|
+
});
|
|
85
75
|
const errorStore = createStore(null);
|
|
86
76
|
const rowsStore = createStore(new Map());
|
|
87
|
-
const
|
|
88
|
-
|
|
77
|
+
const { promise: appOwner, resolve: resolveAppOwner } = Promise.withResolvers();
|
|
78
|
+
if (config?.externalAppOwner) {
|
|
79
|
+
resolveAppOwner(config.externalAppOwner);
|
|
80
|
+
}
|
|
81
|
+
// TODO: Update it for the owner-api
|
|
82
|
+
const _syncStore = createStore(initialSyncState);
|
|
89
83
|
const subscribedQueries = createSubscribedQueries(rowsStore);
|
|
90
84
|
const loadingPromises = createLoadingPromises(subscribedQueries);
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const dbWorker = deps.createDbWorker(
|
|
85
|
+
const onCompleteCallbacks = createCallbacks(deps);
|
|
86
|
+
const exportCallbacks = createCallbacks(deps);
|
|
87
|
+
const dbWorker = deps.createDbWorker(dbConfig.name);
|
|
94
88
|
const getTabId = () => {
|
|
95
89
|
tabId ??= createId(deps);
|
|
96
90
|
return tabId;
|
|
97
91
|
};
|
|
92
|
+
// Worker responses are delivered to all tabs. Each case must handle this
|
|
93
|
+
// properly (e.g., AppOwner promise resolves only once, tabId filtering).
|
|
98
94
|
dbWorker.onMessage((message) => {
|
|
99
95
|
switch (message.type) {
|
|
100
|
-
case "onInit": {
|
|
101
|
-
ownerStore.set(message.owner);
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
96
|
case "onError": {
|
|
105
97
|
errorStore.set(message.error);
|
|
106
98
|
break;
|
|
107
99
|
}
|
|
108
|
-
case "
|
|
100
|
+
case "onGetAppOwner": {
|
|
101
|
+
resolveAppOwner(message.appOwner);
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case "onQueryPatches": {
|
|
109
105
|
if (message.tabId !== getTabId())
|
|
110
106
|
return;
|
|
111
107
|
const state = rowsStore.get();
|
|
112
108
|
const nextState = new Map([
|
|
113
109
|
...state,
|
|
114
|
-
...message.
|
|
110
|
+
...message.queryPatches.map(({ query, patches }) => [
|
|
115
111
|
query,
|
|
116
112
|
applyPatches(patches, state.get(query) ?? emptyRows),
|
|
117
113
|
]),
|
|
118
114
|
]);
|
|
119
|
-
for (const { query } of message.
|
|
115
|
+
for (const { query } of message.queryPatches) {
|
|
120
116
|
loadingPromises.resolve(query, nextState.get(query) ?? emptyRows);
|
|
121
117
|
}
|
|
122
118
|
if (deps.flushSync && message.onCompleteIds.length > 0) {
|
|
@@ -128,15 +124,19 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
128
124
|
rowsStore.set(nextState);
|
|
129
125
|
}
|
|
130
126
|
for (const id of message.onCompleteIds) {
|
|
131
|
-
|
|
127
|
+
onCompleteCallbacks.execute(id);
|
|
132
128
|
}
|
|
133
129
|
break;
|
|
134
130
|
}
|
|
135
|
-
case "
|
|
131
|
+
case "refreshQueries": {
|
|
136
132
|
if (message.tabId && message.tabId === getTabId())
|
|
137
133
|
return;
|
|
138
|
-
loadingPromises.
|
|
139
|
-
|
|
134
|
+
const loadingPromisesQueries = loadingPromises.getQueries();
|
|
135
|
+
loadingPromises.releaseUnsubscribedOnMutation();
|
|
136
|
+
const queries = [
|
|
137
|
+
// Dedupe
|
|
138
|
+
...new Set([...loadingPromisesQueries, ...subscribedQueries.get()]),
|
|
139
|
+
];
|
|
140
140
|
if (isNonEmptyReadonlyArray(queries)) {
|
|
141
141
|
dbWorker.postMessage({ type: "query", tabId: getTabId(), queries });
|
|
142
142
|
}
|
|
@@ -144,22 +144,22 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
144
144
|
}
|
|
145
145
|
case "onReset": {
|
|
146
146
|
if (message.reload) {
|
|
147
|
-
|
|
147
|
+
deps.reloadApp(reloadUrl);
|
|
148
148
|
}
|
|
149
149
|
else {
|
|
150
|
-
|
|
150
|
+
onCompleteCallbacks.execute(message.onCompleteId);
|
|
151
151
|
}
|
|
152
152
|
break;
|
|
153
153
|
}
|
|
154
154
|
case "onExport": {
|
|
155
|
-
|
|
155
|
+
exportCallbacks.execute(message.onCompleteId, message.file);
|
|
156
156
|
break;
|
|
157
157
|
}
|
|
158
158
|
default:
|
|
159
159
|
exhaustiveCheck(message);
|
|
160
160
|
}
|
|
161
161
|
});
|
|
162
|
-
const dbSchema =
|
|
162
|
+
const dbSchema = evoluSchemaToDbSchema(schema, indexes);
|
|
163
163
|
const mutationTypesCache = new Map();
|
|
164
164
|
// Lazy create mutation Types like this: `insertable(Schema.todo)`
|
|
165
165
|
const getMutationType = (table, kind) => {
|
|
@@ -175,103 +175,76 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
175
175
|
}
|
|
176
176
|
return type;
|
|
177
177
|
};
|
|
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
|
-
});
|
|
178
|
+
dbWorker.postMessage({ type: "init", config: dbConfig, dbSchema });
|
|
179
|
+
// We can't use `init` to get AppOwner because `init` runs only once per n tabs.
|
|
180
|
+
dbWorker.postMessage({ type: "getAppOwner" });
|
|
208
181
|
const loadQueryMicrotaskQueue = [];
|
|
209
182
|
const mutateMicrotaskQueue = [];
|
|
183
|
+
const useOwnerMicrotaskQueue = [];
|
|
210
184
|
const createMutation = (kind) => (table, props, options) => {
|
|
211
|
-
const
|
|
212
|
-
const result = Type.fromUnknown(props);
|
|
185
|
+
const result = getMutationType(table, kind).fromUnknown(props);
|
|
213
186
|
const id = kind === "insert"
|
|
214
187
|
? createId(deps)
|
|
215
188
|
: props.id;
|
|
216
189
|
if (options?.onlyValidate !== true) {
|
|
217
190
|
if (!result.ok) {
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
mutateMicrotaskQueue.push([undefined, undefined]);
|
|
191
|
+
// Mark the transaction as invalid by pushing null
|
|
192
|
+
mutateMicrotaskQueue.push([null, undefined]);
|
|
221
193
|
}
|
|
222
194
|
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;
|
|
195
|
+
const values = { ...result.value };
|
|
196
|
+
delete values.id;
|
|
197
|
+
if (kind === "insert" || kind === "upsert") {
|
|
198
|
+
// Only set createdAt if not provided by user
|
|
199
|
+
if (!("createdAt" in values)) {
|
|
200
|
+
values.createdAt = new Date(deps.time.now()).toISOString();
|
|
245
201
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
});
|
|
256
|
-
});
|
|
202
|
+
}
|
|
203
|
+
const dbChange = { table, id, values };
|
|
204
|
+
assert(DbChange.is(dbChange), `Failed to create DbChange for table "${dbChange.table}"`);
|
|
205
|
+
const mutationChange = { ...dbChange, ownerId: options?.ownerId };
|
|
206
|
+
mutateMicrotaskQueue.push([mutationChange, options?.onComplete]);
|
|
207
|
+
}
|
|
208
|
+
if (mutateMicrotaskQueue.length === 1) {
|
|
209
|
+
queueMicrotask(processMutationQueue);
|
|
210
|
+
}
|
|
257
211
|
}
|
|
258
212
|
if (result.ok)
|
|
259
213
|
return ok({ id });
|
|
260
214
|
return err(result.error);
|
|
261
215
|
};
|
|
216
|
+
const processMutationQueue = () => {
|
|
217
|
+
const changes = [];
|
|
218
|
+
const onCompletes = [];
|
|
219
|
+
for (const [change, onComplete] of mutateMicrotaskQueue) {
|
|
220
|
+
if (change !== null)
|
|
221
|
+
changes.push(change);
|
|
222
|
+
if (onComplete)
|
|
223
|
+
onCompletes.push(onComplete);
|
|
224
|
+
}
|
|
225
|
+
const queueLength = mutateMicrotaskQueue.length;
|
|
226
|
+
mutateMicrotaskQueue.length = 0;
|
|
227
|
+
// Don't process any mutations if there was a validation error.
|
|
228
|
+
// All mutations within a queue run as a single transaction.
|
|
229
|
+
if (changes.length !== queueLength) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const onCompleteIds = onCompletes.map(onCompleteCallbacks.register);
|
|
233
|
+
loadingPromises.releaseUnsubscribedOnMutation();
|
|
234
|
+
if (!isNonEmptyArray(changes))
|
|
235
|
+
return;
|
|
236
|
+
dbWorker.postMessage({
|
|
237
|
+
type: "mutate",
|
|
238
|
+
tabId: getTabId(),
|
|
239
|
+
changes,
|
|
240
|
+
onCompleteIds,
|
|
241
|
+
subscribedQueries: subscribedQueries.get(),
|
|
242
|
+
});
|
|
243
|
+
};
|
|
262
244
|
const evolu = {
|
|
263
245
|
subscribeError: errorStore.subscribe,
|
|
264
246
|
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
|
-
},
|
|
247
|
+
createQuery,
|
|
275
248
|
loadQuery: (query) => {
|
|
276
249
|
const { promise, isNew } = loadingPromises.get(query);
|
|
277
250
|
if (isNew) {
|
|
@@ -282,6 +255,7 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
282
255
|
const queries = [...new Set(loadQueryMicrotaskQueue)];
|
|
283
256
|
loadQueryMicrotaskQueue.length = 0;
|
|
284
257
|
assertNonEmptyArray(queries);
|
|
258
|
+
deps.console.log("[evolu]", "loadQuery", { queries });
|
|
285
259
|
dbWorker.postMessage({
|
|
286
260
|
type: "query",
|
|
287
261
|
tabId: getTabId(),
|
|
@@ -309,20 +283,16 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
309
283
|
};
|
|
310
284
|
},
|
|
311
285
|
getQueryRows: (query) => (rowsStore.get().get(query) ?? emptyRows),
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
subscribeSyncState: syncStore.subscribe,
|
|
315
|
-
getSyncState: syncStore.get,
|
|
286
|
+
appOwner,
|
|
287
|
+
// TODO: Update it for the owner-api
|
|
288
|
+
// subscribeSyncState: syncStore.subscribe,
|
|
289
|
+
// getSyncState: syncStore.get,
|
|
316
290
|
insert: createMutation("insert"),
|
|
317
291
|
update: createMutation("update"),
|
|
318
292
|
upsert: createMutation("upsert"),
|
|
319
293
|
resetAppOwner: (options) => {
|
|
320
|
-
// Eslint bug, Promise<void> is correct by docs.
|
|
321
|
-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
322
294
|
const { promise, resolve } = Promise.withResolvers();
|
|
323
|
-
const onCompleteId =
|
|
324
|
-
resolve();
|
|
325
|
-
});
|
|
295
|
+
const onCompleteId = onCompleteCallbacks.register(resolve);
|
|
326
296
|
dbWorker.postMessage({
|
|
327
297
|
type: "reset",
|
|
328
298
|
onCompleteId,
|
|
@@ -331,12 +301,8 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
331
301
|
return promise;
|
|
332
302
|
},
|
|
333
303
|
restoreAppOwner: (mnemonic, options) => {
|
|
334
|
-
// Eslint bug, Promise<void> is correct by docs.
|
|
335
|
-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
336
304
|
const { promise, resolve } = Promise.withResolvers();
|
|
337
|
-
const onCompleteId =
|
|
338
|
-
resolve();
|
|
339
|
-
});
|
|
305
|
+
const onCompleteId = onCompleteCallbacks.register(resolve);
|
|
340
306
|
dbWorker.postMessage({
|
|
341
307
|
type: "reset",
|
|
342
308
|
onCompleteId,
|
|
@@ -346,32 +312,81 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
346
312
|
return promise;
|
|
347
313
|
},
|
|
348
314
|
reloadApp: () => {
|
|
349
|
-
|
|
315
|
+
deps.reloadApp(reloadUrl);
|
|
350
316
|
},
|
|
351
317
|
ensureSchema: (schema) => {
|
|
352
318
|
mutationTypesCache.clear();
|
|
353
|
-
const
|
|
354
|
-
dbWorker.postMessage({
|
|
355
|
-
type: "ensureDbSchema",
|
|
356
|
-
dbSchema: validEvoluSchemaToDbSchema(validSchema),
|
|
357
|
-
});
|
|
319
|
+
const dbSchema = evoluSchemaToDbSchema(schema);
|
|
320
|
+
dbWorker.postMessage({ type: "ensureDbSchema", dbSchema });
|
|
358
321
|
},
|
|
359
322
|
exportDatabase: () => {
|
|
360
323
|
const { promise, resolve } = Promise.withResolvers();
|
|
361
|
-
const onCompleteId =
|
|
362
|
-
if (arg instanceof Uint8Array)
|
|
363
|
-
resolve(arg);
|
|
364
|
-
});
|
|
324
|
+
const onCompleteId = exportCallbacks.register(resolve);
|
|
365
325
|
dbWorker.postMessage({ type: "export", onCompleteId });
|
|
366
326
|
return promise;
|
|
367
327
|
},
|
|
328
|
+
useOwner: (owner) => {
|
|
329
|
+
const scheduleOwnerQueueProcessing = () => {
|
|
330
|
+
if (useOwnerMicrotaskQueue.length !== 1)
|
|
331
|
+
return;
|
|
332
|
+
queueMicrotask(() => {
|
|
333
|
+
const queue = [...useOwnerMicrotaskQueue];
|
|
334
|
+
useOwnerMicrotaskQueue.length = 0;
|
|
335
|
+
const result = [];
|
|
336
|
+
const skipIndices = new Set();
|
|
337
|
+
for (let i = 0; i < queue.length; i++) {
|
|
338
|
+
if (skipIndices.has(i))
|
|
339
|
+
continue;
|
|
340
|
+
const [currentOwner, currentUse, currentOwnerSerialized] = queue[i];
|
|
341
|
+
// Look for opposite action with same owner
|
|
342
|
+
for (let j = i + 1; j < queue.length; j++) {
|
|
343
|
+
if (skipIndices.has(j))
|
|
344
|
+
continue;
|
|
345
|
+
const [, otherUse, otherOwnerSerialized] = queue[j];
|
|
346
|
+
if (currentUse !== otherUse &&
|
|
347
|
+
eqArrayNumber(currentOwnerSerialized, otherOwnerSerialized)) {
|
|
348
|
+
// Found cancel-out pair, skip both
|
|
349
|
+
skipIndices.add(i).add(j);
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (!skipIndices.has(i)) {
|
|
354
|
+
result.push([currentOwner, currentUse, currentOwnerSerialized]);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
for (const [owner, use] of result) {
|
|
358
|
+
dbWorker.postMessage({ type: "useOwner", owner, use });
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
};
|
|
362
|
+
useOwnerMicrotaskQueue.push([owner, true, pack(owner)]);
|
|
363
|
+
scheduleOwnerQueueProcessing();
|
|
364
|
+
const unuse = () => {
|
|
365
|
+
useOwnerMicrotaskQueue.push([owner, false, pack(owner)]);
|
|
366
|
+
scheduleOwnerQueueProcessing();
|
|
367
|
+
};
|
|
368
|
+
return unuse;
|
|
369
|
+
},
|
|
370
|
+
/** Disposal is not implemented yet. */
|
|
371
|
+
[Symbol.dispose]: () => {
|
|
372
|
+
throw new Error("Evolu instance disposal is not yet implemented");
|
|
373
|
+
},
|
|
368
374
|
};
|
|
369
375
|
return evolu;
|
|
370
376
|
};
|
|
371
|
-
export const
|
|
377
|
+
export const createQuery = (queryCallback, options) => {
|
|
378
|
+
const compiledQuery = queryCallback(kysely).compile();
|
|
379
|
+
if (isSqlMutation(compiledQuery.sql))
|
|
380
|
+
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.");
|
|
381
|
+
return serializeQuery({
|
|
382
|
+
sql: compiledQuery.sql,
|
|
383
|
+
parameters: compiledQuery.parameters,
|
|
384
|
+
...(options && { options }),
|
|
385
|
+
});
|
|
386
|
+
};
|
|
372
387
|
const createLoadingPromises = (subscribedQueries) => {
|
|
373
388
|
const loadingPromiseMap = new Map();
|
|
374
|
-
|
|
389
|
+
return {
|
|
375
390
|
get: (query) => {
|
|
376
391
|
let loadingPromise = loadingPromiseMap.get(query);
|
|
377
392
|
const isNew = !loadingPromise;
|
|
@@ -393,15 +408,12 @@ const createLoadingPromises = (subscribedQueries) => {
|
|
|
393
408
|
loadingPromise.resolve(rows);
|
|
394
409
|
}
|
|
395
410
|
else {
|
|
396
|
-
// A promise can't be fulfilled 2x, so we need a new one.
|
|
397
411
|
loadingPromise.promise = Promise.resolve(rows);
|
|
398
412
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
* https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md
|
|
404
|
-
*/
|
|
413
|
+
// Set status and value fields for React's `use` Hook to unwrap synchronously.
|
|
414
|
+
// While undocumented in React docs, React still uses these properties internally,
|
|
415
|
+
// and Evolu's own promise caching logic depends on checking `promise.status`.
|
|
416
|
+
// https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md
|
|
405
417
|
void Object.assign(loadingPromise.promise, {
|
|
406
418
|
status: "fulfilled",
|
|
407
419
|
value: rows,
|
|
@@ -410,14 +422,7 @@ const createLoadingPromises = (subscribedQueries) => {
|
|
|
410
422
|
loadingPromiseMap.delete(query);
|
|
411
423
|
}
|
|
412
424
|
},
|
|
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: () => {
|
|
425
|
+
releaseUnsubscribedOnMutation: () => {
|
|
421
426
|
[...loadingPromiseMap.entries()]
|
|
422
427
|
.filter(([query]) => !subscribedQueries.has(query))
|
|
423
428
|
.forEach(([query, loadingPromise]) => {
|
|
@@ -429,6 +434,6 @@ const createLoadingPromises = (subscribedQueries) => {
|
|
|
429
434
|
}
|
|
430
435
|
});
|
|
431
436
|
},
|
|
437
|
+
getQueries: () => Array.from(loadingPromiseMap.keys()),
|
|
432
438
|
};
|
|
433
|
-
return loadingPromises;
|
|
434
439
|
};
|
|
@@ -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";
|