/* PLAN 323-9 ① — B06 [저장] 뒤 카드 = 서버 새 상세. 진짜 `design_save.ts`(저장 차례) · * `B06_Section_UI_Page_Persist.ts`(횡단 뒤처리) · `B06_Section_Section_Store.ts`(상세 캐시)를 Node 로 돌림. * 서버 창구만 가짜 — 상세는 저장 전(옛 자리) · 저장 뒤(새 자리) 두 벌. */ const path = require("path"); const { makeLoader, memoryStorage } = require("./helper_ts_stub_loader.cjs"); const CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1) const ROOT = path.join(__dirname, "..", ".."); globalThis.window = { sessionStorage: memoryStorage(), localStorage: memoryStorage() }; globalThis.sessionStorage = globalThis.window.sessionStorage; const calls = []; let server = "옛 자리 4+5"; const detail = () => ({ cross_sections: [], longitudinal: { stations: [] }, tag: server }); const B06 = path.join(ROOT, "B06_Section"); const STUBS = { "@ui/ui_template_elements": { showToast: (_text, kind) => calls.push(`toast:${kind}`), showLoadingOverlay: () => calls.push("overlay:on"), hideLoadingOverlay: () => calls.push("overlay:off"), }, // 노선 저장(346-3) — 이 시험은 노선이 그대로인 [저장]만 봄 [path.join(ROOT, "B05_Profile", "B05_Profile_UI_RouteEdit_Apply.ts")]: { pendingRouteSave: async () => null, }, [path.join(B06, "B06_Section_Api_Fetch.ts")]: { fetchSectionDetail: async () => (calls.push(`fetch:${server}`), detail()), saveSections: async () => calls.push("cross:save"), confirmSections: async () => calls.push("cross:confirm"), sectionConfirmBody: () => ({}), warnSectionSave: () => undefined, }, }; // 472-6 한 본문 창구 — 서버가 차례대로 정본을 쓰는 동안 가게가 바뀜(바뀜 신호 = 저장 도중) → 횡단 저장 let duringSave = async () => undefined; STUBS[path.join(ROOT, "A00_Common", "design", "design_save_api.ts")] = { SAVE_PART_KEYS: [ "settings", "pipes", "structures", "plusStations", "alignment", "culvertOptions", "uphill", "cross", ], DesignSaveError: class extends Error {}, postDesignSave: async (_p, _r, confirm, parts) => { await duringSave(); if (parts.cross?.body) calls.push(confirm ? "cross:confirm" : "cross:save"); return { waited: false, steps: {} }; }, }; // 라우터(302-1 draft 길 `import.meta.glob`) — 이 로더로 못 읽음 · 저장 차례는 지금 길만 봄 STUBS[path.join(ROOT, "A00_Common", "router.ts")] = { currentRoute: () => "", remountRoute: () => undefined, navigateTo: () => undefined, setLeaveGuard: () => undefined, }; const anyModule = () => new Proxy({}, { get: () => () => undefined }); const { loadTs } = makeLoader({ root: ROOT, fileStub: (full) => STUBS[full], requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (CATALOG.isCatalogRequest(request)) return CATALOG; if (request.endsWith(".json")) return require(path.join(path.dirname(full), request)); if (request.endsWith(".css")) return {}; if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); if (request.startsWith("@")) return anyModule(); return require(request); }, }); // 측점 열쇠 한 벌(344-3 · 346-2)은 진짜로 — 빈 대역은 열쇠 목록을 못 냄 STUBS["@util/common_util_station_key"] = loadTs( path.join(ROOT, "common_util", "common_util_station_key.ts"), ); // 상단측 차례 본문(463 · 지표 조건 · 확정 지표면 GET · 확정 POST) — 이 시험은 횡단 뒤처리만 봄 const B05_FETCH = path.join(ROOT, "B05_Profile", "B05_Profile_Api_Fetch.ts"); STUBS[B05_FETCH] = { ...loadTs(B05_FETCH), flushRouteParts: async () => undefined, routePartsBody: async () => ({}), }; const { saveDesign, savingDesign } = loadTs( path.join(ROOT, "A00_Common", "design", "design_save.ts"), ); const { crossSaveParts } = loadTs(path.join(B06, "B06_Section_UI_Page_Persist.ts")); const { invalidateSectionDetail, loadSectionDetail } = loadTs( path.join(B06, "B06_Section_Section_Store.ts"), ); (async () => { const P = "p1"; const R = 7; // 횡단 보기 = 바뀜 신호마다 `refreshDetailForStructures` 와 같은 차례(최신만 · 저장 도중 안 그림) let shown = null; let seq = 0; const refresh = async () => { const mine = ++seq; if (savingDesign(P)) return calls.push("refresh:skip"); const got = await loadSectionDetail(P, R); if (mine === seq) shown = got.tag; }; await refresh(); // 저장 전 화면(캐시 = 옛 서버) const before = shown; const ctx = { projectId: P, routeId: () => R, detail: () => null, context: () => null, standardValues: () => undefined, flushCulvertOptions: async () => undefined, patchSources: () => Object.fromEntries( ["rockOffsets", "cutSlopeRatios", "stationWidths", "inletStructures", "basinAdjustments"] .concat(["revetAdjusts", "extraCounts", "extraSpans", "fordAdjusts", "boxAdjusts"]) .concat(["linkFlags"]) .map((key) => [key, new Map()]), ), reconcileDesigns: async () => undefined, redraw: refresh, }; const parts = crossSaveParts(ctx); calls.length = 0; const savingSeen = []; // 관 · 구조물 초안이 정본으로 나가며 가게가 바뀜 → 바뀜 신호(저장 도중) duringSave = async () => { server = "새 자리 4+8 · 47+0"; savingSeen.push(savingDesign(P)); await refresh(); }; const ok = await saveDesign({ projectId: P, routeId: () => R, parts: { // 구조물 · 추가 측점 — 초안 없음 · 이 시험은 안 봄(진짜는 서버 GET) structures: () => null, plusStations: () => null, ...parts.parts, }, crossPart: (confirm) => parts.crossPart(confirm), // 뒤처리 = 한 벌(`design_shared` saveSharedDesign done) 차례 그대로 — 공용 캐시를 한 번 비운 뒤 보기 몫(416 M1 · // 횡단 보기 done 은 비우지 않고 다시 그리기만) done: async () => { savingSeen.push(savingDesign(P)); invalidateSectionDetail(P); await parts.done(false); }, failed: (error) => calls.push(`failed:${error}`), }); process.stdout.write( JSON.stringify({ ok, before, after: shown, savingSeen, calls, savingIdle: savingDesign(P) }), ); })();