@veltrixsecops/app-sdk 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -92,6 +92,22 @@ Two rules for app pages:
92
92
  - Only import third-party client libraries if you accept them being compiled into your
93
93
  bundle; keep pages lean.
94
94
 
95
+ ## Branding
96
+
97
+ Declare your vendor identity in the manifest and the platform applies it in defined
98
+ slots — the app's navbar (logo + accent color) and scoped CSS variables. The platform
99
+ controls where brand color appears, so it stays meaningful rather than theming the shell:
100
+
101
+ ```yaml
102
+ branding:
103
+ primaryColor: "#FC0000" # #RGB or #RRGGBB
104
+ logo: ./assets/logo.svg # svg (preferred) or png, <=128 KB, ~28px display height
105
+ ```
106
+
107
+ In pages, prefer the CSS variables (`--veltrix-app-primary`, `--veltrix-app-accent`);
108
+ use `useAppBranding()` from `./hooks` when you need the values programmatically.
109
+ SVG logos must not contain scripts or event handlers — the validator rejects them.
110
+
95
111
  ## Standard app layout
96
112
 
97
113
  Every Veltrix app follows one canonical folder structure — the CLI scaffolds it (`veltrix init`), `veltrix validate` warns on deviations, and the SDK exports it as constants (`APP_LAYOUT`, `HANDLER_NAMES`, `conventionalPaths(configTypeId)`):
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  authFetch
3
- } from "./chunk-I4JJX4LL.js";
3
+ } from "./chunk-PJN3ATPH.js";
4
4
 
5
5
  // src/hooks/use-app-context.ts
6
6
  import { createContext, useContext } from "react";
@@ -40,9 +40,15 @@ function usePipelineStatus(appId) {
40
40
  return { data, isLoading, error, refresh };
41
41
  }
42
42
 
43
+ // src/hooks/use-app-branding.ts
44
+ function useAppBranding() {
45
+ return useAppContext().branding ?? null;
46
+ }
47
+
43
48
  export {
44
49
  AppContext,
45
50
  useAppContext,
46
- usePipelineStatus
51
+ usePipelineStatus,
52
+ useAppBranding
47
53
  };
48
- //# sourceMappingURL=chunk-JDEA2F7I.js.map
54
+ //# sourceMappingURL=chunk-JOJTOQZQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/use-app-context.ts","../src/hooks/use-pipeline-status.ts","../src/hooks/use-app-branding.ts"],"sourcesContent":["// ========================================================================\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 } 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// 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","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"],"mappings":";;;;;AAKA,SAAS,eAAe,kBAAgC;AAyBxD,IAAM,cACH,WAAuC,yBAGvC;AAEI,IAAM,aACX,eAAe,cAAsC,IAAI;AAepD,SAAS,gBAAiC;AAC/C,QAAM,MAAM,WAAW,UAAU;AACjC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,SAAO;AACT;;;AC1DA,SAAS,UAAU,WAAW,mBAAmB;AAgC1C,SAAS,kBAAkB,OAAe;AAC/C,QAAM,CAAC,MAAM,OAAO,IAAI,SAAoC,IAAI;AAChE,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,IAAI;AAC/C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,IAAI;AAErD,QAAM,UAAU,YAAY,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,YAAU,MAAM;AACd,YAAQ;AAAA,EACV,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,EAAE,MAAM,WAAW,OAAO,QAAQ;AAC3C;;;AC1CO,SAAS,iBAAgD;AAC9D,SAAO,cAAc,EAAE,YAAY;AACrC;","names":[]}
@@ -25,4 +25,4 @@ export {
25
25
  requireHostRuntime,
26
26
  authFetch
27
27
  };
28
- //# sourceMappingURL=chunk-I4JJX4LL.js.map
28
+ //# sourceMappingURL=chunk-PJN3ATPH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/index.ts"],"sourcesContent":["// ========================================================================\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'\n\nexport type { AppBrandingDeclaration } from '../types/manifest'\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\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"],"mappings":";AAuBO,IAAM,sBAAsB;AA8B5B,SAAS,iBAA4C;AAC1D,QAAM,UAAW,WAAuC,mBAAmB;AAC3E,SAAQ,WAAkC;AAC5C;AAGO,SAAS,qBAAyC;AACvD,QAAM,UAAU,eAAe;AAC/B,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR,qHAC0C,mBAAmB;AAAA,IAC/D;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,UAAU,OAAe,MAAuC;AAC9E,QAAM,UAAU,eAAe;AAC/B,MAAI,QAAS,QAAO,QAAQ,UAAU,OAAO,IAAI;AACjD,SAAO,MAAM,OAAO,IAAI;AAC1B;","names":[]}
@@ -30,4 +30,4 @@ export {
30
30
  defineHealthChecker,
31
31
  defineDriftDetector
32
32
  };
