@w6w/ui 0.2.11 → 0.2.55

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 (138) hide show
  1. package/CHANGELOG.md +139 -2
  2. package/README.md +28 -10
  3. package/dist/AppPicker.d.ts +12 -1
  4. package/dist/CanvasContextMenu.d.ts +19 -0
  5. package/dist/{CodeBlock-BqXDA35P.js → CodeBlock-C-oIIYfC.js} +1436 -555
  6. package/dist/CodeBlock-C-oIIYfC.js.map +1 -0
  7. package/dist/CodeBlock-CymlfyeA.cjs +15 -0
  8. package/dist/CodeBlock-CymlfyeA.cjs.map +1 -0
  9. package/dist/CodeEditor.d.ts +4 -1
  10. package/dist/ConditionEditor.d.ts +11 -0
  11. package/dist/JsonEditor.d.ts +12 -0
  12. package/dist/ParamCustomization.d.ts +5 -0
  13. package/dist/ParamsForm.d.ts +11 -3
  14. package/dist/RouterEditor.d.ts +5 -0
  15. package/dist/StepBuilderModal.d.ts +83 -13
  16. package/dist/StepNameEditor.d.ts +11 -0
  17. package/dist/StopEditor.d.ts +5 -0
  18. package/dist/TriggerFillForm.d.ts +14 -1
  19. package/dist/WebhookListenPanel.d.ts +28 -0
  20. package/dist/WorkflowConnections.d.ts +29 -0
  21. package/dist/WorkflowConnectionsPanel.d.ts +5 -0
  22. package/dist/WorkflowFlowEditor.d.ts +67 -8
  23. package/dist/assets/script-completion.worker-DDJKkj5M.js +8290 -0
  24. package/dist/assets/script-completion.worker-DDJKkj5M.js.map +1 -0
  25. package/dist/canvas-context-menu.d.ts +27 -0
  26. package/dist/code.cjs +1 -1
  27. package/dist/code.mjs +3 -4
  28. package/dist/code.mjs.map +1 -1
  29. package/dist/components/AnchoredFlyout.d.ts +7 -0
  30. package/dist/components/ColumnsEditor.d.ts +27 -0
  31. package/dist/components/Combobox.d.ts +12 -1
  32. package/dist/components/ConfirmModal.d.ts +3 -1
  33. package/dist/components/CsvPanel.d.ts +21 -0
  34. package/dist/components/DataEntryEditor.d.ts +38 -0
  35. package/dist/components/DataEntryList.d.ts +33 -0
  36. package/dist/components/DataStoreList.d.ts +33 -0
  37. package/dist/components/DataTableGrid.d.ts +46 -0
  38. package/dist/components/EventbriteTriggerPicker.d.ts +7 -0
  39. package/dist/components/ExecutionDetail.d.ts +17 -1
  40. package/dist/components/ExecutionList.d.ts +19 -1
  41. package/dist/components/ExecutionLogPanel.d.ts +61 -3
  42. package/dist/components/ExpressionEditorModal.d.ts +33 -10
  43. package/dist/components/ExpressionOptions.d.ts +101 -15
  44. package/dist/components/HtmlPreview.d.ts +7 -0
  45. package/dist/components/JsonTree.d.ts +32 -0
  46. package/dist/components/ResolvedParams.d.ts +6 -1
  47. package/dist/components/ResultViewer.d.ts +46 -0
  48. package/dist/components/ScriptBindingsNotice.d.ts +31 -0
  49. package/dist/components/ScriptCodeField.d.ts +13 -0
  50. package/dist/components/StepStatusPill.d.ts +7 -0
  51. package/dist/components/expression-authoring.d.ts +13 -0
  52. package/dist/components/expression-dom.d.ts +17 -2
  53. package/dist/components/expression-template.d.ts +25 -3
  54. package/dist/components/icons.d.ts +1 -1
  55. package/dist/components/json-compose.d.ts +53 -0
  56. package/dist/components/result-format.d.ts +13 -0
  57. package/dist/condition-editor.d.ts +44 -0
  58. package/dist/connection-app-meta-BEXlKBgV.cjs +22 -0
  59. package/dist/connection-app-meta-BEXlKBgV.cjs.map +1 -0
  60. package/dist/connection-app-meta-ZCPakGjD.js +12422 -0
  61. package/dist/connection-app-meta-ZCPakGjD.js.map +1 -0
  62. package/dist/data-stores/data-store-view.d.ts +371 -0
  63. package/dist/eventbrite-trigger-selection.d.ts +13 -0
  64. package/dist/flow-connect.d.ts +22 -16
  65. package/dist/flow-delete.d.ts +16 -0
  66. package/dist/flow-history.d.ts +75 -0
  67. package/dist/flow-layout.d.ts +13 -0
  68. package/dist/flow-loop-colors.d.ts +39 -0
  69. package/dist/flow-loop.d.ts +98 -0
  70. package/dist/flow-node-flags.d.ts +14 -0
  71. package/dist/flow-problems.d.ts +69 -0
  72. package/dist/flow-state.d.ts +20 -0
  73. package/dist/flow-types.d.ts +137 -9
  74. package/dist/flow-utils.d.ts +27 -13
  75. package/dist/flow.cjs +4 -2
  76. package/dist/flow.cjs.map +1 -1
  77. package/dist/flow.d.ts +9 -3
  78. package/dist/flow.mjs +4028 -1585
  79. package/dist/flow.mjs.map +1 -1
  80. package/dist/index.cjs +2 -1
  81. package/dist/index.cjs.map +1 -1
  82. package/dist/index.d.ts +30 -4
  83. package/dist/index.mjs +2559 -1287
  84. package/dist/index.mjs.map +1 -1
  85. package/dist/node-samples.d.ts +80 -0
  86. package/dist/output-shape.d.ts +30 -14
  87. package/dist/param-customization.d.ts +7 -0
  88. package/dist/provider.d.ts +119 -7
  89. package/dist/resolve-params.d.ts +45 -11
  90. package/dist/run-visuals.d.ts +14 -21
  91. package/dist/script-bindings.d.ts +63 -0
  92. package/dist/script-completion-service.d.ts +9 -0
  93. package/dist/script-completion.worker.d.ts +1 -0
  94. package/dist/script-completions.d.ts +2 -0
  95. package/dist/state-shape.d.ts +38 -0
  96. package/dist/step-preview-state.d.ts +155 -6
  97. package/dist/step-problems.d.ts +42 -12
  98. package/dist/test-inputs.d.ts +25 -0
  99. package/dist/trigger-registration.d.ts +28 -0
  100. package/dist/types.d.ts +70 -8
  101. package/dist/use-app-action-defs.d.ts +9 -1
  102. package/dist/use-test-input.d.ts +18 -0
  103. package/dist/workflow-connections.d.ts +78 -0
  104. package/package.json +16 -7
  105. package/src/code.css +60 -6
  106. package/src/styles/_canvas.scss +35 -0
  107. package/src/styles/_code-editor.scss +35 -0
  108. package/src/styles/_code.scss +6 -5
  109. package/src/styles/_connections.scss +64 -0
  110. package/src/styles/_copyable.scss +24 -0
  111. package/src/styles/_data-stores.scss +138 -0
  112. package/src/styles/_data-vars.scss +17 -6
  113. package/src/styles/_execution-history.scss +73 -0
  114. package/src/styles/_execution-log.scss +145 -1
  115. package/src/styles/_expression-modal.scss +106 -16
  116. package/src/styles/_fields.scss +11 -0
  117. package/src/styles/_flow.scss +166 -0
  118. package/src/styles/_fx.scss +3 -1
  119. package/src/styles/_incoming-state.scss +33 -0
  120. package/src/styles/_modal.scss +12 -0
  121. package/src/styles/_multiselect.scss +35 -0
  122. package/src/styles/_sections.scss +36 -0
  123. package/src/styles/_step-builder.scss +145 -0
  124. package/src/styles/_tester.scss +56 -0
  125. package/src/styles/_tokens.scss +27 -1
  126. package/src/styles.css +1347 -41
  127. package/src/styles.scss +2 -0
  128. package/dist/CodeBlock-BqXDA35P.js.map +0 -1
  129. package/dist/CodeBlock-CulfbnZD.cjs +0 -15
  130. package/dist/CodeBlock-CulfbnZD.cjs.map +0 -1
  131. package/dist/ConfirmModal-Ln99S9NT.js +0 -7919
  132. package/dist/ConfirmModal-Ln99S9NT.js.map +0 -1
  133. package/dist/ConfirmModal-fjHW5rN-.cjs +0 -14
  134. package/dist/ConfirmModal-fjHW5rN-.cjs.map +0 -1
  135. package/dist/Copyable-DA6eiU7w.cjs +0 -2
  136. package/dist/Copyable-DA6eiU7w.cjs.map +0 -1
  137. package/dist/Copyable-F4NTaG77.js +0 -847
  138. package/dist/Copyable-F4NTaG77.js.map +0 -1
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Persisted node samples — the server's record of the last output SHAPE (and, when small enough,
3
+ * the value) each node produced, from a step Test, a node run or a full run
4
+ * (`GET /workflows/:id/node-samples`). The editor loads them once per workflow so the picker,
5
+ * the ƒx previews and the Test tab still offer an upstream node's keys after a reload, a reopen or
6
+ * another browser, without re-running anything.
7
+ *
8
+ * They are a FALLBACK under what the editor already holds: a fresh in-memory output (a saved
9
+ * step-test's `lastRunOutput`) wins, unless the persisted sample is strictly newer. Field
10
+ * inference (`inferOutputFields`) is the same either way — a skeleton carries the same keys as
11
+ * the real value it was taken from.
12
+ *
13
+ * Pure and JSX-free so `node --test` exercises it directly.
14
+ */
15
+ /** A node output's structural shape, as the server records it. */
16
+ export type NodeShape = {
17
+ type: "string" | "number" | "boolean" | "null" | "unknown";
18
+ } | {
19
+ type: "object";
20
+ fields: Record<string, NodeShape>;
21
+ } | {
22
+ type: "array";
23
+ items?: NodeShape;
24
+ };
25
+ /** One persisted node sample (`GET /workflows/:id/node-samples` → `nodeSamples[]`). */
26
+ export interface NodeSample {
27
+ nodeId: string;
28
+ shape: NodeShape;
29
+ /** The output itself, when the server kept it (omitted when too large — `sampleOmitted`). */
30
+ sample?: unknown;
31
+ sampleOmitted?: true;
32
+ source: "step_test" | "node_run" | "run";
33
+ runId?: string;
34
+ /** ISO timestamp of the write. */
35
+ updatedAt: string;
36
+ }
37
+ /**
38
+ * A representative value of `shape`: `""`, `0`, `false`, `null`, an object of its fields, an
39
+ * array of one item skeleton (`[]` when the item shape is unknown). `unknown` is `null`.
40
+ */
41
+ export declare function shapeToSkeleton(shape: NodeShape | undefined): unknown;
42
+ /** Index `samples` by node id (the last entry for a repeated id wins). */
43
+ export declare function indexNodeSamples(samples: readonly NodeSample[] | undefined): Record<string, NodeSample>;
44
+ /**
45
+ * One node's effective output for SHAPE inference: the fresh in-memory output wins (unless the
46
+ * persisted sample is strictly newer than `freshAt`), else the persisted `sample`, else
47
+ * {@link shapeToSkeleton} of its shape. `undefined` when neither exists.
48
+ */
49
+ export declare function effectiveNodeOutput(nodeId: string, fresh: Record<string, unknown>, persisted: Record<string, NodeSample>, freshAt?: Record<string, string | null | undefined>): unknown;
50
+ /**
51
+ * {@link effectiveNodeOutput} for every node with a fresh or persisted output — the map the
52
+ * editor hands `StepShapeContext.testOutputs`, so a downstream node's picker offers the
53
+ * persisted keys after a reload.
54
+ */
55
+ export declare function effectiveNodeOutputs(fresh: Record<string, unknown>, persisted: Record<string, NodeSample>, freshAt?: Record<string, string | null | undefined>): Record<string, unknown>;
56
+ /**
57
+ * The outputs the Result PREVIEWS read: real values only — never a skeleton, which would preview
58
+ * a made-up `""`/`0`. Same precedence as {@link effectiveNodeOutput}; a newer persisted sample
59
+ * with no value falls back to the fresh one.
60
+ */
61
+ export declare function previewNodeOutputs(fresh: Record<string, unknown>, persisted: Record<string, NodeSample>, freshAt?: Record<string, string | null | undefined>): Record<string, unknown>;
62
+ /**
63
+ * {@link effectiveNodeOutputs} split into its two tiers, so the editor can hand the picker
64
+ * `testOutputs` (fresh) and `persistedOutputs` (persisted) SEPARATELY and show the right hint.
65
+ * A persisted sample wins only when there is no fresh output or it is strictly newer than
66
+ * `freshAt`; then the fresh entry is dropped from `fresh` and the persisted shape (its kept
67
+ * value, else its skeleton) goes in `persisted`. Elsewhere `fresh` is the fresh map unchanged.
68
+ */
69
+ export declare function splitNodeOutputs(fresh: Record<string, unknown>, persisted: Record<string, NodeSample>, freshAt?: Record<string, string | null | undefined>): {
70
+ fresh: Record<string, unknown>;
71
+ persisted: Record<string, unknown>;
72
+ };
73
+ /**
74
+ * {@link previewNodeOutputs} split into tiers: `persisted` holds only REAL kept values of the
75
+ * persisted samples that win (never a skeleton); `fresh` is the fresh map minus those ids.
76
+ */
77
+ export declare function splitPreviewOutputs(fresh: Record<string, unknown>, persisted: Record<string, NodeSample>, freshAt?: Record<string, string | null | undefined>): {
78
+ fresh: Record<string, unknown>;
79
+ persisted: Record<string, unknown>;
80
+ };
@@ -1,13 +1,11 @@
1
1
  /**
2
2
  * An upstream step's output SHAPE — the field paths a later step can pick —
3
- * from the three places one can come from, in precedence order:
4
- *
5
- * 1. **Declared** — the app action's `output` (`core/rfcs/action.md`
6
- * `OutputField[]`), so a downstream step can reference `get-event`'s
7
- * `name.text` before anything has run.
8
- * 2. **Sample** — the action's `sample` value, when it declares no `output`.
9
- * 3. **Test run** — the step's last saved test output, when the action
10
- * declares neither. With none of the three, the author has to run a test.
3
+ * from the places one can come from. A real shape (a fresh test output, else
4
+ * the persisted one) is the BASE and keeps every key; the app's declared
5
+ * `output` (`core/rfcs/action.md` `OutputField[]`) annotates it and adds its
6
+ * declared-only keys (muted). With no real shape the declared `output` drives
7
+ * the keys alone, then keys inferred from the action's `sample`. With none of
8
+ * these, the author has to run a test.
11
9
  *
12
10
  * Every field is a dot-separated PATH (`start.utc`), the spec's own notation
13
11
  * for nesting (`OutputField.key`: "Dot notation for nested paths"). That is
@@ -24,15 +22,28 @@ export interface OutputFieldRef {
24
22
  label?: string;
25
23
  /** `true` — `key` is a dot path whose segments are checked one by one. */
26
24
  path: true;
25
+ /**
26
+ * `true` when a real (fresh/persisted) shape exists and this key came ONLY from the app's
27
+ * declaration — the picker adds it muted, "declared by the app".
28
+ */
29
+ declaredOnly?: true;
27
30
  }
