@taqwright/taqwright 0.0.24

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.
Files changed (132) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +108 -0
  3. package/dist/auto-appium.d.ts +12 -0
  4. package/dist/auto-appium.js +77 -0
  5. package/dist/bin/branding.d.ts +6 -0
  6. package/dist/bin/branding.js +22 -0
  7. package/dist/bin/index.d.ts +2 -0
  8. package/dist/bin/index.js +321 -0
  9. package/dist/bin/init.d.ts +26 -0
  10. package/dist/bin/init.js +902 -0
  11. package/dist/bin/inspect.d.ts +9 -0
  12. package/dist/bin/inspect.js +91 -0
  13. package/dist/bin/report-branding.d.ts +2 -0
  14. package/dist/bin/report-branding.js +42 -0
  15. package/dist/branding-assets.d.ts +1 -0
  16. package/dist/branding-assets.js +1 -0
  17. package/dist/capabilities-helpers.d.ts +7 -0
  18. package/dist/capabilities-helpers.js +14 -0
  19. package/dist/capabilities.d.ts +6 -0
  20. package/dist/capabilities.js +86 -0
  21. package/dist/config.d.ts +17 -0
  22. package/dist/config.js +235 -0
  23. package/dist/discovery-setup.d.ts +1 -0
  24. package/dist/discovery-setup.js +61 -0
  25. package/dist/discovery.d.ts +17 -0
  26. package/dist/discovery.js +55 -0
  27. package/dist/docs/configuration.html +376 -0
  28. package/dist/docs/custom-reporters.html +265 -0
  29. package/dist/docs/docker.html +339 -0
  30. package/dist/docs/docs.js +173 -0
  31. package/dist/docs/generating-tests.html +161 -0
  32. package/dist/docs/images/taqwright-html-report.png +0 -0
  33. package/dist/docs/index.html +13 -0
  34. package/dist/docs/installation.html +686 -0
  35. package/dist/docs/parallel.html +271 -0
  36. package/dist/docs/running-tests.html +385 -0
  37. package/dist/docs/styles.css +460 -0
  38. package/dist/docs/writing-tests.html +565 -0
  39. package/dist/doctor.d.ts +33 -0
  40. package/dist/doctor.js +508 -0
  41. package/dist/expect.d.ts +38 -0
  42. package/dist/expect.js +96 -0
  43. package/dist/fixture/artifact-mode.d.ts +2 -0
  44. package/dist/fixture/artifact-mode.js +7 -0
  45. package/dist/fixture/index.d.ts +15 -0
  46. package/dist/fixture/index.js +324 -0
  47. package/dist/images/taqwright-html-report.png +0 -0
  48. package/dist/images/taqwright_favicon.png +0 -0
  49. package/dist/images/taqwright_logo.png +0 -0
  50. package/dist/index.d.ts +9 -0
  51. package/dist/index.js +7 -0
  52. package/dist/inspector/codegen-appium.d.ts +3 -0
  53. package/dist/inspector/codegen-appium.js +228 -0
  54. package/dist/inspector/devices.d.ts +41 -0
  55. package/dist/inspector/devices.js +422 -0
  56. package/dist/inspector/locator-suggester.d.ts +23 -0
  57. package/dist/inspector/locator-suggester.js +539 -0
  58. package/dist/inspector/recorder.d.ts +128 -0
  59. package/dist/inspector/recorder.js +162 -0
  60. package/dist/inspector/server.d.ts +39 -0
  61. package/dist/inspector/server.js +1210 -0
  62. package/dist/inspector/session.d.ts +84 -0
  63. package/dist/inspector/session.js +262 -0
  64. package/dist/inspector/ui.d.ts +1 -0
  65. package/dist/inspector/ui.js +5508 -0
  66. package/dist/keys.d.ts +3 -0
  67. package/dist/keys.js +28 -0
  68. package/dist/locator/index.d.ts +206 -0
  69. package/dist/locator/index.js +1506 -0
  70. package/dist/logger.d.ts +5 -0
  71. package/dist/logger.js +5 -0
  72. package/dist/mobile/index.d.ts +130 -0
  73. package/dist/mobile/index.js +762 -0
  74. package/dist/network/android.d.ts +5 -0
  75. package/dist/network/android.js +87 -0
  76. package/dist/network/ca.d.ts +10 -0
  77. package/dist/network/ca.js +136 -0
  78. package/dist/network/har.d.ts +90 -0
  79. package/dist/network/har.js +101 -0
  80. package/dist/network/host-proxy.d.ts +16 -0
  81. package/dist/network/host-proxy.js +134 -0
  82. package/dist/network/index.d.ts +26 -0
  83. package/dist/network/index.js +105 -0
  84. package/dist/network/ios-sim.d.ts +3 -0
  85. package/dist/network/ios-sim.js +29 -0
  86. package/dist/network/proxy.d.ts +13 -0
  87. package/dist/network/proxy.js +310 -0
  88. package/dist/providers/appium.d.ts +23 -0
  89. package/dist/providers/appium.js +288 -0
  90. package/dist/providers/browserstack/index.d.ts +5 -0
  91. package/dist/providers/browserstack/index.js +77 -0
  92. package/dist/providers/browserstack/utils.d.ts +1 -0
  93. package/dist/providers/browserstack/utils.js +6 -0
  94. package/dist/providers/cloud.d.ts +53 -0
  95. package/dist/providers/cloud.js +117 -0
  96. package/dist/providers/emulator/index.d.ts +8 -0
  97. package/dist/providers/emulator/index.js +47 -0
  98. package/dist/providers/index.d.ts +10 -0
  99. package/dist/providers/index.js +33 -0
  100. package/dist/providers/lambdatest/index.d.ts +28 -0
  101. package/dist/providers/lambdatest/index.js +99 -0
  102. package/dist/providers/lambdatest/utils.d.ts +1 -0
  103. package/dist/providers/lambdatest/utils.js +6 -0
  104. package/dist/providers/local/index.d.ts +9 -0
  105. package/dist/providers/local/index.js +53 -0
  106. package/dist/providers/local-session.d.ts +16 -0
  107. package/dist/providers/local-session.js +55 -0
  108. package/dist/setup/archive.d.ts +2 -0
  109. package/dist/setup/archive.js +43 -0
  110. package/dist/setup/avd.d.ts +12 -0
  111. package/dist/setup/avd.js +103 -0
  112. package/dist/setup/index.d.ts +6 -0
  113. package/dist/setup/index.js +55 -0
  114. package/dist/setup/install-android.d.ts +2 -0
  115. package/dist/setup/install-android.js +70 -0
  116. package/dist/setup/install-appium.d.ts +1 -0
  117. package/dist/setup/install-appium.js +64 -0
  118. package/dist/setup/install-jdk.d.ts +1 -0
  119. package/dist/setup/install-jdk.js +58 -0
  120. package/dist/setup/paths.d.ts +16 -0
  121. package/dist/setup/paths.js +88 -0
  122. package/dist/setup/spawn-tool.d.ts +3 -0
  123. package/dist/setup/spawn-tool.js +11 -0
  124. package/dist/tracer/index.d.ts +34 -0
  125. package/dist/tracer/index.js +687 -0
  126. package/dist/tracer/proxy.d.ts +3 -0
  127. package/dist/tracer/proxy.js +60 -0
  128. package/dist/types/index.d.ts +189 -0
  129. package/dist/types/index.js +6 -0
  130. package/dist/utils.d.ts +2 -0
  131. package/dist/utils.js +37 -0
  132. package/package.json +79 -0
