@phantom/browser-sdk 1.0.0-beta.14 → 1.0.0-beta.15

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
@@ -93,24 +93,36 @@ const ethResult = await sdk.ethereum.sendTransaction({
93
93
 
94
94
  ### Connection Options
95
95
 
96
- For embedded user-wallets, you can specify authentication providers:
96
+ The `connect()` method automatically switches between providers based on the authentication method you specify:
97
97
 
98
98
  ```typescript
99
- // Default: Show provider selection screen
99
+ // Connect with current provider (no switching)
100
100
  const result = await sdk.connect();
101
101
 
102
- // Google authentication (skips provider selection)
102
+ // Connect with injected provider (Phantom extension)
103
+ // Automatically switches to injected provider if not already using it
103
104
  const result = await sdk.connect({
104
- authOptions: {
105
- provider: "google",
106
- },
105
+ provider: "injected",
107
106
  });
108
107
 
109
- // Apple authentication (skips provider selection)
108
+ // Connect with Google authentication (embedded provider)
109
+ // Automatically switches to embedded provider if not already using it
110
110
  const result = await sdk.connect({
111
- authOptions: {
112
- provider: "apple",
113
- },
111
+ provider: "google",
112
+ });
113
+
114
+ // Connect with Apple authentication (embedded provider)
115
+ // Automatically switches to embedded provider if not already using it
116
+ const result = await sdk.connect({
117
+ provider: "apple",
118
+ });
119
+
120
+
121
+ // Connect with Phantom authentication (embedded provider)
122
+ // Uses Phantom extension or mobile app for authentication
123
+ // Automatically switches to embedded provider if not already using it
124
+ const result = await sdk.connect({
125
+ provider: "phantom",
114
126
  });
115
127
  ```
116
128
 
package/dist/index.d.ts CHANGED
@@ -52,14 +52,33 @@ declare const DebugCategory: {
52
52
  readonly SESSION: "Session";
53
53
  };
54
54
 
55
+ /**
56
+ * Phantom extension app.login API types
57
+ */
58
+ interface PhantomAppLoginOptions {
59
+ publicKey: string;
60
+ appId: string;
61
+ sessionId: string;
62
+ }
63
+ interface PhantomAppLoginResult {
64
+ walletId: string;
65
+ organizationId: string;
66
+ accountDerivationIndex?: number;
67
+ expiresInMs?: number;
68
+ }
69
+ interface PhantomApp {
70
+ login(options: PhantomAppLoginOptions): Promise<PhantomAppLoginResult>;
71
+ }
55
72
  declare global {
56
73
  interface Window {
57
74
  phantom?: {
58
75
  solana?: unknown;
59
76
  ethereum?: unknown;
60
- };
77
+ app?: PhantomApp;
78
+ } | undefined;
61
79
  }
62
80
  }
81
+
63
82
  interface InjectedProviderConfig {
64
83
  addressTypes: AddressType[];
65
84
  }
@@ -105,9 +124,6 @@ interface ProviderPreference {
105
124
  type: "injected" | "embedded";
106
125
  embeddedWalletType?: "app-wallet" | "user-wallet";
107
126
  }
108
- interface SwitchProviderOptions {
109
- embeddedWalletType?: "app-wallet" | "user-wallet" | (string & Record<never, never>);
110
- }
111
127
 
112
128
  /**
113
129
  * Browser SDK with chain-specific API
@@ -141,10 +157,6 @@ declare class BrowserSDK {
141
157
  * Disconnect from the wallet
142
158
  */
143
159
  disconnect(): Promise<void>;
144
- /**
145
- * Switch between provider types (injected vs embedded)
146
- */
147
- switchProvider(type: "injected" | "embedded", options?: SwitchProviderOptions): Promise<void>;
148
160
  /**
149
161
  * Check if the SDK is connected to a wallet
150
162
  */
package/dist/index.js CHANGED
@@ -901,6 +901,47 @@ var BrowserStorage = class {
901
901
  };
902
902
  });
903
903
  }
904
+ async getShouldClearPreviousSession() {
905
+ debug.log(DebugCategory.STORAGE, "Getting shouldClearPreviousSession flag from IndexedDB");
906
+ const db = await this.getDB();
907
+ return new Promise((resolve, reject) => {
908
+ const transaction = db.transaction([this.storeName], "readonly");
909
+ const store = transaction.objectStore(this.storeName);
910
+ const request = store.get("shouldClearPreviousSession");
911
+ request.onsuccess = () => {
912
+ const shouldClear = request.result ?? false;
913
+ debug.log(DebugCategory.STORAGE, "Retrieved shouldClearPreviousSession flag from IndexedDB", {
914
+ shouldClear
915
+ });
916
+ resolve(shouldClear);
917
+ };
918
+ request.onerror = () => {
919
+ debug.error(DebugCategory.STORAGE, "Failed to get shouldClearPreviousSession flag from IndexedDB", {
920
+ error: request.error
921
+ });
922
+ reject(request.error);
923
+ };
924
+ });
925
+ }
926
+ async setShouldClearPreviousSession(should) {
927
+ debug.log(DebugCategory.STORAGE, "Setting shouldClearPreviousSession flag in IndexedDB", { should });
928
+ const db = await this.getDB();
929
+ return new Promise((resolve, reject) => {
930
+ const transaction = db.transaction([this.storeName], "readwrite");
931
+ const store = transaction.objectStore(this.storeName);
932
+ const request = store.put(should, "shouldClearPreviousSession");
933
+ request.onsuccess = () => {
934
+ debug.log(DebugCategory.STORAGE, "Successfully set shouldClearPreviousSession flag in IndexedDB");
935
+ resolve();
936
+ };
937
+ request.onerror = () => {
938
+ debug.error(DebugCategory.STORAGE, "Failed to set shouldClearPreviousSession flag in IndexedDB", {
939
+ error: request.error
940
+ });
941
+ reject(request.error);
942
+ };
943
+ });
944
+ }
904
945
  };
905
946
 
906
947
  // src/providers/embedded/adapters/url-params.ts
@@ -914,6 +955,147 @@ var browserUrlParamsAccessor = new BrowserURLParamsAccessor();
914
955
 
915
956
  // src/providers/embedded/adapters/auth.ts
916
957
  var import_constants = require("@phantom/constants");
