oauth-callback 1.2.4 → 2.0.0

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/dist/index.js CHANGED
@@ -26,604 +26,6 @@ var __export = (target, all) => {
26
26
  };
27
27
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
28
28
 
29
- // node_modules/open/index.js
30
- import process7 from "node:process";
31
- import path from "node:path";
32
- import { fileURLToPath } from "node:url";
33
- import childProcess3 from "node:child_process";
34
- import fs5, { constants as fsConstants2 } from "node:fs/promises";
35
-
36
- // node_modules/wsl-utils/index.js
37
- import { promisify as promisify2 } from "node:util";
38
- import childProcess2 from "node:child_process";
39
- import fs4, { constants as fsConstants } from "node:fs/promises";
40
-
41
- // node_modules/is-wsl/index.js
42
- import process from "node:process";
43
- import os from "node:os";
44
- import fs3 from "node:fs";
45
-
46
- // node_modules/is-inside-container/index.js
47
- import fs2 from "node:fs";
48
-
49
- // node_modules/is-docker/index.js
50
- import fs from "node:fs";
51
- var isDockerCached;
52
- function hasDockerEnv() {
53
- try {
54
- fs.statSync("/.dockerenv");
55
- return true;
56
- } catch {
57
- return false;
58
- }
59
- }
60
- function hasDockerCGroup() {
61
- try {
62
- return fs.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
63
- } catch {
64
- return false;
65
- }
66
- }
67
- function isDocker() {
68
- if (isDockerCached === undefined) {
69
- isDockerCached = hasDockerEnv() || hasDockerCGroup();
70
- }
71
- return isDockerCached;
72
- }
73
-
74
- // node_modules/is-inside-container/index.js
75
- var cachedResult;
76
- var hasContainerEnv = () => {
77
- try {
78
- fs2.statSync("/run/.containerenv");
79
- return true;
80
- } catch {
81
- return false;
82
- }
83
- };
84
- function isInsideContainer() {
85
- if (cachedResult === undefined) {
86
- cachedResult = hasContainerEnv() || isDocker();
87
- }
88
- return cachedResult;
89
- }
90
-
91
- // node_modules/is-wsl/index.js
92
- var isWsl = () => {
93
- if (process.platform !== "linux") {
94
- return false;
95
- }
96
- if (os.release().toLowerCase().includes("microsoft")) {
97
- if (isInsideContainer()) {
98
- return false;
99
- }
100
- return true;
101
- }
102
- try {
103
- return fs3.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
104
- } catch {
105
- return false;
106
- }
107
- };
108
- var is_wsl_default = process.env.__IS_WSL_TEST__ ? isWsl : isWsl();
109
-
110
- // node_modules/powershell-utils/index.js
111
- import process2 from "node:process";
112
- import { Buffer } from "node:buffer";
113
- import { promisify } from "node:util";
114
- import childProcess from "node:child_process";
115
- var execFile = promisify(childProcess.execFile);
116
- var powerShellPath = () => `${process2.env.SYSTEMROOT || process2.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
117
- var executePowerShell = async (command, options = {}) => {
118
- const {
119
- powerShellPath: psPath,
120
- ...execFileOptions
121
- } = options;
122
- const encodedCommand = executePowerShell.encodeCommand(command);
123
- return execFile(psPath ?? powerShellPath(), [
124
- ...executePowerShell.argumentsPrefix,
125
- encodedCommand
126
- ], {
127
- encoding: "utf8",
128
- ...execFileOptions
129
- });
130
- };
131
- executePowerShell.argumentsPrefix = [
132
- "-NoProfile",
133
- "-NonInteractive",
134
- "-ExecutionPolicy",
135
- "Bypass",
136
- "-EncodedCommand"
137
- ];
138
- executePowerShell.encodeCommand = (command) => Buffer.from(command, "utf16le").toString("base64");
139
- executePowerShell.escapeArgument = (value) => `'${String(value).replaceAll("'", "''")}'`;
140
-
141
- // node_modules/wsl-utils/utilities.js
142
- function parseMountPointFromConfig(content) {
143
- for (const line of content.split(`
144
- `)) {
145
- if (/^\s*#/.test(line)) {
146
- continue;
147
- }
148
- const match = /^\s*root\s*=\s*(?<mountPoint>"[^"]*"|'[^']*'|[^#]*)/.exec(line);
149
- if (!match) {
150
- continue;
151
- }
152
- return match.groups.mountPoint.trim().replaceAll(/^["']|["']$/g, "");
153
- }
154
- }
155
-
156
- // node_modules/wsl-utils/index.js
157
- var execFile2 = promisify2(childProcess2.execFile);
158
- var wslDrivesMountPoint = (() => {
159
- const defaultMountPoint = "/mnt/";
160
- let mountPoint;
161
- return async function() {
162
- if (mountPoint) {
163
- return mountPoint;
164
- }
165
- const configFilePath = "/etc/wsl.conf";
166
- let isConfigFileExists = false;
167
- try {
168
- await fs4.access(configFilePath, fsConstants.F_OK);
169
- isConfigFileExists = true;
170
- } catch {}
171
- if (!isConfigFileExists) {
172
- return defaultMountPoint;
173
- }
174
- const configContent = await fs4.readFile(configFilePath, { encoding: "utf8" });
175
- const parsedMountPoint = parseMountPointFromConfig(configContent);
176
- if (parsedMountPoint === undefined) {
177
- return defaultMountPoint;
178
- }
179
- mountPoint = parsedMountPoint;
180
- mountPoint = mountPoint.endsWith("/") ? mountPoint : `${mountPoint}/`;
181
- return mountPoint;
182
- };
183
- })();
184
- var powerShellPathFromWsl = async () => {
185
- const mountPoint = await wslDrivesMountPoint();
186
- return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
187
- };
188
- var powerShellPath2 = is_wsl_default ? powerShellPathFromWsl : powerShellPath;
189
- var canAccessPowerShellPromise;
190
- var canAccessPowerShell = async () => {
191
- canAccessPowerShellPromise ??= (async () => {
192
- try {
193
- const psPath = await powerShellPath2();
194
- await fs4.access(psPath, fsConstants.X_OK);
195
- return true;
196
- } catch {
197
- return false;
198
- }
199
- })();
200
- return canAccessPowerShellPromise;
201
- };
202
- var wslDefaultBrowser = async () => {
203
- const psPath = await powerShellPath2();
204
- const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
205
- const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
206
- return stdout.trim();
207
- };
208
- var convertWslPathToWindows = async (path) => {
209
- if (/^[a-z]+:\/\//i.test(path)) {
210
- return path;
211
- }
212
- try {
213
- const { stdout } = await execFile2("wslpath", ["-aw", path], { encoding: "utf8" });
214
- return stdout.trim();
215
- } catch {
216
- return path;
217
- }
218
- };
219
-
220
- // node_modules/define-lazy-prop/index.js
221
- function defineLazyProperty(object, propertyName, valueGetter) {
222
- const define = (value) => Object.defineProperty(object, propertyName, { value, enumerable: true, writable: true });
223
- Object.defineProperty(object, propertyName, {
224
- configurable: true,
225
- enumerable: true,
226
- get() {
227
- const result = valueGetter();
228
- define(result);
229
- return result;
230
- },
231
- set(value) {
232
- define(value);
233
- }
234
- });
235
- return object;
236
- }
237
-
238
- // node_modules/default-browser/index.js
239
- import { promisify as promisify6 } from "node:util";
240
- import process5 from "node:process";
241
- import { execFile as execFile6 } from "node:child_process";
242
-
243
- // node_modules/default-browser-id/index.js
244
- import { promisify as promisify3 } from "node:util";
245
- import process3 from "node:process";
246
- import { execFile as execFile3 } from "node:child_process";
247
- var execFileAsync = promisify3(execFile3);
248
- async function defaultBrowserId() {
249
- if (process3.platform !== "darwin") {
250
- throw new Error("macOS only");
251
- }
252
- const { stdout } = await execFileAsync("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
253
- const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
254
- return match?.groups.id ?? "com.apple.Safari";
255
- }
256
-
257
- // node_modules/run-applescript/index.js
258
- import process4 from "node:process";
259
- import { promisify as promisify4 } from "node:util";
260
- import { execFile as execFile4, execFileSync } from "node:child_process";
261
- var execFileAsync2 = promisify4(execFile4);
262
- async function runAppleScript(script, { humanReadableOutput = true } = {}) {
263
- if (process4.platform !== "darwin") {
264
- throw new Error("macOS only");
265
- }
266
- const outputArguments = humanReadableOutput ? [] : ["-ss"];
267
- const { stdout } = await execFileAsync2("osascript", ["-e", script, outputArguments]);
268
- return stdout.trim();
269
- }
270
-
271
- // node_modules/bundle-name/index.js
272
- async function bundleName(bundleId) {
273
- return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string
274
- tell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
275
- }
276
-
277
- // node_modules/default-browser/windows.js
278
- import { promisify as promisify5 } from "node:util";
279
- import { execFile as execFile5 } from "node:child_process";
280
- var execFileAsync3 = promisify5(execFile5);
281
- var windowsBrowserProgIds = {
282
- MSEdgeHTM: { name: "Edge", id: "com.microsoft.edge" },
283
- MSEdgeBHTML: { name: "Edge Beta", id: "com.microsoft.edge.beta" },
284
- MSEdgeDHTML: { name: "Edge Dev", id: "com.microsoft.edge.dev" },
285
- AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
286
- ChromeHTML: { name: "Chrome", id: "com.google.chrome" },
287
- ChromeBHTML: { name: "Chrome Beta", id: "com.google.chrome.beta" },
288
- ChromeDHTML: { name: "Chrome Dev", id: "com.google.chrome.dev" },
289
- ChromiumHTM: { name: "Chromium", id: "org.chromium.Chromium" },
290
- BraveHTML: { name: "Brave", id: "com.brave.Browser" },
291
- BraveBHTML: { name: "Brave Beta", id: "com.brave.Browser.beta" },
292
- BraveDHTML: { name: "Brave Dev", id: "com.brave.Browser.dev" },
293
- BraveSSHTM: { name: "Brave Nightly", id: "com.brave.Browser.nightly" },
294
- FirefoxURL: { name: "Firefox", id: "org.mozilla.firefox" },
295
- OperaStable: { name: "Opera", id: "com.operasoftware.Opera" },
296
- VivaldiHTM: { name: "Vivaldi", id: "com.vivaldi.Vivaldi" },
297
- "IE.HTTP": { name: "Internet Explorer", id: "com.microsoft.ie" }
298
- };
299
- var _windowsBrowserProgIdMap = new Map(Object.entries(windowsBrowserProgIds));
300
-
301
- class UnknownBrowserError extends Error {
302
- }
303
- async function defaultBrowser(_execFileAsync = execFileAsync3) {
304
- const { stdout } = await _execFileAsync("reg", [
305
- "QUERY",
306
- " HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
307
- "/v",
308
- "ProgId"
309
- ]);
310
- const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
311
- if (!match) {
312
- throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
313
- }
314
- const { id } = match.groups;
315
- const browser = windowsBrowserProgIds[id];
316
- if (!browser) {
317
- throw new UnknownBrowserError(`Unknown browser ID: ${id}`);
318
- }
319
- return browser;
320
- }
321
-
322
- // node_modules/default-browser/index.js
323
- var execFileAsync4 = promisify6(execFile6);
324
- var titleize = (string) => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
325
- async function defaultBrowser2() {
326
- if (process5.platform === "darwin") {
327
- const id = await defaultBrowserId();
328
- const name = await bundleName(id);
329
- return { name, id };
330
- }
331
- if (process5.platform === "linux") {
332
- const { stdout } = await execFileAsync4("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
333
- const id = stdout.trim();
334
- const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
335
- return { name, id };
336
- }
337
- if (process5.platform === "win32") {
338
- return defaultBrowser();
339
- }
340
- throw new Error("Only macOS, Linux, and Windows are supported");
341
- }
342
-
343
- // node_modules/is-in-ssh/index.js
344
- import process6 from "node:process";
345
- var isInSsh = Boolean(process6.env.SSH_CONNECTION || process6.env.SSH_CLIENT || process6.env.SSH_TTY);
346
- var is_in_ssh_default = isInSsh;
347
-
348
- // node_modules/open/index.js
349
- var fallbackAttemptSymbol = Symbol("fallbackAttempt");
350
- var __dirname2 = import.meta.url ? path.dirname(fileURLToPath(import.meta.url)) : "";
351
- var localXdgOpenPath = path.join(__dirname2, "xdg-open");
352
- var { platform, arch } = process7;
353
- var tryEachApp = async (apps, opener) => {
354
- if (apps.length === 0) {
355
- return;
356
- }
357
- const errors = [];
358
- for (const app of apps) {
359
- try {
360
- return await opener(app);
361
- } catch (error) {
362
- errors.push(error);
363
- }
364
- }
365
- throw new AggregateError(errors, "Failed to open in all supported apps");
366
- };
367
- var baseOpen = async (options) => {
368
- options = {
369
- wait: false,
370
- background: false,
371
- newInstance: false,
372
- allowNonzeroExitCode: false,
373
- ...options
374
- };
375
- const isFallbackAttempt = options[fallbackAttemptSymbol] === true;
376
- delete options[fallbackAttemptSymbol];
377
- if (Array.isArray(options.app)) {
378
- return tryEachApp(options.app, (singleApp) => baseOpen({
379
- ...options,
380
- app: singleApp,
381
- [fallbackAttemptSymbol]: true
382
- }));
383
- }
384
- let { name: app, arguments: appArguments = [] } = options.app ?? {};
385
- appArguments = [...appArguments];
386
- if (Array.isArray(app)) {
387
- return tryEachApp(app, (appName) => baseOpen({
388
- ...options,
389
- app: {
390
- name: appName,
391
- arguments: appArguments
392
- },
393
- [fallbackAttemptSymbol]: true
394
- }));
395
- }
396
- if (app === "browser" || app === "browserPrivate") {
397
- const ids = {
398
- "com.google.chrome": "chrome",
399
- "google-chrome.desktop": "chrome",
400
- "com.brave.browser": "brave",
401
- "org.mozilla.firefox": "firefox",
402
- "firefox.desktop": "firefox",
403
- "com.microsoft.msedge": "edge",
404
- "com.microsoft.edge": "edge",
405
- "com.microsoft.edgemac": "edge",
406
- "microsoft-edge.desktop": "edge",
407
- "com.apple.safari": "safari"
408
- };
409
- const flags = {
410
- chrome: "--incognito",
411
- brave: "--incognito",
412
- firefox: "--private-window",
413
- edge: "--inPrivate"
414
- };
415
- let browser;
416
- if (is_wsl_default) {
417
- const progId = await wslDefaultBrowser();
418
- const browserInfo = _windowsBrowserProgIdMap.get(progId);
419
- browser = browserInfo ?? {};
420
- } else {
421
- browser = await defaultBrowser2();
422
- }
423
- if (browser.id in ids) {
424
- const browserName = ids[browser.id.toLowerCase()];
425
- if (app === "browserPrivate") {
426
- if (browserName === "safari") {
427
- throw new Error("Safari doesn't support opening in private mode via command line");
428
- }
429
- appArguments.push(flags[browserName]);
430
- }
431
- return baseOpen({
432
- ...options,
433
- app: {
434
- name: apps[browserName],
435
- arguments: appArguments
436
- }
437
- });
438
- }
439
- throw new Error(`${browser.name} is not supported as a default browser`);
440
- }
441
- let command;
442
- const cliArguments = [];
443
- const childProcessOptions = {};
444
- let shouldUseWindowsInWsl = false;
445
- if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
446
- shouldUseWindowsInWsl = await canAccessPowerShell();
447
- }
448
- if (platform === "darwin") {
449
- command = "open";
450
- if (options.wait) {
451
- cliArguments.push("--wait-apps");
452
- }
453
- if (options.background) {
454
- cliArguments.push("--background");
455
- }
456
- if (options.newInstance) {
457
- cliArguments.push("--new");
458
- }
459
- if (app) {
460
- cliArguments.push("-a", app);
461
- }
462
- } else if (platform === "win32" || shouldUseWindowsInWsl) {
463
- command = await powerShellPath2();
464
- cliArguments.push(...executePowerShell.argumentsPrefix);
465
- if (!is_wsl_default) {
466
- childProcessOptions.windowsVerbatimArguments = true;
467
- }
468
- if (is_wsl_default && options.target) {
469
- options.target = await convertWslPathToWindows(options.target);
470
- }
471
- const encodedArguments = ["$ProgressPreference = 'SilentlyContinue';", "Start"];
472
- if (options.wait) {
473
- encodedArguments.push("-Wait");
474
- }
475
- if (app) {
476
- encodedArguments.push(executePowerShell.escapeArgument(app));
477
- if (options.target) {
478
- appArguments.push(options.target);
479
- }
480
- } else if (options.target) {
481
- encodedArguments.push(executePowerShell.escapeArgument(options.target));
482
- }
483
- if (appArguments.length > 0) {
484
- appArguments = appArguments.map((argument) => executePowerShell.escapeArgument(argument));
485
- encodedArguments.push("-ArgumentList", appArguments.join(","));
486
- }
487
- options.target = executePowerShell.encodeCommand(encodedArguments.join(" "));
488
- if (!options.wait) {
489
- childProcessOptions.stdio = "ignore";
490
- }
491
- } else {
492
- if (app) {
493
- command = app;
494
- } else {
495
- const isBundled = !__dirname2 || __dirname2 === "/";
496
- let exeLocalXdgOpen = false;
497
- try {
498
- await fs5.access(localXdgOpenPath, fsConstants2.X_OK);
499
- exeLocalXdgOpen = true;
500
- } catch {}
501
- const useSystemXdgOpen = process7.versions.electron ?? (platform === "android" || isBundled || !exeLocalXdgOpen);
502
- command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
503
- }
504
- if (appArguments.length > 0) {
505
- cliArguments.push(...appArguments);
506
- }
507
- if (!options.wait) {
508
- childProcessOptions.stdio = "ignore";
509
- childProcessOptions.detached = true;
510
- }
511
- }
512
- if (platform === "darwin" && appArguments.length > 0) {
513
- cliArguments.push("--args", ...appArguments);
514
- }
515
- if (options.target) {
516
- cliArguments.push(options.target);
517
- }
518
- const subprocess = childProcess3.spawn(command, cliArguments, childProcessOptions);
519
- if (options.wait) {
520
- return new Promise((resolve, reject) => {
521
- subprocess.once("error", reject);
522
- subprocess.once("close", (exitCode) => {
523
- if (!options.allowNonzeroExitCode && exitCode !== 0) {
524
- reject(new Error(`Exited with code ${exitCode}`));
525
- return;
526
- }
527
- resolve(subprocess);
528
- });
529
- });
530
- }
531
- if (isFallbackAttempt) {
532
- return new Promise((resolve, reject) => {
533
- subprocess.once("error", reject);
534
- subprocess.once("spawn", () => {
535
- subprocess.once("close", (exitCode) => {
536
- subprocess.off("error", reject);
537
- if (exitCode !== 0) {
538
- reject(new Error(`Exited with code ${exitCode}`));
539
- return;
540
- }
541
- subprocess.unref();
542
- resolve(subprocess);
543
- });
544
- });
545
- });
546
- }
547
- subprocess.unref();
548
- return new Promise((resolve, reject) => {
549
- subprocess.once("error", reject);
550
- subprocess.once("spawn", () => {
551
- subprocess.off("error", reject);
552
- resolve(subprocess);
553
- });
554
- });
555
- };
556
- var open = (target, options) => {
557
- if (typeof target !== "string") {
558
- throw new TypeError("Expected a `target`");
559
- }
560
- return baseOpen({
561
- ...options,
562
- target
563
- });
564
- };
565
- function detectArchBinary(binary) {
566
- if (typeof binary === "string" || Array.isArray(binary)) {
567
- return binary;
568
- }
569
- const { [arch]: archBinary } = binary;
570
- if (!archBinary) {
571
- throw new Error(`${arch} is not supported`);
572
- }
573
- return archBinary;
574
- }
575
- function detectPlatformBinary({ [platform]: platformBinary }, { wsl } = {}) {
576
- if (wsl && is_wsl_default) {
577
- return detectArchBinary(wsl);
578
- }
579
- if (!platformBinary) {
580
- throw new Error(`${platform} is not supported`);
581
- }
582
- return detectArchBinary(platformBinary);
583
- }
584
- var apps = {
585
- browser: "browser",
586
- browserPrivate: "browserPrivate"
587
- };
588
- defineLazyProperty(apps, "chrome", () => detectPlatformBinary({
589
- darwin: "google chrome",
590
- win32: "chrome",
591
- linux: ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]
592
- }, {
593
- wsl: {
594
- ia32: "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
595
- x64: ["/mnt/c/Program Files/Google/Chrome/Application/chrome.exe", "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"]
596
- }
597
- }));
598
- defineLazyProperty(apps, "brave", () => detectPlatformBinary({
599
- darwin: "brave browser",
600
- win32: "brave",
601
- linux: ["brave-browser", "brave"]
602
- }, {
603
- wsl: {
604
- ia32: "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe",
605
- x64: ["/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe", "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe"]
606
- }
607
- }));
608
- defineLazyProperty(apps, "firefox", () => detectPlatformBinary({
609
- darwin: "firefox",
610
- win32: String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,
611
- linux: "firefox"
612
- }, {
613
- wsl: "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
614
- }));
615
- defineLazyProperty(apps, "edge", () => detectPlatformBinary({
616
- darwin: "microsoft edge",
617
- win32: "msedge",
618
- linux: ["microsoft-edge", "microsoft-edge-dev"]
619
- }, {
620
- wsl: "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
621
- }));
622
- defineLazyProperty(apps, "safari", () => detectPlatformBinary({
623
- darwin: "Safari"
624
- }));
625
- var open_default = open;
626
-
627
29
  // src/errors.ts
628
30
  class OAuthError extends Error {
629
31
  error;
@@ -638,6 +40,13 @@ class OAuthError extends Error {
638
40
  }
639
41
  }
640
42
 
43
+ class TimeoutError extends Error {
44
+ constructor(message = "OAuth callback timed out") {
45
+ super(message);
46
+ this.name = "TimeoutError";
47
+ }
48
+ }
49
+
641
50
  // src/templates.ts
642
51
  var successTemplate = `<!doctype html><html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Authorization Successful</title><style>:root{--system-font:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;--mono-font:ui-monospace,SFMono-Regular,"SF Mono",Consolas,"Liberation Mono",Menlo,monospace}@media (prefers-color-scheme:dark){:root{color-scheme:dark}}.minimal-body{margin:0;padding:0;min-height:100vh;display:flex;align-items:center;justify-content:center;font-family:var(--system-font);background:linear-gradient(180deg,#fafafa 0%,#f0f0f0 100%);color:#1a1a1a;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.dark .minimal-body{background:linear-gradient(180deg,#0a0a0a 0%,#1a1a1a 100%);color:#fafafa}.minimal-card{background:white;border-radius:16px;padding:48px 40px 40px;max-width:420px;width:90%;text-align:center;box-shadow:0 1px 3px rgba(0,0,0,0.04),0 6px 16px rgba(0,0,0,0.08);animation:fadeUp 0.4s ease-out}.dark .minimal-card{background:#1f1f1f;box-shadow:0 1px 3px rgba(0,0,0,0.2),0 8px 24px rgba(0,0,0,0.4)}.minimal-title{font-size:24px;font-weight:600;margin:0 0 8px;letter-spacing:-0.02em;line-height:1.2}.minimal-subtitle{font-size:15px;color:#666;margin:0 0 32px;line-height:1.5}.dark .minimal-subtitle{color:#999}.success-icon-container{width:56px;height:56px;margin:0 auto 24px}.checkmark{width:56px;height:56px}.checkmark-circle{stroke:#10b981;stroke-width:2;stroke-miterlimit:10;stroke-dasharray:157;stroke-dashoffset:157;fill:none}.checkmark-check{stroke:#10b981;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:48;stroke-dashoffset:48;fill:none}.checkmark.animate .checkmark-circle{animation:stroke 0.6s cubic-bezier(0.65,0,0.45,1) forwards}.checkmark.animate .checkmark-check{animation:stroke 0.3s cubic-bezier(0.65,0,0.45,1) 0.4s forwards}@keyframes stroke{100%{stroke-dashoffset:0}}.error-icon-container{width:56px;height:56px;margin:0 auto 24px;color:#ef4444}.error-icon-container.animate svg{animation:errorPulse 0.5s ease-out,shake 0.4s ease-out 0.3s}.error-icon-container.animate svg circle{stroke-dasharray:63;stroke-dashoffset:63;animation:errorStroke 0.5s cubic-bezier(0.65,0,0.45,1) forwards}.error-icon-container.animate svg path{stroke-dasharray:20;stroke-dashoffset:20;animation:errorStroke 0.4s cubic-bezier(0.65,0,0.45,1) 0.3s forwards}@keyframes errorPulse{0%{transform:scale(0);opacity:0}50%{transform:scale(1.1)}100%{transform:scale(1);opacity:1}}@keyframes errorStroke{to{stroke-dashoffset:0}}@keyframes shake{0%,100%{transform:translateX(0) scale(1)}25%{transform:translateX(-4px) scale(1)}75%{transform:translateX(4px) scale(1)}}.countdown-container{margin-top:32px;padding-top:24px;border-top:1px solid #e5e5e5}.dark .countdown-container{border-top-color:#333}.countdown-label{font-size:13px;color:#666;margin-bottom:12px;font-weight:500;text-transform:uppercase;letter-spacing:0.05em}.dark .countdown-label{color:#999}#countdown-text{font-family:var(--mono-font);font-weight:600;color:#1a1a1a}.dark #countdown-text{color:#fafafa}.progress-track{width:100%;height:3px;background:#e5e5e5;border-radius:3px;overflow:hidden}.dark .progress-track{background:#333}.progress-bar{height:100%;background:linear-gradient(90deg,#10b981 0%,#059669 100%);border-radius:3px;transition:width 100ms linear;width:0}.minimal-actions{display:flex;gap:12px;margin-top:24px;padding-top:24px;border-top:1px solid #e5e5e5}.dark .minimal-actions{border-top-color:#333}.minimal-button{flex:1;padding:10px 20px;border-radius:8px;font-size:14px;font-weight:500;border:none;cursor:pointer;transition:all 0.2s ease;font-family:var(--system-font);letter-spacing:-0.01em}.minimal-button:active{transform:scale(0.98)}.minimal-button.primary{background:#1a1a1a;color:white}.minimal-button.primary:hover{background:#000}.dark .minimal-button.primary{background:#fafafa;color:#1a1a1a}.dark .minimal-button.primary:hover{background:#fff}.minimal-button.secondary{background:#f5f5f5;color:#666}.minimal-button.secondary:hover{background:#e8e8e8}.dark .minimal-button.secondary{background:#2a2a2a;color:#999}.dark .minimal-button.secondary:hover{background:#333}.minimal-help{font-size:12px;color:#999;margin-top:16px;line-height:1.5}.dark .minimal-help{color:#666}.minimal-error-details{background:#fef2f2;border:1px solid #fecaca;border-radius:8px;padding:12px 16px;margin:20px 0;text-align:left;font-size:13px;line-height:1.6}.dark .minimal-error-details{background:rgba(239,68,68,0.1);border-color:rgba(239,68,68,0.3)}.minimal-error-details strong{color:#dc2626;display:block;margin-bottom:4px;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:0.05em}.dark .minimal-error-details strong{color:#ef4444}.minimal-error-details code{font-family:var(--mono-font);background:rgba(0,0,0,0.05);padding:2px 6px;border-radius:4px;font-size:12px}.dark .minimal-error-details code{background:rgba(255,255,255,0.1)}@keyframes fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){*{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important}}.minimal-button:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}@media (prefers-contrast:high){.minimal-card{border:2px solid currentColor}.minimal-button{border:1px solid currentColor}}</style> <script> if (window.matchMedia("(prefers-color-scheme: dark)").matches) { document.documentElement.classList.add("dark"); } window.addEventListener("DOMContentLoaded", () => { const checkmark = document.getElementById("checkmark"); if (checkmark) { setTimeout(() => checkmark.classList.add("animate"), 100); } });</script> </head><body class="minimal-body"><div class="minimal-card"><div class="success-icon-container"><svg id="checkmark" class="checkmark" viewBox="0 0 52 52"><circle class="checkmark-circle" cx="26" cy="26" r="25" fill="none" /><path class="checkmark-check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8" /></svg></div><h1 class="minimal-title">Authorization Successful</h1><p class="minimal-subtitle">You can now return to your application</p></div></body></html>`;
643
52
  var errorTemplate = `<!doctype html><html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>{{ERROR_TITLE}}</title><style>:root{--system-font:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;--mono-font:ui-monospace,SFMono-Regular,"SF Mono",Consolas,"Liberation Mono",Menlo,monospace}@media (prefers-color-scheme:dark){:root{color-scheme:dark}}.minimal-body{margin:0;padding:0;min-height:100vh;display:flex;align-items:center;justify-content:center;font-family:var(--system-font);background:linear-gradient(180deg,#fafafa 0%,#f0f0f0 100%);color:#1a1a1a;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.dark .minimal-body{background:linear-gradient(180deg,#0a0a0a 0%,#1a1a1a 100%);color:#fafafa}.minimal-card{background:white;border-radius:16px;padding:48px 40px 40px;max-width:420px;width:90%;text-align:center;box-shadow:0 1px 3px rgba(0,0,0,0.04),0 6px 16px rgba(0,0,0,0.08);animation:fadeUp 0.4s ease-out}.dark .minimal-card{background:#1f1f1f;box-shadow:0 1px 3px rgba(0,0,0,0.2),0 8px 24px rgba(0,0,0,0.4)}.minimal-title{font-size:24px;font-weight:600;margin:0 0 8px;letter-spacing:-0.02em;line-height:1.2}.minimal-subtitle{font-size:15px;color:#666;margin:0 0 32px;line-height:1.5}.dark .minimal-subtitle{color:#999}.success-icon-container{width:56px;height:56px;margin:0 auto 24px}.checkmark{width:56px;height:56px}.checkmark-circle{stroke:#10b981;stroke-width:2;stroke-miterlimit:10;stroke-dasharray:157;stroke-dashoffset:157;fill:none}.checkmark-check{stroke:#10b981;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:48;stroke-dashoffset:48;fill:none}.checkmark.animate .checkmark-circle{animation:stroke 0.6s cubic-bezier(0.65,0,0.45,1) forwards}.checkmark.animate .checkmark-check{animation:stroke 0.3s cubic-bezier(0.65,0,0.45,1) 0.4s forwards}@keyframes stroke{100%{stroke-dashoffset:0}}.error-icon-container{width:56px;height:56px;margin:0 auto 24px;color:#ef4444}.error-icon-container.animate svg{animation:errorPulse 0.5s ease-out,shake 0.4s ease-out 0.3s}.error-icon-container.animate svg circle{stroke-dasharray:63;stroke-dashoffset:63;animation:errorStroke 0.5s cubic-bezier(0.65,0,0.45,1) forwards}.error-icon-container.animate svg path{stroke-dasharray:20;stroke-dashoffset:20;animation:errorStroke 0.4s cubic-bezier(0.65,0,0.45,1) 0.3s forwards}@keyframes errorPulse{0%{transform:scale(0);opacity:0}50%{transform:scale(1.1)}100%{transform:scale(1);opacity:1}}@keyframes errorStroke{to{stroke-dashoffset:0}}@keyframes shake{0%,100%{transform:translateX(0) scale(1)}25%{transform:translateX(-4px) scale(1)}75%{transform:translateX(4px) scale(1)}}.countdown-container{margin-top:32px;padding-top:24px;border-top:1px solid #e5e5e5}.dark .countdown-container{border-top-color:#333}.countdown-label{font-size:13px;color:#666;margin-bottom:12px;font-weight:500;text-transform:uppercase;letter-spacing:0.05em}.dark .countdown-label{color:#999}#countdown-text{font-family:var(--mono-font);font-weight:600;color:#1a1a1a}.dark #countdown-text{color:#fafafa}.progress-track{width:100%;height:3px;background:#e5e5e5;border-radius:3px;overflow:hidden}.dark .progress-track{background:#333}.progress-bar{height:100%;background:linear-gradient(90deg,#10b981 0%,#059669 100%);border-radius:3px;transition:width 100ms linear;width:0}.minimal-actions{display:flex;gap:12px;margin-top:24px;padding-top:24px;border-top:1px solid #e5e5e5}.dark .minimal-actions{border-top-color:#333}.minimal-button{flex:1;padding:10px 20px;border-radius:8px;font-size:14px;font-weight:500;border:none;cursor:pointer;transition:all 0.2s ease;font-family:var(--system-font);letter-spacing:-0.01em}.minimal-button:active{transform:scale(0.98)}.minimal-button.primary{background:#1a1a1a;color:white}.minimal-button.primary:hover{background:#000}.dark .minimal-button.primary{background:#fafafa;color:#1a1a1a}.dark .minimal-button.primary:hover{background:#fff}.minimal-button.secondary{background:#f5f5f5;color:#666}.minimal-button.secondary:hover{background:#e8e8e8}.dark .minimal-button.secondary{background:#2a2a2a;color:#999}.dark .minimal-button.secondary:hover{background:#333}.minimal-help{font-size:12px;color:#999;margin-top:16px;line-height:1.5}.dark .minimal-help{color:#666}.minimal-error-details{background:#fef2f2;border:1px solid #fecaca;border-radius:8px;padding:12px 16px;margin:20px 0;text-align:left;font-size:13px;line-height:1.6}.dark .minimal-error-details{background:rgba(239,68,68,0.1);border-color:rgba(239,68,68,0.3)}.minimal-error-details strong{color:#dc2626;display:block;margin-bottom:4px;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:0.05em}.dark .minimal-error-details strong{color:#ef4444}.minimal-error-details code{font-family:var(--mono-font);background:rgba(0,0,0,0.05);padding:2px 6px;border-radius:4px;font-size:12px}.dark .minimal-error-details code{background:rgba(255,255,255,0.1)}@keyframes fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){*{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important}}.minimal-button:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}@media (prefers-contrast:high){.minimal-card{border:2px solid currentColor}.minimal-button{border:1px solid currentColor}}</style> <script> if (window.matchMedia("(prefers-color-scheme: dark)").matches) { document.documentElement.classList.add("dark"); } window.addEventListener("DOMContentLoaded", () => { const errorIcon = document.getElementById("error-icon"); if (errorIcon) { setTimeout(() => errorIcon.classList.add("animate"), 100); } }); document.addEventListener("keydown", (e) => { if (e.key === "Escape") { window.close(); } else if (e.key === "Enter") { window.location.reload(); } });</script> </head><body class="minimal-body"><div class="minimal-card"><div class="error-icon-container" id="error-icon">{{ERROR_SVG_ICON}}</div><h1 class="minimal-title">{{ERROR_TITLE}}</h1><p class="minimal-subtitle">{{ERROR_MESSAGE}}</p> {{ERROR_DETAILS}} <div class="minimal-actions"><button onclick="window.location.reload()" class="minimal-button primary" > Try Again </button><button onclick="window.close()" class="minimal-button secondary"> Close </button></div><p class="minimal-help">{{HELP_TEXT}}</p></div></body></html>`;
@@ -718,6 +127,7 @@ class BaseCallbackServer {
718
127
  successHtml;
719
128
  errorHtml;
720
129
  onRequest;
130
+ callbackReceived = false;
721
131
  abortHandler;
722
132
  signal;
723
133
  setup(options) {
@@ -743,27 +153,34 @@ class BaseCallbackServer {
743
153
  for (const [key, value] of url.searchParams)
744
154
  params[key] = value;
745
155
  listener.resolve(params);
156
+ this.callbackReceived = true;
746
157
  return new Response(generateCallbackHTML(params, this.successHtml, this.errorHtml), {
747
158
  status: 200,
748
159
  headers: { "Content-Type": "text/html" }
749
160
  });
750
161
  }
751
- async waitForCallback(path2, timeout) {
752
- if (this.callbackListeners.has(path2))
753
- return Promise.reject(new Error(`A listener for the path "${path2}" is already active.`));
162
+ async waitForCallback(path, timeout) {
163
+ if (!path)
164
+ throw new Error("Callback path is required");
165
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
166
+ if (this.callbackListeners.has(normalizedPath))
167
+ return Promise.reject(new Error(`A listener for the path "${normalizedPath}" is already active.`));
168
+ let timeoutId;
754
169
  try {
755
170
  return await Promise.race([
756
171
  new Promise((resolve, reject) => {
757
- this.callbackListeners.set(path2, { resolve, reject });
172
+ this.callbackListeners.set(normalizedPath, { resolve, reject });
758
173
  }),
759
174
  new Promise((_, reject) => {
760
- setTimeout(() => {
761
- reject(new Error(`OAuth callback timeout after ${timeout}ms waiting for ${path2}`));
175
+ timeoutId = setTimeout(() => {
176
+ reject(new TimeoutError(`OAuth callback timeout after ${timeout}ms waiting for ${normalizedPath}`));
762
177
  }, timeout);
763
178
  })
764
179
  ]);
765
180
  } finally {
766
- this.callbackListeners.delete(path2);
181
+ if (timeoutId)
182
+ clearTimeout(timeoutId);
183
+ this.callbackListeners.delete(normalizedPath);
767
184
  }
768
185
  }
769
186
  async stop() {
@@ -792,13 +209,10 @@ class BunCallbackServer extends BaseCallbackServer {
792
209
  async stopServer() {
793
210
  if (!this.server)
794
211
  return;
795
- if (this.server.pendingRequests > 0) {
796
- while (this.server.pendingRequests > 0) {
797
- await new Promise((resolve) => setTimeout(resolve, 10));
798
- }
799
- await new Promise((resolve) => setTimeout(resolve, 100));
212
+ if (this.callbackReceived) {
213
+ await new Promise((resolve) => setTimeout(resolve, 50));
800
214
  }
801
- this.server.stop();
215
+ this.server.stop(true);
802
216
  this.server = undefined;
803
217
  }
804
218
  }
@@ -809,7 +223,6 @@ class DenoCallbackServer extends BaseCallbackServer {
809
223
  this.setup(options);
810
224
  const { port, hostname = "localhost" } = options;
811
225
  this.abortController = new AbortController;
812
- options.signal?.addEventListener("abort", () => this.abortController?.abort());
813
226
  Deno.serve({ port, hostname, signal: this.abortController.signal }, (request) => this.handleRequest(request));
814
227
  }
815
228
  async stopServer() {
@@ -831,16 +244,15 @@ class NodeCallbackServer extends BaseCallbackServer {
831
244
  try {
832
245
  const request = this.nodeToWebRequest(req, port, hostname);
833
246
  const response = this.handleRequest(request);
247
+ res.shouldKeepAlive = false;
834
248
  res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
835
249
  const body = await response.text();
836
250
  res.end(body);
837
- } catch (error) {
251
+ } catch {
838
252
  res.writeHead(500);
839
253
  res.end("Internal Server Error");
840
254
  }
841
255
  });
842
- if (options.signal)
843
- options.signal.addEventListener("abort", () => this.server?.close());
844
256
  this.server.listen(port, hostname, () => resolve());
845
257
  this.server.on("error", reject);
846
258
  });
@@ -848,7 +260,6 @@ class NodeCallbackServer extends BaseCallbackServer {
848
260
  async stopServer() {
849
261
  if (!this.server)
850
262
  return;
851
- this.server.closeAllConnections();
852
263
  return new Promise((resolve) => {
853
264
  this.server?.close(() => {
854
265
  this.server = undefined;
@@ -898,17 +309,17 @@ function inMemoryStore() {
898
309
  };
899
310
  }
900
311
  // src/storage/file.ts
901
- import * as fs6 from "node:fs/promises";
902
- import * as path2 from "node:path";
903
- import * as os2 from "node:os";
312
+ import * as fs from "node:fs/promises";
313
+ import * as path from "node:path";
314
+ import * as os from "node:os";
904
315
  function fileStore(filepath) {
905
- const file = filepath ?? path2.join(os2.homedir(), ".mcp", "tokens.json");
316
+ const file = filepath ?? path.join(os.homedir(), ".mcp", "tokens.json");
906
317
  async function ensureDir() {
907
- await fs6.mkdir(path2.dirname(file), { recursive: true });
318
+ await fs.mkdir(path.dirname(file), { recursive: true });
908
319
  }
909
320
  async function readStore() {
910
321
  try {
911
- const data = await fs6.readFile(file, "utf-8");
322
+ const data = await fs.readFile(file, "utf-8");
912
323
  return JSON.parse(data);
913
324
  } catch {
914
325
  return {};
@@ -916,7 +327,7 @@ function fileStore(filepath) {
916
327
  }
917
328
  async function writeStore(data) {
918
329
  await ensureDir();
919
- await fs6.writeFile(file, JSON.stringify(data, null, 2), "utf-8");
330
+ await fs.writeFile(file, JSON.stringify(data, null, 2), "utf-8");
920
331
  }
921
332
  return {
922
333
  async get(key) {
@@ -968,8 +379,7 @@ class BrowserOAuthProvider {
968
379
  _hostname;
969
380
  _callbackPath;
970
381
  _authTimeout;
971
- _usePKCE;
972
- _openBrowser;
382
+ _launch;
973
383
  _clientId;
974
384
  _clientSecret;
975
385
  _scope;
@@ -985,7 +395,6 @@ class BrowserOAuthProvider {
985
395
  _tokensLoaded = false;
986
396
  _loadingTokens;
987
397
  _authInProgress;
988
- _refreshInProgress;
989
398
  constructor(options = {}) {
990
399
  this._store = options.store ?? inMemoryStore();
991
400
  this._storeKey = options.storeKey ?? "mcp-tokens";
@@ -993,8 +402,7 @@ class BrowserOAuthProvider {
993
402
  this._hostname = options.hostname ?? "localhost";
994
403
  this._callbackPath = options.callbackPath ?? "/callback";
995
404
  this._authTimeout = options.authTimeout ?? 300000;
996
- this._usePKCE = options.usePKCE ?? true;
997
- this._openBrowser = options.openBrowser ?? true;
405
+ this._launch = options.launch;
998
406
  this._clientId = options.clientId;
999
407
  this._clientSecret = options.clientSecret;
1000
408
  this._scope = options.scope;
@@ -1024,7 +432,7 @@ class BrowserOAuthProvider {
1024
432
  }
1025
433
  if (this._isOAuthStore(this._store)) {
1026
434
  const clientInfo = await this._store.getClient(this._storeKey);
1027
- if (clientInfo) {
435
+ if (clientInfo?.clientId) {
1028
436
  this._clientInfo = {
1029
437
  client_id: clientInfo.clientId,
1030
438
  client_secret: clientInfo.clientSecret,
@@ -1034,18 +442,17 @@ class BrowserOAuthProvider {
1034
442
  };
1035
443
  }
1036
444
  const session = await this._store.getSession(this._storeKey);
1037
- if (session) {
445
+ if (session?.codeVerifier) {
1038
446
  this._codeVerifier = session.codeVerifier;
1039
447
  }
1040
448
  }
1041
449
  this._tokensLoaded = true;
1042
- } catch (error) {
1043
- console.warn("Failed to load stored data:", error);
450
+ } catch {
1044
451
  this._tokensLoaded = true;
1045
452
  }
1046
453
  }
1047
454
  _isOAuthStore(store) {
1048
- return typeof store.getClient === "function";
455
+ return typeof store.getClient === "function" && typeof store.setClient === "function" && typeof store.getSession === "function";
1049
456
  }
1050
457
  get redirectUrl() {
1051
458
  return `http://${this._hostname}:${this._port}${this._callbackPath}`;
@@ -1098,19 +505,8 @@ class BrowserOAuthProvider {
1098
505
  return;
1099
506
  }
1100
507
  const stored = await this._store.get(this._storeKey);
1101
- if (stored?.expiresAt) {
1102
- if (Date.now() >= stored.expiresAt - 60000) {
1103
- if (this._tokens.refresh_token) {
1104
- try {
1105
- await this._refreshTokens();
1106
- return this._tokens;
1107
- } catch (error) {
1108
- console.warn("Token refresh failed:", error);
1109
- return;
1110
- }
1111
- }
1112
- return;
1113
- }
508
+ if (stored?.expiresAt && Date.now() >= stored.expiresAt - 60000) {
509
+ return;
1114
510
  }
1115
511
  return this._tokens;
1116
512
  }
@@ -1138,42 +534,25 @@ class BrowserOAuthProvider {
1138
534
  }
1139
535
  }
1140
536
  async _doAuthorization(authorizationUrl) {
1141
- let lastError;
1142
- const maxRetries = 2;
1143
- for (let attempt = 0;attempt <= maxRetries; attempt++) {
1144
- try {
1145
- const result = await getAuthCode({
1146
- authorizationUrl: authorizationUrl.href,
1147
- port: this._port,
1148
- hostname: this._hostname,
1149
- callbackPath: this._callbackPath,
1150
- timeout: this._authTimeout,
1151
- openBrowser: typeof this._openBrowser === "boolean" ? this._openBrowser : true,
1152
- successHtml: this._successHtml,
1153
- errorHtml: this._errorHtml,
1154
- onRequest: this._onRequest
1155
- });
1156
- this._pendingAuthCode = result.code;
1157
- this._pendingAuthState = result.state;
1158
- setTimeout(() => {
1159
- if (this._pendingAuthCode === result.code) {
1160
- this._pendingAuthCode = undefined;
1161
- this._pendingAuthState = undefined;
1162
- }
1163
- }, this._authTimeout);
1164
- return;
1165
- } catch (error) {
1166
- lastError = error instanceof Error ? error : new Error(String(error));
1167
- if (error instanceof OAuthError) {
1168
- throw error;
1169
- }
1170
- if (attempt < maxRetries) {
1171
- console.warn(`Auth attempt ${attempt + 1} failed, retrying...`, error);
1172
- await new Promise((resolve) => setTimeout(resolve, 1000 * (attempt + 1)));
1173
- }
537
+ const result = await getAuthCode({
538
+ authorizationUrl: authorizationUrl.href,
539
+ port: this._port,
540
+ hostname: this._hostname,
541
+ callbackPath: this._callbackPath,
542
+ timeout: this._authTimeout,
543
+ launch: this._launch,
544
+ successHtml: this._successHtml,
545
+ errorHtml: this._errorHtml,
546
+ onRequest: this._onRequest
547
+ });
548
+ this._pendingAuthCode = result.code;
549
+ this._pendingAuthState = result.state;
550
+ setTimeout(() => {
551
+ if (this._pendingAuthCode === result.code) {
552
+ this._pendingAuthCode = undefined;
553
+ this._pendingAuthState = undefined;
1174
554
  }
1175
- }
1176
- throw new Error(`OAuth authorization failed after ${maxRetries + 1} attempts: ${lastError?.message}`);
555
+ }, this._authTimeout);
1177
556
  }
1178
557
  async saveCodeVerifier(codeVerifier) {
1179
558
  this._codeVerifier = codeVerifier;
@@ -1211,9 +590,7 @@ class BrowserOAuthProvider {
1211
590
  case "client":
1212
591
  this._clientInfo = undefined;
1213
592
  if (this._isOAuthStore(this._store)) {
1214
- await this._store.setClient(this._storeKey, {
1215
- clientId: ""
1216
- });
593
+ await this._store.setClient(this._storeKey, { clientId: "" });
1217
594
  }
1218
595
  break;
1219
596
  case "tokens":
@@ -1244,28 +621,6 @@ class BrowserOAuthProvider {
1244
621
  }
1245
622
  return;
1246
623
  }
1247
- async _refreshTokens() {
1248
- if (this._refreshInProgress) {
1249
- await this._refreshInProgress;
1250
- return;
1251
- }
1252
- this._refreshInProgress = this._doRefreshTokens();
1253
- try {
1254
- await this._refreshInProgress;
1255
- } finally {
1256
- this._refreshInProgress = undefined;
1257
- }
1258
- }
1259
- async _doRefreshTokens() {
1260
- if (!this._tokens?.refresh_token) {
1261
- throw new Error("No refresh token available");
1262
- }
1263
- const clientInfo = await this.clientInformation();
1264
- if (!clientInfo?.client_id) {
1265
- throw new Error("No client information available for refresh");
1266
- }
1267
- throw new Error("Token refresh not yet implemented - requires token endpoint URL");
1268
- }
1269
624
  }
1270
625
  // src/index.ts
1271
626
  async function getAuthCode(input) {
@@ -1274,13 +629,13 @@ async function getAuthCode(input) {
1274
629
  authorizationUrl,
1275
630
  port = 3000,
1276
631
  hostname = "localhost",
1277
- openBrowser = true,
1278
632
  timeout = 30000,
1279
633
  callbackPath = "/callback",
1280
634
  successHtml,
1281
635
  errorHtml,
1282
636
  signal,
1283
- onRequest
637
+ onRequest,
638
+ launch
1284
639
  } = options;
1285
640
  const server = createCallbackServer();
1286
641
  try {
@@ -1292,18 +647,8 @@ async function getAuthCode(input) {
1292
647
  signal,
1293
648
  onRequest
1294
649
  });
1295
- if (openBrowser) {
1296
- await open_default(authorizationUrl);
1297
- } else {
1298
- fetch(authorizationUrl).then(async (response) => {
1299
- if (response.status === 302 || response.status === 301) {
1300
- const location = response.headers.get("Location");
1301
- if (location) {
1302
- await fetch(location);
1303
- }
1304
- }
1305
- }).catch(() => {});
1306
- }
650
+ if (launch)
651
+ Promise.resolve(launch(authorizationUrl)).catch(() => {});
1307
652
  const result = await server.waitForCallback(callbackPath, timeout);
1308
653
  if (result.error) {
1309
654
  throw new OAuthError(result.error, result.error_description, result.error_uri);
@@ -1318,5 +663,6 @@ export {
1318
663
  inMemoryStore,
1319
664
  getAuthCode,
1320
665
  fileStore,
666
+ TimeoutError,
1321
667
  OAuthError
1322
668
  };