@powerhousedao/reactor 4.1.0-dev.101 → 4.1.0-dev.102

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 (56) hide show
  1. package/dist/src/cache/kysely-write-cache.d.ts +4 -3
  2. package/dist/src/cache/kysely-write-cache.d.ts.map +1 -1
  3. package/dist/src/cache/kysely-write-cache.js +12 -12
  4. package/dist/src/cache/kysely-write-cache.js.map +1 -1
  5. package/dist/src/client/reactor-client.d.ts.map +1 -1
  6. package/dist/src/client/reactor-client.js +3 -3
  7. package/dist/src/client/reactor-client.js.map +1 -1
  8. package/dist/src/core/reactor-builder.d.ts +5 -0
  9. package/dist/src/core/reactor-builder.d.ts.map +1 -1
  10. package/dist/src/core/reactor-builder.js +16 -4
  11. package/dist/src/core/reactor-builder.js.map +1 -1
  12. package/dist/src/core/reactor.d.ts +14 -9
  13. package/dist/src/core/reactor.d.ts.map +1 -1
  14. package/dist/src/core/reactor.js +471 -217
  15. package/dist/src/core/reactor.js.map +1 -1
  16. package/dist/src/core/types.d.ts +9 -5
  17. package/dist/src/core/types.d.ts.map +1 -1
  18. package/dist/src/executor/simple-job-executor-manager.d.ts.map +1 -1
  19. package/dist/src/executor/simple-job-executor-manager.js +3 -1
  20. package/dist/src/executor/simple-job-executor-manager.js.map +1 -1
  21. package/dist/src/executor/simple-job-executor.d.ts.map +1 -1
  22. package/dist/src/executor/simple-job-executor.js +1 -0
  23. package/dist/src/executor/simple-job-executor.js.map +1 -1
  24. package/dist/src/executor/util.d.ts +18 -0
  25. package/dist/src/executor/util.d.ts.map +1 -1
  26. package/dist/src/executor/util.js +41 -0
  27. package/dist/src/executor/util.js.map +1 -1
  28. package/dist/src/index.d.ts +6 -5
  29. package/dist/src/index.d.ts.map +1 -1
  30. package/dist/src/index.js +6 -4
  31. package/dist/src/index.js.map +1 -1
  32. package/dist/src/job-tracker/in-memory-job-tracker.d.ts +2 -2
  33. package/dist/src/job-tracker/in-memory-job-tracker.d.ts.map +1 -1
  34. package/dist/src/job-tracker/in-memory-job-tracker.js +7 -1
  35. package/dist/src/job-tracker/in-memory-job-tracker.js.map +1 -1
  36. package/dist/src/job-tracker/interfaces.d.ts +3 -2
  37. package/dist/src/job-tracker/interfaces.d.ts.map +1 -1
  38. package/dist/src/read-models/document-view.d.ts +10 -4
  39. package/dist/src/read-models/document-view.d.ts.map +1 -1
  40. package/dist/src/read-models/document-view.js +115 -3
  41. package/dist/src/read-models/document-view.js.map +1 -1
  42. package/dist/src/shared/consistency-tracker.d.ts +48 -0
  43. package/dist/src/shared/consistency-tracker.d.ts.map +1 -0
  44. package/dist/src/shared/consistency-tracker.js +123 -0
  45. package/dist/src/shared/consistency-tracker.js.map +1 -0
  46. package/dist/src/shared/types.d.ts +23 -0
  47. package/dist/src/shared/types.d.ts.map +1 -1
  48. package/dist/src/shared/types.js.map +1 -1
  49. package/dist/src/storage/interfaces.d.ts +67 -9
  50. package/dist/src/storage/interfaces.d.ts.map +1 -1
  51. package/dist/src/storage/interfaces.js.map +1 -1
  52. package/dist/src/storage/kysely/document-indexer.d.ts +13 -8
  53. package/dist/src/storage/kysely/document-indexer.d.ts.map +1 -1
  54. package/dist/src/storage/kysely/document-indexer.js +79 -10
  55. package/dist/src/storage/kysely/document-indexer.js.map +1 -1
  56. package/package.json +3 -3
