Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@suaveplan/api-plugin-example - Reference plugin for the @suaveplan/api-host plugin system. Exercises every contract surface reachable through createTestHost: definePlugin spec, REST routes (public, RBAC-gated, Zod-validated, streaming, idempotent, rate-limited, schema-violation), custom PLUGIN.EXAMPLE.* error codes mapped to HTTP statuses, manifest-declared OTel instruments (counter + histogram + gauge), seeded secrets, tenant-scoped DB, event publish + subscribe handlers. Ships its own integration suite that runs end-to-end against api-host/testing.createTestHost so any host-side change that breaks the plugin contract fails in one place.
Reference plugin for the @suaveplan/api-host plugin system. The
package is a load-bearing integration target, not a tutorial: every
contract surface the host exposes through createTestHost is wired
through one route or one hook here, and the package's own integration
test runs that surface end-to-end. When a host-side change breaks the
plugin contract, this test fails in one place against the in-memory
stack.
The plugin doubles as the canonical onboarding doc. When a plugin
author asks "how do I use capability X?", the answer is "look at how
api-plugin-example uses it."
Documentation tree
- error-codes — The reference plugin owns a small set of stable error codes that the integration suite asserts are surfaced through the…
- error-codes — The reference plugin owns a small set of stable error codes that the integration suite asserts are surfaced through the…
- events — The reference plugin both publishes and subscribes to events. This module exposes the topic names and the Zod payload s…
- events — The reference plugin both publishes and subscribes to events. This module exposes the topic names and the Zod payload s…
- instruments — The reference plugin declares three OTel instruments that mirror the
telemetry.metrics[]block inplugin.json. The…- instruments — The reference plugin declares three OTel instruments that mirror the
telemetry.metrics[]block inplugin.json. The…
- instruments — The reference plugin declares three OTel instruments that mirror the
- plugin — The reference plugin's
definePlugininvocation lives in its own module so the manifest, the integration test, and any…- plugin — The reference plugin's
definePlugininvocation lives in its own module so the manifest, the integration test, and any…
- plugin — The reference plugin's
- routes — The route table is the heart of the reference plugin. Every route demonstrates one piece of the §6 11-step request pipe…
- routes — The route table is the heart of the reference plugin. Every route demonstrates one piece of the §6 11-step request pipe…
- schemas — Every route in the reference plugin is wired against a Zod schema so the §6 input-validation step always has a contract…
- schemas — Every route in the reference plugin is wired against a Zod schema so the §6 input-validation step always has a contract…
Capability map (scope, against createTestHost)
| Capability | Surface in this package |
|---|---|
definePlugin lifecycle |
src/plugin/plugin.ts — install / start / stop / uninstall / health all wired |
| REST — public | GET /example/public/ping, GET /example/health |
| REST — RBAC-gated | GET /example/invoices, GET /example/invoices/:id, POST /example/invoices |
| REST — Zod-validated body | POST /example/invoices against CreateInvoiceRequest |
| REST — idempotent flag | POST /example/invoices (declared in plugin.json — host gate) |
| REST — rate-limited | GET /example/rate-limited (declared in plugin.json — host gate) |
| REST — streaming | GET /example/stream/large-report (chunked NDJSON) |
| REST — custom error codes | PLUGIN.EXAMPLE.{SUBSCRIPTION_INACTIVE, INVOICE_NOT_FOUND, INVALID_INVOICE_STATE} mapped in errors.statusMap |
| REST — response validation | GET /_/test/fail-response?failResponse=1 (intentional schema violation) |
| Event publish | example.invoice.created on every POST /example/invoices happy path |
| Event subscribe | users.user.deleted via makeUserDeletedHandler |
| Secrets | EXAMPLE_STRIPE_KEY, EXAMPLE_HMAC_SIGNING_KEY, EXAMPLE_WEBHOOK_TOKEN |
| Tenant resolution | Seeded tenant-int reached via X-Tenant-Id |
| OTel instruments | example.invoices_finalised_total, example.stripe.call_duration_ms, example.outstanding_invoice_count |
Capability map (declared in plugin.json for future Stages)
These surfaces are present in plugin.json because the design doc
§13.5 reference plugin SHALL declare every top-level manifest key.
The runtime implementations land alongside the host substrates as
those substrates ship in subsequent changes.
| Capability | Declared in plugin.json |
Host substrate status |
|---|---|---|
| GraphQL types + subscription | graphql.queries / mutations / subscriptions |
@suaveplan/api-graphql-yoga lands later |
| WebSocket fan-out (multi-host) | websockets[] with topic mapping |
@suaveplan/api-ws-bun + broker shipped; cross-host federation later |
SSE with Last-Event-Id |
sse[] with resume: "last-event-id" |
@suaveplan/api-sse-bun shipped; resume wiring later |
| Saga (3-step) + 2PC | sagas[] + twoPhaseCommit |
@suaveplan/api-saga + api-2pc orchestrators land later |
| Migrations (v1 → v2) | database.schemaVersion: 2 + migrationsDir |
@suaveplan/api-migrations rollback driver lands later |
| Sandboxed (vm + wasm) | Future: rebuild against api-sandbox-vm / api-sandbox-wasm |
Both shipped; isolation wiring through createHost lands later |
| Per-tenant cron + queued jobs | jobs.cron[] (incl. perTenant) + jobs.queued[] |
@suaveplan/job-scheduler already lives universal; host bridge later |
Running
bunx --bun turbo lint typecheck test:coverage --filter=@suaveplan/api-plugin-example
The integration suite at
tests/integration/full-flow.integration.test.ts runs against
createTestHost with the seeded principal, tenants, and secrets — no
external services required.
Files
plugin.json # Manifest declaring every top-level key
src/index.ts # Package barrel
src/plugin/plugin.ts # Frozen definePlugin record
src/routes/routes.ts # REST surface (10 routes)
src/schemas/schemas.ts # Zod request / response schemas
src/events/events.ts # Topic constants + handler factories
src/error-codes/error-codes.ts # PLUGIN.EXAMPLE.* registry + status map
src/instruments/instruments.ts # OTel counter + histogram + gauge-as-counter
tests/integration/full-flow.integration.test.ts
# End-to-end against createTestHost
tests/setup/polyfills.ts # In-memory MeterProvider install
Verification
The acceptance gate runs:
bunx --bun turbo lint typecheck test:coverage --filter=@suaveplan/api-plugin-example
100% Istanbul coverage is enforced; the manifest errors.statusMap
is verified end-to-end against the runtime by the integration suite.