UNPKG

kitcn

Version:

kitcn - React Query integration and CLI tools for Convex

154 lines (109 loc) 6.84 kB
# Create Plugins Canonical patterns for new kitcn plugins. ## Goals 1. Keep runtime bundles entry-local. 2. Keep schema extension ownership explicit and local. 3. Keep scaffolds predictable and user-owned. 4. Keep CLI generic; plugin behavior comes from plugin manifests. ## Package Layout Use split ownership: 1. `@kitcn/<plugin>`: runtime capability, middleware, stable helpers, shared types. 2. `packages/kitcn/src/cli/plugins/<plugin>/...`: scaffold templates for local schema/runtime files. 3. `convex/lib/plugins/<plugin>/schema.ts`: app-owned schema extension generated by the CLI. Do not ship first-party schema entrypoints from plugin packages. ## Runtime API Contract Use middleware-scoped access with one runtime primitive: ```ts // app code import { MyPlugin } from '@kitcn/my-plugin'; import { createPluginsMyPluginCaller } from './generated/plugins/my-plugin.runtime'; import { privateMutation } from './lib/crpc'; export const myPlugin = MyPlugin.configure({ enabled: true, }); export const myProcedure = privateMutation.use(myPlugin.middleware()) .mutation(async ({ ctx }) => { const caller = createPluginsMyPluginCaller(ctx); await caller.doWork({}); }); ``` Plugin packages should define their runtime entry with `definePlugin('<key>', ...)` from `kitcn/plugins` and expose a named plugin value such as `ResendPlugin`. Rules: 1. No `ctx.getApi(...)` runtime path. 2. Middleware injects plugin runtime surface at `ctx.api.<plugin>`. 3. Use generated callers (`createPlugins<Plugin>Caller(ctx)`) for internal function composition. 4. Reusable defaults go on plugin chain via `.configure(...)`. 5. App env resolution belongs in scaffold/app code, not inside the package plugin. Package plugins should not read `process.env` for app secrets. 6. Extend plugins with `.extend(({ middleware }) => ({ middleware: () => middleware().pipe(...), ...namedPresets }))`. 7. `plugin.middleware()` still injects `ctx.api.<plugin>`; middleware overrides should build on the helper `middleware()`, not replace injection from scratch. 8. Prefer object config. 9. Use callback config only when context is required. 10. No helper alternatives like `getMyPlugin(...)`. 11. Plugin runtime code must use ORM context (`ctx.orm`) when it touches kitcn schema. ## Private Procedure Contract Plugin internal Convex functions should be scaffold-owned cRPC private procedures. Rules: 1. Reuse project cRPC builders from `convex/<paths.lib>/crpc.ts`. 2. Define plugin internals with `privateQuery`, `privateMutation`, `privateAction` and chain plugin middleware per procedure. 3. Do not define plugin internals with vanilla `internalQuery/internalMutation/internalAction`. 4. Do not ship `create*Runtime` factory patterns from plugin packages. 5. Do not ship `build*Handlers` wrapper factories from plugin packages. 6. Do not use `ctx.runQuery`/`ctx.runMutation`/`ctx.runAction` for plugin internal composition. Use `create<Module>Caller(ctx)` from `generated/<module>.runtime`. 7. Hook/callback fanout should be scaffold-owned internal procedures, not dynamic function-handle config. ## Schema Extension Contract Schema lives in local scaffolded files and is defined with `defineSchemaExtension(...)`. Expected shape: 1. `defineSchemaExtension('<key>', { ...tables })` 2. optional chained `.relations((r) => ({ ... }))` 3. optional chained `.triggers({ ... })` Canonical install: ```ts import { defineSchema } from 'kitcn/orm'; import { myExtension } from '../lib/plugins/my-plugin/schema'; export default defineSchema(tables).extend(myExtension()); ``` Relation composition rules: 1. Extension `relations(...)` is merged before app `defineSchema(...).relations(...)`. 2. Duplicate relation fields (`table.field`) throw. 3. Use extension relation defaults for baseline wiring; app-level relations should extend, not duplicate fields. Trigger composition rules: 1. Extension triggers are merged before app triggers. 2. Duplicate trigger hooks for the same table/event throw. ## Scaffold Rules 1. Local schema file lives at `convex/<paths.lib>/plugins/<plugin>/schema.ts`. 2. Plugin Convex function exports live in `<functionsDir>/plugins/<plugin>.ts`. 3. Non-function helpers live under `convex/<paths.lib>/plugins/<plugin>/...`. 4. `kitcn codegen` must not generate plugin runtime modules. 5. Scaffold templates need stable template IDs. 6. `add` can merge/upsert scaffold mappings; never clobber custom files unless overwrite is explicit. 7. `add --dry-run`, `add --diff [path]`, and `add --view [path]` preview one shared install plan: scaffold files, env bootstrap, `kitcn.json`, schema registration, lockfile write, dependency install status, codegen/hooks, env reminders. 8. Preview comparisons for `.ts`, `.tsx`, `.js`, `.jsx`, and `.json` should be semantic enough to ignore formatter-only churn. 9. `view` is read-only plan inspection. Default template source is lockfile mappings, fallback is the resolved preset, `--preset` forces preset selection. 10. `info` audits installed plugins from schema + lockfile and reports scaffold drift / missing dependencies. 11. Runtime packages should stay focused on stable logic; function wiring and schema stay local. 12. If `paths.env` is missing, `kitcn add <plugin>` bootstraps `${paths.lib}/get-env.ts` and writes `paths.env` into `kitcn.json`. 13. `envFields` can attach reminder metadata; `kitcn add <plugin>` prints those reminders against `<functionsDir>/.env` and includes them in JSON output. 14. Scaffolded Convex files should read env through `getEnv()` only. Do not generate `process.env` access in plugin scaffolds. ## Lockfile Rules Lockfile path is fixed: `<functionsDir>/plugins.lock.json` Current contract: 1. `plugins.<plugin>.package = <packageName>` (required) 2. optional `plugins.<plugin>.files.<templateId> = <relativePath>` No `version` or timestamp fields in lockfile. ## CLI Manifest Guidance Each plugin should provide: 1. `label` / `description` for prompts plus `view` / `info` output 2. `docs` links and `keywords` for `kitcn docs` 3. preset definitions 4. scaffold template resolver 5. local schema registration metadata (`importName`, file path, target root) CLI stays plugin-agnostic; plugin-specific flags should not be added globally. ## Test Checklist 1. plugin ctx typing: `ctx.api.<plugin>` available only after `.use(plugin.middleware())` 2. codegen: no plugin runtime artifacts are generated 3. stale cleanup: `generated/plugins/**` artifacts are removed 4. add flow: idempotent scaffold writes + lockfile mapping 5. preview flow: plan captures changed/missing scaffold files plus schema/lockfile/config/env updates 6. runtime parity tests for plugin API methods exposed via middleware 7. schema registration: local `convex/lib/plugins/<plugin>/schema.ts` is scaffolded and imported once in root schema