Skip to content

Support async transformWsUrl returning a Promise<string>#2042

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/fix-transform-url-promise-issue
Draft

Support async transformWsUrl returning a Promise<string>#2042
Copilot wants to merge 2 commits intomainfrom
copilot/fix-transform-url-promise-issue

Conversation

Copy link
Copy Markdown

Copilot AI commented Mar 6, 2026

transformWsUrl only supported synchronous functions. Returning a Promise<string> (e.g. for async URL signing) caused the client to connect with a stringified [object Promise] URL, resulting in endless reconnect loops.

Changes

  • client.ts: Updated transformWsUrl type to () => string | Promise<string>
  • connect/ws.ts (Node.js): When transformWsUrl returns a Promise, a deferred Duplex stream is returned immediately while buffering writes; once the URL resolves the real WebSocket is created and the buffer is flushed
  • connect/ws.ts (Browser): createBrowserWebSocket propagates the Promise; browserStreamBuilder creates a BufferedDuplex with a placeholder socket and wires up the real socket post-resolution
  • connect/ali.ts / connect/wx.ts: Same pattern — connectSocket is deferred until the URL Promise resolves
  • test/node/websocket_client.ts: Added test covering async transformWsUrl

Usage

mqtt.connect({
  protocol: 'ws',
  transformWsUrl: async (url, opts, client) => {
    const signed = await renewSignedUrl(url)
    return signed
  },
})

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • localhost_fake
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=4 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA (dns block)
  • mqtt
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node -r esbuild-register --test-concurrency 1 test/node/websocket_client.ts (dns block)
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=4 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA (dns block)
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node -r esbuild-register --test-concurrency 1 test/node/websocket_client.ts r bin/sh (dns block)
  • this_hostname_should_not_exist
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node -r esbuild-register --test-concurrency 1 test/node/websocket_client.ts (dns block)
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=4 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA (dns block)
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node -r esbuild-register --test-concurrency 1 test/node/websocket_client.ts r bin/sh (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>TransformUrl function returns a async promise instead of new url string</issue_title>
<issue_description>I'm trying to request for a new signed url from a server that already signs the url, the operation is async and by returning the promise I wasn't able to have the MQTT socket connected (it was never connected, went straight to offline).

mqtt.connect({
      protocal: 'ws',
      transformWsUrl: () => {
        return renewStreamUrl(endpoint, token, conversationId) //this is an async operation
      }
    }
  )

The websocket just starts to reconnect nonstop with a new url.

The expected behavior is: transformUrl callback returns a resolved promise, which in my case is the signed url, but it seems like its only returning a wrapped Promise which causes my mqtt client to reconnect nonstop because it didn't find a valid stream URL</issue_description>

<agent_instructions>Make transformWsUrl option accepting a promise</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@robertsLando PR?

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: robertsLando <11502495+robertsLando@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix TransformUrl function to return resolved signed URL Support async transformWsUrl returning a Promise<string> Mar 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TransformUrl function returns a async promise instead of new url string

2 participants