@veltrixsecops/app-sdk 3.1.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +1 -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-6KUTYJW5.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 +127 -6
  17. package/dist/client/index.d.ts +127 -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 +7 -0
  26. package/dist/hooks/index.cjs.map +1 -1
  27. package/dist/hooks/index.d.cts +24 -3
  28. package/dist/hooks/index.d.ts +24 -3
  29. package/dist/hooks/index.js +7 -1
  30. package/dist/hooks/index.js.map +1 -1
  31. package/dist/index.cjs +7 -0
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +3 -3
  34. package/dist/index.d.ts +3 -3
  35. package/dist/index.js +3 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/opentofu/index.cjs +138 -0
  38. package/dist/opentofu/index.cjs.map +1 -0
  39. package/dist/opentofu/index.d.cts +131 -0
  40. package/dist/opentofu/index.d.ts +131 -0
  41. package/dist/opentofu/index.js +109 -0
  42. package/dist/opentofu/index.js.map +1 -0
  43. package/dist/pipeline/index.cjs +7 -0
  44. package/dist/pipeline/index.cjs.map +1 -1
  45. package/dist/pipeline/index.d.cts +9 -2
  46. package/dist/pipeline/index.d.ts +9 -2
  47. package/dist/pipeline/index.js +3 -1
  48. package/dist/pipeline-beeT8dje.d.cts +408 -0
  49. package/dist/pipeline-beeT8dje.d.ts +408 -0
  50. package/dist/ui/index.cjs +248 -0
  51. package/dist/ui/index.cjs.map +1 -1
  52. package/dist/ui/index.d.cts +238 -1
  53. package/dist/ui/index.d.ts +238 -1
  54. package/dist/ui/index.js +39 -573
  55. package/dist/ui/index.js.map +1 -1
  56. package/dist/{use-app-context-SEU1tyZd.d.cts → use-app-context-Byv_fam0.d.cts} +241 -4
  57. package/dist/{use-app-context-SEU1tyZd.d.ts → use-app-context-Byv_fam0.d.ts} +241 -4
  58. package/opentofu/README.md +114 -0
  59. package/opentofu/modules/aws/main.tf +1008 -0
  60. package/opentofu/modules/aws/outputs.tf +90 -0
  61. package/opentofu/modules/aws/variables.tf +437 -0
  62. package/opentofu/modules/aws/versions.tf +23 -0
  63. package/opentofu/modules/azure/main.tf +920 -0
  64. package/opentofu/modules/azure/outputs.tf +72 -0
  65. package/opentofu/modules/azure/variables.tf +498 -0
  66. package/opentofu/modules/azure/versions.tf +27 -0
  67. package/opentofu/modules/gcp/main.tf +645 -0
  68. package/opentofu/modules/gcp/outputs.tf +73 -0
  69. package/opentofu/modules/gcp/variables.tf +428 -0
  70. package/opentofu/modules/gcp/versions.tf +27 -0
  71. package/opentofu/modules/hetzner/main.tf +432 -0
  72. package/opentofu/modules/hetzner/outputs.tf +72 -0
  73. package/opentofu/modules/hetzner/variables.tf +423 -0
  74. package/opentofu/modules/hetzner/versions.tf +26 -0
  75. package/package.json +33 -1
  76. package/dist/chunk-6KUTYJW5.js.map +0 -1
  77. package/dist/chunk-VWFTOFTI.js.map +0 -1
  78. package/dist/pipeline-A-gSsPRR.d.cts +0 -189
  79. package/dist/pipeline-A-gSsPRR.d.ts +0 -189
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/connections/index.tsx","../../src/client/inventory.ts","../../src/client/environments.ts","../../src/client/credentials.ts","../../src/client/onboarding.ts","../../src/client/index.ts","../../src/ui/index.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from 'react'\nimport {\n listCredentials,\n createCredential,\n updateCredential,\n removeCredential,\n listEnvironments,\n resolveTool,\n testConnection,\n startOnboarding,\n listInventory,\n addInventoryItem,\n updateInventoryItem,\n fetchAppOnboarding,\n type CredentialSummary,\n type EnvironmentRef,\n type TestConnectionResult,\n} from '../client'\nimport {\n Card,\n CardHeader,\n CardBody,\n Button,\n Badge,\n Input,\n Select,\n FormDialog,\n DataTable,\n FilterBar,\n SortSelect,\n Pagination,\n useConfirmDialog,\n type DataTableColumn,\n type FilterDefinition,\n type SortOption,\n} from '../ui'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ConnectionsManagerProps {\n /**\n * Platform Tool name — MUST equal the app's manifest `name`. `resolveTool`\n * matches on it so new connections attach to this app's tool.\n */\n appName: string\n /** Manifest id — used to reach this app's connectivity-test route. */\n appId: string\n /** Card title. Defaults to \"Connections\". */\n title?: string\n /** Placeholder for the connection Name field. */\n namePlaceholder?: string\n /** Placeholder + helper for the Endpoint field. */\n endpointPlaceholder?: string\n endpointHelper?: string\n /**\n * Label for the account / key field (the credential's `username`). Defaults to\n * \"Username\"; pass e.g. \"Access key\" (Tenable) or \"Client ID\" (OAuth apps).\n */\n usernameLabel?: string\n /**\n * Label for the token/secret auth method + its secret field. Defaults to\n * \"API token\"; pass e.g. \"API key\" (Elastic), \"Client secret\" (OAuth),\n * \"Secret key\" (Tenable).\n */\n tokenLabel?: string\n /** Placeholder for the username field when token auth is selected. */\n tokenUsernamePlaceholder?: string\n /** Placeholder for the username field when password auth is selected. */\n passwordUsernamePlaceholder?: string\n /**\n * Whether the username field is optional under token auth (appends \"(optional)\"\n * to its label). Defaults to true — pass false for OAuth/key apps where the\n * username IS the required identifier (e.g. Client ID, Access key).\n */\n usernameOptionalForToken?: boolean\n /**\n * One-click onboarding descriptor (the client-safe subset the platform\n * advertises in the app's `/enabled` payload as `connection.onboarding`). When\n * provided, a primary \"Connect …\" button drives the platform onboarding flow\n * (e.g. Entra admin consent) — no secret is entered by the user. Omit it and\n * the manager renders exactly as before (manual \"Add connection\" only).\n */\n onboarding?: OnboardingDescriptorSummary\n /**\n * The platform component type this app deploys to (a config type's\n * `targets.componentTypes`, e.g. \"okta-org\"). When set, saving a connection\n * ALSO registers/updates a deploy-target Component — hostname taken from the\n * endpoint, linked to the new credential + environment — so the app can\n * actually deploy. A connection alone is only a credential, not a deploy\n * target, so without this Deploy stays disabled. Omit for apps that manage\n * their own components.\n */\n componentType?: string\n}\n\n/** Client-safe onboarding descriptor from the `/enabled` payload. */\nexport interface OnboardingDescriptorSummary {\n provider: string\n /** Button/label text, e.g. \"Connect Microsoft Defender\". */\n label: string\n /** True → brokered (no secret ever entered by the user). */\n brokered: boolean\n /** App settings the admin must supply before the consent redirect. */\n requiredSettings: string[]\n}\n\n/** Per-connection test state: in-flight, or the last result. */\ntype TestState = { loading: true } | TestConnectionResult\n\ninterface FormState {\n name: string\n environmentId: string\n environmentType: string\n authType: string\n username: string\n secret: string\n endpoint: string\n}\n\nconst BLANK_FORM: FormState = {\n name: '',\n environmentId: '',\n environmentType: '',\n authType: 'password',\n username: '',\n secret: '',\n endpoint: '',\n}\n\n/** Display labels for environment-type values (kept in sync with the platform env-type list). */\nconst ENV_TYPE_LABELS: Record<string, string> = {\n production: 'Production',\n 'pre-production': 'Pre-production',\n staging: 'Staging',\n qa: 'QA',\n uat: 'UAT',\n development: 'Development',\n testing: 'Testing',\n sandbox: 'Sandbox',\n dr: 'DR (Disaster Recovery)',\n demo: 'Demo',\n other: 'Other',\n}\nconst envTypeLabel = (t: string): string => ENV_TYPE_LABELS[t] ?? t\n\n// Map the auth choice to/from the platform credential `type`. Token secrets are\n// only encrypted at rest for 'API_KEY'/'TOKEN' — so token auth stores 'TOKEN'.\nfunction toCredentialType(authType: string): string {\n return authType === 'token' ? 'TOKEN' : 'PASSWORD'\n}\n\n/** Host portion of an endpoint URL (no scheme/path) — a component's hostname. */\nfunction hostFromEndpoint(endpoint: string): string {\n const trimmed = (endpoint || '').trim()\n if (!trimmed) return ''\n const withScheme = /^https?:\\/\\//i.test(trimmed) ? trimmed : `https://${trimmed}`\n try {\n return new URL(withScheme).host\n } catch {\n return trimmed.replace(/^https?:\\/\\//i, '').replace(/[/?#].*$/, '')\n }\n}\n\n/**\n * Port portion of an endpoint URL — an explicit port if present, else the\n * scheme default (443 for https, 80 for http). The platform's component create\n * schema requires `port`, so a deploy target always needs one.\n */\nfunction portFromEndpoint(endpoint: string): string {\n const trimmed = (endpoint || '').trim()\n const withScheme = /^https?:\\/\\//i.test(trimmed) ? trimmed : `https://${trimmed}`\n try {\n const url = new URL(withScheme)\n if (url.port) return url.port\n return url.protocol === 'http:' ? '80' : '443'\n } catch {\n return '443'\n }\n}\n\n/**\n * Ensure a deploy-target Component exists for a just-saved connection. A\n * connection is only a credential; deploying requires a Component whose `type`\n * includes the app's componentType (the deploy engine reads its hostname for the\n * target and resolves the linked credential). Idempotent per (componentType,\n * environment): updates the matching target's host/credential link, else creates\n * one. Requires an endpoint (its host becomes the component hostname); a blank\n * endpoint is a no-op. Callers treat any failure as non-fatal.\n */\nasync function ensureConnectionComponent(params: {\n componentType: string\n toolId?: string\n credentialId: string\n endpoint: string\n environmentId: string\n}): Promise<void> {\n const hostname = hostFromEndpoint(params.endpoint)\n if (!hostname) {\n throw new Error('An endpoint is required to register a deploy target for this connection.')\n }\n const items = await listInventory()\n const existing = items.find(\n (c) =>\n (c.type ?? []).includes(params.componentType) &&\n (params.environmentId ? (c.tags ?? []).some((t) => t.id === params.environmentId) : true),\n )\n const input = {\n hostname,\n port: portFromEndpoint(params.endpoint),\n type: [params.componentType],\n toolId: params.toolId,\n credentialId: params.credentialId,\n tagIds: params.environmentId ? [params.environmentId] : [],\n }\n if (existing) await updateInventoryItem(existing.id, input)\n else await addInventoryItem(input)\n}\nfunction fromCredentialType(type: string | null | undefined): string {\n return type === 'TOKEN' ? 'token' : 'password'\n}\n\n/**\n * Reusable Connections manager: full credential CRUD (username/password or\n * token, plus the API endpoint each reaches), tied to an Environment, with a\n * per-row \"Test connectivity\" button that runs the app's own testConnection\n * handler. Every app renders this with its own `appName`/`appId` and auth labels;\n * the data (credentials) stays platform-owned. Secrets are write-only — set here,\n * never read back.\n */\nexport const ConnectionsManager: React.FC<ConnectionsManagerProps> = ({\n appName,\n appId,\n title = 'Connections',\n namePlaceholder = 'e.g. Production API',\n endpointPlaceholder = 'e.g. https://api.example.com',\n endpointHelper = 'API base URL / endpoint this connection reaches.',\n usernameLabel = 'Username',\n tokenLabel = 'API token',\n tokenUsernamePlaceholder = 'service account (optional)',\n passwordUsernamePlaceholder = 'e.g. svc_veltrix',\n usernameOptionalForToken = true,\n onboarding,\n componentType,\n}) => {\n const { confirm } = useConfirmDialog()\n const authTypes = useMemo(\n () => [\n { value: 'password', label: 'Username & password' },\n { value: 'token', label: tokenLabel },\n ],\n [tokenLabel],\n )\n\n const [connections, setConnections] = useState<CredentialSummary[]>([])\n const [environments, setEnvironments] = useState<EnvironmentRef[]>([])\n const [error, setError] = useState<string | null>(null)\n const [isLoading, setIsLoading] = useState(true)\n\n const [search, setSearch] = useState('')\n const [environmentFilter, setEnvironmentFilter] = useState<string | null>(null)\n const [authTypeFilter, setAuthTypeFilter] = useState<string | null>(null)\n const [sortField, setSortField] = useState('name')\n const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc')\n const [page, setPage] = useState(1)\n const [pageSize, setPageSize] = useState(10)\n\n const [dialogOpen, setDialogOpen] = useState(false)\n const [editing, setEditing] = useState<CredentialSummary | null>(null)\n const [form, setForm] = useState<FormState>(BLANK_FORM)\n const [submitting, setSubmitting] = useState(false)\n const [formError, setFormError] = useState<string | null>(null)\n const [tests, setTests] = useState<Record<string, TestState>>({})\n\n // --- One-click onboarding (\"Connect …\") state ---\n const [onboardOpen, setOnboardOpen] = useState(false)\n const [onboardName, setOnboardName] = useState('')\n const [onboardEnvironmentId, setOnboardEnvironmentId] = useState('')\n const [onboardSettings, setOnboardSettings] = useState<Record<string, string>>({})\n const [onboardSubmitting, setOnboardSubmitting] = useState(false)\n const [onboardError, setOnboardError] = useState<string | null>(null)\n const [notice, setNotice] = useState<string | null>(null)\n // The OAuth \"Connect …\" option is offered on every app. The descriptor is the\n // `onboarding` prop when the page passes it, else self-fetched from\n // /apps/enabled. When absent, the option renders disabled (this provider is\n // credential-only — username/password or API token).\n const [fetchedOnboarding, setFetchedOnboarding] = useState<OnboardingDescriptorSummary | null>(null)\n const effectiveOnboarding = onboarding ?? fetchedOnboarding\n\n const load = useCallback(async () => {\n setIsLoading(true)\n setError(null)\n try {\n const tool = await resolveTool(appName)\n const [creds, envs] = await Promise.all([\n tool ? listCredentials(tool.id) : Promise.resolve([] as CredentialSummary[]),\n listEnvironments(),\n ])\n setConnections(creds)\n setEnvironments(envs)\n } catch (e) {\n setError((e as Error).message)\n } finally {\n setIsLoading(false)\n }\n }, [appName])\n\n useEffect(() => {\n void load()\n }, [load])\n\n // Self-resolve the onboarding descriptor when the page didn't pass one.\n useEffect(() => {\n if (onboarding) return\n let cancelled = false\n void fetchAppOnboarding(appId).then((d) => {\n if (cancelled || !d) return\n setFetchedOnboarding({\n provider: d.provider,\n label: d.label,\n brokered: d.brokered ?? false,\n requiredSettings: d.requiredSettings ?? [],\n })\n })\n return () => {\n cancelled = true\n }\n }, [appId, onboarding])\n\n // Surface the result of a completed onboarding redirect\n // (`…/connections?onboarded=ok|error`) and strip the params from the URL.\n useEffect(() => {\n if (typeof window === 'undefined') return\n const params = new URLSearchParams(window.location.search)\n const outcome = params.get('onboarded')\n if (!outcome) return\n if (outcome === 'ok') {\n setNotice(\n params.get('pending') === 'manual'\n ? 'Connection created. One manual step remains — see the connection to finish setup.'\n : 'Connection created successfully.',\n )\n } else {\n setError(`Onboarding failed: ${params.get('reason') || 'unknown error'}`)\n }\n params.delete('onboarded')\n params.delete('pending')\n params.delete('reason')\n params.delete('connectionId')\n const qs = params.toString()\n window.history.replaceState({}, '', `${window.location.pathname}${qs ? `?${qs}` : ''}`)\n }, [])\n\n const openOnboard = () => {\n setOnboardName('')\n setOnboardEnvironmentId('')\n setOnboardSettings({})\n setOnboardError(null)\n setOnboardOpen(true)\n }\n\n const handleStartOnboarding = async () => {\n if (!effectiveOnboarding) return\n const name = onboardName.trim()\n if (!name) {\n setOnboardError('Name is required')\n return\n }\n if (!onboardEnvironmentId) {\n setOnboardError('Environment is required')\n return\n }\n const missing = effectiveOnboarding.requiredSettings.filter((k) => !onboardSettings[k]?.trim())\n if (missing.length > 0) {\n setOnboardError(`Please provide: ${missing.join(', ')}`)\n return\n }\n setOnboardSubmitting(true)\n setOnboardError(null)\n try {\n const { authorizeUrl } = await startOnboarding(appId, {\n environmentId: onboardEnvironmentId,\n connectionName: name,\n settings: onboardSettings,\n })\n // Full-page navigation to the provider's hosted consent page. The\n // platform's static callback redirects back here with `?onboarded=…`.\n if (typeof window !== 'undefined') window.location.assign(authorizeUrl)\n } catch (e) {\n setOnboardError((e as Error).message)\n setOnboardSubmitting(false)\n }\n }\n\n const openCreate = () => {\n setEditing(null)\n setForm(BLANK_FORM)\n setFormError(null)\n setDialogOpen(true)\n }\n\n const openEdit = (row: CredentialSummary) => {\n setEditing(row)\n setForm({\n name: row.name ?? '',\n environmentId: row.tags?.[0]?.id ?? '',\n environmentType: row.environmentType ?? '',\n authType: fromCredentialType(row.type),\n username: row.username ?? '',\n secret: '',\n endpoint: row.endpoint ?? '',\n })\n setFormError(null)\n setDialogOpen(true)\n }\n\n const closeDialog = useCallback(() => {\n if (submitting) return\n setDialogOpen(false)\n }, [submitting])\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) =>\n setForm((prev) => ({ ...prev, [key]: value }))\n\n const handleSubmit = async () => {\n const name = form.name.trim()\n if (!name) {\n setFormError('Name is required')\n return\n }\n if (!form.environmentId) {\n setFormError('Environment is required')\n return\n }\n setSubmitting(true)\n setFormError(null)\n\n const secret = form.secret.trim()\n const username = form.username.trim()\n const endpoint = form.endpoint.trim()\n const tagIds = [form.environmentId]\n\n try {\n let credentialId: string\n let toolId: string | undefined\n if (editing) {\n const update: Record<string, unknown> = {\n name,\n username,\n type: toCredentialType(form.authType),\n endpoint,\n environmentType: form.environmentType || null,\n tagIds,\n }\n if (secret) {\n if (form.authType === 'token') update.apiToken = secret\n else update.password = secret\n }\n await updateCredential(editing.id, update)\n credentialId = editing.id\n } else {\n const tool = await resolveTool(appName)\n if (!tool) {\n throw new Error(\n `No \"${appName}\" tool found for your organization — make sure the app is installed before adding connections.`,\n )\n }\n toolId = tool.id\n const created = await createCredential({\n name,\n username,\n password: form.authType === 'password' ? secret : '',\n apiToken: form.authType === 'token' ? secret : undefined,\n type: toCredentialType(form.authType),\n endpoint,\n environmentType: form.environmentType || null,\n toolId: tool.id,\n tagIds,\n })\n credentialId = created.id\n }\n\n // Register/refresh the deploy target so the app can actually deploy to this\n // connection. Best-effort — a component failure must not lose the saved\n // credential, so surface it as a notice and let the user re-save to retry.\n if (componentType) {\n try {\n if (!toolId) toolId = (await resolveTool(appName))?.id\n await ensureConnectionComponent({\n componentType,\n toolId,\n credentialId,\n endpoint,\n environmentId: form.environmentId,\n })\n } catch (compErr) {\n setNotice(\n `Connection saved, but registering its deploy target failed: ${(compErr as Error).message} ` +\n 'Re-save the connection to retry.',\n )\n }\n }\n\n setDialogOpen(false)\n await load()\n } catch (e) {\n setFormError((e as Error).message)\n } finally {\n setSubmitting(false)\n }\n }\n\n const handleDelete = async (row: CredentialSummary) => {\n const ok = await confirm({\n title: 'Remove connection',\n message: `Remove the connection \"${row.name}\"? This cannot be undone.`,\n confirmText: 'Remove',\n cancelText: 'Cancel',\n variant: 'danger',\n })\n if (!ok) return\n try {\n await removeCredential(row.id)\n await load()\n } catch (e) {\n setError((e as Error).message)\n }\n }\n\n const handleTest = async (row: CredentialSummary) => {\n setTests((t) => ({ ...t, [row.id]: { loading: true } }))\n try {\n const result = await testConnection(appId, row.id)\n setTests((t) => ({ ...t, [row.id]: result }))\n } catch (e) {\n setTests((t) => ({ ...t, [row.id]: { ok: false, message: (e as Error).message } }))\n }\n }\n\n const columns: DataTableColumn<CredentialSummary>[] = [\n { key: 'name', header: 'Name', render: (row) => <strong>{row.name}</strong> },\n {\n key: 'environment',\n header: 'Environment',\n render: (row) =>\n row.tags && row.tags.length > 0 ? (\n <Badge variant=\"info\" size=\"sm\">\n {row.tags[0].name}\n </Badge>\n ) : (\n <Badge variant=\"warning\" size=\"sm\">\n none\n </Badge>\n ),\n },\n { key: 'username', header: usernameLabel, render: (row) => row.username || '—' },\n {\n key: 'auth',\n header: 'Auth',\n render: (row) => (\n <Badge variant={row.hasSecret ? 'success' : 'warning'} size=\"sm\">\n {row.type === 'TOKEN' ? 'token' : 'password'}\n {row.hasSecret ? '' : ' · no secret'}\n </Badge>\n ),\n },\n { key: 'endpoint', header: 'Endpoint', render: (row) => row.endpoint || '—' },\n {\n key: 'actions',\n header: '',\n align: 'right',\n render: (row) => {\n const t = tests[row.id]\n const loading = !!t && 'loading' in t\n const result = t && !('loading' in t) ? t : null\n return (\n <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', alignItems: 'center', flexWrap: 'wrap' }}>\n {result ? (\n <span\n style={{ display: 'inline-flex', alignItems: 'center', gap: 6, maxWidth: 340 }}\n title={[result.message, ...(result.details ?? [])].filter(Boolean).join('\\n')}\n >\n <Badge variant={result.ok ? 'success' : 'danger'} size=\"sm\">\n {result.ok ? '✓' : '✗'}\n </Badge>\n <span\n style={{\n fontSize: 12,\n color: result.ok ? 'var(--color-success, #16a34a)' : 'var(--color-danger, #dc2626)',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {result.message}\n {result.latencyMs != null ? ` · ${result.latencyMs} ms` : ''}\n </span>\n </span>\n ) : null}\n <Button variant=\"ghost\" size=\"sm\" isLoading={loading} onClick={() => void handleTest(row)}>\n Test\n </Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => openEdit(row)}>\n Edit\n </Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void handleDelete(row)}>\n Remove\n </Button>\n </div>\n )\n },\n },\n ]\n\n const isToken = form.authType === 'token'\n const secretLabel = isToken ? tokenLabel : 'Password'\n const usernameFieldLabel =\n isToken && usernameOptionalForToken ? `${usernameLabel} (optional)` : usernameLabel\n const environmentOptions = [\n { value: '', label: environments.length ? '— Select environment —' : '— No environments —' },\n ...environments.map((e) => ({ value: e.id, label: e.name })),\n ]\n\n // Dependent on the selected environment: the type(s) it's classified as. The\n // connection's creds can be scoped to one of them (\"Any type\" = all).\n const selectedEnv = environments.find((e) => e.id === form.environmentId)\n const selectedEnvTypes = selectedEnv?.environmentTypes ?? []\n const environmentTypeOptions = [\n { value: '', label: 'Any type' },\n ...selectedEnvTypes.map((t) => ({ value: t, label: envTypeLabel(t) })),\n ]\n\n const environmentFilterOptions = environments.map((e) => ({ value: e.id, label: e.name }))\n const sortOptions: SortOption[] = [\n { value: 'name', label: 'Name' },\n { value: 'environment', label: 'Environment' },\n { value: 'username', label: usernameLabel },\n ]\n const filters: FilterDefinition[] = [\n {\n key: 'environment',\n label: 'Environment',\n options: environmentFilterOptions,\n value: environmentFilter,\n onChange: setEnvironmentFilter,\n alwaysVisible: true,\n },\n {\n key: 'authType',\n label: 'Auth method',\n options: authTypes,\n value: authTypeFilter,\n onChange: setAuthTypeFilter,\n },\n ]\n\n const filteredSorted = useMemo(() => {\n const term = search.trim().toLowerCase()\n const rows = connections.filter((row) => {\n if (term) {\n const haystack = `${row.name ?? ''} ${row.username ?? ''} ${row.endpoint ?? ''}`.toLowerCase()\n if (!haystack.includes(term)) return false\n }\n if (environmentFilter && row.tags?.[0]?.id !== environmentFilter) return false\n if (authTypeFilter && fromCredentialType(row.type) !== authTypeFilter) return false\n return true\n })\n const dir = sortDir === 'asc' ? 1 : -1\n return [...rows].sort((a, b) => {\n switch (sortField) {\n case 'environment':\n return (a.tags?.[0]?.name ?? '').localeCompare(b.tags?.[0]?.name ?? '') * dir\n case 'username':\n return (a.username ?? '').localeCompare(b.username ?? '') * dir\n case 'name':\n default:\n return (a.name ?? '').localeCompare(b.name ?? '') * dir\n }\n })\n }, [connections, search, environmentFilter, authTypeFilter, sortField, sortDir])\n\n const pageRows = useMemo(\n () => filteredSorted.slice((page - 1) * pageSize, page * pageSize),\n [filteredSorted, page, pageSize],\n )\n\n useEffect(() => {\n setPage(1)\n }, [search, environmentFilter, authTypeFilter, sortField, sortDir])\n\n return (\n <Card variant=\"bordered\">\n <CardHeader\n actions={\n <div style={{ display: 'flex', gap: 8 }}>\n <Button variant=\"secondary\" size=\"sm\" onClick={() => void load()} isLoading={isLoading}>\n Refresh\n </Button>\n {effectiveOnboarding ? (\n <Button variant=\"primary\" size=\"sm\" onClick={openOnboard}>\n {effectiveOnboarding.label}\n </Button>\n ) : (\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled\n title=\"OAuth integration isn't available for this app — connect with a username/password or API token.\"\n >\n Integrate via OAuth\n </Button>\n )}\n <Button variant={effectiveOnboarding ? 'secondary' : 'primary'} size=\"sm\" onClick={openCreate}>\n Add connection\n </Button>\n </div>\n }\n >\n <h2 style={{ margin: 0, fontSize: 16, fontWeight: 600 }}>{title}</h2>\n </CardHeader>\n <CardBody>\n {notice ? (\n <div role=\"status\" style={{ marginBottom: 12 }}>\n <Badge variant=\"success\" size=\"sm\">\n {notice}\n </Badge>\n </div>\n ) : null}\n {error ? (\n <p role=\"alert\">Failed to load connections: {error}</p>\n ) : (\n <>\n <div\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: 12,\n alignItems: 'center',\n justifyContent: 'space-between',\n marginBottom: 12,\n }}\n >\n <FilterBar\n search={{ value: search, onChange: setSearch, placeholder: 'Search connections…' }}\n filters={filters}\n onClearAll={() => {\n setSearch('')\n setEnvironmentFilter(null)\n setAuthTypeFilter(null)\n }}\n />\n <SortSelect\n options={sortOptions}\n value={sortField}\n direction={sortDir}\n onChange={(field, direction) => {\n setSortField(field)\n setSortDir(direction)\n }}\n />\n </div>\n <DataTable\n columns={columns}\n data={pageRows}\n rowKey={(row) => row.id}\n isLoading={isLoading}\n emptyState={{\n title: 'No connections yet',\n description:\n 'Add the credentials this app authenticates with (username/password or a token) and the endpoint they reach.',\n }}\n />\n <div style={{ marginTop: 12 }}>\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={filteredSorted.length}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n pageSizeOptions={[10, 25, 50]}\n />\n </div>\n </>\n )}\n </CardBody>\n\n <FormDialog\n isOpen={dialogOpen}\n onClose={closeDialog}\n title={editing ? `Edit connection \"${editing.name}\"` : 'Add connection'}\n description=\"Credentials this app authenticates with, plus the API endpoint they reach.\"\n onSubmit={handleSubmit}\n submitText={editing ? 'Save changes' : 'Add connection'}\n isSubmitting={submitting}\n submitDisabled={!form.name.trim() || !form.environmentId}\n error={formError}\n size=\"md\"\n >\n <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>\n <Input\n label=\"Name\"\n value={form.name}\n onChange={(e) => setField('name', e.target.value)}\n placeholder={namePlaceholder}\n fullWidth\n autoFocus\n spellCheck={false}\n autoComplete=\"off\"\n />\n <Select\n label=\"Environment\"\n options={environmentOptions}\n value={form.environmentId}\n onChange={(value) => setForm((prev) => ({ ...prev, environmentId: value, environmentType: '' }))}\n helperText=\"The deployment scope this connection belongs to. Manage environments under Environments.\"\n fullWidth\n />\n {selectedEnvTypes.length > 0 && (\n <Select\n label=\"Applies to type (optional)\"\n options={environmentTypeOptions}\n value={form.environmentType}\n onChange={(value) => setField('environmentType', value)}\n helperText=\"Scope these credentials to a specific type within the environment. 'Any type' applies to all of its types.\"\n fullWidth\n />\n )}\n <Input\n label=\"Endpoint (optional)\"\n value={form.endpoint}\n onChange={(e) => setField('endpoint', e.target.value)}\n placeholder={endpointPlaceholder}\n helperText={endpointHelper}\n fullWidth\n spellCheck={false}\n autoComplete=\"off\"\n />\n <Select\n label=\"Auth method\"\n options={authTypes}\n value={form.authType}\n onChange={(value) => setField('authType', value)}\n fullWidth\n />\n <Input\n label={usernameFieldLabel}\n value={form.username}\n onChange={(e) => setField('username', e.target.value)}\n placeholder={isToken ? tokenUsernamePlaceholder : passwordUsernamePlaceholder}\n fullWidth\n spellCheck={false}\n autoComplete=\"off\"\n />\n <Input\n label={secretLabel}\n type=\"password\"\n value={form.secret}\n onChange={(e) => setField('secret', e.target.value)}\n placeholder={editing ? 'leave blank to keep current' : ''}\n helperText=\"Write-only — stored securely and never shown again.\"\n fullWidth\n autoComplete=\"new-password\"\n />\n </div>\n </FormDialog>\n\n {effectiveOnboarding ? (\n <FormDialog\n isOpen={onboardOpen}\n onClose={() => {\n if (!onboardSubmitting) setOnboardOpen(false)\n }}\n title={effectiveOnboarding.label}\n description={\n effectiveOnboarding.brokered\n ? \"You'll approve this on your provider's sign-in page — no secret is entered or stored here.\"\n : \"You'll authorize this on your provider's sign-in page.\"\n }\n onSubmit={handleStartOnboarding}\n submitText=\"Continue to sign-in\"\n isSubmitting={onboardSubmitting}\n submitDisabled={!onboardName.trim() || !onboardEnvironmentId}\n error={onboardError}\n size=\"md\"\n >\n <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>\n <Input\n label=\"Name\"\n value={onboardName}\n onChange={(e) => setOnboardName(e.target.value)}\n placeholder={namePlaceholder}\n fullWidth\n autoFocus\n spellCheck={false}\n autoComplete=\"off\"\n />\n <Select\n label=\"Environment\"\n options={environmentOptions}\n value={onboardEnvironmentId}\n onChange={(value) => setOnboardEnvironmentId(value)}\n helperText=\"The deployment scope this connection belongs to.\"\n fullWidth\n />\n {effectiveOnboarding.requiredSettings.map((key) => (\n <Input\n key={key}\n label={key}\n value={onboardSettings[key] ?? ''}\n onChange={(e) =>\n setOnboardSettings((prev) => ({ ...prev, [key]: e.target.value }))\n }\n fullWidth\n spellCheck={false}\n autoComplete=\"off\"\n />\n ))}\n </div>\n </FormDialog>\n ) : null}\n </Card>\n )\n}\n\nexport default ConnectionsManager\n","// ========================================================================\n// Inventory — the deployment targets an app can deploy configuration to.\n//\n// \"Inventory\" is the app-facing name for the platform's *components*: the\n// servers (hostname/port), domains, and IP/CIDR ranges a customer has\n// registered as deploy targets. These helpers are a typed, convenient\n// surface over the platform's components API (/api/components), enriched\n// with `domains` and `ipRanges`.\n//\n// Framework-free (no React) — safe to import from any client code. Every\n// call goes through the same `authFetch` the '/client' subpath exports, so\n// requests carry the platform's Authorization header. Non-2xx responses are\n// surfaced as thrown Errors carrying the platform's error text.\n// ========================================================================\n\nimport type { InventoryItem, InventoryItemInput } from '../types/platform'\nimport { authFetch } from './index'\n\n/** Base route for the platform's components (inventory) API. */\nconst INVENTORY_API = '/api/components'\n\n/**\n * Loosely-typed shape of a raw component as returned by the platform, before\n * it is normalized down to the {@link InventoryItem} surface.\n */\ninterface RawInventoryItem {\n id: string\n hostname?: string\n port?: string\n webPort?: string | null\n sshUser?: string | null\n splunkHome?: string | null\n type?: string[]\n domains?: string[]\n ipRanges?: string[]\n tags?: Array<{ id: string; name: string }>\n connectivityProviderId?: string | null\n credentialId?: string | null\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function inventoryError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/** Normalize a raw platform component into the typed InventoryItem surface. */\nfunction toInventoryItem(raw: RawInventoryItem): InventoryItem {\n return {\n id: String(raw.id),\n hostname: raw.hostname ?? '',\n port: raw.port ?? undefined,\n webPort: raw.webPort ?? null,\n sshUser: raw.sshUser ?? null,\n splunkHome: raw.splunkHome ?? null,\n type: Array.isArray(raw.type) ? raw.type : undefined,\n domains: Array.isArray(raw.domains) ? raw.domains : [],\n ipRanges: Array.isArray(raw.ipRanges) ? raw.ipRanges : [],\n tags: Array.isArray(raw.tags)\n ? raw.tags.map((tag) => ({ id: String(tag.id), name: String(tag.name) }))\n : [],\n connectivityProviderId: raw.connectivityProviderId ?? null,\n credentialId: raw.credentialId ?? null,\n }\n}\n\n/**\n * A platform Tool. Each installed app is upserted as a Tool keyed by its\n * manifest `name`, and inventory items (components) belong to a tool.\n */\nexport interface Tool {\n id: string\n name: string\n vendor?: string\n}\n\n/**\n * Resolve the platform Tool for an app by its manifest name (the platform\n * upserts `Tool.name === app name`). The tool id is required by the platform\n * when creating an inventory item, so call this once and pass the id as\n * `toolId` to {@link addInventoryItem}. Returns null when no tool matches.\n *\n * GET /api/tools (paginated — `{ data, pagination }` — or a bare array; both are\n * handled). We MUST filter server-side by name: the list is paginated (default 20,\n * ordered by createdAt) so once a tenant has enough apps installed, a bare\n * client-side find over just the first page silently misses a real tool whose row\n * fell to a later page — surfacing a false \"no <app> tool found, install the app\n * first\" error for an app that is in fact installed. `search` is a case-insensitive\n * contains match, so we still exact-match `tool.name === name` on the result.\n */\nexport async function resolveTool(name: string): Promise<Tool | null> {\n const res = await authFetch(`/api/tools?search=${encodeURIComponent(name)}&limit=100`)\n if (!res.ok) throw await inventoryError(res)\n const body = (await res.json()) as unknown\n const tools: Tool[] = Array.isArray(body)\n ? (body as Tool[])\n : Array.isArray((body as { data?: unknown })?.data)\n ? ((body as { data: Tool[] }).data)\n : []\n return tools.find((tool) => tool.name === name) ?? null\n}\n\n/** List the customer's inventory (deployment targets). GET /api/components */\nexport async function listInventory(): Promise<InventoryItem[]> {\n const res = await authFetch(INVENTORY_API)\n if (!res.ok) throw await inventoryError(res)\n const data = (await res.json()) as RawInventoryItem[]\n return Array.isArray(data) ? data.map(toInventoryItem) : []\n}\n\n/** Add a new inventory item (deployment target). POST /api/components */\nexport async function addInventoryItem(input: InventoryItemInput): Promise<InventoryItem> {\n const res = await authFetch(INVENTORY_API, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n if (!res.ok) throw await inventoryError(res)\n return toInventoryItem((await res.json()) as RawInventoryItem)\n}\n\n/** Update an existing inventory item. PUT /api/components/:id */\nexport async function updateInventoryItem(\n id: string,\n input: InventoryItemInput,\n): Promise<InventoryItem> {\n const res = await authFetch(`${INVENTORY_API}/${encodeURIComponent(id)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n })\n if (!res.ok) throw await inventoryError(res)\n return toInventoryItem((await res.json()) as RawInventoryItem)\n}\n\n/** Remove an inventory item. DELETE /api/components/:id */\nexport async function removeInventoryItem(id: string): Promise<void> {\n const res = await authFetch(`${INVENTORY_API}/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n })\n // 204 No Content is the platform's success response for delete.\n if (!res.ok && res.status !== 204) throw await inventoryError(res)\n}\n","// ========================================================================\n// Environments — the deployment scopes that determine where a configuration is\n// pushed. Environments are the customer's platform tags (dev/staging/prod, …);\n// a Connection (credential) or Access Server (component) is tied to one by\n// carrying the environment id in its `tagIds`.\n//\n// A thin reader over the platform's environments API (/api/environments), used\n// to populate the Environment picker on the Connections and Access Servers\n// pages. Framework-free — every call goes through the same `authFetch` the\n// '/client' subpath exports.\n// ========================================================================\n\nimport type { EnvironmentRef } from '../types/pipeline'\nimport { authFetch } from './index'\n\n/** Base route for the platform's environments API. */\nconst ENVIRONMENTS_API = '/api/environments'\n\ninterface RawEnvironment {\n id: string\n name?: string\n environmentTypes?: string[]\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function environmentError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/**\n * List the customer's environments (deployment scopes). GET /api/environments\n * (the endpoint may return a bare array or a paginated `{ data, ... }` shape —\n * both are handled). Use the returned id as a `tagIds` entry to tie a Connection\n * or Access Server to that environment.\n */\nexport async function listEnvironments(): Promise<EnvironmentRef[]> {\n const res = await authFetch(ENVIRONMENTS_API)\n if (!res.ok) throw await environmentError(res)\n const body = (await res.json()) as unknown\n const rows: RawEnvironment[] = Array.isArray(body)\n ? (body as RawEnvironment[])\n : Array.isArray((body as { data?: unknown })?.data)\n ? ((body as { data: RawEnvironment[] }).data)\n : []\n return rows.map((env) => ({\n id: String(env.id),\n name: env.name ?? '',\n environmentTypes: Array.isArray(env.environmentTypes) ? env.environmentTypes : [],\n }))\n}\n","// ========================================================================\n// Credentials — how an app authenticates to a server (\"connection\").\n//\n// A \"connection\" pairs a server (a platform *component* — see inventory.ts)\n// with a *credential*: the account and write-only secret used to reach that\n// server. These helpers are a typed surface over the platform's credentials\n// API (POST /api/credentials, GET /api/tools/:toolId/credentials, PUT/DELETE\n// /api/credentials/:id).\n//\n// Framework-free (no React). Every call goes through the same `authFetch` the\n// '/client' subpath exports, so requests carry the platform's Authorization\n// header. Non-2xx responses are surfaced as thrown Errors carrying the\n// platform's error text.\n//\n// SECURITY: `listCredentials` returns a REDACTED {@link CredentialSummary} —\n// secret material (password / apiToken / certificate) is dropped before it\n// reaches app code, so secrets are never held in memory or logged. Only whether\n// a secret exists is surfaced (`hasSecret`). Secrets are write-only: they can be\n// set via create/update, never read back.\n// ========================================================================\n\nimport type { CredentialInput, CredentialSummary } from '../types/platform'\nimport type { TestConnectionResult } from '../types/pipeline'\nimport { authFetch } from './index'\n\n/** Base route for the platform's credentials API. */\nconst CREDENTIALS_API = '/api/credentials'\n\n/**\n * Loosely-typed shape of a raw credential as returned by the platform, before\n * it is redacted down to the {@link CredentialSummary} surface. The secret\n * fields (`password` / `apiToken` / `certificate`) are read here only to derive\n * `hasSecret` — they are never carried into app-visible data.\n */\ninterface RawCredential {\n id: string\n name?: string\n username?: string\n type?: string | null\n endpoint?: string | null\n environmentType?: string | null\n toolId?: string\n // The platform redacts secrets from credential responses and surfaces only\n // whether each is set via these flags. Older platforms may still send the\n // secret fields instead — both shapes are handled below.\n hasPassword?: boolean\n hasApiToken?: boolean\n hasCertificate?: boolean\n password?: string | null\n apiToken?: string | null\n certificate?: string | null\n tags?: Array<{ id: string; name: string }>\n}\n\n/** Build an Error from a non-2xx response, preferring the platform's message. */\nasync function credentialError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n // Body was not JSON — fall through and use the raw text.\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/**\n * Redact a raw platform credential down to the app-visible summary, dropping\n * every secret field and surfacing only whether a secret is stored.\n */\nfunction toCredentialSummary(raw: RawCredential): CredentialSummary {\n // Prefer the redacted has* flags; fall back to the presence of the secret\n // fields themselves for older platforms that still return them.\n const hasSecret = Boolean(\n raw.hasApiToken ||\n raw.hasPassword ||\n (raw.apiToken && raw.apiToken.length > 0) ||\n (raw.password && raw.password.length > 0),\n )\n return {\n id: String(raw.id),\n name: raw.name ?? '',\n username: raw.username ?? '',\n type: raw.type ?? null,\n endpoint: raw.endpoint ?? null,\n environmentType: raw.environmentType ?? null,\n toolId: raw.toolId ?? '',\n hasSecret,\n tags: Array.isArray(raw.tags)\n ? raw.tags.map((t) => ({ id: String(t.id), name: String(t.name) }))\n : [],\n }\n}\n\n/**\n * List the redacted credentials registered for a tool. GET\n * /api/tools/:toolId/credentials. Secrets are stripped before return — see the\n * module's SECURITY note. Returns an empty array when the tool has none.\n */\nexport async function listCredentials(toolId: string): Promise<CredentialSummary[]> {\n const res = await authFetch(`/api/tools/${encodeURIComponent(toolId)}/credentials`)\n if (!res.ok) throw await credentialError(res)\n const data = (await res.json()) as unknown\n const rows: RawCredential[] = Array.isArray(data)\n ? (data as RawCredential[])\n : Array.isArray((data as { data?: unknown })?.data)\n ? ((data as { data: RawCredential[] }).data)\n : []\n return rows.map(toCredentialSummary)\n}\n\n/**\n * Create a credential. POST /api/credentials. The platform requires `name`,\n * `username`, `password`, `toolId`, and `tagIds` — this helper defaults\n * `tagIds` to `[]` and `password` to `''` (valid for token-only auth, where the\n * secret travels in `apiToken`). Returns the new credential's id.\n */\nexport async function createCredential(input: CredentialInput): Promise<{ id: string }> {\n const res = await authFetch(CREDENTIALS_API, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n name: input.name,\n username: input.username,\n password: input.password ?? '',\n apiToken: input.apiToken,\n type: input.type,\n endpoint: input.endpoint,\n environmentType: input.environmentType ?? null,\n toolId: input.toolId,\n tagIds: input.tagIds ?? [],\n }),\n })\n if (!res.ok) throw await credentialError(res)\n const body = (await res.json()) as { id?: string }\n return { id: String(body.id) }\n}\n\n/**\n * Update a credential. PUT /api/credentials/:id. Only the fields you pass are\n * changed; omit `password`/`apiToken` to leave the stored secret untouched.\n */\nexport async function updateCredential(\n id: string,\n input: Partial<CredentialInput>,\n): Promise<{ id: string }> {\n const body: Record<string, unknown> = {}\n if (input.name !== undefined) body.name = input.name\n if (input.username !== undefined) body.username = input.username\n if (input.password !== undefined) body.password = input.password\n if (input.apiToken !== undefined) body.apiToken = input.apiToken\n if (input.type !== undefined) body.type = input.type\n if (input.endpoint !== undefined) body.endpoint = input.endpoint\n if (input.environmentType !== undefined) body.environmentType = input.environmentType\n if (input.tagIds !== undefined) body.tagIds = input.tagIds\n const res = await authFetch(`${CREDENTIALS_API}/${encodeURIComponent(id)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (!res.ok) throw await credentialError(res)\n const result = (await res.json().catch(() => ({}))) as { id?: string }\n return { id: result.id ? String(result.id) : id }\n}\n\n/** Remove a credential. DELETE /api/credentials/:id. */\nexport async function removeCredential(id: string): Promise<void> {\n const res = await authFetch(`${CREDENTIALS_API}/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n })\n // 204 No Content is the platform's success response for delete.\n if (!res.ok && res.status !== 204) throw await credentialError(res)\n}\n\nexport type { TestConnectionResult }\n\n/**\n * Test a Connection's endpoint + credential. The platform decrypts the secret,\n * runs the owning app's connectivity-test handler in-process (the secret is\n * never returned), and reports whether the endpoint + credentials actually work.\n * A failed test resolves normally with `{ ok: false, message }` — it does not\n * throw. Only transport/auth errors (401/404) surface as `{ ok: false }` too.\n *\n * @param appId the app that owns the connection, e.g. `splunk-cloud`\n * @param credentialId the connection (credential) id\n * @param opts.endpoint optional endpoint to test against instead of the one stored\n * on the credential — e.g. an Access Server's reachable address\n * (`<host>:<port>`). The host is scoped to the caller's tenant.\n */\nexport async function testConnection(\n appId: string,\n credentialId: string,\n opts: { endpoint?: string } = {},\n): Promise<TestConnectionResult> {\n const endpoint = typeof opts.endpoint === 'string' ? opts.endpoint.trim() : ''\n const res = await authFetch(\n `/api/apps/${encodeURIComponent(appId)}/connections/${encodeURIComponent(credentialId)}/test`,\n endpoint\n ? { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ endpoint }) }\n : { method: 'POST' },\n )\n if (!res.ok) {\n const err = await credentialError(res)\n return { ok: false, message: err.message }\n }\n return (await res.json()) as TestConnectionResult\n}\n\n/** Result of running an app operation. Mirrors the server operation handler's return. */\nexport interface OperationResult {\n ok: boolean\n message: string\n details?: string[]\n /** Optional structured payload (e.g. an export download URL, a job id). */\n data?: Record<string, unknown>\n}\n\n/**\n * Run an app operation — a one-off action (restart, export, retry, …), NOT a\n * configuration deploy. The platform decrypts the chosen connection's secret and\n * runs the app's operation handler in-process (the secret is never returned). A\n * failed operation resolves normally with `{ ok: false, message }` rather than\n * throwing.\n *\n * @param appId the app that owns the operation, e.g. `splunk-cloud`\n * @param operationId the operation id declared in the app manifest\n * @param opts the connection (credentialId) to authenticate with, + params\n */\nexport async function runOperation(\n appId: string,\n operationId: string,\n opts: { credentialId?: string; params?: Record<string, unknown> } = {},\n): Promise<OperationResult> {\n const res = await authFetch(\n `/api/apps/${encodeURIComponent(appId)}/operations/${encodeURIComponent(operationId)}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ credentialId: opts.credentialId, params: opts.params ?? {} }),\n },\n )\n if (!res.ok) {\n const err = await credentialError(res)\n return { ok: false, message: err.message }\n }\n return (await res.json()) as OperationResult\n}\n","// ========================================================================\n// Connection onboarding — one-click \"Connect …\" flow (consent + broker).\n//\n// Typed surface over the platform's generic onboarding routes\n// (POST /api/apps/:appId/connections/onboard/start, …/onboard/status,\n// …/onboard/revoke). Framework-free; every call carries the platform's\n// Authorization header via the shared `authFetch`.\n//\n// The flow: call `startOnboarding` → open the returned `authorizeUrl` (the\n// admin consents on Microsoft's hosted page) → the platform's static callback\n// persists the connection and redirects back to the Connections page with an\n// `?onboarded=ok` flag. No secret is ever handled by app code.\n// ========================================================================\n\nimport { authFetch } from './index'\n\nasync function onboardingError(res: Response): Promise<Error> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const body = JSON.parse(text) as { error?: string; message?: string }\n const message = body?.error ?? body?.message\n if (message) return new Error(message)\n } catch {\n /* not JSON */\n }\n return new Error(text)\n }\n return new Error(`HTTP ${res.status}`)\n}\n\n/** The client-safe onboarding descriptor an app advertises in /apps/enabled. */\nexport interface AppOnboardingDescriptor {\n provider: string\n label: string\n brokered?: boolean\n requiredSettings?: string[]\n}\n\n/**\n * Fetch an app's connection-onboarding descriptor from GET /api/apps/enabled,\n * so the Connections UI can offer an \"Integrate via OAuth\" option without the\n * app page having to pass it in. Returns null when the app declares no\n * onboarding (its connections stay credential-only).\n */\nexport async function fetchAppOnboarding(appId: string): Promise<AppOnboardingDescriptor | null> {\n try {\n const res = await authFetch('/api/apps/enabled', { method: 'GET' })\n if (!res.ok) return null\n const data = (await res.json()) as unknown\n const list = Array.isArray(data) ? data : ((data as { data?: unknown[] })?.data ?? [])\n const app = (list as Array<{ appId?: string; id?: string; connection?: { onboarding?: AppOnboardingDescriptor } }>).find(\n (a) => a.appId === appId || a.id === appId,\n )\n return app?.connection?.onboarding ?? null\n } catch {\n return null\n }\n}\n\nexport interface StartOnboardingInput {\n /** The deployment scope (environment) the new connection belongs to. */\n environmentId: string\n /** Display name for the new connection. */\n connectionName: string\n /**\n * Settings the admin supplies BEFORE consent (e.g. Sentinel's\n * subscription_id / resource_group / workspace_name, or a cloud override).\n */\n settings?: Record<string, unknown>\n}\n\nexport interface StartOnboardingResult {\n /** Open this in the browser — the provider's hosted consent page. */\n authorizeUrl: string\n provider: string\n}\n\n/** Begin onboarding a connection. POST /api/apps/:appId/connections/onboard/start. */\nexport async function startOnboarding(\n appId: string,\n input: StartOnboardingInput,\n): Promise<StartOnboardingResult> {\n const res = await authFetch(\n `/api/apps/${encodeURIComponent(appId)}/connections/onboard/start`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n environmentId: input.environmentId,\n connectionName: input.connectionName,\n settings: input.settings ?? {},\n }),\n },\n )\n if (!res.ok) throw await onboardingError(res)\n return (await res.json()) as StartOnboardingResult\n}\n\nexport interface OnboardingStatus {\n provider: string\n brokered: boolean\n tenantId: string | null\n cloud: string\n manualSteps: Array<{\n type: string\n title: string\n description: string\n deepLink?: string\n cliCommand?: string\n verifiable: boolean\n }>\n verify?: { effective: boolean; message: string }\n}\n\n/**\n * Read onboarding status for a connection, optionally running the verify probe\n * for pending manual steps (e.g. the Sentinel role assignment).\n * GET /api/apps/:appId/connections/:credentialId/onboard/status.\n */\nexport async function getOnboardingStatus(\n appId: string,\n credentialId: string,\n opts: { verify?: boolean } = {},\n): Promise<OnboardingStatus> {\n const qs = opts.verify ? '?verify=1' : ''\n const res = await authFetch(\n `/api/apps/${encodeURIComponent(appId)}/connections/${encodeURIComponent(credentialId)}/onboard/status${qs}`,\n )\n if (!res.ok) throw await onboardingError(res)\n return (await res.json()) as OnboardingStatus\n}\n\n/**\n * Disconnect an onboarded connection (best-effort tenant-side teardown, then\n * delete). POST /api/apps/:appId/connections/:credentialId/onboard/revoke.\n */\nexport async function revokeOnboarding(\n appId: string,\n credentialId: string,\n): Promise<{ cleaned: boolean; message: string }> {\n const res = await authFetch(\n `/api/apps/${encodeURIComponent(appId)}/connections/${encodeURIComponent(credentialId)}/onboard/revoke`,\n { method: 'POST' },\n )\n if (!res.ok) throw await onboardingError(res)\n return (await res.json()) as { cleaned: boolean; message: string }\n}\n","// ========================================================================\n// Client runtime contract — how app client bundles talk to the host.\n//\n// App client code is packaged as a hermetic ESM bundle in which `react`,\n// `react-dom`, `react/jsx-runtime`, and every `@veltrixsecops/app-sdk`\n// subpath are compile-time shims that read the host-provided runtime from\n// `globalThis.__VELTRIX_APP_RUNTIME__`. The platform installs that global\n// (with ITS React instance, the shared AppContext, and an authenticated\n// fetch) before dynamically importing any app bundle, so app components\n// render inside the host React tree with working hooks and context.\n//\n// App authors: import from '@veltrixsecops/app-sdk/client' (and /hooks) —\n// never bundle your own copy of react. Use `authFetch` for calls to your\n// app's server routes (/api/apps/<app-id>/...): plain fetch() lacks the\n// platform's Authorization header and will receive 401s.\n// ========================================================================\n\nimport type { ComponentType, Context, LazyExoticComponent } from 'react'\nimport type { AppContextValue } from '../hooks/use-app-context'\nimport type { AppPermissionsApi } from '../types/platform'\n\nexport type { AppBrandingDeclaration } from '../types/manifest'\nexport type { AppPermissionsApi, PermissionEntry, PermissionCheckOptions } from '../types/platform'\n\n// Inventory — typed helpers over the platform's components API (deployment\n// targets: servers, domains, IP/CIDR ranges). Framework-free; they use the\n// `authFetch` exported below internally.\nexport {\n listInventory,\n addInventoryItem,\n updateInventoryItem,\n removeInventoryItem,\n resolveTool,\n} from './inventory'\nexport type { Tool } from './inventory'\nexport type { InventoryItem, InventoryItemInput } from '../types/platform'\n\n// Connectivity providers — reader over the customer's ZTNA providers, used to\n// populate the Access Server link picker. Framework-free; uses `authFetch`.\nexport { listConnectivityProviders } from './connectivity'\nexport type { ConnectivityProviderRef } from '../types/platform'\n\n// Environments — reader over the customer's deployment scopes, used to populate\n// the Environment picker on Connections + Access Servers. Framework-free.\nexport { listEnvironments } from './environments'\nexport type { EnvironmentRef } from '../types/pipeline'\n\n// Credentials — typed helpers over the platform's credentials API. Paired with\n// a server (component) these form a \"connection\". Secrets are write-only:\n// `listCredentials` returns redacted summaries only. Framework-free; they use\n// the `authFetch` exported below internally.\nexport {\n listCredentials,\n createCredential,\n updateCredential,\n removeCredential,\n testConnection,\n runOperation,\n} from './credentials'\nexport type { Credential, CredentialSummary, CredentialInput } from '../types/platform'\nexport type { TestConnectionResult } from '../types/pipeline'\nexport type { OperationResult } from './credentials'\n\n// Connection onboarding — the one-click \"Connect …\" flow (consent + broker).\n// Framework-free; uses `authFetch`.\nexport { startOnboarding, getOnboardingStatus, revokeOnboarding, fetchAppOnboarding } from './onboarding'\nexport type {\n StartOnboardingInput,\n StartOnboardingResult,\n OnboardingStatus,\n AppOnboardingDescriptor,\n} from './onboarding'\n\n/** Name of the global the platform installs before loading app bundles. */\nexport const HOST_RUNTIME_GLOBAL = '__VELTRIX_APP_RUNTIME__'\n\n/**\n * The runtime surface the platform exposes to app client bundles.\n * The react/reactDom/jsxRuntime members are the host's own module objects —\n * app bundles are compiled with shims that re-export them, guaranteeing a\n * single React instance per page.\n */\nexport interface VeltrixHostRuntime {\n /** The host's `react` module object. */\n react: unknown\n /** The host's `react-dom` module object. */\n reactDom: unknown\n /** The host's `react-dom/client` module object. */\n reactDomClient?: unknown\n /** The host's `react/jsx-runtime` module object. */\n jsxRuntime: unknown\n /** Shared app context — the host wraps app pages in its Provider. */\n AppContext: Context<AppContextValue | null>\n /** fetch() with the platform's Authorization header attached. */\n authFetch: (input: string, init?: RequestInit) => Promise<Response>\n /**\n * The SDK surface app bundles receive for `@veltrixsecops/app-sdk`,\n * `.../hooks`, and `.../client` imports (useAppContext, AppContext,\n * usePipelineStatus, authFetch, getHostRuntime, ...).\n */\n sdk: Record<string, unknown>\n /**\n * The platform's design-system components and hooks, host-owned so they\n * share the single host React instance. Keyed by the exact component/hook\n * names re-exported from `@veltrixsecops/app-sdk/ui` (Button, Input, Card,\n * DataTable, useToast, ...). Present only inside the platform.\n */\n ui?: Record<string, unknown>\n /**\n * Platform-scoped permission checks (RBAC/IdP hardening, Wave C4) —\n * `opts.appId` is NOT defaulted here (omit for a platform-scoped check).\n * App page code should prefer `usePermissions()`\n * (`@veltrixsecops/app-sdk/hooks`), which defaults `opts.appId` to the\n * app's own id. Optional for compatibility with a host that hasn't\n * deployed this surface yet.\n */\n permissions?: AppPermissionsApi\n}\n\n/** Read the host runtime, or null outside the platform (tests, storybook). */\nexport function getHostRuntime(): VeltrixHostRuntime | null {\n const runtime = (globalThis as Record<string, unknown>)[HOST_RUNTIME_GLOBAL]\n return (runtime as VeltrixHostRuntime) ?? null\n}\n\n/** Read the host runtime, throwing a diagnosable error when absent. */\nexport function requireHostRuntime(): VeltrixHostRuntime {\n const runtime = getHostRuntime()\n if (!runtime) {\n throw new Error(\n 'Veltrix host runtime not found — app client bundles only run inside the ' +\n `Veltrix platform (missing globalThis.${HOST_RUNTIME_GLOBAL})`,\n )\n }\n return runtime\n}\n\n/**\n * fetch() that carries the platform's Authorization header. Required for an\n * app page to call its own server routes (/api/apps/<app-id>/...), which are\n * bearer-token protected. Falls back to plain fetch outside the platform.\n */\nexport function authFetch(input: string, init?: RequestInit): Promise<Response> {\n const runtime = getHostRuntime()\n if (runtime) return runtime.authFetch(input, init)\n return fetch(input, init)\n}\n\n/** A sidebar entry contributed by the app's client entry module. */\nexport interface AppSidebarItem {\n path: string\n label: string\n icon?: string\n}\n\n/**\n * Shape of the default export of an app's `client/index.tsx`.\n * `pages` keys must match `manifest.client.pages[].component`.\n */\nexport interface AppClientModule {\n id: string\n pages: Record<string, ComponentType | LazyExoticComponent<ComponentType>>\n sidebarItems?: AppSidebarItem[]\n}\n","// ========================================================================\n// @veltrixsecops/app-sdk/ui — the platform's shared component library,\n// exposed to app client bundles.\n//\n// App pages import these instead of hand-rolling raw HTML, so pages render\n// themed inside the Veltrix design system (dark/light + tenant branding via\n// `var(--vx-*)`), consistent with the rest of the portal, and externalized —\n// zero bundle bloat, one React instance. This completes the \"predictable\n// shell, flexible body\" contract: the platform owns the components; apps\n// compose their page body from them.\n//\n// RUNTIME: every export here reads its real implementation off the host at\n// render time — globalThis.__VELTRIX_APP_RUNTIME__.ui.<Name>, populated by\n// the platform from components/shared/* (see\n// client/src/appRuntime/installHostRuntime.ts). At packaging time the CLI's\n// client bundler (and the platform's on-demand esbuild bundle routes) shim\n// the '@veltrixsecops/app-sdk/ui' specifier straight to that `ui` bag, so\n// this module's own render-time lookup only actually executes in\n// non-bundled contexts: local dev, unit tests, a bare `tsc --noEmit`, or\n// Storybook.\n//\n// FALLBACK CONTRACT: outside the platform (host runtime absent, or an older\n// host that predates a given component) every export here renders a\n// minimal, accessible, unstyled fallback instead of throwing — an app page\n// under test or local dev keeps working, just without platform theming.\n// Never throw on render.\n//\n// TYPE FIDELITY: prop types are copied from the platform's real components\n// in client/src/components/shared (see the ADR at\n// _ai_tasks/ui-package/2026-07-10/02_sdk_ui_subpath.md) so app-author\n// typechecking matches what actually renders inside Veltrix. Heavy composite\n// widgets (ConfigurationCanvas, VersionControl, Pipeline) stay out of scope —\n// they carry platform coupling a later phase may expose read-only versions\n// of; see the ADR's \"Non-goals\" section.\n//\n// EXPORTS: Button, Badge, Card (+ CardHeader/CardBody/CardFooter), Input,\n// Textarea, Checkbox, Select, SearchBox, Pagination, FilterBar, SortSelect,\n// FormField, Tabs, EmptyState, Skeleton (+ SkeletonText/SkeletonCard),\n// Tooltip, Spinner, DataTable, StatsCard, FormDialog, useToast,\n// useConfirmDialog.\n//\n// useToast / useConfirmDialog are included because the platform's root\n// App.tsx mounts ToastProvider/ConfirmationDialogProvider around the ENTIRE\n// tree (including every app page, since app pages render inside the host's\n// own React instance) — so the real context-backed hooks resolve correctly\n// from app code with no extra wiring. Outside the platform they degrade to a\n// safe, non-throwing fallback (see each hook's docs below) rather than\n// crashing or silently doing nothing.\n// ========================================================================\n\nimport * as React from 'react'\nimport { getHostRuntime } from '../client'\n\n/** Read one named component off the host's `runtime.ui` bag, or undefined. */\nfunction getHostUi<T>(name: string): T | undefined {\n return getHostRuntime()?.ui?.[name] as T | undefined\n}\n\nconst fallbackNote: React.CSSProperties = { fontFamily: 'inherit' }\n\n// ---------------------------------------------------------------------------\n// Button\n// ---------------------------------------------------------------------------\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'success' | 'warning' | 'ghost' | 'link'\nexport type ButtonSize = 'sm' | 'md' | 'lg'\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant\n size?: ButtonSize\n isLoading?: boolean\n loadingText?: string\n fullWidth?: boolean\n leftIcon?: React.ReactNode\n rightIcon?: React.ReactNode\n}\n\ntype ButtonComponent = React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>\n\n/**\n * Button — delegates to the platform's real Button at render time.\n *\n * @example\n * <Button variant=\"primary\" leftIcon={<RefreshIcon />} onClick={refresh}>Refresh</Button>\n */\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {\n const HostButton = getHostUi<ButtonComponent>('Button')\n if (HostButton) return <HostButton ref={ref} {...props} />\n\n const {\n variant: _variant,\n size: _size,\n isLoading,\n loadingText,\n fullWidth,\n leftIcon,\n rightIcon,\n children,\n disabled,\n type,\n style,\n ...rest\n } = props\n\n return (\n <button\n ref={ref}\n type={type ?? 'button'}\n disabled={disabled || isLoading}\n aria-busy={isLoading || undefined}\n style={{\n ...fallbackNote,\n display: 'inline-flex',\n alignItems: 'center',\n gap: 6,\n padding: '6px 14px',\n borderRadius: 6,\n border: '1px solid currentColor',\n background: 'transparent',\n cursor: disabled || isLoading ? 'not-allowed' : 'pointer',\n width: fullWidth ? '100%' : undefined,\n opacity: disabled || isLoading ? 0.6 : 1,\n justifyContent: 'center',\n ...style,\n }}\n {...rest}\n >\n {leftIcon}\n {isLoading ? loadingText ?? children : children}\n {rightIcon}\n </button>\n )\n})\nButton.displayName = 'Button'\n\n// ---------------------------------------------------------------------------\n// Badge\n// ---------------------------------------------------------------------------\n\nexport type BadgeVariant = 'default' | 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info'\nexport type BadgeSize = 'sm' | 'md' | 'lg'\n\nexport interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {\n variant?: BadgeVariant\n size?: BadgeSize\n rounded?: boolean\n dot?: boolean\n}\n\nconst BADGE_FALLBACK_COLORS: Record<BadgeVariant, string> = {\n default: '#6b7280',\n primary: '#4f46e5',\n secondary: '#6b7280',\n success: '#16a34a',\n danger: '#dc2626',\n warning: '#d97706',\n info: '#0284c7',\n}\n\n/**\n * Badge — delegates to the platform's real Badge at render time.\n *\n * @example\n * <Badge variant={row.deployState === 'deployed' ? 'success' : 'warning'}>{row.deployState}</Badge>\n */\nexport const Badge: React.FC<BadgeProps> = ({ variant = 'default', size = 'md', rounded, dot, children, style, ...rest }) => {\n const HostBadge = getHostUi<React.FC<BadgeProps>>('Badge')\n if (HostBadge) return <HostBadge variant={variant} size={size} rounded={rounded} dot={dot} style={style} {...rest}>{children}</HostBadge>\n\n const color = BADGE_FALLBACK_COLORS[variant]\n const fontSize = size === 'sm' ? 11 : size === 'lg' ? 14 : 12\n return (\n <span\n style={{\n ...fallbackNote,\n display: 'inline-flex',\n alignItems: 'center',\n gap: 4,\n padding: size === 'sm' ? '1px 6px' : size === 'lg' ? '3px 10px' : '2px 8px',\n borderRadius: rounded ? 999 : 4,\n border: `1px solid ${color}`,\n color,\n fontSize,\n fontWeight: 500,\n ...style,\n }}\n {...rest}\n >\n {dot && <span aria-hidden=\"true\" style={{ width: 6, height: 6, borderRadius: '50%', background: color }} />}\n {children}\n </span>\n )\n}\nBadge.displayName = 'Badge'\n\n// ---------------------------------------------------------------------------\n// Card (+ Header/Body/Footer)\n// ---------------------------------------------------------------------------\n\nexport interface CardProps extends React.HTMLAttributes<HTMLDivElement> {\n variant?: 'default' | 'bordered' | 'elevated'\n padding?: 'none' | 'sm' | 'md' | 'lg'\n}\nexport interface CardHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n actions?: React.ReactNode\n}\nexport type CardBodyProps = React.HTMLAttributes<HTMLDivElement>\nexport interface CardFooterProps extends React.HTMLAttributes<HTMLDivElement> {\n variant?: 'default' | 'bordered'\n}\n\n/** Card — delegates to the platform's real Card at render time. */\nexport const Card: React.FC<CardProps> = ({ children, style, ...rest }) => {\n const HostCard = getHostUi<React.FC<CardProps>>('Card')\n if (HostCard) return <HostCard style={style} {...rest}>{children}</HostCard>\n return (\n <div style={{ ...fallbackNote, border: '1px solid #d1d5db', borderRadius: 8, overflow: 'hidden', ...style }} {...rest}>\n {children}\n </div>\n )\n}\nCard.displayName = 'Card'\n\n/** CardHeader — the header section of a Card, with optional trailing actions. */\nexport const CardHeader: React.FC<CardHeaderProps> = ({ actions, children, style, ...rest }) => {\n const HostCardHeader = getHostUi<React.FC<CardHeaderProps>>('CardHeader')\n if (HostCardHeader) return <HostCardHeader actions={actions} style={style} {...rest}>{children}</HostCardHeader>\n return (\n <div\n style={{\n ...fallbackNote,\n padding: '12px 16px',\n borderBottom: '1px solid #d1d5db',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 12,\n ...style,\n }}\n {...rest}\n >\n <div style={{ flex: 1, minWidth: 0 }}>{children}</div>\n {actions && <div>{actions}</div>}\n </div>\n )\n}\nCardHeader.displayName = 'CardHeader'\n\n/** CardBody — the main content area of a Card. */\nexport const CardBody: React.FC<CardBodyProps> = ({ children, style, ...rest }) => {\n const HostCardBody = getHostUi<React.FC<CardBodyProps>>('CardBody')\n if (HostCardBody) return <HostCardBody style={style} {...rest}>{children}</HostCardBody>\n return (\n <div style={{ ...fallbackNote, padding: '12px 16px', ...style }} {...rest}>\n {children}\n </div>\n )\n}\nCardBody.displayName = 'CardBody'\n\n/** CardFooter — the footer section of a Card, typically for actions. */\nexport const CardFooter: React.FC<CardFooterProps> = ({ children, style, ...rest }) => {\n const HostCardFooter = getHostUi<React.FC<CardFooterProps>>('CardFooter')\n if (HostCardFooter) return <HostCardFooter style={style} {...rest}>{children}</HostCardFooter>\n return (\n <div style={{ ...fallbackNote, padding: '12px 16px', borderTop: '1px solid #d1d5db', ...style }} {...rest}>\n {children}\n </div>\n )\n}\nCardFooter.displayName = 'CardFooter'\n\n// ---------------------------------------------------------------------------\n// Input\n// ---------------------------------------------------------------------------\n\nexport type InputSize = 'sm' | 'md' | 'lg'\nexport type InputVariant = 'default' | 'error' | 'success'\n\nexport interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {\n label?: string\n error?: string\n helperText?: string\n leftIcon?: React.ReactNode\n rightIcon?: React.ReactNode\n variant?: InputVariant\n inputSize?: InputSize\n isSuccess?: boolean\n fullWidth?: boolean\n}\n\ntype InputComponent = React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>\n\n/**\n * Input — delegates to the platform's real Input at render time.\n *\n * @example\n * <Input label=\"Index name\" value={name} onChange={(e) => setName(e.target.value)} error={errors.name} />\n */\nexport const Input = React.forwardRef<HTMLInputElement, InputProps>((props, ref) => {\n const HostInput = getHostUi<InputComponent>('Input')\n if (HostInput) return <HostInput ref={ref} {...props} />\n\n const { label, error, helperText, leftIcon, rightIcon, fullWidth = true, id, style, ...rest } = props\n const inputId = id ?? (label ? `vx-input-${label.toLowerCase().replace(/\\s+/g, '-')}` : undefined)\n\n return (\n <div style={{ ...fallbackNote, width: fullWidth ? '100%' : undefined }}>\n {label && (\n <label htmlFor={inputId} style={{ display: 'block', fontSize: 13, fontWeight: 500, marginBottom: 4 }}>\n {label}\n </label>\n )}\n <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>\n {leftIcon}\n <input\n ref={ref}\n id={inputId}\n aria-invalid={!!error || undefined}\n style={{ flex: 1, padding: '6px 10px', borderRadius: 6, border: '1px solid #9ca3af', ...style }}\n {...rest}\n />\n {rightIcon}\n </div>\n {error && (\n <p role=\"alert\" style={{ marginTop: 4, fontSize: 12, color: '#dc2626' }}>\n {error}\n </p>\n )}\n {helperText && !error && <p style={{ marginTop: 4, fontSize: 12, color: '#6b7280' }}>{helperText}</p>}\n </div>\n )\n})\nInput.displayName = 'Input'\n\n// ---------------------------------------------------------------------------\n// Textarea\n// ---------------------------------------------------------------------------\n\nexport interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {\n label?: string\n error?: string\n helperText?: string\n fullWidth?: boolean\n}\n\ntype TextareaComponent = React.ForwardRefExoticComponent<TextareaProps & React.RefAttributes<HTMLTextAreaElement>>\n\n/**\n * Textarea — delegates to the platform's real Textarea at render time.\n *\n * @example\n * <Textarea label=\"Description\" helperText=\"Markdown is supported.\" />\n */\nexport const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, ref) => {\n const HostTextarea = getHostUi<TextareaComponent>('Textarea')\n if (HostTextarea) return <HostTextarea ref={ref} {...props} />\n\n const { label, error, helperText, fullWidth = true, id, rows = 4, style, ...rest } = props\n const textareaId = id ?? (label ? `vx-textarea-${label.toLowerCase().replace(/\\s+/g, '-')}` : undefined)\n\n return (\n <div style={{ ...fallbackNote, width: fullWidth ? '100%' : undefined }}>\n {label && (\n <label htmlFor={textareaId} style={{ display: 'block', fontSize: 13, fontWeight: 500, marginBottom: 4 }}>\n {label}\n </label>\n )}\n <textarea\n ref={ref}\n id={textareaId}\n rows={rows}\n aria-invalid={!!error || undefined}\n style={{ width: '100%', padding: '6px 10px', borderRadius: 6, border: '1px solid #9ca3af', ...style }}\n {...rest}\n />\n {error && (\n <p role=\"alert\" style={{ marginTop: 4, fontSize: 12, color: '#dc2626' }}>\n {error}\n </p>\n )}\n {helperText && !error && <p style={{ marginTop: 4, fontSize: 12, color: '#6b7280' }}>{helperText}</p>}\n </div>\n )\n})\nTextarea.displayName = 'Textarea'\n\n// ---------------------------------------------------------------------------\n// Checkbox\n// ---------------------------------------------------------------------------\n\nexport interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {\n label?: React.ReactNode\n error?: string\n helperText?: string\n}\n\ntype CheckboxComponent = React.ForwardRefExoticComponent<CheckboxProps & React.RefAttributes<HTMLInputElement>>\n\n/**\n * Checkbox — delegates to the platform's real Checkbox at render time.\n *\n * @example\n * <Checkbox label=\"Enable drift detection\" checked={enabled} onChange={(e) => setEnabled(e.target.checked)} />\n */\nexport const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {\n const HostCheckbox = getHostUi<CheckboxComponent>('Checkbox')\n if (HostCheckbox) return <HostCheckbox ref={ref} {...props} />\n\n const { label, error, helperText, id, disabled, ...rest } = props\n const generatedId = React.useId()\n const inputId = id ?? generatedId\n\n return (\n <div style={fallbackNote}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <input ref={ref} id={inputId} type=\"checkbox\" disabled={disabled} aria-invalid={!!error || undefined} {...rest} />\n {label && (\n <label htmlFor={inputId} style={{ fontSize: 13, cursor: disabled ? 'not-allowed' : 'pointer', opacity: disabled ? 0.6 : 1 }}>\n {label}\n </label>\n )}\n </div>\n {error && (\n <p role=\"alert\" style={{ marginTop: 4, fontSize: 12, color: '#dc2626' }}>\n {error}\n </p>\n )}\n {helperText && !error && <p style={{ marginTop: 4, fontSize: 12, color: '#6b7280' }}>{helperText}</p>}\n </div>\n )\n})\nCheckbox.displayName = 'Checkbox'\n\n// ---------------------------------------------------------------------------\n// Select (controlled; onChange receives the new value string)\n// ---------------------------------------------------------------------------\n\nexport type SelectSize = 'sm' | 'md' | 'lg'\n\nexport interface SelectOption {\n value: string\n label: string\n disabled?: boolean\n}\n\nexport interface SelectProps {\n options: SelectOption[]\n value?: string\n onChange?: (value: string) => void\n placeholder?: string\n label?: string\n error?: string\n helperText?: string\n size?: SelectSize\n disabled?: boolean\n fullWidth?: boolean\n className?: string\n id?: string\n name?: string\n 'aria-label'?: string\n}\n\n/**\n * Select — delegates to the platform's real (WAI-ARIA listbox) Select at render time.\n * The fallback is a native `<select>` — functionally equivalent, visually plainer.\n *\n * @example\n * <Select label=\"Environment\" value={env} onChange={setEnv} options={[{ value: 'prod', label: 'Production' }]} />\n */\nexport const Select: React.FC<SelectProps> = (props) => {\n const HostSelect = getHostUi<React.FC<SelectProps>>('Select')\n if (HostSelect) return <HostSelect {...props} />\n\n const { options, value, onChange, placeholder = 'Select…', label, error, helperText, disabled, fullWidth = true, id, name, className } = props\n const selectId = id ?? (label ? `vx-select-${label.toLowerCase().replace(/\\s+/g, '-')}` : undefined)\n\n return (\n <div className={className} style={{ ...fallbackNote, width: fullWidth ? '100%' : undefined }}>\n {label && (\n <label htmlFor={selectId} style={{ display: 'block', fontSize: 13, fontWeight: 500, marginBottom: 4 }}>\n {label}\n </label>\n )}\n <select\n id={selectId}\n name={name}\n aria-label={props['aria-label']}\n aria-invalid={!!error || undefined}\n disabled={disabled}\n value={value ?? ''}\n onChange={(event) => onChange?.(event.target.value)}\n style={{ width: '100%', padding: '6px 10px', borderRadius: 6, border: '1px solid #9ca3af' }}\n >\n {placeholder && (\n <option value=\"\" disabled>\n {placeholder}\n </option>\n )}\n {options.map((option) => (\n <option key={option.value} value={option.value} disabled={option.disabled}>\n {option.label}\n </option>\n ))}\n </select>\n {error && (\n <p role=\"alert\" style={{ marginTop: 4, fontSize: 12, color: '#dc2626' }}>\n {error}\n </p>\n )}\n {helperText && !error && <p style={{ marginTop: 4, fontSize: 12, color: '#6b7280' }}>{helperText}</p>}\n </div>\n )\n}\nSelect.displayName = 'Select'\n\n// ---------------------------------------------------------------------------\n// SearchBox\n// ---------------------------------------------------------------------------\n\nexport type SearchBoxSize = 'sm' | 'md' | 'lg'\n\nexport interface SearchBoxProps {\n /** Controlled search text. */\n value: string\n /** Called with the new text — debounced by `debounceMs` when set. */\n onChange: (value: string) => void\n placeholder?: string\n disabled?: boolean\n size?: SearchBoxSize\n /** Debounces `onChange` by this many ms; omit (or `0`) to call on every keystroke. */\n debounceMs?: number\n className?: string\n 'aria-label'?: string\n}\n\n/**\n * SearchBox — delegates to the platform's real SearchBox at render time (leading search icon,\n * a clear button once there's text, optional debounce). The fallback is a bare\n * `<input type=\"search\">` wired straight to `value`/`onChange` — no icon, no debounce, no\n * clear button — but functionally sufficient for typing and clearing.\n *\n * @example\n * <SearchBox value={search} onChange={setSearch} placeholder=\"Search apps…\" debounceMs={250} />\n */\nexport const SearchBox: React.FC<SearchBoxProps> = (props) => {\n const HostSearchBox = getHostUi<React.FC<SearchBoxProps>>('SearchBox')\n if (HostSearchBox) return <HostSearchBox {...props} />\n\n const { value, onChange, placeholder, disabled, className, 'aria-label': ariaLabel } = props\n return (\n <input\n type=\"search\"\n value={value}\n onChange={(event) => onChange(event.target.value)}\n placeholder={placeholder}\n disabled={disabled}\n aria-label={ariaLabel ?? placeholder ?? 'Search'}\n className={className}\n style={{ ...fallbackNote, width: '100%', padding: '6px 10px', borderRadius: 6, border: '1px solid #9ca3af' }}\n />\n )\n}\nSearchBox.displayName = 'SearchBox'\n\n// ---------------------------------------------------------------------------\n// Pagination\n// ---------------------------------------------------------------------------\n\nexport interface PaginationProps {\n /** 1-based current page. */\n page: number\n pageSize: number\n totalItems: number\n onPageChange: (page: number) => void\n /** Renders a page-size selector when provided together with `pageSizeOptions`. */\n onPageSizeChange?: (size: number) => void\n pageSizeOptions?: number[]\n disabled?: boolean\n className?: string\n}\n\n/**\n * Pagination — delegates to the platform's real Pagination at render time: a \"Showing X–Y of\n * N\" summary, numbered pages with ellipsis for large ranges, an optional page-size Select, and\n * `aria-current=\"page\"` on the active page — visually consistent with DataTable's built-in\n * pagination footer. The fallback is a plain Prev/Next pair with \"page X of Y\" text.\n *\n * @example\n * <Pagination page={page} pageSize={20} totalItems={total} onPageChange={setPage} />\n */\nexport const Pagination: React.FC<PaginationProps> = (props) => {\n const HostPagination = getHostUi<React.FC<PaginationProps>>('Pagination')\n if (HostPagination) return <HostPagination {...props} />\n\n const { page, pageSize, totalItems, onPageChange, disabled, className } = props\n const pageCount = Math.max(1, Math.ceil(totalItems / pageSize))\n const canGoPrev = !disabled && page > 1\n const canGoNext = !disabled && page < pageCount\n\n return (\n <nav aria-label=\"Pagination\" className={className} style={{ ...fallbackNote, display: 'flex', alignItems: 'center', gap: 12 }}>\n <button type=\"button\" onClick={() => onPageChange(page - 1)} disabled={!canGoPrev} style={{ padding: '4px 10px' }}>\n Prev\n </button>\n <span>\n page {page} of {pageCount}\n </span>\n <button type=\"button\" onClick={() => onPageChange(page + 1)} disabled={!canGoNext} style={{ padding: '4px 10px' }}>\n Next\n </button>\n </nav>\n )\n}\nPagination.displayName = 'Pagination'\n\n// ---------------------------------------------------------------------------\n// FilterBar\n// ---------------------------------------------------------------------------\n\nexport interface FilterOption {\n value: string\n label: string\n}\n\nexport interface FilterDefinition {\n /** Stable identifier; also the React key for this filter's dropdown. */\n key: string\n /** Shown as the dropdown's placeholder/aria-label, and as its entry in the \"Add filter\" menu. */\n label: string\n options: FilterOption[]\n /** `null` (not `''`) represents \"no selection\" — the value FilterBar clears back to. */\n value: string | null\n onChange: (value: string | null) => void\n /** Always rendered when true. Omit/false to make this filter addable/removable via the \"Add filter\" menu. */\n alwaysVisible?: boolean\n}\n\nexport interface FilterBarSearchProps {\n value: string\n onChange: (value: string) => void\n placeholder?: string\n}\n\nexport interface FilterBarProps {\n filters: FilterDefinition[]\n /** Renders a SearchBox ahead of the filter dropdowns when provided. */\n search?: FilterBarSearchProps\n /**\n * Called by \"Clear all\" instead of FilterBar's own clearing logic. Omit it to let FilterBar\n * clear every filter with a value itself (`filter.onChange(null)` for each).\n */\n onClearAll?: () => void\n addFilterLabel?: string\n className?: string\n}\n\n/**\n * FilterBar — delegates to the platform's real FilterBar at render time: an optional\n * SearchBox, always-visible filter dropdowns, and optional filters the user can add/remove via\n * an \"Add filter\" menu (a filter with a non-null `value` is always treated as visible, even\n * before the user explicitly adds it). The fallback renders every filter as a plain,\n * always-visible native `<select>` — no add/remove menu, no styled search box — so an app page\n * under test still exposes every filter's full behavior via `onChange`.\n *\n * @example\n * <FilterBar\n * search={{ value: search, onChange: setSearch, placeholder: 'Search apps…' }}\n * filters={[\n * { key: 'vendor', label: 'Vendor', options: vendorOptions, value: vendor, onChange: setVendor, alwaysVisible: true },\n * { key: 'category', label: 'Category', options: categoryOptions, value: category, onChange: setCategory },\n * ]}\n * />\n */\nexport const FilterBar: React.FC<FilterBarProps> = (props) => {\n const HostFilterBar = getHostUi<React.FC<FilterBarProps>>('FilterBar')\n if (HostFilterBar) return <HostFilterBar {...props} />\n\n const { filters, search, className } = props\n return (\n <div className={className} style={{ ...fallbackNote, display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}>\n {search && (\n <input\n type=\"search\"\n aria-label={search.placeholder ?? 'Search'}\n placeholder={search.placeholder}\n value={search.value}\n onChange={(event) => search.onChange(event.target.value)}\n style={{ padding: '6px 10px', borderRadius: 6, border: '1px solid #9ca3af' }}\n />\n )}\n {filters.map((filter) => (\n <label key={filter.key} style={{ display: 'flex', flexDirection: 'column', fontSize: 12 }}>\n {filter.label}\n <select\n aria-label={filter.label}\n value={filter.value ?? ''}\n onChange={(event) => filter.onChange(event.target.value === '' ? null : event.target.value)}\n style={{ padding: '6px 10px', borderRadius: 6, border: '1px solid #9ca3af' }}\n >\n <option value=\"\">{filter.label}</option>\n {filter.options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </label>\n ))}\n </div>\n )\n}\nFilterBar.displayName = 'FilterBar'\n\n// ---------------------------------------------------------------------------\n// SortSelect\n// ---------------------------------------------------------------------------\n\nexport type SortDirection = 'asc' | 'desc'\n\nexport interface SortOption {\n value: string\n label: string\n}\n\nexport interface SortSelectProps {\n /** Sortable fields, e.g. `[{ value: 'name', label: 'Name' }, { value: 'updatedAt', label: 'Updated' }]`. */\n options: SortOption[]\n /** Selected field key. */\n value: string\n direction: SortDirection\n /** Called with the field and direction together, whichever one the interaction changed. */\n onChange: (value: string, direction: SortDirection) => void\n disabled?: boolean\n className?: string\n}\n\n/**\n * SortSelect — delegates to the platform's real SortSelect at render time: a labeled field\n * Select paired with an asc/desc direction toggle button, styled to sit in the same toolbar\n * row as FilterBar. The standalone sort control for list/card surfaces that aren't a\n * DataTable (which has its own column-header sort). The fallback is a native `<select>` for\n * the field plus a button that flips direction.\n *\n * @example\n * <SortSelect\n * options={[{ value: 'name', label: 'Name' }, { value: 'updatedAt', label: 'Last updated' }]}\n * value={sortField}\n * direction={sortDirection}\n * onChange={(field, direction) => { setSortField(field); setSortDirection(direction) }}\n * />\n */\nexport const SortSelect: React.FC<SortSelectProps> = (props) => {\n const HostSortSelect = getHostUi<React.FC<SortSelectProps>>('SortSelect')\n if (HostSortSelect) return <HostSortSelect {...props} />\n\n const { options, value, direction, onChange, disabled, className } = props\n const directionLabel = direction === 'asc' ? 'Sort ascending' : 'Sort descending'\n\n return (\n <div className={className} style={{ ...fallbackNote, display: 'flex', alignItems: 'center', gap: 6 }}>\n <select\n aria-label=\"Sort by\"\n value={value}\n disabled={disabled}\n onChange={(event) => onChange(event.target.value, direction)}\n style={{ padding: '6px 10px', borderRadius: 6, border: '1px solid #9ca3af' }}\n >\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n <button\n type=\"button\"\n disabled={disabled}\n onClick={() => onChange(value, direction === 'asc' ? 'desc' : 'asc')}\n aria-label={directionLabel}\n style={{ padding: '4px 10px' }}\n >\n {direction === 'asc' ? '↑' : '↓'}\n </button>\n </div>\n )\n}\nSortSelect.displayName = 'SortSelect'\n\n// ---------------------------------------------------------------------------\n// FormField — generic label + control + error/hint wrapper\n// ---------------------------------------------------------------------------\n\nexport interface FormFieldProps {\n label?: string\n htmlFor?: string\n error?: string\n hint?: string\n required?: boolean\n className?: string\n children: React.ReactNode\n}\n\n/**\n * FormField — wraps a custom control (not already self-labeling like Input/Select) with\n * the platform's label + error/hint visual language.\n *\n * @example\n * <FormField label=\"Allowed IP ranges\" htmlFor=\"cidrs\" hint=\"One CIDR block per line\">\n * <textarea id=\"cidrs\" />\n * </FormField>\n */\nexport const FormField: React.FC<FormFieldProps> = (props) => {\n const HostFormField = getHostUi<React.FC<FormFieldProps>>('FormField')\n if (HostFormField) return <HostFormField {...props} />\n\n const { label, htmlFor, error, hint, required, className, children } = props\n return (\n <div className={className} style={fallbackNote}>\n {label && (\n <label htmlFor={htmlFor} style={{ display: 'block', fontSize: 13, fontWeight: 500, marginBottom: 4 }}>\n {label}\n {required && (\n <span aria-hidden=\"true\" style={{ color: '#dc2626', marginLeft: 2 }}>\n *\n </span>\n )}\n </label>\n )}\n {children}\n {error && (\n <p role=\"alert\" style={{ marginTop: 4, fontSize: 12, color: '#dc2626' }}>\n {error}\n </p>\n )}\n {hint && !error && <p style={{ marginTop: 4, fontSize: 12, color: '#6b7280' }}>{hint}</p>}\n </div>\n )\n}\nFormField.displayName = 'FormField'\n\n// ---------------------------------------------------------------------------\n// Tabs\n// ---------------------------------------------------------------------------\n\nexport interface TabItem {\n key?: string\n label: string\n content: React.ReactNode\n disabled?: boolean\n}\n\nexport interface TabsProps {\n tabs: TabItem[]\n defaultActiveIndex?: number\n activeIndex?: number\n onTabChange?: (index: number) => void\n children?: React.ReactNode\n className?: string\n}\n\n/**\n * Tabs — delegates to the platform's real (WAI-ARIA tabs pattern) Tabs at render time.\n *\n * @example\n * <Tabs tabs={[{ key: 'indexes', label: 'Indexes', content: <IndexesPanel /> }]} />\n */\nexport const Tabs: React.FC<TabsProps> = (props) => {\n const HostTabs = getHostUi<React.FC<TabsProps>>('Tabs')\n if (HostTabs) return <HostTabs {...props} />\n\n const { tabs, defaultActiveIndex = 0, activeIndex, onTabChange, children, className } = props\n const [internalIndex, setInternalIndex] = React.useState(defaultActiveIndex)\n const isControlled = activeIndex !== undefined\n const currentIndex = isControlled ? activeIndex : internalIndex\n const activeTab = tabs[currentIndex]\n\n const selectTab = (index: number) => {\n if (tabs[index]?.disabled) return\n if (!isControlled) setInternalIndex(index)\n onTabChange?.(index)\n }\n\n return (\n <div className={className} style={fallbackNote}>\n <div role=\"tablist\" style={{ display: 'flex', gap: 4, borderBottom: '1px solid #d1d5db' }}>\n {tabs.map((tab, index) => (\n <button\n key={tab.key ?? index}\n type=\"button\"\n role=\"tab\"\n aria-selected={index === currentIndex}\n disabled={tab.disabled}\n onClick={() => selectTab(index)}\n style={{\n padding: '6px 12px',\n border: 'none',\n borderBottom: index === currentIndex ? '2px solid currentColor' : '2px solid transparent',\n background: 'transparent',\n cursor: tab.disabled ? 'not-allowed' : 'pointer',\n fontWeight: index === currentIndex ? 600 : 400,\n opacity: tab.disabled ? 0.5 : 1,\n }}\n >\n {tab.label}\n </button>\n ))}\n </div>\n <div role=\"tabpanel\" style={{ padding: 12 }}>\n {children || activeTab?.content}\n </div>\n </div>\n )\n}\nTabs.displayName = 'Tabs'\n\n// ---------------------------------------------------------------------------\n// EmptyState\n// ---------------------------------------------------------------------------\n\nexport interface EmptyStateProps {\n icon?: React.ReactNode\n title: string\n description?: string\n action?: React.ReactNode\n className?: string\n}\n\n/** EmptyState — delegates to the platform's real EmptyState at render time. */\nexport const EmptyState: React.FC<EmptyStateProps> = (props) => {\n const HostEmptyState = getHostUi<React.FC<EmptyStateProps>>('EmptyState')\n if (HostEmptyState) return <HostEmptyState {...props} />\n\n const { icon, title, description, action, className } = props\n return (\n <div className={className} style={{ ...fallbackNote, textAlign: 'center', padding: '32px 16px' }}>\n {icon && <div style={{ marginBottom: 12 }}>{icon}</div>}\n <p style={{ fontWeight: 600, marginBottom: description ? 4 : 0 }}>{title}</p>\n {description && <p style={{ fontSize: 13, color: '#6b7280', marginBottom: action ? 12 : 0 }}>{description}</p>}\n {action}\n </div>\n )\n}\nEmptyState.displayName = 'EmptyState'\n\n// ---------------------------------------------------------------------------\n// Skeleton (+ Text/Card)\n// ---------------------------------------------------------------------------\n\nexport type SkeletonVariant = 'text' | 'circular' | 'rectangular'\n\nexport interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {\n variant?: SkeletonVariant\n width?: string | number\n height?: string | number\n animation?: 'pulse' | 'wave' | 'none'\n}\n\nconst skeletonRadius: Record<SkeletonVariant, number | string> = { text: 4, circular: '50%', rectangular: 8 }\n\n/** Skeleton — delegates to the platform's real Skeleton at render time. */\nexport const Skeleton: React.FC<SkeletonProps> = (props) => {\n const HostSkeleton = getHostUi<React.FC<SkeletonProps>>('Skeleton')\n if (HostSkeleton) return <HostSkeleton {...props} />\n\n const { variant = 'text', width, height, style, ...rest } = props\n return (\n <div\n aria-hidden=\"true\"\n style={{\n background: '#e5e7eb',\n borderRadius: skeletonRadius[variant],\n width: width ?? (variant === 'text' ? '100%' : undefined),\n height: height ?? (variant === 'text' ? 14 : undefined),\n ...style,\n }}\n {...rest}\n />\n )\n}\nSkeleton.displayName = 'Skeleton'\n\nexport interface SkeletonTextProps {\n lines?: number\n width?: string | number\n lastLineWidth?: string | number\n className?: string\n}\n\n/** SkeletonText — a multi-line text skeleton, delegating to the platform's real one. */\nexport const SkeletonText: React.FC<SkeletonTextProps> = (props) => {\n const HostSkeletonText = getHostUi<React.FC<SkeletonTextProps>>('SkeletonText')\n if (HostSkeletonText) return <HostSkeletonText {...props} />\n\n const { lines = 3, width = '100%', lastLineWidth = '80%', className } = props\n return (\n <div className={className} style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>\n {Array.from({ length: lines }).map((_, index) => (\n <Skeleton key={index} variant=\"text\" width={index === lines - 1 ? lastLineWidth : width} />\n ))}\n </div>\n )\n}\nSkeletonText.displayName = 'SkeletonText'\n\nexport interface SkeletonCardProps {\n hasAvatar?: boolean\n hasActions?: boolean\n className?: string\n}\n\n/** SkeletonCard — a card-shaped skeleton, delegating to the platform's real one. */\nexport const SkeletonCard: React.FC<SkeletonCardProps> = (props) => {\n const HostSkeletonCard = getHostUi<React.FC<SkeletonCardProps>>('SkeletonCard')\n if (HostSkeletonCard) return <HostSkeletonCard {...props} />\n\n const { hasAvatar, hasActions, className } = props\n return (\n <div className={className} style={{ border: '1px solid #e5e7eb', borderRadius: 8, padding: 16 }}>\n <div style={{ display: 'flex', gap: 12 }}>\n {hasAvatar && <Skeleton variant=\"circular\" width={40} height={40} />}\n <div style={{ flex: 1 }}>\n <Skeleton variant=\"text\" width=\"60%\" />\n <div style={{ marginTop: 8 }}>\n <SkeletonText lines={2} />\n </div>\n </div>\n </div>\n {hasActions && (\n <div style={{ display: 'flex', gap: 8, marginTop: 12 }}>\n <Skeleton variant=\"rectangular\" width={72} height={30} />\n <Skeleton variant=\"rectangular\" width={72} height={30} />\n </div>\n )}\n </div>\n )\n}\nSkeletonCard.displayName = 'SkeletonCard'\n\n// ---------------------------------------------------------------------------\n// Tooltip\n// ---------------------------------------------------------------------------\n\nexport type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right'\n\nexport interface TooltipProps {\n content?: React.ReactNode\n placement?: TooltipPlacement\n delayDuration?: number\n disabled?: boolean\n className?: string\n children: React.ReactNode\n}\n\n/**\n * Tooltip — delegates to the platform's real Tooltip at render time. The fallback uses the\n * browser's native `title` attribute (only works when `content` is plain text).\n */\nexport const Tooltip: React.FC<TooltipProps> = (props) => {\n const HostTooltip = getHostUi<React.FC<TooltipProps>>('Tooltip')\n if (HostTooltip) return <HostTooltip {...props} />\n\n const { content, disabled, className, children } = props\n const titleText = typeof content === 'string' ? content : undefined\n if (disabled || !titleText) return <>{children}</>\n return (\n <span className={className} title={titleText} style={{ display: 'inline-flex' }}>\n {children}\n </span>\n )\n}\nTooltip.displayName = 'Tooltip'\n\n// ---------------------------------------------------------------------------\n// Spinner\n// ---------------------------------------------------------------------------\n\nexport type SpinnerSize = 'sm' | 'md' | 'lg'\n\nexport interface SpinnerProps {\n size?: SpinnerSize\n className?: string\n label?: string\n}\n\nconst spinnerDiameter: Record<SpinnerSize, number> = { sm: 16, md: 24, lg: 32 }\n\n/** Spinner — delegates to the platform's real Spinner at render time. */\nexport const Spinner: React.FC<SpinnerProps> = (props) => {\n const HostSpinner = getHostUi<React.FC<SpinnerProps>>('Spinner')\n if (HostSpinner) return <HostSpinner {...props} />\n\n const { size = 'md', className, label } = props\n const diameter = spinnerDiameter[size]\n return (\n <div className={className} role=\"status\" aria-live=\"polite\" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>\n <div\n aria-hidden=\"true\"\n style={{\n width: diameter,\n height: diameter,\n borderRadius: '50%',\n border: '2px solid currentColor',\n borderTopColor: 'transparent',\n animation: 'vx-sdk-spin 0.8s linear infinite',\n }}\n />\n {label && <p style={{ marginTop: 8, fontSize: 12, color: '#6b7280' }}>{label}</p>}\n <span style={{ position: 'absolute', width: 1, height: 1, overflow: 'hidden', clip: 'rect(0 0 0 0)' }}>{label || 'Loading'}</span>\n <style>{'@keyframes vx-sdk-spin { to { transform: rotate(360deg) } }'}</style>\n </div>\n )\n}\nSpinner.displayName = 'Spinner'\n\n// ---------------------------------------------------------------------------\n// DataTable (generic)\n// ---------------------------------------------------------------------------\n\nexport type DataTableAlign = 'left' | 'center' | 'right'\nexport type DataTableSortOrder = 'asc' | 'desc'\n\nexport interface DataTableSort {\n field: string\n order: DataTableSortOrder\n}\n\nexport interface DataTableColumn<T> {\n key: string\n header: React.ReactNode\n render?: (row: T) => React.ReactNode\n sortable?: boolean\n align?: DataTableAlign\n width?: string\n className?: string\n}\n\nexport interface DataTableEmptyState {\n icon?: React.ReactNode\n title: string\n description?: string\n action?: React.ReactNode\n}\n\nexport interface DataTablePaginationState {\n page: number\n pageSize: number\n total: number\n}\n\nexport interface DataTableProps<T> {\n columns: DataTableColumn<T>[]\n data: T[]\n rowKey: (row: T) => string\n isLoading?: boolean\n emptyState?: DataTableEmptyState\n sort?: DataTableSort\n onSortChange?: (sort: DataTableSort) => void\n pagination?: DataTablePaginationState\n onPageChange?: (page: number) => void\n onRowClick?: (row: T) => void\n rowActions?: (row: T) => React.ReactNode\n className?: string\n}\n\ntype DataTableComponent = (<T>(props: DataTableProps<T>) => React.ReactElement) & { displayName?: string }\n\nfunction FallbackDataTable<T>(props: DataTableProps<T>): React.ReactElement {\n const { columns, data, rowKey, isLoading, emptyState, onRowClick, rowActions, className } = props\n const showEmpty = !isLoading && data.length === 0\n\n return (\n <div className={className} style={fallbackNote}>\n <table style={{ width: '100%', borderCollapse: 'collapse' }}>\n <thead>\n <tr>\n {columns.map((column) => (\n <th key={column.key} style={{ textAlign: column.align ?? 'left', padding: '8px 10px', borderBottom: '1px solid #d1d5db' }}>\n {column.header}\n </th>\n ))}\n {rowActions && <th style={{ padding: '8px 10px', borderBottom: '1px solid #d1d5db' }} />}\n </tr>\n </thead>\n <tbody>\n {isLoading && (\n <tr>\n <td colSpan={Math.max(columns.length + (rowActions ? 1 : 0), 1)} style={{ padding: '12px 10px' }}>\n Loading…\n </td>\n </tr>\n )}\n {showEmpty && (\n <tr>\n <td colSpan={Math.max(columns.length + (rowActions ? 1 : 0), 1)}>\n <EmptyState title={emptyState?.title ?? 'No data'} description={emptyState?.description} icon={emptyState?.icon} action={emptyState?.action} />\n </td>\n </tr>\n )}\n {!isLoading &&\n !showEmpty &&\n data.map((row) => (\n <tr\n key={rowKey(row)}\n onClick={onRowClick ? () => onRowClick(row) : undefined}\n style={{ cursor: onRowClick ? 'pointer' : undefined, borderBottom: '1px solid #e5e7eb' }}\n >\n {columns.map((column) => (\n <td key={column.key} style={{ textAlign: column.align ?? 'left', padding: '8px 10px' }}>\n {column.render ? column.render(row) : String((row as Record<string, unknown>)[column.key] ?? '')}\n </td>\n ))}\n {rowActions && (\n <td style={{ padding: '8px 10px' }} onClick={(event) => event.stopPropagation()}>\n {rowActions(row)}\n </td>\n )}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\n/**\n * DataTable — delegates to the platform's real, server-driven DataTable at render time.\n *\n * @example\n * <DataTable\n * columns={[{ key: 'name', header: 'Name' }, { key: 'deployState', header: 'Status', render: (r) => <Badge>{r.deployState}</Badge> }]}\n * data={indexes}\n * rowKey={(row) => row.id}\n * />\n */\nexport function DataTable<T>(props: DataTableProps<T>): React.ReactElement {\n const HostDataTable = getHostUi<DataTableComponent>('DataTable')\n if (HostDataTable) return <HostDataTable {...props} />\n return <FallbackDataTable {...props} />\n}\n\n// ---------------------------------------------------------------------------\n// StatsCard\n// ---------------------------------------------------------------------------\n\nexport type StatsCardVariant = 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info'\n\nexport interface StatsCardDelta {\n value: string\n direction: 'up' | 'down' | 'neutral'\n label?: string\n}\n\nexport interface StatsCardProps {\n label: string\n value: React.ReactNode\n icon?: React.ReactNode\n delta?: StatsCardDelta\n variant?: StatsCardVariant\n isLoading?: boolean\n onClick?: () => void\n className?: string\n}\n\nconst DELTA_FALLBACK_COLOR: Record<StatsCardDelta['direction'], string> = {\n up: '#16a34a',\n down: '#dc2626',\n neutral: '#6b7280',\n}\n\n/**\n * StatsCard — delegates to the platform's real StatsCard at render time.\n *\n * @example\n * <StatsCard label=\"Indexes\" value={indexes.length} delta={{ value: '+2', direction: 'up' }} />\n */\nexport const StatsCard: React.FC<StatsCardProps> = (props) => {\n const HostStatsCard = getHostUi<React.FC<StatsCardProps>>('StatsCard')\n if (HostStatsCard) return <HostStatsCard {...props} />\n\n const { label, value, icon, delta, isLoading, onClick, className } = props\n const isClickable = Boolean(onClick)\n\n return (\n <div\n role={isClickable ? 'button' : undefined}\n tabIndex={isClickable ? 0 : undefined}\n onClick={onClick}\n onKeyDown={\n isClickable\n ? (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n onClick?.()\n }\n }\n : undefined\n }\n className={className}\n style={{\n ...fallbackNote,\n border: '1px solid #d1d5db',\n borderRadius: 8,\n padding: 16,\n display: 'flex',\n alignItems: 'flex-start',\n justifyContent: 'space-between',\n gap: 12,\n cursor: isClickable ? 'pointer' : undefined,\n }}\n >\n <div style={{ minWidth: 0, flex: 1 }}>\n <p style={{ fontSize: 13, color: '#6b7280', margin: 0 }}>{label}</p>\n <p style={{ fontSize: 28, fontWeight: 700, margin: '4px 0 0' }}>{isLoading ? '…' : value}</p>\n {delta && !isLoading && (\n <p style={{ fontSize: 13, color: DELTA_FALLBACK_COLOR[delta.direction], margin: '4px 0 0' }}>\n {delta.value} {delta.label}\n </p>\n )}\n </div>\n {icon && <div aria-hidden=\"true\">{icon}</div>}\n </div>\n )\n}\nStatsCard.displayName = 'StatsCard'\n\n// ---------------------------------------------------------------------------\n// FormDialog\n// ---------------------------------------------------------------------------\n\nexport type FormDialogSize = 'sm' | 'md' | 'lg'\n\nexport interface FormDialogProps {\n isOpen: boolean\n onClose: () => void\n title: string\n description?: string\n children: React.ReactNode\n onSubmit: () => void | Promise<void>\n submitText?: string\n cancelText?: string\n isSubmitting?: boolean\n error?: string | null\n size?: FormDialogSize\n disableBackdropClose?: boolean\n submitDisabled?: boolean\n}\n\nconst FORM_DIALOG_MAX_WIDTH: Record<FormDialogSize, number> = { sm: 420, md: 520, lg: 680 }\n\n/**\n * FormDialog — delegates to the platform's real FormDialog at render time. The fallback is\n * a minimal, accessible modal shell (role=\"dialog\", Escape-to-close, backdrop click) without\n * the host's focus-trap polish.\n *\n * @example\n * <FormDialog isOpen={isOpen} onClose={close} title=\"Add index\" onSubmit={handleSubmit}>\n * <Input label=\"Name\" value={name} onChange={(e) => setName(e.target.value)} />\n * </FormDialog>\n */\nexport const FormDialog: React.FC<FormDialogProps> = (props) => {\n const HostFormDialog = getHostUi<React.FC<FormDialogProps>>('FormDialog')\n if (HostFormDialog) return <HostFormDialog {...props} />\n\n const {\n isOpen,\n onClose,\n title,\n description,\n children,\n onSubmit,\n submitText = 'Save',\n cancelText = 'Cancel',\n isSubmitting = false,\n error = null,\n size = 'md',\n disableBackdropClose = false,\n submitDisabled = false,\n } = props\n\n React.useEffect(() => {\n if (!isOpen) return\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && !isSubmitting) onClose()\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [isOpen, isSubmitting, onClose])\n\n if (!isOpen) return null\n\n const requestClose = () => {\n if (!isSubmitting) onClose()\n }\n\n return (\n <div\n style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 50 }}\n onClick={disableBackdropClose ? undefined : requestClose}\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n style={{ background: 'white', color: 'black', borderRadius: 8, width: '100%', maxWidth: FORM_DIALOG_MAX_WIDTH[size], maxHeight: '80vh', overflow: 'auto', ...fallbackNote }}\n onClick={(event) => event.stopPropagation()}\n >\n <form\n onSubmit={(event) => {\n event.preventDefault()\n void onSubmit()\n }}\n >\n <div style={{ padding: 16, borderBottom: '1px solid #d1d5db' }}>\n <h3 style={{ margin: 0, fontSize: 16, fontWeight: 600 }}>{title}</h3>\n {description && <p style={{ margin: '4px 0 0', fontSize: 13, color: '#6b7280' }}>{description}</p>}\n </div>\n <div style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 12 }}>\n {error && (\n <p role=\"alert\" style={{ margin: 0, fontSize: 13, color: '#dc2626' }}>\n {error}\n </p>\n )}\n {children}\n </div>\n <div style={{ padding: 16, borderTop: '1px solid #d1d5db', display: 'flex', justifyContent: 'flex-end', gap: 8 }}>\n <Button type=\"button\" variant=\"secondary\" onClick={requestClose} disabled={isSubmitting}>\n {cancelText}\n </Button>\n <Button type=\"submit\" variant=\"primary\" isLoading={isSubmitting} disabled={submitDisabled}>\n {submitText}\n </Button>\n </div>\n </form>\n </div>\n </div>\n )\n}\nFormDialog.displayName = 'FormDialog'\n\n// ---------------------------------------------------------------------------\n// Modal\n// ---------------------------------------------------------------------------\n\nexport type ModalSize = 'sm' | 'md' | 'lg' | 'xl'\n\nexport interface ModalProps {\n /** Controls visibility. Renders nothing when `false`. */\n isOpen: boolean\n /** Called on backdrop click (unless disabled), Escape, and the X button. */\n onClose: () => void\n /** Heading rendered at the top of the modal. */\n title?: React.ReactNode\n /** Secondary line rendered under the title. */\n subtitle?: React.ReactNode\n /** Modal body. */\n children: React.ReactNode\n /** Optional footer content (e.g. action buttons), rendered in a tinted bar. */\n footer?: React.ReactNode\n size?: ModalSize\n /** When true, clicking the backdrop no longer closes the modal. */\n disableBackdropClose?: boolean\n /** When true, Escape no longer closes the modal. */\n disableEscapeClose?: boolean\n /** Hide the top-right close (X) button. */\n hideCloseButton?: boolean\n /** Accessible label for the close button. Defaults to \"Close\". */\n closeLabel?: string\n className?: string\n}\n\nconst MODAL_MAX_WIDTH: Record<ModalSize, number> = { sm: 420, md: 520, lg: 680, xl: 896 }\n\n/**\n * Modal — delegates to the platform's real Modal at render time: a coordinated\n * overlay shell with a `title` + `subtitle` header, a scrollable body, and an\n * optional `footer` action bar. The general-purpose sibling of {@link FormDialog}\n * (form) and the confirm dialog (yes/no) — use it for detail/wizard/picker\n * overlays. Selects and other popovers rendered inside it float above the body,\n * so their menus are never clipped. The fallback is a minimal, accessible,\n * non-throwing shell for local dev / tests.\n *\n * @example\n * <Modal isOpen={open} onClose={close} title=\"Choose a region\" subtitle=\"Where should this live?\"\n * footer={<Button onClick={close}>Done</Button>}>\n * <Select label=\"Region\" options={regions} value={region} onChange={setRegion} />\n * </Modal>\n */\nexport const Modal: React.FC<ModalProps> = (props) => {\n const HostModal = getHostUi<React.FC<ModalProps>>('Modal')\n if (HostModal) return <HostModal {...props} />\n\n const {\n isOpen,\n onClose,\n title,\n subtitle,\n children,\n footer,\n size = 'md',\n disableBackdropClose = false,\n disableEscapeClose = false,\n hideCloseButton = false,\n closeLabel = 'Close',\n className,\n } = props\n\n React.useEffect(() => {\n if (!isOpen) return\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && !disableEscapeClose) onClose()\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [isOpen, disableEscapeClose, onClose])\n\n if (!isOpen) return null\n\n const hasHeader = title != null || subtitle != null || !hideCloseButton\n\n return (\n <div\n style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 50 }}\n onClick={disableBackdropClose ? undefined : onClose}\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n style={{ background: 'white', color: 'black', borderRadius: 8, width: '100%', maxWidth: MODAL_MAX_WIDTH[size], maxHeight: '80vh', overflow: 'auto', ...fallbackNote }}\n className={className}\n onClick={(event) => event.stopPropagation()}\n >\n {hasHeader && (\n <div style={{ padding: 16, borderBottom: '1px solid #d1d5db', display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16 }}>\n <div style={{ minWidth: 0 }}>\n {title != null && <h3 style={{ margin: 0, fontSize: 16, fontWeight: 600 }}>{title}</h3>}\n {subtitle != null && <p style={{ margin: '4px 0 0', fontSize: 13, color: '#6b7280' }}>{subtitle}</p>}\n </div>\n {!hideCloseButton && (\n <button type=\"button\" onClick={onClose} aria-label={closeLabel} style={{ background: 'none', border: 'none', cursor: 'pointer', fontSize: 18, lineHeight: 1 }}>\n ×\n </button>\n )}\n </div>\n )}\n <div style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 12 }}>{children}</div>\n {footer != null && (\n <div style={{ padding: 16, borderTop: '1px solid #d1d5db', display: 'flex', justifyContent: 'flex-end', gap: 8 }}>{footer}</div>\n )}\n </div>\n </div>\n )\n}\nModal.displayName = 'Modal'\n\n// ---------------------------------------------------------------------------\n// Alert\n// ---------------------------------------------------------------------------\n\nexport type AlertVariant = 'info' | 'success' | 'warning' | 'danger'\n\nexport interface AlertProps {\n /** Severity — drives color and default icon. Defaults to `info`. */\n variant?: AlertVariant\n /** Optional bold heading above the message. */\n title?: React.ReactNode\n /** The message body. */\n children?: React.ReactNode\n /** Override the default icon, or pass `false` to hide it. */\n icon?: React.ReactNode | false\n /** When provided, renders a dismiss (X) button that calls this. */\n onDismiss?: () => void\n /** Optional trailing action (e.g. a link or button). */\n action?: React.ReactNode\n className?: string\n}\n\nconst ALERT_FALLBACK_COLORS: Record<AlertVariant, { bg: string; fg: string; border: string }> = {\n info: { bg: '#eff6ff', fg: '#1e40af', border: '#93c5fd' },\n success: { bg: '#ecfdf5', fg: '#065f46', border: '#6ee7b7' },\n warning: { bg: '#fffbeb', fg: '#92400e', border: '#fcd34d' },\n danger: { bg: '#fef2f2', fg: '#991b1b', border: '#fca5a5' },\n}\n\n/**\n * Alert — delegates to the platform's real Alert at render time: an inline,\n * static severity banner for warnings, errors, tips and success notices in the\n * page/form body. For transient pop-up notifications use {@link useToast}\n * instead (its `variant` supports the same `warning`/`error`/`success`/`info`).\n * The fallback is a minimal, accessible colored banner for local dev / tests.\n *\n * @example\n * <Alert variant=\"warning\" title=\"Heads up\">\n * Distributed deployments need at least 3 indexers.\n * </Alert>\n */\nexport const Alert: React.FC<AlertProps> = (props) => {\n const HostAlert = getHostUi<React.FC<AlertProps>>('Alert')\n if (HostAlert) return <HostAlert {...props} />\n\n const { variant = 'info', title, children, icon, onDismiss, action, className } = props\n const c = ALERT_FALLBACK_COLORS[variant]\n const role = variant === 'danger' || variant === 'warning' ? 'alert' : 'status'\n\n return (\n <div\n role={role}\n className={className}\n style={{\n display: 'flex',\n alignItems: 'flex-start',\n gap: 10,\n border: `1px solid ${c.border}`,\n background: c.bg,\n color: c.fg,\n borderRadius: 8,\n padding: '12px 16px',\n ...fallbackNote,\n }}\n >\n {icon !== false && icon != null && <span style={{ flexShrink: 0 }}>{icon}</span>}\n <div style={{ minWidth: 0, flex: 1 }}>\n {title != null && <p style={{ margin: 0, fontSize: 14, fontWeight: 600 }}>{title}</p>}\n {children != null && (\n <div style={{ fontSize: 14, marginTop: title != null ? 2 : 0 }}>{children}</div>\n )}\n </div>\n {action != null && <div style={{ flexShrink: 0 }}>{action}</div>}\n {onDismiss && (\n <button\n type=\"button\"\n onClick={onDismiss}\n aria-label=\"Dismiss\"\n style={{ flexShrink: 0, background: 'none', border: 'none', cursor: 'pointer', color: 'inherit', opacity: 0.7 }}\n >\n ×\n </button>\n )}\n </div>\n )\n}\nAlert.displayName = 'Alert'\n\n// ---------------------------------------------------------------------------\n// MultiSelect\n// ---------------------------------------------------------------------------\n\nexport type MultiSelectSize = 'sm' | 'md' | 'lg'\n\nexport interface MultiSelectOption {\n value: string\n label: string\n disabled?: boolean\n}\n\nexport interface MultiSelectProps {\n options: MultiSelectOption[]\n /** Currently selected values. */\n value?: string[]\n /** Called with the new list of selected values. */\n onChange?: (values: string[]) => void\n placeholder?: string\n label?: string\n error?: string\n helperText?: string\n size?: MultiSelectSize\n disabled?: boolean\n fullWidth?: boolean\n /** Show a filter box inside the dropdown. Defaults to true. */\n searchable?: boolean\n /** How many chips to show in the trigger before \"+N more\". Defaults to 3. */\n maxTagCount?: number\n className?: string\n name?: string\n 'aria-label'?: string\n}\n\n/**\n * MultiSelect — delegates to the platform's real searchable multi-selection\n * dropdown at render time (selected values as removable chips, a filter box,\n * select-all / clear). The fallback is a native `<select multiple>` for local\n * dev / tests. Use for any multi-value picker (environments, tags, targets…).\n *\n * @example\n * <MultiSelect label=\"Environments\" value={ids} onChange={setIds}\n * options={envs.map(e => ({ value: e.id, label: e.name }))} />\n */\nexport const MultiSelect: React.FC<MultiSelectProps> = (props) => {\n const HostMultiSelect = getHostUi<React.FC<MultiSelectProps>>('MultiSelect')\n if (HostMultiSelect) return <HostMultiSelect {...props} />\n\n const { options, value = [], onChange, label, disabled, fullWidth = true, name } = props\n return (\n <label style={{ ...fallbackNote, display: 'block', width: fullWidth ? '100%' : undefined }}>\n {label && <span style={{ display: 'block', marginBottom: 4, fontSize: 14, fontWeight: 500 }}>{label}</span>}\n <select\n multiple\n name={name}\n disabled={disabled}\n value={value}\n aria-label={props['aria-label']}\n onChange={(e) => onChange?.(Array.from(e.target.selectedOptions, (o) => o.value))}\n style={{ width: '100%', minHeight: 96, padding: '6px 10px', borderRadius: 6, border: '1px solid #9ca3af' }}\n >\n {options.map((option) => (\n <option key={option.value} value={option.value} disabled={option.disabled}>\n {option.label}\n </option>\n ))}\n </select>\n </label>\n )\n}\nMultiSelect.displayName = 'MultiSelect'\n\n\n//\n// Unlike the components above, these are HOOKS the host backs with real React\n// context (ToastContext / ConfirmationDialogContext), whose providers the\n// platform's root App.tsx mounts around the entire tree — including app\n// pages, since they render inside the host's own React instance. So inside\n// Veltrix these resolve to the real, working implementation with no extra\n// wiring. Outside the platform (no host, or an older host) they degrade to a\n// safe, non-throwing fallback instead of crashing: `toast()` logs to the\n// console and `confirm()` resolves to `false` (fails closed — no destructive\n// action proceeds without an explicit user confirmation the fallback cannot\n// provide).\n// ---------------------------------------------------------------------------\n\nexport type ToastVariant = 'success' | 'error' | 'warning' | 'info'\n\nexport interface ToastOptions {\n variant?: ToastVariant\n duration?: number\n action?: { label: string; onClick: () => void }\n}\n\nexport interface Toast {\n id: string\n message: string\n variant: ToastVariant\n duration?: number\n action?: { label: string; onClick: () => void }\n}\n\nexport interface ToastContextValue {\n toasts: Toast[]\n toast: (message: string, options?: ToastOptions) => string\n success: (message: string, duration?: number) => string\n error: (message: string, duration?: number) => string\n warning: (message: string, duration?: number) => string\n info: (message: string, duration?: number) => string\n promise: <T>(\n promise: Promise<T>,\n messages: { loading: string; success: string | ((data: T) => string); error: string | ((error: unknown) => string) },\n ) => Promise<T>\n dismiss: (id: string) => void\n dismissAll: () => void\n}\n\nfunction fallbackToast(message: string, options?: ToastOptions): string {\n // eslint-disable-next-line no-console -- deliberate: this IS the fallback surface.\n console.warn(`[@veltrixsecops/app-sdk/ui] Toast (${options?.variant ?? 'info'}): ${message}`)\n return 'fallback-toast'\n}\n\nconst fallbackToastContext: ToastContextValue = {\n toasts: [],\n toast: fallbackToast,\n success: (message) => fallbackToast(message, { variant: 'success' }),\n error: (message) => fallbackToast(message, { variant: 'error' }),\n warning: (message) => fallbackToast(message, { variant: 'warning' }),\n info: (message) => fallbackToast(message, { variant: 'info' }),\n promise: (promise) => promise,\n dismiss: () => {},\n dismissAll: () => {},\n}\n\n/**\n * useToast — delegates to the platform's real toast system when running inside Veltrix.\n * Outside it, `toast()`/`success()`/etc. log to the console instead of throwing.\n *\n * @example\n * const toast = useToast()\n * toast.success('Index saved')\n */\nexport function useToast(): ToastContextValue {\n const hostUseToast = getHostUi<() => ToastContextValue>('useToast')\n // eslint-disable-next-line react-hooks/rules-of-hooks -- `hostUseToast` IS the real hook;\n // its presence is fixed for the app's lifetime (the host runtime installs once at boot),\n // so this never actually violates the \"same hooks every render\" invariant in practice.\n if (hostUseToast) return hostUseToast()\n return fallbackToastContext\n}\n\nexport type ConfirmationVariant = 'danger' | 'warning' | 'info'\n\nexport interface ConfirmationOptions {\n title: string\n message: string\n confirmText?: string\n cancelText?: string\n variant?: ConfirmationVariant\n}\n\nexport interface ConfirmationDialogContextValue {\n confirm: (options: ConfirmationOptions) => Promise<boolean>\n}\n\nconst fallbackConfirmationContext: ConfirmationDialogContextValue = {\n confirm: (options) => {\n // eslint-disable-next-line no-console -- deliberate: this IS the fallback surface.\n console.warn(\n `[@veltrixsecops/app-sdk/ui] ConfirmationDialog is only available inside the Veltrix platform — ` +\n `\"${options.title}\" auto-resolved to \"not confirmed\" (fail closed).`,\n )\n return Promise.resolve(false)\n },\n}\n\n/**\n * useConfirmDialog — delegates to the platform's real confirmation dialog when running\n * inside Veltrix. Outside it, `confirm()` resolves to `false` (fails closed) rather than\n * throwing, so a guarded destructive action simply never proceeds instead of crashing.\n *\n * @example\n * const { confirm } = useConfirmDialog()\n * if (await confirm({ title: 'Delete index', message: 'This cannot be undone.', variant: 'danger' })) {\n * await deleteIndex(id)\n * }\n */\nexport function useConfirmDialog(): ConfirmationDialogContextValue {\n const hostUseConfirmDialog = getHostUi<() => ConfirmationDialogContextValue>('useConfirmDialog')\n // eslint-disable-next-line react-hooks/rules-of-hooks -- see useToast's note above.\n if (hostUseConfirmDialog) return hostUseConfirmDialog()\n return fallbackConfirmationContext\n}\n"],"mappings":"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","names":["import_jsx_runtime"]}
@@ -0,0 +1,78 @@
1
+ import React__default from 'react';
2
+
3
+ interface ConnectionsManagerProps {
4
+ /**
5
+ * Platform Tool name — MUST equal the app's manifest `name`. `resolveTool`
6
+ * matches on it so new connections attach to this app's tool.
7
+ */
8
+ appName: string;
9
+ /** Manifest id — used to reach this app's connectivity-test route. */
10
+ appId: string;
11
+ /** Card title. Defaults to "Connections". */
12
+ title?: string;
13
+ /** Placeholder for the connection Name field. */
14
+ namePlaceholder?: string;
15
+ /** Placeholder + helper for the Endpoint field. */
16
+ endpointPlaceholder?: string;
17
+ endpointHelper?: string;
18
+ /**
19
+ * Label for the account / key field (the credential's `username`). Defaults to
20
+ * "Username"; pass e.g. "Access key" (Tenable) or "Client ID" (OAuth apps).
21
+ */
22
+ usernameLabel?: string;
23
+ /**
24
+ * Label for the token/secret auth method + its secret field. Defaults to
25
+ * "API token"; pass e.g. "API key" (Elastic), "Client secret" (OAuth),
26
+ * "Secret key" (Tenable).
27
+ */
28
+ tokenLabel?: string;
29
+ /** Placeholder for the username field when token auth is selected. */
30
+ tokenUsernamePlaceholder?: string;
31
+ /** Placeholder for the username field when password auth is selected. */
32
+ passwordUsernamePlaceholder?: string;
33
+ /**
34
+ * Whether the username field is optional under token auth (appends "(optional)"
35
+ * to its label). Defaults to true — pass false for OAuth/key apps where the
36
+ * username IS the required identifier (e.g. Client ID, Access key).
37
+ */
38
+ usernameOptionalForToken?: boolean;
39
+ /**
40
+ * One-click onboarding descriptor (the client-safe subset the platform
41
+ * advertises in the app's `/enabled` payload as `connection.onboarding`). When
42
+ * provided, a primary "Connect …" button drives the platform onboarding flow
43
+ * (e.g. Entra admin consent) — no secret is entered by the user. Omit it and
44
+ * the manager renders exactly as before (manual "Add connection" only).
45
+ */
46
+ onboarding?: OnboardingDescriptorSummary;
47
+ /**
48
+ * The platform component type this app deploys to (a config type's
49
+ * `targets.componentTypes`, e.g. "okta-org"). When set, saving a connection
50
+ * ALSO registers/updates a deploy-target Component — hostname taken from the
51
+ * endpoint, linked to the new credential + environment — so the app can
52
+ * actually deploy. A connection alone is only a credential, not a deploy
53
+ * target, so without this Deploy stays disabled. Omit for apps that manage
54
+ * their own components.
55
+ */
56
+ componentType?: string;
57
+ }
58
+ /** Client-safe onboarding descriptor from the `/enabled` payload. */
59
+ interface OnboardingDescriptorSummary {
60
+ provider: string;
61
+ /** Button/label text, e.g. "Connect Microsoft Defender". */
62
+ label: string;
63
+ /** True → brokered (no secret ever entered by the user). */
64
+ brokered: boolean;
65
+ /** App settings the admin must supply before the consent redirect. */
66
+ requiredSettings: string[];
67
+ }
68
+ /**
69
+ * Reusable Connections manager: full credential CRUD (username/password or
70
+ * token, plus the API endpoint each reaches), tied to an Environment, with a
71
+ * per-row "Test connectivity" button that runs the app's own testConnection
72
+ * handler. Every app renders this with its own `appName`/`appId` and auth labels;
73
+ * the data (credentials) stays platform-owned. Secrets are write-only — set here,
74
+ * never read back.
75
+ */
76
+ declare const ConnectionsManager: React__default.FC<ConnectionsManagerProps>;
77
+
78
+ export { ConnectionsManager, type ConnectionsManagerProps, type OnboardingDescriptorSummary, ConnectionsManager as default };
@@ -0,0 +1,78 @@
1
+ import React__default from 'react';
2
+
3
+ interface ConnectionsManagerProps {
4
+ /**
5
+ * Platform Tool name — MUST equal the app's manifest `name`. `resolveTool`
6
+ * matches on it so new connections attach to this app's tool.
7
+ */
8
+ appName: string;
9
+ /** Manifest id — used to reach this app's connectivity-test route. */
10
+ appId: string;
11
+ /** Card title. Defaults to "Connections". */
12
+ title?: string;
13
+ /** Placeholder for the connection Name field. */
14
+ namePlaceholder?: string;
15
+ /** Placeholder + helper for the Endpoint field. */
16
+ endpointPlaceholder?: string;
17
+ endpointHelper?: string;
18
+ /**
19
+ * Label for the account / key field (the credential's `username`). Defaults to
20
+ * "Username"; pass e.g. "Access key" (Tenable) or "Client ID" (OAuth apps).
21
+ */
22
+ usernameLabel?: string;
23
+ /**
24
+ * Label for the token/secret auth method + its secret field. Defaults to
25
+ * "API token"; pass e.g. "API key" (Elastic), "Client secret" (OAuth),
26
+ * "Secret key" (Tenable).
27
+ */
28
+ tokenLabel?: string;
29
+ /** Placeholder for the username field when token auth is selected. */
30
+ tokenUsernamePlaceholder?: string;
31
+ /** Placeholder for the username field when password auth is selected. */
32
+ passwordUsernamePlaceholder?: string;
33
+ /**
34
+ * Whether the username field is optional under token auth (appends "(optional)"
35
+ * to its label). Defaults to true — pass false for OAuth/key apps where the
36
+ * username IS the required identifier (e.g. Client ID, Access key).
37
+ */
38
+ usernameOptionalForToken?: boolean;
39
+ /**
40
+ * One-click onboarding descriptor (the client-safe subset the platform
41
+ * advertises in the app's `/enabled` payload as `connection.onboarding`). When
42
+ * provided, a primary "Connect …" button drives the platform onboarding flow
43
+ * (e.g. Entra admin consent) — no secret is entered by the user. Omit it and
44
+ * the manager renders exactly as before (manual "Add connection" only).
45
+ */
46
+ onboarding?: OnboardingDescriptorSummary;
47
+ /**
48
+ * The platform component type this app deploys to (a config type's
49
+ * `targets.componentTypes`, e.g. "okta-org"). When set, saving a connection
50
+ * ALSO registers/updates a deploy-target Component — hostname taken from the
51
+ * endpoint, linked to the new credential + environment — so the app can
52
+ * actually deploy. A connection alone is only a credential, not a deploy
53
+ * target, so without this Deploy stays disabled. Omit for apps that manage
54
+ * their own components.
55
+ */
56
+ componentType?: string;
57
+ }
58
+ /** Client-safe onboarding descriptor from the `/enabled` payload. */
59
+ interface OnboardingDescriptorSummary {
60
+ provider: string;
61
+ /** Button/label text, e.g. "Connect Microsoft Defender". */
62
+ label: string;
63
+ /** True → brokered (no secret ever entered by the user). */
64
+ brokered: boolean;
65
+ /** App settings the admin must supply before the consent redirect. */
66
+ requiredSettings: string[];
67
+ }
68
+ /**
69
+ * Reusable Connections manager: full credential CRUD (username/password or
70
+ * token, plus the API endpoint each reaches), tied to an Environment, with a
71
+ * per-row "Test connectivity" button that runs the app's own testConnection
72
+ * handler. Every app renders this with its own `appName`/`appId` and auth labels;
73
+ * the data (credentials) stays platform-owned. Secrets are write-only — set here,
74
+ * never read back.
75
+ */
76
+ declare const ConnectionsManager: React__default.FC<ConnectionsManagerProps>;
77
+
78
+ export { ConnectionsManager, type ConnectionsManagerProps, type OnboardingDescriptorSummary, ConnectionsManager as default };