@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/react-router.cjs
CHANGED
|
@@ -530,240 +530,214 @@ function createAppBridge(options = {}) {
|
|
|
530
530
|
return new AppBridge(options);
|
|
531
531
|
}
|
|
532
532
|
|
|
533
|
-
// src/
|
|
534
|
-
var
|
|
535
|
-
var
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const [bridge, setBridge] = (0, import_react.useState)(null);
|
|
553
|
-
const onNavigateRef = (0, import_react.useRef)(onNavigate);
|
|
554
|
-
const normalizedNavigationItems = (0, import_react.useMemo)(
|
|
555
|
-
() => normalizeBridgeNavigationItems(navigationItems),
|
|
556
|
-
[navigationItems]
|
|
557
|
-
);
|
|
558
|
-
const sessionTokenCacheRef = (0, import_react.useRef)(null);
|
|
559
|
-
const projectRef = (0, import_react.useRef)(readInitialProject(selfWindow));
|
|
560
|
-
const pendingSessionTokenRef = (0, import_react.useRef)(null);
|
|
561
|
-
(0, import_react.useEffect)(() => {
|
|
562
|
-
onNavigateRef.current = onNavigate;
|
|
563
|
-
}, [onNavigate]);
|
|
564
|
-
(0, import_react.useEffect)(() => {
|
|
565
|
-
if (typeof window === "undefined" && !selfWindow) {
|
|
566
|
-
return;
|
|
533
|
+
// src/runtime.ts
|
|
534
|
+
var GLOBAL_RUNTIME_KEY = "__thorEmbeddedAppRuntime__";
|
|
535
|
+
var THOR_NAVIGATE_EVENT = "thor:navigate";
|
|
536
|
+
var EmbeddedAppRuntime = class {
|
|
537
|
+
constructor(config) {
|
|
538
|
+
this.currentPath = null;
|
|
539
|
+
this.navigationItems = [];
|
|
540
|
+
this.navigationEventType = "navigation:go";
|
|
541
|
+
this.navigationUpdateEventType = "navigation:update";
|
|
542
|
+
this.readyEventType = "app:ready";
|
|
543
|
+
this.hasSentReady = false;
|
|
544
|
+
this.lastSentNavigationItemsKey = null;
|
|
545
|
+
this.lastReportedPath = null;
|
|
546
|
+
this.pendingHostNavigationPath = null;
|
|
547
|
+
this.sessionTokenCache = null;
|
|
548
|
+
this.pendingSessionToken = null;
|
|
549
|
+
const resolvedWindow = config.selfWindow ?? (typeof window !== "undefined" ? window : void 0);
|
|
550
|
+
if (!resolvedWindow) {
|
|
551
|
+
throw new Error("EmbeddedAppRuntime requires a browser window.");
|
|
567
552
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
553
|
+
this.selfWindow = resolvedWindow;
|
|
554
|
+
this.targetOrigin = config.targetOrigin ?? getReferrerOrigin(resolvedWindow);
|
|
555
|
+
this.clientId = config.clientId;
|
|
556
|
+
this.currentPath = readCurrentPath(resolvedWindow);
|
|
557
|
+
this.project = readInitialProject(resolvedWindow);
|
|
558
|
+
this.readyPayload = config.readyPayload ?? { clientId: config.clientId };
|
|
559
|
+
this.bridge = createAppBridge({
|
|
560
|
+
allowedOrigins: this.targetOrigin ? [this.targetOrigin] : void 0,
|
|
561
|
+
namespace: config.namespace,
|
|
562
|
+
requestTimeoutMs: config.requestTimeoutMs,
|
|
563
|
+
selfWindow: resolvedWindow,
|
|
576
564
|
source: "embedded-app",
|
|
577
565
|
target: "dashboard",
|
|
578
|
-
targetOrigin:
|
|
579
|
-
targetWindow
|
|
566
|
+
targetOrigin: this.targetOrigin,
|
|
567
|
+
targetWindow: config.targetWindow
|
|
580
568
|
});
|
|
581
|
-
|
|
582
|
-
return () => {
|
|
583
|
-
setBridge((currentBridge) => currentBridge === nextBridge ? null : currentBridge);
|
|
584
|
-
nextBridge.destroy();
|
|
585
|
-
};
|
|
586
|
-
}, [
|
|
587
|
-
clientId,
|
|
588
|
-
namespace,
|
|
589
|
-
requestTimeoutMs,
|
|
590
|
-
selfWindow,
|
|
591
|
-
targetOrigin,
|
|
592
|
-
targetWindow
|
|
593
|
-
]);
|
|
594
|
-
(0, import_react.useEffect)(() => {
|
|
595
|
-
if (!bridge || !bridge.hasTargetWindow()) {
|
|
596
|
-
return;
|
|
597
|
-
}
|
|
598
|
-
bridge.send(
|
|
599
|
-
readyEventType,
|
|
600
|
-
readyPayload ?? {
|
|
601
|
-
clientId
|
|
602
|
-
}
|
|
603
|
-
);
|
|
604
|
-
}, [bridge, clientId, readyEventType, readyPayload]);
|
|
605
|
-
(0, import_react.useEffect)(() => {
|
|
606
|
-
if (!bridge || !bridge.hasTargetWindow()) {
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
bridge.send("navigation:items:update", {
|
|
610
|
-
items: normalizedNavigationItems
|
|
611
|
-
});
|
|
612
|
-
}, [bridge, normalizedNavigationItems]);
|
|
613
|
-
(0, import_react.useEffect)(() => {
|
|
614
|
-
if (!bridge || !bridge.hasTargetWindow() || !currentPath) {
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
bridge.send(navigationUpdateEventType, buildNavigationUpdatePayload(currentPath));
|
|
618
|
-
}, [bridge, currentPath, navigationUpdateEventType]);
|
|
619
|
-
(0, import_react.useEffect)(() => {
|
|
620
|
-
if (!bridge || !onNavigate) {
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
return bridge.on(navigationEventType, (message) => {
|
|
569
|
+
this.removeNavigationRequestHandler = this.bridge.on(this.navigationEventType, (message) => {
|
|
624
570
|
const destination = resolveNavigationDestination(message.payload);
|
|
625
571
|
if (!destination) {
|
|
626
572
|
return;
|
|
627
573
|
}
|
|
574
|
+
this.pendingHostNavigationPath = sanitizeEmbeddedAppPath(destination) ?? destination;
|
|
628
575
|
const nextPath = preserveEmbeddedAppLaunchParams(
|
|
629
576
|
destination,
|
|
630
|
-
|
|
577
|
+
this.selfWindow.location.href
|
|
631
578
|
);
|
|
632
|
-
|
|
579
|
+
this.navigate(nextPath ?? destination, message);
|
|
633
580
|
});
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
const nextPath = preserveEmbeddedAppLaunchParams(
|
|
645
|
-
sanitizedPath,
|
|
646
|
-
window.location.href
|
|
647
|
-
);
|
|
648
|
-
onNavigateRef.current?.(nextPath ?? sanitizedPath);
|
|
649
|
-
};
|
|
650
|
-
const handleDocumentClick = (event) => {
|
|
651
|
-
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) {
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
|
-
const target = event.target;
|
|
655
|
-
if (!(target instanceof Element)) {
|
|
656
|
-
return;
|
|
657
|
-
}
|
|
658
|
-
const anchor = target.closest("a[href]");
|
|
659
|
-
if (!(anchor instanceof HTMLAnchorElement)) {
|
|
660
|
-
return;
|
|
661
|
-
}
|
|
662
|
-
if (anchor.hasAttribute("download")) {
|
|
663
|
-
return;
|
|
581
|
+
this.removeNavigationInterceptors = installNavigationInterceptors({
|
|
582
|
+
bridge: this.bridge,
|
|
583
|
+
selfWindow: this.selfWindow,
|
|
584
|
+
navigate: (path) => this.navigate(path)
|
|
585
|
+
});
|
|
586
|
+
this.removeHistoryObserver = installHistoryObserver({
|
|
587
|
+
selfWindow: this.selfWindow,
|
|
588
|
+
onChange: (path) => {
|
|
589
|
+
this.currentPath = path;
|
|
590
|
+
this.syncBridgeState();
|
|
664
591
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
592
|
+
});
|
|
593
|
+
this.restoreFetch = installFetchInterceptor({
|
|
594
|
+
bridge: this.bridge,
|
|
595
|
+
getClientId: () => this.clientId,
|
|
596
|
+
getProject: () => this.project,
|
|
597
|
+
setProject: (project) => {
|
|
598
|
+
this.project = project;
|
|
599
|
+
},
|
|
600
|
+
readCachedToken: () => this.sessionTokenCache,
|
|
601
|
+
writeCachedToken: (token) => {
|
|
602
|
+
this.sessionTokenCache = token;
|
|
603
|
+
},
|
|
604
|
+
readPendingToken: () => this.pendingSessionToken,
|
|
605
|
+
writePendingToken: (token) => {
|
|
606
|
+
this.pendingSessionToken = token;
|
|
669
607
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
608
|
+
});
|
|
609
|
+
this.update(config);
|
|
610
|
+
}
|
|
611
|
+
update(config) {
|
|
612
|
+
if (config.clientId) {
|
|
613
|
+
this.clientId = config.clientId;
|
|
614
|
+
if (!this.readyPayload || typeof this.readyPayload === "object" && this.readyPayload !== null && "clientId" in this.readyPayload) {
|
|
615
|
+
this.readyPayload = config.readyPayload ?? { clientId: config.clientId };
|
|
674
616
|
}
|
|
675
|
-
|
|
617
|
+
}
|
|
618
|
+
if (config.currentPath !== void 0) {
|
|
619
|
+
this.currentPath = config.currentPath;
|
|
620
|
+
}
|
|
621
|
+
if (config.navigationItems !== void 0) {
|
|
622
|
+
this.navigationItems = normalizeBridgeNavigationItems(config.navigationItems);
|
|
623
|
+
}
|
|
624
|
+
if (config.onNavigate !== void 0) {
|
|
625
|
+
this.onNavigate = config.onNavigate;
|
|
626
|
+
}
|
|
627
|
+
if (config.readyEventType) {
|
|
628
|
+
this.readyEventType = config.readyEventType;
|
|
629
|
+
}
|
|
630
|
+
if (config.readyPayload !== void 0) {
|
|
631
|
+
this.readyPayload = config.readyPayload;
|
|
632
|
+
}
|
|
633
|
+
if (config.targetWindow !== void 0) {
|
|
634
|
+
this.bridge.setTargetWindow(config.targetWindow ?? null);
|
|
635
|
+
}
|
|
636
|
+
this.syncBridgeState();
|
|
637
|
+
}
|
|
638
|
+
clearNavigationHandler() {
|
|
639
|
+
this.onNavigate = void 0;
|
|
640
|
+
}
|
|
641
|
+
destroy() {
|
|
642
|
+
this.removeNavigationRequestHandler?.();
|
|
643
|
+
this.removeNavigationInterceptors();
|
|
644
|
+
this.removeHistoryObserver();
|
|
645
|
+
this.restoreFetch();
|
|
646
|
+
this.bridge.destroy();
|
|
647
|
+
}
|
|
648
|
+
syncBridgeState() {
|
|
649
|
+
if (!this.bridge.hasTargetWindow()) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
if (!this.hasSentReady) {
|
|
653
|
+
this.bridge.send(this.readyEventType, this.readyPayload);
|
|
654
|
+
this.hasSentReady = true;
|
|
655
|
+
}
|
|
656
|
+
const navigationItemsKey = JSON.stringify(this.navigationItems);
|
|
657
|
+
if (navigationItemsKey !== this.lastSentNavigationItemsKey) {
|
|
658
|
+
this.bridge.send("navigation:items:update", {
|
|
659
|
+
items: this.navigationItems
|
|
660
|
+
});
|
|
661
|
+
this.lastSentNavigationItemsKey = navigationItemsKey;
|
|
662
|
+
}
|
|
663
|
+
if (this.currentPath) {
|
|
664
|
+
if (this.currentPath === this.pendingHostNavigationPath) {
|
|
665
|
+
this.pendingHostNavigationPath = null;
|
|
666
|
+
this.lastReportedPath = this.currentPath;
|
|
676
667
|
return;
|
|
677
668
|
}
|
|
678
|
-
|
|
679
|
-
if (!nextPath) {
|
|
669
|
+
if (this.currentPath === this.lastReportedPath) {
|
|
680
670
|
return;
|
|
681
671
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
if (targetName === "_top" || targetName === "_parent") {
|
|
693
|
-
bridge.redirectToRemote(new URL(href, window.location.href).toString());
|
|
694
|
-
return null;
|
|
695
|
-
}
|
|
696
|
-
if (!targetName || targetName === "_self") {
|
|
697
|
-
const nextPath = resolveLocalNavigationPath(href, window.location.origin);
|
|
698
|
-
if (nextPath) {
|
|
699
|
-
handleLocalNavigation(nextPath);
|
|
700
|
-
return window;
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
return originalOpen(url, target, features);
|
|
704
|
-
};
|
|
705
|
-
document.addEventListener("click", handleDocumentClick, true);
|
|
706
|
-
return () => {
|
|
707
|
-
document.removeEventListener("click", handleDocumentClick, true);
|
|
708
|
-
window.open = originalOpen;
|
|
709
|
-
};
|
|
710
|
-
}, [bridge, onNavigate]);
|
|
711
|
-
(0, import_react.useEffect)(() => {
|
|
712
|
-
if (!bridge || typeof window === "undefined") {
|
|
672
|
+
this.bridge.send(
|
|
673
|
+
this.navigationUpdateEventType,
|
|
674
|
+
buildNavigationUpdatePayload(this.currentPath)
|
|
675
|
+
);
|
|
676
|
+
this.lastReportedPath = this.currentPath;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
navigate(path, message) {
|
|
680
|
+
if (this.onNavigate) {
|
|
681
|
+
this.onNavigate(path, message);
|
|
713
682
|
return;
|
|
714
683
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
return
|
|
684
|
+
if (dispatchNavigateEvent(this.selfWindow.document, path)) {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
this.selfWindow.location.assign(path);
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
function getOrCreateEmbeddedAppRuntime(config) {
|
|
691
|
+
const globalScope = globalThis;
|
|
692
|
+
const existingRuntime = globalScope[GLOBAL_RUNTIME_KEY];
|
|
693
|
+
if (existingRuntime) {
|
|
694
|
+
existingRuntime.update(config);
|
|
695
|
+
return existingRuntime;
|
|
696
|
+
}
|
|
697
|
+
const runtime = new EmbeddedAppRuntime(config);
|
|
698
|
+
globalScope[GLOBAL_RUNTIME_KEY] = runtime;
|
|
699
|
+
return runtime;
|
|
700
|
+
}
|
|
701
|
+
function dispatchNavigateEvent(document, path) {
|
|
702
|
+
const event = new CustomEvent(THOR_NAVIGATE_EVENT, {
|
|
703
|
+
cancelable: true,
|
|
704
|
+
detail: { path }
|
|
705
|
+
});
|
|
706
|
+
document.dispatchEvent(event);
|
|
707
|
+
return event.defaultPrevented;
|
|
708
|
+
}
|
|
709
|
+
function readCurrentPath(selfWindow) {
|
|
710
|
+
return sanitizeEmbeddedAppPath(
|
|
711
|
+
`${selfWindow.location.pathname}${selfWindow.location.search}${selfWindow.location.hash}`
|
|
712
|
+
) ?? null;
|
|
713
|
+
}
|
|
714
|
+
function installHistoryObserver({
|
|
715
|
+
selfWindow,
|
|
716
|
+
onChange
|
|
717
|
+
}) {
|
|
718
|
+
const history = selfWindow.history;
|
|
719
|
+
const originalPushState = history.pushState;
|
|
720
|
+
const originalReplaceState = history.replaceState;
|
|
721
|
+
const notify = () => {
|
|
722
|
+
onChange(readCurrentPath(selfWindow));
|
|
723
|
+
};
|
|
724
|
+
history.pushState = function(...args) {
|
|
725
|
+
originalPushState.apply(history, args);
|
|
726
|
+
notify();
|
|
727
|
+
};
|
|
728
|
+
history.replaceState = function(...args) {
|
|
729
|
+
originalReplaceState.apply(history, args);
|
|
730
|
+
notify();
|
|
731
|
+
};
|
|
732
|
+
selfWindow.addEventListener("popstate", notify);
|
|
733
|
+
selfWindow.addEventListener("hashchange", notify);
|
|
734
|
+
notify();
|
|
735
|
+
return () => {
|
|
736
|
+
history.pushState = originalPushState;
|
|
737
|
+
history.replaceState = originalReplaceState;
|
|
738
|
+
selfWindow.removeEventListener("popstate", notify);
|
|
739
|
+
selfWindow.removeEventListener("hashchange", notify);
|
|
740
|
+
};
|
|
767
741
|
}
|
|
768
742
|
function getReferrerOrigin(explicitWindow) {
|
|
769
743
|
const resolvedWindow = explicitWindow ?? (typeof window !== "undefined" ? window : void 0);
|
|
@@ -777,40 +751,190 @@ function getReferrerOrigin(explicitWindow) {
|
|
|
777
751
|
return void 0;
|
|
778
752
|
}
|
|
779
753
|
}
|
|
754
|
+
function installNavigationInterceptors({
|
|
755
|
+
bridge,
|
|
756
|
+
selfWindow,
|
|
757
|
+
navigate
|
|
758
|
+
}) {
|
|
759
|
+
const document = selfWindow.document;
|
|
760
|
+
const handleLocalNavigation = (path) => {
|
|
761
|
+
const sanitizedPath = sanitizeEmbeddedAppPath(path);
|
|
762
|
+
if (!sanitizedPath) {
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
const nextPath = preserveEmbeddedAppLaunchParams(
|
|
766
|
+
sanitizedPath,
|
|
767
|
+
selfWindow.location.href
|
|
768
|
+
);
|
|
769
|
+
navigate(nextPath ?? sanitizedPath);
|
|
770
|
+
};
|
|
771
|
+
const handleDocumentClick = (event) => {
|
|
772
|
+
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) {
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
const target = event.target;
|
|
776
|
+
if (!(target instanceof Element)) {
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
const anchor = target.closest("a[href]");
|
|
780
|
+
if (!(anchor instanceof HTMLAnchorElement)) {
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
if (anchor.hasAttribute("download")) {
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
const targetWindow = anchor.target.toLowerCase();
|
|
787
|
+
const href = anchor.getAttribute("href");
|
|
788
|
+
if (!href) {
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
if (targetWindow === "_top" || targetWindow === "_parent") {
|
|
792
|
+
event.preventDefault();
|
|
793
|
+
bridge.redirectToRemote(anchor.href);
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
if (targetWindow && targetWindow !== "_self") {
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
const nextPath = resolveLocalNavigationPath(href, selfWindow.location.origin);
|
|
800
|
+
if (!nextPath) {
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
event.preventDefault();
|
|
804
|
+
handleLocalNavigation(nextPath);
|
|
805
|
+
};
|
|
806
|
+
const originalOpen = selfWindow.open.bind(selfWindow);
|
|
807
|
+
selfWindow.open = (url, target, features) => {
|
|
808
|
+
if (url == null) {
|
|
809
|
+
return originalOpen(url, target, features);
|
|
810
|
+
}
|
|
811
|
+
const href = typeof url === "string" ? url : url.toString();
|
|
812
|
+
const targetName = (target ?? "").toLowerCase();
|
|
813
|
+
if (targetName === "_top" || targetName === "_parent") {
|
|
814
|
+
bridge.redirectToRemote(new URL(href, selfWindow.location.href).toString());
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
if (!targetName || targetName === "_self") {
|
|
818
|
+
const nextPath = resolveLocalNavigationPath(
|
|
819
|
+
href,
|
|
820
|
+
selfWindow.location.origin
|
|
821
|
+
);
|
|
822
|
+
if (nextPath) {
|
|
823
|
+
handleLocalNavigation(nextPath);
|
|
824
|
+
return selfWindow;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return originalOpen(url, target, features);
|
|
828
|
+
};
|
|
829
|
+
document.addEventListener("click", handleDocumentClick, true);
|
|
830
|
+
return () => {
|
|
831
|
+
document.removeEventListener("click", handleDocumentClick, true);
|
|
832
|
+
selfWindow.open = originalOpen;
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
function installFetchInterceptor({
|
|
836
|
+
bridge,
|
|
837
|
+
getClientId,
|
|
838
|
+
getProject,
|
|
839
|
+
setProject,
|
|
840
|
+
readCachedToken,
|
|
841
|
+
writeCachedToken,
|
|
842
|
+
readPendingToken,
|
|
843
|
+
writePendingToken
|
|
844
|
+
}) {
|
|
845
|
+
if (typeof window === "undefined") {
|
|
846
|
+
return () => {
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
const originalFetch = globalThis.fetch.bind(globalThis);
|
|
850
|
+
const interceptedFetch = async (input, init) => {
|
|
851
|
+
if (!shouldAttachSessionToken(input)) {
|
|
852
|
+
return originalFetch(input, init);
|
|
853
|
+
}
|
|
854
|
+
const existingAuthorization = getExistingAuthorization(input, init);
|
|
855
|
+
if (existingAuthorization) {
|
|
856
|
+
return originalFetch(input, init);
|
|
857
|
+
}
|
|
858
|
+
const nextHeaders = new Headers(
|
|
859
|
+
input instanceof Request ? input.headers : init?.headers
|
|
860
|
+
);
|
|
861
|
+
try {
|
|
862
|
+
const sessionToken = await getSessionToken({
|
|
863
|
+
bridge,
|
|
864
|
+
clientId: getClientId(),
|
|
865
|
+
project: getProject,
|
|
866
|
+
setProject,
|
|
867
|
+
readCachedToken,
|
|
868
|
+
writeCachedToken,
|
|
869
|
+
readPendingToken,
|
|
870
|
+
writePendingToken
|
|
871
|
+
});
|
|
872
|
+
nextHeaders.set("Authorization", `Bearer ${sessionToken}`);
|
|
873
|
+
} catch {
|
|
874
|
+
if (!getProject()) {
|
|
875
|
+
throw new Error("Missing Thor embedded session token");
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
if (!nextHeaders.has("X-Requested-With")) {
|
|
879
|
+
nextHeaders.set("X-Requested-With", "XMLHttpRequest");
|
|
880
|
+
}
|
|
881
|
+
if (input instanceof Request) {
|
|
882
|
+
return originalFetch(
|
|
883
|
+
new Request(input, {
|
|
884
|
+
headers: nextHeaders
|
|
885
|
+
})
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
return originalFetch(input, {
|
|
889
|
+
...init,
|
|
890
|
+
headers: nextHeaders
|
|
891
|
+
});
|
|
892
|
+
};
|
|
893
|
+
globalThis.fetch = interceptedFetch;
|
|
894
|
+
window.fetch = interceptedFetch;
|
|
895
|
+
return () => {
|
|
896
|
+
globalThis.fetch = originalFetch;
|
|
897
|
+
window.fetch = originalFetch;
|
|
898
|
+
};
|
|
899
|
+
}
|
|
780
900
|
async function getSessionToken({
|
|
781
901
|
bridge,
|
|
782
902
|
clientId,
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
903
|
+
project,
|
|
904
|
+
setProject,
|
|
905
|
+
readCachedToken,
|
|
906
|
+
writeCachedToken,
|
|
907
|
+
readPendingToken,
|
|
908
|
+
writePendingToken
|
|
786
909
|
}) {
|
|
787
|
-
const cachedToken =
|
|
910
|
+
const cachedToken = readCachedToken();
|
|
788
911
|
if (cachedToken && !isExpired(cachedToken.expiresAt)) {
|
|
789
912
|
return cachedToken.token;
|
|
790
913
|
}
|
|
791
|
-
|
|
792
|
-
|
|
914
|
+
const pendingToken = readPendingToken();
|
|
915
|
+
if (pendingToken) {
|
|
916
|
+
return pendingToken;
|
|
793
917
|
}
|
|
794
|
-
const
|
|
918
|
+
const nextPendingToken = bridge.getSessionToken({ clientId }).then((response) => {
|
|
795
919
|
const token = response.sessionToken ?? response.idToken;
|
|
796
920
|
if (!token) {
|
|
797
921
|
throw new Error("Missing Thor embedded session token");
|
|
798
922
|
}
|
|
799
923
|
if (response.project) {
|
|
800
|
-
|
|
924
|
+
setProject(response.project);
|
|
801
925
|
}
|
|
802
|
-
|
|
926
|
+
writeCachedToken({
|
|
803
927
|
token,
|
|
804
928
|
expiresAt: normalizeTokenExpiry(token, response.exp)
|
|
805
|
-
};
|
|
806
|
-
|
|
929
|
+
});
|
|
930
|
+
writePendingToken(null);
|
|
807
931
|
return token;
|
|
808
932
|
}).catch((error) => {
|
|
809
|
-
|
|
933
|
+
writePendingToken(null);
|
|
810
934
|
throw error;
|
|
811
935
|
});
|
|
812
|
-
|
|
813
|
-
return
|
|
936
|
+
writePendingToken(nextPendingToken);
|
|
937
|
+
return nextPendingToken;
|
|
814
938
|
}
|
|
815
939
|
function shouldAttachSessionToken(input) {
|
|
816
940
|
if (typeof window === "undefined") {
|
|
@@ -877,6 +1001,70 @@ function readInitialProject(explicitWindow) {
|
|
|
877
1001
|
}
|
|
878
1002
|
}
|
|
879
1003
|
|
|
1004
|
+
// src/react.tsx
|
|
1005
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1006
|
+
var AppBridgeContext = (0, import_react.createContext)(null);
|
|
1007
|
+
function AppBridgeProvider({
|
|
1008
|
+
children,
|
|
1009
|
+
clientId,
|
|
1010
|
+
currentPath,
|
|
1011
|
+
navigationItems,
|
|
1012
|
+
navigationEventType = "navigation:go",
|
|
1013
|
+
navigationUpdateEventType = "navigation:update",
|
|
1014
|
+
namespace,
|
|
1015
|
+
onNavigate,
|
|
1016
|
+
readyEventType = "app:ready",
|
|
1017
|
+
readyPayload,
|
|
1018
|
+
requestTimeoutMs,
|
|
1019
|
+
selfWindow,
|
|
1020
|
+
targetOrigin,
|
|
1021
|
+
targetWindow
|
|
1022
|
+
}) {
|
|
1023
|
+
const [bridge, setBridge] = (0, import_react.useState)(null);
|
|
1024
|
+
(0, import_react.useEffect)(() => {
|
|
1025
|
+
if (typeof window === "undefined" && !selfWindow) {
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
const runtime = getOrCreateEmbeddedAppRuntime({
|
|
1029
|
+
clientId,
|
|
1030
|
+
currentPath,
|
|
1031
|
+
navigationEventType,
|
|
1032
|
+
navigationItems,
|
|
1033
|
+
navigationUpdateEventType,
|
|
1034
|
+
namespace,
|
|
1035
|
+
onNavigate,
|
|
1036
|
+
readyEventType,
|
|
1037
|
+
readyPayload,
|
|
1038
|
+
requestTimeoutMs,
|
|
1039
|
+
selfWindow,
|
|
1040
|
+
targetOrigin,
|
|
1041
|
+
targetWindow
|
|
1042
|
+
});
|
|
1043
|
+
setBridge(runtime.bridge);
|
|
1044
|
+
return () => {
|
|
1045
|
+
runtime.clearNavigationHandler();
|
|
1046
|
+
setBridge(
|
|
1047
|
+
(currentBridge) => currentBridge === runtime.bridge ? null : currentBridge
|
|
1048
|
+
);
|
|
1049
|
+
};
|
|
1050
|
+
}, [
|
|
1051
|
+
clientId,
|
|
1052
|
+
currentPath,
|
|
1053
|
+
navigationEventType,
|
|
1054
|
+
navigationItems,
|
|
1055
|
+
navigationUpdateEventType,
|
|
1056
|
+
namespace,
|
|
1057
|
+
onNavigate,
|
|
1058
|
+
readyEventType,
|
|
1059
|
+
readyPayload,
|
|
1060
|
+
requestTimeoutMs,
|
|
1061
|
+
selfWindow,
|
|
1062
|
+
targetOrigin,
|
|
1063
|
+
targetWindow
|
|
1064
|
+
]);
|
|
1065
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppBridgeContext.Provider, { value: bridge, children });
|
|
1066
|
+
}
|
|
1067
|
+
|
|
880
1068
|
// src/react-router.tsx
|
|
881
1069
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
882
1070
|
function ReactRouterAppBridgeProvider({
|