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

@d-i-t-a/web-content-protection

v1.0.1

Published

Client-side content protection for web-based ebook readers. Drag prevention, copy protection, print blocking, text obfuscation, DevTools detection, and more.

Downloads

199

Readme

@d-i-t-a/web-content-protection

Client-side content protection for web-based ebook readers. Framework-agnostic, zero required dependencies, tree-shakable.

Table of Contents

Install

npm install @d-i-t-a/web-content-protection

Optional dependencies for enhanced features:

npm install devtools-detector              # Better DevTools detection
npm install browserslist-useragent-regexp   # Accurate browser enforcement

Demo

An interactive demo is included that lets you toggle each module on/off and test protections in real time.

git clone https://github.com/d-i-t-a/web-content-protection.git
cd web-content-protection
npm install
npm run demo

Then open http://localhost:3456/demo/ in your browser.

The demo simulates a book reader with:

  • Left sidebar — toggle each of the 19 modules on/off
  • Center — sample book content with active protections
  • Right panel — highlights & notes panel (also protected)
  • Bottom — live event log showing every blocked action
  • Toolbar — test buttons for copy, print, select-all, and innerText dump

Try right-clicking, Ctrl+C, Ctrl+P, dragging text, or opening DevTools to see the protections in action.

Quick Start

Use the ContentProtection orchestrator to enable multiple protections at once:

import { ContentProtection } from "@d-i-t-a/web-content-protection";

const contentEl = document.getElementById("reader-content")!;
const iframes = Array.from(document.querySelectorAll("iframe")) as HTMLIFrameElement[];

const protection = new ContentProtection({
  onEvent: (e) => console.log(`[Protection] ${e.type}`, e.detail),

  dragPrevention: { contentElement: contentEl, contentIframes: iframes },
  printProtection: { contentElement: contentEl, contentIframes: iframes },
  copyProtection: { contentElement: contentEl, contentIframes: iframes, mode: "block" },
  contextMenuProtection: { contentElement: contentEl, contentIframes: iframes },
  keyboardProtection: { contentElement: contentEl, contentIframes: iframes },
  devToolsDetection: { action: "redirect" },
  textObfuscation: {
    scrollContainer: iframes[0].contentDocument!.documentElement,
    contentRoot: iframes[0].contentDocument!.body,
  },
});

await protection.activate();

Modules

Each module can be used standalone or through the orchestrator.

| Module | What it does | |--------|-------------| | DragPrevention | Blocks text/image drag-and-drop to external apps | | PrintProtection | Hides content on print (CSS + event-based + keyboard) | | CopyProtection | Blocks or restricts clipboard operations (block / character-limited modes) | | ContextMenuProtection | Disables right-click context menu | | KeyboardProtection | Blocks Save, View Source, DevTools, F12, and custom key combos | | DevToolsDetection | Detects open DevTools; can redirect, clear storage, or fire callback | | TextObfuscation | Scrambles text outside viewport; DOM dumps return gibberish | | LinkUrlHiding | Hides link target URLs from status bar | | BrowserEnforcement | Restricts to whitelisted browsers | | ScreenshotDetection | Blanks content on tab blur / visibility change / PrintScreen | | Watermarking | Invisible visual + zero-width-character watermarks for leak tracing | | SelectionLimiting | Caps how much text can be selected at once | | ImageProtection | Blocks right-click save, drag, open-in-new-tab on images; optional canvas rendering | | AntiAutomation | Detects Puppeteer, Playwright, Selenium, headless browsers | | SpeechSynthesisBlocking | Blocks or restricts speechSynthesis.speak() to prevent TTS extraction | | ContentExpiration | Time-limited viewing sessions with optional extension | | MediaProtection | Protects <audio> and <video> elements — hides download button, blob URLs, blocks right-click, disables PiP | | MediaStreamProtection | Blocks MediaRecorder, AudioContext capture, and captureStream() on protected elements | | TamperDetection | Detects screen grabber CSS injection + DOM tampering via hidden sentinels; blanks content on detection |

Standalone Module Usage

import { CopyProtection } from "@d-i-t-a/web-content-protection";

const copy = new CopyProtection({
  contentElement: document.getElementById("reader")!,
  mode: "restrict",
  maxCharacters: 200,
  onEvent: (e) => console.log(e),
});

copy.activate();

Copy Protection Modes

Block all copying

copyProtection: { contentElement: el, mode: "block" }

Allow limited copying (citations)

copyProtection: { contentElement: el, mode: "restrict", maxCharacters: 200 }

Citation bypass

// Temporarily allow full copy for citation workflow
protection.copyProtection!.citationMode = true;
// ... user copies citation ...
protection.copyProtection!.citationMode = false;

Text Obfuscation

The most effective client-side protection. All text outside the visible viewport is scrambled. A document.body.innerText dump returns gibberish.

textObfuscation: {
  scrollContainer: scrollEl,
  contentRoot: bodyEl,
  excludeNodes: ["script", "style", "code"],
  viewportPadding: 50,
}

Call reinitialize() on page/chapter changes:

protection.textObfuscation!.reinitialize();

Watermarking

Two-layer watermarking for leak tracing:

  1. Visual: Near-invisible overlay with user ID + timestamp. Survives screenshots.
  2. Text fingerprint: Zero-width Unicode characters injected into text. Survives copy-paste.
watermarking: {
  contentRoot: bodyEl,
  userId: "user-12345",
  sessionId: "session-abc",
  enableTextFingerprint: true,
}

Decode a fingerprint from leaked text:

import { Watermarking } from "@d-i-t-a/web-content-protection";

const userId = Watermarking.decodeFingerprint(leakedText);
console.log(`Leaked by: ${userId}`);

