@powerhousedao/reactor 5.0.3 → 5.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cache/index.d.ts +3 -0
- package/dist/src/cache/index.d.ts.map +1 -0
- package/dist/src/cache/index.js +2 -0
- package/dist/src/cache/index.js.map +1 -0
- package/dist/src/cache/kysely-operation-index.d.ts +13 -0
- package/dist/src/cache/kysely-operation-index.d.ts.map +1 -0
- package/dist/src/cache/kysely-operation-index.js +207 -0
- package/dist/src/cache/kysely-operation-index.js.map +1 -0
- package/dist/src/cache/kysely-write-cache.d.ts +5 -4
- package/dist/src/cache/kysely-write-cache.d.ts.map +1 -1
- package/dist/src/cache/kysely-write-cache.js +12 -12
- package/dist/src/cache/kysely-write-cache.js.map +1 -1
- package/dist/src/cache/operation-index-types.d.ts +49 -0
- package/dist/src/cache/operation-index-types.d.ts.map +1 -0
- package/dist/src/cache/operation-index-types.js +4 -0
- package/dist/src/cache/operation-index-types.js.map +1 -0
- package/dist/src/cache/{types.d.ts → write-cache-types.d.ts} +1 -1
- package/dist/src/cache/write-cache-types.d.ts.map +1 -0
- package/dist/src/cache/write-cache-types.js +2 -0
- package/dist/src/cache/write-cache-types.js.map +1 -0
- package/dist/src/client/reactor-client.d.ts +6 -4
- package/dist/src/client/reactor-client.d.ts.map +1 -1
- package/dist/src/client/reactor-client.js +118 -37
- package/dist/src/client/reactor-client.js.map +1 -1
- package/dist/src/client/types.d.ts +4 -4
- package/dist/src/client/types.d.ts.map +1 -1
- package/dist/src/core/builder.d.ts +15 -2
- package/dist/src/core/builder.d.ts.map +1 -1
- package/dist/src/core/builder.js +48 -7
- package/dist/src/core/builder.js.map +1 -1
- package/dist/src/core/reactor-builder.d.ts +40 -0
- package/dist/src/core/reactor-builder.d.ts.map +1 -0
- package/dist/src/core/reactor-builder.js +141 -0
- package/dist/src/core/reactor-builder.js.map +1 -0
- package/dist/src/core/reactor.d.ts +36 -11
- package/dist/src/core/reactor.d.ts.map +1 -1
- package/dist/src/core/reactor.js +609 -279
- package/dist/src/core/reactor.js.map +1 -1
- package/dist/src/core/types.d.ts +84 -7
- package/dist/src/core/types.d.ts.map +1 -1
- package/dist/src/core/utils.d.ts +44 -4
- package/dist/src/core/utils.d.ts.map +1 -1
- package/dist/src/core/utils.js +116 -6
- package/dist/src/core/utils.js.map +1 -1
- package/dist/src/events/types.d.ts +28 -0
- package/dist/src/events/types.d.ts.map +1 -1
- package/dist/src/events/types.js +2 -0
- 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 +19 -1
- package/dist/src/executor/simple-job-executor-manager.js.map +1 -1
- package/dist/src/executor/simple-job-executor.d.ts +16 -2
- package/dist/src/executor/simple-job-executor.d.ts.map +1 -1
- package/dist/src/executor/simple-job-executor.js +458 -252
- package/dist/src/executor/simple-job-executor.js.map +1 -1
- package/dist/src/executor/types.d.ts +2 -0
- package/dist/src/executor/types.d.ts.map +1 -1
- package/dist/src/executor/types.js.map +1 -1
- package/dist/src/executor/util.d.ts +18 -0
- package/dist/src/executor/util.d.ts.map +1 -1
- package/dist/src/executor/util.js +42 -1
- package/dist/src/executor/util.js.map +1 -1
- package/dist/src/index.d.ts +12 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/job-tracker/in-memory-job-tracker.d.ts +10 -1
- package/dist/src/job-tracker/in-memory-job-tracker.d.ts.map +1 -1
- package/dist/src/job-tracker/in-memory-job-tracker.js +57 -23
- package/dist/src/job-tracker/in-memory-job-tracker.js.map +1 -1
- package/dist/src/job-tracker/interfaces.d.ts +5 -7
- package/dist/src/job-tracker/interfaces.d.ts.map +1 -1
- package/dist/src/queue/types.d.ts +6 -1
- package/dist/src/queue/types.d.ts.map +1 -1
- package/dist/src/queue/types.js.map +1 -1
- package/dist/src/read-models/coordinator.d.ts.map +1 -1
- package/dist/src/read-models/coordinator.js +11 -0
- package/dist/src/read-models/coordinator.js.map +1 -1
- package/dist/src/read-models/document-view.d.ts +11 -6
- package/dist/src/read-models/document-view.d.ts.map +1 -1
- package/dist/src/read-models/document-view.js +156 -113
- package/dist/src/read-models/document-view.js.map +1 -1
- package/dist/src/read-models/types.d.ts +3 -3
- package/dist/src/read-models/types.d.ts.map +1 -1
- package/dist/src/shared/awaiter.d.ts +11 -8
- package/dist/src/shared/awaiter.d.ts.map +1 -1
- package/dist/src/shared/awaiter.js +66 -75
- package/dist/src/shared/awaiter.js.map +1 -1
- package/dist/src/shared/consistency-tracker.d.ts +48 -0
- package/dist/src/shared/consistency-tracker.d.ts.map +1 -0
- package/dist/src/shared/consistency-tracker.js +123 -0
- package/dist/src/shared/consistency-tracker.js.map +1 -0
- package/dist/src/shared/types.d.ts +30 -2
- package/dist/src/shared/types.d.ts.map +1 -1
- package/dist/src/shared/types.js +4 -2
- package/dist/src/shared/types.js.map +1 -1
- package/dist/src/storage/index.d.ts +4 -0
- package/dist/src/storage/index.d.ts.map +1 -0
- package/dist/src/storage/index.js +3 -0
- package/dist/src/storage/index.js.map +1 -0
- package/dist/src/storage/interfaces.d.ts +227 -2
- 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 +28 -0
- package/dist/src/storage/kysely/document-indexer.d.ts.map +1 -0
- package/dist/src/storage/kysely/document-indexer.js +350 -0
- package/dist/src/storage/kysely/document-indexer.js.map +1 -0
- package/dist/src/storage/kysely/keyframe-store.d.ts.map +1 -1
- package/dist/src/storage/kysely/keyframe-store.js +6 -13
- package/dist/src/storage/kysely/keyframe-store.js.map +1 -1
- package/dist/src/storage/kysely/store.js +1 -1
- package/dist/src/storage/kysely/store.js.map +1 -1
- package/dist/src/storage/kysely/sync-cursor-storage.d.ts +13 -0
- package/dist/src/storage/kysely/sync-cursor-storage.d.ts.map +1 -0
- package/dist/src/storage/kysely/sync-cursor-storage.js +93 -0
- package/dist/src/storage/kysely/sync-cursor-storage.js.map +1 -0
- package/dist/src/storage/kysely/sync-remote-storage.d.ts +13 -0
- package/dist/src/storage/kysely/sync-remote-storage.d.ts.map +1 -0
- package/dist/src/storage/kysely/sync-remote-storage.js +134 -0
- package/dist/src/storage/kysely/sync-remote-storage.js.map +1 -0
- package/dist/src/storage/kysely/types.d.ts +98 -2
- package/dist/src/storage/kysely/types.d.ts.map +1 -1
- package/dist/src/storage/migrations/001_create_operation_table.d.ts +3 -0
- package/dist/src/storage/migrations/001_create_operation_table.d.ts.map +1 -0
- package/dist/src/storage/migrations/001_create_operation_table.js +40 -0
- package/dist/src/storage/migrations/001_create_operation_table.js.map +1 -0
- package/dist/src/storage/migrations/002_create_keyframe_table.d.ts +3 -0
- package/dist/src/storage/migrations/002_create_keyframe_table.d.ts.map +1 -0
- package/dist/src/storage/migrations/002_create_keyframe_table.js +27 -0
- package/dist/src/storage/migrations/002_create_keyframe_table.js.map +1 -0
- package/dist/src/storage/migrations/003_create_document_table.d.ts +3 -0
- package/dist/src/storage/migrations/003_create_document_table.d.ts.map +1 -0
- package/dist/src/storage/migrations/003_create_document_table.js +10 -0
- package/dist/src/storage/migrations/003_create_document_table.js.map +1 -0
- package/dist/src/storage/migrations/004_create_document_relationship_table.d.ts +3 -0
- package/dist/src/storage/migrations/004_create_document_relationship_table.d.ts.map +1 -0
- package/dist/src/storage/migrations/004_create_document_relationship_table.js +35 -0
- package/dist/src/storage/migrations/004_create_document_relationship_table.js.map +1 -0
- package/dist/src/storage/migrations/005_create_indexer_state_table.d.ts +3 -0
- package/dist/src/storage/migrations/005_create_indexer_state_table.d.ts.map +1 -0
- package/dist/src/storage/migrations/005_create_indexer_state_table.js +10 -0
- package/dist/src/storage/migrations/005_create_indexer_state_table.js.map +1 -0
- package/dist/src/storage/migrations/006_create_document_snapshot_table.d.ts +3 -0
- package/dist/src/storage/migrations/006_create_document_snapshot_table.d.ts.map +1 -0
- package/dist/src/storage/migrations/006_create_document_snapshot_table.js +49 -0
- package/dist/src/storage/migrations/006_create_document_snapshot_table.js.map +1 -0
- package/dist/src/storage/migrations/007_create_slug_mapping_table.d.ts +3 -0
- package/dist/src/storage/migrations/007_create_slug_mapping_table.d.ts.map +1 -0
- package/dist/src/storage/migrations/007_create_slug_mapping_table.js +24 -0
- package/dist/src/storage/migrations/007_create_slug_mapping_table.js.map +1 -0
- package/dist/src/storage/migrations/008_create_view_state_table.d.ts +3 -0
- package/dist/src/storage/migrations/008_create_view_state_table.d.ts.map +1 -0
- package/dist/src/storage/migrations/008_create_view_state_table.js +9 -0
- package/dist/src/storage/migrations/008_create_view_state_table.js.map +1 -0
- package/dist/src/storage/migrations/009_create_operation_index_tables.d.ts +3 -0
- package/dist/src/storage/migrations/009_create_operation_index_tables.d.ts.map +1 -0
- package/dist/src/storage/migrations/009_create_operation_index_tables.js +50 -0
- package/dist/src/storage/migrations/009_create_operation_index_tables.js.map +1 -0
- package/dist/src/storage/migrations/010_create_sync_tables.d.ts +3 -0
- package/dist/src/storage/migrations/010_create_sync_tables.d.ts.map +1 -0
- package/dist/src/storage/migrations/010_create_sync_tables.js +43 -0
- package/dist/src/storage/migrations/010_create_sync_tables.js.map +1 -0
- package/dist/src/storage/migrations/index.d.ts +3 -0
- package/dist/src/storage/migrations/index.d.ts.map +1 -0
- package/dist/src/storage/migrations/index.js +3 -0
- package/dist/src/storage/migrations/index.js.map +1 -0
- package/dist/src/storage/migrations/migrator.d.ts +5 -0
- package/dist/src/storage/migrations/migrator.d.ts.map +1 -0
- package/dist/src/storage/migrations/migrator.js +55 -0
- package/dist/src/storage/migrations/migrator.js.map +1 -0
- package/dist/src/storage/migrations/run-migrations.d.ts +2 -0
- package/dist/src/storage/migrations/run-migrations.d.ts.map +1 -0
- package/dist/src/storage/migrations/run-migrations.js +58 -0
- package/dist/src/storage/migrations/run-migrations.js.map +1 -0
- package/dist/src/storage/migrations/types.d.ts +9 -0
- package/dist/src/storage/migrations/types.d.ts.map +1 -0
- package/dist/src/storage/migrations/types.js.map +1 -0
- package/dist/src/storage/txn.d.ts.map +1 -1
- package/dist/src/storage/txn.js +2 -0
- package/dist/src/storage/txn.js.map +1 -1
- package/dist/src/sync/channels/index.d.ts +3 -0
- package/dist/src/sync/channels/index.d.ts.map +1 -0
- package/dist/src/sync/channels/index.js +3 -0
- package/dist/src/sync/channels/index.js.map +1 -0
- package/dist/src/sync/channels/internal-channel.d.ts +57 -0
- package/dist/src/sync/channels/internal-channel.d.ts.map +1 -0
- package/dist/src/sync/channels/internal-channel.js +106 -0
- package/dist/src/sync/channels/internal-channel.js.map +1 -0
- package/dist/src/sync/channels/utils.d.ts +15 -0
- package/dist/src/sync/channels/utils.d.ts.map +1 -0
- package/dist/src/sync/channels/utils.js +26 -0
- package/dist/src/sync/channels/utils.js.map +1 -0
- package/dist/src/sync/errors.d.ts +10 -0
- package/dist/src/sync/errors.d.ts.map +1 -0
- package/dist/src/sync/errors.js +17 -0
- package/dist/src/sync/errors.js.map +1 -0
- package/dist/src/sync/index.d.ts +12 -0
- package/dist/src/sync/index.d.ts.map +1 -0
- package/dist/src/sync/index.js +9 -0
- package/dist/src/sync/index.js.map +1 -0
- package/dist/src/sync/interfaces.d.ts +150 -0
- package/dist/src/sync/interfaces.d.ts.map +1 -0
- package/dist/src/sync/interfaces.js +2 -0
- package/dist/src/sync/interfaces.js.map +1 -0
- package/dist/src/sync/mailbox.d.ts +21 -0
- package/dist/src/sync/mailbox.d.ts.map +1 -0
- package/dist/src/sync/mailbox.js +59 -0
- package/dist/src/sync/mailbox.js.map +1 -0
- package/dist/src/sync/sync-builder.d.ts +17 -0
- package/dist/src/sync/sync-builder.d.ts.map +1 -0
- package/dist/src/sync/sync-builder.js +29 -0
- package/dist/src/sync/sync-builder.js.map +1 -0
- package/dist/src/sync/sync-manager.d.ts +33 -0
- package/dist/src/sync/sync-manager.d.ts.map +1 -0
- package/dist/src/sync/sync-manager.js +197 -0
- package/dist/src/sync/sync-manager.js.map +1 -0
- package/dist/src/sync/sync-operation.d.ts +28 -0
- package/dist/src/sync/sync-operation.d.ts.map +1 -0
- package/dist/src/sync/sync-operation.js +63 -0
- package/dist/src/sync/sync-operation.js.map +1 -0
- package/dist/src/sync/types.d.ts +61 -0
- package/dist/src/sync/types.d.ts.map +1 -0
- package/dist/src/sync/types.js +16 -0
- package/dist/src/sync/types.js.map +1 -0
- package/dist/src/sync/utils.d.ts +17 -0
- package/dist/src/sync/utils.d.ts.map +1 -0
- package/dist/src/sync/utils.js +34 -0
- package/dist/src/sync/utils.js.map +1 -0
- package/package.json +9 -5
- package/dist/src/cache/types.d.ts.map +0 -1
- package/dist/src/cache/types.js.map +0 -1
- /package/dist/src/{cache → storage/migrations}/types.js +0 -0
package/dist/src/core/reactor.js
CHANGED
|
@@ -3,7 +3,7 @@ import { v4 as uuidv4 } from "uuid";
|
|
|
3
3
|
import { createMutableShutdownStatus } from "../shared/factories.js";
|
|
4
4
|
import { JobStatus } from "../shared/types.js";
|
|
5
5
|
import { matchesScope } from "../shared/utils.js";
|
|
6
|
-
import {
|
|
6
|
+
import { filterByType, getSharedScope, toErrorInfo, topologicalSort, validateActionScopes, validateBatchRequest, } from "./utils.js";
|
|
7
7
|
/**
|
|
8
8
|
* This class implements the IReactor interface and serves as the main entry point
|
|
9
9
|
* for the new Reactor architecture.
|
|
@@ -16,19 +16,33 @@ export class Reactor {
|
|
|
16
16
|
queue;
|
|
17
17
|
jobTracker;
|
|
18
18
|
readModelCoordinator;
|
|
19
|
-
|
|
19
|
+
features;
|
|
20
|
+
documentView;
|
|
21
|
+
_documentIndexer;
|
|
22
|
+
operationStore;
|
|
23
|
+
_syncManager;
|
|
24
|
+
constructor(driveServer, documentStorage, queue, jobTracker, readModelCoordinator, features, documentView, documentIndexer, operationStore) {
|
|
20
25
|
// Store required dependencies
|
|
21
26
|
this.driveServer = driveServer;
|
|
22
27
|
this.documentStorage = documentStorage;
|
|
23
28
|
this.queue = queue;
|
|
24
29
|
this.jobTracker = jobTracker;
|
|
25
30
|
this.readModelCoordinator = readModelCoordinator;
|
|
26
|
-
|
|
27
|
-
this.
|
|
31
|
+
this.features = features;
|
|
32
|
+
this.documentView = documentView;
|
|
33
|
+
this._documentIndexer = documentIndexer;
|
|
34
|
+
this.operationStore = operationStore;
|
|
28
35
|
// Create mutable shutdown status using factory method
|
|
29
36
|
const [status, setter] = createMutableShutdownStatus(false);
|
|
30
37
|
this.shutdownStatus = status;
|
|
31
38
|
this.setShutdown = setter;
|
|
39
|
+
this.readModelCoordinator.start();
|
|
40
|
+
}
|
|
41
|
+
get syncManager() {
|
|
42
|
+
return this._syncManager;
|
|
43
|
+
}
|
|
44
|
+
setSync(syncManager) {
|
|
45
|
+
this._syncManager = syncManager;
|
|
32
46
|
}
|
|
33
47
|
/**
|
|
34
48
|
* Signals that the reactor should shutdown.
|
|
@@ -36,10 +50,14 @@ export class Reactor {
|
|
|
36
50
|
kill() {
|
|
37
51
|
// Mark the reactor as shutdown
|
|
38
52
|
this.setShutdown(true);
|
|
53
|
+
// Stop the sync manager if enabled
|
|
54
|
+
if (this._syncManager) {
|
|
55
|
+
this._syncManager.shutdown();
|
|
56
|
+
}
|
|
39
57
|
// Stop the read model coordinator
|
|
40
58
|
this.readModelCoordinator.stop();
|
|
41
|
-
//
|
|
42
|
-
|
|
59
|
+
// Stop the job tracker
|
|
60
|
+
this.jobTracker.shutdown();
|
|
43
61
|
return this.shutdownStatus;
|
|
44
62
|
}
|
|
45
63
|
/**
|
|
@@ -72,99 +90,196 @@ export class Reactor {
|
|
|
72
90
|
/**
|
|
73
91
|
* Retrieves a specific PHDocument by id
|
|
74
92
|
*/
|
|
75
|
-
async get(id, view, signal) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
93
|
+
async get(id, view, consistencyToken, signal) {
|
|
94
|
+
if (this.features.legacyStorageEnabled) {
|
|
95
|
+
const document = await this.documentStorage.get(id);
|
|
96
|
+
if (signal?.aborted) {
|
|
97
|
+
throw new AbortError();
|
|
98
|
+
}
|
|
99
|
+
const childIds = await this.documentStorage.getChildren(id);
|
|
100
|
+
if (signal?.aborted) {
|
|
101
|
+
throw new AbortError();
|
|
102
|
+
}
|
|
103
|
+
for (const scope in document.state) {
|
|
104
|
+
if (!matchesScope(view, scope)) {
|
|
105
|
+
delete document.state[scope];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
document,
|
|
110
|
+
childIds,
|
|
111
|
+
};
|
|
83
112
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
delete document.state[scope];
|
|
113
|
+
else {
|
|
114
|
+
const document = await this.documentView.get(id, view, consistencyToken, signal);
|
|
115
|
+
if (signal?.aborted) {
|
|
116
|
+
throw new AbortError();
|
|
89
117
|
}
|
|
118
|
+
const relationships = await this._documentIndexer.getOutgoing(id, ["child"], consistencyToken, signal);
|
|
119
|
+
if (signal?.aborted) {
|
|
120
|
+
throw new AbortError();
|
|
121
|
+
}
|
|
122
|
+
const childIds = relationships.map((rel) => rel.targetId);
|
|
123
|
+
return {
|
|
124
|
+
document,
|
|
125
|
+
childIds,
|
|
126
|
+
};
|
|
90
127
|
}
|
|
91
|
-
return {
|
|
92
|
-
document,
|
|
93
|
-
childIds,
|
|
94
|
-
};
|
|
95
128
|
}
|
|
96
129
|
/**
|
|
97
130
|
* Retrieves a specific PHDocument by slug
|
|
98
131
|
*/
|
|
99
|
-
async getBySlug(slug, view, signal) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
132
|
+
async getBySlug(slug, view, consistencyToken, signal) {
|
|
133
|
+
if (this.features.legacyStorageEnabled) {
|
|
134
|
+
let ids;
|
|
135
|
+
try {
|
|
136
|
+
ids = await this.documentStorage.resolveIds([slug], signal);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
if (error instanceof Error && error.message.includes("not found")) {
|
|
140
|
+
throw new Error(`Document not found with slug: ${slug}`);
|
|
141
|
+
}
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
if (ids.length === 0 || !ids[0]) {
|
|
145
|
+
throw new Error(`Document not found with slug: ${slug}`);
|
|
146
|
+
}
|
|
147
|
+
return await this.get(ids[0], view, consistencyToken, signal);
|
|
104
148
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (
|
|
149
|
+
else {
|
|
150
|
+
const documentId = await this.documentView.resolveSlug(slug, view, consistencyToken, signal);
|
|
151
|
+
if (!documentId) {
|
|
108
152
|
throw new Error(`Document not found with slug: ${slug}`);
|
|
109
153
|
}
|
|
110
|
-
|
|
154
|
+
return await this.get(documentId, view, consistencyToken, signal);
|
|
111
155
|
}
|
|
112
|
-
|
|
113
|
-
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Retrieves a specific PHDocument by identifier (either id or slug)
|
|
159
|
+
*/
|
|
160
|
+
async getByIdOrSlug(identifier, view, consistencyToken, signal) {
|
|
161
|
+
if (this.features.legacyStorageEnabled) {
|
|
162
|
+
try {
|
|
163
|
+
return await this.get(identifier, view, consistencyToken, signal);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
try {
|
|
167
|
+
const ids = await this.documentStorage.resolveIds([identifier], signal);
|
|
168
|
+
if (ids.length === 0 || !ids[0]) {
|
|
169
|
+
throw new Error(`Document not found: ${identifier}`);
|
|
170
|
+
}
|
|
171
|
+
return await this.get(ids[0], view, consistencyToken, signal);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
throw new Error(`Document not found: ${identifier}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
const document = await this.documentView.getByIdOrSlug(identifier, view, consistencyToken, signal);
|
|
180
|
+
if (signal?.aborted) {
|
|
181
|
+
throw new AbortError();
|
|
182
|
+
}
|
|
183
|
+
const relationships = await this._documentIndexer.getOutgoing(document.header.id, ["child"], consistencyToken, signal);
|
|
184
|
+
if (signal?.aborted) {
|
|
185
|
+
throw new AbortError();
|
|
186
|
+
}
|
|
187
|
+
const childIds = relationships.map((rel) => rel.targetId);
|
|
188
|
+
return {
|
|
189
|
+
document,
|
|
190
|
+
childIds,
|
|
191
|
+
};
|
|
114
192
|
}
|
|
115
|
-
// Now get the document by its resolved ID
|
|
116
|
-
return await this.get(ids[0], view, signal);
|
|
117
193
|
}
|
|
118
194
|
/**
|
|
119
195
|
* Retrieves the operations for a document
|
|
120
196
|
*/
|
|
121
|
-
async getOperations(documentId, view, paging, signal) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
197
|
+
async getOperations(documentId, view, paging, consistencyToken, signal) {
|
|
198
|
+
if (this.features.legacyStorageEnabled) {
|
|
199
|
+
// Use storage directly to get the document
|
|
200
|
+
const document = await this.documentStorage.get(documentId);
|
|
201
|
+
if (signal?.aborted) {
|
|
202
|
+
throw new AbortError();
|
|
203
|
+
}
|
|
204
|
+
const operations = document.operations;
|
|
205
|
+
const result = {};
|
|
206
|
+
// apply view filter, per scope -- this will be removed when we pass the viewfilter along
|
|
207
|
+
// to the underlying store, but is here now for the interface.
|
|
208
|
+
for (const scope in operations) {
|
|
209
|
+
if (matchesScope(view, scope)) {
|
|
210
|
+
const scopeOperations = operations[scope];
|
|
211
|
+
// apply paging too
|
|
212
|
+
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
213
|
+
const limit = paging?.limit || scopeOperations.length;
|
|
214
|
+
const pagedOperations = scopeOperations.slice(startIndex, startIndex + limit);
|
|
215
|
+
result[scope] = {
|
|
216
|
+
results: pagedOperations,
|
|
217
|
+
options: { cursor: String(startIndex + limit), limit },
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return Promise.resolve(result);
|
|
126
222
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
223
|
+
else {
|
|
224
|
+
// Use operation store to get operations
|
|
225
|
+
const branch = view?.branch || "main";
|
|
226
|
+
// Get all scopes for this document
|
|
227
|
+
const revisions = await this.operationStore.getRevisions(documentId, branch, signal);
|
|
228
|
+
if (signal?.aborted) {
|
|
229
|
+
throw new AbortError();
|
|
230
|
+
}
|
|
231
|
+
const allScopes = Object.keys(revisions.revision);
|
|
232
|
+
const result = {};
|
|
233
|
+
// Filter scopes based on view filter and query operations for each
|
|
234
|
+
for (const scope of allScopes) {
|
|
235
|
+
if (!matchesScope(view, scope)) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (signal?.aborted) {
|
|
239
|
+
throw new AbortError();
|
|
240
|
+
}
|
|
241
|
+
// Get operations for this scope
|
|
242
|
+
const scopeResult = await this.operationStore.getSince(documentId, scope, branch, -1, paging, signal);
|
|
243
|
+
// Transform to Reactor's PagedResults format
|
|
244
|
+
const currentCursor = paging?.cursor ? parseInt(paging.cursor) : 0;
|
|
245
|
+
const currentLimit = paging?.limit || 100;
|
|
138
246
|
result[scope] = {
|
|
139
|
-
results:
|
|
140
|
-
options: {
|
|
247
|
+
results: scopeResult.items,
|
|
248
|
+
options: {
|
|
249
|
+
cursor: scopeResult.nextCursor || String(currentCursor),
|
|
250
|
+
limit: currentLimit,
|
|
251
|
+
},
|
|
252
|
+
nextCursor: scopeResult.nextCursor,
|
|
253
|
+
next: scopeResult.hasMore
|
|
254
|
+
? async () => {
|
|
255
|
+
const nextPage = await this.getOperations(documentId, view, {
|
|
256
|
+
cursor: scopeResult.nextCursor,
|
|
257
|
+
limit: currentLimit,
|
|
258
|
+
}, consistencyToken, signal);
|
|
259
|
+
return nextPage[scope];
|
|
260
|
+
}
|
|
261
|
+
: undefined,
|
|
141
262
|
};
|
|
142
263
|
}
|
|
264
|
+
return result;
|
|
143
265
|
}
|
|
144
|
-
return Promise.resolve(result);
|
|
145
266
|
}
|
|
146
267
|
/**
|
|
147
268
|
* Filters documents by criteria and returns a list of them
|
|
148
269
|
*/
|
|
149
|
-
async find(search, view, paging, signal) {
|
|
270
|
+
async find(search, view, paging, consistencyToken, signal) {
|
|
150
271
|
let results;
|
|
151
272
|
if (search.ids) {
|
|
152
273
|
if (search.slugs && search.slugs.length > 0) {
|
|
153
274
|
throw new Error("Cannot use both ids and slugs in the same search");
|
|
154
275
|
}
|
|
155
|
-
results = await this.findByIds(search.ids, view, paging, signal);
|
|
156
|
-
if (search.parentId) {
|
|
157
|
-
results = filterByParentId(results, search.parentId);
|
|
158
|
-
}
|
|
276
|
+
results = await this.findByIds(search.ids, view, paging, consistencyToken, signal);
|
|
159
277
|
if (search.type) {
|
|
160
278
|
results = filterByType(results, search.type);
|
|
161
279
|
}
|
|
162
280
|
}
|
|
163
281
|
else if (search.slugs) {
|
|
164
|
-
results = await this.findBySlugs(search.slugs, view, paging, signal);
|
|
165
|
-
if (search.parentId) {
|
|
166
|
-
results = filterByParentId(results, search.parentId);
|
|
167
|
-
}
|
|
282
|
+
results = await this.findBySlugs(search.slugs, view, paging, consistencyToken, signal);
|
|
168
283
|
if (search.type) {
|
|
169
284
|
results = filterByType(results, search.type);
|
|
170
285
|
}
|
|
@@ -176,7 +291,7 @@ export class Reactor {
|
|
|
176
291
|
}
|
|
177
292
|
}
|
|
178
293
|
else if (search.type) {
|
|
179
|
-
results = await this.findByType(search.type, view, paging, signal);
|
|
294
|
+
results = await this.findByType(search.type, view, paging, consistencyToken, signal);
|
|
180
295
|
}
|
|
181
296
|
else {
|
|
182
297
|
throw new Error("No search criteria provided");
|
|
@@ -238,10 +353,12 @@ export class Reactor {
|
|
|
238
353
|
// Create a single job with both CREATE_DOCUMENT and UPGRADE_DOCUMENT actions
|
|
239
354
|
const job = {
|
|
240
355
|
id: uuidv4(),
|
|
356
|
+
kind: "mutation",
|
|
241
357
|
documentId: document.header.id,
|
|
242
358
|
scope: "document",
|
|
243
359
|
branch: "main",
|
|
244
360
|
actions: [createAction, upgradeAction],
|
|
361
|
+
operations: [],
|
|
245
362
|
createdAt: new Date().toISOString(),
|
|
246
363
|
queueHint: [],
|
|
247
364
|
maxRetries: 3,
|
|
@@ -252,6 +369,11 @@ export class Reactor {
|
|
|
252
369
|
id: job.id,
|
|
253
370
|
status: JobStatus.PENDING,
|
|
254
371
|
createdAtUtcIso,
|
|
372
|
+
consistencyToken: {
|
|
373
|
+
version: 1,
|
|
374
|
+
createdAtUtcIso,
|
|
375
|
+
coordinates: [],
|
|
376
|
+
},
|
|
255
377
|
};
|
|
256
378
|
this.jobTracker.registerJob(jobInfo);
|
|
257
379
|
// Enqueue the job
|
|
@@ -279,10 +401,12 @@ export class Reactor {
|
|
|
279
401
|
};
|
|
280
402
|
const job = {
|
|
281
403
|
id: uuidv4(),
|
|
404
|
+
kind: "mutation",
|
|
282
405
|
documentId: id,
|
|
283
406
|
scope: "document",
|
|
284
407
|
branch: "main",
|
|
285
408
|
actions: [action],
|
|
409
|
+
operations: [],
|
|
286
410
|
createdAt: new Date().toISOString(),
|
|
287
411
|
queueHint: [],
|
|
288
412
|
maxRetries: 3,
|
|
@@ -292,6 +416,11 @@ export class Reactor {
|
|
|
292
416
|
id: job.id,
|
|
293
417
|
status: JobStatus.PENDING,
|
|
294
418
|
createdAtUtcIso,
|
|
419
|
+
consistencyToken: {
|
|
420
|
+
version: 1,
|
|
421
|
+
createdAtUtcIso,
|
|
422
|
+
coordinates: [],
|
|
423
|
+
},
|
|
295
424
|
};
|
|
296
425
|
this.jobTracker.registerJob(jobInfo);
|
|
297
426
|
await this.queue.enqueue(job);
|
|
@@ -300,17 +429,22 @@ export class Reactor {
|
|
|
300
429
|
/**
|
|
301
430
|
* Applies a list of actions to a document
|
|
302
431
|
*/
|
|
303
|
-
async mutate(
|
|
432
|
+
async mutate(docId, branch, actions, signal) {
|
|
433
|
+
if (signal?.aborted) {
|
|
434
|
+
throw new AbortError();
|
|
435
|
+
}
|
|
304
436
|
const createdAtUtcIso = new Date().toISOString();
|
|
305
437
|
// Determine scope from first action (all actions should have the same scope)
|
|
306
438
|
const scope = actions.length > 0 ? actions[0].scope || "global" : "global";
|
|
307
439
|
// Create a single job with all actions
|
|
308
440
|
const job = {
|
|
309
441
|
id: uuidv4(),
|
|
310
|
-
|
|
442
|
+
kind: "mutation",
|
|
443
|
+
documentId: docId,
|
|
311
444
|
scope: scope,
|
|
312
|
-
branch:
|
|
445
|
+
branch: branch,
|
|
313
446
|
actions: actions,
|
|
447
|
+
operations: [],
|
|
314
448
|
createdAt: new Date().toISOString(),
|
|
315
449
|
queueHint: [],
|
|
316
450
|
maxRetries: 3,
|
|
@@ -321,103 +455,190 @@ export class Reactor {
|
|
|
321
455
|
id: job.id,
|
|
322
456
|
status: JobStatus.PENDING,
|
|
323
457
|
createdAtUtcIso,
|
|
458
|
+
consistencyToken: {
|
|
459
|
+
version: 1,
|
|
460
|
+
createdAtUtcIso,
|
|
461
|
+
coordinates: [],
|
|
462
|
+
},
|
|
324
463
|
};
|
|
325
464
|
this.jobTracker.registerJob(jobInfo);
|
|
326
465
|
// Enqueue the job
|
|
327
466
|
await this.queue.enqueue(job);
|
|
467
|
+
if (signal?.aborted) {
|
|
468
|
+
throw new AbortError();
|
|
469
|
+
}
|
|
328
470
|
return jobInfo;
|
|
329
471
|
}
|
|
330
472
|
/**
|
|
331
|
-
*
|
|
473
|
+
* Imports pre-existing operations that were produced by another reactor.
|
|
474
|
+
* This function may cause a reshuffle, which will generate additional
|
|
475
|
+
* operations.
|
|
332
476
|
*/
|
|
333
|
-
async
|
|
334
|
-
const createdAtUtcIso = new Date().toISOString();
|
|
335
|
-
const jobId = uuidv4();
|
|
336
|
-
// Check abort signal before starting
|
|
477
|
+
async load(docId, branch, operations, signal) {
|
|
337
478
|
if (signal?.aborted) {
|
|
338
479
|
throw new AbortError();
|
|
339
480
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
// Verify parent exists
|
|
343
|
-
try {
|
|
344
|
-
await this.driveServer.getDocument(parentId);
|
|
481
|
+
if (operations.length === 0) {
|
|
482
|
+
throw new Error("load requires at least one operation");
|
|
345
483
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
484
|
+
// validate the operations
|
|
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
|
+
});
|
|
491
|
+
const createdAtUtcIso = new Date().toISOString();
|
|
492
|
+
const job = {
|
|
493
|
+
id: uuidv4(),
|
|
494
|
+
kind: "load",
|
|
495
|
+
documentId: docId,
|
|
496
|
+
scope,
|
|
497
|
+
branch,
|
|
498
|
+
actions: [],
|
|
499
|
+
operations,
|
|
500
|
+
createdAt: createdAtUtcIso,
|
|
501
|
+
queueHint: [],
|
|
502
|
+
maxRetries: 3,
|
|
503
|
+
errorHistory: [],
|
|
504
|
+
};
|
|
505
|
+
const jobInfo = {
|
|
506
|
+
id: job.id,
|
|
507
|
+
status: JobStatus.PENDING,
|
|
508
|
+
createdAtUtcIso,
|
|
509
|
+
consistencyToken: {
|
|
510
|
+
version: 1,
|
|
350
511
|
createdAtUtcIso,
|
|
351
|
-
|
|
352
|
-
}
|
|
512
|
+
coordinates: [],
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
this.jobTracker.registerJob(jobInfo);
|
|
516
|
+
await this.queue.enqueue(job);
|
|
517
|
+
if (signal?.aborted) {
|
|
518
|
+
throw new AbortError();
|
|
353
519
|
}
|
|
354
|
-
|
|
520
|
+
return jobInfo;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Applies multiple mutations across documents with dependency management
|
|
524
|
+
*/
|
|
525
|
+
async mutateBatch(request, signal) {
|
|
355
526
|
if (signal?.aborted) {
|
|
356
527
|
throw new AbortError();
|
|
357
528
|
}
|
|
358
|
-
|
|
359
|
-
for (const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
529
|
+
validateBatchRequest(request.jobs);
|
|
530
|
+
for (const jobPlan of request.jobs) {
|
|
531
|
+
validateActionScopes(jobPlan);
|
|
532
|
+
}
|
|
533
|
+
const createdAtUtcIso = new Date().toISOString();
|
|
534
|
+
const planKeyToJobId = new Map();
|
|
535
|
+
for (const jobPlan of request.jobs) {
|
|
536
|
+
planKeyToJobId.set(jobPlan.key, uuidv4());
|
|
537
|
+
}
|
|
538
|
+
const jobInfos = new Map();
|
|
539
|
+
for (const jobPlan of request.jobs) {
|
|
540
|
+
const jobId = planKeyToJobId.get(jobPlan.key);
|
|
541
|
+
const jobInfo = {
|
|
542
|
+
id: jobId,
|
|
543
|
+
status: JobStatus.PENDING,
|
|
544
|
+
createdAtUtcIso,
|
|
545
|
+
consistencyToken: {
|
|
546
|
+
version: 1,
|
|
367
547
|
createdAtUtcIso,
|
|
368
|
-
|
|
548
|
+
coordinates: [],
|
|
549
|
+
},
|
|
550
|
+
};
|
|
551
|
+
this.jobTracker.registerJob(jobInfo);
|
|
552
|
+
jobInfos.set(jobPlan.key, jobInfo);
|
|
553
|
+
}
|
|
554
|
+
const sortedKeys = topologicalSort(request.jobs);
|
|
555
|
+
const enqueuedKeys = [];
|
|
556
|
+
try {
|
|
557
|
+
for (const key of sortedKeys) {
|
|
558
|
+
if (signal?.aborted) {
|
|
559
|
+
throw new AbortError();
|
|
560
|
+
}
|
|
561
|
+
const jobPlan = request.jobs.find((j) => j.key === key);
|
|
562
|
+
const jobId = planKeyToJobId.get(key);
|
|
563
|
+
const queueHint = jobPlan.dependsOn.map((depKey) => planKeyToJobId.get(depKey));
|
|
564
|
+
const job = {
|
|
565
|
+
id: jobId,
|
|
566
|
+
kind: "mutation",
|
|
567
|
+
documentId: jobPlan.documentId,
|
|
568
|
+
scope: jobPlan.scope,
|
|
569
|
+
branch: jobPlan.branch,
|
|
570
|
+
actions: jobPlan.actions,
|
|
571
|
+
operations: [],
|
|
572
|
+
createdAt: createdAtUtcIso,
|
|
573
|
+
queueHint,
|
|
574
|
+
maxRetries: 3,
|
|
575
|
+
errorHistory: [],
|
|
369
576
|
};
|
|
577
|
+
await this.queue.enqueue(job);
|
|
578
|
+
enqueuedKeys.push(key);
|
|
370
579
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
580
|
+
}
|
|
581
|
+
catch (error) {
|
|
582
|
+
for (const key of enqueuedKeys) {
|
|
583
|
+
const jobId = planKeyToJobId.get(key);
|
|
584
|
+
try {
|
|
585
|
+
await this.queue.remove(jobId);
|
|
586
|
+
}
|
|
587
|
+
catch {
|
|
588
|
+
// Ignore removal errors during cleanup
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
for (const jobInfo of jobInfos.values()) {
|
|
592
|
+
this.jobTracker.markFailed(jobInfo.id, toErrorInfo("Batch enqueue failed"));
|
|
374
593
|
}
|
|
594
|
+
throw error;
|
|
375
595
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
return {
|
|
379
|
-
id: jobId,
|
|
380
|
-
status: JobStatus.COMPLETED,
|
|
381
|
-
createdAtUtcIso,
|
|
382
|
-
completedAtUtcIso: new Date().toISOString(),
|
|
596
|
+
const result = {
|
|
597
|
+
jobs: Object.fromEntries(jobInfos),
|
|
383
598
|
};
|
|
599
|
+
return result;
|
|
384
600
|
}
|
|
385
601
|
/**
|
|
386
|
-
*
|
|
602
|
+
* Adds multiple documents as children to another
|
|
387
603
|
*/
|
|
388
|
-
async
|
|
389
|
-
const createdAtUtcIso = new Date().toISOString();
|
|
390
|
-
const jobId = uuidv4();
|
|
391
|
-
// Check abort signal before starting
|
|
604
|
+
async addChildren(parentId, documentIds, _view, signal) {
|
|
392
605
|
if (signal?.aborted) {
|
|
393
606
|
throw new AbortError();
|
|
394
607
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
608
|
+
const actions = documentIds.map((childId) => ({
|
|
609
|
+
id: uuidv4(),
|
|
610
|
+
type: "ADD_RELATIONSHIP",
|
|
611
|
+
scope: "document",
|
|
612
|
+
timestampUtcMs: new Date().toISOString(),
|
|
613
|
+
input: {
|
|
614
|
+
sourceId: parentId,
|
|
615
|
+
targetId: childId,
|
|
616
|
+
relationshipType: "child",
|
|
617
|
+
},
|
|
618
|
+
}));
|
|
619
|
+
const branch = _view?.branch || "main";
|
|
620
|
+
return await this.mutate(parentId, branch, actions, signal);
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Removes multiple documents as children from another
|
|
624
|
+
*/
|
|
625
|
+
async removeChildren(parentId, documentIds, _view, signal) {
|
|
410
626
|
if (signal?.aborted) {
|
|
411
627
|
throw new AbortError();
|
|
412
628
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
629
|
+
const actions = documentIds.map((childId) => ({
|
|
630
|
+
id: uuidv4(),
|
|
631
|
+
type: "REMOVE_RELATIONSHIP",
|
|
632
|
+
scope: "document",
|
|
633
|
+
timestampUtcMs: new Date().toISOString(),
|
|
634
|
+
input: {
|
|
635
|
+
sourceId: parentId,
|
|
636
|
+
targetId: childId,
|
|
637
|
+
relationshipType: "child",
|
|
638
|
+
},
|
|
639
|
+
}));
|
|
640
|
+
const branch = _view?.branch || "main";
|
|
641
|
+
return await this.mutate(parentId, branch, actions, signal);
|
|
421
642
|
}
|
|
422
643
|
/**
|
|
423
644
|
* Retrieves the status of a job
|
|
@@ -429,12 +650,18 @@ export class Reactor {
|
|
|
429
650
|
const jobInfo = this.jobTracker.getJobStatus(jobId);
|
|
430
651
|
if (!jobInfo) {
|
|
431
652
|
// Job not found - return FAILED status with appropriate error
|
|
653
|
+
const now = new Date().toISOString();
|
|
432
654
|
return Promise.resolve({
|
|
433
655
|
id: jobId,
|
|
434
656
|
status: JobStatus.FAILED,
|
|
435
|
-
createdAtUtcIso:
|
|
436
|
-
completedAtUtcIso:
|
|
437
|
-
error:
|
|
657
|
+
createdAtUtcIso: now,
|
|
658
|
+
completedAtUtcIso: now,
|
|
659
|
+
error: toErrorInfo("Job not found"),
|
|
660
|
+
consistencyToken: {
|
|
661
|
+
version: 1,
|
|
662
|
+
createdAtUtcIso: now,
|
|
663
|
+
coordinates: [],
|
|
664
|
+
},
|
|
438
665
|
});
|
|
439
666
|
}
|
|
440
667
|
return Promise.resolve(jobInfo);
|
|
@@ -442,139 +669,233 @@ export class Reactor {
|
|
|
442
669
|
/**
|
|
443
670
|
* Finds documents by their IDs
|
|
444
671
|
*/
|
|
445
|
-
async findByIds(ids, view, paging, signal) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
672
|
+
async findByIds(ids, view, paging, consistencyToken, signal) {
|
|
673
|
+
if (consistencyToken) {
|
|
674
|
+
await this.documentView.waitForConsistency(consistencyToken, undefined, signal);
|
|
675
|
+
}
|
|
676
|
+
if (this.features.legacyStorageEnabled) {
|
|
677
|
+
const documents = [];
|
|
678
|
+
// Fetch each document by ID using storage directly
|
|
679
|
+
for (const id of ids) {
|
|
680
|
+
if (signal?.aborted) {
|
|
681
|
+
throw new AbortError();
|
|
682
|
+
}
|
|
683
|
+
let document;
|
|
684
|
+
try {
|
|
685
|
+
document = await this.documentStorage.get(id);
|
|
686
|
+
}
|
|
687
|
+
catch {
|
|
688
|
+
// Skip documents that don't exist or can't be accessed
|
|
689
|
+
// This matches the behavior expected from a search operation
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
// Apply view filter - This will be removed when we pass the viewfilter along
|
|
693
|
+
// to the underlying store, but is here now for the interface.
|
|
694
|
+
for (const scope in document.state) {
|
|
695
|
+
if (!matchesScope(view, scope)) {
|
|
696
|
+
delete document.state[scope];
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
documents.push(document);
|
|
700
|
+
}
|
|
449
701
|
if (signal?.aborted) {
|
|
450
702
|
throw new AbortError();
|
|
451
703
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
704
|
+
// Apply paging
|
|
705
|
+
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
706
|
+
const limit = paging?.limit || documents.length;
|
|
707
|
+
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
708
|
+
// Create paged results
|
|
709
|
+
const hasMore = startIndex + limit < documents.length;
|
|
710
|
+
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
711
|
+
return {
|
|
712
|
+
results: pagedDocuments,
|
|
713
|
+
options: paging || { cursor: "0", limit: documents.length },
|
|
714
|
+
nextCursor,
|
|
715
|
+
next: hasMore
|
|
716
|
+
? () => this.findByIds(ids, view, { cursor: nextCursor, limit }, undefined, signal)
|
|
717
|
+
: undefined,
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
else {
|
|
721
|
+
const documents = [];
|
|
722
|
+
// Fetch each document by ID using documentView
|
|
723
|
+
for (const id of ids) {
|
|
724
|
+
if (signal?.aborted) {
|
|
725
|
+
throw new AbortError();
|
|
726
|
+
}
|
|
727
|
+
try {
|
|
728
|
+
const document = await this.documentView.get(id, view, undefined, signal);
|
|
729
|
+
documents.push(document);
|
|
730
|
+
}
|
|
731
|
+
catch {
|
|
732
|
+
// Skip documents that don't exist or can't be accessed
|
|
733
|
+
continue;
|
|
466
734
|
}
|
|
467
735
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
736
|
+
if (signal?.aborted) {
|
|
737
|
+
throw new AbortError();
|
|
738
|
+
}
|
|
739
|
+
// Apply paging
|
|
740
|
+
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
741
|
+
const limit = paging?.limit || documents.length;
|
|
742
|
+
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
743
|
+
// Create paged results
|
|
744
|
+
const hasMore = startIndex + limit < documents.length;
|
|
745
|
+
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
746
|
+
return {
|
|
747
|
+
results: pagedDocuments,
|
|
748
|
+
options: paging || { cursor: "0", limit: documents.length },
|
|
749
|
+
nextCursor,
|
|
750
|
+
next: hasMore
|
|
751
|
+
? () => this.findByIds(ids, view, { cursor: nextCursor, limit }, undefined, signal)
|
|
752
|
+
: undefined,
|
|
753
|
+
};
|
|
472
754
|
}
|
|
473
|
-
// Apply paging
|
|
474
|
-
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
475
|
-
const limit = paging?.limit || documents.length;
|
|
476
|
-
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
477
|
-
// Create paged results
|
|
478
|
-
const hasMore = startIndex + limit < documents.length;
|
|
479
|
-
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
480
|
-
return {
|
|
481
|
-
results: pagedDocuments,
|
|
482
|
-
options: paging || { cursor: "0", limit: documents.length },
|
|
483
|
-
nextCursor,
|
|
484
|
-
next: hasMore
|
|
485
|
-
? () => this.findByIds(ids, view, { cursor: nextCursor, limit }, signal)
|
|
486
|
-
: undefined,
|
|
487
|
-
};
|
|
488
755
|
}
|
|
489
756
|
/**
|
|
490
757
|
* Finds documents by their slugs
|
|
491
758
|
*/
|
|
492
|
-
async findBySlugs(slugs, view, paging, signal) {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
// If slug resolution fails, return empty results
|
|
501
|
-
// This matches the behavior expected from a search operation
|
|
502
|
-
ids = [];
|
|
503
|
-
}
|
|
504
|
-
// Fetch each document by resolved ID
|
|
505
|
-
for (const id of ids) {
|
|
506
|
-
if (signal?.aborted) {
|
|
507
|
-
throw new AbortError();
|
|
508
|
-
}
|
|
509
|
-
let document;
|
|
759
|
+
async findBySlugs(slugs, view, paging, consistencyToken, signal) {
|
|
760
|
+
if (consistencyToken) {
|
|
761
|
+
await this.documentView.waitForConsistency(consistencyToken, undefined, signal);
|
|
762
|
+
}
|
|
763
|
+
if (this.features.legacyStorageEnabled) {
|
|
764
|
+
const documents = [];
|
|
765
|
+
// Use storage to resolve slugs to IDs
|
|
766
|
+
let ids;
|
|
510
767
|
try {
|
|
511
|
-
|
|
768
|
+
ids = await this.documentStorage.resolveIds(slugs, signal);
|
|
512
769
|
}
|
|
513
770
|
catch {
|
|
514
|
-
//
|
|
515
|
-
|
|
771
|
+
// If slug resolution fails, return empty results
|
|
772
|
+
// This matches the behavior expected from a search operation
|
|
773
|
+
ids = [];
|
|
516
774
|
}
|
|
517
|
-
//
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
delete document.state[scope];
|
|
775
|
+
// Fetch each document by resolved ID
|
|
776
|
+
for (const id of ids) {
|
|
777
|
+
if (signal?.aborted) {
|
|
778
|
+
throw new AbortError();
|
|
522
779
|
}
|
|
780
|
+
let document;
|
|
781
|
+
try {
|
|
782
|
+
document = await this.documentStorage.get(id);
|
|
783
|
+
}
|
|
784
|
+
catch {
|
|
785
|
+
// Skip documents that don't exist or can't be accessed
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
// Apply view filter - This will be removed when we pass the viewfilter along
|
|
789
|
+
// to the underlying store, but is here now for the interface.
|
|
790
|
+
for (const scope in document.state) {
|
|
791
|
+
if (!matchesScope(view, scope)) {
|
|
792
|
+
delete document.state[scope];
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
documents.push(document);
|
|
523
796
|
}
|
|
524
|
-
|
|
797
|
+
if (signal?.aborted) {
|
|
798
|
+
throw new AbortError();
|
|
799
|
+
}
|
|
800
|
+
// Apply paging - this will be removed when we pass the paging along
|
|
801
|
+
// to the underlying store, but is here now for the interface.
|
|
802
|
+
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
803
|
+
const limit = paging?.limit || documents.length;
|
|
804
|
+
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
805
|
+
// Create paged results
|
|
806
|
+
const hasMore = startIndex + limit < documents.length;
|
|
807
|
+
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
808
|
+
return {
|
|
809
|
+
results: pagedDocuments,
|
|
810
|
+
options: paging || { cursor: "0", limit: documents.length },
|
|
811
|
+
nextCursor,
|
|
812
|
+
next: hasMore
|
|
813
|
+
? () => this.findBySlugs(slugs, view, { cursor: nextCursor, limit }, undefined, signal)
|
|
814
|
+
: undefined,
|
|
815
|
+
};
|
|
525
816
|
}
|
|
526
|
-
|
|
527
|
-
|
|
817
|
+
else {
|
|
818
|
+
const documents = [];
|
|
819
|
+
// Resolve each slug to a document ID
|
|
820
|
+
const documentIds = [];
|
|
821
|
+
for (const slug of slugs) {
|
|
822
|
+
if (signal?.aborted) {
|
|
823
|
+
throw new AbortError();
|
|
824
|
+
}
|
|
825
|
+
const documentId = await this.documentView.resolveSlug(slug, view, undefined, signal);
|
|
826
|
+
if (documentId) {
|
|
827
|
+
documentIds.push(documentId);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
// Fetch each document
|
|
831
|
+
for (const documentId of documentIds) {
|
|
832
|
+
if (signal?.aborted) {
|
|
833
|
+
throw new AbortError();
|
|
834
|
+
}
|
|
835
|
+
try {
|
|
836
|
+
const document = await this.documentView.get(documentId, view, undefined, signal);
|
|
837
|
+
documents.push(document);
|
|
838
|
+
}
|
|
839
|
+
catch {
|
|
840
|
+
// Skip documents that don't exist or can't be accessed
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
if (signal?.aborted) {
|
|
845
|
+
throw new AbortError();
|
|
846
|
+
}
|
|
847
|
+
// Apply paging
|
|
848
|
+
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
849
|
+
const limit = paging?.limit || documents.length;
|
|
850
|
+
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
851
|
+
// Create paged results
|
|
852
|
+
const hasMore = startIndex + limit < documents.length;
|
|
853
|
+
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
854
|
+
return {
|
|
855
|
+
results: pagedDocuments,
|
|
856
|
+
options: paging || { cursor: "0", limit: documents.length },
|
|
857
|
+
nextCursor,
|
|
858
|
+
next: hasMore
|
|
859
|
+
? () => this.findBySlugs(slugs, view, { cursor: nextCursor, limit }, undefined, signal)
|
|
860
|
+
: undefined,
|
|
861
|
+
};
|
|
528
862
|
}
|
|
529
|
-
// Apply paging - this will be removed when we pass the paging along
|
|
530
|
-
// to the underlying store, but is here now for the interface.
|
|
531
|
-
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
532
|
-
const limit = paging?.limit || documents.length;
|
|
533
|
-
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
534
|
-
// Create paged results
|
|
535
|
-
const hasMore = startIndex + limit < documents.length;
|
|
536
|
-
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
537
|
-
return {
|
|
538
|
-
results: pagedDocuments,
|
|
539
|
-
options: paging || { cursor: "0", limit: documents.length },
|
|
540
|
-
nextCursor,
|
|
541
|
-
next: hasMore
|
|
542
|
-
? () => this.findBySlugs(slugs, view, { cursor: nextCursor, limit }, signal)
|
|
543
|
-
: undefined,
|
|
544
|
-
};
|
|
545
863
|
}
|
|
546
864
|
/**
|
|
547
865
|
* Finds documents by parent ID
|
|
548
866
|
*/
|
|
549
867
|
async findByParentId(parentId, view, paging, signal) {
|
|
550
|
-
// Get child
|
|
551
|
-
const
|
|
868
|
+
// Get child relationships from indexer
|
|
869
|
+
const relationships = await this._documentIndexer.getOutgoing(parentId, ["child"], undefined, signal);
|
|
552
870
|
if (signal?.aborted) {
|
|
553
871
|
throw new AbortError();
|
|
554
872
|
}
|
|
555
873
|
const documents = [];
|
|
556
|
-
// Fetch each child document
|
|
557
|
-
for (const
|
|
874
|
+
// Fetch each child document using the appropriate storage method
|
|
875
|
+
for (const relationship of relationships) {
|
|
558
876
|
if (signal?.aborted) {
|
|
559
877
|
throw new AbortError();
|
|
560
878
|
}
|
|
561
|
-
let document;
|
|
562
879
|
try {
|
|
563
|
-
document
|
|
880
|
+
let document;
|
|
881
|
+
if (this.features.legacyStorageEnabled) {
|
|
882
|
+
document = await this.documentStorage.get(relationship.targetId);
|
|
883
|
+
// Apply view filter for legacy storage
|
|
884
|
+
for (const scope in document.state) {
|
|
885
|
+
if (!matchesScope(view, scope)) {
|
|
886
|
+
delete document.state[scope];
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
document = await this.documentView.get(relationship.targetId, view, undefined, signal);
|
|
892
|
+
}
|
|
893
|
+
documents.push(document);
|
|
564
894
|
}
|
|
565
895
|
catch {
|
|
566
896
|
// Skip documents that don't exist or can't be accessed
|
|
567
|
-
// This matches the behavior expected from a search operation
|
|
568
897
|
continue;
|
|
569
898
|
}
|
|
570
|
-
// Apply view filter - This will be removed when we pass the viewfilter along
|
|
571
|
-
// to the underlying store, but is here now for the interface.
|
|
572
|
-
for (const scope in document.state) {
|
|
573
|
-
if (!matchesScope(view, scope)) {
|
|
574
|
-
delete document.state[scope];
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
documents.push(document);
|
|
578
899
|
}
|
|
579
900
|
if (signal?.aborted) {
|
|
580
901
|
throw new AbortError();
|
|
@@ -598,61 +919,70 @@ export class Reactor {
|
|
|
598
919
|
/**
|
|
599
920
|
* Finds documents by type
|
|
600
921
|
*/
|
|
601
|
-
async findByType(type, view, paging, signal) {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
for (const documentId of documentIds) {
|
|
922
|
+
async findByType(type, view, paging, consistencyToken, signal) {
|
|
923
|
+
if (consistencyToken) {
|
|
924
|
+
await this.documentView.waitForConsistency(consistencyToken, undefined, signal);
|
|
925
|
+
}
|
|
926
|
+
if (this.features.legacyStorageEnabled) {
|
|
927
|
+
const documents = [];
|
|
928
|
+
// Use storage's findByType method directly
|
|
929
|
+
const cursor = paging?.cursor;
|
|
930
|
+
const limit = paging?.limit || 100;
|
|
931
|
+
// Get document IDs of the specified type
|
|
932
|
+
const { documents: documentIds, nextCursor } = await this.documentStorage.findByType(type, limit, cursor);
|
|
613
933
|
if (signal?.aborted) {
|
|
614
934
|
throw new AbortError();
|
|
615
935
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
936
|
+
// Fetch each document by its ID
|
|
937
|
+
for (const documentId of documentIds) {
|
|
938
|
+
if (signal?.aborted) {
|
|
939
|
+
throw new AbortError();
|
|
940
|
+
}
|
|
941
|
+
let document;
|
|
942
|
+
try {
|
|
943
|
+
document = await this.documentStorage.get(documentId);
|
|
944
|
+
}
|
|
945
|
+
catch {
|
|
946
|
+
// Skip documents that can't be retrieved
|
|
947
|
+
continue;
|
|
948
|
+
}
|
|
949
|
+
// Apply view filter
|
|
950
|
+
for (const scope in document.state) {
|
|
951
|
+
if (!matchesScope(view, scope)) {
|
|
952
|
+
delete document.state[scope];
|
|
953
|
+
}
|
|
628
954
|
}
|
|
955
|
+
documents.push(document);
|
|
629
956
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
957
|
+
if (signal?.aborted) {
|
|
958
|
+
throw new AbortError();
|
|
959
|
+
}
|
|
960
|
+
// Results are already paged from the storage layer
|
|
961
|
+
return {
|
|
962
|
+
results: documents,
|
|
963
|
+
options: paging || { cursor: cursor || "0", limit },
|
|
964
|
+
nextCursor,
|
|
965
|
+
next: nextCursor
|
|
966
|
+
? async () => this.findByType(type, view, { cursor: nextCursor, limit }, undefined, signal)
|
|
967
|
+
: undefined,
|
|
968
|
+
};
|
|
634
969
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
: undefined,
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
toErrorInfo(error) {
|
|
646
|
-
if (error instanceof Error) {
|
|
970
|
+
else {
|
|
971
|
+
const result = await this.documentView.findByType(type, view, paging, undefined, signal);
|
|
972
|
+
if (signal?.aborted) {
|
|
973
|
+
throw new AbortError();
|
|
974
|
+
}
|
|
975
|
+
const cursor = paging?.cursor;
|
|
976
|
+
const limit = paging?.limit || 100;
|
|
647
977
|
return {
|
|
648
|
-
|
|
649
|
-
|
|
978
|
+
results: result.items,
|
|
979
|
+
options: paging || { cursor: cursor || "0", limit },
|
|
980
|
+
nextCursor: result.nextCursor,
|
|
981
|
+
next: result.nextCursor
|
|
982
|
+
? async () => this.findByType(type, view, { cursor: result.nextCursor, limit }, undefined, signal)
|
|
983
|
+
: undefined,
|
|
650
984
|
};
|
|
651
985
|
}
|
|
652
|
-
return {
|
|
653
|
-
message: error,
|
|
654
|
-
stack: new Error().stack || "",
|
|
655
|
-
};
|
|
656
986
|
}
|
|
657
987
|
}
|
|
658
988
|
//# sourceMappingURL=reactor.js.map
|