# jso-protector

Lightweight Node.js CLI wrapper for the JavaScript Obfuscator HTTP API.

This package is intentionally dependency-free. It is designed to close the developer workflow gap with npm-first obfuscators while keeping JavaScript Obfuscator's existing hosted API, account plans, and dashboard API keys.

## Quick Start

Install from the local workspace path:

```bash
npm install --save-dev ./packages/jso-protector
npx jso-protector --init
npx jso-protector --init --init-template html-app --manifest dist-protected/jso-manifest.json
npx jso-protector --init --init-template electron-app --input out --output out-protected
```

For another app in the same workspace, pin the local dependency with a `file:` path:

```json
{
  "devDependencies": {
    "jso-protector": "file:../packages/jso-protector"
  }
}
```

For internal sharing without publishing to npm, create a tarball and install that artifact inside the consuming project:

```bash
npm pack --json
npm install --save-dev path/to/jso-protector-0.1.1.tgz
```

Keep local tarballs inside internal storage or build artifacts, not in public package registries.

The package is intentionally local-only: `private:true`, `UNLICENSED`, and `prepublishOnly` block accidental npm publishing.

`--init` supports starter templates for common release patterns:

- `browser-app`: default browser build output in `dist/`
- `html-app`: browser build output plus marked inline HTML/template script protection
- `node-app`: Node.js release output with `.js`, `.cjs`, and `.mjs` extensions
- `electron-app`: Electron release folders with Node-oriented JavaScript plus copied app assets

You can combine `--init` with normal config flags to scaffold a closer first draft:

```bash
npx jso-protector --init --init-template node-app --input build --output build-protected --manifest build-protected/jso-manifest.json
```

For Electron Forge, Electron Builder, or custom desktop packaging flows that emit a release folder, start with the dedicated Electron scaffold:

```bash
npx jso-protector --init --init-template electron-app --input out --output out-protected --manifest out-protected/jso-manifest.json
```

## Payment and API Access

`jso-protector` is a localhost developer client for the paid JavaScript Obfuscator hosted API. The npm package does not unlock protection by itself and does not contain payment logic.

Payment and account enforcement stay on `javascriptobfuscator.com`:

1. The user buys a plan or credits on the JavaScript Obfuscator website.
2. The dashboard provides `JSO_API_KEY` and `JSO_API_PASSWORD`.
3. The local npm CLI sends selected JavaScript to the configured HTTPS API endpoint.
4. The hosted API validates the account, plan, limits, and credentials before returning protected code.

Keep billing, entitlement checks, plan limits, and API secrets on the server side. The local npm package should only read credentials from environment variables or config and call the API.

If the hosted API rejects a request, the CLI reports dashboard credential hints for authentication failures and account/plan/credit guidance for entitlement failures. API keys and passwords are redacted from error messages before they reach terminal logs.

Set credentials from the dashboard:

```bash
set JSO_API_KEY=base64-api-key-from-dashboard
set JSO_API_PASSWORD=base64-api-password-from-dashboard
```

For macOS/Linux shells:

```bash
export JSO_API_KEY=base64-api-key-from-dashboard
export JSO_API_PASSWORD=base64-api-password-from-dashboard
```

Protect generated build output:

```bash
npx jso-protector --config jso.config.json
```

Protect one file through a shell pipe:

```bash
type dist\app.js | npx jso-protector --stdin --stdout --file-name app.js > dist\app.protected.js
```

## npm Scripts

```json
{
  "scripts": {
    "build": "vite build",
    "preflight": "jso-protector --config jso.config.json --release-check --json",
    "protect": "jso-protector --config jso.config.json",
    "release": "npm run build && npm run preflight && npm run protect && npm run smoke"
  }
}
```

## Node API

Use the API when you want to protect code or release folders from a custom build script without spawning the CLI:

```js
const { obfuscate, obfuscateMultiple, getOptionsByPreset, protectCode, protectFiles, planProtection } = require("jso-protector");

const obfuscationResult = await obfuscate("console.log('release');", {
  apiKey: process.env.JSO_API_KEY,
  apiPassword: process.env.JSO_API_PASSWORD,
  controlFlowFlattening: true,
  identifierNamesGenerator: "hexadecimal",
  reservedNames: ["^PublicApi$"],
  stringArrayEncoding: ["rc4"]
}, "app.js");

console.log(obfuscationResult.getObfuscatedCode());
console.log(obfuscationResult.toString());

const multipleResults = await obfuscateMultiple({
  "foo.js": "var foo = 1;",
  "bar.js": "var bar = 2;"
}, {
  apiKey: process.env.JSO_API_KEY,
  apiPassword: process.env.JSO_API_PASSWORD,
  ...getOptionsByPreset("balanced")
});

console.log(multipleResults["foo.js"].getObfuscatedCode());

const protectedCode = await protectCode({
  apiKey: process.env.JSO_API_KEY,
  apiPassword: process.env.JSO_API_PASSWORD,
  preset: "balanced"
}, "console.log('release');", "app.js");

const plan = planProtection({
  input: "dist",
  output: "dist-protected",
  preset: "balanced",
  exclude: ["**/*.map", "**/vendor/**", "**/*-obfuscated.js"]
});

console.log(`Protecting ${plan.summary.files.length} file(s).`);

await protectFiles({
  apiKey: process.env.JSO_API_KEY,
  apiPassword: process.env.JSO_API_PASSWORD,
  input: "dist",
  output: "dist-protected",
  preset: "balanced",
  manifest: "dist-protected/jso-manifest.json"
});
```

