@fluojs/http 1.1.2 → 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.
Files changed (125) hide show
  1. package/README.ko.md +430 -15
  2. package/README.md +437 -15
  3. package/dist/access-log-observer.d.ts +77 -0
  4. package/dist/access-log-observer.d.ts.map +1 -0
  5. package/dist/access-log-observer.js +171 -0
  6. package/dist/adapter.d.ts +9 -0
  7. package/dist/adapter.d.ts.map +1 -1
  8. package/dist/adapter.js +15 -1
  9. package/dist/adapters/binding.d.ts.map +1 -1
  10. package/dist/adapters/binding.js +18 -16
  11. package/dist/adapters/dto-binding-plan.d.ts +2 -2
  12. package/dist/adapters/dto-binding-plan.d.ts.map +1 -1
  13. package/dist/adapters/dto-binding-plan.js +13 -4
  14. package/dist/adapters/dto-validation-adapter.d.ts +2 -1
  15. package/dist/adapters/dto-validation-adapter.d.ts.map +1 -1
  16. package/dist/adapters/dto-validation-adapter.js +2 -2
  17. package/dist/byte-range-response.d.ts +71 -0
  18. package/dist/byte-range-response.d.ts.map +1 -0
  19. package/dist/byte-range-response.js +347 -0
  20. package/dist/client-identity.d.ts +8 -4
  21. package/dist/client-identity.d.ts.map +1 -1
  22. package/dist/client-identity.js +12 -93
  23. package/dist/compiled-route-identity.d.ts +17 -0
  24. package/dist/compiled-route-identity.d.ts.map +1 -0
  25. package/dist/compiled-route-identity.js +29 -0
  26. package/dist/connection.d.ts +67 -0
  27. package/dist/connection.d.ts.map +1 -0
  28. package/dist/connection.js +480 -0
  29. package/dist/context/request-context-node-store.d.ts +6 -0
  30. package/dist/context/request-context-node-store.d.ts.map +1 -1
  31. package/dist/context/request-context-node-store.js +14 -0
  32. package/dist/context/request-context.d.ts +5 -5
  33. package/dist/context/request-context.d.ts.map +1 -1
  34. package/dist/context/request-context.js +8 -83
  35. package/dist/context/request-id.d.ts +10 -0
  36. package/dist/context/request-id.d.ts.map +1 -0
  37. package/dist/context/request-id.js +27 -0
  38. package/dist/context/sse.d.ts +13 -0
  39. package/dist/context/sse.d.ts.map +1 -1
  40. package/dist/context/sse.js +36 -6
  41. package/dist/cookie-helpers.d.ts +47 -0
  42. package/dist/cookie-helpers.d.ts.map +1 -0
  43. package/dist/cookie-helpers.js +166 -0
  44. package/dist/decorators.d.ts +52 -19
  45. package/dist/decorators.d.ts.map +1 -1
  46. package/dist/decorators.js +57 -10
  47. package/dist/dispatch/conditional-request-policy.d.ts +67 -0
  48. package/dist/dispatch/conditional-request-policy.d.ts.map +1 -0
  49. package/dist/dispatch/conditional-request-policy.js +297 -0
  50. package/dist/dispatch/dispatch-content-negotiation.d.ts +8 -0
  51. package/dist/dispatch/dispatch-content-negotiation.d.ts.map +1 -1
  52. package/dist/dispatch/dispatch-content-negotiation.js +89 -42
  53. package/dist/dispatch/dispatch-error-negotiation.d.ts +26 -0
  54. package/dist/dispatch/dispatch-error-negotiation.d.ts.map +1 -0
  55. package/dist/dispatch/dispatch-error-negotiation.js +110 -0
  56. package/dist/dispatch/dispatch-error-policy.d.ts +1 -10
  57. package/dist/dispatch/dispatch-error-policy.d.ts.map +1 -1
  58. package/dist/dispatch/dispatch-error-policy.js +1 -33
  59. package/dist/dispatch/dispatch-error-representation.d.ts +18 -0
  60. package/dist/dispatch/dispatch-error-representation.d.ts.map +1 -0
  61. package/dist/dispatch/dispatch-error-representation.js +125 -0
  62. package/dist/dispatch/dispatch-response-metadata.d.ts +10 -0
  63. package/dist/dispatch/dispatch-response-metadata.d.ts.map +1 -0
  64. package/dist/dispatch/dispatch-response-metadata.js +12 -0
  65. package/dist/dispatch/dispatch-response-policy.d.ts +33 -5
  66. package/dist/dispatch/dispatch-response-policy.d.ts.map +1 -1
  67. package/dist/dispatch/dispatch-response-policy.js +149 -18
  68. package/dist/dispatch/dispatcher.d.ts +5 -1
  69. package/dist/dispatch/dispatcher.d.ts.map +1 -1
  70. package/dist/dispatch/dispatcher.js +178 -107
  71. package/dist/dispatch/fast-path/debug-visibility.d.ts.map +1 -1
  72. package/dist/dispatch/fast-path/debug-visibility.js +6 -3
  73. package/dist/dispatch/fast-path/dispatcher-state.d.ts +10 -0
  74. package/dist/dispatch/fast-path/dispatcher-state.d.ts.map +1 -0
  75. package/dist/dispatch/fast-path/dispatcher-state.js +52 -0
  76. package/dist/dispatch/fast-path/eligibility-checker.d.ts.map +1 -1
  77. package/dist/dispatch/fast-path/eligibility-checker.js +27 -15
  78. package/dist/dispatch/fast-path/eligibility.d.ts +16 -10
  79. package/dist/dispatch/fast-path/eligibility.d.ts.map +1 -1
  80. package/dist/dispatch/fast-path/eligibility.js +10 -4
  81. package/dist/dispatch/fast-path/fast-path-executor.d.ts +14 -1
  82. package/dist/dispatch/fast-path/fast-path-executor.d.ts.map +1 -1
  83. package/dist/dispatch/fast-path/fast-path-executor.js +23 -6
  84. package/dist/dispatch/request-abort.d.ts +22 -0
  85. package/dist/dispatch/request-abort.d.ts.map +1 -0
  86. package/dist/dispatch/request-abort.js +30 -0
  87. package/dist/dispatch/response-integration.d.ts +55 -0
  88. package/dist/dispatch/response-integration.d.ts.map +1 -0
  89. package/dist/dispatch/response-integration.js +57 -0
  90. package/dist/errors.d.ts +10 -0
  91. package/dist/errors.d.ts.map +1 -1
  92. package/dist/errors.js +19 -0
  93. package/dist/guards.d.ts +1 -1
  94. package/dist/guards.d.ts.map +1 -1
  95. package/dist/header-helpers.d.ts +59 -0
  96. package/dist/header-helpers.d.ts.map +1 -0
  97. package/dist/header-helpers.js +185 -0
  98. package/dist/index.d.ts +1 -16
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +4 -15
  101. package/dist/index.portable.d.ts +25 -0
  102. package/dist/index.portable.d.ts.map +1 -0
  103. package/dist/index.portable.js +21 -0
  104. package/dist/internal.d.ts +3 -1
  105. package/dist/internal.d.ts.map +1 -1
  106. package/dist/internal.js +2 -0
  107. package/dist/mapping.d.ts.map +1 -1
  108. package/dist/mapping.js +100 -15
  109. package/dist/middleware/correlation.d.ts +9 -1
  110. package/dist/middleware/correlation.d.ts.map +1 -1
  111. package/dist/middleware/correlation.js +25 -14
  112. package/dist/middleware/cors.d.ts.map +1 -1
  113. package/dist/middleware/cors.js +7 -8
  114. package/dist/middleware/middleware.d.ts +3 -3
  115. package/dist/middleware/middleware.d.ts.map +1 -1
  116. package/dist/middleware/rate-limit.d.ts +13 -2
  117. package/dist/middleware/rate-limit.d.ts.map +1 -1
  118. package/dist/middleware/rate-limit.js +1 -0
  119. package/dist/static-assets.d.ts +77 -0
  120. package/dist/static-assets.d.ts.map +1 -0
  121. package/dist/static-assets.js +270 -0
  122. package/dist/types.d.ts +225 -17
  123. package/dist/types.d.ts.map +1 -1
  124. package/dist/types.js +71 -1
  125. package/package.json +19 -9
