npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@adguard/dnr-converter

v1.1.0

Published

A converter that transforms adblock-style filtering rules into rules compatible with the Declarative Net Request (DNR) API.

Downloads

462

Readme

DNR Converter

npm-badge license-badge

A TypeScript library that converts adblock-style filtering rules into rules compatible with Chrome's Declarative Net Request (DNR) API. It is designed for developers building Manifest V3 browser extensions that need to translate existing AdGuard or other adblock filter lists into the DNR format required by modern Chrome extensions.

Key concepts

  • Filter — an adblock filter list represented as an object with an id and text content. Each filter list (e.g. AdGuard Base, EasyList) becomes one Filter input.
  • Declarative rule — a chrome.declarativeNetRequest.Rule object that the browser evaluates natively. The converter produces these from filter text.
  • Source map — a mapping from each generated declarative rule back to its originating filter and rule index, enabling reverse lookup when a declarative rule fires.
  • Rule safety — Chrome classifies DNR rules as safe (block, allow, allowAllRequests, upgradeScheme) or unsafe (redirect, modifyHeaders). The library exposes a helper to check safety.
  • Converter options — limits and paths that control conversion output (maximum rule counts, resource paths for redirects).

Supported rule types

The converter supports virtually all adblock network rule modifiers. The known MV3 limitations (inherent to the Declarative Net Request API) are:

Supported modifiers (with MV3 notes):

  • Basic blocking/allowing rules, $third-party, $domain (no regexps / .* TLDs), all content types ($script, $image, $stylesheet, etc.)
  • $important, $match-case, $method, $to, $denyallow, $header (no regex values), $all
  • $redirect — allowlist rules not supported
  • $csp, $removeparam, $removeheader, $permissions — allowlist rules not supported; rules with identical conditions are combined only within the same filter, not across filters
  • $urltransform — limited; complex transforms produce multiple declarative rules via pipeline stages
  • $cookie — limited; only supported without parameters (bare $cookie)
  • $badfilter — partial: does not handle $domain intersections correctly

Not yet supported / not convertible to DNR:

  • $popup, $redirect-rule, $referrerpolicy — not yet implemented
  • $replace, $jsonprune, $hls, $network, DNS modifiers ($client, $dnsrewrite, $dnstype, $ctag) — not expressible in DNR; produce conversion errors and are skipped
  • Exception modifiers $jsinject, $stealth, $urlblock, $genericblock — not yet implemented
  • $webrtc — deprecated and not supported

For detailed conversion examples with output, see Conversion Examples.

Installation

# pnpm
pnpm install @adguard/dnr-converter

# yarn
yarn add @adguard/dnr-converter

# npm
npm install @adguard/dnr-converter

Peer dependencies

The package requires @adguard/re2-wasm (1.2.0) as a peer dependency for regex validation.

API overview

The library uses a single FilterConverter class with two conversion modes selected by the withSourceMap option:

| | Simple mode (default) | Advanced mode (withSourceMap: true) | | --- | --- | --- | | Converter | FilterConverter | FilterConverter | | Ruleset | Ruleset (sync, in-memory) | RulesetWithSourceMap (lazy-load, source map) | | Input filter | IFilter | IFilter | | Use case | When you only need DeclarativeRule[] output from plain filter text | When you need source maps, $badfilter cross-filter application, and serialization |

IFilter

import type { IFilter } from '@adguard/dnr-converter';

Single interface used by both conversion flows:

| Method | Type | Description | | --- | --- | --- | | getId() | number | Unique filter identifier | | getContent() | Promise<string> | Returns the full text of the filter list | | getRuleByIndex(index) | Promise<string> | Returns original rule text by character offset | | unloadContent() | void | Releases loaded content from memory |

Filter

The single concrete IFilter implementation shipped by the library supports two construction modes:

  • Pre-loaded (new Filter(id, content: string)) — accepts the filter text directly as a string. getContent() resolves immediately; unloadContent() is a no-op. Use for simple conversion or when content is already in memory.

  • Lazy-loaded (new Filter(id, source: () => Promise<string>)) — accepts an async callback that fetches the content on demand. Supports promise deduplication across concurrent callers, and unloadContent() releases the cached content. Use with withSourceMap: true (advanced mode).

import { Filter } from '@adguard/dnr-converter';

// Pre-loaded (simple mode)
const filter = new Filter(1, '||example.com^');

// Lazy-loaded (advanced mode with withSourceMap: true)
const lazyFilter = new Filter(1, async () => fetchFilterText());

ConverterOptions

import type { ConverterOptions } from '@adguard/dnr-converter';

Configuration for the conversion process:

