@faapi/faapi 6.21.0 → 6.22.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 +35 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +35 -3
- package/dist/index.js.map +1 -1
- package/dist/{routeTypes-gyz4PMh7.d.ts → routeTypes-CXWkRx4W.d.ts} +31 -3
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +35 -3
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
|
@@ -762,7 +762,7 @@ type TaskFailedHandler = (info: TaskFailedInfo) => Promise<void> | void;
|
|
|
762
762
|
*
|
|
763
763
|
* 核心导出:
|
|
764
764
|
* - `encodeSseEvent(event)`:把 SSE 事件对象编码为符合 HTML5 SSE 规范的字符串
|
|
765
|
-
* - `createSseWriter()`:创建一个 SseWriter,封装 ReadableStream + Response,提供 send/close/sendError API
|
|
765
|
+
* - `createSseWriter()`:创建一个 SseWriter,封装 ReadableStream + Response,提供 send/close/onClose/sendError API
|
|
766
766
|
* - `SseWriter`:writer 类型,ctx.sse() 返回此类型
|
|
767
767
|
*
|
|
768
768
|
* 设计要点:
|
|
@@ -803,6 +803,20 @@ interface SseEvent {
|
|
|
803
803
|
/** 注释行(以 : 开头),用于 keep-alive 心跳 */
|
|
804
804
|
comment?: string;
|
|
805
805
|
}
|
|
806
|
+
/**
|
|
807
|
+
* ctx.sse() 的选项
|
|
808
|
+
*/
|
|
809
|
+
interface SseOptions {
|
|
810
|
+
/**
|
|
811
|
+
* 声明流生命周期独立于 handler 返回(长连接订阅模式)
|
|
812
|
+
*
|
|
813
|
+
* `true` 时 handler 返回后框架不自动 close——handler 只负责把连接挂到推送源
|
|
814
|
+
* (事件总线、change stream),注册完立即返回,连接长存直到显式 close()、
|
|
815
|
+
* 客户端断开或 handler 抛错兜底。清理推送源用 `onClose(callback)`。
|
|
816
|
+
* 默认 false(handler 返回时框架自动 close 兜底)。
|
|
817
|
+
*/
|
|
818
|
+
keepOpen?: boolean;
|
|
819
|
+
}
|
|
806
820
|
/**
|
|
807
821
|
* SSE writer:封装流式推送 API
|
|
808
822
|
*
|
|
@@ -827,10 +841,20 @@ interface SseWriter {
|
|
|
827
841
|
sendError(error: unknown): void;
|
|
828
842
|
/** 关闭流(多次调用安全) */
|
|
829
843
|
close(): void;
|
|
844
|
+
/**
|
|
845
|
+
* 注册流结束回调
|
|
846
|
+
*
|
|
847
|
+
* 显式 close、框架兜底 close、客户端断开、sendError 任一路径结束流时恰好触发
|
|
848
|
+
* 一次(多个回调按注册顺序执行,回调自身抛错被忽略);注册时流已结束则立即触发。
|
|
849
|
+
* keepOpen 模式下用于退订/清理推送源,防止客户端断开后订阅随重连累积泄漏。
|
|
850
|
+
*/
|
|
851
|
+
onClose(callback: () => void): void;
|
|
830
852
|
/** 流是否已关闭(handler 主动 close 或框架自动 close) */
|
|
831
853
|
readonly closed: boolean;
|
|
832
854
|
/** 客户端是否已断开(ReadableStream 被 cancel) */
|
|
833
855
|
readonly aborted: boolean;
|
|
856
|
+
/** 创建时是否声明了 `{ keepOpen: true }`(handler 返回后框架不自动 close) */
|
|
857
|
+
readonly keepOpen: boolean;
|
|
834
858
|
/**
|
|
835
859
|
* 流缓冲背压状态(透传 controller.desiredSize)
|
|
836
860
|
*
|
|
@@ -1061,8 +1085,12 @@ interface FaapiContext {
|
|
|
1061
1085
|
* sse.close();
|
|
1062
1086
|
* }
|
|
1063
1087
|
* ```
|
|
1088
|
+
*
|
|
1089
|
+
* @param options `{ keepOpen: true }` 声明流生命周期独立于 handler 返回(长连接
|
|
1090
|
+
* 订阅模式):handler 返回后框架不自动 close,连接长存直到显式 close()、客户端
|
|
1091
|
+
* 断开或 handler 抛错兜底;清理推送源用 `writer.onClose(callback)`。默认 false。
|
|
1064
1092
|
*/
|
|
1065
|
-
sse(): SseWriter;
|
|
1093
|
+
sse(options?: SseOptions): SseWriter;
|
|
1066
1094
|
/**
|
|
1067
1095
|
* 显式包装成功响应(返回 Response 对象)
|
|
1068
1096
|
*
|
|
@@ -1313,4 +1341,4 @@ interface RouteInfo {
|
|
|
1313
1341
|
output: RouteOutputSchema | null;
|
|
1314
1342
|
}
|
|
1315
1343
|
|
|
1316
|
-
export { type
|
|
1344
|
+
export { type ToolPathMeta as $, type AppRegistries as A, type RouteOutputSchema as B, type CorsOptions as C, type RouteParamSchema as D, type SseEvent as E, type FaapiMiddleware as F, type SseOptions as G, type HelmetOptions as H, type InjectorMap as I, type SseWriter as J, type TaskContext as K, type LogConfig as L, type TaskDriverJob as M, type TaskDriverProcess as N, type TaskDriverRecord as O, type TaskFailedInfo as P, type TaskJob as Q, type RouteManifest as R, type SkillRegistry as S, type TaskClient as T, type TaskJobStatus as U, type TaskMetadata as V, type WsRouteManifest as W, type TaskModule as X, type TaskRegistriesSnapshot as Y, type TaskRegistriesView as Z, type ToolCore as _, type FaapiContext as a, type ToolRegistry as a0, cors as a1, createAppRegistries as a2, createTaskRegistriesView as a3, createTaskRegistry as a4, helmet as a5, type TaskFailedHandler as b, type TaskQueueDeps as c, type TaskQueue as d, type TaskDriver as e, type TaskRegistry as f, type TaskManifest as g, type ToolMetadata as h, type AgentCore as i, type AgentMetadata as j, type CreateLoggerOptions as k, type Logger as l, type AgentHandleFactory as m, type AgentHandleStore as n, type AgentPathMeta as o, type AgentRegistry as p, type AgentToolDescriptor as q, type FaapiContextConfig as r, type FaapiTaskMeta as s, type FailOptions as t, type Injector as u, type LogEntry as v, type LogLevel as w, type LogSink as x, type RouteInfo as y, type RouteInputSchema as z };
|
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-CXWkRx4W.js';
|
|
2
2
|
import { Server } from 'node:http';
|
|
3
3
|
import { WebSocket } from 'ws';
|
|
4
4
|
|
package/dist/testing.js
CHANGED
|
@@ -155,13 +155,28 @@ function encodeSseEvent(event) {
|
|
|
155
155
|
return out;
|
|
156
156
|
}
|
|
157
157
|
function createSseWriter(options = {}) {
|
|
158
|
-
const { onFirstWrite } = options;
|
|
158
|
+
const { onFirstWrite, keepOpen = false } = options;
|
|
159
159
|
const encoder = new TextEncoder();
|
|
160
160
|
let controller = null;
|
|
161
161
|
let closed = false;
|
|
162
162
|
let aborted = false;
|
|
163
163
|
let firstWriteTriggered = false;
|
|
164
164
|
let drainWaiters = [];
|
|
165
|
+
let closeCallbacks = [];
|
|
166
|
+
let closeCallbacksFired = false;
|
|
167
|
+
const safeRunCallback = (callback) => {
|
|
168
|
+
try {
|
|
169
|
+
callback();
|
|
170
|
+
} catch {
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
const runCloseCallbacks = () => {
|
|
174
|
+
if (closeCallbacksFired) return;
|
|
175
|
+
closeCallbacksFired = true;
|
|
176
|
+
const callbacks = closeCallbacks;
|
|
177
|
+
closeCallbacks = [];
|
|
178
|
+
for (const callback of callbacks) safeRunCallback(callback);
|
|
179
|
+
};
|
|
165
180
|
const resolveDrainWaiters = () => {
|
|
166
181
|
const waiters = drainWaiters;
|
|
167
182
|
drainWaiters = [];
|
|
@@ -185,6 +200,7 @@ function createSseWriter(options = {}) {
|
|
|
185
200
|
aborted = true;
|
|
186
201
|
closed = true;
|
|
187
202
|
controller = null;
|
|
203
|
+
runCloseCallbacks();
|
|
188
204
|
resolveDrainWaiters();
|
|
189
205
|
}
|
|
190
206
|
},
|
|
@@ -227,6 +243,7 @@ function createSseWriter(options = {}) {
|
|
|
227
243
|
close() {
|
|
228
244
|
if (closed) return;
|
|
229
245
|
closed = true;
|
|
246
|
+
runCloseCallbacks();
|
|
230
247
|
resolveDrainWaiters();
|
|
231
248
|
if (controller) {
|
|
232
249
|
try {
|
|
@@ -236,6 +253,13 @@ function createSseWriter(options = {}) {
|
|
|
236
253
|
controller = null;
|
|
237
254
|
}
|
|
238
255
|
},
|
|
256
|
+
onClose(callback) {
|
|
257
|
+
if (closeCallbacksFired) {
|
|
258
|
+
safeRunCallback(callback);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
closeCallbacks.push(callback);
|
|
262
|
+
},
|
|
239
263
|
get closed() {
|
|
240
264
|
return closed;
|
|
241
265
|
},
|
|
@@ -252,6 +276,9 @@ function createSseWriter(options = {}) {
|
|
|
252
276
|
get aborted() {
|
|
253
277
|
return aborted;
|
|
254
278
|
},
|
|
279
|
+
get keepOpen() {
|
|
280
|
+
return keepOpen;
|
|
281
|
+
},
|
|
255
282
|
get response() {
|
|
256
283
|
return response;
|
|
257
284
|
}
|
|
@@ -633,10 +660,15 @@ function createContextFromUrl(request, url, params, config = {}, ip = "", regist
|
|
|
633
660
|
* 接入底层连接——流式期间字节即产即达(见 createServer.md「SSE 提前接管」)。
|
|
634
661
|
* 无钩子场景(app.inject / 测试直调 invokeHandler)回落为 handler 返回后交出。
|
|
635
662
|
*
|
|
663
|
+
* `{ keepOpen: true }` 声明流生命周期独立于 handler 返回:handler 返回后框架
|
|
664
|
+
* 不自动 close(长连接订阅模式,清理推送源用 writer.onClose);handler 抛错
|
|
665
|
+
* 的兜底 close 不受影响。详见 runtime/sse.md「长连接订阅模式」。
|
|
666
|
+
*
|
|
636
667
|
* 与 ctx.json / ctx.html 互斥:一个 handler 只能用一种响应方式。
|
|
637
668
|
*/
|
|
638
|
-
sse() {
|
|
669
|
+
sse(options) {
|
|
639
670
|
const writer = createSseWriter({
|
|
671
|
+
keepOpen: options?.keepOpen,
|
|
640
672
|
onFirstWrite: (response) => {
|
|
641
673
|
ctx.__earlyRespond?.(response);
|
|
642
674
|
}
|
|
@@ -1265,7 +1297,7 @@ async function invokeHandler(handler, ctx, body, middlewares, injectors) {
|
|
|
1265
1297
|
const pickSseAndAutoClose = () => {
|
|
1266
1298
|
const sseWriter = ctx.__sseWriter;
|
|
1267
1299
|
if (!sseWriter) return null;
|
|
1268
|
-
if (!sseWriter.closed && !sseWriter.aborted) {
|
|
1300
|
+
if (!sseWriter.keepOpen && !sseWriter.closed && !sseWriter.aborted) {
|
|
1269
1301
|
sseWriter.close();
|
|
1270
1302
|
}
|
|
1271
1303
|
return mergeMeta(sseWriter.response, meta);
|