@nvent-addon/app 0.4.5 → 0.5.1

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 (153) hide show
  1. package/dist/module.d.mts +19 -1
  2. package/dist/module.mjs +20 -8
  3. package/dist/runtime/app/components/{nhealth/component-router.d.vue.ts → ComponentRouter.d.vue.ts} +1 -5
  4. package/dist/runtime/app/components/{nhealth/component-router.vue.d.ts → ComponentRouter.vue.d.ts} +1 -5
  5. package/dist/runtime/app/components/{nhealth/component-shell.d.vue.ts → ComponentShell.d.vue.ts} +4 -9
  6. package/dist/runtime/app/components/ComponentShell.vue +87 -0
  7. package/dist/runtime/app/components/{nhealth/component-shell.vue.d.ts → ComponentShell.vue.d.ts} +4 -9
  8. package/dist/runtime/app/components/ConfirmDialog.d.vue.ts +1 -6
  9. package/dist/runtime/app/components/ConfirmDialog.vue.d.ts +1 -6
  10. package/dist/runtime/app/components/ListItem.d.vue.ts +3 -6
  11. package/dist/runtime/app/components/ListItem.vue.d.ts +3 -6
  12. package/dist/runtime/app/components/LiveIndicator.d.vue.ts +7 -0
  13. package/dist/runtime/app/components/LiveIndicator.vue +30 -0
  14. package/dist/runtime/app/components/LiveIndicator.vue.d.ts +7 -0
  15. package/dist/runtime/app/components/{QueueConfigDetails.d.vue.ts → QueueConfiguration.d.vue.ts} +1 -10
  16. package/dist/runtime/app/components/QueueConfiguration.vue +387 -0
  17. package/dist/runtime/app/components/{QueueConfigDetails.vue.d.ts → QueueConfiguration.vue.d.ts} +1 -10
  18. package/dist/runtime/app/components/StatCard.d.vue.ts +9 -0
  19. package/dist/runtime/app/components/StatCard.vue +57 -0
  20. package/dist/runtime/app/components/StatCard.vue.d.ts +9 -0
  21. package/dist/runtime/app/components/TimelineList.vue +67 -0
  22. package/dist/runtime/app/components/flow/AwaitNode.d.vue.ts +18 -0
  23. package/dist/runtime/app/components/flow/AwaitNode.vue +91 -0
  24. package/dist/runtime/app/components/flow/AwaitNode.vue.d.ts +18 -0
  25. package/dist/runtime/app/components/{FlowDiagram.d.vue.ts → flow/Diagram.d.vue.ts} +12 -1
  26. package/dist/runtime/app/components/{FlowDiagram.vue → flow/Diagram.vue} +92 -11
  27. package/dist/runtime/app/components/{FlowDiagram.vue.d.ts → flow/Diagram.vue.d.ts} +12 -1
  28. package/dist/runtime/app/components/{FlowRunOverview.d.vue.ts → flow/RunOverview.d.vue.ts} +3 -0
  29. package/dist/runtime/app/components/{FlowRunOverview.vue → flow/RunOverview.vue} +94 -8
  30. package/dist/runtime/app/components/{FlowRunOverview.vue.d.ts → flow/RunOverview.vue.d.ts} +3 -0
  31. package/dist/runtime/app/components/{FlowRunStatusBadge.d.vue.ts → flow/RunStatusBadge.d.vue.ts} +2 -8
  32. package/dist/runtime/app/components/{FlowRunStatusBadge.vue → flow/RunStatusBadge.vue} +8 -1
  33. package/dist/runtime/app/components/{FlowRunStatusBadge.vue.d.ts → flow/RunStatusBadge.vue.d.ts} +2 -8
  34. package/dist/runtime/app/components/{FlowRunTimeline.vue → flow/RunTimeline.vue} +1 -1
  35. package/dist/runtime/app/components/{FlowStepSelector.d.vue.ts → flow/StepSelector.d.vue.ts} +1 -0
  36. package/dist/runtime/app/components/flow/StepSelector.vue +553 -0
  37. package/dist/runtime/app/components/{FlowStepSelector.vue.d.ts → flow/StepSelector.vue.d.ts} +1 -0
  38. package/dist/runtime/app/components/trigger/BasicInfoCard.d.vue.ts +33 -0
  39. package/dist/runtime/app/components/trigger/BasicInfoCard.vue +168 -0
  40. package/dist/runtime/app/components/trigger/BasicInfoCard.vue.d.ts +33 -0
  41. package/dist/runtime/app/components/{FlowSchedulesList.d.vue.ts → trigger/DangerZone.d.vue.ts} +4 -6
  42. package/dist/runtime/app/components/trigger/DangerZone.vue +46 -0
  43. package/dist/runtime/app/components/{FlowSchedulesList.vue.d.ts → trigger/DangerZone.vue.d.ts} +4 -6
  44. package/dist/runtime/app/components/trigger/EditHeader.d.vue.ts +15 -0
  45. package/dist/runtime/app/components/trigger/EditHeader.vue +55 -0
  46. package/dist/runtime/app/components/trigger/EditHeader.vue.d.ts +15 -0
  47. package/dist/runtime/app/components/trigger/EventConfig.d.vue.ts +24 -0
  48. package/dist/runtime/app/components/trigger/EventConfig.vue +68 -0
  49. package/dist/runtime/app/components/trigger/EventConfig.vue.d.ts +24 -0
  50. package/dist/runtime/app/components/trigger/FlowSubscriptions.d.vue.ts +14 -0
  51. package/dist/runtime/app/components/trigger/FlowSubscriptions.vue +128 -0
  52. package/dist/runtime/app/components/trigger/FlowSubscriptions.vue.d.ts +14 -0
  53. package/dist/runtime/app/components/trigger/ScheduleConfig.d.vue.ts +27 -0
  54. package/dist/runtime/app/components/trigger/ScheduleConfig.vue +375 -0
  55. package/dist/runtime/app/components/trigger/ScheduleConfig.vue.d.ts +27 -0
  56. package/dist/runtime/app/components/{FlowScheduleDialog.d.vue.ts → trigger/StatusConfig.d.vue.ts} +6 -6
  57. package/dist/runtime/app/components/trigger/StatusConfig.vue +78 -0
  58. package/dist/runtime/app/components/{FlowScheduleDialog.vue.d.ts → trigger/StatusConfig.vue.d.ts} +6 -6
  59. package/dist/runtime/app/components/trigger/WebhookConfig.d.vue.ts +30 -0
  60. package/dist/runtime/app/components/trigger/WebhookConfig.vue +97 -0
  61. package/dist/runtime/app/components/trigger/WebhookConfig.vue.d.ts +30 -0
  62. package/dist/runtime/app/composables/useAnalyzedFlows.d.ts +5 -0
  63. package/dist/runtime/app/composables/useAnalyzedFlows.js +15 -1
  64. package/dist/runtime/app/composables/useComponentRouter.d.ts +8 -0
  65. package/dist/runtime/app/composables/useComponentRouter.js +10 -2
  66. package/dist/runtime/app/composables/useFlowRunsInfinite.d.ts +1 -1
  67. package/dist/runtime/app/composables/useFlowState.js +65 -0
  68. package/dist/runtime/app/composables/useFlowWebSocket.d.ts +11 -2
  69. package/dist/runtime/app/composables/useFlowWebSocket.js +181 -65
  70. package/dist/runtime/app/composables/useQueueJobs.d.ts +12 -1
  71. package/dist/runtime/app/composables/useQueueJobs.js +13 -7
  72. package/dist/runtime/app/composables/useTrigger.d.ts +137 -0
  73. package/dist/runtime/app/composables/useTrigger.js +116 -0
  74. package/dist/runtime/app/composables/useTriggerWebSocket.d.ts +35 -0
  75. package/dist/runtime/app/composables/useTriggerWebSocket.js +333 -0
  76. package/dist/runtime/app/pages/dashboard.d.vue.ts +3 -0
  77. package/dist/runtime/app/pages/dashboard.vue +738 -0
  78. package/dist/runtime/app/pages/dashboard.vue.d.ts +3 -0
  79. package/dist/runtime/app/pages/flows/[name].d.vue.ts +3 -0
  80. package/dist/runtime/app/pages/flows/[name].vue +680 -0
  81. package/dist/runtime/app/pages/flows/[name].vue.d.ts +3 -0
  82. package/dist/runtime/app/pages/flows/index.vue +321 -620
  83. package/dist/runtime/app/pages/index.vue +39 -9
  84. package/dist/runtime/app/pages/queues/index.vue +202 -194
  85. package/dist/runtime/app/pages/queues/jobs.vue +534 -207
  86. package/dist/runtime/app/pages/settings/scheduler.d.vue.ts +3 -0
  87. package/dist/runtime/app/pages/settings/scheduler.vue +310 -0
  88. package/dist/runtime/app/pages/settings/scheduler.vue.d.ts +3 -0
  89. package/dist/runtime/app/pages/triggers/[name]/edit.d.vue.ts +3 -0
  90. package/dist/runtime/app/pages/triggers/[name]/edit.vue +429 -0
  91. package/dist/runtime/app/pages/triggers/[name]/edit.vue.d.ts +3 -0
  92. package/dist/runtime/app/pages/triggers/[name].d.vue.ts +3 -0
  93. package/dist/runtime/app/pages/triggers/[name].vue +898 -0
  94. package/dist/runtime/app/pages/triggers/[name].vue.d.ts +3 -0
  95. package/dist/runtime/app/pages/triggers/index.d.vue.ts +3 -0
  96. package/dist/runtime/app/pages/triggers/index.vue +528 -0
  97. package/dist/runtime/app/pages/triggers/index.vue.d.ts +3 -0
  98. package/dist/runtime/app/pages/triggers/new.d.vue.ts +3 -0
  99. package/dist/runtime/app/pages/triggers/new.vue +610 -0
  100. package/dist/runtime/app/pages/triggers/new.vue.d.ts +3 -0
  101. package/dist/runtime/server/api/_flows/[name]/clear-history.delete.d.ts +10 -0
  102. package/dist/runtime/server/api/_flows/[name]/clear-history.delete.js +49 -0
  103. package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.d.ts +2 -0
  104. package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.js +21 -0
  105. package/dist/runtime/server/api/_flows/[name]/runs.get.d.ts +17 -0
  106. package/dist/runtime/server/api/_flows/[name]/runs.get.js +64 -0
  107. package/dist/runtime/server/api/_flows/[name]/start.post.d.ts +2 -0
  108. package/dist/runtime/server/api/_flows/[name]/start.post.js +9 -0
  109. package/dist/runtime/server/api/_flows/index.get.d.ts +7 -0
  110. package/dist/runtime/server/api/_flows/index.get.js +5 -0
  111. package/dist/runtime/server/api/_flows/recent-runs.get.d.ts +15 -0
  112. package/dist/runtime/server/api/_flows/recent-runs.get.js +67 -0
  113. package/dist/runtime/server/api/_flows/ws.d.ts +80 -0
  114. package/dist/runtime/server/api/_flows/ws.js +309 -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 +14 -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 +39 -0
  119. package/dist/runtime/server/api/_queues/index.get.d.ts +2 -0
  120. package/dist/runtime/server/api/_queues/index.get.js +106 -0
  121. package/dist/runtime/server/api/_queues/ws.d.ts +48 -0
  122. package/dist/runtime/server/api/_queues/ws.js +215 -0
  123. package/dist/runtime/server/api/_scheduler/jobs.get.d.ts +19 -0
  124. package/dist/runtime/server/api/_scheduler/jobs.get.js +36 -0
  125. package/dist/runtime/server/api/_triggers/[name]/events.get.d.ts +6 -0
  126. package/dist/runtime/server/api/_triggers/[name]/events.get.js +43 -0
  127. package/dist/runtime/server/api/_triggers/[name]/index.get.d.ts +6 -0
  128. package/dist/runtime/server/api/_triggers/[name]/index.get.js +76 -0
  129. package/dist/runtime/server/api/_triggers/[name].delete.d.ts +7 -0
  130. package/dist/runtime/server/api/_triggers/[name].delete.js +37 -0
  131. package/dist/runtime/server/api/_triggers/[name].patch.d.ts +7 -0
  132. package/dist/runtime/server/api/_triggers/[name].patch.js +117 -0
  133. package/dist/runtime/server/api/_triggers/index.get.d.ts +6 -0
  134. package/dist/runtime/server/api/_triggers/index.get.js +44 -0
  135. package/dist/runtime/server/api/_triggers/index.post.d.ts +7 -0
  136. package/dist/runtime/server/api/_triggers/index.post.js +124 -0
  137. package/dist/runtime/server/api/_triggers/stats.get.d.ts +6 -0
  138. package/dist/runtime/server/api/_triggers/stats.get.js +41 -0
  139. package/dist/runtime/server/api/_triggers/ws.d.ts +74 -0
  140. package/dist/runtime/server/api/_triggers/ws.js +315 -0
  141. package/dist/runtime/server/tsconfig.json +7 -0
  142. package/package.json +8 -8
  143. package/dist/runtime/app/components/FlowScheduleDialog.vue +0 -226
  144. package/dist/runtime/app/components/FlowSchedulesList.vue +0 -99
  145. package/dist/runtime/app/components/FlowStepSelector.vue +0 -238
  146. package/dist/runtime/app/components/QueueConfigDetails.vue +0 -412
  147. package/dist/runtime/app/components/nhealth/component-shell.vue +0 -89
  148. /package/dist/runtime/app/components/{nhealth/component-router.vue → ComponentRouter.vue} +0 -0
  149. /package/dist/runtime/app/components/{FlowNodeCard.d.vue.ts → flow/NodeCard.d.vue.ts} +0 -0
  150. /package/dist/runtime/app/components/{FlowNodeCard.vue → flow/NodeCard.vue} +0 -0
  151. /package/dist/runtime/app/components/{FlowNodeCard.vue.d.ts → flow/NodeCard.vue.d.ts} +0 -0
  152. /package/dist/runtime/app/components/{FlowRunTimeline.d.vue.ts → flow/RunTimeline.d.vue.ts} +0 -0
  153. /package/dist/runtime/app/components/{FlowRunTimeline.vue.d.ts → flow/RunTimeline.vue.d.ts} +0 -0
