From 86b80e93cd8386450980e8eb83fc4be5650cfb9e Mon Sep 17 00:00:00 2001 From: JPeer264 Date: Tue, 19 May 2026 18:46:27 +0200 Subject: [PATCH 1/2] feat(cloudflare): Only capture workflow step error on final retry attempt Cloudflare Workflows (wrangler 4.86.0+) now pass step context with `attempt` and `config.retries.limit` to step callbacks. When available, we use this to only capture errors on the final retry attempt, avoiding duplicate error events during retries. For older wrangler versions without step context, the SDK falls back to legacy behavior (capturing errors on every attempt). - Add step context detection in `instrumentedCallback` - Add integration tests for step context behavior - Add e2e test for legacy behavior (wrangler 4.70.0) Co-Authored-By: Claude Opus 4.5 --- .../cloudflare-integration-tests/package.json | 4 +- .../suites/workflows/step-context/index.ts | 107 +++++++ .../suites/workflows/step-context/test.ts | 109 +++++++ .../workflows/step-context/wrangler.jsonc | 13 + .../.gitignore | 1 + .../package.json | 27 ++ .../playwright.config.ts | 9 + .../src/index.ts | 91 ++++++ .../start-event-proxy.mjs | 6 + .../tests/workflow.test.ts | 36 +++ .../tsconfig.json | 13 + .../wrangler.jsonc | 13 + packages/cloudflare/src/workflows.ts | 17 +- packages/cloudflare/test/workflow.test.ts | 267 ++++++++++-------- yarn.lock | 243 +++++++++++++++- 15 files changed, 841 insertions(+), 115 deletions(-) create mode 100644 dev-packages/cloudflare-integration-tests/suites/workflows/step-context/index.ts create mode 100644 dev-packages/cloudflare-integration-tests/suites/workflows/step-context/test.ts create mode 100644 dev-packages/cloudflare-integration-tests/suites/workflows/step-context/wrangler.jsonc create mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/package.json create mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/playwright.config.ts create mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/src/index.ts create mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/tests/workflow.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/tsconfig.json create mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/wrangler.jsonc diff --git a/dev-packages/cloudflare-integration-tests/package.json b/dev-packages/cloudflare-integration-tests/package.json index 4f6bd95fdc14..56340257edc6 100644 --- a/dev-packages/cloudflare-integration-tests/package.json +++ b/dev-packages/cloudflare-integration-tests/package.json @@ -19,11 +19,11 @@ "hono": "^4.12.18" }, "devDependencies": { - "@cloudflare/workers-types": "^4.20250922.0", + "@cloudflare/workers-types": "^4.20260426.0", "@sentry-internal/test-utils": "10.53.1", "eslint-plugin-regexp": "^1.15.0", "vitest": "^3.2.4", - "wrangler": "4.61.0" + "wrangler": "4.86.0" }, "volta": { "extends": "../../package.json" diff --git a/dev-packages/cloudflare-integration-tests/suites/workflows/step-context/index.ts b/dev-packages/cloudflare-integration-tests/suites/workflows/step-context/index.ts new file mode 100644 index 000000000000..dc663e9dc6d7 --- /dev/null +++ b/dev-packages/cloudflare-integration-tests/suites/workflows/step-context/index.ts @@ -0,0 +1,107 @@ +import * as Sentry from '@sentry/cloudflare'; +import { WorkflowEntrypoint } from 'cloudflare:workers'; +import type { WorkflowEvent, WorkflowStep } from 'cloudflare:workers'; + +interface Env { + SENTRY_DSN: string; + STEP_CONTEXT_WORKFLOW: Workflow; +} + +interface WorkflowParams { + failCount: number; + captureManual?: boolean; +} +class StepContextTestWorkflowBase extends WorkflowEntrypoint { + async run(event: WorkflowEvent, step: WorkflowStep): Promise { + let remainingFailures = event.payload.failCount; + + const result = await step.do( + 'failing-step', + { + retries: { + limit: 2, + delay: 100, + }, + }, + async ctx => { + if (event.payload.captureManual) { + Sentry.captureException(new Error(`Manual capture on attempt ${ctx.attempt}`)); + } + + if (remainingFailures > 0) { + remainingFailures--; + throw new Error('Intentional failure for retry test'); + } + }, + ); + + return result; + } +} + +export const StepContextTestWorkflow = Sentry.instrumentWorkflowWithSentry( + (env: Env) => ({ + dsn: env.SENTRY_DSN, + }), + StepContextTestWorkflowBase, +); + +export default Sentry.withSentry( + (env: Env) => ({ + dsn: env.SENTRY_DSN, + }), + { + async fetch(request, env, _ctx) { + const url = new URL(request.url); + + if (url.pathname === '/trigger-workflow') { + const failCount = parseInt(url.searchParams.get('failCount') || '0', 10); + const captureManual = url.searchParams.get('captureManual') === 'true'; + + try { + const instance = await env.STEP_CONTEXT_WORKFLOW.create({ + params: { failCount, captureManual }, + }); + + return new Response(JSON.stringify({ id: instance.id }), { headers: { 'Content-Type': 'application/json' } }); + } catch (e) { + return new Response(JSON.stringify({ error: 'Failed to create workflow', details: String(e) }), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }); + } + } + + if (url.pathname === '/flush-marker') { + Sentry.captureMessage('flush-marker'); + return new Response(JSON.stringify({ ok: true }), { headers: { 'Content-Type': 'application/json' } }); + } + + const statusMatch = url.pathname.match(/^\/workflow-status\/(.+)$/); + + if (statusMatch) { + const workflowId = statusMatch[1]; + + try { + const instance = await env.STEP_CONTEXT_WORKFLOW.get(workflowId!); + const status = await instance.status(); + + return new Response( + JSON.stringify({ + id: workflowId, + status, + }), + { headers: { 'Content-Type': 'application/json' } }, + ); + } catch (e) { + return new Response(JSON.stringify({ error: 'Failed to get workflow status', details: String(e) }), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }); + } + } + + return new Response('Step Context Test Worker'); + }, + } satisfies ExportedHandler, +); diff --git a/dev-packages/cloudflare-integration-tests/suites/workflows/step-context/test.ts b/dev-packages/cloudflare-integration-tests/suites/workflows/step-context/test.ts new file mode 100644 index 000000000000..05c0c696a021 --- /dev/null +++ b/dev-packages/cloudflare-integration-tests/suites/workflows/step-context/test.ts @@ -0,0 +1,109 @@ +import type { Envelope } from '@sentry/core'; +import { expect, it } from 'vitest'; +import { createRunner } from '../../../runner'; + +interface TriggerResponse { + id: string; +} + +interface StatusResponse { + id: string; + status: { status: string }; +} + +async function waitForWorkflowStatus( + makeRequest: (method: 'get' | 'post', path: string) => Promise, + workflowId: string, +): Promise { + for (let i = 0; i < 30; i++) { + const status = await makeRequest('get', `/workflow-status/${workflowId}`); + if (status?.status?.status === 'errored' || status?.status?.status === 'complete') { + return status; + } + await new Promise(r => setTimeout(r, 200)); + } + return undefined; +} + +const flushMarkerMatcher = (envelope: Envelope): void => { + const [, items] = envelope; + const [itemHeader, itemBody] = items[0] as [{ type: string }, Record]; + + expect(itemHeader.type).toBe('event'); + expect(itemBody.message).toBe('flush-marker'); +}; + +it('With step context, only one error is captured on final retry attempt', async ({ signal }) => { + const runner = createRunner(__dirname) + .expect((envelope: Envelope): void => { + const [, items] = envelope; + const [itemHeader, itemBody] = items[0] as [{ type: string }, Record]; + + expect(itemHeader.type).toBe('event'); + expect(itemBody.exception).toBeDefined(); + + const exception = itemBody.exception as { values?: Array<{ value?: string }> }; + expect(exception?.values?.[0]?.value).toBe('Intentional failure for retry test'); + }) + .expect(flushMarkerMatcher) + .start(signal); + + const trigger = await runner.makeRequest('get', '/trigger-workflow?failCount=3'); + + expect(trigger?.id).toBeDefined(); + + const status = await waitForWorkflowStatus(runner.makeRequest.bind(runner), trigger!.id); + expect(status?.status?.status).toBe('errored'); + + await runner.makeRequest('get', '/flush-marker'); + await runner.completed(); +}); + +it('No error event when step eventually succeeds within retry limit', async ({ signal }) => { + const runner = createRunner(__dirname).expect(flushMarkerMatcher).start(signal); + + const trigger = await runner.makeRequest('get', '/trigger-workflow?failCount=1'); + expect(trigger?.id).toBeDefined(); + + const status = await waitForWorkflowStatus(runner.makeRequest.bind(runner), trigger!.id); + expect(status?.status?.status).toBe('complete'); + + await runner.makeRequest('get', '/flush-marker'); + await runner.completed(); +}); + +it('Manually captured exceptions are always sent on every attempt', async ({ signal }) => { + const runner = createRunner(__dirname) + .expectN(3, (envelope: Envelope): void => { + const [, items] = envelope; + const [itemHeader, itemBody] = items[0] as [{ type: string }, Record]; + + expect(itemHeader.type).toBe('event'); + expect(itemBody.exception).toBeDefined(); + + const exception = itemBody.exception as { values?: Array<{ value?: string }> }; + expect(exception?.values?.[0]?.value).toMatch(/^Manual capture on attempt \d+$/); + }) + .expect((envelope: Envelope): void => { + const [, items] = envelope; + const [itemHeader, itemBody] = items[0] as [{ type: string }, Record]; + + expect(itemHeader.type).toBe('event'); + expect(itemBody.exception).toBeDefined(); + + const exception = itemBody.exception as { values?: Array<{ value?: string }> }; + expect(exception?.values?.[0]?.value).toBe('Intentional failure for retry test'); + }) + .expect(flushMarkerMatcher) + .unordered() + .start(signal); + + const trigger = await runner.makeRequest('get', '/trigger-workflow?failCount=3&captureManual=true'); + expect(trigger?.id).toBeDefined(); + + const status = await waitForWorkflowStatus(runner.makeRequest.bind(runner), trigger!.id); + expect(status?.status?.status).toBe('errored'); + + await runner.makeRequest('get', '/flush-marker'); + await runner.completed(); +}); diff --git a/dev-packages/cloudflare-integration-tests/suites/workflows/step-context/wrangler.jsonc b/dev-packages/cloudflare-integration-tests/suites/workflows/step-context/wrangler.jsonc new file mode 100644 index 000000000000..2c82218469d7 --- /dev/null +++ b/dev-packages/cloudflare-integration-tests/suites/workflows/step-context/wrangler.jsonc @@ -0,0 +1,13 @@ +{ + "name": "workflow-step-context-test", + "compatibility_date": "2026-05-01", + "main": "index.ts", + "compatibility_flags": ["nodejs_compat"], + "workflows": [ + { + "name": "step-context-test-workflow", + "binding": "STEP_CONTEXT_WORKFLOW", + "class_name": "StepContextTestWorkflow", + }, + ], +} diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/.gitignore b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/.gitignore new file mode 100644 index 000000000000..e71378008bf1 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/.gitignore @@ -0,0 +1 @@ +.wrangler diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/package.json b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/package.json new file mode 100644 index 000000000000..c16a50408bf6 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/package.json @@ -0,0 +1,27 @@ +{ + "name": "cloudflare-workers-workflow-legacy", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "wrangler dev --var \"E2E_TEST_DSN:$E2E_TEST_DSN\" --log-level=$(test $CI && echo 'none' || echo 'log')", + "build": "wrangler deploy --dry-run", + "typecheck": "tsc --noEmit", + "test:build": "pnpm install && pnpm build", + "test:assert": "pnpm typecheck && pnpm test:dev", + "test:dev": "TEST_ENV=development playwright test" + }, + "dependencies": { + "@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz" + }, + "devDependencies": { + "@playwright/test": "~1.56.0", + "@cloudflare/workers-types": "^4.20260303.0", + "@sentry-internal/test-utils": "link:../../../test-utils", + "typescript": "^5.5.2", + "wrangler": "4.70.0" + }, + "volta": { + "node": "24.15.0", + "extends": "../../package.json" + } +} diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/playwright.config.ts b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/playwright.config.ts new file mode 100644 index 000000000000..52491387ce02 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/playwright.config.ts @@ -0,0 +1,9 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const config = getPlaywrightConfig({ + startCommand: 'pnpm dev', + port: 8787, + eventProxyFile: 'start-event-proxy.mjs', +}); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/src/index.ts b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/src/index.ts new file mode 100644 index 000000000000..3d9e447a61a8 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/src/index.ts @@ -0,0 +1,91 @@ +import * as Sentry from '@sentry/cloudflare'; +import { WorkflowEntrypoint } from 'cloudflare:workers'; +import type { WorkflowEvent, WorkflowStep } from 'cloudflare:workers'; + +interface Env { + E2E_TEST_DSN: string; + RETRY_WORKFLOW: Workflow; +} + +interface WorkflowParams { + failCount: number; +} + +class RetryTestWorkflowBase extends WorkflowEntrypoint { + async run(event: WorkflowEvent, step: WorkflowStep): Promise { + let remainingFailures = event.payload.failCount; + + await step.do( + 'failing-step', + { + retries: { + limit: 2, + delay: 100, + }, + }, + async () => { + if (remainingFailures > 0) { + remainingFailures--; + throw new Error('Intentional failure for retry test'); + } + return 'success'; + }, + ); + + return 'workflow completed'; + } +} + +export const RetryTestWorkflow = Sentry.instrumentWorkflowWithSentry( + (env: Env) => ({ + dsn: env.E2E_TEST_DSN, + tunnel: 'http://localhost:3031/', + }), + RetryTestWorkflowBase, +); + +export default Sentry.withSentry( + (env: Env) => ({ + dsn: env.E2E_TEST_DSN, + tunnel: 'http://localhost:3031/', + }), + { + async fetch(request, env, _ctx) { + const url = new URL(request.url); + + if (url.pathname === '/flush-marker') { + Sentry.captureMessage('flush-marker'); + return new Response(JSON.stringify({ ok: true }), { headers: { 'Content-Type': 'application/json' } }); + } + + if (url.pathname === '/trigger-workflow') { + const failCount = parseInt(url.searchParams.get('failCount') || '3', 10); + + const instance = await env.RETRY_WORKFLOW.create({ + params: { failCount }, + }); + + // Poll for workflow completion + for (let i = 0; i < 30; i++) { + try { + const status = await instance.status(); + if (status.status === 'complete' || status.status === 'errored') { + return new Response(JSON.stringify({ id: instance.id, status }), { + headers: { 'Content-Type': 'application/json' }, + }); + } + } catch { + // status() may not be available yet + } + await new Promise(r => setTimeout(r, 500)); + } + + return new Response(JSON.stringify({ id: instance.id, status: 'timeout' }), { + headers: { 'Content-Type': 'application/json' }, + }); + } + + return new Response('Workflow Legacy Test Worker'); + }, + } satisfies ExportedHandler, +); diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/start-event-proxy.mjs new file mode 100644 index 000000000000..eff663e39d0b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'cloudflare-workers-workflow-legacy', +}); diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/tests/workflow.test.ts b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/tests/workflow.test.ts new file mode 100644 index 000000000000..184074ab867f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/tests/workflow.test.ts @@ -0,0 +1,36 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/test-utils'; + +test('Workflow emits error event on every retry attempt (legacy behavior without step context)', async ({ + baseURL, +}) => { + const errors: unknown[] = []; + const seenEventIds = new Set(); + + // Waiter that collects 3 unique error events before resolving + const errorCollector = waitForError('cloudflare-workers-workflow-legacy', event => { + if ( + event.exception?.values?.[0]?.value === 'Intentional failure for retry test' && + event.exception?.values?.[0]?.mechanism?.type === 'auto.faas.cloudflare.workflow' + ) { + const eventId = event.event_id; + if (eventId && !seenEventIds.has(eventId)) { + seenEventIds.add(eventId); + errors.push(event); + } + // Return true only when we have all 3 errors + return errors.length >= 3; + } + return false; + }); + + // Trigger workflow with 3 failures (exceeds retry limit of 2) + const response = await fetch(`${baseURL}/trigger-workflow?failCount=3`); + expect(response.status).toBe(200); + + // Wait for all 3 errors to be collected + await errorCollector; + + // Legacy behavior: errors captured on all 3 attempts (unique events) + expect(errors.length).toBe(3); +}); diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/tsconfig.json b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/tsconfig.json new file mode 100644 index 000000000000..6532e903512e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2021", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2021"], + "types": ["@cloudflare/workers-types/2023-07-01"], + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/wrangler.jsonc b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/wrangler.jsonc new file mode 100644 index 000000000000..1edde60ad28c --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers-workflow-legacy/wrangler.jsonc @@ -0,0 +1,13 @@ +{ + "name": "cloudflare-workers-workflow-legacy", + "main": "src/index.ts", + "compatibility_date": "2025-05-01", + "compatibility_flags": ["nodejs_compat"], + "workflows": [ + { + "name": "retry-test-workflow", + "binding": "RETRY_WORKFLOW", + "class_name": "RetryTestWorkflow", + }, + ], +} diff --git a/packages/cloudflare/src/workflows.ts b/packages/cloudflare/src/workflows.ts index e77772c73783..2a5987a67ff8 100644 --- a/packages/cloudflare/src/workflows.ts +++ b/packages/cloudflare/src/workflows.ts @@ -89,6 +89,18 @@ class WrappedWorkflowStep implements WorkflowStep { const config = typeof configOrCallback === 'function' ? undefined : configOrCallback; const instrumentedCallback = async (...args: unknown[]): Promise => { + // Feature detection: Cloudflare Workflows (April 2026+) pass a step context + // with `attempt` and `config.retries.limit`. When available, we only capture + // errors on the final attempt to avoid duplicates during retries. + const stepContext = args[0] as { attempt?: number; config?: { retries?: { limit?: number } } } | undefined; + const attempt = stepContext?.attempt; + const retryLimit = stepContext?.config?.retries?.limit; + const hasStepContext = typeof attempt === 'number' && typeof retryLimit === 'number'; + + // Only capture error on final attempt (attempt > retryLimit means no more retries left) + // or when step context is unavailable (legacy behavior - capture all errors) + const isFinalAttempt = !hasStepContext || attempt > retryLimit; + return startSpan( { op: 'function.step.do', @@ -99,6 +111,7 @@ class WrappedWorkflowStep implements WorkflowStep { 'cloudflare.workflow.retries.backoff': config?.retries?.backoff, 'cloudflare.workflow.retries.delay': config?.retries?.delay, 'cloudflare.workflow.retries.limit': config?.retries?.limit, + 'cloudflare.workflow.attempt': attempt, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.workflow', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'task', }, @@ -109,7 +122,9 @@ class WrappedWorkflowStep implements WorkflowStep { span.setStatus({ code: 1 }); return result; } catch (error) { - captureException(error, { mechanism: { handled: true, type: 'auto.faas.cloudflare.workflow' } }); + if (isFinalAttempt) { + captureException(error, { mechanism: { handled: true, type: 'auto.faas.cloudflare.workflow' } }); + } throw error; } finally { this._waitUntil(flush(2000)); diff --git a/packages/cloudflare/test/workflow.test.ts b/packages/cloudflare/test/workflow.test.ts index 008486697b5b..a424dbcd956b 100644 --- a/packages/cloudflare/test/workflow.test.ts +++ b/packages/cloudflare/test/workflow.test.ts @@ -12,7 +12,7 @@ import { instrumentEnv } from '../src/instrumentations/worker/instrumentEnv'; const NODE_MAJOR_VERSION = parseInt(process.versions.node.split('.')[0]!); -const MOCK_STEP_CTX = { attempt: 1 }; +const MOCK_STEP_CTX = { attempt: 1, config: { retries: { limit: 3 }, timeout: 60000 } }; const mockStep: WorkflowStep = { do: vi @@ -23,16 +23,18 @@ const mockStep: WorkflowStep = { configOrCallback: WorkflowStepConfig | ((...args: unknown[]) => Promise), maybeCallback?: (...args: unknown[]) => Promise, ) => { - let count = 0; + const retryLimit = 3; + let attempt = 0; - while (count <= 5) { - count += 1; + while (attempt <= retryLimit) { + attempt += 1; + const stepCtx = { attempt, config: { retries: { limit: retryLimit }, timeout: 60000 } }; try { if (typeof configOrCallback === 'function') { - return await configOrCallback(MOCK_STEP_CTX); + return await configOrCallback(stepCtx); } else { - return await (maybeCallback ? maybeCallback(MOCK_STEP_CTX) : Promise.resolve()); + return await (maybeCallback ? maybeCallback(stepCtx) : Promise.resolve()); } } catch { await new Promise(resolve => setTimeout(resolve, 1000)); @@ -150,20 +152,20 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { expect.objectContaining({ event_id: expect.any(String), contexts: { - trace: { + trace: expect.objectContaining({ parent_span_id: undefined, span_id: expect.any(String), trace_id: TRACE_ID, - data: { + data: expect.objectContaining({ 'sentry.origin': 'auto.faas.cloudflare.workflow', 'sentry.op': 'function.step.do', 'sentry.source': 'task', 'sentry.sample_rate': 1, - }, + }), op: 'function.step.do', status: 'ok', origin: 'auto.faas.cloudflare.workflow', - }, + }), cloud_resource: { 'cloud.provider': 'cloudflare' }, runtime: { name: 'cloudflare' }, }, @@ -267,20 +269,20 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { expect.objectContaining({ event_id: expect.any(String), contexts: { - trace: { + trace: expect.objectContaining({ parent_span_id: undefined, span_id: expect.any(String), trace_id: '0d2b6d1743ce6d53af4f5ee416ad5d1b', - data: { + data: expect.objectContaining({ 'sentry.origin': 'auto.faas.cloudflare.workflow', 'sentry.op': 'function.step.do', 'sentry.source': 'task', 'sentry.sample_rate': 1, - }, + }), op: 'function.step.do', status: 'ok', origin: 'auto.faas.cloudflare.workflow', - }, + }), cloud_resource: { 'cloud.provider': 'cloudflare' }, runtime: { name: 'cloudflare' }, }, @@ -311,7 +313,9 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { } } - test('Captures step errors', async () => { + test('Does not capture error on non-final retry attempt (step eventually succeeds)', async () => { + // ErrorTestWorkflow fails on first attempt, succeeds on second + // With step context (attempt 1, limit 3), first failure is not final, so no error captured const TestWorkflowInstrumented = instrumentWorkflowWithSentry(getSentryOptions, ErrorTestWorkflow as any); const workflow = new TestWorkflowInstrumented(mockContext, {}) as ErrorTestWorkflow; const event = { payload: {}, timestamp: new Date(), instanceId: INSTANCE_ID }; @@ -319,54 +323,14 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { expect(mockStep.do).toHaveBeenCalledTimes(1); expect(mockStep.do).toHaveBeenCalledWith('sometimes error step', expect.any(Function)); - // One flush for the error transaction, one for the retry success, one at end of run + // One flush for the failed attempt, one for the retry success, one at end of run expect(mockContext.waitUntil).toHaveBeenCalledTimes(3); expect(mockContext.waitUntil).toHaveBeenCalledWith(expect.any(Promise)); - // error event + failed transaction + successful retry transaction - expect(mockTransport.send).toHaveBeenCalledTimes(3); + // No error event (not final attempt), only failed transaction + successful retry transaction + expect(mockTransport.send).toHaveBeenCalledTimes(2); - // First we should get the error event + // First: the failed transaction (no error event since not final attempt) expect(mockTransport.send).toHaveBeenNthCalledWith(1, [ - expect.objectContaining({ - trace: expect.objectContaining({ - transaction: 'sometimes error step', - trace_id: TRACE_ID, - sample_rand: SAMPLE_RAND, - }), - }), - [ - [ - { - type: 'event', - }, - expect.objectContaining({ - event_id: expect.any(String), - contexts: { - trace: { - parent_span_id: undefined, - span_id: expect.any(String), - trace_id: TRACE_ID, - }, - cloud_resource: { 'cloud.provider': 'cloudflare' }, - runtime: { name: 'cloudflare' }, - }, - timestamp: expect.any(Number), - exception: { - values: [ - expect.objectContaining({ - type: 'Error', - value: 'Test error', - mechanism: { type: 'auto.faas.cloudflare.workflow', handled: true }, - }), - ], - }, - }), - ], - ], - ]); - - // The the failed transaction - expect(mockTransport.send).toHaveBeenNthCalledWith(2, [ expect.objectContaining({ trace: expect.objectContaining({ transaction: 'sometimes error step', @@ -382,20 +346,20 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { expect.objectContaining({ event_id: expect.any(String), contexts: { - trace: { + trace: expect.objectContaining({ parent_span_id: undefined, span_id: expect.any(String), trace_id: TRACE_ID, - data: { + data: expect.objectContaining({ 'sentry.origin': 'auto.faas.cloudflare.workflow', 'sentry.op': 'function.step.do', 'sentry.source': 'task', 'sentry.sample_rate': 1, - }, + }), op: 'function.step.do', status: 'internal_error', origin: 'auto.faas.cloudflare.workflow', - }, + }), cloud_resource: { 'cloud.provider': 'cloudflare' }, runtime: { name: 'cloudflare' }, }, @@ -408,8 +372,8 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { ], ]); - // The the successful transaction - expect(mockTransport.send).toHaveBeenNthCalledWith(3, [ + // Second: the successful transaction + expect(mockTransport.send).toHaveBeenNthCalledWith(2, [ expect.objectContaining({ trace: expect.objectContaining({ transaction: 'sometimes error step', @@ -425,20 +389,20 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { expect.objectContaining({ event_id: expect.any(String), contexts: { - trace: { + trace: expect.objectContaining({ parent_span_id: undefined, span_id: expect.any(String), trace_id: TRACE_ID, - data: { + data: expect.objectContaining({ 'sentry.origin': 'auto.faas.cloudflare.workflow', 'sentry.op': 'function.step.do', 'sentry.source': 'task', 'sentry.sample_rate': 1, - }, + }), op: 'function.step.do', status: 'ok', origin: 'auto.faas.cloudflare.workflow', - }, + }), cloud_resource: { 'cloud.provider': 'cloudflare' }, runtime: { name: 'cloudflare' }, }, @@ -452,7 +416,127 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { ]); }); - test('Sampled random via instanceId', async () => { + test('emits a single error event on final retry attempt (with step context)', async () => { + const retryLimit = 2; + // Mock that always throws — simulates a step that fails through every retry + // Passes step context with incrementing attempt and config.retries.limit + const alwaysFailStep: WorkflowStep = { + do: vi + .fn() + .mockImplementation( + async ( + _name: string, + configOrCallback: WorkflowStepConfig | ((...args: unknown[]) => Promise), + maybeCallback?: (...args: unknown[]) => Promise, + ) => { + const callback = (typeof configOrCallback === 'function' ? configOrCallback : maybeCallback)!; + // Simulate attempts: 1 (initial) + 2 retries = 3 total, then surface the final error + let lastError: unknown; + for (let attempt = 1; attempt <= retryLimit + 1; attempt++) { + try { + return await callback({ attempt, config: { retries: { limit: retryLimit }, timeout: 60000 } }); + } catch (err) { + lastError = err; + } + } + throw lastError; + }, + ), + sleep: vi.fn(), + sleepUntil: vi.fn(), + waitForEvent: vi.fn(), + }; + + class AlwaysFailWorkflow { + constructor(_ctx: ExecutionContext, _env: unknown) {} + + async run(_event: Readonly>, step: WorkflowStep): Promise { + await step.do('always failing step', async () => { + throw new Error('boom'); + }); + } + } + + const TestWorkflowInstrumented = instrumentWorkflowWithSentry(() => getSentryOptions(), AlwaysFailWorkflow as any); + const workflow = new TestWorkflowInstrumented(mockContext, {}) as AlwaysFailWorkflow; + const event = { payload: {}, timestamp: new Date(), instanceId: INSTANCE_ID }; + await expect(workflow.run(event, alwaysFailStep)).rejects.toThrow('boom'); + + // Exactly one error event on the final attempt (attempt 3 > limit 2) + const errorEnvelopes = mockTransport.send.mock.calls.filter(call => { + const items = (call[0] as any)[1] as any[]; + return items.some(i => i[0].type === 'event'); + }); + expect(errorEnvelopes).toHaveLength(1); + expect(errorEnvelopes[0]![0][1][0][1]).toMatchObject({ + exception: { + values: [ + expect.objectContaining({ + type: 'Error', + value: 'boom', + mechanism: { type: 'auto.faas.cloudflare.workflow', handled: true }, + }), + ], + }, + }); + }); + + test('emits error per attempt on older Cloudflare without step context', async () => { + // Mock that always throws — simulates a step that fails through every retry + // Does NOT pass step context (simulates older Cloudflare versions) + const alwaysFailStepNoContext: WorkflowStep = { + do: vi + .fn() + .mockImplementation( + async ( + _name: string, + configOrCallback: WorkflowStepConfig | ((...args: unknown[]) => Promise), + maybeCallback?: (...args: unknown[]) => Promise, + ) => { + const callback = (typeof configOrCallback === 'function' ? configOrCallback : maybeCallback)!; + // Simulate 3 retry attempts that all fail, no step context passed + let lastError: unknown; + for (let i = 0; i < 3; i++) { + try { + return await callback(); + } catch (err) { + lastError = err; + } + } + throw lastError; + }, + ), + sleep: vi.fn(), + sleepUntil: vi.fn(), + waitForEvent: vi.fn(), + }; + + class AlwaysFailWorkflow { + constructor(_ctx: ExecutionContext, _env: unknown) {} + + async run(_event: Readonly>, step: WorkflowStep): Promise { + await step.do('always failing step', async () => { + throw new Error('boom'); + }); + } + } + + const TestWorkflowInstrumented = instrumentWorkflowWithSentry(() => getSentryOptions(), AlwaysFailWorkflow as any); + const workflow = new TestWorkflowInstrumented(mockContext, {}) as AlwaysFailWorkflow; + const event = { payload: {}, timestamp: new Date(), instanceId: INSTANCE_ID }; + await expect(workflow.run(event, alwaysFailStepNoContext)).rejects.toThrow('boom'); + + // 3 error events, one per attempt (legacy behavior without step context) + const errorEnvelopes = mockTransport.send.mock.calls.filter(call => { + const items = (call[0] as any)[1] as any[]; + return items.some(i => i[0].type === 'event'); + }); + expect(errorEnvelopes).toHaveLength(3); + }); + + test('Sampled random via instanceId (no events when sampled out and no final error)', async () => { + // ErrorTestWorkflow fails once then succeeds. With step context, non-final errors + // are not captured. With low tracesSampleRate, transactions are also not sent. const TestWorkflowInstrumented = instrumentWorkflowWithSentry( // Override the tracesSampleRate to 0.4 to be below the sampleRand // calculated from the instanceId @@ -465,50 +549,11 @@ describe.skipIf(NODE_MAJOR_VERSION < 20)('workflows', () => { expect(mockStep.do).toHaveBeenCalledTimes(1); expect(mockStep.do).toHaveBeenCalledWith('sometimes error step', expect.any(Function)); - // One flush for the error event and one at end of run expect(mockContext.waitUntil).toHaveBeenCalledTimes(3); expect(mockContext.waitUntil).toHaveBeenCalledWith(expect.any(Promise)); - // We should get the error event and then nothing else. No transactions should be sent - expect(mockTransport.send).toHaveBeenCalledTimes(1); - - expect(mockTransport.send).toHaveBeenCalledWith([ - expect.objectContaining({ - trace: expect.objectContaining({ - transaction: 'sometimes error step', - trace_id: TRACE_ID, - sample_rand: SAMPLE_RAND, - }), - }), - [ - [ - { - type: 'event', - }, - expect.objectContaining({ - event_id: expect.any(String), - contexts: { - trace: { - parent_span_id: undefined, - span_id: expect.any(String), - trace_id: TRACE_ID, - }, - cloud_resource: { 'cloud.provider': 'cloudflare' }, - runtime: { name: 'cloudflare' }, - }, - timestamp: expect.any(Number), - exception: { - values: [ - expect.objectContaining({ - type: 'Error', - value: 'Test error', - }), - ], - }, - }), - ], - ], - ]); + // No error events (not final attempt) and no transactions (sampled out) + expect(mockTransport.send).toHaveBeenCalledTimes(0); }); test('Forwards step context (ctx) to user callback', async () => { diff --git a/yarn.lock b/yarn.lock index 6ae4d0efbae3..3d6562a537c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2931,6 +2931,11 @@ resolved "https://registry.yarnpkg.com/@cloudflare/unenv-preset/-/unenv-preset-2.12.0.tgz#3448ce6a88a8f917a3d49c916b0bc48393f50a32" integrity sha512-NK4vN+2Z/GbfGS4BamtbbVk1rcu5RmqaYGiyHJQrA09AoxdZPHDF3W/EhgI0YSK8p3vRo/VNCtbSJFPON7FWMQ== +"@cloudflare/unenv-preset@2.16.1": + version "2.16.1" + resolved "https://registry.yarnpkg.com/@cloudflare/unenv-preset/-/unenv-preset-2.16.1.tgz#d4a3df263ddbfde855bca268be79ea6062856a54" + integrity sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw== + "@cloudflare/workerd-darwin-64@1.20260124.0": version "1.20260124.0" resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260124.0.tgz#958e475f8a5fce1d9453d47b98c09526f1a45438" @@ -2941,6 +2946,11 @@ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260131.0.tgz#1c295393b261ed27c85be1d99e3ce63d7f2e88e8" integrity sha512-+1X4qErc715NUhJZNhtlpuCxajhD5YNre7Cz50WPMmj+BMUrh9h7fntKEadtrUo5SM2YONY7CDzK7wdWbJJBVA== +"@cloudflare/workerd-darwin-64@1.20260426.1": + version "1.20260426.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260426.1.tgz#36e413967f6a501be692ad5832059d7b20378718" + integrity sha512-Ch7DqsmYzSQRTY87pZpsGsFVz9VVBnLPnCBOHxKt1HH25a7oMu1w1PbPWqVmE0VerCLsj/TScX7Ob3v6E14TZw== + "@cloudflare/workerd-darwin-arm64@1.20260124.0": version "1.20260124.0" resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260124.0.tgz#8dad514564bcadc2fb5ac449bc24837d3d1533f5" @@ -2951,6 +2961,11 @@ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260131.0.tgz#fcead80d4052f4bf243970e9eaf47b85ddcafa28" integrity sha512-M84mXR8WEMEBuX4/dL2IQ4wHV/ALwYjx9if5ePZR8rdbD7if/fkEEoMBq0bGS/1gMLRqqCZLstabxHV+g92NNg== +"@cloudflare/workerd-darwin-arm64@1.20260426.1": + version "1.20260426.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260426.1.tgz#f37eddbd1f3e61849a22bdb5b7bb5552ea2ad942" + integrity sha512-0m0U8vaPRH25SpKjbSyRql6gmPe4rCsETRV2WW0qBnuMdKNr5Vh5/Uez80xVrfiCCRMTULGeg63Nqg2vg6CDOA== + "@cloudflare/workerd-linux-64@1.20260124.0": version "1.20260124.0" resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260124.0.tgz#91845c0f67c73abc2959c3ab90b474cd88e6d6ec" @@ -2961,6 +2976,11 @@ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260131.0.tgz#81a590e0c306f746c37494d7211e78d9bf7fc333" integrity sha512-SWzr48bCL9y5wjkj23tXS6t/6us99EAH9T5TAscMV0hfJFZQt97RY/gaHKyRRjFv6jfJZvk7d4g+OmGeYBnwcg== +"@cloudflare/workerd-linux-64@1.20260426.1": + version "1.20260426.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260426.1.tgz#dc69dfd1a2fa83d625be625a4cb6d871fc75804c" + integrity sha512-C8LlC8uSYzg49y51n++75esxZmMp+Uz1OKHHA/4lkv6rjOTbcHQJuEwSLppjybVIXpv7A8MBhbu9iyCTvyv1mw== + "@cloudflare/workerd-linux-arm64@1.20260124.0": version "1.20260124.0" resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260124.0.tgz#42baebe126d430ae1dfeb23002a456239f24305b" @@ -2971,6 +2991,11 @@ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260131.0.tgz#3146ab166a14863f725fd42539f248d92970a935" integrity sha512-mL0kLPGIBJRPeHS3+erJ2t5dJT3ODhsKvR9aA4BcsY7M30/QhlgJIF6wsgwNisTJ23q8PbobZNHBUKIe8l/E9A== +"@cloudflare/workerd-linux-arm64@1.20260426.1": + version "1.20260426.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260426.1.tgz#5089971a9dd7592d1b9a68c2a9e0c77e0c86426b" + integrity sha512-ESVp/OIFMAqjQsa8BOP2BQQz5Vpfv6ncN6lNnIuNeOgsISQBdYk+LA60bwQHMud9tvmnSYtONp1zkZ8OQz+x6w== + "@cloudflare/workerd-windows-64@1.20260124.0": version "1.20260124.0" resolved "https://registry.yarnpkg.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260124.0.tgz#2721810ca8bcbd2b9dbaf4fb13a971b8ea9e4c21" @@ -2981,11 +3006,21 @@ resolved "https://registry.yarnpkg.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260131.0.tgz#05d877eb8a0eab80d2e8a3d06ff7d23f6365e0f3" integrity sha512-hoQqTFBpP1zntP2OQSpt5dEWbd9vSBliK+G7LmDXjKitPkmkRFo2PB4P9aBRE1edPAIO/fpdoJv928k2HaAn4A== -"@cloudflare/workers-types@4.20250922.0", "@cloudflare/workers-types@^4.20250922.0": +"@cloudflare/workerd-windows-64@1.20260426.1": + version "1.20260426.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260426.1.tgz#b21d2a24afe0b274982d7ccbe7163a5689da1507" + integrity sha512-d3Xj/IjINRgNVwH+eKhpUn4xkkcEewbWXbOvBlapiirKWh5zl9m0Epi3qOqmjyRYK6MICqIGXg4qZBEt0lxudw== + +"@cloudflare/workers-types@4.20250922.0": version "4.20250922.0" resolved "https://registry.yarnpkg.com/@cloudflare/workers-types/-/workers-types-4.20250922.0.tgz#a159fbf3bb785fa85b473ecfaa8c501525827885" integrity sha512-BaqlKnVc0Xzqm9xt3TC4v0yB9EHy5vVqpiWz+DAsbEmdcpUbqdBschvI9502p6FgFbZElD7XcxTEeViXLsoO0A== +"@cloudflare/workers-types@^4.20260426.0": + version "4.20260519.1" + resolved "https://registry.yarnpkg.com/@cloudflare/workers-types/-/workers-types-4.20260519.1.tgz#061b4594e874a0e506ddc6599221939e6718d2a7" + integrity sha512-BMWAwg4RyyZn3zcdoXbqpfogm2DGfNb83DXNCM1oFUMhYtEX8I+B+oxf67YPKvSiAEbzd7nHzW2mLv3eBH8Etw== + "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -3322,6 +3357,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz#1d8be43489a961615d49e037f1bfa0f52a773737" integrity sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A== +"@esbuild/aix-ppc64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz#815b39267f9bffd3407ea6c376ac32946e24f8d2" + integrity sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg== + "@esbuild/aix-ppc64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz#7a289c158e29cbf59ea0afc83cc80f06d1c89402" @@ -3362,6 +3402,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz#bd1763194aad60753fa3338b1ba9bda974b58724" integrity sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ== +"@esbuild/android-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz#19b882408829ad8e12b10aff2840711b2da361e8" + integrity sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg== + "@esbuild/android-arm64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz#b8828d9edfa3a92660644eb8de6e4f3c203d7b17" @@ -3407,6 +3452,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.0.tgz#69c7b57f02d3b3618a5ba4f82d127b57665dc397" integrity sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ== +"@esbuild/android-arm@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.3.tgz#90be58de27915efa27b767fcbdb37a4470627d7b" + integrity sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA== + "@esbuild/android-arm@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.28.0.tgz#5ec1847605e05b5dbe5df90db9ff7e3e4c58dca7" @@ -3447,6 +3497,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.0.tgz#6ea22b5843acb23243d0126c052d7d3b6a11ca90" integrity sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q== +"@esbuild/android-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.3.tgz#d7dcc976f16e01a9aaa2f9b938fbec7389f895ac" + integrity sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ== + "@esbuild/android-x64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.28.0.tgz#390642175b88ef82bad4cce03f8ab13fe9b1912e" @@ -3487,6 +3542,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz#5ad7c02bc1b1a937a420f919afe40665ba14ad1e" integrity sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg== +"@esbuild/darwin-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz#9f6cac72b3a8532298a6a4493ed639a8988e8abd" + integrity sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg== + "@esbuild/darwin-arm64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz#ae45325960d5950cd6951e4f97396f4e1ff7d8d3" @@ -3527,6 +3587,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz#48470c83c5fd6d1fc7c823c2c603aeee96e101c9" integrity sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g== +"@esbuild/darwin-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz#ac61d645faa37fd650340f1866b0812e1fb14d6a" + integrity sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg== + "@esbuild/darwin-x64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz#c079247d589b6b99449659d94f06951b84bff2e4" @@ -3567,6 +3632,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz#d5a8effd8b0be7be613cd1009da34d629d4c2457" integrity sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw== +"@esbuild/freebsd-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz#b8625689d73cf1830fe58c39051acdc12474ea1b" + integrity sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w== + "@esbuild/freebsd-arm64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz#45c456215a486593c94900297202dc11c880a37a" @@ -3607,6 +3677,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz#9bde638bda31aa244d6d64dbafafb41e6e799bcc" integrity sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g== +"@esbuild/freebsd-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz#07be7dd3c9d42fe0eccd2ab9f9ded780bc53bead" + integrity sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA== + "@esbuild/freebsd-x64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz#0399494c1c85e4388e9b7040bd60d48f2a5b0d2c" @@ -3647,6 +3722,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz#96008c3a207d8ca495708db714c475ea5bf7e2af" integrity sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ== +"@esbuild/linux-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz#bf31918fe5c798586460d2b3d6c46ed2c01ca0b6" + integrity sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg== + "@esbuild/linux-arm64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz#d6d9f09ef0de54116bf459a4d53cac7e0952fe39" @@ -3687,6 +3767,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz#9b47cb0f222e567af316e978c7f35307db97bc0e" integrity sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ== +"@esbuild/linux-arm@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz#28493ee46abec1dc3f500223cd9f8d2df08f9d11" + integrity sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw== + "@esbuild/linux-arm@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz#7b42ffa84c288ae94fdc431c1b28a89e3c3b9278" @@ -3727,6 +3812,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz#d1e1e38d406cbdfb8a49f4eca0c25bbc344e18cc" integrity sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw== +"@esbuild/linux-ia32@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz#750752a8b30b43647402561eea764d0a41d0ee29" + integrity sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg== + "@esbuild/linux-ia32@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz#deb15d112ed8dd605346b6b953d23a21ff81253f" @@ -3777,6 +3867,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz#c13bc6a53e3b69b76f248065bebee8415b44dfce" integrity sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg== +"@esbuild/linux-loong64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz#a5a92813a04e71198c50f05adfaf18fc1e95b9ed" + integrity sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA== + "@esbuild/linux-loong64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz#81fb89d07eecc79b157dea61033757726fce0ca4" @@ -3817,6 +3912,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz#05f8322eb0a96ce1bfbc59691abe788f71e2d217" integrity sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg== +"@esbuild/linux-mips64el@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz#deb45d7fd2d2161eadf1fbc593637ed766d50bb1" + integrity sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw== + "@esbuild/linux-mips64el@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz#d0e42691b3ff7af9fb2217b70fc01f343bdb62bb" @@ -3857,6 +3957,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz#6fc5e7af98b4fb0c6a7f0b73ba837ce44dc54980" integrity sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA== +"@esbuild/linux-ppc64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz#6f39ae0b8c4d3d2d61a65b26df79f6e12a1c3d78" + integrity sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA== + "@esbuild/linux-ppc64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz#389f3e5e98f17d477c467cc87136e1a076eead87" @@ -3897,6 +4002,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz#508afa9f69a3f97368c0bf07dd894a04af39d86e" integrity sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ== +"@esbuild/linux-riscv64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz#4c5c19c3916612ec8e3915187030b9df0b955c1d" + integrity sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ== + "@esbuild/linux-riscv64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz#763bd60d59b242be12da1e67d5729f3024c605fa" @@ -3937,6 +4047,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz#21fda656110ee242fc64f87a9e0b0276d4e4ec5b" integrity sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w== +"@esbuild/linux-s390x@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz#9ed17b3198fa08ad5ccaa9e74f6c0aff7ad0156d" + integrity sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw== + "@esbuild/linux-s390x@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz#aac6061634872e4677de693bce8030d73b1fd055" @@ -3977,6 +4092,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz#1758a85dcc09b387fd57621643e77b25e0ccba59" integrity sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw== +"@esbuild/linux-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz#12383dcbf71b7cf6513e58b4b08d95a710bf52a5" + integrity sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA== + "@esbuild/linux-x64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz#4f2917747188fe77632bcec65b2d84b422419779" @@ -3992,6 +4112,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz#a0131159f4db6e490da35cc4bb51ef0d03b7848a" integrity sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w== +"@esbuild/netbsd-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz#dd0cb2fa543205fcd931df44f4786bfcce6df7d7" + integrity sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA== + "@esbuild/netbsd-arm64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz#814df0ae57a0c386814491b8397eeba82094a947" @@ -4032,6 +4157,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz#6f4877d7c2ba425a2b80e4330594e0b43caa2d7d" integrity sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA== +"@esbuild/netbsd-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz#028ad1807a8e03e155153b2d025b506c3787354b" + integrity sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA== + "@esbuild/netbsd-x64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz#e01bdf7e60fa1a08e46d46d960b0d9bb8ac210af" @@ -4052,6 +4182,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz#cbefbd4c2f375cebeb4f965945be6cf81331bd01" integrity sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ== +"@esbuild/openbsd-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz#e3c16ff3490c9b59b969fffca87f350ffc0e2af5" + integrity sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw== + "@esbuild/openbsd-arm64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz#4a15c36aacca68d2d5a4c90b710c06759f4c1ffa" @@ -4092,6 +4227,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz#31fa9e8649fc750d7c2302c8b9d0e1547f57bc84" integrity sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A== +"@esbuild/openbsd-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz#c5a4693fcb03d1cbecbf8b422422468dfc0d2a8b" + integrity sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ== + "@esbuild/openbsd-x64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz#475e6101498a8ecce3008d7c388111d7a27c17bd" @@ -4107,6 +4247,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz#03727780f1fdf606e7b56193693a715d9f1ee001" integrity sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA== +"@esbuild/openharmony-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz#082082444f12db564a0775a41e1991c0e125055e" + integrity sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g== + "@esbuild/openharmony-arm64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz#cfdc3957f0b7a69f1bde129aad17fcc2f6fa033e" @@ -4147,6 +4292,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz#866a35f387234a867ced35af8906dfffb073b9ff" integrity sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA== +"@esbuild/sunos-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz#5ab036c53f929e8405c4e96e865a424160a1b537" + integrity sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA== + "@esbuild/sunos-x64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz#a013c856fecacd1c3aec985c8afe1d1cb017497d" @@ -4187,6 +4337,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz#53de43a9629b8a34678f28cd56cc104db1b67abb" integrity sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg== +"@esbuild/win32-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz#38de700ef4b960a0045370c171794526e589862e" + integrity sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA== + "@esbuild/win32-arm64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz#eae05e0f35271cad3898b43168d3e9a3bbaf47e5" @@ -4227,6 +4382,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz#924d2aed8692fea5d27bfb6500f9b8b9c1a34af4" integrity sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ== +"@esbuild/win32-ia32@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz#451b93dc03ec5d4f38619e6cd64d9f9eff06f55c" + integrity sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q== + "@esbuild/win32-ia32@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz#06161ebc5bf75c08d69feb3c6b22560515913998" @@ -4267,6 +4427,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz#64995295227e001f2940258617c6674efb3ac48d" integrity sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg== +"@esbuild/win32-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz#0eaf705c941a218a43dba8e09f1df1d6cd2f1f17" + integrity sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA== + "@esbuild/win32-x64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz#04d90d5752b4ce65d2b6ac25eba08ff7624fe07c" @@ -15939,6 +16104,38 @@ esbuild@0.27.0: "@esbuild/win32-ia32" "0.27.0" "@esbuild/win32-x64" "0.27.0" +esbuild@0.27.3: + version "0.27.3" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.3.tgz#5859ca8e70a3af956b26895ce4954d7e73bd27a8" + integrity sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg== + optionalDependencies: + "@esbuild/aix-ppc64" "0.27.3" + "@esbuild/android-arm" "0.27.3" + "@esbuild/android-arm64" "0.27.3" + "@esbuild/android-x64" "0.27.3" + "@esbuild/darwin-arm64" "0.27.3" + "@esbuild/darwin-x64" "0.27.3" + "@esbuild/freebsd-arm64" "0.27.3" + "@esbuild/freebsd-x64" "0.27.3" + "@esbuild/linux-arm" "0.27.3" + "@esbuild/linux-arm64" "0.27.3" + "@esbuild/linux-ia32" "0.27.3" + "@esbuild/linux-loong64" "0.27.3" + "@esbuild/linux-mips64el" "0.27.3" + "@esbuild/linux-ppc64" "0.27.3" + "@esbuild/linux-riscv64" "0.27.3" + "@esbuild/linux-s390x" "0.27.3" + "@esbuild/linux-x64" "0.27.3" + "@esbuild/netbsd-arm64" "0.27.3" + "@esbuild/netbsd-x64" "0.27.3" + "@esbuild/openbsd-arm64" "0.27.3" + "@esbuild/openbsd-x64" "0.27.3" + "@esbuild/openharmony-arm64" "0.27.3" + "@esbuild/sunos-x64" "0.27.3" + "@esbuild/win32-arm64" "0.27.3" + "@esbuild/win32-ia32" "0.27.3" + "@esbuild/win32-x64" "0.27.3" + esbuild@^0.15.0: version "0.15.18" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.18.tgz#ea894adaf3fbc036d32320a00d4d6e4978a2f36d" @@ -21728,6 +21925,18 @@ miniflare@4.20260131.0: ws "8.18.0" youch "4.1.0-beta.10" +miniflare@4.20260426.0: + version "4.20260426.0" + resolved "https://registry.yarnpkg.com/miniflare/-/miniflare-4.20260426.0.tgz#b910444fd19106d897f0cbd117c91cec241cb426" + integrity sha512-KM+v76d04qT+NsPfVKVQEgnnuLNE3uzCCl2QKMTJ5OXor5JbBm1vpkQwQ+l7o5ELCrZ74RnyKhJKLiJyUA39Tw== + dependencies: + "@cspotcode/source-map-support" "0.8.1" + sharp "^0.34.5" + undici "7.24.8" + workerd "1.20260426.1" + ws "8.18.0" + youch "4.1.0-beta.10" + minimalistic-assert@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -29203,6 +29412,11 @@ undici@7.18.2: resolved "https://registry.yarnpkg.com/undici/-/undici-7.18.2.tgz#6cf724ef799a67d94fd55adf66b1e184176efcdf" integrity sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw== +undici@7.24.8: + version "7.24.8" + resolved "https://registry.yarnpkg.com/undici/-/undici-7.24.8.tgz#8207d06a68955d4420e50468f2749e940b3eb4cf" + integrity sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ== + undici@^5.25.4: version "5.29.0" resolved "https://registry.yarnpkg.com/undici/-/undici-5.29.0.tgz#419595449ae3f2cdcba3580a2e8903399bd1f5a3" @@ -30711,6 +30925,17 @@ workerd@1.20260131.0: "@cloudflare/workerd-linux-arm64" "1.20260131.0" "@cloudflare/workerd-windows-64" "1.20260131.0" +workerd@1.20260426.1: + version "1.20260426.1" + resolved "https://registry.yarnpkg.com/workerd/-/workerd-1.20260426.1.tgz#a2152c6ef4fbaf0b43b63eb52317893e8b450293" + integrity sha512-ELvGgN8c9oo+E6EPyecxk1TEf6/eAK4TxxQTW5mQ87C7jbjCzhMbg0P2ije49UBHV0dkBYPJcJvcklUltipl2A== + optionalDependencies: + "@cloudflare/workerd-darwin-64" "1.20260426.1" + "@cloudflare/workerd-darwin-arm64" "1.20260426.1" + "@cloudflare/workerd-linux-64" "1.20260426.1" + "@cloudflare/workerd-linux-arm64" "1.20260426.1" + "@cloudflare/workerd-windows-64" "1.20260426.1" + workerpool@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-3.1.2.tgz#b34e79243647decb174b7481ab5b351dc565c426" @@ -30757,6 +30982,22 @@ wrangler@4.62.0: optionalDependencies: fsevents "~2.3.2" +wrangler@4.86.0: + version "4.86.0" + resolved "https://registry.yarnpkg.com/wrangler/-/wrangler-4.86.0.tgz#e56b041c624778bcab609a755a0e86d6e95bf3ea" + integrity sha512-9aa/gbF/HiUeeUEwyQpW5LDPBEzyt7iaE6xHwm0vk2Ly8A6J+jh03pzchqVnCCWR832mNyA28MD8oAYt0Kfvlw== + dependencies: + "@cloudflare/kv-asset-handler" "0.4.2" + "@cloudflare/unenv-preset" "2.16.1" + blake3-wasm "2.1.5" + esbuild "0.27.3" + miniflare "4.20260426.0" + path-to-regexp "6.3.0" + unenv "2.0.0-rc.24" + workerd "1.20260426.1" + optionalDependencies: + fsevents "~2.3.2" + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" From ea2b9b21b90beffc38137a0f30ecffcdb9f2ef56 Mon Sep 17 00:00:00 2001 From: JPeer264 Date: Thu, 21 May 2026 11:17:21 +0200 Subject: [PATCH 2/2] fixup! feat(cloudflare): Only capture workflow step error on final retry attempt --- .../suites/tracing/workflow/test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/workflow/test.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/workflow/test.ts index 568b744f9555..1ae37f87d64b 100644 --- a/dev-packages/cloudflare-integration-tests/suites/tracing/workflow/test.ts +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/workflow/test.ts @@ -29,6 +29,7 @@ it('Workflow steps create transactions with correct attributes', async ({ signal [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.workflow', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'task', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, + 'cloudflare.workflow.attempt': 1, }, }, }), @@ -55,6 +56,7 @@ it('Workflow steps create transactions with correct attributes', async ({ signal [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.faas.cloudflare.workflow', [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'task', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, + 'cloudflare.workflow.attempt': 1, }, }, }),