@w6w/ui 0.2.0 → 0.2.54

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 (160) hide show
  1. package/CHANGELOG.md +146 -2
  2. package/README.md +28 -10
  3. package/dist/AppPicker.d.ts +58 -2
  4. package/dist/CanvasContextMenu.d.ts +19 -0
  5. package/dist/CanvasLayer.d.ts +38 -0
  6. package/dist/{CodeBlock-Dot3Pd7P.js → CodeBlock-C-oIIYfC.js} +1436 -555
  7. package/dist/CodeBlock-C-oIIYfC.js.map +1 -0
  8. package/dist/CodeBlock-CymlfyeA.cjs +15 -0
  9. package/dist/CodeBlock-CymlfyeA.cjs.map +1 -0
  10. package/dist/CodeEditor.d.ts +4 -1
  11. package/dist/ConditionEditor.d.ts +11 -0
  12. package/dist/EditConnectionModal.d.ts +6 -1
  13. package/dist/JsonEditor.d.ts +20 -0
  14. package/dist/Markdown.d.ts +25 -0
  15. package/dist/ParamCustomization.d.ts +5 -0
  16. package/dist/ParamsForm.d.ts +18 -4
  17. package/dist/RouterEditor.d.ts +5 -0
  18. package/dist/StepBuilderModal.d.ts +103 -15
  19. package/dist/StepNameEditor.d.ts +11 -0
  20. package/dist/StopEditor.d.ts +5 -0
  21. package/dist/TriggerFillForm.d.ts +14 -1
  22. package/dist/WebhookListenPanel.d.ts +28 -0
  23. package/dist/WorkflowConnections.d.ts +29 -0
  24. package/dist/WorkflowFlowEditor.d.ts +74 -7
  25. package/dist/app-pages.d.ts +2 -0
  26. package/dist/assets/script-completion.worker-DDJKkj5M.js +8290 -0
  27. package/dist/assets/script-completion.worker-DDJKkj5M.js.map +1 -0
  28. package/dist/canvas-context-menu.d.ts +27 -0
  29. package/dist/canvas-types.d.ts +36 -0
  30. package/dist/canvas-utils.d.ts +44 -0
  31. package/dist/code.cjs +1 -1
  32. package/dist/code.mjs +3 -4
  33. package/dist/code.mjs.map +1 -1
  34. package/dist/components/AnchoredFlyout.d.ts +7 -0
  35. package/dist/components/AuthFieldsForm.d.ts +11 -0
  36. package/dist/components/BudgetMeter.d.ts +15 -0
  37. package/dist/components/CallableList.d.ts +3 -2
  38. package/dist/components/ColumnsEditor.d.ts +27 -0
  39. package/dist/components/Combobox.d.ts +19 -1
  40. package/dist/components/ConnectionList.d.ts +2 -3
  41. package/dist/components/CredentialPicker.d.ts +35 -0
  42. package/dist/components/CsvPanel.d.ts +21 -0
  43. package/dist/components/DataEntryEditor.d.ts +38 -0
  44. package/dist/components/DataEntryList.d.ts +33 -0
  45. package/dist/components/DataStoreList.d.ts +33 -0
  46. package/dist/components/DataTable.d.ts +27 -0
  47. package/dist/components/DataTableGrid.d.ts +46 -0
  48. package/dist/components/EmptyState.d.ts +9 -0
  49. package/dist/components/EventbriteTriggerPicker.d.ts +7 -0
  50. package/dist/components/ExecutionDetail.d.ts +17 -1
  51. package/dist/components/ExecutionList.d.ts +19 -1
  52. package/dist/components/ExecutionLogPanel.d.ts +61 -3
  53. package/dist/components/ExpressionEditorModal.d.ts +33 -10
  54. package/dist/components/ExpressionOptions.d.ts +101 -15
  55. package/dist/components/HtmlPreview.d.ts +7 -0
  56. package/dist/components/JsonTree.d.ts +32 -0
  57. package/dist/components/ResolvedParams.d.ts +6 -1
  58. package/dist/components/ResultViewer.d.ts +46 -0
  59. package/dist/components/ScriptBindingsNotice.d.ts +31 -0
  60. package/dist/components/ScriptCodeField.d.ts +13 -0
  61. package/dist/components/StepStatusPill.d.ts +7 -0
  62. package/dist/components/expression-authoring.d.ts +13 -0
  63. package/dist/components/expression-dom.d.ts +17 -2
  64. package/dist/components/expression-template.d.ts +25 -3
  65. package/dist/components/icons.d.ts +1 -1
  66. package/dist/components/json-compose.d.ts +53 -0
  67. package/dist/components/result-format.d.ts +13 -0
  68. package/dist/condition-editor.d.ts +44 -0
  69. package/dist/data-stores/data-store-view.d.ts +371 -0
  70. package/dist/dynamic-fields.d.ts +60 -0
  71. package/dist/editor-theme.d.ts +24 -0
  72. package/dist/eventbrite-trigger-selection.d.ts +13 -0
  73. package/dist/flow-connect.d.ts +22 -16
  74. package/dist/flow-delete.d.ts +16 -0
  75. package/dist/flow-fanout.d.ts +2 -2
  76. package/dist/flow-history.d.ts +75 -0
  77. package/dist/flow-layout.d.ts +13 -0
  78. package/dist/flow-loop-colors.d.ts +39 -0
  79. package/dist/flow-loop.d.ts +98 -0
  80. package/dist/flow-node-flags.d.ts +14 -0
  81. package/dist/flow-problems.d.ts +69 -0
  82. package/dist/flow-state.d.ts +20 -0
  83. package/dist/flow-types.d.ts +155 -10
  84. package/dist/flow-utils.d.ts +33 -12
  85. package/dist/flow.cjs +4 -2
  86. package/dist/flow.cjs.map +1 -1
  87. package/dist/flow.d.ts +15 -2
  88. package/dist/flow.mjs +3737 -1350
  89. package/dist/flow.mjs.map +1 -1
  90. package/dist/index.cjs +2 -1
  91. package/dist/index.cjs.map +1 -1
  92. package/dist/index.d.ts +43 -5
  93. package/dist/index.mjs +2855 -1333
  94. package/dist/index.mjs.map +1 -1
  95. package/dist/node-samples-ChdyMBnJ.cjs +22 -0
  96. package/dist/node-samples-ChdyMBnJ.cjs.map +1 -0
  97. package/dist/node-samples-DkJL07EX.js +12302 -0
  98. package/dist/node-samples-DkJL07EX.js.map +1 -0
  99. package/dist/node-samples.d.ts +80 -0
  100. package/dist/note-markdown.d.ts +22 -0
  101. package/dist/output-shape.d.ts +30 -14
  102. package/dist/param-customization.d.ts +7 -0
  103. package/dist/provider.d.ts +174 -13
  104. package/dist/resolve-params.d.ts +50 -11
  105. package/dist/run-visuals.d.ts +18 -22
  106. package/dist/script-bindings.d.ts +63 -0
  107. package/dist/script-completion-service.d.ts +9 -0
  108. package/dist/script-completion.worker.d.ts +1 -0
  109. package/dist/script-completions.d.ts +2 -0
  110. package/dist/state-shape.d.ts +38 -0
  111. package/dist/step-preview-state.d.ts +155 -6
  112. package/dist/step-problems.d.ts +64 -0
  113. package/dist/test-inputs.d.ts +25 -0
  114. package/dist/trigger-registration.d.ts +28 -0
  115. package/dist/types.d.ts +179 -11
  116. package/dist/use-app-action-defs.d.ts +9 -1
  117. package/dist/use-dynamic-fields.d.ts +23 -0
  118. package/dist/use-test-input.d.ts +18 -0
  119. package/dist/workflow-connections.d.ts +30 -0
  120. package/package.json +18 -7
  121. package/src/code.css +60 -6
  122. package/src/styles/_budget-meter.scss +46 -0
  123. package/src/styles/_callable-list.scss +15 -6
  124. package/src/styles/_canvas.scss +353 -0
  125. package/src/styles/_code-editor.scss +35 -0
  126. package/src/styles/_code.scss +6 -5
  127. package/src/styles/_connections.scss +2 -14
  128. package/src/styles/_copyable.scss +24 -0
  129. package/src/styles/_data-stores.scss +138 -0
  130. package/src/styles/_data-table.scss +55 -0
  131. package/src/styles/_data-vars.scss +17 -6
  132. package/src/styles/_empty-state.scss +21 -0
  133. package/src/styles/_execution-history.scss +73 -0
  134. package/src/styles/_execution-log.scss +145 -1
  135. package/src/styles/_expression-modal.scss +106 -16
  136. package/src/styles/_fields.scss +11 -0
  137. package/src/styles/_flow.scss +171 -0
  138. package/src/styles/_fx.scss +26 -1
  139. package/src/styles/_incoming-state.scss +33 -0
  140. package/src/styles/_layout.scss +10 -0
  141. package/src/styles/_markdown.scss +20 -0
  142. package/src/styles/_modal.scss +12 -0
  143. package/src/styles/_multiselect.scss +35 -0
  144. package/src/styles/_sections.scss +36 -0
  145. package/src/styles/_step-builder.scss +145 -0
  146. package/src/styles/_tester.scss +57 -14
  147. package/src/styles/_tokens.scss +27 -1
  148. package/src/styles.css +1861 -115
  149. package/src/styles.scss +7 -0
  150. package/dist/CodeBlock-CZu4TFSi.cjs +0 -15
  151. package/dist/CodeBlock-CZu4TFSi.cjs.map +0 -1
  152. package/dist/CodeBlock-Dot3Pd7P.js.map +0 -1
  153. package/dist/ConfirmModal-ChoiGy2K.cjs +0 -11
  154. package/dist/ConfirmModal-ChoiGy2K.cjs.map +0 -1
  155. package/dist/ConfirmModal-Wx3i8F1n.js +0 -6359
  156. package/dist/ConfirmModal-Wx3i8F1n.js.map +0 -1
  157. package/dist/Copyable-1niV9gUa.js +0 -771
  158. package/dist/Copyable-1niV9gUa.js.map +0 -1
  159. package/dist/Copyable-CYZU1o4X.cjs +0 -2
  160. package/dist/Copyable-CYZU1o4X.cjs.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
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The note editor's Markdown round-trip. A note is edited in place as a `contentEditable` block
3
+ * (no textarea); these two functions convert between its stored Markdown and that editable DOM.
4
+ *
5
+ * The editable model is line-based and covers exactly what the note toolbar writes: a heading
6
+ * level per line (`#`…`######`) and `**bold**` runs. Everything else (links, lists, `code`, …) is
7
+ * kept as literal text while editing and rendered by `Markdown` when not, so a line the toolbar
8
+ * never touched round-trips byte-for-byte.
9
+ *
10
+ * The DOM is built with `createElement` + text nodes only — note content is imported,
11
+ * attacker-controllable text and must never reach `innerHTML`.
12
+ */
13
+ /** Build the editable DOM for `content` into `root` (its children are replaced). */
14
+ export declare function renderEditable(root: HTMLElement, content: string): void;
15
+ /** Serialize the editable DOM under `root` back to Markdown. */
16
+ export declare function editableToMarkdown(root: HTMLElement): string;
17
+ /**
18
+ * The content handed to `Markdown` for display. A note is typed line by line, so a single
19
+ * newline must show as a line break — Markdown would fold it into a space. A soft break between
20
+ * two non-empty lines becomes a hard break (two trailing spaces); the stored content is untouched.
21
+ */
22
+ export declare function noteDisplayMarkdown(content: string): string;
@@ -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, ConnectionSummary, FunctionDetail, FunctionSummary, 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,10 @@ 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;
126
+ /** Server-side id allow-list (`GET /apps?ids=a,b`); `total` is then the filtered total. */
127
+ ids?: string[];
114
128
  /** Opaque pagination cursor from a prior {@link AppsPageResult.nextCursor}. */
