- 옮김: B04 2 · B05 23 · B06 11 · A00_Common/design 13 (9,299 줄) · 이미 logic/ 에 있던 16 파일 그대로 - 안 옮김: 묶음 입구(package.json build:*) · 파이썬 · 순수지만 계산 아닌 8 (보기 결선 · 콜백 · API 타입 · 갈아끼움 알림판) - import 266 줄(142 파일) · 시험 · 주석 · 짝 표시 문자열 경로 고침 · B05 · B06 CLAUDE.md 짝 줄 logic/ - 시험 도우미 새 경로: 가짜 require 열쇠 · B05()/B06() 부름 · 형제 import 자리 · tsc 묶어 돌리는 시험 · 정규식 · rglob - design_view_cross 줄 한도 시험 685 → 688 (긴 import 경로 줄바꿈 3줄) - 확인: typecheck 0 · 서버 묶음 넷(corridor · server-calc · b07-extent · b07-note) 다시 지어 코드 같음(//#region 경로 주석만 다름) · 관련 시험 270 파일 — 남은 실패 19 는 옮기기 전과 같음 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
241 lines
8.4 KiB
JavaScript
241 lines
8.4 KiB
JavaScript
/* PLAN 346-2 덧 3 헬퍼 — 노선 초안 동안 횡단 카드 = 묶음 새 노선 상세 · 종단 알약 = follow 뒤 자리.
|
|
* 진짜 TS(`B06_Section_UI_Page_Station_Swap` · `design_pipeline` · `design_store` · `B05_Profile_UI_Structures_Marks` ·
|
|
* `logic/B05_Profile_Structure_Follow`)를 그 자리에서 트랜스파일해 Node 로 돌림. 페이지 묶음 창구
|
|
* (`design_pipeline_page` — pagePipeline · pipelineDetail)와 알약 이름 · 벌룬 · 서버 창구만 가짜.
|
|
* 결과 = 표준출력 JSON. 짝 = `test_346_2_draft_cross_pills.py`. */
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
|
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
const P = "p3463";
|
|
const R = 7;
|
|
|
|
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 = globalThis;
|
|
globalThis.sessionStorage = memoryStorage();
|
|
globalThis.localStorage = memoryStorage();
|
|
localStorage.setItem("frd_current_project_id", P);
|
|
const element = () => {
|
|
const node = {
|
|
style: {},
|
|
dataset: {},
|
|
children: [],
|
|
classList: { toggle: () => undefined, add: () => undefined },
|
|
append: (...items) => node.children.push(...items),
|
|
prepend: (item) => node.children.unshift(item),
|
|
addEventListener: () => undefined,
|
|
};
|
|
return node;
|
|
};
|
|
globalThis.document = { createElement: element };
|
|
|
|
/* 페이지 묶음 창구 가짜 — 묶음은 진짜(`createDesignPipeline`) · 지도는 시험이 쥠 */
|
|
const fake = { pipe: null, detail: null };
|
|
const STUBS = {
|
|
design_pipeline_page: {
|
|
pagePipeline: (id) => (id === P ? fake.pipe : null),
|
|
pipelineDetail: (id) => (id === P ? fake.detail : null),
|
|
},
|
|
B06_Section_Section_Store: {
|
|
onAnySwap: () => () => undefined,
|
|
swapSectionStations: async () => null,
|
|
},
|
|
touch_bridge: { pickPx: () => 14 },
|
|
B05_Profile_Api_Structures: {
|
|
structureAnchorM: (s) => s.chainage_m ?? s.start_m ?? 0,
|
|
},
|
|
B05_Profile_UI_Structures_List: { etcName: () => null, numberDuplicateNames: (names) => names },
|
|
B05_Profile_UI_Structures_Balloon: { balloonLines: () => [] },
|
|
common_util_structure_catalog: {
|
|
catalogRow: (id) => (id === "wall" ? { placement: "interval", owner: "structures" } : null),
|
|
},
|
|
};
|
|
|
|
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) => {
|
|
const key = Object.keys(STUBS).find((name) => request.endsWith(name));
|
|
if (key) return STUBS[key];
|
|
if (request.endsWith("config_frontend") || request.startsWith("@config/"))
|
|
return { CURRENT_PROJECT_ID_KEY: "frd_current_project_id", 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 pipeMod = load("A00_Common", "design", "logic", "design_pipeline.ts");
|
|
const storeMod = load("A00_Common", "design", "design_store.ts");
|
|
const swap = load("B06_Section", "B06_Section_UI_Page_Station_Swap.ts");
|
|
const marks = load("B05_Profile", "B05_Profile_UI_Structures_Marks.ts");
|
|
const follow = load("B05_Profile", "logic", "B05_Profile_Structure_Follow.ts");
|
|
|
|
const tick = (ms = 30) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
const out = {};
|
|
|
|
async function main() {
|
|
/* 1. 횡단 카드 — 저장 노선 → 노선 초안(새 노선 상세) → 숨은 동안 안 그림 → 초안 버림(저장 노선) */
|
|
{
|
|
const saved = { tag: "saved", cross_sections: [] };
|
|
const drawn = [];
|
|
let shown = true;
|
|
const before = swap.shownDetail(P, saved).tag;
|
|
// 횡단 보기가 먼저 섬 — 묶음은 아직 없음(종단 보기가 세움)
|
|
const stop = swap.followSwaps(
|
|
P,
|
|
() => R,
|
|
{ visible: () => shown },
|
|
async () => void drawn.push(swap.shownDetail(P, saved).tag),
|
|
);
|
|
const pipe = pipeMod.createDesignPipeline({
|
|
stages: {
|
|
profile: (ctx) => ({ grade: ctx.inputs.alignment }),
|
|
cross: () => ({ byKey: {} }),
|
|
},
|
|
});
|
|
// 페이지 묶음과 같은 차례 — 가게 잇기 · 새 노선 상세(초안 동안만) · 그다음 듣는 쪽
|
|
pipe.subscribe(pipeMod.publishToStore(storeMod.useDesignStore(P, null)));
|
|
pipe.subscribe((event) => {
|
|
if (event.kind !== "stage" || (event.stage !== "profile" && event.stage !== "cross")) return;
|
|
fake.detail = pipe.input("route")
|
|
? { tag: `draft-${event.stage}-${event.generation}`, cross_sections: [] }
|
|
: null;
|
|
});
|
|
fake.pipe = pipe;
|
|
await pipe.start({
|
|
route: { rev: 1 },
|
|
alignment: 1,
|
|
cross: null,
|
|
pipes: null,
|
|
structures: null,
|
|
});
|
|
await tick();
|
|
const afterDraft = [...drawn];
|
|
const draftNow = swap.shownDetail(P, saved).tag;
|
|
shown = false;
|
|
await pipe.update("grade", 2);
|
|
await tick();
|
|
const whileHidden = [...drawn];
|
|
const hiddenNow = swap.shownDetail(P, saved).tag;
|
|
shown = true;
|
|
await pipe.update("route", null);
|
|
await tick();
|
|
await pipe.update("grade", 3);
|
|
await tick();
|
|
const discarded = [...drawn];
|
|
const savedNow = swap.shownDetail(P, saved).tag;
|
|
stop();
|
|
await pipe.update("route", { rev: 2 });
|
|
await pipe.update("grade", 4);
|
|
await tick();
|
|
out.cards = {
|
|
before,
|
|
afterDraft,
|
|
draftNow,
|
|
whileHidden,
|
|
hiddenNow,
|
|
discarded,
|
|
savedNow,
|
|
afterStop: drawn.length,
|
|
};
|
|
pipe.dispose();
|
|
fake.pipe = null;
|
|
fake.detail = null;
|
|
}
|
|
|
|
/* 2. 종단 알약 — follow 뒤 자리(구조물 id 짝 · 관은 서버 id 와 세션 id 가 달라 차례 짝 · 구간형 띠도) */
|
|
{
|
|
const pill = (structure_id, type_id, chainage_m, start_m = null, end_m = null) => ({
|
|
structure_id,
|
|
type_id,
|
|
placement: start_m === null ? "point" : "interval",
|
|
chainage_m,
|
|
start_m,
|
|
end_m,
|
|
options: {},
|
|
});
|
|
const pills = [
|
|
pill("pipe-s1", "pipe", 30),
|
|
pill("st-1", "gabion", 50),
|
|
pill("st-2", "wall", 70, 60, 80),
|
|
pill("pipe-s2", "pipe", 90),
|
|
pill("st-9", "gabion", 120),
|
|
];
|
|
const inputs = {
|
|
route: { rev: 3 },
|
|
structures: [
|
|
{ structure_id: "st-1", chainage_m: 55 },
|
|
{ structure_id: "st-2", chainage_m: 72, start_m: 62, end_m: 82 },
|
|
],
|
|
pipes: [
|
|
{ id: "PB", chainage_m: 96 },
|
|
{ id: "PA", chainage_m: 33 },
|
|
],
|
|
};
|
|
fake.pipe = { input: (key) => inputs[key] ?? null };
|
|
const lane = () => {
|
|
const root = marks.buildStructureLane({
|
|
structures: pills,
|
|
types: [],
|
|
x: (chainage) => chainage * 10,
|
|
chainageAt: (px) => px / 10,
|
|
maxChainageM: 200,
|
|
widthPx: 2000,
|
|
axisWidthPx: 40,
|
|
stationIntervalM: 20,
|
|
selectedId: null,
|
|
onSelect: () => undefined,
|
|
onMove: () => undefined,
|
|
});
|
|
const left = {};
|
|
const band = [];
|
|
for (const node of root.children) {
|
|
if (node.dataset.structureId) left[node.dataset.structureId] = parseFloat(node.style.left);
|
|
if (node.className?.startsWith("b05-structure__band"))
|
|
band.push(parseFloat(node.style.left), parseFloat(node.style.width));
|
|
}
|
|
return { left, band };
|
|
};
|
|
const draft = lane();
|
|
inputs.route = null; // 초안 버림 — 저장 자리
|
|
const saved = lane();
|
|
// 관 수가 안 맞으면 관 알약은 안 옮김(잘못 짝짓지 않음)
|
|
const short = follow.followedMarks(pills, inputs.structures, [{ id: "PA", chainage_m: 33 }]);
|
|
out.pills = {
|
|
draft,
|
|
saved,
|
|
short: short.map((s) => s.chainage_m),
|
|
untouched: pills[1].chainage_m,
|
|
};
|
|
fake.pipe = null;
|
|
}
|
|
|
|
process.stdout.write(JSON.stringify(out));
|
|
}
|
|
main().catch((error) => {
|
|
console.error(error);
|
|
process.exit(1);
|
|
});
|