unpeek 1.0.0 → 1.0.1

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Efe Arabacı & unpeek contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Efe Arabacı & unpeek contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,246 +1,246 @@
1
- # unpeek
2
-
3
- **Zero-config, call-graph-aware polymorphic JavaScript protection and runtime anti-tamper toolkit with <2% CPU overhead.**
4
-
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-emerald.svg)](LICENSE)
6
- [![CPU Overhead](https://img.shields.io/badge/CPU_Overhead-%3C2%25-10b981.svg)](packages/unpeek/benchmarks/overhead.bench.ts)
7
- [![Gzip Delta](https://img.shields.io/badge/Gzip_Delta-%3C15%25-06b6d4.svg)](packages/unpeek/benchmarks/overhead.bench.ts)
8
- [![Interactive Playground](https://img.shields.io/badge/Playground-Live_Demo-6366f1.svg)](https://woffluon.github.io/unpeek)
9
-
10
- English | [Türkçe](./README.tr.md) · [Live Interactive Playground](https://woffluon.github.io/unpeek) · [Agent Architecture Guide](./AGENTS.md) · [AI Skill](./skills/unpeek/SKILL.md)
11
-
12
- ---
13
-
14
- <baglam>
15
-
16
- ## Why unpeek?
17
-
18
- Shipping a modern web application in Next.js, Vite, or Webpack exposes client-side business logic, internal API endpoints, feature flags, and proprietary algorithms to anyone who opens Chrome DevTools or runs an automated AST deobfuscator (`webcrack`, `synchrony`).
19
-
20
- Existing tools force an uncomfortable trade-off:
21
- - **Standard minifiers (`esbuild`, `SWC`, `Terser`)** compress whitespace and shorten variable names, leaving string literals, endpoint URLs, and control flow readable after a single click on the DevTools Pretty-Print (`{}`) button.
22
- - **Legacy AST obfuscators (`javascript-obfuscator`)** apply indiscriminate control-flow flattening, `eval` traps, and eager string-array rotation to every function in the bundle. This inflates bundle sizes by 40–300%, breaks V8 TurboFan JIT inlining inside 60fps UI rendering loops, and triggers false-positive browser freezes.
23
-
24
- `unpeek` resolves this trade-off by combining static call-graph analysis with polymorphic compile-time encryption:
25
-
26
- | Capability | Standard Minifiers (`Terser` / `SWC`) | Legacy AST Obfuscators | `unpeek` |
27
- | :--- | :--- | :--- | :--- |
28
- | **Runtime CPU Overhead** | ~0% | 15% – 85% (throttles UI loops) | **< 2.0%** (Call-Graph Hot-Path Exemption) |
29
- | **Gzip Bundle Growth** | 0% (Baseline) | +45% – +300% | **< 15%** (O(1) Lazy String Vault) |
30
- | **String & Endpoint Encryption** | Plaintext in bundle | Eager array rotation (high CPU) | **Polymorphic XOR + O(1) Memoized Vault** |
31
- | **DevTools Pretty-Print (`{}`) Defense** | None | `debugger` infinite loops (easily bypassed) | **`Function.prototype.toString()` FNV-1a Integrity** |
32
- | **Source Map Isolation** | Manual CI scripts | Manual configuration | **Automatic `.unpeek-maps/` Vault + Comment Strip** |
33
- | **Static Deobfuscator Resistance** | None | Fixed templates (fingerprinted by `webcrack`) | **Per-Build Polymorphic AST Seed** |
34
- | **SSR / Next.js Compatibility** | Native | Frequently breaks server hydration | **Client-Only Auto-Detection + SSR Guard** |
35
-
36
- ### Core Architecture
37
-
38
- 1. **Call-Graph Hot-Path Exemption (`<2%` CPU Budget)**
39
- Before modifying a single AST node, [`detectHotPaths`](packages/unpeek/src/ast/index.ts) traverses the file's call graph. Functions containing loops (`for`, `while`, `do-while`, `for-of`), array higher-order callbacks (`.map`, `.filter`, `.reduce`, `.forEach`), `requestAnimationFrame` tickers, and React/UI render components (`useState`, `useEffect`, `createElement`) are flagged as hot paths. `unpeek` mangles identifiers and encrypts string constants across the entire bundle, while injecting opaque control-flow predicates exclusively into cold functions (such as license checks, request signers, and auth validators). V8 JIT optimization inside hot loops remains untouched.
40
- 2. **O(1) Lazy String Vault (`<15%` Gzip Budget)**
41
- Plaintext string literals (`minStringLength >= 4`) are encrypted at build time with a polymorphic XOR stream cipher keyed by a per-build 32-bit seed. Instead of decrypting every string on page startup, the injected vault helper decodes each string lazily on its first access and caches the plaintext result in its array slot. Subsequent reads resolve in O(1) time via direct array indexing.
42
- 3. **Self-Defending `Function.prototype.toString()` Integrity & Passive Deterrent**
43
- When an analyst formats a protected chunk using DevTools Pretty-Print (`{}`) or edits a branch instruction in the Sources panel, the formatted source alters `Function.prototype.toString()`. [`verifyCodeIntegrity`](packages/unpeek/src/runtime/index.ts) validates a seeded 32-bit FNV-1a checksum against the function's source representation with zero false positives on untouched minified code, triggering gradual state degradation (`deterrentMode: 'gradual'`) rather than an obvious crash point that attackers can patch out.
44
- 4. **Automatic Source Map Vault (`.unpeek-maps/`)**
45
- Leaving `.js.map` files in a public build directory reconstructs your original TypeScript project tree inside DevTools. [`protectDirectory`](packages/unpeek/src/cli/index.ts) automatically extracts `.map` files from your public build directory into a private `.unpeek-maps/` folder and strips `//# sourceMappingURL=` comments from every client bundle so you can upload maps to Sentry or Datadog without serving them to end users.
46
-
47
- </baglam>
48
-
49
- ---
50
-
51
- <talimat>
52
-
53
- ## Quick Start
54
-
55
- ### 1. One-Command Project Setup (`npx unpeek init`)
56
-
57
- Run the initializer from your project root (Next.js, Vite, Webpack, or vanilla):
58
-
59
- ```bash
60
- npx unpeek init
61
- ```
62
-
63
- This command performs three non-destructive setup actions:
64
- 1. Detects your framework (`nextjs`, `vite`, `webpack`, or `vanilla`) from `package.json` and build config files.
65
- 2. Adds `"postbuild": "unpeek protect"` to your `package.json` scripts so every production build is automatically protected.
66
- 3. Appends `.unpeek-maps/` to `.gitignore` so extracted source maps stay out of version control.
67
-
68
- ### 2. CLI Usage (`unpeek protect`)
69
-
70
- You can also install `unpeek` as a development dependency and run it directly in CI/CD or local builds:
71
-
72
- ```bash
73
- npm install -D unpeek
74
- npx unpeek protect
75
- ```
76
-
77
- #### CLI Options
78
-
79
- | Flag | Description | Default |
80
- | :--- | :--- | :--- |
81
- | `--dir <path>` | Explicit client output directory override (e.g., `dist/assets`, `.next/static/chunks`) | Auto-detected |
82
- | `--seed <number>` | Deterministic 32-bit polymorphic build seed | Random per build |
83
- | `--mode <gradual\|passive>` | Runtime anti-tamper deterrent mode | `gradual` |
84
- | `--min-str <number>` | Minimum character length for O(1) lazy string vault encryption | `4` |
85
- | `--no-shield` | Skip injecting the runtime integrity and DevTools telemetry preamble | Shield enabled |
86
- | `--delete-maps` | Permanently delete `.map` files instead of moving them to `.unpeek-maps/` | Moves to `.unpeek-maps/` |
87
-
88
- ### 3. Universal Bundler Plugin (`unpeek/unplugin`)
89
-
90
- If you prefer in-memory chunk transformations during the bundler lifecycle rather than a post-build CLI pass, use [`unpeek/unplugin`](packages/unpeek/src/bundler/unplugin.ts):
91
-
92
- #### Vite (`vite.config.ts`)
93
-
94
- ```ts
95
- import { defineConfig } from 'vite';
96
- import unpeek from 'unpeek/unplugin';
97
-
98
- export default defineConfig({
99
- build: {
100
- sourcemap: true, // Extracted into .unpeek-maps/ automatically
101
- },
102
- plugins: [
103
- unpeek.vite({
104
- deterrentMode: 'gradual',
105
- keepPrivateMaps: true,
106
- }),
107
- ],
108
- });
109
- ```
110
-
111
- #### Next.js (`next.config.mjs`)
112
-
113
- Scope the plugin to client bundles (`!isServer`) so Node.js server chunks keep their native stack traces:
114
-
115
- ```js
116
- import unpeek from 'unpeek/unplugin';
117
-
118
- /** @type {import('next').NextConfig} */
119
- const nextConfig = {
120
- webpack(config, { dev, isServer }) {
121
- if (!dev && !isServer) {
122
- config.plugins.push(
123
- unpeek.webpack({
124
- deterrentMode: 'gradual',
125
- keepPrivateMaps: true,
126
- })
127
- );
128
- }
129
- return config;
130
- },
131
- };
132
-
133
- export default nextConfig;
134
- ```
135
-
136
- #### Programmatic API
137
-
138
- ```ts
139
- import { transformJavaScript, protectDirectory } from 'unpeek';
140
-
141
- // Transform an individual JavaScript string in memory
142
- const result = transformJavaScript(sourceCode, {
143
- seed: 0x7f3a91,
144
- minStringLength: 4,
145
- injectRuntimeShield: true,
146
- deterrentMode: 'gradual',
147
- });
148
-
149
- console.log(result.hotPathReport);
150
- // { totalFunctions: 12, hotFunctionsSkipped: ['renderLoop'], coldFunctionsProtected: ['signRequest'] }
151
-
152
- // Or protect an entire build directory
153
- const summary = await protectDirectory({
154
- cwd: process.cwd(),
155
- keepPrivateMaps: true,
156
- privateMapsDir: '.unpeek-maps',
157
- });
158
- ```
159
-
160
- ---
161
-
162
- ## `.unpeek-maps/` Source Map Vault & Sentry Workflow
163
-
164
- When `keepPrivateMaps` is enabled (the default), `unpeek` relocates all `.js.map` files from your public output directory into `.unpeek-maps/` while preserving their relative filenames, and strips `//# sourceMappingURL=` directives from the public `.js` chunks.
165
-
166
- To retain full stack-trace symbolication in production error trackers:
167
-
168
- ```bash
169
- # 1. Build your app and run unpeek (automatically triggered if wired via postbuild)
170
- npm run build
171
-
172
- # 2. Upload the private source maps from .unpeek-maps/ to Sentry or Datadog
173
- npx @sentry/cli sourcemaps upload .unpeek-maps --release="$GIT_COMMIT_SHA"
174
-
175
- # 3. Deploy your public build folder (.next/, dist/, or build/)
176
- ```
177
-
178
- ---
179
-
180
- ## Official AI Agent Skill Installation
181
-
182
- `unpeek` ships with an official, token-optimized AI Agent Skill ([`skills/unpeek/SKILL.md`](skills/unpeek/SKILL.md)) following Agent Skills Discovery Optimization (SDO) standards. Installing this skill teaches your coding agent (Claude Code, Cursor, Antigravity, OpenAI Codex, or Gemini CLI) how to configure `unpeek`, guard SSR chunks, and wire `.unpeek-maps/` into CI/CD pipelines.
183
-
184
- ### Project-Scoped Installation (Shared with Team via Git)
185
-
186
- Copy the skill into your repository's `.agents/skills/unpeek/` directory:
187
-
188
- ```bash
189
- mkdir -p .agents/skills/unpeek
190
- curl -fsSL https://raw.githubusercontent.com/Woffluon/unpeek/main/skills/unpeek/SKILL.md \
191
- -o .agents/skills/unpeek/SKILL.md
192
- ```
193
-
194
- ### Global Machine Installation (Antigravity / Gemini CLI / Claude Code)
195
-
196
- ```bash
197
- # Antigravity & Gemini CLI
198
- mkdir -p ~/.gemini/config/skills/unpeek
199
- curl -fsSL https://raw.githubusercontent.com/Woffluon/unpeek/main/skills/unpeek/SKILL.md \
200
- -o ~/.gemini/config/skills/unpeek/SKILL.md
201
-
202
- # Cross-agent standard (~/.agents/skills)
203
- mkdir -p ~/.agents/skills/unpeek
204
- curl -fsSL https://raw.githubusercontent.com/Woffluon/unpeek/main/skills/unpeek/SKILL.md \
205
- -o ~/.agents/skills/unpeek/SKILL.md
206
- ```
207
-
208
- </talimat>
209
-
210
- ---
211
-
212
- <dogrulama>
213
-
214
- ## Verification, Testing & Benchmarks
215
-
216
- This monorepo is managed with `pnpm` and verified against strict semantic equivalence, deobfuscation resistance, and runtime overhead budgets:
217
-
218
- ```bash
219
- # Install workspace dependencies
220
- pnpm install
221
-
222
- # Run unit & reverse-engineering resistance test suites (Vitest)
223
- pnpm test
224
-
225
- # Run the V8 VM CPU overhead (<2%) & Gzip delta (<15%) benchmark
226
- pnpm bench
227
-
228
- # Run strict TypeScript typechecking across packages/unpeek and apps/web
229
- pnpm typecheck
230
-
231
- # Build the core package and static interactive web playground
232
- pnpm build
233
- ```
234
-
235
- ### Benchmark Output Reference ([`packages/unpeek/benchmarks/overhead.bench.ts`](packages/unpeek/benchmarks/overhead.bench.ts))
236
-
237
- - **Build / Transform Time**: `< 3000 ms`
238
- - **Gzip Size Delta**: `< 15%` on production application bundles
239
- - **Runtime CPU Overhead**: `< 2.00%` (interleaved multi-round V8 JIT benchmark)
240
- - **Plaintext Leak Rate**: `0%` for string literals meeting `minStringLength`
241
-
242
- </dogrulama>
243
-
244
- ## License
245
-
246
- Released under the [MIT License](./LICENSE).
1
+ # unpeek
2
+
3
+ **Zero-config, call-graph-aware polymorphic JavaScript protection and runtime anti-tamper toolkit with <2% CPU overhead.**
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-emerald.svg)](LICENSE)
6
+ [![CPU Overhead](https://img.shields.io/badge/CPU_Overhead-%3C2%25-10b981.svg)](packages/unpeek/benchmarks/overhead.bench.ts)
7
+ [![Gzip Delta](https://img.shields.io/badge/Gzip_Delta-%3C15%25-06b6d4.svg)](packages/unpeek/benchmarks/overhead.bench.ts)
8
+ [![Interactive Playground](https://img.shields.io/badge/Playground-Live_Demo-6366f1.svg)](https://woffluon.github.io/unpeek)
9
+
10
+ English | [Türkçe](./README.tr.md) · [Live Interactive Playground](https://woffluon.github.io/unpeek) · [Agent Architecture Guide](./AGENTS.md) · [AI Skill](./skills/unpeek/SKILL.md)
11
+
12
+ ---
13
+
14
+ <baglam>
15
+
16
+ ## Why unpeek?
17
+
18
+ Shipping a modern web application in Next.js, Vite, or Webpack exposes client-side business logic, internal API endpoints, feature flags, and proprietary algorithms to anyone who opens Chrome DevTools or runs an automated AST deobfuscator (`webcrack`, `synchrony`).
19
+
20
+ Existing tools force an uncomfortable trade-off:
21
+ - **Standard minifiers (`esbuild`, `SWC`, `Terser`)** compress whitespace and shorten variable names, leaving string literals, endpoint URLs, and control flow readable after a single click on the DevTools Pretty-Print (`{}`) button.
22
+ - **Legacy AST obfuscators (`javascript-obfuscator`)** apply indiscriminate control-flow flattening, `eval` traps, and eager string-array rotation to every function in the bundle. This inflates bundle sizes by 40–300%, breaks V8 TurboFan JIT inlining inside 60fps UI rendering loops, and triggers false-positive browser freezes.
23
+
24
+ `unpeek` resolves this trade-off by combining static call-graph analysis with polymorphic compile-time encryption:
25
+
26
+ | Capability | Standard Minifiers (`Terser` / `SWC`) | Legacy AST Obfuscators | `unpeek` |
27
+ | :--- | :--- | :--- | :--- |
28
+ | **Runtime CPU Overhead** | ~0% | 15% – 85% (throttles UI loops) | **< 2.0%** (Call-Graph Hot-Path Exemption) |
29
+ | **Gzip Bundle Growth** | 0% (Baseline) | +45% – +300% | **< 15%** (O(1) Lazy String Vault) |
30
+ | **String & Endpoint Encryption** | Plaintext in bundle | Eager array rotation (high CPU) | **Polymorphic XOR + O(1) Memoized Vault** |
31
+ | **DevTools Pretty-Print (`{}`) Defense** | None | `debugger` infinite loops (easily bypassed) | **`Function.prototype.toString()` FNV-1a Integrity** |
32
+ | **Source Map Isolation** | Manual CI scripts | Manual configuration | **Automatic `.unpeek-maps/` Vault + Comment Strip** |
33
+ | **Static Deobfuscator Resistance** | None | Fixed templates (fingerprinted by `webcrack`) | **Per-Build Polymorphic AST Seed** |
34
+ | **SSR / Next.js Compatibility** | Native | Frequently breaks server hydration | **Client-Only Auto-Detection + SSR Guard** |
35
+
36
+ ### Core Architecture
37
+
38
+ 1. **Call-Graph Hot-Path Exemption (`<2%` CPU Budget)**
39
+ Before modifying a single AST node, [`detectHotPaths`](packages/unpeek/src/ast/index.ts) traverses the file's call graph. Functions containing loops (`for`, `while`, `do-while`, `for-of`), array higher-order callbacks (`.map`, `.filter`, `.reduce`, `.forEach`), `requestAnimationFrame` tickers, and React/UI render components (`useState`, `useEffect`, `createElement`) are flagged as hot paths. `unpeek` mangles identifiers and encrypts string constants across the entire bundle, while injecting opaque control-flow predicates exclusively into cold functions (such as license checks, request signers, and auth validators). V8 JIT optimization inside hot loops remains untouched.
40
+ 2. **O(1) Lazy String Vault (`<15%` Gzip Budget)**
41
+ Plaintext string literals (`minStringLength >= 4`) are encrypted at build time with a polymorphic XOR stream cipher keyed by a per-build 32-bit seed. Instead of decrypting every string on page startup, the injected vault helper decodes each string lazily on its first access and caches the plaintext result in its array slot. Subsequent reads resolve in O(1) time via direct array indexing.
42
+ 3. **Self-Defending `Function.prototype.toString()` Integrity & Passive Deterrent**
43
+ When an analyst formats a protected chunk using DevTools Pretty-Print (`{}`) or edits a branch instruction in the Sources panel, the formatted source alters `Function.prototype.toString()`. [`verifyCodeIntegrity`](packages/unpeek/src/runtime/index.ts) validates a seeded 32-bit FNV-1a checksum against the function's source representation with zero false positives on untouched minified code, triggering gradual state degradation (`deterrentMode: 'gradual'`) rather than an obvious crash point that attackers can patch out.
44
+ 4. **Automatic Source Map Vault (`.unpeek-maps/`)**
45
+ Leaving `.js.map` files in a public build directory reconstructs your original TypeScript project tree inside DevTools. [`protectDirectory`](packages/unpeek/src/cli/index.ts) automatically extracts `.map` files from your public build directory into a private `.unpeek-maps/` folder and strips `//# sourceMappingURL=` comments from every client bundle so you can upload maps to Sentry or Datadog without serving them to end users.
46
+
47
+ </baglam>
48
+
49
+ ---
50
+
51
+ <talimat>
52
+
53
+ ## Quick Start
54
+
55
+ ### 1. One-Command Project Setup (`npx unpeek init`)
56
+
57
+ Run the initializer from your project root (Next.js, Vite, Webpack, or vanilla):
58
+
59
+ ```bash
60
+ npx unpeek init
61
+ ```
62
+
63
+ This command performs three non-destructive setup actions:
64
+ 1. Detects your framework (`nextjs`, `vite`, `webpack`, or `vanilla`) from `package.json` and build config files.
65
+ 2. Adds `"postbuild": "unpeek protect"` to your `package.json` scripts so every production build is automatically protected.
66
+ 3. Appends `.unpeek-maps/` to `.gitignore` so extracted source maps stay out of version control.
67
+
68
+ ### 2. CLI Usage (`unpeek protect`)
69
+
70
+ You can also install `unpeek` as a development dependency and run it directly in CI/CD or local builds:
71
+
72
+ ```bash
73
+ npm install -D unpeek
74
+ npx unpeek protect
75
+ ```
76
+
77
+ #### CLI Options
78
+
79
+ | Flag | Description | Default |
80
+ | :--- | :--- | :--- |
81
+ | `--dir <path>` | Explicit client output directory override (e.g., `dist/assets`, `.next/static/chunks`) | Auto-detected |
82
+ | `--seed <number>` | Deterministic 32-bit polymorphic build seed | Random per build |
83
+ | `--mode <gradual\|passive>` | Runtime anti-tamper deterrent mode | `gradual` |
84
+ | `--min-str <number>` | Minimum character length for O(1) lazy string vault encryption | `4` |
85
+ | `--no-shield` | Skip injecting the runtime integrity and DevTools telemetry preamble | Shield enabled |
86
+ | `--delete-maps` | Permanently delete `.map` files instead of moving them to `.unpeek-maps/` | Moves to `.unpeek-maps/` |
87
+
88
+ ### 3. Universal Bundler Plugin (`unpeek/unplugin`)
89
+
90
+ If you prefer in-memory chunk transformations during the bundler lifecycle rather than a post-build CLI pass, use [`unpeek/unplugin`](packages/unpeek/src/bundler/unplugin.ts):
91
+
92
+ #### Vite (`vite.config.ts`)
93
+
94
+ ```ts
95
+ import { defineConfig } from 'vite';
96
+ import unpeek from 'unpeek/unplugin';
97
+
98
+ export default defineConfig({
99
+ build: {
100
+ sourcemap: true, // Extracted into .unpeek-maps/ automatically
101
+ },
102
+ plugins: [
103
+ unpeek.vite({
104
+ deterrentMode: 'gradual',
105
+ keepPrivateMaps: true,
106
+ }),
107
+ ],
108
+ });
109
+ ```
110
+
111
+ #### Next.js (`next.config.mjs`)
112
+
113
+ Scope the plugin to client bundles (`!isServer`) so Node.js server chunks keep their native stack traces:
114
+
115
+ ```js
116
+ import unpeek from 'unpeek/unplugin';
117
+
118
+ /** @type {import('next').NextConfig} */
119
+ const nextConfig = {
120
+ webpack(config, { dev, isServer }) {
121
+ if (!dev && !isServer) {
122
+ config.plugins.push(
123
+ unpeek.webpack({
124
+ deterrentMode: 'gradual',
125
+ keepPrivateMaps: true,
126
+ })
127
+ );
128
+ }
129
+ return config;
130
+ },
131
+ };
132
+
133
+ export default nextConfig;
134
+ ```
135
+
136
+ #### Programmatic API
137
+
138
+ ```ts
139
+ import { transformJavaScript, protectDirectory } from 'unpeek';
140
+
141
+ // Transform an individual JavaScript string in memory
142
+ const result = transformJavaScript(sourceCode, {
143
+ seed: 0x7f3a91,
144
+ minStringLength: 4,
145
+ injectRuntimeShield: true,
146
+ deterrentMode: 'gradual',
147
+ });
148
+
149
+ console.log(result.hotPathReport);
150
+ // { totalFunctions: 12, hotFunctionsSkipped: ['renderLoop'], coldFunctionsProtected: ['signRequest'] }
151
+
152
+ // Or protect an entire build directory
153
+ const summary = await protectDirectory({
154
+ cwd: process.cwd(),
155
+ keepPrivateMaps: true,
156
+ privateMapsDir: '.unpeek-maps',
157
+ });
158
+ ```
159
+
160
+ ---
161
+
162
+ ## `.unpeek-maps/` Source Map Vault & Sentry Workflow
163
+
164
+ When `keepPrivateMaps` is enabled (the default), `unpeek` relocates all `.js.map` files from your public output directory into `.unpeek-maps/` while preserving their relative filenames, and strips `//# sourceMappingURL=` directives from the public `.js` chunks.
165
+
166
+ To retain full stack-trace symbolication in production error trackers:
167
+
168
+ ```bash
169
+ # 1. Build your app and run unpeek (automatically triggered if wired via postbuild)
170
+ npm run build
171
+
172
+ # 2. Upload the private source maps from .unpeek-maps/ to Sentry or Datadog
173
+ npx @sentry/cli sourcemaps upload .unpeek-maps --release="$GIT_COMMIT_SHA"
174
+
175
+ # 3. Deploy your public build folder (.next/, dist/, or build/)
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Official AI Agent Skill Installation
181
+
182
+ `unpeek` ships with an official, token-optimized AI Agent Skill ([`skills/unpeek/SKILL.md`](skills/unpeek/SKILL.md)) following Agent Skills Discovery Optimization (SDO) standards. Installing this skill teaches your coding agent (Claude Code, Cursor, Antigravity, OpenAI Codex, or Gemini CLI) how to configure `unpeek`, guard SSR chunks, and wire `.unpeek-maps/` into CI/CD pipelines.
183
+
184
+ ### Project-Scoped Installation (Shared with Team via Git)
185
+
186
+ Copy the skill into your repository's `.agents/skills/unpeek/` directory:
187
+
188
+ ```bash
189
+ mkdir -p .agents/skills/unpeek
190
+ curl -fsSL https://raw.githubusercontent.com/Woffluon/unpeek/main/skills/unpeek/SKILL.md \
191
+ -o .agents/skills/unpeek/SKILL.md
192
+ ```
193
+
194
+ ### Global Machine Installation (Antigravity / Gemini CLI / Claude Code)
195
+
196
+ ```bash
197
+ # Antigravity & Gemini CLI
198
+ mkdir -p ~/.gemini/config/skills/unpeek
199
+ curl -fsSL https://raw.githubusercontent.com/Woffluon/unpeek/main/skills/unpeek/SKILL.md \
200
+ -o ~/.gemini/config/skills/unpeek/SKILL.md
201
+
202
+ # Cross-agent standard (~/.agents/skills)
203
+ mkdir -p ~/.agents/skills/unpeek
204
+ curl -fsSL https://raw.githubusercontent.com/Woffluon/unpeek/main/skills/unpeek/SKILL.md \
205
+ -o ~/.agents/skills/unpeek/SKILL.md
206
+ ```
207
+
208
+ </talimat>
209
+
210
+ ---
211
+
212
+ <dogrulama>
213
+
214
+ ## Verification, Testing & Benchmarks
215
+
216
+ This monorepo is managed with `pnpm` and verified against strict semantic equivalence, deobfuscation resistance, and runtime overhead budgets:
217
+
218
+ ```bash
219
+ # Install workspace dependencies
220
+ pnpm install
221
+
222
+ # Run unit & reverse-engineering resistance test suites (Vitest)
223
+ pnpm test
224
+
225
+ # Run the V8 VM CPU overhead (<2%) & Gzip delta (<15%) benchmark
226
+ pnpm bench
227
+
228
+ # Run strict TypeScript typechecking across packages/unpeek and apps/web
229
+ pnpm typecheck
230
+
231
+ # Build the core package and static interactive web playground
232
+ pnpm build
233
+ ```
234
+
235
+ ### Benchmark Output Reference ([`packages/unpeek/benchmarks/overhead.bench.ts`](packages/unpeek/benchmarks/overhead.bench.ts))
236
+
237
+ - **Build / Transform Time**: `< 3000 ms`
238
+ - **Gzip Size Delta**: `< 15%` on production application bundles
239
+ - **Runtime CPU Overhead**: `< 2.00%` (interleaved multi-round V8 JIT benchmark)
240
+ - **Plaintext Leak Rate**: `0%` for string literals meeting `minStringLength`
241
+
242
+ </dogrulama>
243
+
244
+ ## License
245
+
246
+ Released under the [MIT License](./LICENSE).
package/bin/unpeek.mjs CHANGED
@@ -1,7 +1,7 @@
1
- #!/usr/bin/env node
2
- import { runCli } from '../dist/cli/index.js';
3
-
4
- runCli(process.argv.slice(2)).catch((err) => {
5
- console.error('[unpeek] fatal error:', err instanceof Error ? err.message : String(err));
6
- process.exit(1);
7
- });
1
+ #!/usr/bin/env node
2
+ import { runCli } from '../dist/cli/index.js';
3
+
4
+ runCli(process.argv.slice(2)).catch((err) => {
5
+ console.error('[unpeek] fatal error:', err instanceof Error ? err.message : String(err));
6
+ process.exit(1);
7
+ });
@@ -103,14 +103,14 @@ export function applyStringVault(ast, seed = 0x9e3779b9, minStringLength = 4) {
103
103
  const serializedTable = JSON.stringify(encryptedPayloads);
104
104
  const integrityToken = computeFnv1aHash(serializedTable, seed);
105
105
  // Decrypt once into const _uC array at module initialization so V8 marks the ContextCell kConst
106
- const helperSource = `
107
- const ${cacheVarName} = ${serializedTable}.map(function(a) {
108
- var s = "";
109
- for (var j = 0; j < a.length; j++) {
110
- s += String.fromCharCode(a.charCodeAt(j) ^ (((${mask} ^ ((j & 1) * ${step})) & 30) | 1));
111
- }
112
- return s;
113
- });
106
+ const helperSource = `
107
+ const ${cacheVarName} = ${serializedTable}.map(function(a) {
108
+ var s = "";
109
+ for (var j = 0; j < a.length; j++) {
110
+ s += String.fromCharCode(a.charCodeAt(j) ^ (((${mask} ^ ((j & 1) * ${step})) & 30) | 1));
111
+ }
112
+ return s;
113
+ });
114
114
  `.trim();
115
115
  const helperAst = acorn.parse(helperSource, {
116
116
  ecmaVersion: 'latest',
package/dist/cli/index.js CHANGED
@@ -1,21 +1,21 @@
1
1
  import { initProject } from './init.js';
2
2
  import { protectProject } from './protect.js';
3
- const VERSION = '1.0.0';
3
+ const VERSION = '1.0.1';
4
4
  function printHelp() {
5
- const helpText = `
6
- unpeek v${VERSION} — Call-graph-aware polymorphic AST obfuscation & runtime shield
7
-
8
- Usage:
9
- unpeek init Configure postbuild hook & .unpeek-maps/ vault
10
- unpeek protect [options] Protect client bundles in-place
11
-
12
- Options:
13
- --out-dir <dir> Explicit client output directory
14
- --seed <number> Deterministic polymorphic seed
15
- --passive Use passive deterrent mode instead of gradual
16
- --no-maps Delete .map files instead of vaulting to .unpeek-maps/
17
- -h, --help Show help message
18
- -v, --version Show version number
5
+ const helpText = `
6
+ unpeek v${VERSION} — Call-graph-aware polymorphic AST obfuscation & runtime shield
7
+
8
+ Usage:
9
+ unpeek init Configure postbuild hook & .unpeek-maps/ vault
10
+ unpeek protect [options] Protect client bundles in-place
11
+
12
+ Options:
13
+ --out-dir <dir> Explicit client output directory
14
+ --seed <number> Deterministic polymorphic seed
15
+ --passive Use passive deterrent mode instead of gradual
16
+ --no-maps Delete .map files instead of vaulting to .unpeek-maps/
17
+ -h, --help Show help message
18
+ -v, --version Show version number
19
19
  `.trim();
20
20
  console.log(helpText);
21
21
  }
package/package.json CHANGED
@@ -1,67 +1,67 @@
1
- {
2
- "name": "unpeek",
3
- "version": "1.0.0",
4
- "description": "Zero-config, call-graph-aware polymorphic AST obfuscation and passive runtime anti-tamper with <2% CPU overhead.",
5
- "type": "module",
6
- "license": "MIT",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/Woffluon/unpeek.git",
10
- "directory": "packages/unpeek"
11
- },
12
- "homepage": "https://woffluon.github.io/unpeek",
13
- "keywords": [
14
- "obfuscator",
15
- "anti-debug",
16
- "anti-tamper",
17
- "devtools",
18
- "sourcemap",
19
- "ast",
20
- "security",
21
- "unplugin",
22
- "vite",
23
- "nextjs",
24
- "webpack"
25
- ],
26
- "bin": {
27
- "unpeek": "./bin/unpeek.mjs"
28
- },
29
- "main": "./dist/index.js",
30
- "types": "./dist/index.d.ts",
31
- "exports": {
32
- ".": {
33
- "types": "./dist/index.d.ts",
34
- "import": "./dist/index.js"
35
- },
36
- "./unplugin": {
37
- "types": "./dist/bundler/unplugin.d.ts",
38
- "import": "./dist/bundler/unplugin.js"
39
- }
40
- },
41
- "files": [
42
- "bin",
43
- "dist",
44
- "README.md",
45
- "LICENSE"
46
- ],
47
- "scripts": {
48
- "build": "tsc -p tsconfig.json",
49
- "typecheck": "tsc --noEmit",
50
- "test": "vitest run",
51
- "test:coverage": "vitest run --coverage",
52
- "bench": "tsx benchmarks/overhead.bench.ts"
53
- },
54
- "dependencies": {
55
- "acorn": "^8.14.1",
56
- "acorn-walk": "^8.3.4",
57
- "astring": "^1.9.0",
58
- "unplugin": "^2.2.2"
59
- },
60
- "devDependencies": {
61
- "@types/node": "^22.13.10",
62
- "@vitest/coverage-v8": "^3.0.8",
63
- "tsx": "^4.19.3",
64
- "typescript": "^5.8.2",
65
- "vitest": "^3.0.8"
66
- }
67
- }
1
+ {
2
+ "name": "unpeek",
3
+ "version": "1.0.1",
4
+ "description": "Zero-config, call-graph-aware polymorphic AST obfuscation and passive runtime anti-tamper with <2% CPU overhead.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/Woffluon/unpeek.git",
10
+ "directory": "packages/unpeek"
11
+ },
12
+ "homepage": "https://woffluon.github.io/unpeek",
13
+ "keywords": [
14
+ "obfuscator",
15
+ "anti-debug",
16
+ "anti-tamper",
17
+ "devtools",
18
+ "sourcemap",
19
+ "ast",
20
+ "security",
21
+ "unplugin",
22
+ "vite",
23
+ "nextjs",
24
+ "webpack"
25
+ ],
26
+ "bin": {
27
+ "unpeek": "./bin/unpeek.mjs"
28
+ },
29
+ "main": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.js"
35
+ },
36
+ "./unplugin": {
37
+ "types": "./dist/bundler/unplugin.d.ts",
38
+ "import": "./dist/bundler/unplugin.js"
39
+ }
40
+ },
41
+ "files": [
42
+ "bin",
43
+ "dist",
44
+ "README.md",
45
+ "LICENSE"
46
+ ],
47
+ "scripts": {
48
+ "build": "tsc -p tsconfig.json",
49
+ "typecheck": "tsc --noEmit",
50
+ "test": "vitest run",
51
+ "test:coverage": "vitest run --coverage",
52
+ "bench": "tsx benchmarks/overhead.bench.ts"
53
+ },
54
+ "dependencies": {
55
+ "acorn": "^8.14.1",
56
+ "acorn-walk": "^8.3.4",
57
+ "astring": "^1.9.0",
58
+ "unplugin": "^2.2.2"
59
+ },
60
+ "devDependencies": {
61
+ "@types/node": "^22.13.10",
62
+ "@vitest/coverage-v8": "^3.0.8",
63
+ "tsx": "^4.19.3",
64
+ "typescript": "^5.8.2",
65
+ "vitest": "^3.0.8"
66
+ }
67
+ }