diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 544bb7900008..5f3d5bb90bfa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -164,6 +164,9 @@ jobs: changed_browser_integration: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry-internal/browser-integration-tests') }} + changed_effect_v3_compatibility: + ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, + '@sentry-internal/effect-3-compatibility-tests') }} job_check_branches: name: Check PR branches @@ -749,6 +752,29 @@ jobs: working-directory: dev-packages/node-core-integration-tests run: yarn test + job_effect_v3_compatibility_tests: + name: Effect v3 Compatibility Tests + needs: [job_get_metadata, job_build] + if: needs.job_build.outputs.changed_effect_v3_compatibility == 'true' || github.event_name != 'pull_request' + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v6 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v6 + with: + node-version-file: 'package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: Run tests + working-directory: dev-packages/effect-3-compatibility-tests + run: yarn test + job_cloudflare_integration_tests: name: Cloudflare Integration Tests needs: [job_get_metadata, job_build] @@ -1123,6 +1149,7 @@ jobs: job_node_unit_tests, job_node_integration_tests, job_node_core_integration_tests, + job_effect_v3_compatibility_tests, job_cloudflare_integration_tests, job_bun_integration_tests, job_browser_playwright_tests, diff --git a/dev-packages/effect-3-compatibility-tests/package.json b/dev-packages/effect-3-compatibility-tests/package.json new file mode 100644 index 000000000000..3df3ae28e93d --- /dev/null +++ b/dev-packages/effect-3-compatibility-tests/package.json @@ -0,0 +1,28 @@ +{ + "name": "@sentry-internal/effect-3-compatibility-tests", + "version": "10.49.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "private": true, + "scripts": { + "lint": "oxlint . --type-aware", + "lint:fix": "oxlint . --fix --type-aware", + "type-check": "tsc", + "test": "vitest run", + "test:watch": "vitest --watch" + }, + "dependencies": { + "@sentry/core": "link:../../packages/core", + "@sentry/effect": "link:../../packages/effect" + }, + "devDependencies": { + "@effect/vitest": "^0.29.0", + "effect": "^3.21.1", + "vitest": "^3.2.4" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/dev-packages/effect-3-compatibility-tests/test/index.test.ts b/dev-packages/effect-3-compatibility-tests/test/index.test.ts new file mode 100644 index 000000000000..ea13276a9bbe --- /dev/null +++ b/dev-packages/effect-3-compatibility-tests/test/index.test.ts @@ -0,0 +1,8 @@ +import { describe, expect, it } from 'vitest'; +import * as index from '@sentry/effect/client'; + +describe('effect index export', () => { + it('has correct exports', () => { + expect(index.captureException).toBeDefined(); + }); +}); diff --git a/dev-packages/effect-3-compatibility-tests/test/layer.test.ts b/dev-packages/effect-3-compatibility-tests/test/layer.test.ts new file mode 100644 index 000000000000..3e458d69f897 --- /dev/null +++ b/dev-packages/effect-3-compatibility-tests/test/layer.test.ts @@ -0,0 +1,180 @@ +import { describe, expect, it } from '@effect/vitest'; +import * as sentryCore from '@sentry/core'; +import { getClient, getCurrentScope, getIsolationScope, SDK_VERSION } from '@sentry/core'; +import { Effect, Layer, Logger, LogLevel } from 'effect'; +import { afterEach, beforeEach, vi } from 'vitest'; +import * as sentryClient from '@sentry/effect/client'; +import * as sentryServer from '@sentry/effect/server'; + +const TEST_DSN = 'https://username@domain/123'; + +function getMockTransport() { + return () => ({ + send: vi.fn().mockResolvedValue({}), + flush: vi.fn().mockResolvedValue(true), + }); +} + +describe.each([ + [ + { + subSdkName: 'browser', + effectLayer: sentryClient.effectLayer, + SentryEffectTracer: sentryClient.SentryEffectTracer, + SentryEffectLogger: sentryClient.SentryEffectLogger, + SentryEffectMetricsLayer: sentryClient.SentryEffectMetricsLayer, + }, + ], + [ + { + subSdkName: 'node-light', + effectLayer: sentryServer.effectLayer, + SentryEffectTracer: sentryServer.SentryEffectTracer, + SentryEffectLogger: sentryServer.SentryEffectLogger, + SentryEffectMetricsLayer: sentryServer.SentryEffectMetricsLayer, + }, + ], +])('effectLayer ($subSdkName)', ({ subSdkName, effectLayer, SentryEffectTracer, SentryEffectLogger }) => { + beforeEach(() => { + getCurrentScope().clear(); + getIsolationScope().clear(); + }); + + afterEach(() => { + getCurrentScope().setClient(undefined); + vi.restoreAllMocks(); + }); + + it('creates a valid Effect layer', () => { + const layer = effectLayer({ + dsn: TEST_DSN, + transport: getMockTransport(), + }); + + expect(layer).toBeDefined(); + expect(Layer.isLayer(layer)).toBe(true); + }); + + it.effect('applies SDK metadata', () => + Effect.gen(function* () { + yield* Effect.void; + + const client = getClient(); + const metadata = client?.getOptions()._metadata?.sdk; + + expect(metadata?.name).toBe('sentry.javascript.effect'); + expect(metadata?.packages).toEqual([ + { name: 'npm:@sentry/effect', version: SDK_VERSION }, + { name: `npm:@sentry/${subSdkName}`, version: SDK_VERSION }, + ]); + }).pipe( + Effect.provide( + effectLayer({ + dsn: TEST_DSN, + transport: getMockTransport(), + }), + ), + ), + ); + + it.effect('layer can be provided to an Effect program', () => + Effect.gen(function* () { + const result = yield* Effect.succeed('test-result'); + expect(result).toBe('test-result'); + }).pipe( + Effect.provide( + effectLayer({ + dsn: TEST_DSN, + transport: getMockTransport(), + }), + ), + ), + ); + + it.effect('layer enables tracing when tracer is set', () => + Effect.gen(function* () { + const startInactiveSpanMock = vi.spyOn(sentryCore, 'startInactiveSpan'); + + const result = yield* Effect.withSpan('test-span')(Effect.succeed('traced')); + expect(result).toBe('traced'); + expect(startInactiveSpanMock).toHaveBeenCalledWith(expect.objectContaining({ name: 'test-span' })); + }).pipe( + Effect.withTracer(SentryEffectTracer), + Effect.provide( + effectLayer({ + dsn: TEST_DSN, + transport: getMockTransport(), + }), + ), + ), + ); + + it.effect('layer can be composed with tracer layer', () => + Effect.gen(function* () { + const startInactiveSpanMock = vi.spyOn(sentryCore, 'startInactiveSpan'); + + const result = yield* Effect.succeed(42).pipe( + Effect.map(n => n * 2), + Effect.withSpan('computation'), + ); + expect(result).toBe(84); + expect(startInactiveSpanMock).toHaveBeenCalledWith(expect.objectContaining({ name: 'computation' })); + }).pipe( + Effect.provide( + Layer.mergeAll( + effectLayer({ + dsn: TEST_DSN, + transport: getMockTransport(), + }), + Layer.setTracer(SentryEffectTracer), + ), + ), + ), + ); + + it.effect('layer can be composed with logger layer', () => + Effect.gen(function* () { + yield* Effect.logInfo('test log'); + const result = yield* Effect.succeed('logged'); + expect(result).toBe('logged'); + }).pipe( + Effect.provide( + Layer.mergeAll( + effectLayer({ + dsn: TEST_DSN, + transport: getMockTransport(), + }), + Logger.replace(Logger.defaultLogger, SentryEffectLogger), + Logger.minimumLogLevel(LogLevel.All), + ), + ), + ), + ); + + it.effect('layer can be composed with all Effect features', () => + Effect.gen(function* () { + const startInactiveSpanMock = vi.spyOn(sentryCore, 'startInactiveSpan'); + + yield* Effect.logInfo('starting computation'); + const result = yield* Effect.succeed(42).pipe( + Effect.map(n => n * 2), + Effect.withSpan('computation'), + ); + yield* Effect.logInfo('computation complete'); + expect(result).toBe(84); + expect(startInactiveSpanMock).toHaveBeenCalledWith(expect.objectContaining({ name: 'computation' })); + }).pipe( + Effect.provide( + Layer.mergeAll( + effectLayer({ + dsn: TEST_DSN, + transport: getMockTransport(), + }), + Layer.setTracer(SentryEffectTracer), + Logger.replace(Logger.defaultLogger, SentryEffectLogger), + Logger.minimumLogLevel(LogLevel.All), + ), + ), + ), + ); +}); diff --git a/dev-packages/effect-3-compatibility-tests/test/logger.test.ts b/dev-packages/effect-3-compatibility-tests/test/logger.test.ts new file mode 100644 index 000000000000..229767a9d390 --- /dev/null +++ b/dev-packages/effect-3-compatibility-tests/test/logger.test.ts @@ -0,0 +1,104 @@ +import { describe, expect, it } from '@effect/vitest'; +import * as sentryCore from '@sentry/core'; +import { Effect, Layer, Logger, LogLevel } from 'effect'; +import { afterEach, vi } from 'vitest'; +import { SentryEffectLogger } from '@sentry/effect'; + +vi.mock('@sentry/core', async importOriginal => { + const original = await importOriginal(); + return { + ...original, + logger: { + ...original.logger, + error: vi.fn(), + warn: vi.fn(), + info: vi.fn(), + debug: vi.fn(), + trace: vi.fn(), + fatal: vi.fn(), + }, + }; +}); + +describe('SentryEffectLogger', () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + const loggerLayer = Layer.mergeAll( + Logger.replace(Logger.defaultLogger, SentryEffectLogger), + Logger.minimumLogLevel(LogLevel.All), + ); + + it.effect('forwards fatal logs to Sentry', () => + Effect.gen(function* () { + yield* Effect.logFatal('This is a fatal message'); + expect(sentryCore.logger.fatal).toHaveBeenCalledWith('This is a fatal message'); + }).pipe(Effect.provide(loggerLayer)), + ); + + it.effect('forwards error logs to Sentry', () => + Effect.gen(function* () { + yield* Effect.logError('This is an error message'); + expect(sentryCore.logger.error).toHaveBeenCalledWith('This is an error message'); + }).pipe(Effect.provide(loggerLayer)), + ); + + it.effect('forwards warning logs to Sentry', () => + Effect.gen(function* () { + yield* Effect.logWarning('This is a warning message'); + expect(sentryCore.logger.warn).toHaveBeenCalledWith('This is a warning message'); + }).pipe(Effect.provide(loggerLayer)), + ); + + it.effect('forwards info logs to Sentry', () => + Effect.gen(function* () { + yield* Effect.logInfo('This is an info message'); + expect(sentryCore.logger.info).toHaveBeenCalledWith('This is an info message'); + }).pipe(Effect.provide(loggerLayer)), + ); + + it.effect('forwards debug logs to Sentry', () => + Effect.gen(function* () { + yield* Effect.logDebug('This is a debug message'); + expect(sentryCore.logger.debug).toHaveBeenCalledWith('This is a debug message'); + }).pipe(Effect.provide(loggerLayer)), + ); + + it.effect('forwards trace logs to Sentry', () => + Effect.gen(function* () { + yield* Effect.logTrace('This is a trace message'); + expect(sentryCore.logger.trace).toHaveBeenCalledWith('This is a trace message'); + }).pipe(Effect.provide(loggerLayer)), + ); + + it.effect('handles object messages by stringifying', () => + Effect.gen(function* () { + yield* Effect.logInfo({ key: 'value', nested: { foo: 'bar' } }); + expect(sentryCore.logger.info).toHaveBeenCalledWith('{"key":"value","nested":{"foo":"bar"}}'); + }).pipe(Effect.provide(loggerLayer)), + ); + + it.effect('handles multiple log calls', () => + Effect.gen(function* () { + yield* Effect.logInfo('First message'); + yield* Effect.logInfo('Second message'); + yield* Effect.logWarning('Third message'); + expect(sentryCore.logger.info).toHaveBeenCalledTimes(2); + expect(sentryCore.logger.info).toHaveBeenNthCalledWith(1, 'First message'); + expect(sentryCore.logger.info).toHaveBeenNthCalledWith(2, 'Second message'); + expect(sentryCore.logger.warn).toHaveBeenCalledWith('Third message'); + }).pipe(Effect.provide(loggerLayer)), + ); + + it.effect('works with Effect.tap for logging side effects', () => + Effect.gen(function* () { + const result = yield* Effect.succeed('data').pipe( + Effect.tap(data => Effect.logInfo(`Processing: ${data}`)), + Effect.map(d => d.toUpperCase()), + ); + expect(result).toBe('DATA'); + expect(sentryCore.logger.info).toHaveBeenCalledWith('Processing: data'); + }).pipe(Effect.provide(loggerLayer)), + ); +}); diff --git a/dev-packages/effect-3-compatibility-tests/test/metrics.test.ts b/dev-packages/effect-3-compatibility-tests/test/metrics.test.ts new file mode 100644 index 000000000000..fdba7e5a400a --- /dev/null +++ b/dev-packages/effect-3-compatibility-tests/test/metrics.test.ts @@ -0,0 +1,295 @@ +import { describe, expect, it } from '@effect/vitest'; +import * as sentryCore from '@sentry/core'; +import { SentryEffectMetricsLayer } from '@sentry/effect/client'; +import { Duration, Effect, Layer, Metric, MetricBoundaries, MetricLabel, TestClock, TestContext } from 'effect'; +import { afterEach, beforeEach, vi } from 'vitest'; + +describe('SentryEffectMetricsLayer', () => { + const mockCount = vi.fn(); + const mockGauge = vi.fn(); + const mockDistribution = vi.fn(); + + beforeEach(() => { + vi.spyOn(sentryCore.metrics, 'count').mockImplementation(mockCount); + vi.spyOn(sentryCore.metrics, 'gauge').mockImplementation(mockGauge); + vi.spyOn(sentryCore.metrics, 'distribution').mockImplementation(mockDistribution); + }); + + afterEach(() => { + vi.clearAllMocks(); + vi.restoreAllMocks(); + }); + + it.effect('creates counter metrics', () => + Effect.gen(function* () { + const counter = Metric.counter('test_counter'); + + yield* Metric.increment(counter); + yield* Metric.increment(counter); + yield* Metric.incrementBy(counter, 5); + + const snapshot = Metric.unsafeSnapshot(); + const counterMetric = snapshot.find(p => p.metricKey.name === 'test_counter'); + + expect(counterMetric).toBeDefined(); + }), + ); + + it.effect('creates gauge metrics', () => + Effect.gen(function* () { + const gauge = Metric.gauge('test_gauge'); + + yield* Metric.set(gauge, 42); + + const snapshot = Metric.unsafeSnapshot(); + const gaugeMetric = snapshot.find(p => p.metricKey.name === 'test_gauge'); + + expect(gaugeMetric).toBeDefined(); + }), + ); + + it.effect('creates histogram metrics', () => + Effect.gen(function* () { + const histogram = Metric.histogram('test_histogram', MetricBoundaries.linear({ start: 0, width: 10, count: 10 })); + + yield* Metric.update(histogram, 5); + yield* Metric.update(histogram, 15); + yield* Metric.update(histogram, 25); + + const snapshot = Metric.unsafeSnapshot(); + const histogramMetric = snapshot.find(p => p.metricKey.name === 'test_histogram'); + + expect(histogramMetric).toBeDefined(); + }), + ); + + it.effect('creates summary metrics', () => + Effect.gen(function* () { + const summary = Metric.summary({ + name: 'test_summary', + maxAge: '1 minute', + maxSize: 100, + error: 0.01, + quantiles: [0.5, 0.9, 0.99], + }); + + yield* Metric.update(summary, 10); + yield* Metric.update(summary, 20); + yield* Metric.update(summary, 30); + + const snapshot = Metric.unsafeSnapshot(); + const summaryMetric = snapshot.find(p => p.metricKey.name === 'test_summary'); + + expect(summaryMetric).toBeDefined(); + }), + ); + + it.effect('creates frequency metrics', () => + Effect.gen(function* () { + const frequency = Metric.frequency('test_frequency'); + + yield* Metric.update(frequency, 'foo'); + yield* Metric.update(frequency, 'bar'); + yield* Metric.update(frequency, 'foo'); + + const snapshot = Metric.unsafeSnapshot(); + const frequencyMetric = snapshot.find(p => p.metricKey.name === 'test_frequency'); + + expect(frequencyMetric).toBeDefined(); + }), + ); + + it.effect('supports metrics with labels', () => + Effect.gen(function* () { + const counter = Metric.counter('labeled_counter').pipe( + Metric.taggedWithLabels([MetricLabel.make('env', 'test'), MetricLabel.make('service', 'my-service')]), + ); + + yield* Metric.increment(counter); + + const snapshot = Metric.unsafeSnapshot(); + const labeledMetric = snapshot.find(p => p.metricKey.name === 'labeled_counter'); + + expect(labeledMetric).toBeDefined(); + const tags = labeledMetric?.metricKey.tags ?? []; + expect(tags.some(t => t.key === 'env' && t.value === 'test')).toBe(true); + expect(tags.some(t => t.key === 'service' && t.value === 'my-service')).toBe(true); + }), + ); + + it.effect('tracks Effect durations with timer metric', () => + Effect.gen(function* () { + const timer = Metric.timerWithBoundaries('operation_duration', [10, 50, 100, 500, 1000]); + + yield* Effect.succeed('done').pipe(Metric.trackDuration(timer)); + + const snapshot = Metric.unsafeSnapshot(); + const timerMetric = snapshot.find(p => p.metricKey.name === 'operation_duration'); + + expect(timerMetric).toBeDefined(); + }), + ); + + it.effect('integrates with Effect.timed', () => + Effect.gen(function* () { + const [duration, result] = yield* Effect.timed(Effect.succeed('completed')); + + expect(result).toBe('completed'); + expect(Duration.toMillis(duration)).toBeGreaterThanOrEqual(0); + }), + ); +}); + +describe('SentryEffectMetricsLayer with TestClock', () => { + const mockCount = vi.fn(); + const mockGauge = vi.fn(); + const mockDistribution = vi.fn(); + + const TestLayer = Layer.provide(SentryEffectMetricsLayer, TestContext.TestContext); + + beforeEach(() => { + vi.spyOn(sentryCore.metrics, 'count').mockImplementation(mockCount); + vi.spyOn(sentryCore.metrics, 'gauge').mockImplementation(mockGauge); + vi.spyOn(sentryCore.metrics, 'distribution').mockImplementation(mockDistribution); + }); + + afterEach(() => { + vi.clearAllMocks(); + vi.restoreAllMocks(); + }); + + it.effect('sends counter metrics to Sentry after 10 seconds', () => + Effect.gen(function* () { + const counter = Metric.counter('flush_test_counter'); + + yield* Metric.increment(counter); + yield* Metric.incrementBy(counter, 4); + + mockCount.mockClear(); + yield* TestClock.adjust('10 seconds'); + + expect(mockCount).toHaveBeenCalledWith('flush_test_counter', 5, { attributes: {} }); + }).pipe(Effect.provide(TestLayer)), + ); + + it.effect('sends gauge metrics to Sentry after 10 seconds', () => + Effect.gen(function* () { + const gauge = Metric.gauge('flush_test_gauge'); + + yield* Metric.set(gauge, 42); + + mockGauge.mockClear(); + yield* TestClock.adjust('10 seconds'); + + expect(mockGauge).toHaveBeenCalledWith('flush_test_gauge', 42, { attributes: {} }); + }).pipe(Effect.provide(TestLayer)), + ); + + it.effect('sends histogram metrics to Sentry after 10 seconds', () => + Effect.gen(function* () { + const histogram = Metric.histogram( + 'flush_test_histogram', + MetricBoundaries.linear({ start: 0, width: 10, count: 5 }), + ); + + yield* Metric.update(histogram, 5); + yield* Metric.update(histogram, 15); + + mockGauge.mockClear(); + yield* TestClock.adjust('10 seconds'); + + expect(mockGauge).toHaveBeenCalledWith('flush_test_histogram.sum', expect.any(Number), { attributes: {} }); + expect(mockGauge).toHaveBeenCalledWith('flush_test_histogram.count', expect.any(Number), { attributes: {} }); + expect(mockGauge).toHaveBeenCalledWith('flush_test_histogram.min', expect.any(Number), { attributes: {} }); + expect(mockGauge).toHaveBeenCalledWith('flush_test_histogram.max', expect.any(Number), { attributes: {} }); + }).pipe(Effect.provide(TestLayer)), + ); + + it.effect('sends summary metrics to Sentry after 10 seconds', () => + Effect.gen(function* () { + const summary = Metric.summary({ + name: 'flush_test_summary', + maxAge: '1 minute', + maxSize: 100, + error: 0.01, + quantiles: [0.5, 0.9, 0.99], + }); + + yield* Metric.update(summary, 10); + yield* Metric.update(summary, 20); + yield* Metric.update(summary, 30); + + mockGauge.mockClear(); + yield* TestClock.adjust('10 seconds'); + + expect(mockGauge).toHaveBeenCalledWith('flush_test_summary.sum', 60, { attributes: {} }); + expect(mockGauge).toHaveBeenCalledWith('flush_test_summary.count', 3, { attributes: {} }); + expect(mockGauge).toHaveBeenCalledWith('flush_test_summary.min', 10, { attributes: {} }); + expect(mockGauge).toHaveBeenCalledWith('flush_test_summary.max', 30, { attributes: {} }); + }).pipe(Effect.provide(TestLayer)), + ); + + it.effect('sends frequency metrics to Sentry after 10 seconds', () => + Effect.gen(function* () { + const frequency = Metric.frequency('flush_test_frequency'); + + yield* Metric.update(frequency, 'apple'); + yield* Metric.update(frequency, 'banana'); + yield* Metric.update(frequency, 'apple'); + + mockCount.mockClear(); + yield* TestClock.adjust('10 seconds'); + + expect(mockCount).toHaveBeenCalledWith('flush_test_frequency', 2, { attributes: { word: 'apple' } }); + expect(mockCount).toHaveBeenCalledWith('flush_test_frequency', 1, { attributes: { word: 'banana' } }); + }).pipe(Effect.provide(TestLayer)), + ); + + it.effect('sends metrics with labels as attributes to Sentry after 10 seconds', () => + Effect.gen(function* () { + const gauge = Metric.gauge('flush_test_labeled_gauge').pipe( + Metric.taggedWithLabels([MetricLabel.make('env', 'production'), MetricLabel.make('region', 'us-east')]), + ); + + yield* Metric.set(gauge, 100); + + mockGauge.mockClear(); + yield* TestClock.adjust('10 seconds'); + + expect(mockGauge).toHaveBeenCalledWith('flush_test_labeled_gauge', 100, { + attributes: { env: 'production', region: 'us-east' }, + }); + }).pipe(Effect.provide(TestLayer)), + ); + + it.effect('sends counter delta values on subsequent flushes', () => + Effect.gen(function* () { + const counter = Metric.counter('flush_test_delta_counter'); + + yield* Metric.incrementBy(counter, 10); + yield* TestClock.adjust('10 seconds'); + + mockCount.mockClear(); + + yield* Metric.incrementBy(counter, 5); + yield* TestClock.adjust('10 seconds'); + + expect(mockCount).toHaveBeenCalledWith('flush_test_delta_counter', 5, { attributes: {} }); + }).pipe(Effect.provide(TestLayer)), + ); + + it.effect('does not send counter when delta is zero', () => + Effect.gen(function* () { + const counter = Metric.counter('flush_test_zero_delta'); + + yield* Metric.incrementBy(counter, 10); + yield* TestClock.adjust('10 seconds'); + + mockCount.mockClear(); + + yield* TestClock.adjust('10 seconds'); + + expect(mockCount).not.toHaveBeenCalledWith('flush_test_zero_delta', 0, { attributes: {} }); + }).pipe(Effect.provide(TestLayer)), + ); +}); diff --git a/dev-packages/effect-3-compatibility-tests/test/tracer.test.ts b/dev-packages/effect-3-compatibility-tests/test/tracer.test.ts new file mode 100644 index 000000000000..70414c2e2986 --- /dev/null +++ b/dev-packages/effect-3-compatibility-tests/test/tracer.test.ts @@ -0,0 +1,284 @@ +import { describe, expect, it } from '@effect/vitest'; +import * as sentryCore from '@sentry/core'; +import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; +import { Effect, Layer } from 'effect'; +import { afterEach, vi } from 'vitest'; +import { SentryEffectTracer } from '@sentry/effect'; + +const TracerLayer = Layer.setTracer(SentryEffectTracer); + +describe('SentryEffectTracer', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it.effect('traces Effect spans to Sentry', () => + Effect.gen(function* () { + let capturedSpanName: string | undefined; + + yield* Effect.withSpan('test-parent-span')( + Effect.gen(function* () { + yield* Effect.annotateCurrentSpan('test-attribute', 'test-value'); + capturedSpanName = 'effect-span-executed'; + }), + ); + + expect(capturedSpanName).toBe('effect-span-executed'); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('creates spans with correct attributes', () => + Effect.gen(function* () { + const result = yield* Effect.withSpan('my-operation')(Effect.succeed('success')); + + expect(result).toBe('success'); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('handles nested spans', () => + Effect.gen(function* () { + const result = yield* Effect.withSpan('outer')( + Effect.gen(function* () { + const inner = yield* Effect.withSpan('inner')(Effect.succeed('inner-result')); + return `outer-${inner}`; + }), + ); + + expect(result).toBe('outer-inner-result'); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('propagates span context through Effect fibers', () => + Effect.gen(function* () { + const results: string[] = []; + + yield* Effect.withSpan('parent')( + Effect.gen(function* () { + results.push('parent-start'); + yield* Effect.withSpan('child-1')(Effect.sync(() => results.push('child-1'))); + yield* Effect.withSpan('child-2')(Effect.sync(() => results.push('child-2'))); + results.push('parent-end'); + }), + ); + + expect(results).toEqual(['parent-start', 'child-1', 'child-2', 'parent-end']); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('handles span failures correctly', () => + Effect.gen(function* () { + const result = yield* Effect.withSpan('failing-span')(Effect.fail('expected-error')).pipe( + Effect.catchAll(e => Effect.succeed(`caught: ${e}`)), + ); + + expect(result).toBe('caught: expected-error'); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('handles span with defects (die)', () => + Effect.gen(function* () { + const result = yield* Effect.withSpan('defect-span')(Effect.die('defect-value')).pipe( + Effect.catchAllDefect(d => Effect.succeed(`caught-defect: ${d}`)), + ); + + expect(result).toBe('caught-defect: defect-value'); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('works with Effect.all for parallel operations', () => + Effect.gen(function* () { + const results = yield* Effect.withSpan('parallel-parent')( + Effect.all([ + Effect.withSpan('task-1')(Effect.succeed(1)), + Effect.withSpan('task-2')(Effect.succeed(2)), + Effect.withSpan('task-3')(Effect.succeed(3)), + ]), + ); + + expect(results).toEqual([1, 2, 3]); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('supports span annotations', () => + Effect.gen(function* () { + const result = yield* Effect.succeed('annotated').pipe( + Effect.withSpan('annotated-span'), + Effect.tap(() => Effect.annotateCurrentSpan('custom-key', 'custom-value')), + ); + + expect(result).toBe('annotated'); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('sets span status to ok on success', () => + Effect.gen(function* () { + const setStatusCalls: Array<{ code: number; message?: string }> = []; + + const mockStartInactiveSpan = vi.spyOn(sentryCore, 'startInactiveSpan').mockImplementation(_options => { + return { + spanContext: () => ({ spanId: 'test-span-id', traceId: 'test-trace-id' }), + isRecording: () => true, + setAttribute: vi.fn(), + setStatus: (status: { code: number; message?: string }) => setStatusCalls.push(status), + addEvent: vi.fn(), + end: vi.fn(), + } as unknown as sentryCore.Span; + }); + + yield* Effect.withSpan('success-span')(Effect.succeed('ok')); + + expect(setStatusCalls).toContainEqual({ code: 1 }); + + mockStartInactiveSpan.mockRestore(); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('sets span status to error on failure', () => + Effect.gen(function* () { + const setStatusCalls: Array<{ code: number; message?: string }> = []; + + const mockStartInactiveSpan = vi.spyOn(sentryCore, 'startInactiveSpan').mockImplementation(_options => { + return { + spanContext: () => ({ spanId: 'test-span-id', traceId: 'test-trace-id' }), + isRecording: () => true, + setAttribute: vi.fn(), + setStatus: (status: { code: number; message?: string }) => setStatusCalls.push(status), + addEvent: vi.fn(), + end: vi.fn(), + } as unknown as sentryCore.Span; + }); + + yield* Effect.withSpan('error-span')(Effect.fail('test-error')).pipe(Effect.catchAll(() => Effect.void)); + + expect(setStatusCalls).toContainEqual({ code: 2, message: 'test-error' }); + + mockStartInactiveSpan.mockRestore(); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('sets span status to error on defect', () => + Effect.gen(function* () { + const setStatusCalls: Array<{ code: number; message?: string }> = []; + + const mockStartInactiveSpan = vi.spyOn(sentryCore, 'startInactiveSpan').mockImplementation(_options => { + return { + spanContext: () => ({ spanId: 'test-span-id', traceId: 'test-trace-id' }), + isRecording: () => true, + setAttribute: vi.fn(), + setStatus: (status: { code: number; message?: string }) => setStatusCalls.push(status), + addEvent: vi.fn(), + end: vi.fn(), + } as unknown as sentryCore.Span; + }); + + yield* Effect.withSpan('defect-span')(Effect.die('fatal-defect')).pipe(Effect.catchAllDefect(() => Effect.void)); + + expect(setStatusCalls).toContainEqual({ code: 2, message: 'fatal-defect' }); + + mockStartInactiveSpan.mockRestore(); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('propagates Sentry span context via withActiveSpan', () => + Effect.gen(function* () { + const withActiveSpanCalls: sentryCore.Span[] = []; + + const mockWithActiveSpan = vi + .spyOn(sentryCore, 'withActiveSpan') + .mockImplementation((span: sentryCore.Span | null, callback: (scope: sentryCore.Scope) => T): T => { + if (span) { + withActiveSpanCalls.push(span); + } + return callback({} as sentryCore.Scope); + }); + + yield* Effect.withSpan('context-span')(Effect.succeed('done')); + + expect(withActiveSpanCalls.length).toBeGreaterThan(0); + + mockWithActiveSpan.mockRestore(); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('sets origin to auto.function.effect for regular spans', () => + Effect.gen(function* () { + let capturedAttributes: Record | undefined; + + const mockStartInactiveSpan = vi.spyOn(sentryCore, 'startInactiveSpan').mockImplementation(options => { + capturedAttributes = options.attributes; + return { + spanContext: () => ({ spanId: 'test-span-id', traceId: 'test-trace-id' }), + isRecording: () => true, + setAttribute: vi.fn(), + setStatus: vi.fn(), + addEvent: vi.fn(), + end: vi.fn(), + } as unknown as sentryCore.Span; + }); + + yield* Effect.withSpan('my-operation')(Effect.succeed('ok')); + + expect(capturedAttributes).toBeDefined(); + expect(capturedAttributes?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]).toBe('auto.function.effect'); + + mockStartInactiveSpan.mockRestore(); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('sets origin to auto.http.effect for http.server spans', () => + Effect.gen(function* () { + let capturedAttributes: Record | undefined; + + const mockStartInactiveSpan = vi.spyOn(sentryCore, 'startInactiveSpan').mockImplementation(options => { + capturedAttributes = options.attributes; + return { + spanContext: () => ({ spanId: 'test-span-id', traceId: 'test-trace-id' }), + isRecording: () => true, + setAttribute: vi.fn(), + setStatus: vi.fn(), + addEvent: vi.fn(), + end: vi.fn(), + } as unknown as sentryCore.Span; + }); + + yield* Effect.withSpan('http.server GET /api/users')(Effect.succeed('ok')); + + expect(capturedAttributes).toBeDefined(); + expect(capturedAttributes?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]).toBe('auto.http.effect'); + + mockStartInactiveSpan.mockRestore(); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('sets origin to auto.http.effect for http.client spans', () => + Effect.gen(function* () { + let capturedAttributes: Record | undefined; + + const mockStartInactiveSpan = vi.spyOn(sentryCore, 'startInactiveSpan').mockImplementation(options => { + capturedAttributes = options.attributes; + return { + spanContext: () => ({ spanId: 'test-span-id', traceId: 'test-trace-id' }), + isRecording: () => true, + setAttribute: vi.fn(), + setStatus: vi.fn(), + addEvent: vi.fn(), + end: vi.fn(), + } as unknown as sentryCore.Span; + }); + + yield* Effect.withSpan('http.client GET https://api.example.com')(Effect.succeed('ok')); + + expect(capturedAttributes).toBeDefined(); + expect(capturedAttributes?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]).toBe('auto.http.effect'); + + mockStartInactiveSpan.mockRestore(); + }).pipe(Effect.provide(TracerLayer)), + ); + + it.effect('can be used with Effect.withTracer', () => + Effect.gen(function* () { + const result = yield* Effect.withSpan('inline-tracer-span')(Effect.succeed('with-tracer')); + expect(result).toBe('with-tracer'); + }).pipe(Effect.withTracer(SentryEffectTracer)), + ); +}); diff --git a/dev-packages/effect-3-compatibility-tests/tsconfig.json b/dev-packages/effect-3-compatibility-tests/tsconfig.json new file mode 100644 index 000000000000..48c6ea89218f --- /dev/null +++ b/dev-packages/effect-3-compatibility-tests/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + + "include": ["test/**/*.ts"], + + "compilerOptions": { + "lib": ["DOM", "es2020"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "types": ["node"] + } +} diff --git a/dev-packages/effect-3-compatibility-tests/tsconfig.test.json b/dev-packages/effect-3-compatibility-tests/tsconfig.test.json new file mode 100644 index 000000000000..fe611a1042ae --- /dev/null +++ b/dev-packages/effect-3-compatibility-tests/tsconfig.test.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + + "include": ["test/**/*.ts", "vitest.config.ts"], + + "compilerOptions": { + "lib": ["DOM", "es2020"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "types": ["node"] + } +} diff --git a/dev-packages/effect-3-compatibility-tests/vitest.config.ts b/dev-packages/effect-3-compatibility-tests/vitest.config.ts new file mode 100644 index 000000000000..67d1fdb538a7 --- /dev/null +++ b/dev-packages/effect-3-compatibility-tests/vitest.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vitest/config'; +import baseConfig from '../../vite/vite.config'; + +export default defineConfig({ + ...baseConfig, + test: { + ...baseConfig.test, + include: ['test/**/*.test.ts'], + server: { + deps: { + inline: [/@sentry\/.*/], + }, + }, + }, +}); diff --git a/dev-packages/effect-3-compatibility-tests/yarn.lock b/dev-packages/effect-3-compatibility-tests/yarn.lock new file mode 100644 index 000000000000..13be42a993c1 --- /dev/null +++ b/dev-packages/effect-3-compatibility-tests/yarn.lock @@ -0,0 +1,780 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@effect/vitest@^0.29.0": + version "0.29.0" + resolved "https://registry.yarnpkg.com/@effect/vitest/-/vitest-0.29.0.tgz#44cfc9b6870e5b0f04044d3ba89adb25ccbacebb" + integrity sha512-DvWr1aeEcaZ8mtu8hNVb4e3rEYvGEwQSr7wsNrW53t6nKYjkmjRICcvVEsXUhjoCblRHSxRsRV0TOt0+UmcvaQ== + +"@esbuild/aix-ppc64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz#82b74f92aa78d720b714162939fb248c90addf53" + integrity sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg== + +"@esbuild/android-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz#f78cb8a3121fc205a53285adb24972db385d185d" + integrity sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ== + +"@esbuild/android-arm@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.7.tgz#593e10a1450bbfcac6cb321f61f468453bac209d" + integrity sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ== + +"@esbuild/android-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.7.tgz#453143d073326033d2d22caf9e48de4bae274b07" + integrity sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg== + +"@esbuild/darwin-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz#6f23000fb9b40b7e04b7d0606c0693bd0632f322" + integrity sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw== + +"@esbuild/darwin-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz#27393dd18bb1263c663979c5f1576e00c2d024be" + integrity sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ== + +"@esbuild/freebsd-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz#22e4638fa502d1c0027077324c97640e3adf3a62" + integrity sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w== + +"@esbuild/freebsd-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz#9224b8e4fea924ce2194e3efc3e9aebf822192d6" + integrity sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ== + +"@esbuild/linux-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz#4f5d1c27527d817b35684ae21419e57c2bda0966" + integrity sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A== + +"@esbuild/linux-arm@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz#b9e9d070c8c1c0449cf12b20eac37d70a4595921" + integrity sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA== + +"@esbuild/linux-ia32@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz#3f80fb696aa96051a94047f35c85b08b21c36f9e" + integrity sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg== + +"@esbuild/linux-loong64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz#9be1f2c28210b13ebb4156221bba356fe1675205" + integrity sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q== + +"@esbuild/linux-mips64el@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz#4ab5ee67a3dfcbcb5e8fd7883dae6e735b1163b8" + integrity sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw== + +"@esbuild/linux-ppc64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz#dac78c689f6499459c4321e5c15032c12307e7ea" + integrity sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ== + +"@esbuild/linux-riscv64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz#050f7d3b355c3a98308e935bc4d6325da91b0027" + integrity sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ== + +"@esbuild/linux-s390x@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz#d61f715ce61d43fe5844ad0d8f463f88cbe4fef6" + integrity sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw== + +"@esbuild/linux-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz#ca8e1aa478fc8209257bf3ac8f79c4dc2982f32a" + integrity sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA== + +"@esbuild/netbsd-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz#1650f2c1b948deeb3ef948f2fc30614723c09690" + integrity sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w== + +"@esbuild/netbsd-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz#65772ab342c4b3319bf0705a211050aac1b6e320" + integrity sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw== + +"@esbuild/openbsd-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz#37ed7cfa66549d7955852fce37d0c3de4e715ea1" + integrity sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A== + +"@esbuild/openbsd-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz#01bf3d385855ef50cb33db7c4b52f957c34cd179" + integrity sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg== + +"@esbuild/openharmony-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz#6c1f94b34086599aabda4eac8f638294b9877410" + integrity sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw== + +"@esbuild/sunos-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz#4b0dd17ae0a6941d2d0fd35a906392517071a90d" + integrity sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA== + +"@esbuild/win32-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz#34193ab5565d6ff68ca928ac04be75102ccb2e77" + integrity sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA== + +"@esbuild/win32-ia32@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz#eb67f0e4482515d8c1894ede631c327a4da9fc4d" + integrity sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw== + +"@esbuild/win32-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz#8fe30b3088b89b4873c3a6cc87597ae3920c0a8b" + integrity sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg== + +"@jridgewell/sourcemap-codec@^1.5.5": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@rollup/rollup-android-arm-eabi@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz#a19c645c375158cd5c50a344106f0fa18eb821c4" + integrity sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw== + +"@rollup/rollup-android-arm64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz#1af19aa9d3ad6d00df2681f59cfcb8bf7499576b" + integrity sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg== + +"@rollup/rollup-darwin-arm64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz#3b8463e03ba2a393453fea70e7d907379c27b649" + integrity sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA== + +"@rollup/rollup-darwin-x64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz#28da23d69fe117f5f0ff330a8549e51bd09f1b6a" + integrity sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g== + +"@rollup/rollup-freebsd-arm64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz#94bacac3190f621de1355922b599f3817786044c" + integrity sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw== + +"@rollup/rollup-freebsd-x64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz#8a0094f533b9fda160b5c90ad9e0c78fca341788" + integrity sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ== + +"@rollup/rollup-linux-arm-gnueabihf@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz#3b7e901a555c7245c87f7440979bee0a1ec882bb" + integrity sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg== + +"@rollup/rollup-linux-arm-musleabihf@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz#ee9a09b72e8ad764cfd6188b32ff1de528ff7ebe" + integrity sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw== + +"@rollup/rollup-linux-arm64-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz#ba483f4aca9be141171d086dbd01ada6ab03b58d" + integrity sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg== + +"@rollup/rollup-linux-arm64-musl@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz#17b595b790e6df68e91c5d02526fc832a985ce4f" + integrity sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA== + +"@rollup/rollup-linux-loong64-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz#551718714075a2bfb36a2813c466e3a0e9d56abf" + integrity sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A== + +"@rollup/rollup-linux-loong64-musl@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz#ba156ed1243447a3d710972001d5dcfe3827ff3d" + integrity sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q== + +"@rollup/rollup-linux-ppc64-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz#6a957a709b86ac62ef68e597ac03dbd4336782b1" + integrity sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw== + +"@rollup/rollup-linux-ppc64-musl@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz#ca4176b4ad53f3edee3b4bfa6f9ef48ff38f167b" + integrity sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ== + +"@rollup/rollup-linux-riscv64-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz#4e6b08f72ebeafdb41f3ec433bd228ba8573473b" + integrity sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A== + +"@rollup/rollup-linux-riscv64-musl@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz#a0b8b8580c7680c8086cb3226527e5472253b895" + integrity sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ== + +"@rollup/rollup-linux-s390x-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz#79fe15b92ce0bae2b609cf26dd158cd3e2b73634" + integrity sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA== + +"@rollup/rollup-linux-x64-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz#6aa8302fa45fd3cbbc510ccd223c9c37bf67e53f" + integrity sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ== + +"@rollup/rollup-linux-x64-musl@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz#0c1a5e9799f80c47a66f2c3a5f1a280f38356047" + integrity sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw== + +"@rollup/rollup-openbsd-x64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz#5f07c863e74fd428794f1dc5749f321b661d1f17" + integrity sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg== + +"@rollup/rollup-openharmony-arm64@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz#8e0d71324be0f423428b12b25a2eb8ea8e0a7833" + integrity sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q== + +"@rollup/rollup-win32-arm64-msvc@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz#a553fdf90a785ace6d7501eed6241c468b088999" + integrity sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ== + +"@rollup/rollup-win32-ia32-msvc@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz#0fb04f0a88027fbfd323e25a446debce4773868c" + integrity sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg== + +"@rollup/rollup-win32-x64-gnu@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz#aaa9e36dbdc0f0e397e5966dcce1b4285354ede2" + integrity sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA== + +"@rollup/rollup-win32-x64-msvc@4.60.2": + version "4.60.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz#3418dcf1388f2abd6b0ccc08fe1ef205ae76d696" + integrity sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA== + +"@sentry-internal/browser-utils@10.49.0": + version "10.49.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-10.49.0.tgz#77abc10883e3a9f6e9d7dd3f59ba949f42b9d60f" + integrity sha512-n0QRx0Ysx6mPfIydTkz7VP0FmwM+/EqMZiRqdsU3aTYsngE9GmEDV0OL1bAy6a8N/C1xf9vntkuAtj6N/8Z51w== + dependencies: + "@sentry/core" "10.49.0" + +"@sentry-internal/feedback@10.49.0": + version "10.49.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-10.49.0.tgz#1abba6895df74e9b49b8257df0e1802693d85322" + integrity sha512-JNsUBGv0faCFE7MeZUH99Y9lU9qq3LBALbLxpE1x7ngNrQnVYRlcFgdqaD/btNBKr8awjYL8gmcSkHBWskGqLQ== + dependencies: + "@sentry/core" "10.49.0" + +"@sentry-internal/replay-canvas@10.49.0": + version "10.49.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-10.49.0.tgz#c2d7fe15603888399d02fe03fe5b7f771f7e424c" + integrity sha512-7D/NrgH1Qwx5trDYaaTSSJmCb1yVQQLqFG4G/S9x2ltzl9876lSGJL8UeW8ReNQgF3CDAcwbmm/9aXaVSBUNZA== + dependencies: + "@sentry-internal/replay" "10.49.0" + "@sentry/core" "10.49.0" + +"@sentry-internal/replay@10.49.0": + version "10.49.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-10.49.0.tgz#8a91bc55d02c38ba60aed8271dedbd034519fe47" + integrity sha512-IEy4lwHVMiRE3JAcn+kFKjsTgalDOCSTf20SoFd+nkt6rN/k1RDyr4xpdfF//Kj3UdeTmbuibYjK5H/FLhhnGg== + dependencies: + "@sentry-internal/browser-utils" "10.49.0" + "@sentry/core" "10.49.0" + +"@sentry/browser@10.49.0": + version "10.49.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-10.49.0.tgz#75ff0cc4b149dfbff78a14218208122fe3a68ba1" + integrity sha512-bGCHc+wK2Dx67YoSbmtlt04alqWfQ+dasD/GVipVOq50gvw/BBIDHTEWRJEjACl+LrvszeY54V+24p8z4IgysA== + dependencies: + "@sentry-internal/browser-utils" "10.49.0" + "@sentry-internal/feedback" "10.49.0" + "@sentry-internal/replay" "10.49.0" + "@sentry-internal/replay-canvas" "10.49.0" + "@sentry/core" "10.49.0" + +"@sentry/core@10.49.0": + version "0.0.0" + uid "" + +"@sentry/core@link:../../packages/core": + version "0.0.0" + uid "" + +"@sentry/effect@link:../../packages/effect": + version "0.0.0" + uid "" + +"@sentry/node-core@10.49.0": + version "10.49.0" + resolved "https://registry.yarnpkg.com/@sentry/node-core/-/node-core-10.49.0.tgz#d742a7e19cac535a17d39d416587bc8c1c742622" + integrity sha512-7WO0KuCDPSq3G54TVUSI1CKFJwB67LasG+n/gDMBqbrarzs/Yh/s34OOMU5gfVQpncxQAmQsy4nEboQms8iNqA== + dependencies: + "@sentry/core" "10.49.0" + "@sentry/opentelemetry" "10.49.0" + import-in-the-middle "^3.0.0" + +"@sentry/opentelemetry@10.49.0": + version "10.49.0" + resolved "https://registry.yarnpkg.com/@sentry/opentelemetry/-/opentelemetry-10.49.0.tgz#f38fbe6baf7d56aa0431047463cd309bd782484e" + integrity sha512-XNLm4dXmtegXQf+EEE2Cs84Ymlo/f5wMx+lg2S2XS4qLbXaPN/HttjhwKftd8D+8iUNfmH+xNMCSshx4s1B/1w== + dependencies: + "@sentry/core" "10.49.0" + +"@standard-schema/spec@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.1.0.tgz#a79b55dbaf8604812f52d140b2c9ab41bc150bb8" + integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== + +"@types/chai@^5.2.2": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-5.2.3.tgz#8e9cd9e1c3581fa6b341a5aed5588eb285be0b4a" + integrity sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA== + dependencies: + "@types/deep-eql" "*" + assertion-error "^2.0.1" + +"@types/deep-eql@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd" + integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== + +"@types/estree@1.0.8", "@types/estree@^1.0.0": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + +"@vitest/expect@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.2.4.tgz#8362124cd811a5ee11c5768207b9df53d34f2433" + integrity sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig== + dependencies: + "@types/chai" "^5.2.2" + "@vitest/spy" "3.2.4" + "@vitest/utils" "3.2.4" + chai "^5.2.0" + tinyrainbow "^2.0.0" + +"@vitest/mocker@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-3.2.4.tgz#4471c4efbd62db0d4fa203e65cc6b058a85cabd3" + integrity sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ== + dependencies: + "@vitest/spy" "3.2.4" + estree-walker "^3.0.3" + magic-string "^0.30.17" + +"@vitest/pretty-format@3.2.4", "@vitest/pretty-format@^3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-3.2.4.tgz#3c102f79e82b204a26c7a5921bf47d534919d3b4" + integrity sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA== + dependencies: + tinyrainbow "^2.0.0" + +"@vitest/runner@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-3.2.4.tgz#5ce0274f24a971f6500f6fc166d53d8382430766" + integrity sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ== + dependencies: + "@vitest/utils" "3.2.4" + pathe "^2.0.3" + strip-literal "^3.0.0" + +"@vitest/snapshot@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-3.2.4.tgz#40a8bc0346ac0aee923c0eefc2dc005d90bc987c" + integrity sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ== + dependencies: + "@vitest/pretty-format" "3.2.4" + magic-string "^0.30.17" + pathe "^2.0.3" + +"@vitest/spy@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-3.2.4.tgz#cc18f26f40f3f028da6620046881f4e4518c2599" + integrity sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw== + dependencies: + tinyspy "^4.0.3" + +"@vitest/utils@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-3.2.4.tgz#c0813bc42d99527fb8c5b138c7a88516bca46fea" + integrity sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA== + dependencies: + "@vitest/pretty-format" "3.2.4" + loupe "^3.1.4" + tinyrainbow "^2.0.0" + +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== + +acorn@^8.15.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== + +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + +chai@^5.2.0: + version "5.3.3" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.3.3.tgz#dd3da955e270916a4bd3f625f4b919996ada7e06" + integrity sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw== + dependencies: + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" + +check-error@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.3.tgz#2427361117b70cca8dc89680ead32b157019caf5" + integrity sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA== + +cjs-module-lexer@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz#b3ca5101843389259ade7d88c77bd06ce55849ca" + integrity sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ== + +debug@^4.4.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +deep-eql@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== + +effect@^3.21.1: + version "3.21.1" + resolved "https://registry.yarnpkg.com/effect/-/effect-3.21.1.tgz#1e2cf409cbeb5c52014b7001f554bf7d2f510f6c" + integrity sha512-bA3TsBd0mByThuCnE6FQqS+L3DLazk4UbE9jp0CqVfIjQl5DKi8sAe93O/ZKeF7clL65fJxcsVGiAKYXdnHByA== + dependencies: + "@standard-schema/spec" "^1.0.0" + fast-check "^3.23.1" + +es-module-lexer@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" + integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== + +esbuild@^0.27.0: + version "0.27.7" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.7.tgz#bcadce22b2f3fd76f257e3a64f83a64986fea11f" + integrity sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w== + optionalDependencies: + "@esbuild/aix-ppc64" "0.27.7" + "@esbuild/android-arm" "0.27.7" + "@esbuild/android-arm64" "0.27.7" + "@esbuild/android-x64" "0.27.7" + "@esbuild/darwin-arm64" "0.27.7" + "@esbuild/darwin-x64" "0.27.7" + "@esbuild/freebsd-arm64" "0.27.7" + "@esbuild/freebsd-x64" "0.27.7" + "@esbuild/linux-arm" "0.27.7" + "@esbuild/linux-arm64" "0.27.7" + "@esbuild/linux-ia32" "0.27.7" + "@esbuild/linux-loong64" "0.27.7" + "@esbuild/linux-mips64el" "0.27.7" + "@esbuild/linux-ppc64" "0.27.7" + "@esbuild/linux-riscv64" "0.27.7" + "@esbuild/linux-s390x" "0.27.7" + "@esbuild/linux-x64" "0.27.7" + "@esbuild/netbsd-arm64" "0.27.7" + "@esbuild/netbsd-x64" "0.27.7" + "@esbuild/openbsd-arm64" "0.27.7" + "@esbuild/openbsd-x64" "0.27.7" + "@esbuild/openharmony-arm64" "0.27.7" + "@esbuild/sunos-x64" "0.27.7" + "@esbuild/win32-arm64" "0.27.7" + "@esbuild/win32-ia32" "0.27.7" + "@esbuild/win32-x64" "0.27.7" + +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + +expect-type@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.3.0.tgz#0d58ed361877a31bbc4dd6cf71bbfef7faf6bd68" + integrity sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA== + +fast-check@^3.23.1: + version "3.23.2" + resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-3.23.2.tgz#0129f1eb7e4f500f58e8290edc83c670e4a574a2" + integrity sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A== + dependencies: + pure-rand "^6.1.0" + +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +import-in-the-middle@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-3.0.1.tgz#8a0a1230c9b865c0e12698171646ae1e3fff691d" + integrity sha512-pYkiyXVL2Mf3pozdlDGV6NAObxQx13Ae8knZk1UJRJ6uRW/ZRmTGHlQYtrsSl7ubuE5F8CD1z+s1n4RHNuTtuA== + dependencies: + acorn "^8.15.0" + acorn-import-attributes "^1.9.5" + cjs-module-lexer "^2.2.0" + module-details-from-path "^1.0.4" + +js-tokens@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4" + integrity sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ== + +loupe@^3.1.0, loupe@^3.1.4: + version "3.2.1" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.2.1.tgz#0095cf56dc5b7a9a7c08ff5b1a8796ec8ad17e76" + integrity sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ== + +magic-string@^0.30.17: + version "0.30.21" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91" + integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.5" + +module-details-from-path@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.4.tgz#b662fdcd93f6c83d3f25289da0ce81c8d9685b94" + integrity sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w== + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + +pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + +pathval@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.1.tgz#8855c5a2899af072d6ac05d11e46045ad0dc605d" + integrity sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^4.0.2, picomatch@^4.0.3, picomatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" + integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== + +postcss@^8.5.6: + version "8.5.10" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.10.tgz#8992d8c30acf3f12169e7c09514a12fed7e48356" + integrity sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +pure-rand@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== + +rollup@^4.43.0: + version "4.60.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.60.2.tgz#ac23fe4bd530304cef9fa61e639d7098b6762cf4" + integrity sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ== + dependencies: + "@types/estree" "1.0.8" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.60.2" + "@rollup/rollup-android-arm64" "4.60.2" + "@rollup/rollup-darwin-arm64" "4.60.2" + "@rollup/rollup-darwin-x64" "4.60.2" + "@rollup/rollup-freebsd-arm64" "4.60.2" + "@rollup/rollup-freebsd-x64" "4.60.2" + "@rollup/rollup-linux-arm-gnueabihf" "4.60.2" + "@rollup/rollup-linux-arm-musleabihf" "4.60.2" + "@rollup/rollup-linux-arm64-gnu" "4.60.2" + "@rollup/rollup-linux-arm64-musl" "4.60.2" + "@rollup/rollup-linux-loong64-gnu" "4.60.2" + "@rollup/rollup-linux-loong64-musl" "4.60.2" + "@rollup/rollup-linux-ppc64-gnu" "4.60.2" + "@rollup/rollup-linux-ppc64-musl" "4.60.2" + "@rollup/rollup-linux-riscv64-gnu" "4.60.2" + "@rollup/rollup-linux-riscv64-musl" "4.60.2" + "@rollup/rollup-linux-s390x-gnu" "4.60.2" + "@rollup/rollup-linux-x64-gnu" "4.60.2" + "@rollup/rollup-linux-x64-musl" "4.60.2" + "@rollup/rollup-openbsd-x64" "4.60.2" + "@rollup/rollup-openharmony-arm64" "4.60.2" + "@rollup/rollup-win32-arm64-msvc" "4.60.2" + "@rollup/rollup-win32-ia32-msvc" "4.60.2" + "@rollup/rollup-win32-x64-gnu" "4.60.2" + "@rollup/rollup-win32-x64-msvc" "4.60.2" + fsevents "~2.3.2" + +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + +std-env@^3.9.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.10.0.tgz#d810b27e3a073047b2b5e40034881f5ea6f9c83b" + integrity sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg== + +strip-literal@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-3.1.0.tgz#222b243dd2d49c0bcd0de8906adbd84177196032" + integrity sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg== + dependencies: + js-tokens "^9.0.1" + +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + +tinyexec@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + +tinyglobby@^0.2.14, tinyglobby@^0.2.15: + version "0.2.16" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6" + integrity sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.4" + +tinypool@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.1.1.tgz#059f2d042bd37567fbc017d3d426bdd2a2612591" + integrity sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg== + +tinyrainbow@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz#9509b2162436315e80e3eee0fcce4474d2444294" + integrity sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw== + +tinyspy@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-4.0.4.tgz#d77a002fb53a88aa1429b419c1c92492e0c81f78" + integrity sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q== + +vite-node@3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.2.4.tgz#f3676d94c4af1e76898c162c92728bca65f7bb07" + integrity sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg== + dependencies: + cac "^6.7.14" + debug "^4.4.1" + es-module-lexer "^1.7.0" + pathe "^2.0.3" + vite "^5.0.0 || ^6.0.0 || ^7.0.0-0" + +"vite@^5.0.0 || ^6.0.0 || ^7.0.0-0": + version "7.3.2" + resolved "https://registry.yarnpkg.com/vite/-/vite-7.3.2.tgz#cb041794d4c1395e28baea98198fd6e8f4b96b5c" + integrity sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg== + dependencies: + esbuild "^0.27.0" + fdir "^6.5.0" + picomatch "^4.0.3" + postcss "^8.5.6" + rollup "^4.43.0" + tinyglobby "^0.2.15" + optionalDependencies: + fsevents "~2.3.3" + +vitest@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.2.4.tgz#0637b903ad79d1539a25bc34c0ed54b5c67702ea" + integrity sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A== + dependencies: + "@types/chai" "^5.2.2" + "@vitest/expect" "3.2.4" + "@vitest/mocker" "3.2.4" + "@vitest/pretty-format" "^3.2.4" + "@vitest/runner" "3.2.4" + "@vitest/snapshot" "3.2.4" + "@vitest/spy" "3.2.4" + "@vitest/utils" "3.2.4" + chai "^5.2.0" + debug "^4.4.1" + expect-type "^1.2.1" + magic-string "^0.30.17" + pathe "^2.0.3" + picomatch "^4.0.2" + std-env "^3.9.0" + tinybench "^2.9.0" + tinyexec "^0.3.2" + tinyglobby "^0.2.14" + tinypool "^1.1.1" + tinyrainbow "^2.0.0" + vite "^5.0.0 || ^6.0.0 || ^7.0.0-0" + vite-node "3.2.4" + why-is-node-running "^2.3.0" + +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2"