33
- //# sourceMappingURL=chunk-PJCHU6ZZ.js.map
33
+ //# sourceMappingURL=chunk-VWFTOFTI.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../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"],"sourcesContent":["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'\r\n\r\n/**\r\n * Define a rollback handler for a configuration type.\r\n */\r\nexport function defineRollbackHandler(\r\n handler: (ctx: RollbackContext) => Promise<RollbackResult>,\r\n): (ctx: RollbackContext) => Promise<RollbackResult> {\r\n return handler\r\n}\r\n","import type { HealthCheckContext, HealthCheckResult } from '../types/pipeline'\r\n\r\n/**\r\n * Define a health check handler for a configuration type.\r\n */\r\nexport function defineHealthChecker(\r\n handler: (ctx: HealthCheckContext) => Promise<HealthCheckResult>,\r\n): (ctx: HealthCheckContext) => Promise<HealthCheckResult> {\r\n return handler\r\n}\r\n","import type { DriftContext, DriftResult } from '../types/pipeline'\r\n\r\n/**\r\n * Define a drift detection handler for a configuration type.\r\n */\r\nexport function defineDriftDetector(\r\n handler: (ctx: DriftContext) => Promise<DriftResult>,\r\n): (ctx: DriftContext) => Promise<DriftResult> {\r\n return handler\r\n}\r\n"],"mappings":";AAmBO,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;","names":[]}
1
+ {"version":3,"sources":["../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"],"sourcesContent":["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"],"mappings":";AAmBO,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;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/index.ts"],"sourcesContent":["// ========================================================================\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'\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\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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBO,IAAM,sBAAsB;AA8B5B,SAAS,iBAA4C;AAC1D,QAAM,UAAW,WAAuC,mBAAmB;AAC3E,SAAQ,WAAkC;AAC5C;AAGO,SAAS,qBAAyC;AACvD,QAAM,UAAU,eAAe;AAC/B,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR,qHAC0C,mBAAmB;AAAA,IAC/D;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,UAAU,OAAe,MAAuC;AAC9E,QAAM,UAAU,eAAe;AAC/B,MAAI,QAAS,QAAO,QAAQ,UAAU,OAAO,IAAI;AACjD,SAAO,MAAM,OAAO,IAAI;AAC1B;","names":[]}
1
+ {"version":3,"sources":["../../src/client/index.ts"],"sourcesContent":["// ========================================================================\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'\n\nexport type { AppBrandingDeclaration } from '../types/manifest'\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\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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBO,IAAM,sBAAsB;AA8B5B,SAAS,iBAA4C;AAC1D,QAAM,UAAW,WAAuC,mBAAmB;AAC3E,SAAQ,WAAkC;AAC5C;AAGO,SAAS,qBAAyC;AACvD,QAAM,UAAU,eAAe;AAC/B,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR,qHAC0C,mBAAmB;AAAA,IAC/D;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,UAAU,OAAe,MAAuC;AAC9E,QAAM,UAAU,eAAe;AAC/B,MAAI,QAAS,QAAO,QAAQ,UAAU,OAAO,IAAI;AACjD,SAAO,MAAM,OAAO,IAAI;AAC1B;","names":[]}
@@ -1,5 +1,6 @@
1
1
  import { ComponentType, LazyExoticComponent, Context } from 'react';
2
- import { A as AppContextValue } from '../use-app-context-PBuG144m.cjs';
2
+ import { A as AppContextValue } from '../use-app-context-DdrtEPeb.cjs';
3
+ export { a as AppBrandingDeclaration } from '../use-app-context-DdrtEPeb.cjs';
3
4
 
4
5
  /** Name of the global the platform installs before loading app bundles. */
5
6
  declare const HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
@@ -1,5 +1,6 @@
1
1
  import { ComponentType, LazyExoticComponent, Context } from 'react';
2
- import { A as AppContextValue } from '../use-app-context-PBuG144m.js';
2
+ import { A as AppContextValue } from '../use-app-context-DdrtEPeb.js';
3
+ export { a as AppBrandingDeclaration } from '../use-app-context-DdrtEPeb.js';
3
4
 
4
5
  /** Name of the global the platform installs before loading app bundles. */
5
6
  declare const HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
@@ -3,7 +3,7 @@ import {
3
3
  authFetch,
4
4
  getHostRuntime,
5
5
  requireHostRuntime
6
- } from "../chunk-I4JJX4LL.js";
6
+ } from "../chunk-PJN3ATPH.js";
7
7
  export {
8
8
  HOST_RUNTIME_GLOBAL,
9
9
  authFetch,
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var hooks_exports = {};
22
22
  __export(hooks_exports, {
23
23
  AppContext: () => AppContext,
24
+ useAppBranding: () => useAppBranding,
24
25
  useAppContext: () => useAppContext,
25
26
  usePipelineStatus: () => usePipelineStatus
26
27
  });
@@ -77,9 +78,15 @@ function usePipelineStatus(appId) {
77
78
  }, [refresh]);
78
79
  return { data, isLoading, error, refresh };
79
80
  }
81
+
82
+ // src/hooks/use-app-branding.ts
83
+ function useAppBranding() {
84
+ return useAppContext().branding ?? null;
85
+ }
80
86
  // Annotate the CommonJS export names for ESM import in node:
