@veltrixsecops/app-sdk 3.2.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 (77) 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-EOOEHZGC.js → chunk-HIL6RIDG.js} +107 -3
  11. package/dist/chunk-HIL6RIDG.js.map +1 -0
  12. package/dist/{chunk-VWFTOFTI.js → chunk-Y6XBNZGR.js} +8 -2
  13. package/dist/chunk-Y6XBNZGR.js.map +1 -0
  14. package/dist/client/index.cjs +112 -2
  15. package/dist/client/index.cjs.map +1 -1
  16. package/dist/client/index.d.cts +118 -6
  17. package/dist/client/index.d.ts +118 -6
  18. package/dist/client/index.js +13 -1
  19. package/dist/connections/index.cjs +1787 -0
  20. package/dist/connections/index.cjs.map +1 -0
  21. package/dist/connections/index.d.cts +78 -0
  22. package/dist/connections/index.d.ts +78 -0
  23. package/dist/connections/index.js +754 -0
  24. package/dist/connections/index.js.map +1 -0
  25. package/dist/hooks/index.cjs.map +1 -1
  26. package/dist/hooks/index.d.cts +2 -2
  27. package/dist/hooks/index.d.ts +2 -2
  28. package/dist/hooks/index.js +1 -1
  29. package/dist/index.cjs +7 -0
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +3 -3
  32. package/dist/index.d.ts +3 -3
  33. package/dist/index.js +3 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/opentofu/index.cjs +138 -0
  36. package/dist/opentofu/index.cjs.map +1 -0
  37. package/dist/opentofu/index.d.cts +131 -0
  38. package/dist/opentofu/index.d.ts +131 -0
  39. package/dist/opentofu/index.js +109 -0
  40. package/dist/opentofu/index.js.map +1 -0
  41. package/dist/pipeline/index.cjs +7 -0
  42. package/dist/pipeline/index.cjs.map +1 -1
  43. package/dist/pipeline/index.d.cts +9 -2
  44. package/dist/pipeline/index.d.ts +9 -2
  45. package/dist/pipeline/index.js +3 -1
  46. package/dist/pipeline-beeT8dje.d.cts +408 -0
  47. package/dist/pipeline-beeT8dje.d.ts +408 -0
  48. package/dist/ui/index.cjs +136 -0
  49. package/dist/ui/index.cjs.map +1 -1
  50. package/dist/ui/index.d.cts +109 -1
  51. package/dist/ui/index.d.ts +109 -1
  52. package/dist/ui/index.js +35 -677
  53. package/dist/ui/index.js.map +1 -1
  54. package/dist/{use-app-context-OQlcmq7t.d.cts → use-app-context-Byv_fam0.d.cts} +206 -4
  55. package/dist/{use-app-context-OQlcmq7t.d.ts → use-app-context-Byv_fam0.d.ts} +206 -4
  56. package/opentofu/README.md +114 -0
  57. package/opentofu/modules/aws/main.tf +1008 -0
  58. package/opentofu/modules/aws/outputs.tf +90 -0
  59. package/opentofu/modules/aws/variables.tf +437 -0
  60. package/opentofu/modules/aws/versions.tf +23 -0
  61. package/opentofu/modules/azure/main.tf +920 -0
  62. package/opentofu/modules/azure/outputs.tf +72 -0
  63. package/opentofu/modules/azure/variables.tf +498 -0
  64. package/opentofu/modules/azure/versions.tf +27 -0
  65. package/opentofu/modules/gcp/main.tf +645 -0
  66. package/opentofu/modules/gcp/outputs.tf +73 -0
  67. package/opentofu/modules/gcp/variables.tf +428 -0
  68. package/opentofu/modules/gcp/versions.tf +27 -0
  69. package/opentofu/modules/hetzner/main.tf +432 -0
  70. package/opentofu/modules/hetzner/outputs.tf +72 -0
  71. package/opentofu/modules/hetzner/variables.tf +423 -0
  72. package/opentofu/modules/hetzner/versions.tf +26 -0
  73. package/package.json +33 -1
  74. package/dist/chunk-EOOEHZGC.js.map +0 -1
  75. package/dist/chunk-VWFTOFTI.js.map +0 -1
  76. package/dist/pipeline-A-gSsPRR.d.cts +0 -189
  77. package/dist/pipeline-A-gSsPRR.d.ts +0 -189
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/connections/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"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/index.ts","../../src/hooks/use-app-context.ts","../../src/hooks/use-pipeline-status.ts","../../src/client/index.ts","../../src/hooks/use-app-branding.ts","../../src/hooks/use-permissions.ts"],"sourcesContent":["export { useAppContext, AppContext, type AppContextValue } from './use-app-context'\nexport { usePipelineStatus, type PipelineStatusData } from './use-pipeline-status'\nexport { useAppBranding } from './use-app-branding'\nexport { usePermissions } from './use-permissions'\n","// ========================================================================\n// React hooks for app developers\n// These provide access to platform data and pipeline state\n// ========================================================================\n\nimport { createContext, useContext, type Context } from 'react'\nimport type { Component, Credential, Tag, User, Customer, AppPermissionsApi } from '../types/platform'\nimport type { AppBrandingDeclaration } from '../types/manifest'\n\nexport interface AppContextValue {\n appId: string\n customerId: string\n user: User | null\n customer: Customer | null\n\n // Platform data accessors\n getComponents: () => Promise<Component[]>\n getCredentials: () => Promise<Credential[]>\n getTags: () => Promise<Tag[]>\n\n // App settings\n settings: Record<string, unknown>\n\n /** The app's manifest branding, resolved by the platform (null when unset). */\n branding?: AppBrandingDeclaration | null\n\n /**\n * Permission checks for THIS app (RBAC/IdP hardening, Wave C4). `has()`\n * without an explicit `opts.appId` checks the app's OWN declared\n * resources by default. Prefer the `usePermissions()` hook\n * (`@veltrixsecops/app-sdk/hooks`) over reaching into this directly.\n */\n permissions: AppPermissionsApi\n}\n\n// Anchor the context on the host runtime when running inside the platform,\n// so a bundle that inlined its own SDK copy still shares the host's context\n// (two createContext objects never match, even with identical shapes).\nconst hostContext = (\n (globalThis as Record<string, unknown>).__VELTRIX_APP_RUNTIME__ as\n | { AppContext?: Context<AppContextValue | null> }\n | undefined\n)?.AppContext\n\nexport const AppContext: Context<AppContextValue | null> =\n hostContext ?? createContext<AppContextValue | null>(null)\n\n/**\n * Access the app context in any app component.\n *\n * @example\n * ```tsx\n * import { useAppContext } from '@veltrixsecops/app-sdk/hooks'\n *\n * function MyComponent() {\n * const { appId, settings, getComponents } = useAppContext()\n * // ...\n * }\n * ```\n */\nexport function useAppContext(): AppContextValue {\n const ctx = useContext(AppContext)\n if (!ctx) {\n throw new Error('useAppContext must be used within an AppContextProvider')\n }\n return ctx\n}\n","import { useState, useEffect, useCallback } from 'react'\nimport { authFetch } from '../client'\n\nexport interface PipelineStatusData {\n pendingApprovals: number\n activeDeployments: number\n failedDeployments: number\n unresolvedDrifts: number\n recentDeployments: Array<{\n id: string\n canvasName: string\n environment: string\n status: string\n startedAt: string\n completedAt?: string\n }>\n}\n\n/**\n * Hook to access pipeline status for the current app/customer.\n *\n * @example\n * ```tsx\n * import { usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'\n *\n * function Dashboard() {\n * const { data, isLoading } = usePipelineStatus('my-app')\n * if (isLoading) return <Spinner />\n * return <div>{data.activeDeployments} active deployments</div>\n * }\n * ```\n */\nexport function usePipelineStatus(appId: string) {\n const [data, setData] = useState<PipelineStatusData | null>(null)\n const [isLoading, setIsLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n const refresh = useCallback(async () => {\n try {\n setIsLoading(true)\n // Platform APIs are bearer-token protected — plain fetch would 401\n const response = await authFetch(`/api/pipeline/summary?appId=${appId}`)\n if (!response.ok) throw new Error(`Failed to fetch pipeline status`)\n const result = await response.json()\n setData(result)\n setError(null)\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Unknown error'))\n } finally {\n setIsLoading(false)\n }\n }, [appId])\n\n useEffect(() => {\n refresh()\n }, [refresh])\n\n return { data, isLoading, error, refresh }\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} from './credentials'\nexport type { Credential, CredentialSummary, CredentialInput } from '../types/platform'\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","import { useAppContext } from './use-app-context'\nimport type { AppBrandingDeclaration } from '../types/manifest'\n\n/**\n * The app's brand identity as declared in manifest.yaml `branding`.\n *\n * The platform already applies it in the defined slots (the app navbar and\n * the scoped CSS variables --veltrix-app-primary / --veltrix-app-accent on\n * the app page container). Use this hook only when a page needs the values\n * programmatically — prefer the CSS variables for styling:\n *\n * @example\n * ```tsx\n * <span style={{ color: 'var(--veltrix-app-primary)' }}>12 detections</span>\n * ```\n */\nexport function useAppBranding(): AppBrandingDeclaration | null {\n return useAppContext().branding ?? null\n}\n","import { useAppContext } from './use-app-context'\nimport type { AppPermissionsApi } from '../types/platform'\n\n/**\n * Permission checks for THIS app (RBAC/IdP hardening, Wave C4). `has()`\n * without an explicit `opts.appId` checks the app's own declared resources\n * by default — pass `{ appId: null }` for a platform resource, or another\n * app's id to check a different app.\n *\n * @example\n * ```tsx\n * import { usePermissions } from '@veltrixsecops/app-sdk/hooks'\n *\n * function IndexesPage() {\n * const { has } = usePermissions()\n * if (!has('indexes', 'write')) {\n * return <p>You don't have permission to edit indexes.</p>\n * }\n * // ...\n * }\n * ```\n */\nexport function usePermissions(): AppPermissionsApi {\n return useAppContext().permissions\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,mBAAwD;AAiCxD,IAAM,cACH,WAAuC,yBAGvC;AAEI,IAAM,aACX,mBAAe,4BAAsC,IAAI;AAepD,SAAS,gBAAiC;AAC/C,QAAM,UAAM,yBAAW,UAAU;AACjC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,SAAO;AACT;;;AClEA,IAAAA,gBAAiD;;;AC4D1C,IAAM,sBAAsB;AA8C5B,SAAS,iBAA4C;AAC1D,QAAM,UAAW,WAAuC,mBAAmB;AAC3E,SAAQ,WAAkC;AAC5C;AAmBO,SAAS,UAAU,OAAe,MAAuC;AAC9E,QAAM,UAAU,eAAe;AAC/B,MAAI,QAAS,QAAO,QAAQ,UAAU,OAAO,IAAI;AACjD,SAAO,MAAM,OAAO,IAAI;AAC1B;;;ADpGO,SAAS,kBAAkB,OAAe;AAC/C,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAoC,IAAI;AAChE,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,IAAI;AAC/C,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,IAAI;AAErD,QAAM,cAAU,2BAAY,YAAY;AACtC,QAAI;AACF,mBAAa,IAAI;AAEjB,YAAM,WAAW,MAAM,UAAU,+BAA+B,KAAK,EAAE;AACvE,UAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,iCAAiC;AACnE,YAAM,SAAS,MAAM,SAAS,KAAK;AACnC,cAAQ,MAAM;AACd,eAAS,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,eAAe,CAAC;AAAA,IAClE,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,KAAK,CAAC;AAEV,+BAAU,MAAM;AACd,YAAQ;AAAA,EACV,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,EAAE,MAAM,WAAW,OAAO,QAAQ;AAC3C;;;AE1CO,SAAS,iBAAgD;AAC9D,SAAO,cAAc,EAAE,YAAY;AACrC;;;ACIO,SAAS,iBAAoC;AAClD,SAAO,cAAc,EAAE;AACzB;","names":["import_react"]}
1
+ {"version":3,"sources":["../../src/hooks/index.ts","../../src/hooks/use-app-context.ts","../../src/hooks/use-pipeline-status.ts","../../src/client/index.ts","../../src/hooks/use-app-branding.ts","../../src/hooks/use-permissions.ts"],"sourcesContent":["export { useAppContext, AppContext, type AppContextValue } from './use-app-context'\nexport { usePipelineStatus, type PipelineStatusData } from './use-pipeline-status'\nexport { useAppBranding } from './use-app-branding'\nexport { usePermissions } from './use-permissions'\n","// ========================================================================\n// React hooks for app developers\n// These provide access to platform data and pipeline state\n// ========================================================================\n\nimport { createContext, useContext, type Context } from 'react'\nimport type { Component, Credential, Tag, User, Customer, AppPermissionsApi } from '../types/platform'\nimport type { AppBrandingDeclaration } from '../types/manifest'\n\nexport interface AppContextValue {\n appId: string\n customerId: string\n user: User | null\n customer: Customer | null\n\n // Platform data accessors\n getComponents: () => Promise<Component[]>\n getCredentials: () => Promise<Credential[]>\n getTags: () => Promise<Tag[]>\n\n // App settings\n settings: Record<string, unknown>\n\n /** The app's manifest branding, resolved by the platform (null when unset). */\n branding?: AppBrandingDeclaration | null\n\n /**\n * Permission checks for THIS app (RBAC/IdP hardening, Wave C4). `has()`\n * without an explicit `opts.appId` checks the app's OWN declared\n * resources by default. Prefer the `usePermissions()` hook\n * (`@veltrixsecops/app-sdk/hooks`) over reaching into this directly.\n */\n permissions: AppPermissionsApi\n}\n\n// Anchor the context on the host runtime when running inside the platform,\n// so a bundle that inlined its own SDK copy still shares the host's context\n// (two createContext objects never match, even with identical shapes).\nconst hostContext = (\n (globalThis as Record<string, unknown>).__VELTRIX_APP_RUNTIME__ as\n | { AppContext?: Context<AppContextValue | null> }\n | undefined\n)?.AppContext\n\nexport const AppContext: Context<AppContextValue | null> =\n hostContext ?? createContext<AppContextValue | null>(null)\n\n/**\n * Access the app context in any app component.\n *\n * @example\n * ```tsx\n * import { useAppContext } from '@veltrixsecops/app-sdk/hooks'\n *\n * function MyComponent() {\n * const { appId, settings, getComponents } = useAppContext()\n * // ...\n * }\n * ```\n */\nexport function useAppContext(): AppContextValue {\n const ctx = useContext(AppContext)\n if (!ctx) {\n throw new Error('useAppContext must be used within an AppContextProvider')\n }\n return ctx\n}\n","import { useState, useEffect, useCallback } from 'react'\nimport { authFetch } from '../client'\n\nexport interface PipelineStatusData {\n pendingApprovals: number\n activeDeployments: number\n failedDeployments: number\n unresolvedDrifts: number\n recentDeployments: Array<{\n id: string\n canvasName: string\n environment: string\n status: string\n startedAt: string\n completedAt?: string\n }>\n}\n\n/**\n * Hook to access pipeline status for the current app/customer.\n *\n * @example\n * ```tsx\n * import { usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'\n *\n * function Dashboard() {\n * const { data, isLoading } = usePipelineStatus('my-app')\n * if (isLoading) return <Spinner />\n * return <div>{data.activeDeployments} active deployments</div>\n * }\n * ```\n */\nexport function usePipelineStatus(appId: string) {\n const [data, setData] = useState<PipelineStatusData | null>(null)\n const [isLoading, setIsLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n const refresh = useCallback(async () => {\n try {\n setIsLoading(true)\n // Platform APIs are bearer-token protected — plain fetch would 401\n const response = await authFetch(`/api/pipeline/summary?appId=${appId}`)\n if (!response.ok) throw new Error(`Failed to fetch pipeline status`)\n const result = await response.json()\n setData(result)\n setError(null)\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Unknown error'))\n } finally {\n setIsLoading(false)\n }\n }, [appId])\n\n useEffect(() => {\n refresh()\n }, [refresh])\n\n return { data, isLoading, error, refresh }\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","import { useAppContext } from './use-app-context'\nimport type { AppBrandingDeclaration } from '../types/manifest'\n\n/**\n * The app's brand identity as declared in manifest.yaml `branding`.\n *\n * The platform already applies it in the defined slots (the app navbar and\n * the scoped CSS variables --veltrix-app-primary / --veltrix-app-accent on\n * the app page container). Use this hook only when a page needs the values\n * programmatically — prefer the CSS variables for styling:\n *\n * @example\n * ```tsx\n * <span style={{ color: 'var(--veltrix-app-primary)' }}>12 detections</span>\n * ```\n */\nexport function useAppBranding(): AppBrandingDeclaration | null {\n return useAppContext().branding ?? null\n}\n","import { useAppContext } from './use-app-context'\nimport type { AppPermissionsApi } from '../types/platform'\n\n/**\n * Permission checks for THIS app (RBAC/IdP hardening, Wave C4). `has()`\n * without an explicit `opts.appId` checks the app's own declared resources\n * by default — pass `{ appId: null }` for a platform resource, or another\n * app's id to check a different app.\n *\n * @example\n * ```tsx\n * import { usePermissions } from '@veltrixsecops/app-sdk/hooks'\n *\n * function IndexesPage() {\n * const { has } = usePermissions()\n * if (!has('indexes', 'write')) {\n * return <p>You don't have permission to edit indexes.</p>\n * }\n * // ...\n * }\n * ```\n */\nexport function usePermissions(): AppPermissionsApi {\n return useAppContext().permissions\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,mBAAwD;AAiCxD,IAAM,cACH,WAAuC,yBAGvC;AAEI,IAAM,aACX,mBAAe,4BAAsC,IAAI;AAepD,SAAS,gBAAiC;AAC/C,QAAM,UAAM,yBAAW,UAAU;AACjC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,SAAO;AACT;;;AClEA,IAAAA,gBAAiD;;;AC0E1C,IAAM,sBAAsB;AA8C5B,SAAS,iBAA4C;AAC1D,QAAM,UAAW,WAAuC,mBAAmB;AAC3E,SAAQ,WAAkC;AAC5C;AAmBO,SAAS,UAAU,OAAe,MAAuC;AAC9E,QAAM,UAAU,eAAe;AAC/B,MAAI,QAAS,QAAO,QAAQ,UAAU,OAAO,IAAI;AACjD,SAAO,MAAM,OAAO,IAAI;AAC1B;;;ADlHO,SAAS,kBAAkB,OAAe;AAC/C,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAoC,IAAI;AAChE,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,IAAI;AAC/C,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,IAAI;AAErD,QAAM,cAAU,2BAAY,YAAY;AACtC,QAAI;AACF,mBAAa,IAAI;AAEjB,YAAM,WAAW,MAAM,UAAU,+BAA+B,KAAK,EAAE;AACvE,UAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,iCAAiC;AACnE,YAAM,SAAS,MAAM,SAAS,KAAK;AACnC,cAAQ,MAAM;AACd,eAAS,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,eAAe,CAAC;AAAA,IAClE,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,KAAK,CAAC;AAEV,+BAAU,MAAM;AACd,YAAQ;AAAA,EACV,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,EAAE,MAAM,WAAW,OAAO,QAAQ;AAC3C;;;AE1CO,SAAS,iBAAgD;AAC9D,SAAO,cAAc,EAAE,YAAY;AACrC;;;ACIO,SAAS,iBAAoC;AAClD,SAAO,cAAc,EAAE;AACzB;","names":["import_react"]}
@@ -1,5 +1,5 @@
1
- import { b as AppBrandingDeclaration, p as AppPermissionsApi } from '../use-app-context-OQlcmq7t.cjs';
2
- export { F as AppContext, d as AppContextValue, G as useAppContext } from '../use-app-context-OQlcmq7t.cjs';
1
+ import { b as AppBrandingDeclaration, r as AppPermissionsApi } from '../use-app-context-Byv_fam0.cjs';
2
+ export { J as AppContext, d as AppContextValue, K as useAppContext } from '../use-app-context-Byv_fam0.cjs';
3
3
  export { P as PipelineStatusData, u as usePipelineStatus } from '../use-pipeline-status-CCSQ9Neh.cjs';
4
4
  import 'react';
5
5
 
@@ -1,5 +1,5 @@
1
- import { b as AppBrandingDeclaration, p as AppPermissionsApi } from '../use-app-context-OQlcmq7t.js';
2
- export { F as AppContext, d as AppContextValue, G as useAppContext } from '../use-app-context-OQlcmq7t.js';
1
+ import { b as AppBrandingDeclaration, r as AppPermissionsApi } from '../use-app-context-Byv_fam0.js';
2
+ export { J as AppContext, d as AppContextValue, K as useAppContext } from '../use-app-context-Byv_fam0.js';
3
3
  export { P as PipelineStatusData, u as usePipelineStatus } from '../use-pipeline-status-CCSQ9Neh.js';
4
4
  import 'react';
5
5
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  authFetch
3
- } from "../chunk-EOOEHZGC.js";
3
+ } from "../chunk-HIL6RIDG.js";
4
4
 
