- 노선 저장 전 · 뒤 정본 관을 받아 초안을 얹음(rebasePipeDraft) — 옛 자리 그대로인 관 = 서버가 옮긴 자리 · 옵션 고침은 그대로 · 사용자가 옮긴 · 새로 놓은 관 그대로 · 지운 관 없음 · 서버가 새로 놓은 교차 관 붙음 - 관 초안이 없거나 노선 저장이 없으면 전과 같음 - 따라감 미리보기 — 좌표 없는 초안 관 중 안 옮긴 것은 정본 좌표로(서버 노선 저장과 같은 측점) - 시험 test_349_0u_save_rebase(실제 design_save · 서버 따라감 = project_pipe_points) · test_349_0u_follow_draft_xy 서버 대조 · design_store_save 가짜 보탬 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
149 lines
5.3 KiB
JavaScript
149 lines
5.3 KiB
JavaScript
/* PLAN 349-0u 덧 sub7 헬퍼 — 노선을 저장한 [저장]에서 세션 관 초안(옛 노선 누가거리)이 서버가 따라 옮긴 관을
|
|
* 덮지 않음 · 사용자 관 고침만 얹음. 실제 `design_save.ts` · 관 초안 창구 · 가게를 Node 로 · 서버 창구 ·
|
|
* 화면 알림만 가짜. 노선 저장 전 GET = `before` · 뒤 GET = `after`(서버 따라감 + 새 교차 관).
|
|
* 덤 — `placeOnLine` 이 안 옮긴 관에 정본 좌표를 씀. 입력 argv[2] JSON. 짝 = `test_349_0u_save_rebase.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 job = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
|
|
|
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;
|
|
},
|
|
};
|
|
}
|
|
globalThis.window = {
|
|
sessionStorage: memoryStorage(),
|
|
localStorage: memoryStorage(),
|
|
setTimeout,
|
|
clearTimeout,
|
|
};
|
|
|
|
const STUBS = {};
|
|
const cache = new Map();
|
|
function loadTs(full) {
|
|
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 },
|
|
}).outputText;
|
|
const box = { exports: {} };
|
|
cache.set(full, box.exports);
|
|
const localRequire = (request) => {
|
|
if (CATALOG.isCatalogRequest(request)) return CATALOG;
|
|
const key = Object.keys(STUBS).find((name) => request.endsWith(name));
|
|
if (key) return STUBS[key];
|
|
if (request.startsWith("@config/")) return { API_BASE_URL: "http://fake" };
|
|
if (request.startsWith("@util/"))
|
|
return loadTs(path.join(ROOT, "common_util", `${request.slice(6)}.ts`));
|
|
if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`));
|
|
return require(request);
|
|
};
|
|
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
|
|
return box.exports;
|
|
}
|
|
const load = (...parts) => loadTs(path.join(ROOT, ...parts));
|
|
|
|
const calls = [];
|
|
const sent = [];
|
|
let routeSavedOnServer = false;
|
|
let routeBody = null;
|
|
const withLonlat = (list) => list.map((pipe) => ({ ...pipe, lonlat: [0, 0] }));
|
|
STUBS["ui_template_elements"] = {
|
|
showLoadingOverlay: () => undefined,
|
|
hideLoadingOverlay: () => undefined,
|
|
showToast: () => undefined,
|
|
};
|
|
STUBS["B04_PreProcess_Api_Fetch"] = {
|
|
fetchDetailPipePoints: async () => {
|
|
calls.push("GET");
|
|
return { pipe_points: withLonlat(routeSavedOnServer ? job.after : job.before) };
|
|
},
|
|
saveDetailPipePoints: async (_projectId, pipes) => {
|
|
calls.push("PUT");
|
|
sent.push(pipes);
|
|
return {};
|
|
},
|
|
};
|
|
STUBS["B05_Profile_Api_Structures"] = { flushPendingStructures: async () => undefined };
|
|
STUBS["B05_Profile_Api_Fetch"] = {
|
|
flushUphillOverrides: async () => undefined,
|
|
saveProfileAlignment: async () => ({}),
|
|
};
|
|
STUBS["B05_Profile_UI_Profile_Edit"] = {
|
|
markAlignmentSaved: () => undefined,
|
|
pendingAlignment: () => null,
|
|
};
|
|
STUBS["B06_Section_Api_Culvert_Options"] = { flushCulvertOptions: async () => undefined };
|
|
STUBS["B05_Profile_UI_RouteEdit_Apply"] = {
|
|
pendingRouteSave: async () => routeBody,
|
|
saveRouteDraft: async () => {
|
|
calls.push("ROUTE");
|
|
routeSavedOnServer = true;
|
|
return { route_id: 8 };
|
|
},
|
|
carryAfterRouteSave: async () => 0,
|
|
routeSaveSummary: () => "",
|
|
rebuildAfterRouteSave: async () => undefined,
|
|
};
|
|
const save = load("A00_Common", "design", "design_save.ts");
|
|
const drafts = load("B05_Profile", "B05_Profile_Api_Pipes_Draft.ts");
|
|
const follow = load("common_util", "common_util_route_follow.ts");
|
|
|
|
const P = "proj-3490u-save";
|
|
async function once(route, draft) {
|
|
calls.length = 0;
|
|
sent.length = 0;
|
|
routeSavedOnServer = false;
|
|
routeBody = route ? { draft: {} } : null;
|
|
drafts.writePendingPipes(P, draft);
|
|
let failure = null;
|
|
const ok = await save.saveDesign({
|
|
projectId: P,
|
|
routeId: () => 7,
|
|
cross: async () => undefined,
|
|
failed: (error) => (failure = String(error && error.stack ? error.stack : error)),
|
|
});
|
|
if (failure) throw new Error(failure);
|
|
return { ok, calls: [...calls], sent: [...sent], left: drafts.readPendingPipes(P) };
|
|
}
|
|
|
|
async function run() {
|
|
window.localStorage.setItem("frd_current_project_id", P);
|
|
const rebased = await once(true, job.draft);
|
|
const noDraft = await once(true, null);
|
|
const noRoute = await once(false, job.draft);
|
|
// 덤 — 좌표 없는 초안 관: 같은 id · 같은 누가거리 정본이면 그 좌표(노선 밖이어도) · 아니면 노선 위
|
|
const placed = follow.placeOnLine(
|
|
[{ id: "a", chainage_m: 40 }, { id: "b", chainage_m: 60 }, { chainage_m: 70 }],
|
|
[
|
|
{ x: 0, y: 0 },
|
|
{ x: 100, y: 0 },
|
|
],
|
|
[
|
|
{ id: "a", chainage_m: 40, x: 40.4, y: 3 },
|
|
{ id: "b", chainage_m: 55, x: 55, y: 0 },
|
|
],
|
|
);
|
|
return { rebased, noDraft, noRoute, placed };
|
|
}
|
|
|
|
run()
|
|
.then((out) => {
|
|
process.stdout.write(JSON.stringify(out));
|
|
process.exit(0);
|
|
})
|
|
.catch((error) => {
|
|
process.stderr.write(String(error && error.stack ? error.stack : error));
|
|
process.exit(1);
|
|
});
|