@iflyrpa/playwright 1.0.8 → 1.0.10

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,23 +1,21 @@
1
1
  'use strict';
2
2
 
3
- const semver = require('semver');
4
3
  const path = require('node:path');
5
- const axios = require('axios');
6
- const fs = require('fs-extra');
7
- const electron = require('electron');
8
- const spawn = require('cross-spawn');
4
+ const fs = require('node:fs');
5
+ const https = require('node:https');
6
+ const log = require('loglevel');
7
+ const node_child_process = require('node:child_process');
9
8
 
10
9
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
11
10
 
12
- const semver__default = /*#__PURE__*/_interopDefaultCompat(semver);
13
11
  const path__default = /*#__PURE__*/_interopDefaultCompat(path);
14
- const axios__default = /*#__PURE__*/_interopDefaultCompat(axios);
15
12
  const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
16
- const spawn__default = /*#__PURE__*/_interopDefaultCompat(spawn);
13
+ const https__default = /*#__PURE__*/_interopDefaultCompat(https);
14
+ const log__default = /*#__PURE__*/_interopDefaultCompat(log);
17
15
 
18
16
  const name = "@iflyrpa/playwright";
19
17
  const type = "module";
20
- const version$1 = "1.0.8";
18
+ const version$1 = "1.0.10";
21
19
  const description = "";
22
20
  const main = "./dist/index.cjs";
23
21
  const module$1 = "./dist/index.mjs";
@@ -35,23 +33,14 @@ const files = [
35
33
  "dist"
36
34
  ];
37
35
  const dependencies = {
38
- axios: "^1.7.4",
39
- "cross-spawn": "^7.0.3",
40
- deepmerge: "^4.3.1",
41
- "fs-extra": "^11.2.0",
42
- playwright: "^1.46.1",
43
- semver: "^7.6.3"
36
+ loglevel: "^1.9.2",
37
+ playwright: "^1.46.1"
44
38
  };
45
39
  const peerDependencies = {
46
40
  electron: "*"
47
41
  };
48
42
  const devDependencies = {
49
- "@types/cross-spawn": "^6.0.6",
50
- "@types/fs-extra": "^11.0.4",
51
- "@types/semver": "^7.5.8",
52
- bumpp: "^9.5.2",
53
43
  esno: "^4.7.0",
54
- "ts-node": "^10.9.2",
55
44
  typescript: "^5.5.2",
56
45
  unbuild: "^2.0.0"
57
46
  };
@@ -73,25 +62,159 @@ const packageJson = {
73
62
  devDependencies: devDependencies
74
63
  };
75
64
 
76
- async function downloadImage(url, path2) {
77
- const response = await axios__default({
78
- url,
79
- method: "GET",
80
- responseType: "stream"
81
- // 重要:设置响应类型为 'stream'
82
- });
83
- await fs__default.ensureFile(path2);
84
- const writer = fs__default.createWriteStream(path2);
85
- response.data.pipe(writer);
65
+ async function downloadImage(url, savePath) {
66
+ await ensureFile(savePath);
86
67
  return new Promise((resolve, reject) => {
87
- writer.on("finish", () => resolve(path2));
88
- writer.on("error", reject);
68
+ https__default.get(url, (response) => {
69
+ if (response.statusCode === 200) {
70
+ const fileStream = fs__default.createWriteStream(savePath);
71
+ response.pipe(fileStream);
72
+ fileStream.on("finish", () => {
73
+ fileStream.close();
74
+ console.log("\u4E0B\u8F7D\u5B8C\u6210\uFF0C\u6587\u4EF6\u5DF2\u4FDD\u5B58\u81F3:", savePath);
75
+ resolve(savePath);
76
+ });
77
+ } else {
78
+ console.log("\u56FE\u7247\u4E0B\u8F7D\u5931\u8D25:", response.statusCode);
79
+ response.resume();
80
+ reject();
81
+ }
82
+ }).on("error", (error) => {
83
+ console.error("\u8BF7\u6C42\u56FE\u7247\u65F6\u53D1\u751F\u9519\u8BEF:", error.message);
84
+ reject();
85
+ });
89
86
  });
90
87
  }
91
88
  function getFilenameFromUrl(imageUrl) {
92
89
  const parsedUrl = new URL(imageUrl);
93
90
  return path__default.basename(parsedUrl.pathname);
94
91
  }
92
+ function pathExists(path2) {
93
+ return new Promise((resolve) => {
94
+ fs__default.stat(path2, (err) => {
95
+ resolve(!err);
96
+ });
97
+ });
98
+ }
99
+ function ensureFile(filePath) {
100
+ return new Promise((resolve, reject) => {
101
+ const dirPath = path__default.dirname(filePath);
102
+ fs__default.stat(dirPath, (err, stats) => {
103
+ if (err) {
104
+ if (err.code === "ENOENT") {
105
+ fs__default.mkdir(dirPath, { recursive: true }, (err2) => {
106
+ if (err2) {
107
+ return reject(err2);
108
+ }
109
+ createFile();
110
+ });
111
+ } else {
112
+ return reject(err);
113
+ }
114
+ } else if (stats.isDirectory()) {
115
+ checkFile();
116
+ } else {
117
+ reject(new Error(`${dirPath} is not a directory`));
118
+ }
119
+ });
120
+ function checkFile() {
121
+ fs__default.stat(filePath, (err, stats) => {
122
+ if (err) {
123
+ if (err.code === "ENOENT") {
124
+ createFile();
125
+ } else {
126
+ reject(err);
127
+ }
128
+ } else if (stats.isFile()) {
129
+ resolve();
130
+ } else {
131
+ reject(new Error(`${filePath} is not a file`));
132
+ }
133
+ });
134
+ }
135
+ function createFile() {
136
+ fs__default.writeFile(filePath, "", (err) => {
137
+ if (err) {
138
+ reject(err);
139
+ } else {
140
+ resolve();
141
+ }
142
+ });
143
+ }
144
+ });
145
+ }
146
+ function ensureFileSync(filePath) {
147
+ const dirPath = path__default.dirname(filePath);
148
+ try {
149
+ if (!fs__default.existsSync(dirPath)) {
150
+ fs__default.mkdirSync(dirPath, { recursive: true });
151
+ }
152
+ } catch (err) {
153
+ if (err instanceof Error) {
154
+ throw new Error(`Error creating directory: ${err.message}`);
155
+ }
156
+ }
157
+ try {
158
+ if (!fs__default.existsSync(filePath)) {
159
+ fs__default.writeFileSync(filePath, "");
160
+ }
161
+ } catch (err) {
162
+ if (err instanceof Error) {
163
+ throw new Error(`Error creating file: ${err.message}`);
164
+ }
165
+ }
166
+ }
167
+ function writeFile(filePath, data) {
168
+ return new Promise((resolve, reject) => {
169
+ fs__default.writeFile(filePath, data, (err) => {
170
+ if (err) {
171
+ console.error("Error writing file:", err);
172
+ reject();
173
+ } else {
174
+ console.log("File written successfully");
175
+ resolve();
176
+ }
177
+ });
178
+ });
179
+ }
180
+ function compareVersions(v1, v2) {
181
+ const parts1 = v1.split(".").map(Number);
182
+ const parts2 = v2.split(".").map(Number);
183
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
184
+ const num1 = i < parts1.length ? parts1[i] : 0;
185
+ const num2 = i < parts2.length ? parts2[i] : 0;
186
+ if (num1 > num2)
187
+ return 1;
188
+ if (num1 < num2)
189
+ return -1;
190
+ }
191
+ return 0;
192
+ }
193
+ const semver = {
194
+ gt: (v1, v2) => compareVersions(v1, v2) === 1
195
+ };
196
+ function fetchJSON(url) {
197
+ return new Promise((resolve, reject) => {
198
+ https__default.get(url, (res) => {
199
+ let data = "";
200
+ res.on("data", (chunk) => {
201
+ data += chunk;
202
+ });
203
+ res.on("end", () => {
204
+ try {
205
+ const parsedData = JSON.parse(data);
206
+ resolve(parsedData);
207
+ } catch (e) {
208
+ if (e instanceof Error) {
209
+ reject(new Error(`Error parsing JSON: ${e.message}`));
210
+ }
211
+ }
212
+ });
213
+ }).on("error", (err) => {
214
+ reject(new Error(`Request failed: ${err.message}`));
215
+ });
216
+ });
217
+ }
95
218
 