| Property | Type | Description | | --- | --- | --- | | resourcesPath | string? | Path to web-accessible resources relative to the extension root (starts with /, no trailing /). Required for $redirect rules. | | maxNumberOfRules | number? | Maximum total declarative rules to produce. Excess rules are trimmed. | | maxNumberOfUnsafeRules | number? | Maximum unsafe (dynamic) rules allowed. | | maxNumberOfRegexpRules | number? | Maximum rules using regexFilter. | | combine | boolean? | Merge all input filters into a single combined rule set. | | withSourceMap | boolean? | When true, returns RulesetWithSourceMap instead of Ruleset. Enables source maps, $badfilter cross-filter support, and lazy loading. | | badFilterRules | Rule[]? | Static $badfilter rules to apply at scan time (withSourceMap: true only). |

Simple flow: FilterConverter + Ruleset

Use this flow when you only need DeclarativeRule[] output from plain filter text, without source maps or lazy loading.

import { FilterConverter, Filter } from '@adguard/dnr-converter';

const filter = new Filter(1, '||example.com^\n@@||example.com/path^');

const converter = new FilterConverter();
const [{ ruleset, errors, limitations }] = await converter.convert([filter]);

console.log(ruleset.getDeclarativeRules()); // DeclarativeRule[]
console.log(ruleset.getSafeRulesCount());    // 2
console.log(errors.length);                 // 0

Combine multiple filters into one ruleset:

const [{ ruleset }] = await converter.convert(
    [filter1, filter2],
    { combine: true },
);
// ruleset.getId() === FilterConverter.COMBINED_RULESET_ID

Serialize and restore:

const json = ruleset.serialize();   // JSON string of DeclarativeRule[]

const { Ruleset } = await import('@adguard/dnr-converter');
const restored = Ruleset.deserialize(ruleset.getId(), json);

Ruleset (returned by default, without withSourceMap) implements IRuleset:

| Method | Returns | Description | | --- | --- | --- | | getId() | string | Rule set identifier (e.g. "ruleset_1") | | getSafeRulesCount() | number | Count of safe declarative rules (excludes unsafe) | | getUnsafeRulesCount() | number | Count of unsafe rules | | getRegexpRulesCount() | number | Count of regexp-based rules | | getDeclarativeRules() | DeclarativeRule[] | All converted DNR rules (synchronous) | | serialize() | string | JSON serialization of declarative rules | | Ruleset.deserialize(id, json) | Ruleset | Static: reconstruct from serialized JSON |

Advanced flow: FilterConverter with withSourceMap: true + RulesetWithSourceMap

Use this mode in browser extension internals when you need source maps, $badfilter cross-filter application, serialization with hash maps, and lazy content loading. Pass withSourceMap: true in the options to get RulesetWithSourceMap results.

import { Filter, FilterConverter } from '@adguard/dnr-converter';

const filter = new Filter(1, async () => '||example.com^\n@@||example.com/path^');

const converter = new FilterConverter();
const [{ ruleset, errors }] = await converter.convert([filter], { withSourceMap: true });

const declarativeRules = await ruleset.getDeclarativeRules(); // Promise<DeclarativeRule[]>
const sources = await ruleset.getRulesById(declarativeRules[0].id);
console.log(sources[0].sourceRule); // '||example.com^'

Apply $badfilter from dynamic filters against static rule sets:

const [{ ruleset: dynamicRuleset }] = await converter.convert(
    [dynamicFilter],
    { withSourceMap: true },
);
const rulesToDisable = await converter.computeRulesToDisable(
    [dynamicRuleset],
    [staticRuleset],
);
// rulesToDisable: UpdateStaticRulesOptions[]

RulesetWithSourceMap (returned when withSourceMap: true) implements IRulesetWithSourceMap:

| Method | Returns | Description | | --- | --- | --- | | getId() | string | Rule set identifier | | getSafeRulesCount() | number | Count of safe declarative rules (excludes unsafe) | | getUnsafeRulesCount() | number | Count of unsafe rules | | getRegexpRulesCount() | number | Count of regexp-based rules | | getDeclarativeRules() | Promise<DeclarativeRule[]> | All converted DNR rules (lazy) | | getUnsafeRules() | Promise<DeclarativeRule[]> | Unsafe rules subset (lazy) | | getRulesById(id) | Promise<SourceRuleAndFilterId[]> | Source rules for a DNR rule | | getBadFilterRules() | NetworkRule[] | $badfilter rules in this set | | getRulesHashMap() | IRulesHashMap | Hash map for fast $badfilter matching | | serializeCompact(unsafeRules, prettyPrint?) | Promise<string> | Compact JSON serialization | | unloadContent() | void | Release lazy-loaded content |

import type { ConversionResult } from '@adguard/dnr-converter';

Result returned by converter methods:

| Property | Type | Description | | --- | --- | --- | | ruleset | IRuleset / IRulesetWithSourceMap | The converted rule set | | errors | (ConversionError \| Error)[] | Rules that could not be converted | | limitations | LimitationError[] | Warnings about exceeded limits | | declarativeRulesToCancel | UpdateStaticRulesOptions[]? | Static rule IDs to disable (from computeRulesToDisable) |

MetadataRuleset

