@powerhousedao/reactor 4.1.0-staging.1 → 5.0.0-staging.10

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 (147) hide show
  1. package/dist/bench/event-bus.bench.js +17 -27
  2. package/dist/bench/event-bus.bench.js.map +1 -1
  3. package/dist/bench/queue-only.bench.js +8 -2
  4. package/dist/bench/queue-only.bench.js.map +1 -1
  5. package/dist/bench/reactor-throughput.bench.js +42 -35
  6. package/dist/bench/reactor-throughput.bench.js.map +1 -1
  7. package/dist/src/events/event-bus.d.ts +3 -3
  8. package/dist/src/events/event-bus.d.ts.map +1 -1
  9. package/dist/src/events/event-bus.js.map +1 -1
  10. package/dist/src/events/interfaces.d.ts +1 -1
  11. package/dist/src/events/interfaces.d.ts.map +1 -1
  12. package/dist/src/events/types.d.ts +1 -1
  13. package/dist/src/events/types.d.ts.map +1 -1
  14. package/dist/src/events/types.js.map +1 -1
  15. package/dist/src/executor/interfaces.d.ts +31 -54
  16. package/dist/src/executor/interfaces.d.ts.map +1 -1
  17. package/dist/src/executor/simple-job-executor-manager.d.ts +27 -0
  18. package/dist/src/executor/simple-job-executor-manager.d.ts.map +1 -0
  19. package/dist/src/executor/simple-job-executor-manager.js +128 -0
  20. package/dist/src/executor/simple-job-executor-manager.js.map +1 -0
  21. package/dist/src/executor/simple-job-executor.d.ts +19 -0
  22. package/dist/src/executor/simple-job-executor.d.ts.map +1 -0
  23. package/dist/src/executor/simple-job-executor.js +69 -0
  24. package/dist/src/executor/simple-job-executor.js.map +1 -0
  25. package/dist/src/executor/types.d.ts +23 -8
  26. package/dist/src/executor/types.d.ts.map +1 -1
  27. package/dist/src/executor/types.js.map +1 -1
  28. package/dist/src/index.d.ts +9 -2
  29. package/dist/src/index.d.ts.map +1 -1
  30. package/dist/src/index.js +8 -2
  31. package/dist/src/index.js.map +1 -1
  32. package/dist/src/interfaces/reactor.d.ts +121 -0
  33. package/dist/src/interfaces/reactor.d.ts.map +1 -0
  34. package/dist/src/interfaces/reactor.js +2 -0
  35. package/dist/src/interfaces/reactor.js.map +1 -0
  36. package/dist/src/queue/interfaces.d.ts +45 -5
  37. package/dist/src/queue/interfaces.d.ts.map +1 -1
  38. package/dist/src/queue/job-execution-handle.d.ts +24 -0
  39. package/dist/src/queue/job-execution-handle.d.ts.map +1 -0
  40. package/dist/src/queue/job-execution-handle.js +62 -0
  41. package/dist/src/queue/job-execution-handle.js.map +1 -0
  42. package/dist/src/queue/queue.d.ts +54 -5
  43. package/dist/src/queue/queue.d.ts.map +1 -1
  44. package/dist/src/queue/queue.js +237 -23
  45. package/dist/src/queue/queue.js.map +1 -1
  46. package/dist/src/queue/types.d.ts +26 -1
  47. package/dist/src/queue/types.d.ts.map +1 -1
  48. package/dist/src/queue/types.js +12 -0
  49. package/dist/src/queue/types.js.map +1 -1
  50. package/dist/src/reactor.d.ts +100 -0
  51. package/dist/src/reactor.d.ts.map +1 -0
  52. package/dist/src/reactor.js +591 -0
  53. package/dist/src/reactor.js.map +1 -0
  54. package/dist/src/registry/implementation.d.ts +62 -0
  55. package/dist/src/registry/implementation.d.ts.map +1 -0
  56. package/dist/src/registry/implementation.js +96 -0
  57. package/dist/src/registry/implementation.js.map +1 -0
  58. package/dist/src/registry/index.d.ts +3 -0
  59. package/dist/src/registry/index.d.ts.map +1 -0
  60. package/dist/src/registry/index.js +2 -0
  61. package/dist/src/registry/index.js.map +1 -0
  62. package/dist/src/registry/interfaces.d.ts +39 -0
  63. package/dist/src/registry/interfaces.d.ts.map +1 -0
  64. package/dist/src/registry/interfaces.js +2 -0
  65. package/dist/src/registry/interfaces.js.map +1 -0
  66. package/dist/src/shared/factories.d.ts +16 -0
  67. package/dist/src/shared/factories.d.ts.map +1 -0
  68. package/dist/src/shared/factories.js +33 -0
  69. package/dist/src/shared/factories.js.map +1 -0
  70. package/dist/src/shared/types.d.ts +83 -19
  71. package/dist/src/shared/types.d.ts.map +1 -1
  72. package/dist/src/shared/types.js +30 -1
  73. package/dist/src/shared/types.js.map +1 -1
  74. package/dist/src/shared/utils.d.ts +3 -0
  75. package/dist/src/shared/utils.d.ts.map +1 -0
  76. package/dist/src/shared/utils.js +8 -0
  77. package/dist/src/shared/utils.js.map +1 -0
  78. package/dist/src/utils.d.ts +11 -0
  79. package/dist/src/utils.d.ts.map +1 -0
  80. package/dist/src/utils.js +31 -0
  81. package/dist/src/utils.js.map +1 -0
  82. package/dist/test/event-bus.test.js +19 -10
  83. package/dist/test/event-bus.test.js.map +1 -1
  84. package/dist/test/executor/executor-integration.test.d.ts +2 -0
  85. package/dist/test/executor/executor-integration.test.d.ts.map +1 -0
  86. package/dist/test/executor/executor-integration.test.js +287 -0
  87. package/dist/test/executor/executor-integration.test.js.map +1 -0
  88. package/dist/test/executor/job-execution-handle.test.d.ts +2 -0
  89. package/dist/test/executor/job-execution-handle.test.d.ts.map +1 -0
  90. package/dist/test/executor/job-execution-handle.test.js +272 -0
  91. package/dist/test/executor/job-execution-handle.test.js.map +1 -0
  92. package/dist/test/executor/simple-job-executor-manager.test.d.ts +2 -0
  93. package/dist/test/executor/simple-job-executor-manager.test.d.ts.map +1 -0
  94. package/dist/test/executor/simple-job-executor-manager.test.js +132 -0
  95. package/dist/test/executor/simple-job-executor-manager.test.js.map +1 -0
  96. package/dist/test/executor/simple-job-executor.test.d.ts +2 -0
  97. package/dist/test/executor/simple-job-executor.test.d.ts.map +1 -0
  98. package/dist/test/executor/simple-job-executor.test.js +139 -0
  99. package/dist/test/executor/simple-job-executor.test.js.map +1 -0
  100. package/dist/test/factories.d.ts +123 -0
  101. package/dist/test/factories.d.ts.map +1 -0
  102. package/dist/test/factories.js +319 -0
  103. package/dist/test/factories.js.map +1 -0
  104. package/dist/test/integration/document-drive-integration.test.d.ts +2 -0
  105. package/dist/test/integration/document-drive-integration.test.d.ts.map +1 -0
  106. package/dist/test/integration/document-drive-integration.test.js +1102 -0
  107. package/dist/test/integration/document-drive-integration.test.js.map +1 -0
  108. package/dist/test/integration/reactor-read.test.d.ts +2 -0
  109. package/dist/test/integration/reactor-read.test.d.ts.map +1 -0
  110. package/dist/test/integration/reactor-read.test.js +291 -0
  111. package/dist/test/integration/reactor-read.test.js.map +1 -0
  112. package/dist/test/queue/queue-integration.test.d.ts +2 -0
  113. package/dist/test/queue/queue-integration.test.d.ts.map +1 -0
  114. package/dist/test/queue/queue-integration.test.js +322 -0
  115. package/dist/test/queue/queue-integration.test.js.map +1 -0
  116. package/dist/test/{queue.test.d.ts.map → queue/queue.test.d.ts.map} +1 -1
  117. package/dist/test/queue/queue.test.js +770 -0
  118. package/dist/test/queue/queue.test.js.map +1 -0
  119. package/dist/test/registry/registry.test.d.ts +2 -0
  120. package/dist/test/registry/registry.test.d.ts.map +1 -0
  121. package/dist/test/registry/registry.test.js +182 -0
  122. package/dist/test/registry/registry.test.js.map +1 -0
  123. package/dist/test/utils.test.d.ts +2 -0
  124. package/dist/test/utils.test.d.ts.map +1 -0
  125. package/dist/test/utils.test.js +66 -0
  126. package/dist/test/utils.test.js.map +1 -0
  127. package/dist/tsconfig.tsbuildinfo +1 -1
  128. package/dist/vitest.config.d.ts +3 -0
  129. package/dist/vitest.config.d.ts.map +1 -0
  130. package/dist/vitest.config.js +11 -0
  131. package/dist/vitest.config.js.map +1 -0
  132. package/package.json +6 -1
  133. package/dist/bench/end-to-end-flow.bench.d.ts +0 -2
  134. package/dist/bench/end-to-end-flow.bench.d.ts.map +0 -1
  135. package/dist/bench/end-to-end-flow.bench.js +0 -256
  136. package/dist/bench/end-to-end-flow.bench.js.map +0 -1
  137. package/dist/src/executor/job-executor.d.ts +0 -62
  138. package/dist/src/executor/job-executor.d.ts.map +0 -1
  139. package/dist/src/executor/job-executor.js +0 -325
  140. package/dist/src/executor/job-executor.js.map +0 -1
  141. package/dist/test/job-executor.test.d.ts +0 -2
  142. package/dist/test/job-executor.test.d.ts.map +0 -1
  143. package/dist/test/job-executor.test.js +0 -581
  144. package/dist/test/job-executor.test.js.map +0 -1
  145. package/dist/test/queue.test.js +0 -396
  146. package/dist/test/queue.test.js.map +0 -1
  147. /package/dist/test/{queue.test.d.ts → queue/queue.test.d.ts} +0 -0
