@salesforce/internal-lightning-out-containers 2.2.0-rc.2

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 (22) hide show
  1. package/dist/modules/force/lightningOutBaseContainer/lightningOutBaseContainer.html +7 -0
  2. package/dist/modules/force/lightningOutBaseContainer/lightningOutBaseContainer.js +251 -0
  3. package/dist/modules/force/lightningOutBaseContainer/lightningOutBaseContainer.js-meta.xml +4 -0
  4. package/dist/modules/force/lightningOutBaseContainer/utils/contants.js +17 -0
  5. package/dist/modules/force/lightningOutBaseContainer/utils/env.js +56 -0
  6. package/dist/modules/force/lightningOutBaseContainer/utils/errorHandling.js +16 -0
  7. package/dist/modules/force/lightningOutBaseContainer/utils/instrumentation.js +58 -0
  8. package/dist/modules/force/lightningOutBaseContainer/utils/style.js +21 -0
  9. package/dist/modules/force/lightningOutLWRAuraContainer/lightningOutLWRAuraContainer.html +17 -0
  10. package/dist/modules/force/lightningOutLWRAuraContainer/lightningOutLWRAuraContainer.js +125 -0
  11. package/dist/modules/force/lightningOutLWRAuraContainer/lightningOutLWRAuraContainer.js-meta.xml +4 -0
  12. package/dist/modules/force/lightningOutLWRAuraContainer/utils/errorHandling.js +55 -0
  13. package/dist/modules/force/lightningOutLWRAuraContainer/utils/gater.js +14 -0
  14. package/dist/modules/force/lightningOutLWRContainer/lightningOutLWRContainer.html +7 -0
  15. package/dist/modules/force/lightningOutLWRContainer/lightningOutLWRContainer.js +7 -0
  16. package/dist/modules/force/lightningOutLWRContainer/lightningOutLWRContainer.js-meta.xml +8 -0
  17. package/dist/modules/force/lightningOutOneRuntimeContainer/lightningOutOneRuntimeContainer.html +14 -0
  18. package/dist/modules/force/lightningOutOneRuntimeContainer/lightningOutOneRuntimeContainer.js +57 -0
  19. package/dist/modules/force/lightningOutOneRuntimeContainer/lightningOutOneRuntimeContainer.js-meta.xml +4 -0
  20. package/dist/modules/force/lightningOutOneRuntimeContainer/utils/errorHandling.js +49 -0
  21. package/dist/modules/force/lightningOutOneRuntimeContainer/utils/gater.js +14 -0
  22. package/package.json +18 -0
