@evolu/common 8.0.0-next.3 → 8.0.0-next.5
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 +10 -5
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +10 -5
- package/dist/src/Error.d.ts +5 -3
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +16 -0
- package/dist/src/Identicon.js +4 -4
- package/dist/src/LockManager.d.ts +74 -0
- package/dist/src/LockManager.d.ts.map +1 -0
- package/dist/src/LockManager.js +103 -0
- package/dist/src/Microtask.d.ts.map +1 -1
- package/dist/src/Microtask.js +90 -26
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/RefCount.d.ts.map +1 -1
- package/dist/src/RefCount.js +153 -68
- package/dist/src/Resource.d.ts +6 -6
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +211 -162
- package/dist/src/Schedule.d.ts +5 -5
- package/dist/src/Schedule.d.ts.map +1 -1
- package/dist/src/Schedule.js +53 -44
- package/dist/src/Sqlite.d.ts +2 -0
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +66 -41
- package/dist/src/Task.d.ts +99 -123
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +273 -256
- package/dist/src/Test.d.ts +14 -15
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Time.d.ts +5 -0
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +5 -0
- package/dist/src/Type.d.ts +7 -0
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +7 -0
- package/dist/src/Types.d.ts +0 -8
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +7 -6
- package/dist/src/Worker.d.ts +42 -8
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +292 -112
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/local-first/Db.d.ts +5 -4
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +43 -37
- package/dist/src/local-first/Evolu.d.ts +3 -2
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +90 -80
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Shared.d.ts +65 -54
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +390 -376
- package/package.json +1 -1
- package/src/Array.ts +8 -8
- package/src/Assert.ts +11 -6
- package/src/Error.ts +5 -4
- package/src/Identicon.ts +4 -4
- package/src/LockManager.ts +181 -0
- package/src/Microtask.ts +17 -11
- package/src/Object.ts +3 -3
- package/src/RefCount.ts +25 -21
- package/src/Resource.ts +63 -56
- package/src/Schedule.ts +70 -48
- package/src/Set.ts +4 -4
- package/src/Sqlite.ts +43 -31
- package/src/Task.ts +392 -430
- package/src/Test.ts +14 -15
- package/src/Time.ts +7 -0
- package/src/Type.ts +9 -0
- package/src/Types.ts +0 -9
- package/src/WebSocket.ts +7 -6
- package/src/Worker.ts +195 -53
- package/src/index.ts +1 -0
- package/src/local-first/Db.ts +86 -61
- package/src/local-first/Evolu.ts +74 -68
- package/src/local-first/Owner.ts +1 -1
- package/src/local-first/Query.ts +1 -1
- package/src/local-first/Shared.ts +485 -476
package/src/local-first/Db.ts
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
type RandomBytesDep,
|
|
19
19
|
} from "../Crypto.js";
|
|
20
20
|
import { exhaustiveCheck, lazyFalse, lazyVoid } from "../Function.js";
|
|
21
|
+
import type { LockManagerDep } from "../LockManager.js";
|
|
22
|
+
import { acquireLeaderLock } from "../LockManager.js";
|
|
21
23
|
import { createRecord, getProperty, objectToEntries } from "../Object.js";
|
|
22
24
|
import { ok, type Result } from "../Result.js";
|
|
23
25
|
import type {
|
|
@@ -35,7 +37,7 @@ import {
|
|
|
35
37
|
sqliteQueryStringToSqliteQuery,
|
|
36
38
|
SqliteValue,
|
|
37
39
|
} from "../Sqlite.js";
|
|
38
|
-
import { callback, type
|
|
40
|
+
import { callback, type Task } from "../Task.js";
|
|
39
41
|
import { Millis, millisToDateIso, type TimeDep } from "../Time.js";
|
|
40
42
|
import type { Name } from "../Type.js";
|
|
41
43
|
import {
|
|
@@ -47,6 +49,7 @@ import {
|
|
|
47
49
|
} from "../Type.js";
|
|
48
50
|
import type { ExtractType } from "../Types.js";
|
|
49
51
|
import type {
|
|
52
|
+
CreateBroadcastChannelDep,
|
|
50
53
|
NativeMessagePort,
|
|
51
54
|
Worker,
|
|
52
55
|
WorkerDeps,
|
|
@@ -72,7 +75,14 @@ import {
|
|
|
72
75
|
getEvoluSqliteSchema,
|
|
73
76
|
systemColumns,
|
|
74
77
|
} from "./Schema.js";
|
|
75
|
-
import type {
|
|
78
|
+
import type {
|
|
79
|
+
ConsoleEntryOrError,
|
|
80
|
+
DbWorkerInput,
|
|
81
|
+
DbWorkerOutput,
|
|
82
|
+
DbWorkerQueuedResponse,
|
|
83
|
+
DbWorkerRequest,
|
|
84
|
+
} from "./Shared.js";
|
|
85
|
+
import { consoleEntryOrErrorBroadcastChannelName } from "./Shared.js";
|
|
76
86
|
import {
|
|
77
87
|
createBaseSqliteStorage,
|
|
78
88
|
createBaseSqliteStorageTables,
|
|
@@ -105,7 +115,7 @@ import {
|
|
|
105
115
|
export type DbWorker = Worker<DbWorkerInit>;
|
|
106
116
|
|
|
107
117
|
export interface DbWorkerInit {
|
|
108
|
-
readonly type: "
|
|
118
|
+
readonly type: "DbWorkerInit";
|
|
109
119
|
readonly name: Name;
|
|
110
120
|
readonly consoleLevel: ConsoleLevel;
|
|
111
121
|
readonly sqliteSchema: SqliteSchema;
|
|
@@ -120,13 +130,16 @@ export interface CreateDbWorkerDep {
|
|
|
120
130
|
readonly createDbWorker: CreateDbWorker;
|
|
121
131
|
}
|
|
122
132
|
|
|
123
|
-
export type DbWorkerDeps = WorkerDeps &
|
|
133
|
+
export type DbWorkerDeps = WorkerDeps &
|
|
134
|
+
CreateBroadcastChannelDep &
|
|
135
|
+
LockManagerDep &
|
|
136
|
+
CreateSqliteDriverDep;
|
|
124
137
|
|
|
125
138
|
export const startDbWorker =
|
|
126
139
|
(self: WorkerSelf<DbWorkerInit>): Task<void, never, DbWorkerDeps> =>
|
|
127
|
-
async (
|
|
128
|
-
await using
|
|
129
|
-
|
|
140
|
+
async (run) => {
|
|
141
|
+
await using disposer = new AsyncDisposableStack();
|
|
142
|
+
disposer.use(self);
|
|
130
143
|
const { deps } = run;
|
|
131
144
|
|
|
132
145
|
let initialized = false;
|
|
@@ -143,25 +156,33 @@ export const startDbWorker =
|
|
|
143
156
|
|
|
144
157
|
const console = deps.console.child(initMessage.name).child("DbWorker");
|
|
145
158
|
console.setLevel(initMessage.consoleLevel);
|
|
146
|
-
console.info("start DbWorker");
|
|
147
159
|
|
|
148
|
-
const port =
|
|
160
|
+
const port = disposer.use(
|
|
149
161
|
deps.createMessagePort<DbWorkerOutput, DbWorkerInput>(initMessage.port),
|
|
150
162
|
);
|
|
163
|
+
const consoleEntryOrErrorBroadcastChannel = disposer.use(
|
|
164
|
+
deps.createBroadcastChannel<ConsoleEntryOrError>(
|
|
165
|
+
consoleEntryOrErrorBroadcastChannelName,
|
|
166
|
+
),
|
|
167
|
+
);
|
|
151
168
|
|
|
152
|
-
|
|
169
|
+
disposer.defer(
|
|
153
170
|
deps.consoleStoreOutputEntry.subscribe(() => {
|
|
154
171
|
const entry = deps.consoleStoreOutputEntry.get();
|
|
155
|
-
if (entry)
|
|
172
|
+
if (entry)
|
|
173
|
+
consoleEntryOrErrorBroadcastChannel.postMessage({
|
|
174
|
+
type: "ConsoleEntry",
|
|
175
|
+
entry,
|
|
176
|
+
});
|
|
156
177
|
}),
|
|
157
178
|
);
|
|
179
|
+
console.debug("createDbWorker");
|
|
158
180
|
|
|
159
|
-
|
|
160
|
-
if (stack.disposed) return ok();
|
|
181
|
+
disposer.use(await run.orThrow(acquireLeaderLock(initMessage.name)));
|
|
161
182
|
|
|
162
183
|
port.postMessage({ type: "LeaderAcquired", name: initMessage.name });
|
|
163
184
|
|
|
164
|
-
const sqlite =
|
|
185
|
+
const sqlite = disposer.use(
|
|
165
186
|
await run.orThrow(
|
|
166
187
|
createSqlite(
|
|
167
188
|
initMessage.name,
|
|
@@ -173,15 +194,19 @@ export const startDbWorker =
|
|
|
173
194
|
);
|
|
174
195
|
console.debug("SQLite created");
|
|
175
196
|
|
|
176
|
-
const baseSqliteStorage = createBaseSqliteStorage({
|
|
197
|
+
const baseSqliteStorage = createBaseSqliteStorage({
|
|
198
|
+
sqlite,
|
|
199
|
+
...deps,
|
|
200
|
+
});
|
|
177
201
|
|
|
178
202
|
const dbDeps = {
|
|
179
|
-
...
|
|
203
|
+
...deps,
|
|
180
204
|
sqlite,
|
|
181
205
|
sqliteSchema: initMessage.sqliteSchema,
|
|
182
206
|
baseSqliteStorage,
|
|
183
207
|
timestampConfig: { maxDrift: defaultTimestampMaxDrift },
|
|
184
208
|
};
|
|
209
|
+
const dbWorkerRun = disposer.use(run.create());
|
|
185
210
|
|
|
186
211
|
const currentSchema = getEvoluSqliteSchema(dbDeps)();
|
|
187
212
|
const dbIsInitialized = "evolu_version" in currentSchema.tables;
|
|
@@ -195,33 +220,25 @@ export const startDbWorker =
|
|
|
195
220
|
|
|
196
221
|
const storage = createClientStorage({ ...dbDeps, clock })({
|
|
197
222
|
onError: (error) => {
|
|
198
|
-
|
|
223
|
+
consoleEntryOrErrorBroadcastChannel.postMessage({
|
|
224
|
+
type: "Error",
|
|
225
|
+
error,
|
|
226
|
+
});
|
|
199
227
|
},
|
|
200
228
|
});
|
|
201
229
|
|
|
202
|
-
|
|
203
|
-
const _moved = stack.move();
|
|
204
|
-
const runWithStorage = run.addDeps({ storage });
|
|
230
|
+
const disposables = disposer.move();
|
|
205
231
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
* memory pressure shows up; even millions of short callback IDs are
|
|
213
|
-
* acceptable here, and naive eviction could let a still-retried request run
|
|
214
|
-
* again.
|
|
215
|
-
*/
|
|
216
|
-
const processedRequestIds = new Set<Id>();
|
|
232
|
+
port.onMessage = (input) => {
|
|
233
|
+
if (input.type === "Dispose") {
|
|
234
|
+
if (!disposables.disposed) console.debug("disposeDbWorker");
|
|
235
|
+
void disposables.disposeAsync();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
217
238
|
|
|
218
|
-
|
|
219
|
-
if (processedRequestIds.has(callbackId)) return;
|
|
220
|
-
processedRequestIds.add(callbackId);
|
|
239
|
+
const { callbackId, request } = input;
|
|
221
240
|
|
|
222
|
-
const postQueuedResponse = (
|
|
223
|
-
response: ExtractType<DbWorkerOutput, "OnQueuedResponse">["response"],
|
|
224
|
-
): void => {
|
|
241
|
+
const postQueuedResponse = (response: DbWorkerQueuedResponse): void => {
|
|
225
242
|
port.postMessage(
|
|
226
243
|
{ type: "OnQueuedResponse", callbackId, response },
|
|
227
244
|
response.type === "ForEvolu" && response.message.type === "Export"
|
|
@@ -238,11 +255,14 @@ export const startDbWorker =
|
|
|
238
255
|
request.message,
|
|
239
256
|
);
|
|
240
257
|
if (!result.ok) {
|
|
241
|
-
|
|
258
|
+
consoleEntryOrErrorBroadcastChannel.postMessage({
|
|
259
|
+
type: "Error",
|
|
260
|
+
error: result.error,
|
|
261
|
+
});
|
|
242
262
|
} else {
|
|
243
263
|
postQueuedResponse({
|
|
244
264
|
type: "ForEvolu",
|
|
245
|
-
|
|
265
|
+
id: request.id,
|
|
246
266
|
message: result.value,
|
|
247
267
|
});
|
|
248
268
|
}
|
|
@@ -252,7 +272,7 @@ export const startDbWorker =
|
|
|
252
272
|
case "Query":
|
|
253
273
|
postQueuedResponse({
|
|
254
274
|
type: "ForEvolu",
|
|
255
|
-
|
|
275
|
+
id: request.id,
|
|
256
276
|
message: {
|
|
257
277
|
type: "Query",
|
|
258
278
|
rowsByQuery: loadQueries(dbDeps)(request.message.queries),
|
|
@@ -263,7 +283,7 @@ export const startDbWorker =
|
|
|
263
283
|
case "Export":
|
|
264
284
|
postQueuedResponse({
|
|
265
285
|
type: "ForEvolu",
|
|
266
|
-
|
|
286
|
+
id: request.id,
|
|
267
287
|
message: {
|
|
268
288
|
type: "Export",
|
|
269
289
|
file: dbDeps.sqlite.export(),
|
|
@@ -307,29 +327,34 @@ export const startDbWorker =
|
|
|
307
327
|
case "ApplySyncMessage": {
|
|
308
328
|
const { owner, inputMessage } = request.message;
|
|
309
329
|
|
|
310
|
-
|
|
311
|
-
|
|
330
|
+
dbWorkerRun<void, never, { readonly storage: ClientStorage }>(
|
|
331
|
+
async (run) => {
|
|
332
|
+
const { storage } = run.deps;
|
|
312
333
|
|
|
313
|
-
|
|
314
|
-
applyProtocolMessageAsClient(inputMessage, {
|
|
315
|
-
writeKey: owner.writeKey,
|
|
316
|
-
}),
|
|
317
|
-
);
|
|
334
|
+
storage.setRequestContext(owner.encryptionKey);
|
|
318
335
|
|
|
319
|
-
|
|
336
|
+
const result = await run(
|
|
337
|
+
applyProtocolMessageAsClient(inputMessage, {
|
|
338
|
+
writeKey: owner.writeKey,
|
|
339
|
+
}),
|
|
340
|
+
);
|
|
320
341
|
|
|
321
|
-
|
|
322
|
-
type: "ForSharedWorker",
|
|
323
|
-
message: {
|
|
324
|
-
type: "ApplySyncMessage",
|
|
325
|
-
ownerId: owner.id,
|
|
326
|
-
didWriteMessages,
|
|
327
|
-
result,
|
|
328
|
-
},
|
|
329
|
-
});
|
|
342
|
+
const didWriteMessages = storage.didWriteMessages();
|
|
330
343
|
|
|
331
|
-
|
|
332
|
-
|
|
344
|
+
postQueuedResponse({
|
|
345
|
+
type: "ForSharedWorker",
|
|
346
|
+
message: {
|
|
347
|
+
type: "ApplySyncMessage",
|
|
348
|
+
ownerId: owner.id,
|
|
349
|
+
didWriteMessages,
|
|
350
|
+
result,
|
|
351
|
+
},
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
return ok();
|
|
355
|
+
},
|
|
356
|
+
{ storage },
|
|
357
|
+
);
|
|
333
358
|
break;
|
|
334
359
|
}
|
|
335
360
|
|
|
@@ -777,7 +802,7 @@ const handleMutation =
|
|
|
777
802
|
) =>
|
|
778
803
|
(
|
|
779
804
|
message: ExtractType<
|
|
780
|
-
ExtractType<
|
|
805
|
+
ExtractType<DbWorkerRequest, "ForEvolu">["message"],
|
|
781
806
|
"Mutate"
|
|
782
807
|
>,
|
|
783
808
|
): Result<
|
package/src/local-first/Evolu.ts
CHANGED
|
@@ -20,6 +20,11 @@ import type { ConsoleDep } from "../Console.js";
|
|
|
20
20
|
import { createConsole } from "../Console.js";
|
|
21
21
|
import { createUnknownError } from "../Error.js";
|
|
22
22
|
import { exhaustiveCheck, todo } from "../Function.js";
|
|
23
|
+
import {
|
|
24
|
+
acquireLeaderLock,
|
|
25
|
+
acquireLeaderLockCallback,
|
|
26
|
+
type LockManagerDep,
|
|
27
|
+
} from "../LockManager.js";
|
|
23
28
|
import { createMicrotaskBatch } from "../Microtask.js";
|
|
24
29
|
import type { FlushSyncDep, ReloadAppDep } from "../Platform.js";
|
|
25
30
|
import { createRefCountByKey } from "../RefCount.js";
|
|
@@ -38,7 +43,10 @@ import {
|
|
|
38
43
|
UrlSafeString,
|
|
39
44
|
} from "../Type.js";
|
|
40
45
|
import type { ExtractType } from "../Types.js";
|
|
41
|
-
import type {
|
|
46
|
+
import type {
|
|
47
|
+
CreateBroadcastChannelDep,
|
|
48
|
+
CreateMessageChannelDep,
|
|
49
|
+
} from "../Worker.js";
|
|
42
50
|
import type { CreateDbWorkerDep } from "./Db.js";
|
|
43
51
|
import type { EvoluError } from "./Error.js";
|
|
44
52
|
import type {
|
|
@@ -68,13 +76,12 @@ import type {
|
|
|
68
76
|
} from "./Schema.js";
|
|
69
77
|
import { evoluSchemaToSqliteSchema } from "./Schema.js";
|
|
70
78
|
import type {
|
|
71
|
-
|
|
72
|
-
DbWorkerOutput,
|
|
79
|
+
ConsoleEntryOrError,
|
|
73
80
|
EvoluInput,
|
|
74
81
|
EvoluOutput,
|
|
75
|
-
EvoluTabOutput,
|
|
76
82
|
SharedWorkerDep,
|
|
77
83
|
} from "./Shared.js";
|
|
84
|
+
import { consoleEntryOrErrorBroadcastChannelName } from "./Shared.js";
|
|
78
85
|
import { DbChange } from "./Storage.js";
|
|
79
86
|
import type { Timestamp } from "./Timestamp.js";
|
|
80
87
|
|
|
@@ -547,7 +554,9 @@ export type EvoluDeps = EvoluPlatformDeps & EvoluErrorDep & Disposable;
|
|
|
547
554
|
* logging and synchronous UI flush.
|
|
548
555
|
*/
|
|
549
556
|
export type EvoluPlatformDeps = CreateDbWorkerDep &
|
|
557
|
+
CreateBroadcastChannelDep &
|
|
550
558
|
CreateMessageChannelDep &
|
|
559
|
+
LockManagerDep &
|
|
551
560
|
ReloadAppDep &
|
|
552
561
|
SharedWorkerDep &
|
|
553
562
|
Partial<ConsoleDep> &
|
|
@@ -565,17 +574,21 @@ export type EvoluPlatformDeps = CreateDbWorkerDep &
|
|
|
565
574
|
* Dispose it only during app shutdown.
|
|
566
575
|
*/
|
|
567
576
|
export const createEvoluDeps = (deps: EvoluPlatformDeps): EvoluDeps => {
|
|
568
|
-
const {
|
|
577
|
+
const { createBroadcastChannel, sharedWorker } = deps;
|
|
569
578
|
const console = deps.console ?? createConsole();
|
|
570
579
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
const evoluError =
|
|
580
|
+
using disposer = new DisposableStack();
|
|
581
|
+
disposer.use(sharedWorker);
|
|
582
|
+
const evoluError = disposer.use(createStore<EvoluError | null>(null));
|
|
574
583
|
|
|
575
|
-
const
|
|
576
|
-
|
|
584
|
+
const consoleEntryOrErrorBroadcastChannel = disposer.use(
|
|
585
|
+
createBroadcastChannel<ConsoleEntryOrError>(
|
|
586
|
+
consoleEntryOrErrorBroadcastChannelName,
|
|
587
|
+
),
|
|
588
|
+
);
|
|
589
|
+
consoleEntryOrErrorBroadcastChannel.onMessage = (message) => {
|
|
577
590
|
switch (message.type) {
|
|
578
|
-
case "
|
|
591
|
+
case "ConsoleEntry":
|
|
579
592
|
console.write(message.entry);
|
|
580
593
|
// Fallback channel for unexpected errors without EvoluError typing.
|
|
581
594
|
if (message.entry.method === "error") {
|
|
@@ -583,7 +596,7 @@ export const createEvoluDeps = (deps: EvoluPlatformDeps): EvoluDeps => {
|
|
|
583
596
|
}
|
|
584
597
|
break;
|
|
585
598
|
|
|
586
|
-
case "
|
|
599
|
+
case "Error":
|
|
587
600
|
evoluError.set(message.error);
|
|
588
601
|
// Keep typed errors visible in logs as operational failures.
|
|
589
602
|
console.error(message.error);
|
|
@@ -594,21 +607,25 @@ export const createEvoluDeps = (deps: EvoluPlatformDeps): EvoluDeps => {
|
|
|
594
607
|
}
|
|
595
608
|
};
|
|
596
609
|
|
|
597
|
-
sharedWorker.port.
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
610
|
+
sharedWorker.port.onMessage = (message) => {
|
|
611
|
+
deps.createDbWorker().postMessage(message, [message.port]);
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
disposer.use(
|
|
615
|
+
acquireLeaderLockCallback(deps)("tab", () => {
|
|
616
|
+
sharedWorker.port.postMessage({
|
|
617
|
+
type: "AnnounceTabLeader",
|
|
618
|
+
consoleLevel: console.getLevel(),
|
|
619
|
+
});
|
|
620
|
+
}),
|
|
604
621
|
);
|
|
605
622
|
|
|
606
|
-
const
|
|
623
|
+
const disposables = disposer.move();
|
|
607
624
|
|
|
608
625
|
return {
|
|
609
626
|
...deps,
|
|
610
627
|
evoluError,
|
|
611
|
-
[Symbol.dispose]: () =>
|
|
628
|
+
[Symbol.dispose]: () => disposables.dispose(),
|
|
612
629
|
};
|
|
613
630
|
};
|
|
614
631
|
|
|
@@ -633,12 +650,14 @@ export const createEvolu =
|
|
|
633
650
|
const console = run.deps.console.child(name).child("Evolu");
|
|
634
651
|
console.info("createEvolu");
|
|
635
652
|
|
|
636
|
-
await using
|
|
653
|
+
await using disposer = new AsyncDisposableStack();
|
|
637
654
|
|
|
638
|
-
const rowsByQueryMapStore =
|
|
655
|
+
const rowsByQueryMapStore = disposer.use(
|
|
639
656
|
createStore<RowsByQueryMap>(new Map()),
|
|
640
657
|
);
|
|
641
|
-
const subscribedQueriesRefCount =
|
|
658
|
+
const subscribedQueriesRefCount = disposer.use(
|
|
659
|
+
createRefCountByKey<Query>(),
|
|
660
|
+
);
|
|
642
661
|
|
|
643
662
|
interface LoadingPromise {
|
|
644
663
|
/**
|
|
@@ -662,7 +681,7 @@ export const createEvolu =
|
|
|
662
681
|
* Settle pending query loads during disposal so awaiting callers and React
|
|
663
682
|
* `use` thenables do not hang forever during teardown.
|
|
664
683
|
*/
|
|
665
|
-
const loadingPromisesByQuery =
|
|
684
|
+
const loadingPromisesByQuery = disposer.adopt(
|
|
666
685
|
new Map<Query, LoadingPromise>(),
|
|
667
686
|
(loadingPromisesByQuery) => {
|
|
668
687
|
for (const loadingPromise of loadingPromisesByQuery.values()) {
|
|
@@ -715,18 +734,18 @@ export const createEvolu =
|
|
|
715
734
|
}
|
|
716
735
|
};
|
|
717
736
|
|
|
718
|
-
const onMutateCompleteCallbacks =
|
|
737
|
+
const onMutateCompleteCallbacks = disposer.use(createCallbacks(run.deps));
|
|
719
738
|
|
|
720
739
|
let exportDatabasePending = null as PromiseWithResolvers<
|
|
721
740
|
Uint8Array<ArrayBuffer>
|
|
722
741
|
> | null;
|
|
723
742
|
|
|
724
|
-
|
|
743
|
+
disposer.defer(() => {
|
|
725
744
|
exportDatabasePending?.reject({ type: "EvoluDisposedError" });
|
|
726
745
|
exportDatabasePending = null;
|
|
727
746
|
});
|
|
728
747
|
|
|
729
|
-
const mutateBatch =
|
|
748
|
+
const mutateBatch = disposer.use(
|
|
730
749
|
createMicrotaskBatch<{
|
|
731
750
|
readonly change: MutationChange;
|
|
732
751
|
readonly onComplete: (() => void) | undefined;
|
|
@@ -747,7 +766,7 @@ export const createEvolu =
|
|
|
747
766
|
}),
|
|
748
767
|
);
|
|
749
768
|
|
|
750
|
-
const queryBatch =
|
|
769
|
+
const queryBatch = disposer.use(
|
|
751
770
|
createMicrotaskBatch<Query>((queries) => {
|
|
752
771
|
const dedupedQueries = new Set(queries);
|
|
753
772
|
assert(
|
|
@@ -759,7 +778,7 @@ export const createEvolu =
|
|
|
759
778
|
}),
|
|
760
779
|
);
|
|
761
780
|
|
|
762
|
-
const useOwnerBatch =
|
|
781
|
+
const useOwnerBatch = disposer.use(
|
|
763
782
|
createMicrotaskBatch<
|
|
764
783
|
ExtractType<EvoluInput, "UseOwner">["actions"][number]
|
|
765
784
|
>((actions) => postMessage({ type: "UseOwner", actions })),
|
|
@@ -769,11 +788,11 @@ export const createEvolu =
|
|
|
769
788
|
|
|
770
789
|
// Scope worker/channel wiring and keep only postMessage outside.
|
|
771
790
|
{
|
|
772
|
-
const
|
|
773
|
-
const
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
const evoluChannel =
|
|
791
|
+
const id = createId<"EvoluInstance">(run.deps);
|
|
792
|
+
const { createMessageChannel, sharedWorker } = run.deps;
|
|
793
|
+
disposer.use(await run.orThrow(acquireLeaderLock(id)));
|
|
794
|
+
|
|
795
|
+
const evoluChannel = disposer.use(
|
|
777
796
|
createMessageChannel<EvoluInput, EvoluOutput>(),
|
|
778
797
|
);
|
|
779
798
|
|
|
@@ -858,35 +877,17 @@ export const createEvolu =
|
|
|
858
877
|
{
|
|
859
878
|
type: "CreateEvolu",
|
|
860
879
|
name,
|
|
861
|
-
|
|
862
|
-
dbWorkerPort: dbWorkerChannel.port2.native,
|
|
863
|
-
},
|
|
864
|
-
[evoluChannel.port2.native, dbWorkerChannel.port2.native],
|
|
865
|
-
);
|
|
866
|
-
|
|
867
|
-
/**
|
|
868
|
-
* No stack.use because Evolu instances don't dispose DbWorker because
|
|
869
|
-
* it's SharedWorder responsibility. DbWorker can be used by another tab.
|
|
870
|
-
* That's required because SQLite WASM needs a single web worker.
|
|
871
|
-
*/
|
|
872
|
-
createDbWorker().postMessage(
|
|
873
|
-
{
|
|
874
|
-
type: "Init",
|
|
875
|
-
name,
|
|
880
|
+
id,
|
|
876
881
|
consoleLevel: console.getLevel(),
|
|
877
882
|
sqliteSchema: evoluSchemaToSqliteSchema(schema, config.indexes),
|
|
878
883
|
encryptionKey: appOwner.encryptionKey,
|
|
879
884
|
memoryOnly,
|
|
880
|
-
|
|
885
|
+
evoluPort: evoluChannel.port2.native,
|
|
881
886
|
},
|
|
882
|
-
[
|
|
887
|
+
[evoluChannel.port2.native],
|
|
883
888
|
);
|
|
884
889
|
|
|
885
890
|
postMessage = evoluChannel.port1.postMessage;
|
|
886
|
-
|
|
887
|
-
stack.defer(() => {
|
|
888
|
-
postMessage({ type: "Dispose" });
|
|
889
|
-
});
|
|
890
891
|
}
|
|
891
892
|
|
|
892
893
|
const createMutation =
|
|
@@ -894,7 +895,7 @@ export const createEvolu =
|
|
|
894
895
|
kind: Kind,
|
|
895
896
|
): Mutation<S, Kind> =>
|
|
896
897
|
(table, values, options) => {
|
|
897
|
-
assertNotDisposed(
|
|
898
|
+
assertNotDisposed(disposables);
|
|
898
899
|
|
|
899
900
|
const {
|
|
900
901
|
id = createId(run.deps),
|
|
@@ -928,7 +929,7 @@ export const createEvolu =
|
|
|
928
929
|
const loadQuery = <R extends Row>(
|
|
929
930
|
query: Query<S, R>,
|
|
930
931
|
): Promise<QueryRows<R>> => {
|
|
931
|
-
assertNotDisposed(
|
|
932
|
+
assertNotDisposed(disposables);
|
|
932
933
|
|
|
933
934
|
const loadingPromise = loadingPromisesByQuery.get(query);
|
|
934
935
|
if (loadingPromise) {
|
|
@@ -951,7 +952,7 @@ export const createEvolu =
|
|
|
951
952
|
};
|
|
952
953
|
|
|
953
954
|
const getQueryRows = <R extends Row>(query: Query<S, R>): QueryRows<R> => {
|
|
954
|
-
assertNotDisposed(
|
|
955
|
+
assertNotDisposed(disposables);
|
|
955
956
|
|
|
956
957
|
return (rowsByQueryMapStore.get().get(query) ??
|
|
957
958
|
emptyArray) as QueryRows<R>;
|
|
@@ -961,7 +962,7 @@ export const createEvolu =
|
|
|
961
962
|
owner: ReadonlyOwner | Owner,
|
|
962
963
|
ownerTransports?: NonEmptyReadonlyArray<OwnerTransport>,
|
|
963
964
|
): UnuseOwner => {
|
|
964
|
-
assertNotDisposed(
|
|
965
|
+
assertNotDisposed(disposables);
|
|
965
966
|
|
|
966
967
|
const effectiveTransports = ownerTransports ?? transports;
|
|
967
968
|
assertNonEmptyReadonlyArray(
|
|
@@ -982,6 +983,7 @@ export const createEvolu =
|
|
|
982
983
|
let isUsed = true;
|
|
983
984
|
|
|
984
985
|
return () => {
|
|
986
|
+
if (disposables.disposed) return;
|
|
985
987
|
assert(isUsed, "UnuseOwner can be called only once.");
|
|
986
988
|
isUsed = false;
|
|
987
989
|
useOwnerBatch.push({
|
|
@@ -991,10 +993,14 @@ export const createEvolu =
|
|
|
991
993
|
};
|
|
992
994
|
};
|
|
993
995
|
|
|
994
|
-
const
|
|
996
|
+
const disposables = disposer.move();
|
|
995
997
|
|
|
996
998
|
if (isNonEmptyArray(transports)) useOwner(appOwner);
|
|
997
999
|
|
|
1000
|
+
if (run.signal.aborted) {
|
|
1001
|
+
await disposables.disposeAsync();
|
|
1002
|
+
}
|
|
1003
|
+
|
|
998
1004
|
return ok({
|
|
999
1005
|
name,
|
|
1000
1006
|
appOwner,
|
|
@@ -1012,7 +1018,7 @@ export const createEvolu =
|
|
|
1012
1018
|
],
|
|
1013
1019
|
|
|
1014
1020
|
subscribeQuery: (query) => (listener) => {
|
|
1015
|
-
assertNotDisposed(
|
|
1021
|
+
assertNotDisposed(disposables);
|
|
1016
1022
|
|
|
1017
1023
|
subscribedQueriesRefCount.increment(query);
|
|
1018
1024
|
let isSubscribed = true;
|
|
@@ -1036,7 +1042,7 @@ export const createEvolu =
|
|
|
1036
1042
|
previousRows = null;
|
|
1037
1043
|
unsubscribe();
|
|
1038
1044
|
|
|
1039
|
-
if (
|
|
1045
|
+
if (disposables.disposed) return;
|
|
1040
1046
|
|
|
1041
1047
|
subscribedQueriesRefCount.decrement(query);
|
|
1042
1048
|
};
|
|
@@ -1044,7 +1050,7 @@ export const createEvolu =
|
|
|
1044
1050
|
getQueryRows,
|
|
1045
1051
|
|
|
1046
1052
|
exportDatabase: () => {
|
|
1047
|
-
assertNotDisposed(
|
|
1053
|
+
assertNotDisposed(disposables);
|
|
1048
1054
|
|
|
1049
1055
|
if (!exportDatabasePending) {
|
|
1050
1056
|
exportDatabasePending =
|
|
@@ -1055,12 +1061,12 @@ export const createEvolu =
|
|
|
1055
1061
|
},
|
|
1056
1062
|
|
|
1057
1063
|
deleteDatabase: () => {
|
|
1058
|
-
assertNotDisposed(
|
|
1064
|
+
assertNotDisposed(disposables);
|
|
1059
1065
|
todo();
|
|
1060
1066
|
},
|
|
1061
1067
|
|
|
1062
1068
|
deleteOwner: (owner) => {
|
|
1063
|
-
assertNotDisposed(
|
|
1069
|
+
assertNotDisposed(disposables);
|
|
1064
1070
|
void owner;
|
|
1065
1071
|
todo();
|
|
1066
1072
|
},
|
|
@@ -1068,8 +1074,8 @@ export const createEvolu =
|
|
|
1068
1074
|
useOwner,
|
|
1069
1075
|
|
|
1070
1076
|
[Symbol.asyncDispose]: () => {
|
|
1071
|
-
console.info("
|
|
1072
|
-
return
|
|
1077
|
+
if (!disposables.disposed) console.info("disposeEvolu");
|
|
1078
|
+
return disposables.disposeAsync();
|
|
1073
1079
|
},
|
|
1074
1080
|
} as Evolu<S>);
|
|
1075
1081
|
};
|
package/src/local-first/Owner.ts
CHANGED
|
@@ -100,7 +100,7 @@ export const ownerIdToOwnerIdBytes = (ownerId: OwnerId): OwnerIdBytes =>
|
|
|
100
100
|
|
|
101
101
|
/** Converts {@link OwnerIdBytes} to {@link OwnerId}. */
|
|
102
102
|
export const ownerIdBytesToOwnerId = (ownerIdBytes: OwnerIdBytes): OwnerId =>
|
|
103
|
-
idBytesToId(ownerIdBytes
|
|
103
|
+
idBytesToId(ownerIdBytes) as OwnerId;
|
|
104
104
|
|
|
105
105
|
export const ownerWriteKeyLength = /*#__PURE__*/ NonNegativeInt.orThrow(16);
|
|
106
106
|
|
package/src/local-first/Query.ts
CHANGED