@fluojs/http
Advanced tools
| import type { RequestContextStore } from './request-context-store.js'; | ||
| type AsyncLocalStorageConstructor = new () => RequestContextStore; | ||
| type NodeAsyncHooksModule = { | ||
| AsyncLocalStorage?: AsyncLocalStorageConstructor; | ||
| }; | ||
| type AsyncLocalStorageResolutionHost = { | ||
| AsyncLocalStorage?: AsyncLocalStorageConstructor; | ||
| process?: { | ||
| getBuiltinModule?(id: 'node:async_hooks'): NodeAsyncHooksModule; | ||
| versions?: { | ||
| node?: string; | ||
| }; | ||
| }; | ||
| }; | ||
| type NodeAsyncHooksLoader = () => Promise<NodeAsyncHooksModule>; | ||
| /** | ||
| * Resolves the host `AsyncLocalStorage` constructor without eagerly importing Node built-ins. | ||
| * | ||
| * @param host Host global-like object to inspect for async-context support. | ||
| * @param loadNodeAsyncHooks Lazy loader for Node's `node:async_hooks` module. | ||
| * @returns The resolved `AsyncLocalStorage` constructor, or `undefined` when unavailable. | ||
| */ | ||
| export declare function resolveAsyncLocalStorageConstructor(host?: AsyncLocalStorageResolutionHost, loadNodeAsyncHooks?: NodeAsyncHooksLoader): Promise<AsyncLocalStorageConstructor | undefined>; | ||
| export {}; | ||
| //# sourceMappingURL=request-context-node-store.d.ts.map |
| {"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"} |
| /** | ||
| * Resolves the host `AsyncLocalStorage` constructor without eagerly importing Node built-ins. | ||
| * | ||
| * @param host Host global-like object to inspect for async-context support. | ||
| * @param loadNodeAsyncHooks Lazy loader for Node's `node:async_hooks` module. | ||
| * @returns The resolved `AsyncLocalStorage` constructor, or `undefined` when unavailable. | ||
| */ | ||
| export async function resolveAsyncLocalStorageConstructor(host = globalThis, loadNodeAsyncHooks = importNodeAsyncHooks) { | ||
| if (typeof host.AsyncLocalStorage === 'function') { | ||
| return host.AsyncLocalStorage; | ||
| } | ||
| const builtinAsyncLocalStorage = host.process?.getBuiltinModule?.('node:async_hooks').AsyncLocalStorage; | ||
| if (typeof builtinAsyncLocalStorage === 'function') { | ||
| return builtinAsyncLocalStorage; | ||
| } | ||
| if (!isNodeHost(host)) { | ||
| return undefined; | ||
| } | ||
| try { | ||
| const nodeAsyncHooks = await loadNodeAsyncHooks(); | ||
| return nodeAsyncHooks.AsyncLocalStorage; | ||
| } catch { | ||
| return undefined; | ||
| } | ||
| } | ||
| function isNodeHost(host) { | ||
| return typeof host.process?.versions?.node === 'string'; | ||
| } | ||
| function importNodeAsyncHooks() { | ||
| const nodeAsyncHooksSpecifier = `node:${'async_hooks'}`; | ||
| return import(nodeAsyncHooksSpecifier); | ||
| } |
| import type { RequestContextStore } from './request-context-store.js'; | ||
| /** | ||
| * Creates the synchronous fallback request-context store used when no async-context primitive exists. | ||
| * | ||
| * @returns A stack-backed request-context store scoped to synchronous callback frames. | ||
| */ | ||
| export declare function createStackRequestContextStore(): RequestContextStore; | ||
| //# sourceMappingURL=request-context-stack-store.d.ts.map |
| {"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"} |
| /** | ||
| * Creates the synchronous fallback request-context store used when no async-context primitive exists. | ||
| * | ||
| * @returns A stack-backed request-context store scoped to synchronous callback frames. | ||
| */ | ||
| export function createStackRequestContextStore() { | ||
| const stack = []; | ||
| return { | ||
| getStore() { | ||
| return stack.at(-1); | ||
| }, | ||
| run(context, callback) { | ||
| stack.push(context); | ||
| try { | ||
| return callback(); | ||
| } catch (error) { | ||
| throw error; | ||
| } finally { | ||
| removeStackContext(stack, context); | ||
| } | ||
| } | ||
| }; | ||
| } | ||
| function removeStackContext(stack, context) { | ||
| const index = stack.lastIndexOf(context); | ||
| if (index >= 0) { | ||
| stack.splice(index, 1); | ||
| } | ||
| } |
| import type { RequestContext } from '../types.js'; | ||
| /** Store abstraction shared by host async-context implementations and the synchronous fallback. */ | ||
| export type RequestContextStore = { | ||
| getStore(): RequestContext | undefined; | ||
| run<T>(context: RequestContext, callback: () => T): T; | ||
| }; | ||
| //# sourceMappingURL=request-context-store.d.ts.map |
| {"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"} |
| export {}; |
| import type { ContextKey, RequestContext } from '../types.js'; | ||
| /** | ||
| * Runs a callback inside the request-scoped AsyncLocalStorage context. | ||
| * Runs a callback inside the request-scoped async context. | ||
| * | ||
| * Hosts with `AsyncLocalStorage` preserve the context across awaited work. Hosts without an async | ||
| * context primitive use a stack fallback that keeps the context only for the synchronous callback | ||
| * frame and clears it before awaited continuations resume. | ||
| * | ||
| * @param context Request context snapshot to bind to the current async execution chain. | ||
@@ -24,5 +28,5 @@ * @param callback Callback executed with `context` available through request-context helpers. | ||
| /** | ||
| * Creates a defensive clone of a request context for AsyncLocalStorage storage. | ||
| * Creates a defensive clone of a request context for async-context storage. | ||
| * | ||
| * @param context Request context to clone before storing in AsyncLocalStorage. | ||
| * @param context Request context to clone before storing in the active async-context store. | ||
| * @returns A shallow clone with copied metadata map to avoid cross-request mutation. | ||
@@ -29,0 +33,0 @@ */ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/context/request-context.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAI9D;;;;;;GAMG;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"} | ||
| {"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,8 +0,13 @@ | ||
| import { AsyncLocalStorage } from 'node:async_hooks'; | ||
| import { FluoError } from '@fluojs/core'; | ||
| const requestContextStore = new AsyncLocalStorage(); | ||
| import { resolveAsyncLocalStorageConstructor } from './request-context-node-store.js'; | ||
| import { createStackRequestContextStore } from './request-context-stack-store.js'; | ||
| const requestContextStore = await createRequestContextStore(); | ||
| /** | ||
| * Runs a callback inside the request-scoped AsyncLocalStorage context. | ||
| * Runs a callback inside the request-scoped async context. | ||
| * | ||
| * Hosts with `AsyncLocalStorage` preserve the context across awaited work. Hosts without an async | ||
| * context primitive use a stack fallback that keeps the context only for the synchronous callback | ||
| * frame and clears it before awaited continuations resume. | ||
| * | ||
| * @param context Request context snapshot to bind to the current async execution chain. | ||
@@ -42,5 +47,5 @@ * @param callback Callback executed with `context` available through request-context helpers. | ||
| /** | ||
| * Creates a defensive clone of a request context for AsyncLocalStorage storage. | ||
| * Creates a defensive clone of a request context for async-context storage. | ||
| * | ||
| * @param context Request context to clone before storing in AsyncLocalStorage. | ||
| * @param context Request context to clone before storing in the active async-context store. | ||
| * @returns A shallow clone with copied metadata map to avoid cross-request mutation. | ||
@@ -90,2 +95,9 @@ */ | ||
| context.metadata[key.id] = value; | ||
| } | ||
| async function createRequestContextStore() { | ||
| const AsyncLocalStorage = await resolveAsyncLocalStorageConstructor(); | ||
| if (typeof AsyncLocalStorage === 'function') { | ||
| return new AsyncLocalStorage(); | ||
| } | ||
| return createStackRequestContextStore(); | ||
| } |
@@ -1,1 +0,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;AAoyBD;;;;;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"} | ||
| {"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"} |
@@ -359,2 +359,6 @@ import { getCompiledDtoBindingPlan } from '../adapters/dto-binding-plan.js'; | ||
| } | ||
| if (options.fastPathDebugHeaders === true && eligibility && !response.committed) { | ||
| const debugInfo = createPathDebugInfo(eligibility); | ||
| addPathDebugHeader(response.setHeader.bind(response), debugInfo); | ||
| } | ||
| const dispatchRequest = request; | ||
@@ -361,0 +365,0 @@ const dispatchScope = createRootDispatchScope(options.rootContainer); |
@@ -0,4 +1,6 @@ | ||
| export { createFetchStyleHttpAdapterRealtimeCapability, type HttpApplicationAdapter, } from './adapter.js'; | ||
| export { DefaultBinder } from './adapters/binding.js'; | ||
| export { resolveClientIdentity } from './client-identity.js'; | ||
| export type { Dispatcher } from './types.js'; | ||
| export { attachFrameworkRequestNativeRouteHandoff, bindRawRequestNativeRouteHandoff, consumeRawRequestNativeRouteHandoff, isRoutePathNormalizationSensitive, readFrameworkRequestNativeRouteHandoff, type NativeRouteHandoff, } from './dispatch/native-route-handoff.js'; | ||
| //# sourceMappingURL=internal.d.ts.map |
@@ -1,1 +0,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"} | ||
| {"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
-0
@@ -0,3 +1,4 @@ | ||
| export { createFetchStyleHttpAdapterRealtimeCapability } from './adapter.js'; | ||
| export { DefaultBinder } from './adapters/binding.js'; | ||
| export { resolveClientIdentity } from './client-identity.js'; | ||
| export { attachFrameworkRequestNativeRouteHandoff, bindRawRequestNativeRouteHandoff, consumeRawRequestNativeRouteHandoff, isRoutePathNormalizationSensitive, readFrameworkRequestNativeRouteHandoff } from './dispatch/native-route-handoff.js'; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"correlation.d.ts","sourceRoot":"","sources":["../../src/middleware/correlation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAY9C;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,UAAU,CAYxD"} | ||
| {"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,2 +0,1 @@ | ||
| import { randomUUID } from 'node:crypto'; | ||
| const REQUEST_ID_HEADER = 'x-request-id'; | ||
@@ -7,4 +6,11 @@ const CORRELATION_ID_HEADER = 'x-correlation-id'; | ||
| const value = Array.isArray(requestId) ? requestId[0] : requestId; | ||
| return value ?? randomUUID(); | ||
| return value ?? createRequestId(); | ||
| } | ||
| function createRequestId() { | ||
| const randomUUID = globalThis.crypto?.randomUUID; | ||
| if (randomUUID) { | ||
| return randomUUID.call(globalThis.crypto); | ||
| } | ||
| return `req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`; | ||
| } | ||
@@ -11,0 +17,0 @@ /** |
+4
-4
@@ -13,3 +13,3 @@ { | ||
| ], | ||
| "version": "1.0.0-beta.10", | ||
| "version": "1.0.0-beta.11", | ||
| "private": false, | ||
@@ -45,5 +45,5 @@ "license": "MIT", | ||
| "dependencies": { | ||
| "@fluojs/core": "^1.0.0-beta.4", | ||
| "@fluojs/validation": "^1.0.0-beta.3", | ||
| "@fluojs/di": "^1.0.0-beta.6" | ||
| "@fluojs/core": "^1.0.0-beta.6", | ||
| "@fluojs/validation": "^1.0.0-beta.4", | ||
| "@fluojs/di": "^1.0.0-beta.8" | ||
| }, | ||
@@ -50,0 +50,0 @@ "devDependencies": { |
+4
-1
@@ -99,2 +99,4 @@ # @fluojs/http | ||
| `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이 재개되기 전에 컨텍스트를 비웁니다. | ||
| ### 프록시 뒤의 속도 제한 | ||
@@ -137,3 +139,3 @@ | ||
| 디스패처는 활성 dispatch 동안에만 `AsyncLocalStorage`로 `RequestContext`를 바인딩합니다. 요청이 controller graph, middleware, guard, interceptor, observer, DTO converter, custom binder 또는 수동 `getCurrentRequestContext()` / `assertRequestContext()` container 접근을 통해 request-scoped DI를 사용할 수 있으면, 디스패처는 요청 observer가 끝난 뒤 `finally` 경로에서 isolated request-scoped DI 컨테이너를 생성하고 dispose합니다. Singleton-only route는 `RequestContext.container`가 접근되기 전까지 이 컨테이너 lifecycle을 건너뛰어 baseline 경로의 불필요한 per-request allocation을 피하면서도, graph가 모호하거나 request-scoped이면 request-scoped provider isolation을 유지합니다. 따라서 공개 `RequestContext.container` 읽기는 request-scoped provider resolve에 항상 안전합니다. singleton-only fast path는 내부 dispatcher 최적화일 뿐, 공개 context가 root container를 노출한다는 약속이 아닙니다. | ||
| 디스패처는 활성 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를 노출한다는 약속이 아닙니다. | ||
@@ -163,2 +165,3 @@ 어댑터는 플랫폼이 제공한다면 `FrameworkRequest.signal`에 `AbortSignal`을 전달해야 합니다. SSE에서는 가능하면 `FrameworkResponse.stream.onClose(...)`도 노출해야 합니다. `SseResponse`는 request abort와 raw stream close를 모두 구독하고, 멱등하게 닫히며, 어느 쪽이 먼저 종료되더라도 등록한 listener를 제거합니다. | ||
| - `resolveClientIdentity(request)`: 속도 제한과 런타임 통합에서 사용하는 보수적 클라이언트 식별 해석기. | ||
| - `createFetchStyleHttpAdapterRealtimeCapability(...)`, `Dispatcher`, `HttpApplicationAdapter`: 전체 HTTP root barrel을 instantiate하면 안 되는 edge/fetch-style platform package를 위한 내부 adapter seam. | ||
@@ -165,0 +168,0 @@ ## 관련 패키지 |
+4
-1
@@ -101,2 +101,4 @@ # @fluojs/http | ||
| `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. | ||
| ### Rate limiting behind proxies | ||
@@ -139,3 +141,3 @@ | ||
| The dispatcher binds `RequestContext` with `AsyncLocalStorage` for the active dispatch only. When a request may use request-scoped DI through its controller graph, middleware, guards, interceptors, observers, DTO converters, a custom binder, or manual `getCurrentRequestContext()` / `assertRequestContext()` container access, the dispatcher creates and disposes an isolated request-scoped DI container from its `finally` path after request observers finish. Singleton-only routes skip that container lifecycle until `RequestContext.container` is accessed, so the baseline path avoids unnecessary per-request allocation while preserving request-scoped provider isolation whenever the graph is ambiguous or request-scoped. Public `RequestContext.container` reads are therefore always safe for resolving request-scoped providers; the singleton-only fast path is an internal dispatcher optimization, not a promise that the public context exposes the root container. | ||
| 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. | ||
@@ -165,2 +167,3 @@ 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. | ||
| - `resolveClientIdentity(request)`: Conservative client identity resolver used by rate limiting and other runtime integrations. | ||
| - `createFetchStyleHttpAdapterRealtimeCapability(...)`, `Dispatcher`, and `HttpApplicationAdapter`: internal adapter seams for edge/fetch-style platform packages that must avoid instantiating the full HTTP root barrel. | ||
@@ -167,0 +170,0 @@ ## Related Packages |
264952
3.63%118
8.26%5409
2.37%179
1.7%Updated
Updated