@edge-markets/connect-link 1.2.0 → 1.4.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/dist/index.mjs CHANGED
@@ -1,10 +1,9 @@
1
1
  // src/edge-link.ts
2
2
  import {
3
- getEnvironmentConfig,
4
3
  ALL_EDGE_SCOPES,
5
- formatScopesForEnvironment,
6
4
  EdgePopupBlockedError,
7
- EdgeStateMismatchError
5
+ formatScopesForEnvironment,
6
+ getEnvironmentConfig
8
7
  } from "@edge-markets/connect";
9
8
 
10
9
  // src/pkce.ts
@@ -300,6 +299,7 @@ var EdgeLink = class {
300
299
  this.state = null;
301
300
  this.messageHandler = null;
302
301
  this.isDestroyed = false;
302
+ this.isInitializing = false;
303
303
  if (!config.clientId) {
304
304
  throw new Error("EdgeLink: clientId is required");
305
305
  }
@@ -344,7 +344,7 @@ var EdgeLink = class {
344
344
  if (this.isDestroyed) {
345
345
  throw new Error("EdgeLink: Cannot open - instance has been destroyed");
346
346
  }
347
- if (this.popup.isOpen()) {
347
+ if (this.popup.isOpen() || this.isInitializing) {
348
348
  this.popup.focus();
349
349
  return;
350
350
  }
@@ -407,6 +407,7 @@ var EdgeLink = class {
407
407
  * Initializes PKCE and navigates popup to auth URL.
408
408
  */
409
409
  async initializeAuth(scopes) {
410
+ this.isInitializing = true;
410
411
  try {
411
412
  this.pkce = await generatePKCE();
412
413
  this.state = generateState();
@@ -423,6 +424,8 @@ var EdgeLink = class {
423
424
  message: error instanceof Error ? error.message : "Failed to initialize"
424
425
  }
425
426
  });
427
+ } finally {
428
+ this.isInitializing = false;
426
429
  }
427
430
  }
428
431
  /**
@@ -454,6 +457,7 @@ var EdgeLink = class {
454
457
  */
455
458
  setupMessageListener() {
456
459
  this.messageHandler = (event) => {
460
+ if (this.isDestroyed) return;
457
461
  if (event.origin !== this.expectedOrigin) {
458
462
  return;
459
463
  }
@@ -498,7 +502,7 @@ var EdgeLink = class {
498
502
  }
499
503
  });
500
504
  this.close();
501
- throw new EdgeStateMismatchError();
505
+ return;
502
506
  }
503
507
  if (!this.pkce?.verifier) {
504
508
  this.config.onExit?.({
@@ -615,25 +619,599 @@ function useEdgeLink(config) {
615
619
  return { open, ready, error, isOpen };
616
620
  }
617
621
 
622
+ // src/edge-transfer-verify.ts
623
+ import { EdgePopupBlockedError as EdgePopupBlockedError2 } from "@edge-markets/connect";
624
+
625
+ // src/iframe-manager.ts
626
+ var DEFAULT_TITLE = "EDGE Connect Transfer Verification";
627
+ var IFRAME_SANDBOX = "allow-scripts allow-same-origin allow-forms";
628
+ var IframeManager = class {
629
+ constructor() {
630
+ this.iframe = null;
631
+ this.loadingEl = null;
632
+ this.container = null;
633
+ this.callbacks = {};
634
+ }
635
+ /**
636
+ * Creates and mounts the iframe into the provided container.
637
+ *
638
+ * Displays a branded loading state while the iframe loads,
639
+ * then swaps to the iframe once it's ready.
640
+ *
641
+ * @param config - Iframe configuration
642
+ * @param callbacks - Optional lifecycle callbacks
643
+ */
644
+ mount(config, callbacks = {}) {
645
+ this.destroy();
646
+ this.container = config.container;
647
+ this.callbacks = callbacks;
648
+ this.renderLoadingState(config.container);
649
+ this.iframe = document.createElement("iframe");
650
+ this.iframe.src = config.url;
651
+ this.iframe.width = config.width || "100%";
652
+ this.iframe.height = config.height || "100%";
653
+ this.iframe.title = config.title || DEFAULT_TITLE;
654
+ this.iframe.setAttribute("sandbox", IFRAME_SANDBOX);
655
+ this.iframe.setAttribute("allow", "clipboard-write");
656
+ this.iframe.style.border = "none";
657
+ this.iframe.style.display = "none";
658
+ this.iframe.style.width = config.width || "100%";
659
+ this.iframe.style.height = config.height || "100%";
660
+ this.iframe.style.minHeight = "400px";
661
+ this.iframe.style.borderRadius = "8px";
662
+ this.iframe.addEventListener("load", () => {
663
+ this.hideLoadingState();
664
+ this.callbacks.onLoad?.();
665
+ });
666
+ this.iframe.addEventListener("error", () => {
667
+ this.hideLoadingState();
668
+ const error = new Error("Failed to load transfer verification iframe");
669
+ this.callbacks.onError?.(error);
670
+ });
671
+ config.container.appendChild(this.iframe);
672
+ }
673
+ /**
674
+ * Gets the iframe's content window for postMessage communication.
675
+ *
676
+ * @returns The iframe's contentWindow, or null if not mounted
677
+ */
678
+ getContentWindow() {
679
+ return this.iframe?.contentWindow ?? null;
680
+ }
681
+ /**
682
+ * Checks if the iframe is currently mounted in the DOM.
683
+ */
684
+ isMounted() {
685
+ return this.iframe !== null && this.iframe.isConnected;
686
+ }
687
+ /**
688
+ * Removes the iframe and loading state from the DOM.
689
+ * Cleans up all references for garbage collection.
690
+ */
691
+ destroy() {
692
+ if (this.loadingEl && this.loadingEl.parentNode) {
693
+ this.loadingEl.parentNode.removeChild(this.loadingEl);
694
+ }
695
+ this.loadingEl = null;
696
+ if (this.iframe && this.iframe.parentNode) {
697
+ this.iframe.parentNode.removeChild(this.iframe);
698
+ }
699
+ this.iframe = null;
700
+ this.container = null;
701
+ this.callbacks = {};
702
+ }
703
+ // ===========================================================================
704
+ // PRIVATE METHODS
705
+ // ===========================================================================
706
+ /**
707
+ * Renders a branded loading state in the container.
708
+ *
709
+ * This displays immediately while the iframe loads the verification URL.
710
+ * Matches the visual style of the PopupManager loading state.
711
+ */
712
+ renderLoadingState(container) {
713
+ this.loadingEl = document.createElement("div");
714
+ this.loadingEl.setAttribute("data-edge-loading", "true");
715
+ this.loadingEl.innerHTML = `
716
+ <div style="
717
+ display: flex;
718
+ align-items: center;
719
+ justify-content: center;
720
+ min-height: 400px;
721
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
722
+ border-radius: 8px;
723
+ color: white;
724
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
725
+ ">
726
+ <div style="text-align: center; padding: 40px;">
727
+ <div style="
728
+ width: 64px;
729
+ height: 64px;
730
+ margin: 0 auto 24px;
731
+ background: linear-gradient(135deg, #00d4aa 0%, #00a080 100%);
732
+ border-radius: 16px;
733
+ display: flex;
734
+ align-items: center;
735
+ justify-content: center;
736
+ font-size: 28px;
737
+ font-weight: bold;
738
+ box-shadow: 0 8px 32px rgba(0, 212, 170, 0.3);
739
+ ">E</div>
740
+ <div style="
741
+ width: 40px;
742
+ height: 40px;
743
+ border: 3px solid rgba(255, 255, 255, 0.2);
744
+ border-top-color: #00d4aa;
745
+ border-radius: 50%;
746
+ animation: edge-spin 1s linear infinite;
747
+ margin: 0 auto 24px;
748
+ "></div>
749
+ <h1 style="
750
+ font-size: 22px;
751
+ font-weight: 600;
752
+ margin-bottom: 8px;
753
+ letter-spacing: -0.5px;
754
+ ">Transfer Verification</h1>
755
+ <p style="
756
+ font-size: 15px;
757
+ color: rgba(255, 255, 255, 0.7);
758
+ line-height: 1.5;
759
+ ">Loading secure verification...</p>
760
+ <div style="
761
+ display: flex;
762
+ align-items: center;
763
+ justify-content: center;
764
+ gap: 6px;
765
+ margin-top: 32px;
766
+ font-size: 13px;
767
+ color: rgba(255, 255, 255, 0.5);
768
+ ">
769
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
770
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
771
+ <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
772
+ </svg>
773
+ <span>256-bit encryption</span>
774
+ </div>
775
+ </div>
776
+ </div>
777
+ <style>
778
+ @keyframes edge-spin {
779
+ to { transform: rotate(360deg); }
780
+ }
781
+ </style>
782
+ `;
783
+ container.appendChild(this.loadingEl);
784
+ }
785
+ /**
786
+ * Hides the loading state and shows the iframe.
787
+ */
788
+ hideLoadingState() {
789
+ if (this.loadingEl && this.loadingEl.parentNode) {
790
+ this.loadingEl.parentNode.removeChild(this.loadingEl);
791
+ this.loadingEl = null;
792
+ }
793
+ if (this.iframe) {
794
+ this.iframe.style.display = "block";
795
+ }
796
+ }
797
+ };
798
+
799
+ // src/edge-transfer-verify.ts
800
+ function generateNonce() {
801
+ const array = new Uint8Array(32);
802
+ crypto.getRandomValues(array);
803
+ return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
804
+ }
805
+ function isValidTransferVerifyMessage(data) {
806
+ if (!data || typeof data !== "object") return false;
807
+ const msg = data;
808
+ const validTypes = [
809
+ "edge:transfer-verify:success",
810
+ "edge:transfer-verify:error",
811
+ "edge:transfer-verify:cancel",
812
+ "edge:transfer-verify:expired",
813
+ "edge:transfer-verify:loaded"
814
+ ];
815
+ if (typeof msg.type !== "string" || !validTypes.includes(msg.type)) {
816
+ return false;
817
+ }
818
+ if (typeof msg.sessionId !== "string" || msg.sessionId.length === 0) return false;
819
+ if (typeof msg.transferId !== "string" || msg.transferId.length === 0) return false;
820
+ if (typeof msg.nonce !== "string" || msg.nonce.length === 0) return false;
821
+ if (typeof msg.timestamp !== "string" || msg.timestamp.length === 0) return false;
822
+ if (msg.error !== void 0 && typeof msg.error !== "string") return false;
823
+ return true;
824
+ }
825
+ var EdgeTransferVerify = class {
826
+ /**
827
+ * Creates a new EdgeTransferVerify instance.
828
+ *
829
+ * @param config - Configuration options
830
+ * @throws Error if required config is missing or invalid
831
+ */
832
+ constructor(config) {
833
+ this.popupManager = null;
834
+ this.iframeManager = null;
835
+ this.messageHandler = null;
836
+ this.isDestroyed = false;
837
+ this.isOpened = false;
838
+ if (!config.verificationUrl) {
839
+ throw new Error("EdgeTransferVerify: verificationUrl is required");
840
+ }
841
+ if (!config.sessionId) {
842
+ throw new Error("EdgeTransferVerify: sessionId is required");
843
+ }
844
+ const mode = config.mode || "iframe";
845
+ if (mode === "iframe" && !config.container) {
846
+ throw new Error("EdgeTransferVerify: container is required for iframe mode");
847
+ }
848
+ this.config = config;
849
+ this.mode = mode;
850
+ this.nonce = config.nonce || generateNonce();
851
+ this.expectedOrigin = new URL(config.verificationUrl).origin;
852
+ this.setupMessageListener();
853
+ }
854
+ // ===========================================================================
855
+ // PUBLIC API
856
+ // ===========================================================================
857
+ /**
858
+ * Opens the transfer verification UI.
859
+ *
860
+ * In iframe mode, this mounts the iframe into the configured container.
861
+ * In popup mode, this opens a popup window.
862
+ *
863
+ * **Popup mode MUST be called directly from a user click handler!**
864
+ * Iframe mode can be called from any context.
865
+ *
866
+ * @throws EdgePopupBlockedError if popup mode and popup is blocked
867
+ * @throws Error if the instance has been destroyed or is already open
868
+ *
869
+ * @example
870
+ * ```typescript
871
+ * // Iframe mode — can call anywhere
872
+ * verify.open()
873
+ *
874
+ * // Popup mode — must call from click handler
875
+ * button.onclick = () => verify.open()
876
+ * ```
877
+ */
878
+ open() {
879
+ if (this.isDestroyed) {
880
+ throw new Error("EdgeTransferVerify: Cannot open - instance has been destroyed");
881
+ }
882
+ if (this.isOpened) {
883
+ if (this.mode === "popup" && this.popupManager?.isOpen()) {
884
+ this.popupManager.focus();
885
+ return;
886
+ }
887
+ if (this.mode === "iframe" && this.iframeManager?.isMounted()) {
888
+ return;
889
+ }
890
+ }
891
+ const url = this.buildVerificationUrl();
892
+ if (this.mode === "popup") {
893
+ this.openPopup(url);
894
+ } else {
895
+ this.openIframe(url);
896
+ }
897
+ this.isOpened = true;
898
+ }
899
+ /**
900
+ * Closes the verification UI.
901
+ *
902
+ * In popup mode, closes the popup window.
903
+ * In iframe mode, removes the iframe from the container.
904
+ * Does not trigger any callbacks.
905
+ */
906
+ close() {
907
+ if (this.mode === "popup") {
908
+ this.popupManager?.close();
909
+ this.popupManager = null;
910
+ } else {
911
+ this.iframeManager?.destroy();
912
+ this.iframeManager = null;
913
+ }
914
+ this.isOpened = false;
915
+ }
916
+ /**
917
+ * Destroys the EdgeTransferVerify instance.
918
+ *
919
+ * Closes the UI, removes the postMessage listener, and cleans up all resources.
920
+ * After destroy(), the instance cannot be reused.
921
+ *
922
+ * @example
923
+ * ```typescript
924
+ * // React cleanup
925
+ * useEffect(() => {
926
+ * const verify = new EdgeTransferVerify({ ... })
927
+ * verify.open()
928
+ * return () => verify.destroy()
929
+ * }, [])
930
+ * ```
931
+ */
932
+ destroy() {
933
+ this.isDestroyed = true;
934
+ this.close();
935
+ this.removeMessageListener();
936
+ }
937
+ /**
938
+ * Checks if the verification UI is currently displayed.
939
+ */
940
+ isOpen() {
941
+ if (this.mode === "popup") {
942
+ return this.popupManager?.isOpen() ?? false;
943
+ }
944
+ return this.iframeManager?.isMounted() ?? false;
945
+ }
946
+ /**
947
+ * Returns the nonce being used for this verification session.
948
+ * Useful for debugging and logging.
949
+ */
950
+ getNonce() {
951
+ return this.nonce;
952
+ }
953
+ // ===========================================================================
954
+ // PRIVATE — LAUNCH METHODS
955
+ // ===========================================================================
956
+ /**
957
+ * Builds the verification URL with nonce and parent origin parameters.
958
+ *
959
+ * Appends:
960
+ * - `nonce` — for replay protection / message correlation
961
+ * - `origin` — so the verification page knows where to postMessage back to
962
+ */
963
+ buildVerificationUrl() {
964
+ const url = new URL(this.config.verificationUrl);
965
+ url.searchParams.set("nonce", this.nonce);
966
+ url.searchParams.set("origin", window.location.origin);
967
+ return url.toString();
968
+ }
969
+ /**
970
+ * Opens the verification UI in a popup window.
971
+ *
972
+ * Uses PopupManager from the existing EdgeLink infrastructure.
973
+ * The popup opens immediately (preserving user gesture) and navigates
974
+ * to the verification URL.
975
+ *
976
+ * @param url - The verification URL to load
977
+ * @throws EdgePopupBlockedError if the popup is blocked
978
+ */
979
+ openPopup(url) {
980
+ this.popupManager = new PopupManager();
981
+ const win = this.popupManager.open({
982
+ onUserClose: () => this.handleUserClose()
983
+ });
984
+ if (!win) {
985
+ this.popupManager = null;
986
+ this.config.onError?.({
987
+ type: "edge:transfer-verify:error",
988
+ sessionId: this.config.sessionId,
989
+ transferId: "",
990
+ nonce: this.nonce,
991
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
992
+ error: "Popup was blocked by the browser. Please allow popups for this site."
993
+ });
994
+ throw new EdgePopupBlockedError2();
995
+ }
996
+ this.popupManager.navigateTo(url);
997
+ }
998
+ /**
999
+ * Opens the verification UI in an iframe.
1000
+ *
1001
+ * Uses IframeManager to create and mount the iframe into the configured container.
1002
+ *
1003
+ * @param url - The verification URL to load
1004
+ */
1005
+ openIframe(url) {
1006
+ this.iframeManager = new IframeManager();
1007
+ this.iframeManager.mount(
1008
+ {
1009
+ url,
1010
+ container: this.config.container,
1011
+ title: "EDGE Connect Transfer Verification"
1012
+ },
1013
+ {
1014
+ onError: (error) => {
1015
+ this.config.onError?.({
1016
+ type: "edge:transfer-verify:error",
1017
+ sessionId: this.config.sessionId,
1018
+ transferId: "",
1019
+ nonce: this.nonce,
1020
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1021
+ error: error.message
1022
+ });
1023
+ }
1024
+ }
1025
+ );
1026
+ }
1027
+ // ===========================================================================
1028
+ // PRIVATE — MESSAGE HANDLING
1029
+ // ===========================================================================
1030
+ /**
1031
+ * Sets up the postMessage listener for receiving events from the verification page.
1032
+ *
1033
+ * SECURITY: Every incoming message is validated for:
1034
+ * 1. Origin matches the expected EDGE verification origin
1035
+ * 2. Message schema matches TransferVerifyMessage
1036
+ * 3. Nonce matches the nonce we sent
1037
+ * 4. SessionId matches the session we opened
1038
+ */
1039
+ setupMessageListener() {
1040
+ this.messageHandler = (event) => {
1041
+ if (event.origin !== this.expectedOrigin) {
1042
+ return;
1043
+ }
1044
+ const data = event.data;
1045
+ if (!isValidTransferVerifyMessage(data)) {
1046
+ return;
1047
+ }
1048
+ if (data.nonce !== this.nonce) {
1049
+ return;
1050
+ }
1051
+ if (data.sessionId !== this.config.sessionId) {
1052
+ return;
1053
+ }
1054
+ const verifiedEvent = {
1055
+ type: data.type,
1056
+ sessionId: data.sessionId,
1057
+ transferId: data.transferId,
1058
+ nonce: data.nonce,
1059
+ timestamp: data.timestamp,
1060
+ error: data.error
1061
+ };
1062
+ this.config.onEvent?.(verifiedEvent);
1063
+ switch (data.type) {
1064
+ case "edge:transfer-verify:success":
1065
+ this.handleSuccess(verifiedEvent);
1066
+ break;
1067
+ case "edge:transfer-verify:error":
1068
+ this.handleError(verifiedEvent);
1069
+ break;
1070
+ case "edge:transfer-verify:cancel":
1071
+ this.handleCancel(verifiedEvent);
1072
+ break;
1073
+ case "edge:transfer-verify:expired":
1074
+ this.handleExpired(verifiedEvent);
1075
+ break;
1076
+ case "edge:transfer-verify:loaded":
1077
+ this.handleLoaded(verifiedEvent);
1078
+ break;
1079
+ }
1080
+ };
1081
+ window.addEventListener("message", this.messageHandler);
1082
+ }
1083
+ /**
1084
+ * Removes the postMessage listener.
1085
+ */
1086
+ removeMessageListener() {
1087
+ if (this.messageHandler) {
1088
+ window.removeEventListener("message", this.messageHandler);
1089
+ this.messageHandler = null;
1090
+ }
1091
+ }
1092
+ // ===========================================================================
1093
+ // PRIVATE — EVENT HANDLERS
1094
+ // ===========================================================================
1095
+ /**
1096
+ * Handles a successful verification event.
1097
+ *
1098
+ * Closes the UI and notifies the consumer.
1099
+ */
1100
+ handleSuccess(event) {
1101
+ this.close();
1102
+ this.config.onSuccess?.(event);
1103
+ }
1104
+ /**
1105
+ * Handles a verification error event.
1106
+ *
1107
+ * Does NOT close the UI — the verification page may allow retry.
1108
+ * The consumer can call close() or destroy() if they want to dismiss.
1109
+ */
1110
+ handleError(event) {
1111
+ this.config.onError?.(event);
1112
+ }
1113
+ /**
1114
+ * Handles a cancellation event from the user.
1115
+ *
1116
+ * Closes the UI and notifies the consumer.
1117
+ */
1118
+ handleCancel(event) {
1119
+ this.close();
1120
+ this.config.onCancel?.(event);
1121
+ }
1122
+ /**
1123
+ * Handles a session/OTP expiration event.
1124
+ *
1125
+ * Closes the UI and notifies the consumer.
1126
+ */
1127
+ handleExpired(event) {
1128
+ this.close();
1129
+ this.config.onExpired?.(event);
1130
+ }
1131
+ /**
1132
+ * Handles the loaded event from the verification page.
1133
+ *
1134
+ * The verification UI is ready for user interaction.
1135
+ * If geolocation data was provided, forwards it to the verification page
1136
+ * via postMessage for server-side cross-referencing.
1137
+ */
1138
+ handleLoaded(event) {
1139
+ this.config.onLoaded?.(event);
1140
+ if (this.config.geolocation) {
1141
+ const target = this.mode === "popup" ? this.popupManager?.getWindow?.() : this.iframeManager?.getContentWindow?.();
1142
+ if (target) {
1143
+ target.postMessage(
1144
+ {
1145
+ type: "edge:transfer-verify:geo",
1146
+ nonce: this.nonce,
1147
+ latitude: this.config.geolocation.latitude,
1148
+ longitude: this.config.geolocation.longitude,
1149
+ accuracy: this.config.geolocation.accuracy,
1150
+ timestamp: this.config.geolocation.timestamp
1151
+ },
1152
+ this.expectedOrigin
1153
+ );
1154
+ }
1155
+ }
1156
+ }
1157
+ /**
1158
+ * Handles user closing the popup manually (popup mode only).
1159
+ *
1160
+ * Treated as a cancellation — we fire onCancel with a synthetic event.
1161
+ */
1162
+ handleUserClose() {
1163
+ this.isOpened = false;
1164
+ this.popupManager = null;
1165
+ const syntheticEvent = {
1166
+ type: "edge:transfer-verify:cancel",
1167
+ sessionId: this.config.sessionId,
1168
+ transferId: "",
1169
+ nonce: this.nonce,
1170
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1171
+ };
1172
+ this.config.onEvent?.(syntheticEvent);
1173
+ this.config.onCancel?.(syntheticEvent);
1174
+ }
1175
+ };
1176
+
1177
+ // src/geolocation.ts
1178
+ async function collectGeolocation(options) {
1179
+ if (typeof navigator === "undefined" || !navigator.geolocation) {
1180
+ return null;
1181
+ }
1182
+ try {
1183
+ const position = await new Promise((resolve, reject) => {
1184
+ navigator.geolocation.getCurrentPosition(resolve, reject, {
1185
+ timeout: options?.timeout ?? 1e4,
1186
+ maximumAge: options?.maximumAge ?? 6e4,
1187
+ enableHighAccuracy: options?.enableHighAccuracy ?? false
1188
+ });
1189
+ });
1190
+ return {
1191
+ latitude: position.coords.latitude,
1192
+ longitude: position.coords.longitude,
1193
+ accuracy: position.coords.accuracy,
1194
+ timestamp: new Date(position.timestamp).toISOString()
1195
+ };
1196
+ } catch {
1197
+ return null;
1198
+ }
1199
+ }
1200
+
618
1201
  // src/index.ts
619
- import {
620
- EdgePopupBlockedError as EdgePopupBlockedError2,
621
- EdgeStateMismatchError as EdgeStateMismatchError2,
622
- EdgeError,
623
- isEdgeError
624
- } from "@edge-markets/connect";
625
- import {
626
- EDGE_SCOPES,
627
- ALL_EDGE_SCOPES as ALL_EDGE_SCOPES2
628
- } from "@edge-markets/connect";
1202
+ import { EdgeError, EdgePopupBlockedError as EdgePopupBlockedError3, EdgeStateMismatchError, isEdgeError } from "@edge-markets/connect";
1203
+ import { ALL_EDGE_SCOPES as ALL_EDGE_SCOPES2, EDGE_SCOPES } from "@edge-markets/connect";
629
1204
  export {
630
1205
  ALL_EDGE_SCOPES2 as ALL_EDGE_SCOPES,
631
1206
  EDGE_SCOPES,
632
1207
  EdgeError,
633
1208
  EdgeLink,
634
- EdgePopupBlockedError2 as EdgePopupBlockedError,
635
- EdgeStateMismatchError2 as EdgeStateMismatchError,
1209
+ EdgePopupBlockedError3 as EdgePopupBlockedError,
1210
+ EdgeStateMismatchError,
1211
+ EdgeTransferVerify,
1212
+ IframeManager,
636
1213
  assertCryptoAvailable,
1214
+ collectGeolocation,
637
1215
  generatePKCE,
638
1216
  generateState,
639
1217
  isEdgeError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edge-markets/connect-link",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Browser SDK for EDGE Connect popup authentication",
5
5
  "author": "EdgeBoost",
6
6
  "license": "MIT",
@@ -21,7 +21,7 @@
21
21
  }
22
22
  },
23
23
  "dependencies": {
24
- "@edge-markets/connect": "^1.0.0"
24
+ "@edge-markets/connect": "1.5.0"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "react": ">=17.0.0"
@@ -32,7 +32,9 @@
32
32
  }
33
33
  },
34
34
  "devDependencies": {
35
+ "@types/jsdom": "^28.0.1",
35
36
  "@types/react": "^18.2.0",
37
+ "jsdom": "^29.0.2",
36
38
  "tsup": "^8.0.0",
37
39
  "typescript": "^5.3.0",
38
40
  "vitest": "^1.0.0"