958
+
959
+ // src/utils/browser-detection.ts
960
+ function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
961
+ let name = "unknown";
962
+ let version = "unknown";
963
+ if (!userAgent || typeof userAgent !== "string") {
964
+ return { name, version, userAgent: "unknown" };
965
+ }
966
+ try {
967
+ if (userAgent.includes("Edg/")) {
968
+ name = "edge";
969
+ const match = userAgent.match(/Edg\/([0-9]+(?:\.[0-9]+)*)/);
970
+ if (match)
971
+ version = match[1].split(".")[0];
972
+ } else if (userAgent.includes("OPR/") || userAgent.includes("Opera/")) {
973
+ name = "opera";
974
+ const match = userAgent.match(/(?:OPR|Opera)\/([0-9]+(?:\.[0-9]+)*)/);
975
+ if (match)
976
+ version = match[1].split(".")[0];
977
+ } else if (userAgent.includes("SamsungBrowser/")) {
978
+ name = "samsung";
979
+ const match = userAgent.match(/SamsungBrowser\/([0-9]+(?:\.[0-9]+)*)/);
980
+ if (match)
981
+ version = match[1].split(".")[0];
982
+ } else if (userAgent.includes("DuckDuckGo/")) {
983
+ name = "duckduckgo";
984
+ const match = userAgent.match(/DuckDuckGo\/([0-9]+(?:\.[0-9]+)*)/);
985
+ if (match)
986
+ version = match[1].split(".")[0];
987
+ } else if (userAgent.includes("Chrome/") && hasBraveAPI) {
988
+ name = "brave";
989
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
990
+ if (match)
991
+ version = match[1].split(".")[0];
992
+ } else if (userAgent.includes("Mobile/") || userAgent.includes("Android")) {
993
+ if (userAgent.includes("Chrome/")) {
994
+ name = "chrome-mobile";
995
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
996
+ if (match)
997
+ version = match[1].split(".")[0];
998
+ } else if (userAgent.includes("Firefox/")) {
999
+ name = "firefox-mobile";
1000
+ const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
1001
+ if (match)
1002
+ version = match[1].split(".")[0];
1003
+ } else if (userAgent.includes("Safari/") && userAgent.includes("Mobile/")) {
1004
+ name = "safari-mobile";
1005
+ const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
1006
+ if (match)
1007
+ version = match[1].split(".")[0];
1008
+ } else {
1009
+ name = "mobile";
1010
+ }
1011
+ } else if (userAgent.includes("Chrome/")) {
1012
+ name = "chrome";
1013
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
1014
+ if (match)
1015
+ version = match[1].split(".")[0];
1016
+ } else if (userAgent.includes("Firefox/")) {
1017
+ name = "firefox";
1018
+ const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
1019
+ if (match)
1020
+ version = match[1].split(".")[0];
1021
+ } else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome/")) {
1022
+ name = "safari";
1023
+ const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
1024
+ if (match)
1025
+ version = match[1].split(".")[0];
1026
+ }
1027
+ if (name === "unknown") {
1028
+ const patterns = [
1029
+ { regex: /Chrome\/([0-9]+)/, name: "chrome" },
1030
+ { regex: /Firefox\/([0-9]+)/, name: "firefox" },
1031
+ { regex: /Safari\/([0-9]+)/, name: "safari" },
1032
+ { regex: /Edge\/([0-9]+)/, name: "edge" },
1033
+ { regex: /Opera\/([0-9]+)/, name: "opera" }
1034
+ ];
1035
+ for (const pattern of patterns) {
1036
+ const match = userAgent.match(pattern.regex);
1037
+ if (match) {
1038
+ name = pattern.name;
1039
+ version = match[1];
1040
+ break;
1041
+ }
1042
+ }
1043
+ }
1044
+ } catch (error) {
1045
+ }
1046
+ return { name, version, userAgent };
1047
+ }
1048
+ function detectBrowser() {
1049
+ if (typeof window === "undefined" || !window.navigator?.userAgent) {
1050
+ return { name: "unknown", version: "unknown", userAgent: "unknown" };
1051
+ }
1052
+ const userAgent = window.navigator.userAgent;
1053
+ const hasBraveAPI = !!navigator.brave;
1054
+ return parseBrowserFromUserAgent(userAgent, hasBraveAPI);
1055
+ }
1056
+ function getPlatformName() {
1057
+ const { name, version } = detectBrowser();
1058
+ return version !== "unknown" ? `${name}-v${version}` : name;
1059
+ }
1060
+ function getBrowserDisplayName() {
1061
+ const { name, version } = detectBrowser();
1062
+ const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
1063
+ return version !== "unknown" ? `${capitalizedName} ${version}` : capitalizedName;
1064
+ }
1065
+ function isMobileDevice() {
1066
+ if (typeof window === "undefined" || !window.navigator?.userAgent) {
1067
+ return false;
1068
+ }
1069
+ const userAgent = window.navigator.userAgent.toLowerCase();
1070
+ const mobilePatterns = [
1071
+ /android/,
1072
+ /iphone|ipad|ipod/,
1073
+ /blackberry/,
1074
+ /windows phone/,
1075
+ /mobile/,
1076
+ /tablet/,
1077
+ /silk/,
1078
+ /kindle/,
1079
+ /opera mini/,
1080
+ /opera mobi/
1081
+ ];
1082
+ const isMobileUA = mobilePatterns.some((pattern) => pattern.test(userAgent));
1083
+ let isSmallScreen = false;
1084
+ try {
1085
+ isSmallScreen = window.screen.width <= 768 || window.screen.height <= 768;
1086
+ } catch (error) {
1087
+ isSmallScreen = false;
1088
+ }
1089
+ let isTouchDevice = false;
1090
+ try {
1091
+ isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
1092
+ } catch (error) {
1093
+ isTouchDevice = false;
1094
+ }
1095
+ return isMobileUA || isSmallScreen && isTouchDevice;
1096
+ }
1097
+
1098
+ // src/providers/embedded/adapters/auth.ts
917
1099
  var BrowserAuthProvider = class {
918
1100
  constructor(urlParamsAccessor) {
919
1101
  this.urlParamsAccessor = urlParamsAccessor;
@@ -935,8 +1117,7 @@ var BrowserAuthProvider = class {
935
1117
  publicKey: phantomOptions.publicKey,
936
1118
  appId: phantomOptions.appId,
937
1119
  provider: phantomOptions.provider,
938
- authUrl: phantomOptions.authUrl,
939
- hasCustomData: !!phantomOptions.customAuthData
1120
+ authUrl: phantomOptions.authUrl
940
1121
  });
941
1122
  const baseUrl = phantomOptions.authUrl || import_constants.DEFAULT_AUTH_URL;
942
1123
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Using auth URL", { baseUrl });
@@ -945,8 +1126,12 @@ var BrowserAuthProvider = class {
945
1126
  app_id: phantomOptions.appId,
946
1127
  redirect_uri: phantomOptions.redirectUrl || (typeof window !== "undefined" ? this.getValidatedCurrentUrl() : ""),
947
1128
  session_id: phantomOptions.sessionId,
948
- clear_previous_session: true.toString(),
949
- sdk_version: "1.0.0-beta.14"
1129
+ // OAuth session management - defaults to allow refresh unless explicitly clearing after logout
1130
+ clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
1131
+ allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
1132
+ sdk_version: "1.0.0-beta.15",
1133
+ sdk_type: "browser",
1134
+ platform: detectBrowser().name
950
1135
  });
951
1136
  if (phantomOptions.provider) {
952
1137
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Provider specified, will skip selection", {
@@ -957,10 +1142,6 @@ var BrowserAuthProvider = class {
957
1142
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "No provider specified, defaulting to Google");
958
1143
  params.append("provider", "google");
959
1144
  }
960
- if (phantomOptions.customAuthData) {
961
- debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Adding custom auth data");
962
- params.append("authData", JSON.stringify(phantomOptions.customAuthData));
963
- }
964
1145
  const authContext = {
965
1146
  publicKey: phantomOptions.publicKey,
966
1147
  appId: phantomOptions.appId,
@@ -1031,12 +1212,14 @@ var BrowserAuthProvider = class {
1031
1212
  });
1032
1213
  const organizationId = this.urlParamsAccessor.getParam("organization_id");
1033
1214
  const expiresInMs = this.urlParamsAccessor.getParam("expires_in_ms");
1215
+ const authUserId = this.urlParamsAccessor.getParam("auth_user_id");
1034
1216
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Auth redirect parameters", {
1035
1217
  walletId,
1036
1218
  organizationId,
1037
1219
  sessionId,
1038
1220
  accountDerivationIndex,
1039
- expiresInMs
1221
+ expiresInMs,
1222
+ authUserId
1040
1223
  });
