@datum-cloud/portal-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/LICENSE +21 -0
- package/README.md +62 -0
- package/dist/context-Cl6RRw0h.js +22 -0
- package/dist/context-Cl6RRw0h.js.map +1 -0
- package/dist/host/index.d.ts +10 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +24 -0
- package/dist/host/index.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/types-CvoOm66Q.d.ts +74 -0
- package/dist/types-CvoOm66Q.d.ts.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Datum Technology, Inc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# @datum-cloud/portal-plugin-sdk
|
|
2
|
+
|
|
3
|
+
The host-contract package for cloud-portal's [Portal Plugin
|
|
4
|
+
System](https://github.com/datum-cloud/cloud-portal/blob/main/docs/enhancements/portal-plugin-system.md).
|
|
5
|
+
A service team's plugin imports this package to reach the three things the
|
|
6
|
+
host provides — never a plugin-declared backend, and never a hand-rolled
|
|
7
|
+
stand-in.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { useProjectContext, usePluginFetch, useResourceWatch } from '@datum-cloud/portal-plugin-sdk';
|
|
11
|
+
|
|
12
|
+
function InstanceList() {
|
|
13
|
+
const { project } = useProjectContext();
|
|
14
|
+
const pluginFetch = usePluginFetch();
|
|
15
|
+
const { lastEvent } = useResourceWatch({
|
|
16
|
+
resourceType: 'compute.miloapis.com/v1alpha1/workloads',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// pluginFetch is pre-scoped to `project`'s control plane and already
|
|
20
|
+
// authenticated — the path below is just the K8s API path.
|
|
21
|
+
// await pluginFetch('/apis/compute.miloapis.com/v1alpha1/namespaces/default/workloads')
|
|
22
|
+
|
|
23
|
+
return <div>{project?.displayName}</div>;
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Why a singleton, not a normal npm dependency
|
|
28
|
+
|
|
29
|
+
Plugins are loaded via Module Federation, in the host's own JavaScript realm.
|
|
30
|
+
If a plugin bundled its own copy of this package, its `useContext()` calls
|
|
31
|
+
would read from its own private Context — never the host's. The host
|
|
32
|
+
registers `@datum-cloud/portal-plugin-sdk` as an **eager singleton** shared
|
|
33
|
+
module (see cloud-portal's `federation-host.ts`), the same way it shares
|
|
34
|
+
`react` and `react-router`, so every plugin resolves the host's exact module
|
|
35
|
+
instance at runtime regardless of what version it was built against.
|
|
36
|
+
|
|
37
|
+
## Two entry points, two audiences
|
|
38
|
+
|
|
39
|
+
- `@datum-cloud/portal-plugin-sdk` — the plugin-facing surface: `useProjectContext`,
|
|
40
|
+
`usePluginFetch`, `useResourceWatch`, and their result types. This is the
|
|
41
|
+
package a plugin repository actually calls.
|
|
42
|
+
- `@datum-cloud/portal-plugin-sdk/host` — host-only wiring
|
|
43
|
+
(`PortalPluginHostProvider`), imported by cloud-portal to supply the real
|
|
44
|
+
implementations behind those three hooks. A plugin should never import this.
|
|
45
|
+
|
|
46
|
+
## Versioning
|
|
47
|
+
|
|
48
|
+
The host advertises its SDK version; a plugin manifest whose `sdk.range`
|
|
49
|
+
doesn't match is not loaded (`Compatible=False`). Per the enhancement doc:
|
|
50
|
+
additive changes (a new optional field, a new hook) are **minor**; removing a
|
|
51
|
+
hook or a required field is **major**, with a deprecation window. Both entry
|
|
52
|
+
points are part of that same contract — a breaking change to either is a
|
|
53
|
+
major bump.
|
|
54
|
+
|
|
55
|
+
## Development
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pnpm install
|
|
59
|
+
pnpm build
|
|
60
|
+
pnpm test
|
|
61
|
+
pnpm typecheck
|
|
62
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createContext } from "react";
|
|
2
|
+
//#region src/context.ts
|
|
3
|
+
/**
|
|
4
|
+
* The single React Context instance shared by both this package's plugin-
|
|
5
|
+
* facing hooks (`.`) and the host-only Provider (`./host`).
|
|
6
|
+
*
|
|
7
|
+
* Why this matters for Module Federation: a Context object is only ever the
|
|
8
|
+
* same Context to `useContext()` if it is literally the same module instance.
|
|
9
|
+
* If the host and a plugin each bundled their own copy of this package, the
|
|
10
|
+
* plugin's `useProjectContext()` would read from the plugin's own empty
|
|
11
|
+
* Context — never the host's Provider. That's why cloud-portal's federation
|
|
12
|
+
* host must register `@datum-cloud/portal-plugin-sdk` itself as a Module
|
|
13
|
+
* Federation shared singleton (`singleton: true, eager: true`), exactly like
|
|
14
|
+
* `react` and `react-router` — see the host's federation-host.ts. As long as
|
|
15
|
+
* that's true, every consumer (host and every plugin) resolves this same
|
|
16
|
+
* module, so this same `createContext` call.
|
|
17
|
+
*/
|
|
18
|
+
const PortalPluginHostContext = createContext(null);
|
|
19
|
+
//#endregion
|
|
20
|
+
export { PortalPluginHostContext as t };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=context-Cl6RRw0h.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-Cl6RRw0h.js","names":[],"sources":["../src/context.ts"],"sourcesContent":["/**\n * The single React Context instance shared by both this package's plugin-\n * facing hooks (`.`) and the host-only Provider (`./host`).\n *\n * Why this matters for Module Federation: a Context object is only ever the\n * same Context to `useContext()` if it is literally the same module instance.\n * If the host and a plugin each bundled their own copy of this package, the\n * plugin's `useProjectContext()` would read from the plugin's own empty\n * Context — never the host's Provider. That's why cloud-portal's federation\n * host must register `@datum-cloud/portal-plugin-sdk` itself as a Module\n * Federation shared singleton (`singleton: true, eager: true`), exactly like\n * `react` and `react-router` — see the host's federation-host.ts. As long as\n * that's true, every consumer (host and every plugin) resolves this same\n * module, so this same `createContext` call.\n */\nimport { createContext } from 'react';\nimport type { PortalPluginHostBindings } from './types';\n\nexport const PortalPluginHostContext = createContext<PortalPluginHostBindings | null>(null);\n"],"mappings":";;;;;;;;;;;;;;;;;AAkBA,MAAa,0BAA0B,cAA+C,IAAI"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { s as PortalPluginHostBindings } from "../types-CvoOm66Q.js";
|
|
2
|
+
import { ReactElement, ReactNode } from "react";
|
|
3
|
+
//#region src/host/index.d.ts
|
|
4
|
+
declare function PortalPluginHostProvider({ bindings, children }: {
|
|
5
|
+
bindings: PortalPluginHostBindings;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
}): ReactElement;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { type PortalPluginHostBindings, PortalPluginHostProvider };
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/host/index.tsx"],"mappings":";;;iBAegB,2BACd,UACA;EAEA,UAAU;EACV,UAAU;IACR"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { t as PortalPluginHostContext } from "../context-Cl6RRw0h.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/host/index.tsx
|
|
4
|
+
/**
|
|
5
|
+
* Host-only wiring — imported by cloud-portal, never by a plugin.
|
|
6
|
+
*
|
|
7
|
+
* Wrap the tree that mounts plugin components (the project layout, or at
|
|
8
|
+
* minimum the plugin catch-all mount) in `<PortalPluginHostProvider>`, and
|
|
9
|
+
* pass real implementations for the three hooks. Because this package is
|
|
10
|
+
* registered as an eager, singleton Module Federation share (see
|
|
11
|
+
* federation-host.ts's `hostShared()`), a plugin's `useProjectContext()` /
|
|
12
|
+
* `usePluginFetch()` / `useResourceWatch()` calls resolve to this exact
|
|
13
|
+
* Provider's value, even though the plugin bundle never imports this module.
|
|
14
|
+
*/
|
|
15
|
+
function PortalPluginHostProvider({ bindings, children }) {
|
|
16
|
+
return /* @__PURE__ */ jsx(PortalPluginHostContext.Provider, {
|
|
17
|
+
value: bindings,
|
|
18
|
+
children
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { PortalPluginHostProvider };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/host/index.tsx"],"sourcesContent":["/**\n * Host-only wiring — imported by cloud-portal, never by a plugin.\n *\n * Wrap the tree that mounts plugin components (the project layout, or at\n * minimum the plugin catch-all mount) in `<PortalPluginHostProvider>`, and\n * pass real implementations for the three hooks. Because this package is\n * registered as an eager, singleton Module Federation share (see\n * federation-host.ts's `hostShared()`), a plugin's `useProjectContext()` /\n * `usePluginFetch()` / `useResourceWatch()` calls resolve to this exact\n * Provider's value, even though the plugin bundle never imports this module.\n */\nimport { PortalPluginHostContext } from '../context';\nimport type { PortalPluginHostBindings } from '../types';\nimport type { ReactElement, ReactNode } from 'react';\n\nexport function PortalPluginHostProvider({\n bindings,\n children,\n}: {\n bindings: PortalPluginHostBindings;\n children: ReactNode;\n}): ReactElement {\n return (\n <PortalPluginHostContext.Provider value={bindings}>{children}</PortalPluginHostContext.Provider>\n );\n}\n\nexport type { PortalPluginHostBindings } from '../types';\n"],"mappings":";;;;;;;;;;;;;;AAeA,SAAgB,yBAAyB,EACvC,UACA,YAIe;CACf,OACE,oBAAC,wBAAwB,UAAzB;EAAkC,OAAO;EAAW;CAA2C,CAAA;AAEnG"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { a as PluginWatchEvent, c as UseResourceWatchOptions, i as PluginProjectContextValue, l as UseResourceWatchResult, n as PluginOrganization, o as PluginWatchEventType, r as PluginProject, t as PluginFetch } from "./types-CvoOm66Q.js";
|
|
2
|
+
//#region src/hooks/use-project-context.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The current project and organization the plugin is mounted under. Backed
|
|
5
|
+
* by the host's own project-resolution state (session, route params, and the
|
|
6
|
+
* project/org fetch) — a plugin never resolves this itself.
|
|
7
|
+
*/
|
|
8
|
+
declare function useProjectContext(): PluginProjectContextValue;
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/hooks/use-plugin-fetch.d.ts
|
|
11
|
+
/**
|
|
12
|
+
* A `fetch` pre-scoped to the current project's control plane and mediated
|
|
13
|
+
* through the portal's authenticated Milo proxy. This is the only data path
|
|
14
|
+
* a plugin gets — every API call a plugin issues, including calls to a
|
|
15
|
+
* service's own aggregated apiserver, must go through it. There is no
|
|
16
|
+
* plugin-declared backend.
|
|
17
|
+
*/
|
|
18
|
+
declare function usePluginFetch(): PluginFetch;
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/hooks/use-resource-watch.d.ts
|
|
21
|
+
/**
|
|
22
|
+
* Live updates for a resource in the current project's control plane,
|
|
23
|
+
* through the portal's existing watch stream (the same multiplexed SSE
|
|
24
|
+
* connection built-in pages use — a plugin never opens its own watch
|
|
25
|
+
* connection to Kubernetes).
|
|
26
|
+
*/
|
|
27
|
+
declare function useResourceWatch<T = unknown>(options: UseResourceWatchOptions<T>): UseResourceWatchResult<T>;
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/version.d.ts
|
|
30
|
+
/**
|
|
31
|
+
* Identity the host and a plugin's manifest both refer to. `SDK_VERSION`
|
|
32
|
+
* follows the semver discipline from docs/enhancements/portal-plugin-system.md:
|
|
33
|
+
* additive changes (new hook, new optional field) are minor releases;
|
|
34
|
+
* removing an extension point or a hook is major, with a deprecation window.
|
|
35
|
+
* The host compares its own advertised version against a plugin manifest's
|
|
36
|
+
* `sdk.range` and refuses to load on a mismatch (`Compatible=False`) — this
|
|
37
|
+
* constant is that source of truth on the SDK side.
|
|
38
|
+
*/
|
|
39
|
+
declare const SDK_NAME = "@datum-cloud/portal-plugin-sdk";
|
|
40
|
+
declare const SDK_VERSION = "1.0.0";
|
|
41
|
+
//#endregion
|
|
42
|
+
export { type PluginFetch, type PluginOrganization, type PluginProject, type PluginProjectContextValue, type PluginWatchEvent, type PluginWatchEventType, SDK_NAME, SDK_VERSION, type UseResourceWatchOptions, type UseResourceWatchResult, usePluginFetch, useProjectContext, useResourceWatch };
|
|
43
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/hooks/use-project-context.ts","../src/hooks/use-plugin-fetch.ts","../src/hooks/use-resource-watch.ts","../src/version.ts"],"mappings":";;;;;;;iBAQgB,qBAAqB;;;;;;;;;;iBCErB,kBAAkB;;;;;;;;;iBCDlB,iBAAiB,aAC/B,SAAS,wBAAwB,KAChC,uBAAuB;;;;;;;;;;;;cCFb;cACA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { t as PortalPluginHostContext } from "./context-Cl6RRw0h.js";
|
|
2
|
+
import { useContext } from "react";
|
|
3
|
+
//#region src/hooks/use-host-bindings.ts
|
|
4
|
+
/**
|
|
5
|
+
* Every plugin-facing hook delegates to the host's real implementation rather
|
|
6
|
+
* than containing any logic itself — the SDK is a contract, not a client
|
|
7
|
+
* library. This throws with a specific, actionable message rather than
|
|
8
|
+
* silently returning defaults, because a missing binding here almost always
|
|
9
|
+
* means the MF shared-singleton wiring is wrong, not that the plugin did
|
|
10
|
+
* something incorrectly.
|
|
11
|
+
*/
|
|
12
|
+
function useHostBindings(hookName) {
|
|
13
|
+
const bindings = useContext(PortalPluginHostContext);
|
|
14
|
+
if (!bindings) throw new Error(`${hookName}() was called outside the portal host's PortalPluginHostProvider. This usually means @datum-cloud/portal-plugin-sdk is not registered as a Module Federation shared singleton on the host, so the plugin's import resolved to a separate, unbound copy of this package instead of the host's — see @datum-cloud/portal-plugin-sdk/host.`);
|
|
15
|
+
return bindings;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/hooks/use-project-context.ts
|
|
19
|
+
/**
|
|
20
|
+
* The current project and organization the plugin is mounted under. Backed
|
|
21
|
+
* by the host's own project-resolution state (session, route params, and the
|
|
22
|
+
* project/org fetch) — a plugin never resolves this itself.
|
|
23
|
+
*/
|
|
24
|
+
function useProjectContext() {
|
|
25
|
+
return useHostBindings("useProjectContext").useProjectContext();
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/hooks/use-plugin-fetch.ts
|
|
29
|
+
/**
|
|
30
|
+
* A `fetch` pre-scoped to the current project's control plane and mediated
|
|
31
|
+
* through the portal's authenticated Milo proxy. This is the only data path
|
|
32
|
+
* a plugin gets — every API call a plugin issues, including calls to a
|
|
33
|
+
* service's own aggregated apiserver, must go through it. There is no
|
|
34
|
+
* plugin-declared backend.
|
|
35
|
+
*/
|
|
36
|
+
function usePluginFetch() {
|
|
37
|
+
return useHostBindings("usePluginFetch").usePluginFetch();
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/hooks/use-resource-watch.ts
|
|
41
|
+
/**
|
|
42
|
+
* Live updates for a resource in the current project's control plane,
|
|
43
|
+
* through the portal's existing watch stream (the same multiplexed SSE
|
|
44
|
+
* connection built-in pages use — a plugin never opens its own watch
|
|
45
|
+
* connection to Kubernetes).
|
|
46
|
+
*/
|
|
47
|
+
function useResourceWatch(options) {
|
|
48
|
+
return useHostBindings("useResourceWatch").useResourceWatch(options);
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/version.ts
|
|
52
|
+
/**
|
|
53
|
+
* Identity the host and a plugin's manifest both refer to. `SDK_VERSION`
|
|
54
|
+
* follows the semver discipline from docs/enhancements/portal-plugin-system.md:
|
|
55
|
+
* additive changes (new hook, new optional field) are minor releases;
|
|
56
|
+
* removing an extension point or a hook is major, with a deprecation window.
|
|
57
|
+
* The host compares its own advertised version against a plugin manifest's
|
|
58
|
+
* `sdk.range` and refuses to load on a mismatch (`Compatible=False`) — this
|
|
59
|
+
* constant is that source of truth on the SDK side.
|
|
60
|
+
*/
|
|
61
|
+
const SDK_NAME = "@datum-cloud/portal-plugin-sdk";
|
|
62
|
+
const SDK_VERSION = "1.0.0";
|
|
63
|
+
//#endregion
|
|
64
|
+
export { SDK_NAME, SDK_VERSION, usePluginFetch, useProjectContext, useResourceWatch };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/hooks/use-host-bindings.ts","../src/hooks/use-project-context.ts","../src/hooks/use-plugin-fetch.ts","../src/hooks/use-resource-watch.ts","../src/version.ts"],"sourcesContent":["import { PortalPluginHostContext } from '../context';\nimport type { PortalPluginHostBindings } from '../types';\nimport { useContext } from 'react';\n\n/**\n * Every plugin-facing hook delegates to the host's real implementation rather\n * than containing any logic itself — the SDK is a contract, not a client\n * library. This throws with a specific, actionable message rather than\n * silently returning defaults, because a missing binding here almost always\n * means the MF shared-singleton wiring is wrong, not that the plugin did\n * something incorrectly.\n */\nexport function useHostBindings(hookName: string): PortalPluginHostBindings {\n const bindings = useContext(PortalPluginHostContext);\n if (!bindings) {\n throw new Error(\n `${hookName}() was called outside the portal host's PortalPluginHostProvider. ` +\n 'This usually means @datum-cloud/portal-plugin-sdk is not registered as a Module ' +\n \"Federation shared singleton on the host, so the plugin's import resolved to a \" +\n \"separate, unbound copy of this package instead of the host's — see \" +\n '@datum-cloud/portal-plugin-sdk/host.'\n );\n }\n return bindings;\n}\n","import { useHostBindings } from './use-host-bindings';\nimport type { PluginProjectContextValue } from '../types';\n\n/**\n * The current project and organization the plugin is mounted under. Backed\n * by the host's own project-resolution state (session, route params, and the\n * project/org fetch) — a plugin never resolves this itself.\n */\nexport function useProjectContext(): PluginProjectContextValue {\n return useHostBindings('useProjectContext').useProjectContext();\n}\n","import { useHostBindings } from './use-host-bindings';\nimport type { PluginFetch } from '../types';\n\n/**\n * A `fetch` pre-scoped to the current project's control plane and mediated\n * through the portal's authenticated Milo proxy. This is the only data path\n * a plugin gets — every API call a plugin issues, including calls to a\n * service's own aggregated apiserver, must go through it. There is no\n * plugin-declared backend.\n */\nexport function usePluginFetch(): PluginFetch {\n return useHostBindings('usePluginFetch').usePluginFetch();\n}\n","import { useHostBindings } from './use-host-bindings';\nimport type { UseResourceWatchOptions, UseResourceWatchResult } from '../types';\n\n/**\n * Live updates for a resource in the current project's control plane,\n * through the portal's existing watch stream (the same multiplexed SSE\n * connection built-in pages use — a plugin never opens its own watch\n * connection to Kubernetes).\n */\nexport function useResourceWatch<T = unknown>(\n options: UseResourceWatchOptions<T>\n): UseResourceWatchResult<T> {\n return useHostBindings('useResourceWatch').useResourceWatch(options);\n}\n","/**\n * Identity the host and a plugin's manifest both refer to. `SDK_VERSION`\n * follows the semver discipline from docs/enhancements/portal-plugin-system.md:\n * additive changes (new hook, new optional field) are minor releases;\n * removing an extension point or a hook is major, with a deprecation window.\n * The host compares its own advertised version against a plugin manifest's\n * `sdk.range` and refuses to load on a mismatch (`Compatible=False`) — this\n * constant is that source of truth on the SDK side.\n */\nexport const SDK_NAME = '@datum-cloud/portal-plugin-sdk';\nexport const SDK_VERSION = '1.0.0';\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,gBAAgB,UAA4C;CAC1E,MAAM,WAAW,WAAW,uBAAuB;CACnD,IAAI,CAAC,UACH,MAAM,IAAI,MACR,GAAG,SAAS,wUAKd;CAEF,OAAO;AACT;;;;;;;;AChBA,SAAgB,oBAA+C;CAC7D,OAAO,gBAAgB,mBAAmB,CAAC,CAAC,kBAAkB;AAChE;;;;;;;;;;ACAA,SAAgB,iBAA8B;CAC5C,OAAO,gBAAgB,gBAAgB,CAAC,CAAC,eAAe;AAC1D;;;;;;;;;ACHA,SAAgB,iBACd,SAC2B;CAC3B,OAAO,gBAAgB,kBAAkB,CAAC,CAAC,iBAAiB,OAAO;AACrE;;;;;;;;;;;;ACJA,MAAa,WAAW;AACxB,MAAa,cAAc"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Plugin-facing contract types. Kept dependency-free (no react-query, no
|
|
4
|
+
* axios, no k8s client) so the SDK's own footprint never forces a version on
|
|
5
|
+
* a plugin's toolchain — the host is free to implement these hooks with
|
|
6
|
+
* whatever internal machinery it likes; only these shapes are the contract.
|
|
7
|
+
*/
|
|
8
|
+
interface PluginProject {
|
|
9
|
+
/** Kubernetes resource name for the project; also its control-plane namespace. */
|
|
10
|
+
name: string;
|
|
11
|
+
displayName?: string;
|
|
12
|
+
}
|
|
13
|
+
interface PluginOrganization {
|
|
14
|
+
name: string;
|
|
15
|
+
displayName?: string;
|
|
16
|
+
}
|
|
17
|
+
interface PluginProjectContextValue {
|
|
18
|
+
project: PluginProject | undefined;
|
|
19
|
+
org: PluginOrganization | undefined;
|
|
20
|
+
isLoading: boolean;
|
|
21
|
+
error: Error | null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A `fetch`-like function pre-scoped to the current project's control plane
|
|
25
|
+
* and already authenticated through the portal's session. `path` is just the
|
|
26
|
+
* Kubernetes API path (e.g. `/apis/compute.miloapis.com/v1alpha1/namespaces/
|
|
27
|
+
* default/workloads`) — the host prepends the project-scoped proxy prefix and
|
|
28
|
+
* injects the session's auth. This is the only data path a plugin gets: there
|
|
29
|
+
* is no plugin-declared backend, and a plugin never picks a base URL or auth
|
|
30
|
+
* strategy itself.
|
|
31
|
+
*/
|
|
32
|
+
type PluginFetch = (path: string, init?: RequestInit) => Promise<Response>;
|
|
33
|
+
type PluginWatchEventType = 'ADDED' | 'MODIFIED' | 'DELETED' | 'BOOKMARK' | 'ERROR';
|
|
34
|
+
interface PluginWatchEvent<T = unknown> {
|
|
35
|
+
type: PluginWatchEventType;
|
|
36
|
+
object: T;
|
|
37
|
+
}
|
|
38
|
+
interface UseResourceWatchOptions<T = unknown> {
|
|
39
|
+
/** e.g. `compute.miloapis.com/v1alpha1/workloads`. Scoped to the current project. */
|
|
40
|
+
resourceType: string;
|
|
41
|
+
/** Watch a single named resource instead of a list. */
|
|
42
|
+
name?: string;
|
|
43
|
+
/** K8s namespace within the project's control plane. Defaults to `default`. */
|
|
44
|
+
namespace?: string;
|
|
45
|
+
labelSelector?: string;
|
|
46
|
+
fieldSelector?: string;
|
|
47
|
+
/** Set false to pause the subscription without unmounting. @default true */
|
|
48
|
+
enabled?: boolean;
|
|
49
|
+
/** Transform the raw watched object before it reaches `lastEvent`/`onEvent`. */
|
|
50
|
+
transform?: (raw: unknown) => T;
|
|
51
|
+
/** Imperative event handler, called for every event in addition to the returned state. */
|
|
52
|
+
onEvent?: (event: PluginWatchEvent<T>) => void;
|
|
53
|
+
}
|
|
54
|
+
interface UseResourceWatchResult<T = unknown> {
|
|
55
|
+
/** Most recently received event, or `null` before the first one arrives. */
|
|
56
|
+
lastEvent: PluginWatchEvent<T> | null;
|
|
57
|
+
/** Whether the underlying watch stream is currently connected. */
|
|
58
|
+
isConnected: boolean;
|
|
59
|
+
error: Error | null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The real implementations the host supplies behind the plugin-facing hooks.
|
|
63
|
+
* Never imported by a plugin — only by cloud-portal, via `./host`. Shaped as
|
|
64
|
+
* a bag of hooks (not a single fetch/watch pair) so the host can freely use
|
|
65
|
+
* its own React Query / watch-manager internals in each implementation.
|
|
66
|
+
*/
|
|
67
|
+
interface PortalPluginHostBindings {
|
|
68
|
+
useProjectContext(): PluginProjectContextValue;
|
|
69
|
+
usePluginFetch(): PluginFetch;
|
|
70
|
+
useResourceWatch<T = unknown>(options: UseResourceWatchOptions<T>): UseResourceWatchResult<T>;
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
export { PluginWatchEvent as a, UseResourceWatchOptions as c, PluginProjectContextValue as i, UseResourceWatchResult as l, PluginOrganization as n, PluginWatchEventType as o, PluginProject as r, PortalPluginHostBindings as s, PluginFetch as t };
|
|
74
|
+
//# sourceMappingURL=types-CvoOm66Q.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-CvoOm66Q.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;;UAWiB;;EAEf;EACA;;UAGe;EACf;EACA;;UAGe;EACf,SAAS;EACT,KAAK;EACL;EACA,OAAO;;;;;;;;;;;KAgBG,eAAe,cAAc,OAAO,gBAAgB,QAAQ;KAM5D;UAEK,iBAAiB;EAChC,MAAM;EACN,QAAQ;;UAGO,wBAAwB;;EAEvC;;EAEA;;EAEA;EACA;EACA;;EAEA;;EAEA,aAAa,iBAAiB;;EAE9B,WAAW,OAAO,iBAAiB;;UAGpB,uBAAuB;;EAEtC,WAAW,iBAAiB;;EAE5B;EACA,OAAO;;;;;;;;UAaQ;EACf,qBAAqB;EACrB,kBAAkB;EAClB,iBAAiB,aAAa,SAAS,wBAAwB,KAAK,uBAAuB"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@datum-cloud/portal-plugin-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "Host-contract SDK for cloud-portal's Portal Plugin System: React hooks (useProjectContext, usePluginFetch, useResourceWatch) that give a plugin its project identity, its only data path (the portal's authenticated Milo control-plane proxy), and live resource updates.",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/datum-cloud/portal-plugin-sdk"
|
|
10
|
+
},
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./host": {
|
|
18
|
+
"types": "./dist/host/index.d.ts",
|
|
19
|
+
"default": "./dist/host/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/index.js",
|
|
23
|
+
"module": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"files": [
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"README.md",
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsdown --config-loader native && node scripts/fix-eof.mjs",
|
|
32
|
+
"dev": "tsdown --config-loader native --watch",
|
|
33
|
+
"lint": "eslint .",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"test:watch": "vitest",
|
|
37
|
+
"clean": "rm -rf dist .turbo node_modules coverage"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"react": ">=19.0.0 <20"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@testing-library/react": "^16.3.2",
|
|
44
|
+
"@types/react": "^19.2.17",
|
|
45
|
+
"@types/react-dom": "^19.2.3",
|
|
46
|
+
"eslint": "^9.19.0",
|
|
47
|
+
"jsdom": "^29.1.1",
|
|
48
|
+
"react": "^19.2.7",
|
|
49
|
+
"react-dom": "^19.2.7",
|
|
50
|
+
"tsdown": "^0.22.3",
|
|
51
|
+
"typescript": "^5.9.3",
|
|
52
|
+
"vitest": "^4.1.9"
|
|
53
|
+
},
|
|
54
|
+
"packageManager": "pnpm@11.8.0",
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
}
|
|
58
|
+
}
|