@meonode/canvas 1.7.2 → 2.0.1
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 +324 -154
- package/dist/cjs/canvas/canvas.type.d.ts +72 -9
- package/dist/cjs/canvas/canvas.type.d.ts.map +1 -1
- package/dist/cjs/canvas/{chart.canvas.util.d.ts → chart.canvas.d.ts} +2 -2
- package/dist/cjs/canvas/chart.canvas.d.ts.map +1 -0
- package/dist/cjs/canvas/{chart.canvas.util.js → chart.canvas.js} +23 -23
- package/dist/cjs/canvas/chart.canvas.js.map +1 -0
- package/dist/{esm/canvas/grid.canvas.util.d.ts → cjs/canvas/grid.canvas.d.ts} +2 -2
- package/dist/cjs/canvas/grid.canvas.d.ts.map +1 -0
- package/dist/cjs/canvas/{grid.canvas.util.js → grid.canvas.js} +4 -4
- package/dist/cjs/canvas/grid.canvas.js.map +1 -0
- package/dist/cjs/canvas/image.canvas.d.ts +54 -0
- package/dist/cjs/canvas/image.canvas.d.ts.map +1 -0
- package/dist/cjs/canvas/{image.canvas.util.js → image.canvas.js} +40 -140
- package/dist/cjs/canvas/image.canvas.js.map +1 -0
- package/dist/{esm/canvas/layout.canvas.util.d.ts → cjs/canvas/layout.canvas.d.ts} +1 -1
- package/dist/cjs/canvas/layout.canvas.d.ts.map +1 -0
- package/dist/cjs/canvas/{layout.canvas.util.js → layout.canvas.js} +1 -1
- package/dist/cjs/canvas/layout.canvas.js.map +1 -0
- package/dist/{esm/canvas/root.canvas.util.d.ts → cjs/canvas/root.canvas.d.ts} +29 -9
- package/dist/cjs/canvas/root.canvas.d.ts.map +1 -0
- package/dist/cjs/canvas/{root.canvas.util.js → root.canvas.js} +75 -55
- package/dist/cjs/canvas/root.canvas.js.map +1 -0
- package/dist/cjs/canvas/{text.canvas.util.d.ts → text.canvas.d.ts} +2 -2
- package/dist/cjs/canvas/text.canvas.d.ts.map +1 -0
- package/dist/cjs/canvas/{text.canvas.util.js → text.canvas.js} +3 -3
- package/dist/cjs/canvas/text.canvas.js.map +1 -0
- package/dist/cjs/index.d.ts +8 -7
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +19 -17
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/util/disk.cache.d.ts +6 -0
- package/dist/cjs/util/disk.cache.d.ts.map +1 -1
- package/dist/cjs/util/disk.cache.js +39 -0
- package/dist/cjs/util/disk.cache.js.map +1 -1
- package/dist/cjs/worker/render.worker.js +2 -2
- package/dist/cjs/worker/render.worker.js.map +1 -1
- package/dist/esm/canvas/canvas.type.d.ts +72 -9
- package/dist/esm/canvas/canvas.type.d.ts.map +1 -1
- package/dist/esm/canvas/{chart.canvas.util.d.ts → chart.canvas.d.ts} +2 -2
- package/dist/esm/canvas/chart.canvas.d.ts.map +1 -0
- package/dist/esm/canvas/{chart.canvas.util.js → chart.canvas.js} +4 -4
- package/dist/{cjs/canvas/grid.canvas.util.d.ts → esm/canvas/grid.canvas.d.ts} +2 -2
- package/dist/esm/canvas/grid.canvas.d.ts.map +1 -0
- package/dist/esm/canvas/{grid.canvas.util.js → grid.canvas.js} +1 -1
- package/dist/esm/canvas/image.canvas.d.ts +54 -0
- package/dist/esm/canvas/image.canvas.d.ts.map +1 -0
- package/dist/esm/canvas/{image.canvas.util.js → image.canvas.js} +39 -136
- package/dist/{cjs/canvas/layout.canvas.util.d.ts → esm/canvas/layout.canvas.d.ts} +1 -1
- package/dist/esm/canvas/layout.canvas.d.ts.map +1 -0
- package/dist/{cjs/canvas/root.canvas.util.d.ts → esm/canvas/root.canvas.d.ts} +29 -9
- package/dist/esm/canvas/root.canvas.d.ts.map +1 -0
- package/dist/esm/canvas/{root.canvas.util.js → root.canvas.js} +63 -44
- package/dist/esm/canvas/{text.canvas.util.d.ts → text.canvas.d.ts} +2 -2
- package/dist/esm/canvas/text.canvas.d.ts.map +1 -0
- package/dist/esm/canvas/{text.canvas.util.js → text.canvas.js} +1 -1
- package/dist/esm/index.d.ts +8 -7
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +7 -6
- package/dist/esm/util/disk.cache.d.ts +6 -0
- package/dist/esm/util/disk.cache.d.ts.map +1 -1
- package/dist/esm/util/disk.cache.js +38 -1
- package/dist/esm/worker/render.worker.js +1 -1
- package/package.json +3 -1
- package/dist/cjs/canvas/chart.canvas.util.d.ts.map +0 -1
- package/dist/cjs/canvas/chart.canvas.util.js.map +0 -1
- package/dist/cjs/canvas/grid.canvas.util.d.ts.map +0 -1
- package/dist/cjs/canvas/grid.canvas.util.js.map +0 -1
- package/dist/cjs/canvas/image.canvas.util.d.ts +0 -82
- package/dist/cjs/canvas/image.canvas.util.d.ts.map +0 -1
- package/dist/cjs/canvas/image.canvas.util.js.map +0 -1
- package/dist/cjs/canvas/layout.canvas.util.d.ts.map +0 -1
- package/dist/cjs/canvas/layout.canvas.util.js.map +0 -1
- package/dist/cjs/canvas/root.canvas.util.d.ts.map +0 -1
- package/dist/cjs/canvas/root.canvas.util.js.map +0 -1
- package/dist/cjs/canvas/text.canvas.util.d.ts.map +0 -1
- package/dist/cjs/canvas/text.canvas.util.js.map +0 -1
- package/dist/esm/canvas/chart.canvas.util.d.ts.map +0 -1
- package/dist/esm/canvas/grid.canvas.util.d.ts.map +0 -1
- package/dist/esm/canvas/image.canvas.util.d.ts +0 -82
- package/dist/esm/canvas/image.canvas.util.d.ts.map +0 -1
- package/dist/esm/canvas/layout.canvas.util.d.ts.map +0 -1
- package/dist/esm/canvas/root.canvas.util.d.ts.map +0 -1
- package/dist/esm/canvas/text.canvas.util.d.ts.map +0 -1
- /package/dist/esm/canvas/{layout.canvas.util.js → layout.canvas.js} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { loadImage } from 'skia-canvas';
|
|
2
|
-
import { BoxNode } from './layout.canvas.
|
|
2
|
+
import { BoxNode } from './layout.canvas.js';
|
|
3
3
|
import { parseBorderRadius, drawRoundedRectPath } from './canvas.helper.js';
|
|
4
4
|
import { promises } from 'fs';
|
|
5
5
|
import { Style } from '../constant/common.const.js';
|
|
@@ -21,86 +21,6 @@ function calculateOffsetFromValue(positionValue, availableSpace) {
|
|
|
21
21
|
console.warn(`[ImageNode] Invalid objectPosition value format: ${value}. Defaulting to 50%.`);
|
|
22
22
|
return availableSpace * 0.5;
|
|
23
23
|
}
|
|
24
|
-
/**
|
|
25
|
-
* A simple LRU cache for resolved `CanvasImage` objects.
|
|
26
|
-
*
|
|
27
|
-
* - Persists across render passes so repeated renders of the same tree don't
|
|
28
|
-
* re-fetch every image.
|
|
29
|
-
* - Bounded by `maxSize` entries; least-recently-used entries are evicted first.
|
|
30
|
-
* - Call `dispose()` to eagerly release all held images, or rely on the
|
|
31
|
-
* automatic `process.on('exit')` hook that clears the singleton.
|
|
32
|
-
*/
|
|
33
|
-
class ImageLRUCache {
|
|
34
|
-
map = new Map();
|
|
35
|
-
maxSize;
|
|
36
|
-
constructor(maxSize) {
|
|
37
|
-
this.maxSize = maxSize;
|
|
38
|
-
}
|
|
39
|
-
get(key) {
|
|
40
|
-
const entry = this.map.get(key);
|
|
41
|
-
if (!entry)
|
|
42
|
-
return undefined;
|
|
43
|
-
// Move to end (most-recently used)
|
|
44
|
-
this.map.delete(key);
|
|
45
|
-
this.map.set(key, entry);
|
|
46
|
-
return entry.image;
|
|
47
|
-
}
|
|
48
|
-
set(key, image) {
|
|
49
|
-
// If key already exists, refresh it
|
|
50
|
-
if (this.map.has(key)) {
|
|
51
|
-
this.map.delete(key);
|
|
52
|
-
}
|
|
53
|
-
// Evict oldest if at capacity
|
|
54
|
-
while (this.map.size >= this.maxSize) {
|
|
55
|
-
const oldest = this.map.keys().next().value;
|
|
56
|
-
this.map.delete(oldest);
|
|
57
|
-
}
|
|
58
|
-
this.map.set(key, { image, key });
|
|
59
|
-
}
|
|
60
|
-
has(key) {
|
|
61
|
-
return this.map.has(key);
|
|
62
|
-
}
|
|
63
|
-
get size() {
|
|
64
|
-
return this.map.size;
|
|
65
|
-
}
|
|
66
|
-
dispose() {
|
|
67
|
-
this.map.clear();
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
/** Module-level singleton — lazily created on first render. */
|
|
71
|
-
let _globalImageCache = null;
|
|
72
|
-
const DEFAULT_CACHE_SIZE = 128;
|
|
73
|
-
// Symbol key on process to track hook registration across module reloads (e.g. Jest resetModules)
|
|
74
|
-
const HOOK_KEY = Symbol.for('__meonode_canvas_image_cache_hook__');
|
|
75
|
-
/**
|
|
76
|
-
* Returns the singleton `ImageLRUCache`, creating it on first access.
|
|
77
|
-
* Registers a one-time process cleanup hook to clear the cache
|
|
78
|
-
* so native image buffers are freed when the process shuts down.
|
|
79
|
-
*/
|
|
80
|
-
function getImageCache(maxSize = DEFAULT_CACHE_SIZE) {
|
|
81
|
-
if (!_globalImageCache) {
|
|
82
|
-
_globalImageCache = new ImageLRUCache(maxSize);
|
|
83
|
-
}
|
|
84
|
-
if (!globalThis[HOOK_KEY]) {
|
|
85
|
-
globalThis[HOOK_KEY] = true;
|
|
86
|
-
const cleanup = () => {
|
|
87
|
-
_globalImageCache?.dispose();
|
|
88
|
-
_globalImageCache = null;
|
|
89
|
-
};
|
|
90
|
-
process.once('exit', cleanup);
|
|
91
|
-
process.once('SIGINT', cleanup);
|
|
92
|
-
process.once('SIGTERM', cleanup);
|
|
93
|
-
}
|
|
94
|
-
return _globalImageCache;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Explicitly disposes the global image cache.
|
|
98
|
-
* Useful in tests or when tearing down the rendering engine.
|
|
99
|
-
*/
|
|
100
|
-
function disposeImageCache() {
|
|
101
|
-
_globalImageCache?.dispose();
|
|
102
|
-
_globalImageCache = null;
|
|
103
|
-
}
|
|
104
24
|
/**
|
|
105
25
|
* Renders images with configurable sizing, positioning, and effects.
|
|
106
26
|
* Supports object-fit modes, positioning, border radius, and saturation filters.
|
|
@@ -120,9 +40,9 @@ class ImageNode extends BoxNode {
|
|
|
120
40
|
...props,
|
|
121
41
|
};
|
|
122
42
|
}
|
|
123
|
-
load(cache) {
|
|
43
|
+
load(cache, diskCacheKeys) {
|
|
124
44
|
if (!this.loadingPromise) {
|
|
125
|
-
this.loadingPromise = this._loadImage(cache);
|
|
45
|
+
this.loadingPromise = this._loadImage(cache, diskCacheKeys);
|
|
126
46
|
}
|
|
127
47
|
return this.loadingPromise;
|
|
128
48
|
}
|
|
@@ -130,10 +50,10 @@ class ImageNode extends BoxNode {
|
|
|
130
50
|
* Fetches and processes the image source into a CanvasImage.
|
|
131
51
|
* Does not touch node state — pure fetch logic.
|
|
132
52
|
*
|
|
133
|
-
* If `diskCacheKey`
|
|
134
|
-
* disk
|
|
53
|
+
* If `diskCacheKey` and `diskCacheKeys` are provided, the resolved image buffer
|
|
54
|
+
* is written to disk and the key is recorded so the caller can clean it up later.
|
|
135
55
|
*/
|
|
136
|
-
async _fetchCanvasImage(diskCacheKey) {
|
|
56
|
+
async _fetchCanvasImage(diskCacheKey, diskCacheKeys) {
|
|
137
57
|
const { fileTypeFromBuffer, fileTypeFromFile } = await import('file-type');
|
|
138
58
|
let finalSource = this.props.src;
|
|
139
59
|
let isSvg = false;
|
|
@@ -192,11 +112,13 @@ class ImageNode extends BoxNode {
|
|
|
192
112
|
const modifiedSvgString = svgString.replace(/fill="[^"]*"/g, `fill="${this.props.color}"`);
|
|
193
113
|
finalSource = modifiedSvgString !== svgString ? Buffer.from(modifiedSvgString) : contentBuffer;
|
|
194
114
|
}
|
|
195
|
-
// Write
|
|
196
|
-
if (diskCacheKey) {
|
|
115
|
+
// Write to disk and track the key so the render owner can clean it up
|
|
116
|
+
if (diskCacheKey && diskCacheKeys) {
|
|
197
117
|
const cacheBuffer = Buffer.isBuffer(finalSource) ? finalSource : contentBuffer;
|
|
198
|
-
if (cacheBuffer)
|
|
199
|
-
writeDiskCache(diskCacheKey, cacheBuffer);
|
|
118
|
+
if (cacheBuffer) {
|
|
119
|
+
await writeDiskCache(diskCacheKey, cacheBuffer);
|
|
120
|
+
diskCacheKeys.add(diskCacheKey);
|
|
121
|
+
}
|
|
200
122
|
}
|
|
201
123
|
return loadImage(finalSource);
|
|
202
124
|
}
|
|
@@ -204,14 +126,15 @@ class ImageNode extends BoxNode {
|
|
|
204
126
|
* Loads and processes an image.
|
|
205
127
|
*
|
|
206
128
|
* Resolution order:
|
|
207
|
-
* 1.
|
|
208
|
-
* 2.
|
|
209
|
-
*
|
|
210
|
-
*
|
|
129
|
+
* 1. Disk cache at `.cache/files/<hash>` — survives process restarts.
|
|
130
|
+
* 2. Per-render dedup cache — avoids duplicate in-flight fetches when
|
|
131
|
+
* multiple ImageNodes share the same src within one render pass.
|
|
132
|
+
* 3. Fresh fetch via `_fetchCanvasImage()` — writes buffer to disk cache.
|
|
211
133
|
*
|
|
212
134
|
* Buffer sources use a SHA-256 hash as their cache key (same as string sources).
|
|
135
|
+
* All resolved images are released when the render completes (no cross-render retention).
|
|
213
136
|
*/
|
|
214
|
-
_loadImage(cache) {
|
|
137
|
+
_loadImage(cache, diskCacheKeys) {
|
|
215
138
|
if (!this.props.src) {
|
|
216
139
|
const aspectRatioFromProps = typeof this.props.aspectRatio === 'number' && this.props.aspectRatio > 0 ? this.props.aspectRatio : undefined;
|
|
217
140
|
this.node.setAspectRatio(aspectRatioFromProps);
|
|
@@ -222,56 +145,36 @@ class ImageNode extends BoxNode {
|
|
|
222
145
|
return new Promise(resolve => {
|
|
223
146
|
const load = async () => {
|
|
224
147
|
try {
|
|
225
|
-
const
|
|
226
|
-
const cacheKey =
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
this.props.onLoad?.();
|
|
243
|
-
resolve();
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
// 2. Check disk cache (persists across process restarts)
|
|
247
|
-
const diskBuffer = await readDiskCache(cacheKey);
|
|
248
|
-
if (diskBuffer) {
|
|
249
|
-
const img = await loadImage(diskBuffer);
|
|
250
|
-
lru.set(cacheKey, img);
|
|
251
|
-
this.loadedImage = img;
|
|
252
|
-
this.naturalWidth = img.width;
|
|
253
|
-
this.naturalHeight = img.height;
|
|
254
|
-
const calculatedAspectRatio = img.width > 0 && img.height > 0 ? img.width / img.height : undefined;
|
|
255
|
-
const finalAspectRatio = typeof this.props.aspectRatio === 'number' && this.props.aspectRatio > 0 ? this.props.aspectRatio : calculatedAspectRatio;
|
|
256
|
-
this.node.setAspectRatio(finalAspectRatio);
|
|
257
|
-
this.props.onLoad?.();
|
|
258
|
-
resolve();
|
|
259
|
-
return;
|
|
148
|
+
const srcHash = typeof this.props.src === 'string' ? hashBuffer(Buffer.from(this.props.src)) : hashBuffer(this.props.src);
|
|
149
|
+
const cacheKey = this.props.color ? `${srcHash}|${this.props.color}` : srcHash;
|
|
150
|
+
// 1. Disk cache read — only when disk caching is enabled for this render
|
|
151
|
+
if (diskCacheKeys) {
|
|
152
|
+
const diskBuffer = await readDiskCache(cacheKey);
|
|
153
|
+
if (diskBuffer) {
|
|
154
|
+
const img = await loadImage(diskBuffer);
|
|
155
|
+
this.loadedImage = img;
|
|
156
|
+
this.naturalWidth = img.width;
|
|
157
|
+
this.naturalHeight = img.height;
|
|
158
|
+
const calculatedAspectRatio = img.width > 0 && img.height > 0 ? img.width / img.height : undefined;
|
|
159
|
+
const finalAspectRatio = typeof this.props.aspectRatio === 'number' && this.props.aspectRatio > 0 ? this.props.aspectRatio : calculatedAspectRatio;
|
|
160
|
+
this.node.setAspectRatio(finalAspectRatio);
|
|
161
|
+
this.props.onLoad?.();
|
|
162
|
+
resolve();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
260
165
|
}
|
|
261
|
-
//
|
|
166
|
+
// 2. Per-render memory dedup cache or fresh fetch
|
|
262
167
|
let imagePromise;
|
|
263
168
|
if (cache) {
|
|
264
169
|
if (!cache.has(cacheKey)) {
|
|
265
|
-
cache.set(cacheKey, this._fetchCanvasImage(cacheKey));
|
|
170
|
+
cache.set(cacheKey, this._fetchCanvasImage(diskCacheKeys ? cacheKey : undefined, diskCacheKeys));
|
|
266
171
|
}
|
|
267
172
|
imagePromise = cache.get(cacheKey);
|
|
268
173
|
}
|
|
269
174
|
else {
|
|
270
|
-
imagePromise = this._fetchCanvasImage(cacheKey);
|
|
175
|
+
imagePromise = this._fetchCanvasImage(diskCacheKeys ? cacheKey : undefined, diskCacheKeys);
|
|
271
176
|
}
|
|
272
177
|
const img = await imagePromise;
|
|
273
|
-
// 4. Store in persistent LRU cache
|
|
274
|
-
lru.set(cacheKey, img);
|
|
275
178
|
this.loadedImage = img;
|
|
276
179
|
this.naturalWidth = img.width;
|
|
277
180
|
this.naturalHeight = img.height;
|
|
@@ -452,4 +355,4 @@ const Image = (props) => ({
|
|
|
452
355
|
props: props,
|
|
453
356
|
});
|
|
454
357
|
|
|
455
|
-
export { Image,
|
|
358
|
+
export { Image, ImageNode };
|
|
@@ -119,4 +119,4 @@ export declare class RowNode extends BoxNode {
|
|
|
119
119
|
* @returns {RowNode} New RowNode instance.
|
|
120
120
|
*/
|
|
121
121
|
export declare const Row: ({ children, ...rest }: BoxProps) => CanvasElement;
|
|
122
|
-
//# sourceMappingURL=layout.canvas.
|
|
122
|
+
//# sourceMappingURL=layout.canvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.canvas.d.ts","sourceRoot":"","sources":["../../../src/canvas/layout.canvas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAEnE,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAkB,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAGjG,OAAa,EAAS,IAAI,EAAE,MAAM,4BAA4B,CAAA;AAE9D;;;;GAIG;AACH,qBAAa,OAAO;IAClB;;OAEG;IACH,YAAY,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IAE/B;;OAEG;IACH,IAAI,EAAE,IAAI,CAAA;IAEV;;OAEG;IACH,QAAQ,EAAE,OAAO,EAAE,CAAA;IAEnB;;OAEG;IACH,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAA;IAE3B;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IAEtB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ;;;OAGG;gBACS,KAAK,GAAE,QAAQ,GAAG,SAAc;IAqB5C;;OAEG;IACI,sBAAsB;IAW7B;;;OAGG;IACH,SAAS,CAAC,sBAAsB,CAAC,WAAW,EAAE,QAAQ,GAAG,SAAS;IAkClE;;OAEG;IACH,SAAS,CAAC,aAAa,IAAI,IAAI;IAI/B;;;;OAIG;IACH,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM;IAanD;;;OAGG;IACI,cAAc,IAAI,OAAO;IAiBhC;;OAEG;IACH,SAAS,CAAC,+BAA+B;IAIzC;;;OAGG;IACH,SAAS,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ;IAqInC;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,wBAAwB,EAAE,OAAO,GAAE,MAAU,EAAE,OAAO,GAAE,MAAU;IA+J9E;;;;;;;;OAQG;IACH,SAAS,CAAC,cAAc,CAAC,GAAG,EAAE,wBAAwB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAoR5G;AAWD;;;;GAIG;AACH,eAAO,MAAM,GAAG,GAAI,uBAAuB,QAAQ,KAAG,aAIpD,CAAA;AAEF;;;GAGG;AACH,qBAAa,UAAW,SAAQ,OAAO;gBACzB,KAAK,GAAE,QAAQ,GAAG,SAAc;CAS7C;AAED;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,uBAAuB,QAAQ,KAAG,aAIvD,CAAA;AAEF;;;GAGG;AACH,qBAAa,OAAQ,SAAQ,OAAO;gBACtB,KAAK,GAAE,QAAQ,GAAG,SAAc;CAS7C;AAED;;;;GAIG;AACH,eAAO,MAAM,GAAG,GAAI,uBAAuB,QAAQ,KAAG,aAIpD,CAAA"}
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import { Canvas } from 'skia-canvas';
|
|
2
|
-
import { ColumnNode, BoxNode } from '../canvas/layout.canvas.
|
|
3
|
-
import type { BaseProps, RootProps, CanvasElement } from '../canvas/canvas.type.js';
|
|
2
|
+
import { ColumnNode, BoxNode } from '../canvas/layout.canvas.js';
|
|
3
|
+
import type { BaseProps, RootProps, CanvasElement, RootPropsWithWorker, RootPropsWithoutWorker } from '../canvas/canvas.type.js';
|
|
4
4
|
export declare const _clearRegisteredFonts: () => void;
|
|
5
5
|
export interface CanvasEngineConfig {
|
|
6
6
|
/** Run rendering in worker threads to avoid blocking the event loop (default: true) */
|
|
7
7
|
workerMode?: boolean;
|
|
8
8
|
/** Number of worker threads in the pool (default: os.cpus().length - 1) */
|
|
9
9
|
workers?: number;
|
|
10
|
-
/** Maximum number of resolved images to keep in the persistent LRU cache (default: 128) */
|
|
11
|
-
imageCacheSize?: number;
|
|
12
10
|
}
|
|
13
11
|
/**
|
|
14
12
|
* Configure the canvas rendering engine.
|
|
15
13
|
* Call this once at application startup before rendering.
|
|
14
|
+
* @deprecated Pass workerMode and workers directly to Root() props instead.
|
|
16
15
|
*/
|
|
17
16
|
export declare function configure(options: CanvasEngineConfig): void;
|
|
17
|
+
/**
|
|
18
|
+
* Terminate all worker pools and free worker thread resources.
|
|
19
|
+
* Call this when shutting down a long-running server to clean up immediately.
|
|
20
|
+
* After calling, you must call configure() again before rendering.
|
|
21
|
+
*/
|
|
22
|
+
export declare function terminate(): void;
|
|
18
23
|
/**
|
|
19
24
|
* Converts a CanvasElement tree into actual BoxNode instances.
|
|
20
25
|
* Used both for non-worker rendering (inline tree building) and inside
|
|
@@ -26,6 +31,7 @@ export declare function buildTree(descriptor: CanvasElement): BoxNode;
|
|
|
26
31
|
* Inherits from ColumnNode to provide vertical layout capabilities.
|
|
27
32
|
*/
|
|
28
33
|
export declare class RootNode extends ColumnNode {
|
|
34
|
+
props: RootProps & BaseProps;
|
|
29
35
|
/** The canvas instance used for rendering */
|
|
30
36
|
private canvas;
|
|
31
37
|
/** The 2D rendering context for the canvas */
|
|
@@ -56,10 +62,24 @@ export declare class RootNode extends ColumnNode {
|
|
|
56
62
|
}
|
|
57
63
|
/**
|
|
58
64
|
* Creates and renders a new root node with the given properties.
|
|
59
|
-
*
|
|
60
|
-
*
|
|
65
|
+
* Rendering runs in worker threads by default for non-blocking operation.
|
|
66
|
+
* @example
|
|
67
|
+
* // Worker mode (default) - .release() available
|
|
68
|
+
* const canvas = await Root({ width: 400, children: [...] })
|
|
69
|
+
* canvas.release() // ✓ OK
|
|
70
|
+
* @example
|
|
71
|
+
* // Worker mode explicit - .release() available
|
|
72
|
+
* const canvas = await Root({ width: 400, workerMode: true, workers: 2 })
|
|
73
|
+
* canvas.release() // ✓ OK
|
|
74
|
+
* @example
|
|
75
|
+
* // Non-worker mode - .release() NOT available, workers not allowed
|
|
76
|
+
* const canvas = await Root({ width: 400, workerMode: false })
|
|
77
|
+
* canvas.release() // ✗ TypeScript error
|
|
61
78
|
* @param props Configuration properties for the root node
|
|
62
|
-
* @returns
|
|
79
|
+
* @returns Canvas with .release() in worker mode, plain Canvas otherwise
|
|
63
80
|
*/
|
|
64
|
-
export declare
|
|
65
|
-
|
|
81
|
+
export declare function Root(props: RootPropsWithWorker): Promise<Canvas & {
|
|
82
|
+
release(): void;
|
|
83
|
+
}>;
|
|
84
|
+
export declare function Root(props: RootPropsWithoutWorker): Promise<Canvas>;
|
|
85
|
+
//# sourceMappingURL=root.canvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root.canvas.d.ts","sourceRoot":"","sources":["../../../src/canvas/root.canvas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAA8C,MAAM,aAAa,CAAA;AAEhF,OAAO,EAAE,UAAU,EAAE,OAAO,EAAW,MAAM,2BAA2B,CAAA;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AAmB/H,eAAO,MAAM,qBAAqB,YAEjC,CAAA;AAwBD,MAAM,WAAW,kBAAkB;IACjC,uFAAuF;IACvF,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,kBAAkB,QAGpD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,SAKxB;AAgMD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,aAAa,GAAG,OAAO,CAmB5D;AAED;;;GAGG;AACH,qBAAa,QAAS,SAAQ,UAAU;IAC9B,KAAK,EAAE,SAAS,GAAG,SAAS,CAAA;IACpC,6CAA6C;IAC7C,OAAO,CAAC,MAAM,CAAoB;IAClC,8CAA8C;IAC9C,OAAO,CAAC,GAAG,CAAwC;IACnD,4CAA4C;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,6CAA6C;IAC7C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,4DAA4D;IAC5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAE9B;;;;OAIG;gBACS,KAAK,EAAE,SAAS,GAAG,SAAS;IAoDxC;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAazB;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;CAqDhC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,GAAG;IAAE,OAAO,IAAI,IAAI,CAAA;CAAE,CAAC,CAAA;AACvF,wBAAgB,IAAI,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { FontLibrary, Canvas } from 'skia-canvas';
|
|
2
|
-
import { ColumnNode, RowNode, BoxNode } from './layout.canvas.
|
|
3
|
-
import { ImageNode
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { ColumnNode, RowNode, BoxNode } from './layout.canvas.js';
|
|
3
|
+
import { ImageNode } from './image.canvas.js';
|
|
4
|
+
import { deleteDiskCache } from '../util/disk.cache.js';
|
|
5
|
+
import { TextNode } from './text.canvas.js';
|
|
6
|
+
import { ChartNode } from './chart.canvas.js';
|
|
7
|
+
import { GridItemNode, GridNode } from './grid.canvas.js';
|
|
7
8
|
import { Style } from '../constant/common.const.js';
|
|
8
9
|
import { WorkerPreProcessor } from './canvas.helper.js';
|
|
9
10
|
import * as path from 'node:path';
|
|
@@ -14,26 +15,48 @@ import { fileURLToPath } from 'node:url';
|
|
|
14
15
|
|
|
15
16
|
/** Registry to track fonts that have already been loaded */
|
|
16
17
|
const registeredFonts = new Map();
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
/**
|
|
19
|
+
* FinalizationRegistry to clean up WorkerCanvas instances that were not explicitly released.
|
|
20
|
+
* This is a safety net — users should still call .release() explicitly.
|
|
21
|
+
*/
|
|
22
|
+
const canvasRegistry = new FinalizationRegistry(heldValue => {
|
|
23
|
+
// Best-effort cleanup — worker may already be terminated
|
|
24
|
+
try {
|
|
25
|
+
// Access workers via a public method or make it accessible
|
|
26
|
+
// For now, just try to send the message and let errors be caught
|
|
27
|
+
if (_workerPool) {
|
|
28
|
+
;
|
|
29
|
+
_workerPool.workers?.[heldValue.workerIdx]?.postMessage({ type: 'release', canvasId: heldValue.canvasId });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Worker already gone — nothing to clean up
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
/** Engine configuration — legacy support for configure() */
|
|
37
|
+
let _defaultWorkerMode = true;
|
|
38
|
+
let _defaultWorkerPoolSize = Math.max(1, cpus().length - 1);
|
|
20
39
|
let _workerPool = null;
|
|
21
40
|
/**
|
|
22
41
|
* Configure the canvas rendering engine.
|
|
23
42
|
* Call this once at application startup before rendering.
|
|
43
|
+
* @deprecated Pass workerMode and workers directly to Root() props instead.
|
|
24
44
|
*/
|
|
25
45
|
function configure(options) {
|
|
26
46
|
if (options.workerMode !== undefined)
|
|
27
|
-
|
|
47
|
+
_defaultWorkerMode = options.workerMode;
|
|
28
48
|
if (options.workers !== undefined)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
49
|
+
_defaultWorkerPoolSize = options.workers;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Terminate all worker pools and free worker thread resources.
|
|
53
|
+
* Call this when shutting down a long-running server to clean up immediately.
|
|
54
|
+
* After calling, you must call configure() again before rendering.
|
|
55
|
+
*/
|
|
56
|
+
function terminate() {
|
|
57
|
+
if (_workerPool) {
|
|
58
|
+
_workerPool.terminate();
|
|
59
|
+
_workerPool = null;
|
|
37
60
|
}
|
|
38
61
|
}
|
|
39
62
|
/**
|
|
@@ -55,6 +78,8 @@ class WorkerCanvas {
|
|
|
55
78
|
this._pool = opts.pool;
|
|
56
79
|
this._workerIdx = opts.workerIdx;
|
|
57
80
|
this._canvasId = opts.canvasId;
|
|
81
|
+
// Register for finalizer-based cleanup if user forgets to call .release()
|
|
82
|
+
canvasRegistry.register(this, { workerIdx: opts.workerIdx, canvasId: opts.canvasId }, this);
|
|
58
83
|
}
|
|
59
84
|
_call(method, ...args) {
|
|
60
85
|
return this._pool.callOnCanvas(this._workerIdx, this._canvasId, method, args);
|
|
@@ -105,6 +130,8 @@ class WorkerCanvas {
|
|
|
105
130
|
/** Release the Canvas from worker memory. Call when done with this object. */
|
|
106
131
|
release() {
|
|
107
132
|
this._pool.releaseCanvas(this._workerIdx, this._canvasId);
|
|
133
|
+
// Unregister from finalizer since we're explicitly cleaning up
|
|
134
|
+
canvasRegistry.unregister(this);
|
|
108
135
|
}
|
|
109
136
|
}
|
|
110
137
|
/** Worker thread pool — routes render and canvas-call messages */
|
|
@@ -295,6 +322,7 @@ class RootNode extends ColumnNode {
|
|
|
295
322
|
* @returns Promise resolving to the rendered Canvas instance
|
|
296
323
|
*/
|
|
297
324
|
async render() {
|
|
325
|
+
const diskCacheKeys = this.props.useDiskCache ? new Set() : undefined;
|
|
298
326
|
try {
|
|
299
327
|
// Step 1: Load all images with a concurrency limit to avoid overwhelming remote sources.
|
|
300
328
|
// A per-render cache deduplicates identical src+color combinations within this render pass.
|
|
@@ -306,7 +334,7 @@ class RootNode extends ColumnNode {
|
|
|
306
334
|
const workers = Array.from({ length: Math.min(CONCURRENCY, queue.length) }, async () => {
|
|
307
335
|
while (queue.length > 0) {
|
|
308
336
|
const node = queue.shift();
|
|
309
|
-
await node.load(imageCache);
|
|
337
|
+
await node.load(imageCache, diskCacheKeys);
|
|
310
338
|
}
|
|
311
339
|
});
|
|
312
340
|
await Promise.allSettled(workers);
|
|
@@ -334,35 +362,26 @@ class RootNode extends ColumnNode {
|
|
|
334
362
|
return this.canvas;
|
|
335
363
|
}
|
|
336
364
|
finally {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
365
|
+
if (diskCacheKeys?.size) {
|
|
366
|
+
await Promise.allSettled([...diskCacheKeys].map(key => deleteDiskCache(key)));
|
|
367
|
+
}
|
|
340
368
|
}
|
|
341
369
|
}
|
|
342
370
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
try {
|
|
352
|
-
if (_workerMode) {
|
|
353
|
-
if (!_workerPool) {
|
|
354
|
-
_workerPool = new WorkerPool(_workerPoolSize);
|
|
355
|
-
}
|
|
356
|
-
const result = await _workerPool.render(props);
|
|
357
|
-
return new WorkerCanvas({ ...result, pool: _workerPool });
|
|
371
|
+
async function Root(props) {
|
|
372
|
+
// Determine worker mode: props override legacy configure()
|
|
373
|
+
const workerMode = props.workerMode ?? _defaultWorkerMode;
|
|
374
|
+
const workerPoolSize = props.workers ?? _defaultWorkerPoolSize;
|
|
375
|
+
if (workerMode) {
|
|
376
|
+
// Lazy initialize worker pool
|
|
377
|
+
if (!_workerPool) {
|
|
378
|
+
_workerPool = new WorkerPool(workerPoolSize);
|
|
358
379
|
}
|
|
359
|
-
|
|
380
|
+
const result = await _workerPool.render(props);
|
|
381
|
+
return new WorkerCanvas({ ...result, pool: _workerPool });
|
|
360
382
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
throw err;
|
|
365
|
-
}
|
|
366
|
-
};
|
|
383
|
+
// Non-worker mode — render directly and return Canvas
|
|
384
|
+
return new RootNode(props).render();
|
|
385
|
+
}
|
|
367
386
|
|
|
368
|
-
export { Root, RootNode, buildTree, configure };
|
|
387
|
+
export { Root, RootNode, buildTree, configure, terminate };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TextProps, CanvasElement } from '../canvas/canvas.type.js';
|
|
2
2
|
import { type CanvasRenderingContext2D } from 'skia-canvas';
|
|
3
|
-
import { BoxNode } from '../canvas/layout.canvas.
|
|
3
|
+
import { BoxNode } from '../canvas/layout.canvas.js';
|
|
4
4
|
/**
|
|
5
5
|
* Node for rendering text content with rich text styling support
|
|
6
6
|
* Supports color and weight variations through HTML-like tags
|
|
@@ -162,4 +162,4 @@ export declare class TextNode extends BoxNode {
|
|
|
162
162
|
* Creates a new TextNode instance with rich text support
|
|
163
163
|
*/
|
|
164
164
|
export declare const Text: (text: number | string, props?: TextProps) => CanvasElement;
|
|
165
|
-
//# sourceMappingURL=text.canvas.
|
|
165
|
+
//# sourceMappingURL=text.canvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.canvas.d.ts","sourceRoot":"","sources":["../../../src/canvas/text.canvas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAe,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACpF,OAAO,EAAU,KAAK,wBAAwB,EAA2B,MAAM,aAAa,CAAA;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAGnD;;;GAGG;AACH,qBAAa,QAAS,SAAQ,OAAO;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAwC;IACzE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,kBAAkB,CAAe;IAEjC,KAAK,EAAE,SAAS,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;gBAElC,IAAI,GAAE,MAAM,GAAG,MAAW,EAAE,KAAK,GAAE,SAAc;IAuB7D;;;;;;;;OAQG;WACW,gBAAgB,CAC5B,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,MAAM,EACZ,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,GAAE;QACL,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;QACpC,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,CAAC,CAAA;QAClC,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,wBAAwB,CAAC,WAAW,CAAC,CAAA;QACjD,YAAY,CAAC,EAAE,wBAAwB,CAAC,cAAc,CAAC,CAAA;KACnD;cAwBW,aAAa,IAAI,IAAI;IAoDxC;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,sBAAsB;IA8B9B;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,aAAa;IA+ErB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,gBAAgB;IAyBxB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAM7B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;IAiCrB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAQjC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,WAAW;IA+NnB;;;;;;;;;OASG;IACH,OAAO,CAAC,YAAY;IA6KpB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAmErB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;;;;;;;;;;;;;;;OAgBG;cACgB,cAAc,CAAC,GAAG,EAAE,wBAAwB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAkXrH;AAED;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,GAAG,MAAM,EAAE,QAAQ,SAAS,KAAG,aAI9D,CAAA"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export * from './constant/common.const.js';
|
|
2
2
|
export * from './canvas/canvas.type.js';
|
|
3
|
-
export { Box, Column, Row, type BoxNode } from './canvas/layout.canvas.
|
|
4
|
-
export { Image
|
|
5
|
-
export { Text } from './canvas/text.canvas.
|
|
6
|
-
export { Root, configure } from './canvas/root.canvas.
|
|
7
|
-
export { GridItem } from './canvas/grid.canvas.
|
|
8
|
-
export { Grid } from './canvas/grid.canvas.
|
|
9
|
-
export { Chart } from './canvas/chart.canvas.
|
|
3
|
+
export { Box, Column, Row, type BoxNode } from './canvas/layout.canvas.js';
|
|
4
|
+
export { Image } from './canvas/image.canvas.js';
|
|
5
|
+
export { Text } from './canvas/text.canvas.js';
|
|
6
|
+
export { Root, configure, terminate } from './canvas/root.canvas.js';
|
|
7
|
+
export { GridItem } from './canvas/grid.canvas.js';
|
|
8
|
+
export { Grid } from './canvas/grid.canvas.js';
|
|
9
|
+
export { Chart } from './canvas/chart.canvas.js';
|
|
10
|
+
export { clearDiskCache } from './util/disk.cache.js';
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,yBAAyB,CAAA;AACvC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,yBAAyB,CAAA;AACvC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAC1E,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAA;AAC9C,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAA;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA"}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { Border, Style } from './constant/common.const.js';
|
|
2
|
-
export { Box, Column, Row } from './canvas/layout.canvas.
|
|
3
|
-
export { Image
|
|
4
|
-
export { Text } from './canvas/text.canvas.
|
|
5
|
-
export { Root, configure } from './canvas/root.canvas.
|
|
6
|
-
export { Grid, GridItem } from './canvas/grid.canvas.
|
|
7
|
-
export { Chart } from './canvas/chart.canvas.
|
|
2
|
+
export { Box, Column, Row } from './canvas/layout.canvas.js';
|
|
3
|
+
export { Image } from './canvas/image.canvas.js';
|
|
4
|
+
export { Text } from './canvas/text.canvas.js';
|
|
5
|
+
export { Root, configure, terminate } from './canvas/root.canvas.js';
|
|
6
|
+
export { Grid, GridItem } from './canvas/grid.canvas.js';
|
|
7
|
+
export { Chart } from './canvas/chart.canvas.js';
|
|
8
|
+
export { clearDiskCache } from './util/disk.cache.js';
|
|
8
9
|
export * from 'yoga-layout';
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
export declare function hashBuffer(buf: Buffer): string;
|
|
2
2
|
export declare function readDiskCache(key: string): Promise<Buffer | null>;
|
|
3
3
|
export declare function writeDiskCache(key: string, data: Buffer): Promise<void>;
|
|
4
|
+
export declare function deleteDiskCache(key: string): Promise<void>;
|
|
5
|
+
/**
|
|
6
|
+
* Delete the entire disk cache directory.
|
|
7
|
+
* Called on process exit to clean up any orphaned cache files.
|
|
8
|
+
*/
|
|
9
|
+
export declare function clearDiskCache(): Promise<void>;
|
|
4
10
|
//# sourceMappingURL=disk.cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"disk.cache.d.ts","sourceRoot":"","sources":["../../../src/util/disk.cache.ts"],"names":[],"mappings":"AAaA,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOvE;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO7E"}
|
|
1
|
+
{"version":3,"file":"disk.cache.d.ts","sourceRoot":"","sources":["../../../src/util/disk.cache.ts"],"names":[],"mappings":"AAaA,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOvE;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO7E;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhE;AAED;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAOpD"}
|
|
@@ -31,5 +31,42 @@ async function writeDiskCache(key, data) {
|
|
|
31
31
|
// best-effort — cache write failures are non-fatal
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
+
async function deleteDiskCache(key) {
|
|
35
|
+
try {
|
|
36
|
+
await promises.unlink(join(CACHE_DIR, key));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// non-fatal — file may not exist if write failed earlier
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Delete the entire disk cache directory.
|
|
44
|
+
* Called on process exit to clean up any orphaned cache files.
|
|
45
|
+
*/
|
|
46
|
+
async function clearDiskCache() {
|
|
47
|
+
_dirEnsured = false;
|
|
48
|
+
try {
|
|
49
|
+
await promises.rm(CACHE_DIR, { recursive: true, force: true });
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// non-fatal — directory may not exist
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Clean up disk cache on process exit to handle crashes mid-render.
|
|
56
|
+
// Guard prevents re-entry: clearDiskCache() is async, so without this the
|
|
57
|
+
// beforeExit → schedule I/O → idle → beforeExit cycle loops forever.
|
|
58
|
+
let _exitCleanupStarted = false;
|
|
59
|
+
process.on('beforeExit', () => {
|
|
60
|
+
if (_exitCleanupStarted)
|
|
61
|
+
return;
|
|
62
|
+
_exitCleanupStarted = true;
|
|
63
|
+
void clearDiskCache();
|
|
64
|
+
});
|
|
65
|
+
// Also clean up on SIGINT/SIGTERM for graceful shutdowns
|
|
66
|
+
const cleanupOnExit = () => {
|
|
67
|
+
clearDiskCache().finally(() => process.exit(0));
|
|
68
|
+
};
|
|
69
|
+
process.on('SIGINT', cleanupOnExit);
|
|
70
|
+
process.on('SIGTERM', cleanupOnExit);
|
|
34
71
|
|
|
35
|
-
export { hashBuffer, readDiskCache, writeDiskCache };
|
|
72
|
+
export { clearDiskCache, deleteDiskCache, hashBuffer, readDiskCache, writeDiskCache };
|