@veltrixsecops/app-sdk 3.2.0 → 3.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +68 -1
  2. package/dist/byol/index.cjs +3802 -0
  3. package/dist/byol/index.cjs.map +1 -0
  4. package/dist/byol/index.d.cts +578 -0
  5. package/dist/byol/index.d.ts +578 -0
  6. package/dist/byol/index.js +2587 -0
  7. package/dist/byol/index.js.map +1 -0
  8. package/dist/chunk-3WK4EVQ3.js +842 -0
  9. package/dist/chunk-3WK4EVQ3.js.map +1 -0
  10. package/dist/{chunk-EOOEHZGC.js → chunk-HIL6RIDG.js} +107 -3
  11. package/dist/chunk-HIL6RIDG.js.map +1 -0
  12. package/dist/{chunk-VWFTOFTI.js → chunk-Y6XBNZGR.js} +8 -2
  13. package/dist/chunk-Y6XBNZGR.js.map +1 -0
  14. package/dist/client/index.cjs +112 -2
  15. package/dist/client/index.cjs.map +1 -1
  16. package/dist/client/index.d.cts +118 -6
  17. package/dist/client/index.d.ts +118 -6
  18. package/dist/client/index.js +13 -1
  19. package/dist/connections/index.cjs +1787 -0
  20. package/dist/connections/index.cjs.map +1 -0
  21. package/dist/connections/index.d.cts +78 -0
  22. package/dist/connections/index.d.ts +78 -0
  23. package/dist/connections/index.js +754 -0
  24. package/dist/connections/index.js.map +1 -0
  25. package/dist/hooks/index.cjs.map +1 -1
  26. package/dist/hooks/index.d.cts +2 -2
  27. package/dist/hooks/index.d.ts +2 -2
  28. package/dist/hooks/index.js +1 -1
  29. package/dist/index.cjs +7 -0
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +3 -3
  32. package/dist/index.d.ts +3 -3
  33. package/dist/index.js +3 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/opentofu/index.cjs +138 -0
  36. package/dist/opentofu/index.cjs.map +1 -0
  37. package/dist/opentofu/index.d.cts +131 -0
  38. package/dist/opentofu/index.d.ts +131 -0
  39. package/dist/opentofu/index.js +109 -0
  40. package/dist/opentofu/index.js.map +1 -0
  41. package/dist/pipeline/index.cjs +7 -0
  42. package/dist/pipeline/index.cjs.map +1 -1
  43. package/dist/pipeline/index.d.cts +9 -2
  44. package/dist/pipeline/index.d.ts +9 -2
  45. package/dist/pipeline/index.js +3 -1
  46. package/dist/pipeline-beeT8dje.d.cts +408 -0
  47. package/dist/pipeline-beeT8dje.d.ts +408 -0
  48. package/dist/ui/index.cjs +136 -0
  49. package/dist/ui/index.cjs.map +1 -1
  50. package/dist/ui/index.d.cts +109 -1
  51. package/dist/ui/index.d.ts +109 -1
  52. package/dist/ui/index.js +35 -677
  53. package/dist/ui/index.js.map +1 -1
  54. package/dist/{use-app-context-OQlcmq7t.d.cts → use-app-context-Byv_fam0.d.cts} +206 -4
  55. package/dist/{use-app-context-OQlcmq7t.d.ts → use-app-context-Byv_fam0.d.ts} +206 -4
  56. package/opentofu/README.md +114 -0
  57. package/opentofu/modules/aws/main.tf +1008 -0
  58. package/opentofu/modules/aws/outputs.tf +90 -0
  59. package/opentofu/modules/aws/variables.tf +437 -0
  60. package/opentofu/modules/aws/versions.tf +23 -0
  61. package/opentofu/modules/azure/main.tf +920 -0
  62. package/opentofu/modules/azure/outputs.tf +72 -0
  63. package/opentofu/modules/azure/variables.tf +498 -0
  64. package/opentofu/modules/azure/versions.tf +27 -0
  65. package/opentofu/modules/gcp/main.tf +645 -0
  66. package/opentofu/modules/gcp/outputs.tf +73 -0
  67. package/opentofu/modules/gcp/variables.tf +428 -0
  68. package/opentofu/modules/gcp/versions.tf +27 -0
  69. package/opentofu/modules/hetzner/main.tf +432 -0
  70. package/opentofu/modules/hetzner/outputs.tf +72 -0
  71. package/opentofu/modules/hetzner/variables.tf +423 -0
  72. package/opentofu/modules/hetzner/versions.tf +26 -0
  73. package/package.json +33 -1
  74. package/dist/chunk-EOOEHZGC.js.map +0 -1
  75. package/dist/chunk-VWFTOFTI.js.map +0 -1
  76. package/dist/pipeline-A-gSsPRR.d.cts +0 -189
  77. package/dist/pipeline-A-gSsPRR.d.ts +0 -189
