@powerhousedao/reactor 6.0.0-dev.25 → 6.0.0-dev.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cache/document-meta-cache.js +3 -3
- package/dist/src/cache/document-meta-cache.js.map +1 -1
- package/dist/src/cache/kysely-operation-index.d.ts +2 -1
- package/dist/src/cache/kysely-operation-index.d.ts.map +1 -1
- package/dist/src/cache/kysely-operation-index.js +16 -4
- package/dist/src/cache/kysely-operation-index.js.map +1 -1
- package/dist/src/cache/kysely-write-cache.js +8 -8
- package/dist/src/cache/kysely-write-cache.js.map +1 -1
- package/dist/src/cache/operation-index-types.d.ts +2 -1
- package/dist/src/cache/operation-index-types.d.ts.map +1 -1
- package/dist/src/cache/operation-index-types.js.map +1 -1
- package/dist/src/client/reactor-client.d.ts +4 -6
- package/dist/src/client/reactor-client.d.ts.map +1 -1
- package/dist/src/client/reactor-client.js +20 -25
- package/dist/src/client/reactor-client.js.map +1 -1
- package/dist/src/client/types.d.ts +1 -4
- package/dist/src/client/types.d.ts.map +1 -1
- package/dist/src/core/reactor-builder.d.ts.map +1 -1
- package/dist/src/core/reactor-builder.js +2 -5
- package/dist/src/core/reactor-builder.js.map +1 -1
- package/dist/src/core/reactor-client-builder.d.ts +3 -2
- package/dist/src/core/reactor-client-builder.d.ts.map +1 -1
- package/dist/src/core/reactor-client-builder.js +13 -4
- package/dist/src/core/reactor-client-builder.js.map +1 -1
- package/dist/src/core/reactor.d.ts +11 -75
- package/dist/src/core/reactor.d.ts.map +1 -1
- package/dist/src/core/reactor.js +89 -552
- package/dist/src/core/reactor.js.map +1 -1
- package/dist/src/core/types.d.ts +28 -19
- package/dist/src/core/types.d.ts.map +1 -1
- package/dist/src/core/utils.d.ts +6 -1
- package/dist/src/core/utils.d.ts.map +1 -1
- package/dist/src/core/utils.js +22 -6
- package/dist/src/core/utils.js.map +1 -1
- package/dist/src/events/types.d.ts +24 -8
- package/dist/src/events/types.d.ts.map +1 -1
- package/dist/src/events/types.js +7 -5
- package/dist/src/events/types.js.map +1 -1
- package/dist/src/executor/simple-job-executor-manager.d.ts.map +1 -1
- package/dist/src/executor/simple-job-executor-manager.js +14 -4
- package/dist/src/executor/simple-job-executor-manager.js.map +1 -1
- package/dist/src/executor/simple-job-executor.d.ts.map +1 -1
- package/dist/src/executor/simple-job-executor.js +24 -9
- package/dist/src/executor/simple-job-executor.js.map +1 -1
- package/dist/src/index.d.ts +4 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/job-tracker/in-memory-job-tracker.d.ts +2 -2
- package/dist/src/job-tracker/in-memory-job-tracker.d.ts.map +1 -1
- package/dist/src/job-tracker/in-memory-job-tracker.js +11 -11
- package/dist/src/job-tracker/in-memory-job-tracker.js.map +1 -1
- package/dist/src/read-models/base-read-model.js +4 -4
- package/dist/src/read-models/base-read-model.js.map +1 -1
- package/dist/src/read-models/coordinator.d.ts +2 -2
- package/dist/src/read-models/coordinator.d.ts.map +1 -1
- package/dist/src/read-models/coordinator.js +8 -8
- package/dist/src/read-models/coordinator.js.map +1 -1
- package/dist/src/read-models/document-view.d.ts +5 -2
- package/dist/src/read-models/document-view.d.ts.map +1 -1
- package/dist/src/read-models/document-view.js +128 -28
- package/dist/src/read-models/document-view.js.map +1 -1
- package/dist/src/shared/awaiter.d.ts +2 -2
- package/dist/src/shared/awaiter.d.ts.map +1 -1
- package/dist/src/shared/awaiter.js +11 -11
- package/dist/src/shared/awaiter.js.map +1 -1
- package/dist/src/shared/collect-all-pages.d.ts +7 -0
- package/dist/src/shared/collect-all-pages.d.ts.map +1 -0
- package/dist/src/shared/collect-all-pages.js +17 -0
- package/dist/src/shared/collect-all-pages.js.map +1 -0
- package/dist/src/shared/types.d.ts +4 -4
- package/dist/src/shared/types.d.ts.map +1 -1
- package/dist/src/shared/types.js +4 -4
- package/dist/src/shared/types.js.map +1 -1
- package/dist/src/signer/passthrough-signer.d.ts +1 -1
- package/dist/src/signer/passthrough-signer.d.ts.map +1 -1
- package/dist/src/signer/passthrough-signer.js +1 -3
- package/dist/src/signer/passthrough-signer.js.map +1 -1
- package/dist/src/storage/interfaces.d.ts +38 -94
- package/dist/src/storage/interfaces.d.ts.map +1 -1
- package/dist/src/storage/interfaces.js.map +1 -1
- package/dist/src/storage/kysely/document-indexer.d.ts +6 -6
- package/dist/src/storage/kysely/document-indexer.d.ts.map +1 -1
- package/dist/src/storage/kysely/document-indexer.js +123 -52
- package/dist/src/storage/kysely/document-indexer.js.map +1 -1
- package/dist/src/storage/kysely/store.d.ts +2 -1
- package/dist/src/storage/kysely/store.d.ts.map +1 -1
- package/dist/src/storage/kysely/store.js +33 -15
- package/dist/src/storage/kysely/store.js.map +1 -1
- package/dist/src/subs/subscription-notification-read-model.d.ts +1 -1
- package/dist/src/subs/subscription-notification-read-model.js +1 -1
- package/dist/src/sync/channels/composite-channel-factory.d.ts.map +1 -1
- package/dist/src/sync/channels/composite-channel-factory.js +5 -2
- package/dist/src/sync/channels/composite-channel-factory.js.map +1 -1
- package/dist/src/sync/channels/gql-channel-factory.d.ts.map +1 -1
- package/dist/src/sync/channels/gql-channel-factory.js +5 -2
- package/dist/src/sync/channels/gql-channel-factory.js.map +1 -1
- package/dist/src/sync/channels/gql-channel.d.ts +4 -9
- package/dist/src/sync/channels/gql-channel.d.ts.map +1 -1
- package/dist/src/sync/channels/gql-channel.js +11 -31
- package/dist/src/sync/channels/gql-channel.js.map +1 -1
- package/dist/src/sync/channels/index.d.ts +2 -0
- package/dist/src/sync/channels/index.d.ts.map +1 -1
- package/dist/src/sync/channels/index.js +2 -0
- package/dist/src/sync/channels/index.js.map +1 -1
- package/dist/src/sync/channels/interval-poll-timer.d.ts +18 -0
- package/dist/src/sync/channels/interval-poll-timer.d.ts.map +1 -0
- package/dist/src/sync/channels/interval-poll-timer.js +43 -0
- package/dist/src/sync/channels/interval-poll-timer.js.map +1 -0
- package/dist/src/sync/channels/poll-timer.d.ts +14 -0
- package/dist/src/sync/channels/poll-timer.d.ts.map +1 -0
- package/dist/src/sync/channels/poll-timer.js +2 -0
- package/dist/src/sync/channels/poll-timer.js.map +1 -0
- package/dist/src/sync/channels/utils.d.ts.map +1 -1
- package/dist/src/sync/channels/utils.js +2 -2
- package/dist/src/sync/channels/utils.js.map +1 -1
- package/dist/src/sync/index.d.ts +3 -3
- package/dist/src/sync/index.d.ts.map +1 -1
- package/dist/src/sync/index.js +2 -2
- package/dist/src/sync/index.js.map +1 -1
- package/dist/src/sync/interfaces.d.ts +11 -1
- package/dist/src/sync/interfaces.d.ts.map +1 -1
- package/dist/src/sync/sync-awaiter.d.ts +34 -0
- package/dist/src/sync/sync-awaiter.d.ts.map +1 -0
- package/dist/src/sync/sync-awaiter.js +124 -0
- package/dist/src/sync/sync-awaiter.js.map +1 -0
- package/dist/src/sync/sync-manager.d.ts +6 -2
- package/dist/src/sync/sync-manager.d.ts.map +1 -1
- package/dist/src/sync/sync-manager.js +87 -9
- package/dist/src/sync/sync-manager.js.map +1 -1
- package/dist/src/sync/sync-operation.d.ts +2 -1
- package/dist/src/sync/sync-operation.d.ts.map +1 -1
- package/dist/src/sync/sync-operation.js +3 -1
- package/dist/src/sync/sync-operation.js.map +1 -1
- package/dist/src/sync/types.d.ts +62 -0
- package/dist/src/sync/types.d.ts.map +1 -1
- package/dist/src/sync/types.js +10 -0
- package/dist/src/sync/types.js.map +1 -1
- package/package.json +3 -3
- package/dist/src/storage/consistency-aware-legacy-storage.d.ts +0 -33
- package/dist/src/storage/consistency-aware-legacy-storage.d.ts.map +0 -1
- package/dist/src/storage/consistency-aware-legacy-storage.js +0 -65
- package/dist/src/storage/consistency-aware-legacy-storage.js.map +0 -1
package/dist/src/core/reactor.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { addRelationshipAction, createDocumentAction, deleteDocumentAction, removeRelationshipAction, upgradeDocumentAction, } from "#actions/index.js";
|
|
2
2
|
import { AbortError } from "document-drive";
|
|
3
3
|
import { v4 as uuidv4 } from "uuid";
|
|
4
|
+
import { ReactorEventTypes } from "../events/types.js";
|
|
4
5
|
import { createMutableShutdownStatus } from "../shared/factories.js";
|
|
5
6
|
import { JobStatus } from "../shared/types.js";
|
|
6
7
|
import { matchesScope } from "../shared/utils.js";
|
|
7
|
-
import { filterByType,
|
|
8
|
+
import { filterByType, getSharedActionScope, getSharedOperationScope, signAction, signActions, toErrorInfo, topologicalSort, validateActionScopes, validateBatchRequest, } from "./utils.js";
|
|
8
9
|
/**
|
|
9
10
|
* This class implements the IReactor interface and serves as the main entry point
|
|
10
11
|
* for the new Reactor architecture.
|
|
@@ -12,7 +13,6 @@ import { filterByType, getSharedScope, signAction, signActions, toErrorInfo, top
|
|
|
12
13
|
export class Reactor {
|
|
13
14
|
logger;
|
|
14
15
|
documentModelRegistry;
|
|
15
|
-
documentStorage;
|
|
16
16
|
shutdownStatus;
|
|
17
17
|
setShutdown;
|
|
18
18
|
queue;
|
|
@@ -20,57 +20,44 @@ export class Reactor {
|
|
|
20
20
|
readModelCoordinator;
|
|
21
21
|
features;
|
|
22
22
|
documentView;
|
|
23
|
-
|
|
23
|
+
documentIndexer;
|
|
24
24
|
operationStore;
|
|
25
|
-
|
|
25
|
+
eventBus;
|
|
26
|
+
constructor(logger, documentModelRegistry, queue, jobTracker, readModelCoordinator, features, documentView, documentIndexer, operationStore, eventBus) {
|
|
26
27
|
this.logger = logger;
|
|
27
28
|
this.documentModelRegistry = documentModelRegistry;
|
|
28
|
-
this.documentStorage = documentStorage;
|
|
29
29
|
this.queue = queue;
|
|
30
30
|
this.jobTracker = jobTracker;
|
|
31
31
|
this.readModelCoordinator = readModelCoordinator;
|
|
32
32
|
this.features = features;
|
|
33
33
|
this.documentView = documentView;
|
|
34
|
-
this.
|
|
34
|
+
this.documentIndexer = documentIndexer;
|
|
35
35
|
this.operationStore = operationStore;
|
|
36
|
+
this.eventBus = eventBus;
|
|
36
37
|
const [status, setter] = createMutableShutdownStatus(false);
|
|
37
38
|
this.shutdownStatus = status;
|
|
38
39
|
this.setShutdown = setter;
|
|
39
|
-
this.logger.verbose("Reactor({ legacyStorage: @legacy })", features.legacyStorageEnabled);
|
|
40
40
|
this.readModelCoordinator.start();
|
|
41
41
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Signals that the reactor should shutdown.
|
|
44
|
-
*/
|
|
45
42
|
kill() {
|
|
46
43
|
this.logger.verbose("kill()");
|
|
47
|
-
// Mark the reactor as shutdown
|
|
48
44
|
this.setShutdown(true);
|
|
49
|
-
// Stop the read model coordinator
|
|
50
45
|
this.readModelCoordinator.stop();
|
|
51
|
-
// Stop the job tracker
|
|
52
46
|
this.jobTracker.shutdown();
|
|
53
47
|
return this.shutdownStatus;
|
|
54
48
|
}
|
|
55
|
-
/**
|
|
56
|
-
* Retrieves a list of document model specifications
|
|
57
|
-
*/
|
|
58
49
|
getDocumentModels(namespace, paging, signal) {
|
|
59
50
|
this.logger.verbose("getDocumentModels(@namespace, @paging)", namespace, paging);
|
|
60
|
-
|
|
51
|
+
if (signal?.aborted) {
|
|
52
|
+
throw new AbortError();
|
|
53
|
+
}
|
|
61
54
|
const modules = this.documentModelRegistry.getAllModules();
|
|
62
55
|
const filteredModels = modules.filter((module) => !namespace || module.documentModel.global.id.startsWith(namespace));
|
|
63
|
-
// Apply paging
|
|
64
56
|
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
65
57
|
const limit = paging?.limit || filteredModels.length;
|
|
66
58
|
const pagedModels = filteredModels.slice(startIndex, startIndex + limit);
|
|
67
|
-
// Create paged results
|
|
68
59
|
const hasMore = startIndex + limit < filteredModels.length;
|
|
69
60
|
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
70
|
-
// even thought this is currently synchronous, they could have passed in an already-aborted signal
|
|
71
|
-
if (signal?.aborted) {
|
|
72
|
-
throw new AbortError();
|
|
73
|
-
}
|
|
74
61
|
return Promise.resolve({
|
|
75
62
|
results: pagedModels,
|
|
76
63
|
options: paging || { cursor: "0", limit: filteredModels.length },
|
|
@@ -80,197 +67,70 @@ export class Reactor {
|
|
|
80
67
|
: undefined,
|
|
81
68
|
});
|
|
82
69
|
}
|
|
83
|
-
/**
|
|
84
|
-
* Retrieves a specific PHDocument by id
|
|
85
|
-
*/
|
|
86
70
|
async get(id, view, consistencyToken, signal) {
|
|
87
71
|
this.logger.verbose("get(@id, @view)", id, view);
|
|
88
|
-
|
|
89
|
-
const document = await this.documentStorage.get(id, consistencyToken, signal);
|
|
90
|
-
if (signal?.aborted) {
|
|
91
|
-
throw new AbortError();
|
|
92
|
-
}
|
|
93
|
-
const childIds = await this.documentStorage.getChildren(id, consistencyToken, signal);
|
|
94
|
-
if (signal?.aborted) {
|
|
95
|
-
throw new AbortError();
|
|
96
|
-
}
|
|
97
|
-
for (const scope in document.state) {
|
|
98
|
-
if (!matchesScope(view, scope)) {
|
|
99
|
-
delete document.state[scope];
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return {
|
|
103
|
-
document,
|
|
104
|
-
childIds,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
const document = await this.documentView.get(id, view, consistencyToken, signal);
|
|
109
|
-
if (signal?.aborted) {
|
|
110
|
-
throw new AbortError();
|
|
111
|
-
}
|
|
112
|
-
const relationships = await this._documentIndexer.getOutgoing(id, ["child"], consistencyToken, signal);
|
|
113
|
-
if (signal?.aborted) {
|
|
114
|
-
throw new AbortError();
|
|
115
|
-
}
|
|
116
|
-
const childIds = relationships.map((rel) => rel.targetId);
|
|
117
|
-
return {
|
|
118
|
-
document,
|
|
119
|
-
childIds,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
72
|
+
return await this.documentView.get(id, view, consistencyToken, signal);
|
|
122
73
|
}
|
|
123
|
-
/**
|
|
124
|
-
* Retrieves a specific PHDocument by slug
|
|
125
|
-
*/
|
|
126
74
|
async getBySlug(slug, view, consistencyToken, signal) {
|
|
127
75
|
this.logger.verbose("getBySlug(@slug, @view)", slug, view);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
ids = await this.documentStorage.resolveIds([slug], consistencyToken, signal);
|
|
132
|
-
}
|
|
133
|
-
catch (error) {
|
|
134
|
-
if (error instanceof Error && error.message.includes("not found")) {
|
|
135
|
-
throw new Error(`Document not found with slug: ${slug}`);
|
|
136
|
-
}
|
|
137
|
-
throw error;
|
|
138
|
-
}
|
|
139
|
-
if (ids.length === 0 || !ids[0]) {
|
|
140
|
-
throw new Error(`Document not found with slug: ${slug}`);
|
|
141
|
-
}
|
|
142
|
-
return await this.get(ids[0], view, consistencyToken, signal);
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
const documentId = await this.documentView.resolveSlug(slug, view, consistencyToken, signal);
|
|
146
|
-
if (!documentId) {
|
|
147
|
-
throw new Error(`Document not found with slug: ${slug}`);
|
|
148
|
-
}
|
|
149
|
-
return await this.get(documentId, view, consistencyToken, signal);
|
|
76
|
+
const documentId = await this.documentView.resolveSlug(slug, view, consistencyToken, signal);
|
|
77
|
+
if (!documentId) {
|
|
78
|
+
throw new Error(`Document not found with slug: ${slug}`);
|
|
150
79
|
}
|
|
80
|
+
return await this.get(documentId, view, consistencyToken, signal);
|
|
151
81
|
}
|
|
152
|
-
/**
|
|
153
|
-
* Retrieves a specific PHDocument by identifier (either id or slug)
|
|
154
|
-
*/
|
|
155
82
|
async getByIdOrSlug(identifier, view, consistencyToken, signal) {
|
|
156
83
|
this.logger.verbose("getByIdOrSlug(@identifier, @view)", identifier, view);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const ids = await this.documentStorage.resolveIds([identifier], consistencyToken, signal);
|
|
164
|
-
if (ids.length === 0 || !ids[0]) {
|
|
165
|
-
throw new Error(`Document not found: ${identifier}`);
|
|
166
|
-
}
|
|
167
|
-
return await this.get(ids[0], view, consistencyToken, signal);
|
|
168
|
-
}
|
|
169
|
-
catch {
|
|
170
|
-
throw new Error(`Document not found: ${identifier}`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
84
|
+
return await this.documentView.getByIdOrSlug(identifier, view, consistencyToken, signal);
|
|
85
|
+
}
|
|
86
|
+
async getChildren(documentId, consistencyToken, signal) {
|
|
87
|
+
const relationships = await this.documentIndexer.getOutgoing(documentId, ["child"], undefined, consistencyToken, signal);
|
|
88
|
+
if (signal?.aborted) {
|
|
89
|
+
throw new AbortError();
|
|
173
90
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (signal?.aborted) {
|
|
181
|
-
throw new AbortError();
|
|
182
|
-
}
|
|
183
|
-
const childIds = relationships.map((rel) => rel.targetId);
|
|
184
|
-
return {
|
|
185
|
-
document,
|
|
186
|
-
childIds,
|
|
187
|
-
};
|
|
91
|
+
return relationships.results.map((rel) => rel.targetId);
|
|
92
|
+
}
|
|
93
|
+
async getParents(childId, consistencyToken, signal) {
|
|
94
|
+
const relationships = await this.documentIndexer.getIncoming(childId, ["parent"], undefined, consistencyToken, signal);
|
|
95
|
+
if (signal?.aborted) {
|
|
96
|
+
throw new AbortError();
|
|
188
97
|
}
|
|
98
|
+
return relationships.results.map((rel) => rel.sourceId);
|
|
189
99
|
}
|
|
190
|
-
/**
|
|
191
|
-
* Retrieves the operations for a document
|
|
192
|
-
*/
|
|
193
100
|
async getOperations(documentId, view, filter, paging, consistencyToken, signal) {
|
|
194
101
|
this.logger.verbose("getOperations(@documentId, @view, @filter, @paging)", documentId, view, filter, paging);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
const operations = document.operations;
|
|
201
|
-
const result = {};
|
|
202
|
-
for (const scope in operations) {
|
|
203
|
-
if (matchesScope(view, scope)) {
|
|
204
|
-
let scopeOperations = operations[scope];
|
|
205
|
-
if (filter) {
|
|
206
|
-
if (filter.actionTypes && filter.actionTypes.length > 0) {
|
|
207
|
-
scopeOperations = scopeOperations.filter((op) => filter.actionTypes.includes(op.action.type));
|
|
208
|
-
}
|
|
209
|
-
if (filter.sinceRevision !== undefined) {
|
|
210
|
-
scopeOperations = scopeOperations.filter((op) => op.index >= filter.sinceRevision);
|
|
211
|
-
}
|
|
212
|
-
if (filter.timestampFrom) {
|
|
213
|
-
const fromMs = new Date(filter.timestampFrom).getTime();
|
|
214
|
-
scopeOperations = scopeOperations.filter((op) => new Date(op.timestampUtcMs).getTime() >= fromMs);
|
|
215
|
-
}
|
|
216
|
-
if (filter.timestampTo) {
|
|
217
|
-
const toMs = new Date(filter.timestampTo).getTime();
|
|
218
|
-
scopeOperations = scopeOperations.filter((op) => new Date(op.timestampUtcMs).getTime() <= toMs);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
222
|
-
const limit = paging?.limit || scopeOperations.length;
|
|
223
|
-
const pagedOperations = scopeOperations.slice(startIndex, startIndex + limit);
|
|
224
|
-
result[scope] = {
|
|
225
|
-
results: pagedOperations,
|
|
226
|
-
options: { cursor: String(startIndex + limit), limit },
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
return Promise.resolve(result);
|
|
102
|
+
const branch = view?.branch || "main";
|
|
103
|
+
const revisions = await this.operationStore.getRevisions(documentId, branch, signal);
|
|
104
|
+
if (signal?.aborted) {
|
|
105
|
+
throw new AbortError();
|
|
231
106
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
107
|
+
const allScopes = Object.keys(revisions.revision);
|
|
108
|
+
const result = {};
|
|
109
|
+
for (const scope of allScopes) {
|
|
110
|
+
if (!matchesScope(view, scope)) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
235
113
|
if (signal?.aborted) {
|
|
236
114
|
throw new AbortError();
|
|
237
115
|
}
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
cursor: scopeResult.nextCursor || String(currentCursor),
|
|
254
|
-
limit: currentLimit,
|
|
255
|
-
},
|
|
256
|
-
nextCursor: scopeResult.nextCursor,
|
|
257
|
-
next: scopeResult.hasMore
|
|
258
|
-
? async () => {
|
|
259
|
-
const nextPage = await this.getOperations(documentId, view, filter, {
|
|
260
|
-
cursor: scopeResult.nextCursor,
|
|
261
|
-
limit: currentLimit,
|
|
262
|
-
}, consistencyToken, signal);
|
|
263
|
-
return nextPage[scope];
|
|
264
|
-
}
|
|
265
|
-
: undefined,
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
return result;
|
|
116
|
+
const scopeResult = await this.operationStore.getSince(documentId, scope, branch, -1, filter, paging, signal);
|
|
117
|
+
result[scope] = {
|
|
118
|
+
results: scopeResult.results,
|
|
119
|
+
options: scopeResult.options,
|
|
120
|
+
nextCursor: scopeResult.nextCursor,
|
|
121
|
+
next: scopeResult.next
|
|
122
|
+
? async () => {
|
|
123
|
+
const nextPage = await this.getOperations(documentId, view, filter, {
|
|
124
|
+
cursor: scopeResult.nextCursor,
|
|
125
|
+
limit: scopeResult.options.limit,
|
|
126
|
+
}, consistencyToken, signal);
|
|
127
|
+
return nextPage[scope];
|
|
128
|
+
}
|
|
129
|
+
: undefined,
|
|
130
|
+
};
|
|
269
131
|
}
|
|
132
|
+
return result;
|
|
270
133
|
}
|
|
271
|
-
/**
|
|
272
|
-
* Filters documents by criteria and returns a list of them
|
|
273
|
-
*/
|
|
274
134
|
async find(search, view, paging, consistencyToken, signal) {
|
|
275
135
|
this.logger.verbose("find(@search, @view, @paging)", search, view, paging);
|
|
276
136
|
let results;
|
|
@@ -290,7 +150,7 @@ export class Reactor {
|
|
|
290
150
|
}
|
|
291
151
|
}
|
|
292
152
|
else if (search.parentId) {
|
|
293
|
-
results = await this.findByParentId(search.parentId, view, paging, signal);
|
|
153
|
+
results = await this.findByParentId(search.parentId, view, paging, consistencyToken, signal);
|
|
294
154
|
if (search.type) {
|
|
295
155
|
results = filterByType(results, search.type);
|
|
296
156
|
}
|
|
@@ -306,9 +166,6 @@ export class Reactor {
|
|
|
306
166
|
}
|
|
307
167
|
return results;
|
|
308
168
|
}
|
|
309
|
-
/**
|
|
310
|
-
* Creates a document
|
|
311
|
-
*/
|
|
312
169
|
async create(document, signer, signal, meta) {
|
|
313
170
|
this.logger.verbose("create(@id, @type, @slug)", document.header.id, document.header.documentType, document.header.slug);
|
|
314
171
|
const createdAtUtcIso = new Date().toISOString();
|
|
@@ -342,12 +199,10 @@ export class Reactor {
|
|
|
342
199
|
toVersion: document.state.document.version,
|
|
343
200
|
initialState: document.state,
|
|
344
201
|
});
|
|
345
|
-
// Sign actions if signer is provided
|
|
346
202
|
let actions = [createAction, upgradeAction];
|
|
347
203
|
if (signer) {
|
|
348
204
|
actions = await signActions(actions, signer, signal);
|
|
349
205
|
}
|
|
350
|
-
// Create a single job with both CREATE_DOCUMENT and UPGRADE_DOCUMENT actions
|
|
351
206
|
const job = {
|
|
352
207
|
id: uuidv4(),
|
|
353
208
|
kind: "mutation",
|
|
@@ -362,7 +217,6 @@ export class Reactor {
|
|
|
362
217
|
errorHistory: [],
|
|
363
218
|
meta,
|
|
364
219
|
};
|
|
365
|
-
// Create job info and register with tracker
|
|
366
220
|
const jobInfo = {
|
|
367
221
|
id: job.id,
|
|
368
222
|
status: JobStatus.PENDING,
|
|
@@ -375,13 +229,10 @@ export class Reactor {
|
|
|
375
229
|
meta,
|
|
376
230
|
};
|
|
377
231
|
this.jobTracker.registerJob(jobInfo);
|
|
378
|
-
|
|
232
|
+
this.emitJobPending(jobInfo.id, meta);
|
|
379
233
|
await this.queue.enqueue(job);
|
|
380
234
|
return jobInfo;
|
|
381
235
|
}
|
|
382
|
-
/**
|
|
383
|
-
* Deletes a document
|
|
384
|
-
*/
|
|
385
236
|
async deleteDocument(id, signer, signal, meta) {
|
|
386
237
|
this.logger.verbose("deleteDocument(@id)", id);
|
|
387
238
|
const createdAtUtcIso = new Date().toISOString();
|
|
@@ -389,7 +240,6 @@ export class Reactor {
|
|
|
389
240
|
throw new AbortError();
|
|
390
241
|
}
|
|
391
242
|
let action = deleteDocumentAction(id);
|
|
392
|
-
// Sign action if signer is provided
|
|
393
243
|
if (signer) {
|
|
394
244
|
action = await signAction(action, signer, signal);
|
|
395
245
|
}
|
|
@@ -419,21 +269,17 @@ export class Reactor {
|
|
|
419
269
|
meta,
|
|
420
270
|
};
|
|
421
271
|
this.jobTracker.registerJob(jobInfo);
|
|
272
|
+
this.emitJobPending(jobInfo.id, meta);
|
|
422
273
|
await this.queue.enqueue(job);
|
|
423
274
|
return jobInfo;
|
|
424
275
|
}
|
|
425
|
-
/**
|
|
426
|
-
* Applies a list of actions to a document
|
|
427
|
-
*/
|
|
428
276
|
async execute(docId, branch, actions, signal, meta) {
|
|
429
277
|
this.logger.verbose("execute(@docId, @branch, @actions)", docId, branch, actions);
|
|
430
278
|
if (signal?.aborted) {
|
|
431
279
|
throw new AbortError();
|
|
432
280
|
}
|
|
433
281
|
const createdAtUtcIso = new Date().toISOString();
|
|
434
|
-
|
|
435
|
-
const scope = actions.length > 0 ? actions[0].scope || "global" : "global";
|
|
436
|
-
// Create a single job with all actions
|
|
282
|
+
const scope = getSharedActionScope(actions);
|
|
437
283
|
const job = {
|
|
438
284
|
id: uuidv4(),
|
|
439
285
|
kind: "mutation",
|
|
@@ -448,7 +294,6 @@ export class Reactor {
|
|
|
448
294
|
errorHistory: [],
|
|
449
295
|
meta,
|
|
450
296
|
};
|
|
451
|
-
// Create job info and register with tracker
|
|
452
297
|
const jobInfo = {
|
|
453
298
|
id: job.id,
|
|
454
299
|
status: JobStatus.PENDING,
|
|
@@ -461,18 +306,13 @@ export class Reactor {
|
|
|
461
306
|
meta,
|
|
462
307
|
};
|
|
463
308
|
this.jobTracker.registerJob(jobInfo);
|
|
464
|
-
|
|
309
|
+
this.emitJobPending(jobInfo.id, meta);
|
|
465
310
|
await this.queue.enqueue(job);
|
|
466
311
|
if (signal?.aborted) {
|
|
467
312
|
throw new AbortError();
|
|
468
313
|
}
|
|
469
314
|
return jobInfo;
|
|
470
315
|
}
|
|
471
|
-
/**
|
|
472
|
-
* Imports pre-existing operations that were produced by another reactor.
|
|
473
|
-
* This function may cause a reshuffle, which will generate additional
|
|
474
|
-
* operations.
|
|
475
|
-
*/
|
|
476
316
|
async load(docId, branch, operations, signal, meta) {
|
|
477
317
|
this.logger.verbose("load(@docId, @branch, @count, @operations)", docId, branch, operations.length, operations);
|
|
478
318
|
if (signal?.aborted) {
|
|
@@ -481,13 +321,7 @@ export class Reactor {
|
|
|
481
321
|
if (operations.length === 0) {
|
|
482
322
|
throw new Error("load requires at least one operation");
|
|
483
323
|
}
|
|
484
|
-
|
|
485
|
-
const scope = getSharedScope(operations);
|
|
486
|
-
operations.forEach((operation, index) => {
|
|
487
|
-
if (!operation.timestampUtcMs) {
|
|
488
|
-
throw new Error(`Operation at position ${index} is missing timestampUtcMs`);
|
|
489
|
-
}
|
|
490
|
-
});
|
|
324
|
+
const scope = getSharedOperationScope(operations);
|
|
491
325
|
const createdAtUtcIso = new Date().toISOString();
|
|
492
326
|
const job = {
|
|
493
327
|
id: uuidv4(),
|
|
@@ -515,15 +349,13 @@ export class Reactor {
|
|
|
515
349
|
meta,
|
|
516
350
|
};
|
|
517
351
|
this.jobTracker.registerJob(jobInfo);
|
|
352
|
+
this.emitJobPending(jobInfo.id, meta);
|
|
518
353
|
await this.queue.enqueue(job);
|
|
519
354
|
if (signal?.aborted) {
|
|
520
355
|
throw new AbortError();
|
|
521
356
|
}
|
|
522
357
|
return jobInfo;
|
|
523
358
|
}
|
|
524
|
-
/**
|
|
525
|
-
* Applies multiple mutations across documents with dependency management
|
|
526
|
-
*/
|
|
527
359
|
async executeBatch(request, signal, meta) {
|
|
528
360
|
this.logger.verbose("executeBatch(@count jobs)", request.jobs.length);
|
|
529
361
|
if (signal?.aborted) {
|
|
@@ -553,6 +385,7 @@ export class Reactor {
|
|
|
553
385
|
meta,
|
|
554
386
|
};
|
|
555
387
|
this.jobTracker.registerJob(jobInfo);
|
|
388
|
+
this.emitJobPending(jobInfo.id, meta);
|
|
556
389
|
jobInfos.set(jobPlan.key, jobInfo);
|
|
557
390
|
}
|
|
558
391
|
const sortedKeys = topologicalSort(request.jobs);
|
|
@@ -603,39 +436,28 @@ export class Reactor {
|
|
|
603
436
|
};
|
|
604
437
|
return result;
|
|
605
438
|
}
|
|
606
|
-
/**
|
|
607
|
-
* Adds multiple documents as children to another
|
|
608
|
-
*/
|
|
609
439
|
async addChildren(parentId, documentIds, branch = "main", signer, signal) {
|
|
610
440
|
this.logger.verbose("addChildren(@parentId, @count children, @branch)", parentId, documentIds.length, branch);
|
|
611
441
|
if (signal?.aborted) {
|
|
612
442
|
throw new AbortError();
|
|
613
443
|
}
|
|
614
444
|
let actions = documentIds.map((childId) => addRelationshipAction(parentId, childId, "child"));
|
|
615
|
-
// Sign actions if signer is provided
|
|
616
445
|
if (signer) {
|
|
617
446
|
actions = await signActions(actions, signer, signal);
|
|
618
447
|
}
|
|
619
448
|
return await this.execute(parentId, branch, actions, signal);
|
|
620
449
|
}
|
|
621
|
-
/**
|
|
622
|
-
* Removes multiple documents as children from another
|
|
623
|
-
*/
|
|
624
450
|
async removeChildren(parentId, documentIds, branch = "main", signer, signal) {
|
|
625
451
|
this.logger.verbose("removeChildren(@parentId, @count children, @branch)", parentId, documentIds.length, branch);
|
|
626
452
|
if (signal?.aborted) {
|
|
627
453
|
throw new AbortError();
|
|
628
454
|
}
|
|
629
455
|
let actions = documentIds.map((childId) => removeRelationshipAction(parentId, childId, "child"));
|
|
630
|
-
// Sign actions if signer is provided
|
|
631
456
|
if (signer) {
|
|
632
457
|
actions = await signActions(actions, signer, signal);
|
|
633
458
|
}
|
|
634
459
|
return await this.execute(parentId, branch, actions, signal);
|
|
635
460
|
}
|
|
636
|
-
/**
|
|
637
|
-
* Retrieves the status of a job
|
|
638
|
-
*/
|
|
639
461
|
getJobStatus(jobId, signal) {
|
|
640
462
|
this.logger.verbose("getJobStatus(@jobId)", jobId);
|
|
641
463
|
if (signal?.aborted) {
|
|
@@ -643,7 +465,6 @@ export class Reactor {
|
|
|
643
465
|
}
|
|
644
466
|
const jobInfo = this.jobTracker.getJobStatus(jobId);
|
|
645
467
|
if (!jobInfo) {
|
|
646
|
-
// Job not found - return FAILED status with appropriate error
|
|
647
468
|
const now = new Date().toISOString();
|
|
648
469
|
return Promise.resolve({
|
|
649
470
|
id: jobId,
|
|
@@ -660,327 +481,43 @@ export class Reactor {
|
|
|
660
481
|
}
|
|
661
482
|
return Promise.resolve(jobInfo);
|
|
662
483
|
}
|
|
663
|
-
/**
|
|
664
|
-
* Finds documents by their IDs
|
|
665
|
-
*/
|
|
666
484
|
async findByIds(ids, view, paging, consistencyToken, signal) {
|
|
667
485
|
this.logger.verbose("findByIds(@count ids)", ids.length);
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
try {
|
|
680
|
-
document = await this.documentStorage.get(id, consistencyToken, signal);
|
|
681
|
-
}
|
|
682
|
-
catch {
|
|
683
|
-
// Skip documents that don't exist or can't be accessed
|
|
684
|
-
// This matches the behavior expected from a search operation
|
|
685
|
-
continue;
|
|
686
|
-
}
|
|
687
|
-
// Apply view filter - This will be removed when we pass the viewfilter along
|
|
688
|
-
// to the underlying store, but is here now for the interface.
|
|
689
|
-
for (const scope in document.state) {
|
|
690
|
-
if (!matchesScope(view, scope)) {
|
|
691
|
-
delete document.state[scope];
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
documents.push(document);
|
|
695
|
-
}
|
|
696
|
-
if (signal?.aborted) {
|
|
697
|
-
throw new AbortError();
|
|
698
|
-
}
|
|
699
|
-
// Apply paging
|
|
700
|
-
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
701
|
-
const limit = paging?.limit || documents.length;
|
|
702
|
-
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
703
|
-
// Create paged results
|
|
704
|
-
const hasMore = startIndex + limit < documents.length;
|
|
705
|
-
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
706
|
-
return {
|
|
707
|
-
results: pagedDocuments,
|
|
708
|
-
options: paging || { cursor: "0", limit: documents.length },
|
|
709
|
-
nextCursor,
|
|
710
|
-
next: hasMore
|
|
711
|
-
? () => this.findByIds(ids, view, { cursor: nextCursor, limit }, consistencyToken, signal)
|
|
712
|
-
: undefined,
|
|
713
|
-
};
|
|
714
|
-
}
|
|
715
|
-
else {
|
|
716
|
-
const documents = [];
|
|
717
|
-
// Fetch each document by ID using documentView
|
|
718
|
-
for (const id of ids) {
|
|
719
|
-
if (signal?.aborted) {
|
|
720
|
-
throw new AbortError();
|
|
721
|
-
}
|
|
722
|
-
try {
|
|
723
|
-
const document = await this.documentView.get(id, view, undefined, signal);
|
|
724
|
-
documents.push(document);
|
|
725
|
-
}
|
|
726
|
-
catch {
|
|
727
|
-
// Skip documents that don't exist or can't be accessed
|
|
728
|
-
continue;
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
if (signal?.aborted) {
|
|
732
|
-
throw new AbortError();
|
|
733
|
-
}
|
|
734
|
-
// Apply paging
|
|
735
|
-
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
736
|
-
const limit = paging?.limit || documents.length;
|
|
737
|
-
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
738
|
-
// Create paged results
|
|
739
|
-
const hasMore = startIndex + limit < documents.length;
|
|
740
|
-
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
741
|
-
return {
|
|
742
|
-
results: pagedDocuments,
|
|
743
|
-
options: paging || { cursor: "0", limit: documents.length },
|
|
744
|
-
nextCursor,
|
|
745
|
-
next: hasMore
|
|
746
|
-
? () => this.findByIds(ids, view, { cursor: nextCursor, limit }, consistencyToken, signal)
|
|
747
|
-
: undefined,
|
|
748
|
-
};
|
|
749
|
-
}
|
|
486
|
+
const startIndex = paging?.cursor ? parseInt(paging.cursor) || 0 : 0;
|
|
487
|
+
const limit = paging?.limit || ids.length;
|
|
488
|
+
const pagedIds = ids.slice(startIndex, startIndex + limit);
|
|
489
|
+
const results = await this.documentView.getMany(pagedIds, view, consistencyToken, signal);
|
|
490
|
+
const hasMore = startIndex + limit < ids.length;
|
|
491
|
+
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
492
|
+
return {
|
|
493
|
+
results,
|
|
494
|
+
options: paging || { cursor: "0", limit: ids.length },
|
|
495
|
+
nextCursor,
|
|
496
|
+
};
|
|
750
497
|
}
|
|
751
|
-
/**
|
|
752
|
-
* Finds documents by their slugs
|
|
753
|
-
*/
|
|
754
498
|
async findBySlugs(slugs, view, paging, consistencyToken, signal) {
|
|
755
499
|
this.logger.verbose("findBySlugs(@count slugs)", slugs.length);
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
}
|
|
759
|
-
if (this.features.legacyStorageEnabled) {
|
|
760
|
-
const documents = [];
|
|
761
|
-
// Use storage to resolve slugs to IDs
|
|
762
|
-
let ids;
|
|
763
|
-
try {
|
|
764
|
-
ids = await this.documentStorage.resolveIds(slugs, consistencyToken, signal);
|
|
765
|
-
}
|
|
766
|
-
catch {
|
|
767
|
-
// If slug resolution fails, return empty results
|
|
768
|
-
// This matches the behavior expected from a search operation
|
|
769
|
-
ids = [];
|
|
770
|
-
}
|
|
771
|
-
// Fetch each document by resolved ID
|
|
772
|
-
for (const id of ids) {
|
|
773
|
-
if (signal?.aborted) {
|
|
774
|
-
throw new AbortError();
|
|
775
|
-
}
|
|
776
|
-
let document;
|
|
777
|
-
try {
|
|
778
|
-
document = await this.documentStorage.get(id, consistencyToken, signal);
|
|
779
|
-
}
|
|
780
|
-
catch {
|
|
781
|
-
// Skip documents that don't exist or can't be accessed
|
|
782
|
-
continue;
|
|
783
|
-
}
|
|
784
|
-
// Apply view filter - This will be removed when we pass the viewfilter along
|
|
785
|
-
// to the underlying store, but is here now for the interface.
|
|
786
|
-
for (const scope in document.state) {
|
|
787
|
-
if (!matchesScope(view, scope)) {
|
|
788
|
-
delete document.state[scope];
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
documents.push(document);
|
|
792
|
-
}
|
|
793
|
-
if (signal?.aborted) {
|
|
794
|
-
throw new AbortError();
|
|
795
|
-
}
|
|
796
|
-
// Apply paging - this will be removed when we pass the paging along
|
|
797
|
-
// to the underlying store, but is here now for the interface.
|
|
798
|
-
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
799
|
-
const limit = paging?.limit || documents.length;
|
|
800
|
-
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
801
|
-
// Create paged results
|
|
802
|
-
const hasMore = startIndex + limit < documents.length;
|
|
803
|
-
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
804
|
-
return {
|
|
805
|
-
results: pagedDocuments,
|
|
806
|
-
options: paging || { cursor: "0", limit: documents.length },
|
|
807
|
-
nextCursor,
|
|
808
|
-
next: hasMore
|
|
809
|
-
? () => this.findBySlugs(slugs, view, { cursor: nextCursor, limit }, consistencyToken, signal)
|
|
810
|
-
: undefined,
|
|
811
|
-
};
|
|
812
|
-
}
|
|
813
|
-
else {
|
|
814
|
-
const documents = [];
|
|
815
|
-
// Resolve each slug to a document ID
|
|
816
|
-
const documentIds = [];
|
|
817
|
-
for (const slug of slugs) {
|
|
818
|
-
if (signal?.aborted) {
|
|
819
|
-
throw new AbortError();
|
|
820
|
-
}
|
|
821
|
-
const documentId = await this.documentView.resolveSlug(slug, view, undefined, signal);
|
|
822
|
-
if (documentId) {
|
|
823
|
-
documentIds.push(documentId);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
// Fetch each document
|
|
827
|
-
for (const documentId of documentIds) {
|
|
828
|
-
if (signal?.aborted) {
|
|
829
|
-
throw new AbortError();
|
|
830
|
-
}
|
|
831
|
-
try {
|
|
832
|
-
const document = await this.documentView.get(documentId, view, undefined, signal);
|
|
833
|
-
documents.push(document);
|
|
834
|
-
}
|
|
835
|
-
catch {
|
|
836
|
-
// Skip documents that don't exist or can't be accessed
|
|
837
|
-
continue;
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
if (signal?.aborted) {
|
|
841
|
-
throw new AbortError();
|
|
842
|
-
}
|
|
843
|
-
// Apply paging
|
|
844
|
-
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
845
|
-
const limit = paging?.limit || documents.length;
|
|
846
|
-
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
847
|
-
// Create paged results
|
|
848
|
-
const hasMore = startIndex + limit < documents.length;
|
|
849
|
-
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
850
|
-
return {
|
|
851
|
-
results: pagedDocuments,
|
|
852
|
-
options: paging || { cursor: "0", limit: documents.length },
|
|
853
|
-
nextCursor,
|
|
854
|
-
next: hasMore
|
|
855
|
-
? () => this.findBySlugs(slugs, view, { cursor: nextCursor, limit }, consistencyToken, signal)
|
|
856
|
-
: undefined,
|
|
857
|
-
};
|
|
858
|
-
}
|
|
500
|
+
const ids = await this.documentView.resolveSlugs(slugs, view, consistencyToken, signal);
|
|
501
|
+
return await this.findByIds(ids, view, paging, consistencyToken, signal);
|
|
859
502
|
}
|
|
860
|
-
|
|
861
|
-
* Finds documents by parent ID
|
|
862
|
-
*/
|
|
863
|
-
async findByParentId(parentId, view, paging, signal) {
|
|
503
|
+
async findByParentId(parentId, view, paging, consistencyToken, signal) {
|
|
864
504
|
this.logger.verbose("findByParentId(@parentId)", parentId);
|
|
865
|
-
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
throw new AbortError();
|
|
869
|
-
}
|
|
870
|
-
const documents = [];
|
|
871
|
-
// Fetch each child document using the appropriate storage method
|
|
872
|
-
for (const relationship of relationships) {
|
|
873
|
-
if (signal?.aborted) {
|
|
874
|
-
throw new AbortError();
|
|
875
|
-
}
|
|
876
|
-
try {
|
|
877
|
-
let document;
|
|
878
|
-
if (this.features.legacyStorageEnabled) {
|
|
879
|
-
document = await this.documentStorage.get(relationship.targetId);
|
|
880
|
-
// Apply view filter for legacy storage
|
|
881
|
-
for (const scope in document.state) {
|
|
882
|
-
if (!matchesScope(view, scope)) {
|
|
883
|
-
delete document.state[scope];
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
else {
|
|
888
|
-
document = await this.documentView.get(relationship.targetId, view, undefined, signal);
|
|
889
|
-
}
|
|
890
|
-
documents.push(document);
|
|
891
|
-
}
|
|
892
|
-
catch {
|
|
893
|
-
// Skip documents that don't exist or can't be accessed
|
|
894
|
-
continue;
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
if (signal?.aborted) {
|
|
898
|
-
throw new AbortError();
|
|
899
|
-
}
|
|
900
|
-
// Apply paging
|
|
901
|
-
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
902
|
-
const limit = paging?.limit || documents.length;
|
|
903
|
-
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
904
|
-
// Create paged results
|
|
905
|
-
const hasMore = startIndex + limit < documents.length;
|
|
906
|
-
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
907
|
-
return {
|
|
908
|
-
results: pagedDocuments,
|
|
909
|
-
options: paging || { cursor: "0", limit: documents.length },
|
|
910
|
-
nextCursor,
|
|
911
|
-
next: hasMore
|
|
912
|
-
? () => this.findByParentId(parentId, view, { cursor: nextCursor, limit }, signal)
|
|
913
|
-
: undefined,
|
|
914
|
-
};
|
|
505
|
+
const relationships = await this.documentIndexer.getOutgoing(parentId, ["child"], paging, consistencyToken, signal);
|
|
506
|
+
const ids = relationships.results.map((rel) => rel.targetId);
|
|
507
|
+
return await this.findByIds(ids, view, paging, undefined, signal);
|
|
915
508
|
}
|
|
916
|
-
/**
|
|
917
|
-
* Finds documents by type
|
|
918
|
-
*/
|
|
919
509
|
async findByType(type, view, paging, consistencyToken, signal) {
|
|
920
510
|
this.logger.verbose("findByType(@type)", type);
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
//
|
|
930
|
-
|
|
931
|
-
if (signal?.aborted) {
|
|
932
|
-
throw new AbortError();
|
|
933
|
-
}
|
|
934
|
-
// Fetch each document by its ID
|
|
935
|
-
for (const documentId of documentIds) {
|
|
936
|
-
if (signal?.aborted) {
|
|
937
|
-
throw new AbortError();
|
|
938
|
-
}
|
|
939
|
-
let document;
|
|
940
|
-
try {
|
|
941
|
-
document = await this.documentStorage.get(documentId, consistencyToken, signal);
|
|
942
|
-
}
|
|
943
|
-
catch {
|
|
944
|
-
// Skip documents that can't be retrieved
|
|
945
|
-
continue;
|
|
946
|
-
}
|
|
947
|
-
// Apply view filter
|
|
948
|
-
for (const scope in document.state) {
|
|
949
|
-
if (!matchesScope(view, scope)) {
|
|
950
|
-
delete document.state[scope];
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
documents.push(document);
|
|
954
|
-
}
|
|
955
|
-
if (signal?.aborted) {
|
|
956
|
-
throw new AbortError();
|
|
957
|
-
}
|
|
958
|
-
// Results are already paged from the storage layer
|
|
959
|
-
return {
|
|
960
|
-
results: documents,
|
|
961
|
-
options: paging || { cursor: cursor || "0", limit },
|
|
962
|
-
nextCursor,
|
|
963
|
-
next: nextCursor
|
|
964
|
-
? async () => this.findByType(type, view, { cursor: nextCursor, limit }, consistencyToken, signal)
|
|
965
|
-
: undefined,
|
|
966
|
-
};
|
|
967
|
-
}
|
|
968
|
-
else {
|
|
969
|
-
const result = await this.documentView.findByType(type, view, paging, consistencyToken, signal);
|
|
970
|
-
if (signal?.aborted) {
|
|
971
|
-
throw new AbortError();
|
|
972
|
-
}
|
|
973
|
-
const cursor = paging?.cursor;
|
|
974
|
-
const limit = paging?.limit || 100;
|
|
975
|
-
return {
|
|
976
|
-
results: result.items,
|
|
977
|
-
options: paging || { cursor: cursor || "0", limit },
|
|
978
|
-
nextCursor: result.nextCursor,
|
|
979
|
-
next: result.nextCursor
|
|
980
|
-
? async () => this.findByType(type, view, { cursor: result.nextCursor, limit }, consistencyToken, signal)
|
|
981
|
-
: undefined,
|
|
982
|
-
};
|
|
983
|
-
}
|
|
511
|
+
return await this.documentView.findByType(type, view, paging, consistencyToken, signal);
|
|
512
|
+
}
|
|
513
|
+
emitJobPending(jobId, meta) {
|
|
514
|
+
const event = {
|
|
515
|
+
jobId,
|
|
516
|
+
jobMeta: meta,
|
|
517
|
+
};
|
|
518
|
+
this.eventBus.emit(ReactorEventTypes.JOB_PENDING, event).catch(() => {
|
|
519
|
+
// Ignore event emission errors
|
|
520
|
+
});
|
|
984
521
|
}
|
|
985
522
|
}
|
|
986
523
|
//# sourceMappingURL=reactor.js.map
|