@liiift-studio/sanity-font-manager 2.5.5 → 2.5.6

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/dist/index.js CHANGED
@@ -7648,6 +7648,25 @@ var require_unbrotli = __commonJS({
7648
7648
  }
7649
7649
  });
7650
7650
 
7651
+ // src/utils/setupDecompressors.js
7652
+ var import_unbrotli;
7653
+ var init_setupDecompressors = __esm({
7654
+ "src/utils/setupDecompressors.js"() {
7655
+ init_pako_esm();
7656
+ import_unbrotli = __toESM(require_unbrotli());
7657
+ globalThis.pako = pako;
7658
+ if (!globalThis.unbrotli) {
7659
+ try {
7660
+ const brotli = require_unbrotli();
7661
+ if (typeof brotli === "function") {
7662
+ globalThis.unbrotli = brotli;
7663
+ }
7664
+ } catch {
7665
+ }
7666
+ }
7667
+ }
7668
+ });
7669
+
7651
7670
  // node_modules/lib-font/src/utils/shim-fetch.js
7652
7671
  var fetchFunction;
7653
7672
  var init_shim_fetch = __esm({
@@ -12433,31 +12452,38 @@ async function parseFont(buffer, filename) {
12433
12452
  throw new Error(`Font file exceeds ${MAX_FONT_FILE_SIZE / 1024 / 1024}MB limit: ${filename} (${(buffer.byteLength / 1024 / 1024).toFixed(1)}MB)`);
12434
12453
  }
12435
12454
  return new Promise((resolve, reject) => {
12455
+ let settled = false;
12456
+ const settle = (fn) => (...args) => {
12457
+ if (!settled) {
12458
+ settled = true;
12459
+ clearTimeout(timer);
12460
+ fn(...args);
12461
+ }
12462
+ };
12463
+ const timer = setTimeout(() => {
12464
+ settle(reject)(new Error(`Parsing timed out for ${filename} (${PARSE_TIMEOUT_MS / 1e3}s). The file may be corrupted or in an unsupported format.`));
12465
+ }, PARSE_TIMEOUT_MS);
12436
12466
  const font2 = new Font("font", { skipStyleSheet: true });
12437
- font2.onload = (evt) => resolve(evt.detail.font);
12438
- font2.onerror = (evt) => {
12467
+ font2.onload = settle((evt) => resolve(evt.detail.font));
12468
+ font2.onerror = settle((evt) => {
12439
12469
  var _a;
12440
12470
  const msg = ((_a = evt.detail) == null ? void 0 : _a.message) || `Failed to parse ${filename}`;
12441
12471
  reject(new Error(msg));
12442
- };
12472
+ });
12443
12473
  try {
12444
12474
  font2.fromDataBuffer(buffer, filename);
12445
12475
  } catch (err2) {
12446
- reject(new Error(`${filename}: ${err2.message}`));
12476
+ settle(reject)(new Error(`${filename}: ${err2.message}`));
12447
12477
  }
12448
12478
  });
12449
12479
  }
12450
- var import_unbrotli2, MAX_FONT_FILE_SIZE;
12480
+ var MAX_FONT_FILE_SIZE, PARSE_TIMEOUT_MS;
12451
12481
  var init_parseFont = __esm({
12452
12482
  "src/utils/parseFont.js"() {
12453
- init_pako_esm();
12454
- import_unbrotli2 = __toESM(require_unbrotli());
12483
+ init_setupDecompressors();
12455
12484
  init_lib_font();
12456
- globalThis.pako = pako;
12457
- if (!globalThis.unbrotli) {
12458
- console.warn("[parseFont] globalThis.unbrotli not set after vendor import \u2014 WOFF2 parsing may fail. TTF/OTF files will still work.");
12459
- }
12460
12485
  MAX_FONT_FILE_SIZE = 50 * 1024 * 1024;
12486
+ PARSE_TIMEOUT_MS = 3e4;
12461
12487
  }
12462
12488
  });
12463
12489
 
@@ -16871,11 +16897,7 @@ __export(index_exports, {
16871
16897
  useSanityClient: () => useSanityClient
16872
16898
  });
16873
16899
  module.exports = __toCommonJS(index_exports);
16874
-
16875
- // src/utils/setupDecompressors.js
16876
- init_pako_esm();
16877
- var import_unbrotli = __toESM(require_unbrotli());
16878
- globalThis.pako = pako;
16900
+ init_setupDecompressors();
16879
16901
 
16880
16902
  // src/components/BatchUploadFonts.jsx
16881
16903
  var import_react14 = __toESM(require("react"));
package/dist/index.mjs CHANGED
@@ -7653,6 +7653,25 @@ var require_unbrotli = __commonJS({
7653
7653
  }
7654
7654
  });
