@veltrixsecops/app-sdk 1.0.0 → 1.2.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 +46 -2
- package/dist/chunk-I4JJX4LL.js +28 -0
- package/dist/chunk-I4JJX4LL.js.map +1 -0
- package/dist/{chunk-TSEIWO6T.js → chunk-JDEA2F7I.js} +8 -3
- package/dist/chunk-JDEA2F7I.js.map +1 -0
- package/dist/client/index.cjs +55 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +58 -0
- package/dist/client/index.d.ts +58 -0
- package/dist/client/index.js +13 -0
- package/dist/client/index.js.map +1 -0
- package/dist/hooks/index.cjs +17 -2
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +38 -1
- package/dist/hooks/index.d.ts +38 -1
- package/dist/hooks/index.js +2 -1
- package/dist/index.cjs +81 -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 +61 -1
- package/dist/index.js.map +1 -1
- package/dist/{index-BI8f_mBw.d.cts → use-app-context-PBuG144m.d.cts} +40 -38
- package/dist/{index-BI8f_mBw.d.ts → use-app-context-PBuG144m.d.ts} +40 -38
- package/package.json +16 -5
- package/dist/chunk-TSEIWO6T.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @veltrixsecops/app-sdk
|
|
2
2
|
|
|
3
|
-
The official SDK for building [Veltrix](https://
|
|
3
|
+
The official SDK for building [Veltrix](https://veltrixsecops.com) Security-as-Code apps. Community apps live in the open-source [veltrix-apps](https://github.com/captivatortechnologies/veltrix-apps) repository; the Veltrix platform itself is a hosted SaaS.
|
|
4
4
|
|
|
5
5
|
A Veltrix app packages everything needed to manage one security tool's configuration as code: pipeline handlers (validate → deploy → rollback → health-check → drift-detect → status), canvas templates, database migrations, lifecycle hooks, and optional client pages. This SDK provides the typed contracts and helpers for all of it.
|
|
6
6
|
|
|
@@ -73,6 +73,50 @@ 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
|
+
## Standard app layout
|
|
96
|
+
|
|
97
|
+
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)`):
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
apps/<app-id>/
|
|
101
|
+
├── manifest.yaml # App contract
|
|
102
|
+
├── package.json / tsconfig.json / README.md
|
|
103
|
+
├── config-types/<configTypeId>/ # Everything for one configuration type:
|
|
104
|
+
│ ├── canvas.yaml # form schema
|
|
105
|
+
│ ├── defaults.yaml # default values
|
|
106
|
+
│ ├── validate.ts, deploy.ts, rollback.ts,
|
|
107
|
+
│ ├── healthCheck.ts, driftDetect.ts, getStatus.ts
|
|
108
|
+
│ └── __tests__/
|
|
109
|
+
├── lib/ # Shared app code (API clients)
|
|
110
|
+
├── hooks/ # onInstall.ts, onUninstall.ts, ...
|
|
111
|
+
├── migrations/ # SQL (with database.tablePrefix)
|
|
112
|
+
├── server/index.ts # Route module (AppRouteContext)
|
|
113
|
+
├── client/index.tsx + client/pages/ # Optional UI
|
|
114
|
+
└── assets/ # Optional icons
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { conventionalPaths } from '@veltrixsecops/app-sdk'
|
|
119
|
+
conventionalPaths('indexes').handlers.deploy // 'config-types/indexes/deploy'
|
|
76
120
|
```
|
|
77
121
|
|
|
78
122
|
## Package layout
|
|
@@ -85,7 +129,7 @@ import { useAppContext, usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'
|
|
|
85
129
|
|
|
86
130
|
## Building an app
|
|
87
131
|
|
|
88
|
-
Start from
|
|
132
|
+
Community apps live in the [veltrix-apps](https://github.com/captivatortechnologies/veltrix-apps) repository. Start from its [`_template`](https://github.com/captivatortechnologies/veltrix-apps/tree/main/_template) app and see the [contribution guide](https://github.com/captivatortechnologies/veltrix-apps/blob/main/CONTRIBUTING.md).
|
|
89
133
|
|
|
90
134
|
## License
|
|
91
135
|
|
|
@@ -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-I4JJX4LL.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\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,6 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
authFetch
|
|
3
|
+
} from "./chunk-I4JJX4LL.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);
|
|
@@ -40,4 +45,4 @@ export {
|
|
|
40
45
|
useAppContext,
|
|
41
46
|
usePipelineStatus
|
|
42
47
|
};
|
|
43
|
-
//# sourceMappingURL=chunk-
|
|
48
|
+
//# sourceMappingURL=chunk-JDEA2F7I.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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":[]}
|
|
@@ -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\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":[]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ComponentType, LazyExoticComponent, Context } from 'react';
|
|
2
|
+
import { A as AppContextValue } from '../use-app-context-PBuG144m.cjs';
|
|
3
|
+
|
|
4
|
+
/** Name of the global the platform installs before loading app bundles. */
|
|
5
|
+
declare const HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
|
|
6
|
+
/**
|
|
7
|
+
* The runtime surface the platform exposes to app client bundles.
|
|
8
|
+
* The react/reactDom/jsxRuntime members are the host's own module objects —
|
|
9
|
+
* app bundles are compiled with shims that re-export them, guaranteeing a
|
|
10
|
+
* single React instance per page.
|
|
11
|
+
*/
|
|
12
|
+
interface VeltrixHostRuntime {
|
|
13
|
+
/** The host's `react` module object. */
|
|
14
|
+
react: unknown;
|
|
15
|
+
/** The host's `react-dom` module object. */
|
|
16
|
+
reactDom: unknown;
|
|
17
|
+
/** The host's `react-dom/client` module object. */
|
|
18
|
+
reactDomClient?: unknown;
|
|
19
|
+
/** The host's `react/jsx-runtime` module object. */
|
|
20
|
+
jsxRuntime: unknown;
|
|
21
|
+
/** Shared app context — the host wraps app pages in its Provider. */
|
|
22
|
+
AppContext: Context<AppContextValue | null>;
|
|
23
|
+
/** fetch() with the platform's Authorization header attached. */
|
|
24
|
+
authFetch: (input: string, init?: RequestInit) => Promise<Response>;
|
|
25
|
+
/**
|
|
26
|
+
* The SDK surface app bundles receive for `@veltrixsecops/app-sdk`,
|
|
27
|
+
* `.../hooks`, and `.../client` imports (useAppContext, AppContext,
|
|
28
|
+
* usePipelineStatus, authFetch, getHostRuntime, ...).
|
|
29
|
+
*/
|
|
30
|
+
sdk: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
/** Read the host runtime, or null outside the platform (tests, storybook). */
|
|
33
|
+
declare function getHostRuntime(): VeltrixHostRuntime | null;
|
|
34
|
+
/** Read the host runtime, throwing a diagnosable error when absent. */
|
|
35
|
+
declare function requireHostRuntime(): VeltrixHostRuntime;
|
|
36
|
+
/**
|
|
37
|
+
* fetch() that carries the platform's Authorization header. Required for an
|
|
38
|
+
* app page to call its own server routes (/api/apps/<app-id>/...), which are
|
|
39
|
+
* bearer-token protected. Falls back to plain fetch outside the platform.
|
|
40
|
+
*/
|
|
41
|
+
declare function authFetch(input: string, init?: RequestInit): Promise<Response>;
|
|
42
|
+
/** A sidebar entry contributed by the app's client entry module. */
|
|
43
|
+
interface AppSidebarItem {
|
|
44
|
+
path: string;
|
|
45
|
+
label: string;
|
|
46
|
+
icon?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Shape of the default export of an app's `client/index.tsx`.
|
|
50
|
+
* `pages` keys must match `manifest.client.pages[].component`.
|
|
51
|
+
*/
|
|
52
|
+
interface AppClientModule {
|
|
53
|
+
id: string;
|
|
54
|
+
pages: Record<string, ComponentType | LazyExoticComponent<ComponentType>>;
|
|
55
|
+
sidebarItems?: AppSidebarItem[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { type AppClientModule, type AppSidebarItem, HOST_RUNTIME_GLOBAL, type VeltrixHostRuntime, authFetch, getHostRuntime, requireHostRuntime };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ComponentType, LazyExoticComponent, Context } from 'react';
|
|
2
|
+
import { A as AppContextValue } from '../use-app-context-PBuG144m.js';
|
|
3
|
+
|
|
4
|
+
/** Name of the global the platform installs before loading app bundles. */
|
|
5
|
+
declare const HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
|
|
6
|
+
/**
|
|
7
|
+
* The runtime surface the platform exposes to app client bundles.
|
|
8
|
+
* The react/reactDom/jsxRuntime members are the host's own module objects —
|
|
9
|
+
* app bundles are compiled with shims that re-export them, guaranteeing a
|
|
10
|
+
* single React instance per page.
|
|
11
|
+
*/
|
|
12
|
+
interface VeltrixHostRuntime {
|
|
13
|
+
/** The host's `react` module object. */
|
|
14
|
+
react: unknown;
|
|
15
|
+
/** The host's `react-dom` module object. */
|
|
16
|
+
reactDom: unknown;
|
|
17
|
+
/** The host's `react-dom/client` module object. */
|
|
18
|
+
reactDomClient?: unknown;
|
|
19
|
+
/** The host's `react/jsx-runtime` module object. */
|
|
20
|
+
jsxRuntime: unknown;
|
|
21
|
+
/** Shared app context — the host wraps app pages in its Provider. */
|
|
22
|
+
AppContext: Context<AppContextValue | null>;
|
|
23
|
+
/** fetch() with the platform's Authorization header attached. */
|
|
24
|
+
authFetch: (input: string, init?: RequestInit) => Promise<Response>;
|
|
25
|
+
/**
|
|
26
|
+
* The SDK surface app bundles receive for `@veltrixsecops/app-sdk`,
|
|
27
|
+
* `.../hooks`, and `.../client` imports (useAppContext, AppContext,
|
|
28
|
+
* usePipelineStatus, authFetch, getHostRuntime, ...).
|
|
29
|
+
*/
|
|
30
|
+
sdk: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
/** Read the host runtime, or null outside the platform (tests, storybook). */
|
|
33
|
+
declare function getHostRuntime(): VeltrixHostRuntime | null;
|
|
34
|
+
/** Read the host runtime, throwing a diagnosable error when absent. */
|
|
35
|
+
declare function requireHostRuntime(): VeltrixHostRuntime;
|
|
36
|
+
/**
|
|
37
|
+
* fetch() that carries the platform's Authorization header. Required for an
|
|
38
|
+
* app page to call its own server routes (/api/apps/<app-id>/...), which are
|
|
39
|
+
* bearer-token protected. Falls back to plain fetch outside the platform.
|
|
40
|
+
*/
|
|
41
|
+
declare function authFetch(input: string, init?: RequestInit): Promise<Response>;
|
|
42
|
+
/** A sidebar entry contributed by the app's client entry module. */
|
|
43
|
+
interface AppSidebarItem {
|
|
44
|
+
path: string;
|
|
45
|
+
label: string;
|
|
46
|
+
icon?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Shape of the default export of an app's `client/index.tsx`.
|
|
50
|
+
* `pages` keys must match `manifest.client.pages[].component`.
|
|
51
|
+
*/
|
|
52
|
+
interface AppClientModule {
|
|
53
|
+
id: string;
|
|
54
|
+
pages: Record<string, ComponentType | LazyExoticComponent<ComponentType>>;
|
|
55
|
+
sidebarItems?: AppSidebarItem[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
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
|
@@ -28,7 +28,8 @@ module.exports = __toCommonJS(hooks_exports);
|
|
|
28
28
|
|
|
29
29
|
// src/hooks/use-app-context.ts
|
|
30
30
|
var import_react = require("react");
|
|
31
|
-
var
|
|
31
|
+
var hostContext = globalThis.__VELTRIX_APP_RUNTIME__?.AppContext;
|
|
32
|
+
var AppContext = hostContext ?? (0, import_react.createContext)(null);
|
|
32
33
|
function useAppContext() {
|
|
33
34
|
const ctx = (0, import_react.useContext)(AppContext);
|
|
34
35
|
if (!ctx) {
|
|
@@ -39,6 +40,20 @@ function useAppContext() {
|
|
|
39
40
|
|
|
40
41
|
// src/hooks/use-pipeline-status.ts
|
|
41
42
|
var import_react2 = require("react");
|
|
43
|
+
|
|
44
|
+
// src/client/index.ts
|
|
45
|
+
var HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
|
|
46
|
+
function getHostRuntime() {
|
|
47
|
+
const runtime = globalThis[HOST_RUNTIME_GLOBAL];
|
|
48
|
+
return runtime ?? null;
|
|
49
|
+
}
|
|
50
|
+
function authFetch(input, init) {
|
|
51
|
+
const runtime = getHostRuntime();
|
|
52
|
+
if (runtime) return runtime.authFetch(input, init);
|
|
53
|
+
return fetch(input, init);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/hooks/use-pipeline-status.ts
|
|
42
57
|
function usePipelineStatus(appId) {
|
|
43
58
|
const [data, setData] = (0, import_react2.useState)(null);
|
|
44
59
|
const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
|
|
@@ -46,7 +61,7 @@ function usePipelineStatus(appId) {
|
|
|
46
61
|
const refresh = (0, import_react2.useCallback)(async () => {
|
|
47
62
|
try {
|
|
48
63
|
setIsLoading(true);
|
|
49
|
-
const response = await
|
|
64
|
+
const response = await authFetch(`/api/pipeline/summary?appId=${appId}`);
|
|
50
65
|
if (!response.ok) throw new Error(`Failed to fetch pipeline status`);
|
|
51
66
|
const result = await response.json();
|
|
52
67
|
setData(result);
|
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
|
|
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"]}
|
package/dist/hooks/index.d.cts
CHANGED
|
@@ -1,2 +1,39 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { d as AppContext, A as AppContextValue, w as useAppContext } from '../use-app-context-PBuG144m.cjs';
|
|
2
2
|
import 'react';
|
|
3
|
+
|
|
4
|
+
interface PipelineStatusData {
|
|
5
|
+
pendingApprovals: number;
|
|
6
|
+
activeDeployments: number;
|
|
7
|
+
failedDeployments: number;
|
|
8
|
+
unresolvedDrifts: number;
|
|
9
|
+
recentDeployments: Array<{
|
|
10
|
+
id: string;
|
|
11
|
+
canvasName: string;
|
|
12
|
+
environment: string;
|
|
13
|
+
status: string;
|
|
14
|
+
startedAt: string;
|
|
15
|
+
completedAt?: string;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Hook to access pipeline status for the current app/customer.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* import { usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'
|
|
24
|
+
*
|
|
25
|
+
* function Dashboard() {
|
|
26
|
+
* const { data, isLoading } = usePipelineStatus('my-app')
|
|
27
|
+
* if (isLoading) return <Spinner />
|
|
28
|
+
* return <div>{data.activeDeployments} active deployments</div>
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare function usePipelineStatus(appId: string): {
|
|
33
|
+
data: PipelineStatusData | null;
|
|
34
|
+
isLoading: boolean;
|
|
35
|
+
error: Error | null;
|
|
36
|
+
refresh: () => Promise<void>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export { type PipelineStatusData, usePipelineStatus };
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1,2 +1,39 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { d as AppContext, A as AppContextValue, w as useAppContext } from '../use-app-context-PBuG144m.js';
|
|
2
2
|
import 'react';
|
|
3
|
+
|
|
4
|
+
interface PipelineStatusData {
|
|
5
|
+
pendingApprovals: number;
|
|
6
|
+
activeDeployments: number;
|
|
7
|
+
failedDeployments: number;
|
|
8
|
+
unresolvedDrifts: number;
|
|
9
|
+
recentDeployments: Array<{
|
|
10
|
+
id: string;
|
|
11
|
+
canvasName: string;
|
|
12
|
+
environment: string;
|
|
13
|
+
status: string;
|
|
14
|
+
startedAt: string;
|
|
15
|
+
completedAt?: string;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Hook to access pipeline status for the current app/customer.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* import { usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'
|
|
24
|
+
*
|
|
25
|
+
* function Dashboard() {
|
|
26
|
+
* const { data, isLoading } = usePipelineStatus('my-app')
|
|
27
|
+
* if (isLoading) return <Spinner />
|
|
28
|
+
* return <div>{data.activeDeployments} active deployments</div>
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare function usePipelineStatus(appId: string): {
|
|
33
|
+
data: PipelineStatusData | null;
|
|
34
|
+
isLoading: boolean;
|
|
35
|
+
error: Error | null;
|
|
36
|
+
refresh: () => Promise<void>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export { type PipelineStatusData, usePipelineStatus };
|
package/dist/hooks/index.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -20,7 +20,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
APP_LAYOUT: () => APP_LAYOUT,
|
|
24
|
+
APP_PAGE_LAYOUTS: () => APP_PAGE_LAYOUTS,
|
|
25
|
+
APP_PAGE_NAV: () => APP_PAGE_NAV,
|
|
23
26
|
AppContext: () => AppContext,
|
|
27
|
+
HANDLER_NAMES: () => HANDLER_NAMES,
|
|
28
|
+
conventionalPaths: () => conventionalPaths,
|
|
24
29
|
defineDeployer: () => defineDeployer,
|
|
25
30
|
defineDriftDetector: () => defineDriftDetector,
|
|
26
31
|
defineHealthChecker: () => defineHealthChecker,
|
|
@@ -58,7 +63,8 @@ function defineDriftDetector(handler) {
|
|
|
58
63
|
|
|
59
64
|
// src/hooks/use-app-context.ts
|
|
60
65
|
var import_react = require("react");
|
|
61
|
-
var
|
|
66
|
+
var hostContext = globalThis.__VELTRIX_APP_RUNTIME__?.AppContext;
|
|
67
|
+
var AppContext = hostContext ?? (0, import_react.createContext)(null);
|
|
62
68
|
function useAppContext() {
|
|
63
69
|
const ctx = (0, import_react.useContext)(AppContext);
|
|
64
70
|
if (!ctx) {
|
|
@@ -69,6 +75,20 @@ function useAppContext() {
|
|
|
69
75
|
|
|
70
76
|
// src/hooks/use-pipeline-status.ts
|
|
71
77
|
var import_react2 = require("react");
|
|
78
|
+
|
|
79
|
+
// src/client/index.ts
|
|
80
|
+
var HOST_RUNTIME_GLOBAL = "__VELTRIX_APP_RUNTIME__";
|
|
81
|
+
function getHostRuntime() {
|
|
82
|
+
const runtime = globalThis[HOST_RUNTIME_GLOBAL];
|
|
83
|
+
return runtime ?? null;
|
|
84
|
+
}
|
|
85
|
+
function authFetch(input, init) {
|
|
86
|
+
const runtime = getHostRuntime();
|
|
87
|
+
if (runtime) return runtime.authFetch(input, init);
|
|
88
|
+
return fetch(input, init);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/hooks/use-pipeline-status.ts
|
|
72
92
|
function usePipelineStatus(appId) {
|
|
73
93
|
const [data, setData] = (0, import_react2.useState)(null);
|
|
74
94
|
const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
|
|
@@ -76,7 +96,7 @@ function usePipelineStatus(appId) {
|
|
|
76
96
|
const refresh = (0, import_react2.useCallback)(async () => {
|
|
77
97
|
try {
|
|
78
98
|
setIsLoading(true);
|
|
79
|
-
const response = await
|
|
99
|
+
const response = await authFetch(`/api/pipeline/summary?appId=${appId}`);
|
|
80
100
|
if (!response.ok) throw new Error(`Failed to fetch pipeline status`);
|
|
81
101
|
const result = await response.json();
|
|
82
102
|
setData(result);
|
|
@@ -92,9 +112,68 @@ function usePipelineStatus(appId) {
|
|
|
92
112
|
}, [refresh]);
|
|
93
113
|
return { data, isLoading, error, refresh };
|
|
94
114
|
}
|
|
115
|
+
|
|
116
|
+
// src/types/manifest.ts
|
|
117
|
+
var APP_PAGE_LAYOUTS = ["standard", "full-bleed", "canvas"];
|
|
118
|
+
var APP_PAGE_NAV = ["sidebar", "tab", "hidden"];
|
|
119
|
+
|
|
120
|
+
// src/structure.ts
|
|
121
|
+
var APP_LAYOUT = {
|
|
122
|
+
/** The app contract. Always at the app root. */
|
|
123
|
+
manifest: "manifest.yaml",
|
|
124
|
+
/**
|
|
125
|
+
* The unit of extension: config-types/<configTypeId>/ colocates everything
|
|
126
|
+
* for one configuration type — canvas.yaml, defaults.yaml, the six pipeline
|
|
127
|
+
* handlers (extensionless in the manifest), and __tests__/.
|
|
128
|
+
*/
|
|
129
|
+
configTypesDir: "config-types",
|
|
130
|
+
/** Canvas form schema filename inside a config-type folder. */
|
|
131
|
+
canvasFile: "canvas.yaml",
|
|
132
|
+
/** Default field values filename inside a config-type folder. */
|
|
133
|
+
defaultsFile: "defaults.yaml",
|
|
134
|
+
/** Lifecycle hooks (camelCase): hooks/onInstall, hooks/onUninstall, ... */
|
|
135
|
+
hooksDir: "hooks",
|
|
136
|
+
/** Shared app code used by multiple handlers (API clients, parsers). */
|
|
137
|
+
libDir: "lib",
|
|
138
|
+
/** SQL migrations (requires manifest `database.tablePrefix`). */
|
|
139
|
+
migrationsDir: "migrations",
|
|
140
|
+
/** Fastify route module receiving (fastify, AppRouteContext). */
|
|
141
|
+
serverEntry: "server/index",
|
|
142
|
+
/** Client entry registering pages/sidebar items (optional). */
|
|
143
|
+
clientEntry: "client/index",
|
|
144
|
+
/** Icons and logos (optional). */
|
|
145
|
+
assetsDir: "assets",
|
|
146
|
+
/** Tests live next to the code they cover: handlers/<id>/__tests__/ */
|
|
147
|
+
testsDirName: "__tests__"
|
|
148
|
+
};
|
|
149
|
+
var HANDLER_NAMES = [
|
|
150
|
+
"validate",
|
|
151
|
+
"deploy",
|
|
152
|
+
"rollback",
|
|
153
|
+
"healthCheck",
|
|
154
|
+
"driftDetect",
|
|
155
|
+
"getStatus"
|
|
156
|
+
];
|
|
157
|
+
function conventionalPaths(configTypeId) {
|
|
158
|
+
const base = `${APP_LAYOUT.configTypesDir}/${configTypeId}`;
|
|
159
|
+
const handlers = {};
|
|
160
|
+
for (const name of HANDLER_NAMES) {
|
|
161
|
+
handlers[name] = `${base}/${name}`;
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
canvasTemplate: `${base}/${APP_LAYOUT.canvasFile}`,
|
|
165
|
+
defaultConfig: `${base}/${APP_LAYOUT.defaultsFile}`,
|
|
166
|
+
handlers
|
|
167
|
+
};
|
|
168
|
+
}
|
|
95
169
|
// Annotate the CommonJS export names for ESM import in node:
|
|
96
170
|
0 && (module.exports = {
|
|
171
|
+
APP_LAYOUT,
|
|
172
|
+
APP_PAGE_LAYOUTS,
|
|
173
|
+
APP_PAGE_NAV,
|
|
97
174
|
AppContext,
|
|
175
|
+
HANDLER_NAMES,
|
|
176
|
+
conventionalPaths,
|
|
98
177
|
defineDeployer,
|
|
99
178
|
defineDriftDetector,
|
|
100
179
|
defineHealthChecker,
|
package/dist/index.cjs.map
CHANGED
|
@@ -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"],"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// 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// 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 } 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;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,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/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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
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 {
|
|
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';
|
|
3
4
|
import 'react';
|
|
5
|
+
|
|
6
|
+
/** Canonical locations inside an app directory. */
|
|
7
|
+
declare const APP_LAYOUT: {
|
|
8
|
+
/** The app contract. Always at the app root. */
|
|
9
|
+
readonly manifest: "manifest.yaml";
|
|
10
|
+
/**
|
|
11
|
+
* The unit of extension: config-types/<configTypeId>/ colocates everything
|
|
12
|
+
* for one configuration type — canvas.yaml, defaults.yaml, the six pipeline
|
|
13
|
+
* handlers (extensionless in the manifest), and __tests__/.
|
|
14
|
+
*/
|
|
15
|
+
readonly configTypesDir: "config-types";
|
|
16
|
+
/** Canvas form schema filename inside a config-type folder. */
|
|
17
|
+
readonly canvasFile: "canvas.yaml";
|
|
18
|
+
/** Default field values filename inside a config-type folder. */
|
|
19
|
+
readonly defaultsFile: "defaults.yaml";
|
|
20
|
+
/** Lifecycle hooks (camelCase): hooks/onInstall, hooks/onUninstall, ... */
|
|
21
|
+
readonly hooksDir: "hooks";
|
|
22
|
+
/** Shared app code used by multiple handlers (API clients, parsers). */
|
|
23
|
+
readonly libDir: "lib";
|
|
24
|
+
/** SQL migrations (requires manifest `database.tablePrefix`). */
|
|
25
|
+
readonly migrationsDir: "migrations";
|
|
26
|
+
/** Fastify route module receiving (fastify, AppRouteContext). */
|
|
27
|
+
readonly serverEntry: "server/index";
|
|
28
|
+
/** Client entry registering pages/sidebar items (optional). */
|
|
29
|
+
readonly clientEntry: "client/index";
|
|
30
|
+
/** Icons and logos (optional). */
|
|
31
|
+
readonly assetsDir: "assets";
|
|
32
|
+
/** Tests live next to the code they cover: handlers/<id>/__tests__/ */
|
|
33
|
+
readonly testsDirName: "__tests__";
|
|
34
|
+
};
|
|
35
|
+
/** The six pipeline handler names, in lifecycle order. */
|
|
36
|
+
declare const HANDLER_NAMES: readonly ["validate", "deploy", "rollback", "healthCheck", "driftDetect", "getStatus"];
|
|
37
|
+
type HandlerName = (typeof HANDLER_NAMES)[number];
|
|
38
|
+
/**
|
|
39
|
+
* Conventional manifest paths for one configuration type — handy when
|
|
40
|
+
* generating or checking a manifest programmatically.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* conventionalPaths('indexes').handlers.deploy // 'config-types/indexes/deploy'
|
|
44
|
+
*/
|
|
45
|
+
declare function conventionalPaths(configTypeId: string): {
|
|
46
|
+
canvasTemplate: string;
|
|
47
|
+
defaultConfig: string;
|
|
48
|
+
handlers: Record<HandlerName, string>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export { APP_LAYOUT, HANDLER_NAMES, type HandlerName, conventionalPaths };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
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 {
|
|
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';
|
|
3
4
|
import 'react';
|
|
5
|
+
|
|
6
|
+
/** Canonical locations inside an app directory. */
|
|
7
|
+
declare const APP_LAYOUT: {
|
|
8
|
+
/** The app contract. Always at the app root. */
|
|
9
|
+
readonly manifest: "manifest.yaml";
|
|
10
|
+
/**
|
|
11
|
+
* The unit of extension: config-types/<configTypeId>/ colocates everything
|
|
12
|
+
* for one configuration type — canvas.yaml, defaults.yaml, the six pipeline
|
|
13
|
+
* handlers (extensionless in the manifest), and __tests__/.
|
|
14
|
+
*/
|
|
15
|
+
readonly configTypesDir: "config-types";
|
|
16
|
+
/** Canvas form schema filename inside a config-type folder. */
|
|
17
|
+
readonly canvasFile: "canvas.yaml";
|
|
18
|
+
/** Default field values filename inside a config-type folder. */
|
|
19
|
+
readonly defaultsFile: "defaults.yaml";
|
|
20
|
+
/** Lifecycle hooks (camelCase): hooks/onInstall, hooks/onUninstall, ... */
|
|
21
|
+
readonly hooksDir: "hooks";
|
|
22
|
+
/** Shared app code used by multiple handlers (API clients, parsers). */
|
|
23
|
+
readonly libDir: "lib";
|
|
24
|
+
/** SQL migrations (requires manifest `database.tablePrefix`). */
|
|
25
|
+
readonly migrationsDir: "migrations";
|
|
26
|
+
/** Fastify route module receiving (fastify, AppRouteContext). */
|
|
27
|
+
readonly serverEntry: "server/index";
|
|
28
|
+
/** Client entry registering pages/sidebar items (optional). */
|
|
29
|
+
readonly clientEntry: "client/index";
|
|
30
|
+
/** Icons and logos (optional). */
|
|
31
|
+
readonly assetsDir: "assets";
|
|
32
|
+
/** Tests live next to the code they cover: handlers/<id>/__tests__/ */
|
|
33
|
+
readonly testsDirName: "__tests__";
|
|
34
|
+
};
|
|
35
|
+
/** The six pipeline handler names, in lifecycle order. */
|
|
36
|
+
declare const HANDLER_NAMES: readonly ["validate", "deploy", "rollback", "healthCheck", "driftDetect", "getStatus"];
|
|
37
|
+
type HandlerName = (typeof HANDLER_NAMES)[number];
|
|
38
|
+
/**
|
|
39
|
+
* Conventional manifest paths for one configuration type — handy when
|
|
40
|
+
* generating or checking a manifest programmatically.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* conventionalPaths('indexes').handlers.deploy // 'config-types/indexes/deploy'
|
|
44
|
+
*/
|
|
45
|
+
declare function conventionalPaths(configTypeId: string): {
|
|
46
|
+
canvasTemplate: string;
|
|
47
|
+
defaultConfig: string;
|
|
48
|
+
handlers: Record<HandlerName, string>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export { APP_LAYOUT, HANDLER_NAMES, type HandlerName, conventionalPaths };
|
package/dist/index.js
CHANGED
|
@@ -9,9 +9,69 @@ import {
|
|
|
9
9
|
AppContext,
|
|
10
10
|
useAppContext,
|
|
11
11
|
usePipelineStatus
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-JDEA2F7I.js";
|
|
13
|
+
import "./chunk-I4JJX4LL.js";
|
|
14
|
+
|
|
15
|
+
// src/types/manifest.ts
|
|
16
|
+
var APP_PAGE_LAYOUTS = ["standard", "full-bleed", "canvas"];
|
|
17
|
+
var APP_PAGE_NAV = ["sidebar", "tab", "hidden"];
|
|
18
|
+
|
|
19
|
+
// src/structure.ts
|
|
20
|
+
var APP_LAYOUT = {
|
|
21
|
+
/** The app contract. Always at the app root. */
|
|
22
|
+
manifest: "manifest.yaml",
|
|
23
|
+
/**
|
|
24
|
+
* The unit of extension: config-types/<configTypeId>/ colocates everything
|
|
25
|
+
* for one configuration type — canvas.yaml, defaults.yaml, the six pipeline
|
|
26
|
+
* handlers (extensionless in the manifest), and __tests__/.
|
|
27
|
+
*/
|
|
28
|
+
configTypesDir: "config-types",
|
|
29
|
+
/** Canvas form schema filename inside a config-type folder. */
|
|
30
|
+
canvasFile: "canvas.yaml",
|
|
31
|
+
/** Default field values filename inside a config-type folder. */
|
|
32
|
+
defaultsFile: "defaults.yaml",
|
|
33
|
+
/** Lifecycle hooks (camelCase): hooks/onInstall, hooks/onUninstall, ... */
|
|
34
|
+
hooksDir: "hooks",
|
|
35
|
+
/** Shared app code used by multiple handlers (API clients, parsers). */
|
|
36
|
+
libDir: "lib",
|
|
37
|
+
/** SQL migrations (requires manifest `database.tablePrefix`). */
|
|
38
|
+
migrationsDir: "migrations",
|
|
39
|
+
/** Fastify route module receiving (fastify, AppRouteContext). */
|
|
40
|
+
serverEntry: "server/index",
|
|
41
|
+
/** Client entry registering pages/sidebar items (optional). */
|
|
42
|
+
clientEntry: "client/index",
|
|
43
|
+
/** Icons and logos (optional). */
|
|
44
|
+
assetsDir: "assets",
|
|
45
|
+
/** Tests live next to the code they cover: handlers/<id>/__tests__/ */
|
|
46
|
+
testsDirName: "__tests__"
|
|
47
|
+
};
|
|
48
|
+
var HANDLER_NAMES = [
|
|
49
|
+
"validate",
|
|
50
|
+
"deploy",
|
|
51
|
+
"rollback",
|
|
52
|
+
"healthCheck",
|
|
53
|
+
"driftDetect",
|
|
54
|
+
"getStatus"
|
|
55
|
+
];
|
|
56
|
+
function conventionalPaths(configTypeId) {
|
|
57
|
+
const base = `${APP_LAYOUT.configTypesDir}/${configTypeId}`;
|
|
58
|
+
const handlers = {};
|
|
59
|
+
for (const name of HANDLER_NAMES) {
|
|
60
|
+
handlers[name] = `${base}/${name}`;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
canvasTemplate: `${base}/${APP_LAYOUT.canvasFile}`,
|
|
64
|
+
defaultConfig: `${base}/${APP_LAYOUT.defaultsFile}`,
|
|
65
|
+
handlers
|
|
66
|
+
};
|
|
67
|
+
}
|
|
13
68
|
export {
|
|
69
|
+
APP_LAYOUT,
|
|
70
|
+
APP_PAGE_LAYOUTS,
|
|
71
|
+
APP_PAGE_NAV,
|
|
14
72
|
AppContext,
|
|
73
|
+
HANDLER_NAMES,
|
|
74
|
+
conventionalPaths,
|
|
15
75
|
defineDeployer,
|
|
16
76
|
defineDriftDetector,
|
|
17
77
|
defineHealthChecker,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","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 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,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Context } from 'react';
|
|
2
2
|
|
|
3
3
|
type AppSource = 'BUILT_IN' | 'MARKETPLACE' | 'CUSTOM';
|
|
4
4
|
type AppStatusType = 'AVAILABLE' | 'DEPRECATED' | 'REMOVED';
|
|
@@ -74,13 +74,50 @@ interface AppPermissionDeclaration {
|
|
|
74
74
|
actions: string[];
|
|
75
75
|
description?: string;
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* How the platform frames an app page.
|
|
79
|
+
* - `standard` — page header + padded content area (default; use for most pages)
|
|
80
|
+
* - `full-bleed` — content area with no padding/toolbar (custom canvases, maps)
|
|
81
|
+
* - `canvas` — Configuration Canvas chrome (section rail + save/validate bar)
|
|
82
|
+
*/
|
|
83
|
+
type AppPageLayout = 'standard' | 'full-bleed' | 'canvas';
|
|
84
|
+
declare const APP_PAGE_LAYOUTS: readonly ["standard", "full-bleed", "canvas"];
|
|
85
|
+
/**
|
|
86
|
+
* Where the page surfaces in navigation.
|
|
87
|
+
* - `sidebar` — an entry beneath the app in the sidebar
|
|
88
|
+
* - `tab` — a tab within its `parent` page
|
|
89
|
+
* - `hidden` — routable but not linked (details/drill-down pages)
|
|
90
|
+
*/
|
|
91
|
+
type AppPageNav = 'sidebar' | 'tab' | 'hidden';
|
|
92
|
+
declare const APP_PAGE_NAV: readonly ["sidebar", "tab", "hidden"];
|
|
93
|
+
/** An app-scoped permission (declared in `permissions.app`) required to see a page. */
|
|
94
|
+
interface AppPagePermission {
|
|
95
|
+
resource: string;
|
|
96
|
+
action: string;
|
|
97
|
+
}
|
|
77
98
|
interface AppPageDeclaration {
|
|
99
|
+
/** Route beneath the app, e.g. `/indexes` → `/apps/<app-id>/indexes` */
|
|
78
100
|
path: string;
|
|
101
|
+
/** Exported component name from the app's client entry */
|
|
79
102
|
component: string;
|
|
80
103
|
label: string;
|
|
104
|
+
description?: string;
|
|
105
|
+
/** Icon name from the platform icon set; falls back to the app icon */
|
|
81
106
|
icon?: string;
|
|
107
|
+
/** @deprecated use `nav: 'sidebar' | 'hidden'` — kept for backward compatibility */
|
|
82
108
|
sidebar?: boolean;
|
|
109
|
+
/** Navigation placement. Defaults to `sidebar` when `sidebar: true`, else `hidden`. */
|
|
110
|
+
nav?: AppPageNav;
|
|
111
|
+
/** Parent page `path` — required for `nav: 'tab'`, optional nesting for sidebar entries */
|
|
83
112
|
parent?: string;
|
|
113
|
+
/** Optional sidebar section label, for apps with many pages */
|
|
114
|
+
group?: string;
|
|
115
|
+
/** Deterministic ordering within its group/parent (ascending; ties break on label) */
|
|
116
|
+
order?: number;
|
|
117
|
+
/** Layout preset the platform renders around the page body */
|
|
118
|
+
layout?: AppPageLayout;
|
|
119
|
+
/** Hide the page (and its nav entry) unless the user holds this app permission */
|
|
120
|
+
requiresPermission?: AppPagePermission;
|
|
84
121
|
}
|
|
85
122
|
interface AppSettingDeclaration {
|
|
86
123
|
key: string;
|
|
@@ -232,7 +269,7 @@ interface AppContextValue {
|
|
|
232
269
|
getTags: () => Promise<Tag[]>;
|
|
233
270
|
settings: Record<string, unknown>;
|
|
234
271
|
}
|
|
235
|
-
declare const AppContext:
|
|
272
|
+
declare const AppContext: Context<AppContextValue | null>;
|
|
236
273
|
/**
|
|
237
274
|
* Access the app context in any app component.
|
|
238
275
|
*
|
|
@@ -248,39 +285,4 @@ declare const AppContext: react.Context<AppContextValue | null>;
|
|
|
248
285
|
*/
|
|
249
286
|
declare function useAppContext(): AppContextValue;
|
|
250
287
|
|
|
251
|
-
|
|
252
|
-
pendingApprovals: number;
|
|
253
|
-
activeDeployments: number;
|
|
254
|
-
failedDeployments: number;
|
|
255
|
-
unresolvedDrifts: number;
|
|
256
|
-
recentDeployments: Array<{
|
|
257
|
-
id: string;
|
|
258
|
-
canvasName: string;
|
|
259
|
-
environment: string;
|
|
260
|
-
status: string;
|
|
261
|
-
startedAt: string;
|
|
262
|
-
completedAt?: string;
|
|
263
|
-
}>;
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Hook to access pipeline status for the current app/customer.
|
|
267
|
-
*
|
|
268
|
-
* @example
|
|
269
|
-
* ```tsx
|
|
270
|
-
* import { usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'
|
|
271
|
-
*
|
|
272
|
-
* function Dashboard() {
|
|
273
|
-
* const { data, isLoading } = usePipelineStatus('my-app')
|
|
274
|
-
* if (isLoading) return <Spinner />
|
|
275
|
-
* return <div>{data.activeDeployments} active deployments</div>
|
|
276
|
-
* }
|
|
277
|
-
* ```
|
|
278
|
-
*/
|
|
279
|
-
declare function usePipelineStatus(appId: string): {
|
|
280
|
-
data: PipelineStatusData | null;
|
|
281
|
-
isLoading: boolean;
|
|
282
|
-
error: Error | null;
|
|
283
|
-
refresh: () => Promise<void>;
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
export { type AppConfigurationTypeManifest as A, type Component as C, type PipelineStatusData as P, type Tag as T, type User as U, AppContext as a, type AppContextValue as b, type AppDetail as c, type AppHookContext as d, type AppInstallationDetail as e, type AppInstallationStatus as f, type AppListItem as g, type AppManifest as h, type AppPageDeclaration as i, type AppPermissionDeclaration as j, type AppRouteContext as k, type AppSettingDeclaration as l, type AppSource as m, type AppStatusType as n, type Connectivity as o, type Credential as p, type Customer as q, type PlatformDatabaseClient as r, usePipelineStatus as s, useAppContext as u };
|
|
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 };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Context } from 'react';
|
|
2
2
|
|
|
3
3
|
type AppSource = 'BUILT_IN' | 'MARKETPLACE' | 'CUSTOM';
|
|
4
4
|
type AppStatusType = 'AVAILABLE' | 'DEPRECATED' | 'REMOVED';
|
|
@@ -74,13 +74,50 @@ interface AppPermissionDeclaration {
|
|
|
74
74
|
actions: string[];
|
|
75
75
|
description?: string;
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* How the platform frames an app page.
|
|
79
|
+
* - `standard` — page header + padded content area (default; use for most pages)
|
|
80
|
+
* - `full-bleed` — content area with no padding/toolbar (custom canvases, maps)
|
|
81
|
+
* - `canvas` — Configuration Canvas chrome (section rail + save/validate bar)
|
|
82
|
+
*/
|
|
83
|
+
type AppPageLayout = 'standard' | 'full-bleed' | 'canvas';
|
|
84
|
+
declare const APP_PAGE_LAYOUTS: readonly ["standard", "full-bleed", "canvas"];
|
|
85
|
+
/**
|
|
86
|
+
* Where the page surfaces in navigation.
|
|
87
|
+
* - `sidebar` — an entry beneath the app in the sidebar
|
|
88
|
+
* - `tab` — a tab within its `parent` page
|
|
89
|
+
* - `hidden` — routable but not linked (details/drill-down pages)
|
|
90
|
+
*/
|
|
91
|
+
type AppPageNav = 'sidebar' | 'tab' | 'hidden';
|
|
92
|
+
declare const APP_PAGE_NAV: readonly ["sidebar", "tab", "hidden"];
|
|
93
|
+
/** An app-scoped permission (declared in `permissions.app`) required to see a page. */
|
|
94
|
+
interface AppPagePermission {
|
|
95
|
+
resource: string;
|
|
96
|
+
action: string;
|
|
97
|
+
}
|
|
77
98
|
interface AppPageDeclaration {
|
|
99
|
+
/** Route beneath the app, e.g. `/indexes` → `/apps/<app-id>/indexes` */
|
|
78
100
|
path: string;
|
|
101
|
+
/** Exported component name from the app's client entry */
|
|
79
102
|
component: string;
|
|
80
103
|
label: string;
|
|
104
|
+
description?: string;
|
|
105
|
+
/** Icon name from the platform icon set; falls back to the app icon */
|
|
81
106
|
icon?: string;
|
|
107
|
+
/** @deprecated use `nav: 'sidebar' | 'hidden'` — kept for backward compatibility */
|
|
82
108
|
sidebar?: boolean;
|
|
109
|
+
/** Navigation placement. Defaults to `sidebar` when `sidebar: true`, else `hidden`. */
|
|
110
|
+
nav?: AppPageNav;
|
|
111
|
+
/** Parent page `path` — required for `nav: 'tab'`, optional nesting for sidebar entries */
|
|
83
112
|
parent?: string;
|
|
113
|
+
/** Optional sidebar section label, for apps with many pages */
|
|
114
|
+
group?: string;
|
|
115
|
+
/** Deterministic ordering within its group/parent (ascending; ties break on label) */
|
|
116
|
+
order?: number;
|
|
117
|
+
/** Layout preset the platform renders around the page body */
|
|
118
|
+
layout?: AppPageLayout;
|
|
119
|
+
/** Hide the page (and its nav entry) unless the user holds this app permission */
|
|
120
|
+
requiresPermission?: AppPagePermission;
|
|
84
121
|
}
|
|
85
122
|
interface AppSettingDeclaration {
|
|
86
123
|
key: string;
|
|
@@ -232,7 +269,7 @@ interface AppContextValue {
|
|
|
232
269
|
getTags: () => Promise<Tag[]>;
|
|
233
270
|
settings: Record<string, unknown>;
|
|
234
271
|
}
|
|
235
|
-
declare const AppContext:
|
|
272
|
+
declare const AppContext: Context<AppContextValue | null>;
|
|
236
273
|
/**
|
|
237
274
|
* Access the app context in any app component.
|
|
238
275
|
*
|
|
@@ -248,39 +285,4 @@ declare const AppContext: react.Context<AppContextValue | null>;
|
|
|
248
285
|
*/
|
|
249
286
|
declare function useAppContext(): AppContextValue;
|
|
250
287
|
|
|
251
|
-
|
|
252
|
-
pendingApprovals: number;
|
|
253
|
-
activeDeployments: number;
|
|
254
|
-
failedDeployments: number;
|
|
255
|
-
unresolvedDrifts: number;
|
|
256
|
-
recentDeployments: Array<{
|
|
257
|
-
id: string;
|
|
258
|
-
canvasName: string;
|
|
259
|
-
environment: string;
|
|
260
|
-
status: string;
|
|
261
|
-
startedAt: string;
|
|
262
|
-
completedAt?: string;
|
|
263
|
-
}>;
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Hook to access pipeline status for the current app/customer.
|
|
267
|
-
*
|
|
268
|
-
* @example
|
|
269
|
-
* ```tsx
|
|
270
|
-
* import { usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'
|
|
271
|
-
*
|
|
272
|
-
* function Dashboard() {
|
|
273
|
-
* const { data, isLoading } = usePipelineStatus('my-app')
|
|
274
|
-
* if (isLoading) return <Spinner />
|
|
275
|
-
* return <div>{data.activeDeployments} active deployments</div>
|
|
276
|
-
* }
|
|
277
|
-
* ```
|
|
278
|
-
*/
|
|
279
|
-
declare function usePipelineStatus(appId: string): {
|
|
280
|
-
data: PipelineStatusData | null;
|
|
281
|
-
isLoading: boolean;
|
|
282
|
-
error: Error | null;
|
|
283
|
-
refresh: () => Promise<void>;
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
export { type AppConfigurationTypeManifest as A, type Component as C, type PipelineStatusData as P, type Tag as T, type User as U, AppContext as a, type AppContextValue as b, type AppDetail as c, type AppHookContext as d, type AppInstallationDetail as e, type AppInstallationStatus as f, type AppListItem as g, type AppManifest as h, type AppPageDeclaration as i, type AppPermissionDeclaration as j, type AppRouteContext as k, type AppSettingDeclaration as l, type AppSource as m, type AppStatusType as n, type Connectivity as o, type Credential as p, type Customer as q, type PlatformDatabaseClient as r, usePipelineStatus as s, useAppContext as u };
|
|
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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veltrixsecops/app-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.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",
|
|
@@ -37,7 +37,18 @@
|
|
|
37
37
|
"types": "./dist/hooks/index.d.cts",
|
|
38
38
|
"default": "./dist/hooks/index.cjs"
|
|
39
39
|
}
|
|
40
|
-
}
|
|
40
|
+
},
|
|
41
|
+
"./client": {
|
|
42
|
+
"import": {
|
|
43
|
+
"types": "./dist/client/index.d.ts",
|
|
44
|
+
"default": "./dist/client/index.js"
|
|
45
|
+
},
|
|
46
|
+
"require": {
|
|
47
|
+
"types": "./dist/client/index.d.cts",
|
|
48
|
+
"default": "./dist/client/index.cjs"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"./package.json": "./package.json"
|
|
41
52
|
},
|
|
42
53
|
"files": [
|
|
43
54
|
"dist",
|
|
@@ -75,10 +86,10 @@
|
|
|
75
86
|
"plugin"
|
|
76
87
|
],
|
|
77
88
|
"license": "MIT",
|
|
78
|
-
"homepage": "https://github.com/captivatortechnologies/
|
|
89
|
+
"homepage": "https://github.com/captivatortechnologies/veltrix-apps/tree/main/sdk",
|
|
79
90
|
"repository": {
|
|
80
91
|
"type": "git",
|
|
81
|
-
"url": "https://github.com/captivatortechnologies/
|
|
82
|
-
"directory": "
|
|
92
|
+
"url": "git+https://github.com/captivatortechnologies/veltrix-apps.git",
|
|
93
|
+
"directory": "sdk"
|
|
83
94
|
}
|
|
84
95
|
}
|
|
@@ -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 } 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":";AAKA,SAAS,eAAe,kBAAkB;AAkBnC,IAAM,aAAa,cAAsC,IAAI;AAe7D,SAAS,gBAAiC;AAC/C,QAAM,MAAM,WAAW,UAAU;AACjC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,SAAO;AACT;;;AC5CA,SAAS,UAAU,WAAW,mBAAmB;AA+B1C,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;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,YAAU,MAAM;AACd,YAAQ;AAAA,EACV,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,EAAE,MAAM,WAAW,OAAO,QAAQ;AAC3C;","names":[]}
|