first commit
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# routes/build-routes
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
The build-routes module constructs the `OpenAPIHono` instance that the
|
||||
plugin mounts under `routePrefix` (`/search` by default). It declares
|
||||
the 9 canonical search endpoints — query, suggest, list indexes,
|
||||
create index, get index, delete index, index document, bulk index, and
|
||||
remove document — wiring every request through Zod validation via
|
||||
`@hono/zod-openapi`, dispatching to the in-memory engine in
|
||||
`service/service.ts`, and propagating typed events through the
|
||||
supplied `emitEvent` callback. Error responses are normalised to the
|
||||
canonical `{ error: string }` shape and the HTTP status is driven by
|
||||
the `context.statusCode` carried on each `SearchError`.
|
||||
|
||||
## Features
|
||||
|
||||
- 9 endpoints with OpenAPI 3.1 metadata baked in: every request body /
|
||||
path param / query string is Zod-validated, every response shape
|
||||
declared.
|
||||
- Typed `SearchRouteEvent` discriminated union so emissions never need
|
||||
`as any` and consumers can pattern-match on `event.type`.
|
||||
- Typed errors mapped to HTTP via `errorPayload`: `SearchError` with
|
||||
`context.statusCode === 404` maps to 404, every other failure to
|
||||
400; non-`SearchError` throwables stringify their message.
|
||||
- Side-effect-free factory — `createSearchRoutes` builds a fresh
|
||||
router on every call, so concurrent plugin instantiations never
|
||||
share router state.
|
||||
- Pure dispatch layer — the routes carry zero business logic; they
|
||||
exist purely to translate HTTP into service calls and back.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { createSearchRoutes } from "@suaveplan/api-plugin-search";
|
||||
|
||||
const router = createSearchRoutes({
|
||||
indexes,
|
||||
serviceConfig: {
|
||||
maxIndexes: 100,
|
||||
maxDocumentsPerIndex: 100_000,
|
||||
defaultAnalyzer: "standard",
|
||||
},
|
||||
});
|
||||
|
||||
app.route("/search", router);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Wire the emitter to the host event bus so domain code can react to
|
||||
search lifecycle:
|
||||
|
||||
```ts
|
||||
const router = createSearchRoutes({
|
||||
indexes,
|
||||
serviceConfig,
|
||||
emitEvent: (event) => {
|
||||
eventBus.emit(event.type, event.payload);
|
||||
logger.info("search-event", { type: event.type });
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `createSearchRoutes(deps): OpenAPIHono`
|
||||
|
||||
Returns the configured router with 9 endpoints registered.
|
||||
|
||||
### `SearchRoutesDeps`
|
||||
|
||||
`{ indexes: Map<string, SearchIndex>; serviceConfig: SearchServiceConfig; emitEvent?: (event: SearchRouteEvent) => void }`.
|
||||
|
||||
### `SearchRouteEvent`
|
||||
|
||||
Discriminated union — `search:query:executed` /
|
||||
`search:index:updated` / `search:index:created` /
|
||||
`search:index:deleted`. Every variant carries `type` + `payload` so
|
||||
emitters can pattern-match without unsafe casts.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The error-handling helper `errorPayload` exists once rather than per
|
||||
route so the mapping rule (`statusCode 404 → 404`, anything else →
|
||||
400) lives in one place. The five lookup routes that throw 404 by
|
||||
contract clamp the payload status to 404 explicitly; the index/bulk
|
||||
write routes that can throw `INDEX_NOT_FOUND` (404) or
|
||||
`DOCUMENT_LIMIT_REACHED` (400) consult the underlying status. The
|
||||
`getSearchIndex` post-create check guards against the impossible case
|
||||
where the service inserted a name that immediately fails lookup —
|
||||
defensive but unreachable, kept to satisfy the `info!` exclamation
|
||||
that upstream relied on.
|
||||
Reference in New Issue
Block a user