88 lines
3.2 KiB
Markdown
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.
|