@dxos/echo-pipeline 0.8.4-main.f9ba587 → 0.8.4-main.fffef41
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/lib/browser/{chunk-GBFX5J5B.mjs → chunk-FR6GJ4S4.mjs} +239 -162
- package/dist/lib/browser/chunk-FR6GJ4S4.mjs.map +7 -0
- package/dist/lib/browser/chunk-WJJXJTNS.mjs +240 -0
- package/dist/lib/browser/chunk-WJJXJTNS.mjs.map +7 -0
- package/dist/lib/browser/filter/index.mjs +3 -1
- package/dist/lib/browser/index.mjs +1102 -347
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +36 -14
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/{chunk-FQFKWA3X.mjs → chunk-JQCCVDLT.mjs} +239 -162
- package/dist/lib/node-esm/chunk-JQCCVDLT.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-LEQ77KAT.mjs +240 -0
- package/dist/lib/node-esm/chunk-LEQ77KAT.mjs.map +7 -0
- package/dist/lib/node-esm/filter/index.mjs +3 -1
- package/dist/lib/node-esm/index.mjs +1102 -347
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +36 -14
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/automerge/automerge-host.d.ts +15 -28
- package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
- package/dist/types/src/automerge/collection-synchronizer.d.ts +3 -2
- package/dist/types/src/automerge/collection-synchronizer.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-network-adapter.d.ts +8 -1
- package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-replicator.d.ts +21 -2
- package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
- package/dist/types/src/automerge/index.d.ts +1 -1
- package/dist/types/src/automerge/index.d.ts.map +1 -1
- package/dist/types/src/automerge/leveldb-storage-adapter.d.ts +1 -1
- package/dist/types/src/automerge/leveldb-storage-adapter.d.ts.map +1 -1
- package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts +1 -0
- package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts.map +1 -1
- package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -1
- package/dist/types/src/common/codec.d.ts +1 -1
- package/dist/types/src/common/codec.d.ts.map +1 -1
- package/dist/types/src/db-host/data-service.d.ts +2 -2
- package/dist/types/src/db-host/data-service.d.ts.map +1 -1
- package/dist/types/src/db-host/database-root.d.ts.map +1 -1
- package/dist/types/src/db-host/documents-synchronizer.d.ts +2 -2
- package/dist/types/src/db-host/documents-synchronizer.d.ts.map +1 -1
- package/dist/types/src/db-host/echo-host.d.ts +2 -2
- package/dist/types/src/db-host/echo-host.d.ts.map +1 -1
- package/dist/types/src/db-host/query-service.d.ts +1 -1
- package/dist/types/src/db-host/query-service.d.ts.map +1 -1
- package/dist/types/src/db-host/space-state-manager.d.ts +1 -1
- package/dist/types/src/db-host/space-state-manager.d.ts.map +1 -1
- package/dist/types/src/edge/echo-edge-replicator.d.ts +4 -2
- package/dist/types/src/edge/echo-edge-replicator.d.ts.map +1 -1
- package/dist/types/src/filter/filter-match.d.ts +4 -1
- package/dist/types/src/filter/filter-match.d.ts.map +1 -1
- package/dist/types/src/metadata/metadata-store.d.ts +1 -1
- package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
- package/dist/types/src/pipeline/pipeline.d.ts +1 -1
- package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
- package/dist/types/src/query/errors.d.ts +23 -9
- package/dist/types/src/query/errors.d.ts.map +1 -1
- package/dist/types/src/query/plan.d.ts +8 -1
- package/dist/types/src/query/plan.d.ts.map +1 -1
- package/dist/types/src/query/query-executor.d.ts +13 -1
- package/dist/types/src/query/query-executor.d.ts.map +1 -1
- package/dist/types/src/query/query-planner.d.ts +2 -0
- package/dist/types/src/query/query-planner.d.ts.map +1 -1
- package/dist/types/src/space/admission-discovery-extension.d.ts.map +1 -1
- package/dist/types/src/space/control-pipeline.d.ts +1 -1
- package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
- package/dist/types/src/space/space-manager.d.ts +1 -1
- package/dist/types/src/space/space-manager.d.ts.map +1 -1
- package/dist/types/src/space/space-protocol.d.ts +1 -1
- package/dist/types/src/space/space-protocol.d.ts.map +1 -1
- package/dist/types/src/space/space.d.ts +1 -1
- package/dist/types/src/space/space.d.ts.map +1 -1
- package/dist/types/src/testing/test-agent-builder.d.ts +2 -2
- package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
- package/dist/types/src/testing/test-data.d.ts.map +1 -1
- package/dist/types/src/testing/test-replicator.d.ts +1 -0
- package/dist/types/src/testing/test-replicator.d.ts.map +1 -1
- package/dist/types/src/testing/test-schema.d.ts +1 -1
- package/dist/types/src/testing/test-schema.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +1 -1
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +40 -38
- package/src/automerge/automerge-host.test.ts +18 -8
- package/src/automerge/automerge-host.ts +273 -74
- package/src/automerge/automerge-repo.test.ts +67 -16
- package/src/automerge/collection-synchronizer.test.ts +6 -5
- package/src/automerge/collection-synchronizer.ts +16 -11
- package/src/automerge/echo-data-monitor.ts +1 -1
- package/src/automerge/echo-network-adapter.test.ts +3 -3
- package/src/automerge/echo-network-adapter.ts +38 -6
- package/src/automerge/echo-replicator.ts +23 -2
- package/src/automerge/index.ts +1 -1
- package/src/automerge/leveldb-storage-adapter.ts +7 -7
- package/src/automerge/mesh-echo-replicator-connection.ts +4 -0
- package/src/automerge/mesh-echo-replicator.ts +2 -1
- package/src/automerge/storage-adapter.test.ts +1 -1
- package/src/common/space-id.ts +1 -1
- package/src/db-host/data-service.ts +9 -17
- package/src/db-host/database-root.ts +2 -2
- package/src/db-host/documents-synchronizer.test.ts +1 -1
- package/src/db-host/documents-synchronizer.ts +39 -26
- package/src/db-host/echo-host.ts +14 -15
- package/src/db-host/query-service.ts +10 -2
- package/src/db-host/space-state-manager.ts +2 -2
- package/src/edge/echo-edge-replicator.test.ts +5 -3
- package/src/edge/echo-edge-replicator.ts +105 -27
- package/src/filter/filter-match.test.ts +33 -13
- package/src/filter/filter-match.ts +164 -10
- package/src/metadata/metadata-store.ts +3 -3
- package/src/pipeline/pipeline-stress.test.ts +4 -2
- package/src/pipeline/pipeline.test.ts +3 -2
- package/src/pipeline/pipeline.ts +8 -5
- package/src/query/errors.ts +2 -0
- package/src/query/plan.ts +12 -1
- package/src/query/query-executor.ts +154 -14
- package/src/query/query-planner.test.ts +236 -7
- package/src/query/query-planner.ts +87 -12
- package/src/space/admission-discovery-extension.ts +2 -2
- package/src/space/control-pipeline.test.ts +4 -3
- package/src/space/control-pipeline.ts +9 -6
- package/src/space/space-manager.browser.test.ts +1 -1
- package/src/space/space-manager.ts +5 -4
- package/src/space/space-protocol.browser.test.ts +2 -2
- package/src/space/space-protocol.test.ts +3 -2
- package/src/space/space-protocol.ts +6 -3
- package/src/space/space.test.ts +1 -1
- package/src/space/space.ts +3 -2
- package/src/testing/test-agent-builder.ts +4 -3
- package/src/testing/test-data.ts +1 -1
- package/src/testing/test-replicator.ts +4 -0
- package/src/testing/test-schema.ts +1 -1
- package/src/util.ts +1 -1
- package/dist/lib/browser/chunk-ANZAS5CC.mjs +0 -126
- package/dist/lib/browser/chunk-ANZAS5CC.mjs.map +0 -7
- package/dist/lib/browser/chunk-GBFX5J5B.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-2SAZ7CCF.mjs +0 -126
- package/dist/lib/node-esm/chunk-2SAZ7CCF.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-FQFKWA3X.mjs.map +0 -7
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import "@dxos/node-std/globals";
|
|
2
2
|
import {
|
|
3
3
|
filterMatchObject,
|
|
4
|
+
filterMatchObjectJSON,
|
|
4
5
|
filterMatchValue
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-WJJXJTNS.mjs";
|
|
6
7
|
import {
|
|
7
8
|
AuthExtension,
|
|
8
9
|
AuthStatus,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
mapTimeframeToFeedIndexes,
|
|
26
27
|
startAfter,
|
|
27
28
|
valueEncoding
|
|
28
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-FR6GJ4S4.mjs";
|
|
29
30
|
import "./chunk-CGS2ULMK.mjs";
|
|
30
31
|
|
|
31
32
|
// src/db-host/data-service.ts
|
|
@@ -35,17 +36,10 @@ import { invariant as invariant7 } from "@dxos/invariant";
|
|
|
35
36
|
import { SpaceId as SpaceId2 } from "@dxos/keys";
|
|
36
37
|
import { log as log7 } from "@dxos/log";
|
|
37
38
|
|
|
38
|
-
// src/db-host/documents-synchronizer.ts
|
|
39
|
-
import { next as A2 } from "@automerge/automerge";
|
|
40
|
-
import { UpdateScheduler } from "@dxos/async";
|
|
41
|
-
import { Resource as Resource5 } from "@dxos/context";
|
|
42
|
-
import { invariant as invariant6 } from "@dxos/invariant";
|
|
43
|
-
import { log as log6 } from "@dxos/log";
|
|
44
|
-
|
|
45
39
|
// src/automerge/automerge-host.ts
|
|
46
|
-
import { getBackend, getHeads,
|
|
40
|
+
import { getBackend, getHeads, equals as headsEquals, isAutomerge, save } from "@automerge/automerge";
|
|
47
41
|
import { Repo, interpretAsDocumentId } from "@automerge/automerge-repo";
|
|
48
|
-
import { Event as Event2, asyncTimeout } from "@dxos/async";
|
|
42
|
+
import { DeferredTask, Event as Event2, asyncTimeout } from "@dxos/async";
|
|
49
43
|
import { Context, Resource as Resource3, cancelWithContext } from "@dxos/context";
|
|
50
44
|
import { DatabaseDirectory } from "@dxos/echo-protocol";
|
|
51
45
|
import { invariant as invariant2 } from "@dxos/invariant";
|
|
@@ -53,11 +47,13 @@ import { PublicKey } from "@dxos/keys";
|
|
|
53
47
|
import { log as log3 } from "@dxos/log";
|
|
54
48
|
import { objectPointerCodec } from "@dxos/protocols";
|
|
55
49
|
import { trace as trace2 } from "@dxos/tracing";
|
|
56
|
-
import { bufferToArray } from "@dxos/util";
|
|
50
|
+
import { ComplexSet, bufferToArray, isNonNullable as isNonNullable2, range } from "@dxos/util";
|
|
57
51
|
|
|
58
52
|
// src/automerge/collection-synchronizer.ts
|
|
59
|
-
import { next as
|
|
60
|
-
import
|
|
53
|
+
import { next as A } from "@automerge/automerge";
|
|
54
|
+
import * as Array2 from "effect/Array";
|
|
55
|
+
import * as Record from "effect/Record";
|
|
56
|
+
import { Event, asyncReturn, scheduleTask, scheduleTaskInterval } from "@dxos/async";
|
|
61
57
|
import { Resource } from "@dxos/context";
|
|
62
58
|
import { log } from "@dxos/log";
|
|
63
59
|
import { trace } from "@dxos/tracing";
|
|
@@ -72,15 +68,18 @@ var __dxlog_file = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/automerg
|
|
|
72
68
|
var MIN_QUERY_INTERVAL = 5e3;
|
|
73
69
|
var POLL_INTERVAL = 3e4;
|
|
74
70
|
var CollectionSynchronizer = class extends Resource {
|
|
71
|
+
_sendCollectionState;
|
|
72
|
+
_queryCollectionState;
|
|
73
|
+
_shouldSyncCollection;
|
|
74
|
+
/**
|
|
75
|
+
* CollectionId -> State.
|
|
76
|
+
*/
|
|
77
|
+
_perCollectionStates = /* @__PURE__ */ new Map();
|
|
78
|
+
_activeCollections = /* @__PURE__ */ new Set();
|
|
79
|
+
_connectedPeers = /* @__PURE__ */ new Set();
|
|
80
|
+
remoteStateUpdated = new Event();
|
|
75
81
|
constructor(params) {
|
|
76
82
|
super();
|
|
77
|
-
/**
|
|
78
|
-
* CollectionId -> State.
|
|
79
|
-
*/
|
|
80
|
-
this._perCollectionStates = /* @__PURE__ */ new Map();
|
|
81
|
-
this._activeCollections = /* @__PURE__ */ new Set();
|
|
82
|
-
this._connectedPeers = /* @__PURE__ */ new Set();
|
|
83
|
-
this.remoteStateUpdated = new Event();
|
|
84
83
|
this._sendCollectionState = params.sendCollectionState;
|
|
85
84
|
this._queryCollectionState = params.queryCollectionState;
|
|
86
85
|
this._shouldSyncCollection = params.shouldSyncCollection;
|
|
@@ -110,7 +109,7 @@ var CollectionSynchronizer = class extends Resource {
|
|
|
110
109
|
state
|
|
111
110
|
}, {
|
|
112
111
|
F: __dxlog_file,
|
|
113
|
-
L:
|
|
112
|
+
L: 78,
|
|
114
113
|
S: this,
|
|
115
114
|
C: (f, a) => f(...a)
|
|
116
115
|
});
|
|
@@ -129,7 +128,7 @@ var CollectionSynchronizer = class extends Resource {
|
|
|
129
128
|
collectionId
|
|
130
129
|
}, {
|
|
131
130
|
F: __dxlog_file,
|
|
132
|
-
L:
|
|
131
|
+
L: 92,
|
|
133
132
|
S: this,
|
|
134
133
|
C: (f, a) => f(...a)
|
|
135
134
|
});
|
|
@@ -212,7 +211,7 @@ var CollectionSynchronizer = class extends Resource {
|
|
|
212
211
|
state
|
|
213
212
|
}, {
|
|
214
213
|
F: __dxlog_file,
|
|
215
|
-
L:
|
|
214
|
+
L: 173,
|
|
216
215
|
S: this,
|
|
217
216
|
C: (f, a) => f(...a)
|
|
218
217
|
});
|
|
@@ -268,19 +267,23 @@ CollectionSynchronizer = _ts_decorate([
|
|
|
268
267
|
trace.resource()
|
|
269
268
|
], CollectionSynchronizer);
|
|
270
269
|
var diffCollectionState = (local, remote) => {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
270
|
+
const localDocuments = Record.filter(local.documents, (heads) => heads.length > 0);
|
|
271
|
+
const remoteDocuments = Record.filter(remote.documents, (heads) => heads.length > 0);
|
|
272
|
+
const allDocuments = [
|
|
273
|
+
.../* @__PURE__ */ new Set([
|
|
274
|
+
...Record.keys(localDocuments),
|
|
275
|
+
...Record.keys(remoteDocuments)
|
|
276
|
+
])
|
|
277
|
+
];
|
|
275
278
|
const missingOnRemote = [];
|
|
276
279
|
const missingOnLocal = [];
|
|
277
280
|
const different = [];
|
|
278
281
|
for (const documentId of allDocuments) {
|
|
279
|
-
if (!
|
|
282
|
+
if (!localDocuments[documentId]) {
|
|
280
283
|
missingOnLocal.push(documentId);
|
|
281
|
-
} else if (!
|
|
284
|
+
} else if (!remoteDocuments[documentId]) {
|
|
282
285
|
missingOnRemote.push(documentId);
|
|
283
|
-
} else if (!
|
|
286
|
+
} else if (!A.equals(local.documents[documentId], remote.documents[documentId])) {
|
|
284
287
|
different.push(documentId);
|
|
285
288
|
}
|
|
286
289
|
}
|
|
@@ -295,7 +298,7 @@ var validateCollectionState = (state) => {
|
|
|
295
298
|
if (!isValidDocumentId(documentId)) {
|
|
296
299
|
throw new Error(`Invalid documentId: ${documentId}`);
|
|
297
300
|
}
|
|
298
|
-
if (
|
|
301
|
+
if (Array2.isArray(heads) && heads.some((head) => typeof head !== "string")) {
|
|
299
302
|
throw new Error(`Invalid heads: ${heads}`);
|
|
300
303
|
}
|
|
301
304
|
});
|
|
@@ -309,7 +312,7 @@ var getSpanName = (peerId) => {
|
|
|
309
312
|
|
|
310
313
|
// src/automerge/echo-network-adapter.ts
|
|
311
314
|
import { NetworkAdapter } from "@automerge/automerge-repo";
|
|
312
|
-
import {
|
|
315
|
+
import { Trigger, synchronized } from "@dxos/async";
|
|
313
316
|
import { LifecycleState } from "@dxos/context";
|
|
314
317
|
import { invariant } from "@dxos/invariant";
|
|
315
318
|
import { log as log2 } from "@dxos/log";
|
|
@@ -329,8 +332,17 @@ function _ts_decorate2(decorators, target, key, desc) {
|
|
|
329
332
|
}
|
|
330
333
|
var __dxlog_file2 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts";
|
|
331
334
|
var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
335
|
+
_params;
|
|
336
|
+
_replicators = /* @__PURE__ */ new Set();
|
|
337
|
+
/**
|
|
338
|
+
* Remote peer id -> connection.
|
|
339
|
+
*/
|
|
340
|
+
_connections = /* @__PURE__ */ new Map();
|
|
341
|
+
_lifecycleState = LifecycleState.CLOSED;
|
|
342
|
+
_connected = new Trigger();
|
|
343
|
+
_ready = new Trigger();
|
|
332
344
|
constructor(_params) {
|
|
333
|
-
super(), this._params = _params
|
|
345
|
+
super(), this._params = _params;
|
|
334
346
|
}
|
|
335
347
|
isReady() {
|
|
336
348
|
return this._lifecycleState === LifecycleState.OPEN;
|
|
@@ -380,7 +392,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
|
380
392
|
async addReplicator(replicator) {
|
|
381
393
|
invariant(this._lifecycleState === LifecycleState.OPEN, void 0, {
|
|
382
394
|
F: __dxlog_file2,
|
|
383
|
-
L:
|
|
395
|
+
L: 137,
|
|
384
396
|
S: this,
|
|
385
397
|
A: [
|
|
386
398
|
"this._lifecycleState === LifecycleState.OPEN",
|
|
@@ -389,7 +401,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
|
389
401
|
});
|
|
390
402
|
invariant(this.peerId, void 0, {
|
|
391
403
|
F: __dxlog_file2,
|
|
392
|
-
L:
|
|
404
|
+
L: 138,
|
|
393
405
|
S: this,
|
|
394
406
|
A: [
|
|
395
407
|
"this.peerId",
|
|
@@ -398,7 +410,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
|
398
410
|
});
|
|
399
411
|
invariant(!this._replicators.has(replicator), void 0, {
|
|
400
412
|
F: __dxlog_file2,
|
|
401
|
-
L:
|
|
413
|
+
L: 139,
|
|
402
414
|
S: this,
|
|
403
415
|
A: [
|
|
404
416
|
"!this._replicators.has(replicator)",
|
|
@@ -422,7 +434,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
|
422
434
|
async removeReplicator(replicator) {
|
|
423
435
|
invariant(this._lifecycleState === LifecycleState.OPEN, void 0, {
|
|
424
436
|
F: __dxlog_file2,
|
|
425
|
-
L:
|
|
437
|
+
L: 158,
|
|
426
438
|
S: this,
|
|
427
439
|
A: [
|
|
428
440
|
"this._lifecycleState === LifecycleState.OPEN",
|
|
@@ -431,7 +443,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
|
431
443
|
});
|
|
432
444
|
invariant(this._replicators.has(replicator), void 0, {
|
|
433
445
|
F: __dxlog_file2,
|
|
434
|
-
L:
|
|
446
|
+
L: 159,
|
|
435
447
|
S: this,
|
|
436
448
|
A: [
|
|
437
449
|
"this._replicators.has(replicator)",
|
|
@@ -482,6 +494,27 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
|
482
494
|
}) ? connection.connection.peerId : null;
|
|
483
495
|
}).filter(isNonNullable);
|
|
484
496
|
}
|
|
497
|
+
bundleSyncEnabledForPeer(peerId) {
|
|
498
|
+
const connection = this._connections.get(peerId);
|
|
499
|
+
if (!connection) {
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
return connection.connection.bundleSyncEnabled;
|
|
503
|
+
}
|
|
504
|
+
async pushBundle(peerId, bundle) {
|
|
505
|
+
const connection = this._connections.get(peerId);
|
|
506
|
+
if (!connection) {
|
|
507
|
+
throw new Error("Connection not found.");
|
|
508
|
+
}
|
|
509
|
+
return connection.connection.pushBundle(bundle);
|
|
510
|
+
}
|
|
511
|
+
async pullBundle(peerId, docHeads) {
|
|
512
|
+
const connection = this._connections.get(peerId);
|
|
513
|
+
if (!connection) {
|
|
514
|
+
throw new Error("Connection not found.");
|
|
515
|
+
}
|
|
516
|
+
return connection.connection.pullBundle(docHeads);
|
|
517
|
+
}
|
|
485
518
|
_send(message) {
|
|
486
519
|
const connectionEntry = this._connections.get(message.targetId);
|
|
487
520
|
if (!connectionEntry) {
|
|
@@ -494,7 +527,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
|
494
527
|
if (connectionEntry.isOpen) {
|
|
495
528
|
log2.catch(err, void 0, {
|
|
496
529
|
F: __dxlog_file2,
|
|
497
|
-
L:
|
|
530
|
+
L: 253,
|
|
498
531
|
S: this,
|
|
499
532
|
C: (f, a) => f(...a)
|
|
500
533
|
});
|
|
@@ -507,13 +540,13 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
|
507
540
|
peerId: connection.peerId
|
|
508
541
|
}, {
|
|
509
542
|
F: __dxlog_file2,
|
|
510
|
-
L:
|
|
543
|
+
L: 261,
|
|
511
544
|
S: this,
|
|
512
545
|
C: (f, a) => f(...a)
|
|
513
546
|
});
|
|
514
547
|
invariant(!this._connections.has(connection.peerId), void 0, {
|
|
515
548
|
F: __dxlog_file2,
|
|
516
|
-
L:
|
|
549
|
+
L: 262,
|
|
517
550
|
S: this,
|
|
518
551
|
A: [
|
|
519
552
|
"!this._connections.has(connection.peerId as PeerId)",
|
|
@@ -540,7 +573,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
|
540
573
|
if (connectionEntry.isOpen) {
|
|
541
574
|
log2.catch(err, void 0, {
|
|
542
575
|
F: __dxlog_file2,
|
|
543
|
-
L:
|
|
576
|
+
L: 286,
|
|
544
577
|
S: this,
|
|
545
578
|
C: (f, a) => f(...a)
|
|
546
579
|
});
|
|
@@ -551,7 +584,7 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
|
551
584
|
peerId: connection.peerId
|
|
552
585
|
}, {
|
|
553
586
|
F: __dxlog_file2,
|
|
554
|
-
L:
|
|
587
|
+
L: 291,
|
|
555
588
|
S: this,
|
|
556
589
|
C: (f, a) => f(...a)
|
|
557
590
|
});
|
|
@@ -573,14 +606,14 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
|
573
606
|
peerId: connection.peerId
|
|
574
607
|
}, {
|
|
575
608
|
F: __dxlog_file2,
|
|
576
|
-
L:
|
|
609
|
+
L: 308,
|
|
577
610
|
S: this,
|
|
578
611
|
C: (f, a) => f(...a)
|
|
579
612
|
});
|
|
580
613
|
const entry = this._connections.get(connection.peerId);
|
|
581
614
|
invariant(entry, void 0, {
|
|
582
615
|
F: __dxlog_file2,
|
|
583
|
-
L:
|
|
616
|
+
L: 310,
|
|
584
617
|
S: this,
|
|
585
618
|
A: [
|
|
586
619
|
"entry",
|
|
@@ -594,13 +627,13 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
|
594
627
|
this._params.monitor?.recordPeerDisconnected(connection.peerId);
|
|
595
628
|
void entry.writer.abort().catch((err) => log2.catch(err, void 0, {
|
|
596
629
|
F: __dxlog_file2,
|
|
597
|
-
L:
|
|
630
|
+
L: 316,
|
|
598
631
|
S: this,
|
|
599
632
|
C: (f, a) => f(...a)
|
|
600
633
|
}));
|
|
601
634
|
void entry.reader.cancel().catch((err) => log2.catch(err, void 0, {
|
|
602
635
|
F: __dxlog_file2,
|
|
603
|
-
L:
|
|
636
|
+
L: 317,
|
|
604
637
|
S: this,
|
|
605
638
|
C: (f, a) => f(...a)
|
|
606
639
|
}));
|
|
@@ -615,14 +648,14 @@ var EchoNetworkAdapter = class extends NetworkAdapter {
|
|
|
615
648
|
peerId: connection.peerId
|
|
616
649
|
}, {
|
|
617
650
|
F: __dxlog_file2,
|
|
618
|
-
L:
|
|
651
|
+
L: 327,
|
|
619
652
|
S: this,
|
|
620
653
|
C: (f, a) => f(...a)
|
|
621
654
|
});
|
|
622
655
|
const entry = this._connections.get(connection.peerId);
|
|
623
656
|
invariant(entry, void 0, {
|
|
624
657
|
F: __dxlog_file2,
|
|
625
|
-
L:
|
|
658
|
+
L: 329,
|
|
626
659
|
S: this,
|
|
627
660
|
A: [
|
|
628
661
|
"entry",
|
|
@@ -662,6 +695,7 @@ var isEchoPeerMetadata = (metadata) => metadata?.dxos_peerSource === "EchoNetwor
|
|
|
662
695
|
// src/automerge/heads-store.ts
|
|
663
696
|
import { headsEncoding } from "@dxos/indexing";
|
|
664
697
|
var HeadsStore = class {
|
|
698
|
+
_db;
|
|
665
699
|
constructor({ db }) {
|
|
666
700
|
this._db = db;
|
|
667
701
|
}
|
|
@@ -682,14 +716,15 @@ var HeadsStore = class {
|
|
|
682
716
|
};
|
|
683
717
|
|
|
684
718
|
// src/automerge/leveldb-storage-adapter.ts
|
|
685
|
-
import {
|
|
719
|
+
import { Resource as Resource2 } from "@dxos/context";
|
|
686
720
|
var LevelDBStorageAdapter = class extends Resource2 {
|
|
721
|
+
_params;
|
|
687
722
|
constructor(_params) {
|
|
688
723
|
super(), this._params = _params;
|
|
689
724
|
}
|
|
690
725
|
async load(keyArray) {
|
|
691
726
|
try {
|
|
692
|
-
if (this.
|
|
727
|
+
if (!this.isOpen) {
|
|
693
728
|
return void 0;
|
|
694
729
|
}
|
|
695
730
|
const startMs = Date.now();
|
|
@@ -707,7 +742,7 @@ var LevelDBStorageAdapter = class extends Resource2 {
|
|
|
707
742
|
}
|
|
708
743
|
}
|
|
709
744
|
async save(keyArray, binary) {
|
|
710
|
-
if (this.
|
|
745
|
+
if (!this.isOpen) {
|
|
711
746
|
return void 0;
|
|
712
747
|
}
|
|
713
748
|
const startMs = Date.now();
|
|
@@ -725,7 +760,7 @@ var LevelDBStorageAdapter = class extends Resource2 {
|
|
|
725
760
|
this._params.monitor?.recordStoreDuration(Date.now() - startMs);
|
|
726
761
|
}
|
|
727
762
|
async remove(keyArray) {
|
|
728
|
-
if (this.
|
|
763
|
+
if (!this.isOpen) {
|
|
729
764
|
return void 0;
|
|
730
765
|
}
|
|
731
766
|
await this._params.db.del(keyArray, {
|
|
@@ -733,7 +768,7 @@ var LevelDBStorageAdapter = class extends Resource2 {
|
|
|
733
768
|
});
|
|
734
769
|
}
|
|
735
770
|
async loadRange(keyPrefix) {
|
|
736
|
-
if (this.
|
|
771
|
+
if (!this.isOpen) {
|
|
737
772
|
return [];
|
|
738
773
|
}
|
|
739
774
|
const startMs = Date.now();
|
|
@@ -756,7 +791,7 @@ var LevelDBStorageAdapter = class extends Resource2 {
|
|
|
756
791
|
return result;
|
|
757
792
|
}
|
|
758
793
|
async removeRange(keyPrefix) {
|
|
759
|
-
if (this.
|
|
794
|
+
if (!this.isOpen) {
|
|
760
795
|
return void 0;
|
|
761
796
|
}
|
|
762
797
|
const batch = this._params.db.batch();
|
|
@@ -800,19 +835,38 @@ var FIND_PARAMS = {
|
|
|
800
835
|
"requesting"
|
|
801
836
|
]
|
|
802
837
|
};
|
|
838
|
+
var BUNDLE_SIZE = 100;
|
|
839
|
+
var BUNDLE_SYNC_CONCURRENCY = 2;
|
|
840
|
+
var BUNDLE_SYNC_THRESHOLD = 50;
|
|
803
841
|
var AutomergeHost = class extends Resource3 {
|
|
842
|
+
_db;
|
|
843
|
+
_indexMetadataStore;
|
|
844
|
+
_echoNetworkAdapter;
|
|
845
|
+
_collectionSynchronizer = new CollectionSynchronizer({
|
|
846
|
+
queryCollectionState: this._queryCollectionState.bind(this),
|
|
847
|
+
sendCollectionState: this._sendCollectionState.bind(this),
|
|
848
|
+
shouldSyncCollection: this._shouldSyncCollection.bind(this)
|
|
849
|
+
});
|
|
850
|
+
_repo;
|
|
851
|
+
_storage;
|
|
852
|
+
_headsStore;
|
|
853
|
+
_syncTask = void 0;
|
|
854
|
+
/**
|
|
855
|
+
* Cache of collections that would be synced on next sync task run.
|
|
856
|
+
*/
|
|
857
|
+
_collectionsToSync = new ComplexSet(({ collectionId, peerId }) => `${collectionId}|${peerId}`);
|
|
858
|
+
_peerId;
|
|
859
|
+
_peerIdProvider;
|
|
860
|
+
_getSpaceKeyByRootDocumentId;
|
|
861
|
+
collectionStateUpdated = new Event2();
|
|
862
|
+
/**
|
|
863
|
+
* Fired after a batch of documents was saved to disk.
|
|
864
|
+
*/
|
|
865
|
+
documentsSaved = new Event2();
|
|
866
|
+
_headsUpdates = /* @__PURE__ */ new Map();
|
|
867
|
+
_onHeadsChangedTask;
|
|
804
868
|
constructor({ db, indexMetadataStore, dataMonitor, peerIdProvider, getSpaceKeyByRootDocumentId }) {
|
|
805
869
|
super();
|
|
806
|
-
this._collectionSynchronizer = new CollectionSynchronizer({
|
|
807
|
-
queryCollectionState: this._queryCollectionState.bind(this),
|
|
808
|
-
sendCollectionState: this._sendCollectionState.bind(this),
|
|
809
|
-
shouldSyncCollection: this._shouldSyncCollection.bind(this)
|
|
810
|
-
});
|
|
811
|
-
this.collectionStateUpdated = new Event2();
|
|
812
|
-
/**
|
|
813
|
-
* Fired after a batch of documents was saved to disk.
|
|
814
|
-
*/
|
|
815
|
-
this.documentsSaved = new Event2();
|
|
816
870
|
this._db = db;
|
|
817
871
|
this._storage = new LevelDBStorageAdapter({
|
|
818
872
|
db: db.sublevel("automerge"),
|
|
@@ -838,6 +892,11 @@ var AutomergeHost = class extends Resource3 {
|
|
|
838
892
|
}
|
|
839
893
|
async _open() {
|
|
840
894
|
this._peerId = `host-${this._peerIdProvider?.() ?? PublicKey.random().toHex()}`;
|
|
895
|
+
this._onHeadsChangedTask = new DeferredTask(this._ctx, async () => {
|
|
896
|
+
const docHeads = Array.from(this._headsUpdates.entries());
|
|
897
|
+
this._headsUpdates.clear();
|
|
898
|
+
this._onHeadsChanged(docHeads);
|
|
899
|
+
});
|
|
841
900
|
await this._storage.open?.();
|
|
842
901
|
this._repo = new Repo({
|
|
843
902
|
peerId: this._peerId,
|
|
@@ -865,6 +924,28 @@ var AutomergeHost = class extends Resource3 {
|
|
|
865
924
|
}
|
|
866
925
|
}
|
|
867
926
|
});
|
|
927
|
+
this._syncTask = new DeferredTask(this._ctx, async () => {
|
|
928
|
+
const collectionToSync = Array.from(this._collectionsToSync.values());
|
|
929
|
+
if (collectionToSync.length === 0) {
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
await Promise.all(collectionToSync.map(async ({ collectionId, peerId }) => {
|
|
933
|
+
try {
|
|
934
|
+
await this._handleCollectionSync(collectionId, peerId);
|
|
935
|
+
} catch (err) {
|
|
936
|
+
log3.error("failed to sync collection", {
|
|
937
|
+
collectionId,
|
|
938
|
+
peerId,
|
|
939
|
+
err
|
|
940
|
+
}, {
|
|
941
|
+
F: __dxlog_file3,
|
|
942
|
+
L: 223,
|
|
943
|
+
S: this,
|
|
944
|
+
C: (f, a) => f(...a)
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
}));
|
|
948
|
+
});
|
|
868
949
|
await this._echoNetworkAdapter.open();
|
|
869
950
|
await this._collectionSynchronizer.open();
|
|
870
951
|
await this._echoNetworkAdapter.open();
|
|
@@ -874,7 +955,8 @@ var AutomergeHost = class extends Resource3 {
|
|
|
874
955
|
await this._collectionSynchronizer.close();
|
|
875
956
|
await this._storage.close?.();
|
|
876
957
|
await this._echoNetworkAdapter.close();
|
|
877
|
-
|
|
958
|
+
this._syncTask = void 0;
|
|
959
|
+
this._onHeadsChangedTask = void 0;
|
|
878
960
|
}
|
|
879
961
|
/**
|
|
880
962
|
* @deprecated To be abstracted away.
|
|
@@ -889,15 +971,42 @@ var AutomergeHost = class extends Resource3 {
|
|
|
889
971
|
return Object.keys(this._repo.handles).length;
|
|
890
972
|
}
|
|
891
973
|
async addReplicator(replicator) {
|
|
974
|
+
invariant2(this.isOpen, "AutomergeHost is not open", {
|
|
975
|
+
F: __dxlog_file3,
|
|
976
|
+
L: 259,
|
|
977
|
+
S: this,
|
|
978
|
+
A: [
|
|
979
|
+
"this.isOpen",
|
|
980
|
+
"'AutomergeHost is not open'"
|
|
981
|
+
]
|
|
982
|
+
});
|
|
892
983
|
await this._echoNetworkAdapter.addReplicator(replicator);
|
|
893
984
|
}
|
|
894
985
|
async removeReplicator(replicator) {
|
|
986
|
+
invariant2(this.isOpen, "AutomergeHost is not open", {
|
|
987
|
+
F: __dxlog_file3,
|
|
988
|
+
L: 264,
|
|
989
|
+
S: this,
|
|
990
|
+
A: [
|
|
991
|
+
"this.isOpen",
|
|
992
|
+
"'AutomergeHost is not open'"
|
|
993
|
+
]
|
|
994
|
+
});
|
|
895
995
|
await this._echoNetworkAdapter.removeReplicator(replicator);
|
|
896
996
|
}
|
|
897
997
|
/**
|
|
898
998
|
* Loads the document handle from the repo and waits for it to be ready.
|
|
899
999
|
*/
|
|
900
1000
|
async loadDoc(ctx, documentId, opts) {
|
|
1001
|
+
invariant2(this.isOpen, "AutomergeHost is not open", {
|
|
1002
|
+
F: __dxlog_file3,
|
|
1003
|
+
L: 272,
|
|
1004
|
+
S: this,
|
|
1005
|
+
A: [
|
|
1006
|
+
"this.isOpen",
|
|
1007
|
+
"'AutomergeHost is not open'"
|
|
1008
|
+
]
|
|
1009
|
+
});
|
|
901
1010
|
let handle;
|
|
902
1011
|
if (typeof documentId === "string") {
|
|
903
1012
|
handle = this._repo.handles[documentId];
|
|
@@ -915,6 +1024,15 @@ var AutomergeHost = class extends Resource3 {
|
|
|
915
1024
|
return handle;
|
|
916
1025
|
}
|
|
917
1026
|
async exportDoc(ctx, id) {
|
|
1027
|
+
invariant2(this.isOpen, "AutomergeHost is not open", {
|
|
1028
|
+
F: __dxlog_file3,
|
|
1029
|
+
L: 295,
|
|
1030
|
+
S: this,
|
|
1031
|
+
A: [
|
|
1032
|
+
"this.isOpen",
|
|
1033
|
+
"'AutomergeHost is not open'"
|
|
1034
|
+
]
|
|
1035
|
+
});
|
|
918
1036
|
const documentId = interpretAsDocumentId(id);
|
|
919
1037
|
const chunks = await this._storage.loadRange([
|
|
920
1038
|
documentId
|
|
@@ -925,6 +1043,15 @@ var AutomergeHost = class extends Resource3 {
|
|
|
925
1043
|
* Create new persisted document.
|
|
926
1044
|
*/
|
|
927
1045
|
createDoc(initialValue, opts) {
|
|
1046
|
+
invariant2(this.isOpen, "AutomergeHost is not open", {
|
|
1047
|
+
F: __dxlog_file3,
|
|
1048
|
+
L: 306,
|
|
1049
|
+
S: this,
|
|
1050
|
+
A: [
|
|
1051
|
+
"this.isOpen",
|
|
1052
|
+
"'AutomergeHost is not open'"
|
|
1053
|
+
]
|
|
1054
|
+
});
|
|
928
1055
|
if (opts?.preserveHistory) {
|
|
929
1056
|
if (initialValue instanceof Uint8Array) {
|
|
930
1057
|
return this._repo.import(initialValue);
|
|
@@ -941,6 +1068,15 @@ var AutomergeHost = class extends Resource3 {
|
|
|
941
1068
|
}
|
|
942
1069
|
}
|
|
943
1070
|
async waitUntilHeadsReplicated(heads) {
|
|
1071
|
+
invariant2(this.isOpen, "AutomergeHost is not open", {
|
|
1072
|
+
F: __dxlog_file3,
|
|
1073
|
+
L: 328,
|
|
1074
|
+
S: this,
|
|
1075
|
+
A: [
|
|
1076
|
+
"this.isOpen",
|
|
1077
|
+
"'AutomergeHost is not open'"
|
|
1078
|
+
]
|
|
1079
|
+
});
|
|
944
1080
|
const entries = heads.entries;
|
|
945
1081
|
if (!entries?.length) {
|
|
946
1082
|
return;
|
|
@@ -956,10 +1092,10 @@ var AutomergeHost = class extends Resource3 {
|
|
|
956
1092
|
return !(currentHeads !== null && headsEquals(currentHeads, targetHeads));
|
|
957
1093
|
});
|
|
958
1094
|
if (headsToWait.length > 0) {
|
|
959
|
-
await Promise.all(headsToWait.map(async (entry
|
|
1095
|
+
await Promise.all(headsToWait.map(async (entry) => {
|
|
960
1096
|
const handle = await this.loadDoc(Context.default(void 0, {
|
|
961
1097
|
F: __dxlog_file3,
|
|
962
|
-
L:
|
|
1098
|
+
L: 346
|
|
963
1099
|
}), entry.documentId);
|
|
964
1100
|
await waitForHeads(handle, entry.heads);
|
|
965
1101
|
}));
|
|
@@ -967,12 +1103,21 @@ var AutomergeHost = class extends Resource3 {
|
|
|
967
1103
|
await this._repo.flush(documentIds.filter((documentId) => this._repo.handles[documentId] && this._repo.handles[documentId].isReady()));
|
|
968
1104
|
}
|
|
969
1105
|
async reIndexHeads(documentIds) {
|
|
1106
|
+
invariant2(this.isOpen, "AutomergeHost is not open", {
|
|
1107
|
+
F: __dxlog_file3,
|
|
1108
|
+
L: 359,
|
|
1109
|
+
S: this,
|
|
1110
|
+
A: [
|
|
1111
|
+
"this.isOpen",
|
|
1112
|
+
"'AutomergeHost is not open'"
|
|
1113
|
+
]
|
|
1114
|
+
});
|
|
970
1115
|
for (const documentId of documentIds) {
|
|
971
1116
|
log3("re-indexing heads for document", {
|
|
972
1117
|
documentId
|
|
973
1118
|
}, {
|
|
974
1119
|
F: __dxlog_file3,
|
|
975
|
-
L:
|
|
1120
|
+
L: 361,
|
|
976
1121
|
S: this,
|
|
977
1122
|
C: (f, a) => f(...a)
|
|
978
1123
|
});
|
|
@@ -982,7 +1127,7 @@ var AutomergeHost = class extends Resource3 {
|
|
|
982
1127
|
documentId
|
|
983
1128
|
}, {
|
|
984
1129
|
F: __dxlog_file3,
|
|
985
|
-
L:
|
|
1130
|
+
L: 364,
|
|
986
1131
|
S: this,
|
|
987
1132
|
C: (f, a) => f(...a)
|
|
988
1133
|
});
|
|
@@ -995,7 +1140,7 @@ var AutomergeHost = class extends Resource3 {
|
|
|
995
1140
|
}
|
|
996
1141
|
log3("done re-indexing heads", void 0, {
|
|
997
1142
|
F: __dxlog_file3,
|
|
998
|
-
L:
|
|
1143
|
+
L: 373,
|
|
999
1144
|
S: this,
|
|
1000
1145
|
C: (f, a) => f(...a)
|
|
1001
1146
|
});
|
|
@@ -1005,9 +1150,6 @@ var AutomergeHost = class extends Resource3 {
|
|
|
1005
1150
|
// NOTE: If both peers return sharePolicy=false the replication will not happen
|
|
1006
1151
|
// https://github.com/automerge/automerge-repo/pull/292
|
|
1007
1152
|
async _sharePolicy(peerId, documentId) {
|
|
1008
|
-
if (peerId.startsWith("client-")) {
|
|
1009
|
-
return false;
|
|
1010
|
-
}
|
|
1011
1153
|
if (!documentId) {
|
|
1012
1154
|
return false;
|
|
1013
1155
|
}
|
|
@@ -1056,13 +1198,31 @@ var AutomergeHost = class extends Resource3 {
|
|
|
1056
1198
|
* Called by AutomergeStorageAdapter after levelDB batch commit.
|
|
1057
1199
|
*/
|
|
1058
1200
|
async _afterSave(path) {
|
|
1201
|
+
if (!this.isOpen) {
|
|
1202
|
+
return void 0;
|
|
1203
|
+
}
|
|
1059
1204
|
this._indexMetadataStore.notifyMarkedDirty();
|
|
1060
1205
|
const documentId = path[0];
|
|
1061
|
-
const
|
|
1062
|
-
if (
|
|
1063
|
-
|
|
1064
|
-
|
|
1206
|
+
const handle = this._repo.handles[documentId];
|
|
1207
|
+
if (!handle || !handle.isReady()) {
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
const document = handle.doc();
|
|
1211
|
+
if (!document) {
|
|
1212
|
+
return;
|
|
1065
1213
|
}
|
|
1214
|
+
const heads = getHeads(document);
|
|
1215
|
+
this._headsUpdates.set(documentId, heads);
|
|
1216
|
+
invariant2(this._onHeadsChangedTask, "onHeadsChangedTask is not initialized", {
|
|
1217
|
+
F: __dxlog_file3,
|
|
1218
|
+
L: 445,
|
|
1219
|
+
S: this,
|
|
1220
|
+
A: [
|
|
1221
|
+
"this._onHeadsChangedTask",
|
|
1222
|
+
"'onHeadsChangedTask is not initialized'"
|
|
1223
|
+
]
|
|
1224
|
+
});
|
|
1225
|
+
this._onHeadsChangedTask.schedule();
|
|
1066
1226
|
this.documentsSaved.emit();
|
|
1067
1227
|
}
|
|
1068
1228
|
_automergePeers() {
|
|
@@ -1099,11 +1259,12 @@ var AutomergeHost = class extends Resource3 {
|
|
|
1099
1259
|
* Flush documents to disk.
|
|
1100
1260
|
*/
|
|
1101
1261
|
async flush({ documentIds } = {}) {
|
|
1102
|
-
const loadedDocuments = documentIds
|
|
1262
|
+
const loadedDocuments = (documentIds ?? Object.keys(this._repo.handles)).filter((documentId) => {
|
|
1103
1263
|
const handle = this._repo.handles[documentId];
|
|
1104
1264
|
return handle && handle.isReady();
|
|
1105
1265
|
});
|
|
1106
1266
|
await this._repo.flush(loadedDocuments);
|
|
1267
|
+
await this._onHeadsChangedTask?.runBlocking();
|
|
1107
1268
|
}
|
|
1108
1269
|
async getHeads(documentIds) {
|
|
1109
1270
|
const result = [];
|
|
@@ -1155,8 +1316,17 @@ var AutomergeHost = class extends Resource3 {
|
|
|
1155
1316
|
missingOnRemote: diff.missingOnRemote.length,
|
|
1156
1317
|
missingOnLocal: diff.missingOnLocal.length,
|
|
1157
1318
|
differentDocuments: diff.different.length,
|
|
1158
|
-
localDocumentCount: Object.
|
|
1159
|
-
remoteDocumentCount: Object.
|
|
1319
|
+
localDocumentCount: Object.entries(localState.documents).filter(([_, heads]) => heads.length > 0).length,
|
|
1320
|
+
remoteDocumentCount: Object.entries(state.documents).filter(([_, heads]) => heads.length > 0).length,
|
|
1321
|
+
totalDocumentCount: (/* @__PURE__ */ new Set([
|
|
1322
|
+
...Object.keys(localState.documents),
|
|
1323
|
+
...Object.keys(state.documents)
|
|
1324
|
+
])).size,
|
|
1325
|
+
unsyncedDocumentCount: (/* @__PURE__ */ new Set([
|
|
1326
|
+
...diff.missingOnLocal,
|
|
1327
|
+
...diff.missingOnRemote,
|
|
1328
|
+
...diff.different
|
|
1329
|
+
])).size
|
|
1160
1330
|
});
|
|
1161
1331
|
}
|
|
1162
1332
|
return result;
|
|
@@ -1173,6 +1343,14 @@ var AutomergeHost = class extends Resource3 {
|
|
|
1173
1343
|
this._collectionSynchronizer.setLocalCollectionState(collectionId, {
|
|
1174
1344
|
documents
|
|
1175
1345
|
});
|
|
1346
|
+
const interestedPeers = this._echoNetworkAdapter.getPeersInterestedInCollection(collectionId);
|
|
1347
|
+
if (interestedPeers.length > 0) {
|
|
1348
|
+
for (const peerId of interestedPeers) {
|
|
1349
|
+
this._sendCollectionState(collectionId, peerId, {
|
|
1350
|
+
documents
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1176
1354
|
}
|
|
1177
1355
|
async clearLocalCollectionState(collectionId) {
|
|
1178
1356
|
this._collectionSynchronizer.clearLocalCollectionState(collectionId);
|
|
@@ -1196,42 +1374,231 @@ var AutomergeHost = class extends Resource3 {
|
|
|
1196
1374
|
this._collectionSynchronizer.onConnectionClosed(peerId);
|
|
1197
1375
|
}
|
|
1198
1376
|
_onRemoteCollectionStateUpdated(collectionId, peerId) {
|
|
1377
|
+
this._collectionsToSync.add({
|
|
1378
|
+
collectionId,
|
|
1379
|
+
peerId
|
|
1380
|
+
});
|
|
1381
|
+
this._syncTask?.schedule();
|
|
1382
|
+
}
|
|
1383
|
+
async _handleCollectionSync(collectionId, peerId) {
|
|
1199
1384
|
const localState = this._collectionSynchronizer.getLocalCollectionState(collectionId);
|
|
1200
1385
|
const remoteState = this._collectionSynchronizer.getRemoteCollectionStates(collectionId).get(peerId);
|
|
1201
1386
|
if (!localState || !remoteState) {
|
|
1202
1387
|
return;
|
|
1203
1388
|
}
|
|
1204
1389
|
const { different, missingOnLocal, missingOnRemote } = diffCollectionState(localState, remoteState);
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1390
|
+
if (different.length === 0 && missingOnLocal.length === 0 && missingOnRemote.length === 0) {
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
const toReplicateWithoutBatching = [
|
|
1208
1394
|
...different
|
|
1209
1395
|
];
|
|
1210
|
-
|
|
1396
|
+
const bundleSyncEnabled = this._echoNetworkAdapter.bundleSyncEnabledForPeer(peerId);
|
|
1397
|
+
if (bundleSyncEnabled && missingOnRemote.length >= BUNDLE_SYNC_THRESHOLD) {
|
|
1398
|
+
log3("pushing bundle", {
|
|
1399
|
+
amount: missingOnRemote.length
|
|
1400
|
+
}, {
|
|
1401
|
+
F: __dxlog_file3,
|
|
1402
|
+
L: 650,
|
|
1403
|
+
S: this,
|
|
1404
|
+
C: (f, a) => f(...a)
|
|
1405
|
+
});
|
|
1406
|
+
const { syncInteractively } = await this._pushInBundles(peerId, missingOnRemote);
|
|
1407
|
+
toReplicateWithoutBatching.push(...syncInteractively);
|
|
1408
|
+
} else {
|
|
1409
|
+
log3.verbose("failed to push bundle, replicating interactively", {
|
|
1410
|
+
collectionId,
|
|
1411
|
+
peerId,
|
|
1412
|
+
amount: missingOnRemote.length
|
|
1413
|
+
}, {
|
|
1414
|
+
F: __dxlog_file3,
|
|
1415
|
+
L: 654,
|
|
1416
|
+
S: this,
|
|
1417
|
+
C: (f, a) => f(...a)
|
|
1418
|
+
});
|
|
1419
|
+
toReplicateWithoutBatching.push(...missingOnRemote);
|
|
1420
|
+
}
|
|
1421
|
+
if (bundleSyncEnabled && missingOnLocal.length >= BUNDLE_SYNC_THRESHOLD) {
|
|
1422
|
+
log3("pulling bundle", {
|
|
1423
|
+
amount: missingOnLocal.length
|
|
1424
|
+
}, {
|
|
1425
|
+
F: __dxlog_file3,
|
|
1426
|
+
L: 662,
|
|
1427
|
+
S: this,
|
|
1428
|
+
C: (f, a) => f(...a)
|
|
1429
|
+
});
|
|
1430
|
+
const { syncInteractively } = await this._pullInBundles(peerId, missingOnLocal);
|
|
1431
|
+
toReplicateWithoutBatching.push(...syncInteractively);
|
|
1432
|
+
} else {
|
|
1433
|
+
log3.verbose("failed to pull bundle, replicating interactively", {
|
|
1434
|
+
collectionId,
|
|
1435
|
+
peerId,
|
|
1436
|
+
amount: missingOnLocal.length
|
|
1437
|
+
}, {
|
|
1438
|
+
F: __dxlog_file3,
|
|
1439
|
+
L: 666,
|
|
1440
|
+
S: this,
|
|
1441
|
+
C: (f, a) => f(...a)
|
|
1442
|
+
});
|
|
1443
|
+
toReplicateWithoutBatching.push(...missingOnLocal);
|
|
1444
|
+
}
|
|
1445
|
+
if (toReplicateWithoutBatching.length === 0) {
|
|
1211
1446
|
return;
|
|
1212
1447
|
}
|
|
1213
1448
|
log3("replicating documents after collection sync", {
|
|
1214
1449
|
collectionId,
|
|
1215
1450
|
peerId,
|
|
1216
|
-
|
|
1217
|
-
count:
|
|
1451
|
+
toReplicateWithoutBatching,
|
|
1452
|
+
count: toReplicateWithoutBatching.length
|
|
1218
1453
|
}, {
|
|
1219
1454
|
F: __dxlog_file3,
|
|
1220
|
-
L:
|
|
1455
|
+
L: 678,
|
|
1221
1456
|
S: this,
|
|
1222
1457
|
C: (f, a) => f(...a)
|
|
1223
1458
|
});
|
|
1224
|
-
for (const documentId of
|
|
1459
|
+
for (const documentId of toReplicateWithoutBatching) {
|
|
1225
1460
|
this._repo.findWithProgress(documentId);
|
|
1226
1461
|
}
|
|
1227
1462
|
}
|
|
1228
|
-
|
|
1463
|
+
// TODO(mykola): Add retries of batches https://gist.github.com/mykola-vrmchk/fde270259e9209fcbf1331e5abbf12cf
|
|
1464
|
+
// TODO(mykola): Use effect to retry batches.
|
|
1465
|
+
async _pushInBundles(peerId, documentIds) {
|
|
1466
|
+
const documentsToPush = [
|
|
1467
|
+
...documentIds
|
|
1468
|
+
];
|
|
1469
|
+
const syncInteractively = [];
|
|
1470
|
+
while (documentsToPush.length > 0) {
|
|
1471
|
+
await Promise.all(range(BUNDLE_SYNC_CONCURRENCY).map(async () => {
|
|
1472
|
+
const bundle = documentsToPush.splice(0, BUNDLE_SIZE);
|
|
1473
|
+
if (bundle.length === 0) {
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
await this._pushBundle(peerId, bundle).catch((err) => {
|
|
1477
|
+
log3.warn("failed to push bundle, replicating interactively", {
|
|
1478
|
+
peerId,
|
|
1479
|
+
bundle,
|
|
1480
|
+
err
|
|
1481
|
+
}, {
|
|
1482
|
+
F: __dxlog_file3,
|
|
1483
|
+
L: 709,
|
|
1484
|
+
S: this,
|
|
1485
|
+
C: (f, a) => f(...a)
|
|
1486
|
+
});
|
|
1487
|
+
syncInteractively.push(...bundle);
|
|
1488
|
+
});
|
|
1489
|
+
}));
|
|
1490
|
+
}
|
|
1491
|
+
return {
|
|
1492
|
+
syncInteractively
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
async _pushBundle(peerId, documentIds) {
|
|
1496
|
+
if (this._ctx.disposed) {
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
const docs = documentIds.map((documentId) => {
|
|
1500
|
+
const handle = this._repo.handles[documentId];
|
|
1501
|
+
if (!handle || !handle.isReady()) {
|
|
1502
|
+
log3.warn("document not ready, skipping", {
|
|
1503
|
+
documentId
|
|
1504
|
+
}, {
|
|
1505
|
+
F: __dxlog_file3,
|
|
1506
|
+
L: 727,
|
|
1507
|
+
S: this,
|
|
1508
|
+
C: (f, a) => f(...a)
|
|
1509
|
+
});
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1512
|
+
const doc = handle.doc();
|
|
1513
|
+
if (!doc) {
|
|
1514
|
+
log3.warn("document not available, skipping", {
|
|
1515
|
+
documentId
|
|
1516
|
+
}, {
|
|
1517
|
+
F: __dxlog_file3,
|
|
1518
|
+
L: 732,
|
|
1519
|
+
S: this,
|
|
1520
|
+
C: (f, a) => f(...a)
|
|
1521
|
+
});
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
return {
|
|
1525
|
+
documentId,
|
|
1526
|
+
data: save(doc),
|
|
1527
|
+
heads: getHeads(doc)
|
|
1528
|
+
};
|
|
1529
|
+
});
|
|
1530
|
+
await this._echoNetworkAdapter.pushBundle(peerId, docs.filter(isNonNullable2));
|
|
1531
|
+
}
|
|
1532
|
+
async _pullInBundles(peerId, documentIds) {
|
|
1533
|
+
const documentsToPull = [
|
|
1534
|
+
...documentIds
|
|
1535
|
+
];
|
|
1536
|
+
const syncInteractively = [];
|
|
1537
|
+
const docsToImport = {};
|
|
1538
|
+
while (documentsToPull.length > 0) {
|
|
1539
|
+
await Promise.all(range(BUNDLE_SYNC_CONCURRENCY).map(async () => {
|
|
1540
|
+
const bundle = documentsToPull.splice(0, BUNDLE_SIZE);
|
|
1541
|
+
if (bundle.length === 0) {
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
const result = await this._pullBundle(peerId, bundle).catch((err) => {
|
|
1545
|
+
log3.warn("failed to pull bundle, replicating interactively", {
|
|
1546
|
+
peerId,
|
|
1547
|
+
bundle,
|
|
1548
|
+
err
|
|
1549
|
+
}, {
|
|
1550
|
+
F: __dxlog_file3,
|
|
1551
|
+
L: 762,
|
|
1552
|
+
S: this,
|
|
1553
|
+
C: (f, a) => f(...a)
|
|
1554
|
+
});
|
|
1555
|
+
syncInteractively.push(...bundle);
|
|
1556
|
+
});
|
|
1557
|
+
if (result) {
|
|
1558
|
+
Object.assign(docsToImport, result.docsToImport);
|
|
1559
|
+
}
|
|
1560
|
+
}));
|
|
1561
|
+
}
|
|
1562
|
+
for (const [documentId, data] of Object.entries(docsToImport)) {
|
|
1563
|
+
this._repo.import(data, {
|
|
1564
|
+
docId: documentId
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
await this._repo.flush(Object.keys(docsToImport));
|
|
1568
|
+
return {
|
|
1569
|
+
syncInteractively
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
async _pullBundle(peerId, documentIds) {
|
|
1573
|
+
if (this._ctx.disposed) {
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
const docHeads = Object.fromEntries(documentIds.map((documentId) => [
|
|
1577
|
+
documentId,
|
|
1578
|
+
[]
|
|
1579
|
+
]));
|
|
1580
|
+
const bundle = await this._echoNetworkAdapter.pullBundle(peerId, docHeads);
|
|
1581
|
+
return {
|
|
1582
|
+
docsToImport: bundle
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
_onHeadsChanged(docHeads) {
|
|
1229
1586
|
const collectionsChanged = /* @__PURE__ */ new Set();
|
|
1230
1587
|
for (const collectionId of this._collectionSynchronizer.getRegisteredCollectionIds()) {
|
|
1231
1588
|
const state = this._collectionSynchronizer.getLocalCollectionState(collectionId);
|
|
1232
|
-
if (state
|
|
1233
|
-
|
|
1234
|
-
|
|
1589
|
+
if (!state) {
|
|
1590
|
+
continue;
|
|
1591
|
+
}
|
|
1592
|
+
let newState;
|
|
1593
|
+
for (const [documentId, heads] of docHeads) {
|
|
1594
|
+
if (documentId in state.documents) {
|
|
1595
|
+
if (!newState) {
|
|
1596
|
+
newState = structuredClone(state);
|
|
1597
|
+
}
|
|
1598
|
+
newState.documents[documentId] = heads;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
if (newState) {
|
|
1235
1602
|
this._collectionSynchronizer.setLocalCollectionState(collectionId, newState);
|
|
1236
1603
|
collectionsChanged.add(collectionId);
|
|
1237
1604
|
}
|
|
@@ -1277,7 +1644,7 @@ var changeIsPresentInDoc = (doc, changeHash) => {
|
|
|
1277
1644
|
var decodeCollectionState = (state) => {
|
|
1278
1645
|
invariant2(typeof state === "object" && state !== null, "Invalid state", {
|
|
1279
1646
|
F: __dxlog_file3,
|
|
1280
|
-
L:
|
|
1647
|
+
L: 845,
|
|
1281
1648
|
S: void 0,
|
|
1282
1649
|
A: [
|
|
1283
1650
|
"typeof state === 'object' && state !== null",
|
|
@@ -1294,10 +1661,10 @@ var encodeCollectionState = (state) => {
|
|
|
1294
1661
|
import { invariant as invariant5 } from "@dxos/invariant";
|
|
1295
1662
|
import { PublicKey as PublicKey2 } from "@dxos/keys";
|
|
1296
1663
|
import { log as log5 } from "@dxos/log";
|
|
1297
|
-
import { ComplexSet, defaultMap as defaultMap2 } from "@dxos/util";
|
|
1664
|
+
import { ComplexSet as ComplexSet2, defaultMap as defaultMap2 } from "@dxos/util";
|
|
1298
1665
|
|
|
1299
1666
|
// src/automerge/mesh-echo-replicator-connection.ts
|
|
1300
|
-
import * as
|
|
1667
|
+
import * as A2 from "@automerge/automerge";
|
|
1301
1668
|
import { cbor } from "@automerge/automerge-repo";
|
|
1302
1669
|
import { Resource as Resource4 } from "@dxos/context";
|
|
1303
1670
|
import { invariant as invariant3 } from "@dxos/invariant";
|
|
@@ -1306,8 +1673,15 @@ import { AutomergeReplicator } from "@dxos/teleport-extension-automerge-replicat
|
|
|
1306
1673
|
var __dxlog_file4 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator-connection.ts";
|
|
1307
1674
|
var DEFAULT_FACTORY = (params) => new AutomergeReplicator(...params);
|
|
1308
1675
|
var MeshReplicatorConnection = class extends Resource4 {
|
|
1676
|
+
_params;
|
|
1677
|
+
readable;
|
|
1678
|
+
writable;
|
|
1679
|
+
remoteDeviceKey = null;
|
|
1680
|
+
replicatorExtension;
|
|
1681
|
+
_remotePeerId = null;
|
|
1682
|
+
_isEnabled = false;
|
|
1309
1683
|
constructor(_params) {
|
|
1310
|
-
super(), this._params = _params
|
|
1684
|
+
super(), this._params = _params;
|
|
1311
1685
|
let readableStreamController;
|
|
1312
1686
|
this.readable = new ReadableStream({
|
|
1313
1687
|
start: (controller) => {
|
|
@@ -1391,6 +1765,9 @@ var MeshReplicatorConnection = class extends Resource4 {
|
|
|
1391
1765
|
get isEnabled() {
|
|
1392
1766
|
return this._isEnabled;
|
|
1393
1767
|
}
|
|
1768
|
+
get bundleSyncEnabled() {
|
|
1769
|
+
return false;
|
|
1770
|
+
}
|
|
1394
1771
|
async shouldAdvertise(params) {
|
|
1395
1772
|
return this._params.shouldAdvertise(params);
|
|
1396
1773
|
}
|
|
@@ -1404,7 +1781,7 @@ var MeshReplicatorConnection = class extends Resource4 {
|
|
|
1404
1781
|
enable() {
|
|
1405
1782
|
invariant3(this._remotePeerId != null, "Remote peer has not connected yet.", {
|
|
1406
1783
|
F: __dxlog_file4,
|
|
1407
|
-
L:
|
|
1784
|
+
L: 131,
|
|
1408
1785
|
S: this,
|
|
1409
1786
|
A: [
|
|
1410
1787
|
"this._remotePeerId != null",
|
|
@@ -1422,7 +1799,7 @@ var MeshReplicatorConnection = class extends Resource4 {
|
|
|
1422
1799
|
};
|
|
1423
1800
|
var logSendSync = (message) => {
|
|
1424
1801
|
log4("sendSyncMessage", () => {
|
|
1425
|
-
const decodedSyncMessage = message.type === "sync" && message.data ?
|
|
1802
|
+
const decodedSyncMessage = message.type === "sync" && message.data ? A2.decodeSyncMessage(message.data) : void 0;
|
|
1426
1803
|
return {
|
|
1427
1804
|
sync: decodedSyncMessage && {
|
|
1428
1805
|
headsLength: decodedSyncMessage.heads.length,
|
|
@@ -1435,7 +1812,7 @@ var logSendSync = (message) => {
|
|
|
1435
1812
|
};
|
|
1436
1813
|
}, {
|
|
1437
1814
|
F: __dxlog_file4,
|
|
1438
|
-
L:
|
|
1815
|
+
L: 144,
|
|
1439
1816
|
S: void 0,
|
|
1440
1817
|
C: (f, a) => f(...a)
|
|
1441
1818
|
});
|
|
@@ -1463,24 +1840,22 @@ var getSpaceIdFromCollectionId = (collectionId) => {
|
|
|
1463
1840
|
// src/automerge/mesh-echo-replicator.ts
|
|
1464
1841
|
var __dxlog_file6 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts";
|
|
1465
1842
|
var MeshEchoReplicator = class {
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
this._context = null;
|
|
1483
|
-
}
|
|
1843
|
+
/**
|
|
1844
|
+
* We might have multiple connections open with a peer (one per space), but there'll be only one enabled
|
|
1845
|
+
* connection at any given moment, because there's a single repo for all the spaces.
|
|
1846
|
+
* When a connection closes (space was closed) it gets removed from the list and the next connection
|
|
1847
|
+
* in the line gets enabled.
|
|
1848
|
+
*/
|
|
1849
|
+
_connectionsPerPeer = /* @__PURE__ */ new Map();
|
|
1850
|
+
/**
|
|
1851
|
+
* A set of all connections (enabled and disabled).
|
|
1852
|
+
*/
|
|
1853
|
+
_connections = /* @__PURE__ */ new Set();
|
|
1854
|
+
/**
|
|
1855
|
+
* spaceId -> deviceKey[]
|
|
1856
|
+
*/
|
|
1857
|
+
_authorizedDevices = /* @__PURE__ */ new Map();
|
|
1858
|
+
_context = null;
|
|
1484
1859
|
async connect(context) {
|
|
1485
1860
|
this._context = context;
|
|
1486
1861
|
}
|
|
@@ -1500,7 +1875,7 @@ var MeshEchoReplicator = class {
|
|
|
1500
1875
|
createExtension(extensionFactory) {
|
|
1501
1876
|
invariant5(this._context, void 0, {
|
|
1502
1877
|
F: __dxlog_file6,
|
|
1503
|
-
L:
|
|
1878
|
+
L: 68,
|
|
1504
1879
|
S: this,
|
|
1505
1880
|
A: [
|
|
1506
1881
|
"this._context",
|
|
@@ -1515,13 +1890,13 @@ var MeshEchoReplicator = class {
|
|
|
1515
1890
|
peerId: connection.peerId
|
|
1516
1891
|
}, {
|
|
1517
1892
|
F: __dxlog_file6,
|
|
1518
|
-
L:
|
|
1893
|
+
L: 74,
|
|
1519
1894
|
S: this,
|
|
1520
1895
|
C: (f, a) => f(...a)
|
|
1521
1896
|
});
|
|
1522
1897
|
invariant5(this._context, void 0, {
|
|
1523
1898
|
F: __dxlog_file6,
|
|
1524
|
-
L:
|
|
1899
|
+
L: 75,
|
|
1525
1900
|
S: this,
|
|
1526
1901
|
A: [
|
|
1527
1902
|
"this._context",
|
|
@@ -1546,7 +1921,7 @@ var MeshEchoReplicator = class {
|
|
|
1546
1921
|
peerId: connection.peerId
|
|
1547
1922
|
}, {
|
|
1548
1923
|
F: __dxlog_file6,
|
|
1549
|
-
L:
|
|
1924
|
+
L: 89,
|
|
1550
1925
|
S: this,
|
|
1551
1926
|
C: (f, a) => f(...a)
|
|
1552
1927
|
});
|
|
@@ -1558,7 +1933,7 @@ var MeshEchoReplicator = class {
|
|
|
1558
1933
|
peerId: connection.peerId
|
|
1559
1934
|
}, {
|
|
1560
1935
|
F: __dxlog_file6,
|
|
1561
|
-
L:
|
|
1936
|
+
L: 97,
|
|
1562
1937
|
S: this,
|
|
1563
1938
|
C: (f, a) => f(...a)
|
|
1564
1939
|
});
|
|
@@ -1580,13 +1955,13 @@ var MeshEchoReplicator = class {
|
|
|
1580
1955
|
documentId: params.documentId
|
|
1581
1956
|
}, {
|
|
1582
1957
|
F: __dxlog_file6,
|
|
1583
|
-
L:
|
|
1958
|
+
L: 115,
|
|
1584
1959
|
S: this,
|
|
1585
1960
|
C: (f, a) => f(...a)
|
|
1586
1961
|
});
|
|
1587
1962
|
invariant5(this._context, void 0, {
|
|
1588
1963
|
F: __dxlog_file6,
|
|
1589
|
-
L:
|
|
1964
|
+
L: 116,
|
|
1590
1965
|
S: this,
|
|
1591
1966
|
A: [
|
|
1592
1967
|
"this._context",
|
|
@@ -1606,7 +1981,7 @@ var MeshEchoReplicator = class {
|
|
|
1606
1981
|
acceptDocument: remoteDocumentExists
|
|
1607
1982
|
}, {
|
|
1608
1983
|
F: __dxlog_file6,
|
|
1609
|
-
L:
|
|
1984
|
+
L: 124,
|
|
1610
1985
|
S: this,
|
|
1611
1986
|
C: (f, a) => f(...a)
|
|
1612
1987
|
});
|
|
@@ -1620,7 +1995,7 @@ var MeshEchoReplicator = class {
|
|
|
1620
1995
|
documentId: params.documentId
|
|
1621
1996
|
}, {
|
|
1622
1997
|
F: __dxlog_file6,
|
|
1623
|
-
L:
|
|
1998
|
+
L: 141,
|
|
1624
1999
|
S: this,
|
|
1625
2000
|
C: (f, a) => f(...a)
|
|
1626
2001
|
});
|
|
@@ -1636,7 +2011,7 @@ var MeshEchoReplicator = class {
|
|
|
1636
2011
|
isAuthorized
|
|
1637
2012
|
}, {
|
|
1638
2013
|
F: __dxlog_file6,
|
|
1639
|
-
L:
|
|
2014
|
+
L: 149,
|
|
1640
2015
|
S: this,
|
|
1641
2016
|
C: (f, a) => f(...a)
|
|
1642
2017
|
});
|
|
@@ -1644,7 +2019,7 @@ var MeshEchoReplicator = class {
|
|
|
1644
2019
|
} catch (err) {
|
|
1645
2020
|
log5.catch(err, void 0, {
|
|
1646
2021
|
F: __dxlog_file6,
|
|
1647
|
-
L:
|
|
2022
|
+
L: 159,
|
|
1648
2023
|
S: this,
|
|
1649
2024
|
C: (f, a) => f(...a)
|
|
1650
2025
|
});
|
|
@@ -1660,7 +2035,7 @@ var MeshEchoReplicator = class {
|
|
|
1660
2035
|
collectionId
|
|
1661
2036
|
}, {
|
|
1662
2037
|
F: __dxlog_file6,
|
|
1663
|
-
L:
|
|
2038
|
+
L: 169,
|
|
1664
2039
|
S: this,
|
|
1665
2040
|
C: (f, a) => f(...a)
|
|
1666
2041
|
});
|
|
@@ -1679,12 +2054,12 @@ var MeshEchoReplicator = class {
|
|
|
1679
2054
|
deviceKey
|
|
1680
2055
|
}, {
|
|
1681
2056
|
F: __dxlog_file6,
|
|
1682
|
-
L:
|
|
2057
|
+
L: 186,
|
|
1683
2058
|
S: this,
|
|
1684
2059
|
C: (f, a) => f(...a)
|
|
1685
2060
|
});
|
|
1686
2061
|
const spaceId = await createIdFromSpaceKey(spaceKey);
|
|
1687
|
-
defaultMap2(this._authorizedDevices, spaceId, () => new
|
|
2062
|
+
defaultMap2(this._authorizedDevices, spaceId, () => new ComplexSet2(PublicKey2.hash)).add(deviceKey);
|
|
1688
2063
|
for (const connection of this._connections) {
|
|
1689
2064
|
if (connection.isEnabled && connection.remoteDeviceKey && connection.remoteDeviceKey.equals(deviceKey)) {
|
|
1690
2065
|
if (this._connectionsPerPeer.has(connection.peerId)) {
|
|
@@ -1697,7 +2072,7 @@ var MeshEchoReplicator = class {
|
|
|
1697
2072
|
|
|
1698
2073
|
// src/automerge/echo-data-monitor.ts
|
|
1699
2074
|
import { trace as trace3 } from "@dxos/tracing";
|
|
1700
|
-
import { CircularBuffer,
|
|
2075
|
+
import { CircularBuffer, SlidingWindowSummary, mapValues } from "@dxos/util";
|
|
1701
2076
|
function _ts_decorate4(decorators, target, key, desc) {
|
|
1702
2077
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1703
2078
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1707,19 +2082,21 @@ function _ts_decorate4(decorators, target, key, desc) {
|
|
|
1707
2082
|
var PER_SECOND_RATE_AVG_WINDOW_SIZE = 5;
|
|
1708
2083
|
var DEFAULT_AVG_WINDOW_SIZE = 25;
|
|
1709
2084
|
var EchoDataMonitor = class {
|
|
2085
|
+
_params;
|
|
2086
|
+
_lastTick = 0;
|
|
2087
|
+
_activeCounters = createLocalCounters();
|
|
2088
|
+
_lastCompleteCounters;
|
|
2089
|
+
_localTimeSeries = createLocalTimeSeries();
|
|
2090
|
+
_storageAverages = createStorageAverages();
|
|
2091
|
+
_replicationAverages = createNetworkAverages();
|
|
2092
|
+
_sizeByMessage = {};
|
|
2093
|
+
_lastReceivedMessages = new CircularBuffer(100);
|
|
2094
|
+
_lastSentMessages = new CircularBuffer(100);
|
|
2095
|
+
_connectionsCount = 0;
|
|
1710
2096
|
constructor(_params = {
|
|
1711
2097
|
timeSeriesLength: 30
|
|
1712
2098
|
}) {
|
|
1713
2099
|
this._params = _params;
|
|
1714
|
-
this._lastTick = 0;
|
|
1715
|
-
this._activeCounters = createLocalCounters();
|
|
1716
|
-
this._localTimeSeries = createLocalTimeSeries();
|
|
1717
|
-
this._storageAverages = createStorageAverages();
|
|
1718
|
-
this._replicationAverages = createNetworkAverages();
|
|
1719
|
-
this._sizeByMessage = {};
|
|
1720
|
-
this._lastReceivedMessages = new CircularBuffer(100);
|
|
1721
|
-
this._lastSentMessages = new CircularBuffer(100);
|
|
1722
|
-
this._connectionsCount = 0;
|
|
1723
2100
|
}
|
|
1724
2101
|
tick(timeMs) {
|
|
1725
2102
|
this._advanceTimeWindow(timeMs - this._lastTick);
|
|
@@ -2057,19 +2434,37 @@ var getByteCount = (message) => {
|
|
|
2057
2434
|
};
|
|
2058
2435
|
|
|
2059
2436
|
// src/db-host/documents-synchronizer.ts
|
|
2437
|
+
import { next as A3 } from "@automerge/automerge";
|
|
2438
|
+
import { UpdateScheduler, sleep } from "@dxos/async";
|
|
2439
|
+
import { LifecycleState as LifecycleState2, Resource as Resource5, cancelWithContext as cancelWithContext2 } from "@dxos/context";
|
|
2440
|
+
import { invariant as invariant6 } from "@dxos/invariant";
|
|
2441
|
+
import { log as log6 } from "@dxos/log";
|
|
2060
2442
|
var __dxlog_file7 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/documents-synchronizer.ts";
|
|
2061
2443
|
var MAX_UPDATE_FREQ = 10;
|
|
2444
|
+
var WRAP_AROUND_RETRY_LIMIT = 3;
|
|
2445
|
+
var WRAP_AROUND_RETRY_INITIAL_DELAY = 100;
|
|
2062
2446
|
var DocumentsSynchronizer = class extends Resource5 {
|
|
2447
|
+
_params;
|
|
2448
|
+
_syncStates = /* @__PURE__ */ new Map();
|
|
2449
|
+
/**
|
|
2450
|
+
* Documents that have pending updates.
|
|
2451
|
+
* Used to batch updates.
|
|
2452
|
+
*/
|
|
2453
|
+
_pendingUpdates = /* @__PURE__ */ new Set();
|
|
2454
|
+
/**
|
|
2455
|
+
* Job that schedules if there are pending updates.
|
|
2456
|
+
*/
|
|
2457
|
+
_sendUpdatesJob = void 0;
|
|
2063
2458
|
constructor(_params) {
|
|
2064
|
-
super(), this._params = _params
|
|
2459
|
+
super(), this._params = _params;
|
|
2065
2460
|
}
|
|
2066
|
-
addDocuments(documentIds, retryCounter = 0) {
|
|
2067
|
-
if (retryCounter >
|
|
2461
|
+
addDocuments(documentIds, retryCounter = 0, wrapAroundRetryDelay = WRAP_AROUND_RETRY_INITIAL_DELAY) {
|
|
2462
|
+
if (retryCounter > WRAP_AROUND_RETRY_LIMIT) {
|
|
2068
2463
|
log6.warn("Failed to load document, retry limit reached", {
|
|
2069
2464
|
documentIds
|
|
2070
2465
|
}, {
|
|
2071
2466
|
F: __dxlog_file7,
|
|
2072
|
-
L:
|
|
2467
|
+
L: 57,
|
|
2073
2468
|
S: this,
|
|
2074
2469
|
C: (f, a) => f(...a)
|
|
2075
2470
|
});
|
|
@@ -2087,13 +2482,13 @@ var DocumentsSynchronizer = class extends Resource5 {
|
|
|
2087
2482
|
error
|
|
2088
2483
|
}, {
|
|
2089
2484
|
F: __dxlog_file7,
|
|
2090
|
-
L:
|
|
2485
|
+
L: 71,
|
|
2091
2486
|
S: this,
|
|
2092
2487
|
C: (f, a) => f(...a)
|
|
2093
2488
|
});
|
|
2094
|
-
this.addDocuments([
|
|
2489
|
+
void cancelWithContext2(this._ctx, sleep(wrapAroundRetryDelay)).then(() => this.addDocuments([
|
|
2095
2490
|
documentId
|
|
2096
|
-
], retryCounter + 1);
|
|
2491
|
+
], retryCounter + 1, wrapAroundRetryDelay * 2));
|
|
2097
2492
|
});
|
|
2098
2493
|
}
|
|
2099
2494
|
}
|
|
@@ -2115,14 +2510,9 @@ var DocumentsSynchronizer = class extends Resource5 {
|
|
|
2115
2510
|
}
|
|
2116
2511
|
async update(updates) {
|
|
2117
2512
|
for (const { documentId, mutation, isNew } of updates) {
|
|
2118
|
-
|
|
2119
|
-
const doc = await this._params.repo.find(documentId, FIND_PARAMS);
|
|
2120
|
-
doc.update((doc2) => A2.loadIncremental(doc2, mutation));
|
|
2121
|
-
this._startSync(doc);
|
|
2122
|
-
} else {
|
|
2123
|
-
this._writeMutation(documentId, mutation);
|
|
2124
|
-
}
|
|
2513
|
+
this._writeMutation(documentId, mutation, isNew);
|
|
2125
2514
|
}
|
|
2515
|
+
await this._params.repo.flush(updates.map(({ documentId }) => documentId));
|
|
2126
2516
|
}
|
|
2127
2517
|
_startSync(doc) {
|
|
2128
2518
|
if (this._syncStates.has(doc.documentId)) {
|
|
@@ -2130,7 +2520,7 @@ var DocumentsSynchronizer = class extends Resource5 {
|
|
|
2130
2520
|
documentId: doc.documentId
|
|
2131
2521
|
}, {
|
|
2132
2522
|
F: __dxlog_file7,
|
|
2133
|
-
L:
|
|
2523
|
+
L: 108,
|
|
2134
2524
|
S: this,
|
|
2135
2525
|
C: (f, a) => f(...a)
|
|
2136
2526
|
});
|
|
@@ -2141,6 +2531,7 @@ var DocumentsSynchronizer = class extends Resource5 {
|
|
|
2141
2531
|
};
|
|
2142
2532
|
this._subscribeForChanges(syncState);
|
|
2143
2533
|
this._syncStates.set(doc.documentId, syncState);
|
|
2534
|
+
return syncState;
|
|
2144
2535
|
}
|
|
2145
2536
|
_subscribeForChanges(syncState) {
|
|
2146
2537
|
const handler = () => {
|
|
@@ -2173,7 +2564,7 @@ var DocumentsSynchronizer = class extends Resource5 {
|
|
|
2173
2564
|
const syncState = this._syncStates.get(documentId);
|
|
2174
2565
|
invariant6(syncState, "Sync state for document not found", {
|
|
2175
2566
|
F: __dxlog_file7,
|
|
2176
|
-
L:
|
|
2567
|
+
L: 150,
|
|
2177
2568
|
S: this,
|
|
2178
2569
|
A: [
|
|
2179
2570
|
"syncState",
|
|
@@ -2185,44 +2576,57 @@ var DocumentsSynchronizer = class extends Resource5 {
|
|
|
2185
2576
|
return;
|
|
2186
2577
|
}
|
|
2187
2578
|
const doc = handle.doc();
|
|
2188
|
-
const mutation = syncState.lastSentHead ?
|
|
2579
|
+
const mutation = syncState.lastSentHead ? A3.saveSince(doc, syncState.lastSentHead) : A3.save(doc);
|
|
2189
2580
|
if (mutation.length === 0) {
|
|
2190
2581
|
return;
|
|
2191
2582
|
}
|
|
2192
|
-
syncState.lastSentHead =
|
|
2583
|
+
syncState.lastSentHead = A3.getHeads(doc);
|
|
2193
2584
|
return mutation;
|
|
2194
2585
|
}
|
|
2195
|
-
_writeMutation(documentId, mutation) {
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
}
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2586
|
+
_writeMutation(documentId, mutation, isNew) {
|
|
2587
|
+
if (this._lifecycleState === LifecycleState2.CLOSED) {
|
|
2588
|
+
return;
|
|
2589
|
+
}
|
|
2590
|
+
if (isNew) {
|
|
2591
|
+
const newHandle = this._params.repo.import(mutation, {
|
|
2592
|
+
docId: documentId
|
|
2593
|
+
});
|
|
2594
|
+
const syncState = this._startSync(newHandle);
|
|
2595
|
+
syncState.lastSentHead = A3.getHeads(newHandle.doc());
|
|
2596
|
+
} else {
|
|
2597
|
+
const syncState = this._syncStates.get(documentId);
|
|
2598
|
+
invariant6(syncState, "Sync state for document not found", {
|
|
2599
|
+
F: __dxlog_file7,
|
|
2600
|
+
L: 174,
|
|
2601
|
+
S: this,
|
|
2602
|
+
A: [
|
|
2603
|
+
"syncState",
|
|
2604
|
+
"'Sync state for document not found'"
|
|
2605
|
+
]
|
|
2606
|
+
});
|
|
2607
|
+
const headsBefore = A3.getHeads(syncState.handle.doc());
|
|
2608
|
+
this._params.repo.import(mutation, {
|
|
2609
|
+
docId: documentId
|
|
2610
|
+
});
|
|
2611
|
+
if (A3.equals(headsBefore, syncState.lastSentHead)) {
|
|
2612
|
+
syncState.lastSentHead = A3.getHeads(syncState.handle.doc());
|
|
2211
2613
|
}
|
|
2212
|
-
|
|
2213
|
-
});
|
|
2614
|
+
}
|
|
2214
2615
|
}
|
|
2215
2616
|
};
|
|
2216
2617
|
|
|
2217
2618
|
// src/db-host/data-service.ts
|
|
2218
2619
|
var __dxlog_file8 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/data-service.ts";
|
|
2219
2620
|
var DataServiceImpl = class {
|
|
2621
|
+
/**
|
|
2622
|
+
* Map of subscriptions.
|
|
2623
|
+
* subscriptionId -> DocumentsSynchronizer
|
|
2624
|
+
*/
|
|
2625
|
+
_subscriptions = /* @__PURE__ */ new Map();
|
|
2626
|
+
_automergeHost;
|
|
2627
|
+
_spaceStateManager;
|
|
2628
|
+
_updateIndexes;
|
|
2220
2629
|
constructor(params) {
|
|
2221
|
-
/**
|
|
2222
|
-
* Map of subscriptions.
|
|
2223
|
-
* subscriptionId -> DocumentsSynchronizer
|
|
2224
|
-
*/
|
|
2225
|
-
this._subscriptions = /* @__PURE__ */ new Map();
|
|
2226
2630
|
this._automergeHost = params.automergeHost;
|
|
2227
2631
|
this._spaceStateManager = params.spaceStateManager;
|
|
2228
2632
|
this._updateIndexes = params.updateIndexes;
|
|
@@ -2238,7 +2642,7 @@ var DataServiceImpl = class {
|
|
|
2238
2642
|
ready();
|
|
2239
2643
|
}).catch((err) => log7.catch(err, void 0, {
|
|
2240
2644
|
F: __dxlog_file8,
|
|
2241
|
-
L:
|
|
2645
|
+
L: 72,
|
|
2242
2646
|
S: this,
|
|
2243
2647
|
C: (f, a) => f(...a)
|
|
2244
2648
|
}));
|
|
@@ -2249,7 +2653,7 @@ var DataServiceImpl = class {
|
|
|
2249
2653
|
const synchronizer = this._subscriptions.get(request.subscriptionId);
|
|
2250
2654
|
invariant7(synchronizer, "Subscription not found", {
|
|
2251
2655
|
F: __dxlog_file8,
|
|
2252
|
-
L:
|
|
2656
|
+
L: 79,
|
|
2253
2657
|
S: this,
|
|
2254
2658
|
A: [
|
|
2255
2659
|
"synchronizer",
|
|
@@ -2270,7 +2674,7 @@ var DataServiceImpl = class {
|
|
|
2270
2674
|
const synchronizer = this._subscriptions.get(request.subscriptionId);
|
|
2271
2675
|
invariant7(synchronizer, "Subscription not found", {
|
|
2272
2676
|
F: __dxlog_file8,
|
|
2273
|
-
L:
|
|
2677
|
+
L: 94,
|
|
2274
2678
|
S: this,
|
|
2275
2679
|
A: [
|
|
2276
2680
|
"synchronizer",
|
|
@@ -2315,7 +2719,7 @@ var DataServiceImpl = class {
|
|
|
2315
2719
|
const spaceId = request.spaceId;
|
|
2316
2720
|
invariant7(SpaceId2.isValid(spaceId), void 0, {
|
|
2317
2721
|
F: __dxlog_file8,
|
|
2318
|
-
L:
|
|
2722
|
+
L: 134,
|
|
2319
2723
|
S: this,
|
|
2320
2724
|
A: [
|
|
2321
2725
|
"SpaceId.isValid(spaceId)",
|
|
@@ -2336,14 +2740,7 @@ var DataServiceImpl = class {
|
|
|
2336
2740
|
peers: []
|
|
2337
2741
|
};
|
|
2338
2742
|
next({
|
|
2339
|
-
peers: state.peers
|
|
2340
|
-
peerId: peer.peerId,
|
|
2341
|
-
missingOnRemote: peer.missingOnRemote,
|
|
2342
|
-
missingOnLocal: peer.missingOnLocal,
|
|
2343
|
-
differentDocuments: peer.differentDocuments,
|
|
2344
|
-
localDocumentCount: peer.localDocumentCount,
|
|
2345
|
-
remoteDocumentCount: peer.remoteDocumentCount
|
|
2346
|
-
}))
|
|
2743
|
+
peers: state.peers
|
|
2347
2744
|
});
|
|
2348
2745
|
});
|
|
2349
2746
|
this._automergeHost.collectionStateUpdated.on(ctx, (e) => {
|
|
@@ -2359,14 +2756,14 @@ var DataServiceImpl = class {
|
|
|
2359
2756
|
// src/db-host/echo-host.ts
|
|
2360
2757
|
import { LifecycleState as LifecycleState5, Resource as Resource9 } from "@dxos/context";
|
|
2361
2758
|
import { todo } from "@dxos/debug";
|
|
2362
|
-
import {
|
|
2759
|
+
import { SpaceDocVersion as SpaceDocVersion3, createIdFromSpaceKey as createIdFromSpaceKey2 } from "@dxos/echo-protocol";
|
|
2363
2760
|
import { IndexMetadataStore, IndexStore, Indexer } from "@dxos/indexing";
|
|
2364
2761
|
import { invariant as invariant13 } from "@dxos/invariant";
|
|
2365
2762
|
import { IndexKind } from "@dxos/protocols/proto/dxos/echo/indexing";
|
|
2366
2763
|
import { trace as trace5 } from "@dxos/tracing";
|
|
2367
2764
|
|
|
2368
2765
|
// src/db-host/documents-iterator.ts
|
|
2369
|
-
import * as
|
|
2766
|
+
import * as A4 from "@automerge/automerge";
|
|
2370
2767
|
import { Context as Context2 } from "@dxos/context";
|
|
2371
2768
|
import { DatabaseDirectory as DatabaseDirectory2, SpaceDocVersion } from "@dxos/echo-protocol";
|
|
2372
2769
|
import { invariant as invariant8 } from "@dxos/invariant";
|
|
@@ -2397,10 +2794,10 @@ var createSelectedDocumentsIterator = (automergeHost) => (
|
|
|
2397
2794
|
""
|
|
2398
2795
|
]
|
|
2399
2796
|
});
|
|
2400
|
-
const currentHeads =
|
|
2401
|
-
if (!
|
|
2797
|
+
const currentHeads = A4.getHeads(doc);
|
|
2798
|
+
if (!A4.equals(currentHeads, heads)) {
|
|
2402
2799
|
const begin = Date.now();
|
|
2403
|
-
doc =
|
|
2800
|
+
doc = A4.view(doc, heads);
|
|
2404
2801
|
const end = Date.now();
|
|
2405
2802
|
if (end - begin > LOG_VIEW_OPERATION_THRESHOLD) {
|
|
2406
2803
|
log8("Checking out document version is taking too long", {
|
|
@@ -2455,8 +2852,8 @@ var createSelectedDocumentsIterator = (automergeHost) => (
|
|
|
2455
2852
|
|
|
2456
2853
|
// src/db-host/query-service.ts
|
|
2457
2854
|
import { getHeads as getHeads3 } from "@automerge/automerge";
|
|
2458
|
-
import
|
|
2459
|
-
import { DeferredTask, scheduleMicroTask, synchronized as synchronized2 } from "@dxos/async";
|
|
2855
|
+
import * as Schema from "effect/Schema";
|
|
2856
|
+
import { DeferredTask as DeferredTask2, scheduleMicroTask, synchronized as synchronized2 } from "@dxos/async";
|
|
2460
2857
|
import { Stream as Stream2 } from "@dxos/codec-protobuf/stream";
|
|
2461
2858
|
import { Context as Context4, Resource as Resource7 } from "@dxos/context";
|
|
2462
2859
|
import { raise } from "@dxos/debug";
|
|
@@ -2466,23 +2863,27 @@ import { objectPointerCodec as objectPointerCodec4 } from "@dxos/protocols";
|
|
|
2466
2863
|
import { trace as trace4 } from "@dxos/tracing";
|
|
2467
2864
|
|
|
2468
2865
|
// src/query/query-executor.ts
|
|
2469
|
-
import
|
|
2866
|
+
import * as Match from "effect/Match";
|
|
2867
|
+
import * as Predicate from "effect/Predicate";
|
|
2470
2868
|
import { Context as Context3, ContextDisposedError, LifecycleState as LifecycleState3, Resource as Resource6 } from "@dxos/context";
|
|
2471
|
-
import { DatabaseDirectory as DatabaseDirectory3,
|
|
2869
|
+
import { DatabaseDirectory as DatabaseDirectory3, ObjectStructure, isEncodedReference } from "@dxos/echo-protocol";
|
|
2472
2870
|
import { EscapedPropPath } from "@dxos/indexing";
|
|
2473
2871
|
import { invariant as invariant10 } from "@dxos/invariant";
|
|
2474
2872
|
import { DXN, PublicKey as PublicKey3 } from "@dxos/keys";
|
|
2475
2873
|
import { log as log9 } from "@dxos/log";
|
|
2476
2874
|
import { objectPointerCodec as objectPointerCodec3 } from "@dxos/protocols";
|
|
2477
|
-
import { getDeep, isNonNullable as
|
|
2875
|
+
import { getDeep, isNonNullable as isNonNullable3 } from "@dxos/util";
|
|
2478
2876
|
|
|
2479
2877
|
// src/query/query-planner.ts
|
|
2878
|
+
import { Order } from "@dxos/echo";
|
|
2480
2879
|
import { invariant as invariant9 } from "@dxos/invariant";
|
|
2481
2880
|
|
|
2482
2881
|
// src/query/errors.ts
|
|
2483
2882
|
import { BaseError } from "@dxos/errors";
|
|
2484
2883
|
var QueryError = class extends BaseError.extend("QUERY_ERROR") {
|
|
2485
2884
|
};
|
|
2885
|
+
var InvalidQueryError = class extends QueryError.extend("INVALID_QUERY") {
|
|
2886
|
+
};
|
|
2486
2887
|
|
|
2487
2888
|
// src/query/plan.ts
|
|
2488
2889
|
(function(QueryPlan2) {
|
|
@@ -2511,6 +2912,7 @@ var DEFAULT_OPTIONS = {
|
|
|
2511
2912
|
defaultTextSearchKind: "full-text"
|
|
2512
2913
|
};
|
|
2513
2914
|
var QueryPlanner = class {
|
|
2915
|
+
_options;
|
|
2514
2916
|
constructor(options) {
|
|
2515
2917
|
this._options = {
|
|
2516
2918
|
...DEFAULT_OPTIONS,
|
|
@@ -2524,6 +2926,7 @@ var QueryPlanner = class {
|
|
|
2524
2926
|
});
|
|
2525
2927
|
plan = this._optimizeEmptyFilters(plan);
|
|
2526
2928
|
plan = this._optimizeSoloUnions(plan);
|
|
2929
|
+
plan = this._ensureOrderStep(plan);
|
|
2527
2930
|
return plan;
|
|
2528
2931
|
}
|
|
2529
2932
|
_generate(query, context) {
|
|
@@ -2546,8 +2949,11 @@ var QueryPlanner = class {
|
|
|
2546
2949
|
return this._generateUnionClause(query, context);
|
|
2547
2950
|
case "set-difference":
|
|
2548
2951
|
return this._generateSetDifferenceClause(query, context);
|
|
2952
|
+
case "order":
|
|
2953
|
+
return this._generateOrderClause(query, context);
|
|
2549
2954
|
default:
|
|
2550
|
-
throw new QueryError(
|
|
2955
|
+
throw new QueryError({
|
|
2956
|
+
message: `Unsupported query type: ${query.type}`,
|
|
2551
2957
|
context: {
|
|
2552
2958
|
query: context.originalQuery
|
|
2553
2959
|
}
|
|
@@ -2571,10 +2977,11 @@ var QueryPlanner = class {
|
|
|
2571
2977
|
}
|
|
2572
2978
|
// TODO(dmaretskyi): This can be rewritten as a function of (filter[]) -> (selection ? undefined, rest: filter[]) that recurses onto itself.
|
|
2573
2979
|
// TODO(dmaretskyi): If the tip of the query ast is a [select, ...filter] shape we can reorder the filters so the query is most efficient.
|
|
2574
|
-
_generateSelectionFromFilter(
|
|
2575
|
-
switch (
|
|
2980
|
+
_generateSelectionFromFilter(filter2, context) {
|
|
2981
|
+
switch (filter2.type) {
|
|
2982
|
+
// Props
|
|
2576
2983
|
case "object": {
|
|
2577
|
-
if (context.selectionInverted &&
|
|
2984
|
+
if (context.selectionInverted && filter2.id === void 0 && filter2.typename === null && Object.keys(filter2.props).length === 0) {
|
|
2578
2985
|
return QueryPlan.Plan.make([
|
|
2579
2986
|
{
|
|
2580
2987
|
_tag: "ClearWorkingSetStep"
|
|
@@ -2583,32 +2990,33 @@ var QueryPlanner = class {
|
|
|
2583
2990
|
]);
|
|
2584
2991
|
}
|
|
2585
2992
|
if (context.selectionInverted) {
|
|
2586
|
-
throw new QueryError(
|
|
2993
|
+
throw new QueryError({
|
|
2994
|
+
message: "Query too complex",
|
|
2587
2995
|
context: {
|
|
2588
2996
|
query: context.originalQuery
|
|
2589
2997
|
}
|
|
2590
2998
|
});
|
|
2591
2999
|
}
|
|
2592
|
-
if (
|
|
3000
|
+
if (filter2.id && filter2.id?.length > 0) {
|
|
2593
3001
|
return QueryPlan.Plan.make([
|
|
2594
3002
|
{
|
|
2595
3003
|
_tag: "SelectStep",
|
|
2596
3004
|
spaces: context.selectionSpaces,
|
|
2597
3005
|
selector: {
|
|
2598
3006
|
_tag: "IdSelector",
|
|
2599
|
-
objectIds:
|
|
3007
|
+
objectIds: filter2.id
|
|
2600
3008
|
}
|
|
2601
3009
|
},
|
|
2602
3010
|
...this._generateDeletedHandlingSteps(context),
|
|
2603
3011
|
{
|
|
2604
3012
|
_tag: "FilterStep",
|
|
2605
3013
|
filter: {
|
|
2606
|
-
...
|
|
3014
|
+
...filter2,
|
|
2607
3015
|
id: void 0
|
|
2608
3016
|
}
|
|
2609
3017
|
}
|
|
2610
3018
|
]);
|
|
2611
|
-
} else if (
|
|
3019
|
+
} else if (filter2.typename) {
|
|
2612
3020
|
return QueryPlan.Plan.make([
|
|
2613
3021
|
{
|
|
2614
3022
|
_tag: "SelectStep",
|
|
@@ -2616,17 +3024,17 @@ var QueryPlanner = class {
|
|
|
2616
3024
|
selector: {
|
|
2617
3025
|
_tag: "TypeSelector",
|
|
2618
3026
|
typename: [
|
|
2619
|
-
|
|
3027
|
+
filter2.typename
|
|
2620
3028
|
],
|
|
2621
3029
|
inverted: false
|
|
2622
3030
|
}
|
|
2623
3031
|
},
|
|
2624
3032
|
...this._generateDeletedHandlingSteps(context),
|
|
3033
|
+
// TODO(dmaretskyi): Normally we could skip filtering by typename here, but since the index does not separate schema versions, we need to do additional filter to only select the correct version.
|
|
2625
3034
|
{
|
|
2626
3035
|
_tag: "FilterStep",
|
|
2627
3036
|
filter: {
|
|
2628
|
-
...
|
|
2629
|
-
typename: null
|
|
3037
|
+
...filter2
|
|
2630
3038
|
}
|
|
2631
3039
|
}
|
|
2632
3040
|
]);
|
|
@@ -2643,12 +3051,32 @@ var QueryPlanner = class {
|
|
|
2643
3051
|
{
|
|
2644
3052
|
_tag: "FilterStep",
|
|
2645
3053
|
filter: {
|
|
2646
|
-
...
|
|
3054
|
+
...filter2
|
|
2647
3055
|
}
|
|
2648
3056
|
}
|
|
2649
3057
|
]);
|
|
2650
3058
|
}
|
|
2651
3059
|
}
|
|
3060
|
+
// Tag
|
|
3061
|
+
case "tag": {
|
|
3062
|
+
return QueryPlan.Plan.make([
|
|
3063
|
+
{
|
|
3064
|
+
_tag: "SelectStep",
|
|
3065
|
+
spaces: context.selectionSpaces,
|
|
3066
|
+
selector: {
|
|
3067
|
+
_tag: "WildcardSelector"
|
|
3068
|
+
}
|
|
3069
|
+
},
|
|
3070
|
+
...this._generateDeletedHandlingSteps(context),
|
|
3071
|
+
{
|
|
3072
|
+
_tag: "FilterStep",
|
|
3073
|
+
filter: {
|
|
3074
|
+
...filter2
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
]);
|
|
3078
|
+
}
|
|
3079
|
+
// Text
|
|
2652
3080
|
case "text-search": {
|
|
2653
3081
|
return QueryPlan.Plan.make([
|
|
2654
3082
|
{
|
|
@@ -2656,55 +3084,61 @@ var QueryPlanner = class {
|
|
|
2656
3084
|
spaces: context.selectionSpaces,
|
|
2657
3085
|
selector: {
|
|
2658
3086
|
_tag: "TextSelector",
|
|
2659
|
-
text:
|
|
2660
|
-
searchKind:
|
|
3087
|
+
text: filter2.text,
|
|
3088
|
+
searchKind: filter2.searchKind ?? this._options.defaultTextSearchKind
|
|
2661
3089
|
}
|
|
2662
3090
|
},
|
|
2663
3091
|
...this._generateDeletedHandlingSteps(context)
|
|
2664
3092
|
]);
|
|
2665
3093
|
}
|
|
3094
|
+
// Compare
|
|
2666
3095
|
case "compare":
|
|
2667
|
-
throw new QueryError(
|
|
3096
|
+
throw new QueryError({
|
|
3097
|
+
message: "Query too complex",
|
|
2668
3098
|
context: {
|
|
2669
3099
|
query: context.originalQuery
|
|
2670
3100
|
}
|
|
2671
3101
|
});
|
|
2672
3102
|
case "in":
|
|
2673
|
-
throw new QueryError(
|
|
3103
|
+
throw new QueryError({
|
|
3104
|
+
message: "Query too complex",
|
|
2674
3105
|
context: {
|
|
2675
3106
|
query: context.originalQuery
|
|
2676
3107
|
}
|
|
2677
3108
|
});
|
|
2678
3109
|
case "range":
|
|
2679
|
-
throw new QueryError(
|
|
3110
|
+
throw new QueryError({
|
|
3111
|
+
message: "Query too complex",
|
|
2680
3112
|
context: {
|
|
2681
3113
|
query: context.originalQuery
|
|
2682
3114
|
}
|
|
2683
3115
|
});
|
|
3116
|
+
// Boolean
|
|
2684
3117
|
case "not":
|
|
2685
|
-
return this._generateSelectionFromFilter(
|
|
3118
|
+
return this._generateSelectionFromFilter(filter2.filter, {
|
|
2686
3119
|
...context,
|
|
2687
3120
|
selectionInverted: !context.selectionInverted
|
|
2688
3121
|
});
|
|
2689
3122
|
case "and":
|
|
2690
|
-
throw new QueryError(
|
|
3123
|
+
throw new QueryError({
|
|
3124
|
+
message: "Query too complex",
|
|
2691
3125
|
context: {
|
|
2692
3126
|
query: context.originalQuery
|
|
2693
3127
|
}
|
|
2694
3128
|
});
|
|
2695
3129
|
case "or":
|
|
2696
|
-
if (
|
|
2697
|
-
const typenames =
|
|
2698
|
-
invariant9(
|
|
3130
|
+
if (filter2.filters.every(isTrivialTypenameFilter)) {
|
|
3131
|
+
const typenames = filter2.filters.map((filter3) => {
|
|
3132
|
+
invariant9(filter3.type === "object" && filter3.typename !== null, void 0, {
|
|
2699
3133
|
F: __dxlog_file10,
|
|
2700
|
-
L:
|
|
3134
|
+
L: 225,
|
|
2701
3135
|
S: this,
|
|
2702
3136
|
A: [
|
|
2703
|
-
"
|
|
3137
|
+
"filter.type === 'object' && filter.typename !== null",
|
|
2704
3138
|
""
|
|
2705
3139
|
]
|
|
2706
3140
|
});
|
|
2707
|
-
return
|
|
3141
|
+
return filter3.typename;
|
|
2708
3142
|
});
|
|
2709
3143
|
return QueryPlan.Plan.make([
|
|
2710
3144
|
{
|
|
@@ -2716,17 +3150,25 @@ var QueryPlanner = class {
|
|
|
2716
3150
|
inverted: context.selectionInverted
|
|
2717
3151
|
}
|
|
2718
3152
|
},
|
|
2719
|
-
...this._generateDeletedHandlingSteps(context)
|
|
3153
|
+
...this._generateDeletedHandlingSteps(context),
|
|
3154
|
+
{
|
|
3155
|
+
_tag: "FilterStep",
|
|
3156
|
+
filter: {
|
|
3157
|
+
...filter2
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
2720
3160
|
]);
|
|
2721
3161
|
} else {
|
|
2722
|
-
throw new QueryError(
|
|
3162
|
+
throw new QueryError({
|
|
3163
|
+
message: "Query too complex",
|
|
2723
3164
|
context: {
|
|
2724
3165
|
query: context.originalQuery
|
|
2725
3166
|
}
|
|
2726
3167
|
});
|
|
2727
3168
|
}
|
|
2728
3169
|
default:
|
|
2729
|
-
throw new QueryError(
|
|
3170
|
+
throw new QueryError({
|
|
3171
|
+
message: `Unsupported filter type: ${filter2.type}`,
|
|
2730
3172
|
context: {
|
|
2731
3173
|
query: context.originalQuery
|
|
2732
3174
|
}
|
|
@@ -2926,6 +3368,45 @@ var QueryPlanner = class {
|
|
|
2926
3368
|
_optimizeSoloUnions(plan) {
|
|
2927
3369
|
return plan;
|
|
2928
3370
|
}
|
|
3371
|
+
_generateOrderClause(query, context) {
|
|
3372
|
+
return QueryPlan.Plan.make([
|
|
3373
|
+
...this._generate(query.query, context).steps,
|
|
3374
|
+
{
|
|
3375
|
+
_tag: "OrderStep",
|
|
3376
|
+
order: query.order
|
|
3377
|
+
}
|
|
3378
|
+
]);
|
|
3379
|
+
}
|
|
3380
|
+
// After complete plan is built, inspect it from the end:
|
|
3381
|
+
// - Walk backwards until hitting an object set changer.
|
|
3382
|
+
// - If an order step is found, skip.
|
|
3383
|
+
// - Otherwise append natural order to the end.
|
|
3384
|
+
_ensureOrderStep(plan) {
|
|
3385
|
+
const OBJECT_SET_CHANGERS = /* @__PURE__ */ new Set([
|
|
3386
|
+
"SelectStep",
|
|
3387
|
+
"TraverseStep",
|
|
3388
|
+
"UnionStep",
|
|
3389
|
+
"SetDifferenceStep"
|
|
3390
|
+
]);
|
|
3391
|
+
for (let i = plan.steps.length - 1; i >= 0; i--) {
|
|
3392
|
+
const step = plan.steps[i];
|
|
3393
|
+
if (step._tag === "OrderStep") {
|
|
3394
|
+
return plan;
|
|
3395
|
+
}
|
|
3396
|
+
if (OBJECT_SET_CHANGERS.has(step._tag)) {
|
|
3397
|
+
break;
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
return QueryPlan.Plan.make([
|
|
3401
|
+
...plan.steps,
|
|
3402
|
+
{
|
|
3403
|
+
_tag: "OrderStep",
|
|
3404
|
+
order: [
|
|
3405
|
+
Order.natural.ast
|
|
3406
|
+
]
|
|
3407
|
+
}
|
|
3408
|
+
]);
|
|
3409
|
+
}
|
|
2929
3410
|
};
|
|
2930
3411
|
var DEFAULT_CONTEXT = {
|
|
2931
3412
|
originalQuery: null,
|
|
@@ -2946,8 +3427,8 @@ var createRelationTraversalStep = (direction) => ({
|
|
|
2946
3427
|
direction
|
|
2947
3428
|
}
|
|
2948
3429
|
});
|
|
2949
|
-
var isTrivialTypenameFilter = (
|
|
2950
|
-
return
|
|
3430
|
+
var isTrivialTypenameFilter = (filter2) => {
|
|
3431
|
+
return filter2.type === "object" && filter2.typename !== null && Object.keys(filter2.props).length === 0 && (filter2.id === void 0 || filter2.id.length === 0) && (filter2.foreignKeys === void 0 || filter2.foreignKeys.length === 0);
|
|
2951
3432
|
};
|
|
2952
3433
|
|
|
2953
3434
|
// src/query/query-executor.ts
|
|
@@ -2959,11 +3440,57 @@ var ExecutionTrace = Object.freeze({
|
|
|
2959
3440
|
objectCount: 0,
|
|
2960
3441
|
documentsLoaded: 0,
|
|
2961
3442
|
indexHits: 0,
|
|
3443
|
+
beginTs: 0,
|
|
3444
|
+
endTs: 0,
|
|
2962
3445
|
indexQueryTime: 0,
|
|
2963
3446
|
documentLoadTime: 0,
|
|
2964
3447
|
executionTime: 0,
|
|
2965
3448
|
children: []
|
|
2966
3449
|
}),
|
|
3450
|
+
markEnd: (trace6) => {
|
|
3451
|
+
trace6.endTs = performance.now();
|
|
3452
|
+
trace6.executionTime = trace6.endTs - trace6.beginTs;
|
|
3453
|
+
},
|
|
3454
|
+
putOnPerformanceTimeline: (trace6) => {
|
|
3455
|
+
performance.measure(trace6.name, {
|
|
3456
|
+
start: trace6.beginTs,
|
|
3457
|
+
end: trace6.endTs,
|
|
3458
|
+
detail: {
|
|
3459
|
+
devtools: {
|
|
3460
|
+
dataType: "track-entry",
|
|
3461
|
+
track: "Query Execution",
|
|
3462
|
+
trackGroup: "ECHO",
|
|
3463
|
+
color: "tertiary-dark",
|
|
3464
|
+
properties: [
|
|
3465
|
+
[
|
|
3466
|
+
"objectCount",
|
|
3467
|
+
trace6.objectCount
|
|
3468
|
+
],
|
|
3469
|
+
[
|
|
3470
|
+
"documentsLoaded",
|
|
3471
|
+
trace6.documentsLoaded
|
|
3472
|
+
],
|
|
3473
|
+
[
|
|
3474
|
+
"index hits",
|
|
3475
|
+
trace6.indexHits
|
|
3476
|
+
],
|
|
3477
|
+
[
|
|
3478
|
+
"indexQueryTime",
|
|
3479
|
+
trace6.indexQueryTime
|
|
3480
|
+
],
|
|
3481
|
+
[
|
|
3482
|
+
"documentLoadTime",
|
|
3483
|
+
trace6.documentLoadTime
|
|
3484
|
+
]
|
|
3485
|
+
],
|
|
3486
|
+
tooltipText: trace6.details
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
});
|
|
3490
|
+
for (const child of trace6.children) {
|
|
3491
|
+
ExecutionTrace.putOnPerformanceTimeline(child);
|
|
3492
|
+
}
|
|
3493
|
+
},
|
|
2967
3494
|
format: (trace6) => {
|
|
2968
3495
|
const go = (trace7, indent) => {
|
|
2969
3496
|
return [
|
|
@@ -2978,10 +3505,21 @@ var ExecutionTrace = Object.freeze({
|
|
|
2978
3505
|
});
|
|
2979
3506
|
var TRACE_QUERY_EXECUTION = false;
|
|
2980
3507
|
var QueryExecutor = class extends Resource6 {
|
|
3508
|
+
_indexer;
|
|
3509
|
+
_automergeHost;
|
|
3510
|
+
_spaceStateManager;
|
|
3511
|
+
/**
|
|
3512
|
+
* Id of this query.
|
|
3513
|
+
*/
|
|
3514
|
+
_id;
|
|
3515
|
+
_query;
|
|
3516
|
+
// TODO(dmaretskyi): Might be used in the future.
|
|
3517
|
+
_reactivity;
|
|
3518
|
+
_plan;
|
|
3519
|
+
_trace = ExecutionTrace.makeEmpty();
|
|
3520
|
+
_lastResultSet = [];
|
|
2981
3521
|
constructor(options) {
|
|
2982
3522
|
super();
|
|
2983
|
-
this._trace = ExecutionTrace.makeEmpty();
|
|
2984
|
-
this._lastResultSet = [];
|
|
2985
3523
|
this._indexer = options.indexer;
|
|
2986
3524
|
this._automergeHost = options.automergeHost;
|
|
2987
3525
|
this._spaceStateManager = options.spaceStateManager;
|
|
@@ -3016,7 +3554,7 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3016
3554
|
async execQuery() {
|
|
3017
3555
|
invariant10(this._lifecycleState === LifecycleState3.OPEN, void 0, {
|
|
3018
3556
|
F: __dxlog_file11,
|
|
3019
|
-
L:
|
|
3557
|
+
L: 211,
|
|
3020
3558
|
S: this,
|
|
3021
3559
|
A: [
|
|
3022
3560
|
"this._lifecycleState === LifecycleState.OPEN",
|
|
@@ -3051,7 +3589,7 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3051
3589
|
trace6.children.push(result.trace);
|
|
3052
3590
|
}
|
|
3053
3591
|
trace6.objectCount = workingSet.length;
|
|
3054
|
-
|
|
3592
|
+
ExecutionTrace.markEnd(trace6);
|
|
3055
3593
|
return {
|
|
3056
3594
|
workingSet,
|
|
3057
3595
|
trace: trace6
|
|
@@ -3089,10 +3627,14 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3089
3627
|
case "TraverseStep":
|
|
3090
3628
|
({ workingSet: newWorkingSet, trace: trace6 } = await this._execTraverseStep(step, workingSet));
|
|
3091
3629
|
break;
|
|
3630
|
+
case "OrderStep":
|
|
3631
|
+
({ workingSet: newWorkingSet, trace: trace6 } = await this._execOrderStep(step, workingSet));
|
|
3632
|
+
break;
|
|
3092
3633
|
default:
|
|
3093
3634
|
throw new Error(`Unknown step type: ${step._tag}`);
|
|
3094
3635
|
}
|
|
3095
|
-
trace6.
|
|
3636
|
+
trace6.beginTs = begin;
|
|
3637
|
+
ExecutionTrace.markEnd(trace6);
|
|
3096
3638
|
return {
|
|
3097
3639
|
workingSet: newWorkingSet,
|
|
3098
3640
|
trace: trace6
|
|
@@ -3105,7 +3647,8 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3105
3647
|
const trace6 = {
|
|
3106
3648
|
...ExecutionTrace.makeEmpty(),
|
|
3107
3649
|
name: "Select",
|
|
3108
|
-
details: JSON.stringify(step.selector)
|
|
3650
|
+
details: JSON.stringify(step.selector),
|
|
3651
|
+
beginTs: performance.now()
|
|
3109
3652
|
};
|
|
3110
3653
|
switch (step.selector._tag) {
|
|
3111
3654
|
case "WildcardSelector": {
|
|
@@ -3126,7 +3669,7 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3126
3669
|
const results = await this._loadDocumentsAfterIndexQuery(indexHits);
|
|
3127
3670
|
trace6.documentsLoaded += results.length;
|
|
3128
3671
|
trace6.documentLoadTime += performance.now() - documentLoadStart;
|
|
3129
|
-
workingSet.push(...results.filter(
|
|
3672
|
+
workingSet.push(...results.filter(isNonNullable3).filter((item) => step.spaces.includes(item.spaceId)));
|
|
3130
3673
|
trace6.objectCount = workingSet.length;
|
|
3131
3674
|
break;
|
|
3132
3675
|
}
|
|
@@ -3136,7 +3679,7 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3136
3679
|
sourceSpaceId: step.spaces[0]
|
|
3137
3680
|
})));
|
|
3138
3681
|
trace6.documentLoadTime += performance.now() - beginLoad;
|
|
3139
|
-
workingSet.push(...items.filter(
|
|
3682
|
+
workingSet.push(...items.filter(isNonNullable3));
|
|
3140
3683
|
trace6.objectCount = workingSet.length;
|
|
3141
3684
|
break;
|
|
3142
3685
|
}
|
|
@@ -3158,7 +3701,7 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3158
3701
|
const results = await this._loadDocumentsAfterIndexQuery(indexHits);
|
|
3159
3702
|
trace6.documentsLoaded += results.length;
|
|
3160
3703
|
trace6.documentLoadTime += performance.now() - documentLoadStart;
|
|
3161
|
-
workingSet.push(...results.filter(
|
|
3704
|
+
workingSet.push(...results.filter(isNonNullable3).filter((item) => step.spaces.includes(item.spaceId)));
|
|
3162
3705
|
trace6.objectCount = workingSet.length;
|
|
3163
3706
|
break;
|
|
3164
3707
|
}
|
|
@@ -3183,7 +3726,7 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3183
3726
|
const results = await this._loadDocumentsAfterIndexQuery(indexHits);
|
|
3184
3727
|
trace6.documentsLoaded += results.length;
|
|
3185
3728
|
trace6.documentLoadTime += performance.now() - documentLoadStart;
|
|
3186
|
-
workingSet.push(...results.filter(
|
|
3729
|
+
workingSet.push(...results.filter(isNonNullable3).filter((item) => step.spaces.includes(item.spaceId)));
|
|
3187
3730
|
trace6.objectCount = workingSet.length;
|
|
3188
3731
|
break;
|
|
3189
3732
|
}
|
|
@@ -3212,17 +3755,6 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3212
3755
|
};
|
|
3213
3756
|
}
|
|
3214
3757
|
async _execFilterDeletedStep(step, workingSet) {
|
|
3215
|
-
if (workingSet.length === 6) {
|
|
3216
|
-
log9.info("FilterDeletedStep", {
|
|
3217
|
-
step,
|
|
3218
|
-
workingSet
|
|
3219
|
-
}, {
|
|
3220
|
-
F: __dxlog_file11,
|
|
3221
|
-
L: 386,
|
|
3222
|
-
S: this,
|
|
3223
|
-
C: (f, a) => f(...a)
|
|
3224
|
-
});
|
|
3225
|
-
}
|
|
3226
3758
|
const expected = step.mode === "only-deleted";
|
|
3227
3759
|
const result = workingSet.filter((item) => ObjectStructure.isDeleted(item.doc) === expected);
|
|
3228
3760
|
return {
|
|
@@ -3260,24 +3792,24 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3260
3792
|
spaceId: item.spaceId
|
|
3261
3793
|
} : null;
|
|
3262
3794
|
} catch {
|
|
3263
|
-
log9.warn("
|
|
3795
|
+
log9.warn("invalid reference", {
|
|
3264
3796
|
ref: ref2["/"]
|
|
3265
3797
|
}, {
|
|
3266
3798
|
F: __dxlog_file11,
|
|
3267
|
-
L:
|
|
3799
|
+
L: 477,
|
|
3268
3800
|
S: this,
|
|
3269
3801
|
C: (f, a) => f(...a)
|
|
3270
3802
|
});
|
|
3271
3803
|
return null;
|
|
3272
3804
|
}
|
|
3273
3805
|
});
|
|
3274
|
-
}).filter(
|
|
3806
|
+
}).filter(isNonNullable3);
|
|
3275
3807
|
const beginLoad = performance.now();
|
|
3276
3808
|
const items = await Promise.all(refs.map(({ ref, spaceId }) => this._loadFromDXN(ref, {
|
|
3277
3809
|
sourceSpaceId: spaceId
|
|
3278
3810
|
})));
|
|
3279
3811
|
trace6.documentLoadTime += performance.now() - beginLoad;
|
|
3280
|
-
newWorkingSet.push(...items.filter(
|
|
3812
|
+
newWorkingSet.push(...items.filter(isNonNullable3));
|
|
3281
3813
|
trace6.objectCount = newWorkingSet.length;
|
|
3282
3814
|
break;
|
|
3283
3815
|
}
|
|
@@ -3296,7 +3828,7 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3296
3828
|
const results = await this._loadDocumentsAfterIndexQuery(indexHits);
|
|
3297
3829
|
trace6.documentsLoaded += results.length;
|
|
3298
3830
|
trace6.documentLoadTime += performance.now() - documentLoadStart;
|
|
3299
|
-
newWorkingSet.push(...results.filter(
|
|
3831
|
+
newWorkingSet.push(...results.filter(isNonNullable3));
|
|
3300
3832
|
trace6.objectCount = newWorkingSet.length;
|
|
3301
3833
|
break;
|
|
3302
3834
|
}
|
|
@@ -3318,23 +3850,23 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3318
3850
|
spaceId: item.spaceId
|
|
3319
3851
|
};
|
|
3320
3852
|
} catch {
|
|
3321
|
-
log9.warn("
|
|
3853
|
+
log9.warn("invalid reference", {
|
|
3322
3854
|
ref: ref["/"]
|
|
3323
3855
|
}, {
|
|
3324
3856
|
F: __dxlog_file11,
|
|
3325
|
-
L:
|
|
3857
|
+
L: 540,
|
|
3326
3858
|
S: this,
|
|
3327
3859
|
C: (f, a) => f(...a)
|
|
3328
3860
|
});
|
|
3329
3861
|
return null;
|
|
3330
3862
|
}
|
|
3331
|
-
}).filter(
|
|
3863
|
+
}).filter(isNonNullable3);
|
|
3332
3864
|
const beginLoad = performance.now();
|
|
3333
3865
|
const items = await Promise.all(refs.map(({ ref, spaceId }) => this._loadFromDXN(ref, {
|
|
3334
3866
|
sourceSpaceId: spaceId
|
|
3335
3867
|
})));
|
|
3336
3868
|
trace6.documentLoadTime += performance.now() - beginLoad;
|
|
3337
|
-
newWorkingSet.push(...items.filter(
|
|
3869
|
+
newWorkingSet.push(...items.filter(isNonNullable3));
|
|
3338
3870
|
trace6.objectCount = newWorkingSet.length;
|
|
3339
3871
|
break;
|
|
3340
3872
|
}
|
|
@@ -3354,7 +3886,7 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3354
3886
|
const results = await this._loadDocumentsAfterIndexQuery(indexHits);
|
|
3355
3887
|
trace6.documentsLoaded += results.length;
|
|
3356
3888
|
trace6.documentLoadTime += performance.now() - documentLoadStart;
|
|
3357
|
-
newWorkingSet.push(...results.filter(
|
|
3889
|
+
newWorkingSet.push(...results.filter(isNonNullable3));
|
|
3358
3890
|
trace6.objectCount = newWorkingSet.length;
|
|
3359
3891
|
break;
|
|
3360
3892
|
}
|
|
@@ -3411,16 +3943,83 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3411
3943
|
trace: trace6
|
|
3412
3944
|
};
|
|
3413
3945
|
}
|
|
3946
|
+
async _execOrderStep(step, workingSet) {
|
|
3947
|
+
const sortedWorkingSet = [
|
|
3948
|
+
...workingSet
|
|
3949
|
+
].sort((a, b) => this._compareMultiOrder(a, b, step.order));
|
|
3950
|
+
return {
|
|
3951
|
+
workingSet: sortedWorkingSet,
|
|
3952
|
+
trace: {
|
|
3953
|
+
...ExecutionTrace.makeEmpty(),
|
|
3954
|
+
name: "Order",
|
|
3955
|
+
details: JSON.stringify(step.order),
|
|
3956
|
+
objectCount: sortedWorkingSet.length
|
|
3957
|
+
}
|
|
3958
|
+
};
|
|
3959
|
+
}
|
|
3960
|
+
_compareMultiOrder(a, b, orders) {
|
|
3961
|
+
if (orders.length === 0) {
|
|
3962
|
+
return 0;
|
|
3963
|
+
} else if (orders.length === 1) {
|
|
3964
|
+
return this._compareByOrder(a, b, orders[0]);
|
|
3965
|
+
}
|
|
3966
|
+
for (const order of orders) {
|
|
3967
|
+
const comparison = this._compareByOrder(a, b, order);
|
|
3968
|
+
if (comparison !== 0) {
|
|
3969
|
+
return comparison;
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
return 0;
|
|
3973
|
+
}
|
|
3974
|
+
_compareByOrder(a, b, order) {
|
|
3975
|
+
switch (order.kind) {
|
|
3976
|
+
case "natural":
|
|
3977
|
+
return a.objectId.localeCompare(b.objectId);
|
|
3978
|
+
case "property": {
|
|
3979
|
+
const comparison = this._compareByProperty(a, b, order.property);
|
|
3980
|
+
return order.direction === "desc" ? -comparison : comparison;
|
|
3981
|
+
}
|
|
3982
|
+
default:
|
|
3983
|
+
return 0;
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
_compareByProperty(a, b, property) {
|
|
3987
|
+
const aValue = a.doc.data[property];
|
|
3988
|
+
const bValue = b.doc.data[property];
|
|
3989
|
+
if (aValue == null && bValue == null) {
|
|
3990
|
+
return 0;
|
|
3991
|
+
}
|
|
3992
|
+
if (aValue == null) {
|
|
3993
|
+
return 1;
|
|
3994
|
+
}
|
|
3995
|
+
if (bValue == null) {
|
|
3996
|
+
return -1;
|
|
3997
|
+
}
|
|
3998
|
+
if (typeof aValue === "string" && typeof bValue === "string") {
|
|
3999
|
+
return aValue.localeCompare(bValue);
|
|
4000
|
+
}
|
|
4001
|
+
if (typeof aValue === "number" && typeof bValue === "number") {
|
|
4002
|
+
return aValue - bValue;
|
|
4003
|
+
}
|
|
4004
|
+
if (typeof aValue === "boolean" && typeof bValue === "boolean") {
|
|
4005
|
+
return aValue === bValue ? 0 : aValue ? 1 : -1;
|
|
4006
|
+
}
|
|
4007
|
+
return String(aValue).localeCompare(String(bValue));
|
|
4008
|
+
}
|
|
3414
4009
|
async _loadDocumentsAfterIndexQuery(indexHits) {
|
|
3415
4010
|
return Promise.all(indexHits.map(async (hit) => {
|
|
3416
4011
|
return this._loadFromIndexHit(hit);
|
|
3417
4012
|
}));
|
|
3418
4013
|
}
|
|
4014
|
+
/**
|
|
4015
|
+
* Space key hex -> SpaceId.
|
|
4016
|
+
*/
|
|
4017
|
+
_spaceIdCache = /* @__PURE__ */ new Map();
|
|
3419
4018
|
async _loadFromIndexHit(hit) {
|
|
3420
4019
|
const { objectId, documentId, spaceKey: spaceKeyInIndex } = objectPointerCodec3.decode(hit.id);
|
|
3421
4020
|
const handle = await this._automergeHost.loadDoc(Context3.default(void 0, {
|
|
3422
4021
|
F: __dxlog_file11,
|
|
3423
|
-
L:
|
|
4022
|
+
L: 738
|
|
3424
4023
|
}), documentId);
|
|
3425
4024
|
const doc = handle.doc();
|
|
3426
4025
|
if (!doc) {
|
|
@@ -3430,6 +4029,11 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3430
4029
|
if (!spaceKey) {
|
|
3431
4030
|
return null;
|
|
3432
4031
|
}
|
|
4032
|
+
let spaceId = this._spaceIdCache.get(spaceKey);
|
|
4033
|
+
if (!spaceId) {
|
|
4034
|
+
spaceId = await createIdFromSpaceKey(PublicKey3.from(spaceKey));
|
|
4035
|
+
this._spaceIdCache.set(spaceKey, spaceId);
|
|
4036
|
+
}
|
|
3433
4037
|
const object = DatabaseDirectory3.getInlineObject(doc, objectId);
|
|
3434
4038
|
if (!object) {
|
|
3435
4039
|
return null;
|
|
@@ -3437,7 +4041,7 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3437
4041
|
return {
|
|
3438
4042
|
objectId,
|
|
3439
4043
|
documentId,
|
|
3440
|
-
spaceId
|
|
4044
|
+
spaceId,
|
|
3441
4045
|
doc: object
|
|
3442
4046
|
};
|
|
3443
4047
|
}
|
|
@@ -3448,7 +4052,7 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3448
4052
|
dxn
|
|
3449
4053
|
}, {
|
|
3450
4054
|
F: __dxlog_file11,
|
|
3451
|
-
L:
|
|
4055
|
+
L: 771,
|
|
3452
4056
|
S: this,
|
|
3453
4057
|
C: (f, a) => f(...a)
|
|
3454
4058
|
});
|
|
@@ -3461,7 +4065,7 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3461
4065
|
spaceId
|
|
3462
4066
|
}, {
|
|
3463
4067
|
F: __dxlog_file11,
|
|
3464
|
-
L:
|
|
4068
|
+
L: 779,
|
|
3465
4069
|
S: this,
|
|
3466
4070
|
C: (f, a) => f(...a)
|
|
3467
4071
|
});
|
|
@@ -3473,7 +4077,7 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3473
4077
|
spaceId
|
|
3474
4078
|
}, {
|
|
3475
4079
|
F: __dxlog_file11,
|
|
3476
|
-
L:
|
|
4080
|
+
L: 784,
|
|
3477
4081
|
S: this,
|
|
3478
4082
|
C: (f, a) => f(...a)
|
|
3479
4083
|
});
|
|
@@ -3494,7 +4098,7 @@ var QueryExecutor = class extends Resource6 {
|
|
|
3494
4098
|
}
|
|
3495
4099
|
const handle = await this._automergeHost.loadDoc(Context3.default(void 0, {
|
|
3496
4100
|
F: __dxlog_file11,
|
|
3497
|
-
L:
|
|
4101
|
+
L: 803
|
|
3498
4102
|
}), link);
|
|
3499
4103
|
const doc = handle.doc();
|
|
3500
4104
|
if (!doc) {
|
|
@@ -3522,9 +4126,13 @@ function _ts_decorate5(decorators, target, key, desc) {
|
|
|
3522
4126
|
}
|
|
3523
4127
|
var __dxlog_file12 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/query-service.ts";
|
|
3524
4128
|
var QueryServiceImpl = class extends Resource7 {
|
|
4129
|
+
_params;
|
|
4130
|
+
// TODO(dmaretskyi): We need to implement query deduping. Idle composer has 80 queries with only 10 being unique.
|
|
4131
|
+
_queries = /* @__PURE__ */ new Set();
|
|
4132
|
+
_updateQueries;
|
|
3525
4133
|
// TODO(burdon): OK for options, but not params. Pass separately and type readonly here.
|
|
3526
4134
|
constructor(_params) {
|
|
3527
|
-
super(), this._params = _params
|
|
4135
|
+
super(), this._params = _params;
|
|
3528
4136
|
trace4.diagnostic({
|
|
3529
4137
|
id: "active-queries",
|
|
3530
4138
|
name: "Active Queries",
|
|
@@ -3541,7 +4149,7 @@ var QueryServiceImpl = class extends Resource7 {
|
|
|
3541
4149
|
}
|
|
3542
4150
|
async _open() {
|
|
3543
4151
|
this._params.indexer.updated.on(this._ctx, () => this.invalidateQueries());
|
|
3544
|
-
this._updateQueries = new
|
|
4152
|
+
this._updateQueries = new DeferredTask2(this._ctx, this._executeQueries.bind(this));
|
|
3545
4153
|
}
|
|
3546
4154
|
async _close() {
|
|
3547
4155
|
await this._updateQueries.join();
|
|
@@ -3552,6 +4160,18 @@ var QueryServiceImpl = class extends Resource7 {
|
|
|
3552
4160
|
}
|
|
3553
4161
|
execQuery(request) {
|
|
3554
4162
|
return new Stream2(({ next, close, ctx }) => {
|
|
4163
|
+
if (this._params.indexer.config?.enabled !== true) {
|
|
4164
|
+
log10.error("indexer is disabled", {
|
|
4165
|
+
config: this._params.indexer.config
|
|
4166
|
+
}, {
|
|
4167
|
+
F: __dxlog_file12,
|
|
4168
|
+
L: 102,
|
|
4169
|
+
S: this,
|
|
4170
|
+
C: (f, a) => f(...a)
|
|
4171
|
+
});
|
|
4172
|
+
close();
|
|
4173
|
+
return;
|
|
4174
|
+
}
|
|
3555
4175
|
const queryEntry = this._createQuery(ctx, request, next, close, close);
|
|
3556
4176
|
scheduleMicroTask(ctx, async () => {
|
|
3557
4177
|
await queryEntry.executor.open();
|
|
@@ -3567,7 +4187,7 @@ var QueryServiceImpl = class extends Resource7 {
|
|
|
3567
4187
|
async reindex() {
|
|
3568
4188
|
log10("Reindexing all documents...", void 0, {
|
|
3569
4189
|
F: __dxlog_file12,
|
|
3570
|
-
L:
|
|
4190
|
+
L: 121,
|
|
3571
4191
|
S: this,
|
|
3572
4192
|
C: (f, a) => f(...a)
|
|
3573
4193
|
});
|
|
@@ -3582,7 +4202,7 @@ var QueryServiceImpl = class extends Resource7 {
|
|
|
3582
4202
|
count: ids.size
|
|
3583
4203
|
}, {
|
|
3584
4204
|
F: __dxlog_file12,
|
|
3585
|
-
L:
|
|
4205
|
+
L: 129,
|
|
3586
4206
|
S: this,
|
|
3587
4207
|
C: (f, a) => f(...a)
|
|
3588
4208
|
});
|
|
@@ -3592,7 +4212,7 @@ var QueryServiceImpl = class extends Resource7 {
|
|
|
3592
4212
|
count: ids.size
|
|
3593
4213
|
}, {
|
|
3594
4214
|
F: __dxlog_file12,
|
|
3595
|
-
L:
|
|
4215
|
+
L: 133,
|
|
3596
4216
|
S: this,
|
|
3597
4217
|
C: (f, a) => f(...a)
|
|
3598
4218
|
});
|
|
@@ -3658,7 +4278,7 @@ var QueryServiceImpl = class extends Resource7 {
|
|
|
3658
4278
|
} catch (err) {
|
|
3659
4279
|
log10.catch(err, void 0, {
|
|
3660
4280
|
F: __dxlog_file12,
|
|
3661
|
-
L:
|
|
4281
|
+
L: 204,
|
|
3662
4282
|
S: this,
|
|
3663
4283
|
C: (f, a) => f(...a)
|
|
3664
4284
|
});
|
|
@@ -3669,7 +4289,7 @@ var QueryServiceImpl = class extends Resource7 {
|
|
|
3669
4289
|
duration: performance.now() - begin
|
|
3670
4290
|
}, {
|
|
3671
4291
|
F: __dxlog_file12,
|
|
3672
|
-
L:
|
|
4292
|
+
L: 208,
|
|
3673
4293
|
S: this,
|
|
3674
4294
|
C: (f, a) => f(...a)
|
|
3675
4295
|
});
|
|
@@ -3720,7 +4340,7 @@ var createDocumentsIterator = (automergeHost) => (
|
|
|
3720
4340
|
}
|
|
3721
4341
|
const linkHandle = await automergeHost.loadDoc(Context4.default(void 0, {
|
|
3722
4342
|
F: __dxlog_file12,
|
|
3723
|
-
L:
|
|
4343
|
+
L: 248
|
|
3724
4344
|
}), urlString);
|
|
3725
4345
|
for await (const result of getObjectsFromHandle(linkHandle)) {
|
|
3726
4346
|
yield result;
|
|
@@ -3745,7 +4365,7 @@ var createDocumentsIterator = (automergeHost) => (
|
|
|
3745
4365
|
import { interpretAsDocumentId as interpretAsDocumentId3 } from "@automerge/automerge-repo";
|
|
3746
4366
|
import isEqual from "lodash.isequal";
|
|
3747
4367
|
import { Event as Event3, UpdateScheduler as UpdateScheduler3 } from "@dxos/async";
|
|
3748
|
-
import {
|
|
4368
|
+
import { Context as Context5, LifecycleState as LifecycleState4, Resource as Resource8 } from "@dxos/context";
|
|
3749
4369
|
import { invariant as invariant12 } from "@dxos/invariant";
|
|
3750
4370
|
|
|
3751
4371
|
// src/db-host/database-root.ts
|
|
@@ -3754,17 +4374,17 @@ import { DatabaseDirectory as DatabaseDirectory5, SpaceDocVersion as SpaceDocVer
|
|
|
3754
4374
|
import { invariant as invariant11 } from "@dxos/invariant";
|
|
3755
4375
|
|
|
3756
4376
|
// src/db-host/automerge-metrics.ts
|
|
3757
|
-
import * as
|
|
4377
|
+
import * as A5 from "@automerge/automerge";
|
|
3758
4378
|
import { log as log11 } from "@dxos/log";
|
|
3759
4379
|
var __dxlog_file13 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/automerge-metrics.ts";
|
|
3760
4380
|
var measureDocMetrics = (doc) => {
|
|
3761
|
-
const snapshot =
|
|
4381
|
+
const snapshot = A5.save(doc);
|
|
3762
4382
|
const start = Date.now();
|
|
3763
|
-
const temp =
|
|
4383
|
+
const temp = A5.load(snapshot);
|
|
3764
4384
|
const end = Date.now();
|
|
3765
|
-
|
|
4385
|
+
A5.free(temp);
|
|
3766
4386
|
const getAllChangesStart = Date.now();
|
|
3767
|
-
const mutationCount =
|
|
4387
|
+
const mutationCount = A5.getAllChanges(doc).length;
|
|
3768
4388
|
const getAllChangesEnd = Date.now();
|
|
3769
4389
|
if (getAllChangesEnd - getAllChangesStart > 300) {
|
|
3770
4390
|
log11.warn("getAllChanges took too long", {
|
|
@@ -3786,6 +4406,7 @@ var measureDocMetrics = (doc) => {
|
|
|
3786
4406
|
// src/db-host/database-root.ts
|
|
3787
4407
|
var __dxlog_file14 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/database-root.ts";
|
|
3788
4408
|
var DatabaseRoot = class {
|
|
4409
|
+
_rootHandle;
|
|
3789
4410
|
static mapLinks(doc, mapping) {
|
|
3790
4411
|
doc.change((d) => {
|
|
3791
4412
|
if (!d.links) {
|
|
@@ -3870,14 +4491,11 @@ var DatabaseRoot = class {
|
|
|
3870
4491
|
// src/db-host/space-state-manager.ts
|
|
3871
4492
|
var __dxlog_file15 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/space-state-manager.ts";
|
|
3872
4493
|
var SpaceStateManager = class extends Resource8 {
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
this._lastSpaceDocumentList = /* @__PURE__ */ new Map();
|
|
3879
|
-
this.spaceDocumentListUpdated = new Event3();
|
|
3880
|
-
}
|
|
4494
|
+
_roots = /* @__PURE__ */ new Map();
|
|
4495
|
+
_rootBySpace = /* @__PURE__ */ new Map();
|
|
4496
|
+
_perRootContext = /* @__PURE__ */ new Map();
|
|
4497
|
+
_lastSpaceDocumentList = /* @__PURE__ */ new Map();
|
|
4498
|
+
spaceDocumentListUpdated = new Event3();
|
|
3881
4499
|
async _close(ctx) {
|
|
3882
4500
|
for (const [_, rootCtx] of this._perRootContext) {
|
|
3883
4501
|
await rootCtx.dispose();
|
|
@@ -3952,6 +4570,10 @@ var SpaceStateManager = class extends Resource8 {
|
|
|
3952
4570
|
}
|
|
3953
4571
|
};
|
|
3954
4572
|
var SpaceDocumentListUpdatedEvent = class {
|
|
4573
|
+
spaceId;
|
|
4574
|
+
spaceRootId;
|
|
4575
|
+
previousRootId;
|
|
4576
|
+
documentIds;
|
|
3955
4577
|
constructor(spaceId, spaceRootId, previousRootId, documentIds) {
|
|
3956
4578
|
this.spaceId = spaceId;
|
|
3957
4579
|
this.spaceRootId = spaceRootId;
|
|
@@ -3968,9 +4590,15 @@ var DEFAULT_INDEXING_CONFIG = {
|
|
|
3968
4590
|
vector: false
|
|
3969
4591
|
};
|
|
3970
4592
|
var EchoHost = class extends Resource9 {
|
|
4593
|
+
_indexMetadataStore;
|
|
4594
|
+
_indexer;
|
|
4595
|
+
_automergeHost;
|
|
4596
|
+
_queryService;
|
|
4597
|
+
_dataService;
|
|
4598
|
+
_spaceStateManager = new SpaceStateManager();
|
|
4599
|
+
_echoDataMonitor;
|
|
3971
4600
|
constructor({ kv, indexing = {}, peerIdProvider, getSpaceKeyByRootDocumentId }) {
|
|
3972
4601
|
super();
|
|
3973
|
-
this._spaceStateManager = new SpaceStateManager();
|
|
3974
4602
|
const indexingConfig = {
|
|
3975
4603
|
...DEFAULT_INDEXING_CONFIG,
|
|
3976
4604
|
...indexing
|
|
@@ -4091,7 +4719,6 @@ var EchoHost = class extends Resource9 {
|
|
|
4091
4719
|
if (e.previousRootId) {
|
|
4092
4720
|
void this._automergeHost.clearLocalCollectionState(deriveCollectionIdFromSpaceId(e.spaceId, e.previousRootId));
|
|
4093
4721
|
}
|
|
4094
|
-
void this._automergeHost.updateLocalCollectionState(deriveCollectionIdFromSpaceId(e.spaceId), e.documentIds);
|
|
4095
4722
|
void this._automergeHost.updateLocalCollectionState(deriveCollectionIdFromSpaceId(e.spaceId, e.spaceRootId), e.documentIds);
|
|
4096
4723
|
});
|
|
4097
4724
|
this._automergeHost.documentsSaved.on(this._ctx, () => {
|
|
@@ -4108,7 +4735,7 @@ var EchoHost = class extends Resource9 {
|
|
|
4108
4735
|
* Flush all pending writes to the underlying storage.
|
|
4109
4736
|
*/
|
|
4110
4737
|
async flush() {
|
|
4111
|
-
await this._automergeHost.
|
|
4738
|
+
await this._automergeHost.flush();
|
|
4112
4739
|
}
|
|
4113
4740
|
/**
|
|
4114
4741
|
* Perform any pending index updates.
|
|
@@ -4137,7 +4764,7 @@ var EchoHost = class extends Resource9 {
|
|
|
4137
4764
|
async createSpaceRoot(spaceKey) {
|
|
4138
4765
|
invariant13(this._lifecycleState === LifecycleState5.OPEN, void 0, {
|
|
4139
4766
|
F: __dxlog_file16,
|
|
4140
|
-
L:
|
|
4767
|
+
L: 254,
|
|
4141
4768
|
S: this,
|
|
4142
4769
|
A: [
|
|
4143
4770
|
"this._lifecycleState === LifecycleState.OPEN",
|
|
@@ -4165,7 +4792,7 @@ var EchoHost = class extends Resource9 {
|
|
|
4165
4792
|
async openSpaceRoot(spaceId, automergeUrl) {
|
|
4166
4793
|
invariant13(this._lifecycleState === LifecycleState5.OPEN, void 0, {
|
|
4167
4794
|
F: __dxlog_file16,
|
|
4168
|
-
L:
|
|
4795
|
+
L: 273,
|
|
4169
4796
|
S: this,
|
|
4170
4797
|
A: [
|
|
4171
4798
|
"this._lifecycleState === LifecycleState.OPEN",
|
|
@@ -4195,16 +4822,17 @@ var EchoHost = class extends Resource9 {
|
|
|
4195
4822
|
};
|
|
4196
4823
|
|
|
4197
4824
|
// src/edge/echo-edge-replicator.ts
|
|
4825
|
+
import * as Automerge from "@automerge/automerge";
|
|
4198
4826
|
import { cbor as cbor2 } from "@automerge/automerge-repo";
|
|
4199
|
-
import { Mutex,
|
|
4827
|
+
import { Mutex, scheduleMicroTask as scheduleMicroTask2, scheduleTask as scheduleTask2 } from "@dxos/async";
|
|
4200
4828
|
import { Context as Context6, Resource as Resource11 } from "@dxos/context";
|
|
4201
4829
|
import { randomUUID } from "@dxos/crypto";
|
|
4202
4830
|
import { invariant as invariant14 } from "@dxos/invariant";
|
|
4203
4831
|
import { log as log13 } from "@dxos/log";
|
|
4204
|
-
import { EdgeService } from "@dxos/protocols";
|
|
4832
|
+
import { DocumentCodec, EdgeService } from "@dxos/protocols";
|
|
4205
4833
|
import { buf } from "@dxos/protocols/buf";
|
|
4206
4834
|
import { MessageSchema as RouterMessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
4207
|
-
import { bufferToArray as bufferToArray2 } from "@dxos/util";
|
|
4835
|
+
import { bufferToArray as bufferToArray2, setDeep } from "@dxos/util";
|
|
4208
4836
|
|
|
4209
4837
|
// src/edge/inflight-request-limiter.ts
|
|
4210
4838
|
import { Trigger as Trigger2 } from "@dxos/async";
|
|
@@ -4212,8 +4840,17 @@ import { Resource as Resource10 } from "@dxos/context";
|
|
|
4212
4840
|
import { log as log12 } from "@dxos/log";
|
|
4213
4841
|
var __dxlog_file17 = "/__w/dxos/dxos/packages/core/echo/echo-pipeline/src/edge/inflight-request-limiter.ts";
|
|
4214
4842
|
var InflightRequestLimiter = class extends Resource10 {
|
|
4843
|
+
_config;
|
|
4844
|
+
/**
|
|
4845
|
+
* Decrement when we receive a sync message, increment when we send one.
|
|
4846
|
+
* Can't exceed _config.maxInflightRequests.
|
|
4847
|
+
* Resets after timeout to avoid replicator being stuck.
|
|
4848
|
+
*/
|
|
4849
|
+
_inflightRequestBalance = 0;
|
|
4850
|
+
_requestBarrier = new Trigger2();
|
|
4851
|
+
_resetBalanceTimeout;
|
|
4215
4852
|
constructor(_config) {
|
|
4216
|
-
super(), this._config = _config
|
|
4853
|
+
super(), this._config = _config;
|
|
4217
4854
|
}
|
|
4218
4855
|
async _open() {
|
|
4219
4856
|
this._inflightRequestBalance = 0;
|
|
@@ -4330,14 +4967,17 @@ var INITIAL_RESTART_DELAY = 500;
|
|
|
4330
4967
|
var RESTART_DELAY_JITTER = 250;
|
|
4331
4968
|
var MAX_RESTART_DELAY = 5e3;
|
|
4332
4969
|
var EchoEdgeReplicator = class {
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4970
|
+
_edgeConnection;
|
|
4971
|
+
_edgeHttpClient;
|
|
4972
|
+
_mutex = new Mutex();
|
|
4973
|
+
_ctx = void 0;
|
|
4974
|
+
_context = null;
|
|
4975
|
+
_connectedSpaces = /* @__PURE__ */ new Set();
|
|
4976
|
+
_connections = /* @__PURE__ */ new Map();
|
|
4977
|
+
_sharePolicyEnabled = true;
|
|
4978
|
+
constructor({ edgeConnection, edgeHttpClient, disableSharePolicy }) {
|
|
4340
4979
|
this._edgeConnection = edgeConnection;
|
|
4980
|
+
this._edgeHttpClient = edgeHttpClient;
|
|
4341
4981
|
this._sharePolicyEnabled = !disableSharePolicy;
|
|
4342
4982
|
}
|
|
4343
4983
|
async connect(context) {
|
|
@@ -4346,14 +4986,14 @@ var EchoEdgeReplicator = class {
|
|
|
4346
4986
|
connectedSpaces: this._connectedSpaces.size
|
|
4347
4987
|
}, {
|
|
4348
4988
|
F: __dxlog_file18,
|
|
4349
|
-
L:
|
|
4989
|
+
L: 73,
|
|
4350
4990
|
S: this,
|
|
4351
4991
|
C: (f, a) => f(...a)
|
|
4352
4992
|
});
|
|
4353
4993
|
this._context = context;
|
|
4354
4994
|
this._ctx = Context6.default(void 0, {
|
|
4355
4995
|
F: __dxlog_file18,
|
|
4356
|
-
L:
|
|
4996
|
+
L: 75
|
|
4357
4997
|
});
|
|
4358
4998
|
this._ctx.onDispose(this._edgeConnection.onReconnected(() => {
|
|
4359
4999
|
this._ctx && scheduleMicroTask2(this._ctx, () => this._handleReconnect());
|
|
@@ -4452,7 +5092,7 @@ var EchoEdgeReplicator = class {
|
|
|
4452
5092
|
async _openConnection(spaceId, reconnects = 0) {
|
|
4453
5093
|
invariant14(this._context, void 0, {
|
|
4454
5094
|
F: __dxlog_file18,
|
|
4455
|
-
L:
|
|
5095
|
+
L: 136,
|
|
4456
5096
|
S: this,
|
|
4457
5097
|
A: [
|
|
4458
5098
|
"this._context",
|
|
@@ -4461,7 +5101,7 @@ var EchoEdgeReplicator = class {
|
|
|
4461
5101
|
});
|
|
4462
5102
|
invariant14(!this._connections.has(spaceId), void 0, {
|
|
4463
5103
|
F: __dxlog_file18,
|
|
4464
|
-
L:
|
|
5104
|
+
L: 137,
|
|
4465
5105
|
S: this,
|
|
4466
5106
|
A: [
|
|
4467
5107
|
"!this._connections.has(spaceId)",
|
|
@@ -4470,14 +5110,31 @@ var EchoEdgeReplicator = class {
|
|
|
4470
5110
|
});
|
|
4471
5111
|
let restartScheduled = false;
|
|
4472
5112
|
const connection = new EdgeReplicatorConnection({
|
|
5113
|
+
edgeHttpClient: this._edgeHttpClient,
|
|
4473
5114
|
edgeConnection: this._edgeConnection,
|
|
4474
5115
|
spaceId,
|
|
4475
5116
|
context: this._context,
|
|
4476
5117
|
sharedPolicyEnabled: this._sharePolicyEnabled,
|
|
4477
5118
|
onRemoteConnected: async () => {
|
|
5119
|
+
log13.trace("dxos.echo.edge.replicator.onRemoteConnected", {
|
|
5120
|
+
spaceId
|
|
5121
|
+
}, {
|
|
5122
|
+
F: __dxlog_file18,
|
|
5123
|
+
L: 148,
|
|
5124
|
+
S: this,
|
|
5125
|
+
C: (f, a) => f(...a)
|
|
5126
|
+
});
|
|
4478
5127
|
this._context?.onConnectionOpen(connection);
|
|
4479
5128
|
},
|
|
4480
5129
|
onRemoteDisconnected: async () => {
|
|
5130
|
+
log13.trace("dxos.echo.edge.replicator.onRemoteDisconnected", {
|
|
5131
|
+
spaceId
|
|
5132
|
+
}, {
|
|
5133
|
+
F: __dxlog_file18,
|
|
5134
|
+
L: 152,
|
|
5135
|
+
S: this,
|
|
5136
|
+
C: (f, a) => f(...a)
|
|
5137
|
+
});
|
|
4481
5138
|
this._context?.onConnectionClosed(connection);
|
|
4482
5139
|
},
|
|
4483
5140
|
onRestartRequested: async () => {
|
|
@@ -4491,7 +5148,7 @@ var EchoEdgeReplicator = class {
|
|
|
4491
5148
|
restartDelay
|
|
4492
5149
|
}, {
|
|
4493
5150
|
F: __dxlog_file18,
|
|
4494
|
-
L:
|
|
5151
|
+
L: 163,
|
|
4495
5152
|
S: this,
|
|
4496
5153
|
C: (f, a) => f(...a)
|
|
4497
5154
|
});
|
|
@@ -4513,6 +5170,16 @@ var EchoEdgeReplicator = class {
|
|
|
4513
5170
|
if (ctx?.disposed) {
|
|
4514
5171
|
return;
|
|
4515
5172
|
}
|
|
5173
|
+
log13.trace("dxos.echo.edge.replicator.restart", {
|
|
5174
|
+
spaceId,
|
|
5175
|
+
reconnects,
|
|
5176
|
+
restartDelay
|
|
5177
|
+
}, {
|
|
5178
|
+
F: __dxlog_file18,
|
|
5179
|
+
L: 180,
|
|
5180
|
+
S: this,
|
|
5181
|
+
C: (f, a) => f(...a)
|
|
5182
|
+
});
|
|
4516
5183
|
await this._openConnection(spaceId, reconnects + 1);
|
|
4517
5184
|
} catch (e) {
|
|
4518
5185
|
env.error = e;
|
|
@@ -4530,17 +5197,32 @@ var EchoEdgeReplicator = class {
|
|
|
4530
5197
|
var MAX_INFLIGHT_REQUESTS = 5;
|
|
4531
5198
|
var MAX_RATE_LIMIT_WAIT_TIME_MS = 3e3;
|
|
4532
5199
|
var EdgeReplicatorConnection = class extends Resource11 {
|
|
4533
|
-
|
|
5200
|
+
_connectionId = randomUUID();
|
|
5201
|
+
_edgeConnection;
|
|
5202
|
+
_edgeHttpClient;
|
|
5203
|
+
_remotePeerId = null;
|
|
5204
|
+
_targetServiceId;
|
|
5205
|
+
_spaceId;
|
|
5206
|
+
_context;
|
|
5207
|
+
_sharedPolicyEnabled;
|
|
5208
|
+
_onRemoteConnected;
|
|
5209
|
+
_onRemoteDisconnected;
|
|
5210
|
+
_onRestartRequested;
|
|
5211
|
+
_sequence = 0;
|
|
5212
|
+
_requestLimiter = new InflightRequestLimiter({
|
|
5213
|
+
maxInflightRequests: MAX_INFLIGHT_REQUESTS,
|
|
5214
|
+
resetBalanceTimeoutMs: MAX_RATE_LIMIT_WAIT_TIME_MS
|
|
5215
|
+
});
|
|
5216
|
+
_readableStreamController;
|
|
5217
|
+
readable;
|
|
5218
|
+
writable;
|
|
5219
|
+
constructor({ edgeConnection, edgeHttpClient, spaceId, context, sharedPolicyEnabled, onRemoteConnected, onRemoteDisconnected, onRestartRequested }) {
|
|
4534
5220
|
super();
|
|
4535
|
-
this._remotePeerId = null;
|
|
4536
|
-
this._requestLimiter = new InflightRequestLimiter({
|
|
4537
|
-
maxInflightRequests: MAX_INFLIGHT_REQUESTS,
|
|
4538
|
-
resetBalanceTimeoutMs: MAX_RATE_LIMIT_WAIT_TIME_MS
|
|
4539
|
-
});
|
|
4540
5221
|
this._edgeConnection = edgeConnection;
|
|
5222
|
+
this._edgeHttpClient = edgeHttpClient;
|
|
4541
5223
|
this._spaceId = spaceId;
|
|
4542
5224
|
this._context = context;
|
|
4543
|
-
this._remotePeerId = `${EdgeService.AUTOMERGE_REPLICATOR}:${spaceId}-${
|
|
5225
|
+
this._remotePeerId = `${EdgeService.AUTOMERGE_REPLICATOR}:${spaceId}-${this._connectionId}`;
|
|
4544
5226
|
this._targetServiceId = `${EdgeService.AUTOMERGE_REPLICATOR}:${spaceId}`;
|
|
4545
5227
|
this._sharedPolicyEnabled = sharedPolicyEnabled;
|
|
4546
5228
|
this._onRemoteConnected = onRemoteConnected;
|
|
@@ -4561,7 +5243,7 @@ var EdgeReplicatorConnection = class extends Resource11 {
|
|
|
4561
5243
|
async _open(ctx) {
|
|
4562
5244
|
log13("opening...", void 0, {
|
|
4563
5245
|
F: __dxlog_file18,
|
|
4564
|
-
L:
|
|
5246
|
+
L: 273,
|
|
4565
5247
|
S: this,
|
|
4566
5248
|
C: (f, a) => f(...a)
|
|
4567
5249
|
});
|
|
@@ -4569,12 +5251,29 @@ var EdgeReplicatorConnection = class extends Resource11 {
|
|
|
4569
5251
|
this._ctx.onDispose(this._edgeConnection.onMessage((msg) => {
|
|
4570
5252
|
this._onMessage(msg);
|
|
4571
5253
|
}));
|
|
5254
|
+
let firstReconnect = true;
|
|
5255
|
+
this._ctx.onDispose(
|
|
5256
|
+
// NOTE: This will fire immediately if the connection is already open.
|
|
5257
|
+
this._edgeConnection.onReconnected(async () => {
|
|
5258
|
+
if (firstReconnect) {
|
|
5259
|
+
log13.verbose("first reconnect skipped", void 0, {
|
|
5260
|
+
F: __dxlog_file18,
|
|
5261
|
+
L: 288,
|
|
5262
|
+
S: this,
|
|
5263
|
+
C: (f, a) => f(...a)
|
|
5264
|
+
});
|
|
5265
|
+
firstReconnect = false;
|
|
5266
|
+
return;
|
|
5267
|
+
}
|
|
5268
|
+
this._onRestartRequested();
|
|
5269
|
+
})
|
|
5270
|
+
);
|
|
4572
5271
|
await this._onRemoteConnected();
|
|
4573
5272
|
}
|
|
4574
5273
|
async _close() {
|
|
4575
5274
|
log13("closing...", void 0, {
|
|
4576
5275
|
F: __dxlog_file18,
|
|
4577
|
-
L:
|
|
5276
|
+
L: 301,
|
|
4578
5277
|
S: this,
|
|
4579
5278
|
C: (f, a) => f(...a)
|
|
4580
5279
|
});
|
|
@@ -4585,7 +5284,7 @@ var EdgeReplicatorConnection = class extends Resource11 {
|
|
|
4585
5284
|
get peerId() {
|
|
4586
5285
|
invariant14(this._remotePeerId, "Not connected", {
|
|
4587
5286
|
F: __dxlog_file18,
|
|
4588
|
-
L:
|
|
5287
|
+
L: 310,
|
|
4589
5288
|
S: this,
|
|
4590
5289
|
A: [
|
|
4591
5290
|
"this._remotePeerId",
|
|
@@ -4610,7 +5309,7 @@ var EdgeReplicatorConnection = class extends Resource11 {
|
|
|
4610
5309
|
remoteId: this._remotePeerId
|
|
4611
5310
|
}, {
|
|
4612
5311
|
F: __dxlog_file18,
|
|
4613
|
-
L:
|
|
5312
|
+
L: 325,
|
|
4614
5313
|
S: this,
|
|
4615
5314
|
C: (f, a) => f(...a)
|
|
4616
5315
|
});
|
|
@@ -4631,20 +5330,42 @@ var EdgeReplicatorConnection = class extends Resource11 {
|
|
|
4631
5330
|
}
|
|
4632
5331
|
const payload = cbor2.decode(message.payload.value);
|
|
4633
5332
|
log13.verbose("received", {
|
|
4634
|
-
|
|
4635
|
-
documentId: payload.type === "sync" && payload.documentId,
|
|
5333
|
+
...getMessageInfo(payload),
|
|
4636
5334
|
remoteId: this._remotePeerId
|
|
4637
5335
|
}, {
|
|
4638
5336
|
F: __dxlog_file18,
|
|
4639
|
-
L:
|
|
5337
|
+
L: 354,
|
|
4640
5338
|
S: this,
|
|
4641
5339
|
C: (f, a) => f(...a)
|
|
4642
5340
|
});
|
|
4643
5341
|
payload.senderId = this._remotePeerId;
|
|
4644
5342
|
this._processMessage(payload);
|
|
4645
5343
|
}
|
|
5344
|
+
get bundleSyncEnabled() {
|
|
5345
|
+
return true;
|
|
5346
|
+
}
|
|
5347
|
+
async pushBundle(bundle) {
|
|
5348
|
+
const request = {
|
|
5349
|
+
bundle: bundle.map(({ documentId, data, heads }) => ({
|
|
5350
|
+
documentId,
|
|
5351
|
+
mutation: DocumentCodec.encode(data),
|
|
5352
|
+
heads
|
|
5353
|
+
}))
|
|
5354
|
+
};
|
|
5355
|
+
await this._edgeHttpClient.importBundle(this._spaceId, request);
|
|
5356
|
+
}
|
|
5357
|
+
async pullBundle(docHeads) {
|
|
5358
|
+
const request = {
|
|
5359
|
+
docHeads
|
|
5360
|
+
};
|
|
5361
|
+
const response = await this._edgeHttpClient.exportBundle(this._spaceId, request);
|
|
5362
|
+
return Object.fromEntries(response.bundle.map((doc) => [
|
|
5363
|
+
doc.documentId,
|
|
5364
|
+
DocumentCodec.decode(doc.mutation)
|
|
5365
|
+
]));
|
|
5366
|
+
}
|
|
4646
5367
|
_processMessage(message) {
|
|
4647
|
-
if (
|
|
5368
|
+
if (isErrorMessage(message)) {
|
|
4648
5369
|
this._onRestartRequested();
|
|
4649
5370
|
return;
|
|
4650
5371
|
}
|
|
@@ -4653,33 +5374,66 @@ var EdgeReplicatorConnection = class extends Resource11 {
|
|
|
4653
5374
|
}
|
|
4654
5375
|
async _sendMessage(message) {
|
|
4655
5376
|
message.targetId = this._targetServiceId;
|
|
5377
|
+
setDeep(message, [
|
|
5378
|
+
"metadata",
|
|
5379
|
+
"dxos_sequence"
|
|
5380
|
+
], this._getSequence());
|
|
5381
|
+
setDeep(message, [
|
|
5382
|
+
"metadata",
|
|
5383
|
+
"dxos_connectionId"
|
|
5384
|
+
], this._connectionId);
|
|
4656
5385
|
log13.verbose("sending...", {
|
|
4657
|
-
|
|
4658
|
-
documentId: message.type === "sync" && message.documentId,
|
|
5386
|
+
...getMessageInfo(message),
|
|
4659
5387
|
remoteId: this._remotePeerId
|
|
4660
5388
|
}, {
|
|
4661
5389
|
F: __dxlog_file18,
|
|
4662
|
-
L:
|
|
5390
|
+
L: 407,
|
|
4663
5391
|
S: this,
|
|
4664
5392
|
C: (f, a) => f(...a)
|
|
4665
5393
|
});
|
|
4666
5394
|
const encoded = cbor2.encode(message);
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
5395
|
+
try {
|
|
5396
|
+
await this._edgeConnection.send(buf.create(RouterMessageSchema, {
|
|
5397
|
+
serviceId: this._targetServiceId,
|
|
5398
|
+
source: {
|
|
5399
|
+
identityKey: this._edgeConnection.identityKey,
|
|
5400
|
+
peerKey: this._edgeConnection.peerKey
|
|
5401
|
+
},
|
|
5402
|
+
payload: {
|
|
5403
|
+
value: bufferToArray2(encoded)
|
|
5404
|
+
}
|
|
5405
|
+
}));
|
|
5406
|
+
} catch (err) {
|
|
5407
|
+
log13.error("failed to send message", {
|
|
5408
|
+
err
|
|
5409
|
+
}, {
|
|
5410
|
+
F: __dxlog_file18,
|
|
5411
|
+
L: 426,
|
|
5412
|
+
S: this,
|
|
5413
|
+
C: (f, a) => f(...a)
|
|
5414
|
+
});
|
|
5415
|
+
}
|
|
5416
|
+
}
|
|
5417
|
+
_getSequence() {
|
|
5418
|
+
return this._sequence++;
|
|
4677
5419
|
}
|
|
4678
5420
|
};
|
|
4679
|
-
var
|
|
5421
|
+
var isErrorMessage = (message) => message.type === "error";
|
|
5422
|
+
var getMessageInfo = (msg) => {
|
|
5423
|
+
const { have, heads, need, changes } = msg.type === "sync" ? Automerge.decodeSyncMessage(msg.data) : {};
|
|
5424
|
+
return {
|
|
5425
|
+
type: msg.type,
|
|
5426
|
+
documentId: "documentId" in msg ? msg.documentId : void 0,
|
|
5427
|
+
have,
|
|
5428
|
+
heads,
|
|
5429
|
+
need,
|
|
5430
|
+
changes: changes?.length,
|
|
5431
|
+
sequence: msg.metadata?.dxos_sequence
|
|
5432
|
+
};
|
|
5433
|
+
};
|
|
4680
5434
|
|
|
4681
5435
|
// src/util.ts
|
|
4682
|
-
import {
|
|
5436
|
+
import { ObjectStructure as ObjectStructure2, decodeReference } from "@dxos/echo-protocol";
|
|
4683
5437
|
var findInlineObjectOfType = (spaceDoc, typename) => {
|
|
4684
5438
|
for (const id in spaceDoc.objects ?? {}) {
|
|
4685
5439
|
const obj = spaceDoc.objects[id];
|
|
@@ -4731,6 +5485,7 @@ export {
|
|
|
4731
5485
|
diffCollectionState,
|
|
4732
5486
|
encodingOptions,
|
|
4733
5487
|
filterMatchObject,
|
|
5488
|
+
filterMatchObjectJSON,
|
|
4734
5489
|
filterMatchValue,
|
|
4735
5490
|
findInlineObjectOfType,
|
|
4736
5491
|
getSpaceIdFromCollectionId,
|