@skrillex1224/playwright-toolkit 3.0.15 → 3.0.17

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/index.d.ts CHANGED
@@ -18,14 +18,29 @@ export interface StatusType {
18
18
  Failed: 'FAILED';
19
19
  }
20
20
 
21
+ export interface ModeType {
22
+ Default: 'default';
23
+ Cloak: 'cloak';
24
+ }
25
+
26
+ export interface ToolkitModeStatic {
27
+ default: 'default';
28
+ cloak: 'cloak';
29
+ }
30
+
31
+ export type ToolkitMode = 'default' | 'cloak';
32
+
21
33
  export interface ConstantsModule {
22
34
  Code: CodeType;
23
35
  Status: StatusType;
36
+ Mode: ModeType;
37
+ mode: ToolkitModeStatic;
24
38
  PresetOfLiveViewKey: string;
25
39
  Device: {
26
40
  Desktop: 'desktop';
27
41
  Mobile: 'mobile';
28
42
  };
43
+ normalizeMode(value?: unknown, fallback?: ToolkitMode): ToolkitMode;
29
44
  normalizeDevice(value?: unknown, fallback?: RuntimeDevice): RuntimeDevice;
30
45
  ActorInfo: Record<string, ActorInfoItem>;
31
46
  }
@@ -33,7 +48,7 @@ export interface ConstantsModule {
33
48
  export type RuntimeDevice = 'desktop' | 'mobile';
34
49
 
35
50
  export interface ActorShareInfo {
36
- mode: 'dom' | 'response';
51
+ mode: 'dom' | 'response' | 'custom';
37
52
  prefix: string;
38
53
  xurl: Array<string | string[]>;
39
54
  }
@@ -400,9 +415,9 @@ export interface CrawlerBaseOptions {
400
415
  requestHandlerTimeoutSecs: number;
401
416
  navigationTimeoutSecs: number;
402
417
  headless: boolean;
403
- browserPoolOptions: {
404
- useFingerprints: boolean;
405
- fingerprintOptions: {
418
+ browserPoolOptions: Record<string, any> & {
419
+ useFingerprints?: boolean;
420
+ fingerprintOptions?: {
406
421
  fingerprintGeneratorOptions: Record<string, any>;
407
422
  };
408
423
  };
@@ -417,10 +432,6 @@ export interface CrawlerNavigationHook {
417
432
  (crawlingContext: any, gotoOptions: Record<string, any>): Promise<void> | void;
418
433
  }
419
434
 
420
- export interface LaunchHooks {
421
- modifyPageOptions?: (pageOptions: Record<string, any>, context: { pageId: string; browserController: any }) => Promise<void> | void;
422
- }
423
-
424
435
  export interface GetPlaywrightCrawlerOptionsInput {
425
436
  customArgs?: string[];
426
437
  proxyConfiguration?: ProxyConfiguration;
@@ -432,7 +443,6 @@ export interface GetPlaywrightCrawlerOptionsInput {
432
443
  isRunningOnApify?: boolean;
433
444
  launcher?: any;
434
445
  runtimeState?: RuntimeEnvState | null;
435
- hooks?: LaunchHooks;
436
446
  preNavigationHooks?: CrawlerNavigationHook[];
437
447
  postNavigationHooks?: CrawlerNavigationHook[];
438
448
  }
@@ -446,6 +456,25 @@ export interface LaunchModule {
446
456
  getPlaywrightCrawlerOptions(options?: GetPlaywrightCrawlerOptionsInput): PlaywrightCrawlerOptions;
447
457
  }
448
458
 
459
+ export interface CloakGetPlaywrightCrawlerOptionsInput extends GetPlaywrightCrawlerOptionsInput {
460
+ cloakOptions?: Record<string, any>;
461
+ humanizeOptions?: boolean | Record<string, any>;
462
+ crawlerBaseOptions?: Partial<CrawlerBaseOptions>;
463
+ browserPoolOptions?: Record<string, any>;
464
+ launchContext?: Record<string, any>;
465
+ recommendedGotoOptions?: Record<string, any>;
466
+ }
467
+
468
+ export interface CloakPlaywrightCrawlerOptions extends PlaywrightCrawlerOptions {
469
+ cleanup?: () => Promise<void>;
470
+ closeActiveBrowsers?: () => Promise<void>;
471
+ forceTerminateActiveProcesses?: () => Promise<void>;
472
+ }
473
+
474
+ export interface CloakLaunchModule {
475
+ getPlaywrightCrawlerOptions(options?: CloakGetPlaywrightCrawlerOptionsInput): Promise<CloakPlaywrightCrawlerOptions>;
476
+ }
477
+
449
478
  // =============================================================================
450
479
  // LiveView
451
480
  // =============================================================================
@@ -659,6 +688,8 @@ export interface ShareScreenCaptureOptions {
659
688
  restore?: boolean;
660
689
  /** 最大截图高度 (默认: 8000px) */
661
690
  maxHeight?: number;
691
+ /** 截图水印合成模式;默认取当前 toolkit mode,`cloak` 时走兼容分支。 */
692
+ mode?: 'default' | 'cloak';
662
693
  /** 返回 base64 字符串的最大字节数,默认 5MiB。 */
663
694
  maxBytes?: number;
664
695
  /** maxBytes 的别名。 */
@@ -845,6 +876,10 @@ export interface PlaywrightToolKit {
845
876
  };
846
877
  }
847
878
 
879
+ export interface CloakPlaywrightToolKit extends PlaywrightToolKit {
880
+ Launch: CloakLaunchModule;
881
+ }
882
+
848
883
  /** Browser/Extension version of the toolkit */
849
884
  export interface BrowserPlaywrightToolKit {
850
885
  Logger: LoggerModule;
@@ -859,3 +894,8 @@ export interface BrowserPlaywrightToolKit {
859
894
  }
860
895
 
861
896
  export declare function usePlaywrightToolKit(): PlaywrightToolKit;
897
+ export declare function usePlaywrightToolKit(mode: 'default'): PlaywrightToolKit;
898
+ export declare function usePlaywrightToolKit(mode: 'cloak'): CloakPlaywrightToolKit;
899
+ export declare namespace usePlaywrightToolKit {
900
+ const Mode: ToolkitModeStatic;
901
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skrillex1224/playwright-toolkit",
3
- "version": "3.0.15",
3
+ "version": "3.0.17",
4
4
  "description": "一个在 Apify/Crawlee Actor 中启用实时截图视图的实用工具库。",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -22,12 +22,14 @@
22
22
  },
23
23
  "scripts": {
24
24
  "build": "node build.js",
25
+ "postinstall": "node ./scripts/postinstall.js",
25
26
  "release": "npm run build && npm version patch && npm publish --registry https://registry.npmjs.org --access public --userconfig ../.npmrc",
26
27
  "prepublishOnly": "npm run build",
27
28
  "test": "echo \"Error: no test specified\" && exit 1"
28
29
  },
29
30
  "files": [
30
31
  "dist/",
32
+ "scripts/",
31
33
  "browser.d.ts",
32
34
  "index.d.ts",
33
35
  "README.md"
@@ -59,10 +61,16 @@
59
61
  },
60
62
  "peerDependencies": {
61
63
  "apify": "*",
64
+ "cloakbrowser": "*",
62
65
  "crawlee": "*",
63
66
  "ghost-cursor-playwright": "*",
64
67
  "playwright": "*"
65
68
  },
69
+ "peerDependenciesMeta": {
70
+ "cloakbrowser": {
71
+ "optional": true
72
+ }
73
+ },
66
74
  "devDependencies": {
67
75
  "esbuild": "^0.24.2"
68
76
  }
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { fileURLToPath, pathToFileURL } from 'node:url';
6
+
7
+ const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
8
+ const PACKAGE_ROOT = path.resolve(SCRIPT_DIR, '..');
9
+ const PREFIX = '[playwright-toolkit:postinstall]';
10
+ const SKIP_ENV = 'PLAYWRIGHT_TOOLKIT_SKIP_CLOAK_POSTINSTALL';
11
+
12
+ const log = (message) => console.log(`${PREFIX} ${message}`);
13
+ const warn = (message) => console.warn(`${PREFIX} ${message}`);
14
+
15
+ const isTruthy = (value) => /^(1|true|yes|on)$/i.test(String(value || '').trim());
16
+
17
+ const readTrimmedFile = (filePath) => {
18
+ try {
19
+ return fs.readFileSync(filePath, 'utf8').trim();
20
+ } catch {
21
+ return '';
22
+ }
23
+ };
24
+
25
+ const isBinaryReady = (binaryPath) => {
26
+ if (!binaryPath || !fs.existsSync(binaryPath)) {
27
+ return false;
28
+ }
29
+
30
+ if (process.platform === 'win32') {
31
+ return true;
32
+ }
33
+
34
+ try {
35
+ fs.accessSync(binaryPath, fs.constants.X_OK);
36
+ return true;
37
+ } catch {
38
+ return false;
39
+ }
40
+ };
41
+
42
+ function* walkParentDirs(startDir) {
43
+ let currentDir = path.resolve(startDir);
44
+ while (true) {
45
+ yield currentDir;
46
+
47
+ const parentDir = path.dirname(currentDir);
48
+ if (parentDir === currentDir) {
49
+ return;
50
+ }
51
+
52
+ currentDir = parentDir;
53
+ }
54
+ }
55
+
56
+ const findPackageRoot = (startDir, packageName) => {
57
+ for (const currentDir of walkParentDirs(startDir)) {
58
+ const packageJsonPath = path.join(currentDir, 'package.json');
59
+ if (!fs.existsSync(packageJsonPath)) {
60
+ continue;
61
+ }
62
+
63
+ try {
64
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
65
+ if (packageJson?.name === packageName) {
66
+ return currentDir;
67
+ }
68
+ } catch {
69
+ // Ignore invalid JSON and keep walking.
70
+ }
71
+ }
72
+
73
+ return null;
74
+ };
75
+
76
+ const findInstalledCloakRoot = async () => {
77
+ try {
78
+ const resolvedEntryUrl = await import.meta.resolve('cloakbrowser');
79
+ const resolvedEntryPath = fileURLToPath(resolvedEntryUrl);
80
+ const packageRoot = findPackageRoot(path.dirname(resolvedEntryPath), 'cloakbrowser');
81
+ if (packageRoot) {
82
+ return packageRoot;
83
+ }
84
+ } catch {
85
+ // Fall back to filesystem lookup.
86
+ }
87
+
88
+ const searchRoots = [
89
+ process.env.INIT_CWD,
90
+ process.cwd(),
91
+ process.env.npm_config_local_prefix,
92
+ PACKAGE_ROOT,
93
+ SCRIPT_DIR,
94
+ ].filter(Boolean);
95
+
96
+ for (const searchRoot of new Set(searchRoots)) {
97
+ for (const currentDir of walkParentDirs(searchRoot)) {
98
+ const packageJsonPath = path.join(currentDir, 'node_modules', 'cloakbrowser', 'package.json');
99
+ if (fs.existsSync(packageJsonPath)) {
100
+ return path.dirname(packageJsonPath);
101
+ }
102
+ }
103
+ }
104
+
105
+ return null;
106
+ };
107
+
108
+ const clearStaleVersionMarkers = ({ cacheDir, expectedVersion, platformTag }) => {
109
+ let clearedMarkers = 0;
110
+
111
+ for (const markerName of [`latest_version_${platformTag}`, 'latest_version']) {
112
+ const markerPath = path.join(cacheDir, markerName);
113
+ const markerVersion = readTrimmedFile(markerPath);
114
+
115
+ if (!markerVersion || markerVersion === expectedVersion) {
116
+ continue;
117
+ }
118
+
119
+ fs.rmSync(markerPath, { force: true });
120
+ clearedMarkers += 1;
121
+ log(`Removed stale ${markerName}=${markerVersion} to keep cloakbrowser pinned to ${expectedVersion}.`);
122
+ }
123
+
124
+ return clearedMarkers;
125
+ };
126
+
127
+ const main = async () => {
128
+ if (isTruthy(process.env[SKIP_ENV])) {
129
+ log(`Skipped because ${SKIP_ENV} is set.`);
130
+ return;
131
+ }
132
+
133
+ const cloakRoot = await findInstalledCloakRoot();
134
+ if (!cloakRoot) {
135
+ log('cloakbrowser is not installed; skipping binary preinstall.');
136
+ return;
137
+ }
138
+
139
+ const [cloakModule, cloakConfig] = await Promise.all([
140
+ import(pathToFileURL(path.join(cloakRoot, 'dist', 'index.js')).href),
141
+ import(pathToFileURL(path.join(cloakRoot, 'dist', 'config.js')).href),
142
+ ]);
143
+
144
+ const localBinaryOverride = cloakConfig.getLocalBinaryOverride?.();
145
+ if (localBinaryOverride) {
146
+ if (!fs.existsSync(localBinaryOverride)) {
147
+ throw new Error(`CLOAKBROWSER_BINARY_PATH points to a missing file: ${localBinaryOverride}`);
148
+ }
149
+
150
+ log(`Using CLOAKBROWSER_BINARY_PATH=${localBinaryOverride}; skipping managed binary install.`);
151
+ return;
152
+ }
153
+
154
+ try {
155
+ cloakConfig.checkPlatformAvailable?.();
156
+ } catch (error) {
157
+ warn(`${error?.message || String(error)} Skipping managed binary install.`);
158
+ return;
159
+ }
160
+
161
+ const expectedVersion = cloakConfig.getChromiumVersion();
162
+ const platformTag = cloakConfig.getPlatformTag();
163
+ const cacheDir = cloakConfig.getCacheDir();
164
+ const expectedBinaryPath = cloakConfig.getBinaryPath(expectedVersion);
165
+
166
+ process.env.CLOAKBROWSER_AUTO_UPDATE = 'false';
167
+
168
+ const clearedMarkers = clearStaleVersionMarkers({
169
+ cacheDir,
170
+ expectedVersion,
171
+ platformTag,
172
+ });
173
+
174
+ if (isBinaryReady(expectedBinaryPath)) {
175
+ const suffix = clearedMarkers > 0 ? ' Cleared stale version markers.' : '';
176
+ log(`cloakbrowser ${expectedVersion} already available at ${expectedBinaryPath}.${suffix}`);
177
+ return;
178
+ }
179
+
180
+ log(`Installing cloakbrowser ${expectedVersion} for ${platformTag}...`);
181
+ const resolvedBinaryPath = await cloakModule.ensureBinary();
182
+
183
+ if (!isBinaryReady(expectedBinaryPath)) {
184
+ throw new Error(
185
+ `Expected cloakbrowser binary at ${expectedBinaryPath}, but it is still missing after ensureBinary() returned ${resolvedBinaryPath}.`,
186
+ );
187
+ }
188
+
189
+ if (path.resolve(resolvedBinaryPath) !== path.resolve(expectedBinaryPath)) {
190
+ warn(
191
+ `ensureBinary() returned ${resolvedBinaryPath}, expected ${expectedBinaryPath}. `
192
+ + 'The package-pinned binary is installed and stale version markers were cleared.',
193
+ );
194
+ }
195
+
196
+ log(`cloakbrowser ${expectedVersion} ready at ${expectedBinaryPath}.`);
197
+ };
198
+
199
+ main().catch((error) => {
200
+ const message = error?.stack || error?.message || String(error);
201
+ console.error(`${PREFIX} ${message}`);
202
+ process.exit(1);
203
+ });