@veltrixsecops/app-sdk 1.0.1 → 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 +60 -0
- package/dist/{chunk-TSEIWO6T.js → chunk-JOJTOQZQ.js} +15 -4
- package/dist/chunk-JOJTOQZQ.js.map +1 -0
- package/dist/chunk-PJN3ATPH.js +28 -0
- package/dist/chunk-PJN3ATPH.js.map +1 -0
- package/dist/{chunk-PJCHU6ZZ.js → chunk-VWFTOFTI.js} +1 -1
- package/dist/{chunk-PJCHU6ZZ.js.map → chunk-VWFTOFTI.js.map} +1 -1
- package/dist/client/index.cjs +55 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +59 -0
- package/dist/client/index.d.ts +59 -0
- package/dist/client/index.js +13 -0
- package/dist/client/index.js.map +1 -0
- package/dist/hooks/index.cjs +24 -2
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +54 -1
- package/dist/hooks/index.d.ts +54 -1
- package/dist/hooks/index.js +4 -1
- package/dist/index.cjs +88 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -1
- package/dist/index.d.ts +49 -1
- package/dist/index.js +64 -2
- package/dist/index.js.map +1 -1
- package/dist/pipeline/index.cjs.map +1 -1
- package/dist/pipeline/index.js +1 -1
- package/dist/{index-BI8f_mBw.d.cts → use-app-context-DdrtEPeb.d.cts} +68 -38
- package/dist/{index-BI8f_mBw.d.ts → use-app-context-DdrtEPeb.d.ts} +68 -38
- package/package.json +11 -1
- package/dist/chunk-TSEIWO6T.js.map +0 -1
package/README.md
CHANGED
|
@@ -73,6 +73,66 @@ export default async function onInstall({ db, appId }: AppHookContext): Promise<
|
|
|
73
73
|
|
|
74
74
|
```tsx
|
|
75
75
|
import { useAppContext, usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'
|
|
76
|
+
import { authFetch } from '@veltrixsecops/app-sdk/client'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Your `client/index.tsx` default-exports an `AppClientModule` (`{ id, pages, sidebarItems }`;
|
|
80
|
+
`pages` keys must match `manifest.client.pages[].component`). At packaging time the CLI
|
|
81
|
+
compiles it into a hermetic browser bundle (`client/dist/index.mjs`): `react`, `react-dom`,
|
|
82
|
+
`react/jsx-runtime`, and all `@veltrixsecops/app-sdk` imports are replaced with shims that
|
|
83
|
+
read the platform-provided runtime from `globalThis.__VELTRIX_APP_RUNTIME__`, so your
|
|
84
|
+
components render inside the host React tree with working hooks and shared context — never
|
|
85
|
+
bundle your own copy of React.
|
|
86
|
+
|
|
87
|
+
Two rules for app pages:
|
|
88
|
+
|
|
89
|
+
- Use **`authFetch`** (not plain `fetch`) for calls to your app's server routes
|
|
90
|
+
(`/api/apps/<app-id>/...`) — they are bearer-token protected and a plain `fetch`
|
|
91
|
+
receives 401s.
|
|
92
|
+
- Only import third-party client libraries if you accept them being compiled into your
|
|
93
|
+
bundle; keep pages lean.
|
|
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
|
+
|
|
111
|
+
## Standard app layout
|
|
112
|
+
|
|
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)`):
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
apps/<app-id>/
|
|
117
|
+
├── manifest.yaml # App contract
|
|
118
|
+
├── package.json / tsconfig.json / README.md
|
|
119
|
+
├── config-types/<configTypeId>/ # Everything for one configuration type:
|
|
120
|
+
│ ├── canvas.yaml # form schema
|
|
121
|
+
│ ├── defaults.yaml # default values
|
|
122
|
+
│ ├── validate.ts, deploy.ts, rollback.ts,
|
|
123
|
+
│ ├── healthCheck.ts, driftDetect.ts, getStatus.ts
|
|
124
|
+
│ └── __tests__/
|
|
125
|
+
├── lib/ # Shared app code (API clients)
|
|
126
|
+
├── hooks/ # onInstall.ts, onUninstall.ts, ...
|
|
127
|
+
├── migrations/ # SQL (with database.tablePrefix)
|
|
128
|
+
├── server/index.ts # Route module (AppRouteContext)
|
|
129
|
+
├── client/index.tsx + client/pages/ # Optional UI
|
|
130
|
+
└── assets/ # Optional icons
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { conventionalPaths } from '@veltrixsecops/app-sdk'
|
|
135
|
+
conventionalPaths('indexes').handlers.deploy // 'config-types/indexes/deploy'
|
|
76
136
|
```
|
|
77
137
|
|
|
78
138
|
## Package layout
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
authFetch
|
|
3
|
+
} from "./chunk-PJN3ATPH.js";
|
|
4
|
+
|
|
1
5
|
// src/hooks/use-app-context.ts
|
|
2
6
|
import { createContext, useContext } from "react";
|
|
3
|
-
var
|
|
7
|
+
var hostContext = globalThis.__VELTRIX_APP_RUNTIME__?.AppContext;
|
|
8
|
+
var AppContext = hostContext ?? createContext(null);
|
|
4
9
|
function useAppContext() {
|
|
5
10
|
const ctx = useContext(AppContext);
|
|
6
11
|
if (!ctx) {
|
|
@@ -18,7 +23,7 @@ function usePipelineStatus(appId) {
|
|
|
18
23
|
const refresh = useCallback(async () => {
|
|
19
24
|
try {
|
|
20
25
|
setIsLoading(true);
|
|
21
|
-
const response = await
|
|
26
|
+
const response = await authFetch(`/api/pipeline/summary?appId=${appId}`);
|
|
22
27
|
if (!response.ok) throw new Error(`Failed to fetch pipeline status`);
|
|
23
28
|
const result = await response.json();
|
|
24
29
|
setData(result);
|
|
@@ -35,9 +40,15 @@ function usePipelineStatus(appId) {
|
|
|
35
40
|
return { data, isLoading, error, refresh };
|
|
36
41
|
}
|
|
37
42
|
|
|
43
|
+
// src/hooks/use-app-branding.ts
|
|
44
|
+
function useAppBranding() {
|
|
45
|
+
return useAppContext().branding ?? null;
|
|
46
|
+
}
|
|
47
|
+
|
|
38
48
|
export {
|
|
39
49
|
AppContext,
|
|
40
50
|
useAppContext,
|
|
41
|
-
usePipelineStatus
|
|
51
|
+
usePipelineStatus,
|
|
52
|
+
useAppBranding
|
|
42
53
|
};
|
|
43
|
-
//# sourceMappingURL=chunk-
|
|
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":[]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/client/index.ts
|
|
2
|
+
var HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
|
|
3
|
+
function getHostRuntime() {
|
|
4
|
+
const runtime = globalThis[HOST_RUNTIME_GLOBAL];
|
|
5
|
+
return runtime ?? null;
|
|
6
|
+
}
|
|
7
|
+
function requireHostRuntime() {
|
|
8
|
+
const runtime = getHostRuntime();
|
|
9
|
+
if (!runtime) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
`Veltrix host runtime not found \u2014 app client bundles only run inside the Veltrix platform (missing globalThis.${HOST_RUNTIME_GLOBAL})`
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
return runtime;
|
|
15
|
+
}
|
|
16
|
+
function authFetch(input, init) {
|
|
17
|
+
const runtime = getHostRuntime();
|
|
18
|
+
if (runtime) return runtime.authFetch(input, init);
|
|
19
|
+
return fetch(input, init);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
HOST_RUNTIME_GLOBAL,
|
|
24
|
+
getHostRuntime,
|
|
25
|
+
requireHostRuntime,
|
|
26
|
+
authFetch
|
|
27
|
+
};
|
|
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":[]}
|
|
@@ -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'\
|
|
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":[]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/client/index.ts
|
|
21
|
+
var client_exports = {};
|
|
22
|
+
__export(client_exports, {
|
|
23
|
+
HOST_RUNTIME_GLOBAL: () => HOST_RUNTIME_GLOBAL,
|
|
24
|
+
authFetch: () => authFetch,
|
|
25
|
+
getHostRuntime: () => getHostRuntime,
|
|
26
|
+
requireHostRuntime: () => requireHostRuntime
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(client_exports);
|
|
29
|
+
var HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
|
|
30
|
+
function getHostRuntime() {
|
|
31
|
+
const runtime = globalThis[HOST_RUNTIME_GLOBAL];
|
|
32
|
+
return runtime ?? null;
|
|
33
|
+
}
|
|
34
|
+
function requireHostRuntime() {
|
|
35
|
+
const runtime = getHostRuntime();
|
|
36
|
+
if (!runtime) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Veltrix host runtime not found \u2014 app client bundles only run inside the Veltrix platform (missing globalThis.${HOST_RUNTIME_GLOBAL})`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return runtime;
|
|
42
|
+
}
|
|
43
|
+
function authFetch(input, init) {
|
|
44
|
+
const runtime = getHostRuntime();
|
|
45
|
+
if (runtime) return runtime.authFetch(input, init);
|
|
46
|
+
return fetch(input, init);
|
|
47
|
+
}
|
|
48
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
49
|
+
0 && (module.exports = {
|
|
50
|
+
HOST_RUNTIME_GLOBAL,
|
|
51
|
+
authFetch,
|
|
52
|
+
getHostRuntime,
|
|
53
|
+
requireHostRuntime
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=index.cjs.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":";;;;;;;;;;;;;;;;;;;;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":[]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ComponentType, LazyExoticComponent, Context } from 'react';
|
|
2
|
+
import { A as AppContextValue } from '../use-app-context-DdrtEPeb.cjs';
|
|
3
|
+
export { a as AppBrandingDeclaration } from '../use-app-context-DdrtEPeb.cjs';
|
|
4
|
+
|
|
5
|
+
/** Name of the global the platform installs before loading app bundles. */
|
|
6
|
+
declare const HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
|
|
7
|
+
/**
|
|
8
|
+
* The runtime surface the platform exposes to app client bundles.
|
|
9
|
+
* The react/reactDom/jsxRuntime members are the host's own module objects —
|
|
10
|
+
* app bundles are compiled with shims that re-export them, guaranteeing a
|
|
11
|
+
* single React instance per page.
|
|
12
|
+
*/
|
|
13
|
+
interface VeltrixHostRuntime {
|
|
14
|
+
/** The host's `react` module object. */
|
|
15
|
+
react: unknown;
|
|
16
|
+
/** The host's `react-dom` module object. */
|
|
17
|
+
reactDom: unknown;
|
|
18
|
+
/** The host's `react-dom/client` module object. */
|
|
19
|
+
reactDomClient?: unknown;
|
|
20
|
+
/** The host's `react/jsx-runtime` module object. */
|
|
21
|
+
jsxRuntime: unknown;
|
|
22
|
+
/** Shared app context — the host wraps app pages in its Provider. */
|
|
23
|
+
AppContext: Context<AppContextValue | null>;
|
|
24
|
+
/** fetch() with the platform's Authorization header attached. */
|
|
25
|
+
authFetch: (input: string, init?: RequestInit) => Promise<Response>;
|
|
26
|
+
/**
|
|
27
|
+
* The SDK surface app bundles receive for `@veltrixsecops/app-sdk`,
|
|
28
|
+
* `.../hooks`, and `.../client` imports (useAppContext, AppContext,
|
|
29
|
+
* usePipelineStatus, authFetch, getHostRuntime, ...).
|
|
30
|
+
*/
|
|
31
|
+
sdk: Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
/** Read the host runtime, or null outside the platform (tests, storybook). */
|
|
34
|
+
declare function getHostRuntime(): VeltrixHostRuntime | null;
|
|
35
|
+
/** Read the host runtime, throwing a diagnosable error when absent. */
|
|
36
|
+
declare function requireHostRuntime(): VeltrixHostRuntime;
|
|
37
|
+
/**
|
|
38
|
+
* fetch() that carries the platform's Authorization header. Required for an
|
|
39
|
+
* app page to call its own server routes (/api/apps/<app-id>/...), which are
|
|
40
|
+
* bearer-token protected. Falls back to plain fetch outside the platform.
|
|
41
|
+
*/
|
|
42
|
+
declare function authFetch(input: string, init?: RequestInit): Promise<Response>;
|
|
43
|
+
/** A sidebar entry contributed by the app's client entry module. */
|
|
44
|
+
interface AppSidebarItem {
|
|
45
|
+
path: string;
|
|
46
|
+
label: string;
|
|
47
|
+
icon?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Shape of the default export of an app's `client/index.tsx`.
|
|
51
|
+
* `pages` keys must match `manifest.client.pages[].component`.
|
|
52
|
+
*/
|
|
53
|
+
interface AppClientModule {
|
|
54
|
+
id: string;
|
|
55
|
+
pages: Record<string, ComponentType | LazyExoticComponent<ComponentType>>;
|
|
56
|
+
sidebarItems?: AppSidebarItem[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { type AppClientModule, type AppSidebarItem, HOST_RUNTIME_GLOBAL, type VeltrixHostRuntime, authFetch, getHostRuntime, requireHostRuntime };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ComponentType, LazyExoticComponent, Context } from 'react';
|
|
2
|
+
import { A as AppContextValue } from '../use-app-context-DdrtEPeb.js';
|
|
3
|
+
export { a as AppBrandingDeclaration } from '../use-app-context-DdrtEPeb.js';
|
|
4
|
+
|
|
5
|
+
/** Name of the global the platform installs before loading app bundles. */
|
|
6
|
+
declare const HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
|
|
7
|
+
/**
|
|
8
|
+
* The runtime surface the platform exposes to app client bundles.
|
|
9
|
+
* The react/reactDom/jsxRuntime members are the host's own module objects —
|
|
10
|
+
* app bundles are compiled with shims that re-export them, guaranteeing a
|
|
11
|
+
* single React instance per page.
|
|
12
|
+
*/
|
|
13
|
+
interface VeltrixHostRuntime {
|
|
14
|
+
/** The host's `react` module object. */
|
|
15
|
+
react: unknown;
|
|
16
|
+
/** The host's `react-dom` module object. */
|
|
17
|
+
reactDom: unknown;
|
|
18
|
+
/** The host's `react-dom/client` module object. */
|
|
19
|
+
reactDomClient?: unknown;
|
|
20
|
+
/** The host's `react/jsx-runtime` module object. */
|
|
21
|
+
jsxRuntime: unknown;
|
|
22
|
+
/** Shared app context — the host wraps app pages in its Provider. */
|
|
23
|
+
AppContext: Context<AppContextValue | null>;
|
|
24
|
+
/** fetch() with the platform's Authorization header attached. */
|
|
25
|
+
authFetch: (input: string, init?: RequestInit) => Promise<Response>;
|
|
26
|
+
/**
|
|
27
|
+
* The SDK surface app bundles receive for `@veltrixsecops/app-sdk`,
|
|
28
|
+
* `.../hooks`, and `.../client` imports (useAppContext, AppContext,
|
|
29
|
+
* usePipelineStatus, authFetch, getHostRuntime, ...).
|
|
30
|
+
*/
|
|
31
|
+
sdk: Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
/** Read the host runtime, or null outside the platform (tests, storybook). */
|
|
34
|
+
declare function getHostRuntime(): VeltrixHostRuntime | null;
|
|
35
|
+
/** Read the host runtime, throwing a diagnosable error when absent. */
|
|
36
|
+
declare function requireHostRuntime(): VeltrixHostRuntime;
|
|
37
|
+
/**
|
|
38
|
+
* fetch() that carries the platform's Authorization header. Required for an
|
|
39
|
+
* app page to call its own server routes (/api/apps/<app-id>/...), which are
|
|
40
|
+
* bearer-token protected. Falls back to plain fetch outside the platform.
|
|
41
|
+
*/
|
|
42
|
+
declare function authFetch(input: string, init?: RequestInit): Promise<Response>;
|
|
43
|
+
/** A sidebar entry contributed by the app's client entry module. */
|
|
44
|
+
interface AppSidebarItem {
|
|
45
|
+
path: string;
|
|
46
|
+
label: string;
|
|
47
|
+
icon?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Shape of the default export of an app's `client/index.tsx`.
|
|
51
|
+
* `pages` keys must match `manifest.client.pages[].component`.
|
|
52
|
+
*/
|
|
53
|
+
interface AppClientModule {
|
|
54
|
+
id: string;
|
|
55
|
+
pages: Record<string, ComponentType | LazyExoticComponent<ComponentType>>;
|
|
56
|
+
sidebarItems?: AppSidebarItem[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { type AppClientModule, type AppSidebarItem, HOST_RUNTIME_GLOBAL, type VeltrixHostRuntime, authFetch, getHostRuntime, requireHostRuntime };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/hooks/index.cjs
CHANGED
|
@@ -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
|
});
|
|
@@ -28,7 +29,8 @@ module.exports = __toCommonJS(hooks_exports);
|
|
|
28
29
|
|
|
29
30
|
// src/hooks/use-app-context.ts
|
|
30
31
|
var import_react = require("react");
|
|
31
|
-
var
|
|
32
|
+
var hostContext = globalThis.__VELTRIX_APP_RUNTIME__?.AppContext;
|
|
33
|
+
var AppContext = hostContext ?? (0, import_react.createContext)(null);
|
|
32
34
|
function useAppContext() {
|
|
33
35
|
const ctx = (0, import_react.useContext)(AppContext);
|
|
34
36
|
if (!ctx) {
|
|
@@ -39,6 +41,20 @@ function useAppContext() {
|
|
|
39
41
|
|
|
40
42
|
// src/hooks/use-pipeline-status.ts
|
|
41
43
|
var import_react2 = require("react");
|
|
44
|
+
|
|
45
|
+
// src/client/index.ts
|
|
46
|
+
var HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
|
|
47
|
+
function getHostRuntime() {
|
|
48
|
+
const runtime = globalThis[HOST_RUNTIME_GLOBAL];
|
|
49
|
+
return runtime ?? null;
|
|
50
|
+
}
|
|
51
|
+
function authFetch(input, init) {
|
|
52
|
+
const runtime = getHostRuntime();
|
|
53
|
+
if (runtime) return runtime.authFetch(input, init);
|
|
54
|
+
return fetch(input, init);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/hooks/use-pipeline-status.ts
|
|
42
58
|
function usePipelineStatus(appId) {
|
|
43
59
|
const [data, setData] = (0, import_react2.useState)(null);
|
|
44
60
|
const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
|
|
@@ -46,7 +62,7 @@ function usePipelineStatus(appId) {
|
|
|
46
62
|
const refresh = (0, import_react2.useCallback)(async () => {
|
|
47
63
|
try {
|
|
48
64
|
setIsLoading(true);
|
|
49
|
-
const response = await
|
|
65
|
+
const response = await authFetch(`/api/pipeline/summary?appId=${appId}`);
|
|
50
66
|
if (!response.ok) throw new Error(`Failed to fetch pipeline status`);
|
|
51
67
|
const result = await response.json();
|
|
52
68
|
setData(result);
|
|
@@ -62,9 +78,15 @@ function usePipelineStatus(appId) {
|
|
|
62
78
|
}, [refresh]);
|
|
63
79
|
return { data, isLoading, error, refresh };
|
|
64
80
|
}
|
|
81
|
+
|
|
82
|
+
// src/hooks/use-app-branding.ts
|
|
83
|
+
function useAppBranding() {
|
|
84
|
+
return useAppContext().branding ?? null;
|
|
85
|
+
}
|
|
65
86
|
// Annotate the CommonJS export names for ESM import in node:
|
|
66
87
|
0 && (module.exports = {
|
|
67
88
|
AppContext,
|
|
89
|
+
useAppBranding,
|
|
68
90
|
useAppContext,
|
|
69
91
|
usePipelineStatus
|
|
70
92
|
});
|
package/dist/hooks/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hooks/index.ts","../../src/hooks/use-app-context.ts","../../src/hooks/use-pipeline-status.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 } 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\nexport const AppContext = 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'\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 const response = await fetch(`/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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,mBAA0C;AAkBnC,IAAM,iBAAa,4BAAsC,IAAI;AAe7D,SAAS,gBAAiC;AAC/C,QAAM,UAAM,yBAAW,UAAU;AACjC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,SAAO;AACT;;;AC5CA,IAAAA,gBAAiD;AA+B1C,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;AACjB,YAAM,WAAW,MAAM,MAAM,+BAA+B,KAAK,EAAE;AACnE,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"]}
|
package/dist/hooks/index.d.cts
CHANGED
|
@@ -1,2 +1,55 @@
|
|
|
1
|
-
|
|
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';
|
|
4
|
+
|
|
5
|
+
interface PipelineStatusData {
|
|
6
|
+
pendingApprovals: number;
|
|
7
|
+
activeDeployments: number;
|
|
8
|
+
failedDeployments: number;
|
|
9
|
+
unresolvedDrifts: number;
|
|
10
|
+
recentDeployments: Array<{
|
|
11
|
+
id: string;
|
|
12
|
+
canvasName: string;
|
|
13
|
+
environment: string;
|
|
14
|
+
status: string;
|
|
15
|
+
startedAt: string;
|
|
16
|
+
completedAt?: string;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Hook to access pipeline status for the current app/customer.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* import { usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'
|
|
25
|
+
*
|
|
26
|
+
* function Dashboard() {
|
|
27
|
+
* const { data, isLoading } = usePipelineStatus('my-app')
|
|
28
|
+
* if (isLoading) return <Spinner />
|
|
29
|
+
* return <div>{data.activeDeployments} active deployments</div>
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare function usePipelineStatus(appId: string): {
|
|
34
|
+
data: PipelineStatusData | null;
|
|
35
|
+
isLoading: boolean;
|
|
36
|
+
error: Error | null;
|
|
37
|
+
refresh: () => Promise<void>;
|
|
38
|
+
};
|
|
39
|
+
|
|
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 };
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1,2 +1,55 @@
|
|
|
1
|
-
|
|
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';
|
|
4
|
+
|
|
5
|
+
interface PipelineStatusData {
|
|
6
|
+
pendingApprovals: number;
|
|
7
|
+
activeDeployments: number;
|
|
8
|
+
failedDeployments: number;
|
|
9
|
+
unresolvedDrifts: number;
|
|
10
|
+
recentDeployments: Array<{
|
|
11
|
+
id: string;
|
|
12
|
+
canvasName: string;
|
|
13
|
+
environment: string;
|
|
14
|
+
status: string;
|
|
15
|
+
startedAt: string;
|
|
16
|
+
completedAt?: string;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Hook to access pipeline status for the current app/customer.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* import { usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'
|
|
25
|
+
*
|
|
26
|
+
* function Dashboard() {
|
|
27
|
+
* const { data, isLoading } = usePipelineStatus('my-app')
|
|
28
|
+
* if (isLoading) return <Spinner />
|
|
29
|
+
* return <div>{data.activeDeployments} active deployments</div>
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare function usePipelineStatus(appId: string): {
|
|
34
|
+
data: PipelineStatusData | null;
|
|
35
|
+
isLoading: boolean;
|
|
36
|
+
error: Error | null;
|
|
37
|
+
refresh: () => Promise<void>;
|
|
38
|
+
};
|
|
39
|
+
|
|
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 };
|
package/dist/hooks/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AppContext,
|
|
3
|
+
useAppBranding,
|
|
3
4
|
useAppContext,
|
|
4
5
|
usePipelineStatus
|
|
5
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-JOJTOQZQ.js";
|
|
7
|
+
import "../chunk-PJN3ATPH.js";
|
|
6
8
|
export {
|
|
7
9
|
AppContext,
|
|
10
|
+
useAppBranding,
|
|
8
11
|
useAppContext,
|
|
9
12
|
usePipelineStatus
|
|
10
13
|
};
|