@mochabug/adapt-web 1.0.0-rc57 → 1.0.0-rc59

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.
@@ -249,6 +249,8 @@ export class AdaptAutomationElement extends BaseElement {
249
249
  options.classNames = this.classNames;
250
250
  if (this.styles !== undefined)
251
251
  options.styles = this.styles;
252
+ if (this.text !== undefined)
253
+ options.text = this.text;
252
254
  // Persist: object options take precedence, otherwise use boolean attribute
253
255
  if (this.persistOptions !== undefined) {
254
256
  options.persist = this.persistOptions;
package/dist/esm/index.js CHANGED
@@ -4,12 +4,12 @@ if (typeof window !== "undefined" &&
4
4
  // Convert to absolute URL - Web Workers run in blob URL context and can't resolve relative paths
5
5
  window.CAP_CUSTOM_WASM_URL = new URL(__CAP_WASM_URL__, window.location.href).href;
6
6
  }
7
- import { configure, createAdaptClient, resetConfig, } from "@mochabug/adapt-core";
7
+ import { Code, configure, ConnectError, createAdaptClient, resetConfig, } from "@mochabug/adapt-core";
8
8
  import { createConnectClient } from "@mochabug/adapt-core/connect";
9
9
  import { AdaptCapWidget } from "./AdaptCapWidget.js";
10
10
  import { getIframeSrc } from "./iframe-url.js";
11
11
  // Re-export standalone Cap widget for independent use
12
- export { AdaptCapWidget, } from "./AdaptCapWidget.js";
12
+ export { AdaptCapWidget } from "./AdaptCapWidget.js";
13
13
  // Re-export challenge functions for direct use
14
14
  export { createChallenge, redeemChallenge } from "./cap-adapter.js";
15
15
  // Re-export createConnectClient for standalone Cap widget usage
@@ -559,7 +559,7 @@ cap-widget::part(attribution) {
559
559
  display: none;
560
560
  }
561
561
 
562
- .mb-adapt__stopped-placeholder {
562
+ .mb-adapt__status-message {
563
563
  display: flex;
564
564
  align-items: center;
565
565
  justify-content: center;
@@ -613,8 +613,8 @@ export class AdaptWebClient {
613
613
  this.backdropClickHandler = null;
614
614
  this.destroyed = false;
615
615
  this.lastForkActive = null;
616
- // Stopped placeholder element
617
- this.stoppedPlaceholder = null;
616
+ // Status message element (stopped / error)
617
+ this.statusMessageElement = null;
618
618
  // Cap.js widget instance (for PoW challenges)
619
619
  this.capWidgetInstance = null;
620
620
  this.options = options;
@@ -652,7 +652,9 @@ export class AdaptWebClient {
652
652
  this.setupDragListeners();
653
653
  this.setupResizeObserver();
654
654
  this.setupMessageListener();
655
- this.init();
655
+ this.init().catch((error) => {
656
+ this.showStatusMessage(this.getErrorMessage(error));
657
+ });
656
658
  }
657
659
  createInitialStructure() {
658
660
  // Create DOM structure immediately based on display mode
@@ -1432,7 +1434,7 @@ export class AdaptWebClient {
1432
1434
  this.forkIframe.classList.add("mb-adapt__iframe--hidden");
1433
1435
  }
1434
1436
  // Reset visual state
1435
- this.removeStoppedPlaceholder();
1437
+ this.removeStatusMessage();
1436
1438
  if (this.forkDisplay.mode === "side-by-side") {
1437
1439
  this.updateSideBySideVisibility();
1438
1440
  }
@@ -1440,7 +1442,12 @@ export class AdaptWebClient {
1440
1442
  this.updateDialogVisibility();
1441
1443
  }
1442
1444
  // Reinitialize
1443
- await this.init();
1445
+ try {
1446
+ await this.init();
1447
+ }
1448
+ catch (error) {
1449
+ this.showStatusMessage(this.getErrorMessage(error));
1450
+ }
1444
1451
  }
1445
1452
  async destroy() {
1446
1453
  this.destroyed = true;
@@ -1492,7 +1499,7 @@ export class AdaptWebClient {
1492
1499
  this.resizeObserver = null;
1493
1500
  this.messageHandler = null;
1494
1501
  this.backdropClickHandler = null;
1495
- this.stoppedPlaceholder = null;
1502
+ this.statusMessageElement = null;
1496
1503
  }
1497
1504
  onUrl(msg) {
1498
1505
  if (this.destroyed)
@@ -1568,7 +1575,7 @@ export class AdaptWebClient {
1568
1575
  this.mainIframe.classList.remove("mb-adapt__iframe--visible");
1569
1576
  this.mainIframe.classList.add("mb-adapt__iframe--hidden");
1570
1577
  }
1571
- this.showStoppedPlaceholder();
1578
+ this.showStatusMessage(this.options.text?.stopped ?? "This session has been stopped");
1572
1579
  return;
1573
1580
  }
1574
1581
  // Fork stopped — remove from queue
@@ -1602,13 +1609,30 @@ export class AdaptWebClient {
1602
1609
  }
1603
1610
  }
1604
1611
  handleMainUrl(url, token) {
1605
- this.removeStoppedPlaceholder();
1612
+ this.removeStatusMessage();
1606
1613
  this.mainUrl = url;
1607
1614
  this.mainToken = token;
1608
1615
  // Update main iframe only - never touch dialog state
1609
1616
  this.updateMainIframe();
1610
1617
  }
1611
1618
  handleForkUrl(url, token, fork) {
1619
+ // If this fork is already the current fork, update it in-place
1620
+ if (this.currentFork?.fork === fork) {
1621
+ this.currentFork.url = url;
1622
+ this.currentFork.token = token;
1623
+ this.currentFork.time = Date.now();
1624
+ this.updateForkIframe();
1625
+ return;
1626
+ }
1627
+ // If this fork is already queued, update in-place
1628
+ const queued = this.forkQueue.find((item) => item.fork === fork);
1629
+ if (queued) {
1630
+ queued.url = url;
1631
+ queued.token = token;
1632
+ queued.time = Date.now();
1633
+ return;
1634
+ }
1635
+ // New fork — add to queue
1612
1636
  const depth = (fork.match(/\//g) || []).length;
1613
1637
  const forkItem = {
1614
1638
  url,
@@ -1623,7 +1647,6 @@ export class AdaptWebClient {
1623
1647
  this.mainToken = token;
1624
1648
  this.updateMainIframe();
1625
1649
  }
1626
- // Add to queue and activate if no current fork or current fork is completed.
1627
1650
  this.forkQueue.push(forkItem);
1628
1651
  if (!this.currentFork || this.currentFork.completed) {
1629
1652
  this.currentFork = null;
@@ -2093,22 +2116,43 @@ export class AdaptWebClient {
2093
2116
  });
2094
2117
  return iframe;
2095
2118
  }
2096
- showStoppedPlaceholder() {
2119
+ showStatusMessage(text) {
2097
2120
  if (!this.mainFrameElement && !this.mainContainer)
2098
2121
  return;
2099
- // Remove existing placeholder if any
2100
- this.removeStoppedPlaceholder();
2101
- this.stoppedPlaceholder = document.createElement("div");
2102
- this.stoppedPlaceholder.className = "mb-adapt__stopped-placeholder";
2103
- this.stoppedPlaceholder.textContent = "This session has been stopped";
2122
+ // Remove existing message if any
2123
+ this.removeStatusMessage();
2124
+ this.statusMessageElement = document.createElement("div");
2125
+ this.statusMessageElement.className =
2126
+ this.options.classNames?.statusMessage ?? "mb-adapt__status-message";
2127
+ this.statusMessageElement.textContent = text;
2104
2128
  const parent = this.mainFrameElement || this.mainContainer;
2105
- parent.appendChild(this.stoppedPlaceholder);
2129
+ parent.appendChild(this.statusMessageElement);
2106
2130
  }
2107
- removeStoppedPlaceholder() {
2108
- if (this.stoppedPlaceholder) {
2109
- this.stoppedPlaceholder.remove();
2110
- this.stoppedPlaceholder = null;
2131
+ removeStatusMessage() {
2132
+ if (this.statusMessageElement) {
2133
+ this.statusMessageElement.remove();
2134
+ this.statusMessageElement = null;
2135
+ }
2136
+ }
2137
+ getErrorMessage(error) {
2138
+ const text = this.options.text;
2139
+ const fallback = text?.error ?? "Something went wrong. Please try again later.";
2140
+ if (error instanceof ConnectError) {
2141
+ switch (error.code) {
2142
+ case Code.ResourceExhausted:
2143
+ return (text?.resourceExhausted ??
2144
+ "This automation is currently at capacity. Please try again later.");
2145
+ case Code.NotFound:
2146
+ return text?.notFound ?? "This automation could not be found.";
2147
+ case Code.PermissionDenied:
2148
+ case Code.Unauthenticated:
2149
+ return (text?.permissionDenied ??
2150
+ "You don't have permission to run this automation.");
2151
+ default:
2152
+ return fallback;
2153
+ }
2111
2154
  }
2155
+ return fallback;
2112
2156
  }
2113
2157
  showIframe(iframe) {
2114
2158
  iframe.classList.remove("mb-adapt__iframe--hidden");
@@ -1,4 +1,4 @@
1
- import type { AdaptWebClientOptions, CapWidgetOptions, Output, PersistOptions, SignalValue, StatusJson } from "./types.js";
1
+ import type { AdaptWebClientOptions, CapWidgetOptions, Output, PersistOptions, SignalValue, StatusJson, StatusText } from "./types.js";
2
2
  /**
3
3
  * `<adapt-automation>` custom element for embedding Adapt automations.
4
4
  *
@@ -34,6 +34,7 @@ export declare class AdaptAutomationElement extends BaseElement {
34
34
  classNames: AdaptWebClientOptions["classNames"];
35
35
  styles: Partial<CSSStyleDeclaration> | undefined;
36
36
  persistOptions: PersistOptions | undefined;
37
+ text: StatusText | undefined;
37
38
  onSessionCallback: ((status: StatusJson, fork?: string) => void) | undefined;
38
39
  onOutputCallback: ((output: Output) => void) | undefined;
39
40
  onForkActiveCallback: ((active: boolean) => void) | undefined;
@@ -6,8 +6,8 @@ declare global {
6
6
  import { configure, resetConfig } from "@mochabug/adapt-core";
7
7
  import { createConnectClient } from "@mochabug/adapt-core/connect";
8
8
  import type { AdaptWebClientOptions } from "./types.js";
9
- export type { AdaptWebClientOptions, CapWidgetI18n, CapWidgetOptions, ChallengeInfo, ForkDisplay, Output, PersistOptions, RedeemedChallenge, SignalValue, StatusJson, } from "./types.js";
10
- export { AdaptCapWidget, type AdaptCapWidgetOptions, } from "./AdaptCapWidget.js";
9
+ export type { AdaptWebClientOptions, CapWidgetI18n, CapWidgetOptions, ChallengeInfo, ForkDisplay, Output, PersistOptions, RedeemedChallenge, SignalValue, StatusJson, StatusText } from "./types.js";
10
+ export { AdaptCapWidget, type AdaptCapWidgetOptions } from "./AdaptCapWidget.js";
11
11
  export { createChallenge, redeemChallenge } from "./cap-adapter.js";
12
12
  export type { SignalData } from "@mochabug/adapt-core";
13
13
  export { createConnectClient };
@@ -82,7 +82,7 @@ export declare class AdaptWebClient {
82
82
  private destroyed;
83
83
  private lastForkActive;
84
84
  private sessionExpiresAt;
85
- private stoppedPlaceholder;
85
+ private statusMessageElement;
86
86
  private capWidgetInstance;
87
87
  constructor(options: AdaptWebClientOptions);
88
88
  private createInitialStructure;
@@ -167,7 +167,8 @@ export declare class AdaptWebClient {
167
167
  private createDialogStructure;
168
168
  private updateDialogVisibility;
169
169
  private createHiddenIframe;
170
- private showStoppedPlaceholder;
171
- private removeStoppedPlaceholder;
170
+ private showStatusMessage;
171
+ private removeStatusMessage;
172
+ private getErrorMessage;
172
173
  private showIframe;
173
174
  }
@@ -361,6 +361,8 @@ export interface AdaptWebClientOptions {
361
361
  dragHandle?: string;
362
362
  /** Expand button for collapsed panels. Default: `'mb-adapt__expand-button'` */
363
363
  expandButton?: string;
364
+ /** Status message overlay (stopped / error). Default: `'mb-adapt__status-message'` */
365
+ statusMessage?: string;
364
366
  /** Fork toolbar container. Default: `'mb-adapt__toolbar'` */
365
367
  toolbar?: string;
366
368
  /** Fork toolbar title text. Default: `'mb-adapt__toolbar-title'` */
@@ -395,4 +397,24 @@ export interface AdaptWebClientOptions {
395
397
  * @default undefined (disabled)
396
398
  */
397
399
  persist?: boolean | PersistOptions;
400
+ /**
401
+ * Custom text overrides for status messages shown to the user.
402
+ * Each property is optional — unspecified messages use built-in defaults.
403
+ */
404
+ text?: StatusText;
405
+ }
406
+ /**
407
+ * Custom text overrides for status messages and error states.
408
+ */
409
+ export interface StatusText {
410
+ /** Shown when the session is stopped. Default: `"This session has been stopped"` */
411
+ stopped?: string;
412
+ /** Shown on ResourceExhausted. Default: `"This automation is currently at capacity. Please try again later."` */
413
+ resourceExhausted?: string;
414
+ /** Shown on NotFound. Default: `"This automation could not be found."` */
415
+ notFound?: string;
416
+ /** Shown on PermissionDenied or Unauthenticated. Default: `"You don't have permission to run this automation."` */
417
+ permissionDenied?: string;
418
+ /** Shown on any other non-retriable error. Default: `"Something went wrong. Please try again later."` */
419
+ error?: string;
398
420
  }
@@ -6073,7 +6073,8 @@ var MbAdapt = (() => {
6073
6073
  Code.PermissionDenied,
6074
6074
  Code.Unauthenticated,
6075
6075
  Code.InvalidArgument,
6076
- Code.FailedPrecondition
6076
+ Code.FailedPrecondition,
6077
+ Code.ResourceExhausted
6077
6078
  ];
6078
6079
  const isRetriableError = (error) => {
6079
6080
  if (error instanceof ConnectError) {
@@ -7398,6 +7399,7 @@ var MbAdapt = (() => {
7398
7399
  if (this.inheritFrom !== void 0) options.inheritFrom = this.inheritFrom;
7399
7400
  if (this.classNames !== void 0) options.classNames = this.classNames;
7400
7401
  if (this.styles !== void 0) options.styles = this.styles;
7402
+ if (this.text !== void 0) options.text = this.text;
7401
7403
  if (this.persistOptions !== void 0) {
7402
7404
  options.persist = this.persistOptions;
7403
7405
  } else if (this.persist) {
@@ -7547,7 +7549,7 @@ var MbAdapt = (() => {
7547
7549
  // src/index.ts
7548
7550
  if (typeof window !== "undefined" && true && !window.CAP_CUSTOM_WASM_URL) {
7549
7551
  window.CAP_CUSTOM_WASM_URL = new URL(
7550
- "https://cdn.mochabug.com/adapt/web/1.0.0-rc57/cap_wasm.js",
7552
+ "https://cdn.mochabug.com/adapt/web/1.0.0-rc59/cap_wasm.js",
7551
7553
  window.location.href
7552
7554
  ).href;
7553
7555
  }
@@ -8081,7 +8083,7 @@ cap-widget::part(attribution) {
8081
8083
  display: none;
8082
8084
  }
8083
8085
 
8084
- .mb-adapt__stopped-placeholder {
8086
+ .mb-adapt__status-message {
8085
8087
  display: flex;
8086
8088
  align-items: center;
8087
8089
  justify-content: center;
@@ -8128,8 +8130,8 @@ cap-widget::part(attribution) {
8128
8130
  this.backdropClickHandler = null;
8129
8131
  this.destroyed = false;
8130
8132
  this.lastForkActive = null;
8131
- // Stopped placeholder element
8132
- this.stoppedPlaceholder = null;
8133
+ // Status message element (stopped / error)
8134
+ this.statusMessageElement = null;
8133
8135
  // Cap.js widget instance (for PoW challenges)
8134
8136
  this.capWidgetInstance = null;
8135
8137
  this.options = options;
@@ -8159,7 +8161,9 @@ cap-widget::part(attribution) {
8159
8161
  this.setupDragListeners();
8160
8162
  this.setupResizeObserver();
8161
8163
  this.setupMessageListener();
8162
- this.init();
8164
+ this.init().catch((error) => {
8165
+ this.showStatusMessage(this.getErrorMessage(error));
8166
+ });
8163
8167
  }
8164
8168
  static {
8165
8169
  this.COLLAPSE_THRESHOLD = 15;
@@ -8794,13 +8798,17 @@ cap-widget::part(attribution) {
8794
8798
  this.forkIframe.classList.remove("mb-adapt__iframe--visible");
8795
8799
  this.forkIframe.classList.add("mb-adapt__iframe--hidden");
8796
8800
  }
8797
- this.removeStoppedPlaceholder();
8801
+ this.removeStatusMessage();
8798
8802
  if (this.forkDisplay.mode === "side-by-side") {
8799
8803
  this.updateSideBySideVisibility();
8800
8804
  } else {
8801
8805
  this.updateDialogVisibility();
8802
8806
  }
8803
- await this.init();
8807
+ try {
8808
+ await this.init();
8809
+ } catch (error) {
8810
+ this.showStatusMessage(this.getErrorMessage(error));
8811
+ }
8804
8812
  }
8805
8813
  async destroy() {
8806
8814
  this.destroyed = true;
@@ -8847,7 +8855,7 @@ cap-widget::part(attribution) {
8847
8855
  this.resizeObserver = null;
8848
8856
  this.messageHandler = null;
8849
8857
  this.backdropClickHandler = null;
8850
- this.stoppedPlaceholder = null;
8858
+ this.statusMessageElement = null;
8851
8859
  }
8852
8860
  onUrl(msg) {
8853
8861
  if (this.destroyed) return;
@@ -8908,7 +8916,9 @@ cap-widget::part(attribution) {
8908
8916
  this.mainIframe.classList.remove("mb-adapt__iframe--visible");
8909
8917
  this.mainIframe.classList.add("mb-adapt__iframe--hidden");
8910
8918
  }
8911
- this.showStoppedPlaceholder();
8919
+ this.showStatusMessage(
8920
+ this.options.text?.stopped ?? "This session has been stopped"
8921
+ );
8912
8922
  return;
8913
8923
  }
8914
8924
  this.forkQueue = this.forkQueue.filter((item) => item.fork !== fork);
@@ -8935,12 +8945,26 @@ cap-widget::part(attribution) {
8935
8945
  }
8936
8946
  }
8937
8947
  handleMainUrl(url, token) {
8938
- this.removeStoppedPlaceholder();
8948
+ this.removeStatusMessage();
8939
8949
  this.mainUrl = url;
8940
8950
  this.mainToken = token;
8941
8951
  this.updateMainIframe();
8942
8952
  }
8943
8953
  handleForkUrl(url, token, fork) {
8954
+ if (this.currentFork?.fork === fork) {
8955
+ this.currentFork.url = url;
8956
+ this.currentFork.token = token;
8957
+ this.currentFork.time = Date.now();
8958
+ this.updateForkIframe();
8959
+ return;
8960
+ }
8961
+ const queued = this.forkQueue.find((item) => item.fork === fork);
8962
+ if (queued) {
8963
+ queued.url = url;
8964
+ queued.token = token;
8965
+ queued.time = Date.now();
8966
+ return;
8967
+ }
8944
8968
  const depth = (fork.match(/\//g) || []).length;
8945
8969
  const forkItem = {
8946
8970
  url,
@@ -9345,20 +9369,38 @@ cap-widget::part(attribution) {
9345
9369
  });
9346
9370
  return iframe;
9347
9371
  }
9348
- showStoppedPlaceholder() {
9372
+ showStatusMessage(text) {
9349
9373
  if (!this.mainFrameElement && !this.mainContainer) return;
9350
- this.removeStoppedPlaceholder();
9351
- this.stoppedPlaceholder = document.createElement("div");
9352
- this.stoppedPlaceholder.className = "mb-adapt__stopped-placeholder";
9353
- this.stoppedPlaceholder.textContent = "This session has been stopped";
9374
+ this.removeStatusMessage();
9375
+ this.statusMessageElement = document.createElement("div");
9376
+ this.statusMessageElement.className = this.options.classNames?.statusMessage ?? "mb-adapt__status-message";
9377
+ this.statusMessageElement.textContent = text;
9354
9378
  const parent = this.mainFrameElement || this.mainContainer;
9355
- parent.appendChild(this.stoppedPlaceholder);
9379
+ parent.appendChild(this.statusMessageElement);
9380
+ }
9381
+ removeStatusMessage() {
9382
+ if (this.statusMessageElement) {
9383
+ this.statusMessageElement.remove();
9384
+ this.statusMessageElement = null;
9385
+ }
9356
9386
  }
9357
- removeStoppedPlaceholder() {
9358
- if (this.stoppedPlaceholder) {
9359
- this.stoppedPlaceholder.remove();
9360
- this.stoppedPlaceholder = null;
9387
+ getErrorMessage(error) {
9388
+ const text = this.options.text;
9389
+ const fallback = text?.error ?? "Something went wrong. Please try again later.";
9390
+ if (error instanceof ConnectError) {
9391
+ switch (error.code) {
9392
+ case Code.ResourceExhausted:
9393
+ return text?.resourceExhausted ?? "This automation is currently at capacity. Please try again later.";
9394
+ case Code.NotFound:
9395
+ return text?.notFound ?? "This automation could not be found.";
9396
+ case Code.PermissionDenied:
9397
+ case Code.Unauthenticated:
9398
+ return text?.permissionDenied ?? "You don't have permission to run this automation.";
9399
+ default:
9400
+ return fallback;
9401
+ }
9361
9402
  }
9403
+ return fallback;
9362
9404
  }
9363
9405
  showIframe(iframe) {
9364
9406
  iframe.classList.remove("mb-adapt__iframe--hidden");