5
5
  // src/hooks/use-app-context.ts
6
6
  import { createContext, useContext } from "react";
package/dist/index.cjs CHANGED
@@ -25,6 +25,7 @@ __export(src_exports, {
25
25
  APP_PAGE_NAV: () => APP_PAGE_NAV,
26
26
  HANDLER_NAMES: () => HANDLER_NAMES,
27
27
  conventionalPaths: () => conventionalPaths,
28
+ defineConnectionTester: () => defineConnectionTester,
28
29
  defineDeployer: () => defineDeployer,
29
30
  defineDriftDetector: () => defineDriftDetector,
30
31
  defineHealthChecker: () => defineHealthChecker,
@@ -58,6 +59,11 @@ function defineDriftDetector(handler) {
58
59
  return handler;
59
60
  }
60
61
 
62
+ // src/pipeline/define-connection-tester.ts
63
+ function defineConnectionTester(handler) {
64
+ return handler;
65
+ }
66
+
61
67
  // src/types/manifest.ts
62
68
  var APP_PAGE_LAYOUTS = ["standard", "full-bleed", "canvas"];
63
69
  var APP_PAGE_NAV = ["sidebar", "tab", "hidden"];
@@ -118,6 +124,7 @@ function conventionalPaths(configTypeId) {
118
124
  APP_PAGE_NAV,
119
125
  HANDLER_NAMES,
120
126
  conventionalPaths,
127
+ defineConnectionTester,
121
128
  defineDeployer,
122
129
  defineDriftDetector,
123
130
  defineHealthChecker,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/pipeline/define-validator.ts","../src/pipeline/define-deployer.ts","../src/pipeline/define-rollback-handler.ts","../src/pipeline/define-health-checker.ts","../src/pipeline/define-drift-detector.ts","../src/types/manifest.ts","../src/structure.ts"],"sourcesContent":["// ========================================================================\n// @veltrixsecops/app-sdk\n//\n// The official SDK for building Veltrix Security-as-Code apps.\n//\n// This root entry is REACT-FREE and safe to load in a bare Node process.\n// Pipeline handlers execute inside the platform's sandbox runner — a child\n// process with a scrubbed environment and no React — so the contract they\n// import must never pull in a UI dependency. (Before 2.0 this entry\n// re-exported the React hooks, which made `require('@veltrixsecops/app-sdk')`\n// load React.) The platform can therefore import HANDLER_NAMES and the\n// handler contracts directly, instead of mirroring them server-side.\n//\n// Usage:\n// import type { PipelineContext, DeployContext } from '@veltrixsecops/app-sdk'\n// import { HANDLER_NAMES, conventionalPaths } from '@veltrixsecops/app-sdk'\n// import { defineValidator, defineDeployer } from '@veltrixsecops/app-sdk/pipeline'\n// import { useAppContext, usePipelineStatus } from '@veltrixsecops/app-sdk/hooks' // React\n// import { authFetch } from '@veltrixsecops/app-sdk/client' // browser\n// ========================================================================\n\n// Pipeline handler helpers (React-free)\nexport {\n defineValidator,\n defineDeployer,\n defineRollbackHandler,\n defineHealthChecker,\n defineDriftDetector,\n} from './pipeline'\n\n// NOTE: React hooks are NOT re-exported here. Import them from\n// '@veltrixsecops/app-sdk/hooks'; browser client helpers live in\n// '@veltrixsecops/app-sdk/client'. Keeping them off the root is what makes\n// this entry loadable in the sandbox runner's bare Node child process.\n\n// Pipeline types\nexport type {\n PipelineContext,\n DeployContext,\n RollbackContext,\n HealthCheckContext,\n DriftContext,\n ValidationResult,\n ValidationError,\n ValidationWarning,\n DeployResult,\n RollbackResult,\n HealthCheckResult,\n HealthCheck,\n DriftResult,\n DriftDiff,\n ConfigStatus,\n ComponentConfigStatus,\n CanvasSnapshot,\n CanvasSectionSnapshot,\n DeploymentStrategy,\n EnvironmentRef,\n UserRef,\n ComponentRef,\n CredentialRef,\n ConnectivityRef,\n PlatformDataApi,\n DeploymentSummary,\n ValidateHandler,\n DeployHandler,\n RollbackHandler,\n HealthCheckHandler,\n DriftDetectHandler,\n GetStatusHandler,\n} from './types/pipeline'\n\n// Platform types\nexport type {\n Component,\n InventoryItem,\n InventoryItemInput,\n ConnectivityProviderRef,\n Credential,\n CredentialSummary,\n CredentialInput,\n Connectivity,\n Tag,\n User,\n Customer,\n PlatformDatabaseClient,\n AppHookContext,\n AppRouteContext,\n PermissionEntry,\n PermissionCheckOptions,\n AppPermissionsApi,\n} from './types/platform'\n\n// App UI & navigation contract\nexport { APP_PAGE_LAYOUTS, APP_PAGE_NAV } from './types/manifest'\nexport type { AppPageLayout, AppPageNav, AppPagePermission } from './types/manifest'\n\n// Manifest types\nexport type {\n AppManifest,\n AppBrandingDeclaration,\n AppConfigurationTypeManifest,\n AppPermissionDeclaration,\n AppPageDeclaration,\n AppSettingDeclaration,\n AppSource,\n AppStatusType,\n AppInstallationStatus,\n AppListItem,\n AppDetail,\n AppInstallationDetail,\n} from './types/manifest'\n\n// Canonical app layout\nexport { APP_LAYOUT, HANDLER_NAMES, conventionalPaths } from './structure'\nexport type { HandlerName } from './structure'\n\n// Hooks types\nexport type { AppContextValue, PipelineStatusData } from './hooks'\n","import type { PipelineContext, ValidationResult } from '../types/pipeline'\n\n/**\n * Define a validation handler for a configuration type.\n *\n * @example\n * ```ts\n * import { defineValidator } from '@veltrixsecops/app-sdk/pipeline'\n *\n * export default defineValidator(async (ctx) => {\n * const errors = []\n * const name = ctx.canvas.sections[0]?.fields['name']\n * if (!name) {\n * errors.push({ field: 'name', message: 'Name is required', code: 'REQUIRED' })\n * }\n * return { valid: errors.length === 0, errors, warnings: [] }\n * })\n * ```\n */\nexport function defineValidator(\n handler: (ctx: PipelineContext) => Promise<ValidationResult>,\n): (ctx: PipelineContext) => Promise<ValidationResult> {\n return handler\n}\n","import type { DeployContext, DeployResult } from '../types/pipeline'\n\n/**\n * Define a deploy handler for a configuration type.\n *\n * @example\n * ```ts\n * import { defineDeployer } from '@veltrixsecops/app-sdk/pipeline'\n *\n * export default defineDeployer(async (ctx) => {\n * const { component, credential, connectivity, canvas } = ctx\n * // Push configuration to the tool\n * return { success: true, message: 'Deployed', rollbackData: previousState }\n * })\n * ```\n */\nexport function defineDeployer(\n handler: (ctx: DeployContext) => Promise<DeployResult>,\n): (ctx: DeployContext) => Promise<DeployResult> {\n return handler\n}\n","import type { RollbackContext, RollbackResult } from '../types/pipeline'\n\n/**\n * Define a rollback handler for a configuration type.\n */\nexport function defineRollbackHandler(\n handler: (ctx: RollbackContext) => Promise<RollbackResult>,\n): (ctx: RollbackContext) => Promise<RollbackResult> {\n return handler\n}\n","import type { HealthCheckContext, HealthCheckResult } from '../types/pipeline'\n\n/**\n * Define a health check handler for a configuration type.\n */\nexport function defineHealthChecker(\n handler: (ctx: HealthCheckContext) => Promise<HealthCheckResult>,\n): (ctx: HealthCheckContext) => Promise<HealthCheckResult> {\n return handler\n}\n","import type { DriftContext, DriftResult } from '../types/pipeline'\n\n/**\n * Define a drift detection handler for a configuration type.\n */\nexport function defineDriftDetector(\n handler: (ctx: DriftContext) => Promise<DriftResult>,\n): (ctx: DriftContext) => Promise<DriftResult> {\n return handler\n}\n","// ========================================================================\n// App manifest types\n//\n// Self-contained copy of the platform's manifest contract so the SDK can\n// be published and consumed outside the platform monorepo. The platform\n// keeps its own copy in shared/types/app.ts — changes to the manifest\n// contract must be applied to both files.\n// ========================================================================\n\nexport type AppSource = 'BUILT_IN' | 'MARKETPLACE' | 'CUSTOM'\nexport type AppStatusType = 'AVAILABLE' | 'DEPRECATED' | 'REMOVED'\nexport type AppInstallationStatus =\n | 'INSTALLING'\n | 'INSTALLED'\n | 'ENABLED'\n | 'DISABLED'\n | 'FAILED'\n | 'UNINSTALLING'\n\n// --- Manifest Types (parsed from manifest.yaml) ---\n\nexport interface AppManifest {\n id: string\n name: string\n version: string\n vendor: string\n description: string\n category: string\n license?: string\n homepage?: string\n icon?: string\n logo?: string\n\n platform: {\n minVersion: string\n }\n\n permissions: {\n platform: string[] // Platform permissions the app needs\n app: AppPermissionDeclaration[] // Permissions the app exposes\n }\n\n database?: {\n migrations: string\n tablePrefix: string\n }\n\n pipeline: {\n configurationTypes: AppConfigurationTypeManifest[]\n pipelineEvents?: string[]\n }\n\n server: {\n entry: string\n routes?: {\n prefix: string\n }\n }\n\n client?: {\n entry: string\n pages?: AppPageDeclaration[]\n /**\n * How the platform lays out this app's navigation (its client pages and\n * one entry per configuration type):\n * - 'tabs' (default): a horizontal tab strip — best for a few items.\n * - 'sidebar': an embedded left rail, grouped into Pages and\n * Configurations — scales to many configuration types without the\n * tab strip overflowing.\n */\n navLayout?: 'tabs' | 'sidebar'\n }\n\n /**\n * Vendor brand identity, applied by the platform in defined slots only —\n * the app navbar (logo, accent) and scoped CSS variables. The platform,\n * not the app, decides where brand color appears, so one vendor's palette\n * never overwhelms the product shell.\n */\n branding?: AppBrandingDeclaration\n\n hooks?: {\n onInstall?: string\n onUninstall?: string\n onEnable?: string\n onDisable?: string\n onUpgrade?: string\n }\n\n events?: string[] // Platform events this app subscribes to\n\n settings?: AppSettingDeclaration[]\n}\n\n/**\n * App brand identity. The platform renders it in a per-app navbar above the\n * app's pages and exposes the colors to app pages as scoped CSS variables\n * (--veltrix-app-primary, --veltrix-app-accent).\n */\nexport interface AppBrandingDeclaration {\n /** Brand accent color as #RGB or #RRGGBB hex (e.g. CrowdStrike red). */\n primaryColor?: string\n /** Optional secondary color as #RGB or #RRGGBB hex. */\n accentColor?: string\n /**\n * Vendor logo shown in the app navbar. Repo-relative .svg (preferred) or\n * .png, at most 128 KB; rendered at 28px height, so use a wide/landscape\n * mark with transparent background.\n */\n logo?: string\n /** Optional logo variant for dark backgrounds; same constraints as logo. */\n logoDark?: string\n}\n\nexport interface AppConfigurationTypeManifest {\n id: string\n name: string\n description?: string\n canvasTemplate: string // Path to canvas template YAML\n defaultConfig?: string // Path to default config YAML\n\n handlers: {\n validate: string\n deploy: string\n rollback: string\n healthCheck: string\n driftDetect?: string | null\n getStatus: string\n }\n\n targets: {\n componentTypes: string[]\n requiresCredential: boolean\n requiresConnectivity: boolean\n }\n}\n\nexport interface AppPermissionDeclaration {\n resource: string\n actions: string[]\n description?: string\n}\n\n// --- App UI & navigation contract ---\n//\n// The platform owns the chrome: breadcrumb, app header, navigation, permission\n// gating, error boundary and loading states are rendered identically for every\n// app. Apps own the page body and compose it from @veltrixsecops/ui.\n// Predictable shell, flexible body.\n\n/**\n * How the platform frames an app page.\n * - `standard` — page header + padded content area (default; use for most pages)\n * - `full-bleed` — content area with no padding/toolbar (custom canvases, maps)\n * - `canvas` — Configuration Canvas chrome (section rail + save/validate bar)\n */\nexport type AppPageLayout = 'standard' | 'full-bleed' | 'canvas'\nexport const APP_PAGE_LAYOUTS = ['standard', 'full-bleed', 'canvas'] as const\n\n/**\n * Where the page surfaces in navigation.\n * - `sidebar` — an entry beneath the app in the sidebar\n * - `tab` — a tab within its `parent` page\n * - `hidden` — routable but not linked (details/drill-down pages)\n */\nexport type AppPageNav = 'sidebar' | 'tab' | 'hidden'\nexport const APP_PAGE_NAV = ['sidebar', 'tab', 'hidden'] as const\n\n/** An app-scoped permission (declared in `permissions.app`) required to see a page. */\nexport interface AppPagePermission {\n resource: string\n action: string\n}\n\nexport interface AppPageDeclaration {\n /** Route beneath the app, e.g. `/indexes` → `/apps/<app-id>/indexes` */\n path: string\n /** Exported component name from the app's client entry */\n component: string\n label: string\n description?: string\n /** Icon name from the platform icon set; falls back to the app icon */\n icon?: string\n /** @deprecated use `nav: 'sidebar' | 'hidden'` — kept for backward compatibility */\n sidebar?: boolean\n /** Navigation placement. Defaults to `sidebar` when `sidebar: true`, else `hidden`. */\n nav?: AppPageNav\n /** Parent page `path` — required for `nav: 'tab'`, optional nesting for sidebar entries */\n parent?: string\n /** Optional sidebar section label, for apps with many pages */\n group?: string\n /** Deterministic ordering within its group/parent (ascending; ties break on label) */\n order?: number\n /** Layout preset the platform renders around the page body */\n layout?: AppPageLayout\n /** Hide the page (and its nav entry) unless the user holds this app permission */\n requiresPermission?: AppPagePermission\n}\n\nexport interface AppSettingDeclaration {\n key: string\n type: 'string' | 'number' | 'boolean' | 'select'\n label: string\n description?: string\n default?: string | number | boolean\n required?: boolean\n options?: Array<{ label: string; value: string }>\n}\n\n// --- API Response Types ---\n\nexport interface AppListItem {\n id: string\n appId: string\n name: string\n version: string\n vendor: string\n description: string\n category: string\n icon?: string\n logo?: string\n source: AppSource\n isDefault: boolean\n status: AppStatusType\n installed?: boolean\n enabled?: boolean\n}\n\nexport interface AppDetail extends AppListItem {\n license?: string\n homepage?: string\n repository?: string\n configurationTypes: Array<{\n id: string\n name: string\n description?: string\n componentTypes: string[]\n }>\n permissions: AppPermissionDeclaration[]\n settings: AppSettingDeclaration[]\n}\n\nexport interface AppInstallationDetail {\n id: string\n appId: string\n customerId: string\n version: string\n enabled: boolean\n installedBy: string\n installedAt: string\n settings: Record<string, unknown>\n status: AppInstallationStatus\n app: AppListItem\n}\n","// ========================================================================\n// Canonical app layout\n//\n// Every Veltrix app follows one predictable folder structure so apps are\n// easy to set up, review, and load. The `veltrix` CLI scaffolds it,\n// `veltrix validate` (and repo CI) warn on deviations, and the platform's\n// app engine resolves manifest references against it.\n// ========================================================================\n\n/** Canonical locations inside an app directory. */\nexport const APP_LAYOUT = {\n /** The app contract. Always at the app root. */\n manifest: 'manifest.yaml',\n /**\n * The unit of extension: config-types/<configTypeId>/ colocates everything\n * for one configuration type — canvas.yaml, defaults.yaml, the six pipeline\n * handlers (extensionless in the manifest), and __tests__/.\n */\n configTypesDir: 'config-types',\n /** Canvas form schema filename inside a config-type folder. */\n canvasFile: 'canvas.yaml',\n /** Default field values filename inside a config-type folder. */\n defaultsFile: 'defaults.yaml',\n /** Lifecycle hooks (camelCase): hooks/onInstall, hooks/onUninstall, ... */\n hooksDir: 'hooks',\n /** Shared app code used by multiple handlers (API clients, parsers). */\n libDir: 'lib',\n /** SQL migrations (requires manifest `database.tablePrefix`). */\n migrationsDir: 'migrations',\n /** Fastify route module receiving (fastify, AppRouteContext). */\n serverEntry: 'server/index',\n /** Client entry registering pages/sidebar items (optional). */\n clientEntry: 'client/index',\n /** Icons and logos (optional). */\n assetsDir: 'assets',\n /** Tests live next to the code they cover: handlers/<id>/__tests__/ */\n testsDirName: '__tests__',\n} as const\n\n/** The six pipeline handler names, in lifecycle order. */\nexport const HANDLER_NAMES = [\n 'validate',\n 'deploy',\n 'rollback',\n 'healthCheck',\n 'driftDetect',\n 'getStatus',\n] as const\n\nexport type HandlerName = (typeof HANDLER_NAMES)[number]\n\n/**\n * Conventional manifest paths for one configuration type — handy when\n * generating or checking a manifest programmatically.\n *\n * @example\n * conventionalPaths('indexes').handlers.deploy // 'config-types/indexes/deploy'\n */\nexport function conventionalPaths(configTypeId: string): {\n canvasTemplate: string\n defaultConfig: string\n handlers: Record<HandlerName, string>\n} {\n const base = `${APP_LAYOUT.configTypesDir}/${configTypeId}`\n const handlers = {} as Record<HandlerName, string>\n for (const name of HANDLER_NAMES) {\n handlers[name] = `${base}/${name}`\n }\n return {\n canvasTemplate: `${base}/${APP_LAYOUT.canvasFile}`,\n defaultConfig: `${base}/${APP_LAYOUT.defaultsFile}`,\n handlers,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACmBO,SAAS,gBACd,SACqD;AACrD,SAAO;AACT;;;ACPO,SAAS,eACd,SAC+C;AAC/C,SAAO;AACT;;;ACfO,SAAS,sBACd,SACmD;AACnD,SAAO;AACT;;;ACJO,SAAS,oBACd,SACyD;AACzD,SAAO;AACT;;;ACJO,SAAS,oBACd,SAC6C;AAC7C,SAAO;AACT;;;ACoJO,IAAM,mBAAmB,CAAC,YAAY,cAAc,QAAQ;AAS5D,IAAM,eAAe,CAAC,WAAW,OAAO,QAAQ;;;AC5JhD,IAAM,aAAa;AAAA;AAAA,EAExB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMV,gBAAgB;AAAA;AAAA,EAEhB,YAAY;AAAA;AAAA,EAEZ,cAAc;AAAA;AAAA,EAEd,UAAU;AAAA;AAAA,EAEV,QAAQ;AAAA;AAAA,EAER,eAAe;AAAA;AAAA,EAEf,aAAa;AAAA;AAAA,EAEb,aAAa;AAAA;AAAA,EAEb,WAAW;AAAA;AAAA,EAEX,cAAc;AAChB;AAGO,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAWO,SAAS,kBAAkB,cAIhC;AACA,QAAM,OAAO,GAAG,WAAW,cAAc,IAAI,YAAY;AACzD,QAAM,WAAW,CAAC;AAClB,aAAW,QAAQ,eAAe;AAChC,aAAS,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI;AAAA,EAClC;AACA,SAAO;AAAA,IACL,gBAAgB,GAAG,IAAI,IAAI,WAAW,UAAU;AAAA,IAChD,eAAe,GAAG,IAAI,IAAI,WAAW,YAAY;AAAA,IACjD;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/pipeline/define-validator.ts","../src/pipeline/define-deployer.ts","../src/pipeline/define-rollback-handler.ts","../src/pipeline/define-health-checker.ts","../src/pipeline/define-drift-detector.ts","../src/pipeline/define-connection-tester.ts","../src/types/manifest.ts","../src/structure.ts"],"sourcesContent":["// ========================================================================\n// @veltrixsecops/app-sdk\n//\n// The official SDK for building Veltrix Security-as-Code apps.\n//\n// This root entry is REACT-FREE and safe to load in a bare Node process.\n// Pipeline handlers execute inside the platform's sandbox runner — a child\n// process with a scrubbed environment and no React — so the contract they\n// import must never pull in a UI dependency. (Before 2.0 this entry\n// re-exported the React hooks, which made `require('@veltrixsecops/app-sdk')`\n// load React.) The platform can therefore import HANDLER_NAMES and the\n// handler contracts directly, instead of mirroring them server-side.\n//\n// Usage:\n// import type { PipelineContext, DeployContext } from '@veltrixsecops/app-sdk'\n// import { HANDLER_NAMES, conventionalPaths } from '@veltrixsecops/app-sdk'\n// import { defineValidator, defineDeployer } from '@veltrixsecops/app-sdk/pipeline'\n// import { useAppContext, usePipelineStatus } from '@veltrixsecops/app-sdk/hooks' // React\n// import { authFetch } from '@veltrixsecops/app-sdk/client' // browser\n// ========================================================================\n\n// Pipeline handler helpers (React-free)\nexport {\n defineValidator,\n defineDeployer,\n defineRollbackHandler,\n defineHealthChecker,\n defineDriftDetector,\n defineConnectionTester,\n} from './pipeline'\n\n// NOTE: React hooks are NOT re-exported here. Import them from\n// '@veltrixsecops/app-sdk/hooks'; browser client helpers live in\n// '@veltrixsecops/app-sdk/client'. Keeping them off the root is what makes\n// this entry loadable in the sandbox runner's bare Node child process.\n\n// Pipeline types\nexport type {\n PipelineContext,\n DeployContext,\n RollbackContext,\n HealthCheckContext,\n DriftContext,\n ValidationResult,\n ValidationError,\n ValidationWarning,\n DeployResult,\n RollbackResult,\n HealthCheckResult,\n HealthCheck,\n DriftResult,\n DriftDiff,\n ConfigStatus,\n ComponentConfigStatus,\n CanvasSnapshot,\n CanvasItemSnapshot,\n CanvasSectionSnapshot,\n DeploymentStrategy,\n EnvironmentRef,\n UserRef,\n ComponentRef,\n CredentialRef,\n ConnectivityRef,\n RemoteExecutor,\n RemoteIntent,\n RemoteResult,\n PlatformDataApi,\n DeploymentSummary,\n ValidateHandler,\n DeployHandler,\n RollbackHandler,\n HealthCheckHandler,\n DriftDetectHandler,\n GetStatusHandler,\n TestConnectionContext,\n TestConnectionResult,\n TestConnectionHandler,\n OptionItem,\n OptionsProviderContext,\n OptionsProvider,\n} from './types/pipeline'\n\n// Platform types\nexport type {\n Component,\n InventoryItem,\n InventoryItemInput,\n ConnectivityProviderRef,\n Credential,\n CredentialSummary,\n CredentialInput,\n Connectivity,\n Tag,\n User,\n Customer,\n PlatformDatabaseClient,\n AppHookContext,\n AppWebhookContext,\n AppEventContext,\n AppRouteContext,\n AppEventPublisher,\n PermissionEntry,\n PermissionCheckOptions,\n AppPermissionsApi,\n} from './types/platform'\n\n// App UI & navigation contract\nexport { APP_PAGE_LAYOUTS, APP_PAGE_NAV } from './types/manifest'\nexport type { AppPageLayout, AppPageNav, AppPagePermission } from './types/manifest'\n\n// Manifest types\nexport type {\n AppManifest,\n AppBrandingDeclaration,\n AppConfigurationTypeManifest,\n AppPermissionDeclaration,\n AppPageDeclaration,\n AppSettingDeclaration,\n AppSource,\n AppStatusType,\n AppInstallationStatus,\n AppListItem,\n AppDetail,\n AppInstallationDetail,\n} from './types/manifest'\n\n// Canonical app layout\nexport { APP_LAYOUT, HANDLER_NAMES, conventionalPaths } from './structure'\nexport type { HandlerName } from './structure'\n\n// NOTE: BYOL topology is intentionally NOT exported from this root entry. It is\n// app-specific (Splunk roles) and an app must not depend on a root SDK export\n// that may be absent in the @veltrixsecops/app-sdk version the platform packages\n// it against. The client `byol` module keeps its own copy; the app server owns\n// its own (apps/<id>/lib/byolTopology.ts).\n\n// Hooks types\nexport type { AppContextValue, PipelineStatusData } from './hooks'\n","import type { PipelineContext, ValidationResult } from '../types/pipeline'\n\n/**\n * Define a validation handler for a configuration type.\n *\n * @example\n * ```ts\n * import { defineValidator } from '@veltrixsecops/app-sdk/pipeline'\n *\n * export default defineValidator(async (ctx) => {\n * const errors = []\n * const name = ctx.canvas.sections[0]?.fields['name']\n * if (!name) {\n * errors.push({ field: 'name', message: 'Name is required', code: 'REQUIRED' })\n * }\n * return { valid: errors.length === 0, errors, warnings: [] }\n * })\n * ```\n */\nexport function defineValidator(\n handler: (ctx: PipelineContext) => Promise<ValidationResult>,\n): (ctx: PipelineContext) => Promise<ValidationResult> {\n return handler\n}\n","import type { DeployContext, DeployResult } from '../types/pipeline'\n\n/**\n * Define a deploy handler for a configuration type.\n *\n * @example\n * ```ts\n * import { defineDeployer } from '@veltrixsecops/app-sdk/pipeline'\n *\n * export default defineDeployer(async (ctx) => {\n * const { component, credential, connectivity, canvas } = ctx\n * // Push configuration to the tool\n * return { success: true, message: 'Deployed', rollbackData: previousState }\n * })\n * ```\n */\nexport function defineDeployer(\n handler: (ctx: DeployContext) => Promise<DeployResult>,\n): (ctx: DeployContext) => Promise<DeployResult> {\n return handler\n}\n","import type { RollbackContext, RollbackResult } from '../types/pipeline'\n\n/**\n * Define a rollback handler for a configuration type.\n */\nexport function defineRollbackHandler(\n handler: (ctx: RollbackContext) => Promise<RollbackResult>,\n): (ctx: RollbackContext) => Promise<RollbackResult> {\n return handler\n}\n","import type { HealthCheckContext, HealthCheckResult } from '../types/pipeline'\n\n/**\n * Define a health check handler for a configuration type.\n */\nexport function defineHealthChecker(\n handler: (ctx: HealthCheckContext) => Promise<HealthCheckResult>,\n): (ctx: HealthCheckContext) => Promise<HealthCheckResult> {\n return handler\n}\n","import type { DriftContext, DriftResult } from '../types/pipeline'\n\n/**\n * Define a drift detection handler for a configuration type.\n */\nexport function defineDriftDetector(\n handler: (ctx: DriftContext) => Promise<DriftResult>,\n): (ctx: DriftContext) => Promise<DriftResult> {\n return handler\n}\n","import type { TestConnectionContext, TestConnectionResult } from '../types/pipeline'\n\n/**\n * Define a connection-test handler for an app. The platform runs it in-process\n * with the decrypted credential to verify a Connection's endpoint + credentials\n * before they're relied on for deployments.\n */\nexport function defineConnectionTester(\n handler: (ctx: TestConnectionContext) => Promise<TestConnectionResult>,\n): (ctx: TestConnectionContext) => Promise<TestConnectionResult> {\n return handler\n}\n","// ========================================================================\n// App manifest types\n//\n// Self-contained copy of the platform's manifest contract so the SDK can\n// be published and consumed outside the platform monorepo. The platform\n// keeps its own copy in shared/types/app.ts — changes to the manifest\n// contract must be applied to both files.\n// ========================================================================\n\nexport type AppSource = 'BUILT_IN' | 'MARKETPLACE' | 'CUSTOM'\nexport type AppStatusType = 'AVAILABLE' | 'DEPRECATED' | 'REMOVED'\nexport type AppInstallationStatus =\n | 'INSTALLING'\n | 'INSTALLED'\n | 'ENABLED'\n | 'DISABLED'\n | 'FAILED'\n | 'UNINSTALLING'\n\n// --- Manifest Types (parsed from manifest.yaml) ---\n\nexport interface AppManifest {\n id: string\n name: string\n version: string\n vendor: string\n description: string\n category: string\n license?: string\n homepage?: string\n icon?: string\n logo?: string\n\n platform: {\n minVersion: string\n }\n\n permissions: {\n platform: string[] // Platform permissions the app needs\n app: AppPermissionDeclaration[] // Permissions the app exposes\n }\n\n database?: {\n migrations: string\n tablePrefix: string\n /**\n * How the app's tables are namespaced in Postgres.\n * - 'shared' (default): prefixed tables in the shared `public` schema.\n * Reserved for trusted first-party apps whose SQL the platform ships.\n * - 'schema': the app gets its own Postgres schema (+ least-privilege\n * role); its migrations run with search_path pinned to it.\n * - 'database': the app gets its own Postgres database — hard,\n * cross-database-query-proof isolation.\n * - 'external': the app owns its datastore entirely; the platform manages\n * no schema for it (connection supplied at runtime via app settings).\n * The platform forces at least 'schema' for marketplace and customer-\n * authored (self-managed) apps, which may opt up to 'database'/'external'.\n */\n isolation?: 'shared' | 'schema' | 'database' | 'external'\n }\n\n pipeline: {\n configurationTypes: AppConfigurationTypeManifest[]\n pipelineEvents?: string[]\n }\n\n server: {\n entry: string\n routes?: {\n prefix: string\n }\n }\n\n client?: {\n entry: string\n pages?: AppPageDeclaration[]\n /**\n * How the platform lays out this app's navigation (its client pages and\n * one entry per configuration type):\n * - 'tabs' (default): a horizontal tab strip — best for a few items.\n * - 'sidebar': an embedded left rail, grouped into Pages and\n * Configurations — scales to many configuration types without the\n * tab strip overflowing.\n */\n navLayout?: 'tabs' | 'sidebar'\n }\n\n /**\n * Vendor brand identity, applied by the platform in defined slots only —\n * the app navbar (logo, accent) and scoped CSS variables. The platform,\n * not the app, decides where brand color appears, so one vendor's palette\n * never overwhelms the product shell.\n */\n branding?: AppBrandingDeclaration\n\n hooks?: {\n onInstall?: string\n onUninstall?: string\n onEnable?: string\n onDisable?: string\n onUpgrade?: string\n /** Handler the platform invokes for inbound webhooks routed to this app. */\n onWebhook?: string\n /** Handler the platform invokes for inbound message-bus events routed to this app. */\n onEvent?: string\n }\n\n events?: string[] // Platform events this app subscribes to\n\n /**\n * Connection-level connectivity testing. `testHandler` is an extensionless path\n * to a module whose default export is a `testConnection` handler — the platform\n * runs it in-process (with the decrypted credential) to verify a Connection's\n * endpoint + credentials. Optional: apps without it report \"test not supported\".\n */\n connectivity?: {\n testHandler?: string\n }\n\n /**\n * Connection lifecycle declarations. `onboarding` opts the app into the\n * platform's one-click connection onboarding: the app declares what it needs\n * (a named onboarding adapter + parameters) and the platform drives it.\n * Microsoft Entra admin-consent (`provider: 'entra-admin-consent'`) is the\n * first adapter; nothing here is provider-specific to the platform core.\n */\n connection?: AppConnectionDeclaration\n\n settings?: AppSettingDeclaration[]\n}\n\n/** App-declared connection lifecycle capabilities. */\nexport interface AppConnectionDeclaration {\n onboarding?: ConnectionOnboardingDescriptor\n}\n\n/**\n * Declarative \"one-click connect\" descriptor. The platform reads it to render a\n * \"Connect …\" button and to drive a named onboarding adapter; the app supplies\n * only data, never platform code.\n */\nexport interface ConnectionOnboardingDescriptor {\n /** Names a platform onboarding adapter (e.g. `entra-admin-consent`). */\n provider: string\n /** Button label in the Connections UI (e.g. \"Connect Microsoft Defender\"). */\n label: string\n params?: ConnectionOnboardingParams\n /**\n * Optional app-provided finalize hook (extensionless path). Run in-process\n * after a successful onboarding, exactly like `connectivity.testHandler`.\n */\n onboardingHandler?: string\n}\n\nexport interface ConnectionOnboardingParams {\n /** App-setting key whose value selects the sovereign cloud (e.g. `azure_cloud`). */\n cloudSetting?: string\n /**\n * App permissions this connection needs — for display + audience selection.\n * The effective grant is fixed on the connector app registration, not here.\n */\n requiredResourceAccess?: OnboardingRequiredResource[]\n /** What the flow captures and where it maps back onto the connection. */\n capture?: OnboardingCapture\n /** True → the connection uses the platform token broker and stores NO secret. */\n brokered?: boolean\n /**\n * App settings the admin must supply BEFORE the consent click (they cannot be\n * derived from consent), e.g. Sentinel's subscription/resource-group/workspace.\n */\n requiredSettings?: string[]\n /** Post-consent provisioning steps the adapter runs (e.g. Sentinel ARM RBAC). */\n provisioning?: OnboardingProvisioningStep[]\n}\n\nexport interface OnboardingRequiredResource {\n /** Well-known resource name or appId (e.g. `WindowsDefenderATP`, `Graph`). */\n resource: string\n /** Application permissions requested on that resource (display only). */\n appPermissions: string[]\n}\n\nexport interface OnboardingCapture {\n /**\n * Where to write the consented tenant id. `setting:<key>` writes it into the\n * named app setting (the app libs read it as their `tenant_id`).\n */\n tenantId?: string\n}\n\n/** A post-consent provisioning step. Only ARM role assignment exists today. */\nexport interface OnboardingProvisioningStep {\n type: 'arm-role-assignment'\n /** Well-known built-in role name (resolved to a role-definition id by the adapter). */\n role: string\n /** ARM scope granularity for the assignment. */\n scope: 'resourceGroup' | 'subscription'\n /**\n * How the ARM token for the assignment is obtained:\n * - `manual` (default): show a portal deep-link/CLI + a verify probe. No\n * extra platform privilege — consent does not grant ARM RBAC.\n * - `delegated`: opt-in second delegated-ARM leg (requires the admin to hold\n * Owner / User Access Administrator). Not implemented in the first cut.\n */\n armToken?: 'manual' | 'delegated'\n}\n\n/**\n * App brand identity. The platform renders it in a per-app navbar above the\n * app's pages and exposes the colors to app pages as scoped CSS variables\n * (--veltrix-app-primary, --veltrix-app-accent).\n */\nexport interface AppBrandingDeclaration {\n /** Brand accent color as #RGB or #RRGGBB hex (e.g. CrowdStrike red). */\n primaryColor?: string\n /** Optional secondary color as #RGB or #RRGGBB hex. */\n accentColor?: string\n /**\n * Vendor logo shown in the app navbar and on the marketplace card. Either a\n * repo-relative .svg (preferred) or .png at most 128 KB, OR an absolute\n * https:// URL to an externally hosted asset. Rendered at ~28px height, so\n * use a wide/landscape mark with a transparent background.\n */\n logo?: string\n /** Optional logo variant for dark backgrounds; same constraints as logo. */\n logoDark?: string\n}\n\nexport interface AppConfigurationTypeManifest {\n id: string\n name: string\n description?: string\n canvasTemplate: string // Path to canvas template YAML\n defaultConfig?: string // Path to default config YAML\n\n handlers: {\n validate: string\n deploy: string\n rollback: string\n healthCheck: string\n driftDetect?: string | null\n getStatus: string\n /**\n * Optional options provider — powers live `remote-multiselect` config fields\n * (returns {value,label} options fetched from the target system). Loaded like\n * driftDetect (optional); omit when the config type has no live-option fields.\n */\n options?: string | null\n }\n\n targets: {\n componentTypes: string[]\n requiresCredential: boolean\n requiresConnectivity: boolean\n }\n}\n\nexport interface AppPermissionDeclaration {\n resource: string\n actions: string[]\n description?: string\n}\n\n// --- App UI & navigation contract ---\n//\n// The platform owns the chrome: breadcrumb, app header, navigation, permission\n// gating, error boundary and loading states are rendered identically for every\n// app. Apps own the page body and compose it from @veltrixsecops/ui.\n// Predictable shell, flexible body.\n\n/**\n * How the platform frames an app page.\n * - `standard` — page header + padded content area (default; use for most pages)\n * - `full-bleed` — content area with no padding/toolbar (custom canvases, maps)\n * - `canvas` — Configuration Canvas chrome (section rail + save/validate bar)\n */\nexport type AppPageLayout = 'standard' | 'full-bleed' | 'canvas'\nexport const APP_PAGE_LAYOUTS = ['standard', 'full-bleed', 'canvas'] as const\n\n/**\n * Where the page surfaces in navigation.\n * - `sidebar` — an entry beneath the app in the sidebar\n * - `tab` — a tab within its `parent` page\n * - `hidden` — routable but not linked (details/drill-down pages)\n */\nexport type AppPageNav = 'sidebar' | 'tab' | 'hidden'\nexport const APP_PAGE_NAV = ['sidebar', 'tab', 'hidden'] as const\n\n/** An app-scoped permission (declared in `permissions.app`) required to see a page. */\nexport interface AppPagePermission {\n resource: string\n action: string\n}\n\nexport interface AppPageDeclaration {\n /** Route beneath the app, e.g. `/indexes` → `/apps/<app-id>/indexes` */\n path: string\n /** Exported component name from the app's client entry */\n component: string\n label: string\n description?: string\n /** Icon name from the platform icon set; falls back to the app icon */\n icon?: string\n /** @deprecated use `nav: 'sidebar' | 'hidden'` — kept for backward compatibility */\n sidebar?: boolean\n /** Navigation placement. Defaults to `sidebar` when `sidebar: true`, else `hidden`. */\n nav?: AppPageNav\n /** Parent page `path` — required for `nav: 'tab'`, optional nesting for sidebar entries */\n parent?: string\n /** Optional sidebar section label, for apps with many pages */\n group?: string\n /** Deterministic ordering within its group/parent (ascending; ties break on label) */\n order?: number\n /** Layout preset the platform renders around the page body */\n layout?: AppPageLayout\n /** Hide the page (and its nav entry) unless the user holds this app permission */\n requiresPermission?: AppPagePermission\n}\n\nexport interface AppSettingDeclaration {\n key: string\n type: 'string' | 'number' | 'boolean' | 'select'\n label: string\n description?: string\n default?: string | number | boolean\n required?: boolean\n options?: Array<{ label: string; value: string }>\n}\n\n// --- API Response Types ---\n\nexport interface AppListItem {\n id: string\n appId: string\n name: string\n version: string\n vendor: string\n description: string\n category: string\n icon?: string\n logo?: string\n source: AppSource\n isDefault: boolean\n status: AppStatusType\n installed?: boolean\n enabled?: boolean\n}\n\nexport interface AppDetail extends AppListItem {\n license?: string\n homepage?: string\n repository?: string\n configurationTypes: Array<{\n id: string\n name: string\n description?: string\n componentTypes: string[]\n }>\n permissions: AppPermissionDeclaration[]\n settings: AppSettingDeclaration[]\n}\n\nexport interface AppInstallationDetail {\n id: string\n appId: string\n customerId: string\n version: string\n enabled: boolean\n installedBy: string\n installedAt: string\n settings: Record<string, unknown>\n status: AppInstallationStatus\n app: AppListItem\n}\n","// ========================================================================\n// Canonical app layout\n//\n// Every Veltrix app follows one predictable folder structure so apps are\n// easy to set up, review, and load. The `veltrix` CLI scaffolds it,\n// `veltrix validate` (and repo CI) warn on deviations, and the platform's\n// app engine resolves manifest references against it.\n// ========================================================================\n\n/** Canonical locations inside an app directory. */\nexport const APP_LAYOUT = {\n /** The app contract. Always at the app root. */\n manifest: 'manifest.yaml',\n /**\n * The unit of extension: config-types/<configTypeId>/ colocates everything\n * for one configuration type — canvas.yaml, defaults.yaml, the six pipeline\n * handlers (extensionless in the manifest), and __tests__/.\n */\n configTypesDir: 'config-types',\n /** Canvas form schema filename inside a config-type folder. */\n canvasFile: 'canvas.yaml',\n /** Default field values filename inside a config-type folder. */\n defaultsFile: 'defaults.yaml',\n /** Lifecycle hooks (camelCase): hooks/onInstall, hooks/onUninstall, ... */\n hooksDir: 'hooks',\n /** Shared app code used by multiple handlers (API clients, parsers). */\n libDir: 'lib',\n /** SQL migrations (requires manifest `database.tablePrefix`). */\n migrationsDir: 'migrations',\n /** Fastify route module receiving (fastify, AppRouteContext). */\n serverEntry: 'server/index',\n /** Client entry registering pages/sidebar items (optional). */\n clientEntry: 'client/index',\n /** Icons and logos (optional). */\n assetsDir: 'assets',\n /** Tests live next to the code they cover: handlers/<id>/__tests__/ */\n testsDirName: '__tests__',\n} as const\n\n/** The six pipeline handler names, in lifecycle order. */\nexport const HANDLER_NAMES = [\n 'validate',\n 'deploy',\n 'rollback',\n 'healthCheck',\n 'driftDetect',\n 'getStatus',\n] as const\n\nexport type HandlerName = (typeof HANDLER_NAMES)[number]\n\n/**\n * Conventional manifest paths for one configuration type — handy when\n * generating or checking a manifest programmatically.\n *\n * @example\n * conventionalPaths('indexes').handlers.deploy // 'config-types/indexes/deploy'\n */\nexport function conventionalPaths(configTypeId: string): {\n canvasTemplate: string\n defaultConfig: string\n handlers: Record<HandlerName, string>\n} {\n const base = `${APP_LAYOUT.configTypesDir}/${configTypeId}`\n const handlers = {} as Record<HandlerName, string>\n for (const name of HANDLER_NAMES) {\n handlers[name] = `${base}/${name}`\n }\n return {\n canvasTemplate: `${base}/${APP_LAYOUT.canvasFile}`,\n defaultConfig: `${base}/${APP_LAYOUT.defaultsFile}`,\n handlers,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACmBO,SAAS,gBACd,SACqD;AACrD,SAAO;AACT;;;ACPO,SAAS,eACd,SAC+C;AAC/C,SAAO;AACT;;;ACfO,SAAS,sBACd,SACmD;AACnD,SAAO;AACT;;;ACJO,SAAS,oBACd,SACyD;AACzD,SAAO;AACT;;;ACJO,SAAS,oBACd,SAC6C;AAC7C,SAAO;AACT;;;ACFO,SAAS,uBACd,SAC+D;AAC/D,SAAO;AACT;;;AC0QO,IAAM,mBAAmB,CAAC,YAAY,cAAc,QAAQ;AAS5D,IAAM,eAAe,CAAC,WAAW,OAAO,QAAQ;;;ACpRhD,IAAM,aAAa;AAAA;AAAA,EAExB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMV,gBAAgB;AAAA;AAAA,EAEhB,YAAY;AAAA;AAAA,EAEZ,cAAc;AAAA;AAAA,EAEd,UAAU;AAAA;AAAA,EAEV,QAAQ;AAAA;AAAA,EAER,eAAe;AAAA;AAAA,EAEf,aAAa;AAAA;AAAA,EAEb,aAAa;AAAA;AAAA,EAEb,WAAW;AAAA;AAAA,EAEX,cAAc;AAChB;AAGO,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAWO,SAAS,kBAAkB,cAIhC;AACA,QAAM,OAAO,GAAG,WAAW,cAAc,IAAI,YAAY;AACzD,QAAM,WAAW,CAAC;AAClB,aAAW,QAAQ,eAAe;AAChC,aAAS,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI;AAAA,EAClC;AACA,SAAO;AAAA,IACL,gBAAgB,GAAG,IAAI,IAAI,WAAW,UAAU;AAAA,IAChD,eAAe,GAAG,IAAI,IAAI,WAAW,YAAY;AAAA,IACjD;AAAA,EACF;AACF;","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- export { defineDeployer, defineDriftDetector, defineHealthChecker, defineRollbackHandler, defineValidator } from './pipeline/index.cjs';
2
- export { C as CanvasSectionSnapshot, a as CanvasSnapshot, b as ComponentConfigStatus, c as ComponentRef, d as ConfigStatus, e as ConnectivityRef, f as CredentialRef, D as DeployContext, g as DeployHandler, h as DeployResult, i as DeploymentStrategy, j as DeploymentSummary, k as DriftContext, l as DriftDetectHandler, m as DriftDiff, n as DriftResult, E as EnvironmentRef, G as GetStatusHandler, H as HealthCheck, o as HealthCheckContext, p as HealthCheckHandler, q as HealthCheckResult, P as PipelineContext, r as PlatformDataApi, R as RollbackContext, s as RollbackHandler, t as RollbackResult, U as UserRef, V as ValidateHandler, u as ValidationError, v as ValidationResult, w as ValidationWarning } from './pipeline-A-gSsPRR.cjs';
3
- export { A as APP_PAGE_LAYOUTS, a as APP_PAGE_NAV, b as AppBrandingDeclaration, c as AppConfigurationTypeManifest, d as AppContextValue, e as AppDetail, f as AppHookContext, g as AppInstallationDetail, h as AppInstallationStatus, i as AppListItem, j as AppManifest, k as AppPageDeclaration, l as AppPageLayout, m as AppPageNav, n as AppPagePermission, o as AppPermissionDeclaration, p as AppPermissionsApi, q as AppRouteContext, r as AppSettingDeclaration, s as AppSource, t as AppStatusType, C as Component, u as Connectivity, v as ConnectivityProviderRef, w as Credential, x as CredentialInput, y as CredentialSummary, z as Customer, I as InventoryItem, B as InventoryItemInput, P as PermissionCheckOptions, D as PermissionEntry, E as PlatformDatabaseClient, T as Tag, U as User } from './use-app-context-OQlcmq7t.cjs';
1
+ export { defineConnectionTester, defineDeployer, defineDriftDetector, defineHealthChecker, defineRollbackHandler, defineValidator } from './pipeline/index.cjs';
2
+ export { C as CanvasItemSnapshot, a as CanvasSectionSnapshot, b as CanvasSnapshot, c as ComponentConfigStatus, d as ComponentRef, e as ConfigStatus, f as ConnectivityRef, g as CredentialRef, D as DeployContext, h as DeployHandler, i as DeployResult, j as DeploymentStrategy, k as DeploymentSummary, l as DriftContext, m as DriftDetectHandler, n as DriftDiff, o as DriftResult, E as EnvironmentRef, G as GetStatusHandler, H as HealthCheck, p as HealthCheckContext, q as HealthCheckHandler, r as HealthCheckResult, O as OptionItem, s as OptionsProvider, t as OptionsProviderContext, P as PipelineContext, u as PlatformDataApi, R as RemoteExecutor, v as RemoteIntent, w as RemoteResult, x as RollbackContext, y as RollbackHandler, z as RollbackResult, T as TestConnectionContext, A as TestConnectionHandler, B as TestConnectionResult, U as UserRef, V as ValidateHandler, F as ValidationError, I as ValidationResult, J as ValidationWarning } from './pipeline-beeT8dje.cjs';
3
+ export { A as APP_PAGE_LAYOUTS, a as APP_PAGE_NAV, b as AppBrandingDeclaration, c as AppConfigurationTypeManifest, d as AppContextValue, e as AppDetail, f as AppEventContext, g as AppEventPublisher, h as AppHookContext, i as AppInstallationDetail, j as AppInstallationStatus, k as AppListItem, l as AppManifest, m as AppPageDeclaration, n as AppPageLayout, o as AppPageNav, p as AppPagePermission, q as AppPermissionDeclaration, r as AppPermissionsApi, s as AppRouteContext, t as AppSettingDeclaration, u as AppSource, v as AppStatusType, w as AppWebhookContext, C as Component, x as Connectivity, y as ConnectivityProviderRef, z as Credential, B as CredentialInput, D as CredentialSummary, E as Customer, I as InventoryItem, F as InventoryItemInput, P as PermissionCheckOptions, G as PermissionEntry, H as PlatformDatabaseClient, T as Tag, U as User } from './use-app-context-Byv_fam0.cjs';
4
4
  export { P as PipelineStatusData } from './use-pipeline-status-CCSQ9Neh.cjs';
5
5
  import 'react';
6
6
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { defineDeployer, defineDriftDetector, defineHealthChecker, defineRollbackHandler, defineValidator } from './pipeline/index.js';
2
- export { C as CanvasSectionSnapshot, a as CanvasSnapshot, b as ComponentConfigStatus, c as ComponentRef, d as ConfigStatus, e as ConnectivityRef, f as CredentialRef, D as DeployContext, g as DeployHandler, h as DeployResult, i as DeploymentStrategy, j as DeploymentSummary, k as DriftContext, l as DriftDetectHandler, m as DriftDiff, n as DriftResult, E as EnvironmentRef, G as GetStatusHandler, H as HealthCheck, o as HealthCheckContext, p as HealthCheckHandler, q as HealthCheckResult, P as PipelineContext, r as PlatformDataApi, R as RollbackContext, s as RollbackHandler, t as RollbackResult, U as UserRef, V as ValidateHandler, u as ValidationError, v as ValidationResult, w as ValidationWarning } from './pipeline-A-gSsPRR.js';
3
- export { A as APP_PAGE_LAYOUTS, a as APP_PAGE_NAV, b as AppBrandingDeclaration, c as AppConfigurationTypeManifest, d as AppContextValue, e as AppDetail, f as AppHookContext, g as AppInstallationDetail, h as AppInstallationStatus, i as AppListItem, j as AppManifest, k as AppPageDeclaration, l as AppPageLayout, m as AppPageNav, n as AppPagePermission, o as AppPermissionDeclaration, p as AppPermissionsApi, q as AppRouteContext, r as AppSettingDeclaration, s as AppSource, t as AppStatusType, C as Component, u as Connectivity, v as ConnectivityProviderRef, w as Credential, x as CredentialInput, y as CredentialSummary, z as Customer, I as InventoryItem, B as InventoryItemInput, P as PermissionCheckOptions, D as PermissionEntry, E as PlatformDatabaseClient, T as Tag, U as User } from './use-app-context-OQlcmq7t.js';
1
+ export { defineConnectionTester, defineDeployer, defineDriftDetector, defineHealthChecker, defineRollbackHandler, defineValidator } from './pipeline/index.js';
2
+ export { C as CanvasItemSnapshot, a as CanvasSectionSnapshot, b as CanvasSnapshot, c as ComponentConfigStatus, d as ComponentRef, e as ConfigStatus, f as ConnectivityRef, g as CredentialRef, D as DeployContext, h as DeployHandler, i as DeployResult, j as DeploymentStrategy, k as DeploymentSummary, l as DriftContext, m as DriftDetectHandler, n as DriftDiff, o as DriftResult, E as EnvironmentRef, G as GetStatusHandler, H as HealthCheck, p as HealthCheckContext, q as HealthCheckHandler, r as HealthCheckResult, O as OptionItem, s as OptionsProvider, t as OptionsProviderContext, P as PipelineContext, u as PlatformDataApi, R as RemoteExecutor, v as RemoteIntent, w as RemoteResult, x as RollbackContext, y as RollbackHandler, z as RollbackResult, T as TestConnectionContext, A as TestConnectionHandler, B as TestConnectionResult, U as UserRef, V as ValidateHandler, F as ValidationError, I as ValidationResult, J as ValidationWarning } from './pipeline-beeT8dje.js';
3
+ export { A as APP_PAGE_LAYOUTS, a as APP_PAGE_NAV, b as AppBrandingDeclaration, c as AppConfigurationTypeManifest, d as AppContextValue, e as AppDetail, f as AppEventContext, g as AppEventPublisher, h as AppHookContext, i as AppInstallationDetail, j as AppInstallationStatus, k as AppListItem, l as AppManifest, m as AppPageDeclaration, n as AppPageLayout, o as AppPageNav, p as AppPagePermission, q as AppPermissionDeclaration, r as AppPermissionsApi, s as AppRouteContext, t as AppSettingDeclaration, u as AppSource, v as AppStatusType, w as AppWebhookContext, C as Component, x as Connectivity, y as ConnectivityProviderRef, z as Credential, B as CredentialInput, D as CredentialSummary, E as Customer, I as InventoryItem, F as InventoryItemInput, P as PermissionCheckOptions, G as PermissionEntry, H as PlatformDatabaseClient, T as Tag, U as User } from './use-app-context-Byv_fam0.js';
4
4
  export { P as PipelineStatusData } from './use-pipeline-status-CCSQ9Neh.js';
5
5
  import 'react';
6
6
 
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import {
2
+ defineConnectionTester,
2
3
  defineDeployer,
3
4
  defineDriftDetector,
4
5
  defineHealthChecker,
5
6
  defineRollbackHandler,
6
7
  defineValidator
7
- } from "./chunk-VWFTOFTI.js";
8
+ } from "./chunk-Y6XBNZGR.js";
8
9
 
9
10
  // src/types/manifest.ts
10
11
  var APP_PAGE_LAYOUTS = ["standard", "full-bleed", "canvas"];
@@ -65,6 +66,7 @@ export {
65
66
  APP_PAGE_NAV,
66
67
  HANDLER_NAMES,
67
68
  conventionalPaths,
69
+ defineConnectionTester,
68
70
  defineDeployer,
69
71
  defineDriftDetector,
70
72
  defineHealthChecker,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types/manifest.ts","../src/structure.ts"],"sourcesContent":["// ========================================================================\n// App manifest types\n//\n// Self-contained copy of the platform's manifest contract so the SDK can\n// be published and consumed outside the platform monorepo. The platform\n// keeps its own copy in shared/types/app.ts — changes to the manifest\n// contract must be applied to both files.\n// ========================================================================\n\nexport type AppSource = 'BUILT_IN' | 'MARKETPLACE' | 'CUSTOM'\nexport type AppStatusType = 'AVAILABLE' | 'DEPRECATED' | 'REMOVED'\nexport type AppInstallationStatus =\n | 'INSTALLING'\n | 'INSTALLED'\n | 'ENABLED'\n | 'DISABLED'\n | 'FAILED'\n | 'UNINSTALLING'\n\n// --- Manifest Types (parsed from manifest.yaml) ---\n\nexport interface AppManifest {\n id: string\n name: string\n version: string\n vendor: string\n description: string\n category: string\n license?: string\n homepage?: string\n icon?: string\n logo?: string\n\n platform: {\n minVersion: string\n }\n\n permissions: {\n platform: string[] // Platform permissions the app needs\n app: AppPermissionDeclaration[] // Permissions the app exposes\n }\n\n database?: {\n migrations: string\n tablePrefix: string\n }\n\n pipeline: {\n configurationTypes: AppConfigurationTypeManifest[]\n pipelineEvents?: string[]\n }\n\n server: {\n entry: string\n routes?: {\n prefix: string\n }\n }\n\n client?: {\n entry: string\n pages?: AppPageDeclaration[]\n /**\n * How the platform lays out this app's navigation (its client pages and\n * one entry per configuration type):\n * - 'tabs' (default): a horizontal tab strip — best for a few items.\n * - 'sidebar': an embedded left rail, grouped into Pages and\n * Configurations — scales to many configuration types without the\n * tab strip overflowing.\n */\n navLayout?: 'tabs' | 'sidebar'\n }\n\n /**\n * Vendor brand identity, applied by the platform in defined slots only —\n * the app navbar (logo, accent) and scoped CSS variables. The platform,\n * not the app, decides where brand color appears, so one vendor's palette\n * never overwhelms the product shell.\n */\n branding?: AppBrandingDeclaration\n\n hooks?: {\n onInstall?: string\n onUninstall?: string\n onEnable?: string\n onDisable?: string\n onUpgrade?: string\n }\n\n events?: string[] // Platform events this app subscribes to\n\n settings?: AppSettingDeclaration[]\n}\n\n/**\n * App brand identity. The platform renders it in a per-app navbar above the\n * app's pages and exposes the colors to app pages as scoped CSS variables\n * (--veltrix-app-primary, --veltrix-app-accent).\n */\nexport interface AppBrandingDeclaration {\n /** Brand accent color as #RGB or #RRGGBB hex (e.g. CrowdStrike red). */\n primaryColor?: string\n /** Optional secondary color as #RGB or #RRGGBB hex. */\n accentColor?: string\n /**\n * Vendor logo shown in the app navbar. Repo-relative .svg (preferred) or\n * .png, at most 128 KB; rendered at 28px height, so use a wide/landscape\n * mark with transparent background.\n */\n logo?: string\n /** Optional logo variant for dark backgrounds; same constraints as logo. */\n logoDark?: string\n}\n\nexport interface AppConfigurationTypeManifest {\n id: string\n name: string\n description?: string\n canvasTemplate: string // Path to canvas template YAML\n defaultConfig?: string // Path to default config YAML\n\n handlers: {\n validate: string\n deploy: string\n rollback: string\n healthCheck: string\n driftDetect?: string | null\n getStatus: string\n }\n\n targets: {\n componentTypes: string[]\n requiresCredential: boolean\n requiresConnectivity: boolean\n }\n}\n\nexport interface AppPermissionDeclaration {\n resource: string\n actions: string[]\n description?: string\n}\n\n// --- App UI & navigation contract ---\n//\n// The platform owns the chrome: breadcrumb, app header, navigation, permission\n// gating, error boundary and loading states are rendered identically for every\n// app. Apps own the page body and compose it from @veltrixsecops/ui.\n// Predictable shell, flexible body.\n\n/**\n * How the platform frames an app page.\n * - `standard` — page header + padded content area (default; use for most pages)\n * - `full-bleed` — content area with no padding/toolbar (custom canvases, maps)\n * - `canvas` — Configuration Canvas chrome (section rail + save/validate bar)\n */\nexport type AppPageLayout = 'standard' | 'full-bleed' | 'canvas'\nexport const APP_PAGE_LAYOUTS = ['standard', 'full-bleed', 'canvas'] as const\n\n/**\n * Where the page surfaces in navigation.\n * - `sidebar` — an entry beneath the app in the sidebar\n * - `tab` — a tab within its `parent` page\n * - `hidden` — routable but not linked (details/drill-down pages)\n */\nexport type AppPageNav = 'sidebar' | 'tab' | 'hidden'\nexport const APP_PAGE_NAV = ['sidebar', 'tab', 'hidden'] as const\n\n/** An app-scoped permission (declared in `permissions.app`) required to see a page. */\nexport interface AppPagePermission {\n resource: string\n action: string\n}\n\nexport interface AppPageDeclaration {\n /** Route beneath the app, e.g. `/indexes` → `/apps/<app-id>/indexes` */\n path: string\n /** Exported component name from the app's client entry */\n component: string\n label: string\n description?: string\n /** Icon name from the platform icon set; falls back to the app icon */\n icon?: string\n /** @deprecated use `nav: 'sidebar' | 'hidden'` — kept for backward compatibility */\n sidebar?: boolean\n /** Navigation placement. Defaults to `sidebar` when `sidebar: true`, else `hidden`. */\n nav?: AppPageNav\n /** Parent page `path` — required for `nav: 'tab'`, optional nesting for sidebar entries */\n parent?: string\n /** Optional sidebar section label, for apps with many pages */\n group?: string\n /** Deterministic ordering within its group/parent (ascending; ties break on label) */\n order?: number\n /** Layout preset the platform renders around the page body */\n layout?: AppPageLayout\n /** Hide the page (and its nav entry) unless the user holds this app permission */\n requiresPermission?: AppPagePermission\n}\n\nexport interface AppSettingDeclaration {\n key: string\n type: 'string' | 'number' | 'boolean' | 'select'\n label: string\n description?: string\n default?: string | number | boolean\n required?: boolean\n options?: Array<{ label: string; value: string }>\n}\n\n// --- API Response Types ---\n\nexport interface AppListItem {\n id: string\n appId: string\n name: string\n version: string\n vendor: string\n description: string\n category: string\n icon?: string\n logo?: string\n source: AppSource\n isDefault: boolean\n status: AppStatusType\n installed?: boolean\n enabled?: boolean\n}\n\nexport interface AppDetail extends AppListItem {\n license?: string\n homepage?: string\n repository?: string\n configurationTypes: Array<{\n id: string\n name: string\n description?: string\n componentTypes: string[]\n }>\n permissions: AppPermissionDeclaration[]\n settings: AppSettingDeclaration[]\n}\n\nexport interface AppInstallationDetail {\n id: string\n appId: string\n customerId: string\n version: string\n enabled: boolean\n installedBy: string\n installedAt: string\n settings: Record<string, unknown>\n status: AppInstallationStatus\n app: AppListItem\n}\n","// ========================================================================\n// Canonical app layout\n//\n// Every Veltrix app follows one predictable folder structure so apps are\n// easy to set up, review, and load. The `veltrix` CLI scaffolds it,\n// `veltrix validate` (and repo CI) warn on deviations, and the platform's\n// app engine resolves manifest references against it.\n// ========================================================================\n\n/** Canonical locations inside an app directory. */\nexport const APP_LAYOUT = {\n /** The app contract. Always at the app root. */\n manifest: 'manifest.yaml',\n /**\n * The unit of extension: config-types/<configTypeId>/ colocates everything\n * for one configuration type — canvas.yaml, defaults.yaml, the six pipeline\n * handlers (extensionless in the manifest), and __tests__/.\n */\n configTypesDir: 'config-types',\n /** Canvas form schema filename inside a config-type folder. */\n canvasFile: 'canvas.yaml',\n /** Default field values filename inside a config-type folder. */\n defaultsFile: 'defaults.yaml',\n /** Lifecycle hooks (camelCase): hooks/onInstall, hooks/onUninstall, ... */\n hooksDir: 'hooks',\n /** Shared app code used by multiple handlers (API clients, parsers). */\n libDir: 'lib',\n /** SQL migrations (requires manifest `database.tablePrefix`). */\n migrationsDir: 'migrations',\n /** Fastify route module receiving (fastify, AppRouteContext). */\n serverEntry: 'server/index',\n /** Client entry registering pages/sidebar items (optional). */\n clientEntry: 'client/index',\n /** Icons and logos (optional). */\n assetsDir: 'assets',\n /** Tests live next to the code they cover: handlers/<id>/__tests__/ */\n testsDirName: '__tests__',\n} as const\n\n/** The six pipeline handler names, in lifecycle order. */\nexport const HANDLER_NAMES = [\n 'validate',\n 'deploy',\n 'rollback',\n 'healthCheck',\n 'driftDetect',\n 'getStatus',\n] as const\n\nexport type HandlerName = (typeof HANDLER_NAMES)[number]\n\n/**\n * Conventional manifest paths for one configuration type — handy when\n * generating or checking a manifest programmatically.\n *\n * @example\n * conventionalPaths('indexes').handlers.deploy // 'config-types/indexes/deploy'\n */\nexport function conventionalPaths(configTypeId: string): {\n canvasTemplate: string\n defaultConfig: string\n handlers: Record<HandlerName, string>\n} {\n const base = `${APP_LAYOUT.configTypesDir}/${configTypeId}`\n const handlers = {} as Record<HandlerName, string>\n for (const name of HANDLER_NAMES) {\n handlers[name] = `${base}/${name}`\n }\n return {\n canvasTemplate: `${base}/${APP_LAYOUT.canvasFile}`,\n defaultConfig: `${base}/${APP_LAYOUT.defaultsFile}`,\n handlers,\n }\n}\n"],"mappings":";;;;;;;;;AA6JO,IAAM,mBAAmB,CAAC,YAAY,cAAc,QAAQ;AAS5D,IAAM,eAAe,CAAC,WAAW,OAAO,QAAQ;;;AC5JhD,IAAM,aAAa;AAAA;AAAA,EAExB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMV,gBAAgB;AAAA;AAAA,EAEhB,YAAY;AAAA;AAAA,EAEZ,cAAc;AAAA;AAAA,EAEd,UAAU;AAAA;AAAA,EAEV,QAAQ;AAAA;AAAA,EAER,eAAe;AAAA;AAAA,EAEf,aAAa;AAAA;AAAA,EAEb,aAAa;AAAA;AAAA,EAEb,WAAW;AAAA;AAAA,EAEX,cAAc;AAChB;AAGO,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAWO,SAAS,kBAAkB,cAIhC;AACA,QAAM,OAAO,GAAG,WAAW,cAAc,IAAI,YAAY;AACzD,QAAM,WAAW,CAAC;AAClB,aAAW,QAAQ,eAAe;AAChC,aAAS,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI;AAAA,EAClC;AACA,SAAO;AAAA,IACL,gBAAgB,GAAG,IAAI,IAAI,WAAW,UAAU;AAAA,IAChD,eAAe,GAAG,IAAI,IAAI,WAAW,YAAY;AAAA,IACjD;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/types/manifest.ts","../src/structure.ts"],"sourcesContent":["// ========================================================================\n// App manifest types\n//\n// Self-contained copy of the platform's manifest contract so the SDK can\n// be published and consumed outside the platform monorepo. The platform\n// keeps its own copy in shared/types/app.ts — changes to the manifest\n// contract must be applied to both files.\n// ========================================================================\n\nexport type AppSource = 'BUILT_IN' | 'MARKETPLACE' | 'CUSTOM'\nexport type AppStatusType = 'AVAILABLE' | 'DEPRECATED' | 'REMOVED'\nexport type AppInstallationStatus =\n | 'INSTALLING'\n | 'INSTALLED'\n | 'ENABLED'\n | 'DISABLED'\n | 'FAILED'\n | 'UNINSTALLING'\n\n// --- Manifest Types (parsed from manifest.yaml) ---\n\nexport interface AppManifest {\n id: string\n name: string\n version: string\n vendor: string\n description: string\n category: string\n license?: string\n homepage?: string\n icon?: string\n logo?: string\n\n platform: {\n minVersion: string\n }\n\n permissions: {\n platform: string[] // Platform permissions the app needs\n app: AppPermissionDeclaration[] // Permissions the app exposes\n }\n\n database?: {\n migrations: string\n tablePrefix: string\n /**\n * How the app's tables are namespaced in Postgres.\n * - 'shared' (default): prefixed tables in the shared `public` schema.\n * Reserved for trusted first-party apps whose SQL the platform ships.\n * - 'schema': the app gets its own Postgres schema (+ least-privilege\n * role); its migrations run with search_path pinned to it.\n * - 'database': the app gets its own Postgres database — hard,\n * cross-database-query-proof isolation.\n * - 'external': the app owns its datastore entirely; the platform manages\n * no schema for it (connection supplied at runtime via app settings).\n * The platform forces at least 'schema' for marketplace and customer-\n * authored (self-managed) apps, which may opt up to 'database'/'external'.\n */\n isolation?: 'shared' | 'schema' | 'database' | 'external'\n }\n\n pipeline: {\n configurationTypes: AppConfigurationTypeManifest[]\n pipelineEvents?: string[]\n }\n\n server: {\n entry: string\n routes?: {\n prefix: string\n }\n }\n\n client?: {\n entry: string\n pages?: AppPageDeclaration[]\n /**\n * How the platform lays out this app's navigation (its client pages and\n * one entry per configuration type):\n * - 'tabs' (default): a horizontal tab strip — best for a few items.\n * - 'sidebar': an embedded left rail, grouped into Pages and\n * Configurations — scales to many configuration types without the\n * tab strip overflowing.\n */\n navLayout?: 'tabs' | 'sidebar'\n }\n\n /**\n * Vendor brand identity, applied by the platform in defined slots only —\n * the app navbar (logo, accent) and scoped CSS variables. The platform,\n * not the app, decides where brand color appears, so one vendor's palette\n * never overwhelms the product shell.\n */\n branding?: AppBrandingDeclaration\n\n hooks?: {\n onInstall?: string\n onUninstall?: string\n onEnable?: string\n onDisable?: string\n onUpgrade?: string\n /** Handler the platform invokes for inbound webhooks routed to this app. */\n onWebhook?: string\n /** Handler the platform invokes for inbound message-bus events routed to this app. */\n onEvent?: string\n }\n\n events?: string[] // Platform events this app subscribes to\n\n /**\n * Connection-level connectivity testing. `testHandler` is an extensionless path\n * to a module whose default export is a `testConnection` handler — the platform\n * runs it in-process (with the decrypted credential) to verify a Connection's\n * endpoint + credentials. Optional: apps without it report \"test not supported\".\n */\n connectivity?: {\n testHandler?: string\n }\n\n /**\n * Connection lifecycle declarations. `onboarding` opts the app into the\n * platform's one-click connection onboarding: the app declares what it needs\n * (a named onboarding adapter + parameters) and the platform drives it.\n * Microsoft Entra admin-consent (`provider: 'entra-admin-consent'`) is the\n * first adapter; nothing here is provider-specific to the platform core.\n */\n connection?: AppConnectionDeclaration\n\n settings?: AppSettingDeclaration[]\n}\n\n/** App-declared connection lifecycle capabilities. */\nexport interface AppConnectionDeclaration {\n onboarding?: ConnectionOnboardingDescriptor\n}\n\n/**\n * Declarative \"one-click connect\" descriptor. The platform reads it to render a\n * \"Connect …\" button and to drive a named onboarding adapter; the app supplies\n * only data, never platform code.\n */\nexport interface ConnectionOnboardingDescriptor {\n /** Names a platform onboarding adapter (e.g. `entra-admin-consent`). */\n provider: string\n /** Button label in the Connections UI (e.g. \"Connect Microsoft Defender\"). */\n label: string\n params?: ConnectionOnboardingParams\n /**\n * Optional app-provided finalize hook (extensionless path). Run in-process\n * after a successful onboarding, exactly like `connectivity.testHandler`.\n */\n onboardingHandler?: string\n}\n\nexport interface ConnectionOnboardingParams {\n /** App-setting key whose value selects the sovereign cloud (e.g. `azure_cloud`). */\n cloudSetting?: string\n /**\n * App permissions this connection needs — for display + audience selection.\n * The effective grant is fixed on the connector app registration, not here.\n */\n requiredResourceAccess?: OnboardingRequiredResource[]\n /** What the flow captures and where it maps back onto the connection. */\n capture?: OnboardingCapture\n /** True → the connection uses the platform token broker and stores NO secret. */\n brokered?: boolean\n /**\n * App settings the admin must supply BEFORE the consent click (they cannot be\n * derived from consent), e.g. Sentinel's subscription/resource-group/workspace.\n */\n requiredSettings?: string[]\n /** Post-consent provisioning steps the adapter runs (e.g. Sentinel ARM RBAC). */\n provisioning?: OnboardingProvisioningStep[]\n}\n\nexport interface OnboardingRequiredResource {\n /** Well-known resource name or appId (e.g. `WindowsDefenderATP`, `Graph`). */\n resource: string\n /** Application permissions requested on that resource (display only). */\n appPermissions: string[]\n}\n\nexport interface OnboardingCapture {\n /**\n * Where to write the consented tenant id. `setting:<key>` writes it into the\n * named app setting (the app libs read it as their `tenant_id`).\n */\n tenantId?: string\n}\n\n/** A post-consent provisioning step. Only ARM role assignment exists today. */\nexport interface OnboardingProvisioningStep {\n type: 'arm-role-assignment'\n /** Well-known built-in role name (resolved to a role-definition id by the adapter). */\n role: string\n /** ARM scope granularity for the assignment. */\n scope: 'resourceGroup' | 'subscription'\n /**\n * How the ARM token for the assignment is obtained:\n * - `manual` (default): show a portal deep-link/CLI + a verify probe. No\n * extra platform privilege — consent does not grant ARM RBAC.\n * - `delegated`: opt-in second delegated-ARM leg (requires the admin to hold\n * Owner / User Access Administrator). Not implemented in the first cut.\n */\n armToken?: 'manual' | 'delegated'\n}\n\n/**\n * App brand identity. The platform renders it in a per-app navbar above the\n * app's pages and exposes the colors to app pages as scoped CSS variables\n * (--veltrix-app-primary, --veltrix-app-accent).\n */\nexport interface AppBrandingDeclaration {\n /** Brand accent color as #RGB or #RRGGBB hex (e.g. CrowdStrike red). */\n primaryColor?: string\n /** Optional secondary color as #RGB or #RRGGBB hex. */\n accentColor?: string\n /**\n * Vendor logo shown in the app navbar and on the marketplace card. Either a\n * repo-relative .svg (preferred) or .png at most 128 KB, OR an absolute\n * https:// URL to an externally hosted asset. Rendered at ~28px height, so\n * use a wide/landscape mark with a transparent background.\n */\n logo?: string\n /** Optional logo variant for dark backgrounds; same constraints as logo. */\n logoDark?: string\n}\n\nexport interface AppConfigurationTypeManifest {\n id: string\n name: string\n description?: string\n canvasTemplate: string // Path to canvas template YAML\n defaultConfig?: string // Path to default config YAML\n\n handlers: {\n validate: string\n deploy: string\n rollback: string\n healthCheck: string\n driftDetect?: string | null\n getStatus: string\n /**\n * Optional options provider — powers live `remote-multiselect` config fields\n * (returns {value,label} options fetched from the target system). Loaded like\n * driftDetect (optional); omit when the config type has no live-option fields.\n */\n options?: string | null\n }\n\n targets: {\n componentTypes: string[]\n requiresCredential: boolean\n requiresConnectivity: boolean\n }\n}\n\nexport interface AppPermissionDeclaration {\n resource: string\n actions: string[]\n description?: string\n}\n\n// --- App UI & navigation contract ---\n//\n// The platform owns the chrome: breadcrumb, app header, navigation, permission\n// gating, error boundary and loading states are rendered identically for every\n// app. Apps own the page body and compose it from @veltrixsecops/ui.\n// Predictable shell, flexible body.\n\n/**\n * How the platform frames an app page.\n * - `standard` — page header + padded content area (default; use for most pages)\n * - `full-bleed` — content area with no padding/toolbar (custom canvases, maps)\n * - `canvas` — Configuration Canvas chrome (section rail + save/validate bar)\n */\nexport type AppPageLayout = 'standard' | 'full-bleed' | 'canvas'\nexport const APP_PAGE_LAYOUTS = ['standard', 'full-bleed', 'canvas'] as const\n\n/**\n * Where the page surfaces in navigation.\n * - `sidebar` — an entry beneath the app in the sidebar\n * - `tab` — a tab within its `parent` page\n * - `hidden` — routable but not linked (details/drill-down pages)\n */\nexport type AppPageNav = 'sidebar' | 'tab' | 'hidden'\nexport const APP_PAGE_NAV = ['sidebar', 'tab', 'hidden'] as const\n\n/** An app-scoped permission (declared in `permissions.app`) required to see a page. */\nexport interface AppPagePermission {\n resource: string\n action: string\n}\n\nexport interface AppPageDeclaration {\n /** Route beneath the app, e.g. `/indexes` → `/apps/<app-id>/indexes` */\n path: string\n /** Exported component name from the app's client entry */\n component: string\n label: string\n description?: string\n /** Icon name from the platform icon set; falls back to the app icon */\n icon?: string\n /** @deprecated use `nav: 'sidebar' | 'hidden'` — kept for backward compatibility */\n sidebar?: boolean\n /** Navigation placement. Defaults to `sidebar` when `sidebar: true`, else `hidden`. */\n nav?: AppPageNav\n /** Parent page `path` — required for `nav: 'tab'`, optional nesting for sidebar entries */\n parent?: string\n /** Optional sidebar section label, for apps with many pages */\n group?: string\n /** Deterministic ordering within its group/parent (ascending; ties break on label) */\n order?: number\n /** Layout preset the platform renders around the page body */\n layout?: AppPageLayout\n /** Hide the page (and its nav entry) unless the user holds this app permission */\n requiresPermission?: AppPagePermission\n}\n\nexport interface AppSettingDeclaration {\n key: string\n type: 'string' | 'number' | 'boolean' | 'select'\n label: string\n description?: string\n default?: string | number | boolean\n required?: boolean\n options?: Array<{ label: string; value: string }>\n}\n\n// --- API Response Types ---\n\nexport interface AppListItem {\n id: string\n appId: string\n name: string\n version: string\n vendor: string\n description: string\n category: string\n icon?: string\n logo?: string\n source: AppSource\n isDefault: boolean\n status: AppStatusType\n installed?: boolean\n enabled?: boolean\n}\n\nexport interface AppDetail extends AppListItem {\n license?: string\n homepage?: string\n repository?: string\n configurationTypes: Array<{\n id: string\n name: string\n description?: string\n componentTypes: string[]\n }>\n permissions: AppPermissionDeclaration[]\n settings: AppSettingDeclaration[]\n}\n\nexport interface AppInstallationDetail {\n id: string\n appId: string\n customerId: string\n version: string\n enabled: boolean\n installedBy: string\n installedAt: string\n settings: Record<string, unknown>\n status: AppInstallationStatus\n app: AppListItem\n}\n","// ========================================================================\n// Canonical app layout\n//\n// Every Veltrix app follows one predictable folder structure so apps are\n// easy to set up, review, and load. The `veltrix` CLI scaffolds it,\n// `veltrix validate` (and repo CI) warn on deviations, and the platform's\n// app engine resolves manifest references against it.\n// ========================================================================\n\n/** Canonical locations inside an app directory. */\nexport const APP_LAYOUT = {\n /** The app contract. Always at the app root. */\n manifest: 'manifest.yaml',\n /**\n * The unit of extension: config-types/<configTypeId>/ colocates everything\n * for one configuration type — canvas.yaml, defaults.yaml, the six pipeline\n * handlers (extensionless in the manifest), and __tests__/.\n */\n configTypesDir: 'config-types',\n /** Canvas form schema filename inside a config-type folder. */\n canvasFile: 'canvas.yaml',\n /** Default field values filename inside a config-type folder. */\n defaultsFile: 'defaults.yaml',\n /** Lifecycle hooks (camelCase): hooks/onInstall, hooks/onUninstall, ... */\n hooksDir: 'hooks',\n /** Shared app code used by multiple handlers (API clients, parsers). */\n libDir: 'lib',\n /** SQL migrations (requires manifest `database.tablePrefix`). */\n migrationsDir: 'migrations',\n /** Fastify route module receiving (fastify, AppRouteContext). */\n serverEntry: 'server/index',\n /** Client entry registering pages/sidebar items (optional). */\n clientEntry: 'client/index',\n /** Icons and logos (optional). */\n assetsDir: 'assets',\n /** Tests live next to the code they cover: handlers/<id>/__tests__/ */\n testsDirName: '__tests__',\n} as const\n\n/** The six pipeline handler names, in lifecycle order. */\nexport const HANDLER_NAMES = [\n 'validate',\n 'deploy',\n 'rollback',\n 'healthCheck',\n 'driftDetect',\n 'getStatus',\n] as const\n\nexport type HandlerName = (typeof HANDLER_NAMES)[number]\n\n/**\n * Conventional manifest paths for one configuration type — handy when\n * generating or checking a manifest programmatically.\n *\n * @example\n * conventionalPaths('indexes').handlers.deploy // 'config-types/indexes/deploy'\n */\nexport function conventionalPaths(configTypeId: string): {\n canvasTemplate: string\n defaultConfig: string\n handlers: Record<HandlerName, string>\n} {\n const base = `${APP_LAYOUT.configTypesDir}/${configTypeId}`\n const handlers = {} as Record<HandlerName, string>\n for (const name of HANDLER_NAMES) {\n handlers[name] = `${base}/${name}`\n }\n return {\n canvasTemplate: `${base}/${APP_LAYOUT.canvasFile}`,\n defaultConfig: `${base}/${APP_LAYOUT.defaultsFile}`,\n handlers,\n }\n}\n"],"mappings":";;;;;;;;;;AAqRO,IAAM,mBAAmB,CAAC,YAAY,cAAc,QAAQ;AAS5D,IAAM,eAAe,CAAC,WAAW,OAAO,QAAQ;;;ACpRhD,IAAM,aAAa;AAAA;AAAA,EAExB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMV,gBAAgB;AAAA;AAAA,EAEhB,YAAY;AAAA;AAAA,EAEZ,cAAc;AAAA;AAAA,EAEd,UAAU;AAAA;AAAA,EAEV,QAAQ;AAAA;AAAA,EAER,eAAe;AAAA;AAAA,EAEf,aAAa;AAAA;AAAA,EAEb,aAAa;AAAA;AAAA,EAEb,WAAW;AAAA;AAAA,EAEX,cAAc;AAChB;AAGO,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAWO,SAAS,kBAAkB,cAIhC;AACA,QAAM,OAAO,GAAG,WAAW,cAAc,IAAI,YAAY;AACzD,QAAM,WAAW,CAAC;AAClB,aAAW,QAAQ,eAAe;AAChC,aAAS,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI;AAAA,EAClC;AACA,SAAO;AAAA,IACL,gBAAgB,GAAG,IAAI,IAAI,WAAW,UAAU;AAAA,IAChD,eAAe,GAAG,IAAI,IAAI,WAAW,YAAY;AAAA,IACjD;AAAA,EACF;AACF;","names":[]}