@@ -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 './adapter.js';
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,OAAO,EACL,GAAG,EACH,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAQ,EACR,UAAU,EACV,UAAU,EACV,QAAQ,EACR,SAAS,EACT,GAAG,EACH,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,GAAG,EACH,QAAQ,EACR,UAAU,EACV,GAAG,EACH,SAAS,EACT,eAAe,EACf,OAAO,GACR,MAAM,iBAAiB,CAAC;AACzB,cAAc,0BAA0B,CAAC;AACzC,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACxF,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,0BAA0B,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,OAAO,EACL,SAAS,EACT,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,4BAA4B,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,cAAc,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -1,15 +1,4 @@
1
- export * from './adapter.js';
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 { FAST_PATH_ELIGIBILITY_SYMBOL, FAST_PATH_STATS_SYMBOL, formatFastPathStats, getDispatcherFastPathStats } from './dispatch/dispatcher.js';
7
- export * from './errors.js';
8
- export * from './exceptions.js';
9
- export * from './mapping.js';
10
- export { forRoutes, isMiddlewareRouteConfig, matchRoutePattern, normalizeRoutePattern } from './middleware/middleware.js';
11
- export * from './middleware/rate-limit.js';
12
- export * from './context/request-context.js';
13
- export * from './middleware/security-headers.js';
14
- export * from './context/sse.js';
15
- export * from './types.js';
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { registerImmediateAsyncLocalStorageConstructor } from './context/request-context-node-store.js';
3
+ registerImmediateAsyncLocalStorageConstructor(AsyncLocalStorage);
4
+ export * from './index.portable.js';
@@ -0,0 +1,25 @@
1
+ export * from './adapter.js';
2
+ export * from './access-log-observer.js';
3
+ export { createByteRangeResponse, } from './byte-range-response.js';
4
+ export type { ByteRangeResponseOptions, ByteRangeResponseSource, } from './byte-range-response.js';
5
+ export { createStaticAssetsMiddleware, } from './static-assets.js';
6
+ export type { StaticAsset, StaticAssetAcceptedEncoding, StaticAssetContentEncoding, StaticAssetNotAcceptable, StaticAssetResolveContext, StaticAssetResolution, StaticAssetSource, StaticAssetsMiddleware, StaticAssetsMiddlewareOptions, } from './static-assets.js';
7
+ export * from './context/request-context.js';
8
+ export * from './context/sse.js';
9
+ export * from './connection.js';
10
+ export * from './cookie-helpers.js';
11
+ export { All, Controller, Convert, Delete, FromBody, FromCookie, FromFiles, FromHeader, FromPath, FromQuery, Get, Head, Header, HttpCode, Optional, Options, Patch, Post, Produces, Put, Query, Redirect, RequestDto, Route, Sse, UseGuards, UseInterceptors, Version, } from './decorators.js';
12
+ export * from './dispatch/dispatcher.js';
13
+ export { FAST_PATH_ELIGIBILITY_SYMBOL, FAST_PATH_STATS_SYMBOL, formatFastPathStats, getDispatcherFastPathStats, } from './dispatch/dispatcher.js';
14
+ export type { FastPathEligibility, FastPathStats } from './dispatch/fast-path/index.js';
15
+ export * from './errors.js';
16
+ export * from './exceptions.js';
17
+ export { appendVaryHeader, buildContentDisposition, getRequestHeader, getResponseHeader, hasResponseHeader, } from './header-helpers.js';
18
+ export * from './mapping.js';
19
+ export * from './middleware/correlation.js';
20
+ export * from './middleware/cors.js';
21
+ export { forRoutes, isMiddlewareRouteConfig, matchRoutePattern, normalizeRoutePattern, } from './middleware/middleware.js';
22
+ export * from './middleware/rate-limit.js';
23
+ export * from './middleware/security-headers.js';
24
+ export * from './types.js';
25
+ //# sourceMappingURL=index.portable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.portable.d.ts","sourceRoot":"","sources":["../src/index.portable.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,0BAA0B,CAAC;AACzC,OAAO,EACL,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,4BAA4B,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,WAAW,EACX,2BAA2B,EAC3B,0BAA0B,EAC1B,wBAAwB,EACxB,yBAAyB,EACzB,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,EACtB,6BAA6B,GAC9B,MAAM,oBAAoB,CAAC;AAC5B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EACL,GAAG,EACH,UAAU,EACV,OAAO,EACP,MAAM,EACN,QAAQ,EACR,UAAU,EACV,SAAS,EACT,UAAU,EACV,QAAQ,EACR,SAAS,EACT,GAAG,EACH,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,GAAG,EACH,KAAK,EACL,QAAQ,EACR,UAAU,EACV,KAAK,EACL,GAAG,EACH,SAAS,EACT,eAAe,EACf,OAAO,GACR,MAAM,iBAAiB,CAAC;AACzB,cAAc,0BAA0B,CAAC;AACzC,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACxF,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,OAAO,EACL,SAAS,EACT,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,4BAA4B,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kCAAkC,CAAC;AACjD,cAAc,YAAY,CAAC"}
@@ -0,0 +1,21 @@
1
+ export * from './adapter.js';
2
+ export * from './access-log-observer.js';
3
+ export { createByteRangeResponse } from './byte-range-response.js';
4
+ export { createStaticAssetsMiddleware } from './static-assets.js';
5
+ export * from './context/request-context.js';
6
+ export * from './context/sse.js';
7
+ export * from './connection.js';
8
+ export * from './cookie-helpers.js';
9
+ export { All, Controller, Convert, Delete, FromBody, FromCookie, FromFiles, FromHeader, FromPath, FromQuery, Get, Head, Header, HttpCode, Optional, Options, Patch, Post, Produces, Put, Query, Redirect, RequestDto, Route, Sse, UseGuards, UseInterceptors, Version } from './decorators.js';
10
+ export * from './dispatch/dispatcher.js';
11
+ export { FAST_PATH_ELIGIBILITY_SYMBOL, FAST_PATH_STATS_SYMBOL, formatFastPathStats, getDispatcherFastPathStats } from './dispatch/dispatcher.js';
12
+ export * from './errors.js';
13
+ export * from './exceptions.js';
14
+ export { appendVaryHeader, buildContentDisposition, getRequestHeader, getResponseHeader, hasResponseHeader } from './header-helpers.js';
15
+ export * from './mapping.js';
16
+ export * from './middleware/correlation.js';
17
+ export * from './middleware/cors.js';
18
+ export { forRoutes, isMiddlewareRouteConfig, matchRoutePattern, normalizeRoutePattern } from './middleware/middleware.js';
19
+ export * from './middleware/rate-limit.js';
20
+ export * from './middleware/security-headers.js';
21
+ export * from './types.js';
@@ -1,6 +1,8 @@
1
1
  export { createFetchStyleHttpAdapterRealtimeCapability, type HttpApplicationAdapter, } from './adapter.js';
