@thor-commerce/app-bridge-react 0.3.0 → 0.4.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.
@@ -1,6 +1,61 @@
1
+ // src/navigation.ts
2
+ function normalizeSearch(search) {
3
+ if (!search) {
4
+ return "";
5
+ }
6
+ return search.startsWith("?") ? search : `?${search}`;
7
+ }
8
+ function normalizeHash(hash) {
9
+ if (!hash) {
10
+ return "";
11
+ }
12
+ return hash.startsWith("#") ? hash : `#${hash}`;
13
+ }
14
+ function buildNavigationUpdatePayload(path) {
15
+ let pathname = path;
16
+ let search = "";
17
+ let hash = "";
18
+ const hashIndex = pathname.indexOf("#");
19
+ if (hashIndex >= 0) {
20
+ hash = pathname.slice(hashIndex);
21
+ pathname = pathname.slice(0, hashIndex);
22
+ }
23
+ const searchIndex = pathname.indexOf("?");
24
+ if (searchIndex >= 0) {
25
+ search = pathname.slice(searchIndex);
26
+ pathname = pathname.slice(0, searchIndex);
27
+ }
28
+ return {
29
+ path: `${pathname || "/"}${search}${hash}`,
30
+ pathname: pathname || "/",
31
+ search,
32
+ hash
33
+ };
34
+ }
35
+ function resolveNavigationDestination(payload) {
36
+ if (typeof payload === "string") {
37
+ return payload;
38
+ }
39
+ if (!payload || typeof payload !== "object") {
40
+ return null;
41
+ }
42
+ const value = payload;
43
+ if (typeof value.path === "string" && value.path) {
44
+ return value.path;
45
+ }
46
+ if (typeof value.href === "string" && value.href) {
47
+ return value.href;
48
+ }
49
+ if (typeof value.pathname !== "string" || !value.pathname) {
50
+ return null;
51
+ }
52
+ return `${value.pathname}${normalizeSearch(value.search ?? "")}${normalizeHash(value.hash ?? "")}`;
53
+ }
54
+
1
55
  // src/core.ts
2
56
  var DEFAULT_NAMESPACE = "thorcommerce:app-bridge";
3
57
  var DEFAULT_TIMEOUT_MS = 1e4;
58
+ var DEFAULT_REDIRECT_EVENT_TYPE = "navigation:redirect";
4
59
  function createMessageId() {
5
60
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
6
61
  return crypto.randomUUID();
@@ -41,6 +96,9 @@ function hasAllowedOrigin(origin, allowedOrigins) {
41
96
  }
42
97
  return allowedOrigins.includes(origin);
43
98
  }
