@lightning-out/containers 2.2.0-rc.0

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 +226 -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 +48 -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 +107 -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 +38 -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,226 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ import { api, LightningElement, track } from "lwc";
8
+
9
+ import { MSG } from "./utils/contants";
10
+
11
+ import { createEnvContext } from "./utils/env";
12
+ import { createGlobalError } from "./utils/errorHandling";
13
+
14
+ import { createInstrumentation } from "./utils/instrumentation";
15
+ import { applyCustomStyleProperties } from "./utils/style";
16
+
17
+ // These settings are important for layout and sizing.
18
+ document.documentElement.style.height = "100%";
19
+ document.documentElement.style.display = "block";
20
+ document.body.style.margin = "0px";
21
+ document.body.style.height = "100%";
22
+ document.body.style.display = "block";
23
+
24
+ export default class LightningOutBaseContainer extends LightningElement {
25
+ static renderMode = "light";
26
+
27
+ #embeddedCmp;
28
+ embeddedCmpCtor;
29
+ embeddedCmpName;
30
+ @track embeddedCmpProps;
31
+ embeddedCmpType = "lwc";
32
+
33
+ #env;
34
+ #eventListeners = new Map();
35
+ #renderActivity;
36
+ #renderActivityStopped = false;
37
+
38
+ #globalError;
39
+ isError = false;
40
+ #instrumentation;
41
+
42
+ constructor() {
43
+ super();
44
+ this.#env = createEnvContext();
45
+ this.#instrumentation = createInstrumentation(
46
+ "LightningOutLWRApp",
47
+ this.#env.componentNameParam,
48
+ this.#env.loAppOrigin,
49
+ );
50
+ this.#globalError = createGlobalError(this.#env.parentPostMessage, this.#instrumentation.logError);
51
+ window.addEventListener("message", this.#messageListener);
52
+ }
53
+
54
+ @api
55
+ get embeddedCmp() {
56
+ return this.refs?.embeddedCmp;
57
+ }
58
+
59
+ getEnv() {
60
+ return this.#env;
61
+ }
62
+
63
+ getInstrumentation() {
64
+ return this.#instrumentation;
65
+ }
66
+
67
+ #messageListener = (event) => {
68
+ // Page must be in an <iframe> unless testMode
69
+ if (!this.#env.testMode && window.top === window.self) {
70
+ window.console.trace("Lightning Out: This page is not in an <iframe>");
71
+ return;
72
+ }
73
+ if (event.origin !== this.#env.loAppOrigin) {
74
+ window.console.log(
75
+ `Lightning Out: Message ignored due to different origin: ${event.origin} vs ${this.#env.loAppOrigin}`,
76
+ );
77
+ return;
78
+ }
79
+ switch (event.data.type) {
80
+ case MSG.ADD_EVENT_LISTENER: {
81
+ const eventName = event.data.name;
82
+ const eventOptions = event.data.options;
83
+ const listenerKey = event.data.listenerKey;
84
+ const listener = (evt) => {
85
+ this.#env.parentPostMessage({
86
+ name: eventName,
87
+ detail: structuredClone(evt.detail),
88
+ type: MSG.DISPATCH_EVENT,
89
+ });
90
+ };
91
+ this.#embeddedCmp.addEventListener(eventName, listener, eventOptions);
92
+ this.#eventListeners.set(listenerKey, listener);
93
+ break;
94
+ }
95
+ case MSG.DISPATCH_EVENT: {
96
+ const customEvent = new CustomEvent(event.data.name, {
97
+ detail: event.data.detail,
98
+ });
99
+ this.#embeddedCmp.dispatchEvent(customEvent);
100
+ break;
101
+ }
102
+ case MSG.REMOVE_EVENT_LISTENER: {
103
+ const eventName = event.data.name;
104
+ const eventOptions = event.data.options;
105
+ const listenerKey = event.data.listenerKey;
106
+ const listener = this.#eventListeners.get(listenerKey);
107
+ if (listener) {
108
+ this.#embeddedCmp.removeEventListener(eventName, listener, eventOptions);
109
+ this.#eventListeners.delete(listenerKey);
110
+ }
111
+ break;
112
+ }
113
+ case MSG.SET_COMPONENT_DATA: {
114
+ this.renderComponent(event.data.componentData);
115
+ break;
116
+ }
117
+ case MSG.SET_COMPONENT_PROPS: {
118
+ this.setComponentProps(event.data.componentProps);
119
+ break;
120
+ }
121
+ case MSG.RELOAD: {
122
+ window.location.reload();
123
+ break;
124
+ }
125
+ default: {
126
+ // noop
127
+ }
128
+ }
129
+ };
130
+
131
+ async renderComponent(componentData) {
132
+ this.embeddedCmpId = componentData.id;
133
+ this.embeddedCmpName = componentData.name;
134
+ this.embeddedCmpProps = componentData.props;
135
+ if (!this.embeddedCmpName || !this.embeddedCmpProps) {
136
+ this.#globalError(`Missing 'componentName' or 'componentProps,' both must be specified.`);
137
+ }
138
+
139
+ const { specifier, type } = this.resolveEmbeddedCmpSpecifier(this.embeddedCmpName);
140
+ this.embeddedCmpType = type;
141
+
142
+ this.#renderActivity = this.#instrumentation.startRenderActivity({
143
+ componentName: specifier,
144
+ componentType: type,
145
+ loAppOrigin: this.#env.loAppOrigin,
146
+ });
147
+
148
+ try {
149
+ const { default: ctor } = await import(specifier);
150
+ this.embeddedCmpCtor = ctor;
151
+ } catch (err) {
152
+ this.isError = true;
153
+ this.#instrumentation.errorRenderActivity(this.#renderActivity, err, {
154
+ componentName: specifier,
155
+ componentType: type,
156
+ componentProps: JSON.stringify(this.embeddedCmpProps || {}),
157
+ message: err?.message,
158
+ hasError: this.isError,
159
+ loAppOrigin: this.#env.loAppOrigin,
160
+ });
161
+ this.#globalError(err);
162
+ } finally {
163
+ if (this.isError && this.#renderActivity && !this.#renderActivityStopped) {
164
+ this.#instrumentation.stopRenderActivity(this.#renderActivity, {
165
+ componentName: specifier,
166
+ componentType: type,
167
+ hasError: this.isError,
168
+ loAppOrigin: this.#env.loAppOrigin,
169
+ });
170
+ this.#renderActivityStopped = true;
171
+ }
172
+ }
173
+ }
174
+
175
+ setComponentProps(componentProps) {
176
+ if (componentProps.style && this.embeddedCmp) {
177
+ applyCustomStyleProperties(componentProps.style, this.embeddedCmp.style);
178
+ delete componentProps.style;
179
+ }
180
+ if (Object.keys(componentProps).length) {
181
+ this.embeddedCmpProps = componentProps;
182
+ }
183
+ }
184
+
185
+ resolveEmbeddedCmpSpecifier(embeddedCmpName) {
186
+ const [name] = embeddedCmpName.split("/v/");
187
+ const type = name.includes(":") ? "aura" : "lwc";
188
+ if (type === "aura") {
189
+ this.#globalError("Aura components require using 'lightningOutOneRuntimeContainer' instead.");
190
+ }
191
+ return { specifier: name, type };
192
+ }
193
+
194
+ connectedCallback() {
195
+ this.#env.parentPostMessage({ type: MSG.LOADED });
196
+ const componentData = this.#env.testMode ? this.#env.getComponentData() : null;
197
+ if (componentData) {
198
+ this.renderComponent(componentData);
199
+ } else {
200
+ this.#env.parentPostMessage({ type: MSG.GET_COMPONENT_DATA });
201
+ }
202
+ }
203
+
204
+ renderedCallback() {
205
+ if (!this.#embeddedCmp && this.refs?.embeddedCmp) {
206
+ this.#embeddedCmp = this.refs.embeddedCmp;
207
+ this.#embeddedCmp.addEventListener("message", (evt) => {
208
+ this.#env.parentPostMessage({
209
+ name: evt.type,
210
+ detail: structuredClone(evt.detail),
211
+ type: MSG.DISPATCH_EVENT,
212
+ });
213
+ });
214
+ this.#env.parentPostMessage({ type: MSG.READY });
215
+ if (this.#renderActivity && !this.#renderActivityStopped) {
216
+ this.#instrumentation.stopRenderActivity(this.#renderActivity, {
217
+ componentName: this.embeddedCmpName,
218
+ componentType: this.embeddedCmpType,
219
+ hasError: this.isError,
220
+ loAppOrigin: this.#env.loAppOrigin,
221
+ });
222
+ this.#renderActivityStopped = true;
223
+ }
224
+ }
225
+ }
226
+ }
@@ -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,48 @@
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 startRenderActivity(instrumentationContext) {
25
+ return instrumentation.startActivity("component-render", {
26
+ instrumentationContext,
27
+ });
28
+ }
29
+
30
+ function errorRenderActivity(activity, err, details) {
31
+ if (activity) {
32
+ activity.error(err, lightningOutComponentRenderSchema, details);
33
+ }
34
+ }
35
+
36
+ function stopRenderActivity(activity, details) {
37
+ if (activity) {
38
+ activity.stop(lightningOutComponentRenderSchema, details);
39
+ }
40
+ }
41
+
42
+ return {
43
+ logError,
44
+ startRenderActivity,
45
+ errorRenderActivity,
46
+ stopRenderActivity,
47
+ };
48
+ }
@@ -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,107 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ import LightningOutBaseContainer from "force/lightningOutBaseContainer";
8
+
9
+ import { createErrorHooks } from "./utils/errorHandling";
10
+ import { isBeaconEnabled } from "./utils/gater";
11
+
12
+ export default class LightningOutLWRAuraContainer extends LightningOutBaseContainer {
13
+ #includeBeacon = false;
14
+ componentName = null;
15
+ config = {
16
+ usePageTransactions: false,
17
+ disableAvroValidation: false,
18
+ };
19
+ #renderActivity;
20
+ #renderActivityStopped = false;
21
+ constructor() {
22
+ super();
23
+ this.#includeBeacon = isBeaconEnabled();
24
+ createErrorHooks(this.getEnv().parentPostMessage, this.getInstrumentation().logError);
25
+ }
26
+
27
+ get includeBeacon() {
28
+ return this.#includeBeacon;
29
+ }
30
+
31
+ async renderComponent(componentData) {
32
+ if (!componentData.name) {
33
+ const error = new Error("LightningOutLWRAuraContainer requires 'componentName'.");
34
+ this.getInstrumentation().logError(error);
35
+ throw error;
36
+ }
37
+
38
+ const isAura = componentData.name.includes(":");
39
+ this.embeddedCmpType = isAura ? "aura" : "lwc";
40
+ this.embeddedCmpId = componentData.id || "componentId";
41
+ this.embeddedCmpName = componentData.name;
42
+ this.embeddedCmpProps = componentData.props || {};
43
+ this.componentName = componentData.name;
44
+ this.isError = false;
45
+
46
+ this.#renderActivityStopped = false;
47
+ this.#renderActivity = this.getInstrumentation().startRenderActivity(this._getInstrumentationContext());
48
+
49
+ try {
50
+ this.getEnv().parentPostMessage({ type: "lo.ready" });
51
+ } catch (err) {
52
+ this.isError = true;
53
+ this._handleRenderError(err);
54
+ }
55
+ }
56
+
57
+ setComponentProps(componentProps) {
58
+ if (!componentProps) {
59
+ return;
60
+ }
61
+ this.embeddedCmpProps = componentProps;
62
+ }
63
+
64
+ resolveEmbeddedCmpSpecifier(embeddedCmpName) {
65
+ const [, version] = embeddedCmpName.split("/v/");
66
+ const calculatedVersion = version || "0_0_1";
67
+
68
+ return { specifier: `aura/interop/v/${calculatedVersion}`, type: "aura" };
69
+ }
70
+
71
+ renderedCallback() {
72
+ super.renderedCallback();
73
+ if (this.refs?.embeddedCmp) {
74
+ this._stopRenderActivity();
75
+ }
76
+ }
77
+
78
+ _handleRenderError(err) {
79
+ this.getInstrumentation().errorRenderActivity(this.#renderActivity, err, {
80
+ ...this._getInstrumentationContext(),
81
+ componentProps: JSON.stringify(this.embeddedCmpProps || {}),
82
+ message: err?.message,
83
+ hasError: true,
84
+ });
85
+ this._stopRenderActivity({ hasError: true });
86
+ }
87
+
88
+ _stopRenderActivity(details = {}) {
89
+ if (!this.#renderActivity || this.#renderActivityStopped) {
90
+ return;
91
+ }
92
+ this.getInstrumentation().stopRenderActivity(this.#renderActivity, {
93
+ ...this._getInstrumentationContext(),
94
+ hasError: this.isError,
95
+ ...details,
96
+ });
97
+ this.#renderActivityStopped = true;
98
+ }
99
+
100
+ _getInstrumentationContext() {
101
+ return {
102
+ componentName: this.embeddedCmpName,
103
+ componentType: this.embeddedCmpType,
104
+ loAppOrigin: this.getEnv().loAppOrigin,
105
+ };
106
+ }
107
+ }
@@ -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,38 @@
1
+ /*
2
+ * Copyright 2025 salesforce.com, inc.
3
+ * All Rights Reserved
4
+ * Company Confidential
5
+ */
6
+
7
+ import LightningOutBaseContainer from "force/lightningOutBaseContainer";
8
+
9
+ import { createErrorHooks } from "./utils/errorHandling";
10
+ import { isBeaconEnabled } from "./utils/gater";
11
+
12
+ export default class LightningOutOneRuntimeContainer extends LightningOutBaseContainer {
13
+ #includeBeacon = false;
14
+ config = {
15
+ usePageTransactions: false,
16
+ disableAvroValidation: false,
17
+ };
18
+
19
+ constructor() {
20
+ super();
21
+ createErrorHooks(this.getEnv().parentPostMessage, this.getInstrumentation().logError);
22
+ this.#includeBeacon = isBeaconEnabled();
23
+ }
24
+
25
+ get includeBeacon() {
26
+ return this.#includeBeacon;
27
+ }
28
+
29
+ resolveEmbeddedCmpSpecifier(embeddedCmpName) {
30
+ const [name, version] = embeddedCmpName.split("/v/");
31
+ const type = name.includes(":") ? "aura" : "lwc";
32
+ const calculatedVersion = version || "0_0_1";
33
+ if (type === "aura") {
34
+ return { specifier: `aura/interop/v/${calculatedVersion}`, type };
35
+ }
36
+ return { specifier: `${name}/v/${calculatedVersion}`, type };
37
+ }
38
+ }
@@ -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": "@lightning-out/containers",
3
+ "version": "2.2.0-rc.0",
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
+ }