7655
7655
 
7656
+ // src/utils/setupDecompressors.js
7657
+ var import_unbrotli;
7658
+ var init_setupDecompressors = __esm({
7659
+ "src/utils/setupDecompressors.js"() {
7660
+ init_pako_esm();
7661
+ import_unbrotli = __toESM(require_unbrotli());
7662
+ globalThis.pako = pako;
7663
+ if (!globalThis.unbrotli) {
7664
+ try {
7665
+ const brotli = require_unbrotli();
7666
+ if (typeof brotli === "function") {
7667
+ globalThis.unbrotli = brotli;
7668
+ }
7669
+ } catch {
7670
+ }
7671
+ }
7672
+ }
7673
+ });
7674
+
7656
7675
  // node_modules/lib-font/src/utils/shim-fetch.js
7657
7676
  var fetchFunction;
7658
7677
  var init_shim_fetch = __esm({
@@ -12438,31 +12457,38 @@ async function parseFont(buffer, filename) {
12438
12457
  throw new Error(`Font file exceeds ${MAX_FONT_FILE_SIZE / 1024 / 1024}MB limit: ${filename} (${(buffer.byteLength / 1024 / 1024).toFixed(1)}MB)`);
12439
12458
  }
12440
12459
  return new Promise((resolve, reject) => {
12460
+ let settled = false;
12461
+ const settle = (fn) => (...args) => {
12462
+ if (!settled) {
12463
+ settled = true;
12464
+ clearTimeout(timer);
12465
+ fn(...args);
12466
+ }
12467
+ };
12468
+ const timer = setTimeout(() => {
12469
+ settle(reject)(new Error(`Parsing timed out for ${filename} (${PARSE_TIMEOUT_MS / 1e3}s). The file may be corrupted or in an unsupported format.`));
12470
+ }, PARSE_TIMEOUT_MS);
12441
12471
  const font2 = new Font("font", { skipStyleSheet: true });
12442
- font2.onload = (evt) => resolve(evt.detail.font);
12443
- font2.onerror = (evt) => {
12472
+ font2.onload = settle((evt) => resolve(evt.detail.font));
12473
+ font2.onerror = settle((evt) => {
12444
12474
  var _a;
12445
12475
  const msg = ((_a = evt.detail) == null ? void 0 : _a.message) || `Failed to parse ${filename}`;
12446
12476
  reject(new Error(msg));
12447
- };
12477
+ });
12448
12478
  try {
12449
12479
  font2.fromDataBuffer(buffer, filename);
12450
12480
  } catch (err2) {
12451
- reject(new Error(`${filename}: ${err2.message}`));
12481
+ settle(reject)(new Error(`${filename}: ${err2.message}`));
12452
12482
  }
12453
12483
  });
12454
12484
  }
12455
- var import_unbrotli2, MAX_FONT_FILE_SIZE;
12485
+ var MAX_FONT_FILE_SIZE, PARSE_TIMEOUT_MS;
12456
12486
  var init_parseFont = __esm({
12457
12487
  "src/utils/parseFont.js"() {
12458
- init_pako_esm();
12459
- import_unbrotli2 = __toESM(require_unbrotli());
12488
+ init_setupDecompressors();
12460
12489
  init_lib_font();
12461
- globalThis.pako = pako;
12462
- if (!globalThis.unbrotli) {
12463
- console.warn("[parseFont] globalThis.unbrotli not set after vendor import \u2014 WOFF2 parsing may fail. TTF/OTF files will still work.");
12464
- }
12465
12490
  MAX_FONT_FILE_SIZE = 50 * 1024 * 1024;
12491
+ PARSE_TIMEOUT_MS = 3e4;
12466
12492
  }
12467
12493
  });
12468
12494
 
@@ -16770,10 +16796,8 @@ var init_UploadModal = __esm({
16770
16796
  }
16771
16797
  });
