@fluojs/http 1.0.0-beta.2 → 1.0.0-beta.4

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 (71) hide show
  1. package/README.ko.md +4 -1
  2. package/README.md +4 -1
  3. package/dist/adapter.d.ts +31 -0
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js +37 -0
  6. package/dist/adapters/binding.d.ts +6 -0
  7. package/dist/adapters/binding.d.ts.map +1 -1
  8. package/dist/adapters/binding.js +20 -51
  9. package/dist/adapters/dto-binding-plan.d.ts +19 -0
  10. package/dist/adapters/dto-binding-plan.d.ts.map +1 -0
  11. package/dist/adapters/dto-binding-plan.js +54 -0
  12. package/dist/adapters/dto-validation-adapter.d.ts +3 -0
  13. package/dist/adapters/dto-validation-adapter.d.ts.map +1 -1
  14. package/dist/adapters/dto-validation-adapter.js +8 -4
  15. package/dist/dispatch/dispatch-content-negotiation.d.ts +17 -0
  16. package/dist/dispatch/dispatch-content-negotiation.d.ts.map +1 -1
  17. package/dist/dispatch/dispatch-content-negotiation.js +21 -0
  18. package/dist/dispatch/dispatch-error-policy.d.ts +8 -0
  19. package/dist/dispatch/dispatch-error-policy.d.ts.map +1 -1
  20. package/dist/dispatch/dispatch-error-policy.js +9 -0
  21. package/dist/dispatch/dispatch-handler-policy.d.ts +11 -1
  22. package/dist/dispatch/dispatch-handler-policy.d.ts.map +1 -1
  23. package/dist/dispatch/dispatch-handler-policy.js +12 -2
  24. package/dist/dispatch/dispatch-response-policy.d.ts +10 -0
  25. package/dist/dispatch/dispatch-response-policy.d.ts.map +1 -1
  26. package/dist/dispatch/dispatch-response-policy.js +43 -0
  27. package/dist/dispatch/dispatch-routing-policy.d.ts +13 -0
  28. package/dist/dispatch/dispatch-routing-policy.d.ts.map +1 -1
  29. package/dist/dispatch/dispatch-routing-policy.js +14 -0
  30. package/dist/dispatch/dispatcher.d.ts +6 -1
  31. package/dist/dispatch/dispatcher.d.ts.map +1 -1
  32. package/dist/dispatch/dispatcher.js +171 -28
  33. package/dist/dispatch/native-route-handoff.d.ts +53 -0
  34. package/dist/dispatch/native-route-handoff.d.ts.map +1 -0
  35. package/dist/dispatch/native-route-handoff.js +94 -0
  36. package/dist/errors.d.ts +3 -0
  37. package/dist/errors.d.ts.map +1 -1
  38. package/dist/errors.js +4 -0
  39. package/dist/guards.d.ts +7 -0
  40. package/dist/guards.d.ts.map +1 -1
  41. package/dist/guards.js +11 -0
  42. package/dist/input-error-detail.d.ts +9 -0
  43. package/dist/input-error-detail.d.ts.map +1 -1
  44. package/dist/input-error-detail.js +10 -0
  45. package/dist/interceptors.d.ts +8 -0
  46. package/dist/interceptors.d.ts.map +1 -1
  47. package/dist/interceptors.js +14 -1
  48. package/dist/internal.d.ts +1 -0
  49. package/dist/internal.d.ts.map +1 -1
  50. package/dist/internal.js +2 -1
  51. package/dist/mapping.d.ts +7 -0
  52. package/dist/mapping.d.ts.map +1 -1
  53. package/dist/mapping.js +93 -11
  54. package/dist/middleware/correlation.d.ts +5 -0
  55. package/dist/middleware/correlation.d.ts.map +1 -1
  56. package/dist/middleware/correlation.js +6 -0
  57. package/dist/middleware/cors.d.ts +9 -0
  58. package/dist/middleware/cors.d.ts.map +1 -1
  59. package/dist/middleware/cors.js +11 -0
  60. package/dist/middleware/middleware.d.ts +34 -0
  61. package/dist/middleware/middleware.d.ts.map +1 -1
  62. package/dist/middleware/middleware.js +47 -0
  63. package/dist/middleware/security-headers.d.ts +9 -0
  64. package/dist/middleware/security-headers.d.ts.map +1 -1
  65. package/dist/middleware/security-headers.js +11 -0
  66. package/dist/route-path.d.ts +41 -0
  67. package/dist/route-path.d.ts.map +1 -1
  68. package/dist/route-path.js +50 -0
  69. package/dist/types.d.ts +5 -0
  70. package/dist/types.d.ts.map +1 -1
  71. package/package.json +2 -2
