expediate 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/CONTRIBUTING.md +150 -0
  3. package/LICENSE +16 -16
  4. package/README.md +330 -444
  5. package/dist/apis.d.ts +504 -27
  6. package/dist/apis.d.ts.map +1 -1
  7. package/dist/apis.js +618 -107
  8. package/dist/apis.js.map +1 -1
  9. package/dist/cjs/index.js +4066 -0
  10. package/dist/cjs/package.json +1 -0
  11. package/dist/git.d.ts +72 -9
  12. package/dist/git.d.ts.map +1 -1
  13. package/dist/git.js +129 -74
  14. package/dist/git.js.map +1 -1
  15. package/dist/http-objects.d.ts +26 -0
  16. package/dist/http-objects.d.ts.map +1 -0
  17. package/dist/http-objects.js +588 -0
  18. package/dist/http-objects.js.map +1 -0
  19. package/dist/index.d.ts +18 -13
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +15 -24
  22. package/dist/index.js.map +1 -1
  23. package/dist/jwt-auth.d.ts +158 -57
  24. package/dist/jwt-auth.d.ts.map +1 -1
  25. package/dist/jwt-auth.js +447 -207
  26. package/dist/jwt-auth.js.map +1 -1
  27. package/dist/middleware.d.ts +476 -0
  28. package/dist/middleware.d.ts.map +1 -0
  29. package/dist/middleware.js +647 -0
  30. package/dist/middleware.js.map +1 -0
  31. package/dist/mimetypes.json +882 -1
  32. package/dist/misc.d.ts +268 -25
  33. package/dist/misc.d.ts.map +1 -1
  34. package/dist/misc.js +449 -168
  35. package/dist/misc.js.map +1 -1
  36. package/dist/openapi.d.ts +433 -0
  37. package/dist/openapi.d.ts.map +1 -0
  38. package/dist/openapi.js +624 -0
  39. package/dist/openapi.js.map +1 -0
  40. package/dist/router-types.d.ts +760 -0
  41. package/dist/router-types.d.ts.map +1 -0
  42. package/dist/router-types.js +23 -0
  43. package/dist/router-types.js.map +1 -0
  44. package/dist/router.d.ts +37 -201
  45. package/dist/router.d.ts.map +1 -1
  46. package/dist/router.js +502 -244
  47. package/dist/router.js.map +1 -1
  48. package/dist/static.d.ts +3 -3
  49. package/dist/static.d.ts.map +1 -1
  50. package/dist/static.js +164 -105
  51. package/dist/static.js.map +1 -1
  52. package/docs/THREAT_MODEL.md +52 -0
  53. package/docs/api-builder-v2-design.md +644 -0
  54. package/docs/api-builder-v3-design.md +397 -0
  55. package/docs/api-builder.md +454 -0
  56. package/docs/benchmark.md +27 -0
  57. package/docs/body-parsing.md +223 -0
  58. package/docs/errors.md +359 -0
  59. package/docs/expediate.png +0 -0
  60. package/docs/git.md +139 -0
  61. package/docs/jwt-auth.md +251 -0
  62. package/docs/logo.svg +12 -0
  63. package/docs/middleware.md +264 -0
  64. package/docs/openapi.md +180 -0
  65. package/docs/router.md +356 -0
  66. package/docs/static.md +128 -0
  67. package/docs/wiki.json +123 -0
  68. package/package.json +47 -8
  69. package/.npmignore +0 -16