Use `obfuscate(code, options, fileName)` and `obfuscateMultiple(sourceCodesObject, options)` when migrating from `javascript-obfuscator` style Node scripts. These methods are asynchronous because they call the hosted API, and their result objects include `getObfuscatedCode()`, `toString()`, `getSourceMap()`, and `getIdentifierNamesCache()` for compatibility-friendly call sites. They use the same marked HTML and conditional-comment planning as the CLI, so `parseHtml` and `honorConditionalComments` work for object-map inputs too. Source maps and identifier caches return `null` because the hosted API workflow is designed to remove release source maps.

The Node API accepts common `javascript-obfuscator` option names directly and maps them to the closest hosted API options. Supported compatibility keys include `optionsPreset`, `parseHtml`, `stringArray`, `stringArrayEncoding`, `controlFlowFlattening`, `deadCodeInjection`, `deadCodeInjectionThreshold`, `identifierNamesGenerator`, `renameGlobals`, `renameProperties`, `reservedNames`, `domainLock`, `compact`, and `target`. Use `translateJavascriptObfuscatorOptions(sourceOptions, overrides)` when you want to inspect or reuse the mapped config before calling the API.

For JS-Confuser migration scripts, pass a `jsConfuserOptions` object or call `translateJsConfuserOptions(sourceOptions, overrides)` first. Current direct mappings cover common presets plus `target`, `renameVariables`, `renameGlobals`, `globalConcealing`, `stringEncoding`, `stringConcealing`, `duplicateLiteralsRemoval`, `stringCompression`, `compact`, `minify`, `controlFlowFlattening`, `deadCode`, `identifierGenerator`, `lock.domainLock`, and `lock.endDate`.

`getOptionsByPreset("standard" | "balanced" | "maximum")` returns a copy of the hosted API preset options for scripts that previously used preset lookup helpers.

Use `protectFile(options, sourcePath, outputPath)` for one physical source file, `protectFiles(options)` for a file or folder configured with `input` and `output`, and `planProtection(options)` when CI needs a dry plan before making the API call. The `obfuscateFile`, `obfuscateFiles`, and `obfuscateDirectory` aliases are available for teams that prefer obfuscator naming. These helpers use the same config merging, preset handling, asset copying, manifest writing, and size budget checks as the CLI.

TypeScript declarations are included for the CLI core, Node API, Browserify, Metro/React Native, esbuild/Vite/Rollup/Webpack/Rspack/Gulp/Grunt integrations, and the Webpack/Rspack loader entrypoints. No separate `@types` package is needed.

## Rspack

Use the Rspack entrypoint when a webpack-compatible Rust build should protect emitted JavaScript assets through the same final-asset stage used by the Webpack plugin.

```js
const JsoProtectorRspackPlugin = require("jso-protector/rspack");

module.exports = {
  plugins: [
    new JsoProtectorRspackPlugin({
      apiKey: process.env.JSO_API_KEY,
      apiPassword: process.env.JSO_API_PASSWORD,
      preset: "balanced",
      exclude: ["vendor.js"],
      manifest: "dist/jso-manifest.json"
    })
  ]
};
```

Use `jso-protector/rspack-loader` when a Rspack project needs module-level protection before bundling. For complete release bundles, prefer the Rspack plugin so final emitted assets and stale source maps are handled together.

## Browserify

Use the Browserify transform when a legacy bundle protects selected modules before bundling:

```js
const browserify = require("browserify");
const fs = require("fs");
const jsoProtector = require("jso-protector/browserify");

browserify("src/app.js", {
  transform: [[jsoProtector, {
    apiKey: process.env.JSO_API_KEY,
    apiPassword: process.env.JSO_API_PASSWORD,
    input: "src",
    preset: "balanced",
    include: ["**/*.js"],
    reservedNames: ["^PublicApi$"],
    manifest: "dist/jso-manifest.json"
  }]]
})
  .bundle()
  .pipe(fs.createWriteStream("dist/app.js"));
```

The transform passes excluded files through untouched, protects selected JavaScript modules, and honors config files, filters, manifests, and size budgets. It respects configured protected extensions, so `.mjs` and `.cjs` module pipelines can opt in through `extensions`.

## Next.js

Use the Next.js entrypoint when you want a first-class wrapper around `next.config.js` instead of wiring the Webpack plugin manually. By default, it adds protection only for production client bundles.