99
+ function isMessageTarget(value) {
100
+ return !!value && typeof value.postMessage === "function";
101
+ }
44
102
  function omitUndefinedFields(value) {
45
103
  return Object.fromEntries(
46
104
  Object.entries(value).filter(([, fieldValue]) => fieldValue !== void 0)
@@ -87,6 +145,27 @@ var AppBridge = class {
87
145
  hasTargetWindow() {
88
146
  return this.targetWindow !== void 0;
89
147
  }
148
+ redirect(payload) {
149
+ const destination = resolveNavigationDestination(payload);
150
+ if (!destination) {
151
+ throw new Error("AppBridge redirect requires a valid destination.");
152
+ }
153
+ if (!this.targetWindow) {
154
+ this.navigateSelf(destination);
155
+ return;
156
+ }
157
+ this.postMessage({
158
+ kind: "event",
159
+ type: DEFAULT_REDIRECT_EVENT_TYPE,
160
+ payload: typeof payload === "string" ? { href: payload } : payload
161
+ });
162
+ }
163
+ redirectToRemote(href) {
164
+ this.redirect({ href });
165
+ }
166
+ redirectToApp(path) {
167
+ this.redirect(typeof path === "string" ? { path } : path);
168
+ }
90
169
  send(type, payload) {
91
170
  this.postMessage({
92
171
  kind: "event",
@@ -169,6 +248,9 @@ var AppBridge = class {
169
248
  origin: event.origin,
170
249
  rawEvent: event
171
250
  };
251
+ if (message.kind === "event" && message.type === DEFAULT_REDIRECT_EVENT_TYPE && this.handleRedirectMessage(receivedMessage)) {
252
+ return;
253
+ }
172
254
  if (message.kind === "response" && message.replyTo) {
173
255
  this.resolvePendingRequest(message.replyTo, message);
174
256
  return;
@@ -200,22 +282,29 @@ var AppBridge = class {
200
282
  if (!handler) {
201
283
  return;
202
284
  }
285
+ const replyTarget = isMessageTarget(message.rawEvent.source) ? message.rawEvent.source : this.targetWindow;
203
286
  try {
204
287
  const payload = await handler(message.payload, message);
205
- this.postMessage({
206
- kind: "response",
207
- type: message.type,
208
- payload,
209
- replyTo: message.id
210
- });
288
+ this.postMessage(
289
+ {
290
+ kind: "response",
291
+ type: message.type,
292
+ payload,
293
+ replyTo: message.id
294
+ },
295
+ replyTarget
296
+ );
211
297
  } catch (error) {
212
298
  const bridgeError = error instanceof Error ? { code: "request_handler_error", message: error.message } : { code: "request_handler_error", message: "Unknown request handler error." };
213
- this.postMessage({
214
- kind: "response",
215
- type: message.type,
216
- error: bridgeError,
217
- replyTo: message.id
218
- });
299
+ this.postMessage(
300
+ {
301
+ kind: "response",
302
+ type: message.type,
303
+ error: bridgeError,
304
+ replyTo: message.id
305
+ },
306
+ replyTarget
307
+ );
219
308
  }
220
309
  }
221
310
  resolvePendingRequest(messageId, message) {
@@ -231,8 +320,8 @@ var AppBridge = class {
231
320
  }
232
321
  pendingRequest.resolve(message.payload);
233
322
  }
234
- postMessage(partialMessage) {
235
- const targetWindow = this.targetWindow;
323
+ postMessage(partialMessage, targetWindowOverride) {
324
+ const targetWindow = targetWindowOverride ?? this.targetWindow;
236
325
  if (!targetWindow) {
237
326
  throw new Error(
238
327
  "AppBridge could not resolve a target window. Pass targetWindow explicitly or call setTargetWindow()."
@@ -252,65 +341,25 @@ var AppBridge = class {
252
341
  });
253
342
  targetWindow.postMessage(message, this.targetOrigin);
254
343
  }
344
+ handleRedirectMessage(message) {
345
+ const destination = resolveNavigationDestination(message.payload);
346
+ if (!destination) {
347
+ return false;
348
+ }
349
+ this.navigateSelf(destination);
350
+ return true;
351
+ }
352
+ navigateSelf(destination) {
353
+ if (!this.selfWindow) {
354
+ throw new Error("AppBridge could not resolve a browser window for redirect.");
355
+ }
356
+ this.selfWindow.location.assign(destination);
357
+ }
255
358
  };
256
359
  function createAppBridge(options = {}) {
257
360
  return new AppBridge(options);
258
361
  }
259
362
 
260
- // src/navigation.ts
261
- function normalizeSearch(search) {
262
- if (!search) {
263
- return "";
264
- }
265
- return search.startsWith("?") ? search : `?${search}`;
266
- }
267
- function normalizeHash(hash) {
268
- if (!hash) {
269
- return "";
270
- }
271
- return hash.startsWith("#") ? hash : `#${hash}`;
272
- }
273
- function buildNavigationUpdatePayload(path) {
274
- let pathname = path;
275
- let search = "";
276
- let hash = "";
277
- const hashIndex = pathname.indexOf("#");
278
- if (hashIndex >= 0) {
279
- hash = pathname.slice(hashIndex);
280
- pathname = pathname.slice(0, hashIndex);
281
- }
282
- const searchIndex = pathname.indexOf("?");
283
- if (searchIndex >= 0) {
284
- search = pathname.slice(searchIndex);
285
- pathname = pathname.slice(0, searchIndex);
286
- }
287
- return {
288
- path: `${pathname || "/"}${search}${hash}`,
289
- pathname: pathname || "/",
290
- search,
291
- hash
292
- };
293
- }
294
- function resolveNavigationDestination(payload) {
295
- if (typeof payload === "string") {
296
- return payload;
297
- }
298
- if (!payload || typeof payload !== "object") {
299
- return null;
300
- }
301
- const value = payload;
302
- if (typeof value.path === "string" && value.path) {
303
- return value.path;
304
- }
305
- if (typeof value.href === "string" && value.href) {
306
- return value.href;
307
- }
308
- if (typeof value.pathname !== "string" || !value.pathname) {
309
- return null;
310
- }
311
- return `${value.pathname}${normalizeSearch(value.search ?? "")}${normalizeHash(value.hash ?? "")}`;
312
- }
313
-
314
363
  // src/react.tsx
315
364
  import {
316
365
  createContext,
@@ -359,7 +408,6 @@ function AppBridgeProvider({
359
408
  targetWindow
360
409
  });
361
410
  setBridge(nextBridge);
362
- nextBridge.send(readyEventType, readyPayload);
363
411
  return () => {
364
412
  setBridge((currentBridge) => currentBridge === nextBridge ? null : currentBridge);
365
413
  nextBridge.destroy();
@@ -405,12 +453,12 @@ function useAppBridge() {
405
453
  }
406
454
 
407
455
  export {
456
+ buildNavigationUpdatePayload,
457
+ resolveNavigationDestination,
408
458
  isBridgeMessage,
409
459
  AppBridge,
410
460
  createAppBridge,
411
- buildNavigationUpdatePayload,
412
- resolveNavigationDestination,
413
461
  AppBridgeProvider,
414
462
  useAppBridge
415
463
  };
416
- //# sourceMappingURL=chunk-JNYIAUPA.js.map
464
+ //# sourceMappingURL=chunk-EASM25ZU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/navigation.ts","../src/core.ts","../src/react.tsx"],"sourcesContent":["export interface BridgeNavigationGoPayload {\n path?: string;\n href?: string;\n pathname?: string;\n search?: string;\n hash?: string;\n}\n\nexport interface BridgeNavigationRedirectPayload extends BridgeNavigationGoPayload {}\n\nexport interface BridgeNavigationUpdatePayload {\n path: string;\n pathname: string;\n search: string;\n hash: string;\n}\n\nfunction normalizeSearch(search: string) {\n if (!search) {\n return \"\";\n }\n\n return search.startsWith(\"?\") ? search : `?${search}`;\n}\n\nfunction normalizeHash(hash: string) {\n if (!hash) {\n return \"\";\n }\n\n return hash.startsWith(\"#\") ? hash : `#${hash}`;\n}\n\nexport function buildNavigationUpdatePayload(path: string): BridgeNavigationUpdatePayload {\n let pathname = path;\n let search = \"\";\n let hash = \"\";\n\n const hashIndex = pathname.indexOf(\"#\");\n if (hashIndex >= 0) {\n hash = pathname.slice(hashIndex);\n pathname = pathname.slice(0, hashIndex);\n }\n\n const searchIndex = pathname.indexOf(\"?\");\n if (searchIndex >= 0) {\n search = pathname.slice(searchIndex);\n pathname = pathname.slice(0, searchIndex);\n }\n\n return {\n path: `${pathname || \"/\"}${search}${hash}`,\n pathname: pathname || \"/\",\n search,\n hash\n };\n}\n\nexport function resolveNavigationDestination(payload: unknown): string | null {\n if (typeof payload === \"string\") {\n return payload;\n }\n\n if (!payload || typeof payload !== \"object\") {\n return null;\n }\n\n const value = payload as BridgeNavigationGoPayload;\n\n if (typeof value.path === \"string\" && value.path) {\n return value.path;\n }\n\n if (typeof value.href === \"string\" && value.href) {\n return value.href;\n }\n\n if (typeof value.pathname !== \"string\" || !value.pathname) {\n return null;\n }\n\n return `${value.pathname}${normalizeSearch(value.search ?? \"\")}${normalizeHash(value.hash ?? \"\")}`;\n}\n","import {\n resolveNavigationDestination,\n type BridgeNavigationGoPayload,\n type BridgeNavigationRedirectPayload\n} from \"./navigation\";\n\nexport 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;\nconst DEFAULT_REDIRECT_EVENT_TYPE = \"navigation:redirect\";\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 isMessageTarget(value: unknown): value is Window {\n return !!value && typeof (value as Window).postMessage === \"function\";\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 hasTargetWindow() {\n return this.targetWindow !== undefined;\n }\n\n redirect(payload: string | BridgeNavigationRedirectPayload) {\n const destination = resolveNavigationDestination(payload);\n if (!destination) {\n throw new Error(\"AppBridge redirect requires a valid destination.\");\n }\n\n if (!this.targetWindow) {\n this.navigateSelf(destination);\n return;\n }\n\n this.postMessage<BridgeNavigationRedirectPayload>({\n kind: \"event\",\n type: DEFAULT_REDIRECT_EVENT_TYPE,\n payload: typeof payload === \"string\" ? { href: payload } : payload\n });\n }\n\n redirectToRemote(href: string) {\n this.redirect({ href });\n }\n\n redirectToApp(path: string | BridgeNavigationGoPayload) {\n this.redirect(typeof path === \"string\" ? { path } : path);\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 (\n message.kind === \"event\" &&\n message.type === DEFAULT_REDIRECT_EVENT_TYPE &&\n this.handleRedirectMessage(receivedMessage)\n ) {\n return;\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 const replyTarget = isMessageTarget(message.rawEvent.source)\n ? message.rawEvent.source\n : this.targetWindow;\n\n try {\n const payload = await handler(message.payload, message);\n this.postMessage(\n {\n kind: \"response\",\n type: message.type,\n payload,\n replyTo: message.id\n },\n replyTarget\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 {\n kind: \"response\",\n type: message.type,\n error: bridgeError,\n replyTo: message.id\n },\n replyTarget\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 targetWindowOverride?: Window\n ) {\n const targetWindow = targetWindowOverride ?? 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 private handleRedirectMessage(message: ReceivedBridgeMessage): boolean {\n const destination = resolveNavigationDestination(message.payload);\n if (!destination) {\n return false;\n }\n\n this.navigateSelf(destination);\n return true;\n }\n\n private navigateSelf(destination: string) {\n if (!this.selfWindow) {\n throw new Error(\"AppBridge could not resolve a browser window for redirect.\");\n }\n\n this.selfWindow.location.assign(destination);\n }\n}\n\nexport function createAppBridge(options: AppBridgeOptions = {}) {\n return new AppBridge(options);\n}\n","import {\n createContext,\n useContext,\n useEffect,\n useRef,\n useState,\n type ReactNode\n} from \"react\";\n\nimport {\n createAppBridge,\n type AppBridge,\n type AppBridgeOptions,\n type ReceivedBridgeMessage\n} from \"./core\";\nimport {\n buildNavigationUpdatePayload,\n resolveNavigationDestination,\n type BridgeNavigationGoPayload\n} from \"./navigation\";\n\nconst AppBridgeContext = createContext<AppBridge | null>(null);\n\nexport interface AppBridgeProviderProps extends AppBridgeOptions {\n children: ReactNode;\n readyEventType?: string;\n readyPayload?: unknown;\n currentPath?: string | null;\n navigationEventType?: string;\n navigationUpdateEventType?: string;\n onNavigate?: (\n path: string,\n message: ReceivedBridgeMessage<BridgeNavigationGoPayload>\n ) => void;\n}\n\nexport function AppBridgeProvider({\n allowedOrigins,\n children,\n currentPath,\n navigationEventType = \"navigation:go\",\n navigationUpdateEventType = \"navigation:update\",\n namespace,\n onNavigate,\n readyEventType = \"app:ready\",\n readyPayload,\n requestTimeoutMs,\n selfWindow,\n source,\n target,\n targetOrigin,\n targetWindow\n}: AppBridgeProviderProps) {\n const [bridge, setBridge] = useState<AppBridge | null>(null);\n const onNavigateRef = useRef(onNavigate);\n const allowedOriginsKey = allowedOrigins?.join(\"\\n\") ?? \"\";\n\n useEffect(() => {\n onNavigateRef.current = onNavigate;\n }, [onNavigate]);\n\n useEffect(() => {\n if (typeof window === \"undefined\" && !selfWindow) {\n return;\n }\n\n const nextBridge = createAppBridge({\n allowedOrigins: allowedOriginsKey ? allowedOriginsKey.split(\"\\n\") : undefined,\n namespace,\n requestTimeoutMs,\n selfWindow,\n source,\n target,\n targetOrigin,\n targetWindow\n });\n\n setBridge(nextBridge);\n\n return () => {\n setBridge((currentBridge) => (currentBridge === nextBridge ? null : currentBridge));\n nextBridge.destroy();\n };\n }, [\n allowedOriginsKey,\n namespace,\n requestTimeoutMs,\n selfWindow,\n source,\n target,\n targetOrigin,\n targetWindow\n ]);\n\n useEffect(() => {\n if (!bridge || !bridge.hasTargetWindow()) {\n return;\n }\n\n bridge.send(readyEventType, readyPayload);\n }, [bridge, readyEventType, readyPayload]);\n\n useEffect(() => {\n if (!bridge || !bridge.hasTargetWindow() || !currentPath) {\n return;\n }\n\n bridge.send(navigationUpdateEventType, buildNavigationUpdatePayload(currentPath));\n }, [bridge, currentPath, navigationUpdateEventType]);\n\n useEffect(() => {\n if (!bridge || !onNavigate) {\n return;\n }\n\n return bridge.on<BridgeNavigationGoPayload>(navigationEventType, (message) => {\n const destination = resolveNavigationDestination(message.payload);\n if (!destination) {\n return;\n }\n\n onNavigateRef.current?.(destination, message);\n });\n }, [bridge, navigationEventType, onNavigate]);\n\n return <AppBridgeContext.Provider value={bridge}>{children}</AppBridgeContext.Provider>;\n}\n\nexport function useAppBridge() {\n return useContext(AppBridgeContext);\n}\n"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -30,9 +30,64 @@ __export(index_exports, {
30
30
  });
31
31
  module.exports = __toCommonJS(index_exports);
32
32
 
33
+ // src/navigation.ts
34
+ function normalizeSearch(search) {
35
+ if (!search) {
36
+ return "";
37
+ }
38
+ return search.startsWith("?") ? search : `?${search}`;
39
+ }
40
+ function normalizeHash(hash) {
41
+ if (!hash) {
42
+ return "";
43
+ }
44
+ return hash.startsWith("#") ? hash : `#${hash}`;
45
+ }
46
+ function buildNavigationUpdatePayload(path) {
47
+ let pathname = path;
48
+ let search = "";
49
+ let hash = "";
50
+ const hashIndex = pathname.indexOf("#");
51
+ if (hashIndex >= 0) {
52
+ hash = pathname.slice(hashIndex);
53
+ pathname = pathname.slice(0, hashIndex);
54
+ }
55
+ const searchIndex = pathname.indexOf("?");
56
+ if (searchIndex >= 0) {
57
+ search = pathname.slice(searchIndex);
58
+ pathname = pathname.slice(0, searchIndex);
59
+ }
60
+ return {
61
+ path: `${pathname || "/"}${search}${hash}`,
62
+ pathname: pathname || "/",
63
+ search,
64
+ hash
65
+ };
66
+ }
67
+ function resolveNavigationDestination(payload) {
68
+ if (typeof payload === "string") {
69
+ return payload;
70
+ }
71
+ if (!payload || typeof payload !== "object") {
72
+ return null;
73
+ }
74
+ const value = payload;
75
+ if (typeof value.path === "string" && value.path) {
76
+ return value.path;
77
+ }
78
+ if (typeof value.href === "string" && value.href) {
79
+ return value.href;
80
+ }
81
+ if (typeof value.pathname !== "string" || !value.pathname) {
82
+ return null;
83
+ }
84
+ return `${value.pathname}${normalizeSearch(value.search ?? "")}${normalizeHash(value.hash ?? "")}`;
85
+ }
86
+
33
87
  // src/core.ts
34
88
  var DEFAULT_NAMESPACE = "thorcommerce:app-bridge";
35
89
  var DEFAULT_TIMEOUT_MS = 1e4;
90
+ var DEFAULT_REDIRECT_EVENT_TYPE = "navigation:redirect";
36
91
  function createMessageId() {
37
92
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
38
93
  return crypto.randomUUID();
@@ -73,6 +128,9 @@ function hasAllowedOrigin(origin, allowedOrigins) {
73
128
  }
74
129
  return allowedOrigins.includes(origin);
75
130
  }
131
+ function isMessageTarget(value) {
132
+ return !!value && typeof value.postMessage === "function";
133
+ }
76
134
  function omitUndefinedFields(value) {
77
135
  return Object.fromEntries(
78
136
  Object.entries(value).filter(([, fieldValue]) => fieldValue !== void 0)
@@ -119,6 +177,27 @@ var AppBridge = class {
119
177
  hasTargetWindow() {
120
178
  return this.targetWindow !== void 0;
121
179
  }
180
+ redirect(payload) {
181
+ const destination = resolveNavigationDestination(payload);
182
+ if (!destination) {
183
+ throw new Error("AppBridge redirect requires a valid destination.");
184
+ }
185
+ if (!this.targetWindow) {
186
+ this.navigateSelf(destination);
187
+ return;
188
+ }
189
+ this.postMessage({
190
+ kind: "event",
191
+ type: DEFAULT_REDIRECT_EVENT_TYPE,
192
+ payload: typeof payload === "string" ? { href: payload } : payload
193
+ });
194
+ }
195
+ redirectToRemote(href) {
196
+ this.redirect({ href });
197
+ }
198
+ redirectToApp(path) {
199
+ this.redirect(typeof path === "string" ? { path } : path);
200
+ }
122
201
  send(type, payload) {
123
202
  this.postMessage({
124
203
  kind: "event",
@@ -201,6 +280,9 @@ var AppBridge = class {
201
280
  origin: event.origin,
202
281
  rawEvent: event
203
282
  };
283
+ if (message.kind === "event" && message.type === DEFAULT_REDIRECT_EVENT_TYPE && this.handleRedirectMessage(receivedMessage)) {
284
+ return;
285
+ }
204
286
  if (message.kind === "response" && message.replyTo) {
205
287
  this.resolvePendingRequest(message.replyTo, message);
206
288
  return;
@@ -232,22 +314,29 @@ var AppBridge = class {
232
314
  if (!handler) {
233
315
  return;
234
316
  }
317
+ const replyTarget = isMessageTarget(message.rawEvent.source) ? message.rawEvent.source : this.targetWindow;
235
318
  try {
236
319
  const payload = await handler(message.payload, message);
237
- this.postMessage({
238
- kind: "response",
239
- type: message.type,
240
- payload,
241
- replyTo: message.id
242
- });
320
+ this.postMessage(
321
+ {
322
+ kind: "response",
323
+ type: message.type,
324
+ payload,
325
+ replyTo: message.id
326
+ },
327
+ replyTarget
328
+ );
243
329
  } catch (error) {
244
330
  const bridgeError = error instanceof Error ? { code: "request_handler_error", message: error.message } : { code: "request_handler_error", message: "Unknown request handler error." };
245
- this.postMessage({
246
- kind: "response",
247
- type: message.type,
248
- error: bridgeError,
249
- replyTo: message.id
250
- });
331
+ this.postMessage(
332
+ {
333
+ kind: "response",
334
+ type: message.type,
335
+ error: bridgeError,
336
+ replyTo: message.id
337
+ },
338
+ replyTarget
339
+ );
251
340
  }
252
341
  }
253
342
  resolvePendingRequest(messageId, message) {
@@ -263,8 +352,8 @@ var AppBridge = class {
263
352
  }
264
353
  pendingRequest.resolve(message.payload);
265
354
  }
266
- postMessage(partialMessage) {
267
- const targetWindow = this.targetWindow;
355
+ postMessage(partialMessage, targetWindowOverride) {
356
+ const targetWindow = targetWindowOverride ?? this.targetWindow;
268
357
  if (!targetWindow) {
269
358
  throw new Error(
270
359
  "AppBridge could not resolve a target window. Pass targetWindow explicitly or call setTargetWindow()."
@@ -284,65 +373,25 @@ var AppBridge = class {
284
373
  });
285
374
  targetWindow.postMessage(message, this.targetOrigin);
286
375
  }
376
+ handleRedirectMessage(message) {
377
+ const destination = resolveNavigationDestination(message.payload);
378
+ if (!destination) {
379
+ return false;
380
+ }
381
+ this.navigateSelf(destination);
382
+ return true;
383
+ }
384
+ navigateSelf(destination) {
385
+ if (!this.selfWindow) {
386
+ throw new Error("AppBridge could not resolve a browser window for redirect.");
387
+ }
388
+ this.selfWindow.location.assign(destination);
389
+ }
287
390
  };
288
391
  function createAppBridge(options = {}) {
289
392
  return new AppBridge(options);
290
393
  }
291
394
 
292
- // src/navigation.ts
293
- function normalizeSearch(search) {
294
- if (!search) {
295
- return "";
296
- }
297
- return search.startsWith("?") ? search : `?${search}`;
298
- }
299
- function normalizeHash(hash) {
300
- if (!hash) {
301
- return "";
302
- }
303
- return hash.startsWith("#") ? hash : `#${hash}`;
304
- }
305
- function buildNavigationUpdatePayload(path) {
306
- let pathname = path;
307
- let search = "";
308
- let hash = "";
309
- const hashIndex = pathname.indexOf("#");
310
- if (hashIndex >= 0) {
311
- hash = pathname.slice(hashIndex);
312
- pathname = pathname.slice(0, hashIndex);
313
- }
314
- const searchIndex = pathname.indexOf("?");
315
- if (searchIndex >= 0) {
316
- search = pathname.slice(searchIndex);
317
- pathname = pathname.slice(0, searchIndex);
318
- }
319
- return {
320
- path: `${pathname || "/"}${search}${hash}`,
321
- pathname: pathname || "/",
322
- search,
323
- hash
324
- };
325
- }
326
- function resolveNavigationDestination(payload) {
327
- if (typeof payload === "string") {
328
- return payload;
329
- }
330
- if (!payload || typeof payload !== "object") {
331
- return null;
332
- }
333
- const value = payload;
334
- if (typeof value.path === "string" && value.path) {
335
- return value.path;
336
- }
337
- if (typeof value.href === "string" && value.href) {
338
- return value.href;
339
- }
340
- if (typeof value.pathname !== "string" || !value.pathname) {
341
- return null;
342
- }
343
- return `${value.pathname}${normalizeSearch(value.search ?? "")}${normalizeHash(value.hash ?? "")}`;
344
- }
345
-
346
395
  // src/react.tsx
347
396
  var import_react = require("react");
348
397
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -385,7 +434,6 @@ function AppBridgeProvider({
385
434
  targetWindow
386
435
  });
387
436
  setBridge(nextBridge);
388
- nextBridge.send(readyEventType, readyPayload);
389
437
  return () => {
390
438
  setBridge((currentBridge) => currentBridge === nextBridge ? null : currentBridge);
391
439
  nextBridge.destroy();