115
129
  cursor?: string;
116
130
  /** Page size; a host may clamp this to its own bounds. */
@@ -135,6 +149,20 @@ export interface AppsPageResult {
135
149
  limit?: number;
136
150
  expiresAt?: string;
137
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
+ }
138
166
  export interface W6WApi {
139
167
  /**
140
168
  * List registered apps to pick from in the connection modal.
@@ -174,15 +202,59 @@ export interface W6WApi {
174
202
  listAppsByIds?(ids: readonly string[], options?: {
175
203
  signal?: AbortSignal;
176
204
  }): Promise<AppSummary[]>;
205
+ /**
206
+ * OPTIONAL: the tenant's model routes, for the `model-route` step-param
207
+ * picker (`@w6w/model` `complete`). Absent, the param renders as a plain text
208
+ * input and this is never called. A `broken` route is still selectable.
209
+ * `project` is the editor's own project (`WorkflowProjectProvider`) when it
210
+ * has one — a host should list THAT project's routes, not guess one from its
211
+ * URL; `undefined` leaves the choice to the host.
212
+ */
213
+ listModelRoutes?(project?: string): Promise<{
214
+ name: string;
215
+ model: string;
216
+ status: "active" | "broken";
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>;
246
+ /**
247
+ * OPTIONAL: resolve the choices of `options: { source: "resolveFields" }`
248
+ * params from the current form (`POST` to the host). Absent, those params
249
+ * render as plain inputs and this is never called.
250
+ */
251
+ resolveActionFields?(appId: string, actionKey: string, body: ResolveFieldsBody, opts?: {
252
+ signal?: AbortSignal;
253
+ }): Promise<ResolveFieldsResult>;
177
254
  /** Load auth methods declared by an app's manifest, with availability flags. */
178
255
  getAppAuth(appId: string): Promise<AuthDef[]>;
179
256
  /** Create a non-OAuth connection with a user-supplied credential. */
180
- createConnection(appId: string, body: {
181
- authKey: string;
182
- credential: Record<string, unknown>;
183
- displayName?: string;
184
- profile?: Record<string, unknown>;
185
- }): Promise<ConnectionSummary>;
257
+ createConnection(appId: string, body: CreateConnectionBody): Promise<ConnectionSummary>;
186
258
  /**
187
259
  * Rename a connection and/or replace some of its credential fields (`PATCH /connections/:id`).
188
260
  * Send only the fields being changed: the server merges a partial `credential` over the stored
@@ -191,8 +263,34 @@ export interface W6WApi {
191
263
  */
192
264
  updateConnection?(id: string, body: {
193
265
  displayName?: string;
266
+ binding?: ConnectionBinding;
267
+ credentialId?: string;
194
268
  credential?: Record<string, unknown>;
195
269
  }): Promise<ConnectionSummary>;
270
+ /** List stored credentials, optionally narrowed (`GET /credentials`). */
271
+ listCredentials?(filter?: {
272
+ contract?: string;
273
+ appId?: string;
274
+ authKey?: string;
275
+ }): Promise<CredentialSummary[]>;
276
+ /** Store a new credential (`POST /credentials`). `source` is the auth's field-value record. */
277
+ createCredential?(body: {
278
+ name: string;
279
+ contract: string;
280
+ source: Record<string, unknown>;
281
+ }): Promise<CredentialSummary>;
282
+ getCredential?(id: string): Promise<CredentialSummary>;
283
+ /** Replace a credential's secret (`POST /credentials/:id/rotate`). */
284
+ rotateCredential?(id: string, body: {
285
+ source: Record<string, unknown>;
286
+ }): Promise<CredentialSummary>;
287
+ /** Disable (`/disable`) or re-enable (`/enable`) a credential. */
288
+ setCredentialStatus?(id: string, status: "active" | "disabled"): Promise<CredentialSummary>;
289
+ /** Mint-level health check; distinct from a connection's app-access test. */
290
+ testCredential?(id: string): Promise<CredentialTestResult>;
291
+ credentialDependents?(id: string): Promise<CredentialDependents>;
292
+ /** Rejects with `ApiError` code `credential_in_use` (body.error.dependents) when in use. */
293
+ deleteCredential?(id: string): Promise<void>;
196
294
  /**
197
295
  * Start an OAuth 2.0 flow. Server builds the provider's authorize URL and
198
296
  * returns it; the caller opens it in a popup and awaits the server's
@@ -231,6 +329,11 @@ export interface W6WApi {
231
329
  project?: string;
232
330
  state?: StepStartState;
233
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;
234
337
  }): Promise<{
235
338
  value: unknown;
236
339
  logs?: string[];
@@ -310,6 +413,28 @@ export interface W6WApi {
310
413
  * `/workflows/:workflowId/steps/:stepId/tests`.
311
414
  */
312
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>;
313
438
  /**
314
439
  * List the caller's registered Functions (core rfcs/function.md) — drives
315
440
  * the step builder's Functions tab (F-2.0). GETs `/functions`.
@@ -359,11 +484,11 @@ export interface W6WApi {
359
484
  }>;
360
485
  /**
361
486
  * List apps that declare at least one trigger — the Triggers tab's
362
- * "App triggers" section (T-0). GETs whatever the host resolves to (e.g.
363
- * 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.
364
490
  *
365
- * OPTIONAL, like `listTestRuns?` above: a host that hasn't wired this member
366
- * still typechecks and simply doesn't render the App-triggers section.
491
+ * OPTIONAL, like `listTestRuns?` above.
367
492
  */
368
493
  listTriggerApps?(): Promise<AppSummary[]>;
369
494
  /**
@@ -376,6 +501,31 @@ export interface W6WApi {
376
501
  * `/workflows/:id/subscriptions`.
377
502
  */
378
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>;
379
529
  /**
380
530
  * Create a Subscription binding an app trigger to a workflow — the one
381
531
  * explicit user action both the Triggers-tab app section (T-0) and the
@@ -431,10 +581,21 @@ export declare function W6WUIProvider({ api, theme, children }: W6WUIProviderPro
431
581
  * `<W6WUIProvider>` — the common mistake is forgetting to wrap the app root.
432
582
  */
433
583
  export declare function useW6WApi(): W6WApi;
434
- /** Scopes test-invokes under it to `project`; the editor wraps its body with this. */
435
- export declare function WorkflowProjectProvider({ project, children, }: {
584
+ /**
585
+ * Like {@link useW6WApi} but returns `undefined` outside a provider instead of
586
+ * throwing — for optional enhancements (e.g. the `model-route` picker).
587
+ */
588
+ export declare function useOptionalW6WApi(): W6WApi | undefined;
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, }: {
436
594
  project?: string;
595
+ workflowId?: string;
437
596
  children: ReactNode;
438
597
  }): import("react").JSX.Element;
439
598
  /** The selected workflow project id in scope, or `undefined` outside the editor. */
440
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,18 +39,39 @@ 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;
40
58
  }>;
59
+ /** `nodes.<id>…` root — the SAME object as `steps` (one state, two roots). */
60
+ nodes?: Record<string, {
61
+ output?: unknown;
62
+ meta?: StepMeta;
63
+ }>;
41
64
  trigger: {
42
65
  event?: unknown;
43
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
+ };
44
75
  /**
45
76
  * The original flat `sampleValues` map, keyed by full ref string — checked
46
77
  * BEFORE the nested walk in {@link resolveRef}. `documentSampleValues`
@@ -54,8 +85,8 @@ export interface ResolveScope {
54
85
  * never reach either shape. An exact flat-key hit here resolves any ref
55
86
  * `sampleValues` names directly, regardless of how many segments it has;
56
87
  * the nested walk stays the fallback for a ref whose own reconstructed
57
- * value happens to be an object (`vars.address` = `{ city: "X" }`, walking
58
- * 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`
59
90
  * never produces a matching flat key for in the first place.
60
91
  *
61
92
  * Optional: a `ResolveScope` built by hand (as most tests in
@@ -64,6 +95,16 @@ export interface ResolveScope {
64
95
  * `buildResolveScope` populates it.
65
96
  */
66
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>>;
67
108
  }
