@fluojs/runtime 1.0.0-beta.7 → 1.0.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ko.md CHANGED
@@ -130,6 +130,8 @@ class UsersModule {}
130
130
  - `multi: true` provider token은 context cache에 memoize되지 않습니다. 각 `get()` 호출은 DI로 위임되어 컨테이너가 새로운 contribution 배열을 조립하며, 각 contribution 자체는 해당 provider scope에 따라 재사용됩니다.
131
131
  - `duplicateProviderPolicy`가 `warn` 또는 `ignore`일 때 context cache 적격성과 lifecycle hook 실행은 bootstrap이 선택한 effective winning provider를 기준으로 결정됩니다. stale losing provider는 cache entry나 lifecycle hook을 만들지 않습니다.
132
132
  - 애플리케이션 또는 컨텍스트 bootstrap이 런타임 리소스나 lifecycle instance 생성 이후 실패하면 fluo는 readiness를 초기화하고, 등록된 runtime cleanup callback을 실행하며, 그 시점까지 해석된 instance의 shutdown hook을 `bootstrap-failed`로 호출하고, 컨테이너를 dispose하고, cleanup 실패를 로그로 남긴 뒤 원래 bootstrap error를 다시 던집니다.
133
+ - 연결된 microservice는 부모 `Application`이 소유하는 child입니다. `startAllMicroservices()`는 순차적으로 시작하며 이후 child 시작이 실패하면 이미 시작된 child를 `bootstrap-failed`로 rollback하고, `Application.close(signal)`은 부모 lifecycle hook, adapter 종료, container dispose보다 먼저 연결된 child를 닫습니다.
134
+ - `FluoFactory.createMicroservice()`는 cleanup이 실패해도 원래 bootstrap/runtime 해석 오류를 보존하고 cleanup 실패는 별도로 로그로 남깁니다.
133
135
  - Bootstrap은 독립적인 singleton lifecycle provider를 병렬로 해석한 뒤 lifecycle hook은 결정적인 provider 순서대로 실행합니다.
134
136
  - 멀티파트 파싱은 누적 바디 크기가 설정된 `multipart.maxTotalSize`를 넘으면 즉시 거부되며, 런타임 어댑터는 별도 재정의가 없으면 이 한도를 `maxBodySize`와 동일하게 맞춥니다.
135
137
  - `createNodeHttpAdapter(...)`, `bootstrapNodeApplication(...)`, `runNodeApplication(...)`는 `maxBodySize`를 0 이상의 정수 바이트 수로만 받으며, 값이 잘못되면 어댑터 생성/부트스트랩 단계에서 즉시 실패합니다.
@@ -137,6 +139,7 @@ class UsersModule {}
137
139
  - 런타임 health 모듈은 bootstrap이 ready로 표시하기 전까지 `/ready`를 HTTP 503과 `starting`으로 보고하며, 애플리케이션/컨텍스트 종료가 시작되는 즉시, 종료 시도가 실패하더라도 다시 `starting`으로 내려갑니다.
138
140
  - 시그널 기반 종료 헬퍼는 bounded drain semantics를 유지하면서 timeout/실패 상황을 로그와 `process.exitCode`로 보고하지만, 최종 프로세스 종료 소유권은 주변 호스트 런타임에 남겨 둡니다.
139
141
  - 플랫폼 snapshot 생산은 런타임에 남아 있고, 그래프 보기와 Mermaid 렌더링은 CLI 및 자동화 호출자가 소비하는 Studio 소유 계약입니다.
142
+ - 모듈 그래프 컴파일 결과 캐시는 `moduleGraphCache: true`를 통한 opt-in입니다. 캐시 항목은 root module identity, runtime provider, validation token, core metadata version, compile algorithm version으로 식별되며, 성공한 컴파일만 저장하고 호출자 mutation이 이후 bootstrap을 오염시키지 않도록 격리된 그래프 복사본을 반환합니다.
140
143
 
141
144
  ## 공개 API 개요
142
145
 
package/README.md CHANGED
@@ -130,6 +130,8 @@ class UsersModule {}
130
130
  - `multi: true` provider tokens are not context-cache memoized: each `get()` call delegates to DI so the container can assemble a fresh contribution array while still reusing each contribution according to its own provider scope.
131
131
  - When `duplicateProviderPolicy` is `warn` or `ignore`, context-cache eligibility and lifecycle hook execution are based on the effective winning provider selected by bootstrap; stale losing providers do not seed cache entries or lifecycle hooks.
132
132
  - If application or context bootstrap fails after runtime resources or lifecycle instances have been created, fluo resets readiness, runs registered runtime cleanup callbacks, invokes shutdown hooks for instances resolved so far with `bootstrap-failed`, disposes the container, logs cleanup failures, and rethrows the original bootstrap error.
133
+ - Connected microservices are owned children of their parent `Application`: `startAllMicroservices()` starts them sequentially and rolls back already-started children with `bootstrap-failed` if a later child fails, while `Application.close(signal)` closes connected children before parent lifecycle hooks, adapter shutdown, and container disposal.
134
+ - `FluoFactory.createMicroservice()` preserves the original bootstrap/runtime-resolution error when cleanup fails and logs cleanup failures separately.
133
135
  - Bootstrap resolves independent singleton lifecycle providers concurrently, then runs lifecycle hooks in deterministic provider order.
134
136
  - Multipart parsing rejects payloads when the cumulative body size exceeds the configured `multipart.maxTotalSize`; runtime adapters default that limit to `maxBodySize` unless you override it.
135
137
  - `createNodeHttpAdapter(...)`, `bootstrapNodeApplication(...)`, and `runNodeApplication(...)` accept `maxBodySize` only as a non-negative integer byte count and fail fast during adapter creation/bootstrap when the value is invalid.
@@ -137,6 +139,7 @@ class UsersModule {}
137
139
  - Runtime health modules report `/ready` as `starting` with HTTP 503 until bootstrap marks them ready, and they return to `starting` as soon as application/context shutdown begins, including failed shutdown attempts.
138
140
  - Signal-driven shutdown helpers preserve bounded drain semantics, log timeout/failure conditions, and set `process.exitCode` when shutdown does not finish cleanly, but they leave final process termination ownership to the surrounding host runtime.
139
141
  - Platform snapshot production stays in runtime; graph viewing and Mermaid rendering are Studio-owned contracts consumed by CLI and automation callers.