package/README.ko.md CHANGED
@@ -115,7 +115,7 @@ stream(_input: undefined, ctx: RequestContext) {
115
115
 
116
116
  ## 요청 정리와 런타임 이식성
117
117
 
118
- 디스패처는 활성 dispatch 동안에만 `AsyncLocalStorage`로 `RequestContext`를 바인딩하고, 요청 observer가 끝난 뒤 `finally` 경로에서 request-scoped DI 컨테이너를 dispose합니다. 이 동작은 정상 성공, 처리된 오류, 중단된 요청 경로에서 request-scoped provider가 다음 요청으로 새지 않게 합니다.
118
+ 디스패처는 활성 dispatch 동안에만 `AsyncLocalStorage`로 `RequestContext`를 바인딩합니다. 요청이 controller graph, middleware, guard, interceptor, observer, DTO converter, custom binder 또는 수동 `getCurrentRequestContext()` / `assertRequestContext()` container 접근을 통해 request-scoped DI를 사용할 수 있으면, 디스패처는 요청 observer가 끝난 뒤 `finally` 경로에서 isolated request-scoped DI 컨테이너를 생성하고 dispose합니다. Singleton-only route는 `RequestContext.container`가 접근되기 전까지 컨테이너 lifecycle을 건너뛰어 baseline 경로의 불필요한 per-request allocation을 피하면서도, graph가 모호하거나 request-scoped이면 request-scoped provider isolation을 유지합니다. 따라서 공개 `RequestContext.container` 읽기는 request-scoped provider resolve에 항상 안전합니다. singleton-only fast path는 내부 dispatcher 최적화일 뿐, 공개 contextroot container를 노출한다는 약속이 아닙니다.
119
119
 
120
120
  어댑터는 플랫폼이 제공한다면 `FrameworkRequest.signal`에 `AbortSignal`을 전달해야 합니다. SSE에서는 가능하면 `FrameworkResponse.stream.onClose(...)`도 노출해야 합니다. `SseResponse`는 request abort와 raw stream close를 모두 구독하고, 멱등하게 닫히며, 어느 쪽이 먼저 종료되더라도 등록한 listener를 제거합니다.
121
121
 
@@ -133,6 +133,9 @@ stream(_input: undefined, ctx: RequestContext) {
133
133
  `./internal` 서브경로는 플랫폼 어댑터와 핵심 런타임에서 사용하는 저수준 유틸리티만 내보냅니다. 이들은 변경될 수 있으며 일반적인 애플리케이션 코드에서 사용해서는 안 됩니다.
134
134
 
135
135
  - `DefaultBinder`: 런타임 부트스트랩 경로에서 사용하는 기본 DTO/요청 바인더.
136
+ - `bindRawRequestNativeRouteHandoff(...)` / `attachFrameworkRequestNativeRouteHandoff(...)`: public dispatcher API를 넓히지 않고 의미 보존이 가능한 native route match를 재사용하기 위한 내부 adapter/runtime 헬퍼.
137
+ - Native route handoff는 framework request에 붙는 시점의 method와 path를 함께 스냅샷합니다. app middleware가 handler matching 전에 둘 중 하나를 rewrite하면 dispatcher는 stale handoff를 무시하고 일반 route matching으로 fallback합니다.
138
+ - `isRoutePathNormalizationSensitive(path)`: duplicate slash와 trailing slash 요청을 generic dispatcher 경로에 남기기 위한 내부 guard.
136
139
  - `resolveClientIdentity(request)`: 속도 제한과 런타임 통합에서 사용하는 보수적 클라이언트 식별 해석기.
137
140
 
138
141
  ## 관련 패키지
package/README.md CHANGED
@@ -117,7 +117,7 @@ stream(_input: undefined, ctx: RequestContext) {
117
117
 
118
118
  ## Request Cleanup and Portability
119
119
 
120
- The dispatcher binds `RequestContext` with `AsyncLocalStorage` for the active dispatch only and disposes the request-scoped DI container from its `finally` path after request observers finish. This keeps per-request providers from leaking across normal success, handled error, and aborted request paths.
120
+ The dispatcher binds `RequestContext` with `AsyncLocalStorage` for the active dispatch only. When a request may use request-scoped DI through its controller graph, middleware, guards, interceptors, observers, DTO converters, a custom binder, or manual `getCurrentRequestContext()` / `assertRequestContext()` container access, the dispatcher creates and disposes an isolated request-scoped DI container from its `finally` path after request observers finish. Singleton-only routes skip that container lifecycle until `RequestContext.container` is accessed, so the baseline path avoids unnecessary per-request allocation while preserving request-scoped provider isolation whenever the graph is ambiguous or request-scoped. Public `RequestContext.container` reads are therefore always safe for resolving request-scoped providers; the singleton-only fast path is an internal dispatcher optimization, not a promise that the public context exposes the root container.
121
121
 
122
122
  Adapters should pass an `AbortSignal` on `FrameworkRequest.signal` when the platform exposes one. For SSE, adapters should also expose `FrameworkResponse.stream.onClose(...)` when possible; `SseResponse` listens to both request abort and raw stream close, closes idempotently, and removes registered listeners when either side terminates first.
123
123
 
@@ -135,6 +135,9 @@ Adapters should pass an `AbortSignal` on `FrameworkRequest.signal` when the plat
135
135
  The `./internal` subpath exports only the low-level utilities used by platform adapters and the core runtime. These are subject to change and should not be used in typical application code.
136
136
 
137
137
  - `DefaultBinder`: Default DTO/request binder used by the runtime bootstrap path.
138
+ - `bindRawRequestNativeRouteHandoff(...)` / `attachFrameworkRequestNativeRouteHandoff(...)`: Internal adapter/runtime helpers for reusing semantically safe native route matches without widening the public dispatcher API.
139
+ - Native route handoffs snapshot the framework request method and path when attached; if app middleware rewrites either value before handler matching, the dispatcher ignores the stale handoff and falls back to normal route matching.
140
+ - `isRoutePathNormalizationSensitive(path)`: Internal guard for keeping duplicate-slash and trailing-slash requests on the generic dispatcher path.
138
141
  - `resolveClientIdentity(request)`: Conservative client identity resolver used by rate limiting and other runtime integrations.
139
142
 
140
143
  ## Related Packages
package/dist/adapter.d.ts CHANGED
@@ -1,14 +1,23 @@
1
1
  import type { MaybePromise } from '@fluojs/core';
2
2
  import type { Dispatcher } from './types.js';
3
+ /**
4
+ * Describes the server backed http adapter realtime capability contract.
5
+ */
3
6
  export interface ServerBackedHttpAdapterRealtimeCapability {
4
7
  kind: 'server-backed';
5
8
  server: unknown;
6
9
  }
10
+ /**
11
+ * Describes the unsupported http adapter realtime capability contract.
12
+ */
7
13
  export interface UnsupportedHttpAdapterRealtimeCapability {
8
14
  kind: 'unsupported';
9
15
  mode: 'no-op';
10
16
  reason: string;
11
17
  }
18
+ /**
19
+ * Describes the fetch style http adapter realtime capability contract.
20
+ */
12
21
  export interface FetchStyleHttpAdapterRealtimeCapability {
13
22
  contract: 'raw-websocket-expansion';
14
23
  kind: 'fetch-style';
@@ -17,9 +26,31 @@ export interface FetchStyleHttpAdapterRealtimeCapability {
17
26
  support: 'contract-only' | 'supported';
18
27
  version: 1;
19
28
  }
29
+ /**
30
+ * Defines the http adapter realtime capability type.
31
+ */
20
32
  export type HttpAdapterRealtimeCapability = ServerBackedHttpAdapterRealtimeCapability | FetchStyleHttpAdapterRealtimeCapability | UnsupportedHttpAdapterRealtimeCapability;
33
+ /**
34
+ * Create server backed http adapter realtime capability.
35
+ *
36
+ * @param server The server.
37
+ * @returns The create server backed http adapter realtime capability result.
38
+ */
21
39
  export declare function createServerBackedHttpAdapterRealtimeCapability(server: unknown): ServerBackedHttpAdapterRealtimeCapability;
40
+ /**
41
+ * Create unsupported http adapter realtime capability.
42
+ *
43
+ * @param reason The reason.
44
+ * @returns The create unsupported http adapter realtime capability result.
45
+ */
22
46
  export declare function createUnsupportedHttpAdapterRealtimeCapability(reason: string): UnsupportedHttpAdapterRealtimeCapability;
47
+ /**
48
+ * Create fetch style http adapter realtime capability.
49
+ *
50
+ * @param reason The reason.
51
+ * @param options The options.
52
+ * @returns The create fetch style http adapter realtime capability result.
53
+ */
23
54
  export declare function createFetchStyleHttpAdapterRealtimeCapability(reason: string, options?: {
24
55
  support?: FetchStyleHttpAdapterRealtimeCapability['support'];
25
56
  }): FetchStyleHttpAdapterRealtimeCapability;
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,yCAAyC;IACxD,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,wCAAwC;IACvD,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uCAAuC;IACtD,QAAQ,EAAE,yBAAyB,CAAC;IACpC,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,eAAe,GAAG,WAAW,CAAC;IACvC,OAAO,EAAE,CAAC,CAAC;CACZ;AAED,MAAM,MAAM,6BAA6B,GACrC,yCAAyC,GACzC,uCAAuC,GACvC,wCAAwC,CAAC;AAE7C,wBAAgB,+CAA+C,CAC7D,MAAM,EAAE,OAAO,GACd,yCAAyC,CAK3C;AAED,wBAAgB,8CAA8C,CAC5D,MAAM,EAAE,MAAM,GACb,wCAAwC,CAM1C;AAED,wBAAgB,6CAA6C,CAC3D,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,uCAAuC,CAAC,SAAS,CAAC,CAAC;CACzD,GACL,uCAAuC,CASzC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,SAAS,CAAC,IAAI,OAAO,CAAC;IAEtB,qBAAqB,CAAC,IAAI,6BAA6B,CAAC;IAExD;;;;;OAKG;IACH,MAAM,CAAC,UAAU,EAAE,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAEnD;;;;;OAKG;IACH,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;CAC5C;AAED;;;;GAIG;AACH,wBAAgB,gCAAgC,IAAI,sBAAsB,CAUzE"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,yCAAyC;IACxD,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,wCAAwC;IACvD,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,uCAAuC;IACtD,QAAQ,EAAE,yBAAyB,CAAC;IACpC,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,eAAe,GAAG,WAAW,CAAC;IACvC,OAAO,EAAE,CAAC,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,MAAM,6BAA6B,GACrC,yCAAyC,GACzC,uCAAuC,GACvC,wCAAwC,CAAC;AAE7C;;;;;GAKG;AACH,wBAAgB,+CAA+C,CAC7D,MAAM,EAAE,OAAO,GACd,yCAAyC,CAK3C;AAED;;;;;GAKG;AACH,wBAAgB,8CAA8C,CAC5D,MAAM,EAAE,MAAM,GACb,wCAAwC,CAM1C;AAED;;;;;;GAMG;AACH,wBAAgB,6CAA6C,CAC3D,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,uCAAuC,CAAC,SAAS,CAAC,CAAC;CACzD,GACL,uCAAuC,CASzC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,SAAS,CAAC,IAAI,OAAO,CAAC;IAEtB,qBAAqB,CAAC,IAAI,6BAA6B,CAAC;IAExD;;;;;OAKG;IACH,MAAM,CAAC,UAAU,EAAE,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAEnD;;;;;OAKG;IACH,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;CAC5C;AAED;;;;GAIG;AACH,wBAAgB,gCAAgC,IAAI,sBAAsB,CAUzE"}
package/dist/adapter.js CHANGED
@@ -1,9 +1,38 @@
1
+ /**
2
+ * Describes the server backed http adapter realtime capability contract.
3
+ */
4
+
5
+ /**
6
+ * Describes the unsupported http adapter realtime capability contract.
7
+ */
8
+
9
+ /**
10
+ * Describes the fetch style http adapter realtime capability contract.
11
+ */
12
+
13
+ /**
14
+ * Defines the http adapter realtime capability type.
15
+ */
16
+
17
+ /**
18
+ * Create server backed http adapter realtime capability.
19
+ *
20
+ * @param server The server.
21
+ * @returns The create server backed http adapter realtime capability result.
22
+ */
1
23
  export function createServerBackedHttpAdapterRealtimeCapability(server) {
2
24
  return {
3
25
  kind: 'server-backed',
4
26
  server
5
27
  };
6
28
  }
29
+
30
+ /**
31
+ * Create unsupported http adapter realtime capability.
32
+ *
33
+ * @param reason The reason.
34
+ * @returns The create unsupported http adapter realtime capability result.
35
+ */
7
36
  export function createUnsupportedHttpAdapterRealtimeCapability(reason) {
8
37
  return {
9
38
  kind: 'unsupported',
@@ -11,6 +40,14 @@ export function createUnsupportedHttpAdapterRealtimeCapability(reason) {
11
40
  reason
12
41
  };
13
42
  }
43
+
44
+ /**
45
+ * Create fetch style http adapter realtime capability.
46
+ *
47
+ * @param reason The reason.
48
+ * @param options The options.
49
+ * @returns The create fetch style http adapter realtime capability result.
50
+ */
14
51
  export function createFetchStyleHttpAdapterRealtimeCapability(reason, options = {}) {
15
52
  return {
16
53
  contract: 'raw-websocket-expansion',
@@ -1,8 +1,14 @@
1
1
  import { type Constructor } from '@fluojs/core';
2
2
  import type { ArgumentResolverContext, Binder, Converter, ConverterLike, ConverterTarget } from '../types.js';
3
+ /**
4
+ * Represents the default converter.
5
+ */
3
6
  export declare class DefaultConverter implements Converter {
4
7
  convert(value: unknown, _target: ConverterTarget): unknown;
5
8
  }
9
+ /**
10
+ * Represents the default binder.
11
+ */
6
12
  export declare class DefaultBinder implements Binder {
7
13
  private readonly converters;
8
14
  constructor(converters?: readonly ConverterLike[]);
@@ -1 +1 @@
1
- {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../../src/adapters/binding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,WAAW,EAA6D,MAAM,cAAc,CAAC;AAK3H,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,eAAe,EAAoB,MAAM,aAAa,CAAC;AA4FhI,qBAAa,gBAAiB,YAAW,SAAS;IAChD,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO;CAG3D;AAyDD,qBAAa,aAAc,YAAW,MAAM;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,GAAE,SAAS,aAAa,EAAO;IAEhE,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC;CA0EjF"}
1
+ {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../../src/adapters/binding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,WAAW,EAAc,MAAM,cAAc,CAAC;AAI5E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,eAAe,EAAoB,MAAM,aAAa,CAAC;AAiDhI;;GAEG;AACH,qBAAa,gBAAiB,YAAW,SAAS;IAChD,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO;CAG3D;AAyDD;;GAEG;AACH,qBAAa,aAAc,YAAW,MAAM;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,GAAE,SAAS,aAAa,EAAO;IAEhE,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC;CA+DjF"}
@@ -1,7 +1,7 @@
1
1
  import { InvariantError } from '@fluojs/core';
2
- import { getDtoBindingSchema } from '@fluojs/core/internal';
3
2
  import { BadRequestException } from '../exceptions.js';
4
3
  import { toInputErrorDetail } from '../input-error-detail.js';
4
+ import { getCompiledDtoBindingPlan } from './dto-binding-plan.js';
5
5
  const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
6
6
  function isPlainObject(value) {
7
7
  if (typeof value !== 'object' || value === null) {
@@ -10,44 +10,6 @@ function isPlainObject(value) {
10
10
  const prototype = Object.getPrototypeOf(value);
11
11
  return prototype === Object.prototype || prototype === null;
12
12
  }
13
- function toFieldName(propertyKey) {
14
- return typeof propertyKey === 'string' ? propertyKey : String(propertyKey);
15
- }
16
- function resolveSourceKey(propertyKey, key) {
17
- return key ?? toFieldName(propertyKey);
18
- }
19
- function readHeader(request, key) {
20
- return request.headers[key.toLowerCase()] ?? request.headers[key];
21
- }
22
- function readSourceValue(request, source, propertyKey, key) {
23
- const resolvedKey = resolveSourceKey(propertyKey, key);
24
- switch (source) {
25
- case 'path':
26
- return request.params[resolvedKey];
27
- case 'query':
28
- return request.query[resolvedKey];
29
- case 'header':
30
- return readHeader(request, resolvedKey);
31
- case 'cookie':
32
- return request.cookies[resolvedKey];
33
- case 'body':
34
- {
35
- if (!isPlainObject(request.body)) {
36
- if (request.body !== undefined && request.body !== null) {
37
- throw new BadRequestException('Request body must be a plain object.', {
38
- details: [toInputErrorDetail({
39
- code: 'INVALID_BODY',
40
- message: 'Request body must be a plain object.',
41
- source: 'body'
42
- })]
43
- });
44
- }
45
- return undefined;
46
- }
47
- return request.body[resolvedKey];
48
- }
49
- }
50
- }
51
13
  function validateBodyKeys(request, bodyKeys) {
52
14
  if (request.body === undefined || request.body === null) {
53
15
  return;
@@ -87,6 +49,10 @@ function validateBodyKeys(request, bodyKeys) {
87
49
  });
88
50
  }
89
51
  }
52
+
53
+ /**
54
+ * Represents the default converter.
55
+ */
90
56
  export class DefaultConverter {
91
57
  convert(value, _target) {
92
58
  return value;
@@ -131,45 +97,48 @@ async function resolveConverter(value, context, cache) {
131
97
  throw error;
132
98
  }
133
99
  }
100
+
101
+ /**
102
+ * Represents the default binder.
103
+ */
134
104
  export class DefaultBinder {
135
105
  constructor(converters = []) {
136
106
  this.converters = converters;
137
107
  }
138
108
  async bind(dto, context) {
139
- const schema = getDtoBindingSchema(dto);
109
+ const plan = getCompiledDtoBindingPlan(dto);
140
110
  const value = new dto();
141
111
  const converterCache = new Map();
142
- const bodyKeys = new Set(schema.filter(entry => entry.metadata.source === 'body').map(entry => resolveSourceKey(entry.propertyKey, entry.metadata.key)));
143
112
  const globalConverters = (await Promise.all(this.converters.map(converter => resolveConverter(converter, context, converterCache)))).filter(converter => Boolean(converter));
144
- validateBodyKeys(context.requestContext.request, bodyKeys);
113
+ validateBodyKeys(context.requestContext.request, plan.bodyKeys);
145
114
  const details = [];
146
- for (const entry of schema) {
147
- const rawValue = readSourceValue(context.requestContext.request, entry.metadata.source, entry.propertyKey, entry.metadata.key);
115
+ for (const entry of plan.entries) {
116
+ const rawValue = entry.read(context.requestContext.request);
148
117
  if (rawValue === undefined) {
149
- if (entry.metadata.optional) {
118
+ if (entry.optional) {
150
119
  continue;
151
120
  }
152
121
  details.push(toInputErrorDetail({
153
122
  code: 'MISSING_FIELD',
154
- field: toFieldName(entry.propertyKey),
155
- message: `Missing required ${entry.metadata.source} field ${resolveSourceKey(entry.propertyKey, entry.metadata.key)}.`,
156
- source: entry.metadata.source
123
+ field: entry.fieldName,
124
+ message: `Missing required ${entry.source} field ${entry.sourceKey}.`,
125
+ source: entry.source
157
126
  }));
158
127
  continue;
159
128
  }
160
129
  const target = {
161
130
  dto,
162
131
  handler: context.handler,
163
- key: resolveSourceKey(entry.propertyKey, entry.metadata.key),
132
+ key: entry.sourceKey,
164
133
  propertyKey: entry.propertyKey,
165
134
  requestContext: context.requestContext,
166
- source: entry.metadata.source
135
+ source: entry.source
167
136
  };
168
137
  let convertedValue = rawValue;
169
138
  for (const converter of globalConverters) {
170
139
  convertedValue = await converter.convert(convertedValue, target);
171
140
  }
172
- const fieldConverter = await resolveConverter(entry.metadata.converter, context, converterCache);
141
+ const fieldConverter = await resolveConverter(entry.converter, context, converterCache);
173
142
  if (fieldConverter) {
174
143
  convertedValue = await fieldConverter.convert(convertedValue, target);
175
144
  }
@@ -0,0 +1,19 @@
1
+ import { type Constructor, type MetadataPropertyKey, type MetadataSource } from '@fluojs/core';
2
+ import { type DtoFieldBindingMetadata } from '@fluojs/core/internal';
3
+ import type { FrameworkRequest } from '../types.js';
4
+ export interface CompiledDtoBindingPlanEntry {
5
+ readonly converter?: DtoFieldBindingMetadata['converter'];
6
+ readonly fieldName: string;
7
+ readonly optional: boolean;
8
+ readonly propertyKey: MetadataPropertyKey;
9
+ readonly read: (request: FrameworkRequest) => unknown;
10
+ readonly source: MetadataSource;
11
+ readonly sourceKey: string;
12
+ }
13
+ export interface CompiledDtoBindingPlan {
14
+ readonly bodyKeys: ReadonlySet<string>;
15
+ readonly entries: readonly CompiledDtoBindingPlanEntry[];
16
+ readonly propertyKeys: readonly MetadataPropertyKey[];
17
+ }
18
+ export declare function getCompiledDtoBindingPlan(dto: Constructor): CompiledDtoBindingPlan;
19
+ //# sourceMappingURL=dto-binding-plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dto-binding-plan.d.ts","sourceRoot":"","sources":["../../src/adapters/dto-binding-plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC/F,OAAO,EAAmD,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAEtH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAcpD,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,uBAAuB,CAAC,WAAW,CAAC,CAAC;IAC1D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC;IACtD,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,SAAS,2BAA2B,EAAE,CAAC;IACzD,QAAQ,CAAC,YAAY,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACvD;AAqBD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,WAAW,GAAG,sBAAsB,CA4BlF"}
@@ -0,0 +1,54 @@
1
+ import { getDtoBindingSchema } from '@fluojs/core/internal';
2
+ function toFieldName(propertyKey) {
3
+ return typeof propertyKey === 'string' ? propertyKey : String(propertyKey);
4
+ }
5
+ function resolveSourceKey(propertyKey, key) {
6
+ return key ?? toFieldName(propertyKey);
7
+ }
8
+ function readHeader(request, key) {
9
+ return request.headers[key.toLowerCase()] ?? request.headers[key];
10
+ }
11
+ const dtoBindingPlanCache = new WeakMap();
12
+ function createSourceReader(source, sourceKey) {
13
+ switch (source) {
14
+ case 'path':
15
+ return request => request.params[sourceKey];
16
+ case 'query':
17
+ return request => request.query[sourceKey];
18
+ case 'header':
19
+ return request => readHeader(request, sourceKey);
20
+ case 'cookie':
21
+ return request => request.cookies[sourceKey];
22
+ case 'body':
23
+ return request => request.body?.[sourceKey];
24
+ default:
25
+ return () => undefined;
26
+ }
27
+ }
28
+ export function getCompiledDtoBindingPlan(dto) {
29
+ const cached = dtoBindingPlanCache.get(dto);
30
+ if (cached) {
31
+ return cached;
32
+ }
33
+ const entries = getDtoBindingSchema(dto).map(entry => {
34
+ const sourceKey = resolveSourceKey(entry.propertyKey, entry.metadata.key);
35
+ return {
36
+ ...(entry.metadata.converter === undefined ? {} : {
37
+ converter: entry.metadata.converter
38
+ }),
39
+ fieldName: toFieldName(entry.propertyKey),
40
+ optional: entry.metadata.optional === true,
41
+ propertyKey: entry.propertyKey,
42
+ read: createSourceReader(entry.metadata.source, sourceKey),
43
+ source: entry.metadata.source,
44
+ sourceKey
45
+ };
46
+ });
47
+ const next = {
48
+ bodyKeys: new Set(entries.filter(entry => entry.source === 'body').map(entry => entry.sourceKey)),
49
+ entries,
50
+ propertyKeys: entries.map(entry => entry.propertyKey)
51
+ };
52
+ dtoBindingPlanCache.set(dto, next);
53
+ return next;
54
+ }
@@ -1,5 +1,8 @@
1
1
  import { type Constructor } from '@fluojs/core';
2
2
  import type { Validator } from '../types.js';
3
+ /**
4
+ * Represents the http dto validation adapter.
5
+ */
3
6
  export declare class HttpDtoValidationAdapter implements Validator {
4
7
  private readonly validator;
5
8
  private throwBadRequestForValidationError;
@@ -1 +1 @@
1
- {"version":3,"file":"dto-validation-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/dto-validation-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAMhD,OAAO,KAAK,EAAmB,SAAS,EAAE,MAAM,aAAa,CAAC;AAE9D,qBAAa,wBAAyB,YAAW,SAAS;IACxD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8B;IAExD,OAAO,CAAC,iCAAiC;IAMzC,OAAO,CAAC,6BAA6B;IAiB/B,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5D,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAWzE"}
1
+ {"version":3,"file":"dto-validation-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/dto-validation-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAKhD,OAAO,KAAK,EAAmB,SAAS,EAAE,MAAM,aAAa,CAAC;AAG9D;;GAEG;AACH,qBAAa,wBAAyB,YAAW,SAAS;IACxD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8B;IAExD,OAAO,CAAC,iCAAiC;IAMzC,OAAO,CAAC,6BAA6B;IAiB/B,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5D,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAWzE"}
@@ -1,7 +1,11 @@
1
- import { getDtoBindingSchema } from '@fluojs/core/internal';
2
1
  import { DefaultValidator as BaseDefaultValidator, DtoValidationError } from '@fluojs/validation';
3
2
  import { BadRequestException } from '../exceptions.js';
4
3
  import { toInputErrorDetail } from '../input-error-detail.js';
4
+ import { getCompiledDtoBindingPlan } from './dto-binding-plan.js';
5
+
6
+ /**
7
+ * Represents the http dto validation adapter.
8
+ */
5
9
  export class HttpDtoValidationAdapter {
6
10
  validator = new BaseDefaultValidator();
7
11
  throwBadRequestForValidationError(error) {
@@ -15,9 +19,9 @@ export class HttpDtoValidationAdapter {
15
19
  }
16
20
  const source = value;
17
21
  const filtered = Object.create(Object.getPrototypeOf(value));
18
- for (const binding of getDtoBindingSchema(target)) {
19
- if (Object.hasOwn(source, binding.propertyKey)) {
20
- filtered[binding.propertyKey] = source[binding.propertyKey];
22
+ for (const propertyKey of getCompiledDtoBindingPlan(target).propertyKeys) {
23
+ if (Object.hasOwn(source, propertyKey)) {
24
+ filtered[propertyKey] = source[propertyKey];
21
25
  }
22
26
  }
23
27
  return filtered;
@@ -1,9 +1,26 @@
1
1
  import type { ContentNegotiationOptions, FrameworkRequest, HandlerDescriptor, ResponseFormatter } from '../types.js';
2
+ /**
3
+ * Describes the resolved content negotiation contract.
4
+ */
2
5
  export interface ResolvedContentNegotiation {
3
6
  defaultFormatter: ResponseFormatter;
4
7
  formatters: ResponseFormatter[];
5
8
  normalizedMediaTypes: string[];
6
9
  }
10
+ /**
11
+ * Resolve content negotiation.
12
+ *
13
+ * @param options The options.
14
+ * @returns The resolve content negotiation result.
15
+ */
7
16
  export declare function resolveContentNegotiation(options: ContentNegotiationOptions | undefined): ResolvedContentNegotiation | undefined;
17
+ /**
18
+ * Select response formatter.
19
+ *
20
+ * @param handler The handler.
21
+ * @param request The request.
22
+ * @param contentNegotiation The content negotiation.
23
+ * @returns The select response formatter result.
24
+ */
8
25
  export declare function selectResponseFormatter(handler: HandlerDescriptor, request: FrameworkRequest, contentNegotiation: ResolvedContentNegotiation): ResponseFormatter;
9
26
  //# sourceMappingURL=dispatch-content-negotiation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch-content-negotiation.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-content-negotiation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAQrB,MAAM,WAAW,0BAA0B;IACzC,gBAAgB,EAAE,iBAAiB,CAAC;IACpC,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,oBAAoB,EAAE,MAAM,EAAE,CAAC;CAChC;AA2GD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,yBAAyB,GAAG,SAAS,GAAG,0BAA0B,GAAG,SAAS,CA+BhI;AAqCD,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,gBAAgB,EACzB,kBAAkB,EAAE,0BAA0B,GAC7C,iBAAiB,CA2CnB"}
1
+ {"version":3,"file":"dispatch-content-negotiation.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-content-negotiation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAQrB;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,gBAAgB,EAAE,iBAAiB,CAAC;IACpC,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,oBAAoB,EAAE,MAAM,EAAE,CAAC;CAChC;AA2GD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,yBAAyB,GAAG,SAAS,GAAG,0BAA0B,GAAG,SAAS,CA+BhI;AAqCD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,gBAAgB,EACzB,kBAAkB,EAAE,0BAA0B,GAC7C,iBAAiB,CA2CnB"}
@@ -1,4 +1,9 @@
1
1
  import { NotAcceptableException } from '../exceptions.js';
2
+
3
+ /**
4
+ * Describes the resolved content negotiation contract.
5
+ */
6
+
2
7
  const NO_ACCEPTABLE_REPRESENTATION_MESSAGE = 'No acceptable response representation found.';
3
8
  function normalizeMediaType(value) {
4
9
  return value.split(';')[0]?.trim().toLowerCase() ?? '';
@@ -77,6 +82,13 @@ function matchesMediaRange(mediaRange, mediaType) {
77
82
  }
78
83
  return rangeSubtype === '*' || rangeSubtype === mediaTypeSubtype;
79
84
  }
85
+
86
+ /**
87
+ * Resolve content negotiation.
88
+ *
89
+ * @param options The options.
90
+ * @returns The resolve content negotiation result.
91
+ */
80
92
  export function resolveContentNegotiation(options) {
81
93
  if (!options?.formatters?.length) {
82
94
  return undefined;
@@ -128,6 +140,15 @@ function resolveDefaultFormatter(allowedFormatters, allowedNormalizedMediaTypes,
128
140
  const idx = allowedNormalizedMediaTypes.indexOf(defaultMediaType);
129
141
  return idx >= 0 ? allowedFormatters[idx] : allowedFormatters[0] ?? contentNegotiation.defaultFormatter;
130
142
  }
143
+
144
+ /**
145
+ * Select response formatter.
146
+ *
147
+ * @param handler The handler.
148
+ * @param request The request.
149
+ * @param contentNegotiation The content negotiation.
150
+ * @returns The select response formatter result.
151
+ */
131
152
  export function selectResponseFormatter(handler, request, contentNegotiation) {
132
153
  const {
133
154
  formatters: allowedFormatters,
@@ -1,3 +1,11 @@
1
1
  import type { FrameworkResponse } from '../types.js';
2
+ /**
3
+ * Write error response.
4
+ *
5
+ * @param error The error.
6
+ * @param response The response.
7
+ * @param requestId The request id.
8
+ * @returns The write error response result.
9
+ */
2
10
  export declare function writeErrorResponse(error: unknown, response: FrameworkResponse, requestId?: string): Promise<void>;
3
11
  //# sourceMappingURL=dispatch-error-policy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch-error-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-error-policy.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAiBrD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQvH"}
1
+ {"version":3,"file":"dispatch-error-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-error-policy.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAiBrD;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQvH"}
@@ -14,6 +14,15 @@ function toHttpException(error) {
14
14
  cause: error
15
15
  });
16
16
  }
17
+
18
+ /**
19
+ * Write error response.
20
+ *
21
+ * @param error The error.
22
+ * @param response The response.
23
+ * @param requestId The request id.
24
+ * @returns The write error response result.
25
+ */
17
26
  export async function writeErrorResponse(error, response, requestId) {
18
27
  if (response.committed) {
19
28
  return;
@@ -1,3 +1,13 @@
1
+ import type { RequestScopeContainer } from '@fluojs/di';
1
2
  import type { Binder, HandlerDescriptor, RequestContext } from '../types.js';
2
- export declare function invokeControllerHandler(handler: HandlerDescriptor, requestContext: RequestContext, binder?: Binder): Promise<unknown>;
3
+ /**
4
+ * Invoke controller handler.
5
+ *
6
+ * @param handler The handler.
7
+ * @param requestContext The request context.
8
+ * @param binder The binder.
9
+ * @param controllerContainer Container used for resolving the controller instance before handler invocation.
10
+ * @returns The invoke controller handler result.
11
+ */
12
+ export declare function invokeControllerHandler(handler: HandlerDescriptor, requestContext: RequestContext, binder?: Binder, controllerContainer?: RequestScopeContainer): Promise<unknown>;
3
13
  //# sourceMappingURL=dispatch-handler-policy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch-handler-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-handler-policy.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAA2B,MAAM,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKtG,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,iBAAiB,EAC1B,cAAc,EAAE,cAAc,EAC9B,MAAM,GAAE,MAAsB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAuBlB"}
1
+ {"version":3,"file":"dispatch-handler-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-handler-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAIxD,OAAO,KAAK,EAA2B,MAAM,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKtG;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,iBAAiB,EAC1B,cAAc,EAAE,cAAc,EAC9B,MAAM,GAAE,MAAsB,EAC9B,mBAAmB,GAAE,qBAAgD,GACpE,OAAO,CAAC,OAAO,CAAC,CAuBlB"}
@@ -3,8 +3,18 @@ import { DefaultBinder } from '../adapters/binding.js';
3
3
  import { HttpDtoValidationAdapter } from '../adapters/dto-validation-adapter.js';
4
4
  const defaultBinder = new DefaultBinder();
5
5
  const defaultValidator = new HttpDtoValidationAdapter();
6
- export async function invokeControllerHandler(handler, requestContext, binder = defaultBinder) {
7
- const controller = await requestContext.container.resolve(handler.controllerToken);
6
+
7
+ /**
8
+ * Invoke controller handler.
9
+ *
10
+ * @param handler The handler.
11
+ * @param requestContext The request context.
12
+ * @param binder The binder.
13
+ * @param controllerContainer Container used for resolving the controller instance before handler invocation.
14
+ * @returns The invoke controller handler result.
15
+ */
16
+ export async function invokeControllerHandler(handler, requestContext, binder = defaultBinder, controllerContainer = requestContext.container) {
17
+ const controller = await controllerContainer.resolve(handler.controllerToken);
8
18
  const method = controller[handler.methodName];
9
19
  if (typeof method !== 'function') {
10
20
  throw new InvariantError(`Controller ${handler.controllerToken.name} does not expose handler method ${handler.methodName}.`);
@@ -1,6 +1,16 @@
1
1
  import { resolveContentNegotiation, type ResolvedContentNegotiation } from './dispatch-content-negotiation.js';
2
2
  import { writeErrorResponse } from './dispatch-error-policy.js';
3
3
  import type { FrameworkRequest, FrameworkResponse, HandlerDescriptor } from '../types.js';
4
+ /**
5
+ * Write success response.
6
+ *
7
+ * @param handler The handler.
8
+ * @param request The request.
9
+ * @param response The response.
10
+ * @param value The value.
11
+ * @param contentNegotiation The content negotiation.
12
+ * @returns The write success response result.
13
+ */
4
14
  export declare function writeSuccessResponse(handler: HandlerDescriptor, request: FrameworkRequest, response: FrameworkResponse, value: unknown, contentNegotiation: ResolvedContentNegotiation | undefined): Promise<void>;
5
15
  export { resolveContentNegotiation, writeErrorResponse };
6
16
  export type { ResolvedContentNegotiation };
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch-response-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-response-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EAEzB,KAAK,0BAA0B,EAChC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAcrB,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,gBAAgB,EACzB,QAAQ,EAAE,iBAAiB,EAC3B,KAAK,EAAE,OAAO,EACd,kBAAkB,EAAE,0BAA0B,GAAG,SAAS,GACzD,OAAO,CAAC,IAAI,CAAC,CAoCf;AAED,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,CAAC;AACzD,YAAY,EAAE,0BAA0B,EAAE,CAAC"}
1
+ {"version":3,"file":"dispatch-response-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-response-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EAEzB,KAAK,0BAA0B,EAChC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAgErB;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,gBAAgB,EACzB,QAAQ,EAAE,iBAAiB,EAC3B,KAAK,EAAE,OAAO,EACd,kBAAkB,EAAE,0BAA0B,GAAG,SAAS,GACzD,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,CAAC;AACzD,YAAY,EAAE,0BAA0B,EAAE,CAAC"}