fingerprint-chromium-engine 0.1.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/README.md ADDED
@@ -0,0 +1,227 @@
1
+ # Chromium Anti-Detect Engine
2
+
3
+ Thư viện điều khiển trình duyệt Chromium chống bot detection, tích hợp fingerprint, proxy và quản lý profile đa phiên.
4
+
5
+ Được xây dựng trên nền `playwright-core` — tương thích hoàn toàn với Playwright API hiện có, không cần viết lại code nghiệp vụ.
6
+
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)](https://www.typescriptlang.org/)
8
+ [![Playwright Compatible](https://img.shields.io/badge/Playwright-Compatible-2ea44f)](https://playwright.dev/)
9
+
10
+ ---
11
+
12
+ ## Tính năng
13
+
14
+ ### Fingerprint từ thiết bị thật, inject ở tầng C native
15
+
16
+ Không sinh fingerprint ngẫu nhiên — engine sử dụng fingerprint thu thập từ các thiết bị thực tế, sau đó inject trực tiếp vào Chromium ở cấp độ C/C++. Kết quả là mọi thuộc tính đều trả về giá trị native, không có dấu hiệu bị override dưới bất kỳ hình thức kiểm tra nào.
17
+
18
+ **Navigator & Platform**
19
+ - Navigator properties (thiết bị, trình duyệt, locale, OS...)
20
+ - Network headers `Accept-Language` và `User-Agent` tự động khớp với navigator
21
+ - Kích thước & độ phân giải màn hình, inner/outer viewport
22
+ - `devicePixelRatio` & HiDPI/Retina screen emulation
23
+
24
+ **Đồ họa**
25
+ - WebGL parameters, supported extensions, context attributes & shader precision formats
26
+ - Canvas 2D — thêm nhiễu chống canvas fingerprinting
27
+ - Font fingerprinting (hỗ trợ FontPack đồng bộ font hệ thống)
28
+
29
+ **Media & Hardware**
30
+ - AudioContext sample rate, output latency & max channel count
31
+ - Device voices & speech playback rates
32
+ - Số lượng microphone, webcam, speaker available
33
+ - Battery API, Sensor API (gia tốc kế, con quay hồi chuyển)
34
+ - ClientRects & DOM element coordinates
35
+
36
+ **Mạng & Vị trí**
37
+ - WebRTC IP spoofing ở tầng protocol — không thể bị detect qua JS
38
+ - Geolocation, timezone & locale
39
+
40
+ ### Proxy & Môi trường thông minh
41
+
42
+ Không chỉ định tuyến traffic — engine tự động đồng bộ toàn bộ môi trường trình duyệt theo IP proxy:
43
+
44
+ - **Timezone** — múi giờ tự động khớp với vị trí địa lý của proxy
45
+ - **Ngôn ngữ** — `Accept-Language`, `navigator.language` theo quốc gia proxy
46
+ - **Geolocation** — vị trí địa lý theo IP (tuỳ chọn)
47
+ - **WebRTC** — che giấu hoặc thay thế IP rò rỉ qua WebRTC
48
+ - **DNS tùy chỉnh** — hỗ trợ `custom-proxy` và `custom-direct` để tránh DNS leak
49
+ - **QUIC** — tùy chọn bật giao thức QUIC nếu proxy hỗ trợ UDP
50
+
51
+ ### Profile đa phiên
52
+
53
+ Duy trì trạng thái đăng nhập, cookies, localStorage và lịch sử giữa các lần chạy:
54
+
55
+ - Mỗi profile độc lập, được lưu theo đường dẫn tùy chọn
56
+ - Tự động khôi phục fingerprint và proxy đã dùng ở phiên trước
57
+ - Lưu profile khi đóng — có thể chỉ định đường dẫn lưu khác nhau mỗi lần
58
+
59
+ ### Tương thích Playwright 100%
60
+
61
+ Trả về `BrowserContext` chuẩn của `playwright-core`. Toàn bộ API Playwright (`page`, `locator`, `expect`, `route`...) hoạt động bình thường — không cần thay đổi code nghiệp vụ.
62
+
63
+ ---
64
+
65
+ ## Cài đặt
66
+
67
+ Cài đặt trực tiếp từ GitHub:
68
+
69
+ ```bash
70
+ # npm
71
+ npm install github:maxlogvn/PrivateBrowser playwright-core
72
+
73
+ # yarn
74
+ yarn add github:maxlogvn/PrivateBrowser playwright-core
75
+
76
+ # bun
77
+ bun add github:maxlogvn/PrivateBrowser playwright-core
78
+ ```
79
+
80
+ > `playwright-core` là peer dependency — cần cài kèm để thư viện hoạt động.
81
+
82
+ ---
83
+
84
+ ## Bắt đầu nhanh
85
+
86
+ ```ts
87
+ import { Chromium } from 'playwright-browser-manager';
88
+
89
+ const context = await Chromium
90
+ .useFingerprint(fingerprintData)
91
+ .useProxy('http://user:pass@127.0.0.1:8080')
92
+ .useProfile('./profiles/user_01')
93
+ .launch({ headless: false })
94
+ .newContext();
95
+
96
+ const page = await context.newPage();
97
+ await page.goto('https://example.com');
98
+
99
+ await Chromium.quit();
100
+ ```
101
+
102
+ > Tất cả method cấu hình (`use*`) trả về `this` — hỗ trợ method chaining.
103
+ > Bắt buộc gọi trước `launch()`. Sau khi `launch()`, cấu hình bị khóa.
104
+
105
+ ---
106
+
107
+ ## Hướng dẫn sử dụng
108
+
109
+ ### Fingerprint
110
+
111
+ ```ts
112
+ Chromium.useFingerprint(fingerprintData, {
113
+ usePerfectCanvas: true, // Canvas chính xác theo fingerprint
114
+ safeWebGL: true, // Che giấu GPU renderer & vendor
115
+ safeAudio: true, // Che giấu thông tin audio hardware
116
+ useFontPack: true, // Đồng bộ font với fingerprint mục tiêu
117
+ })
118
+ ```
119
+
120
+ > `useFontPack` yêu cầu cài đặt [FontPack từ Bablosoft](https://wiki.bablosoft.com/doku.php?id=fontpack). Nếu chưa cài, engine tự fallback.
121
+
122
+ | Tùy chọn | Mô tả | Mặc định |
123
+ |---|---|---|
124
+ | `emulateDeviceScaleFactor` | Giả lập màn hình HiDPI/Retina | `true` |
125
+ | `emulateSensorAPI` | Giả lập cảm biến di động | `true` |
126
+ | `usePerfectCanvas` | Canvas chính xác từ fingerprint | `true` |
127
+ | `useFontPack` | Đồng bộ font hệ thống | `true` |
128
+ | `safeElementSize` | Che giấu tọa độ DOM element | `false` |
129
+ | `safeBattery` | Giả lập Battery API | `true` |
130
+ | `safeCanvas` | Thêm nhiễu Canvas 2D | `true` |
131
+ | `safeAudio` | Thêm nhiễu Web Audio | `true` |
132
+ | `safeWebGL` | Thêm nhiễu WebGL | `true` |
133
+
134
+ ---
135
+
136
+ ### Proxy
137
+
138
+ ```ts
139
+ Chromium.useProxy('http://user:pass@127.0.0.1:8080', {
140
+ changeTimezone: true, // Đồng bộ múi giờ theo IP proxy
141
+ changeGeolocation: true, // Đồng bộ vị trí địa lý
142
+ changeBrowserLanguage: true, // Đồng bộ ngôn ngữ trình duyệt
143
+ changeWebRTC: 'replace', // Thay IP WebRTC bằng IP proxy
144
+ })
145
+ ```
146
+
147
+ **Tùy chọn DNS:**
148
+
149
+ ```ts
150
+ Chromium.useProxy('http://...', {
151
+ dnsMode: 'custom-direct', // phân giải DNS cục bộ, traffic còn lại qua proxy
152
+ dnsIP: '1.1.1.1',
153
+ })
154
+ ```
155
+
156
+ > `custom-proxy` yêu cầu proxy hỗ trợ UDP. Nếu proxy chỉ hỗ trợ TCP, dùng `custom-direct` hoặc `system-proxy`.
157
+
158
+ **Tùy chọn WebRTC:**
159
+
160
+ | Giá trị | Hành vi |
161
+ |---|---|
162
+ | `enable` | Bật WebRTC — lộ IP thật |
163
+ | `disable` | Tắt hoàn toàn WebRTC |
164
+ | `replace` | Thay IP WebRTC bằng IP proxy *(khuyến nghị)* |
165
+
166
+ ---
167
+
168
+ ### Profile
169
+
170
+ ```ts
171
+ // Lần đầu — tạo mới profile
172
+ Chromium.useProfile('./profiles/user_01')
173
+
174
+ // Các lần sau — tự động khôi phục session, proxy, fingerprint
175
+ Chromium.useProfile('./profiles/user_01', {
176
+ loadProxy: true, // khôi phục proxy từ phiên trước
177
+ loadFingerprint: true, // khôi phục fingerprint từ phiên trước
178
+ })
179
+ ```
180
+
181
+ Profile tự động lưu khi gọi `quit()`. Có thể ghi đè đường dẫn lưu:
182
+
183
+ ```ts
184
+ await Chromium.quit('./profiles/user_01_backup');
185
+ ```
186
+
187
+ ---
188
+
189
+ ### Vòng đời trình duyệt
190
+
191
+ ```ts
192
+ // 1. Cấu hình (có thể chaining)
193
+ Chromium
194
+ .useFingerprint(data)
195
+ .useProxy('http://...')
196
+ .useProfile('./profiles/user_01')
197
+
198
+ // 2. Khởi tạo engine — chỉ gọi một lần
199
+ .launch({ headless: false })
200
+
201
+ // 3. Mở phiên duyệt
202
+ const context = await Chromium.newContext();
203
+ const page = await context.newPage();
204
+
205
+ // 4. Đóng và lưu
206
+ await Chromium.quit();
207
+ ```
208
+
209
+ > `launch()` chỉ được gọi **một lần**. Gọi lại sẽ ném lỗi.
210
+ > `newContext()` chỉ cho phép một context tại một thời điểm. Gọi `quit()` trước khi tạo context mới.
211
+
212
+ ---
213
+
214
+ ## Lưu ý
215
+
216
+ **IP Geolocation với ip-api.com** — bản free giới hạn 45 request/phút/IP. Vượt quá giới hạn nhận HTTP 429. Cân nhắc dùng bản Pro hoặc chuyển về `ipInfoMethod: 'database'` khi scale lớn.
217
+
218
+ **FontPack** — cần tải và cài đặt riêng từ [Bablosoft Wiki](https://wiki.bablosoft.com/doku.php?id=fontpack) để `useFontPack` hoạt động đúng.
219
+
220
+ **Thứ tự gọi** — `use*` → `launch()` → `newContext()` → `quit()`. Sai thứ tự sẽ ném lỗi có mô tả rõ ràng.
221
+
222
+ ---
223
+
224
+ ## Đóng góp & Hỗ trợ
225
+
226
+ Gặp vấn đề hoặc muốn đề xuất tính năng — tạo [Issue](../../issues) hoặc [Pull Request](../../pulls).
227
+
package/dist/index.cjs ADDED
@@ -0,0 +1,23 @@
1
+ 'use strict';var g=require('path'),Et=require('crypto'),module$1=require('module'),$e=require('fast-glob'),u=require('fs/promises'),oe=require('proper-lockfile'),Ae=require('debug'),readline=require('readline'),child_process=require('child_process'),url=require('url'),process$1=require('process'),Ge=require('chokidar'),de=require('axios'),He=require('extract-zip'),ze=require('events'),promises=require('stream/promises'),K=require('fs'),I=require('dedent'),nt=require('net'),it=require('once'),ct=require('async-lock'),mt=require('chrome-remote-interface'),promises$1=require('timers/promises'),compareVersions=require('compare-versions');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var g__default=/*#__PURE__*/_interopDefault(g);var Et__default=/*#__PURE__*/_interopDefault(Et);var $e__default=/*#__PURE__*/_interopDefault($e);var u__namespace=/*#__PURE__*/_interopNamespace(u);var oe__default=/*#__PURE__*/_interopDefault(oe);var Ae__default=/*#__PURE__*/_interopDefault(Ae);var Ge__default=/*#__PURE__*/_interopDefault(Ge);var de__default=/*#__PURE__*/_interopDefault(de);var He__default=/*#__PURE__*/_interopDefault(He);var ze__default=/*#__PURE__*/_interopDefault(ze);var K__default=/*#__PURE__*/_interopDefault(K);var I__default=/*#__PURE__*/_interopDefault(I);var nt__default=/*#__PURE__*/_interopDefault(nt);var it__default=/*#__PURE__*/_interopDefault(it);var ct__default=/*#__PURE__*/_interopDefault(ct);var mt__default=/*#__PURE__*/_interopDefault(mt);var Be=()=>typeof document>"u"?new URL(`file:${__filename}`).href:document.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"?document.currentScript.src:new URL("main.js",document.baseURI).href,c=Be();var Se=module$1.createRequire(c),De=(()=>{try{return Se(`./${process.platform}-${process.arch}/mutex`)}catch(r){let e=r,t=e.message?` Chi ti\u1EBFt: ${e.message}`:"";throw process.platform==="win32"?(console.error(`[Mutex] Ki\u1EBFn tr\xFAc kh\xF4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3: ${process.arch}${t}`),new Error(`Unsupported OS architecture for named mutex.${t}`)):(console.error(`[Mutex] N\u1EC1n t\u1EA3ng kh\xF4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3: ${process.platform}${t}`),new Error(`Unsupported OS platform for named mutex.${t}`))}})();var ie=De.create;var _e=Ae__default.default("browser-with-fingerprints:cleaner"),se=15e3,Ie=(r,e)=>[`t/${r}`,`s/${e}.ini`,`s/${e}1.ini`],z=class{#e=null;#t=[];async ignore(e,t,n){await this.#r(true,e,t,n);}async include(e,t,n){await this.#r(false,e,t,n);}watch(e){return this.#t.includes(e)||this.#t.push(e),this.#e||(this.#n(),this.#e=setInterval(()=>{this.#n();},se).unref()),this}async#r(e,t,n,i){for(let o of Ie(n,i)){let s=g.posix.join(t,o);try{await oe__default.default[e?"lock":"unlock"](s,{onCompromised:()=>{_e(`File lock t\u1EA1i \u0111\u01B0\u1EDDng d\u1EABn ${s} kh\xF4ng \u0111\u01B0\u1EE3c c\u1EADp nh\u1EADt.`);}});}catch(a){if(a.code!=="ENOENT")throw a}}}async#n(){for(let e of this.#t){let t=g.posix.join(e,`{${["t","s"].join(",")}}`,"*"),n=await $e__default.default(t,{stats:true,onlyFiles:false});for(let{path:i,stats:o}of n){if(!o||Date.now()-o.mtimeMs<=se)continue;let s=g.posix.parse(i),a=s.ext===".txt"&&g.posix.basename(s.dir)==="s"?g.posix.format({...s,base:void 0,ext:".ini"}):i;await oe__default.default.check(a).catch(()=>false)||await u.rm(i,{recursive:true,force:true});}}}},J=new z;var X=async({args:r=[],timeout:e=3e4,userDataDir:t="",debuggingPort:n=0,executablePath:i=""}={})=>{let o=t?[...r,`--user-data-dir=${g__default.default.resolve(t)}`]:[...r],s=child_process.spawn(i,[...o,`--remote-debugging-port=${n}`],{detached:false,shell:false}),a=await new Promise((m,l)=>{let d;e&&(d=setTimeout(N,e)),readline.createInterface({input:s.stderr}).on("line",x),readline.createInterface({input:s.stdout}).on("line",x);function x(H){let ne=H.match(/DevTools listening on (.*)/);ne&&(d&&clearTimeout(d),m(ne[1]));}function N(){l(new Error(`Timed out after ${e}ms while trying to launch the browser.`));}}),h=Number(new URL(a).port);return {url:a,port:h,close:async()=>{if(s.pid&&!s.killed)return new Promise(m=>{child_process.exec(`taskkill /pid ${s.pid} /T /F`,l=>{l&&s.kill(),s.killed=true,m();});})},process:s,configure:async()=>{}}};var y=class extends Error{constructor(e){super(e),this.name=this.constructor.name,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor);}get[Symbol.toStringTag](){return this.constructor.name}},$=class extends y{constructor(e){super(I__default.default`
2
+ ${e}
3
+ Do các cập nhật mới nhất, bạn cần chỉ định key không chỉ khi nhận fingerprint,
4
+ mà cả khi áp dụng nó vào browser.
5
+ `);}},L=class extends y{constructor(e){super(I__default.default`
6
+ ${e}
7
+ Nguyên nhân có thể do engine chưa được tải xuống hoặc giải nén đúng cách.
8
+ Hướng khắc phục:
9
+ 1. Xóa hoàn toàn thư mục engine hiện tại
10
+ 2. Chạy lại code để hệ thống tự tải engine mới
11
+ 3. Nếu vẫn lỗi, hãy mở issue kèm mô tả chi tiết vấn đề
12
+ `);}},A=class extends y{constructor(e){super(I__default.default`
13
+ ${e}
14
+ Bạn có thể điều chỉnh timeout bằng method "setEngineTimeout" -
15
+ phương thức này thiết lập giới hạn thời gian cho việc tải file engine.
16
+ `);}},_=class extends y{constructor(e){super(I__default.default`
17
+ ${e}
18
+ Bạn có thể điều chỉnh timeout bằng method "setRequestTimeout" -
19
+ phương thức này thiết lập giới hạn thời gian cho việc thực thi request của engine.
20
+ `);}};var P=null;function Ve(r={}){if(P!==null)return P;let{envFileName:e=".env",throwIfNotFound:t=false}=r,n=process.cwd(),i=g__default.default.parse(n).root;for(;n!==i;){if(K__default.default.existsSync(g__default.default.join(n,e)))return P=n,n;n=g__default.default.dirname(n);}if(K__default.default.existsSync(g__default.default.join(i,e)))return P=i,i;if(t)throw new Error(`Kh\xF4ng t\xECm th\u1EA5y t\u1EC7p "${e}" t\u1EEB "${process.cwd()}" l\xEAn th\u01B0 m\u1EE5c g\u1ED1c.`);return P=process.cwd(),P}var j=(...r)=>g__default.default.resolve(Ve(),...r);var ue=process.env.BABLOSOFT_KEY??"",v=process.arch.includes("32")?"32":"64",he=g__default.default.join(process.cwd(),"data"),Y=j("project.xml"),C=j(".tmp/browser/running"),pe=j(process.env.BABLOSOFT_ENGINE_PATH??".tmp/browser/egineWorking"),me=6e4,B=3e5,T=3,Z=["--disable-extensions"],le=["proxy","channel","firefoxUserPrefs"],ge={headless:false,hasTouch:true},fe=['[Fingerprint] Ph\u01B0\u01A1ng th\u1EE9c "launch" t\u1EA1m th\u1EDDi kh\xF4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3 tr\u1EF1c ti\u1EBFp.','N\u1ED9i b\u1ED9 s\u1EBD s\u1EED d\u1EE5ng "launchPersistentContext" thay th\u1EBF.','Khuy\u1EBFn ngh\u1ECB d\xF9ng "launchPersistentContext" tr\u1EF1c ti\u1EBFp \u0111\u1EC3 tr\xE1nh t\xE1c d\u1EE5ng ph\u1EE5.'].join(`
21
+ `);g__default.default.dirname(url.fileURLToPath(c));var f=Ae__default.default("browser-with-fingerprints:connector:engine");async function U(r){try{return await u__namespace.access(r),!0}catch{return false}}async function tt(r){let e=K.createReadStream(r),t=Et.createHash("sha1");return await promises.pipeline(e,t),t.digest("hex")}async function rt(r,e){let t=await de__default.default.get(r,{responseType:"stream"}),n=K.createWriteStream(e);await promises.pipeline(t.data,n);}var F=class extends ze__default.default{#e=null;#t=null;#r=[];#n=B;#i=B;constructor(e={}){super(),this.setCwd(e.cwd),this.setArgs(e.args),this.setEngineTimeout(e.engineTimeout),this.setRequestTimeout(e.requestTimeout);}setCwd(e){this.#t=g__default.default.resolve(e||he);}setArgs(e){this.#r=Array.isArray(e)?e:[];}setEngineTimeout(e){let t=Number(e)||0;this.#n=t>=0?t:B;}get requestTimeout(){return this.#i}setRequestTimeout(e){let t=Number(e)||0;this.#i=t>=0?t:B;}async runFunction(e,t,{engineTimeout:n=this.#n,requestTimeout:i=this.#i}={}){this.#e||await this.#a();let o=await this.#s(n);f(`\u0110ang g\u1ECDi method "${e}" (timeout: ${i}ms)`);let s=g__default.default.join(g__default.default.dirname(o.spawnfile),"r");await u__namespace.mkdir(s,{recursive:true});for(let m of await u__namespace.readdir(s))try{let l=Number(m.split("_")[0]);if(l===o.pid)continue;process$1.kill(l,0);}catch(l){l.code==="ESRCH"&&(f(`X\xF3a file request th\u1EEBa - ${m}`),await u__namespace.unlink(g__default.default.join(s,m)));}let a=g__default.default.join(s,`${o.pid}_${Et.randomUUID()}.json`);f(`T\u1EA1o file request m\u1EDBi cho h\xE0m "${e}" - ${a}`),await u__namespace.writeFile(a,JSON.stringify({name:e,params:t}));let h=Ge__default.default.watch(a,{awaitWriteFinish:true}),p;try{p=await new Promise((m,l)=>{let d=null,x=null;i&&(x=setTimeout(()=>{l(new _(`H\u1EBFt th\u1EDDi gian ch\u1EDD khi g\u1ECDi method "${e}".`));},i).unref());let N=()=>{d=setTimeout(()=>{f("Ti\u1EBFn tr\xECnh engine \u0111\xE3 \u0111\xF3ng trong l\xFAc ch\u1EDD ph\u1EA3n h\u1ED3i"),m("");},me);};h.on("change",async()=>{let H=await u__namespace.readFile(a,"utf8");f("\u0110\xE3 nh\u1EADn k\u1EBFt qu\u1EA3 t\u1EEB engine th\xE0nh c\xF4ng"),x&&clearTimeout(x),d&&clearTimeout(d),o.off("close",N),await u__namespace.unlink(a),m(H);}),o.once("close",N);});}finally{await h.close();}if(!p)return {error:"Engine process closed unexpectedly"};try{return JSON.parse(p)}catch{return {error:"Invalid response format from engine"}}}async#o(){let e=g__default.default.join(this.#t,"script",this.#e.version),t=g__default.default.join(this.#t,"engine",this.#e.version),n=g__default.default.join(t,`FastExecuteScript.x${v}.zip`);return this.#e&&await U(n)&&this.#e.checksum!==await tt(n)&&(await u__namespace.rm(t,{recursive:true,force:true}),f("\u0110\xE3 x\xF3a engine b\u1ECB l\u1ED7i (sai checksum)")),await U(t)||(this.emit("beforeDownload"),await u__namespace.mkdir(t,{recursive:true}),await rt(this.#e.url,n),f("Engine t\u1EA3i xu\u1ED1ng th\xE0nh c\xF4ng")),await U(e)||(this.emit("beforeExtract"),await u__namespace.mkdir(e,{recursive:true}),await He__default.default(n,{dir:e}),f("Engine gi\u1EA3i n\xE9n th\xE0nh c\xF4ng")),await u__namespace.copyFile(Y,g__default.default.join(e,"project.xml")),await u__namespace.writeFile(g__default.default.join(e,"worker_command_line.txt"),"--mock-connector"),await u__namespace.writeFile(g__default.default.join(e,"settings.ini"),"RunProfileRemoverImmediately=true"),f(`\u0110ang kh\u1EDFi ch\u1EA1y ti\u1EBFn tr\xECnh engine (cwd: ${e})`),new Promise((i,o)=>{let s=child_process.execFile(g__default.default.join(e,"FastExecuteScript.exe"),["--silent",...this.#r],{cwd:e},a=>{a&&o(new L(`Kh\xF4ng th\u1EC3 kh\u1EDFi ch\u1EA1y ti\u1EBFn tr\xECnh engine (m\xE3 l\u1ED7i: ${a.code})`));});i(s);})}async#s(e){if(!e)return await this.#o();let t=null,n=await Promise.race([this.#o(),new Promise((i,o)=>{t=setTimeout(()=>o(new A("H\u1EBFt th\u1EDDi gian ch\u1EDD khi kh\u1EDFi t\u1EA1o engine plugin.")),e).unref();})]);return t&&clearTimeout(t),n}async#a(){let t=(await u__namespace.readFile(Y,"utf8")).match(/<EngineVersion>(\d+\.\d+\.\d+)<\/EngineVersion>/);if(!t)throw new Error("Kh\xF4ng th\u1EC3 \u0111\u1ECDc phi\xEAn b\u1EA3n Engine t\u1EEB project.xml");let n=t[1];f(`C\u1EADp nh\u1EADt metadata cho engine (arch: ${v}, version: ${n})`);let i=`http://bablosoft.com/distr/FastExecuteScript${v}/${n}/FastExecuteScript.x${v}.zip.meta.json`,o=g__default.default.join(this.#t,`${n}_${v}.json`);if(await U(o))f(`S\u1EED d\u1EE5ng metadata \u0111\xE3 l\u01B0u t\u1EA1i ${o}`),this.#e=JSON.parse(await u__namespace.readFile(o,"utf8"));else {f(`Y\xEAu c\u1EA7u metadata m\u1EDBi t\u1EEB ${i}`);let{data:s}=await de__default.default.get(i);this.#e={checksum:s.Checksum,url:s.Url,version:n},await u__namespace.mkdir(g__default.default.dirname(o),{recursive:true}),await u__namespace.writeFile(o,JSON.stringify(this.#e));}}};var st=Ae__default.default("browser-with-fingerprints:connector:pcapServer"),ye=it__default.default((r=0,e="127.0.0.1")=>{let t=0;return new Promise(n=>{let i=nt__default.default.createServer(o=>{o.on("data",s=>{if(s.length===0)return;let a=s[0];a===1&&(o.write(new Uint8Array([1,4,0,0,0,10,t&255,t>>8&255,t>>16&255])),t++),a===7&&o.write(new Uint8Array([7,0,0,0,0]));}),o.on("error",s=>st(s));});i.on("error",o=>{o.code==="EADDRINUSE"&&setTimeout(()=>i.listen(r,e),1e3).unref();}),i.listen(r,e,()=>{let o=i.address();o&&typeof o=="object"&&n(o.port);});})});var ht=Ae__default.default("browser-with-fingerprints:connector"),pt=new ct__default.default,w=new F({cwd:process.env.FINGERPRINT_CWD,engineTimeout:process.env.FINGERPRINT_TIMEOUT,requestTimeout:process.env.FINGERPRINT_TIMEOUT});w.on("beforeExtract",()=>{console.log("\u0110ang c\xE0i \u0111\u1EB7t browser \u2014 qu\xE1 tr\xECnh n\xE0y c\xF3 th\u1EC3 m\u1EA5t m\u1ED9t ch\xFAt th\u1EDDi gian.");});w.on("beforeDownload",()=>{console.log("\u0110ang t\u1EA3i browser \u2014 qu\xE1 tr\xECnh n\xE0y c\xF3 th\u1EC3 m\u1EA5t m\u1ED9t ch\xFAt th\u1EDDi gian.");});ye().then(r=>{ht(`PCAP server \u0111ang l\u1EAFng nghe t\u1EA1i port ${r}`),w.setArgs([`--mock-pcap-port=${r}`]);});var q=async(r,e={})=>{let t;return pt.acquire("client",async()=>{try{let{error:n,...i}=await w.runFunction(r,e,{requestTimeout:e?.options?.perfectCanvasRequest?0:w.requestTimeout});if(n)throw n.includes("key is missing")?new $(n):new y(n);return i.response??i}finally{clearTimeout(t);}})};var k={waitForResize:()=>new Promise(r=>{new ResizeObserver((e,t)=>{requestAnimationFrame(()=>requestAnimationFrame(()=>r(t.disconnect())));}).observe(document.body);}),getViewport:()=>({width:window.innerWidth,height:window.innerHeight})};var xe=async(r,{diff:e,width:t,height:n})=>{let i=await mt__default.default(r),{windowId:o}=await i.Browser.getWindowForTarget(),s=e?{...e}:{width:16,height:88};for(let a=0;a<T;++a){let h={width:t+s.width,height:n+s.height};await Promise.all([i.Browser.setWindowBounds({bounds:h,windowId:o}),gt(i)]);let p=await lt(i);if(t===p.width&&n===p.height)break;a===T-1&&console.warn("Kh\xF4ng th\u1EC3 \u0111\u1EB7t k\xEDch th\u01B0\u1EDBc viewport ch\xEDnh x\xE1c."),s.height+=n-p.height,s.width+=t-p.width;}await i.close();},lt=async r=>{let{result:e}=await r.Runtime.evaluate({expression:`(${k.getViewport})()`,returnByValue:true});return e.value},gt=async r=>{await r.Runtime.evaluate({expression:`(${k.waitForResize})()`,returnByValue:true,awaitPromise:true});};var xt=new ct__default.default,V=async(r,e,t={},n=async i=>i())=>{e.process.once("exit",()=>r(e)),e.configure=async()=>{t.width&&t.height&&await n(()=>xe(e,t));},await e.configure();},Pe=async(r,e,t={},n=async()=>{})=>{let i=`${e}/s/${r}1.ini`;await xt.acquire(r,async()=>{let o=await u.readFile(i,"utf8");for(let s of [true,false]){s||await Promise.resolve(n());for(let a of ["availWidth","availHeight"])o=o.replace(new RegExp(`${a}=(.+)`),()=>{let h=s?"BAS_NOT_SET":t[a]??"BAS_NOT_SET";return `${a}=${h}`});await u.writeFile(i,o),await promises$1.setTimeout(2e3);}});};var vt=["--lang=en","--no-proxy-server","--disable-auto-reload","--bas-disable-tab-hook","--disk-cache-size=5000000","--disable-features=NetworkServiceInProcess2,OptimizationGuideModelDownloading,AutoDeElevate"],Tt=["--kiosk","--headless","--user-data-dir","--start-maximized","--start-fullscreen"],ve=({args:r=[],profile:e="",devtools:t=false,headless:n=!t,extensions:i=[]}={})=>{let o=[`--user-data-dir=${e}`],s=r.reduce((a,h)=>{let[p,m]=h.split("=");return Tt.some(l=>h.includes(l))||(p.includes("disable-extensions-except")||p.includes("load-extension")?a.push(`${p}=${i.concat(m||"").filter(Boolean).join(",")}`):a.push(h)),a},i.length?[`--load-extension=${i.join(",")}`]:[]);return n?o.push("--hide-scrollbars","--mute-audio"):o.push("--bas-force-visible-window"),s.concat(o,vt)},Te=({args:r=[],userDataDir:e=""}={})=>{if(e)return g__default.default.resolve(e);let t=r.find(n=>n.startsWith("--user-data-dir"));return t?t.split("=")[1]:""},M=(r,e,t)=>{if(typeof e!="string"||typeof t!="object"||t===null)throw new Error(`Tham s\u1ED1 kh\xF4ng h\u1EE3p l\u1EC7 cho c\u1EA5u h\xECnh "${r}".`)},ke=r=>{if(r==null||typeof r!="object"||typeof r.launch!="function")throw new Error('Browser launcher kh\xF4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3 - y\xEAu c\u1EA7u m\u1ED9t object c\xF3 method "launch".')};var Q,E=class r{launcher;version="default";fingerprint;profile;proxy;constructor(e){this.launcher=e??{launch:X};}static create(e){return ke(e),new r(e)}useFingerprint(e="",t={}){return M("fingerprint",e,t),this.fingerprint={value:e,options:t},this}useProfile(e="",t={}){return M("profile",e,t),this.profile={value:e,options:t},this}useProxy(e="",t={}){return M("proxy",e,t),this.proxy={value:e,options:t},this}useBrowserVersion(e){return this.version=e||"default",this}setProxyFromArguments(e=[]){if(this.proxy==null){for(let t of e)if(t.includes("--proxy-server"))return this.useProxy(t.slice(15))}return this}setWorkingFolder(e){w.setCwd(g__default.default.resolve(e));}setRequestTimeout(e){w.setRequestTimeout(e||0);}setEngineTimeout(e){w.setEngineTimeout(e||0);}setServiceKey(e){Q=e;}async fetch(e={}){return await q("fetch",{key:Q,options:e,version:this.version})}async versions(e="default"){return await q("versions",{format:e})}async spawn(e={}){return this._launch(true,e)}async configure(...e){if(typeof V=="function")return V(...e)}async _launch(e,t={}){this.setProxyFromArguments(t.args||[]);let n=await q("setup",{proxy:this.proxy,fingerprint:this.fingerprint,version:this.version,profile:this.profile??{value:Te(t),options:{loadProxy:true,loadFingerprint:true}},pid:Et__default.default.randomUUID(),key:typeof t.key=="string"?t.key:Q}),{id:i,pid:o,pwd:s,path:a,bounds:h,...p}=n;await J.watch(s).ignore(s,o,i),ie(`BASProcess${o}`);let l=await(e?{launch:X}:t.launcher??this.launcher).launch({...t,headless:false,userDataDir:void 0,defaultViewport:void 0,executablePath:`${a}/worker.exe`,args:[`--parent-process-id=${o}`,`--unique-process-id=${i}`,...ve({...t,...p})]});return await(e?V:this.configure.bind(this))(()=>J.include(s,o,i),l,h,Pe.bind(null,i,s,h)),l}};var Ee=module$1.createRequire(c),S=class r{target;version;packages;constructor(e,t,n=[]){this.target=e,this.version=t,this.packages=n;}static import(e=[]){if(e.length){for(let t of e)try{let n=Ee(t),i=Ee(`${t}/package.json`).version;return [n,i]}catch{continue}throw new Error(`None of the following packages could be found - "${e.join('", "')}".`)}}load(e="chromium"){let t=r.import([this.target,...this.packages]);if(!t)throw new Error(`Failed to resolve package "${this.target}".`);let[n,i]=t;if(i&&this.version&&compareVersions.compare(i,this.version,"<"))throw new Error(`Version ${i} of the "${this.target}" package is not supported - use version ${this.version} or higher.`);return e in n?n[e]:n}};var Ot=new S("playwright","1.27.1",["playwright-core"]),be=Ot;var ee=r=>typeof r=="object"&&r!==null&&"version"in r&&typeof r.version=="function",Re=(r,e)=>{ee(r)?r.once("disconnected",e):r.once("close",()=>e());},Oe=(r,e={})=>{ee(r)&&(r.newContext=new Proxy(r.newContext,{apply:(i,o,[s])=>i.call(o,Bt(s)).then(t)}));function t(i){return i.newPage=new Proxy(i.newPage,{async apply(o,s){let a=await o.call(s);return await e.onPageCreated?.(a),n(a)}}),i}function n(i){return i.setViewportSize=new Proxy(i.setViewportSize,{apply:async()=>{console.warn("[Fingerprint] Kh\xF4ng th\u1EC3 thay \u0111\u1ED5i viewport: k\xEDch th\u01B0\u1EDBc \u0111\xE3 b\u1ECB kho\xE1 b\u1EDFi fingerprint.");}}),i}!ee(r)&&!r.newContext&&t(r);},Ce=async(r,{diff:e,width:t=0,height:n=0})=>{let i=e?{...e}:{width:16,height:88},o=await r.context().newCDPSession(r),{windowId:s}=await o.send("Browser.getWindowForTarget");for(let a=0;a<T;++a){let h={width:t+i.width,height:n+i.height};await Promise.all([o.send("Browser.setWindowBounds",{bounds:h,windowId:s}),Ct(r)]);let p=await te(r);if(t===p.width&&n===p.height)break;if(a===T-1){console.warn("[Fingerprint] Kh\xF4ng th\u1EC3 \u0111\u1EB7t k\xEDch th\u01B0\u1EDBc viewport ch\xEDnh x\xE1c sau nhi\u1EC1u l\u1EA7n th\u1EED.");break}i.height+=n-p.height,i.width+=t-p.width;}await o.detach();},te=r=>r.evaluate(k.getViewport),Ct=r=>r.evaluate(k.waitForResize),Bt=(r={})=>({...r!=null&&typeof r=="object"?r:{},viewport:null});var W=be.load(),Ft={launch:W.launch.bind(W),launchPersistentContext:W.launchPersistentContext.bind(W)},D=class extends E{pwLauncher;constructor(e=Ft){super(),this.pwLauncher=e;}async launch(e={}){return this.#e(e),console.warn(fe),this.launchPersistentContext("",e)}async launchPersistentContext(e,t={}){this.#e(t);let{ignoreDefaultArgs:n}=t,i="launchPersistentContext";if(!this.pwLauncher[i])throw new Error(`Launcher kh\xF4ng h\u1ED7 tr\u1EE3 ph\u01B0\u01A1ng th\u1EE9c "${i}".`);return this._launch(false,{...t,userDataDir:e,viewport:null,launcher:{launch:async(o={})=>{let s=(o.args??[]).filter(a=>!a.startsWith("--user-data-dir"));return this.pwLauncher[i](e,{...o,args:s})}},ignoreDefaultArgs:Array.isArray(n)?n.concat(Z):n||Z})}async configure(e,t,n,i){let o=t;if(Re(o,()=>e(o)),n.width&&n.height){let s=async h=>{let{width:p,height:m}=await te(h);(p!==n.width||m!==n.height)&&await i(()=>Ce(h,n));};Oe(o,{onPageCreated:s});let[a]=o.pages();a&&await s(a);}}#e(e={}){for(let t of le)if(t in e)throw new Error(`Option "${t}" kh\xF4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3 trong plugin n\xE0y.`)}};var G=class{tempRootDir;instanceTempDir;constructor(e={}){this.tempRootDir=e.tempRootDir??g__default.default.join(C,"profile"),this.instanceTempDir=g__default.default.join(this.tempRootDir,this.generateUniqueName());}map(e,t){let n=t??this.instanceTempDir;console.log(n);let i=g__default.default.resolve(e),o=g__default.default.resolve(n);this.ensureDir(i),this.ensureDir(g__default.default.dirname(o));try{K__default.default.cpSync(i,o,{recursive:!0,force:!0});}catch(s){throw new Error(`[DataManager] Sao ch\xE9p th\u1EA5t b\u1EA1i: "${i}" \u2192 "${o}".
22
+ ${s.message}`)}return o}unmap(e){let t=g__default.default.resolve(e);if(!K__default.default.existsSync(t)){console.warn(`[DataManager] B\u1ECF qua xo\xE1: th\u01B0 m\u1EE5c kh\xF4ng t\u1ED3n t\u1EA1i "${t}"`);return}try{K__default.default.rmSync(t,{recursive:!0,force:!0});}catch(n){throw new Error(`[DataManager] D\u1ECDn d\u1EB9p th\u1EA5t b\u1EA1i: "${t}".
23
+ ${n.message}`)}}dispose(){this.unmap(this.instanceTempDir);}ensureDir(e){K__default.default.mkdirSync(e,{recursive:true});}generateUniqueName(){let e=Math.floor(Math.random()*65535).toString(16).padStart(4,"0");return `${Date.now()}_${e}`}};var re=class{options;privateKey;engineWorkingDirPath;engine;dataManager;saveProfileDirPath;profileData;context;isLaunched=false;fingerprints;proxyData;constructor(){this.engine=new D,this.options={...ge},this.privateKey=ue,this.engineWorkingDirPath=pe,this.dataManager=new G,this.profileData=[g__default.default.join(C,"profile")];}usePrivateKey(e){return this.privateKey=e,this}repackChromium(e){return this.engine=new D(e),console.warn("[BrowserEngine] N\xEAn s\u1EED d\u1EE5ng tr\xECnh duy\u1EC7t \u0111\u01B0\u1EE3c patch m\u1EB7c \u0111\u1ECBnh \u0111\u1EC3 \u0111\u1EA3m b\u1EA3o t\xEDnh \u1EA9n danh."),this}useFingerprint(e,t){return this.fingerprints=[e,t],this}useProxy(e,t){return this.proxyData=[e,t],this}useProfile(e,t){return this.saveProfileDirPath=e,this.profileData=[this.dataManager.map(e),t],this}launch(e={}){if(this.isLaunched)throw new Error("[BrowserEngine] Ph\u01B0\u01A1ng th\u1EE9c launch() ch\u1EC9 \u0111\u01B0\u1EE3c g\u1ECDi m\u1ED9t l\u1EA7n.");return this.options={...this.options,...e},this.engine.setServiceKey(this.privateKey),this.engine.setWorkingFolder(this.engineWorkingDirPath),this.engine.useProfile(...this.profileData),this.proxyData&&this.engine.useProxy(...this.proxyData),this.fingerprints&&this.engine.useFingerprint(...this.fingerprints),this.isLaunched=true,this}async newContext(e={}){if(!this.isLaunched)throw new Error("[BrowserEngine] Ph\u1EA3i g\u1ECDi launch() tr\u01B0\u1EDBc khi t\u1EA1o context.");if(this.context)throw new Error("[BrowserEngine] Context \u0111\xE3 \u0111\u01B0\u1EE3c t\u1EA1o. Vui l\xF2ng g\u1ECDi quit() tr\u01B0\u1EDBc khi t\u1EA1o m\u1EDBi.");return this.options={...this.options,...e},this.context=await this.engine.launchPersistentContext(this.profileData[0],this.options),this.context}async quit(e){if(this.isLaunched){if(this.context){await this.context.close(),this.context=void 0;let t=e??this.saveProfileDirPath;t&&this.dataManager.map(this.profileData[0],t);}this.dataManager.unmap(C),this.isLaunched=false;}}},Dt=new re;exports.Chromium=Dt;