appium-remote-debugger 15.9.0 → 15.10.0
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/mixins/connect.js +4 -4
- package/build/lib/mixins/connect.js.map +1 -1
- package/build/lib/mixins/execute.d.ts.map +1 -1
- package/build/lib/mixins/execute.js +2 -2
- package/build/lib/mixins/execute.js.map +1 -1
- package/build/lib/mixins/message-handlers.d.ts.map +1 -1
- package/build/lib/mixins/message-handlers.js +3 -2
- package/build/lib/mixins/message-handlers.js.map +1 -1
- package/build/lib/mixins/misc.d.ts +2 -1
- package/build/lib/mixins/misc.d.ts.map +1 -1
- package/build/lib/mixins/misc.js +5 -4
- package/build/lib/mixins/misc.js.map +1 -1
- package/build/lib/mixins/navigate.d.ts.map +1 -1
- package/build/lib/mixins/navigate.js +3 -2
- package/build/lib/mixins/navigate.js.map +1 -1
- package/build/lib/remote-debugger-real-device.js.map +1 -1
- package/build/lib/remote-debugger.d.ts.map +1 -1
- package/build/lib/remote-debugger.js +5 -7
- package/build/lib/remote-debugger.js.map +1 -1
- package/build/lib/rpc/rpc-client-real-device-shim.js +3 -3
- package/build/lib/rpc/rpc-client-real-device-shim.js.map +1 -1
- package/build/lib/rpc/rpc-client.d.ts.map +1 -1
- package/build/lib/rpc/rpc-client.js +16 -16
- package/build/lib/rpc/rpc-client.js.map +1 -1
- package/build/lib/rpc/rpc-message-handler.d.ts.map +1 -1
- package/build/lib/rpc/rpc-message-handler.js +3 -4
- package/build/lib/rpc/rpc-message-handler.js.map +1 -1
- package/build/lib/utils/async.d.ts +11 -0
- package/build/lib/utils/async.d.ts.map +1 -0
- package/build/lib/utils/async.js +28 -0
- package/build/lib/utils/async.js.map +1 -0
- package/build/lib/utils/errors.d.ts +13 -0
- package/build/lib/utils/errors.d.ts.map +1 -0
- package/build/lib/utils/errors.js +24 -0
- package/build/lib/utils/errors.js.map +1 -0
- package/build/lib/utils/index.d.ts +9 -0
- package/build/lib/utils/index.d.ts.map +1 -0
- package/build/lib/utils/index.js +27 -0
- package/build/lib/utils/index.js.map +1 -0
- package/build/lib/utils/inspector.d.ts +30 -0
- package/build/lib/utils/inspector.d.ts.map +1 -0
- package/build/lib/utils/inspector.js +91 -0
- package/build/lib/utils/inspector.js.map +1 -0
- package/build/lib/utils/javascript.d.ts +25 -0
- package/build/lib/utils/javascript.d.ts.map +1 -0
- package/build/lib/utils/javascript.js +90 -0
- package/build/lib/utils/javascript.js.map +1 -0
- package/build/lib/utils/module.d.ts +20 -0
- package/build/lib/utils/module.d.ts.map +1 -0
- package/build/lib/utils/module.js +36 -0
- package/build/lib/utils/module.js.map +1 -0
- package/build/lib/utils/object.d.ts +29 -0
- package/build/lib/utils/object.d.ts.map +1 -0
- package/build/lib/utils/object.js +54 -0
- package/build/lib/utils/object.js.map +1 -0
- package/build/lib/utils/platform.d.ts +7 -0
- package/build/lib/utils/platform.d.ts.map +1 -0
- package/build/lib/utils/platform.js +13 -0
- package/build/lib/utils/platform.js.map +1 -0
- package/lib/appium-ios-device.d.ts +12 -0
- package/lib/mixins/connect.ts +6 -6
- package/lib/mixins/execute.ts +2 -3
- package/lib/mixins/message-handlers.ts +4 -3
- package/lib/mixins/misc.ts +6 -5
- package/lib/mixins/navigate.ts +8 -3
- package/lib/remote-debugger-real-device.ts +1 -1
- package/lib/remote-debugger.ts +6 -8
- package/lib/rpc/rpc-client-real-device-shim.ts +3 -3
- package/lib/rpc/rpc-client.ts +16 -23
- package/lib/rpc/rpc-message-handler.ts +3 -4
- package/lib/utils/async.ts +32 -0
- package/lib/utils/errors.ts +19 -0
- package/lib/utils/index.ts +18 -0
- package/lib/utils/inspector.ts +99 -0
- package/lib/utils/javascript.ts +89 -0
- package/lib/utils/module.ts +33 -0
- package/lib/utils/object.ts +55 -0
- package/lib/utils/platform.ts +10 -0
- package/package.json +9 -3
- package/build/lib/utils.d.ts +0 -167
- package/build/lib/utils.d.ts.map +0 -1
- package/build/lib/utils.js +0 -422
- package/build/lib/utils.js.map +0 -1
- package/lib/utils.ts +0 -443
package/lib/utils.ts
DELETED
|
@@ -1,443 +0,0 @@
|
|
|
1
|
-
import {errorFromMJSONWPStatusCode} from '@appium/base-driver';
|
|
2
|
-
import {util, node} from '@appium/support';
|
|
3
|
-
import {isDeepStrictEqual} from 'node:util';
|
|
4
|
-
import nodeFs from 'node:fs';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import type {StringRecord} from '@appium/types';
|
|
7
|
-
import type {AppInfo, AppDict, Page} from './types';
|
|
8
|
-
|
|
9
|
-
const MODULE_NAME = 'appium-remote-debugger';
|
|
10
|
-
export const WEB_CONTENT_BUNDLE_ID = 'com.apple.WebKit.WebContent';
|
|
11
|
-
const INACTIVE_APP_CODE = 0;
|
|
12
|
-
// values for the page `WIRTypeKey` entry
|
|
13
|
-
const ACCEPTED_PAGE_TYPES = [
|
|
14
|
-
'WIRTypeWeb', // up to iOS 11.3
|
|
15
|
-
'WIRTypeWebPage', // iOS 11.4
|
|
16
|
-
'WIRTypePage', // iOS 11.4 webview
|
|
17
|
-
];
|
|
18
|
-
export const RESPONSE_LOG_LENGTH = 100;
|
|
19
|
-
|
|
20
|
-
export type CancellableDelay = Promise<void> & {
|
|
21
|
-
cancel: () => void;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Thrown when a cancellable delay is cancelled.
|
|
26
|
-
*/
|
|
27
|
-
export class DelayCancellation extends Error {
|
|
28
|
-
constructor(message: string = 'Delay cancelled') {
|
|
29
|
-
super(message);
|
|
30
|
-
this.name = 'DelayCancellation';
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Error thrown when an async operation exceeds the configured timeout.
|
|
36
|
-
*/
|
|
37
|
-
export class TimeoutError extends Error {
|
|
38
|
-
constructor(message: string = 'Operation timed out') {
|
|
39
|
-
super(message);
|
|
40
|
-
this.name = 'TimeoutError';
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Truncates a string to the requested length and appends ellipsis when needed.
|
|
46
|
-
*
|
|
47
|
-
* @param value - The input string.
|
|
48
|
-
* @param length - Maximum output length.
|
|
49
|
-
* @returns The original string when short enough, otherwise a truncated variant.
|
|
50
|
-
*/
|
|
51
|
-
export function truncateString(value: string, length: number): string {
|
|
52
|
-
if (value.length <= length) {
|
|
53
|
-
return value;
|
|
54
|
-
}
|
|
55
|
-
return `${value.slice(0, Math.max(0, length - 1))}…`;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Creates a shallow object where undefined keys from `target` are filled
|
|
60
|
-
* from `defaultsObj`.
|
|
61
|
-
*
|
|
62
|
-
* @param target - The object with priority values.
|
|
63
|
-
* @param defaultsObj - The object providing fallback values.
|
|
64
|
-
* @returns A new object containing merged defaulted values.
|
|
65
|
-
*/
|
|
66
|
-
export function defaults<T extends Record<string, any>, U extends Record<string, any>>(
|
|
67
|
-
target: T,
|
|
68
|
-
defaultsObj: U,
|
|
69
|
-
): T & U {
|
|
70
|
-
const result = {...target} as T & U;
|
|
71
|
-
for (const [key, value] of Object.entries(defaultsObj)) {
|
|
72
|
-
if (result[key as keyof (T & U)] === undefined) {
|
|
73
|
-
(result as any)[key] = value;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return result;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Determines whether a value is a plain object.
|
|
81
|
-
*
|
|
82
|
-
* @param value - The value to check.
|
|
83
|
-
* @returns True when the value is a non-null non-array object.
|
|
84
|
-
*/
|
|
85
|
-
export function isPlainObject(value: unknown): value is Record<string, any> {
|
|
86
|
-
if (value == null || typeof value !== 'object' || Array.isArray(value)) {
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
const prototype = Object.getPrototypeOf(value);
|
|
90
|
-
return prototype === Object.prototype || prototype === null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Checks whether a value should be treated as empty.
|
|
95
|
-
*
|
|
96
|
-
* @param value - The value to evaluate.
|
|
97
|
-
* @returns True for nullish values, empty arrays/strings/maps/sets, or empty objects.
|
|
98
|
-
*/
|
|
99
|
-
export function isEmpty(value: unknown): boolean {
|
|
100
|
-
if (value == null) {
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
103
|
-
if (Array.isArray(value) || typeof value === 'string') {
|
|
104
|
-
return value.length === 0;
|
|
105
|
-
}
|
|
106
|
-
if (value instanceof Map || value instanceof Set) {
|
|
107
|
-
return value.size === 0;
|
|
108
|
-
}
|
|
109
|
-
if (isPlainObject(value)) {
|
|
110
|
-
return Object.keys(value).length === 0;
|
|
111
|
-
}
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Deduplicates array entries while preserving order.
|
|
117
|
-
*
|
|
118
|
-
* @param items - Items to deduplicate.
|
|
119
|
-
* @returns The input items without duplicates.
|
|
120
|
-
*/
|
|
121
|
-
export function uniq<T>(items: T[]): T[] {
|
|
122
|
-
return [...new Set(items)];
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Performs deep strict equality comparison.
|
|
127
|
-
*
|
|
128
|
-
* @param a - First value.
|
|
129
|
-
* @param b - Second value.
|
|
130
|
-
* @returns True when both values are deeply equal.
|
|
131
|
-
*/
|
|
132
|
-
export function deepEqual(a: unknown, b: unknown): boolean {
|
|
133
|
-
return isDeepStrictEqual(a, b);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Returns a promise that resolves after the specified delay.
|
|
138
|
-
*
|
|
139
|
-
* @param ms - Delay in milliseconds.
|
|
140
|
-
* @returns A promise that resolves when the delay expires.
|
|
141
|
-
*/
|
|
142
|
-
export function delay(ms: number): Promise<void> {
|
|
143
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Returns a delay promise with a `cancel` method that rejects the promise.
|
|
148
|
-
*
|
|
149
|
-
* @param ms - Delay in milliseconds.
|
|
150
|
-
* @returns A cancellable delay promise.
|
|
151
|
-
*/
|
|
152
|
-
export function cancellableDelay(ms: number): CancellableDelay {
|
|
153
|
-
let timeoutId: NodeJS.Timeout | undefined;
|
|
154
|
-
let rejectFn: ((error: Error) => void) | undefined;
|
|
155
|
-
|
|
156
|
-
const promise = new Promise<void>((resolve, reject) => {
|
|
157
|
-
rejectFn = reject;
|
|
158
|
-
timeoutId = setTimeout(resolve, ms);
|
|
159
|
-
}) as CancellableDelay;
|
|
160
|
-
|
|
161
|
-
promise.cancel = () => {
|
|
162
|
-
if (timeoutId) {
|
|
163
|
-
clearTimeout(timeoutId);
|
|
164
|
-
timeoutId = undefined;
|
|
165
|
-
}
|
|
166
|
-
rejectFn?.(new DelayCancellation());
|
|
167
|
-
rejectFn = undefined;
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
return promise;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Wraps a promise with a timeout.
|
|
175
|
-
*
|
|
176
|
-
* @param promise - The promise to resolve.
|
|
177
|
-
* @param timeoutMs - Maximum time to wait in milliseconds.
|
|
178
|
-
* @param message - Optional timeout message.
|
|
179
|
-
* @returns A promise that resolves/rejects with the original promise result, or rejects on timeout.
|
|
180
|
-
*/
|
|
181
|
-
export async function withTimeout<T>(
|
|
182
|
-
promise: Promise<T>,
|
|
183
|
-
timeoutMs: number,
|
|
184
|
-
message?: string,
|
|
185
|
-
): Promise<T> {
|
|
186
|
-
let timeoutId: NodeJS.Timeout | undefined;
|
|
187
|
-
try {
|
|
188
|
-
return await Promise.race([
|
|
189
|
-
promise,
|
|
190
|
-
new Promise<T>((_resolve, reject) => {
|
|
191
|
-
timeoutId = setTimeout(
|
|
192
|
-
() => reject(new TimeoutError(message ?? `Operation timed out after ${timeoutMs}ms`)),
|
|
193
|
-
timeoutMs,
|
|
194
|
-
);
|
|
195
|
-
}),
|
|
196
|
-
]);
|
|
197
|
-
} finally {
|
|
198
|
-
if (timeoutId) {
|
|
199
|
-
clearTimeout(timeoutId);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Takes a dictionary from the remote debugger and converts it into a more
|
|
206
|
-
* manageable AppInfo object with understandable keys.
|
|
207
|
-
*
|
|
208
|
-
* @param dict - Dictionary from the remote debugger containing application information.
|
|
209
|
-
* @returns A tuple containing the application ID and the AppInfo object.
|
|
210
|
-
*/
|
|
211
|
-
export function appInfoFromDict(dict: Record<string, any>): [string, AppInfo] {
|
|
212
|
-
const id = dict.WIRApplicationIdentifierKey;
|
|
213
|
-
const isProxy =
|
|
214
|
-
typeof dict.WIRIsApplicationProxyKey === 'string'
|
|
215
|
-
? dict.WIRIsApplicationProxyKey.toLowerCase() === 'true'
|
|
216
|
-
: dict.WIRIsApplicationProxyKey;
|
|
217
|
-
// automation enabled can be either from the keys
|
|
218
|
-
// - WIRRemoteAutomationEnabledKey (boolean)
|
|
219
|
-
// - WIRAutomationAvailabilityKey (string or boolean)
|
|
220
|
-
let isAutomationEnabled: boolean | string = !!dict.WIRRemoteAutomationEnabledKey;
|
|
221
|
-
if (Object.hasOwn(dict, 'WIRAutomationAvailabilityKey')) {
|
|
222
|
-
if (typeof dict.WIRAutomationAvailabilityKey === 'string') {
|
|
223
|
-
isAutomationEnabled =
|
|
224
|
-
dict.WIRAutomationAvailabilityKey === 'WIRAutomationAvailabilityUnknown'
|
|
225
|
-
? 'Unknown'
|
|
226
|
-
: dict.WIRAutomationAvailabilityKey === 'WIRAutomationAvailabilityAvailable';
|
|
227
|
-
} else {
|
|
228
|
-
isAutomationEnabled = !!dict.WIRAutomationAvailabilityKey;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
const entry: AppInfo = {
|
|
232
|
-
id,
|
|
233
|
-
isProxy,
|
|
234
|
-
name: dict.WIRApplicationNameKey,
|
|
235
|
-
bundleId: dict.WIRApplicationBundleIdentifierKey,
|
|
236
|
-
hostId: dict.WIRHostApplicationIdentifierKey,
|
|
237
|
-
isActive: dict.WIRIsApplicationActiveKey !== INACTIVE_APP_CODE,
|
|
238
|
-
isAutomationEnabled,
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
return [id, entry];
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Takes a dictionary from the remote debugger and converts it into an array
|
|
246
|
-
* of Page objects with understandable keys. Filters out non-web pages.
|
|
247
|
-
*
|
|
248
|
-
* @param pageDict - Dictionary from the remote debugger containing page information.
|
|
249
|
-
* @returns An array of Page objects representing the available pages.
|
|
250
|
-
*/
|
|
251
|
-
export function pageArrayFromDict(pageDict: StringRecord): Page[] {
|
|
252
|
-
return (
|
|
253
|
-
Object.values(pageDict)
|
|
254
|
-
// count only WIRTypeWeb pages and ignore all others (WIRTypeJavaScript etc)
|
|
255
|
-
.filter(
|
|
256
|
-
(dict) => dict.WIRTypeKey === undefined || ACCEPTED_PAGE_TYPES.includes(dict.WIRTypeKey),
|
|
257
|
-
)
|
|
258
|
-
.map((dict) => ({
|
|
259
|
-
id: dict.WIRPageIdentifierKey,
|
|
260
|
-
title: dict.WIRTitleKey,
|
|
261
|
-
url: dict.WIRURLKey,
|
|
262
|
-
isKey: dict.WIRConnectionIdentifierKey !== undefined,
|
|
263
|
-
}))
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Finds all application identifier keys that match the given bundle ID.
|
|
269
|
-
* If no matches are found and the bundle ID is not WEB_CONTENT_BUNDLE_ID,
|
|
270
|
-
* falls back to searching for WEB_CONTENT_BUNDLE_ID.
|
|
271
|
-
*
|
|
272
|
-
* @param bundleId - The bundle identifier to search for.
|
|
273
|
-
* @param appDict - The application dictionary to search in.
|
|
274
|
-
* @returns An array of unique application identifier keys matching the bundle ID.
|
|
275
|
-
*/
|
|
276
|
-
export function appIdsForBundle(bundleId: string, appDict: AppDict): string[] {
|
|
277
|
-
const appIds: string[] = Object.entries(appDict)
|
|
278
|
-
.filter(([, data]) => data.bundleId === bundleId)
|
|
279
|
-
.map(([key]) => key);
|
|
280
|
-
|
|
281
|
-
// if nothing is found, try to get the generic app
|
|
282
|
-
if (appIds.length === 0 && bundleId !== WEB_CONTENT_BUNDLE_ID) {
|
|
283
|
-
return appIdsForBundle(WEB_CONTENT_BUNDLE_ID, appDict);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
return uniq(appIds);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Validates that all parameters in the provided object have non-nil values.
|
|
291
|
-
* Throws an error if any parameters are missing (null or undefined).
|
|
292
|
-
*
|
|
293
|
-
* @template T - The type of the parameters object.
|
|
294
|
-
* @param params - An object containing parameters to validate.
|
|
295
|
-
* @returns The same parameters object if all values are valid.
|
|
296
|
-
* @throws Error if any parameters are missing, listing all missing parameter names.
|
|
297
|
-
*/
|
|
298
|
-
export function checkParams<T extends StringRecord>(params: T): T {
|
|
299
|
-
// check if all parameters have a value
|
|
300
|
-
const errors = Object.entries(params)
|
|
301
|
-
.filter(([, value]) => value == null)
|
|
302
|
-
.map(([param]) => param);
|
|
303
|
-
if (errors.length) {
|
|
304
|
-
throw new Error(`Missing ${util.pluralize('parameter', errors.length)}: ${errors.join(', ')}`);
|
|
305
|
-
}
|
|
306
|
-
return params;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Converts a value to a best-effort JSON string for logging, removing noisy
|
|
311
|
-
* function properties from cloneable objects when possible.
|
|
312
|
-
*
|
|
313
|
-
* Falls back to `String(value)` when JSON serialization returns `undefined`
|
|
314
|
-
* or throws (for example, for functions, symbols, or circular structures).
|
|
315
|
-
*
|
|
316
|
-
* @param value - The value to stringify.
|
|
317
|
-
* @param multiline - If true, formats JSON output with indentation. Defaults to false.
|
|
318
|
-
* @returns A string representation suitable for logging.
|
|
319
|
-
*/
|
|
320
|
-
export function simpleStringify(value: any, multiline: boolean = false): string {
|
|
321
|
-
const stringify = (val: any): string => {
|
|
322
|
-
try {
|
|
323
|
-
return multiline
|
|
324
|
-
? (JSON.stringify(val, null, 2) ?? String(val))
|
|
325
|
-
: (JSON.stringify(val) ?? String(val));
|
|
326
|
-
} catch {
|
|
327
|
-
return String(val);
|
|
328
|
-
}
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
if (!value) {
|
|
332
|
-
return stringify(value);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
let cleanValue = value;
|
|
336
|
-
if (typeof value === 'object' && value !== null) {
|
|
337
|
-
try {
|
|
338
|
-
cleanValue = removeNoisyProperties(structuredClone(value));
|
|
339
|
-
} catch {
|
|
340
|
-
// Fall back to the original value when cloning fails (e.g., non-cloneable graph entries).
|
|
341
|
-
cleanValue = value;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
return stringify(cleanValue);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Converts the result from a JavaScript evaluation in the remote debugger
|
|
349
|
-
* into a usable format. Handles errors, serialization, and cleans up noisy
|
|
350
|
-
* function properties.
|
|
351
|
-
*
|
|
352
|
-
* @param res - The raw result from the remote debugger's JavaScript evaluation.
|
|
353
|
-
* @returns The cleaned and converted result value.
|
|
354
|
-
* @throws Error if the result is undefined, has an unexpected type, or contains
|
|
355
|
-
* an error status code.
|
|
356
|
-
*/
|
|
357
|
-
export function convertJavascriptEvaluationResult(res: any): any {
|
|
358
|
-
if (res === undefined) {
|
|
359
|
-
throw new Error(
|
|
360
|
-
`Did not get OK result from remote debugger. Result was: ${truncateString(simpleStringify(res), RESPONSE_LOG_LENGTH)}`,
|
|
361
|
-
);
|
|
362
|
-
} else if (typeof res === 'string') {
|
|
363
|
-
try {
|
|
364
|
-
res = JSON.parse(res);
|
|
365
|
-
} catch {
|
|
366
|
-
// we might get a serialized object, but we might not
|
|
367
|
-
// if we get here, it is just a value
|
|
368
|
-
}
|
|
369
|
-
} else if ((typeof res !== 'object' && typeof res !== 'function') || res === null) {
|
|
370
|
-
throw new Error(`Result has unexpected type: (${typeof res}).`);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (Object.hasOwn(res, 'status') && res.status !== 0) {
|
|
374
|
-
// we got some form of error.
|
|
375
|
-
throw errorFromMJSONWPStatusCode(res.status, res.value.message || res.value);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// with either have an object with a `value` property (even if `null`),
|
|
379
|
-
// or a plain object
|
|
380
|
-
const value = Object.hasOwn(res, 'value') ? res.value : res;
|
|
381
|
-
return removeNoisyProperties(value);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Calculates the path to the current module's root folder.
|
|
386
|
-
* The result is memoized for performance.
|
|
387
|
-
*
|
|
388
|
-
* @returns The full path to the module root directory.
|
|
389
|
-
* @throws Error if the module root folder cannot be determined.
|
|
390
|
-
*/
|
|
391
|
-
let cachedModuleRoot: string | undefined;
|
|
392
|
-
/**
|
|
393
|
-
* Calculates and memoizes the path to the current module root.
|
|
394
|
-
*
|
|
395
|
-
* @returns The full path to the module root directory.
|
|
396
|
-
* @throws Error if the module root folder cannot be determined.
|
|
397
|
-
*/
|
|
398
|
-
export function getModuleRoot(): string {
|
|
399
|
-
if (cachedModuleRoot) {
|
|
400
|
-
return cachedModuleRoot;
|
|
401
|
-
}
|
|
402
|
-
const root = node.getModuleRootSync(MODULE_NAME, __filename);
|
|
403
|
-
if (!root) {
|
|
404
|
-
throw new Error(`Cannot find the root folder of the ${MODULE_NAME} Node.js module`);
|
|
405
|
-
}
|
|
406
|
-
cachedModuleRoot = root;
|
|
407
|
-
return root;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Reads and parses the package.json file from the module root.
|
|
412
|
-
*
|
|
413
|
-
* @returns The parsed package.json contents as a StringRecord.
|
|
414
|
-
*/
|
|
415
|
-
export function getModuleProperties(): StringRecord {
|
|
416
|
-
const fullPath = path.resolve(getModuleRoot(), 'package.json');
|
|
417
|
-
return JSON.parse(nodeFs.readFileSync(fullPath, 'utf8'));
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Determines if the WebInspector shim can be used based on the provided iOS platform version.
|
|
422
|
-
* @param platformVersion - The iOS platform version string (e.g., "18.0", "17.5.1")
|
|
423
|
-
* @returns true if the WebInspector shim can be used, false otherwise
|
|
424
|
-
*/
|
|
425
|
-
export function canUseWebInspectorShim(platformVersion: string): boolean {
|
|
426
|
-
return !!platformVersion && util.compareVersions(platformVersion, '>=', '18.0');
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* Removes noisy function properties from an object that can muddy the logs.
|
|
431
|
-
* These properties are often added by JavaScript number objects and similar.
|
|
432
|
-
*
|
|
433
|
-
* @param obj - The object to clean.
|
|
434
|
-
* @returns The cleaned object.
|
|
435
|
-
*/
|
|
436
|
-
function removeNoisyProperties<T>(obj: T): T {
|
|
437
|
-
if (obj && typeof obj === 'object') {
|
|
438
|
-
for (const property of ['ceil', 'clone', 'floor', 'round', 'scale', 'toString']) {
|
|
439
|
-
delete obj[property];
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
return obj;
|
|
443
|
-
}
|