92 lines
3.6 KiB
TypeScript
92 lines
3.6 KiB
TypeScript
/* =============================================================================
|
|
* B06_Section_Session_Cells.ts
|
|
* 조정창 값 창구(472-5 5-D) — 측점별 조작값(반폭 · 기슭막이 4축 · 유입 형식 · 집수정 · 단 수 · 단별 구간 · 연동 ·
|
|
* 세월교 · BOX)의 주인은 **세션 칸 하나**. 메모리 사본 없음 — 읽을 때마다 칸을 풀고 쓸 때마다 칸에 씀(창구 =
|
|
* 등록부 `b_page_state` 열쇠 · 가게도 밑은 이것). 되돌리기 · 다시하기 · 측점 갈아끼움 이동(`Station_Keys`)이 칸을
|
|
* 되써도 다음 조작이 옛 값으로 덮지 않음(N5).
|
|
* 가벼운 자리 — 등록부만 끎(세월교 · BOX 제어 · 시험 로더가 화면 줄기를 안 끌게).
|
|
* ========================================================================== */
|
|
|
|
import { readByKey, writeByKey } from "../A00_Common/b_page_state";
|
|
|
|
/** 세션 칸 한 겹(열쇠 = 누가거리 0.01 m 글 · `:역할` · `b` 앞머리). */
|
|
export interface SessionCells<V> {
|
|
get(key: string): V | undefined;
|
|
has(key: string): boolean;
|
|
set(key: string, value: V): void;
|
|
delete(key: string): void;
|
|
clear(): void;
|
|
forEach(fn: (value: V, key: string) => void): void;
|
|
keys(): string[];
|
|
/** 지금 칸 값(새 Map) — [저장] patch 재료. */
|
|
entries(): Map<string, V>;
|
|
}
|
|
|
|
const isObject = (value: unknown): value is Record<string, unknown> =>
|
|
!!value && typeof value === "object";
|
|
|
|
function cellOf(key: string): Record<string, unknown> {
|
|
try {
|
|
const parsed: unknown = JSON.parse(readByKey(key) ?? "null");
|
|
return isObject(parsed) ? parsed : {};
|
|
} catch {
|
|
return {}; // 손상된 세션 값 = 빈 칸(정본 · 기본값으로)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 세션 칸 창구를 만듦. `accept` = 항목마다 검증 · 정규화(`undefined` = 버림 · 손상값 · 옛 꼴 정리 자리) ·
|
|
* `part` = 칸 안 한 겹(연동 칸 `{ detached, grade }`). 빈 맵을 쓰면 칸 지움(272 · 연동은 두 겹 다 빌 때).
|
|
*/
|
|
export function sessionCells<V>(
|
|
sessionKey: () => string | null,
|
|
accept: (value: unknown) => V | undefined,
|
|
part?: string,
|
|
): SessionCells<V> {
|
|
const read = (): Map<string, V> => {
|
|
const values = new Map<string, V>();
|
|
const key = sessionKey();
|
|
if (!key) return values;
|
|
const whole = cellOf(key);
|
|
const cells = part ? whole[part] : whole;
|
|
if (!isObject(cells)) return values;
|
|
for (const [entry, value] of Object.entries(cells)) {
|
|
const taken = accept(value);
|
|
if (taken !== undefined) values.set(entry, taken);
|
|
}
|
|
return values;
|
|
};
|
|
const write = (values: Map<string, V>): void => {
|
|
const key = sessionKey();
|
|
if (!key) return;
|
|
const cells = Object.fromEntries(values);
|
|
const whole = part ? { ...cellOf(key), [part]: cells } : cells;
|
|
const empty = part
|
|
? Object.values(whole).every((v) => !isObject(v) || !Object.keys(v).length)
|
|
: !values.size;
|
|
try {
|
|
writeByKey(key, empty ? null : JSON.stringify(whole));
|
|
} catch {
|
|
/* 세션 저장 실패(용량 초과 등) — 그 조작만 안 남음. */
|
|
}
|
|
};
|
|
const edit = (change: (values: Map<string, V>) => void): void => {
|
|
const values = read();
|
|
change(values);
|
|
write(values);
|
|
};
|
|
return {
|
|
get: (key) => read().get(key),
|
|
has: (key) => read().has(key),
|
|
set: (key, value) => edit((values) => values.set(key, value)),
|
|
delete: (key) => {
|
|
const values = read();
|
|
if (values.delete(key)) write(values);
|
|
},
|
|
clear: () => write(new Map()),
|
|
forEach: (fn) => read().forEach(fn),
|
|
keys: () => [...read().keys()],
|
|
entries: read,
|
|
};
|
|
}
|