@revenexx/studio-integrations 1.1.1 → 1.3.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 (119) hide show
  1. package/CHANGELOG.md +638 -0
  2. package/README.md +3 -1
  3. package/dist/module.json +1 -1
  4. package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +6 -18
  5. package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +13 -23
  6. package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +7 -6
  7. package/dist/runtime/components/Integrations/DeadLettersPanel.vue +15 -4
  8. package/dist/runtime/components/Integrations/ExpressionInput.d.vue.ts +4 -0
  9. package/dist/runtime/components/Integrations/ExpressionInput.vue +45 -19
  10. package/dist/runtime/components/Integrations/ExpressionInput.vue.d.ts +4 -0
  11. package/dist/runtime/components/Integrations/GroupFrameView.vue +5 -4
  12. package/dist/runtime/components/Integrations/LoadingAnnouncement.d.vue.ts +38 -0
  13. package/dist/runtime/components/Integrations/LoadingAnnouncement.vue +16 -0
  14. package/dist/runtime/components/Integrations/LoadingAnnouncement.vue.d.ts +38 -0
  15. package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +10 -2
  16. package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +5 -1
  17. package/dist/runtime/components/Integrations/NodeConfigField.vue +88 -1
  18. package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +5 -1
  19. package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +7 -3
  20. package/dist/runtime/components/Integrations/NodeInspector.vue +125 -54
  21. package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +7 -3
  22. package/dist/runtime/components/Integrations/NodeVersionMoveDialog.d.vue.ts +21 -0
  23. package/dist/runtime/components/Integrations/NodeVersionMoveDialog.vue +133 -0
  24. package/dist/runtime/components/Integrations/NodeVersionMoveDialog.vue.d.ts +21 -0
  25. package/dist/runtime/components/Integrations/RunActionsMenu.vue +2 -1
  26. package/dist/runtime/components/Integrations/RunDetailPanel.vue +5 -17
  27. package/dist/runtime/components/Integrations/RunResponseModal.d.vue.ts +30 -8
  28. package/dist/runtime/components/Integrations/RunResponseModal.vue +92 -24
  29. package/dist/runtime/components/Integrations/RunResponseModal.vue.d.ts +30 -8
  30. package/dist/runtime/components/Integrations/RunsTable.d.vue.ts +61 -3
  31. package/dist/runtime/components/Integrations/RunsTable.vue +129 -18
  32. package/dist/runtime/components/Integrations/RunsTable.vue.d.ts +61 -3
  33. package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +10 -1
  34. package/dist/runtime/components/Integrations/StateNamespaceCreateDialog.d.vue.ts +16 -0
  35. package/dist/runtime/components/Integrations/StateNamespaceCreateDialog.vue +111 -0
  36. package/dist/runtime/components/Integrations/StateNamespaceCreateDialog.vue.d.ts +16 -0
  37. package/dist/runtime/components/Integrations/StickyNoteView.vue +1 -1
  38. package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +18 -3
  39. package/dist/runtime/components/Integrations/WorkflowDeleteDialog.d.vue.ts +18 -0
  40. package/dist/runtime/components/Integrations/WorkflowDeleteDialog.vue +70 -0
  41. package/dist/runtime/components/Integrations/WorkflowDeleteDialog.vue.d.ts +18 -0
  42. package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +2 -1
  43. package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +4 -4
  44. package/dist/runtime/components/Integrations/WorkflowEditor.vue +78 -30
  45. package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +17 -6
  46. package/dist/runtime/components/Integrations/WorkflowNodeView.vue +17 -21
  47. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +8 -11
  48. package/dist/runtime/components/Integrations/WorkflowStateNamespacesDialog.d.vue.ts +13 -0
  49. package/dist/runtime/components/Integrations/WorkflowStateNamespacesDialog.vue +216 -0
  50. package/dist/runtime/components/Integrations/WorkflowStateNamespacesDialog.vue.d.ts +13 -0
  51. package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +6 -7
  52. package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +7 -7
  53. package/dist/runtime/components/Integrations/WorkflowsTable.vue +10 -48
  54. package/dist/runtime/components/Integrations/canvasSides.d.ts +48 -0
  55. package/dist/runtime/components/Integrations/canvasSides.js +22 -0
  56. package/dist/runtime/composables/useCredentialTypes.js +2 -6
  57. package/dist/runtime/composables/useIntegrationsAudit.js +2 -2
  58. package/dist/runtime/composables/useIntegrationsCredentials.js +4 -8
  59. package/dist/runtime/composables/useIntegrationsDeadLetters.d.ts +4 -0
  60. package/dist/runtime/composables/useIntegrationsDeadLetters.js +16 -1
  61. package/dist/runtime/composables/useIntegrationsNodes.js +2 -2
  62. package/dist/runtime/composables/useIntegrationsRevisions.js +2 -6
  63. package/dist/runtime/composables/useIntegrationsRuns.d.ts +4 -15
  64. package/dist/runtime/composables/useIntegrationsRuns.js +49 -34
  65. package/dist/runtime/composables/useIntegrationsSchedules.js +2 -2
  66. package/dist/runtime/composables/useIntegrationsSecrets.js +2 -6
  67. package/dist/runtime/composables/useIntegrationsTemplates.js +7 -5
  68. package/dist/runtime/composables/useIntegrationsTriggers.js +2 -2
  69. package/dist/runtime/composables/useIntegrationsWorkflows.js +10 -7
  70. package/dist/runtime/composables/useNodeConfigResolve.js +2 -2
  71. package/dist/runtime/composables/useOAuthConnect.d.ts +42 -0
  72. package/dist/runtime/composables/useOAuthConnect.js +33 -0
  73. package/dist/runtime/data/expressionEditor.d.ts +17 -5
  74. package/dist/runtime/data/expressionEditor.js +11 -5
  75. package/dist/runtime/data/handleId.d.ts +61 -1
  76. package/dist/runtime/data/handleId.js +8 -1
  77. package/dist/runtime/data/integrationNodeCatalog.d.ts +25 -0
  78. package/dist/runtime/data/integrationNodeCatalog.js +8 -0
  79. package/dist/runtime/data/nodeVersionMove.d.ts +72 -0
  80. package/dist/runtime/data/nodeVersionMove.js +65 -0
  81. package/dist/runtime/data/payloadForm.js +3 -2
  82. package/dist/runtime/i18n/messages/de.json +79 -12
  83. package/dist/runtime/i18n/messages/en.json +79 -12
  84. package/dist/runtime/pages/integrations/audit.vue +7 -6
  85. package/dist/runtime/pages/integrations/credentials.vue +9 -21
  86. package/dist/runtime/pages/integrations/index.vue +55 -8
  87. package/dist/runtime/pages/integrations/runs.vue +116 -7
  88. package/dist/runtime/pages/integrations/secrets.vue +2 -1
  89. package/dist/runtime/pages/integrations/templates/[slug].vue +2 -1
  90. package/dist/runtime/pages/integrations/templates/index.vue +18 -15
  91. package/dist/runtime/pages/integrations/workflows/[id].vue +169 -24
  92. package/dist/runtime/pages/integrations/workflows/index.vue +23 -4
  93. package/dist/runtime/types/integrations.d.ts +49 -1
  94. package/dist/runtime/utils/apiError.d.ts +21 -1
  95. package/dist/runtime/utils/apiError.js +1 -1
  96. package/dist/runtime/utils/buildSettle.d.ts +68 -0
  97. package/dist/runtime/utils/buildSettle.js +29 -0
  98. package/dist/runtime/utils/clipboard.d.ts +33 -0
  99. package/dist/runtime/utils/clipboard.js +11 -0
  100. package/dist/runtime/utils/deepClone.d.ts +41 -0
  101. package/dist/runtime/utils/deepClone.js +3 -0
  102. package/dist/runtime/utils/formatDuration.d.ts +44 -0
  103. package/dist/runtime/utils/formatDuration.js +13 -0
  104. package/dist/runtime/utils/nodeDisplayName.d.ts +22 -0
  105. package/dist/runtime/utils/nodeDisplayName.js +6 -0
  106. package/dist/runtime/utils/notify.d.ts +1 -1
  107. package/dist/runtime/utils/runEnvelope.d.ts +31 -0
  108. package/dist/runtime/utils/runEnvelope.js +12 -0
  109. package/dist/runtime/utils/runFeedQuery.d.ts +135 -0
  110. package/dist/runtime/utils/runFeedQuery.js +66 -0
  111. package/dist/runtime/utils/runOutputs.d.ts +33 -0
  112. package/dist/runtime/utils/runOutputs.js +10 -0
  113. package/dist/runtime/utils/settingCondition.d.ts +48 -0
  114. package/dist/runtime/utils/settingCondition.js +84 -0
  115. package/dist/runtime/utils/stateNamespaceName.d.ts +23 -0
  116. package/dist/runtime/utils/stateNamespaceName.js +8 -0
  117. package/dist/runtime/utils/validationFindings.d.ts +26 -3
  118. package/dist/runtime/utils/validationFindings.js +18 -3
  119. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,643 @@