28
- /** Where a step's offered fields came from. */
29
- export type OutputShapeSource = "declared" | "sample" | "test";
31
+ /**
32
+ * Where a step's offered fields came from: a fresh test output, the persisted test shape,
33
+ * the app's declared `output`, or keys inferred from the app's `sample`.
34
+ */
35
+ export type OutputShapeSource = "fresh" | "persisted" | "declared" | "sample";
30
36
  /**
31
37
  * The fields an action's `output` declares, or `undefined` when it declares
32
38
  * none. A `DynamicOutput` (`{ source }`) is resolved by a hook the editor does
33
39
  * not run, so it counts as "nothing declared" and the test-run fallback applies.
34
40
  */
35
41
  export declare function declaredOutputFields(output: unknown): OutputFieldRef[] | undefined;
42
+ /**
43
+ * The item keys a declared array field's `items` lists (`undefined` when the key is not a
44
+ * declared array field with items) — what a never-run For each over that array iterates.
45
+ */
46
+ export declare function declaredItemFields(output: unknown, key: string): OutputFieldRef[] | undefined;
36
47
  /**
37
48
  * Field paths inferred from a concrete value (an action's `sample` or a test
38
49
  * run's output): every key of a plain object, recursing into nested plain
@@ -48,11 +59,16 @@ export interface ActionShapeDef {
48
59
  sample?: unknown;
49
60
  }
50
61
  /**
51
- * Resolve a step's offered fields by the precedence above. `action` is the
52
- * app action definition (`undefined` while its app's actions are loading, or
53
- * for a node that has none); `testOutput` is the step's last saved test output.
62
+ * Resolve a step's offered fields. KEYS MERGE and never hide:
63
+ * - with a real shape (the fresh test output, else the persisted one) every real key is
64
+ * offered; the app's declared `output` annotates matching keys (label) and its
65
+ * declared-only keys are appended with `declaredOnly: true`;
66
+ * - with no real shape, the declared `output` drives the keys alone (`declared`), else
67
+ * the keys inferred from `sample` (`sample`).
68
+ * `action` is the app action/trigger definition (`undefined` while its app is loading);
69
+ * `fresh` / `persisted` are SEPARATE inputs so the tier stays visible.
54
70
  */
