luau-obfuscator 1.0.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/dist/index.cjs ADDED
@@ -0,0 +1,1097 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ObfuscateDefault: () => ObfuscateDefault,
24
+ PASS_MAP: () => PASS_MAP,
25
+ PASS_ORDER: () => PASS_ORDER,
26
+ obfuscate: () => obfuscate,
27
+ obfuscateByAst: () => obfuscateByAst
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+ var import_luau_parser3 = require("luau-parser");
31
+
32
+ // src/config.ts
33
+ var ObfuscateDefault = {
34
+ Vmify: { active: true },
35
+ Minify: { active: true },
36
+ StringsToExpressions: { active: true, min: 5, max: 10 },
37
+ NumbersToExpressions: { active: true, min: 5, max: 10 },
38
+ EncryptStrings: { active: true },
39
+ EncryptNumbers: { active: true },
40
+ RenameVariables: { active: true, random: () => "_" + globalThis.crypto.randomUUID().replace(/-/g, "") },
41
+ GlobalMapping: { active: true, tableName: "GLOBAL" },
42
+ ConstantArray: { active: true },
43
+ InsertJunk: { active: true, probability: 0.7, maxPerBlock: 5 },
44
+ WrapInFunction: { active: true }
45
+ };
46
+ function mergeConfig(defaults, partial) {
47
+ const result = {};
48
+ for (const key of Object.keys(defaults)) {
49
+ const def = defaults[key];
50
+ const part = partial[key];
51
+ if (!part) {
52
+ result[key] = def;
53
+ } else if (part.active === false) {
54
+ result[key] = { active: false };
55
+ } else {
56
+ result[key] = { ...def, ...part, active: true };
57
+ }
58
+ }
59
+ return result;
60
+ }
61
+
62
+ // src/passes/StripTypes.ts
63
+ function runStripTypes(program) {
64
+ stripBlock(program.body);
65
+ }
66
+ function stripBlock(block2) {
67
+ const kept = [];
68
+ for (const stmt of block2.statements) {
69
+ if (stmt.type === "TypeAliasStatement" || stmt.type === "ExportTypeAliasStatement") {
70
+ continue;
71
+ }
72
+ stripStatement(stmt);
73
+ kept.push(stmt);
74
+ }
75
+ block2.statements = kept;
76
+ }
77
+ function stripFunctionBody(func) {
78
+ func.generics = [];
79
+ for (const param of func.params) delete param.typeAnnotation;
80
+ delete func.varargTypeAnnotation;
81
+ delete func.returnType;
82
+ stripBlock(func.body);
83
+ }
84
+ function stripStatement(stmt) {
85
+ switch (stmt.type) {
86
+ case "LocalStatement":
87
+ for (const name of stmt.names) delete name.typeAnnotation;
88
+ for (let i = 0; i < stmt.init.length; i++) stmt.init[i] = stripExpr(stmt.init[i]);
89
+ return;
90
+ case "LocalFunctionStatement":
91
+ stripFunctionBody(stmt.func);
92
+ return;
93
+ case "FunctionDeclarationStatement":
94
+ stripFunctionBody(stmt.func);
95
+ return;
96
+ case "AssignmentStatement":
97
+ for (let i = 0; i < stmt.targets.length; i++) stmt.targets[i] = stripExpr(stmt.targets[i]);
98
+ for (let i = 0; i < stmt.values.length; i++) stmt.values[i] = stripExpr(stmt.values[i]);
99
+ return;
100
+ case "CompoundAssignmentStatement":
101
+ stmt.target = stripExpr(stmt.target);
102
+ stmt.value = stripExpr(stmt.value);
103
+ return;
104
+ case "CallStatement":
105
+ stmt.expression = stripExpr(stmt.expression);
106
+ return;
107
+ case "DoStatement":
108
+ stripBlock(stmt.body);
109
+ return;
110
+ case "WhileStatement":
111
+ stmt.condition = stripExpr(stmt.condition);
112
+ stripBlock(stmt.body);
113
+ return;
114
+ case "RepeatStatement":
115
+ stripBlock(stmt.body);
116
+ stmt.condition = stripExpr(stmt.condition);
117
+ return;
118
+ case "IfStatement":
119
+ for (const clause of stmt.clauses) {
120
+ clause.condition = stripExpr(clause.condition);
121
+ stripBlock(clause.body);
122
+ }
123
+ if (stmt.alternate) stripBlock(stmt.alternate);
124
+ return;
125
+ case "NumericForStatement":
126
+ delete stmt.variable.typeAnnotation;
127
+ stmt.start = stripExpr(stmt.start);
128
+ stmt.end = stripExpr(stmt.end);
129
+ if (stmt.step) stmt.step = stripExpr(stmt.step);
130
+ stripBlock(stmt.body);
131
+ return;
132
+ case "GenericForStatement":
133
+ for (const v of stmt.variables) delete v.typeAnnotation;
134
+ for (let i = 0; i < stmt.iterators.length; i++) stmt.iterators[i] = stripExpr(stmt.iterators[i]);
135
+ stripBlock(stmt.body);
136
+ return;
137
+ case "ReturnStatement":
138
+ for (let i = 0; i < stmt.arguments.length; i++) stmt.arguments[i] = stripExpr(stmt.arguments[i]);
139
+ return;
140
+ case "BreakStatement":
141
+ case "ContinueStatement":
142
+ return;
143
+ }
144
+ }
145
+ function stripExpr(expr) {
146
+ switch (expr.type) {
147
+ case "InterpolatedStringExpression":
148
+ for (const part of expr.parts) {
149
+ if (part.kind === "expression") part.expression = stripExpr(part.expression);
150
+ }
151
+ return expr;
152
+ case "FunctionExpression":
153
+ stripFunctionBody(expr.func);
154
+ return expr;
155
+ case "TableExpression":
156
+ for (const field of expr.fields) {
157
+ if (field.type === "TableFieldPositional") {
158
+ field.value = stripExpr(field.value);
159
+ } else if (field.type === "TableFieldNamed") {
160
+ field.value = stripExpr(field.value);
161
+ } else if (field.type === "TableFieldComputed") {
162
+ field.key = stripExpr(field.key);
163
+ field.value = stripExpr(field.value);
164
+ }
165
+ }
166
+ return expr;
167
+ case "BinaryExpression":
168
+ expr.left = stripExpr(expr.left);
169
+ expr.right = stripExpr(expr.right);
170
+ return expr;
171
+ case "UnaryExpression":
172
+ expr.argument = stripExpr(expr.argument);
173
+ return expr;
174
+ case "MemberExpression":
175
+ expr.object = stripExpr(expr.object);
176
+ return expr;
177
+ case "IndexExpression":
178
+ expr.object = stripExpr(expr.object);
179
+ expr.index = stripExpr(expr.index);
180
+ return expr;
181
+ case "CallExpression":
182
+ expr.callee = stripExpr(expr.callee);
183
+ for (let i = 0; i < expr.arguments.length; i++) expr.arguments[i] = stripExpr(expr.arguments[i]);
184
+ return expr;
185
+ case "MethodCallExpression":
186
+ expr.object = stripExpr(expr.object);
187
+ for (let i = 0; i < expr.arguments.length; i++) expr.arguments[i] = stripExpr(expr.arguments[i]);
188
+ return expr;
189
+ case "ParenthesizedExpression":
190
+ expr.expression = stripExpr(expr.expression);
191
+ return expr;
192
+ case "TypeAssertionExpression":
193
+ return stripExpr(expr.expression);
194
+ case "IfElseExpression":
195
+ for (const clause of expr.clauses) {
196
+ clause.condition = stripExpr(clause.condition);
197
+ clause.body = stripExpr(clause.body);
198
+ }
199
+ expr.alternate = stripExpr(expr.alternate);
200
+ return expr;
201
+ default:
202
+ return expr;
203
+ }
204
+ }
205
+
206
+ // src/passes/walk.ts
207
+ function visitArray(arr, visitor) {
208
+ for (let i = 0; i < arr.length; i++) {
209
+ arr[i] = mapExpression(arr[i], visitor);
210
+ }
211
+ }
212
+ function mapExpression(expr, visitor) {
213
+ switch (expr.type) {
214
+ case "InterpolatedStringExpression":
215
+ for (const part of expr.parts) {
216
+ if (part.kind === "expression") {
217
+ part.expression = mapExpression(part.expression, visitor);
218
+ }
219
+ }
220
+ break;
221
+ case "FunctionExpression":
222
+ walkFunctionBody(expr.func, visitor);
223
+ break;
224
+ case "TableExpression":
225
+ for (const field of expr.fields) {
226
+ if (field.type === "TableFieldPositional") {
227
+ field.value = mapExpression(field.value, visitor);
228
+ } else if (field.type === "TableFieldNamed") {
229
+ field.value = mapExpression(field.value, visitor);
230
+ } else if (field.type === "TableFieldComputed") {
231
+ field.key = mapExpression(field.key, visitor);
232
+ field.value = mapExpression(field.value, visitor);
233
+ }
234
+ }
235
+ break;
236
+ case "BinaryExpression":
237
+ expr.left = mapExpression(expr.left, visitor);
238
+ expr.right = mapExpression(expr.right, visitor);
239
+ break;
240
+ case "UnaryExpression":
241
+ expr.argument = mapExpression(expr.argument, visitor);
242
+ break;
243
+ case "MemberExpression":
244
+ expr.object = mapExpression(expr.object, visitor);
245
+ break;
246
+ case "IndexExpression":
247
+ expr.object = mapExpression(expr.object, visitor);
248
+ expr.index = mapExpression(expr.index, visitor);
249
+ break;
250
+ case "CallExpression":
251
+ expr.callee = mapExpression(expr.callee, visitor);
252
+ visitArray(expr.arguments, visitor);
253
+ break;
254
+ case "MethodCallExpression":
255
+ expr.object = mapExpression(expr.object, visitor);
256
+ visitArray(expr.arguments, visitor);
257
+ break;
258
+ case "ParenthesizedExpression":
259
+ expr.expression = mapExpression(expr.expression, visitor);
260
+ break;
261
+ case "TypeAssertionExpression":
262
+ expr.expression = mapExpression(expr.expression, visitor);
263
+ break;
264
+ case "IfElseExpression":
265
+ for (const clause of expr.clauses) {
266
+ clause.condition = mapExpression(clause.condition, visitor);
267
+ clause.body = mapExpression(clause.body, visitor);
268
+ }
269
+ expr.alternate = mapExpression(expr.alternate, visitor);
270
+ break;
271
+ }
272
+ return visitor(expr) ?? expr;
273
+ }
274
+ function walkFunctionBody(func, visitor) {
275
+ walkBlock(func.body, visitor);
276
+ }
277
+ function walkBlock(block2, visitor) {
278
+ for (const stmt of block2.statements) walkStatement(stmt, visitor);
279
+ }
280
+ function walkStatement(stmt, visitor) {
281
+ switch (stmt.type) {
282
+ case "LocalStatement":
283
+ visitArray(stmt.init, visitor);
284
+ return;
285
+ case "LocalFunctionStatement":
286
+ walkFunctionBody(stmt.func, visitor);
287
+ return;
288
+ case "FunctionDeclarationStatement":
289
+ walkFunctionBody(stmt.func, visitor);
290
+ return;
291
+ case "AssignmentStatement":
292
+ visitArray(stmt.targets, visitor);
293
+ visitArray(stmt.values, visitor);
294
+ return;
295
+ case "CompoundAssignmentStatement":
296
+ stmt.target = mapExpression(stmt.target, visitor);
297
+ stmt.value = mapExpression(stmt.value, visitor);
298
+ return;
299
+ case "CallStatement":
300
+ stmt.expression = mapExpression(stmt.expression, visitor);
301
+ return;
302
+ case "DoStatement":
303
+ walkBlock(stmt.body, visitor);
304
+ return;
305
+ case "WhileStatement":
306
+ stmt.condition = mapExpression(stmt.condition, visitor);
307
+ walkBlock(stmt.body, visitor);
308
+ return;
309
+ case "RepeatStatement":
310
+ walkBlock(stmt.body, visitor);
311
+ stmt.condition = mapExpression(stmt.condition, visitor);
312
+ return;
313
+ case "IfStatement":
314
+ for (const clause of stmt.clauses) {
315
+ clause.condition = mapExpression(clause.condition, visitor);
316
+ walkBlock(clause.body, visitor);
317
+ }
318
+ if (stmt.alternate) walkBlock(stmt.alternate, visitor);
319
+ return;
320
+ case "NumericForStatement":
321
+ stmt.start = mapExpression(stmt.start, visitor);
322
+ stmt.end = mapExpression(stmt.end, visitor);
323
+ if (stmt.step) stmt.step = mapExpression(stmt.step, visitor);
324
+ walkBlock(stmt.body, visitor);
325
+ return;
326
+ case "GenericForStatement":
327
+ visitArray(stmt.iterators, visitor);
328
+ walkBlock(stmt.body, visitor);
329
+ return;
330
+ case "ReturnStatement":
331
+ visitArray(stmt.arguments, visitor);
332
+ return;
333
+ case "BreakStatement":
334
+ case "ContinueStatement":
335
+ case "TypeAliasStatement":
336
+ case "ExportTypeAliasStatement":
337
+ return;
338
+ }
339
+ }
340
+ function transformExpressions(program, visitor) {
341
+ walkBlock(program.body, visitor);
342
+ }
343
+
344
+ // src/passes/nodeFactory.ts
345
+ var POS = { start: 0, end: 0 };
346
+ function identifier(name) {
347
+ return { type: "Identifier", name, line: POS, column: POS };
348
+ }
349
+ function typedIdentifier(name) {
350
+ return { type: "TypedIdentifier", name, line: POS, column: POS };
351
+ }
352
+ function stringLiteral(value) {
353
+ return { type: "StringLiteral", value, raw: JSON.stringify(value), line: POS, column: POS };
354
+ }
355
+ function numberLiteral(value) {
356
+ return { type: "NumberLiteral", value, raw: String(value), line: POS, column: POS };
357
+ }
358
+ function binary(operator, left, right) {
359
+ return { type: "BinaryExpression", operator, left, right, line: POS, column: POS };
360
+ }
361
+ function unary(operator, argument) {
362
+ return { type: "UnaryExpression", operator, argument, line: POS, column: POS };
363
+ }
364
+ function call(callee, args) {
365
+ return { type: "CallExpression", callee, arguments: args, line: POS, column: POS };
366
+ }
367
+ function member(object, property) {
368
+ return { type: "MemberExpression", object, property: identifier(property), line: POS, column: POS };
369
+ }
370
+ function index(object, key) {
371
+ return { type: "IndexExpression", object, index: key, line: POS, column: POS };
372
+ }
373
+ function paren(expression) {
374
+ return { type: "ParenthesizedExpression", expression, line: POS, column: POS };
375
+ }
376
+ function table(fields) {
377
+ return { type: "TableExpression", fields, line: POS, column: POS };
378
+ }
379
+ function computedField(key, value) {
380
+ return { type: "TableFieldComputed", key, value };
381
+ }
382
+ function positionalField(value) {
383
+ return { type: "TableFieldPositional", value };
384
+ }
385
+ function localStatement(name, init) {
386
+ return {
387
+ type: "LocalStatement",
388
+ names: [typedIdentifier(name)],
389
+ init: [init],
390
+ line: POS,
391
+ column: POS
392
+ };
393
+ }
394
+ function block(statements) {
395
+ return { type: "Block", statements, line: POS, column: POS };
396
+ }
397
+ function assignmentStatement(targets, values) {
398
+ return { type: "AssignmentStatement", targets, values, line: POS, column: POS };
399
+ }
400
+ function returnStatement(args) {
401
+ return { type: "ReturnStatement", arguments: args, line: POS, column: POS };
402
+ }
403
+ function numericForStatement(variableName, start, end, body) {
404
+ return {
405
+ type: "NumericForStatement",
406
+ variable: typedIdentifier(variableName),
407
+ start,
408
+ end,
409
+ body,
410
+ line: POS,
411
+ column: POS
412
+ };
413
+ }
414
+ function functionParam(name) {
415
+ return { type: "FunctionParameter", name, line: POS, column: POS };
416
+ }
417
+ function functionBody(params, body, hasVarargs = false) {
418
+ return {
419
+ type: "FunctionBody",
420
+ generics: [],
421
+ params,
422
+ hasVarargs,
423
+ body,
424
+ line: POS,
425
+ column: POS
426
+ };
427
+ }
428
+ function functionExpression(func) {
429
+ return { type: "FunctionExpression", func, line: POS, column: POS };
430
+ }
431
+ function localFunctionStatement(name, func) {
432
+ return {
433
+ type: "LocalFunctionStatement",
434
+ name: identifier(name),
435
+ func,
436
+ line: POS,
437
+ column: POS
438
+ };
439
+ }
440
+ function doStatement(body) {
441
+ return { type: "DoStatement", body, line: POS, column: POS };
442
+ }
443
+ function ifClause(condition, body) {
444
+ return { type: "IfClause", condition, body, line: POS, column: POS };
445
+ }
446
+ function ifStatement(clauses, alternate) {
447
+ return { type: "IfStatement", clauses, alternate, line: POS, column: POS };
448
+ }
449
+ function vararg() {
450
+ return { type: "VarargExpression", line: POS, column: POS };
451
+ }
452
+
453
+ // src/passes/StringsToExpressions.ts
454
+ function randomInt(min, max) {
455
+ return Math.floor(Math.random() * (max - min + 1)) + min;
456
+ }
457
+ function toUtf8Bytes(value) {
458
+ return Array.from(new TextEncoder().encode(value));
459
+ }
460
+ function splitByteChunks(bytes, min, max) {
461
+ const chunks = [];
462
+ let i = 0;
463
+ while (i < bytes.length) {
464
+ const size = Math.max(1, randomInt(min, max));
465
+ chunks.push(bytes.slice(i, i + size));
466
+ i += size;
467
+ }
468
+ return chunks.length > 0 ? chunks : [bytes];
469
+ }
470
+ function stringCharCall(bytes) {
471
+ return call(member(identifier("string"), "char"), bytes.map(numberLiteral));
472
+ }
473
+ function buildConcatChain(chunks) {
474
+ let expr = stringCharCall(chunks[0]);
475
+ for (let i = 1; i < chunks.length; i++) {
476
+ expr = binary("..", expr, stringCharCall(chunks[i]));
477
+ }
478
+ return expr;
479
+ }
480
+ function runStringsToExpressions(program, options) {
481
+ transformExpressions(program, (expr) => {
482
+ if (expr.type !== "StringLiteral") return;
483
+ if (expr.value.length === 0) return;
484
+ const bytes = toUtf8Bytes(expr.value);
485
+ const chunks = splitByteChunks(bytes, options.min, options.max);
486
+ return buildConcatChain(chunks);
487
+ });
488
+ }
489
+
490
+ // src/passes/NumbersToExpressions.ts
491
+ var MAX_DEPTH = 1;
492
+ var NEST_PROBABILITY = 0.35;
493
+ function randomInt2(min, max) {
494
+ return Math.floor(Math.random() * (max - min + 1)) + min;
495
+ }
496
+ function randSign() {
497
+ return Math.random() < 0.5 ? 1 : -1;
498
+ }
499
+ function pickStrategy(value, min, max) {
500
+ const candidates = ["add", "sub"];
501
+ if (value !== 0) {
502
+ for (let a = min; a <= max; a++) {
503
+ if (a !== 0 && value % a === 0) {
504
+ candidates.push("mul");
505
+ break;
506
+ }
507
+ }
508
+ }
509
+ return candidates[randomInt2(0, candidates.length - 1)];
510
+ }
511
+ function operand(n, min, max, depth) {
512
+ if (depth < MAX_DEPTH && Math.random() < NEST_PROBABILITY) {
513
+ return buildNumberExpr(n, min, max, depth + 1);
514
+ }
515
+ return numberLiteral(n);
516
+ }
517
+ function buildNumberExpr(value, min, max, depth = 0) {
518
+ const strategy = pickStrategy(value, min, max);
519
+ let inner;
520
+ switch (strategy) {
521
+ case "add": {
522
+ const a = randomInt2(min, max) * randSign();
523
+ const b = value - a;
524
+ inner = binary("+", operand(a, min, max, depth), operand(b, min, max, depth));
525
+ break;
526
+ }
527
+ case "sub": {
528
+ const a = randomInt2(min, max) * randSign();
529
+ const b = a - value;
530
+ inner = binary("-", operand(a, min, max, depth), operand(b, min, max, depth));
531
+ break;
532
+ }
533
+ case "mul": {
534
+ let a = 1;
535
+ for (let cand = min; cand <= max; cand++) {
536
+ if (cand !== 0 && value % cand === 0) {
537
+ a = cand;
538
+ break;
539
+ }
540
+ }
541
+ const b = value / a;
542
+ inner = binary("*", operand(a, min, max, depth), operand(b, min, max, depth));
543
+ break;
544
+ }
545
+ }
546
+ return paren(inner);
547
+ }
548
+ function runNumbersToExpressions(program, options) {
549
+ transformExpressions(program, (expr) => {
550
+ if (expr.type !== "NumberLiteral") return;
551
+ return buildNumberExpr(expr.value, options.min, options.max);
552
+ });
553
+ }
554
+
555
+ // src/passes/RenameVariables.ts
556
+ var import_luau_parser = require("luau-parser");
557
+ var counter = 0;
558
+ function defaultRandomName() {
559
+ counter += 1;
560
+ return `_l${counter.toString(36)}`;
561
+ }
562
+ function runRenameVariables(program, options) {
563
+ const analysis = (0, import_luau_parser.analyzeScopes)(program);
564
+ const makeName = options.random ?? defaultRandomName;
565
+ for (const binding of analysis.bindings.values()) {
566
+ if ((0, import_luau_parser.isGlobal)(binding)) continue;
567
+ if (binding.kind === "self") continue;
568
+ const newName = makeName();
569
+ binding.name = newName;
570
+ if (binding.declarationNode) {
571
+ binding.declarationNode.name = newName;
572
+ }
573
+ for (const ref of binding.references) {
574
+ ref.name = newName;
575
+ }
576
+ }
577
+ }
578
+
579
+ // src/passes/GlobalMapping.ts
580
+ var import_luau_parser2 = require("luau-parser");
581
+ function randomInt3(min, max) {
582
+ return Math.floor(Math.random() * (max - min + 1)) + min;
583
+ }
584
+ function randomKey() {
585
+ if (Math.random() < 0.5) return randomInt3(1, 50);
586
+ const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
587
+ const len = randomInt3(2, 4);
588
+ let s = "";
589
+ for (let i = 0; i < len; i++) s += chars[randomInt3(0, chars.length - 1)];
590
+ return s;
591
+ }
592
+ function randomPath() {
593
+ const depth = randomInt3(2, 4);
594
+ const path = [];
595
+ for (let i = 0; i < depth; i++) path.push(randomKey());
596
+ return path;
597
+ }
598
+ function isLeaf(v) {
599
+ return !(v instanceof Map);
600
+ }
601
+ function insertPath(root, path, globalName) {
602
+ let node = root;
603
+ for (let i = 0; i < path.length - 1; i++) {
604
+ const key = path[i];
605
+ let next = node.get(key);
606
+ if (next === void 0) {
607
+ next = /* @__PURE__ */ new Map();
608
+ node.set(key, next);
609
+ } else if (isLeaf(next)) {
610
+ return false;
611
+ }
612
+ node = next;
613
+ }
614
+ const lastKey = path[path.length - 1];
615
+ if (node.has(lastKey)) return false;
616
+ node.set(lastKey, { leaf: globalName });
617
+ return true;
618
+ }
619
+ function assignPaths(globalNames) {
620
+ const root = /* @__PURE__ */ new Map();
621
+ const paths = /* @__PURE__ */ new Map();
622
+ for (const name of globalNames) {
623
+ let path = [];
624
+ let attempts = 0;
625
+ let ok = false;
626
+ while (!ok && attempts < 200) {
627
+ path = randomPath();
628
+ ok = insertPath(root, path, name);
629
+ attempts++;
630
+ }
631
+ paths.set(name, path);
632
+ }
633
+ return { root, paths };
634
+ }
635
+ function keyExpression(key) {
636
+ return typeof key === "number" ? numberLiteral(key) : stringLiteral(key);
637
+ }
638
+ function buildTreeTableExpr(node) {
639
+ const fields = [...node.entries()].map(
640
+ ([key, value]) => computedField(
641
+ keyExpression(key),
642
+ isLeaf(value) ? identifier(value.leaf) : buildTreeTableExpr(value)
643
+ )
644
+ );
645
+ return table(fields);
646
+ }
647
+ function buildIndexChain(tableName, path) {
648
+ let expr = identifier(tableName);
649
+ for (const key of path) {
650
+ expr = index(expr, keyExpression(key));
651
+ }
652
+ return expr;
653
+ }
654
+ function morphIntoIndexChain(node, tableName, path) {
655
+ const built = buildIndexChain(tableName, path);
656
+ const target = node;
657
+ for (const k of Object.keys(target)) delete target[k];
658
+ Object.assign(target, built);
659
+ }
660
+ function collectUnsafeFunctionDeclGlobals(program, analysis) {
661
+ const unsafe = /* @__PURE__ */ new Set();
662
+ function visitBlock(block2) {
663
+ for (const stmt of block2.statements) visitStatement(stmt);
664
+ }
665
+ function visitStatement(stmt) {
666
+ switch (stmt.type) {
667
+ case "FunctionDeclarationStatement": {
668
+ const binding = (0, import_luau_parser2.getBinding)(analysis, stmt.target.base);
669
+ if (binding && (0, import_luau_parser2.isGlobal)(binding)) unsafe.add(binding.name);
670
+ visitBlock(stmt.func.body);
671
+ return;
672
+ }
673
+ case "LocalFunctionStatement":
674
+ visitBlock(stmt.func.body);
675
+ return;
676
+ case "DoStatement":
677
+ visitBlock(stmt.body);
678
+ return;
679
+ case "WhileStatement":
680
+ visitBlock(stmt.body);
681
+ return;
682
+ case "RepeatStatement":
683
+ visitBlock(stmt.body);
684
+ return;
685
+ case "IfStatement":
686
+ for (const clause of stmt.clauses) visitBlock(clause.body);
687
+ if (stmt.alternate) visitBlock(stmt.alternate);
688
+ return;
689
+ case "NumericForStatement":
690
+ visitBlock(stmt.body);
691
+ return;
692
+ case "GenericForStatement":
693
+ visitBlock(stmt.body);
694
+ return;
695
+ default:
696
+ return;
697
+ }
698
+ }
699
+ visitBlock(program.body);
700
+ return unsafe;
701
+ }
702
+ function runGlobalMapping(program, options) {
703
+ const analysis = (0, import_luau_parser2.analyzeScopes)(program);
704
+ const unsafe = collectUnsafeFunctionDeclGlobals(program, analysis);
705
+ const globalBindings = [...analysis.bindings.values()].filter(
706
+ (b) => (0, import_luau_parser2.isGlobal)(b) && b.references.length > 0 && !unsafe.has(b.name)
707
+ );
708
+ if (globalBindings.length === 0) return;
709
+ const { root, paths } = assignPaths(globalBindings.map((b) => b.name));
710
+ for (const binding of globalBindings) {
711
+ const path = paths.get(binding.name);
712
+ for (const ref of binding.references) {
713
+ morphIntoIndexChain(ref, options.tableName, path);
714
+ }
715
+ }
716
+ program.body.statements.unshift(
717
+ localStatement(options.tableName, buildTreeTableExpr(root))
718
+ );
719
+ }
720
+
721
+ // src/passes/ConstantArray.ts
722
+ function keyOf(kind, value) {
723
+ return kind === "string" ? `s:${value}` : `n:${value}`;
724
+ }
725
+ function shuffleIndices(length) {
726
+ const order = Array.from({ length }, (_, i) => i);
727
+ for (let i = order.length - 1; i > 0; i--) {
728
+ const j = Math.floor(Math.random() * (i + 1));
729
+ [order[i], order[j]] = [order[j], order[i]];
730
+ }
731
+ return order;
732
+ }
733
+ function defaultArrayName() {
734
+ return "_CA" + Math.random().toString(36).slice(2, 8);
735
+ }
736
+ function runConstantArray(program, options) {
737
+ const order = [];
738
+ const seen = /* @__PURE__ */ new Set();
739
+ transformExpressions(program, (expr) => {
740
+ if (expr.type === "StringLiteral") {
741
+ const k = keyOf("string", expr.value);
742
+ if (!seen.has(k)) {
743
+ seen.add(k);
744
+ order.push({ kind: "string", value: expr.value });
745
+ }
746
+ } else if (expr.type === "NumberLiteral") {
747
+ const k = keyOf("number", expr.value);
748
+ if (!seen.has(k)) {
749
+ seen.add(k);
750
+ order.push({ kind: "number", value: expr.value });
751
+ }
752
+ }
753
+ return void 0;
754
+ });
755
+ if (order.length === 0) return;
756
+ const shuffledOrder = shuffleIndices(order.length);
757
+ const indexOf = /* @__PURE__ */ new Map();
758
+ shuffledOrder.forEach((originalIndex, slot) => {
759
+ const entry = order[originalIndex];
760
+ indexOf.set(keyOf(entry.kind, entry.value), slot + 1);
761
+ });
762
+ const arrayName = options.arrayName ?? defaultArrayName();
763
+ transformExpressions(program, (expr) => {
764
+ if (expr.type === "StringLiteral") {
765
+ const idx = indexOf.get(keyOf("string", expr.value));
766
+ return index(identifier(arrayName), numberLiteral(idx));
767
+ }
768
+ if (expr.type === "NumberLiteral") {
769
+ const idx = indexOf.get(keyOf("number", expr.value));
770
+ return index(identifier(arrayName), numberLiteral(idx));
771
+ }
772
+ return void 0;
773
+ });
774
+ const fields = shuffledOrder.map((originalIndex) => {
775
+ const entry = order[originalIndex];
776
+ const value = entry.kind === "string" ? stringLiteral(entry.value) : numberLiteral(entry.value);
777
+ return { type: "TableFieldPositional", value };
778
+ });
779
+ program.body.statements.unshift(localStatement(arrayName, table(fields)));
780
+ }
781
+
782
+ // src/passes/EncryptStrings.ts
783
+ function randomInt4(min, max) {
784
+ return Math.floor(Math.random() * (max - min + 1)) + min;
785
+ }
786
+ function randomName() {
787
+ return "_" + globalThis.crypto.randomUUID().replace(/-/g, "");
788
+ }
789
+ function toUtf8Bytes2(value) {
790
+ return Array.from(new TextEncoder().encode(value));
791
+ }
792
+ function buildDecoderStatement(name) {
793
+ const dataParam = randomName();
794
+ const keyParam = randomName();
795
+ const outVar = randomName();
796
+ const iVar = randomName();
797
+ const body = block([
798
+ localStatement(outVar, table([])),
799
+ numericForStatement(
800
+ iVar,
801
+ numberLiteral(1),
802
+ unary("#", identifier(dataParam)),
803
+ block([
804
+ assignmentStatement(
805
+ [index(identifier(outVar), identifier(iVar))],
806
+ [call(member(identifier("string"), "char"), [
807
+ call(member(identifier("bit32"), "bxor"), [
808
+ index(identifier(dataParam), identifier(iVar)),
809
+ identifier(keyParam)
810
+ ])
811
+ ])]
812
+ )
813
+ ])
814
+ ),
815
+ returnStatement([call(member(identifier("table"), "concat"), [identifier(outVar)])])
816
+ ]);
817
+ return localFunctionStatement(name, functionBody([functionParam(dataParam), functionParam(keyParam)], body));
818
+ }
819
+ function runEncryptStrings(program, _options) {
820
+ const decoderName = randomName();
821
+ let used = false;
822
+ transformExpressions(program, (expr) => {
823
+ if (expr.type !== "StringLiteral") return;
824
+ if (expr.value.length === 0) return;
825
+ used = true;
826
+ const key = randomInt4(1, 255);
827
+ const bytes = toUtf8Bytes2(expr.value).map((b) => b ^ key);
828
+ const dataTable = table(bytes.map((b) => positionalField(numberLiteral(b))));
829
+ return call(identifier(decoderName), [dataTable, numberLiteral(key)]);
830
+ });
831
+ if (!used) return;
832
+ program.body.statements.unshift(buildDecoderStatement(decoderName));
833
+ }
834
+
835
+ // src/passes/EncryptNumbers.ts
836
+ var UINT32_MAX = 4294967295;
837
+ function randomInt5(min, max) {
838
+ return Math.floor(Math.random() * (max - min + 1)) + min;
839
+ }
840
+ function randomName2() {
841
+ return "_" + globalThis.crypto.randomUUID().replace(/-/g, "");
842
+ }
843
+ function isEncryptable(value) {
844
+ return Number.isInteger(value) && value >= 0 && value <= UINT32_MAX;
845
+ }
846
+ function xor32(a, b) {
847
+ return (a ^ b) >>> 0;
848
+ }
849
+ function buildDecoderStatement2(name) {
850
+ const nParam = randomName2();
851
+ const keyParam = randomName2();
852
+ const body = block([
853
+ returnStatement([call(member(identifier("bit32"), "bxor"), [identifier(nParam), identifier(keyParam)])])
854
+ ]);
855
+ return localFunctionStatement(name, functionBody([functionParam(nParam), functionParam(keyParam)], body));
856
+ }
857
+ function runEncryptNumbers(program, _options) {
858
+ const decoderName = randomName2();
859
+ let used = false;
860
+ transformExpressions(program, (expr) => {
861
+ if (expr.type !== "NumberLiteral") return;
862
+ if (!isEncryptable(expr.value)) return;
863
+ used = true;
864
+ const key = randomInt5(1, 16777215);
865
+ const encoded = xor32(expr.value, key);
866
+ return call(identifier(decoderName), [numberLiteral(encoded), numberLiteral(key)]);
867
+ });
868
+ if (!used) return;
869
+ program.body.statements.unshift(buildDecoderStatement2(decoderName));
870
+ }
871
+
872
+ // src/passes/InsertJunk.ts
873
+ function randomInt6(min, max) {
874
+ return Math.floor(Math.random() * (max - min + 1)) + min;
875
+ }
876
+ function randomName3() {
877
+ return "_" + globalThis.crypto.randomUUID().replace(/-/g, "");
878
+ }
879
+ function junkNumberExpr() {
880
+ const op = Math.random() < 0.5 ? "+" : "*";
881
+ return binary(op, numberLiteral(randomInt6(1, 999)), numberLiteral(randomInt6(1, 999)));
882
+ }
883
+ function buildJunkStatement() {
884
+ const variants = [
885
+ () => localStatement(randomName3(), junkNumberExpr()),
886
+ () => doStatement(block([localStatement(randomName3(), junkNumberExpr())])),
887
+ () => ifStatement([
888
+ ifClause(
889
+ binary("==", numberLiteral(randomInt6(1, 999)), numberLiteral(randomInt6(1, 999))),
890
+ block([localStatement(randomName3(), junkNumberExpr())])
891
+ )
892
+ ])
893
+ ];
894
+ return variants[randomInt6(0, variants.length - 1)]();
895
+ }
896
+ function runInsertJunk(program, options) {
897
+ function processExpr(expr) {
898
+ switch (expr.type) {
899
+ case "InterpolatedStringExpression":
900
+ for (const part of expr.parts) {
901
+ if (part.kind === "expression") processExpr(part.expression);
902
+ }
903
+ return;
904
+ case "FunctionExpression":
905
+ processBlock(expr.func.body);
906
+ return;
907
+ case "TableExpression":
908
+ for (const field of expr.fields) {
909
+ if (field.type === "TableFieldPositional") processExpr(field.value);
910
+ else if (field.type === "TableFieldNamed") processExpr(field.value);
911
+ else {
912
+ processExpr(field.key);
913
+ processExpr(field.value);
914
+ }
915
+ }
916
+ return;
917
+ case "BinaryExpression":
918
+ processExpr(expr.left);
919
+ processExpr(expr.right);
920
+ return;
921
+ case "UnaryExpression":
922
+ processExpr(expr.argument);
923
+ return;
924
+ case "MemberExpression":
925
+ processExpr(expr.object);
926
+ return;
927
+ case "IndexExpression":
928
+ processExpr(expr.object);
929
+ processExpr(expr.index);
930
+ return;
931
+ case "CallExpression":
932
+ processExpr(expr.callee);
933
+ expr.arguments.forEach(processExpr);
934
+ return;
935
+ case "MethodCallExpression":
936
+ processExpr(expr.object);
937
+ expr.arguments.forEach(processExpr);
938
+ return;
939
+ case "ParenthesizedExpression":
940
+ processExpr(expr.expression);
941
+ return;
942
+ case "TypeAssertionExpression":
943
+ processExpr(expr.expression);
944
+ return;
945
+ case "IfElseExpression":
946
+ for (const clause of expr.clauses) {
947
+ processExpr(clause.condition);
948
+ processExpr(clause.body);
949
+ }
950
+ processExpr(expr.alternate);
951
+ return;
952
+ default:
953
+ return;
954
+ }
955
+ }
956
+ function processStatement(stmt) {
957
+ switch (stmt.type) {
958
+ case "LocalStatement":
959
+ stmt.init.forEach(processExpr);
960
+ return;
961
+ case "LocalFunctionStatement":
962
+ processBlock(stmt.func.body);
963
+ return;
964
+ case "FunctionDeclarationStatement":
965
+ processBlock(stmt.func.body);
966
+ return;
967
+ case "AssignmentStatement":
968
+ stmt.targets.forEach(processExpr);
969
+ stmt.values.forEach(processExpr);
970
+ return;
971
+ case "CompoundAssignmentStatement":
972
+ processExpr(stmt.target);
973
+ processExpr(stmt.value);
974
+ return;
975
+ case "CallStatement":
976
+ processExpr(stmt.expression);
977
+ return;
978
+ case "DoStatement":
979
+ processBlock(stmt.body);
980
+ return;
981
+ case "WhileStatement":
982
+ processExpr(stmt.condition);
983
+ processBlock(stmt.body);
984
+ return;
985
+ case "RepeatStatement":
986
+ processBlock(stmt.body);
987
+ processExpr(stmt.condition);
988
+ return;
989
+ case "IfStatement":
990
+ for (const clause of stmt.clauses) {
991
+ processExpr(clause.condition);
992
+ processBlock(clause.body);
993
+ }
994
+ if (stmt.alternate) processBlock(stmt.alternate);
995
+ return;
996
+ case "NumericForStatement":
997
+ processExpr(stmt.start);
998
+ processExpr(stmt.end);
999
+ if (stmt.step) processExpr(stmt.step);
1000
+ processBlock(stmt.body);
1001
+ return;
1002
+ case "GenericForStatement":
1003
+ stmt.iterators.forEach(processExpr);
1004
+ processBlock(stmt.body);
1005
+ return;
1006
+ case "ReturnStatement":
1007
+ stmt.arguments.forEach(processExpr);
1008
+ return;
1009
+ default:
1010
+ return;
1011
+ }
1012
+ }
1013
+ function processBlock(blk) {
1014
+ for (const stmt of blk.statements) processStatement(stmt);
1015
+ const result = [];
1016
+ let inserted = 0;
1017
+ for (const stmt of blk.statements) {
1018
+ if (inserted < options.maxPerBlock && Math.random() < options.probability) {
1019
+ result.push(buildJunkStatement());
1020
+ inserted++;
1021
+ }
1022
+ result.push(stmt);
1023
+ }
1024
+ blk.statements = result;
1025
+ }
1026
+ processBlock(program.body);
1027
+ }
1028
+
1029
+ // src/passes/WrapInFunction.ts
1030
+ function runWrapInFunction(program, _options) {
1031
+ const innerBody = program.body;
1032
+ const wrapper = functionExpression(functionBody([], innerBody, true));
1033
+ const iife = call(paren(wrapper), [vararg()]);
1034
+ program.body = block([returnStatement([iife])]);
1035
+ }
1036
+
1037
+ // src/pipeline.ts
1038
+ var PASS_ORDER = [
1039
+ "GlobalMapping",
1040
+ "StringsToExpressions",
1041
+ "NumbersToExpressions",
1042
+ "RenameVariables",
1043
+ "ConstantArray",
1044
+ "EncryptStrings",
1045
+ "EncryptNumbers",
1046
+ "InsertJunk",
1047
+ "Vmify",
1048
+ "WrapInFunction",
1049
+ "Minify"
1050
+ ];
1051
+ var PASS_MAP = {
1052
+ GlobalMapping: runGlobalMapping,
1053
+ StringsToExpressions: runStringsToExpressions,
1054
+ NumbersToExpressions: runNumbersToExpressions,
1055
+ RenameVariables: runRenameVariables,
1056
+ ConstantArray: runConstantArray,
1057
+ EncryptStrings: runEncryptStrings,
1058
+ EncryptNumbers: runEncryptNumbers,
1059
+ InsertJunk: runInsertJunk,
1060
+ WrapInFunction: runWrapInFunction
1061
+ };
1062
+ function runPipeline(program, config) {
1063
+ runStripTypes(program);
1064
+ for (const key of PASS_ORDER) {
1065
+ const feature = config[key];
1066
+ if (!feature.active) continue;
1067
+ const pass = PASS_MAP[key];
1068
+ if (!pass) continue;
1069
+ const { active, ...options } = feature;
1070
+ pass(program, options);
1071
+ }
1072
+ }
1073
+
1074
+ // src/passes/Minify.ts
1075
+ function minifyPrinted(code) {
1076
+ return code.replace(/\r\n|\r|\n/g, " ").replace(/[ \t]+/g, " ").trim();
1077
+ }
1078
+
1079
+ // src/index.ts
1080
+ function obfuscateByAst(program, PConfig) {
1081
+ const config = mergeConfig(ObfuscateDefault, PConfig ?? {});
1082
+ runPipeline(program, config);
1083
+ const printed = import_luau_parser3.luauparser.print(program);
1084
+ return config.Minify.active ? minifyPrinted(printed) : printed;
1085
+ }
1086
+ function obfuscate(source, PConfig) {
1087
+ const program = import_luau_parser3.luauparser.parse(source);
1088
+ return obfuscateByAst(program, PConfig);
1089
+ }
1090
+ // Annotate the CommonJS export names for ESM import in node:
1091
+ 0 && (module.exports = {
1092
+ ObfuscateDefault,
1093
+ PASS_MAP,
1094
+ PASS_ORDER,
1095
+ obfuscate,
1096
+ obfuscateByAst
1097
+ });