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,19 @@
1
+ import { getStreamNames } from './streamNames.js';
2
+ import type { EventStoreAdapter } from './types.js';
3
+ export interface EventStoreInstance {
4
+ name: string;
5
+ append: EventStoreAdapter['append'];
6
+ read: EventStoreAdapter['read'];
7
+ subscribe: EventStoreAdapter['subscribe'];
8
+ }
9
+ export interface EventStoreFactory {
10
+ adapter: EventStoreAdapter;
11
+ names: ReturnType<typeof getStreamNames>;
12
+ stream(name: string): EventStoreInstance;
13
+ /** Idempotently start stream store wiring that persists ingress events and projections */
14
+ start(): void;
15
+ /** Stop wiring and release listeners */
16
+ stop(): void;
17
+ }
18
+ export declare function getEventStoreFactory(): EventStoreFactory;
19
+ export declare function setEventStoreFactory(f: EventStoreFactory): void;
@@ -0,0 +1,44 @@
1
+ import { useRuntimeConfig, useServerLogger } from "#imports";
2
+ import { getStreamNames } from "./streamNames.js";
3
+ import { createRedisAdapter } from "./adapters/redis/redisAdapter.js";
4
+ import { createMemoryAdapter } from "./adapters/memoryAdapter.js";
5
+ import { createFileAdapter } from "./adapters/fileAdapter.js";
6
+ import { createWiringRegistry } from "./wiring/registry.js";
7
+ const logger = useServerLogger("event-store-factory");
8
+ let cachedFactory = null;
9
+ export function getEventStoreFactory() {
10
+ if (cachedFactory) return cachedFactory;
11
+ const rc = useRuntimeConfig();
12
+ const adapter = rc?.queue?.eventStore?.adapter || "memory";
13
+ let adapterInstance;
14
+ if (adapter === "memory") adapterInstance = createMemoryAdapter();
15
+ else if (adapter === "file") adapterInstance = createFileAdapter();
16
+ else if (adapter === "redis") adapterInstance = createRedisAdapter();
17
+ else adapterInstance = createMemoryAdapter();
18
+ logger.debug("Event store initialized", { adapter, adapterType: adapter });
19
+ const names = getStreamNames();
20
+ const wiring = createWiringRegistry({ adapter: adapterInstance, names });
21
+ const factory = {
22
+ adapter: adapterInstance,
23
+ names,
24
+ stream(name) {
25
+ return {
26
+ name,
27
+ append: (s, e) => adapterInstance.append(s, e),
28
+ read: (s, o) => adapterInstance.read(s, o),
29
+ subscribe: (s, cb) => adapterInstance.subscribe(s, cb)
30
+ };
31
+ },
32
+ start() {
33
+ wiring.start();
34
+ },
35
+ stop() {
36
+ wiring.stop();
37
+ }
38
+ };
39
+ cachedFactory = factory;
40
+ return factory;
41
+ }
42
+ export function setEventStoreFactory(f) {
43
+ cachedFactory = f;
44
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Stream naming utilities
3
+ *
4
+ * Core streams:
5
+ * - nq:flow:{runId} - Flow run event stream
6
+ * - nq:flows:{flowName} - Sorted set index of flow runs by flow name
7
+ */
8
+ export interface StreamNamesConfig {
9
+ flow?: string | ((runId: string) => string);
10
+ flowIndex?: string | ((flowName: string) => string);
11
+ }
12
+ export interface StreamNames {
13
+ flow: (runId: string) => string;
14
+ flowIndex: (flowName: string) => string;
15
+ }
16
+ export declare function getStreamNames(): StreamNames;
17
+ export default getStreamNames;
@@ -0,0 +1,17 @@
1
+ import { useRuntimeConfig } from "#imports";
2
+ function defaults() {
3
+ return {
4
+ flow: (runId) => `nq:flow:${runId}`,
5
+ flowIndex: (flowName) => `nq:flows:${flowName}`
6
+ };
7
+ }
8
+ export function getStreamNames() {
9
+ const rc = useRuntimeConfig();
10
+ const cfg = rc?.queue?.eventStore?.streams || {};
11
+ const d = defaults();
12
+ return {
13
+ flow: typeof cfg.flow === "string" ? (runId) => `${cfg.flow}${runId}` : cfg.flow || d.flow,
14
+ flowIndex: typeof cfg.flowIndex === "string" ? (flowName) => `${cfg.flowIndex}${flowName}` : cfg.flowIndex || d.flowIndex
15
+ };
16
+ }
17
+ export default getStreamNames;
@@ -0,0 +1,63 @@
1
+ import type { EventRecord } from '../../types.js';
2
+ export interface PublishContext {
3
+ queue?: string;
4
+ jobId?: string;
5
+ /** Flow run ID for flow events */
6
+ flowId?: string;
7
+ /** Trigger ID for trigger events */
8
+ triggerId?: string;
9
+ }
10
+ export interface PublishPayload<T = any> {
11
+ type: string;
12
+ runId: string;
13
+ flowName: string;
14
+ stepName?: string;
15
+ stepId?: string;
16
+ attempt?: number;
17
+ data?: T;
18
+ }
19
+ export interface EventReadOptions {
20
+ fromId?: string;
21
+ limit?: number;
22
+ direction?: 'forward' | 'backward';
23
+ }
24
+ export interface EventSubscription {
25
+ unsubscribe(): void;
26
+ }
27
+ export interface IndexEntry {
28
+ id: string;
29
+ score: number;
30
+ metadata?: {
31
+ status?: 'running' | 'completed' | 'failed';
32
+ startedAt?: number;
33
+ completedAt?: number;
34
+ stepCount?: number;
35
+ completedSteps?: number;
36
+ emittedEvents?: string[];
37
+ version?: number;
38
+ };
39
+ }
40
+ export interface IndexReadOptions {
41
+ offset?: number;
42
+ limit?: number;
43
+ }
44
+ export interface EventStoreAdapter {
45
+ append(subject: string, e: Omit<EventRecord, 'id' | 'ts'>): Promise<EventRecord>;
46
+ read(subject: string, opts?: EventReadOptions): Promise<EventRecord[]>;
47
+ subscribe(subject: string, onEvent: (e: EventRecord) => void): Promise<EventSubscription>;
48
+ indexAdd?(key: string, id: string, score: number, metadata?: Record<string, any>): Promise<void>;
49
+ indexGet?(key: string, id: string): Promise<IndexEntry | null>;
50
+ indexUpdate?(key: string, id: string, metadata: Record<string, any>): Promise<boolean>;
51
+ indexUpdateWithRetry?(key: string, id: string, metadata: Record<string, any>, maxRetries?: number): Promise<void>;
52
+ indexIncrement?(key: string, id: string, field: string, increment?: number): Promise<number>;
53
+ indexRead?(key: string, opts?: IndexReadOptions): Promise<IndexEntry[]>;
54
+ setMetadataTTL?(flowName: string, runId: string, ttlSeconds: number): Promise<void>;
55
+ cleanupCompletedFlows?(key: string, retentionSeconds: number): Promise<number>;
56
+ /** Delete a specific stream/subject */
57
+ deleteStream?(subject: string): Promise<void>;
58
+ /** Delete all streams matching a pattern (e.g., 'flow:*' or 'trigger:webhook-*') */
59
+ deleteByPattern?(pattern: string): Promise<number>;
60
+ /** Delete an index key */
61
+ deleteIndex?(key: string): Promise<void>;
62
+ close(): Promise<void>;
63
+ }
File without changes
@@ -0,0 +1,33 @@
1
+ import type { EventStoreAdapter } from '../types.js';
2
+ import type { EventRecord } from '../../../types.js';
3
+ export interface FlowWiringDeps {
4
+ adapter: EventStoreAdapter;
5
+ }
6
+ /**
7
+ * Check if all dependencies for a step are met
8
+ * Returns true if all subscriptions have been emitted or completed
9
+ */
10
+ export declare function checkPendingStepTriggers(step: any, emittedEvents: Set<string>, completedSteps: Set<string>): boolean;
11
+ /**
12
+ * Analyze flow completion status from events
13
+ * Returns status, step counts, and timestamps
14
+ */
15
+ export declare function analyzeFlowCompletion(flowSteps: Record<string, any>, entryStep: string | undefined, events: EventRecord[]): {
16
+ status: 'running' | 'completed' | 'failed';
17
+ totalSteps: number;
18
+ completedSteps: number;
19
+ startedAt: number;
20
+ completedAt: number;
21
+ };
22
+ /**
23
+ * v0.4 Lean Flow Wiring
24
+ *
25
+ * 1. Persists flow events to streams using runId
26
+ * 2. Maintains a sorted set index using projection names for listing runs
27
+ *
28
+ * Events arrive as "ingress" (no id/ts) and are persisted to `nq:flow:{runId}` streams.
29
+ */
30
+ export declare function createFlowWiring(deps: FlowWiringDeps): {
31
+ start: () => void;
32
+ stop: () => void;
33
+ };
@@ -0,0 +1,406 @@
1
+ import { getEventBus } from "../eventBus.js";
2
+ import { useServerLogger, useEventStore, $useAnalyzedFlows, $useQueueRegistry, useQueue } from "#imports";
3
+ const logger = useServerLogger("flow-wiring");
4
+ export function checkPendingStepTriggers(step, emittedEvents, completedSteps) {
5
+ if (!step.subscribes || step.subscribes.length === 0) {
6
+ return true;
7
+ }
8
+ return step.subscribes.every((sub) => {
9
+ if (emittedEvents.has(sub)) return true;
10
+ const [prefix, value] = sub.split(":");
11
+ if (prefix === "step" && value) {
12
+ return completedSteps.has(value);
13
+ }
14
+ return false;
15
+ });
16
+ }
17
+ async function checkAndTriggerPendingSteps(flowName, runId, store) {
18
+ try {
19
+ const analyzedFlows = $useAnalyzedFlows();
20
+ const registry = $useQueueRegistry();
21
+ const { enqueue } = useQueue();
22
+ const flowDef = analyzedFlows.find((f) => f.id === flowName);
23
+ if (!flowDef?.steps) return;
24
+ const indexKey = store.names().flowIndex(flowName);
25
+ const flowEntry = await store.indexGet(indexKey, runId);
26
+ if (!flowEntry?.metadata) return;
27
+ const emittedEvents = new Set(flowEntry.metadata.emittedEvents || []);
28
+ const streamName = store.names().flow(runId);
29
+ const allEvents = await store.read(streamName);
30
+ const completedSteps = /* @__PURE__ */ new Set();
31
+ for (const event of allEvents) {
32
+ if (event.type === "step.completed" && "stepName" in event) {
33
+ completedSteps.add(event.stepName);
34
+ }
35
+ }
36
+ for (const [stepName, stepDef] of Object.entries(flowDef.steps)) {
37
+ const step = stepDef;
38
+ if (!step.subscribes || completedSteps.has(stepName)) continue;
39
+ const canTrigger = checkPendingStepTriggers(step, emittedEvents, completedSteps);
40
+ if (canTrigger) {
41
+ const flowRegistry = (registry?.flows || {})[flowName];
42
+ const stepMeta = flowRegistry?.steps?.[stepName];
43
+ if (stepMeta?.queue) {
44
+ const emitData = {};
45
+ const subscribes = step.subscribes || [];
46
+ for (const sub of subscribes) {
47
+ const emitEvent = allEvents.find(
48
+ (evt) => evt.type === "emit" && evt.data?.name === sub
49
+ );
50
+ if (emitEvent && emitEvent.data?.payload !== void 0) {
51
+ emitData[sub] = emitEvent.data.payload;
52
+ }
53
+ }
54
+ const payload = {
55
+ flowId: runId,
56
+ flowName,
57
+ input: emitData
58
+ // Keyed by event name
59
+ };
60
+ const jobId = `${runId}__${stepName}`;
61
+ try {
62
+ await enqueue(stepMeta.queue, { name: stepName, data: payload, opts: { jobId } });
63
+ logger.debug("Triggered pending step", {
64
+ flowName,
65
+ runId,
66
+ step: stepName,
67
+ subscribes: step.subscribes
68
+ });
69
+ } catch {
70
+ }
71
+ }
72
+ }
73
+ }
74
+ } catch (err) {
75
+ logger.warn("Failed to check pending steps", {
76
+ flowName,
77
+ runId,
78
+ error: err?.message
79
+ });
80
+ }
81
+ }
82
+ export function analyzeFlowCompletion(flowSteps, entryStep, events) {
83
+ const allSteps = entryStep ? [entryStep, ...Object.keys(flowSteps)] : Object.keys(flowSteps);
84
+ const completedSteps = /* @__PURE__ */ new Set();
85
+ const failedSteps = /* @__PURE__ */ new Set();
86
+ const retriedSteps = /* @__PURE__ */ new Map();
87
+ let startedAt = 0;
88
+ let completedAt = 0;
89
+ for (const event of events) {
90
+ if (event.type === "flow.start") {
91
+ startedAt = typeof event.ts === "string" ? new Date(event.ts).getTime() : 0;
92
+ }
93
+ if (event.type === "step.completed" && "stepName" in event) {
94
+ completedSteps.add(event.stepName);
95
+ }
96
+ if (event.type === "step.retry" && "stepName" in event) {
97
+ const currentAttempts = retriedSteps.get(event.stepName) || 0;
98
+ retriedSteps.set(event.stepName, currentAttempts + 1);
99
+ }
100
+ if (event.type === "step.failed" && "stepName" in event) {
101
+ failedSteps.add(event.stepName);
102
+ }
103
+ }
104
+ const finalFailedSteps = /* @__PURE__ */ new Set();
105
+ for (const stepName of failedSteps) {
106
+ let lastRetryIndex = -1;
107
+ let lastFailedIndex = -1;
108
+ for (let i = 0; i < events.length; i++) {
109
+ const event = events[i];
110
+ if ("stepName" in event && event.stepName === stepName) {
111
+ if (event.type === "step.retry") {
112
+ lastRetryIndex = i;
113
+ }
114
+ if (event.type === "step.failed") {
115
+ lastFailedIndex = i;
116
+ }
117
+ }
118
+ }
119
+ if (lastFailedIndex > lastRetryIndex) {
120
+ finalFailedSteps.add(stepName);
121
+ }
122
+ }
123
+ const totalSteps = allSteps.length;
124
+ const hasFinalFailures = finalFailedSteps.size > 0;
125
+ let hasBlockingFailure = false;
126
+ if (hasFinalFailures) {
127
+ for (const failedStepName of finalFailedSteps) {
128
+ const failedStepDef = flowSteps[failedStepName];
129
+ if (failedStepDef?.emits && failedStepDef.emits.length > 0) {
130
+ for (const [stepName, stepDef] of Object.entries(flowSteps)) {
131
+ const step = stepDef;
132
+ if (stepName === failedStepName) continue;
133
+ if (step.subscribes && step.subscribes.length > 0) {
134
+ const dependsOnFailedStep = step.subscribes.some((sub) => {
135
+ return failedStepDef.emits.some(
136
+ (emit) => sub === `${failedStepName}.${emit}` || sub === emit
137
+ );
138
+ });
139
+ if (dependsOnFailedStep && !completedSteps.has(stepName)) {
140
+ hasBlockingFailure = true;
141
+ break;
142
+ }
143
+ }
144
+ }
145
+ }
146
+ if (hasBlockingFailure) break;
147
+ }
148
+ }
149
+ if (hasBlockingFailure) {
150
+ return {
151
+ status: "failed",
152
+ totalSteps,
153
+ completedSteps: completedSteps.size,
154
+ startedAt,
155
+ completedAt: Date.now()
156
+ };
157
+ }
158
+ const allCompleted = allSteps.every(
159
+ (step) => completedSteps.has(step) || finalFailedSteps.has(step)
160
+ );
161
+ let status = "running";
162
+ if (allCompleted) {
163
+ status = "completed";
164
+ completedAt = Date.now();
165
+ }
166
+ return {
167
+ status,
168
+ totalSteps,
169
+ completedSteps: completedSteps.size,
170
+ startedAt,
171
+ completedAt
172
+ };
173
+ }
174
+ export function createFlowWiring(deps) {
175
+ const { adapter } = deps;
176
+ const bus = getEventBus();
177
+ const unsubs = [];
178
+ let wired = false;
179
+ const indexFlowRun = async (flowName, flowId, timestamp, metadata) => {
180
+ try {
181
+ const store = useEventStore();
182
+ const names = store.names();
183
+ const indexKey = names.flowIndex(flowName);
184
+ await store.indexAdd(indexKey, flowId, timestamp, metadata);
185
+ logger.debug("Indexed run", { flowName, flowId, indexKey, timestamp, metadata });
186
+ } catch (err) {
187
+ logger.error("Failed to index run", { error: err });
188
+ }
189
+ };
190
+ function start() {
191
+ if (wired) return;
192
+ wired = true;
193
+ const store = useEventStore();
194
+ const names = store.names();
195
+ const handlePersistence = async (e) => {
196
+ try {
197
+ if (e.id && e.ts) {
198
+ return;
199
+ }
200
+ const runId = e.runId;
201
+ if (!runId) {
202
+ return;
203
+ }
204
+ const flowName = e.flowName;
205
+ if (!flowName) {
206
+ return;
207
+ }
208
+ const streamName = names.flow(runId);
209
+ const eventData = {
210
+ type: e.type,
211
+ runId: e.runId,
212
+ flowName: e.flowName,
213
+ data: e.data
214
+ };
215
+ if ("stepName" in e && e.stepName) eventData.stepName = e.stepName;
216
+ if ("stepId" in e && e.stepId) eventData.stepId = e.stepId;
217
+ if ("attempt" in e && e.attempt) eventData.attempt = e.attempt;
218
+ await adapter.append(streamName, eventData);
219
+ if (e.type === "flow.completed" || e.type === "flow.failed") {
220
+ logger.info("Stored terminal event to stream", {
221
+ type: e.type,
222
+ flowName,
223
+ runId
224
+ });
225
+ } else {
226
+ logger.debug("Stored event to stream", {
227
+ type: e.type,
228
+ flowName,
229
+ runId,
230
+ stepName: "stepName" in e ? e.stepName : void 0
231
+ });
232
+ }
233
+ } catch (err) {
234
+ logger.error("ERROR persisting event", {
235
+ type: e.type,
236
+ runId: e.runId,
237
+ flowName: e.flowName,
238
+ error: err?.message
239
+ });
240
+ }
241
+ };
242
+ const handleOrchestration = async (e) => {
243
+ try {
244
+ if (e.id && e.ts) {
245
+ return;
246
+ }
247
+ if (e.type === "flow.completed" || e.type === "flow.failed") {
248
+ return;
249
+ }
250
+ const runId = e.runId;
251
+ if (!runId) return;
252
+ const flowName = e.flowName;
253
+ if (!flowName) return;
254
+ const streamName = names.flow(runId);
255
+ const indexKey = names.flowIndex(flowName);
256
+ if (e.type === "flow.start") {
257
+ const timestamp = Date.now();
258
+ await indexFlowRun(flowName, runId, timestamp, {
259
+ status: "running",
260
+ startedAt: timestamp,
261
+ stepCount: 0,
262
+ completedSteps: 0,
263
+ emittedEvents: []
264
+ });
265
+ }
266
+ if (e.type === "step.completed") {
267
+ try {
268
+ const newCount = await store.indexIncrement(indexKey, runId, "completedSteps", 1);
269
+ logger.debug("Incremented completedSteps", {
270
+ flowName,
271
+ runId,
272
+ stepName: "stepName" in e ? e.stepName : "unknown",
273
+ newCount
274
+ });
275
+ } catch (err) {
276
+ logger.warn("Failed to update completedSteps", {
277
+ flowName,
278
+ runId,
279
+ error: err?.message
280
+ });
281
+ }
282
+ }
283
+ if (e.type === "emit") {
284
+ const emitEvent = e;
285
+ const eventName = emitEvent.data?.name || emitEvent.data?.topic;
286
+ if (!eventName) {
287
+ logger.warn("Emit event missing name/topic", { flowName, runId, data: emitEvent.data });
288
+ } else {
289
+ try {
290
+ const currentEntry = await store.indexGet(indexKey, runId);
291
+ const emittedEvents = (currentEntry?.metadata?.emittedEvents || []).filter((item) => item != null && typeof item === "string");
292
+ if (!emittedEvents.includes(eventName)) {
293
+ await store.indexUpdateWithRetry(indexKey, runId, {
294
+ emittedEvents: [...emittedEvents, eventName]
295
+ });
296
+ logger.debug("Tracked emit event", {
297
+ flowName,
298
+ runId,
299
+ name: eventName,
300
+ allEmitted: [...emittedEvents, eventName]
301
+ });
302
+ }
303
+ } catch (err) {
304
+ logger.warn("Failed to track emitted event", {
305
+ flowName,
306
+ runId,
307
+ event: eventName,
308
+ error: err?.message
309
+ });
310
+ }
311
+ await checkAndTriggerPendingSteps(flowName, runId, store);
312
+ }
313
+ }
314
+ if (e.type === "step.completed" || e.type === "step.failed") {
315
+ await checkAndTriggerPendingSteps(flowName, runId, store);
316
+ await new Promise((resolve) => setTimeout(resolve, 50));
317
+ try {
318
+ const allEvents = await store.read(streamName);
319
+ const analyzedFlows = $useAnalyzedFlows();
320
+ const flowDef = analyzedFlows.find((f) => f.id === flowName);
321
+ if (flowDef?.steps) {
322
+ const entryStepName = flowDef.entry?.step;
323
+ const analysis = analyzeFlowCompletion(flowDef.steps, entryStepName, allEvents);
324
+ const updateMetadata = {
325
+ status: analysis.status,
326
+ stepCount: analysis.totalSteps
327
+ };
328
+ if (analysis.status !== "running" && analysis.completedAt) {
329
+ updateMetadata.completedAt = analysis.completedAt;
330
+ }
331
+ await store.indexUpdateWithRetry(indexKey, runId, updateMetadata);
332
+ if (analysis.status === "completed" || analysis.status === "failed") {
333
+ const eventType = analysis.status === "completed" ? "flow.completed" : "flow.failed";
334
+ const terminalEventExists = allEvents.some(
335
+ (evt) => evt.type === "flow.completed" || evt.type === "flow.failed"
336
+ );
337
+ if (terminalEventExists) {
338
+ logger.debug("Terminal event already exists, skipping publish", {
339
+ flowName,
340
+ runId,
341
+ eventType
342
+ });
343
+ } else {
344
+ logger.info("Publishing terminal event to bus", {
345
+ flowName,
346
+ runId,
347
+ eventType
348
+ });
349
+ await bus.publish({
350
+ type: eventType,
351
+ runId,
352
+ flowName,
353
+ data: {}
354
+ });
355
+ }
356
+ }
357
+ }
358
+ } catch (err) {
359
+ logger.warn("Failed to analyze flow completion", {
360
+ flowName,
361
+ runId,
362
+ error: err?.message
363
+ });
364
+ }
365
+ }
366
+ } catch (err) {
367
+ logger.error("ERROR handling event", {
368
+ type: e.type,
369
+ runId: e.runId,
370
+ flowName: e.flowName,
371
+ error: err?.message,
372
+ stack: err?.stack
373
+ });
374
+ }
375
+ };
376
+ const eventTypes = [
377
+ "flow.start",
378
+ "flow.completed",
379
+ "flow.failed",
380
+ "step.started",
381
+ "step.completed",
382
+ "step.failed",
383
+ "step.retry",
384
+ "log",
385
+ "emit",
386
+ "state"
387
+ ];
388
+ for (const type of eventTypes) {
389
+ unsubs.push(bus.onType(type, handlePersistence));
390
+ }
391
+ for (const type of eventTypes) {
392
+ unsubs.push(bus.onType(type, handleOrchestration));
393
+ }
394
+ }
395
+ function stop() {
396
+ for (const u of unsubs.splice(0)) {
397
+ try {
398
+ u();
399
+ } catch {
400
+ }
401
+ }
402
+ wired = false;
403
+ logger.debug("Flow wiring stopped");
404
+ }
405
+ return { start, stop };
406
+ }
@@ -0,0 +1,10 @@
1
+ import type { StreamAdapter } from '../types.js';
2
+ export interface WiringDeps {
3
+ adapter: StreamAdapter;
4
+ names?: any;
5
+ }
6
+ export interface Wiring {
7
+ start(): void;
8
+ stop(): void;
9
+ }
10
+ export declare function createWiringRegistry(deps: WiringDeps): Wiring;
@@ -0,0 +1,24 @@
1
+ import { createFlowWiring } from "./flowWiring.js";
2
+ export function createWiringRegistry(deps) {
3
+ const wirings = [
4
+ createFlowWiring({ adapter: deps.adapter })
5
+ // add future wirings here (triggers, webhooks, etc.)
6
+ ];
7
+ let started = false;
8
+ return {
9
+ start() {
10
+ if (started) return;
11
+ started = true;
12
+ for (const w of wirings) w.start();
13
+ },
14
+ stop() {
15
+ for (const w of wirings) {
16
+ try {
17
+ w.stop();
18
+ } catch {
19
+ }
20
+ }
21
+ started = false;
22
+ }
23
+ };
24
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Flow Run Snapshots (stream-based)
3
+ *
4
+ * Writes append-only snapshot events to nq:proj:flow:<flowName>:<runId>
5
+ * for key lifecycle kinds:
6
+ * - flow.start -> status=running
7
+ * - flow.complete -> status=completed
8
+ * - runner.log (with correlationId) -> logsCount++ (as a patch)
9
+ *
10
+ * Consumers reduce the small snapshot stream to a final object.
11
+ */
12
+ declare const _default: any;
13
+ export default _default;
@@ -0,0 +1,16 @@
1
+ import { defineNitroPlugin } from "#imports";
2
+ import { getEventStoreFactory } from "../events/eventStoreFactory.js";
3
+ export default defineNitroPlugin(() => {
4
+ const factory = getEventStoreFactory();
5
+ factory.start();
6
+ return {
7
+ hooks: {
8
+ close: async () => {
9
+ try {
10
+ factory.stop();
11
+ } catch {
12
+ }
13
+ }
14
+ }
15
+ };
16
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Nitro plugin to handle graceful WebSocket shutdown during HMR
3
+ */
4
+ declare const _default: any;
5
+ export default _default;