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

3.2 KiB

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

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:

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.