@ada-cx/messaging-bridge 1.0.0-setup.0 → 1.0.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.
package/README.md CHANGED
@@ -2,18 +2,26 @@
2
2
 
3
3
  Build your own chat UI (a "custom app") on Ada Messaging's bridge contract.
4
4
 
5
- This package carries TypeScript types, test mocks, and a thin loader. The
6
- bridge runtime always loads from Ada's CDN at
7
- `https://messaging-assets.ada.support/bridge.js`. Ada patches the runtime
8
- continuously, so your installed package version never pins security logic.
5
+ This package carries TypeScript types, test mocks, and a thin loader.
6
+
7
+ Ada core stamps its build identifier into your app frame's `window.name`. The loader uses it to load the matching bridge runtime.
8
+
9
+ Your installed package never contains the runtime. Ada can update runtime security logic through a coordinated core release.
9
10
 
10
11
  ## Requirements
11
12
 
12
13
  - Your custom app runs inside an iframe that Ada's core frame mounts. The
13
14
  core frame is served from Ada's asset host, and it sits inside the page
14
15
  that embeds the widget. Both origins are in your app's ancestor chain.
15
- - You point the Web SDK at your app with its `appUrl` setting. Custom apps
16
- are experimental. Contact your Ada team before you build on this feature.
16
+ - You point the Web SDK at your app with its `appUrl` setting.
17
+ - Your app's origin must be in your handle's **Allowed websites** list. Add
18
+ it in your Ada dashboard, under **Channels** > **Chat**. Until the list
19
+ allows your origin, a configured `appUrl` is dropped with a console
20
+ warning and Ada's default app mounts (with `appUrlFallback: false` the
21
+ activation fails instead). An entry that carries a path, query, or
22
+ fragment does not authorize a custom app. Add the bare origin as its own
23
+ entry. Local development needs no entry when both your app and the
24
+ embedding page run on loopback hosts.
17
25
  - If your app's responses send no `X-Frame-Options` header and no CSP
18
26
  `frame-ancestors` directive, browsers permit framing, and no server change
19
27
  is needed. If your app restricts framing, `frame-ancestors` must allow
@@ -155,64 +163,47 @@ anchors.
155
163
 
156
164
  | Option | Default | Purpose |
157
165
  | --- | --- | --- |
158
- | `cdnBase` | `https://messaging-assets.ada.support` | Asset origin for `bridge.js`. Must be an `https` URL. Plain `http` works only for loopback hosts such as `localhost` during local development. Override only for staging validation. |
159
- | `pinBuildSha` | none | Full 40-character git SHA of a deployed CDN build. Loads that build's immutable copy instead of the current root asset. Not recommended for production. See [Pin the CDN build](#pin-the-cdn-build). |
166
+ | `cdnBase` | `https://messaging-assets.ada.support` | Asset origin for immutable bridge builds. It must use HTTPS. Plain HTTP works only for loopback development. Override it only when your Ada team directs you to during a joint validation. |
167
+
168
+ ### Frame-only asset URL resolution
169
+
170
+ `resolveBridgeCdnUrl()` returns the bridge asset URL selected for the current custom app frame.
171
+ It uses the same validation as `loadMessagingBridge()`.
172
+
173
+ This helper is frame-only. It throws a `MessagingBridgeLoadError` outside an Ada-mounted custom app frame.
174
+ Call it after your app starts inside that frame. Do not evaluate it at module scope in code that also runs elsewhere.
160
175
 
161
176
  Errors: `loadMessagingBridge` rejects with a `MessagingBridgeLoadError`. Its
162
177
  `code` property identifies the failure:
163
178
 
164
179
  | Code | Meaning |
165
180
  | --- | --- |
166
- | `invalid_cdn_base` | The `cdnBase` option is not a valid `https` URL. |
167
- | `invalid_build_sha` | The `pinBuildSha` option is not a full 40-character hex git SHA. |
168
- | `unstamped_build_sha` | The `pinBuildSha` option is the unstamped placeholder from a repository build. |
181
+ | `invalid_cdn_base` | The `cdnBase` option violates the HTTPS or loopback HTTP policy. |
182
+ | `missing_build_sha` | The frame's `window.name` carries no core build marker, or the mounting core predates build-marker delivery. |
183
+ | `invalid_build_sha` | The frame-name marker matches no accepted build identifier form. |
184
+ | `dev_marker_not_loopback` | The frame uses the `dev` marker with a non-loopback asset host. |
169
185
  | `bridge_import_failed` | The asset failed to load (network, CSP, 404). |
170
186
  | `bridge_module_invalid` | The loaded module is not an Ada bridge build. |
171
187
 
172
- ### Pin the CDN build
188
+ ### Matching the core build
173
189
 
174
- The package exports `CDN_BUILD_SHA`. The value is the git commit SHA of the
175
- monorepo commit this npm version was published from. Ada deploys each
176
- commit's bridge runtime as an immutable SHA-rooted copy on the CDN. The
177
- value therefore names the CDN build associated with this npm version.
190
+ Ada core mounts your frame with `window.name` set to `ada-custom-app:<build identifier>` before your app code runs. Your URL and its fragment mount untouched, so hash routes keep working.
178
191
 
179
- Pass the SHA as the `pinBuildSha` loader option. The loader then skips the
180
- root `bridge.js` asset and imports that build's immutable copy,
181
- `<cdnBase>/<sha>/bridge/bridge.js`, directly:
192
+ | Marker | Meaning | Resolved asset |
193
+ | --- | --- | --- |
194
+ | A bare 40-character hexadecimal SHA | An immutable production build. | `<cdnBase>/<sha>/bridge/bridge.js` |
195
+ | Lowercase `pr-<N>` | An Ada-internal preview build. | `<cdnBase>/pr-<N>/bridge/bridge.js` |
196
+ | `dev` | An Ada-internal local core build. Customer pages never receive this marker. | `<cdnBase>/bridge/bridge.js` on loopback only. |
182
197
 
183
- ```ts
184
- import {
185
- CDN_BUILD_SHA,
186
- isCdnBuildShaStamped,
187
- loadMessagingBridge,
188
- } from "@ada-cx/messaging-bridge";
189
-
190
- if (isCdnBuildShaStamped()) {
191
- await loadMessagingBridge({ pinBuildSha: CDN_BUILD_SHA });
192
- }
193
- ```
198
+ The loader accepts only these marker shapes. It never accepts a URL or path from the frame name.
194
199
 
