diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b783d780a71..0852fe2db47 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -42,6 +42,15 @@ updates: - javascript - semver-patch ignore: + # Internal dependencies that we update manually + - dependency-name: "@datadog/libdatadog" + - dependency-name: "@datadog/native-appsec" + - dependency-name: "@datadog/native-iast-taint-tracking" + - dependency-name: "@datadog/native-metrics" + - dependency-name: "@datadog/pprof" + - dependency-name: "@datadog/sketches-js" + - dependency-name: "@datadog/wasm-js-rewriter" + - dependency-name: "@types/node" # Update the types manually with new Node.js version support update-types: ["version-update:semver-major"] @@ -65,6 +74,9 @@ updates: - dependency-name: "@opentelemetry/core" # 2.0.0 onwards only supports Node.js 18.19.0 and above update-types: ["version-update:semver-major"] + - dependency-name: "@opentelemetry/resources" + # 2.0.0 onwards only supports Node.js 18.19.0 and above + update-types: ["version-update:semver-major"] - dependency-name: "tap" # Contain breaking changes that are incompatible with our test usage update-types: ["version-update:semver-major"] @@ -83,15 +95,6 @@ updates: update-types: - "minor" - "patch" - exclude-patterns: - # Add entries that we should update manually. - - "@datadog/libdatadog" - - "@datadog/native-appsec" - - "@datadog/native-iast-taint-tracking" - - "@datadog/native-metrics" - - "@datadog/pprof" - - "@datadog/sketches-js" - - "@datadog/wasm-js-rewriter" - package-ecosystem: "npm" directories: - "/packages/dd-trace/test/plugins/versions" diff --git a/.github/workflows/test-optimization.yml b/.github/workflows/test-optimization.yml index 30695b5d5a7..874ce045360 100644 --- a/.github/workflows/test-optimization.yml +++ b/.github/workflows/test-optimization.yml @@ -95,11 +95,32 @@ jobs: NODE_OPTIONS: '-r ./ci/init' MOCHA_VERSION: ${{ matrix.mocha-version }} + integration-jest: + strategy: + matrix: + version: [oldest, latest] + jest-version: [24.8.0, latest] + runs-on: ubuntu-latest + env: + DD_SERVICE: dd-trace-js-integration-tests + DD_CIVISIBILITY_AGENTLESS_ENABLED: 1 + DD_API_KEY: ${{ secrets.DD_API_KEY }} + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: ./.github/actions/node + with: + version: ${{ matrix.version }} + - uses: ./.github/actions/install + - run: yarn test:integration:jest + env: + NODE_OPTIONS: '-r ./ci/init' + JEST_VERSION: ${{ matrix.jest-version }} + integration-ci: strategy: matrix: version: [oldest, latest] - framework: [cucumber, selenium, jest] + framework: [cucumber, selenium] runs-on: ubuntu-latest env: DD_SERVICE: dd-trace-js-integration-tests @@ -193,47 +214,3 @@ jobs: - uses: ./.github/actions/plugins/test with: dd_api_key: ${{ secrets.DD_API_KEY }} - - # TODO: fix performance issues and test more Node versions - plugin-cypress: - runs-on: ubuntu-latest - env: - PLUGINS: cypress - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: ./.github/actions/testagent/start - - uses: ./.github/actions/node/latest - - uses: ./.github/actions/install - - run: yarn test:plugins:ci - - if: always() - uses: ./.github/actions/testagent/logs - with: - suffix: plugins-${{ github.job }} - - uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 - - uses: DataDog/junit-upload-github-action@762867566348d59ac9bcf479ebb4ec040db8940a # v2.0.0 - if: always() - with: - api_key: ${{ secrets.DD_API_KEY }} - service: dd-trace-js-tests - - # TODO: fix performance issues and test more Node versions - plugin-jest: - runs-on: ubuntu-latest - env: - PLUGINS: jest - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: ./.github/actions/testagent/start - - uses: ./.github/actions/node/latest - - uses: ./.github/actions/install - - run: yarn test:plugins:ci - - if: always() - uses: ./.github/actions/testagent/logs - with: - suffix: plugins-${{ github.job }} - - uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 - - uses: DataDog/junit-upload-github-action@762867566348d59ac9bcf479ebb4ec040db8940a # v2.0.0 - if: always() - with: - api_key: ${{ secrets.DD_API_KEY }} - service: dd-trace-js-tests diff --git a/LICENSE-3rdparty.csv b/LICENSE-3rdparty.csv index 8d7a7e6c2bb..b080f035103 100644 --- a/LICENSE-3rdparty.csv +++ b/LICENSE-3rdparty.csv @@ -11,7 +11,7 @@ require,@opentelemetry/api,Apache license 2.0,Copyright OpenTelemetry Authors require,@opentelemetry/api-logs,Apache license 2.0,Copyright OpenTelemetry Authors require,@opentelemetry/core,Apache license 2.0,Copyright OpenTelemetry Authors require,@opentelemetry/resources,Apache license 2.0,Copyright OpenTelemetry Authors -require,@isaacs/ttlcache,ISC,Copyright (c) 2022-2023 - Isaac Z. Schlueter and Contributors +require,@isaacs/ttlcache,Blue Oak,Copyright Isaac Z. Schlueter and Contributors require,crypto-randomuuid,MIT,Copyright 2021 Node.js Foundation and contributors require,dc-polyfill,MIT,Copyright 2023 Datadog Inc. require,escape-string-regexp,MIT,Copyright Sindre Sorhus diff --git a/integration-tests/CODEOWNERS b/integration-tests/CODEOWNERS index 6c999942b1d..e22b9b1e431 100644 --- a/integration-tests/CODEOWNERS +++ b/integration-tests/CODEOWNERS @@ -1,3 +1,4 @@ # CODEOWNERS for testing purposes ci-visibility/subproject @datadog-dd-trace-js ci-visibility/ @datadog-dd-trace-js +cypress/ @datadog-dd-trace-js diff --git a/integration-tests/ci-visibility/automatic-log-submission/config-jest.js b/integration-tests/ci-visibility/automatic-log-submission/config-jest.js index 453408298ea..7a544332a81 100644 --- a/integration-tests/ci-visibility/automatic-log-submission/config-jest.js +++ b/integration-tests/ci-visibility/automatic-log-submission/config-jest.js @@ -6,5 +6,7 @@ module.exports = { cache: false, testMatch: [ '**/ci-visibility/automatic-log-submission/automatic-log-submission-*' - ] + ], + testRunner: 'jest-circus/runner', + testEnvironment: 'node' } diff --git a/packages/datadog-plugin-jest/test/jest-focus.js b/integration-tests/ci-visibility/jest-plugin-tests/jest-focus.js similarity index 100% rename from packages/datadog-plugin-jest/test/jest-focus.js rename to integration-tests/ci-visibility/jest-plugin-tests/jest-focus.js diff --git a/packages/datadog-plugin-jest/test/jest-hook-failure.js b/integration-tests/ci-visibility/jest-plugin-tests/jest-hook-failure.js similarity index 100% rename from packages/datadog-plugin-jest/test/jest-hook-failure.js rename to integration-tests/ci-visibility/jest-plugin-tests/jest-hook-failure.js diff --git a/packages/datadog-plugin-jest/test/jest-inject-globals.js b/integration-tests/ci-visibility/jest-plugin-tests/jest-inject-globals.js similarity index 59% rename from packages/datadog-plugin-jest/test/jest-inject-globals.js rename to integration-tests/ci-visibility/jest-plugin-tests/jest-inject-globals.js index 1057eec03a2..99b14736416 100644 --- a/packages/datadog-plugin-jest/test/jest-inject-globals.js +++ b/integration-tests/ci-visibility/jest-plugin-tests/jest-inject-globals.js @@ -1,6 +1,6 @@ 'use strict' -const { describe, it, expect } = require('../../../versions/@jest/globals').get() +const { describe, it, expect } = require('@jest/globals') describe('jest-inject-globals', () => { it('will be run', () => { diff --git a/packages/datadog-plugin-jest/test/jest-test-suite.js b/integration-tests/ci-visibility/jest-plugin-tests/jest-test-suite.js similarity index 100% rename from packages/datadog-plugin-jest/test/jest-test-suite.js rename to integration-tests/ci-visibility/jest-plugin-tests/jest-test-suite.js diff --git a/packages/datadog-plugin-jest/test/jest-test.js b/integration-tests/ci-visibility/jest-plugin-tests/jest-test.js similarity index 92% rename from packages/datadog-plugin-jest/test/jest-test.js rename to integration-tests/ci-visibility/jest-plugin-tests/jest-test.js index 8acfd652b97..8046535038b 100644 --- a/packages/datadog-plugin-jest/test/jest-test.js +++ b/integration-tests/ci-visibility/jest-plugin-tests/jest-test.js @@ -4,6 +4,9 @@ const http = require('http') const tracer = require('dd-trace') +const ENDPOINT_URL = process.env.DD_CIVISIBILITY_AGENTLESS_URL || + `http://127.0.0.1:${process.env.DD_TRACE_AGENT_PORT}` + describe('jest-test-suite', () => { jest.setTimeout(400) @@ -40,7 +43,7 @@ describe('jest-test-suite', () => { }) it('can do integration http', (done) => { - const req = http.request('http://test:123', (res) => { + const req = http.request(`${ENDPOINT_URL}/info`, { agent: false }, (res) => { expect(res.statusCode).toEqual(200) done() }) diff --git a/integration-tests/ci-visibility/run-jest.js b/integration-tests/ci-visibility/run-jest.js index 44c87960a24..ae5cf4cae3c 100644 --- a/integration-tests/ci-visibility/run-jest.js +++ b/integration-tests/ci-visibility/run-jest.js @@ -5,12 +5,15 @@ const jest = require('jest') const options = { projects: [__dirname], testPathIgnorePatterns: ['/node_modules/'], + modulePathIgnorePatterns: ['/\\.bun/'], cache: false, testRegex: process.env.TESTS_TO_RUN ? new RegExp(process.env.TESTS_TO_RUN) : /test\/ci-visibility-test/, coverage: !!process.env.ENABLE_CODE_COVERAGE, runInBand: true, shard: process.env.TEST_SHARD || undefined, - setupFilesAfterEnv: process.env.SETUP_FILES_AFTER_ENV ? process.env.SETUP_FILES_AFTER_ENV.split(',') : [] + setupFilesAfterEnv: process.env.SETUP_FILES_AFTER_ENV ? process.env.SETUP_FILES_AFTER_ENV.split(',') : [], + testRunner: 'jest-circus/runner', + testEnvironment: 'node' } if (process.env.RUN_IN_PARALLEL) { @@ -34,6 +37,10 @@ if (process.env.COLLECT_COVERAGE_FROM) { options.collectCoverageFrom = process.env.COLLECT_COVERAGE_FROM.split(',') } +if (process.env.DO_NOT_INJECT_GLOBALS) { + options.injectGlobals = false +} + jest.runCLI( options, options.projects diff --git a/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test-2.js b/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test-2.js index 06676e9b090..2bbefae4ef0 100644 --- a/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test-2.js +++ b/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test-2.js @@ -1,10 +1,9 @@ 'use strict' -const assert = require('node:assert') const sum = require('../sum') describe('ci visibility 2', () => { it('can report tests 2', () => { - assert.strictEqual(sum(1, 2), 3) + expect(sum(1, 2)).toEqual(3) }) }) diff --git a/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test-3.js b/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test-3.js index 9840da383df..e904010e679 100644 --- a/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test-3.js +++ b/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test-3.js @@ -1,10 +1,9 @@ 'use strict' -const assert = require('node:assert') const sum = require('../sum') describe('ci visibility 3', () => { it('can report tests 3', () => { - assert.strictEqual(sum(1, 2), 3) + expect(sum(1, 2)).toEqual(3) }) }) diff --git a/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test-4.js b/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test-4.js index 95185708297..6d282bfd992 100644 --- a/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test-4.js +++ b/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test-4.js @@ -1,10 +1,9 @@ 'use strict' -const assert = require('node:assert') const sum = require('../sum') describe('ci visibility 4', () => { it('can report tests 4', () => { - assert.strictEqual(sum(1, 2), 3) + expect(sum(1, 2)).toEqual(3) }) }) diff --git a/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test.js b/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test.js index 0780ed3a983..fba87f0ea98 100644 --- a/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test.js +++ b/integration-tests/ci-visibility/test/efd-parallel/ci-visibility-test.js @@ -1,10 +1,9 @@ 'use strict' -const assert = require('node:assert') const sum = require('../sum') describe('ci visibility', () => { it('can report tests', () => { - assert.strictEqual(sum(1, 2), 3) + expect(sum(1, 2)).toEqual(3) }) }) diff --git a/integration-tests/config-jest-multiproject.js b/integration-tests/config-jest-multiproject.js index 90190430044..7b810d808b1 100644 --- a/integration-tests/config-jest-multiproject.js +++ b/integration-tests/config-jest-multiproject.js @@ -8,7 +8,9 @@ module.exports = { cache: false, testMatch: [ '**/ci-visibility/test/ci-visibility-test*' - ] + ], + testRunner: 'jest-circus/runner', + testEnvironment: 'node' }, { displayName: 'node', @@ -16,7 +18,9 @@ module.exports = { cache: false, testMatch: [ '**/ci-visibility/test/ci-visibility-test*' - ] + ], + testRunner: 'jest-circus/runner', + testEnvironment: 'node' } ] } diff --git a/integration-tests/config-jest.js b/integration-tests/config-jest.js index 8d85aa43a8f..8436590b832 100644 --- a/integration-tests/config-jest.js +++ b/integration-tests/config-jest.js @@ -6,5 +6,7 @@ module.exports = { cache: false, testMatch: [ process.env.TESTS_TO_RUN || '**/ci-visibility/test/ci-visibility-test*' - ] + ], + testRunner: 'jest-circus/runner', + testEnvironment: 'node' } diff --git a/integration-tests/cucumber/cucumber.spec.js b/integration-tests/cucumber/cucumber.spec.js index 42fb93028fb..845849a17e0 100644 --- a/integration-tests/cucumber/cucumber.spec.js +++ b/integration-tests/cucumber/cucumber.spec.js @@ -1841,11 +1841,19 @@ versions.forEach(version => { './node_modules/.bin/cucumber-js ci-visibility/features-di/test-hit-breakpoint.feature --retry 1', { cwd, - env: envVars, + env: { + ...envVars, + DD_TRACE_DEBUG: '1', + DD_TRACE_LOG_LEVEL: 'warn', + }, stdio: 'pipe' } ) + // TODO: remove once we figure out flakiness + childProcess.stdout.pipe(process.stdout) + childProcess.stderr.pipe(process.stderr) + childProcess.on('exit', () => { Promise.all([eventsPromise, logsPromise]).then(() => { assert.equal(snapshotIdByTest, snapshotIdByLog) diff --git a/integration-tests/cypress/cypress.spec.js b/integration-tests/cypress/cypress.spec.js index 17a8469d287..3f0aa09c215 100644 --- a/integration-tests/cypress/cypress.spec.js +++ b/integration-tests/cypress/cypress.spec.js @@ -23,7 +23,9 @@ const { TEST_STATUS, TEST_COMMAND, TEST_MODULE, + TEST_FRAMEWORK, TEST_FRAMEWORK_VERSION, + TEST_TYPE, TEST_TOOLCHAIN, TEST_CODE_COVERAGE_ENABLED, TEST_ITR_SKIPPING_ENABLED, @@ -63,7 +65,7 @@ const { TEST_IS_MODIFIED } = require('../../packages/dd-trace/src/plugins/util/test') const { DD_HOST_CPU_COUNT } = require('../../packages/dd-trace/src/plugins/util/env') -const { ERROR_MESSAGE } = require('../../packages/dd-trace/src/constants') +const { ERROR_MESSAGE, ERROR_TYPE, COMPONENT } = require('../../packages/dd-trace/src/constants') const { DD_MAJOR, NODE_MAJOR } = require('../../version') const RECEIVER_STOP_TIMEOUT = 20000 @@ -129,10 +131,10 @@ moduleTypes.forEach(({ testCommand = testCommand(version) } + // cypress-fail-fast is required as an incompatible plugin useSandbox([`cypress@${version}`, 'cypress-fail-fast@7.1.0'], true) before(async () => { - // cypress-fail-fast is required as an incompatible plugin cwd = sandboxCwd() const { NODE_OPTIONS, ...restOfEnv } = process.env @@ -176,6 +178,95 @@ moduleTypes.forEach(({ } }) + it('instruments tests with the APM protocol (old agents)', async () => { + receiver.setInfoResponse({ endpoints: [] }) + + const receiverPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url === '/v0.4/traces', (payloads) => { + const testSpans = payloads.flatMap(({ payload }) => payload.flatMap(trace => trace)) + + const passedTestSpan = testSpans.find(span => + span.resource === 'cypress/e2e/basic-pass.js.basic pass suite can pass' + ) + const failedTestSpan = testSpans.find(span => + span.resource === 'cypress/e2e/basic-fail.js.basic fail suite can fail' + ) + + assert.exists(passedTestSpan, 'passed test span should exist') + assert.equal(passedTestSpan.name, 'cypress.test') + assert.equal(passedTestSpan.resource, 'cypress/e2e/basic-pass.js.basic pass suite can pass') + assert.equal(passedTestSpan.type, 'test') + assert.equal(passedTestSpan.meta[TEST_STATUS], 'pass') + assert.equal(passedTestSpan.meta[TEST_NAME], 'basic pass suite can pass') + assert.equal(passedTestSpan.meta[TEST_SUITE], 'cypress/e2e/basic-pass.js') + assert.equal(passedTestSpan.meta[TEST_FRAMEWORK], 'cypress') + assert.equal(passedTestSpan.meta[TEST_TYPE], 'browser') + assert.exists(passedTestSpan.meta[TEST_SOURCE_FILE]) + assert.include(passedTestSpan.meta[TEST_SOURCE_FILE], 'cypress/e2e/basic-pass.js') + assert.exists(passedTestSpan.meta[TEST_FRAMEWORK_VERSION]) + assert.exists(passedTestSpan.meta[COMPONENT]) + assert.exists(passedTestSpan.metrics[TEST_SOURCE_START]) + assert.equal(passedTestSpan.meta[TEST_CODE_OWNERS], JSON.stringify(['@datadog-dd-trace-js'])) + assert.equal(passedTestSpan.meta.customTag, 'customValue') + assert.equal(passedTestSpan.meta.addTagsBeforeEach, 'customBeforeEach') + assert.equal(passedTestSpan.meta.addTagsAfterEach, 'customAfterEach') + + assert.exists(failedTestSpan, 'failed test span should exist') + assert.equal(failedTestSpan.name, 'cypress.test') + assert.equal(failedTestSpan.resource, 'cypress/e2e/basic-fail.js.basic fail suite can fail') + assert.equal(failedTestSpan.type, 'test') + assert.equal(failedTestSpan.meta[TEST_STATUS], 'fail') + assert.equal(failedTestSpan.meta[TEST_NAME], 'basic fail suite can fail') + assert.equal(failedTestSpan.meta[TEST_SUITE], 'cypress/e2e/basic-fail.js') + assert.equal(failedTestSpan.meta[TEST_FRAMEWORK], 'cypress') + assert.equal(failedTestSpan.meta[TEST_TYPE], 'browser') + assert.exists(failedTestSpan.meta[TEST_SOURCE_FILE]) + assert.include(failedTestSpan.meta[TEST_SOURCE_FILE], 'cypress/e2e/basic-fail.js') + assert.exists(failedTestSpan.meta[TEST_FRAMEWORK_VERSION]) + assert.exists(failedTestSpan.meta[COMPONENT]) + assert.exists(failedTestSpan.meta[ERROR_MESSAGE]) + assert.include(failedTestSpan.meta[ERROR_MESSAGE], 'expected') + assert.exists(failedTestSpan.meta[ERROR_TYPE]) + assert.exists(failedTestSpan.metrics[TEST_SOURCE_START]) + assert.equal(passedTestSpan.meta[TEST_CODE_OWNERS], JSON.stringify(['@datadog-dd-trace-js'])) + assert.equal(failedTestSpan.meta.customTag, 'customValue') + assert.equal(failedTestSpan.meta.addTagsBeforeEach, 'customBeforeEach') + assert.equal(failedTestSpan.meta.addTagsAfterEach, 'customAfterEach') + // Tags added after failure should not be present because test failed + assert.notProperty(failedTestSpan.meta, 'addTagsAfterFailure') + }, 60000) + + const { + NODE_OPTIONS, + ...restEnvVars + } = getCiVisEvpProxyConfig(receiver.port) + + const specToRun = 'cypress/e2e/basic-*.js' + + // For Cypress 6.7.0, we need to override the --spec flag that's hardcoded in testCommand + const command = version === '6.7.0' + ? `./node_modules/.bin/cypress run --config-file cypress-config.json --spec "${specToRun}"` + : testCommand + + childProcess = exec( + command, + { + cwd, + env: { + ...restEnvVars, + CYPRESS_BASE_URL: `http://localhost:${webAppPort}`, + SPEC_PATTERN: specToRun + }, + stdio: 'pipe' + } + ) + + await Promise.all([ + once(childProcess, 'exit'), + receiverPromise + ]) + }) + if (version === '6.7.0') { // to be removed when we drop support for cypress@6.7.0 it('logs a warning if using a deprecated version of cypress', async () => { @@ -518,10 +609,11 @@ moduleTypes.forEach(({ context('intelligent test runner', () => { it('can report git metadata', async () => { const searchCommitsRequestPromise = receiver.payloadReceived( - ({ url }) => url.endsWith('/api/v2/git/repository/search_commits') + ({ url }) => url.endsWith('/api/v2/git/repository/search_commits'), + 25000 ) const packfileRequestPromise = receiver - .payloadReceived(({ url }) => url.endsWith('/api/v2/git/repository/packfile')) + .payloadReceived(({ url }) => url.endsWith('/api/v2/git/repository/packfile'), 25000) const { NODE_OPTIONS, // NODE_OPTIONS dd-trace config does not work with cypress @@ -541,6 +633,10 @@ moduleTypes.forEach(({ } ) + // TODO: remove this once we have figured out flakiness + childProcess.stdout.pipe(process.stdout) + childProcess.stderr.pipe(process.stderr) + const [, searchCommitRequest, packfileRequest] = await Promise.all([ once(childProcess, 'exit'), searchCommitsRequestPromise, @@ -629,13 +725,13 @@ moduleTypes.forEach(({ }, 25000) const coverageRequestPromise = receiver - .payloadReceived(({ url }) => url.endsWith('/api/v2/citestcov')) + .payloadReceived(({ url }) => url.endsWith('/api/v2/citestcov'), 25000) .then(coverageRequest => { assert.propertyVal(coverageRequest.headers, 'dd-api-key', '1') }) const skippableRequestPromise = receiver - .payloadReceived(({ url }) => url.endsWith('/api/v2/ci/tests/skippable')) + .payloadReceived(({ url }) => url.endsWith('/api/v2/ci/tests/skippable'), 25000) .then(skippableRequest => { assert.propertyVal(skippableRequest.headers, 'dd-api-key', '1') }) @@ -658,6 +754,10 @@ moduleTypes.forEach(({ } ) + // TODO: remove this once we have figured out flakiness + childProcess.stdout.pipe(process.stdout) + childProcess.stderr.pipe(process.stderr) + await Promise.all([ once(childProcess, 'exit'), eventsPromise, @@ -683,7 +783,7 @@ moduleTypes.forEach(({ receiver.assertPayloadReceived(() => { hasRequestedSkippable = true - }, ({ url }) => url.endsWith('/api/v2/ci/tests/skippable')).catch(() => {}) + }, ({ url }) => url.endsWith('/api/v2/ci/tests/skippable'), 25000).catch(() => {}) const receiverPromise = receiver .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), (payloads) => { @@ -713,6 +813,10 @@ moduleTypes.forEach(({ } ) + // TODO: remove this once we have figured out flakiness + childProcess.stdout.pipe(process.stdout) + childProcess.stderr.pipe(process.stderr) + await Promise.all([ once(childProcess, 'exit'), receiverPromise @@ -788,6 +892,10 @@ moduleTypes.forEach(({ } ) + // TODO: remove this once we have figured out flakiness + childProcess.stdout.pipe(process.stdout) + childProcess.stderr.pipe(process.stderr) + await Promise.all([ once(childProcess, 'exit'), receiverPromise @@ -857,6 +965,10 @@ moduleTypes.forEach(({ } ) + // TODO: remove this once we have figured out flakiness + childProcess.stdout.pipe(process.stdout) + childProcess.stderr.pipe(process.stderr) + await Promise.all([ once(childProcess, 'exit'), receiverPromise @@ -884,10 +996,10 @@ moduleTypes.forEach(({ assert.propertyVal(testModule.meta, TEST_CODE_COVERAGE_ENABLED, 'true') assert.propertyVal(testModule.meta, TEST_ITR_SKIPPING_ENABLED, 'true') assert.propertyVal(testModule.metrics, TEST_ITR_SKIPPING_COUNT, 0) - }, 25000) + }, 30000) const skippableRequestPromise = receiver - .payloadReceived(({ url }) => url.endsWith('/api/v2/ci/tests/skippable')) + .payloadReceived(({ url }) => url.endsWith('/api/v2/ci/tests/skippable'), 30000) .then(skippableRequest => { assert.propertyVal(skippableRequest.headers, 'dd-api-key', '1') }) @@ -910,6 +1022,10 @@ moduleTypes.forEach(({ } ) + // TODO: remove this once we have figured out flakiness + childProcess.stdout.pipe(process.stdout) + childProcess.stderr.pipe(process.stderr) + await Promise.all([ once(childProcess, 'exit'), eventsPromise, @@ -1003,6 +1119,10 @@ moduleTypes.forEach(({ } ) + // TODO: remove this once we have figured out flakiness + childProcess.stdout.pipe(process.stdout) + childProcess.stderr.pipe(process.stderr) + await Promise.all([ once(childProcess, 'exit'), eventsPromise @@ -1434,10 +1554,6 @@ moduleTypes.forEach(({ } ) - // TODO: remove this once we have figured out flakiness - childProcess.stdout.pipe(process.stdout) - childProcess.stderr.pipe(process.stderr) - await Promise.all([ once(childProcess, 'exit'), receiverPromise @@ -1500,10 +1616,6 @@ moduleTypes.forEach(({ } ) - // TODO: remove this once we have figured out flakiness - childProcess.stdout.pipe(process.stdout) - childProcess.stderr.pipe(process.stderr) - await Promise.all([ once(childProcess, 'exit'), receiverPromise, @@ -1592,6 +1704,10 @@ moduleTypes.forEach(({ } ) + // TODO: remove this once we have figured out flakiness + childProcess.stdout.pipe(process.stdout) + childProcess.stderr.pipe(process.stderr) + await Promise.all([ once(childProcess, 'exit'), receiverPromise @@ -2053,6 +2169,10 @@ moduleTypes.forEach(({ } ) + // TODO: remove this once we have figured out flakiness + childProcess.stdout.pipe(process.stdout) + childProcess.stderr.pipe(process.stderr) + const [[exitCode]] = await Promise.all([ once(childProcess, 'exit'), testAssertionsPromise diff --git a/integration-tests/cypress/e2e/basic-fail.js b/integration-tests/cypress/e2e/basic-fail.js new file mode 100644 index 00000000000..c2a618dda63 --- /dev/null +++ b/integration-tests/cypress/e2e/basic-fail.js @@ -0,0 +1,19 @@ +/* eslint-disable */ +describe('basic fail suite', () => { + beforeEach(() => { + cy.visit('/') + cy.task('dd:addTags', { addTagsBeforeEach: 'customBeforeEach' }) + }) + + afterEach(() => { + cy.task('dd:addTags', { addTagsAfterEach: 'customAfterEach' }) + }) + + it('can fail', () => { + cy.task('dd:addTags', { customTag: 'customValue' }) + cy.get('.hello-world') + .should('have.text', 'Hello warld') + cy.task('dd:addTags', { addTagsAfterFailure: 'customAfterFailure' }) + }) +}) + diff --git a/integration-tests/cypress/e2e/basic-pass.js b/integration-tests/cypress/e2e/basic-pass.js new file mode 100644 index 00000000000..6323c75c084 --- /dev/null +++ b/integration-tests/cypress/e2e/basic-pass.js @@ -0,0 +1,18 @@ +/* eslint-disable */ +describe('basic pass suite', () => { + beforeEach(() => { + cy.visit('/') + cy.task('dd:addTags', { addTagsBeforeEach: 'customBeforeEach' }) + }) + + afterEach(() => { + cy.task('dd:addTags', { addTagsAfterEach: 'customAfterEach' }) + }) + + it('can pass', () => { + cy.task('dd:addTags', { customTag: 'customValue' }) + cy.get('.hello-world') + .should('have.text', 'Hello World') + }) +}) + diff --git a/integration-tests/esbuild/index.spec.js b/integration-tests/esbuild/index.spec.js index 3f399451169..8cf991a0d84 100755 --- a/integration-tests/esbuild/index.spec.js +++ b/integration-tests/esbuild/index.spec.js @@ -4,55 +4,59 @@ 'use strict' -const chproc = require('child_process') -const pathModule = require('path') -const fs = require('fs') +const assert = require('node:assert/strict') +const chproc = require('node:child_process') +const pathModule = require('node:path') +const fs = require('node:fs') + // TODO: It shouldn't be necessary to disable n/no-extraneous-require - Research // eslint-disable-next-line n/no-extraneous-require -const { assert } = require('chai') +const { describe, before, it } = require('mocha') // sub process must be executed inside TEST_DIR const TEST_DIR = pathModule.join(__dirname, '.') -const execSync = (command, options) => chproc.execSync(command, { ...(options ?? {}), cwd: TEST_DIR }) +const execSync = (command, options) => { + console.log(command) + chproc.execSync(command, { ...(options ?? {}), cwd: TEST_DIR }) +} const rmSync = (path, options) => fs.rmSync(pathModule.join(TEST_DIR, path), options) const readFileSync = (path, options) => fs.readFileSync(pathModule.join(TEST_DIR, path), options) const originalDir = process.cwd() +const versionsPackageJson = require('../../packages/dd-trace/test/plugins/versions/package.json') +const maximumEsbuildVersion = versionsPackageJson.dependencies.esbuild + // This should switch to our withVersion helper. The order here currently matters. -const esbuildVersions = ['latest', '0.16.12'] +const esbuildVersions = ['0.16.12', maximumEsbuildVersion] +const timeout = 1000 * 45 esbuildVersions.forEach((version) => { - describe(`esbuild ${version}`, () => { + describe(`esbuild ${version}`, function () { + this.timeout(timeout) + before(() => { process.chdir(TEST_DIR) execSync('npm install', { - timeout: 1000 * 30 + timeout + }) + execSync(`npm install esbuild@${version}`, { + timeout }) - if (version === 'latest') { - const versionsPackageJson = require('../../packages/dd-trace/test/plugins/versions/package.json') - const version = versionsPackageJson.dependencies.esbuild - execSync(`npm install esbuild@${version}`, { - timeout: 1000 * 30 - }) - } else { - execSync(`npm install esbuild@${version}`, { - timeout: 1000 * 30 - }) - } }) after(() => { process.chdir(originalDir) + execSync('npm remove esbuild', { + timeout + }) }) it('works', () => { - console.log('npm run build') execSync('npm run build') - console.log('npm run built') try { execSync('npm run built', { - timeout: 1000 * 30 + timeout }) } catch (err) { console.error(err) @@ -63,50 +67,38 @@ esbuildVersions.forEach((version) => { }) it('does not bundle modules listed in .external', () => { - const command = 'node ./build-and-test-skip-external.js' - console.log(command) - execSync(command, { - timeout: 1000 * 30 + execSync('node ./build-and-test-skip-external.js', { + timeout }) }) it('handles typescript apps that import without file extensions', () => { - const command = 'node ./build-and-test-typescript.mjs' - console.log(command) - execSync(command, { - timeout: 1000 * 30 + execSync('node ./build-and-test-typescript.mjs', { + timeout }) }) it('handles the complex aws-sdk package with dynamic requires', () => { - const command = 'node ./build-and-test-aws-sdk.js' - console.log(command) - execSync(command, { - timeout: 1000 * 30 + execSync('node ./build-and-test-aws-sdk.js', { + timeout }) }) it('handles scoped node_modules', () => { - const command = 'node ./build-and-test-koa.mjs' - console.log(command) - execSync(command, { - timeout: 1000 * 30 + execSync('node ./build-and-test-koa.mjs', { + timeout }) }) it('handles instrumentations where the patching function is a property of the hook', () => { - const command = 'node ./build-and-test-openai.js' - console.log(command) - execSync(command, { - timeout: 1000 * 30 + execSync('node ./build-and-test-openai.js', { + timeout }) }) it('injects Git metadata into bundled applications', () => { - const command = 'node ./build-and-test-git-tags.js' - console.log(command) - execSync(command, { - timeout: 1000 * 30 + execSync('node ./build-and-test-git-tags.js', { + timeout }) }) @@ -118,82 +110,66 @@ esbuildVersions.forEach((version) => { }) it('works', () => { - console.log('npm run build:esm') execSync('npm run build:esm') - console.log('npm run built:esm') execSync('npm run built:esm', { - timeout: 1000 * 30 + timeout }) }) it('should not override existing js banner', () => { - const command = 'node ./build-and-run.esm-unrelated-js-banner.mjs' - console.log(command) - execSync(command, { - timeout: 1000 * 30 + execSync('node ./build-and-run.esm-unrelated-js-banner.mjs', { + timeout }) const builtFile = readFileSync('./out.mjs').toString() - assert.include(builtFile, '/* js test */') + assert.match(builtFile, /\/\* js test \*\//m) }) it('should contain the definitions when esm is inferred from outfile', () => { - const command = 'node ./build-and-run.esm-relying-in-extension.mjs' - console.log(command) - execSync(command, { - timeout: 1000 * 30 + execSync('node ./build-and-run.esm-relying-in-extension.mjs', { + timeout }) const builtFile = readFileSync('./out.mjs').toString() - assert.include(builtFile, 'globalThis.__filename ??= $dd_fileURLToPath(import.meta.url);') + assert.match(builtFile, /globalThis\.__filename \?\?= \$dd_fileURLToPath\(import\.meta\.url\);/m) }) it('should contain the definitions when esm is inferred from format', () => { - const command = 'node ./build-and-run.esm-relying-in-format.mjs' - console.log(command) - execSync(command, { - timeout: 1000 * 30 + execSync('node ./build-and-run.esm-relying-in-format.mjs', { + timeout }) const builtFile = readFileSync('./out.mjs').toString() - assert.include(builtFile, 'globalThis.__filename ??= $dd_fileURLToPath(import.meta.url);') + assert.match(builtFile, /globalThis\.__filename \?\?= \$dd_fileURLToPath\(import\.meta\.url\);/m) }) it('should contain the definitions when format is inferred from out extension', () => { - const command = 'node ./build-and-run.esm-relying-in-out-extension.mjs' - console.log(command) - execSync(command, { - timeout: 1000 * 30 + execSync('node ./build-and-run.esm-relying-in-out-extension.mjs', { + timeout }) const builtFile = readFileSync('./basic-test.mjs').toString() - assert.include(builtFile, 'globalThis.__filename ??= $dd_fileURLToPath(import.meta.url);') + assert.match(builtFile, /globalThis\.__filename \?\?= \$dd_fileURLToPath\(import\.meta\.url\);/m) }) it('should not contain the definitions when no esm is specified', () => { - const command = 'node ./build.js' - console.log(command) - execSync(command, { - timeout: 1000 * 30 + execSync('node ./build.js', { + timeout }) const builtFile = readFileSync('./out.js').toString() - assert.notInclude(builtFile, 'globalThis.__filename ??= $dd_fileURLToPath(import.meta.url);') + assert.doesNotMatch(builtFile, /globalThis\.__filename \?\?= \$dd_fileURLToPath\(import\.meta\.url\);/m) }) it('should not crash when it is already patched using global', () => { - const command = 'node ./build-and-run.esm-patched-global-banner.mjs' - console.log(command) - execSync(command, { - timeout: 1000 * 30 + execSync('node ./build-and-run.esm-patched-global-banner.mjs', { + timeout }) }) it('should not crash when it is already patched using const', () => { - const command = 'node ./build-and-run.esm-patched-const-banner.mjs' - console.log(command) - execSync(command, { - timeout: 1000 * 30 + execSync('node ./build-and-run.esm-patched-const-banner.mjs', { + timeout }) }) }) diff --git a/integration-tests/esbuild/package.json b/integration-tests/esbuild/package.json index 9b05b235068..bf87d3bcf76 100644 --- a/integration-tests/esbuild/package.json +++ b/integration-tests/esbuild/package.json @@ -23,11 +23,10 @@ "@apollo/server": "5.1.0", "@koa/router": "14.0.0", "aws-sdk": "2.1692.0", - "axios": "1.13.1", - "esbuild": "^0.16.12", + "axios": "1.13.2", "express": "4.21.2", "knex": "3.1.0", "koa": "3.1.1", - "openai": "6.7.0" + "openai": "6.8.1" } } diff --git a/integration-tests/helpers/index.js b/integration-tests/helpers/index.js index 8e96294e958..998a629e495 100644 --- a/integration-tests/helpers/index.js +++ b/integration-tests/helpers/index.js @@ -422,7 +422,7 @@ varySandbox.VARIANTS = ['default', 'star', 'destructure'] function telemetryForwarder (shouldExpectTelemetryPoints = true) { process.env.DD_TELEMETRY_FORWARDER_PATH = path.join(__dirname, '..', 'telemetry-forwarder.sh') - process.env.FORWARDER_OUT = path.join(__dirname, `forwarder-${Date.now()}.out`) + process.env.FORWARDER_OUT = path.join(__dirname, 'output', `forwarder-${Date.now()}.out`) let retries = 0 diff --git a/integration-tests/helpers/output/.gitignore b/integration-tests/helpers/output/.gitignore new file mode 100644 index 00000000000..f47cb2045f1 --- /dev/null +++ b/integration-tests/helpers/output/.gitignore @@ -0,0 +1 @@ +*.out diff --git a/integration-tests/jest/jest.spec.js b/integration-tests/jest/jest.spec.js index 2e3edd3e69e..adb75fa0c69 100644 --- a/integration-tests/jest/jest.spec.js +++ b/integration-tests/jest/jest.spec.js @@ -59,11 +59,24 @@ const { TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED, TEST_IS_MODIFIED, TEST_RETRY_REASON_TYPES, - DD_CAPABILITIES_IMPACTED_TESTS + DD_CAPABILITIES_IMPACTED_TESTS, + TEST_FRAMEWORK, + TEST_TYPE, + TEST_FRAMEWORK_VERSION, + CI_APP_ORIGIN, + JEST_TEST_RUNNER, + TEST_PARAMETERS, + LIBRARY_VERSION, + TEST_SUITE_ID, + TEST_MODULE_ID, + TEST_SESSION_ID, + TEST_MODULE, + TEST_COMMAND, } = require('../../packages/dd-trace/src/plugins/util/test') const { DD_HOST_CPU_COUNT } = require('../../packages/dd-trace/src/plugins/util/env') -const { ERROR_MESSAGE, ERROR_TYPE } = require('../../packages/dd-trace/src/constants') +const { ERROR_MESSAGE, ERROR_TYPE, ORIGIN_KEY, COMPONENT } = require('../../packages/dd-trace/src/constants') const { NODE_MAJOR } = require('../../version') +const { version: ddTraceVersion } = require('../../package.json') const testFile = 'ci-visibility/run-jest.js' const expectedStdout = 'Test Suites: 2 passed' @@ -74,8 +87,11 @@ const expectedCoverageFiles = [ ] const runTestsCommand = 'node ./ci-visibility/run-jest.js' +const JEST_VERSION = process.env.JEST_VERSION || 'latest' +const onlyLatestIt = JEST_VERSION === 'latest' ? it : it.skip + // TODO: add ESM tests -describe('jest CommonJS', () => { +describe(`jest@${JEST_VERSION} commonJS`, () => { let receiver let childProcess let cwd @@ -83,16 +99,19 @@ describe('jest CommonJS', () => { let testOutput = '' useSandbox([ - 'jest', + `jest@${JEST_VERSION}`, 'chai@v4', - 'jest-jasmine2', - 'jest-environment-jsdom', + `jest-jasmine2@${JEST_VERSION}`, + // jest-environment-jsdom is included in older versions of jest + JEST_VERSION === 'latest' && `jest-environment-jsdom@${JEST_VERSION}`, + // jest-circus is not included in older versions of jest + JEST_VERSION !== 'latest' && `jest-circus@${JEST_VERSION}`, '@happy-dom/jest-environment', 'office-addin-mock', 'winston', 'jest-image-snapshot', '@fast-check/jest' - ], true) + ].filter(Boolean), true) before(function () { cwd = sandboxCwd() @@ -109,81 +128,23 @@ describe('jest CommonJS', () => { await receiver.stop() }) - it('can run tests and report tests with the APM protocol (old agents)', (done) => { - receiver.setInfoResponse({ endpoints: [] }) - receiver.payloadReceived(({ url }) => url === '/v0.4/traces').then(({ payload }) => { - const testSpans = payload.flatMap(trace => trace) - const resourceNames = testSpans.map(span => span.resource) - - assert.includeMembers(resourceNames, - [ - 'ci-visibility/test/ci-visibility-test.js.ci visibility can report tests', - 'ci-visibility/test/ci-visibility-test-2.js.ci visibility 2 can report tests 2' - ] - ) - - const areAllTestSpans = testSpans.every(span => span.name === 'jest.test') - assert.isTrue(areAllTestSpans) - - assert.include(testOutput, expectedStdout) - - // Can read DD_TAGS - testSpans.forEach(testSpan => { - assert.propertyVal(testSpan.meta, 'test.customtag', 'customvalue') - assert.propertyVal(testSpan.meta, 'test.customtag2', 'customvalue2') - }) - - testSpans.forEach(testSpan => { - assert.equal(testSpan.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test'), true) - assert.exists(testSpan.metrics[TEST_SOURCE_START]) - }) - - done() - }) - - childProcess = fork(startupTestFile, { - cwd, - env: { + context('older versions of the agent (APM protocol)', () => { + let oldApmProtocolEnvVars = {} + beforeEach(() => { + receiver.setInfoResponse({ endpoints: [] }) + oldApmProtocolEnvVars = { + ...process.env, + GITHUB_WORKSPACE: '', // so the repository root is not assigned to dd-trace-js + DD_INSTRUMENTATION_TELEMETRY_ENABLED: 'false', DD_TRACE_AGENT_PORT: receiver.port, NODE_OPTIONS: '-r dd-trace/ci/init', - DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2' - }, - stdio: 'pipe' - }) - childProcess.stdout.on('data', (chunk) => { - testOutput += chunk.toString() - }) - childProcess.stderr.on('data', (chunk) => { - testOutput += chunk.toString() - }) - }) - - const nonLegacyReportingOptions = ['agentless', 'evp proxy'] - - nonLegacyReportingOptions.forEach((reportingOption) => { - it(`can run and report tests with ${reportingOption}`, (done) => { - const envVars = reportingOption === 'agentless' - ? getCiVisAgentlessConfig(receiver.port) - : getCiVisEvpProxyConfig(receiver.port) - if (reportingOption === 'evp proxy') { - receiver.setInfoResponse({ endpoints: ['/evp_proxy/v4'] }) + DD_CIVISIBILITY_AGENTLESS_ENABLED: '0', } - receiver.gatherPayloadsMaxTimeout(({ url }) => url.endsWith('citestcycle'), (payloads) => { - const metadataDicts = payloads.flatMap(({ payload }) => payload.metadata) - - metadataDicts.forEach(metadata => { - for (const testLevel of TEST_LEVEL_EVENT_TYPES) { - assert.equal(metadata[testLevel][TEST_SESSION_NAME], 'my-test-session') - } - }) - - const events = payloads.flatMap(({ payload }) => payload.events) - const sessionEventContent = events.find(event => event.type === 'test_session_end').content - const moduleEventContent = events.find(event => event.type === 'test_module_end').content - const suites = events.filter(event => event.type === 'test_suite_end').map(event => event.content) - const tests = events.filter(event => event.type === 'test').map(event => event.content) - - const resourceNames = tests.map(span => span.resource) + }) + it('can run tests and report tests', (done) => { + receiver.payloadReceived(({ url }) => url === '/v0.4/traces').then(({ payload }) => { + const testSpans = payload.flatMap(trace => trace) + const resourceNames = testSpans.map(span => span.resource) assert.includeMembers(resourceNames, [ @@ -191,26 +152,21 @@ describe('jest CommonJS', () => { 'ci-visibility/test/ci-visibility-test-2.js.ci visibility 2 can report tests 2' ] ) - assert.equal(suites.length, 2) - assert.exists(sessionEventContent) - assert.exists(moduleEventContent) + + const areAllTestSpans = testSpans.every(span => span.name === 'jest.test') + assert.isTrue(areAllTestSpans) assert.include(testOutput, expectedStdout) - tests.forEach(testEvent => { - assert.equal(testEvent.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test'), true) - assert.exists(testEvent.metrics[TEST_SOURCE_START]) - assert.equal(testEvent.meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'false') - // Can read DD_TAGS - assert.propertyVal(testEvent.meta, 'test.customtag', 'customvalue') - assert.propertyVal(testEvent.meta, 'test.customtag2', 'customvalue2') - assert.exists(testEvent.metrics[DD_HOST_CPU_COUNT]) + // Can read DD_TAGS + testSpans.forEach(testSpan => { + assert.propertyVal(testSpan.meta, 'test.customtag', 'customvalue') + assert.propertyVal(testSpan.meta, 'test.customtag2', 'customvalue2') }) - suites.forEach(testSuite => { - assert.isTrue(testSuite.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test')) - assert.equal(testSuite.metrics[TEST_SOURCE_START], 1) - assert.exists(testSuite.metrics[DD_HOST_CPU_COUNT]) + testSpans.forEach(testSpan => { + assert.equal(testSpan.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test'), true) + assert.exists(testSpan.metrics[TEST_SOURCE_START]) }) done() @@ -219,10 +175,10 @@ describe('jest CommonJS', () => { childProcess = fork(startupTestFile, { cwd, env: { - ...envVars, - DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2', - DD_TEST_SESSION_NAME: 'my-test-session', - DD_SERVICE: undefined + DD_INSTRUMENTATION_TELEMETRY_ENABLED: 'false', + DD_TRACE_AGENT_PORT: receiver.port, + NODE_OPTIONS: '-r dd-trace/ci/init', + DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2' }, stdio: 'pipe' }) @@ -233,6 +189,396 @@ describe('jest CommonJS', () => { testOutput += chunk.toString() }) }) + + it('should create test spans for sync, async, integration, parameterized and retried tests', async () => { + const eventsPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url === '/v0.4/traces', (payloads) => { + const spans = payloads.flatMap(({ payload }) => payload.flatMap(trace => trace)) + + const expectedTests = [ + { + name: 'jest-test-suite tracer and active span are available', + status: 'pass', + extraTags: { 'test.add.stuff': 'stuff' } + }, + { name: 'jest-test-suite done', status: 'pass' }, + { name: 'jest-test-suite done fail', status: 'fail' }, + { name: 'jest-test-suite done fail uncaught', status: 'fail' }, + { name: 'jest-test-suite can do integration http', status: 'pass' }, + { + name: 'jest-test-suite can do parameterized test', + status: 'pass', + parameters: { arguments: [1, 2, 3], metadata: {} } + }, + { + name: 'jest-test-suite can do parameterized test', + status: 'pass', + parameters: { arguments: [2, 3, 5], metadata: {} } + }, + { name: 'jest-test-suite promise passes', status: 'pass' }, + { name: 'jest-test-suite promise fails', status: 'fail' }, + { name: 'jest-test-suite timeout', status: 'fail', error: 'Exceeded timeout' }, + { name: 'jest-test-suite passes', status: 'pass' }, + { name: 'jest-test-suite fails', status: 'fail' }, + { name: 'jest-test-suite does not crash with missing stack', status: 'fail' }, + { name: 'jest-test-suite skips', status: 'skip' }, + { name: 'jest-test-suite skips todo', status: 'skip' }, + { name: 'jest-circus-test-retry can retry', status: 'fail' }, + { name: 'jest-circus-test-retry can retry', status: 'fail' }, + { name: 'jest-circus-test-retry can retry', status: 'pass' } + ] + + expectedTests.forEach(({ name, status, error, parameters, extraTags }) => { + const test = spans.find(test => + test.meta[TEST_NAME] === name && + test.meta[TEST_STATUS] === status && + test.meta[TEST_SUITE] === 'ci-visibility/jest-plugin-tests/jest-test.js' && + (!parameters || test.meta[TEST_PARAMETERS] === JSON.stringify(parameters)) + ) + + assert.exists(test, `Expected to find test "${name}" with status "${status}"`) + + assert.propertyVal(test.meta, 'language', 'javascript') + assert.propertyVal(test.meta, 'service', 'plugin-tests') + assert.propertyVal(test.meta, ORIGIN_KEY, CI_APP_ORIGIN) + assert.propertyVal(test.meta, TEST_FRAMEWORK, 'jest') + assert.propertyVal(test.meta, TEST_NAME, name) + assert.propertyVal(test.meta, TEST_STATUS, status) + assert.propertyVal(test.meta, TEST_SUITE, 'ci-visibility/jest-plugin-tests/jest-test.js') + assert.propertyVal(test.meta, TEST_SOURCE_FILE, 'ci-visibility/jest-plugin-tests/jest-test.js') + assert.propertyVal(test.meta, TEST_TYPE, 'test') + assert.propertyVal(test.meta, JEST_TEST_RUNNER, 'jest-circus') + assert.propertyVal(test.meta, LIBRARY_VERSION, ddTraceVersion) + assert.propertyVal(test.meta, COMPONENT, 'jest') + assert.include(test.meta[TEST_CODE_OWNERS], '@datadog-dd-trace-js') + + assert.equal(test.type, 'test') + assert.equal(test.name, 'jest.test') + assert.equal(test.service, 'plugin-tests') + assert.equal(test.resource, `ci-visibility/jest-plugin-tests/jest-test.js.${name}`) + + assert.exists(test.metrics[TEST_SOURCE_START]) + assert.exists(test.meta[TEST_FRAMEWORK_VERSION]) + + if (extraTags) { + Object.entries(extraTags).forEach(([key, value]) => { + assert.propertyVal(test.meta, key, value) + }) + } + + if (error) { + assert.include(test.meta[ERROR_MESSAGE], error) + } + + // TODO: why did this work in jsdom before? + if (name === 'jest-test-suite can do integration http') { + const httpSpan = spans.find(span => span.name === 'http.request') + assert.propertyVal(httpSpan.meta, ORIGIN_KEY, CI_APP_ORIGIN) + assert.include(httpSpan.meta['http.url'], '/info') + assert.equal(httpSpan.parent_id.toString(), test.span_id.toString()) + } + }) + }, 25000) + + childProcess = exec( + runTestsCommand, + { + cwd, + env: { + ...oldApmProtocolEnvVars, + TESTS_TO_RUN: 'jest-plugin-tests/jest-test.js', + DD_SERVICE: 'plugin-tests', + }, + stdio: 'inherit' + } + ) + + await Promise.all([ + once(childProcess, 'exit'), + eventsPromise + ]) + }) + + it('should detect an error in hooks', async () => { + const tests = [ + { name: 'jest-hook-failure will not run', error: 'hey, hook error before' }, + { name: 'jest-hook-failure-after will not run', error: 'hey, hook error after' } + ] + + const eventsPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url === '/v0.4/traces', (payloads) => { + const testSpans = payloads.flatMap(({ payload }) => payload.flatMap(trace => trace)) + + tests.forEach(({ name, error }) => { + const testSpan = testSpans.find(span => + span.resource === `ci-visibility/jest-plugin-tests/jest-hook-failure.js.${name}` + ) + + assert.exists(testSpan, `Expected to find test "${name}"`) + assert.propertyVal(testSpan.meta, 'language', 'javascript') + assert.propertyVal(testSpan.meta, ORIGIN_KEY, CI_APP_ORIGIN) + assert.propertyVal(testSpan.meta, TEST_FRAMEWORK, 'jest') + assert.propertyVal(testSpan.meta, TEST_NAME, name) + assert.propertyVal(testSpan.meta, TEST_STATUS, 'fail') + assert.propertyVal(testSpan.meta, TEST_SUITE, 'ci-visibility/jest-plugin-tests/jest-hook-failure.js') + assert.propertyVal(testSpan.meta, TEST_SOURCE_FILE, 'ci-visibility/jest-plugin-tests/jest-hook-failure.js') + assert.propertyVal(testSpan.meta, TEST_TYPE, 'test') + assert.propertyVal(testSpan.meta, JEST_TEST_RUNNER, 'jest-circus') + assert.propertyVal(testSpan.meta, COMPONENT, 'jest') + assert.equal(testSpan.meta[ERROR_MESSAGE], error) + assert.equal(testSpan.type, 'test') + assert.equal(testSpan.name, 'jest.test') + assert.equal(testSpan.resource, `ci-visibility/jest-plugin-tests/jest-hook-failure.js.${name}`) + assert.exists(testSpan.meta[TEST_FRAMEWORK_VERSION]) + }) + }, 25000) + + childProcess = exec( + runTestsCommand, + { + cwd, + env: { + ...oldApmProtocolEnvVars, + TESTS_TO_RUN: 'jest-plugin-tests/jest-hook-failure', + }, + stdio: 'inherit' + } + ) + + await Promise.all([ + once(childProcess, 'exit'), + eventsPromise + ]) + }) + + it('should work with focused tests', async () => { + const tests = [ + { name: 'jest-test-focused will be skipped', status: 'skip' }, + { name: 'jest-test-focused-2 will be skipped too', status: 'skip' }, + { name: 'jest-test-focused can do focused test', status: 'pass' } + ] + + const eventsPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url === '/v0.4/traces', (payloads) => { + const testSpans = payloads.flatMap(({ payload }) => payload.flatMap(trace => trace)) + + tests.forEach(({ name, status }) => { + const testSpan = testSpans.find(span => + span.resource === `ci-visibility/jest-plugin-tests/jest-focus.js.${name}` + ) + + assert.exists(testSpan, `Expected to find test "${name}"`) + assert.propertyVal(testSpan.meta, 'language', 'javascript') + assert.propertyVal(testSpan.meta, ORIGIN_KEY, CI_APP_ORIGIN) + assert.propertyVal(testSpan.meta, TEST_FRAMEWORK, 'jest') + assert.propertyVal(testSpan.meta, TEST_NAME, name) + assert.propertyVal(testSpan.meta, TEST_STATUS, status) + assert.propertyVal(testSpan.meta, TEST_SUITE, 'ci-visibility/jest-plugin-tests/jest-focus.js') + assert.propertyVal(testSpan.meta, TEST_SOURCE_FILE, 'ci-visibility/jest-plugin-tests/jest-focus.js') + assert.propertyVal(testSpan.meta, COMPONENT, 'jest') + assert.equal(testSpan.type, 'test') + assert.equal(testSpan.name, 'jest.test') + assert.equal(testSpan.resource, `ci-visibility/jest-plugin-tests/jest-focus.js.${name}`) + assert.exists(testSpan.meta[TEST_FRAMEWORK_VERSION]) + }) + }, 25000) + + childProcess = exec( + runTestsCommand, + { + cwd, + env: { + ...oldApmProtocolEnvVars, + TESTS_TO_RUN: 'jest-plugin-tests/jest-focus' + }, + stdio: 'inherit' + } + ) + + await Promise.all([ + once(childProcess, 'exit'), + eventsPromise + ]) + }) + + // injectGlobals was added in jest@26 + onlyLatestIt('does not crash when injectGlobals is false', async () => { + const eventsPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url === '/v0.4/traces', (payloads) => { + const testSpan = payloads + .flatMap(({ payload }) => payload.flatMap(trace => trace)) + .find(span => span.type === 'test') + assert.exists(testSpan, 'Expected to find test span') + assert.propertyVal(testSpan.meta, TEST_NAME, 'jest-inject-globals will be run') + assert.propertyVal(testSpan.meta, TEST_STATUS, 'pass') + assert.propertyVal(testSpan.meta, TEST_SUITE, 'ci-visibility/jest-plugin-tests/jest-inject-globals.js') + }) + + childProcess = exec( + runTestsCommand, + { + cwd, + env: { + ...oldApmProtocolEnvVars, + TESTS_TO_RUN: 'jest-plugin-tests/jest-inject-globals', + DO_NOT_INJECT_GLOBALS: true + }, + stdio: 'inherit' + } + ) + + await Promise.all([ + once(childProcess, 'exit'), + eventsPromise + ]) + }) + }) + + const nonLegacyReportingOptions = ['agentless', 'evp proxy'] + + nonLegacyReportingOptions.forEach((reportingOption) => { + context(`reporting via (${reportingOption})`, () => { + it('can run and report tests', (done) => { + const envVars = reportingOption === 'agentless' + ? getCiVisAgentlessConfig(receiver.port) + : getCiVisEvpProxyConfig(receiver.port) + if (reportingOption === 'evp proxy') { + receiver.setInfoResponse({ endpoints: ['/evp_proxy/v4'] }) + } + receiver.gatherPayloadsMaxTimeout(({ url }) => url.endsWith('citestcycle'), (payloads) => { + const metadataDicts = payloads.flatMap(({ payload }) => payload.metadata) + + metadataDicts.forEach(metadata => { + for (const testLevel of TEST_LEVEL_EVENT_TYPES) { + assert.equal(metadata[testLevel][TEST_SESSION_NAME], 'my-test-session') + } + }) + + const events = payloads.flatMap(({ payload }) => payload.events) + const sessionEventContent = events.find(event => event.type === 'test_session_end').content + const moduleEventContent = events.find(event => event.type === 'test_module_end').content + const suites = events.filter(event => event.type === 'test_suite_end').map(event => event.content) + const tests = events.filter(event => event.type === 'test').map(event => event.content) + + const resourceNames = tests.map(span => span.resource) + + assert.includeMembers(resourceNames, + [ + 'ci-visibility/test/ci-visibility-test.js.ci visibility can report tests', + 'ci-visibility/test/ci-visibility-test-2.js.ci visibility 2 can report tests 2' + ] + ) + assert.equal(suites.length, 2) + assert.exists(sessionEventContent) + assert.exists(moduleEventContent) + + assert.include(testOutput, expectedStdout) + + tests.forEach(testEvent => { + assert.equal(testEvent.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test'), true) + assert.exists(testEvent.metrics[TEST_SOURCE_START]) + assert.equal(testEvent.meta[DD_TEST_IS_USER_PROVIDED_SERVICE], 'false') + // Can read DD_TAGS + assert.propertyVal(testEvent.meta, 'test.customtag', 'customvalue') + assert.propertyVal(testEvent.meta, 'test.customtag2', 'customvalue2') + assert.exists(testEvent.metrics[DD_HOST_CPU_COUNT]) + }) + + suites.forEach(testSuite => { + assert.isTrue(testSuite.meta[TEST_SOURCE_FILE].startsWith('ci-visibility/test/ci-visibility-test')) + assert.equal(testSuite.metrics[TEST_SOURCE_START], 1) + assert.exists(testSuite.metrics[DD_HOST_CPU_COUNT]) + }) + + done() + }) + + childProcess = fork(startupTestFile, { + cwd, + env: { + ...envVars, + DD_TAGS: 'test.customtag:customvalue,test.customtag2:customvalue2', + DD_TEST_SESSION_NAME: 'my-test-session', + DD_SERVICE: undefined + }, + stdio: 'pipe' + }) + childProcess.stdout.on('data', (chunk) => { + testOutput += chunk.toString() + }) + childProcess.stderr.on('data', (chunk) => { + testOutput += chunk.toString() + }) + }) + + it('should create events for session, suite and test', async () => { + const envVars = reportingOption === 'agentless' + ? getCiVisAgentlessConfig(receiver.port) + : getCiVisEvpProxyConfig(receiver.port) + if (reportingOption === 'evp proxy') { + receiver.setInfoResponse({ endpoints: ['/evp_proxy/v4'] }) + } + + const eventsPromise = receiver + .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), (payloads) => { + const events = payloads.flatMap(({ payload }) => payload.events) + const testSessionEvent = events.find(event => event.type === 'test_session_end').content + const testModuleEvent = events.find(event => event.type === 'test_module_end').content + const testSuiteEvent = events.find(event => event.type === 'test_suite_end').content + const testEvent = events.find(event => event.type === 'test').content + + assert.exists(testSessionEvent) + assert.equal(testSessionEvent.meta[TEST_STATUS], 'pass') + assert.exists(testSessionEvent[TEST_SESSION_ID]) + assert.exists(testSessionEvent.meta[TEST_COMMAND]) + assert.notExists(testSessionEvent[TEST_SUITE_ID]) + assert.notExists(testSessionEvent[TEST_MODULE_ID]) + + assert.exists(testModuleEvent) + assert.equal(testModuleEvent.meta[TEST_STATUS], 'pass') + assert.exists(testModuleEvent[TEST_SESSION_ID]) + assert.exists(testModuleEvent[TEST_MODULE_ID]) + assert.exists(testModuleEvent.meta[TEST_COMMAND]) + assert.notExists(testModuleEvent[TEST_SUITE_ID]) + + assert.exists(testSuiteEvent) + assert.equal(testSuiteEvent.meta[TEST_STATUS], 'pass') + assert.equal(testSuiteEvent.meta[TEST_SUITE], 'ci-visibility/jest-plugin-tests/jest-test-suite.js') + assert.exists(testSuiteEvent.meta[TEST_COMMAND]) + assert.exists(testSuiteEvent.meta[TEST_MODULE]) + assert.exists(testSuiteEvent[TEST_SUITE_ID]) + assert.exists(testSuiteEvent[TEST_SESSION_ID]) + assert.exists(testSuiteEvent[TEST_MODULE_ID]) + + assert.exists(testEvent) + assert.equal(testEvent.meta[TEST_STATUS], 'pass') + assert.equal(testEvent.meta[TEST_NAME], 'jest-test-suite-visibility works') + assert.equal(testEvent.meta[TEST_SUITE], 'ci-visibility/jest-plugin-tests/jest-test-suite.js') + assert.exists(testEvent.meta[TEST_COMMAND]) + assert.exists(testEvent.meta[TEST_MODULE]) + assert.exists(testEvent[TEST_SUITE_ID]) + assert.exists(testEvent[TEST_SESSION_ID]) + assert.exists(testEvent[TEST_MODULE_ID]) + }) + + childProcess = exec( + runTestsCommand, + { + cwd, + env: { + ...envVars, + TESTS_TO_RUN: 'jest-plugin-tests/jest-test-suite' + }, + stdio: 'inherit' + } + ) + + await Promise.all([ + once(childProcess, 'exit'), + eventsPromise + ]) + }) + }) }) const envVarSettings = ['DD_TRACING_ENABLED', 'DD_TRACE_ENABLED'] @@ -312,10 +658,11 @@ describe('jest CommonJS', () => { env: { ...getCiVisAgentlessConfig(receiver.port), PROJECTS: JSON.stringify([{ - testMatch: ['**/subproject-test*'] + testMatch: ['**/subproject-test*'], + testRunner: 'jest-circus/runner', }]) }, - stdio: 'inherit' + stdio: 'pipe' } ) @@ -326,7 +673,8 @@ describe('jest CommonJS', () => { }) }) - it('works when sharding', (done) => { + // --shard was added in jest@28 + onlyLatestIt('works when sharding', (done) => { receiver.payloadReceived(({ url }) => url === '/api/v2/citestcycle').then(events => { const testSuiteEvents = events.payload.events.filter(event => event.type === 'test_suite_end') assert.equal(testSuiteEvents.length, 3) @@ -527,7 +875,8 @@ describe('jest CommonJS', () => { }).catch(done) }) - it('can work with Failed Test Replay', (done) => { + // older versions handle retries differently + onlyLatestIt('can work with Failed Test Replay', (done) => { receiver.setSettings({ flaky_test_retries_enabled: true, di_enabled: true @@ -619,7 +968,8 @@ describe('jest CommonJS', () => { testOutput += chunk.toString() }) childProcess.on('message', () => { - assert.include(testOutput, 'Exceeded timeout of 100 ms for a test') + // it's "100ms" or "100 ms" depending on the jest version + assert.match(testOutput, /Exceeded timeout of 100\s?ms for a test/) done() }) }) @@ -658,7 +1008,7 @@ describe('jest CommonJS', () => { }) context('when using off timing imports', () => { - it('reports test suite errors when using off timing import', async () => { + onlyLatestIt('reports test suite errors when using off timing import', async () => { const eventsPromise = receiver .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), (payloads) => { const events = payloads.flatMap(({ payload }) => payload.events) @@ -698,7 +1048,7 @@ describe('jest CommonJS', () => { ]) }) - it('reports test suite errors when importing after environment is torn down', async () => { + onlyLatestIt('reports test suite errors when importing after environment is torn down', async () => { const eventsPromise = receiver .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), (payloads) => { const events = payloads.flatMap(({ payload }) => payload.events) @@ -1646,7 +1996,9 @@ describe('jest CommonJS', () => { env: { ...getCiVisAgentlessConfig(receiver.port), PROJECTS: JSON.stringify([{ - testMatch: ['**/subproject-test*'] + testMatch: ['**/subproject-test*'], + testEnvironment: 'node', + testRunner: 'jest-circus/runner', }]) }, stdio: 'inherit' @@ -2203,7 +2555,8 @@ describe('jest CommonJS', () => { }) }) - it('works with snapshot tests', async () => { + // resetting snapshot state logic only works in latest versions + onlyLatestIt('works with snapshot tests', async () => { receiver.setInfoResponse({ endpoints: ['/evp_proxy/v4'] }) receiver.setKnownTests({ @@ -2271,7 +2624,8 @@ describe('jest CommonJS', () => { assert.equal(exitCode, 0) }) - it('works with jest-image-snapshot', async () => { + // resetting snapshot state logic only works in latest versions + onlyLatestIt('works with jest-image-snapshot', async () => { receiver.setInfoResponse({ endpoints: ['/evp_proxy/v4'] }) receiver.setKnownTests({ @@ -2452,7 +2806,7 @@ describe('jest CommonJS', () => { }) }) // happy-dom>=19 can only be used with CJS from node 20 and above - const happyDomTest = NODE_MAJOR < 20 ? it.skip : it + const happyDomTest = NODE_MAJOR < 20 ? it.skip : onlyLatestIt happyDomTest('works with happy-dom', async () => { // Tests from ci-visibility/test/ci-visibility-test-2.js will be considered new receiver.setKnownTests({ @@ -2585,7 +2939,8 @@ describe('jest CommonJS', () => { }) }) - it('does not retry when it.failing is used', (done) => { + // it.failing was added in jest@29 + onlyLatestIt('does not retry when it.failing is used', (done) => { receiver.setInfoResponse({ endpoints: ['/evp_proxy/v4'] }) const NUM_RETRIES_EFD = 3 receiver.setSettings({ @@ -2781,7 +3136,7 @@ describe('jest CommonJS', () => { ]) }) - it('works with snapshot tests', async () => { + onlyLatestIt('works with snapshot tests', async () => { receiver.setInfoResponse({ endpoints: ['/evp_proxy/v4'] }) receiver.setKnownTests({ @@ -3047,7 +3402,7 @@ describe('jest CommonJS', () => { }) context('dynamic instrumentation', () => { - it('does not activate dynamic instrumentation if DD_TEST_FAILED_TEST_REPLAY_ENABLED is set to false', (done) => { + onlyLatestIt('does not activate DI if DD_TEST_FAILED_TEST_REPLAY_ENABLED is set to false', (done) => { receiver.setSettings({ flaky_test_retries_enabled: true, di_enabled: true @@ -3096,7 +3451,7 @@ describe('jest CommonJS', () => { }) }) - it('does not activate dynamic instrumentation if remote settings are disabled', (done) => { + onlyLatestIt('does not activate DI if remote settings are disabled', (done) => { receiver.setSettings({ flaky_test_retries_enabled: true, di_enabled: false @@ -3143,7 +3498,7 @@ describe('jest CommonJS', () => { }) }) - it('runs retries with dynamic instrumentation', (done) => { + onlyLatestIt('runs retries with DI', (done) => { receiver.setSettings({ flaky_test_retries_enabled: true, di_enabled: true @@ -3229,7 +3584,7 @@ describe('jest CommonJS', () => { }) }) - it('runs retries with dynamic instrumentation in parallel mode', (done) => { + onlyLatestIt('runs retries with DI in parallel mode', (done) => { receiver.setSettings({ flaky_test_retries_enabled: true, di_enabled: true @@ -3318,7 +3673,7 @@ describe('jest CommonJS', () => { }) }) - it('does not crash if the retry does not hit the breakpoint', (done) => { + onlyLatestIt('does not crash if the retry does not hit the breakpoint', (done) => { receiver.setSettings({ flaky_test_retries_enabled: true, di_enabled: true @@ -3365,7 +3720,7 @@ describe('jest CommonJS', () => { }) }) - it('does not wait for breakpoint for a passed test', (done) => { + onlyLatestIt('does not wait for breakpoint for a passed test', (done) => { receiver.setSettings({ flaky_test_retries_enabled: true, di_enabled: true @@ -3772,7 +4127,7 @@ describe('jest CommonJS', () => { runAttemptToFixTest(done, { isAttemptToFix: true, isDisabled: true }) }) - it('works with snapshot tests', async () => { + onlyLatestIt('works with snapshot tests', async () => { receiver.setSettings({ test_management: { enabled: true, attempt_to_fix_retries: 3 } }) receiver.setTestManagementTests({ @@ -3833,7 +4188,7 @@ describe('jest CommonJS', () => { ]) }) - it('works with snapshot tests when every attempt passes', async () => { + onlyLatestIt('works with snapshot tests when every attempt passes', async () => { receiver.setSettings({ test_management: { enabled: true, attempt_to_fix_retries: 3 } }) receiver.setTestManagementTests({ @@ -3887,7 +4242,7 @@ describe('jest CommonJS', () => { assert.equal(exitCode, 0) }) - it('works with image snapshot tests', async () => { + onlyLatestIt('works with image snapshot tests', async () => { receiver.setSettings({ test_management: { enabled: true, attempt_to_fix_retries: 3 } }) receiver.setTestManagementTests({ @@ -3966,7 +4321,7 @@ describe('jest CommonJS', () => { ) }) - it('works with snapshot tests', async () => { + onlyLatestIt('works with snapshot tests', async () => { receiver.setSettings({ test_management: { enabled: true, attempt_to_fix_retries: 3 } }) receiver.setTestManagementTests({ @@ -4643,7 +4998,7 @@ describe('jest CommonJS', () => { }) context('fast-check', () => { - it('should remove seed from the test name if @fast-check/jest is used in the test', async () => { + onlyLatestIt('should remove seed from the test name if @fast-check/jest is used in the test', async () => { const eventsPromise = receiver .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), payloads => { const events = payloads.flatMap(({ payload }) => payload.events) @@ -4669,7 +5024,7 @@ describe('jest CommonJS', () => { ]) }) - it('should not remove seed if @fast-check/jest is not used', async () => { + onlyLatestIt('should not remove seed if @fast-check/jest is not used', async () => { const eventsPromise = receiver .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), payloads => { const events = payloads.flatMap(({ payload }) => payload.events) diff --git a/integration-tests/mocha/mocha.spec.js b/integration-tests/mocha/mocha.spec.js index 8cb77344da8..a2521303c51 100644 --- a/integration-tests/mocha/mocha.spec.js +++ b/integration-tests/mocha/mocha.spec.js @@ -3081,7 +3081,7 @@ describe(`mocha@${MOCHA_VERSION}`, function () { }) it('takes into account untested files if "all" is passed to nyc', (done) => { - const linePctMatchRegex = /Lines\s*:\s*(\d+)%/ + const linePctMatchRegex = /Lines\s*:\s*(\d+(?:\.\d+)?)%/ let linePctMatch let linesPctFromNyc = 0 let codeCoverageWithUntestedFiles = 0 diff --git a/integration-tests/openfeature/openfeature-exposure-events.spec.js b/integration-tests/openfeature/openfeature-exposure-events.spec.js index 3aa313cb6cb..97fa2596ad5 100644 --- a/integration-tests/openfeature/openfeature-exposure-events.spec.js +++ b/integration-tests/openfeature/openfeature-exposure-events.spec.js @@ -75,7 +75,7 @@ describe('OpenFeature Remote Config and Exposure Events Integration', () => { agent.on('exposures', ({ payload, headers }) => { assert.property(payload, 'context') assert.property(payload, 'exposures') - assert.equal(payload.context.service_name, 'ffe-test-service') + assert.equal(payload.context.service, 'ffe-test-service') assert.equal(payload.context.version, '1.2.3') assert.equal(payload.context.env, 'test') @@ -159,7 +159,7 @@ describe('OpenFeature Remote Config and Exposure Events Integration', () => { agent.on('exposures', ({ payload }) => { assert.property(payload, 'context') assert.property(payload, 'exposures') - assert.equal(payload.context.service_name, 'ffe-test-service') + assert.equal(payload.context.service, 'ffe-test-service') assert.equal(payload.context.version, '1.2.3') assert.equal(payload.context.env, 'test') diff --git a/package.json b/package.json index 152e1456f1f..c4de889936f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dd-trace", - "version": "5.76.0", + "version": "5.76.1", "description": "Datadog APM tracing client for JavaScript", "main": "index.js", "typings": "index.d.ts", @@ -125,15 +125,15 @@ "@datadog/native-appsec": "10.3.0", "@datadog/native-iast-taint-tracking": "4.0.0", "@datadog/native-metrics": "3.1.1", - "@datadog/openfeature-node-server": "0.1.0-preview.13", + "@datadog/openfeature-node-server": "0.1.0-preview.15", "@datadog/pprof": "5.12.0", "@datadog/sketches-js": "2.1.1", "@datadog/wasm-js-rewriter": "4.0.1", - "@isaacs/ttlcache": "^1.4.1", + "@isaacs/ttlcache": "^2.0.1", "@opentelemetry/api": ">=1.0.0 <1.10.0", "@opentelemetry/api-logs": "<1.0.0", "@opentelemetry/core": ">=1.14.0 <1.31.0", - "@opentelemetry/resources": ">=1.0.0 <1.10.0", + "@opentelemetry/resources": ">=1.0.0 <1.31.0", "crypto-randomuuid": "^1.0.0", "dc-polyfill": "^0.1.10", "escape-string-regexp": "^5.0.0", @@ -160,13 +160,13 @@ "ttl-set": "^1.0.0" }, "devDependencies": { - "@babel/helpers": "^7.27.6", + "@babel/helpers": "^7.28.4", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.29.0", + "@eslint/js": "^9.39.0", "@msgpack/msgpack": "^3.1.2", "@openfeature/core": "^1.9.0", "@openfeature/server-sdk": "^1.20.0", - "@stylistic/eslint-plugin": "^5.0.0", + "@stylistic/eslint-plugin": "^5.5.0", "@types/chai": "^4.3.16", "@types/mocha": "^10.0.10", "@types/node": "^18.19.106", @@ -175,15 +175,15 @@ "axios": "^1.12.2", "benchmark": "^2.1.4", "body-parser": "^2.2.0", - "bun": "1.3.1", + "bun": "1.3.2", "chai": "^4.5.0", - "eslint": "^9.29.0", - "eslint-plugin-cypress": "^5.1.0", + "eslint": "^9.39.0", + "eslint-plugin-cypress": "^5.2.0", "eslint-plugin-import": "^2.32.0", - "eslint-plugin-mocha": "^11.1.0", - "eslint-plugin-n": "^17.20.0", + "eslint-plugin-mocha": "^11.2.0", + "eslint-plugin-n": "^17.23.1", "eslint-plugin-promise": "^7.2.1", - "eslint-plugin-unicorn": "^61.0.2", + "eslint-plugin-unicorn": "^62.0.0", "express": "^5.1.0", "glob": "^10.4.5", "globals": "^16.3.0", diff --git a/packages/datadog-instrumentations/src/jest.js b/packages/datadog-instrumentations/src/jest.js index 8bb12e46ab1..45d32a32615 100644 --- a/packages/datadog-instrumentations/src/jest.js +++ b/packages/datadog-instrumentations/src/jest.js @@ -140,7 +140,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) { this.hasSnapshotTests = undefined this.testSuiteAbsolutePath = context.testPath - this.displayName = config.projectConfig?.displayName?.name + this.displayName = config.projectConfig?.displayName?.name || config.displayName this.testEnvironmentOptions = getTestEnvironmentOptions(config) const repositoryRoot = this.testEnvironmentOptions._ddRepositoryRoot @@ -411,23 +411,19 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) { } else { originalHookFns.set(hook, hookFn) } - // The rule has a bug, see https://github.com/sindresorhus/eslint-plugin-unicorn/issues/2164 - // eslint-disable-next-line unicorn/consistent-function-scoping - const wrapperHook = function () { + const newHookFn = shimmer.wrapFunction(hookFn, hookFn => function () { return testFnCh.runStores(ctx, () => hookFn.apply(this, arguments)) - } - // If we don't do this, the timeout will not be triggered - Object.defineProperty(wrapperHook, 'length', { value: hookFn.length }) - hook.fn = wrapperHook + }) + hook.fn = newHookFn } const originalFn = event.test.fn originalTestFns.set(event.test, originalFn) - const wrapper = function () { - return testFnCh.runStores(ctx, () => originalFn.apply(this, arguments)) - } - // If we don't do this, the timeout will be not be triggered - Object.defineProperty(wrapper, 'length', { value: originalFn.length }) - event.test.fn = wrapper + + const newFn = shimmer.wrapFunction(event.test.fn, testFn => function () { + return testFnCh.runStores(ctx, () => testFn.apply(this, arguments)) + }) + + event.test.fn = newFn }) } @@ -1293,7 +1289,7 @@ addHook({ shimmer.wrap(Runtime.prototype, '_createJestObjectFor', _createJestObjectFor => function (from) { const result = _createJestObjectFor.apply(this, arguments) - const suiteFilePath = this._testPath + const suiteFilePath = this._testPath || from shimmer.wrap(result, 'mock', mock => function (moduleName) { // If the library is mocked with `jest.mock`, we don't want to bypass jest's own require engine @@ -1450,7 +1446,11 @@ addHook({ }, (childProcessWorker) => { const ChildProcessWorker = childProcessWorker.default shimmer.wrap(ChildProcessWorker.prototype, 'send', sendWrapper) - shimmer.wrap(ChildProcessWorker.prototype, '_onMessage', onMessageWrapper) + if (ChildProcessWorker.prototype._onMessage) { + shimmer.wrap(ChildProcessWorker.prototype, '_onMessage', onMessageWrapper) + } else if (ChildProcessWorker.prototype.onMessage) { + shimmer.wrap(ChildProcessWorker.prototype, 'onMessage', onMessageWrapper) + } return childProcessWorker }) diff --git a/packages/datadog-plugin-azure-event-hubs/test/integration-test/client.spec.js b/packages/datadog-plugin-azure-event-hubs/test/integration-test/client.spec.js index a2c9dfcc14c..37bee92dce1 100644 --- a/packages/datadog-plugin-azure-event-hubs/test/integration-test/client.spec.js +++ b/packages/datadog-plugin-azure-event-hubs/test/integration-test/client.spec.js @@ -1,5 +1,8 @@ 'use strict' +const { assert, expect } = require('chai') +const { describe, it, beforeEach, afterEach } = require('mocha') + const { FakeAgent, sandboxCwd, @@ -8,11 +11,11 @@ const { spawnPluginIntegrationTestProc } = require('../../../../integration-tests/helpers') const { withVersions } = require('../../../dd-trace/test/setup/mocha') -const { assert, expect } = require('chai') const spawnEnv = { DD_TRACE_FLUSH_INTERVAL: '2000' } -describe('esm', () => { +// TODO: Fix this test / esm issue +describe.skip('esm', () => { let agent let proc diff --git a/packages/datadog-plugin-cucumber/src/index.js b/packages/datadog-plugin-cucumber/src/index.js index 669b255a3fc..1168c358008 100644 --- a/packages/datadog-plugin-cucumber/src/index.js +++ b/packages/datadog-plugin-cucumber/src/index.js @@ -51,7 +51,8 @@ const { } = require('../../dd-trace/src/ci-visibility/telemetry') const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200 -const BREAKPOINT_SET_GRACE_PERIOD_MS = 200 +const BREAKPOINT_SET_GRACE_PERIOD_MS = 400 + const isCucumberWorker = !!getEnvironmentVariable('CUCUMBER_WORKER_ID') class CucumberPlugin extends CiPlugin { diff --git a/packages/datadog-plugin-cypress/test/app-10/app-server.js b/packages/datadog-plugin-cypress/test/app-10/app-server.js deleted file mode 100644 index 37edf68e055..00000000000 --- a/packages/datadog-plugin-cypress/test/app-10/app-server.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict' - -// File to spin an HTTP server that returns an HTML for cypress to visit -const http = require('http') - -module.exports = http.createServer((req, res) => { - res.setHeader('Content-Type', 'text/html') - res.writeHead(200) - res.end(` - - - - - - -
Hello World
- - - `) -}) diff --git a/packages/datadog-plugin-cypress/test/app-10/cypress.config.js b/packages/datadog-plugin-cypress/test/app-10/cypress.config.js deleted file mode 100644 index fdd1c0b5a49..00000000000 --- a/packages/datadog-plugin-cypress/test/app-10/cypress.config.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - -const setupNodeEvents = require('./cypress/plugins/index.js') - -module.exports = { - video: false, - screenshotOnRunFailure: false, - e2e: { - setupNodeEvents, - supportFile: 'cypress/support/index.js', - specPattern: 'cypress/integration/**.js' - } -} diff --git a/packages/datadog-plugin-cypress/test/app-10/cypress/integration/integration-test.js b/packages/datadog-plugin-cypress/test/app-10/cypress/integration/integration-test.js deleted file mode 100644 index 8ae222d3f4a..00000000000 --- a/packages/datadog-plugin-cypress/test/app-10/cypress/integration/integration-test.js +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable */ -context('can visit a page', () => { - beforeEach(() => { - cy.visit('/') - cy.task('dd:addTags', { addTagsBeforeEach: 'custom' }) - }) - afterEach(() => { - cy.task('dd:addTags', { addTagsAfterEach: 'custom' }) - }) - it('renders a hello world', () => { - cy.task('dd:addTags', { addTags: 'custom' }) - cy.get('.hello-world') - .should('have.text', 'Hello World') - }) - it('will fail', () => { - cy.task('dd:addTags', { addTags: 'custom' }) - cy.get('.hello-world') - .should('have.text', 'Bye World') - cy.task('dd:addTags', { addTagsAfterFailure: 'custom' }) - }) -}) diff --git a/packages/datadog-plugin-cypress/test/app-10/cypress/plugins/index.js b/packages/datadog-plugin-cypress/test/app-10/cypress/plugins/index.js deleted file mode 100644 index 209aa20cb8b..00000000000 --- a/packages/datadog-plugin-cypress/test/app-10/cypress/plugins/index.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -module.exports = (on, config) => { - // We can't use the tracer available in the testing process, because this code is - // run in a different process. We need to init a different tracer reporting to the - // url set by the plugin agent - require('../../../../../dd-trace').init({ startupLogs: false }) - require('../../../../src/plugin')(on, config) -} diff --git a/packages/datadog-plugin-cypress/test/app-10/cypress/support/index.js b/packages/datadog-plugin-cypress/test/app-10/cypress/support/index.js deleted file mode 100644 index 3371ce88469..00000000000 --- a/packages/datadog-plugin-cypress/test/app-10/cypress/support/index.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict' - -require('../../../../src/support') diff --git a/packages/datadog-plugin-cypress/test/app-10/tsconfig.json b/packages/datadog-plugin-cypress/test/app-10/tsconfig.json deleted file mode 100644 index 0967ef424bc..00000000000 --- a/packages/datadog-plugin-cypress/test/app-10/tsconfig.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/datadog-plugin-cypress/test/app/app-server.js b/packages/datadog-plugin-cypress/test/app/app-server.js deleted file mode 100644 index 37edf68e055..00000000000 --- a/packages/datadog-plugin-cypress/test/app/app-server.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict' - -// File to spin an HTTP server that returns an HTML for cypress to visit -const http = require('http') - -module.exports = http.createServer((req, res) => { - res.setHeader('Content-Type', 'text/html') - res.writeHead(200) - res.end(` - - - - - - -
Hello World
- - - `) -}) diff --git a/packages/datadog-plugin-cypress/test/app/cypress.json b/packages/datadog-plugin-cypress/test/app/cypress.json deleted file mode 100644 index e5bcf207a11..00000000000 --- a/packages/datadog-plugin-cypress/test/app/cypress.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "video": false, - "screenshotOnRunFailure": false, - "pluginsFile": "cypress/plugins/index.js", - "supportFile": "cypress/support/index.js" -} diff --git a/packages/datadog-plugin-cypress/test/app/cypress/integration/integration-test.js b/packages/datadog-plugin-cypress/test/app/cypress/integration/integration-test.js deleted file mode 100644 index 8ae222d3f4a..00000000000 --- a/packages/datadog-plugin-cypress/test/app/cypress/integration/integration-test.js +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable */ -context('can visit a page', () => { - beforeEach(() => { - cy.visit('/') - cy.task('dd:addTags', { addTagsBeforeEach: 'custom' }) - }) - afterEach(() => { - cy.task('dd:addTags', { addTagsAfterEach: 'custom' }) - }) - it('renders a hello world', () => { - cy.task('dd:addTags', { addTags: 'custom' }) - cy.get('.hello-world') - .should('have.text', 'Hello World') - }) - it('will fail', () => { - cy.task('dd:addTags', { addTags: 'custom' }) - cy.get('.hello-world') - .should('have.text', 'Bye World') - cy.task('dd:addTags', { addTagsAfterFailure: 'custom' }) - }) -}) diff --git a/packages/datadog-plugin-cypress/test/app/cypress/plugins/index.js b/packages/datadog-plugin-cypress/test/app/cypress/plugins/index.js deleted file mode 100644 index ddeb86b685d..00000000000 --- a/packages/datadog-plugin-cypress/test/app/cypress/plugins/index.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict' - -module.exports = require('../../../../../../ci/cypress/plugin') diff --git a/packages/datadog-plugin-cypress/test/app/cypress/support/index.js b/packages/datadog-plugin-cypress/test/app/cypress/support/index.js deleted file mode 100644 index 3371ce88469..00000000000 --- a/packages/datadog-plugin-cypress/test/app/cypress/support/index.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict' - -require('../../../../src/support') diff --git a/packages/datadog-plugin-cypress/test/app/tsconfig.json b/packages/datadog-plugin-cypress/test/app/tsconfig.json deleted file mode 100644 index 0967ef424bc..00000000000 --- a/packages/datadog-plugin-cypress/test/app/tsconfig.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/datadog-plugin-cypress/test/index.spec.js b/packages/datadog-plugin-cypress/test/index.spec.js deleted file mode 100644 index c9f99c78b62..00000000000 --- a/packages/datadog-plugin-cypress/test/index.spec.js +++ /dev/null @@ -1,137 +0,0 @@ -'use strict' -const { expect } = require('chai') -const semver = require('semver') - -const agent = require('../../dd-trace/test/plugins/agent') -const appServer = require('./app/app-server') -const { ORIGIN_KEY, COMPONENT, ERROR_MESSAGE, ERROR_TYPE } = require('../../dd-trace/src/constants') -const { - TEST_FRAMEWORK, - TEST_TYPE, - TEST_NAME, - TEST_SUITE, - TEST_SOURCE_FILE, - TEST_SOURCE_START, - TEST_STATUS, - TEST_IS_RUM_ACTIVE, - CI_APP_ORIGIN, - TEST_FRAMEWORK_VERSION, - TEST_CODE_OWNERS, - LIBRARY_VERSION -} = require('../../dd-trace/src/plugins/util/test') -const { withVersions } = require('../../dd-trace/test/setup/mocha') - -const { version: ddTraceVersion } = require('../../../package.json') - -const testTimeout = 60000 - -describe('Plugin', function () { - let cypressExecutable - let appPort - let agentListenPort - this.retries(2) - withVersions('cypress', 'cypress', (version, moduleName) => { - beforeEach(() => { - return agent.load() - }) - - beforeEach(function (done) { - // This test does not check test optimization agentless protocol, - // so we'll make the tracer default to reporting to v0.4/traces - agent.setAvailableEndpoints([]) - this.timeout(10000) - agentListenPort = agent.server.address().port - cypressExecutable = require(`../../../versions/cypress@${version}`).get() - appServer.listen(0, () => { - appPort = appServer.address().port - done() - }) - }) - - afterEach(() => agent.close({ ritmReset: false })) - - afterEach(done => { - appServer.close(done) - }) - - describe('cypress', function () { - this.timeout(testTimeout) - - it('instruments tests', function (done) { - process.env.DD_TRACE_AGENT_PORT = agentListenPort - const testSuiteFolder = semver.intersects(version, '>=10') - ? 'app-10' - : 'app' - - cypressExecutable.run({ - project: `./packages/datadog-plugin-cypress/test/${testSuiteFolder}`, - config: { - baseUrl: `http://localhost:${appPort}` - }, - quiet: true, - headless: true - }) - - agent.assertSomeTraces(traces => { - const passedTestSpan = traces[0][0] - const failedTestSpan = traces[1][0] - expect(passedTestSpan.name).to.equal('cypress.test') - expect(passedTestSpan.resource).to.equal( - 'cypress/integration/integration-test.js.can visit a page renders a hello world' - ) - expect(passedTestSpan.type).to.equal('test') - expect(passedTestSpan.meta).to.contain({ - language: 'javascript', - addTags: 'custom', - addTagsBeforeEach: 'custom', - addTagsAfterEach: 'custom', - [TEST_FRAMEWORK]: 'cypress', - [TEST_NAME]: 'can visit a page renders a hello world', - [TEST_STATUS]: 'pass', - [TEST_SUITE]: 'cypress/integration/integration-test.js', - [TEST_SOURCE_FILE]: - `packages/datadog-plugin-cypress/test/${testSuiteFolder}/cypress/integration/integration-test.js`, - [TEST_TYPE]: 'browser', - [ORIGIN_KEY]: CI_APP_ORIGIN, - [TEST_IS_RUM_ACTIVE]: 'true', - [LIBRARY_VERSION]: ddTraceVersion, - [COMPONENT]: 'cypress' - }) - expect(passedTestSpan.meta[TEST_CODE_OWNERS]).to.contain('@DataDog') - expect(passedTestSpan.meta[TEST_FRAMEWORK_VERSION]).not.to.be.undefined - expect(passedTestSpan.metrics[TEST_SOURCE_START]).to.exist - - expect(failedTestSpan.name).to.equal('cypress.test') - expect(failedTestSpan.resource).to.equal( - 'cypress/integration/integration-test.js.can visit a page will fail' - ) - expect(failedTestSpan.type).to.equal('test') - expect(failedTestSpan.meta).to.contain({ - language: 'javascript', - addTags: 'custom', - addTagsBeforeEach: 'custom', - addTagsAfterEach: 'custom', - [TEST_FRAMEWORK]: 'cypress', - [TEST_NAME]: 'can visit a page will fail', - [TEST_STATUS]: 'fail', - [TEST_SUITE]: 'cypress/integration/integration-test.js', - [TEST_SOURCE_FILE]: - `packages/datadog-plugin-cypress/test/${testSuiteFolder}/cypress/integration/integration-test.js`, - [TEST_TYPE]: 'browser', - [ORIGIN_KEY]: CI_APP_ORIGIN, - [ERROR_TYPE]: 'AssertionError', - [TEST_IS_RUM_ACTIVE]: 'true', - [COMPONENT]: 'cypress' - }) - expect(failedTestSpan.meta).to.not.contain({ - addTagsAfterFailure: 'custom' - }) - expect(failedTestSpan.meta[ERROR_MESSAGE]).to.contain( - "expected '' to have text 'Bye World', but the text was 'Hello World'" - ) - expect(failedTestSpan.metrics[TEST_SOURCE_START]).to.exist - }, { timeoutMs: testTimeout }).then(() => done()).catch(done) - }) - }) - }) -}) diff --git a/packages/datadog-plugin-grpc/src/util.js b/packages/datadog-plugin-grpc/src/util.js index f3fd7873c82..101d1691bb7 100644 --- a/packages/datadog-plugin-grpc/src/util.js +++ b/packages/datadog-plugin-grpc/src/util.js @@ -3,6 +3,10 @@ const pick = require('../../datadog-core/src/utils/src/pick') const log = require('../../dd-trace/src/log') +function getEmptyObject () { + return {} +} + module.exports = { getMethodMetadata (path, kind) { const tags = { @@ -57,6 +61,6 @@ module.exports = { log.error('Expected \'%s\' to be an array or function.', filter) } - return () => ({}) + return getEmptyObject } } diff --git a/packages/datadog-plugin-http/src/client.js b/packages/datadog-plugin-http/src/client.js index 40f68adb2b4..12e7b5020da 100644 --- a/packages/datadog-plugin-http/src/client.js +++ b/packages/datadog-plugin-http/src/client.js @@ -183,13 +183,17 @@ function normalizeClientConfig (config) { } } +function is400ErrorCode (code) { + return code < 400 || code >= 500 +} + function getStatusValidator (config) { if (typeof config.validateStatus === 'function') { return config.validateStatus } else if (config.hasOwnProperty('validateStatus')) { log.error('Expected `validateStatus` to be a function.') } - return code => code < 400 || code >= 500 + return is400ErrorCode } function getFilter (config) { diff --git a/packages/datadog-plugin-http2/src/client.js b/packages/datadog-plugin-http2/src/client.js index 31ecc60c9b3..cbc8bf9e1c7 100644 --- a/packages/datadog-plugin-http2/src/client.js +++ b/packages/datadog-plugin-http2/src/client.js @@ -162,13 +162,17 @@ function hasAmazonSignature (headers, path) { return false } +function is400ErrorCode (code) { + return code < 400 || code >= 500 +} + function getStatusValidator (config) { if (typeof config.validateStatus === 'function') { return config.validateStatus } else if (config.hasOwnProperty('validateStatus')) { log.error('Expected `validateStatus` to be a function.') } - return code => code < 400 || code >= 500 + return is400ErrorCode } function normalizeConfig (config) { diff --git a/packages/datadog-plugin-jest/test/circus.spec.js b/packages/datadog-plugin-jest/test/circus.spec.js deleted file mode 100644 index ef2a2a542dd..00000000000 --- a/packages/datadog-plugin-jest/test/circus.spec.js +++ /dev/null @@ -1,418 +0,0 @@ -'use strict' - -const { expect } = require('chai') -const { describe, it, beforeEach, afterEach } = require('mocha') -const nock = require('nock') -const semver = require('semver') - -const fs = require('node:fs') -const path = require('node:path') - -const { ORIGIN_KEY, COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants') -const agent = require('../../dd-trace/test/plugins/agent') -const { withVersions } = require('../../dd-trace/test/setup/mocha') -const { - TEST_FRAMEWORK, - TEST_TYPE, - TEST_NAME, - TEST_SUITE, - TEST_SOURCE_FILE, - TEST_SOURCE_START, - TEST_FRAMEWORK_VERSION, - TEST_STATUS, - CI_APP_ORIGIN, - JEST_TEST_RUNNER, - TEST_PARAMETERS, - TEST_CODE_OWNERS, - LIBRARY_VERSION, - TEST_COMMAND, - TEST_TOOLCHAIN, - TEST_SUITE_ID, - TEST_SESSION_ID, - TEST_MODULE_ID, - TEST_MODULE -} = require('../../dd-trace/src/plugins/util/test') - -const { version: ddTraceVersion } = require('../../../package.json') - -/** - * The assertion timeout needs to be less than the test timeout, - * otherwise failing tests will always fail due to a timeout, - * which is less useful than having an assertion error message. - */ -const assertionTimeout = 15000 -const testTimeout = 20000 - -function loadAgent (moduleName, version, isAgentlessTest, isEvpProxyTest) { - const exporter = isAgentlessTest ? 'datadog' : 'agent_proxy' - if (!isEvpProxyTest) { - agent.setAvailableEndpoints([]) - } - return agent.load( - ['jest', 'http'], - { service: 'test' }, - { isCiVisibility: true, experimental: { exporter } }) - .then(() => { - global.__libraryName__ = moduleName - global.__libraryVersion__ = version - - return { - jestExecutable: require(`../../../versions/jest@${version}`).get(), - jestCommonOptions: { - projects: [__dirname], - testPathIgnorePatterns: ['/node_modules/'], - coverageReporters: ['none'], - reporters: [], - silent: true, - testEnvironment: path.join(__dirname, 'env.js'), - testRunner: require(`../../../versions/jest-circus@${version}`).getPath('jest-circus/runner'), - cache: false, - maxWorkers: '50%' - } - } - }) -} - -describe('Plugin', function () { - let jestExecutable - let jestCommonOptions - - this.timeout(testTimeout) - this.retries(2) - - const versions = ['jest-environment-node', 'jest-environment-jsdom'] - - withVersions('jest', versions, (version, moduleName) => { - afterEach(() => { - delete process.env.DD_API_KEY - const jestTestFile = fs.readdirSync(__dirname).filter(name => name.startsWith('jest-')) - jestTestFile.forEach((testFile) => { - delete require.cache[require.resolve(path.join(__dirname, testFile))] - }) - delete require.cache[require.resolve(path.join(__dirname, 'env.js'))] - nock.cleanAll() - return agent.close({ ritmReset: false, wipe: true }) - }) - beforeEach(function () { - process.env.DD_API_KEY = 'key' - }) - describe('jest with jest-circus', () => { - describe('older versions of the agent', () => { - beforeEach(async () => { - nock('http://test:123') - .get('/') - .reply(200, 'OK') - - const loadedAgent = await loadAgent(moduleName, version, false, false) - jestExecutable = loadedAgent.jestExecutable - jestCommonOptions = loadedAgent.jestCommonOptions - }) - - it('should create test spans for sync, async, integration, parameterized and retried tests', (done) => { - const tests = [ - { - name: 'jest-test-suite tracer and active span are available', - status: 'pass', - extraTags: { 'test.add.stuff': 'stuff' } - }, - { name: 'jest-test-suite done', status: 'pass' }, - { name: 'jest-test-suite done fail', status: 'fail' }, - { name: 'jest-test-suite done fail uncaught', status: 'fail' }, - { name: 'jest-test-suite can do integration http', status: 'pass' }, - { - name: 'jest-test-suite can do parameterized test', - status: 'pass', - parameters: { arguments: [1, 2, 3], metadata: {} } - }, - { - name: 'jest-test-suite can do parameterized test', - status: 'pass', - parameters: { arguments: [2, 3, 5], metadata: {} } - }, - { name: 'jest-test-suite promise passes', status: 'pass' }, - { name: 'jest-test-suite promise fails', status: 'fail' }, - { name: 'jest-test-suite timeout', status: 'fail', error: 'Exceeded timeout' }, - { name: 'jest-test-suite passes', status: 'pass' }, - { name: 'jest-test-suite fails', status: 'fail' }, - { name: 'jest-test-suite does not crash with missing stack', status: 'fail' }, - { name: 'jest-test-suite skips', status: 'skip' }, - { name: 'jest-test-suite skips todo', status: 'skip' }, - { name: 'jest-circus-test-retry can retry', status: 'fail' }, - { name: 'jest-circus-test-retry can retry', status: 'fail' }, - { name: 'jest-circus-test-retry can retry', status: 'pass' } - ] - - const assertionPromises = tests.map(({ name, status, error, parameters, extraTags }) => { - return agent.assertSomeTraces(trace => { - const testSpan = trace[0][0] - expect(testSpan.parent_id.toString()).to.equal('0') - expect(testSpan.meta).to.contain({ - language: 'javascript', - service: 'test', - [ORIGIN_KEY]: CI_APP_ORIGIN, - [TEST_FRAMEWORK]: 'jest', - [TEST_NAME]: name, - [TEST_STATUS]: status, - [TEST_SUITE]: 'packages/datadog-plugin-jest/test/jest-test.js', - [TEST_SOURCE_FILE]: 'packages/datadog-plugin-jest/test/jest-test.js', - [TEST_TYPE]: 'test', - [JEST_TEST_RUNNER]: 'jest-circus', - [LIBRARY_VERSION]: ddTraceVersion, - [COMPONENT]: 'jest' - }) - // reads from dd-trace-js' CODEOWNERS - expect(testSpan.meta[TEST_CODE_OWNERS]).to.contain('@DataDog') - - if (extraTags) { - expect(testSpan.meta).to.contain(extraTags) - } - if (error) { - expect(testSpan.meta[ERROR_MESSAGE]).to.include(error) - } - if (name === 'jest-test-suite can do integration http') { - const httpSpan = trace[0].find(span => span.name === 'http.request') - expect(httpSpan.meta[ORIGIN_KEY]).to.equal(CI_APP_ORIGIN) - expect(httpSpan.meta['http.url']).to.equal('http://test:123/') - expect(httpSpan.parent_id.toString()).to.equal(testSpan.span_id.toString()) - } - if (parameters) { - expect(testSpan.meta[TEST_PARAMETERS]).to.equal(JSON.stringify(parameters)) - } - expect(testSpan.type).to.equal('test') - expect(testSpan.name).to.equal('jest.test') - expect(testSpan.service).to.equal('test') - expect(testSpan.resource).to.equal(`packages/datadog-plugin-jest/test/jest-test.js.${name}`) - expect(testSpan.metrics[TEST_SOURCE_START]).to.exist - expect(testSpan.meta[TEST_FRAMEWORK_VERSION]).not.to.be.undefined - }, { - timeoutMs: assertionTimeout, - spanResourceMatch: new RegExp(`${name}$`) - }) - }) - - Promise.all(assertionPromises).then(() => done()).catch(done) - - const options = { - ...jestCommonOptions, - testRegex: 'jest-test.js' - } - - jestExecutable.runCLI( - options, - options.projects - ) - }) - - it('should detect an error in hooks', (done) => { - const tests = [ - { name: 'jest-hook-failure will not run', error: 'hey, hook error before' }, - { name: 'jest-hook-failure-after will not run', error: 'hey, hook error after' } - ] - const assertionPromises = tests.map(({ name, error }) => { - return agent.assertSomeTraces(trace => { - const testSpan = trace[0][0] - expect(testSpan.parent_id.toString()).to.equal('0') - expect(testSpan.meta).to.contain({ - language: 'javascript', - service: 'test', - [ORIGIN_KEY]: CI_APP_ORIGIN, - [TEST_FRAMEWORK]: 'jest', - [TEST_NAME]: name, - [TEST_STATUS]: 'fail', - [TEST_SUITE]: 'packages/datadog-plugin-jest/test/jest-hook-failure.js', - [TEST_SOURCE_FILE]: 'packages/datadog-plugin-jest/test/jest-hook-failure.js', - [TEST_TYPE]: 'test', - [JEST_TEST_RUNNER]: 'jest-circus', - [COMPONENT]: 'jest' - }) - expect(testSpan.meta[ERROR_MESSAGE]).to.equal(error) - expect(testSpan.type).to.equal('test') - expect(testSpan.name).to.equal('jest.test') - expect(testSpan.service).to.equal('test') - expect(testSpan.resource).to.equal( - `packages/datadog-plugin-jest/test/jest-hook-failure.js.${name}` - ) - expect(testSpan.meta[TEST_FRAMEWORK_VERSION]).not.to.be.undefined - }, { - timeoutMs: assertionTimeout, - spanResourceMatch: new RegExp(`${name}$`) - }) - }) - - Promise.all(assertionPromises).then(() => done()).catch(done) - - const options = { - ...jestCommonOptions, - testRegex: 'jest-hook-failure.js' - } - - jestExecutable.runCLI( - options, - options.projects - ) - }) - - it('should work with focused tests', (done) => { - const tests = [ - { name: 'jest-test-focused will be skipped', status: 'skip' }, - { name: 'jest-test-focused-2 will be skipped too', status: 'skip' }, - { name: 'jest-test-focused can do focused test', status: 'pass' } - ] - - const assertionPromises = tests.map(({ name, status }) => { - return agent.assertSomeTraces(trace => { - const testSpan = trace[0].find(span => span.type === 'test') - expect(testSpan.parent_id.toString()).to.equal('0') - expect(testSpan.meta[ORIGIN_KEY]).to.equal(CI_APP_ORIGIN) - expect(testSpan.meta).to.contain({ - language: 'javascript', - service: 'test', - [TEST_NAME]: name, - [TEST_STATUS]: status, - [TEST_FRAMEWORK]: 'jest', - [TEST_SUITE]: 'packages/datadog-plugin-jest/test/jest-focus.js', - [TEST_SOURCE_FILE]: 'packages/datadog-plugin-jest/test/jest-focus.js', - [COMPONENT]: 'jest' - }) - }, { - timeoutMs: assertionTimeout, - spanResourceMatch: new RegExp(`${name}$`) - }) - }) - - Promise.all(assertionPromises).then(() => done()).catch(done) - - const options = { - ...jestCommonOptions, - testRegex: 'jest-focus.js' - } - - jestExecutable.runCLI( - options, - options.projects - ) - }) - - // option available from 26.5.0: - // https://github.com/facebook/jest/blob/7f2731ef8bebac7f226cfc0d2446854603a557a9/CHANGELOG.md#2650 - if (semver.intersects(version, '>=26.5.0')) { - it('does not crash when injectGlobals is false', (done) => { - agent.assertSomeTraces(trace => { - const testSpan = trace[0].find(span => span.type === 'test') - expect(testSpan.meta).to.contain({ - [TEST_NAME]: 'jest-inject-globals will be run', - [TEST_STATUS]: 'pass', - [TEST_SUITE]: 'packages/datadog-plugin-jest/test/jest-inject-globals.js' - }) - }, { timeoutMs: assertionTimeout }).then(() => done()).catch(done) - - const options = { - ...jestCommonOptions, - testRegex: 'jest-inject-globals.js', - injectGlobals: false - } - - jestExecutable.runCLI( - options, - options.projects - ) - }) - } - }) - - const initOptions = ['agentless', 'evp proxy'] - - initOptions.forEach(option => { - describe(`reporting through ${option}`, () => { - beforeEach(async () => { - const isAgentlessTest = option === 'agentless' - const isEvpProxyTest = option === 'evp proxy' - - const loadedAgent = await loadAgent(moduleName, version, isAgentlessTest, isEvpProxyTest) - jestExecutable = loadedAgent.jestExecutable - jestCommonOptions = loadedAgent.jestCommonOptions - }) - - it('should create events for session, suite and test', (done) => { - const events = [ - { - type: 'test_session_end', - status: 'pass', - spanResourceMatch: /^test_session/ - }, - { - type: 'test_suite_end', - status: 'pass', - suite: 'packages/datadog-plugin-jest/test/jest-test-suite.js', - spanResourceMatch: /^test_suite/ - }, - { - name: 'jest-test-suite-visibility works', - suite: 'packages/datadog-plugin-jest/test/jest-test-suite.js', - status: 'pass', - type: 'test', - spanResourceMatch: /jest-test-suite-visibility works$/ - } - ] - - const assertionPromises = events.map(({ name, suite, status, type, spanResourceMatch }) => { - return agent.assertSomeTraces((agentlessPayload, request) => { - if (option === 'evp proxy') { - expect(request.headers['x-datadog-evp-subdomain']).to.equal('citestcycle-intake') - expect(request.path).to.equal('/evp_proxy/v2/api/v2/citestcycle') - } else { - expect(request.path).to.equal('/api/v2/citestcycle') - } - const { events } = agentlessPayload - const span = events.find(event => event.type === type).content - expect(span.meta[TEST_STATUS]).to.equal(status) - expect(span.meta[COMPONENT]).to.equal('jest') - if (type === 'test_session_end') { // session and module come in the same payload - expect(span.meta[TEST_COMMAND]).not.to.equal(undefined) - expect(span.meta[TEST_TOOLCHAIN]).not.to.equal(undefined) - expect(span[TEST_SUITE_ID]).to.equal(undefined) - expect(span[TEST_MODULE_ID]).to.equal(undefined) - expect(span[TEST_SESSION_ID]).not.to.equal(undefined) - const testModuleSpan = events.find(event => event.type === 'test_module_end').content - expect(testModuleSpan[TEST_SUITE_ID]).to.equal(undefined) - expect(testModuleSpan[TEST_MODULE_ID]).not.to.equal(undefined) - expect(testModuleSpan[TEST_SESSION_ID]).not.to.equal(undefined) - expect(testModuleSpan.meta[TEST_MODULE]).not.to.equal(undefined) - } - if (type === 'test_suite_end') { - expect(span.meta[TEST_SUITE]).to.equal(suite) - expect(span.meta[TEST_COMMAND]).not.to.equal(undefined) - expect(span.meta[TEST_MODULE]).not.to.equal(undefined) - expect(span[TEST_SUITE_ID]).not.to.equal(undefined) - expect(span[TEST_SESSION_ID]).not.to.equal(undefined) - expect(span[TEST_MODULE_ID]).not.to.equal(undefined) - } - if (type === 'test') { - expect(span.meta[TEST_SUITE]).to.equal(suite) - expect(span.meta[TEST_NAME]).to.equal(name) - expect(span.meta[TEST_COMMAND]).not.to.equal(undefined) - expect(span.meta[TEST_MODULE]).not.to.equal(undefined) - expect(span[TEST_SUITE_ID]).not.to.equal(undefined) - expect(span[TEST_SESSION_ID]).not.to.equal(undefined) - expect(span[TEST_MODULE_ID]).not.to.equal(undefined) - } - }, { timeoutMs: assertionTimeout, spanResourceMatch }) - }) - - Promise.all(assertionPromises).then(() => done()).catch(done) - - const options = { - ...jestCommonOptions, - testRegex: 'jest-test-suite.js' - } - - jestExecutable.runCLI( - options, - options.projects - ) - }) - }) - }) - }) - }) -}) diff --git a/packages/datadog-plugin-jest/test/env.js b/packages/datadog-plugin-jest/test/env.js deleted file mode 100644 index 17bac3a2f91..00000000000 --- a/packages/datadog-plugin-jest/test/env.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict' - -const env = require(`../../../versions/${global.__libraryName__}@${global.__libraryVersion__}`).get() - -module.exports = env.default ? env.default : env diff --git a/packages/datadog-plugin-jest/test/fixtures/test-to-run.js b/packages/datadog-plugin-jest/test/fixtures/test-to-run.js deleted file mode 100644 index 40326d2276a..00000000000 --- a/packages/datadog-plugin-jest/test/fixtures/test-to-run.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -const { expect } = require('chai') - -describe('test-to-run', () => { - it('can report tests', () => { - expect(1 + 2).to.equal(3) - }) -}) diff --git a/packages/datadog-plugin-jest/test/fixtures/test-to-skip.js b/packages/datadog-plugin-jest/test/fixtures/test-to-skip.js deleted file mode 100644 index 54223246967..00000000000 --- a/packages/datadog-plugin-jest/test/fixtures/test-to-skip.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -const { expect } = require('chai') - -describe('test-to-skip', () => { - it('can report tests', () => { - expect(1 + 2).to.equal(3) - }) -}) diff --git a/packages/datadog-plugin-jest/test/fixtures/test-unskippable.js b/packages/datadog-plugin-jest/test/fixtures/test-unskippable.js deleted file mode 100644 index a0b0d303340..00000000000 --- a/packages/datadog-plugin-jest/test/fixtures/test-unskippable.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @datadog {"unskippable": true} - */ -'use strict' - -const { expect } = require('chai') - -describe('test-unskippable', () => { - it('can report tests', () => { - expect(1 + 2).to.equal(3) - }) -}) diff --git a/packages/datadog-plugin-langchain/test/integration-test/client.spec.js b/packages/datadog-plugin-langchain/test/integration-test/client.spec.js index 732fddc4070..62d3e63c445 100644 --- a/packages/datadog-plugin-langchain/test/integration-test/client.spec.js +++ b/packages/datadog-plugin-langchain/test/integration-test/client.spec.js @@ -14,11 +14,11 @@ describe('esm', () => { let agent let proc - withVersions('langchain', ['@langchain/core'], '>=0.1', version => { + // TODO(sabrenner, MLOB-4410): follow-up on re-enabling this test in a different PR once a fix lands + withVersions('langchain', ['@langchain/core'], '>=0.1 <1.0.0', version => { useSandbox([ `@langchain/core@${version}`, `@langchain/openai@${version}`, - 'nock' ], false, [ './packages/datadog-plugin-langchain/test/integration-test/*' ]) diff --git a/packages/dd-trace/src/appsec/api_security_sampler.js b/packages/dd-trace/src/appsec/api_security_sampler.js index bf3fc9f513d..c00779c5e3a 100644 --- a/packages/dd-trace/src/appsec/api_security_sampler.js +++ b/packages/dd-trace/src/appsec/api_security_sampler.js @@ -1,6 +1,6 @@ 'use strict' -const TTLCache = require('@isaacs/ttlcache') +const { TTLCache } = require('@isaacs/ttlcache') const web = require('../plugins/util/web') const log = require('../log') const { AUTO_REJECT, USER_REJECT } = require('../../../../ext/priority') diff --git a/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js b/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js index c6330fd3d36..77e8fbd1a25 100644 --- a/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +++ b/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js @@ -8,8 +8,8 @@ const STRINGIFY_SENSITIVE_KEY = STRINGIFY_RANGE_KEY + 'SENSITIVE' const STRINGIFY_SENSITIVE_NOT_STRING_KEY = STRINGIFY_SENSITIVE_KEY + 'NOTSTRING' // eslint-disable-next-line @stylistic/max-len -const KEYS_REGEX_WITH_SENSITIVE_RANGES = new RegExp(`(?:"(${STRINGIFY_RANGE_KEY}_\\d+_))|(?:"(${STRINGIFY_SENSITIVE_KEY}_\\d+_(\\d+)_))|("${STRINGIFY_SENSITIVE_NOT_STRING_KEY}_\\d+_([\\s0-9.a-zA-Z]*)")`, 'gm') -const KEYS_REGEX_WITHOUT_SENSITIVE_RANGES = new RegExp(`"(${STRINGIFY_RANGE_KEY}_\\d+_)`, 'gm') +const KEYS_REGEX_WITH_SENSITIVE_RANGES = new RegExp(String.raw`(?:"(${STRINGIFY_RANGE_KEY}_\d+_))|(?:"(${STRINGIFY_SENSITIVE_KEY}_\d+_(\d+)_))|("${STRINGIFY_SENSITIVE_NOT_STRING_KEY}_\d+_([\s0-9.a-zA-Z]*)")`, 'gm') +const KEYS_REGEX_WITHOUT_SENSITIVE_RANGES = new RegExp(String.raw`"(${STRINGIFY_RANGE_KEY}_\d+_)`, 'gm') const sensitiveValueRegex = new RegExp(DEFAULT_IAST_REDACTION_VALUE_PATTERN, 'gmi') diff --git a/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js b/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js index 201a945b25e..73ac442b3c4 100644 --- a/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +++ b/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js @@ -84,9 +84,10 @@ function sendVulnerabilities (vulnerabilities, span) { const jsonToSend = vulnerabilitiesFormatter.toJson(validatedVulnerabilities) if (jsonToSend.vulnerabilities.length > 0) { - const tags = {} - // TODO: Store this outside of the span and set the tag in the exporter. - tags[IAST_JSON_TAG_KEY] = JSON.stringify(jsonToSend) + const tags = { + // TODO: Store this outside of the span and set the tag in the exporter. + [IAST_JSON_TAG_KEY]: JSON.stringify(jsonToSend) + } span.addTags(tags) } } diff --git a/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js b/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js index 4387412d038..2aa8b9e44c7 100644 --- a/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +++ b/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js @@ -13,6 +13,10 @@ module.exports = { getLocalStateForCallFrame } +function returnError () { + return new Error('Error getting local state') +} + async function getLocalStateForCallFrame ( callFrame, { @@ -37,7 +41,7 @@ async function getLocalStateForCallFrame ( // TODO: We might be able to get part of the scope chain. // Consider if we could set errors just for the part of the scope chain that throws during collection. log.error('[debugger:devtools_client] Error getting local state for call frame', err) - return () => new Error('Error getting local state') + return returnError } // Delay calling `processRawState` so the caller gets a chance to resume the main thread before processing `rawState` diff --git a/packages/dd-trace/src/exporters/common/docker.js b/packages/dd-trace/src/exporters/common/docker.js index 33f4e539cf7..b7d14b353bb 100644 --- a/packages/dd-trace/src/exporters/common/docker.js +++ b/packages/dd-trace/src/exporters/common/docker.js @@ -12,7 +12,7 @@ const uuidSource = const containerSource = '[0-9a-f]{64}' const taskSource = String.raw`[0-9a-f]{32}-\d+` const lineReg = /^(\d+):([^:]*):(.+)$/m -const entityReg = new RegExp(`.*(${uuidSource}|${containerSource}|${taskSource})(?:\\.scope)?$`, 'm') +const entityReg = new RegExp(String.raw`.*(${uuidSource}|${containerSource}|${taskSource})(?:\.scope)?$`, 'm') let inode = 0 let cgroup = '' diff --git a/packages/dd-trace/src/exporters/span-stats/writer.js b/packages/dd-trace/src/exporters/span-stats/writer.js index 692dde47ca4..9a0c8813ebb 100644 --- a/packages/dd-trace/src/exporters/span-stats/writer.js +++ b/packages/dd-trace/src/exporters/span-stats/writer.js @@ -36,13 +36,12 @@ function makeRequest (data, url, cb) { 'Datadog-Meta-Lang': 'javascript', 'Datadog-Meta-Tracer-Version': pkg.version, 'Content-Type': 'application/msgpack' - } + }, + protocol: url.protocol, + hostname: url.hostname, + port: url.port } - options.protocol = url.protocol - options.hostname = url.hostname - options.port = url.port - log.debug('Request to the intake: %j', options) request(data, options, (err, res) => { diff --git a/packages/dd-trace/src/format.js b/packages/dd-trace/src/format.js index 3009ab571da..570b5aee301 100644 --- a/packages/dd-trace/src/format.js +++ b/packages/dd-trace/src/format.js @@ -32,13 +32,13 @@ const map = { 'resource.name': 'resource' } -function format (span) { +function format (span, isChunkRoot) { const formatted = formatSpan(span) extractSpanLinks(formatted, span) extractSpanEvents(formatted, span) extractRootTags(formatted, span) - extractChunkTags(formatted, span) + extractChunkTags(formatted, span, isChunkRoot) extractTags(formatted, span) return formatted @@ -192,11 +192,10 @@ function extractRootTags (formattedSpan, span) { addTag({}, formattedSpan.metrics, TOP_LEVEL_KEY, 1) } -function extractChunkTags (formattedSpan, span) { +function extractChunkTags (formattedSpan, span, isChunkRoot) { const context = span.context() - const isLocalRoot = span === context._trace.started[0] - if (!isLocalRoot) return + if (!isChunkRoot) return for (const [key, value] of Object.entries(context._trace.tags)) { addTag(formattedSpan.meta, formattedSpan.metrics, key, value) diff --git a/packages/dd-trace/src/llmobs/plugins/ai/index.js b/packages/dd-trace/src/llmobs/plugins/ai/index.js index 0d4c9dc8091..e732ce18ab9 100644 --- a/packages/dd-trace/src/llmobs/plugins/ai/index.js +++ b/packages/dd-trace/src/llmobs/plugins/ai/index.js @@ -82,7 +82,9 @@ class VercelAILLMObsPlugin extends BaseLLMObsPlugin { * @param {string} toolDescription * @returns {string | undefined} */ - findToolName (toolDescription) { + findToolName (toolName, toolDescription) { + if (Number.isNaN(Number.parseInt(toolName))) return toolName + for (const availableTool of this.#availableTools) { const description = availableTool.description if (description === toolDescription && availableTool.id) { @@ -260,16 +262,17 @@ class VercelAILLMObsPlugin extends BaseLLMObsPlugin { const formattedToolCalls = [] for (const toolCall of outputMessageToolCalls) { - const toolCallArgs = getJsonStringValue(toolCall.args, {}) + const toolArgs = toolCall.args ?? toolCall.input + const toolCallArgs = typeof toolArgs === 'string' ? getJsonStringValue(toolArgs, {}) : toolArgs const toolDescription = toolsForModel?.find(tool => toolCall.toolName === tool.name)?.description - const name = this.findToolName(toolDescription) + const name = this.findToolName(toolCall.toolName, toolDescription) this.#toolCallIdsToName[toolCall.toolCallId] = name formattedToolCalls.push({ arguments: toolCallArgs, name, toolId: toolCall.toolCallId, - type: 'function' + type: toolCall.toolCallType ?? 'function' }) } @@ -317,10 +320,10 @@ class VercelAILLMObsPlugin extends BaseLLMObsPlugin { finalContent += part.text ?? part.data } else if (type === 'tool-call') { const toolDescription = toolsForModel?.find(tool => part.toolName === tool.name)?.description - const name = this.findToolName(toolDescription) + const name = this.findToolName(part.toolName, toolDescription) toolCalls.push({ - arguments: part.args, + arguments: part.args ?? part.input, name, toolId: part.toolCallId, type: 'function' diff --git a/packages/dd-trace/src/llmobs/plugins/anthropic.js b/packages/dd-trace/src/llmobs/plugins/anthropic.js index bf60592d65f..10a9f4d4022 100644 --- a/packages/dd-trace/src/llmobs/plugins/anthropic.js +++ b/packages/dd-trace/src/llmobs/plugins/anthropic.js @@ -242,12 +242,9 @@ class AnthropicLLMObsPlugin extends LLMObsPlugin { const cacheWriteTokens = usage.cache_creation_input_tokens const cacheReadTokens = usage.cache_read_input_tokens - const metrics = {} - - metrics.inputTokens = - (inputTokens ?? 0) + - (cacheWriteTokens ?? 0) + - (cacheReadTokens ?? 0) + const metrics = { + inputTokens: (inputTokens ?? 0) + (cacheWriteTokens ?? 0) + (cacheReadTokens ?? 0) + } if (outputTokens) metrics.outputTokens = outputTokens const totalTokens = metrics.inputTokens + (outputTokens ?? 0) diff --git a/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js b/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js index a355bf96296..7487194baef 100644 --- a/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +++ b/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js @@ -37,8 +37,9 @@ class LangChainLLMObsHandler { return message } try { - const messageContent = {} - messageContent.content = message.content || '' + const messageContent = { + content: message.content || '' + } const role = this.getRole(message) if (role) messageContent.role = role diff --git a/packages/dd-trace/src/llmobs/span_processor.js b/packages/dd-trace/src/llmobs/span_processor.js index e9364b2177d..2545c82abef 100644 --- a/packages/dd-trace/src/llmobs/span_processor.js +++ b/packages/dd-trace/src/llmobs/span_processor.js @@ -203,8 +203,8 @@ class LLMObsSpanProcessor { // This function can be reused for other fields if needed // Messages, Documents, and Metrics are safeguarded in `llmobs/tagger.js` #addObject (obj, carrier) { - const seenObjects = new WeakSet() - seenObjects.add(obj) // capture root object + // Capture root object by default + const seenObjects = new WeakSet([obj]) const isCircular = value => { if (typeof value !== 'object') return false diff --git a/packages/dd-trace/src/llmobs/util.js b/packages/dd-trace/src/llmobs/util.js index 850f4b56ebe..4e16c4aa3a5 100644 --- a/packages/dd-trace/src/llmobs/util.js +++ b/packages/dd-trace/src/llmobs/util.js @@ -6,7 +6,7 @@ function encodeUnicode (str = '') { let result = '' for (let i = 0; i < str.length; i++) { const code = str.charCodeAt(i) - result += code > 127 ? `\\u${code.toString(16).padStart(4, '0')}` : str[i] + result += code > 127 ? String.raw`\u${code.toString(16).padStart(4, '0')}` : str[i] } return result } diff --git a/packages/dd-trace/src/openfeature/writers/exposures.js b/packages/dd-trace/src/openfeature/writers/exposures.js index 9fcd9a369ab..97e59e81ca0 100644 --- a/packages/dd-trace/src/openfeature/writers/exposures.js +++ b/packages/dd-trace/src/openfeature/writers/exposures.js @@ -27,7 +27,7 @@ const { /** * @typedef {Object} ExposureContext - * @property {string} service_name - Service name + * @property {string} service - Service name * @property {string} [version] - Service version * @property {string} [env] - Service environment */ @@ -127,7 +127,7 @@ class ExposuresWriter extends BaseFFEWriter { */ _buildContext () { const context = { - service_name: this._config.service || 'unknown' + service: this._config.service || 'unknown' } // Only include version and env if they are defined diff --git a/packages/dd-trace/src/plugins/util/serverless.js b/packages/dd-trace/src/plugins/util/serverless.js index 43958314705..1185cd724d2 100644 --- a/packages/dd-trace/src/plugins/util/serverless.js +++ b/packages/dd-trace/src/plugins/util/serverless.js @@ -3,7 +3,6 @@ const types = require('../../../../../ext/types') const web = require('./web') -const serverless = { ...web } -serverless.TYPE = types.SERVERLESS +const serverless = { ...web, TYPE: types.SERVERLESS } module.exports = serverless diff --git a/packages/dd-trace/src/plugins/util/test.js b/packages/dd-trace/src/plugins/util/test.js index 98e5bde7e66..a89fc1808ca 100644 --- a/packages/dd-trace/src/plugins/util/test.js +++ b/packages/dd-trace/src/plugins/util/test.js @@ -627,20 +627,29 @@ function getCodeOwnersFileEntries (rootDir) { return entries.reverse() } +const codeOwnersPerFileName = new Map() + function getCodeOwnersForFilename (filename, entries) { if (!entries) { return null } + if (codeOwnersPerFileName.has(filename)) { + return codeOwnersPerFileName.get(filename) + } for (const entry of entries) { try { const isResponsible = ignore().add(entry.pattern).ignores(filename) if (isResponsible) { - return JSON.stringify(entry.owners) + const codeOwners = JSON.stringify(entry.owners) + codeOwnersPerFileName.set(filename, codeOwners) + return codeOwners } } catch { + codeOwnersPerFileName.set(filename, null) return null } } + codeOwnersPerFileName.set(filename, null) return null } diff --git a/packages/dd-trace/src/plugins/util/web.js b/packages/dd-trace/src/plugins/util/web.js index 48fddbbeb40..91829172f1b 100644 --- a/packages/dd-trace/src/plugins/util/web.js +++ b/packages/dd-trace/src/plugins/util/web.js @@ -577,13 +577,17 @@ function getHeadersToRecord (config) { return [] } +function isNot500ErrorCode (code) { + return code < 500 +} + function getStatusValidator (config) { if (typeof config.validateStatus === 'function') { return config.validateStatus } else if (config.hasOwnProperty('validateStatus')) { log.error('Expected `validateStatus` to be a function.') } - return code => code < 500 + return isNot500ErrorCode } const noop = () => {} diff --git a/packages/dd-trace/src/runtime_metrics/runtime_metrics.js b/packages/dd-trace/src/runtime_metrics/runtime_metrics.js index 7b3bb30136e..fc9cb546adc 100644 --- a/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +++ b/packages/dd-trace/src/runtime_metrics/runtime_metrics.js @@ -22,7 +22,7 @@ let nativeMetrics = null let gcObserver = null let interval = null let client = null -let lastTime = 0n +let lastTime = 0 let lastCpuUsage = null let eventLoopDelayObserver = null @@ -103,7 +103,6 @@ module.exports = { interval = null client = null - lastTime = 0n lastCpuUsage = null gcObserver?.disconnect() @@ -339,6 +338,7 @@ function startGCObserver () { gcObserver = new PerformanceObserver(list => { for (const entry of list.getEntries()) { + // @ts-expect-error - entry.detail?.kind and entry.kind are not typed const type = gcType(entry.detail?.kind || entry.kind) const duration = entry.duration * 1_000_000 diff --git a/packages/dd-trace/src/span_processor.js b/packages/dd-trace/src/span_processor.js index 9bcb6410361..6ff50b6abcb 100644 --- a/packages/dd-trace/src/span_processor.js +++ b/packages/dd-trace/src/span_processor.js @@ -47,11 +47,14 @@ class SpanProcessor { this._spanSampler.sample(spanContext) this._gitMetadataTagger.tagGitMetadata(spanContext) + let isChunkRoot = true + for (const span of started) { if (span._duration === undefined) { active.push(span) } else { - const formattedSpan = format(span) + const formattedSpan = format(span, isChunkRoot) + isChunkRoot = false this._stats?.onSpanFinished(formattedSpan) formatted.push(formattedSpan) diff --git a/packages/dd-trace/src/startup-log.js b/packages/dd-trace/src/startup-log.js index 25c2f24cb0f..b985eef7c12 100644 --- a/packages/dd-trace/src/startup-log.js +++ b/packages/dd-trace/src/startup-log.js @@ -10,18 +10,10 @@ const tracerVersion = require('../../../package.json').version const errors = {} let config let pluginManager +/** @type {import('./sampling_rule')[]} */ let samplingRules = [] let alreadyRan = false -/** - * @returns {Record} - */ -function getIntegrationsAndAnalytics () { - return { - integrations_loaded: Object.keys(pluginManager._pluginsByName) - } -} - /** * @param {{ agentError: { code: string, message: string } }} [options] */ @@ -70,36 +62,30 @@ function tracerInfo () { return JSON.stringify(this, (_key_, value) => { return typeof value === 'bigint' || typeof value === 'symbol' ? String(value) : value }) - } - } - - out.date = new Date().toISOString() - out.os_name = os.type() - out.os_version = os.release() - out.architecture = os.arch() - out.version = tracerVersion - out.lang = 'nodejs' - out.lang_version = process.versions.node - out.env = config.env - out.enabled = config.enabled - out.service = config.service - out.agent_url = url - out.debug = !!config.debug - out.sample_rate = config.sampler.sampleRate - out.sampling_rules = samplingRules - out.tags = config.tags - if (config.tags && config.tags.version) { - out.dd_version = config.tags.version + }, + date: new Date().toISOString(), + os_name: os.type(), + os_version: os.release(), + architecture: os.arch(), + version: tracerVersion, + lang: 'nodejs', + lang_version: process.versions.node, + env: config.env, + enabled: config.enabled, + service: config.service, + agent_url: url, + debug: !!config.debug, + sample_rate: config.sampler.sampleRate, + sampling_rules: samplingRules, + tags: config.tags, + ...(config.tags && config.tags.version && { dd_version: config.tags.version }), + log_injection_enabled: !!config.logInjection, + runtime_metrics_enabled: !!config.runtimeMetrics, + profiling_enabled: config.profiling?.enabled === 'true' || config.profiling?.enabled === 'auto', + integrations_loaded: Object.keys(pluginManager._pluginsByName), + appsec_enabled: !!config.appsec.enabled, } - out.log_injection_enabled = !!config.logInjection - out.runtime_metrics_enabled = !!config.runtimeMetrics - const profilingEnabled = config.profiling?.enabled - out.profiling_enabled = profilingEnabled === 'true' || profilingEnabled === 'auto' - Object.assign(out, getIntegrationsAndAnalytics()) - - out.appsec_enabled = !!config.appsec.enabled - return out } @@ -118,7 +104,7 @@ function setStartupLogPluginManager (thePluginManager) { } /** - * @param {import('./sampling_rule')} theRules + * @param {import('./sampling_rule')[]} theRules */ function setSamplingRules (theRules) { samplingRules = theRules diff --git a/packages/dd-trace/test/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.mquery.plugin.spec.js b/packages/dd-trace/test/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.mquery.plugin.spec.js index 8814534cc25..cde1b3f7fc0 100644 --- a/packages/dd-trace/test/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.mquery.plugin.spec.js +++ b/packages/dd-trace/test/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.mquery.plugin.spec.js @@ -27,10 +27,7 @@ describe('nosql injection detection with mquery', () => { dbName = id().toString() const mongo = require(`../../../../../../versions/mongodb@${mongodbVersion}`).get() - client = new mongo.MongoClient(`mongodb://localhost:27017/${dbName}`, { - useNewUrlParser: true, - useUnifiedTopology: true - }) + client = new mongo.MongoClient(`mongodb://localhost:27017/${dbName}`) await client.connect() testCollection = client.db().collection('Test') diff --git a/packages/dd-trace/test/appsec/index.next.plugin.spec.js b/packages/dd-trace/test/appsec/index.next.plugin.spec.js index 1e596ee9df6..eca21501085 100644 --- a/packages/dd-trace/test/appsec/index.next.plugin.spec.js +++ b/packages/dd-trace/test/appsec/index.next.plugin.spec.js @@ -16,6 +16,9 @@ describe('test suite', () => { } const realVersion = require(`../../../../versions/next@${version}`).version() + if (satisfies(realVersion, '>=16') && NODE_MAJOR < 20) { + return + } const tests = [ { diff --git a/packages/dd-trace/test/format.spec.js b/packages/dd-trace/test/format.spec.js index e1b3a5517c8..3540238bc15 100644 --- a/packages/dd-trace/test/format.spec.js +++ b/packages/dd-trace/test/format.spec.js @@ -293,7 +293,7 @@ describe('format', () => { count: 1 } - trace = format(span) + trace = format(span, true) expect(trace.meta).to.include({ chunk: 'test' @@ -304,13 +304,29 @@ describe('format', () => { }) }) + it('should not extract trace chunk tags when not chunk root', () => { + spanContext._trace.tags = { + chunk: 'test', + count: 1 + } + + trace = format(span, false) + expect(trace.meta).to.not.include({ + chunk: 'test' + }) + + expect(trace.metrics).to.not.include({ + count: 1 + }) + }) + it('should extract empty tags', () => { spanContext._trace.tags = { foo: '', count: 1 } - trace = format(span) + trace = format(span, true) expect(trace.meta).to.include({ foo: '' diff --git a/packages/dd-trace/test/llmobs/plugins/ai/index.spec.js b/packages/dd-trace/test/llmobs/plugins/ai/index.spec.js index 318deca46d4..4bd6278d117 100644 --- a/packages/dd-trace/test/llmobs/plugins/ai/index.spec.js +++ b/packages/dd-trace/test/llmobs/plugins/ai/index.spec.js @@ -346,8 +346,7 @@ describe('Plugin', () => { }) }) - // TODO(sabrenner): Fix this test for v5.0.0 - tool "input" instead of "arguments" - it.skip('creates a span for a tool call', async () => { // eslint-disable-line mocha/no-pending-tests + it('creates a span for a tool call', async () => { let tools let additionalOptions = {} const toolSchema = ai.jsonSchema({ @@ -504,8 +503,7 @@ describe('Plugin', () => { }) }) - // TODO(sabrenner): Fix this test for v5.0.0 - tool "input" instead of "arguments" & parsing, streaming - it.skip('created a span for a tool call from a stream', async () => { // eslint-disable-line mocha/no-pending-tests + it('created a span for a tool call from a stream', async () => { let tools let additionalOptions = {} const toolSchema = ai.jsonSchema({ @@ -629,15 +627,13 @@ describe('Plugin', () => { span: apmSpans[2], parentId: llmobsSpans[0].span_id, /** - * MOCK_STRING used as the stream implementation for ai does not finish the initial llm spans + * Before ai@4.0.2, the stream implementation did not finish the initial llm spans * first to associate the tool call id with the tool itself (by matching descriptions). * - * Usually, this would mean the tool call name is 'toolCall'. - * - * However, because we used mocked responses, the second time this test is called, the tool call - * will have the name 'weather' instead. We just assert that the name exists and is a string to simplify. + * Usually, this would mean the tool call name is 'toolCall'. This is a limitation with the older library + * versions. In v5+, this is resolved as the tool name is not its index in the tools array, but its actual name. */ - name: MOCK_STRING, + name: semifies(realVersion, NODE_MAJOR < 22 ? '<=4.0.2' : '<4.0.2') ? 'toolCall' : 'weather', spanKind: 'tool', inputValue: JSON.stringify({ location: 'Tokyo' }), outputValue: JSON.stringify({ location: 'Tokyo', temperature: 72 }), diff --git a/packages/dd-trace/test/openfeature/writers/exposures.spec.js b/packages/dd-trace/test/openfeature/writers/exposures.spec.js index aaa0cd9a36e..7fea6ce05fb 100644 --- a/packages/dd-trace/test/openfeature/writers/exposures.spec.js +++ b/packages/dd-trace/test/openfeature/writers/exposures.spec.js @@ -178,7 +178,7 @@ describe('OpenFeature Exposures Writer', () => { const payload = writer.makePayload(events) expect(payload.context).to.deep.equal({ - service_name: 'test-service', + service: 'test-service', version: '1.0.0', env: 'test' }) @@ -213,7 +213,7 @@ describe('OpenFeature Exposures Writer', () => { const payload = writerWithoutOptionals.makePayload(events) expect(payload.context).to.deep.equal({ - service_name: 'test-service' + service: 'test-service' }) expect(payload.context).to.not.have.property('version') expect(payload.context).to.not.have.property('env') @@ -279,7 +279,7 @@ describe('OpenFeature Exposures Writer', () => { expect(parsedPayload).to.have.property('exposures') expect(parsedPayload.exposures).to.be.an('array').with.length(1) expect(parsedPayload.exposures[0].timestamp).to.exist - expect(parsedPayload.context.service_name).to.equal('test-service') + expect(parsedPayload.context.service).to.equal('test-service') }) it('should empty buffer after flushing', () => { diff --git a/packages/dd-trace/test/plugins/externals.json b/packages/dd-trace/test/plugins/externals.json index e8e4adeaa06..867773788c5 100644 --- a/packages/dd-trace/test/plugins/externals.json +++ b/packages/dd-trace/test/plugins/externals.json @@ -289,28 +289,6 @@ "versions": ["1.15.0"] } ], - "jest": [ - { - "name": "jest", - "versions": [">=24.8.0"] - }, - { - "name": "jest-environment-jsdom", - "versions": [">=24.8.0"] - }, - { - "name": "jest-environment-node", - "versions": [">=24.8.0"] - }, - { - "name": "jest-circus", - "versions": [">=24.8.0"] - }, - { - "name": "@jest/globals", - "versions": [">=26.5.0"] - } - ], "knex": [ { "name": "pg", diff --git a/packages/dd-trace/test/plugins/versions/package.json b/packages/dd-trace/test/plugins/versions/package.json index f6c934a00e2..cb442731d49 100644 --- a/packages/dd-trace/test/plugins/versions/package.json +++ b/packages/dd-trace/test/plugins/versions/package.json @@ -4,73 +4,73 @@ "license": "BSD-3-Clause", "private": true, "dependencies": { - "@ai-sdk/openai": "2.0.52", - "@anthropic-ai/sdk": "0.67.0", - "@openai/agents": "0.1.9", - "@openai/agents-core": "0.1.8", - "@apollo/gateway": "2.11.3", - "@apollo/server": "5.0.0", - "@apollo/subgraph": "2.11.3", - "@aws-sdk/client-bedrock-runtime": "3.911.0", - "@aws-sdk/client-dynamodb": "3.911.0", - "@aws-sdk/client-kinesis": "3.911.0", - "@aws-sdk/client-lambda": "3.911.0", - "@aws-sdk/client-s3": "3.911.0", - "@aws-sdk/client-sfn": "3.911.0", - "@aws-sdk/client-sns": "3.911.0", - "@aws-sdk/client-sqs": "3.911.0", + "@ai-sdk/openai": "2.0.65", + "@anthropic-ai/sdk": "0.68.0", + "@openai/agents": "0.3.0", + "@openai/agents-core": "0.3.0", + "@apollo/gateway": "2.12.0", + "@apollo/server": "5.1.0", + "@apollo/subgraph": "2.12.0", + "@aws-sdk/client-bedrock-runtime": "3.928.0", + "@aws-sdk/client-dynamodb": "3.928.0", + "@aws-sdk/client-kinesis": "3.928.0", + "@aws-sdk/client-lambda": "3.928.0", + "@aws-sdk/client-s3": "3.928.0", + "@aws-sdk/client-sfn": "3.928.0", + "@aws-sdk/client-sns": "3.928.0", + "@aws-sdk/client-sqs": "3.928.0", "@aws-sdk/node-http-handler": "3.374.0", "@aws-sdk/smithy-client": "3.374.0", "@azure/event-hubs": "6.0.0", - "@azure/functions": "4.8.0", + "@azure/functions": "4.9.0", "@azure/service-bus": "7.9.5", "@confluentinc/kafka-javascript": "1.6.0", "@cucumber/cucumber": "12.2.0", - "@datadog/openfeature-node-server": "0.1.0-preview.12", - "@elastic/elasticsearch": "9.1.1", - "@elastic/transport": "9.2.0", + "@datadog/openfeature-node-server": "0.1.0-preview.15", + "@elastic/elasticsearch": "9.2.0", + "@elastic/transport": "9.2.1", "@fastify/cookie": "11.0.2", - "@fastify/multipart": "9.2.1", + "@fastify/multipart": "9.3.0", "@fast-check/jest": "2.1.1", "@google-cloud/pubsub": "5.2.0", "@google-cloud/vertexai": "1.10.0", - "@graphql-tools/executor": "1.4.9", - "@grpc/grpc-js": "1.14.0", + "@graphql-tools/executor": "1.4.11", + "@grpc/grpc-js": "1.14.1", "@grpc/proto-loader": "0.8.0", "@hapi/boom": "10.0.1", - "@hapi/hapi": "21.4.3", - "@happy-dom/jest-environment": "20.0.4", - "@hono/node-server": "1.19.5", + "@hapi/hapi": "21.4.4", + "@happy-dom/jest-environment": "20.0.10", + "@hono/node-server": "1.19.6", "@jest/core": "30.2.0", "@jest/globals": "30.2.0", "@jest/reporters": "30.2.0", "@jest/test-sequencer": "30.2.0", "@jest/transform": "30.2.0", "@koa/router": "14.0.0", - "@langchain/anthropic": "0.3.32", - "@langchain/classic": "1.0.0", - "@langchain/cohere": "0.3.4", - "@langchain/core": "0.3.78", - "@langchain/google-genai": "0.2.18", - "@langchain/openai": "0.6.16", + "@langchain/anthropic": "1.0.0", + "@langchain/classic": "1.0.1", + "@langchain/cohere": "1.0.0", + "@langchain/core": "1.0.3", + "@langchain/google-genai": "1.0.0", + "@langchain/openai": "1.0.0", "@node-redis/client": "1.0.6", "@openfeature/core": "^1.9.0", - "@openfeature/server-sdk": "~1.19.0", + "@openfeature/server-sdk": "~1.20.0", "@opensearch-project/opensearch": "3.5.1", - "@opentelemetry/exporter-jaeger": "2.1.0", - "@opentelemetry/instrumentation": "0.206.0", - "@opentelemetry/instrumentation-express": "0.55.0", - "@opentelemetry/instrumentation-http": "0.206.0", - "@opentelemetry/sdk-node": "0.206.0", + "@opentelemetry/exporter-jaeger": "2.2.0", + "@opentelemetry/instrumentation": "0.208.0", + "@opentelemetry/instrumentation-express": "0.57.0", + "@opentelemetry/instrumentation-http": "0.208.0", + "@opentelemetry/sdk-node": "0.208.0", "@playwright/test": "1.56.1", - "@prisma/client": "6.17.1", - "@redis/client": "5.8.3", - "@smithy/smithy-client": "4.9.0", - "@vitest/coverage-istanbul": "4.0.6", - "@vitest/coverage-v8": "4.0.6", - "@vitest/runner": "4.0.6", + "@prisma/client": "6.19.0", + "@redis/client": "5.9.0", + "@smithy/smithy-client": "4.9.3", + "@vitest/coverage-istanbul": "4.0.8", + "@vitest/coverage-v8": "4.0.8", + "@vitest/runner": "4.0.8", "aerospike": "6.4.0", - "ai": "5.0.75", + "ai": "5.0.92", "amqp10": "3.6.0", "amqplib": "0.10.9", "apollo-server-core": "3.13.0", @@ -78,11 +78,11 @@ "apollo-server-fastify": "3.13.0", "avsc": "5.7.9", "aws-sdk": "2.1692.0", - "axios": "1.12.2", - "azure-functions-core-tools": "4.3.0", + "axios": "1.13.2", + "azure-functions-core-tools": "4.4.0", "bluebird": "3.7.2", "body-parser": "2.2.0", - "bson": "6.10.4", + "bson": "7.0.0", "bunyan": "2.0.5", "cassandra-driver": "4.8.0", "chai": "6.2.0", @@ -91,27 +91,27 @@ "cookie": "1.0.2", "cookie-parser": "1.4.7", "couchbase": "4.6.0", - "cypress": "15.5.0", + "cypress": "15.6.0", "cypress-fail-fast": "7.1.1", "dd-trace-api": "1.0.0", "ejs": "3.1.10", "elasticsearch": "16.7.3", - "esbuild": "0.25.11", + "esbuild": "0.27.0", "express": "5.1.0", "express-mongo-sanitize": "2.2.0", "express-session": "1.18.2", - "fastify": "5.6.1", + "fastify": "5.6.2", "find-my-way": "9.3.0", "fs": "0.0.2", "generic-pool": "3.9.0", - "graphql": "16.11.0", + "graphql": "16.12.0", "graphql-tag": "2.12.6", - "graphql-tools": "9.0.20", - "graphql-yoga": "5.16.0", + "graphql-tools": "9.0.22", + "graphql-yoga": "5.16.2", "handlebars": "4.7.8", "hapi": "18.1.0", - "hono": "4.10.0", - "ioredis": "5.8.1", + "hono": "4.10.4", + "ioredis": "5.8.2", "iovalkey": "0.3.3", "jest": "30.2.0", "jest-circus": "30.2.0", @@ -124,11 +124,11 @@ "jest-worker": "30.2.0", "kafkajs": "2.2.4", "knex": "3.1.0", - "koa": "3.0.1", + "koa": "3.1.1", "koa-route": "4.0.1", "koa-router": "14.0.0", "koa-websocket": "7.0.0", - "langchain": "0.3.36", + "langchain": "1.0.3", "ldapjs": "3.0.7", "ldapjs-promise": "3.0.7", "limitd-client": "2.14.1", @@ -138,23 +138,23 @@ "memcached": "2.2.2", "microgateway-core": "3.3.7", "middie": "7.1.0", - "mocha": "11.7.4", + "mocha": "11.7.5", "mocha-each": "2.0.1", "moleculer": "0.14.35", - "mongodb": "6.20.0", + "mongodb": "7.0.0", "mongodb-core": "3.2.7", - "mongoose": "8.19.1", + "mongoose": "8.19.3", "mquery": "5.0.0", "multer": "2.0.2", "mysql": "2.18.1", - "mysql2": "3.15.2", - "next": "15.5.5", + "mysql2": "3.15.3", + "next": "16.0.1", "nock": "14.0.10", "node-serialize": "0.0.4", "npm": "11.6.2", "nyc": "17.1.0", - "office-addin-mock": "3.0.3", - "openai": "6.4.0", + "office-addin-mock": "3.0.6", + "openai": "6.8.1", "oracledb": "6.10.0", "passport": "0.7.0", "passport-http": "0.3.0", @@ -163,12 +163,12 @@ "pg-cursor": "2.15.3", "pg-native": "3.5.2", "pg-query-stream": "4.10.3", - "pino": "10.0.0", + "pino": "10.1.0", "pino-pretty": "13.1.2", "playwright": "1.56.1", "playwright-core": "1.56.1", - "pnpm": "10.18.3", - "prisma": "6.17.1", + "pnpm": "10.21.0", + "prisma": "6.19.0", "promise": "8.3.0", "promise-js": "0.0.7", "protobufjs": "7.5.4", @@ -176,7 +176,7 @@ "q": "2.0.3", "react": "19.2.0", "react-dom": "19.2.0", - "redis": "5.8.3", + "redis": "5.9.0", "request": "2.88.2", "restify": "11.1.0", "rhea": "3.0.4", @@ -186,11 +186,11 @@ "sharedb": "5.2.2", "sinon": "21.0.0", "sqlite3": "5.1.7", - "tedious": "19.0.0", + "tedious": "19.1.0", "tinypool": "2.0.0", "typescript": "5.9.3", "undici": "7.16.0", - "vitest": "4.0.6", + "vitest": "4.0.8", "when": "3.7.8", "winston": "3.18.3", "workerpool": "10.0.0", diff --git a/packages/dd-trace/test/runtime_metrics.spec.js b/packages/dd-trace/test/runtime_metrics.spec.js index c1e40431ad4..00b2def33d6 100644 --- a/packages/dd-trace/test/runtime_metrics.spec.js +++ b/packages/dd-trace/test/runtime_metrics.spec.js @@ -23,7 +23,9 @@ function createGarbage (count = 50) { for (let i = 0; i < count; i++) { last.next = { circular: obj, last, obj: { a: 1, b: 2, c: true } } + // @ts-expect-error - Difficult to define type last = last.next + // @ts-expect-error - Difficult to define type last.map = new Map([['a', 1], ['b', 2], ['c', true]]) obj[i] = last } @@ -133,7 +135,7 @@ function createGarbage (count = 50) { expect(runtimeMetrics.decrement).to.not.have.been.called expect(runtimeMetrics.stop).to.have.been.calledOnce }) - }, { skip: isWindows }) + }, { skip: isWindows && !nativeMetrics }) describe('runtimeMetrics', () => { let runtimeMetrics @@ -260,7 +262,7 @@ function createGarbage (count = 50) { // Wait for GC observer to trigger. const startTime = Date.now() - const waitTime = 200 + const waitTime = 200 + (nativeMetrics ? 0 : 200) let iterations = 0 while (Date.now() - startTime < waitTime) { // Need ticks for the event loop delay @@ -514,7 +516,7 @@ function createGarbage (count = 50) { userPercent = number } if (metric === 'runtime.node.cpu.system') { - assert(number >= 0 && number <= 1, `${metric} sanity check failed: ${number}`) + assert(number >= 0 && number <= 5, `${metric} sanity check failed: ${number}`) systemPercent = number } if (metric === 'runtime.node.cpu.total') { @@ -582,7 +584,7 @@ function createGarbage (count = 50) { ) client.gauge.resetHistory() - const nowStub3 = sinon.stub(performance, 'now').returns(startPerformanceNow + 20_001) + const nowStub3 = sinon.stub(performance, 'now').returns(startPerformanceNow + 20_000) clock.tick(10000) // Advance another 10 seconds nowStub3.restore() @@ -782,6 +784,6 @@ function createGarbage (count = 50) { }) }) }) - }, { skip: isWindows }) + }, { skip: isWindows && !nativeMetrics }) }) }) diff --git a/packages/dd-trace/test/span_processor.spec.js b/packages/dd-trace/test/span_processor.spec.js index 90ab0530a23..86d16ea894d 100644 --- a/packages/dd-trace/test/span_processor.spec.js +++ b/packages/dd-trace/test/span_processor.spec.js @@ -115,7 +115,7 @@ describe('SpanProcessor', () => { expect(trace).to.have.deep.property('finished', []) }) - it('should configure span sampler conrrectly', () => { + it('should configure span sampler correctly', () => { const config = { stats: { enabled: false }, sampler: { @@ -156,4 +156,17 @@ describe('SpanProcessor', () => { expect(finishedSpan.context()).to.have.deep.property('_tags', {}) expect(exporter.export).not.to.have.been.called }) + + it('should call format every time a partial flush is triggered', () => { + config.flushMinSpans = 1 + const processor = new SpanProcessor(exporter, prioritySampler, config) + trace.started = [activeSpan, finishedSpan] + trace.finished = [finishedSpan] + processor.process(activeSpan) + + expect(trace).to.have.deep.property('started', [activeSpan]) + expect(trace).to.have.deep.property('finished', []) + expect(format.callCount).to.equal(1) + expect(format).to.have.been.calledWith(finishedSpan, true) + }) }) diff --git a/scripts/flakiness.mjs b/scripts/flakiness.mjs index 90d52b91f76..2039523f6bb 100644 --- a/scripts/flakiness.mjs +++ b/scripts/flakiness.mjs @@ -7,6 +7,8 @@ const { BRANCH, CI, DAYS = '1', + GITHUB_REPOSITORY, + GITHUB_RUN_ID, MERGE = 'true', OCCURRENCES = '1', UNTIL @@ -146,10 +148,9 @@ if (Object.keys(flaky).length === 0) { if (urls.length < OCCURRENCES) continue // Padding is needed because Slack doesn't show single digits as links. const markdownLinks = urls.map((url, idx) => `[${String(idx + 1).padStart(2, '0')}](${url})`) - const slackLinks = urls.map((url, idx) => `<${url}|${String(idx + 1).padStart(2, '0')}>`) const runsBadge = urls.length >= 3 ? ' 🔴' : urls.length === 2 ? ' 🟡' : '' markdown += ` * ${job} (${markdownLinks.join(', ')})${runsBadge}\n` - slack += ` ○ ${job} (${slackLinks.join(', ')})${runsBadge}\\n` + slack += ` ○ ${job} (${urls.length})${runsBadge}\\n` } } @@ -160,6 +161,13 @@ if (Object.keys(flaky).length === 0) { markdown += `* Workflow success rate: ${workflowSuccessRate}%\n` markdown += `* Pipeline success rate (approx): ${pipelineSuccessRate}% ${pipelineBadge}` + if (GITHUB_REPOSITORY && GITHUB_RUN_ID) { + const link = `https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}` + + slack += '\\n' + slack += `View full report with links to failures on <${link}|GitHub>.` + } + slack += '\\n' slack += '*Flakiness stats*\\n' slack += ` ● Total runs: ${totalCount}\\n` diff --git a/yarn.lock b/yarn.lock index 56ef22177b9..3c3b5914709 100644 --- a/yarn.lock +++ b/yarn.lock @@ -98,17 +98,17 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== -"@babel/helper-validator-identifier@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" - integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== +"@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== "@babel/helper-validator-option@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== -"@babel/helpers@^7.27.6", "@babel/helpers@^7.28.4": +"@babel/helpers@^7.28.4": version "7.28.4" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827" integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w== @@ -204,10 +204,10 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" -"@datadog/flagging-core@0.1.0-preview.13": - version "0.1.0-preview.13" - resolved "https://registry.yarnpkg.com/@datadog/flagging-core/-/flagging-core-0.1.0-preview.13.tgz#73fc80aeb21435fa2307f79f2d315b857616cd5d" - integrity sha512-DfQYeBgGvCerKx6coRiXMt0aXWJB8kRIsJhfdTKyejS9rfp6ZBjWc6dctKzhwMAQdpBiN42MEVXNt4Pdcmj1WA== +"@datadog/flagging-core@0.1.0-preview.15": + version "0.1.0-preview.15" + resolved "https://registry.yarnpkg.com/@datadog/flagging-core/-/flagging-core-0.1.0-preview.15.tgz#bd8c21a7a7e96428c584d5b6f103b8011fa4f847" + integrity sha512-p6YhX7lHOlD3IhyBerL2OM7EEmOLA0MmjgIvEUX61cbRiK983m51rF8b8zXyiCCp/tdfBSmmsujtPwjMwXGiug== dependencies: spark-md5 "^3.0.2" @@ -238,12 +238,12 @@ node-addon-api "^6.1.0" node-gyp-build "^3.9.0" -"@datadog/openfeature-node-server@0.1.0-preview.13": - version "0.1.0-preview.13" - resolved "https://registry.yarnpkg.com/@datadog/openfeature-node-server/-/openfeature-node-server-0.1.0-preview.13.tgz#355dacbbe927d29c903fb3c17d14dec7af34ba55" - integrity sha512-W7+Aff5Ex7EbNnH2P22zycSzIMIJ4+3DgSrSwxSguw9lvaj7rbnKmRv0G9q2kffbVymrUWEFkBEhqB+gRy9FLg== +"@datadog/openfeature-node-server@0.1.0-preview.15": + version "0.1.0-preview.15" + resolved "https://registry.yarnpkg.com/@datadog/openfeature-node-server/-/openfeature-node-server-0.1.0-preview.15.tgz#f9ec9f9614aefd9c03e83c62ea57c6163843a053" + integrity sha512-SXWseSmtEg+mKB2WzCzT5f/m61GEmFNBLchBaPO5w6dECk4jz4g6BXmfLTVdFZXi26/M644niZWHpv9cJVhoqg== dependencies: - "@datadog/flagging-core" "0.1.0-preview.13" + "@datadog/flagging-core" "0.1.0-preview.15" "@openfeature/server-sdk" "~1.18.0" "@datadog/pprof@5.12.0": @@ -272,7 +272,7 @@ module-details-from-path "^1.0.3" node-gyp-build "^4.5.0" -"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.4.0", "@eslint-community/eslint-utils@^4.4.1", "@eslint-community/eslint-utils@^4.5.0", "@eslint-community/eslint-utils@^4.7.0", "@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.0": +"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.4.0", "@eslint-community/eslint-utils@^4.4.1", "@eslint-community/eslint-utils@^4.5.0", "@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.0": version "4.9.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== @@ -293,24 +293,17 @@ debug "^4.3.1" minimatch "^3.1.2" -"@eslint/config-helpers@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.1.tgz#7d173a1a35fe256f0989a0fdd8d911ebbbf50037" - integrity sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw== +"@eslint/config-helpers@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz#1bd006ceeb7e2e55b2b773ab318d300e1a66aeda" + integrity sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== dependencies: - "@eslint/core" "^0.16.0" - -"@eslint/core@^0.15.2": - version "0.15.2" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.15.2.tgz#59386327d7862cc3603ebc7c78159d2dcc4a868f" - integrity sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg== - dependencies: - "@types/json-schema" "^7.0.15" + "@eslint/core" "^0.17.0" -"@eslint/core@^0.16.0": - version "0.16.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.16.0.tgz#490254f275ba9667ddbab344f4f0a6b7a7bd7209" - integrity sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q== +"@eslint/core@^0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.17.0.tgz#77225820413d9617509da9342190a2019e78761c" + integrity sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== dependencies: "@types/json-schema" "^7.0.15" @@ -329,30 +322,22 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.38.0", "@eslint/js@^9.29.0": - version "9.38.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.38.0.tgz#f7aa9c7577577f53302c1d795643589d7709ebd1" - integrity sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A== +"@eslint/js@9.39.1", "@eslint/js@^9.39.0": + version "9.39.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.1.tgz#0dd59c3a9f40e3f1882975c321470969243e0164" + integrity sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw== "@eslint/object-schema@^2.1.7": version "2.1.7" resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad" integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== -"@eslint/plugin-kit@^0.3.3": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz#fd8764f0ee79c8ddab4da65460c641cefee017c5" - integrity sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w== - dependencies: - "@eslint/core" "^0.15.2" - levn "^0.4.1" - -"@eslint/plugin-kit@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz#f6a245b42886abf6fc9c7ab7744a932250335ab2" - integrity sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A== +"@eslint/plugin-kit@^0.4.0", "@eslint/plugin-kit@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz#9779e3fd9b7ee33571a57435cf4335a1794a6cb2" + integrity sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== dependencies: - "@eslint/core" "^0.16.0" + "@eslint/core" "^0.17.0" levn "^0.4.1" "@humanfs/core@^0.19.1": @@ -405,10 +390,10 @@ resolve-from "^3.0.0" rimraf "^3.0.0" -"@isaacs/ttlcache@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz#21fb23db34e9b6220c6ba023a0118a2dd3461ea2" - integrity sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA== +"@isaacs/ttlcache@^2.0.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@isaacs/ttlcache/-/ttlcache-2.1.1.tgz#60f940f427088d5553cc373577bcfbba33a7333e" + integrity sha512-idlHm4Umu3Atw80UOTicChGv5kUA9SuF77NFW1g2/cKunb9yaoCYP/1rSiMn6HG0ELEQZh2BrRZoCRgVS2QL0w== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -475,63 +460,63 @@ resolved "https://registry.yarnpkg.com/@msgpack/msgpack/-/msgpack-3.1.2.tgz#fdd25cc2202297519798bbaf4689152ad9609e19" integrity sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ== -"@octokit/app@^16.1.1": - version "16.1.1" - resolved "https://registry.yarnpkg.com/@octokit/app/-/app-16.1.1.tgz#3b72ba6266d67b2e011e5ea369f92712fd920f61" - integrity sha512-pcvKSN6Q6aT3gU5heoDFs3ywU5xejxeqs1rQpUwgN7CmBlxCSy9aCoqFuC6GpVv71O/Qq/VuYfCNzrOZp/9Ycw== - dependencies: - "@octokit/auth-app" "^8.1.1" - "@octokit/auth-unauthenticated" "^7.0.2" - "@octokit/core" "^7.0.5" - "@octokit/oauth-app" "^8.0.2" - "@octokit/plugin-paginate-rest" "^13.2.0" - "@octokit/types" "^15.0.0" +"@octokit/app@^16.1.2": + version "16.1.2" + resolved "https://registry.yarnpkg.com/@octokit/app/-/app-16.1.2.tgz#2077a19e55c984408e2d74b9f7dd397870eac0df" + integrity sha512-8j7sEpUYVj18dxvh0KWj6W/l6uAiVRBl1JBDVRqH1VHKAO/G5eRVl4yEoYACjakWers1DjUkcCHyJNQK47JqyQ== + dependencies: + "@octokit/auth-app" "^8.1.2" + "@octokit/auth-unauthenticated" "^7.0.3" + "@octokit/core" "^7.0.6" + "@octokit/oauth-app" "^8.0.3" + "@octokit/plugin-paginate-rest" "^14.0.0" + "@octokit/types" "^16.0.0" "@octokit/webhooks" "^14.0.0" -"@octokit/auth-app@^8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@octokit/auth-app/-/auth-app-8.1.1.tgz#28b76c2f861b196c1bf050eeecca21c7931dcab1" - integrity sha512-yW9YUy1cuqWlz8u7908ed498wJFt42VYsYWjvepjojM4BdZSp4t+5JehFds7LfvYi550O/GaUI94rgbhswvxfA== +"@octokit/auth-app@^8.1.2": + version "8.1.2" + resolved "https://registry.yarnpkg.com/@octokit/auth-app/-/auth-app-8.1.2.tgz#68f2ce201c525dc678965c50b377963b3bce135f" + integrity sha512-db8VO0PqXxfzI6GdjtgEFHY9tzqUql5xMFXYA12juq8TeTgPAuiiP3zid4h50lwlIP457p5+56PnJOgd2GGBuw== dependencies: - "@octokit/auth-oauth-app" "^9.0.2" - "@octokit/auth-oauth-user" "^6.0.1" - "@octokit/request" "^10.0.5" - "@octokit/request-error" "^7.0.1" - "@octokit/types" "^15.0.0" + "@octokit/auth-oauth-app" "^9.0.3" + "@octokit/auth-oauth-user" "^6.0.2" + "@octokit/request" "^10.0.6" + "@octokit/request-error" "^7.0.2" + "@octokit/types" "^16.0.0" toad-cache "^3.7.0" universal-github-app-jwt "^2.2.0" universal-user-agent "^7.0.0" -"@octokit/auth-oauth-app@^9.0.2": - version "9.0.2" - resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-app/-/auth-oauth-app-9.0.2.tgz#e21b4501d3ce09cf7fb251b566d95b52a769c9dc" - integrity sha512-vmjSHeuHuM+OxZLzOuoYkcY3OPZ8erJ5lfswdTmm+4XiAKB5PmCk70bA1is4uwSl/APhRVAv4KHsgevWfEKIPQ== +"@octokit/auth-oauth-app@^9.0.2", "@octokit/auth-oauth-app@^9.0.3": + version "9.0.3" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-app/-/auth-oauth-app-9.0.3.tgz#7d7f55e0aea5b207a5c75e1db81385b0dd25113a" + integrity sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg== dependencies: - "@octokit/auth-oauth-device" "^8.0.2" - "@octokit/auth-oauth-user" "^6.0.1" - "@octokit/request" "^10.0.5" - "@octokit/types" "^15.0.0" + "@octokit/auth-oauth-device" "^8.0.3" + "@octokit/auth-oauth-user" "^6.0.2" + "@octokit/request" "^10.0.6" + "@octokit/types" "^16.0.0" universal-user-agent "^7.0.0" -"@octokit/auth-oauth-device@^8.0.2": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-device/-/auth-oauth-device-8.0.2.tgz#2ba6a0569e12da8efe4420af2b6b33a857f62532" - integrity sha512-KW7Ywrz7ei7JX+uClWD2DN1259fnkoKuVdhzfpQ3/GdETaCj4Tx0IjvuJrwhP/04OhcMu5yR6tjni0V6LBihdw== +"@octokit/auth-oauth-device@^8.0.3": + version "8.0.3" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-device/-/auth-oauth-device-8.0.3.tgz#d4b6a5d99c9c2365be1c11702d70668035a976be" + integrity sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw== dependencies: - "@octokit/oauth-methods" "^6.0.1" - "@octokit/request" "^10.0.5" - "@octokit/types" "^15.0.0" + "@octokit/oauth-methods" "^6.0.2" + "@octokit/request" "^10.0.6" + "@octokit/types" "^16.0.0" universal-user-agent "^7.0.0" -"@octokit/auth-oauth-user@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-user/-/auth-oauth-user-6.0.1.tgz#fbd838112e5cec0329a8ebe16fe983d065aa187d" - integrity sha512-vlKsL1KUUPvwXpv574zvmRd+/4JiDFXABIZNM39+S+5j2kODzGgjk7w5WtiQ1x24kRKNaE7v9DShNbw43UA3Hw== +"@octokit/auth-oauth-user@^6.0.1", "@octokit/auth-oauth-user@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-user/-/auth-oauth-user-6.0.2.tgz#7ec9d2102a7680f2af0ebe2a96d8ad08a1634513" + integrity sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A== dependencies: - "@octokit/auth-oauth-device" "^8.0.2" - "@octokit/oauth-methods" "^6.0.1" - "@octokit/request" "^10.0.5" - "@octokit/types" "^15.0.0" + "@octokit/auth-oauth-device" "^8.0.3" + "@octokit/oauth-methods" "^6.0.2" + "@octokit/request" "^10.0.6" + "@octokit/types" "^16.0.0" universal-user-agent "^7.0.0" "@octokit/auth-token@^6.0.0": @@ -539,45 +524,45 @@ resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-6.0.0.tgz#b02e9c08a2d8937df09a2a981f226ad219174c53" integrity sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w== -"@octokit/auth-unauthenticated@^7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@octokit/auth-unauthenticated/-/auth-unauthenticated-7.0.2.tgz#3b53e1a93589ca41302fad53c33879a3c5d791c8" - integrity sha512-vjcPRP1xsKWdYKiyKmHkLFCxeH4QvVTv05VJlZxwNToslBFcHRJlsWRaoI2+2JGCf9tIM99x8cN0b1rlAHJiQw== +"@octokit/auth-unauthenticated@^7.0.2", "@octokit/auth-unauthenticated@^7.0.3": + version "7.0.3" + resolved "https://registry.yarnpkg.com/@octokit/auth-unauthenticated/-/auth-unauthenticated-7.0.3.tgz#48a469dcb6676f152fb06d24049f02de87e23993" + integrity sha512-8Jb1mtUdmBHL7lGmop9mU9ArMRUTRhg8vp0T1VtZ4yd9vEm3zcLwmjQkhNEduKawOOORie61xhtYIhTDN+ZQ3g== dependencies: - "@octokit/request-error" "^7.0.1" - "@octokit/types" "^15.0.0" + "@octokit/request-error" "^7.0.2" + "@octokit/types" "^16.0.0" -"@octokit/core@^7.0.5": - version "7.0.5" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-7.0.5.tgz#2611ed0a0b99a278f67e4ffdfedaea4577ca3241" - integrity sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q== +"@octokit/core@^7.0.5", "@octokit/core@^7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-7.0.6.tgz#0d58704391c6b681dec1117240ea4d2a98ac3916" + integrity sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q== dependencies: "@octokit/auth-token" "^6.0.0" - "@octokit/graphql" "^9.0.2" - "@octokit/request" "^10.0.4" - "@octokit/request-error" "^7.0.1" - "@octokit/types" "^15.0.0" + "@octokit/graphql" "^9.0.3" + "@octokit/request" "^10.0.6" + "@octokit/request-error" "^7.0.2" + "@octokit/types" "^16.0.0" before-after-hook "^4.0.0" universal-user-agent "^7.0.0" -"@octokit/endpoint@^11.0.1": - version "11.0.1" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-11.0.1.tgz#16c510afbe873174f2091b747abe3c21769e6b55" - integrity sha512-7P1dRAZxuWAOPI7kXfio88trNi/MegQ0IJD3vfgC3b+LZo1Qe6gRJc2v0mz2USWWJOKrB2h5spXCzGbw+fAdqA== +"@octokit/endpoint@^11.0.2": + version "11.0.2" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-11.0.2.tgz#a8d955e053a244938b81d86cd73efd2dcb5ef5af" + integrity sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ== dependencies: - "@octokit/types" "^15.0.0" + "@octokit/types" "^16.0.0" universal-user-agent "^7.0.2" -"@octokit/graphql@^9.0.2": - version "9.0.2" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-9.0.2.tgz#910a626d1e11c385357d1579cc1840f1725aa6ef" - integrity sha512-iz6KzZ7u95Fzy9Nt2L8cG88lGRMr/qy1Q36ih/XVzMIlPDMYwaNLE/ENhqmIzgPrlNWiYJkwmveEetvxAgFBJw== +"@octokit/graphql@^9.0.3": + version "9.0.3" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-9.0.3.tgz#5b8341c225909e924b466705c13477face869456" + integrity sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA== dependencies: - "@octokit/request" "^10.0.4" - "@octokit/types" "^15.0.0" + "@octokit/request" "^10.0.6" + "@octokit/types" "^16.0.0" universal-user-agent "^7.0.0" -"@octokit/oauth-app@^8.0.2": +"@octokit/oauth-app@^8.0.3": version "8.0.3" resolved "https://registry.yarnpkg.com/@octokit/oauth-app/-/oauth-app-8.0.3.tgz#56172be3be768f9c38f12a63e5ef8d6644fd7efb" integrity sha512-jnAjvTsPepyUaMu9e69hYBuozEPgYqP4Z3UnpmvoIzHDpf8EXDGvTY1l1jK0RsZ194oRd+k6Hm13oRU8EoDFwg== @@ -596,20 +581,20 @@ resolved "https://registry.yarnpkg.com/@octokit/oauth-authorization-url/-/oauth-authorization-url-8.0.0.tgz#fdbab39a07d38faaad8621a5fdf04bc0c36d63e7" integrity sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ== -"@octokit/oauth-methods@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@octokit/oauth-methods/-/oauth-methods-6.0.1.tgz#326c0fbb89531b5495a3f218e7084f295fff40f4" - integrity sha512-xi6Iut3izMCFzXBJtxxJehxJmAKjE8iwj6L5+raPRwlTNKAbOOBJX7/Z8AF5apD4aXvc2skwIdOnC+CQ4QuA8Q== +"@octokit/oauth-methods@^6.0.1", "@octokit/oauth-methods@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@octokit/oauth-methods/-/oauth-methods-6.0.2.tgz#0c3da61244040cd2e9075d5949b5deed3fa6e52c" + integrity sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng== dependencies: "@octokit/oauth-authorization-url" "^8.0.0" - "@octokit/request" "^10.0.5" - "@octokit/request-error" "^7.0.1" - "@octokit/types" "^15.0.0" + "@octokit/request" "^10.0.6" + "@octokit/request-error" "^7.0.2" + "@octokit/types" "^16.0.0" -"@octokit/openapi-types@^26.0.0": - version "26.0.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-26.0.0.tgz#a528b560dbc4f02040dc08d19575498d57fff19d" - integrity sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA== +"@octokit/openapi-types@^27.0.0": + version "27.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-27.0.0.tgz#374ea53781965fd02a9d36cacb97e152cefff12d" + integrity sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA== "@octokit/openapi-webhooks-types@12.0.3": version "12.0.3" @@ -621,61 +606,61 @@ resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-6.0.0.tgz#acdefd7e85ce24716e7ad7352f2df4d29d0e273b" integrity sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ== -"@octokit/plugin-paginate-rest@^13.2.0": - version "13.2.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.2.1.tgz#7ad36e7732a391e5ce9ce4e23fa8023127a0f162" - integrity sha512-Tj4PkZyIL6eBMYcG/76QGsedF0+dWVeLhYprTmuFVVxzDW7PQh23tM0TP0z+1MvSkxB29YFZwnUX+cXfTiSdyw== +"@octokit/plugin-paginate-rest@^14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz#44dc9fff2dacb148d4c5c788b573ddc044503026" + integrity sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw== dependencies: - "@octokit/types" "^15.0.1" + "@octokit/types" "^16.0.0" -"@octokit/plugin-rest-endpoint-methods@^16.1.0": - version "16.1.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-16.1.1.tgz#cf5e901a06c411a879e880499db8152c295b6645" - integrity sha512-VztDkhM0ketQYSh5Im3IcKWFZl7VIrrsCaHbDINkdYeiiAsJzjhS2xRFCSJgfN6VOcsoW4laMtsmf3HcNqIimg== +"@octokit/plugin-rest-endpoint-methods@^17.0.0": + version "17.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz#8c54397d3a4060356a1c8a974191ebf945924105" + integrity sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw== dependencies: - "@octokit/types" "^15.0.1" + "@octokit/types" "^16.0.0" -"@octokit/plugin-retry@^8.0.2": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-8.0.2.tgz#9cb012d9e8f8ea2771052fafa0c309c56df4027a" - integrity sha512-mVPCe77iaD8g1lIX46n9bHPUirFLzc3BfIzsZOpB7bcQh1ecS63YsAgcsyMGqvGa2ARQWKEFTrhMJX2MLJVHVw== +"@octokit/plugin-retry@^8.0.3": + version "8.0.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-8.0.3.tgz#8b7af9700272df724d12fd6333ead98961d135c6" + integrity sha512-vKGx1i3MC0za53IzYBSBXcrhmd+daQDzuZfYDd52X5S0M2otf3kVZTVP8bLA3EkU0lTvd1WEC2OlNNa4G+dohA== dependencies: - "@octokit/request-error" "^7.0.1" - "@octokit/types" "^15.0.0" + "@octokit/request-error" "^7.0.2" + "@octokit/types" "^16.0.0" bottleneck "^2.15.3" -"@octokit/plugin-throttling@^11.0.2": - version "11.0.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-throttling/-/plugin-throttling-11.0.2.tgz#22727f72630ce966cd163149a55bd56a9e0434fb" - integrity sha512-ntNIig4zZhQVOZF4fG9Wt8QCoz9ehb+xnlUwp74Ic2ANChCk8oKmRwV9zDDCtrvU1aERIOvtng8wsalEX7Jk5Q== +"@octokit/plugin-throttling@^11.0.3": + version "11.0.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-throttling/-/plugin-throttling-11.0.3.tgz#584b1a9ca73a5daafeeb7dd5cc13a1bd29a6a60d" + integrity sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg== dependencies: - "@octokit/types" "^15.0.0" + "@octokit/types" "^16.0.0" bottleneck "^2.15.3" -"@octokit/request-error@^7.0.0", "@octokit/request-error@^7.0.1": - version "7.0.1" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-7.0.1.tgz#2651faef1ed387583d73d9a38452b216a5c6975d" - integrity sha512-CZpFwV4+1uBrxu7Cw8E5NCXDWFNf18MSY23TdxCBgjw1tXXHvTrZVsXlW8hgFTOLw8RQR1BBrMvYRtuyaijHMA== +"@octokit/request-error@^7.0.0", "@octokit/request-error@^7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-7.0.2.tgz#2455f45385fdec0867fad39ee78dd7461cbd4dac" + integrity sha512-U8piOROoQQUyExw5c6dTkU3GKxts5/ERRThIauNL7yaRoeXW0q/5bgHWT7JfWBw1UyrbK8ERId2wVkcB32n0uQ== dependencies: - "@octokit/types" "^15.0.0" + "@octokit/types" "^16.0.0" -"@octokit/request@^10.0.4", "@octokit/request@^10.0.5": - version "10.0.5" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-10.0.5.tgz#bbe476579db7876af783a10b802bdc37903c2220" - integrity sha512-TXnouHIYLtgDhKo+N6mXATnDBkV05VwbR0TtMWpgTHIoQdRQfCSzmy/LGqR1AbRMbijq/EckC/E3/ZNcU92NaQ== +"@octokit/request@^10.0.6": + version "10.0.6" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-10.0.6.tgz#c12214f86b1e01fb0915c4f3bcbe076433ba975d" + integrity sha512-FO+UgZCUu+pPnZAR+iKdUt64kPE7QW7ciqpldaMXaNzixz5Jld8dJ31LAUewk0cfSRkNSRKyqG438ba9c/qDlQ== dependencies: - "@octokit/endpoint" "^11.0.1" - "@octokit/request-error" "^7.0.1" - "@octokit/types" "^15.0.0" + "@octokit/endpoint" "^11.0.2" + "@octokit/request-error" "^7.0.2" + "@octokit/types" "^16.0.0" fast-content-type-parse "^3.0.0" universal-user-agent "^7.0.2" -"@octokit/types@^15.0.0", "@octokit/types@^15.0.1": - version "15.0.1" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-15.0.1.tgz#1e84e1e9b783b828b9616a113b174d7fec6a5618" - integrity sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q== +"@octokit/types@^16.0.0": + version "16.0.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-16.0.0.tgz#fbd7fa590c2ef22af881b1d79758bfaa234dbb7c" + integrity sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg== dependencies: - "@octokit/openapi-types" "^26.0.0" + "@octokit/openapi-types" "^27.0.0" "@octokit/webhooks-methods@^6.0.0": version "6.0.0" @@ -707,9 +692,9 @@ integrity sha512-uP8nqEGBS58s3iWXx6d8vnJ6ZVt3DACJL4PWADOAuwIS4xXpID91783e9f6zQ0i1ijQpj3yx+3ZuCB2LfQMUMA== "@opentelemetry/api-logs@<1.0.0": - version "0.207.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.207.0.tgz#ae991c51eedda55af037a3e6fc1ebdb12b289f49" - integrity sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ== + version "0.208.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.208.0.tgz#56d3891010a1fa1cf600ba8899ed61b43ace511c" + integrity sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg== dependencies: "@opentelemetry/api" "^1.3.0" @@ -718,92 +703,80 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== -"@opentelemetry/core@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.9.1.tgz#e343337e1a7bf30e9a6aef3ef659b9b76379762a" - integrity sha512-6/qon6tw2I8ZaJnHAQUUn4BqhTbTNRS0WP8/bA0ynaX+Uzp/DDbd0NS0Cq6TMlh8+mrlsyqDE7mO50nmv2Yvlg== - dependencies: - "@opentelemetry/semantic-conventions" "1.9.1" - -"@opentelemetry/core@>=1.14.0 <1.31.0": +"@opentelemetry/core@1.30.1", "@opentelemetry/core@>=1.14.0 <1.31.0": version "1.30.1" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.30.1.tgz#a0b468bb396358df801881709ea38299fc30ab27" integrity sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ== dependencies: "@opentelemetry/semantic-conventions" "1.28.0" -"@opentelemetry/resources@>=1.0.0 <1.10.0": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.9.1.tgz#5ad3d80ba968a3a0e56498ce4bc82a6a01f2682f" - integrity sha512-VqBGbnAfubI+l+yrtYxeLyOoL358JK57btPMJDd3TCOV3mV5TNBmzvOfmesM4NeTyXuGJByd3XvOHvFezLn3rQ== +"@opentelemetry/resources@>=1.0.0 <1.31.0": + version "1.30.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.30.1.tgz#a4eae17ebd96947fdc7a64f931ca4b71e18ce964" + integrity sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA== dependencies: - "@opentelemetry/core" "1.9.1" - "@opentelemetry/semantic-conventions" "1.9.1" + "@opentelemetry/core" "1.30.1" + "@opentelemetry/semantic-conventions" "1.28.0" "@opentelemetry/semantic-conventions@1.28.0": version "1.28.0" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz#337fb2bca0453d0726696e745f50064411f646d6" integrity sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA== -"@opentelemetry/semantic-conventions@1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.9.1.tgz#ad3367684a57879392513479e0a436cb2ac46dad" - integrity sha512-oPQdbFDmZvjXk5ZDoBGXG8B4tSB/qW5vQunJWQMFUBp7Xe8O1ByPANueJ+Jzg58esEBegyyxZ7LRmfJr7kFcFg== - -"@oven/bun-darwin-aarch64@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.3.1.tgz#24f15a0bdec25bb52e726207bfeb84258b22f1ea" - integrity sha512-7Rap1BHNWqgnexc4wLjjdZeVRQKtk534iGuJ7qZ42i/q1B+cxJZ6zSnrFsYmo+zreH7dUyUXL3AHuXGrl2772Q== +"@oven/bun-darwin-aarch64@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.3.2.tgz#8393d7fc40a5df5a6d5ad072b0dc27ab5a9046a3" + integrity sha512-licBDIbbLP5L5/S0+bwtJynso94XD3KyqSP48K59Sq7Mude6C7dR5ZujZm4Ut4BwZqUFfNOfYNMWBU5nlL7t1A== -"@oven/bun-darwin-x64-baseline@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.3.1.tgz#fc2f3cd9caba96be20862e011ca2d11d0cd1d216" - integrity sha512-mJo715WvwEHmJ6khNymWyxi0QrFzU94wolsUmxolViNHrk+2ugzIkVIJhTnxf7pHnarxxHwyJ/kgatuV//QILQ== +"@oven/bun-darwin-x64-baseline@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@oven/bun-darwin-x64-baseline/-/bun-darwin-x64-baseline-1.3.2.tgz#d0b2209213123bbe014d2f0a533649bd37eb03b4" + integrity sha512-UHxdtbyxdtNJUNcXtIrjx3Lmq8ji3KywlXtIHV/0vn9A8W5mulqOcryqUWMFVH9JTIIzmNn6Q/qVmXHTME63Ww== -"@oven/bun-darwin-x64@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@oven/bun-darwin-x64/-/bun-darwin-x64-1.3.1.tgz#c54e0eec263eda889169b4d83ee6b5ec3290f958" - integrity sha512-wpqmgT/8w+tEr5YMGt1u1sEAMRHhyA2SKZddC6GCPasHxSqkCWOPQvYIHIApnTsoSsxhxP0x6Cpe93+4c7hq/w== +"@oven/bun-darwin-x64@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@oven/bun-darwin-x64/-/bun-darwin-x64-1.3.2.tgz#df4814323672dc530e97492241cc5b608d49aab5" + integrity sha512-hn8lLzsYyyh6ULo2E8v2SqtrWOkdQKJwapeVy1rDw7juTTeHY3KDudGWf4mVYteC9riZU6HD88Fn3nGwyX0eIg== -"@oven/bun-linux-aarch64-musl@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@oven/bun-linux-aarch64-musl/-/bun-linux-aarch64-musl-1.3.1.tgz#851b4cefeea2c8219666bec5324bd560304a29d2" - integrity sha512-gKU3Wv3BTG5VMjqMMnRwqU6tipCveE9oyYNt62efy6cQK3Vo1DOBwY2SmjbFw+yzj+Um20YoFOLGxghfQET4Ng== +"@oven/bun-linux-aarch64-musl@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@oven/bun-linux-aarch64-musl/-/bun-linux-aarch64-musl-1.3.2.tgz#34f9f8529079988608aef38c844208fede6f55a1" + integrity sha512-OD9DYkjes7WXieBn4zQZGXWhRVZhIEWMDGCetZ3H4vxIuweZ++iul/CNX5jdpNXaJ17myb1ROMvmRbrqW44j3w== -"@oven/bun-linux-aarch64@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.3.1.tgz#3ee52397da074f40235d2f55016093cb6c98019d" - integrity sha512-ACn038SZL8del+sFnqCjf+haGB02//j2Ez491IMmPTvbv4a/D0iiNz9xiIB3ICbQd3EwQzi+Ut/om3Ba/KoHbQ== +"@oven/bun-linux-aarch64@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.3.2.tgz#be4f3aa719d6eafce20337c19405a42343df410d" + integrity sha512-5uZzxzvHU/z+3cZwN/A0H8G+enQ+9FkeJVZkE2fwK2XhiJZFUGAuWajCpy7GepvOWlqV7VjPaKi2+Qmr4IX7nQ== -"@oven/bun-linux-x64-baseline@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.3.1.tgz#63e3075607640d6df731cc120e43369256e19d4c" - integrity sha512-7+2aCrL81mtltZQbKdiPB58UL+Gr3DAIuPyUAKm0Ib/KG/Z8t7nD/eSMRY/q6b+NsAjYnVPiPwqSjC3edpMmmQ== +"@oven/bun-linux-x64-baseline@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@oven/bun-linux-x64-baseline/-/bun-linux-x64-baseline-1.3.2.tgz#806fee11023dbf77b8a47860d1b969060b8a531a" + integrity sha512-m9Ov9YH8KjRLui87eNtQQFKVnjGsNk3xgbrR9c8d2FS3NfZSxmVjSeBvEsDjzNf1TXLDriHb/NYOlpiMf/QzDg== -"@oven/bun-linux-x64-musl-baseline@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@oven/bun-linux-x64-musl-baseline/-/bun-linux-x64-musl-baseline-1.3.1.tgz#26608cd5f87fdf998dd494da8f3399528b23e406" - integrity sha512-tP0WWcAqrMayvkggOHBGBoyyoK+QHAqgRUyj1F6x5/udiqc9vCXmIt1tlydxYV/NvyvUAmJ7MWT0af44Xm2kJw== +"@oven/bun-linux-x64-musl-baseline@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@oven/bun-linux-x64-musl-baseline/-/bun-linux-x64-musl-baseline-1.3.2.tgz#83eaf4c8067454f4460d4bb36ab23974aabb6eff" + integrity sha512-q8Hto8hcpofPJjvuvjuwyYvhOaAzPw1F5vRUUeOJDmDwZ4lZhANFM0rUwchMzfWUJCD6jg8/EVQ8MiixnZWU0A== -"@oven/bun-linux-x64-musl@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@oven/bun-linux-x64-musl/-/bun-linux-x64-musl-1.3.1.tgz#fd4b6365943db3c6bfe0193ff8dbcf0998b736a0" - integrity sha512-8AgEAHyuJ5Jm9MUo1L53K1SRYu0bNGqV0E0L5rB5DjkteO4GXrnWGBT8qsuwuy7WMuCMY3bj64/pFjlRkZuiXw== +"@oven/bun-linux-x64-musl@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@oven/bun-linux-x64-musl/-/bun-linux-x64-musl-1.3.2.tgz#36ec0e6cd6401011ef25ee7dd7727abdfede9bac" + integrity sha512-3TuOsRVoG8K+soQWRo+Cp5ACpRs6rTFSu5tAqc/6WrqwbNWmqjov/eWJPTgz3gPXnC7uNKVG7RxxAmV8r2EYTQ== -"@oven/bun-linux-x64@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@oven/bun-linux-x64/-/bun-linux-x64-1.3.1.tgz#19e1417abf33ab0c3fde795ae70b2285300794e0" - integrity sha512-cAUeM3I5CIYlu5Ur52eCOGg9yfqibQd4lzt9G1/rA0ajqcnCBaTuekhUDZETJJf5H9QV+Gm46CqQg2DpdJzJsw== +"@oven/bun-linux-x64@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@oven/bun-linux-x64/-/bun-linux-x64-1.3.2.tgz#e6086b3b7005fb70867ce87704bd7ec0f62a169b" + integrity sha512-EoEuRP9bxAxVKuvi6tZ0ZENjueP4lvjz0mKsMzdG0kwg/2apGKiirH1l0RIcdmvfDGGuDmNiv/XBpkoXq1x8ug== -"@oven/bun-windows-x64-baseline@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@oven/bun-windows-x64-baseline/-/bun-windows-x64-baseline-1.3.1.tgz#c95a119f997f4b6332c43355e1a254bd4b31487e" - integrity sha512-dcA+Kj7hGFrY3G8NWyYf3Lj3/GMViknpttWUf5pI6p6RphltZaoDu0lY5Lr71PkMdRZTwL2NnZopa/x/NWCdKA== +"@oven/bun-windows-x64-baseline@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@oven/bun-windows-x64-baseline/-/bun-windows-x64-baseline-1.3.2.tgz#cd2957965cab1eb89bfd8e224039cbdd89aebc00" + integrity sha512-s00T99MjB+xLOWq+t+wVaVBrry+oBOZNiTJijt+bmkp/MJptYS3FGvs7a+nkjLNzoNDoWQcXgKew6AaHES37Bg== -"@oven/bun-windows-x64@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@oven/bun-windows-x64/-/bun-windows-x64-1.3.1.tgz#a64137c3b67f818b5a8a50f2680cd60d3e6ebe7e" - integrity sha512-xdUjOZRq6PwPbbz4/F2QEMLBZwintGp7AS50cWxgkHnyp7Omz5eJfV6/vWtN4qwZIyR3V3DT/2oXsY1+7p3rtg== +"@oven/bun-windows-x64@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@oven/bun-windows-x64/-/bun-windows-x64-1.3.2.tgz#44514d0d46547d071205fcc13a692fda35d3636f" + integrity sha512-nZJUa5NprPYQ4Ii4cMwtP9PzlJJTp1XhxJ+A9eSn1Jfr6YygVWyN2KLjenyI93IcuBouBAaepDAVZZjH2lFBhg== "@pkgjs/parseargs@^0.11.0": version "0.11.0" @@ -890,7 +863,7 @@ "@sinonjs/commons" "^3.0.1" type-detect "^4.1.0" -"@stylistic/eslint-plugin@^5.0.0": +"@stylistic/eslint-plugin@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin/-/eslint-plugin-5.5.0.tgz#44c2e5454ff827f962b1908f0b5939130a6b18b3" integrity sha512-IeZF+8H0ns6prg4VrkhgL+yrvDXWDH2cKchrbh80ejG9dQgZWp10epHMbgRuQvgchLII/lfh6Xn3lu6+6L86Hw== @@ -1217,9 +1190,9 @@ available-typed-arrays@^1.0.7: possible-typed-array-names "^1.0.0" axios@^1.12.2: - version "1.12.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.2.tgz#6c307390136cf7a2278d09cec63b136dfc6e6da7" - integrity sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw== + version "1.13.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.13.2.tgz#9ada120b7b5ab24509553ec3e40123521117f687" + integrity sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA== dependencies: follow-redirects "^1.15.6" form-data "^4.0.4" @@ -1326,22 +1299,22 @@ builtin-modules@^5.0.0: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-5.0.0.tgz#9be95686dedad2e9eed05592b07733db87dcff1a" integrity sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg== -bun@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/bun/-/bun-1.3.1.tgz#bf1cb09802a8b991b6a0dbd8e44cbc6664f2b0bc" - integrity sha512-enqkEb0RhNOgDzHQwv7uvnIhX3uSzmKzz779dL7kdH8SauyTdQvCz4O1UT2rU0UldQp2K9OlrJNdyDHayPEIvw== +bun@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/bun/-/bun-1.3.2.tgz#363afe2eb0d513fa1cc2bee0caf1e61bee769450" + integrity sha512-x75mPJiEfhO1j4Tfc65+PtW6ZyrAB6yTZInydnjDZXF9u9PRAnr6OK3v0Q9dpDl0dxRHkXlYvJ8tteJxc8t4Sw== optionalDependencies: - "@oven/bun-darwin-aarch64" "1.3.1" - "@oven/bun-darwin-x64" "1.3.1" - "@oven/bun-darwin-x64-baseline" "1.3.1" - "@oven/bun-linux-aarch64" "1.3.1" - "@oven/bun-linux-aarch64-musl" "1.3.1" - "@oven/bun-linux-x64" "1.3.1" - "@oven/bun-linux-x64-baseline" "1.3.1" - "@oven/bun-linux-x64-musl" "1.3.1" - "@oven/bun-linux-x64-musl-baseline" "1.3.1" - "@oven/bun-windows-x64" "1.3.1" - "@oven/bun-windows-x64-baseline" "1.3.1" + "@oven/bun-darwin-aarch64" "1.3.2" + "@oven/bun-darwin-x64" "1.3.2" + "@oven/bun-darwin-x64-baseline" "1.3.2" + "@oven/bun-linux-aarch64" "1.3.2" + "@oven/bun-linux-aarch64-musl" "1.3.2" + "@oven/bun-linux-x64" "1.3.2" + "@oven/bun-linux-x64-baseline" "1.3.2" + "@oven/bun-linux-x64-musl" "1.3.2" + "@oven/bun-linux-x64-musl-baseline" "1.3.2" + "@oven/bun-windows-x64" "1.3.2" + "@oven/bun-windows-x64-baseline" "1.3.2" busboy@^1.6.0: version "1.6.0" @@ -1506,7 +1479,7 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -ci-info@^4.3.0: +ci-info@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.3.1.tgz#355ad571920810b5623e11d40232f443f16f1daa" integrity sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA== @@ -1668,7 +1641,7 @@ cookie@^0.7.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== -core-js-compat@^3.44.0: +core-js-compat@^3.46.0: version "3.46.0" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.46.0.tgz#0c87126a19a1af00371e12b02a2b088a40f3c6f7" integrity sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law== @@ -2042,7 +2015,7 @@ eslint-module-utils@^2.12.1: dependencies: debug "^3.2.7" -eslint-plugin-cypress@^5.1.0: +eslint-plugin-cypress@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-5.2.0.tgz#c01cc5511542b5eaa38d31bcdfe888336bf70611" integrity sha512-vuCUBQloUSILxtJrUWV39vNIQPlbg0L7cTunEAzvaUzv9LFZZym+KFLH18n9j2cZuFPdlxOqTubCvg5se0DyGw== @@ -2083,7 +2056,7 @@ eslint-plugin-import@^2.32.0: string.prototype.trimend "^1.0.9" tsconfig-paths "^3.15.0" -eslint-plugin-mocha@^11.1.0: +eslint-plugin-mocha@^11.2.0: version "11.2.0" resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-11.2.0.tgz#e9fe4907f180467c210d1548118d05638a601267" integrity sha512-nMdy3tEXZac8AH5Z/9hwUkSfWu8xHf4XqwB5UEQzyTQGKcNlgFeciRAjLjliIKC3dR1Ex/a2/5sqgQzvYRkkkA== @@ -2091,7 +2064,7 @@ eslint-plugin-mocha@^11.1.0: "@eslint-community/eslint-utils" "^4.4.1" globals "^15.14.0" -eslint-plugin-n@^17.20.0: +eslint-plugin-n@^17.23.1: version "17.23.1" resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-17.23.1.tgz#467db2aba0ead574ea6150143d079d544c11cf48" integrity sha512-68PealUpYoHOBh332JLLD9Sj7OQUDkFpmcfqt8R9sySfFSeuGJjMTJQvCRRB96zO3A/PELRLkPrzsHmzEFQQ5A== @@ -2113,29 +2086,29 @@ eslint-plugin-promise@^7.2.1: dependencies: "@eslint-community/eslint-utils" "^4.4.0" -eslint-plugin-unicorn@^61.0.2: - version "61.0.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-61.0.2.tgz#fe410b1203666cef4d6a5b13b05caef814a6a2e4" - integrity sha512-zLihukvneYT7f74GNbVJXfWIiNQmkc/a9vYBTE4qPkQZswolWNdu+Wsp9sIXno1JOzdn6OUwLPd19ekXVkahRA== +eslint-plugin-unicorn@^62.0.0: + version "62.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-62.0.0.tgz#7027feb2ffde9c25df3d2067e0f4e579a9ec5019" + integrity sha512-HIlIkGLkvf29YEiS/ImuDZQbP12gWyx5i3C6XrRxMvVdqMroCI9qoVYCoIl17ChN+U89pn9sVwLxhIWj5nEc7g== dependencies: - "@babel/helper-validator-identifier" "^7.27.1" - "@eslint-community/eslint-utils" "^4.7.0" - "@eslint/plugin-kit" "^0.3.3" + "@babel/helper-validator-identifier" "^7.28.5" + "@eslint-community/eslint-utils" "^4.9.0" + "@eslint/plugin-kit" "^0.4.0" change-case "^5.4.4" - ci-info "^4.3.0" + ci-info "^4.3.1" clean-regexp "^1.0.0" - core-js-compat "^3.44.0" + core-js-compat "^3.46.0" esquery "^1.6.0" find-up-simple "^1.0.1" - globals "^16.3.0" + globals "^16.4.0" indent-string "^5.0.0" is-builtin-module "^5.0.0" jsesc "^3.1.0" pluralize "^8.0.0" regexp-tree "^0.1.27" - regjsparser "^0.12.0" - semver "^7.7.2" - strip-indent "^4.0.0" + regjsparser "^0.13.0" + semver "^7.7.3" + strip-indent "^4.1.1" eslint-scope@^8.4.0: version "8.4.0" @@ -2155,19 +2128,19 @@ eslint-visitor-keys@^4.2.1: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== -eslint@^9.29.0: - version "9.38.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.38.0.tgz#3957d2af804e5cf6cc503c618f60acc71acb2e7e" - integrity sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw== +eslint@^9.39.0: + version "9.39.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.1.tgz#be8bf7c6de77dcc4252b5a8dcb31c2efff74a6e5" + integrity sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g== dependencies: "@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/regexpp" "^4.12.1" "@eslint/config-array" "^0.21.1" - "@eslint/config-helpers" "^0.4.1" - "@eslint/core" "^0.16.0" + "@eslint/config-helpers" "^0.4.2" + "@eslint/core" "^0.17.0" "@eslint/eslintrc" "^3.3.1" - "@eslint/js" "9.38.0" - "@eslint/plugin-kit" "^0.4.0" + "@eslint/js" "9.39.1" + "@eslint/plugin-kit" "^0.4.1" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" "@humanwhocodes/retry" "^0.4.2" @@ -2597,10 +2570,10 @@ globals@^15.11.0, globals@^15.14.0: resolved "https://registry.yarnpkg.com/globals/-/globals-15.15.0.tgz#7c4761299d41c32b075715a4ce1ede7897ff72a8" integrity sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg== -globals@^16.2.0, globals@^16.3.0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-16.4.0.tgz#574bc7e72993d40cf27cf6c241f324ee77808e51" - integrity sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw== +globals@^16.2.0, globals@^16.3.0, globals@^16.4.0: + version "16.5.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-16.5.0.tgz#ccf1594a437b97653b2be13ed4d8f5c9f850cac1" + integrity sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ== globalthis@^1.0.4: version "1.0.4" @@ -2626,9 +2599,9 @@ graceful-fs@^4.1.15, graceful-fs@^4.2.4: integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== graphql@*: - version "16.11.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.11.0.tgz#96d17f66370678027fdf59b2d4c20b4efaa8a633" - integrity sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw== + version "16.12.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.12.0.tgz#28cc2462435b1ac3fdc6976d030cef83a0c13ac7" + integrity sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ== has-bigints@^1.0.2: version "1.1.0" @@ -3197,16 +3170,11 @@ jsep@^1.4.0: resolved "https://registry.yarnpkg.com/jsep/-/jsep-1.4.0.tgz#19feccbfa51d8a79f72480b4b8e40ce2e17152f0" integrity sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw== -jsesc@^3.0.2, jsesc@^3.1.0: +jsesc@^3.0.2, jsesc@^3.1.0, jsesc@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== -jsesc@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" - integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== - json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -3527,9 +3495,9 @@ mocha-multi-reporters@^1.5.1: lodash "^4.17.15" mocha@^11.6.0: - version "11.7.4" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.4.tgz#f161b17aeccb0762484b33bdb3f7ab9410ba5c82" - integrity sha512-1jYAaY8x0kAZ0XszLWu14pzsf4KV740Gld4HXkhNTXwcHx4AUEDkPzgEHg9CM5dVcW+zv036tjpsEbLraPJj4w== + version "11.7.5" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.5.tgz#58f5bbfa5e0211ce7e5ee6128107cefc2515a627" + integrity sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig== dependencies: browser-stdout "^1.3.1" chokidar "^4.0.1" @@ -3729,20 +3697,20 @@ object.values@^1.2.1: es-object-atoms "^1.0.0" octokit@^5.0.3: - version "5.0.4" - resolved "https://registry.yarnpkg.com/octokit/-/octokit-5.0.4.tgz#09efcb0c3247f8587237c14c49fcd395da3a5d0f" - integrity sha512-4n/mMoLQs2npBE+aTG5o4H+hZhFKu8aDqZFP/nmUNRUYrTpXpaqvX1ppK5eiCtQ+uP/8jI6vbdfCB2udlBgccA== + version "5.0.5" + resolved "https://registry.yarnpkg.com/octokit/-/octokit-5.0.5.tgz#8122c8db5c818381839ccc1980295abd9909548f" + integrity sha512-4+/OFSqOjoyULo7eN7EA97DE0Xydj/PW5aIckxqQIoFjFwqXKuFCvXUJObyJfBF9Khu4RL/jlDRI9FPaMGfPnw== dependencies: - "@octokit/app" "^16.1.1" - "@octokit/core" "^7.0.5" - "@octokit/oauth-app" "^8.0.2" + "@octokit/app" "^16.1.2" + "@octokit/core" "^7.0.6" + "@octokit/oauth-app" "^8.0.3" "@octokit/plugin-paginate-graphql" "^6.0.0" - "@octokit/plugin-paginate-rest" "^13.2.0" - "@octokit/plugin-rest-endpoint-methods" "^16.1.0" - "@octokit/plugin-retry" "^8.0.2" - "@octokit/plugin-throttling" "^11.0.2" - "@octokit/request-error" "^7.0.1" - "@octokit/types" "^15.0.0" + "@octokit/plugin-paginate-rest" "^14.0.0" + "@octokit/plugin-rest-endpoint-methods" "^17.0.0" + "@octokit/plugin-retry" "^8.0.3" + "@octokit/plugin-throttling" "^11.0.3" + "@octokit/request-error" "^7.0.2" + "@octokit/types" "^16.0.0" "@octokit/webhooks" "^14.0.0" on-finished@^2.4.1: @@ -4169,12 +4137,12 @@ regexp.prototype.flags@^1.5.4: gopd "^1.2.0" set-function-name "^2.0.2" -regjsparser@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.12.0.tgz#0e846df6c6530586429377de56e0475583b088dc" - integrity sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ== +regjsparser@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.13.0.tgz#01f8351335cf7898d43686bc74d2dd71c847ecc0" + integrity sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q== dependencies: - jsesc "~3.0.2" + jsesc "~3.1.0" release-zalgo@^1.0.0: version "1.0.0" @@ -4319,7 +4287,7 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3, semver@^7.7.2: +semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3, semver@^7.7.2, semver@^7.7.3: version "7.7.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== @@ -4670,7 +4638,7 @@ strip-bom@^4.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== -strip-indent@^4.0.0: +strip-indent@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-4.1.1.tgz#aba13de189d4ad9a17f6050e76554ac27585c7af" integrity sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==