@iflyrpa/playwright 1.0.9 → 1.0.11

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/dist/index.cjs CHANGED
@@ -1,21 +1,22 @@
1
1
  'use strict';
2
2
 
3
- const path = require('node:path');
4
3
  const fs = require('node:fs');
4
+ const path = require('node:path');
5
5
  const https = require('node:https');
6
- const electron = require('electron');
7
- const node_child_process = require('node:child_process');
6
+ const log = require('loglevel');
7
+ const npminstall = require('npminstall');
8
8
 
9
9
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
10
10
 
11
- const path__default = /*#__PURE__*/_interopDefaultCompat(path);
12
11
  const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
12
+ const path__default = /*#__PURE__*/_interopDefaultCompat(path);
13
13
  const https__default = /*#__PURE__*/_interopDefaultCompat(https);
14
+ const log__default = /*#__PURE__*/_interopDefaultCompat(log);
15
+ const npminstall__default = /*#__PURE__*/_interopDefaultCompat(npminstall);
14
16
 
15
17
  const name = "@iflyrpa/playwright";
16
18
  const type = "module";
17
- const version$1 = "1.0.9";
18
- const description = "";
19
+ const version$1 = "1.0.11";
19
20
  const main = "./dist/index.cjs";
20
21
  const module$1 = "./dist/index.mjs";
21
22
  const types = "./dist/index.d.ts";
@@ -24,14 +25,14 @@ const scripts = {
24
25
  dev: "unbuild --stub",
25
26
  start: "esno src/index.ts"
26
27
  };
27
- const keywords = [
28
- ];
29
28
  const author = "bijinfeng";
30
29
  const license = "ISC";
31
30
  const files = [
32
31
  "dist"
33
32
  ];
34
33
  const dependencies = {
34
+ loglevel: "^1.9.2",
35
+ npminstall: "^7.12.0",
35
36
  playwright: "^1.46.1"
36
37
  };
