@cleverbrush/server 3.1.0 → 4.1.0
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/README.md +150 -0
- package/dist/CacheTag.d.ts +76 -0
- package/dist/Endpoint.d.ts +169 -39
- package/dist/Server.d.ts +3 -0
- package/dist/chunk-KDTWU6PS.js +2 -0
- package/dist/chunk-KDTWU6PS.js.map +1 -0
- package/dist/contract.d.ts +106 -0
- package/dist/contract.js +1 -1
- package/dist/contract.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/middlewares/Idempotency.d.ts +52 -0
- package/dist/middlewares/ResponseCache.d.ts +50 -0
- package/dist/types.d.ts +45 -0
- package/package.json +6 -4
- package/dist/chunk-W3HQGZZH.js +0 -2
- package/dist/chunk-W3HQGZZH.js.map +0 -1
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ A schema-first HTTP server framework for Node.js. Combines [`@cleverbrush/schema
|
|
|
19
19
|
- **AsyncAPI-ready** — `getSubscriptionRegistrations()` exposes subscription metadata for `generateAsyncApiSpec()` in `@cleverbrush/server-openapi`.
|
|
20
20
|
- **Health check** — optional `/health` endpoint via `server.withHealthcheck()`.
|
|
21
21
|
- **WebSocket subscriptions** — `endpoint.subscription('/ws/path')` with typed incoming/outgoing schemas, `tracked()` events, and async generator handlers.
|
|
22
|
+
- **Contract composition** — `mergeContracts`, `pickGroups`, and `omitGroups` enable audience-scoped bundles: ship only the endpoints each consumer needs.
|
|
22
23
|
|
|
23
24
|
## Installation
|
|
24
25
|
|
|
@@ -123,6 +124,36 @@ const CreateUser = endpoint
|
|
|
123
124
|
.operationId('createUser');
|
|
124
125
|
```
|
|
125
126
|
|
|
127
|
+
### Cache Tags
|
|
128
|
+
|
|
129
|
+
Tag-based cache invalidation. Tags declared on endpoints flow to the
|
|
130
|
+
[`cacheTags` middleware](/client/cache-tags) for automatic HTTP caching and
|
|
131
|
+
invalidation on mutating requests.
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
const ListTodos = endpoint
|
|
135
|
+
.get('/api/todos')
|
|
136
|
+
.query(TodoListQuerySchema)
|
|
137
|
+
.cacheTag('todo-list', p => ({ page: p.query.page, limit: p.query.limit }))
|
|
138
|
+
.returns(array(TodoSchema));
|
|
139
|
+
|
|
140
|
+
const UpdateTodo = endpoint
|
|
141
|
+
.patch('/api/todos/:id')
|
|
142
|
+
.body(UpdateTodoBody)
|
|
143
|
+
.clearsCacheTag('todo-list') // clears the collection cache
|
|
144
|
+
.clearsCacheTag('todo', p => ({ id: p.params.id })) // clears specific entity
|
|
145
|
+
.returns(TodoSchema);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- **`.cacheTag(name)`** — declares the endpoint's data belongs to a cache
|
|
149
|
+
group. Use on GET endpoints.
|
|
150
|
+
- **`.clearsCacheTag(name)`** — declares that this mutation clears matching
|
|
151
|
+
cache entries on success. Use on POST / PUT / PATCH / DELETE.
|
|
152
|
+
- **`.cacheTag(name, p => ({ ... }))`** — property-based tag; each selected
|
|
153
|
+
property becomes part of the cache key (different pages → different entries).
|
|
154
|
+
- **Immutability** — both methods return a new builder; the original is
|
|
155
|
+
unchanged.
|
|
156
|
+
|
|
126
157
|
## Registering and Handling Endpoints
|
|
127
158
|
|
|
128
159
|
```ts
|
|
@@ -154,6 +185,48 @@ await server.listen(3000);
|
|
|
154
185
|
| `ActionResult.stream(readable, contentType)` | 200 | Pipes a `Readable` |
|
|
155
186
|
| `ActionResult.status(status)` | any | Bare status, no body |
|
|
156
187
|
|
|
188
|
+
## File Upload
|
|
189
|
+
|
|
190
|
+
Accept file uploads via `multipart/form-data` by chaining `.upload()` on an endpoint:
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
import { endpoint } from '@cleverbrush/server';
|
|
194
|
+
import { object, string } from '@cleverbrush/schema';
|
|
195
|
+
|
|
196
|
+
const UploadAvatar = endpoint
|
|
197
|
+
.post('/api/avatar')
|
|
198
|
+
.upload({ maxFileSize: 2 * 1024 * 1024, allowedMimeTypes: ['image/*'] })
|
|
199
|
+
.body(object({ description: string().optional() }))
|
|
200
|
+
.authorize(UserPrincipal);
|
|
201
|
+
|
|
202
|
+
const handler: Handler<typeof UploadAvatar> = async ({ body, files }) => {
|
|
203
|
+
const avatar = files['avatar'];
|
|
204
|
+
// avatar: FilePart { filename, mimeType, buffer, size }
|
|
205
|
+
return ActionResult.created({ name: avatar.filename });
|
|
206
|
+
};
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The `files` object on the handler context contains one `FilePart` entry per uploaded file field. Non-file form fields are validated against the body schema and available via `body`.
|
|
210
|
+
|
|
211
|
+
### Options
|
|
212
|
+
|
|
213
|
+
| Option | Type | Default | Description |
|
|
214
|
+
|--------|------|---------|-------------|
|
|
215
|
+
| `maxFileSize` | `number` | 10 MB | Maximum file size per file in bytes |
|
|
216
|
+
| `allowedMimeTypes` | `string[]` | all | MIME type allowlist (supports `image/*` glob) |
|
|
217
|
+
| `maxFileCount` | `number` | 10 | Maximum number of files per request |
|
|
218
|
+
|
|
219
|
+
### FilePart type
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
interface FilePart {
|
|
223
|
+
readonly filename: string;
|
|
224
|
+
readonly mimeType: string;
|
|
225
|
+
readonly buffer: Buffer;
|
|
226
|
+
readonly size: number;
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
157
230
|
## Middleware
|
|
158
231
|
|
|
159
232
|
```ts
|
|
@@ -368,6 +441,83 @@ createServer()
|
|
|
368
441
|
.listen(3000);
|
|
369
442
|
```
|
|
370
443
|
|
|
444
|
+
## Contract Composition
|
|
445
|
+
|
|
446
|
+
When building applications with distinct audiences — a **public client** and an **admin panel**, for example — you want each consumer to import only the endpoints it needs. This eliminates leaking admin schemas into the client bundle and improves tree-shaking.
|
|
447
|
+
|
|
448
|
+
The `@cleverbrush/server/contract` entry point ships three utilities for this.
|
|
449
|
+
|
|
450
|
+
### `mergeContracts`
|
|
451
|
+
|
|
452
|
+
Combine two `ApiContract` objects into one. Groups that only exist in one contract are kept as-is; groups that share a key have their endpoint maps shallowly merged.
|
|
453
|
+
|
|
454
|
+
```ts
|
|
455
|
+
import { defineApi, mergeContracts } from '@cleverbrush/server/contract';
|
|
456
|
+
|
|
457
|
+
// public-api.ts — safe to import in every consumer
|
|
458
|
+
export const publicApi = defineApi({
|
|
459
|
+
todos: { list: ..., get: ..., create: ... },
|
|
460
|
+
auth: { login: ..., register: ... },
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// admin-api.ts — only imported by the admin application
|
|
464
|
+
const adminApi = defineApi({
|
|
465
|
+
admin: { activityLog: ..., banUser: ... },
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// admin-app/contract.ts
|
|
469
|
+
export const fullAdminApi = mergeContracts(publicApi, adminApi);
|
|
470
|
+
// TypeScript sees: { todos, auth, admin } — all fully typed
|
|
471
|
+
|
|
472
|
+
// client-app/contract.ts
|
|
473
|
+
import { publicApi } from '../shared/public-api';
|
|
474
|
+
// TypeScript sees: { todos, auth } — admin is absent from the bundle
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### `pickGroups`
|
|
478
|
+
|
|
479
|
+
Returns a new contract containing only the listed groups. The TypeScript return type is `Pick<T, K>` — the compiler sees exactly the selected groups.
|
|
480
|
+
|
|
481
|
+
```ts
|
|
482
|
+
import { pickGroups } from '@cleverbrush/server/contract';
|
|
483
|
+
|
|
484
|
+
const fullApi = defineApi({ todos: {...}, auth: {...}, admin: {...}, debug: {...} });
|
|
485
|
+
|
|
486
|
+
// Only expose what the frontend needs
|
|
487
|
+
const clientApi = pickGroups(fullApi, 'todos', 'auth');
|
|
488
|
+
// TypeScript: { todos: ..., auth: ... }
|
|
489
|
+
// 'admin' and 'debug' do not exist on the type or at runtime
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### `omitGroups`
|
|
493
|
+
|
|
494
|
+
Inverse of `pickGroups` — strips the listed groups and keeps everything else. Return type is `Omit<T, K>`.
|
|
495
|
+
|
|
496
|
+
```ts
|
|
497
|
+
import { omitGroups } from '@cleverbrush/server/contract';
|
|
498
|
+
|
|
499
|
+
const publicApi = omitGroups(fullApi, 'admin', 'debug');
|
|
500
|
+
// TypeScript: { todos: ..., auth: ... }
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Bundle isolation pattern
|
|
504
|
+
|
|
505
|
+
The key to keeping admin endpoints out of the client bundle is **file-level separation**. Export different slices from different entry points:
|
|
506
|
+
|
|
507
|
+
```
|
|
508
|
+
packages/
|
|
509
|
+
shared-contracts/
|
|
510
|
+
src/
|
|
511
|
+
public.ts // export const publicApi = defineApi({ ... })
|
|
512
|
+
admin.ts // export const adminApi = defineApi({ ... })
|
|
513
|
+
full.ts // export const fullApi = mergeContracts(publicApi, adminApi)
|
|
514
|
+
|
|
515
|
+
apps/
|
|
516
|
+
client/ // imports publicApi — admin endpoints never bundled
|
|
517
|
+
admin-panel/ // imports fullApi — full set of endpoints
|
|
518
|
+
backend/ // imports fullApi — handles all routes
|
|
519
|
+
```
|
|
520
|
+
|
|
371
521
|
## License
|
|
372
522
|
|
|
373
523
|
BSD-3-Clause — see [LICENSE](../../LICENSE).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type ObjectSchemaBuilder, type SchemaBuilder } from '@cleverbrush/schema';
|
|
2
|
+
/**
|
|
3
|
+
* An accessor that can extract a property value from a structured
|
|
4
|
+
* request root. Wraps a {@link PropertyDescriptor}'s `getValue`
|
|
5
|
+
* closure so the middleware layer does not need to know about schemas.
|
|
6
|
+
*/
|
|
7
|
+
export interface CacheTagPropertyAccessor {
|
|
8
|
+
getValue(root: {
|
|
9
|
+
params: Record<string, unknown>;
|
|
10
|
+
body: unknown;
|
|
11
|
+
query: Record<string, unknown>;
|
|
12
|
+
headers: Record<string, string>;
|
|
13
|
+
}): {
|
|
14
|
+
value?: unknown;
|
|
15
|
+
success: boolean;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A serialisable cache-tag definition stored on endpoint metadata.
|
|
20
|
+
*
|
|
21
|
+
* `properties` maps human-readable key names (used as label segments
|
|
22
|
+
* in the final cache key) to accessors that resolve the actual value
|
|
23
|
+
* from call-time request data.
|
|
24
|
+
*/
|
|
25
|
+
export interface CacheTagDefinition {
|
|
26
|
+
readonly name: string;
|
|
27
|
+
readonly properties: Readonly<Record<string, CacheTagPropertyAccessor>>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Builds a synthetic `object({ params, body, query, headers })` schema
|
|
31
|
+
* from the endpoint's schema definitions and returns its
|
|
32
|
+
* `PropertyDescriptorTree` so callers can write type-safe selectors like:
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* endpoint.cacheTag('todo', p => ({
|
|
36
|
+
* id: p.query.id,
|
|
37
|
+
* fromBodyId: p.body.id
|
|
38
|
+
* }))
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* Only non-null schemas are included in the synthetic schema.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createCacheTagTree(schemas: {
|
|
44
|
+
paramsSchema?: SchemaBuilder<any, any, any, any, any> | null;
|
|
45
|
+
bodySchema?: SchemaBuilder<any, any, any, any, any> | null;
|
|
46
|
+
querySchema?: ObjectSchemaBuilder<any, any, any, any, any, any, any> | null;
|
|
47
|
+
headerSchema?: ObjectSchemaBuilder<any, any, any, any, any, any, any> | null;
|
|
48
|
+
}): any;
|
|
49
|
+
/**
|
|
50
|
+
* Serialises the result of a cache-tag selector callback into a
|
|
51
|
+
* {@link CacheTagDefinition} that can be stored on endpoint metadata
|
|
52
|
+
* and forwarded to the client middleware.
|
|
53
|
+
*
|
|
54
|
+
* Each value in `descriptors` must be a {@link PropertyDescriptor} —
|
|
55
|
+
* its `getValue` closure is wrapped in a {@link CacheTagPropertyAccessor}.
|
|
56
|
+
*
|
|
57
|
+
* @throws If any value is not a valid property descriptor.
|
|
58
|
+
*/
|
|
59
|
+
export declare function serializeTag(name: string, descriptors: Record<string, unknown>): CacheTagDefinition;
|
|
60
|
+
/**
|
|
61
|
+
* Computes a deterministic cache key from a tag definition and live
|
|
62
|
+
* request data.
|
|
63
|
+
*
|
|
64
|
+
* - Simple tags (no properties) produce just the tag name.
|
|
65
|
+
* - Tags with properties produce `name:key1=val1,key2=val2` where
|
|
66
|
+
* keys are sorted alphabetically for determinism.
|
|
67
|
+
*
|
|
68
|
+
* Properties whose `getValue` returns `success: false` are skipped
|
|
69
|
+
* (their value is not included in the key).
|
|
70
|
+
*/
|
|
71
|
+
export declare function computeCacheKey(tag: CacheTagDefinition, root: {
|
|
72
|
+
params: Record<string, unknown>;
|
|
73
|
+
body: unknown;
|
|
74
|
+
query: Record<string, unknown>;
|
|
75
|
+
headers: Record<string, string>;
|
|
76
|
+
}): string;
|
package/dist/Endpoint.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { InferType, ObjectSchemaBuilder, ParseStringSchemaBuilder, PropertyDescriptorTree, SchemaBuilder } from '@cleverbrush/schema';
|
|
2
|
+
import { SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR } from '@cleverbrush/schema';
|
|
2
3
|
import type { ActionResult, ContentResult, FileResult, JsonResult, NoContentResult, RedirectResult, StatusCodeResult, StreamResult } from './ActionResult.js';
|
|
4
|
+
import type { CacheTagDefinition } from './CacheTag.js';
|
|
3
5
|
import type { RequestContext } from './RequestContext.js';
|
|
4
6
|
import { type SubscriptionBuilder, type SubscriptionHandlerEntry } from './Subscription.js';
|
|
5
|
-
import type { Middleware } from './types.js';
|
|
7
|
+
import type { FilePart, Middleware, RejectedFile, UploadOptions } from './types.js';
|
|
6
8
|
type Simplify<T> = {
|
|
7
9
|
[K in keyof T]: T[K];
|
|
8
10
|
} & {};
|
|
9
11
|
type HasKeys<T> = keyof T extends never ? false : true;
|
|
10
|
-
type ActionContextParts<TParams, TBody, TQuery, THeaders, TPrincipal> = {
|
|
12
|
+
type ActionContextParts<TParams, TBody, TQuery, THeaders, TPrincipal, TUpload extends boolean> = {
|
|
11
13
|
context: RequestContext;
|
|
12
14
|
} & (HasKeys<TParams> extends true ? {
|
|
13
15
|
params: TParams;
|
|
@@ -19,14 +21,17 @@ type ActionContextParts<TParams, TBody, TQuery, THeaders, TPrincipal> = {
|
|
|
19
21
|
headers: THeaders;
|
|
20
22
|
} : {}) & (TPrincipal extends undefined ? {} : {
|
|
21
23
|
principal: TPrincipal;
|
|
22
|
-
})
|
|
24
|
+
}) & (TUpload extends true ? {
|
|
25
|
+
files: Record<string, FilePart>;
|
|
26
|
+
rejectedFiles?: RejectedFile[];
|
|
27
|
+
} : {});
|
|
23
28
|
/**
|
|
24
29
|
* The fully-typed argument object passed to endpoint handlers.
|
|
25
30
|
*
|
|
26
31
|
* The shape is inferred from the `EndpointBuilder` chain — only the keys
|
|
27
32
|
* actually configured (body, query, headers, params, principal) are present.
|
|
28
33
|
*/
|
|
29
|
-
export type ActionContext<E> = E extends EndpointBuilder<infer TParams, infer TBody, infer TQuery, infer THeaders, any, infer TPrincipal, any, any, any> ? Simplify<ActionContextParts<TParams, TBody, TQuery, THeaders, TPrincipal>> : never;
|
|
34
|
+
export type ActionContext<E> = E extends EndpointBuilder<infer TParams, infer TBody, infer TQuery, infer THeaders, any, infer TPrincipal, any, any, any, infer TUpload> ? Simplify<ActionContextParts<TParams, TBody, TQuery, THeaders, TPrincipal, TUpload>> : never;
|
|
30
35
|
type InferServices<T> = {
|
|
31
36
|
[K in keyof T]: T[K] extends SchemaBuilder<any, any, any, any, any> ? InferType<T[K]> : never;
|
|
32
37
|
};
|
|
@@ -34,13 +39,13 @@ type InferServices<T> = {
|
|
|
34
39
|
* Extracts the injected service schemas map from an `EndpointBuilder` type.
|
|
35
40
|
* Used internally by the `Handler` type to derive the `services` argument.
|
|
36
41
|
*/
|
|
37
|
-
export type ServiceSchemas<E> = E extends EndpointBuilder<any, any, any, any, infer TServices, any, any, any, any> ? TServices : {};
|
|
38
|
-
type ResponseType<E> = E extends EndpointBuilder<any, any, any, any, any, any, any, infer TResponse, any> ? TResponse extends SchemaBuilder<any, any, any, any, any> ? InferType<TResponse> : TResponse : any;
|
|
42
|
+
export type ServiceSchemas<E> = E extends EndpointBuilder<any, any, any, any, infer TServices, any, any, any, any, any> ? TServices : {};
|
|
43
|
+
type ResponseType<E> = E extends EndpointBuilder<any, any, any, any, any, any, any, infer TResponse, any, any> ? TResponse extends SchemaBuilder<any, any, any, any, any> ? InferType<TResponse> : TResponse : any;
|
|
39
44
|
/**
|
|
40
45
|
* Extracts the `TResponses` map from an `EndpointBuilder` type.
|
|
41
46
|
* `TResponses` is a `Record<number, BodyType>` inferred from `.responses()`.
|
|
42
47
|
*/
|
|
43
|
-
export type ResponsesOf<E> = E extends EndpointBuilder<any, any, any, any, any, any, any, any, infer TResponses> ? TResponses : never;
|
|
48
|
+
export type ResponsesOf<E> = E extends EndpointBuilder<any, any, any, any, any, any, any, any, infer TResponses, any> ? TResponses : never;
|
|
44
49
|
type HasResponses<E> = keyof ResponsesOf<E> extends never ? false : true;
|
|
45
50
|
/**
|
|
46
51
|
* The union of permitted return values for a handler whose endpoint
|
|
@@ -64,7 +69,7 @@ export type AllowedResponseReturn<TResponses extends Record<number, any>> = {
|
|
|
64
69
|
*/
|
|
65
70
|
type HandlerReturn<E> = HasResponses<E> extends true ? AllowedResponseReturn<ResponsesOf<E>> : ResponseType<E> | ActionResult;
|
|
66
71
|
export type Handler<E> = HasKeys<ServiceSchemas<E>> extends true ? (arg: ActionContext<E>, services: Simplify<InferServices<ServiceSchemas<E>>>) => HandlerReturn<E> | Promise<HandlerReturn<E>> : (arg: ActionContext<E>) => HandlerReturn<E> | Promise<HandlerReturn<E>>;
|
|
67
|
-
type AnyEndpoint = EndpointBuilder<any, any, any, any, any, any, any, any, any>;
|
|
72
|
+
type AnyEndpoint = EndpointBuilder<any, any, any, any, any, any, any, any, any, any>;
|
|
68
73
|
type AnySubscriptionBuilder = SubscriptionBuilder<any, any, any, any, any, any, any, any>;
|
|
69
74
|
/**
|
|
70
75
|
* A single handler entry in a {@link HandlerMap}.
|
|
@@ -298,6 +303,17 @@ export interface EndpointMetadata {
|
|
|
298
303
|
* OpenAPI Operation Object.
|
|
299
304
|
*/
|
|
300
305
|
readonly callbacks: Record<string, CallbackDefinition> | null;
|
|
306
|
+
/**
|
|
307
|
+
* When set, the endpoint accepts `multipart/form-data` uploads.
|
|
308
|
+
* The configuration controls max file size, allowed MIME types, etc.
|
|
309
|
+
* @see `EndpointBuilder.upload()`
|
|
310
|
+
*/
|
|
311
|
+
readonly fileUpload: UploadOptions | null;
|
|
312
|
+
/**
|
|
313
|
+
* Cache tags declared via `.clearsCacheTag()`, providing tag-based cache
|
|
314
|
+
* key computation for the client middleware.
|
|
315
|
+
*/
|
|
316
|
+
readonly cacheTags: readonly CacheTagDefinition[];
|
|
301
317
|
}
|
|
302
318
|
/**
|
|
303
319
|
* Immutable, fluent builder for HTTP endpoint definitions.
|
|
@@ -324,7 +340,37 @@ export interface EndpointMetadata {
|
|
|
324
340
|
type InferResponsesMap<T extends Record<number, SchemaBuilder<any, any, any, any, any> | null>> = {
|
|
325
341
|
[K in keyof T]: T[K] extends SchemaBuilder<any, any, any, any, any> ? InferType<T[K]> : null;
|
|
326
342
|
};
|
|
327
|
-
|
|
343
|
+
/**
|
|
344
|
+
* A leaf node in a cache-tag property tree — mirrors the shape of the
|
|
345
|
+
* actual runtime {@link PropertyDescriptor} so the compiler accepts
|
|
346
|
+
* values selected by the consumer.
|
|
347
|
+
*/
|
|
348
|
+
interface CacheTagPropertyLeaf {
|
|
349
|
+
readonly [SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]: {
|
|
350
|
+
readonly getValue: (obj: Record<string, unknown>) => {
|
|
351
|
+
readonly value?: unknown;
|
|
352
|
+
readonly success: boolean;
|
|
353
|
+
};
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
/** Recursively builds a typed property tree from an inferred object shape. */
|
|
357
|
+
type CacheTagPropertyTree<T> = CacheTagPropertyLeaf & (T extends Record<string, unknown> ? {
|
|
358
|
+
readonly [K in keyof T]-?: CacheTagPropertyTree<T[K]>;
|
|
359
|
+
} : unknown);
|
|
360
|
+
/**
|
|
361
|
+
* The typed tree passed to the `.clearsCacheTag(name, selector)` callback.
|
|
362
|
+
*
|
|
363
|
+
* `p.params`, `p.query`, and `p.headers` provide IDE completion for
|
|
364
|
+
* each schema's property names, while `p.body` resolves through the
|
|
365
|
+
* body schema's `InferType`.
|
|
366
|
+
*/
|
|
367
|
+
type CacheTagSelector<TParams, TBody, TQuery, THeaders> = {
|
|
368
|
+
readonly params: [keyof TParams] extends [never] ? Record<string, never> : TParams extends Record<string, unknown> ? CacheTagPropertyTree<TParams> : Record<string, never>;
|
|
369
|
+
readonly body: TBody extends undefined ? undefined : TBody extends SchemaBuilder<any, any, any, any, any> ? CacheTagPropertyTree<InferType<TBody>> : Record<string, never>;
|
|
370
|
+
readonly query: [keyof TQuery] extends [never] ? Record<string, never> : TQuery extends Record<string, unknown> ? CacheTagPropertyTree<TQuery> : Record<string, never>;
|
|
371
|
+
readonly headers: [keyof THeaders] extends [never] ? Record<string, never> : THeaders extends Record<string, unknown> ? CacheTagPropertyTree<THeaders> : Record<string, never>;
|
|
372
|
+
};
|
|
373
|
+
export declare class EndpointBuilder<TParams = {}, TBody = undefined, TQuery = {}, THeaders = {}, TServices = {}, TPrincipal = undefined, TRoles extends string = string, TResponse = any, TResponses extends Record<number, any> = {}, TUpload extends boolean = false> {
|
|
328
374
|
#private;
|
|
329
375
|
constructor(method: string, basePath: string, pathTemplate: RoutePath, bodySchema: SchemaBuilder<any, any, any, any, any> | null, querySchema: ObjectSchemaBuilder<any, any, any, any, any, any, any> | null, headerSchema: ObjectSchemaBuilder<any, any, any, any, any, any, any> | null, serviceSchemas?: Record<string, SchemaBuilder<any, any, any, any, any>> | null, authRoles?: readonly string[] | null, summary?: string | null, description?: string | null, tags?: readonly string[], operationId?: string | null, deprecated?: boolean, responseSchema?: SchemaBuilder<any, any, any, any, any> | null, responsesSchemas?: Record<number, SchemaBuilder<any, any, any, any, any> | null> | null, example?: unknown | null, examples?: Record<string, {
|
|
330
376
|
summary?: string;
|
|
@@ -338,15 +384,15 @@ export declare class EndpointBuilder<TParams = {}, TBody = undefined, TQuery = {
|
|
|
338
384
|
}> | null, responseHeaderSchema?: ObjectSchemaBuilder<any, any, any, any, any, any, any> | null, externalDocs?: {
|
|
339
385
|
url: string;
|
|
340
386
|
description?: string;
|
|
341
|
-
} | null, links?: Record<string, LinkDefinition> | null, callbacks?: Record<string, CallbackDefinition> | null);
|
|
387
|
+
} | null, links?: Record<string, LinkDefinition> | null, callbacks?: Record<string, CallbackDefinition> | null, fileUpload?: UploadOptions | null, cacheTags?: readonly CacheTagDefinition[]);
|
|
342
388
|
/** Define the request body schema. Validation failures return 422 Problem Details. */
|
|
343
|
-
body<TSchema extends SchemaBuilder<any, any, any, any, any>>(schema: TSchema): EndpointBuilder<TParams, TSchema, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
389
|
+
body<TSchema extends SchemaBuilder<any, any, any, any, any>>(schema: TSchema): EndpointBuilder<TParams, TSchema, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
344
390
|
/** Define the query string schema (must be an object schema). Validation failures return 422. */
|
|
345
|
-
query<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(schema: TSchema): EndpointBuilder<TParams, TBody, InferType<TSchema>, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
391
|
+
query<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(schema: TSchema): EndpointBuilder<TParams, TBody, InferType<TSchema>, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
346
392
|
/** Define an expected request headers schema (must be an object schema). */
|
|
347
|
-
headers<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(schema: TSchema): EndpointBuilder<TParams, TBody, TQuery, InferType<TSchema>, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
393
|
+
headers<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(schema: TSchema): EndpointBuilder<TParams, TBody, TQuery, InferType<TSchema>, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
348
394
|
/** Declare DI services to be resolved per-request and passed as the second handler argument. */
|
|
349
|
-
inject<TSchemas extends Record<string, SchemaBuilder<any, any, any, any, any>>>(schemas: TSchemas): EndpointBuilder<TParams, TBody, TQuery, THeaders, TSchemas, TPrincipal, TRoles, TResponse, TResponses>;
|
|
395
|
+
inject<TSchemas extends Record<string, SchemaBuilder<any, any, any, any, any>>>(schemas: TSchemas): EndpointBuilder<TParams, TBody, TQuery, THeaders, TSchemas, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
350
396
|
/**
|
|
351
397
|
* Mark this endpoint as requiring authorization.
|
|
352
398
|
*
|
|
@@ -356,8 +402,8 @@ export declare class EndpointBuilder<TParams = {}, TBody = undefined, TQuery = {
|
|
|
356
402
|
*
|
|
357
403
|
* If no roles are specified, any authenticated user is allowed.
|
|
358
404
|
*/
|
|
359
|
-
authorize<TSchema extends SchemaBuilder<any, any, any, any, any>>(principalSchema: TSchema, ...roles: TRoles[]): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, InferType<TSchema>, TRoles, TResponse, TResponses>;
|
|
360
|
-
authorize(...roles: TRoles[]): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, unknown, TRoles, TResponse, TResponses>;
|
|
405
|
+
authorize<TSchema extends SchemaBuilder<any, any, any, any, any>>(principalSchema: TSchema, ...roles: TRoles[]): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, InferType<TSchema>, TRoles, TResponse, TResponses, TUpload>;
|
|
406
|
+
authorize(...roles: TRoles[]): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, unknown, TRoles, TResponse, TResponses, TUpload>;
|
|
361
407
|
/**
|
|
362
408
|
* Declare the response type for OpenAPI spec generation.
|
|
363
409
|
*
|
|
@@ -365,8 +411,8 @@ export declare class EndpointBuilder<TParams = {}, TBody = undefined, TQuery = {
|
|
|
365
411
|
* - `returns<T>()` — generic type only, no runtime schema
|
|
366
412
|
* - `returns(schema)` — provides a schema for spec generation and type inference
|
|
367
413
|
*/
|
|
368
|
-
returns<T>(): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, T, TResponses>;
|
|
369
|
-
returns<TSchema extends SchemaBuilder<any, any, any, any, any>>(schema: TSchema): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TSchema, TResponses>;
|
|
414
|
+
returns<T>(): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, T, TResponses, TUpload>;
|
|
415
|
+
returns<TSchema extends SchemaBuilder<any, any, any, any, any>>(schema: TSchema): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TSchema, TResponses, TUpload>;
|
|
370
416
|
/**
|
|
371
417
|
* Declare per-status-code response schemas for OpenAPI generation and
|
|
372
418
|
* handler return-type enforcement.
|
|
@@ -389,17 +435,17 @@ export declare class EndpointBuilder<TParams = {}, TBody = undefined, TQuery = {
|
|
|
389
435
|
* };
|
|
390
436
|
* ```
|
|
391
437
|
*/
|
|
392
|
-
responses<const T extends Record<number, SchemaBuilder<any, any, any, any, any> | null>>(map: T): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, InferResponsesMap<T
|
|
438
|
+
responses<const T extends Record<number, SchemaBuilder<any, any, any, any, any> | null>>(map: T): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, InferResponsesMap<T>, TUpload>;
|
|
393
439
|
/** Short, human-readable summary for OpenAPI operation objects. */
|
|
394
|
-
summary(text: string): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
440
|
+
summary(text: string): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
395
441
|
/** Longer description for OpenAPI operation objects. Supports Markdown. */
|
|
396
|
-
description(text: string): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
442
|
+
description(text: string): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
397
443
|
/** OpenAPI tags grouping this operation in generated documentation. */
|
|
398
|
-
tags(...tags: string[]): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
444
|
+
tags(...tags: string[]): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
399
445
|
/** A unique, stable identifier for this operation in OpenAPI spec. */
|
|
400
|
-
operationId(id: string): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
446
|
+
operationId(id: string): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
401
447
|
/** Mark this endpoint as deprecated in OpenAPI spec output. */
|
|
402
|
-
deprecated(): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
448
|
+
deprecated(): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
403
449
|
/**
|
|
404
450
|
* Provide a single example value for the request body.
|
|
405
451
|
*
|
|
@@ -408,7 +454,7 @@ export declare class EndpointBuilder<TParams = {}, TBody = undefined, TQuery = {
|
|
|
408
454
|
*
|
|
409
455
|
* @param value - An example request body value.
|
|
410
456
|
*/
|
|
411
|
-
example(value: TBody): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
457
|
+
example(value: TBody): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
412
458
|
/**
|
|
413
459
|
* Provide named examples for the request body.
|
|
414
460
|
*
|
|
@@ -421,7 +467,7 @@ export declare class EndpointBuilder<TParams = {}, TBody = undefined, TQuery = {
|
|
|
421
467
|
summary?: string;
|
|
422
468
|
description?: string;
|
|
423
469
|
value: TBody;
|
|
424
|
-
}>): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
470
|
+
}>): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
425
471
|
/**
|
|
426
472
|
* Declare that this endpoint produces a binary file response.
|
|
427
473
|
*
|
|
@@ -431,7 +477,41 @@ export declare class EndpointBuilder<TParams = {}, TBody = undefined, TQuery = {
|
|
|
431
477
|
* @param contentType - MIME type (default: `'application/octet-stream'`).
|
|
432
478
|
* @param description - Optional response description for the spec.
|
|
433
479
|
*/
|
|
434
|
-
producesFile(contentType?: string, description?: string): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
480
|
+
producesFile(contentType?: string, description?: string): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
481
|
+
/**
|
|
482
|
+
* Mark this endpoint as accepting `multipart/form-data` file uploads.
|
|
483
|
+
*
|
|
484
|
+
* When set, the server parses the request body with a streaming multipart
|
|
485
|
+
* parser instead of the default JSON deserializer. File fields are made
|
|
486
|
+
* available to the handler via `arg.files` (a `Record<string, FilePart>`),
|
|
487
|
+
* while non-file form fields are validated against the body schema and
|
|
488
|
+
* available via `arg.body`.
|
|
489
|
+
*
|
|
490
|
+
* @param options - Upload configuration (max file size, allowed MIME types, etc.).
|
|
491
|
+
*
|
|
492
|
+
* @example
|
|
493
|
+
* ```ts
|
|
494
|
+
* const UploadAvatar = endpoint
|
|
495
|
+
* .post('/api/avatar')
|
|
496
|
+
* .upload({ maxFileSize: 2 * 1024 * 1024, allowedMimeTypes: ['image/*'] })
|
|
497
|
+
* .authorize(PrincipalSchema)
|
|
498
|
+
* .responses({ 200: AvatarSchema });
|
|
499
|
+
*
|
|
500
|
+
* const handler: Handler<typeof UploadAvatar> = async ({ files }) => {
|
|
501
|
+
* const avatar = files['avatar'];
|
|
502
|
+
* // avatar: { filename, mimeType, buffer, size }
|
|
503
|
+
* };
|
|
504
|
+
* ```
|
|
505
|
+
*/
|
|
506
|
+
upload(options?: UploadOptions): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, true>;
|
|
507
|
+
/**
|
|
508
|
+
* The full path for this endpoint, combining `basePath` and `pathTemplate`.
|
|
509
|
+
*
|
|
510
|
+
* For static routes this is the exact URL path (e.g. `"/todos"`).
|
|
511
|
+
* For dynamic routes the template placeholders are included
|
|
512
|
+
* (e.g. `"/todos/:id"`).
|
|
513
|
+
*/
|
|
514
|
+
get path(): string;
|
|
435
515
|
/** Return an immutable snapshot of this builder's configuration as {@link EndpointMetadata}. */
|
|
436
516
|
introspect(): EndpointMetadata;
|
|
437
517
|
/**
|
|
@@ -457,7 +537,7 @@ export declare class EndpointBuilder<TParams = {}, TBody = undefined, TQuery = {
|
|
|
457
537
|
*/
|
|
458
538
|
produces(contentTypes: Record<string, {
|
|
459
539
|
schema?: SchemaBuilder<any, any, any, any, any>;
|
|
460
|
-
}>): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
540
|
+
}>): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
461
541
|
/**
|
|
462
542
|
* Declare response headers emitted by this endpoint.
|
|
463
543
|
*
|
|
@@ -476,7 +556,7 @@ export declare class EndpointBuilder<TParams = {}, TBody = undefined, TQuery = {
|
|
|
476
556
|
* }))
|
|
477
557
|
* ```
|
|
478
558
|
*/
|
|
479
|
-
responseHeaders<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(schema: TSchema): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
559
|
+
responseHeaders<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(schema: TSchema): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
480
560
|
/**
|
|
481
561
|
* Link external documentation to this operation.
|
|
482
562
|
*
|
|
@@ -485,7 +565,7 @@ export declare class EndpointBuilder<TParams = {}, TBody = undefined, TQuery = {
|
|
|
485
565
|
* @param url - The URL to the external documentation.
|
|
486
566
|
* @param description - Optional short description of the external docs.
|
|
487
567
|
*/
|
|
488
|
-
externalDocs(url: string, description?: string): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
568
|
+
externalDocs(url: string, description?: string): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
489
569
|
/**
|
|
490
570
|
* Declare response links for OpenAPI spec generation.
|
|
491
571
|
*
|
|
@@ -506,7 +586,7 @@ export declare class EndpointBuilder<TParams = {}, TBody = undefined, TQuery = {
|
|
|
506
586
|
* })
|
|
507
587
|
* ```
|
|
508
588
|
*/
|
|
509
|
-
links(defs: Record<string, LinkDefinition<TResponse>>): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
589
|
+
links(defs: Record<string, LinkDefinition<TResponse>>): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
510
590
|
/**
|
|
511
591
|
* Declare callbacks for OpenAPI spec generation.
|
|
512
592
|
*
|
|
@@ -529,7 +609,51 @@ export declare class EndpointBuilder<TParams = {}, TBody = undefined, TQuery = {
|
|
|
529
609
|
* })
|
|
530
610
|
* ```
|
|
531
611
|
*/
|
|
532
|
-
callbacks(defs: Record<string, CallbackDefinition<TBody>>): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
612
|
+
callbacks(defs: Record<string, CallbackDefinition<TBody>>): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses, TUpload>;
|
|
613
|
+
/**
|
|
614
|
+
* Declare a cache group for this endpoint.
|
|
615
|
+
*
|
|
616
|
+
* Use on GET / query endpoints to group responses into a named cache.
|
|
617
|
+
* The client-side {@code cacheTags} middleware caches responses keyed
|
|
618
|
+
* by this tag and flushes matching entries when a mutation calls
|
|
619
|
+
* {@link clearsCacheTag}.
|
|
620
|
+
*
|
|
621
|
+
* @overload Simple tag (no properties — single cache entry).
|
|
622
|
+
* @overload Tag with property descriptors for fine-grained keys.
|
|
623
|
+
*
|
|
624
|
+
* @example
|
|
625
|
+
* ```ts
|
|
626
|
+
* // GET — responses cached under "todo" group, keyed by id
|
|
627
|
+
* endpoint.get('/api/todos/:id')
|
|
628
|
+
* .cacheTag('todo', p => ({
|
|
629
|
+
* id: p.params.id
|
|
630
|
+
* }))
|
|
631
|
+
* ```
|
|
632
|
+
*/
|
|
633
|
+
cacheTag(name: string): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
634
|
+
cacheTag(name: string, selector: (tree: CacheTagSelector<TParams, TBody, TQuery, THeaders>) => Record<string, unknown>): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
635
|
+
/**
|
|
636
|
+
* Declare which cache groups are cleared when this mutation succeeds.
|
|
637
|
+
*
|
|
638
|
+
* Use on POST / PUT / PATCH / DELETE endpoints. When the mutation
|
|
639
|
+
* completes, the {@code cacheTags} client middleware invalidates all
|
|
640
|
+
* cache entries matching the declared tag names (prefix match).
|
|
641
|
+
*
|
|
642
|
+
* @overload Simple tag (clears all entries prefixed with the name).
|
|
643
|
+
* @overload Tag with property descriptors for targeted invalidation.
|
|
644
|
+
*
|
|
645
|
+
* @example
|
|
646
|
+
* ```ts
|
|
647
|
+
* // PATCH — clears "todo-list" and "todo:id=42" on success
|
|
648
|
+
* endpoint.patch('/api/todos/:id')
|
|
649
|
+
* .clearsCacheTag('todo-list')
|
|
650
|
+
* .clearsCacheTag('todo', p => ({
|
|
651
|
+
* id: p.params.id
|
|
652
|
+
* }))
|
|
653
|
+
* ```
|
|
654
|
+
*/
|
|
655
|
+
clearsCacheTag(name: string): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
656
|
+
clearsCacheTag(name: string, selector: (tree: CacheTagSelector<TParams, TBody, TQuery, THeaders>) => Record<string, unknown>): EndpointBuilder<TParams, TBody, TQuery, THeaders, TServices, TPrincipal, TRoles, TResponse, TResponses>;
|
|
533
657
|
}
|
|
534
658
|
/**
|
|
535
659
|
* Optional OpenAPI metadata fields accepted by `createEndpoint` / `createEndpoints`.
|
|
@@ -549,6 +673,12 @@ type ScopedEndpointFactoryMethods<TPrincipal, TRoles extends string = string> =
|
|
|
549
673
|
delete<TParams = {}>(pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {}, TPrincipal, TRoles, any, {}>;
|
|
550
674
|
head<TParams = {}>(pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {}, TPrincipal, TRoles, any, {}>;
|
|
551
675
|
options<TParams = {}>(pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {}, TPrincipal, TRoles, any, {}>;
|
|
676
|
+
post<TParams = {}>(pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams extends undefined ? {} : TParams, undefined, {}, {}, {}, TPrincipal, TRoles, any, {}>;
|
|
677
|
+
put<TParams = {}>(pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams extends undefined ? {} : TParams, undefined, {}, {}, {}, TPrincipal, TRoles, any, {}>;
|
|
678
|
+
patch<TParams = {}>(pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams extends undefined ? {} : TParams, undefined, {}, {}, {}, TPrincipal, TRoles, any, {}>;
|
|
679
|
+
delete<TParams = {}>(pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams extends undefined ? {} : TParams, undefined, {}, {}, {}, TPrincipal, TRoles, any, {}>;
|
|
680
|
+
head<TParams = {}>(pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams extends undefined ? {} : TParams, undefined, {}, {}, {}, TPrincipal, TRoles, any, {}>;
|
|
681
|
+
options<TParams = {}>(pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams extends undefined ? {} : TParams, undefined, {}, {}, {}, TPrincipal, TRoles, any, {}>;
|
|
552
682
|
};
|
|
553
683
|
export type ScopedEndpointFactory<TRoles extends string = string> = ScopedEndpointFactoryMethods<undefined, TRoles> & {
|
|
554
684
|
/**
|
|
@@ -562,13 +692,13 @@ export type ScopedEndpointFactory<TRoles extends string = string> = ScopedEndpoi
|
|
|
562
692
|
authorize(...roles: TRoles[]): ScopedEndpointFactoryMethods<unknown, TRoles>;
|
|
563
693
|
};
|
|
564
694
|
type EndpointFactory<TRoles extends string = string> = {
|
|
565
|
-
get<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {},
|
|
566
|
-
post<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {},
|
|
567
|
-
put<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {},
|
|
568
|
-
patch<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {},
|
|
569
|
-
delete<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {},
|
|
570
|
-
head<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {},
|
|
571
|
-
options<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {},
|
|
695
|
+
get<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;
|
|
696
|
+
post<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;
|
|
697
|
+
put<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;
|
|
698
|
+
patch<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;
|
|
699
|
+
delete<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;
|
|
700
|
+
head<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;
|
|
701
|
+
options<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): EndpointBuilder<TParams, undefined, {}, {}, {}, any, TRoles, any, {}>;
|
|
572
702
|
resource(basePath: string): ScopedEndpointFactory<TRoles>;
|
|
573
703
|
subscription<TParams = {}>(basePath: string, pathTemplate?: ParseStringSchemaBuilder<TParams, any, any, any, any>): SubscriptionBuilder<TParams, {}, {}, {}, undefined, TRoles>;
|
|
574
704
|
};
|
package/dist/Server.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ export interface AuthorizationConfig {
|
|
|
46
46
|
*/
|
|
47
47
|
export declare class ServerBuilder {
|
|
48
48
|
#private;
|
|
49
|
+
constructor(options?: ServerOptions);
|
|
49
50
|
/**
|
|
50
51
|
* Configure the DI service collection.
|
|
51
52
|
*
|
|
@@ -78,6 +79,8 @@ export declare class ServerBuilder {
|
|
|
78
79
|
/**
|
|
79
80
|
* Enable the `GET /health` endpoint that returns `{ ok: true }` (200).
|
|
80
81
|
* Useful for load balancer and container readiness probes.
|
|
82
|
+
*
|
|
83
|
+
* The path is available as {@link HEALTHCHECK_PATH}.
|
|
81
84
|
*/
|
|
82
85
|
withHealthcheck(): this;
|
|
83
86
|
/**
|