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/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, }, }, }), 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"