@ddd-ts-kit/http 0.3.0 → 0.4.1

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 (127) hide show
  1. package/README.md +60 -0
  2. package/dist/controller/controller-class.d.ts +7 -0
  3. package/dist/controller/controller-class.d.ts.map +1 -0
  4. package/dist/controller/controller-class.js +2 -0
  5. package/dist/controller/controller-class.js.map +1 -0
  6. package/dist/controller/controller-request.d.ts +11 -0
  7. package/dist/controller/controller-request.d.ts.map +1 -0
  8. package/dist/controller/controller-request.js +2 -0
  9. package/dist/controller/controller-request.js.map +1 -0
  10. package/dist/controller/controller-response.d.ts +10 -0
  11. package/dist/controller/controller-response.d.ts.map +1 -0
  12. package/dist/controller/controller-response.js +2 -0
  13. package/dist/controller/controller-response.js.map +1 -0
  14. package/dist/controller/controller-result.d.ts +7 -0
  15. package/dist/controller/controller-result.d.ts.map +1 -0
  16. package/dist/controller/controller-result.js +2 -0
  17. package/dist/controller/controller-result.js.map +1 -0
  18. package/dist/controller/controller-route.d.ts +7 -0
  19. package/dist/controller/controller-route.d.ts.map +1 -0
  20. package/dist/controller/controller-route.js +10 -0
  21. package/dist/controller/controller-route.js.map +1 -0
  22. package/dist/controller/controller-routes-options.d.ts +19 -0
  23. package/dist/controller/controller-routes-options.d.ts.map +1 -0
  24. package/dist/controller/controller-routes-options.js +2 -0
  25. package/dist/controller/controller-routes-options.js.map +1 -0
  26. package/dist/controller/controller-routes.d.ts +11 -0
  27. package/dist/controller/controller-routes.d.ts.map +1 -0
  28. package/dist/controller/controller-routes.js +72 -0
  29. package/dist/controller/controller-routes.js.map +1 -0
  30. package/dist/controller/controller-spec.d.ts +30 -0
  31. package/dist/controller/controller-spec.d.ts.map +1 -0
  32. package/dist/controller/controller-spec.js +2 -0
  33. package/dist/controller/controller-spec.js.map +1 -0
  34. package/dist/controller/controller.d.ts +20 -0
  35. package/dist/controller/controller.d.ts.map +1 -0
  36. package/dist/controller/controller.js +30 -0
  37. package/dist/controller/controller.js.map +1 -0
  38. package/dist/controller/declared-route.d.ts +13 -0
  39. package/dist/controller/declared-route.d.ts.map +1 -0
  40. package/dist/controller/declared-route.js +54 -0
  41. package/dist/controller/declared-route.js.map +1 -0
  42. package/dist/controller/erased-controller.d.ts +9 -0
  43. package/dist/controller/erased-controller.d.ts.map +1 -0
  44. package/dist/controller/erased-controller.js +2 -0
  45. package/dist/controller/erased-controller.js.map +1 -0
  46. package/dist/controller/guard-documentation.d.ts +10 -0
  47. package/dist/controller/guard-documentation.d.ts.map +1 -0
  48. package/dist/controller/guard-documentation.js +2 -0
  49. package/dist/controller/guard-documentation.js.map +1 -0
  50. package/dist/controller/guard-reference.d.ts +4 -0
  51. package/dist/controller/guard-reference.d.ts.map +1 -0
  52. package/dist/controller/guard-reference.js +2 -0
  53. package/dist/controller/guard-reference.js.map +1 -0
  54. package/dist/controller/guard-request.d.ts +20 -0
  55. package/dist/controller/guard-request.d.ts.map +1 -0
  56. package/dist/controller/guard-request.js +2 -0
  57. package/dist/controller/guard-request.js.map +1 -0
  58. package/dist/controller/guard.d.ts +21 -0
  59. package/dist/controller/guard.d.ts.map +1 -0
  60. package/dist/controller/guard.js +10 -0
  61. package/dist/controller/guard.js.map +1 -0
  62. package/dist/controller/guards/authenticated.d.ts +4 -0
  63. package/dist/controller/guards/authenticated.d.ts.map +1 -0
  64. package/dist/controller/guards/authenticated.js +12 -0
  65. package/dist/controller/guards/authenticated.js.map +1 -0
  66. package/dist/controller/guards/has-role.d.ts +8 -0
  67. package/dist/controller/guards/has-role.d.ts.map +1 -0
  68. package/dist/controller/guards/has-role.js +26 -0
  69. package/dist/controller/guards/has-role.js.map +1 -0
  70. package/dist/controller/guards/public.d.ts +7 -0
  71. package/dist/controller/guards/public.d.ts.map +1 -0
  72. package/dist/controller/guards/public.js +11 -0
  73. package/dist/controller/guards/public.js.map +1 -0
  74. package/dist/controller/http-controller.d.ts +14 -0
  75. package/dist/controller/http-controller.d.ts.map +1 -0
  76. package/dist/controller/http-controller.js +2 -0
  77. package/dist/controller/http-controller.js.map +1 -0
  78. package/dist/controller/http-request.d.ts +27 -0
  79. package/dist/controller/http-request.d.ts.map +1 -0
  80. package/dist/controller/http-request.js +2 -0
  81. package/dist/controller/http-request.js.map +1 -0
  82. package/dist/controller/presentation-registry.d.ts +10 -0
  83. package/dist/controller/presentation-registry.d.ts.map +1 -0
  84. package/dist/controller/presentation-registry.js +21 -0
  85. package/dist/controller/presentation-registry.js.map +1 -0
  86. package/dist/controller/request-mapper.d.ts +12 -0
  87. package/dist/controller/request-mapper.d.ts.map +1 -0
  88. package/dist/controller/request-mapper.js +6 -0
  89. package/dist/controller/request-mapper.js.map +1 -0
  90. package/dist/controller/resolved-controller.d.ts +18 -0
  91. package/dist/controller/resolved-controller.d.ts.map +1 -0
  92. package/dist/controller/resolved-controller.js +47 -0
  93. package/dist/controller/resolved-controller.js.map +1 -0
  94. package/dist/controller/route-group-class.d.ts +3 -0
  95. package/dist/controller/route-group-class.d.ts.map +1 -0
  96. package/dist/controller/route-group-class.js +2 -0
  97. package/dist/controller/route-group-class.js.map +1 -0
  98. package/dist/controller/route-group-options.d.ts +14 -0
  99. package/dist/controller/route-group-options.d.ts.map +1 -0
  100. package/dist/controller/route-group-options.js +2 -0
  101. package/dist/controller/route-group-options.js.map +1 -0
  102. package/dist/controller/route-group.d.ts +10 -0
  103. package/dist/controller/route-group.d.ts.map +1 -0
  104. package/dist/controller/route-group.js +25 -0
  105. package/dist/controller/route-group.js.map +1 -0
  106. package/dist/controller/serializer.d.ts +11 -0
  107. package/dist/controller/serializer.d.ts.map +1 -0
  108. package/dist/controller/serializer.js +6 -0
  109. package/dist/controller/serializer.js.map +1 -0
  110. package/dist/error/to-problem.js +33 -1
  111. package/dist/error/to-problem.js.map +1 -1
  112. package/dist/index.d.ts +24 -0
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js +10 -0
  115. package/dist/index.js.map +1 -1
  116. package/dist/pipeline/execute-route.js +1 -0
  117. package/dist/pipeline/execute-route.js.map +1 -1
  118. package/dist/route/define-route.d.ts.map +1 -1
  119. package/dist/route/define-route.js +2 -2
  120. package/dist/route/define-route.js.map +1 -1
  121. package/dist/route/request-context.d.ts +3 -0
  122. package/dist/route/request-context.d.ts.map +1 -1
  123. package/dist/route/route-path-pattern.d.ts +3 -0
  124. package/dist/route/route-path-pattern.d.ts.map +1 -0
  125. package/dist/route/route-path-pattern.js +3 -0
  126. package/dist/route/route-path-pattern.js.map +1 -0
  127. package/package.json +3 -3