81
87
  0 && (module.exports = {
82
88
  AppContext,
89
+ useAppBranding,
83
90
  useAppContext,
84
91
  usePipelineStatus
85
92
  });
@@ -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"],"sourcesContent":["export { useAppContext, AppContext, type AppContextValue } from './use-app-context'\r\nexport { usePipelineStatus, type PipelineStatusData } from './use-pipeline-status'\r\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 } from '../types/platform'\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\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'\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\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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,mBAAwD;AAqBxD,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;;;ACtDA,IAAAA,gBAAiD;;;ACqB1C,IAAM,sBAAsB;AA8B5B,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;;;AD7CO,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;","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"],"sourcesContent":["export { useAppContext, AppContext, type AppContextValue } from './use-app-context'\nexport { usePipelineStatus, type PipelineStatusData } from './use-pipeline-status'\nexport { useAppBranding } from './use-app-branding'\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 } 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// 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'\n\nexport type { AppBrandingDeclaration } from '../types/manifest'\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\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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,mBAAwD;AAyBxD,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;;;AC1DA,IAAAA,gBAAiD;;;ACuB1C,IAAM,sBAAsB;AA8B5B,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;;;AD/CO,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;","names":["import_react"]}
@@ -1,4 +1,5 @@
1
- export { d as AppContext, A as AppContextValue, w as useAppContext } from '../use-app-context-PBuG144m.cjs';
1
+ import { a as AppBrandingDeclaration } from '../use-app-context-DdrtEPeb.cjs';
2
+ export { e as AppContext, A as AppContextValue, x as useAppContext } from '../use-app-context-DdrtEPeb.cjs';
2
3
  import 'react';
3
4
 
4
5
  interface PipelineStatusData {
@@ -36,4 +37,19 @@ declare function usePipelineStatus(appId: string): {
36
37
  refresh: () => Promise<void>;
37
38
  };
38
39
 
39
- export { type PipelineStatusData, usePipelineStatus };
40
+ /**
41
+ * The app's brand identity as declared in manifest.yaml `branding`.
42
+ *
43
+ * The platform already applies it in the defined slots (the app navbar and
44
+ * the scoped CSS variables --veltrix-app-primary / --veltrix-app-accent on
45
+ * the app page container). Use this hook only when a page needs the values
46
+ * programmatically — prefer the CSS variables for styling:
47
+ *
48
+ * @example
49
+ * ```tsx
50
+ * <span style={{ color: 'var(--veltrix-app-primary)' }}>12 detections</span>
51
+ * ```
52
+ */
53
+ declare function useAppBranding(): AppBrandingDeclaration | null;
54
+
55
+ export { type PipelineStatusData, useAppBranding, usePipelineStatus };
@@ -1,4 +1,5 @@
1
- export { d as AppContext, A as AppContextValue, w as useAppContext } from '../use-app-context-PBuG144m.js';
1
+ import { a as AppBrandingDeclaration } from '../use-app-context-DdrtEPeb.js';
2
+ export { e as AppContext, A as AppContextValue, x as useAppContext } from '../use-app-context-DdrtEPeb.js';
2
3
  import 'react';
3
4
 
4
5
  interface PipelineStatusData {
@@ -36,4 +37,19 @@ declare function usePipelineStatus(appId: string): {
36
37
  refresh: () => Promise<void>;
37
38
  };
38
39
 
39
- export { type PipelineStatusData, usePipelineStatus };
40
+ /**
41
+ * The app's brand identity as declared in manifest.yaml `branding`.
42
+ *
43
+ * The platform already applies it in the defined slots (the app navbar and
44
+ * the scoped CSS variables --veltrix-app-primary / --veltrix-app-accent on
45
+ * the app page container). Use this hook only when a page needs the values
46
+ * programmatically — prefer the CSS variables for styling:
47
+ *
48
+ * @example
49
+ * ```tsx
50
+ * <span style={{ color: 'var(--veltrix-app-primary)' }}>12 detections</span>
51
+ * ```
52
+ */
53
+ declare function useAppBranding(): AppBrandingDeclaration | null;
54
+
55
+ export { type PipelineStatusData, useAppBranding, usePipelineStatus };
@@ -1,11 +1,13 @@
1
1
  import {
2
2
  AppContext,
3
+ useAppBranding,
3
4
  useAppContext,
4
5
  usePipelineStatus
5
- } from "../chunk-JDEA2F7I.js";
6
- import "../chunk-I4JJX4LL.js";
6
+ } from "../chunk-JOJTOQZQ.js";
7
+ import "../chunk-PJN3ATPH.js";
7
8
  export {
8
9
  AppContext,
10
+ useAppBranding,
9
11
  useAppContext,
10
12
  usePipelineStatus
11
13
  };
package/dist/index.cjs CHANGED
@@ -31,6 +31,7 @@ __export(src_exports, {
31
31
  defineHealthChecker: () => defineHealthChecker,
32
32
  defineRollbackHandler: () => defineRollbackHandler,
33
33
  defineValidator: () => defineValidator,
34
+ useAppBranding: () => useAppBranding,
34
35
  useAppContext: () => useAppContext,
35
36
  usePipelineStatus: () => usePipelineStatus
36
37
  });
@@ -113,6 +114,11 @@ function usePipelineStatus(appId) {
113
114
  return { data, isLoading, error, refresh };
114
115
  }
115
116
 
117
+ // src/hooks/use-app-branding.ts
118
+ function useAppBranding() {
119
+ return useAppContext().branding ?? null;
120
+ }
121
+
116
122
  // src/types/manifest.ts
117
123
  var APP_PAGE_LAYOUTS = ["standard", "full-bleed", "canvas"];
118
124
  var APP_PAGE_NAV = ["sidebar", "tab", "hidden"];
@@ -179,6 +185,7 @@ function conventionalPaths(configTypeId) {
179
185
  defineHealthChecker,
180
186
  defineRollbackHandler,
181
187
  defineValidator,
188
+ useAppBranding,
182
189
  useAppContext,
183
190
  usePipelineStatus
184
191
  });
