@thor-commerce/app-bridge-react 0.7.2 → 0.8.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 +971 -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-GX5V7AK7.js +82 -0
- package/dist/chunk-GX5V7AK7.js.map +1 -0
- package/dist/{chunk-XPUL72AN.js → chunk-IRTDLWEQ.js} +337 -270
- package/dist/chunk-IRTDLWEQ.js.map +1 -0
- package/dist/index.cjs +408 -257
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -127
- package/dist/index.d.ts +4 -127
- package/dist/index.js +16 -4
- package/dist/next.cjs +392 -256
- 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 +392 -256
- 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-mEVvh6Rv.d.cts +158 -0
- package/dist/runtime-mEVvh6Rv.d.ts +158 -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-XPUL72AN.js.map +0 -1
package/dist/next.cjs
CHANGED
|
@@ -39,6 +39,7 @@ var EMBEDDED_LAUNCH_PARAMS = [
|
|
|
39
39
|
"id_token",
|
|
40
40
|
"link_source",
|
|
41
41
|
"locale",
|
|
42
|
+
"project",
|
|
42
43
|
"protocol",
|
|
43
44
|
"session",
|
|
44
45
|
"shop",
|
|
@@ -104,6 +105,37 @@ function sanitizeEmbeddedAppPath(path) {
|
|
|
104
105
|
}
|
|
105
106
|
return `${url.pathname}${url.search}${url.hash}`;
|
|
106
107
|
}
|
|
108
|
+
function preserveEmbeddedAppLaunchParams(path, currentHref) {
|
|
109
|
+
if (!path || !currentHref) {
|
|
110
|
+
return path;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const currentUrl = new URL(currentHref, NAVIGATION_BASE_URL);
|
|
114
|
+
const nextUrl = new URL(path, currentUrl.href);
|
|
115
|
+
if (nextUrl.origin !== currentUrl.origin) {
|
|
116
|
+
return path;
|
|
117
|
+
}
|
|
118
|
+
let changed = false;
|
|
119
|
+
for (const key of EMBEDDED_LAUNCH_PARAMS) {
|
|
120
|
+
if (!nextUrl.searchParams.has(key) && currentUrl.searchParams.has(key)) {
|
|
121
|
+
const value = currentUrl.searchParams.get(key);
|
|
122
|
+
if (value != null) {
|
|
123
|
+
nextUrl.searchParams.set(key, value);
|
|
124
|
+
changed = true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!changed) {
|
|
129
|
+
return path;
|
|
130
|
+
}
|
|
131
|
+
if (/^https?:\/\//i.test(path)) {
|
|
132
|
+
return nextUrl.toString();
|
|
133
|
+
}
|
|
134
|
+
return `${nextUrl.pathname}${nextUrl.search}${nextUrl.hash}`;
|
|
135
|
+
} catch {
|
|
136
|
+
return path;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
107
139
|
function resolveLocalNavigationPath(href, currentOrigin) {
|
|
108
140
|
if (!href || href.startsWith("#")) {
|
|
109
141
|
return null;
|
|
@@ -178,13 +210,6 @@ function normalizeBridgeNavigationItems(items) {
|
|
|
178
210
|
}
|
|
179
211
|
|
|
180
212
|
// src/core.ts
|
|
181
|
-
var PRESERVED_EMBEDDED_DOCUMENT_PARAMS = [
|
|
182
|
-
"hmac",
|
|
183
|
-
"id_token",
|
|
184
|
-
"project",
|
|
185
|
-
"tenant",
|
|
186
|
-
"timestamp"
|
|
187
|
-
];
|
|
188
213
|
var DEFAULT_NAMESPACE = "thorcommerce:app-bridge";
|
|
189
214
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
190
215
|
var DEFAULT_REDIRECT_EVENT_TYPE = "navigation:redirect";
|
|
@@ -498,262 +523,159 @@ var AppBridge = class {
|
|
|
498
523
|
throw new Error("AppBridge could not resolve a browser window for redirect.");
|
|
499
524
|
}
|
|
500
525
|
this.selfWindow.location.assign(
|
|
501
|
-
|
|
526
|
+
preserveEmbeddedAppLaunchParams(destination, this.selfWindow.location.href) ?? destination
|
|
502
527
|
);
|
|
503
528
|
}
|
|
504
529
|
};
|
|
505
|
-
function preserveEmbeddedDocumentParams(destination, currentHref) {
|
|
506
|
-
try {
|
|
507
|
-
const currentUrl = new URL(currentHref);
|
|
508
|
-
const nextUrl = new URL(destination, currentUrl.href);
|
|
509
|
-
if (nextUrl.origin !== currentUrl.origin) {
|
|
510
|
-
return destination;
|
|
511
|
-
}
|
|
512
|
-
let changed = false;
|
|
513
|
-
for (const key of PRESERVED_EMBEDDED_DOCUMENT_PARAMS) {
|
|
514
|
-
if (!nextUrl.searchParams.has(key) && currentUrl.searchParams.has(key)) {
|
|
515
|
-
const value = currentUrl.searchParams.get(key);
|
|
516
|
-
if (value != null) {
|
|
517
|
-
nextUrl.searchParams.set(key, value);
|
|
518
|
-
changed = true;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
return changed ? nextUrl.toString() : destination;
|
|
523
|
-
} catch {
|
|
524
|
-
return destination;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
530
|
function createAppBridge(options = {}) {
|
|
528
531
|
return new AppBridge(options);
|
|
529
532
|
}
|
|
530
533
|
|
|
531
|
-
// src/
|
|
532
|
-
var
|
|
533
|
-
var
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
selfWindow,
|
|
547
|
-
targetOrigin,
|
|
548
|
-
targetWindow
|
|
549
|
-
}) {
|
|
550
|
-
const [bridge, setBridge] = (0, import_react.useState)(null);
|
|
551
|
-
const onNavigateRef = (0, import_react.useRef)(onNavigate);
|
|
552
|
-
const normalizedNavigationItems = (0, import_react.useMemo)(
|
|
553
|
-
() => normalizeBridgeNavigationItems(navigationItems),
|
|
554
|
-
[navigationItems]
|
|
555
|
-
);
|
|
556
|
-
const sessionTokenCacheRef = (0, import_react.useRef)(null);
|
|
557
|
-
const projectRef = (0, import_react.useRef)(readInitialProject(selfWindow));
|
|
558
|
-
const pendingSessionTokenRef = (0, import_react.useRef)(null);
|
|
559
|
-
(0, import_react.useEffect)(() => {
|
|
560
|
-
onNavigateRef.current = onNavigate;
|
|
561
|
-
}, [onNavigate]);
|
|
562
|
-
(0, import_react.useEffect)(() => {
|
|
563
|
-
if (typeof window === "undefined" && !selfWindow) {
|
|
564
|
-
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.sessionTokenCache = null;
|
|
545
|
+
this.pendingSessionToken = null;
|
|
546
|
+
const resolvedWindow = config.selfWindow ?? (typeof window !== "undefined" ? window : void 0);
|
|
547
|
+
if (!resolvedWindow) {
|
|
548
|
+
throw new Error("EmbeddedAppRuntime requires a browser window.");
|
|
565
549
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
550
|
+
this.selfWindow = resolvedWindow;
|
|
551
|
+
this.targetOrigin = config.targetOrigin ?? getReferrerOrigin(resolvedWindow);
|
|
552
|
+
this.clientId = config.clientId;
|
|
553
|
+
this.project = readInitialProject(resolvedWindow);
|
|
554
|
+
this.readyPayload = config.readyPayload ?? { clientId: config.clientId };
|
|
555
|
+
this.bridge = createAppBridge({
|
|
556
|
+
allowedOrigins: this.targetOrigin ? [this.targetOrigin] : void 0,
|
|
557
|
+
namespace: config.namespace,
|
|
558
|
+
requestTimeoutMs: config.requestTimeoutMs,
|
|
559
|
+
selfWindow: resolvedWindow,
|
|
574
560
|
source: "embedded-app",
|
|
575
561
|
target: "dashboard",
|
|
576
|
-
targetOrigin:
|
|
577
|
-
targetWindow
|
|
578
|
-
});
|
|
579
|
-
setBridge(nextBridge);
|
|
580
|
-
return () => {
|
|
581
|
-
setBridge((currentBridge) => currentBridge === nextBridge ? null : currentBridge);
|
|
582
|
-
nextBridge.destroy();
|
|
583
|
-
};
|
|
584
|
-
}, [
|
|
585
|
-
clientId,
|
|
586
|
-
namespace,
|
|
587
|
-
requestTimeoutMs,
|
|
588
|
-
selfWindow,
|
|
589
|
-
targetOrigin,
|
|
590
|
-
targetWindow
|
|
591
|
-
]);
|
|
592
|
-
(0, import_react.useEffect)(() => {
|
|
593
|
-
if (!bridge || !bridge.hasTargetWindow()) {
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
bridge.send(
|
|
597
|
-
readyEventType,
|
|
598
|
-
readyPayload ?? {
|
|
599
|
-
clientId
|
|
600
|
-
}
|
|
601
|
-
);
|
|
602
|
-
}, [bridge, clientId, readyEventType, readyPayload]);
|
|
603
|
-
(0, import_react.useEffect)(() => {
|
|
604
|
-
if (!bridge || !bridge.hasTargetWindow()) {
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
bridge.send("navigation:items:update", {
|
|
608
|
-
items: normalizedNavigationItems
|
|
562
|
+
targetOrigin: this.targetOrigin,
|
|
563
|
+
targetWindow: config.targetWindow
|
|
609
564
|
});
|
|
610
|
-
|
|
611
|
-
(0, import_react.useEffect)(() => {
|
|
612
|
-
if (!bridge || !bridge.hasTargetWindow() || !currentPath) {
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
bridge.send(navigationUpdateEventType, buildNavigationUpdatePayload(currentPath));
|
|
616
|
-
}, [bridge, currentPath, navigationUpdateEventType]);
|
|
617
|
-
(0, import_react.useEffect)(() => {
|
|
618
|
-
if (!bridge || !onNavigate) {
|
|
619
|
-
return;
|
|
620
|
-
}
|
|
621
|
-
return bridge.on(navigationEventType, (message) => {
|
|
565
|
+
this.removeNavigationRequestHandler = this.bridge.on(this.navigationEventType, (message) => {
|
|
622
566
|
const destination = resolveNavigationDestination(message.payload);
|
|
623
567
|
if (!destination) {
|
|
624
568
|
return;
|
|
625
569
|
}
|
|
626
|
-
|
|
570
|
+
const nextPath = preserveEmbeddedAppLaunchParams(
|
|
571
|
+
destination,
|
|
572
|
+
this.selfWindow.location.href
|
|
573
|
+
);
|
|
574
|
+
this.navigate(nextPath ?? destination, message);
|
|
627
575
|
});
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
const anchor = target.closest("a[href]");
|
|
649
|
-
if (!(anchor instanceof HTMLAnchorElement)) {
|
|
650
|
-
return;
|
|
651
|
-
}
|
|
652
|
-
if (anchor.hasAttribute("download")) {
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
const targetWindow2 = anchor.target.toLowerCase();
|
|
656
|
-
const href = anchor.getAttribute("href");
|
|
657
|
-
if (!href) {
|
|
658
|
-
return;
|
|
659
|
-
}
|
|
660
|
-
if (targetWindow2 === "_top" || targetWindow2 === "_parent") {
|
|
661
|
-
event.preventDefault();
|
|
662
|
-
bridge.redirectToRemote(anchor.href);
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
if (targetWindow2 && targetWindow2 !== "_self") {
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
const nextPath = resolveLocalNavigationPath(href, window.location.origin);
|
|
669
|
-
if (!nextPath) {
|
|
670
|
-
return;
|
|
671
|
-
}
|
|
672
|
-
event.preventDefault();
|
|
673
|
-
handleLocalNavigation(nextPath);
|
|
674
|
-
};
|
|
675
|
-
const originalOpen = window.open.bind(window);
|
|
676
|
-
window.open = (url, target, features) => {
|
|
677
|
-
if (url == null) {
|
|
678
|
-
return originalOpen(url, target, features);
|
|
679
|
-
}
|
|
680
|
-
const href = typeof url === "string" ? url : url.toString();
|
|
681
|
-
const targetName = (target ?? "").toLowerCase();
|
|
682
|
-
if (targetName === "_top" || targetName === "_parent") {
|
|
683
|
-
bridge.redirectToRemote(new URL(href, window.location.href).toString());
|
|
684
|
-
return null;
|
|
576
|
+
this.removeNavigationInterceptors = installNavigationInterceptors({
|
|
577
|
+
bridge: this.bridge,
|
|
578
|
+
selfWindow: this.selfWindow,
|
|
579
|
+
navigate: (path) => this.navigate(path)
|
|
580
|
+
});
|
|
581
|
+
this.restoreFetch = installFetchInterceptor({
|
|
582
|
+
bridge: this.bridge,
|
|
583
|
+
getClientId: () => this.clientId,
|
|
584
|
+
getProject: () => this.project,
|
|
585
|
+
setProject: (project) => {
|
|
586
|
+
this.project = project;
|
|
587
|
+
},
|
|
588
|
+
readCachedToken: () => this.sessionTokenCache,
|
|
589
|
+
writeCachedToken: (token) => {
|
|
590
|
+
this.sessionTokenCache = token;
|
|
591
|
+
},
|
|
592
|
+
readPendingToken: () => this.pendingSessionToken,
|
|
593
|
+
writePendingToken: (token) => {
|
|
594
|
+
this.pendingSessionToken = token;
|
|
685
595
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
596
|
+
});
|
|
597
|
+
this.update(config);
|
|
598
|
+
}
|
|
599
|
+
update(config) {
|
|
600
|
+
if (config.clientId) {
|
|
601
|
+
this.clientId = config.clientId;
|
|
602
|
+
if (!this.readyPayload || typeof this.readyPayload === "object" && this.readyPayload !== null && "clientId" in this.readyPayload) {
|
|
603
|
+
this.readyPayload = config.readyPayload ?? { clientId: config.clientId };
|
|
692
604
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
605
|
+
}
|
|
606
|
+
if (config.currentPath !== void 0) {
|
|
607
|
+
this.currentPath = config.currentPath;
|
|
608
|
+
}
|
|
609
|
+
if (config.navigationItems !== void 0) {
|
|
610
|
+
this.navigationItems = normalizeBridgeNavigationItems(config.navigationItems);
|
|
611
|
+
}
|
|
612
|
+
if (config.onNavigate !== void 0) {
|
|
613
|
+
this.onNavigate = config.onNavigate;
|
|
614
|
+
}
|
|
615
|
+
if (config.readyEventType) {
|
|
616
|
+
this.readyEventType = config.readyEventType;
|
|
617
|
+
}
|
|
618
|
+
if (config.readyPayload !== void 0) {
|
|
619
|
+
this.readyPayload = config.readyPayload;
|
|
620
|
+
}
|
|
621
|
+
if (config.targetWindow !== void 0) {
|
|
622
|
+
this.bridge.setTargetWindow(config.targetWindow ?? null);
|
|
623
|
+
}
|
|
624
|
+
this.syncBridgeState();
|
|
625
|
+
}
|
|
626
|
+
clearNavigationHandler() {
|
|
627
|
+
this.onNavigate = void 0;
|
|
628
|
+
}
|
|
629
|
+
destroy() {
|
|
630
|
+
this.removeNavigationRequestHandler?.();
|
|
631
|
+
this.removeNavigationInterceptors();
|
|
632
|
+
this.restoreFetch();
|
|
633
|
+
this.bridge.destroy();
|
|
634
|
+
}
|
|
635
|
+
syncBridgeState() {
|
|
636
|
+
if (!this.bridge.hasTargetWindow()) {
|
|
703
637
|
return;
|
|
704
638
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
}
|
|
714
|
-
const nextHeaders = new Headers(
|
|
715
|
-
input instanceof Request ? input.headers : init?.headers
|
|
639
|
+
this.bridge.send(this.readyEventType, this.readyPayload);
|
|
640
|
+
this.bridge.send("navigation:items:update", {
|
|
641
|
+
items: this.navigationItems
|
|
642
|
+
});
|
|
643
|
+
if (this.currentPath) {
|
|
644
|
+
this.bridge.send(
|
|
645
|
+
this.navigationUpdateEventType,
|
|
646
|
+
buildNavigationUpdatePayload(this.currentPath)
|
|
716
647
|
);
|
|
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
|
-
globalThis.fetch = interceptedFetch;
|
|
750
|
-
window.fetch = interceptedFetch;
|
|
751
|
-
return () => {
|
|
752
|
-
globalThis.fetch = originalFetch;
|
|
753
|
-
window.fetch = originalFetch;
|
|
754
|
-
};
|
|
755
|
-
}, [bridge, clientId]);
|
|
756
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppBridgeContext.Provider, { value: bridge, children });
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
navigate(path, message) {
|
|
651
|
+
if (this.onNavigate) {
|
|
652
|
+
this.onNavigate(path, message);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
if (dispatchNavigateEvent(this.selfWindow.document, path)) {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
this.selfWindow.location.assign(path);
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
function getOrCreateEmbeddedAppRuntime(config) {
|
|
662
|
+
const globalScope = globalThis;
|
|
663
|
+
const existingRuntime = globalScope[GLOBAL_RUNTIME_KEY];
|
|
664
|
+
if (existingRuntime) {
|
|
665
|
+
existingRuntime.update(config);
|
|
666
|
+
return existingRuntime;
|
|
667
|
+
}
|
|
668
|
+
const runtime = new EmbeddedAppRuntime(config);
|
|
669
|
+
globalScope[GLOBAL_RUNTIME_KEY] = runtime;
|
|
670
|
+
return runtime;
|
|
671
|
+
}
|
|
672
|
+
function dispatchNavigateEvent(document, path) {
|
|
673
|
+
const event = new CustomEvent(THOR_NAVIGATE_EVENT, {
|
|
674
|
+
cancelable: true,
|
|
675
|
+
detail: { path }
|
|
676
|
+
});
|
|
677
|
+
document.dispatchEvent(event);
|
|
678
|
+
return event.defaultPrevented;
|
|
757
679
|
}
|
|
758
680
|
function getReferrerOrigin(explicitWindow) {
|
|
759
681
|
const resolvedWindow = explicitWindow ?? (typeof window !== "undefined" ? window : void 0);
|
|
@@ -767,40 +689,190 @@ function getReferrerOrigin(explicitWindow) {
|
|
|
767
689
|
return void 0;
|
|
768
690
|
}
|
|
769
691
|
}
|
|
692
|
+
function installNavigationInterceptors({
|
|
693
|
+
bridge,
|
|
694
|
+
selfWindow,
|
|
695
|
+
navigate
|
|
696
|
+
}) {
|
|
697
|
+
const document = selfWindow.document;
|
|
698
|
+
const handleLocalNavigation = (path) => {
|
|
699
|
+
const sanitizedPath = sanitizeEmbeddedAppPath(path);
|
|
700
|
+
if (!sanitizedPath) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
const nextPath = preserveEmbeddedAppLaunchParams(
|
|
704
|
+
sanitizedPath,
|
|
705
|
+
selfWindow.location.href
|
|
706
|
+
);
|
|
707
|
+
navigate(nextPath ?? sanitizedPath);
|
|
708
|
+
};
|
|
709
|
+
const handleDocumentClick = (event) => {
|
|
710
|
+
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) {
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
const target = event.target;
|
|
714
|
+
if (!(target instanceof Element)) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
const anchor = target.closest("a[href]");
|
|
718
|
+
if (!(anchor instanceof HTMLAnchorElement)) {
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
if (anchor.hasAttribute("download")) {
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
const targetWindow = anchor.target.toLowerCase();
|
|
725
|
+
const href = anchor.getAttribute("href");
|
|
726
|
+
if (!href) {
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (targetWindow === "_top" || targetWindow === "_parent") {
|
|
730
|
+
event.preventDefault();
|
|
731
|
+
bridge.redirectToRemote(anchor.href);
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
if (targetWindow && targetWindow !== "_self") {
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
const nextPath = resolveLocalNavigationPath(href, selfWindow.location.origin);
|
|
738
|
+
if (!nextPath) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
event.preventDefault();
|
|
742
|
+
handleLocalNavigation(nextPath);
|
|
743
|
+
};
|
|
744
|
+
const originalOpen = selfWindow.open.bind(selfWindow);
|
|
745
|
+
selfWindow.open = (url, target, features) => {
|
|
746
|
+
if (url == null) {
|
|
747
|
+
return originalOpen(url, target, features);
|
|
748
|
+
}
|
|
749
|
+
const href = typeof url === "string" ? url : url.toString();
|
|
750
|
+
const targetName = (target ?? "").toLowerCase();
|
|
751
|
+
if (targetName === "_top" || targetName === "_parent") {
|
|
752
|
+
bridge.redirectToRemote(new URL(href, selfWindow.location.href).toString());
|
|
753
|
+
return null;
|
|
754
|
+
}
|
|
755
|
+
if (!targetName || targetName === "_self") {
|
|
756
|
+
const nextPath = resolveLocalNavigationPath(
|
|
757
|
+
href,
|
|
758
|
+
selfWindow.location.origin
|
|
759
|
+
);
|
|
760
|
+
if (nextPath) {
|
|
761
|
+
handleLocalNavigation(nextPath);
|
|
762
|
+
return selfWindow;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return originalOpen(url, target, features);
|
|
766
|
+
};
|
|
767
|
+
document.addEventListener("click", handleDocumentClick, true);
|
|
768
|
+
return () => {
|
|
769
|
+
document.removeEventListener("click", handleDocumentClick, true);
|
|
770
|
+
selfWindow.open = originalOpen;
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
function installFetchInterceptor({
|
|
774
|
+
bridge,
|
|
775
|
+
getClientId,
|
|
776
|
+
getProject,
|
|
777
|
+
setProject,
|
|
778
|
+
readCachedToken,
|
|
779
|
+
writeCachedToken,
|
|
780
|
+
readPendingToken,
|
|
781
|
+
writePendingToken
|
|
782
|
+
}) {
|
|
783
|
+
if (typeof window === "undefined") {
|
|
784
|
+
return () => {
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
const originalFetch = globalThis.fetch.bind(globalThis);
|
|
788
|
+
const interceptedFetch = async (input, init) => {
|
|
789
|
+
if (!shouldAttachSessionToken(input)) {
|
|
790
|
+
return originalFetch(input, init);
|
|
791
|
+
}
|
|
792
|
+
const existingAuthorization = getExistingAuthorization(input, init);
|
|
793
|
+
if (existingAuthorization) {
|
|
794
|
+
return originalFetch(input, init);
|
|
795
|
+
}
|
|
796
|
+
const nextHeaders = new Headers(
|
|
797
|
+
input instanceof Request ? input.headers : init?.headers
|
|
798
|
+
);
|
|
799
|
+
try {
|
|
800
|
+
const sessionToken = await getSessionToken({
|
|
801
|
+
bridge,
|
|
802
|
+
clientId: getClientId(),
|
|
803
|
+
project: getProject,
|
|
804
|
+
setProject,
|
|
805
|
+
readCachedToken,
|
|
806
|
+
writeCachedToken,
|
|
807
|
+
readPendingToken,
|
|
808
|
+
writePendingToken
|
|
809
|
+
});
|
|
810
|
+
nextHeaders.set("Authorization", `Bearer ${sessionToken}`);
|
|
811
|
+
} catch {
|
|
812
|
+
if (!getProject()) {
|
|
813
|
+
throw new Error("Missing Thor embedded session token");
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
if (!nextHeaders.has("X-Requested-With")) {
|
|
817
|
+
nextHeaders.set("X-Requested-With", "XMLHttpRequest");
|
|
818
|
+
}
|
|
819
|
+
if (input instanceof Request) {
|
|
820
|
+
return originalFetch(
|
|
821
|
+
new Request(input, {
|
|
822
|
+
headers: nextHeaders
|
|
823
|
+
})
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
return originalFetch(input, {
|
|
827
|
+
...init,
|
|
828
|
+
headers: nextHeaders
|
|
829
|
+
});
|
|
830
|
+
};
|
|
831
|
+
globalThis.fetch = interceptedFetch;
|
|
832
|
+
window.fetch = interceptedFetch;
|
|
833
|
+
return () => {
|
|
834
|
+
globalThis.fetch = originalFetch;
|
|
835
|
+
window.fetch = originalFetch;
|
|
836
|
+
};
|
|
837
|
+
}
|
|
770
838
|
async function getSessionToken({
|
|
771
839
|
bridge,
|
|
772
840
|
clientId,
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
841
|
+
project,
|
|
842
|
+
setProject,
|
|
843
|
+
readCachedToken,
|
|
844
|
+
writeCachedToken,
|
|
845
|
+
readPendingToken,
|
|
846
|
+
writePendingToken
|
|
776
847
|
}) {
|
|
777
|
-
const cachedToken =
|
|
848
|
+
const cachedToken = readCachedToken();
|
|
778
849
|
if (cachedToken && !isExpired(cachedToken.expiresAt)) {
|
|
779
850
|
return cachedToken.token;
|
|
780
851
|
}
|
|
781
|
-
|
|
782
|
-
|
|
852
|
+
const pendingToken = readPendingToken();
|
|
853
|
+
if (pendingToken) {
|
|
854
|
+
return pendingToken;
|
|
783
855
|
}
|
|
784
|
-
const
|
|
856
|
+
const nextPendingToken = bridge.getSessionToken({ clientId }).then((response) => {
|
|
785
857
|
const token = response.sessionToken ?? response.idToken;
|
|
786
858
|
if (!token) {
|
|
787
859
|
throw new Error("Missing Thor embedded session token");
|
|
788
860
|
}
|
|
789
861
|
if (response.project) {
|
|
790
|
-
|
|
862
|
+
setProject(response.project);
|
|
791
863
|
}
|
|
792
|
-
|
|
864
|
+
writeCachedToken({
|
|
793
865
|
token,
|
|
794
866
|
expiresAt: normalizeTokenExpiry(token, response.exp)
|
|
795
|
-
};
|
|
796
|
-
|
|
867
|
+
});
|
|
868
|
+
writePendingToken(null);
|
|
797
869
|
return token;
|
|
798
870
|
}).catch((error) => {
|
|
799
|
-
|
|
871
|
+
writePendingToken(null);
|
|
800
872
|
throw error;
|
|
801
873
|
});
|
|
802
|
-
|
|
803
|
-
return
|
|
874
|
+
writePendingToken(nextPendingToken);
|
|
875
|
+
return nextPendingToken;
|
|
804
876
|
}
|
|
805
877
|
function shouldAttachSessionToken(input) {
|
|
806
878
|
if (typeof window === "undefined") {
|
|
@@ -867,6 +939,70 @@ function readInitialProject(explicitWindow) {
|
|
|
867
939
|
}
|
|
868
940
|
}
|
|
869
941
|
|
|
942
|
+
// src/react.tsx
|
|
943
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
944
|
+
var AppBridgeContext = (0, import_react.createContext)(null);
|
|
945
|
+
function AppBridgeProvider({
|
|
946
|
+
children,
|
|
947
|
+
clientId,
|
|
948
|
+
currentPath,
|
|
949
|
+
navigationItems,
|
|
950
|
+
navigationEventType = "navigation:go",
|
|
951
|
+
navigationUpdateEventType = "navigation:update",
|
|
952
|
+
namespace,
|
|
953
|
+
onNavigate,
|
|
954
|
+
readyEventType = "app:ready",
|
|
955
|
+
readyPayload,
|
|
956
|
+
requestTimeoutMs,
|
|
957
|
+
selfWindow,
|
|
958
|
+
targetOrigin,
|
|
959
|
+
targetWindow
|
|
960
|
+
}) {
|
|
961
|
+
const [bridge, setBridge] = (0, import_react.useState)(null);
|
|
962
|
+
(0, import_react.useEffect)(() => {
|
|
963
|
+
if (typeof window === "undefined" && !selfWindow) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
const runtime = getOrCreateEmbeddedAppRuntime({
|
|
967
|
+
clientId,
|
|
968
|
+
currentPath,
|
|
969
|
+
navigationEventType,
|
|
970
|
+
navigationItems,
|
|
971
|
+
navigationUpdateEventType,
|
|
972
|
+
namespace,
|
|
973
|
+
onNavigate,
|
|
974
|
+
readyEventType,
|
|
975
|
+
readyPayload,
|
|
976
|
+
requestTimeoutMs,
|
|
977
|
+
selfWindow,
|
|
978
|
+
targetOrigin,
|
|
979
|
+
targetWindow
|
|
980
|
+
});
|
|
981
|
+
setBridge(runtime.bridge);
|
|
982
|
+
return () => {
|
|
983
|
+
runtime.clearNavigationHandler();
|
|
984
|
+
setBridge(
|
|
985
|
+
(currentBridge) => currentBridge === runtime.bridge ? null : currentBridge
|
|
986
|
+
);
|
|
987
|
+
};
|
|
988
|
+
}, [
|
|
989
|
+
clientId,
|
|
990
|
+
currentPath,
|
|
991
|
+
navigationEventType,
|
|
992
|
+
navigationItems,
|
|
993
|
+
navigationUpdateEventType,
|
|
994
|
+
namespace,
|
|
995
|
+
onNavigate,
|
|
996
|
+
readyEventType,
|
|
997
|
+
readyPayload,
|
|
998
|
+
requestTimeoutMs,
|
|
999
|
+
selfWindow,
|
|
1000
|
+
targetOrigin,
|
|
1001
|
+
targetWindow
|
|
1002
|
+
]);
|
|
1003
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppBridgeContext.Provider, { value: bridge, children });
|
|
1004
|
+
}
|
|
1005
|
+
|
|
870
1006
|
// src/next.tsx
|
|
871
1007
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
872
1008
|
function NextAppBridgeProvider({
|