@fluojs/runtime 1.0.0-beta.1
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/LICENSE +21 -0
- package/README.ko.md +182 -0
- package/README.md +182 -0
- package/dist/abort.d.ts +19 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/abort.js +39 -0
- package/dist/adapters/internal-http-adapter.d.ts +2 -0
- package/dist/adapters/internal-http-adapter.d.ts.map +1 -0
- package/dist/adapters/internal-http-adapter.js +1 -0
- package/dist/adapters/internal-request-response-factory.d.ts +2 -0
- package/dist/adapters/internal-request-response-factory.d.ts.map +1 -0
- package/dist/adapters/internal-request-response-factory.js +1 -0
- package/dist/adapters/request-response-factory.d.ts +17 -0
- package/dist/adapters/request-response-factory.d.ts.map +1 -0
- package/dist/adapters/request-response-factory.js +27 -0
- package/dist/bootstrap.d.ts +73 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +870 -0
- package/dist/errors.d.ts +39 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +88 -0
- package/dist/health/diagnostics.d.ts +56 -0
- package/dist/health/diagnostics.d.ts.map +1 -0
- package/dist/health/diagnostics.js +155 -0
- package/dist/health/health.d.ts +18 -0
- package/dist/health/health.d.ts.map +1 -0
- package/dist/health/health.js +82 -0
- package/dist/http-adapter-shared.d.ts +88 -0
- package/dist/http-adapter-shared.d.ts.map +1 -0
- package/dist/http-adapter-shared.js +199 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/internal-http-adapter.d.ts +2 -0
- package/dist/internal-http-adapter.d.ts.map +1 -0
- package/dist/internal-http-adapter.js +1 -0
- package/dist/internal-node.d.ts +2 -0
- package/dist/internal-node.d.ts.map +1 -0
- package/dist/internal-node.js +1 -0
- package/dist/internal-request-response-factory.d.ts +2 -0
- package/dist/internal-request-response-factory.d.ts.map +1 -0
- package/dist/internal-request-response-factory.js +1 -0
- package/dist/internal.d.ts +2 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +1 -0
- package/dist/logging/json-logger.d.ts +3 -0
- package/dist/logging/json-logger.d.ts.map +1 -0
- package/dist/logging/json-logger.js +39 -0
- package/dist/logging/logger.d.ts +3 -0
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/logging/logger.js +36 -0
- package/dist/module-graph.d.ts +26 -0
- package/dist/module-graph.d.ts.map +1 -0
- package/dist/module-graph.js +248 -0
- package/dist/multipart.d.ts +45 -0
- package/dist/multipart.d.ts.map +1 -0
- package/dist/multipart.js +195 -0
- package/dist/node/internal-node-compression.d.ts +7 -0
- package/dist/node/internal-node-compression.d.ts.map +1 -0
- package/dist/node/internal-node-compression.js +68 -0
- package/dist/node/internal-node-request.d.ts +34 -0
- package/dist/node/internal-node-request.d.ts.map +1 -0
- package/dist/node/internal-node-request.js +195 -0
- package/dist/node/internal-node-response.d.ts +8 -0
- package/dist/node/internal-node-response.d.ts.map +1 -0
- package/dist/node/internal-node-response.js +166 -0
- package/dist/node/internal-node-shutdown.d.ts +34 -0
- package/dist/node/internal-node-shutdown.d.ts.map +1 -0
- package/dist/node/internal-node-shutdown.js +83 -0
- package/dist/node/internal-node.d.ts +80 -0
- package/dist/node/internal-node.d.ts.map +1 -0
- package/dist/node/internal-node.js +209 -0
- package/dist/node/node-compression.d.ts +2 -0
- package/dist/node/node-compression.d.ts.map +1 -0
- package/dist/node/node-compression.js +1 -0
- package/dist/node/node-request.d.ts +2 -0
- package/dist/node/node-request.d.ts.map +1 -0
- package/dist/node/node-request.js +1 -0
- package/dist/node/node-response.d.ts +2 -0
- package/dist/node/node-response.d.ts.map +1 -0
- package/dist/node/node-response.js +1 -0
- package/dist/node/node-shutdown.d.ts +2 -0
- package/dist/node/node-shutdown.d.ts.map +1 -0
- package/dist/node/node-shutdown.js +1 -0
- package/dist/node/node.d.ts +2 -0
- package/dist/node/node.d.ts.map +1 -0
- package/dist/node/node.js +1 -0
- package/dist/node.d.ts +5 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +3 -0
- package/dist/platform-contract.d.ts +140 -0
- package/dist/platform-contract.d.ts.map +1 -0
- package/dist/platform-contract.js +1 -0
- package/dist/platform-shell.d.ts +45 -0
- package/dist/platform-shell.d.ts.map +1 -0
- package/dist/platform-shell.js +368 -0
- package/dist/request-transaction.d.ts +17 -0
- package/dist/request-transaction.d.ts.map +1 -0
- package/dist/request-transaction.js +39 -0
- package/dist/tokens.d.ts +27 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +24 -0
- package/dist/types.d.ts +161 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/web.d.ts +58 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +431 -0
- package/package.json +86 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { Constructor, MaybePromise, Token } from '@fluojs/core';
|
|
2
|
+
import type { Container, Provider } from '@fluojs/di';
|
|
3
|
+
import type { ConverterLike, Dispatcher, FrameworkRequest, FrameworkResponse, HttpApplicationAdapter, InterceptorLike, MiddlewareLike, RequestObserverLike, VersioningOptions } from '@fluojs/http';
|
|
4
|
+
import type { BootstrapTimingDiagnostics } from './health/diagnostics.js';
|
|
5
|
+
import type { PlatformComponentInput } from './platform-contract.js';
|
|
6
|
+
/** Module class accepted by bootstrap and module-graph compilation helpers. */
|
|
7
|
+
export type ModuleType = Constructor & {
|
|
8
|
+
definition?: ModuleDefinition;
|
|
9
|
+
};
|
|
10
|
+
/** Controller class discovered inside one compiled module definition. */
|
|
11
|
+
export type ControllerType = Constructor;
|
|
12
|
+
/** Programmatic module definition consumed by `defineModule()` and bootstrap. */
|
|
13
|
+
export interface ModuleDefinition {
|
|
14
|
+
imports?: ModuleType[];
|
|
15
|
+
providers?: Provider[];
|
|
16
|
+
controllers?: ControllerType[];
|
|
17
|
+
exports?: Token[];
|
|
18
|
+
middleware?: MiddlewareLike[];
|
|
19
|
+
global?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** Low-level options used while compiling the runtime module graph. */
|
|
22
|
+
export interface BootstrapModuleOptions {
|
|
23
|
+
duplicateProviderPolicy?: 'warn' | 'throw' | 'ignore';
|
|
24
|
+
logger?: ApplicationLogger;
|
|
25
|
+
providers?: Provider[];
|
|
26
|
+
validationTokens?: Token[];
|
|
27
|
+
}
|
|
28
|
+
/** Compiled module record produced by module-graph analysis. */
|
|
29
|
+
export interface CompiledModule {
|
|
30
|
+
type: ModuleType;
|
|
31
|
+
definition: ModuleDefinition;
|
|
32
|
+
exportedTokens: Set<Token>;
|
|
33
|
+
providerTokens: Set<Token>;
|
|
34
|
+
}
|
|
35
|
+
/** Result returned by low-level bootstrap compilation helpers. */
|
|
36
|
+
export interface BootstrapResult {
|
|
37
|
+
container: Container;
|
|
38
|
+
modules: CompiledModule[];
|
|
39
|
+
rootModule: ModuleType;
|
|
40
|
+
}
|
|
41
|
+
/** Lifecycle hook invoked after one module's providers are instantiated. */
|
|
42
|
+
export interface OnModuleInit {
|
|
43
|
+
onModuleInit(): MaybePromise<void>;
|
|
44
|
+
}
|
|
45
|
+
/** Lifecycle hook invoked after the full application bootstrap finishes. */
|
|
46
|
+
export interface OnApplicationBootstrap {
|
|
47
|
+
onApplicationBootstrap(): MaybePromise<void>;
|
|
48
|
+
}
|
|
49
|
+
/** Lifecycle hook invoked when one module is being torn down. */
|
|
50
|
+
export interface OnModuleDestroy {
|
|
51
|
+
onModuleDestroy(): MaybePromise<void>;
|
|
52
|
+
}
|
|
53
|
+
/** Lifecycle hook invoked during application shutdown with the active signal. */
|
|
54
|
+
export interface OnApplicationShutdown {
|
|
55
|
+
onApplicationShutdown(signal?: string): MaybePromise<void>;
|
|
56
|
+
}
|
|
57
|
+
/** Convenience union covering every public runtime lifecycle hook contract. */
|
|
58
|
+
export type LifecycleHooks = OnModuleInit | OnApplicationBootstrap | OnModuleDestroy | OnApplicationShutdown;
|
|
59
|
+
/** Logger contract used by runtime bootstrap and lifecycle diagnostics. */
|
|
60
|
+
export interface ApplicationLogger {
|
|
61
|
+
debug(message: string, context?: string): void;
|
|
62
|
+
error(message: string, error?: unknown, context?: string): void;
|
|
63
|
+
log(message: string, context?: string): void;
|
|
64
|
+
warn(message: string, context?: string): void;
|
|
65
|
+
}
|
|
66
|
+
/** Runtime-visible application states for HTTP and microservice shells. */
|
|
67
|
+
export type ApplicationState = 'bootstrapped' | 'ready' | 'closed';
|
|
68
|
+
/**
|
|
69
|
+
* Called when an unhandled error escapes the request pipeline.
|
|
70
|
+
* Return a value to override the default error response, or return `undefined`
|
|
71
|
+
* to fall through to the next filter or the built-in 500 handler.
|
|
72
|
+
*/
|
|
73
|
+
export interface ExceptionFilterContext {
|
|
74
|
+
request: FrameworkRequest;
|
|
75
|
+
response: FrameworkResponse;
|
|
76
|
+
requestId?: string;
|
|
77
|
+
}
|
|
78
|
+
/** Error filter contract evaluated when a request pipeline throws. */
|
|
79
|
+
export interface ExceptionFilterHandler {
|
|
80
|
+
catch(error: unknown, context: ExceptionFilterContext): MaybePromise<boolean | void>;
|
|
81
|
+
}
|
|
82
|
+
/** High-level bootstrap options for creating an HTTP application shell. */
|
|
83
|
+
export interface BootstrapApplicationOptions {
|
|
84
|
+
adapter?: HttpApplicationAdapter;
|
|
85
|
+
/**
|
|
86
|
+
* Policy for duplicate provider tokens across modules.
|
|
87
|
+
*
|
|
88
|
+
* - `'warn'` — log a warning but continue bootstrap (default)
|
|
89
|
+
* - `'throw'` — throw a `DuplicateProviderError` and abort bootstrap
|
|
90
|
+
* - `'ignore'` — silently allow duplicates (last-registered wins)
|
|
91
|
+
*/
|
|
92
|
+
duplicateProviderPolicy?: 'warn' | 'throw' | 'ignore';
|
|
93
|
+
/**
|
|
94
|
+
* Global exception filters, evaluated before any module/controller/handler
|
|
95
|
+
* scoped filters. Each filter's `catch()` is called in order; the first
|
|
96
|
+
* one that returns `true` (handled) stops the chain.
|
|
97
|
+
*/
|
|
98
|
+
filters?: ExceptionFilterHandler[];
|
|
99
|
+
converters?: readonly ConverterLike[];
|
|
100
|
+
interceptors?: InterceptorLike[];
|
|
101
|
+
logger?: ApplicationLogger;
|
|
102
|
+
middleware?: MiddlewareLike[];
|
|
103
|
+
observers?: RequestObserverLike[];
|
|
104
|
+
providers?: Provider[];
|
|
105
|
+
platform?: {
|
|
106
|
+
components?: readonly PlatformComponentInput[];
|
|
107
|
+
};
|
|
108
|
+
rootModule: ModuleType;
|
|
109
|
+
diagnostics?: {
|
|
110
|
+
timing?: boolean;
|
|
111
|
+
};
|
|
112
|
+
versioning?: VersioningOptions;
|
|
113
|
+
}
|
|
114
|
+
/** Options accepted by `FluoFactory.create(...)`. */
|
|
115
|
+
export type CreateApplicationOptions = Omit<BootstrapApplicationOptions, 'rootModule'>;
|
|
116
|
+
/** Options accepted by `FluoFactory.createApplicationContext(...)`. */
|
|
117
|
+
export interface CreateApplicationContextOptions extends Omit<BootstrapApplicationOptions, 'adapter' | 'converters' | 'filters' | 'middleware' | 'observers' | 'rootModule'> {
|
|
118
|
+
}
|
|
119
|
+
/** Runtime transport contract used by microservice application shells. */
|
|
120
|
+
export interface MicroserviceRuntime {
|
|
121
|
+
emit?(pattern: string, payload: unknown): MaybePromise<void>;
|
|
122
|
+
listen(): MaybePromise<void>;
|
|
123
|
+
send?(pattern: string, payload: unknown, signal?: AbortSignal): MaybePromise<unknown>;
|
|
124
|
+
}
|
|
125
|
+
/** Options accepted by `Application.connectMicroservice(...)`. */
|
|
126
|
+
export interface CreateMicroserviceOptions extends CreateApplicationContextOptions {
|
|
127
|
+
microserviceToken?: Token<MicroserviceRuntime>;
|
|
128
|
+
}
|
|
129
|
+
/** Dependency-injection application shell without an HTTP listener. */
|
|
130
|
+
export interface ApplicationContext {
|
|
131
|
+
readonly bootstrapTiming?: BootstrapTimingDiagnostics;
|
|
132
|
+
readonly container: Container;
|
|
133
|
+
readonly modules: CompiledModule[];
|
|
134
|
+
readonly rootModule: ModuleType;
|
|
135
|
+
close(signal?: string): Promise<void>;
|
|
136
|
+
get<T>(token: Token<T>): Promise<T>;
|
|
137
|
+
}
|
|
138
|
+
/** Full HTTP application shell returned by `FluoFactory.create(...)`. */
|
|
139
|
+
export interface Application {
|
|
140
|
+
readonly bootstrapTiming?: BootstrapTimingDiagnostics;
|
|
141
|
+
readonly container: Container;
|
|
142
|
+
readonly modules: CompiledModule[];
|
|
143
|
+
readonly rootModule: ModuleType;
|
|
144
|
+
readonly state: ApplicationState;
|
|
145
|
+
readonly dispatcher: Dispatcher;
|
|
146
|
+
close(signal?: string): Promise<void>;
|
|
147
|
+
connectMicroservice(options?: CreateMicroserviceOptions): Promise<MicroserviceApplication>;
|
|
148
|
+
dispatch: Dispatcher['dispatch'];
|
|
149
|
+
get<T>(token: Token<T>): Promise<T>;
|
|
150
|
+
startAllMicroservices(): Promise<void>;
|
|
151
|
+
listen(): Promise<void>;
|
|
152
|
+
ready(): Promise<void>;
|
|
153
|
+
}
|
|
154
|
+
/** Connected microservice shell managed by an HTTP application or context. */
|
|
155
|
+
export interface MicroserviceApplication extends ApplicationContext {
|
|
156
|
+
readonly state: ApplicationState;
|
|
157
|
+
emit(pattern: string, payload: unknown): Promise<void>;
|
|
158
|
+
listen(): Promise<void>;
|
|
159
|
+
send(pattern: string, payload: unknown, signal?: AbortSignal): Promise<unknown>;
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/web.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type Dispatcher, type FrameworkRequest, type FrameworkResponse } from '@fluojs/http';
|
|
2
|
+
import { type MultipartOptions, type UploadedFile } from './multipart.js';
|
|
3
|
+
import { type RequestResponseFactory } from './adapters/request-response-factory.js';
|
|
4
|
+
declare module '@fluojs/http' {
|
|
5
|
+
interface FrameworkRequest {
|
|
6
|
+
files?: UploadedFile[];
|
|
7
|
+
rawBody?: Uint8Array;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Configures Web request parsing, multipart handling, and raw body preservation.
|
|
12
|
+
*/
|
|
13
|
+
export interface CreateWebRequestResponseFactoryOptions {
|
|
14
|
+
maxBodySize?: number;
|
|
15
|
+
multipart?: MultipartOptions;
|
|
16
|
+
rawBody?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Describes a dispatched Web request and the runtime options used to handle it.
|
|
20
|
+
*/
|
|
21
|
+
export interface DispatchWebRequestOptions extends CreateWebRequestResponseFactoryOptions {
|
|
22
|
+
dispatcher?: Dispatcher;
|
|
23
|
+
dispatcherNotReadyMessage?: string;
|
|
24
|
+
request: Request;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Represents a framework response that can be materialized as a native Web `Response`.
|
|
28
|
+
*/
|
|
29
|
+
export interface WebFrameworkResponse extends FrameworkResponse {
|
|
30
|
+
toResponse(): Response;
|
|
31
|
+
}
|
|
32
|
+
export { parseMultipart } from './multipart.js';
|
|
33
|
+
/**
|
|
34
|
+
* Creates the request/response factory used by Web-standard adapters.
|
|
35
|
+
*
|
|
36
|
+
* @param options - Web parsing options for body limits, multipart handling, and raw body retention.
|
|
37
|
+
* @returns A request/response factory for Web requests and responses.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createWebRequestResponseFactory(options?: CreateWebRequestResponseFactoryOptions): RequestResponseFactory<Request, AbortSignal | undefined, WebFrameworkResponse>;
|
|
40
|
+
/**
|
|
41
|
+
* Dispatches a native Web request through the shared runtime pipeline.
|
|
42
|
+
*
|
|
43
|
+
* @param options - Dispatch configuration including the request and runtime parsing options.
|
|
44
|
+
* @returns The native Web response produced by the dispatcher.
|
|
45
|
+
*/
|
|
46
|
+
export declare function dispatchWebRequest({ dispatcher, dispatcherNotReadyMessage, request, ...options }: DispatchWebRequestOptions): Promise<Response>;
|
|
47
|
+
/**
|
|
48
|
+
* Creates a framework request from a native Web request.
|
|
49
|
+
*
|
|
50
|
+
* @param request - Native Web request to normalize.
|
|
51
|
+
* @param signal - Abort signal propagated to the framework request.
|
|
52
|
+
* @param multipartOptions - Multipart parser options applied to multipart requests.
|
|
53
|
+
* @param maxBodySize - Maximum allowed non-multipart body size in bytes.
|
|
54
|
+
* @param preserveRawBody - Whether to retain the raw request body bytes.
|
|
55
|
+
* @returns The normalized framework request used by the dispatcher.
|
|
56
|
+
*/
|
|
57
|
+
export declare function createWebFrameworkRequest(request: Request, signal: AbortSignal, multipartOptions?: MultipartOptions, maxBodySize?: number, preserveRawBody?: boolean): Promise<FrameworkRequest>;
|
|
58
|
+
//# sourceMappingURL=web.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,wCAAwC,CAAC;AAEhD,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,gBAAgB;QACxB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;QACvB,OAAO,CAAC,EAAE,UAAU,CAAC;KACtB;CACF;AAOD;;GAEG;AACH,MAAM,WAAW,sCAAsC;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,yBAA0B,SAAQ,sCAAsC;IACvF,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,UAAU,IAAI,QAAQ,CAAC;CACxB;AAED,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAqLhD;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,sCAA2C,GACnD,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,EAAE,oBAAoB,CAAC,CA0BhF;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,UAAU,EACV,yBAAiG,EACjG,OAAO,EACP,GAAG,OAAO,EACX,EAAE,yBAAyB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAU/C;AAED;;;;;;;;;GASG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,WAAW,EACnB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,WAAW,SAAwB,EACnC,eAAe,UAAQ,GACtB,OAAO,CAAC,gBAAgB,CAAC,CA6C3B"}
|
package/dist/web.js
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import { BadRequestException, createErrorResponse, HttpException, InternalServerErrorException, PayloadTooLargeException } from '@fluojs/http';
|
|
2
|
+
import { parseMultipart } from './multipart.js';
|
|
3
|
+
import { dispatchWithRequestResponseFactory } from './adapters/request-response-factory.js';
|
|
4
|
+
const DEFAULT_MAX_BODY_SIZE = 1 * 1024 * 1024;
|
|
5
|
+
const TEXT_ENCODER = new TextEncoder();
|
|
6
|
+
const TEXT_DECODER = new TextDecoder();
|
|
7
|
+
const REQUEST_BODY_LIMIT_MESSAGE = 'Request body exceeds the size limit.';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Configures Web request parsing, multipart handling, and raw body preservation.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Describes a dispatched Web request and the runtime options used to handle it.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Represents a framework response that can be materialized as a native Web `Response`.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export { parseMultipart } from './multipart.js';
|
|
22
|
+
class WebResponseStream {
|
|
23
|
+
closeListeners = new Set();
|
|
24
|
+
controller;
|
|
25
|
+
markedActive = false;
|
|
26
|
+
markedClosed = false;
|
|
27
|
+
readable = new ReadableStream({
|
|
28
|
+
cancel: () => {
|
|
29
|
+
this.close();
|
|
30
|
+
},
|
|
31
|
+
start: controller => {
|
|
32
|
+
this.controller = controller;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
constructor(onActivate) {
|
|
36
|
+
this.onActivate = onActivate;
|
|
37
|
+
}
|
|
38
|
+
get closed() {
|
|
39
|
+
return this.markedClosed;
|
|
40
|
+
}
|
|
41
|
+
close() {
|
|
42
|
+
this.activate();
|
|
43
|
+
if (this.markedClosed) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
this.markedClosed = true;
|
|
47
|
+
this.controller?.close();
|
|
48
|
+
this.emitClose();
|
|
49
|
+
}
|
|
50
|
+
flush() {
|
|
51
|
+
this.activate();
|
|
52
|
+
}
|
|
53
|
+
onClose(listener) {
|
|
54
|
+
if (this.markedClosed) {
|
|
55
|
+
listener();
|
|
56
|
+
return () => {};
|
|
57
|
+
}
|
|
58
|
+
this.closeListeners.add(listener);
|
|
59
|
+
return () => {
|
|
60
|
+
this.closeListeners.delete(listener);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
waitForDrain() {
|
|
64
|
+
this.activate();
|
|
65
|
+
return Promise.resolve();
|
|
66
|
+
}
|
|
67
|
+
write(chunk) {
|
|
68
|
+
this.activate();
|
|
69
|
+
if (this.markedClosed) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
this.controller?.enqueue(typeof chunk === 'string' ? TEXT_ENCODER.encode(chunk) : chunk);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
activate() {
|
|
76
|
+
if (this.markedActive) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.markedActive = true;
|
|
80
|
+
this.onActivate();
|
|
81
|
+
}
|
|
82
|
+
emitClose() {
|
|
83
|
+
for (const listener of this.closeListeners) {
|
|
84
|
+
listener();
|
|
85
|
+
}
|
|
86
|
+
this.closeListeners.clear();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
class MutableWebFrameworkResponse {
|
|
90
|
+
committed = false;
|
|
91
|
+
headers = {};
|
|
92
|
+
raw;
|
|
93
|
+
statusCode;
|
|
94
|
+
statusSet;
|
|
95
|
+
finalizedResponse;
|
|
96
|
+
responseStream = new WebResponseStream(() => {
|
|
97
|
+
this.streamActive = true;
|
|
98
|
+
});
|
|
99
|
+
responseBody;
|
|
100
|
+
streamActive = false;
|
|
101
|
+
stream = this.responseStream;
|
|
102
|
+
redirect(status, location) {
|
|
103
|
+
this.setStatus(status);
|
|
104
|
+
this.setHeader('Location', location);
|
|
105
|
+
void this.send(undefined);
|
|
106
|
+
}
|
|
107
|
+
async send(body) {
|
|
108
|
+
if (this.finalizedResponse) {
|
|
109
|
+
this.committed = true;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const serialized = serializeWebResponseBody(body, typeof this.headers['Content-Type'] === 'string' ? this.headers['Content-Type'] : typeof this.headers['content-type'] === 'string' ? this.headers['content-type'] : undefined);
|
|
113
|
+
if (serialized.defaultContentType && !hasHeader(this.headers, 'content-type')) {
|
|
114
|
+
this.setHeader('Content-Type', serialized.defaultContentType);
|
|
115
|
+
}
|
|
116
|
+
this.responseBody = serialized.payload;
|
|
117
|
+
this.committed = true;
|
|
118
|
+
}
|
|
119
|
+
setHeader(name, value) {
|
|
120
|
+
const existingHeaderName = findHeaderName(this.headers, name) ?? name;
|
|
121
|
+
if (name.toLowerCase() === 'set-cookie') {
|
|
122
|
+
this.headers[existingHeaderName] = mergeSetCookieHeader(this.headers[existingHeaderName], value);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
this.headers[existingHeaderName] = value;
|
|
126
|
+
}
|
|
127
|
+
setStatus(code) {
|
|
128
|
+
this.statusCode = code;
|
|
129
|
+
this.statusSet = true;
|
|
130
|
+
}
|
|
131
|
+
toResponse() {
|
|
132
|
+
if (!this.finalizedResponse) {
|
|
133
|
+
const init = {
|
|
134
|
+
headers: toResponseHeaders(this.headers),
|
|
135
|
+
status: this.statusCode ?? 200
|
|
136
|
+
};
|
|
137
|
+
const responseBody = this.responseBody instanceof Uint8Array ? this.responseBody.slice().buffer : this.responseBody;
|
|
138
|
+
this.finalizedResponse = this.streamActive ? new Response(this.responseStream.readable, init) : new Response(responseBody ?? '', init);
|
|
139
|
+
this.raw = this.finalizedResponse;
|
|
140
|
+
this.committed = true;
|
|
141
|
+
}
|
|
142
|
+
return this.finalizedResponse;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Creates the request/response factory used by Web-standard adapters.
|
|
148
|
+
*
|
|
149
|
+
* @param options - Web parsing options for body limits, multipart handling, and raw body retention.
|
|
150
|
+
* @returns A request/response factory for Web requests and responses.
|
|
151
|
+
*/
|
|
152
|
+
export function createWebRequestResponseFactory(options = {}) {
|
|
153
|
+
return {
|
|
154
|
+
async createRequest(request, signal) {
|
|
155
|
+
return await createWebFrameworkRequest(request, signal, options.multipart, options.maxBodySize ?? DEFAULT_MAX_BODY_SIZE, options.rawBody ?? false);
|
|
156
|
+
},
|
|
157
|
+
createRequestSignal(signal) {
|
|
158
|
+
return signal ?? new AbortController().signal;
|
|
159
|
+
},
|
|
160
|
+
createResponse() {
|
|
161
|
+
return new MutableWebFrameworkResponse();
|
|
162
|
+
},
|
|
163
|
+
resolveRequestId(request) {
|
|
164
|
+
return request.headers.get('x-request-id') ?? request.headers.get('x-correlation-id') ?? undefined;
|
|
165
|
+
},
|
|
166
|
+
async writeErrorResponse(error, response, requestId) {
|
|
167
|
+
const httpError = toHttpException(error);
|
|
168
|
+
response.setStatus(httpError.status);
|
|
169
|
+
await response.send(createErrorResponse(httpError, requestId));
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Dispatches a native Web request through the shared runtime pipeline.
|
|
176
|
+
*
|
|
177
|
+
* @param options - Dispatch configuration including the request and runtime parsing options.
|
|
178
|
+
* @returns The native Web response produced by the dispatcher.
|
|
179
|
+
*/
|
|
180
|
+
export async function dispatchWebRequest({
|
|
181
|
+
dispatcher,
|
|
182
|
+
dispatcherNotReadyMessage = 'Web adapter received a request before dispatcher binding completed.',
|
|
183
|
+
request,
|
|
184
|
+
...options
|
|
185
|
+
}) {
|
|
186
|
+
const frameworkResponse = await dispatchWithRequestResponseFactory({
|
|
187
|
+
dispatcher,
|
|
188
|
+
dispatcherNotReadyMessage,
|
|
189
|
+
factory: createWebRequestResponseFactory(options),
|
|
190
|
+
rawRequest: request,
|
|
191
|
+
rawResponse: request.signal
|
|
192
|
+
});
|
|
193
|
+
return frameworkResponse.toResponse();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Creates a framework request from a native Web request.
|
|
198
|
+
*
|
|
199
|
+
* @param request - Native Web request to normalize.
|
|
200
|
+
* @param signal - Abort signal propagated to the framework request.
|
|
201
|
+
* @param multipartOptions - Multipart parser options applied to multipart requests.
|
|
202
|
+
* @param maxBodySize - Maximum allowed non-multipart body size in bytes.
|
|
203
|
+
* @param preserveRawBody - Whether to retain the raw request body bytes.
|
|
204
|
+
* @returns The normalized framework request used by the dispatcher.
|
|
205
|
+
*/
|
|
206
|
+
export async function createWebFrameworkRequest(request, signal, multipartOptions, maxBodySize = DEFAULT_MAX_BODY_SIZE, preserveRawBody = false) {
|
|
207
|
+
const url = new URL(request.url);
|
|
208
|
+
const headers = cloneWebHeaders(request.headers);
|
|
209
|
+
const contentType = request.headers.get('content-type') ?? undefined;
|
|
210
|
+
const isMultipart = typeof contentType === 'string' && contentType.includes('multipart/form-data');
|
|
211
|
+
let body;
|
|
212
|
+
let files;
|
|
213
|
+
let rawBody;
|
|
214
|
+
if (isMultipart) {
|
|
215
|
+
const result = await parseMultipart(request.clone(), {
|
|
216
|
+
...multipartOptions,
|
|
217
|
+
maxTotalSize: multipartOptions?.maxTotalSize ?? maxBodySize
|
|
218
|
+
});
|
|
219
|
+
body = result.fields;
|
|
220
|
+
files = result.files;
|
|
221
|
+
} else {
|
|
222
|
+
const bodyResult = await readWebRequestBody(request.clone(), contentType, maxBodySize, preserveRawBody);
|
|
223
|
+
body = bodyResult.body;
|
|
224
|
+
rawBody = bodyResult.rawBody;
|
|
225
|
+
}
|
|
226
|
+
const frameworkRequest = {
|
|
227
|
+
body,
|
|
228
|
+
cookies: parseCookieHeader(request.headers.get('cookie') ?? undefined),
|
|
229
|
+
headers,
|
|
230
|
+
method: request.method,
|
|
231
|
+
params: {},
|
|
232
|
+
path: url.pathname,
|
|
233
|
+
query: parseQueryParams(url.searchParams),
|
|
234
|
+
raw: request,
|
|
235
|
+
signal,
|
|
236
|
+
url: url.pathname + url.search
|
|
237
|
+
};
|
|
238
|
+
if (files) {
|
|
239
|
+
frameworkRequest.files = files;
|
|
240
|
+
}
|
|
241
|
+
if (rawBody) {
|
|
242
|
+
frameworkRequest.rawBody = rawBody;
|
|
243
|
+
}
|
|
244
|
+
return frameworkRequest;
|
|
245
|
+
}
|
|
246
|
+
function parseQueryParams(searchParams) {
|
|
247
|
+
const query = {};
|
|
248
|
+
for (const [key, value] of searchParams.entries()) {
|
|
249
|
+
const current = query[key];
|
|
250
|
+
if (current === undefined) {
|
|
251
|
+
query[key] = value;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (Array.isArray(current)) {
|
|
255
|
+
current.push(value);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
query[key] = [current, value];
|
|
259
|
+
}
|
|
260
|
+
return query;
|
|
261
|
+
}
|
|
262
|
+
function cloneWebHeaders(headers) {
|
|
263
|
+
const clonedHeaders = {};
|
|
264
|
+
for (const [name, value] of headers.entries()) {
|
|
265
|
+
clonedHeaders[name] = value;
|
|
266
|
+
}
|
|
267
|
+
return clonedHeaders;
|
|
268
|
+
}
|
|
269
|
+
function decodeCookieValue(raw) {
|
|
270
|
+
try {
|
|
271
|
+
return decodeURIComponent(raw);
|
|
272
|
+
} catch {
|
|
273
|
+
return raw;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function parseCookieHeader(cookieHeader) {
|
|
277
|
+
if (!cookieHeader) {
|
|
278
|
+
return {};
|
|
279
|
+
}
|
|
280
|
+
return Object.fromEntries(cookieHeader.split(';').map(pair => pair.trim()).filter(Boolean).map(pair => {
|
|
281
|
+
const index = pair.indexOf('=');
|
|
282
|
+
if (index === -1) {
|
|
283
|
+
return [pair.trim(), ''];
|
|
284
|
+
}
|
|
285
|
+
return [pair.slice(0, index).trim(), decodeCookieValue(pair.slice(index + 1).trim())];
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
async function readWebRequestBody(request, contentType, maxBodySize = DEFAULT_MAX_BODY_SIZE, preserveRawBody = false) {
|
|
289
|
+
const contentLength = request.headers.get('content-length');
|
|
290
|
+
if (contentLength !== null) {
|
|
291
|
+
const parsedContentLength = Number(contentLength);
|
|
292
|
+
if (Number.isFinite(parsedContentLength) && parsedContentLength > maxBodySize) {
|
|
293
|
+
throw new PayloadTooLargeException(REQUEST_BODY_LIMIT_MESSAGE);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (!request.body) {
|
|
297
|
+
return {
|
|
298
|
+
body: undefined
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const rawBody = await readByteLimitedStream(request.body, maxBodySize);
|
|
302
|
+
if (rawBody.byteLength === 0) {
|
|
303
|
+
return {
|
|
304
|
+
body: undefined
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
const bodyText = TEXT_DECODER.decode(rawBody);
|
|
308
|
+
if (bodyText.length === 0) {
|
|
309
|
+
return {
|
|
310
|
+
body: undefined,
|
|
311
|
+
rawBody: preserveRawBody ? rawBody : undefined
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
if (typeof contentType === 'string' && contentType.includes('application/json')) {
|
|
315
|
+
try {
|
|
316
|
+
return {
|
|
317
|
+
body: JSON.parse(bodyText),
|
|
318
|
+
rawBody: preserveRawBody ? rawBody : undefined
|
|
319
|
+
};
|
|
320
|
+
} catch {
|
|
321
|
+
throw new BadRequestException('Request body contains invalid JSON.');
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
body: bodyText,
|
|
326
|
+
rawBody: preserveRawBody ? rawBody : undefined
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
async function readByteLimitedStream(stream, maxBodySize) {
|
|
330
|
+
const reader = stream.getReader();
|
|
331
|
+
const chunks = [];
|
|
332
|
+
let totalSize = 0;
|
|
333
|
+
try {
|
|
334
|
+
while (true) {
|
|
335
|
+
const {
|
|
336
|
+
done,
|
|
337
|
+
value
|
|
338
|
+
} = await reader.read();
|
|
339
|
+
if (done) {
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
totalSize += value.byteLength;
|
|
343
|
+
if (totalSize > maxBodySize) {
|
|
344
|
+
await reader.cancel(REQUEST_BODY_LIMIT_MESSAGE);
|
|
345
|
+
throw new PayloadTooLargeException(REQUEST_BODY_LIMIT_MESSAGE);
|
|
346
|
+
}
|
|
347
|
+
chunks.push(value);
|
|
348
|
+
}
|
|
349
|
+
} finally {
|
|
350
|
+
reader.releaseLock();
|
|
351
|
+
}
|
|
352
|
+
return concatUint8Arrays(chunks, totalSize);
|
|
353
|
+
}
|
|
354
|
+
function concatUint8Arrays(chunks, totalSize) {
|
|
355
|
+
const rawBody = new Uint8Array(totalSize);
|
|
356
|
+
let offset = 0;
|
|
357
|
+
for (const chunk of chunks) {
|
|
358
|
+
rawBody.set(chunk, offset);
|
|
359
|
+
offset += chunk.byteLength;
|
|
360
|
+
}
|
|
361
|
+
return rawBody;
|
|
362
|
+
}
|
|
363
|
+
function mergeSetCookieHeader(current, incoming) {
|
|
364
|
+
const nextValues = Array.isArray(incoming) ? incoming : [incoming];
|
|
365
|
+
if (current === undefined) {
|
|
366
|
+
return nextValues.length === 1 ? nextValues[0] : [...nextValues];
|
|
367
|
+
}
|
|
368
|
+
const currentValues = Array.isArray(current) ? current : [current];
|
|
369
|
+
const merged = [...currentValues, ...nextValues];
|
|
370
|
+
return merged.length === 1 ? merged[0] : merged;
|
|
371
|
+
}
|
|
372
|
+
function findHeaderName(headers, name) {
|
|
373
|
+
const lowerName = name.toLowerCase();
|
|
374
|
+
return Object.keys(headers).find(key => key.toLowerCase() === lowerName);
|
|
375
|
+
}
|
|
376
|
+
function hasHeader(headers, name) {
|
|
377
|
+
return findHeaderName(headers, name) !== undefined;
|
|
378
|
+
}
|
|
379
|
+
function toResponseHeaders(headers) {
|
|
380
|
+
const responseHeaders = new Headers();
|
|
381
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
382
|
+
if (Array.isArray(value)) {
|
|
383
|
+
for (const headerValue of value) {
|
|
384
|
+
responseHeaders.append(name, headerValue);
|
|
385
|
+
}
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
responseHeaders.set(name, value);
|
|
389
|
+
}
|
|
390
|
+
return responseHeaders;
|
|
391
|
+
}
|
|
392
|
+
function serializeWebResponseBody(body, contentType) {
|
|
393
|
+
if (body === undefined) {
|
|
394
|
+
return {
|
|
395
|
+
payload: ''
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
if (body instanceof Uint8Array) {
|
|
399
|
+
return {
|
|
400
|
+
defaultContentType: 'application/octet-stream',
|
|
401
|
+
payload: body
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
if (body instanceof ArrayBuffer) {
|
|
405
|
+
return {
|
|
406
|
+
defaultContentType: 'application/octet-stream',
|
|
407
|
+
payload: new Uint8Array(body)
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
if (typeof body === 'string') {
|
|
411
|
+
return {
|
|
412
|
+
defaultContentType: isJsonContentType(contentType) ? undefined : 'text/plain; charset=utf-8',
|
|
413
|
+
payload: isJsonContentType(contentType) ? JSON.stringify(body) : body
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
return {
|
|
417
|
+
defaultContentType: 'application/json; charset=utf-8',
|
|
418
|
+
payload: JSON.stringify(body)
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
function isJsonContentType(contentType) {
|
|
422
|
+
return typeof contentType === 'string' && contentType.toLowerCase().includes('application/json');
|
|
423
|
+
}
|
|
424
|
+
function toHttpException(error) {
|
|
425
|
+
if (error instanceof HttpException) {
|
|
426
|
+
return error;
|
|
427
|
+
}
|
|
428
|
+
return new InternalServerErrorException('Internal server error.', {
|
|
429
|
+
cause: error
|
|
430
|
+
});
|
|
431
|
+
}
|