- 초안 시작 hold = 측점 열쇠 · 횡단 초안 칸 · 가게 관 · 구조물 · 세션 한 칸(route-draft-hold)에도 - 초안 둔 채 새로고침 = 그 한 벌(초안이 옮겨 쓴 세션 칸 · 초안 자리 관을 처음 값으로 안 뜸) - 묶음 초안 전 가게(beforeDraft) = hold 관 · 구조물 — 버리면 초안 전 관 자리로 되씀 - 되돌림 = 초안 얹기 다시(되쓴 관) 뒤 초안 전 열쇠 · 세션 한 벌 지움 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
206 lines
8.7 KiB
JavaScript
206 lines
8.7 KiB
JavaScript
/* 451 헬퍼(440-4) — 노선 초안 둔 채 새로고침: 진짜 상세 가게 · 진짜 측점 열쇠 · 세션(모듈을 다시 세워도 남음).
|
|
* 초안 시작 hold = 세션 한 벌(열쇠 · 횡단 초안 칸 · 관 · 구조물) · 새로고침 뒤 hold = 그 한 벌(초안으로 옮겨 쓴 세션 아님) ·
|
|
* 버림 = 초안 전 칸 · 초안 얹기 다시(되쓴 관으로) 뒤 초안 전 열쇠. 서버 · 초안 얹기는 가짜. 짝 = `test_451_refresh_hold.py`.
|
|
*/
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
|
const CATALOG = require("./helper_322_catalog_module.cjs");
|
|
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
const P = "P451";
|
|
const ROUTE = 3;
|
|
|
|
function memoryStorage() {
|
|
const map = new Map();
|
|
return {
|
|
getItem: (k) => (map.has(k) ? map.get(k) : null),
|
|
setItem: (k, v) => map.set(k, String(v)),
|
|
removeItem: (k) => map.delete(k),
|
|
key: (i) => [...map.keys()][i] ?? null,
|
|
get length() {
|
|
return map.size;
|
|
},
|
|
};
|
|
}
|
|
globalThis.window = { sessionStorage: memoryStorage(), localStorage: memoryStorage() };
|
|
|
|
const anyModule = (extra = {}) =>
|
|
new Proxy(extra, { get: (t, key) => (key in t ? t[key] : () => undefined) });
|
|
|
|
/** 서버 — 부를 때마다 지금 `server` 사본 · `gate` 가 풀릴 때 답함. */
|
|
let server = null;
|
|
let gate = Promise.resolve();
|
|
let asked = 0;
|
|
const keyAt = (sections) => sections.map((s) => `at:${s.chainage_m.toFixed(2)}`);
|
|
const STUBS = {
|
|
"design_pipeline_page.ts": {
|
|
reconcilePagePipeline: async () => 0,
|
|
pipelineOwnsCross: () => false,
|
|
pagePipeline: () => null,
|
|
},
|
|
"design_pipeline_cross.ts": { forceCross: () => undefined },
|
|
"B06_Section_Section_Drafts.ts": {
|
|
forgetDraftStations: () => undefined,
|
|
withDraftShapes: async () => undefined,
|
|
withDraftStations: async (detail) => {
|
|
// 초안 얹기 흉내 — 가게 관 자리(세션 pipes)마다 그 측점 관 표시 · 부른 차례를 적음
|
|
const pipes = state.readState("pipes", P) ?? [];
|
|
calls.push(`stations:${pipes.map((p) => p.chainage_m).join(",")}`);
|
|
for (const s of detail.cross_sections)
|
|
s.pipeAt = pipes.some((p) => p.chainage_m === s.chainage_m);
|
|
},
|
|
supersedeDraftStations: () => undefined,
|
|
},
|
|
"B05_Profile_Api_PlusStations.ts": {
|
|
loadSavedPlusStations: async () => undefined,
|
|
plusStationsOf: () => [],
|
|
},
|
|
"B06_Section_Api_Fetch.ts": anyModule({
|
|
fetchSectionDetail: async () => {
|
|
asked += 1;
|
|
await gate;
|
|
return JSON.parse(JSON.stringify(server));
|
|
},
|
|
}),
|
|
"B06_Section_Api_Culvert_Options.ts": anyModule({ STRUCTURE_ADJUST_KEYS: [] }),
|
|
"B06_Section_Swap_Bus.ts": anyModule(),
|
|
"ui_template_elements.ts": anyModule(),
|
|
"B06_Section_UI_Page_Persist.ts": anyModule({ readCutSlopeSession: () => ({}) }),
|
|
"B06_Section_UI_Standard_Panel.ts": anyModule(),
|
|
"B06_Section_Cross_Design_Session.ts": anyModule({ crossDesignChoices: () => new Map() }),
|
|
"B06_Section_Section_Adjusts.ts": anyModule({ withPipeOptions: async () => undefined }),
|
|
"common_util_cross_design.ts": anyModule(),
|
|
"B06_Section_UI_Cross_Culvert_Wire.ts": anyModule(),
|
|
"B06_Section_UI_Cross_Ford_Geom.ts": anyModule(),
|
|
"B06_Section_UI_Cross_Box_Geom.ts": anyModule(),
|
|
"B06_Section_UI_IrregularStations.ts": anyModule({ PIPE_DISPLAY_NAME: {} }),
|
|
"B05_Profile_UI_IrregularStations.ts": anyModule({ PIPE_DISPLAY_NAME: {} }),
|
|
"config_frontend.ts": anyModule({ PIPE_TYPES: [] }),
|
|
};
|
|
|
|
let cache = new Map();
|
|
const calls = [];
|
|
const resolve = (base) =>
|
|
[base, `${base}.ts`, path.join(base, "index.ts")].find(
|
|
(p) => fs.existsSync(p) && fs.statSync(p).isFile(),
|
|
) ?? `${base}.ts`;
|
|
function loadTs(full) {
|
|
const stub = STUBS[path.basename(full)];
|
|
if (stub) return stub;
|
|
if (cache.has(full)) return cache.get(full);
|
|
const js = ts.transpileModule(fs.readFileSync(full, "utf8"), {
|
|
compilerOptions: {
|
|
module: ts.ModuleKind.CommonJS,
|
|
target: ts.ScriptTarget.ES2020,
|
|
esModuleInterop: true,
|
|
},
|
|
}).outputText;
|
|
const box = { exports: {} };
|
|
cache.set(full, box.exports);
|
|
const near = (request) => {
|
|
if (CATALOG.isCatalogRequest(request)) return CATALOG;
|
|
const alias = { "@util/": "common_util", "@ui/": "ui_template", "@config/": "config" };
|
|
for (const [prefix, dir] of Object.entries(alias))
|
|
if (request.startsWith(prefix))
|
|
return loadTs(resolve(path.join(ROOT, dir, request.slice(prefix.length))));
|
|
return loadTs(resolve(path.join(path.dirname(full), request)));
|
|
};
|
|
new Function("exports", "module", "require", js)(box.exports, box, near);
|
|
return box.exports;
|
|
}
|
|
|
|
let store = loadTs(path.join(ROOT, "B06_Section", "B06_Section_Section_Store.ts"));
|
|
const state = loadTs(path.join(ROOT, "A00_Common", "b_page_state.ts"));
|
|
let keys = loadTs(path.join(ROOT, "B06_Section", "B06_Section_Station_Keys.ts"));
|
|
/** 새로고침 흉내 — 모듈 메모리를 비우고 다시 세움(세션 · 서버는 그대로). */
|
|
function refresh() {
|
|
const keep = cache.get(path.join(ROOT, "A00_Common", "b_page_state.ts"));
|
|
cache = new Map([[path.join(ROOT, "A00_Common", "b_page_state.ts"), keep]]);
|
|
store = loadTs(path.join(ROOT, "B06_Section", "B06_Section_Section_Store.ts"));
|
|
keys = loadTs(path.join(ROOT, "B06_Section", "B06_Section_Station_Keys.ts"));
|
|
}
|
|
|
|
const sec = (c, kind) => ({
|
|
chainage_m: c,
|
|
kind,
|
|
samples: [],
|
|
design: { design_elevation_m: 100 },
|
|
});
|
|
// 저장 상세: 340 · 관 351 · 360
|
|
const saved = {
|
|
longitudinal: { design_profiles: [], stations: [] },
|
|
cross_sections: [sec(340, "regular"), sec(351, "irregular"), sec(360, "regular")],
|
|
};
|
|
const at = (c) => ({ key: `at:${c.toFixed(2)}`, chainageM: c });
|
|
const savedPipes = [{ id: "p1", chainage_m: 351 }];
|
|
const draftPipes = [{ id: "p1", chainage_m: 361 }];
|
|
const cell = () => state.readState("crossdesign", P, ROUTE);
|
|
const keysNow = () =>
|
|
keys.registeredKeys(P, ROUTE, store.peekSectionDetail(P, ROUTE).cross_sections, 20);
|
|
|
|
(async () => {
|
|
const out = {};
|
|
server = saved;
|
|
state.writeState("section-detail", saved, P, ROUTE);
|
|
state.writeState("pipes", savedPipes, P);
|
|
state.writeState("crossdesign", { before: true }, P, ROUTE);
|
|
// 탭 1 — 열기 · 노선 초안 시작(hold) · 초안이 세션 칸 · 관을 초안 자리로 옮겨 씀
|
|
const opened = await store.loadSectionDetail(P, ROUTE);
|
|
const first = store.holdSectionDetail(P, ROUTE, opened);
|
|
const kept = state.readState("route-draft-hold", P);
|
|
out.start = {
|
|
kept: !!kept && kept.routeId === ROUTE,
|
|
cells: JSON.stringify(first.cells.find(([n]) => n === "crossdesign")[1]),
|
|
pipes: first.facilities.pipes.map((p) => p.chainage_m),
|
|
keys: first.keys.map((k) => k.chainageM),
|
|
};
|
|
state.writeState("route-draft", { nodes: [], rev: 1 }, P);
|
|
state.writeState("crossdesign", { draft: true }, P, ROUTE);
|
|
state.writeState("pipes", draftPipes, P);
|
|
|
|
// 새로고침(초안 둔 채) — 새 모듈 · 세션 그대로 · 상세 = 세션(초안 관 자리로 얹힘)
|
|
refresh();
|
|
calls.length = 0;
|
|
const reopened = await store.loadSectionDetail(P, ROUTE);
|
|
const hold = store.holdSectionDetail(P, ROUTE, reopened);
|
|
out.refresh = {
|
|
cells: JSON.stringify(hold.cells.find(([n]) => n === "crossdesign")[1]),
|
|
pipes: hold.facilities.pipes.map((p) => p.chainage_m),
|
|
keys: JSON.stringify(hold.keys) === JSON.stringify(first.keys),
|
|
pipeShownAt: reopened.cross_sections.filter((s) => s.pipeAt).map((s) => s.chainage_m),
|
|
};
|
|
// 묶음이 초안을 버림 — 가게 관 = hold 관(묶음 몫 흉내) → 되돌림
|
|
state.writeState("route-draft", null, P);
|
|
state.writeState("pipes", hold.facilities.pipes, P);
|
|
calls.length = 0;
|
|
const back = await store.restoreSectionDetail(
|
|
P,
|
|
ROUTE,
|
|
hold,
|
|
saved.cross_sections.map((s) => at(s.chainage_m)),
|
|
);
|
|
out.drop = {
|
|
same: back && back.same,
|
|
cells: JSON.stringify(cell()),
|
|
kept: state.readState("route-draft-hold", P),
|
|
calls: [...calls],
|
|
pipeShownAt: back.detail.cross_sections.filter((s) => s.pipeAt).map((s) => s.chainage_m),
|
|
keys: JSON.stringify(keysNow()) === JSON.stringify(first.keys.map((k) => k.key)),
|
|
};
|
|
|
|
// 새 초안(route-draft 없음) = 지금 값을 다시 뜸 · 다른 노선 hold = 안 씀
|
|
state.writeState("crossdesign", { later: true }, P, ROUTE);
|
|
const fresh = store.holdSectionDetail(P, ROUTE, back.detail);
|
|
state.writeState("route-draft", { nodes: [], rev: 2 }, P);
|
|
const other = store.holdSectionDetail(P, ROUTE + 1, back.detail);
|
|
out.next = {
|
|
cells: JSON.stringify(fresh.cells.find(([n]) => n === "crossdesign")[1]),
|
|
otherRoute: state.readState("route-draft-hold", P).routeId,
|
|
};
|
|
process.stdout.write(JSON.stringify(out));
|
|
})().catch((error) => {
|
|
process.stderr.write(String(error && error.stack ? error.stack : error));
|
|
process.exit(1);
|
|
});
|