@thor-commerce/app-bridge-react 0.1.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 ADDED
@@ -0,0 +1,40 @@
1
+ # `@thorcommerce/app-bridge-react`
2
+
3
+ Communication bridge between embedded apps and the Thor Commerce dashboard.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @thorcommerce/app-bridge-react
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import { createAppBridge } from "@thorcommerce/app-bridge-react";
15
+
16
+ const bridge = createAppBridge({
17
+ source: "embedded-app",
18
+ target: "dashboard",
19
+ targetOrigin: "https://dashboard.thorcommerce.com"
20
+ });
21
+
22
+ bridge.send("app:ready", { version: "1.0.0" });
23
+
24
+ const session = await bridge.request<undefined, { shopId: string }>("session:get");
25
+
26
+ bridge.on("navigation:update", (message) => {
27
+ console.log(message.payload);
28
+ });
29
+ ```
30
+
31
+ ## API
32
+
33
+ - `createAppBridge(options)` creates a bridge instance and starts listening for `postMessage` events.
34
+ - `targetOrigin` should be set explicitly on both sides of the bridge. The `"*"` fallback is kept for local development compatibility, but it is unsafe for production.
35
+ - `bridge.send(type, payload)` sends a fire-and-forget event.
36
+ - `bridge.request(type, payload, options)` sends a request and waits for a response.
37
+ - `bridge.on(type, handler)` subscribes to events. Use `"*"` to listen to every incoming message.
38
+ - `bridge.onRequest(type, handler)` registers a request handler that can reply with a value or promise.
39
+ - `bridge.setTargetWindow(window)` updates the destination window after construction.
40
+ - `bridge.destroy()` removes listeners and rejects pending requests.
package/dist/index.cjs ADDED
@@ -0,0 +1,288 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AppBridge: () => AppBridge,
24
+ createAppBridge: () => createAppBridge,
25
+ isBridgeMessage: () => isBridgeMessage
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+ var DEFAULT_NAMESPACE = "thorcommerce:app-bridge";
29
+ var DEFAULT_TIMEOUT_MS = 1e4;
30
+ function createMessageId() {
31
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
32
+ return crypto.randomUUID();
33
+ }
34
+ return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
35
+ }
36
+ function resolveSelfWindow(explicitWindow) {
37
+ if (explicitWindow) {
38
+ return explicitWindow;
39
+ }
40
+ if (typeof window !== "undefined") {
41
+ return window;
42
+ }
43
+ return void 0;
44
+ }
45
+ function resolveTargetWindow(selfWindow, targetWindow) {
46
+ if (targetWindow) {
47
+ return targetWindow;
48
+ }
49
+ if (!selfWindow) {
50
+ return void 0;
51
+ }
52
+ if (selfWindow.parent && selfWindow.parent !== selfWindow) {
53
+ return selfWindow.parent;
54
+ }
55
+ return void 0;
56
+ }
57
+ function isDevelopmentEnvironment() {
58
+ const nodeEnv = typeof globalThis !== "undefined" && "process" in globalThis ? globalThis.process?.env?.NODE_ENV : void 0;
59
+ if (nodeEnv) {
60
+ return nodeEnv !== "production";
61
+ }
62
+ return true;
63
+ }
64
+ function hasAllowedOrigin(origin, allowedOrigins) {
65
+ if (!allowedOrigins || allowedOrigins.length === 0) {
66
+ return true;
67
+ }
68
+ return allowedOrigins.includes(origin);
69
+ }
70
+ function omitUndefinedFields(value) {
71
+ return Object.fromEntries(
72
+ Object.entries(value).filter(([, fieldValue]) => fieldValue !== void 0)
73
+ );
74
+ }
75
+ function isBridgeMessage(value, namespace = DEFAULT_NAMESPACE) {
76
+ if (!value || typeof value !== "object") {
77
+ return false;
78
+ }
79
+ const message = value;
80
+ return message.namespace === namespace && message.version === "1.0" && typeof message.kind === "string" && typeof message.id === "string" && typeof message.type === "string" && typeof message.source === "string";
81
+ }
82
+ var AppBridge = class {
83
+ constructor(options = {}) {
84
+ this.eventHandlers = /* @__PURE__ */ new Map();
85
+ this.requestHandlers = /* @__PURE__ */ new Map();
86
+ this.pendingRequests = /* @__PURE__ */ new Map();
87
+ this.namespace = options.namespace ?? DEFAULT_NAMESPACE;
88
+ this.source = options.source ?? "embedded-app";
89
+ this.target = options.target;
90
+ this.targetOrigin = options.targetOrigin ?? "*";
91
+ this.allowedOrigins = options.allowedOrigins;
92
+ this.defaultTimeoutMs = options.requestTimeoutMs ?? DEFAULT_TIMEOUT_MS;
93
+ this.selfWindow = resolveSelfWindow(options.selfWindow);
94
+ this.targetWindow = resolveTargetWindow(this.selfWindow, options.targetWindow);
95
+ this.messageListener = (event) => {
96
+ this.handleMessage(event);
97
+ };
98
+ if (!this.selfWindow) {
99
+ throw new Error(
100
+ "AppBridge requires a browser window. Pass selfWindow explicitly when constructing it outside global window scope."
101
+ );
102
+ }
103
+ if (this.targetOrigin === "*" && isDevelopmentEnvironment()) {
104
+ console.warn(
105
+ 'AppBridge is using "*" as targetOrigin. Set targetOrigin explicitly for both the dashboard and embedded app in production.'
106
+ );
107
+ }
108
+ this.selfWindow.addEventListener("message", this.messageListener);
109
+ }
110
+ setTargetWindow(targetWindow) {
111
+ this.targetWindow = targetWindow ?? void 0;
112
+ }
113
+ send(type, payload) {
114
+ this.postMessage({
115
+ kind: "event",
116
+ type,
117
+ payload
118
+ });
119
+ }
120
+ request(type, payload, options = {}) {
121
+ const messageId = createMessageId();
122
+ const timeoutMs = options.timeoutMs ?? this.defaultTimeoutMs;
123
+ return new Promise((resolve, reject) => {
124
+ const timeoutId = setTimeout(() => {
125
+ this.pendingRequests.delete(messageId);
126
+ reject(new Error(`Bridge request timed out for "${type}" after ${timeoutMs}ms.`));
127
+ }, timeoutMs);
128
+ this.pendingRequests.set(messageId, {
129
+ resolve: (value) => resolve(value),
130
+ reject,
131
+ timeoutId
132
+ });
133
+ try {
134
+ this.postMessage({
135
+ id: messageId,
136
+ kind: "request",
137
+ type,
138
+ payload
139
+ });
140
+ } catch (error) {
141
+ clearTimeout(timeoutId);
142
+ this.pendingRequests.delete(messageId);
143
+ reject(error instanceof Error ? error : new Error("Failed to send bridge request."));
144
+ }
145
+ });
146
+ }
147
+ on(type, handler) {
148
+ const handlers = this.eventHandlers.get(type) ?? /* @__PURE__ */ new Set();
149
+ handlers.add(handler);
150
+ this.eventHandlers.set(type, handlers);
151
+ return () => {
152
+ handlers.delete(handler);
153
+ if (handlers.size === 0) {
154
+ this.eventHandlers.delete(type);
155
+ }
156
+ };
157
+ }
158
+ onRequest(type, handler) {
159
+ this.requestHandlers.set(type, handler);
160
+ return () => {
161
+ const registeredHandler = this.requestHandlers.get(type);
162
+ if (registeredHandler === handler) {
163
+ this.requestHandlers.delete(type);
164
+ }
165
+ };
166
+ }
167
+ destroy() {
168
+ if (this.selfWindow) {
169
+ this.selfWindow.removeEventListener("message", this.messageListener);
170
+ }
171
+ for (const pendingRequest of this.pendingRequests.values()) {
172
+ clearTimeout(pendingRequest.timeoutId);
173
+ pendingRequest.reject(new Error("AppBridge destroyed before a response was received."));
174
+ }
175
+ this.pendingRequests.clear();
176
+ this.eventHandlers.clear();
177
+ this.requestHandlers.clear();
178
+ }
179
+ handleMessage(event) {
180
+ if (!hasAllowedOrigin(event.origin, this.allowedOrigins)) {
181
+ return;
182
+ }
183
+ if (!isBridgeMessage(event.data, this.namespace)) {
184
+ return;
185
+ }
186
+ const message = event.data;
187
+ if (this.target && message.target && message.target !== this.source) {
188
+ return;
189
+ }
190
+ const receivedMessage = {
191
+ ...message,
192
+ origin: event.origin,
193
+ rawEvent: event
194
+ };
195
+ if (message.kind === "response" && message.replyTo) {
196
+ this.resolvePendingRequest(message.replyTo, message);
197
+ return;
198
+ }
199
+ this.emitHandlers(message.type, receivedMessage);
200
+ if (message.kind === "request") {
201
+ void this.handleRequest(receivedMessage);
202
+ }
203
+ }
204
+ emitHandlers(type, message) {
205
+ const typeHandlers = this.eventHandlers.get(type);
206
+ if (typeHandlers) {
207
+ for (const handler of typeHandlers) {
208
+ handler(message);
209
+ }
210
+ }
211
+ if (type === "*") {
212
+ return;
213
+ }
214
+ const wildcardHandlers = this.eventHandlers.get("*");
215
+ if (wildcardHandlers) {
216
+ for (const handler of wildcardHandlers) {
217
+ handler(message);
218
+ }
219
+ }
220
+ }
221
+ async handleRequest(message) {
222
+ const handler = this.requestHandlers.get(message.type);
223
+ if (!handler) {
224
+ return;
225
+ }
226
+ try {
227
+ const payload = await handler(message.payload, message);
228
+ this.postMessage({
229
+ kind: "response",
230
+ type: message.type,
231
+ payload,
232
+ replyTo: message.id
233
+ });
234
+ } catch (error) {
235
+ const bridgeError = error instanceof Error ? { code: "request_handler_error", message: error.message } : { code: "request_handler_error", message: "Unknown request handler error." };
236
+ this.postMessage({
237
+ kind: "response",
238
+ type: message.type,
239
+ error: bridgeError,
240
+ replyTo: message.id
241
+ });
242
+ }
243
+ }
244
+ resolvePendingRequest(messageId, message) {
245
+ const pendingRequest = this.pendingRequests.get(messageId);
246
+ if (!pendingRequest) {
247
+ return;
248
+ }
249
+ clearTimeout(pendingRequest.timeoutId);
250
+ this.pendingRequests.delete(messageId);
251
+ if (message.error) {
252
+ pendingRequest.reject(new Error(`${message.error.code}: ${message.error.message}`));
253
+ return;
254
+ }
255
+ pendingRequest.resolve(message.payload);
256
+ }
257
+ postMessage(partialMessage) {
258
+ const targetWindow = this.targetWindow;
259
+ if (!targetWindow) {
260
+ throw new Error(
261
+ "AppBridge could not resolve a target window. Pass targetWindow explicitly or call setTargetWindow()."
262
+ );
263
+ }
264
+ const message = omitUndefinedFields({
265
+ namespace: this.namespace,
266
+ version: "1.0",
267
+ id: partialMessage.id ?? createMessageId(),
268
+ kind: partialMessage.kind,
269
+ type: partialMessage.type,
270
+ source: this.source,
271
+ target: partialMessage.target ?? this.target,
272
+ payload: partialMessage.payload,
273
+ replyTo: partialMessage.replyTo,
274
+ error: partialMessage.error
275
+ });
276
+ targetWindow.postMessage(message, this.targetOrigin);
277
+ }
278
+ };
279
+ function createAppBridge(options = {}) {
280
+ return new AppBridge(options);
281
+ }
282
+ // Annotate the CommonJS export names for ESM import in node:
283
+ 0 && (module.exports = {
284
+ AppBridge,
285
+ createAppBridge,
286
+ isBridgeMessage
287
+ });
288
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type BridgeParticipant = \"embedded-app\" | \"dashboard\" | \"unknown\";\n\nexport type BridgeMessageKind = \"event\" | \"request\" | \"response\";\n\nexport interface BridgeErrorPayload {\n code: string;\n message: string;\n}\n\nexport interface BridgeMessage<TPayload = unknown> {\n namespace: string;\n version: \"1.0\";\n kind: BridgeMessageKind;\n id: string;\n type: string;\n source: BridgeParticipant;\n target?: BridgeParticipant;\n payload?: TPayload;\n replyTo?: string;\n error?: BridgeErrorPayload;\n}\n\nexport interface AppBridgeOptions {\n source?: BridgeParticipant;\n target?: BridgeParticipant;\n namespace?: string;\n targetOrigin?: string;\n allowedOrigins?: string[];\n requestTimeoutMs?: number;\n selfWindow?: Window;\n targetWindow?: Window | null;\n}\n\nexport interface RequestOptions {\n timeoutMs?: number;\n}\n\nexport interface ReceivedBridgeMessage<TPayload = unknown> extends BridgeMessage<TPayload> {\n origin: string;\n rawEvent: MessageEvent<unknown>;\n}\n\nexport type BridgeEventHandler<TPayload = unknown> = (\n message: ReceivedBridgeMessage<TPayload>\n) => void;\n\nexport type BridgeRequestHandler<TRequest = unknown, TResponse = unknown> = (\n payload: TRequest,\n message: ReceivedBridgeMessage<TRequest>\n) => TResponse | Promise<TResponse>;\n\nexport type Unsubscribe = () => void;\n\nconst DEFAULT_NAMESPACE = \"thorcommerce:app-bridge\";\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nfunction createMessageId() {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction resolveSelfWindow(explicitWindow?: Window) {\n if (explicitWindow) {\n return explicitWindow;\n }\n\n if (typeof window !== \"undefined\") {\n return window;\n }\n\n return undefined;\n}\n\nfunction resolveTargetWindow(selfWindow: Window | undefined, targetWindow?: Window | null) {\n if (targetWindow) {\n return targetWindow;\n }\n\n if (!selfWindow) {\n return undefined;\n }\n\n if (selfWindow.parent && selfWindow.parent !== selfWindow) {\n return selfWindow.parent;\n }\n\n return undefined;\n}\n\nfunction isDevelopmentEnvironment() {\n const nodeEnv =\n typeof globalThis !== \"undefined\" && \"process\" in globalThis\n ? (\n globalThis as typeof globalThis & {\n process?: { env?: { NODE_ENV?: string } };\n }\n ).process?.env?.NODE_ENV\n : undefined;\n\n if (nodeEnv) {\n return nodeEnv !== \"production\";\n }\n\n return true;\n}\n\nfunction hasAllowedOrigin(origin: string, allowedOrigins?: string[]) {\n if (!allowedOrigins || allowedOrigins.length === 0) {\n return true;\n }\n\n return allowedOrigins.includes(origin);\n}\n\nfunction omitUndefinedFields<T extends object>(value: T): T {\n return Object.fromEntries(\n Object.entries(value).filter(([, fieldValue]) => fieldValue !== undefined)\n ) as T;\n}\n\nexport function isBridgeMessage<TPayload = unknown>(\n value: unknown,\n namespace = DEFAULT_NAMESPACE\n): value is BridgeMessage<TPayload> {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const message = value as Partial<BridgeMessage<TPayload>>;\n\n return (\n message.namespace === namespace &&\n message.version === \"1.0\" &&\n typeof message.kind === \"string\" &&\n typeof message.id === \"string\" &&\n typeof message.type === \"string\" &&\n typeof message.source === \"string\"\n );\n}\n\nexport class AppBridge {\n private readonly namespace: string;\n private readonly source: BridgeParticipant;\n private readonly target: BridgeParticipant | undefined;\n private readonly targetOrigin: string;\n private readonly allowedOrigins?: string[];\n private readonly selfWindow?: Window;\n private readonly defaultTimeoutMs: number;\n private targetWindow?: Window;\n private readonly eventHandlers = new Map<string, Set<BridgeEventHandler>>();\n private readonly requestHandlers = new Map<string, BridgeRequestHandler>();\n private readonly pendingRequests = new Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timeoutId: ReturnType<typeof setTimeout>;\n }\n >();\n private readonly messageListener: (event: MessageEvent<unknown>) => void;\n\n constructor(options: AppBridgeOptions = {}) {\n this.namespace = options.namespace ?? DEFAULT_NAMESPACE;\n this.source = options.source ?? \"embedded-app\";\n this.target = options.target;\n this.targetOrigin = options.targetOrigin ?? \"*\";\n this.allowedOrigins = options.allowedOrigins;\n this.defaultTimeoutMs = options.requestTimeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.selfWindow = resolveSelfWindow(options.selfWindow);\n this.targetWindow = resolveTargetWindow(this.selfWindow, options.targetWindow);\n this.messageListener = (event) => {\n this.handleMessage(event);\n };\n\n if (!this.selfWindow) {\n throw new Error(\n \"AppBridge requires a browser window. Pass selfWindow explicitly when constructing it outside global window scope.\"\n );\n }\n\n if (this.targetOrigin === \"*\" && isDevelopmentEnvironment()) {\n console.warn(\n 'AppBridge is using \"*\" as targetOrigin. Set targetOrigin explicitly for both the dashboard and embedded app in production.'\n );\n }\n\n this.selfWindow.addEventListener(\"message\", this.messageListener);\n }\n\n setTargetWindow(targetWindow: Window | null) {\n this.targetWindow = targetWindow ?? undefined;\n }\n\n send<TPayload = unknown>(type: string, payload?: TPayload) {\n this.postMessage({\n kind: \"event\",\n type,\n payload\n });\n }\n\n request<TRequest = unknown, TResponse = unknown>(\n type: string,\n payload?: TRequest,\n options: RequestOptions = {}\n ) {\n const messageId = createMessageId();\n const timeoutMs = options.timeoutMs ?? this.defaultTimeoutMs;\n\n return new Promise<TResponse>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.pendingRequests.delete(messageId);\n reject(new Error(`Bridge request timed out for \"${type}\" after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.pendingRequests.set(messageId, {\n resolve: (value) => resolve(value as TResponse),\n reject,\n timeoutId\n });\n\n try {\n this.postMessage({\n id: messageId,\n kind: \"request\",\n type,\n payload\n });\n } catch (error) {\n clearTimeout(timeoutId);\n this.pendingRequests.delete(messageId);\n reject(error instanceof Error ? error : new Error(\"Failed to send bridge request.\"));\n }\n });\n }\n\n on<TPayload = unknown>(type: string, handler: BridgeEventHandler<TPayload>): Unsubscribe {\n const handlers = this.eventHandlers.get(type) ?? new Set<BridgeEventHandler>();\n handlers.add(handler as BridgeEventHandler);\n this.eventHandlers.set(type, handlers);\n\n return () => {\n handlers.delete(handler as BridgeEventHandler);\n if (handlers.size === 0) {\n this.eventHandlers.delete(type);\n }\n };\n }\n\n onRequest<TRequest = unknown, TResponse = unknown>(\n type: string,\n handler: BridgeRequestHandler<TRequest, TResponse>\n ): Unsubscribe {\n this.requestHandlers.set(type, handler as BridgeRequestHandler);\n\n return () => {\n const registeredHandler = this.requestHandlers.get(type);\n if (registeredHandler === handler) {\n this.requestHandlers.delete(type);\n }\n };\n }\n\n destroy() {\n if (this.selfWindow) {\n this.selfWindow.removeEventListener(\"message\", this.messageListener);\n }\n\n for (const pendingRequest of this.pendingRequests.values()) {\n clearTimeout(pendingRequest.timeoutId);\n pendingRequest.reject(new Error(\"AppBridge destroyed before a response was received.\"));\n }\n\n this.pendingRequests.clear();\n this.eventHandlers.clear();\n this.requestHandlers.clear();\n }\n\n private handleMessage(event: MessageEvent<unknown>) {\n if (!hasAllowedOrigin(event.origin, this.allowedOrigins)) {\n return;\n }\n\n if (!isBridgeMessage(event.data, this.namespace)) {\n return;\n }\n\n const message = event.data;\n\n if (this.target && message.target && message.target !== this.source) {\n return;\n }\n\n const receivedMessage: ReceivedBridgeMessage = {\n ...message,\n origin: event.origin,\n rawEvent: event\n };\n\n if (message.kind === \"response\" && message.replyTo) {\n this.resolvePendingRequest(message.replyTo, message);\n return;\n }\n\n this.emitHandlers(message.type, receivedMessage);\n\n if (message.kind === \"request\") {\n void this.handleRequest(receivedMessage);\n }\n }\n\n private emitHandlers(type: string, message: ReceivedBridgeMessage) {\n const typeHandlers = this.eventHandlers.get(type);\n if (typeHandlers) {\n for (const handler of typeHandlers) {\n handler(message);\n }\n }\n\n if (type === \"*\") {\n return;\n }\n\n const wildcardHandlers = this.eventHandlers.get(\"*\");\n if (wildcardHandlers) {\n for (const handler of wildcardHandlers) {\n handler(message);\n }\n }\n }\n\n private async handleRequest(message: ReceivedBridgeMessage) {\n const handler = this.requestHandlers.get(message.type);\n if (!handler) {\n return;\n }\n\n try {\n const payload = await handler(message.payload, message);\n this.postMessage({\n kind: \"response\",\n type: message.type,\n payload,\n replyTo: message.id\n });\n } catch (error) {\n const bridgeError =\n error instanceof Error\n ? { code: \"request_handler_error\", message: error.message }\n : { code: \"request_handler_error\", message: \"Unknown request handler error.\" };\n\n this.postMessage({\n kind: \"response\",\n type: message.type,\n error: bridgeError,\n replyTo: message.id\n });\n }\n }\n\n private resolvePendingRequest(messageId: string, message: BridgeMessage) {\n const pendingRequest = this.pendingRequests.get(messageId);\n if (!pendingRequest) {\n return;\n }\n\n clearTimeout(pendingRequest.timeoutId);\n this.pendingRequests.delete(messageId);\n\n if (message.error) {\n pendingRequest.reject(new Error(`${message.error.code}: ${message.error.message}`));\n return;\n }\n\n pendingRequest.resolve(message.payload);\n }\n\n private postMessage<TPayload = unknown>(\n partialMessage: Pick<BridgeMessage<TPayload>, \"kind\" | \"type\"> &\n Partial<Omit<BridgeMessage<TPayload>, \"namespace\" | \"version\" | \"source\">>\n ) {\n const targetWindow = this.targetWindow;\n if (!targetWindow) {\n throw new Error(\n \"AppBridge could not resolve a target window. Pass targetWindow explicitly or call setTargetWindow().\"\n );\n }\n\n const message = omitUndefinedFields({\n namespace: this.namespace,\n version: \"1.0\",\n id: partialMessage.id ?? createMessageId(),\n kind: partialMessage.kind,\n type: partialMessage.type,\n source: this.source,\n target: partialMessage.target ?? this.target,\n payload: partialMessage.payload,\n replyTo: partialMessage.replyTo,\n error: partialMessage.error\n }) as BridgeMessage<TPayload>;\n\n targetWindow.postMessage(message, this.targetOrigin);\n }\n}\n\nexport function createAppBridge(options: AppBridgeOptions = {}) {\n return new AppBridge(options);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,68 @@
1
+ type BridgeParticipant = "embedded-app" | "dashboard" | "unknown";
2
+ type BridgeMessageKind = "event" | "request" | "response";
3
+ interface BridgeErrorPayload {
4
+ code: string;
5
+ message: string;
6
+ }
7
+ interface BridgeMessage<TPayload = unknown> {
8
+ namespace: string;
9
+ version: "1.0";
10
+ kind: BridgeMessageKind;
11
+ id: string;
12
+ type: string;
13
+ source: BridgeParticipant;
14
+ target?: BridgeParticipant;
15
+ payload?: TPayload;
16
+ replyTo?: string;
17
+ error?: BridgeErrorPayload;
18
+ }
19
+ interface AppBridgeOptions {
20
+ source?: BridgeParticipant;
21
+ target?: BridgeParticipant;
22
+ namespace?: string;
23
+ targetOrigin?: string;
24
+ allowedOrigins?: string[];
25
+ requestTimeoutMs?: number;
26
+ selfWindow?: Window;
27
+ targetWindow?: Window | null;
28
+ }
29
+ interface RequestOptions {
30
+ timeoutMs?: number;
31
+ }
32
+ interface ReceivedBridgeMessage<TPayload = unknown> extends BridgeMessage<TPayload> {
33
+ origin: string;
34
+ rawEvent: MessageEvent<unknown>;
35
+ }
36
+ type BridgeEventHandler<TPayload = unknown> = (message: ReceivedBridgeMessage<TPayload>) => void;
37
+ type BridgeRequestHandler<TRequest = unknown, TResponse = unknown> = (payload: TRequest, message: ReceivedBridgeMessage<TRequest>) => TResponse | Promise<TResponse>;
38
+ type Unsubscribe = () => void;
39
+ declare function isBridgeMessage<TPayload = unknown>(value: unknown, namespace?: string): value is BridgeMessage<TPayload>;
40
+ declare class AppBridge {
41
+ private readonly namespace;
42
+ private readonly source;
43
+ private readonly target;
44
+ private readonly targetOrigin;
45
+ private readonly allowedOrigins?;
46
+ private readonly selfWindow?;
47
+ private readonly defaultTimeoutMs;
48
+ private targetWindow?;
49
+ private readonly eventHandlers;
50
+ private readonly requestHandlers;
51
+ private readonly pendingRequests;
52
+ private readonly messageListener;
53
+ constructor(options?: AppBridgeOptions);
54
+ setTargetWindow(targetWindow: Window | null): void;
55
+ send<TPayload = unknown>(type: string, payload?: TPayload): void;
56
+ request<TRequest = unknown, TResponse = unknown>(type: string, payload?: TRequest, options?: RequestOptions): Promise<TResponse>;
57
+ on<TPayload = unknown>(type: string, handler: BridgeEventHandler<TPayload>): Unsubscribe;
58
+ onRequest<TRequest = unknown, TResponse = unknown>(type: string, handler: BridgeRequestHandler<TRequest, TResponse>): Unsubscribe;
59
+ destroy(): void;
60
+ private handleMessage;
61
+ private emitHandlers;
62
+ private handleRequest;
63
+ private resolvePendingRequest;
64
+ private postMessage;
65
+ }
66
+ declare function createAppBridge(options?: AppBridgeOptions): AppBridge;
67
+
68
+ export { AppBridge, type AppBridgeOptions, type BridgeErrorPayload, type BridgeEventHandler, type BridgeMessage, type BridgeMessageKind, type BridgeParticipant, type BridgeRequestHandler, type ReceivedBridgeMessage, type RequestOptions, type Unsubscribe, createAppBridge, isBridgeMessage };
@@ -0,0 +1,68 @@
1
+ type BridgeParticipant = "embedded-app" | "dashboard" | "unknown";
2
+ type BridgeMessageKind = "event" | "request" | "response";
3
+ interface BridgeErrorPayload {
4
+ code: string;
5
+ message: string;
6
+ }
7
+ interface BridgeMessage<TPayload = unknown> {
8
+ namespace: string;
9
+ version: "1.0";
10
+ kind: BridgeMessageKind;
11
+ id: string;
12
+ type: string;
13
+ source: BridgeParticipant;
14
+ target?: BridgeParticipant;
15
+ payload?: TPayload;
16
+ replyTo?: string;
17
+ error?: BridgeErrorPayload;
18
+ }
19
+ interface AppBridgeOptions {
20
+ source?: BridgeParticipant;
21
+ target?: BridgeParticipant;
22
+ namespace?: string;
23
+ targetOrigin?: string;
24
+ allowedOrigins?: string[];
25
+ requestTimeoutMs?: number;
26
+ selfWindow?: Window;
27
+ targetWindow?: Window | null;
28
+ }
29
+ interface RequestOptions {
30
+ timeoutMs?: number;
31
+ }
32
+ interface ReceivedBridgeMessage<TPayload = unknown> extends BridgeMessage<TPayload> {
33
+ origin: string;
34
+ rawEvent: MessageEvent<unknown>;
35
+ }
36
+ type BridgeEventHandler<TPayload = unknown> = (message: ReceivedBridgeMessage<TPayload>) => void;
37
+ type BridgeRequestHandler<TRequest = unknown, TResponse = unknown> = (payload: TRequest, message: ReceivedBridgeMessage<TRequest>) => TResponse | Promise<TResponse>;
38
+ type Unsubscribe = () => void;
39
+ declare function isBridgeMessage<TPayload = unknown>(value: unknown, namespace?: string): value is BridgeMessage<TPayload>;
40
+ declare class AppBridge {
41
+ private readonly namespace;
42
+ private readonly source;
43
+ private readonly target;
44
+ private readonly targetOrigin;
45
+ private readonly allowedOrigins?;
46
+ private readonly selfWindow?;
47
+ private readonly defaultTimeoutMs;
48
+ private targetWindow?;
49
+ private readonly eventHandlers;
50
+ private readonly requestHandlers;
51
+ private readonly pendingRequests;
52
+ private readonly messageListener;
53
+ constructor(options?: AppBridgeOptions);
54
+ setTargetWindow(targetWindow: Window | null): void;
55
+ send<TPayload = unknown>(type: string, payload?: TPayload): void;
56
+ request<TRequest = unknown, TResponse = unknown>(type: string, payload?: TRequest, options?: RequestOptions): Promise<TResponse>;
57
+ on<TPayload = unknown>(type: string, handler: BridgeEventHandler<TPayload>): Unsubscribe;
58
+ onRequest<TRequest = unknown, TResponse = unknown>(type: string, handler: BridgeRequestHandler<TRequest, TResponse>): Unsubscribe;
59
+ destroy(): void;
60
+ private handleMessage;
61
+ private emitHandlers;
62
+ private handleRequest;
63
+ private resolvePendingRequest;
64
+ private postMessage;
65
+ }
66
+ declare function createAppBridge(options?: AppBridgeOptions): AppBridge;
67
+
68
+ export { AppBridge, type AppBridgeOptions, type BridgeErrorPayload, type BridgeEventHandler, type BridgeMessage, type BridgeMessageKind, type BridgeParticipant, type BridgeRequestHandler, type ReceivedBridgeMessage, type RequestOptions, type Unsubscribe, createAppBridge, isBridgeMessage };
package/dist/index.js ADDED
@@ -0,0 +1,261 @@
1
+ // src/index.ts
2
+ var DEFAULT_NAMESPACE = "thorcommerce:app-bridge";
3
+ var DEFAULT_TIMEOUT_MS = 1e4;
4
+ function createMessageId() {
5
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
6
+ return crypto.randomUUID();
7
+ }
8
+ return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
9
+ }
10
+ function resolveSelfWindow(explicitWindow) {
11
+ if (explicitWindow) {
12
+ return explicitWindow;
13
+ }
14
+ if (typeof window !== "undefined") {
15
+ return window;
16
+ }
17
+ return void 0;
18
+ }
19
+ function resolveTargetWindow(selfWindow, targetWindow) {
20
+ if (targetWindow) {
21
+ return targetWindow;
22
+ }
23
+ if (!selfWindow) {
24
+ return void 0;
25
+ }
26
+ if (selfWindow.parent && selfWindow.parent !== selfWindow) {
27
+ return selfWindow.parent;
28
+ }
29
+ return void 0;
30
+ }
31
+ function isDevelopmentEnvironment() {
32
+ const nodeEnv = typeof globalThis !== "undefined" && "process" in globalThis ? globalThis.process?.env?.NODE_ENV : void 0;
33
+ if (nodeEnv) {
34
+ return nodeEnv !== "production";
35
+ }
36
+ return true;
37
+ }
38
+ function hasAllowedOrigin(origin, allowedOrigins) {
39
+ if (!allowedOrigins || allowedOrigins.length === 0) {
40
+ return true;
41
+ }
42
+ return allowedOrigins.includes(origin);
43
+ }
44
+ function omitUndefinedFields(value) {
45
+ return Object.fromEntries(
46
+ Object.entries(value).filter(([, fieldValue]) => fieldValue !== void 0)
47
+ );
48
+ }
49
+ function isBridgeMessage(value, namespace = DEFAULT_NAMESPACE) {
50
+ if (!value || typeof value !== "object") {
51
+ return false;
52
+ }
53
+ const message = value;
54
+ return message.namespace === namespace && message.version === "1.0" && typeof message.kind === "string" && typeof message.id === "string" && typeof message.type === "string" && typeof message.source === "string";
55
+ }
56
+ var AppBridge = class {
57
+ constructor(options = {}) {
58
+ this.eventHandlers = /* @__PURE__ */ new Map();
59
+ this.requestHandlers = /* @__PURE__ */ new Map();
60
+ this.pendingRequests = /* @__PURE__ */ new Map();
61
+ this.namespace = options.namespace ?? DEFAULT_NAMESPACE;
62
+ this.source = options.source ?? "embedded-app";
63
+ this.target = options.target;
64
+ this.targetOrigin = options.targetOrigin ?? "*";
65
+ this.allowedOrigins = options.allowedOrigins;
66
+ this.defaultTimeoutMs = options.requestTimeoutMs ?? DEFAULT_TIMEOUT_MS;
67
+ this.selfWindow = resolveSelfWindow(options.selfWindow);
68
+ this.targetWindow = resolveTargetWindow(this.selfWindow, options.targetWindow);
69
+ this.messageListener = (event) => {
70
+ this.handleMessage(event);
71
+ };
72
+ if (!this.selfWindow) {
73
+ throw new Error(
74
+ "AppBridge requires a browser window. Pass selfWindow explicitly when constructing it outside global window scope."
75
+ );
76
+ }
77
+ if (this.targetOrigin === "*" && isDevelopmentEnvironment()) {
78
+ console.warn(
79
+ 'AppBridge is using "*" as targetOrigin. Set targetOrigin explicitly for both the dashboard and embedded app in production.'
80
+ );
81
+ }
82
+ this.selfWindow.addEventListener("message", this.messageListener);
83
+ }
84
+ setTargetWindow(targetWindow) {
85
+ this.targetWindow = targetWindow ?? void 0;
86
+ }
87
+ send(type, payload) {
88
+ this.postMessage({
89
+ kind: "event",
90
+ type,
91
+ payload
92
+ });
93
+ }
94
+ request(type, payload, options = {}) {
95
+ const messageId = createMessageId();
96
+ const timeoutMs = options.timeoutMs ?? this.defaultTimeoutMs;
97
+ return new Promise((resolve, reject) => {
98
+ const timeoutId = setTimeout(() => {
99
+ this.pendingRequests.delete(messageId);
100
+ reject(new Error(`Bridge request timed out for "${type}" after ${timeoutMs}ms.`));
101
+ }, timeoutMs);
102
+ this.pendingRequests.set(messageId, {
103
+ resolve: (value) => resolve(value),
104
+ reject,
105
+ timeoutId
106
+ });
107
+ try {
108
+ this.postMessage({
109
+ id: messageId,
110
+ kind: "request",
111
+ type,
112
+ payload
113
+ });
114
+ } catch (error) {
115
+ clearTimeout(timeoutId);
116
+ this.pendingRequests.delete(messageId);
117
+ reject(error instanceof Error ? error : new Error("Failed to send bridge request."));
118
+ }
119
+ });
120
+ }
121
+ on(type, handler) {
122
+ const handlers = this.eventHandlers.get(type) ?? /* @__PURE__ */ new Set();
123
+ handlers.add(handler);
124
+ this.eventHandlers.set(type, handlers);
125
+ return () => {
126
+ handlers.delete(handler);
127
+ if (handlers.size === 0) {
128
+ this.eventHandlers.delete(type);
129
+ }
130
+ };
131
+ }
132
+ onRequest(type, handler) {
133
+ this.requestHandlers.set(type, handler);
134
+ return () => {
135
+ const registeredHandler = this.requestHandlers.get(type);
136
+ if (registeredHandler === handler) {
137
+ this.requestHandlers.delete(type);
138
+ }
139
+ };
140
+ }
141
+ destroy() {
142
+ if (this.selfWindow) {
143
+ this.selfWindow.removeEventListener("message", this.messageListener);
144
+ }
145
+ for (const pendingRequest of this.pendingRequests.values()) {
146
+ clearTimeout(pendingRequest.timeoutId);
147
+ pendingRequest.reject(new Error("AppBridge destroyed before a response was received."));
148
+ }
149
+ this.pendingRequests.clear();
150
+ this.eventHandlers.clear();
151
+ this.requestHandlers.clear();
152
+ }
153
+ handleMessage(event) {
154
+ if (!hasAllowedOrigin(event.origin, this.allowedOrigins)) {
155
+ return;
156
+ }
157
+ if (!isBridgeMessage(event.data, this.namespace)) {
158
+ return;
159
+ }
160
+ const message = event.data;
161
+ if (this.target && message.target && message.target !== this.source) {
162
+ return;
163
+ }
164
+ const receivedMessage = {
165
+ ...message,
166
+ origin: event.origin,
167
+ rawEvent: event
168
+ };
169
+ if (message.kind === "response" && message.replyTo) {
170
+ this.resolvePendingRequest(message.replyTo, message);
171
+ return;
172
+ }
173
+ this.emitHandlers(message.type, receivedMessage);
174
+ if (message.kind === "request") {
175
+ void this.handleRequest(receivedMessage);
176
+ }
177
+ }
178
+ emitHandlers(type, message) {
179
+ const typeHandlers = this.eventHandlers.get(type);
180
+ if (typeHandlers) {
181
+ for (const handler of typeHandlers) {
182
+ handler(message);
183
+ }
184
+ }
185
+ if (type === "*") {
186
+ return;
187
+ }
188
+ const wildcardHandlers = this.eventHandlers.get("*");
189
+ if (wildcardHandlers) {
190
+ for (const handler of wildcardHandlers) {
191
+ handler(message);
192
+ }
193
+ }
194
+ }
195
+ async handleRequest(message) {
196
+ const handler = this.requestHandlers.get(message.type);
197
+ if (!handler) {
198
+ return;
199
+ }
200
+ try {
201
+ const payload = await handler(message.payload, message);
202
+ this.postMessage({
203
+ kind: "response",
204
+ type: message.type,
205
+ payload,
206
+ replyTo: message.id
207
+ });
208
+ } catch (error) {
209
+ const bridgeError = error instanceof Error ? { code: "request_handler_error", message: error.message } : { code: "request_handler_error", message: "Unknown request handler error." };
210
+ this.postMessage({
211
+ kind: "response",
212
+ type: message.type,
213
+ error: bridgeError,
214
+ replyTo: message.id
215
+ });
216
+ }
217
+ }
218
+ resolvePendingRequest(messageId, message) {
219
+ const pendingRequest = this.pendingRequests.get(messageId);
220
+ if (!pendingRequest) {
221
+ return;
222
+ }
223
+ clearTimeout(pendingRequest.timeoutId);
224
+ this.pendingRequests.delete(messageId);
225
+ if (message.error) {
226
+ pendingRequest.reject(new Error(`${message.error.code}: ${message.error.message}`));
227
+ return;
228
+ }
229
+ pendingRequest.resolve(message.payload);
230
+ }
231
+ postMessage(partialMessage) {
232
+ const targetWindow = this.targetWindow;
233
+ if (!targetWindow) {
234
+ throw new Error(
235
+ "AppBridge could not resolve a target window. Pass targetWindow explicitly or call setTargetWindow()."
236
+ );
237
+ }
238
+ const message = omitUndefinedFields({
239
+ namespace: this.namespace,
240
+ version: "1.0",
241
+ id: partialMessage.id ?? createMessageId(),
242
+ kind: partialMessage.kind,
243
+ type: partialMessage.type,
244
+ source: this.source,
245
+ target: partialMessage.target ?? this.target,
246
+ payload: partialMessage.payload,
247
+ replyTo: partialMessage.replyTo,
248
+ error: partialMessage.error
249
+ });
250
+ targetWindow.postMessage(message, this.targetOrigin);
251
+ }
252
+ };
253
+ function createAppBridge(options = {}) {
254
+ return new AppBridge(options);
255
+ }
256
+ export {
257
+ AppBridge,
258
+ createAppBridge,
259
+ isBridgeMessage
260
+ };
261
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type BridgeParticipant = \"embedded-app\" | \"dashboard\" | \"unknown\";\n\nexport type BridgeMessageKind = \"event\" | \"request\" | \"response\";\n\nexport interface BridgeErrorPayload {\n code: string;\n message: string;\n}\n\nexport interface BridgeMessage<TPayload = unknown> {\n namespace: string;\n version: \"1.0\";\n kind: BridgeMessageKind;\n id: string;\n type: string;\n source: BridgeParticipant;\n target?: BridgeParticipant;\n payload?: TPayload;\n replyTo?: string;\n error?: BridgeErrorPayload;\n}\n\nexport interface AppBridgeOptions {\n source?: BridgeParticipant;\n target?: BridgeParticipant;\n namespace?: string;\n targetOrigin?: string;\n allowedOrigins?: string[];\n requestTimeoutMs?: number;\n selfWindow?: Window;\n targetWindow?: Window | null;\n}\n\nexport interface RequestOptions {\n timeoutMs?: number;\n}\n\nexport interface ReceivedBridgeMessage<TPayload = unknown> extends BridgeMessage<TPayload> {\n origin: string;\n rawEvent: MessageEvent<unknown>;\n}\n\nexport type BridgeEventHandler<TPayload = unknown> = (\n message: ReceivedBridgeMessage<TPayload>\n) => void;\n\nexport type BridgeRequestHandler<TRequest = unknown, TResponse = unknown> = (\n payload: TRequest,\n message: ReceivedBridgeMessage<TRequest>\n) => TResponse | Promise<TResponse>;\n\nexport type Unsubscribe = () => void;\n\nconst DEFAULT_NAMESPACE = \"thorcommerce:app-bridge\";\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nfunction createMessageId() {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction resolveSelfWindow(explicitWindow?: Window) {\n if (explicitWindow) {\n return explicitWindow;\n }\n\n if (typeof window !== \"undefined\") {\n return window;\n }\n\n return undefined;\n}\n\nfunction resolveTargetWindow(selfWindow: Window | undefined, targetWindow?: Window | null) {\n if (targetWindow) {\n return targetWindow;\n }\n\n if (!selfWindow) {\n return undefined;\n }\n\n if (selfWindow.parent && selfWindow.parent !== selfWindow) {\n return selfWindow.parent;\n }\n\n return undefined;\n}\n\nfunction isDevelopmentEnvironment() {\n const nodeEnv =\n typeof globalThis !== \"undefined\" && \"process\" in globalThis\n ? (\n globalThis as typeof globalThis & {\n process?: { env?: { NODE_ENV?: string } };\n }\n ).process?.env?.NODE_ENV\n : undefined;\n\n if (nodeEnv) {\n return nodeEnv !== \"production\";\n }\n\n return true;\n}\n\nfunction hasAllowedOrigin(origin: string, allowedOrigins?: string[]) {\n if (!allowedOrigins || allowedOrigins.length === 0) {\n return true;\n }\n\n return allowedOrigins.includes(origin);\n}\n\nfunction omitUndefinedFields<T extends object>(value: T): T {\n return Object.fromEntries(\n Object.entries(value).filter(([, fieldValue]) => fieldValue !== undefined)\n ) as T;\n}\n\nexport function isBridgeMessage<TPayload = unknown>(\n value: unknown,\n namespace = DEFAULT_NAMESPACE\n): value is BridgeMessage<TPayload> {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const message = value as Partial<BridgeMessage<TPayload>>;\n\n return (\n message.namespace === namespace &&\n message.version === \"1.0\" &&\n typeof message.kind === \"string\" &&\n typeof message.id === \"string\" &&\n typeof message.type === \"string\" &&\n typeof message.source === \"string\"\n );\n}\n\nexport class AppBridge {\n private readonly namespace: string;\n private readonly source: BridgeParticipant;\n private readonly target: BridgeParticipant | undefined;\n private readonly targetOrigin: string;\n private readonly allowedOrigins?: string[];\n private readonly selfWindow?: Window;\n private readonly defaultTimeoutMs: number;\n private targetWindow?: Window;\n private readonly eventHandlers = new Map<string, Set<BridgeEventHandler>>();\n private readonly requestHandlers = new Map<string, BridgeRequestHandler>();\n private readonly pendingRequests = new Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timeoutId: ReturnType<typeof setTimeout>;\n }\n >();\n private readonly messageListener: (event: MessageEvent<unknown>) => void;\n\n constructor(options: AppBridgeOptions = {}) {\n this.namespace = options.namespace ?? DEFAULT_NAMESPACE;\n this.source = options.source ?? \"embedded-app\";\n this.target = options.target;\n this.targetOrigin = options.targetOrigin ?? \"*\";\n this.allowedOrigins = options.allowedOrigins;\n this.defaultTimeoutMs = options.requestTimeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.selfWindow = resolveSelfWindow(options.selfWindow);\n this.targetWindow = resolveTargetWindow(this.selfWindow, options.targetWindow);\n this.messageListener = (event) => {\n this.handleMessage(event);\n };\n\n if (!this.selfWindow) {\n throw new Error(\n \"AppBridge requires a browser window. Pass selfWindow explicitly when constructing it outside global window scope.\"\n );\n }\n\n if (this.targetOrigin === \"*\" && isDevelopmentEnvironment()) {\n console.warn(\n 'AppBridge is using \"*\" as targetOrigin. Set targetOrigin explicitly for both the dashboard and embedded app in production.'\n );\n }\n\n this.selfWindow.addEventListener(\"message\", this.messageListener);\n }\n\n setTargetWindow(targetWindow: Window | null) {\n this.targetWindow = targetWindow ?? undefined;\n }\n\n send<TPayload = unknown>(type: string, payload?: TPayload) {\n this.postMessage({\n kind: \"event\",\n type,\n payload\n });\n }\n\n request<TRequest = unknown, TResponse = unknown>(\n type: string,\n payload?: TRequest,\n options: RequestOptions = {}\n ) {\n const messageId = createMessageId();\n const timeoutMs = options.timeoutMs ?? this.defaultTimeoutMs;\n\n return new Promise<TResponse>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n this.pendingRequests.delete(messageId);\n reject(new Error(`Bridge request timed out for \"${type}\" after ${timeoutMs}ms.`));\n }, timeoutMs);\n\n this.pendingRequests.set(messageId, {\n resolve: (value) => resolve(value as TResponse),\n reject,\n timeoutId\n });\n\n try {\n this.postMessage({\n id: messageId,\n kind: \"request\",\n type,\n payload\n });\n } catch (error) {\n clearTimeout(timeoutId);\n this.pendingRequests.delete(messageId);\n reject(error instanceof Error ? error : new Error(\"Failed to send bridge request.\"));\n }\n });\n }\n\n on<TPayload = unknown>(type: string, handler: BridgeEventHandler<TPayload>): Unsubscribe {\n const handlers = this.eventHandlers.get(type) ?? new Set<BridgeEventHandler>();\n handlers.add(handler as BridgeEventHandler);\n this.eventHandlers.set(type, handlers);\n\n return () => {\n handlers.delete(handler as BridgeEventHandler);\n if (handlers.size === 0) {\n this.eventHandlers.delete(type);\n }\n };\n }\n\n onRequest<TRequest = unknown, TResponse = unknown>(\n type: string,\n handler: BridgeRequestHandler<TRequest, TResponse>\n ): Unsubscribe {\n this.requestHandlers.set(type, handler as BridgeRequestHandler);\n\n return () => {\n const registeredHandler = this.requestHandlers.get(type);\n if (registeredHandler === handler) {\n this.requestHandlers.delete(type);\n }\n };\n }\n\n destroy() {\n if (this.selfWindow) {\n this.selfWindow.removeEventListener(\"message\", this.messageListener);\n }\n\n for (const pendingRequest of this.pendingRequests.values()) {\n clearTimeout(pendingRequest.timeoutId);\n pendingRequest.reject(new Error(\"AppBridge destroyed before a response was received.\"));\n }\n\n this.pendingRequests.clear();\n this.eventHandlers.clear();\n this.requestHandlers.clear();\n }\n\n private handleMessage(event: MessageEvent<unknown>) {\n if (!hasAllowedOrigin(event.origin, this.allowedOrigins)) {\n return;\n }\n\n if (!isBridgeMessage(event.data, this.namespace)) {\n return;\n }\n\n const message = event.data;\n\n if (this.target && message.target && message.target !== this.source) {\n return;\n }\n\n const receivedMessage: ReceivedBridgeMessage = {\n ...message,\n origin: event.origin,\n rawEvent: event\n };\n\n if (message.kind === \"response\" && message.replyTo) {\n this.resolvePendingRequest(message.replyTo, message);\n return;\n }\n\n this.emitHandlers(message.type, receivedMessage);\n\n if (message.kind === \"request\") {\n void this.handleRequest(receivedMessage);\n }\n }\n\n private emitHandlers(type: string, message: ReceivedBridgeMessage) {\n const typeHandlers = this.eventHandlers.get(type);\n if (typeHandlers) {\n for (const handler of typeHandlers) {\n handler(message);\n }\n }\n\n if (type === \"*\") {\n return;\n }\n\n const wildcardHandlers = this.eventHandlers.get(\"*\");\n if (wildcardHandlers) {\n for (const handler of wildcardHandlers) {\n handler(message);\n }\n }\n }\n\n private async handleRequest(message: ReceivedBridgeMessage) {\n const handler = this.requestHandlers.get(message.type);\n if (!handler) {\n return;\n }\n\n try {\n const payload = await handler(message.payload, message);\n this.postMessage({\n kind: \"response\",\n type: message.type,\n payload,\n replyTo: message.id\n });\n } catch (error) {\n const bridgeError =\n error instanceof Error\n ? { code: \"request_handler_error\", message: error.message }\n : { code: \"request_handler_error\", message: \"Unknown request handler error.\" };\n\n this.postMessage({\n kind: \"response\",\n type: message.type,\n error: bridgeError,\n replyTo: message.id\n });\n }\n }\n\n private resolvePendingRequest(messageId: string, message: BridgeMessage) {\n const pendingRequest = this.pendingRequests.get(messageId);\n if (!pendingRequest) {\n return;\n }\n\n clearTimeout(pendingRequest.timeoutId);\n this.pendingRequests.delete(messageId);\n\n if (message.error) {\n pendingRequest.reject(new Error(`${message.error.code}: ${message.error.message}`));\n return;\n }\n\n pendingRequest.resolve(message.payload);\n }\n\n private postMessage<TPayload = unknown>(\n partialMessage: Pick<BridgeMessage<TPayload>, \"kind\" | \"type\"> &\n Partial<Omit<BridgeMessage<TPayload>, \"namespace\" | \"version\" | \"source\">>\n ) {\n const targetWindow = this.targetWindow;\n if (!targetWindow) {\n throw new Error(\n \"AppBridge could not resolve a target window. Pass targetWindow explicitly or call setTargetWindow().\"\n );\n }\n\n const message = omitUndefinedFields({\n namespace: this.namespace,\n version: \"1.0\",\n id: partialMessage.id ?? createMessageId(),\n kind: partialMessage.kind,\n type: partialMessage.type,\n source: this.source,\n target: partialMessage.target ?? this.target,\n payload: partialMessage.payload,\n replyTo: partialMessage.replyTo,\n error: partialMessage.error\n }) as BridgeMessage<TPayload>;\n\n targetWindow.postMessage(message, this.targetOrigin);\n }\n}\n\nexport function createAppBridge(options: AppBridgeOptions = {}) {\n return new AppBridge(options);\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@thor-commerce/app-bridge-react",
3
+ "version": "0.1.0",
4
+ "description": "Communication bridge between embedded apps and the Thor Commerce dashboard",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "dev": "tsup --watch",
22
+ "test": "tsx --test test/*.test.ts",
23
+ "typecheck": "tsc --noEmit",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^25.5.0",
28
+ "tsup": "^8.0.0",
29
+ "tsx": "^4.21.0",
30
+ "typescript": "^5.5.0"
31
+ },
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git"
35
+ },
36
+ "keywords": [
37
+ "thor-commerce",
38
+ "embedded-app",
39
+ "app-bridge",
40
+ "iframe"
41
+ ]
42
+ }