```js
const withJsoProtector = require("jso-protector/next");

module.exports = withJsoProtector({
  reactStrictMode: true
}, {
  apiKey: process.env.JSO_API_KEY,
  apiPassword: process.env.JSO_API_PASSWORD,
  preset: "balanced",
  exclude: ["static/chunks/webpack*.js"],
  manifest: ".next/jso-manifest.json"
});
```

Pass `target: "server"` to protect only server bundles or `target: "both"` to protect both server and client output. Development builds are skipped by default; pass `applyInDevelopment: true` only when you explicitly want protection during `next dev`.

## Metro / React Native

Use the Metro entrypoint when React Native or Expo projects need to protect the final release bundle through `serializer.customSerializer`.

```js
const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");
const { withJsoProtectorMetro } = require("jso-protector/metro");

const baseConfig = getDefaultConfig(__dirname);

module.exports = withJsoProtectorMetro(mergeConfig(baseConfig, {}), {
  apiKey: process.env.JSO_API_KEY,
  apiPassword: process.env.JSO_API_PASSWORD,
  preset: "balanced",
  include: ["index.android.release.bundle.js", "index.ios.release.bundle.js"]
});
```

When you already have a serializer function, wrap it directly:

```js
const { getDefaultConfig } = require("expo/metro-config");
const { createMetroSerializer } = require("jso-protector/metro");

const config = getDefaultConfig(__dirname);
const expoSerializer = config.serializer.customSerializer;

config.serializer.customSerializer = createMetroSerializer({
  serializer: expoSerializer,
  apiKey: process.env.JSO_API_KEY,
  apiPassword: process.env.JSO_API_PASSWORD,
  fileName: "index.android.release.bundle.js"
});

module.exports = config;
```

The Metro helper protects only matching bundle names and removes stale source maps from object-style serializer results unless `removeSourceMaps: false` is set.

If you prefer a framework-specific package path for React Native docs or starter templates, `jso-protector/react-native` is an alias of `jso-protector/metro`:

```js
const { withJsoProtectorMetro } = require("jso-protector/react-native");
```

## esbuild

Use the esbuild entrypoint for direct esbuild release builds. It protects in-memory output files when `write: false` is used, and protects written `outdir` or `outfile` assets in place for normal builds.

```js
const esbuild = require("esbuild");
const jsoProtector = require("jso-protector/esbuild");

await esbuild.build({
  entryPoints: ["src/app.js"],
  bundle: true,
  outdir: "dist",
  plugins: [
    jsoProtector({
      apiKey: process.env.JSO_API_KEY,
      apiPassword: process.env.JSO_API_PASSWORD,
      preset: "balanced",
      exclude: ["vendor.js"],
      manifest: "dist/jso-manifest.json"
    })
  ]
});
```

The esbuild plugin respects configured protected extensions, so Node-oriented bundles can opt into `.mjs` or `.cjs` outputs through `extensions`.

## Vite

Protect emitted JavaScript chunks after Vite finishes bundling:

```js
const jsoProtector = require("jso-protector/vite");

module.exports = {
  plugins: [
    jsoProtector({
      apiKey: process.env.JSO_API_KEY,
      apiPassword: process.env.JSO_API_PASSWORD,
      preset: "balanced",
      exclude: ["**/vendor/**"],
      manifest: "dist/jso-manifest.json"
    })
  ]
};
```

The plugin removes stale JavaScript source maps by default. Pass `removeSourceMaps: false` only when another step regenerates or removes maps.

Bundler plugins use the same protection planner as the CLI and Node API, so `honorConditionalComments` preserves marked JavaScript regions before chunks are sent to the hosted API.

## Rollup

Use the Rollup entrypoint when your release build writes Rollup chunks directly:

```js
const jsoProtector = require("jso-protector/rollup");

module.exports = {
  plugins: [
    jsoProtector({
      apiKey: process.env.JSO_API_KEY,
      apiPassword: process.env.JSO_API_PASSWORD,
      preset: "balanced",
      exclude: ["vendor.js"],
      manifest: "dist/jso-manifest.json"
    })
  ]
};
```

## Webpack

Protect emitted JavaScript assets during the final asset phase. The plugin supports Webpack 5 through `processAssets` and falls back to the legacy `emit` hook for Webpack 4 projects:

```js
const JsoProtectorWebpackPlugin = require("jso-protector/webpack");

module.exports = {
  plugins: [
    new JsoProtectorWebpackPlugin({
      apiKey: process.env.JSO_API_KEY,
      apiPassword: process.env.JSO_API_PASSWORD,
      preset: "balanced",
      exclude: ["vendor.js"],
      manifest: "dist/jso-manifest.json"
    })
  ]
};
```

Like the Vite plugin, the Webpack plugin removes stale emitted source maps by default. It respects configured protected extensions for emitted assets, so `.mjs` and `.cjs` library outputs can use the same plugin path. One package can cover both Webpack 4 and Webpack 5 release builds.

## Webpack Loader

Use the loader when a Webpack project already protects selected modules before bundling. For complete release bundles, prefer the Webpack plugin because it protects final emitted assets and removes stale source maps.

