const FLIGHT_PUSH_MARKER = "self.__next_f.push"; function fail(message, offset) { const suffix = Number.isInteger(offset) ? ` at offset ${offset}` : ""; throw new Error(`Could not decode pinned builder Flight text${suffix}: ${message}`); } function parsePushArgument(html, openParen) { let start = openParen + 1; while (/\s/.test(html[start] ?? "")) start += 1; if (html[start] !== "[") fail("push argument is not a JSON array", openParen); let depth = 0; let quoted = false; let escaped = false; for (let index = start; index < html.length; index += 1) { const character = html[index]; if (quoted) { if (escaped) escaped = false; else if (character === "\\") escaped = true; else if (character === '"') quoted = false; continue; } if (character === '"') quoted = true; else if (character === "[") depth += 1; else if (character === "]") { depth -= 1; if (depth === 0) { const json = html.slice(start, index + 1); let value; try { value = JSON.parse(json); } catch (error) { fail(`push argument is invalid JSON (${error.message})`, openParen); } return { value, end: index + 1 }; } } } fail("push argument array is unterminated", openParen); } function collectTextChunks(builderHtml) { if (typeof builderHtml !== "string") throw new TypeError("builderHtml must be a string"); const chunks = []; let cursor = 0; while (true) { const markerAt = builderHtml.indexOf(FLIGHT_PUSH_MARKER, cursor); if (markerAt < 0) break; const openParen = builderHtml.indexOf("(", markerAt + FLIGHT_PUSH_MARKER.length); if (openParen < 0) fail("push call has no opening parenthesis", markerAt); const parsed = parsePushArgument(builderHtml, openParen); if (!Array.isArray(parsed.value)) fail("push argument did not decode to an array", markerAt); if (Number(parsed.value[0]) === 1 && typeof parsed.value[1] === "string") chunks.push(parsed.value[1]); cursor = parsed.end; } if (chunks.length === 0) fail("no type-1 text chunks were found"); return chunks; } function collectTextRecords(builderHtml) { const text = collectTextChunks(builderHtml).join(""); const bytes = Buffer.from(text, "utf8"); const records = new Map(); let offset = 0; while (offset < bytes.length) { while (bytes[offset] === 0x0a || bytes[offset] === 0x0d) offset += 1; if (offset >= bytes.length) break; const comma = bytes.indexOf(0x2c, offset); const newline = bytes.indexOf(0x0a, offset); if (comma >= 0 && (newline < 0 || comma < newline)) { const header = bytes.toString("ascii", offset, comma); const match = header.match(/^([0-9a-f]+):T([0-9a-f]+)$/i); if (match) { const id = match[1].toLowerCase(); const byteLength = Number.parseInt(match[2], 16); const contentStart = comma + 1; const contentEnd = contentStart + byteLength; if (!Number.isSafeInteger(byteLength) || contentEnd > bytes.length) { fail(`text row ${id} declares ${match[2]} bytes but is truncated`, offset); } const value = bytes.toString("utf8", contentStart, contentEnd); if (records.has(id) && records.get(id) !== value) fail(`text row ${id} is defined more than once with different contents`, offset); records.set(id, value); offset = contentEnd; if (bytes[offset] === 0x0d) offset += 1; if (bytes[offset] === 0x0a) offset += 1; continue; } } const lineEnd = bytes.indexOf(0x0a, offset); if (lineEnd < 0) break; offset = lineEnd + 1; } if (records.size === 0) fail("no length-prefixed text rows were found"); return records; } function resolveDescription(value, records, location) { if (typeof value !== "string" || value.startsWith("$$")) return value; const match = value.match(/^\$([0-9a-f]+)$/i); if (!match) return value; const record = records.get(match[1].toLowerCase()); if (record === undefined) throw new Error(`Pinned builder description reference ${value} at ${location} has no Flight text row`); return record; } function resolveNode(node, records, location) { if (!node || typeof node !== "object" || Array.isArray(node)) return node; if (Object.hasOwn(node, "description")) node.description = resolveDescription(node.description, records, `${location}.description`); if (Array.isArray(node.rankDescriptions)) { node.rankDescriptions.forEach((rank, index) => { if (rank && typeof rank === "object" && !Array.isArray(rank) && Object.hasOwn(rank, "description")) { rank.description = resolveDescription(rank.description, records, `${location}.rankDescriptions[${index}].description`); } }); } return node; } function resolveNodeCollections(owner, records, location) { if (!owner || typeof owner !== "object" || Array.isArray(owner)) return; for (const key of ["nodes", "entries", "talents"]) { if (Array.isArray(owner[key])) { owner[key].forEach((node, index) => resolveNode(node, records, `${location}.${key}[${index}]`)); } } if (Array.isArray(owner.tabs)) { owner.tabs.forEach((tab, index) => resolveNodeCollections(tab, records, `${location}.tabs[${index}]`)); } } /** Resolve exact React Flight string references in archived CoA node descriptions. */ export function resolveArchiveDescriptions(classes, builderHtml) { if (!Array.isArray(classes)) throw new TypeError("classes must be an array"); const records = collectTextRecords(builderHtml); const resolved = structuredClone(classes); resolved.forEach((classRecord, index) => resolveNodeCollections(classRecord, records, `classes[${index}]`)); return resolved; }