/* ============================================================================= * M02_MasterTemplete_Hopyo_Kids.ts * 일위대가 실무자료 만들기 몸 · 하위 호표 함께(PLAN 777-1 · 요구 621-4) — 호표가 다른 호표를 부르면 일위대가 둘 * (하위 일위대가 따로 + 부모 줄 = 하위 사본). * · bodyFor · wholeFor = 확인 창 캐시 위 체크 제안(짝 = 서버 `M02_Hopyo_Kids.proposal` · 캐시 빈 것) * · unmadeKids = 고른 호표 줄이 부르는 확인 안 된 하위 호표(깊이 끝까지 · 하위 먼저) — [저장]이 그 캐시대로 함께 만듦 * · buildWithKids = 확인 창 [생성] — 확인 안 된 하위를 그 캐시대로 먼저 → 부모(실패 = 「하위 제N호표 실패」) * · 캐시 없는 하위도 서버가 부모 저장 때 제안대로 함께 만듦(`M02_Hopyo_Kids.first`) — 화면은 캐시 고름을 살림 * · kidsText = 결과 글 「· 하위 제4호표 SC-11(사본 담음)」 · leftAt = 하위 실패의 남은 줄 = 그 하위 [고르기] * ========================================================================== */ import { t as L } from "@ui/ui_template_locale"; import { StaleError } from "./M02_MasterTemplete_Api_Fetch"; import { buildHopyo, readHopyoOne, type HopyoBuilt, type HopyoOne, type HopyoScope, } from "./M02_MasterTemplete_Hopyo_Api"; import { bodyOf, draftKey, loadDraft, partsOf, toTarget } from "./M02_MasterTemplete_Hopyo_Check"; type Body = Parameters[2]; type Whole = Parameters[3]; type From = Parameters[1]; /** 호표 하나의 몸 — 확인 창 캐시 위 체크 제안 · 통째면 줄 없음 */ export function bodyFor(scope: HopyoScope, key: string, one: HopyoOne): Body { const h = one.호표; const d = loadDraft(draftKey(scope, key, h.번)); return h.통째 && (d.통째 ?? h.통째.체크) ? [] : bodyOf(h.줄, d); } export function wholeFor(scope: HopyoScope, key: string, one: HopyoOne): Whole { const h = one.호표; const d = loadDraft(draftKey(scope, key, h.번)); return h.통째 && (d.통째 ?? h.통째.체크) ? { 대상: h.통째.대상.map(toTarget) } : null; } /** (줄, 통째) 몸 한 벌 */ export const bodyPair = (scope: HopyoScope, key: string, one: HopyoOne): [Body, Whole] => [ bodyFor(scope, key, one), wholeFor(scope, key, one), ]; /** 호표 줄이 부르는 호표 번(체크 안 한 호표 줄 · 캐시에서 고른 호표 대상까지) */ const kidNos = (scope: HopyoScope, key: string, one: HopyoOne): number[] => { const d = loadDraft(draftKey(scope, key, one.호표.번)); return one.호표.줄.flatMap((l) => partsOf(l, d).flatMap((p) => (p.호표 !== undefined ? [p.호표] : [])), ); }; /** [저장] 할 호표 — 함께 만드는 하위 · 체크 줄(통째) 있음 · 하위를 부름(호표 줄 = 하위 사본) */ export function readyFor(scope: HopyoScope, key: string, one: HopyoOne, picked: number[]): boolean { const [lines, whole] = bodyPair(scope, key, one); return ( !picked.includes(one.호표.번) || !!lines.length || !!whole || !!kidNos(scope, key, one).length ); } /** 확인 안 된 하위 호표 보기 — 하위 먼저 · `ones` 와 이미 만든 하위(그 아래도) 뺌 */ export async function unmadeKids( scope: HopyoScope, key: string, target: HopyoScope, ones: HopyoOne[], ): Promise { const out: HopyoOne[] = []; const seen = new Set(ones.map((one) => one.호표.번)); async function visit(one: HopyoOne): Promise { for (const no of kidNos(scope, key, one)) { if (seen.has(no)) continue; seen.add(no); const kid = await readHopyoOne(scope, key, no, target); if (kid.호표.만든) continue; await visit(kid); out.push(kid); } } for (const one of ones) await visit(one); return out; } /** 하위 실패 — 까닭 「하위 제N호표 실패 — …」 · 남은 줄은 그 하위 몫(`leftAt`) */ function kidFail(error: unknown, no: number): StaleError { const left = (error as { detail?: { 남은줄?: unknown } } | null)?.detail?.남은줄; const why = error instanceof Error ? error.message : String(error); const text = `${L("M02_HopyoKidFail").replace("{n}", String(no))} — ${why}`; return new StaleError(text, { 까닭: text, 하위: no, 하위남은줄: left ?? [] }); } /** 확인 창 [생성] — 확인 안 된 하위를 그 캐시대로 먼저 만들고 부모 */ export async function buildWithKids( target: HopyoScope, from: From, 줄: Body, 통째?: Whole, ): Promise { const scope = { layer: from.층, owner: from.주인 }; const me = await readHopyoOne(scope, from.키, from.번, target); for (const kid of await unmadeKids(scope, from.키, target, [me])) { const no = kid.호표.번; const body = [bodyFor(scope, from.키, kid), wholeFor(scope, from.키, kid)] as const; await buildHopyo(target, { ...from, 번: no }, ...body).catch((error: unknown) => { throw kidFail(error, no); }); } return buildHopyo(target, from, 줄, 통째); } /** 하위 실패면 (그 하위 남은 줄 몸, 하위 번) · 아니면 그대로 — 결과 표 [고르기]가 그 하위를 엶 */ export function leftAt(error: unknown, no: number): [unknown, number] { const d = (error as { detail?: { 하위?: number; 하위남은줄?: unknown } } | null)?.detail; return d?.하위 !== undefined ? [{ detail: { 남은줄: d.하위남은줄 } }, d.하위] : [error, no]; } /** 결과 글 꼬리 — 같은 내용 재사용 · 하위 일위대가(사본 담음 · 함께 만듦) */ export function kidsText(got: HopyoBuilt): string { const more = got as HopyoBuilt & { 재사용?: boolean; 하위?: { 번: number; 키: string | null; 새로?: boolean }[]; }; const kids = (more.하위 ?? []).map((k) => { const head = L("M02_HopyoKid") .replace("{n}", String(k.번)) .replace("{key}", k.키 ?? ""); return ` · ${head}${k.새로 ? ` · ${L("M02_HopyoKidNew")}` : ""}`; }); return (more.재사용 ? ` · ${L("M02_HopyoReused")}` : "") + kids.join(""); }