55
- export declare function resolveOutputShape(action: ActionShapeDef | undefined, testOutput: unknown): {
71
+ export declare function resolveOutputShape(action: ActionShapeDef | undefined, fresh: unknown, persisted?: unknown): {
56
72
  fields: OutputFieldRef[];
57
73
  from: OutputShapeSource;
58
74
  } | undefined;
@@ -0,0 +1,7 @@
1
+ import { ResolveScope } from './resolve-params.ts';
2
+ /** Authored JSON customization is kept apart from the editable form values. */
3
+ export declare function hasParamCustomization(values: Record<string, unknown>): boolean;
4
+ /** null: invalid; undefined: a binding whose fields can only be checked at runtime. */
5
+ export declare function customizedParamValues(values: Record<string, unknown>): Record<string, unknown> | undefined | null;
6
+ /** Resolve authored children only. A substituted object is data, not another template. */
7
+ export declare function previewCustomization(value: unknown, scope: ResolveScope, literalKeys?: readonly string[]): unknown;
@@ -1,5 +1,7 @@
1
1
  import { ReactNode } from 'react';
2
- import { ActionDef, ApiCallRecord, AppSummary, AuthDef, ConnectionBinding, ConnectionSummary, CreateConnectionBody, CredentialDependents, CredentialSummary, CredentialTestResult, FunctionDetail, FunctionSummary, ResolveFieldsBody, ResolveFieldsResult, SavedTest, SubscriptionSummary, ThemeMode, TriggerSummary, WorkflowDetail, WorkflowSummary } from './types.ts';
2
+ import { NodeSample } from './node-samples.ts';
3
+ import { NodeTestInput } from './test-inputs.ts';
4
+ import { ActionDef, ApiCallRecord, AppSummary, AuthDef, ConnectionBinding, ConnectionSummary, CreateConnectionBody, CredentialDependents, CredentialSummary, CredentialTestResult, FunctionDetail, FunctionSummary, ResolveFieldsBody, ResolveFieldsResult, SavedTest, SubscriptionSummary, ThemeMode, TriggerSummary, WebhookListenSession, WorkflowDetail, WorkflowSummary } from './types.ts';
3
5
  /**
4
6
  * A single tester-run summary from the unified `run_log` ledger, keyed to a
5
7
  * connection. Thin by design — the full result payload stays server-side in
@@ -75,6 +77,14 @@ export interface StepStartState {
75
77
  trigger?: {
76
78
  event?: unknown;
77
79
  };
80
+ /**
81
+ * The iteration scope of a step inside a For each body → `foreach.item` / `foreach.index`
82
+ * (on the wire `state.foreach = { item, index }`, index defaulting to 0).
83
+ */
84
+ foreach?: {
85
+ item?: unknown;
86
+ index?: number;
87
+ };
78
88
  }
