@meonode/canvas 2.0.2 → 2.0.3

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 (68) hide show
  1. package/dist/cjs/canvas/canvas.helper.js +0 -230
  2. package/dist/cjs/canvas/canvas.helper.js.map +1 -1
  3. package/dist/cjs/canvas/chart.canvas.js +70 -144
  4. package/dist/cjs/canvas/chart.canvas.js.map +1 -1
  5. package/dist/cjs/canvas/image.canvas.js +2 -2
  6. package/dist/cjs/canvas/image.canvas.js.map +1 -1
  7. package/dist/cjs/canvas/layout.canvas.js +6 -6
  8. package/dist/cjs/canvas/layout.canvas.js.map +1 -1
  9. package/dist/cjs/canvas/root.canvas.js +23 -117
  10. package/dist/cjs/canvas/root.canvas.js.map +1 -1
  11. package/dist/cjs/canvas/text.canvas.js +2 -2
  12. package/dist/cjs/canvas/text.canvas.js.map +1 -1
  13. package/dist/cjs/src/canvas/canvas.helper.d.ts +1 -20
  14. package/dist/cjs/src/canvas/canvas.helper.d.ts.map +1 -1
  15. package/dist/cjs/src/canvas/canvas.type.d.ts +1 -12
  16. package/dist/cjs/src/canvas/canvas.type.d.ts.map +1 -1
  17. package/dist/cjs/src/canvas/chart.canvas.d.ts +1 -1
  18. package/dist/cjs/src/canvas/chart.canvas.d.ts.map +1 -1
  19. package/dist/cjs/src/canvas/image.canvas.d.ts +1 -1
  20. package/dist/cjs/src/canvas/image.canvas.d.ts.map +1 -1
  21. package/dist/cjs/src/canvas/layout.canvas.d.ts +2 -2
  22. package/dist/cjs/src/canvas/layout.canvas.d.ts.map +1 -1
  23. package/dist/cjs/src/canvas/root.canvas.d.ts +3 -2
  24. package/dist/cjs/src/canvas/root.canvas.d.ts.map +1 -1
  25. package/dist/cjs/src/canvas/text.canvas.d.ts +1 -1
  26. package/dist/cjs/src/canvas/text.canvas.d.ts.map +1 -1
  27. package/dist/cjs/src/worker/comlink.pool.d.ts +30 -0
  28. package/dist/cjs/src/worker/comlink.pool.d.ts.map +1 -0
  29. package/dist/cjs/src/worker/comlink.setup.d.ts +4 -0
  30. package/dist/cjs/src/worker/comlink.setup.d.ts.map +1 -0
  31. package/dist/cjs/src/worker/worker.types.d.ts +5 -68
  32. package/dist/cjs/src/worker/worker.types.d.ts.map +1 -1
  33. package/dist/cjs/worker/comlink.pool.js +164 -0
  34. package/dist/cjs/worker/comlink.pool.js.map +1 -0
  35. package/dist/cjs/worker/comlink.setup.js +53 -0
  36. package/dist/cjs/worker/comlink.setup.js.map +1 -0
  37. package/dist/cjs/worker/render.worker.js +58 -61
  38. package/dist/cjs/worker/render.worker.js.map +1 -1
  39. package/dist/esm/canvas/canvas.helper.js +1 -230
  40. package/dist/esm/canvas/chart.canvas.js +71 -145
  41. package/dist/esm/canvas/image.canvas.js +2 -2
  42. package/dist/esm/canvas/layout.canvas.js +6 -6
  43. package/dist/esm/canvas/root.canvas.js +23 -116
  44. package/dist/esm/canvas/text.canvas.js +2 -2
  45. package/dist/esm/src/canvas/canvas.helper.d.ts +1 -20
  46. package/dist/esm/src/canvas/canvas.helper.d.ts.map +1 -1
  47. package/dist/esm/src/canvas/canvas.type.d.ts +1 -12
  48. package/dist/esm/src/canvas/canvas.type.d.ts.map +1 -1
  49. package/dist/esm/src/canvas/chart.canvas.d.ts +1 -1
  50. package/dist/esm/src/canvas/chart.canvas.d.ts.map +1 -1
  51. package/dist/esm/src/canvas/image.canvas.d.ts +1 -1
  52. package/dist/esm/src/canvas/image.canvas.d.ts.map +1 -1
  53. package/dist/esm/src/canvas/layout.canvas.d.ts +2 -2
  54. package/dist/esm/src/canvas/layout.canvas.d.ts.map +1 -1
  55. package/dist/esm/src/canvas/root.canvas.d.ts +3 -2
  56. package/dist/esm/src/canvas/root.canvas.d.ts.map +1 -1
  57. package/dist/esm/src/canvas/text.canvas.d.ts +1 -1
  58. package/dist/esm/src/canvas/text.canvas.d.ts.map +1 -1
  59. package/dist/esm/src/worker/comlink.pool.d.ts +30 -0
  60. package/dist/esm/src/worker/comlink.pool.d.ts.map +1 -0
  61. package/dist/esm/src/worker/comlink.setup.d.ts +4 -0
  62. package/dist/esm/src/worker/comlink.setup.d.ts.map +1 -0
  63. package/dist/esm/src/worker/worker.types.d.ts +5 -68
  64. package/dist/esm/src/worker/worker.types.d.ts.map +1 -1
  65. package/dist/esm/worker/comlink.pool.js +139 -0
  66. package/dist/esm/worker/comlink.setup.js +30 -0
  67. package/dist/esm/worker/render.worker.js +38 -60
  68. package/package.json +2 -1
