@quillmark/wasm 0.130.0 → 0.131.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.
@@ -470,10 +470,22 @@ export class Engine {
470
470
  */
471
471
  /** load → Promise<build module>, memoized so each build loads once. */
472
472
  #modules = new Map();
473
- /** load → that build's engine instance (the WASM backend registry). */
473
+ /**
474
+ * load → `{ engine }`, that build's engine instance (the WASM backend
475
+ * registry), or `{ refused }`, the error its construction threw. A refusal
476
+ * is the font library's, which every retry would parse and refuse again.
477
+ */
474
478
  #engines = new Map();
475
479
  /** backendId → descriptor `{ load, formats }`. */
476
480
  #loaders;
481
+ /**
482
+ * The host's font library, copied at construction and handed to each
483
+ * build's engine as it loads; `null` once every build has had it.
484
+ * @type {Uint8Array[] | null}
485
+ */
486
+ #fonts;
487
+ /** How many distinct builds the loaders name. */
488
+ #builds;
477
489
  /**
478
490
  * load → WeakMap<canonical Quill, build-memory clone>, caching the
479
491
  * expensive materialization. WeakMap so dropping the canonical quill makes
@@ -486,10 +498,15 @@ export class Engine {
486
498
  * `load` must resolve to a READY module: a registrant shipping its own
487
499
  * `--target web` build instantiates inside the thunk, and more than one
488
500
  * `Engine` may call it, so memoize (the built-ins do, at module scope).
489
- * @param {{ backends?: Record<string, { load: () => Promise<unknown>, formats: string[] }> }} [options]
501
+ * @param {{ backends?: Record<string, { load: () => Promise<unknown>, formats: string[] }>, fonts?: Uint8Array[] }} [options]
490
502
  */
491
503
  constructor(options) {
492
- rejectUnknownKeys(options, ['backends'], 'Engine options');
504
+ rejectUnknownKeys(options, ['backends', 'fonts'], 'Engine options');
505
+ const fonts = options?.fonts ?? [];
506
+ if (!Array.isArray(fonts) || !fonts.every((font) => font instanceof Uint8Array)) {
507
+ throw new Error('Engine: `fonts` must be an array of Uint8Array.');
508
+ }
509
+ this.#fonts = fonts.map((font) => font.slice());
493
510
  const merged = { ...DEFAULT_BACKENDS, ...(options?.backends ?? {}) };
494
511
  /** @type {Record<string, { load: () => Promise<unknown>, formats: string[] }>} */
495
512
  const loaders = {};
@@ -497,6 +514,7 @@ export class Engine {
497
514
  loaders[id] = validateBackend(id, entry);
498
515
  }
499
516
  this.#loaders = loaders;
517
+ this.#builds = new Set(Object.values(loaders).map((descriptor) => descriptor.load)).size;
500
518
  }
501
519
 
502
520
  /**
@@ -554,12 +572,18 @@ export class Engine {
554
572
  }
555
573
  const mod = await modPromise;
556
574
 
557
- let engine = this.#engines.get(load);
558
- if (!engine) {
559
- engine = new mod.Engine();
560
- this.#engines.set(load, engine);
575
+ let built = this.#engines.get(load);
576
+ if (!built) {
577
+ try {
578
+ built = { engine: new mod.Engine(this.#fonts) };
579
+ } catch (refused) {
580
+ built = { refused };
581
+ }
582
+ this.#engines.set(load, built);
583
+ if (this.#engines.size === this.#builds) this.#fonts = null;
561
584
  }
562
- return { mod, engine };
585
+ if ('refused' in built) throw built.refused;
586
+ return { mod, engine: built.engine };
563
587
  }
564
588
 
565
589
  /**
@@ -658,6 +682,36 @@ export class Engine {
658
682
  );
659
683
  }
660
684
 
685
+ /**
686
+ * Run `fn` against `quill`'s build-memory clone, `quill` snapshotted before
687
+ * the first await as `#withClones` snapshots it.
688
+ * @param {string} method the caller's name, for the rejection message
689
+ * @param {Quill} quill
690
+ * @param {(ctx: { engine: any, quill: any }) => any} fn
691
+ */
692
+ async #withQuill(method, quill, fn) {
693
+ const backendId = this.#backendOf(quill, method);
694
+ const quillTree = this.#quillClones.get(this.#descriptorFor(backendId).load)?.has(quill)
695
+ ? null
696
+ : quill.toTree();
697
+ const { mod, engine } = await this.#resolveBackend(backendId);
698
+ try {
699
+ return fn({ engine, quill: this.#cachedQuillClone(mod, backendId, quill, quillTree) });
700
+ } catch (err) {
701
+ throw backendTrap(err);
702
+ }
703
+ }
704
+
705
+ /**
706
+ * @param {Quill} quill
707
+ * @returns {Promise<import('./runtime.d.ts').FontFace[]>}
708
+ */
709
+ async fontFaces(quill) {
710
+ return this.#withQuill('engine.fontFaces(quill)', quill, ({ engine, quill: q }) =>
711
+ typeof engine.fontFaces === 'function' ? engine.fontFaces(q) : []
712
+ );
713
+ }
714
+
661
715
  /**
662
716
  * @param {Quill} quill
663
717
  * @returns {Promise<void>}