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