@w6w/ui 0.0.0-stage → 0.1.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.
- package/CHANGELOG.md +48 -0
- package/LICENSE +21 -0
- package/README.md +260 -2
- package/dist/ActionTestForm.d.ts +76 -0
- package/dist/AddConnectionModal.d.ts +24 -0
- package/dist/AppPicker.d.ts +83 -0
- package/dist/CodeBlock-CZu4TFSi.cjs +15 -0
- package/dist/CodeBlock-CZu4TFSi.cjs.map +1 -0
- package/dist/CodeBlock-Dot3Pd7P.js +2889 -0
- package/dist/CodeBlock-Dot3Pd7P.js.map +1 -0
- package/dist/CodeBlock.d.ts +65 -0
- package/dist/CodeEditor.d.ts +42 -0
- package/dist/ConfirmModal-DJPYXE9t.cjs +11 -0
- package/dist/ConfirmModal-DJPYXE9t.cjs.map +1 -0
- package/dist/ConfirmModal-sY1tzkBO.js +6335 -0
- package/dist/ConfirmModal-sY1tzkBO.js.map +1 -0
- package/dist/Copyable-1niV9gUa.js +771 -0
- package/dist/Copyable-1niV9gUa.js.map +1 -0
- package/dist/Copyable-CYZU1o4X.cjs +2 -0
- package/dist/Copyable-CYZU1o4X.cjs.map +1 -0
- package/dist/JsonEditor.d.ts +60 -0
- package/dist/NodeConfigForm.d.ts +70 -0
- package/dist/ParamsForm.d.ts +56 -0
- package/dist/PropertyEntryForm.d.ts +56 -0
- package/dist/RetryPolicyFields.d.ts +18 -0
- package/dist/StepBuilderModal.d.ts +375 -0
- package/dist/TriggerFillForm.d.ts +52 -0
- package/dist/WaitEditor.d.ts +6 -0
- package/dist/WorkflowFlowEditor.d.ts +119 -0
- package/dist/YamlEditor.d.ts +46 -0
- package/dist/app-categories.d.ts +38 -0
- package/dist/app-pages.d.ts +111 -0
- package/dist/code.cjs +2 -0
- package/dist/code.cjs.map +1 -0
- package/dist/code.d.ts +29 -0
- package/dist/code.mjs +7 -0
- package/dist/code.mjs.map +1 -0
- package/dist/components/ApiCallsPanel.d.ts +41 -0
- package/dist/components/AppIcon.d.ts +25 -0
- package/dist/components/AuthFieldsForm.d.ts +22 -0
- package/dist/components/Combobox.d.ts +24 -0
- package/dist/components/ConfirmModal.d.ts +33 -0
- package/dist/components/Copyable.d.ts +51 -0
- package/dist/components/CopyableText.d.ts +40 -0
- package/dist/components/DeleteButton.d.ts +16 -0
- package/dist/components/EditButton.d.ts +10 -0
- package/dist/components/ExecutionDetail.d.ts +70 -0
- package/dist/components/ExecutionFilters.d.ts +59 -0
- package/dist/components/ExecutionList.d.ts +60 -0
- package/dist/components/ExecutionLogPanel.d.ts +98 -0
- package/dist/components/ExecutionStats.d.ts +47 -0
- package/dist/components/ExpressionEditorModal.d.ts +42 -0
- package/dist/components/ExpressionInput.d.ts +36 -0
- package/dist/components/ExpressionOptions.d.ts +185 -0
- package/dist/components/HealthStatusPill.d.ts +25 -0
- package/dist/components/HistoryTimeline.d.ts +56 -0
- package/dist/components/Icon.d.ts +34 -0
- package/dist/components/IconButton.d.ts +60 -0
- package/dist/components/InfoTip.d.ts +15 -0
- package/dist/components/InternalIcon.d.ts +19 -0
- package/dist/components/ListItem.d.ts +25 -0
- package/dist/components/Modal.d.ts +29 -0
- package/dist/components/RepoSyncIndicator.d.ts +29 -0
- package/dist/components/ResolvedParams.d.ts +31 -0
- package/dist/components/ServerResources.d.ts +47 -0
- package/dist/components/StepStatusPill.d.ts +51 -0
- package/dist/components/UptimeStrip.d.ts +56 -0
- package/dist/components/execution-format.d.ts +24 -0
- package/dist/components/expression-dom.d.ts +154 -0
- package/dist/components/expression-template.d.ts +86 -0
- package/dist/components/history-scale.d.ts +33 -0
- package/dist/components/icons.d.ts +41 -0
- package/dist/components/resolved-clip.d.ts +42 -0
- package/dist/components/server-resources-format.d.ts +68 -0
- package/dist/components/server-resources.fixture.d.ts +9 -0
- package/dist/components/use-copy.d.ts +14 -0
- package/dist/createW6WApi.d.ts +31 -0
- package/dist/flow-connect.d.ts +138 -0
- package/dist/flow-fanout.d.ts +15 -0
- package/dist/flow-types.d.ts +356 -0
- package/dist/flow-utils.d.ts +284 -0
- package/dist/flow.cjs +3 -0
- package/dist/flow.cjs.map +1 -0
- package/dist/flow.d.ts +22 -0
- package/dist/flow.mjs +1977 -0
- package/dist/flow.mjs.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +94 -0
- package/dist/index.mjs +1811 -0
- package/dist/index.mjs.map +1 -0
- package/dist/oauth-popup.d.ts +17 -0
- package/dist/output-shape.d.ts +58 -0
- package/dist/provider.d.ts +420 -0
- package/dist/resolve-params.d.ts +112 -0
- package/dist/resolved-json.d.ts +21 -0
- package/dist/run-visuals.d.ts +90 -0
- package/dist/step-preview-state.d.ts +143 -0
- package/dist/theme.d.ts +32 -0
- package/dist/trigger-fields.d.ts +56 -0
- package/dist/types.d.ts +413 -0
- package/dist/ui.css +1 -0
- package/dist/use-app-action-defs.d.ts +7 -0
- package/dist/use-enter-submit.d.ts +23 -0
- package/dist/use-seed-sources.d.ts +16 -0
- package/package.json +111 -3
- package/src/code.css +542 -0
- package/src/styles/_buttons.scss +38 -0
- package/src/styles/_chips.scss +210 -0
- package/src/styles/_code.scss +139 -0
- package/src/styles/_copyable.scss +147 -0
- package/src/styles/_data-vars.scss +38 -0
- package/src/styles/_execution-history.scss +282 -0
- package/src/styles/_execution-log.scss +178 -0
- package/src/styles/_expression-field.scss +65 -0
- package/src/styles/_expression-inline.scss +63 -0
- package/src/styles/_expression-modal.scss +292 -0
- package/src/styles/_fields.scss +161 -0
- package/src/styles/_flow.scss +217 -0
- package/src/styles/_fx.scss +158 -0
- package/src/styles/_health.scss +46 -0
- package/src/styles/_history-timeline.scss +156 -0
- package/src/styles/_icon-button.scss +22 -0
- package/src/styles/_icon.scss +15 -0
- package/src/styles/_icons.scss +31 -0
- package/src/styles/_incoming-state.scss +46 -0
- package/src/styles/_info-tip.scss +9 -0
- package/src/styles/_layout.scss +6 -0
- package/src/styles/_lists.scss +64 -0
- package/src/styles/_modal.scss +125 -0
- package/src/styles/_multiselect.scss +69 -0
- package/src/styles/_repo-sync-indicator.scss +99 -0
- package/src/styles/_scale.scss +70 -0
- package/src/styles/_sections.scss +36 -0
- package/src/styles/_server-resources.scss +163 -0
- package/src/styles/_step-builder.scss +376 -0
- package/src/styles/_step-config.scss +195 -0
- package/src/styles/_tabs.scss +54 -0
- package/src/styles/_tester.scss +111 -0
- package/src/styles/_tokens.scss +141 -0
- package/src/styles/_uptime.scss +61 -0
- package/src/styles.css +4208 -0
- package/src/styles.scss +61 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure formatting + geometry helpers for the server-resources family, kept in
|
|
3
|
+
* their own module so they carry no React and can be unit-tested on their own.
|
|
4
|
+
*
|
|
5
|
+
* `ServerResourceSample` is a thin LOCAL copy of the sampler's wire sample
|
|
6
|
+
* (`plan.md` §Pinned wire) — the same deliberate duplication `UptimeStrip.tsx`
|
|
7
|
+
* keeps of the health union, and for the same reason: it keeps `@w6w/ui` a
|
|
8
|
+
* pure-presentation leaf with no transport/spec coupling, so nothing here has
|
|
9
|
+
* to move when the server's payload grows a field.
|
|
10
|
+
*
|
|
11
|
+
* Every helper is TOTAL: an unknown number is never rendered as `0`, it is
|
|
12
|
+
* rendered as an em dash. A node whose CPU reading is missing must not read as
|
|
13
|
+
* an idle node.
|
|
14
|
+
*/
|
|
15
|
+
/** One sample of the host's resource usage, exactly as the server sends it. */
|
|
16
|
+
export interface ServerResourceSample {
|
|
17
|
+
/** ISO-8601 instant the sample was taken. */
|
|
18
|
+
ts: string;
|
|
19
|
+
cpus: number | null;
|
|
20
|
+
cpuLimit: number | null;
|
|
21
|
+
load1: number | null;
|
|
22
|
+
load5: number | null;
|
|
23
|
+
load15: number | null;
|
|
24
|
+
memoryTotal: number | null;
|
|
25
|
+
memoryAvailable: number | null;
|
|
26
|
+
memoryLimit: number | null;
|
|
27
|
+
processRss: number | null;
|
|
28
|
+
processHeapUsed: number | null;
|
|
29
|
+
processHeapTotal: number | null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A byte count as a short human string: exact bytes below 1 KiB (`1023 B`),
|
|
33
|
+
* one decimal place per binary magnitude from KiB up (`1.5 KiB`, `15.6 GiB`).
|
|
34
|
+
* `null` (nothing measured) is `—`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function formatBytes(n: number | null): string;
|
|
37
|
+
/**
|
|
38
|
+
* A 0…1-ish fraction as a whole-percent string (`0.123` → `12%`). Not clamped:
|
|
39
|
+
* a load above the CPU count is real and reads `135%`. `null` is `—`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatPercent(f: number | null): string;
|
|
42
|
+
/**
|
|
43
|
+
* `load1` against the effective CPU budget — the container's `cpuLimit` when
|
|
44
|
+
* the server set one, otherwise the host CPU count. `null` when either operand
|
|
45
|
+
* is unknown or the divisor is ≤ 0 (a zero-CPU budget is not "0% busy", it is
|
|
46
|
+
* "not measurable").
|
|
47
|
+
*/
|
|
48
|
+
export declare function cpuLoadFraction(s: ServerResourceSample | undefined): number | null;
|
|
49
|
+
/**
|
|
50
|
+
* Used memory as a fraction of total: `(memoryTotal − memoryAvailable) /
|
|
51
|
+
* memoryTotal`. `null` when either operand is unknown or the total is ≤ 0.
|
|
52
|
+
*/
|
|
53
|
+
export declare function memoryUsedFraction(s: ServerResourceSample | undefined): number | null;
|
|
54
|
+
/**
|
|
55
|
+
* The `points` attribute of a sparkline polyline, hand-drawn — this package
|
|
56
|
+
* has no chart dependency on purpose (D-5), and a polyline over ~60 samples
|
|
57
|
+
* does not justify one.
|
|
58
|
+
*
|
|
59
|
+
* The x axis is positional (`i / (n − 1) · width`), so a gap in the series
|
|
60
|
+
* still advances the line; `y` is inverted (SVG grows downward) and scaled
|
|
61
|
+
* against `max`. Nulls are SKIPPED, not plotted as zero, and a series of fewer
|
|
62
|
+
* than two values has no line at all (`""` — a single point is not a shape).
|
|
63
|
+
* `max ≤ 0` flattens the line along the baseline rather than dividing by it.
|
|
64
|
+
*
|
|
65
|
+
* Every coordinate is rounded to two decimals so the attribute stays short and
|
|
66
|
+
* a snapshot of it stays readable.
|
|
67
|
+
*/
|
|
68
|
+
export declare function sparklinePoints(values: readonly (number | null)[], width: number, height: number, max: number): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ServerResourceSample } from './server-resources-format.ts';
|
|
2
|
+
/**
|
|
3
|
+
* `count` samples, oldest first, of a host with 8 CPUs under a 2-CPU cgroup
|
|
4
|
+
* limit and 16 GiB of RAM — so the CPU fraction is a fraction of the LIMIT
|
|
5
|
+
* (load ≈ 0.5 of a 2-CPU budget reads ~25%) and the memory fraction moves
|
|
6
|
+
* around 55%. Every series is non-flat, which is the point: a sparkline over a
|
|
7
|
+
* constant is a straight line and proves nothing in a story.
|
|
8
|
+
*/
|
|
9
|
+
export declare function demoServerResourceSamples(count?: number): ServerResourceSample[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Majority precedent across the tree: `admin/src/components/CopyButton.tsx:17`,
|
|
2
|
+
* `studio/src/pages/EndpointsPage.tsx:823`. */
|
|
3
|
+
export declare const COPIED_MS = 1500;
|
|
4
|
+
/**
|
|
5
|
+
* The clipboard-write + copied/revert-timer state machine, factored out of
|
|
6
|
+
* `Copyable` so `JsonEditor` can drive its own in-box button without a second
|
|
7
|
+
* clipboard call site. This is the ONLY spot in `@w6w/ui`'s `src` that writes
|
|
8
|
+
* to the clipboard — `Copyable` and `JsonEditor` both consume it rather than
|
|
9
|
+
* each writing their own.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useCopyToClipboard(value: string, copiedMs?: number): {
|
|
12
|
+
copied: boolean;
|
|
13
|
+
copy: () => Promise<void>;
|
|
14
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { W6WApi } from './provider.tsx';
|
|
2
|
+
export interface CreateW6WApiOptions {
|
|
3
|
+
/** Absolute URL or path prefix — e.g. `"https://w6w.example.com"` or `"/api"`. */
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
/**
|
|
6
|
+
* Bearer token to send. Accepts a string (static) or a supplier function so
|
|
7
|
+
* a JWT that rotates on refresh is fetched fresh on every request. The
|
|
8
|
+
* supplier may be sync or return a `Promise` — either way it is awaited
|
|
9
|
+
* once per request. It receives an optional `{ forceRefresh? }` ctx arg
|
|
10
|
+
* (unused by this client today, but part of the shape so a caller's
|
|
11
|
+
* supplier can share one implementation with `@w6w/sdk`'s `TokenProvider`).
|
|
12
|
+
*/
|
|
13
|
+
token?: string | ((ctx?: {
|
|
14
|
+
forceRefresh?: boolean;
|
|
15
|
+
}) => string | null | undefined | Promise<string | null | undefined>);
|
|
16
|
+
/** Optional fetch replacement — handy for tests. Defaults to `globalThis.fetch`. */
|
|
17
|
+
fetch?: typeof fetch;
|
|
18
|
+
}
|
|
19
|
+
export declare class ApiError extends Error {
|
|
20
|
+
status: number;
|
|
21
|
+
code: string;
|
|
22
|
+
/**
|
|
23
|
+
* The parsed error response body, when the server sent one. Carries the
|
|
24
|
+
* fields that ride alongside an error — e.g. an invoke's `logs` and
|
|
25
|
+
* `apiCalls` — which the message alone would drop.
|
|
26
|
+
*/
|
|
27
|
+
body?: unknown;
|
|
28
|
+
constructor(status: number, code: string, message: string, body?: unknown);
|
|
29
|
+
}
|
|
30
|
+
/** Build a fetch-based W6WApi client bound to a base URL + token supplier. */
|
|
31
|
+
export declare function createW6WApi(opts: CreateW6WApiOptions): W6WApi;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { Edge } from '@xyflow/react';
|
|
2
|
+
import { PortsLookup } from './flow-types.ts';
|
|
3
|
+
import { StepNode } from './flow-utils.ts';
|
|
4
|
+
/**
|
|
5
|
+
* Which lane a React Flow edge belongs to (core `rfcs/workflow.md` · `Edge.when`
|
|
6
|
+
* amendment). **Omitted ⇒ `"success"`**, and read defensively: an edge that
|
|
7
|
+
* predates the `data.when` stamp — or one React Flow synthesised — may carry no
|
|
8
|
+
* `data` at all. The single accessor: every consumer calls this rather than
|
|
9
|
+
* re-deriving the fallback, so "absent means success" is decided once.
|
|
10
|
+
*/
|
|
11
|
+
export declare function edgeLane(e: Edge): "success" | "error";
|
|
12
|
+
/**
|
|
13
|
+
* A refused connection: the source's exit port is full **in this lane** and the cap
|
|
14
|
+
* is finite. Drawing past a finite `out` cap is refused, never satisfied by evicting
|
|
15
|
+
* an existing edge (an unbounded `"many"` out never refuses).
|
|
16
|
+
*/
|
|
17
|
+
export type OutCapRefusal = {
|
|
18
|
+
refused: "out_cap";
|
|
19
|
+
cap: number;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The hard rules for an edge `source → target` — the ones no amount of
|
|
23
|
+
* replacement can satisfy: a real, *distinct* pair (no self-loops), no duplicate
|
|
24
|
+
* edge, the target accepts an entry port (blocks connecting *into* a trigger,
|
|
25
|
+
* which declares `in: 0`), and the source has an exit port. Port **capacity** is
|
|
26
|
+
* deliberately NOT checked here: a full single-slot port is freed by replacement
|
|
27
|
+
* (see `applyConnect`), so dragging a new wire from an already-connected node
|
|
28
|
+
* re-points it rather than being rejected. Used as the live `isValidConnection`.
|
|
29
|
+
*
|
|
30
|
+
* The duplicate-pair check is **lane-blind by design**, and that is a v1 *editor*
|
|
31
|
+
* limit, not a model limit: the model permits a success edge and an error edge
|
|
32
|
+
* between the same two steps (D-T1-8 widened the engine's `skippedEdges` key so
|
|
33
|
+
* they cannot collide), but v1 declines to author that pair because there are no
|
|
34
|
+
* id'd source handles to pick the lane from (D-T1-7).
|
|
35
|
+
*
|
|
36
|
+
* A colliding minted **id** is deliberately NOT rejected here, even though
|
|
37
|
+
* {@link applyConnect} refuses it: this predicate is the live `isValidConnection`,
|
|
38
|
+
* and a `false` here means React Flow never fires `onConnect`, so the editor never
|
|
39
|
+
* gets to *say why*. That refusal is invisible (and, worse, lands in
|
|
40
|
+
* `onConnectEnd`'s "dropped on empty canvas" branch, which opens the step builder).
|
|
41
|
+
* Letting the drop through and refusing in `applyConnect` is what lets the reason
|
|
42
|
+
* paint — see {@link connectConflict}.
|
|
43
|
+
*/
|
|
44
|
+
export declare function canConnect(source: string | null | undefined, target: string | null | undefined, nodes: StepNode[], edges: Edge[], lookup?: PortsLookup): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Build the next edge set for a new `source → target` connection. **Additive at the
|
|
47
|
+
* source**: a source under its `out` cap (or unbounded) keeps every existing edge and
|
|
48
|
+
* gains one; at a finite cap the connection is **refused** (see {@link connectRefusal}),
|
|
49
|
+
* never satisfied by evicting. The target's entry port is still single-slot-replacing:
|
|
50
|
+
* the oldest inbound edge(s) are dropped to make room. Returns `null` when the
|
|
51
|
+
* connection is disallowed by {@link canConnect}, refused at the out cap — **or when the minted id would
|
|
52
|
+
* duplicate a surviving edge's id**, the same refusal {@link setEdgeWhen} performs
|
|
53
|
+
* through the same {@link collidingEdgeId} test.
|
|
54
|
+
*
|
|
55
|
+
* That last one closes the reverse order of the `:error` collision: marking `a → b`
|
|
56
|
+
* as the error lane is refused when `a → "b:error"` already exists, but *first*
|
|
57
|
+
* marking `a → b` Error and *then* drawing `a → "b:error"` used to be allowed and
|
|
58
|
+
* minted a duplicate id — React Flow's id-keyed store then held one wire for two
|
|
59
|
+
* edges (measured `storeSize 1 / 2`). Refusing here is narrow on purpose; rejecting
|
|
60
|
+
* the step id itself is the durable fix and would also have to reject ids inside
|
|
61
|
+
* workflows that already exist (FOLLOWUPS.md). The caller must surface the refusal —
|
|
62
|
+
* see {@link connectConflict}.
|
|
63
|
+
*
|
|
64
|
+
* The source's exit capacity is **per lane** (T3.2.2): success edges compete only
|
|
65
|
+
* with success edges, error edges only with error edges. So a finite `out: 1`
|
|
66
|
+
* step refuses a second success edge *within* a lane,
|
|
67
|
+
* while additionally being able to hold one error edge — which is what makes a
|
|
68
|
+
* `send → (error) → fallback` shape authorable at all. No `ports.out` bump is
|
|
69
|
+
* needed, so every persisted definition and palette entry stays valid.
|
|
70
|
+
*
|
|
71
|
+
* The target's entry capacity stays **lane-blind**, and that asymmetry is the
|
|
72
|
+
* point: a converging tail step receiving one success and one error edge is
|
|
73
|
+
* governed by `ports.in` alone. Partitioning the target rule by lane too — the
|
|
74
|
+
* plausible "symmetry" cleanup — would let a `ports.in: 1` node silently accept
|
|
75
|
+
* two inbound edges.
|
|
76
|
+
*/
|
|
77
|
+
export declare function applyConnect(source: string | null | undefined, target: string | null | undefined, nodes: StepNode[], edges: Edge[], when?: "success" | "error", lookup?: PortsLookup): Edge[] | null;
|
|
78
|
+
/**
|
|
79
|
+
* The id of the existing edge that drawing `source → target` would collide with, or
|
|
80
|
+
* `null` when the connection is safe (or refused for an ordinary reason — a
|
|
81
|
+
* duplicate pair, a self-loop, a portless step — which the drag already showed the
|
|
82
|
+
* user by marking the drop invalid). The creation-path twin of
|
|
83
|
+
* {@link edgeWhenConflict}, and the same channel: it lets the caller *name* the
|
|
84
|
+
* offending edge in an inline refusal instead of losing a wire silently. The editor
|
|
85
|
+
* selects that edge and renders the message in the edge-lane panel (never a browser
|
|
86
|
+
* dialog; see `.ai/conventions.md`).
|
|
87
|
+
*/
|
|
88
|
+
export declare function connectConflict(source: string | null | undefined, target: string | null | undefined, nodes: StepNode[], edges: Edge[], when?: "success" | "error", lookup?: PortsLookup): string | null;
|
|
89
|
+
/**
|
|
90
|
+
* The out-cap refusal for drawing `source → target`, or `null` when the source has
|
|
91
|
+
* room (or is unbounded). The editor renders `cap` in an inline message on the same
|
|
92
|
+
* channel as {@link connectConflict}; {@link applyConnect} returns `null` for it.
|
|
93
|
+
*/
|
|
94
|
+
export declare function connectRefusal(source: string | null | undefined, target: string | null | undefined, nodes: StepNode[], edges: Edge[], when?: "success" | "error", lookup?: PortsLookup): OutCapRefusal | null;
|
|
95
|
+
/**
|
|
96
|
+
* Re-lane one edge. Updates its `data.when`, its visuals and its id, and frees
|
|
97
|
+
* nothing: when the destination lane is at a finite out cap the switch is refused
|
|
98
|
+
* (see {@link relaneRefusal}) — the same rule `applyConnect` applies on creation.
|
|
99
|
+
*
|
|
100
|
+
* Returns a **new** array and mutates nothing (React Flow's state handle compares
|
|
101
|
+
* by identity), or **`null` when the switch must be refused** — the `applyConnect`
|
|
102
|
+
* idiom. It is refused when the re-minted id would duplicate a surviving edge's
|
|
103
|
+
* id, which happens for a step id ending in `:error`: `flowEdgeId` qualifies the
|
|
104
|
+
* error lane with that same suffix, so `a → b` in the error lane and `a → "b:error"`
|
|
105
|
+
* in the success lane mint one id, and React Flow's id-keyed store would collapse
|
|
106
|
+
* them — one wire silently vanishing from the canvas, which reads as the editor
|
|
107
|
+
* losing the author's work. The caller must surface the refusal (see
|
|
108
|
+
* {@link edgeWhenConflict}); rejecting the step id itself is the durable fix and
|
|
109
|
+
* is deliberately NOT done here, because it would also have to reject ids inside
|
|
110
|
+
* workflows that already exist (FOLLOWUPS.md).
|
|
111
|
+
*
|
|
112
|
+
* It is refused for one more reason: an `edgeId` owned by more than one edge. That
|
|
113
|
+
* edge set is already corrupt, and re-laning by id would rewrite **every** match,
|
|
114
|
+
* overwriting the other edge's endpoints (`edgeWhenConflict` returns `null` there —
|
|
115
|
+
* there is no *other* edge to name, so the caller's generic refusal applies).
|
|
116
|
+
*/
|
|
117
|
+
export declare function setEdgeWhen(edges: Edge[], edgeId: string, when: "success" | "error", nodes: StepNode[], lookup?: PortsLookup): Edge[] | null;
|
|
118
|
+
/**
|
|
119
|
+
* The id of the existing edge that {@link setEdgeWhen} would collide with, or
|
|
120
|
+
* `null` when the switch is safe. Lets the caller *name* the offending edge in an
|
|
121
|
+
* inline refusal instead of failing silently — the editor renders it in the
|
|
122
|
+
* edge-lane panel (never a browser dialog; see `.ai/conventions.md`).
|
|
123
|
+
*/
|
|
124
|
+
export declare function edgeWhenConflict(edges: Edge[], edgeId: string, when: "success" | "error", nodes: StepNode[], lookup?: PortsLookup): string | null;
|
|
125
|
+
/** The out-cap refusal for re-laning `edgeId`, or `null` — the re-lane twin of {@link connectRefusal}. */
|
|
126
|
+
export declare function relaneRefusal(edges: Edge[], edgeId: string, when: "success" | "error", nodes: StepNode[], lookup?: PortsLookup): OutCapRefusal | null;
|
|
127
|
+
/**
|
|
128
|
+
* Rewrite the edge set after a step's id changed from `prevId` to `nextId`:
|
|
129
|
+
* re-point every endpoint and **re-mint the id in the edge's own lane**.
|
|
130
|
+
*
|
|
131
|
+
* Extracted from `updateStep` so it is testable, and fixing a real defect: the
|
|
132
|
+
* rewrite used to rebuild every id as `` `${source}->${target}` `` unconditionally,
|
|
133
|
+
* dropping an error edge's `:error` qualifier — so renaming a step collapsed a
|
|
134
|
+
* same-target success+error pair into one edge in React Flow's id-keyed store.
|
|
135
|
+
* That was unreachable while a second outgoing edge deleted the first; per-lane
|
|
136
|
+
* exit capacity is what makes it reachable, hence the fix ships alongside it.
|
|
137
|
+
*/
|
|
138
|
+
export declare function renameStepInEdges(edges: Edge[], prevId: string, nextId: string): Edge[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Edge } from '@xyflow/react';
|
|
2
|
+
import { FlowStep } from './flow-types.ts';
|
|
3
|
+
export type FanOutMode = "sequential" | "parallel";
|
|
4
|
+
/**
|
|
5
|
+
* `count` is the largest number of outgoing edges the step has in any ONE lane
|
|
6
|
+
* (success / error); fan-out only means something at `count >= 2`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function fanOutState(stepId: string, edges: readonly Edge[]): {
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
count: number;
|
|
11
|
+
};
|
|
12
|
+
/** Sequential is the default, so it is expressed by the key's ABSENCE. */
|
|
13
|
+
export declare function setFanOut(step: FlowStep, mode: FanOutMode): FlowStep;
|
|
14
|
+
/** Card-badge wording: how many next steps run, and in which mode. */
|
|
15
|
+
export declare function fanOutLabel(count: number, mode: FanOutMode): string;
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { EdgeRunState } from './run-visuals.ts';
|
|
2
|
+
import { ActionParam } from './types.ts';
|
|
3
|
+
export interface FlowStep {
|
|
4
|
+
id: string;
|
|
5
|
+
uses: {
|
|
6
|
+
app: string;
|
|
7
|
+
action: string;
|
|
8
|
+
connection?: string | null;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Declared connection-port cardinality for this step (see core
|
|
12
|
+
* `rfcs/node-types.md` · Ports & cardinality). Omitted fields fall through field-wise to the catalog action, the internal def, then `{ in: 1, out: "many" }`.
|
|
13
|
+
* A persisted value wins over the node's default — `in > 1` opts the step into
|
|
14
|
+
* accepting multiple inbound edges (e.g. a flow-control aggregator joining
|
|
15
|
+
* several upstream branches).
|
|
16
|
+
*/
|
|
17
|
+
ports?: PortsDecl;
|
|
18
|
+
with?: Record<string, unknown>;
|
|
19
|
+
retry?: {
|
|
20
|
+
maxAttempts: number;
|
|
21
|
+
backoff?: "fixed" | "exponential";
|
|
22
|
+
delayMs?: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* What to do when this step errors:
|
|
26
|
+
* - `fail` — stop the run (default)
|
|
27
|
+
* - `continue` — swallow the error and keep going
|
|
28
|
+
* - `continue-record` — keep going, but record the error into the run's end state
|
|
29
|
+
*/
|
|
30
|
+
onError?: "fail" | "continue" | "continue-record";
|
|
31
|
+
/** Free-form author notes for this step. Not executed. */
|
|
32
|
+
notes?: string;
|
|
33
|
+
/**
|
|
34
|
+
* How this step's outgoing edges in one lane run when there are two or more:
|
|
35
|
+
* `parallel` runs them concurrently; omitted (never written as `"sequential"`)
|
|
36
|
+
* runs them one after another.
|
|
37
|
+
*/
|
|
38
|
+
fanOut?: "sequential" | "parallel";
|
|
39
|
+
/**
|
|
40
|
+
* Authoring-time canvas coordinate for this step, in this editor's own
|
|
41
|
+
* coordinate space (core `rfcs/workflow.md` · "Amendment — 2026-07-29:
|
|
42
|
+
* authoring presentation (`Step.position`, `Workflow.settings`)" — the spec
|
|
43
|
+
* pins no unit, origin or grid). Declarative only: the engine ignores it
|
|
44
|
+
* exactly as it ignores `notes`.
|
|
45
|
+
*
|
|
46
|
+
* **Omitted ⇒ the editor computes a layout** from the graph alone, exactly as
|
|
47
|
+
* before this field existed, so it is additive and backward-compatible.
|
|
48
|
+
* Partial coverage is valid: `workflowToFlow` places the steps that declare a
|
|
49
|
+
* `position` and computes a slot for the rest.
|
|
50
|
+
*
|
|
51
|
+
* Rename-safe by construction — the coordinate travels *with* the step, so
|
|
52
|
+
* there is no workflow-level `id → {x,y}` map to fix up on rename (D-I0-2).
|
|
53
|
+
*
|
|
54
|
+
* Mirrors `Step.position` in `@w6w/workflow-types` by hand — see the header
|
|
55
|
+
* note: these types are deliberately a structural subset with no compiler
|
|
56
|
+
* tie, so the spelling here must stay identical to the engine's.
|
|
57
|
+
*/
|
|
58
|
+
position?: {
|
|
59
|
+
x: number;
|
|
60
|
+
y: number;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export interface FlowEdge {
|
|
64
|
+
from: string;
|
|
65
|
+
to: string;
|
|
66
|
+
/**
|
|
67
|
+
* Which outcome of the `from` step this edge carries (core
|
|
68
|
+
* `rfcs/workflow.md` · "Amendment — 2026-07-29: failure-conditioned edges
|
|
69
|
+
* (`Edge.when`)"). **Omitted ⇒ `"success"`**, so every pre-existing edge is a
|
|
70
|
+
* success edge and needs no migration.
|
|
71
|
+
* - `"success"` — activates when `from` succeeds. Per the house
|
|
72
|
+
* omit-the-default idiom it is written as **absent**, never spelled out:
|
|
73
|
+
* this editor emits no `when` for a success edge, which is what keeps an
|
|
74
|
+
* untouched definition byte-identical across a load/save cycle.
|
|
75
|
+
* - `"error"` — activates when `from` fails, and overrides that step's
|
|
76
|
+
* `onError`.
|
|
77
|
+
*
|
|
78
|
+
* Mirrors `Edge.when` in `@w6w/workflow-types` by hand — see the header note:
|
|
79
|
+
* these types are deliberately a structural subset, with no compiler tie, so
|
|
80
|
+
* the spelling here must stay identical to the engine's.
|
|
81
|
+
*/
|
|
82
|
+
when?: "success" | "error";
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The React Flow presentation for an edge lane — **the one place** the error
|
|
86
|
+
* look is spelled out.
|
|
87
|
+
*
|
|
88
|
+
* Three callers need it and they run at different times: `workflowToFlow` stamps
|
|
89
|
+
* a *stored* edge on load, `setEdgeWhen` (`flow-connect.ts`) stamps an edge the
|
|
90
|
+
* author re-lanes **live**, before any save, and a live-run repaint effect
|
|
91
|
+
* (`WorkflowFlowEditor.tsx`, T1.1.1) restyles an edge as a run polls in. Spelled
|
|
92
|
+
* out more than once, the copies drift and a freshly marked error edge stops
|
|
93
|
+
* looking like a reloaded one. The className is what `styles.css`'s
|
|
94
|
+
* `.w6w-edge-error`/`.w6w-edge-taken`/`.w6w-edge-skipped` blocks paint from
|
|
95
|
+
* `--w6w-danger`/`--w6w-success`/`--w6w-muted`; no custom edge component is
|
|
96
|
+
* involved (D-T1-7).
|
|
97
|
+
*
|
|
98
|
+
* `runState` (T1.1.1) is the edge's OWN taken/skipped verdict (see
|
|
99
|
+
* `run-visuals.ts`'s `edgeRunState`) — omitted (or `undefined`) when there is no
|
|
100
|
+
* run in progress, or the run hasn't decided this edge's fate yet, in which case
|
|
101
|
+
* this returns exactly what it always has. `workflowToFlow`'s and `setEdgeWhen`'s
|
|
102
|
+
* call sites deliberately never have a live run's state available (a *stored*
|
|
103
|
+
* load and a live *re-lane* are never mid-run), so both pass no `runState` at
|
|
104
|
+
* all — only the new repaint-effect call site does.
|
|
105
|
+
*
|
|
106
|
+
* A `"success"`/absent lane with no `runState` returns an **empty object** — not
|
|
107
|
+
* `{ className: "" }` — so an untouched success edge carries neither key and a
|
|
108
|
+
* definition round-trips unchanged. That makes it a trap for a *re-laning*
|
|
109
|
+
* caller: spreading `{}` over an edge that already carries the error class
|
|
110
|
+
* leaves the class in place, so `setEdgeWhen` assigns both keys explicitly
|
|
111
|
+
* rather than spreading.
|
|
112
|
+
*/
|
|
113
|
+
export declare function edgeVisuals(when: "success" | "error" | undefined, runState?: EdgeRunState): {
|
|
114
|
+
className?: string;
|
|
115
|
+
label?: string;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* The React Flow handle id a step/control node's **error exit port** carries
|
|
119
|
+
* (T1.1.1). Internal to `@w6w/ui` — not re-exported from `flow.ts` / `index.ts`
|
|
120
|
+
* (D-P2), since no consumer outside this package needs it.
|
|
121
|
+
*/
|
|
122
|
+
export declare const ERROR_SOURCE_HANDLE = "out-error";
|
|
123
|
+
/**
|
|
124
|
+
* The `sourceHandle` a minted/re-laned/loaded edge carries for a given lane —
|
|
125
|
+
* the inverse of {@link laneForSourceHandle}. `undefined` for `"success"`/
|
|
126
|
+
* absent is **load-bearing, not stylistic**: React Flow anchors a handle-less
|
|
127
|
+
* edge to the node's sole *unnamed* handle, which is the exit every
|
|
128
|
+
* already-saved workflow's edges implicitly use. An empty string would not do
|
|
129
|
+
* this — an empty string is a real, if degenerate, handle id.
|
|
130
|
+
*/
|
|
131
|
+
export declare function sourceHandleForLane(when: "success" | "error" | undefined): string | undefined;
|
|
132
|
+
/**
|
|
133
|
+
* The lane a drag started from, read off the handle id React Flow reports.
|
|
134
|
+
* **The one canonicalisation point** every minting/re-laning site calls
|
|
135
|
+
* (`onConnect`, `onConnectEnd`) so the handle-id spelling is decided once and
|
|
136
|
+
* both drag gestures provably agree — two inline comparisons is the shape
|
|
137
|
+
* where one gesture silently drifts. Exactly {@link ERROR_SOURCE_HANDLE} is
|
|
138
|
+
* `"error"`; everything else — a near-miss id, `null`, `undefined` — is
|
|
139
|
+
* `"success"`.
|
|
140
|
+
*/
|
|
141
|
+
export declare function laneForSourceHandle(handleId: string | null | undefined): "success" | "error";
|
|
142
|
+
export interface FlowWorkflow {
|
|
143
|
+
manifestVersion: string;
|
|
144
|
+
id: string;
|
|
145
|
+
name: string;
|
|
146
|
+
displayName?: string;
|
|
147
|
+
description?: string;
|
|
148
|
+
variables?: Array<{
|
|
149
|
+
key: string;
|
|
150
|
+
type?: string;
|
|
151
|
+
required?: boolean;
|
|
152
|
+
default?: unknown;
|
|
153
|
+
}>;
|
|
154
|
+
steps: FlowStep[];
|
|
155
|
+
edges?: FlowEdge[];
|
|
156
|
+
/**
|
|
157
|
+
* Authoring-time presentation and preferences for this workflow (core
|
|
158
|
+
* `rfcs/workflow.md` · "Amendment — 2026-07-29: authoring presentation
|
|
159
|
+
* (`Step.position`, `Workflow.settings`)"). Declarative only — the engine
|
|
160
|
+
* ignores it, exactly like `FlowStep.notes` and `FlowStep.position`.
|
|
161
|
+
* `viewport` sits *inside* `settings` deliberately, so exactly one new
|
|
162
|
+
* top-level key enters the portable workflow document. `settings` is itself
|
|
163
|
+
* optional; when the object is absent every member below still takes its own
|
|
164
|
+
* default.
|
|
165
|
+
*
|
|
166
|
+
* ⚠️ **`autoSave` and `savePosition` default to `true` when omitted.** That is
|
|
167
|
+
* *deliberately unlike* the `ports`/`onError` rule ("omitted reproduces the
|
|
168
|
+
* status quo") — here it does not. Read them as `?? true` / `!== false`, never
|
|
169
|
+
* `?? false` and never `=== true`. The consequence, stated plainly: **a
|
|
170
|
+
* workflow that has never been saved by the new editor starts persisting step
|
|
171
|
+
* coordinates the first time it is saved.** That is intended (the product
|
|
172
|
+
* requires both features on by default); only an explicit `false` turns one
|
|
173
|
+
* off.
|
|
174
|
+
*
|
|
175
|
+
* HITL-6 asked whether a workflow's arrangement and its auto-save preference
|
|
176
|
+
* belong to *the workflow* or to *the person looking at it*; the amendment
|
|
177
|
+
* takes the **workflow** answer, so everyone who opens a workflow sees the
|
|
178
|
+
* same arrangement. HITL-6 is still open — a viewer-owned answer would move
|
|
179
|
+
* these fields out of the document and re-contract this shape.
|
|
180
|
+
*
|
|
181
|
+
* Mirrors `Workflow.settings` in `@w6w/workflow-types` by hand (structural
|
|
182
|
+
* subset, no compiler tie — the spelling must stay identical).
|
|
183
|
+
*/
|
|
184
|
+
settings?: {
|
|
185
|
+
/**
|
|
186
|
+
* Whether an authoring tool persists edits without an explicit save action.
|
|
187
|
+
* Omitted ⇒ **true** — auto-save is ON. Not read by this module; the editor
|
|
188
|
+
* and its host own it (T3.3.2 / T4.1.x).
|
|
189
|
+
*/
|
|
190
|
+
autoSave?: boolean;
|
|
191
|
+
/**
|
|
192
|
+
* Whether an authoring tool persists step coordinates (`FlowStep.position`)
|
|
193
|
+
* and `settings.viewport` when it saves. Omitted ⇒ **true** — positions ARE
|
|
194
|
+
* persisted. When explicitly `false`, `flowToWorkflow` writes no `position`;
|
|
195
|
+
* any value already stored is **left as it is, never erased**.
|
|
196
|
+
*/
|
|
197
|
+
savePosition?: boolean;
|
|
198
|
+
/**
|
|
199
|
+
* The camera position the author last left the canvas at, so reopening the
|
|
200
|
+
* workflow restores the view. **No default** — a workflow with no `viewport`
|
|
201
|
+
* opens at whatever view the editor computes, exactly as today. Written by
|
|
202
|
+
* the component (T3.3.2), not by `flowToWorkflow`, whose signature cannot
|
|
203
|
+
* see a viewport.
|
|
204
|
+
*/
|
|
205
|
+
viewport?: {
|
|
206
|
+
x: number;
|
|
207
|
+
y: number;
|
|
208
|
+
zoom: number;
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/** Reserved namespace for internal pseudo-apps. */
|
|
213
|
+
export declare const INTERNAL_APP_PREFIX = "@w6w/";
|
|
214
|
+
/** Engine-native flow control (if/foreach/parallel/wait). */
|
|
215
|
+
export declare const CONTROL_APP = "@w6w/control";
|
|
216
|
+
/** Host-run inline JS. */
|
|
217
|
+
export declare const SCRIPT_APP = "@w6w/script";
|
|
218
|
+
/** Host-run typed key/value data. */
|
|
219
|
+
export declare const DATA_APP = "@w6w/data";
|
|
220
|
+
/** The workflow's entry/trigger node. */
|
|
221
|
+
export declare const TRIGGER_APP = "@w6w/trigger";
|
|
222
|
+
/** Host-run outbound HTTP(S) request. */
|
|
223
|
+
export declare const HTTP_APP = "@w6w/http";
|
|
224
|
+
/** Inbound HTTP(S) webhook trigger (entry node; provisions a receive URL). */
|
|
225
|
+
export declare const WEBHOOK_APP = "@w6w/webhook";
|
|
226
|
+
/** Time-based scheduler trigger (entry node; fires the workflow on a schedule). */
|
|
227
|
+
export declare const SCHEDULER_APP = "@w6w/scheduler";
|
|
228
|
+
/** "Respond to Webhook" — shapes the HTTP response for `responseMode: responseNode`. */
|
|
229
|
+
export declare const RESPOND_APP = "@w6w/respond";
|
|
230
|
+
/**
|
|
231
|
+
* Sub-workflow / Function caller (core rfcs/node-types.md · F-3). The host runs
|
|
232
|
+
* this node itself: it resolves `with.target` to a project-scoped `CallableRef` (a
|
|
233
|
+
* Function or a Workflow) and invokes it through `ctx.invokeCallable`, honoring
|
|
234
|
+
* the per-node `with.wait` flag. The engine never loads the target.
|
|
235
|
+
*/
|
|
236
|
+
export declare const CALL_APP = "@w6w/call";
|
|
237
|
+
/**
|
|
238
|
+
* Read a stored JSON document by key (core rfcs/node-types.md · F-3 amendment,
|
|
239
|
+
* "Reserved internal pseudo-app"). Host node: `runInternalNode` short-circuits
|
|
240
|
+
* this id before the registry loads a module — the run's own project is bound
|
|
241
|
+
* host-side, so there is no `project` param (C-6).
|
|
242
|
+
*/
|
|
243
|
+
export declare const DOCUMENT_APP = "@w6w/document";
|
|
244
|
+
/**
|
|
245
|
+
* Compile a Handlebars `template` string against a resolved `values` object
|
|
246
|
+
* (core rfcs/node-types.md · F-3 amendment, "the `@w6w/template` host node").
|
|
247
|
+
* Host node: `runInternalNode` short-circuits this id before the registry
|
|
248
|
+
* loads a module — it needs no privileged host resource (no DB, no
|
|
249
|
+
* tenant/project scope), and runs host-side only because the template
|
|
250
|
+
* compiles to a JS function, the same risk shape `@w6w/script` has.
|
|
251
|
+
*/
|
|
252
|
+
export declare const TEMPLATE_APP = "@w6w/template";
|
|
253
|
+
/** True when `app` is a reserved internal pseudo-app id (`@w6w/*`). */
|
|
254
|
+
export declare function isInternalApp(app: string): boolean;
|
|
255
|
+
/** True when a node is an engine-native flow-control node (can't run standalone). */
|
|
256
|
+
export declare function isControlApp(app: string): boolean;
|
|
257
|
+
/**
|
|
258
|
+
* Whether a step is an entry/trigger node — the one predicate, shared by the
|
|
259
|
+
* step editor's Test tab and the canvas ▶ collect phase. A trigger's configured
|
|
260
|
+
* `fields` are *definitions*, so both surfaces project them into a fillable form
|
|
261
|
+
* and send the filled values as `{ input }` rather than running the raw config.
|
|
262
|
+
*/
|
|
263
|
+
export declare function isTriggerApp(app: string): boolean;
|
|
264
|
+
/** A palette entry for an internal node: its id, label, group, and config schema. */
|
|
265
|
+
export interface InternalNodeDef {
|
|
266
|
+
app: string;
|
|
267
|
+
action: string;
|
|
268
|
+
label: string;
|
|
269
|
+
/**
|
|
270
|
+
* Human display name (mirrors an app's `displayName`). Defaults conceptually
|
|
271
|
+
* to `label`; kept explicit so internal pseudo-apps carry the same info an app
|
|
272
|
+
* does. Shown wherever an app's name would be.
|
|
273
|
+
*/
|
|
274
|
+
displayName: string;
|
|
275
|
+
group: "trigger" | "control" | "compute" | "request";
|
|
276
|
+
/**
|
|
277
|
+
* Inline SVG *inner* markup (paths / circles / polylines) for this primitive's
|
|
278
|
+
* glyph — internal pseudo-apps have no icon asset dir, so the glyph is bundled
|
|
279
|
+
* here. Drawn on a 24×24 `viewBox`, stroked with `currentColor` (theme-aware),
|
|
280
|
+
* so both app nodes and internal nodes display a consistent icon on the canvas.
|
|
281
|
+
*/
|
|
282
|
+
icon: string;
|
|
283
|
+
/**
|
|
284
|
+
* Hidden from every palette list but still resolvable by `internalNodeDef`, so
|
|
285
|
+
* a workflow already using the node keeps rendering and editing.
|
|
286
|
+
*/
|
|
287
|
+
hidden?: boolean;
|
|
288
|
+
/**
|
|
289
|
+
* Connection ports: how many inbound (entry) and outbound (exit) connections
|
|
290
|
+
* this node accepts. A port is the ability to receive/emit a connection —
|
|
291
|
+
* rendered as a React Flow Handle. Defaults to one of each (`{ in: 1, out: 1 }`)
|
|
292
|
+
* when omitted; a trigger overrides to `{ in: 0, out: 1 }` (nothing flows into
|
|
293
|
+
* the entry node). Fixed for now — not user-editable.
|
|
294
|
+
*/
|
|
295
|
+
ports?: PortsDecl;
|
|
296
|
+
/** Config schema (same `ActionParam[]` shape apps declare) rendered by ParamsForm. */
|
|
297
|
+
params: ActionParam[];
|
|
298
|
+
/**
|
|
299
|
+
* This node's static output field keys, when its shape is known ahead of any
|
|
300
|
+
* run — same shape as `ExpressionStepSource.outputs`
|
|
301
|
+
* (`components/ExpressionOptions.tsx`), fed verbatim by `projectStepSources`
|
|
302
|
+
* (`step-preview-state.ts`) so the expression rail can offer
|
|
303
|
+
* `steps.<id>.output.<key>` for a step using this node. Omitted (not `[]`)
|
|
304
|
+
* when the node's output is not statically knowable (e.g. `@w6w/document ·
|
|
305
|
+
* get`, whose shape is whatever JSON the referenced document holds).
|
|
306
|
+
*/
|
|
307
|
+
output?: {
|
|
308
|
+
key: string;
|
|
309
|
+
label?: string;
|
|
310
|
+
}[];
|
|
311
|
+
}
|
|
312
|
+
/** A declared port count: a number, or `"many"` (unbounded). */
|
|
313
|
+
export type PortCount = number | "many";
|
|
314
|
+
/** A (possibly partial) port declaration, as an author / catalog / def writes it. */
|
|
315
|
+
export interface PortsDecl {
|
|
316
|
+
in?: PortCount;
|
|
317
|
+
out?: PortCount;
|
|
318
|
+
}
|
|
319
|
+
/** Resolved inbound (entry) and outbound (exit) connection-port counts; `"many"` is `Infinity`. */
|
|
320
|
+
export interface NodePorts {
|
|
321
|
+
in: number;
|
|
322
|
+
out: number;
|
|
323
|
+
}
|
|
324
|
+
/** The default a node gets when nothing declares ports: 1 in, unbounded out. */
|
|
325
|
+
export declare const DEFAULT_NODE_PORTS: NodePorts;
|
|
326
|
+
/** Field-wise resolution: first declared value per field wins, else the default. */
|
|
327
|
+
export declare function resolvePorts(...layers: (PortsDecl | undefined)[]): NodePorts;
|
|
328
|
+
/** Catalog lookup of an action's declared ports, passed as an argument (never stamped on a step). */
|
|
329
|
+
export type PortsLookup = (app: string, action: string) => PortsDecl | undefined;
|
|
330
|
+
/**
|
|
331
|
+
* Resolve a node's connection ports from its internal def only (triggers drop the
|
|
332
|
+
* entry port); everything else gets the default.
|
|
333
|
+
*/
|
|
334
|
+
export declare function nodePorts(app: string, action: string): NodePorts;
|
|
335
|
+
/**
|
|
336
|
+
* Resolve a *step's* ports field-wise: `step.ports` → catalog action (via `lookup`)
|
|
337
|
+
* → internal def → default `{ in: 1, out: Infinity }`. The catalog is read through
|
|
338
|
+
* the `lookup` argument and never written onto the step, so it never serializes.
|
|
339
|
+
*/
|
|
340
|
+
export declare function nodePortsForStep(step: FlowStep, lookup?: PortsLookup): NodePorts;
|
|
341
|
+
/** The built-in internal nodes, in palette order. */
|
|
342
|
+
export declare const INTERNAL_NODES: InternalNodeDef[];
|
|
343
|
+
/** Look up an internal node's definition by its (app, action) pair. */
|
|
344
|
+
export declare function internalNodeDef(app: string, action: string): InternalNodeDef | undefined;
|
|
345
|
+
/** The label the editor shows for an internal node (falls back to the action key). */
|
|
346
|
+
export declare function internalNodeLabel(app: string, action: string): string;
|
|
347
|
+
/**
|
|
348
|
+
* The inline SVG glyph markup for an internal node (empty when unknown). Lets a
|
|
349
|
+
* node retrieve its icon from just the (app, action) pair — same lookup path as
|
|
350
|
+
* `internalNodeLabel`.
|
|
351
|
+
*/
|
|
352
|
+
export declare function internalNodeIcon(app: string, action: string): string;
|
|
353
|
+
/** The config schema for an internal node (empty when unknown). */
|
|
354
|
+
export declare function internalNodeParams(app: string, action: string): ActionParam[];
|
|
355
|
+
/** Build an internal node's default `with` from its param schema defaults. */
|
|
356
|
+
export declare function internalNodeDefaults(app: string, action: string): Record<string, unknown>;
|