- packages/shared:TypeScript 專案參考建置鏈路、統一日誌模組(Asia/Taipei 格式) - apps/api:最小 NestJS 服務,GET /health - apps/web:最小 Next.js(App Router + Tailwind v4)首頁,套用品牌色票與亮暗模式 - scripts/dev-restart.mjs:讀 .dev-pids 以 process group 訊號安全停止舊行程、背景啟動 api/web 並輪詢健康檢查 - scripts/smoke:npm run smoke -- <群組代號> 冒煙測試框架,A 群組測試涵蓋 /health 與首頁內容 - 本機 port 3000 被既有服務佔用,web 開發埠改為 3100(已記錄於 todo.md) npm run restart && npm run smoke -- A 皆通過(A-V)。
30 lines
911 B
TypeScript
30 lines
911 B
TypeScript
export type LogLevel = "INF" | "WRN" | "ERR" | "TRC" | "DBG";
|
|
|
|
const TAIPEI_FORMATTER = new Intl.DateTimeFormat("en-CA", {
|
|
timeZone: "Asia/Taipei",
|
|
year: "numeric",
|
|
month: "2-digit",
|
|
day: "2-digit",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
second: "2-digit",
|
|
hour12: false,
|
|
});
|
|
|
|
export function formatTaipeiTimestamp(date: Date = new Date()): string {
|
|
const parts = TAIPEI_FORMATTER.formatToParts(date);
|
|
const get = (type: string) => parts.find((p) => p.type === type)?.value ?? "00";
|
|
return `${get("year")}/${get("month")}/${get("day")} ${get("hour")}:${get("minute")}:${get("second")}`;
|
|
}
|
|
|
|
export function log(stage: string, level: LogLevel, message: string): void {
|
|
const line = `[${formatTaipeiTimestamp()}][${stage}][${level}]: ${message}`;
|
|
if (level === "ERR") {
|
|
console.error(line);
|
|
} else if (level === "WRN") {
|
|
console.warn(line);
|
|
} else {
|
|
console.log(line);
|
|
}
|
|
}
|