api_binding_ui 0.0.0-stage → 1.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/README.md CHANGED
@@ -1,3 +1,394 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # API Binding Studio (api_binding_ui)
2
+
3
+ API Binding Studio (`api_binding_ui`) is a browser-side visual API mapper with generic offline request validation. Import OpenAPI contracts, propose editable component mappings, inspect request parts and validate their schemas without calling a server. Validation is not an end-to-end API test or proof of authorization/server behavior.
4
+
5
+ Version 1.1.0 adds `buildCatalogFromOpenApi(parsedDocument)`, operation descriptions/tags, location-aware parameters, JSON object conversion, headers, `validateRequest`, `validateBindings` and context-sensitive `verificationSignature`. The workspace uses the generic `ValidationDialog`; legacy note simulation exports remain available separately. See [SKILL.md](./SKILL.md) for the complete host integration and seed examples.
6
+
7
+ ---
8
+
9
+ ## Architecture Overview
10
+
11
+ The system is engineered as an independent package that mounts into any React application while operating its management workspace in a dedicated browser window.
12
+
13
+ ### 1. Canonical Single-Configuration Model (`src/core/model.ts`)
14
+ The studio is driven by a single canonical configuration (`ApiBindingConfig`) containing:
15
+ - Ordered API calls (`calls: BindingCall[]`) covering both data retrieval (`read`) and action execution (`write`).
16
+ - Component declarations (`fields: BindingField[]`) reflecting all interactive form controls on the screen.
17
+ - Execution parameters, customer iteration rules (`customerRule: "each" | "single"`), post-execution behaviors (`success: "close" | "keep"`), and template population policies.
18
+
19
+ All workspace views, data grids, request payload previews, and simulation runners derive from this single state structure. When API contracts change or are reselected, the reconciliation engine automatically preserves matching parameter assignments while reporting removed or newly required parameters.
20
+
21
+ ### 2. Universal Screen Context and DOM Discovery (`src/core/bindingContext.ts`)
22
+ The `<BindApiButton />` component is screen-agnostic and can be mounted within:
23
+ - Modal dialogs (`[role="dialog"]`, `<dialog>`, `.ds-dialog`)
24
+ - Slide-out drawers and flyout panels (`[role="region"]`, `[data-drawer]`, `<aside>`)
25
+ - Dedicated forms (`<form>`)
26
+ - Full-page views and standalone screens
27
+
28
+ The context extractor progressively inspects the DOM hierarchy using explicit references (`containerRef`, `containerSelector`, `rootElement`, `formId`) or heuristic boundary detection. It automatically discovers form inputs, selects, textareas, custom ARIA widgets (`role="combobox"`, `role="textbox"`, `role="checkbox"`, `role="switch"`), and action buttons.
29
+
30
+ ### 3. Multi-Channel Cross-Window Transport (`src/core/transport.ts`)
31
+ The studio operates in a dedicated browser window identified by a stable key (`api-binding:<key>`). Re-clicking the trigger button re-focuses the existing tab, preserving in-progress unsaved configurations. Communication between the host application and the studio window utilizes three redundant layers:
32
+ 1. Bidirectional `window.postMessage` handshake (`api-binding-ready` and `api-binding-context`).
33
+ 2. `BroadcastChannel("api-binding-studio")` for real-time grid selection updates and cross-tab synchronization.
34
+ 3. Structured `localStorage` persistence keys for context, draft configurations, and applied bindings across page refreshes.
35
+
36
+ ### 4. Design System CSS Custom Property Inheritance (`src/styles/highradius-bridge.css`)
37
+ The studio UI relies entirely on design token custom properties:
38
+ - Background and surfaces: `--ds-color-background`, `--ds-color-surface`, `--ds-color-surface-subtle`
39
+ - Text and typography: `--ds-color-foreground`, `--ds-color-foreground-muted`, `--ds-font-family-sans`, `--ds-font-family-mono`
40
+ - Borders and focus: `--ds-color-border`, `--ds-color-focus`
41
+ - Accents and feedback: `--ds-color-primary`, `--ds-color-destructive`, `--ds-color-warning`, `--ds-color-success`
42
+ - Layout geometry: `--ds-space-*`, `--ds-radius-*`
43
+
44
+ When rendered within an application that declares these variables, the studio immediately inherits the host styling. Standalone fallback values are bundled to ensure rendering fidelity when loaded outside the parent shell.
45
+
46
+ ### 5. Zero-Network Request Validation (`src/components/ValidationDialog.tsx`)
47
+ Before any configuration can be marked as verified or applied:
48
+ - The engine constructs actual HTTP request payloads (headers, query parameters, path variables, request bodies).
49
+ - Dynamic value coercion handles arrays (`number[]`, `string[]`), booleans, nested object dot-paths (`customer.billing.id`), and static text overrides.
50
+ - Ajv validates supported JSON Schema request constraints for all configured read/write calls using captured or user-supplied sample inputs.
51
+ - Validation signatures cover configuration, contracts and captured context. They are consistency checks, not cryptographic security or server verification. No fake saved-record retrieval is required.
52
+ - Multiple selected rows can supply array values; scalar row parameters require one row. There is no live request executor or automatic per-row dispatch.
53
+
54
+ ---
55
+
56
+ ## Installation
57
+
58
+ After the 1.1.0 release has been published, install it from npm:
59
+
60
+ ```bash
61
+ npm install api_binding_ui@1.1.0
62
+ # or
63
+ yarn add api_binding_ui
64
+ # or
65
+ bun add api_binding_ui
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Host Application Setup
71
+
72
+ ### Step 1: Register the Studio Route
73
+
74
+ The studio window opens at the path `/api-binding/$key`. Configure your application router to mount the `ApiBindingWorkspace` component at this path.
75
+
76
+ #### TanStack Router (`src/routes/api-binding.$key.tsx`)
77
+ ```tsx
78
+ import { createFileRoute } from "@tanstack/react-router";
79
+ import { ApiBindingWorkspace } from "api_binding_ui";
80
+ import "api_binding_ui/style.css";
81
+
82
+ export const Route = createFileRoute("/api-binding/$key")({
83
+ head: () => ({
84
+ meta: [
85
+ { title: "API Binding Studio" },
86
+ { name: "description", content: "Visual API mapping and simulation studio." },
87
+ ],
88
+ }),
89
+ component: ApiBindingRouteView,
90
+ });
91
+
92
+ function ApiBindingRouteView() {
93
+ const { key } = Route.useParams();
94
+ return <ApiBindingWorkspace key={key} pageKey={key} />;
95
+ }
96
+ ```
97
+
98
+ #### React Router (`src/App.tsx`)
99
+ ```tsx
100
+ import { BrowserRouter, Routes, Route, useParams } from "react-router-dom";
101
+ import { ApiBindingWorkspace } from "api_binding_ui";
102
+ import "api_binding_ui/style.css";
103
+
104
+ function ApiBindingPage() {
105
+ const { key = "default" } = useParams();
106
+ return <ApiBindingWorkspace key={key} pageKey={key} />;
107
+ }
108
+
109
+ export function App() {
110
+ return (
111
+ <BrowserRouter>
112
+ <Routes>
113
+ <Route path="/api-binding/:key" element={<ApiBindingPage />} />
114
+ {/* Application routes */}
115
+ </Routes>
116
+ </BrowserRouter>
117
+ );
118
+ }
119
+ ```
120
+
121
+ ---
122
+
123
+ ### Step 2: Mount `<BindApiButton />` on Host Screens
124
+
125
+ Place `<BindApiButton />` inside your modal dialogs, slide-out drawers, form panels, or screen toolbars.
126
+
127
+ Supply a complete `ApiDefinition[]` catalog and a per-window `MappingFile` seed. For an OpenAPI file attached to an AI coding task, generate the static catalog from the attachment; the app does not need to store or parse YAML at runtime. If the host already has a parsed OpenAPI 3.0/3.1 object, it may use `buildCatalogFromOpenApi(parsedOpenApi)`. Neither catalog discovery nor semantic mapping inference happens automatically at runtime. Have the host AI generate high-confidence seeds using OpenAPI names, descriptions, tags and actual components; leave ambiguities editable. A `hook` string identifies a catalog operation; it does not create an executable hook.
128
+
129
+ #### Example A: Dialog with Data Grid Context
130
+ ```tsx
131
+ import * as React from "react";
132
+ import { BindApiButton, type ApiDefinition, type MappingFile } from "api_binding_ui";
133
+
134
+ interface CustomerRecord {
135
+ customerId: number;
136
+ customerName: string;
137
+ accountNumber: string;
138
+ }
139
+
140
+ export function AddNotesModal({
141
+ open,
142
+ onClose,
143
+ selectedCustomers,
144
+ definitions,
145
+ mappingFile,
146
+ }: {
147
+ open: boolean;
148
+ onClose: () => void;
149
+ selectedCustomers: CustomerRecord[];
150
+ definitions: ApiDefinition[];
151
+ mappingFile: MappingFile;
152
+ }) {
153
+ if (!open) return null;
154
+
155
+ const noteTypeOptions = [
156
+ { value: "1", label: "General Follow-up" },
157
+ { value: "2", label: "Promise to Pay" },
158
+ ];
159
+
160
+ const templateOptions = [
161
+ {
162
+ value: "tmpl-payment",
163
+ label: "Payment Reminder",
164
+ subject: "Pending statement follow-up",
165
+ description: "Contacted accounts payable regarding past-due invoices.",
166
+ },
167
+ ];
168
+
169
+ return (
170
+ <div role="dialog" aria-labelledby="modal-heading" className="modal-container">
171
+ <h2 id="modal-heading">Add Customer Note</h2>
172
+
173
+ <div className="form-group">
174
+ <label htmlFor="notesType">Note Type</label>
175
+ <select id="notesType" name="notesType" required>
176
+ {noteTypeOptions.map((opt) => (
177
+ <option key={opt.value} value={opt.value}>
178
+ {opt.label}
179
+ </option>
180
+ ))}
181
+ </select>
182
+ </div>
183
+
184
+ <div className="form-group">
185
+ <label htmlFor="subject">Subject</label>
186
+ <input id="subject" name="subject" required />
187
+ </div>
188
+
189
+ <div className="form-group">
190
+ <label htmlFor="notes">Description</label>
191
+ <textarea id="notes" name="notes" required />
192
+ </div>
193
+
194
+ <div className="modal-actions">
195
+ <button type="button" onClick={onClose}>
196
+ Cancel
197
+ </button>
198
+ <button type="submit">
199
+ Save Note
200
+ </button>
201
+
202
+ <BindApiButton
203
+ definitions={definitions}
204
+ mappingFile={mappingFile}
205
+ appId="collections"
206
+ windowId="worklist-add-notes"
207
+ source="Add Notes Dialog"
208
+ parent="Worklist Grid"
209
+ grid={{
210
+ gridId: "worklistUIGrid",
211
+ rows: selectedCustomers,
212
+ }}
213
+ options={{
214
+ notesType: noteTypeOptions,
215
+ template: templateOptions,
216
+ }}
217
+ />
218
+ </div>
219
+ </div>
220
+ );
221
+ }
222
+ ```
223
+
224
+ #### Example B: Standalone Screen or Drawer without a Grid
225
+ ```tsx
226
+ import * as React from "react";
227
+ import { BindApiButton, type ApiDefinition, type MappingFile } from "api_binding_ui";
228
+
229
+ export function CreateDisputeDrawer({ isOpen, onClose, definitions, mappingFile }: { isOpen: boolean; onClose: () => void; definitions: ApiDefinition[]; mappingFile: MappingFile }) {
230
+ if (!isOpen) return null;
231
+
232
+ const reasonCodes = [
233
+ { value: "PRICING_ERROR", label: "Pricing Discrepancy" },
234
+ { value: "DAMAGED_GOODS", label: "Damaged Goods" },
235
+ ];
236
+
237
+ return (
238
+ <aside className="drawer-panel" aria-label="Create Dispute">
239
+ <h2>Create Dispute</h2>
240
+
241
+ <form id="dispute-form">
242
+ <label htmlFor="reasonCode">Reason Code</label>
243
+ <select id="reasonCode" name="reasonCode" required>
244
+ {reasonCodes.map((r) => (
245
+ <option key={r.value} value={r.value}>{r.label}</option>
246
+ ))}
247
+ </select>
248
+
249
+ <label htmlFor="amount">Disputed Amount</label>
250
+ <input id="amount" name="amount" type="number" required />
251
+
252
+ <label htmlFor="comments">Comments</label>
253
+ <textarea id="comments" name="comments" />
254
+
255
+ <div className="drawer-footer">
256
+ <button type="button" onClick={onClose}>Cancel</button>
257
+ <button type="submit">Submit Dispute</button>
258
+
259
+ <BindApiButton
260
+ definitions={definitions}
261
+ mappingFile={mappingFile}
262
+ appId="receivables"
263
+ windowId="dispute-create"
264
+ source="Create Dispute"
265
+ parent="Disputes Management"
266
+ options={{
267
+ reasonCode: reasonCodes,
268
+ }}
269
+ />
270
+ </div>
271
+ </form>
272
+ </aside>
273
+ );
274
+ }
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Component API Specification
280
+
281
+ ### `<BindApiButton />` Props
282
+
283
+ | Prop | Type | Default | Description |
284
+ | :--- | :--- | :--- | :--- |
285
+ | `appId` | `string` | `"app"` | Identifier of the parent application module. |
286
+ | `windowId` | `string` | *(Required)* | Stable identifier of the screen, dialog, or form. |
287
+ | `source` | `string` | *(Required)* | Human-readable title of the current screen or action. |
288
+ | `parent` | `string` | *(Required)* | Human-readable title of the parent view or grid. |
289
+ | `grid` | `BindingGrid` | `undefined` | Grid context including `gridId`, `columns`, and `rows` when actions target selected records. |
290
+ | `parents` | `SnapshotContext[]` | `undefined` | Array of parent context hierarchies for multi-level navigation. |
291
+ | `kind` | `WindowKind` | Automatic | `"dialog" \| "drawer" \| "screen" \| "action-form"`. Detected automatically if omitted. |
292
+ | `trigger` | `WindowTrigger` | `undefined` | Invocation metadata: `button`, `needsSelection`, `gridId`. |
293
+ | `options` | `Record<string, BindingOption[]>` | `{}` | Lookup options for select and combobox fields. Supports `{ value, label, subject, description }`. |
294
+ | `definitions` | `ApiDefinition[]` | `[]` | Registered API endpoints catalog available for mapping. |
295
+ | `mappingFile` | `MappingFile \| null` | `null` | Pre-existing mapping file to preload. |
296
+ | `routeBase` | `string` | `"/api-binding"` | Studio route base path configured in the application router. |
297
+ | `containerRef` | `React.RefObject<HTMLElement>` | `undefined` | Explicit DOM reference to the container element. |
298
+ | `containerSelector` | `string` | `undefined` | CSS selector used to locate the enclosing container element. |
299
+ | `rootElement` | `HTMLElement \| null` | `undefined` | Direct DOM element override. |
300
+ | `formId` | `string` | `undefined` | Target HTML `<form id="...">` identifier. |
301
+ | `components` | `SnapshotComponent[]` | `undefined` | Explicit components list overriding automatic DOM discovery. |
302
+ | `buttons` | `string[]` | `undefined` | Explicit action buttons list overriding automatic DOM discovery. |
303
+ | `className` | `string` | `""` | Additional CSS class for the button. |
304
+ | `children` | `React.ReactNode` | `"Bind API"` | Button label or children nodes. |
305
+
306
+ ---
307
+
308
+ ## DOM Discovery Protocol
309
+
310
+ When `<BindApiButton />` is clicked, the context extraction pipeline executes the following sequence:
311
+
312
+ 1. **Container Resolution**:
313
+ - Checks `rootElement`, `containerRef`, `containerSelector`, and `formId`.
314
+ - Searches upward for `role="dialog"`, `<dialog>`, or `.ds-dialog`.
315
+ - Searches upward for `<form>`.
316
+ - Searches upward for `role="region"`, `[data-drawer]`, `<aside>`, `<main>`, `<section>`, `.drawer`, or `.panel`.
317
+ - Traverses parent elements to locate any ancestor containing at least two input elements.
318
+ - Falls back to `document.body`.
319
+
320
+ 2. **Form Control Discovery**:
321
+ - Evaluates all standard controls (`input`, `select`, `textarea`) and custom ARIA controls (`role="combobox"`, `role="textbox"`, `role="checkbox"`, `role="switch"`).
322
+ - Resolves control names from `name`, `id`, `data-testid`, `data-field`, or `aria-label`.
323
+ - Extracts labels from `<label for="...">`, parent `<label>`, `aria-labelledby`, `aria-label`, or field placeholders.
324
+ - Captures current input values and state (`checked`, `value`, `data-value`).
325
+
326
+ 3. **Action Button Discovery**:
327
+ - Evaluates buttons and submit inputs within the container.
328
+ - Excludes modal close controls, minimize/maximize adornments, dismiss triggers, and API binding triggers.
329
+ - Captures active primary and secondary action labels (e.g., `"Save"`, `"Submit"`, `"Cancel"`, `"Reset"`).
330
+
331
+ ---
332
+
333
+ ## Consuming Applied Bindings
334
+
335
+ When an engineer or analyst clicks **Apply Binding** inside the Studio, the verified configuration is stored in `localStorage` under `api-binding:applied:<key>` and broadcast across active windows.
336
+
337
+ Host components can listen to applied bindings using the `useAppliedBinding` hook:
338
+
339
+ ```tsx
340
+ import * as React from "react";
341
+ import { useAppliedBinding } from "api_binding_ui";
342
+
343
+ export function AddNotesModal() {
344
+ const { isApplied, appliedConfig, appliedAt } = useAppliedBinding(
345
+ "collections.worklistUIGrid.worklist-add-notes"
346
+ );
347
+
348
+ React.useEffect(() => {
349
+ if (isApplied && appliedConfig) {
350
+ console.log("Applied API Binding Configuration:", appliedConfig);
351
+ // Access appliedConfig.calls, appliedConfig.fields, appliedConfig.customerRule
352
+ }
353
+ }, [isApplied, appliedConfig]);
354
+
355
+ // Render modal
356
+ }
357
+ ```
358
+
359
+ ---
360
+
361
+ ## Testing and Verification
362
+
363
+ `useAppliedBinding` observes stored configuration only; the host retains responsibility for any live execution, response-to-field population and durable persistence. Draft edits take precedence over new seeds. Captured context lives in same-origin browser storage; exclude sensitive fields and keep credentials out of snapshots and fixed values. External schema references must be bundled before import. Cookie parameters, non-JSON bodies, non-object root bodies and unsupported wire serialization produce contract limitations rather than network fallbacks.
364
+
365
+ Run test suites using the bundled test runner:
366
+
367
+ ```bash
368
+ bun test
369
+ # or
370
+ yarn test
371
+ ```
372
+
373
+ Build the distribution package:
374
+
375
+ ```bash
376
+ bun run build
377
+ # or
378
+ yarn build
379
+ ```
380
+
381
+ The build produces:
382
+ - `dist/index.js`: Browser ES module bundle.
383
+ - `dist/api-binding-ui.css`: Compiled style bundle with `--ds-*` design token bridges.
384
+ - `dist/**/*.d.ts`: TypeScript declaration definitions.
385
+
386
+ ## Manual Release
387
+
388
+ The original Bun/PowerShell build scripts are unchanged. Run `bun test`, `bun run build`, then `npm pack --dry-run` and inspect the archive/exports. Publish version 1.1.0 yourself using `npm publish` after reviewing the diff. This project has not been published automatically. The Lovable handoff prompt is a separate local document, not part of the npm package's files list.
389
+
390
+ ---
391
+
392
+ ## License
393
+
394
+ Internal enterprise package. All rights reserved.