package/README.md CHANGED
@@ -50,6 +50,59 @@ export const orderRoutes = (orders: OrderQueries) =>
50
50
  - `defineRoutes` and every mount refuse a duplicate `operationId` or method and path.
51
51
  - `prefixRoutes('/api', routes)` moves routes under a prefix, for the server and the contract.
52
52
 
53
+ ## Controllers, route groups and guards
54
+
55
+ A route may also be a class: one class per route, one method, `handle`. Its decorator is the
56
+ declaration, and the compiler holds `handle` to it: a part the schemas do not produce, or
57
+ another body than the declared response, is an error on the decorator.
58
+
59
+ ```ts
60
+ import { Authenticated, Controller, RouteGroup, type HttpRequest } from '@ddd-ts-kit/http';
61
+
62
+ @RouteGroup({ prefix: '/orders', tags: ['orders'], guards: [Authenticated], errors: ['FORBIDDEN'] })
63
+ export class OrdersRoutes {}
64
+
65
+ @Controller({
66
+ group: OrdersRoutes,
67
+ method: 'POST',
68
+ path: '/',
69
+ summary: 'Place an order',
70
+ body,
71
+ response: { status: 201, body: placed },
72
+ errors: ['OUT_OF_STOCK'],
73
+ })
74
+ export class PlaceOrderController {
75
+ constructor(private readonly commands: CommandBus) {}
76
+
77
+ async handle({ body, actor }: HttpRequest<{ body: Infer<typeof body> }>): Promise<Infer<typeof placed>> {
78
+ return { id: await this.commands.execute(new PlaceOrder(actor, body.customerId)) };
79
+ }
80
+ }
81
+
82
+ const routes = controllerRoutes([PlaceOrderController], { resolve: application.resolve });
83
+ ```
84
+
85
+ | Export | What it is |
86
+ |---|---|
87
+ | `Controller(spec)` | the decorator of a controller: `group`, `method`, `path` (after the prefix of the group), `operationId` (the class name without `Controller` by default), `summary`, `description`, `tags`, `params`, `query`, `headers`, `body`, `response: { status, description?, body? }`, `errors`, `guards`, `deprecated` |
88
+ | `RouteGroup({ parent, prefix, tags, guards, errors })` | names a set of routes on an empty class; a child inherits from its `parent` and adds |
89
+ | `Guard` | the interface `{ documents?, check(request: GuardRequest) }`, and the decorator of a guard class that takes dependencies |
90
+ | `Authenticated`, `Public`, `HasRole(...roles)` | the guards the package ships, over the `Actor` of the request: 401 to the anonymous caller, 403 without one of the roles; `Public` lets everybody in and stands alone |
91
+ | `RequestMapper<Request, Message>`, `Serializer<Result, Body>` | the interfaces of the two optional translators (`map`, `serialize`), and the decorators that declare them |
92
+ | `HttpRequest<{ params, query, headers, body }>` | the request as `handle`, a mapper or a guard names it, with `actor` and `context` |
93
+ | `controllerRoutes(controllers, { resolve, security, guards })` | the `Route` of each controller, given as a class (built by `resolve`) or as an instance; `security` names the schemes of the application (`['bearer']` by default); `guards: 'skip'` is for the spec of a controller alone |
94
+ | `controllerRoute(controller, options)` | the one route of one controller, for a spec |
95
+
96
+ A controller with no group and no guard is refused when its class is defined. Guards run in
97
+ order, after validation and before `handle`, inside the execution scope of the request, and
98
+ what they throw is answered as a problem like any refusal. A guard is a control at the edge;
99
+ a permission is declared on the use case (`@Requires`), where a job and an event handler meet
100
+ it too. Controllers are declared classes of the edge (`Edge` of
101
+ `@ddd-ts-kit/kernel/declarations`): `@ddd-ts-kit/di` binds each one under itself and lists them
102
+ in `DeclaredEdges`. A controller has one response and answers its body: a route with several
103
+ success statuses, response headers or a raw body stays a `defineRoute` function. The reasons
104
+ are on the [presentation layer](../../docs/concepts/presentation-layer.md) page.
105
+
53
106
  ## Errors
