appium-mac2-driver 3.4.0 → 3.4.2
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/CHANGELOG.md +12 -0
- package/build/lib/commands/native-record-screen.d.ts +39 -3
- package/build/lib/commands/native-record-screen.d.ts.map +1 -1
- package/build/lib/commands/native-record-screen.js +164 -41
- package/build/lib/commands/native-record-screen.js.map +1 -1
- package/lib/commands/native-record-screen.ts +176 -51
- package/npm-shrinkwrap.json +21 -8
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [3.4.2](https://github.com/appium/appium-mac2-driver/compare/v3.4.1...v3.4.2) (2026-05-13)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* native video broadcast cleanup ([#384](https://github.com/appium/appium-mac2-driver/issues/384)) ([ea81c2f](https://github.com/appium/appium-mac2-driver/commit/ea81c2fd7b5177de2f8d7ae9f157c1ec73c55d3d))
|
|
6
|
+
|
|
7
|
+
## [3.4.1](https://github.com/appium/appium-mac2-driver/compare/v3.4.0...v3.4.1) (2026-05-13)
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* Unhandled error for native video recording ([#383](https://github.com/appium/appium-mac2-driver/issues/383)) ([22a8bff](https://github.com/appium/appium-mac2-driver/commit/22a8bff4efc731b81737fa6d5c36de63582db4f3))
|
|
12
|
+
|
|
1
13
|
## [3.4.0](https://github.com/appium/appium-mac2-driver/compare/v3.3.4...v3.4.0) (2026-05-09)
|
|
2
14
|
|
|
3
15
|
### Features
|
|
@@ -13,18 +13,54 @@ interface DisplayInfo {
|
|
|
13
13
|
isMain: boolean;
|
|
14
14
|
}
|
|
15
15
|
export declare class NativeVideoChunksBroadcaster {
|
|
16
|
-
private _ee;
|
|
17
|
-
private _log;
|
|
18
|
-
private _publishers;
|
|
16
|
+
private readonly _ee;
|
|
17
|
+
private readonly _log;
|
|
18
|
+
private readonly _publishers;
|
|
19
|
+
private readonly _wakeups;
|
|
20
|
+
private readonly _terminationRequests;
|
|
19
21
|
private _terminated;
|
|
20
22
|
constructor(ee: EventEmitter, log: AppiumLogger);
|
|
21
23
|
get hasPublishers(): boolean;
|
|
22
24
|
schedule(uuid: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Signals the publisher for `uuid` that its underlying recording has been
|
|
27
|
+
* stopped. The publisher wakes from its current polling sleep, performs a
|
|
28
|
+
* final stat + read + emit pass to flush any remaining bytes of the
|
|
29
|
+
* recording file to BiDi consumers, and exits.
|
|
30
|
+
*
|
|
31
|
+
* Idempotent and safe to call for an unknown uuid (no-op in that case).
|
|
32
|
+
*
|
|
33
|
+
* Callers SHOULD invoke this immediately after the recording has been
|
|
34
|
+
* stopped on the underlying XCTest side; `waitFor(uuid)` is then
|
|
35
|
+
* contractually guaranteed to resolve as soon as the publisher has
|
|
36
|
+
* finished flushing, without having to wait for the next polling tick.
|
|
37
|
+
*/
|
|
38
|
+
notifyStopped(uuid: string): void;
|
|
23
39
|
waitFor(uuid: string): Promise<void>;
|
|
24
40
|
shutdown(timeoutMs: number): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Background monitor for a native screen recording attachment.
|
|
43
|
+
*
|
|
44
|
+
* This is scheduled as a fire-and-forget task by {@link schedule}; the
|
|
45
|
+
* promise is removed from `_publishers` when the monitor exits (see
|
|
46
|
+
* {@link _finishPublisher}). To make sure a failure here can never surface as an
|
|
47
|
+
* unhandled promise rejection (which terminates the Appium server process
|
|
48
|
+
* on Node.js 24+), this method MUST never reject: every error path logs a
|
|
49
|
+
* warning and returns normally.
|
|
50
|
+
*/
|
|
25
51
|
private _createPublisher;
|
|
26
52
|
private _wait;
|
|
27
53
|
private _cleanup;
|
|
54
|
+
/**
|
|
55
|
+
* Runs {@link _createPublisher} and always clears `_publishers` /
|
|
56
|
+
* `_wakeups` / `_terminationRequests` for `uuid` when the monitor exits.
|
|
57
|
+
*/
|
|
58
|
+
private _runPublisher;
|
|
59
|
+
/**
|
|
60
|
+
* Drops all bookkeeping for a finished publisher so {@link hasPublishers}
|
|
61
|
+
* reflects reality and `notifyStopped` state does not leak across sessions.
|
|
62
|
+
*/
|
|
63
|
+
private _finishPublisher;
|
|
28
64
|
}
|
|
29
65
|
/**
|
|
30
66
|
* Initiates a new native screen recording session via XCTest.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-record-screen.d.ts","sourceRoot":"","sources":["../../../lib/commands/native-record-screen.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"native-record-screen.d.ts","sourceRoot":"","sources":["../../../lib/commands/native-record-screen.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,WAAW,CAAC;AAE1C,OAAO,KAAK,EAAC,YAAY,EAAE,YAAY,EAAC,MAAM,eAAe,CAAC;AAC9D,OAAO,KAAK,YAAY,MAAM,aAAa,CAAC;AAc5C,UAAU,eAAe;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,4BAA4B;IACvC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IAIzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwC;IAKjE,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAc;IACnD,OAAO,CAAC,WAAW,CAAU;gBAEjB,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY;IAS/C,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAM5B;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQ3B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBhD;;;;;;;;;OASG;YACW,gBAAgB;YA6GhB,KAAK;YAiBL,QAAQ;IA6BtB;;;OAGG;YACW,aAAa;IAQ3B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;CAKzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,+BAA+B,CACnD,IAAI,EAAE,UAAU,EAChB,GAAG,CAAC,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,eAAe,CAAC,CAQ1B;AAED;;;;GAIG;AACH,wBAAsB,iCAAiC,CACrD,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAEjC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,UAAU,EAChB,UAAU,CAAC,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,YAAY,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EACxC,aAAa,CAAC,EAAE,MAAM,EACtB,UAAU,CAAC,EAAE,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAC9C,aAAa,CAAC,EAAE,OAAO,GACtB,OAAO,CAAC,MAAM,CAAC,CAuDjB;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAE5F"}
|
|
@@ -8,7 +8,6 @@ exports.macosStartNativeScreenRecording = macosStartNativeScreenRecording;
|
|
|
8
8
|
exports.macosGetNativeScreenRecordingInfo = macosGetNativeScreenRecordingInfo;
|
|
9
9
|
exports.macosStopNativeScreenRecording = macosStopNativeScreenRecording;
|
|
10
10
|
exports.macosListDisplays = macosListDisplays;
|
|
11
|
-
const promises_1 = require("node:timers/promises");
|
|
12
11
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
12
|
const support_1 = require("appium/support");
|
|
14
13
|
const helpers_1 = require("./helpers");
|
|
@@ -21,15 +20,27 @@ const RECORDING_STARTUP_TIMEOUT_MS = 5000;
|
|
|
21
20
|
const BUFFER_SIZE = 0xffff;
|
|
22
21
|
const MONITORING_INTERVAL_DURATION_MS = 1000;
|
|
23
22
|
const MAX_MONITORING_DURATION_MS = 24 * 60 * 60 * 1000; // 1 day
|
|
23
|
+
const STOP_WAIT_TIMEOUT_MS = 5000;
|
|
24
24
|
class NativeVideoChunksBroadcaster {
|
|
25
25
|
_ee;
|
|
26
26
|
_log;
|
|
27
27
|
_publishers;
|
|
28
|
+
// The currently scheduled polling sleep of each publisher, indexed by uuid.
|
|
29
|
+
// Cancelling it wakes the publisher up before the next polling tick so it
|
|
30
|
+
// can perform a final stat + read + emit pass and exit.
|
|
31
|
+
_wakeups;
|
|
32
|
+
// Set of uuids whose underlying recording has been explicitly stopped via
|
|
33
|
+
// `notifyStopped`. The publisher uses this as the primary completion
|
|
34
|
+
// signal; the `lsof` check is only consulted as a fallback for the case
|
|
35
|
+
// where the host XCTest process disappears without anyone telling us.
|
|
36
|
+
_terminationRequests;
|
|
28
37
|
_terminated;
|
|
29
38
|
constructor(ee, log) {
|
|
30
39
|
this._ee = ee;
|
|
31
40
|
this._log = log;
|
|
32
41
|
this._publishers = new Map();
|
|
42
|
+
this._wakeups = new Map();
|
|
43
|
+
this._terminationRequests = new Set();
|
|
33
44
|
this._terminated = false;
|
|
34
45
|
}
|
|
35
46
|
get hasPublishers() {
|
|
@@ -37,9 +48,29 @@ class NativeVideoChunksBroadcaster {
|
|
|
37
48
|
}
|
|
38
49
|
schedule(uuid) {
|
|
39
50
|
if (!this._publishers.has(uuid)) {
|
|
40
|
-
this._publishers.set(uuid, this.
|
|
51
|
+
this._publishers.set(uuid, this._runPublisher(uuid));
|
|
41
52
|
}
|
|
42
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Signals the publisher for `uuid` that its underlying recording has been
|
|
56
|
+
* stopped. The publisher wakes from its current polling sleep, performs a
|
|
57
|
+
* final stat + read + emit pass to flush any remaining bytes of the
|
|
58
|
+
* recording file to BiDi consumers, and exits.
|
|
59
|
+
*
|
|
60
|
+
* Idempotent and safe to call for an unknown uuid (no-op in that case).
|
|
61
|
+
*
|
|
62
|
+
* Callers SHOULD invoke this immediately after the recording has been
|
|
63
|
+
* stopped on the underlying XCTest side; `waitFor(uuid)` is then
|
|
64
|
+
* contractually guaranteed to resolve as soon as the publisher has
|
|
65
|
+
* finished flushing, without having to wait for the next polling tick.
|
|
66
|
+
*/
|
|
67
|
+
notifyStopped(uuid) {
|
|
68
|
+
if (!this._publishers.has(uuid)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
this._terminationRequests.add(uuid);
|
|
72
|
+
this._wakeups.get(uuid)?.cancel();
|
|
73
|
+
}
|
|
43
74
|
async waitFor(uuid) {
|
|
44
75
|
const publisher = this._publishers.get(uuid);
|
|
45
76
|
if (publisher) {
|
|
@@ -47,18 +78,38 @@ class NativeVideoChunksBroadcaster {
|
|
|
47
78
|
}
|
|
48
79
|
}
|
|
49
80
|
async shutdown(timeoutMs) {
|
|
81
|
+
// Snapshot keys before publishers remove themselves from `_publishers`
|
|
82
|
+
// in `_finishPublisher`, so we still know which attachment files to
|
|
83
|
+
// delete after `_wait` completes.
|
|
84
|
+
const uuidsAtShutdown = [...this._publishers.keys()];
|
|
85
|
+
// Wake up every active publisher so they don't have to wait for their
|
|
86
|
+
// next polling tick before flushing and exiting.
|
|
87
|
+
for (const uuid of uuidsAtShutdown) {
|
|
88
|
+
this.notifyStopped(uuid);
|
|
89
|
+
}
|
|
50
90
|
try {
|
|
51
91
|
await this._wait(timeoutMs);
|
|
52
92
|
}
|
|
53
93
|
catch (e) {
|
|
54
94
|
this._log.warn(e instanceof Error ? e.message : String(e));
|
|
55
95
|
}
|
|
56
|
-
await this._cleanup();
|
|
57
|
-
this._publishers
|
|
96
|
+
await this._cleanup(uuidsAtShutdown);
|
|
97
|
+
this._publishers.clear();
|
|
98
|
+
this._wakeups.clear();
|
|
99
|
+
this._terminationRequests.clear();
|
|
58
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Background monitor for a native screen recording attachment.
|
|
103
|
+
*
|
|
104
|
+
* This is scheduled as a fire-and-forget task by {@link schedule}; the
|
|
105
|
+
* promise is removed from `_publishers` when the monitor exits (see
|
|
106
|
+
* {@link _finishPublisher}). To make sure a failure here can never surface as an
|
|
107
|
+
* unhandled promise rejection (which terminates the Appium server process
|
|
108
|
+
* on Node.js 24+), this method MUST never reject: every error path logs a
|
|
109
|
+
* warning and returns normally.
|
|
110
|
+
*/
|
|
59
111
|
async _createPublisher(uuid) {
|
|
60
112
|
let fullPath = '';
|
|
61
|
-
let bytesRead = 0n;
|
|
62
113
|
try {
|
|
63
114
|
await (0, asyncbox_1.waitForCondition)(async () => {
|
|
64
115
|
const paths = await listAttachments();
|
|
@@ -74,33 +125,78 @@ class NativeVideoChunksBroadcaster {
|
|
|
74
125
|
});
|
|
75
126
|
}
|
|
76
127
|
catch {
|
|
77
|
-
|
|
128
|
+
this._log.warn(`The video recording identified by ${uuid} did not ` +
|
|
78
129
|
`start within ${RECORDING_STARTUP_TIMEOUT_MS}ms timeout`);
|
|
130
|
+
return;
|
|
79
131
|
}
|
|
132
|
+
let bytesRead = 0n;
|
|
80
133
|
const startedMs = performance.now();
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
134
|
+
try {
|
|
135
|
+
while (!this._terminated && performance.now() - startedMs < MAX_MONITORING_DURATION_MS) {
|
|
136
|
+
// Primary completion signal: `notifyStopped(uuid)` was called.
|
|
137
|
+
// Secondary fallback: lsof reports no `testman` process holding
|
|
138
|
+
// the recording file open (e.g. the host process exited without
|
|
139
|
+
// anyone telling us — see appium/appium#22269).
|
|
140
|
+
const stopRequested = this._terminationRequests.has(uuid);
|
|
141
|
+
const isCompleted = stopRequested || !(await isFileUsed(fullPath, 'testman'));
|
|
142
|
+
let size;
|
|
143
|
+
try {
|
|
144
|
+
({ size } = await support_1.fs.stat(fullPath, { bigint: true }));
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
// The recording file may have been removed by the OS once the
|
|
148
|
+
// host XCTest process owning the attachment has exited. In that
|
|
149
|
+
// case there is nothing more to read and we should stop monitoring.
|
|
150
|
+
if (e?.code === 'ENOENT') {
|
|
151
|
+
this._log.debug(`The native video recording file for ${uuid} is no longer available. ` +
|
|
152
|
+
`Assuming the recording has been terminated`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
throw e;
|
|
156
|
+
}
|
|
157
|
+
if (bytesRead < size) {
|
|
158
|
+
const handle = await support_1.fs.open(fullPath, 'r');
|
|
159
|
+
try {
|
|
160
|
+
while (bytesRead < size) {
|
|
161
|
+
const bufferSize = Number(size - bytesRead > BUFFER_SIZE ? BUFFER_SIZE : size - bytesRead);
|
|
162
|
+
const buf = Buffer.alloc(bufferSize);
|
|
163
|
+
await support_1.fs.read(handle, buf, 0, bufferSize, bytesRead);
|
|
164
|
+
this._ee.emit(constants_1.BIDI_EVENT_NAME, (0, models_1.toNativeVideoChunkAddedEvent)(uuid, buf));
|
|
165
|
+
bytesRead += BigInt(bufferSize);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
await support_1.fs.close(handle);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (isCompleted) {
|
|
173
|
+
this._log.debug(`The native video recording identified by ${uuid} has been detected as completed`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
// Cancellable sleep until the next polling tick. `notifyStopped`
|
|
177
|
+
// cancels this wakeup so the loop re-runs immediately and detects
|
|
178
|
+
// termination on the very next iteration. `cancelError: null` makes
|
|
179
|
+
// the cancel resolve the promise instead of rejecting it.
|
|
180
|
+
const wakeup = (0, asyncbox_1.sleep)({ ms: MONITORING_INTERVAL_DURATION_MS, cancelError: null });
|
|
181
|
+
this._wakeups.set(uuid, wakeup);
|
|
86
182
|
try {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
bytesRead += BigInt(bufferSize);
|
|
183
|
+
// Guard against a notifyStopped() that landed between the
|
|
184
|
+
// iteration's stop-check above and the wakeup being stored:
|
|
185
|
+
// the cancel above would have been a no-op, so cancel here.
|
|
186
|
+
if (this._terminationRequests.has(uuid)) {
|
|
187
|
+
wakeup.cancel();
|
|
93
188
|
}
|
|
189
|
+
await wakeup;
|
|
94
190
|
}
|
|
95
191
|
finally {
|
|
96
|
-
|
|
192
|
+
this._wakeups.delete(uuid);
|
|
97
193
|
}
|
|
98
194
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
this._log.warn(`Native video chunks publisher for ${uuid} stopped unexpectedly: ` +
|
|
198
|
+
(e instanceof Error ? e.message : String(e)));
|
|
199
|
+
return;
|
|
104
200
|
}
|
|
105
201
|
this._log.warn(`Stopped monitoring of the native video recording identified by ${uuid} ` +
|
|
106
202
|
`because of the timeout`);
|
|
@@ -112,31 +208,27 @@ class NativeVideoChunksBroadcaster {
|
|
|
112
208
|
const timer = setTimeout(() => {
|
|
113
209
|
this._terminated = true;
|
|
114
210
|
}, timeoutMs);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
catch (e) {
|
|
121
|
-
publishingErrors.push(e instanceof Error ? e.message : String(e));
|
|
122
|
-
}
|
|
211
|
+
try {
|
|
212
|
+
// Publishers are guaranteed by `_createPublisher` to never reject,
|
|
213
|
+
// so we can simply await all of them.
|
|
214
|
+
await Promise.all(this._publishers.values());
|
|
123
215
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
throw new Error(publishingErrors.join('\n'));
|
|
216
|
+
finally {
|
|
217
|
+
clearTimeout(timer);
|
|
127
218
|
}
|
|
128
219
|
}
|
|
129
|
-
async _cleanup() {
|
|
130
|
-
if (
|
|
220
|
+
async _cleanup(uuids) {
|
|
221
|
+
if (uuids.length === 0) {
|
|
131
222
|
return;
|
|
132
223
|
}
|
|
224
|
+
const uuidSet = new Set(uuids);
|
|
133
225
|
const attachments = await listAttachments();
|
|
134
226
|
if (attachments.length === 0) {
|
|
135
227
|
return;
|
|
136
228
|
}
|
|
137
229
|
const tasks = attachments
|
|
138
230
|
.map((attachmentPath) => [node_path_1.default.basename(attachmentPath), attachmentPath])
|
|
139
|
-
.filter(([name]) =>
|
|
231
|
+
.filter(([name]) => uuidSet.has(name))
|
|
140
232
|
.map(([, attachmentPath]) => support_1.fs.rimraf(attachmentPath));
|
|
141
233
|
if (tasks.length === 0) {
|
|
142
234
|
return;
|
|
@@ -149,6 +241,27 @@ class NativeVideoChunksBroadcaster {
|
|
|
149
241
|
this._log.warn(`Could not cleanup some leftover video recordings: ${e instanceof Error ? e.message : String(e)}`);
|
|
150
242
|
}
|
|
151
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Runs {@link _createPublisher} and always clears `_publishers` /
|
|
246
|
+
* `_wakeups` / `_terminationRequests` for `uuid` when the monitor exits.
|
|
247
|
+
*/
|
|
248
|
+
async _runPublisher(uuid) {
|
|
249
|
+
try {
|
|
250
|
+
await this._createPublisher(uuid);
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
this._finishPublisher(uuid);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Drops all bookkeeping for a finished publisher so {@link hasPublishers}
|
|
258
|
+
* reflects reality and `notifyStopped` state does not leak across sessions.
|
|
259
|
+
*/
|
|
260
|
+
_finishPublisher(uuid) {
|
|
261
|
+
this._publishers.delete(uuid);
|
|
262
|
+
this._wakeups.delete(uuid);
|
|
263
|
+
this._terminationRequests.delete(uuid);
|
|
264
|
+
}
|
|
152
265
|
}
|
|
153
266
|
exports.NativeVideoChunksBroadcaster = NativeVideoChunksBroadcaster;
|
|
154
267
|
/**
|
|
@@ -216,13 +329,18 @@ async function macosStopNativeScreenRecording(remotePath, user, pass, method, he
|
|
|
216
329
|
throw new Error('There is no active screen recording, thus nothing to stop. Did you start it before?');
|
|
217
330
|
}
|
|
218
331
|
const { uuid } = response;
|
|
332
|
+
// Wake the publisher up so it stops polling and flushes any remaining
|
|
333
|
+
// bytes immediately. Without this, `waitFor` below could still block
|
|
334
|
+
// for up to `MONITORING_INTERVAL_DURATION_MS` while the publisher sits
|
|
335
|
+
// in its polling sleep.
|
|
336
|
+
this._videoChunksBroadcaster.notifyStopped(uuid);
|
|
219
337
|
try {
|
|
220
|
-
await (0, asyncbox_1.withTimeout)(this._videoChunksBroadcaster.waitFor(uuid),
|
|
338
|
+
await (0, asyncbox_1.withTimeout)(this._videoChunksBroadcaster.waitFor(uuid), STOP_WAIT_TIMEOUT_MS);
|
|
221
339
|
}
|
|
222
340
|
catch (e) {
|
|
223
341
|
if (e instanceof asyncbox_1.TimeoutError) {
|
|
224
342
|
this.log.debug(`The BiDi chunks broadcaster for the native screen recording identified ` +
|
|
225
|
-
`by ${uuid} cannot complete within
|
|
343
|
+
`by ${uuid} cannot complete within ${STOP_WAIT_TIMEOUT_MS}ms timeout`);
|
|
226
344
|
}
|
|
227
345
|
else {
|
|
228
346
|
this.log.debug(e instanceof Error ? e.stack : String(e));
|
|
@@ -268,7 +386,12 @@ async function listAttachments() {
|
|
|
268
386
|
});
|
|
269
387
|
}
|
|
270
388
|
async function isFileUsed(fpath, userProcessName) {
|
|
271
|
-
|
|
272
|
-
|
|
389
|
+
try {
|
|
390
|
+
const { stdout } = await (0, teen_process_1.exec)('lsof', [fpath]);
|
|
391
|
+
return stdout.includes(userProcessName);
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
273
396
|
}
|
|
274
397
|
//# sourceMappingURL=native-record-screen.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-record-screen.js","sourceRoot":"","sources":["../../../lib/commands/native-record-screen.ts"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"native-record-screen.js","sourceRoot":"","sources":["../../../lib/commands/native-record-screen.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {setTimeout as delay} from 'node:timers/promises';
|
|
2
1
|
import path from 'node:path';
|
|
3
2
|
import {fs, util} from 'appium/support';
|
|
4
3
|
import type {Mac2Driver} from '../driver';
|
|
5
4
|
import {uploadRecordedMedia} from './helpers';
|
|
6
5
|
import type {AppiumLogger, StringRecord} from '@appium/types';
|
|
7
6
|
import type EventEmitter from 'node:events';
|
|
8
|
-
import {
|
|
7
|
+
import type {CancellablePromise} from 'asyncbox';
|
|
8
|
+
import {TimeoutError, sleep, waitForCondition, withTimeout} from 'asyncbox';
|
|
9
9
|
import {exec} from 'teen_process';
|
|
10
10
|
import {BIDI_EVENT_NAME} from './bidi/constants';
|
|
11
11
|
import {toNativeVideoChunkAddedEvent} from './bidi/models';
|
|
@@ -15,6 +15,7 @@ const RECORDING_STARTUP_TIMEOUT_MS = 5000;
|
|
|
15
15
|
const BUFFER_SIZE = 0xffff;
|
|
16
16
|
const MONITORING_INTERVAL_DURATION_MS = 1000;
|
|
17
17
|
const MAX_MONITORING_DURATION_MS = 24 * 60 * 60 * 1000; // 1 day
|
|
18
|
+
const STOP_WAIT_TIMEOUT_MS = 5000;
|
|
18
19
|
|
|
19
20
|
interface ActiveVideoInfo {
|
|
20
21
|
fps: number;
|
|
@@ -30,15 +31,26 @@ interface DisplayInfo {
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
export class NativeVideoChunksBroadcaster {
|
|
33
|
-
private _ee: EventEmitter;
|
|
34
|
-
private _log: AppiumLogger;
|
|
35
|
-
private _publishers: Map<string, Promise<void>>;
|
|
34
|
+
private readonly _ee: EventEmitter;
|
|
35
|
+
private readonly _log: AppiumLogger;
|
|
36
|
+
private readonly _publishers: Map<string, Promise<void>>;
|
|
37
|
+
// The currently scheduled polling sleep of each publisher, indexed by uuid.
|
|
38
|
+
// Cancelling it wakes the publisher up before the next polling tick so it
|
|
39
|
+
// can perform a final stat + read + emit pass and exit.
|
|
40
|
+
private readonly _wakeups: Map<string, CancellablePromise<void>>;
|
|
41
|
+
// Set of uuids whose underlying recording has been explicitly stopped via
|
|
42
|
+
// `notifyStopped`. The publisher uses this as the primary completion
|
|
43
|
+
// signal; the `lsof` check is only consulted as a fallback for the case
|
|
44
|
+
// where the host XCTest process disappears without anyone telling us.
|
|
45
|
+
private readonly _terminationRequests: Set<string>;
|
|
36
46
|
private _terminated: boolean;
|
|
37
47
|
|
|
38
48
|
constructor(ee: EventEmitter, log: AppiumLogger) {
|
|
39
49
|
this._ee = ee;
|
|
40
50
|
this._log = log;
|
|
41
51
|
this._publishers = new Map();
|
|
52
|
+
this._wakeups = new Map();
|
|
53
|
+
this._terminationRequests = new Set();
|
|
42
54
|
this._terminated = false;
|
|
43
55
|
}
|
|
44
56
|
|
|
@@ -48,10 +60,31 @@ export class NativeVideoChunksBroadcaster {
|
|
|
48
60
|
|
|
49
61
|
schedule(uuid: string): void {
|
|
50
62
|
if (!this._publishers.has(uuid)) {
|
|
51
|
-
this._publishers.set(uuid, this.
|
|
63
|
+
this._publishers.set(uuid, this._runPublisher(uuid));
|
|
52
64
|
}
|
|
53
65
|
}
|
|
54
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Signals the publisher for `uuid` that its underlying recording has been
|
|
69
|
+
* stopped. The publisher wakes from its current polling sleep, performs a
|
|
70
|
+
* final stat + read + emit pass to flush any remaining bytes of the
|
|
71
|
+
* recording file to BiDi consumers, and exits.
|
|
72
|
+
*
|
|
73
|
+
* Idempotent and safe to call for an unknown uuid (no-op in that case).
|
|
74
|
+
*
|
|
75
|
+
* Callers SHOULD invoke this immediately after the recording has been
|
|
76
|
+
* stopped on the underlying XCTest side; `waitFor(uuid)` is then
|
|
77
|
+
* contractually guaranteed to resolve as soon as the publisher has
|
|
78
|
+
* finished flushing, without having to wait for the next polling tick.
|
|
79
|
+
*/
|
|
80
|
+
notifyStopped(uuid: string): void {
|
|
81
|
+
if (!this._publishers.has(uuid)) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
this._terminationRequests.add(uuid);
|
|
85
|
+
this._wakeups.get(uuid)?.cancel();
|
|
86
|
+
}
|
|
87
|
+
|
|
55
88
|
async waitFor(uuid: string): Promise<void> {
|
|
56
89
|
const publisher = this._publishers.get(uuid);
|
|
57
90
|
if (publisher) {
|
|
@@ -60,20 +93,40 @@ export class NativeVideoChunksBroadcaster {
|
|
|
60
93
|
}
|
|
61
94
|
|
|
62
95
|
async shutdown(timeoutMs: number): Promise<void> {
|
|
96
|
+
// Snapshot keys before publishers remove themselves from `_publishers`
|
|
97
|
+
// in `_finishPublisher`, so we still know which attachment files to
|
|
98
|
+
// delete after `_wait` completes.
|
|
99
|
+
const uuidsAtShutdown = [...this._publishers.keys()];
|
|
100
|
+
// Wake up every active publisher so they don't have to wait for their
|
|
101
|
+
// next polling tick before flushing and exiting.
|
|
102
|
+
for (const uuid of uuidsAtShutdown) {
|
|
103
|
+
this.notifyStopped(uuid);
|
|
104
|
+
}
|
|
63
105
|
try {
|
|
64
106
|
await this._wait(timeoutMs);
|
|
65
107
|
} catch (e) {
|
|
66
108
|
this._log.warn(e instanceof Error ? e.message : String(e));
|
|
67
109
|
}
|
|
68
110
|
|
|
69
|
-
await this._cleanup();
|
|
111
|
+
await this._cleanup(uuidsAtShutdown);
|
|
70
112
|
|
|
71
|
-
this._publishers
|
|
113
|
+
this._publishers.clear();
|
|
114
|
+
this._wakeups.clear();
|
|
115
|
+
this._terminationRequests.clear();
|
|
72
116
|
}
|
|
73
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Background monitor for a native screen recording attachment.
|
|
120
|
+
*
|
|
121
|
+
* This is scheduled as a fire-and-forget task by {@link schedule}; the
|
|
122
|
+
* promise is removed from `_publishers` when the monitor exits (see
|
|
123
|
+
* {@link _finishPublisher}). To make sure a failure here can never surface as an
|
|
124
|
+
* unhandled promise rejection (which terminates the Appium server process
|
|
125
|
+
* on Node.js 24+), this method MUST never reject: every error path logs a
|
|
126
|
+
* warning and returns normally.
|
|
127
|
+
*/
|
|
74
128
|
private async _createPublisher(uuid: string): Promise<void> {
|
|
75
129
|
let fullPath = '';
|
|
76
|
-
let bytesRead = 0n;
|
|
77
130
|
try {
|
|
78
131
|
await waitForCondition(
|
|
79
132
|
async () => {
|
|
@@ -91,42 +144,88 @@ export class NativeVideoChunksBroadcaster {
|
|
|
91
144
|
},
|
|
92
145
|
);
|
|
93
146
|
} catch {
|
|
94
|
-
|
|
147
|
+
this._log.warn(
|
|
95
148
|
`The video recording identified by ${uuid} did not ` +
|
|
96
149
|
`start within ${RECORDING_STARTUP_TIMEOUT_MS}ms timeout`,
|
|
97
150
|
);
|
|
151
|
+
return;
|
|
98
152
|
}
|
|
99
153
|
|
|
154
|
+
let bytesRead = 0n;
|
|
100
155
|
const startedMs = performance.now();
|
|
101
|
-
|
|
102
|
-
|
|
156
|
+
try {
|
|
157
|
+
while (!this._terminated && performance.now() - startedMs < MAX_MONITORING_DURATION_MS) {
|
|
158
|
+
// Primary completion signal: `notifyStopped(uuid)` was called.
|
|
159
|
+
// Secondary fallback: lsof reports no `testman` process holding
|
|
160
|
+
// the recording file open (e.g. the host process exited without
|
|
161
|
+
// anyone telling us — see appium/appium#22269).
|
|
162
|
+
const stopRequested = this._terminationRequests.has(uuid);
|
|
163
|
+
const isCompleted = stopRequested || !(await isFileUsed(fullPath, 'testman'));
|
|
103
164
|
|
|
104
|
-
|
|
105
|
-
if (bytesRead < size) {
|
|
106
|
-
const handle = await fs.open(fullPath, 'r');
|
|
165
|
+
let size: bigint;
|
|
107
166
|
try {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
167
|
+
({size} = await fs.stat(fullPath, {bigint: true}));
|
|
168
|
+
} catch (e) {
|
|
169
|
+
// The recording file may have been removed by the OS once the
|
|
170
|
+
// host XCTest process owning the attachment has exited. In that
|
|
171
|
+
// case there is nothing more to read and we should stop monitoring.
|
|
172
|
+
if ((e as NodeJS.ErrnoException)?.code === 'ENOENT') {
|
|
173
|
+
this._log.debug(
|
|
174
|
+
`The native video recording file for ${uuid} is no longer available. ` +
|
|
175
|
+
`Assuming the recording has been terminated`,
|
|
111
176
|
);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
throw e;
|
|
180
|
+
}
|
|
181
|
+
if (bytesRead < size) {
|
|
182
|
+
const handle = await fs.open(fullPath, 'r');
|
|
183
|
+
try {
|
|
184
|
+
while (bytesRead < size) {
|
|
185
|
+
const bufferSize = Number(
|
|
186
|
+
size - bytesRead > BUFFER_SIZE ? BUFFER_SIZE : size - bytesRead,
|
|
187
|
+
);
|
|
188
|
+
const buf = Buffer.alloc(bufferSize);
|
|
189
|
+
await fs.read(handle, buf as any, 0, bufferSize, bytesRead as any);
|
|
190
|
+
this._ee.emit(BIDI_EVENT_NAME, toNativeVideoChunkAddedEvent(uuid, buf));
|
|
191
|
+
bytesRead += BigInt(bufferSize);
|
|
192
|
+
}
|
|
193
|
+
} finally {
|
|
194
|
+
await fs.close(handle);
|
|
116
195
|
}
|
|
117
|
-
} finally {
|
|
118
|
-
await fs.close(handle);
|
|
119
196
|
}
|
|
120
|
-
}
|
|
121
197
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
198
|
+
if (isCompleted) {
|
|
199
|
+
this._log.debug(
|
|
200
|
+
`The native video recording identified by ${uuid} has been detected as completed`,
|
|
201
|
+
);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
128
204
|
|
|
129
|
-
|
|
205
|
+
// Cancellable sleep until the next polling tick. `notifyStopped`
|
|
206
|
+
// cancels this wakeup so the loop re-runs immediately and detects
|
|
207
|
+
// termination on the very next iteration. `cancelError: null` makes
|
|
208
|
+
// the cancel resolve the promise instead of rejecting it.
|
|
209
|
+
const wakeup = sleep({ms: MONITORING_INTERVAL_DURATION_MS, cancelError: null});
|
|
210
|
+
this._wakeups.set(uuid, wakeup);
|
|
211
|
+
try {
|
|
212
|
+
// Guard against a notifyStopped() that landed between the
|
|
213
|
+
// iteration's stop-check above and the wakeup being stored:
|
|
214
|
+
// the cancel above would have been a no-op, so cancel here.
|
|
215
|
+
if (this._terminationRequests.has(uuid)) {
|
|
216
|
+
wakeup.cancel();
|
|
217
|
+
}
|
|
218
|
+
await wakeup;
|
|
219
|
+
} finally {
|
|
220
|
+
this._wakeups.delete(uuid);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
} catch (e) {
|
|
224
|
+
this._log.warn(
|
|
225
|
+
`Native video chunks publisher for ${uuid} stopped unexpectedly: ` +
|
|
226
|
+
(e instanceof Error ? e.message : String(e)),
|
|
227
|
+
);
|
|
228
|
+
return;
|
|
130
229
|
}
|
|
131
230
|
|
|
132
231
|
this._log.warn(
|
|
@@ -143,25 +242,20 @@ export class NativeVideoChunksBroadcaster {
|
|
|
143
242
|
const timer = setTimeout(() => {
|
|
144
243
|
this._terminated = true;
|
|
145
244
|
}, timeoutMs);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
clearTimeout(timer);
|
|
155
|
-
|
|
156
|
-
if (publishingErrors.length > 0) {
|
|
157
|
-
throw new Error(publishingErrors.join('\n'));
|
|
245
|
+
try {
|
|
246
|
+
// Publishers are guaranteed by `_createPublisher` to never reject,
|
|
247
|
+
// so we can simply await all of them.
|
|
248
|
+
await Promise.all(this._publishers.values());
|
|
249
|
+
} finally {
|
|
250
|
+
clearTimeout(timer);
|
|
158
251
|
}
|
|
159
252
|
}
|
|
160
253
|
|
|
161
|
-
private async _cleanup(): Promise<void> {
|
|
162
|
-
if (
|
|
254
|
+
private async _cleanup(uuids: readonly string[]): Promise<void> {
|
|
255
|
+
if (uuids.length === 0) {
|
|
163
256
|
return;
|
|
164
257
|
}
|
|
258
|
+
const uuidSet = new Set(uuids);
|
|
165
259
|
|
|
166
260
|
const attachments = await listAttachments();
|
|
167
261
|
if (attachments.length === 0) {
|
|
@@ -169,7 +263,7 @@ export class NativeVideoChunksBroadcaster {
|
|
|
169
263
|
}
|
|
170
264
|
const tasks: Promise<any>[] = attachments
|
|
171
265
|
.map((attachmentPath) => [path.basename(attachmentPath), attachmentPath])
|
|
172
|
-
.filter(([name]) =>
|
|
266
|
+
.filter(([name]) => uuidSet.has(name))
|
|
173
267
|
.map(([, attachmentPath]) => fs.rimraf(attachmentPath));
|
|
174
268
|
if (tasks.length === 0) {
|
|
175
269
|
return;
|
|
@@ -185,6 +279,28 @@ export class NativeVideoChunksBroadcaster {
|
|
|
185
279
|
);
|
|
186
280
|
}
|
|
187
281
|
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Runs {@link _createPublisher} and always clears `_publishers` /
|
|
285
|
+
* `_wakeups` / `_terminationRequests` for `uuid` when the monitor exits.
|
|
286
|
+
*/
|
|
287
|
+
private async _runPublisher(uuid: string): Promise<void> {
|
|
288
|
+
try {
|
|
289
|
+
await this._createPublisher(uuid);
|
|
290
|
+
} finally {
|
|
291
|
+
this._finishPublisher(uuid);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Drops all bookkeeping for a finished publisher so {@link hasPublishers}
|
|
297
|
+
* reflects reality and `notifyStopped` state does not leak across sessions.
|
|
298
|
+
*/
|
|
299
|
+
private _finishPublisher(uuid: string): void {
|
|
300
|
+
this._publishers.delete(uuid);
|
|
301
|
+
this._wakeups.delete(uuid);
|
|
302
|
+
this._terminationRequests.delete(uuid);
|
|
303
|
+
}
|
|
188
304
|
}
|
|
189
305
|
|
|
190
306
|
/**
|
|
@@ -278,13 +394,18 @@ export async function macosStopNativeScreenRecording(
|
|
|
278
394
|
}
|
|
279
395
|
|
|
280
396
|
const {uuid} = response;
|
|
397
|
+
// Wake the publisher up so it stops polling and flushes any remaining
|
|
398
|
+
// bytes immediately. Without this, `waitFor` below could still block
|
|
399
|
+
// for up to `MONITORING_INTERVAL_DURATION_MS` while the publisher sits
|
|
400
|
+
// in its polling sleep.
|
|
401
|
+
this._videoChunksBroadcaster.notifyStopped(uuid);
|
|
281
402
|
try {
|
|
282
|
-
await withTimeout(this._videoChunksBroadcaster.waitFor(uuid),
|
|
403
|
+
await withTimeout(this._videoChunksBroadcaster.waitFor(uuid), STOP_WAIT_TIMEOUT_MS);
|
|
283
404
|
} catch (e) {
|
|
284
405
|
if (e instanceof TimeoutError) {
|
|
285
406
|
this.log.debug(
|
|
286
407
|
`The BiDi chunks broadcaster for the native screen recording identified ` +
|
|
287
|
-
`by ${uuid} cannot complete within
|
|
408
|
+
`by ${uuid} cannot complete within ${STOP_WAIT_TIMEOUT_MS}ms timeout`,
|
|
288
409
|
);
|
|
289
410
|
} else {
|
|
290
411
|
this.log.debug(e instanceof Error ? e.stack : String(e));
|
|
@@ -341,6 +462,10 @@ async function listAttachments(): Promise<string[]> {
|
|
|
341
462
|
}
|
|
342
463
|
|
|
343
464
|
async function isFileUsed(fpath: string, userProcessName: string): Promise<boolean> {
|
|
344
|
-
|
|
345
|
-
|
|
465
|
+
try {
|
|
466
|
+
const {stdout} = await exec('lsof', [fpath]);
|
|
467
|
+
return stdout.includes(userProcessName);
|
|
468
|
+
} catch {
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
346
471
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium-mac2-driver",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "appium-mac2-driver",
|
|
9
|
-
"version": "3.4.
|
|
9
|
+
"version": "3.4.2",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@appium/strongbox": "^1.0.0-rc.1",
|
|
13
13
|
"@colors/colors": "^1.6.0",
|
|
14
14
|
"appium-xcode": "^6.0.0",
|
|
15
|
-
"asyncbox": "^6.0
|
|
16
|
-
"axios": "^1.
|
|
17
|
-
"portscanner": "2.2.0",
|
|
15
|
+
"asyncbox": "^6.3.0",
|
|
16
|
+
"axios": "^1.16.0",
|
|
17
|
+
"portscanner": "^2.2.0",
|
|
18
18
|
"teen_process": "^4.0.4"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
@@ -138,6 +138,19 @@
|
|
|
138
138
|
"sharp": "0.34.5"
|
|
139
139
|
}
|
|
140
140
|
},
|
|
141
|
+
"node_modules/@appium/support/node_modules/asyncbox": {
|
|
142
|
+
"version": "6.2.0",
|
|
143
|
+
"resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-6.2.0.tgz",
|
|
144
|
+
"integrity": "sha512-z1XpHkoT3y+1aXfazEY5d7HN2eOi50fLq7ZTxG0H4WegLxrtEAI5Vsc6OR9dOwoC3SJQLXyV0ZVnPEh6GIgMKQ==",
|
|
145
|
+
"license": "Apache-2.0",
|
|
146
|
+
"dependencies": {
|
|
147
|
+
"p-limit": "^7.2.0"
|
|
148
|
+
},
|
|
149
|
+
"engines": {
|
|
150
|
+
"node": "^20.19.0 || ^22.12.0 || >=24.0.0",
|
|
151
|
+
"npm": ">=10"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
141
154
|
"node_modules/@appium/tsconfig": {
|
|
142
155
|
"version": "1.1.2",
|
|
143
156
|
"resolved": "https://registry.npmjs.org/@appium/tsconfig/-/tsconfig-1.1.2.tgz",
|
|
@@ -556,9 +569,9 @@
|
|
|
556
569
|
"license": "MIT"
|
|
557
570
|
},
|
|
558
571
|
"node_modules/asyncbox": {
|
|
559
|
-
"version": "6.
|
|
560
|
-
"resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-6.
|
|
561
|
-
"integrity": "sha512-
|
|
572
|
+
"version": "6.3.0",
|
|
573
|
+
"resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-6.3.0.tgz",
|
|
574
|
+
"integrity": "sha512-7IFpnQDltd5rYQjhIJIpyismJtdWmw/pOABZKJfv2WVo0a6iYh2ZzUuCJJclae5mBtK0H/EychxXg91GB7rGdQ==",
|
|
562
575
|
"license": "Apache-2.0",
|
|
563
576
|
"dependencies": {
|
|
564
577
|
"p-limit": "^7.2.0"
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"mac",
|
|
7
7
|
"XCTest"
|
|
8
8
|
],
|
|
9
|
-
"version": "3.4.
|
|
9
|
+
"version": "3.4.2",
|
|
10
10
|
"author": "Appium Contributors",
|
|
11
11
|
"license": "Apache-2.0",
|
|
12
12
|
"repository": {
|
|
@@ -49,9 +49,9 @@
|
|
|
49
49
|
"@appium/strongbox": "^1.0.0-rc.1",
|
|
50
50
|
"@colors/colors": "^1.6.0",
|
|
51
51
|
"appium-xcode": "^6.0.0",
|
|
52
|
-
"asyncbox": "^6.0
|
|
53
|
-
"axios": "^1.
|
|
54
|
-
"portscanner": "2.2.0",
|
|
52
|
+
"asyncbox": "^6.3.0",
|
|
53
|
+
"axios": "^1.16.0",
|
|
54
|
+
"portscanner": "^2.2.0",
|
|
55
55
|
"teen_process": "^4.0.4"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|