2
2
  export { DefaultBinder } from './adapters/binding.js';
3
3
  export { resolveClientIdentity } from './client-identity.js';
4
+ export { getCompiledRouteIdentity } from './compiled-route-identity.js';
5
+ export { FRAMEWORK_RESPONSE_VALUE_FINALIZER, FRAMEWORK_RESPONSE_WRITER, registerFrameworkResponseValueFinalizer, registerFrameworkResponseWriter, type FrameworkResponseValueFinalizer, type FrameworkResponseValueFinalizerContext, type FrameworkResponseWriter, type FrameworkResponseWriterContext, } from './dispatch/response-integration.js';
6
+ export { attachFrameworkRequestNativeRouteHandoff, bindRawRequestNativeRouteHandoff, consumeRawRequestNativeRouteHandoff, isRoutePathNormalizationSensitive, type NativeRouteHandoff, readFrameworkRequestNativeRouteHandoff, } from './dispatch/native-route-handoff.js';
4
7
  export type { Dispatcher } from './types.js';
5
- export { attachFrameworkRequestNativeRouteHandoff, bindRawRequestNativeRouteHandoff, consumeRawRequestNativeRouteHandoff, isRoutePathNormalizationSensitive, readFrameworkRequestNativeRouteHandoff, type NativeRouteHandoff, } from './dispatch/native-route-handoff.js';
6
8
  //# sourceMappingURL=internal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6CAA6C,EAC7C,KAAK,sBAAsB,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EACL,wCAAwC,EACxC,gCAAgC,EAChC,mCAAmC,EACnC,iCAAiC,EACjC,sCAAsC,EACtC,KAAK,kBAAkB,GACxB,MAAM,oCAAoC,CAAC"}
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6CAA6C,EAC7C,KAAK,sBAAsB,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,kCAAkC,EAClC,yBAAyB,EACzB,uCAAuC,EACvC,+BAA+B,EAC/B,KAAK,+BAA+B,EACpC,KAAK,sCAAsC,EAC3C,KAAK,uBAAuB,EAC5B,KAAK,8BAA8B,GACpC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,wCAAwC,EACxC,gCAAgC,EAChC,mCAAmC,EACnC,iCAAiC,EACjC,KAAK,kBAAkB,EACvB,sCAAsC,GACvC,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
package/dist/internal.js CHANGED
@@ -1,4 +1,6 @@
1
1
  export { createFetchStyleHttpAdapterRealtimeCapability } from './adapter.js';