@@ -1,73 +1,70 @@
1
1
  'use strict';
2
2
 
3
- var worker_threads = require('worker_threads');
3
+ var node_worker_threads = require('node:worker_threads');
4
+ require('./comlink.setup.js');
4
5
  var root_canvas = require('../canvas/root.canvas.js');
6
+ var Comlink = require('comlink');
7
+ var nodeEndpoint = require('comlink/dist/esm/node-adapter.mjs');
5
8
 
6
- /**
7
- * Worker thread entry point for off-main-thread canvas rendering.
8
- *
9
- * Message protocol (main → worker):
10
- * { type: 'render', taskId, props } — render and keep Canvas alive
11
- * { type: 'call', taskId, canvasId, method, args } — call a method on a live Canvas
12
- * { type: 'release', canvasId } — free Canvas from memory
13
- *
14
- * Responses (worker main):
15
- * WorkerRenderResponse — render complete (includes pre-encoded PNG buffer)
16
- * WorkerCallResponse — method call result
17
- * WorkerErrorResponse — any failure
18
- */
19
- if (!worker_threads.parentPort) {
9
+ function _interopNamespaceDefault(e) {
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var Comlink__namespace = /*#__PURE__*/_interopNamespaceDefault(Comlink);
27
+
28
+ if (!node_worker_threads.parentPort) {
20
29
  throw new Error('[render.worker] Must be run as a worker thread');
21
30
  }
22
31
  const canvases = new Map();
23
32
  let nextCanvasId = 0;
24
- function reply(msg) {
25
- worker_threads.parentPort.postMessage(msg);
26
- }
27
- worker_threads.parentPort.on('message', async (msg) => {
28
- if (msg.type === 'render') {
29
- try {
30
- const canvas = await new root_canvas.RootNode(msg.props).render();
31
- const canvasId = nextCanvasId++;
32
- canvases.set(canvasId, canvas);
33
- reply({ taskId: msg.taskId, canvasId, buffer: canvas.toBufferSync('png'), width: canvas.width, height: canvas.height });
34
- }
35
- catch (err) {
36
- reply({ taskId: msg.taskId, error: String(err) });
37
- }
38
- }
39
- else if (msg.type === 'call') {
40
- const canvas = canvases.get(msg.canvasId);
33
+ const api = {
34
+ async render(props) {
35
+ const canvas = await new root_canvas.RootNode(props).render();
36
+ const canvasId = nextCanvasId++;
37
+ canvases.set(canvasId, canvas);
38
+ const result = {
39
+ canvasId,
40
+ buffer: canvas.toBufferSync('png'),
41
+ width: canvas.width,
42
+ height: canvas.height,
43
+ };
44
+ return result;
45
+ },
46
+ async callOnCanvas(canvasId, method, args) {
47
+ const canvas = canvases.get(canvasId);
41
48
  if (!canvas) {
42
- reply({ taskId: msg.taskId, error: `[render.worker] Canvas ${msg.canvasId} not found` });
43
- return;
44
- }
45
- try {
46
- let result;
47
- switch (msg.method) {
48
- case 'toBuffer':
49
- result = await canvas.toBuffer(...msg.args);
50
- break;
51
- case 'toURL':
52
- result = await canvas.toURL(...msg.args);
53
- break;
54
- case 'toFile':
55
- result = await canvas.toFile(...msg.args);
56
- break;
57
- case 'toSharp':
58
- // Sharp instances can't be transferred across threads — serialize to buffer
59
- result = await canvas.toSharp(...msg.args).toBuffer();
60
- break;
61
- }
62
- reply({ taskId: msg.taskId, result });
49
+ throw new Error(`[render.worker] Canvas ${canvasId} not found`);
63
50
  }
64
- catch (err) {
65
- reply({ taskId: msg.taskId, error: String(err) });
51
+ switch (method) {
52
+ case 'toBuffer':
53
+ return canvas.toBuffer(...args);
54
+ case 'toURL':
55
+ return canvas.toURL(...args);
56
+ case 'toFile':
57
+ await canvas.toFile(...args);
58
+ return;
59
+ case 'toSharp':
60
+ return await canvas.toSharp(...args).toBuffer();
61
+ default:
62
+ throw new Error(`[render.worker] Unknown method: ${method}`);
66
63
  }
67
- }
68
- else {
69
- // type === 'release'
70
- canvases.delete(msg.canvasId);
71
- }
72
- });
64
+ },
65
+ releaseCanvas(canvasId) {
66
+ canvases.delete(canvasId);
67
+ },
68
+ };
69
+ Comlink__namespace.expose(api, nodeEndpoint(node_worker_threads.parentPort));
73
70
  //# sourceMappingURL=render.worker.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"render.worker.js","sources":["../../../../../src/worker/render.worker.ts"],"sourcesContent":["/**\n * Worker thread entry point for off-main-thread canvas rendering.\n *\n * Message protocol (main → worker):\n * { type: 'render', taskId, props } render and keep Canvas alive\n * { type: 'call', taskId, canvasId, method, args } — call a method on a live Canvas\n * { type: 'release', canvasId } free Canvas from memory\n *\n * Responses (worker → main):\n * WorkerRenderResponse — render complete (includes pre-encoded PNG buffer)\n * WorkerCallResponse — method call result\n * WorkerErrorResponse — any failure\n */\nimport { parentPort } from 'worker_threads'\nimport { RootNode } from '@/canvas/root.canvas.js'\nimport type { Canvas } from 'skia-canvas'\nimport type { WorkerRequest, WorkerRenderResponse, WorkerCallResponse, WorkerErrorResponse } from '@/worker/worker.types.js'\n\nif (!parentPort) {\n throw new Error('[render.worker] Must be run as a worker thread')\n}\n\nconst canvases = new Map<number, Canvas>()\nlet nextCanvasId = 0\n\nfunction reply(msg: WorkerRenderResponse | WorkerCallResponse | WorkerErrorResponse) {\n parentPort!.postMessage(msg)\n}\n\nparentPort.on('message', async (msg: WorkerRequest) => {\n if (msg.type === 'render') {\n try {\n const canvas = await new RootNode(msg.props).render()\n const canvasId = nextCanvasId++\n canvases.set(canvasId, canvas)\n reply({ taskId: msg.taskId, canvasId, buffer: canvas.toBufferSync('png'), width: canvas.width, height: canvas.height })\n } catch (err) {\n reply({ taskId: msg.taskId, error: String(err) })\n }\n } else if (msg.type === 'call') {\n const canvas = canvases.get(msg.canvasId)\n if (!canvas) {\n reply({ taskId: msg.taskId, error: `[render.worker] Canvas ${msg.canvasId} not found` })\n return\n }\n try {\n let result: Buffer | string | void\n switch (msg.method) {\n case 'toBuffer':\n result = await canvas.toBuffer(...msg.args)\n break\n case 'toURL':\n result = await canvas.toURL(...msg.args)\n break\n case 'toFile':\n result = await canvas.toFile(...msg.args)\n break\n case 'toSharp':\n // Sharp instances can't be transferred across threads — serialize to buffer\n result = await canvas.toSharp(...msg.args).toBuffer()\n break\n }\n reply({ taskId: msg.taskId, result })\n } catch (err) {\n reply({ taskId: msg.taskId, error: String(err) })\n }\n } else {\n // type === 'release'\n canvases.delete(msg.canvasId)\n }\n})\n"],"names":["parentPort","RootNode"],"mappings":";;;;;AAAA;;;;;;;;;;;;AAYG;AAMH,IAAI,CAACA,yBAAU,EAAE;AACf,IAAA,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC;AACnE;AAEA,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB;AAC1C,IAAI,YAAY,GAAG,CAAC;AAEpB,SAAS,KAAK,CAAC,GAAoE,EAAA;AACjF,IAAAA,yBAAW,CAAC,WAAW,CAAC,GAAG,CAAC;AAC9B;AAEAA,yBAAU,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,GAAkB,KAAI;AACpD,IAAA,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE;AACzB,QAAA,IAAI;AACF,YAAA,MAAM,MAAM,GAAG,MAAM,IAAIC,oBAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE;AACrD,YAAA,MAAM,QAAQ,GAAG,YAAY,EAAE;AAC/B,YAAA,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC;AAC9B,YAAA,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;QACzH;QAAE,OAAO,GAAG,EAAE;AACZ,YAAA,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACnD;IACF;AAAO,SAAA,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE;QAC9B,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC;QACzC,IAAI,CAAC,MAAM,EAAE;AACX,YAAA,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,0BAA0B,GAAG,CAAC,QAAQ,CAAA,UAAA,CAAY,EAAE,CAAC;YACxF;QACF;AACA,QAAA,IAAI;AACF,YAAA,IAAI,MAA8B;AAClC,YAAA,QAAQ,GAAG,CAAC,MAAM;AAChB,gBAAA,KAAK,UAAU;oBACb,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC;oBAC3C;AACF,gBAAA,KAAK,OAAO;oBACV,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC;oBACxC;AACF,gBAAA,KAAK,QAAQ;oBACX,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC;oBACzC;AACF,gBAAA,KAAK,SAAS;;AAEZ,oBAAA,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;oBACrD;;YAEJ,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QACvC;QAAE,OAAO,GAAG,EAAE;AACZ,YAAA,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACnD;IACF;SAAO;;AAEL,QAAA,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC/B;AACF,CAAC,CAAC;;"}
1
+ {"version":3,"file":"render.worker.js","sources":["../../../../../src/worker/render.worker.ts"],"sourcesContent":["import { parentPort } from 'node:worker_threads'\nimport { Comlink, nodeEndpoint } from '@/worker/comlink.setup.js'\nimport { RootNode } from '@/canvas/root.canvas.js'\nimport type { Canvas } from 'skia-canvas'\nimport type { WorkerAPI, RenderResult } from '@/worker/worker.types.js'\n\nif (!parentPort) {\n throw new Error('[render.worker] Must be run as a worker thread')\n}\n\nconst canvases = new Map<number, Canvas>()\nlet nextCanvasId = 0\n\nconst api: WorkerAPI = {\n async render(props) {\n const canvas = await new RootNode(props).render()\n const canvasId = nextCanvasId++\n canvases.set(canvasId, canvas)\n const result: RenderResult = {\n canvasId,\n buffer: canvas.toBufferSync('png'),\n width: canvas.width,\n height: canvas.height,\n }\n return result\n },\n\n async callOnCanvas(canvasId, method, args) {\n const canvas = canvases.get(canvasId)\n if (!canvas) {\n throw new Error(`[render.worker] Canvas ${canvasId} not found`)\n }\n switch (method) {\n case 'toBuffer':\n return canvas.toBuffer(...(args as [any, any?]))\n case 'toURL':\n return canvas.toURL(...(args as [any, any?]))\n case 'toFile':\n await canvas.toFile(...(args as [string, any?]))\n return\n case 'toSharp':\n return await canvas.toSharp(...(args as [any?])).toBuffer()\n default:\n throw new Error(`[render.worker] Unknown method: ${method}`)\n }\n },\n\n releaseCanvas(canvasId) {\n canvases.delete(canvasId)\n },\n}\n\nComlink.expose(api, nodeEndpoint(parentPort))\n"],"names":["parentPort","RootNode","Comlink"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,IAAI,CAACA,8BAAU,EAAE;AACf,IAAA,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC;AACnE;AAEA,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB;AAC1C,IAAI,YAAY,GAAG,CAAC;AAEpB,MAAM,GAAG,GAAc;IACrB,MAAM,MAAM,CAAC,KAAK,EAAA;QAChB,MAAM,MAAM,GAAG,MAAM,IAAIC,oBAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE;AACjD,QAAA,MAAM,QAAQ,GAAG,YAAY,EAAE;AAC/B,QAAA,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC;AAC9B,QAAA,MAAM,MAAM,GAAiB;YAC3B,QAAQ;AACR,YAAA,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC;YAClC,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,MAAM,CAAC,MAAM;SACtB;AACD,QAAA,OAAO,MAAM;IACf,CAAC;AAED,IAAA,MAAM,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAA;QACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC;QACrC,IAAI,CAAC,MAAM,EAAE;AACX,YAAA,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,CAAA,UAAA,CAAY,CAAC;QACjE;QACA,QAAQ,MAAM;AACZ,YAAA,KAAK,UAAU;AACb,gBAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAI,IAAoB,CAAC;AAClD,YAAA,KAAK,OAAO;AACV,gBAAA,OAAO,MAAM,CAAC,KAAK,CAAC,GAAI,IAAoB,CAAC;AAC/C,YAAA,KAAK,QAAQ;AACX,gBAAA,MAAM,MAAM,CAAC,MAAM,CAAC,GAAI,IAAuB,CAAC;gBAChD;AACF,YAAA,KAAK,SAAS;gBACZ,OAAO,MAAM,MAAM,CAAC,OAAO,CAAC,GAAI,IAAe,CAAC,CAAC,QAAQ,EAAE;AAC7D,YAAA;AACE,gBAAA,MAAM,IAAI,KAAK,CAAC,mCAAmC,MAAM,CAAA,CAAE,CAAC;;IAElE,CAAC;AAED,IAAA,aAAa,CAAC,QAAQ,EAAA;AACpB,QAAA,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC3B,CAAC;CACF;AAEDC,kBAAO,CAAC,MAAM,CAAC,GAAG,EAAE,YAAY,CAACF,8BAAU,CAAC,CAAC;;"}
@@ -209,234 +209,5 @@ function parsePercentage(value, base) {
209
209
  }
210
210
  return 0;
211
211
  }
212
- /**
213
- * Pre-processes a RootProps descriptor tree on the main thread before postMessage.
214
- * Executes function props against available data, stores results as serializable fields,
215
- * then strips remaining functions as a safety net.
216
- */
217
- class WorkerPreProcessor {
218
- static CHART_COLORS = ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40', '#C9CBCF'];
219
- static process(props) {
220
- const cloned = { ...props };
221
- if (cloned.children) {
222
- const childArray = Array.isArray(cloned.children) ? cloned.children : [cloned.children];
223
- cloned.children = childArray.map(child => {
224
- if (child && typeof child === 'object' && '__type' in child) {
225
- return this.processDescriptor(child);
226
- }
227
- return child;
228
- });
229
- }
230
- return this.stripNonSerializable(cloned);
231
- }
232
- static processDescriptor(desc) {
233
- if (desc.__type === 'Chart') {
234
- return this.processChartDescriptor(desc);
235
- }
236
- if ('children' in desc && desc.children) {
237
- return { ...desc, children: desc.children.map(c => this.processDescriptor(c)) };
238
- }
239
- return desc;
240
- }
241
- static processChartDescriptor(desc) {
242
- const props = { ...desc.props };
243
- const options = { ...(props.options || {}) };
244
- const chartType = props.type;
245
- const data = props.data;
246
- // xAxisLabelFormatter
247
- if (typeof options.xAxisLabelFormatter === 'function' && (chartType === 'bar' || chartType === 'line')) {
248
- const cartData = data;
249
- if (cartData.labels) {
250
- options._preComputedXAxisLabels = cartData.labels.map((label, index) => {
251
- try {
252
- return options.xAxisLabelFormatter(label, index);
253
- }
254
- catch (err) {
255
- console.warn(`[WorkerPreProcessor] xAxisLabelFormatter threw for label "${label}" at index ${index}:`, err);
256
- return label;
257
- }
258
- });
259
- }
260
- delete options.xAxisLabelFormatter;
261
- }
262
- // yAxisLabelFormatter
263
- if (typeof options.yAxisLabelFormatter === 'function' && (chartType === 'bar' || chartType === 'line')) {
264
- const cartData = data;
265
- const maxValue = Math.max(...cartData.datasets.flatMap((d) => d.data));
266
- const labels = [];
267
- for (let i = 0; i <= 5; i++) {
268
- const value = maxValue - (maxValue / 5) * i;
269
- try {
270
- labels.push(options.yAxisLabelFormatter(value));
271
- }
272
- catch (err) {
273
- console.warn(`[WorkerPreProcessor] yAxisLabelFormatter threw for value ${value}:`, err);
274
- labels.push(String(value));
275
- }
276
- }
277
- options._preComputedYAxisLabels = labels;
278
- delete options.yAxisLabelFormatter;
279
- }
280
- // renderLegendItem
281
- if (typeof options.renderLegendItem === 'function') {
282
- if (chartType === 'bar' || chartType === 'line') {
283
- const cartData = data;
284
- options._preComputedLegendItems = cartData.datasets.map((item, index) => {
285
- const color = item.color || this.generateColor(index);
286
- try {
287
- const result = options.renderLegendItem({ item, index, color });
288
- return this.nodeToDescriptor(result);
289
- }
290
- catch (err) {
291
- console.warn(`[WorkerPreProcessor] renderLegendItem threw at index ${index}:`, err);
292
- return null;
293
- }
294
- });
295
- }
296
- else {
297
- const pieData = data;
298
- options._preComputedLegendItems = pieData.map((item, index) => {
299
- const color = item.color || this.generateColor(index);
300
- try {
301
- const result = options.renderLegendItem({ item, index, color });
302
- return this.nodeToDescriptor(result);
303
- }
304
- catch (err) {
305
- console.warn(`[WorkerPreProcessor] renderLegendItem threw at index ${index}:`, err);
306
- return null;
307
- }
308
- });
309
- }
310
- delete options.renderLegendItem;
311
- }
312
- // renderLabelItem
313
- if (typeof options.renderLabelItem === 'function') {
314
- if (chartType === 'bar' || chartType === 'line') {
315
- const cartData = data;
316
- options._preComputedLabelItems = cartData.labels.map((label, index) => {
317
- try {
318
- const result = options.renderLabelItem({ item: label, index });
319
- return this.nodeToDescriptor(result);
320
- }
321
- catch (err) {
322
- console.warn(`[WorkerPreProcessor] renderLabelItem threw at index ${index}:`, err);
323
- return null;
324
- }
325
- });
326
- }
327
- else {
328
- const pieData = data;
329
- options._preComputedLabelItems = pieData.map((item, index) => {
330
- try {
331
- const result = options.renderLabelItem({ item, index });
332
- return this.nodeToDescriptor(result);
333
- }
334
- catch (err) {
335
- console.warn(`[WorkerPreProcessor] renderLabelItem threw at index ${index}:`, err);
336
- return null;
337
- }
338
- });
339
- }
340
- delete options.renderLabelItem;
341
- }
342
- // renderValueItem
343
- if (typeof options.renderValueItem === 'function' && (chartType === 'bar' || chartType === 'line')) {
344
- const cartData = data;
345
- options._preComputedValueItems = cartData.datasets.map((dataset, datasetIndex) => dataset.data.map((value, index) => {
346
- try {
347
- const result = options.renderValueItem({ item: value, index, datasetIndex });
348
- return this.nodeToDescriptor(result);
349
- }
350
- catch (err) {
351
- console.warn(`[WorkerPreProcessor] renderValueItem threw at dataset ${datasetIndex}, index ${index}:`, err);
352
- return null;
353
- }
354
- }));
355
- delete options.renderValueItem;
356
- }
357
- props.options = options;
358
- return { ...desc, props };
359
- }
360
- /**
361
- * Converts a BoxNode instance or CanvasElement to a CanvasElement.
362
- * Public API functions (Box(), Text(), etc.) already return CanvasElements,
363
- * but if someone returns an actual class instance, we convert it via initialProps.
364
- */
365
- static nodeToDescriptor(node) {
366
- if (!node)
367
- return node;
368
- // Already a CanvasElement (has __type)
369
- if (typeof node === 'object' && '__type' in node)
370
- return node;
371
- // Class instance (e.g. BoxNode, TextNode, ImageNode) — convert via duck-typing
372
- if (typeof node === 'object' && node.constructor !== Object) {
373
- if ('initialProps' in node && 'name' in node) {
374
- const { children, ...rest } = node.initialProps || {};
375
- const childArray = Array.isArray(children) ? children : children ? [children] : [];
376
- const convertedChildren = childArray
377
- .filter((c) => c)
378
- .map((c) => this.nodeToDescriptor(c))
379
- .filter((c) => !!c);
380
- const name = node.name;
381
- const type = name === 'TextNode'
382
- ? 'Text'
383
- : name === 'Row'
384
- ? 'Row'
385
- : name === 'Image'
386
- ? 'Image'
387
- : rest.flexDirection === Style.FlexDirection.Column
388
- ? 'Column'
389
- : 'Box';
390
- if (type === 'Text') {
391
- // TextNode stores original text in segments — fall back to joined segment text
392
- const text = node.segments?.map((s) => s.text).join('') ?? '';
393
- return { __type: 'Text', text, props: rest };
394
- }
395
- if (type === 'Image') {
396
- return { __type: 'Image', props: rest };
397
- }
398
- return {
399
- __type: type,
400
- props: rest,
401
- ...(convertedChildren.length > 0 ? { children: convertedChildren } : {}),
402
- };
403
- }
404
- console.warn('[WorkerPreProcessor] Render function returned an unrecognized class instance. Use descriptor functions (Box(), Text(), etc.) instead.');
405
- return null;
406
- }
407
- return node;
408
- }
409
- static generateColor(index) {
410
- return this.CHART_COLORS[index % this.CHART_COLORS.length];
411
- }
412
- static stripNonSerializable(obj) {
413
- if (obj === null || obj === undefined)
414
- return obj;
415
- if (typeof obj === 'function')
416
- return undefined;
417
- if (typeof obj === 'symbol')
418
- return undefined;
419
- if (typeof obj !== 'object')
420
- return obj;
421
- // Preserve binary data types
422
- if (Buffer.isBuffer(obj))
423
- return obj;
424
- if (obj instanceof ArrayBuffer)
425
- return obj;
426
- if (ArrayBuffer.isView(obj))
427
- return obj;
428
- if (Array.isArray(obj)) {
429
- return obj.map(item => this.stripNonSerializable(item));
430
- }
431
- const result = {};
432
- for (const key of Object.keys(obj)) {
433
- const value = obj[key];
434
- if (typeof value === 'function' || typeof value === 'symbol')
435
- continue;
436
- result[key] = this.stripNonSerializable(value);
437
- }
438
- return result;
439
- }
440
- }
441
212
 
442
- export { WorkerPreProcessor, drawBorders, drawRoundedRectPath, parseBorderRadius, parsePercentage };
213
+ export { drawBorders, drawRoundedRectPath, parseBorderRadius, parsePercentage };