@phantom/browser-sdk 1.0.0-beta.14 → 1.0.0-beta.16
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 +142 -11
- package/dist/index.d.ts +45 -10
- package/dist/index.js +385 -192
- package/dist/index.mjs +385 -192
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -440,7 +440,12 @@ var InjectedProvider = class {
|
|
|
440
440
|
debug.info(DebugCategory.INJECTED_PROVIDER, "Solana connected successfully", { address: publicKey });
|
|
441
441
|
}
|
|
442
442
|
} catch (err) {
|
|
443
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana", { error: err });
|
|
443
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana, stopping", { error: err });
|
|
444
|
+
this.emit("connect_error", {
|
|
445
|
+
error: err instanceof Error ? err.message : "Failed to connect",
|
|
446
|
+
source: "manual-connect"
|
|
447
|
+
});
|
|
448
|
+
throw err;
|
|
444
449
|
}
|
|
445
450
|
}
|
|
446
451
|
if (this.addressTypes.includes(AddressType4.ethereum)) {
|
|
@@ -456,7 +461,12 @@ var InjectedProvider = class {
|
|
|
456
461
|
debug.info(DebugCategory.INJECTED_PROVIDER, "Ethereum connected successfully", { addresses: accounts });
|
|
457
462
|
}
|
|
458
463
|
} catch (err) {
|
|
459
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum", { error: err });
|
|
464
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum, stopping", { error: err });
|
|
465
|
+
this.emit("connect_error", {
|
|
466
|
+
error: err instanceof Error ? err.message : "Failed to connect",
|
|
467
|
+
source: "manual-connect"
|
|
468
|
+
});
|
|
469
|
+
throw err;
|
|
460
470
|
}
|
|
461
471
|
}
|
|
462
472
|
if (connectedAddresses.length === 0) {
|
|
@@ -865,6 +875,47 @@ var BrowserStorage = class {
|
|
|
865
875
|
};
|
|
866
876
|
});
|
|
867
877
|
}
|
|
878
|
+
async getShouldClearPreviousSession() {
|
|
879
|
+
debug.log(DebugCategory.STORAGE, "Getting shouldClearPreviousSession flag from IndexedDB");
|
|
880
|
+
const db = await this.getDB();
|
|
881
|
+
return new Promise((resolve, reject) => {
|
|
882
|
+
const transaction = db.transaction([this.storeName], "readonly");
|
|
883
|
+
const store = transaction.objectStore(this.storeName);
|
|
884
|
+
const request = store.get("shouldClearPreviousSession");
|
|
885
|
+
request.onsuccess = () => {
|
|
886
|
+
const shouldClear = request.result ?? false;
|
|
887
|
+
debug.log(DebugCategory.STORAGE, "Retrieved shouldClearPreviousSession flag from IndexedDB", {
|
|
888
|
+
shouldClear
|
|
889
|
+
});
|
|
890
|
+
resolve(shouldClear);
|
|
891
|
+
};
|
|
892
|
+
request.onerror = () => {
|
|
893
|
+
debug.error(DebugCategory.STORAGE, "Failed to get shouldClearPreviousSession flag from IndexedDB", {
|
|
894
|
+
error: request.error
|
|
895
|
+
});
|
|
896
|
+
reject(request.error);
|
|
897
|
+
};
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
async setShouldClearPreviousSession(should) {
|
|
901
|
+
debug.log(DebugCategory.STORAGE, "Setting shouldClearPreviousSession flag in IndexedDB", { should });
|
|
902
|
+
const db = await this.getDB();
|
|
903
|
+
return new Promise((resolve, reject) => {
|
|
904
|
+
const transaction = db.transaction([this.storeName], "readwrite");
|
|
905
|
+
const store = transaction.objectStore(this.storeName);
|
|
906
|
+
const request = store.put(should, "shouldClearPreviousSession");
|
|
907
|
+
request.onsuccess = () => {
|
|
908
|
+
debug.log(DebugCategory.STORAGE, "Successfully set shouldClearPreviousSession flag in IndexedDB");
|
|
909
|
+
resolve();
|
|
910
|
+
};
|
|
911
|
+
request.onerror = () => {
|
|
912
|
+
debug.error(DebugCategory.STORAGE, "Failed to set shouldClearPreviousSession flag in IndexedDB", {
|
|
913
|
+
error: request.error
|
|
914
|
+
});
|
|
915
|
+
reject(request.error);
|
|
916
|
+
};
|
|
917
|
+
});
|
|
918
|
+
}
|
|
868
919
|
};
|
|
869
920
|
|
|
870
921
|
// src/providers/embedded/adapters/url-params.ts
|
|
@@ -878,6 +929,147 @@ var browserUrlParamsAccessor = new BrowserURLParamsAccessor();
|
|
|
878
929
|
|
|
879
930
|
// src/providers/embedded/adapters/auth.ts
|
|
880
931
|
import { DEFAULT_AUTH_URL } from "@phantom/constants";
|
|
932
|
+
|
|
933
|
+
// src/utils/browser-detection.ts
|
|
934
|
+
function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
|
|
935
|
+
let name = "unknown";
|
|
936
|
+
let version = "unknown";
|
|
937
|
+
if (!userAgent || typeof userAgent !== "string") {
|
|
938
|
+
return { name, version, userAgent: "unknown" };
|
|
939
|
+
}
|
|
940
|
+
try {
|
|
941
|
+
if (userAgent.includes("Edg/")) {
|
|
942
|
+
name = "edge";
|
|
943
|
+
const match = userAgent.match(/Edg\/([0-9]+(?:\.[0-9]+)*)/);
|
|
944
|
+
if (match)
|
|
945
|
+
version = match[1].split(".")[0];
|
|
946
|
+
} else if (userAgent.includes("OPR/") || userAgent.includes("Opera/")) {
|
|
947
|
+
name = "opera";
|
|
948
|
+
const match = userAgent.match(/(?:OPR|Opera)\/([0-9]+(?:\.[0-9]+)*)/);
|
|
949
|
+
if (match)
|
|
950
|
+
version = match[1].split(".")[0];
|
|
951
|
+
} else if (userAgent.includes("SamsungBrowser/")) {
|
|
952
|
+
name = "samsung";
|
|
953
|
+
const match = userAgent.match(/SamsungBrowser\/([0-9]+(?:\.[0-9]+)*)/);
|
|
954
|
+
if (match)
|
|
955
|
+
version = match[1].split(".")[0];
|
|
956
|
+
} else if (userAgent.includes("DuckDuckGo/")) {
|
|
957
|
+
name = "duckduckgo";
|
|
958
|
+
const match = userAgent.match(/DuckDuckGo\/([0-9]+(?:\.[0-9]+)*)/);
|
|
959
|
+
if (match)
|
|
960
|
+
version = match[1].split(".")[0];
|
|
961
|
+
} else if (userAgent.includes("Chrome/") && hasBraveAPI) {
|
|
962
|
+
name = "brave";
|
|
963
|
+
const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
|
|
964
|
+
if (match)
|
|
965
|
+
version = match[1].split(".")[0];
|
|
966
|
+
} else if (userAgent.includes("Mobile/") || userAgent.includes("Android")) {
|
|
967
|
+
if (userAgent.includes("Chrome/")) {
|
|
968
|
+
name = "chrome-mobile";
|
|
969
|
+
const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
|
|
970
|
+
if (match)
|
|
971
|
+
version = match[1].split(".")[0];
|
|
972
|
+
} else if (userAgent.includes("Firefox/")) {
|
|
973
|
+
name = "firefox-mobile";
|
|
974
|
+
const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
|
|
975
|
+
if (match)
|
|
976
|
+
version = match[1].split(".")[0];
|
|
977
|
+
} else if (userAgent.includes("Safari/") && userAgent.includes("Mobile/")) {
|
|
978
|
+
name = "safari-mobile";
|
|
979
|
+
const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
|
|
980
|
+
if (match)
|
|
981
|
+
version = match[1].split(".")[0];
|
|
982
|
+
} else {
|
|
983
|
+
name = "mobile";
|
|
984
|
+
}
|
|
985
|
+
} else if (userAgent.includes("Chrome/")) {
|
|
986
|
+
name = "chrome";
|
|
987
|
+
const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
|
|
988
|
+
if (match)
|
|
989
|
+
version = match[1].split(".")[0];
|
|
990
|
+
} else if (userAgent.includes("Firefox/")) {
|
|
991
|
+
name = "firefox";
|
|
992
|
+
const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
|
|
993
|
+
if (match)
|
|
994
|
+
version = match[1].split(".")[0];
|
|
995
|
+
} else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome/")) {
|
|
996
|
+
name = "safari";
|
|
997
|
+
const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
|
|
998
|
+
if (match)
|
|
999
|
+
version = match[1].split(".")[0];
|
|
1000
|
+
}
|
|
1001
|
+
if (name === "unknown") {
|
|
1002
|
+
const patterns = [
|
|
1003
|
+
{ regex: /Chrome\/([0-9]+)/, name: "chrome" },
|
|
1004
|
+
{ regex: /Firefox\/([0-9]+)/, name: "firefox" },
|
|
1005
|
+
{ regex: /Safari\/([0-9]+)/, name: "safari" },
|
|
1006
|
+
{ regex: /Edge\/([0-9]+)/, name: "edge" },
|
|
1007
|
+
{ regex: /Opera\/([0-9]+)/, name: "opera" }
|
|
1008
|
+
];
|
|
1009
|
+
for (const pattern of patterns) {
|
|
1010
|
+
const match = userAgent.match(pattern.regex);
|
|
1011
|
+
if (match) {
|
|
1012
|
+
name = pattern.name;
|
|
1013
|
+
version = match[1];
|
|
1014
|
+
break;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
} catch (error) {
|
|
1019
|
+
}
|
|
1020
|
+
return { name, version, userAgent };
|
|
1021
|
+
}
|
|
1022
|
+
function detectBrowser() {
|
|
1023
|
+
if (typeof window === "undefined" || !window.navigator?.userAgent) {
|
|
1024
|
+
return { name: "unknown", version: "unknown", userAgent: "unknown" };
|
|
1025
|
+
}
|
|
1026
|
+
const userAgent = window.navigator.userAgent;
|
|
1027
|
+
const hasBraveAPI = !!navigator.brave;
|
|
1028
|
+
return parseBrowserFromUserAgent(userAgent, hasBraveAPI);
|
|
1029
|
+
}
|
|
1030
|
+
function getPlatformName() {
|
|
1031
|
+
const { name, version } = detectBrowser();
|
|
1032
|
+
return version !== "unknown" ? `${name}-v${version}` : name;
|
|
1033
|
+
}
|
|
1034
|
+
function getBrowserDisplayName() {
|
|
1035
|
+
const { name, version } = detectBrowser();
|
|
1036
|
+
const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
1037
|
+
return version !== "unknown" ? `${capitalizedName} ${version}` : capitalizedName;
|
|
1038
|
+
}
|
|
1039
|
+
function isMobileDevice() {
|
|
1040
|
+
if (typeof window === "undefined" || !window.navigator?.userAgent) {
|
|
1041
|
+
return false;
|
|
1042
|
+
}
|
|
1043
|
+
const userAgent = window.navigator.userAgent.toLowerCase();
|
|
1044
|
+
const mobilePatterns = [
|
|
1045
|
+
/android/,
|
|
1046
|
+
/iphone|ipad|ipod/,
|
|
1047
|
+
/blackberry/,
|
|
1048
|
+
/windows phone/,
|
|
1049
|
+
/mobile/,
|
|
1050
|
+
/tablet/,
|
|
1051
|
+
/silk/,
|
|
1052
|
+
/kindle/,
|
|
1053
|
+
/opera mini/,
|
|
1054
|
+
/opera mobi/
|
|
1055
|
+
];
|
|
1056
|
+
const isMobileUA = mobilePatterns.some((pattern) => pattern.test(userAgent));
|
|
1057
|
+
let isSmallScreen = false;
|
|
1058
|
+
try {
|
|
1059
|
+
isSmallScreen = window.screen.width <= 768 || window.screen.height <= 768;
|
|
1060
|
+
} catch (error) {
|
|
1061
|
+
isSmallScreen = false;
|
|
1062
|
+
}
|
|
1063
|
+
let isTouchDevice = false;
|
|
1064
|
+
try {
|
|
1065
|
+
isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
1066
|
+
} catch (error) {
|
|
1067
|
+
isTouchDevice = false;
|
|
1068
|
+
}
|
|
1069
|
+
return isMobileUA || isSmallScreen && isTouchDevice;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// src/providers/embedded/adapters/auth.ts
|
|
881
1073
|
var BrowserAuthProvider = class {
|
|
882
1074
|
constructor(urlParamsAccessor) {
|
|
883
1075
|
this.urlParamsAccessor = urlParamsAccessor;
|
|
@@ -899,8 +1091,7 @@ var BrowserAuthProvider = class {
|
|
|
899
1091
|
publicKey: phantomOptions.publicKey,
|
|
900
1092
|
appId: phantomOptions.appId,
|
|
901
1093
|
provider: phantomOptions.provider,
|
|
902
|
-
authUrl: phantomOptions.authUrl
|
|
903
|
-
hasCustomData: !!phantomOptions.customAuthData
|
|
1094
|
+
authUrl: phantomOptions.authUrl
|
|
904
1095
|
});
|
|
905
1096
|
const baseUrl = phantomOptions.authUrl || DEFAULT_AUTH_URL;
|
|
906
1097
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Using auth URL", { baseUrl });
|
|
@@ -909,8 +1100,12 @@ var BrowserAuthProvider = class {
|
|
|
909
1100
|
app_id: phantomOptions.appId,
|
|
910
1101
|
redirect_uri: phantomOptions.redirectUrl || (typeof window !== "undefined" ? this.getValidatedCurrentUrl() : ""),
|
|
911
1102
|
session_id: phantomOptions.sessionId,
|
|
912
|
-
|
|
913
|
-
|
|
1103
|
+
// OAuth session management - defaults to allow refresh unless explicitly clearing after logout
|
|
1104
|
+
clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
|
|
1105
|
+
allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
|
|
1106
|
+
sdk_version: "1.0.0-beta.16",
|
|
1107
|
+
sdk_type: "browser",
|
|
1108
|
+
platform: detectBrowser().name
|
|
914
1109
|
});
|
|
915
1110
|
if (phantomOptions.provider) {
|
|
916
1111
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Provider specified, will skip selection", {
|
|
@@ -921,10 +1116,6 @@ var BrowserAuthProvider = class {
|
|
|
921
1116
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "No provider specified, defaulting to Google");
|
|
922
1117
|
params.append("provider", "google");
|
|
923
1118
|
}
|
|
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
1119
|
const authContext = {
|
|
929
1120
|
publicKey: phantomOptions.publicKey,
|
|
930
1121
|
appId: phantomOptions.appId,
|
|
@@ -995,12 +1186,14 @@ var BrowserAuthProvider = class {
|
|
|
995
1186
|
});
|
|
996
1187
|
const organizationId = this.urlParamsAccessor.getParam("organization_id");
|
|
997
1188
|
const expiresInMs = this.urlParamsAccessor.getParam("expires_in_ms");
|
|
1189
|
+
const authUserId = this.urlParamsAccessor.getParam("auth_user_id");
|
|
998
1190
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Auth redirect parameters", {
|
|
999
1191
|
walletId,
|
|
1000
1192
|
organizationId,
|
|
1001
1193
|
sessionId,
|
|
1002
1194
|
accountDerivationIndex,
|
|
1003
|
-
expiresInMs
|
|
1195
|
+
expiresInMs,
|
|
1196
|
+
authUserId
|
|
1004
1197
|
});
|
|
1005
1198
|
if (!organizationId) {
|
|
1006
1199
|
debug.error(DebugCategory.PHANTOM_CONNECT_AUTH, "Missing organization_id in auth response");
|
|
@@ -1014,9 +1207,9 @@ var BrowserAuthProvider = class {
|
|
|
1014
1207
|
return {
|
|
1015
1208
|
walletId,
|
|
1016
1209
|
organizationId,
|
|
1017
|
-
userInfo: context,
|
|
1018
1210
|
accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
|
|
1019
|
-
expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0
|
|
1211
|
+
expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
|
|
1212
|
+
authUserId: authUserId || void 0
|
|
1020
1213
|
};
|
|
1021
1214
|
} catch (error) {
|
|
1022
1215
|
sessionStorage.removeItem("phantom-auth-context");
|
|
@@ -1025,6 +1218,74 @@ var BrowserAuthProvider = class {
|
|
|
1025
1218
|
}
|
|
1026
1219
|
};
|
|
1027
1220
|
|
|
1221
|
+
// src/providers/embedded/adapters/phantom-app.ts
|
|
1222
|
+
import { isPhantomExtensionInstalled } from "@phantom/browser-injected-sdk";
|
|
1223
|
+
var BrowserPhantomAppProvider = class {
|
|
1224
|
+
/**
|
|
1225
|
+
* Check if the Phantom extension is installed in the browser
|
|
1226
|
+
*/
|
|
1227
|
+
isAvailable() {
|
|
1228
|
+
return isPhantomExtensionInstalled();
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* Authenticate using the Phantom browser extension
|
|
1232
|
+
*/
|
|
1233
|
+
async authenticate(options) {
|
|
1234
|
+
if (!this.isAvailable()) {
|
|
1235
|
+
throw new Error(
|
|
1236
|
+
"Phantom extension is not installed. Please install the Phantom browser extension to use this authentication method."
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
1239
|
+
if (!window.phantom?.app?.login || typeof window.phantom.app.login !== "function") {
|
|
1240
|
+
throw new Error(
|
|
1241
|
+
"Phantom extension authentication is not yet implemented. The extension needs to expose an app.login API (window.phantom.app.login)."
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
try {
|
|
1245
|
+
if (!window.phantom?.app?.features || typeof window.phantom.app.features !== "function") {
|
|
1246
|
+
throw new Error(
|
|
1247
|
+
"Phantom Login is not available. The extension does not support the features API."
|
|
1248
|
+
);
|
|
1249
|
+
}
|
|
1250
|
+
const features = await window.phantom.app.features();
|
|
1251
|
+
if (!Array.isArray(features) || !features.includes("phantom_login")) {
|
|
1252
|
+
throw new Error(
|
|
1253
|
+
"Phantom Login is not available. Please update your Phantom extension to use this authentication method."
|
|
1254
|
+
);
|
|
1255
|
+
}
|
|
1256
|
+
} catch (error) {
|
|
1257
|
+
if (error instanceof Error) {
|
|
1258
|
+
throw error;
|
|
1259
|
+
}
|
|
1260
|
+
throw new Error(
|
|
1261
|
+
"Failed to check Phantom Login availability. Please ensure you have the latest version of the Phantom extension."
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
try {
|
|
1265
|
+
const result = await window.phantom.app.login({
|
|
1266
|
+
publicKey: options.publicKey,
|
|
1267
|
+
appId: options.appId,
|
|
1268
|
+
sessionId: options.sessionId
|
|
1269
|
+
});
|
|
1270
|
+
if (!result || !result.walletId || !result.organizationId) {
|
|
1271
|
+
throw new Error("Invalid authentication response from Phantom extension");
|
|
1272
|
+
}
|
|
1273
|
+
return {
|
|
1274
|
+
walletId: result.walletId,
|
|
1275
|
+
organizationId: result.organizationId,
|
|
1276
|
+
provider: "phantom",
|
|
1277
|
+
accountDerivationIndex: result.accountDerivationIndex ?? 0,
|
|
1278
|
+
expiresInMs: result.expiresInMs ?? 0
|
|
1279
|
+
};
|
|
1280
|
+
} catch (error) {
|
|
1281
|
+
if (error instanceof Error) {
|
|
1282
|
+
throw error;
|
|
1283
|
+
}
|
|
1284
|
+
throw new Error(`Phantom extension authentication failed: ${String(error)}`);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1028
1289
|
// src/providers/embedded/adapters/logger.ts
|
|
1029
1290
|
var BrowserLogger = class {
|
|
1030
1291
|
info(category, message, data) {
|
|
@@ -1041,145 +1302,6 @@ var BrowserLogger = class {
|
|
|
1041
1302
|
}
|
|
1042
1303
|
};
|
|
1043
1304
|
|
|
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
1305
|
// src/providers/embedded/index.ts
|
|
1184
1306
|
import { ANALYTICS_HEADERS } from "@phantom/constants";
|
|
1185
1307
|
var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
@@ -1196,6 +1318,7 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
|
1196
1318
|
const platform = {
|
|
1197
1319
|
storage: new BrowserStorage(),
|
|
1198
1320
|
authProvider: new BrowserAuthProvider(urlParamsAccessor),
|
|
1321
|
+
phantomAppProvider: new BrowserPhantomAppProvider(),
|
|
1199
1322
|
urlParamsAccessor,
|
|
1200
1323
|
stamper,
|
|
1201
1324
|
name: platformName,
|
|
@@ -1208,7 +1331,7 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
|
1208
1331
|
// Full user agent for more detailed info
|
|
1209
1332
|
[ANALYTICS_HEADERS.APP_ID]: config.appId,
|
|
1210
1333
|
[ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
|
|
1211
|
-
[ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0-beta.
|
|
1334
|
+
[ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0-beta.16"
|
|
1212
1335
|
// Replaced at build time
|
|
1213
1336
|
}
|
|
1214
1337
|
};
|
|
@@ -1221,6 +1344,25 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
|
1221
1344
|
|
|
1222
1345
|
// src/ProviderManager.ts
|
|
1223
1346
|
import { DEFAULT_WALLET_API_URL, DEFAULT_EMBEDDED_WALLET_TYPE, DEFAULT_AUTH_URL as DEFAULT_AUTH_URL2 } from "@phantom/constants";
|
|
1347
|
+
|
|
1348
|
+
// src/utils/auth-callback.ts
|
|
1349
|
+
function isAuthFailureCallback(searchParams) {
|
|
1350
|
+
if (typeof window === "undefined" && !searchParams)
|
|
1351
|
+
return false;
|
|
1352
|
+
const params = searchParams || new URLSearchParams(window.location.search);
|
|
1353
|
+
const responseType = params.get("response_type");
|
|
1354
|
+
const sessionId = params.get("session_id");
|
|
1355
|
+
return responseType === "failure" && !!sessionId;
|
|
1356
|
+
}
|
|
1357
|
+
function isAuthCallbackUrl(searchParams) {
|
|
1358
|
+
if (typeof window === "undefined" && !searchParams)
|
|
1359
|
+
return false;
|
|
1360
|
+
const params = searchParams || new URLSearchParams(window.location.search);
|
|
1361
|
+
const sessionId = params.get("session_id");
|
|
1362
|
+
return !!(sessionId && (params.has("response_type") || params.has("wallet_id")));
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
// src/ProviderManager.ts
|
|
1224
1366
|
var ProviderManager = class {
|
|
1225
1367
|
// Track which providers have forwarding set up
|
|
1226
1368
|
constructor(config) {
|
|
@@ -1286,24 +1428,53 @@ var ProviderManager = class {
|
|
|
1286
1428
|
}
|
|
1287
1429
|
/**
|
|
1288
1430
|
* Connect using the current provider
|
|
1431
|
+
* Automatically switches provider based on authOptions.provider if specified
|
|
1289
1432
|
*/
|
|
1290
1433
|
async connect(authOptions) {
|
|
1291
1434
|
debug.info(DebugCategory.PROVIDER_MANAGER, "Starting connection", {
|
|
1292
1435
|
currentProviderKey: this.currentProviderKey,
|
|
1293
1436
|
authOptions: authOptions ? { provider: authOptions.provider, hasJwtToken: !!authOptions.jwtToken } : void 0
|
|
1294
1437
|
});
|
|
1438
|
+
if (authOptions?.provider) {
|
|
1439
|
+
const requestedProvider = authOptions.provider;
|
|
1440
|
+
let targetProviderType = null;
|
|
1441
|
+
if (requestedProvider === "injected") {
|
|
1442
|
+
targetProviderType = "injected";
|
|
1443
|
+
} else if (["google", "apple", "jwt", "phantom"].includes(requestedProvider)) {
|
|
1444
|
+
targetProviderType = "embedded";
|
|
1445
|
+
}
|
|
1446
|
+
if (targetProviderType) {
|
|
1447
|
+
const currentInfo = this.getCurrentProviderInfo();
|
|
1448
|
+
if (currentInfo?.type !== targetProviderType) {
|
|
1449
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Auto-switching provider based on auth options", {
|
|
1450
|
+
from: currentInfo?.type,
|
|
1451
|
+
to: targetProviderType,
|
|
1452
|
+
requestedProvider
|
|
1453
|
+
});
|
|
1454
|
+
const switchOptions = {};
|
|
1455
|
+
if (targetProviderType === "embedded") {
|
|
1456
|
+
switchOptions.embeddedWalletType = currentInfo?.embeddedWalletType || this.config.embeddedWalletType;
|
|
1457
|
+
}
|
|
1458
|
+
this.switchProvider(targetProviderType, switchOptions);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1295
1462
|
if (!this.currentProvider) {
|
|
1296
1463
|
debug.error(DebugCategory.PROVIDER_MANAGER, "No provider selected");
|
|
1297
1464
|
throw new Error("No provider selected");
|
|
1298
1465
|
}
|
|
1299
1466
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Delegating to provider connect method");
|
|
1300
1467
|
const result = await this.currentProvider.connect(authOptions);
|
|
1468
|
+
const providerInfo = this.getCurrentProviderInfo();
|
|
1469
|
+
result.providerType = providerInfo?.type;
|
|
1301
1470
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Connection successful, saving preferences", {
|
|
1302
|
-
addressCount: result.addresses?.length || 0
|
|
1471
|
+
addressCount: result.addresses?.length || 0,
|
|
1472
|
+
providerType: result.providerType
|
|
1303
1473
|
});
|
|
1304
1474
|
this.saveProviderPreference();
|
|
1305
1475
|
debug.info(DebugCategory.PROVIDER_MANAGER, "Connect completed", {
|
|
1306
|
-
addresses: result.addresses
|
|
1476
|
+
addresses: result.addresses,
|
|
1477
|
+
providerType: result.providerType
|
|
1307
1478
|
});
|
|
1308
1479
|
return result;
|
|
1309
1480
|
}
|
|
@@ -1337,6 +1508,10 @@ var ProviderManager = class {
|
|
|
1337
1508
|
*/
|
|
1338
1509
|
async autoConnect() {
|
|
1339
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
|
+
}
|
|
1340
1515
|
const embeddedWalletType = this.config.embeddedWalletType || "user-wallet";
|
|
1341
1516
|
const embeddedKey = this.getProviderKey("embedded", embeddedWalletType);
|
|
1342
1517
|
if (this.providers.has(embeddedKey)) {
|
|
@@ -1362,6 +1537,10 @@ var ProviderManager = class {
|
|
|
1362
1537
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Embedded auto-connect failed", {
|
|
1363
1538
|
error: error.message
|
|
1364
1539
|
});
|
|
1540
|
+
if (isAuthCallbackUrl()) {
|
|
1541
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "In auth callback URL, not attempting injected fallback");
|
|
1542
|
+
return false;
|
|
1543
|
+
}
|
|
1365
1544
|
}
|
|
1366
1545
|
}
|
|
1367
1546
|
const injectedKey = this.getProviderKey("injected");
|
|
@@ -1479,7 +1658,11 @@ var ProviderManager = class {
|
|
|
1479
1658
|
}
|
|
1480
1659
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Creating injected provider");
|
|
1481
1660
|
this.createProvider("injected");
|
|
1482
|
-
|
|
1661
|
+
const switchOptions = {};
|
|
1662
|
+
if (defaultType === "embedded") {
|
|
1663
|
+
switchOptions.embeddedWalletType = defaultEmbeddedType;
|
|
1664
|
+
}
|
|
1665
|
+
this.switchProvider(defaultType, switchOptions);
|
|
1483
1666
|
}
|
|
1484
1667
|
/**
|
|
1485
1668
|
* Create a provider instance
|
|
@@ -1539,35 +1722,17 @@ var ProviderManager = class {
|
|
|
1539
1722
|
console.error("Failed to save provider preference:", error);
|
|
1540
1723
|
}
|
|
1541
1724
|
}
|
|
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
1725
|
};
|
|
1561
1726
|
|
|
1562
1727
|
// src/waitForPhantomExtension.ts
|
|
1563
|
-
import { isPhantomExtensionInstalled } from "@phantom/browser-injected-sdk";
|
|
1728
|
+
import { isPhantomExtensionInstalled as isPhantomExtensionInstalled2 } from "@phantom/browser-injected-sdk";
|
|
1564
1729
|
async function waitForPhantomExtension(timeoutMs = 3e3) {
|
|
1565
1730
|
return new Promise((resolve) => {
|
|
1566
1731
|
const startTime = Date.now();
|
|
1567
1732
|
const checkInterval = 100;
|
|
1568
1733
|
const checkForExtension = () => {
|
|
1569
1734
|
try {
|
|
1570
|
-
if (
|
|
1735
|
+
if (isPhantomExtensionInstalled2()) {
|
|
1571
1736
|
resolve(true);
|
|
1572
1737
|
return;
|
|
1573
1738
|
}
|
|
@@ -1660,22 +1825,6 @@ var BrowserSDK = class {
|
|
|
1660
1825
|
throw error;
|
|
1661
1826
|
}
|
|
1662
1827
|
}
|
|
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
1828
|
// ===== STATE QUERIES =====
|
|
1680
1829
|
/**
|
|
1681
1830
|
* Check if the SDK is connected to a wallet
|
|
@@ -1885,6 +2034,49 @@ function getDeeplinkToPhantom(ref) {
|
|
|
1885
2034
|
return `https://phantom.app/ul/browse/${currentUrl}${refParam}`;
|
|
1886
2035
|
}
|
|
1887
2036
|
|
|
2037
|
+
// src/isPhantomLoginAvailable.ts
|
|
2038
|
+
import { isPhantomExtensionInstalled as isPhantomExtensionInstalled3 } from "@phantom/browser-injected-sdk";
|
|
2039
|
+
async function isPhantomLoginAvailable(timeoutMs = 3e3) {
|
|
2040
|
+
const extensionInstalled = await waitForExtension(timeoutMs);
|
|
2041
|
+
if (!extensionInstalled) {
|
|
2042
|
+
return false;
|
|
2043
|
+
}
|
|
2044
|
+
try {
|
|
2045
|
+
if (!window.phantom?.app?.features || typeof window.phantom.app.features !== "function") {
|
|
2046
|
+
return false;
|
|
2047
|
+
}
|
|
2048
|
+
const features = await window.phantom.app.features();
|
|
2049
|
+
if (!Array.isArray(features)) {
|
|
2050
|
+
return false;
|
|
2051
|
+
}
|
|
2052
|
+
return features.includes("phantom_login");
|
|
2053
|
+
} catch (error) {
|
|
2054
|
+
return false;
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
async function waitForExtension(timeoutMs) {
|
|
2058
|
+
return new Promise((resolve) => {
|
|
2059
|
+
const startTime = Date.now();
|
|
2060
|
+
const checkInterval = 100;
|
|
2061
|
+
const checkForExtension = () => {
|
|
2062
|
+
try {
|
|
2063
|
+
if (isPhantomExtensionInstalled3()) {
|
|
2064
|
+
resolve(true);
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
} catch (error) {
|
|
2068
|
+
}
|
|
2069
|
+
const elapsed = Date.now() - startTime;
|
|
2070
|
+
if (elapsed >= timeoutMs) {
|
|
2071
|
+
resolve(false);
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2074
|
+
setTimeout(checkForExtension, checkInterval);
|
|
2075
|
+
};
|
|
2076
|
+
checkForExtension();
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
|
|
1888
2080
|
// src/index.ts
|
|
1889
2081
|
import { NetworkId } from "@phantom/constants";
|
|
1890
2082
|
import { AddressType as AddressType5 } from "@phantom/client";
|
|
@@ -1900,6 +2092,7 @@ export {
|
|
|
1900
2092
|
getDeeplinkToPhantom,
|
|
1901
2093
|
getPlatformName,
|
|
1902
2094
|
isMobileDevice,
|
|
2095
|
+
isPhantomLoginAvailable,
|
|
1903
2096
|
parseBrowserFromUserAgent,
|
|
1904
2097
|
waitForPhantomExtension
|
|
1905
2098
|
};
|