@dxos/echo-pipeline 0.8.3 → 0.8.4-main.1068cf700f
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/neutral/chunk-FJPXA75J.mjs +235 -0
- package/dist/lib/neutral/chunk-FJPXA75J.mjs.map +7 -0
- package/dist/lib/{browser/chunk-35I6ERLG.mjs → neutral/chunk-WIQUYO7H.mjs} +300 -213
- package/dist/lib/neutral/chunk-WIQUYO7H.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/filter/index.mjs +3 -2
- package/dist/lib/{browser → neutral}/index.mjs +2832 -946
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/{browser → neutral}/testing/index.mjs +73 -224
- package/dist/lib/neutral/testing/index.mjs.map +7 -0
- package/dist/types/src/automerge/automerge-host.d.ts +53 -42
- package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge-repo.test.d.ts +1 -1
- package/dist/types/src/automerge/automerge-repo.test.d.ts.map +1 -1
- package/dist/types/src/automerge/collection-synchronizer.d.ts +6 -4
- package/dist/types/src/automerge/collection-synchronizer.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-network-adapter.d.ts +22 -10
- package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-replicator.d.ts +37 -15
- package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
- package/dist/types/src/automerge/heads-store.d.ts +9 -2
- package/dist/types/src/automerge/heads-store.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 +5 -5
- package/dist/types/src/automerge/leveldb-storage-adapter.d.ts.map +1 -1
- package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts +9 -8
- package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts.map +1 -1
- package/dist/types/src/automerge/mesh-echo-replicator.d.ts +5 -5
- 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/automerge-data-source.d.ts +34 -0
- package/dist/types/src/db-host/automerge-data-source.d.ts.map +1 -0
- package/dist/types/src/db-host/automerge-data-source.test.d.ts +2 -0
- package/dist/types/src/db-host/automerge-data-source.test.d.ts.map +1 -0
- package/dist/types/src/db-host/data-service.d.ts +5 -4
- 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 +6 -5
- package/dist/types/src/db-host/documents-synchronizer.d.ts.map +1 -1
- package/dist/types/src/db-host/echo-host.d.ts +44 -23
- package/dist/types/src/db-host/echo-host.d.ts.map +1 -1
- package/dist/types/src/db-host/index.d.ts +1 -0
- package/dist/types/src/db-host/index.d.ts.map +1 -1
- package/dist/types/src/db-host/local-queue-service.d.ts +16 -0
- package/dist/types/src/db-host/local-queue-service.d.ts.map +1 -0
- package/dist/types/src/db-host/query-service.d.ts +14 -9
- package/dist/types/src/db-host/query-service.d.ts.map +1 -1
- package/dist/types/src/db-host/queue-data-source.d.ts +28 -0
- package/dist/types/src/db-host/queue-data-source.d.ts.map +1 -0
- package/dist/types/src/db-host/queue-service.test.d.ts +2 -0
- package/dist/types/src/db-host/queue-service.test.d.ts.map +1 -0
- package/dist/types/src/db-host/space-state-manager.d.ts +2 -1
- package/dist/types/src/db-host/space-state-manager.d.ts.map +1 -1
- package/dist/types/src/db-host/stub.d.ts +10 -0
- package/dist/types/src/db-host/stub.d.ts.map +1 -0
- package/dist/types/src/edge/echo-edge-replicator.d.ts +8 -6
- 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 +3 -3
- package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
- package/dist/types/src/query/errors.d.ts +21 -9
- package/dist/types/src/query/errors.d.ts.map +1 -1
- package/dist/types/src/query/plan.d.ts +57 -5
- package/dist/types/src/query/plan.d.ts.map +1 -1
- package/dist/types/src/query/query-executor.d.ts +49 -10
- package/dist/types/src/query/query-executor.d.ts.map +1 -1
- package/dist/types/src/query/query-planner.d.ts +9 -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/auth.d.ts +3 -3
- package/dist/types/src/space/auth.d.ts.map +1 -1
- package/dist/types/src/space/control-pipeline.d.ts +3 -3
- package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
- package/dist/types/src/space/space-manager.d.ts +7 -7
- package/dist/types/src/space/space-manager.d.ts.map +1 -1
- package/dist/types/src/space/space-protocol.d.ts +6 -6
- package/dist/types/src/space/space-protocol.d.ts.map +1 -1
- package/dist/types/src/space/space.d.ts +4 -4
- package/dist/types/src/space/space.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +0 -2
- package/dist/types/src/testing/index.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-network-adapter.d.ts +1 -1
- package/dist/types/src/testing/test-network-adapter.d.ts.map +1 -1
- package/dist/types/src/testing/test-replicator.d.ts +14 -13
- package/dist/types/src/testing/test-replicator.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 +53 -45
- package/src/automerge/automerge-host.test.ts +55 -24
- package/src/automerge/automerge-host.ts +399 -126
- package/src/automerge/automerge-repo.test.ts +214 -46
- package/src/automerge/collection-synchronizer.test.ts +22 -15
- package/src/automerge/collection-synchronizer.ts +57 -22
- 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 +73 -28
- package/src/automerge/echo-replicator.ts +40 -15
- package/src/automerge/heads-store.ts +42 -3
- package/src/automerge/index.ts +1 -1
- package/src/automerge/leveldb-storage-adapter.ts +11 -11
- package/src/automerge/mesh-echo-replicator-connection.ts +12 -8
- package/src/automerge/mesh-echo-replicator.ts +15 -11
- package/src/automerge/storage-adapter.test.ts +1 -1
- package/src/common/space-id.ts +1 -1
- package/src/db-host/automerge-data-source.test.ts +292 -0
- package/src/db-host/automerge-data-source.ts +144 -0
- package/src/db-host/data-service.ts +19 -20
- package/src/db-host/database-root.ts +2 -2
- package/src/db-host/documents-synchronizer.test.ts +73 -13
- package/src/db-host/documents-synchronizer.ts +58 -44
- package/src/db-host/echo-host.ts +194 -84
- package/src/db-host/index.ts +1 -0
- package/src/db-host/local-queue-service.ts +147 -0
- package/src/db-host/query-service.ts +28 -95
- package/src/db-host/queue-data-source.ts +132 -0
- package/src/db-host/queue-service.test.ts +145 -0
- package/src/db-host/space-state-manager.ts +6 -2
- package/src/db-host/stub.ts +28 -0
- package/src/edge/echo-edge-replicator.test.ts +8 -6
- package/src/edge/echo-edge-replicator.ts +124 -42
- package/src/filter/filter-match.test.ts +58 -32
- package/src/filter/filter-match.ts +163 -15
- package/src/metadata/metadata-store.ts +8 -5
- package/src/pipeline/pipeline-stress.test.ts +4 -2
- package/src/pipeline/pipeline.test.ts +3 -2
- package/src/pipeline/pipeline.ts +10 -7
- package/src/query/errors.ts +3 -1
- package/src/query/plan.ts +75 -5
- package/src/query/query-executor.ts +812 -117
- package/src/query/query-planner.test.ts +652 -34
- package/src/query/query-planner.ts +276 -13
- package/src/space/admission-discovery-extension.ts +3 -3
- package/src/space/auth.ts +6 -6
- package/src/space/control-pipeline.test.ts +4 -3
- package/src/space/control-pipeline.ts +11 -8
- package/src/space/space-manager.browser.test.ts +1 -1
- package/src/space/space-manager.ts +11 -10
- 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 +17 -14
- package/src/space/space.test.ts +1 -1
- package/src/space/space.ts +6 -5
- package/src/testing/index.ts +0 -2
- package/src/testing/test-agent-builder.ts +4 -3
- package/src/testing/test-network-adapter.ts +16 -3
- package/src/testing/test-replicator.ts +23 -18
- package/src/util.ts +7 -3
- package/dist/lib/browser/chunk-35I6ERLG.mjs.map +0 -7
- package/dist/lib/browser/chunk-CGS2ULMK.mjs +0 -11
- package/dist/lib/browser/chunk-CGS2ULMK.mjs.map +0 -7
- package/dist/lib/browser/chunk-TQJTKNMS.mjs +0 -126
- package/dist/lib/browser/chunk-TQJTKNMS.mjs.map +0 -7
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/node/chunk-HOPOFWAL.cjs +0 -147
- package/dist/lib/node/chunk-HOPOFWAL.cjs.map +0 -7
- package/dist/lib/node/chunk-JXX6LF5U.cjs +0 -2084
- package/dist/lib/node/chunk-JXX6LF5U.cjs.map +0 -7
- package/dist/lib/node/chunk-Q7SFCCGT.cjs +0 -33
- package/dist/lib/node/chunk-Q7SFCCGT.cjs.map +0 -7
- package/dist/lib/node/filter/index.cjs +0 -32
- package/dist/lib/node/filter/index.cjs.map +0 -7
- package/dist/lib/node/index.cjs +0 -4699
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node/testing/index.cjs +0 -753
- package/dist/lib/node/testing/index.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-5BHLPT24.mjs +0 -2067
- package/dist/lib/node-esm/chunk-5BHLPT24.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-RVK35BS7.mjs +0 -126
- package/dist/lib/node-esm/chunk-RVK35BS7.mjs.map +0 -7
- package/dist/lib/node-esm/filter/index.mjs +0 -11
- package/dist/lib/node-esm/filter/index.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -4743
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -742
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/types/src/db-host/documents-iterator.d.ts +0 -7
- package/dist/types/src/db-host/documents-iterator.d.ts.map +0 -1
- package/dist/types/src/testing/test-data.d.ts +0 -18
- package/dist/types/src/testing/test-data.d.ts.map +0 -1
- package/dist/types/src/testing/test-schema.d.ts +0 -37
- package/dist/types/src/testing/test-schema.d.ts.map +0 -1
- package/src/db-host/documents-iterator.ts +0 -74
- package/src/testing/test-data.ts +0 -127
- package/src/testing/test-schema.ts +0 -53
- /package/dist/lib/{browser → neutral}/filter/index.mjs.map +0 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type * as SqlClient from '@effect/sql/SqlClient';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Function from 'effect/Function';
|
|
8
|
+
|
|
9
|
+
import { ATTR_META, type ObjectJSON } from '@dxos/echo/internal';
|
|
10
|
+
import type { ForeignKey } from '@dxos/echo-protocol';
|
|
11
|
+
import { RuntimeProvider } from '@dxos/effect';
|
|
12
|
+
import { type FeedStore } from '@dxos/feed';
|
|
13
|
+
import { assertArgument, invariant } from '@dxos/invariant';
|
|
14
|
+
import { type SpaceId } from '@dxos/keys';
|
|
15
|
+
import { FeedProtocol } from '@dxos/protocols';
|
|
16
|
+
import {
|
|
17
|
+
type DeleteFromQueueRequest,
|
|
18
|
+
type InsertIntoQueueRequest,
|
|
19
|
+
type QueryQueueRequest,
|
|
20
|
+
type QueueQueryResult,
|
|
21
|
+
type QueueService,
|
|
22
|
+
} from '@dxos/protocols/proto/dxos/client/services';
|
|
23
|
+
import type { SqlTransaction } from '@dxos/sql-sqlite';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Writes queue data to a local FeedStore.
|
|
27
|
+
*/
|
|
28
|
+
export class LocalQueueServiceImpl implements QueueService {
|
|
29
|
+
#runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient | SqlTransaction.SqlTransaction>;
|
|
30
|
+
#feedStore: FeedStore;
|
|
31
|
+
|
|
32
|
+
constructor(
|
|
33
|
+
runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient | SqlTransaction.SqlTransaction>,
|
|
34
|
+
feedStore: FeedStore,
|
|
35
|
+
) {
|
|
36
|
+
this.#runtime = runtime;
|
|
37
|
+
this.#feedStore = feedStore;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
queryQueue(request: QueryQueueRequest): Promise<QueueQueryResult> {
|
|
41
|
+
const { query } = request;
|
|
42
|
+
invariant(query, 'query is required');
|
|
43
|
+
const { spaceId, queueIds } = query;
|
|
44
|
+
return RuntimeProvider.runPromise(this.#runtime)(
|
|
45
|
+
Effect.gen(this, function* () {
|
|
46
|
+
const cursor = query.after ? parseInt(query.after) : -1;
|
|
47
|
+
const result = yield* this.#feedStore.query({
|
|
48
|
+
requestId: crypto.randomUUID(),
|
|
49
|
+
feedNamespace: request.query.queuesNamespace || FeedProtocol.WellKnownNamespaces.data,
|
|
50
|
+
spaceId: spaceId! as SpaceId,
|
|
51
|
+
query: { feedIds: queueIds ?? [] },
|
|
52
|
+
position: cursor,
|
|
53
|
+
limit: query.limit,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const objects = result.blocks.map((block: FeedProtocol.Block) => {
|
|
57
|
+
const data = JSON.parse(new TextDecoder().decode(block.data));
|
|
58
|
+
if (block.position !== null) {
|
|
59
|
+
setQueuePosition(data, block.position);
|
|
60
|
+
}
|
|
61
|
+
return data;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const lastBlock = result.blocks[result.blocks.length - 1];
|
|
65
|
+
const nextCursor = lastBlock && lastBlock.position != null ? String(lastBlock.position) : null;
|
|
66
|
+
|
|
67
|
+
return Function.identity<QueueQueryResult>({
|
|
68
|
+
objects,
|
|
69
|
+
|
|
70
|
+
// TODO(dmaretskyi): This is wrong, fix later - cursors should come directly from the feed.
|
|
71
|
+
nextCursor: nextCursor?.toString() ?? '',
|
|
72
|
+
prevCursor: '',
|
|
73
|
+
});
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
insertIntoQueue(request: InsertIntoQueueRequest): Promise<void> {
|
|
79
|
+
const { subspaceTag, spaceId, queueId, objects } = request;
|
|
80
|
+
const feedNamespace = subspaceTag ?? FeedProtocol.WellKnownNamespaces.data;
|
|
81
|
+
assertArgument(
|
|
82
|
+
FeedProtocol.isWellKnownNamespace(feedNamespace),
|
|
83
|
+
'request.subspaceTag',
|
|
84
|
+
'expected a well-known queue namespace',
|
|
85
|
+
);
|
|
86
|
+
return RuntimeProvider.runPromise(this.#runtime)(
|
|
87
|
+
Effect.gen(this, function* () {
|
|
88
|
+
const messages = objects!.map((obj) => {
|
|
89
|
+
const data = structuredClone(obj);
|
|
90
|
+
if (data[ATTR_META]?.keys?.find((key: ForeignKey) => key.source === FeedProtocol.KEY_QUEUE_POSITION)) {
|
|
91
|
+
data[ATTR_META].keys = data[ATTR_META].keys.filter(
|
|
92
|
+
(key: ForeignKey) => key.source !== FeedProtocol.KEY_QUEUE_POSITION,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
spaceId: spaceId,
|
|
98
|
+
feedId: queueId!,
|
|
99
|
+
feedNamespace,
|
|
100
|
+
data: new TextEncoder().encode(JSON.stringify(obj)),
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
yield* this.#feedStore.appendLocal(messages);
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
deleteFromQueue(request: DeleteFromQueueRequest): Promise<void> {
|
|
110
|
+
const { subspaceTag, spaceId, queueId, objectIds } = request;
|
|
111
|
+
const feedNamespace = subspaceTag ?? FeedProtocol.WellKnownNamespaces.data;
|
|
112
|
+
assertArgument(
|
|
113
|
+
FeedProtocol.isWellKnownNamespace(feedNamespace),
|
|
114
|
+
'request.subspaceTag',
|
|
115
|
+
'expected a well-known queue namespace',
|
|
116
|
+
);
|
|
117
|
+
return RuntimeProvider.runPromise(this.#runtime)(
|
|
118
|
+
Effect.gen(this, function* () {
|
|
119
|
+
const messages = objectIds!.map((id) => ({
|
|
120
|
+
spaceId: spaceId,
|
|
121
|
+
feedId: queueId!,
|
|
122
|
+
feedNamespace,
|
|
123
|
+
data: new TextEncoder().encode(JSON.stringify({ id, '@deleted': true })),
|
|
124
|
+
}));
|
|
125
|
+
|
|
126
|
+
yield* this.#feedStore.appendLocal(messages);
|
|
127
|
+
}),
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// TODO(dmaretskyi): Duplicated code.
|
|
133
|
+
const setQueuePosition = (obj: ObjectJSON, position: number) => {
|
|
134
|
+
obj[ATTR_META] ??= { keys: [] };
|
|
135
|
+
obj[ATTR_META].keys ??= [];
|
|
136
|
+
for (let i = 0; i < obj[ATTR_META].keys.length; i++) {
|
|
137
|
+
const key = obj[ATTR_META].keys[i];
|
|
138
|
+
if (key.source === FeedProtocol.KEY_QUEUE_POSITION) {
|
|
139
|
+
obj[ATTR_META].keys.splice(i, 1);
|
|
140
|
+
i--;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
obj[ATTR_META].keys.push({
|
|
144
|
+
source: FeedProtocol.KEY_QUEUE_POSITION,
|
|
145
|
+
id: position.toString(),
|
|
146
|
+
});
|
|
147
|
+
};
|
|
@@ -2,19 +2,17 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import { Schema } from 'effect';
|
|
5
|
+
import type * as SqlClient from '@effect/sql/SqlClient';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
8
7
|
|
|
9
8
|
import { DeferredTask, scheduleMicroTask, synchronized } from '@dxos/async';
|
|
10
9
|
import { Stream } from '@dxos/codec-protobuf/stream';
|
|
11
|
-
import { Context, Resource } from '@dxos/context';
|
|
10
|
+
import { type Context, Resource } from '@dxos/context';
|
|
12
11
|
import { raise } from '@dxos/debug';
|
|
13
|
-
import {
|
|
14
|
-
import { type
|
|
12
|
+
import { QueryAST } from '@dxos/echo-protocol';
|
|
13
|
+
import { type RuntimeProvider } from '@dxos/effect';
|
|
14
|
+
import { type IndexEngine } from '@dxos/index-core';
|
|
15
15
|
import { log } from '@dxos/log';
|
|
16
|
-
import { objectPointerCodec } from '@dxos/protocols';
|
|
17
|
-
import { type IndexConfig } from '@dxos/protocols/proto/dxos/echo/indexing';
|
|
18
16
|
import {
|
|
19
17
|
type QueryRequest,
|
|
20
18
|
type QueryResponse,
|
|
@@ -23,12 +21,14 @@ import {
|
|
|
23
21
|
} from '@dxos/protocols/proto/dxos/echo/query';
|
|
24
22
|
import { trace } from '@dxos/tracing';
|
|
25
23
|
|
|
26
|
-
import type { SpaceStateManager } from './space-state-manager';
|
|
27
24
|
import { type AutomergeHost } from '../automerge';
|
|
28
25
|
import { QueryExecutor } from '../query';
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
import type { SpaceStateManager } from './space-state-manager';
|
|
28
|
+
|
|
29
|
+
export type QueryServiceProps = {
|
|
30
|
+
indexEngine: IndexEngine;
|
|
31
|
+
runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient>;
|
|
32
32
|
automergeHost: AutomergeHost;
|
|
33
33
|
spaceStateManager: SpaceStateManager;
|
|
34
34
|
};
|
|
@@ -55,12 +55,13 @@ type ActiveQuery = {
|
|
|
55
55
|
|
|
56
56
|
@trace.resource()
|
|
57
57
|
export class QueryServiceImpl extends Resource implements QueryService {
|
|
58
|
+
// TODO(dmaretskyi): We need to implement query deduping. Idle composer has 80 queries with only 10 being unique.
|
|
58
59
|
private readonly _queries = new Set<ActiveQuery>();
|
|
59
60
|
|
|
60
61
|
private _updateQueries!: DeferredTask;
|
|
61
62
|
|
|
62
63
|
// TODO(burdon): OK for options, but not params. Pass separately and type readonly here.
|
|
63
|
-
constructor(private readonly _params:
|
|
64
|
+
constructor(private readonly _params: QueryServiceProps) {
|
|
64
65
|
super();
|
|
65
66
|
|
|
66
67
|
trace.diagnostic({
|
|
@@ -79,8 +80,6 @@ export class QueryServiceImpl extends Resource implements QueryService {
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
override async _open(): Promise<void> {
|
|
82
|
-
this._params.indexer.updated.on(this._ctx, () => this.invalidateQueries());
|
|
83
|
-
|
|
84
83
|
this._updateQueries = new DeferredTask(this._ctx, this._executeQueries.bind(this));
|
|
85
84
|
}
|
|
86
85
|
|
|
@@ -90,8 +89,19 @@ export class QueryServiceImpl extends Resource implements QueryService {
|
|
|
90
89
|
await Promise.all(Array.from(this._queries).map((query) => query.close()));
|
|
91
90
|
}
|
|
92
91
|
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
/**
|
|
93
|
+
* @deprecated No longer needed with SQL-based indexing.
|
|
94
|
+
*/
|
|
95
|
+
async setConfig(): Promise<void> {
|
|
96
|
+
// No-op: SQL indexer doesn't need explicit configuration.
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @deprecated No longer needed with SQL-based indexing.
|
|
101
|
+
*/
|
|
102
|
+
async reindex(): Promise<void> {
|
|
103
|
+
// No-op: SQL indexer handles re-indexing automatically.
|
|
104
|
+
log.warn('reindex() is deprecated and no longer has any effect');
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
execQuery(request: QueryRequest): Stream<QueryResponse> {
|
|
@@ -106,26 +116,6 @@ export class QueryServiceImpl extends Resource implements QueryService {
|
|
|
106
116
|
});
|
|
107
117
|
}
|
|
108
118
|
|
|
109
|
-
/**
|
|
110
|
-
* Re-index all loaded documents.
|
|
111
|
-
*/
|
|
112
|
-
async reindex(): Promise<void> {
|
|
113
|
-
log('Reindexing all documents...');
|
|
114
|
-
const iterator = createDocumentsIterator(this._params.automergeHost);
|
|
115
|
-
const ids: IdToHeads = new Map();
|
|
116
|
-
for await (const documents of iterator()) {
|
|
117
|
-
for (const { id, heads } of documents) {
|
|
118
|
-
ids.set(id, heads);
|
|
119
|
-
}
|
|
120
|
-
if (ids.size % 100 === 0) {
|
|
121
|
-
log('Collected documents...', { count: ids.size });
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
log('Marking all documents as dirty...', { count: ids.size });
|
|
126
|
-
await this._params.indexer.reindex(ids);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
119
|
/**
|
|
130
120
|
* Schedule re-execution of all queries.
|
|
131
121
|
*/
|
|
@@ -146,7 +136,8 @@ export class QueryServiceImpl extends Resource implements QueryService {
|
|
|
146
136
|
const parsedQuery = QueryAST.Query.pipe(Schema.decodeUnknownSync)(JSON.parse(request.query));
|
|
147
137
|
const queryEntry: ActiveQuery = {
|
|
148
138
|
executor: new QueryExecutor({
|
|
149
|
-
|
|
139
|
+
indexEngine: this._params.indexEngine,
|
|
140
|
+
runtime: this._params.runtime,
|
|
150
141
|
automergeHost: this._params.automergeHost,
|
|
151
142
|
queryId: request.queryId ?? raise(new Error('query id required')),
|
|
152
143
|
query: parsedQuery,
|
|
@@ -200,61 +191,3 @@ export class QueryServiceImpl extends Resource implements QueryService {
|
|
|
200
191
|
log.verbose('executed queries', { count, duration: performance.now() - begin });
|
|
201
192
|
}
|
|
202
193
|
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Factory for `getAllDocuments` iterator.
|
|
206
|
-
*/
|
|
207
|
-
// TODO(dmaretskyi): Get roots from echo-host.
|
|
208
|
-
const createDocumentsIterator = (automergeHost: AutomergeHost) =>
|
|
209
|
-
/**
|
|
210
|
-
* Recursively get all object data blobs from loaded documents from Automerge Repo.
|
|
211
|
-
*/
|
|
212
|
-
// TODO(mykola): Unload automerge handles after usage.
|
|
213
|
-
async function* getAllDocuments(): AsyncGenerator<ObjectSnapshot[], void, void> {
|
|
214
|
-
/** visited automerge handles */
|
|
215
|
-
const visited = new Set<string>();
|
|
216
|
-
|
|
217
|
-
async function* getObjectsFromHandle(handle: DocHandle<DatabaseDirectory>): AsyncGenerator<ObjectSnapshot[]> {
|
|
218
|
-
if (visited.has(handle.documentId) || !handle.isReady()) {
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const doc = handle.doc()!;
|
|
223
|
-
const spaceKey = DatabaseDirectory.getSpaceKey(doc) ?? undefined;
|
|
224
|
-
if (doc.objects) {
|
|
225
|
-
yield Object.entries(doc.objects as { [key: string]: any }).map(([objectId, object]) => {
|
|
226
|
-
return {
|
|
227
|
-
id: objectPointerCodec.encode({ documentId: handle.documentId, objectId, spaceKey }),
|
|
228
|
-
object,
|
|
229
|
-
heads: getHeads(doc),
|
|
230
|
-
};
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (doc.links) {
|
|
235
|
-
for (const id of Object.values(doc.links as { [echoId: string]: string })) {
|
|
236
|
-
const urlString = id.toString();
|
|
237
|
-
if (visited.has(urlString)) {
|
|
238
|
-
continue;
|
|
239
|
-
}
|
|
240
|
-
const linkHandle = await automergeHost.loadDoc<DatabaseDirectory>(Context.default(), urlString as DocumentId);
|
|
241
|
-
for await (const result of getObjectsFromHandle(linkHandle)) {
|
|
242
|
-
yield result;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
visited.add(handle.documentId);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// TODO(mykola): Use list of roots instead of iterating over all handles.
|
|
251
|
-
for (const handle of Object.values(automergeHost.repo.handles)) {
|
|
252
|
-
if (visited.has(handle.documentId)) {
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
for await (const result of getObjectsFromHandle(handle)) {
|
|
256
|
-
yield result;
|
|
257
|
-
}
|
|
258
|
-
visited.add(handle.documentId);
|
|
259
|
-
}
|
|
260
|
-
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type * as SqlClient from '@effect/sql/SqlClient';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
|
|
8
|
+
import { RuntimeProvider } from '@dxos/effect';
|
|
9
|
+
import { type FeedStore } from '@dxos/feed';
|
|
10
|
+
import { type DataSourceCursor, type IndexDataSource, type IndexerObject } from '@dxos/index-core';
|
|
11
|
+
import { failedInvariant } from '@dxos/invariant';
|
|
12
|
+
import type { SpaceId } from '@dxos/keys';
|
|
13
|
+
import { log } from '@dxos/log';
|
|
14
|
+
import { FeedProtocol } from '@dxos/protocols';
|
|
15
|
+
|
|
16
|
+
export type QueueDataSourceOptions = {
|
|
17
|
+
feedStore: FeedStore;
|
|
18
|
+
runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient>;
|
|
19
|
+
/**
|
|
20
|
+
* Callback to get the list of space IDs that should be indexed.
|
|
21
|
+
*/
|
|
22
|
+
getSpaceIds: () => SpaceId[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export class QueueDataSource implements IndexDataSource {
|
|
26
|
+
readonly sourceName = 'queue';
|
|
27
|
+
|
|
28
|
+
private readonly _feedStore: FeedStore;
|
|
29
|
+
private readonly _runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient>;
|
|
30
|
+
private readonly _getSpaceIds: () => SpaceId[];
|
|
31
|
+
|
|
32
|
+
constructor(options: QueueDataSourceOptions) {
|
|
33
|
+
this._feedStore = options.feedStore;
|
|
34
|
+
this._runtime = options.runtime;
|
|
35
|
+
this._getSpaceIds = options.getSpaceIds;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getChangedObjects(
|
|
39
|
+
cursors: DataSourceCursor[],
|
|
40
|
+
opts?: { limit?: number },
|
|
41
|
+
): Effect.Effect<{ objects: IndexerObject[]; cursors: DataSourceCursor[] }> {
|
|
42
|
+
// For queue, the cursor is assumed to have:
|
|
43
|
+
// spaceId = set
|
|
44
|
+
// resourceId = null
|
|
45
|
+
// cursor = feed cursor
|
|
46
|
+
|
|
47
|
+
// We also add new cursors from all previously unindexed spaces.
|
|
48
|
+
|
|
49
|
+
return Effect.gen(this, function* () {
|
|
50
|
+
const objects: IndexerObject[] = [];
|
|
51
|
+
const updatedCursors: DataSourceCursor[] = [];
|
|
52
|
+
|
|
53
|
+
// Augment cursors with new spaces that haven't been indexed yet.
|
|
54
|
+
const existingSpaceIds = new Set(cursors.map((c) => c.spaceId).filter(Boolean));
|
|
55
|
+
const allSpaceIds = this._getSpaceIds();
|
|
56
|
+
const augmentedCursors = [...cursors];
|
|
57
|
+
for (const spaceId of allSpaceIds) {
|
|
58
|
+
if (!existingSpaceIds.has(spaceId)) {
|
|
59
|
+
// Add a new cursor for this space starting from the beginning.
|
|
60
|
+
// Empty string cursor means "start from beginning".
|
|
61
|
+
augmentedCursors.push({
|
|
62
|
+
spaceId,
|
|
63
|
+
resourceId: null,
|
|
64
|
+
cursor: '',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Limit per call, but we might have multiple spaces.
|
|
70
|
+
// We should distribute limit or just fill up to limit.
|
|
71
|
+
let remainingLimit = opts?.limit ?? Infinity;
|
|
72
|
+
|
|
73
|
+
for (const cursor of augmentedCursors) {
|
|
74
|
+
if (remainingLimit <= 0) {
|
|
75
|
+
// Remaining cursors are not processed in this call.
|
|
76
|
+
updatedCursors.push(cursor);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (!cursor.spaceId) {
|
|
80
|
+
// Ignore cursors without spaceId.
|
|
81
|
+
updatedCursors.push(cursor);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Empty string cursor means "start from beginning".
|
|
86
|
+
const currentCursor =
|
|
87
|
+
typeof cursor.cursor === 'string' && cursor.cursor !== ''
|
|
88
|
+
? FeedProtocol.FeedCursor.make(cursor.cursor)
|
|
89
|
+
: undefined;
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const result = yield* this._feedStore.query({
|
|
93
|
+
spaceId: cursor.spaceId,
|
|
94
|
+
feedNamespace: FeedProtocol.WellKnownNamespaces.data,
|
|
95
|
+
cursor: currentCursor,
|
|
96
|
+
limit: remainingLimit,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Process blocks
|
|
100
|
+
for (const block of result.blocks) {
|
|
101
|
+
try {
|
|
102
|
+
const dataString = new TextDecoder().decode(block.data);
|
|
103
|
+
// TODO: Handle non-JSON data?
|
|
104
|
+
const data = JSON.parse(dataString);
|
|
105
|
+
|
|
106
|
+
objects.push({
|
|
107
|
+
spaceId: cursor.spaceId,
|
|
108
|
+
queueId: block.feedId ?? failedInvariant(),
|
|
109
|
+
documentId: null,
|
|
110
|
+
recordId: null,
|
|
111
|
+
data,
|
|
112
|
+
});
|
|
113
|
+
} catch (err) {
|
|
114
|
+
log.warn('Failed to parse block data for indexing', { spaceId: cursor.spaceId, err });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
remainingLimit -= result.blocks.length;
|
|
119
|
+
updatedCursors.push({
|
|
120
|
+
spaceId: cursor.spaceId,
|
|
121
|
+
resourceId: null,
|
|
122
|
+
cursor: result.nextCursor,
|
|
123
|
+
});
|
|
124
|
+
} catch (error) {
|
|
125
|
+
log.error('Error querying queue for indexing', { spaceId: cursor.spaceId, error });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return { objects, cursors: updatedCursors };
|
|
130
|
+
}).pipe(RuntimeProvider.provide(this._runtime), Effect.withSpan('QueueDataSource.getChangedObjects'), Effect.orDie);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type * as SqlClient from '@effect/sql/SqlClient';
|
|
6
|
+
import * as SqliteClient from '@effect/sql-sqlite-node/SqliteClient';
|
|
7
|
+
import { describe, expect, it } from '@effect/vitest';
|
|
8
|
+
import * as Effect from 'effect/Effect';
|
|
9
|
+
import * as Layer from 'effect/Layer';
|
|
10
|
+
|
|
11
|
+
import { RuntimeProvider } from '@dxos/effect';
|
|
12
|
+
import { FeedStore } from '@dxos/feed';
|
|
13
|
+
import { ObjectId, SpaceId } from '@dxos/keys';
|
|
14
|
+
import { FeedProtocol } from '@dxos/protocols';
|
|
15
|
+
import { SqlTransaction } from '@dxos/sql-sqlite';
|
|
16
|
+
|
|
17
|
+
import { LocalQueueServiceImpl } from './local-queue-service';
|
|
18
|
+
|
|
19
|
+
const TestLayer = SqlTransaction.layer.pipe(
|
|
20
|
+
Layer.provideMerge(
|
|
21
|
+
SqliteClient.layer({
|
|
22
|
+
filename: ':memory:',
|
|
23
|
+
}),
|
|
24
|
+
),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
describe('LocalQueueServiceImpl', () => {
|
|
28
|
+
it.effect('should insert and query items', () =>
|
|
29
|
+
Effect.gen(function* () {
|
|
30
|
+
const feedStore = new FeedStore({ localActorId: 'actor-id', assignPositions: true });
|
|
31
|
+
const runtime = yield* RuntimeProvider.currentRuntime<SqlClient.SqlClient | SqlTransaction.SqlTransaction>();
|
|
32
|
+
const service = new LocalQueueServiceImpl(runtime, feedStore);
|
|
33
|
+
yield* feedStore.migrate();
|
|
34
|
+
|
|
35
|
+
const spaceId = SpaceId.random();
|
|
36
|
+
const queueId = ObjectId.random();
|
|
37
|
+
const object1 = { id: 'obj1', data: 'test1' };
|
|
38
|
+
const object2 = { id: 'obj2', data: 'test2' };
|
|
39
|
+
|
|
40
|
+
yield* Effect.promise(() =>
|
|
41
|
+
service.insertIntoQueue({
|
|
42
|
+
subspaceTag: FeedProtocol.WellKnownNamespaces.data,
|
|
43
|
+
spaceId,
|
|
44
|
+
queueId,
|
|
45
|
+
objects: [object1, object2],
|
|
46
|
+
}),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const result = yield* Effect.promise(() =>
|
|
50
|
+
service.queryQueue({
|
|
51
|
+
query: { spaceId, queueIds: [queueId] },
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
expect(result.objects?.[0]).toMatchObject(object1);
|
|
55
|
+
expect(result.objects?.[1]).toMatchObject(object2);
|
|
56
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
it.effect('should delete items', () =>
|
|
60
|
+
Effect.gen(function* () {
|
|
61
|
+
const runtime = Effect.succeed(yield* Effect.runtime<any>());
|
|
62
|
+
const feedStore = new FeedStore({ localActorId: 'actor-id', assignPositions: true });
|
|
63
|
+
const service = new LocalQueueServiceImpl(runtime, feedStore);
|
|
64
|
+
yield* feedStore.migrate();
|
|
65
|
+
|
|
66
|
+
const spaceId = SpaceId.random();
|
|
67
|
+
const queueId = ObjectId.random();
|
|
68
|
+
const object1Id = ObjectId.random();
|
|
69
|
+
const object1 = { id: object1Id, data: 'test1' };
|
|
70
|
+
|
|
71
|
+
yield* Effect.promise(() =>
|
|
72
|
+
service.insertIntoQueue({
|
|
73
|
+
subspaceTag: FeedProtocol.WellKnownNamespaces.data,
|
|
74
|
+
spaceId,
|
|
75
|
+
queueId,
|
|
76
|
+
objects: [object1],
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
yield* Effect.promise(() =>
|
|
80
|
+
service.deleteFromQueue({
|
|
81
|
+
subspaceTag: FeedProtocol.WellKnownNamespaces.data,
|
|
82
|
+
spaceId,
|
|
83
|
+
queueId,
|
|
84
|
+
objectIds: [object1Id],
|
|
85
|
+
}),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const result = yield* Effect.promise(() =>
|
|
89
|
+
service.queryQueue({
|
|
90
|
+
query: { spaceId, queueIds: [queueId] },
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
expect(result.objects).toHaveLength(2);
|
|
94
|
+
expect(result.objects?.[1]).toMatchObject({ id: object1Id, '@deleted': true });
|
|
95
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
it.effect('should support pagination', () =>
|
|
99
|
+
Effect.gen(function* () {
|
|
100
|
+
const runtime = Effect.succeed(yield* Effect.runtime<any>());
|
|
101
|
+
const feedStore = new FeedStore({ localActorId: 'actor-id', assignPositions: true });
|
|
102
|
+
yield* feedStore.migrate();
|
|
103
|
+
const service = new LocalQueueServiceImpl(runtime, feedStore);
|
|
104
|
+
const spaceId = 'space-1' as SpaceId;
|
|
105
|
+
const queueId = ObjectId.random();
|
|
106
|
+
|
|
107
|
+
// Insert 10 items
|
|
108
|
+
const items = Array.from({ length: 10 }, (_, i) => ({ id: `obj${i}`, data: `test${i}` }));
|
|
109
|
+
yield* Effect.promise(() =>
|
|
110
|
+
service.insertIntoQueue({
|
|
111
|
+
subspaceTag: FeedProtocol.WellKnownNamespaces.data,
|
|
112
|
+
spaceId,
|
|
113
|
+
queueId,
|
|
114
|
+
objects: items,
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Query first 5
|
|
119
|
+
const page1 = yield* Effect.promise(() =>
|
|
120
|
+
service.queryQueue({
|
|
121
|
+
query: { spaceId, queueIds: [queueId], limit: 5 },
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
124
|
+
expect(page1.objects).toHaveLength(5);
|
|
125
|
+
expect(page1.objects?.[0]).toMatchObject(items[0]);
|
|
126
|
+
expect(page1.objects?.[4]).toMatchObject(items[4]);
|
|
127
|
+
expect(page1.nextCursor).toBeDefined();
|
|
128
|
+
|
|
129
|
+
// Query next 5
|
|
130
|
+
const page2 = yield* Effect.promise(() =>
|
|
131
|
+
service.queryQueue({
|
|
132
|
+
query: {
|
|
133
|
+
spaceId,
|
|
134
|
+
queueIds: [queueId],
|
|
135
|
+
limit: 5,
|
|
136
|
+
after: page1.nextCursor!,
|
|
137
|
+
},
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
expect(page2.objects).toHaveLength(5);
|
|
141
|
+
expect(page2.objects?.[0]).toMatchObject(items[5]);
|
|
142
|
+
expect(page2.objects?.[4]).toMatchObject(items[9]);
|
|
143
|
+
}).pipe(Effect.provide(TestLayer)),
|
|
144
|
+
);
|
|
145
|
+
});
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { type DocHandle, type DocumentId, interpretAsDocumentId } from '@automerge/automerge-repo';
|
|
6
6
|
import isEqual from 'lodash.isequal';
|
|
7
7
|
|
|
8
8
|
import { Event, UpdateScheduler } from '@dxos/async';
|
|
9
|
-
import {
|
|
9
|
+
import { Context, LifecycleState, Resource } from '@dxos/context';
|
|
10
10
|
import { type DatabaseDirectory } from '@dxos/echo-protocol';
|
|
11
11
|
import { invariant } from '@dxos/invariant';
|
|
12
12
|
import { type SpaceId } from '@dxos/keys';
|
|
@@ -32,6 +32,10 @@ export class SpaceStateManager extends Resource {
|
|
|
32
32
|
return this._roots;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
get spaceIds(): SpaceId[] {
|
|
36
|
+
return Array.from(this._rootBySpace.keys());
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
getRootByDocumentId(documentId: DocumentId): DatabaseRoot | undefined {
|
|
36
40
|
return this._roots.get(documentId);
|
|
37
41
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
DeleteFromQueueRequest,
|
|
7
|
+
InsertIntoQueueRequest,
|
|
8
|
+
QueryQueueRequest,
|
|
9
|
+
QueueQueryResult,
|
|
10
|
+
QueueService,
|
|
11
|
+
} from '@dxos/protocols/proto/dxos/client/services';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Stub implementation for when Edge is not available.
|
|
15
|
+
*/
|
|
16
|
+
export class QueueServiceStub implements QueueService {
|
|
17
|
+
queryQueue(request: QueryQueueRequest): Promise<QueueQueryResult> {
|
|
18
|
+
throw new Error('Not available.');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
insertIntoQueue(request: InsertIntoQueueRequest): Promise<void> {
|
|
22
|
+
throw new Error('Not available.');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
deleteFromQueue(request: DeleteFromQueueRequest): Promise<void> {
|
|
26
|
+
throw new Error('Not available.');
|
|
27
|
+
}
|
|
28
|
+
}
|