16772
16798
 
16773
- // src/utils/setupDecompressors.js
16774
- init_pako_esm();
16775
- var import_unbrotli = __toESM(require_unbrotli());
16776
- globalThis.pako = pako;
16799
+ // src/index.js
16800
+ init_setupDecompressors();
16777
16801
 
16778
16802
  // src/components/BatchUploadFonts.jsx
16779
16803
  import React13, { useCallback as useCallback7, useState as useState9, useMemo as useMemo9, useRef as useRef4, useEffect as useEffect6, lazy as lazy2, Suspense } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liiift-studio/sanity-font-manager",
3
- "version": "2.5.5",
3
+ "version": "2.5.6",
4
4
  "description": "Sanity Studio plugin — full font management suite with batch upload, format conversion, metadata extraction, CSS generation, collection/pair generation, and script variant support. Supports Sanity v3, v4, and v5.",
5
5
  "license": "MIT",
6
6
  "author": "Liiift Studio",
@@ -1,39 +1,22 @@
1
- // Async font parser — ensures decompressor globals are set, with graceful WOFF2 fallback
1
+ // Async font parser — decompressor globals must be set before lib-font is imported
2
2
 
3
- import pako from 'pako';
4
- import '../vendor/unbrotli.js';
3
+ import './setupDecompressors.js';
5
4
  import { Font } from 'lib-font';
6
5
 
7
- // Immediately set globals — this runs at module evaluation time.
8
- // lib-font's woff.js reads globalThis.pako and woff2.js reads globalThis.unbrotli
9
- // at their top level. If this module evaluates before lib-font (which tsup guarantees
10
- // since we import pako and unbrotli first), the globals will be set in time.
11
- //
12
- // When Vite re-bundles our package, it may reorder evaluation so lib-font's woff2.js
13
- // captures globalThis.unbrotli as undefined. In that case, WOFF2 parsing will fail
14
- // and buildUploadPlan falls back to TTF companion metadata.
15
- globalThis.pako = pako;
16
- // unbrotli.js UMD sets globalThis.unbrotli as a side effect — verify it worked
17
- if (!globalThis.unbrotli) {
18
- console.warn('[parseFont] globalThis.unbrotli not set after vendor import — WOFF2 parsing may fail. TTF/OTF files will still work.');
19
- }
20
-
21
6
  /** Maximum font file size accepted for parsing (50 MB) */
22
7
  const MAX_FONT_FILE_SIZE = 50 * 1024 * 1024;
23
8
 
9
+ /** Parse timeout — prevents hanging if lib-font silently fails (30 seconds) */
10
+ const PARSE_TIMEOUT_MS = 30000;
11
+
24
12
  /**
25
13
  * Parse a font file from an ArrayBuffer.
26
14
  * Returns a lib-font Font object with all tables accessible via font.opentype.tables.*.
27
15
  *
28
- * WOFF2 note: If the brotli decoder couldn't be initialized (common with Vite pre-bundling),
29
- * WOFF2 files will fail to parse. The upload plan handles this gracefully — WOFF2 files
30
- * that share a name with a TTF/OTF get metadata from the companion file. Standalone WOFF2
31
- * uploads will show a clear error directing the user to also include TTF/OTF files.
32
- *
33
16
  * @param {ArrayBuffer} buffer - Raw font file bytes
34
17
  * @param {string} filename - Original filename (used for format detection by lib-font)
35
18
  * @returns {Promise<import('lib-font').Font>} Parsed lib-font Font object
36
- * @throws {Error} If the file exceeds MAX_FONT_FILE_SIZE or parsing fails
19
+ * @throws {Error} If the file exceeds MAX_FONT_FILE_SIZE, parsing fails, or times out
37
20
  */
