@rickyzhangca/fpsr 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/abort-CrgARaZd.js +43 -0
- package/dist/abort-CrgARaZd.js.map +1 -0
- package/dist/assets-Dy4fqZBV.d.ts +85 -0
- package/dist/assets-Dy4fqZBV.d.ts.map +1 -0
- package/dist/canvas.d.ts +98 -0
- package/dist/canvas.d.ts.map +1 -0
- package/dist/canvas.js +3 -0
- package/dist/execute-CRZV5zw1.js +963 -0
- package/dist/execute-CRZV5zw1.js.map +1 -0
- package/dist/host-DdX4g1YM.d.ts +34 -0
- package/dist/host-DdX4g1YM.d.ts.map +1 -0
- package/dist/index.d.ts +463 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1776 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +16 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +101 -0
- package/dist/node.js.map +1 -0
- package/dist/planner.d.ts +418 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +96 -0
- package/dist/planner.js.map +1 -0
- package/dist/profile-CTN1Q5SI.d.ts +365 -0
- package/dist/profile-CTN1Q5SI.d.ts.map +1 -0
- package/dist/profile-DR4i6HDp.js +610 -0
- package/dist/profile-DR4i6HDp.js.map +1 -0
- package/dist/render-db-BFQIspDB.d.ts +426 -0
- package/dist/render-db-BFQIspDB.d.ts.map +1 -0
- package/dist/render-db.d.ts +2 -0
- package/dist/render-db.js +1 -0
- package/dist/tiled-draw-list-Dc-5Ledi.js +3990 -0
- package/dist/tiled-draw-list-Dc-5Ledi.js.map +1 -0
- package/dist/types-CHgZBSra.d.ts +105 -0
- package/dist/types-CHgZBSra.d.ts.map +1 -0
- package/dist/upgrade-planner-CNjnMHPJ.d.ts +385 -0
- package/dist/upgrade-planner-CNjnMHPJ.d.ts.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["_exhaustive"],"sources":["../src/book.ts","../src/base64.ts","../src/decode.ts","../src/encode.ts","../src/blueprint-components.ts","../src/rich-text.ts","../src/assets.ts","../src/frame.ts","../src/png-stream.ts","../src/background.ts","../src/renderer.ts"],"sourcesContent":["import { filledBlueprintIcons } from \"./blueprint-icons.js\";\nimport { deconstructionPlannerIcons } from \"./deconstruction-planner.js\";\nimport type {\n Blueprint,\n BlueprintBook,\n BlueprintBookEntry,\n BlueprintDocument,\n BlueprintRef,\n Icon,\n} from \"./types/blueprint.js\";\nimport { upgradePlannerIcons } from \"./upgrade-planner.js\";\n\nexport type BlueprintSelectReason = \"not-found\" | \"planner\" | \"empty-book\";\n\nexport class BlueprintSelectError extends Error {\n readonly reason: BlueprintSelectReason;\n\n constructor(reason: BlueprintSelectReason, message?: string) {\n super(message ?? reason);\n this.name = \"BlueprintSelectError\";\n this.reason = reason;\n }\n}\n\nfunction isPlannerEntry(entry: BlueprintBookEntry): boolean {\n return entry.upgrade_planner !== undefined || entry.deconstruction_planner !== undefined;\n}\n\nfunction flattenBook(\n book: BlueprintBook,\n pathPrefix: number[],\n depth: number,\n out: BlueprintRef[],\n): void {\n const entries = book.blueprints ?? [];\n for (const entry of entries) {\n const entryPath = [...pathPrefix, entry.index];\n\n if (entry.blueprint) {\n out.push({\n path: entryPath,\n label: entry.blueprint.label,\n depth,\n });\n }\n\n if (entry.blueprint_book) {\n flattenBook(entry.blueprint_book, entryPath, depth + 1, out);\n }\n\n // upgrade_planner and deconstruction_planner entries are skipped\n }\n}\n\n/**\n * Depth-first flatten of all renderable blueprints inside a (possibly nested) book.\n * Root bare blueprints get path [].\n */\nexport function listBlueprints(doc: BlueprintDocument): BlueprintRef[] {\n if (doc.blueprint) {\n return [{ path: [], label: doc.blueprint.label, depth: 0 }];\n }\n\n if (doc.blueprint_book) {\n const out: BlueprintRef[] = [];\n flattenBook(doc.blueprint_book, [], 0, out);\n return out;\n }\n\n return [];\n}\n\nfunction findEntryByIndex(book: BlueprintBook, index: number): BlueprintBookEntry | undefined {\n return (book.blueprints ?? []).find((e) => e.index === index);\n}\n\nfunction selectFromBook(book: BlueprintBook, path: number[]): Blueprint {\n if (path.length === 0) {\n const active = book.active_index ?? 0;\n const entry = findEntryByIndex(book, active);\n if (!entry) {\n throw new BlueprintSelectError(\"not-found\", `No book entry at active_index ${active}`);\n }\n if (isPlannerEntry(entry)) {\n throw new BlueprintSelectError(\"planner\", \"Active entry is a planner\");\n }\n if (entry.blueprint) {\n return entry.blueprint;\n }\n if (entry.blueprint_book) {\n return selectFromBook(entry.blueprint_book, []);\n }\n throw new BlueprintSelectError(\"empty-book\", \"Active entry has no blueprint or book\");\n }\n\n const head = path[0];\n if (head === undefined) {\n throw new BlueprintSelectError(\"not-found\", \"Empty path\");\n }\n const rest = path.slice(1);\n const entry = findEntryByIndex(book, head);\n if (!entry) {\n throw new BlueprintSelectError(\"not-found\", `No book entry at index ${head}`);\n }\n if (isPlannerEntry(entry)) {\n throw new BlueprintSelectError(\"planner\", `Entry at index ${head} is a planner`);\n }\n if (rest.length === 0) {\n if (entry.blueprint) {\n return entry.blueprint;\n }\n throw new BlueprintSelectError(\"not-found\", `Entry at index ${head} is not a blueprint`);\n }\n if (entry.blueprint_book) {\n return selectFromBook(entry.blueprint_book, rest);\n }\n throw new BlueprintSelectError(\"not-found\", `Entry at index ${head} has no nested book`);\n}\n\n/**\n * Select a blueprint from a document. Without path, follows active_index through nested books.\n */\nexport function selectBlueprint(doc: BlueprintDocument, path?: number[]): Blueprint {\n if (doc.blueprint) {\n if (path !== undefined && path.length > 0) {\n throw new BlueprintSelectError(\"not-found\", \"Cannot use path on a bare blueprint document\");\n }\n return doc.blueprint;\n }\n\n if (doc.blueprint_book) {\n return selectFromBook(doc.blueprint_book, path ?? []);\n }\n\n throw new BlueprintSelectError(\"not-found\", \"Document has no blueprint or blueprint book\");\n}\n\nfunction selectBookFromBook(book: BlueprintBook, path: number[]): BlueprintBook {\n if (path.length === 0) {\n return book;\n }\n\n const head = path[0];\n if (head === undefined) {\n throw new BlueprintSelectError(\"not-found\", \"Empty path\");\n }\n const rest = path.slice(1);\n const entry = findEntryByIndex(book, head);\n if (!entry) {\n throw new BlueprintSelectError(\"not-found\", `No book entry at index ${head}`);\n }\n if (isPlannerEntry(entry)) {\n throw new BlueprintSelectError(\"planner\", `Entry at index ${head} is a planner`);\n }\n if (!entry.blueprint_book) {\n throw new BlueprintSelectError(\"not-found\", `Entry at index ${head} is not a blueprint book`);\n }\n return selectBookFromBook(entry.blueprint_book, rest);\n}\n\n/**\n * Select a blueprint book from a document by path.\n * No path / `[]` returns the root book; non-empty paths walk nested books only.\n */\nexport function selectBook(doc: BlueprintDocument, path?: number[]): BlueprintBook {\n if (!doc.blueprint_book) {\n throw new BlueprintSelectError(\"not-found\", \"Document has no blueprint book\");\n }\n return selectBookFromBook(doc.blueprint_book, path ?? []);\n}\n\nfunction selectUpgradePlannerFromBook(\n book: BlueprintBook,\n path: number[],\n): Record<string, unknown> {\n if (path.length === 0) {\n throw new BlueprintSelectError(\"not-found\", \"Path required to select an upgrade planner\");\n }\n const head = path[0];\n if (head === undefined) {\n throw new BlueprintSelectError(\"not-found\", \"Empty path\");\n }\n const rest = path.slice(1);\n const entry = findEntryByIndex(book, head);\n if (!entry) {\n throw new BlueprintSelectError(\"not-found\", `No book entry at index ${head}`);\n }\n if (rest.length === 0) {\n if (entry.upgrade_planner) {\n return entry.upgrade_planner;\n }\n throw new BlueprintSelectError(\n entry.deconstruction_planner ? \"planner\" : \"not-found\",\n `Entry at index ${head} is not an upgrade planner`,\n );\n }\n if (entry.blueprint_book) {\n return selectUpgradePlannerFromBook(entry.blueprint_book, rest);\n }\n throw new BlueprintSelectError(\"not-found\", `Entry at index ${head} has no nested book`);\n}\n\n/**\n * Select an upgrade planner from a document.\n * Bare upgrade-planner documents ignore path (or require `[]`).\n * Book documents require a path ending on an `upgrade_planner` entry.\n */\nexport function selectUpgradePlanner(\n doc: BlueprintDocument,\n path?: number[],\n): Record<string, unknown> {\n if (doc.upgrade_planner) {\n if (path !== undefined && path.length > 0) {\n throw new BlueprintSelectError(\n \"not-found\",\n \"Cannot use path on a bare upgrade planner document\",\n );\n }\n return doc.upgrade_planner;\n }\n\n if (doc.blueprint_book) {\n if (path === undefined || path.length === 0) {\n throw new BlueprintSelectError(\"not-found\", \"Path required to select an upgrade planner\");\n }\n return selectUpgradePlannerFromBook(doc.blueprint_book, path);\n }\n\n throw new BlueprintSelectError(\"not-found\", \"Document has no upgrade planner\");\n}\n\nfunction selectDeconstructionPlannerFromBook(\n book: BlueprintBook,\n path: number[],\n): Record<string, unknown> {\n if (path.length === 0) {\n throw new BlueprintSelectError(\"not-found\", \"Path required to select a deconstruction planner\");\n }\n const head = path[0];\n if (head === undefined) {\n throw new BlueprintSelectError(\"not-found\", \"Empty path\");\n }\n const rest = path.slice(1);\n const entry = findEntryByIndex(book, head);\n if (!entry) {\n throw new BlueprintSelectError(\"not-found\", `No book entry at index ${head}`);\n }\n if (rest.length === 0) {\n if (entry.deconstruction_planner) {\n return entry.deconstruction_planner;\n }\n throw new BlueprintSelectError(\n entry.upgrade_planner ? \"planner\" : \"not-found\",\n `Entry at index ${head} is not a deconstruction planner`,\n );\n }\n if (entry.blueprint_book) {\n return selectDeconstructionPlannerFromBook(entry.blueprint_book, rest);\n }\n throw new BlueprintSelectError(\"not-found\", `Entry at index ${head} has no nested book`);\n}\n\n/**\n * Select a deconstruction planner from a document.\n * Bare deconstruction-planner documents ignore path (or require `[]`).\n * Book documents require a path ending on a `deconstruction_planner` entry.\n */\nexport function selectDeconstructionPlanner(\n doc: BlueprintDocument,\n path?: number[],\n): Record<string, unknown> {\n if (doc.deconstruction_planner) {\n if (path !== undefined && path.length > 0) {\n throw new BlueprintSelectError(\n \"not-found\",\n \"Cannot use path on a bare deconstruction planner document\",\n );\n }\n return doc.deconstruction_planner;\n }\n\n if (doc.blueprint_book) {\n if (path === undefined || path.length === 0) {\n throw new BlueprintSelectError(\n \"not-found\",\n \"Path required to select a deconstruction planner\",\n );\n }\n return selectDeconstructionPlannerFromBook(doc.blueprint_book, path);\n }\n\n throw new BlueprintSelectError(\"not-found\", \"Document has no deconstruction planner\");\n}\n\nexport type BookTreeItemKind = \"book\" | \"blueprint\" | \"upgrade_planner\" | \"deconstruction_planner\";\n\n/** Hierarchical book entry for tree UIs (e.g. Headless Tree sync data loader). */\nexport interface BookTreeItem {\n id: string;\n path: number[];\n label: string;\n kind: BookTreeItemKind;\n icons?: Icon[];\n /**\n * When set on a book node, nest a planner composite (this paper + `icons`) as\n * the single icon on the book cover — does not replace the book paper.\n */\n iconBackgroundKey?: string;\n children: string[];\n}\n\nexport interface BookTree {\n rootId: string;\n items: Record<string, BookTreeItem>;\n}\n\nconst ROOT_ID = \"root\";\n\nfunction pathToId(path: number[]): string {\n return path.length === 0 ? ROOT_ID : path.join(\".\");\n}\n\nfunction entryKind(entry: BlueprintBookEntry): BookTreeItemKind | null {\n if (entry.blueprint_book) return \"book\";\n if (entry.blueprint) return \"blueprint\";\n if (entry.upgrade_planner) return \"upgrade_planner\";\n if (entry.deconstruction_planner) return \"deconstruction_planner\";\n return null;\n}\n\nfunction plannerField(planner: Record<string, unknown> | undefined, key: string): unknown {\n return planner?.[key];\n}\n\nfunction entryLabel(entry: BlueprintBookEntry, kind: BookTreeItemKind): string {\n if (kind === \"book\") return entry.blueprint_book?.label ?? \"(untitled)\";\n if (kind === \"blueprint\") return entry.blueprint?.label ?? \"(untitled)\";\n if (kind === \"upgrade_planner\") {\n const label = plannerField(entry.upgrade_planner, \"label\");\n return typeof label === \"string\" && label.length > 0 ? label : \"Upgrade planner\";\n }\n const label = plannerField(entry.deconstruction_planner, \"label\");\n return typeof label === \"string\" && label.length > 0 ? label : \"Deconstruction planner\";\n}\n\nfunction entryIcons(entry: BlueprintBookEntry, kind: BookTreeItemKind): Icon[] | undefined {\n if (kind === \"book\" && entry.blueprint_book) {\n const icons = blueprintBookCover(entry.blueprint_book).icons;\n return icons.length > 0 ? icons : undefined;\n }\n if (kind === \"blueprint\") return entry.blueprint?.icons;\n if (kind === \"upgrade_planner\" && entry.upgrade_planner) {\n const icons = upgradePlannerIcons(entry.upgrade_planner);\n return icons.length > 0 ? icons : undefined;\n }\n if (kind === \"deconstruction_planner\" && entry.deconstruction_planner) {\n const icons = deconstructionPlannerIcons(entry.deconstruction_planner);\n return icons.length > 0 ? icons : undefined;\n }\n return undefined;\n}\n\nconst BOOK_COVER_BACKGROUND = \"item/blueprint-book\";\n\n/** Nested planner thumbnail drawn as the single icon on a book cover. */\nexport interface BlueprintBookCoverNested {\n backgroundKey: string;\n icons: Icon[];\n}\n\n/** Inventory thumbnail for a blueprint book: book paper plus cover contents. */\nexport interface BlueprintBookCover {\n /** Always the book paper; planner composites nest inside via `nested`. */\n backgroundKey: string;\n /** Flat signal icons when the first leaf is a blueprint (or empty). */\n icons: Icon[];\n /**\n * When the first leaf is a planner, its full composite (planner paper + icons)\n * is drawn as the single icon on the book cover.\n */\n nested?: BlueprintBookCoverNested;\n}\n\n/**\n * Inventory thumbnail for a blueprint book: always the composite of the first\n * child entry (lowest `index`), including nested books and planners.\n * Ignores any explicit `book.icons` so the cover tracks book contents.\n *\n * The outer paper is always the book. Planner leaves nest their composite as\n * the cover icon; blueprint leaves place signal icons directly on the book.\n */\nexport function blueprintBookCover(book: BlueprintBook): BlueprintBookCover {\n const entries = [...(book.blueprints ?? [])].sort((a, b) => a.index - b.index);\n const first = entries[0];\n if (!first) return { icons: [], backgroundKey: BOOK_COVER_BACKGROUND };\n\n if (first.blueprint) {\n return {\n icons: filledBlueprintIcons(first.blueprint.icons),\n backgroundKey: BOOK_COVER_BACKGROUND,\n };\n }\n if (first.upgrade_planner) {\n return {\n icons: [],\n backgroundKey: BOOK_COVER_BACKGROUND,\n nested: {\n backgroundKey: \"item/upgrade-planner\",\n icons: upgradePlannerIcons(first.upgrade_planner),\n },\n };\n }\n if (first.deconstruction_planner) {\n return {\n icons: [],\n backgroundKey: BOOK_COVER_BACKGROUND,\n nested: {\n backgroundKey: \"item/deconstruction-planner\",\n icons: deconstructionPlannerIcons(first.deconstruction_planner),\n },\n };\n }\n if (first.blueprint_book) return blueprintBookCover(first.blueprint_book);\n return { icons: [], backgroundKey: BOOK_COVER_BACKGROUND };\n}\n\n/** Signal icons for a book cover (nested planner icons when applicable). */\nexport function blueprintBookCoverIcons(book: BlueprintBook): Icon[] {\n const cover = blueprintBookCover(book);\n return cover.nested?.icons ?? cover.icons;\n}\n\nfunction bookCoverFields(book: BlueprintBook): Pick<BookTreeItem, \"icons\" | \"iconBackgroundKey\"> {\n const cover = blueprintBookCover(book);\n if (cover.nested) {\n return {\n ...(cover.nested.icons.length > 0 ? { icons: cover.nested.icons } : {}),\n iconBackgroundKey: cover.nested.backgroundKey,\n };\n }\n return {\n ...(cover.icons.length > 0 ? { icons: cover.icons } : {}),\n };\n}\n\nfunction addBookEntries(\n book: BlueprintBook,\n pathPrefix: number[],\n items: Record<string, BookTreeItem>,\n): string[] {\n const childIds: string[] = [];\n for (const entry of book.blueprints ?? []) {\n const kind = entryKind(entry);\n if (!kind) continue;\n\n const entryPath = [...pathPrefix, entry.index];\n const id = pathToId(entryPath);\n childIds.push(id);\n\n const icons = entryIcons(entry, kind);\n\n if (kind === \"book\" && entry.blueprint_book) {\n const nestedChildren = addBookEntries(entry.blueprint_book, entryPath, items);\n items[id] = {\n id,\n path: entryPath,\n label: entryLabel(entry, kind),\n kind,\n ...bookCoverFields(entry.blueprint_book),\n children: nestedChildren,\n };\n } else {\n items[id] = {\n id,\n path: entryPath,\n label: entryLabel(entry, kind),\n kind,\n icons,\n children: [],\n };\n }\n }\n return childIds;\n}\n\n/**\n * Build a flat id→item map of the book hierarchy for tree UIs.\n * Returns null when the document is not a blueprint book.\n */\nexport function buildBookTree(doc: BlueprintDocument): BookTree | null {\n if (!doc.blueprint_book) return null;\n\n const items: Record<string, BookTreeItem> = {};\n const children = addBookEntries(doc.blueprint_book, [], items);\n items[ROOT_ID] = {\n id: ROOT_ID,\n path: [],\n label: doc.blueprint_book.label ?? \"(untitled)\",\n kind: \"book\",\n ...bookCoverFields(doc.blueprint_book),\n children,\n };\n\n return { rootId: ROOT_ID, items };\n}\n\nfunction resolveActivePathFromBook(book: BlueprintBook, pathPrefix: number[]): number[] {\n const active = book.active_index ?? 0;\n const entry = findEntryByIndex(book, active);\n if (!entry) {\n throw new BlueprintSelectError(\"not-found\", `No book entry at active_index ${active}`);\n }\n const entryPath = [...pathPrefix, entry.index];\n if (entry.upgrade_planner) {\n return entryPath;\n }\n if (entry.deconstruction_planner) {\n return entryPath;\n }\n if (entry.blueprint) {\n return entryPath;\n }\n if (entry.blueprint_book) {\n return resolveActivePathFromBook(entry.blueprint_book, entryPath);\n }\n throw new BlueprintSelectError(\"empty-book\", \"Active entry has no blueprint or book\");\n}\n\n/**\n * Resolve the concrete blueprint path by following active_index through nested books.\n * Bare blueprint documents return []. Returns null when no active blueprint can be resolved.\n */\nexport function resolveActivePath(doc: BlueprintDocument): number[] | null {\n if (doc.blueprint) return [];\n if (!doc.blueprint_book) return null;\n try {\n return resolveActivePathFromBook(doc.blueprint_book, []);\n } catch {\n return null;\n }\n}\n","/**\n * Cross-environment base64 helpers (browser + Node) without relying on Buffer alone.\n */\n\nconst hasAtob = typeof globalThis.atob === \"function\";\nconst hasBtoa = typeof globalThis.btoa === \"function\";\n\nfunction bytesToBinary(bytes: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n const byte = bytes[i];\n if (byte === undefined) continue;\n binary += String.fromCharCode(byte);\n }\n return binary;\n}\n\nfunction binaryToBytes(binary: string): Uint8Array {\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\nexport function base64Encode(bytes: Uint8Array): string {\n if (hasBtoa) {\n return globalThis.btoa(bytesToBinary(bytes));\n }\n // Node fallback\n const { Buffer } = globalThis as {\n Buffer?: { from(data: Uint8Array): { toString(enc: string): string } };\n };\n if (Buffer) {\n return Buffer.from(bytes).toString(\"base64\");\n }\n throw new Error(\"No base64 encoder available\");\n}\n\nexport function base64Decode(b64: string): Uint8Array {\n if (hasAtob) {\n return binaryToBytes(globalThis.atob(b64));\n }\n const { Buffer } = globalThis as { Buffer?: { from(data: string, enc: string): Uint8Array } };\n if (Buffer) {\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n }\n throw new Error(\"No base64 decoder available\");\n}\n\nexport function utf8Encode(text: string): Uint8Array {\n if (typeof TextEncoder !== \"undefined\") {\n return new TextEncoder().encode(text);\n }\n const { Buffer } = globalThis as { Buffer?: { from(data: string, enc: string): Uint8Array } };\n if (Buffer) {\n return new Uint8Array(Buffer.from(text, \"utf8\"));\n }\n throw new Error(\"No UTF-8 encoder available\");\n}\n\nexport function utf8Decode(bytes: Uint8Array): string {\n if (typeof TextDecoder !== \"undefined\") {\n return new TextDecoder().decode(bytes);\n }\n const { Buffer } = globalThis as {\n Buffer?: { from(data: Uint8Array): { toString(enc: string): string } };\n };\n if (Buffer) {\n return Buffer.from(bytes).toString(\"utf8\");\n }\n throw new Error(\"No UTF-8 decoder available\");\n}\n","import { unzlibSync } from \"fflate\";\nimport { base64Decode, utf8Decode } from \"./base64.js\";\nimport { nowMs, type DecodeStats } from \"./profile.js\";\nimport type { BlueprintDocument } from \"./types/blueprint.js\";\n\nexport type BlueprintDecodeReason =\n | \"unsupported-version\"\n | \"invalid-base64\"\n | \"inflate-failed\"\n | \"invalid-json\"\n | \"invalid-document\";\n\nexport class BlueprintDecodeError extends Error {\n readonly reason: BlueprintDecodeReason;\n\n constructor(reason: BlueprintDecodeReason, message?: string) {\n super(message ?? reason);\n this.name = \"BlueprintDecodeError\";\n this.reason = reason;\n }\n}\n\nconst TOP_LEVEL_KEYS = [\n \"blueprint\",\n \"blueprint_book\",\n \"upgrade_planner\",\n \"deconstruction_planner\",\n] as const;\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nconst ENTRY_WRAPPER_KEYS = [\n \"blueprint\",\n \"blueprint_book\",\n \"upgrade_planner\",\n \"deconstruction_planner\",\n] as const;\n\nfunction validateBlueprint(bp: Record<string, unknown>, path: string): void {\n if (bp.item !== \"blueprint\") {\n throw new BlueprintDecodeError(\"invalid-document\", `${path}.item must be \"blueprint\"`);\n }\n if (typeof bp.version !== \"number\" || !Number.isFinite(bp.version)) {\n throw new BlueprintDecodeError(\"invalid-document\", `${path}.version must be a finite number`);\n }\n}\n\nfunction validatePlannerPayload(value: Record<string, unknown>, path: string): void {\n // Upgrade / deconstruction planners are opaque Record payloads; only require\n // a plain object (already checked by the caller).\n void value;\n void path;\n}\n\nfunction validateBookEntry(entry: unknown, path: string): void {\n if (!isPlainObject(entry)) {\n throw new BlueprintDecodeError(\"invalid-document\", `${path} must be an object`);\n }\n if (!(\"index\" in entry) || typeof entry.index !== \"number\" || !Number.isFinite(entry.index)) {\n throw new BlueprintDecodeError(\"invalid-document\", `${path}.index must be a finite number`);\n }\n const keys = ENTRY_WRAPPER_KEYS.filter((k) => k in entry);\n if (keys.length !== 1) {\n throw new BlueprintDecodeError(\n \"invalid-document\",\n `${path} expected exactly one content key, found ${keys.length}`,\n );\n }\n const key = keys[0]!;\n const value = entry[key];\n if (value === null || value === undefined) {\n throw new BlueprintDecodeError(\n \"invalid-document\",\n `${path}.${key} must be an object, got ${value === null ? \"null\" : \"undefined\"}`,\n );\n }\n if (!isPlainObject(value)) {\n throw new BlueprintDecodeError(\"invalid-document\", `${path}.${key} must be an object`);\n }\n if (key === \"blueprint\") {\n validateBlueprint(value, `${path}.blueprint`);\n } else if (key === \"blueprint_book\") {\n validateBlueprintBook(value, `${path}.blueprint_book`);\n } else {\n validatePlannerPayload(value, `${path}.${key}`);\n }\n}\n\nfunction validateBlueprintBook(book: Record<string, unknown>, path: string): void {\n if (book.item !== \"blueprint-book\") {\n throw new BlueprintDecodeError(\"invalid-document\", `${path}.item must be \"blueprint-book\"`);\n }\n if (typeof book.version !== \"number\" || !Number.isFinite(book.version)) {\n throw new BlueprintDecodeError(\"invalid-document\", `${path}.version must be a finite number`);\n }\n const entries = book.blueprints;\n if (entries === undefined) return;\n if (!Array.isArray(entries)) {\n throw new BlueprintDecodeError(\"invalid-document\", `${path}.blueprints must be an array`);\n }\n for (let i = 0; i < entries.length; i++) {\n validateBookEntry(entries[i], `${path}.blueprints[${i}]`);\n }\n}\n\nfunction validateWrapperValue(\n doc: Record<string, unknown>,\n key: (typeof TOP_LEVEL_KEYS)[number],\n): void {\n const value = doc[key];\n if (value === null || value === undefined) {\n throw new BlueprintDecodeError(\n \"invalid-document\",\n `Top-level \"${key}\" must be an object, got ${value === null ? \"null\" : \"undefined\"}`,\n );\n }\n if (!isPlainObject(value)) {\n throw new BlueprintDecodeError(\"invalid-document\", `Top-level \"${key}\" must be an object`);\n }\n if (key === \"blueprint\") {\n validateBlueprint(value, \"blueprint\");\n } else if (key === \"blueprint_book\") {\n validateBlueprintBook(value, \"blueprint_book\");\n } else {\n validatePlannerPayload(value, key);\n }\n}\n\nfunction validateDocument(doc: unknown): BlueprintDocument {\n if (!isPlainObject(doc)) {\n throw new BlueprintDecodeError(\"invalid-document\", \"Document must be an object\");\n }\n const keys = TOP_LEVEL_KEYS.filter((k) => k in doc);\n if (keys.length !== 1) {\n throw new BlueprintDecodeError(\n \"invalid-document\",\n `Expected exactly one top-level key, found ${keys.length}`,\n );\n }\n const key = keys[0]!;\n validateWrapperValue(doc, key);\n return doc as BlueprintDocument;\n}\n\nfunction decodeCompressedWithStats(source: string): { doc: BlueprintDocument; stats: DecodeStats } {\n const tTotal = nowMs();\n const versionByte = source[0];\n if (versionByte !== \"0\") {\n throw new BlueprintDecodeError(\n \"unsupported-version\",\n `Unsupported blueprint string version byte: ${versionByte}`,\n );\n }\n\n let t = nowMs();\n let bytes: Uint8Array;\n try {\n bytes = base64Decode(source.slice(1));\n } catch {\n throw new BlueprintDecodeError(\"invalid-base64\", \"Invalid base64 payload\");\n }\n const base64Ms = nowMs() - t;\n\n t = nowMs();\n let inflated: Uint8Array;\n try {\n inflated = unzlibSync(bytes);\n } catch {\n throw new BlueprintDecodeError(\"inflate-failed\", \"zlib inflate failed\");\n }\n const inflateMs = nowMs() - t;\n\n t = nowMs();\n let text: string;\n try {\n text = utf8Decode(inflated);\n } catch {\n throw new BlueprintDecodeError(\"invalid-json\", \"Invalid UTF-8 after inflate\");\n }\n const utf8Ms = nowMs() - t;\n\n t = nowMs();\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n throw new BlueprintDecodeError(\"invalid-json\", \"Invalid JSON after inflate\");\n }\n const jsonParseMs = nowMs() - t;\n\n t = nowMs();\n const doc = validateDocument(parsed);\n const validateMs = nowMs() - t;\n\n const compressedBytes = bytes.length;\n const inflatedBytes = inflated.length;\n const stats: DecodeStats = {\n mode: \"compressed\",\n inputChars: source.length,\n compressedBytes,\n inflatedBytes,\n jsonChars: text.length,\n compressionRatio: compressedBytes > 0 ? inflatedBytes / compressedBytes : undefined,\n timings: {\n totalMs: nowMs() - tTotal,\n base64Ms,\n inflateMs,\n utf8Ms,\n jsonParseMs,\n validateMs,\n },\n };\n return { doc, stats };\n}\n\nfunction decodeJsonWithStats(source: string): { doc: BlueprintDocument; stats: DecodeStats } {\n const tTotal = nowMs();\n\n let t = nowMs();\n let parsed: unknown;\n try {\n parsed = JSON.parse(source);\n } catch {\n throw new BlueprintDecodeError(\"invalid-json\", \"Invalid raw JSON\");\n }\n const jsonParseMs = nowMs() - t;\n\n t = nowMs();\n const doc = validateDocument(parsed);\n const validateMs = nowMs() - t;\n\n const stats: DecodeStats = {\n mode: \"json\",\n inputChars: source.length,\n jsonChars: source.length,\n timings: {\n totalMs: nowMs() - tTotal,\n jsonParseMs,\n validateMs,\n },\n };\n return { doc, stats };\n}\n\n/**\n * Decode a Factorio blueprint string (compressed or raw JSON) into a BlueprintDocument.\n */\nexport function decode(source: string): BlueprintDocument {\n return decodeWithStats(source).doc;\n}\n\n/**\n * Decode a blueprint string and collect size + timing stats for profiling.\n */\nexport function decodeWithStats(source: string): { doc: BlueprintDocument; stats: DecodeStats } {\n const trimmed = source.trim();\n if (trimmed.length === 0) {\n throw new BlueprintDecodeError(\"invalid-document\", \"Empty input\");\n }\n\n if (trimmed.startsWith(\"{\")) {\n return decodeJsonWithStats(trimmed);\n }\n\n return decodeCompressedWithStats(trimmed);\n}\n","import { zlibSync } from \"fflate\";\nimport { base64Encode, utf8Encode } from \"./base64.js\";\nimport type { BlueprintDocument } from \"./types/blueprint.js\";\n\n/**\n * Encode a BlueprintDocument into a Factorio blueprint string (version byte 0).\n */\nexport function encode(doc: BlueprintDocument): string {\n const json = JSON.stringify(doc);\n const bytes = utf8Encode(json);\n const deflated = zlibSync(bytes, { level: 9 });\n const b64 = base64Encode(deflated);\n return `0${b64}`;\n}\n","import { migrateTo2x } from \"./migrate.js\";\nimport type { Blueprint } from \"./types/blueprint.js\";\nimport type { RenderDb } from \"./types/render-db.js\";\n\n/** Ground rail pieces that place from the `rail` item. */\nconst RAIL_COMPONENT_NAMES = new Set([\n \"straight-rail\",\n \"curved-rail-a\",\n \"curved-rail-b\",\n \"half-diagonal-rail\",\n \"legacy-straight-rail\",\n \"legacy-curved-rail\",\n]);\n\n/** Placing-item / remapped name + instance count. */\nexport interface BlueprintComponentCount {\n name: string;\n count: number;\n}\n\n/**\n * Summarize blueprint inventory for UI (item icons).\n * - Migrates 1.x names first (`migrateTo2x`) so icons resolve in the 2.x render-db\n * - Ground rail pieces (straight/curved/half-diagonal + legacy) → `\"rail\"`\n * - Tiles → `db.tiles[name].item` when present, else tile prototype name\n * - All other entities → prototype name\n *\n * Sorted by count descending, then name ascending.\n */\nexport function countBlueprintComponents(bp: Blueprint, db: RenderDb): BlueprintComponentCount[] {\n const migrated = migrateTo2x(bp);\n const counts = new Map<string, number>();\n for (const entity of migrated.entities ?? []) {\n const key = RAIL_COMPONENT_NAMES.has(entity.name) ? \"rail\" : entity.name;\n counts.set(key, (counts.get(key) ?? 0) + 1);\n }\n for (const tile of migrated.tiles ?? []) {\n const key = db.tiles[tile.name]?.item ?? tile.name;\n counts.set(key, (counts.get(key) ?? 0) + 1);\n }\n return [...counts.entries()]\n .map(([name, count]) => ({ name, count }))\n .sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));\n}\n","/** Parsed segment of a Factorio rich-text string. */\nexport type RichTextToken =\n | { kind: \"text\"; value: string }\n | {\n kind: \"icon\";\n type: string;\n name: string;\n quality?: string;\n raw: string;\n };\n\nconst ICON_TAG_TYPES = new Set([\n \"item\",\n \"entity\",\n \"recipe\",\n \"fluid\",\n \"virtual-signal\",\n \"quality\",\n \"space-location\",\n \"asteroid-chunk\",\n \"img\",\n]);\n\nconst RICH_TEXT_PREFIX: Record<string, string> = {\n item: \"item\",\n entity: \"entity\",\n recipe: \"recipe\",\n fluid: \"fluid\",\n \"virtual-signal\": \"virtual-signal\",\n quality: \"quality\",\n \"space-location\": \"space-location\",\n \"asteroid-chunk\": \"asteroid-chunk\",\n};\n\nfunction parsePayload(payload: string): { name: string; quality?: string } | undefined {\n const parts = payload.split(\",\");\n const namePart = parts[0]?.trim();\n if (!namePart) return undefined;\n\n let quality: string | undefined;\n for (let i = 1; i < parts.length; i++) {\n const segment = parts[i]?.trim();\n if (!segment) continue;\n const eq = segment.indexOf(\"=\");\n if (eq === -1) continue;\n const key = segment.slice(0, eq).trim();\n const value = segment.slice(eq + 1).trim();\n if (key === \"quality\" && value) quality = value;\n }\n\n return quality ? { name: namePart, quality } : { name: namePart };\n}\n\nfunction parseImgPayload(payload: string): { type: string; name: string } | undefined {\n const slash = payload.indexOf(\"/\");\n const dot = payload.indexOf(\".\");\n const sep = slash !== -1 ? slash : dot !== -1 ? dot : -1;\n if (sep === -1) return undefined;\n const type = payload.slice(0, sep).trim();\n const name = payload.slice(sep + 1).trim();\n if (!type || !name) return undefined;\n return { type, name };\n}\n\nfunction parseTagContent(content: string): RichTextToken | undefined {\n const eq = content.indexOf(\"=\");\n if (eq === -1) return undefined;\n\n const tag = content.slice(0, eq).trim();\n const payload = content.slice(eq + 1).trim();\n if (!tag || !payload) return undefined;\n if (!ICON_TAG_TYPES.has(tag)) return undefined;\n\n const raw = `[${content}]`;\n\n if (tag === \"img\") {\n const img = parseImgPayload(payload);\n if (!img) return undefined;\n return { kind: \"icon\", type: img.type, name: img.name, raw };\n }\n\n const parsed = parsePayload(payload);\n if (!parsed) return undefined;\n\n if (tag === \"quality\") {\n return { kind: \"icon\", type: \"quality\", name: parsed.name, raw };\n }\n\n const token: RichTextToken = { kind: \"icon\", type: tag, name: parsed.name, raw };\n if (parsed.quality && parsed.quality !== \"normal\") {\n return { ...token, quality: parsed.quality };\n }\n return token;\n}\n\n/**\n * Parse Factorio rich-text icon tags into text and icon tokens.\n * Unrecognized bracket sequences are kept as literal text.\n */\nexport function parseRichText(text: string): RichTextToken[] {\n if (!text) return [];\n\n const tokens: RichTextToken[] = [];\n let cursor = 0;\n\n while (cursor < text.length) {\n const open = text.indexOf(\"[\", cursor);\n if (open === -1) {\n tokens.push({ kind: \"text\", value: text.slice(cursor) });\n break;\n }\n\n if (open > cursor) {\n tokens.push({ kind: \"text\", value: text.slice(cursor, open) });\n }\n\n const close = text.indexOf(\"]\", open + 1);\n if (close === -1) {\n tokens.push({ kind: \"text\", value: text.slice(open) });\n break;\n }\n\n const content = text.slice(open + 1, close);\n const parsed = parseTagContent(content);\n if (parsed) {\n tokens.push(parsed);\n cursor = close + 1;\n } else {\n tokens.push({ kind: \"text\", value: text.slice(open, close + 1) });\n cursor = close + 1;\n }\n }\n\n return mergeAdjacentText(tokens);\n}\n\nfunction mergeAdjacentText(tokens: RichTextToken[]): RichTextToken[] {\n const merged: RichTextToken[] = [];\n for (const token of tokens) {\n const last = merged.at(-1);\n if (token.kind === \"text\" && last?.kind === \"text\") {\n last.value += token.value;\n } else {\n merged.push(token);\n }\n }\n return merged;\n}\n\n/** Ordered render-db icon keys for a parsed rich-text icon token. */\nexport function richTextIconKeys(token: Extract<RichTextToken, { kind: \"icon\" }>): string[] {\n const prefix = RICH_TEXT_PREFIX[token.type] ?? token.type;\n const primary = `${prefix}/${token.name}`;\n\n switch (token.type) {\n case \"item\":\n return [primary, `entity/${token.name}`, `recipe/${token.name}`];\n case \"entity\":\n return [primary, `item/${token.name}`];\n case \"recipe\":\n return [primary, `item/${token.name}`];\n default:\n return [primary];\n }\n}\n\n/** Whether a quality badge should be drawn on top of this icon token. */\nexport function richTextIconQuality(\n token: Extract<RichTextToken, { kind: \"icon\" }>,\n): string | undefined {\n if (token.type === \"quality\") return undefined;\n if (!token.quality || token.quality === \"normal\") return undefined;\n return token.quality;\n}\n\n/** Strip rich-text tags, keeping only literal text segments. */\nexport function stripRichText(text: string | undefined): string {\n if (!text) return \"\";\n return parseRichText(text)\n .filter((token): token is Extract<RichTextToken, { kind: \"text\" }> => token.kind === \"text\")\n .map((token) => token.value)\n .join(\"\")\n .trim();\n}\n","import { raceWithAbort, throwIfAborted } from \"./abort.js\";\nimport type { AssetLoadOptions, ImageSource } from \"./host.js\";\nimport { nowMs, type AssetEvent } from \"./profile.js\";\nimport type { RenderDb } from \"./types/render-db.js\";\n\n/**\n * Pluggable atlas / render-db loader. Browser uses ImageBitmap; node uses\n * skia-canvas Image (via fpsr/node).\n *\n * ## AbortSignal semantics\n *\n * `loadRenderDb` / `loadAtlasImage` accept an optional `signal` on the load\n * options. Aborting rejects **only the waiting caller** with an `AbortError`.\n * Shared in-flight network/decode work is **not** cancelled for other\n * concurrent consumers of the same cache key. Aborted waits never write into\n * or delete a successful cache entry; only genuine load failures clear the\n * shared promise slot so a later call can retry.\n */\nexport interface AssetSource {\n loadRenderDb(tier?: AssetTier, options?: AssetLoadOptions): Promise<RenderDb>;\n loadAtlasImage(index: number, tier?: AssetTier, options?: AssetLoadOptions): Promise<ImageSource>;\n /**\n * Optional: register canvas text fonts listed in the asset manifest (e.g. DejaVu).\n * Missing fonts are ignored so renders still work with CSS fallbacks.\n */\n ensureFonts?(options?: AssetLoadOptions): Promise<void>;\n /**\n * Optional: release retained decoded images / clear caches.\n * Ownership stays with the AssetSource caller — Renderer.dispose never invokes this.\n */\n dispose?(): void;\n}\n\nexport type AssetTier = \"1x\" | \"2x\";\n\nexport interface ManifestAtlas {\n file: string;\n w: number;\n h: number;\n sha256?: string;\n}\n\nexport interface ManifestFont {\n file: string;\n family: string;\n sha256?: string;\n bytes?: number;\n}\n\nexport interface AssetManifest {\n schema: 2;\n gameVersion: string;\n mods: string[];\n tiers: Record<AssetTier, AssetTierManifest>;\n /** Optional bundled TTFs for canvas text (e.g. DejaVu Sans). */\n fonts?: ManifestFont[];\n}\n\nexport interface AssetTierManifest {\n density: 1 | 2;\n atlases: ManifestAtlas[];\n renderDb: {\n file: string;\n sha256: string;\n bytes?: number;\n };\n}\n\nexport interface CdnAssetsOptions {\n /**\n * Decode an atlas blob when `createImageBitmap` is unavailable (e.g. Node 22\n * with fetch but no ImageBitmap). Required for node consumers of cdnAssets;\n * browser builds ignore this when createImageBitmap exists.\n */\n decodeImage?: (blob: Blob, signal?: AbortSignal) => Promise<ImageSource>;\n fetchImpl?: typeof fetch;\n /** Maximum simultaneous bitmap decodes. Fetches remain concurrent. Default: 2. */\n maxConcurrentDecodes?: number;\n /** Optional sink for fetch/decode timing events (profiling). */\n onAssetEvent?: (event: AssetEvent) => void;\n}\n\nasync function loadJson<T>(\n url: string,\n fetchImpl: typeof fetch,\n signal?: AbortSignal,\n): Promise<{ value: T; bytes?: number; fetchMs: number }> {\n const t0 = nowMs();\n const res = await fetchImpl(url, signal ? { signal } : undefined);\n if (!res.ok) {\n throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`);\n }\n const body = await res.arrayBuffer();\n const fetchMs = nowMs() - t0;\n const text = new TextDecoder().decode(body);\n return { value: JSON.parse(text) as T, bytes: body.byteLength, fetchMs };\n}\n\nfunction decodeLimiter(\n limit: number,\n): <T>(task: () => Promise<T>) => Promise<{ value: T; queueMs: number }> {\n let active = 0;\n const waiting: (() => void)[] = [];\n\n const acquire = (): Promise<void> => {\n if (active < limit) {\n active++;\n return Promise.resolve();\n }\n return new Promise((resolve) => {\n waiting.push(() => {\n active++;\n resolve();\n });\n });\n };\n\n const release = (): void => {\n active--;\n waiting.shift()?.();\n };\n\n return async <T>(task: () => Promise<T>) => {\n const queuedAt = nowMs();\n await acquire();\n const queueMs = nowMs() - queuedAt;\n try {\n return { value: await task(), queueMs };\n } finally {\n release();\n }\n };\n}\n\nasync function blobToImage(\n blob: Blob,\n decodeImage?: (blob: Blob, signal?: AbortSignal) => Promise<ImageSource>,\n signal?: AbortSignal,\n): Promise<{ image: ImageSource; decodeMs: number }> {\n throwIfAborted(signal);\n const t0 = nowMs();\n if (typeof createImageBitmap === \"function\") {\n const image = await createImageBitmap(blob);\n throwIfAborted(signal);\n return { image: image as ImageSource, decodeMs: nowMs() - t0 };\n }\n if (decodeImage) {\n const image = await decodeImage(blob, signal);\n throwIfAborted(signal);\n return { image, decodeMs: nowMs() - t0 };\n }\n throw new Error(\n \"createImageBitmap is unavailable; pass cdnAssets(baseUrl, { decodeImage }) \" +\n \"or use localAssets from fpsr/node\",\n );\n}\n\n/**\n * Fetch-based AssetSource for CDN (or any HTTP) layouts:\n * {baseUrl}/manifest.json\n * {baseUrl}/{manifest.tiers[tier].renderDb.file}\n * {baseUrl}/{atlas.file}\n */\nexport function cdnAssets(baseUrl: string, options?: CdnAssetsOptions): AssetSource {\n const root = baseUrl.replace(/\\/+$/, \"\");\n const fetchImpl = options?.fetchImpl ?? fetch;\n const onAssetEvent = options?.onAssetEvent;\n const maxConcurrentDecodes = Math.max(1, Math.floor(options?.maxConcurrentDecodes ?? 2));\n const withDecodeSlot = decodeLimiter(maxConcurrentDecodes);\n let manifestPromise: Promise<AssetManifest> | undefined;\n let manifestReady = false;\n const dbPromises = new Map<AssetTier, Promise<RenderDb>>();\n const dbReady = new Set<AssetTier>();\n const atlasCache = new Map<string, Promise<ImageSource>>();\n const atlasReady = new Set<string>();\n let fontsPromise: Promise<void> | undefined;\n\n /**\n * Shared loads intentionally omit AbortSignal so one cancelled waiter does\n * not cancel the underlying fetch for other consumers. Waiters race via\n * {@link raceWithAbort}.\n */\n const loadManifest = (): Promise<AssetManifest> => {\n if (!manifestPromise) {\n const url = `${root}/manifest.json`;\n manifestPromise = (async () => {\n const t0 = nowMs();\n const { value, bytes, fetchMs } = await loadJson<AssetManifest>(url, fetchImpl);\n if (value.schema !== 2) {\n throw new Error(`Unsupported asset manifest schema: ${String(value.schema)}`);\n }\n if (!value.tiers?.[\"1x\"]?.renderDb.file || !value.tiers?.[\"2x\"]?.renderDb.file) {\n throw new Error(\"Asset manifest is missing required 1x/2x tiers\");\n }\n onAssetEvent?.({\n kind: \"manifest\",\n url,\n cached: false,\n fetchMs,\n totalMs: nowMs() - t0,\n bytes,\n });\n manifestReady = true;\n return value;\n })().catch((error) => {\n manifestPromise = undefined;\n manifestReady = false;\n throw error;\n });\n } else if (manifestReady) {\n onAssetEvent?.({\n kind: \"manifest\",\n url: `${root}/manifest.json`,\n cached: true,\n totalMs: 0,\n });\n }\n return manifestPromise;\n };\n\n const source: AssetSource = {\n loadRenderDb(tier: AssetTier = \"2x\", loadOptions?: AssetLoadOptions): Promise<RenderDb> {\n throwIfAborted(loadOptions?.signal);\n let pending = dbPromises.get(tier);\n if (!pending) {\n pending = (async () => {\n const t0 = nowMs();\n const manifest = await loadManifest();\n const url = `${root}/${manifest.tiers[tier].renderDb.file}`;\n const { value, bytes, fetchMs } = await loadJson<RenderDb>(url, fetchImpl);\n if (value.schema !== 2) {\n throw new Error(`Unsupported render-db schema: ${String(value.schema)}`);\n }\n onAssetEvent?.({\n kind: \"render-db\",\n tier,\n url,\n cached: false,\n fetchMs,\n totalMs: nowMs() - t0,\n bytes,\n });\n dbReady.add(tier);\n return value;\n })().catch((error) => {\n dbPromises.delete(tier);\n dbReady.delete(tier);\n throw error;\n });\n dbPromises.set(tier, pending);\n } else if (dbReady.has(tier)) {\n onAssetEvent?.({\n kind: \"render-db\",\n tier,\n url: undefined,\n cached: true,\n totalMs: 0,\n });\n }\n return raceWithAbort(pending, loadOptions?.signal);\n },\n\n loadAtlasImage(\n index: number,\n tier: AssetTier = \"2x\",\n loadOptions?: AssetLoadOptions,\n ): Promise<ImageSource> {\n throwIfAborted(loadOptions?.signal);\n const cacheKey = `${tier}:${index}`;\n let pending = atlasCache.get(cacheKey);\n if (!pending) {\n pending = (async () => {\n const t0 = nowMs();\n const manifest = await loadManifest();\n const entry = manifest.tiers[tier].atlases[index];\n if (!entry) {\n throw new Error(`Atlas index ${index} missing from manifest`);\n }\n const url = `${root}/${entry.file}`;\n const tFetch = nowMs();\n const res = await fetchImpl(url);\n if (!res.ok) {\n throw new Error(`Failed to fetch atlas ${url}: ${res.status}`);\n }\n const blob = await res.blob();\n const fetchMs = nowMs() - tFetch;\n const { value: decoded, queueMs } = await withDecodeSlot(() =>\n blobToImage(blob, options?.decodeImage),\n );\n const { image, decodeMs } = decoded;\n onAssetEvent?.({\n kind: \"atlas\",\n index,\n tier,\n url,\n cached: false,\n fetchMs,\n queueMs,\n decodeMs,\n decodedPixels: entry.w * entry.h,\n totalMs: nowMs() - t0,\n bytes: blob.size,\n });\n atlasReady.add(cacheKey);\n return image;\n })().catch((error) => {\n atlasCache.delete(cacheKey);\n atlasReady.delete(cacheKey);\n throw error;\n });\n atlasCache.set(cacheKey, pending);\n } else if (atlasReady.has(cacheKey)) {\n onAssetEvent?.({\n kind: \"atlas\",\n index,\n tier,\n cached: true,\n totalMs: 0,\n });\n }\n return raceWithAbort(pending, loadOptions?.signal);\n },\n\n ensureFonts(loadOptions?: AssetLoadOptions): Promise<void> {\n throwIfAborted(loadOptions?.signal);\n if (!fontsPromise) {\n fontsPromise = (async () => {\n try {\n const manifest = await loadManifest();\n const fonts = manifest.fonts ?? [];\n if (fonts.length === 0) return;\n if (typeof FontFace === \"undefined\" || typeof document === \"undefined\") return;\n await Promise.all(\n fonts.map(async (font) => {\n const url = `${root}/${font.file}`;\n const face = new FontFace(font.family, `url(${url})`);\n const loaded = await face.load();\n // FontFaceSet typings omit `add` in some DOM lib versions.\n (document.fonts as FontFaceSet & { add(face: FontFace): void }).add(loaded);\n }),\n );\n } catch {\n // Font registration is best-effort; canvas falls back to system fonts.\n }\n })();\n }\n return raceWithAbort(fontsPromise, loadOptions?.signal);\n },\n\n dispose(): void {\n for (const pending of atlasCache.values()) {\n void pending.then((image) => {\n const maybeBitmap = image as { close?: () => void };\n maybeBitmap.close?.();\n });\n }\n atlasCache.clear();\n atlasReady.clear();\n dbPromises.clear();\n dbReady.clear();\n manifestPromise = undefined;\n manifestReady = false;\n fontsPromise = undefined;\n },\n };\n\n return source;\n}\n","import type { DrawListBounds } from \"./types/draw-list.js\";\n\n/** Integer tile-space viewport used for canvas sizing and pan (maps minX/minY to pixel 0). */\nexport interface TileFrame {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n}\n\n/**\n * Tile-aligned viewport from draw-list visual bounds, optionally plus `padTiles`\n * on each side. Visual bounds already include sprite overhang, shadows, and wires;\n * `padTiles` defaults to 0 in the renderer. All edges are integers so the canvas is\n * an exact multiple of `pixelsPerTile` with no fragmented checker cells.\n */\nexport function computeTileFrame(bounds: DrawListBounds, padTiles: number): TileFrame {\n return {\n minX: Math.floor(bounds.minX) - padTiles,\n minY: Math.floor(bounds.minY) - padTiles,\n maxX: Math.ceil(bounds.maxX) + padTiles,\n maxY: Math.ceil(bounds.maxY) + padTiles,\n };\n}\n","import { Zlib } from \"fflate\";\n\nconst PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);\nconst PNG_MAX_DIMENSION = 0x7fff_ffff;\n\nconst crcTable = (() => {\n const table = new Uint32Array(256);\n for (let i = 0; i < table.length; i++) {\n let value = i;\n for (let bit = 0; bit < 8; bit++) {\n value = value & 1 ? 0xedb8_8320 ^ (value >>> 1) : value >>> 1;\n }\n table[i] = value >>> 0;\n }\n return table;\n})();\n\nfunction writeUint32(target: Uint8Array, offset: number, value: number): void {\n target[offset] = (value >>> 24) & 0xff;\n target[offset + 1] = (value >>> 16) & 0xff;\n target[offset + 2] = (value >>> 8) & 0xff;\n target[offset + 3] = value & 0xff;\n}\n\nfunction crc32(type: Uint8Array, data: Uint8Array): number {\n let crc = 0xffff_ffff;\n for (const bytes of [type, data]) {\n for (const byte of bytes) {\n crc = (crcTable[(crc ^ byte) & 0xff] ?? 0) ^ (crc >>> 8);\n }\n }\n return (crc ^ 0xffff_ffff) >>> 0;\n}\n\nfunction pngChunk(type: string, data = new Uint8Array()): Uint8Array {\n const typeBytes = new TextEncoder().encode(type);\n const chunk = new Uint8Array(12 + data.length);\n writeUint32(chunk, 0, data.length);\n chunk.set(typeBytes, 4);\n chunk.set(data, 8);\n writeUint32(chunk, 8 + data.length, crc32(typeBytes, data));\n return chunk;\n}\n\nconst blobPart = (bytes: Uint8Array): ArrayBuffer => bytes.slice().buffer as ArrayBuffer;\n\nexport interface StreamingPngEncoder {\n readonly width: number;\n readonly height: number;\n readonly rowsWritten: number;\n writeRgbaRows(rgba: Uint8Array, rowCount: number): void;\n finish(): Blob;\n}\n\n/**\n * Incremental 8-bit RGBA PNG encoder. Only compressed PNG chunks are retained;\n * callers control raw working memory by choosing how many rows to submit.\n */\nexport function createStreamingPngEncoder(width: number, height: number): StreamingPngEncoder {\n if (!Number.isInteger(width) || width <= 0 || width > PNG_MAX_DIMENSION) {\n throw new Error(`PNG width must be an integer between 1 and ${PNG_MAX_DIMENSION}`);\n }\n if (!Number.isInteger(height) || height <= 0 || height > PNG_MAX_DIMENSION) {\n throw new Error(`PNG height must be an integer between 1 and ${PNG_MAX_DIMENSION}`);\n }\n\n const ihdr = new Uint8Array(13);\n writeUint32(ihdr, 0, width);\n writeUint32(ihdr, 4, height);\n ihdr[8] = 8; // bit depth\n ihdr[9] = 6; // RGBA\n const chunks: BlobPart[] = [blobPart(PNG_SIGNATURE), blobPart(pngChunk(\"IHDR\", ihdr))];\n let rowsWritten = 0;\n let finished = false;\n const zlib = new Zlib({ level: 6 }, (data) => {\n if (data.length > 0) chunks.push(blobPart(pngChunk(\"IDAT\", data)));\n });\n\n return {\n width,\n height,\n get rowsWritten() {\n return rowsWritten;\n },\n writeRgbaRows(rgba, rowCount) {\n if (finished) throw new Error(\"PNG encoder is already finished\");\n if (!Number.isInteger(rowCount) || rowCount <= 0 || rowsWritten + rowCount > height) {\n throw new Error(\"PNG row count exceeds the declared image height\");\n }\n const rgbaStride = width * 4;\n if (rgba.length !== rgbaStride * rowCount) {\n throw new Error(\n `Expected ${rgbaStride * rowCount} RGBA bytes for ${rowCount} rows, got ${rgba.length}`,\n );\n }\n const scanlines = new Uint8Array((rgbaStride + 1) * rowCount);\n for (let row = 0; row < rowCount; row++) {\n const sourceStart = row * rgbaStride;\n const targetStart = row * (rgbaStride + 1) + 1;\n scanlines.set(rgba.subarray(sourceStart, sourceStart + rgbaStride), targetStart);\n }\n rowsWritten += rowCount;\n zlib.push(scanlines, rowsWritten === height);\n },\n finish() {\n if (finished) throw new Error(\"PNG encoder is already finished\");\n if (rowsWritten !== height) {\n throw new Error(`PNG is incomplete: wrote ${rowsWritten} of ${height} rows`);\n }\n finished = true;\n chunks.push(blobPart(pngChunk(\"IEND\")));\n return new Blob(chunks, { type: \"image/png\" });\n },\n };\n}\n","/**\n * Discriminated background mode for {@link RenderOptions}.\n *\n * Replaces the former combinatorial boolean flags (`showCheckerboard`,\n * `showSpace`, `showBackgroundAuto`, `terrainBackground`, …).\n */\nexport type RenderBackground =\n | { type: \"none\" }\n | { type: \"solid\"; color: [number, number, number, number] }\n | { type: \"checkerboard\" }\n | {\n type: \"space\";\n /** Draw the render-db space-platform planet decoration. */\n planet?: boolean;\n /** Factorio planet prototype name when `planet` is true. */\n planetName?: string;\n }\n | { type: \"terrain\"; name: string }\n | {\n /**\n * Pick checkerboard vs space from the blueprint (space platform → space\n * starfield without planet).\n */\n type: \"auto\";\n };\n\nexport class UnknownTerrainBackgroundError extends Error {\n readonly name = \"UnknownTerrainBackgroundError\";\n readonly terrainName: string;\n\n constructor(terrainName: string) {\n super(`Unknown terrain background \"${terrainName}\"`);\n this.terrainName = terrainName;\n }\n}\n\nexport class AssetDensityMismatchError extends Error {\n readonly name = \"AssetDensityMismatchError\";\n readonly tier: \"1x\" | \"2x\";\n readonly expectedDensity: 1 | 2;\n readonly actualDensity: 1 | 2 | undefined;\n\n constructor(tier: \"1x\" | \"2x\", expectedDensity: 1 | 2, actualDensity: 1 | 2 | undefined) {\n super(\n `Render DB assetDensity ${actualDensity ?? \"undefined\"} does not match asset tier ${tier} (expected ${expectedDensity})`,\n );\n this.tier = tier;\n this.expectedDensity = expectedDensity;\n this.actualDensity = actualDensity;\n }\n}\n\n/** Resolved concrete background flags used by the Canvas2D backend. */\nexport interface ResolvedBackground {\n solid: [number, number, number, number] | null;\n showCheckerboard: boolean;\n showSpace: boolean;\n showSpacePlanet: boolean;\n spacePlanet?: string;\n terrainName?: string;\n}\n\nexport function resolveBackground(\n prefersPlatformGraphics: boolean,\n background?: RenderBackground,\n): ResolvedBackground {\n const mode = background ?? { type: \"none\" };\n switch (mode.type) {\n case \"none\":\n return {\n solid: null,\n showCheckerboard: false,\n showSpace: false,\n showSpacePlanet: false,\n };\n case \"solid\":\n return {\n solid: mode.color,\n showCheckerboard: false,\n showSpace: false,\n showSpacePlanet: false,\n };\n case \"checkerboard\":\n return {\n solid: null,\n showCheckerboard: true,\n showSpace: false,\n showSpacePlanet: false,\n };\n case \"space\":\n return {\n solid: null,\n showCheckerboard: false,\n showSpace: true,\n showSpacePlanet: mode.planet === true,\n spacePlanet: mode.planetName,\n };\n case \"terrain\":\n return {\n solid: null,\n showCheckerboard: false,\n showSpace: false,\n showSpacePlanet: false,\n terrainName: mode.name,\n };\n case \"auto\": {\n const useSpace = prefersPlatformGraphics;\n return {\n solid: null,\n showCheckerboard: !useSpace,\n showSpace: useSpace,\n showSpacePlanet: false,\n };\n }\n default: {\n const _exhaustive: never = mode;\n return _exhaustive;\n }\n }\n}\n","import { raceWithAbort, throwIfAborted as throwIfSignalAborted } from \"./abort.js\";\nimport type { AssetSource, AssetTier } from \"./assets.js\";\nimport {\n AssetDensityMismatchError,\n resolveBackground,\n UnknownTerrainBackgroundError,\n type RenderBackground,\n} from \"./background.js\";\nimport { selectBlueprint, selectDeconstructionPlanner, selectUpgradePlanner } from \"./book.js\";\nimport type { Canvas2DContextLike, ExecuteDrawListStats } from \"./canvas2d.js\";\nimport * as canvas2d from \"./canvas2d.js\";\nimport { computeTileFrame, type TileFrame } from \"./frame.js\";\nimport type { ImageDataLike, ImageSource } from \"./host.js\";\nimport {\n bakeEntityInfoSilhouette,\n bakeEntityInfoSilhouetteFromImageData,\n ENTITY_INFO_SILHOUETTE_BLUR_PX,\n ENTITY_INFO_SILHOUETTE_RADIUS_PX,\n type ImageDataContext,\n type SilhouetteCanvasLike,\n} from \"./icon-silhouette.js\";\nimport { planDrawListInternal } from \"./plan.js\";\nimport { planDeconstructionPlannerDrawList } from \"./plan/deconstruction-planner.js\";\nimport { planUpgradePlannerDrawList } from \"./plan/upgrade-planner.js\";\nimport { createStreamingPngEncoder } from \"./png-stream.js\";\nimport {\n nowMs,\n perfMark,\n perfMeasure,\n type AssetEvent,\n type DrawListStats,\n type PlanProfile,\n type RenderProfile,\n} from \"./profile.js\";\nimport { blueprintPrefersPlatformGraphics } from \"./resolve.js\";\nimport { drawListForTile } from \"./tiled-draw-list.js\";\nimport type { Blueprint, BlueprintDocument } from \"./types/blueprint.js\";\nimport type { DrawList } from \"./types/draw-list.js\";\nimport type { RenderDb, SpaceBackground, TerrainPatchBackground } from \"./types/render-db.js\";\n\nexport { AssetDensityMismatchError, UnknownTerrainBackgroundError } from \"./background.js\";\nexport type { RenderBackground } from \"./background.js\";\n\nexport interface CanvasLike {\n width: number;\n height: number;\n getContext(type: \"2d\"): Canvas2DContextLike | null;\n convertToBlob?(options?: { type?: string; quality?: number }): Promise<Blob>;\n toBuffer?(mime?: string, options?: { quality?: number }): Uint8Array | Promise<Uint8Array>;\n}\n\nexport type CreateCanvasFn = (width: number, height: number) => CanvasLike;\n\nexport type RenderImageMimeType = \"image/png\" | \"image/webp\";\n\nexport interface RenderImageOptions {\n /** Output image format. Defaults to PNG. */\n type?: RenderImageMimeType;\n /** Encoder quality from 0 to 1 for lossy formats such as WebP. */\n quality?: number;\n}\n\nexport interface MaxOutputSize {\n width: number;\n height: number;\n}\n\nexport interface RenderMeasurement {\n tileFrame: TileFrame;\n requestedPixelsPerTile: number;\n pixelsPerTile: number;\n requestedWidth: number;\n requestedHeight: number;\n width: number;\n height: number;\n capped: boolean;\n}\n\nexport interface CreateRendererOptions {\n assets: AssetSource;\n /** Physical atlas tier. Defaults to 2x. */\n assetTier?: AssetTier;\n /** Preloaded render-db; when omitted, loaded once via assets.loadRenderDb(). */\n renderDb?: RenderDb;\n /** Canvas factory; defaults to OffscreenCanvas / document.createElement. */\n createCanvas?: CreateCanvasFn;\n}\n\nexport type RenderProgressEvent =\n | { stage: \"planning\" }\n | { stage: \"loading-assets\"; completed: number; total: number }\n | { stage: \"baking-icons\" }\n | { stage: \"painting\" }\n | { stage: \"painting-tiles\"; completed: number; total: number }\n | { stage: \"encoding\" }\n | { stage: \"complete\" };\n\n/**\n * Layout-only options for {@link Renderer.measure}.\n * Honored fields affect planned bounds / output size; paint-only options are omitted.\n */\nexport interface MeasureOptions {\n blueprintPath?: number[];\n pixelsPerTile?: number;\n maxOutputSize?: MaxOutputSize;\n padTiles?: number;\n altMode?: boolean;\n beltEndings?: boolean;\n signal?: AbortSignal;\n}\n\n/** Public render options (no prepared-viewport / tiled internals). */\nexport interface RenderOptions {\n blueprintPath?: number[];\n pixelsPerTile?: number;\n /**\n * Fit the output inside this box by lowering pixelsPerTile before painting.\n * The renderer never creates a full-resolution intermediate canvas.\n */\n maxOutputSize?: MaxOutputSize;\n altMode?: boolean;\n beltEndings?: boolean;\n /** Discriminated background mode. Defaults to `{ type: \"none\" }`. */\n background?: RenderBackground;\n padTiles?: number;\n /** Draw tile grid lines and map-space coordinate labels. */\n showCoordinates?: boolean;\n /** Reuse an existing canvas instead of creating one. */\n canvas?: CanvasLike;\n /** Cancel before the destination canvas is resized or painted. */\n signal?: AbortSignal;\n /** Coarse, host-neutral render stages suitable for progress UI. */\n onProgress?: (event: RenderProgressEvent) => void;\n /**\n * When true, collect stage timings / draw-list stats onto `RenderResult.profile`.\n * Near-zero overhead when omitted/false. Ignored by {@link Renderer.renderTiledPng}.\n */\n profile?: boolean;\n}\n\n/**\n * Internal prepared-viewport paint options used by the viewer tiled preview and\n * the tiled PNG exporter. Not part of the stable public `render()` contract —\n * see `internal/prepared-viewport.ts`.\n */\nexport interface PreparedViewportRenderOptions extends RenderOptions {\n /** Advanced: render only this tile-aligned viewport. */\n tileFrame: TileFrame;\n /** Full frame containing `tileFrame`, used to anchor global backgrounds. */\n outputTileFrame: TileFrame;\n /** Prepared draw list (already clipped) to avoid replanning each tile. */\n preparedDrawList: DrawList;\n}\n\nexport interface RenderResult {\n canvas: CanvasLike;\n width: number;\n height: number;\n drawList: DrawList;\n tileFrame: TileFrame;\n /** Present when `RenderOptions.profile` was true. */\n profile?: RenderProfile;\n /** Encode the current canvas directly into the requested image format. */\n toImageBlob(options?: RenderImageOptions): Promise<Blob>;\n /** Encode the current canvas directly into a browser- or Node-compatible byte array. */\n toImageBuffer(options?: RenderImageOptions): Promise<Uint8Array>;\n toPngBlob(): Promise<Blob>;\n toPngBuffer(): Promise<Uint8Array>;\n}\n\nexport interface Renderer {\n /** Plan bounds and output dimensions without loading atlases or painting. */\n measure(docOrBlueprint: BlueprintDocument | Blueprint, opts?: MeasureOptions): RenderMeasurement;\n render(\n docOrBlueprint: BlueprintDocument | Blueprint,\n opts?: RenderOptions,\n ): Promise<RenderResult>;\n /** Render a full-resolution PNG with bounded raw-pixel working memory. */\n renderTiledPng(\n docOrBlueprint: BlueprintDocument | Blueprint,\n opts?: TiledPngOptions,\n ): Promise<TiledPngResult>;\n /**\n * Release renderer-owned derived caches (icon crops / silhouettes).\n * Does **not** dispose the caller-supplied {@link AssetSource} or close\n * atlas `ImageSource` objects owned by that source.\n */\n dispose(): void;\n}\n\nexport interface TiledPngOptions extends Omit<\n RenderOptions,\n \"canvas\" | \"maxOutputSize\" | \"profile\" | \"onProgress\"\n> {\n /** Target maximum edge of each temporary canvas. Defaults to 2048 px. */\n tileSize?: number;\n /** Maximum assembled raw strip memory. Defaults to 32 MiB. */\n maxStripeBytes?: number;\n signal?: AbortSignal;\n onProgress?: (event: RenderProgressEvent) => void;\n}\n\nexport interface TiledPngResult {\n blob: Blob;\n width: number;\n height: number;\n tileFrame: TileFrame;\n tiled: true;\n}\n\nfunction finitePositive(value: number, label: string): number {\n if (!Number.isFinite(value) || value <= 0) {\n throw new Error(`${label} must be a finite number greater than zero`);\n }\n return value;\n}\n\nexport function measureTileFrame(\n tileFrame: TileFrame,\n requestedPixelsPerTile: number,\n maxOutputSize?: MaxOutputSize,\n): RenderMeasurement {\n const requested = finitePositive(requestedPixelsPerTile, \"pixelsPerTile\");\n const tilesWide = Math.max(0, tileFrame.maxX - tileFrame.minX);\n const tilesHigh = Math.max(0, tileFrame.maxY - tileFrame.minY);\n const requestedWidth = Math.max(1, Math.floor(tilesWide * requested));\n const requestedHeight = Math.max(1, Math.floor(tilesHigh * requested));\n\n let pixelsPerTile = requested;\n if (maxOutputSize) {\n const maxWidth = finitePositive(maxOutputSize.width, \"maxOutputSize.width\");\n const maxHeight = finitePositive(maxOutputSize.height, \"maxOutputSize.height\");\n if (tilesWide > 0) pixelsPerTile = Math.min(pixelsPerTile, maxWidth / tilesWide);\n if (tilesHigh > 0) pixelsPerTile = Math.min(pixelsPerTile, maxHeight / tilesHigh);\n }\n\n const width = Math.max(1, Math.floor(tilesWide * pixelsPerTile));\n const height = Math.max(1, Math.floor(tilesHigh * pixelsPerTile));\n return {\n tileFrame,\n requestedPixelsPerTile: requested,\n pixelsPerTile,\n requestedWidth,\n requestedHeight,\n width,\n height,\n capped: pixelsPerTile < requested,\n };\n}\n\nfunction isBlueprint(value: BlueprintDocument | Blueprint): value is Blueprint {\n return (value as Blueprint).item === \"blueprint\";\n}\n\nfunction trySelectUpgradePlanner(\n doc: BlueprintDocument,\n path: number[] | undefined,\n): Record<string, unknown> | null {\n try {\n return selectUpgradePlanner(doc, path);\n } catch {\n return null;\n }\n}\n\nfunction trySelectDeconstructionPlanner(\n doc: BlueprintDocument,\n path: number[] | undefined,\n): Record<string, unknown> | null {\n try {\n return selectDeconstructionPlanner(doc, path);\n } catch {\n return null;\n }\n}\n\n/** Resolve Auto background into concrete checkerboard / space flags. */\nfunction resolveBackgroundOpts(\n bp: Blueprint | null,\n background: RenderBackground | undefined,\n): ReturnType<typeof resolveBackground> {\n return resolveBackground(bp ? blueprintPrefersPlatformGraphics(bp) : false, background);\n}\n\nfunction planDocumentDrawList(\n docOrBlueprint: BlueprintDocument | Blueprint,\n db: RenderDb,\n opts: {\n blueprintPath?: number[];\n altMode?: boolean;\n beltEndings?: boolean;\n profileOut?: PlanProfile;\n },\n): {\n drawList: DrawList;\n blueprint: Blueprint | null;\n} {\n if (isBlueprint(docOrBlueprint)) {\n return {\n blueprint: docOrBlueprint,\n drawList: planDrawListInternal(docOrBlueprint, db, {\n altMode: opts.altMode,\n beltEndings: opts.beltEndings,\n profileOut: opts.profileOut,\n }).drawList,\n };\n }\n\n const planner = trySelectUpgradePlanner(docOrBlueprint, opts.blueprintPath);\n if (planner) {\n return {\n blueprint: null,\n drawList: planUpgradePlannerDrawList(planner, db),\n };\n }\n\n const deconstruction = trySelectDeconstructionPlanner(docOrBlueprint, opts.blueprintPath);\n if (deconstruction) {\n return {\n blueprint: null,\n drawList: planDeconstructionPlannerDrawList(deconstruction, db),\n };\n }\n\n const blueprint = selectBlueprint(docOrBlueprint, opts.blueprintPath);\n return {\n blueprint,\n drawList: planDrawListInternal(blueprint, db, {\n altMode: opts.altMode,\n beltEndings: opts.beltEndings,\n profileOut: opts.profileOut,\n }).drawList,\n };\n}\n\nfunction defaultCreateCanvas(width: number, height: number): CanvasLike {\n if (typeof OffscreenCanvas !== \"undefined\") {\n return new OffscreenCanvas(width, height) as unknown as CanvasLike;\n }\n if (typeof document !== \"undefined\" && typeof document.createElement === \"function\") {\n const el = document.createElement(\"canvas\");\n el.width = width;\n el.height = height;\n return el as unknown as CanvasLike;\n }\n throw new Error(\n \"No canvas factory available. In Node, pass createCanvas from skia-canvas \" +\n \"(e.g. createRenderer({ assets, createCanvas: (w, h) => new Canvas(w, h) })) \" +\n \"or use fpsr/node helpers.\",\n );\n}\n\nasync function encodeCanvasBlob(\n canvas: CanvasLike,\n options: RenderImageOptions = {},\n): Promise<Blob> {\n const type = options.type ?? \"image/png\";\n let blob: Blob;\n if (typeof canvas.convertToBlob === \"function\") {\n blob = await canvas.convertToBlob({ type, quality: options.quality });\n } else {\n const htmlCanvas = canvas as unknown as {\n toBlob?: (callback: (blob: Blob | null) => void, type?: string, quality?: number) => void;\n };\n if (typeof htmlCanvas.toBlob !== \"function\") {\n throw new Error(\n \"Canvas does not support image blob encoding; use OffscreenCanvas or HTMLCanvasElement.\",\n );\n }\n blob = await new Promise<Blob>((resolve, reject) => {\n htmlCanvas.toBlob!(\n (encoded) => {\n if (encoded) resolve(encoded);\n else reject(new Error(\"canvas.toBlob returned null\"));\n },\n type,\n options.quality,\n );\n });\n }\n\n if (blob.type !== type) {\n throw new Error(`Canvas encoder does not support ${type}; returned ${blob.type || \"unknown\"}`);\n }\n return blob;\n}\n\nasync function encodeCanvasBuffer(\n canvas: CanvasLike,\n options: RenderImageOptions = {},\n): Promise<Uint8Array> {\n const type = options.type ?? \"image/png\";\n if (typeof canvas.toBuffer === \"function\") {\n const buffer = await canvas.toBuffer(type, { quality: options.quality });\n return buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\n }\n const blob = await encodeCanvasBlob(canvas, options);\n return new Uint8Array(await blob.arrayBuffer());\n}\n\nfunction collectAtlasIndices(\n list: DrawList,\n db: RenderDb,\n terrainBackground?: TerrainPatchBackground,\n spacePlanetFrameId?: number,\n): number[] {\n const set = new Set<number>();\n for (const cmd of list.commands) {\n if (cmd.kind === \"sprite\" || cmd.kind === \"icon\") {\n const frame = db.frames[cmd.frame];\n if (frame) set.add(frame.a);\n if (cmd.kind === \"icon\" && cmd.backingFrame != null) {\n const backing = db.frames[cmd.backingFrame];\n if (backing) set.add(backing.a);\n }\n }\n }\n for (const frameId of terrainBackground?.frames ?? []) {\n const frame = db.frames[frameId];\n if (frame) set.add(frame.a);\n }\n for (const patch of terrainBackground?.patches ?? []) {\n for (const frameId of patch.frames) {\n const frame = db.frames[frameId];\n if (frame) set.add(frame.a);\n }\n }\n if (spacePlanetFrameId != null) {\n const frame = db.frames[spacePlanetFrameId];\n if (frame) set.add(frame.a);\n }\n return [...set].sort((a, b) => a - b);\n}\n\nfunction selectTerrainBackground(\n db: RenderDb,\n terrainName: string | undefined,\n): TerrainPatchBackground | undefined {\n if (terrainName == null) return undefined;\n const background = db.terrainBackgrounds?.[terrainName];\n if (!background) {\n throw new UnknownTerrainBackgroundError(terrainName);\n }\n return background;\n}\n\n/** Resolve the starmap frame for an optional planet prototype name. */\nexport function resolveSpacePlanetFrameId(\n spaceBackground: SpaceBackground | undefined,\n spacePlanet?: string,\n): number | undefined {\n if (!spaceBackground) return undefined;\n if (spacePlanet != null) {\n const named = spaceBackground.planets?.[spacePlanet];\n if (named != null) return named;\n }\n return spaceBackground.planetFrame;\n}\n\nfunction selectSpaceBackground(\n db: RenderDb,\n resolved: ReturnType<typeof resolveBackground>,\n): SpaceBackground | undefined {\n if (!resolved.showSpace || !resolved.showSpacePlanet || !db.spaceBackground) return undefined;\n const planetFrame = resolveSpacePlanetFrameId(db.spaceBackground, resolved.spacePlanet);\n if (planetFrame == null) return undefined;\n return { ...db.spaceBackground, planetFrame };\n}\n\nfunction summarizeDrawList(list: DrawList, atlasIndices: number[]): DrawListStats {\n const byKind: Record<string, number> = {};\n const layerHistogram: Record<string, number> = {};\n const frames = new Set<number>();\n for (const cmd of list.commands) {\n byKind[cmd.kind] = (byKind[cmd.kind] ?? 0) + 1;\n const layerKey = String(cmd.layer);\n layerHistogram[layerKey] = (layerHistogram[layerKey] ?? 0) + 1;\n if (cmd.kind === \"sprite\" || cmd.kind === \"icon\") {\n frames.add(cmd.frame);\n if (cmd.kind === \"icon\" && cmd.backingFrame != null) frames.add(cmd.backingFrame);\n }\n }\n return {\n commandCount: list.commands.length,\n byKind,\n uniqueFrames: frames.size,\n layerHistogram,\n atlasIndices,\n };\n}\n\nfunction emptyPlanProfile(): PlanProfile {\n return {\n migrateMs: 0,\n resolveMs: 0,\n tilesMs: 0,\n entitiesMs: 0,\n overlaysMs: 0,\n sortMs: 0,\n totalMs: 0,\n };\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n throwIfSignalAborted(signal, \"Render aborted\");\n}\n\n/**\n * Internal registry so tiled/viewer hosts can paint prepared viewports without\n * exposing that method on the stable {@link Renderer} type.\n */\nconst preparedViewportByRenderer = new WeakMap<\n Renderer,\n (\n docOrBlueprint: BlueprintDocument | Blueprint,\n opts: PreparedViewportRenderOptions,\n ) => Promise<RenderResult>\n>();\n\n/** @internal Used by `internal/prepared-viewport.ts` only. */\nexport function getPreparedViewportHandler(\n renderer: Renderer,\n):\n | ((\n docOrBlueprint: BlueprintDocument | Blueprint,\n opts: PreparedViewportRenderOptions,\n ) => Promise<RenderResult>)\n | undefined {\n return preparedViewportByRenderer.get(renderer);\n}\n\nfunction reportProgress(\n opts: { signal?: AbortSignal; onProgress?: (event: RenderProgressEvent) => void },\n event: RenderProgressEvent,\n): void {\n if (!opts.signal?.aborted) opts.onProgress?.(event);\n}\n\n/**\n * Create a renderer that loads the render-db once and lazily loads atlases\n * referenced by each draw list.\n */\nexport async function createRenderer(options: CreateRendererOptions): Promise<Renderer> {\n const { assets } = options;\n const assetTier = options.assetTier ?? \"2x\";\n const createCanvas = options.createCanvas ?? defaultCreateCanvas;\n const db = options.renderDb ?? (await assets.loadRenderDb(assetTier));\n const expectedDensity = assetTier === \"1x\" ? 1 : 2;\n if (db.assetDensity != null && db.assetDensity !== expectedDensity) {\n throw new AssetDensityMismatchError(assetTier, expectedDensity, db.assetDensity);\n }\n await assets.ensureFonts?.();\n const atlasCache = new Map<number, Promise<ImageSource>>();\n const iconImageCache = new Map<string, ImageSource>();\n const silhouetteImageCache = new Map<string, ImageSource>();\n\n /**\n * Cache only signal-independent shared atlas promises. Per-caller abort is\n * applied via {@link raceWithAbort} so concurrent renders are not coupled.\n */\n const loadAtlas = (index: number, signal?: AbortSignal): Promise<ImageSource> => {\n let pending = atlasCache.get(index);\n if (!pending) {\n pending = assets.loadAtlasImage(index, assetTier).catch((error) => {\n atlasCache.delete(index);\n throw error;\n });\n atlasCache.set(index, pending);\n }\n return raceWithAbort(pending, signal);\n };\n\n /** Shared paint path for both a fresh plan (`render`) and a prepared viewport. */\n async function paint(\n docOrBlueprint: BlueprintDocument | Blueprint,\n opts: RenderOptions & Partial<PreparedViewportRenderOptions>,\n ): Promise<RenderResult> {\n throwIfAborted(opts.signal);\n const wantProfile = opts.profile === true;\n const tTotal = wantProfile ? nowMs() : 0;\n if (wantProfile) perfMark(\"fpsr-render-start\");\n\n let t = wantProfile ? nowMs() : 0;\n const planProfile = wantProfile ? emptyPlanProfile() : undefined;\n reportProgress(opts, { stage: \"planning\" });\n if (wantProfile) perfMark(\"fpsr-plan-start\");\n\n let bp: Blueprint | null;\n let drawList: DrawList;\n if (opts.preparedDrawList !== undefined) {\n drawList = opts.preparedDrawList;\n if (isBlueprint(docOrBlueprint)) {\n bp = docOrBlueprint;\n } else {\n try {\n bp = selectBlueprint(docOrBlueprint, opts.blueprintPath);\n } catch {\n bp = null;\n }\n }\n } else {\n const planned = planDocumentDrawList(docOrBlueprint, db, {\n blueprintPath: opts.blueprintPath,\n altMode: opts.altMode,\n beltEndings: opts.beltEndings,\n profileOut: planProfile,\n });\n bp = planned.blueprint;\n drawList = planned.drawList;\n }\n const selectMs = wantProfile ? nowMs() - t : 0;\n const bg = resolveBackgroundOpts(bp, opts.background);\n\n const requestedPixelsPerTile = opts.pixelsPerTile ?? 64;\n const padTiles = opts.padTiles ?? 0;\n\n throwIfAborted(opts.signal);\n if (wantProfile) {\n perfMark(\"fpsr-plan-end\");\n perfMeasure(\"fpsr-plan\", \"fpsr-plan-start\", \"fpsr-plan-end\");\n }\n\n const terrainBackground = selectTerrainBackground(db, bg.terrainName);\n const spaceBackground = selectSpaceBackground(db, bg);\n const atlasIndices = collectAtlasIndices(\n drawList,\n db,\n terrainBackground,\n spaceBackground?.planetFrame,\n );\n const assetEvents: AssetEvent[] = [];\n let loadedAtlasCount = 0;\n reportProgress(opts, {\n stage: \"loading-assets\",\n completed: loadedAtlasCount,\n total: atlasIndices.length,\n });\n\n if (wantProfile) perfMark(\"fpsr-assets-start\");\n const tAssets = wantProfile ? nowMs() : 0;\n const loaded = await Promise.all(\n atlasIndices.map(async (i) => {\n const cached = atlasCache.has(i);\n const tAtlas = wantProfile ? nowMs() : 0;\n const img = await loadAtlas(i, opts.signal);\n loadedAtlasCount++;\n reportProgress(opts, {\n stage: \"loading-assets\",\n completed: loadedAtlasCount,\n total: atlasIndices.length,\n });\n if (wantProfile) {\n const atlas = db.atlases[i];\n assetEvents.push({\n kind: \"atlas\",\n index: i,\n tier: assetTier,\n cached,\n decodedPixels: atlas ? atlas.width * atlas.height : undefined,\n totalMs: nowMs() - tAtlas,\n });\n }\n return img;\n }),\n );\n const assetsMs = wantProfile ? nowMs() - tAssets : 0;\n throwIfAborted(opts.signal);\n if (wantProfile) {\n perfMark(\"fpsr-assets-end\");\n perfMeasure(\"fpsr-assets\", \"fpsr-assets-start\", \"fpsr-assets-end\");\n }\n\n const images: ImageSource[] = [];\n for (let i = 0; i < atlasIndices.length; i++) {\n const idx = atlasIndices[i];\n const img = loaded[i];\n if (idx !== undefined && img !== undefined) {\n images[idx] = img;\n }\n }\n\n t = wantProfile ? nowMs() : 0;\n reportProgress(opts, { stage: \"baking-icons\" });\n const iconImages = new Map<number, ImageSource>();\n const iconImageData = new Map<number, ImageDataLike>();\n const silhouetteImages = new Map<number, ImageSource>();\n const seenIconKeys = new Set<string>();\n const seenSilhouetteKeys = new Set<string>();\n let iconCacheHits = 0;\n let iconCacheMisses = 0;\n let silhouetteCacheHits = 0;\n let silhouetteCacheMisses = 0;\n for (const cmd of drawList.commands) {\n if (\n cmd.kind !== \"icon\" ||\n (cmd.backingFrame == null && cmd.backing !== true && cmd.silhouette !== true)\n ) {\n continue;\n }\n const frame = db.frames[cmd.frame];\n const atlasImage = frame ? images[frame.a] : undefined;\n if (!frame || !atlasImage || frame.w <= 0 || frame.h <= 0) continue;\n\n const packedWidth = frame.pw ?? frame.w;\n const packedHeight = frame.ph ?? frame.h;\n\n const iconKey = `${assetTier}:${cmd.frame}:${packedWidth}x${packedHeight}`;\n const cachedIcon = iconImageCache.get(iconKey);\n if (!seenIconKeys.has(iconKey)) {\n seenIconKeys.add(iconKey);\n if (cachedIcon) iconCacheHits++;\n }\n if (cachedIcon) {\n iconImages.set(cmd.frame, cachedIcon);\n } else if (!iconImages.has(cmd.frame)) {\n const iconCanvas = createCanvas(packedWidth, packedHeight);\n iconCanvas.width = packedWidth;\n iconCanvas.height = packedHeight;\n const iconContext = iconCanvas.getContext(\"2d\");\n if (!iconContext) continue;\n iconContext.drawImage(\n atlasImage,\n frame.x,\n frame.y,\n packedWidth,\n packedHeight,\n 0,\n 0,\n packedWidth,\n packedHeight,\n );\n const image = iconCanvas as unknown as ImageSource;\n iconImages.set(cmd.frame, image);\n iconImageCache.set(iconKey, image);\n const readableContext = iconContext as unknown as Partial<ImageDataContext>;\n if (\n cmd.backingStyle !== \"request-pin\" &&\n typeof readableContext.getImageData === \"function\"\n ) {\n iconImageData.set(\n cmd.frame,\n readableContext.getImageData(0, 0, packedWidth, packedHeight),\n );\n }\n iconCacheMisses++;\n }\n\n if (cmd.backingStyle === \"request-pin\" || silhouetteImages.has(cmd.frame)) continue;\n const densityScale = (db.assetDensity ?? 2) / 2;\n const dilateRadius = Math.max(1, Math.round(ENTITY_INFO_SILHOUETTE_RADIUS_PX * densityScale));\n const blurRadius = Math.max(1, Math.round(ENTITY_INFO_SILHOUETTE_BLUR_PX * densityScale));\n const silhouetteKey = `${iconKey}:${dilateRadius}:${blurRadius}`;\n const cachedSilhouette = silhouetteImageCache.get(silhouetteKey);\n if (!seenSilhouetteKeys.has(silhouetteKey)) {\n seenSilhouetteKeys.add(silhouetteKey);\n if (cachedSilhouette) silhouetteCacheHits++;\n }\n if (cachedSilhouette) {\n silhouetteImages.set(cmd.frame, cachedSilhouette);\n continue;\n }\n const iconSource = iconImages.get(cmd.frame);\n if (!iconSource) continue;\n const sourceData = iconImageData.get(cmd.frame);\n const silhouette = sourceData\n ? bakeEntityInfoSilhouetteFromImageData(\n sourceData,\n createCanvas as (width: number, height: number) => SilhouetteCanvasLike,\n dilateRadius,\n blurRadius,\n )\n : bakeEntityInfoSilhouette(\n iconSource,\n packedWidth,\n packedHeight,\n createCanvas as (width: number, height: number) => SilhouetteCanvasLike,\n dilateRadius,\n blurRadius,\n );\n if (silhouette) {\n silhouetteImages.set(cmd.frame, silhouette);\n silhouetteImageCache.set(silhouetteKey, silhouette);\n silhouetteCacheMisses++;\n }\n }\n const iconBakeMs = wantProfile ? nowMs() - t : 0;\n\n t = wantProfile ? nowMs() : 0;\n const outputTileFrame = opts.outputTileFrame ?? computeTileFrame(drawList.bounds, padTiles);\n const tileFrame = opts.tileFrame ?? outputTileFrame;\n if (\n tileFrame.minX < outputTileFrame.minX ||\n tileFrame.minY < outputTileFrame.minY ||\n tileFrame.maxX > outputTileFrame.maxX ||\n tileFrame.maxY > outputTileFrame.maxY ||\n tileFrame.minX >= tileFrame.maxX ||\n tileFrame.minY >= tileFrame.maxY\n ) {\n throw new Error(\"tileFrame must be a non-empty viewport inside outputTileFrame\");\n }\n const output = measureTileFrame(tileFrame, requestedPixelsPerTile, opts.maxOutputSize);\n const { width, height, pixelsPerTile } = output;\n\n throwIfAborted(opts.signal);\n const canvas = opts.canvas ?? createCanvas(width, height);\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n throw new Error(\"Failed to acquire 2d canvas context\");\n }\n const frameMs = wantProfile ? nowMs() - t : 0;\n\n if (wantProfile) perfMark(\"fpsr-paint-start\");\n reportProgress(opts, { stage: \"painting\" });\n t = wantProfile ? nowMs() : 0;\n const paintStats: ExecuteDrawListStats = {\n shadowRuns: 0,\n shadowTiles: 0,\n shadowCompositedPixels: 0,\n shadowPeakScratchPixels: 0,\n };\n const paintList = opts.tileFrame ? drawListForTile(drawList, db.frames, tileFrame) : drawList;\n canvas2d.executeDrawList(ctx, paintList, images, {\n pixelsPerTile,\n padTiles,\n tileFrame,\n outputTileFrame,\n background: bg.solid,\n showCheckerboard: bg.showCheckerboard,\n showSpace: bg.showSpace,\n terrainBackground,\n spaceBackground,\n showCoordinates: opts.showCoordinates,\n frames: db.frames,\n iconImages,\n silhouetteImages,\n createCanvas,\n stats: paintStats,\n });\n const paintMs = wantProfile ? nowMs() - t : 0;\n if (wantProfile) {\n perfMark(\"fpsr-paint-end\");\n perfMeasure(\"fpsr-paint\", \"fpsr-paint-start\", \"fpsr-paint-end\");\n perfMark(\"fpsr-render-end\");\n perfMeasure(\"fpsr-render\", \"fpsr-render-start\", \"fpsr-render-end\");\n }\n\n const profile: RenderProfile | undefined = wantProfile\n ? {\n selectMs,\n plan: planProfile ?? emptyPlanProfile(),\n assets: assetEvents,\n assetsMs,\n iconBakeMs,\n iconBakeCount: iconImages.size,\n silhouetteBakeCount: silhouetteImages.size,\n iconCacheHits,\n iconCacheMisses,\n silhouetteCacheHits,\n silhouetteCacheMisses,\n frameMs,\n paintMs,\n shadow: {\n runs: paintStats.shadowRuns,\n tiles: paintStats.shadowTiles,\n compositedPixels: paintStats.shadowCompositedPixels,\n peakScratchPixels: paintStats.shadowPeakScratchPixels,\n },\n totalMs: nowMs() - tTotal,\n cold: assetEvents.some((e) => !e.cached),\n drawList: summarizeDrawList(drawList, atlasIndices),\n output: {\n width,\n height,\n megapixels: (width * height) / 1_000_000,\n pixelsPerTile,\n requestedPixelsPerTile,\n capped: output.capped,\n assetTier,\n tileFrame,\n },\n db: {\n entityDefs: Object.keys(db.entities).length,\n tileDefs: Object.keys(db.tiles).length,\n frameCount: db.frames.length,\n atlasCount: db.atlases.length,\n },\n }\n : undefined;\n\n reportProgress(opts, { stage: \"complete\" });\n\n return {\n canvas,\n width,\n height,\n drawList,\n tileFrame,\n profile,\n toImageBlob(options?: RenderImageOptions): Promise<Blob> {\n return encodeCanvasBlob(canvas, options);\n },\n toImageBuffer(options?: RenderImageOptions): Promise<Uint8Array> {\n return encodeCanvasBuffer(canvas, options);\n },\n async toPngBlob(): Promise<Blob> {\n return encodeCanvasBlob(canvas, { type: \"image/png\" });\n },\n async toPngBuffer(): Promise<Uint8Array> {\n return encodeCanvasBuffer(canvas, { type: \"image/png\" });\n },\n };\n }\n\n const renderer: Renderer = {\n measure(docOrBlueprint, opts = {}): RenderMeasurement {\n throwIfAborted(opts.signal);\n const { drawList } = planDocumentDrawList(docOrBlueprint, db, {\n blueprintPath: opts.blueprintPath,\n altMode: opts.altMode,\n beltEndings: opts.beltEndings,\n });\n throwIfAborted(opts.signal);\n const tileFrame = computeTileFrame(drawList.bounds, opts.padTiles ?? 0);\n return measureTileFrame(tileFrame, opts.pixelsPerTile ?? 64, opts.maxOutputSize);\n },\n\n render(docOrBlueprint, opts = {}): Promise<RenderResult> {\n return paint(docOrBlueprint, opts);\n },\n\n async renderTiledPng(docOrBlueprint, opts = {}): Promise<TiledPngResult> {\n throwIfAborted(opts.signal);\n const pixelsPerTile = opts.pixelsPerTile ?? 64;\n if (!Number.isInteger(pixelsPerTile) || pixelsPerTile <= 0) {\n throw new Error(\"Tiled PNG export requires an integer pixelsPerTile greater than zero\");\n }\n reportProgress(opts, { stage: \"planning\" });\n const { drawList: preparedDrawList } = planDocumentDrawList(docOrBlueprint, db, {\n blueprintPath: opts.blueprintPath,\n altMode: opts.altMode,\n beltEndings: opts.beltEndings,\n });\n const padTiles = opts.padTiles ?? 0;\n const tileFrame = computeTileFrame(preparedDrawList.bounds, padTiles);\n const { width, height } = measureTileFrame(tileFrame, pixelsPerTile);\n const tileSize = Math.max(pixelsPerTile, Math.floor(opts.tileSize ?? 2048));\n const maxPaintTileEdge = Math.max(1, Math.floor(tileSize / pixelsPerTile));\n const viewportBleedTiles = Math.min(2, Math.max(0, Math.floor((maxPaintTileEdge - 1) / 2)));\n const chunkTileWidth = Math.max(1, maxPaintTileEdge - viewportBleedTiles * 2);\n const maxStripeBytes = Math.max(\n width * pixelsPerTile * 4,\n opts.maxStripeBytes ?? 32 * 1024 * 1024,\n );\n const maxStripePixelRows = Math.max(\n pixelsPerTile,\n Math.floor(maxStripeBytes / Math.max(1, width * 4) / pixelsPerTile) * pixelsPerTile,\n );\n const stripTileHeight = Math.max(\n 1,\n Math.min(chunkTileWidth, Math.floor(maxStripePixelRows / pixelsPerTile)),\n );\n const tileColumns = Math.ceil((tileFrame.maxX - tileFrame.minX) / chunkTileWidth);\n const tileRows = Math.ceil((tileFrame.maxY - tileFrame.minY) / stripTileHeight);\n const totalTiles = tileColumns * tileRows;\n const encoder = createStreamingPngEncoder(width, height);\n let completedTiles = 0;\n\n const { tileSize: _tileSize, maxStripeBytes: _maxStripeBytes, ...renderOpts } = opts;\n\n for (let minY = tileFrame.minY; minY < tileFrame.maxY; minY += stripTileHeight) {\n throwIfAborted(opts.signal);\n const maxY = Math.min(tileFrame.maxY, minY + stripTileHeight);\n const stripHeight = (maxY - minY) * pixelsPerTile;\n const strip = new Uint8Array(width * stripHeight * 4);\n\n for (let minX = tileFrame.minX; minX < tileFrame.maxX; minX += chunkTileWidth) {\n throwIfAborted(opts.signal);\n const maxX = Math.min(tileFrame.maxX, minX + chunkTileWidth);\n const paintRegion: TileFrame = {\n minX: Math.max(tileFrame.minX, minX - viewportBleedTiles),\n minY: Math.max(tileFrame.minY, minY - viewportBleedTiles),\n maxX: Math.min(tileFrame.maxX, maxX + viewportBleedTiles),\n maxY: Math.min(tileFrame.maxY, maxY + viewportBleedTiles),\n };\n const regionDrawList = drawListForTile(preparedDrawList, db.frames, paintRegion);\n const rendered = await paint(docOrBlueprint, {\n ...renderOpts,\n pixelsPerTile,\n profile: false,\n tileFrame: paintRegion,\n outputTileFrame: tileFrame,\n preparedDrawList: regionDrawList,\n onProgress: undefined,\n });\n const context = rendered.canvas.getContext(\"2d\") as\n | (Canvas2DContextLike & {\n getImageData(x: number, y: number, width: number, height: number): ImageDataLike;\n })\n | null;\n if (!context || typeof context.getImageData !== \"function\") {\n throw new Error(\"Tiled PNG export requires Canvas2D getImageData support\");\n }\n const sourceX = (minX - paintRegion.minX) * pixelsPerTile;\n const sourceY = (minY - paintRegion.minY) * pixelsPerTile;\n const regionWidth = (maxX - minX) * pixelsPerTile;\n const regionHeight = (maxY - minY) * pixelsPerTile;\n const pixels = context.getImageData(sourceX, sourceY, regionWidth, regionHeight).data;\n const destinationX = (minX - tileFrame.minX) * pixelsPerTile;\n for (let row = 0; row < regionHeight; row++) {\n const sourceStart = row * regionWidth * 4;\n const destinationStart = (row * width + destinationX) * 4;\n strip.set(\n pixels.subarray(sourceStart, sourceStart + regionWidth * 4),\n destinationStart,\n );\n }\n completedTiles++;\n reportProgress(opts, {\n stage: \"painting-tiles\",\n completed: completedTiles,\n total: totalTiles,\n });\n }\n encoder.writeRgbaRows(strip, stripHeight);\n }\n\n reportProgress(opts, { stage: \"encoding\" });\n const blob = encoder.finish();\n reportProgress(opts, { stage: \"complete\" });\n return { blob, width, height, tileFrame, tiled: true };\n },\n\n dispose(): void {\n // Only renderer-owned derived surfaces. Atlas ImageSources and the\n // caller-supplied AssetSource remain owned by the asset host.\n iconImageCache.clear();\n silhouetteImageCache.clear();\n atlasCache.clear();\n },\n };\n\n preparedViewportByRenderer.set(renderer, (docOrBlueprint, opts) => paint(docOrBlueprint, opts));\n\n return renderer;\n}\n"],"mappings":"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"}
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { n as AssetSource } from "./assets-Dy4fqZBV.js";
|
|
2
|
+
|
|
3
|
+
//#region src/node.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Filesystem AssetSource for a pipeline output directory
|
|
6
|
+
* (manifest.json + content-addressed render-db/atlas files).
|
|
7
|
+
*
|
|
8
|
+
* AbortSignal rejects only the waiting caller; shared in-flight loads continue
|
|
9
|
+
* for other consumers. Rejected loads are cleared from the cache so a later
|
|
10
|
+
* call can retry (including abort-driven waiter rejection of a still-pending
|
|
11
|
+
* shared promise, which does not clear the shared slot).
|
|
12
|
+
*/
|
|
13
|
+
declare function localAssets(dir: string): AssetSource;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { localAssets };
|
|
16
|
+
//# sourceMappingURL=node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","names":[],"sources":["../src/node.ts"],"mappings":";;;;AAsCqD;;;;;;;;iBAArC,WAAA,CAAY,GAAA,WAAc,WAAW"}
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { n as throwIfAborted, t as raceWithAbort } from "./abort-CrgARaZd.js";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region src/node.ts
|
|
5
|
+
/**
|
|
6
|
+
* Node-only asset helpers. Import from "@rickyzhangca/fpsr/node", not from the main entry.
|
|
7
|
+
*
|
|
8
|
+
* Requires the optional peer dependency `skia-canvas` for Image decoding.
|
|
9
|
+
*/
|
|
10
|
+
async function loadSkiaLoadImage() {
|
|
11
|
+
try {
|
|
12
|
+
return (await import("skia-canvas")).loadImage;
|
|
13
|
+
} catch {
|
|
14
|
+
throw new Error("fpsr/node localAssets() requires the peer dependency \"skia-canvas\". Install it with: pnpm add skia-canvas");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Filesystem AssetSource for a pipeline output directory
|
|
19
|
+
* (manifest.json + content-addressed render-db/atlas files).
|
|
20
|
+
*
|
|
21
|
+
* AbortSignal rejects only the waiting caller; shared in-flight loads continue
|
|
22
|
+
* for other consumers. Rejected loads are cleared from the cache so a later
|
|
23
|
+
* call can retry (including abort-driven waiter rejection of a still-pending
|
|
24
|
+
* shared promise, which does not clear the shared slot).
|
|
25
|
+
*/
|
|
26
|
+
function localAssets(dir) {
|
|
27
|
+
const root = path.resolve(dir);
|
|
28
|
+
const dbPromises = /* @__PURE__ */ new Map();
|
|
29
|
+
let manifestPromise;
|
|
30
|
+
const atlasCache = /* @__PURE__ */ new Map();
|
|
31
|
+
let fontsPromise;
|
|
32
|
+
const readJson = async (file) => {
|
|
33
|
+
const text = await readFile(path.join(root, file), "utf8");
|
|
34
|
+
return JSON.parse(text);
|
|
35
|
+
};
|
|
36
|
+
const loadManifest = () => {
|
|
37
|
+
if (!manifestPromise) manifestPromise = readJson("manifest.json").catch((error) => {
|
|
38
|
+
manifestPromise = void 0;
|
|
39
|
+
throw error;
|
|
40
|
+
});
|
|
41
|
+
return manifestPromise;
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
loadRenderDb(tier = "2x", options) {
|
|
45
|
+
throwIfAborted(options?.signal);
|
|
46
|
+
let pending = dbPromises.get(tier);
|
|
47
|
+
if (!pending) {
|
|
48
|
+
pending = loadManifest().then(async (manifest) => {
|
|
49
|
+
if (manifest.schema !== 2) throw new Error(`Unsupported asset manifest schema: ${String(manifest.schema)}`);
|
|
50
|
+
const db = await readJson(manifest.tiers[tier].renderDb.file);
|
|
51
|
+
if (db.schema !== 2) throw new Error(`Unsupported render-db schema: ${String(db.schema)}`);
|
|
52
|
+
return db;
|
|
53
|
+
}).catch((error) => {
|
|
54
|
+
dbPromises.delete(tier);
|
|
55
|
+
throw error;
|
|
56
|
+
});
|
|
57
|
+
dbPromises.set(tier, pending);
|
|
58
|
+
}
|
|
59
|
+
return raceWithAbort(pending, options?.signal);
|
|
60
|
+
},
|
|
61
|
+
loadAtlasImage(index, tier = "2x", options) {
|
|
62
|
+
throwIfAborted(options?.signal);
|
|
63
|
+
const cacheKey = `${tier}:${index}`;
|
|
64
|
+
let pending = atlasCache.get(cacheKey);
|
|
65
|
+
if (!pending) {
|
|
66
|
+
pending = (async () => {
|
|
67
|
+
const entry = (await loadManifest()).tiers[tier].atlases[index];
|
|
68
|
+
if (!entry) throw new Error(`Atlas index ${index} missing from manifest at ${root}`);
|
|
69
|
+
return (await loadSkiaLoadImage())(path.join(root, entry.file));
|
|
70
|
+
})().catch((error) => {
|
|
71
|
+
atlasCache.delete(cacheKey);
|
|
72
|
+
throw error;
|
|
73
|
+
});
|
|
74
|
+
atlasCache.set(cacheKey, pending);
|
|
75
|
+
}
|
|
76
|
+
return raceWithAbort(pending, options?.signal);
|
|
77
|
+
},
|
|
78
|
+
ensureFonts(options) {
|
|
79
|
+
throwIfAborted(options?.signal);
|
|
80
|
+
if (!fontsPromise) fontsPromise = (async () => {
|
|
81
|
+
try {
|
|
82
|
+
const fonts = (await loadManifest()).fonts ?? [];
|
|
83
|
+
if (fonts.length === 0) return;
|
|
84
|
+
const skia = await import("skia-canvas");
|
|
85
|
+
for (const font of fonts) skia.FontLibrary.use(font.family, path.join(root, font.file));
|
|
86
|
+
} catch {}
|
|
87
|
+
})();
|
|
88
|
+
return raceWithAbort(fontsPromise, options?.signal);
|
|
89
|
+
},
|
|
90
|
+
dispose() {
|
|
91
|
+
atlasCache.clear();
|
|
92
|
+
dbPromises.clear();
|
|
93
|
+
manifestPromise = void 0;
|
|
94
|
+
fontsPromise = void 0;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
export { localAssets };
|
|
100
|
+
|
|
101
|
+
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","names":[],"sources":["../src/node.ts"],"sourcesContent":["/**\n * Node-only asset helpers. Import from \"@rickyzhangca/fpsr/node\", not from the main entry.\n *\n * Requires the optional peer dependency `skia-canvas` for Image decoding.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { raceWithAbort, throwIfAborted } from \"./abort.js\";\nimport type { AssetManifest, AssetSource, AssetTier } from \"./assets.js\";\nimport type { AssetLoadOptions, ImageSource } from \"./host.js\";\nimport type { RenderDb } from \"./types/render-db.js\";\n\ntype SkiaLoadImage = (src: string | Uint8Array) => Promise<ImageSource>;\n\nasync function loadSkiaLoadImage(): Promise<SkiaLoadImage> {\n try {\n const mod = (await import(\"skia-canvas\")) as unknown as {\n loadImage: SkiaLoadImage;\n };\n return mod.loadImage;\n } catch {\n throw new Error(\n 'fpsr/node localAssets() requires the peer dependency \"skia-canvas\". ' +\n \"Install it with: pnpm add skia-canvas\",\n );\n }\n}\n\n/**\n * Filesystem AssetSource for a pipeline output directory\n * (manifest.json + content-addressed render-db/atlas files).\n *\n * AbortSignal rejects only the waiting caller; shared in-flight loads continue\n * for other consumers. Rejected loads are cleared from the cache so a later\n * call can retry (including abort-driven waiter rejection of a still-pending\n * shared promise, which does not clear the shared slot).\n */\nexport function localAssets(dir: string): AssetSource {\n const root = path.resolve(dir);\n const dbPromises = new Map<AssetTier, Promise<RenderDb>>();\n let manifestPromise: Promise<AssetManifest> | undefined;\n const atlasCache = new Map<string, Promise<ImageSource>>();\n let fontsPromise: Promise<void> | undefined;\n\n const readJson = async <T>(file: string): Promise<T> => {\n const text = await readFile(path.join(root, file), \"utf8\");\n return JSON.parse(text) as T;\n };\n\n const loadManifest = (): Promise<AssetManifest> => {\n if (!manifestPromise) {\n manifestPromise = readJson<AssetManifest>(\"manifest.json\").catch((error) => {\n manifestPromise = undefined;\n throw error;\n });\n }\n return manifestPromise;\n };\n\n return {\n loadRenderDb(tier: AssetTier = \"2x\", options?: AssetLoadOptions): Promise<RenderDb> {\n throwIfAborted(options?.signal);\n let pending = dbPromises.get(tier);\n if (!pending) {\n pending = loadManifest()\n .then(async (manifest) => {\n if (manifest.schema !== 2) {\n throw new Error(`Unsupported asset manifest schema: ${String(manifest.schema)}`);\n }\n const db = await readJson<RenderDb>(manifest.tiers[tier].renderDb.file);\n if (db.schema !== 2) {\n throw new Error(`Unsupported render-db schema: ${String(db.schema)}`);\n }\n return db;\n })\n .catch((error) => {\n dbPromises.delete(tier);\n throw error;\n });\n dbPromises.set(tier, pending);\n }\n return raceWithAbort(pending, options?.signal);\n },\n\n loadAtlasImage(\n index: number,\n tier: AssetTier = \"2x\",\n options?: AssetLoadOptions,\n ): Promise<ImageSource> {\n throwIfAborted(options?.signal);\n const cacheKey = `${tier}:${index}`;\n let pending = atlasCache.get(cacheKey);\n if (!pending) {\n pending = (async () => {\n const manifest = await loadManifest();\n const entry = manifest.tiers[tier].atlases[index];\n if (!entry) {\n throw new Error(`Atlas index ${index} missing from manifest at ${root}`);\n }\n const loadImage = await loadSkiaLoadImage();\n return loadImage(path.join(root, entry.file));\n })().catch((error) => {\n atlasCache.delete(cacheKey);\n throw error;\n });\n atlasCache.set(cacheKey, pending);\n }\n return raceWithAbort(pending, options?.signal);\n },\n\n ensureFonts(options?: AssetLoadOptions): Promise<void> {\n throwIfAborted(options?.signal);\n if (!fontsPromise) {\n fontsPromise = (async () => {\n try {\n const manifest = await loadManifest();\n const fonts = manifest.fonts ?? [];\n if (fonts.length === 0) return;\n const skia = (await import(\"skia-canvas\")) as unknown as {\n FontLibrary: { use: (alias: string, paths: string | string[]) => unknown };\n };\n for (const font of fonts) {\n skia.FontLibrary.use(font.family, path.join(root, font.file));\n }\n } catch {\n // Font registration is best-effort; canvas falls back to system fonts.\n }\n })();\n }\n return raceWithAbort(fontsPromise, options?.signal);\n },\n\n dispose(): void {\n atlasCache.clear();\n dbPromises.clear();\n manifestPromise = undefined;\n fontsPromise = undefined;\n },\n };\n}\n"],"mappings":";;;;;;;;;AAeA,eAAe,oBAA4C;CACzD,IAAI;EAIF,QAAO,MAHY,OAAO,eAAA,CAGf;CACb,QAAQ;EACN,MAAM,IAAI,MACR,6GAEF;CACF;AACF;;;;;;;;;;AAWA,SAAgB,YAAY,KAA0B;CACpD,MAAM,OAAO,KAAK,QAAQ,GAAG;CAC7B,MAAM,6BAAa,IAAI,IAAkC;CACzD,IAAI;CACJ,MAAM,6BAAa,IAAI,IAAkC;CACzD,IAAI;CAEJ,MAAM,WAAW,OAAU,SAA6B;EACtD,MAAM,OAAO,MAAM,SAAS,KAAK,KAAK,MAAM,IAAI,GAAG,MAAM;EACzD,OAAO,KAAK,MAAM,IAAI;CACxB;CAEA,MAAM,qBAA6C;EACjD,IAAI,CAAC,iBACH,kBAAkB,SAAwB,eAAe,CAAC,CAAC,OAAO,UAAU;GAC1E,kBAAkB,KAAA;GAClB,MAAM;EACR,CAAC;EAEH,OAAO;CACT;CAEA,OAAO;EACL,aAAa,OAAkB,MAAM,SAA+C;GAClF,eAAe,SAAS,MAAM;GAC9B,IAAI,UAAU,WAAW,IAAI,IAAI;GACjC,IAAI,CAAC,SAAS;IACZ,UAAU,aAAa,CAAC,CACrB,KAAK,OAAO,aAAa;KACxB,IAAI,SAAS,WAAW,GACtB,MAAM,IAAI,MAAM,sCAAsC,OAAO,SAAS,MAAM,GAAG;KAEjF,MAAM,KAAK,MAAM,SAAmB,SAAS,MAAM,KAAK,CAAC,SAAS,IAAI;KACtE,IAAI,GAAG,WAAW,GAChB,MAAM,IAAI,MAAM,iCAAiC,OAAO,GAAG,MAAM,GAAG;KAEtE,OAAO;IACT,CAAC,CAAC,CACD,OAAO,UAAU;KAChB,WAAW,OAAO,IAAI;KACtB,MAAM;IACR,CAAC;IACH,WAAW,IAAI,MAAM,OAAO;GAC9B;GACA,OAAO,cAAc,SAAS,SAAS,MAAM;EAC/C;EAEA,eACE,OACA,OAAkB,MAClB,SACsB;GACtB,eAAe,SAAS,MAAM;GAC9B,MAAM,WAAW,GAAG,KAAK,GAAG;GAC5B,IAAI,UAAU,WAAW,IAAI,QAAQ;GACrC,IAAI,CAAC,SAAS;IACZ,WAAW,YAAY;KAErB,MAAM,SAAQ,MADS,aAAa,EAAA,CACb,MAAM,KAAK,CAAC,QAAQ;KAC3C,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,eAAe,MAAM,4BAA4B,MAAM;KAGzE,QAAO,MADiB,kBAAkB,EAAA,CACzB,KAAK,KAAK,MAAM,MAAM,IAAI,CAAC;IAC9C,EAAA,CAAG,CAAC,CAAC,OAAO,UAAU;KACpB,WAAW,OAAO,QAAQ;KAC1B,MAAM;IACR,CAAC;IACD,WAAW,IAAI,UAAU,OAAO;GAClC;GACA,OAAO,cAAc,SAAS,SAAS,MAAM;EAC/C;EAEA,YAAY,SAA2C;GACrD,eAAe,SAAS,MAAM;GAC9B,IAAI,CAAC,cACH,gBAAgB,YAAY;IAC1B,IAAI;KAEF,MAAM,SAAQ,MADS,aAAa,EAAA,CACb,SAAS,CAAC;KACjC,IAAI,MAAM,WAAW,GAAG;KACxB,MAAM,OAAQ,MAAM,OAAO;KAG3B,KAAK,MAAM,QAAQ,OACjB,KAAK,YAAY,IAAI,KAAK,QAAQ,KAAK,KAAK,MAAM,KAAK,IAAI,CAAC;IAEhE,QAAQ,CAER;GACF,EAAA,CAAG;GAEL,OAAO,cAAc,cAAc,SAAS,MAAM;EACpD;EAEA,UAAgB;GACd,WAAW,MAAM;GACjB,WAAW,MAAM;GACjB,kBAAkB,KAAA;GAClB,eAAe,KAAA;EACjB;CACF;AACF"}
|