From 0f3e99473fc7874f16c62a795ac116330499208f Mon Sep 17 00:00:00 2001 From: rytkhs Date: Tue, 22 Sep 2026 20:04:01 +0900 Subject: [PATCH] fix(cloudflare): Bind non-instrumented Queue methods to the underlying binding Co-Authored-By: Claude Opus 5 --- .../worker/instrumentQueueProducer.ts | 4 +++- .../worker/instrumentQueueProducer.test.ts | 20 ++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/cloudflare/src/instrumentations/worker/instrumentQueueProducer.ts b/packages/cloudflare/src/instrumentations/worker/instrumentQueueProducer.ts index 293b027d5441..2f58ce88310c 100644 --- a/packages/cloudflare/src/instrumentations/worker/instrumentQueueProducer.ts +++ b/packages/cloudflare/src/instrumentations/worker/instrumentQueueProducer.ts @@ -99,7 +99,9 @@ export function instrumentQueueProducer(queue: T, bindingName: }; } - return Reflect.get(target, prop, receiver); + const value = Reflect.get(target, prop, receiver) as unknown; + + return typeof value === 'function' ? value.bind(target) : value; }, }); } diff --git a/packages/cloudflare/test/instrumentations/worker/instrumentQueueProducer.test.ts b/packages/cloudflare/test/instrumentations/worker/instrumentQueueProducer.test.ts index 5b291bea7e3e..ef8c8bdd0c56 100644 --- a/packages/cloudflare/test/instrumentations/worker/instrumentQueueProducer.test.ts +++ b/packages/cloudflare/test/instrumentations/worker/instrumentQueueProducer.test.ts @@ -1,4 +1,4 @@ -import type { Queue } from '@cloudflare/workers-types'; +import type { Queue, QueueMetrics } from '@cloudflare/workers-types'; import * as SentryCore from '@sentry/core'; import { beforeEach, describe, expect, test, vi } from 'vitest'; import { instrumentQueueProducer } from '../../../src/instrumentations/worker/instrumentQueueProducer'; @@ -10,6 +10,15 @@ function createMockQueue(): Queue { } as unknown as Queue; } +// `metrics()` reads a private field, so it throws when `this` is the Proxy, as workerd's native methods do. +class BrandCheckedQueue { + readonly #metrics: QueueMetrics = { backlogCount: 3, backlogBytes: 2048 }; + + public metrics(): Promise { + return Promise.resolve(this.#metrics); + } +} + describe('instrumentQueueProducer', () => { beforeEach(() => { vi.clearAllMocks(); @@ -178,4 +187,13 @@ describe('instrumentQueueProducer', () => { const wrapped = instrumentQueueProducer(queue, 'MY_QUEUE') as Queue & { customMethod: () => string }; expect(wrapped.customMethod()).toBe('hi'); }); + + test('calls non-instrumented methods on the underlying queue', async () => { + const queue = new BrandCheckedQueue() as unknown as Queue; + const wrapped = instrumentQueueProducer(queue, 'MY_QUEUE'); + + const metrics = await wrapped.metrics(); + + expect(metrics).toEqual({ backlogCount: 3, backlogBytes: 2048 }); + }); });