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