From ff5670772cf32f1f4dbf4da4e25ec2e8d55575b3 Mon Sep 17 00:00:00 2001 From: angelkawai <123734885+luokerenx4@users.noreply.github.com> Date: Thu, 3 Sep 2026 15:26:15 +0800 Subject: [PATCH] fix(connector): use native AbortController instead of bundled polyfill grammY's Node shim imports the legacy abort-controller polyfill, which the connector bundle inlines via noExternal. esbuild renames the polyfill's AbortSignal class to avoid colliding with the native global, and node-fetch v2 gates every request on the signal prototype's constructor name being exactly "AbortSignal". The renamed class no longer matches, so every grammY call fails with "Expected signal to be an instanceof AbortSignal" and Telegram/Discord polling never becomes ready. Alias abort-controller to a shim that re-exports Node's native globals (runtime is Node >= 22), and cover it with a spec. --- .../src/shims/abort-controller.spec.ts | 23 +++++++++++++++++++ .../connector/src/shims/abort-controller.ts | 19 +++++++++++++++ services/connector/tsup.config.ts | 10 ++++++++ 3 files changed, 52 insertions(+) create mode 100644 services/connector/src/shims/abort-controller.spec.ts create mode 100644 services/connector/src/shims/abort-controller.ts diff --git a/services/connector/src/shims/abort-controller.spec.ts b/services/connector/src/shims/abort-controller.spec.ts new file mode 100644 index 000000000..e23229a9d --- /dev/null +++ b/services/connector/src/shims/abort-controller.spec.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from 'vitest' +import { + AbortController, + AbortSignal, + default as DefaultExport, +} from './abort-controller.js' + +describe('abort-controller shim', () => { + it('re-exports the native global AbortController', () => { + expect(AbortController).toBe(globalThis.AbortController) + expect(DefaultExport).toBe(globalThis.AbortController) + }) + + it('re-exports the native global AbortSignal', () => { + expect(AbortSignal).toBe(globalThis.AbortSignal) + }) + + it('produces a signal whose prototype name is AbortSignal', () => { + const signal = new AbortController().signal + const proto = Object.getPrototypeOf(signal) + expect(proto?.constructor?.name).toBe('AbortSignal') + }) +}) diff --git a/services/connector/src/shims/abort-controller.ts b/services/connector/src/shims/abort-controller.ts new file mode 100644 index 000000000..4ebaf1d36 --- /dev/null +++ b/services/connector/src/shims/abort-controller.ts @@ -0,0 +1,19 @@ +/** + * Runtime shim replacing the bundled `abort-controller` polyfill with Node's + * native global implementations. + * + * grammY's Node shim imports `abort-controller` because it supports old Node + * runtimes. Our connector bundle externalizes nothing, so esbuild would inline + * that polyfill. esbuild renames its `AbortSignal` class to avoid a collision + * with the native global, and node-fetch v2 (v2.7.0) gates every request on + * the signal prototype's constructor name being exactly "AbortSignal". The + * renamed class no longer matches, so every grammY HTTP call fails with + * "Expected signal to be an instanceof AbortSignal". + * + * Rather than ship a legacy polyfill (the runtime is Node >= 22), alias + * `abort-controller` to this module so grammY hands node-fetch the native + * `AbortSignal`, whose constructor keeps its real name. + */ +export const AbortController = globalThis.AbortController +export const AbortSignal = globalThis.AbortSignal +export default globalThis.AbortController diff --git a/services/connector/tsup.config.ts b/services/connector/tsup.config.ts index e9640b8e5..40188eeb7 100644 --- a/services/connector/tsup.config.ts +++ b/services/connector/tsup.config.ts @@ -1,3 +1,4 @@ +import { resolve } from 'node:path' import { defineConfig } from 'tsup' export default defineConfig({ @@ -15,5 +16,14 @@ export default defineConfig({ outExtension: () => ({ js: '.cjs' }), esbuildOptions: (options) => { options.conditions = ['openalice-source', ...(options.conditions ?? [])] + // grammY's Node shim imports the legacy `abort-controller` polyfill. + // Bundling it makes esbuild rename its AbortSignal class, which breaks + // node-fetch@2.7.0's `constructor.name === "AbortSignal"` check and fails + // every grammY call. Point it at Node's native global instead (runtime is + // Node >= 22). See src/shims/abort-controller.ts. + options.alias = { + ...(options.alias ?? {}), + 'abort-controller': resolve(import.meta.dirname, 'src/shims/abort-controller.ts'), + } }, })