@veltrixsecops/app-sdk 1.0.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 ADDED
@@ -0,0 +1,92 @@
1
+ # @veltrixsecops/app-sdk
2
+
3
+ The official SDK for building [Veltrix](https://github.com/captivatortechnologies/Veltrix) Security-as-Code apps.
4
+
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
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install --save-dev @veltrixsecops/app-sdk
11
+ ```
12
+
13
+ The SDK is types-first: pipeline handlers typically only need `import type`, so it can be a dev dependency.
14
+
15
+ ## Writing pipeline handlers
16
+
17
+ Every configuration type your app declares in `manifest.yaml` implements six handlers. Each receives a typed context built by the platform:
18
+
19
+ ```ts
20
+ // handlers/indexes/validate.ts
21
+ import type { PipelineContext, ValidationResult } from '@veltrixsecops/app-sdk'
22
+
23
+ export default async function validate(ctx: PipelineContext): Promise<ValidationResult> {
24
+ const errors = []
25
+ for (const section of ctx.canvas.sections) {
26
+ if (!section.fields['name']) {
27
+ errors.push({ field: 'name', message: 'Name is required', code: 'required' })
28
+ }
29
+ }
30
+ return { valid: errors.length === 0, errors, warnings: [] }
31
+ }
32
+ ```
33
+
34
+ Or use the `define*` helpers for inference:
35
+
36
+ ```ts
37
+ import { defineDeployer } from '@veltrixsecops/app-sdk/pipeline'
38
+
39
+ export default defineDeployer(async (ctx) => {
40
+ // ctx.component, ctx.credential, ctx.connectivity, ctx.connectivityProvider
41
+ // ctx.previousConfig, ctx.strategy, ctx.canaryPercent
42
+ return { success: true, message: 'Deployed', rollbackData: {/* prior state */} }
43
+ })
44
+ ```
45
+
46
+ ## Reading platform data
47
+
48
+ Handlers must never query the platform database directly. Use the tenant-scoped
49
+ data API on every context instead:
50
+
51
+ ```ts
52
+ import type { PipelineContext, ConfigStatus } from '@veltrixsecops/app-sdk'
53
+
54
+ export default async function getStatus(ctx: PipelineContext): Promise<ConfigStatus> {
55
+ const latest = await ctx.platform.getLatestDeployment(ctx.canvas.canvasId, { status: 'SUCCEEDED' })
56
+ const components = await ctx.platform.listComponents({ types: ['indexer'] })
57
+ // ...
58
+ }
59
+ ```
60
+
61
+ ## Lifecycle hooks
62
+
63
+ ```ts
64
+ // hooks/on-install.ts
65
+ import type { AppHookContext } from '@veltrixsecops/app-sdk'
66
+
67
+ export default async function onInstall({ db, appId }: AppHookContext): Promise<void> {
68
+ // Seed your app's own (prefixed) tables here.
69
+ }
70
+ ```
71
+
72
+ ## Client pages
73
+
74
+ ```tsx
75
+ import { useAppContext, usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'
76
+ ```
77
+
78
+ ## Package layout
79
+
80
+ | Entry point | Contents |
81
+ |---|---|
82
+ | `@veltrixsecops/app-sdk` | All types: handler contexts/results, manifest, platform refs, hook contexts |
83
+ | `@veltrixsecops/app-sdk/pipeline` | `defineValidator`, `defineDeployer`, `defineRollbackHandler`, `defineHealthChecker`, `defineDriftDetector` |
84
+ | `@veltrixsecops/app-sdk/hooks` | React hooks for app client pages (requires `react`) |
85
+
86
+ ## Building an app
87
+
88
+ Start from the [`_template`](https://github.com/captivatortechnologies/Veltrix/tree/main/apps-and-tools/_template) app and see the contribution guide in `apps-and-tools/CONTRIBUTING.md`.
89
+
90
+ ## License
91
+
92
+ MIT
@@ -0,0 +1,33 @@
1
+ // src/pipeline/define-validator.ts
2
+ function defineValidator(handler) {
3
+ return handler;
4
+ }
5
+
6
+ // src/pipeline/define-deployer.ts
7
+ function defineDeployer(handler) {
8
+ return handler;
9
+ }
10
+
11
+ // src/pipeline/define-rollback-handler.ts
12
+ function defineRollbackHandler(handler) {
13
+ return handler;
14
+ }
15
+
16
+ // src/pipeline/define-health-checker.ts
17
+ function defineHealthChecker(handler) {
18
+ return handler;
19
+ }
20
+
21
+ // src/pipeline/define-drift-detector.ts
22
+ function defineDriftDetector(handler) {
23
+ return handler;
24
+ }
25
+
26
+ export {
27
+ defineValidator,
28
+ defineDeployer,
29
+ defineRollbackHandler,
30
+ defineHealthChecker,
31
+ defineDriftDetector
32
+ };
33
+ //# sourceMappingURL=chunk-PJCHU6ZZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pipeline/define-validator.ts","../src/pipeline/define-deployer.ts","../src/pipeline/define-rollback-handler.ts","../src/pipeline/define-health-checker.ts","../src/pipeline/define-drift-detector.ts"],"sourcesContent":["import type { PipelineContext, ValidationResult } from '../types/pipeline'\n\n/**\n * Define a validation handler for a configuration type.\n *\n * @example\n * ```ts\n * import { defineValidator } from '@veltrixsecops/app-sdk/pipeline'\n *\n * export default defineValidator(async (ctx) => {\n * const errors = []\n * const name = ctx.canvas.sections[0]?.fields['name']\n * if (!name) {\n * errors.push({ field: 'name', message: 'Name is required', code: 'REQUIRED' })\n * }\n * return { valid: errors.length === 0, errors, warnings: [] }\n * })\n * ```\n */\nexport function defineValidator(\n handler: (ctx: PipelineContext) => Promise<ValidationResult>,\n): (ctx: PipelineContext) => Promise<ValidationResult> {\n return handler\n}\n","import type { DeployContext, DeployResult } from '../types/pipeline'\n\n/**\n * Define a deploy handler for a configuration type.\n *\n * @example\n * ```ts\n * import { defineDeployer } from '@veltrixsecops/app-sdk/pipeline'\n *\n * export default defineDeployer(async (ctx) => {\n * const { component, credential, connectivity, canvas } = ctx\n * // Push configuration to the tool\n * return { success: true, message: 'Deployed', rollbackData: previousState }\n * })\n * ```\n */\nexport function defineDeployer(\n handler: (ctx: DeployContext) => Promise<DeployResult>,\n): (ctx: DeployContext) => Promise<DeployResult> {\n return handler\n}\n","import type { RollbackContext, RollbackResult } from '../types/pipeline'\r\n\r\n/**\r\n * Define a rollback handler for a configuration type.\r\n */\r\nexport function defineRollbackHandler(\r\n handler: (ctx: RollbackContext) => Promise<RollbackResult>,\r\n): (ctx: RollbackContext) => Promise<RollbackResult> {\r\n return handler\r\n}\r\n","import type { HealthCheckContext, HealthCheckResult } from '../types/pipeline'\r\n\r\n/**\r\n * Define a health check handler for a configuration type.\r\n */\r\nexport function defineHealthChecker(\r\n handler: (ctx: HealthCheckContext) => Promise<HealthCheckResult>,\r\n): (ctx: HealthCheckContext) => Promise<HealthCheckResult> {\r\n return handler\r\n}\r\n","import type { DriftContext, DriftResult } from '../types/pipeline'\r\n\r\n/**\r\n * Define a drift detection handler for a configuration type.\r\n */\r\nexport function defineDriftDetector(\r\n handler: (ctx: DriftContext) => Promise<DriftResult>,\r\n): (ctx: DriftContext) => Promise<DriftResult> {\r\n return handler\r\n}\r\n"],"mappings":";AAmBO,SAAS,gBACd,SACqD;AACrD,SAAO;AACT;;;ACPO,SAAS,eACd,SAC+C;AAC/C,SAAO;AACT;;;ACfO,SAAS,sBACd,SACmD;AACnD,SAAO;AACT;;;ACJO,SAAS,oBACd,SACyD;AACzD,SAAO;AACT;;;ACJO,SAAS,oBACd,SAC6C;AAC7C,SAAO;AACT;","names":[]}
@@ -0,0 +1,43 @@
1
+ // src/hooks/use-app-context.ts
2
+ import { createContext, useContext } from "react";
3
+ var AppContext = createContext(null);
4
+ function useAppContext() {
5
+ const ctx = useContext(AppContext);
6
+ if (!ctx) {
7
+ throw new Error("useAppContext must be used within an AppContextProvider");
8
+ }
9
+ return ctx;
10
+ }
11
+
12
+ // src/hooks/use-pipeline-status.ts
13
+ import { useState, useEffect, useCallback } from "react";
14
+ function usePipelineStatus(appId) {
15
+ const [data, setData] = useState(null);
16
+ const [isLoading, setIsLoading] = useState(true);
17
+ const [error, setError] = useState(null);
18
+ const refresh = useCallback(async () => {
19
+ try {
20
+ setIsLoading(true);
21
+ const response = await fetch(`/api/pipeline/summary?appId=${appId}`);
22
+ if (!response.ok) throw new Error(`Failed to fetch pipeline status`);
23
+ const result = await response.json();
24
+ setData(result);
25
+ setError(null);
26
+ } catch (err) {
27
+ setError(err instanceof Error ? err : new Error("Unknown error"));
28
+ } finally {
29
+ setIsLoading(false);
30
+ }
31
+ }, [appId]);
32
+ useEffect(() => {
33
+ refresh();
34
+ }, [refresh]);
35
+ return { data, isLoading, error, refresh };
36
+ }
37
+
38
+ export {
39
+ AppContext,
40
+ useAppContext,
41
+ usePipelineStatus
42
+ };
43
+ //# sourceMappingURL=chunk-TSEIWO6T.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 } 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":[]}
@@ -0,0 +1,71 @@
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/hooks/index.ts
21
+ var hooks_exports = {};
22
+ __export(hooks_exports, {
23
+ AppContext: () => AppContext,
24
+ useAppContext: () => useAppContext,
25
+ usePipelineStatus: () => usePipelineStatus
26
+ });
27
+ module.exports = __toCommonJS(hooks_exports);
28
+
29
+ // src/hooks/use-app-context.ts
30
+ var import_react = require("react");
31
+ var AppContext = (0, import_react.createContext)(null);
32
+ function useAppContext() {
33
+ const ctx = (0, import_react.useContext)(AppContext);
34
+ if (!ctx) {
35
+ throw new Error("useAppContext must be used within an AppContextProvider");
36
+ }
37
+ return ctx;
38
+ }
39
+
40
+ // src/hooks/use-pipeline-status.ts
41
+ var import_react2 = require("react");
42
+ function usePipelineStatus(appId) {
43
+ const [data, setData] = (0, import_react2.useState)(null);
44
+ const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
45
+ const [error, setError] = (0, import_react2.useState)(null);
46
+ const refresh = (0, import_react2.useCallback)(async () => {
47
+ try {
48
+ setIsLoading(true);
49
+ const response = await fetch(`/api/pipeline/summary?appId=${appId}`);
50
+ if (!response.ok) throw new Error(`Failed to fetch pipeline status`);
51
+ const result = await response.json();
52
+ setData(result);
53
+ setError(null);
54
+ } catch (err) {
55
+ setError(err instanceof Error ? err : new Error("Unknown error"));
56
+ } finally {
57
+ setIsLoading(false);
58
+ }
59
+ }, [appId]);
60
+ (0, import_react2.useEffect)(() => {
61
+ refresh();
62
+ }, [refresh]);
63
+ return { data, isLoading, error, refresh };
64
+ }
65
+ // Annotate the CommonJS export names for ESM import in node:
66
+ 0 && (module.exports = {
67
+ AppContext,
68
+ useAppContext,
69
+ usePipelineStatus
70
+ });
71
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/index.ts","../../src/hooks/use-app-context.ts","../../src/hooks/use-pipeline-status.ts"],"sourcesContent":["export { useAppContext, AppContext, type AppContextValue } from './use-app-context'\r\nexport { usePipelineStatus, type PipelineStatusData } from './use-pipeline-status'\r\n","// ========================================================================\n// React hooks for app developers\n// These provide access to platform data and pipeline state\n// ========================================================================\n\nimport { createContext, useContext } from 'react'\nimport type { Component, Credential, Tag, User, Customer } from '../types/platform'\n\nexport interface AppContextValue {\n appId: string\n customerId: string\n user: User | null\n customer: Customer | null\n\n // Platform data accessors\n getComponents: () => Promise<Component[]>\n getCredentials: () => Promise<Credential[]>\n getTags: () => Promise<Tag[]>\n\n // App settings\n settings: Record<string, unknown>\n}\n\nexport const AppContext = createContext<AppContextValue | null>(null)\n\n/**\n * Access the app context in any app component.\n *\n * @example\n * ```tsx\n * import { useAppContext } from '@veltrixsecops/app-sdk/hooks'\n *\n * function MyComponent() {\n * const { appId, settings, getComponents } = useAppContext()\n * // ...\n * }\n * ```\n */\nexport function useAppContext(): AppContextValue {\n const ctx = useContext(AppContext)\n if (!ctx) {\n throw new Error('useAppContext must be used within an AppContextProvider')\n }\n return ctx\n}\n","import { useState, useEffect, useCallback } from 'react'\n\nexport interface PipelineStatusData {\n pendingApprovals: number\n activeDeployments: number\n failedDeployments: number\n unresolvedDrifts: number\n recentDeployments: Array<{\n id: string\n canvasName: string\n environment: string\n status: string\n startedAt: string\n completedAt?: string\n }>\n}\n\n/**\n * Hook to access pipeline status for the current app/customer.\n *\n * @example\n * ```tsx\n * import { usePipelineStatus } from '@veltrixsecops/app-sdk/hooks'\n *\n * function Dashboard() {\n * const { data, isLoading } = usePipelineStatus('my-app')\n * if (isLoading) return <Spinner />\n * return <div>{data.activeDeployments} active deployments</div>\n * }\n * ```\n */\nexport function usePipelineStatus(appId: string) {\n const [data, setData] = useState<PipelineStatusData | null>(null)\n const [isLoading, setIsLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n const refresh = useCallback(async () => {\n try {\n setIsLoading(true)\n const response = await fetch(`/api/pipeline/summary?appId=${appId}`)\n if (!response.ok) throw new Error(`Failed to fetch pipeline status`)\n const result = await response.json()\n setData(result)\n setError(null)\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Unknown error'))\n } finally {\n setIsLoading(false)\n }\n }, [appId])\n\n useEffect(() => {\n refresh()\n }, [refresh])\n\n return { data, isLoading, error, refresh }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,mBAA0C;AAkBnC,IAAM,iBAAa,4BAAsC,IAAI;AAe7D,SAAS,gBAAiC;AAC/C,QAAM,UAAM,yBAAW,UAAU;AACjC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,SAAO;AACT;;;AC5CA,IAAAA,gBAAiD;AA+B1C,SAAS,kBAAkB,OAAe;AAC/C,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAoC,IAAI;AAChE,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,IAAI;AAC/C,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,IAAI;AAErD,QAAM,cAAU,2BAAY,YAAY;AACtC,QAAI;AACF,mBAAa,IAAI;AACjB,YAAM,WAAW,MAAM,MAAM,+BAA+B,KAAK,EAAE;AACnE,UAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,iCAAiC;AACnE,YAAM,SAAS,MAAM,SAAS,KAAK;AACnC,cAAQ,MAAM;AACd,eAAS,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,eAAe,CAAC;AAAA,IAClE,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,KAAK,CAAC;AAEV,+BAAU,MAAM;AACd,YAAQ;AAAA,EACV,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,EAAE,MAAM,WAAW,OAAO,QAAQ;AAC3C;","names":["import_react"]}
@@ -0,0 +1,2 @@
1
+ export { a as AppContext, b as AppContextValue, P as PipelineStatusData, u as useAppContext, s as usePipelineStatus } from '../index-BI8f_mBw.cjs';
2
+ import 'react';
@@ -0,0 +1,2 @@
1
+ export { a as AppContext, b as AppContextValue, P as PipelineStatusData, u as useAppContext, s as usePipelineStatus } from '../index-BI8f_mBw.js';
2
+ import 'react';
@@ -0,0 +1,11 @@
1
+ import {
2
+ AppContext,
3
+ useAppContext,
4
+ usePipelineStatus
5
+ } from "../chunk-TSEIWO6T.js";
6
+ export {
7
+ AppContext,
8
+ useAppContext,
9
+ usePipelineStatus
10
+ };
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,286 @@
1
+ import * as react from 'react';
2
+
3
+ type AppSource = 'BUILT_IN' | 'MARKETPLACE' | 'CUSTOM';
4
+ type AppStatusType = 'AVAILABLE' | 'DEPRECATED' | 'REMOVED';
5
+ type AppInstallationStatus = 'INSTALLING' | 'INSTALLED' | 'ENABLED' | 'DISABLED' | 'FAILED' | 'UNINSTALLING';
6
+ interface AppManifest {
7
+ id: string;
8
+ name: string;
9
+ version: string;
10
+ vendor: string;
11
+ description: string;
12
+ category: string;
13
+ license?: string;
14
+ homepage?: string;
15
+ icon?: string;
16
+ logo?: string;
17
+ platform: {
18
+ minVersion: string;
19
+ };
20
+ permissions: {
21
+ platform: string[];
22
+ app: AppPermissionDeclaration[];
23
+ };
24
+ database?: {
25
+ migrations: string;
26
+ tablePrefix: string;
27
+ };
28
+ pipeline: {
29
+ configurationTypes: AppConfigurationTypeManifest[];
30
+ pipelineEvents?: string[];
31
+ };
32
+ server: {
33
+ entry: string;
34
+ routes?: {
35
+ prefix: string;
36
+ };
37
+ };
38
+ client?: {
39
+ entry: string;
40
+ pages?: AppPageDeclaration[];
41
+ };
42
+ hooks?: {
43
+ onInstall?: string;
44
+ onUninstall?: string;
45
+ onEnable?: string;
46
+ onDisable?: string;
47
+ onUpgrade?: string;
48
+ };
49
+ events?: string[];
50
+ settings?: AppSettingDeclaration[];
51
+ }
52
+ interface AppConfigurationTypeManifest {
53
+ id: string;
54
+ name: string;
55
+ description?: string;
56
+ canvasTemplate: string;
57
+ defaultConfig?: string;
58
+ handlers: {
59
+ validate: string;
60
+ deploy: string;
61
+ rollback: string;
62
+ healthCheck: string;
63
+ driftDetect?: string | null;
64
+ getStatus: string;
65
+ };
66
+ targets: {
67
+ componentTypes: string[];
68
+ requiresCredential: boolean;
69
+ requiresConnectivity: boolean;
70
+ };
71
+ }
72
+ interface AppPermissionDeclaration {
73
+ resource: string;
74
+ actions: string[];
75
+ description?: string;
76
+ }
77
+ interface AppPageDeclaration {
78
+ path: string;
79
+ component: string;
80
+ label: string;
81
+ icon?: string;
82
+ sidebar?: boolean;
83
+ parent?: string;
84
+ }
85
+ interface AppSettingDeclaration {
86
+ key: string;
87
+ type: 'string' | 'number' | 'boolean' | 'select';
88
+ label: string;
89
+ description?: string;
90
+ default?: string | number | boolean;
91
+ required?: boolean;
92
+ options?: Array<{
93
+ label: string;
94
+ value: string;
95
+ }>;
96
+ }
97
+ interface AppListItem {
98
+ id: string;
99
+ appId: string;
100
+ name: string;
101
+ version: string;
102
+ vendor: string;
103
+ description: string;
104
+ category: string;
105
+ icon?: string;
106
+ logo?: string;
107
+ source: AppSource;
108
+ isDefault: boolean;
109
+ status: AppStatusType;
110
+ installed?: boolean;
111
+ enabled?: boolean;
112
+ }
113
+ interface AppDetail extends AppListItem {
114
+ license?: string;
115
+ homepage?: string;
116
+ repository?: string;
117
+ configurationTypes: Array<{
118
+ id: string;
119
+ name: string;
120
+ description?: string;
121
+ componentTypes: string[];
122
+ }>;
123
+ permissions: AppPermissionDeclaration[];
124
+ settings: AppSettingDeclaration[];
125
+ }
126
+ interface AppInstallationDetail {
127
+ id: string;
128
+ appId: string;
129
+ customerId: string;
130
+ version: string;
131
+ enabled: boolean;
132
+ installedBy: string;
133
+ installedAt: string;
134
+ settings: Record<string, unknown>;
135
+ status: AppInstallationStatus;
136
+ app: AppListItem;
137
+ }
138
+
139
+ interface Component {
140
+ id: string;
141
+ hostname: string;
142
+ port: string;
143
+ type: string[];
144
+ toolId: string;
145
+ customerId: string;
146
+ }
147
+ interface Credential {
148
+ id: string;
149
+ name: string;
150
+ username: string;
151
+ password: string;
152
+ apiToken: string | null;
153
+ certificate: string | null;
154
+ toolId: string;
155
+ customerId: string;
156
+ }
157
+ interface Connectivity {
158
+ id: string;
159
+ componentId: string;
160
+ status: string;
161
+ sshCommand: string | null;
162
+ httpsUrl: string | null;
163
+ tailscaleDeviceIP: string | null;
164
+ }
165
+ interface Tag {
166
+ id: string;
167
+ name: string;
168
+ customerId: string;
169
+ }
170
+ interface User {
171
+ id: string;
172
+ email: string;
173
+ name: string | null;
174
+ customerId: string;
175
+ roleId: string;
176
+ }
177
+ interface Customer {
178
+ id: string;
179
+ name: string;
180
+ domain: string | null;
181
+ isActive: boolean;
182
+ }
183
+ /**
184
+ * Loosely-typed handle to the platform database, passed to lifecycle hooks.
185
+ * At runtime this is the platform's Prisma client; model delegates are
186
+ * accessed by name (e.g. `db.splunkVersion.upsert(...)` for an app table).
187
+ * Apps should only touch their own prefixed tables and the raw-query
188
+ * escape hatches — anything else is unsupported and may break between
189
+ * platform versions.
190
+ */
191
+ interface PlatformDatabaseClient {
192
+ $queryRawUnsafe<T = unknown>(query: string, ...values: unknown[]): Promise<T>;
193
+ $executeRawUnsafe(query: string, ...values: unknown[]): Promise<number>;
194
+ [modelDelegate: string]: any;
195
+ }
196
+ /**
197
+ * Context passed to app lifecycle hooks
198
+ * (onInstall / onUninstall / onEnable / onDisable / onUpgrade).
199
+ * `customerId` is present only for customer-scoped hooks (onEnable/onDisable).
200
+ */
201
+ interface AppHookContext {
202
+ db: PlatformDatabaseClient;
203
+ appId: string;
204
+ customerId?: string;
205
+ }
206
+ /**
207
+ * Context passed to an app's server route module (the `server.entry`
208
+ * declared in manifest.yaml). The platform mounts the module as a Fastify
209
+ * plugin under `/api/apps/<appId>` with auth + app-enabled checks applied;
210
+ * `hasPermission` returns a preHandler enforcing an app-scoped permission.
211
+ */
212
+ interface AppRouteContext {
213
+ appId: string;
214
+ appDir: string;
215
+ manifest: AppManifest;
216
+ db: PlatformDatabaseClient;
217
+ /**
218
+ * Returns a Fastify preHandler enforcing an app-scoped permission.
219
+ * Typed `any` so the SDK does not depend on Fastify's hook types —
220
+ * pass it straight into a route's `preHandler` array.
221
+ */
222
+ hasPermission: (resource: string, action: string) => any;
223
+ }
224
+
225
+ interface AppContextValue {
226
+ appId: string;
227
+ customerId: string;
228
+ user: User | null;
229
+ customer: Customer | null;
230
+ getComponents: () => Promise<Component[]>;
231
+ getCredentials: () => Promise<Credential[]>;
232
+ getTags: () => Promise<Tag[]>;
233
+ settings: Record<string, unknown>;
234
+ }
235
+ declare const AppContext: react.Context<AppContextValue | null>;
236
+ /**
237
+ * Access the app context in any app component.
238
+ *
239
+ * @example
240
+ * ```tsx
241
+ * import { useAppContext } from '@veltrixsecops/app-sdk/hooks'
242
+ *
243
+ * function MyComponent() {
244
+ * const { appId, settings, getComponents } = useAppContext()
245
+ * // ...
246
+ * }
247
+ * ```
248
+ */
249
+ declare function useAppContext(): AppContextValue;
250
+
251
+ interface PipelineStatusData {
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 };