@@ -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/hooks/use-app-context.ts","../src/hooks/use-pipeline-status.ts","../src/client/index.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// Import pipeline helpers, hooks, and types to build your app.\n//\n// Usage:\n// import { defineValidator, defineDeployer } from '@veltrixsecops/app-sdk/pipeline'\n// import { useAppContext, usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'\n// import type { PipelineContext, DeployContext } from '@veltrixsecops/app-sdk'\n// ========================================================================\n\n// Pipeline handler helpers\nexport {\n defineValidator,\n defineDeployer,\n defineRollbackHandler,\n defineHealthChecker,\n defineDriftDetector,\n} from './pipeline'\n\n// React hooks\nexport {\n useAppContext,\n AppContext,\n usePipelineStatus,\n} from './hooks'\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 ConnectivityProviderRef,\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 Credential,\n Connectivity,\n Tag,\n User,\n Customer,\n PlatformDatabaseClient,\n AppHookContext,\n AppRouteContext,\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 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'\r\n\r\n/**\r\n * Define a rollback handler for a configuration type.\r\n */\r\nexport function defineRollbackHandler(\r\n handler: (ctx: RollbackContext) => Promise<RollbackResult>,\r\n): (ctx: RollbackContext) => Promise<RollbackResult> {\r\n return handler\r\n}\r\n","import type { HealthCheckContext, HealthCheckResult } from '../types/pipeline'\r\n\r\n/**\r\n * Define a health check handler for a configuration type.\r\n */\r\nexport function defineHealthChecker(\r\n handler: (ctx: HealthCheckContext) => Promise<HealthCheckResult>,\r\n): (ctx: HealthCheckContext) => Promise<HealthCheckResult> {\r\n return handler\r\n}\r\n","import type { DriftContext, DriftResult } from '../types/pipeline'\r\n\r\n/**\r\n * Define a drift detection handler for a configuration type.\r\n */\r\nexport function defineDriftDetector(\r\n handler: (ctx: DriftContext) => Promise<DriftResult>,\r\n): (ctx: DriftContext) => Promise<DriftResult> {\r\n return handler\r\n}\r\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 } from '../types/platform'\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\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'\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\n/** Read the host runtime, or null outside the platform (tests, storybook). */\nexport function getHostRuntime(): VeltrixHostRuntime | null {\n const runtime = (globalThis as Record<string, unknown>)[HOST_RUNTIME_GLOBAL]\n return (runtime as VeltrixHostRuntime) ?? null\n}\n\n/** Read the host runtime, throwing a diagnosable error when absent. */\nexport function requireHostRuntime(): VeltrixHostRuntime {\n const runtime = getHostRuntime()\n if (!runtime) {\n throw new Error(\n 'Veltrix host runtime not found — app client bundles only run inside the ' +\n `Veltrix platform (missing globalThis.${HOST_RUNTIME_GLOBAL})`,\n )\n }\n return runtime\n}\n\n/**\n * fetch() that carries the platform's Authorization header. Required for an\n * app page to call its own server routes (/api/apps/<app-id>/...), which are\n * bearer-token protected. Falls back to plain fetch outside the platform.\n */\nexport function authFetch(input: string, init?: RequestInit): Promise<Response> {\n const runtime = getHostRuntime()\n if (runtime) return runtime.authFetch(input, init)\n return fetch(input, init)\n}\n\n/** A sidebar entry contributed by the app's client entry module. */\nexport interface AppSidebarItem {\n path: string\n label: string\n icon?: string\n}\n\n/**\n * Shape of the default export of an app's `client/index.tsx`.\n * `pages` keys must match `manifest.client.pages[].component`.\n */\nexport interface AppClientModule {\n id: string\n pages: Record<string, ComponentType | LazyExoticComponent<ComponentType>>\n sidebarItems?: AppSidebarItem[]\n}\n","// ========================================================================\n// 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\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\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;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;;;ACJA,mBAAwD;AAqBxD,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;;;ACtDA,IAAAA,gBAAiD;;;ACqB1C,IAAM,sBAAsB;AA8B5B,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;;;AD7CO,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;;;AE8DO,IAAM,mBAAmB,CAAC,YAAY,cAAc,QAAQ;AAS5D,IAAM,eAAe,CAAC,WAAW,OAAO,QAAQ;;;ACvHhD,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":["import_react"]}
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/hooks/use-app-context.ts","../src/hooks/use-pipeline-status.ts","../src/client/index.ts","../src/hooks/use-app-branding.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// Import pipeline helpers, hooks, and types to build your app.\n//\n// Usage:\n// import { defineValidator, defineDeployer } from '@veltrixsecops/app-sdk/pipeline'\n// import { useAppContext, usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'\n// import type { PipelineContext, DeployContext } from '@veltrixsecops/app-sdk'\n// ========================================================================\n\n// Pipeline handler helpers\nexport {\n defineValidator,\n defineDeployer,\n defineRollbackHandler,\n defineHealthChecker,\n defineDriftDetector,\n} from './pipeline'\n\n// React hooks\nexport {\n useAppContext,\n AppContext,\n usePipelineStatus,\n useAppBranding,\n} from './hooks'\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 ConnectivityProviderRef,\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 Credential,\n Connectivity,\n Tag,\n User,\n Customer,\n PlatformDatabaseClient,\n AppHookContext,\n AppRouteContext,\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// 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 } 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// 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'\n\nexport type { AppBrandingDeclaration } from '../types/manifest'\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\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","// ========================================================================\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\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;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;;;ACJA,mBAAwD;AAyBxD,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;;;AC1DA,IAAAA,gBAAiD;;;ACuB1C,IAAM,sBAAsB;AA8B5B,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;;;AD/CO,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;;;ACkIO,IAAM,mBAAmB,CAAC,YAAY,cAAc,QAAQ;AAS5D,IAAM,eAAe,CAAC,WAAW,OAAO,QAAQ;;;ACnJhD,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":["import_react"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { C as CanvasSectionSnapshot, a as CanvasSnapshot, b as ComponentConfigStatus, c as ComponentRef, d as ConfigStatus, e as ConnectivityProviderRef, 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, P as PipelineContext, s as PlatformDataApi, R as RollbackContext, t as RollbackHandler, u as RollbackResult, U as UserRef, V as ValidateHandler, v as ValidationError, w as ValidationResult, x as ValidationWarning, y as defineDeployer, z as defineDriftDetector, A as defineHealthChecker, B as defineRollbackHandler, F as defineValidator } from './index-CKeIf4Jx.cjs';
2
- export { a as APP_PAGE_LAYOUTS, b as APP_PAGE_NAV, c as AppConfigurationTypeManifest, d as AppContext, A 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 AppRouteContext, q as AppSettingDeclaration, r as AppSource, s as AppStatusType, C as Component, t as Connectivity, u as Credential, v as Customer, P as PlatformDatabaseClient, T as Tag, U as User, w as useAppContext } from './use-app-context-PBuG144m.cjs';
3
- export { PipelineStatusData, usePipelineStatus } from './hooks/index.cjs';
2
+ export { b as APP_PAGE_LAYOUTS, c as APP_PAGE_NAV, a as AppBrandingDeclaration, d as AppConfigurationTypeManifest, e as AppContext, A as AppContextValue, f as AppDetail, g as AppHookContext, h as AppInstallationDetail, i as AppInstallationStatus, j as AppListItem, k as AppManifest, l as AppPageDeclaration, m as AppPageLayout, n as AppPageNav, o as AppPagePermission, p as AppPermissionDeclaration, q as AppRouteContext, r as AppSettingDeclaration, s as AppSource, t as AppStatusType, C as Component, u as Connectivity, v as Credential, w as Customer, P as PlatformDatabaseClient, T as Tag, U as User, x as useAppContext } from './use-app-context-DdrtEPeb.cjs';
3
+ export { PipelineStatusData, useAppBranding, usePipelineStatus } from './hooks/index.cjs';
4
4
  import 'react';
