@thor-commerce/app-bridge-react 0.3.1 → 0.5.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",
@@ -121,6 +200,13 @@ var AppBridge = class {
121
200
  }
122
201
  });
123
202
  }
203
+ getSessionToken(options = {}) {
204
+ return this.request(
205
+ "thor:session-token:get",
206
+ void 0,
207
+ options
208
+ );
209
+ }
124
210
  on(type, handler) {
125
211
  const handlers = this.eventHandlers.get(type) ?? /* @__PURE__ */ new Set();
126
212
  handlers.add(handler);
@@ -169,6 +255,9 @@ var AppBridge = class {
169
255
  origin: event.origin,
170
256
  rawEvent: event
171
257
  };
258
+ if (message.kind === "event" && message.type === DEFAULT_REDIRECT_EVENT_TYPE && this.handleRedirectMessage(receivedMessage)) {
259
+ return;
260
+ }
172
261
  if (message.kind === "response" && message.replyTo) {
173
262
  this.resolvePendingRequest(message.replyTo, message);
174
263
  return;
@@ -200,22 +289,29 @@ var AppBridge = class {
200
289
  if (!handler) {
201
290
  return;
202
291
  }
292
+ const replyTarget = isMessageTarget(message.rawEvent.source) ? message.rawEvent.source : this.targetWindow;
203
293
  try {
204
294
  const payload = await handler(message.payload, message);
205
- this.postMessage({
206
- kind: "response",
207
- type: message.type,
208
- payload,
209
- replyTo: message.id
210
- });
295
+ this.postMessage(
296
+ {
297
+ kind: "response",
298
+ type: message.type,
299
+ payload,
300
+ replyTo: message.id
301
+ },
302
+ replyTarget
303
+ );
211
304
  } catch (error) {
212
305
  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
- });
306
+ this.postMessage(
307
+ {
308
+ kind: "response",
309
+ type: message.type,
310
+ error: bridgeError,
311
+ replyTo: message.id
312
+ },
313
+ replyTarget
314
+ );
219
315
  }
220
316
  }
221
317
  resolvePendingRequest(messageId, message) {
@@ -231,8 +327,8 @@ var AppBridge = class {
231
327
  }
232
328
  pendingRequest.resolve(message.payload);
233
329
  }
234
- postMessage(partialMessage) {
235
- const targetWindow = this.targetWindow;
330
+ postMessage(partialMessage, targetWindowOverride) {
331
+ const targetWindow = targetWindowOverride ?? this.targetWindow;
236
332
  if (!targetWindow) {
237
333
  throw new Error(
238
334
  "AppBridge could not resolve a target window. Pass targetWindow explicitly or call setTargetWindow()."
@@ -252,65 +348,25 @@ var AppBridge = class {
252
348
  });
253
349
  targetWindow.postMessage(message, this.targetOrigin);
254
350
  }
351
+ handleRedirectMessage(message) {
352
+ const destination = resolveNavigationDestination(message.payload);
353
+ if (!destination) {
354
+ return false;
355
+ }
356
+ this.navigateSelf(destination);
357
+ return true;
358
+ }
359
+ navigateSelf(destination) {
360
+ if (!this.selfWindow) {
361
+ throw new Error("AppBridge could not resolve a browser window for redirect.");
362
+ }
363
+ this.selfWindow.location.assign(destination);
364
+ }
255
365
  };
256
366
  function createAppBridge(options = {}) {
257
367
  return new AppBridge(options);
258
368
  }
259
369
 
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
370
  // src/react.tsx
315
371
  import {
316
372
  createContext,
@@ -404,12 +460,12 @@ function useAppBridge() {
404
460
  }
405
461
 
406
462
  export {
463
+ buildNavigationUpdatePayload,
464
+ resolveNavigationDestination,
407
465
  isBridgeMessage,
408
466
  AppBridge,
409
467
  createAppBridge,
410
- buildNavigationUpdatePayload,
411
- resolveNavigationDestination,
412
468
  AppBridgeProvider,
413
469
  useAppBridge
414
470
  };
415
- //# sourceMappingURL=chunk-LKHY2JKP.js.map
471
+ //# sourceMappingURL=chunk-RWPS2DOE.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 SessionTokenResponse {\n idToken: string;\n exp?: 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 getSessionToken(options: RequestOptions = {}) {\n return this.request<undefined, SessionTokenResponse>(\n \"thor:session-token:get\",\n undefined,\n options\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",
@@ -153,6 +232,13 @@ var AppBridge = class {
153
232
  }
154
233
  });
