@mochabug/adapt-web 1.0.0-rc53 → 1.0.0-rc55

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.
@@ -534,6 +534,7 @@ var MbAdapt = (() => {
534
534
  AdaptCapElement: () => AdaptCapElement,
535
535
  AdaptCapWidget: () => AdaptCapWidget,
536
536
  AdaptWebClient: () => AdaptWebClient,
537
+ clearPersistedState: () => clearPersistedState,
537
538
  configure: () => configure,
538
539
  createChallenge: () => createChallenge,
539
540
  createConnectClient: () => createConnectClient,
@@ -6137,7 +6138,7 @@ var MbAdapt = (() => {
6137
6138
  return false;
6138
6139
  }
6139
6140
  case "session": {
6140
- const isTerminal = TERMINAL_STATUSES.includes(value.status);
6141
+ const isTerminal = TERMINAL_STATUSES.includes(value.status) && !value.fork;
6141
6142
  const status = enumToJson(StatusSchema, value.status);
6142
6143
  currentHandlers?.onSession?.({
6143
6144
  status,
@@ -7201,7 +7202,8 @@ var MbAdapt = (() => {
7201
7202
  "dialog-backdrop-close",
7202
7203
  "dialog-resize-to-content",
7203
7204
  "dark-mode",
7204
- "auto-resizing"
7205
+ "auto-resizing",
7206
+ "persist"
7205
7207
  ];
7206
7208
  var BaseElement = typeof HTMLElement !== "undefined" ? HTMLElement : class {
7207
7209
  };
@@ -7271,6 +7273,24 @@ var MbAdapt = (() => {
7271
7273
  this.removeAttribute("dialog-resize-to-content");
7272
7274
  }
7273
7275
  }
7276
+ get persist() {
7277
+ return this.hasAttribute("persist");
7278
+ }
7279
+ set persist(v) {
7280
+ if (v) {
7281
+ this.setAttribute("persist", "");
7282
+ } else {
7283
+ this.removeAttribute("persist");
7284
+ }
7285
+ }
7286
+ /**
7287
+ * Clear persisted state, stop current session, and reinitialize fresh.
7288
+ */
7289
+ async newSession() {
7290
+ if (this._client) {
7291
+ await this._client.newSession();
7292
+ }
7293
+ }
7274
7294
  // --- Lifecycle ---
7275
7295
  connectedCallback() {
7276
7296
  queueMicrotask(() => this._tryInit());
@@ -7280,7 +7300,7 @@ var MbAdapt = (() => {
7280
7300
  }
7281
7301
  attributeChangedCallback(name, oldValue, newValue) {
7282
7302
  if (oldValue === newValue) return;
7283
- if (name === "automation-id") {
7303
+ if (name === "automation-id" || name === "persist") {
7284
7304
  this._destroyClient();
7285
7305
  queueMicrotask(() => this._tryInit());
7286
7306
  return;
@@ -7378,6 +7398,11 @@ var MbAdapt = (() => {
7378
7398
  if (this.inheritFrom !== void 0) options.inheritFrom = this.inheritFrom;
7379
7399
  if (this.classNames !== void 0) options.classNames = this.classNames;
7380
7400
  if (this.styles !== void 0) options.styles = this.styles;
7401
+ if (this.persistOptions !== void 0) {
7402
+ options.persist = this.persistOptions;
7403
+ } else if (this.persist) {
7404
+ options.persist = true;
7405
+ }
7381
7406
  this._client = new AdaptWebClient(options);
7382
7407
  }
7383
7408
  _destroyClient() {
@@ -7522,10 +7547,19 @@ var MbAdapt = (() => {
7522
7547
  // src/index.ts
7523
7548
  if (typeof window !== "undefined" && true && !window.CAP_CUSTOM_WASM_URL) {
7524
7549
  window.CAP_CUSTOM_WASM_URL = new URL(
7525
- "https://cdn.mochabug.com/adapt/web/1.0.0-rc53/cap_wasm.js",
7550
+ "https://cdn.mochabug.com/adapt/web/1.0.0-rc55/cap_wasm.js",
7526
7551
  window.location.href
7527
7552
  ).href;
7528
7553
  }
7554
+ function clearPersistedState(automationId, storage = "both") {
7555
+ const key = `mb_adapt_${automationId}`;
7556
+ if (storage === "session" || storage === "both") {
7557
+ sessionStorage.removeItem(key);
7558
+ }
7559
+ if (storage === "local" || storage === "both") {
7560
+ localStorage.removeItem(key);
7561
+ }
7562
+ }
7529
7563
  var DEFAULT_STYLES = `
7530
7564
  .mb-adapt {
7531
7565
  width: 100%;
@@ -8429,6 +8463,7 @@ cap-widget::part(attribution) {
8429
8463
  this.splitPercentage = unclampedSplit;
8430
8464
  }
8431
8465
  unclampedSplit = this.splitPercentage;
8466
+ this.saveState();
8432
8467
  };
8433
8468
  this.mouseMoveHandler = handleMouseMove;
8434
8469
  this.mouseUpHandler = handleMouseUp;
@@ -8491,6 +8526,9 @@ cap-widget::part(attribution) {
8491
8526
  if (sessionJson.status === "STATUS_COMPLETED" && !sessionJson.fork) {
8492
8527
  this.handleSessionComplete();
8493
8528
  }
8529
+ if (!sessionJson.fork && (sessionJson.status === "STATUS_COMPLETED" || sessionJson.status === "STATUS_STOPPED" || sessionJson.status === "STATUS_ERRORED")) {
8530
+ this.clearState();
8531
+ }
8494
8532
  if (this.options.onSession) {
8495
8533
  this.options.onSession(
8496
8534
  sessionJson.status || "STATUS_UNSPECIFIED",
@@ -8502,6 +8540,10 @@ cap-widget::part(attribution) {
8502
8540
  if (this.options.onOutput) {
8503
8541
  handlers.onOutput = this.options.onOutput;
8504
8542
  }
8543
+ if (!this.options.sessionToken && this.resolvePersistOptions()) {
8544
+ const restored = await this.tryRestoreSession(handlers);
8545
+ if (restored) return;
8546
+ }
8505
8547
  if (this.options.sessionToken) {
8506
8548
  this.sessionToken = this.options.sessionToken;
8507
8549
  await this.client.subscribe(this.sessionToken, handlers);
@@ -8539,6 +8581,8 @@ cap-widget::part(attribution) {
8539
8581
  result = await this.client.run(runOptions);
8540
8582
  }
8541
8583
  this.sessionToken = result.sessionToken;
8584
+ this.sessionExpiresAt = result.expiresAt;
8585
+ this.saveState();
8542
8586
  }
8543
8587
  }
8544
8588
  /**
@@ -8581,10 +8625,186 @@ cap-widget::part(attribution) {
8581
8625
  this.capWidgetInstance = null;
8582
8626
  }
8583
8627
  }
8628
+ // --- Persistence helpers ---
8629
+ resolvePersistOptions() {
8630
+ const p = this.options.persist;
8631
+ if (!p) return null;
8632
+ if (p === true) return { storage: "session", ttl: 3600 };
8633
+ return { storage: p.storage ?? "session", ttl: p.ttl ?? 3600 };
8634
+ }
8635
+ getStorageKey() {
8636
+ return `mb_adapt_${this.options.id}`;
8637
+ }
8638
+ getStorage() {
8639
+ const opts = this.resolvePersistOptions();
8640
+ if (!opts) return null;
8641
+ return opts.storage === "local" ? localStorage : sessionStorage;
8642
+ }
8643
+ saveState() {
8644
+ const storage = this.getStorage();
8645
+ if (!storage || !this.sessionToken) return;
8646
+ const opts = this.resolvePersistOptions();
8647
+ const state = {
8648
+ v: 1,
8649
+ token: this.sessionToken,
8650
+ expiresAt: this.sessionExpiresAt ? this.sessionExpiresAt.toISOString() : null,
8651
+ savedAt: Date.now(),
8652
+ ttl: opts.ttl,
8653
+ mainUrl: this.mainUrl,
8654
+ mainToken: this.mainToken,
8655
+ currentFork: this.currentFork ? {
8656
+ url: this.currentFork.url,
8657
+ token: this.currentFork.token,
8658
+ fork: this.currentFork.fork,
8659
+ depth: this.currentFork.depth,
8660
+ time: this.currentFork.time,
8661
+ completed: this.currentFork.completed
8662
+ } : null,
8663
+ forkQueue: this.forkQueue.map((f) => ({
8664
+ url: f.url,
8665
+ token: f.token,
8666
+ fork: f.fork,
8667
+ depth: f.depth,
8668
+ time: f.time,
8669
+ completed: f.completed
8670
+ })),
8671
+ forkDisplayMode: this.forkDisplay.mode,
8672
+ splitPercentage: this.splitPercentage
8673
+ };
8674
+ storage.setItem(this.getStorageKey(), JSON.stringify(state));
8675
+ }
8676
+ loadState() {
8677
+ const storage = this.getStorage();
8678
+ if (!storage) return null;
8679
+ const raw = storage.getItem(this.getStorageKey());
8680
+ if (!raw) return null;
8681
+ let state;
8682
+ try {
8683
+ state = JSON.parse(raw);
8684
+ } catch {
8685
+ this.clearState();
8686
+ return null;
8687
+ }
8688
+ if (state.v !== 1) {
8689
+ this.clearState();
8690
+ return null;
8691
+ }
8692
+ const now = Date.now();
8693
+ const leeway = 3e4;
8694
+ if (now - state.savedAt > state.ttl * 1e3 + leeway) {
8695
+ this.clearState();
8696
+ return null;
8697
+ }
8698
+ if (state.expiresAt) {
8699
+ const expires = new Date(state.expiresAt).getTime();
8700
+ if (now > expires + leeway) {
8701
+ this.clearState();
8702
+ return null;
8703
+ }
8704
+ }
8705
+ return state;
8706
+ }
8707
+ clearState() {
8708
+ const storage = this.getStorage();
8709
+ if (!storage) return;
8710
+ storage.removeItem(this.getStorageKey());
8711
+ }
8712
+ async tryRestoreSession(handlers) {
8713
+ const state = this.loadState();
8714
+ if (!state) return false;
8715
+ this.sessionToken = state.token;
8716
+ this.sessionExpiresAt = state.expiresAt ? new Date(state.expiresAt) : void 0;
8717
+ this.mainUrl = state.mainUrl;
8718
+ this.mainToken = state.mainToken;
8719
+ this.forkQueue = state.forkQueue;
8720
+ this.currentFork = state.currentFork;
8721
+ if (state.forkDisplayMode === this.forkDisplay.mode) {
8722
+ if (this.forkDisplay.mode === "side-by-side") {
8723
+ this.splitPercentage = state.splitPercentage;
8724
+ }
8725
+ }
8726
+ if (this.mainUrl && this.mainIframe) {
8727
+ const newSrc = getIframeSrc(this.mainUrl, this.mainToken);
8728
+ if (this.mainIframe.src !== newSrc) {
8729
+ this.mainIframe.src = newSrc;
8730
+ }
8731
+ this.showIframe(this.mainIframe);
8732
+ }
8733
+ if (this.currentFork && this.forkIframe) {
8734
+ this.updateForkIframe();
8735
+ }
8736
+ if (this.forkDisplay.mode === "dialog") {
8737
+ this.updateDialogVisibility();
8738
+ } else {
8739
+ this.updateSideBySideVisibility();
8740
+ }
8741
+ try {
8742
+ await this.client.subscribe(state.token, handlers);
8743
+ handlers.onSession?.({ status: "STATUS_RUNNING" });
8744
+ } catch {
8745
+ this.clearState();
8746
+ this.sessionToken = null;
8747
+ this.sessionExpiresAt = void 0;
8748
+ this.mainUrl = null;
8749
+ this.mainToken = void 0;
8750
+ this.forkQueue = [];
8751
+ this.currentFork = null;
8752
+ if (this.mainIframe) {
8753
+ this.mainIframe.src = "about:blank";
8754
+ this.mainIframe.classList.remove("mb-adapt__iframe--visible");
8755
+ }
8756
+ if (this.forkIframe) {
8757
+ this.forkIframe.src = "about:blank";
8758
+ this.forkIframe.classList.remove("mb-adapt__iframe--visible");
8759
+ this.forkIframe.classList.add("mb-adapt__iframe--hidden");
8760
+ }
8761
+ if (this.forkDisplay.mode === "side-by-side") {
8762
+ this.updateSideBySideVisibility();
8763
+ } else {
8764
+ this.updateDialogVisibility();
8765
+ }
8766
+ return false;
8767
+ }
8768
+ return true;
8769
+ }
8770
+ /**
8771
+ * Clear persisted state, stop current session, and reinitialize fresh.
8772
+ */
8773
+ async newSession() {
8774
+ this.clearState();
8775
+ if (this.sessionToken) {
8776
+ this.client.stop(this.sessionToken).catch(() => {
8777
+ });
8778
+ }
8779
+ await this.client.unsubscribe();
8780
+ this.mainUrl = null;
8781
+ this.mainToken = void 0;
8782
+ this.forkQueue = [];
8783
+ this.currentFork = null;
8784
+ this.sessionToken = null;
8785
+ this.sessionExpiresAt = void 0;
8786
+ this.lastForkActive = null;
8787
+ if (this.mainIframe) {
8788
+ this.mainIframe.src = "about:blank";
8789
+ this.mainIframe.classList.remove("mb-adapt__iframe--visible");
8790
+ }
8791
+ if (this.forkIframe) {
8792
+ this.forkIframe.src = "about:blank";
8793
+ this.forkIframe.classList.remove("mb-adapt__iframe--visible");
8794
+ this.forkIframe.classList.add("mb-adapt__iframe--hidden");
8795
+ }
8796
+ this.removeStoppedPlaceholder();
8797
+ if (this.forkDisplay.mode === "side-by-side") {
8798
+ this.updateSideBySideVisibility();
8799
+ } else {
8800
+ this.updateDialogVisibility();
8801
+ }
8802
+ await this.init();
8803
+ }
8584
8804
  async destroy() {
8585
8805
  this.destroyed = true;
8586
8806
  this.destroyCapWidget();
8587
- if (this.sessionToken) {
8807
+ if (this.sessionToken && !this.loadState()) {
8588
8808
  this.client.stop(this.sessionToken).catch(() => {
8589
8809
  });
8590
8810
  }
@@ -8633,10 +8853,12 @@ cap-widget::part(attribution) {
8633
8853
  const fork = msg.fork || "";
8634
8854
  if (msg.stopped) {
8635
8855
  this.handleStoppedMessage(fork);
8856
+ this.saveState();
8636
8857
  return;
8637
8858
  }
8638
8859
  if (msg.done) {
8639
8860
  this.handleDoneMessage(fork);
8861
+ this.saveState();
8640
8862
  return;
8641
8863
  }
8642
8864
  if (!msg.url) return;
@@ -8645,6 +8867,7 @@ cap-widget::part(attribution) {
8645
8867
  } else {
8646
8868
  this.handleForkUrl(msg.url, msg.token, fork);
8647
8869
  }
8870
+ this.saveState();
8648
8871
  }
8649
8872
  handleSessionComplete() {
8650
8873
  this.currentFork = null;
@@ -8749,6 +8972,7 @@ cap-widget::part(attribution) {
8749
8972
  } else {
8750
8973
  this.updateSideBySideVisibility();
8751
8974
  }
8975
+ this.saveState();
8752
8976
  }
8753
8977
  updateMainIframe() {
8754
8978
  if (!this.rootElement || !this.mainUrl || !this.mainIframe) return;
@@ -8834,6 +9058,7 @@ cap-widget::part(attribution) {
8834
9058
  this.splitPercentage = 50;
8835
9059
  }
8836
9060
  this.updateSideBySideVisibility();
9061
+ this.saveState();
8837
9062
  });
8838
9063
  this.wrapperElement.appendChild(this.mainFrameElement);
8839
9064
  this.wrapperElement.appendChild(this.forkFrameElement);
@@ -8984,6 +9209,7 @@ cap-widget::part(attribution) {
8984
9209
  this.currentFork = null;
8985
9210
  this.updateDialogVisibility();
8986
9211
  }
9212
+ this.saveState();
8987
9213
  }
8988
9214
  handleForkExit() {
8989
9215
  if (!this.currentFork) return;
@@ -9008,6 +9234,7 @@ cap-widget::part(attribution) {
9008
9234
  this.updateDialogVisibility();
9009
9235
  }
9010
9236
  }
9237
+ this.saveState();
9011
9238
  }
9012
9239
  createDialogStructure() {
9013
9240
  if (this.mainContainer) return;