```js
module.exports = {
  module: {
    rules: [{
      test: /\.js$/,
      include: /src/,
      use: [{
        loader: "jso-protector/webpack-loader",
        options: {
          apiKey: process.env.JSO_API_KEY,
          apiPassword: process.env.JSO_API_PASSWORD,
          preset: "balanced",
          include: ["src/*.js"],
          reservedNames: ["^PublicApi$"]
        }
      }]
    }]
  }
};
```

The loader disables Webpack caching for protected modules, returns no source map after protection, and honors config files, `extensions`, `include`, `exclude`, manifests, and size budgets.

## Gulp

Use the Gulp entrypoint when a legacy release task copies files through Vinyl streams:

```js
const { dest, src } = require("gulp");
const jsoProtector = require("jso-protector/gulp");

function protect() {
  return src(["dist/**/*.js", "dist/**/*.{css,html,png,svg}", "!dist/**/*.map"], { base: "dist" })
    .pipe(jsoProtector({
      apiKey: process.env.JSO_API_KEY,
      apiPassword: process.env.JSO_API_PASSWORD,
      preset: "balanced",
      exclude: ["**/vendor/**"],
      manifest: "dist-protected/jso-manifest.json"
    }))
    .pipe(dest("dist-protected"));
}

exports.protect = protect;
```

The Gulp transform batches buffered JavaScript files into one API request, passes non-JavaScript assets through, and removes stale `.js.map` files by default. Pass `removeSourceMaps: false` only when another task handles maps.

## Grunt

Use the Grunt entrypoint when a release pipeline already uses Grunt file mappings:

```js
module.exports = function configureGrunt(grunt) {
  grunt.initConfig({
    jsoProtector: {
      release: {
        options: {
          apiKey: process.env.JSO_API_KEY,
          apiPassword: process.env.JSO_API_PASSWORD,
          input: "dist",
          output: "dist-protected",
          preset: "balanced",
          exclude: ["**/vendor/**"],
          manifest: "dist-protected/jso-manifest.json"
        },
        files: [{
          expand: true,
          cwd: "dist",
          src: ["**/*.js", "!**/*.map"],
          dest: "dist-protected"
        }]
      }
    }
  });

  require("jso-protector/grunt")(grunt);
  grunt.registerTask("protect", ["jsoProtector:release"]);
};
```

The Grunt task protects JavaScript file mappings and writes protected files to each mapping's `dest`. Use a copy task for non-JavaScript assets.

For bundle and stream plugins, `include` and `exclude` match emitted asset names. Use them to protect first-party release chunks while leaving vendor bundles, polyfills, and framework runtime files untouched.

```js
jsoProtector({
  preset: "balanced",
  include: ["assets/*.js"],
  exclude: ["**/vendor/**", "**/polyfills-*.js"],
  maxOutputBytes: 250000,
  maxGrowthRatio: 8
});
```

The Browserify, esbuild, Vite, Rollup, Webpack, Webpack loader, Gulp, and Grunt integrations also honor `manifest`, `maxOutputBytes`, and `maxGrowthRatio`. A failed size budget stops the build before protected chunks replace the original bundle output.

## Configuration

```json
{
  "$schema": "./node_modules/jso-protector/jso.config.schema.json",
  "endpoint": "https://www.javascriptobfuscator.com/HttpApi.ashx",
  "apiKey": "$JSO_API_KEY",
  "apiPassword": "$JSO_API_PASSWORD",
  "projectName": "browser-release",
  "input": "dist",
  "output": "dist-protected",
  "preset": "balanced",
  "extensions": [".js", ".jsx"],
  "markupExtensions": [".html", ".htm", ".php", ".aspx"],
  "exclude": ["**/*.map", "**/vendor/**", "**/*-obfuscated.js"],
  "copyAssets": true,
  "assetExclude": ["**/*.map"],
  "mixedServer": false,
  "parseHtml": false,
  "honorConditionalComments": false,
  "keepHeaderComment": true,
  "protectObjectDeclaration": false,
  "moveNestedFunction": false,
  "formattedOutput": false,
  "keepIndent": false,
  "lineNumbers": false,
  "reservedNames": ["^PublicApi$", "^keep_"],
  "options": {
    "OptimizationMode": "Web",
    "LockDomain": false,
    "LockDate": false
  }
}
```

The package includes `jso.config.schema.json` for editor autocomplete and validation. Generated configs point to `./node_modules/jso-protector/jso.config.schema.json`; the in-package example points to `./jso.config.schema.json`.

For common product features that otherwise require raw HTTP API option names, the config also supports convenience aliases such as `keepHeaderComment`, `protectObjectDeclaration`, `moveNestedFunction`, `formattedOutput`, `keepIndent`, `lineNumbers`, `lockDomainSubdomains`, `lockDomainMessage`, `lockDate`, `lockDateValue`, and `lockDateMessage`. These map to the corresponding `options.*` values before explicit `options` overrides are applied.