@@ -0,0 +1,100 @@
1
+ import type { BaseDocumentDriveServer } from "document-drive";
2
+ import type { IDocumentStorage } from "document-drive/storage/types";
3
+ import type { Action, DocumentModelState, Operation, PHDocument } from "document-model";
4
+ import type { IReactor } from "./interfaces/reactor.js";
5
+ import type { IQueue } from "./queue/interfaces.js";
6
+ import type { JobInfo, PagedResults, PagingOptions, PropagationMode, SearchFilter, ShutdownStatus, ViewFilter } from "./shared/types.js";
7
+ import { JobStatus } from "./shared/types.js";
8
+ /**
9
+ * The Reactor facade implementation.
10
+ *
11
+ * This class implements the IReactor interface and serves as the main entry point
12
+ * for the new Reactor architecture. In Phase 2 of the refactoring plan, it acts
13
+ * as a facade over the existing BaseDocumentDriveServer while we incrementally
14
+ * migrate to the new architecture.
15
+ *
16
+ * The facade pattern allows us to:
17
+ * 1. Present the new IReactor API to clients immediately
18
+ * 2. Internally delegate to the refactored BaseDocumentDriveServer (post Phase 1)
19
+ * 3. Incrementally replace internal implementations without breaking clients
20
+ * 4. Validate the new architecture alongside the existing system
21
+ */
22
+ export declare class Reactor implements IReactor {
23
+ private driveServer;
24
+ private documentStorage;
25
+ private shutdownStatus;
26
+ private setShutdown;
27
+ private queue;
28
+ constructor(driveServer: BaseDocumentDriveServer, documentStorage: IDocumentStorage, queue: IQueue);
29
+ /**
30
+ * Signals that the reactor should shutdown.
31
+ */
32
+ kill(): ShutdownStatus;
33
+ /**
34
+ * Retrieves a list of document model specifications
35
+ */
36
+ getDocumentModels(namespace?: string, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<DocumentModelState>>;
37
+ /**
38
+ * Retrieves a specific PHDocument by id
39
+ */
40
+ get<TDocument extends PHDocument>(id: string, view?: ViewFilter, signal?: AbortSignal): Promise<{
41
+ document: TDocument;
42
+ childIds: string[];
43
+ }>;
44
+ /**
45
+ * Retrieves a specific PHDocument by slug
46
+ */
47
+ getBySlug<TDocument extends PHDocument>(slug: string, view?: ViewFilter, signal?: AbortSignal): Promise<{
48
+ document: TDocument;
49
+ childIds: string[];
50
+ }>;
51
+ /**
52
+ * Retrieves the operations for a document
53
+ */
54
+ getOperations(documentId: string, view?: ViewFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<Record<string, PagedResults<Operation>>>;
55
+ /**
56
+ * Filters documents by criteria and returns a list of them
57
+ */
58
+ find(search: SearchFilter, view?: ViewFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<PHDocument>>;
59
+ /**
60
+ * Creates a document
61
+ */
62
+ create(document: PHDocument, signal?: AbortSignal): Promise<JobStatus>;
63
+ /**
64
+ * Deletes a document
65
+ */
66
+ deleteDocument(id: string, propagate?: PropagationMode, signal?: AbortSignal): Promise<JobInfo>;
67
+ /**
68
+ * Applies a list of actions to a document
69
+ */
70
+ mutate(id: string, actions: Action[]): Promise<JobInfo>;
71
+ /**
72
+ * Adds multiple documents as children to another
73
+ */
74
+ addChildren(parentId: string, documentIds: string[], view?: ViewFilter, signal?: AbortSignal): Promise<JobInfo>;
75
+ /**
76
+ * Removes multiple documents as children from another
77
+ */
78
+ removeChildren(parentId: string, documentIds: string[], view?: ViewFilter, signal?: AbortSignal): Promise<JobInfo>;
79
+ /**
80
+ * Retrieves the status of a job
81
+ */
82
+ getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo>;
83
+ /**
84
+ * Finds documents by their IDs
85
+ */
86
+ private findByIds;
87
+ /**
88
+ * Finds documents by their slugs
89
+ */
90
+ private findBySlugs;
91
+ /**
92
+ * Finds documents by parent ID
93
+ */
94
+ private findByParentId;
95
+ /**
96
+ * Finds documents by type
97
+ */
98
+ private findByType;
99
+ }
100
+ //# sourceMappingURL=reactor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactor.d.ts","sourceRoot":"","sources":["../../src/reactor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,KAAK,EACV,MAAM,EACN,kBAAkB,EAClB,SAAS,EAET,UAAU,EACX,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,aAAa,EACb,eAAe,EACf,YAAY,EACZ,cAAc,EACd,UAAU,EACX,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAI9C;;;;;;;;;;;;;GAaG;AACH,qBAAa,OAAQ,YAAW,QAAQ;IACtC,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,eAAe,CAAmB;IAC1C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,KAAK,CAAS;gBAGpB,WAAW,EAAE,uBAAuB,EACpC,eAAe,EAAE,gBAAgB,EACjC,KAAK,EAAE,MAAM;IAaf;;OAEG;IACH,IAAI,IAAI,cAAc;IAUtB;;OAEG;IACH,iBAAiB,CACf,SAAS,CAAC,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,aAAa,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC;IAsD5C;;OAEG;IACG,GAAG,CAAC,SAAS,SAAS,UAAU,EACpC,EAAE,EAAE,MAAM,EACV,IAAI,CAAC,EAAE,UAAU,EACjB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC;QACT,QAAQ,EAAE,SAAS,CAAC;QACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IA4BF;;OAEG;IACG,SAAS,CAAC,SAAS,SAAS,UAAU,EAC1C,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,UAAU,EACjB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC;QACT,QAAQ,EAAE,SAAS,CAAC;QACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IAsBF;;OAEG;IACG,aAAa,CACjB,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE,UAAU,EACjB,MAAM,CAAC,EAAE,aAAa,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC;IAmCnD;;OAEG;IACG,IAAI,CACR,MAAM,EAAE,YAAY,EACpB,IAAI,CAAC,EAAE,UAAU,EACjB,MAAM,CAAC,EAAE,aAAa,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;IAkDpC;;OAEG;IACG,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC;IAmB5E;;OAEG;IACG,cAAc,CAClB,EAAE,EAAE,MAAM,EACV,SAAS,CAAC,EAAE,eAAe,EAC3B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,OAAO,CAAC;IA6BnB;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAgC7D;;OAEG;IACG,WAAW,CACf,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAAE,EACrB,IAAI,CAAC,EAAE,UAAU,EACjB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,OAAO,CAAC;IAsDnB;;OAEG;IACG,cAAc,CAClB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAAE,EACrB,IAAI,CAAC,EAAE,UAAU,EACjB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,OAAO,CAAC;IAoCnB;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAcnE;;OAEG;YACW,SAAS;IA2DvB;;OAEG;YACW,WAAW;IA0EzB;;OAEG;YACW,cAAc;IAuE5B;;OAEG;YACW,UAAU;CA4DzB"}
@@ -0,0 +1,591 @@
1
+ import { AbortError } from "document-drive/utils/errors";
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 } from "./utils.js";
7
+ /**
8
+ * The Reactor facade implementation.
9
+ *
10
+ * This class implements the IReactor interface and serves as the main entry point
11
+ * for the new Reactor architecture. In Phase 2 of the refactoring plan, it acts
12
+ * as a facade over the existing BaseDocumentDriveServer while we incrementally
13
+ * migrate to the new architecture.
14
+ *
15
+ * The facade pattern allows us to:
16
+ * 1. Present the new IReactor API to clients immediately
17
+ * 2. Internally delegate to the refactored BaseDocumentDriveServer (post Phase 1)
18
+ * 3. Incrementally replace internal implementations without breaking clients
19
+ * 4. Validate the new architecture alongside the existing system
20
+ */
21
+ export class Reactor {
22
+ driveServer;
23
+ documentStorage;
24
+ shutdownStatus;
25
+ setShutdown;
26
+ queue;
27
+ constructor(driveServer, documentStorage, queue) {
28
+ // Store required dependencies
29
+ this.driveServer = driveServer;
30
+ this.documentStorage = documentStorage;
31
+ this.queue = queue;
32
+ // Create mutable shutdown status using factory method
33
+ const [status, setter] = createMutableShutdownStatus(false);
34
+ this.shutdownStatus = status;
35
+ this.setShutdown = setter;
36
+ }
37
+ /**
38
+ * Signals that the reactor should shutdown.
39
+ */
40
+ kill() {
41
+ // Mark the reactor as shutdown
42
+ this.setShutdown(true);
43
+ // TODO: Phase 3+ - Implement graceful shutdown for queue, executors, etc.
44
+ // For now, we just mark as shutdown and return status
45
+ return this.shutdownStatus;
46
+ }
47
+ /**
48
+ * Retrieves a list of document model specifications
49
+ */
50
+ getDocumentModels(namespace, paging, signal) {
51
+ // Get document model modules from the drive server
52
+ // Note: BaseDocumentDriveServer provides modules, not model states
53
+ // This is an adaptation layer that converts modules to states
54
+ const modules = this.driveServer.getDocumentModelModules();
55
+ // Convert modules to DocumentModelState format
56
+ // TODO: Proper conversion when DocumentModelState structure is finalized
57
+ const filteredModels = modules
58
+ .filter((module) => !namespace || module.documentModel.name.startsWith(namespace))
59
+ .map((module) => ({
60
+ id: module.documentModel.id,
61
+ name: module.documentModel.name,
62
+ extension: module.documentModel.extension,
63
+ author: module.documentModel.author,
64
+ description: module.documentModel.description || "",
65
+ specifications: module.documentModel.specifications,
66
+ }));
67
+ // Apply paging
68
+ const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
69
+ const limit = paging?.limit || filteredModels.length;
70
+ const pagedModels = filteredModels.slice(startIndex, startIndex + limit);
71
+ // Create paged results
72
+ const hasMore = startIndex + limit < filteredModels.length;
73
+ const nextCursor = hasMore ? String(startIndex + limit) : undefined;
74
+ // even thought this is currently synchronous, they could have passed in an already-aborted signal
75
+ if (signal?.aborted) {
76
+ throw new AbortError();
77
+ }
78
+ return Promise.resolve({
79
+ results: pagedModels,
80
+ options: paging || { cursor: "0", limit: filteredModels.length },
81
+ nextCursor,
82
+ next: hasMore
83
+ ? () => this.getDocumentModels(namespace, { cursor: nextCursor, limit }, signal)
84
+ : undefined,
85
+ });
86
+ }
87
+ /**
88
+ * Retrieves a specific PHDocument by id
89
+ */
90
+ async get(id, view, signal) {
91
+ const document = await this.documentStorage.get(id);
92
+ if (signal?.aborted) {
93
+ throw new AbortError();
94
+ }
95
+ const childIds = await this.documentStorage.getChildren(id);
96
+ if (signal?.aborted) {
97
+ throw new AbortError();
98
+ }
99
+ // Apply view filter - This will be removed when we pass the viewfilter along
100
+ // to the underlying store, but is here now for the interface.
101
+ for (const scope in document.state) {
102
+ if (!matchesScope(view, scope)) {
103
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
104
+ delete document.state[scope];
105
+ }
106
+ }
107
+ return {
108
+ document,
109
+ childIds,
110
+ };
111
+ }
112
+ /**
113
+ * Retrieves a specific PHDocument by slug
114
+ */
115
+ async getBySlug(slug, view, signal) {
116
+ // Use the storage layer to resolve slug to ID
117
+ let ids;
118
+ try {
119
+ ids = await this.documentStorage.resolveIds([slug], signal);
120
+ }
121
+ catch (error) {
122
+ // If the error is from resolveIds (document not found), wrap it with our message
123
+ if (error instanceof Error && error.message.includes("not found")) {
124
+ throw new Error(`Document not found with slug: ${slug}`);
125
+ }
126
+ throw error;
127
+ }
128
+ if (ids.length === 0 || !ids[0]) {
129
+ throw new Error(`Document not found with slug: ${slug}`);
130
+ }
131
+ // Now get the document by its resolved ID
132
+ return await this.get(ids[0], view, signal);
133
+ }
134
+ /**
135
+ * Retrieves the operations for a document
136
+ */
137
+ async getOperations(documentId, view, paging, signal) {
138
+ // Use storage directly to get the document
139
+ const document = await this.documentStorage.get(documentId);
140
+ if (signal?.aborted) {
141
+ throw new AbortError();
142
+ }
143
+ const operations = document.operations;
144
+ const result = {};
145
+ // apply view filter, per scope -- this will be removed when we pass the viewfilter along
146
+ // to the underlying store, but is here now for the interface.
147
+ for (const scope in operations) {
148
+ if (matchesScope(view, scope)) {
149
+ const scopeOperations = operations[scope];
150
+ // apply paging too
151
+ const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
152
+ const limit = paging?.limit || scopeOperations.length;
153
+ const pagedOperations = scopeOperations.slice(startIndex, startIndex + limit);
154
+ result[scope] = {
155
+ results: pagedOperations,
156
+ options: { cursor: String(startIndex + limit), limit },
157
+ };
158
+ }
159
+ }
160
+ return Promise.resolve(result);
161
+ }
162
+ /**
163
+ * Filters documents by criteria and returns a list of them
164
+ */
165
+ async find(search, view, paging, signal) {
166
+ let results;
167
+ if (search.ids) {
168
+ if (search.slugs && search.slugs.length > 0) {
169
+ throw new Error("Cannot use both ids and slugs in the same search");
170
+ }
171
+ results = await this.findByIds(search.ids, view, paging, signal);
172
+ if (search.parentId) {
173
+ results = filterByParentId(results, search.parentId);
174
+ }
175
+ if (search.type) {
176
+ results = filterByType(results, search.type);
177
+ }
178
+ }
179
+ else if (search.slugs) {
180
+ results = await this.findBySlugs(search.slugs, view, paging, signal);
181
+ if (search.parentId) {
182
+ results = filterByParentId(results, search.parentId);
183
+ }
184
+ if (search.type) {
185
+ results = filterByType(results, search.type);
186
+ }
187
+ }
188
+ else if (search.parentId) {
189
+ results = await this.findByParentId(search.parentId, view, paging, signal);
190
+ if (search.type) {
191
+ results = filterByType(results, search.type);
192
+ }
193
+ }
194
+ else if (search.type) {
195
+ results = await this.findByType(search.type, view, paging, signal);
196
+ }
197
+ else {
198
+ throw new Error("No search criteria provided");
199
+ }
200
+ if (signal?.aborted) {
201
+ throw new AbortError();
202
+ }
203
+ return results;
204
+ }
205
+ /**
206
+ * Creates a document
207
+ */
208
+ async create(document, signal) {
209
+ try {
210
+ // BaseDocumentDriveServer uses addDocument, not createDocument
211
+ // addDocument adds an existing document to a drive
212
+ await this.driveServer.addDocument(document);
213
+ }
214
+ catch {
215
+ // TODO: Phase 4 - This will return a job that can be retried
216
+ return JobStatus.FAILED;
217
+ }
218
+ if (signal?.aborted) {
219
+ throw new AbortError();
220
+ }
221
+ // Return success status
222
+ // TODO: Phase 4 - This will return a job that goes through the queue
223
+ return JobStatus.COMPLETED;
224
+ }
225
+ /**
226
+ * Deletes a document
227
+ */
228
+ async deleteDocument(id, propagate, signal) {
229
+ const jobId = uuidv4();
230
+ try {
231
+ // Delete document using drive server
232
+ await this.driveServer.deleteDocument(id);
233
+ // TODO: Implement cascade deletion when propagate mode is CASCADE
234
+ }
235
+ catch (error) {
236
+ // TODO: Phase 4 - This will return a job that can be retried
237
+ return {
238
+ id: jobId,
239
+ status: JobStatus.FAILED,
240
+ error: error instanceof Error ? error.message : "Unknown error",
241
+ };
242
+ }
243
+ if (signal?.aborted) {
244
+ throw new AbortError();
245
+ }
246
+ // Return success job info
247
+ // TODO: Phase 4 - This will return a job that goes through the queue
248
+ return {
249
+ id: jobId,
250
+ status: JobStatus.COMPLETED,
251
+ };
252
+ }
253
+ /**
254
+ * Applies a list of actions to a document
255
+ */
256
+ async mutate(id, actions) {
257
+ // Create jobs for each action/operation
258
+ const jobs = actions.map((action, index) => ({
259
+ id: uuidv4(),
260
+ documentId: id,
261
+ scope: action.scope || "global",
262
+ branch: "main", // Default to main branch
263
+ operation: {
264
+ index: index,
265
+ timestampUtcMs: String(action.timestampUtcMs || Date.now()),
266
+ hash: "", // Will be computed by the executor
267
+ skip: 0,
268
+ action: action,
269
+ },
270
+ createdAt: new Date().toISOString(),
271
+ queueHint: [],
272
+ maxRetries: 3,
273
+ }));
274
+ // Enqueue all jobs
275
+ for (const job of jobs) {
276
+ await this.queue.enqueue(job);
277
+ }
278
+ // Return job info for the batch (using the first job's ID as the batch ID)
279
+ const batchJobId = jobs.length > 0 ? jobs[0].id : uuidv4();
280
+ return {
281
+ id: batchJobId,
282
+ status: JobStatus.PENDING,
283
+ };
284
+ }
285
+ /**
286
+ * Adds multiple documents as children to another
287
+ */
288
+ async addChildren(parentId, documentIds, view, signal) {
289
+ const jobId = uuidv4();
290
+ // Check abort signal before starting
291
+ if (signal?.aborted) {
292
+ throw new AbortError();
293
+ }
294
+ // TODO: Implement when drive server supports hierarchical documents
295
+ // For now, this is a placeholder implementation
296
+ // Verify parent exists
297
+ try {
298
+ await this.driveServer.getDocument(parentId);
299
+ }
300
+ catch (error) {
301
+ return {
302
+ id: jobId,
303
+ status: JobStatus.FAILED,
304
+ error: error instanceof Error ? error.message : "Unknown error",
305
+ };
306
+ }
307
+ // Check abort signal after parent verification
308
+ if (signal?.aborted) {
309
+ throw new AbortError();
310
+ }
311
+ // Verify all children exist
312
+ for (const childId of documentIds) {
313
+ try {
314
+ await this.driveServer.getDocument(childId);
315
+ }
316
+ catch (error) {
317
+ return {
318
+ id: jobId,
319
+ status: JobStatus.FAILED,
320
+ error: error instanceof Error ? error.message : "Unknown error",
321
+ };
322
+ }
323
+ // Check abort signal after each child verification
324
+ if (signal?.aborted) {
325
+ throw new AbortError();
326
+ }
327
+ }
328
+ // TODO: Actually establish parent-child relationships
329
+ // Return success job info
330
+ return {
331
+ id: jobId,
332
+ status: JobStatus.COMPLETED,
333
+ };
334
+ }
335
+ /**
336
+ * Removes multiple documents as children from another
337
+ */
338
+ async removeChildren(parentId, documentIds, view, signal) {
339
+ const jobId = uuidv4();
340
+ // Check abort signal before starting
341
+ if (signal?.aborted) {
342
+ throw new AbortError();
343
+ }
344
+ // TODO: Implement when drive server supports hierarchical documents
345
+ // For now, this is a placeholder implementation
346
+ // Verify parent exists
347
+ try {
348
+ await this.driveServer.getDocument(parentId);
349
+ }
350
+ catch (error) {
351
+ return {
352
+ id: jobId,
353
+ status: JobStatus.FAILED,
354
+ error: error instanceof Error ? error.message : "Unknown error",
355
+ };
356
+ }
357
+ // Check abort signal after parent verification
358
+ if (signal?.aborted) {
359
+ throw new AbortError();
360
+ }
361
+ // TODO: Actually remove parent-child relationships
362
+ // Return success job info
363
+ return {
364
+ id: jobId,
365
+ status: JobStatus.COMPLETED,
366
+ };
367
+ }
368
+ /**
369
+ * Retrieves the status of a job
370
+ */
371
+ getJobStatus(jobId, signal) {
372
+ if (signal?.aborted) {
373
+ throw new AbortError();
374
+ }
375
+ // TODO: Phase 3 - Implement once IQueue and job tracking is in place
376
+ // For now, return a not found status
377
+ return Promise.resolve({
378
+ id: jobId,
379
+ status: JobStatus.FAILED,
380
+ error: "Job tracking not yet implemented",
381
+ });
382
+ }
383
+ /**
384
+ * Finds documents by their IDs
385
+ */
386
+ async findByIds(ids, view, paging, signal) {
387
+ const documents = [];
388
+ // Fetch each document by ID using storage directly
389
+ for (const id of ids) {
390
+ if (signal?.aborted) {
391
+ throw new AbortError();
392
+ }
393
+ let document;
394
+ try {
395
+ document = await this.documentStorage.get(id);
396
+ }
397
+ catch {
398
+ // Skip documents that don't exist or can't be accessed
399
+ // This matches the behavior expected from a search operation
400
+ continue;
401
+ }
402
+ // Apply view filter - This will be removed when we pass the viewfilter along
403
+ // to the underlying store, but is here now for the interface.
404
+ for (const scope in document.state) {
405
+ if (!matchesScope(view, scope)) {
406
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
407
+ delete document.state[scope];
408
+ }
409
+ }
410
+ documents.push(document);
411
+ }
412
+ if (signal?.aborted) {
413
+ throw new AbortError();
414
+ }
415
+ // Apply paging
416
+ const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
417
+ const limit = paging?.limit || documents.length;
418
+ const pagedDocuments = documents.slice(startIndex, startIndex + limit);
419
+ // Create paged results
420
+ const hasMore = startIndex + limit < documents.length;
421
+ const nextCursor = hasMore ? String(startIndex + limit) : undefined;
422
+ return {
423
+ results: pagedDocuments,
424
+ options: paging || { cursor: "0", limit: documents.length },
425
+ nextCursor,
426
+ next: hasMore
427
+ ? () => this.findByIds(ids, view, { cursor: nextCursor, limit }, signal)
428
+ : undefined,
429
+ };
430
+ }
431
+ /**
432
+ * Finds documents by their slugs
433
+ */
434
+ async findBySlugs(slugs, view, paging, signal) {
435
+ const documents = [];
436
+ // Use storage to resolve slugs to IDs
437
+ let ids;
438
+ try {
439
+ ids = await this.documentStorage.resolveIds(slugs, signal);
440
+ }
441
+ catch {
442
+ // If slug resolution fails, return empty results
443
+ // This matches the behavior expected from a search operation
444
+ ids = [];
445
+ }
446
+ // Fetch each document by resolved ID
447
+ for (const id of ids) {
448
+ if (signal?.aborted) {
449
+ throw new AbortError();
450
+ }
451
+ let document;
452
+ try {
453
+ document = await this.documentStorage.get(id);
454
+ }
455
+ catch {
456
+ // Skip documents that don't exist or can't be accessed
457
+ continue;
458
+ }
459
+ // Apply view filter - This will be removed when we pass the viewfilter along
460
+ // to the underlying store, but is here now for the interface.
461
+ for (const scope in document.state) {
462
+ if (!matchesScope(view, scope)) {
463
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
464
+ delete document.state[scope];
465
+ }
466
+ }
467
+ documents.push(document);
468
+ }
469
+ if (signal?.aborted) {
470
+ throw new AbortError();
471
+ }
472
+ // Apply paging - this will be removed when we pass the paging along
473
+ // to the underlying store, but is here now for the interface.
474
+ const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
475
+ const limit = paging?.limit || documents.length;
476
+ const pagedDocuments = documents.slice(startIndex, startIndex + limit);
477
+ // Create paged results
478
+ const hasMore = startIndex + limit < documents.length;
479
+ const nextCursor = hasMore ? String(startIndex + limit) : undefined;
480
+ return {
481
+ results: pagedDocuments,
482
+ options: paging || { cursor: "0", limit: documents.length },
483
+ nextCursor,
484
+ next: hasMore
485
+ ? () => this.findBySlugs(slugs, view, { cursor: nextCursor, limit }, signal)
486
+ : undefined,
487
+ };
488
+ }
489
+ /**
490
+ * Finds documents by parent ID
491
+ */
492
+ async findByParentId(parentId, view, paging, signal) {
493
+ // Get child document IDs from storage
494
+ const childIds = await this.documentStorage.getChildren(parentId);
495
+ if (signal?.aborted) {
496
+ throw new AbortError();
497
+ }
498
+ const documents = [];
499
+ // Fetch each child document
500
+ for (const childId of childIds) {
501
+ if (signal?.aborted) {
502
+ throw new AbortError();
503
+ }
504
+ let document;
505
+ try {
506
+ document = await this.documentStorage.get(childId);
507
+ }
508
+ catch {
509
+ // Skip documents that don't exist or can't be accessed
510
+ // This matches the behavior expected from a search operation
511
+ continue;
512
+ }
513
+ // Apply view filter - This will be removed when we pass the viewfilter along
514
+ // to the underlying store, but is here now for the interface.
515
+ for (const scope in document.state) {
516
+ if (!matchesScope(view, scope)) {
517
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
518
+ delete document.state[scope];
519
+ }
520
+ }
521
+ documents.push(document);
522
+ }
523
+ if (signal?.aborted) {
524
+ throw new AbortError();
525
+ }
526
+ // Apply paging
527
+ const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;
528
+ const limit = paging?.limit || documents.length;
529
+ const pagedDocuments = documents.slice(startIndex, startIndex + limit);
530
+ // Create paged results
531
+ const hasMore = startIndex + limit < documents.length;
532
+ const nextCursor = hasMore ? String(startIndex + limit) : undefined;
533
+ return {
534
+ results: pagedDocuments,
535
+ options: paging || { cursor: "0", limit: documents.length },
536
+ nextCursor,
537
+ next: hasMore
538
+ ? () => this.findByParentId(parentId, view, { cursor: nextCursor, limit }, signal)
539
+ : undefined,
540
+ };
541
+ }
542
+ /**
543
+ * Finds documents by type
544
+ */
545
+ async findByType(type, view, paging, signal) {
546
+ const documents = [];
547
+ // Use storage's findByType method directly
548
+ const cursor = paging?.cursor;
549
+ const limit = paging?.limit || 100;
550
+ // Get document IDs of the specified type
551
+ const { documents: documentIds, nextCursor } = await this.documentStorage.findByType(type, limit, cursor);
552
+ if (signal?.aborted) {
553
+ throw new AbortError();
554
+ }
555
+ // Fetch each document by its ID
556
+ for (const documentId of documentIds) {
557
+ if (signal?.aborted) {
558
+ throw new AbortError();
559
+ }
560
+ let document;
561
+ try {
562
+ document = await this.documentStorage.get(documentId);
563
+ }
564
+ catch {
565
+ // Skip documents that can't be retrieved
566
+ continue;
567
+ }
568
+ // Apply view filter
569
+ for (const scope in document.state) {
570
+ if (!matchesScope(view, scope)) {
571
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
572
+ delete document.state[scope];
573
+ }
574
+ }
575
+ documents.push(document);
576
+ }
577
+ if (signal?.aborted) {
578
+ throw new AbortError();
579
+ }
580
+ // Results are already paged from the storage layer
581
+ return {
582
+ results: documents,
583
+ options: paging || { cursor: cursor || "0", limit },
584
+ nextCursor,
585
+ next: nextCursor
586
+ ? async () => this.findByType(type, view, { cursor: nextCursor, limit }, signal)
587
+ : undefined,
588
+ };
589
+ }
590
+ }
591
+ //# sourceMappingURL=reactor.js.map