nvent 0.4.0

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 (192) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +389 -0
  3. package/dist/module.d.mts +193 -0
  4. package/dist/module.json +9 -0
  5. package/dist/module.mjs +974 -0
  6. package/dist/runtime/app/components/ConfirmDialog.d.vue.ts +33 -0
  7. package/dist/runtime/app/components/ConfirmDialog.vue +121 -0
  8. package/dist/runtime/app/components/ConfirmDialog.vue.d.ts +33 -0
  9. package/dist/runtime/app/components/FlowDiagram.d.vue.ts +64 -0
  10. package/dist/runtime/app/components/FlowDiagram.vue +338 -0
  11. package/dist/runtime/app/components/FlowDiagram.vue.d.ts +64 -0
  12. package/dist/runtime/app/components/FlowNodeCard.d.vue.ts +29 -0
  13. package/dist/runtime/app/components/FlowNodeCard.vue +156 -0
  14. package/dist/runtime/app/components/FlowNodeCard.vue.d.ts +29 -0
  15. package/dist/runtime/app/components/FlowRunOverview.d.vue.ts +9 -0
  16. package/dist/runtime/app/components/FlowRunOverview.vue +291 -0
  17. package/dist/runtime/app/components/FlowRunOverview.vue.d.ts +9 -0
  18. package/dist/runtime/app/components/FlowRunStatusBadge.d.vue.ts +14 -0
  19. package/dist/runtime/app/components/FlowRunStatusBadge.vue +60 -0
  20. package/dist/runtime/app/components/FlowRunStatusBadge.vue.d.ts +14 -0
  21. package/dist/runtime/app/components/FlowRunTimeline.d.vue.ts +12 -0
  22. package/dist/runtime/app/components/FlowRunTimeline.vue +127 -0
  23. package/dist/runtime/app/components/FlowRunTimeline.vue.d.ts +12 -0
  24. package/dist/runtime/app/components/FlowScheduleDialog.d.vue.ts +16 -0
  25. package/dist/runtime/app/components/FlowScheduleDialog.vue +226 -0
  26. package/dist/runtime/app/components/FlowScheduleDialog.vue.d.ts +16 -0
  27. package/dist/runtime/app/components/FlowSchedulesList.d.vue.ts +12 -0
  28. package/dist/runtime/app/components/FlowSchedulesList.vue +99 -0
  29. package/dist/runtime/app/components/FlowSchedulesList.vue.d.ts +12 -0
  30. package/dist/runtime/app/components/JobScheduling.d.vue.ts +6 -0
  31. package/dist/runtime/app/components/JobScheduling.vue +203 -0
  32. package/dist/runtime/app/components/JobScheduling.vue.d.ts +6 -0
  33. package/dist/runtime/app/components/ListItem.d.vue.ts +23 -0
  34. package/dist/runtime/app/components/ListItem.vue +70 -0
  35. package/dist/runtime/app/components/ListItem.vue.d.ts +23 -0
  36. package/dist/runtime/app/components/QueueConfigDetails.d.vue.ts +45 -0
  37. package/dist/runtime/app/components/QueueConfigDetails.vue +412 -0
  38. package/dist/runtime/app/components/QueueConfigDetails.vue.d.ts +45 -0
  39. package/dist/runtime/app/components/StatCounter.d.vue.ts +9 -0
  40. package/dist/runtime/app/components/StatCounter.vue +25 -0
  41. package/dist/runtime/app/components/StatCounter.vue.d.ts +9 -0
  42. package/dist/runtime/app/components/TimelineList.d.vue.ts +7 -0
  43. package/dist/runtime/app/components/TimelineList.vue +210 -0
  44. package/dist/runtime/app/components/TimelineList.vue.d.ts +7 -0
  45. package/dist/runtime/app/components/nhealth/component-router.d.vue.ts +46 -0
  46. package/dist/runtime/app/components/nhealth/component-router.vue +26 -0
  47. package/dist/runtime/app/components/nhealth/component-router.vue.d.ts +46 -0
  48. package/dist/runtime/app/components/nhealth/component-shell.d.vue.ts +24 -0
  49. package/dist/runtime/app/components/nhealth/component-shell.vue +89 -0
  50. package/dist/runtime/app/components/nhealth/component-shell.vue.d.ts +24 -0
  51. package/dist/runtime/app/composables/useAnalyzedFlows.d.ts +14 -0
  52. package/dist/runtime/app/composables/useAnalyzedFlows.js +7 -0
  53. package/dist/runtime/app/composables/useComponentRouter.d.ts +38 -0
  54. package/dist/runtime/app/composables/useComponentRouter.js +240 -0
  55. package/dist/runtime/app/composables/useFlowRunTimeline.d.ts +15 -0
  56. package/dist/runtime/app/composables/useFlowRunTimeline.js +66 -0
  57. package/dist/runtime/app/composables/useFlowRuns.d.ts +11 -0
  58. package/dist/runtime/app/composables/useFlowRuns.js +31 -0
  59. package/dist/runtime/app/composables/useFlowRunsInfinite.d.ts +24 -0
  60. package/dist/runtime/app/composables/useFlowRunsInfinite.js +123 -0
  61. package/dist/runtime/app/composables/useFlowRunsPolling.d.ts +8 -0
  62. package/dist/runtime/app/composables/useFlowRunsPolling.js +26 -0
  63. package/dist/runtime/app/composables/useFlowState.d.ts +125 -0
  64. package/dist/runtime/app/composables/useFlowState.js +211 -0
  65. package/dist/runtime/app/composables/useFlowWebSocket.d.ts +27 -0
  66. package/dist/runtime/app/composables/useFlowWebSocket.js +205 -0
  67. package/dist/runtime/app/composables/useFlowsNavigation.d.ts +10 -0
  68. package/dist/runtime/app/composables/useFlowsNavigation.js +57 -0
  69. package/dist/runtime/app/composables/useQueueJobs.d.ts +20 -0
  70. package/dist/runtime/app/composables/useQueueJobs.js +20 -0
  71. package/dist/runtime/app/composables/useQueueUpdates.d.ts +26 -0
  72. package/dist/runtime/app/composables/useQueueUpdates.js +122 -0
  73. package/dist/runtime/app/composables/useQueues.d.ts +43 -0
  74. package/dist/runtime/app/composables/useQueues.js +26 -0
  75. package/dist/runtime/app/composables/useQueuesLive.d.ts +19 -0
  76. package/dist/runtime/app/composables/useQueuesLive.js +143 -0
  77. package/dist/runtime/app/pages/flows/index.d.vue.ts +3 -0
  78. package/dist/runtime/app/pages/flows/index.vue +645 -0
  79. package/dist/runtime/app/pages/flows/index.vue.d.ts +3 -0
  80. package/dist/runtime/app/pages/index.d.vue.ts +3 -0
  81. package/dist/runtime/app/pages/index.vue +34 -0
  82. package/dist/runtime/app/pages/index.vue.d.ts +3 -0
  83. package/dist/runtime/app/pages/queues/index.d.vue.ts +3 -0
  84. package/dist/runtime/app/pages/queues/index.vue +229 -0
  85. package/dist/runtime/app/pages/queues/index.vue.d.ts +3 -0
  86. package/dist/runtime/app/pages/queues/job.d.vue.ts +3 -0
  87. package/dist/runtime/app/pages/queues/job.vue +262 -0
  88. package/dist/runtime/app/pages/queues/job.vue.d.ts +3 -0
  89. package/dist/runtime/app/pages/queues/jobs.d.vue.ts +3 -0
  90. package/dist/runtime/app/pages/queues/jobs.vue +291 -0
  91. package/dist/runtime/app/pages/queues/jobs.vue.d.ts +3 -0
  92. package/dist/runtime/app/plugins/vueflow.client.d.ts +6 -0
  93. package/dist/runtime/app/plugins/vueflow.client.js +15 -0
  94. package/dist/runtime/constants.d.ts +11 -0
  95. package/dist/runtime/constants.js +11 -0
  96. package/dist/runtime/python/get_config.py +64 -0
  97. package/dist/runtime/schema.d.ts +37 -0
  98. package/dist/runtime/schema.js +20 -0
  99. package/dist/runtime/server/api/_flows/[name]/clear-history.delete.d.ts +10 -0
  100. package/dist/runtime/server/api/_flows/[name]/clear-history.delete.js +44 -0
  101. package/dist/runtime/server/api/_flows/[name]/runs.get.d.ts +7 -0
  102. package/dist/runtime/server/api/_flows/[name]/runs.get.js +53 -0
  103. package/dist/runtime/server/api/_flows/[name]/schedule.post.d.ts +2 -0
  104. package/dist/runtime/server/api/_flows/[name]/schedule.post.js +57 -0
  105. package/dist/runtime/server/api/_flows/[name]/schedules/[id].delete.d.ts +2 -0
  106. package/dist/runtime/server/api/_flows/[name]/schedules/[id].delete.js +42 -0
  107. package/dist/runtime/server/api/_flows/[name]/schedules.get.d.ts +2 -0
  108. package/dist/runtime/server/api/_flows/[name]/schedules.get.js +48 -0
  109. package/dist/runtime/server/api/_flows/[name]/start.post.d.ts +2 -0
  110. package/dist/runtime/server/api/_flows/[name]/start.post.js +9 -0
  111. package/dist/runtime/server/api/_flows/index.get.d.ts +6 -0
  112. package/dist/runtime/server/api/_flows/index.get.js +5 -0
  113. package/dist/runtime/server/api/_flows/ws.d.ts +60 -0
  114. package/dist/runtime/server/api/_flows/ws.js +183 -0
  115. package/dist/runtime/server/api/_queues/[name]/job/[id].get.d.ts +2 -0
  116. package/dist/runtime/server/api/_queues/[name]/job/[id].get.js +9 -0
  117. package/dist/runtime/server/api/_queues/[name]/job/index.get.d.ts +2 -0
  118. package/dist/runtime/server/api/_queues/[name]/job/index.get.js +18 -0
  119. package/dist/runtime/server/api/_queues/index.get.d.ts +2 -0
  120. package/dist/runtime/server/api/_queues/index.get.js +63 -0
  121. package/dist/runtime/server/api/_queues/ws.d.ts +48 -0
  122. package/dist/runtime/server/api/_queues/ws.js +200 -0
  123. package/dist/runtime/server/events/adapters/fileAdapter.d.ts +2 -0
  124. package/dist/runtime/server/events/adapters/fileAdapter.js +382 -0
  125. package/dist/runtime/server/events/adapters/memoryAdapter.d.ts +2 -0
  126. package/dist/runtime/server/events/adapters/memoryAdapter.js +171 -0
  127. package/dist/runtime/server/events/adapters/redis/redisAdapter.d.ts +2 -0
  128. package/dist/runtime/server/events/adapters/redis/redisAdapter.js +348 -0
  129. package/dist/runtime/server/events/adapters/redis/redisPubSubGateway.d.ts +29 -0
  130. package/dist/runtime/server/events/adapters/redis/redisPubSubGateway.js +82 -0
  131. package/dist/runtime/server/events/eventBus.d.ts +20 -0
  132. package/dist/runtime/server/events/eventBus.js +35 -0
  133. package/dist/runtime/server/events/eventStoreFactory.d.ts +19 -0
  134. package/dist/runtime/server/events/eventStoreFactory.js +44 -0
  135. package/dist/runtime/server/events/streamNames.d.ts +17 -0
  136. package/dist/runtime/server/events/streamNames.js +17 -0
  137. package/dist/runtime/server/events/types.d.ts +63 -0
  138. package/dist/runtime/server/events/types.js +0 -0
  139. package/dist/runtime/server/events/wiring/flowWiring.d.ts +33 -0
  140. package/dist/runtime/server/events/wiring/flowWiring.js +406 -0
  141. package/dist/runtime/server/events/wiring/registry.d.ts +10 -0
  142. package/dist/runtime/server/events/wiring/registry.js +24 -0
  143. package/dist/runtime/server/plugins/00.event-store.d.ts +13 -0
  144. package/dist/runtime/server/plugins/00.event-store.js +16 -0
  145. package/dist/runtime/server/plugins/00.ws-lifecycle.d.ts +5 -0
  146. package/dist/runtime/server/plugins/00.ws-lifecycle.js +66 -0
  147. package/dist/runtime/server/plugins/flow-management.d.ts +13 -0
  148. package/dist/runtime/server/plugins/flow-management.js +65 -0
  149. package/dist/runtime/server/plugins/queue-management.d.ts +2 -0
  150. package/dist/runtime/server/plugins/queue-management.js +27 -0
  151. package/dist/runtime/server/plugins/state-cleanup.d.ts +11 -0
  152. package/dist/runtime/server/plugins/state-cleanup.js +93 -0
  153. package/dist/runtime/server/plugins/worker-management.d.ts +2 -0
  154. package/dist/runtime/server/plugins/worker-management.js +33 -0
  155. package/dist/runtime/server/queue/adapters/bullmq.d.ts +17 -0
  156. package/dist/runtime/server/queue/adapters/bullmq.js +164 -0
  157. package/dist/runtime/server/queue/queueFactory.d.ts +3 -0
  158. package/dist/runtime/server/queue/queueFactory.js +10 -0
  159. package/dist/runtime/server/queue/types.d.ts +47 -0
  160. package/dist/runtime/server/queue/types.js +0 -0
  161. package/dist/runtime/server/state/adapters/redis.d.ts +2 -0
  162. package/dist/runtime/server/state/adapters/redis.js +42 -0
  163. package/dist/runtime/server/state/stateFactory.d.ts +3 -0
  164. package/dist/runtime/server/state/stateFactory.js +17 -0
  165. package/dist/runtime/server/state/types.d.ts +23 -0
  166. package/dist/runtime/server/state/types.js +0 -0
  167. package/dist/runtime/server/tsconfig.json +3 -0
  168. package/dist/runtime/server/utils/defineQueueConfig.d.ts +154 -0
  169. package/dist/runtime/server/utils/defineQueueConfig.js +2 -0
  170. package/dist/runtime/server/utils/defineQueueWorker.d.ts +10 -0
  171. package/dist/runtime/server/utils/defineQueueWorker.js +17 -0
  172. package/dist/runtime/server/utils/useEventManager.d.ts +15 -0
  173. package/dist/runtime/server/utils/useEventManager.js +26 -0
  174. package/dist/runtime/server/utils/useEventStore.d.ts +20 -0
  175. package/dist/runtime/server/utils/useEventStore.js +119 -0
  176. package/dist/runtime/server/utils/useFlowEngine.d.ts +9 -0
  177. package/dist/runtime/server/utils/useFlowEngine.js +44 -0
  178. package/dist/runtime/server/utils/useLogs.d.ts +41 -0
  179. package/dist/runtime/server/utils/useLogs.js +74 -0
  180. package/dist/runtime/server/utils/useQueue.d.ts +31 -0
  181. package/dist/runtime/server/utils/useQueue.js +24 -0
  182. package/dist/runtime/server/utils/useServerLogger.d.ts +42 -0
  183. package/dist/runtime/server/utils/useServerLogger.js +54 -0
  184. package/dist/runtime/server/utils/wsPeerManager.d.ts +34 -0
  185. package/dist/runtime/server/utils/wsPeerManager.js +23 -0
  186. package/dist/runtime/server/worker/adapter.d.ts +4 -0
  187. package/dist/runtime/server/worker/adapter.js +65 -0
  188. package/dist/runtime/server/worker/runner/node.d.ts +27 -0
  189. package/dist/runtime/server/worker/runner/node.js +196 -0
  190. package/dist/runtime/types.d.ts +132 -0
  191. package/dist/types.d.mts +3 -0
  192. package/package.json +75 -0