package/dist/apis.d.ts CHANGED
@@ -1,16 +1,87 @@
1
- import type { RouterRequest, Router } from './router.js';
1
+ import type { RouterRequest, RouterResponse, Router, Middleware } from './router.js';
2
+ import './openapi.js';
3
+ import type { SpecOptions, SpecFormat, OpenApiDocument, OperationMeta, JsonSchema } from './openapi.js';
4
+ /**
5
+ * Context object passed as the first argument to every service method handler.
6
+ *
7
+ * Replaces the old flat `params` map: route parameters, URL query parameters,
8
+ * and other request-scoped data are now namespaced to avoid collisions and to
9
+ * make each data source explicit.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * GET: {
14
+ * '/items/:id': function (ctx: ApiContext) {
15
+ * const id = ctx.params.id; // ':id' from the path pattern
16
+ * const fmt = ctx.query.url.format; // '?format=json' from the query string
17
+ * return getItem(id, fmt);
18
+ * },
19
+ * }
20
+ * ```
21
+ *
22
+ * @template TUser - The shape of the authenticated user payload (e.g.
23
+ * `TokenPayload` from the JWT plugin). Defaults to
24
+ * `unknown`, forcing explicit typing for safe access.
25
+ * @template TState - The shape of the guard-produced `state` bag.
26
+ */
27
+ export interface ApiContext<TUser = unknown, TState = Record<string, unknown>> {
28
+ /** Route and URL query parameters, separated by origin. */
29
+ query: {
30
+ /**
31
+ * Named parameters captured from the route pattern.
32
+ *
33
+ * For example, a route registered as `/items/:id` matched against
34
+ * `/items/42` produces `{ id: '42' }`.
35
+ */
36
+ route: Record<string, string>;
37
+ /**
38
+ * Parameters decoded from the URL query string.
39
+ *
40
+ * Repeated keys are preserved as string arrays.
41
+ * For example, `?q=hello&tag=a&tag=b` → `{ q: 'hello', tag: ['a', 'b'] }`.
42
+ */
43
+ url: Record<string, string | string[]>;
44
+ };
45
+ /**
46
+ * Shorthand alias for {@link ApiContext.query}.route — the named parameters
47
+ * captured from the route pattern. This is the dominant access pattern;
48
+ * the namespaced form remains available for collision cases.
49
+ */
50
+ params: Record<string, string>;
51
+ /**
52
+ * The request path as seen by this API router, after any prefix stripping
53
+ * performed by a parent `use()` mount.
54
+ */
55
+ path: string;
56
+ /**
57
+ * Authentication data attached to the request, if present.
58
+ *
59
+ * Populated when a middleware (e.g. a JWT `authenticate` middleware) sets
60
+ * `req.user` before the service method is called. `undefined` when no
61
+ * authentication data has been attached.
62
+ */
63
+ user?: TUser;
64
+ /**
65
+ * Values produced by guards (loaded resources, resolved roles, …).
66
+ *
67
+ * Each guard that returns an object has that object shallow-merged into
68
+ * this bag before the next guard (or the handler) runs. Starts as `{}`
69
+ * for every request.
70
+ */
71
+ state: TState;
72
+ }
2
73
  /**
3
74
  * An API error thrown (or rejected) by a service method.
4
75
  *
5
76
  * When a service method throws or rejects with an object of this shape, the
6
77
  * framework translates it into an HTTP error response automatically:
7
- * - `httpStatus` → HTTP status code (defaults to `500`).
78
+ * - `status` → HTTP status code (defaults to `500`).
8
79
  * - `data` → JSON-serialised response body (takes precedence over `message`).
9
80
  * - `message` → Plain-text response body.
10
81
  */
