Skip to content

[RUM-18236] Improve api for upload of source maps by debug ID - #502

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 7 commits into
masterfrom
hugo.silva/rum-18236-debug-id-upload
Sep 10, 2026
Merged

[RUM-18236] Improve api for upload of source maps by debug ID#502
gh-worker-dd-mergequeue-cf854d[bot] merged 7 commits into
masterfrom
hugo.silva/rum-18236-debug-id-upload

Conversation

@jhssilva

@jhssilva jhssilva commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

What and why?

Add direct debug-ID source-map uploads to Datadog Build Plugins. A build can now inject debug IDs and upload the corresponding source maps without configuring a service, release version, or minified path prefix.

The existing nested debug-ID and service/version workflows remain backward compatible.

How to use it

For Build Plugins to inject debug IDs and upload the source maps:

datadogWebpackPlugin({
  auth: {
    apiKey: process.env.DATADOG_API_KEY,
    site: 'datadoghq.com',
  },
  sourcemaps: {
    debugId: true,
    upload: true,
  },
});
  • sourcemaps.debugId: true injects a debug ID into each generated JavaScript bundle.
  • sourcemaps.upload: true uploads the corresponding source maps during the build.
  • Omitting upload enables injection without direct upload, for example when datadog-ci performs the upload.
  • auth.apiKey or DATADOG_API_KEY is required only when direct upload is enabled.
  • bailOnError, dryRun, and maxConcurrency can be configured alongside upload: true.

The existing rum.sourceCodeContext and errorTracking.sourcemaps configurations remain supported. They cannot be combined with the new top-level sourcemaps option.

Existing service/version workflow

The legacy configuration continues to work unchanged:

datadogWebpackPlugin({
  auth: {
    apiKey: process.env.DATADOG_API_KEY,
    site: 'datadoghq.com',
  },
  errorTracking: {
    sourcemaps: {
      service: 'my-application',
      releaseVersion: '1.0.0',
      minifiedPathPrefix: 'https://example.com/static/',
    },
  },
  rum: {
    sourceCodeContext: {
      service: 'my-application',
      version: '1.0.0',
    },
  },
});

Implementation

  • Add debug-ID upload support for webpack, Vite, Rollup, esbuild, and Rspack.
  • Add a typed top-level sourcemaps option that distinguishes injection-only and upload-enabled configurations.
  • Normalize the top-level option to the existing RUM injection and Error Tracking upload implementations.
  • Represent upload behavior internally with the typed SourcemapsUploadMode discriminator.
  • Build debug-ID multipart payloads without legacy service/version/path metadata.
  • Require a debug ID for every artifact uploaded in debug-ID mode.
  • Preserve the existing nested debug-ID and service/version behavior.

Validation

  • 77 focused unit and integration tests pass across the factory, RUM, and Error Tracking paths.
  • Package typechecking, formatting, generated documentation/link checks, repository integrity, and open-source compliance pass locally.
  • A real webpack smoke test using sourcemaps: { debugId: true, upload: true } uploaded a source map to the staging intake with bailOnError: true.

@jhssilva
jhssilva marked this pull request as ready for review September 3, 2026 13:49
@jhssilva
jhssilva requested review from a team as code owners September 3, 2026 13:49
@jhssilva
jhssilva requested review from amortemousque and sdkennedy2 and removed request for a team September 3, 2026 13:49
@amortemousque

Copy link
Copy Markdown
Collaborator

💬 suggestion: ‏Nice! Only comment about the public API. I’m not sure repeating debugId is ideal, consider a customer using both plugins:

datadogWebpackPlugin({
  auth: { apiKey: process.env.DATADOG_API_KEY, site: 'datadoghq.com' },
  errorTracking: {
    sourcemaps: {
      debugId: true
    },
  },
  rum: {
    sourceCodeContext: {
      debugId: true
    },
  },
});

Could we instead expose something like this?

{
  auth: { apiKey: process.env.DATADOG_API_KEY, site: 'datadoghq.com' },
  sourcemaps: {
   debugId: true,
   upload: true
 }
}

@jhssilva jhssilva changed the title [RUM-18236] Upload source maps by debug ID [RUM-18236] Improve api for upload of source maps by debug ID Sep 7, 2026

