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,94 @@
# plugin/plugin
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The plugin module exposes `createSearchPlugin`, the
`@suaveplan/api-plugin`-shaped factory that the host
`PluginRegistry` consumes to bring the search engine online. It
defines the three lifecycle hooks (`onInit`, `onReady`, `onClose`),
validates factory options through `SearchPluginOptionsSchema`,
registers the in-memory index map on the `ServiceRegistry`, mounts
the 9-route OpenAPIHono surface under the configured `routePrefix`,
and emits four lifecycle events through the host event bus. The
factory is the single entry point consumers use to add search to an
API host built on `@suaveplan/api`.
## Features
- Zod-validated factory options with typed `SearchError` failure on
invalid input (`SEARCH.INVALID_OPTIONS`).
- Three-phase lifecycle: `onInit` registers the index map, `onReady`
builds + mounts routes, `onClose` clears the map.
- Typed emission through `ctx.eventBus` — no `as any` anywhere; the
emitter pattern-matches on `SearchRouteEvent.type` and forwards the
payload directly.
- `optionalDependencies: ["database", "events"]` so the host can
topo-sort the search plugin after the database and events plugins
when they exist, without forcing them.
- `SERVICE_NOT_AVAILABLE` failure when `app` is absent from the
registry — surfaces misconfiguration during `onReady` rather than
silently no-oping.
## Usage: Basic
```ts
import { createApi } from "@suaveplan/api";
import { createSearchPlugin } from "@suaveplan/api-plugin-search";
const api = createApi({
plugins: [createSearchPlugin()],
});
```
## Usage: Advanced
Bind the plugin to a custom mount path, narrow the per-index document
ceiling, and switch the default analyzer to `whitespace`:
```ts
const api = createApi({
plugins: [
createSearchPlugin({
routePrefix: "/api/fts",
maxIndexes: 50,
maxDocumentsPerIndex: 50_000,
defaultAnalyzer: "whitespace",
}),
],
});
```
## API Reference
### `createSearchPlugin(options?: Partial<SearchPluginConfig>): PluginDescriptor<SearchPluginConfig>`
Validates `options` through `SearchPluginOptionsSchema`. Returns a
descriptor with `name: "search"`, `version: "0.1.0"`,
`optionalDependencies: ["database", "events"]`,
`tags: ["search", "fulltext", "facets", "relevance"]`. Throws
`SearchError(SEARCH.INVALID_OPTIONS)` on validation failure.
The descriptor implements:
- `onInit(ctx)` — registers `searchIndexes` service, emits
`search:initialized`.
- `onReady(ctx)` — resolves `app`, builds routes via
`createSearchRoutes`, mounts under `routePrefix`, emits
`search:ready`. Throws `SearchError(SEARCH.SERVICE_NOT_AVAILABLE)`
if `app` is missing.
- `onClose(ctx)` — clears the in-memory index map.
## Implementation Notes
`runOnReady` extracts the body of `onReady` into a top-level function
so the descriptor's hook arrow stays a one-liner — this keeps the
lambda's source-map exact and keeps test coverage attributable to
`runOnReady` directly rather than to an anonymous closure. The
`indexes` map is captured in the factory closure so the three hooks
share a single reference; this is intentional and matches the
upstream design (a fresh `createSearchPlugin()` call creates a fresh
index map, so concurrent plugin instances never share state).