@@ -0,0 +1,105 @@
1
+ import { startProxy } from './proxy.js';
2
+ import { ensureCa } from './ca.js';
3
+ import { detectAndroidDeviceType, installSystemCa, setHttpProxy, clearHttpProxy, } from './android.js';
4
+ import { isSimulatorUdid, waitForBoot, installRootCert } from './ios-sim.js';
5
+ import { snapshotProxy, applyProxy, restoreProxy } from './host-proxy.js';
6
+ import { Platform } from '../types/index.js';
7
+ const BASE_PROXY_PORT = 8090;
8
+ const EMULATOR_HOST_IP = '10.0.2.2';
9
+ export async function prepareNetworkProxy(opts) {
10
+ const port = BASE_PROXY_PORT + opts.parallelIndex;
11
+ const ca = await ensureCa();
12
+ const proxy = await startProxy({ port, ca });
13
+ const handle = {
14
+ port,
15
+ attemptLog: proxy.attemptLog,
16
+ flush: () => proxy.flush(),
17
+ _proxy: proxy,
18
+ _ca: ca,
19
+ _platform: opts.platform,
20
+ };
21
+ if (opts.platform === Platform.IOS) {
22
+ const state = await snapshotProxy();
23
+ if (state) {
24
+ handle._hostProxyState = state;
25
+ await applyProxy(state, '127.0.0.1', port);
26
+ }
27
+ else {
28
+ proxy.attemptLog.push('network: networksetup unavailable — host proxy not set');
29
+ }
30
+ }
31
+ return handle;
32
+ }
33
+ export async function configureDeviceForCapture(handle, opts) {
34
+ if (!opts.udid) {
35
+ handle.attemptLog.push('network: no device udid available — skipping device config');
36
+ return;
37
+ }
38
+ if (opts.platform === Platform.ANDROID) {
39
+ const type = await detectAndroidDeviceType(opts.udid);
40
+ if (type === 'real-device') {
41
+ handle.attemptLog.push('network: skipped (real device — not supported in v1)');
42
+ return;
43
+ }
44
+ if (type === 'play-emulator') {
45
+ handle.attemptLog.push('network: skipped (Play AVD, release-keys)');
46
+ return;
47
+ }
48
+ if (type !== 'userdebug-emulator') {
49
+ handle.attemptLog.push(`network: skipped (unknown Android device type "${type}")`);
50
+ return;
51
+ }
52
+ const installed = await installSystemCa(opts.udid, handle._ca.certPemPath, handle._ca.androidHashName);
53
+ if (!installed) {
54
+ handle.attemptLog.push('network: failed to install CA on AVD — HTTPS bodies will be missing');
55
+ return;
56
+ }
57
+ try {
58
+ await setHttpProxy(opts.udid, EMULATOR_HOST_IP, handle.port);
59
+ handle._configuredUdid = opts.udid;
60
+ handle._configuredAndroid = true;
61
+ }
62
+ catch (e) {
63
+ handle.attemptLog.push(`network: failed to set device proxy: ${e.message}`);
64
+ }
65
+ return;
66
+ }
67
+ if (!isSimulatorUdid(opts.udid)) {
68
+ handle.attemptLog.push('network: skipped (real device — not supported in v1)');
69
+ return;
70
+ }
71
+ const booted = await waitForBoot(opts.udid);
72
+ if (!booted) {
73
+ handle.attemptLog.push('network: simulator did not finish booting in time');
74
+ return;
75
+ }
76
+ const certInstalled = await installRootCert(opts.udid, handle._ca.certPemPath);
77
+ if (!certInstalled) {
78
+ handle.attemptLog.push('network: failed to add root cert to sim keychain');
79
+ return;
80
+ }
81
+ handle._configuredUdid = opts.udid;
82
+ }
83
+ export async function teardownDeviceCapture(handle) {
84
+ if (handle._configuredAndroid && handle._configuredUdid) {
85
+ await clearHttpProxy(handle._configuredUdid).catch(() => undefined);
86
+ handle._configuredAndroid = false;
87
+ }
88
+ }
89
+ export async function teardownNetworkProxy(handle) {
90
+ if (handle._platform === Platform.IOS) {
91
+ await restoreProxy().catch(() => undefined);
92
+ }
93
+ await handle._proxy.stop().catch(() => undefined);
94
+ }
95
+ export function extractUdid(driverCapabilities) {
96
+ const c = driverCapabilities;
97
+ if (!c)
98
+ return undefined;
99
+ for (const key of ['udid', 'deviceUDID', 'appium:udid', 'appium:deviceUDID']) {
100
+ const v = c[key];
101
+ if (typeof v === 'string' && v.length > 0)
102
+ return v;
103
+ }
104
+ return undefined;
105
+ }
@@ -0,0 +1,3 @@
1
+ export declare function isSimulatorUdid(udid: string): boolean;
2
+ export declare function waitForBoot(udid: string, timeoutMs?: number): Promise<boolean>;
3
+ export declare function installRootCert(udid: string, certPemPath: string): Promise<boolean>;
@@ -0,0 +1,29 @@
1
+ import { exec } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ const execP = promisify(exec);
4
+ export function isSimulatorUdid(udid) {
5
+ return /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/.test(udid);
6
+ }
7
+ export async function waitForBoot(udid, timeoutMs = 60_000) {
8
+ try {
9
+ await execP(`xcrun simctl bootstatus ${shellQuote(udid)} -b`, { timeout: timeoutMs });
10
+ return true;
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
16
+ export async function installRootCert(udid, certPemPath) {
17
+ try {
18
+ await execP(`xcrun simctl keychain ${shellQuote(udid)} add-root-cert ${shellQuote(certPemPath)}`, { timeout: 15_000 });
19
+ return true;
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
25
+ function shellQuote(s) {
26
+ if (/^[A-Za-z0-9_\-./=:,@%+]+$/.test(s))
27
+ return s;
28
+ return `'${s.replace(/'/g, `'\\''`)}'`;
29
+ }
@@ -0,0 +1,13 @@
1
+ import { type CaBundle } from './ca.js';
2
+ import { type HarLog } from './har.js';
3
+ export interface ProxyHandle {
4
+ port: number;
5
+ attemptLog: string[];
6
+ flush(): Promise<HarLog>;
7
+ stop(): Promise<void>;
8
+ }
9
+ export interface ProxyOptions {
10
+ port: number;
11
+ ca: CaBundle;
12
+ }
13
+ export declare function startProxy(opts: ProxyOptions): Promise<ProxyHandle>;
@@ -0,0 +1,310 @@
1
+ import http from 'node:http';
2
+ import https from 'node:https';
3
+ import net from 'node:net';
4
+ import tls from 'node:tls';
5
+ import { createHarBuilder } from './har.js';
6
+ const MAX_BODY_CAPTURE = 1 * 1024 * 1024;
7
+ const PINNING_THRESHOLD = 3;
8
+ const TEXT_MIME = /^(text\/|application\/(json|xml|x-www-form-urlencoded|javascript)|application\/[\w.+-]*\+(json|xml))/i;
9
+ export async function startProxy(opts) {
10
+ const har = createHarBuilder({ creator: 'taqwright', version: '0.0.1' });
11
+ const attemptLog = [];
12
+ const tlsFailCount = new Map();
13
+ const pinnedHosts = new Set();
14
+ const server = http.createServer((req, res) => {
15
+ handleHttp(req, res, har).catch((err) => {
16
+ attemptLog.push(`http ${req.url ?? '?'}: ${err.message}`);
17
+ safeEnd(res, 502);
18
+ });
19
+ });
20
+ server.on('connect', (req, clientSocket, head) => {
21
+ handleConnect({
22
+ req,
23
+ clientSocket: clientSocket,
24
+ head,
25
+ ca: opts.ca,
26
+ har,
27
+ tlsFailCount,
28
+ pinnedHosts,
29
+ attemptLog,
30
+ });
31
+ });
32
+ server.on('clientError', (_err, socket) => {
33
+ try {
34
+ socket.destroy();
35
+ }
36
+ catch {
37
+ }
38
+ });
39
+ await new Promise((resolve, reject) => {
40
+ server.once('error', reject);
41
+ server.listen(opts.port, '127.0.0.1', () => resolve());
42
+ });
43
+ return {
44
+ port: opts.port,
45
+ attemptLog,
46
+ async flush() {
47
+ for (const line of attemptLog)
48
+ har.addComment(line);
49
+ return har.toJson();
50
+ },
51
+ async stop() {
52
+ await new Promise((resolve) => {
53
+ server.closeAllConnections?.();
54
+ server.close(() => resolve());
55
+ });
56
+ },
57
+ };
58
+ }
59
+ async function handleHttp(req, res, har) {
60
+ const url = req.url ?? '';
61
+ let parsed;
62
+ try {
63
+ parsed = new URL(url);
64
+ }
65
+ catch {
66
+ safeEnd(res, 400);
67
+ return;
68
+ }
69
+ const startedAt = new Date();
70
+ const upstream = http.request({
71
+ host: parsed.hostname,
72
+ port: parsed.port || 80,
73
+ method: req.method,
74
+ path: parsed.pathname + parsed.search,
75
+ headers: sanitizeRequestHeaders(req.headers, parsed.host),
76
+ });
77
+ await pipeRequestThrough(req, upstream, parsed.toString(), har, startedAt, res);
78
+ }
79
+ function handleConnect(a) {
80
+ const [host, portStr] = (a.req.url ?? '').split(':');
81
+ const port = Number.parseInt(portStr ?? '443', 10) || 443;
82
+ if (!host) {
83
+ a.clientSocket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
84
+ return;
85
+ }
86
+ if (a.pinnedHosts.has(host)) {
87
+ blindTunnel(a.clientSocket, a.head, host, port);
88
+ return;
89
+ }
90
+ a.clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
91
+ const leaf = a.ca.signLeaf(host);
92
+ const tlsSocket = new tls.TLSSocket(a.clientSocket, {
93
+ isServer: true,
94
+ key: leaf.keyPem,
95
+ cert: leaf.certPem,
96
+ ALPNProtocols: ['http/1.1'],
97
+ });
98
+ let sawHttpByte = false;
99
+ tlsSocket.on('data', () => {
100
+ sawHttpByte = true;
101
+ });
102
+ tlsSocket.on('error', (_err) => {
103
+ if (!sawHttpByte) {
104
+ const count = (a.tlsFailCount.get(host) ?? 0) + 1;
105
+ a.tlsFailCount.set(host, count);
106
+ if (count >= PINNING_THRESHOLD && !a.pinnedHosts.has(host)) {
107
+ a.pinnedHosts.add(host);
108
+ a.attemptLog.push(`pinned: ${host}`);
109
+ }
110
+ }
111
+ try {
112
+ tlsSocket.destroy();
113
+ }
114
+ catch {
115
+ }
116
+ });
117
+ const parser = http.createServer((req2, res2) => {
118
+ handleDecryptedRequest(req2, res2, host, port, a.har).catch((err) => {
119
+ a.attemptLog.push(`https ${host}${req2.url ?? ''}: ${err.message}`);
120
+ safeEnd(res2, 502);
121
+ });
122
+ });
123
+ parser.emit('connection', tlsSocket);
124
+ }
125
+ function blindTunnel(clientSocket, head, host, port) {
126
+ const upstream = net.connect(port, host, () => {
127
+ clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
128
+ if (head.length > 0)
129
+ upstream.write(head);
130
+ clientSocket.pipe(upstream).pipe(clientSocket);
131
+ });
132
+ upstream.on('error', () => {
133
+ try {
134
+ clientSocket.end('HTTP/1.1 502 Bad Gateway\r\n\r\n');
135
+ }
136
+ catch {
137
+ }
138
+ });
139
+ clientSocket.on('error', () => {
140
+ try {
141
+ upstream.destroy();
142
+ }
143
+ catch {
144
+ }
145
+ });
146
+ }
147
+ async function handleDecryptedRequest(req, res, host, port, har) {
148
+ const startedAt = new Date();
149
+ const url = `https://${host}${port === 443 ? '' : `:${port}`}${req.url ?? '/'}`;
150
+ const upstream = https.request({
151
+ host,
152
+ port,
153
+ method: req.method,
154
+ path: req.url,
155
+ headers: sanitizeRequestHeaders(req.headers, host + (port === 443 ? '' : `:${port}`)),
156
+ servername: host,
157
+ });
158
+ await pipeRequestThrough(req, upstream, url, har, startedAt, res);
159
+ }
160
+ async function pipeRequestThrough(req, upstream, url, har, startedAt, res) {
161
+ const reqChunks = [];
162
+ let reqBytes = 0;
163
+ let reqCapped = false;
164
+ req.on('data', (chunk) => {
165
+ reqBytes += chunk.length;
166
+ if (!reqCapped) {
167
+ if (reqBytes <= MAX_BODY_CAPTURE)
168
+ reqChunks.push(chunk);
169
+ else
170
+ reqCapped = true;
171
+ }
172
+ upstream.write(chunk);
173
+ });
174
+ req.on('end', () => upstream.end());
175
+ req.on('error', () => {
176
+ try {
177
+ upstream.destroy();
178
+ }
179
+ catch {
180
+ }
181
+ });
182
+ let upstreamRes;
183
+ try {
184
+ upstreamRes = await new Promise((resolve, reject) => {
185
+ upstream.once('response', resolve);
186
+ upstream.once('error', reject);
187
+ });
188
+ }
189
+ catch (err) {
190
+ const entry = har.startEntry({
191
+ method: req.method ?? 'GET',
192
+ url,
193
+ httpVersion: `HTTP/${req.httpVersion}`,
194
+ headers: headerObjectToArray(req.headers),
195
+ bodyBytes: reqBytes || -1,
196
+ startedAt,
197
+ });
198
+ entry.onError(err.message);
199
+ safeEnd(res, 502);
200
+ return;
201
+ }
202
+ const entry = har.startEntry({
203
+ method: req.method ?? 'GET',
204
+ url,
205
+ httpVersion: `HTTP/${req.httpVersion}`,
206
+ headers: headerObjectToArray(req.headers),
207
+ bodyBytes: reqBytes || -1,
208
+ bodyText: tryDecodeText(Buffer.concat(reqChunks), getHeader(req.headers, 'content-type')),
209
+ startedAt,
210
+ });
211
+ res.statusCode = upstreamRes.statusCode ?? 0;
212
+ res.statusMessage = upstreamRes.statusMessage ?? '';
213
+ for (const [k, v] of Object.entries(upstreamRes.headers)) {
214
+ if (v === undefined)
215
+ continue;
216
+ try {
217
+ res.setHeader(k, v);
218
+ }
219
+ catch {
220
+ }
221
+ }
222
+ const resChunks = [];
223
+ let resBytes = 0;
224
+ let resCapped = false;
225
+ upstreamRes.on('data', (chunk) => {
226
+ resBytes += chunk.length;
227
+ if (!resCapped) {
228
+ if (resBytes <= MAX_BODY_CAPTURE)
229
+ resChunks.push(chunk);
230
+ else
231
+ resCapped = true;
232
+ }
233
+ res.write(chunk);
234
+ });
235
+ await new Promise((resolve) => {
236
+ upstreamRes.on('end', () => {
237
+ res.end();
238
+ resolve();
239
+ });
240
+ upstreamRes.on('error', () => {
241
+ res.end();
242
+ resolve();
243
+ });
244
+ });
245
+ entry.onResponse({
246
+ status: upstreamRes.statusCode ?? 0,
247
+ statusText: upstreamRes.statusMessage ?? '',
248
+ httpVersion: `HTTP/${upstreamRes.httpVersion}`,
249
+ headers: headerObjectToArray(upstreamRes.headers),
250
+ mimeType: getHeader(upstreamRes.headers, 'content-type') ?? '',
251
+ bodyBytes: resBytes,
252
+ bodyText: tryDecodeText(Buffer.concat(resChunks), getHeader(upstreamRes.headers, 'content-type')),
253
+ endedAt: new Date(),
254
+ });
255
+ }
256
+ function headerObjectToArray(h) {
257
+ const out = [];
258
+ for (const [name, value] of Object.entries(h)) {
259
+ if (value === undefined)
260
+ continue;
261
+ if (Array.isArray(value)) {
262
+ for (const v of value)
263
+ out.push({ name, value: v });
264
+ }
265
+ else {
266
+ out.push({ name, value: String(value) });
267
+ }
268
+ }
269
+ return out;
270
+ }
271
+ function getHeader(h, name) {
272
+ const v = h[name.toLowerCase()];
273
+ if (v === undefined)
274
+ return undefined;
275
+ return Array.isArray(v) ? v[0] : v;
276
+ }
277
+ function sanitizeRequestHeaders(h, host) {
278
+ const out = {};
279
+ for (const [name, value] of Object.entries(h)) {
280
+ if (value === undefined)
281
+ continue;
282
+ const lower = name.toLowerCase();
283
+ if (lower === 'proxy-connection' || lower === 'proxy-authorization')
284
+ continue;
285
+ out[name] = value;
286
+ }
287
+ out.host = host;
288
+ return out;
289
+ }
290
+ function tryDecodeText(buf, contentType) {
291
+ if (buf.length === 0)
292
+ return undefined;
293
+ if (!contentType || !TEXT_MIME.test(contentType))
294
+ return undefined;
295
+ try {
296
+ return buf.toString('utf-8');
297
+ }
298
+ catch {
299
+ return undefined;
300
+ }
301
+ }
302
+ function safeEnd(res, status) {
303
+ try {
304
+ if (!res.headersSent)
305
+ res.statusCode = status;
306
+ res.end();
307
+ }
308
+ catch {
309
+ }
310
+ }
@@ -0,0 +1,23 @@
1
+ import { type ChildProcess } from 'node:child_process';
2
+ import { Platform } from '../types/index.js';
3
+ export declare function installDriver(driverName: string, opts?: {
4
+ appiumPath?: string;
5
+ env?: NodeJS.ProcessEnv;
6
+ }): Promise<void>;
7
+ export interface AppiumServerSpawnOptions {
8
+ host?: string;
9
+ port?: number;
10
+ basePath?: string;
11
+ }
12
+ export declare function startAppiumServer(provider: string, opts?: AppiumServerSpawnOptions, avdName?: string): Promise<ChildProcess>;
13
+ export declare function stopAppiumServer(): Promise<string>;
14
+ export declare function killAppiumOnPort(port: number): Promise<boolean>;
15
+ export declare function isEmulatorInstalled(platform: Platform): Promise<boolean>;
16
+ export declare function startAndroidEmulator(avdName?: string, androidHome?: string | undefined): Promise<void>;
17
+ export declare function getAppBundleId(buildPath: string): Promise<string>;
18
+ export declare function getConnectedIOSDeviceUDID(): Promise<string>;
19
+ export declare function getActiveAndroidDevices(): Promise<number>;
20
+ export declare function getApkDetails(buildPath: string): Promise<{
21
+ packageName: string;
22
+ launchableActivity: string;
23
+ }>;