1
1
  # @revenexx/studio-integrations
2
2
 
3
+ ## 1.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 593c0d5: A step can be changed onto another version of its node
8
+
9
+ The editor could say precisely that a step sat on a node version the platform
10
+ cannot run, and name the version that would work — and there it stopped. Getting
11
+ there meant deleting the step and inserting a fresh one, which throws away its
12
+ settings, its name, its note and its connections.
13
+
14
+ The version is now a control wherever there is somewhere to go, and two states
15
+ reach it. Where the pinned version cannot run, the red notice keeps every word it
16
+ had and gains the button it was missing. Where the step runs fine and a newer
17
+ version exists, one grey line in its dialog says so — **and nothing else does**.
18
+ No mark on the canvas card: nothing is wrong there, and a fourth mark for a
19
+ non-problem is what teaches an operator to stop reading marks. A node with a
20
+ single usable version says nothing at all.
21
+
22
+ Older versions are offered too, not only newer ones. A version that turns out to
23
+ behave badly otherwise has no way back.
24
+
25
+ **What the change costs is said before it happens.** The two versions are read
26
+ against the settings the step actually holds, and split three ways: what travels,
27
+ what the chosen version does not have and will be taken out, and what arrives
28
+ needing an eye — a setting it requires and the step has no answer for, or one
29
+ whose type or choices moved. A setting nobody filled in appears nowhere; there is
30
+ nothing to lose. A change that costs nothing says so in a sentence rather than
31
+ showing three empty lists, and where the version being left is no longer in the
32
+ catalogue the answer says the two could not be compared at all.
33
+
34
+ Values the chosen version does not declare are removed rather than left behind.
35
+ The service keeps such a key on save without complaint, so it would otherwise sit
36
+ in the workflow as invisible dead data.
37
+
38
+ Nothing about the step's identity moves: same id, same connections, same name,
39
+ same note, same place on the canvas. It lands as an unsaved change like editing
40
+ any setting, so discarding the editor's changes undoes it.
41
+
42
+ Promised as `specs/node-configuration.md` AC-24 to AC-26. `specs/node-palette.md`
43
+ AC-7 gains the clause that the version it names is somewhere the step can
44
+ actually go, and loses the gap entry that described the dead end.
45
+
46
+ - bb8a80a: Declare what a workflow may remember between runs (PO-374).
47
+
48
+ A new **State** entry in the editor's floating control pane (Shift+T) opens a
49
+ dialog for the blob's `state` block: each namespace with its role — mapping,
50
+ cursor, dedupe or digest — and whether it belongs to this workflow alone or to
51
+ every workflow in the tenant. The engine mints a run's grant from exactly this
52
+ list, so until now a workflow could not reach the state store from the studio at
53
+ all.
54
+
55
+ The role is chosen when the namespace is added and stated as text afterwards:
56
+ entries written with one meaning cannot be read back with another. What sharing
57
+ costs — tenant-wide reads _and_ writes, and a namespace that outlives this
58
+ workflow — is on screen next to the control that chooses it.
59
+
60
+ Also fixes a silent loss in both directions of the workflow mapper, which
61
+ enumerates blob keys one by one: `state` was neither read from the API nor sent
62
+ back on save, so a declaration made anywhere else would have disappeared on the
63
+ next save from the studio.
64
+
65
+ A node setting of the new type `state-ref` picks one of the workflow's declared
66
+ namespaces **of that setting's kind**, and declares a new one from inside the
67
+ picker — the same shape a `credentials-ref` has, so the author never leaves the
68
+ node to type a name that nothing would have compared.
69
+
70
+ A namespace declared inside the picker waits in the node's draft until the
71
+ inspector keeps it — Save, or prev/next — so Esc discards the declaration
72
+ together with the setting that named it, and the workflow does not report
73
+ unsaved changes for something nobody chose to keep.
74
+
75
+ A node's test run now names the workflow being edited, which lets it read that
76
+ workflow's namespaces. Writes stay refused at author time.
77
+
78
+ Promised behaviour: `specs/state-namespaces.md`.
79
+
80
+ - ddff18a: A setting that cannot apply is no longer put in front of the author
81
+
82
+ A node can now say in its manifest when one of its settings applies — another
83
+ setting's key, an operator, and the value to compare against — and the
84
+ configuration form draws it only while that holds. Until now the only thing
85
+ warning an author off a meaningless field was a sentence in small print under
86
+ it, which they read last, having already filled it in. Date Time's **Source**
87
+ is the plainest case: under "Now" it still asked which field to read the date
88
+ out of.
89
+
90
+ What changed on screen:
91
+
92
+ - A setting whose condition does not hold is not drawn, and is not counted
93
+ among the optional ones either — a fold that offers four settings and shows
94
+ three teaches the reader to distrust the number.
95
+ - What was typed into a setting that stops applying is dropped, the same
96
+ cascade the studio already runs for a setting whose options another one
97
+ resolves. A value nowhere on screen is a value nobody can correct.
98
+ - Settings a node resolves while the author edits are treated the same way.
99
+
100
+ The words a condition is written in are the fourteen the condition and filter
101
+ nodes already offer — `equals`, `isEmpty`, `contains`, `>` and the rest — so an
102
+ author who has met them in one place does not learn a second set for the other.
103
+ `specs/node-configuration.md` AC-27 and AC-28 carry the promise.
104
+
105
+ Nothing changes for a node that says nothing: every setting is drawn exactly as
106
+ before. A condition written on a setting _inside a group_ is not honoured here —
107
+ a group is drawn as one value editor rather than as its own settings — and that
108
+ is written into the spec's Gaps rather than left to be discovered.
109
+
110
+ ### Patch Changes
111
+
112
+ - 4dd5b12: The answer that keeps unsaved work is legible, and is the one the question emphasises
113
+
114
+ Leaving a workflow with unsaved changes asks whether to discard them, and the
115
+ question offers two answers. Only one of them looked like an answer: "Keep editing"
116
+ was drawn in a colour its own background shares, so it read as an empty button beside
117
+ "Discard and leave" and its label appeared only once the pointer rested on it. Which
118
+ is the wrong half of the dialog to hide — that button is the one the keyboard lands
119
+ on, so the answer the question exists to make easy was the answer nobody could see.
120
+
121
+ It is now drawn as the filled answer, the way every other dialog in the module draws
122
+ the answer on the right, so the question also says which answer it falls back to. The
123
+ cause was two button variants on one element: the dialog's cancel element applies the
124
+ outline variant itself, and the button inside it was left on the default one, so the
125
+ merge took the background from the first and the text colour from the second. Nothing
126
+ else about the question moves — the wording, the keyboard default and what each answer
127
+ does are unchanged.
128
+
129
+ Promised as `specs/workflow-save.md` AC-13 and AC-14: the label is checked as a
130
+ contrast ratio against the button it sits on, unhovered, and the emphasis as a
131
+ difference between the two answers' surfaces rather than against a class name or a
132
+ colour value.
133
+
134
+ - ab2eb96: A build state stops saying "still building" wherever it is shown
135
+
136
+ Saving a workflow queues the platform to build an executable bundle from it, and
137
+ until that build lands the workflow cannot run. Two surfaces say so: the chip beside
138
+ Save in the editor, and the build column in the workflows list. Only the editor's
139
+ ever became true on its own — the list said whatever its last read said, so a
140
+ workflow that finished building while the list stood open kept reading "Building…"
141
+ until something reloaded the page. Save in the editor, come back to the list, and the
142
+ row never moved.
143
+
144
+ The list now watches a build out the way the editor does: one read of the tenant per
145
+ turn, so thirty workflows building at once cost what one costs, and it stops as soon
146
+ as nothing is waiting. A platform build report still makes it quicker where there is
147
+ one; nothing depends on one arriving any more, on either surface.
148
+
149
+ The two waits now share one interval and one ceiling instead of the editor owning
150
+ both, so neither surface can drift into asking twice as often as the other.
151
+
152
+ Promised as `specs/workflow-build.md`, a new spec for the subject: what becomes of a
153
+ saved workflow before it can run. AC-1 is the editor's promise, moved out of
154
+ `specs/workflow-save.md`, where a reader asking what a build state means would not
155
+ have looked; AC-2 is the list's. `specs/realtime-updates.md` AC-7 keeps the
156
+ announcement half and now says which of the two makes a build state true.
157
+
158
+ ## 1.2.0
159
+
160
+ ### Minor Changes
161
+
162
+ - 3f56bfa: A studio surface that is waiting now says so in words, not only as a shimmer.
163
+
164
+ Seven surfaces drew placeholder blocks while they loaded and said nothing at all —
165
+ which is exactly what they say when there is genuinely nothing to show. Those two
166
+ states ask for opposite reactions: hold on, or go and do something about it. The
167
+ shimmer that separates them for a sighted reader is not information a screen reader
168
+ can reach, so for one reader every one of these surfaces was silent in the same way,
169
+ whether it was loading or empty.
170
+
171
+ The template gallery had been given words of its own; the other six now have them
172
+ too: the dashboard's four panels, the missed-runs list on the Runs page and inside a
173
+ workflow, the version history, the New-workflow dialog's template picker, and the
174
+ dialog that imports a platform SFTP account. Each says what _it_ is waiting for,
175
+ under its own name, and each stops saying it when the answer arrives. Nothing changes
176
+ on screen for anybody who could already see the shimmer.
177
+
178
+ The dashboard's key figures had the sharper half of the problem, and it was worse
179
+ than silence: while the tenant's workflows were still on their way, each tile
180
+ announced itself as "Active workflows: 0" — a figure counted from an empty list,
181
+ which does not read as waiting but as an answer. A tile is now named by what it
182
+ counts until it has counted it.
183
+
184
+ Behind it, one small component owns the announcement — the region that is always in
185
+ the DOM with only its text changing, the naming that keeps several on one page apart,
186
+ and the placeholders' removal from the accessibility tree — so the seventh surface to
187
+ need it cannot get a different version of it than the first.
188
+
189
+ The eight lists that draw the shared table are not in this: their placeholder rows
190
+ come from `@revenexx/studio-shared` and are carried separately.
191
+
192
+ - 69c9e95: The tenant-wide Runs page now pages and narrows on the platform instead of in the
193
+ browser, so a tenant's whole run history is reachable rather than only its newest
194
+ hundred runs.
195
+
196
+ What changes for anyone using the page. Older runs are reached a page at a time,
197
+ and the count under the table is the number of runs that match rather than the
198
+ number of rows loaded — it used to say "of 100" on a tenant with thousands.
199
+ Narrowing by outcome or trigger kind, and searching by workflow name, are applied
200
+ to the history: "what failed last night" no longer searches a window that on a busy
201
+ tenant can be under an hour, and a narrowing applied from a later page starts again
202
+ at the first one. The Missed runs tab pages the same way, where it previously read
203
+ one page and offered no way past it.
204
+
205
+ Two things are deliberately withheld rather than left to mislead. The columns no
206
+ longer offer a sort, because `GET /runs` orders newest first and takes no ordering
207
+ parameter — a sort here would have reordered one page. And the filter menu shows no
208
+ count per option, because a count of the rows in hand would describe the page
209
+ rather than the history the option narrows.
210
+
211
+ The filter control itself is unchanged in shape: the same menu, the same words, one
212
+ value per dimension.
213
+
214
+ Fixed on the way: the outcome filter sent a status the platform does not have.
215
+ The studio's "failed" stands for several of the platform's statuses, and the
216
+ expansion carried `timed_out`, which its `RunStatus` does not list — so choosing
217
+ "failed" answered `422 The selected status.2 is invalid` instead of narrowing.
218
+ The vocabulary now mirrors the enum, the end-to-end mocks refuse what the service
219
+ refuses, and the suite walks every option of every filter rather than a sample.
220
+
221
+ From review: a search taken back inside the debounce window no longer commits
222
+ itself, a read the platform refuses is now said instead of leaving the previous
223
+ answer standing under the new question, a narrowing that matches nothing says so
224
+ rather than claiming the tenant has never run a workflow, the search box is no
225
+ longer left narrowing the list invisibly after a visit elsewhere, the sidebar's
226
+ Runs badge keeps converging while a filter is applied, and the Missed runs tab
227
+ opens on the newest firings when the dashboard links to it.
228
+
229
+ - 1b2a938: The Runs list can be read in an order other than newest first. Ordering by how
230
+ long a run took answers "which of these is slow?", which the page could not
231
+ answer at all: the headers that used to offer it sorted the hundred runs the
232
+ browser was holding, and were withdrawn when the list moved onto the platform.
233
+
234
+ Two of the six columns offer a sort — when the run started, and how long it took
235
+ — and each orders the whole history rather than the page on screen.
236
+ A run the column cannot describe, one that never started or one still running,
237
+ is listed last whichever way the list is read; asking for another order returns
238
+ to the first page, because re-ordering re-numbers them.
239
+
240
+ The other four headers stay fixed. Three are the platform's shape: ordering by
241
+ the workflow's name would need a join on the path the list pages on, the trigger
242
+ kind has four values so it groups rather than orders, and the run id orders by
243
+ nothing a reader would mean.
244
+
245
+ The outcome column is ours. The platform can order by it, and this page does not
246
+ offer it: it would order by the word the platform stores while the column shows
247
+ the studio's own translated word, so the header would sit over an order the
248
+ reader cannot see — and in German not even an alphabetical one.
249
+
250
+ Needs an integrations service that serves `?sort=` on `GET /v1/runs` (PO-371).
251
+ Against an older one the headers are offered and the platform refuses them.
252
+
253
+ - 19829bf: A workflow can be deleted from the page it is open on. Until now the only way to
254
+ get rid of a workflow was to leave the editor, find it again in the list of all
255
+ of them and use its row menu — the editor offered duplicating, pausing and
256
+ saving, and no way to discard the thing it had open.
257
+
258
+ The header carries an actions menu, and Duplicate has moved into it beside
259
+ Delete. The buttons that remain — Cancel, Save — act on the unsaved work in front
260
+ of the operator; the two in the menu make and unmake the workflow that work
261
+ belongs to. Delete is not a button for a reason: it is the one control there that
262
+ cannot be undone, and it has no business a mis-click away from Save. The question
263
+ it asks is the list's own, unchanged: it names the workflow and says that the
264
+ revision history goes permanently with it.
265
+
266
+ Unsaved work on the canvas raises no second question. The draft belonged to a
267
+ workflow that has stopped existing, so the studio takes it along and moves to the
268
+ workflows list rather than offering to keep work that has nowhere left to go.
269
+
270
+ Both surfaces now say so when the platform refuses a deletion: the question stays
271
+ open carrying the reason, and the workflow stays where it is. The list used to
272
+ fail silently here — the dialog simply stopped, over a row that was still there.
273
+
274
+ - b85a8e0: A run started from the workflow editor now ends on what it returned, whichever mode
275
+ the workflow runs in.
276
+
277
+ Pressing Run on a workflow that does not wait for its result used to answer "Run #99
278
+ started" and stop there — the number was the whole answer, and reading what the run
279
+ actually did meant leaving for the run's own record and watching it fill in. The
280
+ studio now stays with that run: the dialog says where it has got to while it is
281
+ going, and shows what it returned when it ends. The mode a workflow runs in decides
282
+ who waits, not whether the author gets an answer.
283
+
284
+ What the result looks like has changed with it. It used to be the run object printed
285
+ whole — two Temporal identifiers, two re-run references and four timestamps around
286
+ the value somebody pressed Run to see, keyed by the node's internal id. Each value
287
+ now stands under the name its node carries on the canvas, with the run's number,
288
+ status and duration on one line above them; the record itself is still one click
289
+ away, where the rest of it belongs. A returned value that is not a map of this
290
+ workflow's nodes is shown whole rather than filed under a name it may not belong to.
291
+
292
+ The way into the run's record is offered from the moment the run starts, so waiting
293
+ here is never the only option. The studio stops following once the run is terminal,
294
+ once the author closes the result, and after a few reads that cannot be answered —
295
+ the confirmation and the way in stand either way.
296
+
297
+ Behind it: one duration formatter for the runs table, the run record and this
298
+ dialog, which had three identical private copies, and one place that decides what a
299
+ node is called, so the result cannot name a node differently from the canvas.
300
+
301
+ - 1704552: A node setting declared as taking several lines is now drawn with room for them.
302
+
303
+ A node type can say that one of its settings runs to more than one line — a mail
304
+ body, a failure message, a list of instructions one per line — and every layer but
305
+ this one carried the declaration: the node contract has held it since it was
306
+ written, and twelve fields across three published packages set it. The editor's own
307
+ field type did not list it, so it was dropped on arrival and every such setting was
308
+ drawn as the same one-line box. The help text under one of them told the author to
309
+ write one instruction per line at a control that refused the second one.
310
+
311
+ Both controls a typed setting can land in now honour it. The plain box becomes a
312
+ text area, and the box that maps values in from earlier steps takes Enter as a new
313
+ line — with expressions going on working line by line, which matters because that is
314
+ where most of the twelve fields land: a notification mail assembled from an upstream
315
+ value is the ordinary case. Such a setting opens at four lines and grows with what is
316
+ typed until ten, after which it scrolls.
317
+
318
+ Nothing has to be edited in a node package for this: the twelve existing
319
+ declarations start working as they were written. A setting that says nothing about
320
+ lines is drawn exactly as before, and a kind of setting with no multi-line meaning —
321
+ a dropdown — ignores the declaration rather than being made to honour it.
322
+
323
+ ### Patch Changes
324
+
325
+ - c94c96e: One place now spells the identifier of a connection point on the canvas, in both
326
+ directions.
327
+
328
+ Nothing changes on screen or in a saved workflow: the ids are byte-identical to the
329
+ ones the canvas drew before, and they were never persisted in a workflow anyway — only
330
+ port names are. What changes is that the twelve places which assembled an id by hand,
331
+ across the node card, the trigger, the group frame, the editor and the two panels that
332
+ rebuild a canvas from a run, now ask one module for it, and the four dot positions are
333
+ declared once instead of six times — including inside the reader, whose own parse is
334
+ built from that one list rather than repeating it.
335
+
336
+ Why it was worth doing while nothing was wrong: the module that reads an id was alone
337
+ there, so a fifth dot position would have had the writers produce a name the reader had
338
+ never heard of, handed back as though it were a port. That is not an empty value, so
339
+ the fallback every call site carries could not have caught it, and the editor would
340
+ have saved an output no node has. The round-trip test now exercises the writers and the
341
+ exported side list rather than a string built in the test, so the two halves are proved
342
+ together.
343
+
344
+ - 31e9a46: The controls the canvas offers on hover are big enough to hit.
345
+
346
+ Deleting a node was the smallest and hardest of them to press, which is the wrong way
347
+ round for the most irreversible thing the editor offers: the button measured 23 by 24
348
+ pixels, a pixel under the minimum a pointer target is allowed to have, and it missed by
349
+ construction rather than by accident — the height was fixed at 24 while the width was
350
+ left to grow from the padding around an 11-pixel glyph. Duplicate beside it, and the
351
+ trigger's delete, were the same.
352
+
353
+ The canvas also disagreed with itself about the size of a toolbar control, in four
354
+ sizes: those two rows, the pair offered on a connection at 20, a sticky note's colour
355
+ swatches at 20, and a group frame's swatches at 16 — the last of them 16 rather than the
356
+ 18 their styling claimed, because an inline size was quietly overriding it. Everything a
357
+ hover reveals on a node, a trigger, a sticky note or a group frame is now 24 by 24 with
358
+ a 13-pixel glyph, so a reader learns one target and keeps it.
359
+
360
+ The toolbar on a connection is raised with the rest and is the one that is still not
361
+ promised: it is drawn in the canvas's own coordinates rather than over the top, so its
362
+ 24 pixels shrink as the operator zooms out. Better than the 20 it was, and a size is not
363
+ the fix — that is carried separately.
364
+
365
+ - 1c461da: A workflow's own Runs tab no longer draws sort headers that do nothing, and the
366
+ tenant-wide Runs page opens on its first page.
367
+
368
+ On a workflow's Runs tab the column headers looked sortable and were not. Started
369
+ and Duration drew a hover state and a sort icon, took the click and did nothing
370
+ with it — the arrow did not move either, and the tab opened with no column marked
371
+ at all. The headers came back with the tenant-wide list's ordering, which the
372
+ platform performs; this tab renders the same table and the platform cannot order
373
+ its feed.
374
+
375
+ They are no longer offered. The alternative was to make them sort what the browser
376
+ holds, and that is a page: this tab pages on the server, so "order by duration"
377
+ would have put the slowest of the twenty-five rows on screen at the top and said
378
+ nothing about the rest of the history — which is the sentence the tenant-wide list
379
+ withdrew its own headers for until the platform could order the whole thing. The
380
+ runs stand newest first, as they always did, and what would bring an order back is
381
+ recorded in the spec rather than mimed by a control.
382
+
383
+ Two things on the tenant-wide Runs page ride along. Arriving at it now opens on the
384
+ first page: the search box comes back empty whatever was searched for, so a page
385
+ number kept from last time was a position inside a narrowing nothing on screen
386
+ still showed — the outcome and trigger kind are drawn back onto the menu and still
387
+ survive. And arriving with a search still in force now costs one read of the list
388
+ rather than the two it used to.
389
+
390
+ - fae7446: A duration now reads the same wherever the studio prints one.
391
+
392
+ The same step used to say `1.50s` on a run's Details tab and `1.5 s` on its Replay
393
+ tab one click away, and a step that took a minute said `1.1m` in the first place and
394
+ `1m 3s` in the second — which reads as two different figures rather than two
395
+ spellings of one. The dashboard spelled it a third way again — two decimals whatever
396
+ the value, so a run of 900 ms read `0.90s` there and `900ms` in the list it links to.
397
+ Five surfaces printed a duration, across three copies of the arithmetic.
398
+
399
+ They now share one, and its form is `900ms`, `1.5s`, `1m 3s`: the unit against the
400
+ number, as in the `10d ago` beside it, a tenth of a second where one was measured, and
401
+ minutes with their seconds above one — where `1.1m` for a 63-second step was the least
402
+ readable of the three candidates and the second decimal of `1.50s` was precision nobody
403
+ acts on.
404
+
405
+ **A run's duration loses its decimal place entirely, because there was never anything in
406
+ it.** The studio derives it from the two timestamps the platform records to the second,
407
+ so `4.00s` was three characters claiming a precision the platform does not store: that
408
+ run reads `4s` now, and one that took a minute reads `1m`. A step keeps its tenth —
409
+ there the milliseconds are measured, so `3.4s` says something `3s` would not. The replay
410
+ panel loses the space before its unit, and the dashboard stops printing seconds for a
411
+ figure that is not in seconds.
412
+
413
+ Two figures that could not come out right before are gone with it: a duration just
414
+ under a minute printed as `60.0s` — a minute said the other way — and the replay
415
+ panel's own arithmetic could produce `1m 60s`.
416
+
417
+ A duration still takes no language, so German is served `1.5s` rather than `1,5 s`.
418
+ That gap is now written down in one place rather than in four.
419
+
420
+ - d63594c: A refused save now states the reason and not the service's count of everything else it
421
+ found — everywhere, rather than on the two credential surfaces it was reported from.
422
+
423
+ The service refuses a save with one message and a tally: "The name has already been
424
+ taken. (and 2 more errors)". That count is honest arithmetic and misleading copy — it
425
+ counts by validation rule, so one missing field the service checks twice is two of them,
426
+ and it counts things no form draws. PO-275 removed it where it was reported. Creating a
427
+ workflow, creating a credential from the node editor's own lightbox and provisioning a
428
+ revenexx source kept showing it, because each had written the error handling out by hand
429
+ and only the credentials page knew the count was there.
430
+
431
+ The secret form gains a fallback it never had: a refusal whose message was empty drew an
432
+ empty sentence there, because nothing stood behind the message at all.
433
+
434
+ Underneath that: one function now answers what an error should say. It had three
435
+ implementations — the one that knew about the count, a byte-identical private
436
+ `captureError` in six composables, and the cast spelled inline at thirty-seven call
437
+ sites. `refusalReason` is `errorReason`, because a function named for refusals was
438
+ answering "could not load the audit log" in half its callers, and the name is why nobody
439
+ reached for it there. The count of call sites is the reason the sweep had to be done
440
+ twice: the same contract was spelled `(e as { message?: string })?.message`,
441
+ `(e as Error).message`, `(e as Error)?.message` and as an `instanceof` guard with the
442
+ message in each branch, and a search for one of those finds one of those.
443
+
444
+ One behaviour changes on the read paths, and it is small: an error whose message is
445
+ nothing but whitespace used to be drawn as an empty sentence and now falls back to the
446
+ surface's own wording.
447
+
448
+ - b0fcc32: Connecting a credential by OAuth now runs through one handshake instead of three copies
449
+ of it.
450
+
451
+ The **Connect** button exists in three places — a row in the credentials list, the
452
+ credential detail dialog, and the create dialog straight after a credential is made —
453
+ and each carried its own copy of the whole dance:
454
+ open the provider's window, listen for the message the return page posts back, and poll
455
+ once a second in case the operator closes the window instead. The two copies agreed, so
456
+ nothing was wrong on screen; what it cost is that the prerequisite for the message path
457
+ ever firing was documented in only one of them, and a fix to one button did not reach
458
+ the other.
459
+
460
+ Three things that were wrong in both copies are fixed by having one:
461
+
462
+ - **The poll kept running after the message arrived.** The listener removed itself and
463
+ reloaded, but the one-second timer stayed up until the window closed, and then
464
+ reloaded a second time. Harmless while the reload is idempotent, which is the sort of
465
+ thing that stops being true quietly.
466
+ - **No copy stopped when its caller went away.** Closing the detail dialog, or leaving
467
+ the page, while the provider's window was still open left a timer and a listener
468
+ running — and the detail dialog is held open by a prop rather than by `v-if`, so
469
+ closing it unmounts nothing. An attempt begun there could announce a connection, and
470
+ refresh the row, for whichever credential the operator opened next.
471
+ - **A second attempt while one was outstanding left the first still watching**, so a
472
+ window the operator had stopped looking at could still trigger a reload.
473
+ - **The completion message was accepted from any sender.** The landing page posts it to
474
+ our own origin, but nothing checked where it came from — and the window we open holds
475
+ a live `window.opener` on us, so a provider page could have announced a connection
476
+ that never happened. The origin is checked now.
477
+ - **A window the browser refuses to open left the poll running for ever.** `window.open`
478
+ returns `null`, `null?.closed` is `undefined`, and neither path could ever settle.
479
+ Nothing is installed in that case now. The operator is still told nothing, which is
480
+ what all three copies did — that is PO-399.
481
+
482
+ What an operator sees is unchanged: the connection is announced when the provider
483
+ answers, and closing the window refreshes quietly without claiming anything — because
484
+ a closed window says nothing about whether the authorisation went through.
485
+
486
+ - f6cb6e2: The deep copy every editing surface takes now comes from one module, and that module
487
+ says what the copy keeps.
488
+
489
+ Nothing changes on screen. Opening a workflow, editing a node, opening the variables
490
+ dialog, duplicating a workflow and building a manual run's payload all begin by copying
491
+ what they are about to let somebody edit, so an unsaved change can be dropped rather
492
+ than undone. That copy was written out eight times — as the same private one-line
493
+ `clone<T>` in the canvas and in the node inspector, and as a bare
494
+ `JSON.parse(JSON.stringify(…))` in six more places. All eight produced the same value,
495
+ and they still do: this is the same round trip, moved.
496
+
497
+ What was missing is the sentence underneath them. The round trip is safe **because a
498
+ workflow blob is JSON** — that is the entire guarantee, and it was written down nowhere,
499
+ so nothing said which inputs it holds for. A caller reaching for the same expression
500
+ with editor state that has not been through the API — a resolved sample, a cached
501
+ schema, anything holding a `Date`, a `Map` or an `undefined` — would lose those values
502
+ silently, in whichever of the eight places they copied from. The guarantee, and the
503
+ five things the round trip does not survive, are now in one doc comment with a unit
504
+ test that pins each of them.
505
+
506
+ `structuredClone` is deliberately not used, and the reason is in the module: it
507
+ preserves all five, which sounds strictly better and is a behaviour change — a field
508
+ dropped today because JSON cannot carry it would start surviving, and something
509
+ downstream may be relying on that loss. Swapping the mechanism is now one edit, and it
510
+ wants its own proof.
511
+
512
+ - 2271245: What the canvas does with a connection dot's side is now three exhaustive maps instead
513
+ of ternary chains scattered across five components.
514
+
515
+ A dot sits on one of four sides, and three separate answers follow from that: where Vue
516
+ Flow draws it, which side an edge arriving from it should land on, and where along the
517
+ side it is spread when a node has several outputs. Each was a chain of `if`s ending in
518
+ an `else`, inside a component: the position one in both the node card and the trigger
519
+ card, and the opposite-side one in the editor and in both read-only canvases.
520
+
521
+ Nothing changes on screen — the four sides get the same answers. What changes is what
522
+ happens to a fifth. An `else` gives a name it has never heard the last branch's answer:
523
+ a `centre` dot would have been drawn on the **left**, told that `right` faces it, and
524
+ spread along the horizontal axis — three plausible answers, no type error, and nothing
525
+ to notice. The maps are `Record<Side, …>`, so adding a name to `SIDES` stops all three
526
+ compiling until they say what it means.
527
+
528
+ This is the follow-up PO-258 wrote down for itself. Its note said three functions were
529
+ duplicated, and named them; two were, and one of those was hiding under a second name
530
+ (`opposite`, not `oppositeSide`) in the two read-only canvases. The note now says so.
531
+
532
+ - 759d21f: A copy control now says what actually happened, and all seven of them say it the same way.
533
+
534
+ The audit log's copy control announced "Copied" the instant it was pressed rather than
535
+ after the copy succeeded. `writeText` returns a promise, and a browser can refuse it —
536
+ a page that is not focused, a context that is not secure, a permission the operator
537
+ declined. The message had already gone up by then, and nothing contradicted it: the
538
+ operator believed they had the entry on their clipboard and pasted whatever was there
539
+ before. It could also do nothing at all — the control guarded on `navigator.clipboard`
540
+ existing and skipped the whole block when it did not, so on a browser without the API
541
+ the button neither copied nor complained.
542
+
543
+ Both are gone. A refused copy now says so, in English and German.
544
+
545
+ One more had the same defect and was found in review: the expression chip in the node
546
+ inspector, which copies an expression when no field is focused. It discarded the promise,
547
+ short-circuited to nothing when the clipboard API was absent, and said "Expression
548
+ copied" either way. It now reports a failure and keeps the useful half of the hint —
549
+ that clicking into a field inserts the expression directly, which still works when the
550
+ clipboard does not.
551
+
552
+ The other five — node config, rotated secret, webhook URL, a variable's expression,
553
+ nodes copied on the canvas — already behaved correctly, each with its own copy of the
554
+ await-and-catch. All seven now go through one helper, so the next one cannot be written
555
+ the wrong way by having nothing to copy from. Each keeps its own two
556
+ messages: copying a node's config, a webhook URL and an audit entry are three different
557
+ things to have failed, and one message covering all of them would name none of them.
558
+
559
+ - 70bbddc: A saved workflow stops claiming its build is queued once it is not.
560
+
561
+ Pressing Save queues a bundle build, and the editor put "Build queued…" on the toolbar
562
+ and left it there — the build finishes on the platform within a second or two, and the
563
+ chip stayed up for as long as the page was open. Reloading showed everything saved. A
564
+ chip that never clears is read as a save that did not happen, and it was reported as
565
+ exactly that.
566
+
567
+ What resolved it before was a platform announcement over the event bus, and nothing
568
+ else — so a host that does not have that channel, one where it is down, and a
569
+ connection that dropped in the second after the save all produced the same permanently
570
+ wrong screen. The announcement is still the fast path; under it, every way into a queued
571
+ build now watches it out, on the same interval and the same ceiling the Run button
572
+ already used when it had to wait a build out: an ordinary save, the resend-as-paused
573
+ after the deactivation dialog, putting an earlier version back, and opening a workflow
574
+ whose build is already running. That last one is the reload an operator reaches for
575
+ when the chip looks stuck, and it used to land on a chip that was stuck for good. A
576
+ build that fails reaches its notice the same way.
577
+
578
+ - 47fedc8: The validation banner and the card's badge now name a setting the way the node's own form names it.
579
+
580
+ A workflow that cannot be activated yet is reported in an amber banner listing the
581
+ settings still to fix, one line per node. Those settings were named by their internal
582
+ key, taken from the path the service reports the finding at — so an operator read
583
+ "Bestellung anlegen — credentials, companyId, customerId" and then opened the node to
584
+ find three fields called **Revenexx-Anmeldedaten**, **Firma** and **Kunde**. The same
585
+ fields, named two ways, on one screen, and in English inside an otherwise German
586
+ interface.
587
+
588
+ The banner asks the node's manifest now, which is where those names already live and
589
+ what the settings form has always rendered. Where there is no name to resolve — a
590
+ package that is gone, or a workflow older than the field it was saved with — the key
591
+ still stands: worse to read, but never the wrong field.
592
+
593
+ The badge on the card — the amber count whose hover lists the settings at fault — says
594
+ the same, and resolves it where it is drawn rather than upstream: the node card already
595
+ watches the manifest catalogue, so a name that arrives late redraws that one card instead
596
+ of re-syncing the whole canvas.
597
+
598
+ The marker inside the node's own settings form is deliberately unchanged. It is keyed by
599
+ the config key, which is how a message finds its input; a translated key would stop
600
+ matching the field it belongs to.
601
+
602
+ - d5838d6: The result of a run started by hand can be read when it is long.
603
+
604
+ The dialog that shows what a run returned capped its width and not its height, so it
605
+ grew as tall as its content: a result of two node outputs came out half a screen taller
606
+ than the screen, ran off the top and the bottom at once, and what stuck out was clipped
607
+ rather than made reachable — including the buttons at its foot, so the only way out was
608
+ the corner cross.
609
+
610
+ The body of the dialog now scrolls, as it already does in the create-workflow,
611
+ node-configuration and SFTP-import dialogs, and a single node's output may take a little
612
+ less of the screen so that two of them plus the footer stay navigable. Nothing about
613
+ what is shown changes.
614
+
615
+ Measured rather than eyeballed: with two outputs of a hundred entries each, the dialog
616
+ came out at 1106 pixels in a 900-pixel window before, and inside the window after.
617
+
618
+ - ea5b275: Picking the same node from the palette again lands it beside the last one instead of on top of it.
619
+
620
+ Clicking a row in the editor's node palette dropped the card in the centre of the view,
621
+ and the centre of the view does not move between two clicks. So five picks in a row
622
+ produced five nodes on one set of coordinates and one visible card — the workflow had
623
+ grown, the canvas said it had not, and the only way to find out was to drag the top card
624
+ away and see the next one underneath it.
625
+
626
+ A click-insert now steps off a spot the canvas already occupies, down and to the right in
627
+ grid steps, until it finds one that is clear. It reads the canvas each time rather than
628
+ counting the clicks so far, which is what makes a spot freed by dragging a card away
629
+ usable again — the next pick goes back to the centre of the view. The empty group frame
630
+ from the control pane, which is centred the same way and stacked the same way, goes
631
+ through the same helper.
632
+
633
+ Notes are deliberately left as they are. A note is drawn behind the cards because it
634
+ belongs over the nodes it explains, so a rule that keeps it clear of everything on the
635
+ canvas would push the first note of a workflow away from the middle of the view — two
636
+ notes in a row still land on one spot, and `specs/sticky-notes.md` says so.
637
+
638
+ Dragging a node out of the palette is deliberately unchanged: that gesture already shows a
639
+ ghost at the exact spot the card will land, so nudging the drop would make the ghost lie.
640
+
3
641
  ## 1.1.1
4
642
 
5
643
  ### Patch Changes
package/README.md CHANGED
@@ -92,7 +92,9 @@ pnpm changeset # record what your change does to the version
92
92
 
93
93
  Branch off `main` as `feature/<desc>`, `fix/<desc>` or `chore/<desc>` — GitHub refuses
94
94
  the first push of any other name, and `main` itself takes a reviewed pull request with
95
- CI green: [`docs/branch-protection.md`](docs/branch-protection.md).
95
+ CI green: [`docs/branch-protection.md`](docs/branch-protection.md). A separate,
96
+ non-blocking `Security Scan` reports secrets and dependency advisories:
97
+ [`docs/security-scanning.md`](docs/security-scanning.md).
96
98
 
97
99
  Hosts to develop against:
98
100
 
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@revenexx/studio-integrations",
3
3
  "configKey": "studioIntegrations",
4
- "version": "1.1.1",
4
+ "version": "1.3.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"