@graphein/node 0.6.0 → 0.7.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.
package/README.md CHANGED
@@ -85,11 +85,11 @@ renderChart(spec, {
85
85
 
86
86
  ## Supported charts
87
87
 
88
- Every canvas-backed type: **line, area, bar, scatter, box, pie, heatmap, sankey,
89
- choropleth, combo, histogram, funnel, treemap, gauge, bullet, calendarHeatmap,
90
- waterfall, slope, dumbbell**.
91
- DOM-only visuals (`kpi`, `table`, `matrix`, slicers, `dashboard`) are HTML — they have
92
- no canvas form and throw a clear error.
88
+ **Every type.** Cartesian + custom canvas charts: **line, area, bar, scatter, box, pie,
89
+ heatmap, sankey, choropleth, combo, histogram, funnel, treemap, gauge, bullet,
90
+ calendarHeatmap, waterfall, slope, dumbbell** — plus the formerly DOM-only **kpi, table,
91
+ matrix, slicers (dropdown/list/search/range/dateRange) and dashboard**, which render a
92
+ static canvas snapshot. So the whole catalog rasterizes + validates headlessly.
93
93
 
94
94
  ## License
95
95
 
package/dist/index.d.ts CHANGED
@@ -17,6 +17,9 @@ export { ChartSpec, RenderReport } from 'graphein';
17
17
  * if (!report.ok) console.warn(report.diagnostics);
18
18
  * await fs.writeFile('chart.png', png);
19
19
  * ```
20
+ *
21
+ * Every chart type rasterizes — kpi/table/matrix/slicers/dashboard render a
22
+ * static canvas snapshot, so the whole catalog can be validated server-side.
20
23
  */
21
24
 
22
25
  /** A font file to register before rendering, improving text fidelity. */
@@ -61,9 +64,9 @@ interface NodeRenderResult {
61
64
  /**
62
65
  * Render `spec` to a PNG buffer and its {@link RenderReport}, entirely in Node.
63
66
  *
64
- * Supports every canvas-backed chart (line, area, bar, scatter, box, pie,
65
- * heatmap, sankey, choropleth, combo, histogram, funnel). DOM-only kinds
66
- * (`kpi`, `table`, `matrix`, slicers, `dashboard`) have no canvas form and throw.
67
+ * Supports the entire catalog: line, area, bar, scatter, box, pie, heatmap,
68
+ * sankey, choropleth, combo, histogram, funnel, and the formerly DOM-only kpi,
69
+ * table, matrix, slicers and dashboard (painted as a static canvas snapshot).
67
70
  */
68
71
  declare function renderChart(spec: ChartSpec, options?: RenderImageOptions): NodeRenderResult;
69
72
  /**
@@ -71,6 +74,6 @@ declare function renderChart(spec: ChartSpec, options?: RenderImageOptions): Nod
71
74
  */
72
75
  declare function renderToPNG(spec: ChartSpec, options?: RenderImageOptions): Buffer;
73
76
 
74
- declare const VERSION = "0.3.0";
77
+ declare const VERSION = "0.7.0";
75
78
 
76
79
  export { type FontFace, type NodeRenderResult, type RenderImageOptions, VERSION, renderChart, renderToPNG };
package/dist/index.js CHANGED
@@ -46,7 +46,7 @@ function renderChart(spec, options = {}) {
46
46
  function renderToPNG(spec, options) {
47
47
  return renderChart(spec, options).png;
48
48
  }
49
- var VERSION = "0.3.0";
49
+ var VERSION = "0.7.0";
50
50
  export {
51
51
  GlobalFonts,
52
52
  VERSION,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@graphein/node` — headless Graphein rendering for Node.\n *\n * Render any canvas-backed {@link ChartSpec} to a PNG buffer **and** its\n * {@link RenderReport} with no browser and no JSDOM, so the whole\n * generate → validate → render → critique loop can run server-side (CI, agents,\n * email/PDF report assets). It wires `graphein`'s dependency-free\n * `renderToContext` to `@napi-rs/canvas`; the core engine stays zero-dependency.\n *\n * ```ts\n * import { renderChart } from '@graphein/node';\n * const { png, report } = renderChart(spec, { width: 900, height: 480, dpr: 2 });\n * if (!report.ok) console.warn(report.diagnostics);\n * await fs.writeFile('chart.png', png);\n * ```\n */\n\nimport { createCanvas, GlobalFonts } from '@napi-rs/canvas';\nimport { renderToContext, setMeasureContext, type ChartSpec, type RenderReport } from 'graphein';\n\n/** A font file to register before rendering, improving text fidelity. */\nexport interface FontFace {\n /** Absolute path to a `.ttf` / `.otf` / `.woff` file. */\n path: string;\n /**\n * The family name to register the font under. Match this to the family your\n * theme uses (Graphein's default is `Inter`) so text renders in that face.\n */\n family: string;\n}\n\n/** Options for {@link renderChart} / {@link renderToPNG}. */\nexport interface RenderImageOptions {\n /** Logical width in CSS pixels. Default `800`. */\n width?: number;\n /** Logical height in CSS pixels. Default `500`. */\n height?: number;\n /**\n * Device pixel ratio. The PNG is rasterized at `width * dpr × height * dpr`\n * for crisp output. Default `2`.\n */\n dpr?: number;\n /** Extra fonts to register before rendering. */\n fonts?: FontFace[];\n}\n\n/** The result of a headless render: the PNG bytes plus the render report. */\nexport interface NodeRenderResult {\n /** PNG-encoded image bytes. */\n png: Buffer;\n /**\n * The same machine-readable diagnostics as `instance.report()` in the\n * browser — `ok`, mark/series/color counts, and any clipping / overlap /\n * contrast warnings. Lets an agent critique the chart with no vision model.\n */\n report: RenderReport;\n /** Pixel width of the PNG (`width * dpr`, rounded). */\n width: number;\n /** Pixel height of the PNG (`height * dpr`, rounded). */\n height: number;\n}\n\nconst DEFAULT_WIDTH = 800;\nconst DEFAULT_HEIGHT = 500;\nconst DEFAULT_DPR = 2;\n\nfunction registerFonts(fonts: FontFace[] | undefined): void {\n if (!fonts) return;\n for (const f of fonts) {\n try {\n GlobalFonts.registerFromPath(f.path, f.family);\n } catch {\n // An unreadable font path is non-fatal — napi falls back to a system sans.\n }\n }\n}\n\n/**\n * Render `spec` to a PNG buffer and its {@link RenderReport}, entirely in Node.\n *\n * Supports every canvas-backed chart (line, area, bar, scatter, box, pie,\n * heatmap, sankey, choropleth, combo, histogram, funnel). DOM-only kinds\n * (`kpi`, `table`, `matrix`, slicers, `dashboard`) have no canvas form and throw.\n */\nexport function renderChart(spec: ChartSpec, options: RenderImageOptions = {}): NodeRenderResult {\n const width = options.width ?? DEFAULT_WIDTH;\n const height = options.height ?? DEFAULT_HEIGHT;\n const dpr = options.dpr ?? DEFAULT_DPR;\n\n registerFonts(options.fonts);\n\n const pxW = Math.max(1, Math.round(width * dpr));\n const pxH = Math.max(1, Math.round(height * dpr));\n\n const canvas = createCanvas(pxW, pxH);\n const ctx = canvas.getContext('2d');\n ctx.scale(dpr, dpr);\n\n // A dedicated 1×1 context for text measurement so layout uses real font\n // metrics (not the SSR heuristic) without mutating the drawing context.\n const measureCtx = createCanvas(1, 1).getContext('2d');\n\n // The interaction layer is unused on the static path; give it its own canvas\n // so a stray hover-layer paint can never land on the marks canvas.\n const interactionCtx = createCanvas(pxW, pxH).getContext('2d');\n interactionCtx.scale(dpr, dpr);\n\n setMeasureContext(measureCtx as unknown as CanvasRenderingContext2D);\n let report: RenderReport;\n try {\n report = renderToContext(\n {\n marks: ctx as unknown as CanvasRenderingContext2D,\n interaction: interactionCtx as unknown as CanvasRenderingContext2D,\n width,\n height,\n },\n spec,\n );\n } finally {\n setMeasureContext(null);\n }\n\n return { png: canvas.toBuffer('image/png'), report, width: pxW, height: pxH };\n}\n\n/**\n * Convenience wrapper around {@link renderChart} that returns only the PNG bytes.\n */\nexport function renderToPNG(spec: ChartSpec, options?: RenderImageOptions): Buffer {\n return renderChart(spec, options).png;\n}\n\n/** Re-exported so callers can register fonts globally without a second dep. */\nexport { GlobalFonts };\nexport type { ChartSpec, RenderReport } from 'graphein';\n\nexport const VERSION = '0.3.0';\n"],"mappings":";AAiBA,SAAS,cAAc,mBAAmB;AAC1C,SAAS,iBAAiB,yBAA4D;AA4CtF,IAAM,gBAAgB;AACtB,IAAM,iBAAiB;AACvB,IAAM,cAAc;AAEpB,SAAS,cAAc,OAAqC;AAC1D,MAAI,CAAC,MAAO;AACZ,aAAW,KAAK,OAAO;AACrB,QAAI;AACF,kBAAY,iBAAiB,EAAE,MAAM,EAAE,MAAM;AAAA,IAC/C,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AASO,SAAS,YAAY,MAAiB,UAA8B,CAAC,GAAqB;AAC/F,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,MAAM,QAAQ,OAAO;AAE3B,gBAAc,QAAQ,KAAK;AAE3B,QAAM,MAAM,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,GAAG,CAAC;AAC/C,QAAM,MAAM,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,GAAG,CAAC;AAEhD,QAAM,SAAS,aAAa,KAAK,GAAG;AACpC,QAAM,MAAM,OAAO,WAAW,IAAI;AAClC,MAAI,MAAM,KAAK,GAAG;AAIlB,QAAM,aAAa,aAAa,GAAG,CAAC,EAAE,WAAW,IAAI;AAIrD,QAAM,iBAAiB,aAAa,KAAK,GAAG,EAAE,WAAW,IAAI;AAC7D,iBAAe,MAAM,KAAK,GAAG;AAE7B,oBAAkB,UAAiD;AACnE,MAAI;AACJ,MAAI;AACF,aAAS;AAAA,MACP;AAAA,QACE,OAAO;AAAA,QACP,aAAa;AAAA,QACb;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF,UAAE;AACA,sBAAkB,IAAI;AAAA,EACxB;AAEA,SAAO,EAAE,KAAK,OAAO,SAAS,WAAW,GAAG,QAAQ,OAAO,KAAK,QAAQ,IAAI;AAC9E;AAKO,SAAS,YAAY,MAAiB,SAAsC;AACjF,SAAO,YAAY,MAAM,OAAO,EAAE;AACpC;AAMO,IAAM,UAAU;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@graphein/node` — headless Graphein rendering for Node.\n *\n * Render any canvas-backed {@link ChartSpec} to a PNG buffer **and** its\n * {@link RenderReport} with no browser and no JSDOM, so the whole\n * generate → validate → render → critique loop can run server-side (CI, agents,\n * email/PDF report assets). It wires `graphein`'s dependency-free\n * `renderToContext` to `@napi-rs/canvas`; the core engine stays zero-dependency.\n *\n * ```ts\n * import { renderChart } from '@graphein/node';\n * const { png, report } = renderChart(spec, { width: 900, height: 480, dpr: 2 });\n * if (!report.ok) console.warn(report.diagnostics);\n * await fs.writeFile('chart.png', png);\n * ```\n *\n * Every chart type rasterizes — kpi/table/matrix/slicers/dashboard render a\n * static canvas snapshot, so the whole catalog can be validated server-side.\n */\n\nimport { createCanvas, GlobalFonts } from '@napi-rs/canvas';\nimport { renderToContext, setMeasureContext, type ChartSpec, type RenderReport } from 'graphein';\n\n/** A font file to register before rendering, improving text fidelity. */\nexport interface FontFace {\n /** Absolute path to a `.ttf` / `.otf` / `.woff` file. */\n path: string;\n /**\n * The family name to register the font under. Match this to the family your\n * theme uses (Graphein's default is `Inter`) so text renders in that face.\n */\n family: string;\n}\n\n/** Options for {@link renderChart} / {@link renderToPNG}. */\nexport interface RenderImageOptions {\n /** Logical width in CSS pixels. Default `800`. */\n width?: number;\n /** Logical height in CSS pixels. Default `500`. */\n height?: number;\n /**\n * Device pixel ratio. The PNG is rasterized at `width * dpr × height * dpr`\n * for crisp output. Default `2`.\n */\n dpr?: number;\n /** Extra fonts to register before rendering. */\n fonts?: FontFace[];\n}\n\n/** The result of a headless render: the PNG bytes plus the render report. */\nexport interface NodeRenderResult {\n /** PNG-encoded image bytes. */\n png: Buffer;\n /**\n * The same machine-readable diagnostics as `instance.report()` in the\n * browser — `ok`, mark/series/color counts, and any clipping / overlap /\n * contrast warnings. Lets an agent critique the chart with no vision model.\n */\n report: RenderReport;\n /** Pixel width of the PNG (`width * dpr`, rounded). */\n width: number;\n /** Pixel height of the PNG (`height * dpr`, rounded). */\n height: number;\n}\n\nconst DEFAULT_WIDTH = 800;\nconst DEFAULT_HEIGHT = 500;\nconst DEFAULT_DPR = 2;\n\nfunction registerFonts(fonts: FontFace[] | undefined): void {\n if (!fonts) return;\n for (const f of fonts) {\n try {\n GlobalFonts.registerFromPath(f.path, f.family);\n } catch {\n // An unreadable font path is non-fatal — napi falls back to a system sans.\n }\n }\n}\n\n/**\n * Render `spec` to a PNG buffer and its {@link RenderReport}, entirely in Node.\n *\n * Supports the entire catalog: line, area, bar, scatter, box, pie, heatmap,\n * sankey, choropleth, combo, histogram, funnel, and the formerly DOM-only kpi,\n * table, matrix, slicers and dashboard (painted as a static canvas snapshot).\n */\nexport function renderChart(spec: ChartSpec, options: RenderImageOptions = {}): NodeRenderResult {\n const width = options.width ?? DEFAULT_WIDTH;\n const height = options.height ?? DEFAULT_HEIGHT;\n const dpr = options.dpr ?? DEFAULT_DPR;\n\n registerFonts(options.fonts);\n\n const pxW = Math.max(1, Math.round(width * dpr));\n const pxH = Math.max(1, Math.round(height * dpr));\n\n const canvas = createCanvas(pxW, pxH);\n const ctx = canvas.getContext('2d');\n ctx.scale(dpr, dpr);\n\n // A dedicated 1×1 context for text measurement so layout uses real font\n // metrics (not the SSR heuristic) without mutating the drawing context.\n const measureCtx = createCanvas(1, 1).getContext('2d');\n\n // The interaction layer is unused on the static path; give it its own canvas\n // so a stray hover-layer paint can never land on the marks canvas.\n const interactionCtx = createCanvas(pxW, pxH).getContext('2d');\n interactionCtx.scale(dpr, dpr);\n\n setMeasureContext(measureCtx as unknown as CanvasRenderingContext2D);\n let report: RenderReport;\n try {\n report = renderToContext(\n {\n marks: ctx as unknown as CanvasRenderingContext2D,\n interaction: interactionCtx as unknown as CanvasRenderingContext2D,\n width,\n height,\n },\n spec,\n );\n } finally {\n setMeasureContext(null);\n }\n\n return { png: canvas.toBuffer('image/png'), report, width: pxW, height: pxH };\n}\n\n/**\n * Convenience wrapper around {@link renderChart} that returns only the PNG bytes.\n */\nexport function renderToPNG(spec: ChartSpec, options?: RenderImageOptions): Buffer {\n return renderChart(spec, options).png;\n}\n\n/** Re-exported so callers can register fonts globally without a second dep. */\nexport { GlobalFonts };\nexport type { ChartSpec, RenderReport } from 'graphein';\n\nexport const VERSION = '0.7.0';\n"],"mappings":";AAoBA,SAAS,cAAc,mBAAmB;AAC1C,SAAS,iBAAiB,yBAA4D;AA4CtF,IAAM,gBAAgB;AACtB,IAAM,iBAAiB;AACvB,IAAM,cAAc;AAEpB,SAAS,cAAc,OAAqC;AAC1D,MAAI,CAAC,MAAO;AACZ,aAAW,KAAK,OAAO;AACrB,QAAI;AACF,kBAAY,iBAAiB,EAAE,MAAM,EAAE,MAAM;AAAA,IAC/C,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AASO,SAAS,YAAY,MAAiB,UAA8B,CAAC,GAAqB;AAC/F,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,MAAM,QAAQ,OAAO;AAE3B,gBAAc,QAAQ,KAAK;AAE3B,QAAM,MAAM,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,GAAG,CAAC;AAC/C,QAAM,MAAM,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,GAAG,CAAC;AAEhD,QAAM,SAAS,aAAa,KAAK,GAAG;AACpC,QAAM,MAAM,OAAO,WAAW,IAAI;AAClC,MAAI,MAAM,KAAK,GAAG;AAIlB,QAAM,aAAa,aAAa,GAAG,CAAC,EAAE,WAAW,IAAI;AAIrD,QAAM,iBAAiB,aAAa,KAAK,GAAG,EAAE,WAAW,IAAI;AAC7D,iBAAe,MAAM,KAAK,GAAG;AAE7B,oBAAkB,UAAiD;AACnE,MAAI;AACJ,MAAI;AACF,aAAS;AAAA,MACP;AAAA,QACE,OAAO;AAAA,QACP,aAAa;AAAA,QACb;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF,UAAE;AACA,sBAAkB,IAAI;AAAA,EACxB;AAEA,SAAO,EAAE,KAAK,OAAO,SAAS,WAAW,GAAG,QAAQ,OAAO,KAAK,QAAQ,IAAI;AAC9E;AAKO,SAAS,YAAY,MAAiB,SAAsC;AACjF,SAAO,YAAY,MAAM,OAAO,EAAE;AACpC;AAMO,IAAM,UAAU;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphein/node",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "description": "Headless Graphein rendering for Node — render any ChartSpec to a PNG buffer with a RenderReport, no browser required.",
6
6
  "license": "MIT",
@@ -56,6 +56,6 @@
56
56
  },
57
57
  "dependencies": {
58
58
  "@napi-rs/canvas": "^1.0.1",
59
- "graphein": "^0.6.0"
59
+ "graphein": "^0.7.0"
60
60
  }
61
61
  }