`--config` accepts JSON files plus trusted CommonJS or ES module config files. When no config path is provided, the CLI looks for `jso.config.json`, then `jso.config.cjs`, then `jso.config.mjs`, then `jso.config.js`.

Config files can also carry mapped `javascript-obfuscator` compatibility keys during migration, including `optionsPreset`, `stringArray`, `stringArrayEncoding`, `controlFlowFlattening`, `deadCodeInjection`, `identifierNamesGenerator`, `renameGlobals`, `renameProperties`, `domainLock`, `compact`, `target`, `parseHtml`, and `ignoreImports`. `optionsPreset` also accepts the current `vm-default`, `vm-low-obfuscation`, `vm-medium-obfuscation`, `vm-high-obfuscation`, `vm-ultra-high-obfuscation`, and `vm-anti-llm` aliases, mapped to the closest hosted preset for starter migration. Review-only compatibility fields such as `selfDefending`, `sourceMap`, `identifiersDictionary`, `identifiersPrefix`, `reservedStrings`, `splitStrings`, `seed`, and `numbersToExpressions` are also accepted in config files so existing configs do not fail immediately; `validate-config` reports them as warnings because the hosted API has no one-to-one local mapping. Explicit `options` values still win, so teams can keep a familiar starter config and override exact hosted API fields as they tune release output.

```js
module.exports = ({ env }) => ({
  apiKey: "$JSO_API_KEY",
  apiPassword: "$JSO_API_PASSWORD",
  input: env.CI ? "dist" : "demo-dist",
  output: "dist-protected",
  preset: "balanced",
  reservedNames: ["^PublicApi$"],
  options: {
    LockDomain: Boolean(env.RELEASE_DOMAIN),
    LockDomainList: env.RELEASE_DOMAIN || ""
  }
});
```

JavaScript config files execute as Node.js code, so only load config files from your own repository or another trusted source. Use JSON config when you want schema validation and no executable config logic.

ES module configs can use `export default { ... }` or `export default ({ env, cwd }) => ({ ... })`. This covers `jso.config.mjs` and `jso.config.js` inside repositories that already use `"type": "module"`.

`apiKey` and `apiPassword` should use the base64 values copied from the JavaScript Obfuscator dashboard. Environment references in the form `$NAME` are resolved at runtime.

Do not commit real API credentials. Keep `apiKey` and `apiPassword` as environment references in shared config files.

The CLI reads `JSO_API_KEY`, `JSO_API_PASSWORD`, and `JSO_ENDPOINT` by default. It also accepts long-form aliases: `JAVASCRIPT_OBFUSCATOR_API_KEY`, `JAVASCRIPT_OBFUSCATOR_API_PASSWORD`, and `JAVASCRIPT_OBFUSCATOR_ENDPOINT`.

## Presets and Public Names

Use `preset` to start from a repeatable protection profile:

- `standard`: core string encoding, string movement, name replacement, and compression.
- `balanced`: standard plus short local names, deep obfuscation, code transposition, string encryption, and flat transform.
- `maximum`: balanced plus member/global renaming, member movement, and low dead-code insertion.

Override or extend any preset with `options`. The option names are the same names sent to `HttpApi.ashx`, such as `EncodeStrings`, `DeepObfuscate`, `ReorderCode`, `FlatTransform`, `LockDomain`, and `LockDate`.

Use `reservedNames` to preserve public API names, framework entry points, globals, or names that external code calls. Each entry is a regular expression and is sent to the API as `VariableExclusion`. You can also set `variableExclusion` or `options.VariableExclusion` directly when you need a multiline value.

```json
{
  "preset": "balanced",
  "reservedNames": ["^PublicApi$", "^renderWidget$", "^keep_"],
  "options": {
    "LockDomain": true,
    "LockDomainList": "example.com\napp.example.com",
    "LockDomainMsg": "This script is not licensed for this domain."
  }
}
```

## Online Presets

The online obfuscator can export a JSON preset for Standard options, premium preview features, and the Variable Exclusion List. Use it directly during a dry run or in a CI config:

```bash
npx jso-protector --config jso.config.json --web-preset javascript-obfuscator-preset.json --dry-run --json
```

Or reference it from config:

```json
{
  "webPreset": "javascript-obfuscator-preset.json",
  "input": "dist",
  "output": "dist-protected"
}
```

By default, the CLI ignores source maps, `node_modules`, and `*-obfuscated.js` files. When the output folder is nested under the input folder, reruns also skip that output folder so already protected files are not protected again. The CLI copies non-protected assets such as HTML, CSS, fonts, and images into the output folder. `assetExclude` keeps files such as source maps out of the protected release. Use `--no-copy-assets` or `"copyAssets": false` when another build step already handles assets.

## Commands

