Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions services/connector/src/shims/abort-controller.spec.ts
Original file line number Diff line number Diff line change
@@ -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')
})
})
19 changes: 19 additions & 0 deletions services/connector/src/shims/abort-controller.ts
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions services/connector/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { resolve } from 'node:path'
import { defineConfig } from 'tsup'

export default defineConfig({
Expand All @@ -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'),
}
},
})