38
21
  export async function parseFont(buffer, filename) {
39
22
  if (buffer.byteLength > MAX_FONT_FILE_SIZE) {
@@ -41,17 +24,33 @@ export async function parseFont(buffer, filename) {
41
24
  }
42
25
 
43
26
  return new Promise((resolve, reject) => {
27
+ let settled = false;
28
+
29
+ const settle = (fn) => (...args) => {
30
+ if (!settled) {
31
+ settled = true;
32
+ clearTimeout(timer);
33
+ fn(...args);
34
+ }
35
+ };
36
+
37
+ // Timeout guard — prevents infinite hang if lib-font fails silently
38
+ const timer = setTimeout(() => {
39
+ settle(reject)(new Error(`Parsing timed out for ${filename} (${PARSE_TIMEOUT_MS / 1000}s). The file may be corrupted or in an unsupported format.`));
40
+ }, PARSE_TIMEOUT_MS);
41
+
44
42
  const font = new Font('font', { skipStyleSheet: true });
45
- font.onload = (evt) => resolve(evt.detail.font);
46
- font.onerror = (evt) => {
43
+ font.onload = settle((evt) => resolve(evt.detail.font));
44
+ font.onerror = settle((evt) => {
47
45
  const msg = evt.detail?.message || `Failed to parse ${filename}`;
48
46
  reject(new Error(msg));
49
- };
47
+ });
48
+
50
49
  try {
51
50
  font.fromDataBuffer(buffer, filename);
52
51
  } catch (err) {
53
- // lib-font may throw synchronously from WOFF2 constructor if brotli decoder is missing
54
- reject(new Error(`${filename}: ${err.message}`));
52
+ // Catches synchronous throws from WOFF2 constructor when brotli is missing
53
+ settle(reject)(new Error(`${filename}: ${err.message}`));
55
54
  }
56
55
  });
57
56
  }
@@ -1,10 +1,27 @@
1
1
  // Sets up globalThis.pako and globalThis.unbrotli for lib-font WOFF/WOFF2 decompression.
2
- // This module MUST be imported before lib-font is ever evaluated.
3
- // It runs synchronously at module evaluation time to set the globals.
2
+ // Must be imported before lib-font.
4
3
 
5
4
  import pako from 'pako';
6
- import '../vendor/unbrotli.js';
7
5
 
6
+ // Set pako for WOFF (zlib) decompression
8
7
  globalThis.pako = pako;
9
- // unbrotli.js is a UMD that sets globalThis.unbrotli as a side effect on evaluation.
10
- // The import above triggers that side effect.
8
+
9
+ // Set unbrotli for WOFF2 (brotli) decompression
10
+ // The vendor unbrotli.js UMD sets globalThis.unbrotli in browser contexts.
11
+ // In Node/bundler contexts it exports via module.exports instead.
12
+ // We use a side-effect import and then check if the global was set.
13
+ import '../vendor/unbrotli.js';
14
+
15
+ // If the UMD didn't set the global (CJS path in Node/bundler), try to require it
16
+ if (!globalThis.unbrotli) {
17
+ try {
18
+ // In bundler context, the UMD file's module.exports is available
19
+ // tsup will resolve this at build time
20
+ const brotli = require('../vendor/unbrotli.js');
21
+ if (typeof brotli === 'function') {
22
+ globalThis.unbrotli = brotli;
23
+ }
24
+ } catch {
25
+ // Silently fail — WOFF2 parsing will error gracefully
26
+ }
27
+ }