@@ -0,0 +1,7 @@
1
+ <template lwc:render-mode="light">
2
+ <lwc:component
3
+ lwc:is={embeddedCmpCtor}
4
+ lwc:ref="embeddedCmp"
5
+ lwc:spread={embeddedCmpProps}
6
+ ></lwc:component>
7
+ </template>
@@ -0,0 +1,251 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ import { api, LightningElement, track } from "lwc";
8
+ import { pdpEventSchema } from "o11y_schema/sf_pdp";
9
+
10
+ import { MSG } from "./utils/contants";
11
+
12
+ import { createEnvContext } from "./utils/env";
13
+ import { createGlobalError } from "./utils/errorHandling";
14
+
15
+ import { createInstrumentation } from "./utils/instrumentation";
16
+ import { applyCustomStyleProperties } from "./utils/style";
17
+
18
+ // These settings are important for layout and sizing.
19
+ document.documentElement.style.height = "100%";
20
+ document.documentElement.style.display = "block";
21
+ document.body.style.margin = "0px";
22
+ document.body.style.height = "100%";
23
+ document.body.style.display = "block";
24
+
25
+ // Apply global style overrides from URL parameter during bootstrap
26
+ const urlSearchParams = new URLSearchParams(window.location.search);
27
+ const globalStyleParam = urlSearchParams.get("globalStyle");
28
+ if (globalStyleParam) {
29
+ try {
30
+ // Apply CSS custom properties directly to document.documentElement
31
+ applyCustomStyleProperties(globalStyleParam, document.documentElement.style);
32
+ } catch (err) {
33
+ window.console.error("Failed to apply globalStyle parameter:", err);
34
+ }
35
+ }
36
+
37
+ export default class LightningOutBaseContainer extends LightningElement {
38
+ static renderMode = "light";
39
+
40
+ #embeddedCmp;
41
+ embeddedCmpCtor;
42
+ embeddedCmpName;
43
+ @track embeddedCmpProps;
44
+ embeddedCmpType = "lwc";
45
+
46
+ #env;
47
+ #eventListeners = new Map();
48
+ #renderActivity;
49
+ #renderActivityStopped = false;
50
+
51
+ #globalError;
52
+ isError = false;
53
+ #instrumentation;
54
+
55
+ constructor() {
56
+ super();
57
+ this.#env = createEnvContext();
58
+ this.#instrumentation = createInstrumentation(
59
+ "LightningOutLWRApp",
60
+ this.#env.componentNameParam,
61
+ this.#env.loAppOrigin,
62
+ );
63
+ this.#globalError = createGlobalError(this.#env.parentPostMessage, this.#instrumentation.logError);
64
+ window.addEventListener("message", this.#messageListener);
65
+ }
66
+
67
+ @api
68
+ get embeddedCmp() {
69
+ return this.refs?.embeddedCmp;
70
+ }
71
+
72
+ getEnv() {
73
+ return this.#env;
74
+ }
75
+
76
+ getInstrumentation() {
77
+ return this.#instrumentation;
78
+ }
79
+
80
+ #messageListener = (event) => {
81
+ // Page must be in an <iframe> unless testMode
82
+ if (!this.#env.testMode && window.top === window.self) {
83
+ window.console.trace("Lightning Out: This page is not in an <iframe>");
84
+ return;
85
+ }
86
+ if (event.origin !== this.#env.loAppOrigin) {
87
+ window.console.log(
88
+ `Lightning Out: Message ignored due to different origin: ${event.origin} vs ${this.#env.loAppOrigin}`,
89
+ );
90
+ return;
91
+ }
92
+ switch (event.data.type) {
93
+ case MSG.ADD_EVENT_LISTENER: {
94
+ const eventName = event.data.name;
95
+ const eventOptions = event.data.options;
96
+ const listenerKey = event.data.listenerKey;
97
+ const listener = (evt) => {
98
+ this.#env.parentPostMessage({
99
+ name: eventName,
100
+ detail: structuredClone(evt.detail),
101
+ type: MSG.DISPATCH_EVENT,
102
+ });
103
+ };
104
+ this.#embeddedCmp.addEventListener(eventName, listener, eventOptions);
105
+ this.#eventListeners.set(listenerKey, listener);
106
+ break;
107
+ }
108
+ case MSG.DISPATCH_EVENT: {
109
+ const customEvent = new CustomEvent(event.data.name, {
110
+ detail: event.data.detail,
111
+ });
112
+ this.#embeddedCmp.dispatchEvent(customEvent);
113
+ break;
114
+ }
115
+ case MSG.REMOVE_EVENT_LISTENER: {
116
+ const eventName = event.data.name;
117
+ const eventOptions = event.data.options;
118
+ const listenerKey = event.data.listenerKey;
119
+ const listener = this.#eventListeners.get(listenerKey);
120
+ if (listener) {
121
+ this.#embeddedCmp.removeEventListener(eventName, listener, eventOptions);
122
+ this.#eventListeners.delete(listenerKey);
123
+ }
124
+ break;
125
+ }
126
+ case MSG.SET_COMPONENT_DATA: {
127
+ this.renderComponent(event.data.componentData);
128
+ break;
129
+ }
130
+ case MSG.SET_COMPONENT_PROPS: {
131
+ this.setComponentProps(event.data.componentProps);
132
+ break;
133
+ }
134
+ case MSG.RELOAD: {
135
+ window.location.reload();
136
+ break;
137
+ }
138
+ default: {
139
+ // noop
140
+ }
141
+ }
142
+ };
143
+
144
+ async renderComponent(componentData) {
145
+ this.embeddedCmpId = componentData.id;
146
+ this.embeddedCmpName = componentData.name;
147
+ this.embeddedCmpProps = componentData.props;
148
+ if (!this.embeddedCmpName || !this.embeddedCmpProps) {
149
+ this.#globalError(`Missing 'componentName' or 'componentProps,' both must be specified.`);
150
+ }
151
+
152
+ const { specifier, type } = this.resolveEmbeddedCmpSpecifier(this.embeddedCmpName);
153
+ this.embeddedCmpType = type;
154
+
155
+ this.#renderActivity = this.#instrumentation.startRenderActivity({
156
+ componentName: specifier,
157
+ componentType: type,
158
+ loAppOrigin: this.#env.loAppOrigin,
159
+ });
160
+
161
+ try {
162
+ // eslint-disable-next-line @lwc/lwc-platform/no-dynamic-import-identifier
163
+ const { default: ctor } = await import(specifier);
164
+ this.embeddedCmpCtor = ctor;
165
+ } catch (err) {
166
+ this.isError = true;
167
+ this.#instrumentation.errorRenderActivity(this.#renderActivity, err, {
168
+ componentName: specifier,
169
+ componentType: type,
170
+ componentProps: JSON.stringify(this.embeddedCmpProps || {}),
171
+ message: err?.message,
172
+ hasError: this.isError,
173
+ loAppOrigin: this.#env.loAppOrigin,
174
+ });
175
+ this.#globalError(err);
176
+ } finally {
177
+ if (this.isError && this.#renderActivity && !this.#renderActivityStopped) {
178
+ this.#instrumentation.stopRenderActivity(this.#renderActivity, {
179
+ componentName: specifier,
180
+ componentType: type,
181
+ hasError: this.isError,
182
+ loAppOrigin: this.#env.loAppOrigin,
183
+ });
184
+ this.#renderActivityStopped = true;
185
+ }
186
+ }
187
+ }
188
+
189
+ setComponentProps(componentProps) {
190
+ if (componentProps.style && this.embeddedCmp) {
191
+ applyCustomStyleProperties(componentProps.style, this.embeddedCmp.style);
192
+ delete componentProps.style;
193
+ }
194
+ if (Object.keys(componentProps).length) {
195
+ this.embeddedCmpProps = componentProps;
196
+ }
197
+ }
198
+
199
+ resolveEmbeddedCmpSpecifier(embeddedCmpName) {
200
+ const [name] = embeddedCmpName.split("/v/");
201
+ const type = name.includes(":") ? "aura" : "lwc";
202
+ if (type === "aura") {
203
+ this.#globalError("Aura components require using 'lightningOutOneRuntimeContainer' instead.");
204
+ }
205
+ return { specifier: name, type };
206
+ }
207
+
208
+ connectedCallback() {
209
+ this.#env.parentPostMessage({ type: MSG.LOADED });
210
+ const componentData = this.#env.testMode ? this.#env.getComponentData() : null;
211
+ if (componentData) {
212
+ this.renderComponent(componentData);
213
+ } else {
214
+ this.#env.parentPostMessage({ type: MSG.GET_COMPONENT_DATA });
215
+ }
216
+ }
217
+
218
+ renderedCallback() {
219
+ if (!this.#embeddedCmp && this.refs?.embeddedCmp) {
220
+ this.#embeddedCmp = this.refs.embeddedCmp;
221
+ this.#embeddedCmp.addEventListener("message", (evt) => {
222
+ this.#env.parentPostMessage({
223
+ name: evt.type,
224
+ detail: structuredClone(evt.detail),
225
+ type: MSG.DISPATCH_EVENT,
226
+ });
227
+ });
228
+ this.#env.parentPostMessage({ type: MSG.READY });
229
+
230
+ // PDP_ProductUsage telemetry
231
+ if (this.#instrumentation?.log) {
232
+ this.#instrumentation.log(pdpEventSchema, {
233
+ eventName: "lightningOutContainer.ready",
234
+ productFeatureId: "aJCEE000000152T4AQ",
235
+ contextName: "componentType",
236
+ contextValue: this.embeddedCmpType,
237
+ });
238
+ }
239
+
240
+ if (this.#renderActivity && !this.#renderActivityStopped) {
241
+ this.#instrumentation.stopRenderActivity(this.#renderActivity, {
242
+ componentName: this.embeddedCmpName,
243
+ componentType: this.embeddedCmpType,
244
+ hasError: this.isError,
245
+ loAppOrigin: this.#env.loAppOrigin,
246
+ });
247
+ this.#renderActivityStopped = true;
248
+ }
249
+ }
250
+ }
251
+ }
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <isExposed>false</isExposed>
4
+ </LightningComponentBundle>
@@ -0,0 +1,17 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ export const MSG = {
8
+ LOADED: "lo.loaded",
9
+ READY: "lo.ready",
10
+ ADD_EVENT_LISTENER: "lo.addEventListener",
11
+ REMOVE_EVENT_LISTENER: "lo.removeEventListener",
12
+ DISPATCH_EVENT: "lo.dispatchEvent",
13
+ SET_COMPONENT_DATA: "lo.setComponentData",
14
+ SET_COMPONENT_PROPS: "lo.setComponentProps",
15
+ RELOAD: "lo.reload",
16
+ GET_COMPONENT_DATA: "lo.getComponentData",
17
+ };
@@ -0,0 +1,56 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ const urlSearchParams = new URLSearchParams(window.location.search);
8
+
9
+ export function createEnvContext() {
10
+ const loAppOrigin = urlSearchParams.get("loAppOrigin");
11
+ const parentElementId = urlSearchParams.get("parentElementId");
12
+ const componentNameParam = urlSearchParams.get("componentName");
13
+ const testMode = urlSearchParams.get("testMode") === "true";
14
+
15
+ try {
16
+ const isInBuilder = window.self !== window.top && window.name === "sitePreviewFrame";
17
+ const isInBuilderContext =
18
+ urlSearchParams.get("app") === "commeditor" && urlSearchParams.get("view") === "editor";
19
+ if (!isInBuilder && !isInBuilderContext) {
20
+ (() => new URL(loAppOrigin))();
21
+ }
22
+ } catch (err) {
23
+ throw new Error(`${err.message} (loAppOrigin: ${loAppOrigin})`);
24
+ }
25
+
26
+ function parentPostMessage(message) {
27
+ if (typeof message !== "string") {
28
+ message.id = parentElementId;
29
+ }
30
+ window.parent.postMessage(message, loAppOrigin);
31
+ }
32
+
33
+ function getComponentData() {
34
+ const componentProps = urlSearchParams.get("componentProps");
35
+ if (!componentProps) {
36
+ return null;
37
+ }
38
+ try {
39
+ return {
40
+ name: componentNameParam,
41
+ props: JSON.parse(componentProps),
42
+ };
43
+ } catch {
44
+ throw new Error(`Invalid component properties: ${componentProps}`);
45
+ }
46
+ }
47
+
48
+ return {
49
+ loAppOrigin,
50
+ parentElementId,
51
+ componentNameParam,
52
+ testMode,
53
+ parentPostMessage,
54
+ getComponentData,
55
+ };
56
+ }
@@ -0,0 +1,16 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ export function createGlobalError(parentPostMessage, logError) {
8
+ return function globalError(err) {
9
+ const msg = err?.message || `${err}`;
10
+ const fullMsg = `Lightning-Out Container Error: ${msg}`;
11
+ logError(fullMsg);
12
+ window.console.error(fullMsg);
13
+ parentPostMessage(fullMsg);
14
+ throw new Error(fullMsg);
15
+ };
16
+ }
@@ -0,0 +1,58 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ import { getInstrumentation } from "o11y/client";
8
+ import { lightningOutComponentRenderSchema } from "o11y_schema/sf_lightningOut";
9
+
10
+ export function createInstrumentation(channelName, componentName, loAppOrigin) {
11
+ const instrumentation = getInstrumentation(channelName);
12
+
13
+ function logError(error) {
14
+ if (instrumentation) {
15
+ instrumentation.error(error, lightningOutComponentRenderSchema, {
16
+ componentName,
17
+ message: error.message || String(error),
18
+ hasError: true,
19
+ loAppOrigin,
20
+ });
21
+ }
22
+ }
23
+
24
+ function log(schema, payload) {
25
+ if (instrumentation && typeof instrumentation.log === "function") {
26
+ instrumentation.log(schema, payload);
27
+ }
28
+ }
29
+
30
+ function startRenderActivity(instrumentationContext) {
31
+ if (instrumentation && typeof instrumentation.startActivity === "function") {
32
+ return instrumentation.startActivity("component-render", {
33
+ instrumentationContext,
34
+ });
35
+ }
36
+ return null;
37
+ }
38
+
39
+ function errorRenderActivity(activity, err, details) {
40
+ if (activity) {
41
+ activity.error(err, lightningOutComponentRenderSchema, details);
42
+ }
43
+ }
44
+
45
+ function stopRenderActivity(activity, details) {
46
+ if (activity) {
47
+ activity.stop(lightningOutComponentRenderSchema, details);
48
+ }
49
+ }
50
+
51
+ return {
52
+ logError,
53
+ startRenderActivity,
54
+ errorRenderActivity,
55
+ stopRenderActivity,
56
+ log,
57
+ };
58
+ }
@@ -0,0 +1,21 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ export function applyCustomStyleProperties(componentPropsStyle, embeddedCmpStyle) {
8
+ for (let i = 0, len = embeddedCmpStyle.length; i < len; i++) {
9
+ const prop = embeddedCmpStyle.item(i);
10
+ if (prop && prop.startsWith("--")) {
11
+ embeddedCmpStyle.removeProperty(prop);
12
+ }
13
+ }
14
+ const allStyles = componentPropsStyle.split(";");
15
+ allStyles.forEach((style) => {
16
+ const [prop, val] = style.split(":");
17
+ if (prop && prop.trim().startsWith("--")) {
18
+ embeddedCmpStyle.setProperty(prop.trim(), val ?? "");
19
+ }
20
+ });
21
+ }
@@ -0,0 +1,17 @@
1
+ <template lwc:render-mode="light">
2
+ <div lwc:if={includeBeacon}>
3
+ <aura-interop
4
+ descriptor="instrumentation:beacon"
5
+ config={config}
6
+ ></aura-interop>
7
+ </div>
8
+ <lwrlex-instrumentation></lwrlex-instrumentation>
9
+
10
+ <div lwc:if={componentName}>
11
+ <aura-interop
12
+ descriptor={componentName}
13
+ lwc:ref="embeddedCmp"
14
+ lwc:spread={embeddedCmpProps}>
15
+ </aura-interop>
16
+ </div>
17
+ </template>
@@ -0,0 +1,125 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ import LightningOutBaseContainer from "force/lightningOutBaseContainer";
8
+ import { pdpEventSchema } from "o11y_schema/sf_pdp";
9
+
10
+ import { createErrorHooks } from "./utils/errorHandling";
11
+ import { isBeaconEnabled } from "./utils/gater";
12
+
13
+ export default class LightningOutLWRAuraContainer extends LightningOutBaseContainer {
14
+ #includeBeacon = false;
15
+ componentName = null;
16
+ config = {
17
+ usePageTransactions: false,
18
+ disableAvroValidation: false,
19
+ };
20
+ #renderActivity;
21
+ #renderActivityStopped = false;
22
+ #telemetryLogged = false;
23
+
24
+ constructor() {
25
+ super();
26
+ this.#includeBeacon = isBeaconEnabled();
27
+ createErrorHooks(this.getEnv().parentPostMessage, this.getInstrumentation().logError);
28
+ }
29
+
30
+ get includeBeacon() {
31
+ return this.#includeBeacon;
32
+ }
33
+
34
+ async renderComponent(componentData) {
35
+ if (!componentData.name) {
36
+ const error = new Error("LightningOutLWRAuraContainer requires 'componentName'.");
37
+ this.getInstrumentation().logError(error);
38
+ throw error;
39
+ }
40
+
41
+ const isAura = componentData.name.includes(":");
42
+ this.embeddedCmpType = isAura ? "aura" : "lwc";
43
+ this.embeddedCmpId = componentData.id || "componentId";
44
+ this.embeddedCmpName = componentData.name;
45
+ this.embeddedCmpProps = componentData.props || {};
46
+ this.componentName = componentData.name;
47
+ this.isError = false;
48
+
49
+ this.#renderActivityStopped = false;
50
+ this.#renderActivity = this.getInstrumentation().startRenderActivity(this._getInstrumentationContext());
51
+
52
+ try {
53
+ this.getEnv().parentPostMessage({ type: "lo.ready" });
54
+ } catch (err) {
55
+ this.isError = true;
56
+ this._handleRenderError(err);
57
+ }
58
+ }
59
+
60
+ setComponentProps(componentProps) {
61
+ if (!componentProps) {
62
+ return;
63
+ }
64
+ this.embeddedCmpProps = componentProps;
65
+ }
66
+
67
+ resolveEmbeddedCmpSpecifier(embeddedCmpName) {
68
+ const [, version] = embeddedCmpName.split("/v/");
69
+ const calculatedVersion = version || "0_0_1";
70
+
71
+ return { specifier: `aura/interop/v/${calculatedVersion}`, type: "aura" };
72
+ }
73
+
74
+ renderedCallback() {
75
+ super.renderedCallback();
76
+ if (this.refs?.embeddedCmp) {
77
+ this._stopRenderActivity();
78
+
79
+ // PDP_ProductUsage telemetry - component is now fully rendered (log only once)
80
+ if (!this.#telemetryLogged) {
81
+ this.#telemetryLogged = true;
82
+ if (this.getInstrumentation().log) {
83
+ this.getInstrumentation().log(pdpEventSchema, {
84
+ eventName: "lightningOutComponent.rendered",
85
+ productFeatureId: "aJCEE000000152T4AQ",
86
+ componentId: this.embeddedCmpId,
87
+ eventVolume: 1,
88
+ contextName: "componentType",
89
+ contextValue: this.embeddedCmpType,
90
+ });
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ _handleRenderError(err) {
97
+ this.getInstrumentation().errorRenderActivity(this.#renderActivity, err, {
98
+ ...this._getInstrumentationContext(),
99
+ componentProps: JSON.stringify(this.embeddedCmpProps || {}),
100
+ message: err?.message,
101
+ hasError: true,
102
+ });
103
+ this._stopRenderActivity({ hasError: true });
104
+ }
105
+
106
+ _stopRenderActivity(details = {}) {
107
+ if (!this.#renderActivity || this.#renderActivityStopped) {
108
+ return;
109
+ }
110
+ this.getInstrumentation().stopRenderActivity(this.#renderActivity, {
111
+ ...this._getInstrumentationContext(),
112
+ hasError: this.isError,
113
+ ...details,
114
+ });
115
+ this.#renderActivityStopped = true;
116
+ }
117
+
118
+ _getInstrumentationContext() {
119
+ return {
120
+ componentName: this.embeddedCmpName,
121
+ componentType: this.embeddedCmpType,
122
+ loAppOrigin: this.getEnv().loAppOrigin,
123
+ };
124
+ }
125
+ }
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <isExposed>false</isExposed>
4
+ </LightningComponentBundle>
@@ -0,0 +1,55 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ import {
8
+ CLIENT_ERROR_TYPES,
9
+ onError,
10
+ onHttpError,
11
+ onInvalidSession,
12
+ onUnhandledRejection,
13
+ } from "@lwrjs/globalErrorHandler";
14
+
15
+ export function createErrorHooks(parentPostMessage, logError) {
16
+ function post(type, payload) {
17
+ parentPostMessage({ type: "lo.error", error: { ...payload, type } });
18
+ logError(payload);
19
+ return { handled: true };
20
+ }
21
+
22
+ onError((error, type) => post(type, { message: `Error: ${error.message}` }));
23
+ onUnhandledRejection((error, type) =>
24
+ post(type, {
25
+ message: `Unhandled Promise Rejection: ${error.message}`,
26
+ }),
27
+ );
28
+ onInvalidSession((error, type) => post(type, { message: `Invalid Session: ${error.message}` }));
29
+ onHttpError((error, type) => {
30
+ if (type === CLIENT_ERROR_TYPES.HTTP_ERROR) {
31
+ const status = error.status || (error.response && error.response.status);
32
+ if (status) {
33
+ let message = "";
34
+ switch (status) {
35
+ case 401:
36
+ message = "Session expired or unauthorized. Please log in again.";
37
+ break;
38
+ case 429:
39
+ message = "Too many requests. Please try again later.";
40
+ break;
41
+ default:
42
+ if (status >= 500) {
43
+ message = "A server error occurred. Please try again later.";
44
+ }
45
+ break;
46
+ }
47
+ return post(type, {
48
+ status,
49
+ message: `HTTP Error: ${message}`,
50
+ });
51
+ }
52
+ }
53
+ return { handled: false };
54
+ });
55
+ }
@@ -0,0 +1,14 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ import disableBeaconGate from "@salesforce/gate/org.salesforce.lo2.lwr.disableBeacon";
8
+
9
+ /**
10
+ * @returns {boolean} Checks if the beacon feature is enabled.
11
+ */
12
+ export function isBeaconEnabled() {
13
+ return !disableBeaconGate.isOpen({ fallback: false });
14
+ }
@@ -0,0 +1,7 @@
1
+ <template lwc:render-mode="light">
2
+ <lwc:component
3
+ lwc:is={embeddedCmpCtor}
4
+ lwc:ref="embeddedCmp"
5
+ lwc:spread={embeddedCmpProps}
6
+ ></lwc:component>
7
+ </template>
@@ -0,0 +1,7 @@
1
+ /*
2
+ * Copyright 2024 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ export { default } from "force/lightningOutBaseContainer";
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <isExposed>true</isExposed>
4
+ <targets>
5
+ <target>lightningCommunity__Page</target>
6
+ <target>lightningCommunity__Default</target>
7
+ </targets>
8
+ </LightningComponentBundle>
@@ -0,0 +1,14 @@
1
+ <template lwc:render-mode="light">
2
+ <div lwc:if={includeBeacon}>
3
+ <aura-interop
4
+ descriptor="instrumentation:beacon"
5
+ config={config}
6
+ ></aura-interop>
7
+ </div>
8
+ <lwrlex-instrumentation></lwrlex-instrumentation>
9
+ <lwc:component
10
+ lwc:is={embeddedCmpCtor}
11
+ lwc:ref="embeddedCmp"
12
+ lwc:spread={embeddedCmpProps}
13
+ ></lwc:component>
14
+ </template>
@@ -0,0 +1,57 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ import LightningOutBaseContainer from "force/lightningOutBaseContainer";
8
+ import { pdpEventSchema } from "o11y_schema/sf_pdp";
9
+
10
+ import { createErrorHooks } from "./utils/errorHandling";
11
+ import { isBeaconEnabled } from "./utils/gater";
12
+
13
+ export default class LightningOutOneRuntimeContainer extends LightningOutBaseContainer {
14
+ #includeBeacon = false;
15
+ #telemetryLogged = false;
16
+ config = {
17
+ usePageTransactions: false,
18
+ disableAvroValidation: false,
19
+ };
20
+
21
+ constructor() {
22
+ super();
23
+ createErrorHooks(this.getEnv().parentPostMessage, this.getInstrumentation().logError);
24
+ this.#includeBeacon = isBeaconEnabled();
25
+ }
26
+
27
+ get includeBeacon() {
28
+ return this.#includeBeacon;
29
+ }
30
+
31
+ renderedCallback() {
32
+ super.renderedCallback();
33
+ if (this.refs?.embeddedCmp && !this.#telemetryLogged) {
34
+ this.#telemetryLogged = true;
35
+ if (this.getInstrumentation().log) {
36
+ this.getInstrumentation().log(pdpEventSchema, {
37
+ eventName: "lightningOutComponent.rendered",
38
+ productFeatureId: "aJCEE000000152T4AQ",
39
+ componentId: this.embeddedCmpId,
40
+ eventVolume: 1,
41
+ contextName: "componentType",
42
+ contextValue: this.embeddedCmpType,
43
+ });
44
+ }
45
+ }
46
+ }
47
+
48
+ resolveEmbeddedCmpSpecifier(embeddedCmpName) {
49
+ const [name, version] = embeddedCmpName.split("/v/");
50
+ const type = name.includes(":") ? "aura" : "lwc";
51
+ const calculatedVersion = version || "0_0_1";
52
+ if (type === "aura") {
53
+ return { specifier: `aura/interop/v/${calculatedVersion}`, type };
54
+ }
55
+ return { specifier: `${name}/v/${calculatedVersion}`, type };
56
+ }
57
+ }
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <isExposed>false</isExposed>
4
+ </LightningComponentBundle>
@@ -0,0 +1,49 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ import {
8
+ CLIENT_ERROR_TYPES,
9
+ onError,
10
+ onHttpError,
11
+ onInvalidSession,
12
+ onUnhandledRejection,
13
+ } from "@lwrjs/globalErrorHandler";
14
+
15
+ export function createErrorHooks(parentPostMessage, logError) {
16
+ function post(type, payload) {
17
+ parentPostMessage({ type: "lo.error", error: { ...payload, type } });
18
+ logError(payload);
19
+ return { handled: true };
20
+ }
21
+
22
+ onError((error, type) => post(type, { message: `Error: ${error.message}` }));
23
+ onUnhandledRejection((error, type) =>
24
+ post(type, {
25
+ message: `Unhandled Promise Rejection: ${error.message}`,
26
+ }),
27
+ );
28
+ onInvalidSession((error, type) => post(type, { message: `Invalid Session: ${error.message}` }));
29
+ onHttpError((error, type) => {
30
+ if (type === CLIENT_ERROR_TYPES.HTTP_ERROR) {
31
+ const status = error.status || (error.response && error.response.status);
32
+ if (status) {
33
+ let message = "";
34
+ if (status === 401) {
35
+ message = "Session expired or unauthorized. Please log in again.";
36
+ } else if (status === 429) {
37
+ message = "Too many requests. Please try again later.";
38
+ } else if (status >= 500) {
39
+ message = "A server error occurred. Please try again later.";
40
+ }
41
+ return post(type, {
42
+ status,
43
+ message: `HTTP Error: ${message}`,
44
+ });
45
+ }
46
+ }
47
+ return { handled: false };
48
+ });
49
+ }
@@ -0,0 +1,14 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ import disableBeaconGate from "@salesforce/gate/org.salesforce.lo2.lwr.disableBeacon";
8
+
9
+ /**
10
+ * @returns {boolean} Checks if the beacon feature is enabled.
11
+ */
12
+ export function isBeaconEnabled() {
13
+ return !disableBeaconGate.isOpen({ fallback: false });
14
+ }
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@salesforce/internal-lightning-out-containers",
3
+ "version": "2.2.0-rc.2",
4
+ "private": false,
5
+ "description": "Lightning Out 2.0 Container Components",
6
+ "type": "module",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "node scripts/build.mjs",
12
+ "typecheck": "tsc --noEmit",
13
+ "clean": "rimraf dist"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ }
18
+ }