javascript-obfuscator 5.5.1 → 5.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  Change Log
2
2
 
3
+ v5.7.0
4
+ ---
5
+ * **New option:** `advertisement` allows to control the display of the JavaScript Obfuscator Pro advertisement message in the console. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1448
6
+
7
+ v5.6.0
8
+ ---
9
+ * Pro API: `obfuscatePro` now fall back to the basic local obfuscation API when no Pro feature (`vmObfuscation` or `parseHtml`) is enabled, instead of throwing an `ApiError`
10
+ * Improved `stringArrayEncoding` runtime performance
11
+
3
12
  v5.5.1
4
13
  ---
5
14
  * Fixed leading whitespace of the source code being stripped without being reflected in the source map, shifting all `sourcesContent` and original position mappings. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1437
package/README.md CHANGED
@@ -348,7 +348,7 @@ console.log(result.getObfuscatedCode());
348
348
  **Parameters:**
349
349
 
350
350
  * `sourceCode` (`string`) – source code to obfuscate
351
- * `options` (`Object`) – obfuscation options. **Must include at least one Pro feature: `vmObfuscation: true` or `parseHtml: true`**
351
+ * `options` (`Object`) – obfuscation options. To use the Pro API, include at least one Pro feature: `vmObfuscation: true` or `parseHtml: true`. When no Pro feature is enabled, `obfuscatePro` falls back to the basic (local) obfuscation API.
352
352
  * `apiConfig` (`Object`) – Pro API configuration:
353
353
  * `apiToken` (`string`, required) – your API token from obfuscator.io
354
354
  * `timeout` (`number`, optional) – request timeout in ms (default: `300000` - 5 minutes)
@@ -357,8 +357,9 @@ console.log(result.getObfuscatedCode());
357
357
 
358
358
  **Returns:** `Promise<ObfuscationResult>`
359
359
 
360
+ > **Note:** When no Pro feature (`vmObfuscation` or `parseHtml`) is enabled, the Pro API is not needed, so `obfuscatePro` falls back to the basic (local) obfuscation API and no API request is made.
361
+
360
362
  **Throws:** `ApiError` if:
361
- - No Pro features (`vmObfuscation` or `parseHtml`) are enabled in options
362
363
  - API token is invalid or expired
363
364
  - API request fails
364
365
 
@@ -554,6 +555,7 @@ Following options are available for the JS Obfuscator:
554
555
 
