All checks were successful
CD / Build and push images (push) Successful in 3m14s
CI / Lint, typecheck, test (push) Successful in 3m18s
CI / Auth e2e pack (push) Successful in 4m3s
CI / Import/export fidelity gate (push) Successful in 54s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 9s
CD / Smoke tests against Test (push) Successful in 1m9s
CD / Promote to Int (push) Has been skipped
The SDK is the contract every other M7 story builds on (ADR 0008,
plugin-architecture.md). New package `@dorfteich/plugin-sdk`, standalone
(only depends on zod) so a plugin author needs nothing else.
- Zod manifest schema (`validateManifest`/`parseManifest`) with actionable
`{ path, message }` issues and cross-field rules (extension-point/kind
match, unique ids, section_style declares no permissions). Fixtures:
3 valid + 14 invalid variants, asserted individually.
- `checkApiVersion` compatibility helper against the host's supported range.
- Capability names + method→capability map as the single source of truth
for the permission gate.
- Transport-agnostic postMessage RPC engine (`createRpcEndpoint`) with
request/response ids, per-request timeouts, unknown-method and
endpoint-disposed handling, plus a `windowTransport` adapter.
- Host side (`createHostBridge`): routes plugin capability calls through
the manifest permission gate; drives plugin lifecycle (render/edit/destroy).
- Plugin side (`createPlugin`): answers lifecycle calls, exposes a typed
`host` proxy. RPC roundtrip verified in a jsdom MessageChannel test
(roundtrip, args, timeout, unknown method, undeclared capability, dispose).
- README documents the protocol with a mermaid sequence diagram.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
62 lines
2.0 KiB
TypeScript
62 lines
2.0 KiB
TypeScript
/**
|
||
* apiVersion compatibility (ADR 0008: "incompatible plugins are refused at
|
||
* install time"). The manifest's `apiVersion` is a single major version string
|
||
* (e.g. `"1"`); the host declares the inclusive range of majors it supports and
|
||
* refuses anything outside it.
|
||
*/
|
||
|
||
/** The highest plugin API major this SDK release implements. */
|
||
export const HOST_API_VERSION = 1;
|
||
|
||
/** Inclusive range of plugin API majors a host accepts. */
|
||
export interface ApiVersionRange {
|
||
min: number;
|
||
max: number;
|
||
}
|
||
|
||
/** The default range: everything from major 1 up to the current host version. */
|
||
export const DEFAULT_API_VERSION_RANGE: ApiVersionRange = { min: 1, max: HOST_API_VERSION };
|
||
|
||
export interface ApiVersionCheck {
|
||
compatible: boolean;
|
||
/** The parsed major, or `null` when `apiVersion` was not a valid version. */
|
||
version: number | null;
|
||
/** Present only when incompatible: a human-readable, actionable reason. */
|
||
reason?: string;
|
||
}
|
||
|
||
/**
|
||
* Checks a manifest `apiVersion` against the host's supported range. Returns a
|
||
* structured result so callers can surface the reason to the Site Admin rather
|
||
* than a bare boolean.
|
||
*/
|
||
export function checkApiVersion(
|
||
apiVersion: string,
|
||
range: ApiVersionRange = DEFAULT_API_VERSION_RANGE,
|
||
): ApiVersionCheck {
|
||
if (!/^\d+$/.test(apiVersion)) {
|
||
return {
|
||
compatible: false,
|
||
version: null,
|
||
reason: `apiVersion "${apiVersion}" is not a whole-number major version`,
|
||
};
|
||
}
|
||
const version = Number.parseInt(apiVersion, 10);
|
||
if (version < range.min || version > range.max) {
|
||
return {
|
||
compatible: false,
|
||
version,
|
||
reason: `plugin apiVersion ${version} is outside the supported range ${range.min}–${range.max}`,
|
||
};
|
||
}
|
||
return { compatible: true, version };
|
||
}
|
||
|
||
/** Convenience boolean form of {@link checkApiVersion}. */
|
||
export function isApiVersionSupported(
|
||
apiVersion: string,
|
||
range: ApiVersionRange = DEFAULT_API_VERSION_RANGE,
|
||
): boolean {
|
||
return checkApiVersion(apiVersion, range).compatible;
|
||
}
|