52 lines
3.9 KiB
TypeScript
52 lines
3.9 KiB
TypeScript
import { observedBoolean } from "./ospf-observed-values.js"
|
|
|
|
/** Resolve flags with native getters or a complete complementary switch-query partition; never invent defaults. */
|
|
export async function readOspfTemplateFlags<T extends { ".id": string; disabled?: string; passive?: string }>(payload: unknown, readProperty: (id: string, key: "disabled" | "passive") => Promise<unknown>, beforeRead: () => Promise<unknown> = async () => {}, readPassiveMembership?: () => Promise<{ present: unknown; absent: unknown }>) {
|
|
if (!Array.isArray(payload) || payload.length > 10000 || Buffer.byteLength(JSON.stringify(payload)) > 1048576 || payload.some(row => !row || typeof row !== "object" || Array.isArray(row) || typeof row[".id"] !== "string" || !row[".id"])) throw new Error("Недостоверный каталог флагов OSPF")
|
|
if (new Set(payload.map(row => row[".id"])).size !== payload.length) throw new Error("Повторяющиеся ID шаблонов OSPF")
|
|
const rows: T[] = payload.map(row => ({ ...row }))
|
|
const missing: { row: T; key: "disabled" | "passive" }[] = []
|
|
const unsetPassive: T[] = []
|
|
for (const row of rows) for (const key of ["disabled", "passive"] as const) {
|
|
const raw = row[key], value = observedBoolean(raw)
|
|
if (value !== null) row[key] = String(value)
|
|
else {
|
|
if (raw !== undefined && raw !== "" || !/^\*[a-z0-9]+$/i.test(row[".id"])) throw new Error("Не подтверждён флаг шаблона OSPF")
|
|
if (key === "passive" && raw === undefined && readPassiveMembership) { unsetPassive.push(row); continue }
|
|
missing.push({ row, key })
|
|
}
|
|
}
|
|
if (missing.length > 128) throw new Error("Превышен бюджет уточнения флагов OSPF")
|
|
let cursor = 0, failed = false
|
|
await Promise.all(Array.from({ length: Math.min(4, missing.length) }, async () => {
|
|
while (!failed && cursor < missing.length) {
|
|
const { row, key } = missing[cursor++]
|
|
try {
|
|
await beforeRead()
|
|
if (failed) return
|
|
const response = await readProperty(row[".id"], key)
|
|
const value = response && typeof response === "object" && "ret" in response && typeof response.ret === "string" ? observedBoolean(response.ret) : null
|
|
if (value === null) throw new Error("Не подтверждён effective флаг шаблона OSPF")
|
|
row[key] = String(value)
|
|
} catch (error) { failed = true; throw error }
|
|
}
|
|
}))
|
|
if (unsetPassive.length) {
|
|
if (!readPassiveMembership || missing.length + 2 > 128) throw new Error("Не подтверждён unset passive шаблона OSPF")
|
|
await beforeRead()
|
|
const membership = await readPassiveMembership()
|
|
const ids = (value: unknown) => {
|
|
if (!Array.isArray(value) || value.length > 10000 || Buffer.byteLength(JSON.stringify(value)) > 1048576 || value.some(row => !row || typeof row !== "object" || typeof row[".id"] !== "string" || !/^\*[a-z0-9]+$/i.test(row[".id"]))) throw new Error("Недостоверный ответ native switch query OSPF")
|
|
const result = new Set<string>(value.map(row => row[".id"]))
|
|
if (result.size !== value.length) throw new Error("Повторяющиеся ID native switch query OSPF")
|
|
return result
|
|
}
|
|
const present = ids(membership.present), absent = ids(membership.absent), all = new Set([...present, ...absent])
|
|
if ([...present].some(id => absent.has(id)) || all.size !== rows.length || rows.some(row => !all.has(row[".id"]))) throw new Error("Native switch query OSPF неполон, противоречив или каталог изменился")
|
|
if (rows.some(row => row.passive === "true" && !present.has(row[".id"]))) throw new Error("Native switch query противоречит подтверждённому passive")
|
|
for (const row of unsetPassive) row.passive = present.has(row[".id"]) ? "true" : "false"
|
|
await beforeRead()
|
|
}
|
|
return rows
|
|
}
|