@powerhousedao/reactor 4.1.0-dev.42 → 4.1.0-dev.44

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.
Files changed (36) hide show
  1. package/dist/bench/event-bus.bench.js.map +1 -1
  2. package/dist/src/events/event-bus.d.ts +2 -2
  3. package/dist/src/events/event-bus.d.ts.map +1 -1
  4. package/dist/src/events/event-bus.js +1 -1
  5. package/dist/src/events/event-bus.js.map +1 -1
  6. package/dist/src/events/interfaces.d.ts +1 -1
  7. package/dist/src/events/interfaces.d.ts.map +1 -1
  8. package/dist/src/events/types.d.ts +1 -1
  9. package/dist/src/events/types.d.ts.map +1 -1
  10. package/dist/src/events/types.js.map +1 -1
  11. package/dist/src/executor/interfaces.d.ts +2 -2
  12. package/dist/src/executor/interfaces.d.ts.map +1 -1
  13. package/dist/src/executor/job-executor.js +1 -1
  14. package/dist/src/executor/job-executor.js.map +1 -1
  15. package/dist/src/executor/types.d.ts +1 -1
  16. package/dist/src/executor/types.d.ts.map +1 -1
  17. package/dist/src/queue/interfaces.d.ts +1 -1
  18. package/dist/src/queue/interfaces.d.ts.map +1 -1
  19. package/dist/src/reactor.d.ts +15 -5
  20. package/dist/src/reactor.d.ts.map +1 -1
  21. package/dist/src/reactor.js +210 -215
  22. package/dist/src/reactor.js.map +1 -1
  23. package/dist/test/reactor-read.test.d.ts +2 -0
  24. package/dist/test/reactor-read.test.d.ts.map +1 -0
  25. package/dist/test/reactor-read.test.js +330 -0
  26. package/dist/test/reactor-read.test.js.map +1 -0
  27. package/dist/test/reactor-write.test.d.ts +2 -0
  28. package/dist/test/reactor-write.test.d.ts.map +1 -0
  29. package/dist/test/reactor-write.test.js +232 -0
  30. package/dist/test/reactor-write.test.js.map +1 -0
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +3 -3
  33. package/dist/test/reactor.test.d.ts +0 -2
  34. package/dist/test/reactor.test.d.ts.map +0 -1
  35. package/dist/test/reactor.test.js +0 -445
  36. package/dist/test/reactor.test.js.map +0 -1
@@ -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
- constructor(driveServer) {
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.driveServer.getDocument(id);
87
- // this should be thrown by the storage layer
88
- if (!document) {
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.driveServer.getDocuments(id);
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
- // Get all drives
113
- const drives = await this.driveServer.getDrives();
114
- // Search through drives to find a document with the matching slug
115
- for (const driveId of drives) {
116
- if (signal?.aborted) {
117
- throw new AbortError();
118
- }
119
- const documentIds = await this.driveServer.getDocuments(driveId);
120
- for (const docId of documentIds) {
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
- throw new Error(`Document not found with slug: ${slug}`);
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
- const document = await this.driveServer.getDocument(documentId);
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
- // Empty search filter - return all documents
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 (error) {
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
- const jobId = uuidv4();
259
- try {
260
- // BaseDocumentDriveServer expects Operations, not Actions
261
- // We need to convert Actions to Operations
262
- const operations = actions.map((action, index) => ({
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 server
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
- // Apply operations to document
270
- await this.driveServer.addOperations(id, operations);
271
- // Return success job info
272
- // TODO: Phase 4 - This will return a job that goes through the queue
273
- return {
274
- id: jobId,
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
- const document = await this.driveServer.getDocument(id);
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
- // Fetch each document by slug
409
- for (const slug of slugs) {
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
- // Search through drives to find a document with the matching slug
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
- if (signal?.aborted) {
446
- throw new AbortError();
447
- }
448
- // Apply paging
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
- ? async () => this.findAll(view, { cursor: nextCursor, limit }, signal)
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 the drive server
514
- const childIds = await this.driveServer.getDocuments(parentId);
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
- const document = await this.driveServer.getDocument(childId);
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
- // Get all drives
567
- const drives = await this.driveServer.getDrives();
568
- for (const driveId of drives) {
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
- const documentIds = await this.driveServer.getDocuments(driveId);
573
- for (const docId of documentIds) {
574
- const document = await this.driveServer.getDocument(docId);
575
- if (document && document.header.documentType === type) {
576
- // Apply view filter
577
- for (const scope in document.state) {
578
- if (!matchesScope(view, scope)) {
579
- // eslint-disable-next-line
580
- delete document.state[scope];
581
- }
582
- }
583
- documents.push(document);
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
- // Apply paging
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: pagedDocuments,
599
- options: paging || { cursor: "0", limit: documents.length },
593
+ results: documents,
594
+ options: paging || { cursor: cursor || "0", limit },
600
595
  nextCursor,
601
- next: hasMore
596
+ next: nextCursor
602
597
  ? async () => this.findByType(type, view, { cursor: nextCursor, limit }, signal)
603
598
  : undefined,
604
599
  };