54
107
 
55
108
  A handler throws a `DomainError`; its `code` is mapped to a status in one place and answered as
@@ -98,6 +151,13 @@ answered 5xx goes to the kernel `ErrorReporter`, once, with the correlation, act
98
151
  ids of its execution context (`context.execution`, present once the request got that far); a
99
152
  4xx never does.
100
153
 
154
+ A refusal built by a factory (`ConceptError`) answers its message as `detail` unless the message
155
+ quotes a fact the factory did not expose: then the client reads a sentence made from the code
156
+ (`INVALID_EMAIL` gives `Invalid email.`) and the message stays in the logs and the error
157
+ reports. A fact is found in the message as a text, a number or an ISO instant; a fact written
158
+ another way (a formatted date, a part of an object) is not seen, so expose what a client may
159
+ read and keep the rest out of the sentence.
160
+
101
161
  ## Options shared by the adapters
102
162
 
103
163
  ```ts
@@ -0,0 +1,7 @@
1
+ /**
2
+ * A class given as a controller. Any class is accepted by the types, so that the classes an
3
+ * application lists as declared controllers are handed over as they are; one that carries no
4
+ * `@Controller` is refused when the routes are built.
5
+ */
6
+ export type ControllerClass = abstract new (...args: never) => unknown;
7
+ //# sourceMappingURL=controller-class.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-class.d.ts","sourceRoot":"","sources":["../../src/controller/controller-class.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=controller-class.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-class.js","sourceRoot":"","sources":["../../src/controller/controller-class.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ import type { Actor } from '@ddd-ts-kit/kernel';
2
+ import type { HandlerInput } from '../route/handler-input.js';
3
+ import type { ControllerSpec } from './controller-spec.js';
4
+ /**
5
+ * What `handle` receives for a given declaration: each part has the type its schema produces,
6
+ * and the actor is the one of the execution context of the request.
7
+ */
8
+ export type ControllerRequest<Spec extends ControllerSpec = ControllerSpec> = HandlerInput<Spec> & {
9
+ readonly actor: Actor;
10
+ };
11
+ //# sourceMappingURL=controller-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-request.d.ts","sourceRoot":"","sources":["../../src/controller/controller-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;GAGG;AACH,MAAM,MAAM,iBAAiB,CAAC,IAAI,SAAS,cAAc,GAAG,cAAc,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG;IACjG,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=controller-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-request.js","sourceRoot":"","sources":["../../src/controller/controller-request.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import type { Schema } from '@ddd-ts-kit/validation';
2
+ /** The one answer of a controller when its use case succeeds. */
3
+ export interface ControllerResponse {
4
+ readonly status: number;
5
+ /** Left out, `The answer.` with a body and `Done.` without. */
6
+ readonly description?: string;
7
+ /** Left out, the controller answers no body and `handle` returns nothing. */
8
+ readonly body?: Schema<unknown>;
9
+ }
10
+ //# sourceMappingURL=controller-response.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-response.d.ts","sourceRoot":"","sources":["../../src/controller/controller-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAErD,iEAAiE;AACjE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;CACjC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=controller-response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-response.js","sourceRoot":"","sources":["../../src/controller/controller-response.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import type { Schema } from '@ddd-ts-kit/validation';
2
+ import type { ControllerSpec } from './controller-spec.js';
3
+ /** What `handle` returns for a given declaration: the body its response schema describes, or nothing. */
4
+ export type ControllerResult<Spec extends ControllerSpec = ControllerSpec> = Spec['response'] extends {
5
+ readonly body: Schema<infer Body>;
6
+ } ? Body : undefined;
7
+ //# sourceMappingURL=controller-result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-result.d.ts","sourceRoot":"","sources":["../../src/controller/controller-result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,yGAAyG;AACzG,MAAM,MAAM,gBAAgB,CAAC,IAAI,SAAS,cAAc,GAAG,cAAc,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS;IACpG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;CACnC,GACG,IAAI,GACJ,SAAS,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=controller-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-result.js","sourceRoot":"","sources":["../../src/controller/controller-result.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import type { Route } from '../route/route.js';
2
+ import type { ControllerClass } from './controller-class.js';
3
+ import type { ErasedController } from './erased-controller.js';
4
+ import type { ControllerRoutesOptions } from './controller-routes-options.js';
5
+ /** The route of one controller: what a spec hands to `executeRoute`. */
6
+ export declare function controllerRoute(controller: ControllerClass | ErasedController, options?: ControllerRoutesOptions): Route;
7
+ //# sourceMappingURL=controller-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-route.d.ts","sourceRoot":"","sources":["../../src/controller/controller-route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAG9E,wEAAwE;AACxE,wBAAgB,eAAe,CAC7B,UAAU,EAAE,eAAe,GAAG,gBAAgB,EAC9C,OAAO,GAAE,uBAA4B,GACpC,KAAK,CAQP"}
@@ -0,0 +1,10 @@
1
+ import { controllerRoutes } from './controller-routes.js';
2
+ /** The route of one controller: what a spec hands to `executeRoute`. */
3
+ export function controllerRoute(controller, options = {}) {
4
+ const [route] = controllerRoutes([controller], options);
5
+ if (route === undefined) {
6
+ throw new Error('A controller declares one route, and none was built.');
7
+ }
8
+ return route;
9
+ }
10
+ //# sourceMappingURL=controller-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-route.js","sourceRoot":"","sources":["../../src/controller/controller-route.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,wEAAwE;AACxE,MAAM,UAAU,eAAe,CAC7B,UAA8C,EAC9C,OAAO,GAA4B,EAAE;IAErC,MAAM,CAAC,KAAK,CAAC,GAAG,gBAAgB,CAAC,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;IAExD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,19 @@
1
+ export interface ControllerRoutesOptions {
2
+ /**
3
+ * Builds what was given as a class, a controller or a guard: `resolve` of the application,
4
+ * which injects each constructor parameter. Left out, only instances can be given.
5
+ */
6
+ readonly resolve?: <T>(target: abstract new (...args: never) => T) => T;
7
+ /**
8
+ * The security schemes of the application, as its contract names them: every route that is
9
+ * not public documents them, unless one of its guards names its own. Default: `['bearer']`.
10
+ */
11
+ readonly security?: readonly string[];
12
+ /**
13
+ * `'skip'` leaves the guards out, for the spec of a controller alone: what it validates, what
14
+ * it hands its use case, what it answers. The guards have specs of their own, and an
15
+ * application never sets this.
16
+ */
17
+ readonly guards?: 'skip';
18
+ }
19
+ //# sourceMappingURL=controller-routes-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-routes-options.d.ts","sourceRoot":"","sources":["../../src/controller/controller-routes-options.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,KAAK,CAAC,KAAK,CAAC,CAAC;IACxE;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=controller-routes-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-routes-options.js","sourceRoot":"","sources":["../../src/controller/controller-routes-options.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ import type { Route } from '../route/route.js';
2
+ import type { ControllerClass } from './controller-class.js';
3
+ import type { ErasedController } from './erased-controller.js';
4
+ import type { ControllerRoutesOptions } from './controller-routes-options.js';
5
+ /**
6
+ * The routes of controllers, for any adapter and for the contract generator: each controller
7
+ * becomes one `Route`, so validation, error mapping, tracing and problem documents are those
8
+ * of every route. Controllers are given as classes with `resolve`, or as instances.
9
+ */
10
+ export declare function controllerRoutes(controllers: readonly (ControllerClass | ErasedController)[], options?: ControllerRoutesOptions): readonly Route[];
11
+ //# sourceMappingURL=controller-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-routes.d.ts","sourceRoot":"","sources":["../../src/controller/controller-routes.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAO9E;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,SAAS,CAAC,eAAe,GAAG,gBAAgB,CAAC,EAAE,EAC5D,OAAO,GAAE,uBAA4B,GACpC,SAAS,KAAK,EAAE,CAMlB"}
@@ -0,0 +1,72 @@
1
+ import { assertUniqueRoutes } from '../route/assert-unique-routes.js';
2
+ import { resolvedController } from './resolved-controller.js';
3
+ const DEFAULT_SECURITY = ['bearer'];
4
+ /**
5
+ * The routes of controllers, for any adapter and for the contract generator: each controller
6
+ * becomes one `Route`, so validation, error mapping, tracing and problem documents are those
7
+ * of every route. Controllers are given as classes with `resolve`, or as instances.
8
+ */
9
+ export function controllerRoutes(controllers, options = {}) {
10
+ const routes = controllers.map((entry) => routeOf(resolvedController(entry, options.resolve), options));
11
+ assertUniqueRoutes(routes);
12
+ return routes;
13
+ }
14
+ function routeOf(resolved, options) {
15
+ const { controller, spec, declared } = resolved;
16
+ const guards = options.guards === 'skip' ? [] : resolved.guards;
17
+ const { status, body, description } = spec.response;
18
+ const identity = { method: spec.method, path: declared.path, operationId: declared.operationId };
19
+ return {
20
+ ...identity,
21
+ summary: spec.summary,
22
+ ...(spec.description === undefined ? {} : { description: spec.description }),
23
+ ...(declared.tags.length === 0 ? {} : { tags: declared.tags }),
24
+ ...requestOf(spec),
25
+ responses: { [status]: { description: description ?? defaultDescription(body), ...bodyOf(body) } },
26
+ errors: [...new Set([...declared.errors, ...resolved.guards.flatMap((guard) => guard.documents?.errors ?? [])])],
27
+ security: securityOf(resolved.guards, options.security ?? DEFAULT_SECURITY),
28
+ ...(spec.deprecated === true ? { deprecated: true } : {}),
29
+ async handle(input) {
30
+ for (const guard of guards) {
31
+ await guard.check(guardRequest(identity, input));
32
+ }
33
+ const answer = await controller.handle({ ...input, actor: input.context.execution.actor });
34
+ return body === undefined ? { status } : { status, body: answer };
35
+ },
36
+ };
37
+ }
38
+ function requestOf(parts) {
39
+ const declared = Object.entries({
40
+ params: parts.params,
41
+ query: parts.query,
42
+ headers: parts.headers,
43
+ body: parts.body,
44
+ }).filter(([, schema]) => schema !== undefined);
45
+ return declared.length === 0 ? {} : { request: Object.fromEntries(declared) };
46
+ }
47
+ function bodyOf(body) {
48
+ return body === undefined ? {} : { body };
49
+ }
50
+ function defaultDescription(body) {
51
+ return body === undefined ? 'Done.' : 'The answer.';
52
+ }
53
+ function securityOf(guards, schemes) {
54
+ if (guards.some((guard) => guard.documents?.public === true)) {
55
+ return false;
56
+ }
57
+ const named = guards.flatMap((guard) => guard.documents?.security ?? []);
58
+ return [...new Set(named.length === 0 ? schemes : named)];
59
+ }
60
+ function guardRequest(identity, input) {
61
+ const { context } = input;
62
+ const { actor, session } = context.execution;
63
+ return {
64
+ ...identity,
65
+ params: typeof input.params === 'object' && input.params !== null ? { ...input.params } : {},
66
+ headers: context.rawHeaders ?? {},
67
+ actor,
68
+ ...(session === undefined ? {} : { session }),
69
+ context,
70
+ };
71
+ }
72
+ //# sourceMappingURL=controller-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-routes.js","sourceRoot":"","sources":["../../src/controller/controller-routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { Schema } from '@ddd-ts-kit/validation';
2
+ import type { HttpMethod } from '../route/http-method.js';
3
+ import type { ControllerResponse } from './controller-response.js';
4
+ import type { GuardReference } from './guard-reference.js';
5
+ import type { RouteGroupClass } from './route-group-class.js';
6
+ /** Everything a route says, in the decorator of its controller and nowhere else. */
7
+ export interface ControllerSpec {
8
+ /** The group the route belongs to: it gives a prefix, tags, guards and error codes. */
9
+ readonly group?: RouteGroupClass;
10
+ readonly method: HttpMethod;
11
+ /** Literal segments and `:name` parameters, after the prefix of the group; `/` for the prefix itself. */
12
+ readonly path: string;
13
+ /** Left out, the name of the class without `Controller`, in camelCase. */
14
+ readonly operationId?: string;
15
+ readonly summary: string;
16
+ readonly description?: string;
17
+ /** Added to the tags of the group. */
18
+ readonly tags?: readonly string[];
19
+ readonly params?: Schema<unknown>;
20
+ readonly query?: Schema<unknown>;
21
+ readonly headers?: Schema<unknown>;
22
+ readonly body?: Schema<unknown>;
23
+ readonly response: ControllerResponse;
24
+ /** Domain error codes the route may answer, added to those of its group and of its guards. */
25
+ readonly errors?: readonly string[];
26
+ /** Run after the guards of the group. With no group, at least one is required: `[Public]` says the route is open. */
27
+ readonly guards?: readonly GuardReference[];
28
+ readonly deprecated?: boolean;
29
+ }
30
+ //# sourceMappingURL=controller-spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-spec.d.ts","sourceRoot":"","sources":["../../src/controller/controller-spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,uFAAuF;IACvF,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,yGAAyG;IACzG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,sCAAsC;IACtC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,8FAA8F;IAC9F,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,qHAAqH;IACrH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IAC5C,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=controller-spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller-spec.js","sourceRoot":"","sources":["../../src/controller/controller-spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ import { type ClassDeclarator } from '@ddd-ts-kit/kernel/declarations';
2
+ import type { ControllerSpec } from './controller-spec.js';
3
+ import type { HttpController } from './http-controller.js';
4
+ /**
5
+ * Declares one route on the class that answers it:
6
+ *
7
+ * @Controller({ group: AppointmentRoutes, method: 'POST', path: '/', summary: 'Book appointment',
8
+ * body, response: { status: 201, body: accepted }, errors: ['SLOT_TAKEN'] })
9
+ * export class BookAppointmentController {
10
+ * constructor(private readonly commands: CommandBus) {}
11
+ * async handle({ body, actor }: HttpRequest<{ body: BookAppointmentBody }>): Promise<BookAppointmentAccepted> { ... }
12
+ * }
13
+ *
14
+ * The declaration is the single statement of the route: the server validates with its schemas
15
+ * and the contract is generated from it. The compiler holds `handle` to it: a part the
16
+ * schemas do not produce, or another body than the declared response, is refused on the
17
+ * decorator. The constructor states the dependencies, as for every declared class.
18
+ */
19
+ export declare function Controller<const Spec extends ControllerSpec>(spec: Spec): ClassDeclarator<HttpController<Spec>, undefined>;
20
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/controller/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAC/F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,KAAK,CAAC,IAAI,SAAS,cAAc,EAC1D,IAAI,EAAE,IAAI,GACT,eAAe,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,CAclD"}
@@ -0,0 +1,30 @@
1
+ import { DeclarationError, Edge } from '@ddd-ts-kit/kernel/declarations';
2
+ import { declaredRoute } from './declared-route.js';
3
+ import { presentationRegistry } from './presentation-registry.js';
4
+ /**
5
+ * Declares one route on the class that answers it:
6
+ *
7
+ * @Controller({ group: AppointmentRoutes, method: 'POST', path: '/', summary: 'Book appointment',
8
+ * body, response: { status: 201, body: accepted }, errors: ['SLOT_TAKEN'] })
9
+ * export class BookAppointmentController {
10
+ * constructor(private readonly commands: CommandBus) {}
11
+ * async handle({ body, actor }: HttpRequest<{ body: BookAppointmentBody }>): Promise<BookAppointmentAccepted> { ... }
12
+ * }
13
+ *
14
+ * The declaration is the single statement of the route: the server validates with its schemas
15
+ * and the contract is generated from it. The compiler holds `handle` to it: a part the
16
+ * schemas do not produce, or another body than the declared response, is refused on the
17
+ * decorator. The constructor states the dependencies, as for every declared class.
18
+ */
19
+ export function Controller(spec) {
20
+ const declare = Edge('controller');
21
+ return (target, context) => {
22
+ if (!target.name.endsWith('Controller')) {
23
+ throw new DeclarationError(`${target.name} carries @Controller: name the class after its use case, ending in Controller.`);
24
+ }
25
+ declaredRoute(target.name, spec);
26
+ declare(target, context);
27
+ presentationRegistry().controllers.set(target, spec);
28
+ };
29
+ }
30
+ //# sourceMappingURL=controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.js","sourceRoot":"","sources":["../../src/controller/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAwB,MAAM,iCAAiC,CAAC;AAE/F,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAElE;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,UAAU,CACxB,IAAU;IAEV,MAAM,OAAO,GAAG,IAAI,CAAuB,YAAY,CAAC,CAAC;IAEzD,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;QACzB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YACxC,MAAM,IAAI,gBAAgB,CACxB,GAAG,MAAM,CAAC,IAAI,gFAAgF,CAC/F,CAAC;QACJ,CAAC;QAED,aAAa,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACjC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACzB,oBAAoB,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACvD,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ControllerSpec } from './controller-spec.js';
2
+ import type { GuardReference } from './guard-reference.js';
3
+ /** What a controller says once the groups above it are folded in. */
4
+ export interface DeclaredRoute {
5
+ readonly path: string;
6
+ readonly operationId: string;
7
+ readonly tags: readonly string[];
8
+ readonly guards: readonly GuardReference[];
9
+ readonly errors: readonly string[];
10
+ }
11
+ /** Folds the groups of a controller into its declaration, and refuses one that cannot stand. */
12
+ export declare function declaredRoute(className: string, spec: ControllerSpec): DeclaredRoute;
13
+ //# sourceMappingURL=declared-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"declared-route.d.ts","sourceRoot":"","sources":["../../src/controller/declared-route.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAK3D,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAWD,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,aAAa,CAepF"}
@@ -0,0 +1,54 @@
1
+ import { DeclarationError } from '@ddd-ts-kit/kernel/declarations';
2
+ import { ROUTE_PATH_PATTERN } from '../route/route-path-pattern.js';
3
+ import { Public } from './guards/public.js';
4
+ import { presentationRegistry } from './presentation-registry.js';
5
+ const NOTHING = { prefix: '', tags: [], guards: [], errors: [] };
6
+ /** Folds the groups of a controller into its declaration, and refuses one that cannot stand. */
7
+ export function declaredRoute(className, spec) {
8
+ const inherited = spec.group === undefined ? NOTHING : inheritedFrom(className, spec.group);
9
+ const guards = [...inherited.guards, ...(spec.guards ?? [])];
10
+ const path = `${inherited.prefix}${spec.path === '/' && inherited.prefix !== '' ? '' : spec.path}`;
11
+ assertPath(className, spec.path, path);
12
+ assertAccess(className, guards);
13
+ return {
14
+ path,
15
+ operationId: spec.operationId ?? operationIdOf(className),
16
+ tags: distinct([...inherited.tags, ...(spec.tags ?? [])]),
17
+ guards,
18
+ errors: distinct([...inherited.errors, ...(spec.errors ?? [])]),
19
+ };
20
+ }
21
+ function inheritedFrom(className, group) {
22
+ const options = presentationRegistry().groups.get(group);
23
+ if (options === undefined) {
24
+ throw new DeclarationError(`Controller ${className}: its group ${group.name} carries no @RouteGroup.`);
25
+ }
26
+ const above = options.parent === undefined ? NOTHING : inheritedFrom(className, options.parent);
27
+ return {
28
+ prefix: `${above.prefix}${options.prefix ?? ''}`,
29
+ tags: [...above.tags, ...(options.tags ?? [])],
30
+ guards: [...above.guards, ...(options.guards ?? [])],
31
+ errors: [...above.errors, ...(options.errors ?? [])],
32
+ };
33
+ }
34
+ function assertPath(className, written, path) {
35
+ if (!ROUTE_PATH_PATTERN.test(written) || !ROUTE_PATH_PATTERN.test(path)) {
36
+ throw new DeclarationError(`Controller ${className}: the path "${written}" must be made of literal segments and ":name" parameters.`);
37
+ }
38
+ }
39
+ function assertAccess(className, guards) {
40
+ if (guards.length === 0) {
41
+ throw new DeclarationError(`Controller ${className} names no group with guards and no guard of its own: nothing says who may call it. Name its group, or declare \`guards: [Public]\` when anybody may.`);
42
+ }
43
+ if (guards.length > 1 && guards.includes(Public)) {
44
+ throw new DeclarationError(`Controller ${className} is behind Public and another guard: a route is open or it is guarded. Move it to a group of its kind.`);
45
+ }
46
+ }
47
+ function operationIdOf(className) {
48
+ const name = className.replace(/Controller$/, '');
49
+ return `${name.charAt(0).toLowerCase()}${name.slice(1)}`;
50
+ }
51
+ function distinct(values) {
52
+ return [...new Set(values)];
53
+ }
54
+ //# sourceMappingURL=declared-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"declared-route.js","sourceRoot":"","sources":["../../src/controller/declared-route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGpE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAmBlE,MAAM,OAAO,GAAc,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAE5E,gGAAgG;AAChG,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAE,IAAoB;IACnE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5F,MAAM,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,GAAG,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,KAAK,GAAG,IAAI,SAAS,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAEnG,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACvC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAEhC,OAAO;QACL,IAAI;QACJ,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,aAAa,CAAC,SAAS,CAAC;QACzD,IAAI,EAAE,QAAQ,CAAC,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;QACzD,MAAM;QACN,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;KAChE,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,SAAiB,EAAE,KAAsB;IAC9D,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEzD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,gBAAgB,CAAC,cAAc,SAAS,eAAe,KAAK,CAAC,IAAI,0BAA0B,CAAC,CAAC;IACzG,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAEhG,OAAO;QACL,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE;QAChD,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;QACpD,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;KACrD,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,SAAiB,EAAE,OAAe,EAAE,IAAY;IAClE,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,gBAAgB,CACxB,cAAc,SAAS,eAAe,OAAO,4DAA4D,CAC1G,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,SAAiB,EAAE,MAAiC;IACxE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,gBAAgB,CACxB,cAAc,SAAS,sJAAsJ,CAC9K,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,gBAAgB,CACxB,cAAc,SAAS,wGAAwG,CAChI,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,SAAiB;IACtC,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAElD,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC3D,CAAC;AAED,SAAS,QAAQ,CAAC,MAAyB;IACzC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Actor } from '@ddd-ts-kit/kernel';
2
+ import type { RouteInput } from '../route/route-input.js';
3
+ /** A controller once its schema types are erased, as the pipeline calls it. */
4
+ export interface ErasedController {
5
+ handle(request: RouteInput & {
6
+ readonly actor: Actor;
7
+ }): unknown;
8
+ }
9
+ //# sourceMappingURL=erased-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erased-controller.d.ts","sourceRoot":"","sources":["../../src/controller/erased-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,+EAA+E;AAC/E,MAAM,WAAW,gBAAgB;IAE/B,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG;QAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;KAAE,GAAG,OAAO,CAAC;CAClE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=erased-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"erased-controller.js","sourceRoot":"","sources":["../../src/controller/erased-controller.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ /** What a guard says in the contract of every route behind it, so no route repeats it. */
2
+ export interface GuardDocumentation {
3
+ /** `true` on the guard that lets everybody in: the route is documented with no security requirement. */
4
+ readonly public?: boolean;
5
+ /** The security schemes the guard authenticates with, when they are not the ones of the application. */
6
+ readonly security?: readonly string[];
7
+ /** The error codes the guard may answer: they join the declared codes of the route. */
8
+ readonly errors?: readonly string[];
9
+ }
10
+ //# sourceMappingURL=guard-documentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-documentation.d.ts","sourceRoot":"","sources":["../../src/controller/guard-documentation.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,MAAM,WAAW,kBAAkB;IACjC,wGAAwG;IACxG,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,wGAAwG;IACxG,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,uFAAuF;IACvF,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=guard-documentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-documentation.js","sourceRoot":"","sources":["../../src/controller/guard-documentation.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type { Guard } from './guard.js';
2
+ /** A guard as a group or a controller names it: the value itself, or the declared class the composition root builds. */
3
+ export type GuardReference = Guard | (abstract new (...args: never) => Guard);
4
+ //# sourceMappingURL=guard-reference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-reference.d.ts","sourceRoot":"","sources":["../../src/controller/guard-reference.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,wHAAwH;AACxH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,CAAC,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,KAAK,KAAK,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=guard-reference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-reference.js","sourceRoot":"","sources":["../../src/controller/guard-reference.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ import type { Actor, Session } from '@ddd-ts-kit/kernel';
2
+ import type { HttpMethod } from '../route/http-method.js';
3
+ import type { RawHeaders } from '../route/raw-headers.js';
4
+ import type { RequestContext } from '../route/request-context.js';
5
+ /** What a guard reads to let a request through or refuse it: who calls, and which route. */
6
+ export interface GuardRequest {
7
+ readonly method: HttpMethod;
8
+ /** The template of the route, the prefixes of its groups included: `/practices/:practiceId/agenda`. */
9
+ readonly path: string;
10
+ readonly operationId: string;
11
+ /** The path parameters, through the schema of the controller when it declares one. */
12
+ readonly params: Readonly<Record<string, unknown>>;
13
+ /** The headers as they arrived, before any schema. */
14
+ readonly headers: RawHeaders;
15
+ readonly actor: Actor;
16
+ /** Absent when the application resolves no session. */
17
+ readonly session?: Session;
18
+ readonly context: RequestContext;
19
+ }
20
+ //# sourceMappingURL=guard-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-request.d.ts","sourceRoot":"","sources":["../../src/controller/guard-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,4FAA4F;AAC5F,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,uGAAuG;IACvG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,sFAAsF;IACtF,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,uDAAuD;IACvD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;CAClC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=guard-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-request.js","sourceRoot":"","sources":["../../src/controller/guard-request.ts"],"names":[],"mappings":""}