@faapi/faapi 6.7.0 → 6.8.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 CHANGED
@@ -2260,8 +2260,10 @@ var init_resolveInjection = __esm({
2260
2260
  // Phase 2.3
2261
2261
  agents: "agents",
2262
2262
  // Phase 2.3
2263
- tasks: "tasks"
2263
+ tasks: "tasks",
2264
2264
  // 任务子系统:TaskClient(入队/查询)
2265
+ log: "log"
2266
+ // 请求级日志器(ctx.log 同一实例:scope http,自动带 requestId 等字段)
2265
2267
  };
2266
2268
  injectionCache = /* @__PURE__ */ new WeakMap();
2267
2269
  }
@@ -6386,7 +6388,131 @@ var init_responseFormatter = __esm({
6386
6388
  }
6387
6389
  });
6388
6390
 
6391
+ // src/logger/logger.ts
6392
+ function assertLogLevel(value, source) {
6393
+ if (typeof value !== "string" || !VALID_LEVELS.includes(value)) {
6394
+ throw new Error(
6395
+ `[faapi] Invalid log level from ${source}: ${String(value)}. Expected one of: ${VALID_LEVELS.join(", ")}.`
6396
+ );
6397
+ }
6398
+ }
6399
+ function configureLogging(config) {
6400
+ if (config === void 0) {
6401
+ globalState.level = null;
6402
+ globalState.sink = null;
6403
+ globalState.disabled = false;
6404
+ return;
6405
+ }
6406
+ const objectConfig = typeof config === "object" ? config : {};
6407
+ globalState.disabled = config === false;
6408
+ globalState.sink = objectConfig.sink ?? null;
6409
+ if (objectConfig.level !== void 0) {
6410
+ assertLogLevel(objectConfig.level, "config.log.level");
6411
+ globalState.level = objectConfig.level;
6412
+ } else if (process.env.LOG_LEVEL !== void 0) {
6413
+ assertLogLevel(process.env.LOG_LEVEL, "LOG_LEVEL env");
6414
+ globalState.level = process.env.LOG_LEVEL;
6415
+ } else {
6416
+ globalState.level = null;
6417
+ }
6418
+ }
6419
+ function globalThreshold() {
6420
+ return globalState.level ?? "info";
6421
+ }
6422
+ function getEffectiveLogLevel() {
6423
+ return globalThreshold();
6424
+ }
6425
+ function serializeFieldValue(value) {
6426
+ if (value instanceof Error) {
6427
+ return { name: value.name, message: value.message, stack: value.stack };
6428
+ }
6429
+ return value;
6430
+ }
6431
+ function formatEntry(entry) {
6432
+ let line = `[${entry.time}] ${entry.level.toUpperCase()} `;
6433
+ if (entry.scope) line += `[${entry.scope}] `;
6434
+ line += entry.message;
6435
+ if (entry.fields !== void 0) {
6436
+ const plain = Object.fromEntries(
6437
+ Object.entries(entry.fields).map(([k, v]) => [k, serializeFieldValue(v)])
6438
+ );
6439
+ try {
6440
+ line += ` ${JSON.stringify(plain)}`;
6441
+ } catch (err) {
6442
+ line += ` [unserializable fields: ${err instanceof Error ? err.message : String(err)}]`;
6443
+ }
6444
+ }
6445
+ return line;
6446
+ }
6447
+ function globalDispatch(entry) {
6448
+ if (globalState.disabled) return;
6449
+ (globalState.sink ?? consoleSink)(entry);
6450
+ }
6451
+ function writeLogEntry(entry) {
6452
+ if (LEVEL_RANK[entry.level] < LEVEL_RANK[globalThreshold()]) return;
6453
+ globalDispatch(entry);
6454
+ }
6455
+ function createLogger(scope, options) {
6456
+ const baseScope = scope;
6457
+ const baseFields = options?.fields;
6458
+ const write = (level, message, callFields, scopeSuffix) => {
6459
+ const threshold = options?.level ?? globalThreshold();
6460
+ if (LEVEL_RANK[level] < LEVEL_RANK[threshold]) return;
6461
+ const entry = {
6462
+ level,
6463
+ message,
6464
+ time: (/* @__PURE__ */ new Date()).toISOString()
6465
+ };
6466
+ const fullScope = scopeSuffix === void 0 ? baseScope : baseScope === void 0 ? scopeSuffix : `${baseScope}:${scopeSuffix}`;
6467
+ if (fullScope !== void 0) entry.scope = fullScope;
6468
+ if (baseFields !== void 0 || callFields !== void 0) {
6469
+ entry.fields = { ...baseFields, ...callFields };
6470
+ }
6471
+ if (options?.sink) {
6472
+ options.sink(entry);
6473
+ } else {
6474
+ globalDispatch(entry);
6475
+ }
6476
+ };
6477
+ const makeLogger = (scopeSuffix) => ({
6478
+ debug: (message, fields) => write("debug", message, fields, scopeSuffix),
6479
+ info: (message, fields) => write("info", message, fields, scopeSuffix),
6480
+ warn: (message, fields) => write("warn", message, fields, scopeSuffix),
6481
+ error: (message, fields) => write("error", message, fields, scopeSuffix),
6482
+ child: (childScope) => makeLogger(scopeSuffix === void 0 ? childScope : `${scopeSuffix}:${childScope}`)
6483
+ });
6484
+ return makeLogger(void 0);
6485
+ }
6486
+ var LEVEL_RANK, VALID_LEVELS, globalState, CONSOLE_METHOD, consoleSink;
6487
+ var init_logger = __esm({
6488
+ "src/logger/logger.ts"() {
6489
+ "use strict";
6490
+ LEVEL_RANK = { debug: 0, info: 1, warn: 2, error: 3 };
6491
+ VALID_LEVELS = ["debug", "info", "warn", "error"];
6492
+ globalState = {
6493
+ level: null,
6494
+ sink: null,
6495
+ disabled: false
6496
+ };
6497
+ CONSOLE_METHOD = {
6498
+ debug: "debug",
6499
+ info: "info",
6500
+ warn: "warn",
6501
+ error: "error"
6502
+ };
6503
+ consoleSink = (entry) => {
6504
+ console[CONSOLE_METHOD[entry.level]](formatEntry(entry));
6505
+ };
6506
+ }
6507
+ });
6508
+
6389
6509
  // src/runtime/createContext.ts