```bash
jso-protector --config jso.config.json
jso-protector --config jso.config.cjs
jso-protector --version
jso-protector --version --json
jso-protector src/app.js
jso-protector --input dist --output dist-protected
jso-protector --list-presets --json
jso-protector --list-options --json
jso-protector --list-migration-map --json
jso-protector --explain-compat self-defending --json
jso-protector --local-only --json
jso-protector --migrate-javascript-obfuscator javascript-obfuscator.json --output jso.config.json
jso-protector --migrate-javascript-obfuscator javascript-obfuscator.config.cjs --output jso.config.json
jso-protector --migrate-js-confuser js-confuser.config.cjs --output jso.config.json
jso-protector --config jso.config.json --release-check --json
jso-protector --config jso.config.json --release-check --strict --json
jso-protector --config jso.config.json --validate-config --json
jso-protector --config jso.config.json --print-config --json
jso-protector --init --init-template html-app --manifest dist-protected/jso-manifest.json
jso-protector --init --init-template electron-app --input out --output out-protected
jso-protector --verify-manifest dist-protected/jso-manifest.json
jso-protector --verify-manifest dist-protected/jso-manifest.json --verify-root protected-dist
jso-protector --config jso.config.json --parse-html
jso-protector --config jso.config.json --honor-conditional-comments
jso-protector --config jso.config.json --keep-header-comment --protect-object-declaration --move-nested-function
jso-protector --config jso.config.json --formatted-output --keep-indent --line-numbers
jso-protector --preset maximum --input dist --output dist-protected
jso-protector --web-preset javascript-obfuscator-preset.json --dry-run --json
jso-protector --stdin --stdout --file-name app.js < dist/app.js > dist/app.protected.js
jso-protector --stdin --file-name app.js --output dist-protected < dist/app.js
jso-protector --config jso.config.json --manifest dist-protected/jso-manifest.json
jso-protector --config jso.config.json --max-output-bytes 250000 --max-growth-ratio 8
jso-protector --config jso.config.json --option LockDomain=true --option LockDomainList=example.com
jso-protector --config jso.config.json --include "assets/*.js" --exclude "**/legacy/**" --asset-exclude "**/*.license"
jso-protector --config jso.config.json --reserved-name "^PublicApi$" --reserved-name "^renderWidget$"
jso-protector dist --output dist-protected --options-preset high-obfuscation --control-flow-flattening --string-array-encoding rc4 --reserved-names "^PublicApi$"
jso-protector --dry-run --json
jso-protector --no-copy-assets
jso-protector --init
```

Use `--stdin` for small custom scripts and build steps that already know which single file should be protected. Use `--stdout` to compose with other shell commands, or omit it and pass `--output` to write the result as `output/file-name`.

For direct single-file migration scripts, `jso-protector src/app.js` writes `src/app-obfuscated.js` when no `--output` or config `output` is set. Folder and config-file workflows keep using the configured output folder, usually `dist-protected`.

Use repeatable `--option Name=value` flags to override API options from CI without editing shared config. Values `true`, `false`, `null`, and numbers are parsed into their JSON equivalents; other values are sent as strings.

Use repeatable `--include pattern` flags to replace the configured protected-file include list for one job. Use repeatable `--exclude pattern` and `--asset-exclude pattern` flags to add job-specific file and asset exclusions without removing the config defaults. Use repeatable `--reserved-name pattern` flags to override `reservedNames` for a release job.

When replacing existing `javascript-obfuscator` package scripts, the CLI accepts familiar flags and maps them to hosted API options. Supported mapped compatibility flags include `--options-preset`, `--string-array`, `--string-array-encoding`, `--unicode-escape-sequence`, `--control-flow-flattening`, `--dead-code-injection`, `--dead-code-injection-threshold`, `--identifier-names-generator`, `--rename-globals`, `--rename-properties`, `--reserved-names`, `--domain-lock`, `--target`, and `--compact`. Config and Node API `domainLock` arrays map to `LockDomain` plus newline-delimited `LockDomainList`; review redirect behavior separately. Config-level compatibility fields are also accepted and covered by the shipped JSON schema. `--options-preset default`, `low-obfuscation`, `vm-default`, and `vm-low-obfuscation` map to `standard`; `medium-obfuscation` and `vm-medium-obfuscation` map to `balanced`; `high-obfuscation`, `vm-high-obfuscation`, `vm-ultra-high-obfuscation`, and `vm-anti-llm` map to `maximum`.

The CLI also accepts common review-only flags such as `--source-map`, `--source-map-sources-mode`, `--identifier-names-cache`, `--identifier-names-cache-path`, `--identifiers-dictionary`, `--identifiers-prefix`, `--self-defending`, `--debug-protection`, `--disable-console-output`, `--reserved-strings`, `--rename-properties-mode`, `--numbers-to-expressions`, `--seed`, `--simplify`, `--split-strings`, and string-array wrapper/index flags so older scripts do not fail immediately. `--ignore-imports` is handled directly and preserves import/export-from statements plus common static `require()` and `import()` statements before the remaining code is protected. Other review-only flags emit compatibility warnings when there is no one-to-one hosted API mapping.

Use `--version` for a quick CLI parity check in scripts. Add `--json` to print package metadata and the default hosted API endpoint.

