@phantom/browser-sdk 1.0.0-beta.1 → 1.0.0-beta.10
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 +37 -60
- package/dist/index.d.ts +48 -15
- package/dist/index.js +283 -88
- package/dist/index.mjs +280 -85
- package/package.json +9 -8
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
import { AddressType } from "@phantom/client";
|
|
3
|
+
|
|
1
4
|
// src/providers/injected/index.ts
|
|
2
|
-
import { AddressType as
|
|
5
|
+
import { AddressType as AddressType4 } from "@phantom/client";
|
|
3
6
|
import { createPhantom, createExtensionPlugin } from "@phantom/browser-injected-sdk";
|
|
4
7
|
import { createSolanaPlugin } from "@phantom/browser-injected-sdk/solana";
|
|
5
8
|
import { createEthereumPlugin } from "@phantom/browser-injected-sdk/ethereum";
|
|
@@ -83,7 +86,7 @@ var DebugCategory = {
|
|
|
83
86
|
|
|
84
87
|
// src/providers/injected/chains/SolanaChain.ts
|
|
85
88
|
import { EventEmitter } from "eventemitter3";
|
|
86
|
-
import { AddressType } from "@phantom/client";
|
|
89
|
+
import { AddressType as AddressType2 } from "@phantom/client";
|
|
87
90
|
import { Buffer } from "buffer";
|
|
88
91
|
var InjectedSolanaChain = class {
|
|
89
92
|
constructor(phantom, callbacks) {
|
|
@@ -108,7 +111,7 @@ var InjectedSolanaChain = class {
|
|
|
108
111
|
return Promise.reject(new Error("Provider not connected. Call provider connect first."));
|
|
109
112
|
}
|
|
110
113
|
const addresses = this.callbacks.getAddresses();
|
|
111
|
-
const solanaAddress = addresses.find((addr) => addr.addressType ===
|
|
114
|
+
const solanaAddress = addresses.find((addr) => addr.addressType === AddressType2.solana);
|
|
112
115
|
if (!solanaAddress) {
|
|
113
116
|
return Promise.reject(new Error("Solana not enabled for this provider"));
|
|
114
117
|
}
|
|
@@ -127,17 +130,42 @@ var InjectedSolanaChain = class {
|
|
|
127
130
|
publicKey: this._publicKey || ""
|
|
128
131
|
};
|
|
129
132
|
}
|
|
130
|
-
signTransaction(
|
|
131
|
-
|
|
132
|
-
new Error("
|
|
133
|
-
|
|
133
|
+
async signTransaction(transaction) {
|
|
134
|
+
if (!this.callbacks.isConnected()) {
|
|
135
|
+
return Promise.reject(new Error("Provider not connected. Call provider connect first."));
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const result = await this.phantom.solana.signTransaction(transaction);
|
|
139
|
+
return result;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
return Promise.reject(error);
|
|
142
|
+
}
|
|
134
143
|
}
|
|
135
144
|
async signAndSendTransaction(transaction) {
|
|
136
145
|
const result = await this.phantom.solana.signAndSendTransaction(transaction);
|
|
137
146
|
return { signature: result.signature };
|
|
138
147
|
}
|
|
139
|
-
signAllTransactions(
|
|
140
|
-
|
|
148
|
+
async signAllTransactions(transactions) {
|
|
149
|
+
if (!this.callbacks.isConnected()) {
|
|
150
|
+
return Promise.reject(new Error("Provider not connected. Call provider connect first."));
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
const result = await this.phantom.solana.signAllTransactions(transactions);
|
|
154
|
+
return result;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return Promise.reject(error);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async signAndSendAllTransactions(transactions) {
|
|
160
|
+
if (!this.callbacks.isConnected()) {
|
|
161
|
+
return Promise.reject(new Error("Provider not connected. Call provider connect first."));
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const result = await this.phantom.solana.signAndSendAllTransactions(transactions);
|
|
165
|
+
return { signatures: result.signatures };
|
|
166
|
+
} catch (error) {
|
|
167
|
+
return Promise.reject(error);
|
|
168
|
+
}
|
|
141
169
|
}
|
|
142
170
|
switchNetwork(_network) {
|
|
143
171
|
return Promise.resolve();
|
|
@@ -163,7 +191,7 @@ var InjectedSolanaChain = class {
|
|
|
163
191
|
this.eventEmitter.emit("accountChanged", publicKey);
|
|
164
192
|
});
|
|
165
193
|
this.callbacks.on("connect", (data) => {
|
|
166
|
-
const solanaAddress = data.addresses?.find((addr) => addr.addressType ===
|
|
194
|
+
const solanaAddress = data.addresses?.find((addr) => addr.addressType === AddressType2.solana);
|
|
167
195
|
if (solanaAddress) {
|
|
168
196
|
this.updateConnectionState(true, solanaAddress.address);
|
|
169
197
|
}
|
|
@@ -174,7 +202,7 @@ var InjectedSolanaChain = class {
|
|
|
174
202
|
}
|
|
175
203
|
syncInitialState() {
|
|
176
204
|
if (this.callbacks.isConnected()) {
|
|
177
|
-
const solanaAddress = this.callbacks.getAddresses().find((addr) => addr.addressType ===
|
|
205
|
+
const solanaAddress = this.callbacks.getAddresses().find((addr) => addr.addressType === AddressType2.solana);
|
|
178
206
|
if (solanaAddress) {
|
|
179
207
|
this.updateConnectionState(true, solanaAddress.address);
|
|
180
208
|
}
|
|
@@ -195,7 +223,7 @@ var InjectedSolanaChain = class {
|
|
|
195
223
|
|
|
196
224
|
// src/providers/injected/chains/EthereumChain.ts
|
|
197
225
|
import { EventEmitter as EventEmitter2 } from "eventemitter3";
|
|
198
|
-
import { AddressType as
|
|
226
|
+
import { AddressType as AddressType3 } from "@phantom/client";
|
|
199
227
|
var InjectedEthereumChain = class {
|
|
200
228
|
constructor(phantom, callbacks) {
|
|
201
229
|
this._connected = false;
|
|
@@ -217,8 +245,13 @@ var InjectedEthereumChain = class {
|
|
|
217
245
|
get accounts() {
|
|
218
246
|
return this._accounts;
|
|
219
247
|
}
|
|
220
|
-
// EIP-1193 core method
|
|
248
|
+
// EIP-1193 core method with eth_signTransaction support
|
|
221
249
|
async request(args) {
|
|
250
|
+
if (args.method === "eth_signTransaction") {
|
|
251
|
+
const [transaction] = args.params;
|
|
252
|
+
const result = await this.signTransaction(transaction);
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
222
255
|
const provider = await this.phantom.ethereum.getProvider();
|
|
223
256
|
return await provider.request(args);
|
|
224
257
|
}
|
|
@@ -228,7 +261,7 @@ var InjectedEthereumChain = class {
|
|
|
228
261
|
return Promise.reject(new Error("Provider not connected. Call provider connect first."));
|
|
229
262
|
}
|
|
230
263
|
const addresses = this.callbacks.getAddresses();
|
|
231
|
-
const ethAddresses = addresses.filter((addr) => addr.addressType ===
|
|
264
|
+
const ethAddresses = addresses.filter((addr) => addr.addressType === AddressType3.ethereum).map((addr) => addr.address);
|
|
232
265
|
this.updateConnectionState(true, ethAddresses);
|
|
233
266
|
return Promise.resolve(ethAddresses);
|
|
234
267
|
}
|
|
@@ -242,6 +275,9 @@ var InjectedEthereumChain = class {
|
|
|
242
275
|
async signTypedData(typedData, address) {
|
|
243
276
|
return await this.phantom.ethereum.signTypedData(typedData, address);
|
|
244
277
|
}
|
|
278
|
+
async signTransaction(transaction) {
|
|
279
|
+
return await this.phantom.ethereum.signTransaction(transaction);
|
|
280
|
+
}
|
|
245
281
|
async sendTransaction(transaction) {
|
|
246
282
|
return await this.phantom.ethereum.sendTransaction(transaction);
|
|
247
283
|
}
|
|
@@ -280,7 +316,7 @@ var InjectedEthereumChain = class {
|
|
|
280
316
|
this.eventEmitter.emit("chainChanged", chainId);
|
|
281
317
|
});
|
|
282
318
|
this.callbacks.on("connect", (data) => {
|
|
283
|
-
const ethAddresses = data.addresses?.filter((addr) => addr.addressType ===
|
|
319
|
+
const ethAddresses = data.addresses?.filter((addr) => addr.addressType === AddressType3.ethereum)?.map((addr) => addr.address) || [];
|
|
284
320
|
if (ethAddresses.length > 0) {
|
|
285
321
|
this.updateConnectionState(true, ethAddresses);
|
|
286
322
|
}
|
|
@@ -291,7 +327,7 @@ var InjectedEthereumChain = class {
|
|
|
291
327
|
}
|
|
292
328
|
syncInitialState() {
|
|
293
329
|
if (this.callbacks.isConnected()) {
|
|
294
|
-
const ethAddresses = this.callbacks.getAddresses().filter((addr) => addr.addressType ===
|
|
330
|
+
const ethAddresses = this.callbacks.getAddresses().filter((addr) => addr.addressType === AddressType3.ethereum).map((addr) => addr.address);
|
|
295
331
|
if (ethAddresses.length > 0) {
|
|
296
332
|
this.updateConnectionState(true, ethAddresses);
|
|
297
333
|
}
|
|
@@ -323,11 +359,11 @@ var InjectedProvider = class {
|
|
|
323
359
|
this.addressTypes = config.addressTypes;
|
|
324
360
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Address types configured", { addressTypes: this.addressTypes });
|
|
325
361
|
const plugins = [createExtensionPlugin()];
|
|
326
|
-
if (this.addressTypes.includes(
|
|
362
|
+
if (this.addressTypes.includes(AddressType4.solana)) {
|
|
327
363
|
plugins.push(createSolanaPlugin());
|
|
328
364
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana plugin added");
|
|
329
365
|
}
|
|
330
|
-
if (this.addressTypes.includes(
|
|
366
|
+
if (this.addressTypes.includes(AddressType4.ethereum)) {
|
|
331
367
|
plugins.push(createEthereumPlugin());
|
|
332
368
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum plugin added");
|
|
333
369
|
}
|
|
@@ -338,10 +374,10 @@ var InjectedProvider = class {
|
|
|
338
374
|
});
|
|
339
375
|
this.phantom = createPhantom({ plugins });
|
|
340
376
|
const callbacks = this.createCallbacks();
|
|
341
|
-
if (this.addressTypes.includes(
|
|
377
|
+
if (this.addressTypes.includes(AddressType4.solana)) {
|
|
342
378
|
this._solanaChain = new InjectedSolanaChain(this.phantom, callbacks);
|
|
343
379
|
}
|
|
344
|
-
if (this.addressTypes.includes(
|
|
380
|
+
if (this.addressTypes.includes(AddressType4.ethereum)) {
|
|
345
381
|
this._ethereumChain = new InjectedEthereumChain(this.phantom, callbacks);
|
|
346
382
|
}
|
|
347
383
|
debug.info(DebugCategory.INJECTED_PROVIDER, "InjectedProvider initialized");
|
|
@@ -350,7 +386,7 @@ var InjectedProvider = class {
|
|
|
350
386
|
* Access to Solana chain operations
|
|
351
387
|
*/
|
|
352
388
|
get solana() {
|
|
353
|
-
if (!this.addressTypes.includes(
|
|
389
|
+
if (!this.addressTypes.includes(AddressType4.solana)) {
|
|
354
390
|
throw new Error("Solana not enabled for this provider");
|
|
355
391
|
}
|
|
356
392
|
if (!this._solanaChain) {
|
|
@@ -362,7 +398,7 @@ var InjectedProvider = class {
|
|
|
362
398
|
* Access to Ethereum chain operations
|
|
363
399
|
*/
|
|
364
400
|
get ethereum() {
|
|
365
|
-
if (!this.addressTypes.includes(
|
|
401
|
+
if (!this.addressTypes.includes(AddressType4.ethereum)) {
|
|
366
402
|
throw new Error("Ethereum not enabled for this provider");
|
|
367
403
|
}
|
|
368
404
|
if (!this._ethereumChain) {
|
|
@@ -392,13 +428,13 @@ var InjectedProvider = class {
|
|
|
392
428
|
}
|
|
393
429
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Phantom extension detected");
|
|
394
430
|
const connectedAddresses = [];
|
|
395
|
-
if (this.addressTypes.includes(
|
|
431
|
+
if (this.addressTypes.includes(AddressType4.solana)) {
|
|
396
432
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting Solana connection");
|
|
397
433
|
try {
|
|
398
434
|
const publicKey = await this.phantom.solana.connect();
|
|
399
435
|
if (publicKey) {
|
|
400
436
|
connectedAddresses.push({
|
|
401
|
-
addressType:
|
|
437
|
+
addressType: AddressType4.solana,
|
|
402
438
|
address: publicKey
|
|
403
439
|
});
|
|
404
440
|
debug.info(DebugCategory.INJECTED_PROVIDER, "Solana connected successfully", { address: publicKey });
|
|
@@ -407,13 +443,13 @@ var InjectedProvider = class {
|
|
|
407
443
|
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana", { error: err });
|
|
408
444
|
}
|
|
409
445
|
}
|
|
410
|
-
if (this.addressTypes.includes(
|
|
446
|
+
if (this.addressTypes.includes(AddressType4.ethereum)) {
|
|
411
447
|
try {
|
|
412
448
|
const accounts = await this.phantom.ethereum.connect();
|
|
413
449
|
if (accounts && accounts.length > 0) {
|
|
414
450
|
connectedAddresses.push(
|
|
415
451
|
...accounts.map((address) => ({
|
|
416
|
-
addressType:
|
|
452
|
+
addressType: AddressType4.ethereum,
|
|
417
453
|
address
|
|
418
454
|
}))
|
|
419
455
|
);
|
|
@@ -455,7 +491,7 @@ var InjectedProvider = class {
|
|
|
455
491
|
}
|
|
456
492
|
async disconnect() {
|
|
457
493
|
debug.info(DebugCategory.INJECTED_PROVIDER, "Starting injected provider disconnect");
|
|
458
|
-
if (this.addressTypes.includes(
|
|
494
|
+
if (this.addressTypes.includes(AddressType4.solana)) {
|
|
459
495
|
try {
|
|
460
496
|
await this.phantom.solana.disconnect();
|
|
461
497
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana disconnected successfully");
|
|
@@ -463,7 +499,7 @@ var InjectedProvider = class {
|
|
|
463
499
|
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to disconnect Solana", { error: err });
|
|
464
500
|
}
|
|
465
501
|
}
|
|
466
|
-
if (this.addressTypes.includes(
|
|
502
|
+
if (this.addressTypes.includes(AddressType4.ethereum)) {
|
|
467
503
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum disconnected (no-op)");
|
|
468
504
|
}
|
|
469
505
|
this.browserInjectedCleanupFunctions.forEach((cleanup) => cleanup());
|
|
@@ -475,6 +511,87 @@ var InjectedProvider = class {
|
|
|
475
511
|
});
|
|
476
512
|
debug.info(DebugCategory.INJECTED_PROVIDER, "Injected provider disconnected successfully");
|
|
477
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* Attempt auto-connection using onlyIfTrusted parameter
|
|
516
|
+
* This will only connect if the dApp is already trusted by the user
|
|
517
|
+
* Should be called after setting up event listeners
|
|
518
|
+
*/
|
|
519
|
+
async autoConnect() {
|
|
520
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting auto-connect with onlyIfTrusted=true");
|
|
521
|
+
this.emit("connect_start", {
|
|
522
|
+
source: "auto-connect",
|
|
523
|
+
providerType: "injected"
|
|
524
|
+
});
|
|
525
|
+
try {
|
|
526
|
+
if (!this.phantom.extension?.isInstalled?.()) {
|
|
527
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Phantom wallet extension not found for auto-connect");
|
|
528
|
+
this.emit("connect_error", {
|
|
529
|
+
error: "Phantom wallet not found",
|
|
530
|
+
source: "auto-connect"
|
|
531
|
+
});
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const connectedAddresses = [];
|
|
535
|
+
if (this.addressTypes.includes(AddressType4.solana)) {
|
|
536
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting Solana auto-connect");
|
|
537
|
+
try {
|
|
538
|
+
const publicKey = await this.phantom.solana.connect({ onlyIfTrusted: true });
|
|
539
|
+
if (publicKey) {
|
|
540
|
+
connectedAddresses.push({
|
|
541
|
+
addressType: AddressType4.solana,
|
|
542
|
+
address: publicKey
|
|
543
|
+
});
|
|
544
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Solana auto-connected successfully", { address: publicKey });
|
|
545
|
+
}
|
|
546
|
+
} catch (err) {
|
|
547
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana auto-connect failed (expected if not trusted)", { error: err });
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
if (this.addressTypes.includes(AddressType4.ethereum)) {
|
|
551
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting Ethereum auto-connect");
|
|
552
|
+
try {
|
|
553
|
+
const accounts = await this.phantom.ethereum.connect({ onlyIfTrusted: true });
|
|
554
|
+
if (accounts && accounts.length > 0) {
|
|
555
|
+
connectedAddresses.push(
|
|
556
|
+
...accounts.map((address) => ({
|
|
557
|
+
addressType: AddressType4.ethereum,
|
|
558
|
+
address
|
|
559
|
+
}))
|
|
560
|
+
);
|
|
561
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Ethereum auto-connected successfully", { addresses: accounts });
|
|
562
|
+
}
|
|
563
|
+
} catch (err) {
|
|
564
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum auto-connect failed (expected if not trusted)", { error: err });
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
if (connectedAddresses.length === 0) {
|
|
568
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Auto-connect failed: no trusted connections available");
|
|
569
|
+
this.emit("connect_error", {
|
|
570
|
+
error: "No trusted connections available",
|
|
571
|
+
source: "auto-connect"
|
|
572
|
+
});
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
this.addresses = connectedAddresses;
|
|
576
|
+
this.connected = true;
|
|
577
|
+
this.emit("connect", {
|
|
578
|
+
addresses: this.addresses,
|
|
579
|
+
source: "auto-connect"
|
|
580
|
+
});
|
|
581
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Auto-connect successful", {
|
|
582
|
+
addressCount: connectedAddresses.length,
|
|
583
|
+
addresses: connectedAddresses.map((addr) => ({ type: addr.addressType, address: addr.address.substring(0, 8) + "..." }))
|
|
584
|
+
});
|
|
585
|
+
} catch (error) {
|
|
586
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Auto-connect failed with error", {
|
|
587
|
+
error: error instanceof Error ? error.message : String(error)
|
|
588
|
+
});
|
|
589
|
+
this.emit("connect_error", {
|
|
590
|
+
error: error instanceof Error ? error.message : "Auto-connect failed",
|
|
591
|
+
source: "auto-connect"
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
}
|
|
478
595
|
getAddresses() {
|
|
479
596
|
return this.addresses;
|
|
480
597
|
}
|
|
@@ -538,10 +655,10 @@ var InjectedProvider = class {
|
|
|
538
655
|
}
|
|
539
656
|
setupBrowserInjectedEvents() {
|
|
540
657
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up browser-injected-sdk event listeners");
|
|
541
|
-
if (this.addressTypes.includes(
|
|
658
|
+
if (this.addressTypes.includes(AddressType4.solana)) {
|
|
542
659
|
this.setupSolanaEvents();
|
|
543
660
|
}
|
|
544
|
-
if (this.addressTypes.includes(
|
|
661
|
+
if (this.addressTypes.includes(AddressType4.ethereum)) {
|
|
545
662
|
this.setupEthereumEvents();
|
|
546
663
|
}
|
|
547
664
|
}
|
|
@@ -549,8 +666,8 @@ var InjectedProvider = class {
|
|
|
549
666
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up Solana event listeners");
|
|
550
667
|
const handleSolanaConnect = (publicKey) => {
|
|
551
668
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana connect event received", { publicKey });
|
|
552
|
-
const solanaAddress = { addressType:
|
|
553
|
-
if (!this.addresses.find((addr) => addr.addressType ===
|
|
669
|
+
const solanaAddress = { addressType: AddressType4.solana, address: publicKey };
|
|
670
|
+
if (!this.addresses.find((addr) => addr.addressType === AddressType4.solana)) {
|
|
554
671
|
this.addresses.push(solanaAddress);
|
|
555
672
|
}
|
|
556
673
|
this.connected = true;
|
|
@@ -561,19 +678,19 @@ var InjectedProvider = class {
|
|
|
561
678
|
};
|
|
562
679
|
const handleSolanaDisconnect = () => {
|
|
563
680
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana disconnect event received");
|
|
564
|
-
this.addresses = this.addresses.filter((addr) => addr.addressType !==
|
|
565
|
-
this.connected =
|
|
681
|
+
this.addresses = this.addresses.filter((addr) => addr.addressType !== AddressType4.solana);
|
|
682
|
+
this.connected = false;
|
|
566
683
|
this.emit("disconnect", {
|
|
567
684
|
source: "injected-extension"
|
|
568
685
|
});
|
|
569
686
|
};
|
|
570
687
|
const handleSolanaAccountChanged = (publicKey) => {
|
|
571
688
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana account changed event received", { publicKey });
|
|
572
|
-
const solanaIndex = this.addresses.findIndex((addr) => addr.addressType ===
|
|
689
|
+
const solanaIndex = this.addresses.findIndex((addr) => addr.addressType === AddressType4.solana);
|
|
573
690
|
if (solanaIndex >= 0) {
|
|
574
|
-
this.addresses[solanaIndex] = { addressType:
|
|
691
|
+
this.addresses[solanaIndex] = { addressType: AddressType4.solana, address: publicKey };
|
|
575
692
|
} else {
|
|
576
|
-
this.addresses.push({ addressType:
|
|
693
|
+
this.addresses.push({ addressType: AddressType4.solana, address: publicKey });
|
|
577
694
|
}
|
|
578
695
|
this.emit("connect", {
|
|
579
696
|
addresses: this.addresses,
|
|
@@ -582,21 +699,18 @@ var InjectedProvider = class {
|
|
|
582
699
|
};
|
|
583
700
|
const cleanupConnect = this.phantom.solana.addEventListener("connect", handleSolanaConnect);
|
|
584
701
|
const cleanupDisconnect = this.phantom.solana.addEventListener("disconnect", handleSolanaDisconnect);
|
|
585
|
-
const cleanupAccountChanged = this.phantom.solana.addEventListener(
|
|
586
|
-
"accountChanged",
|
|
587
|
-
handleSolanaAccountChanged
|
|
588
|
-
);
|
|
702
|
+
const cleanupAccountChanged = this.phantom.solana.addEventListener("accountChanged", handleSolanaAccountChanged);
|
|
589
703
|
this.browserInjectedCleanupFunctions.push(cleanupConnect, cleanupDisconnect, cleanupAccountChanged);
|
|
590
704
|
}
|
|
591
705
|
setupEthereumEvents() {
|
|
592
706
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up Ethereum event listeners");
|
|
593
707
|
const handleEthereumConnect = (accounts) => {
|
|
594
708
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum connect event received", { accounts });
|
|
595
|
-
this.addresses = this.addresses.filter((addr) => addr.addressType !==
|
|
709
|
+
this.addresses = this.addresses.filter((addr) => addr.addressType !== AddressType4.ethereum);
|
|
596
710
|
if (accounts && accounts.length > 0) {
|
|
597
711
|
this.addresses.push(
|
|
598
712
|
...accounts.map((address) => ({
|
|
599
|
-
addressType:
|
|
713
|
+
addressType: AddressType4.ethereum,
|
|
600
714
|
address
|
|
601
715
|
}))
|
|
602
716
|
);
|
|
@@ -609,27 +723,32 @@ var InjectedProvider = class {
|
|
|
609
723
|
};
|
|
610
724
|
const handleEthereumDisconnect = () => {
|
|
611
725
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum disconnect event received");
|
|
612
|
-
this.addresses = this.addresses.filter((addr) => addr.addressType !==
|
|
613
|
-
this.connected =
|
|
726
|
+
this.addresses = this.addresses.filter((addr) => addr.addressType !== AddressType4.ethereum);
|
|
727
|
+
this.connected = false;
|
|
614
728
|
this.emit("disconnect", {
|
|
615
729
|
source: "injected-extension"
|
|
616
730
|
});
|
|
617
731
|
};
|
|
618
732
|
const handleEthereumAccountsChanged = (accounts) => {
|
|
619
733
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum accounts changed event received", { accounts });
|
|
620
|
-
this.addresses = this.addresses.filter((addr) => addr.addressType !==
|
|
734
|
+
this.addresses = this.addresses.filter((addr) => addr.addressType !== AddressType4.ethereum);
|
|
621
735
|
if (accounts && accounts.length > 0) {
|
|
622
736
|
this.addresses.push(
|
|
623
737
|
...accounts.map((address) => ({
|
|
624
|
-
addressType:
|
|
738
|
+
addressType: AddressType4.ethereum,
|
|
625
739
|
address
|
|
626
740
|
}))
|
|
627
741
|
);
|
|
742
|
+
this.emit("connect", {
|
|
743
|
+
addresses: this.addresses,
|
|
744
|
+
source: "injected-extension-account-change"
|
|
745
|
+
});
|
|
746
|
+
} else {
|
|
747
|
+
this.connected = false;
|
|
748
|
+
this.emit("disconnect", {
|
|
749
|
+
source: "injected-extension-account-change"
|
|
750
|
+
});
|
|
628
751
|
}
|
|
629
|
-
this.emit("connect", {
|
|
630
|
-
addresses: this.addresses,
|
|
631
|
-
source: "injected-extension-account-change"
|
|
632
|
-
});
|
|
633
752
|
};
|
|
634
753
|
const cleanupConnect = this.phantom.ethereum.addEventListener("connect", handleEthereumConnect);
|
|
635
754
|
const cleanupDisconnect = this.phantom.ethereum.addEventListener("disconnect", handleEthereumDisconnect);
|
|
@@ -758,15 +877,19 @@ var BrowserURLParamsAccessor = class {
|
|
|
758
877
|
};
|
|
759
878
|
var browserUrlParamsAccessor = new BrowserURLParamsAccessor();
|
|
760
879
|
|
|
761
|
-
// src/constants.ts
|
|
762
|
-
var DEFAULT_AUTH_URL = "https://connect.phantom.app";
|
|
763
|
-
var DEFAULT_WALLET_API_URL = "https://api.phantom.app/v1/wallets";
|
|
764
|
-
|
|
765
880
|
// src/providers/embedded/adapters/auth.ts
|
|
881
|
+
import { DEFAULT_AUTH_URL } from "@phantom/constants";
|
|
766
882
|
var BrowserAuthProvider = class {
|
|
767
883
|
constructor(urlParamsAccessor) {
|
|
768
884
|
this.urlParamsAccessor = urlParamsAccessor;
|
|
769
885
|
}
|
|
886
|
+
getValidatedCurrentUrl() {
|
|
887
|
+
const currentUrl = window.location.href;
|
|
888
|
+
if (!currentUrl.startsWith("http:") && !currentUrl.startsWith("https:")) {
|
|
889
|
+
throw new Error("Invalid URL protocol - only HTTP/HTTPS URLs are supported");
|
|
890
|
+
}
|
|
891
|
+
return currentUrl;
|
|
892
|
+
}
|
|
770
893
|
authenticate(options) {
|
|
771
894
|
return new Promise((resolve) => {
|
|
772
895
|
if ("jwtToken" in options) {
|
|
@@ -775,7 +898,6 @@ var BrowserAuthProvider = class {
|
|
|
775
898
|
const phantomOptions = options;
|
|
776
899
|
debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Starting Phantom Connect authentication", {
|
|
777
900
|
organizationId: phantomOptions.organizationId,
|
|
778
|
-
parentOrganizationId: phantomOptions.parentOrganizationId,
|
|
779
901
|
appId: phantomOptions.appId,
|
|
780
902
|
provider: phantomOptions.provider,
|
|
781
903
|
authUrl: phantomOptions.authUrl,
|
|
@@ -785,11 +907,11 @@ var BrowserAuthProvider = class {
|
|
|
785
907
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Using auth URL", { baseUrl });
|
|
786
908
|
const params = new URLSearchParams({
|
|
787
909
|
organization_id: phantomOptions.organizationId,
|
|
788
|
-
parent_organization_id: phantomOptions.parentOrganizationId,
|
|
789
910
|
app_id: phantomOptions.appId,
|
|
790
|
-
redirect_uri: phantomOptions.redirectUrl || (typeof window !== "undefined" ?
|
|
911
|
+
redirect_uri: phantomOptions.redirectUrl || (typeof window !== "undefined" ? this.getValidatedCurrentUrl() : ""),
|
|
791
912
|
session_id: phantomOptions.sessionId,
|
|
792
|
-
clear_previous_session: true.toString()
|
|
913
|
+
clear_previous_session: true.toString(),
|
|
914
|
+
sdk_version: "1.0.0-beta.10"
|
|
793
915
|
});
|
|
794
916
|
if (phantomOptions.provider) {
|
|
795
917
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Provider specified, will skip selection", {
|
|
@@ -806,7 +928,6 @@ var BrowserAuthProvider = class {
|
|
|
806
928
|
}
|
|
807
929
|
const authContext = {
|
|
808
930
|
organizationId: phantomOptions.organizationId,
|
|
809
|
-
parentOrganizationId: phantomOptions.parentOrganizationId,
|
|
810
931
|
appId: phantomOptions.appId,
|
|
811
932
|
provider: phantomOptions.provider,
|
|
812
933
|
sessionId: phantomOptions.sessionId
|
|
@@ -815,6 +936,9 @@ var BrowserAuthProvider = class {
|
|
|
815
936
|
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Stored auth context in session storage", { authContext });
|
|
816
937
|
const authUrl = `${baseUrl}?${params.toString()}`;
|
|
817
938
|
debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Redirecting to Phantom Connect", { authUrl });
|
|
939
|
+
if (!authUrl.startsWith("https:")) {
|
|
940
|
+
throw new Error("Invalid auth URL - only HTTPS URLs are allowed for authentication");
|
|
941
|
+
}
|
|
818
942
|
window.location.href = authUrl;
|
|
819
943
|
resolve();
|
|
820
944
|
});
|
|
@@ -903,7 +1027,7 @@ function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
|
|
|
903
1027
|
let name = "unknown";
|
|
904
1028
|
let version = "unknown";
|
|
905
1029
|
if (!userAgent || typeof userAgent !== "string") {
|
|
906
|
-
return { name, version };
|
|
1030
|
+
return { name, version, userAgent: "unknown" };
|
|
907
1031
|
}
|
|
908
1032
|
try {
|
|
909
1033
|
if (userAgent.includes("Edg/")) {
|
|
@@ -985,11 +1109,11 @@ function parseBrowserFromUserAgent(userAgent, hasBraveAPI) {
|
|
|
985
1109
|
}
|
|
986
1110
|
} catch (error) {
|
|
987
1111
|
}
|
|
988
|
-
return { name, version };
|
|
1112
|
+
return { name, version, userAgent };
|
|
989
1113
|
}
|
|
990
1114
|
function detectBrowser() {
|
|
991
1115
|
if (typeof window === "undefined" || !window.navigator?.userAgent) {
|
|
992
|
-
return { name: "unknown", version: "unknown" };
|
|
1116
|
+
return { name: "unknown", version: "unknown", userAgent: "unknown" };
|
|
993
1117
|
}
|
|
994
1118
|
const userAgent = window.navigator.userAgent;
|
|
995
1119
|
const hasBraveAPI = !!navigator.brave;
|
|
@@ -1004,25 +1128,70 @@ function getBrowserDisplayName() {
|
|
|
1004
1128
|
const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
1005
1129
|
return version !== "unknown" ? `${capitalizedName} ${version}` : capitalizedName;
|
|
1006
1130
|
}
|
|
1131
|
+
function isMobileDevice() {
|
|
1132
|
+
if (typeof window === "undefined" || !window.navigator?.userAgent) {
|
|
1133
|
+
return false;
|
|
1134
|
+
}
|
|
1135
|
+
const userAgent = window.navigator.userAgent.toLowerCase();
|
|
1136
|
+
const mobilePatterns = [
|
|
1137
|
+
/android/,
|
|
1138
|
+
/iphone|ipad|ipod/,
|
|
1139
|
+
/blackberry/,
|
|
1140
|
+
/windows phone/,
|
|
1141
|
+
/mobile/,
|
|
1142
|
+
/tablet/,
|
|
1143
|
+
/silk/,
|
|
1144
|
+
/kindle/,
|
|
1145
|
+
/opera mini/,
|
|
1146
|
+
/opera mobi/
|
|
1147
|
+
];
|
|
1148
|
+
const isMobileUA = mobilePatterns.some((pattern) => pattern.test(userAgent));
|
|
1149
|
+
let isSmallScreen = false;
|
|
1150
|
+
try {
|
|
1151
|
+
isSmallScreen = window.screen.width <= 768 || window.screen.height <= 768;
|
|
1152
|
+
} catch (error) {
|
|
1153
|
+
isSmallScreen = false;
|
|
1154
|
+
}
|
|
1155
|
+
let isTouchDevice = false;
|
|
1156
|
+
try {
|
|
1157
|
+
isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
1158
|
+
} catch (error) {
|
|
1159
|
+
isTouchDevice = false;
|
|
1160
|
+
}
|
|
1161
|
+
return isMobileUA || isSmallScreen && isTouchDevice;
|
|
1162
|
+
}
|
|
1007
1163
|
|
|
1008
1164
|
// src/providers/embedded/index.ts
|
|
1165
|
+
import { ANALYTICS_HEADERS } from "@phantom/constants";
|
|
1009
1166
|
var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
1010
1167
|
constructor(config) {
|
|
1011
1168
|
debug.log(DebugCategory.EMBEDDED_PROVIDER, "Initializing Browser EmbeddedProvider", { config });
|
|
1012
1169
|
const urlParamsAccessor = new BrowserURLParamsAccessor();
|
|
1013
1170
|
const stamper = new IndexedDbStamper({
|
|
1014
|
-
dbName: `phantom-embedded-sdk-${config.
|
|
1171
|
+
dbName: `phantom-embedded-sdk-${config.appId}`,
|
|
1015
1172
|
storeName: "crypto-keys",
|
|
1016
1173
|
keyName: "signing-key"
|
|
1017
1174
|
});
|
|
1018
1175
|
const platformName = getPlatformName();
|
|
1176
|
+
const { name: browserName, version } = detectBrowser();
|
|
1019
1177
|
const platform = {
|
|
1020
1178
|
storage: new BrowserStorage(),
|
|
1021
1179
|
authProvider: new BrowserAuthProvider(urlParamsAccessor),
|
|
1022
1180
|
urlParamsAccessor,
|
|
1023
1181
|
stamper,
|
|
1024
|
-
name: platformName
|
|
1182
|
+
name: platformName,
|
|
1025
1183
|
// Use detected browser name and version for identification
|
|
1184
|
+
analyticsHeaders: {
|
|
1185
|
+
[ANALYTICS_HEADERS.SDK_TYPE]: "browser",
|
|
1186
|
+
[ANALYTICS_HEADERS.PLATFORM]: browserName,
|
|
1187
|
+
// firefox, chrome, safari, etc.
|
|
1188
|
+
[ANALYTICS_HEADERS.PLATFORM_VERSION]: version,
|
|
1189
|
+
// Full user agent for more detailed info
|
|
1190
|
+
[ANALYTICS_HEADERS.APP_ID]: config.appId,
|
|
1191
|
+
[ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
|
|
1192
|
+
[ANALYTICS_HEADERS.SDK_VERSION]: "1.0.0-beta.10"
|
|
1193
|
+
// Replaced at build time
|
|
1194
|
+
}
|
|
1026
1195
|
};
|
|
1027
1196
|
debug.log(DebugCategory.EMBEDDED_PROVIDER, "Detected platform", { platformName });
|
|
1028
1197
|
const logger = new BrowserLogger();
|
|
@@ -1032,6 +1201,7 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
|
1032
1201
|
};
|
|
1033
1202
|
|
|
1034
1203
|
// src/ProviderManager.ts
|
|
1204
|
+
import { DEFAULT_WALLET_API_URL, DEFAULT_EMBEDDED_WALLET_TYPE, DEFAULT_AUTH_URL as DEFAULT_AUTH_URL2 } from "@phantom/constants";
|
|
1035
1205
|
var ProviderManager = class {
|
|
1036
1206
|
// Track which providers have forwarding set up
|
|
1037
1207
|
constructor(config) {
|
|
@@ -1050,6 +1220,15 @@ var ProviderManager = class {
|
|
|
1050
1220
|
currentProviderKey: this.currentProviderKey
|
|
1051
1221
|
});
|
|
1052
1222
|
}
|
|
1223
|
+
getValidatedCurrentUrl() {
|
|
1224
|
+
if (typeof window === "undefined")
|
|
1225
|
+
return "";
|
|
1226
|
+
const currentUrl = window.location.href;
|
|
1227
|
+
if (!currentUrl.startsWith("http:") && !currentUrl.startsWith("https:")) {
|
|
1228
|
+
throw new Error("Invalid URL protocol - only HTTP/HTTPS URLs are supported");
|
|
1229
|
+
}
|
|
1230
|
+
return currentUrl;
|
|
1231
|
+
}
|
|
1053
1232
|
/**
|
|
1054
1233
|
* Switch to a different provider type
|
|
1055
1234
|
*/
|
|
@@ -1081,7 +1260,8 @@ var ProviderManager = class {
|
|
|
1081
1260
|
getCurrentProviderInfo() {
|
|
1082
1261
|
if (!this.currentProviderKey)
|
|
1083
1262
|
return null;
|
|
1084
|
-
const
|
|
1263
|
+
const parts = this.currentProviderKey.split("-");
|
|
1264
|
+
const [type, embeddedWalletType] = parts;
|
|
1085
1265
|
return {
|
|
1086
1266
|
type,
|
|
1087
1267
|
embeddedWalletType
|
|
@@ -1227,7 +1407,7 @@ var ProviderManager = class {
|
|
|
1227
1407
|
*/
|
|
1228
1408
|
setDefaultProvider() {
|
|
1229
1409
|
const defaultType = this.config.providerType || "embedded";
|
|
1230
|
-
const defaultEmbeddedType = this.config.embeddedWalletType || "
|
|
1410
|
+
const defaultEmbeddedType = this.config.embeddedWalletType || "user-wallet";
|
|
1231
1411
|
this.createProvider(defaultType, defaultEmbeddedType);
|
|
1232
1412
|
this.switchProvider(defaultType, { embeddedWalletType: defaultEmbeddedType });
|
|
1233
1413
|
}
|
|
@@ -1241,22 +1421,27 @@ var ProviderManager = class {
|
|
|
1241
1421
|
let provider;
|
|
1242
1422
|
if (type === "injected") {
|
|
1243
1423
|
provider = new InjectedProvider({
|
|
1244
|
-
|
|
1245
|
-
addressTypes: this.config.addressTypes
|
|
1424
|
+
addressTypes: this.config.addressTypes || [AddressType.solana]
|
|
1246
1425
|
});
|
|
1247
|
-
} else {
|
|
1248
|
-
if (!this.config.
|
|
1249
|
-
throw new Error("
|
|
1426
|
+
} else if (type === "embedded") {
|
|
1427
|
+
if (!this.config.appId) {
|
|
1428
|
+
throw new Error("appId is required for embedded provider");
|
|
1250
1429
|
}
|
|
1430
|
+
const apiBaseUrl = this.config.apiBaseUrl || DEFAULT_WALLET_API_URL;
|
|
1431
|
+
const authUrl = this.config.authOptions?.authUrl || DEFAULT_AUTH_URL2;
|
|
1251
1432
|
provider = new EmbeddedProvider({
|
|
1252
|
-
apiBaseUrl
|
|
1253
|
-
organizationId: this.config.appId,
|
|
1433
|
+
apiBaseUrl,
|
|
1254
1434
|
appId: this.config.appId,
|
|
1255
|
-
authOptions:
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1435
|
+
authOptions: {
|
|
1436
|
+
...this.config.authOptions || {},
|
|
1437
|
+
authUrl,
|
|
1438
|
+
redirectUrl: this.config.authOptions?.redirectUrl || this.getValidatedCurrentUrl()
|
|
1439
|
+
},
|
|
1440
|
+
embeddedWalletType: embeddedWalletType || DEFAULT_EMBEDDED_WALLET_TYPE,
|
|
1441
|
+
addressTypes: this.config.addressTypes || [AddressType.solana]
|
|
1259
1442
|
});
|
|
1443
|
+
} else {
|
|
1444
|
+
throw new Error(`Unsupported provider type: ${type}`);
|
|
1260
1445
|
}
|
|
1261
1446
|
this.providers.set(key, provider);
|
|
1262
1447
|
}
|
|
@@ -1266,8 +1451,10 @@ var ProviderManager = class {
|
|
|
1266
1451
|
getProviderKey(type, embeddedWalletType) {
|
|
1267
1452
|
if (type === "injected") {
|
|
1268
1453
|
return "injected";
|
|
1454
|
+
} else if (type === "embedded") {
|
|
1455
|
+
return `embedded-${embeddedWalletType || "app-wallet"}`;
|
|
1269
1456
|
}
|
|
1270
|
-
|
|
1457
|
+
throw new Error(`Unsupported provider type: ${type}`);
|
|
1271
1458
|
}
|
|
1272
1459
|
/**
|
|
1273
1460
|
* Save provider preference to localStorage
|
|
@@ -1328,6 +1515,7 @@ async function waitForPhantomExtension(timeoutMs = 3e3) {
|
|
|
1328
1515
|
}
|
|
1329
1516
|
|
|
1330
1517
|
// src/BrowserSDK.ts
|
|
1518
|
+
import { DEFAULT_EMBEDDED_WALLET_TYPE as DEFAULT_EMBEDDED_WALLET_TYPE2 } from "@phantom/constants";
|
|
1331
1519
|
var BrowserSDK = class {
|
|
1332
1520
|
constructor(config) {
|
|
1333
1521
|
debug.info(DebugCategory.BROWSER_SDK, "Initializing BrowserSDK", {
|
|
@@ -1339,7 +1527,7 @@ var BrowserSDK = class {
|
|
|
1339
1527
|
debug.error(DebugCategory.BROWSER_SDK, "Invalid providerType", { providerType: config.providerType });
|
|
1340
1528
|
throw new Error(`Invalid providerType: ${config.providerType}. Must be "injected" or "embedded".`);
|
|
1341
1529
|
}
|
|
1342
|
-
const embeddedWalletType = config.embeddedWalletType ||
|
|
1530
|
+
const embeddedWalletType = config.embeddedWalletType || DEFAULT_EMBEDDED_WALLET_TYPE2;
|
|
1343
1531
|
if (config.providerType === "embedded" && !["app-wallet", "user-wallet"].includes(embeddedWalletType)) {
|
|
1344
1532
|
debug.error(DebugCategory.BROWSER_SDK, "Invalid embeddedWalletType", {
|
|
1345
1533
|
embeddedWalletType: config.embeddedWalletType
|
|
@@ -1475,7 +1663,7 @@ var BrowserSDK = class {
|
|
|
1475
1663
|
async autoConnect() {
|
|
1476
1664
|
debug.log(DebugCategory.BROWSER_SDK, "Attempting auto-connect");
|
|
1477
1665
|
const currentProvider = this.providerManager.getCurrentProvider();
|
|
1478
|
-
if (currentProvider
|
|
1666
|
+
if (currentProvider) {
|
|
1479
1667
|
await currentProvider.autoConnect();
|
|
1480
1668
|
} else {
|
|
1481
1669
|
debug.warn(DebugCategory.BROWSER_SDK, "Current provider does not support auto-connect", {
|
|
@@ -1624,8 +1812,15 @@ var BrowserSDK = class {
|
|
|
1624
1812
|
}
|
|
1625
1813
|
};
|
|
1626
1814
|
|
|
1627
|
-
// src/
|
|
1628
|
-
|
|
1815
|
+
// src/utils/deeplink.ts
|
|
1816
|
+
function getDeeplinkToPhantom(ref) {
|
|
1817
|
+
if (!window.location.href.startsWith("http:") && !window.location.href.startsWith("https:")) {
|
|
1818
|
+
throw new Error("Invalid URL protocol - only HTTP/HTTPS URLs are supported for deeplinks");
|
|
1819
|
+
}
|
|
1820
|
+
const currentUrl = encodeURIComponent(window.location.href);
|
|
1821
|
+
const refParam = ref ? `?ref=${encodeURIComponent(ref)}` : "";
|
|
1822
|
+
return `https://phantom.app/ul/browse/${currentUrl}${refParam}`;
|
|
1823
|
+
}
|
|
1629
1824
|
|
|
1630
1825
|
// src/index.ts
|
|
1631
1826
|
import { NetworkId } from "@phantom/constants";
|
|
@@ -1633,15 +1828,15 @@ import { AddressType as AddressType5 } from "@phantom/client";
|
|
|
1633
1828
|
export {
|
|
1634
1829
|
AddressType5 as AddressType,
|
|
1635
1830
|
BrowserSDK,
|
|
1636
|
-
DEFAULT_AUTH_URL,
|
|
1637
|
-
DEFAULT_WALLET_API_URL,
|
|
1638
1831
|
DebugCategory,
|
|
1639
1832
|
DebugLevel,
|
|
1640
1833
|
NetworkId,
|
|
1641
1834
|
debug,
|
|
1642
1835
|
detectBrowser,
|
|
1643
1836
|
getBrowserDisplayName,
|
|
1837
|
+
getDeeplinkToPhantom,
|
|
1644
1838
|
getPlatformName,
|
|
1839
|
+
isMobileDevice,
|
|
1645
1840
|
parseBrowserFromUserAgent,
|
|
1646
1841
|
waitForPhantomExtension
|
|
1647
1842
|
};
|