@thor-commerce/app-bridge-react 0.3.1 → 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.
- package/dist/{chunk-LKHY2JKP.js → chunk-EASM25ZU.js} +120 -71
- package/dist/chunk-EASM25ZU.js.map +1 -0
- package/dist/index.cjs +117 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -17
- package/dist/index.d.ts +24 -17
- package/dist/index.js +1 -1
- package/dist/next.cjs +121 -72
- package/dist/next.cjs.map +1 -1
- package/dist/next.js +1 -1
- package/dist/react-router.cjs +117 -68
- package/dist/react-router.cjs.map +1 -1
- package/dist/react-router.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-LKHY2JKP.js.map +0 -1
|
@@ -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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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,
|
|
@@ -404,12 +453,12 @@ function useAppBridge() {
|
|
|
404
453
|
}
|
|
405
454
|
|
|
406
455
|
export {
|
|
456
|
+
buildNavigationUpdatePayload,
|
|
457
|
+
resolveNavigationDestination,
|
|
407
458
|
isBridgeMessage,
|
|
408
459
|
AppBridge,
|
|
409
460
|
createAppBridge,
|
|
410
|
-
buildNavigationUpdatePayload,
|
|
411
|
-
resolveNavigationDestination,
|
|
412
461
|
AppBridgeProvider,
|
|
413
462
|
useAppBridge
|
|
414
463
|
};
|
|
415
|
-
//# sourceMappingURL=chunk-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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");
|