first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
# plugin/plugin
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`createAuthPlugin(options)` is the factory that returns a typed
`PluginDescriptor<AuthPluginConfig>` ready to register with the host's
`PluginRegistry`. It is the canonical exemplar of the 3-way
plug-and-play contract every `@suaveplan/api-plugin-*` package follows:
the plugin owns its `.surql` DDL, its Zod schemas, and its OpenAPI
routes, and applies all three during `onInit`.
## Features
- Zod-validated options via `AuthPluginOptionsSchema` — invalid input
raises `AuthError(AUTH_INVALID_INPUT)` carrying the `ZodError`.
- 6-step `onInit` lifecycle: resolve services → apply schema →
construct Better-Auth → register `authService` → register routes →
install session middleware.
- `routePrefix`-aware route mounting — paths are rebuilt at the
prefix the operator chose, both in the served Hono app and in the
`OpenAPIRegistry` snapshot.
- Graceful degradation — when `openAPIRegistry` is absent, routes
mount on the Hono app directly and a warning is logged. When neither
`migrationRunner` nor `db` is available with `applySchemaOnInit: true`,
init fails fast with `AUTH_SERVICE_NOT_AVAILABLE`.
- Collision detection — pre-existing routes under the configured
prefix raise `AUTH_ROUTE_PREFIX_COLLISION`.
## Usage: Basic
```ts
import { defineApp } from "@suaveplan/api";
import { createAuthPlugin } from "@suaveplan/api-plugin-auth";
const app = await defineApp()
.name("my-api")
.use(
createAuthPlugin({
betterAuth: {
secret: process.env.BETTER_AUTH_SECRET!,
baseURL: "http://localhost:3000",
emailAndPassword: { enabled: true },
database: surrealAdapter,
},
})
)
.start();
```
## Usage: Advanced
Mount the plugin under a versioned prefix, disable the in-plugin
schema apply step (an external migration tool handles DDL), and inject
a custom principal mapping that decorates the Better-Auth user with
roles derived from the active organisation:
```ts
import { defineApp } from "@suaveplan/api";
import {
createAuthPlugin,
mapBetterAuthUserToPrincipal,
} from "@suaveplan/api-plugin-auth";
const app = await defineApp()
.name("my-api")
.use(
createAuthPlugin({
betterAuth: { /* … */ },
routePrefix: "/api/v1/auth",
applySchemaOnInit: false,
principalMapping: (view) => {
const base = mapBetterAuthUserToPrincipal(view);
const session = view.session as { roles?: string[] };
return {
...base,
roles: [...base.roles, ...(session.roles ?? [])],
};
},
})
)
.start();
```
## API Reference
### `function createAuthPlugin(options: AuthPluginOptions): PluginDescriptor<AuthPluginConfig>`
- Validates `options` via `AuthPluginOptionsSchema`.
- Returns a `PluginDescriptor` with `name: "auth"`, `version: "0.1.0"`,
`tags: ["auth", "security"]`, and the 4 lifecycle hooks (only
`onInit` + `onClose` are wired; `onRegister` and `onReady` are
default no-ops).
- `onInit` runs the canonical 6-step lifecycle described above. It
raises `AUTH_*` codes on the explicit failure modes — see
`../error-codes/error-codes.md`.
- `onClose` releases the cached Better-Auth reference so the registry
can re-init the plugin on subsequent boots.
## Implementation Notes
- The descriptor's `configSchema` is the `AuthPluginOptionsSchema` so
the host registry re-validates the merged config at registration
time. The `defaults` field carries the resolved options so plugins
registered without an explicit config still see the parsed shape.
- The session middleware is installed AFTER routes are registered so
the OpenAPI doc snapshot is taken before any per-request work
begins. Order does not affect runtime behaviour but keeps the
registry's events ordered consistently across reboots.
- The runtime check against `openAPIRegistry === undefined` exists
even though the registry's TS-level slot is non-optional —
`ctx.getService` adds `| undefined` to every slot's return type, so
the absent-runtime case still type-checks.
- `applySchema` prefers the migration runner over the raw `db`
adapter so operators get migration-record tracking when the runner
is wired. Falling back to `applyBetterAuthSchema(db)` ensures the
plugin still boots in lightweight deployments that ship without
`@suaveplan/api-migrations`.