Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
dbb9486
ci(release): fix publish by dropping dist-tag add (#9100)
rochdev Jun 26, 2026
08e3cd0
ci(coverage): merge per-integration coverage in All Green before uplo…
BridgeAR Jun 26, 2026
36bf24b
test(kafkajs): stop pinning the produce offset in the sendBatch test …
BridgeAR Jun 26, 2026
bea040a
test: key plugin version folders by single-digit major (#9052)
BridgeAR Jun 26, 2026
20df728
ci(release): fix branch-diff Infinity crash on scientific notation SH…
rochdev Jun 26, 2026
316656f
refactor(config): introduce namespace field and start pruning interna…
BridgeAR Jun 26, 2026
4eda51c
perf(loader): match instrumented modules with a shouldInclude predica…
BridgeAR Jun 26, 2026
4719ff7
test(llmobs): use DD_LLMOBS_ENABLED in tagger sampling fixtures (#9107)
BridgeAR Jun 26, 2026
f00a5a3
refactor(graphql): migrate shimmer to orchestrion instrumentation (#7…
crysmags Jun 27, 2026
9e42bfd
chore(deps): bump openai (#9122)
dependabot[bot] Jun 29, 2026
694a235
chore(deps): bump the cloud-and-messaging group across 1 directory wi…
dependabot[bot] Jun 29, 2026
87a031b
chore(deps): bump the test-versions group across 1 directory with 4 u…
dependabot[bot] Jun 29, 2026
86d12e5
test(test-optimization): handle 5xx retry in getKnownTests error test…
BridgeAR Jun 29, 2026
f0e0a55
fix(cypress): handle missing beforeEach task result (#9129)
juan-fernandez Jun 29, 2026
88140de
refactor(config): derive supported config paths from canonical names …
BridgeAR Jun 29, 2026
772ef03
feat(test optimization) Add Test Optimization HTTP cache reader (#8860)
cbasitodx Jun 29, 2026
05c8518
test(langchain): bump tested langchain versions with new cassette (#9…
sabrenner Jun 29, 2026
cedff94
fix(iast): scope the mongodb nosql-analysis marker per query (#9076)
BridgeAR Jun 29, 2026
c6a6201
fix(graphql)!: decouple resolver depth from path collapsing (#8774)
BridgeAR Jun 29, 2026
14496f3
feat(aap): In App WAF support for lambda (#7783)
CarlesDD Jun 30, 2026
d388969
Add runtime family tag on appsec lambda span (#9153)
CarlesDD Jun 30, 2026
de025c0
chore: drop dead profile assert helper and fix telemetry typo (#7686)
BridgeAR Jun 30, 2026
c7e2039
ci(scripts): disable V8 Maglev for Windows test children (#9131)
BridgeAR Jun 30, 2026
d443aa9
fix(instrumentation): wrap lazily defined fs.opendir on Node 20 (#9094)
BridgeAR Jun 30, 2026
82fb379
test(kafkajs): drop retries:0 from header-disable producer tests (#9106)
BridgeAR Jun 30, 2026
d14a9b5
test(runtime-metrics): widen GC pause p95 bound to deflake (#9121)
BridgeAR Jun 30, 2026
c34cc96
ci(engines): widen engines.node to >=18 in CI to keep Node 18/20 jobs…
pabloerhard Jun 30, 2026
e191727
test(child_process): load the mock agent once per suite (#9113)
BridgeAR Jun 30, 2026
b0d87d4
feat: [SVLS-9168] add aws.durable.operation_attempt tag to durable op…
lym953 Jun 30, 2026
9209369
chore(release): add v6 into release workflows (#9103)
pabloerhard Jun 30, 2026
0c829b7
chore(deps): bump bullmq (#9164)
dependabot[bot] Jul 1, 2026
e72c591
chore(deps): bump the gh-actions-packages group across 3 directories …
dependabot[bot] Jul 1, 2026
37da147
fix(vitest): route thread workers through main process (#9169)
juan-fernandez Jul 1, 2026
29b6503
refactor(vitest): split main and worker instrumentation (#9170)
juan-fernandez Jul 1, 2026
0119906
refactor(vitest): prepare test metadata in main (#9171)
juan-fernandez Jul 1, 2026
fe8406e
fix(aws-durable-execution-sdk-js): treat FAILED checkpoints as replay…
lym953 Jul 1, 2026
244a608
chore(deps): bump the vendor-minor-and-patch-dependencies group acros…
dependabot[bot] Jul 1, 2026
d1e4732
test(oracledb): bound connect and query timeouts in the ESM integrati…
BridgeAR Jul 1, 2026
6dce963
test(debugger): cover breakpoint error paths (#8996)
watson Jul 2, 2026
4d3a0d0
chore(deps-dev): bump the dev-minor-and-patch-dependencies group acro…
dependabot[bot] Jul 2, 2026
6ce8dfe
docs(agents): clarify internal import ordering rule (#9172)
CarlesDD Jul 2, 2026
50159da
fix(vitest): report typecheck tests (#9176)
juan-fernandez Jul 2, 2026
cc45d35
feat(test-optimization): add vitest no-worker init mode (#9173)
juan-fernandez Jul 2, 2026
2c5b49a
perf(http): load the GCP pubsub push plugin lazily (#9178)
BridgeAR Jul 2, 2026
a7d8e2d
test(child_process): fix Bluebird flake that cascades to every later …
BridgeAR Jul 2, 2026
65074a9
feat(opentelemetry): OTLP trace metrics support (#8206)
ida613 Jul 2, 2026
1a71d21
fix(datastreams): flush on write when flushInterval is 0 (#9120)
BridgeAR Jul 2, 2026
f9ed095
feat(release): add breaking changes to release proposal (#9196)
pabloerhard Jul 2, 2026
59e4520
docs(v6): updated documentation for v6 (#9159)
pabloerhard Jul 2, 2026
b9822a7
v5.111.0
pabloerhard Jul 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions .agents/skills/apm-integrations/references/orchestrion.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,95 @@ For integrations wrapping multiple methods, create a separate plugin class per m
- `ctx.error` — thrown error (on error)
- `ctx.currentStore` — set by `startSpan` in `bindStart`

## Propagating Synchronous Errors From `bindStart`

Subscribers on the prefix `:start` channel and `bindStore` transforms **cannot
propagate a synchronous throw** to the caller of an orchestrion-wrapped
function. Both are wrapped in `try { ... } catch (err) { process.nextTick(() =>
triggerUncaughtException(err)); ... }` by Node's `diagnostics_channel` (see
`lib/diagnostics_channel.js`'s `wrapStoreRun` and `publish`). The error
surfaces async as an uncaught exception, **after** the wrapped fn has already
run and the call has returned normally.

The wrapper's own catch block, however, **does** rethrow:

```js
return ch.start.runStores(__apm$ctx, () => {
try {
const result = __apm$traced();
__apm$ctx.result = result;
return result;
} catch (err) {
__apm$ctx.error = err;
ch.error.publish(__apm$ctx);
throw err; // <- propagates the wrapped fn's error to the caller
} finally {
ch.end.publish(__apm$ctx);
}
});
```

When a contract requires `assert.throws(() => wrapped(...))`-style synchronous
propagation from a `:start` observer (the canonical case is AppSec WAF's
`abortController.abort()` model), use the **Proxy-on-arguments pattern**:

```js
bindStart (ctx) {
// ... normal setup, span creation ...
const abortController = new AbortController()
if (startCh.hasSubscribers) {
startCh.publish({ abortController, args }) // subscribers run sync
if (abortController.signal.aborted) {
// ctx.arguments is the SAME array reference the wrapper spreads into
// the wrapped fn (__apm$wrapped.apply(this, ctx.arguments)). Replace
// arguments[0] with a Proxy whose getters throw AbortError. The
// wrapped fn's first property access (typically a destructure of args)
// triggers the trap → the wrapper's catch+rethrow propagates AbortError
// to the caller. Span lifecycle still completes via end.publish.
ctx.arguments[0] = new Proxy({}, {
get () { throw new AbortError('Aborted') },
has () { throw new AbortError('Aborted') },
})
ctx.ddAborted = true
return ctx.currentStore
}
}
// ... rest of bindStart ...
}

error (ctx) {
if (ctx.ddAborted) return // abort != error tag
// ... regular error handling ...
}
```

Reference implementation: `packages/datadog-plugin-graphql/src/execute.js`
(`apm:graphql:execute:start` contract).

Why this works:
- `ctx.arguments` and the rewriter's `__apm$arguments` are the same array
reference (confirmed by capturing the rewriter output: `const __apm$traced
= () => __apm$wrapped.apply(this, __apm$arguments)`).
- The wrapped fn's body almost always touches `arguments[0]` on the first
statement (destructure, property read, validation). Any read triggers the
Proxy trap.
- The orchestrion wrapper's `catch { ...; throw err }` propagates the thrown
error synchronously to the caller — confirmed in the rewriter template
(vendor's `code-transformer/index.js`, `wrapSync` function).
- `:end` still fires in `finally`, so the plugin's `end(ctx)` runs as usual
and the span lifecycle completes cleanly. Combined with an `error(ctx)`
that no-ops when `ctx.ddAborted`, the span finishes with `error === 0`
(matches the abort contract).

When NOT to use this:
- If you control the wrapped fn (e.g., it's a method you can wrap with
shimmer on top of orchestrion), do that — clearer and avoids the
Proxy indirection.
- If the wrapped fn might catch its own errors before they escape (some
user-resolver patterns do this), the AbortError won't propagate. Verify
the wrapped fn does NOT have a top-level `try/catch` around the
property access that triggers the trap.

## Common Issues

### Wrong filePath
Expand Down
5 changes: 5 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@
/integration-tests/vitest/ @DataDog/ci-app-libraries
/integration-tests/vitest.config.mjs @DataDog/ci-app-libraries
/integration-tests/vitest.second-project.config.mjs @DataDog/ci-app-libraries
/integration-tests/vitest.typecheck.config.mjs @DataDog/ci-app-libraries
/integration-tests/tsconfig.typecheck.json @DataDog/ci-app-libraries
/integration-tests/tsconfig.typecheck-fail.json @DataDog/ci-app-libraries
/integration-tests/tsconfig.typecheck-test-management-fail.json @DataDog/ci-app-libraries
/integration-tests/tsconfig.typecheck-test-management-file-fail.json @DataDog/ci-app-libraries
/integration-tests/ci-visibility-intake.js @DataDog/ci-app-libraries
/integration-tests/ci-visibility-intake.spec.js @DataDog/ci-app-libraries
/integration-tests/CODEOWNERS @DataDog/ci-app-libraries
Expand Down
26 changes: 12 additions & 14 deletions .github/actions/coverage/action.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
name: coverage
description: Verify coverage output and upload it to Codecov and Datadog.
description: >-
Verify a job's coverage output and stash it as an artifact. All Green collects every
`coverage-*` artifact, merges them per integration, and uploads the groups to Codecov and
Datadog — so a commit sends ~100 grouped reports instead of one per matrix cell, which kept
Codecov from silently dropping uploads past its per-commit ceiling.

inputs:
flags:
description: "Codecov flags value"
description: "Coverage flags — the grouping key All Green folds matrix cells by"
required: false
report-dir:
description: "Coverage report directory (defaults to 'coverage')"
Expand All @@ -12,19 +16,13 @@ inputs:
runs:
using: composite
steps:
# Retry once on failure to work around transient issues (e.g. flaky
# Codecov upload network calls).
- id: attempt
uses: ./.github/actions/coverage/upload
continue-on-error: true
with:
flags: ${{ inputs.flags }}
report-dir: ${{ inputs.report-dir }}
- if: steps.attempt.outcome == 'failure'
- name: Verify coverage output
shell: bash
run: sleep 60
- if: steps.attempt.outcome == 'failure'
uses: ./.github/actions/coverage/upload
run: node scripts/verify-coverage.js --flags "${{ inputs.flags }}"

- name: Upload coverage artifact
if: always()
uses: ./.github/actions/upload-coverage-artifact
with:
flags: ${{ inputs.flags }}
report-dir: ${{ inputs.report-dir }}
51 changes: 0 additions & 51 deletions .github/actions/coverage/upload/action.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/actions/install/branch-diff/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ inputs:
runs:
using: composite
steps:
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.npm
key: ${{ github.workflow }}-branch-diff-3.1.1
Expand Down
22 changes: 20 additions & 2 deletions .github/actions/node/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ runs:
}
case "$VERSION" in
eol) version=16 ;;
oldest) oldest_major=$(awk '/"node"/{match($0,/[0-9]+/);print substr($0,RSTART,RLENGTH);exit}' package.json)
version=$(node_version "$oldest_major") ;;
# Pinned to 18 rather than derived from package.json engines.node: CI keeps testing the
# oldest version we still exercise (18) even though the shipped engines floor is now >=22.
# See the Widen engines.node for CI step below, which restores >=18 on the 18/20 legs.
oldest) version=$(node_version 18) ;;
maintenance) version=$(node_version 20) ;;
active) version=$(node_version 24) ;;
latest) version=${LATEST_VERSION:-$(node_version 26)}
Expand All @@ -44,6 +46,22 @@ runs:
node-version: ${{ steps.node-version.outputs.version }}
registry-url: ${{ inputs.registry-url || 'https://registry.npmjs.org' }}

# The shipped package.json pins engines.node to the supported runtime range, but CI keeps
# running the full suite on Node 18/20. Widen the field to >=18 for this checkout so the
# runtime guard and the withVersions test helper don't bail and silently skip those jobs.
# Gate on the major so this never runs on the ancient runtimes the guardrails-unsupported
# job installs (those must keep seeing the shipped >=22 so the guard aborts), and so the
# script is only ever parsed by a Node version new enough to run it.
- name: Widen engines.node for CI
shell: bash
run: |
MAJOR=$(node -e "process.stdout.write(String(parseInt(process.versions.node)))")
if [ "$MAJOR" = "18" ] || [ "$MAJOR" = "20" ]; then
node scripts/ci/widen-engines-for-ci.js
else
echo "Leaving engines.node unchanged on Node $(node -v) (only widened on Node 18/20)."
fi

# Persist the resolved version so subsequent runs within this 60-minute window can reuse it.
- name: Save resolved version
if: steps.node-version-cache.outputs.cache-hit != 'true'
Expand Down
36 changes: 24 additions & 12 deletions .github/actions/upload-coverage-artifact/action.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: Upload Coverage Artifact
description: Upload coverage report as a GitHub Actions artifact for later collection
description: Upload a job's LCOV report as a GitHub Actions artifact for All Green to collect and group.

inputs:
flags:
description: Coverage flags — used as artifact identifier and passed to Datadog on upload
description: Coverage flags — the grouping key All Green folds matrix cells by
required: true
report-dir:
description: Coverage report directory
Expand All @@ -13,26 +13,38 @@ inputs:
runs:
using: composite
steps:
# The reports live at `<report-dir>/node-<version>-<label>/{lcov.info,coverage-final.json}`, one
# level below `report-dir`, so the presence check has to recurse — a top-level-only probe finds
# nothing and silently skips the upload, which is what starved the Datadog coverage upload.
# Both formats ship: Codecov reads branch/function coverage from the istanbul JSON, Datadog from
# the lcov.
- id: check
shell: bash
run: |
count=$(find "${{ inputs.report-dir }}" -maxdepth 1 -type f 2>/dev/null | wc -l | tr -d ' ')
echo "has_files=$([ "$count" -gt 0 ] && echo true || echo false)" >> "$GITHUB_OUTPUT"
- if: steps.check.outputs.has_files == 'true'
count=$(find "${{ inputs.report-dir }}" \( -name lcov.info -o -name coverage-final.json \) -type f 2>/dev/null | wc -l | tr -d ' ')
echo "has_coverage=$([ "$count" -gt 0 ] && echo true || echo false)" >> "$GITHUB_OUTPUT"
# The artifact name carries the grouping flag plus a per-cell suffix (`__<job>-<matrix-index>`)
# so matrix cells that share a flag — cypress varies `spec` outside its flag — upload distinct
# artifacts instead of clobbering each other under one name.
- if: github.actor != 'dependabot[bot]' && steps.check.outputs.has_coverage == 'true'
id: upload
continue-on-error: true
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-${{ inputs.flags }}
path: ${{ inputs.report-dir }}
name: coverage-${{ inputs.flags }}__${{ github.job }}-${{ strategy.job-index }}
path: |
${{ inputs.report-dir }}/**/lcov.info
${{ inputs.report-dir }}/**/coverage-final.json
retention-days: 1
- if: steps.check.outputs.has_files == 'true' && steps.upload.outcome == 'failure'
- if: github.actor != 'dependabot[bot]' && steps.check.outputs.has_coverage == 'true' && steps.upload.outcome == 'failure'
shell: bash
run: sleep 10
- if: steps.check.outputs.has_files == 'true' && steps.upload.outcome == 'failure'
- if: github.actor != 'dependabot[bot]' && steps.check.outputs.has_coverage == 'true' && steps.upload.outcome == 'failure'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-${{ inputs.flags }}
path: ${{ inputs.report-dir }}
overwrite: true
name: coverage-${{ inputs.flags }}__${{ github.job }}-${{ strategy.job-index }}
path: |
${{ inputs.report-dir }}/**/lcov.info
${{ inputs.report-dir }}/**/coverage-final.json
retention-days: 1
overwrite: true
4 changes: 2 additions & 2 deletions .github/chainguard/self.github.release.push-tags.sts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ subject: repo:DataDog/dd-trace-js:environment:npm

claim_pattern:
event_name: (push|workflow_dispatch)
ref: refs/heads/(v[345]\.x|master)
ref: refs/heads/(v[3456]\.x|master)
repository: DataDog/dd-trace-js
job_workflow_ref: DataDog/dd-trace-js/\.github/workflows/release\.yml@refs/heads/(v[345]\.x|master)
job_workflow_ref: DataDog/dd-trace-js/\.github/workflows/release\.yml@refs/heads/(v[3456]\.x|master)

permissions:
contents: write
55 changes: 54 additions & 1 deletion .github/workflows/all-green.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ jobs:
GITHUB_TOKEN: ${{ steps.octo-sts.outputs.token }}
POLLING_INTERVAL: 1
RETRIES: 20
# Sort every downloaded per-cell `coverage-*` artifact into one directory per integration under
# `coverage-upload/<group>/{lcov,json}/`. The reports are not merged here — both backends merge
# same-flag uploads server-side — so each cell's report passes through byte-for-byte. Codecov
# gets the istanbul JSON (carries branch/function coverage), Datadog gets the lcov.
- if: "!cancelled()"
run: node scripts/group-coverage.mjs
- if: "!cancelled()"
uses: ./.github/actions/dd-sts-api-key
id: dd-sts
Expand All @@ -49,7 +55,54 @@ jobs:
run: datadog-ci junit upload --service dd-trace-js-tests --auto-discovery junit-results
env:
DD_API_KEY: ${{ steps.dd-sts.outputs.api_key }}
# One Datadog upload per integration, flagged with the integration name so coverage is
# attributable in the Datadog UI the same way the Codecov flags attribute it. Datadog reads the
# lcov reports; it does not ingest istanbul's JSON.
- if: "!cancelled()"
run: datadog-ci coverage upload coverage-results
run: |
set -o pipefail
shopt -s nullglob
# shellcheck disable=SC2016 # the inner shell must expand "$1", not the template.
printf '%s\0' coverage-upload/*/lcov/ | xargs -0 -r -P 10 -I {} \
bash -c 'datadog-ci coverage upload "$1" --flags "$(basename "$(dirname "$1")")"' _ {}
env:
DD_API_KEY: ${{ steps.dd-sts.outputs.api_key }}
# One Codecov upload per integration via the CLI directly — the codecov-action would re-validate
# and re-download the uploader once per group (~100×). Codecov reads branch/function coverage
# from the istanbul JSON, so the upload points at each group's `json/` subdirectory.
# `master-coverage` is the flag `.codecov.yml` gates `codecov/patch` on; attach it only on PRs
# targeting master so release-branch PRs auto-pass. The SHA/branch/PR are passed explicitly
# because All Green checks out the merge ref, not the head the other workflows reported coverage
# for. No token: the repo uploads tokenless (a Codecov token would have to be an org secret,
# which forks can't read).
- if: "!cancelled()"
run: |
commit_args=(--sha "$HEAD_SHA" --branch "$HEAD_BRANCH")
upload_args=(--sha "$HEAD_SHA")
if [ -n "$PR_NUMBER" ]; then
commit_args+=(--pr "$PR_NUMBER")
upload_args+=(--pr "$PR_NUMBER")
fi
master_flag=()
if [ "$GITHUB_EVENT_NAME" = "pull_request" ] && [ "$BASE_REF" = "master" ]; then
master_flag=(-F master-coverage)
fi
shopt -s nullglob
groups=(coverage-upload/*/json/)
[ ${#groups[@]} -gt 0 ] || exit 0
pip install --quiet codecov-cli
# create-commit and create-report set up the report once and must finish before any upload;
# only the do-upload fan-out is safe to parallelize.
codecovcli create-commit "${commit_args[@]}"
codecovcli create-report --sha "$HEAD_SHA"
set -o pipefail
# shellcheck disable=SC2016 # the inner shell must expand "$@"/"$dir", not the template.
printf '%s\0' "${groups[@]}" | xargs -0 -r -P 10 -I {} \
bash -c 'dir="$1"; shift; codecovcli do-upload "$@" --dir "$dir" -F "$(basename "$(dirname "$dir")")"' \
_ {} "${upload_args[@]}" "${master_flag[@]}"
env:
GITHUB_EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
HEAD_BRANCH: ${{ github.head_ref || github.ref_name }}
PR_NUMBER: ${{ github.event.pull_request.number }}
2 changes: 1 addition & 1 deletion .github/workflows/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- uses: ./.github/actions/node/latest
- name: actionlint
id: actionlint
uses: raven-actions/actionlint@205b530c5d9fa8f44ae9ed59f341a0db994aa6f8 # v2.1.2
uses: raven-actions/actionlint@3d39aea434753780c3b3d4a1a31c854b4dbf49d7 # v2.2.0
with:
matcher: true
fail-on-error: true
Expand Down
Loading
Loading