chess2img 0.1.0

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.
Files changed (69) hide show
  1. package/ATTRIBUTION.md +19 -0
  2. package/README.md +209 -0
  3. package/assets/themes/alpha/bB.svg +20 -0
  4. package/assets/themes/alpha/bK.svg +21 -0
  5. package/assets/themes/alpha/bN.svg +19 -0
  6. package/assets/themes/alpha/bP.svg +19 -0
  7. package/assets/themes/alpha/bQ.svg +22 -0
  8. package/assets/themes/alpha/bR.svg +22 -0
  9. package/assets/themes/alpha/wB.svg +20 -0
  10. package/assets/themes/alpha/wK.svg +21 -0
  11. package/assets/themes/alpha/wN.svg +19 -0
  12. package/assets/themes/alpha/wP.svg +19 -0
  13. package/assets/themes/alpha/wQ.svg +22 -0
  14. package/assets/themes/alpha/wR.svg +22 -0
  15. package/assets/themes/cburnett/bB.svg +20 -0
  16. package/assets/themes/cburnett/bK.svg +21 -0
  17. package/assets/themes/cburnett/bN.svg +19 -0
  18. package/assets/themes/cburnett/bP.svg +19 -0
  19. package/assets/themes/cburnett/bQ.svg +22 -0
  20. package/assets/themes/cburnett/bR.svg +22 -0
  21. package/assets/themes/cburnett/wB.svg +20 -0
  22. package/assets/themes/cburnett/wK.svg +21 -0
  23. package/assets/themes/cburnett/wN.svg +19 -0
  24. package/assets/themes/cburnett/wP.svg +19 -0
  25. package/assets/themes/cburnett/wQ.svg +22 -0
  26. package/assets/themes/cburnett/wR.svg +22 -0
  27. package/assets/themes/cheq/bB.svg +20 -0
  28. package/assets/themes/cheq/bK.svg +21 -0
  29. package/assets/themes/cheq/bN.svg +19 -0
  30. package/assets/themes/cheq/bP.svg +19 -0
  31. package/assets/themes/cheq/bQ.svg +22 -0
  32. package/assets/themes/cheq/bR.svg +22 -0
  33. package/assets/themes/cheq/wB.svg +20 -0
  34. package/assets/themes/cheq/wK.svg +21 -0
  35. package/assets/themes/cheq/wN.svg +19 -0
  36. package/assets/themes/cheq/wP.svg +19 -0
  37. package/assets/themes/cheq/wQ.svg +22 -0
  38. package/assets/themes/cheq/wR.svg +22 -0
  39. package/assets/themes/leipzig/bB.svg +20 -0
  40. package/assets/themes/leipzig/bK.svg +21 -0
  41. package/assets/themes/leipzig/bN.svg +19 -0
  42. package/assets/themes/leipzig/bP.svg +19 -0
  43. package/assets/themes/leipzig/bQ.svg +22 -0
  44. package/assets/themes/leipzig/bR.svg +22 -0
  45. package/assets/themes/leipzig/wB.svg +20 -0
  46. package/assets/themes/leipzig/wK.svg +21 -0
  47. package/assets/themes/leipzig/wN.svg +19 -0
  48. package/assets/themes/leipzig/wP.svg +19 -0
  49. package/assets/themes/leipzig/wQ.svg +22 -0
  50. package/assets/themes/leipzig/wR.svg +22 -0
  51. package/assets/themes/merida/bB.svg +20 -0
  52. package/assets/themes/merida/bK.svg +21 -0
  53. package/assets/themes/merida/bN.svg +19 -0
  54. package/assets/themes/merida/bP.svg +19 -0
  55. package/assets/themes/merida/bQ.svg +22 -0
  56. package/assets/themes/merida/bR.svg +22 -0
  57. package/assets/themes/merida/wB.svg +20 -0
  58. package/assets/themes/merida/wK.svg +21 -0
  59. package/assets/themes/merida/wN.svg +19 -0
  60. package/assets/themes/merida/wP.svg +19 -0
  61. package/assets/themes/merida/wQ.svg +22 -0
  62. package/assets/themes/merida/wR.svg +22 -0
  63. package/dist/index.cjs +653 -0
  64. package/dist/index.cjs.map +1 -0
  65. package/dist/index.d.cts +108 -0
  66. package/dist/index.d.ts +108 -0
  67. package/dist/index.js +626 -0
  68. package/dist/index.js.map +1 -0
  69. package/package.json +52 -0