195
- **Pinning is not recommended for production.** A pinned runtime misses Ada's
196
- fixes and the loader-fence rollout. A pinned runtime can also predate later
197
- core or server contract changes and stop working. Use a pin only to debug an
198
- issue, to validate a staged build, or to reproduce a report against a known
199
- runtime.
200
+ The loader snapshots `window.name` when your app first evaluates the package, through either entry: `@ada-cx/messaging-bridge` or `@ada-cx/messaging-bridge/react`. Keep that import in a module your page evaluates at startup. A route-level code split evaluates too late. Code that runs first, for example a library that uses `window.name`, could overwrite the name. If the route that renders `AdaBridgeProvider` is lazily loaded, add a bare `import "@ada-cx/messaging-bridge";` to your entry module. The loader keeps the snapshot value if code overwrites `window.name` later.
200
201
 
201
- The option accepts any full 40-character hex git SHA of a deployed main
202
- build. The loader rejects any other value with the `invalid_build_sha` error
203
- code. Only published packages carry a real SHA. In the repository, and in a
204
- locally built copy, `CDN_BUILD_SHA` is a 40-zero placeholder and
205
- `isCdnBuildShaStamped()` returns `false`. The loader rejects the placeholder
206
- with the `unstamped_build_sha` error code.
202
+ Do not persist or replay the frame-name marker. Core supplies the authoritative value for every frame load.
207
203
 
208
- The npm publish and the CDN deploy of the same commit run in parallel. In
209
- the first minutes after a release, a pin can fail with
210
- `bridge_import_failed` until the deploy completes. If the deploy of that
211
- commit failed, the pinned build never exists. The unpinned default is
212
- unaffected in both cases.
204
+ Do not set or change `window.name`. The loader fails closed when your page runs outside an Ada-mounted custom app frame.
213
205
 
214
- `pinBuildSha` composes with `cdnBase`. The pinned copy resolves under the
215
- asset host you pass.
206
+ The `dev` marker and PR preview aliases are Ada-internal workflows. Local Ada core builds stamp `ada-custom-app:dev`, which works only with a loopback `cdnBase`. An Ada preview core selects its matching preview bridge automatically. You do not need a `cdnBase` override for a joint validation when the page loads `sdk.js` from the production asset root. A preview alias against an asset host that lacks the preview fails closed.
216
207
 
217
208
  ## React
218
209
 
@@ -241,6 +232,13 @@ function MyChatUi() {
241
232
  }