@@ -0,0 +1,315 @@
1
+ import {
2
+ defineWebSocketHandler,
3
+ usePeerManager,
4
+ useNventLogger,
5
+ useStreamAdapter,
6
+ useStoreAdapter,
7
+ useStreamTopics,
8
+ useTrigger
9
+ } from "#imports";
10
+ const peerContexts = /* @__PURE__ */ new WeakMap();
11
+ function safeSend(peer, data) {
12
+ try {
13
+ peer.send(JSON.stringify(data));
14
+ return true;
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
19
+ export default defineWebSocketHandler({
20
+ async open(peer) {
21
+ const logger = useNventLogger("api-triggers-ws");
22
+ logger.info("[ws] client connected:", { peerId: peer.id });
23
+ const { registerWsPeer } = usePeerManager();
24
+ registerWsPeer(peer);
25
+ peerContexts.set(peer, {
26
+ subscriptions: /* @__PURE__ */ new Map()
27
+ });
28
+ safeSend(peer, {
29
+ type: "connected",
30
+ timestamp: Date.now()
31
+ });
32
+ },
33
+ async message(peer, message) {
34
+ const logger = useNventLogger("api-triggers-ws");
35
+ const context = peerContexts.get(peer);
36
+ if (!context) {
37
+ logger.error("[ws] no context for peer:", { peerId: peer.id });
38
+ return;
39
+ }
40
+ let data;
41
+ try {
42
+ data = JSON.parse(message.text());
43
+ } catch {
44
+ safeSend(peer, {
45
+ type: "error",
46
+ message: "Invalid JSON"
47
+ });
48
+ return;
49
+ }
50
+ const { type, triggerName } = data;
51
+ if (type === "subscribe") {
52
+ if (!triggerName) {
53
+ safeSend(peer, {
54
+ type: "error",
55
+ message: "Missing triggerName"
56
+ });
57
+ return;
58
+ }
59
+ let stream;
60
+ let store;
61
+ try {
62
+ stream = useStreamAdapter();
63
+ store = useStoreAdapter();
64
+ } catch (err) {
65
+ logger.error("[ws] Adapters not initialized yet:", { error: err });
66
+ safeSend(peer, {
67
+ type: "error",
68
+ message: "Server initializing, please retry"
69
+ });
70
+ return;
71
+ }
72
+ const existingHandle = context.subscriptions.get(triggerName);
73
+ if (existingHandle) {
74
+ try {
75
+ if (typeof existingHandle === "function") {
76
+ await existingHandle();
77
+ } else {
78
+ await existingHandle.unsubscribe();
79
+ }
80
+ } catch (err) {
81
+ logger.error("[ws] error unsubscribing:", { error: err });
82
+ }
83
+ }
84
+ const { StoreSubjects, StreamTopics } = useStreamTopics();
85
+ const streamTopic = StreamTopics.triggerEvents(triggerName);
86
+ const storeSubject = StoreSubjects.triggerStream(triggerName);
87
+ const handle = await stream.subscribe(streamTopic, async (message2) => {
88
+ const event = message2.data?.event;
89
+ if (!event) {
90
+ logger.warn("[ws] Received message without event data:", message2);
91
+ return;
92
+ }
93
+ safeSend(peer, {
94
+ type: "event",
95
+ triggerName,
96
+ event
97
+ });
98
+ });
99
+ context.subscriptions.set(triggerName, handle);
100
+ try {
101
+ const historicalEvents = await store.stream.read(storeSubject, {
102
+ limit: 100,
103
+ order: "asc"
104
+ // Forward order to match flows
105
+ });
106
+ safeSend(peer, {
107
+ type: "history",
108
+ triggerName,
109
+ events: historicalEvents || []
110
+ });
111
+ } catch (err) {
112
+ logger.error("[ws] error sending history:", { error: err });
113
+ safeSend(peer, {
114
+ type: "error",
115
+ message: "Failed to load history"
116
+ });
117
+ }
118
+ safeSend(peer, {
119
+ type: "subscribed",
120
+ triggerName
121
+ });
122
+ } else if (type === "unsubscribe") {
123
+ if (!triggerName) {
124
+ safeSend(peer, {
125
+ type: "error",
126
+ message: "Missing triggerName"
127
+ });
128
+ return;
129
+ }
130
+ const handle = context.subscriptions.get(triggerName);
131
+ if (handle) {
132
+ try {
133
+ if (typeof handle === "function") {
134
+ await handle();
135
+ } else {
136
+ await handle.unsubscribe();
137
+ }
138
+ context.subscriptions.delete(triggerName);
139
+ safeSend(peer, {
140
+ type: "unsubscribed",
141
+ triggerName
142
+ });
143
+ } catch (err) {
144
+ logger.error("[ws] error unsubscribing:", { error: err });
145
+ safeSend(peer, {
146
+ type: "error",
147
+ message: "Failed to unsubscribe"
148
+ });
149
+ }
150
+ }
151
+ } else if (type === "subscribe.stats") {
152
+ const statsKey = "stats";
153
+ const existingUnsub = context.subscriptions.get(statsKey);
154
+ if (existingUnsub) {
155
+ safeSend(peer, {
156
+ type: "error",
157
+ message: "Already subscribed to stats"
158
+ });
159
+ return;
160
+ }
161
+ try {
162
+ const stream = useStreamAdapter();
163
+ const { StreamTopics } = useStreamTopics();
164
+ const topic = StreamTopics.triggerStats();
165
+ const handle = await stream.subscribe(topic, (message2) => {
166
+ safeSend(peer, {
167
+ type: "trigger.stats.update",
168
+ data: message2,
169
+ timestamp: Date.now()
170
+ });
171
+ });
172
+ const unsub = async () => {
173
+ try {
174
+ await handle.unsubscribe();
175
+ } catch (err) {
176
+ logger.error("[ws] error in stats unsub:", { error: err });
177
+ }
178
+ };
179
+ context.subscriptions.set(statsKey, unsub);
180
+ try {
181
+ const trigger = useTrigger();
182
+ const allTriggers = trigger.getAllTriggers();
183
+ const { getSubscribedFlows, getTriggerStats } = trigger;
184
+ if (allTriggers && allTriggers.length > 0) {
185
+ for (const triggerEntry of allTriggers) {
186
+ const subscribedFlows = getSubscribedFlows(triggerEntry.name);
187
+ const stats = await getTriggerStats(triggerEntry.name);
188
+ safeSend(peer, {
189
+ type: "trigger.stats.initial",
190
+ data: {
191
+ id: triggerEntry.name,
192
+ metadata: {
193
+ "name": triggerEntry.name,
194
+ "type": triggerEntry.type,
195
+ "scope": triggerEntry.scope,
196
+ "displayName": triggerEntry.displayName,
197
+ "description": triggerEntry.description,
198
+ "source": triggerEntry.source,
199
+ "status": triggerEntry.status || "active",
200
+ "registeredAt": triggerEntry.registeredAt,
201
+ "lastActivityAt": triggerEntry.lastActivityAt,
202
+ "webhook": triggerEntry.webhook,
203
+ "schedule": triggerEntry.schedule,
204
+ "config": triggerEntry.config,
205
+ "subscribedFlows": subscribedFlows,
206
+ "subscriptionCount": subscribedFlows.length,
207
+ "stats.totalFires": stats?.totalFires || 0,
208
+ "stats.totalFlowsStarted": stats?.totalFlowsStarted || 0,
209
+ "stats.activeSubscribers": stats?.activeSubscribers || subscribedFlows.length,
210
+ "stats.lastFiredAt": stats?.lastFiredAt
211
+ }
212
+ },
213
+ timestamp: Date.now()
214
+ });
215
+ }
216
+ }
217
+ } catch (err) {
218
+ logger.error("[ws] error fetching initial trigger stats:", { error: err });
219
+ }
220
+ safeSend(peer, {
221
+ type: "stats.subscribed",
222
+ timestamp: Date.now()
223
+ });
224
+ } catch (err) {
225
+ logger.error("[ws] error subscribing to stats:", { error: err });
226
+ safeSend(peer, {
227
+ type: "error",
228
+ message: "Failed to subscribe to stats"
229
+ });
230
+ }
231
+ } else if (type === "unsubscribe.stats") {
232
+ const statsKey = "stats";
233
+ const unsub = context.subscriptions.get(statsKey);
234
+ if (unsub) {
235
+ try {
236
+ if (typeof unsub === "function") {
237
+ await unsub();
238
+ } else {
239
+ await unsub.unsubscribe();
240
+ }
241
+ context.subscriptions.delete(statsKey);
242
+ safeSend(peer, {
243
+ type: "stats.unsubscribed",
244
+ timestamp: Date.now()
245
+ });
246
+ } catch (err) {
247
+ logger.error("[ws] error unsubscribing from stats:", { error: err });
248
+ safeSend(peer, {
249
+ type: "error",
250
+ message: "Failed to unsubscribe from stats"
251
+ });
252
+ }
253
+ }
254
+ } else if (type === "ping") {
255
+ safeSend(peer, {
256
+ type: "pong",
257
+ timestamp: Date.now()
258
+ });
259
+ } else {
260
+ safeSend(peer, {
261
+ type: "error",
262
+ message: `Unknown message type: ${type}`
263
+ });
264
+ }
265
+ },
266
+ async close(peer, event) {
267
+ const logger = useNventLogger("api-triggers-ws");
268
+ const isNormalClosure = event?.code === 1e3 || event?.code === 1001;
269
+ if (!isNormalClosure) {
270
+ logger.info("[ws] client disconnected:", { peerId: peer.id, code: event?.code, reason: event?.reason });
271
+ }
272
+ const { unregisterWsPeer } = usePeerManager();
273
+ unregisterWsPeer(peer);
274
+ const context = peerContexts.get(peer);
275
+ if (context) {
276
+ for (const handleOrUnsub of Array.from(context.subscriptions.values())) {
277
+ try {
278
+ if (typeof handleOrUnsub === "function") {
279
+ await handleOrUnsub();
280
+ } else {
281
+ await handleOrUnsub.unsubscribe();
282
+ }
283
+ } catch (err) {
284
+ if (!isNormalClosure) {
285
+ logger.error("[ws] error unsubscribing on close:", { error: err });
286
+ }
287
+ }
288
+ }
289
+ context.subscriptions.clear();
290
+ peerContexts.delete(peer);
291
+ }
292
+ },
293
+ async error(peer, error) {
294
+ const logger = useNventLogger("api-triggers-ws");
295
+ logger.error("[ws] error for peer:", { peerId: peer.id, error });
296
+ const { unregisterWsPeer } = usePeerManager();
297
+ unregisterWsPeer(peer);
298
+ const context = peerContexts.get(peer);
299
+ if (context) {
300
+ for (const handleOrUnsub of Array.from(context.subscriptions.values())) {
301
+ try {
302
+ if (typeof handleOrUnsub === "function") {
303
+ await handleOrUnsub();
304
+ } else {
305
+ await handleOrUnsub.unsubscribe();
306
+ }
307
+ } catch (err) {
308
+ logger.error("[ws] error unsubscribing on error:", { error: err });
309
+ }
310
+ }
311
+ context.subscriptions.clear();
312
+ peerContexts.delete(peer);
313
+ }
314
+ }
315
+ });
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../../../../../playground/.nuxt/tsconfig.server.json",
3
+ "exclude": [
4
+ "node_modules",
5
+ "dist"
6
+ ]
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nvent-addon/app",
3
- "version": "0.4.5",
3
+ "version": "0.5.1",
4
4
  "description": "nvent app module for Nuxt.js",
5
5
  "repository": "DevJoghurt/nvent",
6
6
  "license": "MIT",
@@ -19,29 +19,29 @@
19
19
  "scripts": {
20
20
  "build": "nuxt-module-build build",
21
21
  "dev": "nuxt-module-build build --stub",
22
- "prepack": "pnpm build",
23
- "test": "vitest"
22
+ "prepack": "pnpm build"
24
23
  },
25
24
  "dependencies": {
26
- "@iconify-json/devicon": "^1.2.49",
25
+ "@iconify-json/devicon": "^1.2.51",
27
26
  "@iconify-json/heroicons": "1.2.3",
28
- "@iconify-json/lucide": "^1.2.73",
27
+ "@iconify-json/lucide": "^1.2.76",
29
28
  "@nuxt/kit": "4.2.1",
29
+ "@nuxt/ui": "4.2.1",
30
30
  "@vue-flow/background": "^1.3.2",
31
31
  "@vue-flow/controls": "^1.1.3",
32
- "@vue-flow/core": "^1.47.0",
32
+ "@vue-flow/core": "^1.48.0",
33
33
  "@vue-flow/minimap": "^1.5.4",
34
34
  "defu": "^6.1.4",
35
35
  "json-editor-vue": "^0.18.1",
36
36
  "nuxt": "4.2.1",
37
37
  "pathe": "^2.0.3",
38
- "zod": "^4.1.12"
38
+ "zod": "^4.1.13"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@nuxt/module-builder": "^1.0.2",
42
42
  "@nuxt/schema": "4.2.1",
43
43
  "@types/node": "^24.10.1",
44
44
  "typescript": "latest",
45
- "vitest": "^4.0.10"
45
+ "vitest": "^4.0.14"
46
46
  }
47
47
  }
@@ -1,226 +0,0 @@
1
- <template>
2
- <UModal v-model:open="isOpen">
3
- <template #header>
4
- <div class="flex items-center justify-between">
5
- <div>
6
- <h3 class="text-lg font-semibold">
7
- Schedule Flow
8
- </h3>
9
- <p class="text-sm text-gray-500 mt-1">
10
- {{ flowName }}
11
- </p>
12
- </div>
13
- </div>
14
- </template>
15
- <template #body>
16
- <div class="space-y-4">
17
- <!-- Schedule Type -->
18
- <div>
19
- <label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
20
- Schedule Type
21
- </label>
22
- <UTabs
23
- v-model="scheduleType"
24
- :items="typeOptions"
25
- size="sm"
26
- variant="pill"
27
- color="neutral"
28
- />
29
- </div>
30
-
31
- <!-- Cron Pattern -->
32
- <div v-if="scheduleType === 'cron'">
33
- <label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
34
- Pattern
35
- </label>
36
- <USelectMenu
37
- v-model="selectedPreset"
38
- :items="cronPresets"
39
- class="w-full"
40
- placeholder="Select preset or custom"
41
- />
42
- <UInput
43
- v-if="selectedPreset?.value === 'custom'"
44
- v-model="customCron"
45
- placeholder="0 2 * * *"
46
- class="mt-2 w-full"
47
- />
48
- </div>
49
-
50
- <!-- Delay -->
51
- <div v-else-if="scheduleType === 'delay'">
52
- <label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
53
- Delay
54
- </label>
55
- <div class="flex gap-2">
56
- <UInput
57
- v-model="delayValue"
58
- type="number"
59
- placeholder="5"
60
- class="flex-1"
61
- />
62
- <USelectMenu
63
- v-model="delayUnit"
64
- :items="delayUnits"
65
- class="w-32"
66
- />
67
- </div>
68
- </div>
69
-
70
- <!-- Input Data -->
71
- <div>
72
- <label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
73
- Input Data (JSON)
74
- </label>
75
- <UTextarea
76
- v-model="inputJson"
77
- placeholder="{ &quot;key&quot;: &quot;value&quot; }"
78
- :rows="4"
79
- class="font-mono text-sm w-full"
80
- />
81
- <p
82
- v-if="jsonError"
83
- class="text-xs text-red-500 mt-1"
84
- >
85
- {{ jsonError }}
86
- </p>
87
- </div>
88
-
89
- <!-- Description -->
90
- <div>
91
- <label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
92
- Description (optional)
93
- </label>
94
- <UInput
95
- v-model="description"
96
- class="w-full"
97
- placeholder="Daily cleanup job"
98
- />
99
- </div>
100
- </div>
101
- </template>
102
- <template #footer>
103
- <div class="flex justify-end gap-2">
104
- <UButton
105
- color="neutral"
106
- variant="ghost"
107
- @click="isOpen = false"
108
- >
109
- Cancel
110
- </UButton>
111
- <UButton
112
- color="primary"
113
- :loading="isSubmitting"
114
- :disabled="!canSubmit"
115
- @click="handleSubmit"
116
- >
117
- Schedule Flow
118
- </UButton>
119
- </div>
120
- </template>
121
- </UModal>
122
- </template>
123
-
124
- <script setup>
125
- import { ref, computed, watch } from "#imports";
126
- import { UModal, UButton, UTabs, USelectMenu, UInput, UTextarea } from "#components";
127
- const props = defineProps({
128
- flowName: { type: String, required: true }
129
- });
130
- const emit = defineEmits(["scheduled"]);
131
- const isOpen = defineModel({ type: Boolean, ...{ default: false } });
132
- const scheduleType = ref("cron");
133
- const typeOptions = [
134
- { label: "Recurring (Cron)", value: "cron" },
135
- { label: "One-time (Delay)", value: "delay" }
136
- ];
137
- const cronPresets = [
138
- { label: "Every minute", value: "* * * * *" },
139
- { label: "Every 5 minutes", value: "*/5 * * * *" },
140
- { label: "Every hour", value: "0 * * * *" },
141
- { label: "Daily at 2 AM", value: "0 2 * * *" },
142
- { label: "Daily at noon", value: "0 12 * * *" },
143
- { label: "Weekly (Monday 9 AM)", value: "0 9 * * 1" },
144
- { label: "Monthly (1st at midnight)", value: "0 0 1 * *" },
145
- { label: "Custom", value: "custom" }
146
- ];
147
- const selectedPreset = ref(cronPresets[3]);
148
- const customCron = ref("");
149
- const delayValue = ref("5");
150
- const delayUnit = ref({ label: "Minutes", value: 6e4 });
151
- const delayUnits = [
152
- { label: "Seconds", value: 1e3 },
153
- { label: "Minutes", value: 6e4 },
154
- { label: "Hours", value: 36e5 },
155
- { label: "Days", value: 864e5 }
156
- ];
157
- const inputJson = ref("");
158
- const description = ref("");
159
- const isSubmitting = ref(false);
160
- const jsonError = computed(() => {
161
- if (!inputJson.value)
162
- return null;
163
- try {
164
- JSON.parse(inputJson.value);
165
- return null;
166
- } catch (e) {
167
- return `Invalid JSON: ${e.message}`;
168
- }
169
- });
170
- const canSubmit = computed(() => {
171
- if (jsonError.value)
172
- return false;
173
- if (scheduleType.value === "cron") {
174
- if (selectedPreset.value?.value === "custom") {
175
- return customCron.value.trim().length > 0;
176
- }
177
- return selectedPreset.value?.value != null;
178
- } else {
179
- const num = Number.parseInt(delayValue.value);
180
- return !Number.isNaN(num) && num > 0;
181
- }
182
- });
183
- const handleSubmit = async () => {
184
- isSubmitting.value = true;
185
- try {
186
- let input = {};
187
- if (inputJson.value) {
188
- input = JSON.parse(inputJson.value);
189
- }
190
- const body = {
191
- input,
192
- metadata: {
193
- description: description.value || void 0
194
- }
195
- };
196
- if (scheduleType.value === "cron") {
197
- body.cron = selectedPreset.value?.value === "custom" ? customCron.value : selectedPreset.value?.value;
198
- } else {
199
- const num = Number.parseInt(delayValue.value);
200
- body.delay = num * delayUnit.value.value;
201
- }
202
- await $fetch(`/api/_flows/${props.flowName}/schedule`, {
203
- method: "POST",
204
- body
205
- });
206
- emit("scheduled");
207
- isOpen.value = false;
208
- inputJson.value = "";
209
- description.value = "";
210
- customCron.value = "";
211
- delayValue.value = "5";
212
- } finally {
213
- isSubmitting.value = false;
214
- }
215
- };
216
- watch(isOpen, (newVal) => {
217
- if (!newVal) {
218
- scheduleType.value = "cron";
219
- selectedPreset.value = cronPresets[3];
220
- inputJson.value = "";
221
- description.value = "";
222
- customCron.value = "";
223
- delayValue.value = "5";
224
- }
225
- });
226
- </script>
@@ -1,99 +0,0 @@
1
- <template>
2
- <div class="space-y-4">
3
- <div v-if="!schedules || schedules.length === 0">
4
- <UAlert
5
- color="info"
6
- title="No schedules configured"
7
- variant="subtle"
8
- icon="i-lucide-info"
9
- description="Create a schedule to run this flow automatically"
10
- />
11
- </div>
12
-
13
- <div
14
- v-for="schedule in schedules"
15
- :key="schedule.id"
16
- class="border border-gray-200 dark:border-gray-800 rounded-lg p-4"
17
- >
18
- <div class="flex items-start justify-between">
19
- <div class="flex-1">
20
- <div class="flex items-center gap-2 mb-2">
21
- <UIcon
22
- name="i-lucide-clock"
23
- class="text-blue-500"
24
- />
25
- <span class="font-medium text-sm">
26
- {{ schedule.schedule.cron || "One-time delay" }}
27
- </span>
28
- </div>
29
- <div class="text-xs text-gray-500 space-y-1">
30
- <div>
31
- <span class="font-medium">Next run:</span>
32
- {{ formatDate(schedule.nextRun) }}
33
- </div>
34
- <div v-if="schedule.metadata?.description">
35
- <span class="font-medium">Description:</span>
36
- {{ schedule.metadata.description }}
37
- </div>
38
- </div>
39
- </div>
40
- <UButton
41
- icon="i-lucide-trash-2"
42
- color="error"
43
- variant="ghost"
44
- size="xs"
45
- :loading="deletingId === schedule.id"
46
- @click="handleDelete(schedule.id)"
47
- />
48
- </div>
49
- </div>
50
- </div>
51
- </template>
52
-
53
- <script setup>
54
- import { ref, onMounted } from "#imports";
55
- import { UAlert, UButton, UIcon } from "#components";
56
- const props = defineProps({
57
- flowName: { type: String, required: true }
58
- });
59
- const emit = defineEmits(["updated"]);
60
- const schedules = ref([]);
61
- const deletingId = ref(null);
62
- const loadSchedules = async () => {
63
- try {
64
- const data = await $fetch(`/api/_flows/${props.flowName}/schedules`);
65
- schedules.value = data;
66
- } catch (error) {
67
- console.error("Failed to load schedules:", error);
68
- schedules.value = [];
69
- }
70
- };
71
- const handleDelete = async (id) => {
72
- deletingId.value = id;
73
- try {
74
- await $fetch(`/api/_flows/${props.flowName}/schedules/${id}`, {
75
- method: "DELETE"
76
- });
77
- await loadSchedules();
78
- emit("updated");
79
- } catch (error) {
80
- console.error("Failed to delete schedule:", error);
81
- } finally {
82
- deletingId.value = null;
83
- }
84
- };
85
- const formatDate = (date) => {
86
- if (!date)
87
- return "N/A";
88
- return new Date(date).toLocaleString("en-US", {
89
- dateStyle: "medium",
90
- timeStyle: "short"
91
- });
92
- };
93
- onMounted(() => {
94
- loadSchedules();
95
- });
96
- defineExpose({
97
- loadSchedules
98
- });
99
- </script>