Image Protection

Prevents image extraction via right-click, drag, and open-in-new-tab.

imageProtection: {
  contentRoot: bodyEl,
  disablePointerEvents: true,  // blocks right-click "Save image as..."
  overlayMode: true,           // transparent overlay above images
  blockDrag: true,             // prevents drag to desktop/other apps
  canvasMode: false,           // replace <img> with <canvas> (strongest)
  blobUrls: false,             // convert src to blob URLs (hides original URL)
}

Anti-Automation Detection

Detects headless browsers and automation frameworks (Selenium, Puppeteer, Playwright).

antiAutomation: {
  action: "block",             // "block" | "warn" | "callback"
  detectWebDriver: true,       // navigator.webdriver flag
  detectHeadless: true,        // HeadlessChrome, missing plugins, etc.
  detectAutomationProps: true,  // framework-specific globals
  recheckInterval: 5000,       // re-check every 5s (automation can be injected late)
}

Speech Synthesis Blocking

Prevents text extraction via the Web Speech API (speechSynthesis.speak()).

speechSynthesisBlocking: {
  mode: "block",               // "block" all or "restrict" to maxCharacters
  maxCharacters: 500,          // only used in "restrict" mode
}

Content Expiration

Time-limited viewing sessions. Content blanks or redirects when the session expires.

contentExpiration: {
  protectedElements: [contentEl],
  sessionDuration: 3600000,    // 1 hour
  warningBefore: 300000,       // warn 5 min before
  action: "blank",             // "blank" | "redirect" | "callback"
  allowExtension: true,        // user can extend session
  maxExtensions: 3,
  extensionDuration: 1800000,  // 30 min per extension
  onWarning: (ms) => showToast(`Session expires in ${ms/1000}s`),
  onExpired: () => showModal("Session expired"),
}

Extend programmatically:

protection.contentExpiration!.extend(); // returns false if max extensions reached

Clipboard Attribution

Appends source attribution to any copied text (in "restrict" mode):

copyProtection: {
  contentElement: el,
  mode: "restrict",
  maxCharacters: 200,
  attribution: "Copied from 'Book Title' — © Publisher Name",
  attributionSeparator: "\n\n—\n",
}

Media Protection

Protects <audio> and <video> elements embedded in EPUB3 or any HTML content. Especially relevant for enriched ebooks, educational content, and audiobook previews.

mediaProtection: {
  contentRoot: bodyEl,
  hideDownloadButton: true,     // CSS-hides the native download button
  blobUrls: true,               // converts src to blob: URLs (hides original URL)
  blockContextMenu: true,       // blocks right-click "Save audio/video as..."
  enforceNoDownload: true,      // sets controlslist="nodownload"
  disablePictureInPicture: true, // prevents PiP on video elements
  protectSourceUrls: true,      // intercepts currentSrc getter
  detectRecording: false,       // detect virtual audio devices (heuristic)
}

Media Stream Protection

Blocks JavaScript-based stream capture — MediaRecorder, AudioContext routing, and captureStream().

mediaStreamProtection: {
  contentRoot: bodyEl,
  blockMediaRecorder: true,     // blocks new MediaRecorder() on protected streams
  blockAudioCapture: true,      // blocks createMediaElementSource() routing
  blockCaptureStream: true,     // blocks captureStream() on media + canvas
}

These two modules are designed for EPUB3 media content rendered inside a web reader. The audio/video files end up as <audio>/<video> elements in the DOM — same context as the text, same protection surface.

Tamper Detection

Detects screen grabber extensions (GoFullPage, Nimbus, Awesome Screenshot, etc.) that inject CSS styles onto DOM elements to time their captures. Places invisible sentinel elements throughout the content and watches for style injection via mutation observers and periodic computed-style checks.

tamperDetection: {
  protectedElements: [contentEl, annotationsPanel],
  contentRoot: bodyEl,
  sentinelCount: 3,              // distribute 3 sentinels in content
  action: "blank",               // "blank" | "scramble" | "callback"
  autoRestore: true,             // restore when tampering stops
  restoreDelay: 2000,            // wait 2s before restoring
}

Detects:

  • Inline style injection on sentinel elements (animation, transition, transform, filter, etc.)
  • Computed style anomalies from CSS rule injection (class/id-based)
  • Sentinel removal from DOM

Screenshot Detection

Blanks content when the tab loses focus or visibility changes (common when using screenshot tools).

screenshotDetection: {
  protectedElements: [contentEl, annotationsPanel],
  blankOnBlur: true,
  blankOnHidden: true,
  detectPrintScreen: true,
  restoreDelay: 500,
}

Page Navigation

When the reader navigates to a new page or chapter, reinitialize content-dependent modules:

reader.on("pageChange", () => {
  protection.textObfuscation?.reinitialize();
  protection.watermarking?.reinitialize();
});

Event Logging

All protection events are logged:

const events = protection.getEventLog();
// Send to analytics
analytics.track("content_protection_events", events);

Integration Notes

Finding the right elements

| Reader Architecture | Content Target | Scroll Target | |---------------------|---------------|---------------| | iframe-based | iframe.contentDocument.body | iframe.contentDocument.documentElement | | Shadow DOM | shadowRoot.querySelector('.content') | Shadow host or inner scroller | | Direct injection | .reader-content | Same element or parent |

Adding iframes dynamically

If iframes are loaded after page init, deactivate and re-activate with new references:

protection.deactivate();
// Update config with new iframes
const newProtection = new ContentProtection({ ...config, contentIframes: newIframes });
await newProtection.activate();

License

Apache-2.0 — Copyright 2018-2026 DITA (AM Consulting LLC)