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 +227 -0
- package/dist/index.cjs +23 -0
- package/dist/index.d.cts +459 -0
- package/dist/index.d.ts +459 -0
- package/dist/index.js +23 -0
- package/dist/plugin/mutex/win32-ia32/mutex.node +0 -0
- package/dist/plugin/mutex/win32-x64/mutex.node +0 -0
- package/package.json +104 -0
- package/project.xml +964 -0
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
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](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;
|