/** * Magic vars — workflow YAML 內建變數 * * 對應 LI SDD M2.x improvement(feedback block c47bf70b)。 * * 任何以 `_` 開頭的變數名都是 reserved(system)。常見:時間、執行 metadata。 * 用於 page_name / file path / URL 等需要時間戳的場景。 * * 範例 YAML: * page_name: "roadmap-week-{{_iso_week}}" # roadmap-week-2026-W20 * page_name: "log-{{_today}}" # log-2026-05-16 * filename: "snapshot-{{_now_unix}}.json" # snapshot-1778940000123.json * * 不違反 §2.2:這是 orchestrator routing 提供的「環境變數」(像 shell 的 $DATE), * 不涉及 secret / credential / JWT,跟既有 ctx 變數展開同層。 */ /** * 算 ISO 8601 週數(W01-W53)。 * 週一為週首,W01 含當年首個週四(ISO 標準)。 * https://en.wikipedia.org/wiki/ISO_week_date */ function isoWeekNumber(d: Date): { year: number; week: number } { const target = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())); const dayNum = (target.getUTCDay() + 6) % 7; // Mon=0 target.setUTCDate(target.getUTCDate() - dayNum + 3); const firstThursday = new Date(Date.UTC(target.getUTCFullYear(), 0, 4)); const weekNum = 1 + Math.round( ((target.getTime() - firstThursday.getTime()) / 86400000 - 3 + ((firstThursday.getUTCDay() + 6) % 7)) / 7 ); return { year: target.getUTCFullYear(), week: weekNum }; } function pad2(n: number): string { return n.toString().padStart(2, '0'); } /** * 建立 magic vars。每次 workflow 觸發時呼叫一次,貫穿整個執行。 * * 設計:UTC 為基準(避免 worker 跨時區誤判)。需要本地時區的場景, * 用戶可自己組(例如 yaml 寫 `{{_today_utc}}` + 自己處理偏移)。 */ export function buildMagicVars(now: Date = new Date()): Record { const iso = now.toISOString(); // 2026-05-16T09:30:00.123Z const yyyy = now.getUTCFullYear(); const mm = pad2(now.getUTCMonth() + 1); const dd = pad2(now.getUTCDate()); const hh = pad2(now.getUTCHours()); const mi = pad2(now.getUTCMinutes()); const ss = pad2(now.getUTCSeconds()); const yesterday = new Date(now.getTime() - 86400000); const yMm = pad2(yesterday.getUTCMonth() + 1); const yDd = pad2(yesterday.getUTCDate()); const { year: isoYear, week: isoWeek } = isoWeekNumber(now); return { // 日期 / 時間(UTC) _today: `${yyyy}-${mm}-${dd}`, // 2026-05-16 _yesterday: `${yesterday.getUTCFullYear()}-${yMm}-${yDd}`, // 2026-05-15 _now: iso, // ISO 8601 _now_unix: now.getTime(), // unix ms _now_unix_s: Math.floor(now.getTime() / 1000), // unix sec // 個別欄位(給 path / page_name 拼) _year: yyyy, _month: mm, _day: dd, _hour: hh, _minute: mi, _second: ss, // ISO 週(roadmap weekly archive 必備) _iso_week: `${isoYear}-W${pad2(isoWeek)}`, // 2026-W20 _iso_week_num: isoWeek, _iso_year: isoYear, // 簡單時間 slot(cron-friendly) _yyyymm: `${yyyy}${mm}`, // 202605 _yyyymmdd: `${yyyy}${mm}${dd}`, // 20260516 // 週幾(0=週日,1=週一 ... 6=週六;ISO 風格在 _iso_weekday) _weekday: now.getUTCDay(), _iso_weekday: ((now.getUTCDay() + 6) % 7) + 1, // 1=Mon...7=Sun }; }