2
2
  export { DefaultBinder } from './adapters/binding.js';
3
3
  export { resolveClientIdentity } from './client-identity.js';
4
+ export { getCompiledRouteIdentity } from './compiled-route-identity.js';
5
+ export { FRAMEWORK_RESPONSE_VALUE_FINALIZER, FRAMEWORK_RESPONSE_WRITER, registerFrameworkResponseValueFinalizer, registerFrameworkResponseWriter } from './dispatch/response-integration.js';
4
6
  export { attachFrameworkRequestNativeRouteHandoff, bindRawRequestNativeRouteHandoff, consumeRawRequestNativeRouteHandoff, isRoutePathNormalizationSensitive, readFrameworkRequestNativeRouteHandoff } from './dispatch/native-route-handoff.js';
@@ -1 +1 @@
1
- {"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../src/mapping.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAIV,cAAc,EAEd,aAAa,EAIb,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAOpB,UAAU,2BAA2B;IACnC,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AA6VD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,OAAO,CAAC,EAAE,2BAA2B,GAAG,cAAc,CAuEpH"}
1
+ {"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../src/mapping.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAIV,cAAc,EAId,aAAa,EAOb,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAQpB,UAAU,2BAA2B;IACnC,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAycD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,OAAO,CAAC,EAAE,2BAA2B,GAAG,cAAc,CA8EpH"}
package/dist/mapping.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { getControllerMetadata, getRouteMetadata } from '@fluojs/core/internal';
2
+ import { attachCompiledRouteIdentity } from './compiled-route-identity.js';
2
3
  import { getRouteProducesMetadata } from './decorators.js';
