@fluojs/runtime 1.0.0-beta.2 → 1.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.ko.md +7 -0
  2. package/README.md +7 -0
  3. package/dist/adapters/request-response-factory.d.ts +9 -0
  4. package/dist/adapters/request-response-factory.d.ts.map +1 -1
  5. package/dist/adapters/request-response-factory.js +11 -0
  6. package/dist/bootstrap.d.ts.map +1 -1
  7. package/dist/bootstrap.js +220 -46
  8. package/dist/health/diagnostics.d.ts +38 -0
  9. package/dist/health/diagnostics.d.ts.map +1 -1
  10. package/dist/health/diagnostics.js +48 -0
  11. package/dist/health/health.d.ts +21 -0
  12. package/dist/health/health.d.ts.map +1 -1
  13. package/dist/health/health.js +27 -0
  14. package/dist/logging/json-logger.d.ts +5 -0
  15. package/dist/logging/json-logger.d.ts.map +1 -1
  16. package/dist/logging/json-logger.js +6 -0
  17. package/dist/logging/logger.d.ts +5 -0
  18. package/dist/logging/logger.d.ts.map +1 -1
  19. package/dist/logging/logger.js +6 -0
  20. package/dist/module-graph.d.ts.map +1 -1
  21. package/dist/module-graph.js +17 -7
  22. package/dist/node/internal-node-compression.d.ts +15 -0
  23. package/dist/node/internal-node-compression.d.ts.map +1 -1
  24. package/dist/node/internal-node-compression.js +16 -0
  25. package/dist/node/internal-node-request.d.ts +21 -0
  26. package/dist/node/internal-node-request.d.ts.map +1 -1
  27. package/dist/node/internal-node-request.js +100 -29
  28. package/dist/node/internal-node-response.d.ts +21 -1
  29. package/dist/node/internal-node-response.d.ts.map +1 -1
  30. package/dist/node/internal-node-response.js +42 -3
  31. package/dist/node/internal-node.d.ts +40 -0
  32. package/dist/node/internal-node.d.ts.map +1 -1
  33. package/dist/node/internal-node.js +56 -3
  34. package/dist/node/node-request.d.ts +1 -1
  35. package/dist/node/node-request.d.ts.map +1 -1
  36. package/dist/node/node-request.js +1 -1
  37. package/dist/platform-shell.d.ts +4 -0
  38. package/dist/platform-shell.d.ts.map +1 -1
  39. package/dist/platform-shell.js +72 -20
  40. package/dist/request-transaction.d.ts +28 -0
  41. package/dist/request-transaction.d.ts.map +1 -1
  42. package/dist/request-transaction.js +33 -0
  43. package/dist/types.d.ts +9 -0
  44. package/dist/types.d.ts.map +1 -1
  45. package/dist/web.d.ts.map +1 -1
  46. package/dist/web.js +116 -40
  47. package/package.json +6 -6
@@ -1,18 +1,39 @@
1
1
  import type { ModuleType } from '../types.js';
2
+ /**
3
+ * Describes the health status contract.
4
+ */
2
5
  export interface HealthStatus {
3
6
  status: 'ok' | 'unavailable';
4
7
  }
8
+ /**
9
+ * Describes the health check response contract.
10
+ */
5
11
  export interface HealthCheckResponse {
6
12
  body: unknown;
7
13
  statusCode?: number;
8
14
  }
15
+ /**
16
+ * Describes the readiness status contract.
17
+ */
9
18
  export interface ReadinessStatus {
10
19
  status: 'ready' | 'starting' | 'unavailable';
11
20
  }
21
+ /**
22
+ * Describes the health module options contract.
23
+ */
12
24
  export interface HealthModuleOptions {
13
25
  healthCheck?: (ctx: import('@fluojs/http').RequestContext) => HealthStatus | HealthCheckResponse | Promise<HealthStatus | HealthCheckResponse>;
14
26
  path?: string;
15
27
  }
28
+ /**
29
+ * Defines the readiness check type.
30
+ */
16
31
  export type ReadinessCheck = () => boolean | Promise<boolean>;
32
+ /**
33
+ * Create health module.
34
+ *
35
+ * @param options The options.
36
+ * @returns The create health module result.
37
+ */
17
38
  export declare function createHealthModule(options?: HealthModuleOptions): ModuleType;