37
38
  const peerDependencies = {
@@ -46,12 +47,10 @@ const packageJson = {
46
47
  name: name,
47
48
  type: type,
48
49
  version: version$1,
49
- description: description,
50
50
  main: main,
51
51
  module: module$1,
52
52
  types: types,
53
53
  scripts: scripts,
54
- keywords: keywords,
55
54
  author: author,
56
55
  license: license,
57
56
  files: files,
@@ -219,7 +218,7 @@ const visibleRangeTexts = {
219
218
  private: "\u79C1\u5BC6"
220
219
  };
221
220
  const xiaohongshuPublishAction = async (props) => {
222
- const { page, cachePath, params } = props;
221
+ const { page, tmpCachePath, params } = props;
223
222
  const selectAddress = async (selector, address) => {
224
223
  const instance = typeof selector === "string" ? page.locator(selector) : selector;
225
224
  await instance.click();
@@ -242,11 +241,13 @@ const xiaohongshuPublishAction = async (props) => {
242
241
  await page.locator("#content-area .menu-container .publish-video a").click().catch(() => {
243
242
  throw new Error("\u672A\u627E\u5230\u53D1\u5E03\u7B14\u8BB0\u6309\u94AE");
244
243
  });
245
- await page.locator(".creator-container .header .title").filter({ hasText: /^上传图文$/ }).click();
244
+ await page.locator(".creator-container .header .title").filter({ hasText: /^上传图文$/ }).click().catch(() => {
245
+ throw new Error("\u672A\u627E\u5230\u4E0A\u4F20\u56FE\u6587\u6309\u94AE");
246
+ });
246
247
  const images = await Promise.all(
247
248
  params.banners.map((url) => {
248
249
  const fileName = getFilenameFromUrl(url);
249
- return downloadImage(url, path__default.join(cachePath, "image-tmp", fileName));
250
+ return downloadImage(url, path__default.join(tmpCachePath, fileName));
250
251
  })
251
252
  );
252
253
  const fileChooserPromise = page.waitForEvent("filechooser");
@@ -336,6 +337,64 @@ const xiaohongshuPublishAction = async (props) => {
336
337
  return response;
337
338
  };
338
339
 
340
+ var __defProp$2 = Object.defineProperty;
341
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
342
+ var __publicField$2 = (obj, key, value) => {
343
+ __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
344
+ return value;
345
+ };
346
+ const _Logger = class _Logger {
347
+ constructor(cachePath) {
348
+ __publicField$2(this, "stream");
349
+ if (_Logger.instance) {
350
+ return _Logger.instance;
351
+ }
352
+ const logFile = path__default.join(cachePath, "rpa.log");
353
+ this.stream = fs__default.createWriteStream(logFile, { flags: "a" });
354
+ log__default.setLevel("debug");
355
+ log__default.methodFactory = (methodName) => {
356
+ return (message) => {
357
+ this.stream.write(
358
+ `[${( new Date()).toISOString()}] ${methodName.toUpperCase()}: ${message}
359
+ `
360
+ );
361
+ };
362
+ };
363
+ log__default.setLevel(log__default.getLevel());
364
+ _Logger.instance = this;
365
+ }
366
+ static getInstance(cachePath) {
367
+ if (!_Logger.instance) {
368
+ _Logger.instance = new _Logger(cachePath);
369
+ }
370
+ return _Logger.instance;
371
+ }
372
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
373
+ debug(...msg) {
374
+ log__default.debug(...msg);
375
+ }
376
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
377
+ info(...msg) {
378
+ log__default.info(...msg);
379
+ }
380
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
381
+ warn(...msg) {
382
+ log__default.warn(...msg);
383
+ }
384
+ error(prefix, error) {
385
+ log__default.error(prefix);
386
+ if (error instanceof Error) {
387
+ log__default.error(`${error.message}
388
+ Error stack: ${error.stack}`);
389
+ }
390
+ }
391
+ close() {
392
+ this.stream.end();
393
+ }
394
+ };
395
+ __publicField$2(_Logger, "instance", null);
396
+ let Logger = _Logger;
397
+
339
398
  const template = `
340
399
  const { app } = require("electron");
341
400
 
@@ -352,15 +411,28 @@ const generateFile = async (dir) => {
352
411
  }
353
412
  return filePath;
354
413
  };
355
- const createElectronApp = async (cachePath, playwrightPackage) => {
356
- const playwright = await playwrightPackage;
357
- const mainPath = await generateFile(cachePath);
358
- const electronApp = await playwright._electron.launch({
359
- executablePath: electron.app.getPath("exe"),
360
- // 获取 Electron 可执行文件的路径
361
- args: [mainPath]
362
- });
363
- return electronApp;
414
+ const createElectronApp = async (cachePath, playwright) => {
415
+ const logger = Logger.getInstance(cachePath);
416
+ try {
417
+ const executablePath = path__default.join(
418
+ cachePath,
419
+ "node_modules",
420
+ ".bin",
421
+ "electron"
422
+ );
423
+ const mainPath = await generateFile(cachePath);
424
+ const electronApp = await playwright._electron.launch({
425
+ executablePath,
426
+ // 获取 Electron 可执行文件的路径
427
+ args: [mainPath],
428
+ cwd: cachePath
429
+ });
430
+ logger.info(`electron \u542F\u52A8\u6210\u529F\uFF1A${executablePath} ${mainPath}`);
431
+ return electronApp;
432
+ } catch (error) {
433
+ logger.error("electron \u542F\u52A8\u5931\u8D25\uFF1A", error);
434
+ throw error;
435
+ }
364
436
  };
365
437
 
366
438
  var __defProp$1 = Object.defineProperty;
@@ -369,6 +441,7 @@ var __publicField$1 = (obj, key, value) => {
369
441
  __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
370
442
  return value;
371
443
  };
444
+ const NPM_REGISTRY = "https://registry.npmmirror.com/";
372
445
  class PackageManager {
373
446
  constructor(params) {
374
447
  __publicField$1(this, "cacheDir");
@@ -376,47 +449,23 @@ class PackageManager {
376
449
  __publicField$1(this, "forceUpdate");
377
450
  // 是否强制更新
378
451
  __publicField$1(this, "initPromise");
452
+ __publicField$1(this, "logger");
453
+ this.logger = Logger.getInstance(params.cacheDir);
379
454
  this.cacheDir = params.cacheDir;
380
455
  this.forceUpdate = params.forceUpdate || true;
456
+ this.initCacheProject();
381
457
  this.initPromise = this.init(params.packageName, params.packageVersion);
382
458
  }
383
- // 在子线程执行 npm 命令
384
- execCommand(cmd, modules, where = this.cacheDir) {
385
- return new Promise((resolve) => {
386
- const args = [cmd].concat(modules).concat("--color=always").concat("--save");
387
- try {
388
- const npm = node_child_process.spawn("npm", args, { cwd: where });
389
- let output = "";
390
- npm.stdout?.on("data", (data) => {
391
- output += data;
392
- }).pipe(process.stdout);
393
- npm.stderr?.on("data", (data) => {
394
- output += data;
395
- }).pipe(process.stderr);
396
- npm.on("close", (code) => {
397
- if (!code) {
398
- resolve({ code: 0, data: output });
399
- } else {
400
- resolve({ code, data: output });
401
- }
402
- });
403
- npm.on("error", (err) => {
404
- console.error("NPM is not installed");
405
- console.error(err);
406
- });
407
- } catch (error) {
408
- console.error(error);
409
- }
410
- });
411
- }
412
459
  // 构建 package.json 文件,有了该文件后,依赖可以安装在该目录下,避免污染全局环境
413
460
  initCacheProject() {
414
461
  const packagePath = path__default.join(this.cacheDir, "package.json");
415
462
  if (!fs__default.existsSync(packagePath)) {
463
+ this.logger.info("package.json \u4E0D\u5B58\u5728\uFF0C\u6DFB\u52A0\u8BE5\u6587\u4EF6");
416
464
  const pkg = {
417
465
  name: "rpa-plugins",
418
466
  description: "rpa-plugins",
419
- license: "MIT"
467
+ license: "MIT",
468
+ main: "./src/main.js"
420
469
  };
421
470
  ensureFileSync(packagePath);
422
471
  fs__default.writeFileSync(packagePath, JSON.stringify(pkg), "utf8");
@@ -436,6 +485,7 @@ class PackageManager {
436
485
  try {
437
486
  return require(path__default.join(pluginDir, module));
438
487
  } catch (error) {
488
+ this.logger.warn(`${module}\u672C\u5730\u4F9D\u8D56\u4E0D\u5B58\u5728\uFF0C${pluginDir}`);
439
489
  return null;
440
490
  }
441
491
  }
@@ -445,28 +495,30 @@ class PackageManager {
445
495
  }
446
496
  // 安装依赖
447
497
  install(module, version) {
448
- const moduleName = version ? `${module}@${version}` : module;
449
- return this.execCommand("install", [moduleName]);
450
- }
451
- // 更新依赖
452
- update(module) {
453
- return this.execCommand("update", [module]);
498
+ return npminstall__default({
499
+ root: this.cacheDir,
500
+ pkgs: [{ name: module, version }],
501
+ registry: NPM_REGISTRY
502
+ });
454
503
  }
455
504
  async init(name, version) {
456
- this.initCacheProject();
457
505
  const plugin = this.getPlugin(path__default.join(name, "package.json"));
458
- if (!plugin) {
459
- await this.install(name, version);
460
- } else if (this.forceUpdate) {
461
- const pkInfo = await this.getPluginInfo(name);
462
- if (!pkInfo)
463
- return;
464
- const hasNewVersion = semver.gt(pkInfo.version, plugin.version);
465
- if (hasNewVersion) {
466
- await this.install(name, pkInfo.version);
506
+ try {
507
+ if (!plugin) {
508
+ await this.install(name, version);
509
+ } else if (this.forceUpdate) {
510
+ const pkInfo = await this.getPluginInfo(name);
511
+ if (!pkInfo)
512
+ return;
513
+ const hasNewVersion = semver.gt(pkInfo.version, plugin.version);
514
+ if (hasNewVersion) {
515
+ await this.install(name, pkInfo.version);
516
+ }
467
517
  }
518
+ this.logger.info(`${name} package manager init done!`);
519
+ } catch (error) {
520
+ this.logger.error(`${name} package manager init error`, error);
468
521
  }
469
- console.log("Package manager init done!");
470
522
  }
471
523
  }
472
524
 
@@ -477,19 +529,20 @@ var __publicField = (obj, key, value) => {
477
529
  return value;
478
530
  };
479
531
  const PLAYWRIGHT_VERSION = "1.46.1";
532
+ const ELECTRON_VERSION = "22.3.27";
480
533
  class LocalAutomateTask {
481
- constructor(params) {
534
+ constructor({ cachePath, debug }) {
535
+ __publicField(this, "logger");
482
536
  __publicField(this, "cachePath");
483
537
  __publicField(this, "debug");
484
- __publicField(this, "appWhenReady");
485
538
  __publicField(this, "playwrightPackage");
486
- this.cachePath = params.cachePath;
487
- this.debug = params.debug || false;
539
+ __publicField(this, "electronPackage");
540
+ __publicField(this, "_electronApp", null);
541
+ this.cachePath = cachePath;
542
+ this.debug = debug || false;
543
+ this.logger = Logger.getInstance(cachePath);
488
544
  this.playwrightPackage = this.installPlaywright();
489
- this.appWhenReady = createElectronApp(
490
- params.cachePath,
491
- this.playwrightPackage
492
- );
545
+ this.electronPackage = this.installElectron();
493
546
  }
494
547
  /**
495
548
  * 安装 playwright
@@ -503,19 +556,67 @@ class LocalAutomateTask {
503
556
  });
504
557
  return playwrightPackageManager.getPluginAfterInit("playwright");
505
558
  }
559
+ /**
560
+ * 安装 electron
561
+ * @returns
562
+ */
563
+ installElectron() {
564
+ const playwrightPackageManager = new PackageManager({
565
+ packageName: "electron",
566
+ packageVersion: ELECTRON_VERSION,
567
+ cacheDir: this.cachePath
568
+ });
569
+ return playwrightPackageManager.getPluginAfterInit("electron");
570
+ }
571
+ /**
572
+ * 启动 Electron
573
+ * @returns
574
+ */
575
+ async getElectronApp() {
576
+ if (!this._electronApp) {
577
+ const [playwright] = await Promise.all([
578
+ this.playwrightPackage,
579
+ this.electronPackage
580
+ ]);
581
+ this._electronApp = await createElectronApp(this.cachePath, playwright);
582
+ }
583
+ return this._electronApp;
584
+ }
585
+ /**
586
+ * 临时文件目录
587
+ * @returns
588
+ */
589
+ getTmpPath() {
590
+ return path__default.join(this.cachePath, "tmp");
591
+ }
592
+ /**
593
+ * 清空临时文件
594
+ */
595
+ clearTmpPath() {
596
+ const tmpPath = this.getTmpPath();
597
+ return fs__default.rm(tmpPath, { recursive: true, force: true }, (err) => {
598
+ if (err) {
599
+ this.logger.error("\u5220\u9664\u4E34\u65F6\u6587\u4EF6\u5931\u8D25\uFF0C", err);
600
+ } else {
601
+ this.logger.info("\u5220\u9664\u4E34\u65F6\u6587\u4EF6\u6210\u529F");
602
+ }
603
+ });
604
+ }
506
605
  /**
507
606
  * 关闭 playwright 启动的 electron 客户端
508
607
  * @returns
509
608
  */
510
609
  async close() {
511
- const electronApp = await this.appWhenReady;
610
+ this.logger.close();
611
+ this.clearTmpPath();
612
+ const electronApp = await this.getElectronApp();
512
613
  return electronApp.close();
513
614
  }
514
615
  /**
515
616
  * 小红书自动化发布
516
617
  */
517
618
  async xiaohongshuPublish(params) {
518
- const electronApp = await this.appWhenReady;
619
+ const electronApp = await this.getElectronApp();
519
620
  const commonCookies = {
520
621
  path: "/",
521
622
  sameSite: "lax",
@@ -555,22 +656,29 @@ class LocalAutomateTask {
555
656
  { pageParams }
556
657
  )
557
658
  ]);
558
- const res = await xiaohongshuPublishAction({
559
- page,
560
- params,
561
- cachePath: this.cachePath,
562
- debug: !!this.debug
563
- });
564
- return res;
659
+ try {
660
+ const res = await xiaohongshuPublishAction({
661
+ page,
662
+ params,
663
+ tmpCachePath: this.getTmpPath(),
664
+ debug: !!this.debug
665
+ });
666
+ return res;
667
+ } catch (error) {
668
+ this.logger.error("\u5C0F\u7EA2\u4E66\u53D1\u5E03\u5931\u8D25", error);
669
+ throw error;
670
+ }
565
671
  }
566
672
  }
567
673
  const RpaTask = (params) => {
674
+ const logger = Logger.getInstance(params.cachePath);
568
675
  const packageManager = new PackageManager({
569
676
  packageName: packageJson.name,
570
677
  cacheDir: params.cachePath
571
678
  });
572
679
  const localPackge = packageManager.getPlugin(packageJson.name);
573
680
  if (localPackge?.LocalAutomateTask && localPackge?.version && semver.gt(localPackge.version, packageJson.version)) {
681
+ logger.info(`\u4F7F\u7528\u8FDC\u7A0B\u7684\u65B0\u7248\u672C\uFF0C\u7248\u672C\u53F7\u4E3A\uFF1A${localPackge.version}`);
574
682
  return new localPackge.LocalAutomateTask(params);
575
683
  }
576
684
  return new LocalAutomateTask(params);
package/dist/index.d.cts CHANGED
@@ -1,4 +1,3 @@
1
- import { ElectronApplication } from 'playwright';
2
1
  import { CookiesSetDetails } from 'electron';
3
2
 
4
3
  type CookieMap = CookiesSetDetails[];
@@ -44,16 +43,37 @@ interface TaskParams {
44
43
  cachePath: string;
45
44
  }
46
45
  declare class LocalAutomateTask implements TaskParams {
46
+ private logger;
47
47
  cachePath: string;
48
48
  debug: boolean;
49
- appWhenReady: Promise<ElectronApplication>;
50
- playwrightPackage: Promise<unknown>;
51
- constructor(params: TaskParams);
49
+ private playwrightPackage;
50
+ private electronPackage;
51
+ private _electronApp;
52
+ constructor({ cachePath, debug }: TaskParams);
52
53
  /**
53
54
  * 安装 playwright
54
55
  * @returns
55
56
  */
56
- installPlaywright(): Promise<any>;
57
+ private installPlaywright;
58
+ /**
59
+ * 安装 electron
60
+ * @returns
61
+ */
62
+ private installElectron;
63
+ /**
64
+ * 启动 Electron
65
+ * @returns
66
+ */
67
+ private getElectronApp;
68
+ /**
69
+ * 临时文件目录
70
+ * @returns
71
+ */
72
+ private getTmpPath;
73
+ /**
74
+ * 清空临时文件
75
+ */
76
+ private clearTmpPath;
57
77
  /**
58
78
  * 关闭 playwright 启动的 electron 客户端
59
79
  * @returns
package/dist/index.d.mts CHANGED
@@ -1,4 +1,3 @@
1
- import { ElectronApplication } from 'playwright';
2
1
  import { CookiesSetDetails } from 'electron';
3
2
 
4
3
  type CookieMap = CookiesSetDetails[];
@@ -44,16 +43,37 @@ interface TaskParams {
44
43
  cachePath: string;
45
44
  }
46
45
  declare class LocalAutomateTask implements TaskParams {
46
+ private logger;
47
47
  cachePath: string;
48
48
  debug: boolean;
49
- appWhenReady: Promise<ElectronApplication>;
50
- playwrightPackage: Promise<unknown>;
51
- constructor(params: TaskParams);
49
+ private playwrightPackage;
50
+ private electronPackage;
51
+ private _electronApp;
52
+ constructor({ cachePath, debug }: TaskParams);
52
53
  /**
53
54
  * 安装 playwright
54
55
  * @returns
55
56
  */
56
- installPlaywright(): Promise<any>;
57
+ private installPlaywright;
58
+ /**
59
+ * 安装 electron
60
+ * @returns
61
+ */
62
+ private installElectron;
63
+ /**
64
+ * 启动 Electron
65
+ * @returns
66
+ */
67
+ private getElectronApp;
68
+ /**
69
+ * 临时文件目录
70
+ * @returns
71
+ */
72
+ private getTmpPath;
73
+ /**
74
+ * 清空临时文件
75
+ */
76
+ private clearTmpPath;
57
77
  /**
58
78
  * 关闭 playwright 启动的 electron 客户端
59
79
  * @returns
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { ElectronApplication } from 'playwright';
2
1
  import { CookiesSetDetails } from 'electron';
3
2
 
4
3
  type CookieMap = CookiesSetDetails[];
@@ -44,16 +43,37 @@ interface TaskParams {
44
43
  cachePath: string;
45
44
  }
46
45
  declare class LocalAutomateTask implements TaskParams {
46
+ private logger;
47
47
  cachePath: string;
48
48
  debug: boolean;
49
- appWhenReady: Promise<ElectronApplication>;
50
- playwrightPackage: Promise<unknown>;
51
- constructor(params: TaskParams);
49
+ private playwrightPackage;
50
+ private electronPackage;
51
+ private _electronApp;
52
+ constructor({ cachePath, debug }: TaskParams);
52
53
  /**
53
54
  * 安装 playwright
54
55
  * @returns
55
56
  */
56
- installPlaywright(): Promise<any>;
57
+ private installPlaywright;
58
+ /**
59
+ * 安装 electron
60
+ * @returns
61
+ */
62
+ private installElectron;
63
+ /**
64
+ * 启动 Electron
65
+ * @returns
66
+ */
67
+ private getElectronApp;
68
+ /**
69
+ * 临时文件目录
70
+ * @returns
71
+ */
72
+ private getTmpPath;
73
+ /**
74
+ * 清空临时文件
75
+ */
76
+ private clearTmpPath;
57
77
  /**
58
78
  * 关闭 playwright 启动的 electron 客户端
59
79
  * @returns
package/dist/index.mjs CHANGED
@@ -1,13 +1,12 @@
1
- import path from 'node:path';
2
1
  import fs from 'node:fs';
2
+ import path from 'node:path';
3
3
  import https from 'node:https';
4
- import { app } from 'electron';
5
- import { spawn } from 'node:child_process';
4
+ import log from 'loglevel';
5
+ import npminstall from 'npminstall';
6
6
 
7
7
  const name = "@iflyrpa/playwright";
8
8
  const type = "module";
9
- const version$1 = "1.0.9";
10
- const description = "";
9
+ const version$1 = "1.0.11";
11
10
  const main = "./dist/index.cjs";
12
11
  const module = "./dist/index.mjs";
13
12
  const types = "./dist/index.d.ts";
@@ -16,14 +15,14 @@ const scripts = {
16
15
  dev: "unbuild --stub",
17
16
  start: "esno src/index.ts"
18
17
  };
19
- const keywords = [
20
- ];
21
18
  const author = "bijinfeng";
22
19
  const license = "ISC";
23
20
  const files = [
24
21
  "dist"
25
22
  ];
26
23
  const dependencies = {
24
+ loglevel: "^1.9.2",
25
+ npminstall: "^7.12.0",
27
26
  playwright: "^1.46.1"
28
27
  };
29
28
  const peerDependencies = {
@@ -38,12 +37,10 @@ const packageJson = {
38
37
  name: name,
39
38
  type: type,
40
39
  version: version$1,
41
- description: description,
42
40
  main: main,
43
41
  module: module,
44
42
  types: types,
45
43
  scripts: scripts,
46
- keywords: keywords,
47
44
  author: author,
48
45
  license: license,
49
46
  files: files,
@@ -211,7 +208,7 @@ const visibleRangeTexts = {
211
208
  private: "\u79C1\u5BC6"
212
209
  };
213
210
  const xiaohongshuPublishAction = async (props) => {
214
- const { page, cachePath, params } = props;
211
+ const { page, tmpCachePath, params } = props;
215
212
  const selectAddress = async (selector, address) => {
216
213
  const instance = typeof selector === "string" ? page.locator(selector) : selector;
217
214
  await instance.click();
@@ -234,11 +231,13 @@ const xiaohongshuPublishAction = async (props) => {
234
231
  await page.locator("#content-area .menu-container .publish-video a").click().catch(() => {
235
232
  throw new Error("\u672A\u627E\u5230\u53D1\u5E03\u7B14\u8BB0\u6309\u94AE");
236
233
  });
237
- await page.locator(".creator-container .header .title").filter({ hasText: /^上传图文$/ }).click();
234
+ await page.locator(".creator-container .header .title").filter({ hasText: /^上传图文$/ }).click().catch(() => {
235
+ throw new Error("\u672A\u627E\u5230\u4E0A\u4F20\u56FE\u6587\u6309\u94AE");
236
+ });
238
237
  const images = await Promise.all(
239
238
  params.banners.map((url) => {
240
239
  const fileName = getFilenameFromUrl(url);
241
- return downloadImage(url, path.join(cachePath, "image-tmp", fileName));
240
+ return downloadImage(url, path.join(tmpCachePath, fileName));
242
241
  })
243
242
  );
244
243
  const fileChooserPromise = page.waitForEvent("filechooser");
@@ -328,6 +327,64 @@ const xiaohongshuPublishAction = async (props) => {
328
327
  return response;
329
328
  };
330
329
 
330
+ var __defProp$2 = Object.defineProperty;
331
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
332
+ var __publicField$2 = (obj, key, value) => {
333
+ __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
334
+ return value;
335
+ };
336
+ const _Logger = class _Logger {
337
+ constructor(cachePath) {
338
+ __publicField$2(this, "stream");
339
+ if (_Logger.instance) {
340
+ return _Logger.instance;
341
+ }
342
+ const logFile = path.join(cachePath, "rpa.log");
343
+ this.stream = fs.createWriteStream(logFile, { flags: "a" });
344
+ log.setLevel("debug");
345
+ log.methodFactory = (methodName) => {
346
+ return (message) => {
347
+ this.stream.write(
348
+ `[${( new Date()).toISOString()}] ${methodName.toUpperCase()}: ${message}
349
+ `
350
+ );
351
+ };
352
+ };
353
+ log.setLevel(log.getLevel());
354
+ _Logger.instance = this;
355
+ }
356
+ static getInstance(cachePath) {
357
+ if (!_Logger.instance) {
358
+ _Logger.instance = new _Logger(cachePath);
359
+ }
360
+ return _Logger.instance;
361
+ }
362
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
363
+ debug(...msg) {
364
+ log.debug(...msg);
365
+ }
366
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
367
+ info(...msg) {
368
+ log.info(...msg);
369
+ }
370
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
371
+ warn(...msg) {
372
+ log.warn(...msg);
373
+ }
374
+ error(prefix, error) {
375
+ log.error(prefix);
376
+ if (error instanceof Error) {
377
+ log.error(`${error.message}
378
+ Error stack: ${error.stack}`);
379
+ }
380
+ }
381
+ close() {
382
+ this.stream.end();
383
+ }
384
+ };
385
+ __publicField$2(_Logger, "instance", null);
386
+ let Logger = _Logger;
387
+
331
388
  const template = `
332
389
  const { app } = require("electron");
333
390
 
@@ -344,15 +401,28 @@ const generateFile = async (dir) => {
344
401
  }
345
402
  return filePath;
346
403
  };
347
- const createElectronApp = async (cachePath, playwrightPackage) => {
348
- const playwright = await playwrightPackage;
349
- const mainPath = await generateFile(cachePath);
350
- const electronApp = await playwright._electron.launch({
351
- executablePath: app.getPath("exe"),
352
- // 获取 Electron 可执行文件的路径
353
- args: [mainPath]
354
- });
355
- return electronApp;
404
+ const createElectronApp = async (cachePath, playwright) => {
405
+ const logger = Logger.getInstance(cachePath);
406
+ try {
407
+ const executablePath = path.join(
408
+ cachePath,
409
+ "node_modules",
410
+ ".bin",
411
+ "electron"
412
+ );
413
+ const mainPath = await generateFile(cachePath);
414
+ const electronApp = await playwright._electron.launch({
415
+ executablePath,
416
+ // 获取 Electron 可执行文件的路径
417
+ args: [mainPath],
418
+ cwd: cachePath
419
+ });
420
+ logger.info(`electron \u542F\u52A8\u6210\u529F\uFF1A${executablePath} ${mainPath}`);
421
+ return electronApp;
422
+ } catch (error) {
423
+ logger.error("electron \u542F\u52A8\u5931\u8D25\uFF1A", error);
424
+ throw error;
425
+ }
356
426
  };
357
427
 
358
428
  var __defProp$1 = Object.defineProperty;
@@ -361,6 +431,7 @@ var __publicField$1 = (obj, key, value) => {
361
431
  __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
362
432
  return value;
363
433
  };
434
+ const NPM_REGISTRY = "https://registry.npmmirror.com/";
364
435
  class PackageManager {
365
436
  constructor(params) {
366
437
  __publicField$1(this, "cacheDir");
@@ -368,47 +439,23 @@ class PackageManager {
368
439
  __publicField$1(this, "forceUpdate");
369
440
  // 是否强制更新
370
441
  __publicField$1(this, "initPromise");
442
+ __publicField$1(this, "logger");
443
+ this.logger = Logger.getInstance(params.cacheDir);
371
444
  this.cacheDir = params.cacheDir;
372
445
  this.forceUpdate = params.forceUpdate || true;
446
+ this.initCacheProject();
373
447
  this.initPromise = this.init(params.packageName, params.packageVersion);
374
448
  }
375
- // 在子线程执行 npm 命令
376
- execCommand(cmd, modules, where = this.cacheDir) {
377
- return new Promise((resolve) => {
378
- const args = [cmd].concat(modules).concat("--color=always").concat("--save");
379
- try {
380
- const npm = spawn("npm", args, { cwd: where });
381
- let output = "";
382
- npm.stdout?.on("data", (data) => {
383
- output += data;
384
- }).pipe(process.stdout);
385
- npm.stderr?.on("data", (data) => {
386
- output += data;
387
- }).pipe(process.stderr);
388
- npm.on("close", (code) => {
389
- if (!code) {
390
- resolve({ code: 0, data: output });
391
- } else {
392
- resolve({ code, data: output });
393
- }
394
- });
395
- npm.on("error", (err) => {
396
- console.error("NPM is not installed");
397
- console.error(err);
398
- });
399
- } catch (error) {
400
- console.error(error);
401
- }
402
- });
403
- }
404
449
  // 构建 package.json 文件,有了该文件后,依赖可以安装在该目录下,避免污染全局环境
405
450
  initCacheProject() {
406
451
  const packagePath = path.join(this.cacheDir, "package.json");
407
452
  if (!fs.existsSync(packagePath)) {
453
+ this.logger.info("package.json \u4E0D\u5B58\u5728\uFF0C\u6DFB\u52A0\u8BE5\u6587\u4EF6");
408
454
  const pkg = {
409
455
  name: "rpa-plugins",
410
456
  description: "rpa-plugins",
411
- license: "MIT"
457
+ license: "MIT",
458
+ main: "./src/main.js"
412
459
  };
413
460
  ensureFileSync(packagePath);
414
461
  fs.writeFileSync(packagePath, JSON.stringify(pkg), "utf8");
@@ -428,6 +475,7 @@ class PackageManager {
428
475
  try {
429
476
  return require(path.join(pluginDir, module));
430
477
  } catch (error) {
478
+ this.logger.warn(`${module}\u672C\u5730\u4F9D\u8D56\u4E0D\u5B58\u5728\uFF0C${pluginDir}`);
431
479
  return null;
432
480
  }
433
481
  }
@@ -437,28 +485,30 @@ class PackageManager {
437
485
  }
438
486
  // 安装依赖
439
487
  install(module, version) {
440
- const moduleName = version ? `${module}@${version}` : module;
441
- return this.execCommand("install", [moduleName]);
442
- }
443
- // 更新依赖
444
- update(module) {
445
- return this.execCommand("update", [module]);
488
+ return npminstall({
489
+ root: this.cacheDir,
490
+ pkgs: [{ name: module, version }],
491
+ registry: NPM_REGISTRY
492
+ });
446
493
  }
447
494
  async init(name, version) {
448
- this.initCacheProject();
449
495
  const plugin = this.getPlugin(path.join(name, "package.json"));
450
- if (!plugin) {
451
- await this.install(name, version);
452
- } else if (this.forceUpdate) {
453
- const pkInfo = await this.getPluginInfo(name);
454
- if (!pkInfo)
455
- return;
456
- const hasNewVersion = semver.gt(pkInfo.version, plugin.version);
457
- if (hasNewVersion) {
458
- await this.install(name, pkInfo.version);
496
+ try {
497
+ if (!plugin) {
498
+ await this.install(name, version);
499
+ } else if (this.forceUpdate) {
500
+ const pkInfo = await this.getPluginInfo(name);
501
+ if (!pkInfo)
502
+ return;
503
+ const hasNewVersion = semver.gt(pkInfo.version, plugin.version);
504
+ if (hasNewVersion) {
505
+ await this.install(name, pkInfo.version);
506
+ }
459
507
  }
508
+ this.logger.info(`${name} package manager init done!`);
509
+ } catch (error) {
510
+ this.logger.error(`${name} package manager init error`, error);
460
511
  }
461
- console.log("Package manager init done!");
462
512
  }
463
513
  }
464
514
 
@@ -469,19 +519,20 @@ var __publicField = (obj, key, value) => {
469
519
  return value;
470
520
  };
471
521
  const PLAYWRIGHT_VERSION = "1.46.1";
522
+ const ELECTRON_VERSION = "22.3.27";
472
523
  class LocalAutomateTask {
473
- constructor(params) {
524
+ constructor({ cachePath, debug }) {
525
+ __publicField(this, "logger");
474
526
  __publicField(this, "cachePath");
475
527
  __publicField(this, "debug");
476
- __publicField(this, "appWhenReady");
477
528
  __publicField(this, "playwrightPackage");
478
- this.cachePath = params.cachePath;
479
- this.debug = params.debug || false;
529
+ __publicField(this, "electronPackage");
530
+ __publicField(this, "_electronApp", null);
531
+ this.cachePath = cachePath;
532
+ this.debug = debug || false;
533
+ this.logger = Logger.getInstance(cachePath);
480
534
  this.playwrightPackage = this.installPlaywright();
481
- this.appWhenReady = createElectronApp(
482
- params.cachePath,
483
- this.playwrightPackage
484
- );
535
+ this.electronPackage = this.installElectron();
485
536
  }
486
537
  /**
487
538
  * 安装 playwright
@@ -495,19 +546,67 @@ class LocalAutomateTask {
495
546
  });
496
547
  return playwrightPackageManager.getPluginAfterInit("playwright");
497
548
  }
549
+ /**
550
+ * 安装 electron
551
+ * @returns
552
+ */
553
+ installElectron() {
554
+ const playwrightPackageManager = new PackageManager({
555
+ packageName: "electron",
556
+ packageVersion: ELECTRON_VERSION,
557
+ cacheDir: this.cachePath
558
+ });
559
+ return playwrightPackageManager.getPluginAfterInit("electron");
560
+ }
561
+ /**
562
+ * 启动 Electron
563
+ * @returns
564
+ */
565
+ async getElectronApp() {
566
+ if (!this._electronApp) {
567
+ const [playwright] = await Promise.all([
568
+ this.playwrightPackage,
569
+ this.electronPackage
570
+ ]);
571
+ this._electronApp = await createElectronApp(this.cachePath, playwright);
572
+ }
573
+ return this._electronApp;
574
+ }
575
+ /**
576
+ * 临时文件目录
577
+ * @returns
578
+ */
579
+ getTmpPath() {
580
+ return path.join(this.cachePath, "tmp");
581
+ }
582
+ /**
583
+ * 清空临时文件
584
+ */
585
+ clearTmpPath() {
586
+ const tmpPath = this.getTmpPath();
587
+ return fs.rm(tmpPath, { recursive: true, force: true }, (err) => {
588
+ if (err) {
589
+ this.logger.error("\u5220\u9664\u4E34\u65F6\u6587\u4EF6\u5931\u8D25\uFF0C", err);
590
+ } else {
591
+ this.logger.info("\u5220\u9664\u4E34\u65F6\u6587\u4EF6\u6210\u529F");
592
+ }
593
+ });
594
+ }
498
595
  /**
499
596
  * 关闭 playwright 启动的 electron 客户端
500
597
  * @returns
501
598
  */
502
599
  async close() {
503
- const electronApp = await this.appWhenReady;
600
+ this.logger.close();
601
+ this.clearTmpPath();
602
+ const electronApp = await this.getElectronApp();
504
603
  return electronApp.close();
505
604
  }
506
605
  /**
507
606
  * 小红书自动化发布
508
607
  */
509
608
  async xiaohongshuPublish(params) {
510
- const electronApp = await this.appWhenReady;
609
+ const electronApp = await this.getElectronApp();
511
610
  const commonCookies = {
512
611
  path: "/",
513
612
  sameSite: "lax",
@@ -547,22 +646,29 @@ class LocalAutomateTask {
547
646
  { pageParams }
548
647
  )
549
648
  ]);
550
- const res = await xiaohongshuPublishAction({
551
- page,
552
- params,
553
- cachePath: this.cachePath,
554
- debug: !!this.debug
555
- });
556
- return res;
649
+ try {
650
+ const res = await xiaohongshuPublishAction({
651
+ page,
652
+ params,
653
+ tmpCachePath: this.getTmpPath(),
654
+ debug: !!this.debug
655
+ });
656
+ return res;
657
+ } catch (error) {
658
+ this.logger.error("\u5C0F\u7EA2\u4E66\u53D1\u5E03\u5931\u8D25", error);
659
+ throw error;
660
+ }
557
661
  }
558
662
  }
559
663
  const RpaTask = (params) => {
664
+ const logger = Logger.getInstance(params.cachePath);
560
665
  const packageManager = new PackageManager({
561
666
  packageName: packageJson.name,
562
667
  cacheDir: params.cachePath
563
668
  });
564
669
  const localPackge = packageManager.getPlugin(packageJson.name);
565
670
  if (localPackge?.LocalAutomateTask && localPackge?.version && semver.gt(localPackge.version, packageJson.version)) {
671
+ logger.info(`\u4F7F\u7528\u8FDC\u7A0B\u7684\u65B0\u7248\u672C\uFF0C\u7248\u672C\u53F7\u4E3A\uFF1A${localPackge.version}`);
566
672
  return new localPackge.LocalAutomateTask(params);
567
673
  }
568
674
  return new LocalAutomateTask(params);
package/package.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "name": "@iflyrpa/playwright",
3
3
  "type": "module",
4
- "version": "1.0.9",
5
- "description": "",
4
+ "version": "1.0.11",
6
5
  "main": "./dist/index.cjs",
7
6
  "module": "./dist/index.mjs",
8
7
  "types": "./dist/index.d.ts",
@@ -11,11 +10,12 @@
11
10
  "dev": "unbuild --stub",
12
11
  "start": "esno src/index.ts"
13
12
  },
14
- "keywords": [],
15
13
  "author": "bijinfeng",
16
14
  "license": "ISC",
17
15
  "files": ["dist"],
18
16
  "dependencies": {
17
+ "loglevel": "^1.9.2",
18
+ "npminstall": "^7.12.0",
19
19
  "playwright": "^1.46.1"
20
20
  },
21
21
  "peerDependencies": {