@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
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FaapiMiddleware, A as AppRegistries, R as RouteManifest, a as FaapiContext, C as CorsOptions, T as TaskClient, H as HelmetOptions, L as LogConfig, I as InjectorMap, b as TaskFailedHandler, c as TaskQueueDeps, d as TaskQueue, e as TaskDriver, f as TaskRegistry, g as TaskManifest, h as ToolMetadata, i as AgentCore, j as AgentMetadata, k as CreateLoggerOptions, l as Logger, W as WsRouteManifest } from './routeTypes-
|
|
2
|
-
export { m as AgentHandleFactory, n as AgentHandleStore, o as AgentPathMeta, p as AgentRegistry, q as AgentToolDescriptor, r as FaapiContextConfig, s as FaapiTaskMeta, t as FailOptions, u as Injector, v as LogEntry, w as LogLevel, x as LogSink, y as RouteInfo, z as RouteInputSchema, B as RouteOutputSchema, D as RouteParamSchema, S as SkillRegistry, E as SseEvent, G as SseWriter, J as TaskContext, K as TaskDriverJob, M as TaskDriverProcess, N as TaskDriverRecord, O as TaskFailedInfo, P as TaskJob, Q as TaskJobStatus, U as TaskMetadata, V as TaskModule, X as TaskRegistriesSnapshot, Y as TaskRegistriesView, Z as ToolCore, _ as ToolPathMeta, $ as ToolRegistry, a0 as cors, a1 as createAppRegistries, a2 as createTaskRegistriesView, a3 as createTaskRegistry, a4 as helmet } from './routeTypes-
|
|
1
|
+
import { F as FaapiMiddleware, A as AppRegistries, R as RouteManifest, a as FaapiContext, C as CorsOptions, T as TaskClient, H as HelmetOptions, L as LogConfig, I as InjectorMap, b as TaskFailedHandler, c as TaskQueueDeps, d as TaskQueue, e as TaskDriver, f as TaskRegistry, g as TaskManifest, h as ToolMetadata, i as AgentCore, j as AgentMetadata, k as CreateLoggerOptions, l as Logger, W as WsRouteManifest } from './routeTypes-CVPV8Yd3.js';
|
|
2
|
+
export { m as AgentHandleFactory, n as AgentHandleStore, o as AgentPathMeta, p as AgentRegistry, q as AgentToolDescriptor, r as FaapiContextConfig, s as FaapiTaskMeta, t as FailOptions, u as Injector, v as LogEntry, w as LogLevel, x as LogSink, y as RouteInfo, z as RouteInputSchema, B as RouteOutputSchema, D as RouteParamSchema, S as SkillRegistry, E as SseEvent, G as SseWriter, J as TaskContext, K as TaskDriverJob, M as TaskDriverProcess, N as TaskDriverRecord, O as TaskFailedInfo, P as TaskJob, Q as TaskJobStatus, U as TaskMetadata, V as TaskModule, X as TaskRegistriesSnapshot, Y as TaskRegistriesView, Z as ToolCore, _ as ToolPathMeta, $ as ToolRegistry, a0 as cors, a1 as createAppRegistries, a2 as createTaskRegistriesView, a3 as createTaskRegistry, a4 as helmet } from './routeTypes-CVPV8Yd3.js';
|
|
3
3
|
import * as node_http from 'node:http';
|
|
4
4
|
import { Server, IncomingMessage, ServerResponse } from 'node:http';
|
|
5
5
|
import { Socket } from 'node:net';
|
|
@@ -1622,18 +1622,12 @@ interface InjectOptions {
|
|
|
1622
1622
|
interface InjectResponse {
|
|
1623
1623
|
status: number;
|
|
1624
1624
|
headers: Headers;
|
|
1625
|
-
/** 响应字节按 JSON 反序列化的结果(非 JSON 回退字符串)——HTTP 语义,与真实客户端一致 */
|
|
1626
|
-
body: unknown;
|
|
1627
1625
|
/**
|
|
1628
|
-
*
|
|
1629
|
-
*
|
|
1630
|
-
*
|
|
1631
|
-
* 快路径判断用 `res.raw !== undefined`);handler 返回 Response 形态
|
|
1632
|
-
* (ctx.ok/ctx.fail/ctx.json)、抛错、中间件拦截、SSE、404/405/校验失败等
|
|
1633
|
-
* handler 未产生数据返回值的场景为 undefined。语义详见 createAppCore.md
|
|
1634
|
-
* 「inject 的 raw 字段」节。
|
|
1626
|
+
* 响应字节按 JSON 反序列化的结果(非 JSON 回退字符串)——HTTP 语义,与真实客户端一致。
|
|
1627
|
+
* 序列化契约(Date → 毫秒时间戳、BigInt → 字符串等,JSON 原生类型之外全部转换)
|
|
1628
|
+
* 见 `src/utils/stringifyJson.md`,与真实 HTTP 响应完全相同
|
|
1635
1629
|
*/
|
|
1636
|
-
|
|
1630
|
+
body: unknown;
|
|
1637
1631
|
}
|
|
1638
1632
|
/**
|
|
1639
1633
|
* 获取当前 faapi app 单例
|
|
@@ -1693,9 +1687,8 @@ interface AppBase {
|
|
|
1693
1687
|
* 无服务器测试注入
|
|
1694
1688
|
*
|
|
1695
1689
|
* 构建一个模拟请求直接走完整请求链路(CORS / helmet / logger / 全局中间件 / 路由匹配 /
|
|
1696
|
-
* schema 校验 / 目录中间件 / handler),不绑定端口,返回已解析的
|
|
1697
|
-
*
|
|
1698
|
-
* raw 为 handler 原始返回值(同进程取数类型保真,语义见 InjectResponse.raw)。
|
|
1690
|
+
* schema 校验 / 目录中间件 / handler),不绑定端口,返回已解析的 `{ status, headers, body }`
|
|
1691
|
+
* ——body 为 JSON 反序列化结果,与真实 HTTP 客户端拿到的一致(同一序列化契约)。
|
|
1699
1692
|
*
|
|
1700
1693
|
* `listen()` 前后均可调用——`listen()` 后调用常用于 Next.js Server Component 等同进程场景
|
|
1701
1694
|
* (配合 `getApp()` 拿到 app 实例)。
|
package/dist/index.js
CHANGED
|
@@ -1388,7 +1388,7 @@ function baseExpression(type, ctx) {
|
|
|
1388
1388
|
case "union":
|
|
1389
1389
|
return generateUnionExpression(type.members, ctx);
|
|
1390
1390
|
case "date":
|
|
1391
|
-
return 'z.preprocess((v) => (typeof v === "string" ? new Date(v) : v), z.date())';
|
|
1391
|
+
return 'z.preprocess((v) => (typeof v === "string" || typeof v === "number" ? new Date(v) : v), z.date())';
|
|
1392
1392
|
case "record":
|
|
1393
1393
|
return `z.record(${runtimeTypeToZodExpression(type.key, ctx)}, ${runtimeTypeToZodExpression(type.value, ctx)})`;
|
|
1394
1394
|
case "map":
|
|
@@ -4508,6 +4508,74 @@ async function loadRouteModule(filePath, method, rootDir) {
|
|
|
4508
4508
|
return { handler, method };
|
|
4509
4509
|
}
|
|
4510
4510
|
|
|
4511
|
+
// src/utils/isPlainObject.ts
|
|
4512
|
+
function isPlainObject(value) {
|
|
4513
|
+
if (value === null || typeof value !== "object") {
|
|
4514
|
+
return false;
|
|
4515
|
+
}
|
|
4516
|
+
if (Array.isArray(value)) {
|
|
4517
|
+
return false;
|
|
4518
|
+
}
|
|
4519
|
+
const proto = Object.getPrototypeOf(value);
|
|
4520
|
+
return proto === null || proto === Object.prototype;
|
|
4521
|
+
}
|
|
4522
|
+
|
|
4523
|
+
// src/utils/stringifyJson.ts
|
|
4524
|
+
function stringifyJson(value) {
|
|
4525
|
+
return JSON.stringify(convertValue(value, /* @__PURE__ */ new Set()));
|
|
4526
|
+
}
|
|
4527
|
+
function convertValue(value, ancestors) {
|
|
4528
|
+
if (value === null) return value;
|
|
4529
|
+
const type = typeof value;
|
|
4530
|
+
if (type === "bigint") return value.toString();
|
|
4531
|
+
if (type === "number") return Number.isFinite(value) ? value : String(value);
|
|
4532
|
+
if (type !== "object") return value;
|
|
4533
|
+
const obj = value;
|
|
4534
|
+
if (ancestors.has(obj)) {
|
|
4535
|
+
throw new TypeError("[faapi] Converting circular structure to JSON");
|
|
4536
|
+
}
|
|
4537
|
+
if (obj instanceof Date) return obj.getTime();
|
|
4538
|
+
if (obj instanceof Map) {
|
|
4539
|
+
ancestors.add(obj);
|
|
4540
|
+
const entries = Array.from(obj, ([k, v]) => [
|
|
4541
|
+
convertValue(k, ancestors),
|
|
4542
|
+
convertValue(v, ancestors)
|
|
4543
|
+
]);
|
|
4544
|
+
ancestors.delete(obj);
|
|
4545
|
+
return entries;
|
|
4546
|
+
}
|
|
4547
|
+
if (obj instanceof Set) {
|
|
4548
|
+
ancestors.add(obj);
|
|
4549
|
+
const values = Array.from(obj, (v) => convertValue(v, ancestors));
|
|
4550
|
+
ancestors.delete(obj);
|
|
4551
|
+
return values;
|
|
4552
|
+
}
|
|
4553
|
+
if (obj instanceof RegExp) return obj.toString();
|
|
4554
|
+
const toJSON = obj.toJSON;
|
|
4555
|
+
if (typeof toJSON === "function") {
|
|
4556
|
+
ancestors.add(obj);
|
|
4557
|
+
const result = convertValue(toJSON.call(obj), ancestors);
|
|
4558
|
+
ancestors.delete(obj);
|
|
4559
|
+
return result;
|
|
4560
|
+
}
|
|
4561
|
+
if (Array.isArray(obj)) {
|
|
4562
|
+
ancestors.add(obj);
|
|
4563
|
+
const arr = obj.map((item) => convertValue(item, ancestors));
|
|
4564
|
+
ancestors.delete(obj);
|
|
4565
|
+
return arr;
|
|
4566
|
+
}
|
|
4567
|
+
if (isPlainObject(obj)) {
|
|
4568
|
+
ancestors.add(obj);
|
|
4569
|
+
const out = {};
|
|
4570
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
4571
|
+
out[key] = convertValue(val, ancestors);
|
|
4572
|
+
}
|
|
4573
|
+
ancestors.delete(obj);
|
|
4574
|
+
return out;
|
|
4575
|
+
}
|
|
4576
|
+
return obj;
|
|
4577
|
+
}
|
|
4578
|
+
|
|
4511
4579
|
// src/runtime/sse.ts
|
|
4512
4580
|
function encodeSseEvent(event) {
|
|
4513
4581
|
let out = "";
|
|
@@ -4534,7 +4602,7 @@ function encodeSseEvent(event) {
|
|
|
4534
4602
|
} else if (event.data === null) {
|
|
4535
4603
|
dataStr = "null";
|
|
4536
4604
|
} else {
|
|
4537
|
-
dataStr =
|
|
4605
|
+
dataStr = stringifyJson(event.data);
|
|
4538
4606
|
}
|
|
4539
4607
|
const lines = dataStr.split("\n");
|
|
4540
4608
|
for (const line of lines) {
|
|
@@ -4565,7 +4633,9 @@ function createSseWriter() {
|
|
|
4565
4633
|
headers: {
|
|
4566
4634
|
"Content-Type": "text/event-stream",
|
|
4567
4635
|
"Cache-Control": "no-cache",
|
|
4568
|
-
Connection: "keep-alive"
|
|
4636
|
+
Connection: "keep-alive",
|
|
4637
|
+
// nginx 认到该头后按响应跳过 proxy_buffering;其他反代/客户端忽略,无副作用
|
|
4638
|
+
"X-Accel-Buffering": "no"
|
|
4569
4639
|
}
|
|
4570
4640
|
});
|
|
4571
4641
|
const writer = {
|
|
@@ -4640,7 +4710,7 @@ function jsonRaw(body, status, extraHeaders) {
|
|
|
4640
4710
|
const extra = new Headers(extraHeaders);
|
|
4641
4711
|
extra.forEach((value, key) => headers.set(key, value));
|
|
4642
4712
|
}
|
|
4643
|
-
return new Response(
|
|
4713
|
+
return new Response(stringifyJson(body), { status, headers });
|
|
4644
4714
|
}
|
|
4645
4715
|
function wrapOkResult(result, config) {
|
|
4646
4716
|
if (result instanceof Response) return result;
|
|
@@ -4966,18 +5036,6 @@ async function resolveBodyForQueryMethod(request) {
|
|
|
4966
5036
|
return result.data;
|
|
4967
5037
|
}
|
|
4968
5038
|
|
|
4969
|
-
// src/utils/isPlainObject.ts
|
|
4970
|
-
function isPlainObject(value) {
|
|
4971
|
-
if (value === null || typeof value !== "object") {
|
|
4972
|
-
return false;
|
|
4973
|
-
}
|
|
4974
|
-
if (Array.isArray(value)) {
|
|
4975
|
-
return false;
|
|
4976
|
-
}
|
|
4977
|
-
const proto = Object.getPrototypeOf(value);
|
|
4978
|
-
return proto === null || proto === Object.prototype;
|
|
4979
|
-
}
|
|
4980
|
-
|
|
4981
5039
|
// src/response/pendingMeta.ts
|
|
4982
5040
|
var pending = /* @__PURE__ */ new WeakMap();
|
|
4983
5041
|
function deferMetaHeaders(response, headers) {
|
|
@@ -5057,7 +5115,7 @@ async function toResponse(value, meta) {
|
|
|
5057
5115
|
});
|
|
5058
5116
|
}
|
|
5059
5117
|
if (isPlainObject(value) || Array.isArray(value)) {
|
|
5060
|
-
const body2 =
|
|
5118
|
+
const body2 = stringifyJson(value);
|
|
5061
5119
|
const headers2 = new Headers({ "Content-Type": "application/json" });
|
|
5062
5120
|
applyMeta(headers2);
|
|
5063
5121
|
return new Response(body2, {
|
|
@@ -5081,7 +5139,7 @@ async function toResponse(value, meta) {
|
|
|
5081
5139
|
headers: headers2
|
|
5082
5140
|
});
|
|
5083
5141
|
}
|
|
5084
|
-
const body =
|
|
5142
|
+
const body = stringifyJson(value);
|
|
5085
5143
|
const headers = new Headers({ "Content-Type": "application/json" });
|
|
5086
5144
|
applyMeta(headers);
|
|
5087
5145
|
return new Response(body, {
|
|
@@ -5164,9 +5222,6 @@ async function injectParamsAsync(handler, ctx, body, injectors) {
|
|
|
5164
5222
|
function wrapResult(result, ctx) {
|
|
5165
5223
|
return wrapOkResult(result, ctx.config);
|
|
5166
5224
|
}
|
|
5167
|
-
function stashHandlerResult(ctx, result) {
|
|
5168
|
-
ctx.__faapiHandlerResult = result;
|
|
5169
|
-
}
|
|
5170
5225
|
function mergeMeta(response, meta) {
|
|
5171
5226
|
const hasMeta = meta.status !== void 0 || Object.keys(meta.headers).length > 0 || meta.setCookies.length > 0;
|
|
5172
5227
|
if (!hasMeta) return response;
|
|
@@ -5235,9 +5290,7 @@ async function invokeHandler(handler, ctx, body, middlewares, injectors) {
|
|
|
5235
5290
|
const result = await injectParamsAsync(handler, ctx, body, injectors);
|
|
5236
5291
|
const sseResponse = pickSseAndAutoClose();
|
|
5237
5292
|
if (sseResponse) return sseResponse;
|
|
5238
|
-
|
|
5239
|
-
if (!(result instanceof Response)) stashHandlerResult(ctx, result);
|
|
5240
|
-
return response;
|
|
5293
|
+
return toResponse(wrapResult(result, ctx), meta);
|
|
5241
5294
|
} catch (err) {
|
|
5242
5295
|
autoCloseSseOnError();
|
|
5243
5296
|
throw err;
|
|
@@ -5248,9 +5301,7 @@ async function invokeHandler(handler, ctx, body, middlewares, injectors) {
|
|
|
5248
5301
|
const result = await injectParamsAsync(handler, ctx, body, injectors);
|
|
5249
5302
|
const sseResponse = pickSseAndAutoClose();
|
|
5250
5303
|
if (sseResponse) return sseResponse;
|
|
5251
|
-
|
|
5252
|
-
if (!(result instanceof Response)) stashHandlerResult(ctx, result);
|
|
5253
|
-
return response;
|
|
5304
|
+
return toResponse(wrapResult(result, ctx), meta);
|
|
5254
5305
|
} catch (err) {
|
|
5255
5306
|
autoCloseSseOnError();
|
|
5256
5307
|
throw err;
|
|
@@ -5686,7 +5737,7 @@ async function loadMergedMiddlewares(middlewarePaths) {
|
|
|
5686
5737
|
function wrapWsSocket(rawSocket) {
|
|
5687
5738
|
return {
|
|
5688
5739
|
send(data) {
|
|
5689
|
-
const payload = typeof data === "string" || Buffer.isBuffer(data) ? data :
|
|
5740
|
+
const payload = typeof data === "string" || Buffer.isBuffer(data) ? data : stringifyJson(data);
|
|
5690
5741
|
rawSocket.send(payload);
|
|
5691
5742
|
},
|
|
5692
5743
|
close(code, reason) {
|
|
@@ -6160,7 +6211,6 @@ async function handleRequest(routes, rootDir, dist, req, res, outerMiddlewares,
|
|
|
6160
6211
|
globalInjectors
|
|
6161
6212
|
});
|
|
6162
6213
|
const response = outerMiddlewares.length > 0 ? await compose(outerMiddlewares, ctx, routePipeline) : await routePipeline();
|
|
6163
|
-
res.__faapiHandlerResult = ctx.__faapiHandlerResult;
|
|
6164
6214
|
await sendSuccessResponse(response, res);
|
|
6165
6215
|
} catch (err) {
|
|
6166
6216
|
if (res.destroyed || res.writableEnded) return;
|
|
@@ -7204,10 +7254,7 @@ async function createAppBase(options) {
|
|
|
7204
7254
|
resolve({
|
|
7205
7255
|
status: mockRes.statusCode,
|
|
7206
7256
|
headers: new Headers(mockRes._headers),
|
|
7207
|
-
body: parsed
|
|
7208
|
-
// inject raw 捕获桥(第三跳):handler 原始返回值(handleRequest 从 ctx
|
|
7209
|
-
// 桥接到 res;未捕获时 undefined——语义见 InjectResponse.raw 注释)
|
|
7210
|
-
raw: mockRes.__faapiHandlerResult
|
|
7257
|
+
body: parsed
|
|
7211
7258
|
});
|
|
7212
7259
|
});
|
|
7213
7260
|
const listeners = server.listeners("request");
|