Use `--list-presets` and `--list-options` to inspect available presets and commonly used HTTP API options. Add `--list-migration-map --json` to inspect `javascript-obfuscator` option migration coverage before converting a config. The migration map includes mapped, direct, approximate, review-only, and total known option counts for migration tooling. Use `--explain-compat <option>` when one migrated option needs a human-readable answer before you edit a build script.

Use `--migrate-javascript-obfuscator` to convert a common `javascript-obfuscator` JSON or trusted CommonJS config into a starter `jso.config.json`. The migration report maps familiar options where possible, lists review items for options that are not one-to-one, includes a per-config summary with source option, mapped, review-only, unmapped, and automatic coverage counts, and prints the next validate, dry-run, doctor, release-check, and protect commands. CommonJS source configs execute as Node.js code, so only migrate configs from your own repository or another trusted source.

Use `--list-js-confuser-migration-map --json`, `--explain-js-confuser-compat <option>`, and `--migrate-js-confuser` when migrating from JS-Confuser. The current migration path maps common preset, lock, string, identifier, minify, and control-flow options into the closest hosted API settings and flags runtime lock features such as integrity, self-defending, start-date gates, and countermeasures for manual review.

Use `--release-check --json` as the default CI preflight before protection. It runs config validation, dry-run file planning, and doctor checks in one report without sending source code to the API unless you also pass `--check-api`. The plan includes the number of API items plus any transformed files created from marked HTML scripts or conditional regions. Add `--strict` when warnings, such as unknown option names or not-yet-created output folders, should fail the release job.

Use `--parse-html` when built HTML or template files contain inline scripts that should be protected. Markup files require this explicit mode before protection; the CLI will not send a whole document to the JavaScript API path by accident. Only marked inline blocks are sent to the API:

```html
<script data-javascript-obfuscator>
  window.releaseWidget = createWidget();
</script>
```

Unmarked inline scripts are preserved. Marked external scripts and marked module scripts fail with a clear warning because markup mode protects inline script contents only; protect referenced files directly through the JavaScript build pipeline. When `parseHtml` is enabled, the CLI automatically adds the configured `markupExtensions` list to protected extensions. By default that includes `.html`, `.htm`, `.php`, `.phtml`, `.asp`, `.aspx`, `.jsp`, `.cshtml`, and `.vbhtml`.

Use `--honor-conditional-comments` to preserve source regions marked with `javascript-obfuscator:disable` and `javascript-obfuscator:enable`. Without the flag, the CLI fails with a clear warning when markers are found so protected regions are not sent unexpectedly. With the flag, enabled regions are sent as separate API items and disabled regions are copied back verbatim. Markers must be balanced; an unmatched `disable`, unmatched `enable`, or nested disabled region fails before any source is sent.

```js
console.log("protect this");
// javascript-obfuscator:disable
console.log("keep this exact local code");
// javascript-obfuscator:enable
console.log("protect this too");
```

Use `--local-only` when source cannot leave the build machine or when a teammate needs a quick reminder of the local package install path. The npm CLI can still run validation, dry-run planning, doctor checks, and release-check reports locally, but actual npm CLI protection uses the hosted HTTP API. Use the desktop/local workflow for fully local protection. The command also reports that this package is local-only, not published to npm, and should be installed from a workspace path, `file:` dependency, or internal `npm pack` tarball.

Use `--validate-config --json` to check config shape, endpoint format, credentials presence, credential storage, path settings, budget values, and local option-name references without sending source code to the API. Unknown option names are reported as warnings so teams can catch typos while still using advanced HTTP API options. Inline `apiKey`, `apiPassword`, `--api-key`, and `--api-password` values also warn; prefer `$JSO_API_KEY` and `$JSO_API_PASSWORD` environment references so dashboard credentials do not land in committed config, shell history, or process logs.

Use `--print-config --json` to inspect the final resolved config after presets, config files, environment variables, and CLI overrides are merged. API credentials are redacted as `[set]` or `[missing]`.

Use `--manifest file.json` or `"manifest": "file.json"` to write a release manifest after a successful protection run. The manifest includes project metadata, preset, enabled option names, processing metadata for virtual API items, protected file sizes and SHA-256 hashes, and copied asset hashes.

Use `--verify-manifest file.json` to verify that a protected output folder or unpacked release artifact still matches the manifest hashes. By default, verification reads each entry's recorded `outputPath`. Add `--verify-root path/to/artifact` when the protected files were moved into a different folder after the manifest was written; verification then resolves each manifest `fileName` under that alternate root.

Use `--max-output-bytes` / `"maxOutputBytes"` and `--max-growth-ratio` / `"maxGrowthRatio"` to fail CI when protected output exceeds your release size budget. The same budget fields work in bundle plugins.

## Package Verification

Before publishing or tagging a release, run the install verification smoke:

```bash
npm run verify:package
```

The verifier packs the package, installs the tarball into a fresh temporary project, checks root and subpath exports, checks shipped example syntax/config JSON, runs the CLI through the installed bin link, and verifies the direct single-file output default.

## Security and Processing

