@salesforce/experimental-mfe-lwc-shell 2.2.1-rc.1 → 2.2.1-rc.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.
@@ -40,7 +40,6 @@ export declare class InternalHostLwcShell extends HTMLElement {
40
40
  private _title;
41
41
  private _view;
42
42
  private _debugEnabled;
43
- private _graphqlHandler;
44
43
  static get observedAttributes(): string[];
45
44
  constructor();
46
45
  connectedCallback(): void;
package/dist/index.esm.js CHANGED
@@ -1,6 +1,4 @@
1
- /*! @salesforce/experimental-mfe-lwc-shell v2.2.1-rc.1 (2026-04-09) */
2
- import { gql, unstable_graphql_imperative } from 'lightning/graphql';
3
-
1
+ /*! @salesforce/experimental-mfe-lwc-shell v2.2.1-rc.7 (2026-06-16) */
4
2
  /**
5
3
  * EmbeddingResizer - Handles dynamic iframe/container resizing
6
4
  * Uses ResizeObserver to monitor element size changes and notify the host
@@ -18,119 +16,6 @@ function getUUID() {
18
16
  return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36);
19
17
  }
20
18
 
21
- async function executeGraphQL(query, variables) {
22
- if (typeof query !== "string" || query.trim() === "") {
23
- throw new Error("Invalid GraphQL query: query must be a non-empty string.");
24
- }
25
- const documentNode = gql `
26
- ${query}
27
- `;
28
- const result = await unstable_graphql_imperative({ query: documentNode, variables });
29
- return { data: result?.data, errors: result?.errors };
30
- }
31
- class GraphQLBridgeHandler {
32
- #sendResponse;
33
- #pendingCount = 0;
34
- #queue = [];
35
- #maxConcurrent;
36
- #maxQueueSize;
37
- #generation = 0;
38
- constructor(sendResponse, maxConcurrent = 10, maxQueueSize = 50) {
39
- this.#sendResponse = sendResponse;
40
- this.#maxConcurrent = maxConcurrent;
41
- this.#maxQueueSize = maxQueueSize;
42
- }
43
- static isValidRequest(data) {
44
- const req = data;
45
- return (typeof req?.requestId === "string" &&
46
- typeof req?.query === "string" &&
47
- (req.variables == null || (typeof req.variables === "object" && !Array.isArray(req.variables))));
48
- }
49
- rejectInvalidRequest(data) {
50
- const req = data;
51
- if (typeof req?.requestId !== "string") {
52
- this.#sendResponse({
53
- requestId: req?.requestId,
54
- ok: false,
55
- error: { message: "Invalid GraphQL request data" },
56
- });
57
- return;
58
- }
59
- const { requestId, query, variables } = req;
60
- if (typeof query !== "string") {
61
- this.#sendResponse({
62
- requestId: requestId,
63
- ok: false,
64
- error: { message: "Invalid GraphQL query: query must be a string" },
65
- });
66
- return;
67
- }
68
- if (variables != null && (typeof variables !== "object" || Array.isArray(variables))) {
69
- this.#sendResponse({
70
- requestId: requestId,
71
- ok: false,
72
- error: { message: "Invalid GraphQL variables: must be a plain object" },
73
- });
74
- }
75
- }
76
- handleRequest({ requestId, query, variables }) {
77
- if (this.#pendingCount >= this.#maxConcurrent) {
78
- return this.#enqueueRequest(requestId, query, variables);
79
- }
80
- return this.#execute(requestId, query, variables);
81
- }
82
- reset() {
83
- this.#generation++;
84
- this.#queue = [];
85
- this.#pendingCount = 0;
86
- }
87
- #execute = async (requestId, query, variables) => {
88
- const gen = this.#generation;
89
- this.#pendingCount++;
90
- try {
91
- const result = await executeGraphQL(query, variables);
92
- if (gen === this.#generation) {
93
- this.#sendResponse({ requestId, ok: true, result });
94
- }
95
- }
96
- catch (err) {
97
- if (gen === this.#generation) {
98
- this.#sendResponse({
99
- requestId,
100
- ok: false,
101
- error: { message: err?.message || "GraphQL request failed" },
102
- });
103
- }
104
- }
105
- finally {
106
- if (gen === this.#generation) {
107
- this.#pendingCount--;
108
- this.#processRequestQueue();
109
- }
110
- }
111
- };
112
- #enqueueRequest = (requestId, query, variables) => {
113
- if (this.#queue.length >= this.#maxQueueSize) {
114
- this.#sendResponse({
115
- requestId,
116
- ok: false,
117
- error: { message: "Too many queued GraphQL requests" },
118
- });
119
- return Promise.resolve();
120
- }
121
- return new Promise((resolve) => {
122
- this.#queue.push({ requestId, query, variables, done: resolve });
123
- });
124
- };
125
- #processRequestQueue = () => {
126
- if (this.#queue.length === 0 || this.#pendingCount >= this.#maxConcurrent) {
127
- return;
128
- }
129
- const next = this.#queue.shift();
130
- this.#execute(next.requestId, next.query, next.variables).then(next.done);
131
- };
132
- }
133
-
134
19
  /**
135
20
  * InternalHostLwcShell
136
21
  *
@@ -153,11 +38,9 @@ class GraphQLBridgeHandler {
153
38
  *
154
39
  * See the README and the `productRegistration` demo for a full recipe.
155
40
  */
156
- const MAX_CONCURRENT_GRAPHQL_REQUESTS = 10;
157
- const MAX_GRAPHQL_QUEUE_SIZE = 50;
158
- const BASE_TOKENS = ["allow-scripts", "allow-pointer-lock"];
41
+ const BASE_TOKENS = ["allow-scripts", "allow-pointer-lock", "allow-same-origin"];
159
42
  const OPTIONAL_TOKENS = ["allow-downloads", "allow-forms", "allow-modals"];
160
- const BLOCKED_TOKENS = ["allow-same-origin", "allow-top-navigation", "allow-popups"];
43
+ const BLOCKED_TOKENS = ["allow-top-navigation", "allow-popups"];
161
44
  const STATES = {
162
45
  LOADING: "state-loading",
163
46
  LOADED: "state-loaded",
@@ -251,16 +134,12 @@ class InternalHostLwcShell extends HTMLElement {
251
134
  _title = "Embedded widget";
252
135
  _view = "compact";
253
136
  _debugEnabled = true;
254
- _graphqlHandler;
255
137
  static get observedAttributes() {
256
138
  return ["src", "srcdoc", "sandbox", "title", "view", "debug"];
257
139
  }
258
140
  constructor() {
259
141
  super();
260
142
  this._shadow = this.attachShadow({ mode: "closed" });
261
- this._graphqlHandler = new GraphQLBridgeHandler((data) => {
262
- this._postToIframe("bridge-graphql-response", data);
263
- }, MAX_CONCURRENT_GRAPHQL_REQUESTS, MAX_GRAPHQL_QUEUE_SIZE);
264
143
  }
265
144
  connectedCallback() {
266
145
  this._renderInitial();
@@ -269,7 +148,6 @@ class InternalHostLwcShell extends HTMLElement {
269
148
  }
270
149
  disconnectedCallback() {
271
150
  window.removeEventListener("message", this._handleMessage);
272
- this._graphqlHandler.reset();
273
151
  if (this._readinessTimeout) {
274
152
  clearTimeout(this._readinessTimeout);
275
153
  this._readinessTimeout = null;
@@ -427,13 +305,8 @@ class InternalHostLwcShell extends HTMLElement {
427
305
  const shadow = this._shadow;
428
306
  shadow.innerHTML = `
429
307
  <style>${STYLES}</style>
430
- <div class="container" data-state="${this._currentState}"
431
- tabindex="0" role="region" aria-label="${this._title}">
432
- <iframe
433
- class="${this._frameClass}"
434
- title="${this._title}"
435
- aria-label="Interactive widget content"
436
- ></iframe>
308
+ <div class="container" data-state="${this._currentState}">
309
+ <iframe class="${this._frameClass}" title="${this._title}"></iframe>
437
310
  </div>
438
311
  `;
439
312
  this._container = shadow.querySelector(".container");
@@ -453,9 +326,6 @@ class InternalHostLwcShell extends HTMLElement {
453
326
  if (this._iframe) {
454
327
  this._iframe.setAttribute("title", this._title);
455
328
  }
456
- if (this._container) {
457
- this._container.setAttribute("aria-label", this._title);
458
- }
459
329
  this._log("title", this._title);
460
330
  }
461
331
  _updateViewDOM() {
@@ -534,14 +404,6 @@ class InternalHostLwcShell extends HTMLElement {
534
404
  this.dispatchEvent(new CustomEvent(eventType, { detail, bubbles: true, cancelable: true, composed: true }));
535
405
  }
536
406
  }
537
- else if (type === "bridge-graphql-request") {
538
- if (GraphQLBridgeHandler.isValidRequest(data)) {
539
- this._graphqlHandler.handleRequest(data);
540
- }
541
- else {
542
- this._graphqlHandler.rejectInvalidRequest(data);
543
- }
544
- }
545
407
  else if (type === "bridge-ready") {
546
408
  this._handleBridgeReady();
547
409
  }
@@ -647,7 +509,6 @@ class InternalHostLwcShell extends HTMLElement {
647
509
  return;
648
510
  // reset tracking
649
511
  this._bridgeReady = false;
650
- this._graphqlHandler.reset();
651
512
  this._currentState = STATES.LOADING;
652
513
  this._updateContainerState();
653
514
  // clear existing
@@ -1,5 +1,5 @@
1
- /*! @salesforce/experimental-mfe-lwc-shell v2.2.1-rc.1 (2026-04-09) */
2
- var LwcShell = (function (exports, graphql) {
1
+ /*! @salesforce/experimental-mfe-lwc-shell v2.2.1-rc.7 (2026-06-16) */
2
+ var LwcShell = (function (exports) {
3
3
  'use strict';
4
4
 
5
5
  /**
@@ -19,119 +19,6 @@ var LwcShell = (function (exports, graphql) {
19
19
  return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36);
20
20
  }
21
21
 
22
- async function executeGraphQL(query, variables) {
23
- if (typeof query !== "string" || query.trim() === "") {
24
- throw new Error("Invalid GraphQL query: query must be a non-empty string.");
25
- }
26
- const documentNode = graphql.gql `
27
- ${query}
28
- `;
29
- const result = await graphql.unstable_graphql_imperative({ query: documentNode, variables });
30
- return { data: result?.data, errors: result?.errors };
31
- }
32
- class GraphQLBridgeHandler {
33
- #sendResponse;
34
- #pendingCount = 0;
35
- #queue = [];
36
- #maxConcurrent;
37
- #maxQueueSize;
38
- #generation = 0;
39
- constructor(sendResponse, maxConcurrent = 10, maxQueueSize = 50) {
40
- this.#sendResponse = sendResponse;
41
- this.#maxConcurrent = maxConcurrent;
42
- this.#maxQueueSize = maxQueueSize;
43
- }
44
- static isValidRequest(data) {
45
- const req = data;
46
- return (typeof req?.requestId === "string" &&
47
- typeof req?.query === "string" &&
48
- (req.variables == null || (typeof req.variables === "object" && !Array.isArray(req.variables))));
49
- }
50
- rejectInvalidRequest(data) {
51
- const req = data;
52
- if (typeof req?.requestId !== "string") {
53
- this.#sendResponse({
54
- requestId: req?.requestId,
55
- ok: false,
56
- error: { message: "Invalid GraphQL request data" },
57
- });
58
- return;
59
- }
60
- const { requestId, query, variables } = req;
61
- if (typeof query !== "string") {
62
- this.#sendResponse({
63
- requestId: requestId,
64
- ok: false,
65
- error: { message: "Invalid GraphQL query: query must be a string" },
66
- });
67
- return;
68
- }
69
- if (variables != null && (typeof variables !== "object" || Array.isArray(variables))) {
70
- this.#sendResponse({
71
- requestId: requestId,
72
- ok: false,
73
- error: { message: "Invalid GraphQL variables: must be a plain object" },
74
- });
75
- }
76
- }
77
- handleRequest({ requestId, query, variables }) {
78
- if (this.#pendingCount >= this.#maxConcurrent) {
79
- return this.#enqueueRequest(requestId, query, variables);
80
- }
81
- return this.#execute(requestId, query, variables);
82
- }
83
- reset() {
84
- this.#generation++;
85
- this.#queue = [];
86
- this.#pendingCount = 0;
87
- }
88
- #execute = async (requestId, query, variables) => {
89
- const gen = this.#generation;
90
- this.#pendingCount++;
91
- try {
92
- const result = await executeGraphQL(query, variables);
93
- if (gen === this.#generation) {
94
- this.#sendResponse({ requestId, ok: true, result });
95
- }
96
- }
97
- catch (err) {
98
- if (gen === this.#generation) {
99
- this.#sendResponse({
100
- requestId,
101
- ok: false,
102
- error: { message: err?.message || "GraphQL request failed" },
103
- });
104
- }
105
- }
106
- finally {
107
- if (gen === this.#generation) {
108
- this.#pendingCount--;
109
- this.#processRequestQueue();
110
- }
111
- }
112
- };
113
- #enqueueRequest = (requestId, query, variables) => {
114
- if (this.#queue.length >= this.#maxQueueSize) {
115
- this.#sendResponse({
116
- requestId,
117
- ok: false,
118
- error: { message: "Too many queued GraphQL requests" },
119
- });
120
- return Promise.resolve();
121
- }
122
- return new Promise((resolve) => {
123
- this.#queue.push({ requestId, query, variables, done: resolve });
124
- });
125
- };
126
- #processRequestQueue = () => {
127
- if (this.#queue.length === 0 || this.#pendingCount >= this.#maxConcurrent) {
128
- return;
129
- }
130
- const next = this.#queue.shift();
131
- this.#execute(next.requestId, next.query, next.variables).then(next.done);
132
- };
133
- }
134
-
135
22
  /**
136
23
  * InternalHostLwcShell
137
24
  *
@@ -154,11 +41,9 @@ var LwcShell = (function (exports, graphql) {
154
41
  *
155
42
  * See the README and the `productRegistration` demo for a full recipe.
156
43
  */
157
- const MAX_CONCURRENT_GRAPHQL_REQUESTS = 10;
158
- const MAX_GRAPHQL_QUEUE_SIZE = 50;
159
- const BASE_TOKENS = ["allow-scripts", "allow-pointer-lock"];
44
+ const BASE_TOKENS = ["allow-scripts", "allow-pointer-lock", "allow-same-origin"];
160
45
  const OPTIONAL_TOKENS = ["allow-downloads", "allow-forms", "allow-modals"];
161
- const BLOCKED_TOKENS = ["allow-same-origin", "allow-top-navigation", "allow-popups"];
46
+ const BLOCKED_TOKENS = ["allow-top-navigation", "allow-popups"];
162
47
  const STATES = {
163
48
  LOADING: "state-loading",
164
49
  LOADED: "state-loaded",
@@ -252,16 +137,12 @@ var LwcShell = (function (exports, graphql) {
252
137
  _title = "Embedded widget";
253
138
  _view = "compact";
254
139
  _debugEnabled = true;
255
- _graphqlHandler;
256
140
  static get observedAttributes() {
257
141
  return ["src", "srcdoc", "sandbox", "title", "view", "debug"];
258
142
  }
259
143
  constructor() {
260
144
  super();
261
145
  this._shadow = this.attachShadow({ mode: "closed" });
262
- this._graphqlHandler = new GraphQLBridgeHandler((data) => {
263
- this._postToIframe("bridge-graphql-response", data);
264
- }, MAX_CONCURRENT_GRAPHQL_REQUESTS, MAX_GRAPHQL_QUEUE_SIZE);
265
146
  }
266
147
  connectedCallback() {
267
148
  this._renderInitial();
@@ -270,7 +151,6 @@ var LwcShell = (function (exports, graphql) {
270
151
  }
271
152
  disconnectedCallback() {
272
153
  window.removeEventListener("message", this._handleMessage);
273
- this._graphqlHandler.reset();
274
154
  if (this._readinessTimeout) {
275
155
  clearTimeout(this._readinessTimeout);
276
156
  this._readinessTimeout = null;
@@ -428,13 +308,8 @@ var LwcShell = (function (exports, graphql) {
428
308
  const shadow = this._shadow;
429
309
  shadow.innerHTML = `
430
310
  <style>${STYLES}</style>
431
- <div class="container" data-state="${this._currentState}"
432
- tabindex="0" role="region" aria-label="${this._title}">
433
- <iframe
434
- class="${this._frameClass}"
435
- title="${this._title}"
436
- aria-label="Interactive widget content"
437
- ></iframe>
311
+ <div class="container" data-state="${this._currentState}">
312
+ <iframe class="${this._frameClass}" title="${this._title}"></iframe>
438
313
  </div>
439
314
  `;
440
315
  this._container = shadow.querySelector(".container");
@@ -454,9 +329,6 @@ var LwcShell = (function (exports, graphql) {
454
329
  if (this._iframe) {
455
330
  this._iframe.setAttribute("title", this._title);
456
331
  }
457
- if (this._container) {
458
- this._container.setAttribute("aria-label", this._title);
459
- }
460
332
  this._log("title", this._title);
461
333
  }
462
334
  _updateViewDOM() {
@@ -535,14 +407,6 @@ var LwcShell = (function (exports, graphql) {
535
407
  this.dispatchEvent(new CustomEvent(eventType, { detail, bubbles: true, cancelable: true, composed: true }));
536
408
  }
537
409
  }
538
- else if (type === "bridge-graphql-request") {
539
- if (GraphQLBridgeHandler.isValidRequest(data)) {
540
- this._graphqlHandler.handleRequest(data);
541
- }
542
- else {
543
- this._graphqlHandler.rejectInvalidRequest(data);
544
- }
545
- }
546
410
  else if (type === "bridge-ready") {
547
411
  this._handleBridgeReady();
548
412
  }
@@ -648,7 +512,6 @@ var LwcShell = (function (exports, graphql) {
648
512
  return;
649
513
  // reset tracking
650
514
  this._bridgeReady = false;
651
- this._graphqlHandler.reset();
652
515
  this._currentState = STATES.LOADING;
653
516
  this._updateContainerState();
654
517
  // clear existing
@@ -768,5 +631,5 @@ var LwcShell = (function (exports, graphql) {
768
631
 
769
632
  return exports;
770
633
 
771
- })({}, typeof lightningGraphql !== 'undefined' ? lightningGraphql : {});
634
+ })({});
772
635
  //# sourceMappingURL=index.iife.js.map
@@ -1,5 +1,3 @@
1
- /*! @salesforce/experimental-mfe-lwc-shell v2.2.1-rc.1 (2026-04-09) */
2
- var LwcShell=function(e,t){"use strict";class s{#e;#t=0;#s=[];#i;#n;#a=0;constructor(e,t=10,s=50){this.#e=e,this.#i=t,this.#n=s}static isValidRequest(e){const t=e;return"string"==typeof t?.requestId&&"string"==typeof t?.query&&(null==t.variables||"object"==typeof t.variables&&!Array.isArray(t.variables))}rejectInvalidRequest(e){const t=e;if("string"!=typeof t?.requestId)return void this.#e({requestId:t?.requestId,ok:!1,error:{message:"Invalid GraphQL request data"}});const{requestId:s,query:i,variables:n}=t;"string"==typeof i?null==n||"object"==typeof n&&!Array.isArray(n)||this.#e({requestId:s,ok:!1,error:{message:"Invalid GraphQL variables: must be a plain object"}}):this.#e({requestId:s,ok:!1,error:{message:"Invalid GraphQL query: query must be a string"}})}handleRequest({requestId:e,query:t,variables:s}){return this.#t>=this.#i?this.#r(e,t,s):this.#l(e,t,s)}reset(){this.#a++,this.#s=[],this.#t=0}#l=async(e,s,i)=>{const n=this.#a;this.#t++;try{const a=await async function(e,s){if("string"!=typeof e||""===e.trim())throw new Error("Invalid GraphQL query: query must be a non-empty string.");const i=t.gql`
3
- ${e}
4
- `,n=await t.unstable_graphql_imperative({query:i,variables:s});return{data:n?.data,errors:n?.errors}}(s,i);n===this.#a&&this.#e({requestId:e,ok:!0,result:a})}catch(t){n===this.#a&&this.#e({requestId:e,ok:!1,error:{message:t?.message||"GraphQL request failed"}})}finally{n===this.#a&&(this.#t--,this.#o())}};#r=(e,t,s)=>this.#s.length>=this.#n?(this.#e({requestId:e,ok:!1,error:{message:"Too many queued GraphQL requests"}}),Promise.resolve()):new Promise(i=>{this.#s.push({requestId:e,query:t,variables:s,done:i})});#o=()=>{if(0===this.#s.length||this.#t>=this.#i)return;const e=this.#s.shift();this.#l(e.requestId,e.query,e.variables).then(e.done)}}const i=["allow-scripts","allow-pointer-lock"],n=["allow-downloads","allow-forms","allow-modals"],a=["allow-same-origin","allow-top-navigation","allow-popups"],r="state-loading",l="state-loaded";class o extends HTMLElement{_shadow;_iframe=null;_container=null;_currentState=r;_readinessTimeout=null;_isFullscreen=!1;_preFullscreenHeight="";_lastThemeData={};_lastPayloadData={};_bridgeReady=!1;_shellInstanceId=function(){return Math.floor(Math.random()*Number.MAX_SAFE_INTEGER).toString(36)}();_hasSentTheme=!1;_hasSentData=!1;_src=null;_srcdoc=null;_sandbox=null;_title="Embedded widget";_view="compact";_debugEnabled=!0;_graphqlHandler;static get observedAttributes(){return["src","srcdoc","sandbox","title","view","debug"]}constructor(){super(),this._shadow=this.attachShadow({mode:"closed"}),this._graphqlHandler=new s(e=>{this._postToIframe("bridge-graphql-response",e)},10,50)}connectedCallback(){this._renderInitial(),this._setupMessageListener(),this._log("connectedCallback")}disconnectedCallback(){window.removeEventListener("message",this._handleMessage),this._graphqlHandler.reset(),this._readinessTimeout&&(clearTimeout(this._readinessTimeout),this._readinessTimeout=null),this._log("disconnectedCallback")}attributeChangedCallback(e,t,s){if(t!==s)switch(e){case"src":this.src=s;break;case"srcdoc":this.srcdoc=s;break;case"sandbox":this.sandbox=s;break;case"title":this.title=s;break;case"view":this.view=s;break;case"debug":this.debug=null!==s}}get src(){return this._src}set src(e){const t=e||null;this._src!==t&&(this._src=t,null!==t?this.setAttribute("src",t):this.removeAttribute("src"),this._updateIframeSrc())}get srcdoc(){return this._srcdoc}set srcdoc(e){const t=e||null;this._srcdoc!==t&&(this._srcdoc=t,null!==t?this.setAttribute("srcdoc",t):this.removeAttribute("srcdoc"),this._updateIframeSrc())}get sandbox(){return this._sandbox}set sandbox(e){const t=e||null;this._sandbox!==t&&(this._sandbox=t,null!==t?this.setAttribute("sandbox",t):this.removeAttribute("sandbox"),this._applySandbox())}get title(){return this._title}set title(e){const t=e||"Embedded widget";this._title!==t&&(this._title=t,this.setAttribute("title",t),this._updateTitle())}get view(){return this._view}set view(e){const t="full"===e?"full":"compact";this._view!==t&&(this._view=t,this.setAttribute("view",t),this._updateViewDOM())}get debug(){return this._debugEnabled}set debug(e){const t=!!e;this._debugEnabled!==t&&(this._debugEnabled=t,t?this.setAttribute("debug",""):this.removeAttribute("debug"))}updateData(e){if(!e||"object"!=typeof e)return;Object.entries(e).forEach(([e,t])=>{const s=`data-${String(e).replace(/[A-Z]/g,"-$&").toLowerCase()}`;this.setAttribute(s,String(t))});const t=this._collectDataAttributes();this._lastPayloadData=t,this._bridgeReady?(this._postToIframe("data",t),this._hasSentData=!0,this._log("send data",t)):this._log("queue data until bridge ready",t)}refreshTheme(){this._sendInitialTheme()}get _isFullView(){return"full"===this._view}get _frameClass(){return this._isFullView?"frame frameFull":"frame"}_log(...e){this._debugEnabled&&console.log("[InternalHostLwcShell]",JSON.stringify(e,null,2))}_renderInitial(){const e=this._shadow;e.innerHTML=`\n <style>\n:host {\n display: block;\n position: relative;\n height: 100%;\n overflow: auto;\n box-sizing: border-box;\n}\n\n.container {\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.frame {\n visibility: hidden;\n display: block;\n width: 100%;\n height: 100%;\n border: none;\n}\n\n.container[data-state='state-loaded'] .frame {\n visibility: visible;\n}\n\n/* Fullscreen overlay */\n.overlayBackdrop {\n position: fixed;\n inset: 0;\n width: 100vw;\n height: 100vh;\n z-index: 9998;\n background: rgba(0, 0, 0, 0.8);\n backdrop-filter: blur(4px);\n}\n\n.overlayClose {\n position: fixed;\n top: 24px;\n right: 24px;\n z-index: 9999;\n width: 32px;\n height: 32px;\n background: rgba(0, 0, 0, 0.7);\n color: #fff;\n border-radius: 50%;\n border: none;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n font-weight: bold;\n cursor: pointer;\n}\n\n.frameFull {\n position: fixed;\n inset: 0;\n width: 90vw;\n height: 90vh !important;\n margin: 5vh 5vw;\n z-index: 10000;\n background: #fff;\n border-radius: 8px;\n box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);\n}\n</style>\n <div class="container" data-state="${this._currentState}"\n tabindex="0" role="region" aria-label="${this._title}">\n <iframe\n class="${this._frameClass}"\n title="${this._title}"\n aria-label="Interactive widget content"\n ></iframe>\n </div>\n `,this._container=e.querySelector(".container"),this._iframe=e.querySelector("iframe"),this._container.addEventListener("click",()=>this._handleContainerClick()),this._applySandbox(),this._updateIframeSrc(),this._sendInitialTheme(),this._log("renderInitial: iframe ready")}_updateContainerState(){this._container&&this._container.setAttribute("data-state",this._currentState)}_updateTitle(){this._iframe&&this._iframe.setAttribute("title",this._title),this._container&&this._container.setAttribute("aria-label",this._title),this._log("title",this._title)}_updateViewDOM(){if(this._iframe&&(this._iframe.className=this._frameClass),!this._container)return;const e=this._container.querySelector(".overlayBackdrop"),t=this._container.querySelector(".overlayClose");if(e&&e.remove(),t&&t.remove(),this._isFullView){const e=document.createElement("div");e.className="overlayBackdrop",e.setAttribute("aria-hidden","true");const t=document.createElement("button");t.className="overlayClose",t.type="button",t.setAttribute("aria-label","Close fullscreen"),t.textContent="✕",t.addEventListener("click",this._exitFullscreen),this._container.appendChild(e),this._container.appendChild(t)}}_setState(e){this._currentState=e,this._updateContainerState(),e===l&&this._sendInitialData()}_setupMessageListener(){window.addEventListener("message",this._handleMessage)}_handleMessage=e=>{const t=this._iframe?.contentWindow;if(e.source!==t)return;const i=e.data||{},{type:n,data:a,id:r}=i;if(r?r===this._shellInstanceId:"bridge-ready"===n)if(this._log("receive",n,a),"bridge-event"===n){const{eventType:e,detail:t}=a||{};if(this._log("bridge-event",e,t),"resize"===e)this._handleResize(t);else{if("widget-ready"!==e)throw new RangeError(`Invalid bridge event ${e}`);this._handleWidgetReady()}}else if("custom-event"===n){const{eventType:e,detail:t}=a||{};if(this._log("custom-event",e,t),"fullscreen-request"===e){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,cancelable:!0}))&&this._handleFullscreenRequest()}else this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,cancelable:!0,composed:!0}))}else"bridge-graphql-request"===n?s.isValidRequest(a)?this._graphqlHandler.handleRequest(a):this._graphqlHandler.rejectInvalidRequest(a):"bridge-ready"===n?this._handleBridgeReady():"bridge-error"===n&&this._handleBridgeError(a)};_handleResize({height:e}){const t=new CustomEvent("resize",{detail:{height:e},cancelable:!0});this.dispatchEvent(t),t.defaultPrevented||this._isFullscreen||"number"!=typeof e||!Number.isFinite(e)||(this._iframe&&(this._iframe.style.height=e+"px"),this._log("applied resize",e))}_handleWidgetReady(){this._readinessTimeout&&(clearTimeout(this._readinessTimeout),this._readinessTimeout=null),this.dispatchEvent(new CustomEvent("widget-ready",{bubbles:!0})),this._log("widget-ready")}_handleFullscreenRequest(){this._isFullscreen||this._enterFullscreen()}_enterFullscreen(){this._isFullscreen=!0,this._preFullscreenHeight=this._iframe?.style.height??"",this._view="full",this._updateViewDOM(),this._iframe&&(this._iframe.style.height="100%"),this.updateData({view:"full"}),this.dispatchEvent(new CustomEvent("fullscreen-entered",{detail:{element:this},bubbles:!0})),this._log("enter fullscreen")}_exitFullscreen=()=>{this._isFullscreen=!1,this._view="compact",this._updateViewDOM(),this._iframe&&(this._iframe.style.height=this._preFullscreenHeight),this.updateData({view:"compact"}),this.dispatchEvent(new CustomEvent("fullscreen-exited",{detail:{element:this},bubbles:!0})),this._log("exit fullscreen")};_handleBridgeReady(){this._bridgeReady=!0,this._postToIframe("shell-ready"),this._setState(l)}_handleBridgeError(e){this.dispatchEvent(new CustomEvent("widget-bridge-error",{detail:e})),this._log("bridge-error",e)}_handleContainerClick(){}_applySandbox(){const e=this._iframe;if(!e)return;const t=this._computeSandboxTokens();e.setAttribute("sandbox",t),this._log("sandbox",t)}_computeSandboxTokens(){const e=[...i];if(this._sandbox){String(this._sandbox).split(/\s+/).filter(Boolean).forEach(t=>{n.includes(t)&&!e.includes(t)&&e.push(t),a.includes(t)&&this.dispatchEvent(new CustomEvent("security-violation",{detail:{type:"blocked-sandbox-token",token:t,element:this}}))})}return e.join(" ")}_updateIframeSrc(){const e=this._iframe;if(e){if(this._bridgeReady=!1,this._graphqlHandler.reset(),this._currentState=r,this._updateContainerState(),e.removeAttribute("src"),e.removeAttribute("srcdoc"),this._src)e.setAttribute("src",this._src);else if(this._srcdoc)try{e.setAttribute("srcdoc",this._srcdoc)}catch{e.setAttribute("src","about:blank")}e.onload=this._handleIframeLoad,e.onerror=this._handleIframeError,this._log("updateIframeSrc",this._src?"src":this._srcdoc?"srcdoc":"blank")}}_handleIframeLoad=()=>{this.dispatchEvent(new CustomEvent("iframe-loaded",{detail:{element:this},bubbles:!0})),this._log("iframe-loaded"),this._readinessTimeout=setTimeout(()=>{this._currentState!==l&&(this.dispatchEvent(new CustomEvent("widget-readiness-warning",{detail:{element:this,message:"Widget may not be using Bridge for readiness signaling"},bubbles:!0})),this._log("widget-readiness-warning"))},3e3)};_handleIframeError=e=>{this.dispatchEvent(new CustomEvent("widget-error",{detail:{error:e,element:this}}))};_postToIframe(e,t){const s=this._iframe?.contentWindow;if(s)try{const i=`salesforce-${e}`;s.postMessage({type:i,data:t,id:this._shellInstanceId},"*"),this._log("postMessage",i,t)}catch(e){this._log("postMessage error",e instanceof Error?e.message:String(e))}}_collectDataAttributes(){const e={};for(let t=0;t<this.attributes.length;t++){const s=this.attributes[t];if(s.name.startsWith("data-")){e[s.name.replace(/^data-/,"").replace(/-([a-z])/g,(e,t)=>t.toUpperCase())]=s.value}}return e}_sendInitialTheme(){const e=getComputedStyle(this),t={};for(let s=0;s<e.length;s++){const i=e[s];if(i.startsWith("--")){const s=e.getPropertyValue(i).trim();s&&(t[i]=s)}}this._lastThemeData=t,this._bridgeReady?(this._postToIframe("theme",t),this._hasSentTheme=!0,this._log("send theme",t)):this._log("queue theme until bridge ready",t)}_sendInitialData(){this._lastThemeData&&Object.keys(this._lastThemeData).length&&(this._postToIframe("theme",this._lastThemeData),this._hasSentTheme=!0,this._log("send theme (initial)",this._lastThemeData));const e=this._collectDataAttributes();this._lastPayloadData={...e},this._postToIframe("data",this._lastPayloadData),this._hasSentData=!0,this._log("send data (initial)",this._lastPayloadData)}}return window.customElements.get("lwc-shell")||window.customElements.define("lwc-shell",o),e.InternalHostLwcShell=o,e.default=o,Object.defineProperty(e,"__esModule",{value:!0}),e}({},"undefined"!=typeof lightningGraphql?lightningGraphql:{});
1
+ /*! @salesforce/experimental-mfe-lwc-shell v2.2.1-rc.7 (2026-06-16) */
2
+ var LwcShell=function(e){"use strict";const t=["allow-scripts","allow-pointer-lock","allow-same-origin"],s=["allow-downloads","allow-forms","allow-modals"],i=["allow-top-navigation","allow-popups"],n="state-loading",a="state-loaded";class r extends HTMLElement{_shadow;_iframe=null;_container=null;_currentState=n;_readinessTimeout=null;_isFullscreen=!1;_preFullscreenHeight="";_lastThemeData={};_lastPayloadData={};_bridgeReady=!1;_shellInstanceId=function(){return Math.floor(Math.random()*Number.MAX_SAFE_INTEGER).toString(36)}();_hasSentTheme=!1;_hasSentData=!1;_src=null;_srcdoc=null;_sandbox=null;_title="Embedded widget";_view="compact";_debugEnabled=!0;static get observedAttributes(){return["src","srcdoc","sandbox","title","view","debug"]}constructor(){super(),this._shadow=this.attachShadow({mode:"closed"})}connectedCallback(){this._renderInitial(),this._setupMessageListener(),this._log("connectedCallback")}disconnectedCallback(){window.removeEventListener("message",this._handleMessage),this._readinessTimeout&&(clearTimeout(this._readinessTimeout),this._readinessTimeout=null),this._log("disconnectedCallback")}attributeChangedCallback(e,t,s){if(t!==s)switch(e){case"src":this.src=s;break;case"srcdoc":this.srcdoc=s;break;case"sandbox":this.sandbox=s;break;case"title":this.title=s;break;case"view":this.view=s;break;case"debug":this.debug=null!==s}}get src(){return this._src}set src(e){const t=e||null;this._src!==t&&(this._src=t,null!==t?this.setAttribute("src",t):this.removeAttribute("src"),this._updateIframeSrc())}get srcdoc(){return this._srcdoc}set srcdoc(e){const t=e||null;this._srcdoc!==t&&(this._srcdoc=t,null!==t?this.setAttribute("srcdoc",t):this.removeAttribute("srcdoc"),this._updateIframeSrc())}get sandbox(){return this._sandbox}set sandbox(e){const t=e||null;this._sandbox!==t&&(this._sandbox=t,null!==t?this.setAttribute("sandbox",t):this.removeAttribute("sandbox"),this._applySandbox())}get title(){return this._title}set title(e){const t=e||"Embedded widget";this._title!==t&&(this._title=t,this.setAttribute("title",t),this._updateTitle())}get view(){return this._view}set view(e){const t="full"===e?"full":"compact";this._view!==t&&(this._view=t,this.setAttribute("view",t),this._updateViewDOM())}get debug(){return this._debugEnabled}set debug(e){const t=!!e;this._debugEnabled!==t&&(this._debugEnabled=t,t?this.setAttribute("debug",""):this.removeAttribute("debug"))}updateData(e){if(!e||"object"!=typeof e)return;Object.entries(e).forEach(([e,t])=>{const s=`data-${String(e).replace(/[A-Z]/g,"-$&").toLowerCase()}`;this.setAttribute(s,String(t))});const t=this._collectDataAttributes();this._lastPayloadData=t,this._bridgeReady?(this._postToIframe("data",t),this._hasSentData=!0,this._log("send data",t)):this._log("queue data until bridge ready",t)}refreshTheme(){this._sendInitialTheme()}get _isFullView(){return"full"===this._view}get _frameClass(){return this._isFullView?"frame frameFull":"frame"}_log(...e){this._debugEnabled&&console.log("[InternalHostLwcShell]",JSON.stringify(e,null,2))}_renderInitial(){const e=this._shadow;e.innerHTML=`\n <style>\n:host {\n display: block;\n position: relative;\n height: 100%;\n overflow: auto;\n box-sizing: border-box;\n}\n\n.container {\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.frame {\n visibility: hidden;\n display: block;\n width: 100%;\n height: 100%;\n border: none;\n}\n\n.container[data-state='state-loaded'] .frame {\n visibility: visible;\n}\n\n/* Fullscreen overlay */\n.overlayBackdrop {\n position: fixed;\n inset: 0;\n width: 100vw;\n height: 100vh;\n z-index: 9998;\n background: rgba(0, 0, 0, 0.8);\n backdrop-filter: blur(4px);\n}\n\n.overlayClose {\n position: fixed;\n top: 24px;\n right: 24px;\n z-index: 9999;\n width: 32px;\n height: 32px;\n background: rgba(0, 0, 0, 0.7);\n color: #fff;\n border-radius: 50%;\n border: none;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n font-weight: bold;\n cursor: pointer;\n}\n\n.frameFull {\n position: fixed;\n inset: 0;\n width: 90vw;\n height: 90vh !important;\n margin: 5vh 5vw;\n z-index: 10000;\n background: #fff;\n border-radius: 8px;\n box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);\n}\n</style>\n <div class="container" data-state="${this._currentState}">\n <iframe class="${this._frameClass}" title="${this._title}"></iframe>\n </div>\n `,this._container=e.querySelector(".container"),this._iframe=e.querySelector("iframe"),this._container.addEventListener("click",()=>this._handleContainerClick()),this._applySandbox(),this._updateIframeSrc(),this._sendInitialTheme(),this._log("renderInitial: iframe ready")}_updateContainerState(){this._container&&this._container.setAttribute("data-state",this._currentState)}_updateTitle(){this._iframe&&this._iframe.setAttribute("title",this._title),this._log("title",this._title)}_updateViewDOM(){if(this._iframe&&(this._iframe.className=this._frameClass),!this._container)return;const e=this._container.querySelector(".overlayBackdrop"),t=this._container.querySelector(".overlayClose");if(e&&e.remove(),t&&t.remove(),this._isFullView){const e=document.createElement("div");e.className="overlayBackdrop",e.setAttribute("aria-hidden","true");const t=document.createElement("button");t.className="overlayClose",t.type="button",t.setAttribute("aria-label","Close fullscreen"),t.textContent="✕",t.addEventListener("click",this._exitFullscreen),this._container.appendChild(e),this._container.appendChild(t)}}_setState(e){this._currentState=e,this._updateContainerState(),e===a&&this._sendInitialData()}_setupMessageListener(){window.addEventListener("message",this._handleMessage)}_handleMessage=e=>{const t=this._iframe?.contentWindow;if(e.source!==t)return;const s=e.data||{},{type:i,data:n,id:a}=s;if(a?a===this._shellInstanceId:"bridge-ready"===i)if(this._log("receive",i,n),"bridge-event"===i){const{eventType:e,detail:t}=n||{};if(this._log("bridge-event",e,t),"resize"===e)this._handleResize(t);else{if("widget-ready"!==e)throw new RangeError(`Invalid bridge event ${e}`);this._handleWidgetReady()}}else if("custom-event"===i){const{eventType:e,detail:t}=n||{};if(this._log("custom-event",e,t),"fullscreen-request"===e){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,cancelable:!0}))&&this._handleFullscreenRequest()}else this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,cancelable:!0,composed:!0}))}else"bridge-ready"===i?this._handleBridgeReady():"bridge-error"===i&&this._handleBridgeError(n)};_handleResize({height:e}){const t=new CustomEvent("resize",{detail:{height:e},cancelable:!0});this.dispatchEvent(t),t.defaultPrevented||this._isFullscreen||"number"!=typeof e||!Number.isFinite(e)||(this._iframe&&(this._iframe.style.height=e+"px"),this._log("applied resize",e))}_handleWidgetReady(){this._readinessTimeout&&(clearTimeout(this._readinessTimeout),this._readinessTimeout=null),this.dispatchEvent(new CustomEvent("widget-ready",{bubbles:!0})),this._log("widget-ready")}_handleFullscreenRequest(){this._isFullscreen||this._enterFullscreen()}_enterFullscreen(){this._isFullscreen=!0,this._preFullscreenHeight=this._iframe?.style.height??"",this._view="full",this._updateViewDOM(),this._iframe&&(this._iframe.style.height="100%"),this.updateData({view:"full"}),this.dispatchEvent(new CustomEvent("fullscreen-entered",{detail:{element:this},bubbles:!0})),this._log("enter fullscreen")}_exitFullscreen=()=>{this._isFullscreen=!1,this._view="compact",this._updateViewDOM(),this._iframe&&(this._iframe.style.height=this._preFullscreenHeight),this.updateData({view:"compact"}),this.dispatchEvent(new CustomEvent("fullscreen-exited",{detail:{element:this},bubbles:!0})),this._log("exit fullscreen")};_handleBridgeReady(){this._bridgeReady=!0,this._postToIframe("shell-ready"),this._setState(a)}_handleBridgeError(e){this.dispatchEvent(new CustomEvent("widget-bridge-error",{detail:e})),this._log("bridge-error",e)}_handleContainerClick(){}_applySandbox(){const e=this._iframe;if(!e)return;const t=this._computeSandboxTokens();e.setAttribute("sandbox",t),this._log("sandbox",t)}_computeSandboxTokens(){const e=[...t];if(this._sandbox){String(this._sandbox).split(/\s+/).filter(Boolean).forEach(t=>{s.includes(t)&&!e.includes(t)&&e.push(t),i.includes(t)&&this.dispatchEvent(new CustomEvent("security-violation",{detail:{type:"blocked-sandbox-token",token:t,element:this}}))})}return e.join(" ")}_updateIframeSrc(){const e=this._iframe;if(e){if(this._bridgeReady=!1,this._currentState=n,this._updateContainerState(),e.removeAttribute("src"),e.removeAttribute("srcdoc"),this._src)e.setAttribute("src",this._src);else if(this._srcdoc)try{e.setAttribute("srcdoc",this._srcdoc)}catch{e.setAttribute("src","about:blank")}e.onload=this._handleIframeLoad,e.onerror=this._handleIframeError,this._log("updateIframeSrc",this._src?"src":this._srcdoc?"srcdoc":"blank")}}_handleIframeLoad=()=>{this.dispatchEvent(new CustomEvent("iframe-loaded",{detail:{element:this},bubbles:!0})),this._log("iframe-loaded"),this._readinessTimeout=setTimeout(()=>{this._currentState!==a&&(this.dispatchEvent(new CustomEvent("widget-readiness-warning",{detail:{element:this,message:"Widget may not be using Bridge for readiness signaling"},bubbles:!0})),this._log("widget-readiness-warning"))},3e3)};_handleIframeError=e=>{this.dispatchEvent(new CustomEvent("widget-error",{detail:{error:e,element:this}}))};_postToIframe(e,t){const s=this._iframe?.contentWindow;if(s)try{const i=`salesforce-${e}`;s.postMessage({type:i,data:t,id:this._shellInstanceId},"*"),this._log("postMessage",i,t)}catch(e){this._log("postMessage error",e instanceof Error?e.message:String(e))}}_collectDataAttributes(){const e={};for(let t=0;t<this.attributes.length;t++){const s=this.attributes[t];if(s.name.startsWith("data-")){e[s.name.replace(/^data-/,"").replace(/-([a-z])/g,(e,t)=>t.toUpperCase())]=s.value}}return e}_sendInitialTheme(){const e=getComputedStyle(this),t={};for(let s=0;s<e.length;s++){const i=e[s];if(i.startsWith("--")){const s=e.getPropertyValue(i).trim();s&&(t[i]=s)}}this._lastThemeData=t,this._bridgeReady?(this._postToIframe("theme",t),this._hasSentTheme=!0,this._log("send theme",t)):this._log("queue theme until bridge ready",t)}_sendInitialData(){this._lastThemeData&&Object.keys(this._lastThemeData).length&&(this._postToIframe("theme",this._lastThemeData),this._hasSentTheme=!0,this._log("send theme (initial)",this._lastThemeData));const e=this._collectDataAttributes();this._lastPayloadData={...e},this._postToIframe("data",this._lastPayloadData),this._hasSentData=!0,this._log("send data (initial)",this._lastPayloadData)}}return window.customElements.get("lwc-shell")||window.customElements.define("lwc-shell",r),e.InternalHostLwcShell=r,e.default=r,Object.defineProperty(e,"__esModule",{value:!0}),e}({});
5
3
  //# sourceMappingURL=index.iife.prod.js.map
package/index.d.ts CHANGED
@@ -40,7 +40,6 @@ declare class InternalHostLwcShell extends HTMLElement {
40
40
  private _title;
41
41
  private _view;
42
42
  private _debugEnabled;
43
- private _graphqlHandler;
44
43
  static get observedAttributes(): string[];
45
44
  constructor();
46
45
  connectedCallback(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/experimental-mfe-lwc-shell",
3
- "version": "2.2.1-rc.1",
3
+ "version": "2.2.1-rc.7",
4
4
  "private": false,
5
5
  "description": "A standard Web Component shell for UI Embedding",
6
6
  "license": "SEE LICENSE IN LICENSE.txt",
@@ -25,9 +25,13 @@
25
25
  "test": "jest",
26
26
  "clean": "rimraf dist"
27
27
  },
28
- "devDependencies": {},
28
+ "devDependencies": {
29
+ "utils": "2.2.1-rc.7"
30
+ },
29
31
  "files": [
30
32
  "dist/",
33
+ "!dist/__tests__/",
34
+ "!dist/__mocks__/",
31
35
  "!dist/*.test.js",
32
36
  "!dist/*.map",
33
37
  "scripts/",
@@ -1,29 +0,0 @@
1
- export interface GraphQLBridgeRequest {
2
- requestId: string;
3
- query: string;
4
- variables?: Record<string, unknown>;
5
- }
6
- export interface GraphQLBridgeResponse {
7
- requestId: string;
8
- ok: boolean;
9
- result?: {
10
- data: unknown;
11
- errors?: unknown[];
12
- };
13
- error?: {
14
- message: string;
15
- };
16
- }
17
- export declare function executeGraphQL(query: string, variables?: Record<string, unknown>): Promise<{
18
- data: unknown;
19
- errors?: unknown[];
20
- }>;
21
- export declare class GraphQLBridgeHandler {
22
- #private;
23
- constructor(sendResponse: (data: GraphQLBridgeResponse) => void, maxConcurrent?: number, maxQueueSize?: number);
24
- static isValidRequest(data: unknown): data is GraphQLBridgeRequest;
25
- rejectInvalidRequest(data: unknown): void;
26
- handleRequest({ requestId, query, variables }: GraphQLBridgeRequest): Promise<void>;
27
- reset(): void;
28
- }
29
- //# sourceMappingURL=graphqlBridgeHandler.d.ts.map