expediate 1.0.5 → 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.
- package/CHANGELOG.md +138 -0
- package/CONTRIBUTING.md +150 -0
- package/README.md +278 -779
- package/dist/apis.d.ts +372 -12
- package/dist/apis.d.ts.map +1 -1
- package/dist/apis.js +483 -65
- package/dist/apis.js.map +1 -1
- package/dist/cjs/index.js +2290 -807
- package/dist/git.d.ts +1 -1
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +5 -5
- package/dist/git.js.map +1 -1
- package/dist/http-objects.d.ts +26 -0
- package/dist/http-objects.d.ts.map +1 -0
- package/dist/http-objects.js +588 -0
- package/dist/http-objects.js.map +1 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/jwt-auth.d.ts +11 -0
- package/dist/jwt-auth.d.ts.map +1 -1
- package/dist/jwt-auth.js +9 -9
- package/dist/jwt-auth.js.map +1 -1
- package/dist/middleware.js +2 -2
- package/dist/middleware.js.map +1 -1
- package/dist/mimetypes.json +882 -1
- package/dist/misc.d.ts +161 -25
- package/dist/misc.d.ts.map +1 -1
- package/dist/misc.js +228 -80
- package/dist/misc.js.map +1 -1
- package/dist/openapi.d.ts +156 -13
- package/dist/openapi.d.ts.map +1 -1
- package/dist/openapi.js +214 -71
- package/dist/openapi.js.map +1 -1
- package/dist/router-types.d.ts +760 -0
- package/dist/router-types.d.ts.map +1 -0
- package/dist/router-types.js +23 -0
- package/dist/router-types.js.map +1 -0
- package/dist/router.d.ts +7 -530
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +128 -375
- package/dist/router.js.map +1 -1
- package/dist/static.d.ts +2 -2
- package/dist/static.d.ts.map +1 -1
- package/dist/static.js +77 -22
- package/dist/static.js.map +1 -1
- package/docs/THREAT_MODEL.md +52 -0
- package/docs/api-builder-v2-design.md +644 -0
- package/docs/api-builder-v3-design.md +397 -0
- package/docs/api-builder.md +454 -0
- package/docs/benchmark.md +27 -0
- package/docs/body-parsing.md +223 -0
- package/docs/errors.md +359 -0
- package/docs/expediate.png +0 -0
- package/docs/git.md +139 -0
- package/docs/jwt-auth.md +251 -0
- package/docs/logo.svg +12 -0
- package/docs/middleware.md +264 -0
- package/docs/openapi.md +180 -0
- package/docs/router.md +356 -0
- package/docs/static.md +128 -0
- package/docs/wiki.json +123 -0
- package/package.json +30 -8
- package/dist/cjs/apis.js +0 -327
- package/dist/cjs/git.js +0 -293
- package/dist/cjs/jwt-auth.js +0 -532
- package/dist/cjs/middleware.js +0 -511
- package/dist/cjs/mimetypes.json +0 -1
- package/dist/cjs/misc.js +0 -787
- package/dist/cjs/openapi.js +0 -485
- package/dist/cjs/router.js +0 -898
- package/dist/cjs/static.js +0 -669
package/dist/apis.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { RouterRequest, RouterResponse, Router } from './router.js';
|
|
1
|
+
import type { RouterRequest, RouterResponse, Router, Middleware } from './router.js';
|
|
2
2
|
import './openapi.js';
|
|
3
|
-
import type { SpecOptions, SpecFormat, OpenApiDocument } from './openapi.js';
|
|
3
|
+
import type { SpecOptions, SpecFormat, OpenApiDocument, OperationMeta, JsonSchema } from './openapi.js';
|
|
4
4
|
/**
|
|
5
5
|
* Context object passed as the first argument to every service method handler.
|
|
6
6
|
*
|
|
@@ -12,14 +12,19 @@ import type { SpecOptions, SpecFormat, OpenApiDocument } from './openapi.js';
|
|
|
12
12
|
* ```ts
|
|
13
13
|
* GET: {
|
|
14
14
|
* '/items/:id': function (ctx: ApiContext) {
|
|
15
|
-
* const id = ctx.
|
|
15
|
+
* const id = ctx.params.id; // ':id' from the path pattern
|
|
16
16
|
* const fmt = ctx.query.url.format; // '?format=json' from the query string
|
|
17
17
|
* return getItem(id, fmt);
|
|
18
18
|
* },
|
|
19
19
|
* }
|
|
20
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.
|
|
21
26
|
*/
|
|
22
|
-
export interface ApiContext {
|
|
27
|
+
export interface ApiContext<TUser = unknown, TState = Record<string, unknown>> {
|
|
23
28
|
/** Route and URL query parameters, separated by origin. */
|
|
24
29
|
query: {
|
|
25
30
|
/**
|
|
@@ -37,6 +42,12 @@ export interface ApiContext {
|
|
|
37
42
|
*/
|
|
38
43
|
url: Record<string, string | string[]>;
|
|
39
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>;
|
|
40
51
|
/**
|
|
41
52
|
* The request path as seen by this API router, after any prefix stripping
|
|
42
53
|
* performed by a parent `use()` mount.
|
|
@@ -49,7 +60,15 @@ export interface ApiContext {
|
|
|
49
60
|
* `req.user` before the service method is called. `undefined` when no
|
|
50
61
|
* authentication data has been attached.
|
|
51
62
|
*/
|
|
52
|
-
user?:
|
|
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;
|
|
53
72
|
}
|
|
54
73
|
/**
|
|
55
74
|
* An API error thrown (or rejected) by a service method.
|
|
@@ -101,16 +120,182 @@ export type ServiceInstance = Record<string, unknown> & {
|
|
|
101
120
|
* Methods declared here are copied onto the instance object, bound to `this`,
|
|
102
121
|
* so they can call each other and read/write instance state naturally.
|
|
103
122
|
*/
|
|
104
|
-
export type ServiceMethods<TInstance extends ServiceInstance = ServiceInstance> =
|
|
105
|
-
[name: string]: (this: TInstance, ...args: unknown[]) => unknown;
|
|
106
|
-
};
|
|
123
|
+
export type ServiceMethods<TInstance extends ServiceInstance = ServiceInstance> = Record<string, (this: TInstance, ...args: unknown[]) => unknown>;
|
|
107
124
|
/**
|
|
108
125
|
* A route map: keys are Express-style path patterns, values are handler
|
|
109
126
|
* functions that are invoked with `this` bound to the service instance.
|
|
110
127
|
*/
|
|
111
|
-
export type RouteMap<TInstance extends ServiceInstance = ServiceInstance> =
|
|
112
|
-
|
|
113
|
-
|
|
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
|
+
}
|
|
114
299
|
/**
|
|
115
300
|
* Extra methods attached to the router returned by {@link apiBuilder}.
|
|
116
301
|
*
|
|
@@ -219,6 +404,65 @@ export interface ServiceDefinition<TInstance extends ServiceInstance = ServiceIn
|
|
|
219
404
|
* objects to trigger HTTP error responses.
|
|
220
405
|
*/
|
|
221
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;
|
|
222
466
|
/** Route handlers for `GET` requests. */
|
|
223
467
|
GET?: RouteMap<TInstance>;
|
|
224
468
|
/** Route handlers for `POST` requests. */
|
|
@@ -230,6 +474,112 @@ export interface ServiceDefinition<TInstance extends ServiceInstance = ServiceIn
|
|
|
230
474
|
/** Route handlers for `PATCH` requests. */
|
|
231
475
|
PATCH?: RouteMap<TInstance>;
|
|
232
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>;
|
|
233
583
|
/**
|
|
234
584
|
* Build an Express-compatible router from a service definition object.
|
|
235
585
|
*
|
|
@@ -275,9 +625,19 @@ export interface ServiceDefinition<TInstance extends ServiceInstance = ServiceIn
|
|
|
275
625
|
* as a JSON body.
|
|
276
626
|
* - Any other thrown value produces `500 Internal Server Error`.
|
|
277
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
|
+
*
|
|
278
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.
|
|
279
639
|
* @returns A router instance pre-configured with all declared routes.
|
|
280
640
|
*/
|
|
281
|
-
export declare function apiBuilder<TInstance extends ServiceInstance = ServiceInstance>(service: ServiceDefinition<TInstance
|
|
641
|
+
export declare function apiBuilder<TInstance extends ServiceInstance = ServiceInstance>(service: ServiceDefinition<TInstance>, options?: ApiBuilderOptions): ApiRouter;
|
|
282
642
|
export default apiBuilder;
|
|
283
643
|
//# sourceMappingURL=apis.d.ts.map
|
package/dist/apis.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apis.d.ts","sourceRoot":"","sources":["../src/apis.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"apis.d.ts","sourceRoot":"","sources":["../src/apis.ts"],"names":[],"mappings":"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"}
|