@takumi-rs/wasm 2.0.0-beta.5 → 2.0.0-beta.7

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/bundlers/vite.mjs CHANGED
@@ -1,18 +1,31 @@
1
1
  import url from "../pkg/takumi_wasm_bg.wasm?url";
2
2
 
3
+ // `?url` is a browser path; map it to a file for server reads. Vite emits the asset next to the
4
+ // importing server chunk, so resolve relative to import.meta.url, not the framework output dir.
3
5
  async function processUrl() {
4
6
  if (typeof process !== "undefined" && process.versions?.node != null) {
5
7
  const { readFile } = await import("node:fs/promises");
8
+ const path = decodeURIComponent(url.replace(/[?#].*$/, ""));
6
9
 
7
- if (!url.startsWith("/")) {
8
- return readFile(new URL(url, import.meta.url));
10
+ // Dev SSR serves an `/@fs/<abs-path>` URL.
11
+ if (path.startsWith("/@fs/")) {
12
+ return readFile(path.slice("/@fs".length));
9
13
  }
10
14
 
11
- for (const candidate of [`../client${url}`, `../../client${url}`]) {
15
+ if (!path.startsWith("/")) {
16
+ return readFile(new URL(path, import.meta.url));
17
+ }
18
+
19
+ const basename = path.slice(path.lastIndexOf("/") + 1);
20
+ const candidates = [`./${basename}`, `../client${path}`, `../../client${path}`];
21
+
22
+ let lastError;
23
+ for (const candidate of candidates) {
12
24
  try {
13
25
  return await readFile(new URL(candidate, import.meta.url));
14
26
  } catch (error) {
15
27
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
28
+ lastError = error;
16
29
  continue;
17
30
  }
18
31
 
@@ -20,7 +33,7 @@ async function processUrl() {
20
33
  }
21
34
  }
22
35
 
23
- throw new Error(`Unable to locate Takumi WASM asset for SSR: ${url}`);
36
+ throw new Error(`Unable to locate Takumi WASM asset for SSR: ${url}`, { cause: lastError });
24
37
  }
25
38
 
26
39
  return fetch(new URL(url, import.meta.url)).then((response) => response.arrayBuffer());
package/dist/export.cjs CHANGED
@@ -564,16 +564,32 @@ async function resolveImageLoaders(images) {
564
564
  })));
565
565
  }
566
566
  var Renderer = class {
567
- fontMapping = /* @__PURE__ */ new Map();
567
+ fontsByKey = /* @__PURE__ */ new Map();
568
+ fontsByData = /* @__PURE__ */ new WeakMap();
568
569
  inner = new Renderer$1();
570
+ getFont(key) {
571
+ return typeof key === "string" ? this.fontsByKey.get(key) : this.fontsByData.get(key);
572
+ }
573
+ setFont(key, family) {
574
+ if (typeof key === "string") this.fontsByKey.set(key, family);
575
+ else this.fontsByData.set(key, family);
576
+ }
577
+ deleteFont(key) {
578
+ if (typeof key === "string") this.fontsByKey.delete(key);
579
+ else this.fontsByData.delete(key);
580
+ }
569
581
  async prepareFonts(fonts) {
570
582
  if (!fonts) return;
571
583
  const families = await Promise.all(fonts.map(this.registerFont.bind(this)));
572
584
  return [...new Set(families.flat().map((f) => f.name))];
573
585
  }
574
586
  async render(node, options) {
575
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
576
- const registeredFamilies = await this.prepareFonts(fonts);
587
+ const { fonts, fontFamilies, images, subset, ...rest } = options ?? {};
588
+ const registeredFamilies = await this.prepareFonts((0, _takumi_rs_helpers.pickFonts)({
589
+ fonts,
590
+ source: node,
591
+ subset
592
+ }));
577
593
  const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
578
594
  return this.inner.render(node, {
579
595
  ...rest,
@@ -582,8 +598,12 @@ var Renderer = class {
582
598
  });
583
599
  }
584
600
  async renderAsDataUrl(node, options) {
585
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
586
- const registeredFamilies = await this.prepareFonts(fonts);
601
+ const { fonts, fontFamilies, images, subset, ...rest } = options ?? {};
602
+ const registeredFamilies = await this.prepareFonts((0, _takumi_rs_helpers.pickFonts)({
603
+ fonts,
604
+ source: node,
605
+ subset
606
+ }));
587
607
  const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
588
608
  return this.inner.renderAsDataUrl(node, {
589
609
  ...rest,
@@ -592,8 +612,12 @@ var Renderer = class {
592
612
  });
593
613
  }
594
614
  async renderSvg(node, options) {
595
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
596
- const registeredFamilies = await this.prepareFonts(fonts);
615
+ const { fonts, fontFamilies, images, subset, ...rest } = options ?? {};
616
+ const registeredFamilies = await this.prepareFonts((0, _takumi_rs_helpers.pickFonts)({
617
+ fonts,
618
+ source: node,
619
+ subset
620
+ }));
597
621
  const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
598
622
  return this.inner.renderSvg(node, {
599
623
  ...rest,
@@ -602,8 +626,12 @@ var Renderer = class {
602
626
  });
603
627
  }
604
628
  async measure(node, options) {
605
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
606
- const registeredFamilies = await this.prepareFonts(fonts);
629
+ const { fonts, fontFamilies, images, subset, ...rest } = options ?? {};
630
+ const registeredFamilies = await this.prepareFonts((0, _takumi_rs_helpers.pickFonts)({
631
+ fonts,
632
+ source: node,
633
+ subset
634
+ }));
607
635
  const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
608
636
  return this.inner.measure(node, {
609
637
  ...rest,
@@ -612,8 +640,13 @@ var Renderer = class {
612
640
  });
613
641
  }
614
642
  async renderAnimation(options) {
615
- const { fonts, fontFamilies, images, ...rest } = options;
616
- const registeredFamilies = await this.prepareFonts(fonts);
643
+ const { fonts, fontFamilies, images, subset, ...rest } = options;
644
+ const nodes = options.scenes.map((scene) => scene.node);
645
+ const registeredFamilies = await this.prepareFonts((0, _takumi_rs_helpers.pickFonts)({
646
+ fonts,
647
+ source: nodes,
648
+ subset
649
+ }));
617
650
  const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
618
651
  return this.inner.renderAnimation({
619
652
  ...rest,
@@ -622,8 +655,13 @@ var Renderer = class {
622
655
  });
623
656
  }
624
657
  async encodeFrames(frames, options) {
625
- const { fonts, fontFamilies, images, ...rest } = options;
626
- const registeredFamilies = await this.prepareFonts(fonts);
658
+ const { fonts, fontFamilies, images, subset, ...rest } = options;
659
+ const nodes = frames.map((frame) => frame.node);
660
+ const registeredFamilies = await this.prepareFonts((0, _takumi_rs_helpers.pickFonts)({
661
+ fonts,
662
+ source: nodes,
663
+ subset
664
+ }));
627
665
  const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
628
666
  return this.inner.encodeFrames(frames, {
629
667
  ...rest,
@@ -636,7 +674,7 @@ var Renderer = class {
636
674
  }
637
675
  async registerFont(font) {
638
676
  const key = createFontKey(font);
639
- const cached = this.fontMapping.get(key);
677
+ const cached = this.getFont(key);
640
678
  if (cached) return cached;
641
679
  const extracted = extractFontBuffer(font);
642
680
  const register = (data) => this.inner.registerFont(isBuffer(font) ? data : {
@@ -645,14 +683,14 @@ var Renderer = class {
645
683
  });
646
684
  if (isBuffer(extracted)) {
647
685
  const binded = register(extracted);
648
- this.fontMapping.set(key, Promise.resolve(binded));
686
+ this.setFont(key, Promise.resolve(binded));
649
687
  return binded;
650
688
  }
651
689
  const promise = extracted.then(register).catch((error) => {
652
- this.fontMapping.delete(key);
690
+ this.deleteFont(key);
653
691
  throw error;
654
692
  });
655
- this.fontMapping.set(key, promise);
693
+ this.setFont(key, promise);
656
694
  return promise;
657
695
  }
658
696
  };
@@ -673,10 +711,5 @@ function isBuffer(data) {
673
711
  //#endregion
674
712
  exports.Renderer = Renderer;
675
713
  exports.default = __wbg_init;
676
- Object.defineProperty(exports, "extractResourceUrls", {
677
- enumerable: true,
678
- get: function() {
679
- return _takumi_rs_helpers.extractResourceUrls;
680
- }
681
- });
714
+ exports.extractResourceUrls = _takumi_rs_helpers.extractResourceUrls;
682
715
  exports.initSync = initSync;
package/dist/export.d.cts CHANGED
@@ -240,7 +240,8 @@ declare function __wbg_init(module_or_path?: {
240
240
  //#endregion
241
241
  //#region src/export.d.ts
242
242
  type FontLoader = Font | (Omit<FontDetails, "data"> & {
243
- data: () => Promise<FontDetails["data"]> | FontDetails["data"];
243
+ data: () => Promise<FontDetails["data"]> | FontDetails["data"]; /** Inclusive codepoint ranges this face covers; lets `render` skip it when unused. */
244
+ ranges?: [number, number][];
244
245
  } & ({
245
246
  key: string;
246
247
  } | {
@@ -269,7 +270,8 @@ type OutputFormatOptions = {
269
270
  };
270
271
  type RenderOptions = Omit<RenderOptions$1, "images" | "format" | "quality"> & OutputFormatOptions & {
271
272
  fonts?: FontLoader[];
272
- images?: ImageLoader[];
273
+ images?: ImageLoader[]; /** Register only the `fonts` subsets the content renders. @default true */
274
+ subset?: boolean;
273
275
  };
274
276
  /**
275
277
  * Animation output format. On wasm, WebP animation is always lossless (lossy
@@ -284,19 +286,26 @@ type AnimationOutputFormatOptions = {
284
286
  };
285
287
  type RenderAnimationOptions = Omit<RenderAnimationOptions$1, "images" | "format"> & AnimationOutputFormatOptions & {
286
288
  fonts?: FontLoader[];
287
- images?: ImageLoader[];
289
+ images?: ImageLoader[]; /** Register only the `fonts` subsets the content renders. @default true */
290
+ subset?: boolean;
288
291
  };
289
292
  type EncodeFramesOptions = Omit<EncodeFramesOptions$1, "images" | "format"> & AnimationOutputFormatOptions & {
290
293
  fonts?: FontLoader[];
291
- images?: ImageLoader[];
294
+ images?: ImageLoader[]; /** Register only the `fonts` subsets the content renders. @default true */
295
+ subset?: boolean;
292
296
  };
293
297
  type SvgRenderOptions = Omit<SvgRenderOptions$1, "images"> & {
294
298
  fonts?: FontLoader[];
295
- images?: ImageLoader[];
299
+ images?: ImageLoader[]; /** Register only the `fonts` subsets the content renders. @default true */
300
+ subset?: boolean;
296
301
  };
297
302
  declare class Renderer {
298
- private fontMapping;
303
+ private fontsByKey;
304
+ private fontsByData;
299
305
  private inner;
306
+ private getFont;
307
+ private setFont;
308
+ private deleteFont;
300
309
  private prepareFonts;
301
310
  render(node: Node, options?: RenderOptions): Promise<Uint8Array<ArrayBufferLike>>;
302
311
  renderAsDataUrl(node: Node, options?: RenderOptions): Promise<string>;
@@ -305,7 +314,7 @@ declare class Renderer {
305
314
  renderAnimation(options: RenderAnimationOptions): Promise<Uint8Array<ArrayBufferLike>>;
306
315
  encodeFrames(frames: AnimationFrameSource[], options: EncodeFramesOptions): Promise<Uint8Array<ArrayBufferLike>>;
307
316
  free(): void;
308
- registerFont(font: FontLoader): Promise<any>;
317
+ registerFont(font: FontLoader): Promise<RegisteredFamily[]>;
309
318
  }
310
319
  //#endregion
311
320
  export { AnimationFrameSource, AnimationOutputFormatOptions, AnimationSceneSource, ByteBuf, ContainerNode, EncodeFramesOptions, Font, FontDetails, FontLoader, ImageLoader, ImageNode, ImageSource, InitInput, InitOutput, KeyframeRule, Keyframes, KeyframesMap, KeyframesRule, KeyframesRuleList, MeasuredNode, MeasuredTextRun, type Node, NodeMetadata, OutputFormatOptions, RegisteredFace, RegisteredFamily, RenderAnimationOptions, RenderOptions, Renderer, SvgRenderOptions, SyncInitInput, TextNode, __wbg_init as default, extractResourceUrls, initSync };
package/dist/export.d.mts CHANGED
@@ -240,7 +240,8 @@ declare function __wbg_init(module_or_path?: {
240
240
  //#endregion
241
241
  //#region src/export.d.ts
242
242
  type FontLoader = Font | (Omit<FontDetails, "data"> & {
243
- data: () => Promise<FontDetails["data"]> | FontDetails["data"];
243
+ data: () => Promise<FontDetails["data"]> | FontDetails["data"]; /** Inclusive codepoint ranges this face covers; lets `render` skip it when unused. */
244
+ ranges?: [number, number][];
244
245
  } & ({
245
246
  key: string;
246
247
  } | {
@@ -269,7 +270,8 @@ type OutputFormatOptions = {
269
270
  };
270
271
  type RenderOptions = Omit<RenderOptions$1, "images" | "format" | "quality"> & OutputFormatOptions & {
271
272
  fonts?: FontLoader[];
272
- images?: ImageLoader[];
273
+ images?: ImageLoader[]; /** Register only the `fonts` subsets the content renders. @default true */
274
+ subset?: boolean;
273
275
  };
274
276
  /**
275
277
  * Animation output format. On wasm, WebP animation is always lossless (lossy
@@ -284,19 +286,26 @@ type AnimationOutputFormatOptions = {
284
286
  };
285
287
  type RenderAnimationOptions = Omit<RenderAnimationOptions$1, "images" | "format"> & AnimationOutputFormatOptions & {
286
288
  fonts?: FontLoader[];
287
- images?: ImageLoader[];
289
+ images?: ImageLoader[]; /** Register only the `fonts` subsets the content renders. @default true */
290
+ subset?: boolean;
288
291
  };
289
292
  type EncodeFramesOptions = Omit<EncodeFramesOptions$1, "images" | "format"> & AnimationOutputFormatOptions & {
290
293
  fonts?: FontLoader[];
291
- images?: ImageLoader[];
294
+ images?: ImageLoader[]; /** Register only the `fonts` subsets the content renders. @default true */
295
+ subset?: boolean;
292
296
  };
293
297
  type SvgRenderOptions = Omit<SvgRenderOptions$1, "images"> & {
294
298
  fonts?: FontLoader[];
295
- images?: ImageLoader[];
299
+ images?: ImageLoader[]; /** Register only the `fonts` subsets the content renders. @default true */
300
+ subset?: boolean;
296
301
  };
297
302
  declare class Renderer {
298
- private fontMapping;
303
+ private fontsByKey;
304
+ private fontsByData;
299
305
  private inner;
306
+ private getFont;
307
+ private setFont;
308
+ private deleteFont;
300
309
  private prepareFonts;
301
310
  render(node: Node, options?: RenderOptions): Promise<Uint8Array<ArrayBufferLike>>;
302
311
  renderAsDataUrl(node: Node, options?: RenderOptions): Promise<string>;
@@ -305,7 +314,7 @@ declare class Renderer {
305
314
  renderAnimation(options: RenderAnimationOptions): Promise<Uint8Array<ArrayBufferLike>>;
306
315
  encodeFrames(frames: AnimationFrameSource[], options: EncodeFramesOptions): Promise<Uint8Array<ArrayBufferLike>>;
307
316
  free(): void;
308
- registerFont(font: FontLoader): Promise<any>;
317
+ registerFont(font: FontLoader): Promise<RegisteredFamily[]>;
309
318
  }
310
319
  //#endregion
311
320
  export { AnimationFrameSource, AnimationOutputFormatOptions, AnimationSceneSource, ByteBuf, ContainerNode, EncodeFramesOptions, Font, FontDetails, FontLoader, ImageLoader, ImageNode, ImageSource, InitInput, InitOutput, KeyframeRule, Keyframes, KeyframesMap, KeyframesRule, KeyframesRuleList, MeasuredNode, MeasuredTextRun, type Node, NodeMetadata, OutputFormatOptions, RegisteredFace, RegisteredFamily, RenderAnimationOptions, RenderOptions, Renderer, SvgRenderOptions, SyncInitInput, TextNode, __wbg_init as default, extractResourceUrls, initSync };
package/dist/export.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { extractResourceUrls } from "@takumi-rs/helpers";
1
+ import { extractResourceUrls, pickFonts } from "@takumi-rs/helpers";
2
2
  //#region pkg/takumi_wasm.js
3
3
  /**
4
4
  * The main renderer for Takumi image rendering engine.
@@ -560,16 +560,32 @@ async function resolveImageLoaders(images) {
560
560
  })));
561
561
  }
562
562
  var Renderer = class {
563
- fontMapping = /* @__PURE__ */ new Map();
563
+ fontsByKey = /* @__PURE__ */ new Map();
564
+ fontsByData = /* @__PURE__ */ new WeakMap();
564
565
  inner = new Renderer$1();
566
+ getFont(key) {
567
+ return typeof key === "string" ? this.fontsByKey.get(key) : this.fontsByData.get(key);
568
+ }
569
+ setFont(key, family) {
570
+ if (typeof key === "string") this.fontsByKey.set(key, family);
571
+ else this.fontsByData.set(key, family);
572
+ }
573
+ deleteFont(key) {
574
+ if (typeof key === "string") this.fontsByKey.delete(key);
575
+ else this.fontsByData.delete(key);
576
+ }
565
577
  async prepareFonts(fonts) {
566
578
  if (!fonts) return;
567
579
  const families = await Promise.all(fonts.map(this.registerFont.bind(this)));
568
580
  return [...new Set(families.flat().map((f) => f.name))];
569
581
  }
570
582
  async render(node, options) {
571
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
572
- const registeredFamilies = await this.prepareFonts(fonts);
583
+ const { fonts, fontFamilies, images, subset, ...rest } = options ?? {};
584
+ const registeredFamilies = await this.prepareFonts(pickFonts({
585
+ fonts,
586
+ source: node,
587
+ subset
588
+ }));
573
589
  const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
574
590
  return this.inner.render(node, {
575
591
  ...rest,
@@ -578,8 +594,12 @@ var Renderer = class {
578
594
  });
579
595
  }
580
596
  async renderAsDataUrl(node, options) {
581
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
582
- const registeredFamilies = await this.prepareFonts(fonts);
597
+ const { fonts, fontFamilies, images, subset, ...rest } = options ?? {};
598
+ const registeredFamilies = await this.prepareFonts(pickFonts({
599
+ fonts,
600
+ source: node,
601
+ subset
602
+ }));
583
603
  const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
584
604
  return this.inner.renderAsDataUrl(node, {
585
605
  ...rest,
@@ -588,8 +608,12 @@ var Renderer = class {
588
608
  });
589
609
  }
590
610
  async renderSvg(node, options) {
591
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
592
- const registeredFamilies = await this.prepareFonts(fonts);
611
+ const { fonts, fontFamilies, images, subset, ...rest } = options ?? {};
612
+ const registeredFamilies = await this.prepareFonts(pickFonts({
613
+ fonts,
614
+ source: node,
615
+ subset
616
+ }));
593
617
  const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
594
618
  return this.inner.renderSvg(node, {
595
619
  ...rest,
@@ -598,8 +622,12 @@ var Renderer = class {
598
622
  });
599
623
  }
600
624
  async measure(node, options) {
601
- const { fonts, fontFamilies, images, ...rest } = options ?? {};
602
- const registeredFamilies = await this.prepareFonts(fonts);
625
+ const { fonts, fontFamilies, images, subset, ...rest } = options ?? {};
626
+ const registeredFamilies = await this.prepareFonts(pickFonts({
627
+ fonts,
628
+ source: node,
629
+ subset
630
+ }));
603
631
  const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
604
632
  return this.inner.measure(node, {
605
633
  ...rest,
@@ -608,8 +636,13 @@ var Renderer = class {
608
636
  });
609
637
  }
610
638
  async renderAnimation(options) {
611
- const { fonts, fontFamilies, images, ...rest } = options;
612
- const registeredFamilies = await this.prepareFonts(fonts);
639
+ const { fonts, fontFamilies, images, subset, ...rest } = options;
640
+ const nodes = options.scenes.map((scene) => scene.node);
641
+ const registeredFamilies = await this.prepareFonts(pickFonts({
642
+ fonts,
643
+ source: nodes,
644
+ subset
645
+ }));
613
646
  const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
614
647
  return this.inner.renderAnimation({
615
648
  ...rest,
@@ -618,8 +651,13 @@ var Renderer = class {
618
651
  });
619
652
  }
620
653
  async encodeFrames(frames, options) {
621
- const { fonts, fontFamilies, images, ...rest } = options;
622
- const registeredFamilies = await this.prepareFonts(fonts);
654
+ const { fonts, fontFamilies, images, subset, ...rest } = options;
655
+ const nodes = frames.map((frame) => frame.node);
656
+ const registeredFamilies = await this.prepareFonts(pickFonts({
657
+ fonts,
658
+ source: nodes,
659
+ subset
660
+ }));
623
661
  const resolvedImages = images ? await resolveImageLoaders(images) : void 0;
624
662
  return this.inner.encodeFrames(frames, {
625
663
  ...rest,
@@ -632,7 +670,7 @@ var Renderer = class {
632
670
  }
633
671
  async registerFont(font) {
634
672
  const key = createFontKey(font);
635
- const cached = this.fontMapping.get(key);
673
+ const cached = this.getFont(key);
636
674
  if (cached) return cached;
637
675
  const extracted = extractFontBuffer(font);
638
676
  const register = (data) => this.inner.registerFont(isBuffer(font) ? data : {
@@ -641,14 +679,14 @@ var Renderer = class {
641
679
  });
642
680
  if (isBuffer(extracted)) {
643
681
  const binded = register(extracted);
644
- this.fontMapping.set(key, Promise.resolve(binded));
682
+ this.setFont(key, Promise.resolve(binded));
645
683
  return binded;
646
684
  }
647
685
  const promise = extracted.then(register).catch((error) => {
648
- this.fontMapping.delete(key);
686
+ this.deleteFont(key);
649
687
  throw error;
650
688
  });
651
- this.fontMapping.set(key, promise);
689
+ this.setFont(key, promise);
652
690
  return promise;
653
691
  }
654
692
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takumi-rs/wasm",
3
- "version": "2.0.0-beta.5",
3
+ "version": "2.0.0-beta.7",
4
4
  "keywords": [
5
5
  "css",
6
6
  "image",
@@ -89,13 +89,13 @@
89
89
  "publish-lint": "attw --pack . && publint --strict ."
90
90
  },
91
91
  "dependencies": {
92
- "@takumi-rs/helpers": "2.0.0-beta.5"
92
+ "@takumi-rs/helpers": "2.0.0-beta.7"
93
93
  },
94
94
  "devDependencies": {
95
- "@types/bun": "catalog:",
95
+ "@types/bun": "^1.3.14",
96
96
  "csstype": "^3.2.3",
97
- "tsdown": "catalog:",
98
- "unrun": "catalog:"
97
+ "tsdown": "^0.22.3",
98
+ "unrun": "^0.3.1"
99
99
  },
100
100
  "readme": "README.md"
101
101
  }
Binary file