chartjs2img 0.3.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.
@@ -0,0 +1,283 @@
1
+ // Chart rendering via a managed Chromium.
2
+ //
3
+ // The Renderer class owns a single puppeteer Browser plus a semaphore
4
+ // and page-safety-net timers. Multiple Renderer instances are safe
5
+ // (they don't share state). For backwards compatibility with earlier
6
+ // module-level callers, a lazily-constructed default singleton backs
7
+ // the module-level renderChart / closeBrowser / rendererStats
8
+ // functions.
9
+ //
10
+ // Keeping state inside a class (rather than module vars) means tests
11
+ // can spin up an isolated Renderer and tear it down without affecting
12
+ // whatever other code the same process is running.
13
+ import puppeteer from 'puppeteer-core';
14
+ import { buildHtml } from './template';
15
+ import { Semaphore } from './semaphore';
16
+ import { computeHash, getCache, setCache } from './cache';
17
+ import { ensureChromiumInstalled } from './chromium';
18
+ const CONTENT_TYPES = {
19
+ png: 'image/png',
20
+ jpeg: 'image/jpeg',
21
+ };
22
+ const DEFAULT_MAX_CONCURRENCY = Number(process.env.CONCURRENCY ?? '8');
23
+ const DEFAULT_MAX_RENDER_TIME_MS = Number(process.env.MAX_RENDER_TIME_SECONDS ?? '30') * 1000;
24
+ function defaultSafetyNetMs(maxRenderTimeMs) {
25
+ // Legacy operators may still set PAGE_TIMEOUT_SECONDS; honor it
26
+ // verbatim rather than silently override.
27
+ if (process.env.PAGE_TIMEOUT_SECONDS) {
28
+ return Number(process.env.PAGE_TIMEOUT_SECONDS) * 1000;
29
+ }
30
+ // goto + waitForFunction + cleanup margin. If the finally block
31
+ // runs, it clears this timer before it ever fires.
32
+ return maxRenderTimeMs * 2 + 10_000;
33
+ }
34
+ // Console noise from Chromium that should never surface to the caller.
35
+ const IGNORED_CONSOLE_PATTERNS = [
36
+ 'A parser-blocking, cross site',
37
+ 'Third-party cookie will be blocked',
38
+ ];
39
+ // ---------------------------------------------------------------------------
40
+ // Renderer class
41
+ // ---------------------------------------------------------------------------
42
+ export class Renderer {
43
+ maxConcurrency;
44
+ maxRenderTimeMs;
45
+ pageSafetyNetMs;
46
+ semaphore;
47
+ browser = null;
48
+ launching = false;
49
+ launchPromise = null;
50
+ // Pages that haven't reached their render finally block yet. Each
51
+ // maps to a setTimeout() that force-closes the page if the finally
52
+ // block never runs (genuinely orphaned tab).
53
+ activePages = new Map();
54
+ constructor(config = {}) {
55
+ this.maxConcurrency = config.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY;
56
+ this.maxRenderTimeMs = config.maxRenderTimeMs ?? DEFAULT_MAX_RENDER_TIME_MS;
57
+ this.pageSafetyNetMs = config.pageSafetyNetMs ?? defaultSafetyNetMs(this.maxRenderTimeMs);
58
+ this.semaphore = new Semaphore(this.maxConcurrency);
59
+ }
60
+ // -- browser lifecycle ----------------------------------------------------
61
+ async launchBrowser() {
62
+ const execPath = await ensureChromiumInstalled();
63
+ const b = await puppeteer.launch({
64
+ executablePath: execPath,
65
+ headless: true,
66
+ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu', '--disable-dev-shm-usage'],
67
+ });
68
+ b.on('disconnected', () => {
69
+ console.error('[renderer] Browser disconnected unexpectedly');
70
+ this.browser = null;
71
+ this.launching = false;
72
+ this.launchPromise = null;
73
+ });
74
+ return b;
75
+ }
76
+ async ensureBrowser() {
77
+ if (this.browser && this.browser.connected) {
78
+ return this.browser;
79
+ }
80
+ if (this.launching && this.launchPromise) {
81
+ return this.launchPromise;
82
+ }
83
+ this.launching = true;
84
+ this.launchPromise = this.launchBrowser()
85
+ .then((b) => {
86
+ this.launching = false;
87
+ this.browser = b;
88
+ console.log(`[renderer] Browser launched (concurrency: ${this.maxConcurrency})`);
89
+ return b;
90
+ })
91
+ .catch((err) => {
92
+ this.launching = false;
93
+ this.launchPromise = null;
94
+ this.browser = null;
95
+ throw err;
96
+ });
97
+ return this.launchPromise;
98
+ }
99
+ // -- page lifecycle helpers ----------------------------------------------
100
+ schedulePageCleanup(page) {
101
+ const timer = setTimeout(async () => {
102
+ this.activePages.delete(page);
103
+ try {
104
+ if (!page.isClosed()) {
105
+ console.warn(`[renderer] Safety net fired after ${this.pageSafetyNetMs}ms — force-closing orphaned page. ` +
106
+ 'This indicates the render finally block did not run; please file a bug.');
107
+ await page.close();
108
+ }
109
+ }
110
+ catch {
111
+ // page already closed
112
+ }
113
+ }, this.pageSafetyNetMs);
114
+ this.activePages.set(page, timer);
115
+ }
116
+ clearPageCleanup(page) {
117
+ const timer = this.activePages.get(page);
118
+ if (timer) {
119
+ clearTimeout(timer);
120
+ this.activePages.delete(page);
121
+ }
122
+ }
123
+ // -- public API ----------------------------------------------------------
124
+ async render(options) {
125
+ const hash = computeHash(options);
126
+ const format = options.format ?? 'png';
127
+ const contentType = CONTENT_TYPES[format] ?? 'image/png';
128
+ // Check cache first (fast path, avoids semaphore wait entirely).
129
+ const cached = getCache(hash);
130
+ if (cached) {
131
+ return { buffer: cached.buffer, hash, contentType: cached.contentType, cached: true, messages: [] };
132
+ }
133
+ // Acquire semaphore slot (waits if at max concurrency).
134
+ await this.semaphore.acquire();
135
+ try {
136
+ // Re-check cache — another request may have rendered the same
137
+ // input while we were blocked on the semaphore.
138
+ const cachedAgain = getCache(hash);
139
+ if (cachedAgain) {
140
+ return {
141
+ buffer: cachedAgain.buffer,
142
+ hash,
143
+ contentType: cachedAgain.contentType,
144
+ cached: true,
145
+ messages: [],
146
+ };
147
+ }
148
+ const b = await this.ensureBrowser();
149
+ const page = await b.newPage();
150
+ this.schedulePageCleanup(page);
151
+ // Capture browser console messages (filter out Chromium internal noise)
152
+ const messages = [];
153
+ page.on('console', (msg) => {
154
+ const type = msg.type();
155
+ if (type === 'error' || type === 'warning' || type === 'warn') {
156
+ const text = msg.text();
157
+ if (IGNORED_CONSOLE_PATTERNS.some((p) => text.includes(p)))
158
+ return;
159
+ messages.push({ level: type === 'warning' || type === 'warn' ? 'warn' : 'error', message: text });
160
+ }
161
+ });
162
+ page.on('pageerror', (err) => {
163
+ messages.push({ level: 'error', message: err instanceof Error ? err.message : String(err) });
164
+ });
165
+ try {
166
+ const html = buildHtml(options);
167
+ const width = options.width ?? 800;
168
+ const height = options.height ?? 600;
169
+ await page.setViewport({ width, height });
170
+ // Use data URL with page.goto instead of setContent — puppeteer's
171
+ // setContent does not reliably honor networkidle for external
172
+ // <script src> loading, so charts would sometimes screenshot
173
+ // before Chart.js was even parsed.
174
+ const dataUrl = 'data:text/html;charset=utf-8,' + encodeURIComponent(html);
175
+ await page.goto(dataUrl, { waitUntil: 'networkidle0', timeout: this.maxRenderTimeMs });
176
+ await page.waitForFunction('window.__chartRendered === true', { timeout: this.maxRenderTimeMs });
177
+ await new Promise((r) => setTimeout(r, 100));
178
+ // Collect messages captured inside the page (Chart.js warnings/errors)
179
+ const pageMessages = await page.evaluate(() => window.__chartMessages ?? []);
180
+ for (const m of pageMessages) {
181
+ if (!messages.some((existing) => existing.message === m.message && existing.level === m.level)) {
182
+ messages.push({ level: m.level, message: m.message });
183
+ }
184
+ }
185
+ // Check if Chart.js threw an error during construction
186
+ const chartError = await page.evaluate(() => window.__chartError);
187
+ if (chartError) {
188
+ if (!messages.some((m) => m.message === chartError)) {
189
+ messages.push({ level: 'error', message: chartError });
190
+ }
191
+ }
192
+ const container = await page.$('#chart-container');
193
+ if (!container)
194
+ throw new Error('Chart container element not found');
195
+ const screenshotOptions = {
196
+ type: format,
197
+ omitBackground: options.backgroundColor === 'transparent',
198
+ };
199
+ if (format === 'jpeg') {
200
+ screenshotOptions.quality = options.quality ?? 90;
201
+ }
202
+ const rawBuffer = await container.screenshot(screenshotOptions);
203
+ const buffer = Buffer.from(rawBuffer);
204
+ setCache(hash, buffer, contentType);
205
+ return { buffer, hash, contentType, cached: false, messages };
206
+ }
207
+ finally {
208
+ this.clearPageCleanup(page);
209
+ try {
210
+ if (!page.isClosed())
211
+ await page.close();
212
+ }
213
+ catch {
214
+ // ignore
215
+ }
216
+ }
217
+ }
218
+ finally {
219
+ this.semaphore.release();
220
+ }
221
+ }
222
+ async close() {
223
+ for (const [page, timer] of this.activePages) {
224
+ clearTimeout(timer);
225
+ try {
226
+ if (!page.isClosed())
227
+ await page.close();
228
+ }
229
+ catch {
230
+ // ignore
231
+ }
232
+ }
233
+ this.activePages.clear();
234
+ if (this.browser) {
235
+ await this.browser.close();
236
+ this.browser = null;
237
+ }
238
+ }
239
+ stats() {
240
+ const safetyNetSeconds = this.pageSafetyNetMs / 1000;
241
+ return {
242
+ browserConnected: this.browser?.connected ?? false,
243
+ concurrency: { max: this.maxConcurrency, active: this.semaphore.active, pending: this.semaphore.pending },
244
+ activePages: this.activePages.size,
245
+ maxRenderTimeSeconds: this.maxRenderTimeMs / 1000,
246
+ pageSafetyNetSeconds: safetyNetSeconds,
247
+ pageTimeoutSeconds: safetyNetSeconds, // deprecated alias
248
+ };
249
+ }
250
+ }
251
+ // ---------------------------------------------------------------------------
252
+ // Default-singleton module API (backwards compatibility)
253
+ // ---------------------------------------------------------------------------
254
+ let defaultRenderer = null;
255
+ function getDefault() {
256
+ if (!defaultRenderer)
257
+ defaultRenderer = new Renderer();
258
+ return defaultRenderer;
259
+ }
260
+ export async function renderChart(options) {
261
+ return getDefault().render(options);
262
+ }
263
+ export async function closeBrowser() {
264
+ if (defaultRenderer) {
265
+ await defaultRenderer.close();
266
+ defaultRenderer = null;
267
+ }
268
+ }
269
+ export function rendererStats() {
270
+ if (!defaultRenderer) {
271
+ const safetyNetSeconds = defaultSafetyNetMs(DEFAULT_MAX_RENDER_TIME_MS) / 1000;
272
+ return {
273
+ browserConnected: false,
274
+ concurrency: { max: DEFAULT_MAX_CONCURRENCY, active: 0, pending: 0 },
275
+ activePages: 0,
276
+ maxRenderTimeSeconds: DEFAULT_MAX_RENDER_TIME_MS / 1000,
277
+ pageSafetyNetSeconds: safetyNetSeconds,
278
+ pageTimeoutSeconds: safetyNetSeconds, // deprecated alias
279
+ };
280
+ }
281
+ return defaultRenderer.stats();
282
+ }
283
+ //# sourceMappingURL=renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.js","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,sEAAsE;AACtE,mEAAmE;AACnE,qEAAqE;AACrE,qEAAqE;AACrE,8DAA8D;AAC9D,aAAa;AACb,EAAE;AACF,qEAAqE;AACrE,sEAAsE;AACtE,mDAAmD;AACnD,OAAO,SAAsC,MAAM,gBAAgB,CAAA;AACnE,OAAO,EAAE,SAAS,EAAsB,MAAM,YAAY,CAAA;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AA2DpD,MAAM,aAAa,GAA2B;IAC5C,GAAG,EAAE,WAAW;IAChB,IAAI,EAAE,YAAY;CACnB,CAAA;AAED,MAAM,uBAAuB,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,CAAA;AACtE,MAAM,0BAA0B,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,IAAI,CAAC,GAAG,IAAI,CAAA;AAE7F,SAAS,kBAAkB,CAAC,eAAuB;IACjD,gEAAgE;IAChE,0CAA0C;IAC1C,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,CAAC;QACrC,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAA;IACxD,CAAC;IACD,gEAAgE;IAChE,mDAAmD;IACnD,OAAO,eAAe,GAAG,CAAC,GAAG,MAAM,CAAA;AACrC,CAAC;AAED,uEAAuE;AACvE,MAAM,wBAAwB,GAAG;IAC/B,+BAA+B;IAC/B,oCAAoC;CACrC,CAAA;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,MAAM,OAAO,QAAQ;IACF,cAAc,CAAQ;IACtB,eAAe,CAAQ;IACvB,eAAe,CAAQ;IACvB,SAAS,CAAW;IAE7B,OAAO,GAAmB,IAAI,CAAA;IAC9B,SAAS,GAAG,KAAK,CAAA;IACjB,aAAa,GAA4B,IAAI,CAAA;IAErD,kEAAkE;IAClE,mEAAmE;IACnE,6CAA6C;IAC5B,WAAW,GAAG,IAAI,GAAG,EAAwB,CAAA;IAE9D,YAAY,SAAyB,EAAE;QACrC,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,uBAAuB,CAAA;QACtE,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,IAAI,0BAA0B,CAAA;QAC3E,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,IAAI,kBAAkB,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;QACzF,IAAI,CAAC,SAAS,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;IACrD,CAAC;IAED,4EAA4E;IAEpE,KAAK,CAAC,aAAa;QACzB,MAAM,QAAQ,GAAG,MAAM,uBAAuB,EAAE,CAAA;QAEhD,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC;YAC/B,cAAc,EAAE,QAAQ;YACxB,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,CAAC,cAAc,EAAE,0BAA0B,EAAE,eAAe,EAAE,yBAAyB,CAAC;SAC/F,CAAC,CAAA;QAEF,CAAC,CAAC,EAAE,CAAC,cAAc,EAAE,GAAG,EAAE;YACxB,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAA;YAC7D,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;YACnB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;YACtB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAA;QAC3B,CAAC,CAAC,CAAA;QAEF,OAAO,CAAC,CAAA;IACV,CAAC;IAEO,KAAK,CAAC,aAAa;QACzB,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YAC3C,OAAO,IAAI,CAAC,OAAO,CAAA;QACrB,CAAC;QAED,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC,aAAa,CAAA;QAC3B,CAAC;QAED,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;QACrB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,EAAE;aACtC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YACV,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;YACtB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAA;YAChB,OAAO,CAAC,GAAG,CAAC,6CAA6C,IAAI,CAAC,cAAc,GAAG,CAAC,CAAA;YAChF,OAAO,CAAC,CAAA;QACV,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACb,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;YACtB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAA;YACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;YACnB,MAAM,GAAG,CAAA;QACX,CAAC,CAAC,CAAA;QAEJ,OAAO,IAAI,CAAC,aAAa,CAAA;IAC3B,CAAC;IAED,2EAA2E;IAEnE,mBAAmB,CAAC,IAAU;QACpC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,IAAI,EAAE;YAClC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YAC7B,IAAI,CAAC;gBACH,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;oBACrB,OAAO,CAAC,IAAI,CACV,qCAAqC,IAAI,CAAC,eAAe,oCAAoC;wBAC3F,yEAAyE,CAC5E,CAAA;oBACD,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;gBACpB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,sBAAsB;YACxB,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,CAAA;QACxB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACnC,CAAC;IAEO,gBAAgB,CAAC,IAAU;QACjC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACxC,IAAI,KAAK,EAAE,CAAC;YACV,YAAY,CAAC,KAAK,CAAC,CAAA;YACnB,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC;IAED,2EAA2E;IAE3E,KAAK,CAAC,MAAM,CAAC,OAAsB;QACjC,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;QACjC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,CAAA;QACtC,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,WAAW,CAAA;QAExD,iEAAiE;QACjE,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;QAC7B,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;QACrG,CAAC;QAED,wDAAwD;QACxD,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAA;QAE9B,IAAI,CAAC;YACH,8DAA8D;YAC9D,gDAAgD;YAChD,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;YAClC,IAAI,WAAW,EAAE,CAAC;gBAChB,OAAO;oBACL,MAAM,EAAE,WAAW,CAAC,MAAM;oBAC1B,IAAI;oBACJ,WAAW,EAAE,WAAW,CAAC,WAAW;oBACpC,MAAM,EAAE,IAAI;oBACZ,QAAQ,EAAE,EAAE;iBACb,CAAA;YACH,CAAC;YAED,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,CAAA;YACpC,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,OAAO,EAAE,CAAA;YAC9B,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAA;YAE9B,wEAAwE;YACxE,MAAM,QAAQ,GAAqB,EAAE,CAAA;YACrC,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;gBACzB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAY,CAAA;gBACjC,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC9D,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;oBACvB,IAAI,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;wBAAE,OAAM;oBAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;gBACnG,CAAC;YACH,CAAC,CAAC,CAAA;YACF,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,GAAY,EAAE,EAAE;gBACpC,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YAC9F,CAAC,CAAC,CAAA;YAEF,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,CAAA;gBAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,GAAG,CAAA;gBAClC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,GAAG,CAAA;gBAEpC,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;gBACzC,kEAAkE;gBAClE,8DAA8D;gBAC9D,6DAA6D;gBAC7D,mCAAmC;gBACnC,MAAM,OAAO,GAAG,+BAA+B,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAA;gBAC1E,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC,CAAA;gBACtF,MAAM,IAAI,CAAC,eAAe,CAAC,iCAAiC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC,CAAA;gBAChG,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAA;gBAE5C,uEAAuE;gBACvE,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAE,MAAc,CAAC,eAAe,IAAI,EAAE,CAAC,CAAA;gBACrF,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;oBAC7B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,IAAI,QAAQ,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;wBAC/F,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;oBACvD,CAAC;gBACH,CAAC;gBAED,uDAAuD;gBACvD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAE,MAAc,CAAC,YAAY,CAAC,CAAA;gBAC1E,IAAI,UAAU,EAAE,CAAC;oBACf,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,EAAE,CAAC;wBACpD,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAA;oBACxD,CAAC;gBACH,CAAC;gBAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAA;gBAClD,IAAI,CAAC,SAAS;oBAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;gBAEpE,MAAM,iBAAiB,GAA4B;oBACjD,IAAI,EAAE,MAAM;oBACZ,cAAc,EAAE,OAAO,CAAC,eAAe,KAAK,aAAa;iBAC1D,CAAA;gBACD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;oBACtB,iBAAiB,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAA;gBACnD,CAAC;gBAED,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAA;gBAC/D,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;gBAErC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,CAAA;gBAEnC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;YAC/D,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAA;gBAC3B,IAAI,CAAC;oBACH,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;wBAAE,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;gBAC1C,CAAC;gBAAC,MAAM,CAAC;oBACP,SAAS;gBACX,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAA;QAC1B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC7C,YAAY,CAAC,KAAK,CAAC,CAAA;YACnB,IAAI,CAAC;gBACH,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;oBAAE,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;YAC1C,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAA;QAExB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;YAC1B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACrB,CAAC;IACH,CAAC;IAED,KAAK;QACH,MAAM,gBAAgB,GAAG,IAAI,CAAC,eAAe,GAAG,IAAI,CAAA;QACpD,OAAO;YACL,gBAAgB,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,IAAI,KAAK;YAClD,WAAW,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE;YACzG,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI;YAClC,oBAAoB,EAAE,IAAI,CAAC,eAAe,GAAG,IAAI;YACjD,oBAAoB,EAAE,gBAAgB;YACtC,kBAAkB,EAAE,gBAAgB,EAAE,mBAAmB;SAC1D,CAAA;IACH,CAAC;CACF;AAED,8EAA8E;AAC9E,yDAAyD;AACzD,8EAA8E;AAE9E,IAAI,eAAe,GAAoB,IAAI,CAAA;AAE3C,SAAS,UAAU;IACjB,IAAI,CAAC,eAAe;QAAE,eAAe,GAAG,IAAI,QAAQ,EAAE,CAAA;IACtD,OAAO,eAAe,CAAA;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAsB;IACtD,OAAO,UAAU,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;AACrC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,IAAI,eAAe,EAAE,CAAC;QACpB,MAAM,eAAe,CAAC,KAAK,EAAE,CAAA;QAC7B,eAAe,GAAG,IAAI,CAAA;IACxB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAAA;QAC9E,OAAO;YACL,gBAAgB,EAAE,KAAK;YACvB,WAAW,EAAE,EAAE,GAAG,EAAE,uBAAuB,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE;YACpE,WAAW,EAAE,CAAC;YACd,oBAAoB,EAAE,0BAA0B,GAAG,IAAI;YACvD,oBAAoB,EAAE,gBAAgB;YACtC,kBAAkB,EAAE,gBAAgB,EAAE,mBAAmB;SAC1D,CAAA;IACH,CAAC;IACD,OAAO,eAAe,CAAC,KAAK,EAAE,CAAA;AAChC,CAAC"}
@@ -0,0 +1,12 @@
1
+ /** Simple async semaphore for concurrency control */
2
+ export declare class Semaphore {
3
+ private readonly max;
4
+ private queue;
5
+ private running;
6
+ constructor(max: number);
7
+ acquire(): Promise<void>;
8
+ release(): void;
9
+ get pending(): number;
10
+ get active(): number;
11
+ }
12
+ //# sourceMappingURL=semaphore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semaphore.d.ts","sourceRoot":"","sources":["../src/semaphore.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,qBAAa,SAAS;IAIR,OAAO,CAAC,QAAQ,CAAC,GAAG;IAHhC,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,OAAO,CAAI;gBAEU,GAAG,EAAE,MAAM;IAElC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAa9B,OAAO,IAAI,IAAI;IAcf,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;CACF"}
@@ -0,0 +1,42 @@
1
+ /** Simple async semaphore for concurrency control */
2
+ export class Semaphore {
3
+ max;
4
+ queue = [];
5
+ running = 0;
6
+ constructor(max) {
7
+ this.max = max;
8
+ }
9
+ async acquire() {
10
+ if (this.running < this.max) {
11
+ this.running++;
12
+ return;
13
+ }
14
+ return new Promise((resolve) => {
15
+ this.queue.push(() => {
16
+ this.running++;
17
+ resolve();
18
+ });
19
+ });
20
+ }
21
+ release() {
22
+ if (this.running <= 0) {
23
+ // release() was called more times than acquire() — always a bug
24
+ // in the caller's try/finally pairing. Refuse to go negative
25
+ // (which would silently break the max-concurrency invariant) and
26
+ // surface the mistake instead.
27
+ console.warn('[semaphore] release() called with no active holders — mismatched acquire/release in caller');
28
+ return;
29
+ }
30
+ this.running--;
31
+ const next = this.queue.shift();
32
+ if (next)
33
+ next();
34
+ }
35
+ get pending() {
36
+ return this.queue.length;
37
+ }
38
+ get active() {
39
+ return this.running;
40
+ }
41
+ }
42
+ //# sourceMappingURL=semaphore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semaphore.js","sourceRoot":"","sources":["../src/semaphore.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,MAAM,OAAO,SAAS;IAIS;IAHrB,KAAK,GAAsB,EAAE,CAAA;IAC7B,OAAO,GAAG,CAAC,CAAA;IAEnB,YAA6B,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;IAAG,CAAC;IAE5C,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,EAAE,CAAA;YACd,OAAM;QACR,CAAC;QACD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE;gBACnB,IAAI,CAAC,OAAO,EAAE,CAAA;gBACd,OAAO,EAAE,CAAA;YACX,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;YACtB,gEAAgE;YAChE,6DAA6D;YAC7D,iEAAiE;YACjE,+BAA+B;YAC/B,OAAO,CAAC,IAAI,CAAC,4FAA4F,CAAC,CAAA;YAC1G,OAAM;QACR,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,CAAA;QACd,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QAC/B,IAAI,IAAI;YAAE,IAAI,EAAE,CAAA;IAClB,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAA;IAC1B,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAA;IACrB,CAAC;CACF"}
@@ -0,0 +1,85 @@
1
+ export declare const LIBS: {
2
+ readonly chartjs: {
3
+ readonly pkg: "chart.js";
4
+ readonly version: "4.4.9";
5
+ readonly file: "dist/chart.umd.min.js";
6
+ };
7
+ readonly datalabels: {
8
+ readonly pkg: "chartjs-plugin-datalabels";
9
+ readonly version: "2.2.0";
10
+ readonly file: "dist/chartjs-plugin-datalabels.min.js";
11
+ };
12
+ readonly annotation: {
13
+ readonly pkg: "chartjs-plugin-annotation";
14
+ readonly version: "3.1.0";
15
+ readonly file: "dist/chartjs-plugin-annotation.min.js";
16
+ };
17
+ readonly zoom: {
18
+ readonly pkg: "chartjs-plugin-zoom";
19
+ readonly version: "2.2.0";
20
+ readonly file: "dist/chartjs-plugin-zoom.min.js";
21
+ };
22
+ readonly gradient: {
23
+ readonly pkg: "chartjs-plugin-gradient";
24
+ readonly version: "0.6.1";
25
+ readonly file: "dist/chartjs-plugin-gradient.min.js";
26
+ };
27
+ readonly matrix: {
28
+ readonly pkg: "chartjs-chart-matrix";
29
+ readonly version: "2.0.1";
30
+ readonly file: "dist/chartjs-chart-matrix.min.js";
31
+ };
32
+ readonly sankey: {
33
+ readonly pkg: "chartjs-chart-sankey";
34
+ readonly version: "0.12.1";
35
+ readonly file: "dist/chartjs-chart-sankey.min.js";
36
+ };
37
+ readonly treemap: {
38
+ readonly pkg: "chartjs-chart-treemap";
39
+ readonly version: "2.3.1";
40
+ readonly file: "dist/chartjs-chart-treemap.min.js";
41
+ };
42
+ readonly wordcloud: {
43
+ readonly pkg: "chartjs-chart-wordcloud";
44
+ readonly version: "4.4.3";
45
+ readonly file: "build/index.umd.min.js";
46
+ };
47
+ readonly geo: {
48
+ readonly pkg: "chartjs-chart-geo";
49
+ readonly version: "4.3.3";
50
+ readonly file: "build/index.umd.min.js";
51
+ };
52
+ readonly graph: {
53
+ readonly pkg: "chartjs-chart-graph";
54
+ readonly version: "4.3.3";
55
+ readonly file: "build/index.umd.min.js";
56
+ };
57
+ readonly venn: {
58
+ readonly pkg: "chartjs-chart-venn";
59
+ readonly version: "4.3.3";
60
+ readonly file: "build/index.umd.min.js";
61
+ };
62
+ readonly adapterDateFns: {
63
+ readonly pkg: "chartjs-adapter-date-fns";
64
+ readonly version: "3.0.0";
65
+ readonly file: "dist/chartjs-adapter-date-fns.bundle.min.js";
66
+ };
67
+ };
68
+ export interface RenderOptions {
69
+ /** Chart.js configuration object */
70
+ chart: Record<string, unknown>;
71
+ /** Canvas width in pixels (default: 800) */
72
+ width?: number;
73
+ /** Canvas height in pixels (default: 600) */
74
+ height?: number;
75
+ /** Device pixel ratio (default: 2) */
76
+ devicePixelRatio?: number;
77
+ /** Background color (default: 'white') */
78
+ backgroundColor?: string;
79
+ /** Output format: png or jpeg (default: 'png') */
80
+ format?: 'png' | 'jpeg';
81
+ /** JPEG quality 0-100 (default: 90) */
82
+ quality?: number;
83
+ }
84
+ export declare function buildHtml(options: RenderOptions): string;
85
+ //# sourceMappingURL=template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqBP,CAAA;AAMV,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,sCAAsC;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,0CAA0C;IAC1C,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,kDAAkD;IAClD,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IACvB,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAiGxD"}
@@ -0,0 +1,120 @@
1
+ // HTML template that loads Chart.js + plugins and renders a chart
2
+ // Chart.js and plugins are loaded from CDN inside the Playwright browser context
3
+ const CDN = 'https://cdn.jsdelivr.net/npm';
4
+ export const LIBS = {
5
+ chartjs: { pkg: 'chart.js', version: '4.4.9', file: 'dist/chart.umd.min.js' },
6
+ // Plugins
7
+ datalabels: { pkg: 'chartjs-plugin-datalabels', version: '2.2.0', file: 'dist/chartjs-plugin-datalabels.min.js' },
8
+ annotation: { pkg: 'chartjs-plugin-annotation', version: '3.1.0', file: 'dist/chartjs-plugin-annotation.min.js' },
9
+ zoom: { pkg: 'chartjs-plugin-zoom', version: '2.2.0', file: 'dist/chartjs-plugin-zoom.min.js' },
10
+ gradient: { pkg: 'chartjs-plugin-gradient', version: '0.6.1', file: 'dist/chartjs-plugin-gradient.min.js' },
11
+ // Additional chart types
12
+ matrix: { pkg: 'chartjs-chart-matrix', version: '2.0.1', file: 'dist/chartjs-chart-matrix.min.js' },
13
+ sankey: { pkg: 'chartjs-chart-sankey', version: '0.12.1', file: 'dist/chartjs-chart-sankey.min.js' },
14
+ treemap: { pkg: 'chartjs-chart-treemap', version: '2.3.1', file: 'dist/chartjs-chart-treemap.min.js' },
15
+ // These four publish UMD under build/, not dist/. jsDelivr returns 404
16
+ // for the dist path and the plugins silently don't register.
17
+ wordcloud: { pkg: 'chartjs-chart-wordcloud', version: '4.4.3', file: 'build/index.umd.min.js' },
18
+ geo: { pkg: 'chartjs-chart-geo', version: '4.3.3', file: 'build/index.umd.min.js' },
19
+ graph: { pkg: 'chartjs-chart-graph', version: '4.3.3', file: 'build/index.umd.min.js' },
20
+ venn: { pkg: 'chartjs-chart-venn', version: '4.3.3', file: 'build/index.umd.min.js' },
21
+ // Date adapter — chartjs-adapter-date-fns.bundle.min.js ships date-fns
22
+ // inside the UMD so no second <script> is needed. (The previously-used
23
+ // chartjs-adapter-dayjs-4 does not ship a browser UMD build.)
24
+ adapterDateFns: { pkg: 'chartjs-adapter-date-fns', version: '3.0.0', file: 'dist/chartjs-adapter-date-fns.bundle.min.js' },
25
+ };
26
+ function cdnUrl(lib) {
27
+ return `${CDN}/${lib.pkg}@${lib.version}/${lib.file}`;
28
+ }
29
+ export function buildHtml(options) {
30
+ const { chart, width = 800, height = 600, devicePixelRatio = 2, backgroundColor = 'white', } = options;
31
+ return `<!DOCTYPE html>
32
+ <html>
33
+ <head>
34
+ <meta charset="utf-8">
35
+ <style>
36
+ * { margin: 0; padding: 0; }
37
+ body { background: ${backgroundColor}; }
38
+ #chart-container {
39
+ width: ${width}px;
40
+ height: ${height}px;
41
+ }
42
+ </style>
43
+ ${Object.values(LIBS)
44
+ .map((lib) => `<script src="${cdnUrl(lib)}"></script>`)
45
+ .join('\n')}
46
+ </head>
47
+ <body>
48
+ <div id="chart-container">
49
+ <canvas id="chart"></canvas>
50
+ </div>
51
+ <script>
52
+ (function() {
53
+ var config = ${JSON.stringify(chart)};
54
+
55
+ // Set device pixel ratio
56
+ config.options = config.options || {};
57
+ config.options.devicePixelRatio = ${devicePixelRatio};
58
+
59
+ // Set responsive
60
+ config.options.responsive = true;
61
+ config.options.maintainAspectRatio = false;
62
+
63
+ // Initialize message capture BEFORE anything that might log, so plugin
64
+ // registration probes / errors surface to the caller.
65
+ window.__chartMessages = [];
66
+ var origWarn = console.warn;
67
+ var origError = console.error;
68
+ console.warn = function() {
69
+ var msg = Array.prototype.slice.call(arguments).join(' ');
70
+ window.__chartMessages.push({ level: 'warn', message: msg });
71
+ origWarn.apply(console, arguments);
72
+ };
73
+ console.error = function() {
74
+ var msg = Array.prototype.slice.call(arguments).join(' ');
75
+ window.__chartMessages.push({ level: 'error', message: msg });
76
+ origError.apply(console, arguments);
77
+ };
78
+
79
+ // Register plugins that don't auto-register. Most community plugins
80
+ // publish their UMD build as a single exported object holding the
81
+ // controllers + elements the plugin adds, and auto-register themselves
82
+ // when the UMD runs. The ones below need manual registration.
83
+ if (window.ChartDataLabels) {
84
+ Chart.register(ChartDataLabels);
85
+ // Default to hidden — show only when explicitly configured
86
+ Chart.defaults.set('plugins.datalabels', { display: false });
87
+ }
88
+ if (window.ChartGeo) {
89
+ Object.values(ChartGeo).forEach(function(v) {
90
+ if (v && v.id) Chart.register(v);
91
+ });
92
+ }
93
+
94
+ // Force all animations off for instant rendering
95
+ config.options.animation = false;
96
+ config.options.animations = { colors: false, x: false, y: false };
97
+ config.options.transitions = {
98
+ active: { animation: { duration: 0 } },
99
+ resize: { animation: { duration: 0 } },
100
+ show: { animation: { duration: 0 } },
101
+ hide: { animation: { duration: 0 } },
102
+ };
103
+
104
+ var canvas = document.getElementById('chart');
105
+ var ctx = canvas.getContext('2d');
106
+ try {
107
+ var myChart = new Chart(ctx, config);
108
+ } catch (e) {
109
+ window.__chartMessages.push({ level: 'error', message: e.message || String(e) });
110
+ window.__chartError = e.message || String(e);
111
+ }
112
+
113
+ // Signal that chart is ready (even on error, so we don't timeout)
114
+ window.__chartRendered = true;
115
+ })();
116
+ </script>
117
+ </body>
118
+ </html>`;
119
+ }
120
+ //# sourceMappingURL=template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,iFAAiF;AAEjF,MAAM,GAAG,GAAG,8BAA8B,CAAA;AAE1C,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,OAAO,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,uBAAuB,EAAE;IAC7E,UAAU;IACV,UAAU,EAAE,EAAE,GAAG,EAAE,2BAA2B,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,uCAAuC,EAAE;IACjH,UAAU,EAAE,EAAE,GAAG,EAAE,2BAA2B,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,uCAAuC,EAAE;IACjH,IAAI,EAAE,EAAE,GAAG,EAAE,qBAAqB,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,iCAAiC,EAAE;IAC/F,QAAQ,EAAE,EAAE,GAAG,EAAE,yBAAyB,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,qCAAqC,EAAE;IAC3G,yBAAyB;IACzB,MAAM,EAAE,EAAE,GAAG,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,kCAAkC,EAAE;IACnG,MAAM,EAAE,EAAE,GAAG,EAAE,sBAAsB,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,kCAAkC,EAAE;IACpG,OAAO,EAAE,EAAE,GAAG,EAAE,uBAAuB,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,mCAAmC,EAAE;IACtG,uEAAuE;IACvE,6DAA6D;IAC7D,SAAS,EAAE,EAAE,GAAG,EAAE,yBAAyB,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,wBAAwB,EAAE;IAC/F,GAAG,EAAE,EAAE,GAAG,EAAE,mBAAmB,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,wBAAwB,EAAE;IACnF,KAAK,EAAE,EAAE,GAAG,EAAE,qBAAqB,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,wBAAwB,EAAE;IACvF,IAAI,EAAE,EAAE,GAAG,EAAE,oBAAoB,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,wBAAwB,EAAE;IACrF,uEAAuE;IACvE,uEAAuE;IACvE,8DAA8D;IAC9D,cAAc,EAAE,EAAE,GAAG,EAAE,0BAA0B,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,6CAA6C,EAAE;CAClH,CAAA;AAEV,SAAS,MAAM,CAAC,GAAqC;IACnD,OAAO,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,IAAI,EAAE,CAAA;AACvD,CAAC;AAmBD,MAAM,UAAU,SAAS,CAAC,OAAsB;IAC9C,MAAM,EACJ,KAAK,EACL,KAAK,GAAG,GAAG,EACX,MAAM,GAAG,GAAG,EACZ,gBAAgB,GAAG,CAAC,EACpB,eAAe,GAAG,OAAO,GAC1B,GAAG,OAAO,CAAA;IAEX,OAAO;;;;;;uBAMc,eAAe;;aAEzB,KAAK;cACJ,MAAM;;;EAGlB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;SACd,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC;SACtD,IAAI,CAAC,IAAI,CAAC;;;;;;;;iBAQA,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;;;;sCAIA,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA6D9C,CAAA;AACR,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare const VERSION: string;
2
+ export declare const NAME: string;
3
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,EAAE,MAAoB,CAAA;AAC1C,eAAO,MAAM,IAAI,EAAE,MAAiB,CAAA"}
@@ -0,0 +1,4 @@
1
+ import pkg from '../package.json';
2
+ export const VERSION = pkg.version;
3
+ export const NAME = pkg.name;
4
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,iBAAiB,CAAA;AAEjC,MAAM,CAAC,MAAM,OAAO,GAAW,GAAG,CAAC,OAAO,CAAA;AAC1C,MAAM,CAAC,MAAM,IAAI,GAAW,GAAG,CAAC,IAAI,CAAA"}