155
234
  }
235
+ getSessionToken(options = {}) {
236
+ return this.request(
237
+ "thor:session-token:get",
238
+ void 0,
239
+ options
240
+ );
241
+ }
156
242
  on(type, handler) {
157
243
  const handlers = this.eventHandlers.get(type) ?? /* @__PURE__ */ new Set();
158
244
  handlers.add(handler);
@@ -201,6 +287,9 @@ var AppBridge = class {
201
287
  origin: event.origin,
202
288
  rawEvent: event
203
289
  };
290
+ if (message.kind === "event" && message.type === DEFAULT_REDIRECT_EVENT_TYPE && this.handleRedirectMessage(receivedMessage)) {
291
+ return;
292
+ }
204
293
  if (message.kind === "response" && message.replyTo) {
205
294
  this.resolvePendingRequest(message.replyTo, message);
206
295
  return;
@@ -232,22 +321,29 @@ var AppBridge = class {
232
321
  if (!handler) {
233
322
  return;
234
323
  }
324
+ const replyTarget = isMessageTarget(message.rawEvent.source) ? message.rawEvent.source : this.targetWindow;
235
325
  try {
236
326
  const payload = await handler(message.payload, message);
237
- this.postMessage({
238
- kind: "response",
239
- type: message.type,
240
- payload,
241
- replyTo: message.id
242
- });
327
+ this.postMessage(
328
+ {
329
+ kind: "response",
330
+ type: message.type,
331
+ payload,
332
+ replyTo: message.id
333
+ },
334
+ replyTarget
335
+ );
243
336
  } catch (error) {
244
337
  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
- });
338
+ this.postMessage(
339
+ {
340
+ kind: "response",
341
+ type: message.type,
342
+ error: bridgeError,
343
+ replyTo: message.id
344
+ },
345
+ replyTarget
346
+ );
251
347
  }
252
348
  }
253
349
  resolvePendingRequest(messageId, message) {
@@ -263,8 +359,8 @@ var AppBridge = class {
263
359
  }
264
360
  pendingRequest.resolve(message.payload);
265
361
  }
266
- postMessage(partialMessage) {
267
- const targetWindow = this.targetWindow;
362
+ postMessage(partialMessage, targetWindowOverride) {
363
+ const targetWindow = targetWindowOverride ?? this.targetWindow;
268
364
  if (!targetWindow) {
269
365
  throw new Error(
270
366
  "AppBridge could not resolve a target window. Pass targetWindow explicitly or call setTargetWindow()."
@@ -284,65 +380,25 @@ var AppBridge = class {
284
380
  });
285
381
  targetWindow.postMessage(message, this.targetOrigin);
286
382
  }
383
+ handleRedirectMessage(message) {
384
+ const destination = resolveNavigationDestination(message.payload);
385
+ if (!destination) {
386
+ return false;
387
+ }
388
+ this.navigateSelf(destination);
389
+ return true;
390
+ }
391
+ navigateSelf(destination) {
392
+ if (!this.selfWindow) {
393
+ throw new Error("AppBridge could not resolve a browser window for redirect.");
394
+ }
395
+ this.selfWindow.location.assign(destination);
396
+ }
287
397
  };
288
398
  function createAppBridge(options = {}) {
289
399
  return new AppBridge(options);
290
400
  }
291
401
 
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
402
  // src/react.tsx
347
403
  var import_react = require("react");
348
404
  var import_jsx_runtime = require("react/jsx-runtime");