@buranmert buranmert left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i like the idea but i'm wondering how can we make sure RUM and Error Tracking plugins are used together?
(i'm not familiar with how to use build plugins, i assume the user picks whichever plugins they want to use)

Comment on lines +42 to +51
export type ServiceVersionSourcemapsOptionsWithDefaults = SourcemapsUploadOptionsWithDefaults &
Required<
Pick<ServiceVersionSourcemapsOptions, 'minifiedPathPrefix' | 'releaseVersion' | 'service'>
> & {
mode: SourcemapsUploadMode.SERVICE_VERSION;
};

export type DebugIdSourcemapsOptionsWithDefaults = SourcemapsUploadOptionsWithDefaults & {
mode: SourcemapsUploadMode.DEBUG_ID;
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 suggestion: ‏Why do we need a new type here? Instead of introducing a mode field, could we use debugId as the discriminant?

12:25 PM

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great suggestion.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 4cbb00d. I removed SourcemapsUploadMode and now use debugId as the discriminant in the normalized types and throughout file selection, payload creation, and upload metrics.

return errors;
};

const normalizeSourcemapsOptions = (options: Options, errors: string[]): Options => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 suggestion: ‏This function re-implements validation that rum/validate.ts and error-tracking/validate.ts already do (apiKey required, service/version conflicts, enable-flag conflicts), same rules, second place to keep in sync. Could we avoid repeating them

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The normalizer is still needed to translate the new top-level sourcemaps option into the existing RUM and Error Tracking plugin configurations. It only validates the top-level shape and cross-plugin contradictions; API-key and service/version/path validation remain in the plugin validators. The explicit enable:false checks must happen here because disabled plugins do not run their validators. Let me know if you would prefer this split to be made more explicit in naming or structure.

// resolves; otherwise an error has been recorded and the caller will
// throw before the config is read.
if (releaseVersion) {
const { debugId: _debugId, ...serviceVersionOptions } = sourcemapsCfg;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ question: ‏Couldn't we directly pass sourcemapsCfg instead of adding this spread?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, done in 4cbb00d. The service/version branch now spreads sourcemapsCfg directly, then sets the normalized debugId:false value and resolved releaseVersion.

// throw before the config is read.
if (releaseVersion) {
if (toReturn.errors.length === 0) {
const { debugId: _debugId, ...uploadOptions } = sourcemapsCfg;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ question: ‏Couldn't we directly pass sourcemapsCfg instead of adding this spread?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, done in 4cbb00d. The debug-ID branch now spreads sourcemapsCfg directly, preserving debugId:true.

Comment on lines +10 to 20
type DebugIdSourceCodeContextOptions = {
debugId: true;
service?: never;
version?: never;
};

type ServiceVersionSourceCodeContextOptions = {
debugId?: false;
service: string;
version?: string;
debugId?: boolean;
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔨 warning: This assumption is incorrect. A customer can provide source code context with both a debugId and service/version. The service and version identify and allow to filter events from a specific micro-frontend; they are independent of the unminification.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4cbb00d. The debug-ID source-code-context variant now accepts optional service and version, validation preserves them, and the injected context serializes service, version, and ddDebugId together. The legacy service/version variant remains unchanged.

@jhssilva

jhssilva commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

i like the idea but i'm wondering how can we make sure RUM and Error Tracking plugins are used together? (i'm not familiar with how to use build plugins, i assume the user picks whichever plugins they want to use)

@buranmert @Aymeric replied:

All Datadog plugins are bundled into the same @datadog/webpack-plugin package, so Error Tracking uploads and RUM Debug ID injection can use the same sourcemaps configuration.


// Compute deterministic debug IDs whenever possible to prevent the backend from storing
// duplicate source maps for identical builds.
const debugId =

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥜 nitpick: Nested ternaries are difficult to read. Could we revert to the original code if the behavior is unchanged?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, the behavior is unchanged. I've reverted this to the original variable declaration and if-statement form to avoid the nested ternary. Thanks!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

errors.push(`Sourcemap file not found: ${sourcemap.sourcemapFilePath}`);
}
if (debugIdRequired && !debugId) {
errors.push(`No debug ID found in minified file: ${sourcemap.minifiedFilePath}`);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 suggestion: ‏I don’t think we should abort all uploads when a single file is missing a debug ID. To remain consistent with datadog-ci upload: if all debug IDs are missing, fail with exit code 1; if only some are missing, skip those files and upload the rest.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed 035b29f

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not fully fan with the validation flow in this file, but it could be improve in followup PRs

@jhssilva

jhssilva commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

/code blockers

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented Sep 9, 2026

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-09-09 12:26:32 UTC ℹ️ Start processing command /code blockers


2026-09-09 12:26:33 UTC ℹ️ Devflow:

Checking merge blockers for #502...


2026-09-09 12:26:36 UTC ℹ️ Devflow: /code blockers

Detected 1 merge blocker(s) to address:

🟠 Pending

  • Merge gate rule reviewers-approval is running: 1 approval missing
    All required reviewers must approve this pull request before it can be merged. Learn more in our FAQ.

    Hint: Questions about this check? Reach out in #dx-source-code-management on Slack.

@nchapma2 nchapma2 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving for build-plugins. I don't really know this code at all and in the plugin files, it seems like we could do some cleaning up if debug-ids are the identifier going forward

@jhssilva

Copy link
Copy Markdown
Contributor Author

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented Sep 10, 2026

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-09-10 16:41:15 UTC ℹ️ Start processing command /merge


2026-09-10 16:41:20 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in master is approximately 2m (p90).


2026-09-10 16:42:43 UTC ℹ️ MergeQueue: This merge request was merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants