@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.js
CHANGED
|
@@ -31,6 +31,7 @@ __export(src_exports, {
|
|
|
31
31
|
getDeeplinkToPhantom: () => getDeeplinkToPhantom,
|
|
32
32
|
getPlatformName: () => getPlatformName,
|
|
33
33
|
isMobileDevice: () => isMobileDevice,
|
|
34
|
+
isPhantomLoginAvailable: () => isPhantomLoginAvailable,
|
|
34
35
|
parseBrowserFromUserAgent: () => parseBrowserFromUserAgent,
|
|
35
36
|
waitForPhantomExtension: () => waitForPhantomExtension
|
|
36
37
|
});
|
|
@@ -476,7 +477,12 @@ var InjectedProvider = class {
|
|
|
476
477
|
debug.info(DebugCategory.INJECTED_PROVIDER, "Solana connected successfully", { address: publicKey });
|
|
477
478
|
}
|
|
478
479
|
} catch (err) {
|
|
479
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana", { error: err });
|
|
480
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana, stopping", { error: err });
|
|
481
|
+
this.emit("connect_error", {
|
|
482
|
+
error: err instanceof Error ? err.message : "Failed to connect",
|
|
483
|
+
source: "manual-connect"
|
|
484
|
+
});
|
|
485
|
+
throw err;
|
|
480
486
|
}
|
|
481
487
|
}
|
|
482
488
|
if (this.addressTypes.includes(import_client4.AddressType.ethereum)) {
|
|
@@ -492,7 +498,12 @@ var InjectedProvider = class {
|
|
|
492
498
|
debug.info(DebugCategory.INJECTED_PROVIDER, "Ethereum connected successfully", { addresses: accounts });
|
|
493
499
|
}
|
|
494
500
|
} catch (err) {
|
|
495
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum", { error: err });
|
|
501
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum, stopping", { error: err });
|
|
502
|
+
this.emit("connect_error", {
|
|
503
|
+
error: err instanceof Error ? err.message : "Failed to connect",
|
|
504
|
+
source: "manual-connect"
|
|
505
|
+
});
|
|
506
|
+
throw err;
|
|
496
507
|
}
|
|
497
508
|
}
|
|
498
509
|
if (connectedAddresses.length === 0) {
|
|
@@ -901,6 +912,47 @@ var BrowserStorage = class {
|
|
|
901
912
|
};
|
|
902
913
|
});
|
|
903
914
|
}
|
|
915
|
+
async getShouldClearPreviousSession() {
|
|
916
|
+
debug.log(DebugCategory.STORAGE, "Getting shouldClearPreviousSession flag from IndexedDB");
|
|
917
|
+
const db = await this.getDB();
|
|
918
|
+
return new Promise((resolve, reject) => {
|
|
919
|
+
const transaction = db.transaction([this.storeName], "readonly");
|
|
920
|
+
const store = transaction.objectStore(this.storeName);
|
|
921
|
+
const request = store.get("shouldClearPreviousSession");
|
|
922
|
+
request.onsuccess = () => {
|
|
923
|
+
const shouldClear = request.result ?? false;
|
|
924
|
+
debug.log(DebugCategory.STORAGE, "Retrieved shouldClearPreviousSession flag from IndexedDB", {
|
|
925
|
+
shouldClear
|
|
926
|
+
});
|
|
927
|
+
resolve(shouldClear);
|
|
928
|
+
};
|
|
929
|
+
request.onerror = () => {
|
|
930
|
+
debug.error(DebugCategory.STORAGE, "Failed to get shouldClearPreviousSession flag from IndexedDB", {
|
|
931
|
+
error: request.error
|
|
932
|
+
});
|
|
933
|
+
reject(request.error);
|
|
934
|
+
};
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
async setShouldClearPreviousSession(should) {
|
|
938
|
+
debug.log(DebugCategory.STORAGE, "Setting shouldClearPreviousSession flag in IndexedDB", { should });
|
|
939
|
+
const db = await this.getDB();
|
|
940
|
+
return new Promise((resolve, reject) => {
|
|
941
|
+
const transaction = db.transaction([this.storeName], "readwrite");
|
|
942
|
+
const store = transaction.objectStore(this.storeName);
|
|
943
|
+
const request = store.put(should, "shouldClearPreviousSession");
|
|
944
|
+
request.onsuccess = () => {
|
|
945
|
+
debug.log(DebugCategory.STORAGE, "Successfully set shouldClearPreviousSession flag in IndexedDB");
|
|
946
|
+
resolve();
|
|
947
|
+
};
|
|
948
|
+
request.onerror = () => {
|
|
949
|
+
debug.error(DebugCategory.STORAGE, "Failed to set shouldClearPreviousSession flag in IndexedDB", {
|
|
950
|
+
error: request.error
|
|
951
|
+
});
|
|
952
|
+
reject(request.error);
|
|
953
|
+
};
|
|
954
|
+
});
|
|
955
|
+
}
|
|
904
956
|
};
|
|
905
957
|
|
|
906
958
|
// src/providers/embedded/adapters/url-params.ts
|
|
@@ -914,6 +966,147 @@ var browserUrlParamsAccessor = new BrowserURLParamsAccessor();
|
|
|
914
966
|
|
|
915
967
|
// src/providers/embedded/adapters/auth.ts
|
|
916
968
|
var import_constants = require("@phantom/constants");
|
|
969
|
+
|
|
970
|
+
// src/utils/browser-detection.ts
|
|
971
|
+
function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
|
|
972
|
+
let name = "unknown";
|
|
973
|
+
let version = "unknown";
|
|
974
|
+
if (!userAgent || typeof userAgent !== "string") {
|
|
975
|
+
return { name, version, userAgent: "unknown" };
|
|
976
|
+
}
|
|
977
|
+
try {
|
|
978
|
+
if (userAgent.includes("Edg/")) {
|
|
979
|
+
name = "edge";
|
|
980
|
+
const match = userAgent.match(/Edg\/([0-9]+(?:\.[0-9]+)*)/);
|
|
981
|
+
if (match)
|
|
982
|
+
version = match[1].split(".")[0];
|
|
983
|
+
} else if (userAgent.includes("OPR/") || userAgent.includes("Opera/")) {
|
|
984
|
+
name = "opera";
|
|
985
|
+
const match = userAgent.match(/(?:OPR|Opera)\/([0-9]+(?:\.[0-9]+)*)/);
|
|
986
|
+
if (match)
|
|
987
|
+
version = match[1].split(".")[0];
|
|
988
|
+
} else if (userAgent.includes("SamsungBrowser/")) {
|
|
989
|
+
name = "samsung";
|
|
990
|
+
const match = userAgent.match(/SamsungBrowser\/([0-9]+(?:\.[0-9]+)*)/);
|
|
991
|
+
if (match)
|
|
992
|
+
version = match[1].split(".")[0];
|
|
993
|
+
} else if (userAgent.includes("DuckDuckGo/")) {
|
|
994
|
+
name = "duckduckgo";
|
|
995
|
+
const match = userAgent.match(/DuckDuckGo\/([0-9]+(?:\.[0-9]+)*)/);
|
|
996
|
+
if (match)
|
|
997
|
+
version = match[1].split(".")[0];
|
|
998
|
+
} else if (userAgent.includes("Chrome/") && hasBraveAPI) {
|
|
999
|
+
name = "brave";
|
|
1000
|
+
const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
|
|
1001
|
+
if (match)
|
|
1002
|
+
version = match[1].split(".")[0];
|
|
1003
|
+
} else if (userAgent.includes("Mobile/") || userAgent.includes("Android")) {
|
|
1004
|
+
if (userAgent.includes("Chrome/")) {
|
|
1005
|
+
name = "chrome-mobile";
|
|
1006
|
+
const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
|
|
1007
|
+
if (match)
|
|
1008
|
+
version = match[1].split(".")[0];
|
|
1009
|
+
} else if (userAgent.includes("Firefox/")) {
|
|
1010
|
+
name = "firefox-mobile";
|
|
1011
|
+
const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
|
|
1012
|
+
if (match)
|
|
1013
|
+
version = match[1].split(".")[0];
|
|
1014
|
+
} else if (userAgent.includes("Safari/") && userAgent.includes("Mobile/")) {
|
|
1015
|
+
name = "safari-mobile";
|
|
1016
|
+
const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
|
|
1017
|
+
if (match)
|
|
1018
|
+
version = match[1].split(".")[0];
|
|
1019
|
+
} else {
|
|
1020
|
+
name = "mobile";
|
|
1021
|
+
}
|
|
1022
|
+
} else if (userAgent.includes("Chrome/")) {
|
|
1023
|
+
name = "chrome";
|
|
1024
|
+
const match = userAgent.match(/Chrome\/([0-9]+(?:\.[0-9]+)*)/);
|
|
1025
|
+
if (match)
|
|
1026
|
+
version = match[1].split(".")[0];
|
|
1027
|
+
} else if (userAgent.includes("Firefox/")) {
|
|
1028
|
+
name = "firefox";
|
|
1029
|
+
const match = userAgent.match(/Firefox\/([0-9]+(?:\.[0-9]+)*)/);
|
|
1030
|
+
if (match)
|
|
1031
|
+
version = match[1].split(".")[0];
|
|
1032
|
+
} else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome/")) {
|
|
1033
|
+
name = "safari";
|
|
1034
|
+
const match = userAgent.match(/Version\/([0-9]+(?:\.[0-9]+)*)/);
|
|
1035
|
+
if (match)
|
|
1036
|
+
version = match[1].split(".")[0];
|
|
1037
|
+
}
|
|
1038
|
+
if (name === "unknown") {
|
|
1039
|
+
const patterns = [
|
|
1040
|
+
{ regex: /Chrome\/([0-9]+)/, name: "chrome" },
|
|
1041
|
+
{ regex: /Firefox\/([0-9]+)/, name: "firefox" },
|
|
1042
|
+
{ regex: /Safari\/([0-9]+)/, name: "safari" },
|
|
1043
|
+
{ regex: /Edge\/([0-9]+)/, name: "edge" },
|
|
1044
|
+
{ regex: /Opera\/([0-9]+)/, name: "opera" }
|
|
1045
|
+
];
|
|
1046
|
+
for (const pattern of patterns) {
|
|
1047
|
+
const match = userAgent.match(pattern.regex);
|
|
1048
|
+
if (match) {
|
|
1049
|
+
name = pattern.name;
|
|
1050
|
+
version = match[1];
|
|
1051
|
+
break;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
} catch (error) {
|
|
1056
|
+
}
|
|
1057
|
+
return { name, version, userAgent };
|
|
1058
|
+
}
|
|
1059
|
+
function detectBrowser() {
|
|
1060
|
+
if (typeof window === "undefined" || !window.navigator?.userAgent) {
|
|
1061
|
+
return { name: "unknown", version: "unknown", userAgent: "unknown" };
|
|
1062
|
+
}
|
|
1063
|
+
const userAgent = window.navigator.userAgent;
|
|
1064
|
+
const hasBraveAPI = !!navigator.brave;
|
|
1065
|
+
return parseBrowserFromUserAgent(userAgent, hasBraveAPI);
|
|
1066
|
+
}
|
|
1067
|
+
function getPlatformName() {
|
|
1068
|
+
const { name, version } = detectBrowser();
|
|
1069
|
+
return version !== "unknown" ? `${name}-v${version}` : name;
|
|
1070
|
+
}
|
|
1071
|
+
function getBrowserDisplayName() {
|
|
1072
|
+
const { name, version } = detectBrowser();
|
|
1073
|
+
const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
1074
|
+
return version !== "unknown" ? `${capitalizedName} ${version}` : capitalizedName;
|
|
1075
|
+
}
|
|
1076
|
+
function isMobileDevice() {
|
|
1077
|
+
if (typeof window === "undefined" || !window.navigator?.userAgent) {
|
|
1078
|
+
return false;
|
|
1079
|
+
}
|
|
1080
|
+
const userAgent = window.navigator.userAgent.toLowerCase();
|
|
1081
|
+
const mobilePatterns = [
|
|
1082
|
+
/android/,
|
|
1083
|
+
/iphone|ipad|ipod/,
|
|
1084
|
+
/blackberry/,
|
|
1085
|
+
/windows phone/,
|
|
1086
|
+
/mobile/,
|
|
1087
|
+
/tablet/,
|
|
1088
|
+
/silk/,
|
|
1089
|
+
/kindle/,
|
|
1090
|
+
/opera mini/,
|
|
1091
|
+
/opera mobi/
|
|
1092
|
+
];
|
|
1093
|
+
const isMobileUA = mobilePatterns.some((pattern) => pattern.test(userAgent));
|
|
1094
|
+
let isSmallScreen = false;
|
|
1095
|
+
try {
|
|
1096
|
+
isSmallScreen = window.screen.width <= 768 || window.screen.height <= 768;
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
isSmallScreen = false;
|
|
1099
|
+
}
|
|
1100
|
+
let isTouchDevice = false;
|
|
1101
|
+
try {
|
|
1102
|
+
isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
1103
|
+
} catch (error) {
|
|
1104
|
+
isTouchDevice = false;
|
|
1105
|
+
}
|
|
1106
|
+
return isMobileUA || isSmallScreen && isTouchDevice;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// src/providers/embedded/adapters/auth.ts
|
|
917
1110
|
var BrowserAuthProvider = class {
|
|
918
1111
|
constructor(urlParamsAccessor) {
|
|
919
1112
|
this.urlParamsAccessor = urlParamsAccessor;
|
|
@@ -935,8 +1128,7 @@ var BrowserAuthProvider = class {
|
|
|
935
1128
|
publicKey: phantomOptions.publicKey,
|
|
936
1129
|
appId: phantomOptions.appId,
|
|
937
1130
|
provider: phantomOptions.provider,
|
|
938
|
-
authUrl: phantomOptions.authUrl
|
|
939
|
-
hasCustomData: !!phantomOptions.customAuthData
|
|
1131
|
+
authUrl: phantomOptions.authUrl
|
|
940
1132
|
});
|
|
941
1133
|
const baseUrl = phantomOptions.authUrl || import_constants.DEFAULT_AUTH_URL;
|
|
942
1134
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Using auth URL", { baseUrl });
|
|
@@ -945,8 +1137,12 @@ var BrowserAuthProvider = class {
|
|
|
945
1137
|
app_id: phantomOptions.appId,
|
|
946
1138
|
redirect_uri: phantomOptions.redirectUrl || (typeof window !== "undefined" ? this.getValidatedCurrentUrl() : ""),
|
|
947
1139
|
session_id: phantomOptions.sessionId,
|
|
948
|
-
|
|
949
|
-
|
|
1140
|
+
// OAuth session management - defaults to allow refresh unless explicitly clearing after logout
|
|
1141
|
+
clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
|
|
1142
|
+
allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
|
|
1143
|
+
sdk_version: "1.0.0-beta.16",
|
|
1144
|
+
sdk_type: "browser",
|
|
1145
|
+
platform: detectBrowser().name
|
|
950
1146
|
});
|
|
951
1147
|
if (phantomOptions.provider) {
|
|
952
1148
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Provider specified, will skip selection", {
|
|
@@ -957,10 +1153,6 @@ var BrowserAuthProvider = class {
|
|
|
957
1153
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "No provider specified, defaulting to Google");
|
|
958
1154
|
params.append("provider", "google");
|
|
959
1155
|
}
|
|
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
1156
|
const authContext = {
|
|
965
1157
|
publicKey: phantomOptions.publicKey,
|
|
966
1158
|
appId: phantomOptions.appId,
|
|
@@ -1031,12 +1223,14 @@ var BrowserAuthProvider = class {
|
|
|
1031
1223
|
});
|
|
1032
1224
|
const organizationId = this.urlParamsAccessor.getParam("organization_id");
|
|
1033
1225
|
const expiresInMs = this.urlParamsAccessor.getParam("expires_in_ms");
|
|
1226
|
+
const authUserId = this.urlParamsAccessor.getParam("auth_user_id");
|
|
1034
1227
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Auth redirect parameters", {
|
|
1035
1228
|
walletId,
|
|
1036
1229
|
organizationId,
|
|
1037
1230
|
sessionId,
|
|
1038
1231
|
accountDerivationIndex,
|
|
1039
|
-
expiresInMs
|
|
1232
|
+
expiresInMs,
|
|
1233
|
+
authUserId
|
|
1040
1234
|
});
|
|
1041
1235
|
if (!organizationId) {
|
|
1042
1236
|
debug.error(DebugCategory.PHANTOM_CONNECT_AUTH, "Missing organization_id in auth response");
|
|
@@ -1050,9 +1244,9 @@ var BrowserAuthProvider = class {
|
|
|
1050
1244
|
return {
|
|
1051
1245
|
walletId,
|
|
1052
1246
|
organizationId,
|
|
1053
|
-
userInfo: context,
|
|
1054
1247
|
accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
|
|
1055
|
-
expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0
|
|
1248
|
+
expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
|
|
1249
|
+
authUserId: authUserId || void 0
|
|
1056
1250
|
};
|
|
1057
1251
|
} catch (error) {
|
|
1058
1252
|
sessionStorage.removeItem("phantom-auth-context");
|
|
@@ -1061,6 +1255,74 @@ var BrowserAuthProvider = class {
|
|
|
1061
1255
|
}
|
|
1062
1256
|
};
|
|
1063
1257
|
|
|
1258
|
+
// src/providers/embedded/adapters/phantom-app.ts
|
|
1259
|
+
var import_browser_injected_sdk2 = require("@phantom/browser-injected-sdk");
|
|
1260
|
+
var BrowserPhantomAppProvider = class {
|
|
1261
|
+
/**
|
|
1262
|
+
* Check if the Phantom extension is installed in the browser
|
|
1263
|
+
*/
|
|
1264
|
+
isAvailable() {
|
|
1265
|
+
return (0, import_browser_injected_sdk2.isPhantomExtensionInstalled)();
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Authenticate using the Phantom browser extension
|
|
1269
|
+
*/
|
|
1270
|
+
async authenticate(options) {
|
|
1271
|
+
if (!this.isAvailable()) {
|
|
1272
|
+
throw new Error(
|
|
1273
|
+
"Phantom extension is not installed. Please install the Phantom browser extension to use this authentication method."
|
|
1274
|
+
);
|
|
1275
|
+
}
|
|
1276
|
+
if (!window.phantom?.app?.login || typeof window.phantom.app.login !== "function") {
|
|
1277
|
+
throw new Error(
|
|
1278
|
+
"Phantom extension authentication is not yet implemented. The extension needs to expose an app.login API (window.phantom.app.login)."
|
|
1279
|
+
);
|
|
1280
|
+
}
|
|
1281
|
+
try {
|
|
1282
|
+
if (!window.phantom?.app?.features || typeof window.phantom.app.features !== "function") {
|
|
1283
|
+
throw new Error(
|
|
1284
|
+
"Phantom Login is not available. The extension does not support the features API."
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
const features = await window.phantom.app.features();
|
|
1288
|
+
if (!Array.isArray(features) || !features.includes("phantom_login")) {
|
|
1289
|
+
throw new Error(
|
|
1290
|
+
"Phantom Login is not available. Please update your Phantom extension to use this authentication method."
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
} catch (error) {
|
|
1294
|
+
if (error instanceof Error) {
|
|
1295
|
+
throw error;
|
|
1296
|
+
}
|
|
1297
|
+
throw new Error(
|
|
1298
|
+
"Failed to check Phantom Login availability. Please ensure you have the latest version of the Phantom extension."
|
|
1299
|
+
);
|
|
1300
|
+
}
|
|
1301
|
+
try {
|
|
1302
|
+
const result = await window.phantom.app.login({
|
|
1303
|
+
publicKey: options.publicKey,
|
|
1304
|
+
appId: options.appId,
|
|
1305
|
+
sessionId: options.sessionId
|
|
1306
|
+
});
|
|
1307
|
+
if (!result || !result.walletId || !result.organizationId) {
|
|
1308
|
+
throw new Error("Invalid authentication response from Phantom extension");
|
|
1309
|
+
}
|
|
1310
|
+
return {
|
|
1311
|
+
walletId: result.walletId,
|
|
1312
|
+
organizationId: result.organizationId,
|
|
1313
|
+
provider: "phantom",
|
|
1314
|
+
accountDerivationIndex: result.accountDerivationIndex ?? 0,
|
|
1315
|
+
expiresInMs: result.expiresInMs ?? 0
|
|
1316
|
+
};
|
|
1317
|
+
} catch (error) {
|
|
1318
|
+
if (error instanceof Error) {
|
|
1319
|
+
throw error;
|
|
1320
|
+
}
|
|
1321
|
+
throw new Error(`Phantom extension authentication failed: ${String(error)}`);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
};
|
|
1325
|
+
|
|
1064
1326
|
// src/providers/embedded/adapters/logger.ts
|
|
1065
1327
|
var BrowserLogger = class {
|
|
1066
1328
|
info(category, message, data) {
|
|
@@ -1077,145 +1339,6 @@ var BrowserLogger = class {
|
|
|
1077
1339
|
}
|
|
1078
1340
|
};
|
|
1079
1341
|
|
|
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
1342
|
// src/providers/embedded/index.ts
|
|
1220
1343
|
var import_constants2 = require("@phantom/constants");
|
|
1221
1344
|
var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvider {
|
|
@@ -1232,6 +1355,7 @@ var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvi
|
|
|
1232
1355
|
const platform = {
|
|
1233
1356
|
storage: new BrowserStorage(),
|
|
1234
1357
|
authProvider: new BrowserAuthProvider(urlParamsAccessor),
|
|
1358
|
+
phantomAppProvider: new BrowserPhantomAppProvider(),
|
|
1235
1359
|
urlParamsAccessor,
|
|
1236
1360
|
stamper,
|
|
1237
1361
|
name: platformName,
|
|
@@ -1244,7 +1368,7 @@ var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvi
|
|
|
1244
1368
|
// Full user agent for more detailed info
|
|
1245
1369
|
[import_constants2.ANALYTICS_HEADERS.APP_ID]: config.appId,
|
|
1246
1370
|
[import_constants2.ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
|
|
1247
|
-
[import_constants2.ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0-beta.
|
|
1371
|
+
[import_constants2.ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0-beta.16"
|
|
1248
1372
|
// Replaced at build time
|
|
1249
1373
|
}
|
|
1250
1374
|
};
|
|
@@ -1257,6 +1381,25 @@ var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvi
|
|
|
1257
1381
|
|
|
1258
1382
|
// src/ProviderManager.ts
|
|
1259
1383
|
var import_constants3 = require("@phantom/constants");
|
|
1384
|
+
|
|
1385
|
+
// src/utils/auth-callback.ts
|
|
1386
|
+
function isAuthFailureCallback(searchParams) {
|
|
1387
|
+
if (typeof window === "undefined" && !searchParams)
|
|
1388
|
+
return false;
|
|
1389
|
+
const params = searchParams || new URLSearchParams(window.location.search);
|
|
1390
|
+
const responseType = params.get("response_type");
|
|
1391
|
+
const sessionId = params.get("session_id");
|
|
1392
|
+
return responseType === "failure" && !!sessionId;
|
|
1393
|
+
}
|
|
1394
|
+
function isAuthCallbackUrl(searchParams) {
|
|
1395
|
+
if (typeof window === "undefined" && !searchParams)
|
|
1396
|
+
return false;
|
|
1397
|
+
const params = searchParams || new URLSearchParams(window.location.search);
|
|
1398
|
+
const sessionId = params.get("session_id");
|
|
1399
|
+
return !!(sessionId && (params.has("response_type") || params.has("wallet_id")));
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
// src/ProviderManager.ts
|
|
1260
1403
|
var ProviderManager = class {
|
|
1261
1404
|
// Track which providers have forwarding set up
|
|
1262
1405
|
constructor(config) {
|
|
@@ -1322,24 +1465,53 @@ var ProviderManager = class {
|
|
|
1322
1465
|
}
|
|
1323
1466
|
/**
|
|
1324
1467
|
* Connect using the current provider
|
|
1468
|
+
* Automatically switches provider based on authOptions.provider if specified
|
|
1325
1469
|
*/
|
|
1326
1470
|
async connect(authOptions) {
|
|
1327
1471
|
debug.info(DebugCategory.PROVIDER_MANAGER, "Starting connection", {
|
|
1328
1472
|
currentProviderKey: this.currentProviderKey,
|
|
1329
1473
|
authOptions: authOptions ? { provider: authOptions.provider, hasJwtToken: !!authOptions.jwtToken } : void 0
|
|
1330
1474
|
});
|
|
1475
|
+
if (authOptions?.provider) {
|
|
1476
|
+
const requestedProvider = authOptions.provider;
|
|
1477
|
+
let targetProviderType = null;
|
|
1478
|
+
if (requestedProvider === "injected") {
|
|
1479
|
+
targetProviderType = "injected";
|
|
1480
|
+
} else if (["google", "apple", "jwt", "phantom"].includes(requestedProvider)) {
|
|
1481
|
+
targetProviderType = "embedded";
|
|
1482
|
+
}
|
|
1483
|
+
if (targetProviderType) {
|
|
1484
|
+
const currentInfo = this.getCurrentProviderInfo();
|
|
1485
|
+
if (currentInfo?.type !== targetProviderType) {
|
|
1486
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Auto-switching provider based on auth options", {
|
|
1487
|
+
from: currentInfo?.type,
|
|
1488
|
+
to: targetProviderType,
|
|
1489
|
+
requestedProvider
|
|
1490
|
+
});
|
|
1491
|
+
const switchOptions = {};
|
|
1492
|
+
if (targetProviderType === "embedded") {
|
|
1493
|
+
switchOptions.embeddedWalletType = currentInfo?.embeddedWalletType || this.config.embeddedWalletType;
|
|
1494
|
+
}
|
|
1495
|
+
this.switchProvider(targetProviderType, switchOptions);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1331
1499
|
if (!this.currentProvider) {
|
|
1332
1500
|
debug.error(DebugCategory.PROVIDER_MANAGER, "No provider selected");
|
|
1333
1501
|
throw new Error("No provider selected");
|
|
1334
1502
|
}
|
|
1335
1503
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Delegating to provider connect method");
|
|
1336
1504
|
const result = await this.currentProvider.connect(authOptions);
|
|
1505
|
+
const providerInfo = this.getCurrentProviderInfo();
|
|
1506
|
+
result.providerType = providerInfo?.type;
|
|
1337
1507
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Connection successful, saving preferences", {
|
|
1338
|
-
addressCount: result.addresses?.length || 0
|
|
1508
|
+
addressCount: result.addresses?.length || 0,
|
|
1509
|
+
providerType: result.providerType
|
|
1339
1510
|
});
|
|
1340
1511
|
this.saveProviderPreference();
|
|
1341
1512
|
debug.info(DebugCategory.PROVIDER_MANAGER, "Connect completed", {
|
|
1342
|
-
addresses: result.addresses
|
|
1513
|
+
addresses: result.addresses,
|
|
1514
|
+
providerType: result.providerType
|
|
1343
1515
|
});
|
|
1344
1516
|
return result;
|
|
1345
1517
|
}
|
|
@@ -1373,6 +1545,10 @@ var ProviderManager = class {
|
|
|
1373
1545
|
*/
|
|
1374
1546
|
async autoConnect() {
|
|
1375
1547
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Starting auto-connect with fallback strategy");
|
|
1548
|
+
if (isAuthFailureCallback()) {
|
|
1549
|
+
debug.warn(DebugCategory.PROVIDER_MANAGER, "Auth failure detected in URL, skipping autoConnect fallback");
|
|
1550
|
+
return false;
|
|
1551
|
+
}
|
|
1376
1552
|
const embeddedWalletType = this.config.embeddedWalletType || "user-wallet";
|
|
1377
1553
|
const embeddedKey = this.getProviderKey("embedded", embeddedWalletType);
|
|
1378
1554
|
if (this.providers.has(embeddedKey)) {
|
|
@@ -1398,6 +1574,10 @@ var ProviderManager = class {
|
|
|
1398
1574
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Embedded auto-connect failed", {
|
|
1399
1575
|
error: error.message
|
|
1400
1576
|
});
|
|
1577
|
+
if (isAuthCallbackUrl()) {
|
|
1578
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "In auth callback URL, not attempting injected fallback");
|
|
1579
|
+
return false;
|
|
1580
|
+
}
|
|
1401
1581
|
}
|
|
1402
1582
|
}
|
|
1403
1583
|
const injectedKey = this.getProviderKey("injected");
|
|
@@ -1515,7 +1695,11 @@ var ProviderManager = class {
|
|
|
1515
1695
|
}
|
|
1516
1696
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Creating injected provider");
|
|
1517
1697
|
this.createProvider("injected");
|
|
1518
|
-
|
|
1698
|
+
const switchOptions = {};
|
|
1699
|
+
if (defaultType === "embedded") {
|
|
1700
|
+
switchOptions.embeddedWalletType = defaultEmbeddedType;
|
|
1701
|
+
}
|
|
1702
|
+
this.switchProvider(defaultType, switchOptions);
|
|
1519
1703
|
}
|
|
1520
1704
|
/**
|
|
1521
1705
|
* Create a provider instance
|
|
@@ -1575,35 +1759,17 @@ var ProviderManager = class {
|
|
|
1575
1759
|
console.error("Failed to save provider preference:", error);
|
|
1576
1760
|
}
|
|
1577
1761
|
}
|
|
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
1762
|
};
|
|
1597
1763
|
|
|
1598
1764
|
// src/waitForPhantomExtension.ts
|
|
1599
|
-
var
|
|
1765
|
+
var import_browser_injected_sdk3 = require("@phantom/browser-injected-sdk");
|
|
1600
1766
|
async function waitForPhantomExtension(timeoutMs = 3e3) {
|
|
1601
1767
|
return new Promise((resolve) => {
|
|
1602
1768
|
const startTime = Date.now();
|
|
1603
1769
|
const checkInterval = 100;
|
|
1604
1770
|
const checkForExtension = () => {
|
|
1605
1771
|
try {
|
|
1606
|
-
if ((0,
|
|
1772
|
+
if ((0, import_browser_injected_sdk3.isPhantomExtensionInstalled)()) {
|
|
1607
1773
|
resolve(true);
|
|
1608
1774
|
return;
|
|
1609
1775
|
}
|
|
@@ -1696,22 +1862,6 @@ var BrowserSDK = class {
|
|
|
1696
1862
|
throw error;
|
|
1697
1863
|
}
|
|
1698
1864
|
}
|
|
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
1865
|
// ===== STATE QUERIES =====
|
|
1716
1866
|
/**
|
|
1717
1867
|
* Check if the SDK is connected to a wallet
|
|
@@ -1921,6 +2071,49 @@ function getDeeplinkToPhantom(ref) {
|
|
|
1921
2071
|
return `https://phantom.app/ul/browse/${currentUrl}${refParam}`;
|
|
1922
2072
|
}
|
|
1923
2073
|
|
|
2074
|
+
// src/isPhantomLoginAvailable.ts
|
|
2075
|
+
var import_browser_injected_sdk4 = require("@phantom/browser-injected-sdk");
|
|
2076
|
+
async function isPhantomLoginAvailable(timeoutMs = 3e3) {
|
|
2077
|
+
const extensionInstalled = await waitForExtension(timeoutMs);
|
|
2078
|
+
if (!extensionInstalled) {
|
|
2079
|
+
return false;
|
|
2080
|
+
}
|
|
2081
|
+
try {
|
|
2082
|
+
if (!window.phantom?.app?.features || typeof window.phantom.app.features !== "function") {
|
|
2083
|
+
return false;
|
|
2084
|
+
}
|
|
2085
|
+
const features = await window.phantom.app.features();
|
|
2086
|
+
if (!Array.isArray(features)) {
|
|
2087
|
+
return false;
|
|
2088
|
+
}
|
|
2089
|
+
return features.includes("phantom_login");
|
|
2090
|
+
} catch (error) {
|
|
2091
|
+
return false;
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
async function waitForExtension(timeoutMs) {
|
|
2095
|
+
return new Promise((resolve) => {
|
|
2096
|
+
const startTime = Date.now();
|
|
2097
|
+
const checkInterval = 100;
|
|
2098
|
+
const checkForExtension = () => {
|
|
2099
|
+
try {
|
|
2100
|
+
if ((0, import_browser_injected_sdk4.isPhantomExtensionInstalled)()) {
|
|
2101
|
+
resolve(true);
|
|
2102
|
+
return;
|
|
2103
|
+
}
|
|
2104
|
+
} catch (error) {
|
|
2105
|
+
}
|
|
2106
|
+
const elapsed = Date.now() - startTime;
|
|
2107
|
+
if (elapsed >= timeoutMs) {
|
|
2108
|
+
resolve(false);
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
setTimeout(checkForExtension, checkInterval);
|
|
2112
|
+
};
|
|
2113
|
+
checkForExtension();
|
|
2114
|
+
});
|
|
2115
|
+
}
|
|
2116
|
+
|
|
1924
2117
|
// src/index.ts
|
|
1925
2118
|
var import_constants5 = require("@phantom/constants");
|
|
1926
2119
|
var import_client5 = require("@phantom/client");
|