5
5
 
6
6
  /** Canonical locations inside an app directory. */
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { C as CanvasSectionSnapshot, a as CanvasSnapshot, b as ComponentConfigStatus, c as ComponentRef, d as ConfigStatus, e as ConnectivityProviderRef, 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, P as PipelineContext, s as PlatformDataApi, R as RollbackContext, t as RollbackHandler, u as RollbackResult, U as UserRef, V as ValidateHandler, v as ValidationError, w as ValidationResult, x as ValidationWarning, y as defineDeployer, z as defineDriftDetector, A as defineHealthChecker, B as defineRollbackHandler, F as defineValidator } from './index-CKeIf4Jx.js';
2
- export { a as APP_PAGE_LAYOUTS, b as APP_PAGE_NAV, c as AppConfigurationTypeManifest, d as AppContext, A 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 AppRouteContext, q as AppSettingDeclaration, r as AppSource, s as AppStatusType, C as Component, t as Connectivity, u as Credential, v as Customer, P as PlatformDatabaseClient, T as Tag, U as User, w as useAppContext } from './use-app-context-PBuG144m.js';
3
- export { PipelineStatusData, usePipelineStatus } from './hooks/index.js';
2
+ export { b as APP_PAGE_LAYOUTS, c as APP_PAGE_NAV, a as AppBrandingDeclaration, d as AppConfigurationTypeManifest, e as AppContext, A as AppContextValue, f as AppDetail, g as AppHookContext, h as AppInstallationDetail, i as AppInstallationStatus, j as AppListItem, k as AppManifest, l as AppPageDeclaration, m as AppPageLayout, n as AppPageNav, o as AppPagePermission, p as AppPermissionDeclaration, q as AppRouteContext, r as AppSettingDeclaration, s as AppSource, t as AppStatusType, C as Component, u as Connectivity, v as Credential, w as Customer, P as PlatformDatabaseClient, T as Tag, U as User, x as useAppContext } from './use-app-context-DdrtEPeb.js';
3
+ export { PipelineStatusData, useAppBranding, usePipelineStatus } from './hooks/index.js';
4
4
  import 'react';
