Examples And Fixtures
This guide defines the public, stable example surface for@anikitenko/fdo-sdk.
If you are starting a new plugin, prefer the fixture set under examples/fixtures/ before the numbered learning examples.
Recommended Starting Order
examples/fixtures/minimal-plugin.fixture.tsUse when you need the smallest valid plugin scaffold. It is intentionally plain: metadata,init(), andrender()only, with no extra bridge or UI-helper concepts mixed in.examples/fixtures/error-handling-plugin.fixture.tsUse when you need deterministic render fallback behavior. It demonstrates@handleError, realUI_MESSAGEhandler invocation, and runtime-safe fallback UI without extra UI abstraction.examples/fixtures/storage-plugin.fixture.tsUse when you need plugin-scoped storage with graceful JSON-store handling. It demonstrates backend storage handlers, session fallback when JSON storage is unavailable, and UI_MESSAGE-driven storage actions instead of a static snapshot only.examples/fixtures/operator-kubernetes-plugin.fixture.tsUse when building akubectl-style operator plugin. It demonstrates the current curated operator pattern: declare preset capabilities, build validated operator/workflow envelopes in backend code, fetch them throughUI_MESSAGE, and send them through the host privileged-action path.examples/fixtures/operator-terraform-plugin.fixture.tsUse when building a Terraform preview/apply plugin. It demonstrates the same curated operator pattern for Terraform: declare preset capabilities, build validated plan/workflow envelopes in backend code, fetch them throughUI_MESSAGE, and send them through the host privileged-action path.examples/fixtures/operator-custom-tool-plugin.fixture.tsUse when you need a host-specific scoped tool that is not covered by a curated operator preset. It demonstrates the generic scoped-process pattern: declare the broad execution capability plus a narrow custom process scope, build a validated scoped-process envelope in backend code, fetch it throughUI_MESSAGE, and send it through the host privileged-action path.
examples/01-... to examples/10-... are learning references. They are not the default production starting point.
Production-Grade Rules
Use these rules for examples, fixtures, and AI-generated plugin scaffolds:- Keep backend orchestration in plugin methods and registered handlers.
- Keep
renderOnLoad()thin and UI-focused. - Do not call
PluginRegistry.useStore(...)in class-field initializers; acquire stores lazily or ininit()after metadata is available. - If
render()uses styled SDK DOM-helper output, wrap the final helper markup withrenderHTML(...). - Treat
renderHTML(...)as mandatory for styled DOM-helper output because it emits the extracted goober CSS alongside the markup. - Treat DOM helpers as the preferred SDK pattern for general structured UI.
- Use plain markup intentionally only when the example is isolating a different lesson, such as injected libraries or the iframe/backend boundary.
- The numbered learning examples are an intentional progression:
01plain markup for the minimal lifecycle contract02plain markup for handlers,renderOnLoad(), andUI_MESSAGE03plain markup for storage and backend/UI separation04plain markup for quick actions and side-panel routing05DOM helpers directly, including therenderHTML(...)rule06plain markup for error handling and runtime-safe fallback behavior07plain markup for injected iframe libraries and browser-only helpers08plain markup for low-level privileged-action transport and response handling09plain markup for the curated operator-helper path, declared preset capabilities, and host-mediated execution10plain markup for generic scoped filesystem mutation when a plugin needs to edit a system file other than/etc/hosts
- Even plain markup examples must remain JSX-compatible for the FDO host transform.
- Escape JSX-sensitive code samples and prefer JSX-safe tags such as
<br />. - Prefer descriptive text over raw guard-sensitive capability/runtime tokens in
render()when the literal token is not required for the lesson. - Example: in
09, describe the broad capability plus narrow scope instead of embedding rawsystem.process.*strings in the rendered UI, because host fail-fast guards may conservatively flag those tokens. - Do not embed raw JSON snapshots directly in
render(); use a safe placeholder and load the structured data after iframe initialization. - For UI-to-backend calls, use the real bridge contract:
-
If a backend handler returns a privileged-action envelope from
createPrivilegedActionBackendRequest(...), extract the validated request object before calling the rawrequestPrivilegedActionbridge. PreferextractPrivilegedActionRequest(envelopeOrRequest). If you need fallback compatibility, useenvelope?.result?.request ?? envelope?.request ?? envelopeand keepenvelope?.result?.correlationId ?? envelope?.correlationIdonly for diagnostics/fallback display. -
For non-
okprivileged responses, format errors withformatPrivilegedActionError(...)so users get correlation IDs and host process diagnostics (stderr,stdout,exitCode, command,cwd) when available. -
In
renderOnLoad()string runtimes, usegetInlinePrivilegedActionErrorFormatterSource()to inline the same formatter function. -
For known operator tool families, prefer:
createOperatorToolCapabilityPreset(...)requestOperatorTool(...)
-
For multi-step operator flows, prefer:
requestScopedWorkflow(...)
-
For host-specific or non-curated tools, prefer:
requestScopedProcessExec(...)
-
For privileged or operator plugins, declare expected capabilities in code via
declareCapabilities(). -
AI provider globals should follow one canonical host naming scheme:
- assistants list:
globalThis.__FDO_AI_LIST_ASSISTANTS - AI request:
globalThis.__FDO_AI_REQUEST
- assistants list:
Validation Expectations
The examples surface is considered stable only when all of the following stay true:npm run test:examplespasses- public example docs do not reference local-only or internal planning files
- public example docs do not reference stale docs domains
createBackendReq(...)examples reflect the realUI_MESSAGEhandler pattern- DOM-helper examples that rely on helper-generated styles call
renderHTML(...) - the canonical fixtures remain the primary recommended starting point
Fixture Runtime Matrix Contract
Use the SDK fixture runtime matrix helpers as the source of truth for host CI smoke coverage:getFixtureRuntimeMatrix()listFixtureRuntimeMatrixCases()getFixtureRuntimeMatrixCase(id)
- canonical fixture path
- required lifecycle probes (
init,render,renderOnLoad) - canonical
UI_MESSAGEhandler probes - required capabilities when relevant (for example storage/operator fixtures)
Canonical Operator Patterns
For operator plugins, the recommended order is:- start from the closest fixture
- use curated capability presets for known tool families
- use
requestOperatorTool(...)for single-action known-tool execution - use
requestScopedWorkflow(...)for preview/apply or inspect/act flows - use
requestScopedProcessExec(...)for host-specific internal tools - use lower-level transport helpers only when transport-level control is explicitly required