Skip to content

fix(runtime/npm/debug): support full DEBUG namespace syntax + extend delimiter#554

Open
tsushanth wants to merge 1 commit into
unjs:mainfrom
tsushanth:fix/issue-546-debug-polyfill-patterns
Open

fix(runtime/npm/debug): support full DEBUG namespace syntax + extend delimiter#554
tsushanth wants to merge 1 commit into
unjs:mainfrom
tsushanth:fix/issue-546-debug-polyfill-patterns

Conversation

@tsushanth

@tsushanth tsushanth commented Jun 12, 2026

Copy link
Copy Markdown

Summary

Closes #546.

The debug polyfill at src/runtime/npm/debug.ts enabled a debugger only when DEBUG was exactly * or env.startsWith(namespace). That silently dropped two well-documented features of the upstream debug package:

  • Comma- or whitespace-separated namespace listsDEBUG=worker1,worker2 enabled NEITHER worker1 nor worker2 (only the literal string worker1,worker2 as a prefix).
  • Wildcard patternsDEBUG=worker:* did not match worker:db / worker:queue because the env didn't startsWith the child namespace.

And because startsWith is unanchored, DEBUG=work would also have falsely enabled the worker namespace (no boundary).

Fix

Implement the upstream parsing algorithm. Split DEBUG on , / whitespace, treat a leading - as a negation, glob-escape regex metacharacters, promote * to .*, anchor with ^…$. A namespace is enabled when it matches at least one include pattern and no exclude pattern.