79
89
  /**
80
90
  * A caller-supplied override merged into an app action's outbound request at
@@ -111,6 +121,8 @@ export interface ListAppsPageOptions {
111
121
  q?: string;
112
122
  /** Server-side category filter (e.g. `"ai"`). */
113
123
  category?: string;
124
+ /** Server-side filter to apps declaring at least one trigger (`GET /apps?hasTriggers=true`); `total` is then the filtered total. */
125
+ hasTriggers?: boolean;
114
126
  /** Server-side id allow-list (`GET /apps?ids=a,b`); `total` is then the filtered total. */
115
127
  ids?: string[];
116
128
  /** Opaque pagination cursor from a prior {@link AppsPageResult.nextCursor}. */
@@ -137,6 +149,20 @@ export interface AppsPageResult {
137
149
  limit?: number;
138
150
  expiresAt?: string;
139
151
  }
152
+ /**
153
+ * One page of a data store's entries for the `data-store-key` picker
154
+ * ({@link W6WApi.listDataStoreEntries}). `value` is the stored JSON, which the
155
+ * picker shortens to a one-line preview; `updatedAt` is an ISO timestamp.
156
+ */
157
+ export interface DataStoreEntryOptions {
158
+ items: {
159
+ key: string;
160
+ value?: unknown;
161
+ updatedAt?: string;
162
+ }[];
163
+ /** True when further matching entries exist past this page. */
164
+ more?: boolean;
165
+ }
140
166
  export interface W6WApi {
141
167
  /**
142
168
  * List registered apps to pick from in the connection modal.
@@ -189,6 +215,34 @@ export interface W6WApi {
189
215
  model: string;
190
216
  status: "active" | "broken";
191
217
  }[]>;
218
+ /**
219
+ * OPTIONAL: the project's data stores, for the `data-store` step-param
220
+ * picker (`@w6w/store`). Absent, the param renders as a plain text input and
221
+ * this is never called. Archived stores are not listed by the host.
222
+ * `project` is the editor's own project (`WorkflowProjectProvider`) when it
223
+ * has one — a host should list THAT project's stores, not guess one from its
224
+ * URL; `undefined` leaves the choice to the host.
225
+ */
226
+ listDataStores?(project?: string): Promise<{
227
+ name: string;
228
+ kind: string;
229
+ }[]>;
230
+ /**
231
+ * OPTIONAL: ONE page of a data store's entries, for the `data-store-key`
232
+ * step-param picker (`@w6w/store` get/set/delete/increment). Absent, the key
233
+ * renders as a plain text input and this is never called.
234
+ * `store` is the store's NAME (what the step's `store` param holds) — the
235
+ * host resolves it to its own id. `prefix` is the server-side key search
236
+ * (the host's `GET /data-stores/:id/entries?prefix=`); `limit` caps the page,
237
+ * so a picker never loads a whole store. `more` is true when the store holds
238
+ * further matches past this page. `project` follows the `listDataStores` rule.
239
+ */
240
+ listDataStoreEntries?(store: string, opts: {
241
+ project?: string;
242
+ prefix?: string;
243
+ limit?: number;
244
+ signal?: AbortSignal;
245
+ }): Promise<DataStoreEntryOptions>;
192
246
  /**
193
247
  * OPTIONAL: resolve the choices of `options: { source: "resolveFields" }`
194
248
  * params from the current form (`POST` to the host). Absent, those params
@@ -275,6 +329,11 @@ export interface W6WApi {
275
329
  project?: string;
276
330
  state?: StepStartState;
277
331
  overrides?: RequestOverrides;
332
+ /**
333
+ * The workflow the step belongs to, so the host resolves `nodes.<id>.config` (e.g. a
334
+ * webhook node's `url`) as on a run. The host reads only the caller's own workflows.
335
+ */
336
+ workflowId?: string;
278
337
  }): Promise<{
279
338
  value: unknown;
280
339
  logs?: string[];
@@ -354,6 +413,28 @@ export interface W6WApi {
354
413
  * `/workflows/:workflowId/steps/:stepId/tests`.
355
414
  */
356
415
  listStepTests(workflowId: string, stepId: string): Promise<StepTest[]>;
416
+ /**
417
+ * OPTIONAL. The workflow's persisted node samples — each node's last output shape (and value,
418
+ * when kept) from a step Test, a node run or a full run, written server-side on every one of
419
+ * them. The flow editor loads them once per workflow so upstream keys survive a reload
420
+ * (`node-samples.ts`); a host without it behaves exactly as before. GETs
421
+ * `/workflows/:workflowId/node-samples` → `{ nodeSamples }`.
422
+ */
423
+ loadNodeSamples?(workflowId: string): Promise<NodeSample[]>;
424
+ /**
425
+ * OPTIONAL. The test input each node of the workflow last ran with in the editor (a trigger's
426
+ * filled fields, any other node's incoming state) — so the Run node modal, the Test tabs and the
427
+ * header Run reopen prefilled (`test-inputs.ts`). Server-side, never in the workflow definition.
428
+ * GETs `/workflows/:workflowId/test-inputs` → `{ testInputs }`. A host without it falls back to
429
+ * the latest step-test fixture, as before.
430
+ */
431
+ loadTestInputs?(workflowId: string): Promise<NodeTestInput[]>;
432
+ /**
433
+ * OPTIONAL. Remember the node's test input (newest wins; `{}` is a CLEAR). The server drops
434
+ * sealed envelopes; the caller drops literal secrets first (`rememberableInput`). PUTs
435
+ * `/workflows/:workflowId/nodes/:nodeId/test-input` `{ input }`.
436
+ */
437
+ saveTestInput?(workflowId: string, nodeId: string, input: Record<string, unknown>): Promise<void>;
357
438
  /**
358
439
  * List the caller's registered Functions (core rfcs/function.md) — drives
359
440
  * the step builder's Functions tab (F-2.0). GETs `/functions`.
@@ -403,11 +484,11 @@ export interface W6WApi {
403
484
  }>;
404
485
  /**
405
486
  * List apps that declare at least one trigger — the Triggers tab's
406
- * "App triggers" section (T-0). GETs whatever the host resolves to (e.g.
407
- * apps filtered by `triggerCount > 0`).
487
+ * Apps filtered by `triggerCount > 0`. UNUSED by the step builder: the Triggers
488
+ * tab resolves connected apps via `listAppsByIds` and searches via
489
+ * `listAppsPage({ hasTriggers: true })`. The member stays on the public surface.
408
490
  *
409
- * OPTIONAL, like `listTestRuns?` above: a host that hasn't wired this member
410
- * still typechecks and simply doesn't render the App-triggers section.
491
+ * OPTIONAL, like `listTestRuns?` above.
411
492
  */
412
493
  listTriggerApps?(): Promise<AppSummary[]>;
413
494
  /**
@@ -420,6 +501,31 @@ export interface W6WApi {
420
501
  * `/workflows/:id/subscriptions`.
421
502
  */
422
503
  listSubscriptionsForWorkflow?(workflowId: string): Promise<SubscriptionSummary[]>;
504
+ /**
505
+ * "Listen for test event" on a `@w6w/webhook` subscription: opens a short-lived session in
506
+ * which the receive URL captures the next request, even for an unpublished workflow. POSTs
507
+ * `/subscriptions/:id/listen`.
508
+ */
509
+ startWebhookListen?(subscriptionId: string): Promise<{
510
+ session: WebhookListenSession;
511
+ webhookUrl: string;
512
+ }>;
513
+ /**
514
+ * Long-poll a listen session (`GET /subscriptions/:id/listen/:sessionId?wait=`). Resolves as
515
+ * soon as a request is captured or the session expires, else after `waitSeconds` (max 25).
516
+ */
517
+ pollWebhookListen?(subscriptionId: string, sessionId: string, opts?: {
518
+ waitSeconds?: number;
519
+ signal?: AbortSignal;
520
+ }): Promise<WebhookListenSession>;
521
+ /** Stop listening (`DELETE /subscriptions/:id/listen/:sessionId`). */
522
+ stopWebhookListen?(subscriptionId: string, sessionId: string): Promise<void>;
523
+ /**
524
+ * Retry a failed trigger registration. POSTs
525
+ * `/subscriptions/:id/retry-registration`; resolves the updated Subscription.
526
+ * Absent => the trigger node offers no Retry.
527
+ */
528
+ retrySubscriptionRegistration?(subscriptionId: string): Promise<SubscriptionSummary>;
423
529
  /**
424
530
  * Create a Subscription binding an app trigger to a workflow — the one
425
531
  * explicit user action both the Triggers-tab app section (T-0) and the
@@ -480,10 +586,16 @@ export declare function useW6WApi(): W6WApi;
480
586
  * throwing — for optional enhancements (e.g. the `model-route` picker).
481
587
  */
482
588
  export declare function useOptionalW6WApi(): W6WApi | undefined;
483
- /** Scopes test-invokes under it to `project`; the editor wraps its body with this. */
484
- export declare function WorkflowProjectProvider({ project, children, }: {
589
+ /**
590
+ * Scopes test-invokes under it to `project`, and names `workflowId` on them; the editor wraps
591
+ * its body with this.
592
+ */
593
+ export declare function WorkflowProjectProvider({ project, workflowId, children, }: {
485
594
  project?: string;
595
+ workflowId?: string;
486
596
  children: ReactNode;
487
597
  }): import("react").JSX.Element;
488
598
  /** The selected workflow project id in scope, or `undefined` outside the editor. */
489
599
  export declare function useWorkflowProject(): string | undefined;
600
+ /** The edited workflow's id in scope, or `undefined` outside the editor. */
601
+ export declare function useWorkflowId(): string | undefined;
@@ -18,6 +18,16 @@ export type ResolvedSegment = {
18
18
  } | {
19
19
  status: "unresolved";
20
20
  ref: string;
21
+ }
22
+ /**
23
+ * A `nodes.<id>.meta.config.<key>` (alias `nodes.<id>.config.<key>`) ref this client cannot
24
+ * know — a key no node configures here, or one the host fills only at run time — or a
25
+ * `nodes.<id>.meta.<key>` its static meta lacks (a `version` the canvas does not know). Neutral, not a failure: the engine
26
+ * resolves it on the run.
27
+ */
28
+ | {
29
+ status: "at-run";
30
+ ref: string;
21
31
  } | {
22
32
  status: "not-evaluated";
23
33
  ref?: string;
@@ -29,11 +39,19 @@ export type ResolvedSegment = {
29
39
  * The scope a param's `var` refs resolve against. Built by {@link buildResolveScope}
30
40
  * from the SAME two things `StepEditModal` already computes: `testStartState`
31
41
  * (for `steps.*`/`trigger.*` — the object literally sent as the test's `state`)
32
- * and `sampleValues` (for `vars.*`/`documents.*`).
42
+ * and `sampleValues` (for `var.*`/`documents.*`/`const.*`).
43
+ *
44
+ * The fields mirror the engine's `RunScope` (`vars`, `constants`); the roots a ref
45
+ * names are what the engine's data root exposes — `var.*` (with `vars.*` as its
46
+ * permanent alias) and `const.*`. {@link resolveRef} maps one onto the other.
33
47
  */
34
48
  export interface ResolveScope {
35
49
  vars: Record<string, unknown>;
50
+ /** The SAME object as {@link vars}: `state`, `var` and `vars` are one tree (optional: hand-built scopes omit it). */
51
+ state?: Record<string, unknown>;
36
52
  documents: Record<string, unknown>;
53
+ /** Account constants, read as `const.<name>`. */
54
+ constants?: Record<string, unknown>;
37
55
  steps: Record<string, {
38
56
  output?: unknown;
39
57
  meta?: StepMeta;
@@ -46,6 +64,14 @@ export interface ResolveScope {
46
64
  trigger: {
47
65
  event?: unknown;
48
66
  };
67
+ /**
68
+ * The For each iteration scope (`foreach.item` / `foreach.index`), present only when
69
+ * `sampleValues` carries a `foreach.*` key. Mirrors the engine's inner scope.
70
+ */
71
+ foreach?: {
72
+ item?: unknown;
73
+ index?: number;
74
+ };
49
75
  /**
50
76
  * The original flat `sampleValues` map, keyed by full ref string — checked
51
77
  * BEFORE the nested walk in {@link resolveRef}. `documentSampleValues`
@@ -59,8 +85,8 @@ export interface ResolveScope {
59
85
  * never reach either shape. An exact flat-key hit here resolves any ref
60
86
  * `sampleValues` names directly, regardless of how many segments it has;
61
87
  * the nested walk stays the fallback for a ref whose own reconstructed
62
- * value happens to be an object (`vars.address` = `{ city: "X" }`, walking
63
- * into it for `vars.address.city`), which is a value shape `sampleValues`
88
+ * value happens to be an object (`var.address` = `{ city: "X" }`, walking
89
+ * into it for `var.address.city`), which is a value shape `sampleValues`
64
90
  * never produces a matching flat key for in the first place.
65
91
  *
66
92
  * Optional: a `ResolveScope` built by hand (as most tests in
@@ -69,6 +95,16 @@ export interface ResolveScope {
69
95
  * `buildResolveScope` populates it.
70
96
  */
71
97
  flat?: Record<string, unknown>;
98
+ /**
99
+ * Each node's static configuration ({@link ExpressionOptions.nodeConfig}), laid over `nodes`
100
+ * at `meta.config` and the `config` alias exactly as the engine's `withConfig` does.
101
+ */
102
+ config?: Record<string, Record<string, unknown>>;
103
+ /**
104
+ * Each node's static metadata ({@link ExpressionOptions.nodeMeta}), laid under the state's own
105
+ * `meta` (which wins key by key) exactly as the engine's `withConfig` does.
106
+ */
107
+ meta?: Record<string, Record<string, unknown>>;
72
108
  }
73
109
  /**
74
110
  * Build a {@link ResolveScope} from the two sources scope fidelity requires
@@ -77,15 +113,13 @@ export interface ResolveScope {
77
113
  * - `steps`/`trigger` come from `testStartState` — the exact object the test
78
114
  * invoke sends as `state`, so a `steps.<id>.output.<field>` ref resolves to
79
115
  * what the test will ACTUALLY see, not a design-time sample.
80
- * - `vars`/`documents` come from `sampleValues` (`useExpressionOptions()`), a
81
- * FLAT map keyed by full ref (`"vars.from_email"`, `"documents.<key>"`).
82
- * Reconstructed into a nested object here (strip the root prefix, one hop)
83
- * so {@link walkPath} can dot-walk into a var/document whose OWN value is an
84
- * object — `sampleValues["vars.address"]` holding `{ city: "X" }` still
85
- * resolves `vars.address.city` correctly, because the walk continues INTO
86
- * the reconstructed value rather than doing a second flat-key lookup.
116
+ * - `vars`/`documents`/`constants` come from `sampleValues` (`useExpressionOptions()`), a
117
+ * FLAT map keyed by full ref (`"var.from_email"`, `"documents.<key>"`, `"const.<name>"`).
118
+ * State paths are reconstructed into one nested tree; whole-object samples
119
+ * take precedence over their indexed descendants. The original flat map
120
+ * remains available for exact field lookups, including parsed document fields.
87
121
  */
88
- export declare function buildResolveScope(sampleValues: Record<string, unknown> | undefined, testStartState: StepStartState | undefined): ResolveScope;
122
+ export declare function buildResolveScope(sampleValues: Record<string, unknown> | undefined, testStartState: StepStartState | undefined, config?: Record<string, Record<string, unknown>>, meta?: Record<string, Record<string, unknown>>): ResolveScope;
89
123
  /** A hand-authored whole-value path marker: `{ "$": "<path>" }`, exactly one key. */
90
124
  export declare function isDollarPath(v: unknown): v is {
91
125
  $: unknown;
@@ -1,21 +1,4 @@
1
- /**
2
- * Pure derivation of a live run's canvas visuals (T1.1.1) — step node styling
3
- * and main-graph edge taken/skipped/untouched state. Plain functions of (run
4
- * state, step/edge identity) → visual state, with **no React import**, so
5
- * `node --test` exercises them directly (A7) the same way `flow-connect.ts`'s
6
- * connection rules do.
7
- *
8
- * `RunStatus`/`StepStatus`/`RunState`/`StepExecution` below are thin LOCAL
9
- * copies of the shapes `@w6w/workflow-types` declares
10
- * (`packages/w6w-workflow/packages/types/mod.ts`) — the same house idiom
11
- * `components/HealthStatusPill.tsx`'s `HealthPillState` uses, and the same
12
- * design note `flow-types.ts`'s own header already states for
13
- * `FlowWorkflow`/`FlowStep`: `packages/ui/package.json` has no dependency on
14
- * `@w6w/workflow-types` and must not gain one, so this module stays a
15
- * pure-presentation leaf with no transport/spec coupling. A caller who
16
- * already has the real `RunState`/`StepExecution` is structurally compatible
17
- * for free.
18
- */
1
+ import { CanvasLane } from './flow-types.ts';
19
2
  /** Mirrors `RunStatus` in `@w6w/workflow-types`. */
20
3
  export type RunStatus = "queued" | "running" | "succeeded" | "failed" | "canceled";
21
4
  /** Mirrors `StepStatus` in `@w6w/workflow-types`. */
@@ -23,6 +6,15 @@ export type StepStatus = "pending" | "running" | "succeeded" | "failed" | "skipp
23
6
  /** The slice of `StepExecution` this module reads. */
24
7
  export interface StepExecution {
25
8
  status: StepStatus;
9
+ /** Why a `skipped` step did not run; `"not_on_path"` = never part of this run (no paint). */
10
+ skipReason?: string;
11
+ output?: unknown;
12
+ /** For each only (I3): per-iteration body-step records, keyed by body-step id. */
13
+ iterations?: ReadonlyArray<{
14
+ nodes?: Record<string, {
15
+ status: StepStatus;
16
+ }>;
17
+ }>;
26
18
  }
27
19
  /** The slice of `RunState` this module reads — steps keyed by step id, same
28
20
  * as the engine's own `RunState.steps`. */
@@ -46,9 +38,10 @@ export type StepVisualState = "no-run" | StepStatus;
46
38
  * The rendered state for step `stepId` under `runState` — the StepStatus
47
39
  * half of A3's RunStatus × StepStatus mapping. Pure function of the run's own
48
40
  * step records; no additional data needed (`GET /runs/:id` already returns
49
- * everything this reads).
41
+ * everything this reads). A step whose record says `skipReason: "not_on_path"` (or whose
42
+ * enclosing loop's does) was never part of the run: `"no-run"`, the unpainted look.
50
43
  */
51
- export declare function stepVisualState(runState: RunState | undefined, stepId: string): StepVisualState;
44
+ export declare function stepVisualState(runState: RunState | undefined, stepId: string, loops?: readonly string[]): StepVisualState;
52
45
  /** One node's non-persisted, run-aware look. Colour is never the only signal
53
46
  * (the house rule `HealthStatusPill`'s own doc comment states) — every
54
47
  * non-default state also differs from the default (`"no-run"`) in
@@ -90,4 +83,4 @@ export type EdgeRunState = "taken" | "skipped";
90
83
  * - `from` is **running**, or has no record at all (not yet reached): the
91
84
  * edge's fate isn't decided yet ⇒ `undefined`.
92
85
  */
93
- export declare function edgeRunState(runState: RunState | undefined, from: string, when: "success" | "error" | undefined): EdgeRunState | undefined;
86
+ export declare function edgeRunState(runState: RunState | undefined, from: string, when: CanvasLane | undefined, route?: string, to?: string): EdgeRunState | undefined;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * `{{ }}` inside a Run script's CODE. The host never templates `@w6w/script`'s `code` (the
3
+ * engine's `LITERAL_PARAMS`, node-types RFC amendment 2026-10-30): a marker typed into the code
4
+ * reaches the sandbox as literal text — `'{{ nodes.a.output.total }}'` is that exact string at
5
+ * run time, never the value. JavaScript/TypeScript insert `$.get(root, ...keys)` reads;
6
+ * Python inserts `ctx.get(root, *keys)` reads. Existing globals remain supported.
7
+ *
8
+ * {@link findCodeBindings} finds the markers that would run (strings and code, not comments),
9
+ * {@link codeReadFor} spells one reference as that code read, and
10
+ * {@link convertCodeBindingsToReads} rewrites every convertible marker in a script — what the
11
+ * step editor does when a script opens, behind a visible note with Undo.
12
+ */
13
+ export type ScriptLanguage = "javascript" | "typescript" | "python";
14
+ /** Where a marker sits: plain code, or inside a string literal of the given quote. */
15
+ type Context = {
16
+ kind: "code";
17
+ } | {
18
+ kind: "string";
19
+ quote: string;
20
+ start: number;
21
+ end: number;
22
+ };
23
+ export interface CodeBinding {
24
+ /** Offset of the `{{` in the code. */
25
+ start: number;
26
+ /** Offset just past the `}}`. */
27
+ end: number;
28
+ /** The marker's inner text, trimmed — what Input binds (`{{ <ref> }}`). */
29
+ ref: string;
30
+ context: Context;
31
+ }
32
+ /**
33
+ * Every `{{ … }}` in `code` that is not inside a comment, with the string literal (if any)
34
+ * around it. A small scanner, not a parser: it tracks `'…'`, `"…"` (and, in JavaScript,
35
+ * `` `…` ``; in Python, triple quotes) and the language's comments, which is what deciding
36
+ * "would this marker run" needs.
37
+ */
38
+ export declare function findCodeBindings(code: string, language?: ScriptLanguage): CodeBinding[];
39
+ /**
40
+ * Turn a reference into `$.get(root, ...keys)` (JavaScript/TypeScript) or
41
+ * `ctx.get(root, *keys)` (Python). Unsupported roots (secrets, documents, Function inputs)
42
+ * and non-path expressions return null. State always means the state tree without run aliases.
43
+ */
44
+ export declare function codeReadFor(ref: string, language?: ScriptLanguage): string | null;
45
+ export interface ConvertResult {
46
+ code: string;
47
+ /** Markers rewritten to code reads. */
48
+ converted: number;
49
+ /** Markers left as they were: not a readable reference, or inside a string they can't leave. */
50
+ left: number;
51
+ }
52
+ /**
53
+ * Rewrite each running `{{ ref }}` in `code` to the code read {@link codeReadFor} gives:
54
+ * - a marker that IS a whole string literal (`'{{ ref }}'`) becomes the read, keeping the
55
+ * value's type;
56
+ * - a bare marker in code becomes the read;
57
+ * - inside a JavaScript template literal it becomes `${read}`;
58
+ * - inside a longer `'…'`/`"…"` string it is spliced in by concatenation (`str(read)` in Python);
59
+ * - anything else (a Python triple-quoted or prefixed string, a reference with no code read) is
60
+ * left as it was and counted in `left`.
61
+ */
62
+ export declare function convertCodeBindingsToReads(code: string, language?: ScriptLanguage): ConvertResult;
63
+ export {};
@@ -0,0 +1,9 @@
1
+ export interface ScriptSuggestion {
2
+ label: string;
3
+ type: string;
4
+ }
5
+ /** Virtual files only: no evaluation of user code, filesystem, or network access. */
6
+ export declare function createScriptCompletionService(libraries: Record<string, string>): {
7
+ complete(code: string, position: number): ScriptSuggestion[];
8
+ dispose: () => void;
9
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ /** Per-editor lazy worker; compiling suggestions never blocks typing or runs the script. */
2
+ export declare function scriptCompletions(): import('@codemirror/state').Extension[];
@@ -0,0 +1,38 @@
1
+ import { Edge } from '@xyflow/react';
2
+ import { ExpressionStateField } from './components/ExpressionOptions.tsx';
3
+ import { FlowWorkflow } from './flow-types.ts';
4
+ import { StepNode } from './flow-utils.ts';
5
+ import { StepShapeContext } from './step-preview-state.ts';
6
+ /** The merged state at one anchor node. */
7
+ export interface StateShape {
8
+ /** Every field path of the merged tree (depth/count capped like a step's output fields). */
9
+ fields: ExpressionStateField[];
10
+ /**
11
+ * Preview values keyed by the ref the picker inserts (`state.<path>`), from REAL values only
12
+ * (test outputs, the app's `sample`, starting data, variable defaults) — never a skeleton.
13
+ */
14
+ sampleValues: Record<string, unknown>;
15
+ }
16
+ export interface StateShapeInput {
17
+ /** The node whose incoming state this is (`null` → empty shape). */
18
+ anchorId: string | null | undefined;
19
+ nodes: StepNode[];
20
+ edges: Edge[];
21
+ initialState?: FlowWorkflow["initialState"];
22
+ variables?: FlowWorkflow["variables"];
23
+ /** The same shape context the picker's "Previous steps" projection reads. */
24
+ shapes?: StepShapeContext;
25
+ /** Each node's REAL output (fresh test, else persisted), for preview values. */
26
+ previewOutputs?: Record<string, unknown>;
27
+ }
28
+ /**
29
+ * The engine's run order over `nodes`/`edges`: Kahn's, the ready queue seeded in declared node
30
+ * order, successors released in declared edge order. Nodes a cycle leaves out follow in declared
31
+ * order (the engine refuses such a plan; the editor still has to show something).
32
+ */
33
+ export declare function stateWriteOrder(nodes: StepNode[], edges: Edge[]): string[];
34
+ /**
35
+ * The merged state shape a node reads — see the file header. Empty (no fields) when nothing on
36
+ * any path into `anchorId` writes state and there is no starting data or variable.
37
+ */
38
+ export declare function computeStateShape(input: StateShapeInput): StateShape;