- B05_Profile_Api_Structures: definitionStale(열 때 판 알림이 표시 · 저장 성공이 풂) · flushPendingStructures 가 초안 없어도 옛 판이면 정본 그대로 내보냄 - B05 보기 saveStructuresIfDirty 같은 조건 - 시험: test_323_9_definition_stamp(옛 판 열기 알림 · 고침 없음 저장 = 판 도장 · 다시 열기 알림 없음 · 또 저장 안 나감) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
109 lines
4.2 KiB
JavaScript
109 lines
4.2 KiB
JavaScript
/* PLAN 323-9 ② — 정의 판 알림 되풀이. 진짜 `B05_Profile_Api_Structures.ts`(판 알림 · 정본 내보내기)를
|
|
* Node 로 돌림. 서버는 가짜 한 벌 — 구조물 PUT 이 지금 정의 판을 도장(진짜 저장소와 같음).
|
|
*/
|
|
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"); // 구조물 표(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 cache = new Map();
|
|
function loadTs(file) {
|
|
const full = path.resolve(file);
|
|
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;
|
|
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);
|
|
};
|
|
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
|
|
cache.set(full, box.exports);
|
|
return box.exports;
|
|
}
|
|
|
|
const api = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_Api_Structures.ts"));
|
|
|
|
(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 api.flushPendingStructures(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 api.flushPendingStructures(P);
|
|
process.stdout.write(
|
|
JSON.stringify({
|
|
notice,
|
|
staleOnOpen,
|
|
afterSave,
|
|
again,
|
|
staleAfter: api.definitionStale(P),
|
|
putsAfterSecond: puts.length,
|
|
structures: server.structures,
|
|
}),
|
|
);
|
|
})();
|