AfterPack यह बदलता है कि आपका code कैसा पढ़ा जाता है, और यह बनाए रखता है कि वह क्या करता है। एक obfuscated build वही values लौटाती है, उसी प्रकार के errors throw करती है, वही output लिखती है और page में वही बदलाव करती है जो आपकी दी हुई build करती थी। यह page engine 0.2.3 और उसके बाद के लिए यह contract बताता है: क्या वैसा ही रहता है, हर release को कैसे जाँचा जाता है, और वे कुछ जगहें जहाँ output जान-बूझकर अलग है।
क्या वैसा ही रहता है
हर preset पर और हर seed के साथ, obfuscated build ये बनाए रखती है:
- नतीजे और errors। Functions वही values लौटाते हैं। जो code error throw करता था, वह अब भी उसी प्रकार का error throw करता है।
- Effects और उनका क्रम। Console output, DOM बदलाव, network requests और storage writes उसी क्रम में, उन्हीं values के साथ होते हैं।
- Async क्रम।
asyncfunctions, promises और timers उसी क्रम में settle होते हैं। - Global surface। एक classic script पहले जैसे ही globals परिभाषित करती है, इसलिए page की दूसरी scripts अब भी उसके functions call कर सकती हैं और उसके variables पढ़ सकती हैं। एक ES module
windowमें कुछ नहीं जोड़ता और किसी global को overwrite नहीं करता।importयाexportवाली फ़ाइल module के रूप में पढ़ी जाती है; जिस फ़ाइल में ये नहीं हैं, उसके लिए framework plugin, CLI याsourceTypeengine को बताता है। - Language के वे नियम जिन पर आपका code निर्भर है। Strict mode के errors,
thisऔर call receivers, getters और setters, class fields,BigIntarithmetic, औरevalवwithके scopes वैसा ही व्यवहार करते हैं जैसा लिखा गया है। - वे names जिन्हें आपका code पढ़ता है। जो फ़ाइल
this.constructor.nameयाnew.target.nameपढ़ती है, जैसा कोई custom error class करती है, उसमें classes और constructors वही names रखते हैं जो आपने लिखे थे, जान-बूझकर रखे गए अंतरों में दिए अपवादों को छोड़कर।
अलग-अलग seeds वाली दो builds का आकार अलग होता है, व्यवहार कभी नहीं।
Design के तहत क्या बदलता है
- Code का text।
fn.toString()जो लौटाता है, line और column numbers, और stack trace के names बदल जाते हैं। Production stack traces पढ़ने के लिए source map को private रखें। - Size और speed। Output बड़ा होता है, और
mediumव उससे ऊपर हर operation पर ज़्यादा काम करता है। मापे गए आँकड़े Performance पर हैं। - ख़ुद को inspect करने वाला code। जब engine ऐसा code पाता है जो runtime पर अपनी ही संरचना पढ़ता है, जैसे
fn.toString(), तो वह build रोक देता है और pattern का नाम बताता है। Pattern कोreflection.allowसे acknowledge करें, याpaths.excludeसे फ़ाइल को बिना बदले ship करें।
हर release को कैसे जाँचा जाता है
Engine 0.2.3 से, हर engine release ship होने से पहले एक semantic gate पास करता है। यह gate हर program को दो बार चलाता है, एक बार जैसा लिखा गया है और एक बार obfuscated, और तुलना करता है कि हर run क्या करता है: लौटाई गई values, throw किए गए errors, console output, और asynchronous काम का क्रम। कोई भी अंतर release को fail कर देता है, जब तक वह नीचे दिए जान-बूझकर रखे गए अंतरों में से एक न हो।
यह gate चलाता है:
- छोटे, लक्षित programs, हर एक language के किसी ऐसे नियम पर केंद्रित जिसे code transformer ग़लत कर सकता है: classes और private fields, closures और scopes, generators,
asyncक्रम, template literals,BigInt,evalऔरwith, strict mode, labels औरswitch। हर preset, कई seeds, और native engine व WebAssembly engine दोनों। - असली libraries, pinned versions पर, हर एक को एक छोटी script चलाती है: dates के लिए dayjs, Moment.js और Luxon,
BigInt-heavy cryptography के लिए viem, ethers और @noble/curves, और Comlink, lodash का_.templateऔर get-intrinsic। - Chromium में pages, जो कई classic scripts को क्रम से load करते हैं, imports वाले और बिना imports वाले ES modules, inline event handlers और custom elements, और एक single-chunk Vue app। हर page को वैसा ही व्यवहार करना होता है और
windowको ठीक वैसा ही छोड़ना होता है जैसा original छोड़ता है: न कोई नया global, न कोई overwrite किया गया global।
जान-बूझकर रखे गए अंतर
Gate केवल इन्हीं अंतरों की, और table के बाद दिए class names के अंतर की, अनुमति देता है। Table की हर row ऐसे code से जुड़ी है जो built-ins को बदलता है, instances को Proxy में लपेटता है, किसी class को new के बिना call करता है या JavaScript engine के error messages पढ़ता है, इसलिए ज़्यादातर applications का इनसे कभी सामना नहीं होता। ये उन classes के अंदर लागू होते हैं जिन्हें engine फिर से लिखता है, light और उससे ऊपर; minify classes को वैसा ही छोड़ता है जैसा लिखा गया है।
| आपका code | Native व्यवहार | Obfuscated output |
|---|---|---|
ऐसे instance पर public class fields परिभाषित करता है जो ख़ुद एक Proxy है (base constructor से लौटाया गया) या जिसकी prototype chain में एक Proxy है | केवल proxy का defineProperty trap चलता है | हर field के लिए has trap भी चलता है, और जो field object में नहीं है उसके लिए set trap और वह सब जहाँ वह आगे भेजता है |
किसी class constructor को new के बिना call करता है | किसी भी parameter default या destructuring के चलने से पहले TypeError throw करता है | Parameter defaults और destructuring चलाता है, फिर वही TypeError throw करता है |
Function.prototype.call को बदलने के बाद, या अपनी call property वाले method पर, private method या super.method() call करता है | Method को सीधे call करता है | बदले गए call से होकर जाता है |
Private-field check, private field के दूसरे initialization या new के बिना call की गई class से आए TypeError का message पढ़ता है | JavaScript engine का अपना message | AfterPack का अपना message, उसी error type के साथ |
फ़ाइल load होने से पहले TypeError, Object, Object.defineProperty, Reflect.construct, Reflect.ownKeys, WeakMap या WeakSet को बदलता है | Classes अब भी original built-ins इस्तेमाल करती हैं | फिर से लिखी गई classes बदला हुआ version इस्तेमाल करती हैं |
Class और constructor names कुछ मामलों में अलग होते हैं, हर preset पर। जो फ़ाइल इन्हें केवल दूसरे तरीक़ों से पढ़ती है, जैसे err.constructor.name या const { name } = new.target, उसे बदले हुए names मिलते हैं। जो फ़ाइल this.constructor.name या new.target.name पढ़ती है, उसमें भी इन मामलों में बदला हुआ name दिखता है: new (class X {})() के रूप में construct की गई class, किसी default parameter से name पाने वाली anonymous class, ऐसा function जिसे किसी switch case में उस case से पहले construct किया जाता है जो उसे declare करता है, और ऐसा function जिसे केवल किसी दूसरे reference से construct किया जाता है, जैसे new ns.F(), कोई alias या Reflect.construct।
क्या वैसा ही रहता है जैसा आपने लिखा
ये constructs वहाँ वैसे ही छोड़े जाते हैं जैसे लिखे गए हैं, जहाँ उन्हें सटीकता से फिर से लिखना सस्ता नहीं है। इनके आसपास का code फिर भी rename और encode होता है, with और direct eval के आसपास को छोड़कर।
asyncfunctions औरawait।delete, और किसी अकेले name पर लगाया गयाtypeof।- ऐसा function जिसका
catch (e)blockvar edeclare करता है। - किसी
switchcase में सीधे declare किया गयाletयाconst। - ऐसा optional call या tag जिसका callee parentheses में है या
supermember है:(o?.m)?.(),(o?.m)`x`,super.m?.()। - ऐसी class जो अपने ही name को assign करती है, किसी
supermember को optional call, tag या parenthesized callee के रूप में call करती है, या जिसके derived constructor में top level पर ठीक एकsuper()call नहीं है। - Loops,
minifyऔरlightपर। - किसी
withstatement या directevalके आसपास के scopes। उसे घेरने वाले हर scope में, फ़ाइल के top level तक, names अपनी मूल spelling रखते हैं, औरwithbody के अंदर globals वैसे ही छोड़े जाते हैं जैसे लिखे गए हैं। Build इसेDIAG_DYNAMIC_SCOPE_NATIVEसे report करती है। अगर evaluate होने वाले code को local variables की ज़रूरत नहीं है, तोevalको indirectly call करें,(0, eval)(code), या जिस code कोevalयाwithचाहिए उसे एक अलग फ़ाइल में ले जाएँ।
Engine 0.2.3 से पहले
Engine 0.2.2 और उससे पहले के versions कुछ ऐसे मामलों में इस contract को पूरा नहीं करते थे जिन्हें gate अब cover करता है: दूसरी scripts के साथ साझा की गई classic scripts के top-level names, बिना imports वाले ES modules, template literals के अंदर dates जैसे objects, constructors के रूप में इस्तेमाल किए गए functions, inherited accessors के ऊपर class fields, ख़ुद को refer करने वाले functions, medium और उससे ऊपर BigInt arithmetic, eval और with, ऐसा catch block जो अपने variable को var से फिर से declare करता है, async क्रम, parentheses में optional calls, constant ** expressions, और runtime पर पढ़े गए class names। Engine 0.2.3 या उसके बाद के version से फिर से build करें; चेंजलॉग हर fix की सूची देता है।
किसी अंतर की report करें
इस page पर न दिया गया कोई भी अंतर एक bug है। github.com/afterpack-dev/afterpack/issues पर एक छोटे reproduction के साथ issue खोलें। जब तक वह fix नहीं होता, paths.exclude प्रभावित फ़ाइल को बिना बदले ship करता है।
आगे
- Diagnostics: हर code जो build report कर सकती है, और उसके बारे में क्या करना है।
- AfterPack कैसे काम करता है: transformations आपके code के साथ क्या करते हैं।
- Presets: हर स्तर क्या लागू करता है।
- कॉन्फ़िगरेशन:
reflection.allow,paths.excludeऔर वे दूसरी keys जिनसे यह page link करता है।