wasm-html-to-image 1.0.11 → 1.0.12

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.
@@ -108,7 +108,8 @@ async function fetchResourceBytesInner(url, baseUrl, userAgent, timeoutMs, confi
108
108
  const headers = userAgent && isNodeRuntime() ? { "User-Agent": userAgent } : undefined;
109
109
  // Abort hung connections so one stalled server cannot outlive `timeoutMs`.
110
110
  const signal = timeoutMs !== undefined
111
- ? AbortSignal.timeout(Math.max(1, timeoutMs))
111
+ ? // Node >= 24 requires an integer delay; the remaining budget is fractional.
112
+ AbortSignal.timeout(Math.max(1, Math.ceil(timeoutMs)))
112
113
  : undefined;
113
114
  const fetchBytes = async (target) => {
114
115
  // Servers may vary the body by User-Agent (e.g. woff2 vs ttf CSS).
@@ -440,7 +441,17 @@ export async function resolveHtmlResources(module, satoruInst, htmls, width, hei
440
441
  // later sheet must not land before (and so lose to) a large earlier one. They are
441
442
  // buffered here and injected in discovery order once the whole round has settled.
442
443
  const loadedCss = [];
443
- await mapWithLimit(pending, FETCH_CONCURRENCY, async (r) => {
444
+ // When maxResourceCount cuts a round short, what is dropped should be the least harmful: a missing
445
+ // image leaves a hole, a missing font turns the whole text into tofu. Stylesheets go first (they
446
+ // cascade by document order and may reveal more), then fonts, then images; discovery order is
447
+ // kept within each kind.
448
+ const rank = (t) => (t === "css" ? 0 : t === "font" ? 1 : 2);
449
+ const ordered = pending
450
+ .map((r, i) => ({ r, i }))
451
+ .sort((a, b) => rank(a.r.type) - rank(b.r.type) || a.i - b.i)
452
+ .map((e) => e.r);
453
+ let fontsSkippedByCount = 0;
454
+ await mapWithLimit(ordered, FETCH_CONCURRENCY, async (r) => {
444
455
  const diag = ctx.diag;
445
456
  const entry = diag
446
457
  ? {
@@ -482,6 +493,8 @@ export async function resolveHtmlResources(module, satoruInst, htmls, width, hei
482
493
  if (ctx.limits.maxResourceCount !== undefined &&
483
494
  ctx.usage.resourceCount >= ctx.limits.maxResourceCount) {
484
495
  const message = `Maximum resource count (${ctx.limits.maxResourceCount}) exceeded`;
496
+ if (r.type === "font")
497
+ fontsSkippedByCount++;
485
498
  skipWithError(DIAGNOSTIC_CODES.LIMIT_RESOURCE_COUNT, message);
486
499
  return;
487
500
  }
@@ -538,6 +551,17 @@ export async function resolveHtmlResources(module, satoruInst, htmls, width, hei
538
551
  diag?.warnings.push(d);
539
552
  }
540
553
  });
554
+ if (fontsSkippedByCount > 0) {
555
+ // Text in those fonts renders as tofu: say what to change instead of leaving a silent bad image.
556
+ const message = `${fontsSkippedByCount} font file(s) were not loaded because maxResourceCount ` +
557
+ `(${ctx.limits.maxResourceCount}) was reached; text using them may render as missing glyphs. ` +
558
+ "Raise limits.maxResourceCount (a Google Fonts stylesheet alone can reference over a hundred files).";
559
+ ctx.diag?.warnings.push({
560
+ code: DIAGNOSTIC_CODES.LIMIT_RESOURCE_COUNT,
561
+ message,
562
+ });
563
+ ctx.emitLog(LogLevel.Warning, message);
564
+ }
541
565
  loadedCss.sort((a, b) => a.index - b.index);
542
566
  for (const css of loadedCss) {
543
567
  const text = new TextDecoder().decode(css.bytes);