@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.
- package/dist/{chunk-LKHY2JKP.js → chunk-RWPS2DOE.js} +127 -71
- package/dist/chunk-RWPS2DOE.js.map +1 -0
- package/dist/index.cjs +124 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -17
- package/dist/index.d.ts +29 -17
- package/dist/index.js +1 -1
- package/dist/next.cjs +128 -72
- package/dist/next.cjs.map +1 -1
- package/dist/next.js +1 -1
- package/dist/react-router.cjs +124 -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",
|
|
@@ -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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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");
|