242
233
  ```
243
234
 
235
+ If your app lazily loads the route that renders `AdaBridgeProvider`, add a
236
+ bare `import "@ada-cx/messaging-bridge";` to your entry module. The loader
237
+ then snapshots the frame name at startup, before other code can overwrite
238
+ `window.name`. A statically imported `createBridgeProvider` already evaluates
239
+ the loader through a used binding; the bare import matters only when that
240
+ import itself lives in a lazily loaded chunk.
241
+
244
242
  The provider accepts two more props for load failures. `errorFallback`
245
243
  renders when the runtime fails to load. `onError` receives the
246
244
  `MessagingBridgeLoadError`. Without them, the provider logs the error and
@@ -312,8 +310,9 @@ await handle.settled();
312
310
  pinned to your first document, so your app must not navigate or reload its
313
311
  own frame. A navigation disconnects the bridge permanently.
314
312
  - The runtime detects the custom-app frame by the frame name core sets
315
- (`ada-custom-app`), or by an opaque origin under older core builds. Do not
316
- change `window.name` inside your app document.
313
+ (`ada-custom-app:<build identifier>`), which also selects the bridge
314
+ build the loader imports. Do not change `window.name` inside your app
315
+ document.
317
316
  - The custom-app frame keeps your real origin, so your own cookies and
318
317
  storage work inside it. Browsers partition third-party storage by the
319
318
  embedding site. Cookies need `Partitioned; Secure; SameSite=None`.
@@ -50,11 +50,11 @@ export interface BridgeClient {
50
50
  /**
51
51
  * Create a {@link BridgeClient} bound to the parent core frame.
52
52
  *
53
- * Declaration only (D21 dependency inversion): the implementation lives in
54
- * the private `packages/bridge-runtime` workspace and ships exclusively as
55
- * the CDN asset `bridge.js` — obtain it through `loadMessagingBridge()`.
56
- * The runtime compiles against this signature (`MessagingBridgeModule` is
57
- * pinned both ways by bridge-runtime's `cdn-contract.ts`), so the published
53
+ * Declaration only: the implementation lives in this package's private
54
+ * runtime source and ships exclusively as the CDN asset `bridge.js`.
55
+ * Obtain it through `loadMessagingBridge()`. The runtime compiles against
56
+ * this signature (`MessagingBridgeModule` is pinned both ways by
57
+ * `runtime/cdn-contract.ts`), so the published
58
58
  * type can never drift from the CDN implementation.
59
59
  *
60
60
  * Security model: the core origin is derived from `document.referrer` when
@@ -5,11 +5,10 @@ import type { AppDisplayState, Message } from "./types.js";
5
5
  * so a custom app does not have to rediscover the edge cases the hard way.
6
6
  * No DOM, no client: every helper is a pure function of state or messages.
7
7
  *
8
- * Declarations only (D21 dependency inversion): the implementations live in
9
- * the private `packages/bridge-runtime` workspace and ship exclusively on
10
- * the CDN runtime, typed on `MessagingBridgeModule`. The runtime compiles
11
- * against these signatures (pinned both ways by bridge-runtime's
12
- * `cdn-contract.ts`), so the published types cannot drift from the CDN
8
+ * Declarations only: the implementations live in `src/runtime` and ship
9
+ * exclusively on the CDN runtime, typed on `MessagingBridgeModule`. The
10
+ * runtime compiles against these signatures (pinned both ways by
11
+ * `runtime/cdn-contract.ts`), so the published types cannot drift from the CDN
13
12
  * implementation.
14
13
  */
15
14
  /**
@@ -257,7 +257,15 @@ export interface BridgeOperations {
257
257
  * that predates the seq, falls back to the `pending -> terminal` edge.
258
258
  * The email is trimmed; an empty result rejects immediately without
259
259
  * sending (core drops an empty email silently, so nothing would ever
260
- * settle it).
260
+ * settle it). The request is single-flight and address-aware: a call for
261
+ * the SAME address as an in-flight request joins it and settles on its
262
+ * verdict; a call for a DIFFERENT address is rejected address-matched on
263
+ * `transcript.email.conflict.*` — ONLY the conflicting call's promise
264
+ * rejects, while the in-flight request still completes, writes the final
265
+ * status, and settles its own handle with the true outcome (a rejection
266
+ * here never means the in-flight email was not sent). On a core document
267
+ * that predates the conflict channel, a conflicting call settles on the
268
+ * in-flight verdict instead.
261
269
  */
262
270
  emailTranscript(email: string, opts?: SettleOptions): Promise<void>;
263
271
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Typed constants for all bridge state keys.
2
+ * Typed contract constants for all bridge state keys.
3
3
  *
4
4
  * Using these constants prevents silent typos — a misspelled string literal
5
5
  * compiles without error but returns `undefined` at runtime.
@@ -1,6 +1,6 @@
1
- import type { CsatFeedbackOption, CsatSurveySettings, FileUploadErrorData } from "./shared-utils/index.js";
1
+ import type { CsatFeedbackOption, CsatSurveySettings, FileUploadErrorData } from "../shared-utils/index.js";
2
2
  /**
3
- * Bridge type definitions describing the state and events that flow between
3
+ * Bridge contract definitions describing the state and events that flow between
4
4
  * the messaging core frame and the display layer (app or native WebView).
5
5
  *
6
6
  * These types are the source of truth for the `@ada-cx/messaging-bridge`
@@ -374,6 +374,16 @@ export interface AppDisplayState {
374
374
  * edge never appears — correlate on this advancing to a terminal status
375
375
  * instead. Optional: absent on a core document that predates it. */
376
376
  "transcript.email.status.seq"?: number;
377
+ /** The trimmed address of the most recently rejected CONFLICTING email
378
+ * request — an `emailTranscript` for a DIFFERENT address while one was in
379
+ * flight. Its own channel: `emailTranscript` rejects on this advancing
380
+ * with ITS address, so only the conflicting caller rejects while the
381
+ * in-flight request settles on its own status. Optional: absent on a core
382
+ * document that predates it (there a conflicting caller settles on the
383
+ * in-flight verdict instead). */
384
+ "transcript.email.conflict.email"?: string | null;
385
+ /** Advances on every `transcript.email.conflict.email` write. */
386
+ "transcript.email.conflict.seq"?: number;
377
387
  /** Same lifecycle as `transcript.email.status`, for
378
388
  * `settings.transcript.download.request`. */
379
389
  "transcript.download.status": "idle" | "pending" | "success" | "error";
@@ -0,0 +1,197 @@
1
+ //#region \0@oxc-project+runtime@0.122.0/helpers/typeof.js
2
+ function _typeof(o) {
3
+ "@babel/helpers - typeof";
4
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
5
+ return typeof o;
6
+ } : function(o) {
7
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
8
+ }, _typeof(o);
9
+ }
10
+ //#endregion
11
+ //#region \0@oxc-project+runtime@0.122.0/helpers/toPrimitive.js
12
+ function toPrimitive(t, r) {
13
+ if ("object" != _typeof(t) || !t) return t;
14
+ var e = t[Symbol.toPrimitive];
15
+ if (void 0 !== e) {
16
+ var i = e.call(t, r || "default");
17
+ if ("object" != _typeof(i)) return i;
18
+ throw new TypeError("@@toPrimitive must return a primitive value.");
19
+ }
20
+ return ("string" === r ? String : Number)(t);
21
+ }
22
+ //#endregion
23
+ //#region \0@oxc-project+runtime@0.122.0/helpers/toPropertyKey.js
24
+ function toPropertyKey(t) {
25
+ var i = toPrimitive(t, "string");
26
+ return "symbol" == _typeof(i) ? i : i + "";
27
+ }
28
+ //#endregion
29
+ //#region \0@oxc-project+runtime@0.122.0/helpers/defineProperty.js
30
+ function _defineProperty(e, r, t) {
31
+ return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
32
+ value: t,
33
+ enumerable: !0,
34
+ configurable: !0,
35
+ writable: !0
36
+ }) : e[r] = t, e;
37
+ }
38
+ //#endregion
39
+ //#region src/npm/loader.ts
40
+ var DEFAULT_CDN_BASE = "https://messaging-assets.ada.support";
41
+ var CUSTOM_APP_FRAME_NAME = "ada-custom-app";
42
+ var BRIDGE_ENTRY_PATH = "bridge/bridge.js";
43
+ var DEV_BUILD_MARKER = "dev";
44
+ var PINNED_BUILD_SHA_PATTERN = /^[0-9a-f]{40}$/i;
45
+ var PR_BUILD_ALIAS_PATTERN = /^pr-\d+$/;
46
+ /** Typed failure raised by {@link loadMessagingBridge}. */
47
+ var MessagingBridgeLoadError = class extends Error {
48
+ constructor(message, code, cause) {
49
+ super(message);
50
+ _defineProperty(this, "code", void 0);
51
+ this.name = "MessagingBridgeLoadError";
52
+ this.code = code;
53
+ if (cause !== void 0) this.cause = cause;
54
+ }
55
+ };
56
+ var defaultImportModule = (url) => import(
57
+ /* webpackIgnore: true */
58
+ /* @vite-ignore */
59
+ url
60
+ );
61
+ var inFlightByImporter = /* @__PURE__ */ new WeakMap();
62
+ var LOOPBACK_HOSTS = new Set([
63
+ "localhost",
64
+ "127.0.0.1",
65
+ "[::1]",
66
+ "::1"
67
+ ]);
68
+ function isLoopbackHostname(hostname) {
69
+ const normalized = hostname.trim().toLowerCase();
70
+ return LOOPBACK_HOSTS.has(normalized) || normalized.endsWith(".localhost");
71
+ }
72
+ function trimTrailingSlashes(value) {
73
+ let end = value.length;
74
+ while (end > 0 && value[end - 1] === "/") end -= 1;
75
+ return value.slice(0, end);
76
+ }
77
+ function bridgeFrameOnlyMessage(message) {
78
+ return `${message} The bridge only runs inside an Ada-mounted custom-app frame.`;
79
+ }
80
+ var FRAME_NAME_MARKER_PREFIX = `${CUSTOM_APP_FRAME_NAME}:`;
81
+ function frameNameCarriesBuild(name) {
82
+ return name.startsWith(FRAME_NAME_MARKER_PREFIX);
83
+ }
84
+ /**
85
+ * Read the build marker core stamps into the frame's `window.name`
86
+ * (`ada-custom-app:<marker>`). The name is Ada-owned for this frame, so the
87
+ * marker never touches customer-owned URL state such as a hash route.
88
+ */
89
+ function resolveBridgeBuildFromFrameName(name) {
90
+ if (name === CUSTOM_APP_FRAME_NAME) throw new MessagingBridgeLoadError("The mounting Ada core predates build-marker delivery, so it cannot select a versioned bridge. The Ada core rollout must include the marker-stamping core before the npm bridge can load.", "missing_build_sha");
91
+ if (!frameNameCarriesBuild(name)) throw new MessagingBridgeLoadError(`${bridgeFrameOnlyMessage("This document was not mounted by an Ada core frame: window.name carries no ada-custom-app build marker.")} If this frame is Ada-mounted, make sure @ada-cx/messaging-bridge (or its /react entry) is imported from a module your page evaluates at startup — a lazily loaded chunk evaluates after customer code could overwrite window.name.`, "missing_build_sha");
92
+ const value = name.slice(FRAME_NAME_MARKER_PREFIX.length);
93
+ if (value === DEV_BUILD_MARKER) return { kind: "dev" };
94
+ if (PR_BUILD_ALIAS_PATTERN.test(value)) return {
95
+ kind: "alias",
96
+ identifier: value
97
+ };
98
+ if (!PINNED_BUILD_SHA_PATTERN.test(value)) throw new MessagingBridgeLoadError(bridgeFrameOnlyMessage(`The window.name build marker must be exactly a bare 40-character hex git SHA or a lowercase pr-<N> alias. URLs and paths are not allowed: "${value}".`), "invalid_build_sha");
99
+ return {
100
+ kind: "sha",
101
+ identifier: value.toLowerCase()
102
+ };
103
+ }
104
+ var initialFrameName = typeof window === "undefined" ? "" : window.name;
105
+ var initialFrameNameHasBuild = frameNameCarriesBuild(initialFrameName);
106
+ function currentFrameName() {
107
+ if (initialFrameNameHasBuild) return initialFrameName;
108
+ return typeof window === "undefined" ? "" : window.name;
109
+ }
110
+ function resolveCdnBase(cdnBase) {
111
+ const base = trimTrailingSlashes((cdnBase ?? DEFAULT_CDN_BASE).trim());
112
+ let url;
113
+ try {
114
+ url = new URL(base);
115
+ } catch (cause) {
116
+ throw new MessagingBridgeLoadError(`The cdnBase option is not a valid URL: "${base}".`, "invalid_cdn_base", cause);
117
+ }
118
+ const isHttps = url.protocol === "https:";
119
+ const isLoopbackHttp = url.protocol === "http:" && isLoopbackHostname(url.hostname);
120
+ if (!isHttps && !isLoopbackHttp) throw new MessagingBridgeLoadError(`The cdnBase option must use https (plain http is allowed only for loopback hosts): "${base}".`, "invalid_cdn_base");
121
+ return {
122
+ base,
123
+ hostname: url.hostname
124
+ };
125
+ }
126
+ /**
127
+ * Resolve the bridge asset URL from this frame's name marker and the CDN
128
+ * base. Production builds always resolve the immutable SHA-rooted asset.
129
+ * PR aliases stay under the configured CDN base. A production-base alias that
130
+ * does not exist fails closed through the normal bridge import error.
131
+ * Local development resolves `/bridge/bridge.js` only on a loopback base.
132
+ */
133
+ function resolveBridgeCdnUrl(options) {
134
+ const build = resolveBridgeBuildFromFrameName(currentFrameName());
135
+ const { base, hostname } = resolveCdnBase(options?.cdnBase);
136
+ if (build.kind === "dev" && !isLoopbackHostname(hostname)) throw new MessagingBridgeLoadError(bridgeFrameOnlyMessage(`The ${FRAME_NAME_MARKER_PREFIX}${DEV_BUILD_MARKER} frame-name marker requires a loopback cdnBase.`), "dev_marker_not_loopback");
137
+ const assetPath = build.kind === "dev" ? `/${BRIDGE_ENTRY_PATH}` : `/${build.identifier}/${BRIDGE_ENTRY_PATH}`;
138
+ return new URL(`${base}${assetPath}`).toString();
139
+ }
140
+ var REQUIRED_BRIDGE_FUNCTION_EXPORTS = [
141
+ "createBridgeClient",
142
+ "messageKey",
143
+ "isHistoricalRow",
144
+ "findFirstUnread",
145
+ "selectUnread",
146
+ "groupMessages",
147
+ "filterDisplayable",
148
+ "resolveBotName",
149
+ "isAgentTyping",
150
+ "isConnectivityLost"
151
+ ];
152
+ function isMessagingBridgeModule(value) {
153
+ if (typeof value !== "object" || value === null) return false;
154
+ const candidate = value;
155
+ return candidate.STATE != null && REQUIRED_BRIDGE_FUNCTION_EXPORTS.every((key) => typeof candidate[key] === "function");
156
+ }
157
+ async function importBridgeModule(url, importModule) {
158
+ let loaded;
159
+ try {
160
+ loaded = await importModule(url);
161
+ } catch (cause) {
162
+ throw new MessagingBridgeLoadError(`The Ada bridge runtime failed to load from ${url}.`, "bridge_import_failed", cause);
163
+ }
164
+ if (!isMessagingBridgeModule(loaded)) throw new MessagingBridgeLoadError(`The module at ${url} does not expose the full Ada bridge surface; it is not a complete Ada bridge build.`, "bridge_module_invalid");
165
+ return loaded;
166
+ }
167
+ /**
168
+ * Load the bridge runtime that matches the mounting core frame. Loads are
169
+ * memoized per resolved URL and importer. A failed load is evicted for retry.
170
+ */
171
+ function loadMessagingBridge(options = {}) {
172
+ const importModule = options.importModule ?? defaultImportModule;
173
+ let url;
174
+ try {
175
+ url = resolveBridgeCdnUrl(options);
176
+ } catch (error) {
177
+ return Promise.reject(error);
178
+ }
179
+ let inFlight = inFlightByImporter.get(importModule);
180
+ if (!inFlight) {
181
+ inFlight = /* @__PURE__ */ new Map();
182
+ inFlightByImporter.set(importModule, inFlight);
183
+ }
184
+ const existing = inFlight.get(url);
185
+ if (existing) return existing;
186
+ const cache = inFlight;
187
+ const pending = importBridgeModule(url, importModule);
188
+ cache.set(url, pending);
189
+ pending.catch(() => {
190
+ if (cache.get(url) === pending) cache.delete(url);
191
+ });
192
+ return pending;
193
+ }
194
+ //#endregion
195
+ export { loadMessagingBridge as n, resolveBridgeCdnUrl as r, MessagingBridgeLoadError as t };
196
+
197
+ //# sourceMappingURL=loader-CfCnvaTU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader-CfCnvaTU.js","names":[],"sources":["../src/npm/loader.ts"],"sourcesContent":["import type { createBridgeClient } from \"../contract/bridge-client\";\nimport type {\n\tfilterDisplayable,\n\tfindFirstUnread,\n\tgroupMessages,\n\tisAgentTyping,\n\tisConnectivityLost,\n\tisHistoricalRow,\n\tmessageKey,\n\tresolveBotName,\n\tselectUnread,\n} from \"../contract/derive\";\nimport type { STATE } from \"../contract/state-keys\";\n\nconst DEFAULT_CDN_BASE = \"https://messaging-assets.ada.support\";\n// Keep aligned with packages/core/src/app-bridge.ts; pinned by\n// scripts/core-html-contract.test.mjs.\nconst CUSTOM_APP_FRAME_NAME = \"ada-custom-app\";\nconst BRIDGE_ENTRY_PATH = \"bridge/bridge.js\";\nconst DEV_BUILD_MARKER = \"dev\";\n\n// Only canonical build identifiers may become part of an executable asset URL.\nconst PINNED_BUILD_SHA_PATTERN = /^[0-9a-f]{40}$/i;\n// Deliberately narrower than scripts/sdk.js parseVersionOverride (7-40 hex,\n// case-insensitive): deployed keys are always lowercase `pr-<N>`, and a short\n// SHA never names an uploaded tree, so the narrower grammar loses no reachable\n// build while keeping the executable-path grammar minimal.\nconst PR_BUILD_ALIAS_PATTERN = /^pr-\\d+$/;\n\n/** Why a {@link MessagingBridgeLoadError} was raised. */\nexport type MessagingBridgeLoadErrorCode =\n\t/** The `cdnBase` option violates the HTTPS or loopback HTTP policy. */\n\t| \"invalid_cdn_base\"\n\t/** The frame's `window.name` carries no core build marker. */\n\t| \"missing_build_sha\"\n\t/** The frame-name marker is not a supported bare build identifier. */\n\t| \"invalid_build_sha\"\n\t/** The `dev` marker was paired with a non-loopback CDN base. */\n\t| \"dev_marker_not_loopback\"\n\t/** The dynamic import of the CDN asset failed. */\n\t| \"bridge_import_failed\"\n\t/** The imported module does not expose the Ada bridge surface. */\n\t| \"bridge_module_invalid\";\n\n/** Typed failure raised by {@link loadMessagingBridge}. */\nexport class MessagingBridgeLoadError extends Error {\n\treadonly code: MessagingBridgeLoadErrorCode;\n\n\tconstructor(\n\t\tmessage: string,\n\t\tcode: MessagingBridgeLoadErrorCode,\n\t\tcause?: unknown,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"MessagingBridgeLoadError\";\n\t\tthis.code = code;\n\t\tif (cause !== undefined) {\n\t\t\tthis.cause = cause;\n\t\t}\n\t}\n}\n\n/** The module shape served by the SHA-matched bridge CDN asset. */\nexport interface MessagingBridgeModule {\n\tcreateBridgeClient: typeof createBridgeClient;\n\tSTATE: typeof STATE;\n\tmessageKey: typeof messageKey;\n\tisHistoricalRow: typeof isHistoricalRow;\n\tfindFirstUnread: typeof findFirstUnread;\n\tselectUnread: typeof selectUnread;\n\tgroupMessages: typeof groupMessages;\n\tfilterDisplayable: typeof filterDisplayable;\n\tresolveBotName: typeof resolveBotName;\n\tisAgentTyping: typeof isAgentTyping;\n\tisConnectivityLost: typeof isConnectivityLost;\n}\n\ntype ImportBridgeModule = (url: string) => Promise<unknown>;\n\n/** Options for {@link loadMessagingBridge}. */\nexport interface LoadMessagingBridgeOptions {\n\t/**\n\t * Asset base for staged validation. It must use HTTPS. Plain HTTP is\n\t * accepted only for loopback hosts.\n\t */\n\tcdnBase?: string;\n\t/** Import seam for unit tests. */\n\timportModule?: ImportBridgeModule;\n}\n\n// Both annotations are load-bearing. Customer bundlers must leave the\n// browser-resolved CDN URL untouched.\nconst defaultImportModule: ImportBridgeModule = (url) =>\n\timport(/* webpackIgnore: true */ /* @vite-ignore */ url);\n\nconst inFlightByImporter = new WeakMap<\n\tImportBridgeModule,\n\tMap<string, Promise<MessagingBridgeModule>>\n>();\n\nconst LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"[::1]\", \"::1\"]);\n\nfunction isLoopbackHostname(hostname: string): boolean {\n\tconst normalized = hostname.trim().toLowerCase();\n\treturn LOOPBACK_HOSTS.has(normalized) || normalized.endsWith(\".localhost\");\n}\n\nfunction trimTrailingSlashes(value: string): string {\n\tlet end = value.length;\n\twhile (end > 0 && value[end - 1] === \"/\") {\n\t\tend -= 1;\n\t}\n\treturn value.slice(0, end);\n}\n\ntype BridgeBuild =\n\t| { kind: \"sha\"; identifier: string }\n\t| { kind: \"alias\"; identifier: string }\n\t| { kind: \"dev\" };\n\nfunction bridgeFrameOnlyMessage(message: string): string {\n\treturn `${message} The bridge only runs inside an Ada-mounted custom-app frame.`;\n}\n\nconst FRAME_NAME_MARKER_PREFIX = `${CUSTOM_APP_FRAME_NAME}:`;\n\nfunction frameNameCarriesBuild(name: string): boolean {\n\treturn name.startsWith(FRAME_NAME_MARKER_PREFIX);\n}\n\n/**\n * Read the build marker core stamps into the frame's `window.name`\n * (`ada-custom-app:<marker>`). The name is Ada-owned for this frame, so the\n * marker never touches customer-owned URL state such as a hash route.\n */\nexport function resolveBridgeBuildFromFrameName(name: string): BridgeBuild {\n\tif (name === CUSTOM_APP_FRAME_NAME) {\n\t\tthrow new MessagingBridgeLoadError(\n\t\t\t\"The mounting Ada core predates build-marker delivery, so it cannot select a versioned bridge. The Ada core rollout must include the marker-stamping core before the npm bridge can load.\",\n\t\t\t\"missing_build_sha\",\n\t\t);\n\t}\n\tif (!frameNameCarriesBuild(name)) {\n\t\tthrow new MessagingBridgeLoadError(\n\t\t\t`${bridgeFrameOnlyMessage(\n\t\t\t\t\"This document was not mounted by an Ada core frame: window.name carries no ada-custom-app build marker.\",\n\t\t\t)} If this frame is Ada-mounted, make sure @ada-cx/messaging-bridge (or its /react entry) is imported from a module your page evaluates at startup — a lazily loaded chunk evaluates after customer code could overwrite window.name.`,\n\t\t\t\"missing_build_sha\",\n\t\t);\n\t}\n\tconst value = name.slice(FRAME_NAME_MARKER_PREFIX.length);\n\tif (value === DEV_BUILD_MARKER) {\n\t\treturn { kind: \"dev\" };\n\t}\n\tif (PR_BUILD_ALIAS_PATTERN.test(value)) {\n\t\treturn { kind: \"alias\", identifier: value };\n\t}\n\tif (!PINNED_BUILD_SHA_PATTERN.test(value)) {\n\t\tthrow new MessagingBridgeLoadError(\n\t\t\tbridgeFrameOnlyMessage(\n\t\t\t\t`The window.name build marker must be exactly a bare 40-character hex git SHA or a lowercase pr-<N> alias. URLs and paths are not allowed: \"${value}\".`,\n\t\t\t),\n\t\t\t\"invalid_build_sha\",\n\t\t);\n\t}\n\treturn { kind: \"sha\", identifier: value.toLowerCase() };\n}\n\n// Latch window.name at module evaluation: core stamps the marker on the\n// frame element before the document loads, and customer code — for example a\n// library that uses window.name as a storage channel — could overwrite it\n// after boot. The latch therefore only holds when this package is imported\n// from the page's entry module rather than a lazily loaded chunk — the\n// boundary README_NPM.md documents and the snapshot tests in loader.test.ts\n// pin.\nconst initialFrameName = typeof window === \"undefined\" ? \"\" : window.name;\nconst initialFrameNameHasBuild = frameNameCarriesBuild(initialFrameName);\n\nfunction currentFrameName(): string {\n\tif (initialFrameNameHasBuild) {\n\t\treturn initialFrameName;\n\t}\n\treturn typeof window === \"undefined\" ? \"\" : window.name;\n}\n\nfunction resolveCdnBase(cdnBase: string | undefined): {\n\tbase: string;\n\thostname: string;\n} {\n\tconst base = trimTrailingSlashes((cdnBase ?? DEFAULT_CDN_BASE).trim());\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(base);\n\t} catch (cause) {\n\t\tthrow new MessagingBridgeLoadError(\n\t\t\t`The cdnBase option is not a valid URL: \"${base}\".`,\n\t\t\t\"invalid_cdn_base\",\n\t\t\tcause,\n\t\t);\n\t}\n\tconst isHttps = url.protocol === \"https:\";\n\tconst isLoopbackHttp =\n\t\turl.protocol === \"http:\" && isLoopbackHostname(url.hostname);\n\tif (!isHttps && !isLoopbackHttp) {\n\t\tthrow new MessagingBridgeLoadError(\n\t\t\t`The cdnBase option must use https (plain http is allowed only for loopback hosts): \"${base}\".`,\n\t\t\t\"invalid_cdn_base\",\n\t\t);\n\t}\n\treturn { base, hostname: url.hostname };\n}\n\n/**\n * Resolve the bridge asset URL from this frame's name marker and the CDN\n * base. Production builds always resolve the immutable SHA-rooted asset.\n * PR aliases stay under the configured CDN base. A production-base alias that\n * does not exist fails closed through the normal bridge import error.\n * Local development resolves `/bridge/bridge.js` only on a loopback base.\n */\nexport function resolveBridgeCdnUrl(\n\toptions?: Pick<LoadMessagingBridgeOptions, \"cdnBase\">,\n): string {\n\tconst build = resolveBridgeBuildFromFrameName(currentFrameName());\n\tconst { base, hostname } = resolveCdnBase(options?.cdnBase);\n\tif (build.kind === \"dev\" && !isLoopbackHostname(hostname)) {\n\t\tthrow new MessagingBridgeLoadError(\n\t\t\tbridgeFrameOnlyMessage(\n\t\t\t\t`The ${FRAME_NAME_MARKER_PREFIX}${DEV_BUILD_MARKER} frame-name marker requires a loopback cdnBase.`,\n\t\t\t),\n\t\t\t\"dev_marker_not_loopback\",\n\t\t);\n\t}\n\tconst assetPath =\n\t\tbuild.kind === \"dev\"\n\t\t\t? `/${BRIDGE_ENTRY_PATH}`\n\t\t\t: `/${build.identifier}/${BRIDGE_ENTRY_PATH}`;\n\treturn new URL(`${base}${assetPath}`).toString();\n}\n\n// A partial CDN module (mid-deploy, or a rolled-back build missing a newer\n// export) must fail here as a typed bridge_module_invalid error, not later as\n// a TypeError on the first missing call — so every export is checked. The\n// `satisfies` clause rejects names that are not interface members; the\n// exhaustiveness type below is what forces this list to grow with it.\nconst REQUIRED_BRIDGE_FUNCTION_EXPORTS = [\n\t\"createBridgeClient\",\n\t\"messageKey\",\n\t\"isHistoricalRow\",\n\t\"findFirstUnread\",\n\t\"selectUnread\",\n\t\"groupMessages\",\n\t\"filterDisplayable\",\n\t\"resolveBotName\",\n\t\"isAgentTyping\",\n\t\"isConnectivityLost\",\n] as const satisfies readonly (keyof MessagingBridgeModule)[];\n\ntype MissingRequiredBridgeExport = Exclude<\n\tkeyof MessagingBridgeModule,\n\t(typeof REQUIRED_BRIDGE_FUNCTION_EXPORTS)[number] | \"STATE\"\n>;\n// Compile-time exhaustiveness: adding a MessagingBridgeModule member without\n// listing it in REQUIRED_BRIDGE_FUNCTION_EXPORTS (or checking it explicitly,\n// like STATE) makes the Exclude non-`never`, so the `true` assignment below\n// stops compiling.\ntype AllBridgeExportsListed = MissingRequiredBridgeExport extends never\n\t? true\n\t: never;\nconst _allBridgeExportsListed: AllBridgeExportsListed = true;\nvoid _allBridgeExportsListed;\n\nfunction isMessagingBridgeModule(\n\tvalue: unknown,\n): value is MessagingBridgeModule {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\tconst candidate = value as Record<string, unknown>;\n\t// STATE is a constant object, not a function, so it gets a presence check.\n\treturn (\n\t\tcandidate.STATE != null &&\n\t\tREQUIRED_BRIDGE_FUNCTION_EXPORTS.every(\n\t\t\t(key) => typeof candidate[key] === \"function\",\n\t\t)\n\t);\n}\n\nasync function importBridgeModule(\n\turl: string,\n\timportModule: ImportBridgeModule,\n): Promise<MessagingBridgeModule> {\n\tlet loaded: unknown;\n\ttry {\n\t\tloaded = await importModule(url);\n\t} catch (cause) {\n\t\tthrow new MessagingBridgeLoadError(\n\t\t\t`The Ada bridge runtime failed to load from ${url}.`,\n\t\t\t\"bridge_import_failed\",\n\t\t\tcause,\n\t\t);\n\t}\n\tif (!isMessagingBridgeModule(loaded)) {\n\t\tthrow new MessagingBridgeLoadError(\n\t\t\t`The module at ${url} does not expose the full Ada bridge surface; it is not a complete Ada bridge build.`,\n\t\t\t\"bridge_module_invalid\",\n\t\t);\n\t}\n\treturn loaded;\n}\n\n/**\n * Load the bridge runtime that matches the mounting core frame. Loads are\n * memoized per resolved URL and importer. A failed load is evicted for retry.\n */\nexport function loadMessagingBridge(\n\toptions: LoadMessagingBridgeOptions = {},\n): Promise<MessagingBridgeModule> {\n\tconst importModule = options.importModule ?? defaultImportModule;\n\tlet url: string;\n\ttry {\n\t\turl = resolveBridgeCdnUrl(options);\n\t} catch (error) {\n\t\treturn Promise.reject(error);\n\t}\n\n\tlet inFlight = inFlightByImporter.get(importModule);\n\tif (!inFlight) {\n\t\tinFlight = new Map();\n\t\tinFlightByImporter.set(importModule, inFlight);\n\t}\n\tconst existing = inFlight.get(url);\n\tif (existing) {\n\t\treturn existing;\n\t}\n\n\tconst cache = inFlight;\n\tconst pending = importBridgeModule(url, importModule);\n\tcache.set(url, pending);\n\tpending.catch(() => {\n\t\tif (cache.get(url) === pending) {\n\t\t\tcache.delete(url);\n\t\t}\n\t});\n\treturn pending;\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import type { BridgeClient } from "./bridge-client.js";
1
+ import type { BridgeClient } from "../contract/bridge-client.js";
2
+ import type { AppDisplayState } from "../contract/types.js";
2
3
  import type { MessagingBridgeModule } from "./loader.js";
3
- import type { AppDisplayState } from "./types.js";
4
4
  export declare const BridgeContext: import("react").Context<BridgeClient | null>;
5
5
  /**
6
6
  * Provide a {@link BridgeClient} to the React tree.
@@ -6,12 +6,11 @@
6
6
  * `loadMessagingBridge()` so Ada can patch security-bearing logic without a
7
7
  * customer release. Never add runtime implementation to this entry.
8
8
  */
9
- export type { FileUploadErrorData, FileUploadErrorReason, } from "./shared-utils/index.js";
10
- export type { BridgeClient } from "./bridge-client.js";
11
- export { CDN_BUILD_SHA, isCdnBuildShaStamped } from "./build-info.js";
12
- export type { MessageGroupingEntry, UnreadSelection } from "./derive.js";
9
+ export type { FileUploadErrorData, FileUploadErrorReason, } from "../shared-utils/index.js";
10
+ export type { BridgeClient } from "../contract/bridge-client.js";
11
+ export type { MessageGroupingEntry, UnreadSelection } from "../contract/derive.js";
12
+ export type { BridgeClientDestroyedError, BridgeOperations, BridgeOperationsHost, CaptureHandle, CsatSubmitHandle, EndChatEligibilityResult, FileUploadHandle, ObserveOptions, OperationResult, SendHandle, SettleOptions, } from "../contract/operations.js";
13
+ export type { StateKey } from "../contract/state-keys.js";
14
+ export type { AppDisplayState, AppEvents, CaptureData, CsatMessage, CsatScaleType, DownloadableMessage, FallbackUiConfig, LinkMessage, ListSelectionData, LiveChatStateData, Message, NotificationPermissionMessage, OptionItem, OptionsMessage, PictureMessage, PresenceMessage, QueueBotBlockMessage, QuickRepliesMessage, QuickReply, SdkPlatform, SeamlessOAuthMessage, SelectableListItem, SelectableListMessage, SignInMessage, TextMessage, VideoMessage, WidgetMessage, } from "../contract/types.js";
13
15
  export type { LoadMessagingBridgeOptions, MessagingBridgeLoadErrorCode, MessagingBridgeModule, } from "./loader.js";
14
16
  export { loadMessagingBridge, MessagingBridgeLoadError, resolveBridgeCdnUrl, } from "./loader.js";
15
- export type { BridgeClientDestroyedError, BridgeOperations, BridgeOperationsHost, CaptureHandle, CsatSubmitHandle, EndChatEligibilityResult, FileUploadHandle, ObserveOptions, OperationResult, SendHandle, SettleOptions, } from "./operations.js";
16
- export type { StateKey } from "./state-keys.js";
17
- export type { AppDisplayState, AppEvents, CaptureData, CsatMessage, CsatScaleType, DownloadableMessage, FallbackUiConfig, LinkMessage, ListSelectionData, LiveChatStateData, Message, NotificationPermissionMessage, OptionItem, OptionsMessage, PictureMessage, PresenceMessage, QueueBotBlockMessage, QuickRepliesMessage, QuickReply, SdkPlatform, SeamlessOAuthMessage, SelectableListItem, SelectableListMessage, SignInMessage, TextMessage, VideoMessage, WidgetMessage, } from "./types.js";
@@ -0,0 +1,2 @@
1
+ import { n as loadMessagingBridge, r as resolveBridgeCdnUrl, t as MessagingBridgeLoadError } from "../loader-CfCnvaTU.js";
2
+ export { MessagingBridgeLoadError, loadMessagingBridge, resolveBridgeCdnUrl };
@@ -0,0 +1,76 @@
1
+ import type { createBridgeClient } from "../contract/bridge-client.js";
2
+ import type { filterDisplayable, findFirstUnread, groupMessages, isAgentTyping, isConnectivityLost, isHistoricalRow, messageKey, resolveBotName, selectUnread } from "../contract/derive.js";
3
+ import type { STATE } from "../contract/state-keys.js";
4
+ /** Why a {@link MessagingBridgeLoadError} was raised. */
5
+ export type MessagingBridgeLoadErrorCode =
6
+ /** The `cdnBase` option violates the HTTPS or loopback HTTP policy. */
7
+ "invalid_cdn_base"
8
+ /** The frame's `window.name` carries no core build marker. */
9
+ | "missing_build_sha"
10
+ /** The frame-name marker is not a supported bare build identifier. */
11
+ | "invalid_build_sha"
12
+ /** The `dev` marker was paired with a non-loopback CDN base. */
13
+ | "dev_marker_not_loopback"
14
+ /** The dynamic import of the CDN asset failed. */
15
+ | "bridge_import_failed"
16
+ /** The imported module does not expose the Ada bridge surface. */
17
+ | "bridge_module_invalid";
18
+ /** Typed failure raised by {@link loadMessagingBridge}. */
19
+ export declare class MessagingBridgeLoadError extends Error {
20
+ readonly code: MessagingBridgeLoadErrorCode;
21
+ constructor(message: string, code: MessagingBridgeLoadErrorCode, cause?: unknown);
22
+ }
23
+ /** The module shape served by the SHA-matched bridge CDN asset. */
24
+ export interface MessagingBridgeModule {
25
+ createBridgeClient: typeof createBridgeClient;
26
+ STATE: typeof STATE;
27
+ messageKey: typeof messageKey;
28
+ isHistoricalRow: typeof isHistoricalRow;
29
+ findFirstUnread: typeof findFirstUnread;
30
+ selectUnread: typeof selectUnread;
31
+ groupMessages: typeof groupMessages;
32
+ filterDisplayable: typeof filterDisplayable;
33
+ resolveBotName: typeof resolveBotName;
34
+ isAgentTyping: typeof isAgentTyping;
35
+ isConnectivityLost: typeof isConnectivityLost;
36
+ }
37
+ type ImportBridgeModule = (url: string) => Promise<unknown>;
38
+ /** Options for {@link loadMessagingBridge}. */
39
+ export interface LoadMessagingBridgeOptions {
40
+ /**
41
+ * Asset base for staged validation. It must use HTTPS. Plain HTTP is
42
+ * accepted only for loopback hosts.
43
+ */
44
+ cdnBase?: string;
45
+ /** Import seam for unit tests. */
46
+ importModule?: ImportBridgeModule;
47
+ }
48
+ type BridgeBuild = {
49
+ kind: "sha";
50
+ identifier: string;
51
+ } | {
52
+ kind: "alias";
53
+ identifier: string;
54
+ } | {
55
+ kind: "dev";
56
+ };
57
+ /**
58
+ * Read the build marker core stamps into the frame's `window.name`
59
+ * (`ada-custom-app:<marker>`). The name is Ada-owned for this frame, so the
60
+ * marker never touches customer-owned URL state such as a hash route.
61
+ */
62
+ export declare function resolveBridgeBuildFromFrameName(name: string): BridgeBuild;
63
+ /**
64
+ * Resolve the bridge asset URL from this frame's name marker and the CDN
65
+ * base. Production builds always resolve the immutable SHA-rooted asset.
66
+ * PR aliases stay under the configured CDN base. A production-base alias that
67
+ * does not exist fails closed through the normal bridge import error.
68
+ * Local development resolves `/bridge/bridge.js` only on a loopback base.
69
+ */
70
+ export declare function resolveBridgeCdnUrl(options?: Pick<LoadMessagingBridgeOptions, "cdnBase">): string;
71
+ /**
72
+ * Load the bridge runtime that matches the mounting core frame. Loads are
73
+ * memoized per resolved URL and importer. A failed load is evicted for retry.
74
+ */
75
+ export declare function loadMessagingBridge(options?: LoadMessagingBridgeOptions): Promise<MessagingBridgeModule>;
76
+ export {};
@@ -5,9 +5,9 @@
5
5
  * created by the CDN-loaded runtime. Never import `createBridgeClient` here —
6
6
  * the security-bearing runtime must not reach the npm tarball.
7
7
  */
8
- export type { BridgeClient } from "./bridge-client.js";
8
+ export type { BridgeClient } from "../contract/bridge-client.js";
9
+ export type { AppDisplayState, AppEvents } from "../contract/types.js";
9
10
  export type { LoadedBridgeProviderProps } from "./bridge-react.js";
10
11
  export { BridgeProvider, createBridgeProvider, useBridgeClient, useBridgeState, useBridgeStateKey, } from "./bridge-react.js";
11
12
  export type { LoadMessagingBridgeOptions, MessagingBridgeLoadErrorCode, MessagingBridgeModule, } from "./loader.js";
12
13
  export { loadMessagingBridge, MessagingBridgeLoadError, resolveBridgeCdnUrl, } from "./loader.js";
13
- export type { AppDisplayState, AppEvents } from "./types.js";
@@ -1,7 +1,7 @@
1
- import { n as loadMessagingBridge, r as resolveBridgeCdnUrl, t as MessagingBridgeLoadError } from "./loader-CVynp73o.js";
1
+ import { n as loadMessagingBridge, r as resolveBridgeCdnUrl, t as MessagingBridgeLoadError } from "../loader-CfCnvaTU.js";
2
2
  import { createContext, useCallback, useContext, useEffect, useRef, useState, useSyncExternalStore } from "react";
3
3
  import { Fragment, jsx } from "react/jsx-runtime";
4
- //#region src/bridge-react.tsx
4
+ //#region src/npm/bridge-react.tsx
5
5
  var BridgeContext = createContext(null);
6
6
  /**
7
7
  * Provide a {@link BridgeClient} to the React tree.
@@ -43,7 +43,7 @@ function useBridgeState() {
43
43
  */
44
44
  function useBridgeStateKey(key) {
45
45
  const client = useBridgeClient();
46
- const subscribe = useCallback((onStoreChange) => typeof client.subscribeKey === "function" ? client.subscribeKey(key, onStoreChange) : client.subscribe(onStoreChange), [client, key]);
46
+ const subscribe = useCallback((onStoreChange) => client.subscribeKey(key, onStoreChange), [client, key]);
47
47
  const getSnapshot = useCallback(() => {
48
48
  const state = client.getState();
49
49
  if (state === null) return null;
@@ -110,4 +110,4 @@ function createBridgeProvider(load = loadMessagingBridge) {
110
110
  //#endregion
111
111
  export { BridgeProvider, MessagingBridgeLoadError, createBridgeProvider, loadMessagingBridge, resolveBridgeCdnUrl, useBridgeClient, useBridgeState, useBridgeStateKey };
112
112
 
113
- //# sourceMappingURL=npm-react.js.map
113
+ //# sourceMappingURL=react.js.map