@fluojs/platform-express 1.0.0-beta.1 → 1.0.0-beta.3

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 @@ fluo 런타임을 위한 Express 기반 HTTP 어댑터 패키지입니다.
10
10
  - [사용 시점](#사용-시점)
11
11
  - [빠른 시작](#빠른-시작)
12
12
  - [주요 패턴](#주요-패턴)
13
+ - [어댑터 계약](#어댑터-계약)
13
14
  - [공개 API 개요](#공개-api-개요)
14
15
  - [관련 패키지](#관련-패키지)
15
16
  - [예제 소스](#예제-소스)
@@ -53,7 +54,7 @@ async streamEvents(@Res() res: FrameworkResponse) {
53
54
  ```
54
55
 
55
56
  ### 바디 파싱 및 멀티파트
56
- `rawBody` 및 멀티파트 form-data 파싱을 즉시 사용할 수 있습니다. 어댑터를 직접 생성할 때는 멀티파트 제한을 두 번째 인자로 전달하고, `bootstrapExpressApplication(...)` 및 `runExpressApplication(...)`에서는 같은 설정을 `options.multipart` 아래에 전달하면 됩니다.
57
+ `rawBody` 및 멀티파트 form-data 파싱을 즉시 사용할 수 있습니다. 어댑터를 직접 생성할 때는 멀티파트 제한을 두 번째 인자로 전달하고, `bootstrapExpressApplication(...)` 및 `runExpressApplication(...)`에서는 같은 설정을 `options.multipart` 아래에 전달하면 됩니다. `multipart.maxTotalSize`를 지정하지 않으면 `maxBodySize`가 기본 총 멀티파트 payload 제한으로 사용되어 HTTP 어댑터 간 바디 크기 제한 동작이 portable하게 유지됩니다.
57
58
 
58
59
  ```typescript
59
60
  const adapter = createExpressAdapter(
@@ -67,6 +68,21 @@ const adapter = createExpressAdapter(
67
68
  );
68
69
  ```
69
70
 
71
+ ### 안전한 fallback을 포함한 Native Route Registration
72
+ 이제 어댑터는 의미 보존이 가능한 명시적 HTTP 메서드 라우트를 Express Router에 사전 등록하면서도, 실제 요청 처리는 계속 공유 fluo dispatcher를 통해 수행합니다.
73
+
74
+ 덕분에 guards, interceptors, observers, body parsing, raw body 캡처, SSE, 오류 응답은 기존과 같은 framework-owned 실행 경로를 유지하면서, Express가 먼저 일부 경로 매칭 비용을 줄일 수 있습니다.
75
+
76
+ 문서화된 fluo semantics를 바꾸지 않기 위해 `/:id` 와 `/:slug`처럼 shape가 겹치는 파라미터 라우트, `@All(...)` 핸들러, `OPTIONS` 소유권, 그리고 duplicate slash/trailing slash 정규화에 의존하는 요청은 catch-all fallback 경로에 남겨둡니다.
77
+
78
+ ## 어댑터 계약
79
+
80
+ - **공유 dispatcher 소유권 유지**: Native Express Router 매치 이후에도 실제 요청은 공유 fluo dispatcher가 처리하므로 middleware, guards, interceptors, observers, params, error envelope 계약은 그대로 유지됩니다.
81
+ - **안전한 fallback 범위**: `@All(...)` 핸들러와 shape가 겹치는 파라미터 라우트는 Express Router에 강제 등록하지 않고 의도적으로 catch-all fallback 경로에 둡니다.
82
+ - **OPTIONS 소유권 parity**: 어댑터는 native route에 대해 Express Router가 `OPTIONS`를 자동 응답하지 못하게 막아, 미지원 메서드도 계속 fluo dispatcher semantics로 흘러가고 `@All(...)` 핸들러가 정의된 경우 `OPTIONS`도 그대로 소유할 수 있게 합니다.
83
+ - **경로 정규화 parity**: duplicate slash 변형처럼 Express Router와 fluo의 정규화 방식이 다를 수 있는 요청도 fallback dispatch를 통해 fluo의 normalized route contract를 유지합니다.
84
+ - **버저닝 parity**: Express Router가 최초 path match를 하더라도 header/media-type/custom version 선택은 계속 dispatcher가 최종 결정합니다.
85
+
70
86
  ## 공개 API 개요
71
87
 
72
88
  - `createExpressAdapter(options)`: Express HTTP 어댑터를 위한 팩토리입니다.
package/README.md CHANGED
@@ -10,6 +10,7 @@ Express-backed HTTP adapter for the fluo runtime.
10
10
  - [When to Use](#when-to-use)
11
11
  - [Quick Start](#quick-start)
12
12
  - [Common Patterns](#common-patterns)
13
+ - [Adapter Contract](#adapter-contract)
13
14
  - [Public API Overview](#public-api-overview)
14
15
  - [Related Packages](#related-packages)
15
16
  - [Example Sources](#example-sources)
@@ -53,7 +54,7 @@ async streamEvents(@Res() res: FrameworkResponse) {
53
54
  ```
54
55
 
55
56
  ### Body Parsing and Multipart
56
- The adapter handles `rawBody` and multipart form-data parsing out of the box. When you construct the adapter directly, pass multipart limits as the second argument. `bootstrapExpressApplication(...)` and `runExpressApplication(...)` accept the same multipart settings under `options.multipart`.
57
+ The adapter handles `rawBody` and multipart form-data parsing out of the box. When you construct the adapter directly, pass multipart limits as the second argument. `bootstrapExpressApplication(...)` and `runExpressApplication(...)` accept the same multipart settings under `options.multipart`. When `multipart.maxTotalSize` is not set, `maxBodySize` becomes the default total multipart payload cap so body-size limits stay portable across HTTP adapters.
57
58
 
58
59
  ```typescript
59
60
  const adapter = createExpressAdapter(
@@ -67,6 +68,21 @@ const adapter = createExpressAdapter(
67
68
  );
68
69
  ```
69
70
 
71
+ ### Native Route Registration with Safe Fallback
72
+ The adapter now pre-registers semantically safe Express Router handlers for explicit HTTP methods and still dispatches those requests through the shared fluo dispatcher.
73
+
74
+ This keeps guards, interceptors, observers, body parsing, raw body capture, SSE, and error responses on the same framework-owned execution path while allowing Express to short-circuit some path matching work first.
75
+
76
+ To avoid changing documented fluo semantics, overlapping same-shape param routes such as `/:id` and `/:slug`, `@All(...)` handlers, `OPTIONS` ownership, and requests that rely on fluo's duplicate-slash/trailing-slash normalization stay on the catch-all fallback path.
77
+
78
+ ## Adapter Contract
79
+
80
+ - **Shared dispatcher ownership**: Native Express Router matches still hand off to the shared fluo dispatcher, so middleware, guards, interceptors, observers, params, and error envelopes remain framework-defined.
81
+ - **Safe fallback scope**: `@All(...)` handlers and overlapping same-shape param routes intentionally stay on the catch-all fallback path instead of being force-registered through Express Router.
82
+ - **OPTIONS ownership parity**: The adapter prevents Express Router from auto-answering `OPTIONS` for native routes, so unsupported methods still fall through to fluo dispatcher semantics and `@All(...)` handlers can continue to own `OPTIONS` when defined.
83
+ - **Path normalization parity**: Requests that Express Router does not normalize the same way as fluo, such as duplicate-slash variants, still resolve through fallback dispatch so fluo's normalized route contract is preserved.
84
+ - **Versioning parity**: Header/media-type/custom version selection remains dispatcher-owned even when Express Router handles the initial path match.
85
+
70
86
  ## Public API Overview
71
87
 
72
88
  - `createExpressAdapter(options)`: Factory for the Express HTTP adapter.
package/dist/adapter.d.ts CHANGED
@@ -7,6 +7,9 @@ declare module '@fluojs/http' {
7
7
  rawBody?: Uint8Array;
8
8
  }
9
9
  }
10
+ /**
11
+ * Describes the express adapter options contract.
12
+ */
10
13
  export interface ExpressAdapterOptions {
11
14
  host?: string;
12
15
  https?: HttpsServerOptions;
@@ -17,8 +20,17 @@ export interface ExpressAdapterOptions {
17
20
  retryLimit?: number;
18
21
  shutdownTimeoutMs?: number;
19
22
  }
23
+ /**
24
+ * Defines the express application signal type.
25
+ */
20
26
  export type ExpressApplicationSignal = 'SIGINT' | 'SIGTERM';
27
+ /**
28
+ * Defines the cors input type.
29
+ */
21
30
  export type CorsInput = false | string | string[] | CorsOptions;
31
+ /**
32
+ * Describes the bootstrap express application options contract.
33
+ */
22
34
  export interface BootstrapExpressApplicationOptions extends Omit<CreateApplicationOptions, 'adapter' | 'logger' | 'middleware'> {
23
35
  cors?: CorsInput;
24
36
  globalPrefix?: string;
@@ -36,6 +48,9 @@ export interface BootstrapExpressApplicationOptions extends Omit<CreateApplicati
36
48
  securityHeaders?: false | SecurityHeadersOptions;
37
49
  shutdownTimeoutMs?: number;
38
50
  }
51
+ /**
52
+ * Describes the run express application options contract.
53
+ */
39
54
  export interface RunExpressApplicationOptions extends BootstrapExpressApplicationOptions {
40
55
  forceExitTimeoutMs?: number;
41
56
  shutdownSignals?: false | readonly ExpressApplicationSignal[];
@@ -44,6 +59,9 @@ interface ExpressListenTarget {
44
59
  bindTarget: string;
45
60
  url: string;
46
61
  }
62
+ /**
63
+ * Represents the express http application adapter.
64
+ */
47
65
  export declare class ExpressHttpApplicationAdapter implements HttpApplicationAdapter {
48
66
  private readonly port;
49
67
  private readonly host;
@@ -57,7 +75,9 @@ export declare class ExpressHttpApplicationAdapter implements HttpApplicationAda
57
75
  private closeInFlight?;
58
76
  private dispatcher?;
59
77
  private readonly app;
78
+ private nativeRoutesReady;
60
79
  private readonly requestResponseFactory;
80
+ private readonly router;
61
81
  private readonly server;
62
82
  private readonly sockets;
63
83
  constructor(port: number, host: string | undefined, retryDelayMs: number | undefined, retryLimit: number | undefined, httpsOptions: HttpsServerOptions | undefined, multipartOptions?: MultipartOptions | undefined, maxBodySize?: number, preserveRawBody?: boolean, shutdownTimeoutMs?: number);
@@ -67,11 +87,39 @@ export declare class ExpressHttpApplicationAdapter implements HttpApplicationAda
67
87
  listen(dispatcher: Dispatcher): Promise<void>;
68
88
  close(): Promise<void>;
69
89
  private listenWithRetry;
90
+ private registerNativeRoutes;
70
91
  private handleRequest;
71
92
  }
93
+ /**
94
+ * Create express adapter.
95
+ *
96
+ * @param options The options.
97
+ * @param multipartOptions The multipart options.
98
+ * @returns The create express adapter result.
99
+ */
72
100
  export declare function createExpressAdapter(options?: ExpressAdapterOptions, multipartOptions?: MultipartOptions): HttpApplicationAdapter;
101
+ /**
102
+ * Bootstrap express application.
103
+ *
104
+ * @param rootModule The root module.
105
+ * @param options The options.
106
+ * @returns The bootstrap express application result.
107
+ */
73
108
  export declare function bootstrapExpressApplication(rootModule: ModuleType, options: BootstrapExpressApplicationOptions): Promise<Application>;
109
+ /**
110
+ * Run express application.
111
+ *
112
+ * @param rootModule The root module.
113
+ * @param options The options.
114
+ * @returns The run express application result.
115
+ */
74
116
  export declare function runExpressApplication(rootModule: ModuleType, options: RunExpressApplicationOptions): Promise<Application>;
117
+ /**
118
+ * Is express multipart too large error.
119
+ *
120
+ * @param error The error.
121
+ * @returns The is express multipart too large error result.
122
+ */
75
123
  export declare function isExpressMultipartTooLargeError(error: unknown): boolean;
76
124
  export {};
77
125
  //# sourceMappingURL=adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
package/dist/adapter.js CHANGED
@@ -8,13 +8,41 @@ import { createNodeShutdownSignalRegistration, defaultNodeShutdownSignals } from
8
8
  import { parseMultipart } from '@fluojs/runtime/web';
9
9
  import { bootstrapHttpAdapterApplication, runHttpAdapterApplication } from '@fluojs/runtime/internal/http-adapter';
10
10
  import { dispatchWithRequestResponseFactory } from '@fluojs/runtime/internal/request-response-factory';
11
+
12
+ /**
13
+ * Describes the express adapter options contract.
14
+ */
15
+
16
+ /**
17
+ * Defines the express application signal type.
18
+ */
19
+
20
+ /**
21
+ * Defines the cors input type.
22
+ */
23
+
11
24
  const DEFAULT_MAX_BODY_SIZE = 1 * 1024 * 1024;
12
25
  const DEFAULT_SHUTDOWN_TIMEOUT_MS = 10_000;
26
+ const EXPRESS_NATIVE_ROUTE_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'];
27
+
28
+ /**
29
+ * Describes the bootstrap express application options contract.
30
+ */
31
+
32
+ /**
33
+ * Describes the run express application options contract.
34
+ */
35
+
36
+ /**
37
+ * Represents the express http application adapter.
38
+ */
13
39
  export class ExpressHttpApplicationAdapter {
14
40
  closeInFlight;
15
41
  dispatcher;
16
42
  app;
43
+ nativeRoutesReady = false;
17
44
  requestResponseFactory;
45
+ router = express.Router();
18
46
  server;
19
47
  sockets = new Set();
20
48
  constructor(port, host, retryDelayMs = 150, retryLimit = 20, httpsOptions, multipartOptions, maxBodySize = DEFAULT_MAX_BODY_SIZE, preserveRawBody = false, shutdownTimeoutMs = DEFAULT_SHUTDOWN_TIMEOUT_MS) {
@@ -30,6 +58,7 @@ export class ExpressHttpApplicationAdapter {
30
58
  this.app = express();
31
59
  this.requestResponseFactory = createExpressRequestResponseFactory(this.multipartOptions, this.maxBodySize, this.preserveRawBody);
32
60
  this.server = createExpressServer(this.httpsOptions, this.app);
61
+ this.app.use(this.router);
33
62
  this.app.use((request, response) => {
34
63
  void this.handleRequest(request, response);
35
64
  });
@@ -51,6 +80,7 @@ export class ExpressHttpApplicationAdapter {
51
80
  }
52
81
  async listen(dispatcher) {
53
82
  this.dispatcher = dispatcher;
83
+ this.registerNativeRoutes(dispatcher);
54
84
  await this.listenWithRetry();
55
85
  }
56
86
  async close() {
@@ -87,6 +117,23 @@ export class ExpressHttpApplicationAdapter {
87
117
  }
88
118
  }
89
119
  }
120
+ registerNativeRoutes(dispatcher) {
121
+ if (this.nativeRoutesReady) {
122
+ return;
123
+ }
124
+ const nativeRoutes = createExpressNativeRoutes(resolveDispatcherRouteDescriptors(dispatcher));
125
+ Reflect.set(this.router, '__fluoNativeRoutes', nativeRoutes);
126
+ for (const route of nativeRoutes) {
127
+ this.router.all(route.path, (request, response, next) => {
128
+ if (!route.methods.includes(request.method.toUpperCase())) {
129
+ next();
130
+ return;
131
+ }
132
+ void this.handleRequest(request, response);
133
+ });
134
+ }
135
+ this.nativeRoutesReady = true;
136
+ }
90
137
  async handleRequest(request, response) {
91
138
  await dispatchWithRequestResponseFactory({
92
139
  dispatcher: this.dispatcher,
@@ -97,6 +144,60 @@ export class ExpressHttpApplicationAdapter {
97
144
  });
98
145
  }
99
146
  }
147
+ function resolveDispatcherRouteDescriptors(dispatcher) {
148
+ return dispatcher.describeRoutes?.() ?? [];
149
+ }
150
+ function createExpressNativeRoutes(descriptors) {
151
+ const candidates = new Map();
152
+ const shapePaths = new Map();
153
+ for (const descriptor of descriptors) {
154
+ if (descriptor.route.method === 'ALL') {
155
+ continue;
156
+ }
157
+ registerExpressNativeRouteCandidate(candidates, shapePaths, descriptor.route.method, descriptor.route.path);
158
+ }
159
+ const routesByPath = new Map();
160
+ for (const candidate of candidates.values()) {
161
+ if (shapePaths.get(candidate.shapeKey)?.size !== 1) {
162
+ continue;
163
+ }
164
+ let methods = routesByPath.get(candidate.path);
165
+ if (!methods) {
166
+ methods = new Set();
167
+ routesByPath.set(candidate.path, methods);
168
+ }
169
+ methods.add(candidate.method);
170
+ }
171
+ return [...routesByPath.entries()].map(([path, methods]) => ({
172
+ methods: [...methods],
173
+ path
174
+ }));
175
+ }
176
+ function registerExpressNativeRouteCandidate(candidates, shapePaths, method, path) {
177
+ if (!EXPRESS_NATIVE_ROUTE_METHODS.includes(method)) {
178
+ return;
179
+ }
180
+ const nativeMethod = method;
181
+ const routeKey = `${nativeMethod}:${path}`;
182
+ const shapeKey = `${nativeMethod}:${canonicalizeExpressRouteShape(path)}`;
183
+ if (!candidates.has(routeKey)) {
184
+ candidates.set(routeKey, {
185
+ method: nativeMethod,
186
+ path,
187
+ shapeKey
188
+ });
189
+ }
190
+ let paths = shapePaths.get(shapeKey);
191
+ if (!paths) {
192
+ paths = new Set();
193
+ shapePaths.set(shapeKey, paths);
194
+ }
195
+ paths.add(path);
196
+ }
197
+ function canonicalizeExpressRouteShape(path) {
198
+ const segments = path.split('/').filter(Boolean).map(segment => segment.startsWith(':') ? ':' : segment);
199
+ return segments.length === 0 ? '/' : `/${segments.join('/')}`;
200
+ }
100
201
  function createExpressRequestResponseFactory(multipartOptions, maxBodySize = DEFAULT_MAX_BODY_SIZE, preserveRawBody = false) {
101
202
  return {
102
203
  async createRequest(request, signal) {
@@ -118,12 +219,36 @@ function createExpressRequestResponseFactory(multipartOptions, maxBodySize = DEF
118
219
  }
119
220
  };
120
221
  }
222
+
223
+ /**
224
+ * Create express adapter.
225
+ *
226
+ * @param options The options.
227
+ * @param multipartOptions The multipart options.
228
+ * @returns The create express adapter result.
229
+ */
121
230
  export function createExpressAdapter(options = {}, multipartOptions) {
122
231
  return new ExpressHttpApplicationAdapter(resolvePort(options.port), options.host, options.retryDelayMs, options.retryLimit, options.https, multipartOptions, options.maxBodySize, options.rawBody, options.shutdownTimeoutMs);
123
232
  }
233
+
234
+ /**
235
+ * Bootstrap express application.
236
+ *
237
+ * @param rootModule The root module.
238
+ * @param options The options.
239
+ * @returns The bootstrap express application result.
240
+ */
124
241
  export async function bootstrapExpressApplication(rootModule, options) {
125
242
  return bootstrapHttpAdapterApplication(rootModule, options, createExpressAdapter(options, options.multipart));
126
243
  }
244
+
245
+ /**
246
+ * Run express application.
247
+ *
248
+ * @param rootModule The root module.
249
+ * @param options The options.
250
+ * @returns The run express application result.
251
+ */
127
252
  export async function runExpressApplication(rootModule, options) {
128
253
  const adapter = createExpressAdapter(options, options.multipart);
129
254
  return runHttpAdapterApplication(rootModule, {
@@ -218,7 +343,10 @@ async function createFrameworkRequest(request, signal, multipartOptions, maxBody
218
343
  let files;
219
344
  let rawBody;
220
345
  if (isMultipart) {
221
- const parsed = await parseMultipartRequest(request, multipartOptions);
346
+ const parsed = await parseMultipartRequest(request, {
347
+ ...multipartOptions,
348
+ maxTotalSize: multipartOptions?.maxTotalSize ?? maxBodySize
349
+ });
222
350
  body = parsed.fields;
223
351
  files = parsed.files;
224
352
  } else {
@@ -264,6 +392,13 @@ async function parseMultipartRequest(request, options = {}) {
264
392
  throw error;
265
393
  }
266
394
  }
395
+
396
+ /**
397
+ * Is express multipart too large error.
398
+ *
399
+ * @param error The error.
400
+ * @returns The is express multipart too large error result.
401
+ */
267
402
  export function isExpressMultipartTooLargeError(error) {
268
403
  if (error instanceof PayloadTooLargeException) {
269
404
  return true;
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "platform",
9
9
  "server"
10
10
  ],
11
- "version": "1.0.0-beta.1",
11
+ "version": "1.0.0-beta.3",
12
12
  "private": false,
13
13
  "license": "MIT",
14
14
  "repository": {
@@ -36,8 +36,8 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "express": "^5.1.0",
39
- "@fluojs/http": "^1.0.0-beta.1",
40
- "@fluojs/runtime": "^1.0.0-beta.1"
39
+ "@fluojs/http": "^1.0.0-beta.3",
40
+ "@fluojs/runtime": "^1.0.0-beta.4"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/express": "^5.0.3",