@fluojs/platform-express 1.0.0-beta.3 → 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.
- package/README.ko.md +7 -2
- package/README.md +6 -2
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +83 -17
- package/dist/testing-http-adapter-portability.d.js +0 -0
- package/package.json +5 -4
package/README.ko.md
CHANGED
|
@@ -44,6 +44,8 @@ await app.listen();
|
|
|
44
44
|
### 스트리밍 응답 처리 (SSE)
|
|
45
45
|
Express 어댑터는 공유 `SseResponse` 유틸리티를 통해 Server-Sent Events(SSE)를 지원하며, Express 전용 스트림 처리를 추상화합니다.
|
|
46
46
|
|
|
47
|
+
Express 기반 응답 스트림은 공유 fluo 백프레셔 계약도 따릅니다. `response.stream.waitForDrain()`은 `drain`, `close`, `error` 중 어느 쪽이 먼저 와도 완료되므로, 백프레셔가 풀리기 전에 클라이언트가 연결을 끊어도 스트리밍 작성기가 멈추지 않습니다.
|
|
48
|
+
|
|
47
49
|
```typescript
|
|
48
50
|
@Get('events')
|
|
49
51
|
async streamEvents(@Res() res: FrameworkResponse) {
|
|
@@ -71,9 +73,11 @@ const adapter = createExpressAdapter(
|
|
|
71
73
|
### 안전한 fallback을 포함한 Native Route Registration
|
|
72
74
|
이제 어댑터는 의미 보존이 가능한 명시적 HTTP 메서드 라우트를 Express Router에 사전 등록하면서도, 실제 요청 처리는 계속 공유 fluo dispatcher를 통해 수행합니다.
|
|
73
75
|
|
|
74
|
-
|
|
76
|
+
의미 보존이 가능한 unversioned route에서는 Express가 미리 고른 descriptor와 params를 공유 dispatcher에 전달하므로 duplicate route matching을 건너뛰면서도 guards, interceptors, observers, body parsing, raw body 캡처, SSE, 오류 응답은 기존과 같은 framework-owned 실행 경로를 유지합니다.
|
|
77
|
+
|
|
78
|
+
어댑터가 native handoff를 붙인 뒤 app middleware가 framework request의 method 또는 path를 rewrite하면 dispatcher는 그 handoff를 stale로 보고 원래 Express match를 재사용하지 않고 rewrite된 요청을 다시 매칭합니다.
|
|
75
79
|
|
|
76
|
-
문서화된 fluo semantics를 바꾸지 않기 위해 `/:id` 와 `/:slug`처럼 shape가 겹치는 파라미터 라우트, `@All(...)` 핸들러, `OPTIONS` 소유권, 그리고 duplicate slash/trailing slash 정규화에 의존하는 요청은 catch-all fallback 경로에 남겨둡니다.
|
|
80
|
+
문서화된 fluo semantics를 바꾸지 않기 위해 `/:id` 와 `/:slug`처럼 shape가 겹치는 파라미터 라우트, `@All(...)` 핸들러, `OPTIONS` 소유권, non-URI versioning, 그리고 duplicate slash/trailing slash 정규화에 의존하는 요청은 catch-all fallback 경로에 남겨둡니다.
|
|
77
81
|
|
|
78
82
|
## 어댑터 계약
|
|
79
83
|
|
|
@@ -82,6 +86,7 @@ const adapter = createExpressAdapter(
|
|
|
82
86
|
- **OPTIONS 소유권 parity**: 어댑터는 native route에 대해 Express Router가 `OPTIONS`를 자동 응답하지 못하게 막아, 미지원 메서드도 계속 fluo dispatcher semantics로 흘러가고 `@All(...)` 핸들러가 정의된 경우 `OPTIONS`도 그대로 소유할 수 있게 합니다.
|
|
83
87
|
- **경로 정규화 parity**: duplicate slash 변형처럼 Express Router와 fluo의 정규화 방식이 다를 수 있는 요청도 fallback dispatch를 통해 fluo의 normalized route contract를 유지합니다.
|
|
84
88
|
- **버저닝 parity**: Express Router가 최초 path match를 하더라도 header/media-type/custom version 선택은 계속 dispatcher가 최종 결정합니다.
|
|
89
|
+
- **Middleware rewrite parity**: App middleware가 method/path를 rewrite하면 native handoff는 무효화되고 rewrite된 요청을 기준으로 다시 매칭합니다.
|
|
85
90
|
|
|
86
91
|
## 공개 API 개요
|
|
87
92
|
|
package/README.md
CHANGED
|
@@ -44,6 +44,8 @@ await app.listen();
|
|
|
44
44
|
### Handling Streaming Responses (SSE)
|
|
45
45
|
The Express adapter supports Server-Sent Events (SSE) via the shared `SseResponse` utility, abstracting away the Express-specific stream handling.
|
|
46
46
|
|
|
47
|
+
Express-backed response streams also honor the shared fluo backpressure contract: `response.stream.waitForDrain()` settles on `drain`, `close`, or `error`, so streaming writers do not hang when clients disconnect before backpressure clears.
|
|
48
|
+
|
|
47
49
|
```typescript
|
|
48
50
|
@Get('events')
|
|
49
51
|
async streamEvents(@Res() res: FrameworkResponse) {
|
|
@@ -71,9 +73,11 @@ const adapter = createExpressAdapter(
|
|
|
71
73
|
### Native Route Registration with Safe Fallback
|
|
72
74
|
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
75
|
|
|
74
|
-
|
|
76
|
+
For semantically safe unversioned routes, Express hands the pre-matched descriptor and params to the shared dispatcher so duplicate route matching is skipped while guards, interceptors, observers, body parsing, raw body capture, SSE, and error responses stay on the same framework-owned execution path.
|
|
77
|
+
|
|
78
|
+
If app middleware rewrites the framework request method or path after the adapter attaches a native handoff, the dispatcher treats that handoff as stale and rematches the rewritten request instead of reusing the original Express match.
|
|
75
79
|
|
|
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.
|
|
80
|
+
To avoid changing documented fluo semantics, overlapping same-shape param routes such as `/:id` and `/:slug`, `@All(...)` handlers, `OPTIONS` ownership, non-URI versioning, and requests that rely on fluo's duplicate-slash/trailing-slash normalization stay on the catch-all fallback path.
|
|
77
81
|
|
|
78
82
|
## Adapter Contract
|
|
79
83
|
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,KAAK,aAAa,IAAI,kBAAkB,EACzC,MAAM,YAAY,CAAC;AAWpB,OAAO,EAOL,KAAK,WAAW,EAChB,KAAK,UAAU,EAKf,KAAK,sBAAsB,
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/adapter.js
CHANGED
|
@@ -4,6 +4,7 @@ import { Readable } from 'node:stream';
|
|
|
4
4
|
import { URL } from 'node:url';
|
|
5
5
|
import express from 'express';
|
|
6
6
|
import { BadRequestException, createServerBackedHttpAdapterRealtimeCapability, createErrorResponse, HttpException, InternalServerErrorException, PayloadTooLargeException } from '@fluojs/http';
|
|
7
|
+
import { attachFrameworkRequestNativeRouteHandoff, bindRawRequestNativeRouteHandoff, consumeRawRequestNativeRouteHandoff, isRoutePathNormalizationSensitive } from '@fluojs/http/internal';
|
|
7
8
|
import { createNodeShutdownSignalRegistration, defaultNodeShutdownSignals } from '@fluojs/runtime/node';
|
|
8
9
|
import { parseMultipart } from '@fluojs/runtime/web';
|
|
9
10
|
import { bootstrapHttpAdapterApplication, runHttpAdapterApplication } from '@fluojs/runtime/internal/http-adapter';
|
|
@@ -129,6 +130,16 @@ export class ExpressHttpApplicationAdapter {
|
|
|
129
130
|
next();
|
|
130
131
|
return;
|
|
131
132
|
}
|
|
133
|
+
const nativeMethod = request.method.toUpperCase();
|
|
134
|
+
const requestPath = new URL(request.originalUrl || request.url || '/', 'http://localhost').pathname;
|
|
135
|
+
const descriptor = route.descriptorsByMethod[nativeMethod];
|
|
136
|
+
const params = normalizeNativeRouteParams(request.params);
|
|
137
|
+
if (descriptor && !isRoutePathNormalizationSensitive(requestPath) && !hasNativeRouteParamSeparators(params)) {
|
|
138
|
+
bindRawRequestNativeRouteHandoff(request, {
|
|
139
|
+
descriptor,
|
|
140
|
+
params
|
|
141
|
+
});
|
|
142
|
+
}
|
|
132
143
|
void this.handleRequest(request, response);
|
|
133
144
|
});
|
|
134
145
|
}
|
|
@@ -150,38 +161,46 @@ function resolveDispatcherRouteDescriptors(dispatcher) {
|
|
|
150
161
|
function createExpressNativeRoutes(descriptors) {
|
|
151
162
|
const candidates = new Map();
|
|
152
163
|
const shapePaths = new Map();
|
|
164
|
+
const versionSensitiveRouteKeys = collectVersionSensitiveRouteKeys(descriptors);
|
|
153
165
|
for (const descriptor of descriptors) {
|
|
154
|
-
if (descriptor.route.method
|
|
166
|
+
if (!isExpressNativeRouteDescriptor(descriptor) || versionSensitiveRouteKeys.has(`${descriptor.route.method}:${descriptor.route.path}`)) {
|
|
155
167
|
continue;
|
|
156
168
|
}
|
|
157
|
-
registerExpressNativeRouteCandidate(candidates, shapePaths, descriptor
|
|
169
|
+
registerExpressNativeRouteCandidate(candidates, shapePaths, descriptor);
|
|
158
170
|
}
|
|
159
171
|
const routesByPath = new Map();
|
|
160
172
|
for (const candidate of candidates.values()) {
|
|
161
173
|
if (shapePaths.get(candidate.shapeKey)?.size !== 1) {
|
|
162
174
|
continue;
|
|
163
175
|
}
|
|
164
|
-
let
|
|
165
|
-
if (!
|
|
166
|
-
|
|
167
|
-
|
|
176
|
+
let route = routesByPath.get(candidate.path);
|
|
177
|
+
if (!route) {
|
|
178
|
+
route = {
|
|
179
|
+
descriptorsByMethod: {},
|
|
180
|
+
methods: new Set()
|
|
181
|
+
};
|
|
182
|
+
routesByPath.set(candidate.path, route);
|
|
168
183
|
}
|
|
169
|
-
methods.add(candidate.method);
|
|
184
|
+
route.methods.add(candidate.method);
|
|
185
|
+
route.descriptorsByMethod[candidate.method] = candidate.descriptor;
|
|
170
186
|
}
|
|
171
|
-
return [...routesByPath.entries()].map(([path,
|
|
172
|
-
|
|
187
|
+
return [...routesByPath.entries()].map(([path, route]) => ({
|
|
188
|
+
descriptorsByMethod: route.descriptorsByMethod,
|
|
189
|
+
methods: [...route.methods],
|
|
173
190
|
path
|
|
174
191
|
}));
|
|
175
192
|
}
|
|
176
|
-
function
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const nativeMethod = method;
|
|
193
|
+
function isExpressNativeRouteDescriptor(descriptor) {
|
|
194
|
+
return descriptor.route.method !== 'ALL' && EXPRESS_NATIVE_ROUTE_METHODS.includes(descriptor.route.method) && descriptor.route.version === undefined;
|
|
195
|
+
}
|
|
196
|
+
function registerExpressNativeRouteCandidate(candidates, shapePaths, descriptor) {
|
|
197
|
+
const nativeMethod = descriptor.route.method;
|
|
198
|
+
const path = descriptor.route.path;
|
|
181
199
|
const routeKey = `${nativeMethod}:${path}`;
|
|
182
200
|
const shapeKey = `${nativeMethod}:${canonicalizeExpressRouteShape(path)}`;
|
|
183
201
|
if (!candidates.has(routeKey)) {
|
|
184
202
|
candidates.set(routeKey, {
|
|
203
|
+
descriptor,
|
|
185
204
|
method: nativeMethod,
|
|
186
205
|
path,
|
|
187
206
|
shapeKey
|
|
@@ -281,6 +300,18 @@ function createFrameworkResponse(response) {
|
|
|
281
300
|
this.committed = true;
|
|
282
301
|
response.send(serialized.payload);
|
|
283
302
|
},
|
|
303
|
+
async sendSimpleJson(body) {
|
|
304
|
+
if (response.writableEnded) {
|
|
305
|
+
this.committed = true;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
const serialized = serializeResponseBody(body);
|
|
309
|
+
if (!response.hasHeader('content-type') && serialized.defaultContentType) {
|
|
310
|
+
response.setHeader('content-type', serialized.defaultContentType);
|
|
311
|
+
}
|
|
312
|
+
this.committed = true;
|
|
313
|
+
response.send(serialized.payload);
|
|
314
|
+
},
|
|
284
315
|
setHeader(name, value) {
|
|
285
316
|
const lowerName = name.toLowerCase();
|
|
286
317
|
if (lowerName === 'set-cookie') {
|
|
@@ -321,11 +352,19 @@ function createFrameworkResponseStream(response) {
|
|
|
321
352
|
};
|
|
322
353
|
},
|
|
323
354
|
waitForDrain() {
|
|
324
|
-
if (response.writableEnded) {
|
|
355
|
+
if (response.writableEnded || response.destroyed) {
|
|
325
356
|
return Promise.resolve();
|
|
326
357
|
}
|
|
327
358
|
return new Promise(resolve => {
|
|
328
|
-
|
|
359
|
+
const settle = () => {
|
|
360
|
+
response.removeListener('drain', settle);
|
|
361
|
+
response.removeListener('close', settle);
|
|
362
|
+
response.removeListener('error', settle);
|
|
363
|
+
resolve();
|
|
364
|
+
};
|
|
365
|
+
response.once('drain', settle);
|
|
366
|
+
response.once('close', settle);
|
|
367
|
+
response.once('error', settle);
|
|
329
368
|
});
|
|
330
369
|
},
|
|
331
370
|
write(chunk) {
|
|
@@ -372,7 +411,34 @@ async function createFrameworkRequest(request, signal, multipartOptions, maxBody
|
|
|
372
411
|
if (rawBody) {
|
|
373
412
|
frameworkRequest.rawBody = rawBody;
|
|
374
413
|
}
|
|
375
|
-
|
|
414
|
+
const nativeRouteHandoff = consumeRawRequestNativeRouteHandoff(request);
|
|
415
|
+
return nativeRouteHandoff ? attachFrameworkRequestNativeRouteHandoff(frameworkRequest, nativeRouteHandoff) : frameworkRequest;
|
|
416
|
+
}
|
|
417
|
+
function normalizeNativeRouteParams(params) {
|
|
418
|
+
if (typeof params !== 'object' || params === null) {
|
|
419
|
+
return {};
|
|
420
|
+
}
|
|
421
|
+
return Object.fromEntries(Object.entries(params).flatMap(([key, value]) => typeof value === 'string' ? [[key, value]] : value === undefined ? [] : [[key, String(value)]]));
|
|
422
|
+
}
|
|
423
|
+
function hasNativeRouteParamSeparators(params) {
|
|
424
|
+
return Object.values(params).some(value => value.includes('/'));
|
|
425
|
+
}
|
|
426
|
+
function collectVersionSensitiveRouteKeys(descriptors) {
|
|
427
|
+
const grouped = new Map();
|
|
428
|
+
for (const descriptor of descriptors) {
|
|
429
|
+
if (!EXPRESS_NATIVE_ROUTE_METHODS.includes(descriptor.route.method)) {
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
const routeKey = `${descriptor.route.method}:${descriptor.route.path}`;
|
|
433
|
+
const current = grouped.get(routeKey) ?? {
|
|
434
|
+
count: 0,
|
|
435
|
+
hasVersioned: false
|
|
436
|
+
};
|
|
437
|
+
current.count += 1;
|
|
438
|
+
current.hasVersioned ||= descriptor.route.version !== undefined;
|
|
439
|
+
grouped.set(routeKey, current);
|
|
440
|
+
}
|
|
441
|
+
return new Set([...grouped.entries()].filter(([, current]) => current.count > 1 || current.hasVersioned).map(([routeKey]) => routeKey));
|
|
376
442
|
}
|
|
377
443
|
async function parseMultipartRequest(request, options = {}) {
|
|
378
444
|
try {
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"platform",
|
|
9
9
|
"server"
|
|
10
10
|
],
|
|
11
|
-
"version": "1.0.0-beta.
|
|
11
|
+
"version": "1.0.0-beta.4",
|
|
12
12
|
"private": false,
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"repository": {
|
|
@@ -36,12 +36,13 @@
|
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"express": "^5.1.0",
|
|
39
|
-
"@fluojs/http": "^1.0.0-beta.
|
|
40
|
-
"@fluojs/runtime": "^1.0.0-beta.
|
|
39
|
+
"@fluojs/http": "^1.0.0-beta.4",
|
|
40
|
+
"@fluojs/runtime": "^1.0.0-beta.5"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/express": "^5.0.3",
|
|
44
|
-
"vitest": "^3.2.4"
|
|
44
|
+
"vitest": "^3.2.4",
|
|
45
|
+
"@fluojs/di": "^1.0.0-beta.5"
|
|
45
46
|
},
|
|
46
47
|
"scripts": {
|
|
47
48
|
"prebuild": "node ../../tooling/scripts/clean-dist.mjs",
|