555
556
  ```javascript
556
557
  {
558
+ advertisement: true,
557
559
  compact: true,
558
560
  controlFlowFlattening: false,
559
561
  controlFlowFlatteningThreshold: 0.75,
@@ -617,6 +619,7 @@ Following options are available for the JS Obfuscator:
617
619
 
618
620
  -o, --output
619
621
 
622
+ --advertisement <boolean>
620
623
  --compact <boolean>
621
624
  --config <string>
622
625
  --control-flow-flattening <boolean>
@@ -634,6 +637,7 @@ Following options are available for the JS Obfuscator:
634
637
  --identifier-names-generator <string> [dictionary, hexadecimal, mangled, mangled-shuffled]
635
638
  --identifiers-dictionary '<list>' (comma separated)
636
639
  --identifiers-prefix <string>
640
+ --random-identifiers-prefix <boolean>
637
641
  --ignore-imports <boolean>
638
642
  --log <boolean>
639
643
  --numbers-to-expressions <boolean>
@@ -667,6 +671,7 @@ Following options are available for the JS Obfuscator:
667
671
  --string-array-wrappers-type <string> [variable, function]
668
672
  --string-array-threshold <number>
669
673
  --target <string> [browser, browser-no-eval, node]
674
+ --browser-environment <string> [http, https]
670
675
  --transform-object-keys <boolean>
671
676
  --unicode-escape-sequence <boolean>
672
677
  --pro-api-token <string>
@@ -678,6 +683,7 @@ Following options are available for the JS Obfuscator:
678
683
  --vm-target-functions '<list>' (comma separated)
679
684
  --vm-exclude-functions '<list>' (comma separated)
680
685
  --vm-target-functions-mode <string> [root, comment]
686
+ --vm-force-compile-dynamic-code <boolean>
681
687
  --vm-wrap-top-level-initializers <boolean>
682
688
  --vm-opcode-shuffle <boolean>
683
689
  --vm-bytecode-encoding <boolean>
@@ -691,12 +697,19 @@ Following options are available for the JS Obfuscator:
691
697
  --vm-split-dispatcher <boolean>
692
698
  --vm-macro-ops <boolean>
693
699
  --vm-debug-protection <boolean>
700
+ --vm-self-defending <boolean>
694
701
  --vm-runtime-opcode-derivation <boolean>
695
702
  --vm-stateful-opcodes <boolean>
703
+ --vm-async-executor <boolean>
704
+ --vm-call-context-opcodes <boolean>
696
705
  --vm-stack-encoding <boolean>
697
706
  --vm-randomize-keys <boolean>
698
707
  --vm-indirect-dispatch <boolean>
699
708
  --vm-compact-dispatcher <boolean>
709
+ --vm-register-based <boolean>
710
+ --vm-string-array-bytecode-only <boolean>
711
+ --vm-domain-lock '<list>' (comma separated)
712
+ --vm-domain-lock-redirect-url <string>
700
713
  --vm-bytecode-format <string> [binary, json]
701
714
  --parse-html <boolean>
702
715
  --strict-mode <boolean>
@@ -704,6 +717,13 @@ Following options are available for the JS Obfuscator:
704
717
 
705
718
  <!-- ##options-start## -->
706
719
 
720
+ ### `advertisement`
721
+ Type: `boolean` Default: `true`
722
+
723
+ Allows to control the display of the JavaScript Obfuscator Pro advertisement message in the console.
724
+
725
+ The message is only shown when using the Node.js CLI in an interactive (TTY) terminal, is never shown in CI environments or in the browser, and is limited to a few displays. Set this option to `false` to disable the advertisement message completely.
726
+
707
727
  ### `compact`
708
728
  Type: `boolean` Default: `true`
709
729
 
@@ -1968,6 +1988,35 @@ function outer() {
1968
1988
 
1969
1989
  **When to use:** When you need surgical control over exactly which functions get VM protection, especially nested functions that contain sensitive logic. Unlike `vmTargetFunctions` which only works with root-level named functions, comment mode lets you protect any function anywhere in your code.
1970
1990
 
1991
+ ### `vmForceCompileDynamicCode`
1992
+ Type: `boolean` Default: `false`
1993
+
1994
+ Controls what VM obfuscation does with a function that contains a direct `eval`, `new Function(...)`, or `Function(...)` call.
1995
+
1996
+ By default, such a function (and every function defined inside it) is skipped from VM bytecoding and a `VMDynamicCodeSkipped` warning is reported on `result.getWarnings()`. This is because the runtime-built source may reference identifiers from the surrounding scope chain — identifiers that the obfuscator has renamed.
1997
+
1998
+ When set to `true`, the function is bytecoded anyway and the `VMDynamicCodeSkipped` warning is no longer emitted.
1999
+
2000
+ The separate `DynamicCodeRenameRisk` warning continues to fire regardless of this option, because the rename risk it describes is independent of the VM skip — turning this option on does not make the underlying pattern any safer.
2001
+
2002
+ ```javascript
2003
+ // Source code
2004
+ function loadConfig(src) {
2005
+ return eval(src);
2006
+ }
2007
+ loadConfig('1 + 2');
2008
+ ```
2009
+
2010
+ ```javascript
2011
+ // Options
2012
+ {
2013
+ vmObfuscation: true,
2014
+ vmForceCompileDynamicCode: true
2015
+ }
2016
+ ```
2017
+
2018
+ With the option off (default), `loadConfig` is left as plain JavaScript. With the option on, `loadConfig` is compiled to VM bytecode like any other function. Use this when you have audited the call site and know the runtime-built code does not depend on closure-renamed identifiers.
2019
+
1971
2020
  ### `vmWrapTopLevelInitializers`
1972
2021
  Type: `boolean` Default: `false`
1973
2022
 
@@ -2065,6 +2114,44 @@ JavaScriptObfuscator.obfuscate(code, {
2065
2114
  window.__VM_KEY__ = 'mySecretKey123';
2066
2115
  ```
