@faapi/faapi 6.9.1 → 6.10.1

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.
@@ -306,24 +306,27 @@ interface CreateLoggerOptions {
306
306
  /**
307
307
  * `config.log` 的对象形状(config 侧另接受 boolean:false 全静默、true 默认)
308
308
  *
309
- * level 解析顺序:显式值 > `LOG_LEVEL` 环境变量 > 模式默认(`dir` 文件模式不过滤,
310
- * 其余模式 `'info'`;非法值启动报错)。`sink` 与 `dir` 互斥,同时配置启动报错。
309
+ * 参考 egg.js 的单管道模型:一条日志管道、两个输出出口(文件/sink 与 console),
310
+ * 各有独立阈值——`level` 管管道出口(不配不过滤),`consoleLevel` 管 console 出口
311
+ * (不配 `'info'`)。非法级别值启动报错。`sink` 与 `dir` 互斥,同时配置启动报错。
311
312
  */
312
313
  interface LogConfig {
313
- /** 实例级级别(覆盖全局 level) */
314
+ /** 管道阈值(文件/sink 收到的条目;不配不过滤,LOG_LEVEL env 同源) */
314
315
  level?: LogLevel;
316
+ /** console 出口阈值(不配 `'info'`;`false` 关闭 console 输出) */
317
+ consoleLevel?: LogLevel | false;
315
318
  /** 自定义输出管道(整体接管,与 `dir` 互斥) */
316
319
  sink?: LogSink;
317
320
  /**
318
321
  * 文件日志目录(egg 风格):配置即启用内置文件管道——默认 `app.log` 全量 +
319
- * `error.log` 仅 error(dup 语义);请求日志自动并入(`accessLog` 缺省 true)
322
+ * `error.log` 仅 error(dup 语义);请求日志自动并入
320
323
  */
321
324
  dir?: string;
322
325
  /** true 时按级别四文件 `debug.log`/`info.log`/`warn.log`/`error.log`(各只含对应级别) */
323
326
  splitByLevel?: boolean;
324
327
  /** 文件模式下是否保留 console 双写(默认 true;false 纯文件) */
325
328
  stdout?: boolean;
326
- /** 请求日志是否并入统一管道;缺省 = `dir` 配置时 true(见 middleware/logger.md) */
329
+ /** 请求日志是否并入管道(默认 true;false 关闭请求日志,不输出) */
327
330
  accessLog?: boolean;
328
331
  }
329
332
 
@@ -1125,34 +1128,6 @@ interface CorsOptions {
1125
1128
  */
1126
1129
  declare function cors(options?: CorsOptions): FaapiMiddleware;
1127
1130
 
1128
- type LoggerFn = (messageOrObj: string | Record<string, unknown>, message?: string) => void;
1129
- interface LoggerOptions {
1130
- /**
1131
- * 自定义日志函数
1132
- *
1133
- * - 传入 `console.log`(默认):纯文本格式 `GET /api/users 200 12ms`
1134
- * - 传入 pino logger:结构化日志 `logger.info({ method, path, status, durationMs }, 'request completed')`
1135
- * - 传入 winston logger:`logger.info('GET /api/users 200 12ms', { method, path })`
1136
- */
1137
- log?: LoggerFn;
1138
- }
1139
- /**
1140
- * 创建请求日志中间件(洋葱模型)
1141
- *
1142
- * 日志格式(文本模式):GET /api/users 200 12ms
1143
- * 错误格式(文本模式):POST /api/users 400 45ms - Error: ...
1144
- *
1145
- * 结构化模式:传入 pino/winston 等 logger 实例时,会自动传递结构化字段。
1146
- *
1147
- * before/after 一体,闭包变量共享开始时间,无需污染 ctx。
1148
- * 错误用 try/catch 捕获,记录后重新抛出(让上层处理)。
1149
- * 成功时从 next() 返回的 Response 读取状态码。
1150
- *
1151
- * log 函数每次请求时读取(options.log ?? defaultLog),运行时替换 console.log 或
1152
- * 切换全局日志配置均会生效。
1153
- */
1154
- declare function logger(options?: LoggerOptions): FaapiMiddleware;
1155
-
1156
1131
  interface HelmetOptions {
1157
1132
  contentSecurityPolicy?: string | false;
1158
1133
  xFrameOptions?: 'DENY' | 'SAMEORIGIN' | false;
@@ -1256,4 +1231,4 @@ interface RouteInfo {
1256
1231
  output: RouteOutputSchema | null;
1257
1232
  }
1258
1233
 
1259
- export { type ToolPathMeta as $, type AppRegistries as A, type RouteInputSchema as B, type CorsOptions as C, type RouteOutputSchema as D, type RouteParamSchema as E, type FaapiContext as F, type SseEvent as G, type HelmetOptions as H, type InjectorMap as I, type SseWriter as J, type TaskContext as K, type LoggerOptions 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 LogConfig as a, type ToolRegistry as a0, cors as a1, createAppRegistries as a2, createTaskRegistriesView as a3, createTaskRegistry as a4, helmet as a5, logger as a6, type FaapiMiddleware as b, type TaskFailedHandler as c, type TaskQueueDeps as d, type TaskQueue as e, type TaskDriver as f, type TaskRegistry as g, type TaskManifest as h, type ToolMetadata as i, type AgentCore as j, type AgentMetadata as k, type CreateLoggerOptions as l, type Logger as m, type AgentHandleFactory as n, type AgentHandleStore as o, type AgentPathMeta as p, type AgentRegistry as q, type AgentToolDescriptor as r, type FaapiContextConfig as s, type FaapiTaskMeta as t, type FailOptions as u, type Injector as v, type LogEntry as w, type LogLevel as x, type LogSink as y, type RouteInfo as z };
1234
+ export { type ToolRegistry 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 SseWriter as G, type HelmetOptions as H, type InjectorMap as I, type TaskContext as J, type TaskDriverJob as K, type LogConfig as L, type TaskDriverProcess as M, type TaskDriverRecord as N, type TaskFailedInfo as O, type TaskJob as P, type TaskJobStatus as Q, type RouteManifest as R, type SkillRegistry as S, type TaskClient as T, type TaskMetadata as U, type TaskModule as V, type WsRouteManifest as W, type TaskRegistriesSnapshot as X, type TaskRegistriesView as Y, type ToolCore as Z, type ToolPathMeta as _, type FaapiContext as a, cors as a0, createAppRegistries as a1, createTaskRegistriesView as a2, createTaskRegistry as a3, helmet as a4, 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 { F as FaapiContext, b as FaapiMiddleware, I as InjectorMap, R as RouteManifest, W as WsRouteManifest, C as CorsOptions, H as HelmetOptions, L as LoggerOptions } from './routeTypes-CuGMDCGB.js';
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-m9wZHWyo.js';
2
2
  import { Server } from 'node:http';
3
3
  import { WebSocket } from 'ws';
4
4
 
@@ -76,8 +76,6 @@ interface TestServerOptions {
76
76
  cors?: CorsOptions | boolean;
77
77
  /** 安全头配置,默认 false */
78
78
  helmet?: HelmetOptions | boolean;
79
- /** 请求日志配置,默认 false(避免污染测试输出) */
80
- logger?: LoggerOptions | boolean;
81
79
  /** 全局中间件(外层洋葱) */
82
80
  middlewares?: FaapiMiddleware[];
83
81
  /** 全局注入器 */
@@ -126,7 +124,7 @@ interface TestServer {
126
124
  * 2. sortRoutes 排序
127
125
  * 3. mkdtemp 创建临时 schema 目录(或用传入的 dist)
128
126
  * 4. generateSchemaFiles 生成 zod.js
129
- * 5. createServer 创建 server(默认禁用 CORS/Helmet/Logger,避免污染断言)
127
+ * 5. createServer 创建 server(默认禁用 CORS/Helmet,避免污染断言)
130
128
  * 6. server.listen(0) 随机端口
131
129
  * 7. 返回 TestServer
132
130
  *
package/dist/testing.js CHANGED
@@ -271,50 +271,74 @@ function formatEntry(entry) {
271
271
 
272
272
  // src/logger/logger.ts
273
273
  var LEVEL_RANK = { debug: 0, info: 1, warn: 2, error: 3 };
274
- var globalState = { level: null, sink: null, fileSink: null, stdout: true, accessLog: false, disabled: false };
275
- function globalThreshold() {
276
- if (globalState.level) return globalState.level;
277
- if (globalState.fileSink) return void 0;
278
- return "info";
274
+ var LOGGING_STATE_KEY = /* @__PURE__ */ Symbol.for("faapi.logger.state");
275
+ function createDefaultLoggingState() {
276
+ return {
277
+ level: null,
278
+ consoleLevel: null,
279
+ sink: null,
280
+ fileSink: null,
281
+ stdout: true,
282
+ accessLog: true,
283
+ disabled: false
284
+ };
285
+ }
286
+ function getLoggingState() {
287
+ const g = globalThis;
288
+ let state2 = g[LOGGING_STATE_KEY];
289
+ if (!state2) {
290
+ state2 = createDefaultLoggingState();
291
+ g[LOGGING_STATE_KEY] = state2;
292
+ }
293
+ return state2;
294
+ }
295
+ function pipelineThreshold() {
296
+ return getLoggingState().level ?? void 0;
297
+ }
298
+ function consoleThreshold() {
299
+ return getLoggingState().consoleLevel ?? "info";
279
300
  }
280
301
  function belowThreshold(level, threshold) {
281
302
  return threshold !== void 0 && LEVEL_RANK[level] < LEVEL_RANK[threshold];
282
303
  }
283
- var consoleSink = (entry) => {
284
- console[CONSOLE_METHOD[entry.level]](formatEntry(entry));
285
- };
286
304
  var CONSOLE_METHOD = {
287
305
  debug: "debug",
288
306
  info: "info",
289
307
  warn: "warn",
290
308
  error: "error"
291
309
  };
310
+ function consoleDispatch(entry) {
311
+ const threshold = consoleThreshold();
312
+ if (threshold === false || belowThreshold(entry.level, threshold)) return;
313
+ console[CONSOLE_METHOD[entry.level]](formatEntry(entry));
314
+ }
292
315
  function globalDispatch(entry) {
293
- if (globalState.disabled) return;
294
- if (globalState.sink) {
295
- globalState.sink(entry);
316
+ const state2 = getLoggingState();
317
+ if (state2.disabled) return;
318
+ const pipeThreshold = pipelineThreshold();
319
+ if (state2.sink) {
320
+ if (!belowThreshold(entry.level, pipeThreshold)) state2.sink(entry);
296
321
  return;
297
322
  }
298
- if (globalState.fileSink) {
299
- globalState.fileSink.write(entry);
300
- if (globalState.stdout) consoleSink(entry);
323
+ if (state2.fileSink) {
324
+ if (!belowThreshold(entry.level, pipeThreshold)) state2.fileSink.write(entry);
325
+ if (state2.stdout) consoleDispatch(entry);
301
326
  return;
302
327
  }
303
- consoleSink(entry);
328
+ consoleDispatch(entry);
304
329
  }
305
330
  function writeLogEntry(entry) {
306
- if (belowThreshold(entry.level, globalThreshold())) return;
307
331
  globalDispatch(entry);
308
332
  }
309
- function isAccessLogPiped() {
310
- return !globalState.disabled && globalState.accessLog;
333
+ function isAccessLogEnabled() {
334
+ const state2 = getLoggingState();
335
+ return !state2.disabled && state2.accessLog;
311
336
  }
312
337
  function createLogger(scope, options) {
313
338
  const baseScope = scope;
314
339
  const baseFields = options?.fields;
315
340
  const write = (level, message, callFields, scopeSuffix) => {
316
- const threshold = options?.level ?? globalThreshold();
317
- if (belowThreshold(level, threshold)) return;
341
+ if (belowThreshold(level, options?.level)) return;
318
342
  const entry = {
319
343
  level,
320
344
  message,
@@ -4212,18 +4236,14 @@ function levelForStatus(status) {
4212
4236
  return "info";
4213
4237
  }
4214
4238
  function defaultLog(entry, text) {
4215
- if (isAccessLogPiped()) {
4216
- const logEntry = {
4217
- level: levelForStatus(entry.status),
4218
- message: text,
4219
- time: (/* @__PURE__ */ new Date()).toISOString(),
4220
- scope: "access",
4221
- fields: entry
4222
- };
4223
- writeLogEntry(logEntry);
4224
- return;
4225
- }
4226
- console.log(entry);
4239
+ if (!isAccessLogEnabled()) return;
4240
+ writeLogEntry({
4241
+ level: levelForStatus(entry.status),
4242
+ message: text,
4243
+ time: (/* @__PURE__ */ new Date()).toISOString(),
4244
+ scope: "access",
4245
+ fields: entry
4246
+ });
4227
4247
  }
4228
4248
  function logger(options = {}) {
4229
4249
  return async (ctx, next) => {
@@ -4586,7 +4606,6 @@ function createServer(options) {
4586
4606
  compression: compressionOption,
4587
4607
  etag: etagOption,
4588
4608
  registries,
4589
- logger: loggerOption,
4590
4609
  bodyLimit = DEFAULT_BODY_LIMIT,
4591
4610
  http2: http2Option,
4592
4611
  trustedProxy = false
@@ -4603,8 +4622,7 @@ function createServer(options) {
4603
4622
  const helmOpts = typeof helmetOption === "object" ? helmetOption : {};
4604
4623
  configMiddlewares.push(helmet(helmOpts));
4605
4624
  }
4606
- const loggerMiddlewareInst = loggerOption === false ? null : loggerOption === true || loggerOption === void 0 ? logger() : logger(loggerOption);
4607
- if (loggerMiddlewareInst) configMiddlewares.push(loggerMiddlewareInst);
4625
+ configMiddlewares.push(logger());
4608
4626
  if (etagOption) {
4609
4627
  const etagOpts = typeof etagOption === "object" ? etagOption : {};
4610
4628
  configMiddlewares.push(etag(etagOpts));
@@ -4787,7 +4805,6 @@ async function createTestServer(options) {
4787
4805
  dist,
4788
4806
  cors: cors2 = false,
4789
4807
  helmet: helmet2 = false,
4790
- logger: logger2 = false,
4791
4808
  middlewares,
4792
4809
  injectors,
4793
4810
  onError,
@@ -4804,7 +4821,6 @@ async function createTestServer(options) {
4804
4821
  dist: schemaDist,
4805
4822
  cors: cors2,
4806
4823
  helmet: helmet2,
4807
- logger: logger2,
4808
4824
  middlewares,
4809
4825
  injectors,
4810
4826
  onError,