From 3e854f164ea150027823597d8f366179bb69cee8 Mon Sep 17 00:00:00 2001 From: Pedro Filho Date: Thu, 8 Oct 2026 14:42:14 -0300 Subject: [PATCH] fix(pg-cloudflare): emit the destroy error so a connect timeout says why Fixes #3809 --- packages/pg-cloudflare/src/index.ts | 6 ++-- packages/pg-esm-test/pg-cloudflare.test.js | 41 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/packages/pg-cloudflare/src/index.ts b/packages/pg-cloudflare/src/index.ts index 527b433f5..d3900ef5f 100644 --- a/packages/pg-cloudflare/src/index.ts +++ b/packages/pg-cloudflare/src/index.ts @@ -126,9 +126,11 @@ export class CloudflareSocket extends EventEmitter { return this } - destroy(reason: string) { - log('destroying CF socket', reason) + destroy(error?: Error) { + log('destroying CF socket', error) this.destroyed = true + // Like net.Socket#destroy(error): 'error' with the reason, then 'close'. + if (error) queueMicrotask(() => this.emit('error', error)) return this.end() } diff --git a/packages/pg-esm-test/pg-cloudflare.test.js b/packages/pg-esm-test/pg-cloudflare.test.js index 683229158..eba8351e0 100644 --- a/packages/pg-esm-test/pg-cloudflare.test.js +++ b/packages/pg-esm-test/pg-cloudflare.test.js @@ -38,6 +38,47 @@ describe('pg-cloudflare', () => { await promise }) + it('should emit the destroy error before close', async () => { + const socket = new CloudflareSocket() + socket._cfSocket = { + closed: new Promise(() => {}), + close: async () => {}, + } + socket._addClosedHandler() + + const events = [] + const error = new Error('timeout expired') + socket.on('error', (emitted) => events.push(emitted)) + await new Promise((resolve) => { + socket.once('close', () => { + events.push('close') + resolve() + }) + socket.destroy(error) + }) + + assert.ok(socket.destroyed) + assert.deepEqual(events, [error, 'close']) + }) + + it('should not emit an error when destroyed without one', async () => { + const socket = new CloudflareSocket() + socket._cfSocket = { + closed: new Promise(() => {}), + close: async () => {}, + } + socket._addClosedHandler() + + const errors = [] + socket.on('error', (error) => errors.push(error)) + await new Promise((resolve) => { + socket.once('close', resolve) + socket.destroy() + }) + + assert.deepEqual(errors, []) + }) + it('should emit close when ending a socket whose closed promise never settles', async () => { const socket = new CloudflareSocket() socket._cfSocket = {