@@ -16,13 +16,21 @@ export class Reactor {
16
16
  queue;
17
17
  jobTracker;
18
18
  readModelCoordinator;
19
- constructor(driveServer, documentStorage, queue, jobTracker, readModelCoordinator) {
19
+ features;
20
+ documentView;
21
+ documentIndexer;
22
+ operationStore;
23
+ constructor(driveServer, documentStorage, queue, jobTracker, readModelCoordinator, features, documentView, documentIndexer, operationStore) {
20
24
  // Store required dependencies
21
25
  this.driveServer = driveServer;
22
26
  this.documentStorage = documentStorage;
23
27
  this.queue = queue;
24
28
  this.jobTracker = jobTracker;
25
29
  this.readModelCoordinator = readModelCoordinator;
30
+ this.features = features;
31
+ this.documentView = documentView;
32
+ this.documentIndexer = documentIndexer;
33
+ this.operationStore = operationStore;
26
34
  // Start the read model coordinator
27
35
  this.readModelCoordinator.start();
28
36
  // Create mutable shutdown status using factory method
@@ -72,87 +80,153 @@ export class Reactor {
72
80
  /**
73
81
  * Retrieves a specific PHDocument by id
74
82
  */
75
- async get(id, view, signal) {
76
- const document = await this.documentStorage.get(id);
77
- if (signal?.aborted) {
78
- throw new AbortError();
79
- }
80
- const childIds = await this.documentStorage.getChildren(id);
81
- if (signal?.aborted) {
82
- throw new AbortError();
83
+ async get(id, view, consistencyToken, signal) {
84
+ if (this.features.legacyStorageEnabled) {
85
+ const document = await this.documentStorage.get(id);
86
+ if (signal?.aborted) {
87
+ throw new AbortError();
88
+ }
89
+ const childIds = await this.documentStorage.getChildren(id);
90
+ if (signal?.aborted) {
91
+ throw new AbortError();
92
+ }
93
+ for (const scope in document.state) {
94
+ if (!matchesScope(view, scope)) {
95
+ delete document.state[scope];
96
+ }
97
+ }
98
+ return {
99
+ document,
100
+ childIds,
101
+ };
83
102
  }
84
- // Apply view filter - This will be removed when we pass the viewfilter along
85
- // to the underlying store, but is here now for the interface.
86
- for (const scope in document.state) {
87
- if (!matchesScope(view, scope)) {
88
- delete document.state[scope];
103
+ else {
104
+ const document = await this.documentView.get(id, view, consistencyToken, signal);
105
+ if (signal?.aborted) {
106
+ throw new AbortError();
107
+ }
108
+ const relationships = await this.documentIndexer.getOutgoing(id, ["child"], consistencyToken, signal);
109
+ if (signal?.aborted) {
110
+ throw new AbortError();
89
111
  }
112
+ const childIds = relationships.map((rel) => rel.targetId);
113
+ return {
114
+ document,
115
+ childIds,
116
+ };
90
117
  }
91
- return {
92
- document,
93
- childIds,
94
- };
95
118
  }
96
119
  /**
97
120
  * Retrieves a specific PHDocument by slug
98
121
  */
99
- async getBySlug(slug, view, signal) {
100
- // Use the storage layer to resolve slug to ID
101
- let ids;
102
- try {
103
- ids = await this.documentStorage.resolveIds([slug], signal);
104
- }
105
- catch (error) {
106
- // If the error is from resolveIds (document not found), wrap it with our message
107
- if (error instanceof Error && error.message.includes("not found")) {
122
+ async getBySlug(slug, view, consistencyToken, signal) {
123
+ if (this.features.legacyStorageEnabled) {
124
+ let ids;
125
+ try {
126
+ ids = await this.documentStorage.resolveIds([slug], signal);
127
+ }
128
+ catch (error) {
129
+ if (error instanceof Error && error.message.includes("not found")) {
130
+ throw new Error(`Document not found with slug: ${slug}`);
131
+ }
132
+ throw error;
133
+ }
134
+ if (ids.length === 0 || !ids[0]) {
108
135
  throw new Error(`Document not found with slug: ${slug}`);
109
136
  }
110
- throw error;
137
+ return await this.get(ids[0], view, consistencyToken, signal);
111
138
  }
112
- if (ids.length === 0 || !ids[0]) {
113
- throw new Error(`Document not found with slug: ${slug}`);
139
+ else {
140
+ const documentId = await this.documentView.resolveSlug(slug, view, consistencyToken, signal);
141
+ if (!documentId) {
142
+ throw new Error(`Document not found with slug: ${slug}`);
143
+ }
144
+ return await this.get(documentId, view, consistencyToken, signal);
114
145
  }
115
- // Now get the document by its resolved ID
116
- return await this.get(ids[0], view, signal);
117
146
  }
118
147
  /**
119
148
  * Retrieves the operations for a document
120
149
  */
121
- async getOperations(documentId, view, paging, signal) {
122
- // Use storage directly to get the document
123
- const document = await this.documentStorage.get(documentId);
124
- if (signal?.aborted) {
125
- throw new AbortError();
150
+ async getOperations(documentId, view, paging, consistencyToken, signal) {
151
+ if (this.features.legacyStorageEnabled) {
152
+ // Use storage directly to get the document
153
+ const document = await this.documentStorage.get(documentId);
154
+ if (signal?.aborted) {
155
+ throw new AbortError();
156
+ }
157
+ const operations = document.operations;
158
+ const result = {};
159
+ // apply view filter, per scope -- this will be removed when we pass the viewfilter along
160
+ // to the underlying store, but is here now for the interface.
161
+ for (const scope in operations) {
162
+ if (matchesScope(view, scope)) {
163
+ const scopeOperations = operations[scope];
164
+ // apply paging too
165
+ const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
166
+ const limit = paging?.limit || scopeOperations.length;
167
+ const pagedOperations = scopeOperations.slice(startIndex, startIndex + limit);
168
+ result[scope] = {
169
+ results: pagedOperations,
170
+ options: { cursor: String(startIndex + limit), limit },
171
+ };
172
+ }
173
+ }
174
+ return Promise.resolve(result);
126
175
  }
127
- const operations = document.operations;
128
- const result = {};
129
- // apply view filter, per scope -- this will be removed when we pass the viewfilter along
130
- // to the underlying store, but is here now for the interface.
131
- for (const scope in operations) {
132
- if (matchesScope(view, scope)) {
133
- const scopeOperations = operations[scope];
134
- // apply paging too
135
- const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
136
- const limit = paging?.limit || scopeOperations.length;
137
- const pagedOperations = scopeOperations.slice(startIndex, startIndex + limit);
176
+ else {
177
+ // Use operation store to get operations
178
+ const branch = view?.branch || "main";
179
+ // Get all scopes for this document
180
+ const revisions = await this.operationStore.getRevisions(documentId, branch, signal);
181
+ if (signal?.aborted) {
182
+ throw new AbortError();
183
+ }
184
+ const allScopes = Object.keys(revisions.revision);
185
+ const result = {};
186
+ // Filter scopes based on view filter and query operations for each
187
+ for (const scope of allScopes) {
188
+ if (!matchesScope(view, scope)) {
189
+ continue;
190
+ }
191
+ if (signal?.aborted) {
192
+ throw new AbortError();
193
+ }
194
+ // Get operations for this scope
195
+ const scopeResult = await this.operationStore.getSince(documentId, scope, branch, -1, paging, signal);
196
+ // Transform to Reactor's PagedResults format
197
+ const currentCursor = paging?.cursor ? parseInt(paging.cursor) : 0;
198
+ const currentLimit = paging?.limit || 100;
138
199
  result[scope] = {
139
- results: pagedOperations,
140
- options: { cursor: String(startIndex + limit), limit },
200
+ results: scopeResult.items,
201
+ options: {
202
+ cursor: scopeResult.nextCursor || String(currentCursor),
203
+ limit: currentLimit,
204
+ },
205
+ nextCursor: scopeResult.nextCursor,
206
+ next: scopeResult.hasMore
207
+ ? async () => {
208
+ const nextPage = await this.getOperations(documentId, view, {
209
+ cursor: scopeResult.nextCursor,
210
+ limit: currentLimit,
211
+ }, consistencyToken, signal);
212
+ return nextPage[scope];
213
+ }
214
+ : undefined,
141
215
  };
142
216
  }
217
+ return result;
143
218
  }
144
- return Promise.resolve(result);
145
219
  }
146
220
  /**
147
221
  * Filters documents by criteria and returns a list of them
148
222
  */
149
- async find(search, view, paging, signal) {
223
+ async find(search, view, paging, consistencyToken, signal) {
150
224
  let results;
151
225
  if (search.ids) {
152
226
  if (search.slugs && search.slugs.length > 0) {
153
227
  throw new Error("Cannot use both ids and slugs in the same search");
154
228
  }
155
- results = await this.findByIds(search.ids, view, paging, signal);
229
+ results = await this.findByIds(search.ids, view, paging, consistencyToken, signal);
156
230
  if (search.parentId) {
157
231
  results = filterByParentId(results, search.parentId);
158
232
  }
@@ -161,7 +235,7 @@ export class Reactor {
161
235
  }
162
236
  }
163
237
  else if (search.slugs) {
164
- results = await this.findBySlugs(search.slugs, view, paging, signal);
238
+ results = await this.findBySlugs(search.slugs, view, paging, consistencyToken, signal);
165
239
  if (search.parentId) {
166
240
  results = filterByParentId(results, search.parentId);
167
241
  }
@@ -176,7 +250,7 @@ export class Reactor {
176
250
  }
177
251
  }
178
252
  else if (search.type) {
179
- results = await this.findByType(search.type, view, paging, signal);
253
+ results = await this.findByType(search.type, view, paging, consistencyToken, signal);
180
254
  }
181
255
  else {
182
256
  throw new Error("No search criteria provided");
@@ -252,6 +326,11 @@ export class Reactor {
252
326
  id: job.id,
253
327
  status: JobStatus.PENDING,
254
328
  createdAtUtcIso,
329
+ consistencyToken: {
330
+ version: 1,
331
+ createdAtUtcIso,
332
+ coordinates: [],
333
+ },
255
334
  };
256
335
  this.jobTracker.registerJob(jobInfo);
257
336
  // Enqueue the job
@@ -292,6 +371,11 @@ export class Reactor {
292
371
  id: job.id,
293
372
  status: JobStatus.PENDING,
294
373
  createdAtUtcIso,
374
+ consistencyToken: {
375
+ version: 1,
376
+ createdAtUtcIso,
377
+ coordinates: [],
378
+ },
295
379
  };
296
380
  this.jobTracker.registerJob(jobInfo);
297
381
  await this.queue.enqueue(job);
@@ -321,6 +405,11 @@ export class Reactor {
321
405
  id: job.id,
322
406
  status: JobStatus.PENDING,
323
407
  createdAtUtcIso,
408
+ consistencyToken: {
409
+ version: 1,
410
+ createdAtUtcIso,
411
+ coordinates: [],
412
+ },
324
413
  };
325
414
  this.jobTracker.registerJob(jobInfo);
326
415
  // Enqueue the job
@@ -350,6 +439,11 @@ export class Reactor {
350
439
  id: jobId,
351
440
  status: JobStatus.PENDING,
352
441
  createdAtUtcIso,
442
+ consistencyToken: {
443
+ version: 1,
444
+ createdAtUtcIso,
445
+ coordinates: [],
446
+ },
353
447
  };
354
448
  this.jobTracker.registerJob(jobInfo);
355
449
  jobInfos.set(jobPlan.key, jobInfo);
@@ -385,7 +479,7 @@ export class Reactor {
385
479
  try {
386
480
  await this.queue.remove(jobId);
387
481
  }
388
- catch (removeError) {
482
+ catch {
389
483
  // Ignore removal errors during cleanup
390
484
  }
391
485
  }
@@ -402,7 +496,7 @@ export class Reactor {
402
496
  /**
403
497
  * Adds multiple documents as children to another
404
498
  */
405
- async addChildren(parentId, documentIds, view, signal) {
499
+ async addChildren(parentId, documentIds, _view, signal) {
406
500
  if (signal?.aborted) {
407
501
  throw new AbortError();
408
502
  }
@@ -422,7 +516,7 @@ export class Reactor {
422
516
  /**
423
517
  * Removes multiple documents as children from another
424
518
  */
425
- async removeChildren(parentId, documentIds, view, signal) {
519
+ async removeChildren(parentId, documentIds, _view, signal) {
426
520
  if (signal?.aborted) {
427
521
  throw new AbortError();
428
522
  }
@@ -449,12 +543,18 @@ export class Reactor {
449
543
  const jobInfo = this.jobTracker.getJobStatus(jobId);
450
544
  if (!jobInfo) {
451
545
  // Job not found - return FAILED status with appropriate error
546
+ const now = new Date().toISOString();
452
547
  return Promise.resolve({
453
548
  id: jobId,
454
549
  status: JobStatus.FAILED,
455
- createdAtUtcIso: new Date().toISOString(),
456
- completedAtUtcIso: new Date().toISOString(),
550
+ createdAtUtcIso: now,
551
+ completedAtUtcIso: now,
457
552
  error: toErrorInfo("Job not found"),
553
+ consistencyToken: {
554
+ version: 1,
555
+ createdAtUtcIso: now,
556
+ coordinates: [],
557
+ },
458
558
  });
459
559
  }
460
560
  return Promise.resolve(jobInfo);
@@ -462,205 +562,359 @@ export class Reactor {
462
562
  /**
463
563
  * Finds documents by their IDs
464
564
  */
465
- async findByIds(ids, view, paging, signal) {
466
- const documents = [];
467
- // Fetch each document by ID using storage directly
468
- for (const id of ids) {
565
+ async findByIds(ids, view, paging, consistencyToken, signal) {
566
+ if (consistencyToken) {
567
+ await this.documentView.waitForConsistency(consistencyToken, undefined, signal);
568
+ }
569
+ if (this.features.legacyStorageEnabled) {
570
+ const documents = [];
571
+ // Fetch each document by ID using storage directly
572
+ for (const id of ids) {
573
+ if (signal?.aborted) {
574
+ throw new AbortError();
575
+ }
576
+ let document;
577
+ try {
578
+ document = await this.documentStorage.get(id);
579
+ }
580
+ catch {
581
+ // Skip documents that don't exist or can't be accessed
582
+ // This matches the behavior expected from a search operation
583
+ continue;
584
+ }
585
+ // Apply view filter - This will be removed when we pass the viewfilter along
586
+ // to the underlying store, but is here now for the interface.
587
+ for (const scope in document.state) {
588
+ if (!matchesScope(view, scope)) {
589
+ delete document.state[scope];
590
+ }
591
+ }
592
+ documents.push(document);
593
+ }
469
594
  if (signal?.aborted) {
470
595
  throw new AbortError();
471
596
  }
472
- let document;
473
- try {
474
- document = await this.documentStorage.get(id);
475
- }
476
- catch {
477
- // Skip documents that don't exist or can't be accessed
478
- // This matches the behavior expected from a search operation
479
- continue;
480
- }
481
- // Apply view filter - This will be removed when we pass the viewfilter along
482
- // to the underlying store, but is here now for the interface.
483
- for (const scope in document.state) {
484
- if (!matchesScope(view, scope)) {
485
- delete document.state[scope];
597
+ // Apply paging
598
+ const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
599
+ const limit = paging?.limit || documents.length;
600
+ const pagedDocuments = documents.slice(startIndex, startIndex + limit);
601
+ // Create paged results
602
+ const hasMore = startIndex + limit < documents.length;
603
+ const nextCursor = hasMore ? String(startIndex + limit) : undefined;
604
+ return {
605
+ results: pagedDocuments,
606
+ options: paging || { cursor: "0", limit: documents.length },
607
+ nextCursor,
608
+ next: hasMore
609
+ ? () => this.findByIds(ids, view, { cursor: nextCursor, limit }, undefined, signal)
610
+ : undefined,
611
+ };
612
+ }
613
+ else {
614
+ const documents = [];
615
+ // Fetch each document by ID using documentView
616
+ for (const id of ids) {
617
+ if (signal?.aborted) {
618
+ throw new AbortError();
619
+ }
620
+ try {
621
+ const document = await this.documentView.get(id, view, undefined, signal);
622
+ documents.push(document);
623
+ }
624
+ catch {
625
+ // Skip documents that don't exist or can't be accessed
626
+ continue;
486
627
  }
487
628
  }
488
- documents.push(document);
489
- }
490
- if (signal?.aborted) {
491
- throw new AbortError();
629
+ if (signal?.aborted) {
630
+ throw new AbortError();
631
+ }
632
+ // Apply paging
633
+ const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
634
+ const limit = paging?.limit || documents.length;
635
+ const pagedDocuments = documents.slice(startIndex, startIndex + limit);
636
+ // Create paged results
637
+ const hasMore = startIndex + limit < documents.length;
638
+ const nextCursor = hasMore ? String(startIndex + limit) : undefined;
639
+ return {
640
+ results: pagedDocuments,
641
+ options: paging || { cursor: "0", limit: documents.length },
642
+ nextCursor,
643
+ next: hasMore
644
+ ? () => this.findByIds(ids, view, { cursor: nextCursor, limit }, undefined, signal)
645
+ : undefined,
646
+ };
492
647
  }
493
- // Apply paging
494
- const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
495
- const limit = paging?.limit || documents.length;
496
- const pagedDocuments = documents.slice(startIndex, startIndex + limit);
497
- // Create paged results
498
- const hasMore = startIndex + limit < documents.length;
499
- const nextCursor = hasMore ? String(startIndex + limit) : undefined;
500
- return {
501
- results: pagedDocuments,
502
- options: paging || { cursor: "0", limit: documents.length },
503
- nextCursor,
504
- next: hasMore
505
- ? () => this.findByIds(ids, view, { cursor: nextCursor, limit }, signal)
506
- : undefined,
507
- };
508
648
  }
509
649
  /**
510
650
  * Finds documents by their slugs
511
651
  */
512
- async findBySlugs(slugs, view, paging, signal) {
513
- const documents = [];
514
- // Use storage to resolve slugs to IDs
515
- let ids;
516
- try {
517
- ids = await this.documentStorage.resolveIds(slugs, signal);
518
- }
519
- catch {
520
- // If slug resolution fails, return empty results
521
- // This matches the behavior expected from a search operation
522
- ids = [];
523
- }
524
- // Fetch each document by resolved ID
525
- for (const id of ids) {
526
- if (signal?.aborted) {
527
- throw new AbortError();
528
- }
529
- let document;
652
+ async findBySlugs(slugs, view, paging, consistencyToken, signal) {
653
+ if (consistencyToken) {
654
+ await this.documentView.waitForConsistency(consistencyToken, undefined, signal);
655
+ }
656
+ if (this.features.legacyStorageEnabled) {
657
+ const documents = [];
658
+ // Use storage to resolve slugs to IDs
659
+ let ids;
530
660
  try {
531
- document = await this.documentStorage.get(id);
661
+ ids = await this.documentStorage.resolveIds(slugs, signal);
532
662
  }
533
663
  catch {
534
- // Skip documents that don't exist or can't be accessed
535
- continue;
664
+ // If slug resolution fails, return empty results
665
+ // This matches the behavior expected from a search operation
666
+ ids = [];
536
667
  }
537
- // Apply view filter - This will be removed when we pass the viewfilter along
538
- // to the underlying store, but is here now for the interface.
539
- for (const scope in document.state) {
540
- if (!matchesScope(view, scope)) {
541
- delete document.state[scope];
668
+ // Fetch each document by resolved ID
669
+ for (const id of ids) {
670
+ if (signal?.aborted) {
671
+ throw new AbortError();
672
+ }
673
+ let document;
674
+ try {
675
+ document = await this.documentStorage.get(id);
676
+ }
677
+ catch {
678
+ // Skip documents that don't exist or can't be accessed
679
+ continue;
542
680
  }
681
+ // Apply view filter - This will be removed when we pass the viewfilter along
682
+ // to the underlying store, but is here now for the interface.
683
+ for (const scope in document.state) {
684
+ if (!matchesScope(view, scope)) {
685
+ delete document.state[scope];
686
+ }
687
+ }
688
+ documents.push(document);
689
+ }
690
+ if (signal?.aborted) {
691
+ throw new AbortError();
543
692
  }
544
- documents.push(document);
693
+ // Apply paging - this will be removed when we pass the paging along
694
+ // to the underlying store, but is here now for the interface.
695
+ const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
696
+ const limit = paging?.limit || documents.length;
697
+ const pagedDocuments = documents.slice(startIndex, startIndex + limit);
698
+ // Create paged results
699
+ const hasMore = startIndex + limit < documents.length;
700
+ const nextCursor = hasMore ? String(startIndex + limit) : undefined;
701
+ return {
702
+ results: pagedDocuments,
703
+ options: paging || { cursor: "0", limit: documents.length },
704
+ nextCursor,
705
+ next: hasMore
706
+ ? () => this.findBySlugs(slugs, view, { cursor: nextCursor, limit }, undefined, signal)
707
+ : undefined,
708
+ };
545
709
  }
546
- if (signal?.aborted) {
547
- throw new AbortError();
710
+ else {
711
+ const documents = [];
712
+ // Resolve each slug to a document ID
713
+ const documentIds = [];
714
+ for (const slug of slugs) {
715
+ if (signal?.aborted) {
716
+ throw new AbortError();
717
+ }
718
+ const documentId = await this.documentView.resolveSlug(slug, view, undefined, signal);
719
+ if (documentId) {
720
+ documentIds.push(documentId);
721
+ }
722
+ }
723
+ // Fetch each document
724
+ for (const documentId of documentIds) {
725
+ if (signal?.aborted) {
726
+ throw new AbortError();
727
+ }
728
+ try {
729
+ const document = await this.documentView.get(documentId, view, undefined, signal);
730
+ documents.push(document);
731
+ }
732
+ catch {
733
+ // Skip documents that don't exist or can't be accessed
734
+ continue;
735
+ }
736
+ }
737
+ if (signal?.aborted) {
738
+ throw new AbortError();
739
+ }
740
+ // Apply paging
741
+ const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
742
+ const limit = paging?.limit || documents.length;
743
+ const pagedDocuments = documents.slice(startIndex, startIndex + limit);
744
+ // Create paged results
745
+ const hasMore = startIndex + limit < documents.length;
746
+ const nextCursor = hasMore ? String(startIndex + limit) : undefined;
747
+ return {
748
+ results: pagedDocuments,
749
+ options: paging || { cursor: "0", limit: documents.length },
750
+ nextCursor,
751
+ next: hasMore
752
+ ? () => this.findBySlugs(slugs, view, { cursor: nextCursor, limit }, undefined, signal)
753
+ : undefined,
754
+ };
548
755
  }
549
- // Apply paging - this will be removed when we pass the paging along
550
- // to the underlying store, but is here now for the interface.
551
- const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
552
- const limit = paging?.limit || documents.length;
553
- const pagedDocuments = documents.slice(startIndex, startIndex + limit);
554
- // Create paged results
555
- const hasMore = startIndex + limit < documents.length;
556
- const nextCursor = hasMore ? String(startIndex + limit) : undefined;
557
- return {
558
- results: pagedDocuments,
559
- options: paging || { cursor: "0", limit: documents.length },
560
- nextCursor,
561
- next: hasMore
562
- ? () => this.findBySlugs(slugs, view, { cursor: nextCursor, limit }, signal)
563
- : undefined,
564
- };
565
756
  }
566
757
  /**
567
758
  * Finds documents by parent ID
568
759
  */
569
760
  async findByParentId(parentId, view, paging, signal) {
570
- // Get child document IDs from storage
571
- const childIds = await this.documentStorage.getChildren(parentId);
572
- if (signal?.aborted) {
573
- throw new AbortError();
574
- }
575
- const documents = [];
576
- // Fetch each child document
577
- for (const childId of childIds) {
761
+ if (this.features.legacyStorageEnabled) {
762
+ // Get child document IDs from storage
763
+ const childIds = await this.documentStorage.getChildren(parentId);
578
764
  if (signal?.aborted) {
579
765
  throw new AbortError();
580
766
  }
581
- let document;
582
- try {
583
- document = await this.documentStorage.get(childId);
767
+ const documents = [];
768
+ // Fetch each child document
769
+ for (const childId of childIds) {
770
+ if (signal?.aborted) {
771
+ throw new AbortError();
772
+ }
773
+ let document;
774
+ try {
775
+ document = await this.documentStorage.get(childId);
776
+ }
777
+ catch {
778
+ // Skip documents that don't exist or can't be accessed
779
+ // This matches the behavior expected from a search operation
780
+ continue;
781
+ }
782
+ // Apply view filter - This will be removed when we pass the viewfilter along
783
+ // to the underlying store, but is here now for the interface.
784
+ for (const scope in document.state) {
785
+ if (!matchesScope(view, scope)) {
786
+ delete document.state[scope];
787
+ }
788
+ }
789
+ documents.push(document);
584
790
  }
585
- catch {
586
- // Skip documents that don't exist or can't be accessed
587
- // This matches the behavior expected from a search operation
588
- continue;
791
+ if (signal?.aborted) {
792
+ throw new AbortError();
589
793
  }
590
- // Apply view filter - This will be removed when we pass the viewfilter along
591
- // to the underlying store, but is here now for the interface.
592
- for (const scope in document.state) {
593
- if (!matchesScope(view, scope)) {
594
- delete document.state[scope];
794
+ // Apply paging
795
+ const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
796
+ const limit = paging?.limit || documents.length;
797
+ const pagedDocuments = documents.slice(startIndex, startIndex + limit);
798
+ // Create paged results
799
+ const hasMore = startIndex + limit < documents.length;
800
+ const nextCursor = hasMore ? String(startIndex + limit) : undefined;
801
+ return {
802
+ results: pagedDocuments,
803
+ options: paging || { cursor: "0", limit: documents.length },
804
+ nextCursor,
805
+ next: hasMore
806
+ ? () => this.findByParentId(parentId, view, { cursor: nextCursor, limit }, signal)
807
+ : undefined,
808
+ };
809
+ }
810
+ else {
811
+ // Get child relationships from indexer
812
+ const relationships = await this.documentIndexer.getOutgoing(parentId, ["child"], undefined, signal);
813
+ if (signal?.aborted) {
814
+ throw new AbortError();
815
+ }
816
+ const documents = [];
817
+ // Fetch each child document
818
+ for (const relationship of relationships) {
819
+ if (signal?.aborted) {
820
+ throw new AbortError();
821
+ }
822
+ try {
823
+ const document = await this.documentView.get(relationship.targetId, view, undefined, signal);
824
+ documents.push(document);
825
+ }
826
+ catch {
827
+ // Skip documents that don't exist or can't be accessed
828
+ continue;
595
829
  }
596
830
  }
597
- documents.push(document);
598
- }
599
- if (signal?.aborted) {
600
- throw new AbortError();
831
+ if (signal?.aborted) {
832
+ throw new AbortError();
833
+ }
834
+ // Apply paging
835
+ const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
836
+ const limit = paging?.limit || documents.length;
837
+ const pagedDocuments = documents.slice(startIndex, startIndex + limit);
838
+ // Create paged results
839
+ const hasMore = startIndex + limit < documents.length;
840
+ const nextCursor = hasMore ? String(startIndex + limit) : undefined;
841
+ return {
842
+ results: pagedDocuments,
843
+ options: paging || { cursor: "0", limit: documents.length },
844
+ nextCursor,
845
+ next: hasMore
846
+ ? () => this.findByParentId(parentId, view, { cursor: nextCursor, limit }, signal)
847
+ : undefined,
848
+ };
601
849
  }
602
- // Apply paging
603
- const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
604
- const limit = paging?.limit || documents.length;
605
- const pagedDocuments = documents.slice(startIndex, startIndex + limit);
606
- // Create paged results
607
- const hasMore = startIndex + limit < documents.length;
608
- const nextCursor = hasMore ? String(startIndex + limit) : undefined;
609
- return {
610
- results: pagedDocuments,
611
- options: paging || { cursor: "0", limit: documents.length },
612
- nextCursor,
613
- next: hasMore
614
- ? () => this.findByParentId(parentId, view, { cursor: nextCursor, limit }, signal)
615
- : undefined,
616
- };
617
850
  }
618
851
  /**
619
852
  * Finds documents by type
620
853
  */
621
- async findByType(type, view, paging, signal) {
622
- const documents = [];
623
- // Use storage's findByType method directly
624
- const cursor = paging?.cursor;
625
- const limit = paging?.limit || 100;
626
- // Get document IDs of the specified type
627
- const { documents: documentIds, nextCursor } = await this.documentStorage.findByType(type, limit, cursor);
628
- if (signal?.aborted) {
629
- throw new AbortError();
630
- }
631
- // Fetch each document by its ID
632
- for (const documentId of documentIds) {
854
+ async findByType(type, view, paging, consistencyToken, signal) {
855
+ if (consistencyToken) {
856
+ await this.documentView.waitForConsistency(consistencyToken, undefined, signal);
857
+ }
858
+ if (this.features.legacyStorageEnabled) {
859
+ const documents = [];
860
+ // Use storage's findByType method directly
861
+ const cursor = paging?.cursor;
862
+ const limit = paging?.limit || 100;
863
+ // Get document IDs of the specified type
864
+ const { documents: documentIds, nextCursor } = await this.documentStorage.findByType(type, limit, cursor);
633
865
  if (signal?.aborted) {
634
866
  throw new AbortError();
635
867
  }
636
- let document;
637
- try {
638
- document = await this.documentStorage.get(documentId);
639
- }
640
- catch {
641
- // Skip documents that can't be retrieved
642
- continue;
643
- }
644
- // Apply view filter
645
- for (const scope in document.state) {
646
- if (!matchesScope(view, scope)) {
647
- delete document.state[scope];
868
+ // Fetch each document by its ID
869
+ for (const documentId of documentIds) {
870
+ if (signal?.aborted) {
871
+ throw new AbortError();
872
+ }
873
+ let document;
874
+ try {
875
+ document = await this.documentStorage.get(documentId);
648
876
  }
877
+ catch {
878
+ // Skip documents that can't be retrieved
879
+ continue;
880
+ }
881
+ // Apply view filter
882
+ for (const scope in document.state) {
883
+ if (!matchesScope(view, scope)) {
884
+ delete document.state[scope];
885
+ }
886
+ }
887
+ documents.push(document);
649
888
  }
650
- documents.push(document);
889
+ if (signal?.aborted) {
890
+ throw new AbortError();
891
+ }
892
+ // Results are already paged from the storage layer
893
+ return {
894
+ results: documents,
895
+ options: paging || { cursor: cursor || "0", limit },
896
+ nextCursor,
897
+ next: nextCursor
898
+ ? async () => this.findByType(type, view, { cursor: nextCursor, limit }, undefined, signal)
899
+ : undefined,
900
+ };
651
901
  }
652
- if (signal?.aborted) {
653
- throw new AbortError();
902
+ else {
903
+ const result = await this.documentView.findByType(type, view, paging, undefined, signal);
904
+ if (signal?.aborted) {
905
+ throw new AbortError();
906
+ }
907
+ const cursor = paging?.cursor;
908
+ const limit = paging?.limit || 100;
909
+ return {
910
+ results: result.items,
911
+ options: paging || { cursor: cursor || "0", limit },
912
+ nextCursor: result.nextCursor,
913
+ next: result.nextCursor
914
+ ? async () => this.findByType(type, view, { cursor: result.nextCursor, limit }, undefined, signal)
915
+ : undefined,
916
+ };
654
917
  }
655
- // Results are already paged from the storage layer
656
- return {
657
- results: documents,
658
- options: paging || { cursor: cursor || "0", limit },
659
- nextCursor,
660
- next: nextCursor
661
- ? async () => this.findByType(type, view, { cursor: nextCursor, limit }, signal)
662
- : undefined,
663
- };
664
918
  }
665
919
  }
666
920
  //# sourceMappingURL=reactor.js.map