@faapi/faapi 6.11.0 → 6.13.0
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/dist/cli/index.js +95 -38
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +8 -15
- package/dist/index.js +80 -33
- package/dist/index.js.map +1 -1
- package/dist/{routeTypes-m9wZHWyo.d.ts → routeTypes-CVPV8Yd3.d.ts} +3 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +79 -29
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
|
@@ -752,7 +752,9 @@ type TaskFailedHandler = (info: TaskFailedInfo) => Promise<void> | void;
|
|
|
752
752
|
*
|
|
753
753
|
* 设计要点:
|
|
754
754
|
* - writer 内部用 ReadableStream + TextEncoder,send 时 enqueue,close 时 close controller
|
|
755
|
-
* - response 预设 text/event-stream、no-cache、keep-alive 头,状态码默认 200
|
|
755
|
+
* - response 预设 text/event-stream、no-cache、keep-alive、X-Accel-Buffering: no 头,状态码默认 200
|
|
756
|
+
* (X-Accel-Buffering: no 让 nginx 按响应跳过 proxy_buffering,SSE 小包不被攒到流结束才下发;
|
|
757
|
+
* 非 nginx 反代不识别该头,当普通自定义头透传)
|
|
756
758
|
* - close 后再 send 静默忽略,避免 handler 异步流程中误写已关闭的流
|
|
757
759
|
* - sendError 向流写入 event: error 后关闭,用于流式输出中报错的优雅终止
|
|
758
760
|
*
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as FaapiContext, F as FaapiMiddleware, I as InjectorMap, R as RouteManifest, W as WsRouteManifest, C as CorsOptions, H as HelmetOptions } from './routeTypes-
|
|
1
|
+
import { a as FaapiContext, F as FaapiMiddleware, I as InjectorMap, R as RouteManifest, W as WsRouteManifest, C as CorsOptions, H as HelmetOptions } from './routeTypes-CVPV8Yd3.js';
|
|
2
2
|
import { Server } from 'node:http';
|
|
3
3
|
import { WebSocket } from 'ws';
|
|
4
4
|
|
package/dist/testing.js
CHANGED
|
@@ -1,3 +1,71 @@
|
|
|
1
|
+
// src/utils/isPlainObject.ts
|
|
2
|
+
function isPlainObject(value) {
|
|
3
|
+
if (value === null || typeof value !== "object") {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
if (Array.isArray(value)) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
const proto = Object.getPrototypeOf(value);
|
|
10
|
+
return proto === null || proto === Object.prototype;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/utils/stringifyJson.ts
|
|
14
|
+
function stringifyJson(value) {
|
|
15
|
+
return JSON.stringify(convertValue(value, /* @__PURE__ */ new Set()));
|
|
16
|
+
}
|
|
17
|
+
function convertValue(value, ancestors) {
|
|
18
|
+
if (value === null) return value;
|
|
19
|
+
const type = typeof value;
|
|
20
|
+
if (type === "bigint") return value.toString();
|
|
21
|
+
if (type === "number") return Number.isFinite(value) ? value : String(value);
|
|
22
|
+
if (type !== "object") return value;
|
|
23
|
+
const obj = value;
|
|
24
|
+
if (ancestors.has(obj)) {
|
|
25
|
+
throw new TypeError("[faapi] Converting circular structure to JSON");
|
|
26
|
+
}
|
|
27
|
+
if (obj instanceof Date) return obj.getTime();
|
|
28
|
+
if (obj instanceof Map) {
|
|
29
|
+
ancestors.add(obj);
|
|
30
|
+
const entries = Array.from(obj, ([k, v]) => [
|
|
31
|
+
convertValue(k, ancestors),
|
|
32
|
+
convertValue(v, ancestors)
|
|
33
|
+
]);
|
|
34
|
+
ancestors.delete(obj);
|
|
35
|
+
return entries;
|
|
36
|
+
}
|
|
37
|
+
if (obj instanceof Set) {
|
|
38
|
+
ancestors.add(obj);
|
|
39
|
+
const values = Array.from(obj, (v) => convertValue(v, ancestors));
|
|
40
|
+
ancestors.delete(obj);
|
|
41
|
+
return values;
|
|
42
|
+
}
|
|
43
|
+
if (obj instanceof RegExp) return obj.toString();
|
|
44
|
+
const toJSON = obj.toJSON;
|
|
45
|
+
if (typeof toJSON === "function") {
|
|
46
|
+
ancestors.add(obj);
|
|
47
|
+
const result = convertValue(toJSON.call(obj), ancestors);
|
|
48
|
+
ancestors.delete(obj);
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(obj)) {
|
|
52
|
+
ancestors.add(obj);
|
|
53
|
+
const arr = obj.map((item) => convertValue(item, ancestors));
|
|
54
|
+
ancestors.delete(obj);
|
|
55
|
+
return arr;
|
|
56
|
+
}
|
|
57
|
+
if (isPlainObject(obj)) {
|
|
58
|
+
ancestors.add(obj);
|
|
59
|
+
const out = {};
|
|
60
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
61
|
+
out[key] = convertValue(val, ancestors);
|
|
62
|
+
}
|
|
63
|
+
ancestors.delete(obj);
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
return obj;
|
|
67
|
+
}
|
|
68
|
+
|
|
1
69
|
// src/runtime/sse.ts
|
|
2
70
|
function encodeSseEvent(event) {
|
|
3
71
|
let out = "";
|
|
@@ -24,7 +92,7 @@ function encodeSseEvent(event) {
|
|
|
24
92
|
} else if (event.data === null) {
|
|
25
93
|
dataStr = "null";
|
|
26
94
|
} else {
|
|
27
|
-
dataStr =
|
|
95
|
+
dataStr = stringifyJson(event.data);
|
|
28
96
|
}
|
|
29
97
|
const lines = dataStr.split("\n");
|
|
30
98
|
for (const line of lines) {
|
|
@@ -55,7 +123,9 @@ function createSseWriter() {
|
|
|
55
123
|
headers: {
|
|
56
124
|
"Content-Type": "text/event-stream",
|
|
57
125
|
"Cache-Control": "no-cache",
|
|
58
|
-
Connection: "keep-alive"
|
|
126
|
+
Connection: "keep-alive",
|
|
127
|
+
// nginx 认到该头后按响应跳过 proxy_buffering;其他反代/客户端忽略,无副作用
|
|
128
|
+
"X-Accel-Buffering": "no"
|
|
59
129
|
}
|
|
60
130
|
});
|
|
61
131
|
const writer = {
|
|
@@ -182,7 +252,7 @@ function jsonRaw(body, status, extraHeaders) {
|
|
|
182
252
|
const extra = new Headers(extraHeaders);
|
|
183
253
|
extra.forEach((value, key) => headers.set(key, value));
|
|
184
254
|
}
|
|
185
|
-
return new Response(
|
|
255
|
+
return new Response(stringifyJson(body), { status, headers });
|
|
186
256
|
}
|
|
187
257
|
function wrapOkResult(result, config) {
|
|
188
258
|
if (result instanceof Response) return result;
|
|
@@ -541,18 +611,6 @@ function createTestContext(options) {
|
|
|
541
611
|
return createContext(request, params, config, ip, registries);
|
|
542
612
|
}
|
|
543
613
|
|
|
544
|
-
// src/utils/isPlainObject.ts
|
|
545
|
-
function isPlainObject(value) {
|
|
546
|
-
if (value === null || typeof value !== "object") {
|
|
547
|
-
return false;
|
|
548
|
-
}
|
|
549
|
-
if (Array.isArray(value)) {
|
|
550
|
-
return false;
|
|
551
|
-
}
|
|
552
|
-
const proto = Object.getPrototypeOf(value);
|
|
553
|
-
return proto === null || proto === Object.prototype;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
614
|
// src/response/pendingMeta.ts
|
|
557
615
|
var pending = /* @__PURE__ */ new WeakMap();
|
|
558
616
|
function deferMetaHeaders(response, headers) {
|
|
@@ -632,7 +690,7 @@ async function toResponse(value, meta) {
|
|
|
632
690
|
});
|
|
633
691
|
}
|
|
634
692
|
if (isPlainObject(value) || Array.isArray(value)) {
|
|
635
|
-
const body2 =
|
|
693
|
+
const body2 = stringifyJson(value);
|
|
636
694
|
const headers2 = new Headers({ "Content-Type": "application/json" });
|
|
637
695
|
applyMeta(headers2);
|
|
638
696
|
return new Response(body2, {
|
|
@@ -656,7 +714,7 @@ async function toResponse(value, meta) {
|
|
|
656
714
|
headers: headers2
|
|
657
715
|
});
|
|
658
716
|
}
|
|
659
|
-
const body =
|
|
717
|
+
const body = stringifyJson(value);
|
|
660
718
|
const headers = new Headers({ "Content-Type": "application/json" });
|
|
661
719
|
applyMeta(headers);
|
|
662
720
|
return new Response(body, {
|
|
@@ -1036,9 +1094,6 @@ async function injectParamsAsync(handler, ctx, body, injectors) {
|
|
|
1036
1094
|
function wrapResult(result, ctx) {
|
|
1037
1095
|
return wrapOkResult(result, ctx.config);
|
|
1038
1096
|
}
|
|
1039
|
-
function stashHandlerResult(ctx, result) {
|
|
1040
|
-
ctx.__faapiHandlerResult = result;
|
|
1041
|
-
}
|
|
1042
1097
|
function mergeMeta(response, meta) {
|
|
1043
1098
|
const hasMeta = meta.status !== void 0 || Object.keys(meta.headers).length > 0 || meta.setCookies.length > 0;
|
|
1044
1099
|
if (!hasMeta) return response;
|
|
@@ -1107,9 +1162,7 @@ async function invokeHandler(handler, ctx, body, middlewares, injectors) {
|
|
|
1107
1162
|
const result = await injectParamsAsync(handler, ctx, body, injectors);
|
|
1108
1163
|
const sseResponse = pickSseAndAutoClose();
|
|
1109
1164
|
if (sseResponse) return sseResponse;
|
|
1110
|
-
|
|
1111
|
-
if (!(result instanceof Response)) stashHandlerResult(ctx, result);
|
|
1112
|
-
return response;
|
|
1165
|
+
return toResponse(wrapResult(result, ctx), meta);
|
|
1113
1166
|
} catch (err) {
|
|
1114
1167
|
autoCloseSseOnError();
|
|
1115
1168
|
throw err;
|
|
@@ -1120,9 +1173,7 @@ async function invokeHandler(handler, ctx, body, middlewares, injectors) {
|
|
|
1120
1173
|
const result = await injectParamsAsync(handler, ctx, body, injectors);
|
|
1121
1174
|
const sseResponse = pickSseAndAutoClose();
|
|
1122
1175
|
if (sseResponse) return sseResponse;
|
|
1123
|
-
|
|
1124
|
-
if (!(result instanceof Response)) stashHandlerResult(ctx, result);
|
|
1125
|
-
return response;
|
|
1176
|
+
return toResponse(wrapResult(result, ctx), meta);
|
|
1126
1177
|
} catch (err) {
|
|
1127
1178
|
autoCloseSseOnError();
|
|
1128
1179
|
throw err;
|
|
@@ -2695,7 +2746,7 @@ function baseExpression(type, ctx) {
|
|
|
2695
2746
|
case "union":
|
|
2696
2747
|
return generateUnionExpression(type.members, ctx);
|
|
2697
2748
|
case "date":
|
|
2698
|
-
return 'z.preprocess((v) => (typeof v === "string" ? new Date(v) : v), z.date())';
|
|
2749
|
+
return 'z.preprocess((v) => (typeof v === "string" || typeof v === "number" ? new Date(v) : v), z.date())';
|
|
2699
2750
|
case "record":
|
|
2700
2751
|
return `z.record(${runtimeTypeToZodExpression(type.key, ctx)}, ${runtimeTypeToZodExpression(type.value, ctx)})`;
|
|
2701
2752
|
case "map":
|
|
@@ -4322,7 +4373,7 @@ function buildErrorResponse(err, config) {
|
|
|
4322
4373
|
function wrapWsSocket(rawSocket) {
|
|
4323
4374
|
return {
|
|
4324
4375
|
send(data) {
|
|
4325
|
-
const payload = typeof data === "string" || Buffer.isBuffer(data) ? data :
|
|
4376
|
+
const payload = typeof data === "string" || Buffer.isBuffer(data) ? data : stringifyJson(data);
|
|
4326
4377
|
rawSocket.send(payload);
|
|
4327
4378
|
},
|
|
4328
4379
|
close(code, reason) {
|
|
@@ -4795,7 +4846,6 @@ async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares,
|
|
|
4795
4846
|
globalInjectors
|
|
4796
4847
|
});
|
|
4797
4848
|
const response = outerMiddlewares.length > 0 ? await compose(outerMiddlewares, ctx, routePipeline) : await routePipeline();
|
|
4798
|
-
res.__faapiHandlerResult = ctx.__faapiHandlerResult;
|
|
4799
4849
|
await sendSuccessResponse(response, res);
|
|
4800
4850
|
} catch (err) {
|
|
4801
4851
|
if (res.destroyed || res.writableEnded) return;
|