@powerhousedao/reactor 4.1.0-dev.11 → 4.1.0-dev.110

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