first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,96 @@
# define
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `define` module exports a single function — `definePlugin<TConfig>(descriptor)`
— that is the canonical entry point for authoring a plugin. The function
returns its input unchanged at runtime; the value is entirely in its
type signature, which threads the `TConfig` generic through every place
the descriptor references it: `configSchema?: ZodType<TConfig>`,
`defaults?: Partial<TConfig>`, and the four lifecycle hooks
(`onRegister/onInit/onReady/onClose`). Inside each hook,
`ctx.getOwnConfig()` returns `TConfig` with no cast required.
Using `definePlugin` is preferred over building a raw `PluginDescriptor`
literal because it pins the generic at the call site. TypeScript will
then flag mismatches at the source of the error (the hook body), not at
the registration site one or two files away.
## Features
- Zero runtime overhead — the function body is `return descriptor`.
- Full `TConfig` inference flows into hooks via `PluginContext<TConfig>`.
- Compatible with descriptor field defaults (`description`, `dependencies`,
`optionalDependencies`, `priority`, `enabledByDefault`, `tags`).
- Plays nicely with `as const` literals — readers preserve `readonly`
arrays for `dependencies` etc.
## Usage: Basic
```ts
import { definePlugin } from "@suaveplan/api-plugin/define";
import z from "zod";
export const auth = definePlugin({
name: "auth",
version: "0.1.0",
configSchema: z.object({ sessionTtlSeconds: z.number().int().positive() }),
defaults: { sessionTtlSeconds: 3600 },
onInit(ctx) {
const { sessionTtlSeconds } = ctx.getOwnConfig();
ctx.logger.info("auth ready", { sessionTtlSeconds });
},
});
```
## Usage: Advanced
Multiple hooks with `await` and the abort signal:
```ts
export const queue = definePlugin<{ concurrency: number }>({
name: "queue",
version: "0.1.0",
dependencies: ["db"],
defaults: { concurrency: 4 },
async onInit(ctx) {
const { concurrency } = ctx.getOwnConfig();
ctx.logger.info("starting queue", { concurrency });
},
async onReady(ctx) {
if (ctx.signal.aborted) return;
ctx.logger.info("queue ready");
},
async onClose(ctx) {
ctx.logger.info("queue draining");
},
});
```
## API Reference
| Export | Signature |
|---|---|
| `definePlugin<TConfig>(descriptor)` | `(PluginDescriptor<TConfig>) => PluginDescriptor<TConfig>` |
## Implementation Notes
The function is intentionally an identity at runtime so it can be
constant-folded by bundlers — the dependency graph rewards leaf
helpers that the optimiser can drop. Because the `descriptor` parameter
is typed as `PluginDescriptor<TConfig>`, TypeScript will not narrow
`TConfig` away if the call site supplies an explicit generic
(`definePlugin<MyConfig>({...})`). Implicit inference also works
when `configSchema` is provided as a Zod schema, because the
descriptor field is typed `ZodType<TConfig>` and Zod's schema generic
is contravariant in this position.
Calling `definePlugin` is preferred over a bare object literal even
though both are runtime-equivalent: the call site is the right place
for an editor to surface "Quick Info" on the generic, and it keeps the
public author surface consistent across the 40+ in-flight
`@suaveplan/api-plugin-*` packages.