@fluojs/http 1.0.0-beta.8 → 1.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 +27 -3
- package/README.md +27 -3
- package/dist/context/request-context-node-store.d.ts +25 -0
- package/dist/context/request-context-node-store.d.ts.map +1 -0
- package/dist/context/request-context-node-store.js +32 -0
- package/dist/context/request-context-stack-store.d.ts +8 -0
- package/dist/context/request-context-stack-store.d.ts.map +1 -0
- package/dist/context/request-context-stack-store.js +29 -0
- package/dist/context/request-context-store.d.ts +7 -0
- package/dist/context/request-context-store.d.ts.map +1 -0
- package/dist/context/request-context-store.js +1 -0
- package/dist/context/request-context.d.ts +7 -3
- package/dist/context/request-context.d.ts.map +1 -1
- package/dist/context/request-context.js +17 -5
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +261 -52
- package/dist/dispatch/dispatch-routing-policy.d.ts.map +1 -1
- package/dist/dispatch/dispatch-routing-policy.js +35 -6
- package/dist/dispatch/dispatcher.d.ts.map +1 -1
- package/dist/dispatch/dispatcher.js +4 -0
- package/dist/internal.d.ts +2 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -0
- package/dist/middleware/correlation.d.ts.map +1 -1
- package/dist/middleware/correlation.js +8 -2
- package/package.json +4 -4
package/README.ko.md
CHANGED
|
@@ -96,6 +96,8 @@ function someDeepHelper() {
|
|
|
96
96
|
}
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
`runWithRequestContext(...)`는 호스트가 `globalThis.AsyncLocalStorage` 또는 Node 내장 `node:async_hooks` 모듈로 `AsyncLocalStorage`를 제공할 때 활성 컨텍스트를 `await` 이후까지 보존합니다. 선언된 `>=20.0.0` 지원 범위의 Node 런타임은 `process.getBuiltinModule(...)`이 없어도 `node:async_hooks`를 동적으로 해석해 ALS 의미론을 유지합니다. 비동기 컨텍스트 primitive가 없는 비 Node 호스트에서는 동기 stack fallback을 사용하며, 겹치는 비동기 요청이 서로의 컨텍스트를 관찰하지 않도록 awaited continuation이 재개되기 전에 컨텍스트를 비웁니다.
|
|
100
|
+
|
|
99
101
|
### 프록시 뒤의 속도 제한
|
|
100
102
|
|
|
101
103
|
`createRateLimitMiddleware(...)`는 기본적으로 raw socket `remoteAddress`만으로 클라이언트 식별자를 해석합니다. `Forwarded`, `X-Forwarded-For`, `X-Real-IP`를 신뢰하려면 해당 헤더를 신뢰 가능한 프록시가 덮어쓰는 환경에서만 `trustProxyHeaders: true`를 명시적으로 켜세요. 어댑터가 신뢰 가능한 프록시 체인도 raw socket 식별자도 제공하지 않는다면 공유 fallback 버킷에 의존하지 말고 명시적인 `keyResolver`를 설정하세요.
|
|
@@ -113,9 +115,27 @@ stream(_input: undefined, ctx: RequestContext) {
|
|
|
113
115
|
}
|
|
114
116
|
```
|
|
115
117
|
|
|
118
|
+
### Versioning
|
|
119
|
+
|
|
120
|
+
`createHandlerMapping(...)`은 `VersioningType`과 `versioning` option을 통해 URI, header, media-type, custom versioning strategy를 지원합니다. Route registration은 exact/static match를 fallback보다 앞에 두고, 동등하게 정규화된 route는 registration order를 보존합니다.
|
|
121
|
+
|
|
122
|
+
### Request context helper
|
|
123
|
+
|
|
124
|
+
Framework integration이 명시적인 request context boundary나 typed per-request storage가 필요할 때 `runWithRequestContext(...)`, `assertRequestContext()`, `createRequestContext(...)`, `createContextKey(...)`, `getContextValue(...)`, `setContextValue(...)`를 사용합니다.
|
|
125
|
+
|
|
126
|
+
### Fast-path observability
|
|
127
|
+
|
|
128
|
+
Dispatcher는 adapter와 diagnostics를 위해 `FAST_PATH_ELIGIBILITY_SYMBOL`, `FAST_PATH_STATS_SYMBOL`, `formatFastPathStats(...)`, `getDispatcherFastPathStats(...)`로 fast-path observability를 노출합니다.
|
|
129
|
+
|
|
130
|
+
### Bun decorator bundling compatibility
|
|
131
|
+
|
|
132
|
+
Fluo의 HTTP 데코레이터는 TC39 표준 데코레이터이며, runtime 또는 compiler가 표준 decorator context를 제공하면 계속 `context.metadata`를 통해 metadata를 기록합니다. Bun이 legacy TypeScript decorator transform으로 애플리케이션을 번들링하는 경우에도 controller, route, DTO binding, guard/interceptor, header, redirect, versioning, status, request DTO, `@Produces(...)` metadata를 Fluo 내부 metadata store에 기록하여 생성된 Bun bundle의 route mapping 동작을 보존합니다.
|
|
133
|
+
|
|
134
|
+
이 호환 경로는 Bun bundle output을 위한 실행 fallback입니다. 애플리케이션 소스는 계속 Fluo 표준 데코레이터를 사용해야 하며, `emitDecoratorMetadata`를 켜거나 `reflect-metadata`에 의존해서는 안 됩니다.
|
|
135
|
+
|
|
116
136
|
## 요청 정리와 런타임 이식성
|
|
117
137
|
|
|
118
|
-
디스패처는 활성 dispatch 동안에만
|
|
138
|
+
디스패처는 활성 dispatch 동안에만 호스트 비동기 컨텍스트 저장소로 `RequestContext`를 바인딩합니다. 지원되는 Node 20+ 런타임을 포함해 `AsyncLocalStorage`가 있는 호스트에서는 컨텍스트가 awaited work 이후까지 유지됩니다. 비동기 컨텍스트 primitive가 없는 비 Node 호스트에서는 fallback 컨텍스트가 동기 프레임에만 유효하고, 겹치는 요청이 서로의 컨텍스트를 관찰하지 않도록 `await` 이후에는 의도적으로 사용할 수 없습니다. 요청이 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 최적화일 뿐, 공개 context가 root container를 노출한다는 약속이 아닙니다.
|
|
119
139
|
|
|
120
140
|
어댑터는 플랫폼이 제공한다면 `FrameworkRequest.signal`에 `AbortSignal`을 전달해야 합니다. SSE에서는 가능하면 `FrameworkResponse.stream.onClose(...)`도 노출해야 합니다. `SseResponse`는 request abort와 raw stream close를 모두 구독하고, 멱등하게 닫히며, 어느 쪽이 먼저 종료되더라도 등록한 listener를 제거합니다.
|
|
121
141
|
|
|
@@ -125,8 +145,10 @@ stream(_input: undefined, ctx: RequestContext) {
|
|
|
125
145
|
- **바인딩 데코레이터**: `FromBody`, `FromQuery`, `FromPath`, `FromHeader`, `FromCookie`, `RequestDto`, `Optional`, `Convert`
|
|
126
146
|
- **실행 데코레이터**: `UseGuards`, `UseInterceptors`, `HttpCode`, `Version`, `Header`, `Redirect`, `Produces`
|
|
127
147
|
- **핵심 런타임 타입**: `RequestContext`, `FrameworkRequest`, `FrameworkResponse`, `SseResponse`
|
|
128
|
-
-
|
|
129
|
-
-
|
|
148
|
+
- **Adapter API**: `HttpApplicationAdapter`, `createNoopHttpApplicationAdapter`, `createServerBackedHttpAdapterRealtimeCapability`, `createUnsupportedHttpAdapterRealtimeCapability`, `createFetchStyleHttpAdapterRealtimeCapability`
|
|
149
|
+
- **예외와 오류**: `HttpException`, `BadRequestException`, `UnauthorizedException`, `ForbiddenException`, `NotFoundException`, `ConflictException`, `NotAcceptableException`, `TooManyRequestsException`, `InternalServerErrorException`, `PayloadTooLargeException`, `createErrorResponse`, `RouteConflictError`, `InvalidRoutePathError`, `HandlerNotFoundError`, `RequestAbortedError`
|
|
150
|
+
- **헬퍼**: `createHandlerMapping`, `createDispatcher`, `forRoutes`, `normalizeRoutePattern`, `matchRoutePattern`, `isMiddlewareRouteConfig`, `createCorrelationMiddleware`, `createCorsMiddleware`, `createRateLimitMiddleware`, `createSecurityHeadersMiddleware`, `runWithRequestContext`, `getCurrentRequestContext`, `assertRequestContext`, `createRequestContext`, `createContextKey`, `getContextValue`, `setContextValue`, `encodeSseComment`, `encodeSseMessage`
|
|
151
|
+
- **Option type**: `CorsOptions`, `RateLimitOptions`, `RateLimitStore`, `SecurityHeadersOptions`, `SseSendOptions`
|
|
130
152
|
|
|
131
153
|
## 내부 서브경로 (`@fluojs/http/internal`)
|
|
132
154
|
|
|
@@ -134,9 +156,11 @@ stream(_input: undefined, ctx: RequestContext) {
|
|
|
134
156
|
|
|
135
157
|
- `DefaultBinder`: 런타임 부트스트랩 경로에서 사용하는 기본 DTO/요청 바인더.
|
|
136
158
|
- `bindRawRequestNativeRouteHandoff(...)` / `attachFrameworkRequestNativeRouteHandoff(...)`: public dispatcher API를 넓히지 않고 의미 보존이 가능한 native route match를 재사용하기 위한 내부 adapter/runtime 헬퍼.
|
|
159
|
+
- `consumeRawRequestNativeRouteHandoff(...)` / `readFrameworkRequestNativeRouteHandoff(...)`: native route handoff를 읽거나 소비하기 위한 내부 helper.
|
|
137
160
|
- Native route handoff는 framework request에 붙는 시점의 method와 path를 함께 스냅샷합니다. app middleware가 handler matching 전에 둘 중 하나를 rewrite하면 dispatcher는 stale handoff를 무시하고 일반 route matching으로 fallback합니다.
|
|
138
161
|
- `isRoutePathNormalizationSensitive(path)`: duplicate slash와 trailing slash 요청을 generic dispatcher 경로에 남기기 위한 내부 guard.
|
|
139
162
|
- `resolveClientIdentity(request)`: 속도 제한과 런타임 통합에서 사용하는 보수적 클라이언트 식별 해석기.
|
|
163
|
+
- `createFetchStyleHttpAdapterRealtimeCapability(...)`, `Dispatcher`, `HttpApplicationAdapter`: 전체 HTTP root barrel을 instantiate하면 안 되는 edge/fetch-style platform package를 위한 내부 adapter seam.
|
|
140
164
|
|
|
141
165
|
## 관련 패키지
|
|
142
166
|
|
package/README.md
CHANGED
|
@@ -98,6 +98,8 @@ function someDeepHelper() {
|
|
|
98
98
|
}
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
+
`runWithRequestContext(...)` preserves the active context across awaited work when the host provides `AsyncLocalStorage` through `globalThis.AsyncLocalStorage` or Node's built-in `node:async_hooks` module. Node runtimes in the declared `>=20.0.0` support range keep ALS semantics even when `process.getBuiltinModule(...)` is unavailable by resolving `node:async_hooks` dynamically. Non-Node hosts without an async-context primitive use a synchronous stack fallback that clears the context before awaited continuations resume, avoiding cross-request leaks instead of pretending to isolate overlapping async work.
|
|
102
|
+
|
|
101
103
|
### Rate limiting behind proxies
|
|
102
104
|
|
|
103
105
|
`createRateLimitMiddleware(...)` resolves client identity from the raw socket `remoteAddress` by default. To trust `Forwarded`, `X-Forwarded-For`, or `X-Real-IP`, opt in with `trustProxyHeaders: true` only when your adapter sits behind a trusted proxy that overwrites those headers. If your adapter exposes neither a trusted proxy chain nor a raw socket identity, provide an explicit `keyResolver`.
|
|
@@ -115,9 +117,27 @@ stream(_input: undefined, ctx: RequestContext) {
|
|
|
115
117
|
}
|
|
116
118
|
```
|
|
117
119
|
|
|
120
|
+
### Versioning
|
|
121
|
+
|
|
122
|
+
`createHandlerMapping(...)` supports URI, header, media-type, and custom versioning strategies through `VersioningType` and the `versioning` option. Route registration keeps exact/static matches ahead of fallbacks while preserving registration order for equivalent normalized routes.
|
|
123
|
+
|
|
124
|
+
### Request context helpers
|
|
125
|
+
|
|
126
|
+
Use `runWithRequestContext(...)`, `assertRequestContext()`, `createRequestContext(...)`, `createContextKey(...)`, `getContextValue(...)`, and `setContextValue(...)` when framework integrations need explicit request context boundaries or typed per-request storage.
|
|
127
|
+
|
|
128
|
+
### Fast-path observability
|
|
129
|
+
|
|
130
|
+
The dispatcher exposes fast-path observability for adapters and diagnostics through `FAST_PATH_ELIGIBILITY_SYMBOL`, `FAST_PATH_STATS_SYMBOL`, `formatFastPathStats(...)`, and `getDispatcherFastPathStats(...)`.
|
|
131
|
+
|
|
132
|
+
### Bun decorator bundling compatibility
|
|
133
|
+
|
|
134
|
+
Fluo's HTTP decorators are standard TC39 decorators and continue to record metadata through `context.metadata` when the runtime or compiler provides the standard decorator context. When Bun bundles an application through its legacy TypeScript decorator transform, the same controller, route, DTO binding, guard/interceptor, header, redirect, versioning, status, request DTO, and `@Produces(...)` metadata is recorded through Fluo's internal metadata stores so generated Bun bundles preserve route mapping behavior.
|
|
135
|
+
|
|
136
|
+
This compatibility path is an execution fallback for Bun bundle output; application source should still use Fluo's standard decorators and should not enable `emitDecoratorMetadata` or rely on `reflect-metadata`.
|
|
137
|
+
|
|
118
138
|
## Request Cleanup and Portability
|
|
119
139
|
|
|
120
|
-
The dispatcher binds `RequestContext` with
|
|
140
|
+
The dispatcher binds `RequestContext` with host async-context storage for the active dispatch only. On hosts with `AsyncLocalStorage`, including supported Node 20+ runtimes, the context remains available across awaited work. On non-Node hosts without an async-context primitive, the fallback context is synchronous-only and intentionally unavailable after `await` so overlapping requests cannot observe one another's context. 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
141
|
|
|
122
142
|
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
143
|
|
|
@@ -127,8 +147,10 @@ Adapters should pass an `AbortSignal` on `FrameworkRequest.signal` when the plat
|
|
|
127
147
|
- **Binding decorators**: `FromBody`, `FromQuery`, `FromPath`, `FromHeader`, `FromCookie`, `RequestDto`, `Optional`, `Convert`
|
|
128
148
|
- **Execution decorators**: `UseGuards`, `UseInterceptors`, `HttpCode`, `Version`, `Header`, `Redirect`, `Produces`
|
|
129
149
|
- **Core runtime types**: `RequestContext`, `FrameworkRequest`, `FrameworkResponse`, `SseResponse`
|
|
130
|
-
- **
|
|
131
|
-
- **
|
|
150
|
+
- **Adapter API**: `HttpApplicationAdapter`, `createNoopHttpApplicationAdapter`, `createServerBackedHttpAdapterRealtimeCapability`, `createUnsupportedHttpAdapterRealtimeCapability`, `createFetchStyleHttpAdapterRealtimeCapability`
|
|
151
|
+
- **Exceptions and errors**: `HttpException`, `BadRequestException`, `UnauthorizedException`, `ForbiddenException`, `NotFoundException`, `ConflictException`, `NotAcceptableException`, `TooManyRequestsException`, `InternalServerErrorException`, `PayloadTooLargeException`, `createErrorResponse`, `RouteConflictError`, `InvalidRoutePathError`, `HandlerNotFoundError`, `RequestAbortedError`
|
|
152
|
+
- **Helpers**: `createHandlerMapping`, `createDispatcher`, `forRoutes`, `normalizeRoutePattern`, `matchRoutePattern`, `isMiddlewareRouteConfig`, `createCorrelationMiddleware`, `createCorsMiddleware`, `createRateLimitMiddleware`, `createSecurityHeadersMiddleware`, `runWithRequestContext`, `getCurrentRequestContext`, `assertRequestContext`, `createRequestContext`, `createContextKey`, `getContextValue`, `setContextValue`, `encodeSseComment`, `encodeSseMessage`
|
|
153
|
+
- **Option types**: `CorsOptions`, `RateLimitOptions`, `RateLimitStore`, `SecurityHeadersOptions`, `SseSendOptions`
|
|
132
154
|
|
|
133
155
|
## Internal Subpath (`@fluojs/http/internal`)
|
|
134
156
|
|
|
@@ -136,9 +158,11 @@ The `./internal` subpath exports only the low-level utilities used by platform a
|
|
|
136
158
|
|
|
137
159
|
- `DefaultBinder`: Default DTO/request binder used by the runtime bootstrap path.
|
|
138
160
|
- `bindRawRequestNativeRouteHandoff(...)` / `attachFrameworkRequestNativeRouteHandoff(...)`: Internal adapter/runtime helpers for reusing semantically safe native route matches without widening the public dispatcher API.
|
|
161
|
+
- `consumeRawRequestNativeRouteHandoff(...)` / `readFrameworkRequestNativeRouteHandoff(...)`: Internal helpers for reading or consuming native route handoffs.
|
|
139
162
|
- 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
163
|
- `isRoutePathNormalizationSensitive(path)`: Internal guard for keeping duplicate-slash and trailing-slash requests on the generic dispatcher path.
|
|
141
164
|
- `resolveClientIdentity(request)`: Conservative client identity resolver used by rate limiting and other runtime integrations.
|
|
165
|
+
- `createFetchStyleHttpAdapterRealtimeCapability(...)`, `Dispatcher`, and `HttpApplicationAdapter`: internal adapter seams for edge/fetch-style platform packages that must avoid instantiating the full HTTP root barrel.
|
|
142
166
|
|
|
143
167
|
## Related Packages
|
|
144
168
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { RequestContextStore } from './request-context-store.js';
|
|
2
|
+
type AsyncLocalStorageConstructor = new () => RequestContextStore;
|
|
3
|
+
type NodeAsyncHooksModule = {
|
|
4
|
+
AsyncLocalStorage?: AsyncLocalStorageConstructor;
|
|
5
|
+
};
|
|
6
|
+
type AsyncLocalStorageResolutionHost = {
|
|
7
|
+
AsyncLocalStorage?: AsyncLocalStorageConstructor;
|
|
8
|
+
process?: {
|
|
9
|
+
getBuiltinModule?(id: 'node:async_hooks'): NodeAsyncHooksModule;
|
|
10
|
+
versions?: {
|
|
11
|
+
node?: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
type NodeAsyncHooksLoader = () => Promise<NodeAsyncHooksModule>;
|
|
16
|
+
/**
|
|
17
|
+
* Resolves the host `AsyncLocalStorage` constructor without eagerly importing Node built-ins.
|
|
18
|
+
*
|
|
19
|
+
* @param host Host global-like object to inspect for async-context support.
|
|
20
|
+
* @param loadNodeAsyncHooks Lazy loader for Node's `node:async_hooks` module.
|
|
21
|
+
* @returns The resolved `AsyncLocalStorage` constructor, or `undefined` when unavailable.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveAsyncLocalStorageConstructor(host?: AsyncLocalStorageResolutionHost, loadNodeAsyncHooks?: NodeAsyncHooksLoader): Promise<AsyncLocalStorageConstructor | undefined>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=request-context-node-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-context-node-store.d.ts","sourceRoot":"","sources":["../../src/context/request-context-node-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,KAAK,4BAA4B,GAAG,UAAU,mBAAmB,CAAC;AAElE,KAAK,oBAAoB,GAAG;IAC1B,iBAAiB,CAAC,EAAE,4BAA4B,CAAC;CAClD,CAAC;AAEF,KAAK,+BAA+B,GAAG;IACrC,iBAAiB,CAAC,EAAE,4BAA4B,CAAC;IACjD,OAAO,CAAC,EAAE;QACR,gBAAgB,CAAC,CAAC,EAAE,EAAE,kBAAkB,GAAG,oBAAoB,CAAC;QAChE,QAAQ,CAAC,EAAE;YACT,IAAI,CAAC,EAAE,MAAM,CAAC;SACf,CAAC;KACH,CAAC;CACH,CAAC;AAEF,KAAK,oBAAoB,GAAG,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAEhE;;;;;;GAMG;AACH,wBAAsB,mCAAmC,CACvD,IAAI,GAAE,+BAA4C,EAClD,kBAAkB,GAAE,oBAA2C,GAC9D,OAAO,CAAC,4BAA4B,GAAG,SAAS,CAAC,CAsBnD"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the host `AsyncLocalStorage` constructor without eagerly importing Node built-ins.
|
|
3
|
+
*
|
|
4
|
+
* @param host Host global-like object to inspect for async-context support.
|
|
5
|
+
* @param loadNodeAsyncHooks Lazy loader for Node's `node:async_hooks` module.
|
|
6
|
+
* @returns The resolved `AsyncLocalStorage` constructor, or `undefined` when unavailable.
|
|
7
|
+
*/
|
|
8
|
+
export async function resolveAsyncLocalStorageConstructor(host = globalThis, loadNodeAsyncHooks = importNodeAsyncHooks) {
|
|
9
|
+
if (typeof host.AsyncLocalStorage === 'function') {
|
|
10
|
+
return host.AsyncLocalStorage;
|
|
11
|
+
}
|
|
12
|
+
const builtinAsyncLocalStorage = host.process?.getBuiltinModule?.('node:async_hooks').AsyncLocalStorage;
|
|
13
|
+
if (typeof builtinAsyncLocalStorage === 'function') {
|
|
14
|
+
return builtinAsyncLocalStorage;
|
|
15
|
+
}
|
|
16
|
+
if (!isNodeHost(host)) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const nodeAsyncHooks = await loadNodeAsyncHooks();
|
|
21
|
+
return nodeAsyncHooks.AsyncLocalStorage;
|
|
22
|
+
} catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function isNodeHost(host) {
|
|
27
|
+
return typeof host.process?.versions?.node === 'string';
|
|
28
|
+
}
|
|
29
|
+
function importNodeAsyncHooks() {
|
|
30
|
+
const nodeAsyncHooksSpecifier = `node:${'async_hooks'}`;
|
|
31
|
+
return import(nodeAsyncHooksSpecifier);
|
|
32
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RequestContextStore } from './request-context-store.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates the synchronous fallback request-context store used when no async-context primitive exists.
|
|
4
|
+
*
|
|
5
|
+
* @returns A stack-backed request-context store scoped to synchronous callback frames.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createStackRequestContextStore(): RequestContextStore;
|
|
8
|
+
//# sourceMappingURL=request-context-stack-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-context-stack-store.d.ts","sourceRoot":"","sources":["../../src/context/request-context-stack-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE;;;;GAIG;AACH,wBAAgB,8BAA8B,IAAI,mBAAmB,CAmBpE"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the synchronous fallback request-context store used when no async-context primitive exists.
|
|
3
|
+
*
|
|
4
|
+
* @returns A stack-backed request-context store scoped to synchronous callback frames.
|
|
5
|
+
*/
|
|
6
|
+
export function createStackRequestContextStore() {
|
|
7
|
+
const stack = [];
|
|
8
|
+
return {
|
|
9
|
+
getStore() {
|
|
10
|
+
return stack.at(-1);
|
|
11
|
+
},
|
|
12
|
+
run(context, callback) {
|
|
13
|
+
stack.push(context);
|
|
14
|
+
try {
|
|
15
|
+
return callback();
|
|
16
|
+
} catch (error) {
|
|
17
|
+
throw error;
|
|
18
|
+
} finally {
|
|
19
|
+
removeStackContext(stack, context);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function removeStackContext(stack, context) {
|
|
25
|
+
const index = stack.lastIndexOf(context);
|
|
26
|
+
if (index >= 0) {
|
|
27
|
+
stack.splice(index, 1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RequestContext } from '../types.js';
|
|
2
|
+
/** Store abstraction shared by host async-context implementations and the synchronous fallback. */
|
|
3
|
+
export type RequestContextStore = {
|
|
4
|
+
getStore(): RequestContext | undefined;
|
|
5
|
+
run<T>(context: RequestContext, callback: () => T): T;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=request-context-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-context-store.d.ts","sourceRoot":"","sources":["../../src/context/request-context-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,mGAAmG;AACnG,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,IAAI,cAAc,GAAG,SAAS,CAAC;IACvC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;CACvD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { ContextKey, RequestContext } from '../types.js';
|
|
2
2
|
/**
|
|
3
|
-
* Runs a callback inside the request-scoped
|
|
3
|
+
* Runs a callback inside the request-scoped async context.
|
|
4
|
+
*
|
|
5
|
+
* Hosts with `AsyncLocalStorage` preserve the context across awaited work. Hosts without an async
|
|
6
|
+
* context primitive use a stack fallback that keeps the context only for the synchronous callback
|
|
7
|
+
* frame and clears it before awaited continuations resume.
|
|
4
8
|
*
|
|
5
9
|
* @param context Request context snapshot to bind to the current async execution chain.
|
|
6
10
|
* @param callback Callback executed with `context` available through request-context helpers.
|
|
@@ -21,9 +25,9 @@ export declare function getCurrentRequestContext(): RequestContext | undefined;
|
|
|
21
25
|
*/
|
|
22
26
|
export declare function assertRequestContext(): RequestContext;
|
|
23
27
|
/**
|
|
24
|
-
* Creates a defensive clone of a request context for
|
|
28
|
+
* Creates a defensive clone of a request context for async-context storage.
|
|
25
29
|
*
|
|
26
|
-
* @param context Request context to clone before storing in
|
|
30
|
+
* @param context Request context to clone before storing in the active async-context store.
|
|
27
31
|
* @returns A shallow clone with copied metadata map to avoid cross-request mutation.
|
|
28
32
|
*/
|
|
29
33
|
export declare function createRequestContext(context: RequestContext): RequestContext;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/context/request-context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/context/request-context.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAO9D;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAEtF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,cAAc,GAAG,SAAS,CAErE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,cAAc,CAUrD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,CAK5E;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,CAKtE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,CAE7F;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAE9F"}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
1
|
import { FluoError } from '@fluojs/core';
|
|
3
|
-
|
|
2
|
+
import { resolveAsyncLocalStorageConstructor } from './request-context-node-store.js';
|
|
3
|
+
import { createStackRequestContextStore } from './request-context-stack-store.js';
|
|
4
|
+
const requestContextStore = await createRequestContextStore();
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* Runs a callback inside the request-scoped
|
|
7
|
+
* Runs a callback inside the request-scoped async context.
|
|
8
|
+
*
|
|
9
|
+
* Hosts with `AsyncLocalStorage` preserve the context across awaited work. Hosts without an async
|
|
10
|
+
* context primitive use a stack fallback that keeps the context only for the synchronous callback
|
|
11
|
+
* frame and clears it before awaited continuations resume.
|
|
7
12
|
*
|
|
8
13
|
* @param context Request context snapshot to bind to the current async execution chain.
|
|
9
14
|
* @param callback Callback executed with `context` available through request-context helpers.
|
|
@@ -39,9 +44,9 @@ export function assertRequestContext() {
|
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
/**
|
|
42
|
-
* Creates a defensive clone of a request context for
|
|
47
|
+
* Creates a defensive clone of a request context for async-context storage.
|
|
43
48
|
*
|
|
44
|
-
* @param context Request context to clone before storing in
|
|
49
|
+
* @param context Request context to clone before storing in the active async-context store.
|
|
45
50
|
* @returns A shallow clone with copied metadata map to avoid cross-request mutation.
|
|
46
51
|
*/
|
|
47
52
|
export function createRequestContext(context) {
|
|
@@ -86,4 +91,11 @@ export function getContextValue(context, key) {
|
|
|
86
91
|
*/
|
|
87
92
|
export function setContextValue(context, key, value) {
|
|
88
93
|
context.metadata[key.id] = value;
|
|
94
|
+
}
|
|
95
|
+
async function createRequestContextStore() {
|
|
96
|
+
const AsyncLocalStorage = await resolveAsyncLocalStorageConstructor();
|
|
97
|
+
if (typeof AsyncLocalStorage === 'function') {
|
|
98
|
+
return new AsyncLocalStorage();
|
|
99
|
+
}
|
|
100
|
+
return createStackRequestContextStore();
|
|
89
101
|
}
|
package/dist/decorators.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,mBAAmB,EAEzB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,mBAAmB,EAEzB,MAAM,cAAc,CAAC;AAgBtB,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAc,eAAe,EAAE,MAAM,YAAY,CAAC;AAGxF,KAAK,wBAAwB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAC1F,KAAK,yBAAyB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAC;AACjG,KAAK,wBAAwB,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC;AAI1H,KAAK,kBAAkB,GAAG,wBAAwB,CAAC;AACnD,KAAK,mBAAmB,GAAG,yBAAyB,CAAC;AACrD,KAAK,0BAA0B,GAAG,wBAAwB,GAAG,yBAAyB,CAAC;AACvF,KAAK,kBAAkB,GAAG,wBAAwB,CAAC;AAqUnD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,QAAQ,SAAK,GAAG,kBAAkB,CAa5D;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,0BAA0B,CAuBnE;AAED;;;;;GAKG;AACH,eAAO,MAAM,GAAG,SAxHA,MAAM,KAAG,mBAwHqB,CAAC;AAC/C;;;;;GAKG;AACH,eAAO,MAAM,IAAI,SA/HD,MAAM,KAAG,mBA+HuB,CAAC;AACjD;;;;;GAKG;AACH,eAAO,MAAM,GAAG,SAtIA,MAAM,KAAG,mBAsIqB,CAAC;AAC/C;;;;;GAKG;AACH,eAAO,MAAM,KAAK,SA7IF,MAAM,KAAG,mBA6IyB,CAAC;AACnD;;;;;GAKG;AACH,eAAO,MAAM,MAAM,SApJH,MAAM,KAAG,mBAoJ2B,CAAC;AACrD;;;;;GAKG;AACH,eAAO,MAAM,OAAO,SA3JJ,MAAM,KAAG,mBA2J6B,CAAC;AACvD;;;;;GAKG;AACH,eAAO,MAAM,IAAI,SAlKD,MAAM,KAAG,mBAkKuB,CAAC;AACjD;;;;;GAKG;AACH,eAAO,MAAM,GAAG,SAzKA,MAAM,KAAG,mBAyKqB,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,UAAU,0BA5JF,mBA8JnB,CAAC;AAEH;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,UAAU,EAAE,MAAM,EAAE,GAAG,mBAAmB,CAIrE;AAED;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,qBAlLA,mBAoLnB,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,GAAG,MAAM,EAAE,GAAG,SAAS,CAM7H;AAED;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,SAxLL,MAAM,KAAG,kBAwL8B,CAAC;AACxD;;;;;GAKG;AACH,eAAO,MAAM,SAAS,SA/LN,MAAM,KAAG,kBA+LgC,CAAC;AAC1D;;;;;GAKG;AACH,eAAO,MAAM,UAAU,SAtMP,MAAM,KAAG,kBAsMkC,CAAC;AAC5D;;;;;GAKG;AACH,eAAO,MAAM,UAAU,SA7MP,MAAM,KAAG,kBA6MkC,CAAC;AAC5D;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,SApNL,MAAM,KAAG,kBAoN8B,CAAC;AAExD;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,kBAAkB,CAa7C;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,aAAa,GAAG,kBAAkB,CAapE;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAcvE;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAa9E;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,GAAG,0BAA0B,CAyB5E;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,YAAY,EAAE,eAAe,EAAE,GAAG,0BAA0B,CAyB9F"}
|
package/dist/decorators.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { getStandardMetadataBag as readStandardMetadataBag
|
|
1
|
+
import { defineControllerMetadata, defineDtoFieldBindingMetadata, defineRouteMetadata, ensureMetadataSymbol, getControllerMetadata, getDtoFieldBindingMetadata, getRouteMetadata, getStandardMetadataBag as readStandardMetadataBag } from '@fluojs/core/internal';
|
|
2
2
|
import { validateRoutePath } from './route-path.js';
|
|
3
3
|
const standardControllerMetadataKey = Symbol.for('fluo.standard.controller');
|
|
4
4
|
const standardRouteMetadataKey = Symbol.for('fluo.standard.route');
|
|
5
5
|
const standardDtoBindingMetadataKey = Symbol.for('fluo.standard.dto-binding');
|
|
6
|
+
const legacyRouteMetadataStore = new WeakMap();
|
|
7
|
+
const legacyDtoBindingMetadataStore = new WeakMap();
|
|
8
|
+
ensureMetadataSymbol();
|
|
6
9
|
function normalizeProducesMediaTypes(mediaTypes) {
|
|
7
10
|
const normalized = [];
|
|
8
11
|
for (const mediaType of mediaTypes) {
|
|
@@ -24,8 +27,115 @@ function mergeUnique(existing, values) {
|
|
|
24
27
|
return merged;
|
|
25
28
|
}
|
|
26
29
|
function getStandardMetadataBag(metadata) {
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
return typeof metadata === 'object' && metadata !== null ? metadata : {};
|
|
31
|
+
}
|
|
32
|
+
function isStandardDecoratorContext(value) {
|
|
33
|
+
return typeof value === 'object' && value !== null && 'kind' in value && 'name' in value;
|
|
34
|
+
}
|
|
35
|
+
function isMetadataPropertyKey(value) {
|
|
36
|
+
return typeof value === 'string' || typeof value === 'symbol';
|
|
37
|
+
}
|
|
38
|
+
function getLegacyRouteRecord(target, propertyKey) {
|
|
39
|
+
let routeMap = legacyRouteMetadataStore.get(target);
|
|
40
|
+
if (!routeMap) {
|
|
41
|
+
routeMap = new Map();
|
|
42
|
+
legacyRouteMetadataStore.set(target, routeMap);
|
|
43
|
+
}
|
|
44
|
+
let record = routeMap.get(propertyKey);
|
|
45
|
+
if (!record) {
|
|
46
|
+
record = {};
|
|
47
|
+
routeMap.set(propertyKey, record);
|
|
48
|
+
}
|
|
49
|
+
return record;
|
|
50
|
+
}
|
|
51
|
+
function flushLegacyRouteMetadata(target, propertyKey, record) {
|
|
52
|
+
if (!record.method || record.path === undefined) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const existing = getRouteMetadata(target, propertyKey);
|
|
56
|
+
const metadata = {
|
|
57
|
+
guards: mergeUnique(existing?.guards, record.guards ?? []),
|
|
58
|
+
headers: record.headers ?? existing?.headers,
|
|
59
|
+
interceptors: mergeUnique(existing?.interceptors, record.interceptors ?? []),
|
|
60
|
+
method: record.method,
|
|
61
|
+
path: record.path,
|
|
62
|
+
redirect: record.redirect ?? existing?.redirect,
|
|
63
|
+
request: record.request ?? existing?.request,
|
|
64
|
+
successStatus: record.successStatus ?? existing?.successStatus,
|
|
65
|
+
version: record.version ?? existing?.version
|
|
66
|
+
};
|
|
67
|
+
defineRouteMetadata(target, propertyKey, metadata);
|
|
68
|
+
}
|
|
69
|
+
function mergeLegacyRouteMetadata(target, propertyKey, partial) {
|
|
70
|
+
const record = getLegacyRouteRecord(target, propertyKey);
|
|
71
|
+
Object.assign(record, partial);
|
|
72
|
+
flushLegacyRouteMetadata(target, propertyKey, record);
|
|
73
|
+
}
|
|
74
|
+
function defineLegacyControllerMetadata(target, partial) {
|
|
75
|
+
const existing = getControllerMetadata(target);
|
|
76
|
+
defineControllerMetadata(target, {
|
|
77
|
+
basePath: partial.basePath ?? existing?.basePath ?? '',
|
|
78
|
+
guards: partial.guards ?? existing?.guards,
|
|
79
|
+
interceptors: partial.interceptors ?? existing?.interceptors,
|
|
80
|
+
version: partial.version ?? existing?.version
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function getLegacyDtoBindingRecord(target, propertyKey) {
|
|
84
|
+
let bindingMap = legacyDtoBindingMetadataStore.get(target);
|
|
85
|
+
if (!bindingMap) {
|
|
86
|
+
bindingMap = new Map();
|
|
87
|
+
legacyDtoBindingMetadataStore.set(target, bindingMap);
|
|
88
|
+
}
|
|
89
|
+
let record = bindingMap.get(propertyKey);
|
|
90
|
+
if (!record) {
|
|
91
|
+
record = {};
|
|
92
|
+
bindingMap.set(propertyKey, record);
|
|
93
|
+
}
|
|
94
|
+
return record;
|
|
95
|
+
}
|
|
96
|
+
function mergeLegacyDtoBinding(target, propertyKey, partial) {
|
|
97
|
+
const record = getLegacyDtoBindingRecord(target, propertyKey);
|
|
98
|
+
Object.assign(record, partial);
|
|
99
|
+
const source = record.source ?? getDtoFieldBindingMetadata(target, propertyKey)?.source;
|
|
100
|
+
if (!source) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
defineDtoFieldBindingMetadata(target, propertyKey, {
|
|
104
|
+
converter: record.converter,
|
|
105
|
+
key: record.key,
|
|
106
|
+
optional: record.optional,
|
|
107
|
+
source
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function appendLegacyRouteHeader(target, propertyKey, name, value) {
|
|
111
|
+
const record = getLegacyRouteRecord(target, propertyKey);
|
|
112
|
+
record.headers = [...(record.headers ?? []), {
|
|
113
|
+
name,
|
|
114
|
+
value
|
|
115
|
+
}];
|
|
116
|
+
flushLegacyRouteMetadata(target, propertyKey, record);
|
|
117
|
+
}
|
|
118
|
+
function appendLegacyControllerGuards(target, guards) {
|
|
119
|
+
const existing = getControllerMetadata(target);
|
|
120
|
+
defineLegacyControllerMetadata(target, {
|
|
121
|
+
guards: mergeUnique(existing?.guards, guards)
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function appendLegacyControllerInterceptors(target, interceptors) {
|
|
125
|
+
const existing = getControllerMetadata(target);
|
|
126
|
+
defineLegacyControllerMetadata(target, {
|
|
127
|
+
interceptors: mergeUnique(existing?.interceptors, interceptors)
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function appendLegacyRouteGuards(target, propertyKey, guards) {
|
|
131
|
+
const record = getLegacyRouteRecord(target, propertyKey);
|
|
132
|
+
record.guards = mergeUnique(record.guards, guards);
|
|
133
|
+
flushLegacyRouteMetadata(target, propertyKey, record);
|
|
134
|
+
}
|
|
135
|
+
function appendLegacyRouteInterceptors(target, propertyKey, interceptors) {
|
|
136
|
+
const record = getLegacyRouteRecord(target, propertyKey);
|
|
137
|
+
record.interceptors = mergeUnique(record.interceptors, interceptors);
|
|
138
|
+
flushLegacyRouteMetadata(target, propertyKey, record);
|
|
29
139
|
}
|
|
30
140
|
function getStandardControllerRecord(metadata) {
|
|
31
141
|
const bag = getStandardMetadataBag(metadata);
|
|
@@ -77,29 +187,55 @@ function mergeStandardDtoBinding(metadata, propertyKey, partial) {
|
|
|
77
187
|
function createRouteDecorator(method) {
|
|
78
188
|
return path => {
|
|
79
189
|
validateRoutePath(path, `@${method}() path`);
|
|
80
|
-
const decorator = (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
190
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
191
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
192
|
+
const route = getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name);
|
|
193
|
+
route.method = method;
|
|
194
|
+
route.path = path;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
198
|
+
mergeLegacyRouteMetadata(valueOrTarget, contextOrPropertyKey, {
|
|
199
|
+
method,
|
|
200
|
+
path
|
|
201
|
+
});
|
|
202
|
+
}
|
|
84
203
|
};
|
|
85
204
|
return decorator;
|
|
86
205
|
};
|
|
87
206
|
}
|
|
88
207
|
function createRouteValueDecorator(apply) {
|
|
89
208
|
return value => {
|
|
90
|
-
const decorator = (
|
|
91
|
-
|
|
209
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
210
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
211
|
+
apply(getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name), value);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
215
|
+
const record = getLegacyRouteRecord(valueOrTarget, contextOrPropertyKey);
|
|
216
|
+
apply(record, value);
|
|
217
|
+
flushLegacyRouteMetadata(valueOrTarget, contextOrPropertyKey, record);
|
|
218
|
+
}
|
|
92
219
|
};
|
|
93
220
|
return decorator;
|
|
94
221
|
};
|
|
95
222
|
}
|
|
96
223
|
function createDtoFieldDecorator(source) {
|
|
97
224
|
return key => {
|
|
98
|
-
const decorator = (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
225
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
226
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
227
|
+
mergeStandardDtoBinding(contextOrPropertyKey.metadata, contextOrPropertyKey.name, {
|
|
228
|
+
key,
|
|
229
|
+
source
|
|
230
|
+
});
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (isMetadataPropertyKey(contextOrPropertyKey) && valueOrTarget && typeof valueOrTarget === 'object') {
|
|
234
|
+
mergeLegacyDtoBinding(valueOrTarget, contextOrPropertyKey, {
|
|
235
|
+
key,
|
|
236
|
+
source
|
|
237
|
+
});
|
|
238
|
+
}
|
|
103
239
|
};
|
|
104
240
|
return decorator;
|
|
105
241
|
};
|
|
@@ -113,8 +249,14 @@ function createDtoFieldDecorator(source) {
|
|
|
113
249
|
*/
|
|
114
250
|
export function Controller(basePath = '') {
|
|
115
251
|
validateRoutePath(basePath, '@Controller() base path');
|
|
116
|
-
const decorator = (
|
|
117
|
-
|
|
252
|
+
const decorator = (target, context) => {
|
|
253
|
+
if (isStandardDecoratorContext(context)) {
|
|
254
|
+
getStandardControllerRecord(context.metadata).basePath = basePath;
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
defineLegacyControllerMetadata(target, {
|
|
258
|
+
basePath
|
|
259
|
+
});
|
|
118
260
|
};
|
|
119
261
|
return decorator;
|
|
120
262
|
}
|
|
@@ -126,12 +268,26 @@ export function Controller(basePath = '') {
|
|
|
126
268
|
* @returns A decorator that applies version metadata at class or method scope.
|
|
127
269
|
*/
|
|
128
270
|
export function Version(version) {
|
|
129
|
-
const decorator = (
|
|
130
|
-
if (
|
|
131
|
-
|
|
271
|
+
const decorator = (target, contextOrPropertyKey) => {
|
|
272
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
273
|
+
if (contextOrPropertyKey.kind === 'class') {
|
|
274
|
+
getStandardControllerRecord(contextOrPropertyKey.metadata).version = version;
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name).version = version;
|
|
132
278
|
return;
|
|
133
279
|
}
|
|
134
|
-
|
|
280
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
281
|
+
mergeLegacyRouteMetadata(target, contextOrPropertyKey, {
|
|
282
|
+
version
|
|
283
|
+
});
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (typeof target === 'function') {
|
|
287
|
+
defineLegacyControllerMetadata(target, {
|
|
288
|
+
version
|
|
289
|
+
});
|
|
290
|
+
}
|
|
135
291
|
};
|
|
136
292
|
return decorator;
|
|
137
293
|
}
|
|
@@ -235,7 +391,7 @@ export const HttpCode = createRouteValueDecorator((record, status) => {
|
|
|
235
391
|
export function getRouteProducesMetadata(controllerToken, propertyKey) {
|
|
236
392
|
const bag = readStandardMetadataBag(controllerToken);
|
|
237
393
|
const routeMap = bag?.[standardRouteMetadataKey];
|
|
238
|
-
const produces = routeMap?.get(propertyKey)?.produces;
|
|
394
|
+
const produces = routeMap?.get(propertyKey)?.produces ?? legacyRouteMetadataStore.get(controllerToken.prototype)?.get(propertyKey)?.produces;
|
|
239
395
|
return produces ? [...produces] : undefined;
|
|
240
396
|
}
|
|
241
397
|
|
|
@@ -281,10 +437,18 @@ export const FromBody = createDtoFieldDecorator('body');
|
|
|
281
437
|
* @returns A field decorator that marks the DTO binding as optional.
|
|
282
438
|
*/
|
|
283
439
|
export function Optional() {
|
|
284
|
-
const decorator = (
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
440
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
441
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
442
|
+
mergeStandardDtoBinding(contextOrPropertyKey.metadata, contextOrPropertyKey.name, {
|
|
443
|
+
optional: true
|
|
444
|
+
});
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (isMetadataPropertyKey(contextOrPropertyKey) && valueOrTarget && typeof valueOrTarget === 'object') {
|
|
448
|
+
mergeLegacyDtoBinding(valueOrTarget, contextOrPropertyKey, {
|
|
449
|
+
optional: true
|
|
450
|
+
});
|
|
451
|
+
}
|
|
288
452
|
};
|
|
289
453
|
return decorator;
|
|
290
454
|
}
|
|
@@ -296,10 +460,18 @@ export function Optional() {
|
|
|
296
460
|
* @returns A field decorator that stores converter metadata for the DTO field.
|
|
297
461
|
*/
|
|
298
462
|
export function Convert(converter) {
|
|
299
|
-
const decorator = (
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
463
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
464
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
465
|
+
mergeStandardDtoBinding(contextOrPropertyKey.metadata, contextOrPropertyKey.name, {
|
|
466
|
+
converter
|
|
467
|
+
});
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (isMetadataPropertyKey(contextOrPropertyKey) && valueOrTarget && typeof valueOrTarget === 'object') {
|
|
471
|
+
mergeLegacyDtoBinding(valueOrTarget, contextOrPropertyKey, {
|
|
472
|
+
converter
|
|
473
|
+
});
|
|
474
|
+
}
|
|
303
475
|
};
|
|
304
476
|
return decorator;
|
|
305
477
|
}
|
|
@@ -312,12 +484,18 @@ export function Convert(converter) {
|
|
|
312
484
|
* @returns A method decorator that appends route-level response-header metadata.
|
|
313
485
|
*/
|
|
314
486
|
export function Header(name, value) {
|
|
315
|
-
const decorator = (
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
487
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
488
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
489
|
+
const route = getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name);
|
|
490
|
+
route.headers = [...(route.headers ?? []), {
|
|
491
|
+
name,
|
|
492
|
+
value
|
|
493
|
+
}];
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
497
|
+
appendLegacyRouteHeader(valueOrTarget, contextOrPropertyKey, name, value);
|
|
498
|
+
}
|
|
321
499
|
};
|
|
322
500
|
return decorator;
|
|
323
501
|
}
|
|
@@ -330,11 +508,22 @@ export function Header(name, value) {
|
|
|
330
508
|
* @returns A method decorator that writes redirect metadata for the route.
|
|
331
509
|
*/
|
|
332
510
|
export function Redirect(url, statusCode) {
|
|
333
|
-
const decorator = (
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
511
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
512
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
513
|
+
getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name).redirect = {
|
|
514
|
+
url,
|
|
515
|
+
statusCode
|
|
516
|
+
};
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
520
|
+
mergeLegacyRouteMetadata(valueOrTarget, contextOrPropertyKey, {
|
|
521
|
+
redirect: {
|
|
522
|
+
url,
|
|
523
|
+
statusCode
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
}
|
|
338
527
|
};
|
|
339
528
|
return decorator;
|
|
340
529
|
}
|
|
@@ -346,14 +535,24 @@ export function Redirect(url, statusCode) {
|
|
|
346
535
|
* @returns A decorator applicable to classes and methods.
|
|
347
536
|
*/
|
|
348
537
|
export function UseGuards(...guards) {
|
|
349
|
-
const decorator = (
|
|
350
|
-
if (
|
|
351
|
-
|
|
352
|
-
|
|
538
|
+
const decorator = (target, contextOrPropertyKey) => {
|
|
539
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
540
|
+
if (contextOrPropertyKey.kind === 'class') {
|
|
541
|
+
const controller = getStandardControllerRecord(contextOrPropertyKey.metadata);
|
|
542
|
+
controller.guards = mergeUnique(controller.guards, guards);
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
const route = getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name);
|
|
546
|
+
route.guards = mergeUnique(route.guards, guards);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
550
|
+
appendLegacyRouteGuards(target, contextOrPropertyKey, guards);
|
|
353
551
|
return;
|
|
354
552
|
}
|
|
355
|
-
|
|
356
|
-
|
|
553
|
+
if (typeof target === 'function') {
|
|
554
|
+
appendLegacyControllerGuards(target, guards);
|
|
555
|
+
}
|
|
357
556
|
};
|
|
358
557
|
return decorator;
|
|
359
558
|
}
|
|
@@ -365,14 +564,24 @@ export function UseGuards(...guards) {
|
|
|
365
564
|
* @returns A decorator applicable to classes and methods.
|
|
366
565
|
*/
|
|
367
566
|
export function UseInterceptors(...interceptors) {
|
|
368
|
-
const decorator = (
|
|
369
|
-
if (
|
|
370
|
-
|
|
371
|
-
|
|
567
|
+
const decorator = (target, contextOrPropertyKey) => {
|
|
568
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
569
|
+
if (contextOrPropertyKey.kind === 'class') {
|
|
570
|
+
const controller = getStandardControllerRecord(contextOrPropertyKey.metadata);
|
|
571
|
+
controller.interceptors = mergeUnique(controller.interceptors, interceptors);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
const route = getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name);
|
|
575
|
+
route.interceptors = mergeUnique(route.interceptors, interceptors);
|
|
372
576
|
return;
|
|
373
577
|
}
|
|
374
|
-
|
|
375
|
-
|
|
578
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
579
|
+
appendLegacyRouteInterceptors(target, contextOrPropertyKey, interceptors);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
if (typeof target === 'function') {
|
|
583
|
+
appendLegacyControllerInterceptors(target, interceptors);
|
|
584
|
+
}
|
|
376
585
|
};
|
|
377
586
|
return decorator;
|
|
378
587
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch-routing-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-routing-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElG;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,cAAc,EAAE,OAAO,EAAE,gBAAgB,GAAG,YAAY,CAQ3G;
|
|
1
|
+
{"version":3,"file":"dispatch-routing-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-routing-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElG;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,cAAc,EAAE,OAAO,EAAE,gBAAgB,GAAG,YAAY,CAQ3G;AA2BD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI,CAsBlH"}
|
|
@@ -13,6 +13,25 @@ export function matchHandlerOrThrow(handlerMapping, request) {
|
|
|
13
13
|
}
|
|
14
14
|
return match;
|
|
15
15
|
}
|
|
16
|
+
function defineRequestParams(request, params) {
|
|
17
|
+
Object.defineProperty(request, 'params', {
|
|
18
|
+
configurable: true,
|
|
19
|
+
enumerable: true,
|
|
20
|
+
value: params,
|
|
21
|
+
writable: true
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function findInheritedParamsDescriptor(request) {
|
|
25
|
+
let prototype = Object.getPrototypeOf(request);
|
|
26
|
+
while (prototype) {
|
|
27
|
+
const descriptor = Object.getOwnPropertyDescriptor(prototype, 'params');
|
|
28
|
+
if (descriptor) {
|
|
29
|
+
return descriptor;
|
|
30
|
+
}
|
|
31
|
+
prototype = Object.getPrototypeOf(prototype);
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
16
35
|
|
|
17
36
|
/**
|
|
18
37
|
* Update request params.
|
|
@@ -21,10 +40,20 @@ export function matchHandlerOrThrow(handlerMapping, request) {
|
|
|
21
40
|
* @param params The params.
|
|
22
41
|
*/
|
|
23
42
|
export function updateRequestParams(requestContext, params) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
value
|
|
28
|
-
|
|
29
|
-
|
|
43
|
+
const request = requestContext.request;
|
|
44
|
+
const ownDescriptor = Object.getOwnPropertyDescriptor(request, 'params');
|
|
45
|
+
if (ownDescriptor) {
|
|
46
|
+
if ('value' in ownDescriptor && ownDescriptor.writable) {
|
|
47
|
+
request.params = params;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
defineRequestParams(request, params);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const inheritedDescriptor = findInheritedParamsDescriptor(request);
|
|
54
|
+
if (!inheritedDescriptor || 'value' in inheritedDescriptor && inheritedDescriptor.writable) {
|
|
55
|
+
request.params = params;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
defineRequestParams(request, params);
|
|
30
59
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAyB,MAAM,YAAY,CAAC;AAQnE,OAAO,KAAK,EACV,MAAM,EACN,yBAAyB,EACzB,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EAIjB,cAAc,EAEd,eAAe,EAEf,cAAc,EAId,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAKrB,OAAO,EAKL,KAAK,aAAa,EAOnB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAE,4BAA4B,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE5F,gEAAgE;AAChE,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,iBAAiB,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;AAEpK,uDAAuD;AACvD,MAAM,WAAW,uBAAuB;IACtC,iDAAiD;IACjD,aAAa,CAAC,EAAE,cAAc,EAAE,CAAC;IACjC,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,kBAAkB,CAAC,EAAE,yBAAyB,CAAC;IAC/C,sDAAsD;IACtD,cAAc,EAAE,cAAc,CAAC;IAC/B,2DAA2D;IAC3D,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;IACjC,0DAA0D;IAC1D,SAAS,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAClC,+DAA+D;IAC/D,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qCAAqC;IACrC,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,sEAAsE;IACtE,YAAY,CAAC,EAAE;QACb,wDAAwD;QACxD,oBAAoB,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;KACjD,CAAC;IACF,qDAAqD;IACrD,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,qDAAqD;IACrD,aAAa,EAAE,SAAS,CAAC;IACzB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAyB,MAAM,YAAY,CAAC;AAQnE,OAAO,KAAK,EACV,MAAM,EACN,yBAAyB,EACzB,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EAIjB,cAAc,EAEd,eAAe,EAEf,cAAc,EAId,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAKrB,OAAO,EAKL,KAAK,aAAa,EAOnB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAE,4BAA4B,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE5F,gEAAgE;AAChE,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,iBAAiB,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;AAEpK,uDAAuD;AACvD,MAAM,WAAW,uBAAuB;IACtC,iDAAiD;IACjD,aAAa,CAAC,EAAE,cAAc,EAAE,CAAC;IACjC,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,kBAAkB,CAAC,EAAE,yBAAyB,CAAC;IAC/C,sDAAsD;IACtD,cAAc,EAAE,cAAc,CAAC;IAC/B,2DAA2D;IAC3D,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;IACjC,0DAA0D;IAC1D,SAAS,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAClC,+DAA+D;IAC/D,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qCAAqC;IACrC,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,sEAAsE;IACtE,YAAY,CAAC,EAAE;QACb,wDAAwD;QACxD,oBAAoB,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;KACjD,CAAC;IACF,qDAAqD;IACrD,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,qDAAqD;IACrD,aAAa,EAAE,SAAS,CAAC;IACzB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAyyBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,UAAU,CAuF7E;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,UAAU,GAAG,aAAa,GAAG,SAAS,CAE5F;AAED,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -356,6 +356,10 @@ async function dispatchNativeFastRoute(match, request, response, options, conten
|
|
|
356
356
|
if (!shouldUseFastPathForRequest(eligibility, request)) {
|
|
357
357
|
return false;
|
|
358
358
|
}
|
|
359
|
+
if (options.fastPathDebugHeaders === true && eligibility && !response.committed) {
|
|
360
|
+
const debugInfo = createPathDebugInfo(eligibility);
|
|
361
|
+
addPathDebugHeader(response.setHeader.bind(response), debugInfo);
|
|
362
|
+
}
|
|
359
363
|
const dispatchRequest = request;
|
|
360
364
|
const dispatchScope = createRootDispatchScope(options.rootContainer);
|
|
361
365
|
let phaseContext;
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
export { createFetchStyleHttpAdapterRealtimeCapability, type HttpApplicationAdapter, } from './adapter.js';
|
|
1
2
|
export { DefaultBinder } from './adapters/binding.js';
|
|
2
3
|
export { resolveClientIdentity } from './client-identity.js';
|
|
4
|
+
export type { Dispatcher } from './types.js';
|
|
3
5
|
export { attachFrameworkRequestNativeRouteHandoff, bindRawRequestNativeRouteHandoff, consumeRawRequestNativeRouteHandoff, isRoutePathNormalizationSensitive, readFrameworkRequestNativeRouteHandoff, type NativeRouteHandoff, } from './dispatch/native-route-handoff.js';
|
|
4
6
|
//# sourceMappingURL=internal.d.ts.map
|
package/dist/internal.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,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,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"}
|
package/dist/internal.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { createFetchStyleHttpAdapterRealtimeCapability } from './adapter.js';
|
|
1
2
|
export { DefaultBinder } from './adapters/binding.js';
|
|
2
3
|
export { resolveClientIdentity } from './client-identity.js';
|
|
3
4
|
export { attachFrameworkRequestNativeRouteHandoff, bindRawRequestNativeRouteHandoff, consumeRawRequestNativeRouteHandoff, isRoutePathNormalizationSensitive, readFrameworkRequestNativeRouteHandoff } from './dispatch/native-route-handoff.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"correlation.d.ts","sourceRoot":"","sources":["../../src/middleware/correlation.ts"],"names":[],"mappings":"
|
|
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,10 +1,16 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
1
|
const REQUEST_ID_HEADER = 'x-request-id';
|
|
3
2
|
const CORRELATION_ID_HEADER = 'x-correlation-id';
|
|
4
3
|
function resolveInboundRequestId(headers) {
|
|
5
4
|
const requestId = headers[REQUEST_ID_HEADER] ?? headers[CORRELATION_ID_HEADER];
|
|
6
5
|
const value = Array.isArray(requestId) ? requestId[0] : requestId;
|
|
7
|
-
return value ??
|
|
6
|
+
return value ?? createRequestId();
|
|
7
|
+
}
|
|
8
|
+
function createRequestId() {
|
|
9
|
+
const randomUUID = globalThis.crypto?.randomUUID;
|
|
10
|
+
if (randomUUID) {
|
|
11
|
+
return randomUUID.call(globalThis.crypto);
|
|
12
|
+
}
|
|
13
|
+
return `req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
/**
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"controller",
|
|
11
11
|
"rest"
|
|
12
12
|
],
|
|
13
|
-
"version": "1.0.0
|
|
13
|
+
"version": "1.0.0",
|
|
14
14
|
"private": false,
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"repository": {
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"dist"
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@fluojs/core": "^1.0.0
|
|
45
|
-
"@fluojs/
|
|
46
|
-
"@fluojs/
|
|
44
|
+
"@fluojs/core": "^1.0.0",
|
|
45
|
+
"@fluojs/validation": "^1.0.0",
|
|
46
|
+
"@fluojs/di": "^1.0.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"vitest": "^3.2.4"
|