@@ -0,0 +1,1787 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/connections/index.tsx
31
+ var connections_exports = {};
32
+ __export(connections_exports, {
33
+ ConnectionsManager: () => ConnectionsManager,
34
+ default: () => connections_default
35
+ });
36
+ module.exports = __toCommonJS(connections_exports);
37
+ var import_react = require("react");
38
+
39
+ // src/client/inventory.ts
40
+ var INVENTORY_API = "/api/components";
41
+ async function inventoryError(res) {
42
+ const text = await res.text().catch(() => "");
43
+ if (text) {
44
+ try {
45
+ const body = JSON.parse(text);
46
+ const message = body?.error ?? body?.message;
47
+ if (message) return new Error(message);
48
+ } catch {
49
+ }
50
+ return new Error(text);
51
+ }
52
+ return new Error(`HTTP ${res.status}`);
53
+ }
54
+ function toInventoryItem(raw) {
55
+ return {
56
+ id: String(raw.id),
57
+ hostname: raw.hostname ?? "",
58
+ port: raw.port ?? void 0,
59
+ webPort: raw.webPort ?? null,
60
+ sshUser: raw.sshUser ?? null,
61
+ splunkHome: raw.splunkHome ?? null,
62
+ type: Array.isArray(raw.type) ? raw.type : void 0,
63
+ domains: Array.isArray(raw.domains) ? raw.domains : [],
64
+ ipRanges: Array.isArray(raw.ipRanges) ? raw.ipRanges : [],
65
+ tags: Array.isArray(raw.tags) ? raw.tags.map((tag) => ({ id: String(tag.id), name: String(tag.name) })) : [],
66
+ connectivityProviderId: raw.connectivityProviderId ?? null,
67
+ credentialId: raw.credentialId ?? null
68
+ };
69
+ }
70
+ async function resolveTool(name) {
71
+ const res = await authFetch(`/api/tools?search=${encodeURIComponent(name)}&limit=100`);
72
+ if (!res.ok) throw await inventoryError(res);
73
+ const body = await res.json();
74
+ const tools = Array.isArray(body) ? body : Array.isArray(body?.data) ? body.data : [];
75
+ return tools.find((tool) => tool.name === name) ?? null;
76
+ }
77
+ async function listInventory() {
78
+ const res = await authFetch(INVENTORY_API);
79
+ if (!res.ok) throw await inventoryError(res);
80
+ const data = await res.json();
81
+ return Array.isArray(data) ? data.map(toInventoryItem) : [];
82
+ }
83
+ async function addInventoryItem(input) {
84
+ const res = await authFetch(INVENTORY_API, {
85
+ method: "POST",
86
+ headers: { "Content-Type": "application/json" },
87
+ body: JSON.stringify(input)
88
+ });
89
+ if (!res.ok) throw await inventoryError(res);
90
+ return toInventoryItem(await res.json());
91
+ }
92
+ async function updateInventoryItem(id, input) {
93
+ const res = await authFetch(`${INVENTORY_API}/${encodeURIComponent(id)}`, {
94
+ method: "PUT",
95
+ headers: { "Content-Type": "application/json" },
96
+ body: JSON.stringify(input)
97
+ });
98
+ if (!res.ok) throw await inventoryError(res);
99
+ return toInventoryItem(await res.json());
100
+ }
101
+
102
+ // src/client/environments.ts
103
+ var ENVIRONMENTS_API = "/api/environments";
104
+ async function environmentError(res) {
105
+ const text = await res.text().catch(() => "");
106
+ if (text) {
107
+ try {
108
+ const body = JSON.parse(text);
109
+ const message = body?.error ?? body?.message;
110
+ if (message) return new Error(message);
111
+ } catch {
112
+ }
113
+ return new Error(text);
114
+ }
115
+ return new Error(`HTTP ${res.status}`);
116
+ }
117
+ async function listEnvironments() {
118
+ const res = await authFetch(ENVIRONMENTS_API);
119
+ if (!res.ok) throw await environmentError(res);
120
+ const body = await res.json();
121
+ const rows = Array.isArray(body) ? body : Array.isArray(body?.data) ? body.data : [];
122
+ return rows.map((env) => ({
123
+ id: String(env.id),
124
+ name: env.name ?? "",
125
+ environmentTypes: Array.isArray(env.environmentTypes) ? env.environmentTypes : []
126
+ }));
127
+ }
128
+
129
+ // src/client/credentials.ts
130
+ var CREDENTIALS_API = "/api/credentials";
131
+ async function credentialError(res) {
132
+ const text = await res.text().catch(() => "");
133
+ if (text) {
134
+ try {
135
+ const body = JSON.parse(text);
136
+ const message = body?.error ?? body?.message;
137
+ if (message) return new Error(message);
138
+ } catch {
139
+ }
140
+ return new Error(text);
141
+ }
142
+ return new Error(`HTTP ${res.status}`);
143
+ }
144
+ function toCredentialSummary(raw) {
145
+ const hasSecret = Boolean(
146
+ raw.hasApiToken || raw.hasPassword || raw.apiToken && raw.apiToken.length > 0 || raw.password && raw.password.length > 0
147
+ );
148
+ return {
149
+ id: String(raw.id),
150
+ name: raw.name ?? "",
151
+ username: raw.username ?? "",
152
+ type: raw.type ?? null,
153
+ endpoint: raw.endpoint ?? null,
154
+ environmentType: raw.environmentType ?? null,
155
+ toolId: raw.toolId ?? "",
156
+ hasSecret,
157
+ tags: Array.isArray(raw.tags) ? raw.tags.map((t) => ({ id: String(t.id), name: String(t.name) })) : []
158
+ };
159
+ }
160
+ async function listCredentials(toolId) {
161
+ const res = await authFetch(`/api/tools/${encodeURIComponent(toolId)}/credentials`);
162
+ if (!res.ok) throw await credentialError(res);
163
+ const data = await res.json();
164
+ const rows = Array.isArray(data) ? data : Array.isArray(data?.data) ? data.data : [];
165
+ return rows.map(toCredentialSummary);
166
+ }
167
+ async function createCredential(input) {
168
+ const res = await authFetch(CREDENTIALS_API, {
169
+ method: "POST",
170
+ headers: { "Content-Type": "application/json" },
171
+ body: JSON.stringify({
172
+ name: input.name,
173
+ username: input.username,
174
+ password: input.password ?? "",
175
+ apiToken: input.apiToken,
176
+ type: input.type,
177
+ endpoint: input.endpoint,
178
+ environmentType: input.environmentType ?? null,
179
+ toolId: input.toolId,
180
+ tagIds: input.tagIds ?? []
181
+ })
182
+ });
183
+ if (!res.ok) throw await credentialError(res);
184
+ const body = await res.json();
185
+ return { id: String(body.id) };
186
+ }
187
+ async function updateCredential(id, input) {
188
+ const body = {};
189
+ if (input.name !== void 0) body.name = input.name;
190
+ if (input.username !== void 0) body.username = input.username;
191
+ if (input.password !== void 0) body.password = input.password;
192
+ if (input.apiToken !== void 0) body.apiToken = input.apiToken;
193
+ if (input.type !== void 0) body.type = input.type;
194
+ if (input.endpoint !== void 0) body.endpoint = input.endpoint;
195
+ if (input.environmentType !== void 0) body.environmentType = input.environmentType;
196
+ if (input.tagIds !== void 0) body.tagIds = input.tagIds;
197
+ const res = await authFetch(`${CREDENTIALS_API}/${encodeURIComponent(id)}`, {
198
+ method: "PUT",
199
+ headers: { "Content-Type": "application/json" },
200
+ body: JSON.stringify(body)
201
+ });
202
+ if (!res.ok) throw await credentialError(res);
203
+ const result = await res.json().catch(() => ({}));
204
+ return { id: result.id ? String(result.id) : id };
205
+ }
206
+ async function removeCredential(id) {
207
+ const res = await authFetch(`${CREDENTIALS_API}/${encodeURIComponent(id)}`, {
208
+ method: "DELETE"
209
+ });
210
+ if (!res.ok && res.status !== 204) throw await credentialError(res);
211
+ }
212
+ async function testConnection(appId, credentialId, opts = {}) {
213
+ const endpoint = typeof opts.endpoint === "string" ? opts.endpoint.trim() : "";
214
+ const res = await authFetch(
215
+ `/api/apps/${encodeURIComponent(appId)}/connections/${encodeURIComponent(credentialId)}/test`,
216
+ endpoint ? { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ endpoint }) } : { method: "POST" }
217
+ );
218
+ if (!res.ok) {
219
+ const err = await credentialError(res);
220
+ return { ok: false, message: err.message };
221
+ }
222
+ return await res.json();
223
+ }
224
+
225
+ // src/client/onboarding.ts
226
+ async function onboardingError(res) {
227
+ const text = await res.text().catch(() => "");
228
+ if (text) {
229
+ try {
230
+ const body = JSON.parse(text);
231
+ const message = body?.error ?? body?.message;
232
+ if (message) return new Error(message);
233
+ } catch {
234
+ }
235
+ return new Error(text);
236
+ }
237
+ return new Error(`HTTP ${res.status}`);
238
+ }
239
+ async function fetchAppOnboarding(appId) {
240
+ try {
241
+ const res = await authFetch("/api/apps/enabled", { method: "GET" });
242
+ if (!res.ok) return null;
243
+ const data = await res.json();
244
+ const list = Array.isArray(data) ? data : data?.data ?? [];
245
+ const app = list.find(
246
+ (a) => a.appId === appId || a.id === appId
247
+ );
248
+ return app?.connection?.onboarding ?? null;
249
+ } catch {
250
+ return null;
251
+ }
252
+ }
253
+ async function startOnboarding(appId, input) {
254
+ const res = await authFetch(
255
+ `/api/apps/${encodeURIComponent(appId)}/connections/onboard/start`,
256
+ {
257
+ method: "POST",
258
+ headers: { "Content-Type": "application/json" },
259
+ body: JSON.stringify({
260
+ environmentId: input.environmentId,
261
+ connectionName: input.connectionName,
262
+ settings: input.settings ?? {}
263
+ })
264
+ }
265
+ );
266
+ if (!res.ok) throw await onboardingError(res);
267
+ return await res.json();
268
+ }
269
+
270
+ // src/client/index.ts
271
+ var HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
272
+ function getHostRuntime() {
273
+ const runtime = globalThis[HOST_RUNTIME_GLOBAL];
274
+ return runtime ?? null;
275
+ }
276
+ function authFetch(input, init) {
277
+ const runtime = getHostRuntime();
278
+ if (runtime) return runtime.authFetch(input, init);
279
+ return fetch(input, init);
280
+ }
281
+
282
+ // src/ui/index.tsx
283
+ var React = __toESM(require("react"), 1);
284
+ var import_jsx_runtime = require("react/jsx-runtime");
285
+ function getHostUi(name) {
286
+ return getHostRuntime()?.ui?.[name];
287
+ }
288
+ var fallbackNote = { fontFamily: "inherit" };
289
+ var Button = React.forwardRef((props, ref) => {
290
+ const HostButton = getHostUi("Button");
291
+ if (HostButton) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostButton, { ref, ...props });
292
+ const {
293
+ variant: _variant,
294
+ size: _size,
295
+ isLoading,
296
+ loadingText,
297
+ fullWidth,
298
+ leftIcon,
299
+ rightIcon,
300
+ children,
301
+ disabled,
302
+ type,
303
+ style,
304
+ ...rest
305
+ } = props;
306
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
307
+ "button",
308
+ {
309
+ ref,
310
+ type: type ?? "button",
311
+ disabled: disabled || isLoading,
312
+ "aria-busy": isLoading || void 0,
313
+ style: {
314
+ ...fallbackNote,
315
+ display: "inline-flex",
316
+ alignItems: "center",
317
+ gap: 6,
318
+ padding: "6px 14px",
319
+ borderRadius: 6,
320
+ border: "1px solid currentColor",
321
+ background: "transparent",
322
+ cursor: disabled || isLoading ? "not-allowed" : "pointer",
323
+ width: fullWidth ? "100%" : void 0,
324
+ opacity: disabled || isLoading ? 0.6 : 1,
325
+ justifyContent: "center",
326
+ ...style
327
+ },
328
+ ...rest,
329
+ children: [
330
+ leftIcon,
331
+ isLoading ? loadingText ?? children : children,
332
+ rightIcon
333
+ ]
334
+ }
335
+ );
336
+ });
337
+ Button.displayName = "Button";
338
+ var BADGE_FALLBACK_COLORS = {
339
+ default: "#6b7280",
340
+ primary: "#4f46e5",
341
+ secondary: "#6b7280",
342
+ success: "#16a34a",
343
+ danger: "#dc2626",
344
+ warning: "#d97706",
345
+ info: "#0284c7"
346
+ };
347
+ var Badge = ({ variant = "default", size = "md", rounded, dot, children, style, ...rest }) => {
348
+ const HostBadge = getHostUi("Badge");
349
+ if (HostBadge) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostBadge, { variant, size, rounded, dot, style, ...rest, children });
350
+ const color = BADGE_FALLBACK_COLORS[variant];
351
+ const fontSize = size === "sm" ? 11 : size === "lg" ? 14 : 12;
352
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
353
+ "span",
354
+ {
355
+ style: {
356
+ ...fallbackNote,
357
+ display: "inline-flex",
358
+ alignItems: "center",
359
+ gap: 4,
360
+ padding: size === "sm" ? "1px 6px" : size === "lg" ? "3px 10px" : "2px 8px",
361
+ borderRadius: rounded ? 999 : 4,
362
+ border: `1px solid ${color}`,
363
+ color,
364
+ fontSize,
365
+ fontWeight: 500,
366
+ ...style
367
+ },
368
+ ...rest,
369
+ children: [
370
+ dot && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": "true", style: { width: 6, height: 6, borderRadius: "50%", background: color } }),
371
+ children
372
+ ]
373
+ }
374
+ );
375
+ };
376
+ Badge.displayName = "Badge";
377
+ var Card = ({ children, style, ...rest }) => {
378
+ const HostCard = getHostUi("Card");
379
+ if (HostCard) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCard, { style, ...rest, children });
380
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...fallbackNote, border: "1px solid #d1d5db", borderRadius: 8, overflow: "hidden", ...style }, ...rest, children });
381
+ };
382
+ Card.displayName = "Card";
383
+ var CardHeader = ({ actions, children, style, ...rest }) => {
384
+ const HostCardHeader = getHostUi("CardHeader");
385
+ if (HostCardHeader) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCardHeader, { actions, style, ...rest, children });
386
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
387
+ "div",
388
+ {
389
+ style: {
390
+ ...fallbackNote,
391
+ padding: "12px 16px",
392
+ borderBottom: "1px solid #d1d5db",
393
+ display: "flex",
394
+ alignItems: "center",
395
+ justifyContent: "space-between",
396
+ gap: 12,
397
+ ...style
398
+ },
399
+ ...rest,
400
+ children: [
401
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1, minWidth: 0 }, children }),
402
+ actions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: actions })
403
+ ]
404
+ }
405
+ );
406
+ };
407
+ CardHeader.displayName = "CardHeader";
408
+ var CardBody = ({ children, style, ...rest }) => {
409
+ const HostCardBody = getHostUi("CardBody");
410
+ if (HostCardBody) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCardBody, { style, ...rest, children });
411
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...fallbackNote, padding: "12px 16px", ...style }, ...rest, children });
412
+ };
413
+ CardBody.displayName = "CardBody";
414
+ var CardFooter = ({ children, style, ...rest }) => {
415
+ const HostCardFooter = getHostUi("CardFooter");
416
+ if (HostCardFooter) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCardFooter, { style, ...rest, children });
417
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...fallbackNote, padding: "12px 16px", borderTop: "1px solid #d1d5db", ...style }, ...rest, children });
418
+ };
419
+ CardFooter.displayName = "CardFooter";
420
+ var Input = React.forwardRef((props, ref) => {
421
+ const HostInput = getHostUi("Input");
422
+ if (HostInput) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostInput, { ref, ...props });
423
+ const { label, error, helperText, leftIcon, rightIcon, fullWidth = true, id, style, ...rest } = props;
424
+ const inputId = id ?? (label ? `vx-input-${label.toLowerCase().replace(/\s+/g, "-")}` : void 0);
425
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...fallbackNote, width: fullWidth ? "100%" : void 0 }, children: [
426
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: inputId, style: { display: "block", fontSize: 13, fontWeight: 500, marginBottom: 4 }, children: label }),
427
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
428
+ leftIcon,
429
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
430
+ "input",
431
+ {
432
+ ref,
433
+ id: inputId,
434
+ "aria-invalid": !!error || void 0,
435
+ style: { flex: 1, padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af", ...style },
436
+ ...rest
437
+ }
438
+ ),
439
+ rightIcon
440
+ ] }),
441
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
442
+ helperText && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: helperText })
443
+ ] });
444
+ });
445
+ Input.displayName = "Input";
446
+ var Textarea = React.forwardRef((props, ref) => {
447
+ const HostTextarea = getHostUi("Textarea");
448
+ if (HostTextarea) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostTextarea, { ref, ...props });
449
+ const { label, error, helperText, fullWidth = true, id, rows = 4, style, ...rest } = props;
450
+ const textareaId = id ?? (label ? `vx-textarea-${label.toLowerCase().replace(/\s+/g, "-")}` : void 0);
451
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...fallbackNote, width: fullWidth ? "100%" : void 0 }, children: [
452
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: textareaId, style: { display: "block", fontSize: 13, fontWeight: 500, marginBottom: 4 }, children: label }),
453
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
454
+ "textarea",
455
+ {
456
+ ref,
457
+ id: textareaId,
458
+ rows,
459
+ "aria-invalid": !!error || void 0,
460
+ style: { width: "100%", padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af", ...style },
461
+ ...rest
462
+ }
463
+ ),
464
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
465
+ helperText && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: helperText })
466
+ ] });
467
+ });
468
+ Textarea.displayName = "Textarea";
469
+ var Checkbox = React.forwardRef((props, ref) => {
470
+ const HostCheckbox = getHostUi("Checkbox");
471
+ if (HostCheckbox) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostCheckbox, { ref, ...props });
472
+ const { label, error, helperText, id, disabled, ...rest } = props;
473
+ const generatedId = React.useId();
474
+ const inputId = id ?? generatedId;
475
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: fallbackNote, children: [
476
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
477
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ref, id: inputId, type: "checkbox", disabled, "aria-invalid": !!error || void 0, ...rest }),
478
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: inputId, style: { fontSize: 13, cursor: disabled ? "not-allowed" : "pointer", opacity: disabled ? 0.6 : 1 }, children: label })
479
+ ] }),
480
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
481
+ helperText && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: helperText })
482
+ ] });
483
+ });
484
+ Checkbox.displayName = "Checkbox";
485
+ var Select = (props) => {
486
+ const HostSelect = getHostUi("Select");
487
+ if (HostSelect) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSelect, { ...props });
488
+ const { options, value, onChange, placeholder = "Select\u2026", label, error, helperText, disabled, fullWidth = true, id, name, className } = props;
489
+ const selectId = id ?? (label ? `vx-select-${label.toLowerCase().replace(/\s+/g, "-")}` : void 0);
490
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { ...fallbackNote, width: fullWidth ? "100%" : void 0 }, children: [
491
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: selectId, style: { display: "block", fontSize: 13, fontWeight: 500, marginBottom: 4 }, children: label }),
492
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
493
+ "select",
494
+ {
495
+ id: selectId,
496
+ name,
497
+ "aria-label": props["aria-label"],
498
+ "aria-invalid": !!error || void 0,
499
+ disabled,
500
+ value: value ?? "",
501
+ onChange: (event) => onChange?.(event.target.value),
502
+ style: { width: "100%", padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af" },
503
+ children: [
504
+ placeholder && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", disabled: true, children: placeholder }),
505
+ options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value))
506
+ ]
507
+ }
508
+ ),
509
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
510
+ helperText && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: helperText })
511
+ ] });
512
+ };
513
+ Select.displayName = "Select";
514
+ var SearchBox = (props) => {
515
+ const HostSearchBox = getHostUi("SearchBox");
516
+ if (HostSearchBox) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSearchBox, { ...props });
517
+ const { value, onChange, placeholder, disabled, className, "aria-label": ariaLabel } = props;
518
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
519
+ "input",
520
+ {
521
+ type: "search",
522
+ value,
523
+ onChange: (event) => onChange(event.target.value),
524
+ placeholder,
525
+ disabled,
526
+ "aria-label": ariaLabel ?? placeholder ?? "Search",
527
+ className,
528
+ style: { ...fallbackNote, width: "100%", padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af" }
529
+ }
530
+ );
531
+ };
532
+ SearchBox.displayName = "SearchBox";
533
+ var Pagination = (props) => {
534
+ const HostPagination = getHostUi("Pagination");
535
+ if (HostPagination) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostPagination, { ...props });
536
+ const { page, pageSize, totalItems, onPageChange, disabled, className } = props;
537
+ const pageCount = Math.max(1, Math.ceil(totalItems / pageSize));
538
+ const canGoPrev = !disabled && page > 1;
539
+ const canGoNext = !disabled && page < pageCount;
540
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("nav", { "aria-label": "Pagination", className, style: { ...fallbackNote, display: "flex", alignItems: "center", gap: 12 }, children: [
541
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => onPageChange(page - 1), disabled: !canGoPrev, style: { padding: "4px 10px" }, children: "Prev" }),
542
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
543
+ "page ",
544
+ page,
545
+ " of ",
546
+ pageCount
547
+ ] }),
548
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => onPageChange(page + 1), disabled: !canGoNext, style: { padding: "4px 10px" }, children: "Next" })
549
+ ] });
550
+ };
551
+ Pagination.displayName = "Pagination";
552
+ var FilterBar = (props) => {
553
+ const HostFilterBar = getHostUi("FilterBar");
554
+ if (HostFilterBar) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostFilterBar, { ...props });
555
+ const { filters, search, className } = props;
556
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { ...fallbackNote, display: "flex", flexWrap: "wrap", gap: 8, alignItems: "center" }, children: [
557
+ search && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
558
+ "input",
559
+ {
560
+ type: "search",
561
+ "aria-label": search.placeholder ?? "Search",
562
+ placeholder: search.placeholder,
563
+ value: search.value,
564
+ onChange: (event) => search.onChange(event.target.value),
565
+ style: { padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af" }
566
+ }
567
+ ),
568
+ filters.map((filter) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { display: "flex", flexDirection: "column", fontSize: 12 }, children: [
569
+ filter.label,
570
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
571
+ "select",
572
+ {
573
+ "aria-label": filter.label,
574
+ value: filter.value ?? "",
575
+ onChange: (event) => filter.onChange(event.target.value === "" ? null : event.target.value),
576
+ style: { padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af" },
577
+ children: [
578
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: filter.label }),
579
+ filter.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.value, children: option.label }, option.value))
580
+ ]
581
+ }
582
+ )
583
+ ] }, filter.key))
584
+ ] });
585
+ };
586
+ FilterBar.displayName = "FilterBar";
587
+ var SortSelect = (props) => {
588
+ const HostSortSelect = getHostUi("SortSelect");
589
+ if (HostSortSelect) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSortSelect, { ...props });
590
+ const { options, value, direction, onChange, disabled, className } = props;
591
+ const directionLabel = direction === "asc" ? "Sort ascending" : "Sort descending";
592
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { ...fallbackNote, display: "flex", alignItems: "center", gap: 6 }, children: [
593
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
594
+ "select",
595
+ {
596
+ "aria-label": "Sort by",
597
+ value,
598
+ disabled,
599
+ onChange: (event) => onChange(event.target.value, direction),
600
+ style: { padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af" },
601
+ children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.value, children: option.label }, option.value))
602
+ }
603
+ ),
604
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
605
+ "button",
606
+ {
607
+ type: "button",
608
+ disabled,
609
+ onClick: () => onChange(value, direction === "asc" ? "desc" : "asc"),
610
+ "aria-label": directionLabel,
611
+ style: { padding: "4px 10px" },
612
+ children: direction === "asc" ? "\u2191" : "\u2193"
613
+ }
614
+ )
615
+ ] });
616
+ };
617
+ SortSelect.displayName = "SortSelect";
618
+ var FormField = (props) => {
619
+ const HostFormField = getHostUi("FormField");
620
+ if (HostFormField) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostFormField, { ...props });
621
+ const { label, htmlFor, error, hint, required, className, children } = props;
622
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: fallbackNote, children: [
623
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { htmlFor, style: { display: "block", fontSize: 13, fontWeight: 500, marginBottom: 4 }, children: [
624
+ label,
625
+ required && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": "true", style: { color: "#dc2626", marginLeft: 2 }, children: "*" })
626
+ ] }),
627
+ children,
628
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { marginTop: 4, fontSize: 12, color: "#dc2626" }, children: error }),
629
+ hint && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 4, fontSize: 12, color: "#6b7280" }, children: hint })
630
+ ] });
631
+ };
632
+ FormField.displayName = "FormField";
633
+ var Tabs = (props) => {
634
+ const HostTabs = getHostUi("Tabs");
635
+ if (HostTabs) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostTabs, { ...props });
636
+ const { tabs, defaultActiveIndex = 0, activeIndex, onTabChange, children, className } = props;
637
+ const [internalIndex, setInternalIndex] = React.useState(defaultActiveIndex);
638
+ const isControlled = activeIndex !== void 0;
639
+ const currentIndex = isControlled ? activeIndex : internalIndex;
640
+ const activeTab = tabs[currentIndex];
641
+ const selectTab = (index) => {
642
+ if (tabs[index]?.disabled) return;
643
+ if (!isControlled) setInternalIndex(index);
644
+ onTabChange?.(index);
645
+ };
646
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: fallbackNote, children: [
647
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { role: "tablist", style: { display: "flex", gap: 4, borderBottom: "1px solid #d1d5db" }, children: tabs.map((tab, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
648
+ "button",
649
+ {
650
+ type: "button",
651
+ role: "tab",
652
+ "aria-selected": index === currentIndex,
653
+ disabled: tab.disabled,
654
+ onClick: () => selectTab(index),
655
+ style: {
656
+ padding: "6px 12px",
657
+ border: "none",
658
+ borderBottom: index === currentIndex ? "2px solid currentColor" : "2px solid transparent",
659
+ background: "transparent",
660
+ cursor: tab.disabled ? "not-allowed" : "pointer",
661
+ fontWeight: index === currentIndex ? 600 : 400,
662
+ opacity: tab.disabled ? 0.5 : 1
663
+ },
664
+ children: tab.label
665
+ },
666
+ tab.key ?? index
667
+ )) }),
668
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { role: "tabpanel", style: { padding: 12 }, children: children || activeTab?.content })
669
+ ] });
670
+ };
671
+ Tabs.displayName = "Tabs";
672
+ var EmptyState = (props) => {
673
+ const HostEmptyState = getHostUi("EmptyState");
674
+ if (HostEmptyState) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostEmptyState, { ...props });
675
+ const { icon, title, description, action, className } = props;
676
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { ...fallbackNote, textAlign: "center", padding: "32px 16px" }, children: [
677
+ icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 12 }, children: icon }),
678
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontWeight: 600, marginBottom: description ? 4 : 0 }, children: title }),
679
+ description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 13, color: "#6b7280", marginBottom: action ? 12 : 0 }, children: description }),
680
+ action
681
+ ] });
682
+ };
683
+ EmptyState.displayName = "EmptyState";
684
+ var skeletonRadius = { text: 4, circular: "50%", rectangular: 8 };
685
+ var Skeleton = (props) => {
686
+ const HostSkeleton = getHostUi("Skeleton");
687
+ if (HostSkeleton) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSkeleton, { ...props });
688
+ const { variant = "text", width, height, style, ...rest } = props;
689
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
690
+ "div",
691
+ {
692
+ "aria-hidden": "true",
693
+ style: {
694
+ background: "#e5e7eb",
695
+ borderRadius: skeletonRadius[variant],
696
+ width: width ?? (variant === "text" ? "100%" : void 0),
697
+ height: height ?? (variant === "text" ? 14 : void 0),
698
+ ...style
699
+ },
700
+ ...rest
701
+ }
702
+ );
703
+ };
704
+ Skeleton.displayName = "Skeleton";
705
+ var SkeletonText = (props) => {
706
+ const HostSkeletonText = getHostUi("SkeletonText");
707
+ if (HostSkeletonText) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSkeletonText, { ...props });
708
+ const { lines = 3, width = "100%", lastLineWidth = "80%", className } = props;
709
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, style: { display: "flex", flexDirection: "column", gap: 8 }, children: Array.from({ length: lines }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "text", width: index === lines - 1 ? lastLineWidth : width }, index)) });
710
+ };
711
+ SkeletonText.displayName = "SkeletonText";
712
+ var SkeletonCard = (props) => {
713
+ const HostSkeletonCard = getHostUi("SkeletonCard");
714
+ if (HostSkeletonCard) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSkeletonCard, { ...props });
715
+ const { hasAvatar, hasActions, className } = props;
716
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: { border: "1px solid #e5e7eb", borderRadius: 8, padding: 16 }, children: [
717
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 12 }, children: [
718
+ hasAvatar && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "circular", width: 40, height: 40 }),
719
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1 }, children: [
720
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "text", width: "60%" }),
721
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SkeletonText, { lines: 2 }) })
722
+ ] })
723
+ ] }),
724
+ hasActions && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 8, marginTop: 12 }, children: [
725
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "rectangular", width: 72, height: 30 }),
726
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, { variant: "rectangular", width: 72, height: 30 })
727
+ ] })
728
+ ] });
729
+ };
730
+ SkeletonCard.displayName = "SkeletonCard";
731
+ var Tooltip = (props) => {
732
+ const HostTooltip = getHostUi("Tooltip");
733
+ if (HostTooltip) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostTooltip, { ...props });
734
+ const { content, disabled, className, children } = props;
735
+ const titleText = typeof content === "string" ? content : void 0;
736
+ if (disabled || !titleText) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
737
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className, title: titleText, style: { display: "inline-flex" }, children });
738
+ };
739
+ Tooltip.displayName = "Tooltip";
740
+ var spinnerDiameter = { sm: 16, md: 24, lg: 32 };
741
+ var Spinner = (props) => {
742
+ const HostSpinner = getHostUi("Spinner");
743
+ if (HostSpinner) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostSpinner, { ...props });
744
+ const { size = "md", className, label } = props;
745
+ const diameter = spinnerDiameter[size];
746
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, role: "status", "aria-live": "polite", style: { display: "flex", flexDirection: "column", alignItems: "center" }, children: [
747
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
748
+ "div",
749
+ {
750
+ "aria-hidden": "true",
751
+ style: {
752
+ width: diameter,
753
+ height: diameter,
754
+ borderRadius: "50%",
755
+ border: "2px solid currentColor",
756
+ borderTopColor: "transparent",
757
+ animation: "vx-sdk-spin 0.8s linear infinite"
758
+ }
759
+ }
760
+ ),
761
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { marginTop: 8, fontSize: 12, color: "#6b7280" }, children: label }),
762
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)" }, children: label || "Loading" }),
763
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: "@keyframes vx-sdk-spin { to { transform: rotate(360deg) } }" })
764
+ ] });
765
+ };
766
+ Spinner.displayName = "Spinner";
767
+ function FallbackDataTable(props) {
768
+ const { columns, data, rowKey, isLoading, emptyState, onRowClick, rowActions, className } = props;
769
+ const showEmpty = !isLoading && data.length === 0;
770
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, style: fallbackNote, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { style: { width: "100%", borderCollapse: "collapse" }, children: [
771
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
772
+ columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: { textAlign: column.align ?? "left", padding: "8px 10px", borderBottom: "1px solid #d1d5db" }, children: column.header }, column.key)),
773
+ rowActions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: { padding: "8px 10px", borderBottom: "1px solid #d1d5db" } })
774
+ ] }) }),
775
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tbody", { children: [
776
+ isLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { colSpan: Math.max(columns.length + (rowActions ? 1 : 0), 1), style: { padding: "12px 10px" }, children: "Loading\u2026" }) }),
777
+ showEmpty && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { colSpan: Math.max(columns.length + (rowActions ? 1 : 0), 1), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyState, { title: emptyState?.title ?? "No data", description: emptyState?.description, icon: emptyState?.icon, action: emptyState?.action }) }) }),
778
+ !isLoading && !showEmpty && data.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
779
+ "tr",
780
+ {
781
+ onClick: onRowClick ? () => onRowClick(row) : void 0,
782
+ style: { cursor: onRowClick ? "pointer" : void 0, borderBottom: "1px solid #e5e7eb" },
783
+ children: [
784
+ columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: { textAlign: column.align ?? "left", padding: "8px 10px" }, children: column.render ? column.render(row) : String(row[column.key] ?? "") }, column.key)),
785
+ rowActions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { style: { padding: "8px 10px" }, onClick: (event) => event.stopPropagation(), children: rowActions(row) })
786
+ ]
787
+ },
788
+ rowKey(row)
789
+ ))
790
+ ] })
791
+ ] }) });
792
+ }
793
+ function DataTable(props) {
794
+ const HostDataTable = getHostUi("DataTable");
795
+ if (HostDataTable) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostDataTable, { ...props });
796
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FallbackDataTable, { ...props });
797
+ }
798
+ var DELTA_FALLBACK_COLOR = {
799
+ up: "#16a34a",
800
+ down: "#dc2626",
801
+ neutral: "#6b7280"
802
+ };
803
+ var StatsCard = (props) => {
804
+ const HostStatsCard = getHostUi("StatsCard");
805
+ if (HostStatsCard) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostStatsCard, { ...props });
806
+ const { label, value, icon, delta, isLoading, onClick, className } = props;
807
+ const isClickable = Boolean(onClick);
808
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
809
+ "div",
810
+ {
811
+ role: isClickable ? "button" : void 0,
812
+ tabIndex: isClickable ? 0 : void 0,
813
+ onClick,
814
+ onKeyDown: isClickable ? (event) => {
815
+ if (event.key === "Enter" || event.key === " ") {
816
+ event.preventDefault();
817
+ onClick?.();
818
+ }
819
+ } : void 0,
820
+ className,
821
+ style: {
822
+ ...fallbackNote,
823
+ border: "1px solid #d1d5db",
824
+ borderRadius: 8,
825
+ padding: 16,
826
+ display: "flex",
827
+ alignItems: "flex-start",
828
+ justifyContent: "space-between",
829
+ gap: 12,
830
+ cursor: isClickable ? "pointer" : void 0
831
+ },
832
+ children: [
833
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { minWidth: 0, flex: 1 }, children: [
834
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 13, color: "#6b7280", margin: 0 }, children: label }),
835
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 28, fontWeight: 700, margin: "4px 0 0" }, children: isLoading ? "\u2026" : value }),
836
+ delta && !isLoading && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: { fontSize: 13, color: DELTA_FALLBACK_COLOR[delta.direction], margin: "4px 0 0" }, children: [
837
+ delta.value,
838
+ " ",
839
+ delta.label
840
+ ] })
841
+ ] }),
842
+ icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "aria-hidden": "true", children: icon })
843
+ ]
844
+ }
845
+ );
846
+ };
847
+ StatsCard.displayName = "StatsCard";
848
+ var FORM_DIALOG_MAX_WIDTH = { sm: 420, md: 520, lg: 680 };
849
+ var FormDialog = (props) => {
850
+ const HostFormDialog = getHostUi("FormDialog");
851
+ if (HostFormDialog) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostFormDialog, { ...props });
852
+ const {
853
+ isOpen,
854
+ onClose,
855
+ title,
856
+ description,
857
+ children,
858
+ onSubmit,
859
+ submitText = "Save",
860
+ cancelText = "Cancel",
861
+ isSubmitting = false,
862
+ error = null,
863
+ size = "md",
864
+ disableBackdropClose = false,
865
+ submitDisabled = false
866
+ } = props;
867
+ React.useEffect(() => {
868
+ if (!isOpen) return;
869
+ const handleKeyDown = (event) => {
870
+ if (event.key === "Escape" && !isSubmitting) onClose();
871
+ };
872
+ document.addEventListener("keydown", handleKeyDown);
873
+ return () => document.removeEventListener("keydown", handleKeyDown);
874
+ }, [isOpen, isSubmitting, onClose]);
875
+ if (!isOpen) return null;
876
+ const requestClose = () => {
877
+ if (!isSubmitting) onClose();
878
+ };
879
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
880
+ "div",
881
+ {
882
+ style: { position: "fixed", inset: 0, background: "rgba(0,0,0,0.5)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 50 },
883
+ onClick: disableBackdropClose ? void 0 : requestClose,
884
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
885
+ "div",
886
+ {
887
+ role: "dialog",
888
+ "aria-modal": "true",
889
+ "aria-label": title,
890
+ style: { background: "white", color: "black", borderRadius: 8, width: "100%", maxWidth: FORM_DIALOG_MAX_WIDTH[size], maxHeight: "80vh", overflow: "auto", ...fallbackNote },
891
+ onClick: (event) => event.stopPropagation(),
892
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
893
+ "form",
894
+ {
895
+ onSubmit: (event) => {
896
+ event.preventDefault();
897
+ void onSubmit();
898
+ },
899
+ children: [
900
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, borderBottom: "1px solid #d1d5db" }, children: [
901
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: { margin: 0, fontSize: 16, fontWeight: 600 }, children: title }),
902
+ description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { margin: "4px 0 0", fontSize: 13, color: "#6b7280" }, children: description })
903
+ ] }),
904
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, display: "flex", flexDirection: "column", gap: 12 }, children: [
905
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", style: { margin: 0, fontSize: 13, color: "#dc2626" }, children: error }),
906
+ children
907
+ ] }),
908
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, borderTop: "1px solid #d1d5db", display: "flex", justifyContent: "flex-end", gap: 8 }, children: [
909
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { type: "button", variant: "secondary", onClick: requestClose, disabled: isSubmitting, children: cancelText }),
910
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, { type: "submit", variant: "primary", isLoading: isSubmitting, disabled: submitDisabled, children: submitText })
911
+ ] })
912
+ ]
913
+ }
914
+ )
915
+ }
916
+ )
917
+ }
918
+ );
919
+ };
920
+ FormDialog.displayName = "FormDialog";
921
+ var MODAL_MAX_WIDTH = { sm: 420, md: 520, lg: 680, xl: 896 };
922
+ var Modal = (props) => {
923
+ const HostModal = getHostUi("Modal");
924
+ if (HostModal) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostModal, { ...props });
925
+ const {
926
+ isOpen,
927
+ onClose,
928
+ title,
929
+ subtitle,
930
+ children,
931
+ footer,
932
+ size = "md",
933
+ disableBackdropClose = false,
934
+ disableEscapeClose = false,
935
+ hideCloseButton = false,
936
+ closeLabel = "Close",
937
+ className
938
+ } = props;
939
+ React.useEffect(() => {
940
+ if (!isOpen) return;
941
+ const handleKeyDown = (event) => {
942
+ if (event.key === "Escape" && !disableEscapeClose) onClose();
943
+ };
944
+ document.addEventListener("keydown", handleKeyDown);
945
+ return () => document.removeEventListener("keydown", handleKeyDown);
946
+ }, [isOpen, disableEscapeClose, onClose]);
947
+ if (!isOpen) return null;
948
+ const hasHeader = title != null || subtitle != null || !hideCloseButton;
949
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
950
+ "div",
951
+ {
952
+ style: { position: "fixed", inset: 0, background: "rgba(0,0,0,0.5)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 50 },
953
+ onClick: disableBackdropClose ? void 0 : onClose,
954
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
955
+ "div",
956
+ {
957
+ role: "dialog",
958
+ "aria-modal": "true",
959
+ style: { background: "white", color: "black", borderRadius: 8, width: "100%", maxWidth: MODAL_MAX_WIDTH[size], maxHeight: "80vh", overflow: "auto", ...fallbackNote },
960
+ className,
961
+ onClick: (event) => event.stopPropagation(),
962
+ children: [
963
+ hasHeader && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, borderBottom: "1px solid #d1d5db", display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 16 }, children: [
964
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { minWidth: 0 }, children: [
965
+ title != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: { margin: 0, fontSize: 16, fontWeight: 600 }, children: title }),
966
+ subtitle != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { margin: "4px 0 0", fontSize: 13, color: "#6b7280" }, children: subtitle })
967
+ ] }),
968
+ !hideCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: onClose, "aria-label": closeLabel, style: { background: "none", border: "none", cursor: "pointer", fontSize: 18, lineHeight: 1 }, children: "\xD7" })
969
+ ] }),
970
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: 16, display: "flex", flexDirection: "column", gap: 12 }, children }),
971
+ footer != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: 16, borderTop: "1px solid #d1d5db", display: "flex", justifyContent: "flex-end", gap: 8 }, children: footer })
972
+ ]
973
+ }
974
+ )
975
+ }
976
+ );
977
+ };
978
+ Modal.displayName = "Modal";
979
+ var ALERT_FALLBACK_COLORS = {
980
+ info: { bg: "#eff6ff", fg: "#1e40af", border: "#93c5fd" },
981
+ success: { bg: "#ecfdf5", fg: "#065f46", border: "#6ee7b7" },
982
+ warning: { bg: "#fffbeb", fg: "#92400e", border: "#fcd34d" },
983
+ danger: { bg: "#fef2f2", fg: "#991b1b", border: "#fca5a5" }
984
+ };
985
+ var Alert = (props) => {
986
+ const HostAlert = getHostUi("Alert");
987
+ if (HostAlert) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostAlert, { ...props });
988
+ const { variant = "info", title, children, icon, onDismiss, action, className } = props;
989
+ const c = ALERT_FALLBACK_COLORS[variant];
990
+ const role = variant === "danger" || variant === "warning" ? "alert" : "status";
991
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
992
+ "div",
993
+ {
994
+ role,
995
+ className,
996
+ style: {
997
+ display: "flex",
998
+ alignItems: "flex-start",
999
+ gap: 10,
1000
+ border: `1px solid ${c.border}`,
1001
+ background: c.bg,
1002
+ color: c.fg,
1003
+ borderRadius: 8,
1004
+ padding: "12px 16px",
1005
+ ...fallbackNote
1006
+ },
1007
+ children: [
1008
+ icon !== false && icon != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { flexShrink: 0 }, children: icon }),
1009
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { minWidth: 0, flex: 1 }, children: [
1010
+ title != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { margin: 0, fontSize: 14, fontWeight: 600 }, children: title }),
1011
+ children != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 14, marginTop: title != null ? 2 : 0 }, children })
1012
+ ] }),
1013
+ action != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flexShrink: 0 }, children: action }),
1014
+ onDismiss && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1015
+ "button",
1016
+ {
1017
+ type: "button",
1018
+ onClick: onDismiss,
1019
+ "aria-label": "Dismiss",
1020
+ style: { flexShrink: 0, background: "none", border: "none", cursor: "pointer", color: "inherit", opacity: 0.7 },
1021
+ children: "\xD7"
1022
+ }
1023
+ )
1024
+ ]
1025
+ }
1026
+ );
1027
+ };
1028
+ Alert.displayName = "Alert";
1029
+ var MultiSelect = (props) => {
1030
+ const HostMultiSelect = getHostUi("MultiSelect");
1031
+ if (HostMultiSelect) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HostMultiSelect, { ...props });
1032
+ const { options, value = [], onChange, label, disabled, fullWidth = true, name } = props;
1033
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { ...fallbackNote, display: "block", width: fullWidth ? "100%" : void 0 }, children: [
1034
+ label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { display: "block", marginBottom: 4, fontSize: 14, fontWeight: 500 }, children: label }),
1035
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1036
+ "select",
1037
+ {
1038
+ multiple: true,
1039
+ name,
1040
+ disabled,
1041
+ value,
1042
+ "aria-label": props["aria-label"],
1043
+ onChange: (e) => onChange?.(Array.from(e.target.selectedOptions, (o) => o.value)),
1044
+ style: { width: "100%", minHeight: 96, padding: "6px 10px", borderRadius: 6, border: "1px solid #9ca3af" },
1045
+ children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value))
1046
+ }
1047
+ )
1048
+ ] });
1049
+ };
1050
+ MultiSelect.displayName = "MultiSelect";
1051
+ var fallbackConfirmationContext = {
1052
+ confirm: (options) => {
1053
+ console.warn(
1054
+ `[@veltrixsecops/app-sdk/ui] ConfirmationDialog is only available inside the Veltrix platform \u2014 "${options.title}" auto-resolved to "not confirmed" (fail closed).`
1055
+ );
1056
+ return Promise.resolve(false);
1057
+ }
1058
+ };
1059
+ function useConfirmDialog() {
1060
+ const hostUseConfirmDialog = getHostUi("useConfirmDialog");
1061
+ if (hostUseConfirmDialog) return hostUseConfirmDialog();
1062
+ return fallbackConfirmationContext;
1063
+ }
1064
+
1065
+ // src/connections/index.tsx
1066
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1067
+ var BLANK_FORM = {
1068
+ name: "",
1069
+ environmentId: "",
1070
+ environmentType: "",
1071
+ authType: "password",
1072
+ username: "",
1073
+ secret: "",
1074
+ endpoint: ""
1075
+ };
1076
+ var ENV_TYPE_LABELS = {
1077
+ production: "Production",
1078
+ "pre-production": "Pre-production",
1079
+ staging: "Staging",
1080
+ qa: "QA",
1081
+ uat: "UAT",
1082
+ development: "Development",
1083
+ testing: "Testing",
1084
+ sandbox: "Sandbox",
1085
+ dr: "DR (Disaster Recovery)",
1086
+ demo: "Demo",
1087
+ other: "Other"
1088
+ };
1089
+ var envTypeLabel = (t) => ENV_TYPE_LABELS[t] ?? t;
1090
+ function toCredentialType(authType) {
1091
+ return authType === "token" ? "TOKEN" : "PASSWORD";
1092
+ }
1093
+ function hostFromEndpoint(endpoint) {
1094
+ const trimmed = (endpoint || "").trim();
1095
+ if (!trimmed) return "";
1096
+ const withScheme = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
1097
+ try {
1098
+ return new URL(withScheme).host;
1099
+ } catch {
1100
+ return trimmed.replace(/^https?:\/\//i, "").replace(/[/?#].*$/, "");
1101
+ }
1102
+ }
1103
+ function portFromEndpoint(endpoint) {
1104
+ const trimmed = (endpoint || "").trim();
1105
+ const withScheme = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
1106
+ try {
1107
+ const url = new URL(withScheme);
1108
+ if (url.port) return url.port;
1109
+ return url.protocol === "http:" ? "80" : "443";
1110
+ } catch {
1111
+ return "443";
1112
+ }
1113
+ }
1114
+ async function ensureConnectionComponent(params) {
1115
+ const hostname = hostFromEndpoint(params.endpoint);
1116
+ if (!hostname) {
1117
+ throw new Error("An endpoint is required to register a deploy target for this connection.");
1118
+ }
1119
+ const items = await listInventory();
1120
+ const existing = items.find(
1121
+ (c) => (c.type ?? []).includes(params.componentType) && (params.environmentId ? (c.tags ?? []).some((t) => t.id === params.environmentId) : true)
1122
+ );
1123
+ const input = {
1124
+ hostname,
1125
+ port: portFromEndpoint(params.endpoint),
1126
+ type: [params.componentType],
1127
+ toolId: params.toolId,
1128
+ credentialId: params.credentialId,
1129
+ tagIds: params.environmentId ? [params.environmentId] : []
1130
+ };
1131
+ if (existing) await updateInventoryItem(existing.id, input);
1132
+ else await addInventoryItem(input);
1133
+ }
1134
+ function fromCredentialType(type) {
1135
+ return type === "TOKEN" ? "token" : "password";
1136
+ }
1137
+ var ConnectionsManager = ({
1138
+ appName,
1139
+ appId,
1140
+ title = "Connections",
1141
+ namePlaceholder = "e.g. Production API",
1142
+ endpointPlaceholder = "e.g. https://api.example.com",
1143
+ endpointHelper = "API base URL / endpoint this connection reaches.",
1144
+ usernameLabel = "Username",
1145
+ tokenLabel = "API token",
1146
+ tokenUsernamePlaceholder = "service account (optional)",
1147
+ passwordUsernamePlaceholder = "e.g. svc_veltrix",
1148
+ usernameOptionalForToken = true,
1149
+ onboarding,
1150
+ componentType
1151
+ }) => {
1152
+ const { confirm } = useConfirmDialog();
1153
+ const authTypes = (0, import_react.useMemo)(
1154
+ () => [
1155
+ { value: "password", label: "Username & password" },
1156
+ { value: "token", label: tokenLabel }
1157
+ ],
1158
+ [tokenLabel]
1159
+ );
1160
+ const [connections, setConnections] = (0, import_react.useState)([]);
1161
+ const [environments, setEnvironments] = (0, import_react.useState)([]);
1162
+ const [error, setError] = (0, import_react.useState)(null);
1163
+ const [isLoading, setIsLoading] = (0, import_react.useState)(true);
1164
+ const [search, setSearch] = (0, import_react.useState)("");
1165
+ const [environmentFilter, setEnvironmentFilter] = (0, import_react.useState)(null);
1166
+ const [authTypeFilter, setAuthTypeFilter] = (0, import_react.useState)(null);
1167
+ const [sortField, setSortField] = (0, import_react.useState)("name");
1168
+ const [sortDir, setSortDir] = (0, import_react.useState)("asc");
1169
+ const [page, setPage] = (0, import_react.useState)(1);
1170
+ const [pageSize, setPageSize] = (0, import_react.useState)(10);
1171
+ const [dialogOpen, setDialogOpen] = (0, import_react.useState)(false);
1172
+ const [editing, setEditing] = (0, import_react.useState)(null);
1173
+ const [form, setForm] = (0, import_react.useState)(BLANK_FORM);
1174
+ const [submitting, setSubmitting] = (0, import_react.useState)(false);
1175
+ const [formError, setFormError] = (0, import_react.useState)(null);
1176
+ const [tests, setTests] = (0, import_react.useState)({});
1177
+ const [onboardOpen, setOnboardOpen] = (0, import_react.useState)(false);
1178
+ const [onboardName, setOnboardName] = (0, import_react.useState)("");
1179
+ const [onboardEnvironmentId, setOnboardEnvironmentId] = (0, import_react.useState)("");
1180
+ const [onboardSettings, setOnboardSettings] = (0, import_react.useState)({});
1181
+ const [onboardSubmitting, setOnboardSubmitting] = (0, import_react.useState)(false);
1182
+ const [onboardError, setOnboardError] = (0, import_react.useState)(null);
1183
+ const [notice, setNotice] = (0, import_react.useState)(null);
1184
+ const [fetchedOnboarding, setFetchedOnboarding] = (0, import_react.useState)(null);
1185
+ const effectiveOnboarding = onboarding ?? fetchedOnboarding;
1186
+ const load = (0, import_react.useCallback)(async () => {
1187
+ setIsLoading(true);
1188
+ setError(null);
1189
+ try {
1190
+ const tool = await resolveTool(appName);
1191
+ const [creds, envs] = await Promise.all([
1192
+ tool ? listCredentials(tool.id) : Promise.resolve([]),
1193
+ listEnvironments()
1194
+ ]);
1195
+ setConnections(creds);
1196
+ setEnvironments(envs);
1197
+ } catch (e) {
1198
+ setError(e.message);
1199
+ } finally {
1200
+ setIsLoading(false);
1201
+ }
1202
+ }, [appName]);
1203
+ (0, import_react.useEffect)(() => {
1204
+ void load();
1205
+ }, [load]);
1206
+ (0, import_react.useEffect)(() => {
1207
+ if (onboarding) return;
1208
+ let cancelled = false;
1209
+ void fetchAppOnboarding(appId).then((d) => {
1210
+ if (cancelled || !d) return;
1211
+ setFetchedOnboarding({
1212
+ provider: d.provider,
1213
+ label: d.label,
1214
+ brokered: d.brokered ?? false,
1215
+ requiredSettings: d.requiredSettings ?? []
1216
+ });
1217
+ });
1218
+ return () => {
1219
+ cancelled = true;
1220
+ };
1221
+ }, [appId, onboarding]);
1222
+ (0, import_react.useEffect)(() => {
1223
+ if (typeof window === "undefined") return;
1224
+ const params = new URLSearchParams(window.location.search);
1225
+ const outcome = params.get("onboarded");
1226
+ if (!outcome) return;
1227
+ if (outcome === "ok") {
1228
+ setNotice(
1229
+ params.get("pending") === "manual" ? "Connection created. One manual step remains \u2014 see the connection to finish setup." : "Connection created successfully."
1230
+ );
1231
+ } else {
1232
+ setError(`Onboarding failed: ${params.get("reason") || "unknown error"}`);
1233
+ }
1234
+ params.delete("onboarded");
1235
+ params.delete("pending");
1236
+ params.delete("reason");
1237
+ params.delete("connectionId");
1238
+ const qs = params.toString();
1239
+ window.history.replaceState({}, "", `${window.location.pathname}${qs ? `?${qs}` : ""}`);
1240
+ }, []);
1241
+ const openOnboard = () => {
1242
+ setOnboardName("");
1243
+ setOnboardEnvironmentId("");
1244
+ setOnboardSettings({});
1245
+ setOnboardError(null);
1246
+ setOnboardOpen(true);
1247
+ };
1248
+ const handleStartOnboarding = async () => {
1249
+ if (!effectiveOnboarding) return;
1250
+ const name = onboardName.trim();
1251
+ if (!name) {
1252
+ setOnboardError("Name is required");
1253
+ return;
1254
+ }
1255
+ if (!onboardEnvironmentId) {
1256
+ setOnboardError("Environment is required");
1257
+ return;
1258
+ }
1259
+ const missing = effectiveOnboarding.requiredSettings.filter((k) => !onboardSettings[k]?.trim());
1260
+ if (missing.length > 0) {
1261
+ setOnboardError(`Please provide: ${missing.join(", ")}`);
1262
+ return;
1263
+ }
1264
+ setOnboardSubmitting(true);
1265
+ setOnboardError(null);
1266
+ try {
1267
+ const { authorizeUrl } = await startOnboarding(appId, {
1268
+ environmentId: onboardEnvironmentId,
1269
+ connectionName: name,
1270
+ settings: onboardSettings
1271
+ });
1272
+ if (typeof window !== "undefined") window.location.assign(authorizeUrl);
1273
+ } catch (e) {
1274
+ setOnboardError(e.message);
1275
+ setOnboardSubmitting(false);
1276
+ }
1277
+ };
1278
+ const openCreate = () => {
1279
+ setEditing(null);
1280
+ setForm(BLANK_FORM);
1281
+ setFormError(null);
1282
+ setDialogOpen(true);
1283
+ };
1284
+ const openEdit = (row) => {
1285
+ setEditing(row);
1286
+ setForm({
1287
+ name: row.name ?? "",
1288
+ environmentId: row.tags?.[0]?.id ?? "",
1289
+ environmentType: row.environmentType ?? "",
1290
+ authType: fromCredentialType(row.type),
1291
+ username: row.username ?? "",
1292
+ secret: "",
1293
+ endpoint: row.endpoint ?? ""
1294
+ });
1295
+ setFormError(null);
1296
+ setDialogOpen(true);
1297
+ };
1298
+ const closeDialog = (0, import_react.useCallback)(() => {
1299
+ if (submitting) return;
1300
+ setDialogOpen(false);
1301
+ }, [submitting]);
1302
+ const setField = (key, value) => setForm((prev) => ({ ...prev, [key]: value }));
1303
+ const handleSubmit = async () => {
1304
+ const name = form.name.trim();
1305
+ if (!name) {
1306
+ setFormError("Name is required");
1307
+ return;
1308
+ }
1309
+ if (!form.environmentId) {
1310
+ setFormError("Environment is required");
1311
+ return;
1312
+ }
1313
+ setSubmitting(true);
1314
+ setFormError(null);
1315
+ const secret = form.secret.trim();
1316
+ const username = form.username.trim();
1317
+ const endpoint = form.endpoint.trim();
1318
+ const tagIds = [form.environmentId];
1319
+ try {
1320
+ let credentialId;
1321
+ let toolId;
1322
+ if (editing) {
1323
+ const update = {
1324
+ name,
1325
+ username,
1326
+ type: toCredentialType(form.authType),
1327
+ endpoint,
1328
+ environmentType: form.environmentType || null,
1329
+ tagIds
1330
+ };
1331
+ if (secret) {
1332
+ if (form.authType === "token") update.apiToken = secret;
1333
+ else update.password = secret;
1334
+ }
1335
+ await updateCredential(editing.id, update);
1336
+ credentialId = editing.id;
1337
+ } else {
1338
+ const tool = await resolveTool(appName);
1339
+ if (!tool) {
1340
+ throw new Error(
1341
+ `No "${appName}" tool found for your organization \u2014 make sure the app is installed before adding connections.`
1342
+ );
1343
+ }
1344
+ toolId = tool.id;
1345
+ const created = await createCredential({
1346
+ name,
1347
+ username,
1348
+ password: form.authType === "password" ? secret : "",
1349
+ apiToken: form.authType === "token" ? secret : void 0,
1350
+ type: toCredentialType(form.authType),
1351
+ endpoint,
1352
+ environmentType: form.environmentType || null,
1353
+ toolId: tool.id,
1354
+ tagIds
1355
+ });
1356
+ credentialId = created.id;
1357
+ }
1358
+ if (componentType) {
1359
+ try {
1360
+ if (!toolId) toolId = (await resolveTool(appName))?.id;
1361
+ await ensureConnectionComponent({
1362
+ componentType,
1363
+ toolId,
1364
+ credentialId,
1365
+ endpoint,
1366
+ environmentId: form.environmentId
1367
+ });
1368
+ } catch (compErr) {
1369
+ setNotice(
1370
+ `Connection saved, but registering its deploy target failed: ${compErr.message} Re-save the connection to retry.`
1371
+ );
1372
+ }
1373
+ }
1374
+ setDialogOpen(false);
1375
+ await load();
1376
+ } catch (e) {
1377
+ setFormError(e.message);
1378
+ } finally {
1379
+ setSubmitting(false);
1380
+ }
1381
+ };
1382
+ const handleDelete = async (row) => {
1383
+ const ok = await confirm({
1384
+ title: "Remove connection",
1385
+ message: `Remove the connection "${row.name}"? This cannot be undone.`,
1386
+ confirmText: "Remove",
1387
+ cancelText: "Cancel",
1388
+ variant: "danger"
1389
+ });
1390
+ if (!ok) return;
1391
+ try {
1392
+ await removeCredential(row.id);
1393
+ await load();
1394
+ } catch (e) {
1395
+ setError(e.message);
1396
+ }
1397
+ };
1398
+ const handleTest = async (row) => {
1399
+ setTests((t) => ({ ...t, [row.id]: { loading: true } }));
1400
+ try {
1401
+ const result = await testConnection(appId, row.id);
1402
+ setTests((t) => ({ ...t, [row.id]: result }));
1403
+ } catch (e) {
1404
+ setTests((t) => ({ ...t, [row.id]: { ok: false, message: e.message } }));
1405
+ }
1406
+ };
1407
+ const columns = [
1408
+ { key: "name", header: "Name", render: (row) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: row.name }) },
1409
+ {
1410
+ key: "environment",
1411
+ header: "Environment",
1412
+ render: (row) => row.tags && row.tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Badge, { variant: "info", size: "sm", children: row.tags[0].name }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Badge, { variant: "warning", size: "sm", children: "none" })
1413
+ },
1414
+ { key: "username", header: usernameLabel, render: (row) => row.username || "\u2014" },
1415
+ {
1416
+ key: "auth",
1417
+ header: "Auth",
1418
+ render: (row) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Badge, { variant: row.hasSecret ? "success" : "warning", size: "sm", children: [
1419
+ row.type === "TOKEN" ? "token" : "password",
1420
+ row.hasSecret ? "" : " \xB7 no secret"
1421
+ ] })
1422
+ },
1423
+ { key: "endpoint", header: "Endpoint", render: (row) => row.endpoint || "\u2014" },
1424
+ {
1425
+ key: "actions",
1426
+ header: "",
1427
+ align: "right",
1428
+ render: (row) => {
1429
+ const t = tests[row.id];
1430
+ const loading = !!t && "loading" in t;
1431
+ const result = t && !("loading" in t) ? t : null;
1432
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end", alignItems: "center", flexWrap: "wrap" }, children: [
1433
+ result ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1434
+ "span",
1435
+ {
1436
+ style: { display: "inline-flex", alignItems: "center", gap: 6, maxWidth: 340 },
1437
+ title: [result.message, ...result.details ?? []].filter(Boolean).join("\n"),
1438
+ children: [
1439
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Badge, { variant: result.ok ? "success" : "danger", size: "sm", children: result.ok ? "\u2713" : "\u2717" }),
1440
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1441
+ "span",
1442
+ {
1443
+ style: {
1444
+ fontSize: 12,
1445
+ color: result.ok ? "var(--color-success, #16a34a)" : "var(--color-danger, #dc2626)",
1446
+ overflow: "hidden",
1447
+ textOverflow: "ellipsis",
1448
+ whiteSpace: "nowrap"
1449
+ },
1450
+ children: [
1451
+ result.message,
1452
+ result.latencyMs != null ? ` \xB7 ${result.latencyMs} ms` : ""
1453
+ ]
1454
+ }
1455
+ )
1456
+ ]
1457
+ }
1458
+ ) : null,
1459
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { variant: "ghost", size: "sm", isLoading: loading, onClick: () => void handleTest(row), children: "Test" }),
1460
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { variant: "ghost", size: "sm", onClick: () => openEdit(row), children: "Edit" }),
1461
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { variant: "ghost", size: "sm", onClick: () => void handleDelete(row), children: "Remove" })
1462
+ ] });
1463
+ }
1464
+ }
1465
+ ];
1466
+ const isToken = form.authType === "token";
1467
+ const secretLabel = isToken ? tokenLabel : "Password";
1468
+ const usernameFieldLabel = isToken && usernameOptionalForToken ? `${usernameLabel} (optional)` : usernameLabel;
1469
+ const environmentOptions = [
1470
+ { value: "", label: environments.length ? "\u2014 Select environment \u2014" : "\u2014 No environments \u2014" },
1471
+ ...environments.map((e) => ({ value: e.id, label: e.name }))
1472
+ ];
1473
+ const selectedEnv = environments.find((e) => e.id === form.environmentId);
1474
+ const selectedEnvTypes = selectedEnv?.environmentTypes ?? [];
1475
+ const environmentTypeOptions = [
1476
+ { value: "", label: "Any type" },
1477
+ ...selectedEnvTypes.map((t) => ({ value: t, label: envTypeLabel(t) }))
1478
+ ];
1479
+ const environmentFilterOptions = environments.map((e) => ({ value: e.id, label: e.name }));
1480
+ const sortOptions = [
1481
+ { value: "name", label: "Name" },
1482
+ { value: "environment", label: "Environment" },
1483
+ { value: "username", label: usernameLabel }
1484
+ ];
1485
+ const filters = [
1486
+ {
1487
+ key: "environment",
1488
+ label: "Environment",
1489
+ options: environmentFilterOptions,
1490
+ value: environmentFilter,
1491
+ onChange: setEnvironmentFilter,
1492
+ alwaysVisible: true
1493
+ },
1494
+ {
1495
+ key: "authType",
1496
+ label: "Auth method",
1497
+ options: authTypes,
1498
+ value: authTypeFilter,
1499
+ onChange: setAuthTypeFilter
1500
+ }
1501
+ ];
1502
+ const filteredSorted = (0, import_react.useMemo)(() => {
1503
+ const term = search.trim().toLowerCase();
1504
+ const rows = connections.filter((row) => {
1505
+ if (term) {
1506
+ const haystack = `${row.name ?? ""} ${row.username ?? ""} ${row.endpoint ?? ""}`.toLowerCase();
1507
+ if (!haystack.includes(term)) return false;
1508
+ }
1509
+ if (environmentFilter && row.tags?.[0]?.id !== environmentFilter) return false;
1510
+ if (authTypeFilter && fromCredentialType(row.type) !== authTypeFilter) return false;
1511
+ return true;
1512
+ });
1513
+ const dir = sortDir === "asc" ? 1 : -1;
1514
+ return [...rows].sort((a, b) => {
1515
+ switch (sortField) {
1516
+ case "environment":
1517
+ return (a.tags?.[0]?.name ?? "").localeCompare(b.tags?.[0]?.name ?? "") * dir;
1518
+ case "username":
1519
+ return (a.username ?? "").localeCompare(b.username ?? "") * dir;
1520
+ case "name":
1521
+ default:
1522
+ return (a.name ?? "").localeCompare(b.name ?? "") * dir;
1523
+ }
1524
+ });
1525
+ }, [connections, search, environmentFilter, authTypeFilter, sortField, sortDir]);
1526
+ const pageRows = (0, import_react.useMemo)(
1527
+ () => filteredSorted.slice((page - 1) * pageSize, page * pageSize),
1528
+ [filteredSorted, page, pageSize]
1529
+ );
1530
+ (0, import_react.useEffect)(() => {
1531
+ setPage(1);
1532
+ }, [search, environmentFilter, authTypeFilter, sortField, sortDir]);
1533
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Card, { variant: "bordered", children: [
1534
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1535
+ CardHeader,
1536
+ {
1537
+ actions: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: 8 }, children: [
1538
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { variant: "secondary", size: "sm", onClick: () => void load(), isLoading, children: "Refresh" }),
1539
+ effectiveOnboarding ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { variant: "primary", size: "sm", onClick: openOnboard, children: effectiveOnboarding.label }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1540
+ Button,
1541
+ {
1542
+ variant: "secondary",
1543
+ size: "sm",
1544
+ disabled: true,
1545
+ title: "OAuth integration isn't available for this app \u2014 connect with a username/password or API token.",
1546
+ children: "Integrate via OAuth"
1547
+ }
1548
+ ),
1549
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { variant: effectiveOnboarding ? "secondary" : "primary", size: "sm", onClick: openCreate, children: "Add connection" })
1550
+ ] }),
1551
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { style: { margin: 0, fontSize: 16, fontWeight: 600 }, children: title })
1552
+ }
1553
+ ),
1554
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(CardBody, { children: [
1555
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { role: "status", style: { marginBottom: 12 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Badge, { variant: "success", size: "sm", children: notice }) }) : null,
1556
+ error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { role: "alert", children: [
1557
+ "Failed to load connections: ",
1558
+ error
1559
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1560
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1561
+ "div",
1562
+ {
1563
+ style: {
1564
+ display: "flex",
1565
+ flexWrap: "wrap",
1566
+ gap: 12,
1567
+ alignItems: "center",
1568
+ justifyContent: "space-between",
1569
+ marginBottom: 12
1570
+ },
1571
+ children: [
1572
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1573
+ FilterBar,
1574
+ {
1575
+ search: { value: search, onChange: setSearch, placeholder: "Search connections\u2026" },
1576
+ filters,
1577
+ onClearAll: () => {
1578
+ setSearch("");
1579
+ setEnvironmentFilter(null);
1580
+ setAuthTypeFilter(null);
1581
+ }
1582
+ }
1583
+ ),
1584
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1585
+ SortSelect,
1586
+ {
1587
+ options: sortOptions,
1588
+ value: sortField,
1589
+ direction: sortDir,
1590
+ onChange: (field, direction) => {
1591
+ setSortField(field);
1592
+ setSortDir(direction);
1593
+ }
1594
+ }
1595
+ )
1596
+ ]
1597
+ }
1598
+ ),
1599
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1600
+ DataTable,
1601
+ {
1602
+ columns,
1603
+ data: pageRows,
1604
+ rowKey: (row) => row.id,
1605
+ isLoading,
1606
+ emptyState: {
1607
+ title: "No connections yet",
1608
+ description: "Add the credentials this app authenticates with (username/password or a token) and the endpoint they reach."
1609
+ }
1610
+ }
1611
+ ),
1612
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginTop: 12 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1613
+ Pagination,
1614
+ {
1615
+ page,
1616
+ pageSize,
1617
+ totalItems: filteredSorted.length,
1618
+ onPageChange: setPage,
1619
+ onPageSizeChange: setPageSize,
1620
+ pageSizeOptions: [10, 25, 50]
1621
+ }
1622
+ ) })
1623
+ ] })
1624
+ ] }),
1625
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1626
+ FormDialog,
1627
+ {
1628
+ isOpen: dialogOpen,
1629
+ onClose: closeDialog,
1630
+ title: editing ? `Edit connection "${editing.name}"` : "Add connection",
1631
+ description: "Credentials this app authenticates with, plus the API endpoint they reach.",
1632
+ onSubmit: handleSubmit,
1633
+ submitText: editing ? "Save changes" : "Add connection",
1634
+ isSubmitting: submitting,
1635
+ submitDisabled: !form.name.trim() || !form.environmentId,
1636
+ error: formError,
1637
+ size: "md",
1638
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
1639
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1640
+ Input,
1641
+ {
1642
+ label: "Name",
1643
+ value: form.name,
1644
+ onChange: (e) => setField("name", e.target.value),
1645
+ placeholder: namePlaceholder,
1646
+ fullWidth: true,
1647
+ autoFocus: true,
1648
+ spellCheck: false,
1649
+ autoComplete: "off"
1650
+ }
1651
+ ),
1652
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1653
+ Select,
1654
+ {
1655
+ label: "Environment",
1656
+ options: environmentOptions,
1657
+ value: form.environmentId,
1658
+ onChange: (value) => setForm((prev) => ({ ...prev, environmentId: value, environmentType: "" })),
1659
+ helperText: "The deployment scope this connection belongs to. Manage environments under Environments.",
1660
+ fullWidth: true
1661
+ }
1662
+ ),
1663
+ selectedEnvTypes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1664
+ Select,
1665
+ {
1666
+ label: "Applies to type (optional)",
1667
+ options: environmentTypeOptions,
1668
+ value: form.environmentType,
1669
+ onChange: (value) => setField("environmentType", value),
1670
+ helperText: "Scope these credentials to a specific type within the environment. 'Any type' applies to all of its types.",
1671
+ fullWidth: true
1672
+ }
1673
+ ),
1674
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1675
+ Input,
1676
+ {
1677
+ label: "Endpoint (optional)",
1678
+ value: form.endpoint,
1679
+ onChange: (e) => setField("endpoint", e.target.value),
1680
+ placeholder: endpointPlaceholder,
1681
+ helperText: endpointHelper,
1682
+ fullWidth: true,
1683
+ spellCheck: false,
1684
+ autoComplete: "off"
1685
+ }
1686
+ ),
1687
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1688
+ Select,
1689
+ {
1690
+ label: "Auth method",
1691
+ options: authTypes,
1692
+ value: form.authType,
1693
+ onChange: (value) => setField("authType", value),
1694
+ fullWidth: true
1695
+ }
1696
+ ),
1697
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1698
+ Input,
1699
+ {
1700
+ label: usernameFieldLabel,
1701
+ value: form.username,
1702
+ onChange: (e) => setField("username", e.target.value),
1703
+ placeholder: isToken ? tokenUsernamePlaceholder : passwordUsernamePlaceholder,
1704
+ fullWidth: true,
1705
+ spellCheck: false,
1706
+ autoComplete: "off"
1707
+ }
1708
+ ),
1709
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1710
+ Input,
1711
+ {
1712
+ label: secretLabel,
1713
+ type: "password",
1714
+ value: form.secret,
1715
+ onChange: (e) => setField("secret", e.target.value),
1716
+ placeholder: editing ? "leave blank to keep current" : "",
1717
+ helperText: "Write-only \u2014 stored securely and never shown again.",
1718
+ fullWidth: true,
1719
+ autoComplete: "new-password"
1720
+ }
1721
+ )
1722
+ ] })
1723
+ }
1724
+ ),
1725
+ effectiveOnboarding ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1726
+ FormDialog,
1727
+ {
1728
+ isOpen: onboardOpen,
1729
+ onClose: () => {
1730
+ if (!onboardSubmitting) setOnboardOpen(false);
1731
+ },
1732
+ title: effectiveOnboarding.label,
1733
+ description: effectiveOnboarding.brokered ? "You'll approve this on your provider's sign-in page \u2014 no secret is entered or stored here." : "You'll authorize this on your provider's sign-in page.",
1734
+ onSubmit: handleStartOnboarding,
1735
+ submitText: "Continue to sign-in",
1736
+ isSubmitting: onboardSubmitting,
1737
+ submitDisabled: !onboardName.trim() || !onboardEnvironmentId,
1738
+ error: onboardError,
1739
+ size: "md",
1740
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
1741
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1742
+ Input,
1743
+ {
1744
+ label: "Name",
1745
+ value: onboardName,
1746
+ onChange: (e) => setOnboardName(e.target.value),
1747
+ placeholder: namePlaceholder,
1748
+ fullWidth: true,
1749
+ autoFocus: true,
1750
+ spellCheck: false,
1751
+ autoComplete: "off"
1752
+ }
1753
+ ),
1754
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1755
+ Select,
1756
+ {
1757
+ label: "Environment",
1758
+ options: environmentOptions,
1759
+ value: onboardEnvironmentId,
1760
+ onChange: (value) => setOnboardEnvironmentId(value),
1761
+ helperText: "The deployment scope this connection belongs to.",
1762
+ fullWidth: true
1763
+ }
1764
+ ),
1765
+ effectiveOnboarding.requiredSettings.map((key) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1766
+ Input,
1767
+ {
1768
+ label: key,
1769
+ value: onboardSettings[key] ?? "",
1770
+ onChange: (e) => setOnboardSettings((prev) => ({ ...prev, [key]: e.target.value })),
1771
+ fullWidth: true,
1772
+ spellCheck: false,
1773
+ autoComplete: "off"
1774
+ },
1775
+ key
1776
+ ))
1777
+ ] })
1778
+ }
1779
+ ) : null
1780
+ ] });
1781
+ };
1782
+ var connections_default = ConnectionsManager;
1783
+ // Annotate the CommonJS export names for ESM import in node:
1784
+ 0 && (module.exports = {
1785
+ ConnectionsManager
1786
+ });
1787
+ //# sourceMappingURL=index.cjs.map