@salesforce/experimental-mfe-bridge 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.
Files changed (2) hide show
  1. package/dist/index.esm.js +62 -19
  2. package/package.json +4 -2
package/dist/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @salesforce/experimental-mfe-bridge v2.2.1-rc.1 (2026-04-09) */
1
+ /*! @salesforce/experimental-mfe-bridge v2.2.1-rc.7 (2026-06-16) */
2
2
  /**
3
3
  * EmbeddingResizer - Handles dynamic iframe/container resizing
4
4
  * Uses ResizeObserver to monitor element size changes and notify the host
@@ -83,6 +83,7 @@ class EmbeddingResizer {
83
83
  this.#lastHeight = heightPx;
84
84
  if (!this.#scheduled) {
85
85
  this.#scheduled = true;
86
+ // eslint-disable-next-line @lwc/lwc/no-async-operation
86
87
  requestAnimationFrame(() => {
87
88
  this.#scheduled = false;
88
89
  if (!this.#isObserving)
@@ -118,6 +119,15 @@ class EmbeddingResizer {
118
119
  * Auto-handles theme synchronization and error telemetry
119
120
  */
120
121
  const GRAPHQL_TIMEOUT_MS = 30_000;
122
+ /** Parses `value` as a URL and returns its `.origin`, or `null` if it does not parse. */
123
+ function extractOrigin(value) {
124
+ try {
125
+ return new URL(value).origin;
126
+ }
127
+ catch {
128
+ return null;
129
+ }
130
+ }
121
131
  class ExternalEmbedBridge extends EventTarget {
122
132
  #connected = false;
123
133
  #hostAppOrigin = null;
@@ -129,6 +139,14 @@ class ExternalEmbedBridge extends EventTarget {
129
139
  #graphqlCounter = 0;
130
140
  constructor() {
131
141
  super();
142
+ // Abort init if hostMetaData is missing/invalid — widget surfaces a clear failure
143
+ // instead of running half-initialised.
144
+ const failures = this.#readMetadataFromUrl();
145
+ if (failures.length > 0) {
146
+ // eslint-disable-next-line no-console
147
+ console.error("[Bridge] Bridge could not be established:\n - " + failures.join("\n - "));
148
+ return;
149
+ }
132
150
  window.addEventListener("message", (e) => this.#handleHostMessage(e));
133
151
  this.#setupErrorCapture();
134
152
  this.#sendToHost("bridge-ready");
@@ -136,16 +154,44 @@ class ExternalEmbedBridge extends EventTarget {
136
154
  // eslint-disable-next-line no-console
137
155
  console.log("[Bridge] Initialized and ready for communication");
138
156
  }
157
+ /** Populates #instanceId and #hostAppOrigin from the iframe URL's `hostMetaData`
158
+ * query param. Returns failure reasons (empty = success). */
159
+ #readMetadataFromUrl() {
160
+ const raw = new URLSearchParams(window.location.search).get("hostMetaData");
161
+ if (!raw)
162
+ return ["`hostMetaData` query param is missing"];
163
+ let parsed;
164
+ try {
165
+ parsed = JSON.parse(raw);
166
+ }
167
+ catch (e) {
168
+ const message = e instanceof Error ? e.message : String(e);
169
+ return [`\`hostMetaData\` is not valid JSON: ${message}`];
170
+ }
171
+ const instanceId = typeof parsed?.instanceId === "string" ? parsed.instanceId : "";
172
+ const rawHostAppOrigin = typeof parsed?.hostAppOrigin === "string" ? parsed.hostAppOrigin : "";
173
+ const hostAppOrigin = rawHostAppOrigin ? extractOrigin(rawHostAppOrigin) : "";
174
+ const failures = [];
175
+ if (!instanceId)
176
+ failures.push("`instanceId` is missing or empty");
177
+ if (!rawHostAppOrigin) {
178
+ failures.push("`hostAppOrigin` is missing or empty");
179
+ }
180
+ else if (!hostAppOrigin) {
181
+ failures.push("`hostAppOrigin` is not a valid URL");
182
+ }
183
+ if (failures.length === 0) {
184
+ this.#instanceId = instanceId;
185
+ this.#hostAppOrigin = hostAppOrigin;
186
+ }
187
+ return failures;
188
+ }
139
189
  #handleHostMessage(event) {
140
- if (event.source !== window.parent)
141
- return;
142
- if (this.#hostAppOrigin && event.origin !== this.#hostAppOrigin)
190
+ if (event.source !== window.parent || event.origin !== this.#hostAppOrigin)
143
191
  return;
144
192
  const payload = event.data || {};
145
193
  const { type, data, id } = payload;
146
- if (typeof type !== "string" || !type.startsWith("salesforce-"))
147
- return;
148
- if (this.#instanceId && id !== this.#instanceId)
194
+ if (typeof type !== "string" || !type.startsWith("salesforce-") || id !== this.#instanceId)
149
195
  return;
150
196
  // eslint-disable-next-line no-console
151
197
  console.log("[Bridge] host->widget", type, data);
@@ -157,7 +203,7 @@ class ExternalEmbedBridge extends EventTarget {
157
203
  this.#handleDataUpdate(data);
158
204
  break;
159
205
  case "salesforce-shell-ready":
160
- this.#handleConnectionReady(event.origin, id);
206
+ this.#handleConnectionReady();
161
207
  break;
162
208
  case "salesforce-bridge-graphql-response":
163
209
  this.#handleGraphQLResponse(data);
@@ -175,10 +221,8 @@ class ExternalEmbedBridge extends EventTarget {
175
221
  // use super to escape the override
176
222
  super.dispatchEvent(new CustomEvent("data", { detail: payload }));
177
223
  }
178
- #handleConnectionReady(origin, id) {
224
+ #handleConnectionReady() {
179
225
  this.#connected = true;
180
- this.#hostAppOrigin = origin;
181
- this.#instanceId = id;
182
226
  // use super to escape the override
183
227
  super.dispatchEvent(new CustomEvent("connected"));
184
228
  }
@@ -230,18 +274,12 @@ class ExternalEmbedBridge extends EventTarget {
230
274
  });
231
275
  this.#resizer.start();
232
276
  }
233
- #getHostAppOrigin(type) {
234
- return this.#hostAppOrigin || (type === "bridge-ready" ? "*" : null);
235
- }
236
277
  #sendToHost(type, data) {
237
- const origin = this.#getHostAppOrigin(type);
238
- if (!origin)
239
- return;
240
278
  try {
241
- const message = this.#instanceId ? { type, data, id: this.#instanceId } : { type, data };
279
+ const message = { type, data, id: this.#instanceId };
242
280
  // eslint-disable-next-line no-console
243
281
  console.log("[Bridge] Sending message to host:", message);
244
- window.parent.postMessage(message, origin);
282
+ window.parent.postMessage(message, this.#hostAppOrigin);
245
283
  }
246
284
  catch (error) {
247
285
  // eslint-disable-next-line no-console
@@ -283,6 +321,11 @@ class ExternalEmbedBridge extends EventTarget {
283
321
  // Override dispatchEvent to forward custom events to the host via post message
284
322
  dispatchEvent(event) {
285
323
  const result = super.dispatchEvent(event);
324
+ if (!this.#connected) {
325
+ // eslint-disable-next-line no-console
326
+ console.warn("[Bridge] dispatchEvent: bridge not connected, dropping", event.type);
327
+ return result;
328
+ }
286
329
  this.#sendToHost("custom-event", {
287
330
  eventType: event.type,
288
331
  detail: event.detail,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/experimental-mfe-bridge",
3
- "version": "2.2.1-rc.1",
3
+ "version": "2.2.1-rc.7",
4
4
  "private": false,
5
5
  "description": "EventTarget-Based Embed ↔ Host Communication for UI Embedding",
6
6
  "license": "SEE LICENSE IN LICENSE.txt",
@@ -25,10 +25,12 @@
25
25
  "clean": "rimraf dist"
26
26
  },
27
27
  "devDependencies": {
28
- "utils": "2.2.1-rc.1"
28
+ "utils": "2.2.1-rc.7"
29
29
  },
30
30
  "files": [
31
31
  "dist/",
32
+ "!dist/__tests__/",
33
+ "!dist/__mocks__/",
32
34
  "!dist/*.test.js",
33
35
  "!dist/*.map",
34
36
  "README.md",