@forgeportal/plugin-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/.turbo/turbo-build.log +4 -0
- package/LICENSE +21 -0
- package/README.md +165 -0
- package/__tests__/registry.test.ts +195 -0
- package/__tests__/types.test-d.ts +60 -0
- package/dist/backend.d.ts +56 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +37 -0
- package/dist/backend.js.map +1 -0
- package/dist/context.d.ts +24 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +25 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/react.d.ts +32 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +65 -0
- package/dist/react.js.map +1 -0
- package/dist/registry.d.ts +31 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +80 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +212 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +48 -0
- package/src/backend.ts +84 -0
- package/src/context.tsx +63 -0
- package/src/index.ts +34 -0
- package/src/react.ts +80 -0
- package/src/registry.ts +103 -0
- package/src/types.ts +221 -0
- package/tsconfig.json +11 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAMA,OAAO,EAAY,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAIzC,OAAO,EACL,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAEtB;;;;;GAKG;AACH,wBAAgB,SAAS,IAAI;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAS9C;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAGjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,KAAK,GAAG,OAAO,EACpC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,GAC7D,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,CAkB9B"}
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hooks and context providers for use inside plugin UI components
|
|
3
|
+
* and the ForgePortal UI app shell.
|
|
4
|
+
* Import from: @forgeportal/plugin-sdk/react
|
|
5
|
+
*/
|
|
6
|
+
import { useCallback } from 'react';
|
|
7
|
+
import { useQuery } from '@tanstack/react-query';
|
|
8
|
+
import { useEntityContext, usePluginConfigContext } from './context.js';
|
|
9
|
+
// ─── Re-export React context providers for the app shell ─────────────────────
|
|
10
|
+
// These are used by EntityDetailPage and EntityOverviewTab to wrap plugin components.
|
|
11
|
+
export { EntityProvider, EntityContext, PluginConfigProvider, PluginConfigContext, } from './context.js';
|
|
12
|
+
/**
|
|
13
|
+
* Returns the current entity from the entity detail page context.
|
|
14
|
+
* Must be used inside a component rendered as an EntityTab or EntityCard.
|
|
15
|
+
*
|
|
16
|
+
* @throws if called outside of an EntityProvider.
|
|
17
|
+
*/
|
|
18
|
+
export function useEntity() {
|
|
19
|
+
const ctx = useEntityContext();
|
|
20
|
+
if (!ctx) {
|
|
21
|
+
throw new Error('[ForgePortal SDK] useEntity() must be called inside an EntityTab or EntityCard component. ' +
|
|
22
|
+
'Ensure this component is registered via sdk.registerEntityTab() or sdk.registerEntityCard().');
|
|
23
|
+
}
|
|
24
|
+
return ctx;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns the plugin-scoped config value for the given key.
|
|
28
|
+
* Config is sourced from `forgeportal.yaml` -> `plugins.<pluginId>.config`.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const apiUrl = useConfig<string>('apiEndpoint');
|
|
32
|
+
*/
|
|
33
|
+
export function useConfig(key) {
|
|
34
|
+
const config = usePluginConfigContext();
|
|
35
|
+
return config.get(key);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Typed API fetcher backed by TanStack Query.
|
|
39
|
+
* Automatically includes credentials for session-based auth.
|
|
40
|
+
*
|
|
41
|
+
* @param path - Absolute API path, e.g. '/api/v1/entities'
|
|
42
|
+
* @param options - Optional TanStack Query options to override defaults
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* const { data, isPending } = useApi<MyResponse[]>('/api/v1/my-plugin/data');
|
|
46
|
+
*/
|
|
47
|
+
export function useApi(path, options) {
|
|
48
|
+
const queryFn = useCallback(async () => {
|
|
49
|
+
const res = await fetch(path, {
|
|
50
|
+
headers: { 'Accept': 'application/json' },
|
|
51
|
+
credentials: 'include',
|
|
52
|
+
});
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
const text = await res.text().catch(() => res.statusText);
|
|
55
|
+
throw new Error(`[ForgePortal SDK] API ${path} returned ${res.status}: ${text}`);
|
|
56
|
+
}
|
|
57
|
+
return res.json();
|
|
58
|
+
}, [path]);
|
|
59
|
+
return useQuery({
|
|
60
|
+
queryKey: ['forge-plugin-api', path],
|
|
61
|
+
queryFn,
|
|
62
|
+
...options,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,QAAQ,EAA6C,MAAM,uBAAuB,CAAC;AAC5F,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAGxE,gFAAgF;AAChF,sFAAsF;AACtF,OAAO,EACL,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAEtB;;;;;GAKG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;IAC/B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,4FAA4F;YAC5F,8FAA8F,CAC/F,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAc,GAAW;IAChD,MAAM,MAAM,GAAG,sBAAsB,EAAE,CAAC;IACxC,OAAO,MAAM,CAAC,GAAG,CAAI,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,MAAM,CACpB,IAAY,EACZ,OAA8D;IAE9D,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAoB,EAAE;QACrD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE;YAC5B,OAAO,EAAM,EAAE,QAAQ,EAAE,kBAAkB,EAAE;YAC7C,WAAW,EAAE,SAAS;SACvB,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC1D,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,aAAa,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;QACnF,CAAC;QACD,OAAO,GAAG,CAAC,IAAI,EAAoB,CAAC;IACtC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,OAAO,QAAQ,CAAe;QAC5B,QAAQ,EAAE,CAAC,kBAAkB,EAAE,IAAI,CAAC;QACpC,OAAO;QACP,GAAG,OAAO;KACX,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ForgePluginSDK, EntityTab, EntityCard, Route, ActionProvider, CatalogProvider } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* In-memory registry — the canonical implementation of ForgePluginSDK.
|
|
4
|
+
* Instantiated once per app (API or UI) at startup.
|
|
5
|
+
* Plugins receive this as the `sdk` argument in `registerPlugin(sdk)`.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PluginRegistry implements ForgePluginSDK {
|
|
8
|
+
private readonly _entityTabs;
|
|
9
|
+
private readonly _entityCards;
|
|
10
|
+
private readonly _routes;
|
|
11
|
+
private readonly _actionProviders;
|
|
12
|
+
private readonly _catalogProviders;
|
|
13
|
+
registerEntityTab(tab: EntityTab): void;
|
|
14
|
+
registerEntityCard(card: EntityCard): void;
|
|
15
|
+
registerRoute(route: Route): void;
|
|
16
|
+
registerActionProvider(provider: ActionProvider): void;
|
|
17
|
+
registerCatalogProvider(provider: CatalogProvider): void;
|
|
18
|
+
/**
|
|
19
|
+
* Returns entity tabs, optionally filtered by entity kind.
|
|
20
|
+
* Tabs with no `appliesTo.kinds` constraint match all kinds.
|
|
21
|
+
*/
|
|
22
|
+
getEntityTabs(entityKind?: string): EntityTab[];
|
|
23
|
+
getEntityCards(entityKind?: string): EntityCard[];
|
|
24
|
+
getRoutes(): Route[];
|
|
25
|
+
getActionProviders(): ActionProvider[];
|
|
26
|
+
getActionProvider(id: string, version: string): ActionProvider | undefined;
|
|
27
|
+
getCatalogProviders(): CatalogProvider[];
|
|
28
|
+
}
|
|
29
|
+
/** Global singleton — used by the app shell (API and UI). */
|
|
30
|
+
export declare const globalRegistry: PluginRegistry;
|
|
31
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,UAAU,EACV,KAAK,EACL,cAAc,EACd,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AACH,qBAAa,cAAe,YAAW,cAAc;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;IAClE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsC;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;IAC9D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAsC;IACvE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAsC;IAExE,iBAAiB,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI;IAQvC,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAQ1C,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAQjC,sBAAsB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;IAStD,uBAAuB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAUxD;;;OAGG;IACH,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE;IAQ/C,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE;IAQjD,SAAS,IAAI,KAAK,EAAE;IAIpB,kBAAkB,IAAI,cAAc,EAAE;IAItC,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI1E,mBAAmB,IAAI,eAAe,EAAE;CAGzC;AAED,6DAA6D;AAC7D,eAAO,MAAM,cAAc,gBAAuB,CAAC"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory registry — the canonical implementation of ForgePluginSDK.
|
|
3
|
+
* Instantiated once per app (API or UI) at startup.
|
|
4
|
+
* Plugins receive this as the `sdk` argument in `registerPlugin(sdk)`.
|
|
5
|
+
*/
|
|
6
|
+
export class PluginRegistry {
|
|
7
|
+
_entityTabs = new Map();
|
|
8
|
+
_entityCards = new Map();
|
|
9
|
+
_routes = new Map();
|
|
10
|
+
_actionProviders = new Map();
|
|
11
|
+
_catalogProviders = new Map();
|
|
12
|
+
registerEntityTab(tab) {
|
|
13
|
+
if (this._entityTabs.has(tab.id)) {
|
|
14
|
+
console.warn(`[ForgePortal SDK] EntityTab "${tab.id}" already registered — skipping duplicate.`);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
this._entityTabs.set(tab.id, tab);
|
|
18
|
+
}
|
|
19
|
+
registerEntityCard(card) {
|
|
20
|
+
if (this._entityCards.has(card.id)) {
|
|
21
|
+
console.warn(`[ForgePortal SDK] EntityCard "${card.id}" already registered — skipping duplicate.`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
this._entityCards.set(card.id, card);
|
|
25
|
+
}
|
|
26
|
+
registerRoute(route) {
|
|
27
|
+
if (this._routes.has(route.path)) {
|
|
28
|
+
console.warn(`[ForgePortal SDK] Route "${route.path}" already registered — skipping duplicate.`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this._routes.set(route.path, route);
|
|
32
|
+
}
|
|
33
|
+
registerActionProvider(provider) {
|
|
34
|
+
const key = `${provider.id}@${provider.version}`;
|
|
35
|
+
if (this._actionProviders.has(key)) {
|
|
36
|
+
console.warn(`[ForgePortal SDK] ActionProvider "${key}" already registered — skipping duplicate.`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
this._actionProviders.set(key, provider);
|
|
40
|
+
}
|
|
41
|
+
registerCatalogProvider(provider) {
|
|
42
|
+
if (this._catalogProviders.has(provider.id)) {
|
|
43
|
+
console.warn(`[ForgePortal SDK] CatalogProvider "${provider.id}" already registered — skipping duplicate.`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
this._catalogProviders.set(provider.id, provider);
|
|
47
|
+
}
|
|
48
|
+
// ─── Getters (used by the app shell to read registered capabilities) ────────
|
|
49
|
+
/**
|
|
50
|
+
* Returns entity tabs, optionally filtered by entity kind.
|
|
51
|
+
* Tabs with no `appliesTo.kinds` constraint match all kinds.
|
|
52
|
+
*/
|
|
53
|
+
getEntityTabs(entityKind) {
|
|
54
|
+
const all = Array.from(this._entityTabs.values());
|
|
55
|
+
if (!entityKind)
|
|
56
|
+
return all;
|
|
57
|
+
return all.filter(t => !t.appliesTo?.kinds || t.appliesTo.kinds.includes(entityKind));
|
|
58
|
+
}
|
|
59
|
+
getEntityCards(entityKind) {
|
|
60
|
+
const all = Array.from(this._entityCards.values());
|
|
61
|
+
if (!entityKind)
|
|
62
|
+
return all;
|
|
63
|
+
return all.filter(c => !c.appliesTo?.kinds || c.appliesTo.kinds.includes(entityKind));
|
|
64
|
+
}
|
|
65
|
+
getRoutes() {
|
|
66
|
+
return Array.from(this._routes.values());
|
|
67
|
+
}
|
|
68
|
+
getActionProviders() {
|
|
69
|
+
return Array.from(this._actionProviders.values());
|
|
70
|
+
}
|
|
71
|
+
getActionProvider(id, version) {
|
|
72
|
+
return this._actionProviders.get(`${id}@${version}`);
|
|
73
|
+
}
|
|
74
|
+
getCatalogProviders() {
|
|
75
|
+
return Array.from(this._catalogProviders.values());
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Global singleton — used by the app shell (API and UI). */
|
|
79
|
+
export const globalRegistry = new PluginRegistry();
|
|
80
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
export interface Entity {
|
|
3
|
+
id: string;
|
|
4
|
+
kind: string;
|
|
5
|
+
namespace: string;
|
|
6
|
+
name: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
tags?: string[];
|
|
10
|
+
links?: Array<{
|
|
11
|
+
title: string;
|
|
12
|
+
url: string;
|
|
13
|
+
}>;
|
|
14
|
+
owner_ref?: string;
|
|
15
|
+
lifecycle?: string;
|
|
16
|
+
spec?: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
export interface EntityDraft {
|
|
19
|
+
kind: string;
|
|
20
|
+
namespace: string;
|
|
21
|
+
name: string;
|
|
22
|
+
title?: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
tags?: string[];
|
|
25
|
+
links?: Array<{
|
|
26
|
+
title: string;
|
|
27
|
+
url: string;
|
|
28
|
+
}>;
|
|
29
|
+
owner_ref?: string;
|
|
30
|
+
lifecycle?: string;
|
|
31
|
+
spec?: Record<string, unknown>;
|
|
32
|
+
relations?: Array<{
|
|
33
|
+
type: string;
|
|
34
|
+
targetRef: string;
|
|
35
|
+
}>;
|
|
36
|
+
sources?: Array<{
|
|
37
|
+
kind: string;
|
|
38
|
+
url: string;
|
|
39
|
+
ref?: string;
|
|
40
|
+
}>;
|
|
41
|
+
}
|
|
42
|
+
export interface EntityTabAppliesTo {
|
|
43
|
+
kinds?: string[];
|
|
44
|
+
lifecycle?: string[];
|
|
45
|
+
}
|
|
46
|
+
export interface EntityTab {
|
|
47
|
+
id: string;
|
|
48
|
+
title: string;
|
|
49
|
+
/** Rendered inside the entity detail page tab panel. Receives the current entity. */
|
|
50
|
+
component: React.ComponentType<{
|
|
51
|
+
entity: Entity;
|
|
52
|
+
}>;
|
|
53
|
+
appliesTo?: EntityTabAppliesTo;
|
|
54
|
+
}
|
|
55
|
+
export interface EntityCard {
|
|
56
|
+
id: string;
|
|
57
|
+
title: string;
|
|
58
|
+
/** Rendered as a card on the entity overview tab. */
|
|
59
|
+
component: React.ComponentType<{
|
|
60
|
+
entity: Entity;
|
|
61
|
+
}>;
|
|
62
|
+
appliesTo?: {
|
|
63
|
+
kinds?: string[];
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export interface Route {
|
|
67
|
+
/** URL path, e.g. '/pagerduty'. Must be globally unique. */
|
|
68
|
+
path: string;
|
|
69
|
+
component: React.ComponentType;
|
|
70
|
+
/** If provided, appears in the sidebar navigation. */
|
|
71
|
+
navLabel?: string;
|
|
72
|
+
/** Optional icon name or emoji character. */
|
|
73
|
+
icon?: string;
|
|
74
|
+
}
|
|
75
|
+
export type JsonSchemaType = 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
76
|
+
export interface JsonSchema {
|
|
77
|
+
type: JsonSchemaType | JsonSchemaType[];
|
|
78
|
+
title?: string;
|
|
79
|
+
description?: string;
|
|
80
|
+
properties?: Record<string, JsonSchema>;
|
|
81
|
+
required?: string[];
|
|
82
|
+
items?: JsonSchema;
|
|
83
|
+
enum?: unknown[];
|
|
84
|
+
default?: unknown;
|
|
85
|
+
/** Set true to mark as secret — redacted in audit logs. */
|
|
86
|
+
'x-secret'?: boolean;
|
|
87
|
+
}
|
|
88
|
+
export interface ActionResult {
|
|
89
|
+
status: 'success' | 'failed';
|
|
90
|
+
outputs: Record<string, unknown>;
|
|
91
|
+
links?: Array<{
|
|
92
|
+
title: string;
|
|
93
|
+
url: string;
|
|
94
|
+
}>;
|
|
95
|
+
warnings?: string[];
|
|
96
|
+
error?: {
|
|
97
|
+
code: string;
|
|
98
|
+
message: string;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
export interface ActionLogger {
|
|
102
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
103
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
104
|
+
error(message: string, meta?: Record<string, unknown>): void;
|
|
105
|
+
}
|
|
106
|
+
export interface ActionScmAccessor {
|
|
107
|
+
/** Returns file content as UTF-8 string, or null if not found. */
|
|
108
|
+
getFile(repoUrl: string, path: string, ref?: string): Promise<string | null>;
|
|
109
|
+
/** Lists file paths under prefix (streaming). */
|
|
110
|
+
listFiles(repoUrl: string, prefix?: string): AsyncIterable<string>;
|
|
111
|
+
}
|
|
112
|
+
export interface ActionDbAccessor {
|
|
113
|
+
/** Execute a read-only SQL query. Throws if the query mutates data. */
|
|
114
|
+
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
115
|
+
}
|
|
116
|
+
export interface ActionConfigAccessor {
|
|
117
|
+
/** Get a plugin-specific config value. Returns undefined if not set. */
|
|
118
|
+
get<T = unknown>(key: string): T | undefined;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Provided to every action handler at execution time.
|
|
122
|
+
* Exposes safe, scoped access to infrastructure services.
|
|
123
|
+
*/
|
|
124
|
+
export interface ActionContext {
|
|
125
|
+
/** Plugin-scoped config from forgeportal.yaml plugins.<pluginId>.config */
|
|
126
|
+
config: ActionConfigAccessor;
|
|
127
|
+
/** Structured logger — writes to action run logs visible in the UI. */
|
|
128
|
+
logger: ActionLogger;
|
|
129
|
+
/** SCM operations (getFile, listFiles). */
|
|
130
|
+
scm: ActionScmAccessor;
|
|
131
|
+
/** Read-only database access. */
|
|
132
|
+
db: ActionDbAccessor;
|
|
133
|
+
/**
|
|
134
|
+
* Acquires an advisory lock on a repository URL.
|
|
135
|
+
* Prevents concurrent SCM writes to the same repo within this action run.
|
|
136
|
+
* Automatically released when the action run completes.
|
|
137
|
+
*/
|
|
138
|
+
acquireRepoLock(repoUrl: string): Promise<void>;
|
|
139
|
+
/**
|
|
140
|
+
* Append a log line to the current action run log (persisted in DB,
|
|
141
|
+
* visible in the Actions UI).
|
|
142
|
+
*/
|
|
143
|
+
log(level: 'debug' | 'info' | 'warn' | 'error', message: string): Promise<void>;
|
|
144
|
+
}
|
|
145
|
+
export interface ActionProvider {
|
|
146
|
+
/** Globally unique action ID, e.g. "slack.sendMessage". */
|
|
147
|
+
id: string;
|
|
148
|
+
/** Semantic version string, e.g. "v1". */
|
|
149
|
+
version: string;
|
|
150
|
+
schema: {
|
|
151
|
+
input: JsonSchema;
|
|
152
|
+
output?: JsonSchema;
|
|
153
|
+
};
|
|
154
|
+
handler(ctx: ActionContext, input: Record<string, unknown>): Promise<ActionResult>;
|
|
155
|
+
}
|
|
156
|
+
export interface CatalogProviderContext {
|
|
157
|
+
logger: ActionLogger;
|
|
158
|
+
config: ActionConfigAccessor;
|
|
159
|
+
}
|
|
160
|
+
export interface CatalogProvider {
|
|
161
|
+
/** Globally unique provider ID, e.g. "pagerduty-catalog". */
|
|
162
|
+
id: string;
|
|
163
|
+
/** Called periodically by the worker to ingest entities from an external system. */
|
|
164
|
+
ingest(ctx: CatalogProviderContext): AsyncIterable<EntityDraft>;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* The main SDK object passed to every plugin's `registerPlugin` function.
|
|
168
|
+
* Plugins call `sdk.registerXxx(...)` to expose their capabilities to ForgePortal.
|
|
169
|
+
*/
|
|
170
|
+
export interface ForgePluginSDK {
|
|
171
|
+
registerEntityTab(tab: EntityTab): void;
|
|
172
|
+
registerEntityCard(card: EntityCard): void;
|
|
173
|
+
registerRoute(route: Route): void;
|
|
174
|
+
registerActionProvider(provider: ActionProvider): void;
|
|
175
|
+
registerCatalogProvider(provider: CatalogProvider): void;
|
|
176
|
+
}
|
|
177
|
+
export interface PluginConfigFieldSchema {
|
|
178
|
+
type: 'string' | 'number' | 'boolean';
|
|
179
|
+
description?: string;
|
|
180
|
+
required?: boolean;
|
|
181
|
+
/** If true, the value must come from an env var and is never logged. */
|
|
182
|
+
secret?: boolean;
|
|
183
|
+
default?: string | number | boolean;
|
|
184
|
+
}
|
|
185
|
+
export interface PluginCapabilities {
|
|
186
|
+
ui?: {
|
|
187
|
+
entityTabs?: string[];
|
|
188
|
+
entityCards?: string[];
|
|
189
|
+
routes?: string[];
|
|
190
|
+
};
|
|
191
|
+
backend?: {
|
|
192
|
+
routes?: string[];
|
|
193
|
+
actionProviders?: string[];
|
|
194
|
+
catalogProviders?: string[];
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
export interface PluginManifest {
|
|
198
|
+
/** npm package name, e.g. "@myorg/forge-plugin-pagerduty" */
|
|
199
|
+
name: string;
|
|
200
|
+
version: string;
|
|
201
|
+
forgeportal: {
|
|
202
|
+
/** semver range against @forgeportal/plugin-sdk version, e.g. "^1.0.0" */
|
|
203
|
+
engineVersion: string;
|
|
204
|
+
type: 'ui' | 'backend' | 'fullstack';
|
|
205
|
+
capabilities: PluginCapabilities;
|
|
206
|
+
/** Required RBAC permissions, e.g. ["action:run"]. */
|
|
207
|
+
permissions?: string[];
|
|
208
|
+
/** Config field schemas declared by the plugin. */
|
|
209
|
+
config?: Record<string, PluginConfigFieldSchema>;
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@forgeportal/plugin-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"import": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"./react": {
|
|
11
|
+
"types": "./dist/react.d.ts",
|
|
12
|
+
"import": "./dist/react.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"main": "dist/index.js",
|
|
16
|
+
"types": "dist/index.d.ts",
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@forgeportal/core": "0.0.0"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@tanstack/react-query": ">=5",
|
|
25
|
+
"react": ">=19"
|
|
26
|
+
},
|
|
27
|
+
"peerDependenciesMeta": {
|
|
28
|
+
"react": {
|
|
29
|
+
"optional": true
|
|
30
|
+
},
|
|
31
|
+
"@tanstack/react-query": {
|
|
32
|
+
"optional": true
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@tanstack/react-query": "*",
|
|
37
|
+
"@types/react": "*",
|
|
38
|
+
"fastify": "^5.3.3",
|
|
39
|
+
"react": "*",
|
|
40
|
+
"vitest": "*"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"lint": "eslint src/",
|
|
46
|
+
"clean": "rm -rf dist"
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/backend.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import type {
|
|
3
|
+
ActionProvider,
|
|
4
|
+
CatalogProvider,
|
|
5
|
+
ActionConfigAccessor,
|
|
6
|
+
ActionLogger,
|
|
7
|
+
} from './types.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A backend route registration — Fastify plugin function scoped under
|
|
11
|
+
* /api/v1/plugins/{pluginId}/{path}/
|
|
12
|
+
*/
|
|
13
|
+
export interface BackendRoute {
|
|
14
|
+
/** Relative path prefix, e.g. '/alerts'. No leading slash required. */
|
|
15
|
+
path: string;
|
|
16
|
+
/**
|
|
17
|
+
* Fastify async plugin that registers route handlers.
|
|
18
|
+
* Receives a Fastify instance pre-scoped to the plugin's route prefix.
|
|
19
|
+
* Do NOT call fastify.listen() — only register routes.
|
|
20
|
+
*/
|
|
21
|
+
handler: (fastify: FastifyInstance) => Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The SDK object passed to backend and fullstack plugin entry points.
|
|
26
|
+
* Distinct from ForgePluginSDK (which uses React components).
|
|
27
|
+
*
|
|
28
|
+
* Backend plugin entry: export function registerBackendPlugin(sdk: ForgeBackendPluginSDK): void
|
|
29
|
+
*/
|
|
30
|
+
export interface ForgeBackendPluginSDK {
|
|
31
|
+
/** Plugin-scoped config accessor (from forgeportal.yaml plugins.<id>.config). */
|
|
32
|
+
readonly config: ActionConfigAccessor;
|
|
33
|
+
/** Structured logger scoped to this plugin. */
|
|
34
|
+
readonly logger: ActionLogger;
|
|
35
|
+
/** Register an action provider (available in action runner + templates). */
|
|
36
|
+
registerActionProvider(provider: ActionProvider): void;
|
|
37
|
+
/** Register a catalog provider (periodic ingestion of external entities). */
|
|
38
|
+
registerCatalogProvider(provider: CatalogProvider): void;
|
|
39
|
+
/**
|
|
40
|
+
* Register backend routes under /api/v1/plugins/{pluginId}/{route.path}/
|
|
41
|
+
* The Fastify instance passed to handler is already authenticated (authGuard runs).
|
|
42
|
+
*/
|
|
43
|
+
registerBackendRoute(route: BackendRoute): void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Backend plugin registry — in-memory store for backend capabilities.
|
|
48
|
+
* Instantiated by the plugin loader for each plugin.
|
|
49
|
+
*/
|
|
50
|
+
export class BackendPluginRegistry implements ForgeBackendPluginSDK {
|
|
51
|
+
private readonly _actionProviders = new Map<string, ActionProvider>();
|
|
52
|
+
private readonly _catalogProviders = new Map<string, CatalogProvider>();
|
|
53
|
+
private readonly _routes: BackendRoute[] = [];
|
|
54
|
+
|
|
55
|
+
constructor(
|
|
56
|
+
readonly config: ActionConfigAccessor,
|
|
57
|
+
readonly logger: ActionLogger,
|
|
58
|
+
) {}
|
|
59
|
+
|
|
60
|
+
registerActionProvider(provider: ActionProvider): void {
|
|
61
|
+
const key = `${provider.id}@${provider.version}`;
|
|
62
|
+
if (this._actionProviders.has(key)) {
|
|
63
|
+
console.warn(`[ForgePortal SDK] ActionProvider "${key}" already registered — skipping.`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this._actionProviders.set(key, provider);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
registerCatalogProvider(provider: CatalogProvider): void {
|
|
70
|
+
if (this._catalogProviders.has(provider.id)) {
|
|
71
|
+
console.warn(`[ForgePortal SDK] CatalogProvider "${provider.id}" already registered — skipping.`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this._catalogProviders.set(provider.id, provider);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
registerBackendRoute(route: BackendRoute): void {
|
|
78
|
+
this._routes.push(route);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getActionProviders(): ActionProvider[] { return [...this._actionProviders.values()]; }
|
|
82
|
+
getCatalogProviders(): CatalogProvider[] { return [...this._catalogProviders.values()]; }
|
|
83
|
+
getBackendRoutes(): BackendRoute[] { return [...this._routes]; }
|
|
84
|
+
}
|
package/src/context.tsx
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { createContext, useContext } from 'react';
|
|
2
|
+
import type { Entity } from './types.js';
|
|
3
|
+
|
|
4
|
+
// ─── EntityContext ───────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
interface EntityContextValue {
|
|
7
|
+
entity: Entity;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const EntityContext = createContext<EntityContextValue | null>(null);
|
|
11
|
+
|
|
12
|
+
export function EntityProvider({
|
|
13
|
+
entity,
|
|
14
|
+
children,
|
|
15
|
+
}: {
|
|
16
|
+
entity: Entity;
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
}) {
|
|
19
|
+
return (
|
|
20
|
+
<EntityContext.Provider value={{ entity }}>
|
|
21
|
+
{children}
|
|
22
|
+
</EntityContext.Provider>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ─── PluginConfigContext ──────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
interface PluginConfigContextValue {
|
|
29
|
+
get<T = unknown>(key: string): T | undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const defaultConfig: PluginConfigContextValue = {
|
|
33
|
+
get: () => undefined,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const PluginConfigContext = createContext<PluginConfigContextValue>(defaultConfig);
|
|
37
|
+
|
|
38
|
+
export function PluginConfigProvider({
|
|
39
|
+
config,
|
|
40
|
+
children,
|
|
41
|
+
}: {
|
|
42
|
+
config: Record<string, unknown>;
|
|
43
|
+
children: React.ReactNode;
|
|
44
|
+
}) {
|
|
45
|
+
const accessor: PluginConfigContextValue = {
|
|
46
|
+
get: <T = unknown>(key: string) => config[key] as T | undefined,
|
|
47
|
+
};
|
|
48
|
+
return (
|
|
49
|
+
<PluginConfigContext.Provider value={accessor}>
|
|
50
|
+
{children}
|
|
51
|
+
</PluginConfigContext.Provider>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** @internal Used by useEntity hook */
|
|
56
|
+
export function useEntityContext(): EntityContextValue | null {
|
|
57
|
+
return useContext(EntityContext);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** @internal Used by useConfig hook */
|
|
61
|
+
export function usePluginConfigContext(): PluginConfigContextValue {
|
|
62
|
+
return useContext(PluginConfigContext);
|
|
63
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
2
|
+
export type {
|
|
3
|
+
Entity,
|
|
4
|
+
EntityDraft,
|
|
5
|
+
EntityTab,
|
|
6
|
+
EntityTabAppliesTo,
|
|
7
|
+
EntityCard,
|
|
8
|
+
Route,
|
|
9
|
+
JsonSchema,
|
|
10
|
+
JsonSchemaType,
|
|
11
|
+
ActionResult,
|
|
12
|
+
ActionLogger,
|
|
13
|
+
ActionScmAccessor,
|
|
14
|
+
ActionDbAccessor,
|
|
15
|
+
ActionConfigAccessor,
|
|
16
|
+
ActionContext,
|
|
17
|
+
ActionProvider,
|
|
18
|
+
CatalogProviderContext,
|
|
19
|
+
CatalogProvider,
|
|
20
|
+
ForgePluginSDK,
|
|
21
|
+
PluginConfigFieldSchema,
|
|
22
|
+
PluginCapabilities,
|
|
23
|
+
PluginManifest,
|
|
24
|
+
} from './types.js';
|
|
25
|
+
|
|
26
|
+
// ─── Registry ─────────────────────────────────────────────────────────────────
|
|
27
|
+
export { PluginRegistry, globalRegistry } from './registry.js';
|
|
28
|
+
|
|
29
|
+
// ─── Backend SDK (for backend and fullstack plugins) ─────────────────────────
|
|
30
|
+
export type { BackendRoute, ForgeBackendPluginSDK } from './backend.js';
|
|
31
|
+
export { BackendPluginRegistry } from './backend.js';
|
|
32
|
+
|
|
33
|
+
// ─── SDK version (used by plugin loader for engineVersion compatibility check) ─
|
|
34
|
+
export const SDK_VERSION = '1.0.0';
|