arn-browser 0.0.14 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arn-browser",
3
- "version": "0.0.14",
3
+ "version": "0.1.0",
4
4
  "description": "A lightweight, browser autmation helper.",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -11,7 +11,7 @@
11
11
  "test": "test"
12
12
  },
13
13
  "dependencies": {
14
- "@aws-sdk/client-ec2": "^3.946.0",
14
+ "@aws-sdk/client-ec2": "^3.953.0",
15
15
  "@ghostery/adblocker": "^2.13.0",
16
16
  "arn-knexjs": "^0.0.3",
17
17
  "camoufox-js": "^0.8.4",
@@ -174,24 +174,32 @@ export interface LaunchOptions {
174
174
  CapSolver?: boolean;
175
175
 
176
176
  /**
177
- * Maximum width constraint for fingerprint generation.
178
- * - If <= 10: Treated as multiplier of detected screen width (e.g. 1 = 100%, 0.8 = 80%).
179
- * - If > 10: Treated as specific pixel width.
180
- * Default: 1 (100% of detected screen width or 1920px fallback)
181
- */
182
- maxWidth?: number;
183
-
184
- /**
185
- * Whether to spoof browser fingerprint with FingerprintInjector.
186
- * When true: Applies userAgent, viewport, locale from fingerprintData and uses FingerprintInjector.
187
- * When false: Uses null viewport and applies stealth script only.
177
+ * Configuration for browser fingerprint spoofing.
178
+ *
179
+ * - `false`: No fingerprint spoofing (default)
180
+ * - `true`: Spoof fingerprint with viewport: null (maximized window)
181
+ * - `{ minWidth?, maxWidth?, minHeight?, maxHeight? }`: Spoof fingerprint with screen constraints
182
+ * and use the generated screen dimensions as viewport.
188
183
  *
189
184
  * **For persistent Brave profiles:** The value is locked on first launch.
190
185
  * Subsequent launches will use the saved value, ignoring the current parameter.
191
186
  *
187
+ * @example
188
+ * // Spoof with maximized window (viewport: null)
189
+ * spoof_fingerprint: true
190
+ *
191
+ * @example
192
+ * // Spoof with specific screen constraints
193
+ * spoof_fingerprint: { minWidth: 1366, maxWidth: 1920, minHeight: 768 }
194
+ *
192
195
  * Default: false
193
196
  */
194
- spoof_fingerprint?: boolean;
197
+ spoof_fingerprint?: boolean | {
198
+ minWidth?: number;
199
+ maxWidth?: number;
200
+ minHeight?: number;
201
+ maxHeight?: number;
202
+ };
195
203
 
196
204
  // ========================================================================
197
205
  // 5. ENGINE SPECIFIC GROUPS
@@ -137,8 +137,10 @@ function cleanUpTempProfiles(ageLimitMinutes) {
137
137
 
138
138
  /**
139
139
  * Helper to generate consistent fingerprint options.
140
+ * @param {string} browserType - 'chromium' | 'firefox' | 'brave'
141
+ * @param {object|null} screenOptions - Optional screen constraints { minWidth, maxWidth, minHeight, maxHeight }
140
142
  */
141
- function getFingerprintConfig(browserType, maxWidth) {
143
+ function getFingerprintConfig(browserType, screenOptions = null) {
142
144
  const config = {
143
145
  devices: ["desktop"],
144
146
  operatingSystems: [detectedOs],
@@ -148,16 +150,19 @@ function getFingerprintConfig(browserType, maxWidth) {
148
150
  };
149
151
 
150
152
  if (browserType === "chromium") {
151
- config.browsers.push({ name: "chrome", minVersion: 140 });
153
+ config.browsers.push({ name: "chrome", minVersion: 141 });
152
154
  } else if (browserType === "firefox") {
153
- config.browsers.push({ name: "firefox", minVersion: 140 });
155
+ config.browsers.push({ name: "firefox", minVersion: 141 });
154
156
  } else if (browserType === "brave") {
155
- config.browsers.push({ name: "chrome", minVersion: 140 });
157
+ config.browsers.push({ name: "chrome", minVersion: 141 });
156
158
  }
157
159
 
158
- if (maxWidth) {
159
- const widthVal = maxWidth <= 10 ? Math.round(maxWidth * 1920) : maxWidth;
160
- config.screen.maxWidth = widthVal;
160
+ // Apply screen filter options if provided
161
+ if (screenOptions && typeof screenOptions === 'object') {
162
+ if (screenOptions.minWidth) config.screen.minWidth = screenOptions.minWidth;
163
+ if (screenOptions.maxWidth) config.screen.maxWidth = screenOptions.maxWidth;
164
+ if (screenOptions.minHeight) config.screen.minHeight = screenOptions.minHeight;
165
+ if (screenOptions.maxHeight) config.screen.maxHeight = screenOptions.maxHeight;
161
166
  }
162
167
 
163
168
  return config;
@@ -171,7 +176,6 @@ export async function launchBrowser({
171
176
  // Common
172
177
  timezoneId = null,
173
178
  proxy,
174
- maxWidth = 1,
175
179
 
176
180
  // Path & Storage
177
181
  profile_path = null,
@@ -183,6 +187,9 @@ export async function launchBrowser({
183
187
  humanize_options = {}, // { humanize, maxTime, minTime, showCursor } - defaults to enabled for non-camoufox
184
188
 
185
189
  // Spoof Fingerprint
190
+ // - false: No spoofing
191
+ // - true: Spoof fingerprint with viewport: null (no screen constraint)
192
+ // - { minWidth?, maxWidth?, minHeight?, maxHeight? }: Spoof with screen constraints
186
193
  spoof_fingerprint = false,
187
194
 
188
195
  // Browser Specific Grouped Options
@@ -214,7 +221,6 @@ export async function launchBrowser({
214
221
  proxy,
215
222
  timezoneId,
216
223
  CapSolver,
217
- maxWidth,
218
224
  humanize_options: effectiveHumanizeOptions,
219
225
  spoof_fingerprint,
220
226
  });
@@ -224,7 +230,6 @@ export async function launchBrowser({
224
230
  profilePath: fullPath,
225
231
  proxy,
226
232
  timezoneId,
227
- maxWidth,
228
233
  humanize_options: effectiveHumanizeOptions,
229
234
  spoof_fingerprint,
230
235
  });
@@ -236,7 +241,6 @@ export async function launchBrowser({
236
241
  proxy,
237
242
  timezoneId,
238
243
  CapSolver,
239
- maxWidth,
240
244
  humanize_options: effectiveHumanizeOptions,
241
245
  spoof_fingerprint,
242
246
  });
@@ -247,7 +251,6 @@ export async function launchBrowser({
247
251
  profilePath: fullPath,
248
252
  proxy,
249
253
  timezoneId,
250
- maxWidth,
251
254
  camoufox_options,
252
255
  // Spoof Fingerprint not needed for Camoufox
253
256
  });
@@ -274,9 +277,7 @@ export async function launchBrowser({
274
277
  // ==========================================================================
275
278
  // 4. ENGINE: CHROMIUM
276
279
  // ==========================================================================
277
- async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, maxWidth, humanize_options
278
- , spoof_fingerprint
279
- }) {
280
+ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, humanize_options, spoof_fingerprint }) {
280
281
  const isPersistent = !!profilePath;
281
282
 
282
283
  // 1. Determine Path (Temp needs it for fingerprint storage, Persistent needs it for data)
@@ -324,12 +325,15 @@ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, max
324
325
  const proxyObj = formatProxy(proxy);
325
326
  const tz = timezoneId || undefined;
326
327
 
328
+ // Determine screen options from spoof_fingerprint if it's an object
329
+ const screenOptions = (spoof_fingerprint && typeof spoof_fingerprint === 'object') ? spoof_fingerprint : null;
330
+
327
331
  // ==================================================================
328
332
  // BRANCH A: TEMP PROFILE (launch + newInjectedContext)
329
333
  // ==================================================================
330
334
  if (!isPersistent) {
331
335
  try {
332
- const fpConfig = getFingerprintConfig("chromium", maxWidth);
336
+ const fpConfig = getFingerprintConfig("chromium", screenOptions);
333
337
  const fingerprintData = new FingerprintGenerator().getFingerprint(fpConfig);
334
338
 
335
339
  // Launch standard browser (not persistent context)
@@ -350,6 +354,7 @@ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, max
350
354
  } else {
351
355
  context = await browser.newContext({
352
356
  timezoneId: tz,
357
+ viewport: null,
353
358
  });
354
359
  // Manual Stealth Script
355
360
  await addStealthScript(context);
@@ -398,7 +403,7 @@ async function chromiumLauncher({ profilePath, proxy, timezoneId, CapSolver, max
398
403
  // ==========================================================================
399
404
  // 5. ENGINE: FIREFOX
400
405
  // ==========================================================================
401
- async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth, humanize_options, spoof_fingerprint }) {
406
+ async function firefoxLauncher({ profilePath, proxy, timezoneId, humanize_options, spoof_fingerprint }) {
402
407
  const isPersistent = !!profilePath;
403
408
 
404
409
  // 1. Determine Path
@@ -408,6 +413,9 @@ async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth, human
408
413
  const proxyObj = formatProxy(proxy);
409
414
  const tz = timezoneId || undefined;
410
415
 
416
+ // Determine screen options from spoof_fingerprint if it's an object
417
+ const screenOptions = (spoof_fingerprint && typeof spoof_fingerprint === 'object') ? spoof_fingerprint : null;
418
+
411
419
  // Firefox specific preferences
412
420
  const firefoxUserPrefs = {
413
421
  "dom.webdriver.enabled": false,
@@ -420,7 +428,7 @@ async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth, human
420
428
  // ==================================================================
421
429
  if (!isPersistent) {
422
430
  try {
423
- const fpConfig = getFingerprintConfig("firefox", maxWidth);
431
+ const fpConfig = getFingerprintConfig("firefox", screenOptions);
424
432
  const fingerprintData = new FingerprintGenerator().getFingerprint(fpConfig);
425
433
 
426
434
  const browser = await firefox.launch({
@@ -441,6 +449,7 @@ async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth, human
441
449
  else {
442
450
  context = await browser.newContext({
443
451
  timezoneId: tz,
452
+ viewport: null,
444
453
  });
445
454
  // Manual Stealth Script
446
455
  await addStealthScript(context);
@@ -488,7 +497,7 @@ async function firefoxLauncher({ profilePath, proxy, timezoneId, maxWidth, human
488
497
  // ==========================================================================
489
498
  // 6. ENGINE: BRAVE
490
499
  // ==========================================================================
491
- async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, maxWidth, humanize_options, spoof_fingerprint }) {
500
+ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, humanize_options, spoof_fingerprint }) {
492
501
  const isPersistent = !!profilePath;
493
502
  const activePath = isPersistent ? profilePath : path.join(TEMP_DIR, crypto.randomUUID());
494
503
 
@@ -507,22 +516,31 @@ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, maxWid
507
516
  // Load saved settings from first launch
508
517
  const savedSettings = JSON.parse(fs.readFileSync(settingsFilePath, "utf-8"));
509
518
  effectiveSpoofFingerprint = savedSettings.spoof_fingerprint;
510
- console.log(`📋 Using saved spoof_fingerprint: ${effectiveSpoofFingerprint} (ignoring current: ${spoof_fingerprint})`);
519
+ console.log(`📋 Using saved spoof_fingerprint: ${JSON.stringify(effectiveSpoofFingerprint)} (ignoring current: ${JSON.stringify(spoof_fingerprint)})`);
511
520
  } else {
512
521
  // First launch - save the current setting
513
522
  const settings = { spoof_fingerprint: spoof_fingerprint };
514
523
  fs.writeFileSync(settingsFilePath, JSON.stringify(settings, null, 2), "utf-8");
515
- console.log(`📝 Saved spoof_fingerprint setting: ${spoof_fingerprint}`);
524
+ console.log(`📝 Saved spoof_fingerprint setting: ${JSON.stringify(spoof_fingerprint)}`);
516
525
  }
517
526
  }
518
527
 
528
+ // Determine screen options from spoof_fingerprint if it's an object
529
+ const screenOptions = (effectiveSpoofFingerprint && typeof effectiveSpoofFingerprint === 'object') ? effectiveSpoofFingerprint : null;
530
+
531
+ // Determine if we should use screen viewport or null
532
+ // - spoof_fingerprint = true (boolean) -> spoof with viewport: null
533
+ // - spoof_fingerprint = { minWidth, maxWidth, ... } (object) -> spoof with generated screen viewport
534
+ const useScreenViewport = screenOptions !== null;
535
+
519
536
  let fingerprintData;
520
537
  const fingerprintFilePath = path.join(activePath, "fingerprint.json");
521
538
 
522
539
  if (fs.existsSync(fingerprintFilePath)) {
523
540
  fingerprintData = JSON.parse(fs.readFileSync(fingerprintFilePath, "utf-8"));
524
541
  } else {
525
- const fpConfig = getFingerprintConfig("brave", maxWidth);
542
+ // Generate fingerprint with screen options (null if spoof_fingerprint is just true)
543
+ const fpConfig = getFingerprintConfig("brave", screenOptions);
526
544
  fingerprintData = new FingerprintGenerator().getFingerprint(fpConfig);
527
545
 
528
546
  if (isPersistent) {
@@ -601,6 +619,9 @@ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, maxWid
601
619
 
602
620
  let context;
603
621
  if (effectiveSpoofFingerprint) {
622
+ // Determine viewport: use screen from fingerprint if object provided, otherwise null
623
+ const viewport = useScreenViewport ? fingerprintData.fingerprint.screen : null;
624
+
604
625
  context = await chromium.launchPersistentContext(activePath, {
605
626
  headless: false,
606
627
  executablePath: braveBin,
@@ -609,7 +630,9 @@ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, maxWid
609
630
  ignoreDefaultArgs: ignoreDefaultArgs,
610
631
  args: args,
611
632
  userAgent: fingerprintData.fingerprint.navigator.userAgent,
612
- viewport: fingerprintData.fingerprint.screen,
633
+ // viewport: viewport,
634
+ // For Brave browser viewport set not working
635
+ viewport: null,
613
636
  locale: fingerprintData.fingerprint.navigator.language,
614
637
  });
615
638
 
@@ -653,7 +676,7 @@ async function braveLauncher({ profilePath, proxy, CapSolver, timezoneId, maxWid
653
676
  // ==========================================================================
654
677
  // 7. ENGINE: CAMOUFOX
655
678
  // ==========================================================================
656
- async function camoufoxLauncher({ profilePath, proxy, timezoneId, maxWidth, camoufox_options = {} }) {
679
+ async function camoufoxLauncher({ profilePath, proxy, timezoneId, camoufox_options = {} }) {
657
680
  const isPersistent = !!profilePath;
658
681
  const activePath = isPersistent ? profilePath : path.join(TEMP_DIR, crypto.randomUUID());
659
682
 
@@ -686,7 +709,6 @@ async function camoufoxLauncher({ profilePath, proxy, timezoneId, maxWidth, camo
686
709
  const randomIndex = Math.floor(Math.random() * validMajorVersions.length);
687
710
  return validMajorVersions[randomIndex];
688
711
  }
689
- const screenWidth = maxWidth ? (maxWidth <= 10 ? Math.round(maxWidth * 1920) : maxWidth) : 1920;
690
712
 
691
713
  // Prepare the base options
692
714
  const optionsGenerator = {
@@ -704,12 +726,9 @@ async function camoufoxLauncher({ profilePath, proxy, timezoneId, maxWidth, camo
704
726
  ...camoufox_options, // Spread user overrides
705
727
  };
706
728
 
729
+ // Use screen from camoufox_options if provided
707
730
  if (camoufox_options.screen) {
708
731
  optionsGenerator.screen = camoufox_options.screen;
709
- } else {
710
- optionsGenerator.screen = {
711
- maxWidth: screenWidth,
712
- };
713
732
  }
714
733
  // If GeoIP is TRUE, pass proxy inside launchOptions
715
734
  if (isGeoIpEnabled && proxyObj) {