oauth-callback 1.2.3 → 1.2.5

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
@@ -718,6 +718,7 @@ class BaseCallbackServer {
718
718
  successHtml;
719
719
  errorHtml;
720
720
  onRequest;
721
+ callbackReceived = false;
721
722
  abortHandler;
722
723
  signal;
723
724
  setup(options) {
@@ -743,6 +744,7 @@ class BaseCallbackServer {
743
744
  for (const [key, value] of url.searchParams)
744
745
  params[key] = value;
745
746
  listener.resolve(params);
747
+ this.callbackReceived = true;
746
748
  return new Response(generateCallbackHTML(params, this.successHtml, this.errorHtml), {
747
749
  status: 200,
748
750
  headers: { "Content-Type": "text/html" }
@@ -751,18 +753,21 @@ class BaseCallbackServer {
751
753
  async waitForCallback(path2, timeout) {
752
754
  if (this.callbackListeners.has(path2))
753
755
  return Promise.reject(new Error(`A listener for the path "${path2}" is already active.`));
756
+ let timeoutId;
754
757
  try {
755
758
  return await Promise.race([
756
759
  new Promise((resolve, reject) => {
757
760
  this.callbackListeners.set(path2, { resolve, reject });
758
761
  }),
759
762
  new Promise((_, reject) => {
760
- setTimeout(() => {
763
+ timeoutId = setTimeout(() => {
761
764
  reject(new Error(`OAuth callback timeout after ${timeout}ms waiting for ${path2}`));
762
765
  }, timeout);
763
766
  })
764
767
  ]);
765
768
  } finally {
769
+ if (timeoutId)
770
+ clearTimeout(timeoutId);
766
771
  this.callbackListeners.delete(path2);
767
772
  }
768
773
  }
@@ -792,11 +797,10 @@ class BunCallbackServer extends BaseCallbackServer {
792
797
  async stopServer() {
793
798
  if (!this.server)
794
799
  return;
795
- while (this.server.pendingRequests > 0) {
796
- await new Promise((resolve) => setTimeout(resolve, 10));
800
+ if (this.callbackReceived) {
801
+ await new Promise((resolve) => setTimeout(resolve, 50));
797
802
  }
798
- await new Promise((resolve) => setTimeout(resolve, 100));
799
- this.server.stop();
803
+ this.server.stop(true);
800
804
  this.server = undefined;
801
805
  }
802
806
  }
package/dist/mcp.js CHANGED
@@ -738,6 +738,7 @@ class BaseCallbackServer {
738
738
  successHtml;
739
739
  errorHtml;
740
740
  onRequest;
741
+ callbackReceived = false;
741
742
  abortHandler;
742
743
  signal;
743
744
  setup(options) {
@@ -763,6 +764,7 @@ class BaseCallbackServer {
763
764
  for (const [key, value] of url.searchParams)
764
765
  params[key] = value;
765
766
  listener.resolve(params);
767
+ this.callbackReceived = true;
766
768
  return new Response(generateCallbackHTML(params, this.successHtml, this.errorHtml), {
767
769
  status: 200,
768
770
  headers: { "Content-Type": "text/html" }
@@ -771,18 +773,21 @@ class BaseCallbackServer {
771
773
  async waitForCallback(path2, timeout) {
772
774
  if (this.callbackListeners.has(path2))
773
775
  return Promise.reject(new Error(`A listener for the path "${path2}" is already active.`));
776
+ let timeoutId;
774
777
  try {
775
778
  return await Promise.race([
776
779
  new Promise((resolve, reject) => {
777
780
  this.callbackListeners.set(path2, { resolve, reject });
778
781
  }),
779
782
  new Promise((_, reject) => {
780
- setTimeout(() => {
783
+ timeoutId = setTimeout(() => {
781
784
  reject(new Error(`OAuth callback timeout after ${timeout}ms waiting for ${path2}`));
782
785
  }, timeout);
783
786
  })
784
787
  ]);
785
788
  } finally {
789
+ if (timeoutId)
790
+ clearTimeout(timeoutId);
786
791
  this.callbackListeners.delete(path2);
787
792
  }
788
793
  }
@@ -812,11 +817,10 @@ class BunCallbackServer extends BaseCallbackServer {
812
817
  async stopServer() {
813
818
  if (!this.server)
814
819
  return;
815
- while (this.server.pendingRequests > 0) {
816
- await new Promise((resolve) => setTimeout(resolve, 10));
820
+ if (this.callbackReceived) {
821
+ await new Promise((resolve) => setTimeout(resolve, 50));
817
822
  }
818
- await new Promise((resolve) => setTimeout(resolve, 100));
819
- this.server.stop();
823
+ this.server.stop(true);
820
824
  this.server = undefined;
821
825
  }
822
826
  }
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mHAAmH;IACnH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,oFAAoF;IACpF,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,KAAK,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,iEAAiE;IACjE,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACxE,4CAA4C;IAC5C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AA2RD;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,cAAc,CAKrD"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mHAAmH;IACnH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,oFAAoF;IACpF,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,KAAK,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,iEAAiE;IACjE,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACxE,4CAA4C;IAC5C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AA8RD;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,cAAc,CAKrD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oauth-callback",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "Lightweight OAuth 2.0 callback handler for Node.js, Deno, and Bun with built-in browser flow and MCP SDK integration",
5
5
  "keywords": [
6
6
  "oauth",
@@ -104,6 +104,7 @@
104
104
  "build": "bun run build:templates && bun build ./src/index.ts --outdir=./dist --target=node && bun build ./src/mcp.ts --outdir=./dist --target=node && tsc --declaration --emitDeclarationOnly --outDir ./dist",
105
105
  "clean": "rm -rf dist",
106
106
  "test": "bun test",
107
+ "test:login": "bun run scripts/test-login.ts",
107
108
  "typecheck": "bun tsc -p tsconfig.json --noEmit",
108
109
  "format": "bun prettier --write .",
109
110
  "format:check": "bun prettier --check .",
package/src/server.ts CHANGED
@@ -100,6 +100,7 @@ abstract class BaseCallbackServer implements CallbackServer {
100
100
  protected successHtml?: string;
101
101
  protected errorHtml?: string;
102
102
  protected onRequest?: (req: Request) => void;
103
+ protected callbackReceived = false;
103
104
  private abortHandler?: () => void;
104
105
  private signal?: AbortSignal;
105
106
 
@@ -142,6 +143,7 @@ abstract class BaseCallbackServer implements CallbackServer {
142
143
 
143
144
  // Resolve the promise for the waiting listener.
144
145
  listener.resolve(params);
146
+ this.callbackReceived = true;
145
147
 
146
148
  return new Response(
147
149
  generateCallbackHTML(params, this.successHtml, this.errorHtml),
@@ -164,6 +166,8 @@ abstract class BaseCallbackServer implements CallbackServer {
164
166
  new Error(`A listener for the path "${path}" is already active.`),
165
167
  );
166
168
 
169
+ let timeoutId: ReturnType<typeof setTimeout> | undefined;
170
+
167
171
  try {
168
172
  // Race a promise that waits for the callback against a promise that rejects on timeout.
169
173
  return await Promise.race([
@@ -173,7 +177,7 @@ abstract class BaseCallbackServer implements CallbackServer {
173
177
  }),
174
178
  // This promise rejects after the specified timeout.
175
179
  new Promise<CallbackResult>((_, reject) => {
176
- setTimeout(() => {
180
+ timeoutId = setTimeout(() => {
177
181
  reject(
178
182
  new Error(
179
183
  `OAuth callback timeout after ${timeout}ms waiting for ${path}`,
@@ -183,8 +187,9 @@ abstract class BaseCallbackServer implements CallbackServer {
183
187
  }),
184
188
  ]);
185
189
  } finally {
186
- // CRITICAL: Always clean up the listener to prevent memory leaks,
187
- // regardless of whether the promise resolved or rejected.
190
+ // CRITICAL: Always clean up the listener and timeout to prevent memory leaks
191
+ // and allow the process to exit cleanly.
192
+ if (timeoutId) clearTimeout(timeoutId);
188
193
  this.callbackListeners.delete(path);
189
194
  }
190
195
  }
@@ -225,13 +230,11 @@ class BunCallbackServer extends BaseCallbackServer {
225
230
 
226
231
  protected async stopServer(): Promise<void> {
227
232
  if (!this.server) return;
228
- // Wait for pending requests to complete
229
- while (this.server.pendingRequests > 0) {
230
- await new Promise((resolve) => setTimeout(resolve, 10));
233
+ // Brief delay to allow response bytes to flush to the client
234
+ if (this.callbackReceived) {
235
+ await new Promise((resolve) => setTimeout(resolve, 50));
231
236
  }
232
- // Allow response bytes to flush to the client before shutdown
233
- await new Promise((resolve) => setTimeout(resolve, 100));
234
- this.server.stop();
237
+ this.server.stop(true); // Force close connections
235
238
  this.server = undefined;
236
239
  }
237
240
  }