@testsmith/api-spector 0.5.0 → 0.5.2

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.
@@ -0,0 +1,255 @@
1
+ import { r as reactExports, j as jsxRuntimeExports, R as ReactCodeMirror, k as commentKeymap, o as oneDark, x as xml } from "./index-C7kyUDAy.js";
2
+ const SOAP_11_CONTENT_TYPE = "text/xml; charset=utf-8";
3
+ const SOAP_12_CONTENT_TYPE = "application/soap+xml; charset=utf-8";
4
+ function contentTypeForSoap(version) {
5
+ return version === "1.2" ? SOAP_12_CONTENT_TYPE : SOAP_11_CONTENT_TYPE;
6
+ }
7
+ function withContentType(headers, value) {
8
+ const idx = headers.findIndex((h) => h.key.toLowerCase() === "content-type");
9
+ const next = { key: "Content-Type", value, enabled: true };
10
+ if (idx === -1) return [...headers, next];
11
+ return headers.map((h, i) => i === idx ? { ...h, value, enabled: true } : h);
12
+ }
13
+ const { electron } = window;
14
+ function ParamTree({ params, depth = 0 }) {
15
+ if (params.length === 0) {
16
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-600 italic", children: "No parameters declared in WSDL." });
17
+ }
18
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: depth === 0 ? "flex flex-col gap-0.5" : "flex flex-col gap-0.5 ml-4 border-l border-surface-800 pl-3 mt-1", children: params.map((p, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: "text-[11px] font-mono", children: [
19
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-200", children: p.name }),
20
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-600", children: ": " }),
21
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-blue-400", children: p.typeHint }),
22
+ p.children && p.children.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(ParamTree, { params: p.children, depth: depth + 1 })
23
+ ] }, `${p.name}-${i}`)) });
24
+ }
25
+ function SoapEditor({ request, onChange }) {
26
+ const soap = request.body.soap ?? { wsdlUrl: "", envelope: "" };
27
+ const [operations, setOperations] = reactExports.useState([]);
28
+ const [endpoints, setEndpoints] = reactExports.useState([]);
29
+ const [targetNs, setTargetNs] = reactExports.useState("");
30
+ const [fetchError, setFetchError] = reactExports.useState(null);
31
+ const [fetching, setFetching] = reactExports.useState(false);
32
+ const [showXml, setShowXml] = reactExports.useState(true);
33
+ reactExports.useEffect(() => {
34
+ const url = soap.wsdlUrl?.trim();
35
+ if (!url) return;
36
+ let cancelled = false;
37
+ (async () => {
38
+ try {
39
+ const result = await electron.wsdlFetch(url);
40
+ if (cancelled) return;
41
+ setOperations(result.operations);
42
+ setEndpoints(result.endpoints);
43
+ setTargetNs(result.targetNamespace);
44
+ } catch {
45
+ }
46
+ })();
47
+ return () => {
48
+ cancelled = true;
49
+ };
50
+ }, [request.id]);
51
+ function updateSoap(patch) {
52
+ onChange({ body: { ...request.body, soap: { ...soap, ...patch } } });
53
+ }
54
+ function applyOperation(op) {
55
+ const headers = withContentType(request.headers ?? [], contentTypeForSoap(op.soapVersion));
56
+ onChange({
57
+ method: "POST",
58
+ headers,
59
+ ...op.endpoint ? { url: op.endpoint } : {},
60
+ body: {
61
+ ...request.body,
62
+ soap: {
63
+ ...soap,
64
+ operationName: op.name,
65
+ soapVersion: op.soapVersion,
66
+ binding: op.binding,
67
+ soapAction: op.soapAction ?? "",
68
+ envelope: op.inputTemplate
69
+ }
70
+ }
71
+ });
72
+ }
73
+ async function fetchWsdl() {
74
+ if (!soap.wsdlUrl.trim()) return;
75
+ setFetching(true);
76
+ setFetchError(null);
77
+ try {
78
+ const result = await electron.wsdlFetch(soap.wsdlUrl.trim());
79
+ setOperations(result.operations);
80
+ setEndpoints(result.endpoints);
81
+ setTargetNs(result.targetNamespace);
82
+ if (result.operations.length > 0) {
83
+ applyOperation(result.operations[0]);
84
+ }
85
+ } catch (err) {
86
+ setFetchError(err instanceof Error ? err.message : String(err));
87
+ } finally {
88
+ setFetching(false);
89
+ }
90
+ }
91
+ function selectOperation(op) {
92
+ applyOperation(op);
93
+ }
94
+ const selected = operations.find((o) => o.name === soap.operationName && (soap.soapVersion === void 0 || o.soapVersion === soap.soapVersion) && (soap.binding === void 0 || (o.binding ?? "") === (soap.binding ?? ""))) ?? operations.find((o) => o.name === soap.operationName) ?? operations[0];
95
+ const primaryEndpoint = endpoints[0]?.address;
96
+ const versions = Array.from(new Set(operations.map((o) => o.soapVersion))).sort();
97
+ const hasSavedSoap = Boolean(soap.envelope?.trim() || soap.operationName || soap.wsdlUrl?.trim());
98
+ const showFullUi = operations.length > 0;
99
+ const showFallback = !showFullUi && hasSavedSoap;
100
+ const showEmpty = !showFullUi && !showFallback;
101
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3 h-full min-h-0", children: [
102
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 items-center", children: [
103
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] uppercase tracking-wider text-surface-500 font-medium whitespace-nowrap", children: "WSDL" }),
104
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
105
+ "input",
106
+ {
107
+ value: soap.wsdlUrl,
108
+ onChange: (e) => updateSoap({ wsdlUrl: e.target.value }),
109
+ placeholder: "https://example.com/service?WSDL",
110
+ onKeyDown: (e) => {
111
+ if (e.key === "Enter" && !fetching) fetchWsdl();
112
+ },
113
+ className: "flex-1 text-xs bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500 placeholder-surface-600 font-mono"
114
+ }
115
+ ),
116
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
117
+ "button",
118
+ {
119
+ onClick: fetchWsdl,
120
+ disabled: fetching || !soap.wsdlUrl.trim(),
121
+ className: "px-3 py-1.5 text-xs bg-blue-700 hover:bg-blue-600 disabled:bg-surface-800 disabled:text-surface-600 rounded transition-colors whitespace-nowrap",
122
+ children: fetching ? "Fetching…" : operations.length > 0 ? "Refresh" : "Fetch WSDL"
123
+ }
124
+ )
125
+ ] }),
126
+ fetchError && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-red-400 bg-red-950/30 border border-red-900 rounded px-2.5 py-1.5", children: fetchError }),
127
+ operations.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-surface-900/40 border border-surface-800 rounded-md px-3 py-2 text-[11px] flex flex-col gap-1", children: [
128
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-surface-500", children: [
129
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "uppercase tracking-wider text-[9px] font-semibold text-surface-600 w-20 shrink-0", children: "Endpoint" }),
130
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "text-surface-200 font-mono truncate", children: primaryEndpoint ?? "(not declared)" })
131
+ ] }),
132
+ targetNs && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-surface-500", children: [
133
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "uppercase tracking-wider text-[9px] font-semibold text-surface-600 w-20 shrink-0", children: "Namespace" }),
134
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "text-surface-200 font-mono truncate", children: targetNs })
135
+ ] }),
136
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-surface-500", children: [
137
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "uppercase tracking-wider text-[9px] font-semibold text-surface-600 w-20 shrink-0", children: "Operations" }),
138
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-surface-300", children: [
139
+ operations.length,
140
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-surface-600", children: [
141
+ " · SOAP ",
142
+ versions.join(", ")
143
+ ] })
144
+ ] })
145
+ ] })
146
+ ] }),
147
+ showFullUi && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-3 min-h-0 flex-1", children: [
148
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-44 shrink-0 flex flex-col bg-surface-900/40 border border-surface-800 rounded-md overflow-hidden", children: [
149
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2.5 py-1.5 text-[9px] uppercase tracking-wider font-semibold text-surface-600 border-b border-surface-800", children: "Operations" }),
150
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "overflow-y-auto flex-1", children: operations.map((op) => {
151
+ const active = selected && op.name === selected.name && op.soapVersion === selected.soapVersion && (op.binding ?? "") === (selected.binding ?? "");
152
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
153
+ "button",
154
+ {
155
+ onClick: () => selectOperation(op),
156
+ className: `w-full text-left px-2.5 py-1.5 text-[11px] border-b border-surface-800/50 transition-colors ${active ? "bg-blue-900/30 text-blue-300" : "text-surface-300 hover:bg-surface-800/60"}`,
157
+ children: [
158
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-mono truncate", children: op.name }),
159
+ op.soapVersion === "1.2" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[9px] text-surface-600 uppercase", children: "SOAP 1.2" })
160
+ ]
161
+ },
162
+ `${op.binding ?? ""}:${op.name}:${op.soapVersion}`
163
+ );
164
+ }) })
165
+ ] }),
166
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-w-0 min-h-0 overflow-y-auto flex flex-col gap-2 pr-1", children: selected && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
167
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-baseline gap-2", children: [
168
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold text-white font-mono", children: selected.name }),
169
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] uppercase tracking-wider text-surface-500", children: [
170
+ "SOAP ",
171
+ selected.soapVersion
172
+ ] })
173
+ ] }),
174
+ selected.soapAction && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[11px] text-surface-500 font-mono truncate", children: [
175
+ "SOAPAction: ",
176
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-300", children: selected.soapAction })
177
+ ] }),
178
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-surface-900/40 border border-surface-800 rounded-md px-3 py-2", children: [
179
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[9px] uppercase tracking-wider font-semibold text-surface-600 mb-1.5", children: "Inputs" }),
180
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ParamTree, { params: selected.params ?? [] })
181
+ ] }),
182
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
183
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] uppercase tracking-wider font-semibold text-surface-600", children: "Envelope" }),
184
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
185
+ "button",
186
+ {
187
+ onClick: () => setShowXml((v) => !v),
188
+ className: "text-[10px] text-surface-500 hover:text-surface-300 transition-colors",
189
+ children: showXml ? "▾ Hide XML" : "▸ Edit XML"
190
+ }
191
+ )
192
+ ] }),
193
+ !showXml && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[10px] text-surface-600 leading-relaxed", children: [
194
+ "Envelope auto-generated from the operation's input parameters. Method (POST), endpoint URL, and Content-Type are managed for you. Click ",
195
+ /* @__PURE__ */ jsxRuntimeExports.jsx("em", { children: "Edit XML" }),
196
+ " to tweak."
197
+ ] }),
198
+ showXml && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded overflow-hidden border border-surface-700 h-72 shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
199
+ ReactCodeMirror,
200
+ {
201
+ value: soap.envelope ?? "",
202
+ height: "100%",
203
+ theme: oneDark,
204
+ extensions: [xml(), commentKeymap],
205
+ onChange: (val) => updateSoap({ envelope: val }),
206
+ basicSetup: { lineNumbers: true, foldGutter: true }
207
+ }
208
+ ) })
209
+ ] }) })
210
+ ] }),
211
+ showFallback && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2 flex-1 min-h-0", children: [
212
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-surface-900/40 border border-surface-800 rounded-md px-3 py-2 text-[11px] flex items-center gap-3", children: [
213
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
214
+ soap.operationName && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-mono text-surface-200 truncate", children: soap.operationName }),
215
+ soap.soapAction && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-surface-500 truncate", children: [
216
+ "SOAPAction: ",
217
+ soap.soapAction
218
+ ] }),
219
+ !soap.operationName && !soap.soapAction && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-surface-500", children: soap.wsdlUrl?.trim() ? "WSDL not loaded - the saved envelope below is still sent on Send." : "No WSDL - hand-crafted SOAP envelope." })
220
+ ] }),
221
+ soap.wsdlUrl?.trim() && /* @__PURE__ */ jsxRuntimeExports.jsx(
222
+ "button",
223
+ {
224
+ onClick: fetchWsdl,
225
+ disabled: fetching,
226
+ className: "px-2.5 py-1 text-[11px] bg-surface-800 hover:bg-surface-700 disabled:opacity-50 rounded transition-colors whitespace-nowrap",
227
+ children: fetching ? "Loading…" : "Reload WSDL"
228
+ }
229
+ )
230
+ ] }),
231
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded overflow-hidden border border-surface-700 flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
232
+ ReactCodeMirror,
233
+ {
234
+ value: soap.envelope ?? "",
235
+ height: "100%",
236
+ theme: oneDark,
237
+ extensions: [xml(), commentKeymap],
238
+ onChange: (val) => updateSoap({ envelope: val }),
239
+ basicSetup: { lineNumbers: true, foldGutter: true }
240
+ }
241
+ ) })
242
+ ] }),
243
+ showEmpty && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col items-center justify-center text-center gap-2 border border-dashed border-surface-800 rounded-md py-8 px-4", children: [
244
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-surface-400", children: [
245
+ "Paste a WSDL URL above and click ",
246
+ /* @__PURE__ */ jsxRuntimeExports.jsx("em", { children: "Fetch WSDL" }),
247
+ "."
248
+ ] }),
249
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-600 max-w-sm", children: "The endpoint, SOAP version, Content-Type header, and per-operation envelope are derived from the WSDL - you only pick the operation and fill the parameters." })
250
+ ] })
251
+ ] });
252
+ }
253
+ export {
254
+ SoapEditor
255
+ };