@salesforce-ui-embedding/internal-lightning-ui-embedding 2.2.7-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.
- package/LICENSE.txt +21 -0
- package/README.md +3 -0
- package/dist/modules/lightning/uiEmbedding/uiEmbedding.css +23 -0
- package/dist/modules/lightning/uiEmbedding/uiEmbedding.html +8 -0
- package/dist/modules/lightning/uiEmbedding/uiEmbedding.js +965 -0
- package/dist/modules/lightning/uiEmbedding/uiEmbedding.js-meta.xml +9 -0
- package/dist/modules/lightning/uiEmbedding/utils/bootstrap.js +63 -0
- package/dist/modules/lightning/uiEmbedding/utils/constants.js +43 -0
- package/dist/modules/lightning/uiEmbedding/utils/host-bridge.js +150 -0
- package/dist/modules/lightning/uiEmbedding/utils/host-event-registry.js +35 -0
- package/dist/modules/lightning/uiEmbedding/utils/host-info.js +13 -0
- package/dist/modules/lightning/uiEmbedding/utils/instrumentation.js +90 -0
- package/dist/modules/lightning/uiEmbedding/utils/jsonrpc.js +122 -0
- package/dist/modules/lightning/uiEmbedding/utils/log.js +12 -0
- package/dist/modules/lightning/uiEmbedding/utils/request-router.js +375 -0
- package/dist/modules/lightning/uiEmbedding/utils/sandbox.js +27 -0
- package/dist/modules/lightning/uiEmbedding/utils/sf-embedding-contract.js +66 -0
- package/dist/modules/lightning/uiEmbedding/utils/ui-state.js +76 -0
- package/dist/modules/lightning/uiEmbedding/utils/url.js +21 -0
- package/dist/modules/types/external.js +0 -0
- package/package.json +32 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!--
|
|
3
|
+
Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
4
|
+
For full license text, see the LICENSE.txt file in this package.
|
|
5
|
+
-->
|
|
6
|
+
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
7
|
+
<isExposed>true</isExposed>
|
|
8
|
+
<minApiVersion>62.0</minApiVersion>
|
|
9
|
+
</LightningComponentBundle>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
3
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ERROR_CODES } from "./constants";
|
|
7
|
+
import { BOOTSTRAP_ENVELOPE_TYPE, HEARTBEAT_TYPE, PROTOCOL_NAME, SHUTDOWN_TYPE } from "./sf-embedding-contract";
|
|
8
|
+
|
|
9
|
+
// TODO: MVP — exact-match. API stays the same; internal algorithm as per spec definition will change later.
|
|
10
|
+
const HOST_SUPPORTED_PROTOCOL_VERSIONS = new Set(["1.0.0"]);
|
|
11
|
+
|
|
12
|
+
export function negotiateProtocolVersion(version) {
|
|
13
|
+
if (typeof version === "string" && HOST_SUPPORTED_PROTOCOL_VERSIONS.has(version)) {
|
|
14
|
+
return { ok: true, protocolVersion: version };
|
|
15
|
+
}
|
|
16
|
+
return { ok: false, protocolVersion: version };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function validateHeartbeat(input) {
|
|
20
|
+
if (!hasMessageType(input.data, HEARTBEAT_TYPE)) return { ok: false, reason: ERROR_CODES.WRONG_TYPE };
|
|
21
|
+
if (input.source !== input.expectedSource) return { ok: false, reason: ERROR_CODES.WRONG_SOURCE };
|
|
22
|
+
if (input.origin !== input.expectedOrigin) return { ok: false, reason: ERROR_CODES.WRONG_ORIGIN };
|
|
23
|
+
const declared = input.data.protocolVersion;
|
|
24
|
+
const negotiated = negotiateProtocolVersion(declared);
|
|
25
|
+
if (!negotiated.ok) {
|
|
26
|
+
return {
|
|
27
|
+
ok: false,
|
|
28
|
+
reason: ERROR_CODES.PROTOCOL_VERSION_MISMATCH,
|
|
29
|
+
protocolVersion: typeof negotiated.protocolVersion === "string" ? negotiated.protocolVersion : undefined,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const instanceId = input.data.instanceId;
|
|
33
|
+
if (typeof instanceId !== "string" || instanceId !== input.expectedInstanceId) {
|
|
34
|
+
return { ok: false, reason: ERROR_CODES.INSTANCE_ID_MISMATCH };
|
|
35
|
+
}
|
|
36
|
+
if (input.alreadyTransferred) return { ok: false, reason: ERROR_CODES.DUPLICATE };
|
|
37
|
+
return { ok: true, protocolVersion: negotiated.protocolVersion };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function validateShutdown(input) {
|
|
41
|
+
if (!hasMessageType(input.data, SHUTDOWN_TYPE)) return { ok: false, reason: ERROR_CODES.WRONG_TYPE };
|
|
42
|
+
if (input.source !== input.expectedSource) return { ok: false, reason: ERROR_CODES.WRONG_SOURCE };
|
|
43
|
+
if (input.origin !== input.expectedOrigin) return { ok: false, reason: ERROR_CODES.WRONG_ORIGIN };
|
|
44
|
+
const reason = input.data.reason;
|
|
45
|
+
// Any string is accepted as-is; non-strings resolve to undefined and the caller uses
|
|
46
|
+
// ?? EMBEDDING_SHUTDOWN. Callers that emit this to Argus must bound cardinality themselves.
|
|
47
|
+
const safeReason = typeof reason === "string" ? reason : undefined;
|
|
48
|
+
return { ok: true, reason: safeReason };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function buildBootstrapEnvelope(opts) {
|
|
52
|
+
return {
|
|
53
|
+
type: BOOTSTRAP_ENVELOPE_TYPE,
|
|
54
|
+
protocol: PROTOCOL_NAME,
|
|
55
|
+
instanceId: opts.instanceId,
|
|
56
|
+
allowedOrigins: opts.allowedOrigins,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function hasMessageType(value, expected) {
|
|
61
|
+
if (typeof value !== "object" || value === null) return false;
|
|
62
|
+
return value.type === expected;
|
|
63
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
3
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const DEFAULT_SANDBOX_TOKENS = ["allow-scripts", "allow-same-origin"];
|
|
7
|
+
|
|
8
|
+
// Default advisory heartbeat deadline; Infinity opts out.
|
|
9
|
+
export const DEFAULT_HEARTBEAT_TIMEOUT_MS = 30_000;
|
|
10
|
+
|
|
11
|
+
export const ERROR_PHASES = {
|
|
12
|
+
CONFIGURATION: "configuration",
|
|
13
|
+
BOOTSTRAP: "bootstrap",
|
|
14
|
+
EVENT_FORWARDING: "event-forwarding",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const ERROR_CODES = {
|
|
18
|
+
// Configuration Errors
|
|
19
|
+
ALLOW_SCRIPTS_STRIPPED: "ALLOW_SCRIPTS_STRIPPED",
|
|
20
|
+
BLOCKED_SANDBOX_TOKEN: "BLOCKED_SANDBOX_TOKEN",
|
|
21
|
+
MISSING_SRC: "MISSING_SRC",
|
|
22
|
+
INVALID_SRC: "INVALID_SRC",
|
|
23
|
+
SAME_ORIGIN_SRC: "SAME_ORIGIN_SRC",
|
|
24
|
+
SESSION_BINDING_MUTATED: "SESSION_BINDING_MUTATED",
|
|
25
|
+
// Bootstrap / Heartbeat Errors
|
|
26
|
+
PROTOCOL_VERSION_MISMATCH: "PROTOCOL_VERSION_MISMATCH",
|
|
27
|
+
HEARTBEAT_TIMEOUT: "HEARTBEAT_TIMEOUT",
|
|
28
|
+
TRANSFER_FAILED: "TRANSFER_FAILED",
|
|
29
|
+
WRONG_TYPE: "WRONG_TYPE",
|
|
30
|
+
WRONG_SOURCE: "WRONG_SOURCE",
|
|
31
|
+
WRONG_ORIGIN: "WRONG_ORIGIN",
|
|
32
|
+
INSTANCE_ID_MISMATCH: "INSTANCE_ID_MISMATCH",
|
|
33
|
+
DUPLICATE: "DUPLICATE",
|
|
34
|
+
DUPLICATE_PORT_TRANSFER: "DUPLICATE_PORT_TRANSFER",
|
|
35
|
+
EMBEDDING_SHUTDOWN: "EMBEDDING_SHUTDOWN",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const STATES = {
|
|
39
|
+
PRE_HEARTBEAT: "pre-heartbeat",
|
|
40
|
+
POST_HEARTBEAT: "post-heartbeat",
|
|
41
|
+
READY: "ready",
|
|
42
|
+
CLOSED: "closed",
|
|
43
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
3
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Host-side peer. One object that routes inbound requests (via the shared
|
|
7
|
+
// JsonRpcRouter), handles inbound notifications, and exposes named methods for
|
|
8
|
+
// the host's outbound traffic. Inbound handlers are registered in the
|
|
9
|
+
// constructor; outbound methods (e.g. announceInitialized) wrap the inherited
|
|
10
|
+
// send/notify so traceId `_meta` is stamped centrally by the BridgeClient.
|
|
11
|
+
|
|
12
|
+
import { HostEventRegistry } from "./host-event-registry";
|
|
13
|
+
|
|
14
|
+
import { MessageChannelTransport, StandardErrorCode } from "./jsonrpc";
|
|
15
|
+
import { createLogger } from "./log";
|
|
16
|
+
import { BridgeClient, JsonRpcRouter } from "./request-router";
|
|
17
|
+
import {
|
|
18
|
+
EMBEDDING_INITIALIZED_METHOD,
|
|
19
|
+
ERROR_METHOD,
|
|
20
|
+
EVENTS_DISPATCH_METHOD,
|
|
21
|
+
EVENTS_SUBSCRIBE_METHOD,
|
|
22
|
+
EVENTS_UNSUBSCRIBE_METHOD,
|
|
23
|
+
HOST_INITIALIZED_METHOD,
|
|
24
|
+
JsonRpcHandlerError,
|
|
25
|
+
RESIZE_METHOD,
|
|
26
|
+
UI_STATE_SUBSCRIBE_METHOD,
|
|
27
|
+
UI_STATE_UNSUBSCRIBE_METHOD,
|
|
28
|
+
} from "./sf-embedding-contract";
|
|
29
|
+
|
|
30
|
+
export class HostBridge extends JsonRpcRouter {
|
|
31
|
+
// Active UiState wire subscription. Only one is permitted at a time; a second
|
|
32
|
+
// subscribe rejects until the first unsubscribes. Lives on the bridge because
|
|
33
|
+
// it's pure wire-routing state, scoped to the session the bridge represents.
|
|
34
|
+
_uiStateSubscriptionId = null;
|
|
35
|
+
_uiStateSubscriptionCounter = 0;
|
|
36
|
+
|
|
37
|
+
// Custom-event subscriptions for this session. Coarse "is anyone listening?"
|
|
38
|
+
// bookkeeping; per-eventType filtering happens on the embedding side.
|
|
39
|
+
_hostEvents = new HostEventRegistry();
|
|
40
|
+
|
|
41
|
+
_instrumentation = null;
|
|
42
|
+
|
|
43
|
+
constructor(
|
|
44
|
+
port,
|
|
45
|
+
composeUiState,
|
|
46
|
+
redispatchToHost,
|
|
47
|
+
onResizeHint,
|
|
48
|
+
onSfEmbeddingError,
|
|
49
|
+
log = createLogger("[lightning-ui-embedding:host-bridge]", () => false),
|
|
50
|
+
instrumentation,
|
|
51
|
+
) {
|
|
52
|
+
// Wrap the transferred port in the shared transport; the BridgeClient
|
|
53
|
+
// subscribes to it (lazily starting the port), and the router claims
|
|
54
|
+
// inbound requests on the same transport.
|
|
55
|
+
super(new BridgeClient(new MessageChannelTransport(port)), log);
|
|
56
|
+
this._instrumentation = instrumentation ?? null;
|
|
57
|
+
|
|
58
|
+
// Lifecycle notification: the embedding side signals it has initialized.
|
|
59
|
+
// No host action beyond logging today, but routing it keeps the single
|
|
60
|
+
// dispatch path (no special-casing in a raw port listener).
|
|
61
|
+
this.onNotification(EMBEDDING_INITIALIZED_METHOD, () => {
|
|
62
|
+
log("embedding-initialized received");
|
|
63
|
+
this._instrumentation?.incrementCounter("sfembedding.embedding_initialized.received", 1, false);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// UiState subscription axis. One active subscription per session; the
|
|
67
|
+
// subscribe response carries the initial snapshot, and the host pushes
|
|
68
|
+
// ui-state-changed notifications (via the component) until unsubscribe.
|
|
69
|
+
this.onRequest(UI_STATE_SUBSCRIBE_METHOD, () => {
|
|
70
|
+
if (this._uiStateSubscriptionId !== null) {
|
|
71
|
+
this._instrumentation?.incrementCounter("sfembedding.uistate.subscribe_rejected", 1, true, {
|
|
72
|
+
reason: "already_active",
|
|
73
|
+
});
|
|
74
|
+
throw new JsonRpcHandlerError(StandardErrorCode.INVALID_PARAMS, "ui-state subscription already active");
|
|
75
|
+
}
|
|
76
|
+
const subscriptionId = `ui-state-${++this._uiStateSubscriptionCounter}`;
|
|
77
|
+
this._uiStateSubscriptionId = subscriptionId;
|
|
78
|
+
this._instrumentation?.incrementCounter("sfembedding.uistate.subscribed", 1, false);
|
|
79
|
+
return { subscriptionId, current: composeUiState() };
|
|
80
|
+
});
|
|
81
|
+
this.onRequest(UI_STATE_UNSUBSCRIBE_METHOD, (params) => {
|
|
82
|
+
if (!params || params.subscriptionId !== this._uiStateSubscriptionId) {
|
|
83
|
+
this._instrumentation?.incrementCounter("sfembedding.uistate.unsubscribe_rejected", 1, true, {
|
|
84
|
+
reason: "unknown_id",
|
|
85
|
+
});
|
|
86
|
+
throw new JsonRpcHandlerError(
|
|
87
|
+
StandardErrorCode.INVALID_PARAMS,
|
|
88
|
+
`unknown subscriptionId: ${params?.subscriptionId}`,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
this._uiStateSubscriptionId = null;
|
|
92
|
+
this._instrumentation?.incrementCounter("sfembedding.uistate.unsubscribed", 1, false);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Bidirectional custom events. Inbound dispatch re-fires on the host
|
|
96
|
+
// element (via the component-supplied callback, which owns the re-entrancy
|
|
97
|
+
// guard); subscribe/unsubscribe maintain the session-wide registry.
|
|
98
|
+
this.onNotification(EVENTS_DISPATCH_METHOD, (params) => {
|
|
99
|
+
this._instrumentation?.incrementCounter("sfembedding.events.dispatched_to_host", 1, false);
|
|
100
|
+
redispatchToHost(params);
|
|
101
|
+
});
|
|
102
|
+
this.onRequest(EVENTS_SUBSCRIBE_METHOD, () => {
|
|
103
|
+
this._instrumentation?.incrementCounter("sfembedding.events.subscribed", 1, false);
|
|
104
|
+
return { subscriptionId: this._hostEvents.subscribe() };
|
|
105
|
+
});
|
|
106
|
+
this.onRequest(EVENTS_UNSUBSCRIBE_METHOD, (params) => {
|
|
107
|
+
const id = params?.subscriptionId;
|
|
108
|
+
if (typeof id !== "string") {
|
|
109
|
+
this._instrumentation?.incrementCounter("sfembedding.events.unsubscribe_rejected", 1, true, {
|
|
110
|
+
reason: "missing_id",
|
|
111
|
+
});
|
|
112
|
+
throw new JsonRpcHandlerError(StandardErrorCode.INVALID_PARAMS, "subscriptionId required");
|
|
113
|
+
}
|
|
114
|
+
if (!this._hostEvents.unsubscribe(id)) {
|
|
115
|
+
this._instrumentation?.incrementCounter("sfembedding.events.unsubscribe_rejected", 1, true, {
|
|
116
|
+
reason: "unknown_id",
|
|
117
|
+
});
|
|
118
|
+
throw new JsonRpcHandlerError(StandardErrorCode.INVALID_PARAMS, `unknown subscriptionId: ${id}`);
|
|
119
|
+
}
|
|
120
|
+
this._instrumentation?.incrementCounter("sfembedding.events.unsubscribed", 1, false);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Resize hint (fire-and-forget); component applies to iframe.
|
|
124
|
+
this.onNotification(RESIZE_METHOD, (params) => {
|
|
125
|
+
this._instrumentation?.incrementCounter("sfembedding.resize.hint_received", 1, false);
|
|
126
|
+
onResizeHint(params);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Iframe error notification (non-PII); component pipes to o11y.
|
|
130
|
+
this.onNotification(ERROR_METHOD, (params) => {
|
|
131
|
+
log("embedding error received:", params);
|
|
132
|
+
onSfEmbeddingError(params);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** The active UiState wire subscription id, or null when none is active. */
|
|
137
|
+
get uiStateSubscriptionId() {
|
|
138
|
+
return this._uiStateSubscriptionId;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Number of active custom-event subscriptions for this session. */
|
|
142
|
+
get hostEventSubscriberCount() {
|
|
143
|
+
return this._hostEvents.size;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Announce that the host has initialized. traceId `_meta` is stamped by the client. */
|
|
147
|
+
announceInitialized(params) {
|
|
148
|
+
this.notify(HOST_INITIALIZED_METHOD, params);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
3
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Subscription registry for ui/events/subscribe + ui/events/unsubscribe.
|
|
7
|
+
// Global subscribe — tracks "is anyone listening from this iframe?" coarsely;
|
|
8
|
+
// per-eventType filtering happens on the embedding side.
|
|
9
|
+
|
|
10
|
+
export class HostEventRegistry {
|
|
11
|
+
ids = new Set();
|
|
12
|
+
counter = 0;
|
|
13
|
+
|
|
14
|
+
/** Register a new session-wide subscription. Returns an opaque ID. */
|
|
15
|
+
subscribe() {
|
|
16
|
+
const id = `evt-${++this.counter}`;
|
|
17
|
+
this.ids.add(id);
|
|
18
|
+
return id;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Drop a subscription by id. Returns false for unknown ids. */
|
|
22
|
+
unsubscribe(id) {
|
|
23
|
+
return this.ids.delete(id);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** True iff at least one subscription is active. */
|
|
27
|
+
get size() {
|
|
28
|
+
return this.ids.size;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Drop everything. */
|
|
32
|
+
clear() {
|
|
33
|
+
this.ids.clear();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
3
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// `"2.2.7-rc.2"` is a repo-wide ambient declared in /types/globals.d.ts;
|
|
7
|
+
// build.mjs substitutes the literal at build time, jest globals supply a test value.
|
|
8
|
+
|
|
9
|
+
/** Identity emitted on `ui/notifications/host-initialized`. */
|
|
10
|
+
export const HOST_INFO = {
|
|
11
|
+
name: "lightning-ui-embedding",
|
|
12
|
+
version: "2.2.7-rc.2",
|
|
13
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 salesforce.com, inc.
|
|
3
|
+
* All Rights Reserved
|
|
4
|
+
* Company Confidential
|
|
5
|
+
*
|
|
6
|
+
* o11y wrapper for the lightning-ui-embedding host component. Narrowed to the
|
|
7
|
+
* bootstrap lifecycle and host-side error reporting:
|
|
8
|
+
*
|
|
9
|
+
* - uiEmbeddingLoadSchema (uxact) — embedding_init activity; stop() = success
|
|
10
|
+
* (timed span from src-set to READY)
|
|
11
|
+
* - uiEmbeddingErrorSchema (uxerr) — failure on the bootstrap path or any
|
|
12
|
+
* host-side protocol error
|
|
13
|
+
*
|
|
14
|
+
* Channel: LightningUiEmbedding.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { getInstrumentation } from "o11y/client";
|
|
18
|
+
import { uiEmbeddingErrorSchema, uiEmbeddingLoadSchema } from "o11y_schema/sf_uiEmbedding";
|
|
19
|
+
|
|
20
|
+
/** Free-form user payload merged into an o11y activity/error record. */
|
|
21
|
+
|
|
22
|
+
/** Opaque o11y activity handle returned by `startActivity` — surface varies by runtime. */
|
|
23
|
+
|
|
24
|
+
export function createInstrumentation(channelName, widgetSrc, hostAppOrigin) {
|
|
25
|
+
const instrumentation = getInstrumentation(channelName);
|
|
26
|
+
|
|
27
|
+
function logError(error, details) {
|
|
28
|
+
if (!instrumentation || typeof instrumentation.error !== "function") return;
|
|
29
|
+
try {
|
|
30
|
+
instrumentation.error(error, uiEmbeddingErrorSchema, {
|
|
31
|
+
widgetSrc,
|
|
32
|
+
hostAppOrigin,
|
|
33
|
+
message: error?.message || String(error),
|
|
34
|
+
isHostError: true,
|
|
35
|
+
...details,
|
|
36
|
+
});
|
|
37
|
+
} catch {
|
|
38
|
+
// swallow
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function startLoadActivity(instrumentationContext) {
|
|
43
|
+
if (!instrumentation || typeof instrumentation.startActivity !== "function") return null;
|
|
44
|
+
try {
|
|
45
|
+
return instrumentation.startActivity("embedding_init", { instrumentationContext });
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function errorLoadActivity(activity, err, details) {
|
|
52
|
+
const act = activity;
|
|
53
|
+
if (!act || typeof act.error !== "function") return;
|
|
54
|
+
try {
|
|
55
|
+
act.error(err, uiEmbeddingErrorSchema, details);
|
|
56
|
+
} catch {
|
|
57
|
+
// swallow
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function stopLoadActivity(activity, details) {
|
|
62
|
+
const act = activity;
|
|
63
|
+
if (!act || typeof act.stop !== "function") return;
|
|
64
|
+
try {
|
|
65
|
+
act.stop(uiEmbeddingLoadSchema, details);
|
|
66
|
+
} catch {
|
|
67
|
+
// swallow
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Direct-to-Argus counter emission. In production the o11y client's
|
|
72
|
+
// incrementCounter() flows through Core → Funnel → Argus.
|
|
73
|
+
// Metric naming: core_ui_LightningUiEmbedding:<scope>.<name>.COUNT.Count
|
|
74
|
+
function incrementCounter(name, value, isError, tags) {
|
|
75
|
+
if (!instrumentation || typeof instrumentation.incrementCounter !== "function") return;
|
|
76
|
+
try {
|
|
77
|
+
instrumentation.incrementCounter(name, value, isError, tags || {});
|
|
78
|
+
} catch {
|
|
79
|
+
// swallow
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
logError,
|
|
85
|
+
startLoadActivity,
|
|
86
|
+
errorLoadActivity,
|
|
87
|
+
stopLoadActivity,
|
|
88
|
+
incrementCounter,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
function _classPrivateMethodInitSpec(e, a) {_checkPrivateRedeclaration(e, a), a.add(e);}function _classPrivateFieldInitSpec(e, t, a) {_checkPrivateRedeclaration(e, t), t.set(e, a);}function _checkPrivateRedeclaration(e, t) {if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object");}function _classPrivateFieldGet(s, a) {return s.get(_assertClassBrand(s, a));}function _classPrivateFieldSet(s, a, r) {return s.set(_assertClassBrand(s, a), r), r;}function _assertClassBrand(e, t, n) {if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n;throw new TypeError("Private element is not present on this object");}function R() {
|
|
2
|
+
}var _t = /*#__PURE__*/new WeakMap();var _s = /*#__PURE__*/new WeakMap();var _r = /*#__PURE__*/new WeakMap();var _n = /*#__PURE__*/new WeakMap();var _e = /*#__PURE__*/new WeakMap();var _O_brand = /*#__PURE__*/new WeakSet();var _i = /*#__PURE__*/new WeakMap();
|
|
3
|
+
class O {
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
constructor(_t2) {_classPrivateMethodInitSpec(this, _O_brand);_classPrivateFieldInitSpec(this, _t, void 0);_classPrivateFieldInitSpec(this, _s, /* @__PURE__ */new Set());_classPrivateFieldInitSpec(this, _r, false);_classPrivateFieldInitSpec(this, _n, false); /** Messages queued before `port.start()` has been called. Flushed on first subscribe. */_classPrivateFieldInitSpec(this, _e, []);
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
_classPrivateFieldInitSpec(this, _i, (t) => {
|
|
91
|
+
const s = [..._classPrivateFieldGet(_s, this)];
|
|
92
|
+
for (const n of s)
|
|
93
|
+
try {
|
|
94
|
+
n(t.data);
|
|
95
|
+
} catch {
|
|
96
|
+
}
|
|
97
|
+
});_classPrivateFieldSet(_t, this, _t2);} /**
|
|
98
|
+
* Post a message to the peer.
|
|
99
|
+
*
|
|
100
|
+
* Never throws on transient failure — a closed / neutered / disposed
|
|
101
|
+
* port silently drops the message. If the transport has not yet
|
|
102
|
+
* started (no `onMessage` subscriber), the message is buffered and
|
|
103
|
+
* flushed on first subscription.
|
|
104
|
+
*/post(t) {if (!_classPrivateFieldGet(_n, this)) {if (!_classPrivateFieldGet(_r, this)) {_classPrivateFieldGet(_e, this).push(t);return;}try {_classPrivateFieldGet(_t, this).postMessage(t);} catch {}}} /**
|
|
105
|
+
* Subscribe to inbound messages from the peer. The callback receives
|
|
106
|
+
* the **unwrapped** payload (`MessageEvent.data`) — callers never see
|
|
107
|
+
* the `MessageEvent` wrapper.
|
|
108
|
+
*
|
|
109
|
+
* Starts the underlying port on first subscription and flushes any
|
|
110
|
+
* messages queued via `post` before the port was started.
|
|
111
|
+
*
|
|
112
|
+
* Returns an unsubscribe function. Calling it is idempotent.
|
|
113
|
+
*/onMessage(t) {if (_classPrivateFieldGet(_n, this)) return R;_classPrivateFieldGet(_s, this).add(t), _classPrivateFieldGet(_r, this) || _assertClassBrand(_O_brand, this, _o).call(this);let s = false;return () => {s || (s = true, _classPrivateFieldGet(_s, this).delete(t));};} /**
|
|
114
|
+
* Tear down the transport:
|
|
115
|
+
* - remove the internal `message` listener
|
|
116
|
+
* - call `port.close()`
|
|
117
|
+
* - clear subscriber set and outbound buffer
|
|
118
|
+
* - subsequent `post` calls silently no-op
|
|
119
|
+
* - subsequent `onMessage` calls return a no-op unsubscribe
|
|
120
|
+
*/dispose() {if (!_classPrivateFieldGet(_n, this)) {_classPrivateFieldSet(_n, this, true);try {_classPrivateFieldGet(_t, this).removeEventListener("message", _classPrivateFieldGet(_i, this));} catch {}_classPrivateFieldGet(_s, this).clear(), _classPrivateFieldGet(_e, this).length = 0;try {_classPrivateFieldGet(_t, this).close();} catch {}}}}function _o() {_classPrivateFieldSet(_r, this, true), _classPrivateFieldGet(_t, this).addEventListener("message", _classPrivateFieldGet(_i, this));try {_classPrivateFieldGet(_t, this).start();} catch {}if (_classPrivateFieldGet(_e, this).length > 0) {const t = _classPrivateFieldGet(_e, this);_classPrivateFieldSet(_e, this, []);for (const s of t) try {_classPrivateFieldGet(_t, this).postMessage(s);} catch {}}}const a = { // --- Standard JSON-RPC 2.0 ----------------------------------------------
|
|
121
|
+
PARSE_ERROR: -32700, INVALID_REQUEST: -32600, METHOD_NOT_FOUND: -32601, INVALID_PARAMS: -32602, INTERNAL_ERROR: -32603, // --- Transport-level (any JsonRpcClient over a transport will emit) -----
|
|
122
|
+
/** Transport closed; pending requests rejected locally. */PORT_CLOSED: -32016, /** Payload could not be structured-cloned across the transport. */SERIALIZATION_FAILURE: -32017, /** `default` / `long` timeout-class request expired. */REQUEST_TIMEOUT: -32018, /** In-flight cap reached. */BACKPRESSURE: -32019, /** Terminal response to a $/cancel-ed request. */REQUEST_CANCELLED: -32024 }; /* @__PURE__ */new Set([a.REQUEST_TIMEOUT, a.BACKPRESSURE]);export { O as MessageChannelTransport, a as StandardErrorCode };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
3
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// TODO: require per-event log levels (warn/error/info), not a single debug-gated level.
|
|
7
|
+
|
|
8
|
+
export function createLogger(prefix, isEnabled) {
|
|
9
|
+
return (...args) => {
|
|
10
|
+
if (isEnabled()) window.console.log(prefix, ...args);
|
|
11
|
+
};
|
|
12
|
+
}
|