for (const raw of env.split(/[\s,]+/)) {
  if (!raw) continue;
  const negated = raw.startsWith("-");
  const pattern = negated ? raw.slice(1) : raw;
  const escaped = pattern
    .replace(/[\\^$.+?()|[\]{}]/g, String.raw\`\\\$&\`)
    .replace(/\*/g, ".*");
  const re = new RegExp(\`^\${escaped}\$\`);
  (negated ? skips : names).push(re);
}

Compiled patterns are cached in a single slot keyed on the env string so the common case (DEBUG unchanged between log calls) does not reparse. Cache invalidation is === on the string — DEBUG flips rarely at runtime and that's sufficient.

Also fixed extend(ns, delimiter) which previously discarded the delimiter argument and concatenated raw:

-extend: (ns: string, _del?: string) => createDebug(namespace + ns),
+extend: (ns: string, delimiter: string = ":") =>
+  createDebug(\`\${namespace}\${delimiter}\${ns}\`),

So createDebug("app").extend("worker") now yields app:worker (matching upstream) instead of appworker. The delimiter parameter is honoured when supplied.

Tests

New file test/runtime-npm-debug.test.ts. 12 tests covering:

  • *, exact match, env unset (baseline)
  • comma-separated namespaces
  • whitespace-separated namespaces
  • wildcard pattern (worker:*)
  • anchor guard (worker:* does NOT match the bare worker)
  • negation (-worker:internal)
  • prefix-not-substring (DEBUG=work does NOT enable worker)
  • env-change-detection between calls
  • extend default delimiter (:)
  • extend custom delimiter

Verified the new tests fail on main (7/12 fail) and pass on this branch (12/12).

Test plan

  • npx vitest run test/runtime-npm-debug.test.ts — 12/12 pass
  • Verified failure-on-master by stashing the production change and re-running — 7/12 fail, confirming the tests are real regressions and not just covering already-correct behavior
  • npm run lint clean for the touched files (the 5 prettier warnings surfaced are pre-existing in files this PR does not touch)

Out of scope

The full upstream debug API also handles humanize, color picking, and the enable()/disable() runtime control. This PR keeps the existing pass-through stubs for those — they're separately tracked and out of scope for the namespace-matching regression.

Summary by CodeRabbit

  • Improvements

    • Enhanced DEBUG environment variable parsing with improved pattern matching for namespace filtering, including support for wildcards, exclusion patterns, and proper delimiter handling.
  • Tests

    • Added comprehensive test coverage for debug namespace matching behavior.

…delimiter

Closes unjs#546.

The `debug` polyfill at `src/runtime/npm/debug.ts` enabled a debugger
only when DEBUG was exactly `*` or `env.startsWith(namespace)`. That
silently dropped two well-documented features of the upstream `debug`
package:

  - Comma- or whitespace-separated namespace lists: `DEBUG=worker1,worker2`
    enabled NEITHER worker1 nor worker2 — only `worker1,worker2` as a
    literal prefix.
  - Wildcard patterns: `DEBUG=worker:*` did not match `worker:db` /
    `worker:queue` because the env didn't startsWith the child namespace.

And because `startsWith` is unanchored, `DEBUG=work` would also have
falsely enabled the `worker` namespace.

Implement the upstream parsing algorithm: split DEBUG on `,` / whitespace,
treat a leading `-` as a negation, glob-escape regex metacharacters,
promote `*` to `.*`, anchor with `^…$`. A namespace is enabled when it
matches at least one include pattern and no exclude pattern. Compiled
patterns are cached in a single slot keyed on the env string so the
common case (DEBUG unchanged between log calls) does not reparse.

Also fix `extend(ns, delimiter)` which previously discarded the
delimiter argument and concatenated raw: `createDebug("app").extend("worker")`
now yields `app:worker` (matching upstream) instead of `appworker`. The
delimiter parameter is honoured when supplied.

Tests (`test/runtime-npm-debug.test.ts`) cover `*`, exact match, env
unset, comma-separated, whitespace-separated, wildcard, anchor guard
(`worker:*` does not match `worker`), negation, prefix-not-substring
(DEBUG=work does not enable `worker`), env-change-detection, and both
`extend` shapes. Verified 7/12 fail on master, 12/12 pass on the fix.
@tsushanth tsushanth requested a review from pi0 as a code owner June 12, 2026 14:56
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bf026837-4bdb-42dc-b81a-70f6d47c0d38

📥 Commits

Reviewing files that changed from the base of the PR and between f89b7cc and d5e6ec3.

📒 Files selected for processing (2)
  • src/runtime/npm/debug.ts
  • test/runtime-npm-debug.test.ts

📝 Walkthrough

Walkthrough

This PR fixes the debug polyfill to properly parse the DEBUG environment variable with support for wildcard patterns, multiple comma/whitespace-separated namespaces, negation syntax, and cached regex matching. It also updates the namespace extension method to respect configurable delimiters.

Changes

Debug Polyfill Enhancement

Layer / File(s) Summary
DEBUG environment parsing with regex compilation and namespace matching
src/runtime/npm/debug.ts
The DEBUG environment variable is now parsed once and cached into separate regex lists for enabled namespaces and negated patterns. The new isNamespaceEnabled function replaces simple prefix checking with wildcard (*) support, negation via leading -, proper anchor-based matching, and full whitespace/comma-separated list parsing.
Tests for DEBUG namespace matching behavior
test/runtime-npm-debug.test.ts
Test module with harness setup validates exact namespace matches, wildcard patterns with correct anchoring to prevent false prefix matches, comma- and whitespace-separated lists, negation syntax, and confirms debug decisions are re-evaluated when process.env.DEBUG changes between calls.
Namespace extension with configurable delimiter
src/runtime/npm/debug.ts, test/runtime-npm-debug.test.ts
The extend method now accepts an optional delimiter parameter (defaulting to :) when composing child namespaces, with corresponding tests validating both default colon joining and custom delimiter behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A debug that bounces with wildcards and grace,
No more simple prefixes in this brand new space,
With negation and commas, patterns compile with care,
And extend joins namespaces with delimiters to share!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding full DEBUG namespace syntax support and fixing the extend delimiter, matching the PR's core objectives.
Linked Issues check ✅ Passed The PR directly addresses issue #546 by implementing namespace wildcards (e.g., worker:*), comma/whitespace-separated lists, and fixing prefix-match false positives.
Out of Scope Changes check ✅ Passed All changes are scoped to DEBUG namespace parsing and extend delimiter behavior; upstream features like humanize and color picking remain as stubs.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bad debug polyfill

1 participant