@powerhousedao/reactor 6.0.0-dev.4 → 6.0.0-dev.41

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