@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.
- package/dist/index.esm.js +62 -19
- 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
|
+
/*! @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(
|
|
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(
|
|
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 =
|
|
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,
|
|
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.
|
|
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.
|
|
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",
|