@returningai/widget-sdk 1.8.4 → 1.8.5

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.
@@ -12823,6 +12823,31 @@ function subscribeAuthSession(config, state, onSnapshot) {
12823
12823
  };
12824
12824
  }
12825
12825
  const BUNDLE_READY_TIMEOUT_MS = 3e4;
12826
+ class BundleReadinessTimeoutError extends Error {
12827
+ constructor(phase, message) {
12828
+ super(message);
12829
+ __publicField(this, "phase");
12830
+ this.name = "BundleReadinessTimeoutError";
12831
+ this.phase = phase;
12832
+ }
12833
+ }
12834
+ function isBundleReadinessTimeoutError(error) {
12835
+ return error instanceof BundleReadinessTimeoutError;
12836
+ }
12837
+ function createDefaultEnvironment() {
12838
+ const visibilityDocument = typeof document === "undefined" ? void 0 : document;
12839
+ const pageWindow = typeof window === "undefined" ? void 0 : window;
12840
+ return {
12841
+ isHidden: () => (visibilityDocument == null ? void 0 : visibilityDocument.visibilityState) === "hidden",
12842
+ now: () => Date.now(),
12843
+ setTimeout: (callback, delayMs) => globalThis.setTimeout(callback, delayMs),
12844
+ clearTimeout: (timerId) => globalThis.clearTimeout(timerId),
12845
+ addVisibilityChangeListener: (listener) => visibilityDocument == null ? void 0 : visibilityDocument.addEventListener("visibilitychange", listener),
12846
+ removeVisibilityChangeListener: (listener) => visibilityDocument == null ? void 0 : visibilityDocument.removeEventListener("visibilitychange", listener),
12847
+ addPageShowListener: (listener) => pageWindow == null ? void 0 : pageWindow.addEventListener("pageshow", listener),
12848
+ removePageShowListener: (listener) => pageWindow == null ? void 0 : pageWindow.removeEventListener("pageshow", listener)
12849
+ };
12850
+ }
12826
12851
  function isPromiseLike(value) {
12827
12852
  return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
12828
12853
  }
@@ -12832,28 +12857,101 @@ function isReadyHandle(value) {
12832
12857
  }
12833
12858
  return isPromiseLike(value.ready);
12834
12859
  }
