94 lines
3.6 KiB
Markdown
94 lines
3.6 KiB
Markdown
# plugin/plugin
|
|
|
|
<!-- begin:auto-docs -->
|
|
_(no submodules)_
|
|
<!-- end:auto-docs -->
|
|
|
|
## Purpose
|
|
|
|
`createVaultPlugin(options)` is the factory that returns a typed
|
|
`PluginDescriptor<VaultPluginConfig>` ready to register with the host's
|
|
`PluginRegistry`. Third instance of the 3-way plug-and-play contract:
|
|
the plugin owns its `.surql` DDL, its Zod schemas, and its OpenAPI
|
|
routes, and applies all three during `onInit`. Depends on
|
|
`api-plugin-auth` so every route's principal scoping has a typed
|
|
upstream supplier.
|
|
|
|
## Features
|
|
|
|
- Zod-validated options via `VaultPluginOptionsSchema` — invalid input
|
|
raises `VaultPluginError(VAULT_PLUGIN_INVALID_INPUT)` carrying the
|
|
`ZodError`.
|
|
- Declares `dependencies: ["auth"]` so the registry refuses to load
|
|
the plugin without `api-plugin-auth` registered upstream.
|
|
- 6-step `onInit` lifecycle: resolve services → apply schema →
|
|
construct `vaultService` → register `vaultService` → register routes
|
|
→ (no global middleware — `requireAuth()` is per-route).
|
|
- `routePrefix`-aware route mounting — paths are rebuilt at the prefix
|
|
the operator chose, both in the served Hono app and in the
|
|
`OpenAPIRegistry` snapshot. Default `/vault`.
|
|
- Collision detection — pre-existing routes under the configured
|
|
prefix raise `VAULT_PLUGIN_ROUTE_PREFIX_COLLISION`.
|
|
- Graceful degradation — when `openAPIRegistry` is absent, routes
|
|
mount on the Hono app directly (no doc); when neither
|
|
`migrationRunner` nor `db` is available with `applySchemaOnInit: true`,
|
|
init fails fast with `VAULT_PLUGIN_SERVICE_NOT_AVAILABLE`.
|
|
|
|
## Usage: Basic
|
|
|
|
```ts
|
|
import { defineApp } from "@suaveplan/api";
|
|
import { createAuthPlugin } from "@suaveplan/api-plugin-auth";
|
|
import { createVaultPlugin } from "@suaveplan/api-plugin-vault";
|
|
|
|
const app = await defineApp()
|
|
.name("my-api")
|
|
.use(createAuthPlugin({ betterAuth: { /* ... */ } }))
|
|
.use(createVaultPlugin({ storage: driveAdapter }))
|
|
.start();
|
|
```
|
|
|
|
## Usage: Advanced
|
|
|
|
```ts
|
|
const plugin = createVaultPlugin({
|
|
storage: driveAdapter,
|
|
routePrefix: "/v1/vault",
|
|
maxItemSizeBytes: 4 * 1024 * 1024,
|
|
serverPepper: process.env["VAULT_SERVER_PEPPER"]!,
|
|
unlockExtractor: (c) =>
|
|
c.req.header("X-Custom-Vault-Unlock") ?? null,
|
|
applySchemaOnInit: false, // operator runs migrations externally
|
|
});
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### `createVaultPlugin(options: VaultPluginOptions): PluginDescriptor<VaultPluginConfig>`
|
|
|
|
Returns a `definePlugin` descriptor. `name: "vault"`, `version: "0.1.0"`,
|
|
`dependencies: ["auth"]`.
|
|
|
|
The `defaults` field carries every resolved option after Zod
|
|
validation — the registry passes that to `onInit` via `ctx.getOwnConfig()`.
|
|
|
|
`onInit(ctx)` resolves services, applies the schema, constructs the
|
|
service, registers it, and registers the 5 routes.
|
|
|
|
## Implementation Notes
|
|
|
|
- The plugin DOES NOT install global middleware. `requireAuth()` from
|
|
`api-plugin-auth` is applied per-route inside `buildVaultRoutes` so
|
|
the public `/openapi.json` doc only carries vault routes for the
|
|
authenticated scope and operators can leave the vault prefix
|
|
out of public-facing API documentation tooling.
|
|
- The `rewritePath` helper preserves `{id}` segments verbatim — when
|
|
the operator chooses `/v1/vault`, the route path
|
|
`/vault/items/{id}` becomes `/v1/vault/items/{id}`.
|
|
- The collision check inspects every previously registered route whose
|
|
path starts with the configured prefix — operators wiring multiple
|
|
vault plugin instances under different prefixes are unaffected.
|
|
- The plugin does not provide an `onClose` hook because the
|
|
`vaultService` is stateless across requests — every handle is
|
|
opened + closed within a single request lifecycle.
|