Files
skills/docs/api-stack/api/api-plugin-user/src/plugin/plugin.md
2026-05-29 08:20:56 +01:00

88 lines
3.2 KiB
Markdown

# plugin/plugin
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The plugin module exports `createUserPlugin`, the public factory for
the user plugin's `PluginDescriptor`. The factory validates the
operator-supplied options against `UserPluginOptionsSchema`,
constructs the resolved `UserPluginConfig`, and returns a descriptor
that — when the host's `PluginRegistry` invokes `onInit(ctx)` —
applies the bundled schema, registers the `userService` slot in the
ServiceRegistry, installs `requireAuth()` on the route prefix, and
mounts the three `/users/me` routes via `definePluginRoutes`.
## Features
- Zod-validated factory input — invalid options throw
`UserError(USER_INVALID_INPUT)` carrying the `ZodError`.
- Declares `dependencies: ["auth"]` so the registry topo-sorts after
`api-plugin-auth` automatically.
- Resolves the `app`, `authService`, and `db` services from the
host's `ServiceRegistry`; raises `USER_SERVICE_NOT_AVAILABLE`
listing missing slot names when any is absent.
- Applies `USER_PROFILE_SCHEMA` via the `migrationRunner` when
registered (so the migration is tracked) and falls back to a direct
`db.execute` otherwise. Bypassed entirely when
`applySchemaOnInit: false`.
- Installs the auth plugin's `requireAuth()` middleware on the route
prefix so every `/users/me` handler runs under an authenticated
context.
- Detects route-prefix collisions in the OpenAPI registry and raises
`USER_ROUTE_PREFIX_COLLISION` rather than silently double-mounting.
## Usage: Basic
```ts
import { defineApp } from "@suaveplan/api";
import { createAuthPlugin } from "@suaveplan/api-plugin-auth";
import { createUserPlugin } from "@suaveplan/api-plugin-user";
await defineApp()
.use(createAuthPlugin({ betterAuth: { /* … */ } }))
.use(createUserPlugin({}))
.start();
```
## Usage: Advanced
Custom profile fields and the disabled self-delete path:
```ts
const userPlugin = createUserPlugin({
profileFields: ["displayName", "avatarUrl", "bio", "locale"],
allowSelfDelete: false,
applySchemaOnInit: true,
});
```
The resolved descriptor exposes `defaults` so a test harness can
inspect the post-Zod config without booting the registry.
## API Reference
### `createUserPlugin(options?: UserPluginOptions): PluginDescriptor<UserPluginConfig>`
Returns the typed descriptor consumed by the host's
`PluginRegistry`. `options` is fully optional — every field has a
default in `UserPluginOptionsSchema`.
## Implementation Notes
- The `onInit` closure captures the resolved config — the registry
rebuilds the per-hook `PluginContext` each call, so capturing the
config in the closure keeps the lifecycle stateless from the
registry's perspective.
- `requireAuth()` is installed via `app.use(prefix/*, ...)` so every
route handler sees a non-null Principal; the handlers also
defensively re-check the principal in case the operator changes the
middleware ordering.
- The route-prefix collision check compares the `/me` suffix
specifically — registering siblings under the same base path (e.g.
a future `/users/admin`) does NOT trigger the collision because
the prefix-aware audit narrows to the exact route paths the plugin
registers.