import { MetadataRuleset, METADATA_RULESET_ID } from '@adguard/dnr-converter';

A specialized ruleset that stores checksums and additional properties for a collection of DNR rule sets. It serializes as a single-element JSON array containing a declarative rule with a metadata field, acting as a data carrier within serialized ruleset files (never matches real requests).

METADATA_RULESET_ID is the constant 0; the ruleset's string ID is always "ruleset_0".

Checksum methods:

| Method | Returns | Description | | --- | --- | --- | | getId() | string | Always "ruleset_0" | | setChecksum(rulesetId, checksum) | void | Store checksum for a rule set | | getChecksum(rulesetId) | string \| undefined | Retrieve checksum, or undefined if not set | | getRulesetIds() | string[] | All rule set IDs that have checksums |

Additional-property methods:

| Method | Returns | Description | | --- | --- | --- | | setAdditionalProperty(key, value) | void | Store an arbitrary JSON-serializable property | | getAdditionalProperty(key) | unknown \| undefined | Retrieve a property value | | hasAdditionalProperty(key) | boolean | Check whether a property exists | | removeAdditionalProperty(key) | void | Remove a property (no-op if missing) |

Serialization:

| Method | Returns | Description | | --- | --- | --- | | serialize(pretty?) | string | JSON string; pretty=true for human-readable output | | MetadataRuleset.deserialize(json) | MetadataRuleset | Reconstruct from a serialized string; throws on invalid input |

const meta = new MetadataRuleset();
meta.setChecksum('ruleset_1', 'abc123');
meta.setAdditionalProperty('version', '2.0');

const json = meta.serialize();
const restored = MetadataRuleset.deserialize(json);

console.log(restored.getChecksum('ruleset_1')); // "abc123"
console.log(restored.getAdditionalProperty('version')); // "2.0"

isSafeRule(rule)

import { isSafeRule } from '@adguard/dnr-converter';

// DeclarativeRule from the DNR API
const rule = {
    id: 2,
    priority: 1,
    action: { type: 'block' },
    condition: { urlFilter: 'example.com' },
};

isSafeRule(rule); // true — "block" is a safe action

Returns true if the declarative rule's action is one of the safe rule actions (block, allow, allowAllRequests, upgradeScheme). Useful for separating safe static rules from unsafe dynamic rules that require additional review.

DNR_CONVERTER_VERSION

import { DNR_CONVERTER_VERSION } from '@adguard/dnr-converter';

console.log(DNR_CONVERTER_VERSION); // e.g. "0.0.1"

A string constant with the current library version.

Network rule types

The package exports types related to network rule parsing and validation:

import {
    Rule,
    HttpHeaderMatcher,
    RuleDeclarativeValidator,
} from '@adguard/dnr-converter';
  • Rule — parsed network rule with accessors for domains, resource types, methods, advanced modifier values, priority, and option flags. Create instances via Rule.createFromText(filterListId, index, text).
  • HttpHeaderMatcher — type describing the parsed $header modifier value.
  • RuleDeclarativeValidator — static helper that checks whether a Rule can be converted to a DNR rule. Call RuleDeclarativeValidator.shouldConvertRule(rule) — returns true if the rule is convertible, false if it should be silently skipped, or throws UnsupportedModifierError if the rule uses an unsupported modifier.

CLI

The package ships a dnr-converter command-line tool for converting filter lists to DNR rulesets and extracting filter content back from compiled rulesets.

Commands

convert

Converts AdGuard filter lists from a metadata directory into DNR rulesets.

dnr-converter convert \
  ./filters \
  ./resources \
  ./dist/rulesets

Arguments:

| Argument | Description | Default | | --- | --- | --- | | <filters_and_metadata_dir> | Directory containing filters.json and filter list files | (required) | | <resources_dir> | Directory with redirect resources | (required) | | [dest_rule_sets_dir] | Output directory for generated rulesets | ./build/rulesets |

Options:

| Option | Description | Default | | --- | --- | --- | | --debug | Enable debug logging | false | | --prettify-json <bool> | Pretty-print JSON output files | true | | --additional-properties <json> | Additional properties to include in the metadata ruleset as JSON string | {} |

The filters.json metadata file must be an array of filter metadata objects each with at minimum filterId and name fields.

extract-filters

Extracts original filter list content that was embedded in compiled DNR rulesets by the convert command.

dnr-converter extract-filters \
  ./dist/rulesets \
  ./extracted-filters

Arguments:

| Argument | Description | | --- | --- | | <path-to-rulesets> | Directory containing compiled ruleset files | | <path-to-output> | Directory to write extracted filter files |

Programmatic API

The CLI logic is also available as a programmatic API via the @adguard/dnr-converter/cli subpath export:

import { convertFilters, generateMD5Hash, type ConvertFiltersOptions } from '@adguard/dnr-converter/cli';

await convertFilters(
    './filters',
    './resources',
    './dist/rulesets',
    { debug: true, prettifyJson: true },
);

Documentation