/* 346-3 노선 저장 이음 헬퍼 — `B05_Profile_UI_RouteEdit_Apply.ts` 를 트랜스파일해 서버 · 화면 창구는 가짜로, * 가게 · 캐시(`design_store` · `b_page_state`)는 진짜로 돌림. 보낸 본문 · 부른 차례를 적어 JSON 한 벌로 냄. */ const fs = require("fs"); const path = require("path"); const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); function memoryStorage() { const map = new Map(); return { getItem: (key) => (map.has(key) ? map.get(key) : null), setItem: (key, value) => map.set(key, String(value)), removeItem: (key) => map.delete(key), key: (index) => [...map.keys()][index] ?? null, get length() { return map.size; }, }; } const session = memoryStorage(); globalThis.window = { sessionStorage: session, localStorage: memoryStorage() }; globalThis.sessionStorage = session; const calls = []; const bodies = []; let failReplan = false; const STUBS = { "@ui/ui_template_elements": { showToast: (text, kind) => calls.push(`toast:${kind}:${text}`), showLoadingOverlay: () => calls.push("overlay:on"), hideLoadingOverlay: () => calls.push("overlay:off"), }, "../A00_Common/router": { currentRoute: () => "b05-profile", remountRoute: () => calls.push("remount"), }, "../A00_Common/design/design_pipeline_page": { commitRouteFacilities: () => calls.push("commit"), // 저장 = 초안 전 관 · 구조물 버림(6-B) }, "../B06_Section/B06_Section_Section_Store": { invalidateSectionDetail: (_p, routeId) => calls.push(`invalidate:${routeId ?? "all"}`), }, // 6-B — 넘김은 서버(Node · 같은 TS) · 여기서는 옛 노선 세션 칸 원본만 냄 "../B06_Section/B06_Section_Station_Keys": { routeCarryInputs: (_p, from) => (calls.push(`carryInputs:${from}`), { from_keys: [], from }), }, "../B06_Section/B06_Section_Api_Fetch": { sectionConfirmBody: (standard, patches, massHaul) => ({ standard, patches, massHaul }), }, "../B06_Section/B06_Section_UI_Standard_Panel": { readStandardCrossSession: () => ({ std: 1 }) }, "@util/common_util_mass_haul_balance_view": { balloonOffsetsPayload: () => ({ b: [1, 2] }) }, "./B05_Profile_Api_Fetch": { clearRouteLatestCache: () => calls.push("latest:clear"), }, // 509-2 — 추천 [적용] 뒤 받아 둔 설계 지형 격자 버림 "./B05_Profile_Api_TerrainGrid": { forgetDesignTerrainGrid: () => calls.push("grid:forget"), }, "./B05_Profile_Api_Replan": { replanRoute: async (_p, body) => { bodies.push(JSON.parse(JSON.stringify(body))); calls.push("replan"); if (failReplan) throw new Error("체인 실패"); return { route_id: 9, pipes: { kept: 3, crossing: 1 }, structures: { moved: 1, far: 0 }, cross_dropped: 1, drainage_stale: true, }; }, }, }; const { loadTs } = makeLoader({ root: ROOT, requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); }, }); const { useDesignStore } = loadTs(path.join(ROOT, "A00_Common", "design", "design_store.ts")); const { readState, writeState } = loadTs(path.join(ROOT, "A00_Common", "b_page_state.ts")); const apply = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_RouteEdit_Apply.ts")); (async () => { const out = {}; const draft = { rev: 3, nodes: [ [0, 0], [5, 5], [10, 0], ], curveOn: [true, true, true], }; // 편집기 없음 — 가게 routeDraft 를 보냄 · 없으면 null out.noneAtAll = await apply.pendingRouteSave("p1"); useDesignStore("p1", null).set("routeDraft", draft); out.storeOnly = await apply.pendingRouteSave("p1"); // 편집기 등록 — 안 바뀌었으면 null(가게에 초안이 있어도 편집기가 정함) · 바뀌면 계약 꼴 let changed = false; let kept = 0; let rebuilt = 0; apply.bindRouteApply({ projectId: "p1", nodes: () => ({ planned: [ [0, 0], [5, 5], [10, 0], ], curveOn: [true, false], curveRadius: [null, 25], }), changed: () => changed, snap: () => ({ stationSnap: true, stationSnapStep: 20 }), // 390-5 — 켬끔 · 간격 한 벌 initial: { current: () => ({ mode: "follow" }), keep: () => (kept += 1) }, onApplied: () => (rebuilt += 1), }); out.unchanged = await apply.pendingRouteSave("p1"); changed = true; const body = await apply.pendingRouteSave("p1"); out.body = body; // 저장 — 가게 초안 비움 · 결과 캐시 비움 · 추천 다시 적음 writeState("design-stations", [{ k: 1 }], "p1"); const saved = await apply.saveRouteDraft("p1", body); out.savedRouteId = saved.route_id; out.storeAfterSave = useDesignStore("p1", null).get("routeDraft"); out.keptAfterSave = kept; out.summary = typeof apply.routeSaveSummary; // 498-3 — 결과 한 줄 걷음 // 노선 바뀐 [저장] 횡단 조각(6-B) — 옛 노선 세션 칸 원본 + 표준 횡단 · 풍선 · 저장 뒤 상단측 · 종류 바꿈 비움 calls.length = 0; writeState("uphill", { "40.00": "right" }, "p1"); writeState("kindreset", { "45.00": "culvert" }, "p1"); const part = apply.routeCarryPart("p1", 7); out.carry = part.carry; out.crossBody = part.cross.body; out.uphillBeforeAfter = readState("uphill", "p1"); await part.cross.after(undefined); out.uphillAfter = readState("uphill", "p1"); out.kindresetAfter = readState("kindreset", "p1"); out.carryCalls = [...calls]; // 추천 [적용] — as_initial · 잠금 막 · 초안 비움 · 다시 세움 useDesignStore("p1", null).set("routeDraft", draft); writeState("pipes", [{ chainage_m: 1 }], "p1"); calls.length = 0; const snapshot = { planned: [ [1, 1], [9, 9], ], curveOn: [true, true], curveRadius: [null, null], }; out.recommendOk = await apply.saveRecommendedRoute( "p1", snapshot, { mode: "earthwork" }, true, "sheet", // 509-5 — 창에서 고른 지형 ); out.recommendBody = bodies[bodies.length - 1]; out.recommendCalls = calls.filter((c) => !c.startsWith("toast:")); out.recommendDraftsLeft = { route: useDesignStore("p1", null).get("routeDraft"), pipes: readState("pipes", "p1"), }; out.rebuilt = rebuilt; // 추천 저장 실패 — false · 오류 한 줄 · 다시 안 세움 failReplan = true; calls.length = 0; out.recommendFail = await apply.saveRecommendedRoute("p1", snapshot, null, false); out.recommendFailCalls = [...calls]; out.rebuiltAfterFail = rebuilt; console.log(JSON.stringify(out)); })();