package/dist/index.js ADDED
@@ -0,0 +1,626 @@
1
+ // node_modules/tsup/assets/esm_shims.js
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ var getFilename = () => fileURLToPath(import.meta.url);
5
+ var getDirname = () => path.dirname(getFilename());
6
+ var __dirname = /* @__PURE__ */ getDirname();
7
+
8
+ // src/types/errors.ts
9
+ var ValidationError = class extends Error {
10
+ constructor(message, options) {
11
+ super(message, options);
12
+ this.name = "ValidationError";
13
+ }
14
+ };
15
+ var ParseError = class extends Error {
16
+ constructor(message, options) {
17
+ super(message, options);
18
+ this.name = "ParseError";
19
+ }
20
+ };
21
+ var ThemeError = class extends Error {
22
+ constructor(message, options) {
23
+ super(message, options);
24
+ this.name = "ThemeError";
25
+ }
26
+ };
27
+ var RenderError = class extends Error {
28
+ constructor(message, options) {
29
+ super(message, options);
30
+ this.name = "RenderError";
31
+ }
32
+ };
33
+ var IOError = class extends Error {
34
+ constructor(message, options) {
35
+ super(message, options);
36
+ this.name = "IOError";
37
+ }
38
+ };
39
+
40
+ // src/core/parsers.ts
41
+ import { Chess } from "chess.js";
42
+
43
+ // src/core/board.ts
44
+ var FILES = ["a", "b", "c", "d", "e", "f", "g", "h"];
45
+ var RANKS = ["8", "7", "6", "5", "4", "3", "2", "1"];
46
+ var SQUARES = RANKS.flatMap(
47
+ (rank) => FILES.map((file) => `${file}${rank}`)
48
+ );
49
+ function createEmptyBoardPosition() {
50
+ return {
51
+ squares: Object.fromEntries(
52
+ SQUARES.map((square) => [square, null])
53
+ )
54
+ };
55
+ }
56
+
57
+ // src/core/validators.ts
58
+ var SQUARE_PATTERN = /^[a-h][1-8]$/;
59
+ var PIECE_PATTERN = /^[prnbqkPRNBQK]$/;
60
+ function validateSize(size) {
61
+ if (!Number.isFinite(size) || size <= 0) {
62
+ throw new ValidationError(`Invalid board size: ${size}`);
63
+ }
64
+ return Math.round(size);
65
+ }
66
+ function normalizePadding(padding) {
67
+ const candidate = padding ?? [0, 0, 0, 0];
68
+ if (!Array.isArray(candidate) || candidate.length !== 4 || candidate.some((value) => !Number.isFinite(value) || value < 0)) {
69
+ throw new ValidationError("Padding must be a 4-item array of non-negative numbers");
70
+ }
71
+ return [
72
+ Math.round(candidate[0]),
73
+ Math.round(candidate[1]),
74
+ Math.round(candidate[2]),
75
+ Math.round(candidate[3])
76
+ ];
77
+ }
78
+ function validateSquare(square) {
79
+ const normalized = square.trim().toLowerCase();
80
+ if (!SQUARE_PATTERN.test(normalized)) {
81
+ throw new ValidationError(`Invalid square: ${square}`);
82
+ }
83
+ return normalized;
84
+ }
85
+ function validateBoardCell(cell) {
86
+ if (cell === null || cell === "" || cell === " ") {
87
+ return null;
88
+ }
89
+ if (typeof cell !== "string" || !PIECE_PATTERN.test(cell)) {
90
+ throw new ValidationError(`Invalid board piece: ${String(cell)}`);
91
+ }
92
+ return cell;
93
+ }
94
+ function validateBoardArray(board) {
95
+ if (!Array.isArray(board) || board.length !== 8) {
96
+ throw new ValidationError("Board array must have exactly 8 ranks");
97
+ }
98
+ return board.map((rank, rankIndex) => {
99
+ if (!Array.isArray(rank) || rank.length !== 8) {
100
+ throw new ValidationError(`Board rank ${rankIndex} must contain exactly 8 files`);
101
+ }
102
+ return rank.map(validateBoardCell);
103
+ });
104
+ }
105
+ function validateThemeName(name) {
106
+ const normalized = name.trim().toLowerCase();
107
+ if (!/^[a-z0-9-]+$/.test(normalized)) {
108
+ throw new ValidationError(`Invalid theme name: ${name}`);
109
+ }
110
+ return normalized;
111
+ }
112
+
113
+ // src/core/parsers.ts
114
+ var PIECE_SYMBOL_TO_KEY = {
115
+ K: "wK",
116
+ Q: "wQ",
117
+ R: "wR",
118
+ B: "wB",
119
+ N: "wN",
120
+ P: "wP",
121
+ k: "bK",
122
+ q: "bQ",
123
+ r: "bR",
124
+ b: "bB",
125
+ n: "bN",
126
+ p: "bP"
127
+ };
128
+ function chessBoardToBoardArray(board) {
129
+ return board.map(
130
+ (rank) => rank.map((piece) => {
131
+ if (!piece) {
132
+ return null;
133
+ }
134
+ return piece.color === "w" ? piece.type.toUpperCase() : piece.type;
135
+ })
136
+ );
137
+ }
138
+ function parseFEN(fen) {
139
+ const chess = new Chess();
140
+ try {
141
+ chess.load(fen);
142
+ } catch (error) {
143
+ throw new ParseError("Invalid FEN", { cause: error });
144
+ }
145
+ return parseBoardArray(chessBoardToBoardArray(chess.board()));
146
+ }
147
+ function parsePGN(pgn) {
148
+ const chess = new Chess();
149
+ try {
150
+ chess.loadPgn(pgn);
151
+ } catch (error) {
152
+ throw new ParseError("Invalid PGN", { cause: error });
153
+ }
154
+ return parseBoardArray(chessBoardToBoardArray(chess.board()));
155
+ }
156
+ function parseBoardArray(board) {
157
+ const validatedBoard = validateBoardArray(board);
158
+ const position = createEmptyBoardPosition();
159
+ validatedBoard.forEach((rank, rankIndex) => {
160
+ rank.forEach((cell, fileIndex) => {
161
+ if (cell === null) {
162
+ return;
163
+ }
164
+ const pieceKey = PIECE_SYMBOL_TO_KEY[cell];
165
+ if (!pieceKey) {
166
+ throw new ValidationError(`Invalid board piece: ${cell}`);
167
+ }
168
+ const square = `${FILES[fileIndex]}${8 - rankIndex}`;
169
+ position.squares[square] = pieceKey;
170
+ });
171
+ });
172
+ return position;
173
+ }
174
+
175
+ // src/core/highlights.ts
176
+ function normalizeHighlights(input) {
177
+ return [...new Set(input.map(validateSquare))].sort();
178
+ }
179
+
180
+ // src/render/canvas-renderer.ts
181
+ import { createCanvas as createCanvas2 } from "canvas";
182
+
183
+ // src/core/geometry.ts
184
+ function createBoardGeometry({
185
+ size,
186
+ padding,
187
+ flipped
188
+ }) {
189
+ const [top, right, bottom, left] = padding;
190
+ const squareSize = size / 8;
191
+ const squares = Object.fromEntries(
192
+ SQUARES.map((square, index) => {
193
+ const fileIndex = index % 8;
194
+ const rankIndex = Math.floor(index / 8);
195
+ const x = left + (flipped ? 7 - fileIndex : fileIndex) * squareSize;
196
+ const y = top + (flipped ? 7 - rankIndex : rankIndex) * squareSize;
197
+ return [square, { x, y, size: squareSize }];
198
+ })
199
+ );
200
+ return {
201
+ imageWidth: left + size + right,
202
+ imageHeight: top + size + bottom,
203
+ squareSize,
204
+ boardX: left,
205
+ boardY: top,
206
+ boardSize: size,
207
+ squares
208
+ };
209
+ }
210
+
211
+ // src/render/asset-cache.ts
212
+ function createRasterCacheKey(themeName, pieceKey, squareSize, backend) {
213
+ return `${themeName}:${pieceKey}:${squareSize}:${backend}`;
214
+ }
215
+ var RasterAssetCache = class {
216
+ cache = /* @__PURE__ */ new Map();
217
+ get(key) {
218
+ return this.cache.get(key);
219
+ }
220
+ set(key, value) {
221
+ this.cache.set(key, value);
222
+ }
223
+ };
224
+ var SourceAssetCache = class {
225
+ cache = /* @__PURE__ */ new Map();
226
+ get(key) {
227
+ return this.cache.get(key);
228
+ }
229
+ set(key, value) {
230
+ this.cache.set(key, value);
231
+ }
232
+ };
233
+
234
+ // src/render/rasterizer.ts
235
+ import { readFile } from "fs/promises";
236
+ import { createCanvas, loadImage } from "canvas";
237
+ import { Resvg } from "@resvg/resvg-js";
238
+ var svgSourceCache = new SourceAssetCache();
239
+ async function readSvgSource(filePath) {
240
+ const cached = svgSourceCache.get(filePath);
241
+ if (cached) {
242
+ return cached;
243
+ }
244
+ try {
245
+ const source = await readFile(filePath, "utf8");
246
+ svgSourceCache.set(filePath, source);
247
+ return source;
248
+ } catch (error) {
249
+ throw new RenderError(`Failed to read SVG asset: ${filePath}`, { cause: error });
250
+ }
251
+ }
252
+ async function rasterizeSvgAsset(filePath, squareSize) {
253
+ try {
254
+ const svgSource = await readSvgSource(filePath);
255
+ const resvg = new Resvg(svgSource, {
256
+ fitTo: {
257
+ mode: "width",
258
+ value: squareSize
259
+ }
260
+ });
261
+ const pngBuffer = resvg.render().asPng();
262
+ const image = await loadImage(pngBuffer);
263
+ const canvas = createCanvas(squareSize, squareSize);
264
+ const context = canvas.getContext("2d");
265
+ context.drawImage(image, 0, 0, squareSize, squareSize);
266
+ return canvas;
267
+ } catch (error) {
268
+ throw new RenderError(`Failed to rasterize SVG asset: ${filePath}`, { cause: error });
269
+ }
270
+ }
271
+
272
+ // src/render/canvas-renderer.ts
273
+ var pieceRasterCache = new RasterAssetCache();
274
+ function isDarkSquare(square) {
275
+ const fileIndex = square.charCodeAt(0) - 97;
276
+ const rankNumber = Number(square[1]);
277
+ return (fileIndex + rankNumber) % 2 === 1;
278
+ }
279
+ async function getPieceRaster(themeName, pieceKey, assetPath2, squareSize) {
280
+ const cacheKey = createRasterCacheKey(themeName, pieceKey, squareSize, "png-canvas");
281
+ const cached = pieceRasterCache.get(cacheKey);
282
+ if (cached) {
283
+ return cached;
284
+ }
285
+ const raster = await rasterizeSvgAsset(assetPath2, squareSize);
286
+ pieceRasterCache.set(cacheKey, raster);
287
+ return raster;
288
+ }
289
+ var CanvasPngRenderer = class {
290
+ async render(request) {
291
+ try {
292
+ const geometry = createBoardGeometry({
293
+ size: request.size,
294
+ padding: request.padding,
295
+ flipped: request.flipped
296
+ });
297
+ const canvas = createCanvas2(geometry.imageWidth, geometry.imageHeight);
298
+ const context = canvas.getContext("2d");
299
+ context.fillStyle = request.colors.lightSquare;
300
+ context.fillRect(0, 0, geometry.imageWidth, geometry.imageHeight);
301
+ for (const square of SQUARES) {
302
+ const squareGeometry = geometry.squares[square];
303
+ context.fillStyle = isDarkSquare(square) ? request.colors.darkSquare : request.colors.lightSquare;
304
+ context.fillRect(
305
+ squareGeometry.x,
306
+ squareGeometry.y,
307
+ squareGeometry.size,
308
+ squareGeometry.size
309
+ );
310
+ if (request.highlights.includes(square)) {
311
+ context.fillStyle = request.colors.highlight;
312
+ context.fillRect(
313
+ squareGeometry.x,
314
+ squareGeometry.y,
315
+ squareGeometry.size,
316
+ squareGeometry.size
317
+ );
318
+ }
319
+ const pieceKey = request.board.squares[square];
320
+ if (!pieceKey) {
321
+ continue;
322
+ }
323
+ const assetPath2 = request.theme.pieces[pieceKey].source;
324
+ const raster = await getPieceRaster(
325
+ request.theme.name,
326
+ pieceKey,
327
+ assetPath2,
328
+ Math.round(geometry.squareSize)
329
+ );
330
+ context.drawImage(
331
+ raster,
332
+ squareGeometry.x,
333
+ squareGeometry.y,
334
+ squareGeometry.size,
335
+ squareGeometry.size
336
+ );
337
+ }
338
+ return canvas.toBuffer("image/png");
339
+ } catch (error) {
340
+ if (error instanceof RenderError) {
341
+ throw error;
342
+ }
343
+ throw new RenderError("Failed to render chess board", { cause: error });
344
+ }
345
+ }
346
+ };
347
+
348
+ // src/utils/io.ts
349
+ import { writeFile } from "fs/promises";
350
+ async function writeBufferToFile(filePath, buffer) {
351
+ try {
352
+ await writeFile(filePath, buffer);
353
+ } catch (error) {
354
+ throw new IOError(`Failed to write file: ${filePath}`, { cause: error });
355
+ }
356
+ }
357
+
358
+ // src/themes/builtins.ts
359
+ import { existsSync } from "fs";
360
+ import { resolve } from "path";
361
+
362
+ // src/themes/validation.ts
363
+ var REQUIRED_PIECES = [
364
+ "wK",
365
+ "wQ",
366
+ "wR",
367
+ "wB",
368
+ "wN",
369
+ "wP",
370
+ "bK",
371
+ "bQ",
372
+ "bR",
373
+ "bB",
374
+ "bN",
375
+ "bP"
376
+ ];
377
+ function validateThemeDefinition(theme) {
378
+ const normalizedName = validateThemeName(theme.name);
379
+ if (!theme.displayName.trim()) {
380
+ throw new ValidationError("Theme displayName is required");
381
+ }
382
+ if (!theme.license.trim()) {
383
+ throw new ValidationError("Theme license is required");
384
+ }
385
+ if (!theme.attribution.trim()) {
386
+ throw new ValidationError("Theme attribution is required");
387
+ }
388
+ for (const pieceKey of REQUIRED_PIECES) {
389
+ const asset = theme.pieces[pieceKey];
390
+ if (!asset || asset.kind !== "svg" || !asset.source.trim()) {
391
+ throw new ThemeError(`Theme "${normalizedName}" is missing SVG asset ${pieceKey}`);
392
+ }
393
+ }
394
+ return {
395
+ ...theme,
396
+ name: normalizedName
397
+ };
398
+ }
399
+
400
+ // src/themes/registry.ts
401
+ var registry = /* @__PURE__ */ new Map();
402
+ function registerTheme(theme) {
403
+ const validatedTheme = validateThemeDefinition(theme);
404
+ if (registry.has(validatedTheme.name)) {
405
+ throw new ThemeError(`Theme "${validatedTheme.name}" is already registered`);
406
+ }
407
+ registry.set(validatedTheme.name, validatedTheme);
408
+ return validatedTheme;
409
+ }
410
+ function getTheme(name) {
411
+ return registry.get(name.trim().toLowerCase());
412
+ }
413
+
414
+ // src/themes/builtins.ts
415
+ var builtInThemeNames = [
416
+ "merida",
417
+ "alpha",
418
+ "cburnett",
419
+ "cheq",
420
+ "leipzig"
421
+ ];
422
+ var PIECE_KEYS = [
423
+ "wK",
424
+ "wQ",
425
+ "wR",
426
+ "wB",
427
+ "wN",
428
+ "wP",
429
+ "bK",
430
+ "bQ",
431
+ "bR",
432
+ "bB",
433
+ "bN",
434
+ "bP"
435
+ ];
436
+ var initialized = false;
437
+ function assetPath(themeName, pieceKey) {
438
+ const candidates = [
439
+ resolve(__dirname, "../../assets/themes", themeName, `${pieceKey}.svg`),
440
+ resolve(__dirname, "../assets/themes", themeName, `${pieceKey}.svg`),
441
+ resolve(process.cwd(), "assets/themes", themeName, `${pieceKey}.svg`)
442
+ ];
443
+ const match = candidates.find((candidate) => existsSync(candidate));
444
+ return match ?? candidates[0];
445
+ }
446
+ function createBuiltInTheme(themeName) {
447
+ return {
448
+ name: themeName,
449
+ displayName: themeName[0].toUpperCase() + themeName.slice(1),
450
+ license: "MIT",
451
+ attribution: "Project-authored built-in SVG theme",
452
+ pieces: Object.fromEntries(
453
+ PIECE_KEYS.map((pieceKey) => [
454
+ pieceKey,
455
+ {
456
+ kind: "svg",
457
+ source: assetPath(themeName, pieceKey)
458
+ }
459
+ ])
460
+ )
461
+ };
462
+ }
463
+ function initializeBuiltInThemes() {
464
+ if (initialized) {
465
+ return;
466
+ }
467
+ for (const themeName of builtInThemeNames) {
468
+ registerTheme(createBuiltInTheme(themeName));
469
+ }
470
+ initialized = true;
471
+ }
472
+
473
+ // src/themes/resolver.ts
474
+ function resolveTheme({ theme, style }) {
475
+ initializeBuiltInThemes();
476
+ if (typeof theme === "object" && theme !== null) {
477
+ try {
478
+ return validateThemeDefinition(theme);
479
+ } catch (error) {
480
+ if (error instanceof ThemeError) {
481
+ throw error;
482
+ }
483
+ if (error instanceof ValidationError) {
484
+ throw new ThemeError(error.message, { cause: error });
485
+ }
486
+ throw error;
487
+ }
488
+ }
489
+ const requestedName = typeof theme === "string" ? theme : style ?? "merida";
490
+ const resolvedTheme = getTheme(requestedName);
491
+ if (!resolvedTheme) {
492
+ throw new ThemeError(`Unknown theme: ${requestedName}`);
493
+ }
494
+ return resolvedTheme;
495
+ }
496
+
497
+ // src/utils/normalization.ts
498
+ var DEFAULT_SIZE = 480;
499
+ var DEFAULT_PADDING = [0, 0, 0, 0];
500
+ var DEFAULT_COLORS = {
501
+ lightSquare: "#f0d9b5",
502
+ darkSquare: "#b58863",
503
+ highlight: "rgba(255, 206, 0, 0.45)"
504
+ };
505
+ function normalizeColors(colors) {
506
+ return {
507
+ lightSquare: colors?.lightSquare ?? DEFAULT_COLORS.lightSquare,
508
+ darkSquare: colors?.darkSquare ?? DEFAULT_COLORS.darkSquare,
509
+ highlight: colors?.highlight ?? DEFAULT_COLORS.highlight
510
+ };
511
+ }
512
+ function normalizeRenderInputs(options) {
513
+ return {
514
+ size: validateSize(options.size ?? DEFAULT_SIZE),
515
+ padding: normalizePadding(options.padding ?? DEFAULT_PADDING),
516
+ flipped: options.flipped ?? false,
517
+ theme: resolveTheme({
518
+ theme: options.theme,
519
+ style: options.style
520
+ }),
521
+ highlightSquares: normalizeHighlights(options.highlightSquares ?? []),
522
+ colors: normalizeColors(options.colors)
523
+ };
524
+ }
525
+
526
+ // src/api/class-api.ts
527
+ var ChessImageGenerator = class {
528
+ position = null;
529
+ defaults;
530
+ highlights = [];
531
+ constructor(options = {}) {
532
+ this.defaults = { ...options };
533
+ normalizeRenderInputs({
534
+ ...this.defaults,
535
+ highlightSquares: []
536
+ });
537
+ }
538
+ async loadFEN(fen) {
539
+ this.position = parseFEN(fen);
540
+ this.clearHighlights();
541
+ }
542
+ async loadPGN(pgn) {
543
+ this.position = parsePGN(pgn);
544
+ this.clearHighlights();
545
+ }
546
+ async loadBoard(board) {
547
+ this.position = parseBoardArray(board);
548
+ this.clearHighlights();
549
+ }
550
+ setHighlights(squares) {
551
+ this.highlights = normalizeHighlights(squares);
552
+ }
553
+ clearHighlights() {
554
+ this.highlights = [];
555
+ }
556
+ async toBuffer() {
557
+ if (!this.position) {
558
+ throw new ValidationError("No board position loaded");
559
+ }
560
+ const renderer = new CanvasPngRenderer();
561
+ const normalized = normalizeRenderInputs({
562
+ ...this.defaults,
563
+ highlightSquares: this.highlights
564
+ });
565
+ return renderer.render({
566
+ board: this.position,
567
+ theme: normalized.theme,
568
+ highlights: normalized.highlightSquares,
569
+ size: normalized.size,
570
+ padding: normalized.padding,
571
+ flipped: normalized.flipped,
572
+ colors: normalized.colors
573
+ });
574
+ }
575
+ async toFile(filePath) {
576
+ const buffer = await this.toBuffer();
577
+ await writeBufferToFile(filePath, buffer);
578
+ }
579
+ };
580
+
581
+ // src/api/functional-api.ts
582
+ function parseInputPosition(options) {
583
+ if (typeof options.fen === "string") {
584
+ return parseFEN(options.fen);
585
+ }
586
+ if (typeof options.pgn === "string") {
587
+ return parsePGN(options.pgn);
588
+ }
589
+ if (Array.isArray(options.board)) {
590
+ return parseBoardArray(options.board);
591
+ }
592
+ throw new ValidationError("Exactly one of fen, pgn, or board must be provided");
593
+ }
594
+ async function renderChess(options) {
595
+ const provided = [
596
+ typeof options.fen === "string" ? options.fen : void 0,
597
+ typeof options.pgn === "string" ? options.pgn : void 0,
598
+ Array.isArray(options.board) ? options.board : void 0
599
+ ].filter((value) => value !== void 0);
600
+ if (provided.length !== 1) {
601
+ throw new ValidationError("Exactly one of fen, pgn, or board must be provided");
602
+ }
603
+ const position = parseInputPosition(options);
604
+ const renderer = new CanvasPngRenderer();
605
+ const normalized = normalizeRenderInputs(options);
606
+ return renderer.render({
607
+ board: position,
608
+ theme: normalized.theme,
609
+ highlights: normalized.highlightSquares,
610
+ size: normalized.size,
611
+ padding: normalized.padding,
612
+ flipped: normalized.flipped,
613
+ colors: normalized.colors
614
+ });
615
+ }
616
+ export {
617
+ ChessImageGenerator,
618
+ IOError,
619
+ ParseError,
620
+ RenderError,
621
+ ThemeError,
622
+ ValidationError,
623
+ registerTheme,
624
+ renderChess
625
+ };
626
+ //# sourceMappingURL=index.js.map