1041
1224
  if (!organizationId) {
1042
1225
  debug.error(DebugCategory.PHANTOM_CONNECT_AUTH, "Missing organization_id in auth response");
@@ -1050,9 +1233,9 @@ var BrowserAuthProvider = class {
1050
1233
  return {
1051
1234
  walletId,
1052
1235
  organizationId,
1053
- userInfo: context,
1054
1236
  accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
1055
- expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0
1237
+ expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
1238
+ authUserId: authUserId || void 0
1056
1239
  };
1057
1240
  } catch (error) {
1058
1241
  sessionStorage.removeItem("phantom-auth-context");
@@ -1061,6 +1244,54 @@ var BrowserAuthProvider = class {
1061
1244
  }
1062
1245
  };
1063
1246
 
1247
+ // src/providers/embedded/adapters/phantom-app.ts
1248
+ var import_browser_injected_sdk2 = require("@phantom/browser-injected-sdk");
1249
+ var BrowserPhantomAppProvider = class {
1250
+ /**
1251
+ * Check if the Phantom extension is installed in the browser
1252
+ */
1253
+ isAvailable() {
1254
+ return (0, import_browser_injected_sdk2.isPhantomExtensionInstalled)();
1255
+ }
1256
+ /**
1257
+ * Authenticate using the Phantom browser extension
1258
+ */
1259
+ async authenticate(options) {
1260
+ if (!this.isAvailable()) {
1261
+ throw new Error(
1262
+ "Phantom extension is not installed. Please install the Phantom browser extension to use this authentication method."
1263
+ );
1264
+ }
1265
+ if (!window.phantom?.app?.login || typeof window.phantom.app.login !== "function") {
1266
+ throw new Error(
1267
+ "Phantom extension authentication is not yet implemented. The extension needs to expose an app.login API (window.phantom.app.login)."
1268
+ );
1269
+ }
1270
+ try {
1271
+ const result = await window.phantom.app.login({
1272
+ publicKey: options.publicKey,
1273
+ appId: options.appId,
1274
+ sessionId: options.sessionId
1275
+ });
1276
+ if (!result || !result.walletId || !result.organizationId) {
1277
+ throw new Error("Invalid authentication response from Phantom extension");
1278
+ }
1279
+ return {
1280
+ walletId: result.walletId,
1281
+ organizationId: result.organizationId,
1282
+ provider: "phantom",
1283
+ accountDerivationIndex: result.accountDerivationIndex ?? 0,
1284
+ expiresInMs: result.expiresInMs ?? 0
1285
+ };
1286
+ } catch (error) {
1287
+ if (error instanceof Error) {
1288
+ throw error;
1289
+ }
1290
+ throw new Error(`Phantom extension authentication failed: ${String(error)}`);
1291
+ }
1292
+ }
1293
+ };
1294
+
1064
1295
  // src/providers/embedded/adapters/logger.ts
1065
1296
  var BrowserLogger = class {
1066
1297
  info(category, message, data) {
@@ -1077,145 +1308,6 @@ var BrowserLogger = class {
1077
1308
  }
1078
1309
  };
1079
1310
 
1080
- // src/utils/browser-detection.ts
1081
- function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
1082
- let name = "unknown";
1083
- let version = "unknown";
1084
- if (!userAgent || typeof userAgent !== "string") {
1085
- return { name, version, userAgent: "unknown" };
1086
- }
1087
- try {
1088
- if (userAgent.includes("Edg/")) {
1089
- name = "edge";
1090
- const match = userAgent.match(/Edg\/([0-9]+(?:\.[0-9]+)*)/);
1091
- if (match)
1092
- version = match[1].split(".")[0];
1093
- } else if (userAgent.includes("OPR/") || userAgent.includes("Opera/")) {
1094
- name = "opera";
1095
- const match = userAgent.match(/(?:OPR|Opera)\/([0-9]+(?:\.[0-9]+)*)/);
1096
- if (match)
1097
- version = match[1].split(".")[0];
1098
- } else if (userAgent.includes("SamsungBrowser/")) {
1099
- name = "samsung";
1100
- const match = userAgent.match(/SamsungBrowser\/([0-9]+(?:\.[0-9]+)*)/);
1101
- if (match)
1102
- version = match[1].split(".")[0];
1103
- } else if (userAgent.includes("DuckDuckGo/")) {
1104
- name = "duckduckgo";
1105
- const match = userAgent.match(/DuckDuckGo\/([0-9]+(?:\.[0-9]+)*)/);
1106
- if (match)
1107
- version = match[1].split(".")[0];
1108
- } else if (userAgent.includes("Chrome/") && hasBraveAPI) {
1109
- name = "brave";
1110
- const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
1111
- if (match)
1112
- version = match[1].split(".")[0];
1113
- } else if (userAgent.includes("Mobile/") || userAgent.includes("Android")) {
1114
- if (userAgent.includes("Chrome/")) {
1115
- name = "chrome-mobile";
1116
- const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
1117
- if (match)
1118
- version = match[1].split(".")[0];
1119
- } else if (userAgent.includes("Firefox/")) {
1120
- name = "firefox-mobile";
1121
- const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
1122
- if (match)
1123
- version = match[1].split(".")[0];
1124
- } else if (userAgent.includes("Safari/") && userAgent.includes("Mobile/")) {
1125
- name = "safari-mobile";
1126
- const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
1127
- if (match)
1128
- version = match[1].split(".")[0];
1129
- } else {
1130
- name = "mobile";
1131
- }
1132
- } else if (userAgent.includes("Chrome/")) {
1133
- name = "chrome";
1134
- const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
1135
- if (match)
1136
- version = match[1].split(".")[0];
1137
- } else if (userAgent.includes("Firefox/")) {
1138
- name = "firefox";
1139
- const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
1140
- if (match)
1141
- version = match[1].split(".")[0];
1142
- } else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome/")) {
1143
- name = "safari";
1144
- const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
1145
- if (match)
1146
- version = match[1].split(".")[0];
1147
- }
1148
- if (name === "unknown") {
1149
- const patterns = [
1150
- { regex: /Chrome\/([0-9]+)/, name: "chrome" },
1151
- { regex: /Firefox\/([0-9]+)/, name: "firefox" },
1152
- { regex: /Safari\/([0-9]+)/, name: "safari" },
1153
- { regex: /Edge\/([0-9]+)/, name: "edge" },
1154
- { regex: /Opera\/([0-9]+)/, name: "opera" }
1155
- ];
1156
- for (const pattern of patterns) {
1157
- const match = userAgent.match(pattern.regex);
1158
- if (match) {
1159
- name = pattern.name;
1160
- version = match[1];
1161
- break;
1162
- }
1163
- }
1164
- }
1165
- } catch (error) {
1166
- }
1167
- return { name, version, userAgent };
1168
- }
1169
- function detectBrowser() {
1170
- if (typeof window === "undefined" || !window.navigator?.userAgent) {
1171
- return { name: "unknown", version: "unknown", userAgent: "unknown" };
1172
- }
1173
- const userAgent = window.navigator.userAgent;
1174
- const hasBraveAPI = !!navigator.brave;
1175
- return parseBrowserFromUserAgent(userAgent, hasBraveAPI);
1176
- }
1177
- function getPlatformName() {
1178
- const { name, version } = detectBrowser();
1179
- return version !== "unknown" ? `${name}-v${version}` : name;
1180
- }
1181
- function getBrowserDisplayName() {
1182
- const { name, version } = detectBrowser();
1183
- const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
1184
- return version !== "unknown" ? `${capitalizedName} ${version}` : capitalizedName;
1185
- }
1186
- function isMobileDevice() {
1187
- if (typeof window === "undefined" || !window.navigator?.userAgent) {
1188
- return false;
1189
- }
1190
- const userAgent = window.navigator.userAgent.toLowerCase();
1191
- const mobilePatterns = [
1192
- /android/,
1193
- /iphone|ipad|ipod/,
1194
- /blackberry/,
1195
- /windows phone/,
1196
- /mobile/,
1197
- /tablet/,
1198
- /silk/,
1199
- /kindle/,
1200
- /opera mini/,
1201
- /opera mobi/
1202
- ];
1203
- const isMobileUA = mobilePatterns.some((pattern) => pattern.test(userAgent));
1204
- let isSmallScreen = false;
1205
- try {
1206
- isSmallScreen = window.screen.width <= 768 || window.screen.height <= 768;
1207
- } catch (error) {
1208
- isSmallScreen = false;
1209
- }
1210
- let isTouchDevice = false;
1211
- try {
1212
- isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
1213
- } catch (error) {
1214
- isTouchDevice = false;
1215
- }
1216
- return isMobileUA || isSmallScreen && isTouchDevice;
1217
- }
1218
-
1219
1311
  // src/providers/embedded/index.ts
