@iflyrpa/playwright 1.1.0 → 1.1.2-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,4 +2,80 @@
2
2
 
3
3
  iflyrpa 封装的一组特定场景的自动化脚本,支持的场景如下:
4
4
 
5
- 1. 小红书自动发布
5
+ | 平台 | 脚本 | 说明 | 支持的方式 |
6
+ | ---------------------------- | ---------------------- | -------------------------------------------------- | -------------- |
7
+ | 头条 | toutiaoPublish | 发布/保存草稿 | Mock Api |
8
+ | 头条 | getToutiaoConfig | 获取头条发布配置的话题信息、位置信息和原创首发信息 | Mock Api |
9
+ | 头条 | searchToutiaoTopicList | 搜索头条话题 | Mock Api |
10
+ | 微头条 | weitoutiaoPublish | 发布 | Mock Api / RPA |
11
+ | 百家号 | getBaijiahaoActivity | 查询百家号活动投稿 | Mock Api |
12
+ | 百家号 | baijiahaoPublish | 发布/保存草稿 | Mock Api |
13
+ | 小绿书(微信公众号图文模式) | weixinmpPublish | 发布 | Mock Api / RPA |
14
+ | 小红书 | xiaohongshuPublish | 发布 | Mock Api / RPA |
15
+
16
+ ## 安装
17
+
18
+ ```bash
19
+ # npm
20
+ npm i @iflyrpa/playwright
21
+
22
+ # yarn
23
+ yarn add @iflyrpa/playwright
24
+
25
+ # pnpm
26
+ pnpm install @iflyrpa/playwright
27
+ ```
28
+
29
+ ## 使用方法
30
+
31
+ ```typescript
32
+ import { RpaTask } from "@iflyrpa/playwright";
33
+
34
+ // 1. 创建缓存目录
35
+ const cachePath = join(__dirname, "../main/cache");
36
+
37
+ // 2. 初始化
38
+ const automateTask = new RpaTask({
39
+ cachePath, // 缓存目录
40
+ debug: true, // debug 模式下可视化展示所有操作
41
+ forceUpdate: false, // 是否使用远程最新版本
42
+ });
43
+
44
+ // 3. 执行脚本,支持的脚本如上
45
+ automateTask.actions.xiaohongshuPublish(xiaohonshuPublishParams)
46
+
47
+ // 4. 应用关闭前,清除副作用
48
+ app.on("before-quit", (event) => {
49
+ if (!automateTask.isClosed) {
50
+ // 阻止应用立即退出
51
+ event.preventDefault();
52
+
53
+ automateTask.close().finally(() => {
54
+ console.log("close app");
55
+ app.quit();
56
+ });
57
+ }
58
+ });
59
+ ```
60
+
61
+ ## 注意事项
62
+
63
+ **1. 缓存目录包含以下文件:**
64
+
65
+ - packages:运行时依赖和脚本缓存目录
66
+ - tmp:运行时缓存目录
67
+ - rpa.log:运行日志文件
68
+
69
+ **2. 如何获取脚本入参类型:**
70
+
71
+ ```typescript
72
+ import type { ActionMethodParams } from "@iflyrpa/playwright";
73
+
74
+ export const xiaohonshuPublishParams: ActionMethodParams['xiaohongshuPublish'] = {}
75
+
76
+ automateTask.actions.xiaohongshuPublish(xiaohonshuPublishParams)
77
+ ```
78
+
79
+ **3. 云端日志**
80
+
81
+ <https://sentry-new.iflyrpa.com/rpa/turbodesk-rpa>
@@ -0,0 +1,170 @@
1
+ 'use strict';
2
+
3
+ async function pMap(
4
+ iterable,
5
+ mapper,
6
+ {
7
+ concurrency = Number.POSITIVE_INFINITY,
8
+ stopOnError = true,
9
+ signal,
10
+ } = {},
11
+ ) {
12
+ return new Promise((resolve, reject_) => {
13
+ if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) {
14
+ throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`);
15
+ }
16
+
17
+ if (typeof mapper !== 'function') {
18
+ throw new TypeError('Mapper function is required');
19
+ }
20
+
21
+ if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) {
22
+ throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`);
23
+ }
24
+
25
+ const result = [];
26
+ const errors = [];
27
+ const skippedIndexesMap = new Map();
28
+ let isRejected = false;
29
+ let isResolved = false;
30
+ let isIterableDone = false;
31
+ let resolvingCount = 0;
32
+ let currentIndex = 0;
33
+ const iterator = iterable[Symbol.iterator] === undefined ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator]();
34
+
35
+ const reject = reason => {
36
+ isRejected = true;
37
+ isResolved = true;
38
+ reject_(reason);
39
+ };
40
+
41
+ if (signal) {
42
+ if (signal.aborted) {
43
+ reject(signal.reason);
44
+ }
45
+
46
+ signal.addEventListener('abort', () => {
47
+ reject(signal.reason);
48
+ });
49
+ }
50
+
51
+ const next = async () => {
52
+ if (isResolved) {
53
+ return;
54
+ }
55
+
56
+ const nextItem = await iterator.next();
57
+
58
+ const index = currentIndex;
59
+ currentIndex++;
60
+
61
+ // Note: `iterator.next()` can be called many times in parallel.
62
+ // This can cause multiple calls to this `next()` function to
63
+ // receive a `nextItem` with `done === true`.
64
+ // The shutdown logic that rejects/resolves must be protected
65
+ // so it runs only one time as the `skippedIndex` logic is
66
+ // non-idempotent.
67
+ if (nextItem.done) {
68
+ isIterableDone = true;
69
+
70
+ if (resolvingCount === 0 && !isResolved) {
71
+ if (!stopOnError && errors.length > 0) {
72
+ reject(new AggregateError(errors)); // eslint-disable-line unicorn/error-message
73
+ return;
74
+ }
75
+
76
+ isResolved = true;
77
+
78
+ if (skippedIndexesMap.size === 0) {
79
+ resolve(result);
80
+ return;
81
+ }
82
+
83
+ const pureResult = [];
84
+
85
+ // Support multiple `pMapSkip`'s.
86
+ for (const [index, value] of result.entries()) {
87
+ if (skippedIndexesMap.get(index) === pMapSkip) {
88
+ continue;
89
+ }
90
+
91
+ pureResult.push(value);
92
+ }
93
+
94
+ resolve(pureResult);
95
+ }
96
+
97
+ return;
98
+ }
99
+
100
+ resolvingCount++;
101
+
102
+ // Intentionally detached
103
+ (async () => {
104
+ try {
105
+ const element = await nextItem.value;
106
+
107
+ if (isResolved) {
108
+ return;
109
+ }
110
+
111
+ const value = await mapper(element, index);
112
+
113
+ // Use Map to stage the index of the element.
114
+ if (value === pMapSkip) {
115
+ skippedIndexesMap.set(index, value);
116
+ }
117
+
118
+ result[index] = value;
119
+
120
+ resolvingCount--;
121
+ await next();
122
+ } catch (error) {
123
+ if (stopOnError) {
124
+ reject(error);
125
+ } else {
126
+ errors.push(error);
127
+ resolvingCount--;
128
+
129
+ // In that case we can't really continue regardless of `stopOnError` state
130
+ // since an iterable is likely to continue throwing after it throws once.
131
+ // If we continue calling `next()` indefinitely we will likely end up
132
+ // in an infinite loop of failed iteration.
133
+ try {
134
+ await next();
135
+ } catch (error) {
136
+ reject(error);
137
+ }
138
+ }
139
+ }
140
+ })();
141
+ };
142
+
143
+ // Create the concurrent runners in a detached (non-awaited)
144
+ // promise. We need this so we can await the `next()` calls
145
+ // to stop creating runners before hitting the concurrency limit
146
+ // if the iterable has already been marked as done.
147
+ // NOTE: We *must* do this for async iterators otherwise we'll spin up
148
+ // infinite `next()` calls by default and never start the event loop.
149
+ (async () => {
150
+ for (let index = 0; index < concurrency; index++) {
151
+ try {
152
+ // eslint-disable-next-line no-await-in-loop
153
+ await next();
154
+ } catch (error) {
155
+ reject(error);
156
+ break;
157
+ }
158
+
159
+ if (isIterableDone || isRejected) {
160
+ break;
161
+ }
162
+ }
163
+ })();
164
+ });
165
+ }
166
+
167
+ const pMapSkip = Symbol('skip');
168
+
169
+ exports.default = pMap;
170
+ exports.pMapSkip = pMapSkip;
@@ -0,0 +1,167 @@
1
+ async function pMap(
2
+ iterable,
3
+ mapper,
4
+ {
5
+ concurrency = Number.POSITIVE_INFINITY,
6
+ stopOnError = true,
7
+ signal,
8
+ } = {},
9
+ ) {
10
+ return new Promise((resolve, reject_) => {
11
+ if (iterable[Symbol.iterator] === undefined && iterable[Symbol.asyncIterator] === undefined) {
12
+ throw new TypeError(`Expected \`input\` to be either an \`Iterable\` or \`AsyncIterable\`, got (${typeof iterable})`);
13
+ }
14
+
15
+ if (typeof mapper !== 'function') {
16
+ throw new TypeError('Mapper function is required');
17
+ }
18
+
19
+ if (!((Number.isSafeInteger(concurrency) && concurrency >= 1) || concurrency === Number.POSITIVE_INFINITY)) {
20
+ throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`);
21
+ }
22
+
23
+ const result = [];
24
+ const errors = [];
25
+ const skippedIndexesMap = new Map();
26
+ let isRejected = false;
27
+ let isResolved = false;
28
+ let isIterableDone = false;
29
+ let resolvingCount = 0;
30
+ let currentIndex = 0;
31
+ const iterator = iterable[Symbol.iterator] === undefined ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator]();
32
+
33
+ const reject = reason => {
34
+ isRejected = true;
35
+ isResolved = true;
36
+ reject_(reason);
37
+ };
38
+
39
+ if (signal) {
40
+ if (signal.aborted) {
41
+ reject(signal.reason);
42
+ }
43
+
44
+ signal.addEventListener('abort', () => {
45
+ reject(signal.reason);
46
+ });
47
+ }
48
+
49
+ const next = async () => {
50
+ if (isResolved) {
51
+ return;
52
+ }
53
+
54
+ const nextItem = await iterator.next();
55
+
56
+ const index = currentIndex;
57
+ currentIndex++;
58
+
59
+ // Note: `iterator.next()` can be called many times in parallel.
60
+ // This can cause multiple calls to this `next()` function to
61
+ // receive a `nextItem` with `done === true`.
62
+ // The shutdown logic that rejects/resolves must be protected
63
+ // so it runs only one time as the `skippedIndex` logic is
64
+ // non-idempotent.
65
+ if (nextItem.done) {
66
+ isIterableDone = true;
67
+
68
+ if (resolvingCount === 0 && !isResolved) {
69
+ if (!stopOnError && errors.length > 0) {
70
+ reject(new AggregateError(errors)); // eslint-disable-line unicorn/error-message
71
+ return;
72
+ }
73
+
74
+ isResolved = true;
75
+
76
+ if (skippedIndexesMap.size === 0) {
77
+ resolve(result);
78
+ return;
79
+ }
80
+
81
+ const pureResult = [];
82
+
83
+ // Support multiple `pMapSkip`'s.
84
+ for (const [index, value] of result.entries()) {
85
+ if (skippedIndexesMap.get(index) === pMapSkip) {
86
+ continue;
87
+ }
88
+
89
+ pureResult.push(value);
90
+ }
91
+
92
+ resolve(pureResult);
93
+ }
94
+
95
+ return;
96
+ }
97
+
98
+ resolvingCount++;
99
+
100
+ // Intentionally detached
101
+ (async () => {
102
+ try {
103
+ const element = await nextItem.value;
104
+
105
+ if (isResolved) {
106
+ return;
107
+ }
108
+
109
+ const value = await mapper(element, index);
110
+
111
+ // Use Map to stage the index of the element.
112
+ if (value === pMapSkip) {
113
+ skippedIndexesMap.set(index, value);
114
+ }
115
+
116
+ result[index] = value;
117
+
118
+ resolvingCount--;
119
+ await next();
120
+ } catch (error) {
121
+ if (stopOnError) {
122
+ reject(error);
123
+ } else {
124
+ errors.push(error);
125
+ resolvingCount--;
126
+
127
+ // In that case we can't really continue regardless of `stopOnError` state
128
+ // since an iterable is likely to continue throwing after it throws once.
129
+ // If we continue calling `next()` indefinitely we will likely end up
130
+ // in an infinite loop of failed iteration.
131
+ try {
132
+ await next();
133
+ } catch (error) {
134
+ reject(error);
135
+ }
136
+ }
137
+ }
138
+ })();
139
+ };
140
+
141
+ // Create the concurrent runners in a detached (non-awaited)
142
+ // promise. We need this so we can await the `next()` calls
143
+ // to stop creating runners before hitting the concurrency limit
144
+ // if the iterable has already been marked as done.
145
+ // NOTE: We *must* do this for async iterators otherwise we'll spin up
146
+ // infinite `next()` calls by default and never start the event loop.
147
+ (async () => {
148
+ for (let index = 0; index < concurrency; index++) {
149
+ try {
150
+ // eslint-disable-next-line no-await-in-loop
151
+ await next();
152
+ } catch (error) {
153
+ reject(error);
154
+ break;
155
+ }
156
+
157
+ if (isIterableDone || isRejected) {
158
+ break;
159
+ }
160
+ }
161
+ })();
162
+ });
163
+ }
164
+
165
+ const pMapSkip = Symbol('skip');
166
+
167
+ export { pMap as default, pMapSkip };