@returningai/widget-sdk 1.7.0 → 1.7.2

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.
@@ -11741,9 +11741,10 @@ function sanitizeBannerHtml(html2) {
11741
11741
  ALLOW_DATA_ATTR: false
11742
11742
  });
11743
11743
  }
11744
- function resolveCssToken(token) {
11744
+ function resolveCssToken(token, preferredScope) {
11745
11745
  const normalizedToken = token.startsWith("--") ? token : `--${token}`;
11746
11746
  const candidates = [
11747
+ preferredScope,
11747
11748
  document.documentElement,
11748
11749
  document.body,
11749
11750
  ...Array.from(
@@ -11763,32 +11764,33 @@ function resolveCssToken(token) {
11763
11764
  )
11764
11765
  )
11765
11766
  ].filter(Boolean);
11767
+ const checked = /* @__PURE__ */ new Set();
11766
11768
  for (const node of candidates) {
11769
+ if (checked.has(node)) continue;
11770
+ checked.add(node);
11767
11771
  const value = getComputedStyle(node).getPropertyValue(normalizedToken).trim();
11768
11772
  if (value) return value;
11769
11773
  }
11770
11774
  for (const node of Array.from(document.querySelectorAll("*"))) {
11775
+ if (checked.has(node)) continue;
11771
11776
  const value = getComputedStyle(node).getPropertyValue(normalizedToken).trim();
11772
11777
  if (value) return value;
11773
11778
  }
11774
11779
  return void 0;
11775
11780
  }
11776
- const safeColor = (v) => {
11781
+ function safeColor(v, preferredScope) {
11777
11782
  if (!v) return void 0;
11778
11783
  const value = v.trim();
11779
11784
  if (/^(#|rgb|hsl)/i.test(value)) return value;
11780
11785
  if (/^(--)?[a-z0-9-]+$/i.test(value)) {
11781
- const resolved = resolveCssToken(value);
11782
- return resolved;
11786
+ return resolveCssToken(value, preferredScope);
11783
11787
  }
11784
11788
  const varMatch = value.match(/^var\((--[a-z0-9-]+)\)$/i);
11785
11789
  if (varMatch) {
11786
- const token = varMatch[1];
11787
- const resolved = resolveCssToken(token);
11788
- return resolved;
11790
+ return resolveCssToken(varMatch[1], preferredScope);
11789
11791
  }
11790
11792
  return void 0;
11791
- };
11793
+ }
11792
11794
  function el(tag, styles, attrs) {
11793
11795
  const node = document.createElement(tag);
11794
11796
  Object.assign(node.style, styles);
@@ -11872,15 +11874,15 @@ function justifyFromTextAlign(align) {
11872
11874
  if (align === "right") return "flex-end";
11873
11875
  return "flex-start";
11874
11876
  }
11875
- function renderBanner(banner, handlers) {
11877
+ function renderBanner(banner, handlers, colorScope) {
11876
11878
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
11877
11879
  const placement = banner.placement || {};
11878
11880
  const mode = placement.mode || "overlay";
11879
11881
  const position = placement.position || "top";
11880
11882
  const zIndex = String(placement.zIndex || 2147483e3);
11881
11883
  const imageBanner = isImageBanner(banner);
11882
- const bg = imageBanner ? "transparent" : safeColor((_a = banner.style) == null ? void 0 : _a.backgroundColor) || "#1f2024";
11883
- const color = safeColor((_b = banner.style) == null ? void 0 : _b.color) || "#ffffff";
11884
+ const bg = imageBanner ? "transparent" : safeColor((_a = banner.style) == null ? void 0 : _a.backgroundColor, colorScope) || "#1f2024";
11885
+ const color = safeColor((_b = banner.style) == null ? void 0 : _b.color, colorScope) || "#ffffff";
11884
11886
  const isCard = mode === "overlay" || mode === "popup";
11885
11887
  const ctaPosition = getCtaStyleValue(banner, ["position"]) || "center";
11886
11888
  const imageHorizontal = imageAlign((_c = banner.image) == null ? void 0 : _c.horizontalAlignment);
@@ -12021,9 +12023,13 @@ function renderBanner(banner, handlers) {
12021
12023
  }
12022
12024
  if (!imageBanner && ((_n = banner.cta) == null ? void 0 : _n.enabled)) {
12023
12025
  const ctaBg = safeColor(
12024
- getCtaStyleValue(banner, ["backgroundColor", "buttonColor", "aColor"])
12026
+ getCtaStyleValue(banner, ["backgroundColor", "buttonColor", "aColor"]),
12027
+ colorScope
12025
12028
  ) || "#2f6df6";
12026
- const ctaColor = safeColor(getCtaStyleValue(banner, ["textColor", "color"])) || "#ffffff";
12029
+ const ctaColor = safeColor(
12030
+ getCtaStyleValue(banner, ["textColor", "color"]),
12031
+ colorScope
12032
+ ) || "#ffffff";
12027
12033
  const btn = el("button", {
12028
12034
  cursor: "pointer",
12029
12035
  border: "none",
@@ -12498,6 +12504,33 @@ function clearState(state) {
12498
12504
  state.syncTimer = null;
12499
12505
  }
12500
12506
  }
12507
+ const AUTO_THEME_LEASE_STATE = "__RAI_AUTO_THEME_LEASE_STATE__";
12508
+ const DISABLE_AUTO_THEME = "__DISABLE_AUTO_THEME__";
12509
+ function acquireAutoThemeDisableLease(target = window) {
12510
+ let state = target[AUTO_THEME_LEASE_STATE];
12511
+ if (!state) {
12512
+ state = {
12513
+ count: 0,
12514
+ previousValue: target[DISABLE_AUTO_THEME]
12515
+ };
12516
+ target[AUTO_THEME_LEASE_STATE] = state;
12517
+ }
12518
+ state.count += 1;
12519
+ target[DISABLE_AUTO_THEME] = true;
12520
+ let released = false;
12521
+ return () => {
12522
+ if (released) return;
12523
+ released = true;
12524
+ state.count = Math.max(0, state.count - 1);
12525
+ if (state.count > 0) return;
12526
+ if (state.previousValue === void 0) {
12527
+ delete target[DISABLE_AUTO_THEME];
12528
+ } else {
12529
+ target[DISABLE_AUTO_THEME] = state.previousValue;
12530
+ }
12531
+ delete target[AUTO_THEME_LEASE_STATE];
12532
+ };
12533
+ }
12501
12534
  const BUNDLE_READY_TIMEOUT_MS = 3e4;
12502
12535
  function isPromiseLike(value) {
12503
12536
  return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
@@ -12542,6 +12575,80 @@ async function waitForBundleMountReadiness(mountResult, firstPaint, timeoutMs =
12542
12575
  );
12543
12576
  return "deep-ready";
12544
12577
  }
12578
+ function isWidgetBundleRuntime(value) {
12579
+ return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.mount === "function";
12580
+ }
12581
+ function normalizeBundleUrl(url, baseUrl) {
12582
+ const normalized = new URL(url, baseUrl);
12583
+ normalized.hash = "";
12584
+ return normalized.href;
12585
+ }
12586
+ function createBundleRuntimeLoader({
12587
+ loadScript,
12588
+ readGlobal,
12589
+ baseUrl
12590
+ }) {
12591
+ const runtimePromises = /* @__PURE__ */ new Map();
12592
+ const globalLoadTails = /* @__PURE__ */ new Map();
12593
+ return async function getBundleRuntime(globalName, bundleUrl) {
12594
+ const normalizedUrl = normalizeBundleUrl(bundleUrl, baseUrl());
12595
+ const cacheKey = `${globalName}:${normalizedUrl}`;
12596
+ const cachedRuntime = runtimePromises.get(cacheKey);
12597
+ if (cachedRuntime) return cachedRuntime;
12598
+ const previousLoad = globalLoadTails.get(globalName) || Promise.resolve();
12599
+ const runtimePromise = previousLoad.then(async () => {
12600
+ await loadScript(normalizedUrl);
12601
+ const runtime = readGlobal(globalName);
12602
+ if (!isWidgetBundleRuntime(runtime)) {
12603
+ throw new Error(
12604
+ `window.${globalName}.mount not found after loading ${normalizedUrl}`
12605
+ );
12606
+ }
12607
+ const mount = runtime.mount;
12608
+ return {
12609
+ mount(container, config) {
12610
+ return mount.call(runtime, container, config);
12611
+ }
12612
+ };
12613
+ });
12614
+ runtimePromises.set(cacheKey, runtimePromise);
12615
+ const loadTail = runtimePromise.then(
12616
+ () => void 0,
12617
+ () => void 0
12618
+ );
12619
+ globalLoadTails.set(globalName, loadTail);
12620
+ void loadTail.then(() => {
12621
+ if (globalLoadTails.get(globalName) === loadTail) {
12622
+ globalLoadTails.delete(globalName);
12623
+ }
12624
+ });
12625
+ try {
12626
+ return await runtimePromise;
12627
+ } catch (error) {
12628
+ if (runtimePromises.get(cacheKey) === runtimePromise) {
12629
+ runtimePromises.delete(cacheKey);
12630
+ }
12631
+ throw error;
12632
+ }
12633
+ };
12634
+ }
12635
+ function appendScript(url) {
12636
+ return new Promise((resolve, reject) => {
12637
+ const script = document.createElement("script");
12638
+ script.src = url;
12639
+ script.onload = () => resolve();
12640
+ script.onerror = () => reject(new Error(`Failed to load ${url}`));
12641
+ document.head.appendChild(script);
12642
+ });
12643
+ }
12644
+ const loadBrowserBundleRuntime = createBundleRuntimeLoader({
12645
+ loadScript: appendScript,
12646
+ readGlobal: (globalName) => window[globalName],
12647
+ baseUrl: () => document.baseURI
12648
+ });
12649
+ function getOrLoadBundleRuntime(globalName, bundleUrl) {
12650
+ return loadBrowserBundleRuntime(globalName, bundleUrl);
12651
+ }
12545
12652
  function assertCallbackName(callbackName) {
12546
12653
  if (!callbackName || typeof callbackName !== "string") {
12547
12654
  throw new TypeError("ReturningAI callback name must be a non-empty string");
@@ -12860,15 +12967,6 @@ function resolveWidgetServerUrl(domainKey) {
12860
12967
  function resolveV2ApiUrl(domainKey) {
12861
12968
  return DOMAIN_KEY_TO_V2_API_URL[domainKey] || DOMAIN_KEY_TO_V2_API_URL.PROD;
12862
12969
  }
12863
- function loadScript(url) {
12864
- return new Promise((resolve, reject) => {
12865
- const s = document.createElement("script");
12866
- s.src = url;
12867
- s.onload = () => resolve();
12868
- s.onerror = () => reject(new Error(`Failed to load ${url}`));
12869
- document.head.appendChild(s);
12870
- });
12871
- }
12872
12970
  const loadedFonts = /* @__PURE__ */ new Set();
12873
12971
  function ensureGoogleFontLoaded(family) {
12874
12972
  if (!family || loadedFonts.has(family)) return;
@@ -13068,6 +13166,7 @@ class BaseWidget extends HTMLElement {
13068
13166
  __publicField(this, "lottieInstances", []);
13069
13167
  __publicField(this, "callbackRegistry", new WidgetCallbackRegistry());
13070
13168
  __publicField(this, "bannerNode", null);
13169
+ __publicField(this, "releaseAutoThemeDisableLease");
13071
13170
  this.shadow = this.attachShadow({ mode: "closed" });
13072
13171
  }
13073
13172
  static get observedAttributes() {
@@ -13540,24 +13639,28 @@ class BaseWidget extends HTMLElement {
13540
13639
  if (!banner) return;
13541
13640
  if (((_a = banner.placement) == null ? void 0 : _a.mobileBehavior) === "hide" && device === "mobile")
13542
13641
  return;
13543
- const node = renderBanner(banner, {
13544
- onCtaClick: () => {
13545
- var _a2, _b2, _c;
13546
- fireTracking((_a2 = banner.tracking) == null ? void 0 : _a2.clickUrl);
13547
- this.emit("rai-banner-click", { bannerId: banner.id });
13548
- const url = (_b2 = banner.cta) == null ? void 0 : _b2.url;
13549
- if (!url) return;
13550
- if ((_c = banner.cta) == null ? void 0 : _c.openInNewTab)
13551
- window.open(url, "_blank", "noopener,noreferrer");
13552
- else window.location.href = url;
13642
+ const node = renderBanner(
13643
+ banner,
13644
+ {
13645
+ onCtaClick: () => {
13646
+ var _a2, _b2, _c;
13647
+ fireTracking((_a2 = banner.tracking) == null ? void 0 : _a2.clickUrl);
13648
+ this.emit("rai-banner-click", { bannerId: banner.id });
13649
+ const url = (_b2 = banner.cta) == null ? void 0 : _b2.url;
13650
+ if (!url) return;
13651
+ if ((_c = banner.cta) == null ? void 0 : _c.openInNewTab)
13652
+ window.open(url, "_blank", "noopener,noreferrer");
13653
+ else window.location.href = url;
13654
+ },
13655
+ onDismiss: () => {
13656
+ var _a2;
13657
+ fireTracking((_a2 = banner.tracking) == null ? void 0 : _a2.dismissUrl);
13658
+ this.emit("rai-banner-dismiss", { bannerId: banner.id });
13659
+ this.removeBanner();
13660
+ }
13553
13661
  },
13554
- onDismiss: () => {
13555
- var _a2;
13556
- fireTracking((_a2 = banner.tracking) == null ? void 0 : _a2.dismissUrl);
13557
- this.emit("rai-banner-dismiss", { bannerId: banner.id });
13558
- this.removeBanner();
13559
- }
13560
- });
13662
+ this
13663
+ );
13561
13664
  this.bannerNode = node;
13562
13665
  document.body.appendChild(node);
13563
13666
  fireTracking((_b = banner.tracking) == null ? void 0 : _b.impressionUrl);
@@ -13665,6 +13768,9 @@ class BaseWidget extends HTMLElement {
13665
13768
  return this.isConnected && initId === this.initGeneration;
13666
13769
  }
13667
13770
  teardownMountedWidget() {
13771
+ var _a;
13772
+ (_a = this.releaseAutoThemeDisableLease) == null ? void 0 : _a.call(this);
13773
+ this.releaseAutoThemeDisableLease = void 0;
13668
13774
  if (this.cleanupListener) {
13669
13775
  this.cleanupListener();
13670
13776
  this.cleanupListener = void 0;
@@ -13722,6 +13828,9 @@ class BaseWidget extends HTMLElement {
13722
13828
  let container = null;
13723
13829
  try {
13724
13830
  this.teardownMountedWidget();
13831
+ if (this.config.widgetType === "custom") {
13832
+ this.releaseAutoThemeDisableLease = acquireAutoThemeDisableLease();
13833
+ }
13725
13834
  const bundleApiUrl = this.config.v2ApiUrl || resolveV2ApiUrl(this.config.domainKey);
13726
13835
  const w = window;
13727
13836
  if (!w.DomainKey) {
@@ -13742,16 +13851,11 @@ class BaseWidget extends HTMLElement {
13742
13851
  window.loadingAnimation = this.state.loadingAnimation;
13743
13852
  }
13744
13853
  const globalName = WIDGET_GLOBALS[this.config.widgetType];
13745
- if (!((_a = window[globalName]) == null ? void 0 : _a.mount)) {
13746
- await loadScript(this.config.bundleUrl);
13747
- }
13854
+ const widgetGlobal = await getOrLoadBundleRuntime(
13855
+ globalName,
13856
+ this.config.bundleUrl
13857
+ );
13748
13858
  if (!this.isCurrentInit(initId)) return;
13749
- const widgetGlobal = window[globalName];
13750
- if (!(widgetGlobal == null ? void 0 : widgetGlobal.mount)) {
13751
- throw new Error(
13752
- `window.${globalName}.mount not found after loading bundle`
13753
- );
13754
- }
13755
13859
  container = document.createElement("div");
13756
13860
  const hideNestedBundleUntilReady = this.config.widgetType === "custom";
13757
13861
  container.className = "rai-widget-root";
@@ -13794,7 +13898,7 @@ class BaseWidget extends HTMLElement {
13794
13898
  // post. Mirrors the iframe behavior, where `isOpenWidget` is only
13795
13899
  // injected by SSR for the explicit `/open-widget` route.
13796
13900
  isOpenWidget: !this.state.accessToken,
13797
- defaultColorSchema: this.config.theme === "dark" ? "dark" : "light",
13901
+ defaultColorSchema: this.config.widgetType === "custom" && !this.config.themeExplicit ? null : colorMode,
13798
13902
  requestHostCallback: async (callbackName, payload) => {
13799
13903
  if (this.config.widgetType !== "store" && this.config.widgetType !== "custom") {
13800
13904
  throw new Error(
@@ -13822,6 +13926,8 @@ class BaseWidget extends HTMLElement {
13822
13926
  return;
13823
13927
  }
13824
13928
  console.error("[rai-widget] Bundle mount failed:", err);
13929
+ (_a = this.releaseAutoThemeDisableLease) == null ? void 0 : _a.call(this);
13930
+ this.releaseAutoThemeDisableLease = void 0;
13825
13931
  this.showError();
13826
13932
  }
13827
13933
  }
@@ -13997,17 +14103,19 @@ class CustomWidget extends BaseWidget {
13997
14103
  const base = config.widgetUrl.endsWith("custom-widget") ? `${config.widgetUrl}/${config.widgetId}` : config.widgetUrl;
13998
14104
  try {
13999
14105
  const u = new URL(base);
14000
- u.searchParams.set("color", config.theme);
14106
+ u.searchParams.set("auth", "postmessage");
14107
+ if (config.themeExplicit) u.searchParams.set("color", config.theme);
14001
14108
  if (config.locale) u.searchParams.set("locale", config.locale);
14002
14109
  return u.toString();
14003
14110
  } catch {
14004
- const params = [`color=${config.theme}`];
14111
+ const params = ["auth=postmessage"];
14112
+ if (config.themeExplicit) params.push(`color=${config.theme}`);
14005
14113
  if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
14006
- return `${base}?${params.join("&")}`;
14114
+ return params.length > 0 ? `${base}?${params.join("&")}` : base;
14007
14115
  }
14008
14116
  }
14009
14117
  }
14010
- console.log(`[rai-widget] v${"1.7.0"}`);
14118
+ console.log(`[rai-widget] v${"1.7.2"}`);
14011
14119
  class StoreWidgetCompat extends StoreWidget {
14012
14120
  }
14013
14121
  const WIDGET_REGISTRY = [
@@ -14071,7 +14179,7 @@ function exposePublicApi() {
14071
14179
  })();
14072
14180
  const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
14073
14181
  window.ReturningAIWidget = {
14074
- version: "1.7.0",
14182
+ version: "1.7.2",
14075
14183
  reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
14076
14184
  logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
14077
14185
  isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,
@@ -19,6 +19,7 @@ export declare abstract class BaseWidget extends HTMLElement {
19
19
  private lottieInstances;
20
20
  private callbackRegistry;
21
21
  private bannerNode;
22
+ private releaseAutoThemeDisableLease?;
22
23
  constructor();
23
24
  registerCallback(callbackName: typeof STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME, callback: ReturningAIStoreCallbackFieldOptionsCallback): void;
24
25
  registerCallback(callbackName: typeof STORE_PURCHASE_SUCCESS_CALLBACK_NAME, callback: ReturningAIStorePurchaseSuccessCallback): void;
@@ -0,0 +1,3 @@
1
+ /** Resolve a CSS token from the owning widget before consulting the host page. */
2
+ export declare function resolveCssToken(token: string, preferredScope?: Element): string | undefined;
3
+ export declare function safeColor(v?: string, preferredScope?: Element): string | undefined;
@@ -7,6 +7,8 @@ export interface BannerHandlers {
7
7
  * Builds the banner DOM (plain elements + inline styles, so it renders correctly
8
8
  * on any host site without external CSS). Honors `placement.mode`:
9
9
  * overlay/popup → fixed centered card; inline → in-flow full-width bar.
10
+ * Color tokens resolve from `colorScope` first so a banner uses its owning
11
+ * widget's palette instead of an unrelated host-page or sibling-widget theme.
10
12
  * The caller decides where to attach it (body for overlay, target for inline).
11
13
  */
12
- export declare function renderBanner(banner: EligibleBanner, handlers: BannerHandlers): HTMLElement;
14
+ export declare function renderBanner(banner: EligibleBanner, handlers: BannerHandlers, colorScope?: Element): HTMLElement;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Keep automatic host theming disabled while one or more custom bundles are
3
+ * mounted, then restore the value that existed before the first mount.
4
+ */
5
+ export declare function acquireAutoThemeDisableLease(target?: Record<string, unknown>): () => void;
@@ -0,0 +1,20 @@
1
+ export interface WidgetBundleRuntime {
2
+ mount(container: HTMLElement, config: unknown): unknown;
3
+ }
4
+ interface BundleRuntimeLoaderOptions {
5
+ loadScript: (url: string) => Promise<void>;
6
+ readGlobal: (globalName: string) => unknown;
7
+ baseUrl: () => string;
8
+ }
9
+ export declare function normalizeBundleUrl(url: string, baseUrl: string): string;
10
+ /**
11
+ * Cache widget runtimes by both their window global and normalized bundle URL.
12
+ *
13
+ * Different custom-widget bundles intentionally reuse the same global name, so
14
+ * the current value on window is not enough to identify the requested runtime.
15
+ * Capturing the runtime immediately after its script loads also lets A -> B -> A
16
+ * reuse A without re-evaluating its IIFE.
17
+ */
18
+ export declare function createBundleRuntimeLoader({ loadScript, readGlobal, baseUrl, }: BundleRuntimeLoaderOptions): (globalName: string, bundleUrl: string) => Promise<WidgetBundleRuntime>;
19
+ export declare function getOrLoadBundleRuntime(globalName: string, bundleUrl: string): Promise<WidgetBundleRuntime>;
20
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@returningai/widget-sdk",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "Shadow DOM isolated widget SDK for ReturningAI",
5
5
  "main": "dist/rai-widget.iife.js",
6
6
  "module": "dist/rai-widget.js",
@@ -26,13 +26,18 @@
26
26
  "build:all": "npm run build && npm run build:banner",
27
27
  "build:all:min": "npm run build:min && npm run build:banner:min",
28
28
  "types": "tsc --emitDeclarationOnly --outDir dist/types && cp src/jsx.d.ts dist/types/jsx.d.ts",
29
- "test": "npm run test:host-callback && npm run test:bundle-readiness && npm run test:jsx-types",
29
+ "test": "npm run test:host-callback && npm run test:bundle-readiness && npm run test:bundle-runtime-cache && npm run test:jsx-types",
30
30
  "test:host-callback": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/postmessage-host-callback.test.js",
31
31
  "test:bundle-readiness": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/bundle-readiness.test.js",
32
+ "test:bundle-runtime-cache": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/bundle-runtime-cache.test.js",
32
33
  "test:jsx-types": "tsc --noEmit --strict --lib ES2020,DOM --skipLibCheck false src/jsx.d.ts",
34
+ "coverage:repowise": "rm -rf coverage && npm run coverage:host-callback && npm run coverage:bundle-readiness && npm run test:jsx-types",
35
+ "coverage:host-callback": "rm -rf test/.tmp && tsc -p test/tsconfig.json && c8 --reporter=lcov --reports-dir=coverage/host-callback --exclude-after-remap --exclude='**/*.test.ts' node test/.tmp/test/postmessage-host-callback.test.js && node scripts/name-lcov-test.mjs coverage/host-callback/lcov.info test/postmessage-host-callback.test.ts",
36
+ "coverage:bundle-readiness": "rm -rf test/.tmp && tsc -p test/tsconfig.json && c8 --reporter=lcov --reports-dir=coverage/bundle-readiness --exclude-after-remap --exclude='**/*.test.ts' node test/.tmp/test/bundle-readiness.test.js && node scripts/name-lcov-test.mjs coverage/bundle-readiness/lcov.info test/bundle-readiness.test.ts",
33
37
  "prepublishOnly": "npm run build:all:min && npm run types"
34
38
  },
35
39
  "devDependencies": {
40
+ "c8": "^11.0.0",
36
41
  "terser": "^5.46.0",
37
42
  "typescript": "^5.3.3",
38
43
  "vite": "^5.1.0"