96
219
  const visibleRangeTexts = {
97
220
  public: "\u516C\u5F00",
@@ -215,6 +338,65 @@ const xiaohongshuPublishAction = async (props) => {
215
338
  return response;
216
339
  };
217
340
 
341
+ var __defProp$2 = Object.defineProperty;
342
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
343
+ var __publicField$2 = (obj, key, value) => {
344
+ __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
345
+ return value;
346
+ };
347
+ const _Logger = class _Logger {
348
+ constructor(cachePath) {
349
+ __publicField$2(this, "stream");
350
+ if (_Logger.instance) {
351
+ return _Logger.instance;
352
+ }
353
+ const logFile = path__default.join(cachePath, "rpa.log");
354
+ this.stream = fs__default.createWriteStream(logFile, { flags: "a" });
355
+ log__default.setLevel("debug");
356
+ log__default.methodFactory = (methodName) => {
357
+ return (message) => {
358
+ this.stream.write(
359
+ `[${( new Date()).toISOString()}] ${methodName.toUpperCase()}: ${message}
360
+ `
361
+ );
362
+ };
363
+ };
364
+ log__default.setLevel(log__default.getLevel());
365
+ _Logger.instance = this;
366
+ }
367
+ static getInstance(cachePath) {
368
+ if (!_Logger.instance) {
369
+ _Logger.instance = new _Logger(cachePath);
370
+ }
371
+ return _Logger.instance;
372
+ }
373
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
374
+ debug(...msg) {
375
+ log__default.debug(...msg);
376
+ }
377
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
378
+ info(...msg) {
379
+ log__default.info(...msg);
380
+ }
381
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
382
+ warn(...msg) {
383
+ log__default.warn(...msg);
384
+ }
385
+ error(prefix, error) {
386
+ let errorMessage = error;
387
+ if (error instanceof Error) {
388
+ errorMessage = `${error.message}
389
+ Error stack: ${error.stack}`;
390
+ }
391
+ log__default.error(prefix, errorMessage);
392
+ }
393
+ close() {
394
+ this.stream.end();
395
+ }
396
+ };
397
+ __publicField$2(_Logger, "instance", null);
398
+ let Logger = _Logger;
399
+
218
400
  const template = `
219
401
  const { app } = require("electron");
220
402
 
@@ -224,22 +406,35 @@ app.on("window-all-closed", (e) => e.preventDefault());
224
406
  `;
225
407
  const generateFile = async (dir) => {
226
408
  const filePath = path__default.join(dir, "src", "main.js");
227
- const pathExists = await fs__default.pathExists(filePath);
228
- if (!pathExists) {
229
- await fs__default.ensureFile(filePath);
230
- await fs__default.writeFile(filePath, template);
409
+ const isPathExists = await pathExists(filePath);
410
+ if (!isPathExists) {
411
+ await ensureFile(filePath);
412
+ await writeFile(filePath, template);
231
413
  }
232
414
  return filePath;
233
415
  };
234
416
  const createElectronApp = async (cachePath, playwrightPackage) => {
235
- const playwright = await playwrightPackage;
236
- const mainPath = await generateFile(cachePath);
237
- const electronApp = await playwright._electron.launch({
238
- executablePath: electron.app.getPath("exe"),
239
- // 获取 Electron 可执行文件的路径
240
- args: [mainPath]
241
- });
242
- return electronApp;
417
+ const logger = Logger.getInstance(cachePath);
418
+ try {
419
+ const executablePath = path__default.join(
420
+ cachePath,
421
+ "node_modules",
422
+ ".bin",
423
+ "electron"
424
+ );
425
+ const playwright = await playwrightPackage;
426
+ const mainPath = await generateFile(cachePath);
427
+ const electronApp = await playwright._electron.launch({
428
+ executablePath,
429
+ // 获取 Electron 可执行文件的路径
430
+ args: [mainPath],
431
+ cwd: cachePath
432
+ });
433
+ logger.info(`electron \u542F\u52A8\u6210\u529F\uFF1A${executablePath} ${mainPath}`);
434
+ return electronApp;
435
+ } catch (error) {
436
+ logger.error("electron \u542F\u52A8\u5931\u8D25\uFF1A", error);
437
+ }
243
438
  };
244
439
 
245
440
  var __defProp$1 = Object.defineProperty;
@@ -255,8 +450,11 @@ class PackageManager {
255
450
  __publicField$1(this, "forceUpdate");
256
451
  // 是否强制更新
257
452
  __publicField$1(this, "initPromise");
453
+ __publicField$1(this, "logger");
454
+ this.logger = Logger.getInstance(params.cacheDir);
258
455
  this.cacheDir = params.cacheDir;
259
456
  this.forceUpdate = params.forceUpdate || true;
457
+ this.initCacheProject();
260
458
  this.initPromise = this.init(params.packageName, params.packageVersion);
261
459
  }
262
460
  // 在子线程执行 npm 命令
@@ -264,7 +462,8 @@ class PackageManager {
264
462
  return new Promise((resolve) => {
265
463
  const args = [cmd].concat(modules).concat("--color=always").concat("--save");
266
464
  try {
267
- const npm = spawn__default("npm", args, { cwd: where });
465
+ const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
466
+ const npm = node_child_process.spawn(npmCmd, args, { cwd: where });
268
467
  let output = "";
269
468
  npm.stdout?.on("data", (data) => {
270
469
  output += data;
@@ -284,6 +483,7 @@ class PackageManager {
284
483
  console.error(err);
285
484
  });
286
485
  } catch (error) {
486
+ this.logger.error(`npm ${args.join(" ")}: `, error);
287
487
  console.error(error);
288
488
  }
289
489
  });
@@ -292,21 +492,23 @@ class PackageManager {
292
492
  initCacheProject() {
293
493
  const packagePath = path__default.join(this.cacheDir, "package.json");
294
494
  if (!fs__default.existsSync(packagePath)) {
495
+ this.logger.info("package.json \u4E0D\u5B58\u5728\uFF0C\u6DFB\u52A0\u8BE5\u6587\u4EF6");
295
496
  const pkg = {
296
497
  name: "rpa-plugins",
297
498
  description: "rpa-plugins",
298
- license: "MIT"
499
+ license: "MIT",
500
+ main: "./src/main.js"
299
501
  };
300
- fs__default.ensureFileSync(packagePath);
502
+ ensureFileSync(packagePath);
301
503
  fs__default.writeFileSync(packagePath, JSON.stringify(pkg), "utf8");
302
504
  }
303
505
  }
304
506
  // 获取依赖信息
305
507
  async getPluginInfo(module) {
306
- const res = await axios__default.get(
508
+ const res = await fetchJSON(
307
509
  `https://registry.npmjs.com/-/v1/search?text=${module}`
308
510
  );
309
- const packages = res.data.objects;
511
+ const packages = res.objects;
310
512
  return packages.find((it) => it.package.name === module)?.package;
311
513
  }
312
514
  // 查询本地安装的依赖
@@ -315,6 +517,7 @@ class PackageManager {
315
517
  try {
316
518
  return require(path__default.join(pluginDir, module));
317
519
  } catch (error) {
520
+ this.logger.warn(`${module}\u672C\u5730\u4F9D\u8D56\u4E0D\u5B58\u5728\uFF0C${pluginDir}`);
318
521
  return null;
319
522
  }
320
523
  }
@@ -325,27 +528,26 @@ class PackageManager {
325
528
  // 安装依赖
326
529
  install(module, version) {
327
530
  const moduleName = version ? `${module}@${version}` : module;
328
- return this.execCommand("install", [moduleName]);
531
+ return this.execCommand("install", [moduleName, "--save"]);
329
532
  }
330
533
  // 更新依赖
331
534
  update(module) {
332
535
  return this.execCommand("update", [module]);
333
536
  }
334
537
  async init(name, version) {
335
- this.initCacheProject();
336
538
  const plugin = this.getPlugin(path__default.join(name, "package.json"));
337
539
  if (!plugin) {
338
540
  await this.install(name, version);
339
541
  } else if (this.forceUpdate) {
340
542
  const pkInfo = await this.getPluginInfo(name);
341
543
  if (!pkInfo)
342
- throw new Error("\u4F9D\u8D56\u4FE1\u606F\u83B7\u53D6\u5931\u8D25");
343
- const hasNewVersion = semver__default.gt(pkInfo.version, plugin.version);
544
+ return;
545
+ const hasNewVersion = semver.gt(pkInfo.version, plugin.version);
344
546
  if (hasNewVersion) {
345
547
  await this.install(name, pkInfo.version);
346
548
  }
347
549
  }
348
- console.log("Package manager init done!");
550
+ this.logger.info(`${name} package manager init done!`);
349
551
  }
350
552
  }
351
553
 
@@ -357,18 +559,16 @@ var __publicField = (obj, key, value) => {
357
559
  };
358
560
  const PLAYWRIGHT_VERSION = "1.46.1";
359
561
  class LocalAutomateTask {
360
- constructor(params) {
562
+ constructor({ cachePath, debug }) {
361
563
  __publicField(this, "cachePath");
362
564
  __publicField(this, "debug");
363
- __publicField(this, "appWhenReady");
364
565
  __publicField(this, "playwrightPackage");
365
- this.cachePath = params.cachePath;
366
- this.debug = params.debug || false;
566
+ __publicField(this, "logger");
567
+ __publicField(this, "_electronApp", null);
568
+ this.cachePath = cachePath;
569
+ this.debug = debug || false;
570
+ this.logger = Logger.getInstance(cachePath);
367
571
  this.playwrightPackage = this.installPlaywright();
368
- this.appWhenReady = createElectronApp(
369
- params.cachePath,
370
- this.playwrightPackage
371
- );
372
572
  }
373
573
  /**
374
574
  * 安装 playwright
@@ -382,19 +582,33 @@ class LocalAutomateTask {
382
582
  });
383
583
  return playwrightPackageManager.getPluginAfterInit("playwright");
384
584
  }
585
+ /**
586
+ * 启动 Electron
587
+ * @returns
588
+ */
589
+ async getElectronApp() {
590
+ if (!this._electronApp) {
591
+ this._electronApp = await createElectronApp(
592
+ this.cachePath,
593
+ this.playwrightPackage
594
+ );
595
+ }
596
+ return this._electronApp;
597
+ }
385
598
  /**
386
599
  * 关闭 playwright 启动的 electron 客户端
387
600
  * @returns
388
601
  */
389
602
  async close() {
390
- const electronApp = await this.appWhenReady;
603
+ this.logger.close();
604
+ const electronApp = await this.getElectronApp();
391
605
  return electronApp.close();
392
606
  }
393
607
  /**
394
608
  * 小红书自动化发布
395
609
  */
396
610
  async xiaohongshuPublish(params) {
397
- const electronApp = await this.appWhenReady;
611
+ const electronApp = await this.getElectronApp();
398
612
  const commonCookies = {
399
613
  path: "/",
400
614
  sameSite: "lax",
@@ -434,22 +648,29 @@ class LocalAutomateTask {
434
648
  { pageParams }
435
649
  )
436
650
  ]);
437
- const res = await xiaohongshuPublishAction({
438
- page,
439
- params,
440
- cachePath: this.cachePath,
441
- debug: !!this.debug
442
- });
443
- return res;
651
+ try {
652
+ const res = await xiaohongshuPublishAction({
653
+ page,
654
+ params,
655
+ cachePath: this.cachePath,
656
+ debug: !!this.debug
657
+ });
658
+ return res;
659
+ } catch (error) {
660
+ this.logger.error("\u5C0F\u7EA2\u4E66\u53D1\u5E03\u5931\u8D25", error);
661
+ return error;
662
+ }
444
663
  }
445
664
  }
446
665
  const RpaTask = (params) => {
666
+ const logger = Logger.getInstance(params.cachePath);
447
667
  const packageManager = new PackageManager({
448
668
  packageName: packageJson.name,
449
669
  cacheDir: params.cachePath
450
670
  });
451
671
  const localPackge = packageManager.getPlugin(packageJson.name);
452
- if (localPackge?.LocalAutomateTask && localPackge?.version && semver__default.gt(localPackge.version, packageJson.version)) {
672
+ if (localPackge?.LocalAutomateTask && localPackge?.version && semver.gt(localPackge.version, packageJson.version)) {
673
+ logger.info(`\u4F7F\u7528\u8FDC\u7A0B\u7684\u65B0\u7248\u672C\uFF0C\u7248\u672C\u53F7\u4E3A\uFF1A${localPackge.version}`);
453
674
  return new localPackge.LocalAutomateTask(params);
454
675
  }
455
676
  return new LocalAutomateTask(params);
package/dist/index.d.cts CHANGED
@@ -1,6 +1,17 @@
1
- import { ElectronApplication } from 'playwright';
2
1
  import { CookiesSetDetails } from 'electron';
3
2
 
3
+ declare class Logger {
4
+ static instance: Logger | null;
5
+ private stream;
6
+ constructor(cachePath: string);
7
+ static getInstance(cachePath: string): Logger;
8
+ debug(...msg: any[]): void;
9
+ info(...msg: any[]): void;
10
+ warn(...msg: any[]): void;
11
+ error(prefix: string, error: unknown): void;
12
+ close(): void;
13
+ }
14
+
4
15
  type CookieMap = CookiesSetDetails[];
5
16
 
6
17
  interface FictionalRendition {
@@ -46,14 +57,20 @@ interface TaskParams {
46
57
  declare class LocalAutomateTask implements TaskParams {
47
58
  cachePath: string;
48
59
  debug: boolean;
49
- appWhenReady: Promise<ElectronApplication>;
50
60
  playwrightPackage: Promise<unknown>;
51
- constructor(params: TaskParams);
61
+ logger: Logger;
62
+ private _electronApp;
63
+ constructor({ cachePath, debug }: TaskParams);
52
64
  /**
53
65
  * 安装 playwright
54
66
  * @returns
55
67
  */
56
- installPlaywright(): Promise<any>;
68
+ private installPlaywright;
69
+ /**
70
+ * 启动 Electron
71
+ * @returns
72
+ */
73
+ private getElectronApp;
57
74
  /**
58
75
  * 关闭 playwright 启动的 electron 客户端
59
76
  * @returns
@@ -62,7 +79,7 @@ declare class LocalAutomateTask implements TaskParams {
62
79
  /**
63
80
  * 小红书自动化发布
64
81
  */
65
- xiaohongshuPublish(params: XiaohonshuPublishParams): Promise<string>;
82
+ xiaohongshuPublish(params: XiaohonshuPublishParams): Promise<unknown>;
66
83
  }
67
84
  declare const RpaTask: (params: TaskParams) => LocalAutomateTask;
68
85
 
package/dist/index.d.mts CHANGED
@@ -1,6 +1,17 @@
1
- import { ElectronApplication } from 'playwright';
2
1
  import { CookiesSetDetails } from 'electron';
3
2
 
3
+ declare class Logger {
4
+ static instance: Logger | null;
5
+ private stream;
6
+ constructor(cachePath: string);
7
+ static getInstance(cachePath: string): Logger;
8
+ debug(...msg: any[]): void;
9
+ info(...msg: any[]): void;
10
+ warn(...msg: any[]): void;
11
+ error(prefix: string, error: unknown): void;
12
+ close(): void;
13
+ }
14
+
4
15
  type CookieMap = CookiesSetDetails[];
5
16
 
6
17
  interface FictionalRendition {
@@ -46,14 +57,20 @@ interface TaskParams {
46
57
  declare class LocalAutomateTask implements TaskParams {
47
58
  cachePath: string;
48
59
  debug: boolean;
49
- appWhenReady: Promise<ElectronApplication>;
50
60
  playwrightPackage: Promise<unknown>;
51
- constructor(params: TaskParams);
61
+ logger: Logger;
62
+ private _electronApp;
63
+ constructor({ cachePath, debug }: TaskParams);
52
64
  /**
53
65
  * 安装 playwright
54
66
  * @returns
55
67
  */
56
- installPlaywright(): Promise<any>;
68
+ private installPlaywright;
69
+ /**
70
+ * 启动 Electron
71
+ * @returns
72
+ */
73
+ private getElectronApp;
57
74
  /**
58
75
  * 关闭 playwright 启动的 electron 客户端
59
76
  * @returns
@@ -62,7 +79,7 @@ declare class LocalAutomateTask implements TaskParams {
62
79
  /**
63
80
  * 小红书自动化发布
64
81
  */
65
- xiaohongshuPublish(params: XiaohonshuPublishParams): Promise<string>;
82
+ xiaohongshuPublish(params: XiaohonshuPublishParams): Promise<unknown>;
66
83
  }
67
84
  declare const RpaTask: (params: TaskParams) => LocalAutomateTask;
68
85
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,17 @@
1
- import { ElectronApplication } from 'playwright';
2
1
  import { CookiesSetDetails } from 'electron';
3
2
 
3
+ declare class Logger {
4
+ static instance: Logger | null;
5
+ private stream;
6
+ constructor(cachePath: string);
7
+ static getInstance(cachePath: string): Logger;
8
+ debug(...msg: any[]): void;
9
+ info(...msg: any[]): void;
10
+ warn(...msg: any[]): void;
11
+ error(prefix: string, error: unknown): void;
12
+ close(): void;
13
+ }
14
+
4
15
  type CookieMap = CookiesSetDetails[];
5
16
 
6
17
  interface FictionalRendition {
@@ -46,14 +57,20 @@ interface TaskParams {
46
57
  declare class LocalAutomateTask implements TaskParams {
47
58
  cachePath: string;
48
59
  debug: boolean;
49
- appWhenReady: Promise<ElectronApplication>;
50
60
  playwrightPackage: Promise<unknown>;
51
- constructor(params: TaskParams);
61
+ logger: Logger;
62
+ private _electronApp;
63
+ constructor({ cachePath, debug }: TaskParams);
52
64
  /**
53
65
  * 安装 playwright
54
66
  * @returns
55
67
  */
56
- installPlaywright(): Promise<any>;
68
+ private installPlaywright;
69
+ /**
70
+ * 启动 Electron
71
+ * @returns
72
+ */
73
+ private getElectronApp;
57
74
  /**
58
75
  * 关闭 playwright 启动的 electron 客户端
59
76
  * @returns
@@ -62,7 +79,7 @@ declare class LocalAutomateTask implements TaskParams {
62
79
  /**
63
80
  * 小红书自动化发布
64
81
  */
65
- xiaohongshuPublish(params: XiaohonshuPublishParams): Promise<string>;
82
+ xiaohongshuPublish(params: XiaohonshuPublishParams): Promise<unknown>;
66
83
  }
67
84
  declare const RpaTask: (params: TaskParams) => LocalAutomateTask;
68
85
 
package/dist/index.mjs CHANGED
@@ -1,13 +1,12 @@
1
- import semver from 'semver';
2
1
  import path from 'node:path';
3
- import axios from 'axios';
4
- import fs from 'fs-extra';
5
- import { app } from 'electron';
6
- import spawn from 'cross-spawn';
2
+ import fs from 'node:fs';
3
+ import https from 'node:https';
4
+ import log from 'loglevel';
5
+ import { spawn } from 'node:child_process';
7
6
 
8
7
  const name = "@iflyrpa/playwright";
9
8
  const type = "module";
10
- const version$1 = "1.0.8";
9
+ const version$1 = "1.0.10";
11
10
  const description = "";
12
11
  const main = "./dist/index.cjs";
13
12
  const module = "./dist/index.mjs";
@@ -25,23 +24,14 @@ const files = [
25
24
  "dist"
26
25
  ];
27
26
  const dependencies = {
28
- axios: "^1.7.4",
29
- "cross-spawn": "^7.0.3",
30
- deepmerge: "^4.3.1",
31
- "fs-extra": "^11.2.0",
32
- playwright: "^1.46.1",
33
- semver: "^7.6.3"
27
+ loglevel: "^1.9.2",
28
+ playwright: "^1.46.1"
34
29
  };
35
30
  const peerDependencies = {
36
31
  electron: "*"
37
32
  };
38
33
  const devDependencies = {
39
- "@types/cross-spawn": "^6.0.6",
40
- "@types/fs-extra": "^11.0.4",
41
- "@types/semver": "^7.5.8",
42
- bumpp: "^9.5.2",
43
34
  esno: "^4.7.0",
44
- "ts-node": "^10.9.2",
45
35
  typescript: "^5.5.2",
46
36
  unbuild: "^2.0.0"
47
37
  };
@@ -63,25 +53,159 @@ const packageJson = {
63
53
  devDependencies: devDependencies
64
54
  };
65
55
 
66
- async function downloadImage(url, path2) {
67
- const response = await axios({
68
- url,
69
- method: "GET",
70
- responseType: "stream"
71
- // 重要:设置响应类型为 'stream'
72
- });
73
- await fs.ensureFile(path2);
74
- const writer = fs.createWriteStream(path2);
75
- response.data.pipe(writer);
56
+ async function downloadImage(url, savePath) {
57
+ await ensureFile(savePath);
76
58
  return new Promise((resolve, reject) => {
77
- writer.on("finish", () => resolve(path2));
78
- writer.on("error", reject);
59
+ https.get(url, (response) => {
60
+ if (response.statusCode === 200) {
61
+ const fileStream = fs.createWriteStream(savePath);
62
+ response.pipe(fileStream);
63
+ fileStream.on("finish", () => {
64
+ fileStream.close();
65
+ console.log("\u4E0B\u8F7D\u5B8C\u6210\uFF0C\u6587\u4EF6\u5DF2\u4FDD\u5B58\u81F3:", savePath);
66
+ resolve(savePath);
67
+ });
68
+ } else {
69
+ console.log("\u56FE\u7247\u4E0B\u8F7D\u5931\u8D25:", response.statusCode);
70
+ response.resume();
71
+ reject();
72
+ }
73
+ }).on("error", (error) => {
74
+ console.error("\u8BF7\u6C42\u56FE\u7247\u65F6\u53D1\u751F\u9519\u8BEF:", error.message);
75
+ reject();
76
+ });
79
77
  });
80
78
  }
81
79
  function getFilenameFromUrl(imageUrl) {
82
80
  const parsedUrl = new URL(imageUrl);
83
81
  return path.basename(parsedUrl.pathname);
84
82
  }
83
+ function pathExists(path2) {
84
+ return new Promise((resolve) => {
85
+ fs.stat(path2, (err) => {
86
+ resolve(!err);
87
+ });
88
+ });
89
+ }
90
+ function ensureFile(filePath) {
91
+ return new Promise((resolve, reject) => {
92
+ const dirPath = path.dirname(filePath);
93
+ fs.stat(dirPath, (err, stats) => {
94
+ if (err) {
95
+ if (err.code === "ENOENT") {
96
+ fs.mkdir(dirPath, { recursive: true }, (err2) => {
97
+ if (err2) {
98
+ return reject(err2);
99
+ }
100
+ createFile();
101
+ });
102
+ } else {
103
+ return reject(err);
104
+ }
105
+ } else if (stats.isDirectory()) {
106
+ checkFile();
107
+ } else {
108
+ reject(new Error(`${dirPath} is not a directory`));
109
+ }
110
+ });
111
+ function checkFile() {
112
+ fs.stat(filePath, (err, stats) => {
113
+ if (err) {
114
+ if (err.code === "ENOENT") {
115
+ createFile();
116
+ } else {
117
+ reject(err);
118
+ }
119
+ } else if (stats.isFile()) {
120
+ resolve();
121
+ } else {
122
+ reject(new Error(`${filePath} is not a file`));
123
+ }
124
+ });
125
+ }
126
+ function createFile() {
127
+ fs.writeFile(filePath, "", (err) => {
128
+ if (err) {
129
+ reject(err);
130
+ } else {
131
+ resolve();
132
+ }
133
+ });
134
+ }
135
+ });
136
+ }
137
+ function ensureFileSync(filePath) {
138
+ const dirPath = path.dirname(filePath);
139
+ try {
140
+ if (!fs.existsSync(dirPath)) {
141
+ fs.mkdirSync(dirPath, { recursive: true });
142
+ }
143
+ } catch (err) {
144
+ if (err instanceof Error) {
145
+ throw new Error(`Error creating directory: ${err.message}`);
146
+ }
147
+ }
148
+ try {
149
+ if (!fs.existsSync(filePath)) {
150
+ fs.writeFileSync(filePath, "");
151
+ }
152
+ } catch (err) {
153
+ if (err instanceof Error) {
154
+ throw new Error(`Error creating file: ${err.message}`);
155
+ }
156
+ }
157
+ }
158
+ function writeFile(filePath, data) {
159
+ return new Promise((resolve, reject) => {
160
+ fs.writeFile(filePath, data, (err) => {
161
+ if (err) {
162
+ console.error("Error writing file:", err);
163
+ reject();
164
+ } else {
165
+ console.log("File written successfully");
166
+ resolve();
167
+ }
168
+ });
169
+ });
170
+ }
171
+ function compareVersions(v1, v2) {
172
+ const parts1 = v1.split(".").map(Number);
173
+ const parts2 = v2.split(".").map(Number);
174
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
175
+ const num1 = i < parts1.length ? parts1[i] : 0;
176
+ const num2 = i < parts2.length ? parts2[i] : 0;
177
+ if (num1 > num2)
178
+ return 1;
179
+ if (num1 < num2)
180
+ return -1;
181
+ }
182
+ return 0;
183
+ }
184
+ const semver = {
185
+ gt: (v1, v2) => compareVersions(v1, v2) === 1
186
+ };
187
+ function fetchJSON(url) {
188
+ return new Promise((resolve, reject) => {
189
+ https.get(url, (res) => {
190
+ let data = "";
191
+ res.on("data", (chunk) => {
192
+ data += chunk;
193
+ });
194
+ res.on("end", () => {
195
+ try {
196
+ const parsedData = JSON.parse(data);
197
+ resolve(parsedData);
198
+ } catch (e) {
199
+ if (e instanceof Error) {
200
+ reject(new Error(`Error parsing JSON: ${e.message}`));
201
+ }
202
+ }
203
+ });
204
+ }).on("error", (err) => {
205
+ reject(new Error(`Request failed: ${err.message}`));
206
+ });
207
+ });
208
+ }
85
209
 
86
210
  const visibleRangeTexts = {
87
211
  public: "\u516C\u5F00",
@@ -205,6 +329,65 @@ const xiaohongshuPublishAction = async (props) => {
205
329
  return response;
206
330
  };
207
331
 
332
+ var __defProp$2 = Object.defineProperty;
333
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
334
+ var __publicField$2 = (obj, key, value) => {
335
+ __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
336
+ return value;
337
+ };
338
+ const _Logger = class _Logger {
339
+ constructor(cachePath) {
340
+ __publicField$2(this, "stream");
341
+ if (_Logger.instance) {
342
+ return _Logger.instance;
343
+ }
344
+ const logFile = path.join(cachePath, "rpa.log");
345
+ this.stream = fs.createWriteStream(logFile, { flags: "a" });
346
+ log.setLevel("debug");
347
+ log.methodFactory = (methodName) => {
348
+ return (message) => {
349
+ this.stream.write(
350
+ `[${( new Date()).toISOString()}] ${methodName.toUpperCase()}: ${message}
351
+ `
352
+ );
353
+ };
354
+ };
355
+ log.setLevel(log.getLevel());
356
+ _Logger.instance = this;
357
+ }
358
+ static getInstance(cachePath) {
359
+ if (!_Logger.instance) {
360
+ _Logger.instance = new _Logger(cachePath);
361
+ }
362
+ return _Logger.instance;
363
+ }
364
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
365
+ debug(...msg) {
366
+ log.debug(...msg);
367
+ }
368
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
369
+ info(...msg) {
370
+ log.info(...msg);
371
+ }
372
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
373
+ warn(...msg) {
374
+ log.warn(...msg);
375
+ }
376
+ error(prefix, error) {
377
+ let errorMessage = error;
378
+ if (error instanceof Error) {
379
+ errorMessage = `${error.message}
380
+ Error stack: ${error.stack}`;
381
+ }
382
+ log.error(prefix, errorMessage);
383
+ }
384
+ close() {
385
+ this.stream.end();
386
+ }
387
+ };
388
+ __publicField$2(_Logger, "instance", null);
389
+ let Logger = _Logger;
390
+
208
391
  const template = `
209
392
  const { app } = require("electron");
210
393
 
@@ -214,22 +397,35 @@ app.on("window-all-closed", (e) => e.preventDefault());
214
397
  `;
215
398
  const generateFile = async (dir) => {
216
399
  const filePath = path.join(dir, "src", "main.js");
217
- const pathExists = await fs.pathExists(filePath);
218
- if (!pathExists) {
219
- await fs.ensureFile(filePath);
220
- await fs.writeFile(filePath, template);
400
+ const isPathExists = await pathExists(filePath);
401
+ if (!isPathExists) {
402
+ await ensureFile(filePath);
403
+ await writeFile(filePath, template);
221
404
  }
222
405
  return filePath;
223
406
  };
224
407
  const createElectronApp = async (cachePath, playwrightPackage) => {
225
- const playwright = await playwrightPackage;
226
- const mainPath = await generateFile(cachePath);
227
- const electronApp = await playwright._electron.launch({
228
- executablePath: app.getPath("exe"),
229
- // 获取 Electron 可执行文件的路径
230
- args: [mainPath]
231
- });
232
- return electronApp;
408
+ const logger = Logger.getInstance(cachePath);
409
+ try {
410
+ const executablePath = path.join(
411
+ cachePath,
412
+ "node_modules",
413
+ ".bin",
414
+ "electron"
415
+ );
416
+ const playwright = await playwrightPackage;
417
+ const mainPath = await generateFile(cachePath);
418
+ const electronApp = await playwright._electron.launch({
419
+ executablePath,
420
+ // 获取 Electron 可执行文件的路径
421
+ args: [mainPath],
422
+ cwd: cachePath
423
+ });
424
+ logger.info(`electron \u542F\u52A8\u6210\u529F\uFF1A${executablePath} ${mainPath}`);
425
+ return electronApp;
426
+ } catch (error) {
427
+ logger.error("electron \u542F\u52A8\u5931\u8D25\uFF1A", error);
428
+ }
233
429
  };
234
430
 
235
431
  var __defProp$1 = Object.defineProperty;
@@ -245,8 +441,11 @@ class PackageManager {
245
441
  __publicField$1(this, "forceUpdate");
246
442
  // 是否强制更新
247
443
  __publicField$1(this, "initPromise");
444
+ __publicField$1(this, "logger");
445
+ this.logger = Logger.getInstance(params.cacheDir);
248
446
  this.cacheDir = params.cacheDir;
249
447
  this.forceUpdate = params.forceUpdate || true;
448
+ this.initCacheProject();
250
449
  this.initPromise = this.init(params.packageName, params.packageVersion);
251
450
  }
252
451
  // 在子线程执行 npm 命令
@@ -254,7 +453,8 @@ class PackageManager {
254
453
  return new Promise((resolve) => {
255
454
  const args = [cmd].concat(modules).concat("--color=always").concat("--save");
256
455
  try {
257
- const npm = spawn("npm", args, { cwd: where });
456
+ const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
457
+ const npm = spawn(npmCmd, args, { cwd: where });
258
458
  let output = "";
259
459
  npm.stdout?.on("data", (data) => {
260
460
  output += data;
@@ -274,6 +474,7 @@ class PackageManager {
274
474
  console.error(err);
275
475
  });
276
476
  } catch (error) {
477
+ this.logger.error(`npm ${args.join(" ")}: `, error);
277
478
  console.error(error);
278
479
  }
279
480
  });
@@ -282,21 +483,23 @@ class PackageManager {
282
483
  initCacheProject() {
283
484
  const packagePath = path.join(this.cacheDir, "package.json");
284
485
  if (!fs.existsSync(packagePath)) {
486
+ this.logger.info("package.json \u4E0D\u5B58\u5728\uFF0C\u6DFB\u52A0\u8BE5\u6587\u4EF6");
285
487
  const pkg = {
286
488
  name: "rpa-plugins",
287
489
  description: "rpa-plugins",
288
- license: "MIT"
490
+ license: "MIT",
491
+ main: "./src/main.js"
289
492
  };
290
- fs.ensureFileSync(packagePath);
493
+ ensureFileSync(packagePath);
291
494
  fs.writeFileSync(packagePath, JSON.stringify(pkg), "utf8");
292
495
  }
293
496
  }
294
497
  // 获取依赖信息
295
498
  async getPluginInfo(module) {
296
- const res = await axios.get(
499
+ const res = await fetchJSON(
297
500
  `https://registry.npmjs.com/-/v1/search?text=${module}`
298
501
  );
299
- const packages = res.data.objects;
502
+ const packages = res.objects;
300
503
  return packages.find((it) => it.package.name === module)?.package;
301
504
  }
302
505
  // 查询本地安装的依赖
@@ -305,6 +508,7 @@ class PackageManager {
305
508
  try {
306
509
  return require(path.join(pluginDir, module));
307
510
  } catch (error) {
511
+ this.logger.warn(`${module}\u672C\u5730\u4F9D\u8D56\u4E0D\u5B58\u5728\uFF0C${pluginDir}`);
308
512
  return null;
309
513
  }
310
514
  }
@@ -315,27 +519,26 @@ class PackageManager {
315
519
  // 安装依赖
316
520
  install(module, version) {
317
521
  const moduleName = version ? `${module}@${version}` : module;
318
- return this.execCommand("install", [moduleName]);
522
+ return this.execCommand("install", [moduleName, "--save"]);
319
523
  }
320
524
  // 更新依赖
321
525
  update(module) {
322
526
  return this.execCommand("update", [module]);
323
527
  }
324
528
  async init(name, version) {
325
- this.initCacheProject();
326
529
  const plugin = this.getPlugin(path.join(name, "package.json"));
327
530
  if (!plugin) {
328
531
  await this.install(name, version);
329
532
  } else if (this.forceUpdate) {
330
533
  const pkInfo = await this.getPluginInfo(name);
331
534
  if (!pkInfo)
332
- throw new Error("\u4F9D\u8D56\u4FE1\u606F\u83B7\u53D6\u5931\u8D25");
535
+ return;
333
536
  const hasNewVersion = semver.gt(pkInfo.version, plugin.version);
334
537
  if (hasNewVersion) {
335
538
  await this.install(name, pkInfo.version);
336
539
  }
337
540
  }
338
- console.log("Package manager init done!");
541
+ this.logger.info(`${name} package manager init done!`);
339
542
  }
340
543
  }
341
544
 
@@ -347,18 +550,16 @@ var __publicField = (obj, key, value) => {
347
550
  };
348
551
  const PLAYWRIGHT_VERSION = "1.46.1";
349
552
  class LocalAutomateTask {
350
- constructor(params) {
553
+ constructor({ cachePath, debug }) {
351
554
  __publicField(this, "cachePath");
352
555
  __publicField(this, "debug");
353
- __publicField(this, "appWhenReady");
354
556
  __publicField(this, "playwrightPackage");
355
- this.cachePath = params.cachePath;
356
- this.debug = params.debug || false;
557
+ __publicField(this, "logger");
558
+ __publicField(this, "_electronApp", null);
559
+ this.cachePath = cachePath;
560
+ this.debug = debug || false;
561
+ this.logger = Logger.getInstance(cachePath);
357
562
  this.playwrightPackage = this.installPlaywright();
358
- this.appWhenReady = createElectronApp(
359
- params.cachePath,
360
- this.playwrightPackage
361
- );
362
563
  }
363
564
  /**
364
565
  * 安装 playwright
@@ -372,19 +573,33 @@ class LocalAutomateTask {
372
573
  });
373
574
  return playwrightPackageManager.getPluginAfterInit("playwright");
374
575
  }
576
+ /**
577
+ * 启动 Electron
578
+ * @returns
579
+ */
580
+ async getElectronApp() {
581
+ if (!this._electronApp) {
582
+ this._electronApp = await createElectronApp(
583
+ this.cachePath,
584
+ this.playwrightPackage
585
+ );
586
+ }
587
+ return this._electronApp;
588
+ }
375
589
  /**
376
590
  * 关闭 playwright 启动的 electron 客户端
377
591
  * @returns
378
592
  */
379
593
  async close() {
380
- const electronApp = await this.appWhenReady;
594
+ this.logger.close();
595
+ const electronApp = await this.getElectronApp();
381
596
  return electronApp.close();
382
597
  }
383
598
  /**
384
599
  * 小红书自动化发布
385
600
  */
386
601
  async xiaohongshuPublish(params) {
387
- const electronApp = await this.appWhenReady;
602
+ const electronApp = await this.getElectronApp();
388
603
  const commonCookies = {
389
604
  path: "/",
390
605
  sameSite: "lax",
@@ -424,22 +639,29 @@ class LocalAutomateTask {
424
639
  { pageParams }
425
640
  )
426
641
  ]);
427
- const res = await xiaohongshuPublishAction({
428
- page,
429
- params,
430
- cachePath: this.cachePath,
431
- debug: !!this.debug
432
- });
433
- return res;
642
+ try {
643
+ const res = await xiaohongshuPublishAction({
644
+ page,
645
+ params,
646
+ cachePath: this.cachePath,
647
+ debug: !!this.debug
648
+ });
649
+ return res;
650
+ } catch (error) {
651
+ this.logger.error("\u5C0F\u7EA2\u4E66\u53D1\u5E03\u5931\u8D25", error);
652
+ return error;
653
+ }
434
654
  }
435
655
  }
436
656
  const RpaTask = (params) => {
657
+ const logger = Logger.getInstance(params.cachePath);
437
658
  const packageManager = new PackageManager({
438
659
  packageName: packageJson.name,
439
660
  cacheDir: params.cachePath
440
661
  });
441
662
  const localPackge = packageManager.getPlugin(packageJson.name);
442
663
  if (localPackge?.LocalAutomateTask && localPackge?.version && semver.gt(localPackge.version, packageJson.version)) {
664
+ logger.info(`\u4F7F\u7528\u8FDC\u7A0B\u7684\u65B0\u7248\u672C\uFF0C\u7248\u672C\u53F7\u4E3A\uFF1A${localPackge.version}`);
443
665
  return new localPackge.LocalAutomateTask(params);
444
666
  }
445
667
  return new LocalAutomateTask(params);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@iflyrpa/playwright",
3
3
  "type": "module",
4
- "version": "1.0.8",
4
+ "version": "1.0.10",
5
5
  "description": "",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.mjs",
@@ -16,23 +16,14 @@
16
16
  "license": "ISC",
17
17
  "files": ["dist"],
18
18
  "dependencies": {
19
- "axios": "^1.7.4",
20
- "cross-spawn": "^7.0.3",
21
- "deepmerge": "^4.3.1",
22
- "fs-extra": "^11.2.0",
23
- "playwright": "^1.46.1",
24
- "semver": "^7.6.3"
19
+ "loglevel": "^1.9.2",
20
+ "playwright": "^1.46.1"
25
21
  },
26
22
  "peerDependencies": {
27
23
  "electron": "*"
28
24
  },
29
25
  "devDependencies": {
30
- "@types/cross-spawn": "^6.0.6",
31
- "@types/fs-extra": "^11.0.4",
32
- "@types/semver": "^7.5.8",
33
- "bumpp": "^9.5.2",
34
26
  "esno": "^4.7.0",
35
- "ts-node": "^10.9.2",
36
27
  "typescript": "^5.5.2",
37
28
  "unbuild": "^2.0.0"
38
29
  }