2067
2116
 
2117
+ ### `vmAsyncExecutor`
2118
+ Type: `boolean` Default: `false`
2119
+
2120
+ Enables the asynchronous VM executor, which lets [`vmBytecodeArrayEncodingKeyGetter`](#vmbytecodearrayencodingkeygetter) return a `Promise` (an **asynchronous** key getter) — so the decryption key can be fetched at runtime (network request, IndexedDB, etc.) instead of having to be available synchronously when the code loads.
2121
+
2122
+ **Heavily recommended for fully async codebases.** In this mode only `async` functions are virtualized — a synchronous function can't be made async without turning its return value into a `Promise` and breaking its callers — so code that is `async` throughout gets the most coverage. It still works when the root is synchronous (e.g. a sync IIFE / UMD wrapper): the outermost `async` functions inside are protected, and the sync parts are left as-is.
2123
+
2124
+ **What gets transformed:** every **outermost** `async` function, wherever it appears (including nested inside sync wrappers). The outermost async in each chain is the protected unit — everything inside it, sync and async, is compiled in. Synchronous functions and plain generators are left unobfuscated.
2125
+ ```js
2126
+ function foo() { // sync — left as-is
2127
+ function bar() {} // sync — left as-is
2128
+
2129
+ async function baz() { // transformed
2130
+ // any code here, including calls to other async or sync functions
2131
+ }
2132
+
2133
+ async function bark() { // transformed
2134
+ // any code here, including calls to other async or sync functions
2135
+ }
2136
+ }
2137
+ ```
2138
+
2139
+ **Skips and warnings.** Async generators are also left unobfuscated when an asynchronous key getter is active (an async generator must return its iterator synchronously and can't wait for the key). In the default `vmTargetFunctionsMode: 'root'` skips are silent (selection is automatic); in `comment` [mode](#vmtargetfunctionsmode) a warning is emitted through `ObfuscationResult.getWarnings()` whenever a function you explicitly marked can't be virtualized — it turned out synchronous, or it's an async generator under an async key getter.
2140
+
2141
+ The asynchronous key getter additionally requires `vmBytecodeArrayEncoding` with a `vmBytecodeArrayEncodingKeyGetter`.
2142
+
2143
+ **Usage example:**
2144
+ ```ts
2145
+ JavaScriptObfuscator.obfuscate(code, {
2146
+ vmObfuscation: true,
2147
+ vmAsyncExecutor: true,
2148
+ vmBytecodeArrayEncoding: true,
2149
+ vmBytecodeArrayEncodingKey: 'mySecretKey123',
2150
+ // the key getter may now return a Promise
2151
+ vmBytecodeArrayEncodingKeyGetter: 'fetch("/vm-key").then((res) => res.text())'
2152
+ });
2153
+ ```
2154
+
2068
2155
  ### `vmJumpsEncoding`
2069
2156
  Type: `boolean` Default: `false`
2070
2157
 
@@ -2099,11 +2186,137 @@ Adds multi-layered tamper detection, anti-hooking, and anti-reverse-engineering
2099
2186
 
2100
2187
  Strongly recommended to use together with [`vmDebugProtection`](#vmDebugProtection), [`vmBytecodeArrayEncodingKey`](#vmbytecodeArrayEncodingKey), and [`vmBytecodeArrayEncodingKeyGetter`](#vmbytecodeArrayEncodingKeyGetter).
2101
2188
 
2189
+ ### `vmDefenseHook`
2190
+ Type: `{ name: string, aliases?: object }` Default: `''`
2191
+
2192
+ `vmDefenseHook` takes an object with two keys: **`name`** (required) and **`aliases`** (optional).
2193
+
2194
+ `name` is a **global function your host page defines** that a VM defense (`vmDebugProtection` / `vmSelfDefending`) calls with a signal object when it detects a hostile signal — a debugger or inspector, a headless / automation browser, an AI-coding-agent process, a disallowed domain, and so on. Use it to report the event to your backend (e.g. `navigator.sendBeacon`). The hook is a **pure telemetry sink**: its return value is ignored, and a missing or throwing hook is a silent no-op that can never disable a defense. To change what a defense *does* on detection, use [`vmDefenseReaction`](#vmdefensereaction).
2195
+
2196
+ `aliases` optionally renames the fields of that signal object — covered under *Renaming the signal fields* below.
2197
+
2198
+ **The signal object.** The hook receives a single `signal`:
2199
+
2200
+ - `source` — the specific detector that fired (see the table).
2201
+ - `category` — the group it reports under: `automation` (non-human browsers), `debugger` (a debugger/inspector is active), `sandbox` (instrumented/fake host), `domain` (domain-lock violation), `tamper` (built-ins patched at runtime), or `integrity` (the VM's own code was altered).
2202
+ - `score` / `threshold` — how strongly the detector fired and the value it had to reach; the hook fires only once `score >= threshold`. Most checks are all-or-nothing (a single decisive signal); `headless` sums several browser-shape signals, so its `score` is typically higher than its `threshold`.
2203
+
2204
+ | `source` | detects | `category` |
2205
+ |---|----------------------------------------------------------------------------------|---|
2206
+ | `integrity` | the obfuscated VM code itself has been modified | `integrity` |
2207
+ | `node` | browser-targeted code running under Node.js | `debugger` |
2208
+ | `debugger` | an attached or active debugger or inspector session, or a debug environment | `debugger` |
2209
+ | `headless` | a headless browser is used to run the code | `automation` |
2210
+ | `agent` | an AI coding-agent running the code | `automation` |
2211
+ | `timing` | an execution pause suggesting a breakpoint or a stepping debugger | `debugger` |
2212
+ | `sandbox` | the code runs in a sandbox or a faked host environment | `sandbox` |
2213
+ | `domain` | the page origin is not in the [`vmDomainLock`](#vmdomainlock) allow-list | `domain` |
2214
+ | `nativeHook` | a native built-in function has been replaced or hooked | `tamper` |
2215
+
2216
+ **Registering the hook.** Define it as a plain global **before** the obfuscated bundle loads — the VM runtime and its defenses run before your (protected) program, so many detections fire during startup:
2217
+
2218
+ ```js
2219
+ // in your page, before the obfuscated script:
2220
+ window.__vmDetection = function (signal) { navigator.sendBeacon('/vm-defense', JSON.stringify(signal)); };
2221
+ ```
2222
+ ```js
2223
+ // obfuscation option:
2224
+ vmDefenseHook: { name: '__vmDetection' }
2225
+ ```
2226
+
2227
+ > A hook defined *inside* the obfuscated source is registered too late to catch startup-time detections, and if it gets VM-compiled it can't be reached until your program runs. It's kept safe either way (a missing hook no-ops, and a re-entrancy guard prevents any runaway), but for complete coverage register it up front. To still protect your reporting logic, keep the registered hook a one-line buffer (`(window.__vmDet = window.__vmDet || []).push(signal)`) and read/send that buffer from your obfuscated code.
2228
+
2229
+ **Renaming the signal fields (`aliases`).** The default `source`/`category` values are descriptive names, so anyone instrumenting the callback (or reading the output) can recognise the protection and which detector fired. `aliases` renames signal fields to opaque tokens of your choice, applied inside the VM *before* the signal is emitted, so those names never appear in the output or reach the callback. Your app knows its own mapping and forwards the tokens to your backend.
2230
+
2231
+ Aliases are per field, keeping key and value renames separate: each field takes a `key` (the property name the callback receives); the string name-fields `source` and `category` also take a `values` map, while `score`/`threshold` are numbers and take only a `key`. The names you can map (anything else is rejected at build time):
2232
+
2233
+ - **field keys** — `source`, `category`, `score`, `threshold`
2234
+ - **`source` values** — `headless`, `agent`, `node`, `debugger`, `timing`, `sandbox`, `domain`, `nativeHook`, `integrity`
2235
+ - **`category` values** — `automation`, `debugger`, `sandbox`, `domain`, `tamper`, `integrity`
2236
+
2237
+ ```js
2238
+ vmDefenseHook: {
2239
+ name: '__vmDetection',
2240
+ aliases: {
2241
+ source: { key: 'a8Qm', values: { headless: 'xP4m9Q' } },
2242
+ category: { key: 'p3Tx', values: { automation: 'bQ7s1M' } },
2243
+ score: { key: 's1' },
2244
+ threshold: { key: 't1' }
2245
+ }
2246
+ // the callback now receives e.g. { a8Qm: 'xP4m9Q', p3Tx: 'bQ7s1M', s1: <score>, t1: <threshold> }
2247
+ }
2248
+ ```
2249
+
2250
+ This is fingerprint avoidance, not secrecy — the mapping can still be inferred by repeated testing — so its only benefit is not exposing stable, self-explanatory names. Unset entries keep their default names.
2251
+
2252
+ > A bare string (`vmDefenseHook: '__vmDetection'`) is accepted as shorthand for `{ name: '__vmDetection' }` but is **deprecated** — prefer the object form.
2253
+
2254
+ ### `vmDefenseReaction`
2255
+ Type: `object` Default: `{ automation: 'break', debugger: 'decoy', sandbox: 'decoy', domain: 'break', tamper: 'break', integrity: 'break' }`
2256
+
2257
+ Configures how each detection category reacts. It does **not** enable anything — the defenses themselves are turned on by [`vmSelfDefending`](#vmselfdefending), [`vmDebugProtection`](#vmdebugprotection), and [`vmDomainLock`](#vmdomainlock); this option only selects *how* an enabled defense reacts. The **category is the unit of control** — every detector in a category enacts that category's reaction.
2258
+
2259
+ Each **category** groups the detectors that watch for one kind of hostile condition. A category only reacts when the option that emits its detectors is enabled:
2260
+
2261
+ | Category | Enabled by | Reacts when |
2262
+ |---|---|---|
2263
+ | `automation` | `vmSelfDefending` or `vmDebugProtection` | The code is being driven by software instead of a person: a headless or automated browser, a scraping / testing framework, or an AI coding-agent stepping the page. |
2264
+ | `debugger` | `vmDebugProtection` or `vmSelfDefending` | Someone has a debugger or the browser's developer-tools inspector open and is stepping through the running code to understand it. |
2265
+ | `sandbox` | `vmDebugProtection` | The code is not running in a real browser at all — it has been lifted into an emulated or scripted JavaScript environment to be executed and studied offline. |
2266
+ | `domain` | `vmDomainLock` | The code is running on a site you did not authorize: a host not in your [`vmDomainLock`](#vmdomainlock) allow-list (for example, your bundle copied onto someone else's domain). |
2267
+ | `tamper` | `vmSelfDefending` | The JavaScript environment around the VM has been modified to watch or hijack it, such as native browser built-ins swapped out for instrumented versions. |
2268
+ | `integrity` | `vmSelfDefending` | The protected bundle's own code has been edited or patched since you generated it. |
2269
+
2270
+ Every category maps to one or more of [`vmSelfDefending`](#vmselfdefending), [`vmDebugProtection`](#vmdebugprotection), and [`vmDomainLock`](#vmdomainlock); there is no category outside those three options, and a reaction set for a category whose option is off simply has no effect.
2271
+
2272
+ Keys are these six category names, or `default` (a fallback for unspecified categories). Values are:
2273
+
2274
+ - `break` — break immediately
2275
+ - `decoy` — keep running on poisoned state, silently producing wrong results
2276
+ - `none` — do nothing locally (telemetry only)
2277
+
2278
+ The per-category defaults are shown above; a category you don't set (or set to its default value) uses that default. `default` reaches **every** category, including the correct-by-construction ones (`integrity`, `tamper`), so `{ default: 'none' }` is a genuinely non-breaking, telemetry-only build:
2279
+
2280
+ ```js
2281
+ vmDefenseReaction: { default: 'none' } // never break — pair with vmDefenseHook
2282
+ vmDefenseReaction: { automation: 'none', domain: 'break' } // tolerate automation FPs, still break on a bad domain
2283
+ ```
2284
+
2285
+ ### `browserEnvironment`
2286
+ Type: `object` Default: `{}`
2287
+
2288
+ Declares facts about the environment your production build is served in, so the protected code can bind itself to them. Only takes effect together with [`vmSelfDefending`](#vmselfdefending), and only for `browser` / `browser-no-eval` / `service-worker` targets — it is rejected for `node`, `userscript`, and `bytenode`.
2289
+
2290
+ Currently one field:
2291
+
2292
+ - **`transport`** — the scheme your production serves the bundle over: `'http'` or `'https'`. With `'https'`, the build ties its integrity to being served over HTTPS, so a copy an analyst lifts and serves over plain HTTP (a common local reverse-engineering setup) will not run correctly. `'http'` or an unset field adds no binding.
2293
+
2294
+ ```js
2295
+ browserEnvironment: { transport: 'https' }
2296
+ ```
2297
+
2298
+ > :warning: A build declared `transport: 'https'` runs correctly **only** where it is actually served over `https:`. Every other scheme corrupts it, so declare it only when every context that loads your production build is HTTPS. That excludes: plain `http://` (including `http://localhost` in development), `file://` (Electron / Cordova / packaged apps), and `blob:` / `about:` embeddings (a bundle running inside an `about:blank` or `srcdoc` iframe). A client-side HTTP→HTTPS redirect still renders the HTTP page first, so the bundle must not run before the redirect completes.
2299
+
2102
2300
  ### `vmStatefulOpcodes`
2103
2301
  Type: `boolean` Default: `false`
2104
2302
 
2105
2303
  Makes opcode meanings depend on position in the bytecode. Each position has a different opcode-to-handler mapping derived from a seed, so the same opcode number performs different operations at different positions.
2106
2304
 
2305
+ ### `vmCallContextOpcodes`
2306
+ Type: `boolean` Default: `false`
2307
+
2308
+ Makes a protected function depend on where it's called from, so it can't be lifted out of the code and run or analyzed on its own — it only behaves correctly when invoked through its real call sites in the program. This option affects runtime performance.
2309
+
2310
+ Currently only the following constructions are supported:
2311
+
2312
+ - function declarations (`function f() {}`);
2313
+ - function expressions and arrow functions assigned to a variable (`const f = () => {}`);
2314
+ - instance private methods (`this.#m()`).
2315
+
2316
+ In every case the function must always be reached through a direct call (`f()`, `this.#m()`). If it is stored in another variable, passed as an argument, or otherwise used as a value, it is left unprotected. Async functions are supported; generators are not.
2317
+
2318
+ This option is experimental and may break your code, so test the output thoroughly before using it.
2319
+
2107
2320
  ### `vmStackEncoding`
2108
2321
  Type: `boolean` Default: `false`
2109
2322
 
@@ -2119,6 +2332,17 @@ Uses a single VM executor instead of dual executors (sync + generator). Reduces
2119
2332
  - `false` (default): dual executors — optimal performance, larger output
2120
2333
  - `true`: single executor — smaller output, slightly slower
2121
2334
 
2335
+ ### `vmRegisterBased`
2336
+ Type: `boolean` Default: `false`
2337
+
2338
+ Switches the VM from the default stack-based bytecode to a register-based execution model, which improves VM runtime performance in some cases by ~15-20%, but slightly increases obfuscated code size.
2339
+
2340
+ Because it emits a structurally different bytecode and executor, it also gives the VM a more unique fingerprint than the default stack-based one — use it when you want to vary the VM's shape so it is less recognizable to generic analysis.
2341
+
2342
+ Under the hood this isn't a native register-based compiler — the regular stack-based compiler still generates the bytecode, which a separate transformation stage then rewrites into register-based form.
2343
+
2344
+ This option is experimental - test that your code runs well when `vmRegisterBased` is enabled.
2345
+
2122
2346
  ### `vmStringArrayBytecodeOnly`
2123
2347
  Type: `boolean` Default: `false`
2124
2348
 
@@ -2131,6 +2355,25 @@ When enabled, the string array will **only** extract strings from bytecode data
2131
2355
  - `stringArrayThreshold` still controls what percentage of those bytecode strings are extracted
2132
2356
 
2133
2357
 
2358
+ ### `vmDomainLock`
2359
+ Type: `string[]` Default: `[]`
2360
+
2361
+ ##### :warning: This option does not work with `target: 'node'`, `target: 'service-worker'` or `target: 'bytenode'`
2362
+
2363
+ Restricts the obfuscated code to specific domains and/or sub-domains, and is much harder to locate and strip than `domainLock`.
2364
+
2365
+ If the source code isn't run on the domains specified by this option, the browser will be redirected to the URL passed to [`vmDomainLockRedirectUrl`](#vmdomainlockredirecturl), and further protected calls will return incorrect results even if the redirect is suppressed.
2366
+
2367
+ ##### Multiple domains and sub-domains
2368
+ It's possible to lock your code to more than one domain or sub-domain. For instance, to lock it so the code only runs on **www.example.com** add `www.example.com`. To make it work on the root domain including any sub-domains (`example.com`, `sub.example.com`), use `.example.com`.
2369
+
2370
+ ### `vmDomainLockRedirectUrl`
2371
+ Type: `string` Default: `about:blank`
2372
+
2373
+ ##### :warning: This option does not work with `target: 'node'`, `target: 'service-worker'` or `target: 'bytenode'`
2374
+
2375
+ Allows the browser to be redirected to a passed URL if the source code isn't run on the domains specified by [`vmDomainLock`](#vmdomainlock).
2376
+
2134
2377
  ### `strictMode`
2135
2378
  Type: `boolean | null` Default: `null`
2136
2379
 
@@ -2197,6 +2440,15 @@ JavaScriptObfuscator.obfuscate(html, {
2197
2440
  // output: HTML with only the marked script obfuscated
2198
2441
  ```
2199
2442
 
2443
+ ### `randomIdentifiersPrefix`
2444
+ Type: `boolean` Default: `false`
2445
+
2446
+ Appends a seeded random prefix (6 alphanumeric characters) to all global identifiers. Use this option to avoid collisions between separately obfuscated bundles that are loaded into the same global scope — it removes the need to pick a unique `identifiersPrefix` per bundle manually.
2447
+
2448
+ - The random value is derived from the `seed` option and the source code hash, so reproducible builds with the same seed produce the same prefix.
2449
+ - When combined with `identifiersPrefix`, the random characters are appended to the user-provided prefix (e.g. `myApp` + random `aBc123` → `myAppaBc123`).
2450
+ - When combined with `vmObfuscation`, the random value replaces the default `vm` prefix — randomness already guarantees uniqueness.
2451
+
2200
2452
  ## Frequently Asked Questions
2201
2453
 
2202
2454
  ### What javascript versions are supported?