/* PLAN 323-9 ② — 정의 판 알림 되풀이. 진짜 `B05_Profile_Api_Structures.ts`(판 알림 · 정본 내보내기)를 * Node 로 돌림. 서버는 가짜 한 벌 — 구조물 PUT 이 지금 정의 판을 도장(진짜 저장소와 같음). */ const fs = require("fs"); const path = require("path"); const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1) const ROOT = path.join(__dirname, "..", ".."); const memory = () => { 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; }, }; }; globalThis.window = { sessionStorage: memory(), localStorage: memory(), setTimeout: () => 0, clearTimeout: () => undefined, }; globalThis.sessionStorage = globalThis.window.sessionStorage; // 가짜 서버 — 정의 판 21 · 프로젝트 정본은 판 20 으로 적힘 const NOW = 21; const server = { revision: 3, structures: [{ structure_id: "s1", type_id: "retaining_wall", chainage_m: 40 }], definition_version: 20, }; const puts = []; const reply = (body) => ({ ok: true, status: 200, json: async () => body }); globalThis.fetch = async (url, init = {}) => { const at = url.replace(/^.*?(?=\/(m02|projects))/, ""); if (at === "/m02/structure-definition") return reply({ 판: NOW, types: [] }); if (at === "/projects/structure-types") return reply({ types: [] }); if (at.endsWith("/route/structures") && init.method === "PUT") { const body = JSON.parse(init.body); puts.push(body.structures.length); Object.assign(server, { revision: server.revision + 1, structures: body.structures, definition_version: NOW, }); return reply({ revision: server.revision }); } if (at.endsWith("/route/structures")) return reply({ ...server }); throw new Error(`모르는 요청 ${at}`); }; const { loadTs } = makeLoader({ root: ROOT, requireFor: (full) => (request) => { if (CATALOG.isCatalogRequest(request)) return CATALOG; if (request === "@config/config_frontend") return { API_BASE_URL: "", API_TIMEOUT_MS: 1000 }; 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 new Proxy({}, { get: () => () => undefined }); return require(request); }, }); const api = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_Api_Structures.ts")); // 472-6 — B06 [저장] = 한 본문 구조물 조각(옛 기본 창구 `flushPendingStructures` 걷음) · 서버 창구 = 가짜 PUT async function saveLikeOneBody(projectId) { const part = await api.structuresPart(projectId); if (!part) return; await api.saveStructures(projectId, part.body.base_revision, part.body.structures); await part.after?.(undefined); } (async () => { const P = "p1"; // 열기 — 종류(정의 판) · 정본(옛 판) → 알림 한 줄 · 옛 판 표시 await api.fetchStructureTypes(true); const opened = await api.fetchStructures(P); const notice = api.staleDefinitionNotice(P, opened.definition_version); const staleOnOpen = api.definitionStale(P); // 고침 없음 [저장](B06 기본 창구) — 정본을 그대로 다시 써 판 도장 await saveLikeOneBody(P); const afterSave = { version: server.definition_version, puts: [...puts] }; // 다시 열기 — 알림 없음 · 또 [저장] 해도 안 나감 const reopened = await api.fetchStructures(P); const again = api.staleDefinitionNotice("p1-새탭", reopened.definition_version); api.staleDefinitionNotice(P, reopened.definition_version); await saveLikeOneBody(P); process.stdout.write( JSON.stringify({ notice, staleOnOpen, afterSave, again, staleAfter: api.definitionStale(P), putsAfterSecond: puts.length, structures: server.structures, }), ); })();