142
+ - Module graph compile-result caching is opt-in through `moduleGraphCache: true`; it keys entries by root module identity, runtime providers, validation tokens, core metadata versions, and the compile algorithm version, caches only successful compilations, and returns isolated graph copies so caller mutations cannot poison later bootstraps.
140
143
 
141
144
  ## Public API Overview
142
145
 
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EAEX,uBAAuB,EAGvB,2BAA2B,EAC3B,sBAAsB,EACtB,eAAe,EAGf,wBAAwB,EACxB,+BAA+B,EAC/B,yBAAyB,EAEzB,gBAAgB,EAChB,UAAU,EAKX,MAAM,YAAY,CAAC;AA2apB;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,UAAU,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,gBAAgB,GAAG,CAAC,CAIjG;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,sBAA2B,GAAG,eAAe,CA+B7G;AAmqBD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,WAAW,CAAC,CAqHrG;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB;;;;;;;OAOG;WACU,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,WAAW,CAAC;IAOzG;;;;;;;OAOG;WACU,wBAAwB,CACnC,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,kBAAkB,CAAC;IAoG9B;;;;;;;;OAQG;WACU,kBAAkB,CAC7B,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,uBAAuB,CAAC;CAuBpC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,oBAAc,CAAC"}
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EAEX,uBAAuB,EAGvB,2BAA2B,EAC3B,sBAAsB,EACtB,eAAe,EAGf,wBAAwB,EACxB,+BAA+B,EAC/B,yBAAyB,EAEzB,gBAAgB,EAChB,UAAU,EAKX,MAAM,YAAY,CAAC;AAubpB;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,UAAU,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,gBAAgB,GAAG,CAAC,CAIjG;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,sBAA2B,GAAG,eAAe,CA+B7G;AAkvBD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,WAAW,CAAC,CAsHrG;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB;;;;;;;OAOG;WACU,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,WAAW,CAAC;IAOzG;;;;;;;OAOG;WACU,wBAAwB,CACnC,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,kBAAkB,CAAC;IAqG9B;;;;;;;;OAQG;WACU,kBAAkB,CAC7B,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,uBAAuB,CAAC;CA+BpC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,oBAAc,CAAC"}
package/dist/bootstrap.js CHANGED
@@ -140,6 +140,15 @@ function hasReadinessStateMethods(value) {
140
140
  function isMicroserviceRuntime(value) {
141
141
  return hasMethod(value, 'listen');
142
142
  }
143
+ function hasMicroserviceRuntimeClose(value) {
144
+ return typeof value.close === 'function';
145
+ }
146
+ async function closeMicroserviceRuntime(runtime, signal) {
147
+ if (!hasMicroserviceRuntimeClose(runtime)) {
148
+ return;
149
+ }
150
+ await runtime.close(signal);
151
+ }
143
152
  function resetReadinessState(modules) {
144
153
  for (const compiledModule of modules) {
145
154
  if (hasReadinessStateMethods(compiledModule.type)) {
@@ -391,12 +400,43 @@ class FluoApplication {
391
400
  if (!isMicroserviceRuntime(runtime)) {
392
401
  throw new InvariantError('Resolved microservice token does not implement listen().');
393
402
  }
394
- const microservice = new FluoMicroserviceApplication(this, this.logger, runtime);
403
+ const microservice = new FluoMicroserviceApplication(this, this.logger, runtime, false);
395
404
  this.connectedMicroservices.push(microservice);
396
405
  return microservice;
397
406
  }
398
407
  async startAllMicroservices() {
399
- await Promise.all(this.connectedMicroservices.map(async microservice => microservice.listen()));
408
+ const startedMicroservices = [];
409
+ for (const microservice of this.connectedMicroservices) {
410
+ try {
411
+ await microservice.listen();
412
+ startedMicroservices.push(microservice);
413
+ } catch (error) {
414
+ await this.rollbackStartedMicroservices(startedMicroservices);
415
+ throw error;
416
+ }
417
+ }
418
+ }
419
+ async rollbackStartedMicroservices(startedMicroservices) {
420
+ for (const microservice of [...startedMicroservices].reverse()) {
421
+ try {
422
+ await microservice.close('bootstrap-failed');
423
+ } catch (rollbackError) {
424
+ this.logger.error('Failed to roll back a started microservice after startup failure.', rollbackError, 'FluoApplication');
425
+ }
426
+ }
427
+ }
428
+ async closeConnectedMicroservices(signal) {
429
+ const errors = [];
430
+ for (const microservice of [...this.connectedMicroservices].reverse()) {
431
+ try {
432
+ await microservice.close(signal);
433
+ } catch (error) {
434
+ errors.push(error);
435
+ }
436
+ }
437
+ if (errors.length > 0) {
438
+ throw createLifecycleCloseError(errors);
439
+ }
400
440
  }
401
441
 
402
442
  /**
@@ -438,14 +478,27 @@ class FluoApplication {
438
478
  return;
439
479
  }
440
480
  this.closingPromise = (async () => {
441
- await closeRuntimeResources({
442
- adapter: this.adapter,
443
- container: this.container,
444
- lifecycleInstances: this.lifecycleInstances,
445
- modules: this.modules,
446
- runtimeCleanup: this.runtimeCleanup,
447
- signal
448
- });
481
+ const errors = [];
482
+ try {
483
+ await this.closeConnectedMicroservices(signal);
484
+ } catch (error) {
485
+ errors.push(error);
486
+ }
487
+ try {
488
+ await closeRuntimeResources({
489
+ adapter: this.adapter,
490
+ container: this.container,
491
+ lifecycleInstances: this.lifecycleInstances,
492
+ modules: this.modules,
493
+ runtimeCleanup: this.runtimeCleanup,
494
+ signal
495
+ });
496
+ } catch (error) {
497
+ errors.push(error);
498
+ }
499
+ if (errors.length > 0) {
500
+ throw createLifecycleCloseError(errors);
501
+ }
449
502
  this.closed = true;
450
503
  this.applicationState = 'closed';
451
504
  })();
@@ -507,10 +560,11 @@ class FluoMicroserviceApplication {
507
560
  closed = false;
508
561
  closingPromise;
509
562
  microserviceState = 'bootstrapped';
510
- constructor(context, logger, runtime) {
563
+ constructor(context, logger, runtime, closeContextOnClose) {
511
564
  this.context = context;
512
565
  this.logger = logger;
513
566
  this.runtime = runtime;
567
+ this.closeContextOnClose = closeContextOnClose;
514
568
  }
515
569
  get container() {
516
570
  return this.context.container;
@@ -559,7 +613,24 @@ class FluoMicroserviceApplication {
559
613
  return;
560
614
  }
561
615
  this.closingPromise = (async () => {
562
- await this.context.close(signal);
616
+ if (this.closeContextOnClose) {
617
+ const errors = [];
618
+ try {
619
+ await closeMicroserviceRuntime(this.runtime, signal);
620
+ } catch (error) {
621
+ errors.push(error);
622
+ }
623
+ try {
624
+ await this.context.close(signal);
625
+ } catch (error) {
626
+ errors.push(error);
627
+ }
628
+ if (errors.length > 0) {
629
+ throw createLifecycleCloseError(errors);
630
+ }
631
+ } else {
632
+ await closeMicroserviceRuntime(this.runtime, signal);
633
+ }
563
634
  this.closed = true;
564
635
  this.microserviceState = 'closed';
565
636
  })();
@@ -856,6 +927,7 @@ export async function bootstrapApplication(options) {
856
927
  const bootstrapped = bootstrapModule(options.rootModule, {
857
928
  duplicateProviderPolicy: options.duplicateProviderPolicy,
858
929
  logger,
930
+ moduleGraphCache: options.moduleGraphCache,
859
931
  providers: runtimeProviders,
860
932
  validationTokens: [RUNTIME_CONTAINER, COMPILED_MODULES, HTTP_APPLICATION_ADAPTER]
861
933
  });
@@ -964,6 +1036,7 @@ export class FluoFactory {
964
1036
  const bootstrapped = bootstrapModule(rootModule, {
965
1037
  duplicateProviderPolicy: options.duplicateProviderPolicy,
966
1038
  logger,
1039
+ moduleGraphCache: options.moduleGraphCache,
967
1040
  providers: runtimeProviders,
968
1041
  validationTokens: [RUNTIME_CONTAINER, COMPILED_MODULES]
969
1042
  });
@@ -1038,9 +1111,13 @@ export class FluoFactory {
1038
1111
  if (!isMicroserviceRuntime(runtime)) {
1039
1112
  throw new InvariantError('Resolved microservice token does not implement listen().');
1040
1113
  }
1041
- return new FluoMicroserviceApplication(context, logger, runtime);
1114
+ return new FluoMicroserviceApplication(context, logger, runtime, true);
1042
1115
  } catch (error) {
1043
- await context.close('bootstrap-failed');
1116
+ try {
1117
+ await context.close('bootstrap-failed');
1118
+ } catch (cleanupError) {
1119
+ logger.error('Failed to clean up after microservice bootstrap failure.', cleanupError, 'FluoFactory');
1120
+ }
1044
1121
  logger.error('Failed to bootstrap microservice context. Check the error below for what failed and how to fix it.', error, 'FluoFactory');
1045
1122
  throw error;
1046
1123
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './abort.js';
2
2
  export * from './bootstrap.js';
3
- export * from './health/diagnostics.js';
3
+ export { createBootstrapTimingDiagnostics, createRuntimeDiagnosticsGraph, } from './health/diagnostics.js';
4
+ export type { BootstrapTimingDiagnostics, BootstrapTimingPhase, RuntimeDiagnosticsGraph, RuntimeDiagnosticsModule, RuntimeDiagnosticsProvider, RuntimeDiagnosticsRelationships, } from './health/diagnostics.js';
4
5
  export * from './errors.js';
5
6
  export * from './health/health.js';
6
7
  export type { MultipartOptions, MultipartRequestLike, MultipartResult, UploadedFile, } from './multipart.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,iCAAiC,EACjC,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,6BAA6B,EAC7B,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACjE,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EACL,gCAAgC,EAChC,6BAA6B,GAC9B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,0BAA0B,EAC1B,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,0BAA0B,EAC1B,+BAA+B,GAChC,MAAM,yBAAyB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,iCAAiC,EACjC,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,6BAA6B,EAC7B,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACjE,cAAc,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from './abort.js';
2
2
  export * from './bootstrap.js';
3
- export * from './health/diagnostics.js';
3
+ export { createBootstrapTimingDiagnostics, createRuntimeDiagnosticsGraph } from './health/diagnostics.js';
4
4
  export * from './errors.js';
5
5
  export * from './health/health.js';
6
6
  export * from './request-transaction.js';
@@ -8,6 +8,22 @@ import type { BootstrapModuleOptions, CompiledModule, ModuleType } from './types
8
8
  * @returns The token that should be registered and resolved for the provider.
9
9
  */
10
10
  export declare function providerToken(provider: Provider): Token;
11
+ /** Clears the process-local module graph compile cache for isolated regression tests. */
12
+ export declare function clearModuleGraphCompileCacheForTesting(): void;
13
+ /**
14
+ * Reads the current process-local module graph compile cache size for tests.
15
+ *
16
+ * @returns Number of successful compile snapshots currently retained in the cache.
17
+ */
18
+ export declare function getModuleGraphCompileCacheSizeForTesting(): number;
19
+ /**
20
+ * Builds the key used for opt-in module graph compile caching.
21
+ *
22
+ * @param rootModule Root module that would be compiled.
23
+ * @param options Bootstrap options that influence graph validation.
24
+ * @returns Process-local key that changes when metadata or runtime validation inputs change.
25
+ */
26
+ export declare function createModuleGraphCacheKey(rootModule: ModuleType, options?: BootstrapModuleOptions): string;
11
27
  /**
12
28
  * Collects runtime provider tokens into a set for visibility and validation checks.
13
29
  *
@@ -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;AA8UD;;;;;;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;AAW1C,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AAEvG;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAMvD;AAiBD,yFAAyF;AACzF,wBAAgB,sCAAsC,IAAI,IAAI,CAE7D;AAED;;;;GAIG;AACH,wBAAgB,wCAAwC,IAAI,MAAM,CAEjE;AAgFD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,sBAA2B,GAAG,MAAM,CAY9G;AAiRD;;;;;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,CA4BjH"}
@@ -1,4 +1,4 @@
1
- import { getClassDiMetadata, getModuleMetadata, getOwnClassDiMetadata } from '@fluojs/core/internal';
1
+ import { getClassDiMetadata, getClassDiMetadataVersion, getModuleMetadata, getModuleMetadataVersion, getOwnClassDiMetadata } from '@fluojs/core/internal';
2
2
  import { ModuleGraphError, ModuleInjectionMetadataError, ModuleVisibilityError } from './errors.js';
3
3
  /**
4
4
  * Returns the public token represented by a provider declaration.
@@ -12,6 +12,280 @@ export function providerToken(provider) {
12
12
  }
13
13
  return provider.provide;
14
14
  }
15
+ const objectTokenIds = new WeakMap();
16
+ const symbolTokenIds = new Map();
17
+ let nextTokenId = 0;
18
+ const MODULE_GRAPH_COMPILE_ALGORITHM_VERSION = 1;
19
+ const moduleGraphCompileCache = new Map();
20
+
21
+ /** Clears the process-local module graph compile cache for isolated regression tests. */
22
+ export function clearModuleGraphCompileCacheForTesting() {
23
+ moduleGraphCompileCache.clear();
24
+ }
25
+
26
+ /**
27
+ * Reads the current process-local module graph compile cache size for tests.
28
+ *
29
+ * @returns Number of successful compile snapshots currently retained in the cache.
30
+ */
31
+ export function getModuleGraphCompileCacheSizeForTesting() {
32
+ return moduleGraphCompileCache.size;
33
+ }
34
+ function getFunctionTokenId(token) {
35
+ const existing = objectTokenIds.get(token);
36
+ if (existing !== undefined) {
37
+ return existing;
38
+ }
39
+ nextTokenId += 1;
40
+ objectTokenIds.set(token, nextTokenId);
41
+ return nextTokenId;
42
+ }
43
+ function getSymbolTokenId(token) {
44
+ const existing = symbolTokenIds.get(token);
45
+ if (existing !== undefined) {
46
+ return existing;
47
+ }
48
+ nextTokenId += 1;
49
+ symbolTokenIds.set(token, nextTokenId);
50
+ return nextTokenId;
51
+ }
52
+ function describeTokenForCacheKey(token) {
53
+ if (typeof token === 'function') {
54
+ return `fn:${getFunctionTokenId(token)}`;
55
+ }
56
+ if (typeof token === 'symbol') {
57
+ return `sym:${getSymbolTokenId(token)}`;
58
+ }
59
+ return `str:${JSON.stringify(token)}`;
60
+ }
61
+ function describeInjectionTokenForCacheKey(token) {
62
+ if (isForwardRef(token)) {
63
+ return `forward:${describeTokenForCacheKey(token.forwardRef())}`;
64
+ }
65
+ if (isOptionalToken(token)) {
66
+ return `optional:${describeTokenForCacheKey(token.token)}`;
67
+ }
68
+ return describeTokenForCacheKey(token);
69
+ }
70
+ function describeProviderForCacheKey(provider) {
71
+ if (typeof provider === 'function') {
72
+ return `class:${describeTokenForCacheKey(provider)}`;
73
+ }
74
+ const provide = describeTokenForCacheKey(provider.provide);
75
+ const multi = 'multi' in provider && provider.multi === true ? 'multi' : 'single';
76
+ if ('useClass' in provider) {
77
+ const inject = provider.inject?.map(describeInjectionTokenForCacheKey).join(',') ?? '';
78
+ return `useClass:${provide}:${describeTokenForCacheKey(provider.useClass)}:${provider.scope ?? ''}:${multi}:${inject}`;
79
+ }
80
+ if ('useFactory' in provider) {
81
+ const inject = provider.inject?.map(describeInjectionTokenForCacheKey).join(',') ?? '';
82
+ const resolverClass = provider.resolverClass ? describeTokenForCacheKey(provider.resolverClass) : '';
83
+ return `useFactory:${provide}:${getFunctionTokenId(provider.useFactory)}:${resolverClass}:${provider.scope ?? ''}:${multi}:${inject}`;
84
+ }
85
+ if ('useExisting' in provider) {
86
+ return `useExisting:${provide}:${describeTokenForCacheKey(provider.useExisting)}`;
87
+ }
88
+ return `useValue:${provide}:${multi}`;
89
+ }
90
+
91
+ /**
92
+ * Builds the key used for opt-in module graph compile caching.
93
+ *
94
+ * @param rootModule Root module that would be compiled.
95
+ * @param options Bootstrap options that influence graph validation.
96
+ * @returns Process-local key that changes when metadata or runtime validation inputs change.
97
+ */
98
+ export function createModuleGraphCacheKey(rootModule, options = {}) {
99
+ const runtimeProviders = (options.providers ?? []).map(describeProviderForCacheKey).join('|');
100
+ const validationTokens = (options.validationTokens ?? []).map(describeTokenForCacheKey).join('|');
101
+ return [`root:${describeTokenForCacheKey(rootModule)}`, `module:${getModuleMetadataVersion()}`, `class-di:${getClassDiMetadataVersion()}`, `algorithm:${MODULE_GRAPH_COMPILE_ALGORITHM_VERSION}`, `runtime:${runtimeProviders}`, `validation:${validationTokens}`].join(';');
102
+ }
103
+ function cloneModuleDefinition(definition) {
104
+ return {
105
+ global: definition.global,
106
+ imports: definition.imports ? [...definition.imports] : undefined,
107
+ providers: definition.providers ? definition.providers.map(provider => cloneProvider(provider)) : undefined,
108
+ controllers: definition.controllers ? [...definition.controllers] : undefined,
109
+ exports: definition.exports ? [...definition.exports] : undefined,
110
+ middleware: definition.middleware ? definition.middleware.map(middleware => cloneMutableValue(middleware)) : undefined
111
+ };
112
+ }
113
+ function cloneMutableValue(value, clones = new WeakMap()) {
114
+ if (typeof value !== 'object' || value === null) {
115
+ return value;
116
+ }
117
+ const existing = clones.get(value);
118
+ if (existing !== undefined) {
119
+ return existing;
120
+ }
121
+ if (value instanceof Date) {
122
+ return new Date(value);
123
+ }
124
+ if (value instanceof RegExp) {
125
+ return new RegExp(value);
126
+ }
127
+ if (Array.isArray(value)) {
128
+ const clonedArray = [];
129
+ clones.set(value, clonedArray);
130
+ for (const item of value) {
131
+ clonedArray.push(cloneMutableValue(item, clones));
132
+ }
133
+ return clonedArray;
134
+ }
135
+ if (value instanceof Map) {
136
+ const clonedMap = new Map();
137
+ clones.set(value, clonedMap);
138
+ for (const [key, entryValue] of value) {
139
+ clonedMap.set(cloneMutableValue(key, clones), cloneMutableValue(entryValue, clones));
140
+ }
141
+ return clonedMap;
142
+ }
143
+ if (value instanceof Set) {
144
+ const clonedSet = new Set();
145
+ clones.set(value, clonedSet);
146
+ for (const entryValue of value) {
147
+ clonedSet.add(cloneMutableValue(entryValue, clones));
148
+ }
149
+ return clonedSet;
150
+ }
151
+ const source = value;
152
+ const prototype = Object.getPrototypeOf(value);
153
+ const clonedObject = Object.create(prototype);
154
+ clones.set(value, clonedObject);
155
+ for (const key of Reflect.ownKeys(source)) {
156
+ clonedObject[key] = cloneMutableValue(source[key], clones);
157
+ }
158
+ return clonedObject;
159
+ }
160
+ function cloneInjectionToken(token) {
161
+ if (isForwardRef(token)) {
162
+ return {
163
+ __forwardRef__: true,
164
+ forwardRef: token.forwardRef
165
+ };
166
+ }
167
+ if (isOptionalToken(token)) {
168
+ return {
169
+ __optional__: true,
170
+ token: token.token
171
+ };
172
+ }
173
+ return token;
174
+ }
175
+ function cloneProvider(provider) {
176
+ if (typeof provider === 'function') {
177
+ return provider;
178
+ }
179
+ if ('useClass' in provider) {
180
+ return {
181
+ ...provider,
182
+ ...(provider.inject ? {
183
+ inject: provider.inject.map(token => cloneInjectionToken(token))
184
+ } : {})
185
+ };
186
+ }
187
+ if ('useFactory' in provider) {
188
+ return {
189
+ ...provider,
190
+ ...(provider.inject ? {
191
+ inject: provider.inject.map(token => cloneInjectionToken(token))
192
+ } : {})
193
+ };
194
+ }
195
+ if ('useValue' in provider) {
196
+ return {
197
+ ...provider,
198
+ useValue: cloneMutableValue(provider.useValue)
199
+ };
200
+ }
201
+ return {
202
+ ...provider
203
+ };
204
+ }
205
+ function cloneCompiledModule(compiledModule) {
206
+ return {
207
+ type: compiledModule.type,
208
+ definition: cloneModuleDefinition(compiledModule.definition),
209
+ accessibleTokens: new Set(compiledModule.accessibleTokens),
210
+ exportedTokens: new Set(compiledModule.exportedTokens),
211
+ importedExportedTokens: new Set(compiledModule.importedExportedTokens),
212
+ providerTokens: new Set(compiledModule.providerTokens)
213
+ };
214
+ }
215
+ function cloneCompiledModules(modules) {
216
+ return modules.map(compiledModule => cloneCompiledModule(compiledModule));
217
+ }
218
+ function freezeInjectionToken(token) {
219
+ if (isForwardRef(token) || isOptionalToken(token)) {
220
+ Object.freeze(token);
221
+ }
222
+ return token;
223
+ }
224
+ function freezeMutableValue(value, frozen = new WeakSet()) {
225
+ if (typeof value !== 'object' || value === null || frozen.has(value)) {
226
+ return value;
227
+ }
228
+ frozen.add(value);
229
+ if (value instanceof Map) {
230
+ for (const [key, entryValue] of value) {
231
+ freezeMutableValue(key, frozen);
232
+ freezeMutableValue(entryValue, frozen);
233
+ }
234
+ } else if (value instanceof Set) {
235
+ for (const entryValue of value) {
236
+ freezeMutableValue(entryValue, frozen);
237
+ }
238
+ } else {
239
+ const source = value;
240
+ for (const key of Reflect.ownKeys(source)) {
241
+ freezeMutableValue(source[key], frozen);
242
+ }
243
+ }
244
+ Object.freeze(value);
245
+ return value;
246
+ }
247
+ function freezeProvider(provider) {
248
+ if (typeof provider === 'function') {
249
+ return provider;
250
+ }
251
+ if ('inject' in provider && provider.inject) {
252
+ for (const token of provider.inject) {
253
+ freezeInjectionToken(token);
254
+ }
255
+ Object.freeze(provider.inject);
256
+ }
257
+ if ('useValue' in provider) {
258
+ freezeMutableValue(provider.useValue);
259
+ }
260
+ return Object.freeze(provider);
261
+ }
262
+ function freezeMiddleware(middleware) {
263
+ if (typeof middleware === 'object' && middleware !== null) {
264
+ freezeMutableValue(middleware);
265
+ }
266
+ return middleware;
267
+ }
268
+ function freezeModuleDefinition(definition) {
269
+ definition.imports && Object.freeze(definition.imports);
270
+ for (const provider of definition.providers ?? []) {
271
+ freezeProvider(provider);
272
+ }
273
+ definition.providers && Object.freeze(definition.providers);
274
+ definition.controllers && Object.freeze(definition.controllers);
275
+ definition.exports && Object.freeze(definition.exports);
276
+ for (const middleware of definition.middleware ?? []) {
277
+ freezeMiddleware(middleware);
278
+ }
279
+ definition.middleware && Object.freeze(definition.middleware);
280
+ return Object.freeze(definition);
281
+ }
282
+ function freezeCompiledModule(compiledModule) {
283
+ freezeModuleDefinition(compiledModule.definition);
284
+ return Object.freeze(compiledModule);
285
+ }
286
+ function createModuleGraphCacheSnapshot(modules) {
287
+ return Object.freeze(cloneCompiledModules(modules).map(compiledModule => freezeCompiledModule(compiledModule)));
288
+ }
15
289
  function getEffectiveClassDiMetadata(target) {
16
290
  const metadata = getClassDiMetadata(target);
17
291
  if (!metadata) {
@@ -249,10 +523,22 @@ function validateCompiledModules(modules, runtimeProviders, runtimeProviderToken
249
523
  * @returns Compiled modules in dependency order after visibility and injection validation succeed.
250
524
  */
251
525
  export function compileModuleGraph(rootModule, options = {}) {
526
+ const cacheKey = options.moduleGraphCache === true ? createModuleGraphCacheKey(rootModule, options) : undefined;
527
+ if (cacheKey !== undefined) {
528
+ const cachedModules = moduleGraphCompileCache.get(cacheKey);
529
+ if (cachedModules !== undefined) {
530
+ return cloneCompiledModules(cachedModules);
531
+ }
532
+ }
252
533
  const ordered = [];
253
534
  const runtimeProviders = options.providers ?? [];
254
535
  const runtimeProviderTokens = mergeRuntimeTokenSets(runtimeProviders, options.validationTokens ?? []);
255
536
  compileModule(rootModule, runtimeProviderTokens, new Map(), new Set(), ordered);
256
537
  validateCompiledModules(ordered, runtimeProviders, runtimeProviderTokens);
538
+ if (cacheKey !== undefined) {
539
+ const cacheSnapshot = createModuleGraphCacheSnapshot(ordered);
540
+ moduleGraphCompileCache.set(cacheKey, cacheSnapshot);
541
+ return cloneCompiledModules(cacheSnapshot);
542
+ }
257
543
  return ordered;
258
544
  }
@@ -16,7 +16,8 @@ export interface DeferredFrameworkRequestShellOptions<RawRequest> {
16
16
  query?: QueryRecord;
17
17
  queryFactory?: () => QueryRecord;
18
18
  raw: RawRequest;
19
- signal: AbortSignal;
19
+ requestId?: string;
20
+ signal: AbortSignal | (() => AbortSignal);
20
21
  url: string;
21
22
  }
22
23
  /**
@@ -55,7 +56,7 @@ export declare function createDeferredFrameworkRequest(request: IncomingMessage,
55
56
  * @param options - Raw request, metadata factories, and deferred body materialization hooks.
56
57
  * @returns A framework request with lazy headers, cookies, query values, and optional body materialization.
57
58
  */
58
- export declare function createDeferredFrameworkRequestShell<RawRequest>({ cookieHeader, headers, headersFactory, materializeBody, method, path, query, queryFactory, raw, signal, url, }: DeferredFrameworkRequestShellOptions<RawRequest>): FrameworkRequest;
59
+ export declare function createDeferredFrameworkRequestShell<RawRequest>({ cookieHeader, headers, headersFactory, materializeBody, method, path, query, queryFactory, raw, requestId, signal, url, }: DeferredFrameworkRequestShellOptions<RawRequest>): FrameworkRequest;
59
60
  /**
60
61
  * Materializes a deferred Node framework request body exactly once.
61
62
  *
@@ -1 +1 @@
1
- {"version":3,"file":"internal-node-request.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-request.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"internal-node-request.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-request.ts"],"names":[],"mappings":"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"}
@@ -94,6 +94,7 @@ export function createDeferredFrameworkRequest(request, signal, multipartOptions
94
94
  path: urlParts.path,
95
95
  queryFactory: () => parseQueryParamsFromSearch(urlParts.search),
96
96
  raw: request,
97
+ requestId: resolvePrimaryRequestIdFromHeaders(headers),
97
98
  signal,
98
99
  url: urlParts.path + urlParts.search
99
100
  });
@@ -116,12 +117,15 @@ export function createDeferredFrameworkRequestShell({
116
117
  query,
117
118
  queryFactory,
118
119
  raw,
120
+ requestId,
119
121
  signal,
120
122
  url
121
123
  }) {
124
+ const hasQuerySnapshot = query !== undefined;
125
+ const hasLazySignal = typeof signal === 'function';
122
126
  const resolveHeaders = headersFactory ? createMemoizedValue(headersFactory) : () => headers ?? {};
123
127
  const resolveCookies = createMemoizedValue(() => parseCookieHeader(cookieHeader ?? resolveHeaders().cookie));
124
- const resolveQuery = createMemoizedValue(() => query ?? queryFactory?.() ?? {});
128
+ const resolveQuery = hasQuerySnapshot ? undefined : createMemoizedValue(() => queryFactory?.() ?? {});
125
129
  const frameworkRequest = {
126
130
  get cookies() {
127
131
  return resolveCookies();
@@ -132,13 +136,32 @@ export function createDeferredFrameworkRequestShell({
132
136
  method: method ?? 'GET',
133
137
  params: {},
134
138
  path,
135
- get query() {
136
- return resolveQuery();
137
- },
139
+ requestId,
138
140
  raw,
139
- signal,
140
141
  url
141
142
  };
143
+ if (hasLazySignal) {
144
+ Object.defineProperty(frameworkRequest, 'signal', {
145
+ configurable: true,
146
+ enumerable: true,
147
+ get() {
148
+ return signal();
149
+ }
150
+ });
151
+ } else {
152
+ frameworkRequest.signal = signal;
153
+ }
154
+ if (hasQuerySnapshot) {
155
+ frameworkRequest.query = query;
156
+ } else {
157
+ Object.defineProperty(frameworkRequest, 'query', {
158
+ configurable: true,
159
+ enumerable: true,
160
+ get() {
161
+ return resolveQuery();
162
+ }
163
+ });
164
+ }
142
165
  if (materializeBody) {
143
166
  frameworkRequest.materializeBody = materializeBody;
144
167
  }
@@ -232,6 +255,10 @@ export function resolveRequestIdFromHeaders(headers) {
232
255
  const requestId = headers['x-request-id'] ?? headers['x-correlation-id'];
233
256
  return Array.isArray(requestId) ? requestId[0] : requestId;
234
257
  }
258
+ function resolvePrimaryRequestIdFromHeaders(headers) {
259
+ const requestId = headers['x-request-id'];
260
+ return Array.isArray(requestId) ? requestId[0] : requestId;
261
+ }
235
262
 
236
263
  /**
237
264
  * Parses a raw URL search string into the framework query shape.
package/dist/types.d.ts CHANGED
@@ -22,6 +22,15 @@ export interface ModuleDefinition {
22
22
  export interface BootstrapModuleOptions {
23
23
  duplicateProviderPolicy?: 'warn' | 'throw' | 'ignore';
24
24
  logger?: ApplicationLogger;
25
+ /**
26
+ * Opts this bootstrap into the process-local module graph compile result cache.
27
+ *
28
+ * The cache is disabled by default. When enabled, successful graph compiles are
29
+ * keyed by root module identity, runtime provider inputs, validation tokens,
30
+ * core metadata write versions, and the runtime compile algorithm version.
31
+ * Failed compilations are never cached.
32
+ */
33
+ moduleGraphCache?: boolean;
25
34
  providers?: Provider[];
26
35
  validationTokens?: Token[];
27
36
  }
@@ -91,6 +100,12 @@ export interface ExceptionFilterHandler {
91
100
  /** High-level bootstrap options for creating an HTTP application shell. */
92
101
  export interface BootstrapApplicationOptions {
93
102
  adapter?: HttpApplicationAdapter;
103
+ /**
104
+ * Enables the opt-in process-local module graph compile result cache for this
105
+ * bootstrap. The default is `false`, so each bootstrap compiles a fresh graph
106
+ * unless callers explicitly request cache reuse.
107
+ */
108
+ moduleGraphCache?: boolean;
94
109
  /**
95
110
  * Policy for duplicate provider tokens across modules.
96
111
  *
@@ -127,8 +142,13 @@ export interface CreateApplicationContextOptions extends Omit<BootstrapApplicati
127
142
  }
128
143
  /** Runtime transport contract used by microservice application shells. */
129
144
  export interface MicroserviceRuntime {
145
+ /** Release transport resources owned by the microservice runtime during shutdown. */
146
+ close?(signal?: string): MaybePromise<void>;
147
+ /** Emit a fire-and-forget message for the given transport pattern. */
130
148
  emit?(pattern: string, payload: unknown): MaybePromise<void>;
149
+ /** Start listening for transport messages. */
131
150
  listen(): MaybePromise<void>;
151
+ /** Send a request/response message for the given transport pattern. */
132
152
  send?(pattern: string, payload: unknown, signal?: AbortSignal): MaybePromise<unknown>;
133
153
  }
134
154
  /** Options accepted by `Application.connectMicroservice(...)`. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/web.d.ts CHANGED
@@ -11,8 +11,10 @@ declare module '@fluojs/http' {
11
11
  * Configures Web request parsing, multipart handling, and raw body preservation.
12
12
  */
13
13
  export interface CreateWebRequestResponseFactoryOptions {
14
+ consumeOriginalBody?: boolean;
14
15
  maxBodySize?: number;
15
16
  multipart?: MultipartOptions;
17
+ preferNativeJsonBodyReader?: boolean;
16
18
  rawBody?: boolean;
17
19
  }
18
20
  /**
package/dist/web.d.ts.map CHANGED
@@ -1 +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;AAMtB,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;;;;OAIG;IACH,OAAO,CAAC,EAAE,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,EAAE,oBAAoB,CAAC,CAAC;IACzF,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;AAgNhD;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,sCAA2C,GACnD,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,EAAE,oBAAoB,CAAC,CA6BhF;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,UAAU,EACV,yBAAiG,EACjG,OAAO,EACP,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,CAW3B"}
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;AAMtB,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,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,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;;;;OAIG;IACH,OAAO,CAAC,EAAE,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,EAAE,oBAAoB,CAAC,CAAC;IACzF,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;AAgNhD;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,sCAA2C,GACnD,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,EAAE,oBAAoB,CAAC,CA+BhF;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,UAAU,EACV,yBAAiG,EACjG,OAAO,EACP,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,CAW3B"}
package/dist/web.js CHANGED
@@ -170,7 +170,7 @@ class MutableWebFrameworkResponse {
170
170
  export function createWebRequestResponseFactory(options = {}) {
171
171
  return {
172
172
  async createRequest(request, signal) {
173
- return createDeferredWebFrameworkRequest(request, signal, options.multipart, options.maxBodySize ?? DEFAULT_MAX_BODY_SIZE, options.rawBody ?? false);
173
+ return createDeferredWebFrameworkRequest(request, signal, options.multipart, options.maxBodySize ?? DEFAULT_MAX_BODY_SIZE, options.rawBody ?? false, options.preferNativeJsonBodyReader ?? false, options.consumeOriginalBody ?? false);
174
174
  },
175
175
  materializeRequest(request) {
176
176
  return materializeWebFrameworkRequestBody(request);
@@ -241,7 +241,7 @@ export async function createWebFrameworkRequest(request, signal, multipartOption
241
241
  * @param preserveRawBody - Whether materialization should retain raw request body bytes.
242
242
  * @returns The framework request shell with metadata snapshotted and body materialization deferred.
243
243
  */
244
- function createDeferredWebFrameworkRequest(request, signal, multipartOptions, maxBodySize = DEFAULT_MAX_BODY_SIZE, preserveRawBody = false) {
244
+ function createDeferredWebFrameworkRequest(request, signal, multipartOptions, maxBodySize = DEFAULT_MAX_BODY_SIZE, preserveRawBody = false, preferNativeJsonBodyReader = false, consumeOriginalBody = false) {
245
245
  const url = new URL(request.url);
246
246
  const requestHeaders = new Headers(request.headers);
247
247
  const method = request.method;
@@ -250,15 +250,11 @@ function createDeferredWebFrameworkRequest(request, signal, multipartOptions, ma
250
250
  const query = createMemoizedValue(() => parseQueryString(url.search));
251
251
  const contentType = requestHeaders.get('content-type') ?? undefined;
252
252
  const isMultipart = typeof contentType === 'string' && contentType.includes('multipart/form-data');
253
- const materializeBody = createMemoizedAsyncValue(async () => {
253
+ const hasRequestBody = request.body !== null;
254
+ const materializeBody = hasRequestBody ? createMemoizedAsyncValue(async () => {
254
255
  if (isMultipart) {
255
256
  const materializedRequest = request.clone();
256
- const result = await parseMultipart({
257
- body: materializedRequest.body,
258
- headers: requestHeaders,
259
- method,
260
- url: request.url
261
- }, {
257
+ const result = await parseMultipart(createRequestWithSnapshotMetadata(materializedRequest, request.url, method, requestHeaders), {
262
258
  ...multipartOptions,
263
259
  maxTotalSize: multipartOptions?.maxTotalSize ?? maxBodySize
264
260
  });
@@ -271,12 +267,13 @@ function createDeferredWebFrameworkRequest(request, signal, multipartOptions, ma
271
267
  frameworkRequest.body = undefined;
272
268
  return;
273
269
  }
274
- const bodyResult = await readWebRequestBody(request.clone(), contentType, maxBodySize, preserveRawBody);
270
+ const requestToRead = consumeOriginalBody ? request : request.clone();
271
+ const bodyResult = await readWebRequestBody(requestToRead, contentType, maxBodySize, preserveRawBody, preferNativeJsonBodyReader);
275
272
  frameworkRequest.body = bodyResult.body;
276
273
  if (bodyResult.rawBody) {
277
274
  frameworkRequest.rawBody = bodyResult.rawBody;
278
275
  }
279
- });
276
+ }) : undefined;
280
277
  const frameworkRequest = {
281
278
  get cookies() {
282
279
  return cookies();
@@ -291,13 +288,28 @@ function createDeferredWebFrameworkRequest(request, signal, multipartOptions, ma
291
288
  return query();
292
289
  },
293
290
  raw: request,
291
+ requestId: requestHeaders.get('x-request-id') ?? undefined,
294
292
  signal,
295
293
  url: url.pathname + url.search,
296
294
  materializeBody
297
295
  };
296
+ if (!hasRequestBody) {
297
+ frameworkRequest.body = undefined;
298
+ }
298
299
  const nativeRouteHandoff = consumeRawRequestNativeRouteHandoff(request);
299
300
  return nativeRouteHandoff ? attachFrameworkRequestNativeRouteHandoff(frameworkRequest, nativeRouteHandoff) : frameworkRequest;
300
301
  }
302
+ function createRequestWithSnapshotMetadata(request, url, method, headers) {
303
+ const init = {
304
+ headers: new Headers(headers),
305
+ method
306
+ };
307
+ if (request.body) {
308
+ init.body = request.body;
309
+ init.duplex = 'half';
310
+ }
311
+ return new Request(url, init);
312
+ }
301
313
  function validateWebRequestContentLength(request, maxBodySize) {
302
314
  const contentLength = request.headers.get('content-length');
303
315
  if (contentLength === null) {
@@ -410,14 +422,23 @@ function parseCookieHeader(cookieHeader) {
410
422
  return [pair.slice(0, index).trim(), decodeCookieValue(pair.slice(index + 1).trim())];
411
423
  }));
412
424
  }
413
- async function readWebRequestBody(request, contentType, maxBodySize = DEFAULT_MAX_BODY_SIZE, preserveRawBody = false) {
425
+ async function readWebRequestBody(request, contentType, maxBodySize = DEFAULT_MAX_BODY_SIZE, preserveRawBody = false, preferNativeJsonBodyReader = false) {
414
426
  validateWebRequestContentLength(request, maxBodySize);
415
427
  if (!request.body) {
416
428
  return {
417
429
  body: undefined
418
430
  };
419
431
  }
420
- const rawBody = await readByteLimitedStream(request.body, maxBodySize);
432
+ if (!preserveRawBody && isJsonContentType(contentType) && (preferNativeJsonBodyReader || isContentLengthWithinLimit(request, maxBodySize))) {
433
+ const rawBody = new Uint8Array(await request.arrayBuffer());
434
+ if (rawBody.byteLength > maxBodySize) {
435
+ throw new PayloadTooLargeException(REQUEST_BODY_LIMIT_MESSAGE);
436
+ }
437
+ return parseWebRequestRawBody(rawBody, contentType, preserveRawBody);
438
+ }
439
+ return parseWebRequestRawBody(await readByteLimitedStream(request.body, maxBodySize), contentType, preserveRawBody);
440
+ }
441
+ function parseWebRequestRawBody(rawBody, contentType, preserveRawBody) {
421
442
  if (rawBody.byteLength === 0) {
422
443
  return {
423
444
  body: undefined
@@ -430,7 +451,7 @@ async function readWebRequestBody(request, contentType, maxBodySize = DEFAULT_MA
430
451
  rawBody: preserveRawBody ? rawBody : undefined
431
452
  };
432
453
  }
433
- if (typeof contentType === 'string' && contentType.includes('application/json')) {
454
+ if (isJsonContentType(contentType)) {
434
455
  try {
435
456
  return {
436
457
  body: JSON.parse(bodyText),
@@ -445,6 +466,14 @@ async function readWebRequestBody(request, contentType, maxBodySize = DEFAULT_MA
445
466
  rawBody: preserveRawBody ? rawBody : undefined
446
467
  };
447
468
  }
469
+ function isContentLengthWithinLimit(request, maxBodySize) {
470
+ const contentLength = request.headers.get('content-length');
471
+ if (contentLength === null) {
472
+ return false;
473
+ }
474
+ const parsedContentLength = Number(contentLength);
475
+ return Number.isFinite(parsedContentLength) && parsedContentLength > 0 && parsedContentLength <= maxBodySize;
476
+ }
448
477
  async function readByteLimitedStream(stream, maxBodySize) {
449
478
  const reader = stream.getReader();
450
479
  const chunks = [];
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "module-graph",
10
10
  "orchestration"
11
11
  ],
12
- "version": "1.0.0-beta.7",
12
+ "version": "1.0.0-beta.9",
13
13
  "private": false,
14
14
  "license": "MIT",
15
15
  "repository": {
@@ -67,14 +67,14 @@
67
67
  "dist"
68
68
  ],
69
69
  "dependencies": {
70
- "@fluojs/config": "^1.0.0-beta.3",
71
- "@fluojs/core": "^1.0.0-beta.2",
72
- "@fluojs/di": "^1.0.0-beta.5",
73
- "@fluojs/http": "^1.0.0-beta.5"
70
+ "@fluojs/core": "^1.0.0-beta.3",
71
+ "@fluojs/config": "^1.0.0-beta.5",
72
+ "@fluojs/di": "^1.0.0-beta.6",
73
+ "@fluojs/http": "^1.0.0-beta.9"
74
74
  },
75
75
  "devDependencies": {
76
76
  "vitest": "^3.2.4",
77
- "@fluojs/serialization": "^1.0.0-beta.3"
77
+ "@fluojs/serialization": "^1.0.0-beta.4"
78
78
  },
79
79
  "scripts": {
80
80
  "prebuild": "node ../../tooling/scripts/clean-dist.mjs",