AphrodyBun GitHub

Runtime docs · Runtime · Utilities

Adblock

Match network requests and cosmetic filters with Brave's adblock-rust engine, built into Bun.

bun:adblock exposes the filter engine of Brave's adblock-rust (MPL-2.0), compiled into Bun as the bun_adblock crate. It parses Adblock Plus, uBlock Origin and hosts-file lists and needs no network: you pass the list text. Nothing is loaded until the first new Engine().

import { Engine } from "bun:adblock";

const engine = new Engine({ rules: await Bun.file("easylist.txt").text() });

engine.check("https://ads.example.com/a.js", "https://news.example/", "script");
// => { matched: true, blocked: true, important: false }

Building an engine

new Engine({
  rules: "||ads.example.com^\n##.ad-banner", // or an array with one rule per element
  format: "standard", // or "hosts" for "0.0.0.0 host" lines
  debug: false, // true keeps the raw rule text in `filter` / `exception`
  resources: [], // optional redirect/scriptlet resources (adblock-rust resource JSON)
});

Parsing a large list takes time and memory. Cache the compiled form with serialize() and rebuild it with Engine.deserialize(); the format is tied to the Bun version that wrote it.

await Bun.write("engine.dat", engine.serialize());
const cached = Engine.deserialize(await Bun.file("engine.dat").bytes());

Network requests

check(url, sourceUrl?, requestType?, method?) returns:

FieldMeaning
matchedA blocking rule matched
blockedThe request must be blocked (important, or matched without an exception)
importantAn $important rule matched; exceptions do not apply
filterThe matched blocking rule (raw text with debug: true)
exceptionThe matched exception rule (raw text with debug: true)
redirectdata: URL of a $redirect resource, when resources provides it
rewrittenUrlThe URL after $removeparam rules

sourceUrl defaults to url (a first-party request); requestType is one of document, subdocument, script, stylesheet, image, font, media, object, xmlhttprequest, websocket, ping or other (the default).

Cosmetic filters

const page = engine.urlCosmeticResources("https://example.org/");
page.hideSelectors; // selectors specific to this host
page.injectedScript; // scriptlets to inject
// Generic rules for the classes and ids present in the page:
engine.hiddenClassIdSelectors(["ad-banner"], ["sidebar-ad"], page.exceptions);

fetch()

Pass an engine as adblock to check a request before any I/O. A blocking match rejects with a TypeError whose code is ERR_ADBLOCK_BLOCKED:

await fetch("https://ads.example.com/a.js", { adblock: engine }); // rejects

The request type is xmlhttprequest; the source URL is the Referer header, or the request URL. redirect and rewrittenUrl are not applied by fetch(): use check() to act on them. Redirects followed by fetch() are not re-checked.

Bun.WebView

attachWebView(view, engine) blocks the matching requests of a Chrome-protocol view (backend: "chrome" and the bun-webview-host backends) through the DevTools Fetch domain. Call it after the first navigate(); it resolves to a function that detaches the engine. The WebKit backend has no DevTools bridge and throws.

import { Engine, attachWebView } from "bun:adblock";

const view = new Bun.WebView({ backend: "chrome" });
await view.navigate("about:blank");
const detach = await attachWebView(view, engine);
await view.navigate("https://example.com");
await detach();

License

The engine sources in src/adblock come from adblock-rust and stay under the Mozilla Public License 2.0 (src/adblock/LICENSE).