# plugin/plugin _(no submodules)_ ## 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` 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.