1220
1312
  var import_constants2 = require("@phantom/constants");
1221
1313
  var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvider {
@@ -1232,6 +1324,7 @@ var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvi
1232
1324
  const platform = {
1233
1325
  storage: new BrowserStorage(),
1234
1326
  authProvider: new BrowserAuthProvider(urlParamsAccessor),
1327
+ phantomAppProvider: new BrowserPhantomAppProvider(),
1235
1328
  urlParamsAccessor,
1236
1329
  stamper,
1237
1330
  name: platformName,
@@ -1244,7 +1337,7 @@ var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvi
1244
1337
  // Full user agent for more detailed info
1245
1338
  [import_constants2.ANALYTICS_HEADERS.APP_ID]: config.appId,
1246
1339
  [import_constants2.ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
1247
- [import_constants2.ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0-beta.14"
1340
+ [import_constants2.ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0-beta.15"
1248
1341
  // Replaced at build time
1249
1342
  }
1250
1343
  };
@@ -1257,6 +1350,25 @@ var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvi
1257
1350
 
1258
1351
  // src/ProviderManager.ts
1259
1352
  var import_constants3 = require("@phantom/constants");
1353
+
1354
+ // src/utils/auth-callback.ts
1355
+ function isAuthFailureCallback(searchParams) {
1356
+ if (typeof window === "undefined" && !searchParams)
1357
+ return false;
1358
+ const params = searchParams || new URLSearchParams(window.location.search);
1359
+ const responseType = params.get("response_type");
1360
+ const sessionId = params.get("session_id");
1361
+ return responseType === "failure" && !!sessionId;
1362
+ }
1363
+ function isAuthCallbackUrl(searchParams) {
1364
+ if (typeof window === "undefined" && !searchParams)
1365
+ return false;
1366
+ const params = searchParams || new URLSearchParams(window.location.search);
1367
+ const sessionId = params.get("session_id");
1368
+ return !!(sessionId && (params.has("response_type") || params.has("wallet_id")));
1369
+ }
1370
+
1371
+ // src/ProviderManager.ts
1260
1372
  var ProviderManager = class {
1261
1373
  // Track which providers have forwarding set up
1262
1374
  constructor(config) {
@@ -1322,12 +1434,35 @@ var ProviderManager = class {
1322
1434
  }
1323
1435
  /**
1324
1436
  * Connect using the current provider
1437
+ * Automatically switches provider based on authOptions.provider if specified
1325
1438
  */
1326
1439
  async connect(authOptions) {
1327
1440
  debug.info(DebugCategory.PROVIDER_MANAGER, "Starting connection", {
1328
1441
  currentProviderKey: this.currentProviderKey,
1329
1442
  authOptions: authOptions ? { provider: authOptions.provider, hasJwtToken: !!authOptions.jwtToken } : void 0
1330
1443
  });
1444
+ if (authOptions?.provider) {
1445
+ const requestedProvider = authOptions.provider;
1446
+ let targetProviderType = null;
1447
+ if (requestedProvider === "injected") {
1448
+ targetProviderType = "injected";
1449
+ } else if (["google", "apple", "jwt", "phantom"].includes(requestedProvider)) {
1450
+ targetProviderType = "embedded";
1451
+ }
1452
+ if (targetProviderType) {
1453
+ const currentInfo = this.getCurrentProviderInfo();
1454
+ if (currentInfo?.type !== targetProviderType) {
1455
+ debug.log(DebugCategory.PROVIDER_MANAGER, "Auto-switching provider based on auth options", {
1456
+ from: currentInfo?.type,
1457
+ to: targetProviderType,
1458
+ requestedProvider
1459
+ });
1460
+ this.switchProvider(targetProviderType, {
1461
+ embeddedWalletType: currentInfo?.embeddedWalletType || this.config.embeddedWalletType
1462
+ });
1463
+ }
1464
+ }
1465
+ }
1331
1466
  if (!this.currentProvider) {
1332
1467
  debug.error(DebugCategory.PROVIDER_MANAGER, "No provider selected");
1333
1468
  throw new Error("No provider selected");
@@ -1373,6 +1508,10 @@ var ProviderManager = class {
1373
1508
  */
1374
1509
  async autoConnect() {
1375
1510
  debug.log(DebugCategory.PROVIDER_MANAGER, "Starting auto-connect with fallback strategy");
1511
+ if (isAuthFailureCallback()) {
1512
+ debug.warn(DebugCategory.PROVIDER_MANAGER, "Auth failure detected in URL, skipping autoConnect fallback");
1513
+ return false;
1514
+ }
1376
1515
  const embeddedWalletType = this.config.embeddedWalletType || "user-wallet";
1377
1516
  const embeddedKey = this.getProviderKey("embedded", embeddedWalletType);
1378
1517
  if (this.providers.has(embeddedKey)) {
@@ -1398,6 +1537,10 @@ var ProviderManager = class {
1398
1537
  debug.log(DebugCategory.PROVIDER_MANAGER, "Embedded auto-connect failed", {
1399
1538
  error: error.message
1400
1539
  });
1540
+ if (isAuthCallbackUrl()) {
1541
+ debug.log(DebugCategory.PROVIDER_MANAGER, "In auth callback URL, not attempting injected fallback");
1542
+ return false;
1543
+ }
1401
1544
  }
1402
1545
  }
1403
1546
  const injectedKey = this.getProviderKey("injected");
@@ -1575,35 +1718,17 @@ var ProviderManager = class {
1575
1718
  console.error("Failed to save provider preference:", error);
1576
1719
  }
1577
1720
  }
1578
- /**
1579
- * Restore provider preference from localStorage
1580
- */
1581
- /*
1582
- private restoreProviderPreference(): void {
1583
- try {
1584
- const saved = localStorage.getItem("phantom-provider-preference");
1585
- if (saved) {
1586
- const preference: ProviderPreference = JSON.parse(saved);
1587
- this.switchProvider(preference.type, {
1588
- embeddedWalletType: preference.embeddedWalletType,
1589
- });
1590
- }
1591
- } catch (error) {
1592
- // Ignore localStorage errors - just use default provider
1593
- console.error("Failed to restore provider preference:", error);
1594
- }
1595
- }*/
1596
1721
  };
1597
1722
 
1598
1723
  // src/waitForPhantomExtension.ts
1599
- var import_browser_injected_sdk2 = require("@phantom/browser-injected-sdk");
1724
+ var import_browser_injected_sdk3 = require("@phantom/browser-injected-sdk");
1600
1725
  async function waitForPhantomExtension(timeoutMs = 3e3) {
1601
1726
  return new Promise((resolve) => {
1602
1727
  const startTime = Date.now();
1603
1728
  const checkInterval = 100;
1604
1729
  const checkForExtension = () => {
1605
1730
  try {
1606
- if ((0, import_browser_injected_sdk2.isPhantomExtensionInstalled)()) {
1731
+ if ((0, import_browser_injected_sdk3.isPhantomExtensionInstalled)()) {
1607
1732
  resolve(true);
1608
1733
  return;
1609
1734
  }
@@ -1696,22 +1821,6 @@ var BrowserSDK = class {
1696
1821
  throw error;
1697
1822
  }
1698
1823
  }
1699
- /**
1700
- * Switch between provider types (injected vs embedded)
1701
- */
1702
- async switchProvider(type, options) {
1703
- debug.info(DebugCategory.BROWSER_SDK, "Switching provider", { type, options });
1704
- try {
1705
- await this.providerManager.switchProvider(type, options);
1706
- debug.info(DebugCategory.BROWSER_SDK, "Provider switch successful", { type });
1707
- } catch (error) {
1708
- debug.error(DebugCategory.BROWSER_SDK, "Provider switch failed", {
1709
- type,
1710
- error: error.message
1711
- });
1712
- throw error;
1713
- }
1714
- }
1715
1824
  // ===== STATE QUERIES =====
1716
1825
  /**
1717
1826
  * Check if the SDK is connected to a wallet
package/dist/index.mjs CHANGED
@@ -865,6 +865,47 @@ var BrowserStorage = class {
865
865
  };
866
866
  });
867
867
  }
868
+ async getShouldClearPreviousSession() {
869
+ debug.log(DebugCategory.STORAGE, "Getting shouldClearPreviousSession flag from IndexedDB");
870
+ const db = await this.getDB();
871
+ return new Promise((resolve, reject) => {
872
+ const transaction = db.transaction([this.storeName], "readonly");
873
+ const store = transaction.objectStore(this.storeName);
874
+ const request = store.get("shouldClearPreviousSession");
875
+ request.onsuccess = () => {
876
+ const shouldClear = request.result ?? false;
877
+ debug.log(DebugCategory.STORAGE, "Retrieved shouldClearPreviousSession flag from IndexedDB", {
878
+ shouldClear
879
+ });
880
+ resolve(shouldClear);
881
+ };
882
+ request.onerror = () => {
883
+ debug.error(DebugCategory.STORAGE, "Failed to get shouldClearPreviousSession flag from IndexedDB", {
884
+ error: request.error
885
+ });
886
+ reject(request.error);
887
+ };
888
+ });
889
+ }
890
+ async setShouldClearPreviousSession(should) {
891
+ debug.log(DebugCategory.STORAGE, "Setting shouldClearPreviousSession flag in IndexedDB", { should });
892
+ const db = await this.getDB();
893
+ return new Promise((resolve, reject) => {
894
+ const transaction = db.transaction([this.storeName], "readwrite");
895
+ const store = transaction.objectStore(this.storeName);
896
+ const request = store.put(should, "shouldClearPreviousSession");
897
+ request.onsuccess = () => {
898
+ debug.log(DebugCategory.STORAGE, "Successfully set shouldClearPreviousSession flag in IndexedDB");
899
+ resolve();
900
+ };
901
+ request.onerror = () => {
902
+ debug.error(DebugCategory.STORAGE, "Failed to set shouldClearPreviousSession flag in IndexedDB", {
903
+ error: request.error
904
+ });
905
+ reject(request.error);
906
+ };
907
+ });
908
+ }
868
909
  };
869
910
 
870
911
  // src/providers/embedded/adapters/url-params.ts
@@ -878,6 +919,147 @@ var browserUrlParamsAccessor = new BrowserURLParamsAccessor();
878
919
 
879
920
  // src/providers/embedded/adapters/auth.ts
880
921
  import { DEFAULT_AUTH_URL } from "@phantom/constants";
922
+
923
+ // src/utils/browser-detection.ts
924
+ function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
925
+ let name = "unknown";
926
+ let version = "unknown";
927
+ if (!userAgent || typeof userAgent !== "string") {
928
+ return { name, version, userAgent: "unknown" };
929
+ }
930
+ try {
931
+ if (userAgent.includes("Edg/")) {
932
+ name = "edge";
933
+ const match = userAgent.match(/Edg\/([0-9]+(?:\.[0-9]+)*)/);
934
+ if (match)
935
+ version = match[1].split(".")[0];
936
+ } else if (userAgent.includes("OPR/") || userAgent.includes("Opera/")) {
937
+ name = "opera";
938
+ const match = userAgent.match(/(?:OPR|Opera)\/([0-9]+(?:\.[0-9]+)*)/);
939
+ if (match)
940
+ version = match[1].split(".")[0];
941
+ } else if (userAgent.includes("SamsungBrowser/")) {
942
+ name = "samsung";
943
+ const match = userAgent.match(/SamsungBrowser\/([0-9]+(?:\.[0-9]+)*)/);
944
+ if (match)
945
+ version = match[1].split(".")[0];
946
+ } else if (userAgent.includes("DuckDuckGo/")) {
947
+ name = "duckduckgo";
948
+ const match = userAgent.match(/DuckDuckGo\/([0-9]+(?:\.[0-9]+)*)/);
949
+ if (match)
950
+ version = match[1].split(".")[0];
951
+ } else if (userAgent.includes("Chrome/") && hasBraveAPI) {
952
+ name = "brave";
953
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
954
+ if (match)
955
+ version = match[1].split(".")[0];
956
+ } else if (userAgent.includes("Mobile/") || userAgent.includes("Android")) {
957
+ if (userAgent.includes("Chrome/")) {
958
+ name = "chrome-mobile";
959
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
960
+ if (match)
961
+ version = match[1].split(".")[0];
962
+ } else if (userAgent.includes("Firefox/")) {
963
+ name = "firefox-mobile";
964
+ const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
965
+ if (match)
966
+ version = match[1].split(".")[0];
967
+ } else if (userAgent.includes("Safari/") && userAgent.includes("Mobile/")) {
968
+ name = "safari-mobile";
969
+ const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
970
+ if (match)
971
+ version = match[1].split(".")[0];
972
+ } else {
973
+ name = "mobile";
974
+ }
975
+ } else if (userAgent.includes("Chrome/")) {
976
+ name = "chrome";
977
+ const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
978
+ if (match)
979
+ version = match[1].split(".")[0];
980
+ } else if (userAgent.includes("Firefox/")) {
981
+ name = "firefox";
982
+ const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
983
+ if (match)
984
+ version = match[1].split(".")[0];
985
+ } else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome/")) {
986
+ name = "safari";
987
+ const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
988
+ if (match)
989
+ version = match[1].split(".")[0];
990
+ }
991
+ if (name === "unknown") {
992
+ const patterns = [
993
+ { regex: /Chrome\/([0-9]+)/, name: "chrome" },
994
+ { regex: /Firefox\/([0-9]+)/, name: "firefox" },
995
+ { regex: /Safari\/([0-9]+)/, name: "safari" },
996
+ { regex: /Edge\/([0-9]+)/, name: "edge" },
997
+ { regex: /Opera\/([0-9]+)/, name: "opera" }
998
+ ];
999
+ for (const pattern of patterns) {
1000
+ const match = userAgent.match(pattern.regex);
1001
+ if (match) {
1002
+ name = pattern.name;
1003
+ version = match[1];
1004
+ break;
1005
+ }
1006
+ }
1007
+ }
1008
+ } catch (error) {
1009
+ }
1010
+ return { name, version, userAgent };
1011
+ }
1012
+ function detectBrowser() {
1013
+ if (typeof window === "undefined" || !window.navigator?.userAgent) {
1014
+ return { name: "unknown", version: "unknown", userAgent: "unknown" };
1015
+ }
1016
+ const userAgent = window.navigator.userAgent;
1017
+ const hasBraveAPI = !!navigator.brave;
1018
+ return parseBrowserFromUserAgent(userAgent, hasBraveAPI);
1019
+ }
1020
+ function getPlatformName() {
1021
+ const { name, version } = detectBrowser();
1022
+ return version !== "unknown" ? `${name}-v${version}` : name;
1023
+ }
1024
+ function getBrowserDisplayName() {
1025
+ const { name, version } = detectBrowser();
1026
+ const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
1027
+ return version !== "unknown" ? `${capitalizedName} ${version}` : capitalizedName;
1028
+ }
1029
+ function isMobileDevice() {
1030
+ if (typeof window === "undefined" || !window.navigator?.userAgent) {
1031
+ return false;
1032
+ }
1033
+ const userAgent = window.navigator.userAgent.toLowerCase();
1034
+ const mobilePatterns = [
1035
+ /android/,
1036
+ /iphone|ipad|ipod/,
1037
+ /blackberry/,
1038
+ /windows phone/,
1039
+ /mobile/,
1040
+ /tablet/,
1041
+ /silk/,
1042
+ /kindle/,
1043
+ /opera mini/,
1044
+ /opera mobi/
1045
+ ];
1046
+ const isMobileUA = mobilePatterns.some((pattern) => pattern.test(userAgent));
1047
+ let isSmallScreen = false;
1048
+ try {
1049
+ isSmallScreen = window.screen.width <= 768 || window.screen.height <= 768;
1050
+ } catch (error) {
1051
+ isSmallScreen = false;
1052
+ }
1053
+ let isTouchDevice = false;
1054
+ try {
1055
+ isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
1056
+ } catch (error) {
1057
+ isTouchDevice = false;
1058
+ }
1059
+ return isMobileUA || isSmallScreen && isTouchDevice;
1060
+ }
1061
+
1062
+ // src/providers/embedded/adapters/auth.ts
881
1063
  var BrowserAuthProvider = class {
882
1064
  constructor(urlParamsAccessor) {
883
1065
  this.urlParamsAccessor = urlParamsAccessor;
@@ -899,8 +1081,7 @@ var BrowserAuthProvider = class {
899
1081
  publicKey: phantomOptions.publicKey,
900
1082
  appId: phantomOptions.appId,
901
1083
  provider: phantomOptions.provider,
902
- authUrl: phantomOptions.authUrl,
903
- hasCustomData: !!phantomOptions.customAuthData
1084
+ authUrl: phantomOptions.authUrl
904
1085
  });
905
1086
  const baseUrl = phantomOptions.authUrl || DEFAULT_AUTH_URL;
906
1087
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Using auth URL", { baseUrl });
@@ -909,8 +1090,12 @@ var BrowserAuthProvider = class {
909
1090
  app_id: phantomOptions.appId,
910
1091
  redirect_uri: phantomOptions.redirectUrl || (typeof window !== "undefined" ? this.getValidatedCurrentUrl() : ""),
911
1092
  session_id: phantomOptions.sessionId,
912
- clear_previous_session: true.toString(),
913
- sdk_version: "1.0.0-beta.14"
1093
+ // OAuth session management - defaults to allow refresh unless explicitly clearing after logout
1094
+ clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
1095
+ allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
1096
+ sdk_version: "1.0.0-beta.15",
1097
+ sdk_type: "browser",
1098
+ platform: detectBrowser().name
914
1099
  });
915
1100
  if (phantomOptions.provider) {
916
1101
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Provider specified, will skip selection", {
@@ -921,10 +1106,6 @@ var BrowserAuthProvider = class {
921
1106
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "No provider specified, defaulting to Google");
922
1107
  params.append("provider", "google");
923
1108
  }
924
- if (phantomOptions.customAuthData) {
925
- debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Adding custom auth data");
926
- params.append("authData", JSON.stringify(phantomOptions.customAuthData));
927
- }
928
1109
  const authContext = {
929
1110
  publicKey: phantomOptions.publicKey,
930
1111
  appId: phantomOptions.appId,
@@ -995,12 +1176,14 @@ var BrowserAuthProvider = class {
995
1176
  });
996
1177
  const organizationId = this.urlParamsAccessor.getParam("organization_id");
997
1178
  const expiresInMs = this.urlParamsAccessor.getParam("expires_in_ms");
1179
+ const authUserId = this.urlParamsAccessor.getParam("auth_user_id");
998
1180
  debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Auth redirect parameters", {
999
1181
  walletId,
1000
1182
  organizationId,
1001
1183
  sessionId,
1002
1184
  accountDerivationIndex,
1003
- expiresInMs
1185
+ expiresInMs,
1186
+ authUserId
1004
1187
  });
1005
1188
  if (!organizationId) {
1006
1189
  debug.error(DebugCategory.PHANTOM_CONNECT_AUTH, "Missing organization_id in auth response");
@@ -1014,9 +1197,9 @@ var BrowserAuthProvider = class {
1014
1197
  return {
1015
1198
  walletId,
1016
1199
  organizationId,
1017
- userInfo: context,
1018
1200
  accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
1019
- expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0
1201
+ expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
1202
+ authUserId: authUserId || void 0
1020
1203
  };
1021
1204
  } catch (error) {
1022
1205
  sessionStorage.removeItem("phantom-auth-context");
@@ -1025,6 +1208,54 @@ var BrowserAuthProvider = class {
1025
1208
  }
1026
1209
  };
1027
1210
 
1211
+ // src/providers/embedded/adapters/phantom-app.ts
1212
+ import { isPhantomExtensionInstalled } from "@phantom/browser-injected-sdk";
1213
+ var BrowserPhantomAppProvider = class {
1214
+ /**
1215
+ * Check if the Phantom extension is installed in the browser
1216
+ */
1217
+ isAvailable() {
1218
+ return isPhantomExtensionInstalled();
1219
+ }
1220
+ /**
1221
+ * Authenticate using the Phantom browser extension
1222
+ */
1223
+ async authenticate(options) {
1224
+ if (!this.isAvailable()) {
1225
+ throw new Error(
1226
+ "Phantom extension is not installed. Please install the Phantom browser extension to use this authentication method."
1227
+ );
1228
+ }
1229
+ if (!window.phantom?.app?.login || typeof window.phantom.app.login !== "function") {
1230
+ throw new Error(
1231
+ "Phantom extension authentication is not yet implemented. The extension needs to expose an app.login API (window.phantom.app.login)."
1232
+ );
1233
+ }
1234
+ try {
1235
+ const result = await window.phantom.app.login({
1236
+ publicKey: options.publicKey,
1237
+ appId: options.appId,
1238
+ sessionId: options.sessionId
1239
+ });
1240
+ if (!result || !result.walletId || !result.organizationId) {
1241
+ throw new Error("Invalid authentication response from Phantom extension");
1242
+ }
1243
+ return {
1244
+ walletId: result.walletId,
1245
+ organizationId: result.organizationId,
1246
+ provider: "phantom",
1247
+ accountDerivationIndex: result.accountDerivationIndex ?? 0,
1248
+ expiresInMs: result.expiresInMs ?? 0
1249
+ };
1250
+ } catch (error) {
1251
+ if (error instanceof Error) {
1252
+ throw error;
1253
+ }
1254
+ throw new Error(`Phantom extension authentication failed: ${String(error)}`);
1255
+ }
1256
+ }
1257
+ };
1258
+
1028
1259
  // src/providers/embedded/adapters/logger.ts
1029
1260
  var BrowserLogger = class {
1030
1261
  info(category, message, data) {
@@ -1041,145 +1272,6 @@ var BrowserLogger = class {
1041
1272
  }
1042
1273
  };
1043
1274
 
1044
- // src/utils/browser-detection.ts
1045
- function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
1046
- let name = "unknown";
1047
- let version = "unknown";
1048
- if (!userAgent || typeof userAgent !== "string") {
1049
- return { name, version, userAgent: "unknown" };
1050
- }
1051
- try {
1052
- if (userAgent.includes("Edg/")) {
1053
- name = "edge";
1054
- const match = userAgent.match(/Edg\/([0-9]+(?:\.[0-9]+)*)/);
1055
- if (match)
1056
- version = match[1].split(".")[0];
1057
- } else if (userAgent.includes("OPR/") || userAgent.includes("Opera/")) {
1058
- name = "opera";
1059
- const match = userAgent.match(/(?:OPR|Opera)\/([0-9]+(?:\.[0-9]+)*)/);
1060
- if (match)
1061
- version = match[1].split(".")[0];
1062
- } else if (userAgent.includes("SamsungBrowser/")) {
1063
- name = "samsung";
1064
- const match = userAgent.match(/SamsungBrowser\/([0-9]+(?:\.[0-9]+)*)/);
1065
- if (match)
1066
- version = match[1].split(".")[0];
1067
- } else if (userAgent.includes("DuckDuckGo/")) {
1068
- name = "duckduckgo";
1069
- const match = userAgent.match(/DuckDuckGo\/([0-9]+(?:\.[0-9]+)*)/);
1070
- if (match)
1071
- version = match[1].split(".")[0];
1072
- } else if (userAgent.includes("Chrome/") && hasBraveAPI) {
1073
- name = "brave";
1074
- const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
1075
- if (match)
1076
- version = match[1].split(".")[0];
1077
- } else if (userAgent.includes("Mobile/") || userAgent.includes("Android")) {
1078
- if (userAgent.includes("Chrome/")) {
1079
- name = "chrome-mobile";
1080
- const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
1081
- if (match)
1082
- version = match[1].split(".")[0];
1083
- } else if (userAgent.includes("Firefox/")) {
1084
- name = "firefox-mobile";
1085
- const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
1086
- if (match)
1087
- version = match[1].split(".")[0];
1088
- } else if (userAgent.includes("Safari/") && userAgent.includes("Mobile/")) {
1089
- name = "safari-mobile";
1090
- const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
1091
- if (match)
1092
- version = match[1].split(".")[0];
1093
- } else {
1094
- name = "mobile";
1095
- }
1096
- } else if (userAgent.includes("Chrome/")) {
1097
- name = "chrome";
1098
- const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
1099
- if (match)
1100
- version = match[1].split(".")[0];
1101
- } else if (userAgent.includes("Firefox/")) {
1102
- name = "firefox";
1103
- const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
1104
- if (match)
1105
- version = match[1].split(".")[0];
1106
- } else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome/")) {
1107
- name = "safari";
1108
- const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
1109
- if (match)
1110
- version = match[1].split(".")[0];
1111
- }
1112
- if (name === "unknown") {
1113
- const patterns = [
1114
- { regex: /Chrome\/([0-9]+)/, name: "chrome" },
1115
- { regex: /Firefox\/([0-9]+)/, name: "firefox" },
1116
- { regex: /Safari\/([0-9]+)/, name: "safari" },
1117
- { regex: /Edge\/([0-9]+)/, name: "edge" },
1118
- { regex: /Opera\/([0-9]+)/, name: "opera" }
1119
- ];
1120
- for (const pattern of patterns) {
1121
- const match = userAgent.match(pattern.regex);
1122
- if (match) {
1123
- name = pattern.name;
1124
- version = match[1];
1125
- break;
1126
- }
1127
- }
1128
- }
1129
- } catch (error) {
1130
- }
1131
- return { name, version, userAgent };
1132
- }
1133
- function detectBrowser() {
1134
- if (typeof window === "undefined" || !window.navigator?.userAgent) {
1135
- return { name: "unknown", version: "unknown", userAgent: "unknown" };
1136
- }
1137
- const userAgent = window.navigator.userAgent;
1138
- const hasBraveAPI = !!navigator.brave;
1139
- return parseBrowserFromUserAgent(userAgent, hasBraveAPI);
1140
- }
1141
- function getPlatformName() {
1142
- const { name, version } = detectBrowser();
1143
- return version !== "unknown" ? `${name}-v${version}` : name;
1144
- }
1145
- function getBrowserDisplayName() {
1146
- const { name, version } = detectBrowser();
1147
- const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
1148
- return version !== "unknown" ? `${capitalizedName} ${version}` : capitalizedName;
1149
- }
1150
- function isMobileDevice() {
1151
- if (typeof window === "undefined" || !window.navigator?.userAgent) {
1152
- return false;
1153
- }
1154
- const userAgent = window.navigator.userAgent.toLowerCase();
1155
- const mobilePatterns = [
1156
- /android/,
1157
- /iphone|ipad|ipod/,
1158
- /blackberry/,
1159
- /windows phone/,
1160
- /mobile/,
1161
- /tablet/,
1162
- /silk/,
1163
- /kindle/,
1164
- /opera mini/,
1165
- /opera mobi/
1166
- ];
1167
- const isMobileUA = mobilePatterns.some((pattern) => pattern.test(userAgent));
1168
- let isSmallScreen = false;
1169
- try {
1170
- isSmallScreen = window.screen.width <= 768 || window.screen.height <= 768;
1171
- } catch (error) {
1172
- isSmallScreen = false;
1173
- }
1174
- let isTouchDevice = false;
1175
- try {
1176
- isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
1177
- } catch (error) {
1178
- isTouchDevice = false;
1179
- }
1180
- return isMobileUA || isSmallScreen && isTouchDevice;
1181
- }
1182
-
1183
1275
  // src/providers/embedded/index.ts
1184
1276
  import { ANALYTICS_HEADERS } from "@phantom/constants";
1185
1277
  var EmbeddedProvider = class extends CoreEmbeddedProvider {
@@ -1196,6 +1288,7 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
1196
1288
  const platform = {
1197
1289
  storage: new BrowserStorage(),
1198
1290
  authProvider: new BrowserAuthProvider(urlParamsAccessor),
1291
+ phantomAppProvider: new BrowserPhantomAppProvider(),
1199
1292
  urlParamsAccessor,
1200
1293
  stamper,
1201
1294
  name: platformName,
@@ -1208,7 +1301,7 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
1208
1301
  // Full user agent for more detailed info
1209
1302
  [ANALYTICS_HEADERS.APP_ID]: config.appId,
1210
1303
  [ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
1211
- [ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0-beta.14"
1304
+ [ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0-beta.15"
1212
1305
  // Replaced at build time
1213
1306
  }
1214
1307
  };
@@ -1221,6 +1314,25 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
1221
1314
 
1222
1315
  // src/ProviderManager.ts
1223
1316
  import { DEFAULT_WALLET_API_URL, DEFAULT_EMBEDDED_WALLET_TYPE, DEFAULT_AUTH_URL as DEFAULT_AUTH_URL2 } from "@phantom/constants";
1317
+
1318
+ // src/utils/auth-callback.ts
1319
+ function isAuthFailureCallback(searchParams) {
1320
+ if (typeof window === "undefined" && !searchParams)
1321
+ return false;
1322
+ const params = searchParams || new URLSearchParams(window.location.search);
1323
+ const responseType = params.get("response_type");
1324
+ const sessionId = params.get("session_id");
1325
+ return responseType === "failure" && !!sessionId;
1326
+ }
1327
+ function isAuthCallbackUrl(searchParams) {
1328
+ if (typeof window === "undefined" && !searchParams)
1329
+ return false;
1330
+ const params = searchParams || new URLSearchParams(window.location.search);
1331
+ const sessionId = params.get("session_id");
1332
+ return !!(sessionId && (params.has("response_type") || params.has("wallet_id")));
1333
+ }
1334
+
1335
+ // src/ProviderManager.ts
1224
1336
  var ProviderManager = class {
1225
1337
  // Track which providers have forwarding set up
1226
1338
  constructor(config) {
@@ -1286,12 +1398,35 @@ var ProviderManager = class {
1286
1398
  }
1287
1399
  /**
1288
1400
  * Connect using the current provider
1401
+ * Automatically switches provider based on authOptions.provider if specified
1289
1402
  */
1290
1403
  async connect(authOptions) {
1291
1404
  debug.info(DebugCategory.PROVIDER_MANAGER, "Starting connection", {
1292
1405
  currentProviderKey: this.currentProviderKey,
1293
1406
  authOptions: authOptions ? { provider: authOptions.provider, hasJwtToken: !!authOptions.jwtToken } : void 0
1294
1407
  });
1408
+ if (authOptions?.provider) {
1409
+ const requestedProvider = authOptions.provider;
1410
+ let targetProviderType = null;
1411
+ if (requestedProvider === "injected") {
1412
+ targetProviderType = "injected";
1413
+ } else if (["google", "apple", "jwt", "phantom"].includes(requestedProvider)) {
1414
+ targetProviderType = "embedded";
1415
+ }
1416
+ if (targetProviderType) {
1417
+ const currentInfo = this.getCurrentProviderInfo();
1418
+ if (currentInfo?.type !== targetProviderType) {
1419
+ debug.log(DebugCategory.PROVIDER_MANAGER, "Auto-switching provider based on auth options", {
1420
+ from: currentInfo?.type,
1421
+ to: targetProviderType,
1422
+ requestedProvider
1423
+ });
1424
+ this.switchProvider(targetProviderType, {
1425
+ embeddedWalletType: currentInfo?.embeddedWalletType || this.config.embeddedWalletType
1426
+ });
1427
+ }
1428
+ }
1429
+ }
1295
1430
  if (!this.currentProvider) {
1296
1431
  debug.error(DebugCategory.PROVIDER_MANAGER, "No provider selected");
1297
1432
  throw new Error("No provider selected");
@@ -1337,6 +1472,10 @@ var ProviderManager = class {
1337
1472
  */
1338
1473
  async autoConnect() {
1339
1474
  debug.log(DebugCategory.PROVIDER_MANAGER, "Starting auto-connect with fallback strategy");
1475
+ if (isAuthFailureCallback()) {
1476
+ debug.warn(DebugCategory.PROVIDER_MANAGER, "Auth failure detected in URL, skipping autoConnect fallback");
1477
+ return false;
1478
+ }
1340
1479
  const embeddedWalletType = this.config.embeddedWalletType || "user-wallet";
1341
1480
  const embeddedKey = this.getProviderKey("embedded", embeddedWalletType);
1342
1481
  if (this.providers.has(embeddedKey)) {
@@ -1362,6 +1501,10 @@ var ProviderManager = class {
1362
1501
  debug.log(DebugCategory.PROVIDER_MANAGER, "Embedded auto-connect failed", {
1363
1502
  error: error.message
1364
1503
  });
1504
+ if (isAuthCallbackUrl()) {
1505
+ debug.log(DebugCategory.PROVIDER_MANAGER, "In auth callback URL, not attempting injected fallback");
1506
+ return false;
1507
+ }
1365
1508
  }
1366
1509
  }
1367
1510
  const injectedKey = this.getProviderKey("injected");
@@ -1539,35 +1682,17 @@ var ProviderManager = class {
1539
1682
  console.error("Failed to save provider preference:", error);
1540
1683
  }
1541
1684
  }
1542
- /**
1543
- * Restore provider preference from localStorage
1544
- */
1545
- /*
1546
- private restoreProviderPreference(): void {
1547
- try {
1548
- const saved = localStorage.getItem("phantom-provider-preference");
1549
- if (saved) {
1550
- const preference: ProviderPreference = JSON.parse(saved);
1551
- this.switchProvider(preference.type, {
1552
- embeddedWalletType: preference.embeddedWalletType,
1553
- });
1554
- }
1555
- } catch (error) {
1556
- // Ignore localStorage errors - just use default provider
1557
- console.error("Failed to restore provider preference:", error);
1558
- }
1559
- }*/
1560
1685
  };
1561
1686
 
1562
1687
  // src/waitForPhantomExtension.ts
1563
- import { isPhantomExtensionInstalled } from "@phantom/browser-injected-sdk";
1688
+ import { isPhantomExtensionInstalled as isPhantomExtensionInstalled2 } from "@phantom/browser-injected-sdk";
1564
1689
  async function waitForPhantomExtension(timeoutMs = 3e3) {
1565
1690
  return new Promise((resolve) => {
1566
1691
  const startTime = Date.now();
1567
1692
  const checkInterval = 100;
1568
1693
  const checkForExtension = () => {
1569
1694
  try {
1570
- if (isPhantomExtensionInstalled()) {
1695
+ if (isPhantomExtensionInstalled2()) {
1571
1696
  resolve(true);
1572
1697
  return;
1573
1698
  }
@@ -1660,22 +1785,6 @@ var BrowserSDK = class {
1660
1785
  throw error;
1661
1786
  }
1662
1787
  }
1663
- /**
1664
- * Switch between provider types (injected vs embedded)
1665
- */
1666
- async switchProvider(type, options) {
1667
- debug.info(DebugCategory.BROWSER_SDK, "Switching provider", { type, options });
1668
- try {
1669
- await this.providerManager.switchProvider(type, options);
1670
- debug.info(DebugCategory.BROWSER_SDK, "Provider switch successful", { type });
1671
- } catch (error) {
1672
- debug.error(DebugCategory.BROWSER_SDK, "Provider switch failed", {
1673
- type,
1674
- error: error.message
1675
- });
1676
- throw error;
1677
- }
1678
- }
1679
1788
  // ===== STATE QUERIES =====
1680
1789
  /**
1681
1790
  * Check if the SDK is connected to a wallet
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phantom/browser-sdk",
3
- "version": "1.0.0-beta.14",
3
+ "version": "1.0.0-beta.15",
4
4
  "description": "Browser SDK for Phantom Wallet",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -31,9 +31,9 @@
31
31
  "@phantom/base64url": "^1.0.0-beta.7",
32
32
  "@phantom/browser-injected-sdk": "^1.0.0-beta.5",
33
33
  "@phantom/chain-interfaces": "^1.0.0-beta.7",
34
- "@phantom/client": "^1.0.0-beta.14",
34
+ "@phantom/client": "^1.0.0-beta.15",
35
35
  "@phantom/constants": "^1.0.0-beta.7",
36
- "@phantom/embedded-provider-core": "^1.0.0-beta.14",
36
+ "@phantom/embedded-provider-core": "^1.0.0-beta.15",
37
37
  "@phantom/indexed-db-stamper": "^1.0.0-beta.1",
38
38
  "@phantom/parsers": "^1.0.0-beta.7",
39
39
  "@phantom/sdk-types": "^1.0.0-beta.7",