@evolu/common 6.0.1-preview.9 → 7.1.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/README.md +27 -29
- package/dist/src/Array.d.ts +317 -12
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +219 -9
- package/dist/src/Assert.d.ts +0 -13
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +0 -15
- 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 +39 -0
- package/dist/src/Cache.d.ts.map +1 -0
- package/dist/src/Cache.js +47 -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 +32 -7
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +73 -10
- package/dist/src/Crypto.d.ts +56 -42
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +45 -54
- package/dist/src/Evolu/Db.d.ts +162 -74
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +284 -702
- 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 +213 -133
- package/dist/src/Evolu/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu/Evolu.js +189 -180
- package/dist/src/Evolu/Internal.d.ts +2 -4
- package/dist/src/Evolu/Internal.d.ts.map +1 -1
- package/dist/src/Evolu/Internal.js +2 -4
- 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 +268 -240
- package/dist/src/Evolu/Protocol.d.ts.map +1 -1
- package/dist/src/Evolu/Protocol.js +595 -447
- package/dist/src/Evolu/Public.d.ts +7 -9
- package/dist/src/Evolu/Public.d.ts.map +1 -1
- package/dist/src/Evolu/Public.js +3 -4
- package/dist/src/Evolu/PublicKysely.d.ts.map +1 -1
- package/dist/src/Evolu/PublicKysely.js +3 -4
- package/dist/src/Evolu/Query.d.ts +2 -1
- package/dist/src/Evolu/Query.d.ts.map +1 -1
- package/dist/src/Evolu/Query.js +1 -1
- package/dist/src/Evolu/Relay.d.ts +91 -8
- package/dist/src/Evolu/Relay.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.js +214 -88
- package/dist/src/Evolu/Schema.d.ts +125 -47
- package/dist/src/Evolu/Schema.d.ts.map +1 -1
- package/dist/src/Evolu/Schema.js +175 -31
- package/dist/src/Evolu/Storage.d.ts +249 -27
- package/dist/src/Evolu/Storage.d.ts.map +1 -1
- package/dist/src/Evolu/Storage.js +198 -92
- 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 +469 -20
- package/dist/src/Evolu/Timestamp.d.ts +83 -30
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.js +79 -34
- 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/Number.d.ts +4 -3
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +5 -4
- package/dist/src/Object.d.ts +10 -4
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +9 -3
- package/dist/src/Order.d.ts +0 -5
- package/dist/src/Order.d.ts.map +1 -1
- package/dist/src/Order.js +0 -5
- package/dist/src/Platform.d.ts +15 -0
- package/dist/src/Platform.d.ts.map +1 -0
- package/dist/src/Platform.js +17 -0
- package/dist/src/Random.d.ts +3 -7
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +0 -5
- package/dist/src/Ref.d.ts.map +1 -1
- package/dist/src/Relation.d.ts +79 -0
- package/dist/src/Relation.d.ts.map +1 -0
- package/dist/src/Relation.js +127 -0
- 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 +189 -59
- 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 +89 -5
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +136 -9
- package/dist/src/Store.d.ts +0 -5
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +0 -5
- package/dist/src/Task.d.ts +580 -0
- package/dist/src/Task.d.ts.map +1 -0
- package/dist/src/Task.js +464 -0
- package/dist/src/Time.d.ts +66 -6
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +96 -7
- package/dist/src/Type.d.ts +677 -344
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +718 -467
- package/dist/src/Types.d.ts +38 -75
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/WebSocket.d.ts +5 -7
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +12 -23
- package/dist/src/Worker.d.ts +39 -16
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +22 -9
- package/dist/src/index.d.ts +8 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +8 -3
- package/package.json +14 -13
- package/src/Array.ts +396 -19
- package/src/Assert.ts +0 -35
- package/src/Brand.ts +75 -0
- package/src/Buffer.ts +7 -7
- package/src/Cache.ts +79 -0
- package/src/Callbacks.ts +62 -22
- package/src/Console.ts +92 -12
- package/src/Crypto.ts +99 -91
- package/src/Evolu/Db.ts +519 -1026
- package/src/Evolu/Diff.ts +7 -5
- package/src/Evolu/Evolu.ts +476 -360
- package/src/Evolu/Internal.ts +2 -4
- 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 +846 -742
- package/src/Evolu/Public.ts +8 -15
- package/src/Evolu/PublicKysely.ts +4 -5
- package/src/Evolu/Query.ts +3 -2
- package/src/Evolu/Relay.ts +406 -103
- package/src/Evolu/Schema.ts +323 -91
- package/src/Evolu/Storage.ts +559 -137
- package/src/Evolu/Sync.ts +819 -36
- package/src/Evolu/Timestamp.ts +90 -58
- package/src/Identicon.ts +197 -0
- package/src/Instances.ts +90 -0
- package/src/Number.ts +6 -10
- package/src/Object.ts +13 -5
- package/src/Order.ts +0 -6
- package/src/Platform.ts +20 -0
- package/src/Random.ts +3 -8
- package/src/Ref.ts +0 -5
- package/src/Relation.ts +234 -0
- package/src/Resources.ts +367 -0
- package/src/Result.ts +196 -61
- package/src/Skiplist.ts +1 -1
- package/src/Sqlite.ts +152 -17
- package/src/Store.ts +0 -5
- package/src/Task.ts +894 -0
- package/src/Time.ts +179 -10
- package/src/Type.ts +1136 -731
- package/src/Types.ts +43 -77
- package/src/WebSocket.ts +27 -31
- package/src/Worker.ts +72 -29
- package/src/index.ts +8 -3
- 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/ManyToManyMap.d.ts +0 -26
- package/dist/src/ManyToManyMap.d.ts.map +0 -1
- package/dist/src/ManyToManyMap.js +0 -92
- 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/ManyToManyMap.ts +0 -140
- package/src/NanoId.ts +0 -39
- package/src/Promise.ts +0 -295
package/dist/src/Evolu/Evolu.js
CHANGED
|
@@ -1,39 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { pack } from "msgpackr";
|
|
2
|
+
import { dedupeArray, isNonEmptyArray, isNonEmptyReadonlyArray, } from "../Array.js";
|
|
3
|
+
import { assertNonEmptyReadonlyArray } 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
|
-
import { isSqlMutation } from "../Sqlite.js";
|
|
9
|
+
import { isSqlMutation, SqliteBoolean, } 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
|
-
import { DbChange, } from "./Protocol.js";
|
|
13
14
|
import { createSubscribedQueries, emptyRows, serializeQuery, } from "./Query.js";
|
|
14
|
-
import { evoluSchemaToDbSchema, insertable, updateable, upsertable, } from "./Schema.js";
|
|
15
|
+
import { evoluSchemaToDbSchema, insertable, kysely, updateable, upsertable, } from "./Schema.js";
|
|
16
|
+
import { DbChange } from "./Storage.js";
|
|
15
17
|
import { initialSyncState } from "./Sync.js";
|
|
16
|
-
|
|
17
|
-
|
|
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
|
+
*/
|
|
18
23
|
let tabId = null;
|
|
19
24
|
/**
|
|
20
|
-
* Creates an {@link Evolu} instance configured with the specified
|
|
21
|
-
* {@link EvoluSchema} and optional
|
|
22
|
-
*
|
|
23
|
-
* This function returns a configured Evolu instance, providing a typed
|
|
24
|
-
* interface for querying, mutating, and syncing your application's data. The
|
|
25
|
-
* returned instance includes:
|
|
26
|
-
*
|
|
27
|
-
* - Subscription methods for receiving updates on queries, the owner, errors, and
|
|
28
|
-
* sync state.
|
|
29
|
-
* - Methods for creating, updating, or deleting rows in a type-safe manner.
|
|
30
|
-
* - Methods for querying data using Evolu's typed SQL queries, leveraging Kysely
|
|
31
|
-
* under the hood.
|
|
32
|
-
* - Built-in support for local-first and offline-first data with automatic sync
|
|
33
|
-
* and merging.
|
|
34
|
-
* - Automatic schema evolution that updates the underlying database with new
|
|
35
|
-
* columns or tables.
|
|
36
|
-
* - 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.
|
|
37
28
|
*
|
|
38
29
|
* ### Example
|
|
39
30
|
*
|
|
@@ -62,59 +53,66 @@ let tabId = null;
|
|
|
62
53
|
*
|
|
63
54
|
* const evolu = createEvolu(evoluReactDeps)(Schema);
|
|
64
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.
|
|
65
63
|
*/
|
|
66
|
-
export const createEvolu = (deps) => (schema,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
return evolu;
|
|
78
|
-
};
|
|
79
|
-
const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
80
|
-
deps.console.enabled = evoluConfig.enableLogging ?? false;
|
|
81
|
-
deps.console.log("[evolu]", "createEvoluInstance");
|
|
82
|
-
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
|
+
});
|
|
83
75
|
const errorStore = createStore(null);
|
|
84
76
|
const rowsStore = createStore(new Map());
|
|
85
|
-
const
|
|
86
|
-
|
|
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);
|
|
87
83
|
const subscribedQueries = createSubscribedQueries(rowsStore);
|
|
88
84
|
const loadingPromises = createLoadingPromises(subscribedQueries);
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const dbWorker = deps.createDbWorker(
|
|
85
|
+
const onCompleteCallbacks = createCallbacks(deps);
|
|
86
|
+
const exportCallbacks = createCallbacks(deps);
|
|
87
|
+
const dbWorker = deps.createDbWorker(dbConfig.name);
|
|
92
88
|
const getTabId = () => {
|
|
93
89
|
tabId ??= createId(deps);
|
|
94
90
|
return tabId;
|
|
95
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).
|
|
96
94
|
dbWorker.onMessage((message) => {
|
|
97
95
|
switch (message.type) {
|
|
98
|
-
case "onInit": {
|
|
99
|
-
ownerStore.set(message.owner);
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
96
|
case "onError": {
|
|
103
97
|
errorStore.set(message.error);
|
|
104
98
|
break;
|
|
105
99
|
}
|
|
106
|
-
case "
|
|
100
|
+
case "onGetAppOwner": {
|
|
101
|
+
resolveAppOwner(message.appOwner);
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case "onQueryPatches": {
|
|
107
105
|
if (message.tabId !== getTabId())
|
|
108
106
|
return;
|
|
109
107
|
const state = rowsStore.get();
|
|
110
108
|
const nextState = new Map([
|
|
111
109
|
...state,
|
|
112
|
-
...message.
|
|
110
|
+
...message.queryPatches.map(({ query, patches }) => [
|
|
113
111
|
query,
|
|
114
112
|
applyPatches(patches, state.get(query) ?? emptyRows),
|
|
115
113
|
]),
|
|
116
114
|
]);
|
|
117
|
-
for (const { query } of message.
|
|
115
|
+
for (const { query } of message.queryPatches) {
|
|
118
116
|
loadingPromises.resolve(query, nextState.get(query) ?? emptyRows);
|
|
119
117
|
}
|
|
120
118
|
if (deps.flushSync && message.onCompleteIds.length > 0) {
|
|
@@ -126,15 +124,19 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
126
124
|
rowsStore.set(nextState);
|
|
127
125
|
}
|
|
128
126
|
for (const id of message.onCompleteIds) {
|
|
129
|
-
|
|
127
|
+
onCompleteCallbacks.execute(id);
|
|
130
128
|
}
|
|
131
129
|
break;
|
|
132
130
|
}
|
|
133
|
-
case "
|
|
131
|
+
case "refreshQueries": {
|
|
134
132
|
if (message.tabId && message.tabId === getTabId())
|
|
135
133
|
return;
|
|
136
|
-
loadingPromises.
|
|
137
|
-
|
|
134
|
+
const loadingPromisesQueries = loadingPromises.getQueries();
|
|
135
|
+
loadingPromises.releaseUnsubscribedOnMutation();
|
|
136
|
+
const queries = dedupeArray([
|
|
137
|
+
...loadingPromisesQueries,
|
|
138
|
+
...subscribedQueries.get(),
|
|
139
|
+
]);
|
|
138
140
|
if (isNonEmptyReadonlyArray(queries)) {
|
|
139
141
|
dbWorker.postMessage({ type: "query", tabId: getTabId(), queries });
|
|
140
142
|
}
|
|
@@ -142,15 +144,15 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
142
144
|
}
|
|
143
145
|
case "onReset": {
|
|
144
146
|
if (message.reload) {
|
|
145
|
-
|
|
147
|
+
deps.reloadApp(reloadUrl);
|
|
146
148
|
}
|
|
147
149
|
else {
|
|
148
|
-
|
|
150
|
+
onCompleteCallbacks.execute(message.onCompleteId);
|
|
149
151
|
}
|
|
150
152
|
break;
|
|
151
153
|
}
|
|
152
154
|
case "onExport": {
|
|
153
|
-
|
|
155
|
+
exportCallbacks.execute(message.onCompleteId, message.file);
|
|
154
156
|
break;
|
|
155
157
|
}
|
|
156
158
|
default:
|
|
@@ -173,110 +175,86 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
173
175
|
}
|
|
174
176
|
return type;
|
|
175
177
|
};
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
* (initial data are already in the DB) because we don't want to wait for
|
|
180
|
-
* SQLite's response. Initial data should be small (because they are inlined
|
|
181
|
-
* in the code), so it's ok.
|
|
182
|
-
*/
|
|
183
|
-
if (initialData)
|
|
184
|
-
initialData({
|
|
185
|
-
insert: (table, props) => {
|
|
186
|
-
const id = createId(deps);
|
|
187
|
-
const values = getMutationType(table, "insert").fromUnknown(props);
|
|
188
|
-
if (values.ok) {
|
|
189
|
-
const dbChange = { table, id, values: values.value };
|
|
190
|
-
assertValidDbChange(dbChange);
|
|
191
|
-
initialDataDbChanges.push(dbChange);
|
|
192
|
-
return ok({ id });
|
|
193
|
-
}
|
|
194
|
-
return values;
|
|
195
|
-
},
|
|
196
|
-
});
|
|
197
|
-
dbWorker.postMessage({
|
|
198
|
-
type: "init",
|
|
199
|
-
config,
|
|
200
|
-
dbSchema,
|
|
201
|
-
initialData: initialDataDbChanges,
|
|
202
|
-
});
|
|
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" });
|
|
203
181
|
const loadQueryMicrotaskQueue = [];
|
|
204
182
|
const mutateMicrotaskQueue = [];
|
|
183
|
+
const useOwnerMicrotaskQueue = [];
|
|
205
184
|
const createMutation = (kind) => (table, props, options) => {
|
|
206
|
-
const
|
|
207
|
-
const result = Type.fromUnknown(props);
|
|
185
|
+
const result = getMutationType(table, kind).fromUnknown(props);
|
|
208
186
|
const id = kind === "insert"
|
|
209
187
|
? createId(deps)
|
|
210
188
|
: props.id;
|
|
211
189
|
if (options?.onlyValidate !== true) {
|
|
212
190
|
if (!result.ok) {
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
mutateMicrotaskQueue.push([undefined, undefined]);
|
|
191
|
+
// Mark the transaction as invalid by pushing null
|
|
192
|
+
mutateMicrotaskQueue.push([null, undefined]);
|
|
216
193
|
}
|
|
217
194
|
else {
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
queueMicrotask(() => {
|
|
226
|
-
const changes = [];
|
|
227
|
-
const onCompletes = [];
|
|
228
|
-
for (const [change, onComplete] of mutateMicrotaskQueue) {
|
|
229
|
-
if (change)
|
|
230
|
-
changes.push(change);
|
|
231
|
-
if (onComplete)
|
|
232
|
-
onCompletes.push(onComplete);
|
|
233
|
-
}
|
|
234
|
-
const mutateMicrotaskQueueLength = mutateMicrotaskQueue.length;
|
|
235
|
-
mutateMicrotaskQueue.length = 0;
|
|
236
|
-
// Don't mutate anything if there was a validation error.
|
|
237
|
-
// All mutations within a queue are considered to be a transaction.
|
|
238
|
-
if (changes.length !== mutateMicrotaskQueueLength) {
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
const onCompleteIds = onCompletes.map((onComplete) => callbacks.register(onComplete));
|
|
242
|
-
loadingPromises.releaseUnsubscribed();
|
|
243
|
-
if (isNonEmptyArray(changes))
|
|
244
|
-
dbWorker.postMessage({
|
|
245
|
-
type: "mutate",
|
|
246
|
-
tabId: getTabId(),
|
|
247
|
-
changes,
|
|
248
|
-
onCompleteIds,
|
|
249
|
-
subscribedQueries: subscribedQueries.get(),
|
|
250
|
-
});
|
|
195
|
+
const { id: _, isDeleted, ...values } = result.value;
|
|
196
|
+
const dbChange = DbChange.orThrow({
|
|
197
|
+
table,
|
|
198
|
+
id,
|
|
199
|
+
values,
|
|
200
|
+
isInsert: kind === "insert" || kind === "upsert",
|
|
201
|
+
isDelete: SqliteBoolean.is(isDeleted) ? Boolean(isDeleted) : null,
|
|
251
202
|
});
|
|
203
|
+
mutateMicrotaskQueue.push([
|
|
204
|
+
{ ...dbChange, ownerId: options?.ownerId },
|
|
205
|
+
options?.onComplete,
|
|
206
|
+
]);
|
|
207
|
+
}
|
|
208
|
+
if (mutateMicrotaskQueue.length === 1) {
|
|
209
|
+
queueMicrotask(processMutationQueue);
|
|
210
|
+
}
|
|
252
211
|
}
|
|
253
212
|
if (result.ok)
|
|
254
213
|
return ok({ id });
|
|
255
214
|
return err(result.error);
|
|
256
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
|
+
};
|
|
257
244
|
const evolu = {
|
|
258
245
|
subscribeError: errorStore.subscribe,
|
|
259
246
|
getError: errorStore.get,
|
|
260
|
-
createQuery
|
|
261
|
-
const compiledQuery = queryCallback(kysely).compile();
|
|
262
|
-
if (isSqlMutation(compiledQuery.sql))
|
|
263
|
-
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.");
|
|
264
|
-
return serializeQuery({
|
|
265
|
-
sql: compiledQuery.sql,
|
|
266
|
-
parameters: compiledQuery.parameters,
|
|
267
|
-
...(options && { options }),
|
|
268
|
-
});
|
|
269
|
-
},
|
|
247
|
+
createQuery,
|
|
270
248
|
loadQuery: (query) => {
|
|
271
249
|
const { promise, isNew } = loadingPromises.get(query);
|
|
272
250
|
if (isNew) {
|
|
273
251
|
loadQueryMicrotaskQueue.push(query);
|
|
274
252
|
if (loadQueryMicrotaskQueue.length === 1) {
|
|
275
253
|
queueMicrotask(() => {
|
|
276
|
-
|
|
277
|
-
const queries = [...new Set(loadQueryMicrotaskQueue)];
|
|
254
|
+
const queries = dedupeArray(loadQueryMicrotaskQueue);
|
|
278
255
|
loadQueryMicrotaskQueue.length = 0;
|
|
279
|
-
|
|
256
|
+
assertNonEmptyReadonlyArray(queries);
|
|
257
|
+
deps.console.log("[evolu]", "loadQuery", { queries });
|
|
280
258
|
dbWorker.postMessage({
|
|
281
259
|
type: "query",
|
|
282
260
|
tabId: getTabId(),
|
|
@@ -304,20 +282,16 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
304
282
|
};
|
|
305
283
|
},
|
|
306
284
|
getQueryRows: (query) => (rowsStore.get().get(query) ?? emptyRows),
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
subscribeSyncState: syncStore.subscribe,
|
|
310
|
-
getSyncState: syncStore.get,
|
|
285
|
+
appOwner,
|
|
286
|
+
// TODO: Update it for the owner-api
|
|
287
|
+
// subscribeSyncState: syncStore.subscribe,
|
|
288
|
+
// getSyncState: syncStore.get,
|
|
311
289
|
insert: createMutation("insert"),
|
|
312
290
|
update: createMutation("update"),
|
|
313
291
|
upsert: createMutation("upsert"),
|
|
314
292
|
resetAppOwner: (options) => {
|
|
315
|
-
// Eslint bug, Promise<void> is correct by docs.
|
|
316
|
-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
317
293
|
const { promise, resolve } = Promise.withResolvers();
|
|
318
|
-
const onCompleteId =
|
|
319
|
-
resolve();
|
|
320
|
-
});
|
|
294
|
+
const onCompleteId = onCompleteCallbacks.register(resolve);
|
|
321
295
|
dbWorker.postMessage({
|
|
322
296
|
type: "reset",
|
|
323
297
|
onCompleteId,
|
|
@@ -326,12 +300,8 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
326
300
|
return promise;
|
|
327
301
|
},
|
|
328
302
|
restoreAppOwner: (mnemonic, options) => {
|
|
329
|
-
// Eslint bug, Promise<void> is correct by docs.
|
|
330
|
-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
331
303
|
const { promise, resolve } = Promise.withResolvers();
|
|
332
|
-
const onCompleteId =
|
|
333
|
-
resolve();
|
|
334
|
-
});
|
|
304
|
+
const onCompleteId = onCompleteCallbacks.register(resolve);
|
|
335
305
|
dbWorker.postMessage({
|
|
336
306
|
type: "reset",
|
|
337
307
|
onCompleteId,
|
|
@@ -341,7 +311,7 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
341
311
|
return promise;
|
|
342
312
|
},
|
|
343
313
|
reloadApp: () => {
|
|
344
|
-
|
|
314
|
+
deps.reloadApp(reloadUrl);
|
|
345
315
|
},
|
|
346
316
|
ensureSchema: (schema) => {
|
|
347
317
|
mutationTypesCache.clear();
|
|
@@ -350,20 +320,72 @@ const createEvoluInstance = (deps) => (schema, evoluConfig) => {
|
|
|
350
320
|
},
|
|
351
321
|
exportDatabase: () => {
|
|
352
322
|
const { promise, resolve } = Promise.withResolvers();
|
|
353
|
-
const onCompleteId =
|
|
354
|
-
if (arg instanceof Uint8Array)
|
|
355
|
-
resolve(arg);
|
|
356
|
-
});
|
|
323
|
+
const onCompleteId = exportCallbacks.register(resolve);
|
|
357
324
|
dbWorker.postMessage({ type: "export", onCompleteId });
|
|
358
325
|
return promise;
|
|
359
326
|
},
|
|
327
|
+
useOwner: (owner) => {
|
|
328
|
+
const scheduleOwnerQueueProcessing = () => {
|
|
329
|
+
if (useOwnerMicrotaskQueue.length !== 1)
|
|
330
|
+
return;
|
|
331
|
+
queueMicrotask(() => {
|
|
332
|
+
const queue = [...useOwnerMicrotaskQueue];
|
|
333
|
+
useOwnerMicrotaskQueue.length = 0;
|
|
334
|
+
const result = [];
|
|
335
|
+
const skipIndices = new Set();
|
|
336
|
+
for (let i = 0; i < queue.length; i++) {
|
|
337
|
+
if (skipIndices.has(i))
|
|
338
|
+
continue;
|
|
339
|
+
const [currentOwner, currentUse, currentOwnerSerialized] = queue[i];
|
|
340
|
+
// Look for opposite action with same owner
|
|
341
|
+
for (let j = i + 1; j < queue.length; j++) {
|
|
342
|
+
if (skipIndices.has(j))
|
|
343
|
+
continue;
|
|
344
|
+
const [, otherUse, otherOwnerSerialized] = queue[j];
|
|
345
|
+
if (currentUse !== otherUse &&
|
|
346
|
+
eqArrayNumber(currentOwnerSerialized, otherOwnerSerialized)) {
|
|
347
|
+
// Found cancel-out pair, skip both
|
|
348
|
+
skipIndices.add(i).add(j);
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (!skipIndices.has(i)) {
|
|
353
|
+
result.push([currentOwner, currentUse, currentOwnerSerialized]);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
for (const [owner, use] of result) {
|
|
357
|
+
dbWorker.postMessage({ type: "useOwner", owner, use });
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
};
|
|
361
|
+
useOwnerMicrotaskQueue.push([owner, true, pack(owner)]);
|
|
362
|
+
scheduleOwnerQueueProcessing();
|
|
363
|
+
const unuse = () => {
|
|
364
|
+
useOwnerMicrotaskQueue.push([owner, false, pack(owner)]);
|
|
365
|
+
scheduleOwnerQueueProcessing();
|
|
366
|
+
};
|
|
367
|
+
return unuse;
|
|
368
|
+
},
|
|
369
|
+
/** Disposal is not implemented yet. */
|
|
370
|
+
[Symbol.dispose]: () => {
|
|
371
|
+
throw new Error("Evolu instance disposal is not yet implemented");
|
|
372
|
+
},
|
|
360
373
|
};
|
|
361
374
|
return evolu;
|
|
362
375
|
};
|
|
363
|
-
export const
|
|
376
|
+
export const createQuery = (queryCallback, options) => {
|
|
377
|
+
const compiledQuery = queryCallback(kysely).compile();
|
|
378
|
+
if (isSqlMutation(compiledQuery.sql))
|
|
379
|
+
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.");
|
|
380
|
+
return serializeQuery({
|
|
381
|
+
sql: compiledQuery.sql,
|
|
382
|
+
parameters: compiledQuery.parameters,
|
|
383
|
+
...(options && { options }),
|
|
384
|
+
});
|
|
385
|
+
};
|
|
364
386
|
const createLoadingPromises = (subscribedQueries) => {
|
|
365
387
|
const loadingPromiseMap = new Map();
|
|
366
|
-
|
|
388
|
+
return {
|
|
367
389
|
get: (query) => {
|
|
368
390
|
let loadingPromise = loadingPromiseMap.get(query);
|
|
369
391
|
const isNew = !loadingPromise;
|
|
@@ -385,15 +407,12 @@ const createLoadingPromises = (subscribedQueries) => {
|
|
|
385
407
|
loadingPromise.resolve(rows);
|
|
386
408
|
}
|
|
387
409
|
else {
|
|
388
|
-
// A promise can't be fulfilled 2x, so we need a new one.
|
|
389
410
|
loadingPromise.promise = Promise.resolve(rows);
|
|
390
411
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
* https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md
|
|
396
|
-
*/
|
|
412
|
+
// Set status and value fields for React's `use` Hook to unwrap synchronously.
|
|
413
|
+
// While undocumented in React docs, React still uses these properties internally,
|
|
414
|
+
// and Evolu's own promise caching logic depends on checking `promise.status`.
|
|
415
|
+
// https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md
|
|
397
416
|
void Object.assign(loadingPromise.promise, {
|
|
398
417
|
status: "fulfilled",
|
|
399
418
|
value: rows,
|
|
@@ -402,14 +421,7 @@ const createLoadingPromises = (subscribedQueries) => {
|
|
|
402
421
|
loadingPromiseMap.delete(query);
|
|
403
422
|
}
|
|
404
423
|
},
|
|
405
|
-
|
|
406
|
-
* We can't delete loading promises in `resolveLoadingPromises` because they
|
|
407
|
-
* must be cached, so repeated calls to `loadQuery` will always return the
|
|
408
|
-
* same promise until the data changes, and we also can't cache them forever
|
|
409
|
-
* because only subscribed queries are automatically updated (reactivity is
|
|
410
|
-
* expensive) hence this function must be called manually on any mutation.
|
|
411
|
-
*/
|
|
412
|
-
releaseUnsubscribed: () => {
|
|
424
|
+
releaseUnsubscribedOnMutation: () => {
|
|
413
425
|
[...loadingPromiseMap.entries()]
|
|
414
426
|
.filter(([query]) => !subscribedQueries.has(query))
|
|
415
427
|
.forEach(([query, loadingPromise]) => {
|
|
@@ -421,9 +433,6 @@ const createLoadingPromises = (subscribedQueries) => {
|
|
|
421
433
|
}
|
|
422
434
|
});
|
|
423
435
|
},
|
|
436
|
+
getQueries: () => Array.from(loadingPromiseMap.keys()),
|
|
424
437
|
};
|
|
425
|
-
return loadingPromises;
|
|
426
|
-
};
|
|
427
|
-
const assertValidDbChange = (dbChange) => {
|
|
428
|
-
assert(DbChange.is(dbChange), `Failed to create DbChange for table "${dbChange.table}". If you see this message, you either disabled EvoluSchema validation or Evolu has a bug - please report it.`);
|
|
429
438
|
};
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Internal Evolu
|
|
3
3
|
*
|
|
4
4
|
* ### Example
|
|
5
5
|
*
|
|
6
6
|
* ```ts
|
|
7
|
-
* import {
|
|
7
|
+
* import { createTimestamp } from "@evolu/common/evolu";
|
|
8
8
|
* ```
|
|
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"}
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Internal Evolu
|
|
3
3
|
*
|
|
4
4
|
* ### Example
|
|
5
5
|
*
|
|
6
6
|
* ```ts
|
|
7
|
-
* import {
|
|
7
|
+
* import { createTimestamp } from "@evolu/common/evolu";
|
|
8
8
|
* ```
|
|
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";
|