See `SECURITY.md` for the source-processing matrix, CI secret guidance, source-map policy, manifest metadata notes, and local-only workflow guidance. Use `--release-check`, `--dry-run`, `--validate-config`, and `--doctor` when you need preflight checks before sending source code to the hosted API.

## Live API Smoke Test

After setting `JSO_API_KEY` and `JSO_API_PASSWORD`, run a tiny live API check from this package directory:

```bash
npm run smoke:api
```

The command protects the sample file in `dist/` and writes the result to `dist-protected/`. Use `npm run smoke` when you only want offline validation and a dry run.

## Doctor

Run `release-check` before a release job when you want one CI-friendly report that combines config validation, dry-run file planning, and doctor checks:

```bash
jso-protector --config jso.config.json --release-check
jso-protector --config jso.config.json --release-check --json
jso-protector --config jso.config.json --release-check --strict --json
```

Run `validate-config` first when you want a fast static check:

```bash
jso-protector --config jso.config.json --validate-config
jso-protector --config jso.config.json --validate-config --json
jso-protector --config jso.config.json --validate-config --strict --json
```

Run `doctor` before a release job to check config, credentials, paths, matched files, copied assets, output readiness, presets, and enabled options without sending source code to the API:

```bash
jso-protector --config jso.config.json --doctor
jso-protector --config jso.config.json --doctor --json
```

Add `--check-api` to `release-check` or `doctor` when CI should send a tiny live request to verify the endpoint and credentials:

```bash
jso-protector --config jso.config.json --release-check --check-api --json
jso-protector --config jso.config.json --doctor --check-api --json
```

## Examples and CI Templates

Copyable examples are included in `examples/`:

- `examples/cli-basic`
- `examples/node-api`
- `examples/browserify`
- `examples/metro.config.js`
- `examples/nextjs`
- `examples/rspack`
- `examples/esbuild`
- `examples/vite`
- `examples/rollup`
- `examples/webpack`
- `examples/webpack-loader`
- `examples/gulp`
- `examples/grunt`
- `examples/react-native/metro.config.js`

`examples/node-api/release-summary.js` is the most complete custom-script starting point: it validates config, prints a dry release plan, protects files, and reports written files, copied assets, and manifest output.

CI templates are included in `ci/`:

- `ci/github-actions.yml`
- `ci/gitlab-ci.yml`
- `ci/azure-pipelines.yml`

The templates run `npm run verify:publish-metadata --if-present` so package projects can confirm the local-only metadata policy while application projects can ignore the optional script.

## Migrating from javascript-obfuscator

See `MIGRATION.md` when replacing the open-source `javascript-obfuscator` package or one of its bundler plugins with the hosted JavaScript Obfuscator API workflow. It covers command changes, concept mapping, direct Node API replacement, bundle-plugin replacement, and a migration checklist.

For JS-Confuser projects, the same package now includes `--migrate-js-confuser`, `--list-js-confuser-migration-map`, `--explain-js-confuser-compat`, and `translateJsConfuserOptions(...)` so you can generate a starter `jso.config.json` and review the runtime-only gaps before switching release pipelines.

## GitHub Actions

Store `JSO_API_KEY` and `JSO_API_PASSWORD` as encrypted repository or organization secrets, then run release preflight and protection after your normal frontend build:

```yaml
name: protected-release

on:
  workflow_dispatch:

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npm run build
      - run: npm run verify:package --if-present
      - run: npm run verify:publish-metadata --if-present
      - name: Release preflight
        run: npx jso-protector --config jso.config.json --release-check --json
        env:
          JSO_API_KEY: ${{ secrets.JSO_API_KEY }}
          JSO_API_PASSWORD: ${{ secrets.JSO_API_PASSWORD }}
      - name: Protect JavaScript
        run: npx jso-protector --config jso.config.json --manifest dist-protected/jso-manifest.json
        env:
          JSO_API_KEY: ${{ secrets.JSO_API_KEY }}
          JSO_API_PASSWORD: ${{ secrets.JSO_API_PASSWORD }}
      - run: npm run smoke
      - uses: actions/upload-artifact@v4
        with:
          name: protected-dist
          path: |
            dist-protected/
            dist-protected/jso-manifest.json
```

## Release Checklist

1. Build unprotected JavaScript into a temporary output folder.
2. Run `jso-protector --release-check --json` to validate config, confirm the file list, and check paths, assets, output readiness, presets, and enabled options before source is sent.
3. Protect into a separate output folder such as `dist-protected` and write `dist-protected/jso-manifest.json`.
4. Run browser smoke tests against the protected output.
5. Run `jso-protector --verify-manifest dist-protected/jso-manifest.json` before publishing or after unpacking release artifacts.
6. Publish only the protected artifacts and release manifest.

## Notes

- The CLI sends file contents to the configured HTTP API endpoint.
- Use the desktop app instead when your policy requires local-only handling.
- The HTTP API is for paid accounts.
- The CLI expects the API response shape used by `HttpApi.ashx`: `Type`, `Items`, `Message`, `FileName`, and optional error fields.
