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