18
39
  //# sourceMappingURL=health.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/health/health.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,IAAI,GAAG,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,aAAa,CAAC;CAC9C;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,cAAc,EAAE,cAAc,KACrD,YAAY,GACZ,mBAAmB,GACnB,OAAO,CAAC,YAAY,GAAG,mBAAmB,CAAC,CAAC;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAE9D,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,UAAU,CA0EhF"}
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/health/health.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,IAAI,GAAG,aAAa,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,aAAa,CAAC;CAC9C;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,cAAc,EAAE,cAAc,KACrD,YAAY,GACZ,mBAAmB,GACnB,OAAO,CAAC,YAAY,GAAG,mBAAmB,CAAC,CAAC;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAE9D;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,UAAU,CA0EhF"}
@@ -5,6 +5,33 @@ function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.descrip
5
5
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
6
6
  import { Controller, Get } from '@fluojs/http';
7
7
  import { defineModule } from '../bootstrap.js';
8
+
9
+ /**
10
+ * Describes the health status contract.
11
+ */
12
+
13
+ /**
14
+ * Describes the health check response contract.
15
+ */
16
+
17
+ /**
18
+ * Describes the readiness status contract.
19
+ */
20
+
21
+ /**
22
+ * Describes the health module options contract.
23
+ */
24
+
25
+ /**
26
+ * Defines the readiness check type.
27
+ */
28
+
29
+ /**
30
+ * Create health module.
31
+ *
32
+ * @param options The options.
33
+ * @returns The create health module result.
34
+ */
8
35
  export function createHealthModule(options = {}) {
9
36
  let _initProto, _initClass;
10
37
  const basePath = options.path ?? '';
@@ -1,3 +1,8 @@
1
1
  import type { ApplicationLogger } from '../types.js';
2
+ /**
3
+ * Create json application logger.
4
+ *
5
+ * @returns The create json application logger result.
6
+ */
2
7
  export declare function createJsonApplicationLogger(): ApplicationLogger;
3
8
  //# sourceMappingURL=json-logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"json-logger.d.ts","sourceRoot":"","sources":["../../src/logging/json-logger.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqCrD,wBAAgB,2BAA2B,IAAI,iBAAiB,CAe/D"}
1
+ {"version":3,"file":"json-logger.d.ts","sourceRoot":"","sources":["../../src/logging/json-logger.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqCrD;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,iBAAiB,CAe/D"}
@@ -21,6 +21,12 @@ function buildEntry(level, message, context, error) {
21
21
  }
22
22
  return entry;
23
23
  }
24
+
25
+ /**
26
+ * Create json application logger.
27
+ *
28
+ * @returns The create json application logger result.
29
+ */
24
30
  export function createJsonApplicationLogger() {
25
31
  return {
26
32
  debug(message, context) {
@@ -1,3 +1,8 @@
1
1
  import type { ApplicationLogger } from '../types.js';
2
+ /**
3
+ * Create console application logger.
4
+ *
5
+ * @returns The create console application logger result.
6
+ */
2
7
  export declare function createConsoleApplicationLogger(): ApplicationLogger;
3
8
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logging/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAuBrD,wBAAgB,8BAA8B,IAAI,iBAAiB,CAmBlE"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logging/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAuBrD;;;;GAIG;AACH,wBAAgB,8BAA8B,IAAI,iBAAiB,CAmBlE"}
@@ -15,6 +15,12 @@ function formatLog(level, context, message, color) {
15
15
  const contextLabel = colorize(`[${context}]`, BRIGHT_YELLOW, color);
16
16
  return `${prefix} ${pid} - ${timestamp} ${levelLabel} ${contextLabel} ${message}`;
17
17
  }
18
+
19
+ /**
20
+ * Create console application logger.
21
+ *
22
+ * @returns The create console application logger result.
23
+ */
18
24
  export function createConsoleApplicationLogger() {
19
25
  return {
20
26
  debug(message, context = 'fluo') {
@@ -1 +1 @@
1
- {"version":3,"file":"module-graph.d.ts","sourceRoot":"","sources":["../src/module-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAK1C,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AAEvG;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAMvD;AAyDD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,GAAE,QAAQ,EAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAE5E;AAyTD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,sBAA2B,GAAG,cAAc,EAAE,CASjH"}
1
+ {"version":3,"file":"module-graph.d.ts","sourceRoot":"","sources":["../src/module-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAK1C,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AAEvG;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAMvD;AAyDD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,GAAE,QAAQ,EAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAE5E;AA8UD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,sBAA2B,GAAG,cAAc,EAAE,CASjH"}
@@ -132,7 +132,9 @@ function compileModule(moduleType, runtimeProviderTokens, compiled = new Map(),
132
132
  const compiledModule = {
133
133
  type: moduleType,
134
134
  definition,
135
+ accessibleTokens: new Set(),
135
136
  exportedTokens: new Set(),
137
+ importedExportedTokens: new Set(),
136
138
  providerTokens
137
139
  };
138
140
  compiled.set(moduleType, compiledModule);
@@ -154,11 +156,21 @@ function resolveImportedModules(compiledModule, compiledByType) {
154
156
  });
155
157
  }
156
158
  function createImportedExportedTokenSet(importedModules) {
157
- return new Set(importedModules.flatMap(imported => Array.from(imported.exportedTokens)));
159
+ const importedExportedTokens = new Set();
160
+ for (const importedModule of importedModules) {
161
+ for (const token of importedModule.exportedTokens) {
162
+ importedExportedTokens.add(token);
163
+ }
164
+ }
165
+ return importedExportedTokens;
158
166
  }
159
167
  function createAccessibleTokenSet(runtimeProviderTokens, moduleProviderTokens, importedExportedTokens, globalExportedTokens) {
160
168
  return new Set([...runtimeProviderTokens, ...moduleProviderTokens, ...importedExportedTokens, ...globalExportedTokens]);
161
169
  }
170
+ function memoizeAccessibleTokenSet(compiledModule, importedModules, runtimeProviderTokens, globalExportedTokens) {
171
+ compiledModule.importedExportedTokens = createImportedExportedTokenSet(importedModules);
172
+ compiledModule.accessibleTokens = createAccessibleTokenSet(runtimeProviderTokens, compiledModule.providerTokens, compiledModule.importedExportedTokens, globalExportedTokens);
173
+ }
162
174
  function validateProviderVisibility(compiledModule, scope, accessibleTokens) {
163
175
  for (const provider of compiledModule.definition.providers ?? []) {
164
176
  validateProviderInjectionMetadata(provider, scope);
@@ -222,12 +234,10 @@ function validateCompiledModules(modules, runtimeProviders, runtimeProviderToken
222
234
  }
223
235
  for (const compiledModule of modules) {
224
236
  const scope = `module ${compiledModule.type.name}`;
225
- const importedModules = resolveImportedModules(compiledModule, compiledByType);
226
- const importedExportedTokens = createImportedExportedTokenSet(importedModules);
227
- const accessibleTokens = createAccessibleTokenSet(runtimeProviderTokens, compiledModule.providerTokens, importedExportedTokens, globalExportedTokens);
228
- validateProviderVisibility(compiledModule, scope, accessibleTokens);
229
- validateControllerVisibility(compiledModule, scope, accessibleTokens);
230
- compiledModule.exportedTokens = createExportedTokenSet(compiledModule, importedExportedTokens);
237
+ memoizeAccessibleTokenSet(compiledModule, resolveImportedModules(compiledModule, compiledByType), runtimeProviderTokens, globalExportedTokens);
238
+ validateProviderVisibility(compiledModule, scope, compiledModule.accessibleTokens);
239
+ validateControllerVisibility(compiledModule, scope, compiledModule.accessibleTokens);
240
+ compiledModule.exportedTokens = createExportedTokenSet(compiledModule, compiledModule.importedExportedTokens);
231
241
  }
232
242
  }
233
243
 
@@ -1,7 +1,22 @@
1
1
  import type { ServerResponse } from 'node:http';
2
2
  import type { FrameworkResponseCompression } from '@fluojs/http';
3
3
  type Encoding = 'br' | 'gzip' | 'identity';
4
+ /**
5
+ * Create node response compression.
6
+ *
7
+ * @param response The response.
8
+ * @param acceptEncoding The accept encoding.
9
+ * @returns The create node response compression result.
10
+ */
4
11
  export declare function createNodeResponseCompression(response: ServerResponse, acceptEncoding: string | undefined): FrameworkResponseCompression | undefined;
12
+ /**
13
+ * Compress node response.
14
+ *
15
+ * @param response The response.
16
+ * @param body The body.
17
+ * @param encoding The encoding.
18
+ * @returns The compress node response result.
19
+ */
5
20
  export declare function compressNodeResponse(response: ServerResponse, body: Uint8Array, encoding: Exclude<Encoding, 'identity'>): Promise<void>;
6
21
  export {};
7
22
  //# sourceMappingURL=internal-node-compression.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal-node-compression.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-compression.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,KAAK,EACV,4BAA4B,EAE7B,MAAM,cAAc,CAAC;AAiBtB,KAAK,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,UAAU,CAAC;AAE3C,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,cAAc,EACxB,cAAc,EAAE,MAAM,GAAG,SAAS,GACjC,4BAA4B,GAAG,SAAS,CAqB1C;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAcf"}
1
+ {"version":3,"file":"internal-node-compression.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-compression.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,KAAK,EACV,4BAA4B,EAE7B,MAAM,cAAc,CAAC;AAiBtB,KAAK,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,UAAU,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,cAAc,EACxB,cAAc,EAAE,MAAM,GAAG,SAAS,GACjC,4BAA4B,GAAG,SAAS,CAqB1C;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAcf"}
@@ -1,6 +1,13 @@
1
1
  import { createBrotliCompress, createGzip } from 'node:zlib';
2
2
  const COMPRESS_THRESHOLD = 1024;
3
3
  const SKIP_CONTENT_TYPES = new Set(['image/', 'audio/', 'video/', 'application/zip', 'application/gzip', 'application/x-bzip', 'application/x-bzip2', 'application/x-xz', 'application/zstd', 'application/octet-stream']);
4
+ /**
5
+ * Create node response compression.
6
+ *
7
+ * @param response The response.
8
+ * @param acceptEncoding The accept encoding.
9
+ * @returns The create node response compression result.
10
+ */
4
11
  export function createNodeResponseCompression(response, acceptEncoding) {
5
12
  if (!acceptEncoding) {
6
13
  return undefined;
@@ -19,6 +26,15 @@ export function createNodeResponseCompression(response, acceptEncoding) {
19
26
  }
20
27
  };
21
28
  }
29
+
30
+ /**
31
+ * Compress node response.
32
+ *
33
+ * @param response The response.
34
+ * @param body The body.
35
+ * @param encoding The encoding.
36
+ * @returns The compress node response result.
37
+ */
22
38
  export function compressNodeResponse(response, body, encoding) {
23
39
  const stream = encoding === 'br' ? createBrotliCompress() : createGzip();
24
40
  response.setHeader('Content-Encoding', encoding);
@@ -1,6 +1,9 @@
1
1
  import type { IncomingHttpHeaders, IncomingMessage, ServerResponse } from 'node:http';
2
2
  import { PayloadTooLargeException, type FrameworkRequest } from '@fluojs/http';
3
3
  import { type MultipartOptions } from '../multipart.js';
4
+ /**
5
+ * HTTP payload-size error that closes the underlying Node request stream after the response commits.
6
+ */
4
7
  export declare class NodeRequestPayloadTooLargeException extends PayloadTooLargeException {
5
8
  private readonly request;
6
9
  constructor(request: IncomingMessage);
@@ -17,6 +20,24 @@ export declare class NodeRequestPayloadTooLargeException extends PayloadTooLarge
17
20
  * @returns The normalized framework request used by the dispatcher.
18
21
  */
19
22
  export declare function createFrameworkRequest(request: IncomingMessage, signal: AbortSignal, multipartOptions?: MultipartOptions, maxBodySize?: number, preserveRawBody?: boolean): Promise<FrameworkRequest>;
23
+ /**
24
+ * Creates the cheap Node framework request shell before consuming the body stream.
25
+ *
26
+ * @param request - Raw Node request carrying headers, URL, and body stream.
27
+ * @param signal - Abort signal tied to the response lifecycle.
28
+ * @param multipartOptions - Multipart parser options applied when materializing multipart requests.
29
+ * @param maxBodySize - Maximum allowed non-multipart body size in bytes.
30
+ * @param preserveRawBody - Whether materialization should retain raw request body bytes.
31
+ * @returns The framework request shell with metadata snapshotted and body materialization deferred.
32
+ */
33
+ export declare function createDeferredFrameworkRequest(request: IncomingMessage, signal: AbortSignal, multipartOptions?: MultipartOptions, maxBodySize?: number, preserveRawBody?: boolean): FrameworkRequest;
34
+ /**
35
+ * Materializes a deferred Node framework request body exactly once.
36
+ *
37
+ * @param request - Framework request returned by {@link createDeferredFrameworkRequest}.
38
+ * @returns A promise that settles after body, rawBody, and files fields are populated when applicable.
39
+ */
40
+ export declare function materializeFrameworkRequestBody(request: FrameworkRequest): Promise<void>;
20
41
  /**
21
42
  * Creates an abort signal that fires when the Node response closes unexpectedly.
22
43
  *
@@ -1 +1 @@
1
- {"version":3,"file":"internal-node-request.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EACf,cAAc,EACf,MAAM,WAAW,CAAC;AAInB,OAAO,EAEL,wBAAwB,EACxB,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAEL,KAAK,gBAAgB,EAEtB,MAAM,iBAAiB,CAAC;AAOzB,qBAAa,mCAAoC,SAAQ,wBAAwB;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,eAAe;IAIrD,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;CAahD;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,WAAW,EACnB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,WAAW,SAAkB,EAC7B,eAAe,UAAQ,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAqD3B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,GAAG,WAAW,CAezE;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAG5F"}
1
+ {"version":3,"file":"internal-node-request.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EACf,cAAc,EACf,MAAM,WAAW,CAAC;AAInB,OAAO,EAEL,wBAAwB,EACxB,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAEL,KAAK,gBAAgB,EAEtB,MAAM,iBAAiB,CAAC;AAUzB;;GAEG;AACH,qBAAa,mCAAoC,SAAQ,wBAAwB;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,eAAe;IAIrD,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;CAahD;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,WAAW,EACnB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,WAAW,SAAkB,EAC7B,eAAe,UAAQ,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAW3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,WAAW,EACnB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,WAAW,SAAkB,EAC7B,eAAe,UAAQ,GACtB,gBAAgB,CA2DlB;AAoBD;;;;;GAKG;AACH,wBAAsB,+BAA+B,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9F;AAyBD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,GAAG,WAAW,CAezE;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAG5F"}
@@ -2,6 +2,9 @@ import { Readable } from 'node:stream';
2
2
  import { URL } from 'node:url';
3
3
  import { BadRequestException, PayloadTooLargeException } from '@fluojs/http';
4
4
  import { parseMultipart } from '../multipart.js';
5
+ /**
6
+ * HTTP payload-size error that closes the underlying Node request stream after the response commits.
7
+ */
5
8
  export class NodeRequestPayloadTooLargeException extends PayloadTooLargeException {
6
9
  constructor(request) {
7
10
  super('Request body exceeds the size limit.');
@@ -31,49 +34,114 @@ export class NodeRequestPayloadTooLargeException extends PayloadTooLargeExceptio
31
34
  * @returns The normalized framework request used by the dispatcher.
32
35
  */
33
36
  export async function createFrameworkRequest(request, signal, multipartOptions, maxBodySize = 1 * 1024 * 1024, preserveRawBody = false) {
37
+ const frameworkRequest = createDeferredFrameworkRequest(request, signal, multipartOptions, maxBodySize, preserveRawBody);
38
+ await materializeFrameworkRequestBody(frameworkRequest);
39
+ return frameworkRequest;
40
+ }
41
+
42
+ /**
43
+ * Creates the cheap Node framework request shell before consuming the body stream.
44
+ *
45
+ * @param request - Raw Node request carrying headers, URL, and body stream.
46
+ * @param signal - Abort signal tied to the response lifecycle.
47
+ * @param multipartOptions - Multipart parser options applied when materializing multipart requests.
48
+ * @param maxBodySize - Maximum allowed non-multipart body size in bytes.
49
+ * @param preserveRawBody - Whether materialization should retain raw request body bytes.
50
+ * @returns The framework request shell with metadata snapshotted and body materialization deferred.
51
+ */
52
+ export function createDeferredFrameworkRequest(request, signal, multipartOptions, maxBodySize = 1 * 1024 * 1024, preserveRawBody = false) {
34
53
  const url = new URL(request.url ?? '/', 'http://localhost');
35
54
  const headers = cloneRequestHeaders(request.headers);
55
+ const cookieHeader = cloneHeaderValue(headers.cookie);
56
+ const searchParams = new URLSearchParams(url.searchParams);
57
+ const cookies = createMemoizedValue(() => parseCookieHeader(cookieHeader));
58
+ const query = createMemoizedValue(() => parseQueryParams(searchParams));
36
59
  const contentType = readPrimaryHeaderValue(headers['content-type']);
37
60
  const isMultipart = typeof contentType === 'string' && contentType.includes('multipart/form-data');
38
- let body;
39
- let files;
40
- let rawBody;
41
- if (isMultipart) {
42
- const result = await parseMultipart({
43
- body: Readable.toWeb(request),
44
- headers,
45
- method: request.method,
46
- url: url.toString()
47
- }, {
48
- ...multipartOptions,
49
- maxTotalSize: multipartOptions?.maxTotalSize ?? maxBodySize
50
- });
51
- body = result.fields;
52
- files = result.files;
53
- } else {
61
+ const materializeBody = createMemoizedAsyncValue(async () => {
62
+ if (isMultipart) {
63
+ const result = await parseMultipart({
64
+ body: Readable.toWeb(request),
65
+ headers,
66
+ method: request.method,
67
+ url: url.toString()
68
+ }, {
69
+ ...multipartOptions,
70
+ maxTotalSize: multipartOptions?.maxTotalSize ?? maxBodySize
71
+ });
72
+ frameworkRequest.body = result.fields;
73
+ frameworkRequest.files = result.files;
74
+ return;
75
+ }
76
+ if (!hasNodeRequestBody(request)) {
77
+ frameworkRequest.body = undefined;
78
+ return;
79
+ }
54
80
  const bodyResult = await readRequestBody(request, headers['content-type'], maxBodySize, preserveRawBody);
55
- body = bodyResult.body;
56
- rawBody = bodyResult.rawBody;
57
- }
81
+ frameworkRequest.body = bodyResult.body;
82
+ if (bodyResult.rawBody) {
83
+ frameworkRequest.rawBody = bodyResult.rawBody;
84
+ }
85
+ });
58
86
  const frameworkRequest = {
59
- body,
60
- cookies: parseCookieHeader(headers.cookie),
87
+ get cookies() {
88
+ return cookies();
89
+ },
61
90
  headers,
62
91
  method: request.method ?? 'GET',
63
92
  params: {},
64
93
  path: url.pathname,
65
- query: parseQueryParams(url.searchParams),
94
+ get query() {
95
+ return query();
96
+ },
66
97
  raw: request,
67
98
  signal,
68
- url: url.pathname + url.search
99
+ url: url.pathname + url.search,
100
+ materializeBody
69
101
  };
70
- if (files) {
71
- frameworkRequest.files = files;
102
+ return frameworkRequest;
103
+ }
104
+ function hasNodeRequestBody(request) {
105
+ const contentLength = request.headers['content-length'];
106
+ const transferEncoding = request.headers['transfer-encoding'];
107
+ const primaryContentLength = Array.isArray(contentLength) ? contentLength[0] : contentLength;
108
+ if (transferEncoding !== undefined) {
109
+ return true;
72
110
  }
73
- if (rawBody) {
74
- frameworkRequest.rawBody = rawBody;
111
+ if (primaryContentLength === undefined) {
112
+ return true;
75
113
  }
76
- return frameworkRequest;
114
+ const parsedContentLength = Number(primaryContentLength);
115
+ return !Number.isFinite(parsedContentLength) || parsedContentLength !== 0;
116
+ }
117
+
118
+ /**
119
+ * Materializes a deferred Node framework request body exactly once.
120
+ *
121
+ * @param request - Framework request returned by {@link createDeferredFrameworkRequest}.
122
+ * @returns A promise that settles after body, rawBody, and files fields are populated when applicable.
123
+ */
124
+ export async function materializeFrameworkRequestBody(request) {
125
+ await request.materializeBody?.();
126
+ delete request.materializeBody;
127
+ }
128
+ function createMemoizedValue(factory) {
129
+ let initialized = false;
130
+ let value;
131
+ return () => {
132
+ if (!initialized) {
133
+ value = factory();
134
+ initialized = true;
135
+ }
136
+ return value;
137
+ };
138
+ }
139
+ function createMemoizedAsyncValue(factory) {
140
+ let promise;
141
+ return () => {
142
+ promise ??= factory();
143
+ return promise;
144
+ };
77
145
  }
78
146
 
79
147
  /**
@@ -124,9 +192,12 @@ function parseQueryParams(searchParams) {
124
192
  return query;
125
193
  }
126
194
  function cloneRequestHeaders(headers) {
127
- const clonedEntries = Object.entries(headers).map(([name, value]) => [name, Array.isArray(value) ? [...value] : value]);
195
+ const clonedEntries = Object.entries(headers).map(([name, value]) => [name, cloneHeaderValue(value)]);
128
196
  return Object.fromEntries(clonedEntries);
129
197
  }
198
+ function cloneHeaderValue(value) {
199
+ return Array.isArray(value) ? [...value] : value;
200
+ }
130
201
  function readPrimaryHeaderValue(headerValue) {
131
202
  if (Array.isArray(headerValue)) {
132
203
  return headerValue[0];
@@ -1,8 +1,28 @@
1
1
  import type { ServerResponse } from 'node:http';
2
2
  import { type FrameworkResponseCompression, type FrameworkResponse } from '@fluojs/http';
3
+ /**
4
+ * Defines the mutable framework response type.
5
+ */
3
6
  export type MutableFrameworkResponse = FrameworkResponse & {
4
7
  statusSet?: boolean;
5
8
  };
6
- export declare function createFrameworkResponse(response: ServerResponse, compression?: FrameworkResponseCompression): MutableFrameworkResponse;
9
+ type FrameworkResponseCompressionFactory = () => FrameworkResponseCompression | undefined;
10
+ /**
11
+ * Create framework response.
12
+ *
13
+ * @param response The response.
14
+ * @param compression The compression.
15
+ * @returns The create framework response result.
16
+ */
17
+ export declare function createFrameworkResponse(response: ServerResponse, compression?: FrameworkResponseCompression | FrameworkResponseCompressionFactory): MutableFrameworkResponse;
18
+ /**
19
+ * Write node adapter error response.
20
+ *
21
+ * @param error The error.
22
+ * @param response The response.
23
+ * @param requestId The request id.
24
+ * @returns The write node adapter error response result.
25
+ */
7
26
  export declare function writeNodeAdapterErrorResponse(error: unknown, response: FrameworkResponse, requestId?: string): Promise<void>;
27
+ export {};
8
28
  //# sourceMappingURL=internal-node-response.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal-node-response.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAIL,KAAK,4BAA4B,EACjC,KAAK,iBAAiB,EAEvB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,wBAAwB,GAAG,iBAAiB,GAAG;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AA6CnF,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,cAAc,EACxB,WAAW,CAAC,EAAE,4BAA4B,GACzC,wBAAwB,CA+F1B;AAED,wBAAsB,6BAA6B,CACjD,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAIf"}
1
+ {"version":3,"file":"internal-node-response.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAIL,KAAK,4BAA4B,EACjC,KAAK,iBAAiB,EAEvB,MAAM,cAAc,CAAC;AAEtB;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,iBAAiB,GAAG;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEnF,KAAK,mCAAmC,GAAG,MAAM,4BAA4B,GAAG,SAAS,CAAC;AA6C1F;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,cAAc,EACxB,WAAW,CAAC,EAAE,4BAA4B,GAAG,mCAAmC,GAC/E,wBAAwB,CAqH1B;AAED;;;;;;;GAOG;AACH,wBAAsB,6BAA6B,CACjD,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAIf"}
@@ -1,4 +1,9 @@
1
1
  import { createErrorResponse, HttpException, InternalServerErrorException } from '@fluojs/http';
2
+
3
+ /**
4
+ * Defines the mutable framework response type.
5
+ */
6
+
2
7
  function createFrameworkResponseStream(response) {
3
8
  return {
4
9
  close() {
@@ -39,7 +44,28 @@ function createFrameworkResponseStream(response) {
39
44
  }
40
45
  };
41
46
  }
47
+
48
+ /**
49
+ * Create framework response.
50
+ *
51
+ * @param response The response.
52
+ * @param compression The compression.
53
+ * @returns The create framework response result.
54
+ */
42
55
  export function createFrameworkResponse(response, compression) {
56
+ let activeStream;
57
+ const resolveCompression = (() => {
58
+ const factory = typeof compression === 'function' ? compression : () => compression;
59
+ let resolved = false;
60
+ let value;
61
+ return () => {
62
+ if (!resolved) {
63
+ value = factory();
64
+ resolved = true;
65
+ }
66
+ return value;
67
+ };
68
+ })();
43
69
  const mergeSetCookieHeader = (current, incoming) => {
44
70
  const nextValues = Array.isArray(incoming) ? incoming : [incoming];
45
71
  if (current === undefined) {
@@ -56,7 +82,10 @@ export function createFrameworkResponse(response, compression) {
56
82
  committed: response.headersSent || response.writableEnded,
57
83
  headers: {},
58
84
  raw: response,
59
- stream: createFrameworkResponseStream(response),
85
+ get stream() {
86
+ activeStream ??= createFrameworkResponseStream(response);
87
+ return activeStream;
88
+ },
60
89
  redirect(status, location) {
61
90
  this.setStatus(status);
62
91
  this.setHeader('Location', location);
@@ -74,9 +103,10 @@ export function createFrameworkResponse(response, compression) {
74
103
  }
75
104
  const contentType = response.getHeader('Content-Type');
76
105
  const payload = typeof serialized.payload === 'string' ? Buffer.from(serialized.payload, 'utf8') : serialized.payload;
77
- if (compression) {
106
+ const activeCompression = resolveCompression();
107
+ if (activeCompression) {
78
108
  this.committed = true;
79
- return Promise.resolve(compression.write(payload, {
109
+ return Promise.resolve(activeCompression.write(payload, {
80
110
  contentType
81
111
  })).then(handled => {
82
112
  if (!handled && !response.writableEnded) {
@@ -113,6 +143,15 @@ export function createFrameworkResponse(response, compression) {
113
143
  };
114
144
  return frameworkResponse;
115
145
  }
146
+
147
+ /**
148
+ * Write node adapter error response.
149
+ *
150
+ * @param error The error.
151
+ * @param response The response.
152
+ * @param requestId The request id.
153
+ * @returns The write node adapter error response result.
154
+ */
116
155
  export async function writeNodeAdapterErrorResponse(error, response, requestId) {
117
156
  const httpError = toHttpException(error);
118
157
  response.setStatus(httpError.status);
@@ -11,6 +11,9 @@ declare module '@fluojs/http' {
11
11
  rawBody?: Uint8Array;
12
12
  }
13
13
  }
14
+ /**
15
+ * Describes the node http adapter options contract.
16
+ */
14
17
  export interface NodeHttpAdapterOptions {
15
18
  host?: string;
16
19
  https?: HttpsServerOptions;
@@ -21,8 +24,17 @@ export interface NodeHttpAdapterOptions {
21
24
  retryLimit?: number;
22
25
  shutdownTimeoutMs?: number;
23
26
  }
27
+ /**
28
+ * Defines the node application signal type.
29
+ */
24
30
  export type NodeApplicationSignal = 'SIGINT' | 'SIGTERM';
31
+ /**
32
+ * Defines the cors input type.
33
+ */
25
34
  export type CorsInput = false | string | string[] | CorsOptions;
35
+ /**
36
+ * Describes the bootstrap node application options contract.
37
+ */
26
38
  export interface BootstrapNodeApplicationOptions extends Omit<CreateApplicationOptions, 'adapter' | 'logger' | 'middleware'> {
27
39
  compression?: boolean;
28
40
  cors?: CorsInput;
@@ -41,6 +53,9 @@ export interface BootstrapNodeApplicationOptions extends Omit<CreateApplicationO
41
53
  securityHeaders?: false | SecurityHeadersOptions;
42
54
  shutdownTimeoutMs?: number;
43
55
  }
56
+ /**
57
+ * Describes the run node application options contract.
58
+ */
44
59
  export interface RunNodeApplicationOptions extends BootstrapNodeApplicationOptions {
45
60
  forceExitTimeoutMs?: number;
46
61
  shutdownSignals?: false | readonly NodeApplicationSignal[];
@@ -50,6 +65,9 @@ interface NodeListenTarget {
50
65
  url: string;
51
66
  }
52
67
  type NodeServer = ReturnType<typeof createHttpServer> | ReturnType<typeof createHttpsServer>;
68
+ /**
69
+ * Represents the node http application adapter.
70
+ */
53
71
  export declare class NodeHttpApplicationAdapter implements HttpApplicationAdapter {
54
72
  private readonly port;
55
73
  private readonly host;
@@ -73,8 +91,30 @@ export declare class NodeHttpApplicationAdapter implements HttpApplicationAdapte
73
91
  close(): Promise<void>;
74
92
  private handleRequest;
75
93
  }
94
+ /**
95
+ * Create node http adapter.
96
+ *
97
+ * @param options The options.
98
+ * @param compression The compression.
99
+ * @param multipartOptions The multipart options.
100
+ * @returns The create node http adapter result.
101
+ */
76
102
  export declare function createNodeHttpAdapter(options?: NodeHttpAdapterOptions, compression?: boolean, multipartOptions?: MultipartOptions): HttpApplicationAdapter;
103
+ /**
104
+ * Bootstrap node application.
105
+ *
106
+ * @param rootModule The root module.
107
+ * @param options The options.
108
+ * @returns The bootstrap node application result.
109
+ */
77
110
  export declare function bootstrapNodeApplication(rootModule: ModuleType, options: BootstrapNodeApplicationOptions): Promise<Application>;
111
+ /**
112
+ * Run node application.
113
+ *
114
+ * @param rootModule The root module.
115
+ * @param options The options.
116
+ * @returns The run node application result.
117
+ */
78
118
  export declare function runNodeApplication(rootModule: ModuleType, options: RunNodeApplicationOptions): Promise<Application>;
79
119
  export { compressNodeResponse, createNodeResponseCompression, createNodeShutdownSignalRegistration, defaultNodeShutdownSignals, registerShutdownSignals, };
80
120
  //# sourceMappingURL=internal-node.d.ts.map