68
109
  /**
69
110
  * Build a {@link ResolveScope} from the two sources scope fidelity requires
@@ -72,15 +113,13 @@ export interface ResolveScope {
72
113
  * - `steps`/`trigger` come from `testStartState` — the exact object the test
73
114
  * invoke sends as `state`, so a `steps.<id>.output.<field>` ref resolves to
74
115
  * what the test will ACTUALLY see, not a design-time sample.
75
- * - `vars`/`documents` come from `sampleValues` (`useExpressionOptions()`), a
76
- * FLAT map keyed by full ref (`"vars.from_email"`, `"documents.<key>"`).
77
- * Reconstructed into a nested object here (strip the root prefix, one hop)
78
- * so {@link walkPath} can dot-walk into a var/document whose OWN value is an
79
- * object — `sampleValues["vars.address"]` holding `{ city: "X" }` still
80
- * resolves `vars.address.city` correctly, because the walk continues INTO
81
- * 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.
82
121
  */
83
- 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;
84
123
  /** A hand-authored whole-value path marker: `{ "$": "<path>" }`, exactly one key. */
85
124
  export declare function isDollarPath(v: unknown): v is {
86
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,12 +6,24 @@ 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`. */
29
21
  export interface RunState {
30
22
  status: RunStatus;
31
- steps: Record<string, StepExecution>;
23
+ /** Canonical per-node records (D2). */
24
+ nodes?: Record<string, StepExecution>;
25
+ /** Legacy alias of `nodes`; read as `nodes ?? steps` (un-upgraded server). */
26
+ steps?: Record<string, StepExecution>;
32
27
  }
33
28
  /**
34
29
  * A step node's rendered state — exactly the five {@link StepStatus} values
@@ -43,9 +38,10 @@ export type StepVisualState = "no-run" | StepStatus;
43
38
  * The rendered state for step `stepId` under `runState` — the StepStatus
44
39
  * half of A3's RunStatus × StepStatus mapping. Pure function of the run's own
45
40
  * step records; no additional data needed (`GET /runs/:id` already returns
46
- * 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.
47
43
  */
48
- export declare function stepVisualState(runState: RunState | undefined, stepId: string): StepVisualState;
44
+ export declare function stepVisualState(runState: RunState | undefined, stepId: string, loops?: readonly string[]): StepVisualState;
49
45
  /** One node's non-persisted, run-aware look. Colour is never the only signal
50
46
  * (the house rule `HealthStatusPill`'s own doc comment states) — every
51
47
  * non-default state also differs from the default (`"no-run"`) in
@@ -87,4 +83,4 @@ export type EdgeRunState = "taken" | "skipped";
87
83
  * - `from` is **running**, or has no record at all (not yet reached): the
88
84
  * edge's fate isn't decided yet ⇒ `undefined`.
89
85
  */
90
- 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;