// 저장 왕복 시험용 — 화면 TS 를 node 로 묶어 저장 본문을 만든다. // node helper_b05_save_payload.mjs <저장소 뿌리> · 표준입력 = 작업 목록(JSON) · 표준출력 끝 줄 = 결과 목록 // {op:"attach", entries:[관 응답], moves:[[옛, 새]], points:[{chainage_m, source}]} → attach 결과 // {op:"station", chainage, interval} → 구조물 측점 칸에 쓰고 다시 읽은 값 // {op:"culvert", server:[관 응답], pending:{측점:조각}, moves:{측점:새 측점}} → PUT 본문 points 또는 null import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; const root = path.resolve(process.argv[2] ?? "."); const { build } = await import( pathToFileURL(path.join(root, "config/node_modules/vite/dist/node/index.js")).href ); // 화면 칸 · 세션만 흉내 — 측점 칸은 값 · 클래스 · 이벤트만 쓴다 class Node { constructor() { this.value = ""; this.children = []; this.classList = { toggle() {}, add() {}, remove() {} }; this.style = {}; this.dataset = {}; } append(...kids) { this.children.push(...kids); } appendChild(kid) { this.children.push(kid); return kid; } addEventListener() {} setAttribute() {} } const session = new Map(); globalThis.document = { createElement: () => new Node(), createTextNode: () => new Node() }; globalThis.window = { sessionStorage: { getItem: (k) => (session.has(k) ? session.get(k) : null), setItem: (k, v) => session.set(k, String(v)), removeItem: (k) => session.delete(k), }, location: { origin: "http://localhost" }, setTimeout: (...a) => setTimeout(...a), clearTimeout: (id) => clearTimeout(id), }; globalThis.sessionStorage = globalThis.window.sessionStorage; globalThis.localStorage = globalThis.window.sessionStorage; const outDir = fs.mkdtempSync(path.join(os.tmpdir(), "aislo-save-payload-")); try { await build({ configFile: false, logLevel: "silent", root, resolve: { alias: { "@ui": path.join(root, "ui_template"), "@config": path.join(root, "config"), "@util": path.join(root, "common_util"), }, }, build: { outDir, minify: false, target: "es2022", lib: { entry: path.join(root, "resources/tester/helper_b05_save_payload_entry.ts"), formats: ["es"], fileName: "entry", }, }, }); const mod = await import(pathToFileURL(path.join(outDir, "entry.js")).href); const jobs = JSON.parse(fs.readFileSync(0, "utf-8")); const results = []; for (const job of jobs) { if (job.op === "attach") { const store = mod.createFacilityStore(); store.replaceFromResponse(job.entries); // 지도에서 끈 관 — 화면이 끌기를 마치면 속성을 새 측점으로 옮김(onMove) for (const [from, to] of job.moves ?? []) store.move(from, to); results.push(store.attach(job.points)); } else if (job.op === "station") { const fields = mod.stationFields("측점", () => job.interval); fields.write(job.chainage, job.interval); results.push(fields.read(job.interval, true)); } else if (job.op === "culvert") { session.clear(); session.set("opt", JSON.stringify(job.pending ?? {})); session.set("move", JSON.stringify(job.moves ?? {})); let sent = null; globalThis.fetch = async (_url, init = {}) => { const body = init.method === "PUT" ? JSON.parse(init.body) : null; if (body) sent = body.points; const payload = body ? { pipe_points: body.points } : { pipe_points: job.server }; return new Response(JSON.stringify(payload), { status: 200, headers: { "Content-Type": "application/json" }, }); }; await mod.flushCulvertOptions("p", "opt", "move"); results.push(sent); } else { throw new Error(`모르는 작업 ${job.op}`); } } console.log(JSON.stringify(results)); } finally { fs.rmSync(outDir, { recursive: true, force: true }); }