@thor-commerce/app-bridge-react 0.7.3 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/browser.cjs +1034 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +16 -0
- package/dist/browser.d.ts +16 -0
- package/dist/browser.js +37 -0
- package/dist/browser.js.map +1 -0
- package/dist/{chunk-FGZGTJQS.js → chunk-GEBMO3KT.js} +359 -241
- package/dist/chunk-GEBMO3KT.js.map +1 -0
- package/dist/chunk-ORDG3T77.js +82 -0
- package/dist/chunk-ORDG3T77.js.map +1 -0
- package/dist/index.cjs +429 -228
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -128
- package/dist/index.d.ts +4 -128
- package/dist/index.js +14 -4
- package/dist/next.cjs +415 -227
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +1 -0
- package/dist/next.d.ts +1 -0
- package/dist/next.js +4 -2
- package/dist/next.js.map +1 -1
- package/dist/react-router.cjs +415 -227
- package/dist/react-router.cjs.map +1 -1
- package/dist/react-router.d.cts +1 -0
- package/dist/react-router.d.ts +1 -0
- package/dist/react-router.js +4 -2
- package/dist/react-router.js.map +1 -1
- package/dist/runtime-BsE3hxuZ.d.cts +163 -0
- package/dist/runtime-BsE3hxuZ.d.ts +163 -0
- package/dist/thor-app-bridge.js +2 -0
- package/dist/thor-app-bridge.js.map +1 -0
- package/package.json +9 -1
- package/dist/chunk-FGZGTJQS.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -23,8 +23,12 @@ __export(index_exports, {
|
|
|
23
23
|
AppBridge: () => AppBridge,
|
|
24
24
|
AppBridgeProvider: () => AppBridgeProvider,
|
|
25
25
|
EMBEDDED_LAUNCH_PARAMS: () => EMBEDDED_LAUNCH_PARAMS,
|
|
26
|
+
EmbeddedAppRuntime: () => EmbeddedAppRuntime,
|
|
27
|
+
THOR_NAVIGATE_EVENT: () => THOR_NAVIGATE_EVENT,
|
|
26
28
|
buildNavigationUpdatePayload: () => buildNavigationUpdatePayload,
|
|
27
29
|
createAppBridge: () => createAppBridge,
|
|
30
|
+
getEmbeddedAppRuntime: () => getEmbeddedAppRuntime,
|
|
31
|
+
getOrCreateEmbeddedAppRuntime: () => getOrCreateEmbeddedAppRuntime,
|
|
28
32
|
isBridgeMessage: () => isBridgeMessage,
|
|
29
33
|
normalizeBridgeNavigationItem: () => normalizeBridgeNavigationItem,
|
|
30
34
|
normalizeBridgeNavigationItems: () => normalizeBridgeNavigationItems,
|
|
@@ -539,242 +543,218 @@ function createAppBridge(options = {}) {
|
|
|
539
543
|
|
|
540
544
|
// src/react.tsx
|
|
541
545
|
var import_react = require("react");
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
const normalizedNavigationItems = (0, import_react.useMemo)(
|
|
563
|
-
() => normalizeBridgeNavigationItems(navigationItems),
|
|
564
|
-
[navigationItems]
|
|
565
|
-
);
|
|
566
|
-
const sessionTokenCacheRef = (0, import_react.useRef)(null);
|
|
567
|
-
const projectRef = (0, import_react.useRef)(readInitialProject(selfWindow));
|
|
568
|
-
const pendingSessionTokenRef = (0, import_react.useRef)(null);
|
|
569
|
-
(0, import_react.useEffect)(() => {
|
|
570
|
-
onNavigateRef.current = onNavigate;
|
|
571
|
-
}, [onNavigate]);
|
|
572
|
-
(0, import_react.useEffect)(() => {
|
|
573
|
-
if (typeof window === "undefined" && !selfWindow) {
|
|
574
|
-
return;
|
|
546
|
+
|
|
547
|
+
// src/runtime.ts
|
|
548
|
+
var GLOBAL_RUNTIME_KEY = "__thorEmbeddedAppRuntime__";
|
|
549
|
+
var THOR_NAVIGATE_EVENT = "thor:navigate";
|
|
550
|
+
var EmbeddedAppRuntime = class {
|
|
551
|
+
constructor(config) {
|
|
552
|
+
this.currentPath = null;
|
|
553
|
+
this.navigationItems = [];
|
|
554
|
+
this.navigationEventType = "navigation:go";
|
|
555
|
+
this.navigationUpdateEventType = "navigation:update";
|
|
556
|
+
this.readyEventType = "app:ready";
|
|
557
|
+
this.hasSentReady = false;
|
|
558
|
+
this.lastSentNavigationItemsKey = null;
|
|
559
|
+
this.lastReportedPath = null;
|
|
560
|
+
this.pendingHostNavigationPath = null;
|
|
561
|
+
this.sessionTokenCache = null;
|
|
562
|
+
this.pendingSessionToken = null;
|
|
563
|
+
const resolvedWindow = config.selfWindow ?? (typeof window !== "undefined" ? window : void 0);
|
|
564
|
+
if (!resolvedWindow) {
|
|
565
|
+
throw new Error("EmbeddedAppRuntime requires a browser window.");
|
|
575
566
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
567
|
+
this.selfWindow = resolvedWindow;
|
|
568
|
+
this.targetOrigin = config.targetOrigin ?? getReferrerOrigin(resolvedWindow);
|
|
569
|
+
this.clientId = config.clientId;
|
|
570
|
+
this.currentPath = readCurrentPath(resolvedWindow);
|
|
571
|
+
this.project = readInitialProject(resolvedWindow);
|
|
572
|
+
this.readyPayload = config.readyPayload ?? { clientId: config.clientId };
|
|
573
|
+
this.bridge = createAppBridge({
|
|
574
|
+
allowedOrigins: this.targetOrigin ? [this.targetOrigin] : void 0,
|
|
575
|
+
namespace: config.namespace,
|
|
576
|
+
requestTimeoutMs: config.requestTimeoutMs,
|
|
577
|
+
selfWindow: resolvedWindow,
|
|
584
578
|
source: "embedded-app",
|
|
585
579
|
target: "dashboard",
|
|
586
|
-
targetOrigin:
|
|
587
|
-
targetWindow
|
|
580
|
+
targetOrigin: this.targetOrigin,
|
|
581
|
+
targetWindow: config.targetWindow
|
|
588
582
|
});
|
|
589
|
-
|
|
590
|
-
return () => {
|
|
591
|
-
setBridge((currentBridge) => currentBridge === nextBridge ? null : currentBridge);
|
|
592
|
-
nextBridge.destroy();
|
|
593
|
-
};
|
|
594
|
-
}, [
|
|
595
|
-
clientId,
|
|
596
|
-
namespace,
|
|
597
|
-
requestTimeoutMs,
|
|
598
|
-
selfWindow,
|
|
599
|
-
targetOrigin,
|
|
600
|
-
targetWindow
|
|
601
|
-
]);
|
|
602
|
-
(0, import_react.useEffect)(() => {
|
|
603
|
-
if (!bridge || !bridge.hasTargetWindow()) {
|
|
604
|
-
return;
|
|
605
|
-
}
|
|
606
|
-
bridge.send(
|
|
607
|
-
readyEventType,
|
|
608
|
-
readyPayload ?? {
|
|
609
|
-
clientId
|
|
610
|
-
}
|
|
611
|
-
);
|
|
612
|
-
}, [bridge, clientId, readyEventType, readyPayload]);
|
|
613
|
-
(0, import_react.useEffect)(() => {
|
|
614
|
-
if (!bridge || !bridge.hasTargetWindow()) {
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
bridge.send("navigation:items:update", {
|
|
618
|
-
items: normalizedNavigationItems
|
|
619
|
-
});
|
|
620
|
-
}, [bridge, normalizedNavigationItems]);
|
|
621
|
-
(0, import_react.useEffect)(() => {
|
|
622
|
-
if (!bridge || !bridge.hasTargetWindow() || !currentPath) {
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
bridge.send(navigationUpdateEventType, buildNavigationUpdatePayload(currentPath));
|
|
626
|
-
}, [bridge, currentPath, navigationUpdateEventType]);
|
|
627
|
-
(0, import_react.useEffect)(() => {
|
|
628
|
-
if (!bridge || !onNavigate) {
|
|
629
|
-
return;
|
|
630
|
-
}
|
|
631
|
-
return bridge.on(navigationEventType, (message) => {
|
|
583
|
+
this.removeNavigationRequestHandler = this.bridge.on(this.navigationEventType, (message) => {
|
|
632
584
|
const destination = resolveNavigationDestination(message.payload);
|
|
633
585
|
if (!destination) {
|
|
634
586
|
return;
|
|
635
587
|
}
|
|
588
|
+
this.pendingHostNavigationPath = sanitizeEmbeddedAppPath(destination) ?? destination;
|
|
636
589
|
const nextPath = preserveEmbeddedAppLaunchParams(
|
|
637
590
|
destination,
|
|
638
|
-
|
|
591
|
+
this.selfWindow.location.href
|
|
639
592
|
);
|
|
640
|
-
|
|
593
|
+
this.navigate(nextPath ?? destination, message);
|
|
641
594
|
});
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
const nextPath = preserveEmbeddedAppLaunchParams(
|
|
653
|
-
sanitizedPath,
|
|
654
|
-
window.location.href
|
|
655
|
-
);
|
|
656
|
-
onNavigateRef.current?.(nextPath ?? sanitizedPath);
|
|
657
|
-
};
|
|
658
|
-
const handleDocumentClick = (event) => {
|
|
659
|
-
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) {
|
|
660
|
-
return;
|
|
661
|
-
}
|
|
662
|
-
const target = event.target;
|
|
663
|
-
if (!(target instanceof Element)) {
|
|
664
|
-
return;
|
|
665
|
-
}
|
|
666
|
-
const anchor = target.closest("a[href]");
|
|
667
|
-
if (!(anchor instanceof HTMLAnchorElement)) {
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
if (anchor.hasAttribute("download")) {
|
|
671
|
-
return;
|
|
595
|
+
this.removeNavigationInterceptors = installNavigationInterceptors({
|
|
596
|
+
bridge: this.bridge,
|
|
597
|
+
selfWindow: this.selfWindow,
|
|
598
|
+
navigate: (path) => this.navigate(path)
|
|
599
|
+
});
|
|
600
|
+
this.removeHistoryObserver = installHistoryObserver({
|
|
601
|
+
selfWindow: this.selfWindow,
|
|
602
|
+
onChange: (path) => {
|
|
603
|
+
this.currentPath = path;
|
|
604
|
+
this.syncBridgeState();
|
|
672
605
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
606
|
+
});
|
|
607
|
+
this.restoreFetch = installFetchInterceptor({
|
|
608
|
+
bridge: this.bridge,
|
|
609
|
+
getClientId: () => this.clientId,
|
|
610
|
+
getProject: () => this.project,
|
|
611
|
+
setProject: (project) => {
|
|
612
|
+
this.project = project;
|
|
613
|
+
},
|
|
614
|
+
readCachedToken: () => this.sessionTokenCache,
|
|
615
|
+
writeCachedToken: (token) => {
|
|
616
|
+
this.sessionTokenCache = token;
|
|
617
|
+
},
|
|
618
|
+
readPendingToken: () => this.pendingSessionToken,
|
|
619
|
+
writePendingToken: (token) => {
|
|
620
|
+
this.pendingSessionToken = token;
|
|
677
621
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
622
|
+
});
|
|
623
|
+
this.update(config);
|
|
624
|
+
}
|
|
625
|
+
update(config) {
|
|
626
|
+
if (config.clientId) {
|
|
627
|
+
this.clientId = config.clientId;
|
|
628
|
+
if (!this.readyPayload || typeof this.readyPayload === "object" && this.readyPayload !== null && "clientId" in this.readyPayload) {
|
|
629
|
+
this.readyPayload = config.readyPayload ?? { clientId: config.clientId };
|
|
682
630
|
}
|
|
683
|
-
|
|
631
|
+
}
|
|
632
|
+
if (config.currentPath !== void 0) {
|
|
633
|
+
this.currentPath = config.currentPath;
|
|
634
|
+
}
|
|
635
|
+
if (config.navigationItems !== void 0) {
|
|
636
|
+
this.navigationItems = normalizeBridgeNavigationItems(config.navigationItems);
|
|
637
|
+
}
|
|
638
|
+
if (config.onNavigate !== void 0) {
|
|
639
|
+
this.onNavigate = config.onNavigate;
|
|
640
|
+
}
|
|
641
|
+
if (config.readyEventType) {
|
|
642
|
+
this.readyEventType = config.readyEventType;
|
|
643
|
+
}
|
|
644
|
+
if (config.readyPayload !== void 0) {
|
|
645
|
+
this.readyPayload = config.readyPayload;
|
|
646
|
+
}
|
|
647
|
+
if (config.targetWindow !== void 0) {
|
|
648
|
+
this.bridge.setTargetWindow(config.targetWindow ?? null);
|
|
649
|
+
}
|
|
650
|
+
this.syncBridgeState();
|
|
651
|
+
}
|
|
652
|
+
clearNavigationHandler() {
|
|
653
|
+
this.onNavigate = void 0;
|
|
654
|
+
}
|
|
655
|
+
destroy() {
|
|
656
|
+
this.removeNavigationRequestHandler?.();
|
|
657
|
+
this.removeNavigationInterceptors();
|
|
658
|
+
this.removeHistoryObserver();
|
|
659
|
+
this.restoreFetch();
|
|
660
|
+
this.bridge.destroy();
|
|
661
|
+
}
|
|
662
|
+
syncBridgeState() {
|
|
663
|
+
if (!this.bridge.hasTargetWindow()) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (!this.hasSentReady) {
|
|
667
|
+
this.bridge.send(this.readyEventType, this.readyPayload);
|
|
668
|
+
this.hasSentReady = true;
|
|
669
|
+
}
|
|
670
|
+
const navigationItemsKey = JSON.stringify(this.navigationItems);
|
|
671
|
+
if (navigationItemsKey !== this.lastSentNavigationItemsKey) {
|
|
672
|
+
this.bridge.send("navigation:items:update", {
|
|
673
|
+
items: this.navigationItems
|
|
674
|
+
});
|
|
675
|
+
this.lastSentNavigationItemsKey = navigationItemsKey;
|
|
676
|
+
}
|
|
677
|
+
if (this.currentPath) {
|
|
678
|
+
if (this.currentPath === this.pendingHostNavigationPath) {
|
|
679
|
+
this.pendingHostNavigationPath = null;
|
|
680
|
+
this.lastReportedPath = this.currentPath;
|
|
684
681
|
return;
|
|
685
682
|
}
|
|
686
|
-
|
|
687
|
-
if (!nextPath) {
|
|
683
|
+
if (this.currentPath === this.lastReportedPath) {
|
|
688
684
|
return;
|
|
689
685
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
if (targetName === "_top" || targetName === "_parent") {
|
|
701
|
-
bridge.redirectToRemote(new URL(href, window.location.href).toString());
|
|
702
|
-
return null;
|
|
703
|
-
}
|
|
704
|
-
if (!targetName || targetName === "_self") {
|
|
705
|
-
const nextPath = resolveLocalNavigationPath(href, window.location.origin);
|
|
706
|
-
if (nextPath) {
|
|
707
|
-
handleLocalNavigation(nextPath);
|
|
708
|
-
return window;
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
return originalOpen(url, target, features);
|
|
712
|
-
};
|
|
713
|
-
document.addEventListener("click", handleDocumentClick, true);
|
|
714
|
-
return () => {
|
|
715
|
-
document.removeEventListener("click", handleDocumentClick, true);
|
|
716
|
-
window.open = originalOpen;
|
|
717
|
-
};
|
|
718
|
-
}, [bridge, onNavigate]);
|
|
719
|
-
(0, import_react.useEffect)(() => {
|
|
720
|
-
if (!bridge || typeof window === "undefined") {
|
|
686
|
+
this.bridge.send(
|
|
687
|
+
this.navigationUpdateEventType,
|
|
688
|
+
buildNavigationUpdatePayload(this.currentPath)
|
|
689
|
+
);
|
|
690
|
+
this.lastReportedPath = this.currentPath;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
navigate(path, message) {
|
|
694
|
+
if (this.onNavigate) {
|
|
695
|
+
this.onNavigate(path, message);
|
|
721
696
|
return;
|
|
722
697
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const sessionToken = await getSessionToken({
|
|
740
|
-
bridge,
|
|
741
|
-
clientId,
|
|
742
|
-
pendingSessionTokenRef,
|
|
743
|
-
projectRef,
|
|
744
|
-
sessionTokenCacheRef
|
|
745
|
-
});
|
|
746
|
-
nextHeaders.set("Authorization", `Bearer ${sessionToken}`);
|
|
747
|
-
} catch {
|
|
748
|
-
if (!projectRef.current) {
|
|
749
|
-
throw new Error("Missing Thor embedded session token");
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
if (!nextHeaders.has("X-Requested-With")) {
|
|
753
|
-
nextHeaders.set("X-Requested-With", "XMLHttpRequest");
|
|
754
|
-
}
|
|
755
|
-
if (input instanceof Request) {
|
|
756
|
-
return originalFetch(
|
|
757
|
-
new Request(input, {
|
|
758
|
-
headers: nextHeaders
|
|
759
|
-
})
|
|
760
|
-
);
|
|
761
|
-
}
|
|
762
|
-
return originalFetch(input, {
|
|
763
|
-
...init,
|
|
764
|
-
headers: nextHeaders
|
|
765
|
-
});
|
|
766
|
-
};
|
|
767
|
-
globalThis.fetch = interceptedFetch;
|
|
768
|
-
window.fetch = interceptedFetch;
|
|
769
|
-
return () => {
|
|
770
|
-
globalThis.fetch = originalFetch;
|
|
771
|
-
window.fetch = originalFetch;
|
|
772
|
-
};
|
|
773
|
-
}, [bridge, clientId]);
|
|
774
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppBridgeContext.Provider, { value: bridge, children });
|
|
698
|
+
if (dispatchNavigateEvent(this.selfWindow.document, path)) {
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
this.selfWindow.location.assign(path);
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
function getOrCreateEmbeddedAppRuntime(config) {
|
|
705
|
+
const globalScope = globalThis;
|
|
706
|
+
const existingRuntime = globalScope[GLOBAL_RUNTIME_KEY];
|
|
707
|
+
if (existingRuntime) {
|
|
708
|
+
existingRuntime.update(config);
|
|
709
|
+
return existingRuntime;
|
|
710
|
+
}
|
|
711
|
+
const runtime = new EmbeddedAppRuntime(config);
|
|
712
|
+
globalScope[GLOBAL_RUNTIME_KEY] = runtime;
|
|
713
|
+
return runtime;
|
|
775
714
|
}
|
|
776
|
-
function
|
|
777
|
-
return
|
|
715
|
+
function getEmbeddedAppRuntime() {
|
|
716
|
+
return globalThis[GLOBAL_RUNTIME_KEY] ?? null;
|
|
717
|
+
}
|
|
718
|
+
function dispatchNavigateEvent(document, path) {
|
|
719
|
+
const event = new CustomEvent(THOR_NAVIGATE_EVENT, {
|
|
720
|
+
cancelable: true,
|
|
721
|
+
detail: { path }
|
|
722
|
+
});
|
|
723
|
+
document.dispatchEvent(event);
|
|
724
|
+
return event.defaultPrevented;
|
|
725
|
+
}
|
|
726
|
+
function readCurrentPath(selfWindow) {
|
|
727
|
+
return sanitizeEmbeddedAppPath(
|
|
728
|
+
`${selfWindow.location.pathname}${selfWindow.location.search}${selfWindow.location.hash}`
|
|
729
|
+
) ?? null;
|
|
730
|
+
}
|
|
731
|
+
function installHistoryObserver({
|
|
732
|
+
selfWindow,
|
|
733
|
+
onChange
|
|
734
|
+
}) {
|
|
735
|
+
const history = selfWindow.history;
|
|
736
|
+
const originalPushState = history.pushState;
|
|
737
|
+
const originalReplaceState = history.replaceState;
|
|
738
|
+
const notify = () => {
|
|
739
|
+
onChange(readCurrentPath(selfWindow));
|
|
740
|
+
};
|
|
741
|
+
history.pushState = function(...args) {
|
|
742
|
+
originalPushState.apply(history, args);
|
|
743
|
+
notify();
|
|
744
|
+
};
|
|
745
|
+
history.replaceState = function(...args) {
|
|
746
|
+
originalReplaceState.apply(history, args);
|
|
747
|
+
notify();
|
|
748
|
+
};
|
|
749
|
+
selfWindow.addEventListener("popstate", notify);
|
|
750
|
+
selfWindow.addEventListener("hashchange", notify);
|
|
751
|
+
notify();
|
|
752
|
+
return () => {
|
|
753
|
+
history.pushState = originalPushState;
|
|
754
|
+
history.replaceState = originalReplaceState;
|
|
755
|
+
selfWindow.removeEventListener("popstate", notify);
|
|
756
|
+
selfWindow.removeEventListener("hashchange", notify);
|
|
757
|
+
};
|
|
778
758
|
}
|
|
779
759
|
function getReferrerOrigin(explicitWindow) {
|
|
780
760
|
const resolvedWindow = explicitWindow ?? (typeof window !== "undefined" ? window : void 0);
|
|
@@ -788,40 +768,190 @@ function getReferrerOrigin(explicitWindow) {
|
|
|
788
768
|
return void 0;
|
|
789
769
|
}
|
|
790
770
|
}
|
|
771
|
+
function installNavigationInterceptors({
|
|
772
|
+
bridge,
|
|
773
|
+
selfWindow,
|
|
774
|
+
navigate
|
|
775
|
+
}) {
|
|
776
|
+
const document = selfWindow.document;
|
|
777
|
+
const handleLocalNavigation = (path) => {
|
|
778
|
+
const sanitizedPath = sanitizeEmbeddedAppPath(path);
|
|
779
|
+
if (!sanitizedPath) {
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
const nextPath = preserveEmbeddedAppLaunchParams(
|
|
783
|
+
sanitizedPath,
|
|
784
|
+
selfWindow.location.href
|
|
785
|
+
);
|
|
786
|
+
navigate(nextPath ?? sanitizedPath);
|
|
787
|
+
};
|
|
788
|
+
const handleDocumentClick = (event) => {
|
|
789
|
+
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) {
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
const target = event.target;
|
|
793
|
+
if (!(target instanceof Element)) {
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
const anchor = target.closest("a[href]");
|
|
797
|
+
if (!(anchor instanceof HTMLAnchorElement)) {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
if (anchor.hasAttribute("download")) {
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
const targetWindow = anchor.target.toLowerCase();
|
|
804
|
+
const href = anchor.getAttribute("href");
|
|
805
|
+
if (!href) {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
if (targetWindow === "_top" || targetWindow === "_parent") {
|
|
809
|
+
event.preventDefault();
|
|
810
|
+
bridge.redirectToRemote(anchor.href);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
if (targetWindow && targetWindow !== "_self") {
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
const nextPath = resolveLocalNavigationPath(href, selfWindow.location.origin);
|
|
817
|
+
if (!nextPath) {
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
event.preventDefault();
|
|
821
|
+
handleLocalNavigation(nextPath);
|
|
822
|
+
};
|
|
823
|
+
const originalOpen = selfWindow.open.bind(selfWindow);
|
|
824
|
+
selfWindow.open = (url, target, features) => {
|
|
825
|
+
if (url == null) {
|
|
826
|
+
return originalOpen(url, target, features);
|
|
827
|
+
}
|
|
828
|
+
const href = typeof url === "string" ? url : url.toString();
|
|
829
|
+
const targetName = (target ?? "").toLowerCase();
|
|
830
|
+
if (targetName === "_top" || targetName === "_parent") {
|
|
831
|
+
bridge.redirectToRemote(new URL(href, selfWindow.location.href).toString());
|
|
832
|
+
return null;
|
|
833
|
+
}
|
|
834
|
+
if (!targetName || targetName === "_self") {
|
|
835
|
+
const nextPath = resolveLocalNavigationPath(
|
|
836
|
+
href,
|
|
837
|
+
selfWindow.location.origin
|
|
838
|
+
);
|
|
839
|
+
if (nextPath) {
|
|
840
|
+
handleLocalNavigation(nextPath);
|
|
841
|
+
return selfWindow;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return originalOpen(url, target, features);
|
|
845
|
+
};
|
|
846
|
+
document.addEventListener("click", handleDocumentClick, true);
|
|
847
|
+
return () => {
|
|
848
|
+
document.removeEventListener("click", handleDocumentClick, true);
|
|
849
|
+
selfWindow.open = originalOpen;
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
function installFetchInterceptor({
|
|
853
|
+
bridge,
|
|
854
|
+
getClientId,
|
|
855
|
+
getProject,
|
|
856
|
+
setProject,
|
|
857
|
+
readCachedToken,
|
|
858
|
+
writeCachedToken,
|
|
859
|
+
readPendingToken,
|
|
860
|
+
writePendingToken
|
|
861
|
+
}) {
|
|
862
|
+
if (typeof window === "undefined") {
|
|
863
|
+
return () => {
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
const originalFetch = globalThis.fetch.bind(globalThis);
|
|
867
|
+
const interceptedFetch = async (input, init) => {
|
|
868
|
+
if (!shouldAttachSessionToken(input)) {
|
|
869
|
+
return originalFetch(input, init);
|
|
870
|
+
}
|
|
871
|
+
const existingAuthorization = getExistingAuthorization(input, init);
|
|
872
|
+
if (existingAuthorization) {
|
|
873
|
+
return originalFetch(input, init);
|
|
874
|
+
}
|
|
875
|
+
const nextHeaders = new Headers(
|
|
876
|
+
input instanceof Request ? input.headers : init?.headers
|
|
877
|
+
);
|
|
878
|
+
try {
|
|
879
|
+
const sessionToken = await getSessionToken({
|
|
880
|
+
bridge,
|
|
881
|
+
clientId: getClientId(),
|
|
882
|
+
project: getProject,
|
|
883
|
+
setProject,
|
|
884
|
+
readCachedToken,
|
|
885
|
+
writeCachedToken,
|
|
886
|
+
readPendingToken,
|
|
887
|
+
writePendingToken
|
|
888
|
+
});
|
|
889
|
+
nextHeaders.set("Authorization", `Bearer ${sessionToken}`);
|
|
890
|
+
} catch {
|
|
891
|
+
if (!getProject()) {
|
|
892
|
+
throw new Error("Missing Thor embedded session token");
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
if (!nextHeaders.has("X-Requested-With")) {
|
|
896
|
+
nextHeaders.set("X-Requested-With", "XMLHttpRequest");
|
|
897
|
+
}
|
|
898
|
+
if (input instanceof Request) {
|
|
899
|
+
return originalFetch(
|
|
900
|
+
new Request(input, {
|
|
901
|
+
headers: nextHeaders
|
|
902
|
+
})
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
return originalFetch(input, {
|
|
906
|
+
...init,
|
|
907
|
+
headers: nextHeaders
|
|
908
|
+
});
|
|
909
|
+
};
|
|
910
|
+
globalThis.fetch = interceptedFetch;
|
|
911
|
+
window.fetch = interceptedFetch;
|
|
912
|
+
return () => {
|
|
913
|
+
globalThis.fetch = originalFetch;
|
|
914
|
+
window.fetch = originalFetch;
|
|
915
|
+
};
|
|
916
|
+
}
|
|
791
917
|
async function getSessionToken({
|
|
792
918
|
bridge,
|
|
793
919
|
clientId,
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
920
|
+
project,
|
|
921
|
+
setProject,
|
|
922
|
+
readCachedToken,
|
|
923
|
+
writeCachedToken,
|
|
924
|
+
readPendingToken,
|
|
925
|
+
writePendingToken
|
|
797
926
|
}) {
|
|
798
|
-
const cachedToken =
|
|
927
|
+
const cachedToken = readCachedToken();
|
|
799
928
|
if (cachedToken && !isExpired(cachedToken.expiresAt)) {
|
|
800
929
|
return cachedToken.token;
|
|
801
930
|
}
|
|
802
|
-
|
|
803
|
-
|
|
931
|
+
const pendingToken = readPendingToken();
|
|
932
|
+
if (pendingToken) {
|
|
933
|
+
return pendingToken;
|
|
804
934
|
}
|
|
805
|
-
const
|
|
935
|
+
const nextPendingToken = bridge.getSessionToken({ clientId }).then((response) => {
|
|
806
936
|
const token = response.sessionToken ?? response.idToken;
|
|
807
937
|
if (!token) {
|
|
808
938
|
throw new Error("Missing Thor embedded session token");
|
|
809
939
|
}
|
|
810
940
|
if (response.project) {
|
|
811
|
-
|
|
941
|
+
setProject(response.project);
|
|
812
942
|
}
|
|
813
|
-
|
|
943
|
+
writeCachedToken({
|
|
814
944
|
token,
|
|
815
945
|
expiresAt: normalizeTokenExpiry(token, response.exp)
|
|
816
|
-
};
|
|
817
|
-
|
|
946
|
+
});
|
|
947
|
+
writePendingToken(null);
|
|
818
948
|
return token;
|
|
819
949
|
}).catch((error) => {
|
|
820
|
-
|
|
950
|
+
writePendingToken(null);
|
|
821
951
|
throw error;
|
|
822
952
|
});
|
|
823
|
-
|
|
824
|
-
return
|
|
953
|
+
writePendingToken(nextPendingToken);
|
|
954
|
+
return nextPendingToken;
|
|
825
955
|
}
|
|
826
956
|
function shouldAttachSessionToken(input) {
|
|
827
957
|
if (typeof window === "undefined") {
|
|
@@ -887,13 +1017,84 @@ function readInitialProject(explicitWindow) {
|
|
|
887
1017
|
return null;
|
|
888
1018
|
}
|
|
889
1019
|
}
|
|
1020
|
+
|
|
1021
|
+
// src/react.tsx
|
|
1022
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1023
|
+
var AppBridgeContext = (0, import_react.createContext)(null);
|
|
1024
|
+
function AppBridgeProvider({
|
|
1025
|
+
children,
|
|
1026
|
+
clientId,
|
|
1027
|
+
currentPath,
|
|
1028
|
+
navigationItems,
|
|
1029
|
+
navigationEventType = "navigation:go",
|
|
1030
|
+
navigationUpdateEventType = "navigation:update",
|
|
1031
|
+
namespace,
|
|
1032
|
+
onNavigate,
|
|
1033
|
+
readyEventType = "app:ready",
|
|
1034
|
+
readyPayload,
|
|
1035
|
+
requestTimeoutMs,
|
|
1036
|
+
selfWindow,
|
|
1037
|
+
targetOrigin,
|
|
1038
|
+
targetWindow
|
|
1039
|
+
}) {
|
|
1040
|
+
const [bridge, setBridge] = (0, import_react.useState)(null);
|
|
1041
|
+
(0, import_react.useEffect)(() => {
|
|
1042
|
+
if (typeof window === "undefined" && !selfWindow) {
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
const runtime = getOrCreateEmbeddedAppRuntime({
|
|
1046
|
+
clientId,
|
|
1047
|
+
currentPath,
|
|
1048
|
+
navigationEventType,
|
|
1049
|
+
navigationItems,
|
|
1050
|
+
navigationUpdateEventType,
|
|
1051
|
+
namespace,
|
|
1052
|
+
onNavigate,
|
|
1053
|
+
readyEventType,
|
|
1054
|
+
readyPayload,
|
|
1055
|
+
requestTimeoutMs,
|
|
1056
|
+
selfWindow,
|
|
1057
|
+
targetOrigin,
|
|
1058
|
+
targetWindow
|
|
1059
|
+
});
|
|
1060
|
+
setBridge(runtime.bridge);
|
|
1061
|
+
return () => {
|
|
1062
|
+
runtime.clearNavigationHandler();
|
|
1063
|
+
setBridge(
|
|
1064
|
+
(currentBridge) => currentBridge === runtime.bridge ? null : currentBridge
|
|
1065
|
+
);
|
|
1066
|
+
};
|
|
1067
|
+
}, [
|
|
1068
|
+
clientId,
|
|
1069
|
+
currentPath,
|
|
1070
|
+
navigationEventType,
|
|
1071
|
+
navigationItems,
|
|
1072
|
+
navigationUpdateEventType,
|
|
1073
|
+
namespace,
|
|
1074
|
+
onNavigate,
|
|
1075
|
+
readyEventType,
|
|
1076
|
+
readyPayload,
|
|
1077
|
+
requestTimeoutMs,
|
|
1078
|
+
selfWindow,
|
|
1079
|
+
targetOrigin,
|
|
1080
|
+
targetWindow
|
|
1081
|
+
]);
|
|
1082
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppBridgeContext.Provider, { value: bridge, children });
|
|
1083
|
+
}
|
|
1084
|
+
function useAppBridge() {
|
|
1085
|
+
return (0, import_react.useContext)(AppBridgeContext);
|
|
1086
|
+
}
|
|
890
1087
|
// Annotate the CommonJS export names for ESM import in node:
|
|
891
1088
|
0 && (module.exports = {
|
|
892
1089
|
AppBridge,
|
|
893
1090
|
AppBridgeProvider,
|
|
894
1091
|
EMBEDDED_LAUNCH_PARAMS,
|
|
1092
|
+
EmbeddedAppRuntime,
|
|
1093
|
+
THOR_NAVIGATE_EVENT,
|
|
895
1094
|
buildNavigationUpdatePayload,
|
|
896
1095
|
createAppBridge,
|
|
1096
|
+
getEmbeddedAppRuntime,
|
|
1097
|
+
getOrCreateEmbeddedAppRuntime,
|
|
897
1098
|
isBridgeMessage,
|
|
898
1099
|
normalizeBridgeNavigationItem,
|
|
899
1100
|
normalizeBridgeNavigationItems,
|