@lark-apaas/devtool-kits 1.2.24 → 1.2.25-alpha.20260819082505
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/index.cjs +230 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +128 -1
- package/dist/index.d.ts +128 -1
- package/dist/index.js +217 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -274,6 +274,133 @@ declare function getQueryParam(req: AnyRequest, key: string): string | undefined
|
|
|
274
274
|
*/
|
|
275
275
|
declare function registerMiddlewares(server: ExpressApp | ViteMiddleware, middlewares: Middleware[], options?: Partial<MiddlewareContext>): void;
|
|
276
276
|
|
|
277
|
+
/**
|
|
278
|
+
* 妙搭应用 Health Check 接口类型定义。
|
|
279
|
+
*
|
|
280
|
+
* 协议(飞书文档 - 妙搭应用 Health Check 接口规范):
|
|
281
|
+
* - HTTP connection refused / timeout → dev server 没起来 → 外壳重启沙箱
|
|
282
|
+
* - 200 + { ready: true } → 应用已就绪,放行
|
|
283
|
+
* - 200 + { ready: false, failed: false } → 还在启动,继续轮询
|
|
284
|
+
* - 200 + { ready: false, failed: true } → 终态失败,停止轮询并把 message 呈现给用户
|
|
285
|
+
*
|
|
286
|
+
* 约束:`ready: true` 时 `failed` 必须为 false。
|
|
287
|
+
*/
|
|
288
|
+
interface HealthPayload {
|
|
289
|
+
/** 应用是否已就绪。client-only 栈等价于 dev server 自身就绪。 */
|
|
290
|
+
ready: boolean;
|
|
291
|
+
/** 终态失败:等下去也不会好。缺省 false。 */
|
|
292
|
+
failed: boolean;
|
|
293
|
+
/** 失败时的人类可读说明,仅用于展示与排查,外壳不得用于流程决策。 */
|
|
294
|
+
message?: string;
|
|
295
|
+
}
|
|
296
|
+
/** 单个信号的判定结果,仅内部使用。 */
|
|
297
|
+
interface ServerLiveness {
|
|
298
|
+
/** Nest 服务当前是否存活 */
|
|
299
|
+
alive: boolean;
|
|
300
|
+
/** 最近一次成功 listen 的进程 pid(无则 null) */
|
|
301
|
+
pid: number | null;
|
|
302
|
+
/** 最近一次成功 listen 的时间(ISO,无则 null) */
|
|
303
|
+
listenAt: string | null;
|
|
304
|
+
}
|
|
305
|
+
interface ResolveHealthOptions {
|
|
306
|
+
/** 项目根目录 */
|
|
307
|
+
rootDir: string;
|
|
308
|
+
/** 日志目录,默认 `<rootDir>/logs` */
|
|
309
|
+
logDir?: string;
|
|
310
|
+
/**
|
|
311
|
+
* 本栈是否包含 Node 服务端。
|
|
312
|
+
* false(vite-react / html 等 client-only 栈)时直接返回 dev server 自身状态,
|
|
313
|
+
* 行为与改造前完全一致。
|
|
314
|
+
*/
|
|
315
|
+
hasServer: boolean;
|
|
316
|
+
/** dev server 自身是否已 listen */
|
|
317
|
+
clientReady: boolean;
|
|
318
|
+
/** 本次 dev 会话首次拉起 server 的时间戳(ms)。缺省时不做超时判定。 */
|
|
319
|
+
firstStartedAt?: number | null;
|
|
320
|
+
/** 编译产物入口,默认 `<rootDir>/dist/server/main.js` */
|
|
321
|
+
serverEntry?: string;
|
|
322
|
+
/** 超时上限(ms),超过则判 failed。默认 300000。 */
|
|
323
|
+
timeoutMs?: number;
|
|
324
|
+
/**
|
|
325
|
+
* 本次 dev 会话的启动时间(ms),用于排除 pid 复用导致的陈旧 marker。
|
|
326
|
+
* 与 `firstStartedAt` 分开:后者是「超时从何时起算」的锚点,崩溃后会前移。
|
|
327
|
+
*/
|
|
328
|
+
sessionStartedAt?: number | null;
|
|
329
|
+
/** 探测到 server 存活时回调,供宿主记录「最后一次存活时刻」 */
|
|
330
|
+
onServerAlive?: () => void;
|
|
331
|
+
/** 注入点,便于测试 */
|
|
332
|
+
now?: () => number;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* 判定应用健康状态。
|
|
337
|
+
*
|
|
338
|
+
* 设计原则:**`failed: true` 只能由肯定性事实触发,绝不能由否定性推断触发。**
|
|
339
|
+
* 「读不到日志」「匹配不到 marker」「产物看不见」都属于否定性推断,
|
|
340
|
+
* 一律降级为「还在启动」(`ready:false, failed:false`),让外壳继续等。
|
|
341
|
+
* 只有「超过 timeoutMs 仍未就绪」这一个时间事实可以判 failed。
|
|
342
|
+
*/
|
|
343
|
+
declare function resolveHealth(options: ResolveHealthOptions): HealthPayload;
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* `process.kill(pid, 0)` 不发信号,只做存在性检查。
|
|
347
|
+
* EPERM 表示进程存在但当前用户无权发信号 —— 仍算存活。
|
|
348
|
+
*/
|
|
349
|
+
declare function isPidAlive(pid: number): boolean;
|
|
350
|
+
/**
|
|
351
|
+
* 判定 Nest 服务当前是否存活。
|
|
352
|
+
*
|
|
353
|
+
* 两步都是「肯定性事实」:
|
|
354
|
+
* 1. 日志里存在 listen marker → 曾经成功启动过(匹配不到就不下结论)
|
|
355
|
+
* 2. 该 marker 的 pid 仍然存活 → 现在确实还活着
|
|
356
|
+
*
|
|
357
|
+
* 只有第 1 步会导致误判:`dev.js` 以 append 模式写日志,历史成功记录会长期留存,
|
|
358
|
+
* 因此第 2 步的 pid 验活不可省略。
|
|
359
|
+
*
|
|
360
|
+
* `sessionStartedAt` 用于排除 pid 复用:marker 时间早于本次会话启动时间的,一律不采信。
|
|
361
|
+
*/
|
|
362
|
+
declare function resolveServerLiveness(logDir: string, sessionStartedAt?: number | null): ServerLiveness;
|
|
363
|
+
|
|
364
|
+
/** 沙箱外壳探测的是固定根路径,不跟 clientBasePath 走 */
|
|
365
|
+
declare const HEALTH_ENDPOINT = "/dev/health";
|
|
366
|
+
interface HealthHandlerOptions {
|
|
367
|
+
/** 项目根目录 */
|
|
368
|
+
rootDir: string;
|
|
369
|
+
/** 日志目录,默认 `<rootDir>/logs` */
|
|
370
|
+
logDir?: string;
|
|
371
|
+
/** 本栈是否包含 Node 服务端 */
|
|
372
|
+
hasServer: boolean;
|
|
373
|
+
/** dev server 自身是否已 listen(由各 preset 注入) */
|
|
374
|
+
isClientReady: () => boolean;
|
|
375
|
+
/** 超时上限(ms);未传时读环境变量 MIAODA_HEALTH_TIMEOUT_MS,再兜底 5min */
|
|
376
|
+
timeoutMs?: number;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* 构造一个有状态的健康探测函数。宿主自行决定怎么把结果写进响应
|
|
380
|
+
* (Express 用 `res.status().json()`,Connect/Vite 用 `res.end()`)。
|
|
381
|
+
*
|
|
382
|
+
* 之所以有状态:超时窗口必须从「最后一次确认存活」起算,而不是从会话启动起算。
|
|
383
|
+
* 否则一个健康跑了 10 分钟的服务一旦崩溃,会因为 `now - firstStartedAt` 早已超限
|
|
384
|
+
* 而被**立刻**判成终态失败,supervisor 连重启的机会都没有。
|
|
385
|
+
*
|
|
386
|
+
* 判定本身出任何错都降级为「还在启动」——health 是旁路能力,
|
|
387
|
+
* 绝不能因为它自己的异常而让外壳误判失败、阻塞用户启动。
|
|
388
|
+
*/
|
|
389
|
+
declare function createHealthProbe(options: HealthHandlerOptions): () => HealthPayload;
|
|
390
|
+
/**
|
|
391
|
+
* 单次计算健康状态(无状态版本)。
|
|
392
|
+
* 长驻宿主请用 {@link createHealthProbe},它会正确处理「崩溃后重新计时」。
|
|
393
|
+
*/
|
|
394
|
+
declare function getHealthPayload(options: HealthHandlerOptions): HealthPayload;
|
|
395
|
+
/**
|
|
396
|
+
* 构造 `/dev/health` 的 Connect / Vite 中间件。
|
|
397
|
+
*
|
|
398
|
+
* 响应码恒 200:`connection refused` / `timeout` 已被外壳用来表示
|
|
399
|
+
* 「dev server 自身没起来 → 重启沙箱」,应用级失败必须走 body,
|
|
400
|
+
* 否则会和传输层失败混淆。
|
|
401
|
+
*/
|
|
402
|
+
declare function createHealthHandler(options: HealthHandlerOptions): (req: IncomingMessage, res: ServerResponse, next?: () => void) => void;
|
|
403
|
+
|
|
277
404
|
interface PageRouteInfo {
|
|
278
405
|
path?: string;
|
|
279
406
|
index?: boolean;
|
|
@@ -300,4 +427,4 @@ declare function parseRoutesFromFile(appPath: string, basePath: string, options?
|
|
|
300
427
|
path: string;
|
|
301
428
|
}>;
|
|
302
429
|
|
|
303
|
-
export { type GlobalMiddleware, type Middleware, type MiddlewareContext, type Options, type PageRouteInfo, type ParseRoutesOptions, type RouteInfo, type RouteMiddleware, buildFullPath, calculateFileHash, createCollectLogsMiddleware, createDevLogsMiddleware, createOpenapiMiddleware, evaluateTemplateLiteral, extractPageRouteInfo, getQuery, getQueryParam, handleDevProxyError, isRouteComponent, normalizeBasePath, parseAndGenerateNestResourceTemplate, parseApiRoutes, parseRoutesFromFile, postprocessDrizzleSchema, registerMiddlewares, routeParserLog, sendError, sendJson, sendSuccess };
|
|
430
|
+
export { type GlobalMiddleware, HEALTH_ENDPOINT, type HealthHandlerOptions, type HealthPayload, type Middleware, type MiddlewareContext, type Options, type PageRouteInfo, type ParseRoutesOptions, type ResolveHealthOptions, type RouteInfo, type RouteMiddleware, type ServerLiveness, buildFullPath, calculateFileHash, createCollectLogsMiddleware, createDevLogsMiddleware, createHealthHandler, createHealthProbe, createOpenapiMiddleware, evaluateTemplateLiteral, extractPageRouteInfo, getHealthPayload, getQuery, getQueryParam, handleDevProxyError, isPidAlive, isRouteComponent, normalizeBasePath, parseAndGenerateNestResourceTemplate, parseApiRoutes, parseRoutesFromFile, postprocessDrizzleSchema, registerMiddlewares, resolveHealth, resolveServerLiveness, routeParserLog, sendError, sendJson, sendSuccess };
|
package/dist/index.d.ts
CHANGED
|
@@ -274,6 +274,133 @@ declare function getQueryParam(req: AnyRequest, key: string): string | undefined
|
|
|
274
274
|
*/
|
|
275
275
|
declare function registerMiddlewares(server: ExpressApp | ViteMiddleware, middlewares: Middleware[], options?: Partial<MiddlewareContext>): void;
|
|
276
276
|
|
|
277
|
+
/**
|
|
278
|
+
* 妙搭应用 Health Check 接口类型定义。
|
|
279
|
+
*
|
|
280
|
+
* 协议(飞书文档 - 妙搭应用 Health Check 接口规范):
|
|
281
|
+
* - HTTP connection refused / timeout → dev server 没起来 → 外壳重启沙箱
|
|
282
|
+
* - 200 + { ready: true } → 应用已就绪,放行
|
|
283
|
+
* - 200 + { ready: false, failed: false } → 还在启动,继续轮询
|
|
284
|
+
* - 200 + { ready: false, failed: true } → 终态失败,停止轮询并把 message 呈现给用户
|
|
285
|
+
*
|
|
286
|
+
* 约束:`ready: true` 时 `failed` 必须为 false。
|
|
287
|
+
*/
|
|
288
|
+
interface HealthPayload {
|
|
289
|
+
/** 应用是否已就绪。client-only 栈等价于 dev server 自身就绪。 */
|
|
290
|
+
ready: boolean;
|
|
291
|
+
/** 终态失败:等下去也不会好。缺省 false。 */
|
|
292
|
+
failed: boolean;
|
|
293
|
+
/** 失败时的人类可读说明,仅用于展示与排查,外壳不得用于流程决策。 */
|
|
294
|
+
message?: string;
|
|
295
|
+
}
|
|
296
|
+
/** 单个信号的判定结果,仅内部使用。 */
|
|
297
|
+
interface ServerLiveness {
|
|
298
|
+
/** Nest 服务当前是否存活 */
|
|
299
|
+
alive: boolean;
|
|
300
|
+
/** 最近一次成功 listen 的进程 pid(无则 null) */
|
|
301
|
+
pid: number | null;
|
|
302
|
+
/** 最近一次成功 listen 的时间(ISO,无则 null) */
|
|
303
|
+
listenAt: string | null;
|
|
304
|
+
}
|
|
305
|
+
interface ResolveHealthOptions {
|
|
306
|
+
/** 项目根目录 */
|
|
307
|
+
rootDir: string;
|
|
308
|
+
/** 日志目录,默认 `<rootDir>/logs` */
|
|
309
|
+
logDir?: string;
|
|
310
|
+
/**
|
|
311
|
+
* 本栈是否包含 Node 服务端。
|
|
312
|
+
* false(vite-react / html 等 client-only 栈)时直接返回 dev server 自身状态,
|
|
313
|
+
* 行为与改造前完全一致。
|
|
314
|
+
*/
|
|
315
|
+
hasServer: boolean;
|
|
316
|
+
/** dev server 自身是否已 listen */
|
|
317
|
+
clientReady: boolean;
|
|
318
|
+
/** 本次 dev 会话首次拉起 server 的时间戳(ms)。缺省时不做超时判定。 */
|
|
319
|
+
firstStartedAt?: number | null;
|
|
320
|
+
/** 编译产物入口,默认 `<rootDir>/dist/server/main.js` */
|
|
321
|
+
serverEntry?: string;
|
|
322
|
+
/** 超时上限(ms),超过则判 failed。默认 300000。 */
|
|
323
|
+
timeoutMs?: number;
|
|
324
|
+
/**
|
|
325
|
+
* 本次 dev 会话的启动时间(ms),用于排除 pid 复用导致的陈旧 marker。
|
|
326
|
+
* 与 `firstStartedAt` 分开:后者是「超时从何时起算」的锚点,崩溃后会前移。
|
|
327
|
+
*/
|
|
328
|
+
sessionStartedAt?: number | null;
|
|
329
|
+
/** 探测到 server 存活时回调,供宿主记录「最后一次存活时刻」 */
|
|
330
|
+
onServerAlive?: () => void;
|
|
331
|
+
/** 注入点,便于测试 */
|
|
332
|
+
now?: () => number;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* 判定应用健康状态。
|
|
337
|
+
*
|
|
338
|
+
* 设计原则:**`failed: true` 只能由肯定性事实触发,绝不能由否定性推断触发。**
|
|
339
|
+
* 「读不到日志」「匹配不到 marker」「产物看不见」都属于否定性推断,
|
|
340
|
+
* 一律降级为「还在启动」(`ready:false, failed:false`),让外壳继续等。
|
|
341
|
+
* 只有「超过 timeoutMs 仍未就绪」这一个时间事实可以判 failed。
|
|
342
|
+
*/
|
|
343
|
+
declare function resolveHealth(options: ResolveHealthOptions): HealthPayload;
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* `process.kill(pid, 0)` 不发信号,只做存在性检查。
|
|
347
|
+
* EPERM 表示进程存在但当前用户无权发信号 —— 仍算存活。
|
|
348
|
+
*/
|
|
349
|
+
declare function isPidAlive(pid: number): boolean;
|
|
350
|
+
/**
|
|
351
|
+
* 判定 Nest 服务当前是否存活。
|
|
352
|
+
*
|
|
353
|
+
* 两步都是「肯定性事实」:
|
|
354
|
+
* 1. 日志里存在 listen marker → 曾经成功启动过(匹配不到就不下结论)
|
|
355
|
+
* 2. 该 marker 的 pid 仍然存活 → 现在确实还活着
|
|
356
|
+
*
|
|
357
|
+
* 只有第 1 步会导致误判:`dev.js` 以 append 模式写日志,历史成功记录会长期留存,
|
|
358
|
+
* 因此第 2 步的 pid 验活不可省略。
|
|
359
|
+
*
|
|
360
|
+
* `sessionStartedAt` 用于排除 pid 复用:marker 时间早于本次会话启动时间的,一律不采信。
|
|
361
|
+
*/
|
|
362
|
+
declare function resolveServerLiveness(logDir: string, sessionStartedAt?: number | null): ServerLiveness;
|
|
363
|
+
|
|
364
|
+
/** 沙箱外壳探测的是固定根路径,不跟 clientBasePath 走 */
|
|
365
|
+
declare const HEALTH_ENDPOINT = "/dev/health";
|
|
366
|
+
interface HealthHandlerOptions {
|
|
367
|
+
/** 项目根目录 */
|
|
368
|
+
rootDir: string;
|
|
369
|
+
/** 日志目录,默认 `<rootDir>/logs` */
|
|
370
|
+
logDir?: string;
|
|
371
|
+
/** 本栈是否包含 Node 服务端 */
|
|
372
|
+
hasServer: boolean;
|
|
373
|
+
/** dev server 自身是否已 listen(由各 preset 注入) */
|
|
374
|
+
isClientReady: () => boolean;
|
|
375
|
+
/** 超时上限(ms);未传时读环境变量 MIAODA_HEALTH_TIMEOUT_MS,再兜底 5min */
|
|
376
|
+
timeoutMs?: number;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* 构造一个有状态的健康探测函数。宿主自行决定怎么把结果写进响应
|
|
380
|
+
* (Express 用 `res.status().json()`,Connect/Vite 用 `res.end()`)。
|
|
381
|
+
*
|
|
382
|
+
* 之所以有状态:超时窗口必须从「最后一次确认存活」起算,而不是从会话启动起算。
|
|
383
|
+
* 否则一个健康跑了 10 分钟的服务一旦崩溃,会因为 `now - firstStartedAt` 早已超限
|
|
384
|
+
* 而被**立刻**判成终态失败,supervisor 连重启的机会都没有。
|
|
385
|
+
*
|
|
386
|
+
* 判定本身出任何错都降级为「还在启动」——health 是旁路能力,
|
|
387
|
+
* 绝不能因为它自己的异常而让外壳误判失败、阻塞用户启动。
|
|
388
|
+
*/
|
|
389
|
+
declare function createHealthProbe(options: HealthHandlerOptions): () => HealthPayload;
|
|
390
|
+
/**
|
|
391
|
+
* 单次计算健康状态(无状态版本)。
|
|
392
|
+
* 长驻宿主请用 {@link createHealthProbe},它会正确处理「崩溃后重新计时」。
|
|
393
|
+
*/
|
|
394
|
+
declare function getHealthPayload(options: HealthHandlerOptions): HealthPayload;
|
|
395
|
+
/**
|
|
396
|
+
* 构造 `/dev/health` 的 Connect / Vite 中间件。
|
|
397
|
+
*
|
|
398
|
+
* 响应码恒 200:`connection refused` / `timeout` 已被外壳用来表示
|
|
399
|
+
* 「dev server 自身没起来 → 重启沙箱」,应用级失败必须走 body,
|
|
400
|
+
* 否则会和传输层失败混淆。
|
|
401
|
+
*/
|
|
402
|
+
declare function createHealthHandler(options: HealthHandlerOptions): (req: IncomingMessage, res: ServerResponse, next?: () => void) => void;
|
|
403
|
+
|
|
277
404
|
interface PageRouteInfo {
|
|
278
405
|
path?: string;
|
|
279
406
|
index?: boolean;
|
|
@@ -300,4 +427,4 @@ declare function parseRoutesFromFile(appPath: string, basePath: string, options?
|
|
|
300
427
|
path: string;
|
|
301
428
|
}>;
|
|
302
429
|
|
|
303
|
-
export { type GlobalMiddleware, type Middleware, type MiddlewareContext, type Options, type PageRouteInfo, type ParseRoutesOptions, type RouteInfo, type RouteMiddleware, buildFullPath, calculateFileHash, createCollectLogsMiddleware, createDevLogsMiddleware, createOpenapiMiddleware, evaluateTemplateLiteral, extractPageRouteInfo, getQuery, getQueryParam, handleDevProxyError, isRouteComponent, normalizeBasePath, parseAndGenerateNestResourceTemplate, parseApiRoutes, parseRoutesFromFile, postprocessDrizzleSchema, registerMiddlewares, routeParserLog, sendError, sendJson, sendSuccess };
|
|
430
|
+
export { type GlobalMiddleware, HEALTH_ENDPOINT, type HealthHandlerOptions, type HealthPayload, type Middleware, type MiddlewareContext, type Options, type PageRouteInfo, type ParseRoutesOptions, type ResolveHealthOptions, type RouteInfo, type RouteMiddleware, type ServerLiveness, buildFullPath, calculateFileHash, createCollectLogsMiddleware, createDevLogsMiddleware, createHealthHandler, createHealthProbe, createOpenapiMiddleware, evaluateTemplateLiteral, extractPageRouteInfo, getHealthPayload, getQuery, getQueryParam, handleDevProxyError, isPidAlive, isRouteComponent, normalizeBasePath, parseAndGenerateNestResourceTemplate, parseApiRoutes, parseRoutesFromFile, postprocessDrizzleSchema, registerMiddlewares, resolveHealth, resolveServerLiveness, routeParserLog, sendError, sendJson, sendSuccess };
|
package/dist/index.js
CHANGED
|
@@ -1610,8 +1610,8 @@ function hasSpecialPatterns(pattern) {
|
|
|
1610
1610
|
return /[{*]/.test(pattern);
|
|
1611
1611
|
}
|
|
1612
1612
|
__name(hasSpecialPatterns, "hasSpecialPatterns");
|
|
1613
|
-
function normalizePathForMatching(
|
|
1614
|
-
return
|
|
1613
|
+
function normalizePathForMatching(path8) {
|
|
1614
|
+
return path8.replace(/\/+/g, "/").replace(/\/+$/, "");
|
|
1615
1615
|
}
|
|
1616
1616
|
__name(normalizePathForMatching, "normalizePathForMatching");
|
|
1617
1617
|
|
|
@@ -2160,7 +2160,7 @@ __name(readLogsBySource, "readLogsBySource");
|
|
|
2160
2160
|
// src/middlewares/dev-logs/services/trigger.service.ts
|
|
2161
2161
|
import { createReadStream as createReadStream3 } from "fs";
|
|
2162
2162
|
import { createInterface as createInterface3 } from "readline";
|
|
2163
|
-
async function readTriggerList(filePath, trigger,
|
|
2163
|
+
async function readTriggerList(filePath, trigger, path8, limit, triggerID) {
|
|
2164
2164
|
if (!await fileExists(filePath)) {
|
|
2165
2165
|
return void 0;
|
|
2166
2166
|
}
|
|
@@ -2186,7 +2186,7 @@ async function readTriggerList(filePath, trigger, path5, limit, triggerID) {
|
|
|
2186
2186
|
if (alreadyAdded) {
|
|
2187
2187
|
return false;
|
|
2188
2188
|
}
|
|
2189
|
-
const isAutomationTrigger = builder.path?.endsWith(
|
|
2189
|
+
const isAutomationTrigger = builder.path?.endsWith(path8);
|
|
2190
2190
|
if (!isAutomationTrigger) {
|
|
2191
2191
|
return false;
|
|
2192
2192
|
}
|
|
@@ -2269,7 +2269,7 @@ async function readTriggerList(filePath, trigger, path5, limit, triggerID) {
|
|
|
2269
2269
|
};
|
|
2270
2270
|
}
|
|
2271
2271
|
__name(readTriggerList, "readTriggerList");
|
|
2272
|
-
async function readTriggerDetail(filePath,
|
|
2272
|
+
async function readTriggerDetail(filePath, path8, instanceID) {
|
|
2273
2273
|
const exists = await fileExists(filePath);
|
|
2274
2274
|
if (!exists) {
|
|
2275
2275
|
return void 0;
|
|
@@ -2285,7 +2285,7 @@ async function readTriggerDetail(filePath, path5, instanceID) {
|
|
|
2285
2285
|
for await (const line of rl) {
|
|
2286
2286
|
const entry = parseLogLine(line);
|
|
2287
2287
|
if (!entry) continue;
|
|
2288
|
-
const isAutomationTrigger = entry.path?.endsWith(
|
|
2288
|
+
const isAutomationTrigger = entry.path?.endsWith(path8);
|
|
2289
2289
|
const hasInstanceID = entry.instance_id === instanceID && entry.trigger;
|
|
2290
2290
|
if (!isAutomationTrigger || !hasInstanceID) continue;
|
|
2291
2291
|
matches.push(entry);
|
|
@@ -2539,16 +2539,16 @@ function createGetTriggerListHandler(logDir) {
|
|
|
2539
2539
|
});
|
|
2540
2540
|
}
|
|
2541
2541
|
const triggerID = typeof req.query.triggerID === "string" ? req.query.triggerID.trim() : void 0;
|
|
2542
|
-
const
|
|
2542
|
+
const path8 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
|
|
2543
2543
|
const limit = parseLimit(req.query.limit, 10, 200);
|
|
2544
2544
|
try {
|
|
2545
|
-
const result = await readTriggerList(traceLogPath, trigger,
|
|
2545
|
+
const result = await readTriggerList(traceLogPath, trigger, path8, limit, triggerID);
|
|
2546
2546
|
if (!result) {
|
|
2547
2547
|
return handleNotFound(res, traceLogPath);
|
|
2548
2548
|
}
|
|
2549
2549
|
res.json({
|
|
2550
2550
|
file: getRelativePath(traceLogPath),
|
|
2551
|
-
path:
|
|
2551
|
+
path: path8,
|
|
2552
2552
|
...result
|
|
2553
2553
|
});
|
|
2554
2554
|
} catch (error) {
|
|
@@ -2566,9 +2566,9 @@ function createGetTriggerDetailHandler(logDir) {
|
|
|
2566
2566
|
message: "instanceID is required"
|
|
2567
2567
|
});
|
|
2568
2568
|
}
|
|
2569
|
-
const
|
|
2569
|
+
const path8 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
|
|
2570
2570
|
try {
|
|
2571
|
-
const result = await readTriggerDetail(traceLogPath,
|
|
2571
|
+
const result = await readTriggerDetail(traceLogPath, path8, instanceID);
|
|
2572
2572
|
if (!result) {
|
|
2573
2573
|
return handleNotFound(res, traceLogPath);
|
|
2574
2574
|
}
|
|
@@ -3194,8 +3194,8 @@ __name(createSSEHandler, "createSSEHandler");
|
|
|
3194
3194
|
|
|
3195
3195
|
// src/middlewares/dev-logs/api-list-handler.ts
|
|
3196
3196
|
var SERVER_PORT = process.env.SERVER_PORT || "3000";
|
|
3197
|
-
function extractModuleFromPath(
|
|
3198
|
-
const segments =
|
|
3197
|
+
function extractModuleFromPath(path8) {
|
|
3198
|
+
const segments = path8.split("/").filter(Boolean);
|
|
3199
3199
|
let startIndex = 0;
|
|
3200
3200
|
if (segments[0] === "api") {
|
|
3201
3201
|
startIndex = 1;
|
|
@@ -3210,8 +3210,8 @@ function extractModuleFromPath(path5) {
|
|
|
3210
3210
|
return moduleName;
|
|
3211
3211
|
}
|
|
3212
3212
|
__name(extractModuleFromPath, "extractModuleFromPath");
|
|
3213
|
-
function generateRouteId(method,
|
|
3214
|
-
const cleanPath =
|
|
3213
|
+
function generateRouteId(method, path8) {
|
|
3214
|
+
const cleanPath = path8.replace(/[/:]/g, "_").replace(/^_+|_+$/g, "");
|
|
3215
3215
|
return `${method.toLowerCase()}_${cleanPath}`;
|
|
3216
3216
|
}
|
|
3217
3217
|
__name(generateRouteId, "generateRouteId");
|
|
@@ -3685,17 +3685,217 @@ function registerMiddlewares(server, middlewares, options) {
|
|
|
3685
3685
|
}
|
|
3686
3686
|
}
|
|
3687
3687
|
__name(registerMiddlewares, "registerMiddlewares");
|
|
3688
|
+
|
|
3689
|
+
// src/health/resolve.ts
|
|
3690
|
+
import fs11 from "fs";
|
|
3691
|
+
import path6 from "path";
|
|
3692
|
+
|
|
3693
|
+
// src/health/server-liveness.ts
|
|
3694
|
+
import fs10 from "fs";
|
|
3695
|
+
import path5 from "path";
|
|
3696
|
+
var TAIL_BYTES = 64 * 1024;
|
|
3697
|
+
function isListenMarker(line) {
|
|
3698
|
+
const message = String(line.message ?? "");
|
|
3699
|
+
if (line.context === "Bootstrap" && message.startsWith("Server running on")) {
|
|
3700
|
+
return true;
|
|
3701
|
+
}
|
|
3702
|
+
return line.context === "PlatformHealth" && message.startsWith("listening");
|
|
3703
|
+
}
|
|
3704
|
+
__name(isListenMarker, "isListenMarker");
|
|
3705
|
+
function readLastListenMarker(logFile) {
|
|
3706
|
+
let fd;
|
|
3707
|
+
try {
|
|
3708
|
+
const size = fs10.statSync(logFile).size;
|
|
3709
|
+
if (size === 0) return null;
|
|
3710
|
+
const length = Math.min(size, TAIL_BYTES);
|
|
3711
|
+
const buf = Buffer.alloc(length);
|
|
3712
|
+
fd = fs10.openSync(logFile, "r");
|
|
3713
|
+
fs10.readSync(fd, buf, 0, length, size - length);
|
|
3714
|
+
const lines = buf.toString("utf8").split("\n");
|
|
3715
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3716
|
+
const raw = lines[i].trim();
|
|
3717
|
+
if (!raw.startsWith("{")) continue;
|
|
3718
|
+
try {
|
|
3719
|
+
const parsed = JSON.parse(raw);
|
|
3720
|
+
if (isListenMarker(parsed)) return parsed;
|
|
3721
|
+
} catch {
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
} catch {
|
|
3725
|
+
} finally {
|
|
3726
|
+
if (fd !== void 0) {
|
|
3727
|
+
try {
|
|
3728
|
+
fs10.closeSync(fd);
|
|
3729
|
+
} catch {
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3733
|
+
return null;
|
|
3734
|
+
}
|
|
3735
|
+
__name(readLastListenMarker, "readLastListenMarker");
|
|
3736
|
+
function isPidAlive(pid) {
|
|
3737
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
3738
|
+
try {
|
|
3739
|
+
process.kill(pid, 0);
|
|
3740
|
+
return true;
|
|
3741
|
+
} catch (err) {
|
|
3742
|
+
return err?.code === "EPERM";
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
__name(isPidAlive, "isPidAlive");
|
|
3746
|
+
function resolveServerLiveness(logDir, sessionStartedAt) {
|
|
3747
|
+
const marker = readLastListenMarker(path5.join(logDir, "server.log"));
|
|
3748
|
+
if (!marker || typeof marker.pid !== "number") {
|
|
3749
|
+
return {
|
|
3750
|
+
alive: false,
|
|
3751
|
+
pid: null,
|
|
3752
|
+
listenAt: null
|
|
3753
|
+
};
|
|
3754
|
+
}
|
|
3755
|
+
const listenAt = typeof marker.time === "string" ? marker.time : null;
|
|
3756
|
+
if (sessionStartedAt && listenAt) {
|
|
3757
|
+
const markerMs = Date.parse(listenAt);
|
|
3758
|
+
if (Number.isFinite(markerMs) && markerMs < sessionStartedAt - 5e3) {
|
|
3759
|
+
return {
|
|
3760
|
+
alive: false,
|
|
3761
|
+
pid: marker.pid,
|
|
3762
|
+
listenAt
|
|
3763
|
+
};
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
return {
|
|
3767
|
+
alive: isPidAlive(marker.pid),
|
|
3768
|
+
pid: marker.pid,
|
|
3769
|
+
listenAt
|
|
3770
|
+
};
|
|
3771
|
+
}
|
|
3772
|
+
__name(resolveServerLiveness, "resolveServerLiveness");
|
|
3773
|
+
|
|
3774
|
+
// src/health/resolve.ts
|
|
3775
|
+
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
3776
|
+
function resolveHealth(options) {
|
|
3777
|
+
const { rootDir, hasServer, clientReady, firstStartedAt, logDir = path6.join(rootDir, "logs"), serverEntry = path6.join(rootDir, "dist", "server", "main.js"), timeoutMs = DEFAULT_TIMEOUT_MS, now = Date.now, sessionStartedAt = null, onServerAlive } = options;
|
|
3778
|
+
if (!hasServer) {
|
|
3779
|
+
return {
|
|
3780
|
+
ready: clientReady,
|
|
3781
|
+
failed: false
|
|
3782
|
+
};
|
|
3783
|
+
}
|
|
3784
|
+
const liveness = resolveServerLiveness(logDir, sessionStartedAt);
|
|
3785
|
+
if (liveness.alive) {
|
|
3786
|
+
onServerAlive?.();
|
|
3787
|
+
return {
|
|
3788
|
+
ready: clientReady,
|
|
3789
|
+
failed: false
|
|
3790
|
+
};
|
|
3791
|
+
}
|
|
3792
|
+
const elapsed = firstStartedAt ? now() - firstStartedAt : 0;
|
|
3793
|
+
if (!firstStartedAt || elapsed <= timeoutMs) {
|
|
3794
|
+
return {
|
|
3795
|
+
ready: false,
|
|
3796
|
+
failed: false
|
|
3797
|
+
};
|
|
3798
|
+
}
|
|
3799
|
+
const seconds = Math.round(elapsed / 1e3);
|
|
3800
|
+
const compiled = safeExists(serverEntry);
|
|
3801
|
+
const message = compiled ? `\u540E\u7AEF\u670D\u52A1\u542F\u52A8\u8D85\u65F6\uFF08\u5DF2\u7B49\u5F85 ${seconds}s\uFF09\uFF1A\u7F16\u8BD1\u4EA7\u7269\u5DF2\u751F\u6210\u4F46\u670D\u52A1\u672A\u76D1\u542C\uFF0C\u8BF7\u67E5\u770B ${path6.join(logDir, "server.std.log")}` : `\u540E\u7AEF\u670D\u52A1\u7F16\u8BD1\u672A\u901A\u8FC7\uFF08\u5DF2\u7B49\u5F85 ${seconds}s\uFF09\uFF1A\u672A\u751F\u6210 ${path6.relative(rootDir, serverEntry)}\uFF0C\u8BF7\u67E5\u770B ${path6.join(logDir, "server.std.log")}`;
|
|
3802
|
+
return {
|
|
3803
|
+
ready: false,
|
|
3804
|
+
failed: true,
|
|
3805
|
+
message
|
|
3806
|
+
};
|
|
3807
|
+
}
|
|
3808
|
+
__name(resolveHealth, "resolveHealth");
|
|
3809
|
+
function safeExists(file) {
|
|
3810
|
+
try {
|
|
3811
|
+
return fs11.existsSync(file);
|
|
3812
|
+
} catch {
|
|
3813
|
+
return false;
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
__name(safeExists, "safeExists");
|
|
3817
|
+
|
|
3818
|
+
// src/health/handler.ts
|
|
3819
|
+
import fs12 from "fs";
|
|
3820
|
+
import path7 from "path";
|
|
3821
|
+
var HEALTH_ENDPOINT = "/dev/health";
|
|
3822
|
+
function resolveTimeoutMs(explicit) {
|
|
3823
|
+
if (typeof explicit === "number") return explicit;
|
|
3824
|
+
const raw = Number(process.env.MIAODA_HEALTH_TIMEOUT_MS);
|
|
3825
|
+
return Number.isFinite(raw) && raw > 0 ? raw : void 0;
|
|
3826
|
+
}
|
|
3827
|
+
__name(resolveTimeoutMs, "resolveTimeoutMs");
|
|
3828
|
+
function readSessionStartedAt(logDir) {
|
|
3829
|
+
try {
|
|
3830
|
+
const raw = fs12.readFileSync(path7.join(logDir, "dev-status.json"), "utf8");
|
|
3831
|
+
const parsed = JSON.parse(raw);
|
|
3832
|
+
return typeof parsed.firstStartedAt === "number" ? parsed.firstStartedAt : null;
|
|
3833
|
+
} catch {
|
|
3834
|
+
return null;
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
__name(readSessionStartedAt, "readSessionStartedAt");
|
|
3838
|
+
function createHealthProbe(options) {
|
|
3839
|
+
const logDir = options.logDir ?? path7.join(options.rootDir, "logs");
|
|
3840
|
+
let lastAliveAt = null;
|
|
3841
|
+
return /* @__PURE__ */ __name(function probe() {
|
|
3842
|
+
try {
|
|
3843
|
+
const sessionStartedAt = readSessionStartedAt(logDir);
|
|
3844
|
+
const since = lastAliveAt !== null && (sessionStartedAt === null || lastAliveAt > sessionStartedAt) ? lastAliveAt : sessionStartedAt;
|
|
3845
|
+
const payload = resolveHealth({
|
|
3846
|
+
rootDir: options.rootDir,
|
|
3847
|
+
logDir,
|
|
3848
|
+
hasServer: options.hasServer,
|
|
3849
|
+
clientReady: options.isClientReady(),
|
|
3850
|
+
firstStartedAt: since,
|
|
3851
|
+
timeoutMs: resolveTimeoutMs(options.timeoutMs),
|
|
3852
|
+
onServerAlive: /* @__PURE__ */ __name(() => {
|
|
3853
|
+
lastAliveAt = Date.now();
|
|
3854
|
+
}, "onServerAlive")
|
|
3855
|
+
});
|
|
3856
|
+
return payload;
|
|
3857
|
+
} catch {
|
|
3858
|
+
return {
|
|
3859
|
+
ready: false,
|
|
3860
|
+
failed: false
|
|
3861
|
+
};
|
|
3862
|
+
}
|
|
3863
|
+
}, "probe");
|
|
3864
|
+
}
|
|
3865
|
+
__name(createHealthProbe, "createHealthProbe");
|
|
3866
|
+
function getHealthPayload(options) {
|
|
3867
|
+
return createHealthProbe(options)();
|
|
3868
|
+
}
|
|
3869
|
+
__name(getHealthPayload, "getHealthPayload");
|
|
3870
|
+
function createHealthHandler(options) {
|
|
3871
|
+
const probe = createHealthProbe(options);
|
|
3872
|
+
return /* @__PURE__ */ __name(function healthHandler(req, res, next) {
|
|
3873
|
+
if (req.method && req.method !== "GET" && next) {
|
|
3874
|
+
return next();
|
|
3875
|
+
}
|
|
3876
|
+
res.setHeader("Content-Type", "application/json");
|
|
3877
|
+
res.setHeader("Cache-Control", "no-store");
|
|
3878
|
+
res.statusCode = 200;
|
|
3879
|
+
res.end(JSON.stringify(probe()));
|
|
3880
|
+
}, "healthHandler");
|
|
3881
|
+
}
|
|
3882
|
+
__name(createHealthHandler, "createHealthHandler");
|
|
3688
3883
|
export {
|
|
3884
|
+
HEALTH_ENDPOINT,
|
|
3689
3885
|
buildFullPath,
|
|
3690
3886
|
calculateFileHash,
|
|
3691
3887
|
createCollectLogsMiddleware,
|
|
3692
3888
|
createDevLogsMiddleware,
|
|
3889
|
+
createHealthHandler,
|
|
3890
|
+
createHealthProbe,
|
|
3693
3891
|
createOpenapiMiddleware,
|
|
3694
3892
|
evaluateTemplateLiteral,
|
|
3695
3893
|
extractPageRouteInfo,
|
|
3894
|
+
getHealthPayload,
|
|
3696
3895
|
getQuery,
|
|
3697
3896
|
getQueryParam,
|
|
3698
3897
|
handleDevProxyError,
|
|
3898
|
+
isPidAlive,
|
|
3699
3899
|
isRouteComponent,
|
|
3700
3900
|
normalizeBasePath,
|
|
3701
3901
|
parseAndGenerateNestResourceTemplate,
|
|
@@ -3703,6 +3903,8 @@ export {
|
|
|
3703
3903
|
parseRoutesFromFile,
|
|
3704
3904
|
postprocessDrizzleSchema,
|
|
3705
3905
|
registerMiddlewares,
|
|
3906
|
+
resolveHealth,
|
|
3907
|
+
resolveServerLiveness,
|
|
3706
3908
|
routeParserLog,
|
|
3707
3909
|
sendError,
|
|
3708
3910
|
sendJson,
|