expose-kit 0.9.0 → 0.10.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/README.md CHANGED
@@ -1,230 +1,233 @@
1
- # Expose Kit
2
- ![release workflow](https://github.com/evex-dev/linejs/actions/workflows/release.yml/badge.svg)
3
- [![](https://dcbadge.limes.pink/api/server/evex)](https://discord.gg/evex)
4
-
5
- > A universal toolkit for JavaScript deobfuscation
6
-
7
- ---
8
-
9
- ## What is this?
10
-
11
- JavaScript deobfuscation tools are everywhere.
12
- But many of them are **too aggressive**, rewriting code until it breaks.
13
-
14
- Expose Kit takes a **different approach**.
15
-
16
- - No brute force
17
- - Step-by-step, verifiable transforms
18
- - Designed to *not* break your code silently
19
-
20
- Each transformation is meant to be **checked and validated**, so you always know *when* something goes wrong.
21
-
22
- Alongside deobfuscation, Expose Kit also provides a set of **practical utilities** for working with obfuscated JavaScript.
23
-
24
- ---
25
-
26
- ## Installation
27
-
28
- Just one step:
29
-
30
- ```bash
31
- npm i -g expose-kit
32
- # or
33
- bun i -g expose-kit
34
- ```
35
-
36
- ```bash
37
- expose --help
38
- expose parsable sample.js
39
- ```
40
-
41
- ---
42
-
43
- ## Usage Notes
44
-
45
- ### Default arguments
46
-
47
- - The first argument is the input file
48
- (`--file` / `--input` can also be used)
49
- - If required options are missing, Expose Kit will **prompt you**
50
- - A timeout is enabled by default to avoid hangs
51
- Use `--unlimited` for long-running execution
52
-
53
- ---
54
-
55
- ## Recommended Workflow
56
-
57
- First, an important premise:
58
-
59
- > It is **impossible** to create a static deobfuscation tool that *never* breaks.
60
-
61
- Reasons include:
62
- - Unpredictable execution (`eval`, dynamic code)
63
- - Bugs or edge cases in AST manipulation
64
-
65
- Because of this, you should **verify the code at every step**.
66
-
67
- ### 1. Always verify with `parsable`
68
-
69
- After each transformation, run:
70
-
71
- ```bash
72
- expose parsable file.js
73
- ```
74
-
75
- This ensures the syntax is still valid.
76
-
77
- ---
78
-
79
- ### 2. Make scopes safe first
80
-
81
- One of the most common causes of breakage is **variable name confusion**.
82
-
83
- If you try to write your own deobfuscation logic (e.g. in Python), you’ll quickly realize how painful it is to track scopes correctly.
84
-
85
- That’s why you should **always start with**:
86
-
87
- ```bash
88
- expose safe-scope input.js
89
- ```
90
-
91
- This renames bindings per scope, producing code like:
92
-
93
- ```js
94
- Before: var x = 810;((x) => console.log(x))(114514);
95
- After: var x = 810;((_x) => console.log(_x))(114514);
96
- ```
97
-
98
- With this alone:
99
- - The code becomes far more resistant to breakage
100
- - Writing custom deobfuscation logic becomes much easier
101
- - You no longer need to worry about scope collisions
102
-
103
- ---
104
-
105
- ### 3. Apply transforms step by step
106
-
107
- After `safe-scope`, combine common techniques like:
108
- - `expand-array`, `expand-object` and more
109
- - legacy obfuscator-specific commands
110
-
111
- After **each step**, run `parsable` again.
112
-
113
- Expose Kit will also clearly indicate whether a **diff** exists, making inspection easy.
114
-
115
- Repeat this process, and the original code will gradually reveal itself.
116
-
117
- ---
118
-
119
- ## Commands
120
-
121
- ### `expose parsable`
122
-
123
- Check whether a file is syntactically valid.
124
-
125
- ```js
126
- parsable: const x = 810;
127
- not parsable: const ;x; == 810;
128
- ```
129
-
130
- ```bash
131
- expose parsable path/to/file.js
132
- ```
133
-
134
- Args:
135
- - Default args only
136
-
137
- ---
138
-
139
- ### `expose safe-scope`
140
-
141
- Rename bindings per scope for safer transformations.
142
-
143
- ```bash
144
- expose safe-scope path/to/file.js --output path/to/file.safe-scope.js
145
- ```
146
- Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/safe-scope/mocks).
147
-
148
- Args:
149
- - `--o, --output <file>`
150
- Output file path
151
- - No extension → `file.safe-scope.js`
152
- - With extension → `file.safe-scope.<ext>`
153
-
154
- ---
155
-
156
- ### `expose pre-evaluate`
157
-
158
- Pre-evaluate const numeric/string expressions. (Safe evaluate)
159
-
160
- ```js
161
- const a = 1 + 2 * 3; // => 7
162
- const c = "aaaa";
163
- const b = "a" + c; // => "aaaaa"
164
- ```
165
-
166
- ```bash
167
- expose pre-evaluate path/to/file.js --output path/to/file.pre-evaluate.js
168
- ```
169
-
170
- Args:
171
- - `--o, --output <file>`
172
- Output file path
173
- - No extension → `file.pre-evaluate.js`
174
- - With extension → `file.pre-evaluate.<ext>`
175
-
176
- ---
177
-
178
- ### `expose expand-array`
179
-
180
- Expand array index access for primitive values.
181
- ```js
182
- var a = [1, 1, 4, 5, 1, 4];
183
- // before
184
- console.log(a[0], a[2], a[3]);
185
- // after
186
- console.log(1, 4, 5);
187
- ```
188
- Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/expand-array/mocks).
189
-
190
- ```bash
191
- expose expand-array path/to/file.js --target arrayName --output path/to/file.expand-array.js
192
- ```
193
-
194
- Args:
195
- - `--target <name>`
196
- Target array variable name
197
- - `--o, --output <file>`
198
- Output file path
199
-
200
- Notes:
201
- - Each replacement is validated by reparsing; unsafe replacements are skipped.
202
- (This array is intended to be immutable, so caution is required)
203
-
204
- ---
205
-
1
+ # Expose Kit
2
+ ![release workflow](https://github.com/evex-dev/linejs/actions/workflows/release.yml/badge.svg)
3
+ [![](https://dcbadge.limes.pink/api/server/evex)](https://discord.gg/evex)
4
+
5
+ > A universal toolkit for JavaScript deobfuscation
6
+
7
+ ---
8
+
9
+ ## What is this?
10
+
11
+ JavaScript deobfuscation tools are everywhere.
12
+ But many of them are **too aggressive**, rewriting code until it breaks.
13
+
14
+ Expose Kit takes a **different approach**.
15
+
16
+ - No brute force
17
+ - Step-by-step, verifiable transforms
18
+ - Designed to *not* break your code silently
19
+
20
+ Each transformation is meant to be **checked and validated**, so you always know *when* something goes wrong.
21
+
22
+ Alongside deobfuscation, Expose Kit also provides a set of **practical utilities** for working with obfuscated JavaScript.
23
+
24
+ ---
25
+
26
+ ## Installation
27
+
28
+ Just one step:
29
+
30
+ ```bash
31
+ npm i -g expose-kit
32
+ # or
33
+ bun i -g expose-kit
34
+ ```
35
+
36
+ ```bash
37
+ expose --help
38
+ expose parsable sample.js
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Usage Notes
44
+
45
+ ### Default arguments
46
+
47
+ - The first argument is the input file
48
+ (`--file` / `--input` can also be used)
49
+ - If required options are missing, Expose Kit will **prompt you**
50
+ - A timeout is enabled by default to avoid hangs
51
+ Use `--unlimited` for long-running execution
52
+
53
+ ---
54
+
55
+ ## Recommended Workflow
56
+
57
+ First, an important premise:
58
+
59
+ > It is **impossible** to create a static deobfuscation tool that *never* breaks.
60
+
61
+ Reasons include:
62
+ - Unpredictable execution (`eval`, dynamic code)
63
+ - Bugs or edge cases in AST manipulation
64
+
65
+ Because of this, you should **verify the code at every step**.
66
+
67
+ ### 1. Always verify with `parsable`
68
+
69
+ After each transformation, run:
70
+
71
+ ```bash
72
+ expose parsable file.js
73
+ ```
74
+
75
+ This ensures the syntax is still valid.
76
+
77
+ ---
78
+
79
+ ### 2. Make scopes safe first
80
+
81
+ One of the most common causes of breakage is **variable name confusion**.
82
+
83
+ If you try to write your own deobfuscation logic (e.g. in Python), you’ll quickly realize how painful it is to track scopes correctly.
84
+
85
+ That’s why you should **always start with**:
86
+
87
+ ```bash
88
+ expose safe-scope input.js
89
+ ```
90
+
91
+ This renames bindings per scope, producing code like:
92
+
93
+ ```js
94
+ Before: var x = 810;((x) => console.log(x))(114514);
95
+ After: var x = 810;((_x) => console.log(_x))(114514);
96
+ ```
97
+
98
+ With this alone:
99
+ - The code becomes far more resistant to breakage
100
+ - Writing custom deobfuscation logic becomes much easier
101
+ - You no longer need to worry about scope collisions
102
+
103
+ ---
104
+
105
+ ### 3. Apply transforms step by step
106
+
107
+ After `safe-scope`, combine common techniques like:
108
+ - `expand-array`, `expand-object` and more
109
+
110
+ After **each step**, run `parsable` and `remove-unused` again.
111
+ (even if it's not frequent, we might overlook something broken)
112
+
113
+ Expose Kit will also clearly indicate whether a **diff** exists, making inspection easy.
114
+
115
+ Repeat this process, and the original code will gradually reveal itself.
116
+
117
+ ---
118
+
119
+ ## Commands
120
+
121
+ ### `expose parsable`
122
+
123
+ Check whether a file is syntactically valid.
124
+
125
+ ```js
126
+ parsable: const x = 810;
127
+ not parsable: const ;x; == 810;
128
+ ```
129
+
130
+ ```bash
131
+ expose parsable path/to/file.js
132
+ ```
133
+
134
+ Args:
135
+ - Default args only
136
+
137
+ ---
138
+
139
+ ### `expose safe-scope`
140
+
141
+ Rename bindings per scope for safer transformations.
142
+
143
+ ```bash
144
+ expose safe-scope path/to/file.js --output path/to/file.safe-scope.js
145
+ ```
146
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/safe-scope/mocks).
147
+
148
+ Args:
149
+ - `--o, --output <file>`
150
+ Output file path
151
+ - No extension → `file.safe-scope.js`
152
+ - With extension → `file.safe-scope.<ext>`
153
+
154
+ ---
155
+
156
+ ### `expose pre-evaluate`
157
+
158
+ Pre-evaluate const numeric/string expressions. (Safe evaluate)
159
+
160
+ ```js
161
+ const a = 1 + 2 * 3; // => 7
162
+ const c = "aaaa";
163
+ const b = "a" + c; // => "aaaaa"
164
+ ```
165
+
166
+ ```bash
167
+ expose pre-evaluate path/to/file.js --output path/to/file.pre-evaluate.js
168
+ ```
169
+
170
+ Args:
171
+ - `--o, --output <file>`
172
+ Output file path
173
+ - No extension → `file.pre-evaluate.js`
174
+ - With extension → `file.pre-evaluate.<ext>`
175
+
176
+ Notes:
177
+ - Inlines zero-arg functions that return safe array literals by hoisting them to a var.
178
+
179
+ ---
180
+
181
+ ### `expose expand-array`
182
+
183
+ Expand array index access for primitive values.
184
+ ```js
185
+ var a = [1, 1, 4, 5, 1, 4];
186
+ // before
187
+ console.log(a[0], a[2], a[3]);
188
+ // after
189
+ console.log(1, 4, 5);
190
+ ```
191
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/expand-array/mocks).
192
+
193
+ ```bash
194
+ expose expand-array path/to/file.js --target arrayName --output path/to/file.expand-array.js
195
+ ```
196
+
197
+ Args:
198
+ - `--target <name>`
199
+ Target array variable name
200
+ - `--o, --output <file>`
201
+ Output file path
202
+
203
+ Notes:
204
+ - Each replacement is validated by reparsing; unsafe replacements are skipped.
205
+ (This array is intended to be immutable, so caution is required)
206
+
207
+ ---
208
+
206
209
  ### `expose expand-object`
207
-
208
- Expand object property access for primitive values.
209
- ```js
210
- const obj = { a: 1, b: 2 };
211
- // before
212
- console.log(obj.a, obj["b"]);
213
- // after
214
- console.log(1, 2);
215
- ```
216
- Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/expand-object/mocks).
217
-
218
- ```bash
219
- expose expand-object path/to/file.js --target objectName --output path/to/file.expand-object.js
220
- ```
221
-
222
- Args:
223
- - `--target <name>`
224
- Target object variable name
225
- - `--o, --output <file>`
226
- Output file path
227
-
210
+
211
+ Expand object property access for primitive values.
212
+ ```js
213
+ const obj = { a: 1, b: 2 };
214
+ // before
215
+ console.log(obj.a, obj["b"]);
216
+ // after
217
+ console.log(1, 2);
218
+ ```
219
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/expand-object/mocks).
220
+
221
+ ```bash
222
+ expose expand-object path/to/file.js --target objectName --output path/to/file.expand-object.js
223
+ ```
224
+
225
+ Args:
226
+ - `--target <name>`
227
+ Target object variable name
228
+ - `--o, --output <file>`
229
+ Output file path
230
+
228
231
  Notes:
229
232
  - Each replacement is validated by reparsing; unsafe replacements are skipped.
230
233
  (This object is intended to be immutable, so caution is required)
@@ -346,38 +349,65 @@ Notes:
346
349
 
347
350
  ---
348
351
 
352
+ ### `expose sequence-split`
353
+
354
+ Split sequence expressions into statements.
355
+ ```js
356
+ // before
357
+ a, b, c;
358
+ // after
359
+ a;
360
+ b;
361
+ c;
362
+ ```
363
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/sequence-split/mocks).
364
+
365
+ ```bash
366
+ expose sequence-split path/to/file.js --output path/to/file.sequence-split.js
367
+ ```
368
+
369
+ Args:
370
+ - `--o, --output <file>`
371
+ Output file path
372
+
373
+ Notes:
374
+ - Splits safe sequence expressions into standalone statements.
375
+ - Also normalizes single-statement control flow blocks.
376
+
377
+ ---
378
+
349
379
  ### `expose remove-unused`
350
380
 
351
381
  Remove unused variabless.
352
- ```js
353
- // before
354
- var a = 0, b = 1;
355
- console.log(a);
356
- // after
357
- var a = 0;
358
- console.log(a);
359
- ```
360
- Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/remove-unused/mocks).
361
-
362
- ```bash
363
- expose remove-unused path/to/file.js --output path/to/file.remove-unused.js
364
- ```
365
-
366
- Args:
367
- - `--o, --output <file>`
368
- Output file path
369
-
370
-
371
- ## Community & Support
372
-
373
- - Missing a feature? → [Create an issue](https://github.com/EdamAme-x/expose-kit/issues)
374
- - Not sure which command to use? → Join our [Discord](https://evex.land)
375
-
376
- ---
377
-
378
- ## Author
379
-
380
- - [EdamAme-x](https://github.com/EdamAme-x)
381
-
382
- Built for research, not abuse.
383
- Want stronger obfuscation? Then build something this tool can’t reverse.
382
+ ```js
383
+ // before
384
+ var a = 0, b = 1;
385
+ console.log(a);
386
+ // after
387
+ var a = 0;
388
+ console.log(a);
389
+ ```
390
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/remove-unused/mocks).
391
+
392
+ ```bash
393
+ expose remove-unused path/to/file.js --output path/to/file.remove-unused.js
394
+ ```
395
+
396
+ Args:
397
+ - `--o, --output <file>`
398
+ Output file path
399
+
400
+
401
+ ## Community & Support
402
+
403
+ - Missing a feature? → [Create an issue](https://github.com/EdamAme-x/expose-kit/issues)
404
+ - Not sure which command to use? → Join our [Discord](https://evex.land)
405
+
406
+ ---
407
+
408
+ ## Author
409
+
410
+ - [EdamAme-x](https://github.com/EdamAme-x)
411
+
412
+ Built for research, not abuse.
413
+ Want stronger obfuscation? Then build something this tool can’t reverse.
package/dist/index.js CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  // index.ts
4
4
  import { Command } from "commander";
5
- import { dirname as dirname10, join as join10 } from "path";
5
+ import { dirname as dirname11, join as join11 } from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  import chalk4 from "chalk";
8
8
 
9
9
  // utils/cli/createCommand.ts
10
10
  var createCommand = (creator) => {
11
- return (program2) => {
12
- creator(program2);
11
+ return (program3) => {
12
+ creator(program3);
13
13
  };
14
14
  };
15
15
 
@@ -85,8 +85,8 @@ var showError = (message) => {
85
85
  };
86
86
 
87
87
  // commands/parsable/index.ts
88
- var parsable_default = createCommand((program2) => {
89
- program2.command("parsable").description("Check if the file is parsable").argument("[file]", "The file to check").option("--input, --file <file>", "The file to check").option("--unlimited", "Unlimited timeout").action(
88
+ var parsable_default = createCommand((program3) => {
89
+ program3.command("parsable").description("Check if the file is parsable").argument("[file]", "The file to check").option("--input, --file <file>", "The file to check").option("--unlimited", "Unlimited timeout").action(
90
90
  async (fileArgument, options) => {
91
91
  await timeout(
92
92
  async ({ finish }) => {
@@ -215,8 +215,8 @@ var renameBindingsByScope = (code, filename) => {
215
215
  });
216
216
  return patchDefault(generate)(ast).code;
217
217
  };
218
- var safe_scope_default = createCommand((program2) => {
219
- program2.command("safe-scope").description("Rename bindings per scope for safer transforms").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
218
+ var safe_scope_default = createCommand((program3) => {
219
+ program3.command("safe-scope").description("Rename bindings per scope for safer transforms").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
220
220
  async (fileArgument, options) => {
221
221
  await timeout(
222
222
  async ({ finish }) => {
@@ -450,8 +450,8 @@ var expandArrayAccess = async (code, filename, targetName) => {
450
450
  replacedCount
451
451
  };
452
452
  };
453
- var expand_array_default = createCommand((program2) => {
454
- program2.command("expand-array").description("Expand array index access for primitive values").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--target <name>", "Target array variable name").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
453
+ var expand_array_default = createCommand((program3) => {
454
+ program3.command("expand-array").description("Expand array index access for primitive values").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--target <name>", "Target array variable name").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
455
455
  async (fileArgument, options) => {
456
456
  await timeout(
457
457
  async ({ finish }) => {
@@ -721,8 +721,8 @@ var expandObjectAccess = async (code, filename, targetName) => {
721
721
  replacedCount
722
722
  };
723
723
  };
724
- var expand_object_default = createCommand((program2) => {
725
- program2.command("expand-object").description("Expand object property access for primitive values").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--target <name>", "Target object variable name").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
724
+ var expand_object_default = createCommand((program3) => {
725
+ program3.command("expand-object").description("Expand object property access for primitive values").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--target <name>", "Target object variable name").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
726
726
  async (fileArgument, options) => {
727
727
  await timeout(
728
728
  async ({ finish }) => {
@@ -908,8 +908,8 @@ var packObjectProperties = (code, filename) => {
908
908
  removedStatements
909
909
  };
910
910
  };
911
- var object_packer_default = createCommand((program2) => {
912
- program2.command("object-packer").description("Pack consecutive object property assignments into literals").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
911
+ var object_packer_default = createCommand((program3) => {
912
+ program3.command("object-packer").description("Pack consecutive object property assignments into literals").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
913
913
  async (fileArgument, options) => {
914
914
  await timeout(
915
915
  async ({ finish }) => {
@@ -1105,6 +1105,108 @@ var shouldSkipReferencedIdentifier = (path) => {
1105
1105
  }
1106
1106
  return false;
1107
1107
  };
1108
+ var getArrayReturnExpression = (node) => {
1109
+ if (node.params.length !== 0) return null;
1110
+ if (t4.isBlockStatement(node.body)) {
1111
+ if (node.body.body.length !== 1) return null;
1112
+ const statement = node.body.body[0];
1113
+ if (!t4.isReturnStatement(statement) || !statement.argument) return null;
1114
+ return t4.isArrayExpression(statement.argument) ? statement.argument : null;
1115
+ }
1116
+ return t4.isArrayExpression(node.body) ? node.body : null;
1117
+ };
1118
+ var isSafeArrayElement = (node) => {
1119
+ if (!node) return true;
1120
+ if (t4.isNumericLiteral(node) || t4.isStringLiteral(node) || t4.isBooleanLiteral(node) || t4.isNullLiteral(node)) {
1121
+ return true;
1122
+ }
1123
+ if (t4.isUnaryExpression(node) && (node.operator === "-" || node.operator === "+") && t4.isNumericLiteral(node.argument)) {
1124
+ return true;
1125
+ }
1126
+ if (t4.isArrayExpression(node)) {
1127
+ return node.elements.every((element) => {
1128
+ if (element === null) return true;
1129
+ if (t4.isSpreadElement(element)) return false;
1130
+ return isSafeArrayElement(element);
1131
+ });
1132
+ }
1133
+ return false;
1134
+ };
1135
+ var isSafeArrayExpression = (node) => {
1136
+ return node.elements.every((element) => {
1137
+ if (element === null) return true;
1138
+ if (t4.isSpreadElement(element)) return false;
1139
+ return isSafeArrayElement(element);
1140
+ });
1141
+ };
1142
+ var collectArrayReturnFunctions = (ast) => {
1143
+ const results = [];
1144
+ let counter = 0;
1145
+ patchDefault(traverse5)(ast, {
1146
+ FunctionDeclaration(path) {
1147
+ if (!path.node.id) return;
1148
+ const arrayExpression = getArrayReturnExpression(path.node);
1149
+ if (!arrayExpression || !isSafeArrayExpression(arrayExpression)) return;
1150
+ const binding = path.scope.getBinding(path.node.id.name);
1151
+ if (!binding) return;
1152
+ const programScope = path.scope.getProgramParent();
1153
+ let replacementName = `${path.node.id.name}_${counter}`;
1154
+ counter += 1;
1155
+ while (programScope.hasBinding(replacementName)) {
1156
+ replacementName = `${path.node.id.name}_${counter}`;
1157
+ counter += 1;
1158
+ }
1159
+ results.push({
1160
+ binding,
1161
+ arrayNode: arrayExpression,
1162
+ replacementName,
1163
+ used: false
1164
+ });
1165
+ },
1166
+ VariableDeclarator(path) {
1167
+ if (!t4.isIdentifier(path.node.id)) return;
1168
+ const init = path.node.init;
1169
+ if (!init || !t4.isFunctionExpression(init) && !t4.isArrowFunctionExpression(init)) {
1170
+ return;
1171
+ }
1172
+ const arrayExpression = getArrayReturnExpression(init);
1173
+ if (!arrayExpression || !isSafeArrayExpression(arrayExpression)) return;
1174
+ const binding = path.scope.getBinding(path.node.id.name);
1175
+ if (!binding || !binding.constant || binding.kind !== "const") return;
1176
+ const programScope = path.scope.getProgramParent();
1177
+ let replacementName = `${path.node.id.name}_${counter}`;
1178
+ counter += 1;
1179
+ while (programScope.hasBinding(replacementName)) {
1180
+ replacementName = `${path.node.id.name}_${counter}`;
1181
+ counter += 1;
1182
+ }
1183
+ results.push({
1184
+ binding,
1185
+ arrayNode: arrayExpression,
1186
+ replacementName,
1187
+ used: false
1188
+ });
1189
+ }
1190
+ });
1191
+ return results;
1192
+ };
1193
+ var insertArrayReturnDeclarations = (ast, functions) => {
1194
+ const declarations = functions.filter((info) => info.used).map(
1195
+ (info) => t4.variableDeclaration("var", [
1196
+ t4.variableDeclarator(
1197
+ t4.identifier(info.replacementName),
1198
+ t4.cloneNode(info.arrayNode, true)
1199
+ )
1200
+ ])
1201
+ );
1202
+ if (declarations.length === 0) return;
1203
+ const body = ast.program.body;
1204
+ let insertIndex = 0;
1205
+ while (insertIndex < body.length && t4.isExpressionStatement(body[insertIndex]) && "directive" in (body[insertIndex] || {})) {
1206
+ insertIndex += 1;
1207
+ }
1208
+ body.splice(insertIndex, 0, ...declarations);
1209
+ };
1108
1210
  var preEvaluate = (code, filename) => {
1109
1211
  const ast = parse6(code, createParseOptions(filename));
1110
1212
  const state = {
@@ -1112,7 +1214,23 @@ var preEvaluate = (code, filename) => {
1112
1214
  bindingStack: /* @__PURE__ */ new Set()
1113
1215
  };
1114
1216
  let replacedCount = 0;
1217
+ const arrayReturnFunctions = collectArrayReturnFunctions(ast);
1218
+ const arrayReturnMap = /* @__PURE__ */ new Map();
1219
+ for (const info of arrayReturnFunctions) {
1220
+ arrayReturnMap.set(info.binding, info);
1221
+ }
1115
1222
  patchDefault(traverse5)(ast, {
1223
+ CallExpression(path) {
1224
+ if (!t4.isIdentifier(path.node.callee)) return;
1225
+ if (path.node.arguments.length !== 0) return;
1226
+ const binding = path.scope.getBinding(path.node.callee.name);
1227
+ if (!binding) return;
1228
+ const info = arrayReturnMap.get(binding);
1229
+ if (!info) return;
1230
+ path.replaceWith(t4.identifier(info.replacementName));
1231
+ info.used = true;
1232
+ replacedCount += 1;
1233
+ },
1116
1234
  ReferencedIdentifier(path) {
1117
1235
  if (shouldSkipReferencedIdentifier(path)) {
1118
1236
  return;
@@ -1163,13 +1281,14 @@ var preEvaluate = (code, filename) => {
1163
1281
  }
1164
1282
  }
1165
1283
  });
1284
+ insertArrayReturnDeclarations(ast, arrayReturnFunctions);
1166
1285
  return {
1167
1286
  code: patchDefault(generate5)(ast).code,
1168
1287
  replacedCount
1169
1288
  };
1170
1289
  };
1171
- var pre_evaluate_default = createCommand((program2) => {
1172
- program2.command("pre-evaluate").description("Pre-evaluate const numeric/string expressions").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1290
+ var pre_evaluate_default = createCommand((program3) => {
1291
+ program3.command("pre-evaluate").description("Pre-evaluate const numeric/string expressions").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1173
1292
  async (fileArgument, options) => {
1174
1293
  await timeout(
1175
1294
  async ({ finish }) => {
@@ -1262,8 +1381,8 @@ var removeUpdaters = (code, filename) => {
1262
1381
  replacedCount
1263
1382
  };
1264
1383
  };
1265
- var remove_updater_default = createCommand((program2) => {
1266
- program2.command("remove-updater").description("Replace safe update expressions with += or -=").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1384
+ var remove_updater_default = createCommand((program3) => {
1385
+ program3.command("remove-updater").description("Replace safe update expressions with += or -=").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1267
1386
  async (fileArgument, options) => {
1268
1387
  await timeout(
1269
1388
  async ({ finish }) => {
@@ -1505,8 +1624,8 @@ var removeReassign = (code, filename) => {
1505
1624
  wrapperReplacedCount
1506
1625
  };
1507
1626
  };
1508
- var remove_reassign_default = createCommand((program2) => {
1509
- program2.command("remove-reassign").description("Inline safe alias assignments and wrapper calls").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1627
+ var remove_reassign_default = createCommand((program3) => {
1628
+ program3.command("remove-reassign").description("Inline safe alias assignments and wrapper calls").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1510
1629
  async (fileArgument, options) => {
1511
1630
  await timeout(
1512
1631
  async ({ finish }) => {
@@ -1611,8 +1730,8 @@ var removeUnusedVariables = (code, filename) => {
1611
1730
  changed
1612
1731
  };
1613
1732
  };
1614
- var remove_unused_default = createCommand((program2) => {
1615
- program2.command("remove-unused").description("Remove unused variables and declarations").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1733
+ var remove_unused_default = createCommand((program3) => {
1734
+ program3.command("remove-unused").description("Remove unused variables and declarations").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1616
1735
  async (fileArgument, options) => {
1617
1736
  await timeout(
1618
1737
  async ({ finish }) => {
@@ -1682,11 +1801,15 @@ var isProxyFunctionExpression = (node) => {
1682
1801
  return t8.isFunction(node) && node.params.every((param) => t8.isIdentifier(param)) && (t8.isBlockStatement(node.body) && node.body.body.length === 1 && t8.isReturnStatement(node.body.body[0]) && (node.body.body[0].argument === void 0 || t8.isExpression(node.body.body[0].argument) && isProxyValue(node.body.body[0].argument)) || t8.isArrowFunctionExpression(node) && t8.isExpression(node.body) && isProxyValue(node.body));
1683
1802
  };
1684
1803
  var isProxyValue = (node) => {
1685
- if (t8.isFunction(node) || t8.isBlockStatement(node) || t8.isSequenceExpression(node)) {
1804
+ if (t8.isFunction(node) || t8.isBlockStatement(node) || t8.isSequenceExpression(node) || t8.isAssignmentExpression(node)) {
1686
1805
  return false;
1687
1806
  }
1688
1807
  let isValid = true;
1689
- walk(node, {
1808
+ if (!t8.isExpression(node)) {
1809
+ return false;
1810
+ }
1811
+ const wrapper = t8.file(t8.program([t8.expressionStatement(node)]));
1812
+ walk(wrapper, {
1690
1813
  "SequenceExpression|BlockStatement|Function|AssignmentExpression"(path) {
1691
1814
  isValid = false;
1692
1815
  path.stop();
@@ -1716,9 +1839,31 @@ var ProxyFunction = class {
1716
1839
  ])
1717
1840
  );
1718
1841
  const pathsToReplace = [];
1719
- walk(expression, {
1842
+ const shouldSkipIdentifier = (path) => {
1843
+ const parent = path.parentPath;
1844
+ if (!parent) return false;
1845
+ if (parent.isMemberExpression() || parent.isOptionalMemberExpression()) {
1846
+ if (path.key === "property" && !parent.node.computed) {
1847
+ return true;
1848
+ }
1849
+ }
1850
+ if (parent.isObjectProperty()) {
1851
+ if (parent.node.shorthand) return true;
1852
+ if (parent.get("key") === path && !parent.node.computed) {
1853
+ return true;
1854
+ }
1855
+ }
1856
+ if (parent.isObjectMethod()) {
1857
+ if (parent.get("key") === path && !parent.node.computed) {
1858
+ return true;
1859
+ }
1860
+ }
1861
+ return false;
1862
+ };
1863
+ const wrapper = t8.file(t8.program([t8.expressionStatement(expression)]));
1864
+ walk(wrapper, {
1720
1865
  enter(path) {
1721
- if (t8.isIdentifier(path.node) && !(path.parentPath?.isMemberExpression() && path.key === "property") && paramMap.has(path.node.name)) {
1866
+ if (t8.isIdentifier(path.node) && !shouldSkipIdentifier(path) && paramMap.has(path.node.name)) {
1722
1867
  const replacement = paramMap.get(path.node.name);
1723
1868
  pathsToReplace.push([path, replacement]);
1724
1869
  }
@@ -1794,8 +1939,8 @@ var inlineProxyFunctions = (code, filename) => {
1794
1939
  replacedCount
1795
1940
  };
1796
1941
  };
1797
- var fn_inliner_default = createCommand((program2) => {
1798
- program2.command("fn-inliner").description("Inline proxy function calls into expressions").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1942
+ var fn_inliner_default = createCommand((program3) => {
1943
+ program3.command("fn-inliner").description("Inline proxy function calls into expressions").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1799
1944
  async (fileArgument, options) => {
1800
1945
  await timeout(
1801
1946
  async ({ finish }) => {
@@ -1843,6 +1988,189 @@ var fn_inliner_default = createCommand((program2) => {
1843
1988
  );
1844
1989
  });
1845
1990
 
1991
+ // commands/sequence-split/index.ts
1992
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync10 } from "fs";
1993
+ import { basename as basename10, dirname as dirname10, extname as extname10, join as join10 } from "path";
1994
+ import { parse as parse11 } from "@babel/parser";
1995
+ import traverse10 from "@babel/traverse";
1996
+ import generate10 from "@babel/generator";
1997
+ import * as t9 from "@babel/types";
1998
+ import loading11 from "loading-cli";
1999
+ var walk2 = patchDefault(traverse10);
2000
+ var createDefaultOutputPath10 = (inputPath) => {
2001
+ const ext = extname10(inputPath);
2002
+ if (!ext) {
2003
+ return `${inputPath}.sequence-split.js`;
2004
+ }
2005
+ const base = basename10(inputPath, ext);
2006
+ return join10(dirname10(inputPath), `${base}.sequence-split${ext}`);
2007
+ };
2008
+ var isExcluded = (node) => {
2009
+ return t9.isIdentifier(node) && node.name === "eval";
2010
+ };
2011
+ var sequenceSplit = (code, filename) => {
2012
+ const ast = parse11(code, createParseOptions(filename));
2013
+ let changedCount = 0;
2014
+ const markChanged = () => {
2015
+ changedCount += 1;
2016
+ };
2017
+ walk2(ast, {
2018
+ ConditionalExpression(path) {
2019
+ if (!path.parentPath || !path.parentPath.isExpressionStatement()) return;
2020
+ const replacement = t9.ifStatement(
2021
+ path.node.test,
2022
+ t9.expressionStatement(path.node.consequent),
2023
+ t9.expressionStatement(path.node.alternate)
2024
+ );
2025
+ if (path.parentPath.parentPath && path.parentPath.parentPath.key === "alternate" && path.parentPath.parentPath.isBlockStatement() && path.parentPath.parentPath.node.body.length === 1) {
2026
+ path.parentPath.parentPath.replaceWith(replacement);
2027
+ } else {
2028
+ path.parentPath.replaceWith(replacement);
2029
+ }
2030
+ path.skip();
2031
+ markChanged();
2032
+ },
2033
+ LogicalExpression(path) {
2034
+ if (!path.parentPath || !path.parentPath.isExpressionStatement()) return;
2035
+ if (path.node.operator !== "&&" && path.node.operator !== "||") return;
2036
+ const test = path.node.operator === "&&" ? path.node.left : t9.unaryExpression("!", path.node.left);
2037
+ const replacement = t9.ifStatement(
2038
+ test,
2039
+ t9.expressionStatement(path.node.right)
2040
+ );
2041
+ if (path.parentPath.parentPath && path.parentPath.parentPath.key === "alternate" && path.parentPath.parentPath.isBlockStatement() && path.parentPath.parentPath.node.body.length === 1) {
2042
+ path.parentPath.parentPath.replaceWith(replacement);
2043
+ } else {
2044
+ path.parentPath.replaceWith(replacement);
2045
+ }
2046
+ path.skip();
2047
+ markChanged();
2048
+ },
2049
+ "ForStatement|WhileStatement|DoWhileStatement"(path) {
2050
+ if (!path.isForStatement() && !path.isWhileStatement() && !path.isDoWhileStatement()) return;
2051
+ if (t9.isBlockStatement(path.node.body)) return;
2052
+ path.node.body = t9.blockStatement([path.node.body]);
2053
+ markChanged();
2054
+ },
2055
+ IfStatement(path) {
2056
+ if (!t9.isBlockStatement(path.node.consequent)) {
2057
+ path.node.consequent = t9.blockStatement([path.node.consequent]);
2058
+ markChanged();
2059
+ }
2060
+ if (path.node.alternate && !t9.isBlockStatement(path.node.alternate) && !t9.isIfStatement(path.node.alternate)) {
2061
+ path.node.alternate = t9.blockStatement([path.node.alternate]);
2062
+ markChanged();
2063
+ }
2064
+ },
2065
+ VariableDeclaration(path) {
2066
+ if (path.node.declarations.length <= 1) return;
2067
+ const replacements = path.node.declarations.map(
2068
+ (declaration) => t9.variableDeclaration(path.node.kind, [declaration])
2069
+ );
2070
+ if (path.parentPath?.isForStatement() && path.parentKey === "init") {
2071
+ const lastDeclaration = replacements.pop();
2072
+ if (lastDeclaration) {
2073
+ path.parentPath.insertBefore(replacements);
2074
+ path.parentPath.node.init = lastDeclaration;
2075
+ }
2076
+ } else {
2077
+ path.replaceWithMultiple(replacements);
2078
+ }
2079
+ markChanged();
2080
+ },
2081
+ SequenceExpression(path) {
2082
+ const expressions = path.node.expressions;
2083
+ if (expressions.length === 1 && expressions[0]) {
2084
+ path.replaceWith(expressions[0]);
2085
+ markChanged();
2086
+ return;
2087
+ }
2088
+ let outerPath = path;
2089
+ while (!t9.isStatement(outerPath.node)) {
2090
+ const parent = outerPath.parentPath;
2091
+ if (!parent) return;
2092
+ if (parent.isConditionalExpression() && (outerPath.key === "consequent" || outerPath.key === "alternate") || parent.isLogicalExpression() && outerPath.key === "right" || parent.isForStatement() && (outerPath.key === "test" || outerPath.key === "update") || parent.isDoWhileStatement() && outerPath.key === "test" || parent.isArrowFunctionExpression() && outerPath.key === "body") {
2093
+ return;
2094
+ }
2095
+ outerPath = parent;
2096
+ }
2097
+ const lastExpression = expressions[expressions.length - 1];
2098
+ if (lastExpression && isExcluded(lastExpression)) {
2099
+ const firstExpressions = expressions.splice(0, expressions.length - 2);
2100
+ if (firstExpressions.length > 0) {
2101
+ const expressionStatements = firstExpressions.map(
2102
+ (expression) => t9.expressionStatement(expression)
2103
+ );
2104
+ outerPath.insertBefore(expressionStatements);
2105
+ markChanged();
2106
+ }
2107
+ } else {
2108
+ const finalExpression = expressions.splice(expressions.length - 1, 1)[0];
2109
+ const expressionStatements = expressions.map(
2110
+ (expression) => t9.expressionStatement(expression)
2111
+ );
2112
+ outerPath.insertBefore(expressionStatements);
2113
+ if (finalExpression) {
2114
+ path.replaceWith(finalExpression);
2115
+ }
2116
+ markChanged();
2117
+ }
2118
+ }
2119
+ });
2120
+ return {
2121
+ code: patchDefault(generate10)(ast).code,
2122
+ changedCount
2123
+ };
2124
+ };
2125
+ var sequence_split_default = createCommand((program3) => {
2126
+ program3.command("sequence-split").description("Split sequence expressions into statements").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
2127
+ async (fileArgument, options) => {
2128
+ await timeout(
2129
+ async ({ finish }) => {
2130
+ const filename = fileArgument ?? options.file ?? await createPrompt("Enter the file path:");
2131
+ if (!filename) {
2132
+ showError("No file provided");
2133
+ return finish();
2134
+ }
2135
+ try {
2136
+ const fileContent = readFileSync11(filename, "utf8");
2137
+ const defaultOutputPath = createDefaultOutputPath10(filename);
2138
+ let outputPath = options.output;
2139
+ if (!outputPath) {
2140
+ const promptPath = (await createPrompt("Enter the output file path:"))?.trim();
2141
+ outputPath = promptPath || defaultOutputPath;
2142
+ }
2143
+ const loader = loading11("Splitting sequences...").start();
2144
+ try {
2145
+ const { code: output, changedCount } = sequenceSplit(
2146
+ fileContent,
2147
+ filename
2148
+ );
2149
+ writeFileSync10(outputPath, output, "utf8");
2150
+ loader.succeed(
2151
+ `Saved sequence-split file to: ${outputPath} (${diff(fileContent, output).length} lines changed, ${changedCount} transforms)`
2152
+ );
2153
+ return finish();
2154
+ } catch (error) {
2155
+ loader.fail("Failed to apply sequence-split transform");
2156
+ showError(
2157
+ `Error transforming file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
2158
+ );
2159
+ return finish();
2160
+ }
2161
+ } catch (error) {
2162
+ showError(
2163
+ `Error reading file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
2164
+ );
2165
+ return finish();
2166
+ }
2167
+ },
2168
+ options.unlimited ? null : 120 * 1e3
2169
+ );
2170
+ }
2171
+ );
2172
+ });
2173
+
1846
2174
  // utils/cli/showCredit.ts
1847
2175
  import chalk3 from "chalk";
1848
2176
  var beautify = (strings, ...values) => {
@@ -1867,10 +2195,10 @@ var calmGradienrain = (text) => {
1867
2195
  const endHue = 300;
1868
2196
  const saturation = 0.45;
1869
2197
  const value = 0.8;
1870
- const ease = (t9) => t9 * t9 * (3 - 2 * t9);
2198
+ const ease = (t10) => t10 * t10 * (3 - 2 * t10);
1871
2199
  return text.split("").map((char, i) => {
1872
- const t9 = ease(i / Math.max(text.length - 1, 1));
1873
- const hue = startHue + (endHue - startHue) * t9;
2200
+ const t10 = ease(i / Math.max(text.length - 1, 1));
2201
+ const hue = startHue + (endHue - startHue) * t10;
1874
2202
  const c = value * saturation;
1875
2203
  const h = hue / 60;
1876
2204
  const x = c * (1 - Math.abs(h % 2 - 1));
@@ -1894,14 +2222,14 @@ ${calmGradienrain(`Expose Kit v${VERSION}`)}
1894
2222
  `;
1895
2223
 
1896
2224
  // index.ts
1897
- import { readFileSync as readFileSync11 } from "fs";
2225
+ import { readFileSync as readFileSync12 } from "fs";
1898
2226
  var __filename = fileURLToPath(import.meta.url);
1899
- var __dirname = dirname10(__filename);
1900
- var pkg = JSON.parse(readFileSync11(join10(__dirname, "package.json"), "utf8"));
2227
+ var __dirname = dirname11(__filename);
2228
+ var pkg = JSON.parse(readFileSync12(join11(__dirname, "package.json"), "utf8"));
1901
2229
  console.log(showCredit(pkg.version));
1902
2230
  console.log();
1903
- var program = new Command();
1904
- program.name("expose").description("CLI for Deobfuscating").version(
2231
+ var program2 = new Command();
2232
+ program2.name("expose").description("CLI for Deobfuscating").version(
1905
2233
  chalk4.bold("It's written above, lol"),
1906
2234
  "-v, --version",
1907
2235
  "display version number"
@@ -1916,9 +2244,10 @@ var commands = [
1916
2244
  remove_updater_default,
1917
2245
  remove_reassign_default,
1918
2246
  fn_inliner_default,
1919
- remove_unused_default
2247
+ remove_unused_default,
2248
+ sequence_split_default
1920
2249
  ];
1921
2250
  for (const command of commands) {
1922
- command(program);
2251
+ command(program2);
1923
2252
  }
1924
- program.parse();
2253
+ program2.parse();
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expose-kit",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "author": "EdamAmex <edame8080@gmail.com> (https://github.com/EdamAme-x)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expose-kit",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "author": "EdamAmex <edame8080@gmail.com> (https://github.com/EdamAme-x)",