@powerhousedao/reactor 4.1.0-dev.42 → 4.1.0-dev.43
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/bench/event-bus.bench.js.map +1 -1
- package/dist/src/events/event-bus.d.ts +2 -2
- package/dist/src/events/event-bus.d.ts.map +1 -1
- package/dist/src/events/event-bus.js +1 -1
- package/dist/src/events/event-bus.js.map +1 -1
- package/dist/src/events/interfaces.d.ts +1 -1
- package/dist/src/events/interfaces.d.ts.map +1 -1
- package/dist/src/events/types.d.ts +1 -1
- package/dist/src/events/types.d.ts.map +1 -1
- package/dist/src/events/types.js.map +1 -1
- package/dist/src/executor/interfaces.d.ts +2 -2
- package/dist/src/executor/interfaces.d.ts.map +1 -1
- package/dist/src/executor/job-executor.js +1 -1
- package/dist/src/executor/job-executor.js.map +1 -1
- package/dist/src/executor/types.d.ts +1 -1
- package/dist/src/executor/types.d.ts.map +1 -1
- package/dist/src/queue/interfaces.d.ts +1 -1
- package/dist/src/queue/interfaces.d.ts.map +1 -1
- package/dist/src/reactor.d.ts +15 -5
- package/dist/src/reactor.d.ts.map +1 -1
- package/dist/src/reactor.js +210 -215
- package/dist/src/reactor.js.map +1 -1
- package/dist/test/reactor-read.test.d.ts +2 -0
- package/dist/test/reactor-read.test.d.ts.map +1 -0
- package/dist/test/reactor-read.test.js +330 -0
- package/dist/test/reactor-read.test.js.map +1 -0
- package/dist/test/reactor-write.test.d.ts +2 -0
- package/dist/test/reactor-write.test.d.ts.map +1 -0
- package/dist/test/reactor-write.test.js +232 -0
- package/dist/test/reactor-write.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/dist/test/reactor.test.d.ts +0 -2
- package/dist/test/reactor.test.d.ts.map +0 -1
- package/dist/test/reactor.test.js +0 -445
- package/dist/test/reactor.test.js.map +0 -1
package/dist/src/reactor.js
CHANGED
|
@@ -20,10 +20,20 @@ import { filterByParentId, filterByType } from "./utils.js";
|
|
|
20
20
|
*/
|
|
21
21
|
export class Reactor {
|
|
22
22
|
driveServer;
|
|
23
|
+
documentStorage;
|
|
23
24
|
shutdownStatus;
|
|
24
25
|
setShutdown;
|
|
25
|
-
|
|
26
|
+
eventBus;
|
|
27
|
+
queue;
|
|
28
|
+
jobExecutor;
|
|
29
|
+
jobExecutorStarted = false;
|
|
30
|
+
constructor(driveServer, documentStorage, eventBus, queue, jobExecutor) {
|
|
31
|
+
// Store required dependencies
|
|
26
32
|
this.driveServer = driveServer;
|
|
33
|
+
this.documentStorage = documentStorage;
|
|
34
|
+
this.eventBus = eventBus;
|
|
35
|
+
this.queue = queue;
|
|
36
|
+
this.jobExecutor = jobExecutor;
|
|
27
37
|
// Create mutable shutdown status using factory method
|
|
28
38
|
const [status, setter] = createMutableShutdownStatus(false);
|
|
29
39
|
this.shutdownStatus = status;
|
|
@@ -83,12 +93,11 @@ export class Reactor {
|
|
|
83
93
|
* Retrieves a specific PHDocument by id
|
|
84
94
|
*/
|
|
85
95
|
async get(id, view, signal) {
|
|
86
|
-
const document = await this.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
throw new Error(`Document not found: ${id}`);
|
|
96
|
+
const document = await this.documentStorage.get(id);
|
|
97
|
+
if (signal?.aborted) {
|
|
98
|
+
throw new AbortError();
|
|
90
99
|
}
|
|
91
|
-
const childIds = await this.
|
|
100
|
+
const childIds = await this.documentStorage.getChildren(id);
|
|
92
101
|
if (signal?.aborted) {
|
|
93
102
|
throw new AbortError();
|
|
94
103
|
}
|
|
@@ -96,7 +105,6 @@ export class Reactor {
|
|
|
96
105
|
// to the underlying store, but is here now for the interface.
|
|
97
106
|
for (const scope in document.state) {
|
|
98
107
|
if (!matchesScope(view, scope)) {
|
|
99
|
-
// eslint-disable-next-line
|
|
100
108
|
delete document.state[scope];
|
|
101
109
|
}
|
|
102
110
|
}
|
|
@@ -109,40 +117,30 @@ export class Reactor {
|
|
|
109
117
|
* Retrieves a specific PHDocument by slug
|
|
110
118
|
*/
|
|
111
119
|
async getBySlug(slug, view, signal) {
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const document = await this.driveServer.getDocument(docId);
|
|
122
|
-
if (document.header.slug === slug) {
|
|
123
|
-
const childIds = await this.driveServer.getDocuments(docId);
|
|
124
|
-
if (signal?.aborted) {
|
|
125
|
-
throw new AbortError();
|
|
126
|
-
}
|
|
127
|
-
// Apply view filter - This will be removed when we pass the viewfilter along
|
|
128
|
-
// to the underlying store, but is here now for the interface.
|
|
129
|
-
for (const scope in document.state) {
|
|
130
|
-
if (!matchesScope(view, scope)) {
|
|
131
|
-
// eslint-disable-next-line
|
|
132
|
-
delete document.state[scope];
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return { document, childIds };
|
|
136
|
-
}
|
|
120
|
+
// Use the storage layer to resolve slug to ID
|
|
121
|
+
let ids;
|
|
122
|
+
try {
|
|
123
|
+
ids = await this.documentStorage.resolveIds([slug], signal);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
// If the error is from resolveIds (document not found), wrap it with our message
|
|
127
|
+
if (error instanceof Error && error.message.includes("not found")) {
|
|
128
|
+
throw new Error(`Document not found with slug: ${slug}`);
|
|
137
129
|
}
|
|
130
|
+
throw error;
|
|
138
131
|
}
|
|
139
|
-
|
|
132
|
+
if (ids.length === 0 || !ids[0]) {
|
|
133
|
+
throw new Error(`Document not found with slug: ${slug}`);
|
|
134
|
+
}
|
|
135
|
+
// Now get the document by its resolved ID
|
|
136
|
+
return await this.get(ids[0], view, signal);
|
|
140
137
|
}
|
|
141
138
|
/**
|
|
142
139
|
* Retrieves the operations for a document
|
|
143
140
|
*/
|
|
144
141
|
async getOperations(documentId, view, paging, signal) {
|
|
145
|
-
|
|
142
|
+
// Use storage directly to get the document
|
|
143
|
+
const document = await this.documentStorage.get(documentId);
|
|
146
144
|
if (signal?.aborted) {
|
|
147
145
|
throw new AbortError();
|
|
148
146
|
}
|
|
@@ -201,8 +199,7 @@ export class Reactor {
|
|
|
201
199
|
results = await this.findByType(search.type, view, paging, signal);
|
|
202
200
|
}
|
|
203
201
|
else {
|
|
204
|
-
|
|
205
|
-
results = await this.findAll(view, paging, signal);
|
|
202
|
+
throw new Error("No search criteria provided");
|
|
206
203
|
}
|
|
207
204
|
if (signal?.aborted) {
|
|
208
205
|
throw new AbortError();
|
|
@@ -217,14 +214,17 @@ export class Reactor {
|
|
|
217
214
|
// BaseDocumentDriveServer uses addDocument, not createDocument
|
|
218
215
|
// addDocument adds an existing document to a drive
|
|
219
216
|
await this.driveServer.addDocument(document);
|
|
220
|
-
// Return success status
|
|
221
|
-
// TODO: Phase 4 - This will return a job that goes through the queue
|
|
222
|
-
return JobStatus.COMPLETED;
|
|
223
217
|
}
|
|
224
|
-
catch
|
|
218
|
+
catch {
|
|
225
219
|
// TODO: Phase 4 - This will return a job that can be retried
|
|
226
220
|
return JobStatus.FAILED;
|
|
227
221
|
}
|
|
222
|
+
if (signal?.aborted) {
|
|
223
|
+
throw new AbortError();
|
|
224
|
+
}
|
|
225
|
+
// Return success status
|
|
226
|
+
// TODO: Phase 4 - This will return a job that goes through the queue
|
|
227
|
+
return JobStatus.COMPLETED;
|
|
228
228
|
}
|
|
229
229
|
/**
|
|
230
230
|
* Deletes a document
|
|
@@ -235,12 +235,6 @@ export class Reactor {
|
|
|
235
235
|
// Delete document using drive server
|
|
236
236
|
await this.driveServer.deleteDocument(id);
|
|
237
237
|
// TODO: Implement cascade deletion when propagate mode is CASCADE
|
|
238
|
-
// Return success job info
|
|
239
|
-
// TODO: Phase 4 - This will return a job that goes through the queue
|
|
240
|
-
return {
|
|
241
|
-
id: jobId,
|
|
242
|
-
status: JobStatus.COMPLETED,
|
|
243
|
-
};
|
|
244
238
|
}
|
|
245
239
|
catch (error) {
|
|
246
240
|
// TODO: Phase 4 - This will return a job that can be retried
|
|
@@ -250,60 +244,63 @@ export class Reactor {
|
|
|
250
244
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
251
245
|
};
|
|
252
246
|
}
|
|
247
|
+
if (signal?.aborted) {
|
|
248
|
+
throw new AbortError();
|
|
249
|
+
}
|
|
250
|
+
// Return success job info
|
|
251
|
+
// TODO: Phase 4 - This will return a job that goes through the queue
|
|
252
|
+
return {
|
|
253
|
+
id: jobId,
|
|
254
|
+
status: JobStatus.COMPLETED,
|
|
255
|
+
};
|
|
253
256
|
}
|
|
254
257
|
/**
|
|
255
258
|
* Applies a list of actions to a document
|
|
256
259
|
*/
|
|
257
260
|
async mutate(id, actions) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
261
|
+
// Ensure the job executor is running
|
|
262
|
+
await this.ensureJobExecutorRunning();
|
|
263
|
+
// Create jobs for each action/operation
|
|
264
|
+
const jobs = actions.map((action, index) => ({
|
|
265
|
+
id: uuidv4(),
|
|
266
|
+
documentId: id,
|
|
267
|
+
scope: action.scope || "global",
|
|
268
|
+
branch: "main", // Default to main branch
|
|
269
|
+
operation: {
|
|
263
270
|
index: index,
|
|
264
|
-
timestampUtcMs: action.timestampUtcMs,
|
|
265
|
-
hash: "", // Will be computed by the
|
|
271
|
+
timestampUtcMs: String(action.timestampUtcMs || Date.now()),
|
|
272
|
+
hash: "", // Will be computed by the executor
|
|
266
273
|
skip: 0,
|
|
267
274
|
action: action,
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
status: JobStatus.COMPLETED,
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
catch (error) {
|
|
279
|
-
// TODO: Phase 4 - This will return a job that can be retried
|
|
280
|
-
return {
|
|
281
|
-
id: jobId,
|
|
282
|
-
status: JobStatus.FAILED,
|
|
283
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
284
|
-
};
|
|
275
|
+
},
|
|
276
|
+
createdAt: new Date().toISOString(),
|
|
277
|
+
maxRetries: 3,
|
|
278
|
+
}));
|
|
279
|
+
// Enqueue all jobs
|
|
280
|
+
for (const job of jobs) {
|
|
281
|
+
await this.queue.enqueue(job);
|
|
285
282
|
}
|
|
283
|
+
// Return job info for the batch (using the first job's ID as the batch ID)
|
|
284
|
+
const batchJobId = jobs.length > 0 ? jobs[0].id : uuidv4();
|
|
285
|
+
return {
|
|
286
|
+
id: batchJobId,
|
|
287
|
+
status: JobStatus.PENDING,
|
|
288
|
+
};
|
|
286
289
|
}
|
|
287
290
|
/**
|
|
288
291
|
* Adds multiple documents as children to another
|
|
289
292
|
*/
|
|
290
293
|
async addChildren(parentId, documentIds, view, signal) {
|
|
291
294
|
const jobId = uuidv4();
|
|
295
|
+
// Check abort signal before starting
|
|
296
|
+
if (signal?.aborted) {
|
|
297
|
+
throw new AbortError();
|
|
298
|
+
}
|
|
299
|
+
// TODO: Implement when drive server supports hierarchical documents
|
|
300
|
+
// For now, this is a placeholder implementation
|
|
301
|
+
// Verify parent exists
|
|
292
302
|
try {
|
|
293
|
-
// TODO: Implement when drive server supports hierarchical documents
|
|
294
|
-
// For now, this is a placeholder implementation
|
|
295
|
-
// Verify parent exists
|
|
296
303
|
await this.driveServer.getDocument(parentId);
|
|
297
|
-
// Verify all children exist
|
|
298
|
-
for (const childId of documentIds) {
|
|
299
|
-
await this.driveServer.getDocument(childId);
|
|
300
|
-
}
|
|
301
|
-
// TODO: Actually establish parent-child relationships
|
|
302
|
-
// Return success job info
|
|
303
|
-
return {
|
|
304
|
-
id: jobId,
|
|
305
|
-
status: JobStatus.COMPLETED,
|
|
306
|
-
};
|
|
307
304
|
}
|
|
308
305
|
catch (error) {
|
|
309
306
|
return {
|
|
@@ -312,23 +309,48 @@ export class Reactor {
|
|
|
312
309
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
313
310
|
};
|
|
314
311
|
}
|
|
312
|
+
// Check abort signal after parent verification
|
|
313
|
+
if (signal?.aborted) {
|
|
314
|
+
throw new AbortError();
|
|
315
|
+
}
|
|
316
|
+
// Verify all children exist
|
|
317
|
+
for (const childId of documentIds) {
|
|
318
|
+
try {
|
|
319
|
+
await this.driveServer.getDocument(childId);
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
return {
|
|
323
|
+
id: jobId,
|
|
324
|
+
status: JobStatus.FAILED,
|
|
325
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
// Check abort signal after each child verification
|
|
329
|
+
if (signal?.aborted) {
|
|
330
|
+
throw new AbortError();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// TODO: Actually establish parent-child relationships
|
|
334
|
+
// Return success job info
|
|
335
|
+
return {
|
|
336
|
+
id: jobId,
|
|
337
|
+
status: JobStatus.COMPLETED,
|
|
338
|
+
};
|
|
315
339
|
}
|
|
316
340
|
/**
|
|
317
341
|
* Removes multiple documents as children from another
|
|
318
342
|
*/
|
|
319
343
|
async removeChildren(parentId, documentIds, view, signal) {
|
|
320
344
|
const jobId = uuidv4();
|
|
345
|
+
// Check abort signal before starting
|
|
346
|
+
if (signal?.aborted) {
|
|
347
|
+
throw new AbortError();
|
|
348
|
+
}
|
|
349
|
+
// TODO: Implement when drive server supports hierarchical documents
|
|
350
|
+
// For now, this is a placeholder implementation
|
|
351
|
+
// Verify parent exists
|
|
321
352
|
try {
|
|
322
|
-
// TODO: Implement when drive server supports hierarchical documents
|
|
323
|
-
// For now, this is a placeholder implementation
|
|
324
|
-
// Verify parent exists
|
|
325
353
|
await this.driveServer.getDocument(parentId);
|
|
326
|
-
// TODO: Actually remove parent-child relationships
|
|
327
|
-
// Return success job info
|
|
328
|
-
return {
|
|
329
|
-
id: jobId,
|
|
330
|
-
status: JobStatus.COMPLETED,
|
|
331
|
-
};
|
|
332
354
|
}
|
|
333
355
|
catch (error) {
|
|
334
356
|
return {
|
|
@@ -337,6 +359,16 @@ export class Reactor {
|
|
|
337
359
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
338
360
|
};
|
|
339
361
|
}
|
|
362
|
+
// Check abort signal after parent verification
|
|
363
|
+
if (signal?.aborted) {
|
|
364
|
+
throw new AbortError();
|
|
365
|
+
}
|
|
366
|
+
// TODO: Actually remove parent-child relationships
|
|
367
|
+
// Return success job info
|
|
368
|
+
return {
|
|
369
|
+
id: jobId,
|
|
370
|
+
status: JobStatus.COMPLETED,
|
|
371
|
+
};
|
|
340
372
|
}
|
|
341
373
|
/**
|
|
342
374
|
* Retrieves the status of a job
|
|
@@ -350,36 +382,46 @@ export class Reactor {
|
|
|
350
382
|
error: "Job tracking not yet implemented",
|
|
351
383
|
};
|
|
352
384
|
}
|
|
385
|
+
/**
|
|
386
|
+
* Starts the job executor if not already running.
|
|
387
|
+
* Called automatically when the first job is enqueued.
|
|
388
|
+
*/
|
|
389
|
+
async ensureJobExecutorRunning() {
|
|
390
|
+
if (!this.jobExecutorStarted) {
|
|
391
|
+
await this.jobExecutor.start({
|
|
392
|
+
maxConcurrency: 5,
|
|
393
|
+
jobTimeout: 30000,
|
|
394
|
+
});
|
|
395
|
+
this.jobExecutorStarted = true;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
353
398
|
/**
|
|
354
399
|
* Finds documents by their IDs
|
|
355
400
|
*/
|
|
356
401
|
async findByIds(ids, view, paging, signal) {
|
|
357
402
|
const documents = [];
|
|
358
|
-
// Fetch each document by ID
|
|
403
|
+
// Fetch each document by ID using storage directly
|
|
359
404
|
for (const id of ids) {
|
|
360
405
|
if (signal?.aborted) {
|
|
361
406
|
throw new AbortError();
|
|
362
407
|
}
|
|
408
|
+
let document;
|
|
363
409
|
try {
|
|
364
|
-
|
|
365
|
-
if (!document) {
|
|
366
|
-
continue; // Skip if document not found
|
|
367
|
-
}
|
|
368
|
-
// Apply view filter - This will be removed when we pass the viewfilter along
|
|
369
|
-
// to the underlying store, but is here now for the interface.
|
|
370
|
-
for (const scope in document.state) {
|
|
371
|
-
if (!matchesScope(view, scope)) {
|
|
372
|
-
// eslint-disable-next-line
|
|
373
|
-
delete document.state[scope];
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
documents.push(document);
|
|
410
|
+
document = await this.documentStorage.get(id);
|
|
377
411
|
}
|
|
378
412
|
catch {
|
|
379
413
|
// Skip documents that don't exist or can't be accessed
|
|
380
414
|
// This matches the behavior expected from a search operation
|
|
381
415
|
continue;
|
|
382
416
|
}
|
|
417
|
+
// Apply view filter - This will be removed when we pass the viewfilter along
|
|
418
|
+
// to the underlying store, but is here now for the interface.
|
|
419
|
+
for (const scope in document.state) {
|
|
420
|
+
if (!matchesScope(view, scope)) {
|
|
421
|
+
delete document.state[scope];
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
documents.push(document);
|
|
383
425
|
}
|
|
384
426
|
if (signal?.aborted) {
|
|
385
427
|
throw new AbortError();
|
|
@@ -405,92 +447,43 @@ export class Reactor {
|
|
|
405
447
|
*/
|
|
406
448
|
async findBySlugs(slugs, view, paging, signal) {
|
|
407
449
|
const documents = [];
|
|
408
|
-
//
|
|
409
|
-
|
|
450
|
+
// Use storage to resolve slugs to IDs
|
|
451
|
+
let ids;
|
|
452
|
+
try {
|
|
453
|
+
ids = await this.documentStorage.resolveIds(slugs, signal);
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
// If slug resolution fails, return empty results
|
|
457
|
+
// This matches the behavior expected from a search operation
|
|
458
|
+
ids = [];
|
|
459
|
+
}
|
|
460
|
+
// Fetch each document by resolved ID
|
|
461
|
+
for (const id of ids) {
|
|
410
462
|
if (signal?.aborted) {
|
|
411
463
|
throw new AbortError();
|
|
412
464
|
}
|
|
465
|
+
let document;
|
|
413
466
|
try {
|
|
414
|
-
|
|
415
|
-
const drives = await this.driveServer.getDrives();
|
|
416
|
-
let found = false;
|
|
417
|
-
for (const driveId of drives) {
|
|
418
|
-
const documentIds = await this.driveServer.getDocuments(driveId);
|
|
419
|
-
for (const docId of documentIds) {
|
|
420
|
-
const document = await this.driveServer.getDocument(docId);
|
|
421
|
-
if (document && document.header.slug === slug) {
|
|
422
|
-
// Apply view filter - This will be removed when we pass the viewfilter along
|
|
423
|
-
// to the underlying store, but is here now for the interface.
|
|
424
|
-
for (const scope in document.state) {
|
|
425
|
-
if (!matchesScope(view, scope)) {
|
|
426
|
-
// eslint-disable-next-line
|
|
427
|
-
delete document.state[scope];
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
documents.push(document);
|
|
431
|
-
found = true;
|
|
432
|
-
break;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
if (found)
|
|
436
|
-
break;
|
|
437
|
-
}
|
|
467
|
+
document = await this.documentStorage.get(id);
|
|
438
468
|
}
|
|
439
469
|
catch {
|
|
440
470
|
// Skip documents that don't exist or can't be accessed
|
|
441
|
-
// This matches the behavior expected from a search operation
|
|
442
471
|
continue;
|
|
443
472
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
450
|
-
const limit = paging?.limit || documents.length;
|
|
451
|
-
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
452
|
-
// Create paged results
|
|
453
|
-
const hasMore = startIndex + limit < documents.length;
|
|
454
|
-
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
455
|
-
return {
|
|
456
|
-
results: pagedDocuments,
|
|
457
|
-
options: paging || { cursor: "0", limit: documents.length },
|
|
458
|
-
nextCursor,
|
|
459
|
-
next: hasMore
|
|
460
|
-
? () => this.findBySlugs(slugs, view, { cursor: nextCursor, limit }, signal)
|
|
461
|
-
: undefined,
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
/**
|
|
465
|
-
* Finds all documents
|
|
466
|
-
*/
|
|
467
|
-
async findAll(view, paging, signal) {
|
|
468
|
-
const documents = [];
|
|
469
|
-
// Get all drives
|
|
470
|
-
const drives = await this.driveServer.getDrives();
|
|
471
|
-
for (const driveId of drives) {
|
|
472
|
-
if (signal?.aborted) {
|
|
473
|
-
throw new AbortError();
|
|
474
|
-
}
|
|
475
|
-
const documentIds = await this.driveServer.getDocuments(driveId);
|
|
476
|
-
for (const docId of documentIds) {
|
|
477
|
-
const document = await this.driveServer.getDocument(docId);
|
|
478
|
-
if (document) {
|
|
479
|
-
// Apply view filter
|
|
480
|
-
for (const scope in document.state) {
|
|
481
|
-
if (!matchesScope(view, scope)) {
|
|
482
|
-
// eslint-disable-next-line
|
|
483
|
-
delete document.state[scope];
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
documents.push(document);
|
|
473
|
+
// Apply view filter - This will be removed when we pass the viewfilter along
|
|
474
|
+
// to the underlying store, but is here now for the interface.
|
|
475
|
+
for (const scope in document.state) {
|
|
476
|
+
if (!matchesScope(view, scope)) {
|
|
477
|
+
delete document.state[scope];
|
|
487
478
|
}
|
|
488
479
|
}
|
|
480
|
+
documents.push(document);
|
|
489
481
|
}
|
|
490
482
|
if (signal?.aborted) {
|
|
491
483
|
throw new AbortError();
|
|
492
484
|
}
|
|
493
|
-
// Apply paging
|
|
485
|
+
// Apply paging - this will be removed when we pass the paging along
|
|
486
|
+
// to the underlying store, but is here now for the interface.
|
|
494
487
|
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
495
488
|
const limit = paging?.limit || documents.length;
|
|
496
489
|
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
@@ -502,7 +495,7 @@ export class Reactor {
|
|
|
502
495
|
options: paging || { cursor: "0", limit: documents.length },
|
|
503
496
|
nextCursor,
|
|
504
497
|
next: hasMore
|
|
505
|
-
?
|
|
498
|
+
? () => this.findBySlugs(slugs, view, { cursor: nextCursor, limit }, signal)
|
|
506
499
|
: undefined,
|
|
507
500
|
};
|
|
508
501
|
}
|
|
@@ -510,8 +503,8 @@ export class Reactor {
|
|
|
510
503
|
* Finds documents by parent ID
|
|
511
504
|
*/
|
|
512
505
|
async findByParentId(parentId, view, paging, signal) {
|
|
513
|
-
// Get child document IDs from
|
|
514
|
-
const childIds = await this.
|
|
506
|
+
// Get child document IDs from storage
|
|
507
|
+
const childIds = await this.documentStorage.getChildren(parentId);
|
|
515
508
|
if (signal?.aborted) {
|
|
516
509
|
throw new AbortError();
|
|
517
510
|
}
|
|
@@ -521,23 +514,23 @@ export class Reactor {
|
|
|
521
514
|
if (signal?.aborted) {
|
|
522
515
|
throw new AbortError();
|
|
523
516
|
}
|
|
517
|
+
let document;
|
|
524
518
|
try {
|
|
525
|
-
|
|
526
|
-
// Apply view filter - This will be removed when we pass the viewfilter along
|
|
527
|
-
// to the underlying store, but is here now for the interface.
|
|
528
|
-
for (const scope in document.state) {
|
|
529
|
-
if (!matchesScope(view, scope)) {
|
|
530
|
-
// eslint-disable-next-line
|
|
531
|
-
delete document.state[scope];
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
documents.push(document);
|
|
519
|
+
document = await this.documentStorage.get(childId);
|
|
535
520
|
}
|
|
536
521
|
catch {
|
|
537
522
|
// Skip documents that don't exist or can't be accessed
|
|
538
523
|
// This matches the behavior expected from a search operation
|
|
539
524
|
continue;
|
|
540
525
|
}
|
|
526
|
+
// Apply view filter - This will be removed when we pass the viewfilter along
|
|
527
|
+
// to the underlying store, but is here now for the interface.
|
|
528
|
+
for (const scope in document.state) {
|
|
529
|
+
if (!matchesScope(view, scope)) {
|
|
530
|
+
delete document.state[scope];
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
documents.push(document);
|
|
541
534
|
}
|
|
542
535
|
if (signal?.aborted) {
|
|
543
536
|
throw new AbortError();
|
|
@@ -563,42 +556,44 @@ export class Reactor {
|
|
|
563
556
|
*/
|
|
564
557
|
async findByType(type, view, paging, signal) {
|
|
565
558
|
const documents = [];
|
|
566
|
-
//
|
|
567
|
-
const
|
|
568
|
-
|
|
559
|
+
// Use storage's findByType method directly
|
|
560
|
+
const cursor = paging?.cursor;
|
|
561
|
+
const limit = paging?.limit || 100;
|
|
562
|
+
// Get document IDs of the specified type
|
|
563
|
+
const { documents: documentIds, nextCursor } = await this.documentStorage.findByType(type, limit, cursor);
|
|
564
|
+
if (signal?.aborted) {
|
|
565
|
+
throw new AbortError();
|
|
566
|
+
}
|
|
567
|
+
// Fetch each document by its ID
|
|
568
|
+
for (const documentId of documentIds) {
|
|
569
569
|
if (signal?.aborted) {
|
|
570
570
|
throw new AbortError();
|
|
571
571
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
572
|
+
let document;
|
|
573
|
+
try {
|
|
574
|
+
document = await this.documentStorage.get(documentId);
|
|
575
|
+
}
|
|
576
|
+
catch {
|
|
577
|
+
// Skip documents that can't be retrieved
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
// Apply view filter
|
|
581
|
+
for (const scope in document.state) {
|
|
582
|
+
if (!matchesScope(view, scope)) {
|
|
583
|
+
delete document.state[scope];
|
|
584
584
|
}
|
|
585
585
|
}
|
|
586
|
+
documents.push(document);
|
|
586
587
|
}
|
|
587
588
|
if (signal?.aborted) {
|
|
588
589
|
throw new AbortError();
|
|
589
590
|
}
|
|
590
|
-
//
|
|
591
|
-
const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
|
|
592
|
-
const limit = paging?.limit || documents.length;
|
|
593
|
-
const pagedDocuments = documents.slice(startIndex, startIndex + limit);
|
|
594
|
-
// Create paged results
|
|
595
|
-
const hasMore = startIndex + limit < documents.length;
|
|
596
|
-
const nextCursor = hasMore ? String(startIndex + limit) : undefined;
|
|
591
|
+
// Results are already paged from the storage layer
|
|
597
592
|
return {
|
|
598
|
-
results:
|
|
599
|
-
options: paging || { cursor: "0", limit
|
|
593
|
+
results: documents,
|
|
594
|
+
options: paging || { cursor: cursor || "0", limit },
|
|
600
595
|
nextCursor,
|
|
601
|
-
next:
|
|
596
|
+
next: nextCursor
|
|
602
597
|
? async () => this.findByType(type, view, { cursor: nextCursor, limit }, signal)
|
|
603
598
|
: undefined,
|
|
604
599
|
};
|