5
5
 
6
6
  /** Canonical locations inside an app directory. */
package/dist/index.js CHANGED
@@ -4,13 +4,14 @@ import {
4
4
  defineHealthChecker,
5
5
  defineRollbackHandler,
6
6
  defineValidator
7
- } from "./chunk-PJCHU6ZZ.js";
7
+ } from "./chunk-VWFTOFTI.js";
8
8
  import {
9
9
  AppContext,
10
+ useAppBranding,
10
11
  useAppContext,
11
12
  usePipelineStatus
12
- } from "./chunk-JDEA2F7I.js";
13
- import "./chunk-I4JJX4LL.js";
13
+ } from "./chunk-JOJTOQZQ.js";
14
+ import "./chunk-PJN3ATPH.js";
14
15
 
15
16
  // src/types/manifest.ts
16
17
  var APP_PAGE_LAYOUTS = ["standard", "full-bleed", "canvas"];
@@ -77,6 +78,7 @@ export {
77
78
  defineHealthChecker,
78
79
  defineRollbackHandler,
79
80
  defineValidator,
81
+ useAppBranding,
80
82
  useAppContext,
81
83
  usePipelineStatus
82
84
  };
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\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\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":";;;;;;;;;;;;;;;AAwHO,IAAM,mBAAmB,CAAC,YAAY,cAAc,QAAQ;AAS5D,IAAM,eAAe,CAAC,WAAW,OAAO,QAAQ;;;ACvHhD,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\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\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":";;;;;;;;;;;;;;;;AAoJO,IAAM,mBAAmB,CAAC,YAAY,cAAc,QAAQ;AAS5D,IAAM,eAAe,CAAC,WAAW,OAAO,QAAQ;;;ACnJhD,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 +1 @@
1
- {"version":3,"sources":["../../src/pipeline/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"],"sourcesContent":["export { defineValidator } from './define-validator'\r\nexport { defineDeployer } from './define-deployer'\r\nexport { defineRollbackHandler } from './define-rollback-handler'\r\nexport { defineHealthChecker } from './define-health-checker'\r\nexport { defineDriftDetector } from './define-drift-detector'\r\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'\r\n\r\n/**\r\n * Define a rollback handler for a configuration type.\r\n */\r\nexport function defineRollbackHandler(\r\n handler: (ctx: RollbackContext) => Promise<RollbackResult>,\r\n): (ctx: RollbackContext) => Promise<RollbackResult> {\r\n return handler\r\n}\r\n","import type { HealthCheckContext, HealthCheckResult } from '../types/pipeline'\r\n\r\n/**\r\n * Define a health check handler for a configuration type.\r\n */\r\nexport function defineHealthChecker(\r\n handler: (ctx: HealthCheckContext) => Promise<HealthCheckResult>,\r\n): (ctx: HealthCheckContext) => Promise<HealthCheckResult> {\r\n return handler\r\n}\r\n","import type { DriftContext, DriftResult } from '../types/pipeline'\r\n\r\n/**\r\n * Define a drift detection handler for a configuration type.\r\n */\r\nexport function defineDriftDetector(\r\n handler: (ctx: DriftContext) => Promise<DriftResult>,\r\n): (ctx: DriftContext) => Promise<DriftResult> {\r\n return handler\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;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;","names":[]}
1
+ {"version":3,"sources":["../../src/pipeline/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"],"sourcesContent":["export { defineValidator } from './define-validator'\nexport { defineDeployer } from './define-deployer'\nexport { defineRollbackHandler } from './define-rollback-handler'\nexport { defineHealthChecker } from './define-health-checker'\nexport { defineDriftDetector } from './define-drift-detector'\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"],"mappings":";;;;;;;;;;;;;;;;;;;;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;","names":[]}
@@ -4,7 +4,7 @@ import {
4
4
  defineHealthChecker,
5
5
  defineRollbackHandler,
6
6
  defineValidator
7
- } from "../chunk-PJCHU6ZZ.js";
7
+ } from "../chunk-VWFTOFTI.js";
8
8
  export {
9
9
  defineDeployer,
10
10
  defineDriftDetector,
@@ -39,6 +39,13 @@ interface AppManifest {
39
39
  entry: string;
40
40
  pages?: AppPageDeclaration[];
41
41
  };
42
+ /**
43
+ * Vendor brand identity, applied by the platform in defined slots only —
44
+ * the app navbar (logo, accent) and scoped CSS variables. The platform,
45
+ * not the app, decides where brand color appears, so one vendor's palette
46
+ * never overwhelms the product shell.
47
+ */
48
+ branding?: AppBrandingDeclaration;
42
49
  hooks?: {
43
50
  onInstall?: string;
44
51
  onUninstall?: string;
@@ -49,6 +56,25 @@ interface AppManifest {
49
56
  events?: string[];
50
57
  settings?: AppSettingDeclaration[];
51
58
  }
59
+ /**
60
+ * App brand identity. The platform renders it in a per-app navbar above the
61
+ * app's pages and exposes the colors to app pages as scoped CSS variables
62
+ * (--veltrix-app-primary, --veltrix-app-accent).
63
+ */
64
+ interface AppBrandingDeclaration {
65
+ /** Brand accent color as #RGB or #RRGGBB hex (e.g. CrowdStrike red). */
66
+ primaryColor?: string;
67
+ /** Optional secondary color as #RGB or #RRGGBB hex. */
68
+ accentColor?: string;
69
+ /**
70
+ * Vendor logo shown in the app navbar. Repo-relative .svg (preferred) or
71
+ * .png, at most 128 KB; rendered at 28px height, so use a wide/landscape
72
+ * mark with transparent background.
73
+ */
74
+ logo?: string;
75
+ /** Optional logo variant for dark backgrounds; same constraints as logo. */
76
+ logoDark?: string;
77
+ }
52
78
  interface AppConfigurationTypeManifest {
53
79
  id: string;
54
80
  name: string;
@@ -268,6 +294,8 @@ interface AppContextValue {
268
294
  getCredentials: () => Promise<Credential[]>;
269
295
  getTags: () => Promise<Tag[]>;
270
296
  settings: Record<string, unknown>;
297
+ /** The app's manifest branding, resolved by the platform (null when unset). */
298
+ branding?: AppBrandingDeclaration | null;
271
299
  }
272
300
  declare const AppContext: Context<AppContextValue | null>;
273
301
  /**
@@ -285,4 +313,4 @@ declare const AppContext: Context<AppContextValue | null>;
285
313
  */
286
314
  declare function useAppContext(): AppContextValue;
287
315
 
288
- export { type AppContextValue as A, type Component as C, type PlatformDatabaseClient as P, type Tag as T, type User as U, APP_PAGE_LAYOUTS as a, APP_PAGE_NAV as b, type AppConfigurationTypeManifest as c, AppContext as d, type AppDetail as e, type AppHookContext as f, type AppInstallationDetail as g, type AppInstallationStatus as h, type AppListItem as i, type AppManifest as j, type AppPageDeclaration as k, type AppPageLayout as l, type AppPageNav as m, type AppPagePermission as n, type AppPermissionDeclaration as o, type AppRouteContext as p, type AppSettingDeclaration as q, type AppSource as r, type AppStatusType as s, type Connectivity as t, type Credential as u, type Customer as v, useAppContext as w };
316
+ export { type AppContextValue as A, type Component as C, type PlatformDatabaseClient as P, type Tag as T, type User as U, type AppBrandingDeclaration as a, APP_PAGE_LAYOUTS as b, APP_PAGE_NAV as c, type AppConfigurationTypeManifest as d, AppContext as e, type AppDetail as f, type AppHookContext as g, type AppInstallationDetail as h, type AppInstallationStatus as i, type AppListItem as j, type AppManifest as k, type AppPageDeclaration as l, type AppPageLayout as m, type AppPageNav as n, type AppPagePermission as o, type AppPermissionDeclaration as p, type AppRouteContext as q, type AppSettingDeclaration as r, type AppSource as s, type AppStatusType as t, type Connectivity as u, type Credential as v, type Customer as w, useAppContext as x };
@@ -39,6 +39,13 @@ interface AppManifest {
39
39
  entry: string;
40
40
  pages?: AppPageDeclaration[];
41
41
  };
42
+ /**
43
+ * Vendor brand identity, applied by the platform in defined slots only —
44
+ * the app navbar (logo, accent) and scoped CSS variables. The platform,
45
+ * not the app, decides where brand color appears, so one vendor's palette
46
+ * never overwhelms the product shell.
47
+ */
48
+ branding?: AppBrandingDeclaration;
42
49
  hooks?: {
43
50
  onInstall?: string;
44
51
  onUninstall?: string;
@@ -49,6 +56,25 @@ interface AppManifest {
49
56
  events?: string[];
50
57
  settings?: AppSettingDeclaration[];
51
58
  }
59
+ /**
60
+ * App brand identity. The platform renders it in a per-app navbar above the
61
+ * app's pages and exposes the colors to app pages as scoped CSS variables
62
+ * (--veltrix-app-primary, --veltrix-app-accent).
63
+ */
64
+ interface AppBrandingDeclaration {
65
+ /** Brand accent color as #RGB or #RRGGBB hex (e.g. CrowdStrike red). */
66
+ primaryColor?: string;
67
+ /** Optional secondary color as #RGB or #RRGGBB hex. */
68
+ accentColor?: string;
69
+ /**
70
+ * Vendor logo shown in the app navbar. Repo-relative .svg (preferred) or
71
+ * .png, at most 128 KB; rendered at 28px height, so use a wide/landscape
72
+ * mark with transparent background.
73
+ */
74
+ logo?: string;
75
+ /** Optional logo variant for dark backgrounds; same constraints as logo. */
76
+ logoDark?: string;
77
+ }
52
78
  interface AppConfigurationTypeManifest {
53
79
  id: string;
54
80
  name: string;
@@ -268,6 +294,8 @@ interface AppContextValue {
268
294
  getCredentials: () => Promise<Credential[]>;
269
295
  getTags: () => Promise<Tag[]>;
270
296
  settings: Record<string, unknown>;
297
+ /** The app's manifest branding, resolved by the platform (null when unset). */
298
+ branding?: AppBrandingDeclaration | null;
271
299
  }
272
300
  declare const AppContext: Context<AppContextValue | null>;
273
301
  /**
@@ -285,4 +313,4 @@ declare const AppContext: Context<AppContextValue | null>;
285
313
  */
286
314
  declare function useAppContext(): AppContextValue;
287
315
 
288
- export { type AppContextValue as A, type Component as C, type PlatformDatabaseClient as P, type Tag as T, type User as U, APP_PAGE_LAYOUTS as a, APP_PAGE_NAV as b, type AppConfigurationTypeManifest as c, AppContext as d, type AppDetail as e, type AppHookContext as f, type AppInstallationDetail as g, type AppInstallationStatus as h, type AppListItem as i, type AppManifest as j, type AppPageDeclaration as k, type AppPageLayout as l, type AppPageNav as m, type AppPagePermission as n, type AppPermissionDeclaration as o, type AppRouteContext as p, type AppSettingDeclaration as q, type AppSource as r, type AppStatusType as s, type Connectivity as t, type Credential as u, type Customer as v, useAppContext as w };
316
+ export { type AppContextValue as A, type Component as C, type PlatformDatabaseClient as P, type Tag as T, type User as U, type AppBrandingDeclaration as a, APP_PAGE_LAYOUTS as b, APP_PAGE_NAV as c, type AppConfigurationTypeManifest as d, AppContext as e, type AppDetail as f, type AppHookContext as g, type AppInstallationDetail as h, type AppInstallationStatus as i, type AppListItem as j, type AppManifest as k, type AppPageDeclaration as l, type AppPageLayout as m, type AppPageNav as n, type AppPagePermission as o, type AppPermissionDeclaration as p, type AppRouteContext as q, type AppSettingDeclaration as r, type AppSource as s, type AppStatusType as t, type Connectivity as u, type Credential as v, type Customer as w, useAppContext as x };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veltrixsecops/app-sdk",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Official SDK for building Veltrix Security-as-Code apps — typed pipeline handler contracts, lifecycle hook types, manifest types, and React hooks.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/client/index.ts"],"sourcesContent":["// ========================================================================\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'\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\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"],"mappings":";AAqBO,IAAM,sBAAsB;AA8B5B,SAAS,iBAA4C;AAC1D,QAAM,UAAW,WAAuC,mBAAmB;AAC3E,SAAQ,WAAkC;AAC5C;AAGO,SAAS,qBAAyC;AACvD,QAAM,UAAU,eAAe;AAC/B,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR,qHAC0C,mBAAmB;AAAA,IAC/D;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,UAAU,OAAe,MAAuC;AAC9E,QAAM,UAAU,eAAe;AAC/B,MAAI,QAAS,QAAO,QAAQ,UAAU,OAAO,IAAI;AACjD,SAAO,MAAM,OAAO,IAAI;AAC1B;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/use-app-context.ts","../src/hooks/use-pipeline-status.ts"],"sourcesContent":["// ========================================================================\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 } from '../types/platform'\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\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"],"mappings":";;;;;AAKA,SAAS,eAAe,kBAAgC;AAqBxD,IAAM,cACH,WAAuC,yBAGvC;AAEI,IAAM,aACX,eAAe,cAAsC,IAAI;AAepD,SAAS,gBAAiC;AAC/C,QAAM,MAAM,WAAW,UAAU;AACjC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,SAAO;AACT;;;ACtDA,SAAS,UAAU,WAAW,mBAAmB;AAgC1C,SAAS,kBAAkB,OAAe;AAC/C,QAAM,CAAC,MAAM,OAAO,IAAI,SAAoC,IAAI;AAChE,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,IAAI;AAC/C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,IAAI;AAErD,QAAM,UAAU,YAAY,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,YAAU,MAAM;AACd,YAAQ;AAAA,EACV,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,EAAE,MAAM,WAAW,OAAO,QAAQ;AAC3C;","names":[]}