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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@digitalprimates/darken

v0.0.7

Published

Code obfuscation library for Roku BrightScript

Readme

Darken

What is darken?

Darken is a library providing useful components to be used when doing simple obfuscation of Brightscript scripts. It is not intended to be a tool or complete solution on its own.

When combined with a parser that can return AST (such as @roku-road/bright), the library can be used to do basic literal obfuscation, reference renaming, comment stripping and type removal. The project includes a simple manifest parser to determine entry points and related files to ensure consistent renaming.

Usage

Parse the XML files using manifestParser. manifestParser takes parsed XML data and yields an object comprised of the field names, function names, and Brightscript scripts defined in the XML.

const data = XML.parse(String(fs.readFileSync(manifest)));
const {
  fields: publicFields,
  fns: publicFns,
  scripts
} = manifestParser(data);

fields and fns are the public fields and functions and will be used later when traversing the AST with the ObfuscationVisitor.

scripts is an array of objects which each have a uri property. They will be used to generate the AST which will be run through the obfuscator.

Once you have the script URIs, the basics steps are as follows:

  1. Map each script file to its contents as a String
  2. Map each file's contents to a Brightscript AST (see @roku-road/bright for one such parser)
  3. Obfuscate each AST using traverse, ObfuscationVisitor, fields and fns from above.
  4. Generate new syntax using OutputVisitor

Map script URI to file contents

For each script, you'll have to read the file based on the uri property. These path names have "no concept of a current working directory or relative paths" (Roku File System), so you will need to replace the "device field" portion of the uri.

const { uri } = script; //script object from manifestParser

// DEVICE_FIELD typically looks like "tmp:", "pkg:", "common:", or "ext1:"
// This will depend on your implementation.
const sourcePath = path.resolve(uri.replace(DEVICE_FIELD, RELATIVE_PARENT_FOLDER));
const content = String(fs.readFileSync(sourcePath));

Map script contents to Brightscript AST

Provide the script content to a Brightscript AST parser (@roku-road/bright).

const { ast: toBrightAST } = require('@roku-road/bright');

const ast = toBrightAST(content);

Obfuscate AST using traverse + ObfuscationVisitor

Provide the AST and a new ObfuscationVisitor to traverse to yield an obfuscated AST. You will also need to provide an object with a "renamed" property along with a new ObfuscationVisitor. The "renamed" property should contain an array of objects with the following shape: {src: String, dst: String}. The ObfuscationVisitor uses this array to help determine if an identifier can be safely renamed during obfuscation. If dst does not exist or if it does and does not equal the name of the current identifier being visited, it will be renamed. publicFields and publicFns pulled out of the manifestParser should be included in the renamed array so they can be publicly accessed after obfuscation.

// setting src and dst to the same value prevents them from being renamed during obfuscation.
const renamed = [...publicFields, publicFns].map( ({id}) => ({src: id, dst: id}));
const { ast: obfuscatedAST } = traverse(ast)(new ObfuscationVisitor, {renamed});

Generate obfuscated syntax using traverse + OutputVisitor

Provide the AST and a new OutputVisitor to traverse to generate Brightscript syntax from the AST.

const { syntax } = traverse(obfuscatedAST)(new OutputVisitor);

syntax will be a String representation of the obfuscated Brighscript code and can be written out the file system at this point.

Full Example

  const { ast: toBrightAST } = require('@roku-road/bright');

  // Map functions
  const toContents = script => {
    const { uri } = script; //script object from manifestParser

    // DEVICE_FIELD typically looks like "tmp:", "pkg:", "common:", or "ext1:"
    // This will depend on your implementation.
    const sourcePath = path.resolve(uri.replace(DEVICE_FIELD, RELATIVE_PARENT_FOLDER));
    const contents = String(fs.readFileSync(sourcePath));
    return contents;
  };

  const toAST = content => {
    const ast = toBrightAST(content);
    return ast;
  };

  const toObfuscation = (publicFields, publicFns) => ast => {
    // To preserve the names of public API.
    const renamed = [...publicFields, publicFns].map( ({id}) => ({src: id, dst: id}));
    const { ast: obfuscatedAST } = traverse(ast)(new ObfuscationnVisitor, {renamed});
    return obfuscatedAST;
  };

  const toSyntax = ast => {
    const { syntax } = traverse(ast)(new OutputVisitor);
    return syntax;
  };

  // Parse XML files
  const data = XML.parse(String(fs.readFileSync(/*PATH_TO_MANIFEST_FILE*/)));
  const {
    fields: publicFields,
    fns: publicFns,
    scripts
  } = manifestParser(data);

  const obfuscatedScripts = scripts
    .map(toContents)
    .map(toAST)
    .map(toObfuscation(publicFields, publicFns))
    .map(toSyntax);