@w6w/ui 0.1.1 → 0.2.11
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 +27 -0
- package/README.md +7 -5
- package/dist/AppPicker.d.ts +48 -2
- package/dist/CanvasLayer.d.ts +38 -0
- package/dist/{CodeBlock-Dot3Pd7P.js → CodeBlock-BqXDA35P.js} +2 -2
- package/dist/{CodeBlock-Dot3Pd7P.js.map → CodeBlock-BqXDA35P.js.map} +1 -1
- package/dist/{CodeBlock-CZu4TFSi.cjs → CodeBlock-CulfbnZD.cjs} +2 -2
- package/dist/{CodeBlock-CZu4TFSi.cjs.map → CodeBlock-CulfbnZD.cjs.map} +1 -1
- package/dist/ConfirmModal-Ln99S9NT.js +7919 -0
- package/dist/ConfirmModal-Ln99S9NT.js.map +1 -0
- package/dist/ConfirmModal-fjHW5rN-.cjs +14 -0
- package/dist/ConfirmModal-fjHW5rN-.cjs.map +1 -0
- package/dist/ConnectionHeader.d.ts +19 -0
- package/dist/Copyable-DA6eiU7w.cjs +2 -0
- package/dist/Copyable-DA6eiU7w.cjs.map +1 -0
- package/dist/{Copyable-1niV9gUa.js → Copyable-F4NTaG77.js} +91 -15
- package/dist/Copyable-F4NTaG77.js.map +1 -0
- package/dist/EditConnectionModal.d.ts +26 -0
- package/dist/JsonEditor.d.ts +8 -0
- package/dist/Markdown.d.ts +25 -0
- package/dist/ParamsForm.d.ts +7 -1
- package/dist/StepBuilderModal.d.ts +23 -5
- package/dist/WorkflowFlowEditor.d.ts +14 -3
- package/dist/app-pages.d.ts +5 -0
- package/dist/canvas-types.d.ts +36 -0
- package/dist/canvas-utils.d.ts +44 -0
- package/dist/code.cjs +1 -1
- package/dist/code.mjs +2 -2
- package/dist/components/AuthFieldsForm.d.ts +19 -1
- package/dist/components/BudgetMeter.d.ts +15 -0
- package/dist/components/CallableList.d.ts +58 -0
- package/dist/components/Combobox.d.ts +8 -1
- package/dist/components/ConnectionList.d.ts +43 -0
- package/dist/components/CredentialPicker.d.ts +35 -0
- package/dist/components/DataTable.d.ts +27 -0
- package/dist/components/EmptyState.d.ts +9 -0
- package/dist/components/ListItem.d.ts +16 -6
- package/dist/components/ReliabilityChart.d.ts +10 -0
- package/dist/components/ReliabilityList.d.ts +47 -0
- package/dist/components/icons.d.ts +1 -1
- package/dist/connection-app-meta.d.ts +32 -0
- package/dist/dynamic-fields.d.ts +60 -0
- package/dist/editor-theme.d.ts +24 -0
- package/dist/flow-fanout.d.ts +2 -2
- package/dist/flow-types.d.ts +18 -1
- package/dist/flow-utils.d.ts +10 -3
- package/dist/flow.cjs +2 -2
- package/dist/flow.cjs.map +1 -1
- package/dist/flow.d.ts +9 -0
- package/dist/flow.mjs +1519 -1138
- package/dist/flow.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +30 -2
- package/dist/index.mjs +2053 -1005
- package/dist/index.mjs.map +1 -1
- package/dist/note-markdown.d.ts +22 -0
- package/dist/provider.d.ts +73 -5
- package/dist/reliability-board-layout.d.ts +121 -0
- package/dist/resolve-params.d.ts +5 -0
- package/dist/run-visuals.d.ts +4 -1
- package/dist/step-problems.d.ts +34 -0
- package/dist/types.d.ts +117 -3
- package/dist/use-dynamic-fields.d.ts +23 -0
- package/package.json +5 -3
- package/src/styles/_budget-meter.scss +46 -0
- package/src/styles/_callable-list.scss +69 -0
- package/src/styles/_canvas.scss +318 -0
- package/src/styles/_connections.scss +88 -0
- package/src/styles/_data-table.scss +55 -0
- package/src/styles/_empty-state.scss +21 -0
- package/src/styles/_flow.scss +11 -0
- package/src/styles/_fx.scss +23 -0
- package/src/styles/_layout.scss +10 -0
- package/src/styles/_lists.scss +4 -0
- package/src/styles/_markdown.scss +20 -0
- package/src/styles/_reliability.scss +339 -0
- package/src/styles/_tester.scss +1 -14
- package/src/styles.css +1142 -17
- package/src/styles.scss +8 -0
- package/dist/ConfirmModal-8avl9Nv8.cjs +0 -11
- package/dist/ConfirmModal-8avl9Nv8.cjs.map +0 -1
- package/dist/ConfirmModal-CD-P8mhN.js +0 -6335
- package/dist/ConfirmModal-CD-P8mhN.js.map +0 -1
- package/dist/Copyable-1niV9gUa.js.map +0 -1
- package/dist/Copyable-CYZU1o4X.cjs +0 -2
- package/dist/Copyable-CYZU1o4X.cjs.map +0 -1
|
@@ -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;
|
package/dist/provider.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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 { ActionDef, ApiCallRecord, AppSummary, AuthDef, ConnectionBinding, ConnectionSummary, CreateConnectionBody, CredentialDependents, CredentialSummary, CredentialTestResult, FunctionDetail, FunctionSummary, ResolveFieldsBody, ResolveFieldsResult, SavedTest, SubscriptionSummary, ThemeMode, TriggerSummary, WorkflowDetail, WorkflowSummary } from './types.ts';
|
|
3
3
|
/**
|
|
4
4
|
* A single tester-run summary from the unified `run_log` ledger, keyed to a
|
|
5
5
|
* connection. Thin by design — the full result payload stays server-side in
|
|
@@ -111,6 +111,8 @@ export interface ListAppsPageOptions {
|
|
|
111
111
|
q?: string;
|
|
112
112
|
/** Server-side category filter (e.g. `"ai"`). */
|
|
113
113
|
category?: string;
|
|
114
|
+
/** Server-side id allow-list (`GET /apps?ids=a,b`); `total` is then the filtered total. */
|
|
115
|
+
ids?: string[];
|
|
114
116
|
/** Opaque pagination cursor from a prior {@link AppsPageResult.nextCursor}. */
|
|
115
117
|
cursor?: string;
|
|
116
118
|
/** Page size; a host may clamp this to its own bounds. */
|
|
@@ -119,12 +121,21 @@ export interface ListAppsPageOptions {
|
|
|
119
121
|
compact?: boolean;
|
|
120
122
|
/** Abort this request. Local request control only; never a wire field. */
|
|
121
123
|
signal?: AbortSignal;
|
|
124
|
+
/** Snapshot id from a prior {@link AppsPageResult.snapshot}. Navigation normally uses `cursor` alone. */
|
|
125
|
+
snapshot?: string;
|
|
126
|
+
/** Absolute offset into the snapshot. */
|
|
127
|
+
offset?: number;
|
|
122
128
|
}
|
|
123
129
|
/** One page of apps, as returned by {@link W6WApi.listAppsPage}. */
|
|
124
130
|
export interface AppsPageResult {
|
|
125
131
|
apps: AppSummary[];
|
|
126
132
|
/** Present unless this is the last page. */
|
|
127
133
|
nextCursor?: string;
|
|
134
|
+
snapshot?: string;
|
|
135
|
+
total?: number;
|
|
136
|
+
offset?: number;
|
|
137
|
+
limit?: number;
|
|
138
|
+
expiresAt?: string;
|
|
128
139
|
}
|
|
129
140
|
export interface W6WApi {
|
|
130
141
|
/**
|
|
@@ -165,15 +176,67 @@ export interface W6WApi {
|
|
|
165
176
|
listAppsByIds?(ids: readonly string[], options?: {
|
|
166
177
|
signal?: AbortSignal;
|
|
167
178
|
}): Promise<AppSummary[]>;
|
|
179
|
+
/**
|
|
180
|
+
* OPTIONAL: the tenant's model routes, for the `model-route` step-param
|
|
181
|
+
* picker (`@w6w/model` `complete`). Absent, the param renders as a plain text
|
|
182
|
+
* input and this is never called. A `broken` route is still selectable.
|
|
183
|
+
* `project` is the editor's own project (`WorkflowProjectProvider`) when it
|
|
184
|
+
* has one — a host should list THAT project's routes, not guess one from its
|
|
185
|
+
* URL; `undefined` leaves the choice to the host.
|
|
186
|
+
*/
|
|
187
|
+
listModelRoutes?(project?: string): Promise<{
|
|
188
|
+
name: string;
|
|
189
|
+
model: string;
|
|
190
|
+
status: "active" | "broken";
|
|
191
|
+
}[]>;
|
|
192
|
+
/**
|
|
193
|
+
* OPTIONAL: resolve the choices of `options: { source: "resolveFields" }`
|
|
194
|
+
* params from the current form (`POST` to the host). Absent, those params
|
|
195
|
+
* render as plain inputs and this is never called.
|
|
196
|
+
*/
|
|
197
|
+
resolveActionFields?(appId: string, actionKey: string, body: ResolveFieldsBody, opts?: {
|
|
198
|
+
signal?: AbortSignal;
|
|
199
|
+
}): Promise<ResolveFieldsResult>;
|
|
168
200
|
/** Load auth methods declared by an app's manifest, with availability flags. */
|
|
169
201
|
getAppAuth(appId: string): Promise<AuthDef[]>;
|
|
170
202
|
/** Create a non-OAuth connection with a user-supplied credential. */
|
|
171
|
-
createConnection(appId: string, body:
|
|
172
|
-
|
|
173
|
-
|
|
203
|
+
createConnection(appId: string, body: CreateConnectionBody): Promise<ConnectionSummary>;
|
|
204
|
+
/**
|
|
205
|
+
* Rename a connection and/or replace some of its credential fields (`PATCH /connections/:id`).
|
|
206
|
+
* Send only the fields being changed: the server merges a partial `credential` over the stored
|
|
207
|
+
* one. Optional: when absent, `EditConnectionModal` shows no Save button (a one-line notice
|
|
208
|
+
* instead) and hosts should not offer an Edit action. It never throws for the absence.
|
|
209
|
+
*/
|
|
210
|
+
updateConnection?(id: string, body: {
|
|
174
211
|
displayName?: string;
|
|
175
|
-
|
|
212
|
+
binding?: ConnectionBinding;
|
|
213
|
+
credentialId?: string;
|
|
214
|
+
credential?: Record<string, unknown>;
|
|
176
215
|
}): Promise<ConnectionSummary>;
|
|
216
|
+
/** List stored credentials, optionally narrowed (`GET /credentials`). */
|
|
217
|
+
listCredentials?(filter?: {
|
|
218
|
+
contract?: string;
|
|
219
|
+
appId?: string;
|
|
220
|
+
authKey?: string;
|
|
221
|
+
}): Promise<CredentialSummary[]>;
|
|
222
|
+
/** Store a new credential (`POST /credentials`). `source` is the auth's field-value record. */
|
|
223
|
+
createCredential?(body: {
|
|
224
|
+
name: string;
|
|
225
|
+
contract: string;
|
|
226
|
+
source: Record<string, unknown>;
|
|
227
|
+
}): Promise<CredentialSummary>;
|
|
228
|
+
getCredential?(id: string): Promise<CredentialSummary>;
|
|
229
|
+
/** Replace a credential's secret (`POST /credentials/:id/rotate`). */
|
|
230
|
+
rotateCredential?(id: string, body: {
|
|
231
|
+
source: Record<string, unknown>;
|
|
232
|
+
}): Promise<CredentialSummary>;
|
|
233
|
+
/** Disable (`/disable`) or re-enable (`/enable`) a credential. */
|
|
234
|
+
setCredentialStatus?(id: string, status: "active" | "disabled"): Promise<CredentialSummary>;
|
|
235
|
+
/** Mint-level health check; distinct from a connection's app-access test. */
|
|
236
|
+
testCredential?(id: string): Promise<CredentialTestResult>;
|
|
237
|
+
credentialDependents?(id: string): Promise<CredentialDependents>;
|
|
238
|
+
/** Rejects with `ApiError` code `credential_in_use` (body.error.dependents) when in use. */
|
|
239
|
+
deleteCredential?(id: string): Promise<void>;
|
|
177
240
|
/**
|
|
178
241
|
* Start an OAuth 2.0 flow. Server builds the provider's authorize URL and
|
|
179
242
|
* returns it; the caller opens it in a popup and awaits the server's
|
|
@@ -412,6 +475,11 @@ export declare function W6WUIProvider({ api, theme, children }: W6WUIProviderPro
|
|
|
412
475
|
* `<W6WUIProvider>` — the common mistake is forgetting to wrap the app root.
|
|
413
476
|
*/
|
|
414
477
|
export declare function useW6WApi(): W6WApi;
|
|
478
|
+
/**
|
|
479
|
+
* Like {@link useW6WApi} but returns `undefined` outside a provider instead of
|
|
480
|
+
* throwing — for optional enhancements (e.g. the `model-route` picker).
|
|
481
|
+
*/
|
|
482
|
+
export declare function useOptionalW6WApi(): W6WApi | undefined;
|
|
415
483
|
/** Scopes test-invokes under it to `project`; the editor wraps its body with this. */
|
|
416
484
|
export declare function WorkflowProjectProvider({ project, children, }: {
|
|
417
485
|
project?: string;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How the reliability board arranges itself — the decisions that are arithmetic
|
|
3
|
+
* rather than markup, kept pure (no React, no DOM) so they test without a DOM
|
|
4
|
+
* and so `@w6w/embed` can import them beside the components.
|
|
5
|
+
*
|
|
6
|
+
* **None of these decide a STATE.** Every state read below arrived already
|
|
7
|
+
* decided by the server; nothing here compares a ratio, applies a threshold to
|
|
8
|
+
* call data, or invents a fifth word. What these decide is where a row is PUT,
|
|
9
|
+
* which is presentation.
|
|
10
|
+
*/
|
|
11
|
+
/** The four words the server uses for a health verdict. */
|
|
12
|
+
export type ReliabilityViewState = "ok" | "degraded" | "down" | "unknown";
|
|
13
|
+
/**
|
|
14
|
+
* The row shape the reliability components render — owned by `@w6w/ui` so it
|
|
15
|
+
* imports nothing from the SDK. The SDK's `ReliabilityService` assigns to it
|
|
16
|
+
* structurally; the generic functions below keep the caller's richer row type.
|
|
17
|
+
*/
|
|
18
|
+
export interface ReliabilityServiceView {
|
|
19
|
+
appId: string;
|
|
20
|
+
displayName: string;
|
|
21
|
+
/** Inline icon fields — the board never asks a catalog for them. */
|
|
22
|
+
iconSrc?: string;
|
|
23
|
+
iconSrcDark?: string;
|
|
24
|
+
brandColor?: string;
|
|
25
|
+
/** "Did YOUR calls succeed". */
|
|
26
|
+
state: ReliabilityViewState;
|
|
27
|
+
calls: number;
|
|
28
|
+
errors: {
|
|
29
|
+
e4xx: number;
|
|
30
|
+
e429: number;
|
|
31
|
+
eAuth: number;
|
|
32
|
+
e5xx: number;
|
|
33
|
+
};
|
|
34
|
+
p95Ms: number | null;
|
|
35
|
+
ongoingOutageId: string | null;
|
|
36
|
+
/** One entry per calendar day, oldest first, exactly as the server sent it. */
|
|
37
|
+
uptime: readonly {
|
|
38
|
+
day: string;
|
|
39
|
+
state: ReliabilityViewState;
|
|
40
|
+
}[];
|
|
41
|
+
/** "What does the vendor say about itself"; `null` when no public check exists. */
|
|
42
|
+
vendorStatus: {
|
|
43
|
+
state: ReliabilityViewState;
|
|
44
|
+
message: string | null;
|
|
45
|
+
} | null;
|
|
46
|
+
/** The server's tier for the row — read, never re-derived. */
|
|
47
|
+
attention: "now" | "recent" | "none";
|
|
48
|
+
}
|
|
49
|
+
/** One point of the performance scatter — a service with both a call volume and a measured p95. */
|
|
50
|
+
export interface PerformancePoint {
|
|
51
|
+
appId: string;
|
|
52
|
+
displayName: string;
|
|
53
|
+
calls: number;
|
|
54
|
+
p95Ms: number;
|
|
55
|
+
state: ReliabilityViewState;
|
|
56
|
+
/** Total errors across all four kinds — drives the point's size, not its position. */
|
|
57
|
+
errorCount: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Every service with at least one timed call, as a (calls, p95) point — what
|
|
61
|
+
* the performance chart plots. Excludes `p95Ms: null`: there is no Y position
|
|
62
|
+
* for a service with nothing timed.
|
|
63
|
+
*/
|
|
64
|
+
export declare function performancePoints(services: readonly ReliabilityServiceView[]): PerformancePoint[];
|
|
65
|
+
/**
|
|
66
|
+
* Does this service want the reader's attention? READS the tier the server
|
|
67
|
+
* sent (`attention`), never re-derives it from `state` or `vendorStatus`: the
|
|
68
|
+
* server's `boardRank` is the one implementation, and a second copy here would
|
|
69
|
+
* drift. `now` and `recent` both count; they differ in tense only.
|
|
70
|
+
*/
|
|
71
|
+
export declare function needsAttention(service: Pick<ReliabilityServiceView, "attention">): boolean;
|
|
72
|
+
/** The board in its three groups, each keeping the server's own ordering. */
|
|
73
|
+
export interface BoardPartition<T extends ReliabilityServiceView = ReliabilityServiceView> {
|
|
74
|
+
/** Broken right now, on either signal. */
|
|
75
|
+
now: T[];
|
|
76
|
+
/** Clear now, but this window holds a failed stretch or an open advisory. */
|
|
77
|
+
recent: T[];
|
|
78
|
+
/** Everything else, in the server's recency order. */
|
|
79
|
+
clear: T[];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Group the board without reordering within any group. Bucketing is not
|
|
83
|
+
* sorting: each group comes out in the order it arrived. An unrecognised
|
|
84
|
+
* `attention` value falls to `clear` rather than throwing — a client can be
|
|
85
|
+
* older than the server that answered it.
|
|
86
|
+
*/
|
|
87
|
+
export declare function partitionBoard<T extends ReliabilityServiceView>(services: readonly T[]): BoardPartition<T>;
|
|
88
|
+
/**
|
|
89
|
+
* The uptime strip's cells for one service — the server's own array, in the
|
|
90
|
+
* order it arrived (oldest first). A passthrough on purpose: no sort, no
|
|
91
|
+
* reverse, no gap filling. The strip has no date axis, so a reversed array is
|
|
92
|
+
* visually plausible and would silently move an outage to the wrong end.
|
|
93
|
+
*/
|
|
94
|
+
export declare function uptimeDays(service: Pick<ReliabilityServiceView, "uptime">): {
|
|
95
|
+
day: string;
|
|
96
|
+
state: ReliabilityViewState;
|
|
97
|
+
}[];
|
|
98
|
+
/** Compact mode shows at most this many needs-attention cards, `now` filling first. */
|
|
99
|
+
export declare const ATTENTION_DISPLAY_CAP = 3;
|
|
100
|
+
/** Compact mode shows this many table rows unless the caller passes `limit`. */
|
|
101
|
+
export declare const RECENT_DISPLAY_CAP = 5;
|
|
102
|
+
/** What the board shows and what a display cap hid. */
|
|
103
|
+
export interface BoardDisplay<T extends ReliabilityServiceView = ReliabilityServiceView> {
|
|
104
|
+
/** TRUE total of attention rows (the whole input, not the capped slice). */
|
|
105
|
+
attentionCount: number;
|
|
106
|
+
shownNow: T[];
|
|
107
|
+
shownRecent: T[];
|
|
108
|
+
hiddenAttentionCount: number;
|
|
109
|
+
/** Table rows in the server's order, every attention tier included. */
|
|
110
|
+
shownTable: T[];
|
|
111
|
+
hiddenTableCount: number;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Apply the display caps to the complete board. Compact: ≤ 3 attention cards
|
|
115
|
+
* (`now` first, `recent` tops up) and `limit ?? 5` table rows; otherwise
|
|
116
|
+
* uncapped. Hidden counts are computed from the full input, so they are real.
|
|
117
|
+
*/
|
|
118
|
+
export declare function displayBoard<T extends ReliabilityServiceView>(services: readonly T[], opts?: {
|
|
119
|
+
compact?: boolean;
|
|
120
|
+
limit?: number;
|
|
121
|
+
}): BoardDisplay<T>;
|
package/dist/resolve-params.d.ts
CHANGED
|
@@ -38,6 +38,11 @@ export interface ResolveScope {
|
|
|
38
38
|
output?: unknown;
|
|
39
39
|
meta?: StepMeta;
|
|
40
40
|
}>;
|
|
41
|
+
/** `nodes.<id>…` root — the SAME object as `steps` (one state, two roots). */
|
|
42
|
+
nodes?: Record<string, {
|
|
43
|
+
output?: unknown;
|
|
44
|
+
meta?: StepMeta;
|
|
45
|
+
}>;
|
|
41
46
|
trigger: {
|
|
42
47
|
event?: unknown;
|
|
43
48
|
};
|
package/dist/run-visuals.d.ts
CHANGED
|
@@ -28,7 +28,10 @@ export interface StepExecution {
|
|
|
28
28
|
* as the engine's own `RunState.steps`. */
|
|
29
29
|
export interface RunState {
|
|
30
30
|
status: RunStatus;
|
|
31
|
-
|
|
31
|
+
/** Canonical per-node records (D2). */
|
|
32
|
+
nodes?: Record<string, StepExecution>;
|
|
33
|
+
/** Legacy alias of `nodes`; read as `nodes ?? steps` (un-upgraded server). */
|
|
34
|
+
steps?: Record<string, StepExecution>;
|
|
32
35
|
}
|
|
33
36
|
/**
|
|
34
37
|
* A step node's rendered state — exactly the five {@link StepStatus} values
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-supplied per-step problems painted on the canvas. Pure — no React or
|
|
3
|
+
* @xyflow imports. The host owns detection; ui never infers a problem itself.
|
|
4
|
+
*/
|
|
5
|
+
export interface StepProblem {
|
|
6
|
+
code: "unknown_connection";
|
|
7
|
+
connection: string;
|
|
8
|
+
}
|
|
9
|
+
/** Keyed by step id. */
|
|
10
|
+
export type StepProblems = Record<string, StepProblem>;
|
|
11
|
+
interface ProblemNode {
|
|
12
|
+
id: string;
|
|
13
|
+
data: {
|
|
14
|
+
stepProblem?: StepProblem;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Stamp `data.stepProblem` from `problems`, clearing it for steps no longer in
|
|
19
|
+
* the map. Returns the SAME node object when nothing changed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function applyStepProblems<N extends ProblemNode>(nodes: N[], problems: StepProblems | undefined): N[];
|
|
22
|
+
export declare function stepProblemTooltip(p: StepProblem): string;
|
|
23
|
+
/** True while the step still points at the connection the host reports missing. */
|
|
24
|
+
export declare function isConnectionBlocked(step: {
|
|
25
|
+
uses: {
|
|
26
|
+
connection?: string | null;
|
|
27
|
+
};
|
|
28
|
+
}, problem: StepProblem | undefined): boolean;
|
|
29
|
+
/** The Setup tab's Connection button label — exactly five outcomes. */
|
|
30
|
+
export declare function connectionButtonLabel(connection: string | undefined | null, conns: {
|
|
31
|
+
id: string;
|
|
32
|
+
displayName?: string;
|
|
33
|
+
}[] | null, failed: boolean): string;
|
|
34
|
+
export {};
|
package/dist/types.d.ts
CHANGED
|
@@ -22,6 +22,11 @@ export interface AppSummary {
|
|
|
22
22
|
maturity?: string;
|
|
23
23
|
visibility?: string;
|
|
24
24
|
successor?: string;
|
|
25
|
+
/** Model-provider designation; absent when the app does not declare one. */
|
|
26
|
+
modelProvider?: {
|
|
27
|
+
modelTypes: string[];
|
|
28
|
+
modelFamilies?: string[];
|
|
29
|
+
};
|
|
25
30
|
/**
|
|
26
31
|
* True when the calling tenant has zero-credential access flagged for this
|
|
27
32
|
* app — usable with no connection step at all (server-computed per caller;
|
|
@@ -68,6 +73,8 @@ export interface AuthField {
|
|
|
68
73
|
required?: boolean;
|
|
69
74
|
default?: unknown;
|
|
70
75
|
hint?: string;
|
|
76
|
+
/** Masks the input like a `secret` field whatever its `type` (e.g. a `json` key file). */
|
|
77
|
+
secret?: boolean;
|
|
71
78
|
}
|
|
72
79
|
/**
|
|
73
80
|
* One declared parameter of an action, as returned by GET /apps/:id (the
|
|
@@ -119,7 +126,7 @@ export interface ActionParam {
|
|
|
119
126
|
* Constrained choices. Renders as a single-select dropdown for `select` (or any
|
|
120
127
|
* param with options), and as a multi-select pill picker for `multiselect`.
|
|
121
128
|
*/
|
|
122
|
-
options?: ParamOption[];
|
|
129
|
+
options?: ParamOption[] | DynamicOptions;
|
|
123
130
|
/** Type-specific render/behavior options. */
|
|
124
131
|
config?: ParamConfig;
|
|
125
132
|
/**
|
|
@@ -146,6 +153,34 @@ export interface ActionParam {
|
|
|
146
153
|
export interface ParamOption {
|
|
147
154
|
value: string | number;
|
|
148
155
|
label: string;
|
|
156
|
+
description?: string;
|
|
157
|
+
}
|
|
158
|
+
/** Host-resolved options (mirrors @w6w/types `DynamicOptions`). */
|
|
159
|
+
export interface DynamicOptions {
|
|
160
|
+
source: string;
|
|
161
|
+
searchable?: boolean;
|
|
162
|
+
cache?: "session" | "none";
|
|
163
|
+
refreshOn?: string[];
|
|
164
|
+
}
|
|
165
|
+
/** True for the form-resolved arm: a non-array object with `source === "resolveFields"`. */
|
|
166
|
+
export declare function isFormResolvedOptions(o: unknown): o is DynamicOptions;
|
|
167
|
+
/** Body of `W6WApi.resolveActionFields`. */
|
|
168
|
+
export interface ResolveFieldsBody {
|
|
169
|
+
connectionId: string;
|
|
170
|
+
version?: string;
|
|
171
|
+
reason: "init" | "change";
|
|
172
|
+
form: Record<string, unknown>;
|
|
173
|
+
previous?: Record<string, unknown>;
|
|
174
|
+
changed?: string[];
|
|
175
|
+
fields: string[];
|
|
176
|
+
unresolved: string[];
|
|
177
|
+
}
|
|
178
|
+
/** Result of `W6WApi.resolveActionFields`. */
|
|
179
|
+
export interface ResolveFieldsResult {
|
|
180
|
+
fields: Record<string, {
|
|
181
|
+
options?: ParamOption[];
|
|
182
|
+
error?: string;
|
|
183
|
+
}>;
|
|
149
184
|
}
|
|
150
185
|
/**
|
|
151
186
|
* The element schema for a `type: "array"` param. Either a scalar list
|
|
@@ -184,6 +219,8 @@ export interface ParamCondition {
|
|
|
184
219
|
export interface ParamConfig {
|
|
185
220
|
/** Render as a multi-line textarea. Implied by `type: "text"`. */
|
|
186
221
|
multiline?: boolean;
|
|
222
|
+
/** With options: accept a value not in the list (Combobox `forceSelection` off). */
|
|
223
|
+
allowCustom?: boolean;
|
|
187
224
|
}
|
|
188
225
|
/** Summary of an action an app exposes, as returned by GET /apps/:id. */
|
|
189
226
|
export interface ActionDef {
|
|
@@ -267,16 +304,83 @@ export interface WorkflowDetail {
|
|
|
267
304
|
name: string;
|
|
268
305
|
displayName?: string;
|
|
269
306
|
description?: string;
|
|
270
|
-
|
|
307
|
+
/** The canonical (manifest v2) step list. */
|
|
308
|
+
nodes?: WorkflowStepSummary[];
|
|
309
|
+
/** The legacy (v1) step list; a definition carries one or the other. */
|
|
310
|
+
steps?: WorkflowStepSummary[];
|
|
311
|
+
}
|
|
312
|
+
/** A `serviceAccount` auth's declaration: the credential contract it accepts and its binding. */
|
|
313
|
+
export interface ServiceAccountConfig {
|
|
314
|
+
/** Credential contract ids this auth accepts; the first one is used to list/create. */
|
|
315
|
+
accepts: string[];
|
|
316
|
+
/** Scopes the binding requests by default (shown read-only; the server applies them). */
|
|
317
|
+
scopes: string[];
|
|
318
|
+
/** Whether a connection must/may name an impersonated user (`delegatedUser`). */
|
|
319
|
+
impersonation: "none" | "optional" | "required";
|
|
320
|
+
/** Extra per-connection (binding) fields; each lands in `binding.settings`. */
|
|
321
|
+
bindingFields?: AuthField[];
|
|
322
|
+
}
|
|
323
|
+
export type CredentialStatus = "active" | "disabled" | "revoked";
|
|
324
|
+
/** A stored credential as the API lists it. Never carries the secret payload. */
|
|
325
|
+
export interface CredentialSummary {
|
|
326
|
+
id: string;
|
|
327
|
+
name: string;
|
|
328
|
+
contract: string;
|
|
329
|
+
status: CredentialStatus | string;
|
|
330
|
+
revision: number;
|
|
331
|
+
/** Non-secret display values (e.g. the service account's email). */
|
|
332
|
+
display: Record<string, string>;
|
|
333
|
+
private: boolean;
|
|
334
|
+
connectionCount: number;
|
|
335
|
+
createdAt: string;
|
|
336
|
+
updatedAt: string;
|
|
337
|
+
}
|
|
338
|
+
/** The connections that use a credential. */
|
|
339
|
+
export interface CredentialDependents {
|
|
340
|
+
connections: Array<{
|
|
341
|
+
id: string;
|
|
342
|
+
appId: string;
|
|
343
|
+
authKey: string;
|
|
344
|
+
displayName?: string | null;
|
|
345
|
+
}>;
|
|
271
346
|
}
|
|
347
|
+
export interface CredentialTestResult {
|
|
348
|
+
ok: boolean;
|
|
349
|
+
code?: string;
|
|
350
|
+
message?: string;
|
|
351
|
+
}
|
|
352
|
+
/** Per-connection settings applied on top of a shared credential. */
|
|
353
|
+
export interface ConnectionBinding {
|
|
354
|
+
scopes?: string[];
|
|
355
|
+
/** The user the credential impersonates (Google domain-wide delegation). */
|
|
356
|
+
delegatedUser?: string;
|
|
357
|
+
settings?: Record<string, string | number | boolean>;
|
|
358
|
+
}
|
|
359
|
+
/** Body of `createConnection`: an inline credential XOR a reference to a stored one. */
|
|
360
|
+
export type CreateConnectionBody = {
|
|
361
|
+
authKey: string;
|
|
362
|
+
credential: Record<string, unknown>;
|
|
363
|
+
credentialId?: never;
|
|
364
|
+
displayName?: string;
|
|
365
|
+
profile?: Record<string, unknown>;
|
|
366
|
+
} | {
|
|
367
|
+
authKey: string;
|
|
368
|
+
credentialId: string;
|
|
369
|
+
credential?: never;
|
|
370
|
+
binding?: ConnectionBinding;
|
|
371
|
+
displayName?: string;
|
|
372
|
+
profile?: never;
|
|
373
|
+
};
|
|
272
374
|
/** Auth method declaration as exposed by an app's manifest. */
|
|
273
375
|
export interface AuthDef {
|
|
274
376
|
key: string;
|
|
275
|
-
type: "oauth2" | "apiKey" | "basic" | "bearer" | "custom" | "tenantAuth" | "jit" | string;
|
|
377
|
+
type: "oauth2" | "apiKey" | "basic" | "bearer" | "custom" | "tenantAuth" | "jit" | "serviceAccount" | string;
|
|
276
378
|
displayName?: string;
|
|
277
379
|
description?: string;
|
|
278
380
|
connectionLabel?: string;
|
|
279
381
|
fields?: AuthField[];
|
|
382
|
+
/** Present when `type === "serviceAccount"`: what credential it accepts and how it binds. */
|
|
383
|
+
serviceAccount?: ServiceAccountConfig;
|
|
280
384
|
/** OAuth2 endpoints (only present when type === "oauth2"). */
|
|
281
385
|
oauth2?: Record<string, unknown>;
|
|
282
386
|
/**
|
|
@@ -314,6 +418,8 @@ export interface ConnectionSummary {
|
|
|
314
418
|
id: string;
|
|
315
419
|
appId: string;
|
|
316
420
|
authKey: string;
|
|
421
|
+
/** The stored credential this connection uses, when it has one. */
|
|
422
|
+
credentialId?: string | null;
|
|
317
423
|
/** The owning tenant. The server sends `tenant`/`subject`, never `owner`. */
|
|
318
424
|
tenant?: string;
|
|
319
425
|
/** The owning subject (the partner's user id). */
|
|
@@ -328,6 +434,14 @@ export interface ConnectionSummary {
|
|
|
328
434
|
profile?: Record<string, unknown>;
|
|
329
435
|
createdAt?: string;
|
|
330
436
|
updatedAt?: string;
|
|
437
|
+
/**
|
|
438
|
+
* True for the server's synthetic "no credential needed" rows. They have no stored credential,
|
|
439
|
+
* so the server 404s get/test/delete on their ids; the connection UI offers nothing on them.
|
|
440
|
+
* Detect with this flag only, never an id prefix.
|
|
441
|
+
*/
|
|
442
|
+
builtIn?: boolean;
|
|
443
|
+
/** The last test result: `false` = failed, `null`/absent = never tested. */
|
|
444
|
+
lastTestOk?: boolean | null;
|
|
331
445
|
}
|
|
332
446
|
/** A saved set of action-test input values, scoped to a connection + action. */
|
|
333
447
|
export interface SavedTest {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { DynamicContext } from './dynamic-fields.ts';
|
|
2
|
+
import { ActionParam, ParamOption } from './types.ts';
|
|
3
|
+
/** Per-field state the form renders from. */
|
|
4
|
+
export interface DynamicFieldState {
|
|
5
|
+
options?: ParamOption[];
|
|
6
|
+
loading: boolean;
|
|
7
|
+
error?: string;
|
|
8
|
+
/** Why the field is free text (no connection / unresolved refreshOn). */
|
|
9
|
+
hint?: string;
|
|
10
|
+
/** Flag for a kept value not in the returned list. */
|
|
11
|
+
notInList?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface UseDynamicFields {
|
|
14
|
+
fields: Record<string, DynamicFieldState>;
|
|
15
|
+
/** A pick committed `value` for `key` (select/Combobox/checkbox): one change call if any field refreshes on it. */
|
|
16
|
+
commit(key: string, value: unknown): void;
|
|
17
|
+
/** Focus entered `key`: remember the pre-edit form (the `previous` of a later blur commit). */
|
|
18
|
+
focus(key: string): void;
|
|
19
|
+
/** Focus left `key`: commit a text edit made since focus (never per keystroke). */
|
|
20
|
+
blur(key: string): void;
|
|
21
|
+
retry(key: string): void;
|
|
22
|
+
}
|
|
23
|
+
export declare function useDynamicFields(params: ActionParam[], values: Record<string, unknown>, dynamic: DynamicContext | undefined, onChange: (next: Record<string, unknown>) => void): UseDynamicFields;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@w6w/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "React components for w6w — central API management. Build plugins, apps and integrations on them.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://w6w.io",
|
|
@@ -73,7 +73,8 @@
|
|
|
73
73
|
"@uiw/react-codemirror": "^4.23.7",
|
|
74
74
|
"@w6w/expr": "^0.1.0",
|
|
75
75
|
"@xyflow/react": "^12.3.6",
|
|
76
|
-
"prism-react-renderer": "^2.4.1"
|
|
76
|
+
"prism-react-renderer": "^2.4.1",
|
|
77
|
+
"react-markdown": "^10.1.0"
|
|
77
78
|
},
|
|
78
79
|
"devDependencies": {
|
|
79
80
|
"@biomejs/biome": "^1.9.4",
|
|
@@ -98,8 +99,9 @@
|
|
|
98
99
|
"build:css": "node scripts/build-css.mjs",
|
|
99
100
|
"check:css": "node scripts/build-css.mjs --check",
|
|
100
101
|
"typecheck": "tsc -b --noEmit",
|
|
101
|
-
"test": "node --conditions=w6w-source --import ./src/test-jsx-loader.mjs --test src/__tests__/*.test.ts src/components/__tests__/*.test.ts",
|
|
102
|
+
"test": "node --max-old-space-size=2048 --conditions=w6w-source --import ./src/test-jsx-loader.mjs --test --test-concurrency=4 --test-timeout=120000 src/__tests__/*.test.ts src/components/__tests__/*.test.ts",
|
|
102
103
|
"test:picker-layout": "bash test/picker-layout/run.sh",
|
|
104
|
+
"test:canvas-layer": "bash test/canvas-layer/run.sh",
|
|
103
105
|
"test:expr-template": "bash test/expr-template/run.sh",
|
|
104
106
|
"test:action-test-form": "bash test/action-test-form/run.sh",
|
|
105
107
|
"test:host-isolation": "bash test/host-isolation/run.sh",
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/* BudgetMeter — usage-vs-limit bar with a reserved segment.
|
|
2
|
+
*
|
|
3
|
+
* Token-only: every colour is a --w6w-* custom property, so a host restyles
|
|
4
|
+
* the meter (including the over-limit state) by overriding tokens, never by
|
|
5
|
+
* targeting a hex value here. */
|
|
6
|
+
.w6w-budget-meter {
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
gap: var(--w6w-sp-1);
|
|
10
|
+
}
|
|
11
|
+
.w6w-budget-meter-head {
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: baseline;
|
|
14
|
+
justify-content: space-between;
|
|
15
|
+
gap: var(--w6w-sp-2);
|
|
16
|
+
color: var(--w6w-muted);
|
|
17
|
+
}
|
|
18
|
+
.w6w-budget-meter-label {
|
|
19
|
+
color: var(--w6w-text);
|
|
20
|
+
}
|
|
21
|
+
.w6w-budget-meter-value {
|
|
22
|
+
font-variant-numeric: tabular-nums;
|
|
23
|
+
}
|
|
24
|
+
.w6w-budget-meter-track {
|
|
25
|
+
display: flex;
|
|
26
|
+
width: 100%;
|
|
27
|
+
height: 8px;
|
|
28
|
+
overflow: hidden;
|
|
29
|
+
background: var(--w6w-panel-2);
|
|
30
|
+
border: 1px solid var(--w6w-border);
|
|
31
|
+
border-radius: var(--w6w-radius);
|
|
32
|
+
}
|
|
33
|
+
.w6w-budget-meter-used {
|
|
34
|
+
height: 100%;
|
|
35
|
+
background: var(--w6w-accent);
|
|
36
|
+
}
|
|
37
|
+
.w6w-budget-meter-reserved {
|
|
38
|
+
height: 100%;
|
|
39
|
+
background: var(--w6w-health-degraded);
|
|
40
|
+
}
|
|
41
|
+
.w6w-budget-meter-over .w6w-budget-meter-used {
|
|
42
|
+
background: var(--w6w-danger);
|
|
43
|
+
}
|
|
44
|
+
.w6w-budget-meter-over .w6w-budget-meter-value {
|
|
45
|
+
color: var(--w6w-danger);
|
|
46
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
.w6w-callable-list {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--w6w-sp-4);
|
|
5
|
+
}
|
|
6
|
+
.w6w-callable-list-head {
|
|
7
|
+
display: flex;
|
|
8
|
+
justify-content: space-between;
|
|
9
|
+
align-items: flex-start;
|
|
10
|
+
gap: var(--w6w-sp-4);
|
|
11
|
+
}
|
|
12
|
+
.w6w-callable-list-heading {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
gap: var(--w6w-sp-1);
|
|
16
|
+
min-width: 0;
|
|
17
|
+
}
|
|
18
|
+
.w6w-callable-list-title {
|
|
19
|
+
margin: 0;
|
|
20
|
+
}
|
|
21
|
+
.w6w-callable-list-description {
|
|
22
|
+
margin: 0;
|
|
23
|
+
}
|
|
24
|
+
.w6w-callable-list-toggle {
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
gap: var(--w6w-sp-2);
|
|
28
|
+
cursor: pointer;
|
|
29
|
+
}
|
|
30
|
+
.w6w-callable-list-actions {
|
|
31
|
+
display: flex;
|
|
32
|
+
gap: var(--w6w-sp-2);
|
|
33
|
+
flex-shrink: 0;
|
|
34
|
+
white-space: nowrap;
|
|
35
|
+
}
|
|
36
|
+
.w6w-callable-list-body {
|
|
37
|
+
border: 1px solid var(--w6w-border);
|
|
38
|
+
border-radius: 8px;
|
|
39
|
+
background: var(--w6w-panel);
|
|
40
|
+
overflow: hidden;
|
|
41
|
+
}
|
|
42
|
+
.w6w-callable-list-main {
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
gap: var(--w6w-sp-0-5);
|
|
46
|
+
min-width: 0;
|
|
47
|
+
text-align: left;
|
|
48
|
+
}
|
|
49
|
+
.w6w-callable-list-open {
|
|
50
|
+
all: unset;
|
|
51
|
+
cursor: pointer;
|
|
52
|
+
display: block;
|
|
53
|
+
width: 100%;
|
|
54
|
+
}
|
|
55
|
+
.w6w-callable-list-trail {
|
|
56
|
+
display: inline-flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
gap: var(--w6w-sp-2);
|
|
59
|
+
}
|
|
60
|
+
.w6w-callable-list-badge {
|
|
61
|
+
padding: var(--w6w-sp-0-5) var(--w6w-sp-2);
|
|
62
|
+
border-radius: 999px;
|
|
63
|
+
border: 1px solid var(--w6w-border);
|
|
64
|
+
color: var(--w6w-muted);
|
|
65
|
+
font-size: var(--w6w-fs-xs);
|
|
66
|
+
}
|
|
67
|
+
.w6w-callable-list-row-desc {
|
|
68
|
+
display: block;
|
|
69
|
+
}
|