@salesforce/sf-embedding-bridge 2.2.4 → 2.2.5-rc.1
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/bootstrap-session.d.ts +9 -1
- package/dist/index.cjs.js +124 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.esm.js +124 -3
- package/package.json +3 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type JsonRpcMeta, type Transport } from "@salesforce/jsonrpc";
|
|
2
|
-
import { type EventsDispatchParams, type EventsSubscribeResult, type HostMetaData, type ResizeParams, type UiStateChangedParams, type UiStateSubscribeResult } from "@salesforce/sf-embedding-contract";
|
|
2
|
+
import { type EmbeddingErrorParams, type EventsDispatchParams, type EventsSubscribeResult, type HostMetaData, type ResizeParams, type UiStateChangedParams, type UiStateSubscribeResult } from "@salesforce/sf-embedding-contract";
|
|
3
3
|
import { EmbeddingResizer, JsonRpcRouter, type RequestHandler, type Unregister } from "utils";
|
|
4
4
|
import { type BootstrapFailureReason } from "./bootstrap-listener";
|
|
5
5
|
export type HostInitializedPayload = Record<string, unknown>;
|
|
@@ -54,7 +54,15 @@ export declare class EmbeddingBridge extends JsonRpcRouter {
|
|
|
54
54
|
sendResize(dimensions: ResizeParams): void;
|
|
55
55
|
/** Attach an auto-emit resizer; stopped on dispose. Replaces any prior resizer. */
|
|
56
56
|
attachResizer(resizer: EmbeddingResizer): void;
|
|
57
|
+
/** Send outbound RPC request; on rejection, report error to host then rethrow. */
|
|
58
|
+
send<TParams = unknown, TResult = unknown>(method: string, params: TParams): Promise<TResult>;
|
|
57
59
|
dispose(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Report an error to the host for observability. Non-PII by construction.
|
|
62
|
+
* Two usage modes: (1) automatic uncaught-error capture (sdkMethod="uncaught"); (2) deliberate
|
|
63
|
+
* author reporting from try/catch (concrete sdkMethod name passed by the author).
|
|
64
|
+
*/
|
|
65
|
+
reportError(params: EmbeddingErrorParams): void;
|
|
58
66
|
}
|
|
59
67
|
export type { RequestHandler, Unregister };
|
|
60
68
|
/** Single-attempt per iframe; recovery from failure requires a host-driven remount. */
|
package/dist/index.cjs.js
CHANGED
|
@@ -90,6 +90,8 @@ class JsonRpcHandlerError extends Error {
|
|
|
90
90
|
const HOST_INITIALIZED_METHOD = "ui/notifications/host-initialized";
|
|
91
91
|
/** Embedding acknowledges receipt of `host-initialized`. Fire-and-forget. */
|
|
92
92
|
const EMBEDDING_INITIALIZED_METHOD = "ui/notifications/embedding-initialized";
|
|
93
|
+
/** Iframe-side error notification. Fire-and-forget, non-PII observability signal. */
|
|
94
|
+
const ERROR_METHOD = "ui/notifications/error";
|
|
93
95
|
|
|
94
96
|
// Resize hint channel: embedding → host, fire-and-forget. CSS pixels.
|
|
95
97
|
const RESIZE_METHOD = "ui/notifications/resize";
|
|
@@ -99,6 +101,10 @@ const UI_STATE_SUBSCRIBE_METHOD = "ui/subscribe/ui-state";
|
|
|
99
101
|
const UI_STATE_UNSUBSCRIBE_METHOD = "ui/unsubscribe/ui-state";
|
|
100
102
|
const UI_STATE_CHANGED_METHOD = "ui/notifications/ui-state-changed";
|
|
101
103
|
|
|
104
|
+
/*
|
|
105
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
106
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
107
|
+
*/
|
|
102
108
|
// Bootstrap envelope validator for the iframe (embedding) side of the
|
|
103
109
|
// sf-embedding handshake.
|
|
104
110
|
//
|
|
@@ -142,6 +148,10 @@ function validateBootstrapEnvelope(input) {
|
|
|
142
148
|
return { ok: true, envelope: input.data, port: input.ports[0] };
|
|
143
149
|
}
|
|
144
150
|
|
|
151
|
+
/*
|
|
152
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
153
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
154
|
+
*/
|
|
145
155
|
/**
|
|
146
156
|
* Parses the JSON-encoded `hostMetaData` query parameter from an iframe URL's
|
|
147
157
|
* search string (per ADR 0029). Returns null on any malformed input — caller
|
|
@@ -187,6 +197,10 @@ function readHostMetaData(search) {
|
|
|
187
197
|
return { instanceId, hostAppOrigin };
|
|
188
198
|
}
|
|
189
199
|
|
|
200
|
+
/*
|
|
201
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
202
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
203
|
+
*/
|
|
190
204
|
const BRIDGE_PROTOCOL_VERSION = "1.0.0";
|
|
191
205
|
class BootstrapFailureError extends Error {
|
|
192
206
|
reason;
|
|
@@ -550,12 +564,20 @@ class JsonRpcRouter {
|
|
|
550
564
|
}
|
|
551
565
|
}
|
|
552
566
|
|
|
567
|
+
/*
|
|
568
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
569
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
570
|
+
*/
|
|
553
571
|
/** Identity emitted on `ui/notifications/embedding-initialized`. */
|
|
554
572
|
const EMBEDDING_INFO = {
|
|
555
573
|
name: "@salesforce/sf-embedding-bridge",
|
|
556
|
-
version: "2.2.
|
|
574
|
+
version: "2.2.5-rc.1",
|
|
557
575
|
};
|
|
558
576
|
|
|
577
|
+
/*
|
|
578
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
579
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
580
|
+
*/
|
|
559
581
|
class SessionFailureError extends Error {
|
|
560
582
|
reason;
|
|
561
583
|
constructor(reason, cause) {
|
|
@@ -637,11 +659,35 @@ class EmbeddingBridge extends JsonRpcRouter {
|
|
|
637
659
|
this.resizer.stop();
|
|
638
660
|
this.resizer = resizer;
|
|
639
661
|
}
|
|
662
|
+
/** Send outbound RPC request; on rejection, report error to host then rethrow. */
|
|
663
|
+
send(method, params) {
|
|
664
|
+
return super.send(method, params).catch((err) => {
|
|
665
|
+
this.reportError({ sdkMethod: method, type: "rpc-failed" });
|
|
666
|
+
throw err;
|
|
667
|
+
});
|
|
668
|
+
}
|
|
640
669
|
dispose() {
|
|
641
670
|
this.resizer?.stop();
|
|
642
671
|
this.resizer = null;
|
|
672
|
+
uninstallErrorListeners();
|
|
643
673
|
super.dispose();
|
|
644
674
|
}
|
|
675
|
+
/**
|
|
676
|
+
* Report an error to the host for observability. Non-PII by construction.
|
|
677
|
+
* Two usage modes: (1) automatic uncaught-error capture (sdkMethod="uncaught"); (2) deliberate
|
|
678
|
+
* author reporting from try/catch (concrete sdkMethod name passed by the author).
|
|
679
|
+
*/
|
|
680
|
+
reportError(params) {
|
|
681
|
+
if (errorEmitCount >= ERROR_MAX_PER_SESSION)
|
|
682
|
+
return;
|
|
683
|
+
errorEmitCount++;
|
|
684
|
+
try {
|
|
685
|
+
this.notify(ERROR_METHOD, params);
|
|
686
|
+
}
|
|
687
|
+
catch {
|
|
688
|
+
// Rethrow would refire window.onerror and spiral.
|
|
689
|
+
}
|
|
690
|
+
}
|
|
645
691
|
}
|
|
646
692
|
// `cleanup` lets the caller drop our listener from the consumer-owned signal once the race resolves.
|
|
647
693
|
function rejectOnAbort(signal, cleanup) {
|
|
@@ -657,6 +703,71 @@ function rejectOnAbort(signal, cleanup) {
|
|
|
657
703
|
});
|
|
658
704
|
}
|
|
659
705
|
let sessionPromise = null;
|
|
706
|
+
// Safe as module state: sessionPromise singleton ensures one bridge per realm; listeners are realm-global.
|
|
707
|
+
let errorListenersAttached = false;
|
|
708
|
+
let errorEmitCount = 0;
|
|
709
|
+
let errorHandler = null;
|
|
710
|
+
let rejectionHandler = null;
|
|
711
|
+
const ERROR_MAX_PER_SESSION = 50;
|
|
712
|
+
function sanitizeFilename(filename) {
|
|
713
|
+
if (!filename)
|
|
714
|
+
return undefined;
|
|
715
|
+
// Blob/data URLs: scheme marker only (unparseable for pathname).
|
|
716
|
+
if (filename.startsWith("blob:") || filename.startsWith("data:")) {
|
|
717
|
+
return filename.split(":")[0] + ":<sanitized>";
|
|
718
|
+
}
|
|
719
|
+
try {
|
|
720
|
+
return new URL(filename).pathname;
|
|
721
|
+
}
|
|
722
|
+
catch {
|
|
723
|
+
return undefined;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
function stripUndefined(obj) {
|
|
727
|
+
const out = {};
|
|
728
|
+
for (const key of Object.keys(obj)) {
|
|
729
|
+
if (obj[key] !== undefined)
|
|
730
|
+
out[key] = obj[key];
|
|
731
|
+
}
|
|
732
|
+
return out;
|
|
733
|
+
}
|
|
734
|
+
function extractFromErrorEvent(e) {
|
|
735
|
+
return stripUndefined({
|
|
736
|
+
sdkMethod: "uncaught",
|
|
737
|
+
type: "javascript-error",
|
|
738
|
+
filename: sanitizeFilename(e.filename),
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
function extractFromRejectionEvent(_e) {
|
|
742
|
+
return stripUndefined({
|
|
743
|
+
sdkMethod: "uncaught",
|
|
744
|
+
type: "unhandled-rejection",
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
// Non-PII by construction: all EmbeddingErrorParams fields are sanitized.
|
|
748
|
+
function installErrorListeners(bridge) {
|
|
749
|
+
if (errorListenersAttached)
|
|
750
|
+
return;
|
|
751
|
+
errorListenersAttached = true;
|
|
752
|
+
errorHandler = (e) => bridge.reportError(extractFromErrorEvent(e));
|
|
753
|
+
rejectionHandler = (e) => bridge.reportError(extractFromRejectionEvent());
|
|
754
|
+
window.addEventListener("error", errorHandler);
|
|
755
|
+
window.addEventListener("unhandledrejection", rejectionHandler);
|
|
756
|
+
}
|
|
757
|
+
function uninstallErrorListeners() {
|
|
758
|
+
if (!errorListenersAttached)
|
|
759
|
+
return;
|
|
760
|
+
if (errorHandler) {
|
|
761
|
+
window.removeEventListener("error", errorHandler);
|
|
762
|
+
errorHandler = null;
|
|
763
|
+
}
|
|
764
|
+
if (rejectionHandler) {
|
|
765
|
+
window.removeEventListener("unhandledrejection", rejectionHandler);
|
|
766
|
+
rejectionHandler = null;
|
|
767
|
+
}
|
|
768
|
+
errorListenersAttached = false;
|
|
769
|
+
errorEmitCount = 0;
|
|
770
|
+
}
|
|
660
771
|
/** Single-attempt per iframe; recovery from failure requires a host-driven remount. */
|
|
661
772
|
function bootstrapSession(options = {}) {
|
|
662
773
|
if (sessionPromise)
|
|
@@ -672,6 +783,7 @@ function bootstrapSession(options = {}) {
|
|
|
672
783
|
rejectOnAbort(options.signal, abortCleanup.signal),
|
|
673
784
|
]);
|
|
674
785
|
bridge.sendInitializedNotification();
|
|
786
|
+
installErrorListeners(bridge);
|
|
675
787
|
const handle = {
|
|
676
788
|
bridge,
|
|
677
789
|
hostMetaData: capture.hostMetaData,
|
|
@@ -688,7 +800,12 @@ function bootstrapSession(options = {}) {
|
|
|
688
800
|
return handle;
|
|
689
801
|
}
|
|
690
802
|
catch (err) {
|
|
691
|
-
// Abort or unexpected failure after construction: dispose so the port doesn't leak.
|
|
803
|
+
// Abort or unexpected failure after construction: report error, then dispose so the port doesn't leak.
|
|
804
|
+
const reason = err instanceof SessionFailureError && err.reason === "ABORTED";
|
|
805
|
+
bridge.reportError({
|
|
806
|
+
sdkMethod: "bootstrapSession",
|
|
807
|
+
type: reason ? "session-aborted" : "bootstrap-failed",
|
|
808
|
+
});
|
|
692
809
|
bridge.dispose();
|
|
693
810
|
throw err;
|
|
694
811
|
}
|
|
@@ -711,6 +828,10 @@ function bootstrapSession(options = {}) {
|
|
|
711
828
|
return sessionPromise;
|
|
712
829
|
}
|
|
713
830
|
|
|
831
|
+
/*
|
|
832
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
833
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
834
|
+
*/
|
|
714
835
|
// Bridge-side error vocabulary: composes the standard JSON-RPC + transport
|
|
715
836
|
// codes from `@salesforce/jsonrpc` with the sf-embedding-specific
|
|
716
837
|
// codes from `@salesforce/sf-embedding-contract`, and wraps `makeJsonRpcError`
|
|
@@ -732,6 +853,7 @@ exports.BOOTSTRAP_ENVELOPE_TYPE = BOOTSTRAP_ENVELOPE_TYPE;
|
|
|
732
853
|
exports.BootstrapFailureError = BootstrapFailureError;
|
|
733
854
|
exports.BridgeClient = BridgeClient;
|
|
734
855
|
exports.EMBEDDING_INITIALIZED_METHOD = EMBEDDING_INITIALIZED_METHOD;
|
|
856
|
+
exports.ERROR_METHOD = ERROR_METHOD;
|
|
735
857
|
exports.EVENTS_DISPATCH_METHOD = EVENTS_DISPATCH_METHOD;
|
|
736
858
|
exports.EVENTS_SUBSCRIBE_METHOD = EVENTS_SUBSCRIBE_METHOD;
|
|
737
859
|
exports.EVENTS_UNSUBSCRIBE_METHOD = EVENTS_UNSUBSCRIBE_METHOD;
|
package/dist/index.d.ts
CHANGED
|
@@ -153,6 +153,17 @@ interface HostInitializedParams {
|
|
|
153
153
|
interface EmbeddingInitializedParams {
|
|
154
154
|
embeddingInfo: PeerInfo;
|
|
155
155
|
}
|
|
156
|
+
/** Iframe-side error notification. Fire-and-forget, non-PII observability signal. */
|
|
157
|
+
declare const ERROR_METHOD: "ui/notifications/error";
|
|
158
|
+
/** Params for `ui/notifications/error`. */
|
|
159
|
+
interface EmbeddingErrorParams {
|
|
160
|
+
/** Method the error belongs to. "uncaught" when captured by global listeners (origin unknown); concrete method name when reported deliberately. */
|
|
161
|
+
sdkMethod: string;
|
|
162
|
+
/** Error category. Automatic capture: "javascript-error" / "unhandled-rejection". Deliberate reports: any freeform string. */
|
|
163
|
+
type: string;
|
|
164
|
+
/** Sanitized pathname only (no origin/query/hash). */
|
|
165
|
+
filename?: string;
|
|
166
|
+
}
|
|
156
167
|
|
|
157
168
|
declare const RESIZE_METHOD: "ui/notifications/resize";
|
|
158
169
|
interface ResizeParams {
|
|
@@ -698,7 +709,15 @@ declare class EmbeddingBridge extends JsonRpcRouter {
|
|
|
698
709
|
sendResize(dimensions: ResizeParams): void;
|
|
699
710
|
/** Attach an auto-emit resizer; stopped on dispose. Replaces any prior resizer. */
|
|
700
711
|
attachResizer(resizer: EmbeddingResizer): void;
|
|
712
|
+
/** Send outbound RPC request; on rejection, report error to host then rethrow. */
|
|
713
|
+
send<TParams = unknown, TResult = unknown>(method: string, params: TParams): Promise<TResult>;
|
|
701
714
|
dispose(): void;
|
|
715
|
+
/**
|
|
716
|
+
* Report an error to the host for observability. Non-PII by construction.
|
|
717
|
+
* Two usage modes: (1) automatic uncaught-error capture (sdkMethod="uncaught"); (2) deliberate
|
|
718
|
+
* author reporting from try/catch (concrete sdkMethod name passed by the author).
|
|
719
|
+
*/
|
|
720
|
+
reportError(params: EmbeddingErrorParams): void;
|
|
702
721
|
}
|
|
703
722
|
|
|
704
723
|
/** Single-attempt per iframe; recovery from failure requires a host-driven remount. */
|
|
@@ -740,5 +759,5 @@ declare function makeJsonRpcError(code: number, options?: {
|
|
|
740
759
|
*/
|
|
741
760
|
declare function readHostMetaData(search: string): HostMetaData | null;
|
|
742
761
|
|
|
743
|
-
export { BOOTSTRAP_ENVELOPE_TYPE, BootstrapFailureError, BridgeClient, EMBEDDING_INITIALIZED_METHOD, EVENTS_DISPATCH_METHOD, EVENTS_SUBSCRIBE_METHOD, EVENTS_UNSUBSCRIBE_METHOD, EmbeddingBridge, ErrorCode, HEARTBEAT_TYPE, HOST_INITIALIZED_METHOD, HOST_META_DATA_PARAM, HostLwcEvent, JsonRpcHandlerError, JsonRpcRouter, NON_RETRYABLE_CODES, PROTOCOL_NAME, RESIZE_METHOD, SHUTDOWN_TYPE, SessionFailureError, SfEmbeddingErrorCode, UI_STATE_CHANGED_METHOD, UI_STATE_SUBSCRIBE_METHOD, UI_STATE_UNSUBSCRIBE_METHOD, bootstrapSession, isBootstrapEnvelope, makeJsonRpcError, readHostMetaData, validateBootstrapEnvelope };
|
|
744
|
-
export type { BootstrapEnvelope, BootstrapEnvelopeValidationFailure, BootstrapEnvelopeValidationInput, BootstrapEnvelopeValidationResult, BootstrapFailureReason, BootstrapSessionOptions, EmbeddingInitializedParams, ErrorCodeValue, EventsDispatchParams, EventsSubscribeParams, EventsSubscribeResult, EventsUnsubscribeParams, HeartbeatEnvelope, HostErrorEventDetail, HostInitializedParams, HostInitializedPayload, HostLwcEventName, HostMetaData, HostReadyEventDetail, HostStyles, NotificationHandler, PeerInfo, RequestHandler, ResizeParams, SessionFailureReason, SessionHandle, SfEmbeddingErrorCodeValue, ShutdownEnvelope, UiState, UiStateChangedParams, UiStateSubscribeParams, UiStateSubscribeResult, UiStateUnsubscribeParams, Unregister };
|
|
762
|
+
export { BOOTSTRAP_ENVELOPE_TYPE, BootstrapFailureError, BridgeClient, EMBEDDING_INITIALIZED_METHOD, ERROR_METHOD, EVENTS_DISPATCH_METHOD, EVENTS_SUBSCRIBE_METHOD, EVENTS_UNSUBSCRIBE_METHOD, EmbeddingBridge, ErrorCode, HEARTBEAT_TYPE, HOST_INITIALIZED_METHOD, HOST_META_DATA_PARAM, HostLwcEvent, JsonRpcHandlerError, JsonRpcRouter, NON_RETRYABLE_CODES, PROTOCOL_NAME, RESIZE_METHOD, SHUTDOWN_TYPE, SessionFailureError, SfEmbeddingErrorCode, UI_STATE_CHANGED_METHOD, UI_STATE_SUBSCRIBE_METHOD, UI_STATE_UNSUBSCRIBE_METHOD, bootstrapSession, isBootstrapEnvelope, makeJsonRpcError, readHostMetaData, validateBootstrapEnvelope };
|
|
763
|
+
export type { BootstrapEnvelope, BootstrapEnvelopeValidationFailure, BootstrapEnvelopeValidationInput, BootstrapEnvelopeValidationResult, BootstrapFailureReason, BootstrapSessionOptions, EmbeddingErrorParams, EmbeddingInitializedParams, ErrorCodeValue, EventsDispatchParams, EventsSubscribeParams, EventsSubscribeResult, EventsUnsubscribeParams, HeartbeatEnvelope, HostErrorEventDetail, HostInitializedParams, HostInitializedPayload, HostLwcEventName, HostMetaData, HostReadyEventDetail, HostStyles, NotificationHandler, PeerInfo, RequestHandler, ResizeParams, SessionFailureReason, SessionHandle, SfEmbeddingErrorCodeValue, ShutdownEnvelope, UiState, UiStateChangedParams, UiStateSubscribeParams, UiStateSubscribeResult, UiStateUnsubscribeParams, Unregister };
|
package/dist/index.esm.js
CHANGED
|
@@ -88,6 +88,8 @@ class JsonRpcHandlerError extends Error {
|
|
|
88
88
|
const HOST_INITIALIZED_METHOD = "ui/notifications/host-initialized";
|
|
89
89
|
/** Embedding acknowledges receipt of `host-initialized`. Fire-and-forget. */
|
|
90
90
|
const EMBEDDING_INITIALIZED_METHOD = "ui/notifications/embedding-initialized";
|
|
91
|
+
/** Iframe-side error notification. Fire-and-forget, non-PII observability signal. */
|
|
92
|
+
const ERROR_METHOD = "ui/notifications/error";
|
|
91
93
|
|
|
92
94
|
// Resize hint channel: embedding → host, fire-and-forget. CSS pixels.
|
|
93
95
|
const RESIZE_METHOD = "ui/notifications/resize";
|
|
@@ -97,6 +99,10 @@ const UI_STATE_SUBSCRIBE_METHOD = "ui/subscribe/ui-state";
|
|
|
97
99
|
const UI_STATE_UNSUBSCRIBE_METHOD = "ui/unsubscribe/ui-state";
|
|
98
100
|
const UI_STATE_CHANGED_METHOD = "ui/notifications/ui-state-changed";
|
|
99
101
|
|
|
102
|
+
/*
|
|
103
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
104
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
105
|
+
*/
|
|
100
106
|
// Bootstrap envelope validator for the iframe (embedding) side of the
|
|
101
107
|
// sf-embedding handshake.
|
|
102
108
|
//
|
|
@@ -140,6 +146,10 @@ function validateBootstrapEnvelope(input) {
|
|
|
140
146
|
return { ok: true, envelope: input.data, port: input.ports[0] };
|
|
141
147
|
}
|
|
142
148
|
|
|
149
|
+
/*
|
|
150
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
151
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
152
|
+
*/
|
|
143
153
|
/**
|
|
144
154
|
* Parses the JSON-encoded `hostMetaData` query parameter from an iframe URL's
|
|
145
155
|
* search string (per ADR 0029). Returns null on any malformed input — caller
|
|
@@ -185,6 +195,10 @@ function readHostMetaData(search) {
|
|
|
185
195
|
return { instanceId, hostAppOrigin };
|
|
186
196
|
}
|
|
187
197
|
|
|
198
|
+
/*
|
|
199
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
200
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
201
|
+
*/
|
|
188
202
|
const BRIDGE_PROTOCOL_VERSION = "1.0.0";
|
|
189
203
|
class BootstrapFailureError extends Error {
|
|
190
204
|
reason;
|
|
@@ -548,12 +562,20 @@ class JsonRpcRouter {
|
|
|
548
562
|
}
|
|
549
563
|
}
|
|
550
564
|
|
|
565
|
+
/*
|
|
566
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
567
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
568
|
+
*/
|
|
551
569
|
/** Identity emitted on `ui/notifications/embedding-initialized`. */
|
|
552
570
|
const EMBEDDING_INFO = {
|
|
553
571
|
name: "@salesforce/sf-embedding-bridge",
|
|
554
|
-
version: "2.2.
|
|
572
|
+
version: "2.2.5-rc.1",
|
|
555
573
|
};
|
|
556
574
|
|
|
575
|
+
/*
|
|
576
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
577
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
578
|
+
*/
|
|
557
579
|
class SessionFailureError extends Error {
|
|
558
580
|
reason;
|
|
559
581
|
constructor(reason, cause) {
|
|
@@ -635,11 +657,35 @@ class EmbeddingBridge extends JsonRpcRouter {
|
|
|
635
657
|
this.resizer.stop();
|
|
636
658
|
this.resizer = resizer;
|
|
637
659
|
}
|
|
660
|
+
/** Send outbound RPC request; on rejection, report error to host then rethrow. */
|
|
661
|
+
send(method, params) {
|
|
662
|
+
return super.send(method, params).catch((err) => {
|
|
663
|
+
this.reportError({ sdkMethod: method, type: "rpc-failed" });
|
|
664
|
+
throw err;
|
|
665
|
+
});
|
|
666
|
+
}
|
|
638
667
|
dispose() {
|
|
639
668
|
this.resizer?.stop();
|
|
640
669
|
this.resizer = null;
|
|
670
|
+
uninstallErrorListeners();
|
|
641
671
|
super.dispose();
|
|
642
672
|
}
|
|
673
|
+
/**
|
|
674
|
+
* Report an error to the host for observability. Non-PII by construction.
|
|
675
|
+
* Two usage modes: (1) automatic uncaught-error capture (sdkMethod="uncaught"); (2) deliberate
|
|
676
|
+
* author reporting from try/catch (concrete sdkMethod name passed by the author).
|
|
677
|
+
*/
|
|
678
|
+
reportError(params) {
|
|
679
|
+
if (errorEmitCount >= ERROR_MAX_PER_SESSION)
|
|
680
|
+
return;
|
|
681
|
+
errorEmitCount++;
|
|
682
|
+
try {
|
|
683
|
+
this.notify(ERROR_METHOD, params);
|
|
684
|
+
}
|
|
685
|
+
catch {
|
|
686
|
+
// Rethrow would refire window.onerror and spiral.
|
|
687
|
+
}
|
|
688
|
+
}
|
|
643
689
|
}
|
|
644
690
|
// `cleanup` lets the caller drop our listener from the consumer-owned signal once the race resolves.
|
|
645
691
|
function rejectOnAbort(signal, cleanup) {
|
|
@@ -655,6 +701,71 @@ function rejectOnAbort(signal, cleanup) {
|
|
|
655
701
|
});
|
|
656
702
|
}
|
|
657
703
|
let sessionPromise = null;
|
|
704
|
+
// Safe as module state: sessionPromise singleton ensures one bridge per realm; listeners are realm-global.
|
|
705
|
+
let errorListenersAttached = false;
|
|
706
|
+
let errorEmitCount = 0;
|
|
707
|
+
let errorHandler = null;
|
|
708
|
+
let rejectionHandler = null;
|
|
709
|
+
const ERROR_MAX_PER_SESSION = 50;
|
|
710
|
+
function sanitizeFilename(filename) {
|
|
711
|
+
if (!filename)
|
|
712
|
+
return undefined;
|
|
713
|
+
// Blob/data URLs: scheme marker only (unparseable for pathname).
|
|
714
|
+
if (filename.startsWith("blob:") || filename.startsWith("data:")) {
|
|
715
|
+
return filename.split(":")[0] + ":<sanitized>";
|
|
716
|
+
}
|
|
717
|
+
try {
|
|
718
|
+
return new URL(filename).pathname;
|
|
719
|
+
}
|
|
720
|
+
catch {
|
|
721
|
+
return undefined;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
function stripUndefined(obj) {
|
|
725
|
+
const out = {};
|
|
726
|
+
for (const key of Object.keys(obj)) {
|
|
727
|
+
if (obj[key] !== undefined)
|
|
728
|
+
out[key] = obj[key];
|
|
729
|
+
}
|
|
730
|
+
return out;
|
|
731
|
+
}
|
|
732
|
+
function extractFromErrorEvent(e) {
|
|
733
|
+
return stripUndefined({
|
|
734
|
+
sdkMethod: "uncaught",
|
|
735
|
+
type: "javascript-error",
|
|
736
|
+
filename: sanitizeFilename(e.filename),
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
function extractFromRejectionEvent(_e) {
|
|
740
|
+
return stripUndefined({
|
|
741
|
+
sdkMethod: "uncaught",
|
|
742
|
+
type: "unhandled-rejection",
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
// Non-PII by construction: all EmbeddingErrorParams fields are sanitized.
|
|
746
|
+
function installErrorListeners(bridge) {
|
|
747
|
+
if (errorListenersAttached)
|
|
748
|
+
return;
|
|
749
|
+
errorListenersAttached = true;
|
|
750
|
+
errorHandler = (e) => bridge.reportError(extractFromErrorEvent(e));
|
|
751
|
+
rejectionHandler = (e) => bridge.reportError(extractFromRejectionEvent());
|
|
752
|
+
window.addEventListener("error", errorHandler);
|
|
753
|
+
window.addEventListener("unhandledrejection", rejectionHandler);
|
|
754
|
+
}
|
|
755
|
+
function uninstallErrorListeners() {
|
|
756
|
+
if (!errorListenersAttached)
|
|
757
|
+
return;
|
|
758
|
+
if (errorHandler) {
|
|
759
|
+
window.removeEventListener("error", errorHandler);
|
|
760
|
+
errorHandler = null;
|
|
761
|
+
}
|
|
762
|
+
if (rejectionHandler) {
|
|
763
|
+
window.removeEventListener("unhandledrejection", rejectionHandler);
|
|
764
|
+
rejectionHandler = null;
|
|
765
|
+
}
|
|
766
|
+
errorListenersAttached = false;
|
|
767
|
+
errorEmitCount = 0;
|
|
768
|
+
}
|
|
658
769
|
/** Single-attempt per iframe; recovery from failure requires a host-driven remount. */
|
|
659
770
|
function bootstrapSession(options = {}) {
|
|
660
771
|
if (sessionPromise)
|
|
@@ -670,6 +781,7 @@ function bootstrapSession(options = {}) {
|
|
|
670
781
|
rejectOnAbort(options.signal, abortCleanup.signal),
|
|
671
782
|
]);
|
|
672
783
|
bridge.sendInitializedNotification();
|
|
784
|
+
installErrorListeners(bridge);
|
|
673
785
|
const handle = {
|
|
674
786
|
bridge,
|
|
675
787
|
hostMetaData: capture.hostMetaData,
|
|
@@ -686,7 +798,12 @@ function bootstrapSession(options = {}) {
|
|
|
686
798
|
return handle;
|
|
687
799
|
}
|
|
688
800
|
catch (err) {
|
|
689
|
-
// Abort or unexpected failure after construction: dispose so the port doesn't leak.
|
|
801
|
+
// Abort or unexpected failure after construction: report error, then dispose so the port doesn't leak.
|
|
802
|
+
const reason = err instanceof SessionFailureError && err.reason === "ABORTED";
|
|
803
|
+
bridge.reportError({
|
|
804
|
+
sdkMethod: "bootstrapSession",
|
|
805
|
+
type: reason ? "session-aborted" : "bootstrap-failed",
|
|
806
|
+
});
|
|
690
807
|
bridge.dispose();
|
|
691
808
|
throw err;
|
|
692
809
|
}
|
|
@@ -709,6 +826,10 @@ function bootstrapSession(options = {}) {
|
|
|
709
826
|
return sessionPromise;
|
|
710
827
|
}
|
|
711
828
|
|
|
829
|
+
/*
|
|
830
|
+
* Copyright 2026 Salesforce, Inc. All rights reserved.
|
|
831
|
+
* For full license text, see the LICENSE.txt file in this package.
|
|
832
|
+
*/
|
|
712
833
|
// Bridge-side error vocabulary: composes the standard JSON-RPC + transport
|
|
713
834
|
// codes from `@salesforce/jsonrpc` with the sf-embedding-specific
|
|
714
835
|
// codes from `@salesforce/sf-embedding-contract`, and wraps `makeJsonRpcError`
|
|
@@ -726,5 +847,5 @@ function makeJsonRpcError(code, options = {}) {
|
|
|
726
847
|
return makeJsonRpcError$1(code, options);
|
|
727
848
|
}
|
|
728
849
|
|
|
729
|
-
export { BOOTSTRAP_ENVELOPE_TYPE, BootstrapFailureError, BridgeClient, EMBEDDING_INITIALIZED_METHOD, EVENTS_DISPATCH_METHOD, EVENTS_SUBSCRIBE_METHOD, EVENTS_UNSUBSCRIBE_METHOD, EmbeddingBridge, ErrorCode, HEARTBEAT_TYPE, HOST_INITIALIZED_METHOD, HOST_META_DATA_PARAM, HostLwcEvent, JsonRpcHandlerError, JsonRpcRouter, NON_RETRYABLE_CODES, PROTOCOL_NAME, RESIZE_METHOD, SHUTDOWN_TYPE, SessionFailureError, SfEmbeddingErrorCode, UI_STATE_CHANGED_METHOD, UI_STATE_SUBSCRIBE_METHOD, UI_STATE_UNSUBSCRIBE_METHOD, bootstrapSession, isBootstrapEnvelope, makeJsonRpcError, readHostMetaData, validateBootstrapEnvelope };
|
|
850
|
+
export { BOOTSTRAP_ENVELOPE_TYPE, BootstrapFailureError, BridgeClient, EMBEDDING_INITIALIZED_METHOD, ERROR_METHOD, EVENTS_DISPATCH_METHOD, EVENTS_SUBSCRIBE_METHOD, EVENTS_UNSUBSCRIBE_METHOD, EmbeddingBridge, ErrorCode, HEARTBEAT_TYPE, HOST_INITIALIZED_METHOD, HOST_META_DATA_PARAM, HostLwcEvent, JsonRpcHandlerError, JsonRpcRouter, NON_RETRYABLE_CODES, PROTOCOL_NAME, RESIZE_METHOD, SHUTDOWN_TYPE, SessionFailureError, SfEmbeddingErrorCode, UI_STATE_CHANGED_METHOD, UI_STATE_SUBSCRIBE_METHOD, UI_STATE_UNSUBSCRIBE_METHOD, bootstrapSession, isBootstrapEnvelope, makeJsonRpcError, readHostMetaData, validateBootstrapEnvelope };
|
|
730
851
|
//# sourceMappingURL=index.esm.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/sf-embedding-bridge",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.5-rc.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "JSON-RPC 2.0 bridge for the sf-embedding host ↔ embedding protocol",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"@salesforce/jsonrpc": "^10.12.3"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@salesforce/sf-embedding-contract": "2.2.
|
|
28
|
-
"utils": "2.2.
|
|
27
|
+
"@salesforce/sf-embedding-contract": "2.2.5-rc.1",
|
|
28
|
+
"utils": "2.2.5-rc.1"
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
31
31
|
"dist/",
|