12835
- function waitWithTimeout(promise, timeoutMs, timeoutMessage) {
12860
+ function waitWithActiveTimeTimeout(promise, timeoutMs, timeoutError, environment) {
12836
12861
  return new Promise((resolve, reject) => {
12837
- const timeoutId = setTimeout(() => {
12838
- reject(new Error(timeoutMessage));
12839
- }, timeoutMs);
12862
+ let settled = false;
12863
+ let remainingMs = Math.max(0, timeoutMs);
12864
+ let visibleStartedAt = null;
12865
+ let timeoutId = null;
12866
+ const clearActiveTimer = () => {
12867
+ if (timeoutId !== null) {
12868
+ environment.clearTimeout(timeoutId);
12869
+ timeoutId = null;
12870
+ }
12871
+ };
12872
+ const cleanup = () => {
12873
+ clearActiveTimer();
12874
+ environment.removeVisibilityChangeListener(handleVisibilityChange);
12875
+ };
12876
+ const finish = (callback) => {
12877
+ if (settled) return;
12878
+ settled = true;
12879
+ cleanup();
12880
+ callback();
12881
+ };
12882
+ const pauseDeadline = () => {
12883
+ clearActiveTimer();
12884
+ if (visibleStartedAt === null) return;
12885
+ remainingMs = Math.max(
12886
+ 0,
12887
+ remainingMs - Math.max(0, environment.now() - visibleStartedAt)
12888
+ );
12889
+ visibleStartedAt = null;
12890
+ };
12891
+ const startDeadline = () => {
12892
+ if (settled || environment.isHidden() || timeoutId !== null) return;
12893
+ if (remainingMs <= 0) {
12894
+ finish(() => reject(timeoutError()));
12895
+ return;
12896
+ }
12897
+ visibleStartedAt = environment.now();
12898
+ timeoutId = environment.setTimeout(() => {
12899
+ timeoutId = null;
12900
+ if (environment.isHidden()) {
12901
+ visibleStartedAt = null;
12902
+ return;
12903
+ }
12904
+ visibleStartedAt = null;
12905
+ finish(() => reject(timeoutError()));
12906
+ }, remainingMs);
12907
+ };
12908
+ function handleVisibilityChange() {
12909
+ if (environment.isHidden()) {
12910
+ pauseDeadline();
12911
+ } else {
12912
+ startDeadline();
12913
+ }
12914
+ }
12915
+ environment.addVisibilityChangeListener(handleVisibilityChange);
12916
+ startDeadline();
12840
12917
  Promise.resolve(promise).then(
12841
12918
  (value) => {
12842
- clearTimeout(timeoutId);
12843
- resolve(value);
12919
+ finish(() => resolve(value));
12844
12920
  },
12845
12921
  (error) => {
12846
- clearTimeout(timeoutId);
12847
- reject(error);
12922
+ finish(() => reject(error));
12848
12923
  }
12849
12924
  );
12850
12925
  });
12851
12926
  }
12852
- function resolveBundleMountResult(mountResult, timeoutMs = BUNDLE_READY_TIMEOUT_MS) {
12853
- return isPromiseLike(mountResult) ? waitWithTimeout(
12927
+ function scheduleForegroundRecovery(recover, environment = createDefaultEnvironment()) {
12928
+ if (!environment.isHidden()) return () => {
12929
+ };
12930
+ let active = true;
12931
+ const cleanup = () => {
12932
+ if (!active) return;
12933
+ active = false;
12934
+ environment.removeVisibilityChangeListener(handleForeground);
12935
+ environment.removePageShowListener(handleForeground);
12936
+ };
12937
+ const handleForeground = () => {
12938
+ if (!active || environment.isHidden()) return;
12939
+ cleanup();
12940
+ recover();
12941
+ };
12942
+ environment.addVisibilityChangeListener(handleForeground);
12943
+ environment.addPageShowListener(handleForeground);
12944
+ return cleanup;
12945
+ }
12946
+ function resolveBundleMountResult(mountResult, timeoutMs = BUNDLE_READY_TIMEOUT_MS, environment = createDefaultEnvironment()) {
12947
+ return isPromiseLike(mountResult) ? waitWithActiveTimeTimeout(
12854
12948
  mountResult,
12855
12949
  timeoutMs,
12856
- `Widget bundle mount did not resolve within ${timeoutMs}ms`
12950
+ () => new BundleReadinessTimeoutError(
12951
+ "mount",
12952
+ `Widget bundle mount did not resolve within ${timeoutMs}ms`
12953
+ ),
12954
+ environment
12857
12955
  ) : Promise.resolve(mountResult);
12858
12956
  }
12859
12957
  function createBundleMountCleanup(mountResult) {
@@ -12869,19 +12967,24 @@ function createBundleMountCleanup(mountResult) {
12869
12967
  unmount.call(mountResult);
12870
12968
  };
12871
12969
  }
12872
- async function waitForBundleMountReadiness(mountResult, firstPaint, timeoutMs = BUNDLE_READY_TIMEOUT_MS) {
12970
+ async function waitForBundleMountReadiness(mountResult, firstPaint, timeoutMs = BUNDLE_READY_TIMEOUT_MS, environment = createDefaultEnvironment()) {
12873
12971
  const resolvedMountResult = await resolveBundleMountResult(
12874
12972
  mountResult,
12875
- timeoutMs
12973
+ timeoutMs,
12974
+ environment
12876
12975
  );
12877
12976
  if (!isReadyHandle(resolvedMountResult)) {
12878
12977
  await firstPaint;
12879
12978
  return "first-paint";
12880
12979
  }
12881
- await waitWithTimeout(
12980
+ await waitWithActiveTimeTimeout(
12882
12981
  resolvedMountResult.ready,
12883
12982
  timeoutMs,
12884
- `Widget bundle did not report deep readiness within ${timeoutMs}ms`
12983
+ () => new BundleReadinessTimeoutError(
12984
+ "deep-ready",
12985
+ `Widget bundle did not report deep readiness within ${timeoutMs}ms`
12986
+ ),
12987
+ environment
12885
12988
  );
12886
12989
  return "deep-ready";
12887
12990
  }
@@ -13610,6 +13713,45 @@ function ensureWidgetPreconnects(origins, targetDocument) {
13610
13713
  documentTarget.head.appendChild(link);
13611
13714
  }
13612
13715
  }
13716
+ const USER_LOOKUP_ERRORS = /* @__PURE__ */ new Set([
13717
+ "User not found",
13718
+ "Missing required user identifier fields",
13719
+ "Missing user identifier fields"
13720
+ ]);
13721
+ function selectWidgetErrorPresentation({
13722
+ failureKind,
13723
+ customMessage,
13724
+ lastAuthError
13725
+ }) {
13726
+ if (failureKind === "bundle") {
13727
+ const message = "Widget failed to load. Please try again.";
13728
+ return {
13729
+ useCustomMessage: false,
13730
+ displayMessage: message,
13731
+ emittedMessage: message
13732
+ };
13733
+ }
13734
+ const useCustomMessage = Boolean(
13735
+ customMessage && (lastAuthError === null || (lastAuthError.error ? USER_LOOKUP_ERRORS.has(lastAuthError.error) : false))
13736
+ );
13737
+ if (useCustomMessage) {
13738
+ return {
13739
+ useCustomMessage: true,
13740
+ displayMessage: customMessage,
13741
+ emittedMessage: customMessage,
13742
+ hint: lastAuthError == null ? void 0 : lastAuthError.hint,
13743
+ status: lastAuthError == null ? void 0 : lastAuthError.status
13744
+ };
13745
+ }
13746
+ const emittedMessage = (lastAuthError == null ? void 0 : lastAuthError.error) || "Authentication failed";
13747
+ return {
13748
+ useCustomMessage: false,
13749
+ displayMessage: (lastAuthError == null ? void 0 : lastAuthError.hint) ? `${emittedMessage}: ${lastAuthError.hint}` : (lastAuthError == null ? void 0 : lastAuthError.error) ? emittedMessage : "Authentication failed. Please try again later.",
13750
+ emittedMessage,
13751
+ hint: lastAuthError == null ? void 0 : lastAuthError.hint,
13752
+ status: lastAuthError == null ? void 0 : lastAuthError.status
13753
+ };
13754
+ }
13613
13755
  const widgetCSS = ':host{display:block;position:relative;width:100%;height:100%}.rai-loader{position:absolute;top:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.75rem;width:100%;height:100%;background:var(--rai-loading-background, var(--background-5, var(--rai-loader-bg, #ffffff)));border-radius:8px;z-index:10;transition:opacity .3s ease-out;font-family:var(--rai-loader-font, "Roboto", system-ui, -apple-system, sans-serif)}.rai-loader.fade-out{opacity:0;pointer-events:none}.rai-loader-pending-settings{visibility:hidden}.rai-loader-text{margin:.5rem 0 0;font-family:var(--rai-loading-text-font, var(--rai-loader-font, "Roboto", system-ui, -apple-system, sans-serif));font-size:var(--rai-loading-text-size, 1.125rem);line-height:1.75rem;font-weight:var(--rai-loading-text-weight, 400);font-style:var(--rai-loading-text-style, normal);text-decoration:var(--rai-loading-text-decoration, none);letter-spacing:normal;color:var(--rai-loading-text-color, var(--accent-1, var(--rai-accent, #000000)));text-align:center}@media (min-width: 768px){.rai-loader-text{font-size:var(--rai-loading-text-size, 1.5rem);line-height:2rem}}.rai-error{display:none;position:absolute;top:0;left:0;width:100%;height:100%;align-items:center;justify-content:center;flex-direction:column;gap:12px;background:var(--rai-error-bg, #1a1a1a);border-radius:8px;padding:24px;box-sizing:border-box;text-align:center;color:var(--rai-error-text, #9ca3af);font-family:system-ui,-apple-system,sans-serif;z-index:10}.rai-error.visible{display:flex}iframe{display:block;border:none;opacity:0;transition:opacity .3s ease-in}iframe.loaded{opacity:1}.rai-lottie-loader{width:100px;height:100px}.rai-lottie-loader svg{width:100%!important;height:100%!important}.rai-lottie-spinner{width:32px;height:32px;margin:34px auto;border:3px solid var(--rai-text4, #6b7280);border-top-color:var(--rai-accent, #000000);border-radius:50%;animation:rai-spin .7s linear infinite}@keyframes rai-spin{to{transform:rotate(360deg)}}.rai-sdk-loader{position:relative;width:75px;height:100px}.rai-sdk-loader__bar{position:absolute;bottom:0;width:10px;height:50%;background:var(--rai-accent, #000000);transform-origin:center bottom;box-shadow:1px 1px #0003}.rai-sdk-loader__bar:nth-child(1){left:0;transform:scaleY(.2);animation:rai-sdk-barUp1 4s infinite}.rai-sdk-loader__bar:nth-child(2){left:15px;transform:scaleY(.4);animation:rai-sdk-barUp2 4s infinite}.rai-sdk-loader__bar:nth-child(3){left:30px;transform:scaleY(.6);animation:rai-sdk-barUp3 4s infinite}.rai-sdk-loader__bar:nth-child(4){left:45px;transform:scaleY(.8);animation:rai-sdk-barUp4 4s infinite}.rai-sdk-loader__bar:nth-child(5){left:60px;transform:scale(1);animation:rai-sdk-barUp5 4s infinite}.rai-sdk-loader__ball{position:absolute;bottom:10px;left:0;width:10px;height:10px;background:var(--rai-accent, #000000);border-radius:50%;animation:rai-sdk-ball 4s infinite}@keyframes rai-sdk-ball{0%{transform:translate(0)}5%{transform:translate(8px,-14px)}10%{transform:translate(15px,-10px)}17%{transform:translate(23px,-24px)}20%{transform:translate(30px,-20px)}27%{transform:translate(38px,-34px)}30%{transform:translate(45px,-30px)}37%{transform:translate(53px,-44px)}40%{transform:translate(60px,-40px)}50%{transform:translate(60px)}57%{transform:translate(53px,-14px)}60%{transform:translate(45px,-10px)}67%{transform:translate(37px,-24px)}70%{transform:translate(30px,-20px)}77%{transform:translate(22px,-34px)}80%{transform:translate(15px,-30px)}87%{transform:translate(7px,-44px)}90%{transform:translateY(-40px)}to{transform:translate(0)}}@keyframes rai-sdk-barUp1{0%{transform:scaleY(.2)}40%{transform:scaleY(.2)}50%{transform:scale(1)}90%{transform:scale(1)}to{transform:scaleY(.2)}}@keyframes rai-sdk-barUp2{0%{transform:scaleY(.4)}40%{transform:scaleY(.4)}50%{transform:scaleY(.8)}90%{transform:scaleY(.8)}to{transform:scaleY(.4)}}@keyframes rai-sdk-barUp3{0%{transform:scaleY(.6)}to{transform:scaleY(.6)}}@keyframes rai-sdk-barUp4{0%{transform:scaleY(.8)}40%{transform:scaleY(.8)}50%{transform:scaleY(.4)}90%{transform:scaleY(.4)}to{transform:scaleY(.8)}}@keyframes rai-sdk-barUp5{0%{transform:scale(1)}40%{transform:scale(1)}50%{transform:scaleY(.2)}90%{transform:scaleY(.2)}to{transform:scale(1)}}#loading-square{width:75px;aspect-ratio:1;display:flex;color:var(--rai-accent, #000000);background:linear-gradient(currentColor 0 0) right / 51% 100%,linear-gradient(currentColor 0 0) bottom / 100% 51%;background-repeat:no-repeat;animation:l16-0 2s infinite linear .25s}#loading-square>div{width:50%;height:50%;background:currentColor;animation:l16-1 .5s infinite linear}@keyframes l16-0{0%,12.49%{transform:rotate(0)}12.5%,37.49%{transform:rotate(90deg)}37.5%,62.49%{transform:rotate(180deg)}62.5%,87.49%{transform:rotate(270deg)}87.5%,to{transform:rotate(360deg)}}@keyframes l16-1{0%{transform:perspective(80px) rotate3d(-1,-1,0,0)}80%,to{transform:perspective(80px) rotate3d(-1,-1,0,-180deg)}}#loading-circle{width:75px;aspect-ratio:1;display:grid;grid:50%/50%;color:var(--rai-accent, #000000);border-radius:50%;--_g: no-repeat linear-gradient(currentColor 0 0);background:var(--_g),var(--_g),var(--_g);background-size:50.1% 50.1%;animation:l9-0 1.5s infinite steps(1) alternate,l9-0-0 3s infinite steps(1) alternate}#loading-circle>div{background:var(--rai-text4, #6b7280);border-top-left-radius:100px;transform:perspective(150px) rotateY(0) rotateX(0);transform-origin:bottom right;animation:l9-1 1.5s infinite linear alternate}@keyframes l9-0{0%{background-position:0 100%,100% 100%,100% 0}33%{background-position:100% 100%,100% 100%,100% 0}66%{background-position:100% 0,100% 0,100% 0}}@keyframes l9-0-0{0%{transform:scaleX(1) rotate(0)}50%{transform:scaleX(-1) rotate(-90deg)}}@keyframes l9-1{16.5%{transform:perspective(150px) rotateX(-90deg) rotateY(0) rotateX(0);filter:grayscale(.8)}33%{transform:perspective(150px) rotateX(-180deg) rotateY(0) rotateX(0)}66%{transform:perspective(150px) rotateX(-180deg) rotateY(-180deg) rotateX(0)}to{transform:perspective(150px) rotateX(-180deg) rotateY(-180deg) rotateX(-180deg);filter:grayscale(.8)}}.rai-custom-error{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.tiptap strong.tw-font-bold,.tiptap b.tw-font-bold{font-weight:700}.tiptap em.tw-font-italic,.tiptap i.tw-font-italic{font-style:italic}.tiptap a.tiptap-rich-text-link{color:#3b82f6;text-decoration:underline;position:relative;display:inline-block;width:fit-content}.tiptap a.tiptap-rich-text-link:hover{color:#3b82f6cc;text-decoration:none}.tiptap a.tiptap-rich-text-link:not([title=""]):before{content:attr(title);position:absolute;top:-2rem;left:50%;transform:translate(-50%);white-space:nowrap;border-radius:.25rem;background-color:#000;padding:.25rem .5rem;color:#fff;opacity:0;transition:opacity .2s;pointer-events:none}.tiptap a.tiptap-rich-text-link:not([title=""]):hover:before{opacity:1}.tiptap p[style*="text-align: left"]{text-align:left}.tiptap p[style*="text-align: center"]{text-align:center}.tiptap p[style*="text-align: right"]{text-align:right}.tiptap p[style*="text-align: justify"]{text-align:justify}.tiptap u{text-decoration:underline}.tiptap.ProseMirror{background-color:transparent;height:fit-content!important;word-wrap:break-word;white-space:pre-wrap;white-space:break-spaces;-webkit-font-variant-ligatures:none;font-variant-ligatures:none;font-feature-settings:"liga" 0;padding:0;width:100%;overflow-y:auto}.tiptap{line-height:1.6;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.tiptap p{margin:.5em 0}.tiptap p:first-child{margin-top:0}.tiptap p:last-child{margin-bottom:0}.tiptap img{max-width:100%;height:auto;display:block}.tiptap p[style*="text-align: left"] img{margin-left:0;margin-right:auto}.tiptap p[style*="text-align: center"] img{margin-left:auto;margin-right:auto}.tiptap p[style*="text-align: right"] img{margin-left:auto;margin-right:0}.rai-retry-btn{padding:8px 20px;border:none;border-radius:6px;background:var(--rai-accent, #000000);color:var(--rai-error-bg, #ffffff);font-size:14px;font-family:system-ui,-apple-system,sans-serif;cursor:pointer}.rai-retry-btn:hover{opacity:.85}';
13614
13756
  const DEFINED_ATTRS = /* @__PURE__ */ new Set([
13615
13757
  "community-id",
@@ -14007,6 +14149,7 @@ class BaseWidget extends HTMLElement {
14007
14149
  __publicField(this, "bannerNode", null);
14008
14150
  __publicField(this, "releaseAutoThemeDisableLease");
14009
14151
  __publicField(this, "bundleUnmount");
14152
+ __publicField(this, "cancelBundleForegroundRecovery");
14010
14153
  __publicField(this, "authSession");
14011
14154
  this.shadow = this.attachShadow({ mode: "closed" });
14012
14155
  }
@@ -14062,15 +14205,17 @@ class BaseWidget extends HTMLElement {
14062
14205
  }
14063
14206
  }
14064
14207
  disconnectedCallback() {
14065
- var _a, _b, _c, _d;
14208
+ var _a, _b, _c, _d, _e;
14066
14209
  (_a = this.intersectionObserver) == null ? void 0 : _a.disconnect();
14067
14210
  (_b = this.themeObserver) == null ? void 0 : _b.disconnect();
14068
14211
  (_c = this.identityAttributeObserver) == null ? void 0 : _c.disconnect();
14212
+ (_d = this.cancelBundleForegroundRecovery) == null ? void 0 : _d.call(this);
14213
+ this.cancelBundleForegroundRecovery = void 0;
14069
14214
  this.teardownMountedWidget();
14070
14215
  this.clearLottieInstances();
14071
14216
  this.restoreLoaderHeight();
14072
14217
  this.removeBanner();
14073
- (_d = this.authSession) == null ? void 0 : _d.release();
14218
+ (_e = this.authSession) == null ? void 0 : _e.release();
14074
14219
  this.authSession = void 0;
14075
14220
  clearState(this.state);
14076
14221
  }
@@ -14498,25 +14643,24 @@ class BaseWidget extends HTMLElement {
14498
14643
  if (container.children.length > 0) finish();
14499
14644
  });
14500
14645
  }
14501
- showError() {
14646
+ showError(failureKind = "authentication") {
14502
14647
  this.hideLoader();
14503
14648
  const settings = this.state.errorSettings;
14504
- const lastAuthError = this.state.lastAuthError;
14505
- const isUserLookupError = (lastAuthError == null ? void 0 : lastAuthError.error) === "User not found" || (lastAuthError == null ? void 0 : lastAuthError.error) === "Missing required user identifier fields" || (lastAuthError == null ? void 0 : lastAuthError.error) === "Missing user identifier fields";
14506
- const shouldUseCustomMessage = !!(settings == null ? void 0 : settings.errorMessage) && (isUserLookupError || !lastAuthError);
14507
- if (shouldUseCustomMessage) {
14649
+ const presentation = selectWidgetErrorPresentation({
14650
+ failureKind,
14651
+ customMessage: settings == null ? void 0 : settings.errorMessage,
14652
+ lastAuthError: this.state.lastAuthError
14653
+ });
14654
+ if (presentation.useCustomMessage) {
14508
14655
  this.renderCustomError(settings);
14509
14656
  } else if (this.errorEl) {
14510
- if ((lastAuthError == null ? void 0 : lastAuthError.error) && this.msgEl) {
14511
- this.msgEl.textContent = lastAuthError.hint ? `${lastAuthError.error}: ${lastAuthError.hint}` : lastAuthError.error;
14512
- }
14657
+ if (this.msgEl) this.msgEl.textContent = presentation.displayMessage;
14513
14658
  this.errorEl.classList.add("visible");
14514
14659
  }
14515
- const emittedMessage = shouldUseCustomMessage ? settings.errorMessage : (lastAuthError == null ? void 0 : lastAuthError.error) || "Authentication failed";
14516
14660
  this.emit("rai-error", {
14517
- message: emittedMessage,
14518
- hint: lastAuthError == null ? void 0 : lastAuthError.hint,
14519
- status: lastAuthError == null ? void 0 : lastAuthError.status
14661
+ message: presentation.emittedMessage,
14662
+ hint: presentation.hint,
14663
+ status: presentation.status
14520
14664
  });
14521
14665
  }
14522
14666
  renderCustomError(settings) {
@@ -14672,7 +14816,9 @@ class BaseWidget extends HTMLElement {
14672
14816
  }
14673
14817
  // ── Initialization ─────────────────────────────────────────────────────
14674
14818
  async init() {
14675
- var _a;
14819
+ var _a, _b;
14820
+ (_a = this.cancelBundleForegroundRecovery) == null ? void 0 : _a.call(this);
14821
+ this.cancelBundleForegroundRecovery = void 0;
14676
14822
  const initId = ++this.initGeneration;
14677
14823
  this.hasAttemptedInit = true;
14678
14824
  if (!this.config.communityId) {
@@ -14772,7 +14918,7 @@ class BaseWidget extends HTMLElement {
14772
14918
  launchWidget();
14773
14919
  return;
14774
14920
  }
14775
- (_a = this.authSession) == null ? void 0 : _a.publish();
14921
+ (_b = this.authSession) == null ? void 0 : _b.publish();
14776
14922
  const refreshed = await finishStartup(
14777
14923
  this.authSession ? this.authSession.run(
14778
14924
  "refresh",
@@ -14896,7 +15042,7 @@ class BaseWidget extends HTMLElement {
14896
15042
  return { bundleApiUrl, colorMode };
14897
15043
  }
14898
15044
  async mountWidget(initId) {
14899
- var _a;
15045
+ var _a, _b;
14900
15046
  let container = null;
14901
15047
  let cleanupBundleMount;
14902
15048
  try {
@@ -15017,7 +15163,19 @@ class BaseWidget extends HTMLElement {
15017
15163
  container == null ? void 0 : container.remove();
15018
15164
  (_a = this.releaseAutoThemeDisableLease) == null ? void 0 : _a.call(this);
15019
15165
  this.releaseAutoThemeDisableLease = void 0;
15020
- this.showError();
15166
+ if (isBundleReadinessTimeoutError(err) && document.visibilityState === "hidden") {
15167
+ (_b = this.cancelBundleForegroundRecovery) == null ? void 0 : _b.call(this);
15168
+ let scheduledRecovery;
15169
+ scheduledRecovery = scheduleForegroundRecovery(() => {
15170
+ if (this.cancelBundleForegroundRecovery === scheduledRecovery) {
15171
+ this.cancelBundleForegroundRecovery = void 0;
15172
+ }
15173
+ if (this.isConnected) void this.init();
15174
+ });
15175
+ this.cancelBundleForegroundRecovery = scheduledRecovery;
15176
+ return;
15177
+ }
15178
+ this.showError("bundle");
15021
15179
  }
15022
15180
  }
15023
15181
  // ── Token scheduling ──────────────────────────────────────────────────
@@ -15253,7 +15411,7 @@ class CustomWidget extends BaseWidget {
15253
15411
  }
15254
15412
  }
15255
15413
  }
15256
- console.log(`[rai-widget] v${"1.8.4"}`);
15414
+ console.log(`[rai-widget] v${"1.8.5"}`);
15257
15415
  class StoreWidgetCompat extends StoreWidget {
15258
15416
  }
15259
15417
  const WIDGET_REGISTRY = [
@@ -15317,7 +15475,7 @@ function exposePublicApi() {
15317
15475
  })();
15318
15476
  const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
15319
15477
  window.ReturningAIWidget = {
15320
- version: "1.8.4",
15478
+ version: "1.8.5",
15321
15479
  reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
15322
15480
  logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
15323
15481
  isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,
@@ -22,6 +22,7 @@ export declare abstract class BaseWidget extends HTMLElement {
22
22
  private bannerNode;
23
23
  private releaseAutoThemeDisableLease?;
24
24
  private bundleUnmount?;
25
+ private cancelBundleForegroundRecovery?;
25
26
  private authSession?;
26
27
  constructor();
27
28
  registerCallback<K extends keyof ReturningAIWidgetCallbackMap>(callbackName: K, callback: ReturningAIWidgetCallbackMap[K]): void;
@@ -6,7 +6,24 @@ export interface BundleMountUnmountHandle {
6
6
  unmount: () => void;
7
7
  }
8
8
  export type BundleMountReadiness = "deep-ready" | "first-paint";
9
- export declare function resolveBundleMountResult(mountResult: unknown, timeoutMs?: number): Promise<unknown>;
9
+ export type BundleReadinessPhase = "mount" | "deep-ready";
10
+ export interface BundleReadinessEnvironment {
11
+ isHidden: () => boolean;
12
+ now: () => number;
13
+ setTimeout: (callback: () => void, delayMs: number) => unknown;
14
+ clearTimeout: (timerId: unknown) => void;
15
+ addVisibilityChangeListener: (listener: () => void) => void;
16
+ removeVisibilityChangeListener: (listener: () => void) => void;
17
+ addPageShowListener: (listener: () => void) => void;
18
+ removePageShowListener: (listener: () => void) => void;
19
+ }
20
+ export declare class BundleReadinessTimeoutError extends Error {
21
+ readonly phase: BundleReadinessPhase;
22
+ constructor(phase: BundleReadinessPhase, message: string);
23
+ }
24
+ export declare function isBundleReadinessTimeoutError(error: unknown): error is BundleReadinessTimeoutError;
25
+ export declare function scheduleForegroundRecovery(recover: () => void, environment?: BundleReadinessEnvironment): () => void;
26
+ export declare function resolveBundleMountResult(mountResult: unknown, timeoutMs?: number, environment?: BundleReadinessEnvironment): Promise<unknown>;
10
27
  export declare function createBundleMountCleanup(mountResult: unknown): (() => void) | undefined;
11
28
  /**
12
29
  * Wait for the strongest readiness signal a bundle provides.
@@ -17,4 +34,4 @@ export declare function createBundleMountCleanup(mountResult: unknown): (() => v
17
34
  * to an object with a `ready` promise. Legacy bundles keep the first-paint
18
35
  * behavior unchanged.
19
36
  */
20
- export declare function waitForBundleMountReadiness(mountResult: unknown, firstPaint: PromiseLike<void>, timeoutMs?: number): Promise<BundleMountReadiness>;
37
+ export declare function waitForBundleMountReadiness(mountResult: unknown, firstPaint: PromiseLike<void>, timeoutMs?: number, environment?: BundleReadinessEnvironment): Promise<BundleMountReadiness>;
@@ -0,0 +1,15 @@
1
+ import type { WidgetState } from "../types";
2
+ export type WidgetFailureKind = "authentication" | "bundle";
3
+ export interface WidgetErrorPresentationInput {
4
+ failureKind: WidgetFailureKind;
5
+ customMessage?: string | null;
6
+ lastAuthError: WidgetState["lastAuthError"];
7
+ }
8
+ export interface WidgetErrorPresentation {
9
+ useCustomMessage: boolean;
10
+ displayMessage: string;
11
+ emittedMessage: string;
12
+ hint?: string;
13
+ status?: number;
14
+ }
15
+ export declare function selectWidgetErrorPresentation({ failureKind, customMessage, lastAuthError, }: WidgetErrorPresentationInput): WidgetErrorPresentation;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@returningai/widget-sdk",
3
- "version": "1.8.4",
3
+ "version": "1.8.5",
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",
@@ -25,7 +25,7 @@
25
25
  "build:all": "npm run build && npm run build:banner",
26
26
  "build:all:min": "npm run build:min && npm run build:banner:min",
27
27
  "types": "tsc --emitDeclarationOnly --outDir dist/types && cp src/jsx.d.ts dist/types/jsx.d.ts",
28
- "test": "npm run test:host-callback && npm run test:bundle-readiness && npm run test:bundle-runtime-cache && npm run test:bundle-token-sync && npm run test:auth-bootstrap && npm run test:auth-error-reporting && npm run test:auth-refresh-state && npm run test:auth-session-coordinator && npm run test:startup-performance && npm run test:jsx-types",
28
+ "test": "npm run test:host-callback && npm run test:bundle-readiness && npm run test:bundle-runtime-cache && npm run test:bundle-token-sync && npm run test:auth-bootstrap && npm run test:auth-error-reporting && npm run test:auth-refresh-state && npm run test:auth-session-coordinator && npm run test:startup-performance && npm run test:widget-error-presentation && npm run test:jsx-types",
29
29
  "test:host-callback": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/postmessage-host-callback.test.js",
30
30
  "test:bundle-readiness": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/bundle-readiness.test.js",
31
31
  "test:bundle-runtime-cache": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/bundle-runtime-cache.test.js",
@@ -35,6 +35,7 @@
35
35
  "test:auth-refresh-state": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/auth-refresh-state.test.js",
36
36
  "test:auth-session-coordinator": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/auth-session-coordinator.test.js",
37
37
  "test:startup-performance": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/startup-performance.test.js",
38
+ "test:widget-error-presentation": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/widget-error-presentation.test.js",
38
39
  "test:jsx-types": "tsc --noEmit --strict --lib ES2020,DOM --skipLibCheck false src/jsx.d.ts",
39
40
  "coverage:repowise": "rm -rf coverage && npm run coverage:host-callback && npm run coverage:bundle-readiness && npm run test:jsx-types",
40
41
  "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",