@@ -0,0 +1,200 @@
1
+ import {
2
+ defineWebSocketHandler,
3
+ useQueue,
4
+ registerWsPeer,
5
+ unregisterWsPeer,
6
+ useServerLogger
7
+ } from "#imports";
8
+ const peerContexts = /* @__PURE__ */ new WeakMap();
9
+ const logger = useServerLogger("api-queues-ws");
10
+ function safeSend(peer, data) {
11
+ try {
12
+ peer.send(JSON.stringify(data));
13
+ return true;
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
18
+ export default defineWebSocketHandler({
19
+ open(peer) {
20
+ logger.info("[ws:queues] client connected:", peer.id);
21
+ registerWsPeer(peer);
22
+ peerContexts.set(peer, {
23
+ subscriptions: /* @__PURE__ */ new Map()
24
+ });
25
+ safeSend(peer, {
26
+ type: "connected",
27
+ timestamp: Date.now()
28
+ });
29
+ },
30
+ async message(peer, message) {
31
+ const context = peerContexts.get(peer);
32
+ if (!context) {
33
+ logger.error("[ws:queues] no context for peer:", peer.id);
34
+ return;
35
+ }
36
+ let data;
37
+ try {
38
+ data = JSON.parse(message.text());
39
+ } catch {
40
+ safeSend(peer, {
41
+ type: "error",
42
+ message: "Invalid JSON"
43
+ });
44
+ return;
45
+ }
46
+ const { type, queueName } = data;
47
+ if (type === "subscribe") {
48
+ if (!queueName) {
49
+ safeSend(peer, {
50
+ type: "error",
51
+ message: "Missing queueName"
52
+ });
53
+ return;
54
+ }
55
+ const queue = useQueue();
56
+ const existingUnsub = context.subscriptions.get(queueName);
57
+ if (existingUnsub) {
58
+ try {
59
+ existingUnsub();
60
+ } catch (err) {
61
+ logger.error("[ws:queues] error unsubscribing:", err);
62
+ }
63
+ }
64
+ const events = [
65
+ "waiting",
66
+ "active",
67
+ "completed",
68
+ "failed",
69
+ "progress"
70
+ ];
71
+ const unsubs = events.map(
72
+ (eventType) => queue.on(queueName, eventType, async (payload) => {
73
+ safeSend(peer, {
74
+ type: "event",
75
+ queueName,
76
+ event: {
77
+ eventType,
78
+ ...payload
79
+ }
80
+ });
81
+ if (["waiting", "active", "completed", "failed"].includes(eventType)) {
82
+ try {
83
+ const counts = await queue.getJobCounts(queueName);
84
+ safeSend(peer, {
85
+ type: "counts",
86
+ queueName,
87
+ counts
88
+ });
89
+ } catch (err) {
90
+ logger.error("[ws:queues] error fetching counts after event:", err);
91
+ }
92
+ }
93
+ })
94
+ );
95
+ const unsub = () => {
96
+ for (const u of unsubs) {
97
+ try {
98
+ u();
99
+ } catch (err) {
100
+ logger.error("[ws:queues] error unsubscribing:", err);
101
+ }
102
+ }
103
+ };
104
+ context.subscriptions.set(queueName, unsub);
105
+ try {
106
+ const counts = await queue.getJobCounts(queueName);
107
+ safeSend(peer, {
108
+ type: "counts",
109
+ queueName,
110
+ counts
111
+ });
112
+ } catch (err) {
113
+ logger.error("[ws:queues] error fetching counts:", err);
114
+ }
115
+ safeSend(peer, {
116
+ type: "subscribed",
117
+ queueName
118
+ });
119
+ } else if (type === "unsubscribe") {
120
+ if (!queueName) {
121
+ safeSend(peer, {
122
+ type: "error",
123
+ message: "Missing queueName"
124
+ });
125
+ return;
126
+ }
127
+ const unsub = context.subscriptions.get(queueName);
128
+ if (unsub) {
129
+ try {
130
+ unsub();
131
+ context.subscriptions.delete(queueName);
132
+ safeSend(peer, {
133
+ type: "unsubscribed",
134
+ queueName
135
+ });
136
+ } catch (err) {
137
+ logger.error("[ws:queues] error unsubscribing:", err);
138
+ safeSend(peer, {
139
+ type: "error",
140
+ message: "Failed to unsubscribe"
141
+ });
142
+ }
143
+ }
144
+ } else if (type === "ping") {
145
+ safeSend(peer, {
146
+ type: "pong",
147
+ timestamp: Date.now()
148
+ });
149
+ } else {
150
+ safeSend(peer, {
151
+ type: "error",
152
+ message: `Unknown message type: ${type}`
153
+ });
154
+ }
155
+ },
156
+ close(peer, event) {
157
+ const isNormalClosure = event?.code === 1e3 || event?.code === 1001;
158
+ if (!isNormalClosure) {
159
+ logger.info("[ws:queues] client disconnected:", {
160
+ peerId: peer.id,
161
+ code: event?.code,
162
+ reason: event?.reason
163
+ });
164
+ }
165
+ unregisterWsPeer(peer);
166
+ const context = peerContexts.get(peer);
167
+ if (context) {
168
+ for (const unsub of context.subscriptions.values()) {
169
+ try {
170
+ unsub();
171
+ } catch (err) {
172
+ if (!isNormalClosure) {
173
+ logger.error("[ws:queues] error unsubscribing on close:", err);
174
+ }
175
+ }
176
+ }
177
+ context.subscriptions.clear();
178
+ peerContexts.delete(peer);
179
+ }
180
+ },
181
+ error(peer, error) {
182
+ logger.error("[ws:queues] error for peer:", {
183
+ peerId: peer.id,
184
+ error
185
+ });
186
+ unregisterWsPeer(peer);
187
+ const context = peerContexts.get(peer);
188
+ if (context) {
189
+ for (const unsub of context.subscriptions.values()) {
190
+ try {
191
+ unsub();
192
+ } catch (err) {
193
+ logger.error("[ws:queues] error unsubscribing on error:", { error: err });
194
+ }
195
+ }
196
+ context.subscriptions.clear();
197
+ peerContexts.delete(peer);
198
+ }
199
+ }
200
+ });
@@ -0,0 +1,2 @@
1
+ import type { EventStoreAdapter } from '../types.js';
2
+ export declare function createFileAdapter(): EventStoreAdapter;
@@ -0,0 +1,382 @@
1
+ import { promises as fsp } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { useRuntimeConfig } from "#imports";
4
+ function nowIso() {
5
+ return (/* @__PURE__ */ new Date()).toISOString();
6
+ }
7
+ function sanitize(name) {
8
+ return name.replace(/[^\w.-]/g, "_");
9
+ }
10
+ async function ensureDir(path) {
11
+ try {
12
+ await fsp.mkdir(path, { recursive: true });
13
+ } catch {
14
+ }
15
+ }
16
+ async function fileExists(path) {
17
+ try {
18
+ await fsp.access(path);
19
+ return true;
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
24
+ export function createFileAdapter() {
25
+ const rc = useRuntimeConfig();
26
+ const rootDir = rc?.queue?.rootDir || process.cwd();
27
+ const dirRoot = rc?.queue?.eventStore?.options?.file?.dir ? join(rootDir, rc.queue.eventStore.options.file.dir) : join(rootDir, ".data/nq-events");
28
+ const ext = rc?.queue?.eventStore?.options?.file?.ext || ".ndjson";
29
+ const pollMs = rc?.queue?.eventStore?.options?.file?.pollMs ?? 1e3;
30
+ const subscribers = /* @__PURE__ */ new Map();
31
+ const timers = /* @__PURE__ */ new Map();
32
+ const lastIds = /* @__PURE__ */ new Map();
33
+ const indices = /* @__PURE__ */ new Map();
34
+ const streamPath = (stream) => join(dirRoot, sanitize(stream) + ext);
35
+ const indexPath = (key) => join(dirRoot, "indices", sanitize(key) + ".json");
36
+ const readAll = async (stream) => {
37
+ const p = streamPath(stream);
38
+ if (!await fileExists(p)) return [];
39
+ const content = await fsp.readFile(p, "utf8");
40
+ const lines = content.split("\n").filter(Boolean);
41
+ const out = [];
42
+ for (const line of lines) {
43
+ try {
44
+ const rec = JSON.parse(line);
45
+ out.push(rec);
46
+ } catch {
47
+ }
48
+ }
49
+ return out;
50
+ };
51
+ const adapter = {
52
+ async append(stream, e) {
53
+ const id = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
54
+ const rec = { ...e, id, ts: nowIso() };
55
+ const p = streamPath(stream);
56
+ await ensureDir(dirname(p));
57
+ await fsp.appendFile(p, JSON.stringify(rec) + "\n", { encoding: "utf8" });
58
+ const set = subscribers.get(stream);
59
+ if (set) {
60
+ for (const cb of set) {
61
+ try {
62
+ cb(rec);
63
+ } catch {
64
+ }
65
+ }
66
+ }
67
+ return rec;
68
+ },
69
+ async read(stream, opts) {
70
+ const list = await readAll(stream);
71
+ const dir = opts?.direction || "forward";
72
+ if (dir === "backward") {
73
+ let end = list.length;
74
+ if (opts?.fromId) {
75
+ const idx2 = list.findIndex((e) => e.id === opts.fromId);
76
+ end = idx2 >= 0 ? idx2 : list.length;
77
+ }
78
+ const count = opts?.limit && opts.limit > 0 ? opts.limit : end;
79
+ const start = Math.max(0, end - count);
80
+ const slice = list.slice(start, end);
81
+ return slice.reverse();
82
+ }
83
+ if (!opts?.fromId) {
84
+ if (opts?.limit && opts.limit > 0) return list.slice(0, opts.limit);
85
+ return list;
86
+ }
87
+ const idx = list.findIndex((e) => e.id === opts.fromId);
88
+ const sliced = idx >= 0 ? list.slice(idx + 1) : list;
89
+ if (opts?.limit && opts.limit > 0) return sliced.slice(0, opts.limit);
90
+ return sliced;
91
+ },
92
+ async subscribe(stream, onEvent) {
93
+ let set = subscribers.get(stream);
94
+ if (!set) {
95
+ set = /* @__PURE__ */ new Set();
96
+ subscribers.set(stream, set);
97
+ }
98
+ set.add(onEvent);
99
+ if (!timers.has(stream)) {
100
+ const t = setInterval(async () => {
101
+ try {
102
+ const fromId = lastIds.get(stream);
103
+ const fresh = await adapter.read(stream, fromId ? { fromId } : void 0);
104
+ if (fresh.length) {
105
+ for (const rec of fresh) {
106
+ set.forEach((cb) => {
107
+ try {
108
+ cb(rec);
109
+ } catch {
110
+ }
111
+ });
112
+ lastIds.set(stream, rec.id);
113
+ }
114
+ }
115
+ } catch {
116
+ }
117
+ }, pollMs);
118
+ timers.set(stream, t);
119
+ }
120
+ return {
121
+ unsubscribe() {
122
+ const s = subscribers.get(stream);
123
+ if (s) s.delete(onEvent);
124
+ if (s && s.size === 0) {
125
+ subscribers.delete(stream);
126
+ const t = timers.get(stream);
127
+ if (t) {
128
+ try {
129
+ clearInterval(t);
130
+ } catch {
131
+ }
132
+ timers.delete(stream);
133
+ }
134
+ }
135
+ }
136
+ };
137
+ },
138
+ async deleteStream(subject) {
139
+ const p = streamPath(subject);
140
+ try {
141
+ await fsp.unlink(p);
142
+ } catch {
143
+ }
144
+ subscribers.delete(subject);
145
+ const t = timers.get(subject);
146
+ if (t) {
147
+ clearInterval(t);
148
+ timers.delete(subject);
149
+ }
150
+ lastIds.delete(subject);
151
+ },
152
+ async deleteByPattern(pattern) {
153
+ const regexPattern = pattern.replace(/\*/g, ".*").replace(/\?/g, ".");
154
+ const regex = new RegExp(`^${regexPattern}$`);
155
+ let count = 0;
156
+ try {
157
+ await ensureDir(dirRoot);
158
+ const files = await fsp.readdir(dirRoot);
159
+ for (const file of files) {
160
+ if (!file.endsWith(ext)) continue;
161
+ const subject = file.slice(0, -ext.length);
162
+ if (regex.test(subject)) {
163
+ const p = join(dirRoot, file);
164
+ await fsp.unlink(p);
165
+ subscribers.delete(subject);
166
+ const t = timers.get(subject);
167
+ if (t) {
168
+ clearInterval(t);
169
+ timers.delete(subject);
170
+ }
171
+ lastIds.delete(subject);
172
+ count++;
173
+ }
174
+ }
175
+ } catch {
176
+ }
177
+ return count;
178
+ },
179
+ async deleteIndex(key) {
180
+ indices.delete(key);
181
+ const p = indexPath(key);
182
+ try {
183
+ await fsp.unlink(p);
184
+ } catch {
185
+ }
186
+ },
187
+ async indexAdd(key, id, score, metadata) {
188
+ let data = indices.get(key);
189
+ if (!data) {
190
+ const p2 = indexPath(key);
191
+ if (await fileExists(p2)) {
192
+ try {
193
+ const content = await fsp.readFile(p2, "utf8");
194
+ data = JSON.parse(content);
195
+ } catch {
196
+ data = [];
197
+ }
198
+ } else {
199
+ data = [];
200
+ }
201
+ indices.set(key, data);
202
+ }
203
+ const existing = data.findIndex((entry) => entry.id === id);
204
+ if (existing >= 0) {
205
+ data[existing] = { ...data[existing], score, ...metadata };
206
+ } else {
207
+ data.push({ id, score, ...metadata });
208
+ }
209
+ const p = indexPath(key);
210
+ await ensureDir(dirname(p));
211
+ await fsp.writeFile(p, JSON.stringify(data, null, 2), "utf8");
212
+ },
213
+ async indexRead(key, opts) {
214
+ let data = indices.get(key);
215
+ if (!data) {
216
+ const p = indexPath(key);
217
+ if (await fileExists(p)) {
218
+ try {
219
+ const content = await fsp.readFile(p, "utf8");
220
+ data = JSON.parse(content);
221
+ indices.set(key, data);
222
+ } catch {
223
+ data = [];
224
+ }
225
+ } else {
226
+ data = [];
227
+ }
228
+ }
229
+ const sorted = [...data].sort((a, b) => b.score - a.score);
230
+ const offset = opts?.offset || 0;
231
+ const limit = opts?.limit || 50;
232
+ return sorted.slice(offset, offset + limit).map((entry) => ({
233
+ id: entry.id,
234
+ score: entry.score,
235
+ metadata: {
236
+ status: entry.status,
237
+ startedAt: entry.startedAt,
238
+ completedAt: entry.completedAt,
239
+ stepCount: entry.stepCount,
240
+ completedSteps: entry.completedSteps,
241
+ emittedEvents: entry.emittedEvents
242
+ }
243
+ }));
244
+ },
245
+ async indexGet(key, id) {
246
+ let data = indices.get(key);
247
+ if (!data) {
248
+ const p = indexPath(key);
249
+ if (await fileExists(p)) {
250
+ try {
251
+ const content = await fsp.readFile(p, "utf8");
252
+ data = JSON.parse(content);
253
+ indices.set(key, data);
254
+ } catch {
255
+ data = [];
256
+ }
257
+ } else {
258
+ data = [];
259
+ }
260
+ }
261
+ const entry = data.find((e) => e.id === id);
262
+ if (!entry) return null;
263
+ return {
264
+ id: entry.id,
265
+ score: entry.score,
266
+ metadata: {
267
+ status: entry.status,
268
+ startedAt: entry.startedAt,
269
+ completedAt: entry.completedAt,
270
+ stepCount: entry.stepCount,
271
+ completedSteps: entry.completedSteps,
272
+ emittedEvents: entry.emittedEvents
273
+ }
274
+ };
275
+ },
276
+ async indexUpdate(key, id, metadata) {
277
+ let data = indices.get(key);
278
+ if (!data) {
279
+ const p2 = indexPath(key);
280
+ if (await fileExists(p2)) {
281
+ try {
282
+ const content = await fsp.readFile(p2, "utf8");
283
+ data = JSON.parse(content);
284
+ } catch {
285
+ data = [];
286
+ }
287
+ } else {
288
+ data = [];
289
+ }
290
+ indices.set(key, data);
291
+ }
292
+ const entry = data.find((e) => e.id === id);
293
+ if (!entry) return false;
294
+ Object.assign(entry, metadata);
295
+ const p = indexPath(key);
296
+ await ensureDir(dirname(p));
297
+ await fsp.writeFile(p, JSON.stringify(data, null, 2), "utf8");
298
+ return true;
299
+ },
300
+ async indexUpdateWithRetry(key, id, metadata, maxRetries = 3) {
301
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
302
+ const success = await this.indexUpdate(key, id, metadata);
303
+ if (success) return;
304
+ await new Promise((resolve) => setTimeout(resolve, 10 * Math.pow(2, attempt)));
305
+ }
306
+ throw new Error(`Failed to update index after ${maxRetries} retries`);
307
+ },
308
+ async indexIncrement(key, id, field, increment = 1) {
309
+ let data = indices.get(key);
310
+ if (!data) {
311
+ const p2 = indexPath(key);
312
+ if (await fileExists(p2)) {
313
+ try {
314
+ const content = await fsp.readFile(p2, "utf8");
315
+ data = JSON.parse(content);
316
+ } catch {
317
+ data = [];
318
+ }
319
+ } else {
320
+ data = [];
321
+ }
322
+ indices.set(key, data);
323
+ }
324
+ const entry = data.find((e) => e.id === id);
325
+ if (!entry) return 0;
326
+ const currentValue = entry[field] || 0;
327
+ const newValue = currentValue + increment;
328
+ entry[field] = newValue;
329
+ const p = indexPath(key);
330
+ await ensureDir(dirname(p));
331
+ await fsp.writeFile(p, JSON.stringify(data, null, 2), "utf8");
332
+ return newValue;
333
+ },
334
+ async cleanupCompletedFlows(key, retentionSeconds) {
335
+ let data = indices.get(key);
336
+ if (!data) {
337
+ const p = indexPath(key);
338
+ if (await fileExists(p)) {
339
+ try {
340
+ const content = await fsp.readFile(p, "utf8");
341
+ data = JSON.parse(content);
342
+ } catch {
343
+ data = [];
344
+ }
345
+ } else {
346
+ data = [];
347
+ }
348
+ indices.set(key, data);
349
+ }
350
+ const now = Date.now();
351
+ const cutoffTime = now - retentionSeconds * 1e3;
352
+ const originalLength = data.length;
353
+ const filtered = data.filter((entry) => {
354
+ const isTerminal = entry.status === "completed" || entry.status === "failed";
355
+ const isOld = entry.completedAt ? entry.completedAt < cutoffTime : false;
356
+ return !(isTerminal && isOld);
357
+ });
358
+ const removedCount = originalLength - filtered.length;
359
+ if (removedCount > 0) {
360
+ indices.set(key, filtered);
361
+ const p = indexPath(key);
362
+ await ensureDir(dirname(p));
363
+ await fsp.writeFile(p, JSON.stringify(filtered, null, 2), "utf8");
364
+ }
365
+ return removedCount;
366
+ },
367
+ async setMetadataTTL(_key, _id, _ttlSeconds) {
368
+ },
369
+ async close() {
370
+ for (const t of timers.values()) {
371
+ try {
372
+ clearInterval(t);
373
+ } catch {
374
+ }
375
+ }
376
+ timers.clear();
377
+ subscribers.clear();
378
+ lastIds.clear();
379
+ }
380
+ };
381
+ return adapter;
382
+ }
@@ -0,0 +1,2 @@
1
+ import type { EventStoreAdapter } from '../types.js';
2
+ export declare function createMemoryAdapter(): EventStoreAdapter;