@fluojs/http 2.0.1 → 3.0.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.ko.md +417 -16
- package/README.md +424 -16
- package/dist/access-log-observer.d.ts +77 -0
- package/dist/access-log-observer.d.ts.map +1 -0
- package/dist/access-log-observer.js +171 -0
- package/dist/adapter.d.ts +9 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +15 -1
- package/dist/adapters/binding.d.ts.map +1 -1
- package/dist/adapters/binding.js +18 -16
- package/dist/adapters/dto-binding-plan.d.ts +2 -2
- package/dist/adapters/dto-binding-plan.d.ts.map +1 -1
- package/dist/adapters/dto-binding-plan.js +13 -4
- package/dist/adapters/dto-validation-adapter.d.ts +2 -1
- package/dist/adapters/dto-validation-adapter.d.ts.map +1 -1
- package/dist/adapters/dto-validation-adapter.js +2 -2
- package/dist/byte-range-response.d.ts +71 -0
- package/dist/byte-range-response.d.ts.map +1 -0
- package/dist/byte-range-response.js +347 -0
- package/dist/client-identity.d.ts +8 -4
- package/dist/client-identity.d.ts.map +1 -1
- package/dist/client-identity.js +12 -93
- package/dist/compiled-route-identity.d.ts +17 -0
- package/dist/compiled-route-identity.d.ts.map +1 -0
- package/dist/compiled-route-identity.js +29 -0
- package/dist/connection.d.ts +67 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +480 -0
- package/dist/context/request-context-node-store.d.ts +6 -0
- package/dist/context/request-context-node-store.d.ts.map +1 -1
- package/dist/context/request-context-node-store.js +14 -0
- package/dist/context/request-context.d.ts +5 -5
- package/dist/context/request-context.d.ts.map +1 -1
- package/dist/context/request-context.js +8 -83
- package/dist/context/request-id.d.ts +10 -0
- package/dist/context/request-id.d.ts.map +1 -0
- package/dist/context/request-id.js +27 -0
- package/dist/context/sse.d.ts +13 -0
- package/dist/context/sse.d.ts.map +1 -1
- package/dist/context/sse.js +36 -6
- package/dist/cookie-helpers.d.ts +47 -0
- package/dist/cookie-helpers.d.ts.map +1 -0
- package/dist/cookie-helpers.js +166 -0
- package/dist/decorators.d.ts +52 -19
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +57 -10
- package/dist/dispatch/conditional-request-policy.d.ts +67 -0
- package/dist/dispatch/conditional-request-policy.d.ts.map +1 -0
- package/dist/dispatch/conditional-request-policy.js +297 -0
- package/dist/dispatch/dispatch-content-negotiation.d.ts +8 -0
- package/dist/dispatch/dispatch-content-negotiation.d.ts.map +1 -1
- package/dist/dispatch/dispatch-content-negotiation.js +89 -42
- package/dist/dispatch/dispatch-error-negotiation.d.ts +26 -0
- package/dist/dispatch/dispatch-error-negotiation.d.ts.map +1 -0
- package/dist/dispatch/dispatch-error-negotiation.js +110 -0
- package/dist/dispatch/dispatch-error-policy.d.ts +1 -10
- package/dist/dispatch/dispatch-error-policy.d.ts.map +1 -1
- package/dist/dispatch/dispatch-error-policy.js +1 -33
- package/dist/dispatch/dispatch-error-representation.d.ts +18 -0
- package/dist/dispatch/dispatch-error-representation.d.ts.map +1 -0
- package/dist/dispatch/dispatch-error-representation.js +125 -0
- package/dist/dispatch/dispatch-response-metadata.d.ts +10 -0
- package/dist/dispatch/dispatch-response-metadata.d.ts.map +1 -0
- package/dist/dispatch/dispatch-response-metadata.js +12 -0
- package/dist/dispatch/dispatch-response-policy.d.ts +30 -3
- package/dist/dispatch/dispatch-response-policy.d.ts.map +1 -1
- package/dist/dispatch/dispatch-response-policy.js +104 -15
- package/dist/dispatch/dispatcher.d.ts +5 -1
- package/dist/dispatch/dispatcher.d.ts.map +1 -1
- package/dist/dispatch/dispatcher.js +140 -100
- package/dist/dispatch/fast-path/debug-visibility.d.ts.map +1 -1
- package/dist/dispatch/fast-path/debug-visibility.js +6 -3
- package/dist/dispatch/fast-path/dispatcher-state.d.ts +10 -0
- package/dist/dispatch/fast-path/dispatcher-state.d.ts.map +1 -0
- package/dist/dispatch/fast-path/dispatcher-state.js +52 -0
- package/dist/dispatch/fast-path/eligibility-checker.d.ts.map +1 -1
- package/dist/dispatch/fast-path/eligibility-checker.js +27 -15
- package/dist/dispatch/fast-path/eligibility.d.ts +16 -10
- package/dist/dispatch/fast-path/eligibility.d.ts.map +1 -1
- package/dist/dispatch/fast-path/eligibility.js +10 -4
- package/dist/dispatch/fast-path/fast-path-executor.d.ts +1 -1
- package/dist/dispatch/fast-path/fast-path-executor.d.ts.map +1 -1
- package/dist/dispatch/fast-path/fast-path-executor.js +8 -5
- package/dist/dispatch/request-abort.d.ts +22 -0
- package/dist/dispatch/request-abort.d.ts.map +1 -0
- package/dist/dispatch/request-abort.js +30 -0
- package/dist/dispatch/response-integration.d.ts +55 -0
- package/dist/dispatch/response-integration.d.ts.map +1 -0
- package/dist/dispatch/response-integration.js +57 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +19 -0
- package/dist/guards.d.ts +1 -1
- package/dist/guards.d.ts.map +1 -1
- package/dist/header-helpers.d.ts +59 -0
- package/dist/header-helpers.d.ts.map +1 -0
- package/dist/header-helpers.js +185 -0
- package/dist/index.d.ts +1 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -15
- package/dist/index.portable.d.ts +25 -0
- package/dist/index.portable.d.ts.map +1 -0
- package/dist/index.portable.js +21 -0
- package/dist/internal.d.ts +3 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -0
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +100 -15
- package/dist/middleware/correlation.d.ts +9 -1
- package/dist/middleware/correlation.d.ts.map +1 -1
- package/dist/middleware/correlation.js +25 -14
- package/dist/middleware/cors.d.ts.map +1 -1
- package/dist/middleware/cors.js +5 -8
- package/dist/middleware/middleware.d.ts +3 -3
- package/dist/middleware/middleware.d.ts.map +1 -1
- package/dist/middleware/rate-limit.d.ts +13 -2
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/middleware/rate-limit.js +1 -0
- package/dist/static-assets.d.ts +77 -0
- package/dist/static-assets.d.ts.map +1 -0
- package/dist/static-assets.js +270 -0
- package/dist/types.d.ts +209 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +69 -1
- package/package.json +19 -9
|
@@ -31,31 +31,37 @@ export interface FastPathEligibility {
|
|
|
31
31
|
/** Whether adapter-level plugin behavior may affect execution. */
|
|
32
32
|
readonly hasAdapterPluginInfluence: boolean;
|
|
33
33
|
/** Resolved execution path, either 'fast' or 'full'. */
|
|
34
|
-
executionPath: 'fast' | 'full';
|
|
34
|
+
readonly executionPath: 'fast' | 'full';
|
|
35
35
|
/** Explanation for full-path fallback when fast path is not selected. */
|
|
36
|
-
fallbackReason?: string;
|
|
36
|
+
readonly fallbackReason?: string;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* Statistics collected about fast path decisions for observability.
|
|
40
40
|
*/
|
|
41
41
|
export interface FastPathStats {
|
|
42
42
|
/** Total number of routes analyzed. */
|
|
43
|
-
totalRoutes: number;
|
|
43
|
+
readonly totalRoutes: number;
|
|
44
44
|
/** Number of routes eligible for fast path. */
|
|
45
|
-
fastPathRoutes: number;
|
|
45
|
+
readonly fastPathRoutes: number;
|
|
46
46
|
/** Number of routes using full path. */
|
|
47
|
-
fullPathRoutes: number;
|
|
47
|
+
readonly fullPathRoutes: number;
|
|
48
48
|
/** Per-route eligibility details. */
|
|
49
|
-
routes:
|
|
49
|
+
readonly routes: readonly Readonly<FastPathEligibility>[];
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
52
|
+
* Public symbol key for fast path eligibility metadata attached to dispatcher route snapshots.
|
|
53
|
+
*
|
|
54
|
+
* @remarks
|
|
55
|
+
* Adapter and diagnostics integrations can use this key to inspect the route's
|
|
56
|
+
* frozen {@link FastPathEligibility} metadata without accessing dispatcher-owned state.
|
|
54
57
|
*/
|
|
55
58
|
export declare const FAST_PATH_ELIGIBILITY_SYMBOL: unique symbol;
|
|
56
59
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
60
|
+
* Public symbol key for fast path execution statistics attached to a dispatcher.
|
|
61
|
+
*
|
|
62
|
+
* @remarks
|
|
63
|
+
* Adapter and diagnostics integrations can use this key to read the same
|
|
64
|
+
* {@link FastPathStats} value exposed by `getDispatcherFastPathStats(...)`.
|
|
59
65
|
*/
|
|
60
66
|
export declare const FAST_PATH_STATS_SYMBOL: unique symbol;
|
|
61
67
|
//# sourceMappingURL=eligibility.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eligibility.d.ts","sourceRoot":"","sources":["../../../src/dispatch/fast-path/eligibility.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,wDAAwD;IACxD,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAEhC,+CAA+C;IAC/C,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAEhC,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAE3B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B,sDAAsD;IACtD,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IAEjC,8DAA8D;IAC9D,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IAErC,yDAAyD;IACzD,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IAEtC,iDAAiD;IACjD,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;IAEvC,kEAAkE;IAClE,QAAQ,CAAC,yBAAyB,EAAE,OAAO,CAAC;IAE5C,wDAAwD;IACxD,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"eligibility.d.ts","sourceRoot":"","sources":["../../../src/dispatch/fast-path/eligibility.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,wDAAwD;IACxD,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAEhC,+CAA+C;IAC/C,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAEhC,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAE3B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B,sDAAsD;IACtD,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IAEjC,8DAA8D;IAC9D,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IAErC,yDAAyD;IACzD,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IAEtC,iDAAiD;IACjD,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;IAEvC,kEAAkE;IAClE,QAAQ,CAAC,yBAAyB,EAAE,OAAO,CAAC;IAE5C,wDAAwD;IACxD,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;IAExC,yEAAyE;IACzE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,uCAAuC;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,+CAA+C;IAC/C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAEhC,wCAAwC;IACxC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAEhC,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,SAAS,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;CAC3D;AAED;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,eAAgC,CAAC;AAE1E;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,eAA0B,CAAC"}
|
|
@@ -11,13 +11,19 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Public symbol key for fast path eligibility metadata attached to dispatcher route snapshots.
|
|
15
|
+
*
|
|
16
|
+
* @remarks
|
|
17
|
+
* Adapter and diagnostics integrations can use this key to inspect the route's
|
|
18
|
+
* frozen {@link FastPathEligibility} metadata without accessing dispatcher-owned state.
|
|
16
19
|
*/
|
|
17
20
|
export const FAST_PATH_ELIGIBILITY_SYMBOL = Symbol('fastPathEligibility');
|
|
18
21
|
|
|
19
22
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
23
|
+
* Public symbol key for fast path execution statistics attached to a dispatcher.
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* Adapter and diagnostics integrations can use this key to read the same
|
|
27
|
+
* {@link FastPathStats} value exposed by `getDispatcherFastPathStats(...)`.
|
|
22
28
|
*/
|
|
23
29
|
export const FAST_PATH_STATS_SYMBOL = Symbol('fastPathStats');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RequestScopeContainer } from '@fluojs/di';
|
|
2
|
-
import { type ResolvedContentNegotiation } from '../dispatch-response-policy.js';
|
|
3
2
|
import type { Binder, FrameworkRequest, FrameworkResponse, HandlerDescriptor, RequestContext } from '../../types.js';
|
|
3
|
+
import { type ResolvedContentNegotiation } from '../dispatch-response-policy.js';
|
|
4
4
|
import type { FastPathExecutionResult } from './eligibility-checker.js';
|
|
5
5
|
interface ExecuteFastPathOptions {
|
|
6
6
|
binder?: Binder;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-path-executor.d.ts","sourceRoot":"","sources":["../../../src/dispatch/fast-path/fast-path-executor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAOxD,OAAO,
|
|
1
|
+
{"version":3,"file":"fast-path-executor.d.ts","sourceRoot":"","sources":["../../../src/dispatch/fast-path/fast-path-executor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAOxD,OAAO,KAAK,EACV,MAAM,EACN,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,KAAK,0BAA0B,EAEhC,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AASxE,UAAU,sBAAsB;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,0BAA0B,CAAC;IAChD,mBAAmB,EAAE,qBAAqB,CAAC;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,KAAK,OAAO,CAAC;IACnF,OAAO,EAAE,gBAAgB,CAAC;IAC1B,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,iBAAiB,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,uBAAuB,CAAC,CAyDlC;AASD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE;IAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG,SAAS,EAC3D,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAWT"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { DefaultBinder } from '../../adapters/binding.js';
|
|
2
2
|
import { getCompiledDtoBindingPlan } from '../../adapters/dto-binding-plan.js';
|
|
3
3
|
import { HttpDtoValidationAdapter } from '../../adapters/dto-validation-adapter.js';
|
|
4
|
-
import { SseResponse } from '../../context/sse.js';
|
|
4
|
+
import { SseResponse, waitForSseResponseCompletion } from '../../context/sse.js';
|
|
5
5
|
import { RequestAbortedError } from '../../errors.js';
|
|
6
6
|
import { writeSuccessResponse } from '../dispatch-response-policy.js';
|
|
7
|
+
import { isRequestAborted } from '../request-abort.js';
|
|
7
8
|
const defaultBinder = new DefaultBinder();
|
|
8
9
|
const defaultValidator = new HttpDtoValidationAdapter();
|
|
9
10
|
/**
|
|
@@ -49,7 +50,12 @@ export async function executeFastPath(options) {
|
|
|
49
50
|
if (isRequestAborted(request)) {
|
|
50
51
|
throw new RequestAbortedError();
|
|
51
52
|
}
|
|
52
|
-
if (
|
|
53
|
+
if (result instanceof SseResponse) {
|
|
54
|
+
await waitForSseResponseCompletion(result);
|
|
55
|
+
if (isRequestAborted(request)) {
|
|
56
|
+
throw new RequestAbortedError();
|
|
57
|
+
}
|
|
58
|
+
} else if (!response.committed) {
|
|
53
59
|
const writeResult = writeSuccessResponse(handler, request, response, result, contentNegotiation, requestContext);
|
|
54
60
|
if (isThenable(writeResult)) {
|
|
55
61
|
await writeResult;
|
|
@@ -88,7 +94,4 @@ export function shouldUseFastPathForRequest(eligibility, request) {
|
|
|
88
94
|
return false;
|
|
89
95
|
}
|
|
90
96
|
return true;
|
|
91
|
-
}
|
|
92
|
-
function isRequestAborted(request) {
|
|
93
|
-
return request.isAborted?.() ?? request.signal?.aborted === true;
|
|
94
97
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { FrameworkRequest, RequestContext } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reports whether the adapter-provided request abort probes have fired.
|
|
4
|
+
*
|
|
5
|
+
* @param request Adapter-normalized request to inspect.
|
|
6
|
+
* @returns Whether transport cancellation has been observed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isRequestAborted(request: FrameworkRequest): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Reports whether a request context was cancelled by transport or response-stream closure.
|
|
11
|
+
*
|
|
12
|
+
* @param context Request context to inspect.
|
|
13
|
+
* @returns Whether cancellation has been observed.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isRequestContextAborted(context: RequestContext): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Marks a request context as cancelled by a response-stream disconnect.
|
|
18
|
+
*
|
|
19
|
+
* @param context Request context whose stream closed unexpectedly.
|
|
20
|
+
*/
|
|
21
|
+
export declare function markRequestContextAborted(context: RequestContext): void;
|
|
22
|
+
//# sourceMappingURL=request-abort.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-abort.d.ts","sourceRoot":"","sources":["../../src/dispatch/request-abort.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIpE;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAEnE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAGxE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAEvE"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const REQUEST_ABORTED_BY_RESPONSE_STREAM = Symbol('fluo.http.requestAbortedByResponseStream');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reports whether the adapter-provided request abort probes have fired.
|
|
5
|
+
*
|
|
6
|
+
* @param request Adapter-normalized request to inspect.
|
|
7
|
+
* @returns Whether transport cancellation has been observed.
|
|
8
|
+
*/
|
|
9
|
+
export function isRequestAborted(request) {
|
|
10
|
+
return request.isAborted?.() === true || request.signal?.aborted === true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Reports whether a request context was cancelled by transport or response-stream closure.
|
|
15
|
+
*
|
|
16
|
+
* @param context Request context to inspect.
|
|
17
|
+
* @returns Whether cancellation has been observed.
|
|
18
|
+
*/
|
|
19
|
+
export function isRequestContextAborted(context) {
|
|
20
|
+
return isRequestAborted(context.request) || context.metadata[REQUEST_ABORTED_BY_RESPONSE_STREAM] === true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Marks a request context as cancelled by a response-stream disconnect.
|
|
25
|
+
*
|
|
26
|
+
* @param context Request context whose stream closed unexpectedly.
|
|
27
|
+
*/
|
|
28
|
+
export function markRequestContextAborted(context) {
|
|
29
|
+
context.metadata[REQUEST_ABORTED_BY_RESPONSE_STREAM] = true;
|
|
30
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { FrameworkRequest, FrameworkResponse, HandlerDescriptor, RequestContext, ResponseValidators } from '../types.js';
|
|
2
|
+
/** Shared response-entry brand recognized by the HTTP success-response policy. */
|
|
3
|
+
export declare const FRAMEWORK_RESPONSE_WRITER: unique symbol;
|
|
4
|
+
/** Request-local metadata key for response-value finalization before response writing. */
|
|
5
|
+
export declare const FRAMEWORK_RESPONSE_VALUE_FINALIZER: unique symbol;
|
|
6
|
+
/** Context supplied when an integration writes a successful framework response. */
|
|
7
|
+
export type FrameworkResponseWriterContext = {
|
|
8
|
+
readonly applySuccessResponseMetadata: () => void;
|
|
9
|
+
readonly handler: HandlerDescriptor;
|
|
10
|
+
readonly request: FrameworkRequest;
|
|
11
|
+
readonly requestContext: RequestContext;
|
|
12
|
+
readonly response: FrameworkResponse;
|
|
13
|
+
/** Validators resolved by the dispatcher before the handler executed. */
|
|
14
|
+
readonly validators: ResponseValidators | undefined;
|
|
15
|
+
/** Finalized handler value that owns this response writer. */
|
|
16
|
+
readonly value: unknown;
|
|
17
|
+
};
|
|
18
|
+
/** Writer installed by an HTTP integration on one response entry. */
|
|
19
|
+
export type FrameworkResponseWriter = (context: FrameworkResponseWriterContext) => ReturnType<FrameworkResponse['send']> | void;
|
|
20
|
+
/** Context supplied when an integration finalizes a handler result before response writing. */
|
|
21
|
+
export type FrameworkResponseValueFinalizerContext = {
|
|
22
|
+
readonly handler: HandlerDescriptor;
|
|
23
|
+
readonly request: FrameworkRequest;
|
|
24
|
+
readonly requestContext: RequestContext;
|
|
25
|
+
readonly response: FrameworkResponse;
|
|
26
|
+
readonly value: unknown;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Request-local transformation applied before the HTTP policy selects a response writer.
|
|
30
|
+
*
|
|
31
|
+
* Finalizers may resolve asynchronously. The dispatcher awaits their results and
|
|
32
|
+
* routes thrown errors and rejections through its normal error policy.
|
|
33
|
+
*/
|
|
34
|
+
export type FrameworkResponseValueFinalizer = (context: FrameworkResponseValueFinalizerContext) => unknown | Promise<unknown>;
|
|
35
|
+
/**
|
|
36
|
+
* Brands a response entry with an integration-owned writer.
|
|
37
|
+
*
|
|
38
|
+
* @param entry Response entry returned by an HTTP handler.
|
|
39
|
+
* @param writer Writer invoked by the shared success-response policy.
|
|
40
|
+
* @returns The branded response entry.
|
|
41
|
+
*/
|
|
42
|
+
export declare function registerFrameworkResponseWriter<Entry extends object>(entry: Entry, writer: FrameworkResponseWriter): Entry;
|
|
43
|
+
/**
|
|
44
|
+
* Registers a request-local handler-result finalizer for an HTTP integration.
|
|
45
|
+
*
|
|
46
|
+
* @param context Active request context whose metadata carries the finalizer.
|
|
47
|
+
* @param finalizer Transformation applied before response-writer selection.
|
|
48
|
+
*
|
|
49
|
+
* @remarks
|
|
50
|
+
* Multiple registrations compose in registration order. Each later finalizer
|
|
51
|
+
* receives the previous finalizer's resolved value, and any thrown error or
|
|
52
|
+
* rejection is handled by the dispatcher's existing error policy.
|
|
53
|
+
*/
|
|
54
|
+
export declare function registerFrameworkResponseValueFinalizer(context: Pick<RequestContext, 'metadata'>, finalizer: FrameworkResponseValueFinalizer): void;
|
|
55
|
+
//# sourceMappingURL=response-integration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-integration.d.ts","sourceRoot":"","sources":["../../src/dispatch/response-integration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAErB,kFAAkF;AAClF,eAAO,MAAM,yBAAyB,eAAyC,CAAC;AAEhF,0FAA0F;AAC1F,eAAO,MAAM,kCAAkC,eAAiD,CAAC;AAEjG,mFAAmF;AACnF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,QAAQ,CAAC,4BAA4B,EAAE,MAAM,IAAI,CAAC;IAClD,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,kBAAkB,GAAG,SAAS,CAAC;IACpD,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,qEAAqE;AACrE,MAAM,MAAM,uBAAuB,GAAG,CACpC,OAAO,EAAE,8BAA8B,KACpC,UAAU,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC;AAElD,+FAA+F;AAC/F,MAAM,MAAM,sCAAsC,GAAG;IACnD,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,+BAA+B,GAAG,CAC5C,OAAO,EAAE,sCAAsC,KAC5C,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,SAAS,MAAM,EAClE,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,uBAAuB,GAC9B,KAAK,CAOP;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uCAAuC,CACrD,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,EACzC,SAAS,EAAE,+BAA+B,GACzC,IAAI,CAiBN"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** Shared response-entry brand recognized by the HTTP success-response policy. */
|
|
2
|
+
export const FRAMEWORK_RESPONSE_WRITER = Symbol.for('fluo.http.responseWriter');
|
|
3
|
+
|
|
4
|
+
/** Request-local metadata key for response-value finalization before response writing. */
|
|
5
|
+
export const FRAMEWORK_RESPONSE_VALUE_FINALIZER = Symbol.for('fluo.http.responseValueFinalizer');
|
|
6
|
+
|
|
7
|
+
/** Context supplied when an integration writes a successful framework response. */
|
|
8
|
+
|
|
9
|
+
/** Writer installed by an HTTP integration on one response entry. */
|
|
10
|
+
|
|
11
|
+
/** Context supplied when an integration finalizes a handler result before response writing. */
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Request-local transformation applied before the HTTP policy selects a response writer.
|
|
15
|
+
*
|
|
16
|
+
* Finalizers may resolve asynchronously. The dispatcher awaits their results and
|
|
17
|
+
* routes thrown errors and rejections through its normal error policy.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Brands a response entry with an integration-owned writer.
|
|
22
|
+
*
|
|
23
|
+
* @param entry Response entry returned by an HTTP handler.
|
|
24
|
+
* @param writer Writer invoked by the shared success-response policy.
|
|
25
|
+
* @returns The branded response entry.
|
|
26
|
+
*/
|
|
27
|
+
export function registerFrameworkResponseWriter(entry, writer) {
|
|
28
|
+
Object.defineProperty(entry, FRAMEWORK_RESPONSE_WRITER, {
|
|
29
|
+
enumerable: false,
|
|
30
|
+
value: writer
|
|
31
|
+
});
|
|
32
|
+
return entry;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Registers a request-local handler-result finalizer for an HTTP integration.
|
|
37
|
+
*
|
|
38
|
+
* @param context Active request context whose metadata carries the finalizer.
|
|
39
|
+
* @param finalizer Transformation applied before response-writer selection.
|
|
40
|
+
*
|
|
41
|
+
* @remarks
|
|
42
|
+
* Multiple registrations compose in registration order. Each later finalizer
|
|
43
|
+
* receives the previous finalizer's resolved value, and any thrown error or
|
|
44
|
+
* rejection is handled by the dispatcher's existing error policy.
|
|
45
|
+
*/
|
|
46
|
+
export function registerFrameworkResponseValueFinalizer(context, finalizer) {
|
|
47
|
+
const existingFinalizer = context.metadata[FRAMEWORK_RESPONSE_VALUE_FINALIZER];
|
|
48
|
+
if (typeof existingFinalizer !== 'function') {
|
|
49
|
+
context.metadata[FRAMEWORK_RESPONSE_VALUE_FINALIZER] = finalizer;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const previousFinalizer = existingFinalizer;
|
|
53
|
+
context.metadata[FRAMEWORK_RESPONSE_VALUE_FINALIZER] = async finalizerContext => finalizer({
|
|
54
|
+
...finalizerContext,
|
|
55
|
+
value: await previousFinalizer(finalizerContext)
|
|
56
|
+
});
|
|
57
|
+
}
|
package/dist/errors.d.ts
CHANGED
|
@@ -11,6 +11,10 @@ export declare class RouteConflictError extends FluoError {
|
|
|
11
11
|
export declare class InvalidRoutePathError extends FluoError {
|
|
12
12
|
constructor(message: string);
|
|
13
13
|
}
|
|
14
|
+
/** Error thrown when a generic route method is not a valid HTTP token or uses the reserved `ALL` sentinel. */
|
|
15
|
+
export declare class InvalidHttpMethodError extends FluoError {
|
|
16
|
+
constructor(message: string);
|
|
17
|
+
}
|
|
14
18
|
/**
|
|
15
19
|
* Error thrown when no handler matches the incoming request path or method.
|
|
16
20
|
*/
|
|
@@ -23,4 +27,10 @@ export declare class HandlerNotFoundError extends FluoError {
|
|
|
23
27
|
export declare class RequestAbortedError extends FluoError {
|
|
24
28
|
constructor(message?: string);
|
|
25
29
|
}
|
|
30
|
+
/** Error thrown when a supported adapter cannot emit one Early Hints response. */
|
|
31
|
+
export declare class EarlyHintsWriteError extends FluoError {
|
|
32
|
+
constructor(message?: string, options?: {
|
|
33
|
+
cause?: unknown;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
26
36
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,SAAS;gBACnC,OAAO,EAAE,MAAM;CAG5B;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,SAAS;gBACtC,OAAO,EAAE,MAAM;CAG5B;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,SAAS;gBACrC,OAAO,EAAE,MAAM;CAG5B;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,OAAO,SAA4C;CAGhE"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,SAAS;gBACnC,OAAO,EAAE,MAAM;CAG5B;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,SAAS;gBACtC,OAAO,EAAE,MAAM;CAG5B;AAED,8GAA8G;AAC9G,qBAAa,sBAAuB,SAAQ,SAAS;gBACvC,OAAO,EAAE,MAAM;CAG5B;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,SAAS;gBACrC,OAAO,EAAE,MAAM;CAG5B;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,OAAO,SAA4C;CAGhE;AAED,kFAAkF;AAClF,qBAAa,oBAAqB,SAAQ,SAAS;gBAE/C,OAAO,SAA0C,EACjD,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;CAOpC"}
|
package/dist/errors.js
CHANGED
|
@@ -22,6 +22,15 @@ export class InvalidRoutePathError extends FluoError {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/** Error thrown when a generic route method is not a valid HTTP token or uses the reserved `ALL` sentinel. */
|
|
26
|
+
export class InvalidHttpMethodError extends FluoError {
|
|
27
|
+
constructor(message) {
|
|
28
|
+
super(message, {
|
|
29
|
+
code: 'INVALID_HTTP_METHOD'
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
25
34
|
/**
|
|
26
35
|
* Error thrown when no handler matches the incoming request path or method.
|
|
27
36
|
*/
|
|
@@ -42,4 +51,14 @@ export class RequestAbortedError extends FluoError {
|
|
|
42
51
|
code: 'REQUEST_ABORTED'
|
|
43
52
|
});
|
|
44
53
|
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Error thrown when a supported adapter cannot emit one Early Hints response. */
|
|
57
|
+
export class EarlyHintsWriteError extends FluoError {
|
|
58
|
+
constructor(message = 'Failed to write HTTP 103 Early Hints.', options = {}) {
|
|
59
|
+
super(message, {
|
|
60
|
+
cause: options.cause,
|
|
61
|
+
code: 'EARLY_HINTS_WRITE_FAILED'
|
|
62
|
+
});
|
|
63
|
+
}
|
|
45
64
|
}
|
package/dist/guards.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ import type { GuardContext, GuardLike } from './types.js';
|
|
|
6
6
|
* @param context The context.
|
|
7
7
|
* @returns The run guard chain result.
|
|
8
8
|
*/
|
|
9
|
-
export declare function runGuardChain(definitions: GuardLike[], context: GuardContext): Promise<void>;
|
|
9
|
+
export declare function runGuardChain(definitions: readonly GuardLike[], context: GuardContext): Promise<void>;
|
|
10
10
|
//# sourceMappingURL=guards.d.ts.map
|
package/dist/guards.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../src/guards.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAS,YAAY,EAAE,SAAS,EAAkB,MAAM,YAAY,CAAC;AAcjF;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,WAAW,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../src/guards.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAS,YAAY,EAAE,SAAS,EAAkB,MAAM,YAAY,CAAC;AAcjF;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,WAAW,EAAE,SAAS,SAAS,EAAE,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAa3G"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { FrameworkRequest, FrameworkResponse } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reads one request header without flattening multi-value arrays.
|
|
4
|
+
*
|
|
5
|
+
* @param request Adapter-normalized request carrying the inbound headers map.
|
|
6
|
+
* @param name Header name to resolve case-insensitively.
|
|
7
|
+
* @returns The original scalar, array, or `undefined` stored on the request.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getRequestHeader(request: FrameworkRequest, name: string): string | string[] | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Reads one response header without flattening multi-value arrays.
|
|
12
|
+
*
|
|
13
|
+
* @param response Adapter-normalized response carrying the outbound headers map.
|
|
14
|
+
* @param name Header name to resolve case-insensitively.
|
|
15
|
+
* @returns The original scalar, array, or `undefined` stored on the response.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getResponseHeader(response: FrameworkResponse, name: string): string | string[] | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Checks whether one response header is present without mutating the response.
|
|
20
|
+
*
|
|
21
|
+
* @param response Adapter-normalized response carrying the outbound headers map.
|
|
22
|
+
* @param name Header name to resolve case-insensitively.
|
|
23
|
+
* @returns `true` when a matching response header has a value.
|
|
24
|
+
*/
|
|
25
|
+
export declare function hasResponseHeader(response: FrameworkResponse, name: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Creates a portable Content-Disposition value with ASCII and UTF-8 filename parameters.
|
|
28
|
+
*
|
|
29
|
+
* @param disposition Whether the response is an `attachment` or rendered `inline`.
|
|
30
|
+
* @param filename Original filename to encode for a response header.
|
|
31
|
+
* @returns A Content-Disposition field value with escaped ASCII and RFC 8187 UTF-8 parameters.
|
|
32
|
+
* @throws {TypeError} When the disposition is unsupported or the filename contains CR or LF.
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildContentDisposition(disposition: 'attachment' | 'inline', filename: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Reads the first non-empty request header value across duplicate case variants.
|
|
37
|
+
*
|
|
38
|
+
* @param request Adapter-normalized request carrying the inbound headers map.
|
|
39
|
+
* @param name Header name to resolve case-insensitively.
|
|
40
|
+
* @returns The first trimmed scalar or joined array value that is not blank.
|
|
41
|
+
*/
|
|
42
|
+
export declare function readFirstNonEmptyRequestHeaderValue(request: FrameworkRequest, name: string): string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Reads and joins every non-empty request header field value in wire order.
|
|
45
|
+
*
|
|
46
|
+
* @param request Adapter-normalized request carrying the inbound headers map.
|
|
47
|
+
* @param name Header name to resolve case-insensitively.
|
|
48
|
+
* @returns Comma-delimited values from every matching field, or `undefined` when all are blank.
|
|
49
|
+
*/
|
|
50
|
+
export declare function readJoinedNonEmptyRequestHeaderValues(request: FrameworkRequest, name: string): string | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Appends one or more fields to the response `Vary` header with case-insensitive deduplication.
|
|
53
|
+
*
|
|
54
|
+
* @param response Mutable framework response facade that owns the header map.
|
|
55
|
+
* @param fields Header field names or comma-delimited field lists to append.
|
|
56
|
+
* @returns Nothing. The helper updates the response header map in place when needed.
|
|
57
|
+
*/
|
|
58
|
+
export declare function appendVaryHeader(response: FrameworkResponse, ...fields: string[]): void;
|
|
59
|
+
//# sourceMappingURL=header-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header-helpers.d.ts","sourceRoot":"","sources":["../src/header-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAmEtE;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAE/B;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAE/B;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAET;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,YAAY,GAAG,QAAQ,EACpC,QAAQ,EAAE,MAAM,GACf,MAAM,CAsCR;AAED;;;;;;GAMG;AACH,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAmBpB;AAED;;;;;;GAMG;AACH,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAqBpB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,iBAAiB,EAC3B,GAAG,MAAM,EAAE,MAAM,EAAE,GAClB,IAAI,CA0CN"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
function normalizeHeaderName(name) {
|
|
2
|
+
const normalized = name.trim().toLowerCase();
|
|
3
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
4
|
+
}
|
|
5
|
+
function findCaseInsensitiveHeaderEntry(headers, headerName) {
|
|
6
|
+
const normalizedHeaderName = normalizeHeaderName(headerName);
|
|
7
|
+
if (!normalizedHeaderName) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
let firstMatch;
|
|
11
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
12
|
+
if (name.toLowerCase() !== normalizedHeaderName) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (value !== undefined) {
|
|
16
|
+
return [name, value];
|
|
17
|
+
}
|
|
18
|
+
firstMatch ??= [name, value];
|
|
19
|
+
}
|
|
20
|
+
return firstMatch;
|
|
21
|
+
}
|
|
22
|
+
function parseHeaderTokens(values) {
|
|
23
|
+
const tokens = [];
|
|
24
|
+
for (const value of values) {
|
|
25
|
+
for (const token of value.split(',')) {
|
|
26
|
+
const normalized = token.trim();
|
|
27
|
+
if (normalized.length > 0) {
|
|
28
|
+
tokens.push(normalized);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return tokens;
|
|
33
|
+
}
|
|
34
|
+
function readJoinedNonEmptyHeaderValue(value) {
|
|
35
|
+
if (Array.isArray(value)) {
|
|
36
|
+
const normalizedValues = value.map(entry => entry.trim()).filter(entry => entry.length > 0);
|
|
37
|
+
return normalizedValues.length > 0 ? normalizedValues.join(',') : undefined;
|
|
38
|
+
}
|
|
39
|
+
const normalized = value?.trim();
|
|
40
|
+
return normalized ? normalized : undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Reads one request header without flattening multi-value arrays.
|
|
45
|
+
*
|
|
46
|
+
* @param request Adapter-normalized request carrying the inbound headers map.
|
|
47
|
+
* @param name Header name to resolve case-insensitively.
|
|
48
|
+
* @returns The original scalar, array, or `undefined` stored on the request.
|
|
49
|
+
*/
|
|
50
|
+
export function getRequestHeader(request, name) {
|
|
51
|
+
return findCaseInsensitiveHeaderEntry(request.headers, name)?.[1];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Reads one response header without flattening multi-value arrays.
|
|
56
|
+
*
|
|
57
|
+
* @param response Adapter-normalized response carrying the outbound headers map.
|
|
58
|
+
* @param name Header name to resolve case-insensitively.
|
|
59
|
+
* @returns The original scalar, array, or `undefined` stored on the response.
|
|
60
|
+
*/
|
|
61
|
+
export function getResponseHeader(response, name) {
|
|
62
|
+
return findCaseInsensitiveHeaderEntry(response.headers, name)?.[1];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Checks whether one response header is present without mutating the response.
|
|
67
|
+
*
|
|
68
|
+
* @param response Adapter-normalized response carrying the outbound headers map.
|
|
69
|
+
* @param name Header name to resolve case-insensitively.
|
|
70
|
+
* @returns `true` when a matching response header has a value.
|
|
71
|
+
*/
|
|
72
|
+
export function hasResponseHeader(response, name) {
|
|
73
|
+
return getResponseHeader(response, name) !== undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Creates a portable Content-Disposition value with ASCII and UTF-8 filename parameters.
|
|
78
|
+
*
|
|
79
|
+
* @param disposition Whether the response is an `attachment` or rendered `inline`.
|
|
80
|
+
* @param filename Original filename to encode for a response header.
|
|
81
|
+
* @returns A Content-Disposition field value with escaped ASCII and RFC 8187 UTF-8 parameters.
|
|
82
|
+
* @throws {TypeError} When the disposition is unsupported or the filename contains CR or LF.
|
|
83
|
+
*/
|
|
84
|
+
export function buildContentDisposition(disposition, filename) {
|
|
85
|
+
if (disposition !== 'attachment' && disposition !== 'inline') {
|
|
86
|
+
throw new TypeError('Content-Disposition disposition must be attachment or inline.');
|
|
87
|
+
}
|
|
88
|
+
if (filename.includes('\r') || filename.includes('\n')) {
|
|
89
|
+
throw new TypeError('Content-Disposition filenames cannot contain CR or LF characters.');
|
|
90
|
+
}
|
|
91
|
+
const asciiFilename = filename.replace(/[^\x20-\x7E]/gu, '?').replaceAll('\\', '\\\\').replaceAll('"', '\\"');
|
|
92
|
+
const utf8Filename = Array.from(new TextEncoder().encode(filename), byte => {
|
|
93
|
+
const isAlphaNumeric = byte >= 0x30 && byte <= 0x39 || byte >= 0x41 && byte <= 0x5a || byte >= 0x61 && byte <= 0x7a;
|
|
94
|
+
const isRfc8187Punctuation = byte === 0x21 || byte === 0x23 || byte === 0x24 || byte === 0x26 || byte === 0x2b || byte === 0x2d || byte === 0x2e || byte === 0x5e || byte === 0x5f || byte === 0x60 || byte === 0x7c || byte === 0x7e;
|
|
95
|
+
return isAlphaNumeric || isRfc8187Punctuation ? String.fromCharCode(byte) : `%${byte.toString(16).toUpperCase().padStart(2, '0')}`;
|
|
96
|
+
}).join('');
|
|
97
|
+
return `${disposition}; filename="${asciiFilename}"; filename*=UTF-8''${utf8Filename}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Reads the first non-empty request header value across duplicate case variants.
|
|
102
|
+
*
|
|
103
|
+
* @param request Adapter-normalized request carrying the inbound headers map.
|
|
104
|
+
* @param name Header name to resolve case-insensitively.
|
|
105
|
+
* @returns The first trimmed scalar or joined array value that is not blank.
|
|
106
|
+
*/
|
|
107
|
+
export function readFirstNonEmptyRequestHeaderValue(request, name) {
|
|
108
|
+
const normalizedHeaderName = normalizeHeaderName(name);
|
|
109
|
+
if (!normalizedHeaderName) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
for (const [headerName, value] of Object.entries(request.headers)) {
|
|
113
|
+
if (headerName.toLowerCase() !== normalizedHeaderName) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const normalizedValue = readJoinedNonEmptyHeaderValue(value);
|
|
117
|
+
if (normalizedValue !== undefined) {
|
|
118
|
+
return normalizedValue;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Reads and joins every non-empty request header field value in wire order.
|
|
126
|
+
*
|
|
127
|
+
* @param request Adapter-normalized request carrying the inbound headers map.
|
|
128
|
+
* @param name Header name to resolve case-insensitively.
|
|
129
|
+
* @returns Comma-delimited values from every matching field, or `undefined` when all are blank.
|
|
130
|
+
*/
|
|
131
|
+
export function readJoinedNonEmptyRequestHeaderValues(request, name) {
|
|
132
|
+
const normalizedHeaderName = normalizeHeaderName(name);
|
|
133
|
+
if (!normalizedHeaderName) {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
const values = [];
|
|
137
|
+
for (const [headerName, value] of Object.entries(request.headers)) {
|
|
138
|
+
if (headerName.toLowerCase() !== normalizedHeaderName) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const normalizedValue = readJoinedNonEmptyHeaderValue(value);
|
|
142
|
+
if (normalizedValue !== undefined) {
|
|
143
|
+
values.push(normalizedValue);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return values.length > 0 ? values.join(',') : undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Appends one or more fields to the response `Vary` header with case-insensitive deduplication.
|
|
151
|
+
*
|
|
152
|
+
* @param response Mutable framework response facade that owns the header map.
|
|
153
|
+
* @param fields Header field names or comma-delimited field lists to append.
|
|
154
|
+
* @returns Nothing. The helper updates the response header map in place when needed.
|
|
155
|
+
*/
|
|
156
|
+
export function appendVaryHeader(response, ...fields) {
|
|
157
|
+
const existingEntries = Object.entries(response.headers).filter(([name]) => name.toLowerCase() === 'vary');
|
|
158
|
+
const existingTokens = parseHeaderTokens(existingEntries.flatMap(([, value]) => Array.isArray(value) ? value : [value]));
|
|
159
|
+
const appendedTokens = parseHeaderTokens(fields);
|
|
160
|
+
const mergedTokens = [...existingTokens, ...appendedTokens];
|
|
161
|
+
if (mergedTokens.length === 0) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const canonicalHeaderName = existingEntries[0]?.[0] ?? 'Vary';
|
|
165
|
+
for (const [name] of existingEntries.slice(1)) {
|
|
166
|
+
delete response.headers[name];
|
|
167
|
+
}
|
|
168
|
+
if (mergedTokens.some(token => token === '*')) {
|
|
169
|
+
response.setHeader(canonicalHeaderName, '*');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const dedupedTokens = [];
|
|
173
|
+
const seen = new Set();
|
|
174
|
+
for (const token of mergedTokens) {
|
|
175
|
+
const normalized = token.toLowerCase();
|
|
176
|
+
if (seen.has(normalized)) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
seen.add(normalized);
|
|
180
|
+
dedupedTokens.push(token);
|
|
181
|
+
}
|
|
182
|
+
if (dedupedTokens.length > 0) {
|
|
183
|
+
response.setHeader(canonicalHeaderName, dedupedTokens.join(', '));
|
|
184
|
+
}
|
|
185
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,2 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './middleware/correlation.js';
|
|
3
|
-
export * from './middleware/cors.js';
|
|
4
|
-
export { All, Controller, Convert, Delete, FromBody, FromCookie, FromHeader, FromPath, FromQuery, Get, Head, Header, HttpCode, Optional, Options, Patch, Post, Produces, Put, Redirect, RequestDto, Sse, UseGuards, UseInterceptors, Version, } from './decorators.js';
|
|
5
|
-
export * from './dispatch/dispatcher.js';
|
|
6
|
-
export type { FastPathEligibility, FastPathStats } from './dispatch/fast-path/index.js';
|
|
7
|
-
export { FAST_PATH_ELIGIBILITY_SYMBOL, FAST_PATH_STATS_SYMBOL, formatFastPathStats, getDispatcherFastPathStats, } from './dispatch/dispatcher.js';
|
|
8
|
-
export * from './errors.js';
|
|
9
|
-
export * from './exceptions.js';
|
|
10
|
-
export * from './mapping.js';
|
|
11
|
-
export { forRoutes, isMiddlewareRouteConfig, matchRoutePattern, normalizeRoutePattern, } from './middleware/middleware.js';
|
|
12
|
-
export * from './middleware/rate-limit.js';
|
|
13
|
-
export * from './context/request-context.js';
|
|
14
|
-
export * from './middleware/security-headers.js';
|
|
15
|
-
export * from './context/sse.js';
|
|
16
|
-
export * from './types.js';
|
|
1
|
+
export * from './index.portable.js';
|
|
17
2
|
//# sourceMappingURL=index.d.ts.map
|