11
82
  export interface ApiError {
12
83
  /** HTTP status code to send (e.g. `404`, `503`). Defaults to `500`. */
13
- httpStatus?: number;
84
+ status?: number;
14
85
  /** Structured error payload; serialised to JSON when present. */
15
86
  data?: unknown;
16
87
  /** Human-readable error message used when `data` is absent. */
@@ -21,21 +92,23 @@ export interface ApiError {
21
92
  *
22
93
  * Called with `this` bound to the current service instance.
23
94
  *
24
- * @param params - Route parameters extracted from the URL (e.g. `{ uid: '42' }`),
25
- * merged with URL query-string parameters.
26
- * @param body - Parsed request body (populated by a body-parsing middleware
27
- * such as `json()`).
95
+ * @param ctx - Request context containing route parameters, URL query
96
+ * parameters, the request path, and optional auth data.
97
+ * See {@link ApiContext}.
98
+ * @param body - Parsed request body (populated by a body-parsing middleware
99
+ * such as `json()`).
28
100
  * @returns The value to send as the JSON response body, a `Promise` of the
29
101
  * same, or `undefined` / `null` / any falsy value to send **201 No
30
102
  * Content** (useful for mutations that produce no response body).
31
103
  */
32
- export type ServiceMethod<TInstance = ServiceInstance> = (this: TInstance, params: Record<string, string>, body?: unknown) => unknown | Promise<unknown>;
104
+ export type ServiceMethod<TInstance = ServiceInstance, TResponse = any, TBody = any> = (this: TInstance, ctx: ApiContext, body?: TBody) => TResponse | Promise<TResponse>;
33
105
  /**
34
106
  * The runtime state object that backs a service instance.
35
107
  *
36
- * Produced by `service.data()` and extended with the methods from
37
- * `service.methods` before `service.setup()` is called. A hidden `$key`
38
- * property carries the scope key when `data()` is not supplied.
108
+ * Produced by `service.data()` (which returns `Partial<TInstance>`) and
109
+ * extended with the methods from `service.methods` before `service.setup()`
110
+ * is called. A hidden `$key` property carries the scope key when `data()` is
111
+ * not supplied.
39
112
  */
40
113
  export type ServiceInstance = Record<string, unknown> & {
41
114
  /** The scope key used to identify this instance (set by the framework). */
@@ -47,16 +120,222 @@ export type ServiceInstance = Record<string, unknown> & {
47
120
  * Methods declared here are copied onto the instance object, bound to `this`,
48
121
  * so they can call each other and read/write instance state naturally.
49
122
  */
50
- export type ServiceMethods<TInstance extends ServiceInstance = ServiceInstance> = {
51
- [name: string]: (this: TInstance, ...args: unknown[]) => unknown;
52
- };
123
+ export type ServiceMethods<TInstance extends ServiceInstance = ServiceInstance> = Record<string, (this: TInstance, ...args: unknown[]) => unknown>;
53
124
  /**
54
125
  * A route map: keys are Express-style path patterns, values are handler
55
126
  * functions that are invoked with `this` bound to the service instance.
56
127
  */
57
- export type RouteMap<TInstance extends ServiceInstance = ServiceInstance> = {
58
- [path: string]: ServiceMethod<TInstance>;
59
- };
128
+ export type RouteMap<TInstance extends ServiceInstance = ServiceInstance> = Record<string, ServiceMethod<TInstance>>;
129
+ /**
130
+ * A pre-handler hook running in the `ctx` world.
131
+ *
132
+ * Guards attach at three levels — API (`ServiceDefinition.guards`),
133
+ * controller (`ControllerDefinition.guards`), and route
134
+ * (`OperationMeta.guards` via `describe()`) — and run outermost-first:
135
+ *
136
+ * ```
137
+ * auth.authenticate → auth.check → api guards → controller guards → route guards → handler
138
+ * ```
139
+ *
140
+ * A guard may:
141
+ * - **throw / reject** an {@link ApiError} → translated to an HTTP error response;
142
+ * - **return an object** → shallow-merged into `ctx.state`;
143
+ * - **return void** → pure check.
144
+ *
145
+ * Guards are loosely typed on purpose (`ctx.user` is `any` here); declare
146
+ * `ApiContext<TUser, TState>` explicitly in handlers for strict typing.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * const requireAdmin: Guard = (ctx) => {
151
+ * if (!ctx.user?.isAdmin) throw { status: 403, message: 'Admin access required' };
152
+ * return { admin: true }; // → ctx.state.admin
153
+ * };
154
+ * ```
155
+ */
156
+ export type Guard = (ctx: ApiContext<any, Record<string, unknown>>, req: RouterRequest) => void | Record<string, unknown> | Promise<void | Record<string, unknown>>;
157
+ /**
158
+ * A group of routes sharing a path prefix, default OpenAPI tags, guards, and
159
+ * a default permission requirement.
160
+ *
161
+ * Controllers are *route organisation*, not isolation boundaries: handlers in
162
+ * every controller run with `this` bound to the same service instance (the
163
+ * instance lifecycle — `scope` / `data` / `setup` / `methods` — stays at the
164
+ * {@link ServiceDefinition} level).
165
+ *
166
+ * @template TInstance - The shape of the service's state object.
167
+ */
168
+ export interface ControllerDefinition<TInstance extends ServiceInstance = ServiceInstance> {
169
+ /** Path prefix prepended to every route in this controller (may contain params). */
170
+ prefix?: string;
171
+ /** Default OpenAPI tags applied to routes that do not declare their own. */
172
+ tags?: string[];
173
+ /** Guards run before every handler of this controller (see {@link Guard}). */
174
+ guards?: Guard[];
175
+ /**
176
+ * Default permission requirement for every route of this controller.
177
+ * Route-level `OperationMeta.permission` overrides it. When set, the
178
+ * pipeline runs `auth.check(ctx, required)` before the guards.
179
+ */
180
+ permission?: string | string[];
181
+ /** Route handlers for `GET` requests (paths relative to `prefix`). */
182
+ GET?: RouteMap<TInstance>;
183
+ /** Route handlers for `POST` requests (paths relative to `prefix`). */
184
+ POST?: RouteMap<TInstance>;
185
+ /** Route handlers for `PUT` requests (paths relative to `prefix`). */
186
+ PUT?: RouteMap<TInstance>;
187
+ /** Route handlers for `DELETE` requests (paths relative to `prefix`). */
188
+ DELETE?: RouteMap<TInstance>;
189
+ /** Route handlers for `PATCH` requests (paths relative to `prefix`). */
190
+ PATCH?: RouteMap<TInstance>;
191
+ }
192
+ /**
193
+ * Identity helper for type inference and discoverability when declaring a
194
+ * {@link ControllerDefinition} in its own file.
195
+ *
196
+ * @example
197
+ * ```ts
198
+ * export const wikiController = defineController({
199
+ * prefix: '/p/:proj/wiki',
200
+ * tags: ['Wiki'],
201
+ * permission: 'wiki.read',
202
+ * GET: { '/tree': (ctx) => listPages(ctx.params.proj) },
203
+ * });
204
+ * ```
205
+ */
206
+ export declare function defineController<TInstance extends ServiceInstance = ServiceInstance>(c: ControllerDefinition<TInstance>): ControllerDefinition<TInstance>;
207
+ /**
208
+ * Authentication / authorization binding connecting an auth layer (typically
209
+ * the JWT plugin) to the API Builder pipeline.
210
+ *
211
+ * @example
212
+ * ```ts
213
+ * const jwt = createJwtPlugin({ accessTokenSecret: SECRET });
214
+ * const api = apiBuilder({
215
+ * auth: { authenticate: jwt.authenticate }, // default check() reads ctx.user.permissions
216
+ * controllers: [ ... ],
217
+ * });
218
+ * ```
219
+ *
220
+ * @template TUser - The shape of the authenticated user payload.
221
+ */
222
+ export interface AuthBinding<TUser = unknown> {
223
+ /**
224
+ * Router middleware run before any guard or handler — typically
225
+ * `jwtPlugin.authenticate`. Registered by `apiBuilder` on its internal
226
+ * router, so the client no longer wires it per-mount.
227
+ */
228
+ authenticate?: Middleware;
229
+ /**
230
+ * Enforce a permission requirement for the current request.
231
+ *
232
+ * Default implementation: require `ctx.user` (else `401`) and check that
233
+ * `ctx.user.permissions` contains **all** required entries (else `403`) —
234
+ * i.e. the exact semantics of `jwtPlugin.requirePermission`, but in the
235
+ * `ctx` world. Override for resource-scoped models (per-project roles,
236
+ * ownership, …); the override may load resources and share them through
237
+ * `ctx.state`.
238
+ *
239
+ * Failure is signalled by throwing / rejecting an {@link ApiError}.
240
+ */
241
+ check?: (ctx: ApiContext<TUser>, required: string[]) => void | Promise<void>;
242
+ /**
243
+ * OpenAPI security scheme emitted into `components.securitySchemes.bearerAuth`
244
+ * when at least one route declares a `permission`.
245
+ *
246
+ * Default: `{ type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }`.
247
+ */
248
+ scheme?: Record<string, unknown>;
249
+ /**
250
+ * Name of the vendor extension listing the required permissions on each
251
+ * secured operation in the generated OpenAPI document.
252
+ *
253
+ * Default: `'x-required-permissions'`.
254
+ */
255
+ permissionsExtension?: string;
256
+ }
257
+ /**
258
+ * Validation controls for {@link apiBuilder}.
259
+ *
260
+ * Used both as the factory's optional second argument and as the object form of
261
+ * the {@link ServiceDefinition.validate} field. When passed as the second
262
+ * argument it is authoritative and overrides `service.validate`:
263
+ *
264
+ * ```ts
265
+ * apiBuilder(service); // follows service.validate
266
+ * apiBuilder(service, {}); // validate requests (default), not responses
267
+ * apiBuilder(service, { validateRequests: false }); // validate nothing
268
+ * apiBuilder(service, { validateResponses: true }); // requests + responses (500 on mismatch)
269
+ * apiBuilder(service, { validateResponses: 'warn' }); // requests + responses (log only, no 500)
270
+ * ```
271
+ */
272
+ export interface ApiBuilderOptions {
273
+ /**
274
+ * Validate incoming request bodies against each route's declared
275
+ * `OperationMeta.requestBody` schema. Failures produce `400` with
276
+ * `{ message, fieldErrors }`.
277
+ *
278
+ * @default true — pass `false` to cancel the incoming-data check.
279
+ */
280
+ validateRequests?: boolean;
281
+ /**
282
+ * Validate each handler's return value against the route's declared
283
+ * `OperationMeta.responses['200']` schema before it is sent.
284
+ *
285
+ * - `true` — a mismatch is a server-contract breach: the off-spec body is
286
+ * **not** sent; instead a `500` with `{ message, fieldErrors }` is returned.
287
+ * - `'warn'` — a mismatch is logged server-side via `console.warn` and the
288
+ * response is sent unchanged (handy in development).
289
+ * - `false` — no response checking.
290
+ *
291
+ * Only truthy returns (sent as `200` JSON) are checked; falsy returns
292
+ * (`201 No Content`) and routes without a declared `200` response schema are
293
+ * skipped.
294
+ *
295
+ * @default false
296
+ */
297
+ validateResponses?: boolean | 'warn';
298
+ }
299
+ /**
300
+ * Extra methods attached to the router returned by {@link apiBuilder}.
301
+ *
302
+ * These allow the service's route definitions to be introspected and
303
+ * serialised as an OpenAPI 3.1.0 document.
304
+ */
305
+ export interface ApiRouterExtensions {
306
+ /**
307
+ * Generate an OpenAPI 3.1.0 document from the service definition.
308
+ *
309
+ * @param opts - Top-level spec options (title, version, basePath, …).
310
+ * @returns A plain-object OpenAPI document ready to be serialised with
311
+ * `JSON.stringify`.
312
+ */
313
+ spec(opts: SpecOptions): OpenApiDocument;
314
+ /**
315
+ * Return a request handler that serves the OpenAPI spec on `GET`.
316
+ *
317
+ * The spec is generated once and cached as a serialised string on the first
318
+ * call to the returned handler.
319
+ *
320
+ * ```ts
321
+ * // JSON (default)
322
+ * app.get('/openapi.json', itemsApi.specHandler({ title: 'Items API', version: '1.0.0' }));
323
+ *
324
+ * // YAML
325
+ * app.get('/openapi.yaml', itemsApi.specHandler({ title: 'Items API', version: '1.0.0' }, 'yaml'));
326
+ * ```
327
+ *
328
+ * @param opts - Top-level spec options (title, version, basePath, …).
329
+ * @param format - Output format: `'json'` (default) or `'yaml'`.
330
+ * @returns An Express-compatible middleware handler.
331
+ */
332
+ specHandler(opts: SpecOptions, format?: SpecFormat): (req: RouterRequest, res: RouterResponse) => void;
333
+ }
334
+ /**
335
+ * The return type of {@link apiBuilder}: a standard {@link Router} augmented
336
+ * with OpenAPI introspection helpers ({@link ApiRouterExtensions}).
337
+ */
338
+ export type ApiRouter = Router & ApiRouterExtensions;
60
339
  /**
61
340
  * A service definition object — the single argument to {@link apiBuilder}.
62
341
  *
@@ -90,7 +369,12 @@ export interface ServiceDefinition<TInstance extends ServiceInstance = ServiceIn
90
369
  */
91
370
  scope?: (req: RouterRequest) => string | null;
92
371
  /**
93
- * Factory that returns the initial state object for a new instance.
372
+ * Factory that returns the **initial data** portion of a new instance.
373
+ *
374
+ * The return type is `Partial<TInstance>` because the methods declared in
375
+ * {@link ServiceDefinition.methods} are mixed in *after* `data()` is called,
376
+ * completing the instance shape. By the time any route handler or
377
+ * `setup()` runs the instance is fully initialised.
94
378
  *
95
379
  * When omitted, the instance is initialised as `{ $key: key }`.
96
380
  *
@@ -98,14 +382,18 @@ export interface ServiceDefinition<TInstance extends ServiceInstance = ServiceIn
98
382
  * global instances, `null` for ephemeral ones, or the string
99
383
  * returned by `scope()`).
100
384
  */
101
- data?: (key: string | null) => TInstance;
385
+ data?: (key: string | null) => Partial<TInstance>;
102
386
  /**
103
387
  * Lifecycle hook called once after an instance is created and its methods
104
388
  * are mixed in.
105
389
  *
106
- * May be synchronous or asynchronous. When asynchronous, the returned
107
- * `Promise` is not awaited by the framework use a `throwIfNotReady()`
108
- * pattern in your methods to guard against premature access.
390
+ * May be synchronous or **asynchronous**. When asynchronous, the framework
391
+ * now awaits the returned `Promise` before marking the instance as ready:
392
+ * - For **singleton** services, route handlers are only registered (and
393
+ * requests served) once setup resolves. Requests that arrive while setup
394
+ * is in progress receive **503 Service not ready**.
395
+ * - For **keyed / ephemeral** services, the route handler awaits instance
396
+ * creation (including setup) per request before invoking the method.
109
397
  */
110
398
  setup?: (this: TInstance) => void | Promise<void>;
111
399
  /**
@@ -116,6 +404,65 @@ export interface ServiceDefinition<TInstance extends ServiceInstance = ServiceIn
116
404
  * objects to trigger HTTP error responses.
117
405
  */
118
406
  methods?: ServiceMethods<TInstance>;
407
+ /**
408
+ * Sub-controllers merged into this API.
409
+ *
410
+ * Each controller's routes are rewritten to `joinPath(prefix, path)`, then
411
+ * all routes of all controllers (plus the root-level route maps below) are
412
+ * concatenated and sorted by specificity **globally**. A duplicate
413
+ * `(verb, joined path)` pair across controllers **throws at build time**.
414
+ *
415
+ * All controllers share the single service instance lifecycle declared at
416
+ * this level — controllers organise routes, they do not isolate state.
417
+ */
418
+ controllers?: ControllerDefinition<TInstance>[];
419
+ /** Guards run before every handler of the whole API (see {@link Guard}). */
420
+ guards?: Guard[];
421
+ /** Authentication / authorization binding (see {@link AuthBinding}). */
422
+ auth?: AuthBinding;
423
+ /**
424
+ * Enable runtime validation of declared schemas.
425
+ *
426
+ * - `true` — validate request bodies against each route's
427
+ * `OperationMeta.requestBody` schema (`400` with `{ message, fieldErrors }`
428
+ * on failure).
429
+ * - An {@link ApiBuilderOptions} object — fine-grained control over request
430
+ * and response validation.
431
+ *
432
+ * Overridden when an {@link ApiBuilderOptions} object is passed as the second
433
+ * argument to {@link apiBuilder}.
434
+ */
435
+ validate?: boolean | ApiBuilderOptions;
436
+ /**
437
+ * Reusable JSON Schema components, shared by request validation and spec
438
+ * generation. `$ref: '#/components/schemas/Name'` references in operation
439
+ * metadata are resolved against this map by the validator, and the map is
440
+ * merged into `components.schemas` of the generated OpenAPI document
441
+ * (taking precedence over `SpecOptions.schemas`).
442
+ */
443
+ schemas?: Record<string, JsonSchema>;
444
+ /**
445
+ * Hook invoked whenever a handler, guard, auth check, or validation step
446
+ * throws or rejects — before the default {@link ApiError} → HTTP translation.
447
+ *
448
+ * Use it to log the failure and/or shape a better response:
449
+ * - **Return nothing** (`undefined`) → the error is left untouched and the
450
+ * built-in translation runs (`{ status, message | data }`, else `500`).
451
+ * Ideal for log-only use.
452
+ * - **Return an {@link ApiError}** → that value is sent instead of the
453
+ * original (e.g. to hide internals behind a generic message, or attach a
454
+ * correlation id).
455
+ * - **Throw** → the thrown value is escalated to the surrounding app's error
456
+ * channel (`router.error()` / `onError`) instead of being answered here,
457
+ * letting a process-wide handler take over.
458
+ *
459
+ * @param err - The caught value (thrown or rejected).
460
+ * @param ctx - The {@link ApiContext} for the failing request.
461
+ * @param req - The underlying request.
462
+ * @returns An {@link ApiError} to override the response, or nothing to keep
463
+ * the default translation.
464
+ */
465
+ onError?(err: unknown, ctx: ApiContext<any>, req: RouterRequest): void | ApiError;
119
466
  /** Route handlers for `GET` requests. */
120
467
  GET?: RouteMap<TInstance>;
121
468
  /** Route handlers for `POST` requests. */
@@ -127,6 +474,112 @@ export interface ServiceDefinition<TInstance extends ServiceInstance = ServiceIn
127
474
  /** Route handlers for `PATCH` requests. */
128
475
  PATCH?: RouteMap<TInstance>;
129
476
  }
477
+ /** The five HTTP verbs supported by `apiBuilder`. */
478
+ declare const VERBS: readonly ["GET", "POST", "PUT", "DELETE", "PATCH"];
479
+ /** One of the five HTTP verbs supported by `apiBuilder`. */
480
+ export type ApiVerb = typeof VERBS[number];
481
+ /**
482
+ * One merged route entry produced by {@link collectRoutes}.
483
+ *
484
+ * Records per-route provenance — effective tags, composed guard chain, and
485
+ * permission requirement — consumed by both the request pipeline
486
+ * (`apiBuilder`) and the spec generator (`openApiSpec`).
487
+ *
488
+ * @template TInstance - The shape of the service's state object.
489
+ */
490
+ export interface CollectedRoute<TInstance extends ServiceInstance = ServiceInstance> {
491
+ /** HTTP verb of the route. */
492
+ verb: ApiVerb;
493
+ /** Full path after joining the controller prefix (Express-style pattern). */
494
+ path: string;
495
+ /** The route handler (possibly `describe()`-wrapped). */
496
+ handler: ServiceMethod<TInstance>;
497
+ /** Operation metadata attached via `describe()`, when present. */
498
+ meta?: OperationMeta;
499
+ /** Effective tags: route-level `meta.tags`, else the controller's `tags`. */
500
+ tags?: string[];
501
+ /** Composed guard chain: API guards, then controller guards, then route guards. */
502
+ guards: Guard[];
503
+ /** Normalised permission requirement (route-level overrides controller-level). */
504
+ permission?: string[];
505
+ /** Human-readable controller identifier used in diagnostics. */
506
+ controller: string;
507
+ }
508
+ /**
509
+ * Join a controller prefix and a route path into a single normalised pattern.
510
+ *
511
+ * Duplicate slashes are collapsed and a trailing slash is stripped (except
512
+ * for the root path), so `joinPath('/p/:proj/wiki', '/')` → `'/p/:proj/wiki'`.
513
+ *
514
+ * Exported for use by `openapi.ts` (multi-definition spec merging); not part
515
+ * of the public package API.
516
+ *
517
+ * @param prefix - The controller prefix (may be empty).
518
+ * @param path - The route path relative to the prefix.
519
+ */
520
+ export declare function joinPath(prefix: string, path: string): string;
521
+ /**
522
+ * Compute the specificity score of a route pattern.
523
+ *
524
+ * `score = (segment count × 100) − (parameter count × 10)`. Higher scores
525
+ * are registered first so that more precise patterns (more segments, fewer
526
+ * parameters) cannot be shadowed by prefix matches.
527
+ *
528
+ * Exported for use by `openapi.ts` (multi-definition spec merging); not part
529
+ * of the public package API.
530
+ */
531
+ export declare function routeScore(path: string): number;
532
+ /**
533
+ * Normalise a `permission` declaration (`string | string[]`) to an array,
534
+ * or `undefined` when absent.
535
+ *
536
+ * Exported for use by `openapi.ts` (multi-definition spec merging); not part
537
+ * of the public package API.
538
+ */
539
+ export declare function normalizePermission(permission: string | string[] | undefined): string[] | undefined;
540
+ /**
541
+ * Build the merged route table for a service definition.
542
+ *
543
+ * The algorithm (see `docs/api-builder-v2-design.md` §4):
544
+ * 1. Normalises the root-level route maps into an anonymous controller
545
+ * (`prefix: ''`) so v1 single-definition services keep working.
546
+ * 2. Rewrites each controller route to `joinPath(prefix, path)`.
547
+ * 3. Concatenates all routes of all controllers, then sorts them by
548
+ * decreasing specificity **globally** (the score is computed on the
549
+ * joined path, so prefix parameters are accounted for).
550
+ * 4. **Throws** on a duplicate `(verb, joined path)` pair, naming both
551
+ * declaring controllers.
552
+ * 5. Records per-route provenance (tags, guards, permission) consumed by
553
+ * both the request pipeline and `openApiSpec()`.
554
+ *
555
+ * Exported for use by `openapi.ts`; not part of the public package API.
556
+ *
557
+ * @param service - The service definition to collect routes from.
558
+ * @returns The merged, globally sorted route table.
559
+ * @throws Error on duplicate `(verb, path)` declarations.
560
+ */
561
+ export declare function collectRoutes<TInstance extends ServiceInstance = ServiceInstance>(service: ServiceDefinition<TInstance>): CollectedRoute<TInstance>[];
562
+ /**
563
+ * Validate a value against a JSON Schema subset, collecting field errors.
564
+ *
565
+ * Supported keywords: `type`, `required`, `properties`, `items`, `enum`,
566
+ * `pattern`, `minLength` / `maxLength`, `minimum` / `maximum`,
567
+ * `additionalProperties`, `allOf` / `anyOf` / `oneOf`, and `$ref` (resolved
568
+ * against `components`, i.e. `ServiceDefinition.schemas`).
569
+ *
570
+ * Field-error paths are dotted (`name`, `address.city`, `tags.0`); errors on
571
+ * the value itself are keyed `'$'`.
572
+ *
573
+ * Exported for testing; not part of the public package API.
574
+ *
575
+ * @param value - The value to validate.
576
+ * @param schema - The schema to validate against.
577
+ * @param components - Reusable schemas for `$ref` resolution.
578
+ * @param path - Current field path (used in recursion; omit at the root).
579
+ * @param errors - Accumulator (used in recursion; omit at the root).
580
+ * @returns A map of field path → first error message (empty when valid).
581
+ */
582
+ export declare function validateSchema(value: unknown, schema: JsonSchema, components?: Record<string, JsonSchema>, path?: string, errors?: Record<string, string>): Record<string, string>;
130
583
  /**
131
584
  * Build an Express-compatible router from a service definition object.
132
585
  *
@@ -138,11 +591,25 @@ export interface ServiceDefinition<TInstance extends ServiceInstance = ServiceIn
138
591
  * app.use('/api', apiBuilder(myService));
139
592
  * ```
140
593
  *
594
+ * **Singleton setup lifecycle:**
595
+ * When `service.scope` is absent the service is a singleton. The framework
596
+ * pre-builds the instance eagerly and registers a **503 Service not ready**
597
+ * guard middleware on the router immediately. Route handlers are registered
598
+ * only once `setup()` resolves (or immediately, for sync setup). Any request
599
+ * that arrives before setup completes receives a 503 response.
600
+ *
601
+ * **Keyed / ephemeral setup lifecycle:**
602
+ * Route handlers are registered immediately. For each request,
603
+ * `resolveInstance()` is awaited inside the handler, so `setup()` is always
604
+ * complete before the service method is invoked.
605
+ *
141
606
  * **Route handlers** declared in `service.GET`, `service.POST`, etc. are
142
607
  * called with `this` bound to the service instance. They receive two
143
608
  * arguments:
144
- * 1. `params` merged route + query-string parameters from `req.params`.
145
- * 2. `body` — the parsed request body from `req.body` (requires a
609
+ * 1. `ctx` an {@link ApiContext} object containing `ctx.query.route`
610
+ * (named route parameters), `ctx.query.url` (URL query-string parameters),
611
+ * `ctx.path` (the request path), and `ctx.user` (optional auth data).
612
+ * 2. `body` — the parsed request body from `req.body` (requires a
146
613
  * body-parsing middleware such as `json()` to run first).
147
614
  *
148
615
  * **Return values:**
@@ -152,15 +619,25 @@ export interface ServiceDefinition<TInstance extends ServiceInstance = ServiceIn
152
619
  * `Promise` resolving to one → `201 No Content` (useful for mutations).
153
620
  *
154
621
  * **Error handling:**
155
- * - Throwing or rejecting with `{ httpStatus, message }` sends the
622
+ * - Throwing or rejecting with `{ status, message }` sends the
156
623
  * corresponding HTTP error.
157
- * - Throwing or rejecting with `{ httpStatus, data }` sends the `data` object
624
+ * - Throwing or rejecting with `{ status, data }` sends the `data` object
158
625
  * as a JSON body.
159
626
  * - Any other thrown value produces `500 Internal Server Error`.
160
627
  *
628
+ * **Validation:**
629
+ * - With no `options`, validation follows the {@link ServiceDefinition.validate}
630
+ * field.
631
+ * - With `options`, request validation defaults **on** (cancel via
632
+ * `{ validateRequests: false }`) and response validation can be enabled with
633
+ * `{ validateResponses: true }` (500 on mismatch) or `{ validateResponses:
634
+ * 'warn' }` (log only). See {@link ApiBuilderOptions}.
635
+ *
161
636
  * @param service - The service definition (see {@link ServiceDefinition}).
637
+ * @param options - Optional validation controls (see {@link ApiBuilderOptions}).
638
+ * When provided, it overrides the legacy `service.validate` field.
162
639
  * @returns A router instance pre-configured with all declared routes.
163
640
  */
164
- export declare function apiBuilder<TInstance extends ServiceInstance = ServiceInstance>(service: ServiceDefinition<TInstance>): Router;
641
+ export declare function apiBuilder<TInstance extends ServiceInstance = ServiceInstance>(service: ServiceDefinition<TInstance>, options?: ApiBuilderOptions): ApiRouter;
165
642
  export default apiBuilder;
166
643
  //# sourceMappingURL=apis.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"apis.d.ts","sourceRoot":"","sources":["../src/apis.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,EAAE,MAAM,aAAa,CAAC;AAMzE;;;;;;;;GAQG;AACH,MAAM,WAAW,QAAQ;IACvB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,aAAa,CAAC,SAAS,GAAG,eAAe,IAAI,CACvD,IAAI,EAAI,SAAS,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,IAAI,CAAC,EAAG,OAAO,KACZ,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACtD,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,cAAc,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe,IAAI;IAChF,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;CAClE,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,QAAQ,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe,IAAI;IAC1E,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;CAC1C,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,iBAAiB,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe;IACpF;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,MAAM,GAAG,IAAI,CAAC;IAE9C;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,SAAS,CAAC;IAEzC;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElD;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IAEpC,yCAAyC;IACzC,GAAG,CAAC,EAAK,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC7B,0CAA0C;IAC1C,IAAI,CAAC,EAAI,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC7B,yCAAyC;IACzC,GAAG,CAAC,EAAK,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC7B,4CAA4C;IAC5C,MAAM,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC7B,2CAA2C;IAC3C,KAAK,CAAC,EAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;CAC9B;AAyID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,UAAU,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe,EAC5E,OAAO,EAAE,iBAAiB,CAAC,SAAS,CAAC,GACpC,MAAM,CAmFR;AAED,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"apis.d.ts","sourceRoot":"","sources":["../src/apis.ts"],"names":[],"mappings":"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"}