@benji-money/connect-sdk 1.1.0-beta.3 → 1.1.0-beta.5

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 CHANGED
@@ -102,7 +102,7 @@ The SDK accepts the following configuration options:
102
102
  | ---- | ---------- | ----- |
103
103
  | **CONNECT** (default) | Centered or anchored modal iframe | No `authType`; standard connect / verify flow |
104
104
  | **LOGIN + DUMMY / no `authType`** | Same visible modal iframe | Partner auth runs in a nested iframe inside Benji Connect |
105
- | **LOGIN + non-DUMMY** | Partner popup only | Pass `authType`; Benji Connect iframe is headless — see [LOGIN popup example](#login-popup-example) below |
105
+ | **LOGIN + non-DUMMY** | Partner popup only | Pass `authType`; Benji Connect iframe is headless — popup shows a loading spinner until partner auth navigates — see [LOGIN popup example](#login-popup-example) below |
106
106
 
107
107
  ### Placement
108
108
 
@@ -172,7 +172,7 @@ const sdk = new ConnectSDK({
172
172
  await sdk.open();
173
173
  ```
174
174
 
175
- > **`bottom-end`** positions the **partner popup** below the anchor with its right edge aligned to the anchor's right edge. The Benji Connect iframe stays headless (not visible).
175
+ > **`bottom-end`** positions the **partner popup** below the anchor with its right edge aligned to the anchor's right edge. The Benji Connect iframe stays headless (not visible). The pre-opened popup shows generic loading UI (`Loading sign-in…`) until Benji Connect sends the partner auth URL.
176
176
 
177
177
  ### Environments
178
178
 
@@ -0,0 +1,14 @@
1
+ // These are compile-time constants replaced by tsup's `define` option.
2
+ // Declaring them globally prevents TypeScript errors.
3
+
4
+
5
+ declare global {
6
+ const __VERSION__: string;
7
+ const __NAMESPACE__: string;
8
+
9
+ // Global browser exposure for UMD builds
10
+ interface Window {
11
+ // SDK main class (UMD builds attach this)
12
+ ConnectSDK?: any;
13
+ }
14
+ }
@@ -110,7 +110,7 @@ var ConnectSDK = (() => {
110
110
  // src/utils/config.ts
111
111
  var buildContext = () => ({
112
112
  namespace: "benji-connect-sdk" ? "benji-connect-sdk" : "benji-connect-sdk",
113
- version: "1.1.0-beta.3" ? String("1.1.0-beta.3") : "0.0.0"
113
+ version: "1.1.0-beta.5" ? String("1.1.0-beta.5") : "0.0.0"
114
114
  });
115
115
  function isBenjiConnectEnvironment(environment) {
116
116
  return environment === "local" /* LOCAL */ || environment === "development" /* DEVELOPMENT */ || environment === "sandbox" /* SANDBOX */ || environment === "production" /* PRODUCTION */;
@@ -130,6 +130,92 @@ var ConnectSDK = (() => {
130
130
  return defaultEnvironment;
131
131
  }
132
132
 
133
+ // src/utils/loginPopupLoading.ts
134
+ var LOGIN_POPUP_TITLE = "User sign-in";
135
+ var LOGIN_POPUP_MESSAGE = "Loading sign-in\u2026";
136
+ function buildLoginPopupLoadingHtml() {
137
+ return `<!DOCTYPE html>
138
+ <html lang="en">
139
+ <head>
140
+ <meta charset="utf-8">
141
+ <meta name="viewport" content="width=device-width, initial-scale=1">
142
+ <title>${LOGIN_POPUP_TITLE}</title>
143
+ <style>
144
+ * { box-sizing: border-box; }
145
+ html, body {
146
+ margin: 0;
147
+ height: 100%;
148
+ background: #fff;
149
+ }
150
+ body {
151
+ width: 100%;
152
+ height: 100%;
153
+ display: flex;
154
+ justify-content: center;
155
+ align-items: center;
156
+ font-family: Inter, system-ui, -apple-system, sans-serif;
157
+ }
158
+ .loading-content {
159
+ display: flex;
160
+ flex-direction: column;
161
+ align-items: center;
162
+ gap: 14px;
163
+ padding: 24px;
164
+ max-width: 320px;
165
+ transform: translateY(-28px);
166
+ }
167
+ .loader {
168
+ width: 48px;
169
+ height: 48px;
170
+ border: 3px solid #e5e7eb;
171
+ border-top-color: #9ca3af;
172
+ border-radius: 50%;
173
+ animation: spin 0.75s linear infinite;
174
+ }
175
+ @keyframes spin {
176
+ to { transform: rotate(360deg); }
177
+ }
178
+ .loading-text {
179
+ font-size: 16px;
180
+ text-align: center;
181
+ font-family: Inter, sans-serif;
182
+ margin: 0;
183
+ background: linear-gradient(
184
+ 90deg,
185
+ #374151 0%,
186
+ #9ca3af 50%,
187
+ #374151 100%
188
+ );
189
+ background-size: 200% 100%;
190
+ -webkit-background-clip: text;
191
+ background-clip: text;
192
+ -webkit-text-fill-color: transparent;
193
+ animation: shimmer 1.8s ease-in-out infinite;
194
+ }
195
+ @keyframes shimmer {
196
+ 0% { background-position: 100% 0; }
197
+ 100% { background-position: -100% 0; }
198
+ }
199
+ </style>
200
+ </head>
201
+ <body>
202
+ <div class="loading-content">
203
+ <div class="loader" aria-hidden="true"></div>
204
+ <p class="loading-text">${LOGIN_POPUP_MESSAGE}</p>
205
+ </div>
206
+ </body>
207
+ </html>`;
208
+ }
209
+ function writeLoginPopupLoadingDocument(popup) {
210
+ try {
211
+ popup.document.open();
212
+ popup.document.write(buildLoginPopupLoadingHtml());
213
+ popup.document.close();
214
+ } catch (error) {
215
+ console.warn("[Connect SDK] Failed to write LOGIN popup loading document", error);
216
+ }
217
+ }
218
+
133
219
  // src/utils/placement.ts
134
220
  var IFRAME_WIDTH = 400;
135
221
  var IFRAME_HEIGHT = 645;
@@ -584,7 +670,28 @@ var ConnectSDK = (() => {
584
670
  })(BenjiConnectEventType || {});
585
671
 
586
672
  // src/utils/auth.ts
673
+ var AUTH_MESSAGE_TYPES = /* @__PURE__ */ new Set([
674
+ "auth_config",
675
+ "code_received",
676
+ "code_verification_error",
677
+ "loyalty_verification",
678
+ "phone_verification",
679
+ "request_auth_config",
680
+ "request_code_exchange",
681
+ "request_code_with_login",
682
+ "request_code_with_phone",
683
+ "request_loyalty_verification",
684
+ "request_phone_verification"
685
+ ]);
587
686
  var CONNECT_MESSAGE_TYPES = new Set(Object.values(BenjiConnectEventType));
687
+ function isAuthMessageType(data2) {
688
+ if (!data2 || typeof data2 !== "object") return false;
689
+ const type = data2.type;
690
+ return typeof type === "string" && AUTH_MESSAGE_TYPES.has(type);
691
+ }
692
+ function shouldRelayMessage(mode, data2) {
693
+ return mode === 5 /* LOGIN */ && isAuthMessageType(data2);
694
+ }
588
695
  function isConnectMessageType(data2) {
589
696
  if (!data2 || typeof data2 !== "object") return false;
590
697
  const type = data2.type;
@@ -714,6 +821,12 @@ var ConnectSDK = (() => {
714
821
  this.closeLoginPopup = config.closeLoginPopup;
715
822
  this.navigateLoginPopup = config.navigateLoginPopup;
716
823
  }
824
+ static setLoginContext(context) {
825
+ this.iframe = context.iframe;
826
+ this.loginPopup = context.loginPopup;
827
+ this.mode = context.mode;
828
+ this.connectOrigin = new URL(Endpoints.benji_connect_auth_url).origin;
829
+ }
717
830
  static setMode(mode) {
718
831
  this.mode = mode;
719
832
  }
@@ -729,7 +842,10 @@ var ConnectSDK = (() => {
729
842
  window.removeEventListener("unhandledrejection", this.errorEventHandler);
730
843
  window.removeEventListener("message", this.messageEventHandler);
731
844
  this.configuredListeners = false;
845
+ this.iframe = null;
846
+ this.loginPopup = null;
732
847
  this.mode = 1 /* CONNECT */;
848
+ this.connectOrigin = "";
733
849
  }
734
850
  static handleErrorMessage(event) {
735
851
  switch (event.type) {
@@ -749,8 +865,49 @@ var ConnectSDK = (() => {
749
865
  }
750
866
  }
751
867
  }
868
+ static relayToIframe(event) {
869
+ var _a;
870
+ const iframeWindow = (_a = this.iframe) == null ? void 0 : _a.contentWindow;
871
+ if (!iframeWindow) {
872
+ console.warn("[Benji Connect SDK] Skipping login auth relay, iframe not ready");
873
+ return;
874
+ }
875
+ iframeWindow.postMessage(event.data, this.connectOrigin);
876
+ }
877
+ static relayToPopup(payload) {
878
+ if (!this.loginPopup) {
879
+ console.warn("[Benji Connect SDK] Skipping login auth relay, no login popup");
880
+ return;
881
+ }
882
+ try {
883
+ this.loginPopup.postMessage(payload, "*");
884
+ } catch (error) {
885
+ console.error("[Benji Connect SDK] Failed to forward login auth message to popup", error);
886
+ }
887
+ }
752
888
  static handleMessage(event) {
753
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
889
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
890
+ if (shouldRelayMessage(this.mode, event.data) && this.loginPopup) {
891
+ const expectedOrigin2 = this.connectOrigin || new URL(Endpoints.benji_connect_auth_url).origin;
892
+ const iframeWindow = (_a = this.iframe) == null ? void 0 : _a.contentWindow;
893
+ if (event.source === this.loginPopup) {
894
+ this.relayToIframe(event);
895
+ return;
896
+ }
897
+ if (iframeWindow && event.source === iframeWindow) {
898
+ if (event.origin !== expectedOrigin2) {
899
+ console.warn(
900
+ "[Benji Connect SDK] Skipped auth relay, unexpected origin",
901
+ event.origin,
902
+ expectedOrigin2
903
+ );
904
+ return;
905
+ }
906
+ this.relayToPopup(event.data);
907
+ return;
908
+ }
909
+ return;
910
+ }
754
911
  if (!isConnectMessageType(event.data)) return;
755
912
  const expectedOrigin = new URL(Endpoints.benji_connect_auth_url).origin;
756
913
  if (!event || event.origin !== expectedOrigin) {
@@ -764,48 +921,53 @@ var ConnectSDK = (() => {
764
921
  const message = partial;
765
922
  try {
766
923
  switch (message.type) {
924
+ case "PARTNER_LOGIN_OPEN" /* PARTNER_LOGIN_OPEN */: {
925
+ const connectMessage = message;
926
+ const url = (_b = connectMessage.data) == null ? void 0 : _b.url;
927
+ if (url) {
928
+ (_c = this.navigateLoginPopup) == null ? void 0 : _c.call(this, url);
929
+ } else {
930
+ (_d = this.closeLoginPopup) == null ? void 0 : _d.call(this);
931
+ }
932
+ break;
933
+ }
767
934
  case "AUTH_SUCCESS" /* AUTH_SUCCESS */: {
768
935
  const connectMessage = message;
769
936
  const callbackData = BenjiConnectCallbackMapperMap.AUTH_SUCCESS(connectMessage, connectMessage.data);
770
- (_a = this.onEvent) == null ? void 0 : _a.call(this, callbackData.type, callbackData.metadata);
937
+ (_e = this.onEvent) == null ? void 0 : _e.call(this, callbackData.type, callbackData.metadata);
771
938
  break;
772
939
  }
773
940
  case "FLOW_EXIT" /* FLOW_EXIT */: {
774
941
  const connectMessage = message;
775
942
  const callbackData = BenjiConnectCallbackMapperMap.FLOW_EXIT(connectMessage.data);
776
- (_b = this.onExit) == null ? void 0 : _b.call(this, callbackData.metadata);
777
- (_c = this.close) == null ? void 0 : _c.call(this);
943
+ (_f = this.onExit) == null ? void 0 : _f.call(this, callbackData.metadata);
944
+ (_g = this.close) == null ? void 0 : _g.call(this);
778
945
  break;
779
946
  }
780
947
  case "FLOW_SUCCESS" /* FLOW_SUCCESS */: {
781
948
  const connectMessage = message;
782
949
  const callbackData = BenjiConnectCallbackMapperMap.FLOW_SUCCESS(connectMessage.data);
783
- (_d = this.onSuccess) == null ? void 0 : _d.call(this, callbackData.token, callbackData.metadata);
950
+ (_h = this.onSuccess) == null ? void 0 : _h.call(this, callbackData.token, callbackData.metadata);
784
951
  if (this.mode === 5 /* LOGIN */) {
785
- (_e = this.close) == null ? void 0 : _e.call(this);
952
+ (_i = this.close) == null ? void 0 : _i.call(this);
786
953
  }
787
954
  break;
788
955
  }
789
956
  case "ERROR" /* ERROR */: {
790
957
  const connectMessage = message;
791
958
  const callbackData = BenjiConnectCallbackMapperMap.ERROR(connectMessage.data);
792
- (_f = this.onError) == null ? void 0 : _f.call(this, callbackData.error, callbackData.error_id, callbackData.metadata);
959
+ (_j = this.onError) == null ? void 0 : _j.call(this, callbackData.error, callbackData.error_id, callbackData.metadata);
793
960
  break;
794
961
  }
795
962
  case "EVENT" /* EVENT */: {
796
963
  const connectMessage = message;
797
964
  const callbackData = mapToOnEventData(connectMessage, connectMessage.data);
798
- (_g = this.onEvent) == null ? void 0 : _g.call(this, callbackData.type, callbackData.metadata);
965
+ (_k = this.onEvent) == null ? void 0 : _k.call(this, callbackData.type, callbackData.metadata);
799
966
  break;
800
967
  }
801
- case "PARTNER_LOGIN_OPEN" /* PARTNER_LOGIN_OPEN */: {
802
- const connectMessage = message;
803
- const url = (_h = connectMessage.data) == null ? void 0 : _h.url;
804
- if (url) {
805
- (_i = this.navigateLoginPopup) == null ? void 0 : _i.call(this, url);
806
- } else {
807
- (_j = this.closeLoginPopup) == null ? void 0 : _j.call(this);
808
- }
968
+ default: {
969
+ const _exhaustive = message.type;
970
+ console.warn("[Benji Connect SDK] Unhandled connect message type", _exhaustive);
809
971
  break;
810
972
  }
811
973
  }
@@ -814,7 +976,10 @@ var ConnectSDK = (() => {
814
976
  }
815
977
  };
816
978
  _MessageRouter.configuredListeners = false;
979
+ _MessageRouter.iframe = null;
980
+ _MessageRouter.loginPopup = null;
817
981
  _MessageRouter.mode = 1 /* CONNECT */;
982
+ _MessageRouter.connectOrigin = "";
818
983
  _MessageRouter.errorEventHandler = (event) => {
819
984
  return _MessageRouter.handleErrorMessage(event);
820
985
  };
@@ -907,6 +1072,7 @@ var ConnectSDK = (() => {
907
1072
  );
908
1073
  return false;
909
1074
  }
1075
+ writeLoginPopupLoadingDocument(this.loginPopup);
910
1076
  this.startPopupClosedPoll();
911
1077
  return true;
912
1078
  }
@@ -969,8 +1135,17 @@ var ConnectSDK = (() => {
969
1135
  return;
970
1136
  }
971
1137
  this.openUI();
1138
+ this.syncLoginContext(mode);
1139
+ }
1140
+ syncLoginContext(mode) {
1141
+ MessageRouter.setLoginContext({
1142
+ iframe: this.iframe,
1143
+ loginPopup: this.loginPopup,
1144
+ mode
1145
+ });
972
1146
  }
973
1147
  openUI() {
1148
+ var _a;
974
1149
  if (this.iframe) return;
975
1150
  const headless = this.isLoginPopupOnly();
976
1151
  this.container = document.createElement("div");
@@ -1019,9 +1194,9 @@ var ConnectSDK = (() => {
1019
1194
  }
1020
1195
  );
1021
1196
  this.container.addEventListener("click", (e) => {
1022
- var _a, _b;
1197
+ var _a2, _b;
1023
1198
  if (e.target === this.container) {
1024
- (_b = (_a = this.sdkConfig).onExit) == null ? void 0 : _b.call(_a, {
1199
+ (_b = (_a2 = this.sdkConfig).onExit) == null ? void 0 : _b.call(_a2, {
1025
1200
  context: buildContext(),
1026
1201
  trigger: "TAPPED_OUT_OF_BOUNDS" /* TAPPED_OUT_OF_BOUNDS */
1027
1202
  });
@@ -1029,6 +1204,7 @@ var ConnectSDK = (() => {
1029
1204
  }
1030
1205
  });
1031
1206
  }
1207
+ this.syncLoginContext((_a = this.sdkConfig.mode) != null ? _a : 1 /* CONNECT */);
1032
1208
  }
1033
1209
  navigateLoginPopup(url) {
1034
1210
  if (!this.loginPopup || this.loginPopup.closed) {