chess2img 0.3.0 → 0.3.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/dist/index.cjs CHANGED
@@ -249,6 +249,9 @@ function validateHighlightEntry(entry) {
249
249
  if (entry.lineWidth !== void 0 && (!Number.isFinite(entry.lineWidth) || entry.lineWidth <= 0)) {
250
250
  throw new ValidationError("highlight.lineWidth must be a finite number greater than 0");
251
251
  }
252
+ if (entry.radius !== void 0 && (!Number.isFinite(entry.radius) || entry.radius <= 0 || entry.radius > 0.5)) {
253
+ throw new ValidationError("highlight.radius must be a finite number greater than 0 and at most 0.5");
254
+ }
252
255
  }
253
256
  function validateHighlightOptions(highlights) {
254
257
  if (highlights === void 0) {
@@ -569,6 +572,10 @@ function resolveCircleLineWidth(squareSize, lineWidth) {
569
572
  const candidate = lineWidth ?? squareSize * 0.08;
570
573
  return Math.max(2, Math.min(8, candidate));
571
574
  }
575
+ function resolveCircleRadius(squareSize, radius, lineWidth) {
576
+ const radiusPx = squareSize * (radius ?? 0.42);
577
+ return Math.max(0, radiusPx - lineWidth / 2);
578
+ }
572
579
  function resolveBorderCoordinateFontSize(context, geometry) {
573
580
  const maxFontSize = Math.floor(
574
581
  Math.min(geometry.squareSize * 0.6, geometry.borderSize * 0.65)
@@ -718,11 +725,16 @@ function drawCircleHighlights(context, request, geometry) {
718
725
  squareGeometry.size,
719
726
  highlight.lineWidth
720
727
  );
728
+ const radius = resolveCircleRadius(
729
+ squareGeometry.size,
730
+ highlight.radius,
731
+ context.lineWidth
732
+ );
721
733
  context.beginPath();
722
734
  context.arc(
723
735
  centerX,
724
736
  centerY,
725
- squareGeometry.size * 0.32,
737
+ radius,
726
738
  0,
727
739
  Math.PI * 2
728
740
  );
@@ -796,7 +808,11 @@ function normalizeHighlightEntries(input) {
796
808
  if (typeof entry === "string") {
797
809
  return {
798
810
  square: validateSquare(entry),
799
- style: "fill"
811
+ style: "fill",
812
+ color: void 0,
813
+ opacity: void 0,
814
+ lineWidth: void 0,
815
+ radius: void 0
800
816
  };
801
817
  }
802
818
  const style = entry.style ?? "fill";
@@ -805,7 +821,8 @@ function normalizeHighlightEntries(input) {
805
821
  style,
806
822
  color: entry.color ?? (style === "circle" ? "#ffcc00" : void 0),
807
823
  opacity: entry.opacity ?? (style === "circle" ? 0.9 : void 0),
808
- lineWidth: entry.lineWidth
824
+ lineWidth: entry.lineWidth,
825
+ radius: style === "circle" ? entry.radius ?? 0.42 : void 0
809
826
  };
810
827
  });
811
828
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/types/errors.ts","../src/core/parsers.ts","../src/core/board.ts","../src/core/validators.ts","../src/render/canvas-renderer.ts","../src/core/geometry.ts","../src/render/asset-cache.ts","../src/render/rasterizer.ts","../src/utils/io.ts","../src/core/highlights.ts","../src/themes/builtins.ts","../src/themes/validation.ts","../src/themes/registry.ts","../src/themes/resolver.ts","../src/utils/normalization.ts","../src/api/class-api.ts","../src/api/functional-api.ts"],"sourcesContent":["export * from \"./types/errors\";\nexport * from \"./types/types\";\nexport * from \"./api/class-api\";\nexport * from \"./api/functional-api\";\nexport * from \"./api/theme-api\";\n","export class ValidationError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ValidationError\";\n }\n}\n\nexport class ParseError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ParseError\";\n }\n}\n\nexport class ThemeError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ThemeError\";\n }\n}\n\nexport class RenderError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"RenderError\";\n }\n}\n\nexport class IOError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"IOError\";\n }\n}\n","import { Chess } from \"chess.js\";\nimport type { BoardArray, BoardCell, PieceKey } from \"../types/types\";\nimport { ParseError, ValidationError } from \"../types/errors\";\nimport { createEmptyBoardPosition, FILES } from \"./board\";\nimport { validateBoardArray } from \"./validators\";\n\nconst PIECE_SYMBOL_TO_KEY: Record<string, PieceKey> = {\n K: \"wK\",\n Q: \"wQ\",\n R: \"wR\",\n B: \"wB\",\n N: \"wN\",\n P: \"wP\",\n k: \"bK\",\n q: \"bQ\",\n r: \"bR\",\n b: \"bB\",\n n: \"bN\",\n p: \"bP\",\n};\n\nfunction chessBoardToBoardArray(board: ReturnType<Chess[\"board\"]>): BoardArray {\n return board.map((rank) =>\n rank.map((piece): BoardCell => {\n if (!piece) {\n return null;\n }\n\n return piece.color === \"w\" ? piece.type.toUpperCase() : piece.type;\n }),\n );\n}\n\nexport function parseFEN(fen: string) {\n const chess = new Chess();\n\n try {\n chess.load(fen);\n } catch (error) {\n throw new ParseError(\"Invalid FEN\", { cause: error });\n }\n\n return parseBoardArray(chessBoardToBoardArray(chess.board()));\n}\n\nexport function parsePGN(pgn: string) {\n const chess = new Chess();\n\n try {\n chess.loadPgn(pgn);\n } catch (error) {\n throw new ParseError(\"Invalid PGN\", { cause: error });\n }\n\n return parseBoardArray(chessBoardToBoardArray(chess.board()));\n}\n\nexport function parseBoardArray(board: BoardArray) {\n const validatedBoard = validateBoardArray(board);\n const position = createEmptyBoardPosition();\n\n validatedBoard.forEach((rank, rankIndex) => {\n rank.forEach((cell, fileIndex) => {\n if (cell === null) {\n return;\n }\n\n const pieceKey = PIECE_SYMBOL_TO_KEY[cell];\n\n if (!pieceKey) {\n throw new ValidationError(`Invalid board piece: ${cell}`);\n }\n\n const square = `${FILES[fileIndex]}${8 - rankIndex}` as keyof typeof position.squares;\n position.squares[square] = pieceKey;\n });\n });\n\n return position;\n}\n","import type { PieceKey, Square } from \"../types/types\";\n\nexport const FILES = [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"] as const;\nexport const RANKS = [\"8\", \"7\", \"6\", \"5\", \"4\", \"3\", \"2\", \"1\"] as const;\n\nexport const SQUARES: Square[] = RANKS.flatMap((rank) =>\n FILES.map((file) => `${file}${rank}`),\n);\n\nexport interface BoardPosition {\n squares: Record<Square, PieceKey | null>;\n}\n\nexport function createEmptyBoardPosition(): BoardPosition {\n return {\n squares: Object.fromEntries(\n SQUARES.map((square) => [square, null]),\n ) as Record<Square, PieceKey | null>,\n };\n}\n","import { createCanvas } from \"canvas\";\nimport type {\n BoardArray,\n BoardCell,\n BoardColors,\n CoordinatesInput,\n CoordinatesOptions,\n CoordinatesPosition,\n HighlightInput,\n HighlightOptions,\n HighlightStyle,\n Padding,\n Square,\n} from \"../types/types\";\nimport { ValidationError } from \"../types/errors\";\n\nconst SQUARE_PATTERN = /^[a-h][1-8]$/;\nconst PIECE_PATTERN = /^[prnbqkPRNBQK]$/;\nconst BUILT_IN_THEME_PATTERN = /^(merida|alpha|cburnett|cheq|leipzig)$/;\nconst colorValidationContext = createCanvas(1, 1).getContext(\"2d\");\n\nexport function validateSize(size: number): number {\n if (!Number.isFinite(size) || size <= 0) {\n throw new ValidationError(`Invalid board size: ${size}`);\n }\n\n return Math.round(size);\n}\n\nexport function normalizePadding(padding?: number[] | Padding): Padding {\n const candidate = padding ?? [0, 0, 0, 0];\n\n if (\n !Array.isArray(candidate) ||\n candidate.length !== 4 ||\n candidate.some((value) => !Number.isFinite(value) || value < 0)\n ) {\n throw new ValidationError(\"Padding must be a 4-item array of non-negative numbers\");\n }\n\n return [\n Math.round(candidate[0]),\n Math.round(candidate[1]),\n Math.round(candidate[2]),\n Math.round(candidate[3]),\n ];\n}\n\nexport function validateSquare(square: string): Square {\n const normalized = square.trim().toLowerCase();\n\n if (!SQUARE_PATTERN.test(normalized)) {\n throw new ValidationError(`Invalid square: ${square}`);\n }\n\n return normalized;\n}\n\nexport function validateBoardCell(cell: BoardCell): BoardCell {\n if (cell === null || cell === \"\" || cell === \" \") {\n return null;\n }\n\n if (typeof cell !== \"string\" || !PIECE_PATTERN.test(cell)) {\n throw new ValidationError(`Invalid board piece: ${String(cell)}`);\n }\n\n return cell;\n}\n\nexport function validateBoardArray(board: BoardArray): BoardArray {\n if (!Array.isArray(board) || board.length !== 8) {\n throw new ValidationError(\"Board array must have exactly 8 ranks\");\n }\n\n return board.map((rank, rankIndex) => {\n if (!Array.isArray(rank) || rank.length !== 8) {\n throw new ValidationError(`Board rank ${rankIndex} must contain exactly 8 files`);\n }\n\n return rank.map(validateBoardCell);\n });\n}\n\nexport function validateStyleName(style: string): string {\n const normalized = style.trim().toLowerCase();\n\n if (!BUILT_IN_THEME_PATTERN.test(normalized)) {\n throw new ValidationError(`Unknown built-in style: ${style}`);\n }\n\n return normalized;\n}\n\nexport function validateThemeName(name: string): string {\n const normalized = name.trim().toLowerCase();\n\n if (!/^[a-z0-9-]+$/.test(normalized)) {\n throw new ValidationError(`Invalid theme name: ${name}`);\n }\n\n return normalized;\n}\n\nexport function validateBorderSize(borderSize: number, size: number): number {\n const normalizedSize = validateSize(size);\n const normalizedBorderSize = Math.round(borderSize);\n const maxBorderSize = Math.floor(normalizedSize / 8);\n\n if (!Number.isFinite(borderSize) || normalizedBorderSize < 0) {\n throw new ValidationError(`Invalid borderSize: ${borderSize}`);\n }\n\n if (normalizedBorderSize > maxBorderSize) {\n throw new ValidationError(\n `Invalid borderSize: ${borderSize}. Maximum allowed for size ${normalizedSize} is ${maxBorderSize}`,\n );\n }\n\n return normalizedBorderSize;\n}\n\nexport function validateColorString(color: string, label = \"color\"): string {\n if (typeof color !== \"string\" || color.trim() === \"\") {\n throw new ValidationError(`Invalid ${label}: ${String(color)}`);\n }\n\n const normalized = color.trim();\n colorValidationContext.fillStyle = \"#010203\";\n colorValidationContext.fillStyle = normalized;\n const firstPass = String(colorValidationContext.fillStyle);\n colorValidationContext.fillStyle = \"#fefefe\";\n colorValidationContext.fillStyle = normalized;\n const secondPass = String(colorValidationContext.fillStyle);\n\n if (firstPass !== secondPass) {\n throw new ValidationError(`Invalid ${label}: ${color}`);\n }\n\n return normalized;\n}\n\nexport function validateBoardColors(colors?: BoardColors): void {\n if (!colors) {\n return;\n }\n\n if (colors.lightSquare !== undefined) {\n validateColorString(colors.lightSquare, \"lightSquare color\");\n }\n\n if (colors.darkSquare !== undefined) {\n validateColorString(colors.darkSquare, \"darkSquare color\");\n }\n\n if (colors.highlight !== undefined) {\n validateColorString(colors.highlight, \"highlight color\");\n }\n}\n\nfunction isCoordinatesOptions(value: unknown): value is CoordinatesOptions {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isCoordinatesPosition(value: unknown): value is CoordinatesPosition {\n return value === \"border\" || value === \"inside\";\n}\n\nfunction isHighlightOptions(value: unknown): value is HighlightOptions {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isHighlightStyle(value: unknown): value is HighlightStyle {\n return value === \"fill\" || value === \"circle\";\n}\n\nexport function validateCoordinatesOption(\n coordinates: CoordinatesInput | undefined,\n borderSize: number,\n): void {\n if (coordinates === undefined || typeof coordinates === \"boolean\") {\n return;\n }\n\n if (isCoordinatesPosition(coordinates)) {\n if (coordinates === \"border\" && borderSize === 0) {\n throw new ValidationError(\n \"coordinates position 'border' requires borderSize > 0\",\n );\n }\n\n return;\n }\n\n if (!isCoordinatesOptions(coordinates)) {\n throw new ValidationError(\n \"coordinates must be false, true, 'border', 'inside', or an options object\",\n );\n }\n\n if (\n coordinates.enabled !== undefined &&\n typeof coordinates.enabled !== \"boolean\"\n ) {\n throw new ValidationError(\"coordinates.enabled must be a boolean\");\n }\n\n if (\n coordinates.position !== undefined &&\n !isCoordinatesPosition(coordinates.position)\n ) {\n throw new ValidationError(\"coordinates.position must be 'border' or 'inside'\");\n }\n\n if (\n coordinates.enabled !== false &&\n coordinates.position === \"border\" &&\n borderSize === 0\n ) {\n throw new ValidationError(\n \"coordinates position 'border' requires borderSize > 0\",\n );\n }\n\n if (coordinates.color !== undefined) {\n validateColorString(coordinates.color, \"coordinates.color\");\n }\n}\n\nfunction validateHighlightEntry(entry: HighlightInput): void {\n if (typeof entry === \"string\") {\n validateSquare(entry);\n return;\n }\n\n if (!isHighlightOptions(entry)) {\n throw new ValidationError(\"highlights entries must be square strings or highlight objects\");\n }\n\n if (typeof entry.square !== \"string\") {\n throw new ValidationError(\"highlight.square must be a valid algebraic square\");\n }\n\n validateSquare(entry.square);\n\n if (entry.style !== undefined && !isHighlightStyle(entry.style)) {\n throw new ValidationError(\"highlight.style must be 'fill' or 'circle'\");\n }\n\n if (entry.color !== undefined) {\n validateColorString(entry.color, \"highlight.color\");\n }\n\n if (\n entry.opacity !== undefined &&\n (!Number.isFinite(entry.opacity) || entry.opacity < 0 || entry.opacity > 1)\n ) {\n throw new ValidationError(\"highlight.opacity must be a finite number between 0 and 1\");\n }\n\n if (\n entry.lineWidth !== undefined &&\n (!Number.isFinite(entry.lineWidth) || entry.lineWidth <= 0)\n ) {\n throw new ValidationError(\"highlight.lineWidth must be a finite number greater than 0\");\n }\n}\n\nexport function validateHighlightOptions(highlights: HighlightInput[] | undefined): void {\n if (highlights === undefined) {\n return;\n }\n\n if (!Array.isArray(highlights)) {\n throw new ValidationError(\"highlights must be an array\");\n }\n\n for (const entry of highlights) {\n validateHighlightEntry(entry);\n }\n}\n\nexport function validateHighlightsInput(\n highlights: HighlightInput[] | undefined,\n highlightSquares: HighlightInput[] | undefined,\n): void {\n if (highlights !== undefined && highlightSquares !== undefined) {\n throw new ValidationError(\"Use either highlights or highlightSquares, not both\");\n }\n\n validateHighlightOptions(highlights);\n validateHighlightOptions(highlightSquares);\n}\n","import { createCanvas } from \"canvas\";\nimport { SQUARES } from \"../core/board\";\nimport { createBoardGeometry } from \"../core/geometry\";\nimport { RenderError } from \"../types/errors\";\nimport type { ThemeAssetSource } from \"../types/types\";\nimport { createRasterCacheKey, RasterAssetCache } from \"./asset-cache\";\nimport type { RenderRequest, Renderer } from \"./renderer\";\nimport { rasterizeThemeAsset } from \"./rasterizer\";\n\nconst pieceRasterCache = new RasterAssetCache<Awaited<ReturnType<typeof rasterizeThemeAsset>>>();\nconst MIN_COORDINATE_FONT_SIZE = 8;\nconst MAX_FILE_LABEL_WIDTH_RATIO = 0.75;\nconst MAX_RANK_LABEL_WIDTH_RATIO = 0.7;\nconst MAX_LABEL_HEIGHT_RATIO = 0.7;\nconst INSIDE_COORDINATE_MAX_FONT_RATIO = 0.34;\nconst INSIDE_LIGHT_LABEL_COLOR = \"rgba(255,255,255,0.6)\";\nconst INSIDE_DARK_LABEL_COLOR = \"rgba(0,0,0,0.45)\";\n\nfunction isDarkSquare(square: string): boolean {\n const fileIndex = square.charCodeAt(0) - 97;\n const rankNumber = Number(square[1]);\n return (fileIndex + rankNumber) % 2 === 1;\n}\n\nasync function getPieceRaster(\n themeName: string,\n pieceKey: string,\n asset: ThemeAssetSource,\n squareSize: number,\n) {\n const cacheKey = createRasterCacheKey(themeName, pieceKey, squareSize, \"png-canvas\");\n const cached = pieceRasterCache.get(cacheKey);\n\n if (cached) {\n return cached;\n }\n\n const raster = await rasterizeThemeAsset(asset, squareSize);\n pieceRasterCache.set(cacheKey, raster);\n return raster;\n}\n\nfunction resolveInsideLabelColor(\n request: RenderRequest,\n square: string,\n) {\n if (request.coordinates.color) {\n return request.coordinates.color;\n }\n\n return isDarkSquare(square) ? INSIDE_LIGHT_LABEL_COLOR : INSIDE_DARK_LABEL_COLOR;\n}\n\nfunction resolveHighlightOpacity(style: \"fill\" | \"circle\", color: string | undefined, opacity: number | undefined) {\n if (opacity !== undefined) {\n return opacity;\n }\n\n if (style === \"circle\" || color !== undefined) {\n return 0.9;\n }\n\n // Preserve legacy fill behavior when the board highlight color is used directly.\n return 1;\n}\n\nfunction resolveCircleLineWidth(squareSize: number, lineWidth: number | undefined) {\n const candidate = lineWidth ?? squareSize * 0.08;\n return Math.max(2, Math.min(8, candidate));\n}\n\nfunction resolveBorderCoordinateFontSize(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n const maxFontSize = Math.floor(\n Math.min(geometry.squareSize * 0.6, geometry.borderSize * 0.65),\n );\n let fontSize: number | null = null;\n\n for (let candidate = maxFontSize; candidate >= MIN_COORDINATE_FONT_SIZE; candidate -= 1) {\n context.font = `${candidate}px sans-serif`;\n\n const filesFit = geometry.borderFileLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.squareSize * MAX_FILE_LABEL_WIDTH_RATIO &&\n textHeight <= geometry.borderSize * MAX_LABEL_HEIGHT_RATIO\n );\n });\n const ranksFit = geometry.borderRankLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.borderSize * MAX_RANK_LABEL_WIDTH_RATIO &&\n textHeight <= geometry.squareSize * MAX_LABEL_HEIGHT_RATIO\n );\n });\n\n if (filesFit && ranksFit) {\n fontSize = candidate;\n break;\n }\n }\n\n return fontSize;\n}\n\nfunction resolveInsideCoordinateFontSize(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n const maxFontSize = Math.floor(\n geometry.squareSize * INSIDE_COORDINATE_MAX_FONT_RATIO,\n );\n let fontSize: number | null = null;\n\n for (let candidate = maxFontSize; candidate >= MIN_COORDINATE_FONT_SIZE; candidate -= 1) {\n context.font = `${candidate}px sans-serif`;\n\n const filesFit = geometry.insideFileLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.insideLabelMaxWidth &&\n textHeight <= geometry.insideLabelMaxHeight\n );\n });\n const ranksFit = geometry.insideRankLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.insideLabelMaxWidth &&\n textHeight <= geometry.insideLabelMaxHeight\n );\n });\n\n if (filesFit && ranksFit) {\n fontSize = candidate;\n break;\n }\n }\n\n return fontSize;\n}\n\nfunction drawBorderCoordinates(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n if (geometry.borderSize === 0) {\n return;\n }\n\n const fontSize = resolveBorderCoordinateFontSize(context, geometry);\n\n if (fontSize === null) {\n return;\n }\n\n context.fillStyle = request.coordinates.color ?? \"#333\";\n context.font = `${fontSize}px sans-serif`;\n context.textAlign = \"center\";\n context.textBaseline = \"middle\";\n\n for (const label of geometry.borderFileLabels) {\n context.fillText(label.text, label.x, label.y);\n }\n\n for (const label of geometry.borderRankLabels) {\n context.fillText(label.text, label.x, label.y);\n }\n}\n\nfunction drawInsideCoordinates(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n const fontSize = resolveInsideCoordinateFontSize(context, geometry);\n\n if (fontSize === null) {\n return;\n }\n\n context.font = `${fontSize}px sans-serif`;\n\n for (const label of geometry.insideFileLabels) {\n context.fillStyle = resolveInsideLabelColor(request, label.square);\n context.textAlign = label.textAlign;\n context.textBaseline = label.textBaseline;\n context.fillText(label.text, label.x, label.y);\n }\n\n for (const label of geometry.insideRankLabels) {\n context.fillStyle = resolveInsideLabelColor(request, label.square);\n context.textAlign = label.textAlign;\n context.textBaseline = label.textBaseline;\n context.fillText(label.text, label.x, label.y);\n }\n}\n\nfunction drawCoordinates(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n if (!request.coordinates.enabled) {\n return;\n }\n\n if (request.coordinates.position === \"border\") {\n drawBorderCoordinates(context, request, geometry);\n return;\n }\n\n drawInsideCoordinates(context, request, geometry);\n}\n\nfunction drawBoardSquares(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const square of SQUARES) {\n const squareGeometry = geometry.squares[square];\n context.fillStyle = isDarkSquare(square)\n ? request.colors.darkSquare\n : request.colors.lightSquare;\n context.fillRect(\n squareGeometry.x,\n squareGeometry.y,\n squareGeometry.size,\n squareGeometry.size,\n );\n }\n}\n\nfunction drawFillHighlights(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const highlight of request.highlights) {\n if (highlight.style !== \"fill\") {\n continue;\n }\n\n const squareGeometry = geometry.squares[highlight.square];\n context.save();\n context.globalAlpha = resolveHighlightOpacity(\n highlight.style,\n highlight.color,\n highlight.opacity,\n );\n context.fillStyle = highlight.color ?? request.colors.highlight;\n context.fillRect(\n squareGeometry.x,\n squareGeometry.y,\n squareGeometry.size,\n squareGeometry.size,\n );\n context.restore();\n }\n}\n\nfunction drawCircleHighlights(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const highlight of request.highlights) {\n if (highlight.style !== \"circle\") {\n continue;\n }\n\n const squareGeometry = geometry.squares[highlight.square];\n const centerX = squareGeometry.x + squareGeometry.size / 2;\n const centerY = squareGeometry.y + squareGeometry.size / 2;\n context.save();\n context.globalAlpha = resolveHighlightOpacity(\n highlight.style,\n highlight.color,\n highlight.opacity,\n );\n context.strokeStyle = highlight.color ?? \"#ffcc00\";\n context.lineWidth = resolveCircleLineWidth(\n squareGeometry.size,\n highlight.lineWidth,\n );\n context.beginPath();\n context.arc(\n centerX,\n centerY,\n squareGeometry.size * 0.32,\n 0,\n Math.PI * 2,\n );\n context.stroke();\n context.restore();\n }\n}\n\nasync function drawPieces(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const square of SQUARES) {\n const squareGeometry = geometry.squares[square];\n const pieceKey = request.board.squares[square];\n if (!pieceKey) {\n continue;\n }\n\n const raster = await getPieceRaster(\n request.theme.name,\n pieceKey,\n request.theme.pieces[pieceKey],\n Math.round(geometry.squareSize),\n );\n context.drawImage(\n raster,\n squareGeometry.x,\n squareGeometry.y,\n squareGeometry.size,\n squareGeometry.size,\n );\n }\n}\n\nexport class CanvasPngRenderer implements Renderer<Buffer> {\n async render(request: RenderRequest): Promise<Buffer> {\n try {\n const geometry = createBoardGeometry({\n size: request.size,\n padding: request.padding,\n borderSize: request.borderSize,\n flipped: request.flipped,\n });\n\n const canvas = createCanvas(geometry.imageWidth, geometry.imageHeight);\n const context = canvas.getContext(\"2d\");\n\n context.fillStyle = request.colors.lightSquare;\n context.fillRect(0, 0, geometry.imageWidth, geometry.imageHeight);\n drawBoardSquares(context, request, geometry);\n drawFillHighlights(context, request, geometry);\n drawCircleHighlights(context, request, geometry);\n drawCoordinates(context, request, geometry);\n await drawPieces(context, request, geometry);\n\n return canvas.toBuffer(\"image/png\");\n } catch (error) {\n if (error instanceof RenderError) {\n throw error;\n }\n\n throw new RenderError(\"Failed to render chess board\", { cause: error });\n }\n }\n}\n","import type { Padding, Square } from \"../types/types\";\nimport { FILES, RANKS, SQUARES } from \"./board\";\n\nexport interface SquareGeometry {\n x: number;\n y: number;\n size: number;\n}\n\nexport interface CoordinateLabelGeometry {\n text: string;\n x: number;\n y: number;\n square: Square;\n textAlign: \"left\" | \"center\" | \"right\";\n textBaseline: \"top\" | \"middle\" | \"bottom\";\n}\n\nexport interface BoardGeometry {\n imageWidth: number;\n imageHeight: number;\n squareSize: number;\n borderSize: number;\n boardOuterX: number;\n boardOuterY: number;\n boardOuterSize: number;\n boardX: number;\n boardY: number;\n boardSize: number;\n borderFileLabels: CoordinateLabelGeometry[];\n borderRankLabels: CoordinateLabelGeometry[];\n insideFileLabels: CoordinateLabelGeometry[];\n insideRankLabels: CoordinateLabelGeometry[];\n insideFileInsetX: number;\n insideFileInsetY: number;\n insideRankInsetX: number;\n insideRankInsetY: number;\n insideLabelMaxWidth: number;\n insideLabelMaxHeight: number;\n squares: Record<Square, SquareGeometry>;\n}\n\ninterface BoardGeometryOptions {\n size: number;\n padding: Padding;\n borderSize: number;\n flipped: boolean;\n}\n\nexport function createBoardGeometry({\n size,\n padding,\n borderSize,\n flipped,\n}: BoardGeometryOptions): BoardGeometry {\n const [top, right, bottom, left] = padding;\n const boardOuterSize = size;\n const boardOuterX = left;\n const boardOuterY = top;\n const boardX = left + borderSize;\n const boardY = top + borderSize;\n const boardSize = size - borderSize * 2;\n const squareSize = boardSize / 8;\n\n const squares = Object.fromEntries(\n SQUARES.map((square, index) => {\n const fileIndex = index % 8;\n const rankIndex = Math.floor(index / 8);\n\n const x = boardX + (flipped ? 7 - fileIndex : fileIndex) * squareSize;\n const y = boardY + (flipped ? 7 - rankIndex : rankIndex) * squareSize;\n\n return [square, { x, y, size: squareSize }];\n }),\n ) as Record<Square, SquareGeometry>;\n\n const displayedFiles = flipped ? [...FILES].reverse() : [...FILES];\n const displayedRanks = flipped ? [...RANKS].reverse() : [...RANKS];\n const bottomEdgeRank = flipped ? \"8\" : \"1\";\n const leftEdgeFile = flipped ? \"h\" : \"a\";\n const borderFileLabels = borderSize\n ? displayedFiles.map((file, fileIndex) => ({\n text: file,\n x: boardX + fileIndex * squareSize + squareSize / 2,\n y: boardOuterY + boardOuterSize - borderSize / 2,\n square: `${file}${bottomEdgeRank}` as Square,\n textAlign: \"center\" as const,\n textBaseline: \"middle\" as const,\n }))\n : [];\n const borderRankLabels = borderSize\n ? displayedRanks.map((rank, rankIndex) => ({\n text: rank,\n x: boardOuterX + borderSize / 2,\n y: boardY + rankIndex * squareSize + squareSize / 2,\n square: `${leftEdgeFile}${rank}` as Square,\n textAlign: \"center\" as const,\n textBaseline: \"middle\" as const,\n }))\n : [];\n const insideFileInsetX = squareSize * 0.14;\n const insideFileInsetY = squareSize * 0.1;\n const insideRankInsetX = squareSize * 0.14;\n const insideRankInsetY = squareSize * 0.1;\n const insideLabelMaxWidth = squareSize * 0.26;\n const insideLabelMaxHeight = squareSize * 0.24;\n const insideFileLabels = displayedFiles.map((file) => {\n const square = `${file}${bottomEdgeRank}` as Square;\n const squareGeometry = squares[square];\n\n return {\n text: file,\n x: squareGeometry.x + squareGeometry.size - insideFileInsetX,\n y: squareGeometry.y + squareGeometry.size - insideFileInsetY,\n square,\n textAlign: \"right\" as const,\n textBaseline: \"bottom\" as const,\n };\n });\n const insideRankLabels = displayedRanks.map((rank) => {\n const square = `${leftEdgeFile}${rank}` as Square;\n const squareGeometry = squares[square];\n\n return {\n text: rank,\n x: squareGeometry.x + insideRankInsetX,\n y: squareGeometry.y + insideRankInsetY,\n square,\n textAlign: \"left\" as const,\n textBaseline: \"top\" as const,\n };\n });\n\n return {\n imageWidth: left + size + right,\n imageHeight: top + size + bottom,\n squareSize,\n borderSize,\n boardOuterX,\n boardOuterY,\n boardOuterSize,\n boardX,\n boardY,\n boardSize,\n borderFileLabels,\n borderRankLabels,\n insideFileLabels,\n insideRankLabels,\n insideFileInsetX,\n insideFileInsetY,\n insideRankInsetX,\n insideRankInsetY,\n insideLabelMaxWidth,\n insideLabelMaxHeight,\n squares,\n };\n}\n","export function createRasterCacheKey(\n themeName: string,\n pieceKey: string,\n squareSize: number,\n backend: string,\n): string {\n return `${themeName}:${pieceKey}:${squareSize}:${backend}`;\n}\n\nexport class RasterAssetCache<T> {\n private readonly cache = new Map<string, T>();\n\n get(key: string): T | undefined {\n return this.cache.get(key);\n }\n\n set(key: string, value: T): void {\n this.cache.set(key, value);\n }\n}\n\nexport class SourceAssetCache<T> {\n private readonly cache = new Map<string, T>();\n\n get(key: string): T | undefined {\n return this.cache.get(key);\n }\n\n set(key: string, value: T): void {\n this.cache.set(key, value);\n }\n}\n","import { readFile } from \"node:fs/promises\";\nimport { createCanvas, loadImage } from \"canvas\";\nimport { Resvg } from \"@resvg/resvg-js\";\nimport { RenderError } from \"../types/errors\";\nimport type { ThemeAssetSource } from \"../types/types\";\nimport { SourceAssetCache } from \"./asset-cache\";\n\nconst svgSourceCache = new SourceAssetCache<string>();\nconst imageBufferCache = new SourceAssetCache<Buffer>();\n\nasync function readSvgSource(filePath: string): Promise<string> {\n const cached = svgSourceCache.get(filePath);\n\n if (cached) {\n return cached;\n }\n\n try {\n const source = await readFile(filePath, \"utf8\");\n svgSourceCache.set(filePath, source);\n return source;\n } catch (error) {\n throw new RenderError(`Failed to read SVG asset: ${filePath}`, { cause: error });\n }\n}\n\nasync function readBinaryAsset(filePath: string): Promise<Buffer> {\n const cached = imageBufferCache.get(filePath);\n\n if (cached) {\n return cached;\n }\n\n try {\n const source = await readFile(filePath);\n imageBufferCache.set(filePath, source);\n return source;\n } catch (error) {\n throw new RenderError(`Failed to read image asset: ${filePath}`, { cause: error });\n }\n}\n\nasync function rasterizeSvgAsset(filePath: string, squareSize: number) {\n try {\n const svgSource = await readSvgSource(filePath);\n const resvg = new Resvg(svgSource, {\n fitTo: {\n mode: \"width\",\n value: squareSize,\n },\n });\n const pngBuffer = resvg.render().asPng();\n const image = await loadImage(pngBuffer);\n const canvas = createCanvas(squareSize, squareSize);\n const context = canvas.getContext(\"2d\");\n context.drawImage(image, 0, 0, squareSize, squareSize);\n return canvas;\n } catch (error) {\n throw new RenderError(`Failed to rasterize SVG asset: ${filePath}`, { cause: error });\n }\n}\n\nasync function rasterizePngAsset(filePath: string, squareSize: number) {\n try {\n const pngSource = await readBinaryAsset(filePath);\n const image = await loadImage(pngSource);\n const canvas = createCanvas(squareSize, squareSize);\n const context = canvas.getContext(\"2d\");\n context.drawImage(image, 0, 0, squareSize, squareSize);\n return canvas;\n } catch (error) {\n throw new RenderError(`Failed to rasterize PNG asset: ${filePath}`, { cause: error });\n }\n}\n\nexport async function rasterizeThemeAsset(asset: ThemeAssetSource, squareSize: number) {\n if (asset.kind === \"svg\") {\n return rasterizeSvgAsset(asset.source, squareSize);\n }\n\n return rasterizePngAsset(asset.source, squareSize);\n}\n","import { writeFile } from \"node:fs/promises\";\nimport { IOError } from \"../types/errors\";\n\nexport async function writeBufferToFile(filePath: string, buffer: Buffer): Promise<void> {\n try {\n await writeFile(filePath, buffer);\n } catch (error) {\n throw new IOError(`Failed to write file: ${filePath}`, { cause: error });\n }\n}\n","import type { HighlightInput, ResolvedHighlight } from \"../types/types\";\nimport { validateSquare } from \"./validators\";\n\nexport function normalizeHighlightEntries(input: HighlightInput[]): ResolvedHighlight[] {\n return input.map((entry) => {\n if (typeof entry === \"string\") {\n return {\n square: validateSquare(entry),\n style: \"fill\",\n };\n }\n\n const style = entry.style ?? \"fill\";\n\n return {\n square: validateSquare(entry.square),\n style,\n color: entry.color ?? (style === \"circle\" ? \"#ffcc00\" : undefined),\n opacity: entry.opacity ?? (style === \"circle\" ? 0.9 : undefined),\n lineWidth: entry.lineWidth,\n };\n });\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { PieceKey, PieceStyle, ThemeDefinition } from \"../types/types\";\nimport { registerTheme } from \"./registry\";\n\nexport const builtInThemeNames = [\n \"merida\",\n \"alpha\",\n \"cburnett\",\n \"cheq\",\n \"leipzig\",\n] as const satisfies readonly PieceStyle[];\n\nconst PIECE_KEYS: PieceKey[] = [\n \"wK\",\n \"wQ\",\n \"wR\",\n \"wB\",\n \"wN\",\n \"wP\",\n \"bK\",\n \"bQ\",\n \"bR\",\n \"bB\",\n \"bN\",\n \"bP\",\n];\n\nlet initialized = false;\n\nfunction assetPath(themeName: PieceStyle, pieceKey: PieceKey): string {\n const candidates = [\n resolve(__dirname, \"../../assets/themes\", themeName, `${pieceKey}.png`),\n resolve(__dirname, \"../assets/themes\", themeName, `${pieceKey}.png`),\n resolve(process.cwd(), \"assets/themes\", themeName, `${pieceKey}.png`),\n ];\n\n const match = candidates.find((candidate) => existsSync(candidate));\n return match ?? candidates[0];\n}\n\nfunction createBuiltInTheme(themeName: PieceStyle): ThemeDefinition {\n return {\n name: themeName,\n displayName: themeName[0].toUpperCase() + themeName.slice(1),\n license: \"Derived from upstream chess-image-generator resource pack; original pack license not fully verified\",\n attribution:\n \"Derived from andyruwruw/chess-image-generator bundled resources; upstream README cites Marcel van Kervinck as source\",\n pieces: Object.fromEntries(\n PIECE_KEYS.map((pieceKey) => [\n pieceKey,\n {\n kind: \"png\",\n source: assetPath(themeName, pieceKey),\n },\n ]),\n ) as ThemeDefinition[\"pieces\"],\n };\n}\n\nexport function initializeBuiltInThemes(): void {\n if (initialized) {\n return;\n }\n\n for (const themeName of builtInThemeNames) {\n registerTheme(createBuiltInTheme(themeName));\n }\n\n initialized = true;\n}\n\nexport function resetBuiltInThemesForTesting(): void {\n initialized = false;\n}\n","import { validateThemeName } from \"../core/validators\";\nimport { ThemeError, ValidationError } from \"../types/errors\";\nimport type { PieceKey, ThemeDefinition } from \"../types/types\";\n\nconst REQUIRED_PIECES: PieceKey[] = [\n \"wK\",\n \"wQ\",\n \"wR\",\n \"wB\",\n \"wN\",\n \"wP\",\n \"bK\",\n \"bQ\",\n \"bR\",\n \"bB\",\n \"bN\",\n \"bP\",\n];\n\nexport function validateThemeDefinition(theme: ThemeDefinition): ThemeDefinition {\n const normalizedName = validateThemeName(theme.name);\n\n if (!theme.displayName.trim()) {\n throw new ValidationError(\"Theme displayName is required\");\n }\n\n if (!theme.license.trim()) {\n throw new ValidationError(\"Theme license is required\");\n }\n\n if (!theme.attribution.trim()) {\n throw new ValidationError(\"Theme attribution is required\");\n }\n\n for (const pieceKey of REQUIRED_PIECES) {\n const asset = theme.pieces[pieceKey];\n\n if (!asset || (asset.kind !== \"svg\" && asset.kind !== \"png\") || !asset.source.trim()) {\n throw new ThemeError(`Theme \"${normalizedName}\" is missing asset ${pieceKey}`);\n }\n }\n\n return {\n ...theme,\n name: normalizedName,\n };\n}\n","import type { ThemeDefinition } from \"../types/types\";\nimport { ThemeError } from \"../types/errors\";\nimport { validateThemeDefinition } from \"./validation\";\n\nconst registry = new Map<string, ThemeDefinition>();\n\nexport function registerTheme(theme: ThemeDefinition): ThemeDefinition {\n const validatedTheme = validateThemeDefinition(theme);\n\n if (registry.has(validatedTheme.name)) {\n throw new ThemeError(`Theme \"${validatedTheme.name}\" is already registered`);\n }\n\n registry.set(validatedTheme.name, validatedTheme);\n return validatedTheme;\n}\n\nexport function getTheme(name: string): ThemeDefinition | undefined {\n return registry.get(name.trim().toLowerCase());\n}\n\nexport function clearThemeRegistryForTesting(): void {\n registry.clear();\n}\n","import type { PieceStyle, ThemeDefinition } from \"../types/types\";\nimport { ThemeError, ValidationError } from \"../types/errors\";\nimport { initializeBuiltInThemes } from \"./builtins\";\nimport { getTheme } from \"./registry\";\nimport { validateThemeDefinition } from \"./validation\";\n\ninterface ResolveThemeOptions {\n theme?: string | ThemeDefinition;\n style?: PieceStyle;\n}\n\nexport function resolveTheme({ theme, style }: ResolveThemeOptions): ThemeDefinition {\n initializeBuiltInThemes();\n\n if (typeof theme === \"object\" && theme !== null) {\n try {\n return validateThemeDefinition(theme);\n } catch (error) {\n if (error instanceof ThemeError) {\n throw error;\n }\n\n if (error instanceof ValidationError) {\n throw new ThemeError(error.message, { cause: error });\n }\n\n throw error;\n }\n }\n\n const requestedName = typeof theme === \"string\" ? theme : style ?? \"merida\";\n const resolvedTheme = getTheme(requestedName);\n\n if (!resolvedTheme) {\n throw new ThemeError(`Unknown theme: ${requestedName}`);\n }\n\n return resolvedTheme;\n}\n","import type {\n BoardColors,\n CoordinatesInput,\n CoordinatesPosition,\n HighlightInput,\n Padding,\n RenderOptions,\n ResolvedColors,\n ResolvedCoordinates,\n ResolvedHighlight,\n ResolvedRenderOptions,\n ThemeDefinition,\n} from \"../types/types\";\nimport { normalizeHighlightEntries as normalizeCanonicalHighlightEntries } from \"../core/highlights\";\nimport {\n normalizePadding,\n validateBoardColors,\n validateBorderSize,\n validateCoordinatesOption,\n validateHighlightsInput,\n validateSize,\n} from \"../core/validators\";\nimport { resolveTheme } from \"../themes/resolver\";\n\nexport const DEFAULT_SIZE = 480;\nexport const DEFAULT_PADDING: Padding = [0, 0, 0, 0];\nexport const DEFAULT_BORDER_SIZE = 0;\nexport const DEFAULT_COLORS: ResolvedColors = {\n lightSquare: \"#f0d9b5\",\n darkSquare: \"#b58863\",\n highlight: \"rgba(255, 206, 0, 0.45)\",\n};\nexport const DEFAULT_COORDINATES: ResolvedCoordinates = {\n enabled: false,\n position: \"inside\",\n};\n\nexport function normalizeColors(colors?: BoardColors): ResolvedColors {\n return {\n lightSquare: colors?.lightSquare ?? DEFAULT_COLORS.lightSquare,\n darkSquare: colors?.darkSquare ?? DEFAULT_COLORS.darkSquare,\n highlight: colors?.highlight ?? DEFAULT_COLORS.highlight,\n };\n}\n\nexport function normalizeCoordinates(\n coordinates: CoordinatesInput | undefined,\n borderSize: number,\n): ResolvedCoordinates {\n if (coordinates === undefined || coordinates === false) {\n return { ...DEFAULT_COORDINATES };\n }\n\n const defaultPosition: CoordinatesPosition =\n borderSize > 0 ? \"border\" : \"inside\";\n\n if (coordinates === true) {\n return {\n enabled: true,\n position: defaultPosition,\n color: defaultPosition === \"border\" ? \"#333\" : undefined,\n };\n }\n\n if (coordinates === \"border\" || coordinates === \"inside\") {\n return {\n enabled: true,\n position: coordinates,\n color: coordinates === \"border\" ? \"#333\" : undefined,\n };\n }\n\n const position = coordinates.position ?? defaultPosition;\n\n return {\n enabled: coordinates.enabled ?? true,\n position,\n color: coordinates.color ?? (position === \"border\" ? \"#333\" : undefined),\n };\n}\n\nexport function normalizeHighlightEntries(\n highlights: HighlightInput[] | undefined,\n): ResolvedHighlight[] {\n return normalizeCanonicalHighlightEntries(highlights ?? []);\n}\n\nexport function normalizeRenderInputs(\n options: RenderOptions,\n): ResolvedRenderOptions {\n const size = validateSize(options.size ?? DEFAULT_SIZE);\n const borderSize = validateBorderSize(\n options.borderSize ?? DEFAULT_BORDER_SIZE,\n size,\n );\n validateBoardColors(options.colors);\n validateCoordinatesOption(options.coordinates, borderSize);\n validateHighlightsInput(options.highlights, options.highlightSquares);\n const highlightInput = options.highlights ?? options.highlightSquares;\n\n return {\n size,\n padding: normalizePadding(options.padding ?? DEFAULT_PADDING),\n borderSize,\n flipped: options.flipped ?? false,\n theme: resolveTheme({\n theme: options.theme,\n style: options.style,\n }),\n highlights: normalizeHighlightEntries(highlightInput),\n colors: normalizeColors(options.colors),\n coordinates: normalizeCoordinates(options.coordinates, borderSize),\n };\n}\n","import { parseBoardArray, parseFEN, parsePGN } from \"../core/parsers\";\nimport { validateHighlightsInput } from \"../core/validators\";\nimport { ValidationError } from \"../types/errors\";\nimport type {\n BoardArray,\n ChessImageGeneratorOptions,\n HighlightInput,\n RenderOptions,\n} from \"../types/types\";\nimport { CanvasPngRenderer } from \"../render/canvas-renderer\";\nimport { writeBufferToFile } from \"../utils/io\";\nimport { normalizeRenderInputs } from \"../utils/normalization\";\nimport type { BoardPosition } from \"../core/board\";\n\nexport class ChessImageGenerator {\n private position: BoardPosition | null = null;\n\n private readonly defaults: RenderOptions;\n\n private highlights: HighlightInput[] = [];\n\n constructor(options: ChessImageGeneratorOptions = {}) {\n this.defaults = { ...options };\n normalizeRenderInputs(this.defaults);\n }\n\n async loadFEN(fen: string): Promise<void> {\n this.position = parseFEN(fen);\n this.clearHighlights();\n }\n\n async loadPGN(pgn: string): Promise<void> {\n this.position = parsePGN(pgn);\n this.clearHighlights();\n }\n\n async loadBoard(board: BoardArray): Promise<void> {\n this.position = parseBoardArray(board);\n this.clearHighlights();\n }\n\n setHighlights(highlights: HighlightInput[]): void {\n validateHighlightsInput(highlights, undefined);\n this.highlights = [...highlights];\n }\n\n clearHighlights(): void {\n this.highlights = [];\n }\n\n async toBuffer(): Promise<Buffer> {\n if (!this.position) {\n throw new ValidationError(\"No board position loaded\");\n }\n\n const renderer = new CanvasPngRenderer();\n const normalized = normalizeRenderInputs({\n ...this.defaults,\n highlights: this.highlights,\n highlightSquares: undefined,\n });\n\n return renderer.render({\n board: this.position,\n theme: normalized.theme,\n highlights: normalized.highlights,\n size: normalized.size,\n padding: normalized.padding,\n borderSize: normalized.borderSize,\n flipped: normalized.flipped,\n colors: normalized.colors,\n coordinates: normalized.coordinates,\n });\n }\n\n async toFile(filePath: string): Promise<void> {\n const buffer = await this.toBuffer();\n await writeBufferToFile(filePath, buffer);\n }\n}\n","import { parseBoardArray, parseFEN, parsePGN } from \"../core/parsers\";\nimport { ValidationError } from \"../types/errors\";\nimport type { RenderChessOptions } from \"../types/types\";\nimport { CanvasPngRenderer } from \"../render/canvas-renderer\";\nimport { normalizeRenderInputs } from \"../utils/normalization\";\n\nfunction parseInputPosition(options: RenderChessOptions) {\n if (typeof options.fen === \"string\") {\n return parseFEN(options.fen);\n }\n\n if (typeof options.pgn === \"string\") {\n return parsePGN(options.pgn);\n }\n\n if (Array.isArray(options.board)) {\n return parseBoardArray(options.board);\n }\n\n throw new ValidationError(\"Exactly one of fen, pgn, or board must be provided\");\n}\n\nexport async function renderChess(options: RenderChessOptions): Promise<Buffer> {\n const provided = [\n typeof options.fen === \"string\" ? options.fen : undefined,\n typeof options.pgn === \"string\" ? options.pgn : undefined,\n Array.isArray(options.board) ? options.board : undefined,\n ].filter((value) => value !== undefined);\n\n if (provided.length !== 1) {\n throw new ValidationError(\"Exactly one of fen, pgn, or board must be provided\");\n }\n\n const position = parseInputPosition(options);\n const renderer = new CanvasPngRenderer();\n const normalized = normalizeRenderInputs(options);\n\n return renderer.render({\n board: position,\n theme: normalized.theme,\n highlights: normalized.highlights,\n size: normalized.size,\n padding: normalized.padding,\n borderSize: normalized.borderSize,\n flipped: normalized.flipped,\n colors: normalized.colors,\n coordinates: normalized.coordinates,\n });\n}\n"],"mappings":"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","names":["import_canvas","import_canvas","import_promises","normalizeHighlightEntries"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/types/errors.ts","../src/core/parsers.ts","../src/core/board.ts","../src/core/validators.ts","../src/render/canvas-renderer.ts","../src/core/geometry.ts","../src/render/asset-cache.ts","../src/render/rasterizer.ts","../src/utils/io.ts","../src/core/highlights.ts","../src/themes/builtins.ts","../src/themes/validation.ts","../src/themes/registry.ts","../src/themes/resolver.ts","../src/utils/normalization.ts","../src/api/class-api.ts","../src/api/functional-api.ts"],"sourcesContent":["export * from \"./types/errors\";\nexport * from \"./types/types\";\nexport * from \"./api/class-api\";\nexport * from \"./api/functional-api\";\nexport * from \"./api/theme-api\";\n","export class ValidationError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ValidationError\";\n }\n}\n\nexport class ParseError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ParseError\";\n }\n}\n\nexport class ThemeError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ThemeError\";\n }\n}\n\nexport class RenderError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"RenderError\";\n }\n}\n\nexport class IOError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"IOError\";\n }\n}\n","import { Chess } from \"chess.js\";\nimport type { BoardArray, BoardCell, PieceKey } from \"../types/types\";\nimport { ParseError, ValidationError } from \"../types/errors\";\nimport { createEmptyBoardPosition, FILES } from \"./board\";\nimport { validateBoardArray } from \"./validators\";\n\nconst PIECE_SYMBOL_TO_KEY: Record<string, PieceKey> = {\n K: \"wK\",\n Q: \"wQ\",\n R: \"wR\",\n B: \"wB\",\n N: \"wN\",\n P: \"wP\",\n k: \"bK\",\n q: \"bQ\",\n r: \"bR\",\n b: \"bB\",\n n: \"bN\",\n p: \"bP\",\n};\n\nfunction chessBoardToBoardArray(board: ReturnType<Chess[\"board\"]>): BoardArray {\n return board.map((rank) =>\n rank.map((piece): BoardCell => {\n if (!piece) {\n return null;\n }\n\n return piece.color === \"w\" ? piece.type.toUpperCase() : piece.type;\n }),\n );\n}\n\nexport function parseFEN(fen: string) {\n const chess = new Chess();\n\n try {\n chess.load(fen);\n } catch (error) {\n throw new ParseError(\"Invalid FEN\", { cause: error });\n }\n\n return parseBoardArray(chessBoardToBoardArray(chess.board()));\n}\n\nexport function parsePGN(pgn: string) {\n const chess = new Chess();\n\n try {\n chess.loadPgn(pgn);\n } catch (error) {\n throw new ParseError(\"Invalid PGN\", { cause: error });\n }\n\n return parseBoardArray(chessBoardToBoardArray(chess.board()));\n}\n\nexport function parseBoardArray(board: BoardArray) {\n const validatedBoard = validateBoardArray(board);\n const position = createEmptyBoardPosition();\n\n validatedBoard.forEach((rank, rankIndex) => {\n rank.forEach((cell, fileIndex) => {\n if (cell === null) {\n return;\n }\n\n const pieceKey = PIECE_SYMBOL_TO_KEY[cell];\n\n if (!pieceKey) {\n throw new ValidationError(`Invalid board piece: ${cell}`);\n }\n\n const square = `${FILES[fileIndex]}${8 - rankIndex}` as keyof typeof position.squares;\n position.squares[square] = pieceKey;\n });\n });\n\n return position;\n}\n","import type { PieceKey, Square } from \"../types/types\";\n\nexport const FILES = [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"] as const;\nexport const RANKS = [\"8\", \"7\", \"6\", \"5\", \"4\", \"3\", \"2\", \"1\"] as const;\n\nexport const SQUARES: Square[] = RANKS.flatMap((rank) =>\n FILES.map((file) => `${file}${rank}`),\n);\n\nexport interface BoardPosition {\n squares: Record<Square, PieceKey | null>;\n}\n\nexport function createEmptyBoardPosition(): BoardPosition {\n return {\n squares: Object.fromEntries(\n SQUARES.map((square) => [square, null]),\n ) as Record<Square, PieceKey | null>,\n };\n}\n","import { createCanvas } from \"canvas\";\nimport type {\n BoardArray,\n BoardCell,\n BoardColors,\n CoordinatesInput,\n CoordinatesOptions,\n CoordinatesPosition,\n HighlightInput,\n HighlightOptions,\n HighlightStyle,\n Padding,\n Square,\n} from \"../types/types\";\nimport { ValidationError } from \"../types/errors\";\n\nconst SQUARE_PATTERN = /^[a-h][1-8]$/;\nconst PIECE_PATTERN = /^[prnbqkPRNBQK]$/;\nconst BUILT_IN_THEME_PATTERN = /^(merida|alpha|cburnett|cheq|leipzig)$/;\nconst colorValidationContext = createCanvas(1, 1).getContext(\"2d\");\n\nexport function validateSize(size: number): number {\n if (!Number.isFinite(size) || size <= 0) {\n throw new ValidationError(`Invalid board size: ${size}`);\n }\n\n return Math.round(size);\n}\n\nexport function normalizePadding(padding?: number[] | Padding): Padding {\n const candidate = padding ?? [0, 0, 0, 0];\n\n if (\n !Array.isArray(candidate) ||\n candidate.length !== 4 ||\n candidate.some((value) => !Number.isFinite(value) || value < 0)\n ) {\n throw new ValidationError(\"Padding must be a 4-item array of non-negative numbers\");\n }\n\n return [\n Math.round(candidate[0]),\n Math.round(candidate[1]),\n Math.round(candidate[2]),\n Math.round(candidate[3]),\n ];\n}\n\nexport function validateSquare(square: string): Square {\n const normalized = square.trim().toLowerCase();\n\n if (!SQUARE_PATTERN.test(normalized)) {\n throw new ValidationError(`Invalid square: ${square}`);\n }\n\n return normalized;\n}\n\nexport function validateBoardCell(cell: BoardCell): BoardCell {\n if (cell === null || cell === \"\" || cell === \" \") {\n return null;\n }\n\n if (typeof cell !== \"string\" || !PIECE_PATTERN.test(cell)) {\n throw new ValidationError(`Invalid board piece: ${String(cell)}`);\n }\n\n return cell;\n}\n\nexport function validateBoardArray(board: BoardArray): BoardArray {\n if (!Array.isArray(board) || board.length !== 8) {\n throw new ValidationError(\"Board array must have exactly 8 ranks\");\n }\n\n return board.map((rank, rankIndex) => {\n if (!Array.isArray(rank) || rank.length !== 8) {\n throw new ValidationError(`Board rank ${rankIndex} must contain exactly 8 files`);\n }\n\n return rank.map(validateBoardCell);\n });\n}\n\nexport function validateStyleName(style: string): string {\n const normalized = style.trim().toLowerCase();\n\n if (!BUILT_IN_THEME_PATTERN.test(normalized)) {\n throw new ValidationError(`Unknown built-in style: ${style}`);\n }\n\n return normalized;\n}\n\nexport function validateThemeName(name: string): string {\n const normalized = name.trim().toLowerCase();\n\n if (!/^[a-z0-9-]+$/.test(normalized)) {\n throw new ValidationError(`Invalid theme name: ${name}`);\n }\n\n return normalized;\n}\n\nexport function validateBorderSize(borderSize: number, size: number): number {\n const normalizedSize = validateSize(size);\n const normalizedBorderSize = Math.round(borderSize);\n const maxBorderSize = Math.floor(normalizedSize / 8);\n\n if (!Number.isFinite(borderSize) || normalizedBorderSize < 0) {\n throw new ValidationError(`Invalid borderSize: ${borderSize}`);\n }\n\n if (normalizedBorderSize > maxBorderSize) {\n throw new ValidationError(\n `Invalid borderSize: ${borderSize}. Maximum allowed for size ${normalizedSize} is ${maxBorderSize}`,\n );\n }\n\n return normalizedBorderSize;\n}\n\nexport function validateColorString(color: string, label = \"color\"): string {\n if (typeof color !== \"string\" || color.trim() === \"\") {\n throw new ValidationError(`Invalid ${label}: ${String(color)}`);\n }\n\n const normalized = color.trim();\n colorValidationContext.fillStyle = \"#010203\";\n colorValidationContext.fillStyle = normalized;\n const firstPass = String(colorValidationContext.fillStyle);\n colorValidationContext.fillStyle = \"#fefefe\";\n colorValidationContext.fillStyle = normalized;\n const secondPass = String(colorValidationContext.fillStyle);\n\n if (firstPass !== secondPass) {\n throw new ValidationError(`Invalid ${label}: ${color}`);\n }\n\n return normalized;\n}\n\nexport function validateBoardColors(colors?: BoardColors): void {\n if (!colors) {\n return;\n }\n\n if (colors.lightSquare !== undefined) {\n validateColorString(colors.lightSquare, \"lightSquare color\");\n }\n\n if (colors.darkSquare !== undefined) {\n validateColorString(colors.darkSquare, \"darkSquare color\");\n }\n\n if (colors.highlight !== undefined) {\n validateColorString(colors.highlight, \"highlight color\");\n }\n}\n\nfunction isCoordinatesOptions(value: unknown): value is CoordinatesOptions {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isCoordinatesPosition(value: unknown): value is CoordinatesPosition {\n return value === \"border\" || value === \"inside\";\n}\n\nfunction isHighlightOptions(value: unknown): value is HighlightOptions {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isHighlightStyle(value: unknown): value is HighlightStyle {\n return value === \"fill\" || value === \"circle\";\n}\n\nexport function validateCoordinatesOption(\n coordinates: CoordinatesInput | undefined,\n borderSize: number,\n): void {\n if (coordinates === undefined || typeof coordinates === \"boolean\") {\n return;\n }\n\n if (isCoordinatesPosition(coordinates)) {\n if (coordinates === \"border\" && borderSize === 0) {\n throw new ValidationError(\n \"coordinates position 'border' requires borderSize > 0\",\n );\n }\n\n return;\n }\n\n if (!isCoordinatesOptions(coordinates)) {\n throw new ValidationError(\n \"coordinates must be false, true, 'border', 'inside', or an options object\",\n );\n }\n\n if (\n coordinates.enabled !== undefined &&\n typeof coordinates.enabled !== \"boolean\"\n ) {\n throw new ValidationError(\"coordinates.enabled must be a boolean\");\n }\n\n if (\n coordinates.position !== undefined &&\n !isCoordinatesPosition(coordinates.position)\n ) {\n throw new ValidationError(\"coordinates.position must be 'border' or 'inside'\");\n }\n\n if (\n coordinates.enabled !== false &&\n coordinates.position === \"border\" &&\n borderSize === 0\n ) {\n throw new ValidationError(\n \"coordinates position 'border' requires borderSize > 0\",\n );\n }\n\n if (coordinates.color !== undefined) {\n validateColorString(coordinates.color, \"coordinates.color\");\n }\n}\n\nfunction validateHighlightEntry(entry: HighlightInput): void {\n if (typeof entry === \"string\") {\n validateSquare(entry);\n return;\n }\n\n if (!isHighlightOptions(entry)) {\n throw new ValidationError(\"highlights entries must be square strings or highlight objects\");\n }\n\n if (typeof entry.square !== \"string\") {\n throw new ValidationError(\"highlight.square must be a valid algebraic square\");\n }\n\n validateSquare(entry.square);\n\n if (entry.style !== undefined && !isHighlightStyle(entry.style)) {\n throw new ValidationError(\"highlight.style must be 'fill' or 'circle'\");\n }\n\n if (entry.color !== undefined) {\n validateColorString(entry.color, \"highlight.color\");\n }\n\n if (\n entry.opacity !== undefined &&\n (!Number.isFinite(entry.opacity) || entry.opacity < 0 || entry.opacity > 1)\n ) {\n throw new ValidationError(\"highlight.opacity must be a finite number between 0 and 1\");\n }\n\n if (\n entry.lineWidth !== undefined &&\n (!Number.isFinite(entry.lineWidth) || entry.lineWidth <= 0)\n ) {\n throw new ValidationError(\"highlight.lineWidth must be a finite number greater than 0\");\n }\n\n if (\n entry.radius !== undefined &&\n (!Number.isFinite(entry.radius) || entry.radius <= 0 || entry.radius > 0.5)\n ) {\n throw new ValidationError(\"highlight.radius must be a finite number greater than 0 and at most 0.5\");\n }\n}\n\nexport function validateHighlightOptions(highlights: HighlightInput[] | undefined): void {\n if (highlights === undefined) {\n return;\n }\n\n if (!Array.isArray(highlights)) {\n throw new ValidationError(\"highlights must be an array\");\n }\n\n for (const entry of highlights) {\n validateHighlightEntry(entry);\n }\n}\n\nexport function validateHighlightsInput(\n highlights: HighlightInput[] | undefined,\n highlightSquares: HighlightInput[] | undefined,\n): void {\n if (highlights !== undefined && highlightSquares !== undefined) {\n throw new ValidationError(\"Use either highlights or highlightSquares, not both\");\n }\n\n validateHighlightOptions(highlights);\n validateHighlightOptions(highlightSquares);\n}\n","import { createCanvas } from \"canvas\";\nimport { SQUARES } from \"../core/board\";\nimport { createBoardGeometry } from \"../core/geometry\";\nimport { RenderError } from \"../types/errors\";\nimport type { ThemeAssetSource } from \"../types/types\";\nimport { createRasterCacheKey, RasterAssetCache } from \"./asset-cache\";\nimport type { RenderRequest, Renderer } from \"./renderer\";\nimport { rasterizeThemeAsset } from \"./rasterizer\";\n\nconst pieceRasterCache = new RasterAssetCache<Awaited<ReturnType<typeof rasterizeThemeAsset>>>();\nconst MIN_COORDINATE_FONT_SIZE = 8;\nconst MAX_FILE_LABEL_WIDTH_RATIO = 0.75;\nconst MAX_RANK_LABEL_WIDTH_RATIO = 0.7;\nconst MAX_LABEL_HEIGHT_RATIO = 0.7;\nconst INSIDE_COORDINATE_MAX_FONT_RATIO = 0.34;\nconst INSIDE_LIGHT_LABEL_COLOR = \"rgba(255,255,255,0.6)\";\nconst INSIDE_DARK_LABEL_COLOR = \"rgba(0,0,0,0.45)\";\n\nfunction isDarkSquare(square: string): boolean {\n const fileIndex = square.charCodeAt(0) - 97;\n const rankNumber = Number(square[1]);\n return (fileIndex + rankNumber) % 2 === 1;\n}\n\nasync function getPieceRaster(\n themeName: string,\n pieceKey: string,\n asset: ThemeAssetSource,\n squareSize: number,\n) {\n const cacheKey = createRasterCacheKey(themeName, pieceKey, squareSize, \"png-canvas\");\n const cached = pieceRasterCache.get(cacheKey);\n\n if (cached) {\n return cached;\n }\n\n const raster = await rasterizeThemeAsset(asset, squareSize);\n pieceRasterCache.set(cacheKey, raster);\n return raster;\n}\n\nfunction resolveInsideLabelColor(\n request: RenderRequest,\n square: string,\n) {\n if (request.coordinates.color) {\n return request.coordinates.color;\n }\n\n return isDarkSquare(square) ? INSIDE_LIGHT_LABEL_COLOR : INSIDE_DARK_LABEL_COLOR;\n}\n\nfunction resolveHighlightOpacity(style: \"fill\" | \"circle\", color: string | undefined, opacity: number | undefined) {\n if (opacity !== undefined) {\n return opacity;\n }\n\n if (style === \"circle\" || color !== undefined) {\n return 0.9;\n }\n\n // Preserve legacy fill behavior when the board highlight color is used directly.\n return 1;\n}\n\nfunction resolveCircleLineWidth(squareSize: number, lineWidth: number | undefined) {\n const candidate = lineWidth ?? squareSize * 0.08;\n return Math.max(2, Math.min(8, candidate));\n}\n\nfunction resolveCircleRadius(\n squareSize: number,\n radius: number | undefined,\n lineWidth: number,\n) {\n const radiusPx = squareSize * (radius ?? 0.42);\n return Math.max(0, radiusPx - lineWidth / 2);\n}\n\nfunction resolveBorderCoordinateFontSize(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n const maxFontSize = Math.floor(\n Math.min(geometry.squareSize * 0.6, geometry.borderSize * 0.65),\n );\n let fontSize: number | null = null;\n\n for (let candidate = maxFontSize; candidate >= MIN_COORDINATE_FONT_SIZE; candidate -= 1) {\n context.font = `${candidate}px sans-serif`;\n\n const filesFit = geometry.borderFileLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.squareSize * MAX_FILE_LABEL_WIDTH_RATIO &&\n textHeight <= geometry.borderSize * MAX_LABEL_HEIGHT_RATIO\n );\n });\n const ranksFit = geometry.borderRankLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.borderSize * MAX_RANK_LABEL_WIDTH_RATIO &&\n textHeight <= geometry.squareSize * MAX_LABEL_HEIGHT_RATIO\n );\n });\n\n if (filesFit && ranksFit) {\n fontSize = candidate;\n break;\n }\n }\n\n return fontSize;\n}\n\nfunction resolveInsideCoordinateFontSize(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n const maxFontSize = Math.floor(\n geometry.squareSize * INSIDE_COORDINATE_MAX_FONT_RATIO,\n );\n let fontSize: number | null = null;\n\n for (let candidate = maxFontSize; candidate >= MIN_COORDINATE_FONT_SIZE; candidate -= 1) {\n context.font = `${candidate}px sans-serif`;\n\n const filesFit = geometry.insideFileLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.insideLabelMaxWidth &&\n textHeight <= geometry.insideLabelMaxHeight\n );\n });\n const ranksFit = geometry.insideRankLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.insideLabelMaxWidth &&\n textHeight <= geometry.insideLabelMaxHeight\n );\n });\n\n if (filesFit && ranksFit) {\n fontSize = candidate;\n break;\n }\n }\n\n return fontSize;\n}\n\nfunction drawBorderCoordinates(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n if (geometry.borderSize === 0) {\n return;\n }\n\n const fontSize = resolveBorderCoordinateFontSize(context, geometry);\n\n if (fontSize === null) {\n return;\n }\n\n context.fillStyle = request.coordinates.color ?? \"#333\";\n context.font = `${fontSize}px sans-serif`;\n context.textAlign = \"center\";\n context.textBaseline = \"middle\";\n\n for (const label of geometry.borderFileLabels) {\n context.fillText(label.text, label.x, label.y);\n }\n\n for (const label of geometry.borderRankLabels) {\n context.fillText(label.text, label.x, label.y);\n }\n}\n\nfunction drawInsideCoordinates(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n const fontSize = resolveInsideCoordinateFontSize(context, geometry);\n\n if (fontSize === null) {\n return;\n }\n\n context.font = `${fontSize}px sans-serif`;\n\n for (const label of geometry.insideFileLabels) {\n context.fillStyle = resolveInsideLabelColor(request, label.square);\n context.textAlign = label.textAlign;\n context.textBaseline = label.textBaseline;\n context.fillText(label.text, label.x, label.y);\n }\n\n for (const label of geometry.insideRankLabels) {\n context.fillStyle = resolveInsideLabelColor(request, label.square);\n context.textAlign = label.textAlign;\n context.textBaseline = label.textBaseline;\n context.fillText(label.text, label.x, label.y);\n }\n}\n\nfunction drawCoordinates(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n if (!request.coordinates.enabled) {\n return;\n }\n\n if (request.coordinates.position === \"border\") {\n drawBorderCoordinates(context, request, geometry);\n return;\n }\n\n drawInsideCoordinates(context, request, geometry);\n}\n\nfunction drawBoardSquares(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const square of SQUARES) {\n const squareGeometry = geometry.squares[square];\n context.fillStyle = isDarkSquare(square)\n ? request.colors.darkSquare\n : request.colors.lightSquare;\n context.fillRect(\n squareGeometry.x,\n squareGeometry.y,\n squareGeometry.size,\n squareGeometry.size,\n );\n }\n}\n\nfunction drawFillHighlights(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const highlight of request.highlights) {\n if (highlight.style !== \"fill\") {\n continue;\n }\n\n const squareGeometry = geometry.squares[highlight.square];\n context.save();\n context.globalAlpha = resolveHighlightOpacity(\n highlight.style,\n highlight.color,\n highlight.opacity,\n );\n context.fillStyle = highlight.color ?? request.colors.highlight;\n context.fillRect(\n squareGeometry.x,\n squareGeometry.y,\n squareGeometry.size,\n squareGeometry.size,\n );\n context.restore();\n }\n}\n\nfunction drawCircleHighlights(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const highlight of request.highlights) {\n if (highlight.style !== \"circle\") {\n continue;\n }\n\n const squareGeometry = geometry.squares[highlight.square];\n const centerX = squareGeometry.x + squareGeometry.size / 2;\n const centerY = squareGeometry.y + squareGeometry.size / 2;\n context.save();\n context.globalAlpha = resolveHighlightOpacity(\n highlight.style,\n highlight.color,\n highlight.opacity,\n );\n context.strokeStyle = highlight.color ?? \"#ffcc00\";\n context.lineWidth = resolveCircleLineWidth(\n squareGeometry.size,\n highlight.lineWidth,\n );\n const radius = resolveCircleRadius(\n squareGeometry.size,\n highlight.radius,\n context.lineWidth,\n );\n context.beginPath();\n context.arc(\n centerX,\n centerY,\n radius,\n 0,\n Math.PI * 2,\n );\n context.stroke();\n context.restore();\n }\n}\n\nasync function drawPieces(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const square of SQUARES) {\n const squareGeometry = geometry.squares[square];\n const pieceKey = request.board.squares[square];\n if (!pieceKey) {\n continue;\n }\n\n const raster = await getPieceRaster(\n request.theme.name,\n pieceKey,\n request.theme.pieces[pieceKey],\n Math.round(geometry.squareSize),\n );\n context.drawImage(\n raster,\n squareGeometry.x,\n squareGeometry.y,\n squareGeometry.size,\n squareGeometry.size,\n );\n }\n}\n\nexport class CanvasPngRenderer implements Renderer<Buffer> {\n async render(request: RenderRequest): Promise<Buffer> {\n try {\n const geometry = createBoardGeometry({\n size: request.size,\n padding: request.padding,\n borderSize: request.borderSize,\n flipped: request.flipped,\n });\n\n const canvas = createCanvas(geometry.imageWidth, geometry.imageHeight);\n const context = canvas.getContext(\"2d\");\n\n context.fillStyle = request.colors.lightSquare;\n context.fillRect(0, 0, geometry.imageWidth, geometry.imageHeight);\n drawBoardSquares(context, request, geometry);\n drawFillHighlights(context, request, geometry);\n drawCircleHighlights(context, request, geometry);\n drawCoordinates(context, request, geometry);\n await drawPieces(context, request, geometry);\n\n return canvas.toBuffer(\"image/png\");\n } catch (error) {\n if (error instanceof RenderError) {\n throw error;\n }\n\n throw new RenderError(\"Failed to render chess board\", { cause: error });\n }\n }\n}\n","import type { Padding, Square } from \"../types/types\";\nimport { FILES, RANKS, SQUARES } from \"./board\";\n\nexport interface SquareGeometry {\n x: number;\n y: number;\n size: number;\n}\n\nexport interface CoordinateLabelGeometry {\n text: string;\n x: number;\n y: number;\n square: Square;\n textAlign: \"left\" | \"center\" | \"right\";\n textBaseline: \"top\" | \"middle\" | \"bottom\";\n}\n\nexport interface BoardGeometry {\n imageWidth: number;\n imageHeight: number;\n squareSize: number;\n borderSize: number;\n boardOuterX: number;\n boardOuterY: number;\n boardOuterSize: number;\n boardX: number;\n boardY: number;\n boardSize: number;\n borderFileLabels: CoordinateLabelGeometry[];\n borderRankLabels: CoordinateLabelGeometry[];\n insideFileLabels: CoordinateLabelGeometry[];\n insideRankLabels: CoordinateLabelGeometry[];\n insideFileInsetX: number;\n insideFileInsetY: number;\n insideRankInsetX: number;\n insideRankInsetY: number;\n insideLabelMaxWidth: number;\n insideLabelMaxHeight: number;\n squares: Record<Square, SquareGeometry>;\n}\n\ninterface BoardGeometryOptions {\n size: number;\n padding: Padding;\n borderSize: number;\n flipped: boolean;\n}\n\nexport function createBoardGeometry({\n size,\n padding,\n borderSize,\n flipped,\n}: BoardGeometryOptions): BoardGeometry {\n const [top, right, bottom, left] = padding;\n const boardOuterSize = size;\n const boardOuterX = left;\n const boardOuterY = top;\n const boardX = left + borderSize;\n const boardY = top + borderSize;\n const boardSize = size - borderSize * 2;\n const squareSize = boardSize / 8;\n\n const squares = Object.fromEntries(\n SQUARES.map((square, index) => {\n const fileIndex = index % 8;\n const rankIndex = Math.floor(index / 8);\n\n const x = boardX + (flipped ? 7 - fileIndex : fileIndex) * squareSize;\n const y = boardY + (flipped ? 7 - rankIndex : rankIndex) * squareSize;\n\n return [square, { x, y, size: squareSize }];\n }),\n ) as Record<Square, SquareGeometry>;\n\n const displayedFiles = flipped ? [...FILES].reverse() : [...FILES];\n const displayedRanks = flipped ? [...RANKS].reverse() : [...RANKS];\n const bottomEdgeRank = flipped ? \"8\" : \"1\";\n const leftEdgeFile = flipped ? \"h\" : \"a\";\n const borderFileLabels = borderSize\n ? displayedFiles.map((file, fileIndex) => ({\n text: file,\n x: boardX + fileIndex * squareSize + squareSize / 2,\n y: boardOuterY + boardOuterSize - borderSize / 2,\n square: `${file}${bottomEdgeRank}` as Square,\n textAlign: \"center\" as const,\n textBaseline: \"middle\" as const,\n }))\n : [];\n const borderRankLabels = borderSize\n ? displayedRanks.map((rank, rankIndex) => ({\n text: rank,\n x: boardOuterX + borderSize / 2,\n y: boardY + rankIndex * squareSize + squareSize / 2,\n square: `${leftEdgeFile}${rank}` as Square,\n textAlign: \"center\" as const,\n textBaseline: \"middle\" as const,\n }))\n : [];\n const insideFileInsetX = squareSize * 0.14;\n const insideFileInsetY = squareSize * 0.1;\n const insideRankInsetX = squareSize * 0.14;\n const insideRankInsetY = squareSize * 0.1;\n const insideLabelMaxWidth = squareSize * 0.26;\n const insideLabelMaxHeight = squareSize * 0.24;\n const insideFileLabels = displayedFiles.map((file) => {\n const square = `${file}${bottomEdgeRank}` as Square;\n const squareGeometry = squares[square];\n\n return {\n text: file,\n x: squareGeometry.x + squareGeometry.size - insideFileInsetX,\n y: squareGeometry.y + squareGeometry.size - insideFileInsetY,\n square,\n textAlign: \"right\" as const,\n textBaseline: \"bottom\" as const,\n };\n });\n const insideRankLabels = displayedRanks.map((rank) => {\n const square = `${leftEdgeFile}${rank}` as Square;\n const squareGeometry = squares[square];\n\n return {\n text: rank,\n x: squareGeometry.x + insideRankInsetX,\n y: squareGeometry.y + insideRankInsetY,\n square,\n textAlign: \"left\" as const,\n textBaseline: \"top\" as const,\n };\n });\n\n return {\n imageWidth: left + size + right,\n imageHeight: top + size + bottom,\n squareSize,\n borderSize,\n boardOuterX,\n boardOuterY,\n boardOuterSize,\n boardX,\n boardY,\n boardSize,\n borderFileLabels,\n borderRankLabels,\n insideFileLabels,\n insideRankLabels,\n insideFileInsetX,\n insideFileInsetY,\n insideRankInsetX,\n insideRankInsetY,\n insideLabelMaxWidth,\n insideLabelMaxHeight,\n squares,\n };\n}\n","export function createRasterCacheKey(\n themeName: string,\n pieceKey: string,\n squareSize: number,\n backend: string,\n): string {\n return `${themeName}:${pieceKey}:${squareSize}:${backend}`;\n}\n\nexport class RasterAssetCache<T> {\n private readonly cache = new Map<string, T>();\n\n get(key: string): T | undefined {\n return this.cache.get(key);\n }\n\n set(key: string, value: T): void {\n this.cache.set(key, value);\n }\n}\n\nexport class SourceAssetCache<T> {\n private readonly cache = new Map<string, T>();\n\n get(key: string): T | undefined {\n return this.cache.get(key);\n }\n\n set(key: string, value: T): void {\n this.cache.set(key, value);\n }\n}\n","import { readFile } from \"node:fs/promises\";\nimport { createCanvas, loadImage } from \"canvas\";\nimport { Resvg } from \"@resvg/resvg-js\";\nimport { RenderError } from \"../types/errors\";\nimport type { ThemeAssetSource } from \"../types/types\";\nimport { SourceAssetCache } from \"./asset-cache\";\n\nconst svgSourceCache = new SourceAssetCache<string>();\nconst imageBufferCache = new SourceAssetCache<Buffer>();\n\nasync function readSvgSource(filePath: string): Promise<string> {\n const cached = svgSourceCache.get(filePath);\n\n if (cached) {\n return cached;\n }\n\n try {\n const source = await readFile(filePath, \"utf8\");\n svgSourceCache.set(filePath, source);\n return source;\n } catch (error) {\n throw new RenderError(`Failed to read SVG asset: ${filePath}`, { cause: error });\n }\n}\n\nasync function readBinaryAsset(filePath: string): Promise<Buffer> {\n const cached = imageBufferCache.get(filePath);\n\n if (cached) {\n return cached;\n }\n\n try {\n const source = await readFile(filePath);\n imageBufferCache.set(filePath, source);\n return source;\n } catch (error) {\n throw new RenderError(`Failed to read image asset: ${filePath}`, { cause: error });\n }\n}\n\nasync function rasterizeSvgAsset(filePath: string, squareSize: number) {\n try {\n const svgSource = await readSvgSource(filePath);\n const resvg = new Resvg(svgSource, {\n fitTo: {\n mode: \"width\",\n value: squareSize,\n },\n });\n const pngBuffer = resvg.render().asPng();\n const image = await loadImage(pngBuffer);\n const canvas = createCanvas(squareSize, squareSize);\n const context = canvas.getContext(\"2d\");\n context.drawImage(image, 0, 0, squareSize, squareSize);\n return canvas;\n } catch (error) {\n throw new RenderError(`Failed to rasterize SVG asset: ${filePath}`, { cause: error });\n }\n}\n\nasync function rasterizePngAsset(filePath: string, squareSize: number) {\n try {\n const pngSource = await readBinaryAsset(filePath);\n const image = await loadImage(pngSource);\n const canvas = createCanvas(squareSize, squareSize);\n const context = canvas.getContext(\"2d\");\n context.drawImage(image, 0, 0, squareSize, squareSize);\n return canvas;\n } catch (error) {\n throw new RenderError(`Failed to rasterize PNG asset: ${filePath}`, { cause: error });\n }\n}\n\nexport async function rasterizeThemeAsset(asset: ThemeAssetSource, squareSize: number) {\n if (asset.kind === \"svg\") {\n return rasterizeSvgAsset(asset.source, squareSize);\n }\n\n return rasterizePngAsset(asset.source, squareSize);\n}\n","import { writeFile } from \"node:fs/promises\";\nimport { IOError } from \"../types/errors\";\n\nexport async function writeBufferToFile(filePath: string, buffer: Buffer): Promise<void> {\n try {\n await writeFile(filePath, buffer);\n } catch (error) {\n throw new IOError(`Failed to write file: ${filePath}`, { cause: error });\n }\n}\n","import type { HighlightInput, ResolvedHighlight } from \"../types/types\";\nimport { validateSquare } from \"./validators\";\n\nexport function normalizeHighlightEntries(input: HighlightInput[]): ResolvedHighlight[] {\n return input.map((entry) => {\n if (typeof entry === \"string\") {\n return {\n square: validateSquare(entry),\n style: \"fill\",\n color: undefined,\n opacity: undefined,\n lineWidth: undefined,\n radius: undefined,\n };\n }\n\n const style = entry.style ?? \"fill\";\n\n return {\n square: validateSquare(entry.square),\n style,\n color: entry.color ?? (style === \"circle\" ? \"#ffcc00\" : undefined),\n opacity: entry.opacity ?? (style === \"circle\" ? 0.9 : undefined),\n lineWidth: entry.lineWidth,\n radius: style === \"circle\" ? (entry.radius ?? 0.42) : undefined,\n };\n });\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { PieceKey, PieceStyle, ThemeDefinition } from \"../types/types\";\nimport { registerTheme } from \"./registry\";\n\nexport const builtInThemeNames = [\n \"merida\",\n \"alpha\",\n \"cburnett\",\n \"cheq\",\n \"leipzig\",\n] as const satisfies readonly PieceStyle[];\n\nconst PIECE_KEYS: PieceKey[] = [\n \"wK\",\n \"wQ\",\n \"wR\",\n \"wB\",\n \"wN\",\n \"wP\",\n \"bK\",\n \"bQ\",\n \"bR\",\n \"bB\",\n \"bN\",\n \"bP\",\n];\n\nlet initialized = false;\n\nfunction assetPath(themeName: PieceStyle, pieceKey: PieceKey): string {\n const candidates = [\n resolve(__dirname, \"../../assets/themes\", themeName, `${pieceKey}.png`),\n resolve(__dirname, \"../assets/themes\", themeName, `${pieceKey}.png`),\n resolve(process.cwd(), \"assets/themes\", themeName, `${pieceKey}.png`),\n ];\n\n const match = candidates.find((candidate) => existsSync(candidate));\n return match ?? candidates[0];\n}\n\nfunction createBuiltInTheme(themeName: PieceStyle): ThemeDefinition {\n return {\n name: themeName,\n displayName: themeName[0].toUpperCase() + themeName.slice(1),\n license: \"Derived from upstream chess-image-generator resource pack; original pack license not fully verified\",\n attribution:\n \"Derived from andyruwruw/chess-image-generator bundled resources; upstream README cites Marcel van Kervinck as source\",\n pieces: Object.fromEntries(\n PIECE_KEYS.map((pieceKey) => [\n pieceKey,\n {\n kind: \"png\",\n source: assetPath(themeName, pieceKey),\n },\n ]),\n ) as ThemeDefinition[\"pieces\"],\n };\n}\n\nexport function initializeBuiltInThemes(): void {\n if (initialized) {\n return;\n }\n\n for (const themeName of builtInThemeNames) {\n registerTheme(createBuiltInTheme(themeName));\n }\n\n initialized = true;\n}\n\nexport function resetBuiltInThemesForTesting(): void {\n initialized = false;\n}\n","import { validateThemeName } from \"../core/validators\";\nimport { ThemeError, ValidationError } from \"../types/errors\";\nimport type { PieceKey, ThemeDefinition } from \"../types/types\";\n\nconst REQUIRED_PIECES: PieceKey[] = [\n \"wK\",\n \"wQ\",\n \"wR\",\n \"wB\",\n \"wN\",\n \"wP\",\n \"bK\",\n \"bQ\",\n \"bR\",\n \"bB\",\n \"bN\",\n \"bP\",\n];\n\nexport function validateThemeDefinition(theme: ThemeDefinition): ThemeDefinition {\n const normalizedName = validateThemeName(theme.name);\n\n if (!theme.displayName.trim()) {\n throw new ValidationError(\"Theme displayName is required\");\n }\n\n if (!theme.license.trim()) {\n throw new ValidationError(\"Theme license is required\");\n }\n\n if (!theme.attribution.trim()) {\n throw new ValidationError(\"Theme attribution is required\");\n }\n\n for (const pieceKey of REQUIRED_PIECES) {\n const asset = theme.pieces[pieceKey];\n\n if (!asset || (asset.kind !== \"svg\" && asset.kind !== \"png\") || !asset.source.trim()) {\n throw new ThemeError(`Theme \"${normalizedName}\" is missing asset ${pieceKey}`);\n }\n }\n\n return {\n ...theme,\n name: normalizedName,\n };\n}\n","import type { ThemeDefinition } from \"../types/types\";\nimport { ThemeError } from \"../types/errors\";\nimport { validateThemeDefinition } from \"./validation\";\n\nconst registry = new Map<string, ThemeDefinition>();\n\nexport function registerTheme(theme: ThemeDefinition): ThemeDefinition {\n const validatedTheme = validateThemeDefinition(theme);\n\n if (registry.has(validatedTheme.name)) {\n throw new ThemeError(`Theme \"${validatedTheme.name}\" is already registered`);\n }\n\n registry.set(validatedTheme.name, validatedTheme);\n return validatedTheme;\n}\n\nexport function getTheme(name: string): ThemeDefinition | undefined {\n return registry.get(name.trim().toLowerCase());\n}\n\nexport function clearThemeRegistryForTesting(): void {\n registry.clear();\n}\n","import type { PieceStyle, ThemeDefinition } from \"../types/types\";\nimport { ThemeError, ValidationError } from \"../types/errors\";\nimport { initializeBuiltInThemes } from \"./builtins\";\nimport { getTheme } from \"./registry\";\nimport { validateThemeDefinition } from \"./validation\";\n\ninterface ResolveThemeOptions {\n theme?: string | ThemeDefinition;\n style?: PieceStyle;\n}\n\nexport function resolveTheme({ theme, style }: ResolveThemeOptions): ThemeDefinition {\n initializeBuiltInThemes();\n\n if (typeof theme === \"object\" && theme !== null) {\n try {\n return validateThemeDefinition(theme);\n } catch (error) {\n if (error instanceof ThemeError) {\n throw error;\n }\n\n if (error instanceof ValidationError) {\n throw new ThemeError(error.message, { cause: error });\n }\n\n throw error;\n }\n }\n\n const requestedName = typeof theme === \"string\" ? theme : style ?? \"merida\";\n const resolvedTheme = getTheme(requestedName);\n\n if (!resolvedTheme) {\n throw new ThemeError(`Unknown theme: ${requestedName}`);\n }\n\n return resolvedTheme;\n}\n","import type {\n BoardColors,\n CoordinatesInput,\n CoordinatesPosition,\n HighlightInput,\n Padding,\n RenderOptions,\n ResolvedColors,\n ResolvedCoordinates,\n ResolvedHighlight,\n ResolvedRenderOptions,\n ThemeDefinition,\n} from \"../types/types\";\nimport { normalizeHighlightEntries as normalizeCanonicalHighlightEntries } from \"../core/highlights\";\nimport {\n normalizePadding,\n validateBoardColors,\n validateBorderSize,\n validateCoordinatesOption,\n validateHighlightsInput,\n validateSize,\n} from \"../core/validators\";\nimport { resolveTheme } from \"../themes/resolver\";\n\nexport const DEFAULT_SIZE = 480;\nexport const DEFAULT_PADDING: Padding = [0, 0, 0, 0];\nexport const DEFAULT_BORDER_SIZE = 0;\nexport const DEFAULT_COLORS: ResolvedColors = {\n lightSquare: \"#f0d9b5\",\n darkSquare: \"#b58863\",\n highlight: \"rgba(255, 206, 0, 0.45)\",\n};\nexport const DEFAULT_COORDINATES: ResolvedCoordinates = {\n enabled: false,\n position: \"inside\",\n};\n\nexport function normalizeColors(colors?: BoardColors): ResolvedColors {\n return {\n lightSquare: colors?.lightSquare ?? DEFAULT_COLORS.lightSquare,\n darkSquare: colors?.darkSquare ?? DEFAULT_COLORS.darkSquare,\n highlight: colors?.highlight ?? DEFAULT_COLORS.highlight,\n };\n}\n\nexport function normalizeCoordinates(\n coordinates: CoordinatesInput | undefined,\n borderSize: number,\n): ResolvedCoordinates {\n if (coordinates === undefined || coordinates === false) {\n return { ...DEFAULT_COORDINATES };\n }\n\n const defaultPosition: CoordinatesPosition =\n borderSize > 0 ? \"border\" : \"inside\";\n\n if (coordinates === true) {\n return {\n enabled: true,\n position: defaultPosition,\n color: defaultPosition === \"border\" ? \"#333\" : undefined,\n };\n }\n\n if (coordinates === \"border\" || coordinates === \"inside\") {\n return {\n enabled: true,\n position: coordinates,\n color: coordinates === \"border\" ? \"#333\" : undefined,\n };\n }\n\n const position = coordinates.position ?? defaultPosition;\n\n return {\n enabled: coordinates.enabled ?? true,\n position,\n color: coordinates.color ?? (position === \"border\" ? \"#333\" : undefined),\n };\n}\n\nexport function normalizeHighlightEntries(\n highlights: HighlightInput[] | undefined,\n): ResolvedHighlight[] {\n return normalizeCanonicalHighlightEntries(highlights ?? []);\n}\n\nexport function normalizeRenderInputs(\n options: RenderOptions,\n): ResolvedRenderOptions {\n const size = validateSize(options.size ?? DEFAULT_SIZE);\n const borderSize = validateBorderSize(\n options.borderSize ?? DEFAULT_BORDER_SIZE,\n size,\n );\n validateBoardColors(options.colors);\n validateCoordinatesOption(options.coordinates, borderSize);\n validateHighlightsInput(options.highlights, options.highlightSquares);\n const highlightInput = options.highlights ?? options.highlightSquares;\n\n return {\n size,\n padding: normalizePadding(options.padding ?? DEFAULT_PADDING),\n borderSize,\n flipped: options.flipped ?? false,\n theme: resolveTheme({\n theme: options.theme,\n style: options.style,\n }),\n highlights: normalizeHighlightEntries(highlightInput),\n colors: normalizeColors(options.colors),\n coordinates: normalizeCoordinates(options.coordinates, borderSize),\n };\n}\n","import { parseBoardArray, parseFEN, parsePGN } from \"../core/parsers\";\nimport { validateHighlightsInput } from \"../core/validators\";\nimport { ValidationError } from \"../types/errors\";\nimport type {\n BoardArray,\n ChessImageGeneratorOptions,\n HighlightInput,\n RenderOptions,\n} from \"../types/types\";\nimport { CanvasPngRenderer } from \"../render/canvas-renderer\";\nimport { writeBufferToFile } from \"../utils/io\";\nimport { normalizeRenderInputs } from \"../utils/normalization\";\nimport type { BoardPosition } from \"../core/board\";\n\nexport class ChessImageGenerator {\n private position: BoardPosition | null = null;\n\n private readonly defaults: RenderOptions;\n\n private highlights: HighlightInput[] = [];\n\n constructor(options: ChessImageGeneratorOptions = {}) {\n this.defaults = { ...options };\n normalizeRenderInputs(this.defaults);\n }\n\n async loadFEN(fen: string): Promise<void> {\n this.position = parseFEN(fen);\n this.clearHighlights();\n }\n\n async loadPGN(pgn: string): Promise<void> {\n this.position = parsePGN(pgn);\n this.clearHighlights();\n }\n\n async loadBoard(board: BoardArray): Promise<void> {\n this.position = parseBoardArray(board);\n this.clearHighlights();\n }\n\n setHighlights(highlights: HighlightInput[]): void {\n validateHighlightsInput(highlights, undefined);\n this.highlights = [...highlights];\n }\n\n clearHighlights(): void {\n this.highlights = [];\n }\n\n async toBuffer(): Promise<Buffer> {\n if (!this.position) {\n throw new ValidationError(\"No board position loaded\");\n }\n\n const renderer = new CanvasPngRenderer();\n const normalized = normalizeRenderInputs({\n ...this.defaults,\n highlights: this.highlights,\n highlightSquares: undefined,\n });\n\n return renderer.render({\n board: this.position,\n theme: normalized.theme,\n highlights: normalized.highlights,\n size: normalized.size,\n padding: normalized.padding,\n borderSize: normalized.borderSize,\n flipped: normalized.flipped,\n colors: normalized.colors,\n coordinates: normalized.coordinates,\n });\n }\n\n async toFile(filePath: string): Promise<void> {\n const buffer = await this.toBuffer();\n await writeBufferToFile(filePath, buffer);\n }\n}\n","import { parseBoardArray, parseFEN, parsePGN } from \"../core/parsers\";\nimport { ValidationError } from \"../types/errors\";\nimport type { RenderChessOptions } from \"../types/types\";\nimport { CanvasPngRenderer } from \"../render/canvas-renderer\";\nimport { normalizeRenderInputs } from \"../utils/normalization\";\n\nfunction parseInputPosition(options: RenderChessOptions) {\n if (typeof options.fen === \"string\") {\n return parseFEN(options.fen);\n }\n\n if (typeof options.pgn === \"string\") {\n return parsePGN(options.pgn);\n }\n\n if (Array.isArray(options.board)) {\n return parseBoardArray(options.board);\n }\n\n throw new ValidationError(\"Exactly one of fen, pgn, or board must be provided\");\n}\n\nexport async function renderChess(options: RenderChessOptions): Promise<Buffer> {\n const provided = [\n typeof options.fen === \"string\" ? options.fen : undefined,\n typeof options.pgn === \"string\" ? options.pgn : undefined,\n Array.isArray(options.board) ? options.board : undefined,\n ].filter((value) => value !== undefined);\n\n if (provided.length !== 1) {\n throw new ValidationError(\"Exactly one of fen, pgn, or board must be provided\");\n }\n\n const position = parseInputPosition(options);\n const renderer = new CanvasPngRenderer();\n const normalized = normalizeRenderInputs(options);\n\n return renderer.render({\n board: position,\n theme: normalized.theme,\n highlights: normalized.highlights,\n size: normalized.size,\n padding: normalized.padding,\n borderSize: normalized.borderSize,\n flipped: normalized.flipped,\n colors: normalized.colors,\n coordinates: normalized.coordinates,\n });\n}\n"],"mappings":"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","names":["import_canvas","import_canvas","import_promises","normalizeHighlightEntries"]}
package/dist/index.d.cts CHANGED
@@ -58,6 +58,7 @@ interface HighlightOptions {
58
58
  color?: string;
59
59
  opacity?: number;
60
60
  lineWidth?: number;
61
+ radius?: number;
61
62
  }
62
63
  type HighlightInput = string | HighlightOptions;
63
64
  interface ResolvedHighlight {
@@ -66,6 +67,7 @@ interface ResolvedHighlight {
66
67
  color?: string;
67
68
  opacity?: number;
68
69
  lineWidth?: number;
70
+ radius?: number;
69
71
  }
70
72
  type CoordinatesPosition = "border" | "inside";
71
73
  interface CoordinatesOptions {
package/dist/index.d.ts CHANGED
@@ -58,6 +58,7 @@ interface HighlightOptions {
58
58
  color?: string;
59
59
  opacity?: number;
60
60
  lineWidth?: number;
61
+ radius?: number;
61
62
  }
62
63
  type HighlightInput = string | HighlightOptions;
63
64
  interface ResolvedHighlight {
@@ -66,6 +67,7 @@ interface ResolvedHighlight {
66
67
  color?: string;
67
68
  opacity?: number;
68
69
  lineWidth?: number;
70
+ radius?: number;
69
71
  }
70
72
  type CoordinatesPosition = "border" | "inside";
71
73
  interface CoordinatesOptions {
package/dist/index.js CHANGED
@@ -223,6 +223,9 @@ function validateHighlightEntry(entry) {
223
223
  if (entry.lineWidth !== void 0 && (!Number.isFinite(entry.lineWidth) || entry.lineWidth <= 0)) {
224
224
  throw new ValidationError("highlight.lineWidth must be a finite number greater than 0");
225
225
  }
226
+ if (entry.radius !== void 0 && (!Number.isFinite(entry.radius) || entry.radius <= 0 || entry.radius > 0.5)) {
227
+ throw new ValidationError("highlight.radius must be a finite number greater than 0 and at most 0.5");
228
+ }
226
229
  }
227
230
  function validateHighlightOptions(highlights) {
228
231
  if (highlights === void 0) {
@@ -543,6 +546,10 @@ function resolveCircleLineWidth(squareSize, lineWidth) {
543
546
  const candidate = lineWidth ?? squareSize * 0.08;
544
547
  return Math.max(2, Math.min(8, candidate));
545
548
  }
549
+ function resolveCircleRadius(squareSize, radius, lineWidth) {
550
+ const radiusPx = squareSize * (radius ?? 0.42);
551
+ return Math.max(0, radiusPx - lineWidth / 2);
552
+ }
546
553
  function resolveBorderCoordinateFontSize(context, geometry) {
547
554
  const maxFontSize = Math.floor(
548
555
  Math.min(geometry.squareSize * 0.6, geometry.borderSize * 0.65)
@@ -692,11 +699,16 @@ function drawCircleHighlights(context, request, geometry) {
692
699
  squareGeometry.size,
693
700
  highlight.lineWidth
694
701
  );
702
+ const radius = resolveCircleRadius(
703
+ squareGeometry.size,
704
+ highlight.radius,
705
+ context.lineWidth
706
+ );
695
707
  context.beginPath();
696
708
  context.arc(
697
709
  centerX,
698
710
  centerY,
699
- squareGeometry.size * 0.32,
711
+ radius,
700
712
  0,
701
713
  Math.PI * 2
702
714
  );
@@ -770,7 +782,11 @@ function normalizeHighlightEntries(input) {
770
782
  if (typeof entry === "string") {
771
783
  return {
772
784
  square: validateSquare(entry),
773
- style: "fill"
785
+ style: "fill",
786
+ color: void 0,
787
+ opacity: void 0,
788
+ lineWidth: void 0,
789
+ radius: void 0
774
790
  };
775
791
  }
776
792
  const style = entry.style ?? "fill";
@@ -779,7 +795,8 @@ function normalizeHighlightEntries(input) {
779
795
  style,
780
796
  color: entry.color ?? (style === "circle" ? "#ffcc00" : void 0),
781
797
  opacity: entry.opacity ?? (style === "circle" ? 0.9 : void 0),
782
- lineWidth: entry.lineWidth
798
+ lineWidth: entry.lineWidth,
799
+ radius: style === "circle" ? entry.radius ?? 0.42 : void 0
783
800
  };
784
801
  });
785
802
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../node_modules/tsup/assets/esm_shims.js","../src/types/errors.ts","../src/core/parsers.ts","../src/core/board.ts","../src/core/validators.ts","../src/render/canvas-renderer.ts","../src/core/geometry.ts","../src/render/asset-cache.ts","../src/render/rasterizer.ts","../src/utils/io.ts","../src/core/highlights.ts","../src/themes/builtins.ts","../src/themes/validation.ts","../src/themes/registry.ts","../src/themes/resolver.ts","../src/utils/normalization.ts","../src/api/class-api.ts","../src/api/functional-api.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","export class ValidationError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ValidationError\";\n }\n}\n\nexport class ParseError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ParseError\";\n }\n}\n\nexport class ThemeError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ThemeError\";\n }\n}\n\nexport class RenderError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"RenderError\";\n }\n}\n\nexport class IOError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"IOError\";\n }\n}\n","import { Chess } from \"chess.js\";\nimport type { BoardArray, BoardCell, PieceKey } from \"../types/types\";\nimport { ParseError, ValidationError } from \"../types/errors\";\nimport { createEmptyBoardPosition, FILES } from \"./board\";\nimport { validateBoardArray } from \"./validators\";\n\nconst PIECE_SYMBOL_TO_KEY: Record<string, PieceKey> = {\n K: \"wK\",\n Q: \"wQ\",\n R: \"wR\",\n B: \"wB\",\n N: \"wN\",\n P: \"wP\",\n k: \"bK\",\n q: \"bQ\",\n r: \"bR\",\n b: \"bB\",\n n: \"bN\",\n p: \"bP\",\n};\n\nfunction chessBoardToBoardArray(board: ReturnType<Chess[\"board\"]>): BoardArray {\n return board.map((rank) =>\n rank.map((piece): BoardCell => {\n if (!piece) {\n return null;\n }\n\n return piece.color === \"w\" ? piece.type.toUpperCase() : piece.type;\n }),\n );\n}\n\nexport function parseFEN(fen: string) {\n const chess = new Chess();\n\n try {\n chess.load(fen);\n } catch (error) {\n throw new ParseError(\"Invalid FEN\", { cause: error });\n }\n\n return parseBoardArray(chessBoardToBoardArray(chess.board()));\n}\n\nexport function parsePGN(pgn: string) {\n const chess = new Chess();\n\n try {\n chess.loadPgn(pgn);\n } catch (error) {\n throw new ParseError(\"Invalid PGN\", { cause: error });\n }\n\n return parseBoardArray(chessBoardToBoardArray(chess.board()));\n}\n\nexport function parseBoardArray(board: BoardArray) {\n const validatedBoard = validateBoardArray(board);\n const position = createEmptyBoardPosition();\n\n validatedBoard.forEach((rank, rankIndex) => {\n rank.forEach((cell, fileIndex) => {\n if (cell === null) {\n return;\n }\n\n const pieceKey = PIECE_SYMBOL_TO_KEY[cell];\n\n if (!pieceKey) {\n throw new ValidationError(`Invalid board piece: ${cell}`);\n }\n\n const square = `${FILES[fileIndex]}${8 - rankIndex}` as keyof typeof position.squares;\n position.squares[square] = pieceKey;\n });\n });\n\n return position;\n}\n","import type { PieceKey, Square } from \"../types/types\";\n\nexport const FILES = [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"] as const;\nexport const RANKS = [\"8\", \"7\", \"6\", \"5\", \"4\", \"3\", \"2\", \"1\"] as const;\n\nexport const SQUARES: Square[] = RANKS.flatMap((rank) =>\n FILES.map((file) => `${file}${rank}`),\n);\n\nexport interface BoardPosition {\n squares: Record<Square, PieceKey | null>;\n}\n\nexport function createEmptyBoardPosition(): BoardPosition {\n return {\n squares: Object.fromEntries(\n SQUARES.map((square) => [square, null]),\n ) as Record<Square, PieceKey | null>,\n };\n}\n","import { createCanvas } from \"canvas\";\nimport type {\n BoardArray,\n BoardCell,\n BoardColors,\n CoordinatesInput,\n CoordinatesOptions,\n CoordinatesPosition,\n HighlightInput,\n HighlightOptions,\n HighlightStyle,\n Padding,\n Square,\n} from \"../types/types\";\nimport { ValidationError } from \"../types/errors\";\n\nconst SQUARE_PATTERN = /^[a-h][1-8]$/;\nconst PIECE_PATTERN = /^[prnbqkPRNBQK]$/;\nconst BUILT_IN_THEME_PATTERN = /^(merida|alpha|cburnett|cheq|leipzig)$/;\nconst colorValidationContext = createCanvas(1, 1).getContext(\"2d\");\n\nexport function validateSize(size: number): number {\n if (!Number.isFinite(size) || size <= 0) {\n throw new ValidationError(`Invalid board size: ${size}`);\n }\n\n return Math.round(size);\n}\n\nexport function normalizePadding(padding?: number[] | Padding): Padding {\n const candidate = padding ?? [0, 0, 0, 0];\n\n if (\n !Array.isArray(candidate) ||\n candidate.length !== 4 ||\n candidate.some((value) => !Number.isFinite(value) || value < 0)\n ) {\n throw new ValidationError(\"Padding must be a 4-item array of non-negative numbers\");\n }\n\n return [\n Math.round(candidate[0]),\n Math.round(candidate[1]),\n Math.round(candidate[2]),\n Math.round(candidate[3]),\n ];\n}\n\nexport function validateSquare(square: string): Square {\n const normalized = square.trim().toLowerCase();\n\n if (!SQUARE_PATTERN.test(normalized)) {\n throw new ValidationError(`Invalid square: ${square}`);\n }\n\n return normalized;\n}\n\nexport function validateBoardCell(cell: BoardCell): BoardCell {\n if (cell === null || cell === \"\" || cell === \" \") {\n return null;\n }\n\n if (typeof cell !== \"string\" || !PIECE_PATTERN.test(cell)) {\n throw new ValidationError(`Invalid board piece: ${String(cell)}`);\n }\n\n return cell;\n}\n\nexport function validateBoardArray(board: BoardArray): BoardArray {\n if (!Array.isArray(board) || board.length !== 8) {\n throw new ValidationError(\"Board array must have exactly 8 ranks\");\n }\n\n return board.map((rank, rankIndex) => {\n if (!Array.isArray(rank) || rank.length !== 8) {\n throw new ValidationError(`Board rank ${rankIndex} must contain exactly 8 files`);\n }\n\n return rank.map(validateBoardCell);\n });\n}\n\nexport function validateStyleName(style: string): string {\n const normalized = style.trim().toLowerCase();\n\n if (!BUILT_IN_THEME_PATTERN.test(normalized)) {\n throw new ValidationError(`Unknown built-in style: ${style}`);\n }\n\n return normalized;\n}\n\nexport function validateThemeName(name: string): string {\n const normalized = name.trim().toLowerCase();\n\n if (!/^[a-z0-9-]+$/.test(normalized)) {\n throw new ValidationError(`Invalid theme name: ${name}`);\n }\n\n return normalized;\n}\n\nexport function validateBorderSize(borderSize: number, size: number): number {\n const normalizedSize = validateSize(size);\n const normalizedBorderSize = Math.round(borderSize);\n const maxBorderSize = Math.floor(normalizedSize / 8);\n\n if (!Number.isFinite(borderSize) || normalizedBorderSize < 0) {\n throw new ValidationError(`Invalid borderSize: ${borderSize}`);\n }\n\n if (normalizedBorderSize > maxBorderSize) {\n throw new ValidationError(\n `Invalid borderSize: ${borderSize}. Maximum allowed for size ${normalizedSize} is ${maxBorderSize}`,\n );\n }\n\n return normalizedBorderSize;\n}\n\nexport function validateColorString(color: string, label = \"color\"): string {\n if (typeof color !== \"string\" || color.trim() === \"\") {\n throw new ValidationError(`Invalid ${label}: ${String(color)}`);\n }\n\n const normalized = color.trim();\n colorValidationContext.fillStyle = \"#010203\";\n colorValidationContext.fillStyle = normalized;\n const firstPass = String(colorValidationContext.fillStyle);\n colorValidationContext.fillStyle = \"#fefefe\";\n colorValidationContext.fillStyle = normalized;\n const secondPass = String(colorValidationContext.fillStyle);\n\n if (firstPass !== secondPass) {\n throw new ValidationError(`Invalid ${label}: ${color}`);\n }\n\n return normalized;\n}\n\nexport function validateBoardColors(colors?: BoardColors): void {\n if (!colors) {\n return;\n }\n\n if (colors.lightSquare !== undefined) {\n validateColorString(colors.lightSquare, \"lightSquare color\");\n }\n\n if (colors.darkSquare !== undefined) {\n validateColorString(colors.darkSquare, \"darkSquare color\");\n }\n\n if (colors.highlight !== undefined) {\n validateColorString(colors.highlight, \"highlight color\");\n }\n}\n\nfunction isCoordinatesOptions(value: unknown): value is CoordinatesOptions {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isCoordinatesPosition(value: unknown): value is CoordinatesPosition {\n return value === \"border\" || value === \"inside\";\n}\n\nfunction isHighlightOptions(value: unknown): value is HighlightOptions {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isHighlightStyle(value: unknown): value is HighlightStyle {\n return value === \"fill\" || value === \"circle\";\n}\n\nexport function validateCoordinatesOption(\n coordinates: CoordinatesInput | undefined,\n borderSize: number,\n): void {\n if (coordinates === undefined || typeof coordinates === \"boolean\") {\n return;\n }\n\n if (isCoordinatesPosition(coordinates)) {\n if (coordinates === \"border\" && borderSize === 0) {\n throw new ValidationError(\n \"coordinates position 'border' requires borderSize > 0\",\n );\n }\n\n return;\n }\n\n if (!isCoordinatesOptions(coordinates)) {\n throw new ValidationError(\n \"coordinates must be false, true, 'border', 'inside', or an options object\",\n );\n }\n\n if (\n coordinates.enabled !== undefined &&\n typeof coordinates.enabled !== \"boolean\"\n ) {\n throw new ValidationError(\"coordinates.enabled must be a boolean\");\n }\n\n if (\n coordinates.position !== undefined &&\n !isCoordinatesPosition(coordinates.position)\n ) {\n throw new ValidationError(\"coordinates.position must be 'border' or 'inside'\");\n }\n\n if (\n coordinates.enabled !== false &&\n coordinates.position === \"border\" &&\n borderSize === 0\n ) {\n throw new ValidationError(\n \"coordinates position 'border' requires borderSize > 0\",\n );\n }\n\n if (coordinates.color !== undefined) {\n validateColorString(coordinates.color, \"coordinates.color\");\n }\n}\n\nfunction validateHighlightEntry(entry: HighlightInput): void {\n if (typeof entry === \"string\") {\n validateSquare(entry);\n return;\n }\n\n if (!isHighlightOptions(entry)) {\n throw new ValidationError(\"highlights entries must be square strings or highlight objects\");\n }\n\n if (typeof entry.square !== \"string\") {\n throw new ValidationError(\"highlight.square must be a valid algebraic square\");\n }\n\n validateSquare(entry.square);\n\n if (entry.style !== undefined && !isHighlightStyle(entry.style)) {\n throw new ValidationError(\"highlight.style must be 'fill' or 'circle'\");\n }\n\n if (entry.color !== undefined) {\n validateColorString(entry.color, \"highlight.color\");\n }\n\n if (\n entry.opacity !== undefined &&\n (!Number.isFinite(entry.opacity) || entry.opacity < 0 || entry.opacity > 1)\n ) {\n throw new ValidationError(\"highlight.opacity must be a finite number between 0 and 1\");\n }\n\n if (\n entry.lineWidth !== undefined &&\n (!Number.isFinite(entry.lineWidth) || entry.lineWidth <= 0)\n ) {\n throw new ValidationError(\"highlight.lineWidth must be a finite number greater than 0\");\n }\n}\n\nexport function validateHighlightOptions(highlights: HighlightInput[] | undefined): void {\n if (highlights === undefined) {\n return;\n }\n\n if (!Array.isArray(highlights)) {\n throw new ValidationError(\"highlights must be an array\");\n }\n\n for (const entry of highlights) {\n validateHighlightEntry(entry);\n }\n}\n\nexport function validateHighlightsInput(\n highlights: HighlightInput[] | undefined,\n highlightSquares: HighlightInput[] | undefined,\n): void {\n if (highlights !== undefined && highlightSquares !== undefined) {\n throw new ValidationError(\"Use either highlights or highlightSquares, not both\");\n }\n\n validateHighlightOptions(highlights);\n validateHighlightOptions(highlightSquares);\n}\n","import { createCanvas } from \"canvas\";\nimport { SQUARES } from \"../core/board\";\nimport { createBoardGeometry } from \"../core/geometry\";\nimport { RenderError } from \"../types/errors\";\nimport type { ThemeAssetSource } from \"../types/types\";\nimport { createRasterCacheKey, RasterAssetCache } from \"./asset-cache\";\nimport type { RenderRequest, Renderer } from \"./renderer\";\nimport { rasterizeThemeAsset } from \"./rasterizer\";\n\nconst pieceRasterCache = new RasterAssetCache<Awaited<ReturnType<typeof rasterizeThemeAsset>>>();\nconst MIN_COORDINATE_FONT_SIZE = 8;\nconst MAX_FILE_LABEL_WIDTH_RATIO = 0.75;\nconst MAX_RANK_LABEL_WIDTH_RATIO = 0.7;\nconst MAX_LABEL_HEIGHT_RATIO = 0.7;\nconst INSIDE_COORDINATE_MAX_FONT_RATIO = 0.34;\nconst INSIDE_LIGHT_LABEL_COLOR = \"rgba(255,255,255,0.6)\";\nconst INSIDE_DARK_LABEL_COLOR = \"rgba(0,0,0,0.45)\";\n\nfunction isDarkSquare(square: string): boolean {\n const fileIndex = square.charCodeAt(0) - 97;\n const rankNumber = Number(square[1]);\n return (fileIndex + rankNumber) % 2 === 1;\n}\n\nasync function getPieceRaster(\n themeName: string,\n pieceKey: string,\n asset: ThemeAssetSource,\n squareSize: number,\n) {\n const cacheKey = createRasterCacheKey(themeName, pieceKey, squareSize, \"png-canvas\");\n const cached = pieceRasterCache.get(cacheKey);\n\n if (cached) {\n return cached;\n }\n\n const raster = await rasterizeThemeAsset(asset, squareSize);\n pieceRasterCache.set(cacheKey, raster);\n return raster;\n}\n\nfunction resolveInsideLabelColor(\n request: RenderRequest,\n square: string,\n) {\n if (request.coordinates.color) {\n return request.coordinates.color;\n }\n\n return isDarkSquare(square) ? INSIDE_LIGHT_LABEL_COLOR : INSIDE_DARK_LABEL_COLOR;\n}\n\nfunction resolveHighlightOpacity(style: \"fill\" | \"circle\", color: string | undefined, opacity: number | undefined) {\n if (opacity !== undefined) {\n return opacity;\n }\n\n if (style === \"circle\" || color !== undefined) {\n return 0.9;\n }\n\n // Preserve legacy fill behavior when the board highlight color is used directly.\n return 1;\n}\n\nfunction resolveCircleLineWidth(squareSize: number, lineWidth: number | undefined) {\n const candidate = lineWidth ?? squareSize * 0.08;\n return Math.max(2, Math.min(8, candidate));\n}\n\nfunction resolveBorderCoordinateFontSize(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n const maxFontSize = Math.floor(\n Math.min(geometry.squareSize * 0.6, geometry.borderSize * 0.65),\n );\n let fontSize: number | null = null;\n\n for (let candidate = maxFontSize; candidate >= MIN_COORDINATE_FONT_SIZE; candidate -= 1) {\n context.font = `${candidate}px sans-serif`;\n\n const filesFit = geometry.borderFileLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.squareSize * MAX_FILE_LABEL_WIDTH_RATIO &&\n textHeight <= geometry.borderSize * MAX_LABEL_HEIGHT_RATIO\n );\n });\n const ranksFit = geometry.borderRankLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.borderSize * MAX_RANK_LABEL_WIDTH_RATIO &&\n textHeight <= geometry.squareSize * MAX_LABEL_HEIGHT_RATIO\n );\n });\n\n if (filesFit && ranksFit) {\n fontSize = candidate;\n break;\n }\n }\n\n return fontSize;\n}\n\nfunction resolveInsideCoordinateFontSize(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n const maxFontSize = Math.floor(\n geometry.squareSize * INSIDE_COORDINATE_MAX_FONT_RATIO,\n );\n let fontSize: number | null = null;\n\n for (let candidate = maxFontSize; candidate >= MIN_COORDINATE_FONT_SIZE; candidate -= 1) {\n context.font = `${candidate}px sans-serif`;\n\n const filesFit = geometry.insideFileLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.insideLabelMaxWidth &&\n textHeight <= geometry.insideLabelMaxHeight\n );\n });\n const ranksFit = geometry.insideRankLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.insideLabelMaxWidth &&\n textHeight <= geometry.insideLabelMaxHeight\n );\n });\n\n if (filesFit && ranksFit) {\n fontSize = candidate;\n break;\n }\n }\n\n return fontSize;\n}\n\nfunction drawBorderCoordinates(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n if (geometry.borderSize === 0) {\n return;\n }\n\n const fontSize = resolveBorderCoordinateFontSize(context, geometry);\n\n if (fontSize === null) {\n return;\n }\n\n context.fillStyle = request.coordinates.color ?? \"#333\";\n context.font = `${fontSize}px sans-serif`;\n context.textAlign = \"center\";\n context.textBaseline = \"middle\";\n\n for (const label of geometry.borderFileLabels) {\n context.fillText(label.text, label.x, label.y);\n }\n\n for (const label of geometry.borderRankLabels) {\n context.fillText(label.text, label.x, label.y);\n }\n}\n\nfunction drawInsideCoordinates(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n const fontSize = resolveInsideCoordinateFontSize(context, geometry);\n\n if (fontSize === null) {\n return;\n }\n\n context.font = `${fontSize}px sans-serif`;\n\n for (const label of geometry.insideFileLabels) {\n context.fillStyle = resolveInsideLabelColor(request, label.square);\n context.textAlign = label.textAlign;\n context.textBaseline = label.textBaseline;\n context.fillText(label.text, label.x, label.y);\n }\n\n for (const label of geometry.insideRankLabels) {\n context.fillStyle = resolveInsideLabelColor(request, label.square);\n context.textAlign = label.textAlign;\n context.textBaseline = label.textBaseline;\n context.fillText(label.text, label.x, label.y);\n }\n}\n\nfunction drawCoordinates(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n if (!request.coordinates.enabled) {\n return;\n }\n\n if (request.coordinates.position === \"border\") {\n drawBorderCoordinates(context, request, geometry);\n return;\n }\n\n drawInsideCoordinates(context, request, geometry);\n}\n\nfunction drawBoardSquares(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const square of SQUARES) {\n const squareGeometry = geometry.squares[square];\n context.fillStyle = isDarkSquare(square)\n ? request.colors.darkSquare\n : request.colors.lightSquare;\n context.fillRect(\n squareGeometry.x,\n squareGeometry.y,\n squareGeometry.size,\n squareGeometry.size,\n );\n }\n}\n\nfunction drawFillHighlights(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const highlight of request.highlights) {\n if (highlight.style !== \"fill\") {\n continue;\n }\n\n const squareGeometry = geometry.squares[highlight.square];\n context.save();\n context.globalAlpha = resolveHighlightOpacity(\n highlight.style,\n highlight.color,\n highlight.opacity,\n );\n context.fillStyle = highlight.color ?? request.colors.highlight;\n context.fillRect(\n squareGeometry.x,\n squareGeometry.y,\n squareGeometry.size,\n squareGeometry.size,\n );\n context.restore();\n }\n}\n\nfunction drawCircleHighlights(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const highlight of request.highlights) {\n if (highlight.style !== \"circle\") {\n continue;\n }\n\n const squareGeometry = geometry.squares[highlight.square];\n const centerX = squareGeometry.x + squareGeometry.size / 2;\n const centerY = squareGeometry.y + squareGeometry.size / 2;\n context.save();\n context.globalAlpha = resolveHighlightOpacity(\n highlight.style,\n highlight.color,\n highlight.opacity,\n );\n context.strokeStyle = highlight.color ?? \"#ffcc00\";\n context.lineWidth = resolveCircleLineWidth(\n squareGeometry.size,\n highlight.lineWidth,\n );\n context.beginPath();\n context.arc(\n centerX,\n centerY,\n squareGeometry.size * 0.32,\n 0,\n Math.PI * 2,\n );\n context.stroke();\n context.restore();\n }\n}\n\nasync function drawPieces(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const square of SQUARES) {\n const squareGeometry = geometry.squares[square];\n const pieceKey = request.board.squares[square];\n if (!pieceKey) {\n continue;\n }\n\n const raster = await getPieceRaster(\n request.theme.name,\n pieceKey,\n request.theme.pieces[pieceKey],\n Math.round(geometry.squareSize),\n );\n context.drawImage(\n raster,\n squareGeometry.x,\n squareGeometry.y,\n squareGeometry.size,\n squareGeometry.size,\n );\n }\n}\n\nexport class CanvasPngRenderer implements Renderer<Buffer> {\n async render(request: RenderRequest): Promise<Buffer> {\n try {\n const geometry = createBoardGeometry({\n size: request.size,\n padding: request.padding,\n borderSize: request.borderSize,\n flipped: request.flipped,\n });\n\n const canvas = createCanvas(geometry.imageWidth, geometry.imageHeight);\n const context = canvas.getContext(\"2d\");\n\n context.fillStyle = request.colors.lightSquare;\n context.fillRect(0, 0, geometry.imageWidth, geometry.imageHeight);\n drawBoardSquares(context, request, geometry);\n drawFillHighlights(context, request, geometry);\n drawCircleHighlights(context, request, geometry);\n drawCoordinates(context, request, geometry);\n await drawPieces(context, request, geometry);\n\n return canvas.toBuffer(\"image/png\");\n } catch (error) {\n if (error instanceof RenderError) {\n throw error;\n }\n\n throw new RenderError(\"Failed to render chess board\", { cause: error });\n }\n }\n}\n","import type { Padding, Square } from \"../types/types\";\nimport { FILES, RANKS, SQUARES } from \"./board\";\n\nexport interface SquareGeometry {\n x: number;\n y: number;\n size: number;\n}\n\nexport interface CoordinateLabelGeometry {\n text: string;\n x: number;\n y: number;\n square: Square;\n textAlign: \"left\" | \"center\" | \"right\";\n textBaseline: \"top\" | \"middle\" | \"bottom\";\n}\n\nexport interface BoardGeometry {\n imageWidth: number;\n imageHeight: number;\n squareSize: number;\n borderSize: number;\n boardOuterX: number;\n boardOuterY: number;\n boardOuterSize: number;\n boardX: number;\n boardY: number;\n boardSize: number;\n borderFileLabels: CoordinateLabelGeometry[];\n borderRankLabels: CoordinateLabelGeometry[];\n insideFileLabels: CoordinateLabelGeometry[];\n insideRankLabels: CoordinateLabelGeometry[];\n insideFileInsetX: number;\n insideFileInsetY: number;\n insideRankInsetX: number;\n insideRankInsetY: number;\n insideLabelMaxWidth: number;\n insideLabelMaxHeight: number;\n squares: Record<Square, SquareGeometry>;\n}\n\ninterface BoardGeometryOptions {\n size: number;\n padding: Padding;\n borderSize: number;\n flipped: boolean;\n}\n\nexport function createBoardGeometry({\n size,\n padding,\n borderSize,\n flipped,\n}: BoardGeometryOptions): BoardGeometry {\n const [top, right, bottom, left] = padding;\n const boardOuterSize = size;\n const boardOuterX = left;\n const boardOuterY = top;\n const boardX = left + borderSize;\n const boardY = top + borderSize;\n const boardSize = size - borderSize * 2;\n const squareSize = boardSize / 8;\n\n const squares = Object.fromEntries(\n SQUARES.map((square, index) => {\n const fileIndex = index % 8;\n const rankIndex = Math.floor(index / 8);\n\n const x = boardX + (flipped ? 7 - fileIndex : fileIndex) * squareSize;\n const y = boardY + (flipped ? 7 - rankIndex : rankIndex) * squareSize;\n\n return [square, { x, y, size: squareSize }];\n }),\n ) as Record<Square, SquareGeometry>;\n\n const displayedFiles = flipped ? [...FILES].reverse() : [...FILES];\n const displayedRanks = flipped ? [...RANKS].reverse() : [...RANKS];\n const bottomEdgeRank = flipped ? \"8\" : \"1\";\n const leftEdgeFile = flipped ? \"h\" : \"a\";\n const borderFileLabels = borderSize\n ? displayedFiles.map((file, fileIndex) => ({\n text: file,\n x: boardX + fileIndex * squareSize + squareSize / 2,\n y: boardOuterY + boardOuterSize - borderSize / 2,\n square: `${file}${bottomEdgeRank}` as Square,\n textAlign: \"center\" as const,\n textBaseline: \"middle\" as const,\n }))\n : [];\n const borderRankLabels = borderSize\n ? displayedRanks.map((rank, rankIndex) => ({\n text: rank,\n x: boardOuterX + borderSize / 2,\n y: boardY + rankIndex * squareSize + squareSize / 2,\n square: `${leftEdgeFile}${rank}` as Square,\n textAlign: \"center\" as const,\n textBaseline: \"middle\" as const,\n }))\n : [];\n const insideFileInsetX = squareSize * 0.14;\n const insideFileInsetY = squareSize * 0.1;\n const insideRankInsetX = squareSize * 0.14;\n const insideRankInsetY = squareSize * 0.1;\n const insideLabelMaxWidth = squareSize * 0.26;\n const insideLabelMaxHeight = squareSize * 0.24;\n const insideFileLabels = displayedFiles.map((file) => {\n const square = `${file}${bottomEdgeRank}` as Square;\n const squareGeometry = squares[square];\n\n return {\n text: file,\n x: squareGeometry.x + squareGeometry.size - insideFileInsetX,\n y: squareGeometry.y + squareGeometry.size - insideFileInsetY,\n square,\n textAlign: \"right\" as const,\n textBaseline: \"bottom\" as const,\n };\n });\n const insideRankLabels = displayedRanks.map((rank) => {\n const square = `${leftEdgeFile}${rank}` as Square;\n const squareGeometry = squares[square];\n\n return {\n text: rank,\n x: squareGeometry.x + insideRankInsetX,\n y: squareGeometry.y + insideRankInsetY,\n square,\n textAlign: \"left\" as const,\n textBaseline: \"top\" as const,\n };\n });\n\n return {\n imageWidth: left + size + right,\n imageHeight: top + size + bottom,\n squareSize,\n borderSize,\n boardOuterX,\n boardOuterY,\n boardOuterSize,\n boardX,\n boardY,\n boardSize,\n borderFileLabels,\n borderRankLabels,\n insideFileLabels,\n insideRankLabels,\n insideFileInsetX,\n insideFileInsetY,\n insideRankInsetX,\n insideRankInsetY,\n insideLabelMaxWidth,\n insideLabelMaxHeight,\n squares,\n };\n}\n","export function createRasterCacheKey(\n themeName: string,\n pieceKey: string,\n squareSize: number,\n backend: string,\n): string {\n return `${themeName}:${pieceKey}:${squareSize}:${backend}`;\n}\n\nexport class RasterAssetCache<T> {\n private readonly cache = new Map<string, T>();\n\n get(key: string): T | undefined {\n return this.cache.get(key);\n }\n\n set(key: string, value: T): void {\n this.cache.set(key, value);\n }\n}\n\nexport class SourceAssetCache<T> {\n private readonly cache = new Map<string, T>();\n\n get(key: string): T | undefined {\n return this.cache.get(key);\n }\n\n set(key: string, value: T): void {\n this.cache.set(key, value);\n }\n}\n","import { readFile } from \"node:fs/promises\";\nimport { createCanvas, loadImage } from \"canvas\";\nimport { Resvg } from \"@resvg/resvg-js\";\nimport { RenderError } from \"../types/errors\";\nimport type { ThemeAssetSource } from \"../types/types\";\nimport { SourceAssetCache } from \"./asset-cache\";\n\nconst svgSourceCache = new SourceAssetCache<string>();\nconst imageBufferCache = new SourceAssetCache<Buffer>();\n\nasync function readSvgSource(filePath: string): Promise<string> {\n const cached = svgSourceCache.get(filePath);\n\n if (cached) {\n return cached;\n }\n\n try {\n const source = await readFile(filePath, \"utf8\");\n svgSourceCache.set(filePath, source);\n return source;\n } catch (error) {\n throw new RenderError(`Failed to read SVG asset: ${filePath}`, { cause: error });\n }\n}\n\nasync function readBinaryAsset(filePath: string): Promise<Buffer> {\n const cached = imageBufferCache.get(filePath);\n\n if (cached) {\n return cached;\n }\n\n try {\n const source = await readFile(filePath);\n imageBufferCache.set(filePath, source);\n return source;\n } catch (error) {\n throw new RenderError(`Failed to read image asset: ${filePath}`, { cause: error });\n }\n}\n\nasync function rasterizeSvgAsset(filePath: string, squareSize: number) {\n try {\n const svgSource = await readSvgSource(filePath);\n const resvg = new Resvg(svgSource, {\n fitTo: {\n mode: \"width\",\n value: squareSize,\n },\n });\n const pngBuffer = resvg.render().asPng();\n const image = await loadImage(pngBuffer);\n const canvas = createCanvas(squareSize, squareSize);\n const context = canvas.getContext(\"2d\");\n context.drawImage(image, 0, 0, squareSize, squareSize);\n return canvas;\n } catch (error) {\n throw new RenderError(`Failed to rasterize SVG asset: ${filePath}`, { cause: error });\n }\n}\n\nasync function rasterizePngAsset(filePath: string, squareSize: number) {\n try {\n const pngSource = await readBinaryAsset(filePath);\n const image = await loadImage(pngSource);\n const canvas = createCanvas(squareSize, squareSize);\n const context = canvas.getContext(\"2d\");\n context.drawImage(image, 0, 0, squareSize, squareSize);\n return canvas;\n } catch (error) {\n throw new RenderError(`Failed to rasterize PNG asset: ${filePath}`, { cause: error });\n }\n}\n\nexport async function rasterizeThemeAsset(asset: ThemeAssetSource, squareSize: number) {\n if (asset.kind === \"svg\") {\n return rasterizeSvgAsset(asset.source, squareSize);\n }\n\n return rasterizePngAsset(asset.source, squareSize);\n}\n","import { writeFile } from \"node:fs/promises\";\nimport { IOError } from \"../types/errors\";\n\nexport async function writeBufferToFile(filePath: string, buffer: Buffer): Promise<void> {\n try {\n await writeFile(filePath, buffer);\n } catch (error) {\n throw new IOError(`Failed to write file: ${filePath}`, { cause: error });\n }\n}\n","import type { HighlightInput, ResolvedHighlight } from \"../types/types\";\nimport { validateSquare } from \"./validators\";\n\nexport function normalizeHighlightEntries(input: HighlightInput[]): ResolvedHighlight[] {\n return input.map((entry) => {\n if (typeof entry === \"string\") {\n return {\n square: validateSquare(entry),\n style: \"fill\",\n };\n }\n\n const style = entry.style ?? \"fill\";\n\n return {\n square: validateSquare(entry.square),\n style,\n color: entry.color ?? (style === \"circle\" ? \"#ffcc00\" : undefined),\n opacity: entry.opacity ?? (style === \"circle\" ? 0.9 : undefined),\n lineWidth: entry.lineWidth,\n };\n });\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { PieceKey, PieceStyle, ThemeDefinition } from \"../types/types\";\nimport { registerTheme } from \"./registry\";\n\nexport const builtInThemeNames = [\n \"merida\",\n \"alpha\",\n \"cburnett\",\n \"cheq\",\n \"leipzig\",\n] as const satisfies readonly PieceStyle[];\n\nconst PIECE_KEYS: PieceKey[] = [\n \"wK\",\n \"wQ\",\n \"wR\",\n \"wB\",\n \"wN\",\n \"wP\",\n \"bK\",\n \"bQ\",\n \"bR\",\n \"bB\",\n \"bN\",\n \"bP\",\n];\n\nlet initialized = false;\n\nfunction assetPath(themeName: PieceStyle, pieceKey: PieceKey): string {\n const candidates = [\n resolve(__dirname, \"../../assets/themes\", themeName, `${pieceKey}.png`),\n resolve(__dirname, \"../assets/themes\", themeName, `${pieceKey}.png`),\n resolve(process.cwd(), \"assets/themes\", themeName, `${pieceKey}.png`),\n ];\n\n const match = candidates.find((candidate) => existsSync(candidate));\n return match ?? candidates[0];\n}\n\nfunction createBuiltInTheme(themeName: PieceStyle): ThemeDefinition {\n return {\n name: themeName,\n displayName: themeName[0].toUpperCase() + themeName.slice(1),\n license: \"Derived from upstream chess-image-generator resource pack; original pack license not fully verified\",\n attribution:\n \"Derived from andyruwruw/chess-image-generator bundled resources; upstream README cites Marcel van Kervinck as source\",\n pieces: Object.fromEntries(\n PIECE_KEYS.map((pieceKey) => [\n pieceKey,\n {\n kind: \"png\",\n source: assetPath(themeName, pieceKey),\n },\n ]),\n ) as ThemeDefinition[\"pieces\"],\n };\n}\n\nexport function initializeBuiltInThemes(): void {\n if (initialized) {\n return;\n }\n\n for (const themeName of builtInThemeNames) {\n registerTheme(createBuiltInTheme(themeName));\n }\n\n initialized = true;\n}\n\nexport function resetBuiltInThemesForTesting(): void {\n initialized = false;\n}\n","import { validateThemeName } from \"../core/validators\";\nimport { ThemeError, ValidationError } from \"../types/errors\";\nimport type { PieceKey, ThemeDefinition } from \"../types/types\";\n\nconst REQUIRED_PIECES: PieceKey[] = [\n \"wK\",\n \"wQ\",\n \"wR\",\n \"wB\",\n \"wN\",\n \"wP\",\n \"bK\",\n \"bQ\",\n \"bR\",\n \"bB\",\n \"bN\",\n \"bP\",\n];\n\nexport function validateThemeDefinition(theme: ThemeDefinition): ThemeDefinition {\n const normalizedName = validateThemeName(theme.name);\n\n if (!theme.displayName.trim()) {\n throw new ValidationError(\"Theme displayName is required\");\n }\n\n if (!theme.license.trim()) {\n throw new ValidationError(\"Theme license is required\");\n }\n\n if (!theme.attribution.trim()) {\n throw new ValidationError(\"Theme attribution is required\");\n }\n\n for (const pieceKey of REQUIRED_PIECES) {\n const asset = theme.pieces[pieceKey];\n\n if (!asset || (asset.kind !== \"svg\" && asset.kind !== \"png\") || !asset.source.trim()) {\n throw new ThemeError(`Theme \"${normalizedName}\" is missing asset ${pieceKey}`);\n }\n }\n\n return {\n ...theme,\n name: normalizedName,\n };\n}\n","import type { ThemeDefinition } from \"../types/types\";\nimport { ThemeError } from \"../types/errors\";\nimport { validateThemeDefinition } from \"./validation\";\n\nconst registry = new Map<string, ThemeDefinition>();\n\nexport function registerTheme(theme: ThemeDefinition): ThemeDefinition {\n const validatedTheme = validateThemeDefinition(theme);\n\n if (registry.has(validatedTheme.name)) {\n throw new ThemeError(`Theme \"${validatedTheme.name}\" is already registered`);\n }\n\n registry.set(validatedTheme.name, validatedTheme);\n return validatedTheme;\n}\n\nexport function getTheme(name: string): ThemeDefinition | undefined {\n return registry.get(name.trim().toLowerCase());\n}\n\nexport function clearThemeRegistryForTesting(): void {\n registry.clear();\n}\n","import type { PieceStyle, ThemeDefinition } from \"../types/types\";\nimport { ThemeError, ValidationError } from \"../types/errors\";\nimport { initializeBuiltInThemes } from \"./builtins\";\nimport { getTheme } from \"./registry\";\nimport { validateThemeDefinition } from \"./validation\";\n\ninterface ResolveThemeOptions {\n theme?: string | ThemeDefinition;\n style?: PieceStyle;\n}\n\nexport function resolveTheme({ theme, style }: ResolveThemeOptions): ThemeDefinition {\n initializeBuiltInThemes();\n\n if (typeof theme === \"object\" && theme !== null) {\n try {\n return validateThemeDefinition(theme);\n } catch (error) {\n if (error instanceof ThemeError) {\n throw error;\n }\n\n if (error instanceof ValidationError) {\n throw new ThemeError(error.message, { cause: error });\n }\n\n throw error;\n }\n }\n\n const requestedName = typeof theme === \"string\" ? theme : style ?? \"merida\";\n const resolvedTheme = getTheme(requestedName);\n\n if (!resolvedTheme) {\n throw new ThemeError(`Unknown theme: ${requestedName}`);\n }\n\n return resolvedTheme;\n}\n","import type {\n BoardColors,\n CoordinatesInput,\n CoordinatesPosition,\n HighlightInput,\n Padding,\n RenderOptions,\n ResolvedColors,\n ResolvedCoordinates,\n ResolvedHighlight,\n ResolvedRenderOptions,\n ThemeDefinition,\n} from \"../types/types\";\nimport { normalizeHighlightEntries as normalizeCanonicalHighlightEntries } from \"../core/highlights\";\nimport {\n normalizePadding,\n validateBoardColors,\n validateBorderSize,\n validateCoordinatesOption,\n validateHighlightsInput,\n validateSize,\n} from \"../core/validators\";\nimport { resolveTheme } from \"../themes/resolver\";\n\nexport const DEFAULT_SIZE = 480;\nexport const DEFAULT_PADDING: Padding = [0, 0, 0, 0];\nexport const DEFAULT_BORDER_SIZE = 0;\nexport const DEFAULT_COLORS: ResolvedColors = {\n lightSquare: \"#f0d9b5\",\n darkSquare: \"#b58863\",\n highlight: \"rgba(255, 206, 0, 0.45)\",\n};\nexport const DEFAULT_COORDINATES: ResolvedCoordinates = {\n enabled: false,\n position: \"inside\",\n};\n\nexport function normalizeColors(colors?: BoardColors): ResolvedColors {\n return {\n lightSquare: colors?.lightSquare ?? DEFAULT_COLORS.lightSquare,\n darkSquare: colors?.darkSquare ?? DEFAULT_COLORS.darkSquare,\n highlight: colors?.highlight ?? DEFAULT_COLORS.highlight,\n };\n}\n\nexport function normalizeCoordinates(\n coordinates: CoordinatesInput | undefined,\n borderSize: number,\n): ResolvedCoordinates {\n if (coordinates === undefined || coordinates === false) {\n return { ...DEFAULT_COORDINATES };\n }\n\n const defaultPosition: CoordinatesPosition =\n borderSize > 0 ? \"border\" : \"inside\";\n\n if (coordinates === true) {\n return {\n enabled: true,\n position: defaultPosition,\n color: defaultPosition === \"border\" ? \"#333\" : undefined,\n };\n }\n\n if (coordinates === \"border\" || coordinates === \"inside\") {\n return {\n enabled: true,\n position: coordinates,\n color: coordinates === \"border\" ? \"#333\" : undefined,\n };\n }\n\n const position = coordinates.position ?? defaultPosition;\n\n return {\n enabled: coordinates.enabled ?? true,\n position,\n color: coordinates.color ?? (position === \"border\" ? \"#333\" : undefined),\n };\n}\n\nexport function normalizeHighlightEntries(\n highlights: HighlightInput[] | undefined,\n): ResolvedHighlight[] {\n return normalizeCanonicalHighlightEntries(highlights ?? []);\n}\n\nexport function normalizeRenderInputs(\n options: RenderOptions,\n): ResolvedRenderOptions {\n const size = validateSize(options.size ?? DEFAULT_SIZE);\n const borderSize = validateBorderSize(\n options.borderSize ?? DEFAULT_BORDER_SIZE,\n size,\n );\n validateBoardColors(options.colors);\n validateCoordinatesOption(options.coordinates, borderSize);\n validateHighlightsInput(options.highlights, options.highlightSquares);\n const highlightInput = options.highlights ?? options.highlightSquares;\n\n return {\n size,\n padding: normalizePadding(options.padding ?? DEFAULT_PADDING),\n borderSize,\n flipped: options.flipped ?? false,\n theme: resolveTheme({\n theme: options.theme,\n style: options.style,\n }),\n highlights: normalizeHighlightEntries(highlightInput),\n colors: normalizeColors(options.colors),\n coordinates: normalizeCoordinates(options.coordinates, borderSize),\n };\n}\n","import { parseBoardArray, parseFEN, parsePGN } from \"../core/parsers\";\nimport { validateHighlightsInput } from \"../core/validators\";\nimport { ValidationError } from \"../types/errors\";\nimport type {\n BoardArray,\n ChessImageGeneratorOptions,\n HighlightInput,\n RenderOptions,\n} from \"../types/types\";\nimport { CanvasPngRenderer } from \"../render/canvas-renderer\";\nimport { writeBufferToFile } from \"../utils/io\";\nimport { normalizeRenderInputs } from \"../utils/normalization\";\nimport type { BoardPosition } from \"../core/board\";\n\nexport class ChessImageGenerator {\n private position: BoardPosition | null = null;\n\n private readonly defaults: RenderOptions;\n\n private highlights: HighlightInput[] = [];\n\n constructor(options: ChessImageGeneratorOptions = {}) {\n this.defaults = { ...options };\n normalizeRenderInputs(this.defaults);\n }\n\n async loadFEN(fen: string): Promise<void> {\n this.position = parseFEN(fen);\n this.clearHighlights();\n }\n\n async loadPGN(pgn: string): Promise<void> {\n this.position = parsePGN(pgn);\n this.clearHighlights();\n }\n\n async loadBoard(board: BoardArray): Promise<void> {\n this.position = parseBoardArray(board);\n this.clearHighlights();\n }\n\n setHighlights(highlights: HighlightInput[]): void {\n validateHighlightsInput(highlights, undefined);\n this.highlights = [...highlights];\n }\n\n clearHighlights(): void {\n this.highlights = [];\n }\n\n async toBuffer(): Promise<Buffer> {\n if (!this.position) {\n throw new ValidationError(\"No board position loaded\");\n }\n\n const renderer = new CanvasPngRenderer();\n const normalized = normalizeRenderInputs({\n ...this.defaults,\n highlights: this.highlights,\n highlightSquares: undefined,\n });\n\n return renderer.render({\n board: this.position,\n theme: normalized.theme,\n highlights: normalized.highlights,\n size: normalized.size,\n padding: normalized.padding,\n borderSize: normalized.borderSize,\n flipped: normalized.flipped,\n colors: normalized.colors,\n coordinates: normalized.coordinates,\n });\n }\n\n async toFile(filePath: string): Promise<void> {\n const buffer = await this.toBuffer();\n await writeBufferToFile(filePath, buffer);\n }\n}\n","import { parseBoardArray, parseFEN, parsePGN } from \"../core/parsers\";\nimport { ValidationError } from \"../types/errors\";\nimport type { RenderChessOptions } from \"../types/types\";\nimport { CanvasPngRenderer } from \"../render/canvas-renderer\";\nimport { normalizeRenderInputs } from \"../utils/normalization\";\n\nfunction parseInputPosition(options: RenderChessOptions) {\n if (typeof options.fen === \"string\") {\n return parseFEN(options.fen);\n }\n\n if (typeof options.pgn === \"string\") {\n return parsePGN(options.pgn);\n }\n\n if (Array.isArray(options.board)) {\n return parseBoardArray(options.board);\n }\n\n throw new ValidationError(\"Exactly one of fen, pgn, or board must be provided\");\n}\n\nexport async function renderChess(options: RenderChessOptions): Promise<Buffer> {\n const provided = [\n typeof options.fen === \"string\" ? options.fen : undefined,\n typeof options.pgn === \"string\" ? options.pgn : undefined,\n Array.isArray(options.board) ? options.board : undefined,\n ].filter((value) => value !== undefined);\n\n if (provided.length !== 1) {\n throw new ValidationError(\"Exactly one of fen, pgn, or board must be provided\");\n }\n\n const position = parseInputPosition(options);\n const renderer = new CanvasPngRenderer();\n const normalized = normalizeRenderInputs(options);\n\n return renderer.render({\n board: position,\n theme: normalized.theme,\n highlights: normalized.highlights,\n size: normalized.size,\n padding: normalized.padding,\n borderSize: normalized.borderSize,\n flipped: normalized.flipped,\n colors: normalized.colors,\n coordinates: normalized.coordinates,\n });\n}\n"],"mappings":"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","names":["createCanvas","createCanvas","createCanvas","createCanvas","normalizeHighlightEntries"]}
1
+ {"version":3,"sources":["../node_modules/tsup/assets/esm_shims.js","../src/types/errors.ts","../src/core/parsers.ts","../src/core/board.ts","../src/core/validators.ts","../src/render/canvas-renderer.ts","../src/core/geometry.ts","../src/render/asset-cache.ts","../src/render/rasterizer.ts","../src/utils/io.ts","../src/core/highlights.ts","../src/themes/builtins.ts","../src/themes/validation.ts","../src/themes/registry.ts","../src/themes/resolver.ts","../src/utils/normalization.ts","../src/api/class-api.ts","../src/api/functional-api.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","export class ValidationError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ValidationError\";\n }\n}\n\nexport class ParseError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ParseError\";\n }\n}\n\nexport class ThemeError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ThemeError\";\n }\n}\n\nexport class RenderError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"RenderError\";\n }\n}\n\nexport class IOError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"IOError\";\n }\n}\n","import { Chess } from \"chess.js\";\nimport type { BoardArray, BoardCell, PieceKey } from \"../types/types\";\nimport { ParseError, ValidationError } from \"../types/errors\";\nimport { createEmptyBoardPosition, FILES } from \"./board\";\nimport { validateBoardArray } from \"./validators\";\n\nconst PIECE_SYMBOL_TO_KEY: Record<string, PieceKey> = {\n K: \"wK\",\n Q: \"wQ\",\n R: \"wR\",\n B: \"wB\",\n N: \"wN\",\n P: \"wP\",\n k: \"bK\",\n q: \"bQ\",\n r: \"bR\",\n b: \"bB\",\n n: \"bN\",\n p: \"bP\",\n};\n\nfunction chessBoardToBoardArray(board: ReturnType<Chess[\"board\"]>): BoardArray {\n return board.map((rank) =>\n rank.map((piece): BoardCell => {\n if (!piece) {\n return null;\n }\n\n return piece.color === \"w\" ? piece.type.toUpperCase() : piece.type;\n }),\n );\n}\n\nexport function parseFEN(fen: string) {\n const chess = new Chess();\n\n try {\n chess.load(fen);\n } catch (error) {\n throw new ParseError(\"Invalid FEN\", { cause: error });\n }\n\n return parseBoardArray(chessBoardToBoardArray(chess.board()));\n}\n\nexport function parsePGN(pgn: string) {\n const chess = new Chess();\n\n try {\n chess.loadPgn(pgn);\n } catch (error) {\n throw new ParseError(\"Invalid PGN\", { cause: error });\n }\n\n return parseBoardArray(chessBoardToBoardArray(chess.board()));\n}\n\nexport function parseBoardArray(board: BoardArray) {\n const validatedBoard = validateBoardArray(board);\n const position = createEmptyBoardPosition();\n\n validatedBoard.forEach((rank, rankIndex) => {\n rank.forEach((cell, fileIndex) => {\n if (cell === null) {\n return;\n }\n\n const pieceKey = PIECE_SYMBOL_TO_KEY[cell];\n\n if (!pieceKey) {\n throw new ValidationError(`Invalid board piece: ${cell}`);\n }\n\n const square = `${FILES[fileIndex]}${8 - rankIndex}` as keyof typeof position.squares;\n position.squares[square] = pieceKey;\n });\n });\n\n return position;\n}\n","import type { PieceKey, Square } from \"../types/types\";\n\nexport const FILES = [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"] as const;\nexport const RANKS = [\"8\", \"7\", \"6\", \"5\", \"4\", \"3\", \"2\", \"1\"] as const;\n\nexport const SQUARES: Square[] = RANKS.flatMap((rank) =>\n FILES.map((file) => `${file}${rank}`),\n);\n\nexport interface BoardPosition {\n squares: Record<Square, PieceKey | null>;\n}\n\nexport function createEmptyBoardPosition(): BoardPosition {\n return {\n squares: Object.fromEntries(\n SQUARES.map((square) => [square, null]),\n ) as Record<Square, PieceKey | null>,\n };\n}\n","import { createCanvas } from \"canvas\";\nimport type {\n BoardArray,\n BoardCell,\n BoardColors,\n CoordinatesInput,\n CoordinatesOptions,\n CoordinatesPosition,\n HighlightInput,\n HighlightOptions,\n HighlightStyle,\n Padding,\n Square,\n} from \"../types/types\";\nimport { ValidationError } from \"../types/errors\";\n\nconst SQUARE_PATTERN = /^[a-h][1-8]$/;\nconst PIECE_PATTERN = /^[prnbqkPRNBQK]$/;\nconst BUILT_IN_THEME_PATTERN = /^(merida|alpha|cburnett|cheq|leipzig)$/;\nconst colorValidationContext = createCanvas(1, 1).getContext(\"2d\");\n\nexport function validateSize(size: number): number {\n if (!Number.isFinite(size) || size <= 0) {\n throw new ValidationError(`Invalid board size: ${size}`);\n }\n\n return Math.round(size);\n}\n\nexport function normalizePadding(padding?: number[] | Padding): Padding {\n const candidate = padding ?? [0, 0, 0, 0];\n\n if (\n !Array.isArray(candidate) ||\n candidate.length !== 4 ||\n candidate.some((value) => !Number.isFinite(value) || value < 0)\n ) {\n throw new ValidationError(\"Padding must be a 4-item array of non-negative numbers\");\n }\n\n return [\n Math.round(candidate[0]),\n Math.round(candidate[1]),\n Math.round(candidate[2]),\n Math.round(candidate[3]),\n ];\n}\n\nexport function validateSquare(square: string): Square {\n const normalized = square.trim().toLowerCase();\n\n if (!SQUARE_PATTERN.test(normalized)) {\n throw new ValidationError(`Invalid square: ${square}`);\n }\n\n return normalized;\n}\n\nexport function validateBoardCell(cell: BoardCell): BoardCell {\n if (cell === null || cell === \"\" || cell === \" \") {\n return null;\n }\n\n if (typeof cell !== \"string\" || !PIECE_PATTERN.test(cell)) {\n throw new ValidationError(`Invalid board piece: ${String(cell)}`);\n }\n\n return cell;\n}\n\nexport function validateBoardArray(board: BoardArray): BoardArray {\n if (!Array.isArray(board) || board.length !== 8) {\n throw new ValidationError(\"Board array must have exactly 8 ranks\");\n }\n\n return board.map((rank, rankIndex) => {\n if (!Array.isArray(rank) || rank.length !== 8) {\n throw new ValidationError(`Board rank ${rankIndex} must contain exactly 8 files`);\n }\n\n return rank.map(validateBoardCell);\n });\n}\n\nexport function validateStyleName(style: string): string {\n const normalized = style.trim().toLowerCase();\n\n if (!BUILT_IN_THEME_PATTERN.test(normalized)) {\n throw new ValidationError(`Unknown built-in style: ${style}`);\n }\n\n return normalized;\n}\n\nexport function validateThemeName(name: string): string {\n const normalized = name.trim().toLowerCase();\n\n if (!/^[a-z0-9-]+$/.test(normalized)) {\n throw new ValidationError(`Invalid theme name: ${name}`);\n }\n\n return normalized;\n}\n\nexport function validateBorderSize(borderSize: number, size: number): number {\n const normalizedSize = validateSize(size);\n const normalizedBorderSize = Math.round(borderSize);\n const maxBorderSize = Math.floor(normalizedSize / 8);\n\n if (!Number.isFinite(borderSize) || normalizedBorderSize < 0) {\n throw new ValidationError(`Invalid borderSize: ${borderSize}`);\n }\n\n if (normalizedBorderSize > maxBorderSize) {\n throw new ValidationError(\n `Invalid borderSize: ${borderSize}. Maximum allowed for size ${normalizedSize} is ${maxBorderSize}`,\n );\n }\n\n return normalizedBorderSize;\n}\n\nexport function validateColorString(color: string, label = \"color\"): string {\n if (typeof color !== \"string\" || color.trim() === \"\") {\n throw new ValidationError(`Invalid ${label}: ${String(color)}`);\n }\n\n const normalized = color.trim();\n colorValidationContext.fillStyle = \"#010203\";\n colorValidationContext.fillStyle = normalized;\n const firstPass = String(colorValidationContext.fillStyle);\n colorValidationContext.fillStyle = \"#fefefe\";\n colorValidationContext.fillStyle = normalized;\n const secondPass = String(colorValidationContext.fillStyle);\n\n if (firstPass !== secondPass) {\n throw new ValidationError(`Invalid ${label}: ${color}`);\n }\n\n return normalized;\n}\n\nexport function validateBoardColors(colors?: BoardColors): void {\n if (!colors) {\n return;\n }\n\n if (colors.lightSquare !== undefined) {\n validateColorString(colors.lightSquare, \"lightSquare color\");\n }\n\n if (colors.darkSquare !== undefined) {\n validateColorString(colors.darkSquare, \"darkSquare color\");\n }\n\n if (colors.highlight !== undefined) {\n validateColorString(colors.highlight, \"highlight color\");\n }\n}\n\nfunction isCoordinatesOptions(value: unknown): value is CoordinatesOptions {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isCoordinatesPosition(value: unknown): value is CoordinatesPosition {\n return value === \"border\" || value === \"inside\";\n}\n\nfunction isHighlightOptions(value: unknown): value is HighlightOptions {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isHighlightStyle(value: unknown): value is HighlightStyle {\n return value === \"fill\" || value === \"circle\";\n}\n\nexport function validateCoordinatesOption(\n coordinates: CoordinatesInput | undefined,\n borderSize: number,\n): void {\n if (coordinates === undefined || typeof coordinates === \"boolean\") {\n return;\n }\n\n if (isCoordinatesPosition(coordinates)) {\n if (coordinates === \"border\" && borderSize === 0) {\n throw new ValidationError(\n \"coordinates position 'border' requires borderSize > 0\",\n );\n }\n\n return;\n }\n\n if (!isCoordinatesOptions(coordinates)) {\n throw new ValidationError(\n \"coordinates must be false, true, 'border', 'inside', or an options object\",\n );\n }\n\n if (\n coordinates.enabled !== undefined &&\n typeof coordinates.enabled !== \"boolean\"\n ) {\n throw new ValidationError(\"coordinates.enabled must be a boolean\");\n }\n\n if (\n coordinates.position !== undefined &&\n !isCoordinatesPosition(coordinates.position)\n ) {\n throw new ValidationError(\"coordinates.position must be 'border' or 'inside'\");\n }\n\n if (\n coordinates.enabled !== false &&\n coordinates.position === \"border\" &&\n borderSize === 0\n ) {\n throw new ValidationError(\n \"coordinates position 'border' requires borderSize > 0\",\n );\n }\n\n if (coordinates.color !== undefined) {\n validateColorString(coordinates.color, \"coordinates.color\");\n }\n}\n\nfunction validateHighlightEntry(entry: HighlightInput): void {\n if (typeof entry === \"string\") {\n validateSquare(entry);\n return;\n }\n\n if (!isHighlightOptions(entry)) {\n throw new ValidationError(\"highlights entries must be square strings or highlight objects\");\n }\n\n if (typeof entry.square !== \"string\") {\n throw new ValidationError(\"highlight.square must be a valid algebraic square\");\n }\n\n validateSquare(entry.square);\n\n if (entry.style !== undefined && !isHighlightStyle(entry.style)) {\n throw new ValidationError(\"highlight.style must be 'fill' or 'circle'\");\n }\n\n if (entry.color !== undefined) {\n validateColorString(entry.color, \"highlight.color\");\n }\n\n if (\n entry.opacity !== undefined &&\n (!Number.isFinite(entry.opacity) || entry.opacity < 0 || entry.opacity > 1)\n ) {\n throw new ValidationError(\"highlight.opacity must be a finite number between 0 and 1\");\n }\n\n if (\n entry.lineWidth !== undefined &&\n (!Number.isFinite(entry.lineWidth) || entry.lineWidth <= 0)\n ) {\n throw new ValidationError(\"highlight.lineWidth must be a finite number greater than 0\");\n }\n\n if (\n entry.radius !== undefined &&\n (!Number.isFinite(entry.radius) || entry.radius <= 0 || entry.radius > 0.5)\n ) {\n throw new ValidationError(\"highlight.radius must be a finite number greater than 0 and at most 0.5\");\n }\n}\n\nexport function validateHighlightOptions(highlights: HighlightInput[] | undefined): void {\n if (highlights === undefined) {\n return;\n }\n\n if (!Array.isArray(highlights)) {\n throw new ValidationError(\"highlights must be an array\");\n }\n\n for (const entry of highlights) {\n validateHighlightEntry(entry);\n }\n}\n\nexport function validateHighlightsInput(\n highlights: HighlightInput[] | undefined,\n highlightSquares: HighlightInput[] | undefined,\n): void {\n if (highlights !== undefined && highlightSquares !== undefined) {\n throw new ValidationError(\"Use either highlights or highlightSquares, not both\");\n }\n\n validateHighlightOptions(highlights);\n validateHighlightOptions(highlightSquares);\n}\n","import { createCanvas } from \"canvas\";\nimport { SQUARES } from \"../core/board\";\nimport { createBoardGeometry } from \"../core/geometry\";\nimport { RenderError } from \"../types/errors\";\nimport type { ThemeAssetSource } from \"../types/types\";\nimport { createRasterCacheKey, RasterAssetCache } from \"./asset-cache\";\nimport type { RenderRequest, Renderer } from \"./renderer\";\nimport { rasterizeThemeAsset } from \"./rasterizer\";\n\nconst pieceRasterCache = new RasterAssetCache<Awaited<ReturnType<typeof rasterizeThemeAsset>>>();\nconst MIN_COORDINATE_FONT_SIZE = 8;\nconst MAX_FILE_LABEL_WIDTH_RATIO = 0.75;\nconst MAX_RANK_LABEL_WIDTH_RATIO = 0.7;\nconst MAX_LABEL_HEIGHT_RATIO = 0.7;\nconst INSIDE_COORDINATE_MAX_FONT_RATIO = 0.34;\nconst INSIDE_LIGHT_LABEL_COLOR = \"rgba(255,255,255,0.6)\";\nconst INSIDE_DARK_LABEL_COLOR = \"rgba(0,0,0,0.45)\";\n\nfunction isDarkSquare(square: string): boolean {\n const fileIndex = square.charCodeAt(0) - 97;\n const rankNumber = Number(square[1]);\n return (fileIndex + rankNumber) % 2 === 1;\n}\n\nasync function getPieceRaster(\n themeName: string,\n pieceKey: string,\n asset: ThemeAssetSource,\n squareSize: number,\n) {\n const cacheKey = createRasterCacheKey(themeName, pieceKey, squareSize, \"png-canvas\");\n const cached = pieceRasterCache.get(cacheKey);\n\n if (cached) {\n return cached;\n }\n\n const raster = await rasterizeThemeAsset(asset, squareSize);\n pieceRasterCache.set(cacheKey, raster);\n return raster;\n}\n\nfunction resolveInsideLabelColor(\n request: RenderRequest,\n square: string,\n) {\n if (request.coordinates.color) {\n return request.coordinates.color;\n }\n\n return isDarkSquare(square) ? INSIDE_LIGHT_LABEL_COLOR : INSIDE_DARK_LABEL_COLOR;\n}\n\nfunction resolveHighlightOpacity(style: \"fill\" | \"circle\", color: string | undefined, opacity: number | undefined) {\n if (opacity !== undefined) {\n return opacity;\n }\n\n if (style === \"circle\" || color !== undefined) {\n return 0.9;\n }\n\n // Preserve legacy fill behavior when the board highlight color is used directly.\n return 1;\n}\n\nfunction resolveCircleLineWidth(squareSize: number, lineWidth: number | undefined) {\n const candidate = lineWidth ?? squareSize * 0.08;\n return Math.max(2, Math.min(8, candidate));\n}\n\nfunction resolveCircleRadius(\n squareSize: number,\n radius: number | undefined,\n lineWidth: number,\n) {\n const radiusPx = squareSize * (radius ?? 0.42);\n return Math.max(0, radiusPx - lineWidth / 2);\n}\n\nfunction resolveBorderCoordinateFontSize(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n const maxFontSize = Math.floor(\n Math.min(geometry.squareSize * 0.6, geometry.borderSize * 0.65),\n );\n let fontSize: number | null = null;\n\n for (let candidate = maxFontSize; candidate >= MIN_COORDINATE_FONT_SIZE; candidate -= 1) {\n context.font = `${candidate}px sans-serif`;\n\n const filesFit = geometry.borderFileLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.squareSize * MAX_FILE_LABEL_WIDTH_RATIO &&\n textHeight <= geometry.borderSize * MAX_LABEL_HEIGHT_RATIO\n );\n });\n const ranksFit = geometry.borderRankLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.borderSize * MAX_RANK_LABEL_WIDTH_RATIO &&\n textHeight <= geometry.squareSize * MAX_LABEL_HEIGHT_RATIO\n );\n });\n\n if (filesFit && ranksFit) {\n fontSize = candidate;\n break;\n }\n }\n\n return fontSize;\n}\n\nfunction resolveInsideCoordinateFontSize(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n const maxFontSize = Math.floor(\n geometry.squareSize * INSIDE_COORDINATE_MAX_FONT_RATIO,\n );\n let fontSize: number | null = null;\n\n for (let candidate = maxFontSize; candidate >= MIN_COORDINATE_FONT_SIZE; candidate -= 1) {\n context.font = `${candidate}px sans-serif`;\n\n const filesFit = geometry.insideFileLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.insideLabelMaxWidth &&\n textHeight <= geometry.insideLabelMaxHeight\n );\n });\n const ranksFit = geometry.insideRankLabels.every((label) => {\n const metrics = context.measureText(label.text);\n const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;\n\n return (\n metrics.width <= geometry.insideLabelMaxWidth &&\n textHeight <= geometry.insideLabelMaxHeight\n );\n });\n\n if (filesFit && ranksFit) {\n fontSize = candidate;\n break;\n }\n }\n\n return fontSize;\n}\n\nfunction drawBorderCoordinates(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n if (geometry.borderSize === 0) {\n return;\n }\n\n const fontSize = resolveBorderCoordinateFontSize(context, geometry);\n\n if (fontSize === null) {\n return;\n }\n\n context.fillStyle = request.coordinates.color ?? \"#333\";\n context.font = `${fontSize}px sans-serif`;\n context.textAlign = \"center\";\n context.textBaseline = \"middle\";\n\n for (const label of geometry.borderFileLabels) {\n context.fillText(label.text, label.x, label.y);\n }\n\n for (const label of geometry.borderRankLabels) {\n context.fillText(label.text, label.x, label.y);\n }\n}\n\nfunction drawInsideCoordinates(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n const fontSize = resolveInsideCoordinateFontSize(context, geometry);\n\n if (fontSize === null) {\n return;\n }\n\n context.font = `${fontSize}px sans-serif`;\n\n for (const label of geometry.insideFileLabels) {\n context.fillStyle = resolveInsideLabelColor(request, label.square);\n context.textAlign = label.textAlign;\n context.textBaseline = label.textBaseline;\n context.fillText(label.text, label.x, label.y);\n }\n\n for (const label of geometry.insideRankLabels) {\n context.fillStyle = resolveInsideLabelColor(request, label.square);\n context.textAlign = label.textAlign;\n context.textBaseline = label.textBaseline;\n context.fillText(label.text, label.x, label.y);\n }\n}\n\nfunction drawCoordinates(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n if (!request.coordinates.enabled) {\n return;\n }\n\n if (request.coordinates.position === \"border\") {\n drawBorderCoordinates(context, request, geometry);\n return;\n }\n\n drawInsideCoordinates(context, request, geometry);\n}\n\nfunction drawBoardSquares(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const square of SQUARES) {\n const squareGeometry = geometry.squares[square];\n context.fillStyle = isDarkSquare(square)\n ? request.colors.darkSquare\n : request.colors.lightSquare;\n context.fillRect(\n squareGeometry.x,\n squareGeometry.y,\n squareGeometry.size,\n squareGeometry.size,\n );\n }\n}\n\nfunction drawFillHighlights(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const highlight of request.highlights) {\n if (highlight.style !== \"fill\") {\n continue;\n }\n\n const squareGeometry = geometry.squares[highlight.square];\n context.save();\n context.globalAlpha = resolveHighlightOpacity(\n highlight.style,\n highlight.color,\n highlight.opacity,\n );\n context.fillStyle = highlight.color ?? request.colors.highlight;\n context.fillRect(\n squareGeometry.x,\n squareGeometry.y,\n squareGeometry.size,\n squareGeometry.size,\n );\n context.restore();\n }\n}\n\nfunction drawCircleHighlights(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const highlight of request.highlights) {\n if (highlight.style !== \"circle\") {\n continue;\n }\n\n const squareGeometry = geometry.squares[highlight.square];\n const centerX = squareGeometry.x + squareGeometry.size / 2;\n const centerY = squareGeometry.y + squareGeometry.size / 2;\n context.save();\n context.globalAlpha = resolveHighlightOpacity(\n highlight.style,\n highlight.color,\n highlight.opacity,\n );\n context.strokeStyle = highlight.color ?? \"#ffcc00\";\n context.lineWidth = resolveCircleLineWidth(\n squareGeometry.size,\n highlight.lineWidth,\n );\n const radius = resolveCircleRadius(\n squareGeometry.size,\n highlight.radius,\n context.lineWidth,\n );\n context.beginPath();\n context.arc(\n centerX,\n centerY,\n radius,\n 0,\n Math.PI * 2,\n );\n context.stroke();\n context.restore();\n }\n}\n\nasync function drawPieces(\n context: ReturnType<typeof createCanvas>[\"getContext\"],\n request: RenderRequest,\n geometry: ReturnType<typeof createBoardGeometry>,\n) {\n for (const square of SQUARES) {\n const squareGeometry = geometry.squares[square];\n const pieceKey = request.board.squares[square];\n if (!pieceKey) {\n continue;\n }\n\n const raster = await getPieceRaster(\n request.theme.name,\n pieceKey,\n request.theme.pieces[pieceKey],\n Math.round(geometry.squareSize),\n );\n context.drawImage(\n raster,\n squareGeometry.x,\n squareGeometry.y,\n squareGeometry.size,\n squareGeometry.size,\n );\n }\n}\n\nexport class CanvasPngRenderer implements Renderer<Buffer> {\n async render(request: RenderRequest): Promise<Buffer> {\n try {\n const geometry = createBoardGeometry({\n size: request.size,\n padding: request.padding,\n borderSize: request.borderSize,\n flipped: request.flipped,\n });\n\n const canvas = createCanvas(geometry.imageWidth, geometry.imageHeight);\n const context = canvas.getContext(\"2d\");\n\n context.fillStyle = request.colors.lightSquare;\n context.fillRect(0, 0, geometry.imageWidth, geometry.imageHeight);\n drawBoardSquares(context, request, geometry);\n drawFillHighlights(context, request, geometry);\n drawCircleHighlights(context, request, geometry);\n drawCoordinates(context, request, geometry);\n await drawPieces(context, request, geometry);\n\n return canvas.toBuffer(\"image/png\");\n } catch (error) {\n if (error instanceof RenderError) {\n throw error;\n }\n\n throw new RenderError(\"Failed to render chess board\", { cause: error });\n }\n }\n}\n","import type { Padding, Square } from \"../types/types\";\nimport { FILES, RANKS, SQUARES } from \"./board\";\n\nexport interface SquareGeometry {\n x: number;\n y: number;\n size: number;\n}\n\nexport interface CoordinateLabelGeometry {\n text: string;\n x: number;\n y: number;\n square: Square;\n textAlign: \"left\" | \"center\" | \"right\";\n textBaseline: \"top\" | \"middle\" | \"bottom\";\n}\n\nexport interface BoardGeometry {\n imageWidth: number;\n imageHeight: number;\n squareSize: number;\n borderSize: number;\n boardOuterX: number;\n boardOuterY: number;\n boardOuterSize: number;\n boardX: number;\n boardY: number;\n boardSize: number;\n borderFileLabels: CoordinateLabelGeometry[];\n borderRankLabels: CoordinateLabelGeometry[];\n insideFileLabels: CoordinateLabelGeometry[];\n insideRankLabels: CoordinateLabelGeometry[];\n insideFileInsetX: number;\n insideFileInsetY: number;\n insideRankInsetX: number;\n insideRankInsetY: number;\n insideLabelMaxWidth: number;\n insideLabelMaxHeight: number;\n squares: Record<Square, SquareGeometry>;\n}\n\ninterface BoardGeometryOptions {\n size: number;\n padding: Padding;\n borderSize: number;\n flipped: boolean;\n}\n\nexport function createBoardGeometry({\n size,\n padding,\n borderSize,\n flipped,\n}: BoardGeometryOptions): BoardGeometry {\n const [top, right, bottom, left] = padding;\n const boardOuterSize = size;\n const boardOuterX = left;\n const boardOuterY = top;\n const boardX = left + borderSize;\n const boardY = top + borderSize;\n const boardSize = size - borderSize * 2;\n const squareSize = boardSize / 8;\n\n const squares = Object.fromEntries(\n SQUARES.map((square, index) => {\n const fileIndex = index % 8;\n const rankIndex = Math.floor(index / 8);\n\n const x = boardX + (flipped ? 7 - fileIndex : fileIndex) * squareSize;\n const y = boardY + (flipped ? 7 - rankIndex : rankIndex) * squareSize;\n\n return [square, { x, y, size: squareSize }];\n }),\n ) as Record<Square, SquareGeometry>;\n\n const displayedFiles = flipped ? [...FILES].reverse() : [...FILES];\n const displayedRanks = flipped ? [...RANKS].reverse() : [...RANKS];\n const bottomEdgeRank = flipped ? \"8\" : \"1\";\n const leftEdgeFile = flipped ? \"h\" : \"a\";\n const borderFileLabels = borderSize\n ? displayedFiles.map((file, fileIndex) => ({\n text: file,\n x: boardX + fileIndex * squareSize + squareSize / 2,\n y: boardOuterY + boardOuterSize - borderSize / 2,\n square: `${file}${bottomEdgeRank}` as Square,\n textAlign: \"center\" as const,\n textBaseline: \"middle\" as const,\n }))\n : [];\n const borderRankLabels = borderSize\n ? displayedRanks.map((rank, rankIndex) => ({\n text: rank,\n x: boardOuterX + borderSize / 2,\n y: boardY + rankIndex * squareSize + squareSize / 2,\n square: `${leftEdgeFile}${rank}` as Square,\n textAlign: \"center\" as const,\n textBaseline: \"middle\" as const,\n }))\n : [];\n const insideFileInsetX = squareSize * 0.14;\n const insideFileInsetY = squareSize * 0.1;\n const insideRankInsetX = squareSize * 0.14;\n const insideRankInsetY = squareSize * 0.1;\n const insideLabelMaxWidth = squareSize * 0.26;\n const insideLabelMaxHeight = squareSize * 0.24;\n const insideFileLabels = displayedFiles.map((file) => {\n const square = `${file}${bottomEdgeRank}` as Square;\n const squareGeometry = squares[square];\n\n return {\n text: file,\n x: squareGeometry.x + squareGeometry.size - insideFileInsetX,\n y: squareGeometry.y + squareGeometry.size - insideFileInsetY,\n square,\n textAlign: \"right\" as const,\n textBaseline: \"bottom\" as const,\n };\n });\n const insideRankLabels = displayedRanks.map((rank) => {\n const square = `${leftEdgeFile}${rank}` as Square;\n const squareGeometry = squares[square];\n\n return {\n text: rank,\n x: squareGeometry.x + insideRankInsetX,\n y: squareGeometry.y + insideRankInsetY,\n square,\n textAlign: \"left\" as const,\n textBaseline: \"top\" as const,\n };\n });\n\n return {\n imageWidth: left + size + right,\n imageHeight: top + size + bottom,\n squareSize,\n borderSize,\n boardOuterX,\n boardOuterY,\n boardOuterSize,\n boardX,\n boardY,\n boardSize,\n borderFileLabels,\n borderRankLabels,\n insideFileLabels,\n insideRankLabels,\n insideFileInsetX,\n insideFileInsetY,\n insideRankInsetX,\n insideRankInsetY,\n insideLabelMaxWidth,\n insideLabelMaxHeight,\n squares,\n };\n}\n","export function createRasterCacheKey(\n themeName: string,\n pieceKey: string,\n squareSize: number,\n backend: string,\n): string {\n return `${themeName}:${pieceKey}:${squareSize}:${backend}`;\n}\n\nexport class RasterAssetCache<T> {\n private readonly cache = new Map<string, T>();\n\n get(key: string): T | undefined {\n return this.cache.get(key);\n }\n\n set(key: string, value: T): void {\n this.cache.set(key, value);\n }\n}\n\nexport class SourceAssetCache<T> {\n private readonly cache = new Map<string, T>();\n\n get(key: string): T | undefined {\n return this.cache.get(key);\n }\n\n set(key: string, value: T): void {\n this.cache.set(key, value);\n }\n}\n","import { readFile } from \"node:fs/promises\";\nimport { createCanvas, loadImage } from \"canvas\";\nimport { Resvg } from \"@resvg/resvg-js\";\nimport { RenderError } from \"../types/errors\";\nimport type { ThemeAssetSource } from \"../types/types\";\nimport { SourceAssetCache } from \"./asset-cache\";\n\nconst svgSourceCache = new SourceAssetCache<string>();\nconst imageBufferCache = new SourceAssetCache<Buffer>();\n\nasync function readSvgSource(filePath: string): Promise<string> {\n const cached = svgSourceCache.get(filePath);\n\n if (cached) {\n return cached;\n }\n\n try {\n const source = await readFile(filePath, \"utf8\");\n svgSourceCache.set(filePath, source);\n return source;\n } catch (error) {\n throw new RenderError(`Failed to read SVG asset: ${filePath}`, { cause: error });\n }\n}\n\nasync function readBinaryAsset(filePath: string): Promise<Buffer> {\n const cached = imageBufferCache.get(filePath);\n\n if (cached) {\n return cached;\n }\n\n try {\n const source = await readFile(filePath);\n imageBufferCache.set(filePath, source);\n return source;\n } catch (error) {\n throw new RenderError(`Failed to read image asset: ${filePath}`, { cause: error });\n }\n}\n\nasync function rasterizeSvgAsset(filePath: string, squareSize: number) {\n try {\n const svgSource = await readSvgSource(filePath);\n const resvg = new Resvg(svgSource, {\n fitTo: {\n mode: \"width\",\n value: squareSize,\n },\n });\n const pngBuffer = resvg.render().asPng();\n const image = await loadImage(pngBuffer);\n const canvas = createCanvas(squareSize, squareSize);\n const context = canvas.getContext(\"2d\");\n context.drawImage(image, 0, 0, squareSize, squareSize);\n return canvas;\n } catch (error) {\n throw new RenderError(`Failed to rasterize SVG asset: ${filePath}`, { cause: error });\n }\n}\n\nasync function rasterizePngAsset(filePath: string, squareSize: number) {\n try {\n const pngSource = await readBinaryAsset(filePath);\n const image = await loadImage(pngSource);\n const canvas = createCanvas(squareSize, squareSize);\n const context = canvas.getContext(\"2d\");\n context.drawImage(image, 0, 0, squareSize, squareSize);\n return canvas;\n } catch (error) {\n throw new RenderError(`Failed to rasterize PNG asset: ${filePath}`, { cause: error });\n }\n}\n\nexport async function rasterizeThemeAsset(asset: ThemeAssetSource, squareSize: number) {\n if (asset.kind === \"svg\") {\n return rasterizeSvgAsset(asset.source, squareSize);\n }\n\n return rasterizePngAsset(asset.source, squareSize);\n}\n","import { writeFile } from \"node:fs/promises\";\nimport { IOError } from \"../types/errors\";\n\nexport async function writeBufferToFile(filePath: string, buffer: Buffer): Promise<void> {\n try {\n await writeFile(filePath, buffer);\n } catch (error) {\n throw new IOError(`Failed to write file: ${filePath}`, { cause: error });\n }\n}\n","import type { HighlightInput, ResolvedHighlight } from \"../types/types\";\nimport { validateSquare } from \"./validators\";\n\nexport function normalizeHighlightEntries(input: HighlightInput[]): ResolvedHighlight[] {\n return input.map((entry) => {\n if (typeof entry === \"string\") {\n return {\n square: validateSquare(entry),\n style: \"fill\",\n color: undefined,\n opacity: undefined,\n lineWidth: undefined,\n radius: undefined,\n };\n }\n\n const style = entry.style ?? \"fill\";\n\n return {\n square: validateSquare(entry.square),\n style,\n color: entry.color ?? (style === \"circle\" ? \"#ffcc00\" : undefined),\n opacity: entry.opacity ?? (style === \"circle\" ? 0.9 : undefined),\n lineWidth: entry.lineWidth,\n radius: style === \"circle\" ? (entry.radius ?? 0.42) : undefined,\n };\n });\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { PieceKey, PieceStyle, ThemeDefinition } from \"../types/types\";\nimport { registerTheme } from \"./registry\";\n\nexport const builtInThemeNames = [\n \"merida\",\n \"alpha\",\n \"cburnett\",\n \"cheq\",\n \"leipzig\",\n] as const satisfies readonly PieceStyle[];\n\nconst PIECE_KEYS: PieceKey[] = [\n \"wK\",\n \"wQ\",\n \"wR\",\n \"wB\",\n \"wN\",\n \"wP\",\n \"bK\",\n \"bQ\",\n \"bR\",\n \"bB\",\n \"bN\",\n \"bP\",\n];\n\nlet initialized = false;\n\nfunction assetPath(themeName: PieceStyle, pieceKey: PieceKey): string {\n const candidates = [\n resolve(__dirname, \"../../assets/themes\", themeName, `${pieceKey}.png`),\n resolve(__dirname, \"../assets/themes\", themeName, `${pieceKey}.png`),\n resolve(process.cwd(), \"assets/themes\", themeName, `${pieceKey}.png`),\n ];\n\n const match = candidates.find((candidate) => existsSync(candidate));\n return match ?? candidates[0];\n}\n\nfunction createBuiltInTheme(themeName: PieceStyle): ThemeDefinition {\n return {\n name: themeName,\n displayName: themeName[0].toUpperCase() + themeName.slice(1),\n license: \"Derived from upstream chess-image-generator resource pack; original pack license not fully verified\",\n attribution:\n \"Derived from andyruwruw/chess-image-generator bundled resources; upstream README cites Marcel van Kervinck as source\",\n pieces: Object.fromEntries(\n PIECE_KEYS.map((pieceKey) => [\n pieceKey,\n {\n kind: \"png\",\n source: assetPath(themeName, pieceKey),\n },\n ]),\n ) as ThemeDefinition[\"pieces\"],\n };\n}\n\nexport function initializeBuiltInThemes(): void {\n if (initialized) {\n return;\n }\n\n for (const themeName of builtInThemeNames) {\n registerTheme(createBuiltInTheme(themeName));\n }\n\n initialized = true;\n}\n\nexport function resetBuiltInThemesForTesting(): void {\n initialized = false;\n}\n","import { validateThemeName } from \"../core/validators\";\nimport { ThemeError, ValidationError } from \"../types/errors\";\nimport type { PieceKey, ThemeDefinition } from \"../types/types\";\n\nconst REQUIRED_PIECES: PieceKey[] = [\n \"wK\",\n \"wQ\",\n \"wR\",\n \"wB\",\n \"wN\",\n \"wP\",\n \"bK\",\n \"bQ\",\n \"bR\",\n \"bB\",\n \"bN\",\n \"bP\",\n];\n\nexport function validateThemeDefinition(theme: ThemeDefinition): ThemeDefinition {\n const normalizedName = validateThemeName(theme.name);\n\n if (!theme.displayName.trim()) {\n throw new ValidationError(\"Theme displayName is required\");\n }\n\n if (!theme.license.trim()) {\n throw new ValidationError(\"Theme license is required\");\n }\n\n if (!theme.attribution.trim()) {\n throw new ValidationError(\"Theme attribution is required\");\n }\n\n for (const pieceKey of REQUIRED_PIECES) {\n const asset = theme.pieces[pieceKey];\n\n if (!asset || (asset.kind !== \"svg\" && asset.kind !== \"png\") || !asset.source.trim()) {\n throw new ThemeError(`Theme \"${normalizedName}\" is missing asset ${pieceKey}`);\n }\n }\n\n return {\n ...theme,\n name: normalizedName,\n };\n}\n","import type { ThemeDefinition } from \"../types/types\";\nimport { ThemeError } from \"../types/errors\";\nimport { validateThemeDefinition } from \"./validation\";\n\nconst registry = new Map<string, ThemeDefinition>();\n\nexport function registerTheme(theme: ThemeDefinition): ThemeDefinition {\n const validatedTheme = validateThemeDefinition(theme);\n\n if (registry.has(validatedTheme.name)) {\n throw new ThemeError(`Theme \"${validatedTheme.name}\" is already registered`);\n }\n\n registry.set(validatedTheme.name, validatedTheme);\n return validatedTheme;\n}\n\nexport function getTheme(name: string): ThemeDefinition | undefined {\n return registry.get(name.trim().toLowerCase());\n}\n\nexport function clearThemeRegistryForTesting(): void {\n registry.clear();\n}\n","import type { PieceStyle, ThemeDefinition } from \"../types/types\";\nimport { ThemeError, ValidationError } from \"../types/errors\";\nimport { initializeBuiltInThemes } from \"./builtins\";\nimport { getTheme } from \"./registry\";\nimport { validateThemeDefinition } from \"./validation\";\n\ninterface ResolveThemeOptions {\n theme?: string | ThemeDefinition;\n style?: PieceStyle;\n}\n\nexport function resolveTheme({ theme, style }: ResolveThemeOptions): ThemeDefinition {\n initializeBuiltInThemes();\n\n if (typeof theme === \"object\" && theme !== null) {\n try {\n return validateThemeDefinition(theme);\n } catch (error) {\n if (error instanceof ThemeError) {\n throw error;\n }\n\n if (error instanceof ValidationError) {\n throw new ThemeError(error.message, { cause: error });\n }\n\n throw error;\n }\n }\n\n const requestedName = typeof theme === \"string\" ? theme : style ?? \"merida\";\n const resolvedTheme = getTheme(requestedName);\n\n if (!resolvedTheme) {\n throw new ThemeError(`Unknown theme: ${requestedName}`);\n }\n\n return resolvedTheme;\n}\n","import type {\n BoardColors,\n CoordinatesInput,\n CoordinatesPosition,\n HighlightInput,\n Padding,\n RenderOptions,\n ResolvedColors,\n ResolvedCoordinates,\n ResolvedHighlight,\n ResolvedRenderOptions,\n ThemeDefinition,\n} from \"../types/types\";\nimport { normalizeHighlightEntries as normalizeCanonicalHighlightEntries } from \"../core/highlights\";\nimport {\n normalizePadding,\n validateBoardColors,\n validateBorderSize,\n validateCoordinatesOption,\n validateHighlightsInput,\n validateSize,\n} from \"../core/validators\";\nimport { resolveTheme } from \"../themes/resolver\";\n\nexport const DEFAULT_SIZE = 480;\nexport const DEFAULT_PADDING: Padding = [0, 0, 0, 0];\nexport const DEFAULT_BORDER_SIZE = 0;\nexport const DEFAULT_COLORS: ResolvedColors = {\n lightSquare: \"#f0d9b5\",\n darkSquare: \"#b58863\",\n highlight: \"rgba(255, 206, 0, 0.45)\",\n};\nexport const DEFAULT_COORDINATES: ResolvedCoordinates = {\n enabled: false,\n position: \"inside\",\n};\n\nexport function normalizeColors(colors?: BoardColors): ResolvedColors {\n return {\n lightSquare: colors?.lightSquare ?? DEFAULT_COLORS.lightSquare,\n darkSquare: colors?.darkSquare ?? DEFAULT_COLORS.darkSquare,\n highlight: colors?.highlight ?? DEFAULT_COLORS.highlight,\n };\n}\n\nexport function normalizeCoordinates(\n coordinates: CoordinatesInput | undefined,\n borderSize: number,\n): ResolvedCoordinates {\n if (coordinates === undefined || coordinates === false) {\n return { ...DEFAULT_COORDINATES };\n }\n\n const defaultPosition: CoordinatesPosition =\n borderSize > 0 ? \"border\" : \"inside\";\n\n if (coordinates === true) {\n return {\n enabled: true,\n position: defaultPosition,\n color: defaultPosition === \"border\" ? \"#333\" : undefined,\n };\n }\n\n if (coordinates === \"border\" || coordinates === \"inside\") {\n return {\n enabled: true,\n position: coordinates,\n color: coordinates === \"border\" ? \"#333\" : undefined,\n };\n }\n\n const position = coordinates.position ?? defaultPosition;\n\n return {\n enabled: coordinates.enabled ?? true,\n position,\n color: coordinates.color ?? (position === \"border\" ? \"#333\" : undefined),\n };\n}\n\nexport function normalizeHighlightEntries(\n highlights: HighlightInput[] | undefined,\n): ResolvedHighlight[] {\n return normalizeCanonicalHighlightEntries(highlights ?? []);\n}\n\nexport function normalizeRenderInputs(\n options: RenderOptions,\n): ResolvedRenderOptions {\n const size = validateSize(options.size ?? DEFAULT_SIZE);\n const borderSize = validateBorderSize(\n options.borderSize ?? DEFAULT_BORDER_SIZE,\n size,\n );\n validateBoardColors(options.colors);\n validateCoordinatesOption(options.coordinates, borderSize);\n validateHighlightsInput(options.highlights, options.highlightSquares);\n const highlightInput = options.highlights ?? options.highlightSquares;\n\n return {\n size,\n padding: normalizePadding(options.padding ?? DEFAULT_PADDING),\n borderSize,\n flipped: options.flipped ?? false,\n theme: resolveTheme({\n theme: options.theme,\n style: options.style,\n }),\n highlights: normalizeHighlightEntries(highlightInput),\n colors: normalizeColors(options.colors),\n coordinates: normalizeCoordinates(options.coordinates, borderSize),\n };\n}\n","import { parseBoardArray, parseFEN, parsePGN } from \"../core/parsers\";\nimport { validateHighlightsInput } from \"../core/validators\";\nimport { ValidationError } from \"../types/errors\";\nimport type {\n BoardArray,\n ChessImageGeneratorOptions,\n HighlightInput,\n RenderOptions,\n} from \"../types/types\";\nimport { CanvasPngRenderer } from \"../render/canvas-renderer\";\nimport { writeBufferToFile } from \"../utils/io\";\nimport { normalizeRenderInputs } from \"../utils/normalization\";\nimport type { BoardPosition } from \"../core/board\";\n\nexport class ChessImageGenerator {\n private position: BoardPosition | null = null;\n\n private readonly defaults: RenderOptions;\n\n private highlights: HighlightInput[] = [];\n\n constructor(options: ChessImageGeneratorOptions = {}) {\n this.defaults = { ...options };\n normalizeRenderInputs(this.defaults);\n }\n\n async loadFEN(fen: string): Promise<void> {\n this.position = parseFEN(fen);\n this.clearHighlights();\n }\n\n async loadPGN(pgn: string): Promise<void> {\n this.position = parsePGN(pgn);\n this.clearHighlights();\n }\n\n async loadBoard(board: BoardArray): Promise<void> {\n this.position = parseBoardArray(board);\n this.clearHighlights();\n }\n\n setHighlights(highlights: HighlightInput[]): void {\n validateHighlightsInput(highlights, undefined);\n this.highlights = [...highlights];\n }\n\n clearHighlights(): void {\n this.highlights = [];\n }\n\n async toBuffer(): Promise<Buffer> {\n if (!this.position) {\n throw new ValidationError(\"No board position loaded\");\n }\n\n const renderer = new CanvasPngRenderer();\n const normalized = normalizeRenderInputs({\n ...this.defaults,\n highlights: this.highlights,\n highlightSquares: undefined,\n });\n\n return renderer.render({\n board: this.position,\n theme: normalized.theme,\n highlights: normalized.highlights,\n size: normalized.size,\n padding: normalized.padding,\n borderSize: normalized.borderSize,\n flipped: normalized.flipped,\n colors: normalized.colors,\n coordinates: normalized.coordinates,\n });\n }\n\n async toFile(filePath: string): Promise<void> {\n const buffer = await this.toBuffer();\n await writeBufferToFile(filePath, buffer);\n }\n}\n","import { parseBoardArray, parseFEN, parsePGN } from \"../core/parsers\";\nimport { ValidationError } from \"../types/errors\";\nimport type { RenderChessOptions } from \"../types/types\";\nimport { CanvasPngRenderer } from \"../render/canvas-renderer\";\nimport { normalizeRenderInputs } from \"../utils/normalization\";\n\nfunction parseInputPosition(options: RenderChessOptions) {\n if (typeof options.fen === \"string\") {\n return parseFEN(options.fen);\n }\n\n if (typeof options.pgn === \"string\") {\n return parsePGN(options.pgn);\n }\n\n if (Array.isArray(options.board)) {\n return parseBoardArray(options.board);\n }\n\n throw new ValidationError(\"Exactly one of fen, pgn, or board must be provided\");\n}\n\nexport async function renderChess(options: RenderChessOptions): Promise<Buffer> {\n const provided = [\n typeof options.fen === \"string\" ? options.fen : undefined,\n typeof options.pgn === \"string\" ? options.pgn : undefined,\n Array.isArray(options.board) ? options.board : undefined,\n ].filter((value) => value !== undefined);\n\n if (provided.length !== 1) {\n throw new ValidationError(\"Exactly one of fen, pgn, or board must be provided\");\n }\n\n const position = parseInputPosition(options);\n const renderer = new CanvasPngRenderer();\n const normalized = normalizeRenderInputs(options);\n\n return renderer.render({\n board: position,\n theme: normalized.theme,\n highlights: normalized.highlights,\n size: normalized.size,\n padding: normalized.padding,\n borderSize: normalized.borderSize,\n flipped: normalized.flipped,\n colors: normalized.colors,\n coordinates: normalized.coordinates,\n });\n}\n"],"mappings":"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","names":["createCanvas","createCanvas","createCanvas","createCanvas","normalizeHighlightEntries"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chess2img",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Modern chess board image rendering for Node.js",
5
5
  "license": "MIT",
6
6
  "repository": {