6510
+ function resolveRequestId(request) {
6511
+ const header = request.headers.get("x-request-id");
6512
+ const first = header?.split(",")[0]?.trim();
6513
+ if (first) return first;
6514
+ return crypto.randomUUID();
6515
+ }
6390
6516
  function parseCookies(cookieHeader) {
6391
6517
  const cookies = /* @__PURE__ */ new Map();
6392
6518
  if (!cookieHeader) return cookies;
@@ -6420,7 +6546,13 @@ function createContextFromUrl(request, url, params, config = {}, ip = "", regist
6420
6546
  for (const [key, val] of parsedCookies) {
6421
6547
  cookiesObj[key] = val;
6422
6548
  }
6549
+ const requestId = resolveRequestId(request);
6550
+ const log = createLogger("http", {
6551
+ fields: { requestId, method: request.method, path: url.pathname }
6552
+ });
6423
6553
  const ctx = {
6554
+ requestId,
6555
+ log,
6424
6556
  request,
6425
6557
  params,
6426
6558
  query: url.searchParams,
@@ -6525,6 +6657,7 @@ var init_createContext = __esm({
6525
6657
  "use strict";
6526
6658
  init_sse();
6527
6659
  init_responseFormatter();
6660
+ init_logger();
6528
6661
  }
6529
6662
  });
6530
6663
 
@@ -7092,6 +7225,9 @@ function getBuiltinInjectionValue(type, ctx, body) {
7092
7225
  // 任务子系统:注入 TaskClient(入队/查询);未注册工厂(无 app 编排)时 undefined
7093
7226
  case "tasks":
7094
7227
  return ctx.registries ? ctx.registries.taskHandle.get(ctx) : void 0;
7228
+ // 请求级日志器:与 ctx.log 同一实例(scope http,自动带 requestId/method/path 字段)
7229
+ case "log":
7230
+ return ctx.log;
7095
7231
  default:
7096
7232
  return void 0;
7097
7233
  }
@@ -7716,6 +7852,7 @@ function logger(options = {}) {
7716
7852
  const response = await next();
7717
7853
  const duration = Date.now() - start;
7718
7854
  const entry = {
7855
+ requestId: ctx.requestId,
7719
7856
  method: ctx.method,
7720
7857
  path: ctx.path,
7721
7858
  status: response.status,
@@ -7728,6 +7865,7 @@ function logger(options = {}) {
7728
7865
  const message = err instanceof Error ? err.message : String(err);
7729
7866
  const status = err?.statusCode ?? 500;
7730
7867
  const entry = {
7868
+ requestId: ctx.requestId,
7731
7869
  method: ctx.method,
7732
7870
  path: ctx.path,
7733
7871
  status,
@@ -7739,7 +7877,7 @@ function logger(options = {}) {
7739
7877
  }
7740
7878
  };
7741
7879
  }
7742
- var init_logger = __esm({
7880
+ var init_logger2 = __esm({
7743
7881
  "src/middleware/logger.ts"() {
7744
7882
  "use strict";
7745
7883
  }
@@ -8317,7 +8455,7 @@ var init_createServer = __esm({
8317
8455
  init_helmet();
8318
8456
  init_compression();
8319
8457
  init_etag();
8320
- init_logger();
8458
+ init_logger2();
8321
8459
  init_handleWsUpgrade();
8322
8460
  init_serverUtils();
8323
8461
  init_generateSchemaFiles();
@@ -8386,6 +8524,8 @@ ${BUILD_VIEW_SOURCE}
8386
8524
 
8387
8525
  ${SERIALIZE_ERROR_SOURCE}
8388
8526
 
8527
+ ${BUILD_LOGGER_SOURCE}
8528
+
8389
8529
  const run = mod.run;
8390
8530
  if (typeof run !== 'function') {
8391
8531
  parentPort.postMessage({
@@ -8405,7 +8545,8 @@ if (typeof run !== 'function') {
8405
8545
  try {
8406
8546
  const registries = buildRegistriesView(msg.registries);
8407
8547
  const progress = (value) => parentPort.postMessage({ type: 'progress', value });
8408
- const result = await run(payload, { ...taskCtx, signal: controller.signal, registries, progress });
8548
+ const taskLog = msg.log ? createTaskLogger(msg.log.level, msg.log.scope, msg.log.fields) : undefined;
8549
+ const result = await run(payload, { ...taskCtx, signal: controller.signal, registries, progress, log: taskLog });
8409
8550
  parentPort.postMessage({ type: 'done', result });
8410
8551
  } catch (err) {
8411
8552
  const serialized = serializeError(err);
@@ -8513,6 +8654,8 @@ async function runTaskInWorker(options) {
8513
8654
  finish(() => reject(reviveError(msg.error)));
8514
8655
  } else if (msg?.type === "progress") {
8515
8656
  options.onProgress?.(msg.value);
8657
+ } else if (msg?.type === "log") {
8658
+ options.onLog?.(msg.entry);
8516
8659
  }
8517
8660
  }
8518
8661
  );
@@ -8536,7 +8679,8 @@ async function runTaskInWorker(options) {
8536
8679
  type: "run",
8537
8680
  payload,
8538
8681
  taskCtx: { config: safeConfig(taskCtx.config), job: taskCtx.job },
8539
- registries: options.registries
8682
+ registries: options.registries,
8683
+ log: options.log
8540
8684
  });
8541
8685
  } catch (err) {
8542
8686
  finish(
@@ -8547,7 +8691,7 @@ async function runTaskInWorker(options) {
8547
8691
  }
8548
8692
  });
8549
8693
  }
8550
- var KILL_GRACE_MS, TaskCancelledError, BUILD_VIEW_SOURCE, SERIALIZE_ERROR_SOURCE;
8694
+ var KILL_GRACE_MS, TaskCancelledError, BUILD_VIEW_SOURCE, SERIALIZE_ERROR_SOURCE, BUILD_LOGGER_SOURCE;
8551
8695
  var init_taskWorker = __esm({
8552
8696
  "src/task/taskWorker.ts"() {
8553
8697
  "use strict";
@@ -8625,6 +8769,36 @@ function serializeError(err) {
8625
8769
  }
8626
8770
  return { name: 'Error', message: String(err), stack: undefined, props: {} };
8627
8771
  }
8772
+ `;
8773
+ BUILD_LOGGER_SOURCE = `
8774
+ var LOG_RANK = { debug: 0, info: 1, warn: 2, error: 3 };
8775
+ function createTaskLogger(level, scope, fields) {
8776
+ function make(suffix) {
8777
+ function write(lvl, message, callFields) {
8778
+ if (LOG_RANK[lvl] < LOG_RANK[level]) return;
8779
+ var entry = { level: lvl, message: message, time: new Date().toISOString() };
8780
+ var fullScope = suffix === undefined ? scope : scope === undefined ? suffix : scope + ':' + suffix;
8781
+ if (fullScope !== undefined) entry.scope = fullScope;
8782
+ if (fields !== undefined || callFields !== undefined) {
8783
+ entry.fields = Object.assign({}, fields, callFields);
8784
+ }
8785
+ try {
8786
+ parentPort.postMessage({ type: 'log', entry: entry });
8787
+ } catch (_) {
8788
+ entry.fields = { warning: 'log fields not cloneable across worker boundary, dropped' };
8789
+ parentPort.postMessage({ type: 'log', entry: entry });
8790
+ }
8791
+ }
8792
+ return {
8793
+ debug: function (m, f) { write('debug', m, f); },
8794
+ info: function (m, f) { write('info', m, f); },
8795
+ warn: function (m, f) { write('warn', m, f); },
8796
+ error: function (m, f) { write('error', m, f); },
8797
+ child: function (cs) { return make(suffix === undefined ? cs : suffix + ':' + cs); },
8798
+ };
8799
+ }
8800
+ return make(undefined);
8801
+ }
8628
8802
  `;
8629
8803
  }
8630
8804
  });
@@ -8731,12 +8905,20 @@ function createTaskQueue(deps) {
8731
8905
  job: { id: job.id, name: job.name, attempt: job.attempt }
8732
8906
  },
8733
8907
  registries: snapshotRegistries(),
8908
+ // 日志配置随 workerData 下发(纯数据),worker 内联重建 taskCtx.log,
8909
+ // 条目回传宿主走统一管道(scope/fields 与进程内路径一致)
8910
+ log: {
8911
+ level: getEffectiveLogLevel(),
8912
+ scope: `task:${job.name}`,
8913
+ fields: { jobId: job.id, task: job.name, attempt: job.attempt }
8914
+ },
8734
8915
  timeoutMs: meta.timeoutMs,
8735
8916
  graceMs: meta.graceMs,
8736
8917
  externalSignal: job.signal,
8737
8918
  onProgress: (value) => {
8738
8919
  if (record.status === "running") record.progress = value;
8739
- }
8920
+ },
8921
+ onLog: writeLogEntry
8740
8922
  });
8741
8923
  } else {
8742
8924
  let mod = moduleCache2.get(job.name);
@@ -8752,6 +8934,9 @@ function createTaskQueue(deps) {
8752
8934
  config: deps.config,
8753
8935
  job: { id: job.id, name: job.name, attempt: job.attempt },
8754
8936
  registries: registriesView,
8937
+ log: createLogger(`task:${job.name}`, {
8938
+ fields: { jobId: job.id, task: job.name, attempt: job.attempt }
8939
+ }),
8755
8940
  progress: (value) => {
8756
8941
  if (record.status === "running") record.progress = value;
8757
8942
  }
@@ -8906,6 +9091,7 @@ var init_taskQueue = __esm({
8906
9091
  init_httpErrors();
8907
9092
  init_taskWorker();
8908
9093
  init_registries();
9094
+ init_logger();
8909
9095
  TERMINAL_STATUSES = /* @__PURE__ */ new Set(["done", "failed", "cancelled"]);
8910
9096
  MAX_FINISHED_RECORDS = 1e3;
8911
9097
  }
@@ -9253,6 +9439,7 @@ async function createAppBase(options) {
9253
9439
  );
9254
9440
  }
9255
9441
  const config = await loadConfig(rootDir, dist);
9442
+ configureLogging(config?.log);
9256
9443
  const serialized = await importWithCacheBust(routesPath);
9257
9444
  const hydrated = await hydrateRoutes(serialized);
9258
9445
  let sorted = sortRoutes(hydrated.routes);
@@ -9589,6 +9776,7 @@ var init_createAppCore = __esm({
9589
9776
  init_loadTaskDriver();
9590
9777
  init_idleTaskDriver();
9591
9778
  init_cronScheduler();
9779
+ init_logger();
9592
9780
  init_loadPlugins();
9593
9781
  init_importWithCacheBust();
9594
9782
  init_registries();
@@ -9612,6 +9800,7 @@ var init_createAppCore = __esm({
9612
9800
  "etag",
9613
9801
  "bodyLimit",
9614
9802
  "logger",
9803
+ "log",
9615
9804
  "http2",
9616
9805
  "trustedProxy",
9617
9806
  "response",