3
4
  import { RouteConflictError } from './errors.js';
5
+ import { isMiddlewareRouteConfig } from './middleware/middleware.js';
4
6
  import { extractRoutePathParams, normalizeRoutePath, parseRoutePath } from './route-path.js';
5
7
  import { VersioningType } from './types.js';
6
8
  function joinPaths(basePath, routePath) {
@@ -19,16 +21,27 @@ function applyVersionPrefix(path, version) {
19
21
  function normalizeVersionValue(version) {
20
22
  return version.trim().replace(/^v/i, '');
21
23
  }
22
- function readHeaderValue(request, headerName) {
23
- const normalizedHeaderName = headerName.trim().toLowerCase();
24
+ function normalizeHeaderName(headerName) {
25
+ const normalized = headerName.trim().toLowerCase();
26
+ return normalized.length > 0 ? normalized : undefined;
27
+ }
28
+ function getMatchingRequestHeaderValues(request, headerName) {
29
+ const normalizedHeaderName = normalizeHeaderName(headerName);
24
30
  if (!normalizedHeaderName) {
25
- return undefined;
31
+ return [];
26
32
  }
27
- for (const [key, raw] of Object.entries(request.headers)) {
28
- if (key.toLowerCase() !== normalizedHeaderName) {
29
- continue;
33
+ const matches = [];
34
+ for (const [name, value] of Object.entries(request.headers)) {
35
+ if (name.toLowerCase() === normalizedHeaderName) {
36
+ matches.push(value);
30
37
  }
31
- const values = Array.isArray(raw) ? raw : [raw];
38
+ }
39
+ return matches;
40
+ }
41
+ function readHeaderValue(request, headerName) {
42
+ const matches = getMatchingRequestHeaderValues(request, headerName);
43
+ for (const match of matches) {
44
+ const values = Array.isArray(match) ? match : [match];
32
45
  for (const value of values) {
33
46
  const normalized = value?.trim();
34
47
  if (normalized) {
@@ -38,11 +51,25 @@ function readHeaderValue(request, headerName) {
38
51
  }
39
52
  return undefined;
40
53
  }
54
+ function readCombinedHeaderValue(request, headerName) {
55
+ const matches = getMatchingRequestHeaderValues(request, headerName);
56
+ const values = [];
57
+ for (const match of matches) {
58
+ const entries = Array.isArray(match) ? match : [match];
59
+ for (const entry of entries) {
60
+ const normalized = entry?.trim();
61
+ if (normalized) {
62
+ values.push(normalized);
63
+ }
64
+ }
65
+ }
66
+ return values.length > 0 ? values.join(',') : undefined;
67
+ }
41
68
  function escapeRegExp(value) {
42
69
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
43
70
  }
44
71
  function extractVersionFromMediaType(request, key) {
45
- const accept = readHeaderValue(request, 'accept');
72
+ const accept = readCombinedHeaderValue(request, 'accept');
46
73
  if (!accept) {
47
74
  return undefined;
48
75
  }
@@ -209,12 +236,12 @@ function matchParameterizedRoute(candidate, incomingSegments) {
209
236
  }
210
237
  return params;
211
238
  }
212
- function createHandlerDescriptors(source, versioning) {
239
+ function createHandlerDescriptors(source, versioning, sourceIndex) {
213
240
  const controllerMetadata = getControllerMetadata(source.controllerToken) ?? {
214
241
  basePath: ''
215
242
  };
216
243
  const descriptors = [];
217
- for (const propertyKey of getControllerMethodNames(source.controllerToken)) {
244
+ for (const [methodIndex, propertyKey] of getControllerMethodNames(source.controllerToken).entries()) {
218
245
  const routeMetadata = getRouteMetadata(source.controllerToken.prototype, propertyKey);
219
246
  if (!routeMetadata) {
220
247
  continue;
@@ -223,7 +250,7 @@ function createHandlerDescriptors(source, versioning) {
223
250
  const routePath = joinPaths(controllerMetadata.basePath, routeMetadata.path);
224
251
  const effectivePath = versioning.type === VersioningType.URI ? applyVersionPrefix(routePath, effectiveVersion) : routePath;
225
252
  const produces = getRouteProducesMetadata(source.controllerToken, propertyKey);
226
- descriptors.push({
253
+ descriptors.push(attachCompiledRouteIdentity({
227
254
  controllerToken: source.controllerToken,
228
255
  metadata: {
229
256
  controllerPath: controllerMetadata.basePath,
@@ -244,12 +271,12 @@ function createHandlerDescriptors(source, versioning) {
244
271
  path: effectivePath,
245
272
  version: effectiveVersion
246
273
  }
247
- });
274
+ }, `v1:${sourceIndex}:${methodIndex}`));
248
275
  }
249
276
  return descriptors;
250
277
  }
251
278
  function buildDescriptorList(sources, versioning) {
252
- const descriptors = sources.flatMap(source => createHandlerDescriptors(source, versioning));
279
+ const descriptors = sources.flatMap((source, sourceIndex) => createHandlerDescriptors(source, versioning, sourceIndex));
253
280
  const seen = new Set();
254
281
  for (const descriptor of descriptors) {
255
282
  const routeVersion = descriptor.route.version === undefined ? '<none>' : normalizeVersionValue(descriptor.route.version);
@@ -261,6 +288,59 @@ function buildDescriptorList(sources, versioning) {
261
288
  }
262
289
  return descriptors;
263
290
  }
291
+ function freezeDescriptorSnapshot(descriptors) {
292
+ const snapshot = descriptors.map(descriptor => {
293
+ const {
294
+ metadata,
295
+ route
296
+ } = descriptor;
297
+ const frozenMetadata = Object.freeze({
298
+ ...metadata,
299
+ moduleMiddleware: freezeModuleMiddlewareSnapshot(metadata.moduleMiddleware),
300
+ pathParams: Object.freeze([...metadata.pathParams])
301
+ });
302
+ const frozenRoute = Object.freeze({
303
+ ...route,
304
+ ...(route.guards ? {
305
+ guards: Object.freeze([...route.guards])
306
+ } : {}),
307
+ ...(route.headers ? {
308
+ headers: Object.freeze(route.headers.map(header => Object.freeze({
309
+ ...header
310
+ })))
311
+ } : {}),
312
+ ...(route.interceptors ? {
313
+ interceptors: Object.freeze([...route.interceptors])
314
+ } : {}),
315
+ ...(route.produces ? {
316
+ produces: Object.freeze([...route.produces])
317
+ } : {}),
318
+ ...(route.redirect ? {
319
+ redirect: Object.freeze({
320
+ ...route.redirect
321
+ })
322
+ } : {})
323
+ });
324
+ return Object.freeze({
325
+ ...descriptor,
326
+ metadata: frozenMetadata,
327
+ route: frozenRoute
328
+ });
329
+ });
330
+ return Object.freeze(snapshot);
331
+ }
332
+ function freezeModuleMiddlewareSnapshot(definitions) {
333
+ const snapshot = definitions.map(definition => {
334
+ if (!isMiddlewareRouteConfig(definition)) {
335
+ return definition;
336
+ }
337
+ return Object.freeze({
338
+ middleware: definition.middleware,
339
+ routes: Object.freeze([...definition.routes])
340
+ });
341
+ });
342
+ return Object.freeze(snapshot);
343
+ }
264
344
 
265
345
  /**
266
346
  * Create handler mapping.
@@ -271,9 +351,9 @@ function buildDescriptorList(sources, versioning) {
271
351
  */
272
352
  export function createHandlerMapping(sources, options) {
273
353
  const versioning = resolveVersioning(options);
274
- const descriptors = buildDescriptorList(sources, versioning);
354
+ const descriptors = freezeDescriptorSnapshot(buildDescriptorList(sources, versioning));
275
355
  const descriptorIndex = buildDescriptorIndex(descriptors);
276
- return {
356
+ const mapping = {
277
357
  descriptors,
278
358
  match(request) {
279
359
  const method = request.method.toUpperCase();
@@ -323,4 +403,9 @@ export function createHandlerMapping(sources, options) {
323
403
  return undefined;
324
404
  }
325
405
  };
406
+ Object.defineProperty(mapping, 'descriptors', {
407
+ configurable: false,
408
+ writable: false
409
+ });
410
+ return mapping;
326
411
  }
@@ -1,4 +1,12 @@
1
- import type { Middleware } from '../types.js';
1
+ import type { Middleware, MiddlewareLike, RequestContext } from '../types.js';
2
+ /**
3
+ * Initializes correlation-aware request contexts before lifecycle observers run.
4
+ *
5
+ * @internal
6
+ * @param middleware Global middleware definitions registered with the dispatcher.
7
+ * @param context Request context to initialize.
8
+ */
9
+ export declare function initializeCorrelationMiddlewareRequestContext(middleware: readonly MiddlewareLike[], context: RequestContext): void;
2
10
  /**
3
11
  * Create correlation middleware.
4
12
  *
@@ -1 +1 @@
1
- {"version":3,"file":"correlation.d.ts","sourceRoot":"","sources":["../../src/middleware/correlation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAsB9C;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,UAAU,CAYxD"}
1
+ {"version":3,"file":"correlation.d.ts","sourceRoot":"","sources":["../../src/middleware/correlation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAoB,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAuBhG;;;;;;GAMG;AACH,wBAAgB,6CAA6C,CAC3D,UAAU,EAAE,SAAS,cAAc,EAAE,EACrC,OAAO,EAAE,cAAc,GACtB,IAAI,CAUN;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,UAAU,CAWxD"}
@@ -1,16 +1,28 @@
1
+ import { resolveRequestId } from '../context/request-id.js';
1
2
  const REQUEST_ID_HEADER = 'x-request-id';
2
- const CORRELATION_ID_HEADER = 'x-correlation-id';
3
- function resolveInboundRequestId(headers) {
4
- const requestId = headers[REQUEST_ID_HEADER] ?? headers[CORRELATION_ID_HEADER];
5
- const value = Array.isArray(requestId) ? requestId[0] : requestId;
6
- return value ?? createRequestId();
3
+ const CORRELATION_REQUEST_CONTEXT_INITIALIZER = Symbol('fluo.http.correlationRequestContextInitializer');
4
+ function initializeCorrelationRequestContext(context, request = context.request) {
5
+ const requestId = context.requestId ?? resolveRequestId(request);
6
+ if (requestId === undefined) {
7
+ throw new Error('Correlation request ID generation unexpectedly returned undefined.');
8
+ }
9
+ context.requestId = requestId;
10
+ return requestId;
7
11
  }
8
- function createRequestId() {
9
- const randomUUID = globalThis.crypto?.randomUUID;
10
- if (randomUUID) {
11
- return randomUUID.call(globalThis.crypto);
12
+
13
+ /**
14
+ * Initializes correlation-aware request contexts before lifecycle observers run.
15
+ *
16
+ * @internal
17
+ * @param middleware Global middleware definitions registered with the dispatcher.
18
+ * @param context Request context to initialize.
19
+ */
20
+ export function initializeCorrelationMiddlewareRequestContext(middleware, context) {
21
+ for (const definition of middleware) {
22
+ if (typeof definition === 'object' && definition !== null && CORRELATION_REQUEST_CONTEXT_INITIALIZER in definition) {
23
+ definition[CORRELATION_REQUEST_CONTEXT_INITIALIZER](context);
24
+ }
12
25
  }
13
- return `req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
14
26
  }
15
27
 
16
28
  /**
@@ -20,11 +32,10 @@ function createRequestId() {
20
32
  */
21
33
  export function createCorrelationMiddleware() {
22
34
  return {
35
+ [CORRELATION_REQUEST_CONTEXT_INITIALIZER]: initializeCorrelationRequestContext,
23
36
  async handle(context, next) {
24
- if (!context.requestContext.requestId) {
25
- context.requestContext.requestId = resolveInboundRequestId(context.request.headers);
26
- }
27
- context.response.setHeader(REQUEST_ID_HEADER, context.requestContext.requestId);
37
+ const requestId = initializeCorrelationRequestContext(context.requestContext, context.request);
38
+ context.response.setHeader(REQUEST_ID_HEADER, requestId);
28
39
  await next();
29
40
  }
30
41
  };
@@ -1 +1 @@
1
- {"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../src/middleware/cors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC;IACvF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA4BD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,WAAgB,GAAG,UAAU,CA0D1E"}
1
+ {"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../src/middleware/cors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC;IACvF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA4BD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,WAAgB,GAAG,UAAU,CA0D1E"}
@@ -1,3 +1,5 @@
1
+ import { appendVaryHeader, getRequestHeader } from '../header-helpers.js';
2
+
1
3
  /**
2
4
  * Describes the cors options contract.
3
5
  */
@@ -35,7 +37,7 @@ export function createCorsMiddleware(options = {}) {
35
37
  const allowMethods = options.allowMethods ?? ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD'];
36
38
  return {
37
39
  async handle(context, next) {
38
- const requestOriginHeader = context.request.headers.origin;
40
+ const requestOriginHeader = getRequestHeader(context.request, 'origin');
39
41
  const requestOrigin = Array.isArray(requestOriginHeader) ? requestOriginHeader[0] : requestOriginHeader;
40
42
  const origin = resolveOrigin(options, requestOrigin);
41
43
  if (origin) {
@@ -50,14 +52,11 @@ export function createCorsMiddleware(options = {}) {
50
52
  setHeaderIfValue(context.response, 'Access-Control-Allow-Credentials', options.allowCredentials ? 'true' : undefined);
51
53
  setHeaderIfValue(context.response, 'Access-Control-Max-Age', options.maxAge !== undefined ? String(options.maxAge) : undefined);
52
54
  if (origin && origin !== '*') {
53
- const existingVary = context.response.headers['vary'] ?? context.response.headers['Vary'];
54
- const varyValues = existingVary ? Array.isArray(existingVary) ? existingVary : String(existingVary).split(',').map(v => v.trim()) : [];
55
- if (!varyValues.some(v => v.toLowerCase() === 'origin')) {
56
- varyValues.push('Origin');
57
- }
58
- context.response.setHeader('Vary', varyValues.join(', '));
55
+ appendVaryHeader(context.response, 'Origin');
59
56
  }
60
- if (context.request.method === 'OPTIONS') {
57
+ const requestedPreflightMethod = getRequestHeader(context.request, 'access-control-request-method');
58
+ const hasPreflightMethod = Array.isArray(requestedPreflightMethod) ? requestedPreflightMethod.length > 0 : requestedPreflightMethod !== undefined;
59
+ if (context.request.method === 'OPTIONS' && hasPreflightMethod) {
61
60
  context.response.setStatus(204);
62
61
  await context.response.send(undefined);
63
62
  return;
@@ -1,12 +1,12 @@
1
1
  import type { Constructor } from '@fluojs/core';
2
- import type { Middleware, MiddlewareContext, MiddlewareLike, MiddlewareRouteConfig, Next } from '../types.js';
2
+ import type { Middleware, MiddlewareContext, MiddlewareLike, MiddlewareRouteConfig, MiddlewareRouteSnapshot, MiddlewareSnapshotLike, Next } from '../types.js';
3
3
  /**
4
4
  * Is middleware route config.
5
5
  *
6
6
  * @param value The value.
7
7
  * @returns The is middleware route config result.
8
8
  */
9
- export declare function isMiddlewareRouteConfig(value: MiddlewareLike): value is MiddlewareRouteConfig;
9
+ export declare function isMiddlewareRouteConfig(value: MiddlewareLike | MiddlewareSnapshotLike): value is MiddlewareRouteConfig | MiddlewareRouteSnapshot;
10
10
  /**
11
11
  * Normalize route pattern.
12
12
  *
@@ -38,5 +38,5 @@ export declare function forRoutes<T extends Constructor<Middleware>>(middlewareC
38
38
  * @param terminal The terminal.
39
39
  * @returns The run middleware chain result.
40
40
  */
41
- export declare function runMiddlewareChain(definitions: MiddlewareLike[], context: MiddlewareContext, terminal: Next): Promise<void>;
41
+ export declare function runMiddlewareChain(definitions: readonly (MiddlewareLike | MiddlewareSnapshotLike)[], context: MiddlewareContext, terminal: Next): Promise<void>;
42
42
  //# sourceMappingURL=middleware.d.ts.map