@fluojs/http 1.0.0-beta.1 → 1.0.0-beta.2

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
@@ -10,6 +10,7 @@
10
10
  - [사용 시점](#사용-시점)
11
11
  - [빠른 시작](#빠른-시작)
12
12
  - [주요 패턴](#주요-패턴)
13
+ - [요청 정리와 런타임 이식성](#요청-정리와-런타임-이식성)
13
14
  - [공개 API](#공개-api)
14
15
  - [관련 패키지](#관련-패키지)
15
16
  - [예제 소스](#예제-소스)
@@ -112,14 +113,20 @@ stream(_input: undefined, ctx: RequestContext) {
112
113
  }
113
114
  ```
114
115
 
116
+ ## 요청 정리와 런타임 이식성
117
+
118
+ 디스패처는 활성 dispatch 동안에만 `AsyncLocalStorage`로 `RequestContext`를 바인딩하고, 요청 observer가 끝난 뒤 `finally` 경로에서 request-scoped DI 컨테이너를 dispose합니다. 이 동작은 정상 성공, 처리된 오류, 중단된 요청 경로에서 request-scoped provider가 다음 요청으로 새지 않게 합니다.
119
+
120
+ 어댑터는 플랫폼이 제공한다면 `FrameworkRequest.signal`에 `AbortSignal`을 전달해야 합니다. SSE에서는 가능하면 `FrameworkResponse.stream.onClose(...)`도 노출해야 합니다. `SseResponse`는 request abort와 raw stream close를 모두 구독하고, 멱등하게 닫히며, 어느 쪽이 먼저 종료되더라도 등록한 listener를 제거합니다.
121
+
115
122
  ## 공개 API
116
123
 
117
- - **라우팅 데코레이터**: `Controller`, `Get`, `Post`, `Put`, `Patch`, `Delete`, `All`
118
- - **바인딩 데코레이터**: `FromBody`, `FromQuery`, `FromPath`, `FromHeader`, `FromCookie`, `RequestDto`
119
- - **실행 데코레이터**: `UseGuards`, `UseInterceptors`, `HttpCode`, `Version`, `Header`, `Redirect`
124
+ - **라우팅 데코레이터**: `Controller`, `Get`, `Post`, `Put`, `Patch`, `Delete`, `All`, `Options`, `Head`
125
+ - **바인딩 데코레이터**: `FromBody`, `FromQuery`, `FromPath`, `FromHeader`, `FromCookie`, `RequestDto`, `Optional`, `Convert`
126
+ - **실행 데코레이터**: `UseGuards`, `UseInterceptors`, `HttpCode`, `Version`, `Header`, `Redirect`, `Produces`
120
127
  - **핵심 런타임 타입**: `RequestContext`, `FrameworkRequest`, `FrameworkResponse`, `SseResponse`
121
128
  - **예외**: `BadRequestException`, `UnauthorizedException`, `ForbiddenException`, `NotFoundException`, `InternalServerErrorException`, `PayloadTooLargeException`
122
- - **헬퍼**: `createHandlerMapping`, `createDispatcher`, `createCorsMiddleware`, `createRateLimitMiddleware`, `getCurrentRequestContext`
129
+ - **헬퍼**: `createHandlerMapping`, `createDispatcher`, `forRoutes`, `normalizeRoutePattern`, `matchRoutePattern`, `isMiddlewareRouteConfig`, `createCorrelationMiddleware`, `createCorsMiddleware`, `createRateLimitMiddleware`, `createSecurityHeadersMiddleware`, `getCurrentRequestContext`, `encodeSseComment`, `encodeSseMessage`
123
130
 
124
131
  ## 내부 서브경로 (`@fluojs/http/internal`)
125
132
 
package/README.md CHANGED
@@ -10,6 +10,7 @@ The HTTP execution layer that turns route metadata into a request pipeline with
10
10
  - [When to Use](#when-to-use)
11
11
  - [Quick Start](#quick-start)
12
12
  - [Common Patterns](#common-patterns)
13
+ - [Request Cleanup and Portability](#request-cleanup-and-portability)
13
14
  - [Public API](#public-api)
14
15
  - [Related Packages](#related-packages)
15
16
  - [Example Sources](#example-sources)
@@ -114,14 +115,20 @@ stream(_input: undefined, ctx: RequestContext) {
114
115
  }
115
116
  ```
116
117
 
118
+ ## Request Cleanup and Portability
119
+
120
+ The dispatcher binds `RequestContext` with `AsyncLocalStorage` for the active dispatch only and disposes the request-scoped DI container from its `finally` path after request observers finish. This keeps per-request providers from leaking across normal success, handled error, and aborted request paths.
121
+
122
+ Adapters should pass an `AbortSignal` on `FrameworkRequest.signal` when the platform exposes one. For SSE, adapters should also expose `FrameworkResponse.stream.onClose(...)` when possible; `SseResponse` listens to both request abort and raw stream close, closes idempotently, and removes registered listeners when either side terminates first.
123
+
117
124
  ## Public API
118
125
 
119
- - **Routing decorators**: `Controller`, `Get`, `Post`, `Put`, `Patch`, `Delete`, `All`
120
- - **Binding decorators**: `FromBody`, `FromQuery`, `FromPath`, `FromHeader`, `FromCookie`, `RequestDto`
121
- - **Execution decorators**: `UseGuards`, `UseInterceptors`, `HttpCode`, `Version`, `Header`, `Redirect`
126
+ - **Routing decorators**: `Controller`, `Get`, `Post`, `Put`, `Patch`, `Delete`, `All`, `Options`, `Head`
127
+ - **Binding decorators**: `FromBody`, `FromQuery`, `FromPath`, `FromHeader`, `FromCookie`, `RequestDto`, `Optional`, `Convert`
128
+ - **Execution decorators**: `UseGuards`, `UseInterceptors`, `HttpCode`, `Version`, `Header`, `Redirect`, `Produces`
122
129
  - **Core runtime types**: `RequestContext`, `FrameworkRequest`, `FrameworkResponse`, `SseResponse`
123
130
  - **Exceptions**: `BadRequestException`, `UnauthorizedException`, `ForbiddenException`, `NotFoundException`, `InternalServerErrorException`, `PayloadTooLargeException`
124
- - **Helpers**: `createHandlerMapping`, `createDispatcher`, `createCorsMiddleware`, `createRateLimitMiddleware`, `getCurrentRequestContext`
131
+ - **Helpers**: `createHandlerMapping`, `createDispatcher`, `forRoutes`, `normalizeRoutePattern`, `matchRoutePattern`, `isMiddlewareRouteConfig`, `createCorrelationMiddleware`, `createCorsMiddleware`, `createRateLimitMiddleware`, `createSecurityHeadersMiddleware`, `getCurrentRequestContext`, `encodeSseComment`, `encodeSseMessage`
125
132
 
126
133
  ## Internal Subpath (`@fluojs/http/internal`)
127
134
 
@@ -1,11 +1,35 @@
1
1
  import type { RequestContext } from '../types.js';
2
+ /** Options that customize the fields emitted for one server-sent event frame. */
2
3
  export interface SseSendOptions {
4
+ /** Optional SSE event name. Newline characters are stripped before writing. */
3
5
  event?: string;
6
+ /** Optional SSE event id. Newline characters are stripped before writing. */
4
7
  id?: string | number;
8
+ /** Optional client retry delay in milliseconds. Non-finite or negative values are ignored. */
5
9
  retry?: number;
6
10
  }
11
+ /**
12
+ * Encodes a comment as a canonical server-sent event comment frame.
13
+ *
14
+ * @param comment Comment text to split into SSE comment lines.
15
+ * @returns A complete SSE comment frame ending in a blank line.
16
+ */
7
17
  export declare function encodeSseComment(comment: string): string;
18
+ /**
19
+ * Encodes data and optional event fields as a server-sent event message frame.
20
+ *
21
+ * @param data Payload to write. Strings are sent as-is; other values are JSON serialized.
22
+ * @param options Optional event metadata fields.
23
+ * @returns A complete SSE message frame ending in a blank line.
24
+ * @throws {TypeError} When `data` cannot be represented as an SSE data field.
25
+ */
8
26
  export declare function encodeSseMessage(data: unknown, options?: SseSendOptions): string;
27
+ /**
28
+ * Response helper for server-sent event streams backed by an adapter-provided response stream.
29
+ *
30
+ * The helper commits SSE headers immediately, closes idempotently on request abort
31
+ * or raw stream close, and removes all registered close/abort listeners during cleanup.
32
+ */
9
33
  export declare class SseResponse {
10
34
  private readonly context;
11
35
  private closed;
@@ -13,8 +37,22 @@ export declare class SseResponse {
13
37
  private removeCloseListener?;
14
38
  private readonly onAbort;
15
39
  constructor(context: RequestContext);
40
+ /**
41
+ * Writes one SSE data message when the stream is still open.
42
+ *
43
+ * @param data Payload to encode into `data:` lines.
44
+ * @param options Optional event metadata fields.
45
+ * @returns `true` when the underlying stream accepted the frame without backpressure.
46
+ */
16
47
  send(data: unknown, options?: SseSendOptions): boolean;
48
+ /**
49
+ * Writes one SSE comment frame when the stream is still open.
50
+ *
51
+ * @param comment Comment text to encode.
52
+ * @returns `true` when the underlying stream accepted the frame without backpressure.
53
+ */
17
54
  comment(comment: string): boolean;
55
+ /** Closes the SSE stream and removes registered abort/close listeners exactly once. */
18
56
  close(): void;
19
57
  private writeFrame;
20
58
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/context/sse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA8C,cAAc,EAAE,MAAM,aAAa,CAAC;AAE9F,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoCD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKxD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,cAAmB,GAAG,MAAM,CAoBpF;AAED,qBAAa,WAAW;IASV,OAAO,CAAC,QAAQ,CAAC,OAAO;IARpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,mBAAmB,CAAC,CAAa;IAEzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAEtB;gBAE2B,OAAO,EAAE,cAAc;IA0BpD,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO;IAI1D,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIjC,KAAK,IAAI,IAAI;IAiBb,OAAO,CAAC,UAAU;CAYnB"}
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/context/sse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA8C,cAAc,EAAE,MAAM,aAAa,CAAC;AAE9F,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,8FAA8F;IAC9F,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoCD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKxD;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,cAAmB,GAAG,MAAM,CAoBpF;AAED;;;;;GAKG;AACH,qBAAa,WAAW;IASV,OAAO,CAAC,QAAQ,CAAC,OAAO;IARpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,mBAAmB,CAAC,CAAa;IAEzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAEtB;gBAE2B,OAAO,EAAE,cAAc;IAmCpD;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO;IAI1D;;;;;OAKG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIjC,uFAAuF;IACvF,KAAK,IAAI,IAAI;IAiBb,OAAO,CAAC,UAAU;CAYnB"}
@@ -1,3 +1,5 @@
1
+ /** Options that customize the fields emitted for one server-sent event frame. */
2
+
1
3
  function sanitizeSseField(value) {
2
4
  return value.replace(/\r/g, '').replace(/\n/g, '');
3
5
  }
@@ -23,11 +25,27 @@ function resolveSseStream(response) {
23
25
  }
24
26
  return response.stream;
25
27
  }
28
+
29
+ /**
30
+ * Encodes a comment as a canonical server-sent event comment frame.
31
+ *
32
+ * @param comment Comment text to split into SSE comment lines.
33
+ * @returns A complete SSE comment frame ending in a blank line.
34
+ */
26
35
  export function encodeSseComment(comment) {
27
36
  const lines = splitSseLines(comment);
28
37
  const encoded = lines.map(line => line.length === 0 ? ':' : `: ${line}`);
29
38
  return `${encoded.join('\n')}\n\n`;
30
39
  }
40
+
41
+ /**
42
+ * Encodes data and optional event fields as a server-sent event message frame.
43
+ *
44
+ * @param data Payload to write. Strings are sent as-is; other values are JSON serialized.
45
+ * @param options Optional event metadata fields.
46
+ * @returns A complete SSE message frame ending in a blank line.
47
+ * @throws {TypeError} When `data` cannot be represented as an SSE data field.
48
+ */
31
49
  export function encodeSseMessage(data, options = {}) {
32
50
  const lines = [];
33
51
  if (options.event !== undefined) {
@@ -44,6 +62,13 @@ export function encodeSseMessage(data, options = {}) {
44
62
  }
45
63
  return `${lines.join('\n')}\n\n`;
46
64
  }
65
+
66
+ /**
67
+ * Response helper for server-sent event streams backed by an adapter-provided response stream.
68
+ *
69
+ * The helper commits SSE headers immediately, closes idempotently on request abort
70
+ * or raw stream close, and removes all registered close/abort listeners during cleanup.
71
+ */
47
72
  export class SseResponse {
48
73
  closed = false;
49
74
  stream;
@@ -70,16 +95,39 @@ export class SseResponse {
70
95
  context.request.signal?.addEventListener('abort', this.onAbort, {
71
96
  once: true
72
97
  });
73
- if (context.request.signal === undefined) {
74
- this.removeCloseListener = this.stream.onClose?.(this.onAbort) ?? undefined;
98
+ const removeCloseListener = this.stream.onClose?.(this.onAbort) ?? undefined;
99
+ if (this.closed) {
100
+ removeCloseListener?.();
101
+ return;
102
+ }
103
+ this.removeCloseListener = removeCloseListener;
104
+ if (this.stream.closed) {
105
+ this.close();
75
106
  }
76
107
  }
108
+
109
+ /**
110
+ * Writes one SSE data message when the stream is still open.
111
+ *
112
+ * @param data Payload to encode into `data:` lines.
113
+ * @param options Optional event metadata fields.
114
+ * @returns `true` when the underlying stream accepted the frame without backpressure.
115
+ */
77
116
  send(data, options = {}) {
78
117
  return this.writeFrame(encodeSseMessage(data, options));
79
118
  }
119
+
120
+ /**
121
+ * Writes one SSE comment frame when the stream is still open.
122
+ *
123
+ * @param comment Comment text to encode.
124
+ * @returns `true` when the underlying stream accepted the frame without backpressure.
125
+ */
80
126
  comment(comment) {
81
127
  return this.writeFrame(encodeSseComment(comment));
82
128
  }
129
+
130
+ /** Closes the SSE stream and removes registered abort/close listeners exactly once. */
83
131
  close() {
84
132
  if (this.closed) {
85
133
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { metadataSymbol } from '@fluojs/core/internal';
1
+ import { getStandardMetadataBag as readStandardMetadataBag, metadataSymbol } from '@fluojs/core/internal';
2
2
  import { validateRoutePath } from './route-path.js';
3
3
  const standardControllerMetadataKey = Symbol.for('fluo.standard.controller');
4
4
  const standardRouteMetadataKey = Symbol.for('fluo.standard.route');
@@ -233,7 +233,7 @@ export const HttpCode = createRouteValueDecorator((record, status) => {
233
233
  * @returns A defensive copy of declared media types, or `undefined` when not configured.
234
234
  */
235
235
  export function getRouteProducesMetadata(controllerToken, propertyKey) {
236
- const bag = controllerToken[metadataSymbol];
236
+ const bag = readStandardMetadataBag(controllerToken);
237
237
  const routeMap = bag?.[standardRouteMetadataKey];
238
238
  const produces = routeMap?.get(propertyKey)?.produces;
239
239
  return produces ? [...produces] : undefined;
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "controller",
11
11
  "rest"
12
12
  ],
13
- "version": "1.0.0-beta.1",
13
+ "version": "1.0.0-beta.2",
14
14
  "private": false,
15
15
  "license": "MIT",
16
16
  "repository": {
@@ -41,9 +41,9 @@
41
41
  "dist"
42
42
  ],
43
43
  "dependencies": {
44
+ "@fluojs/core": "^1.0.0-beta.2",
44
45
  "@fluojs/validation": "^1.0.0-beta.1",
45
- "@fluojs/core": "^1.0.0-beta.1",
46
- "@fluojs/di": "^1.0.0-beta.1"
46
+ "@fluojs/di": "^1.0.0-beta.3"
47
47
  },
48
48
  "devDependencies": {
49
49
  "vitest": "^3.2.4"