appium-ios-remotexpc 5.2.2 → 5.4.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/src/index.d.ts +6 -1
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +3 -0
- package/build/src/index.js.map +1 -1
- package/build/src/lib/plist/binary-plist-parser.d.ts.map +1 -1
- package/build/src/lib/plist/binary-plist-parser.js +17 -0
- package/build/src/lib/plist/binary-plist-parser.js.map +1 -1
- package/build/src/lib/remote-xpc/constants.d.ts +2 -2
- package/build/src/lib/remote-xpc/constants.d.ts.map +1 -1
- package/build/src/lib/remote-xpc/constants.js +6 -2
- package/build/src/lib/remote-xpc/constants.js.map +1 -1
- package/build/src/lib/types.d.ts +144 -0
- package/build/src/lib/types.d.ts.map +1 -1
- package/build/src/services/index.d.ts +2 -1
- package/build/src/services/index.d.ts.map +1 -1
- package/build/src/services/index.js +2 -1
- package/build/src/services/index.js.map +1 -1
- package/build/src/services/ios/app-service/index.d.ts +150 -0
- package/build/src/services/ios/app-service/index.d.ts.map +1 -0
- package/build/src/services/ios/app-service/index.js +151 -0
- package/build/src/services/ios/app-service/index.js.map +1 -0
- package/build/src/services/ios/core-device/core-device-service.d.ts +70 -0
- package/build/src/services/ios/core-device/core-device-service.d.ts.map +1 -0
- package/build/src/services/ios/core-device/core-device-service.js +262 -0
- package/build/src/services/ios/core-device/core-device-service.js.map +1 -0
- package/build/src/services/ios/dvt/instruments/device-info.d.ts +15 -0
- package/build/src/services/ios/dvt/instruments/device-info.d.ts.map +1 -1
- package/build/src/services/ios/dvt/instruments/device-info.js +30 -0
- package/build/src/services/ios/dvt/instruments/device-info.js.map +1 -1
- package/build/src/services/ios/dvt/instruments/sysmontap.d.ts +115 -0
- package/build/src/services/ios/dvt/instruments/sysmontap.d.ts.map +1 -0
- package/build/src/services/ios/dvt/instruments/sysmontap.js +259 -0
- package/build/src/services/ios/dvt/instruments/sysmontap.js.map +1 -0
- package/build/src/services/ios/dvt/nskeyedarchiver-decoder.d.ts.map +1 -1
- package/build/src/services/ios/dvt/nskeyedarchiver-decoder.js +7 -2
- package/build/src/services/ios/dvt/nskeyedarchiver-decoder.js.map +1 -1
- package/build/src/services/ios/hid-indigo/index.d.ts +3 -8
- package/build/src/services/ios/hid-indigo/index.d.ts.map +1 -1
- package/build/src/services/ios/hid-indigo/index.js +6 -33
- package/build/src/services/ios/hid-indigo/index.js.map +1 -1
- package/build/src/services.d.ts +8 -0
- package/build/src/services.d.ts.map +1 -1
- package/build/src/services.js +13 -0
- package/build/src/services.js.map +1 -1
- package/package.json +3 -1
- package/src/index.ts +19 -0
- package/src/lib/plist/binary-plist-parser.ts +19 -0
- package/src/lib/remote-xpc/constants.ts +6 -2
- package/src/lib/types.ts +158 -0
- package/src/services/index.ts +2 -0
- package/src/services/ios/app-service/index.ts +280 -0
- package/src/services/ios/core-device/core-device-service.ts +368 -0
- package/src/services/ios/dvt/instruments/device-info.ts +46 -0
- package/src/services/ios/dvt/instruments/sysmontap.ts +308 -0
- package/src/services/ios/dvt/nskeyedarchiver-decoder.ts +9 -2
- package/src/services/ios/hid-indigo/index.ts +6 -46
- package/src/services.ts +14 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { XPCDictionary } from '../../../lib/types.js';
|
|
2
|
+
import { type CoreDeviceInvokeOptions, CoreDeviceService } from '../core-device/core-device-service.js';
|
|
3
|
+
/** A process as reported by the device (pid + executable location). */
|
|
4
|
+
export interface AppServiceProcessToken {
|
|
5
|
+
processIdentifier: number;
|
|
6
|
+
executableURL?: {
|
|
7
|
+
relative?: string;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
};
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* An installed application entry. Only the most commonly used fields are typed;
|
|
14
|
+
* the device returns additional metadata accessible via the index signature.
|
|
15
|
+
*/
|
|
16
|
+
export interface InstalledApp {
|
|
17
|
+
bundleIdentifier?: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
version?: string;
|
|
20
|
+
path?: string;
|
|
21
|
+
isRemovable?: boolean;
|
|
22
|
+
isInternal?: boolean;
|
|
23
|
+
isHidden?: boolean;
|
|
24
|
+
isAppClip?: boolean;
|
|
25
|
+
isDefault?: boolean;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
export interface ListAppsOptions {
|
|
29
|
+
includeAppClips?: boolean;
|
|
30
|
+
includeRemovableApps?: boolean;
|
|
31
|
+
includeHiddenApps?: boolean;
|
|
32
|
+
includeInternalApps?: boolean;
|
|
33
|
+
includeDefaultApps?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Restrict results to apps the caller has container access to. Required by
|
|
36
|
+
* iOS 26+; defaults to false (list all apps).
|
|
37
|
+
*/
|
|
38
|
+
requireContainerAccess?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Include each app's app-group identifiers in the response. Required key on
|
|
41
|
+
* iOS 26+; defaults to false.
|
|
42
|
+
*/
|
|
43
|
+
includeAppGroupIdentifiers?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Include each app's on-device container paths in the response. Required key
|
|
46
|
+
* on iOS 26+; defaults to false.
|
|
47
|
+
*/
|
|
48
|
+
includeContainerPaths?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Override the default invocation timeout (ms). Recommended on iOS 26+, where
|
|
51
|
+
* the device may not respond to a full app enumeration over this path.
|
|
52
|
+
*/
|
|
53
|
+
timeoutMs?: number;
|
|
54
|
+
}
|
|
55
|
+
export interface LaunchApplicationOptions {
|
|
56
|
+
/** Launch arguments passed to the application. */
|
|
57
|
+
arguments?: string[];
|
|
58
|
+
/** Environment variables to inject. */
|
|
59
|
+
environment?: Record<string, string>;
|
|
60
|
+
/** Start the process suspended (stopped) instead of running. */
|
|
61
|
+
startSuspended?: boolean;
|
|
62
|
+
/** Terminate an already-running instance before launching. Defaults to true. */
|
|
63
|
+
terminateExisting?: boolean;
|
|
64
|
+
/** Extra platform-specific options serialized into a plist. */
|
|
65
|
+
platformSpecificOptions?: Record<string, unknown>;
|
|
66
|
+
/** Override the default invocation timeout. */
|
|
67
|
+
timeoutMs?: number;
|
|
68
|
+
}
|
|
69
|
+
/** Result of launching an application. */
|
|
70
|
+
export interface LaunchedApplication {
|
|
71
|
+
/** The launched process token (pid + executable location). */
|
|
72
|
+
processToken?: AppServiceProcessToken;
|
|
73
|
+
/** Convenience accessor for the launched process id, when available. */
|
|
74
|
+
processIdentifier?: number;
|
|
75
|
+
[key: string]: unknown;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* CoreDevice AppService — manage applications and processes on the device.
|
|
79
|
+
*
|
|
80
|
+
* This is the modern (`devicectl`) backend for app lifecycle on iOS 17+: listing
|
|
81
|
+
* installed apps, launching/terminating apps, enumerating processes, signaling
|
|
82
|
+
* processes and uninstalling apps. Communicates over RemoteXPC via the shared
|
|
83
|
+
* {@link CoreDeviceService} invocation envelope.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* import { constants as osConstants } from 'node:os';
|
|
88
|
+
*
|
|
89
|
+
* const appService = await Services.startAppService(udid);
|
|
90
|
+
* try {
|
|
91
|
+
* const launched = await appService.launchApplication('com.apple.Preferences');
|
|
92
|
+
* await appService.sendSignalToProcess(
|
|
93
|
+
* launched.processIdentifier!,
|
|
94
|
+
* osConstants.signals.SIGKILL,
|
|
95
|
+
* );
|
|
96
|
+
* } finally {
|
|
97
|
+
* await appService.close();
|
|
98
|
+
* }
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare class AppService extends CoreDeviceService {
|
|
102
|
+
static readonly RSD_SERVICE_NAME = "com.apple.coredevice.appservice";
|
|
103
|
+
constructor(udid: string);
|
|
104
|
+
/**
|
|
105
|
+
* Lists installed applications. All categories are included by default.
|
|
106
|
+
*
|
|
107
|
+
* ⚠️ Limited on iOS 26+: the device gates app enumeration on this path and
|
|
108
|
+
* does not respond for requests that would return apps whose data containers
|
|
109
|
+
* the caller cannot access (the call hangs until `timeoutMs`). It only returns
|
|
110
|
+
* for result sets needing no container access (typically empty). For reliable
|
|
111
|
+
* app enumeration across iOS versions, prefer
|
|
112
|
+
* {@link InstallationProxyService.browse} (`Services.startInstallationProxyService`).
|
|
113
|
+
* This method remains for parity with the modern `devicectl`-style API and for
|
|
114
|
+
* environments/devices where the privileged context is available.
|
|
115
|
+
*/
|
|
116
|
+
listApps(options?: ListAppsOptions): Promise<InstalledApp[]>;
|
|
117
|
+
/**
|
|
118
|
+
* Launches an application by bundle identifier and returns its process token.
|
|
119
|
+
*
|
|
120
|
+
* Throws a {@link CoreDeviceError} if the bundle id is not installed (the
|
|
121
|
+
* device reports e.g. "The requested application … is not installed.").
|
|
122
|
+
*/
|
|
123
|
+
launchApplication(bundleId: string, options?: LaunchApplicationOptions): Promise<LaunchedApplication>;
|
|
124
|
+
/**
|
|
125
|
+
* Lists the processes currently running on the device.
|
|
126
|
+
*/
|
|
127
|
+
listProcesses(): Promise<AppServiceProcessToken[]>;
|
|
128
|
+
/**
|
|
129
|
+
* Sends a POSIX signal to a process by pid. Use `os.constants.signals` for
|
|
130
|
+
* signal numbers (e.g. `SIGKILL` to terminate, `SIGTERM` to ask politely).
|
|
131
|
+
*
|
|
132
|
+
* Throws a {@link CoreDeviceError} if the pid is not running (the device
|
|
133
|
+
* reports `com.apple.dt.CoreDeviceError`).
|
|
134
|
+
*/
|
|
135
|
+
sendSignalToProcess(pid: number, signal: number): Promise<XPCDictionary>;
|
|
136
|
+
/**
|
|
137
|
+
* Uninstalls an application by bundle identifier. This is idempotent: the
|
|
138
|
+
* device resolves successfully even if the app is not installed.
|
|
139
|
+
*/
|
|
140
|
+
uninstallApp(bundleId: string): Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Monitors termination of a process and resolves with its exit status
|
|
143
|
+
* (`{ status: { exitCode, wasCoreDumpCreated } }`). If the pid is not running,
|
|
144
|
+
* the device resolves immediately rather than waiting. Pass `timeoutMs` via
|
|
145
|
+
* `options` to bound how long to wait for a still-running process to exit.
|
|
146
|
+
*/
|
|
147
|
+
monitorProcessTermination(pid: number, options?: CoreDeviceInvokeOptions): Promise<XPCDictionary>;
|
|
148
|
+
}
|
|
149
|
+
export default AppService;
|
|
150
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/services/ios/app-service/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { createPlist } from '../../../lib/plist/index.js';
|
|
2
|
+
import { CoreDeviceService, } from '../core-device/core-device-service.js';
|
|
3
|
+
const FEATURE_LIST_APPS = 'com.apple.coredevice.feature.listapps';
|
|
4
|
+
const FEATURE_LAUNCH_APPLICATION = 'com.apple.coredevice.feature.launchapplication';
|
|
5
|
+
const FEATURE_LIST_PROCESSES = 'com.apple.coredevice.feature.listprocesses';
|
|
6
|
+
const FEATURE_UNINSTALL_APP = 'com.apple.coredevice.feature.uninstallapp';
|
|
7
|
+
const FEATURE_SEND_SIGNAL = 'com.apple.coredevice.feature.sendsignaltoprocess';
|
|
8
|
+
const FEATURE_MONITOR_PROCESS_TERMINATION = 'com.apple.coredevice.feature.monitorprocesstermination';
|
|
9
|
+
/**
|
|
10
|
+
* CoreDevice AppService — manage applications and processes on the device.
|
|
11
|
+
*
|
|
12
|
+
* This is the modern (`devicectl`) backend for app lifecycle on iOS 17+: listing
|
|
13
|
+
* installed apps, launching/terminating apps, enumerating processes, signaling
|
|
14
|
+
* processes and uninstalling apps. Communicates over RemoteXPC via the shared
|
|
15
|
+
* {@link CoreDeviceService} invocation envelope.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { constants as osConstants } from 'node:os';
|
|
20
|
+
*
|
|
21
|
+
* const appService = await Services.startAppService(udid);
|
|
22
|
+
* try {
|
|
23
|
+
* const launched = await appService.launchApplication('com.apple.Preferences');
|
|
24
|
+
* await appService.sendSignalToProcess(
|
|
25
|
+
* launched.processIdentifier!,
|
|
26
|
+
* osConstants.signals.SIGKILL,
|
|
27
|
+
* );
|
|
28
|
+
* } finally {
|
|
29
|
+
* await appService.close();
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class AppService extends CoreDeviceService {
|
|
34
|
+
static RSD_SERVICE_NAME = 'com.apple.coredevice.appservice';
|
|
35
|
+
constructor(udid) {
|
|
36
|
+
super(udid, AppService.RSD_SERVICE_NAME);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Lists installed applications. All categories are included by default.
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ Limited on iOS 26+: the device gates app enumeration on this path and
|
|
42
|
+
* does not respond for requests that would return apps whose data containers
|
|
43
|
+
* the caller cannot access (the call hangs until `timeoutMs`). It only returns
|
|
44
|
+
* for result sets needing no container access (typically empty). For reliable
|
|
45
|
+
* app enumeration across iOS versions, prefer
|
|
46
|
+
* {@link InstallationProxyService.browse} (`Services.startInstallationProxyService`).
|
|
47
|
+
* This method remains for parity with the modern `devicectl`-style API and for
|
|
48
|
+
* environments/devices where the privileged context is available.
|
|
49
|
+
*/
|
|
50
|
+
async listApps(options = {}) {
|
|
51
|
+
const output = await this.invoke(FEATURE_LIST_APPS, {
|
|
52
|
+
includeAppClips: options.includeAppClips ?? true,
|
|
53
|
+
includeRemovableApps: options.includeRemovableApps ?? true,
|
|
54
|
+
includeHiddenApps: options.includeHiddenApps ?? true,
|
|
55
|
+
includeInternalApps: options.includeInternalApps ?? true,
|
|
56
|
+
includeDefaultApps: options.includeDefaultApps ?? true,
|
|
57
|
+
requireContainerAccess: options.requireContainerAccess ?? false,
|
|
58
|
+
includeAppGroupIdentifiers: options.includeAppGroupIdentifiers ?? false,
|
|
59
|
+
includeContainerPaths: options.includeContainerPaths ?? false,
|
|
60
|
+
}, { timeoutMs: options.timeoutMs });
|
|
61
|
+
return asArray(output);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Launches an application by bundle identifier and returns its process token.
|
|
65
|
+
*
|
|
66
|
+
* Throws a {@link CoreDeviceError} if the bundle id is not installed (the
|
|
67
|
+
* device reports e.g. "The requested application … is not installed.").
|
|
68
|
+
*/
|
|
69
|
+
async launchApplication(bundleId, options = {}) {
|
|
70
|
+
const platformOptions = createPlist((options.platformSpecificOptions ?? {}));
|
|
71
|
+
const platformOptionsBuffer = Buffer.isBuffer(platformOptions)
|
|
72
|
+
? platformOptions
|
|
73
|
+
: Buffer.from(platformOptions, 'utf8');
|
|
74
|
+
const output = asDict(await this.invoke(FEATURE_LAUNCH_APPLICATION, {
|
|
75
|
+
applicationSpecifier: {
|
|
76
|
+
bundleIdentifier: { _0: bundleId },
|
|
77
|
+
},
|
|
78
|
+
options: {
|
|
79
|
+
arguments: options.arguments ?? [],
|
|
80
|
+
environmentVariables: options.environment ?? {},
|
|
81
|
+
standardIOUsesPseudoterminals: true,
|
|
82
|
+
startStopped: options.startSuspended ?? false,
|
|
83
|
+
terminateExisting: options.terminateExisting ?? true,
|
|
84
|
+
user: { shortName: 'mobile' },
|
|
85
|
+
platformSpecificOptions: platformOptionsBuffer,
|
|
86
|
+
},
|
|
87
|
+
standardIOIdentifiers: {},
|
|
88
|
+
}, { timeoutMs: options.timeoutMs }));
|
|
89
|
+
return {
|
|
90
|
+
...output,
|
|
91
|
+
processIdentifier: extractProcessIdentifier(output),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Lists the processes currently running on the device.
|
|
96
|
+
*/
|
|
97
|
+
async listProcesses() {
|
|
98
|
+
const output = asDict(await this.invoke(FEATURE_LIST_PROCESSES));
|
|
99
|
+
return asArray(output.processTokens);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Sends a POSIX signal to a process by pid. Use `os.constants.signals` for
|
|
103
|
+
* signal numbers (e.g. `SIGKILL` to terminate, `SIGTERM` to ask politely).
|
|
104
|
+
*
|
|
105
|
+
* Throws a {@link CoreDeviceError} if the pid is not running (the device
|
|
106
|
+
* reports `com.apple.dt.CoreDeviceError`).
|
|
107
|
+
*/
|
|
108
|
+
async sendSignalToProcess(pid, signal) {
|
|
109
|
+
return asDict(await this.invoke(FEATURE_SEND_SIGNAL, {
|
|
110
|
+
process: { processIdentifier: pid },
|
|
111
|
+
signal,
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Uninstalls an application by bundle identifier. This is idempotent: the
|
|
116
|
+
* device resolves successfully even if the app is not installed.
|
|
117
|
+
*/
|
|
118
|
+
async uninstallApp(bundleId) {
|
|
119
|
+
await this.invoke(FEATURE_UNINSTALL_APP, { bundleIdentifier: bundleId });
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Monitors termination of a process and resolves with its exit status
|
|
123
|
+
* (`{ status: { exitCode, wasCoreDumpCreated } }`). If the pid is not running,
|
|
124
|
+
* the device resolves immediately rather than waiting. Pass `timeoutMs` via
|
|
125
|
+
* `options` to bound how long to wait for a still-running process to exit.
|
|
126
|
+
*/
|
|
127
|
+
async monitorProcessTermination(pid, options = {}) {
|
|
128
|
+
return asDict(await this.invoke(FEATURE_MONITOR_PROCESS_TERMINATION, { processToken: { processIdentifier: pid } }, options));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function asDict(value) {
|
|
132
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
return {};
|
|
136
|
+
}
|
|
137
|
+
function asArray(value) {
|
|
138
|
+
return Array.isArray(value) ? value : [];
|
|
139
|
+
}
|
|
140
|
+
function extractProcessIdentifier(output) {
|
|
141
|
+
const token = output.processToken;
|
|
142
|
+
if (token && typeof token === 'object' && !Array.isArray(token)) {
|
|
143
|
+
const pid = token.processIdentifier;
|
|
144
|
+
if (typeof pid === 'number') {
|
|
145
|
+
return pid;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
export default AppService;
|
|
151
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/services/ios/app-service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAM1D,OAAO,EAEL,iBAAiB,GAClB,MAAM,uCAAuC,CAAC;AAE/C,MAAM,iBAAiB,GAAG,uCAAuC,CAAC;AAClE,MAAM,0BAA0B,GAC9B,gDAAgD,CAAC;AACnD,MAAM,sBAAsB,GAAG,4CAA4C,CAAC;AAC5E,MAAM,qBAAqB,GAAG,2CAA2C,CAAC;AAC1E,MAAM,mBAAmB,GAAG,kDAAkD,CAAC;AAC/E,MAAM,mCAAmC,GACvC,wDAAwD,CAAC;AA8E3D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,UAAW,SAAQ,iBAAiB;IAC/C,MAAM,CAAU,gBAAgB,GAAG,iCAAiC,CAAC;IAErE,YAAY,IAAY;QACtB,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,QAAQ,CAAC,UAA2B,EAAE;QAC1C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAC9B,iBAAiB,EACjB;YACE,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,IAAI;YAChD,oBAAoB,EAAE,OAAO,CAAC,oBAAoB,IAAI,IAAI;YAC1D,iBAAiB,EAAE,OAAO,CAAC,iBAAiB,IAAI,IAAI;YACpD,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,IAAI,IAAI;YACxD,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,IAAI,IAAI;YACtD,sBAAsB,EAAE,OAAO,CAAC,sBAAsB,IAAI,KAAK;YAC/D,0BAA0B,EAAE,OAAO,CAAC,0BAA0B,IAAI,KAAK;YACvE,qBAAqB,EAAE,OAAO,CAAC,qBAAqB,IAAI,KAAK;SAC9D,EACD,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CACjC,CAAC;QACF,OAAO,OAAO,CAAC,MAAM,CAAmB,CAAC;IAC3C,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,iBAAiB,CACrB,QAAgB,EAChB,UAAoC,EAAE;QAEtC,MAAM,eAAe,GAAG,WAAW,CACjC,CAAC,OAAO,CAAC,uBAAuB,IAAI,EAAE,CAAoB,CAC3D,CAAC;QACF,MAAM,qBAAqB,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;YAC5D,CAAC,CAAC,eAAe;YACjB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QAEzC,MAAM,MAAM,GAAG,MAAM,CACnB,MAAM,IAAI,CAAC,MAAM,CACf,0BAA0B,EAC1B;YACE,oBAAoB,EAAE;gBACpB,gBAAgB,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE;aACnC;YACD,OAAO,EAAE;gBACP,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,EAAE;gBAClC,oBAAoB,EAAE,OAAO,CAAC,WAAW,IAAI,EAAE;gBAC/C,6BAA6B,EAAE,IAAI;gBACnC,YAAY,EAAE,OAAO,CAAC,cAAc,IAAI,KAAK;gBAC7C,iBAAiB,EAAE,OAAO,CAAC,iBAAiB,IAAI,IAAI;gBACpD,IAAI,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE;gBAC7B,uBAAuB,EAAE,qBAAqB;aAC/C;YACD,qBAAqB,EAAE,EAAE;SAC1B,EACD,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CACjC,CACF,CAAC;QAEF,OAAO;YACL,GAAG,MAAM;YACT,iBAAiB,EAAE,wBAAwB,CAAC,MAAM,CAAC;SACpD,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa;QACjB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC;QACjE,OAAO,OAAO,CAAC,MAAM,CAAC,aAAa,CAA6B,CAAC;IACnE,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,mBAAmB,CACvB,GAAW,EACX,MAAc;QAEd,OAAO,MAAM,CACX,MAAM,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE;YACrC,OAAO,EAAE,EAAE,iBAAiB,EAAE,GAAG,EAAE;YACnC,MAAM;SACP,CAAC,CACH,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,YAAY,CAAC,QAAgB;QACjC,MAAM,IAAI,CAAC,MAAM,CAAC,qBAAqB,EAAE,EAAE,gBAAgB,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,yBAAyB,CAC7B,GAAW,EACX,UAAmC,EAAE;QAErC,OAAO,MAAM,CACX,MAAM,IAAI,CAAC,MAAM,CACf,mCAAmC,EACnC,EAAE,YAAY,EAAE,EAAE,iBAAiB,EAAE,GAAG,EAAE,EAAE,EAC5C,OAAO,CACR,CACF,CAAC;IACJ,CAAC;;AAGH,SAAS,MAAM,CAAC,KAAe;IAC7B,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,OAAO,KAAsB,CAAC;IAChC,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,OAAO,CAAC,KAAe;IAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC3C,CAAC;AAED,SAAS,wBAAwB,CAAC,MAAqB;IACrD,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC;IAClC,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,GAAG,GAAI,KAAuB,CAAC,iBAAiB,CAAC;QACvD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,OAAO,GAAG,CAAC;QACb,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { RemoteXpcFramedTransport } from '../../../lib/remote-xpc/remote-xpc-framed-transport.js';
|
|
2
|
+
import type { XPCDictionary, XPCValue } from '../../../lib/types.js';
|
|
3
|
+
import { BaseService } from '../base-service.js';
|
|
4
|
+
export interface CoreDeviceInvokeOptions {
|
|
5
|
+
/** Optional action identifier for the invocation. */
|
|
6
|
+
actionIdentifier?: string;
|
|
7
|
+
/** Override the default response timeout. */
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Error thrown when a CoreDevice invocation fails or returns no output.
|
|
12
|
+
*/
|
|
13
|
+
export declare class CoreDeviceError extends Error {
|
|
14
|
+
readonly response?: XPCDictionary;
|
|
15
|
+
constructor(message: string, response?: XPCDictionary);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Base class for iOS CoreDevice (`com.apple.coredevice.*`) services.
|
|
19
|
+
*
|
|
20
|
+
* CoreDevice services speak RemoteXPC over the tunnel and wrap every request in
|
|
21
|
+
* a common invocation envelope. This base owns the framed transport lifecycle
|
|
22
|
+
* and exposes:
|
|
23
|
+
* - {@link invoke} for request/response features (the common case)
|
|
24
|
+
* - {@link send} for fire-and-forget messages (e.g. HID events)
|
|
25
|
+
*
|
|
26
|
+
* Subclasses pass their RSD service name to the constructor and typically also
|
|
27
|
+
* expose it via a static `RSD_SERVICE_NAME` for catalog checks.
|
|
28
|
+
*/
|
|
29
|
+
export declare abstract class CoreDeviceService extends BaseService {
|
|
30
|
+
private readonly serviceName;
|
|
31
|
+
protected transport: RemoteXpcFramedTransport | null;
|
|
32
|
+
protected nextMessageId: number;
|
|
33
|
+
/** Serializes invocations so concurrent calls do not interleave replies. */
|
|
34
|
+
private invokeQueue;
|
|
35
|
+
constructor(udid: string, serviceName: string);
|
|
36
|
+
close(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Sends a fire-and-forget XPC message on the root channel. Used by services
|
|
39
|
+
* that do not expect a reply (e.g. HID event streams).
|
|
40
|
+
*/
|
|
41
|
+
protected send(body: XPCDictionary): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Invokes a CoreDevice feature and returns its `CoreDevice.output`.
|
|
44
|
+
*
|
|
45
|
+
* Each invocation uses a fresh connection: CoreDevice services close the
|
|
46
|
+
* connection after a request/response cycle, so reusing a connection
|
|
47
|
+
* across invocations fails. Calls are also serialized, so they never overlap.
|
|
48
|
+
*/
|
|
49
|
+
protected invoke(featureIdentifier?: string, input?: XPCDictionary, options?: CoreDeviceInvokeOptions): Promise<XPCValue>;
|
|
50
|
+
protected createTransport(): Promise<RemoteXpcFramedTransport>;
|
|
51
|
+
protected getTransport(): Promise<RemoteXpcFramedTransport>;
|
|
52
|
+
/**
|
|
53
|
+
* Creates a transport and attaches a permanent `'error'` listener. The
|
|
54
|
+
* framed transport is an EventEmitter; an `'error'` emitted with no listener
|
|
55
|
+
* is thrown by Node and crashes the process. `invoke()` registers a per-call
|
|
56
|
+
* listener, but fire-and-forget `send()` does not — so this guarantees a
|
|
57
|
+
* listener always exists for the transport's lifetime.
|
|
58
|
+
*/
|
|
59
|
+
private newTransport;
|
|
60
|
+
/**
|
|
61
|
+
* Closes any existing connection and opens a fresh one. Used by {@link invoke}
|
|
62
|
+
* because CoreDevice services are one-shot per connection.
|
|
63
|
+
*/
|
|
64
|
+
private refreshTransport;
|
|
65
|
+
private invokeInternal;
|
|
66
|
+
private buildEnvelope;
|
|
67
|
+
private waitForResponse;
|
|
68
|
+
}
|
|
69
|
+
export default CoreDeviceService;
|
|
70
|
+
//# sourceMappingURL=core-device-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-device-service.d.ts","sourceRoot":"","sources":["../../../../../src/services/ios/core-device/core-device-service.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,wBAAwB,EAAE,MAAM,wDAAwD,CAAC;AAElG,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AA6BjD,MAAM,WAAW,uBAAuB;IACtC,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;gBAEtB,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa;CAKtD;AAED;;;;;;;;;;;GAWG;AACH,8BAAsB,iBAAkB,SAAQ,WAAW;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC,SAAS,CAAC,SAAS,EAAE,wBAAwB,GAAG,IAAI,CAAQ;IAC5D,SAAS,CAAC,aAAa,SAAK;IAE5B,4EAA4E;IAC5E,OAAO,CAAC,WAAW,CAAuC;gBAE9C,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;IAKvC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAU5B;;;OAGG;cACa,IAAI,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAcxD;;;;;;OAMG;cACa,MAAM,CACpB,iBAAiB,CAAC,EAAE,MAAM,EAC1B,KAAK,GAAE,aAAkB,EACzB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,QAAQ,CAAC;cAuBJ,eAAe,IAAI,OAAO,CAAC,wBAAwB,CAAC;cAQpD,YAAY,IAAI,OAAO,CAAC,wBAAwB,CAAC;IAOjE;;;;;;OAMG;YACW,YAAY;IAS1B;;;OAGG;YACW,gBAAgB;YAUhB,cAAc;IAuC5B,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,eAAe;CA4DxB;AAqDD,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { getLogger } from '../../../lib/logger.js';
|
|
3
|
+
import { Http2Constants, XpcConstants, } from '../../../lib/remote-xpc/constants.js';
|
|
4
|
+
import { RemoteXpcFramedTransport } from '../../../lib/remote-xpc/remote-xpc-framed-transport.js';
|
|
5
|
+
import { encodeMessage } from '../../../lib/remote-xpc/xpc-protocol.js';
|
|
6
|
+
import { BaseService } from '../base-service.js';
|
|
7
|
+
const log = getLogger('CoreDeviceService');
|
|
8
|
+
const CONNECT_TIMEOUT_MS = 10_000;
|
|
9
|
+
const DEFAULT_INVOKE_TIMEOUT_MS = 30_000;
|
|
10
|
+
/**
|
|
11
|
+
* CoreDevice protocol version reported to the device.
|
|
12
|
+
*/
|
|
13
|
+
const CORE_DEVICE_VERSION_STRING = '629.3';
|
|
14
|
+
const CORE_DEVICE_DDI_PROTOCOL_VERSION = 2;
|
|
15
|
+
/**
|
|
16
|
+
* Builds the `CoreDevice.coreDeviceVersion` dictionary. `components` are encoded
|
|
17
|
+
* as XPC uint64 values (hence `bigint`), while `originalComponentsCount` is an
|
|
18
|
+
* int64 (a plain integer).
|
|
19
|
+
*/
|
|
20
|
+
function buildCoreDeviceVersion(version) {
|
|
21
|
+
const components = version.split('.');
|
|
22
|
+
return {
|
|
23
|
+
components: components.map((component) => BigInt(component)),
|
|
24
|
+
originalComponentsCount: components.length,
|
|
25
|
+
stringValue: version,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const CORE_DEVICE_VERSION = buildCoreDeviceVersion(CORE_DEVICE_VERSION_STRING);
|
|
29
|
+
/**
|
|
30
|
+
* Error thrown when a CoreDevice invocation fails or returns no output.
|
|
31
|
+
*/
|
|
32
|
+
export class CoreDeviceError extends Error {
|
|
33
|
+
response;
|
|
34
|
+
constructor(message, response) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = 'CoreDeviceError';
|
|
37
|
+
this.response = response;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Base class for iOS CoreDevice (`com.apple.coredevice.*`) services.
|
|
42
|
+
*
|
|
43
|
+
* CoreDevice services speak RemoteXPC over the tunnel and wrap every request in
|
|
44
|
+
* a common invocation envelope. This base owns the framed transport lifecycle
|
|
45
|
+
* and exposes:
|
|
46
|
+
* - {@link invoke} for request/response features (the common case)
|
|
47
|
+
* - {@link send} for fire-and-forget messages (e.g. HID events)
|
|
48
|
+
*
|
|
49
|
+
* Subclasses pass their RSD service name to the constructor and typically also
|
|
50
|
+
* expose it via a static `RSD_SERVICE_NAME` for catalog checks.
|
|
51
|
+
*/
|
|
52
|
+
export class CoreDeviceService extends BaseService {
|
|
53
|
+
serviceName;
|
|
54
|
+
transport = null;
|
|
55
|
+
nextMessageId = 1;
|
|
56
|
+
/** Serializes invocations so concurrent calls do not interleave replies. */
|
|
57
|
+
invokeQueue = Promise.resolve();
|
|
58
|
+
constructor(udid, serviceName) {
|
|
59
|
+
super(udid);
|
|
60
|
+
this.serviceName = serviceName;
|
|
61
|
+
}
|
|
62
|
+
async close() {
|
|
63
|
+
if (!this.transport) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const transport = this.transport;
|
|
67
|
+
this.transport = null;
|
|
68
|
+
await transport.close();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Sends a fire-and-forget XPC message on the root channel. Used by services
|
|
72
|
+
* that do not expect a reply (e.g. HID event streams).
|
|
73
|
+
*/
|
|
74
|
+
async send(body) {
|
|
75
|
+
const transport = await this.getTransport();
|
|
76
|
+
transport.sendDataFrame(encodeMessage({
|
|
77
|
+
flags: XpcConstants.XPC_FLAGS_ALWAYS_SET |
|
|
78
|
+
XpcConstants.XPC_FLAGS_DATA_PRESENT,
|
|
79
|
+
id: this.nextMessageId++,
|
|
80
|
+
body,
|
|
81
|
+
}), Http2Constants.ROOT_CHANNEL);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Invokes a CoreDevice feature and returns its `CoreDevice.output`.
|
|
85
|
+
*
|
|
86
|
+
* Each invocation uses a fresh connection: CoreDevice services close the
|
|
87
|
+
* connection after a request/response cycle, so reusing a connection
|
|
88
|
+
* across invocations fails. Calls are also serialized, so they never overlap.
|
|
89
|
+
*/
|
|
90
|
+
async invoke(featureIdentifier, input = {}, options = {}) {
|
|
91
|
+
// Serialize invocations: await the previous call's completion, then install
|
|
92
|
+
// a new tail that the next caller will await. Prior failures are ignored so
|
|
93
|
+
// one failed call does not poison the queue.
|
|
94
|
+
const previous = this.invokeQueue;
|
|
95
|
+
let release = () => undefined;
|
|
96
|
+
this.invokeQueue = new Promise((resolve) => {
|
|
97
|
+
release = resolve;
|
|
98
|
+
});
|
|
99
|
+
try {
|
|
100
|
+
await previous;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Ignore the previous invocation's outcome.
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
return await this.invokeInternal(featureIdentifier, input, options);
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
release();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async createTransport() {
|
|
113
|
+
const transport = new RemoteXpcFramedTransport(await this.resolveServiceAddress(this.serviceName));
|
|
114
|
+
await transport.connect({ timeoutMs: CONNECT_TIMEOUT_MS });
|
|
115
|
+
return transport;
|
|
116
|
+
}
|
|
117
|
+
async getTransport() {
|
|
118
|
+
if (!this.transport?.isConnected) {
|
|
119
|
+
this.transport = await this.newTransport();
|
|
120
|
+
}
|
|
121
|
+
return this.transport;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Creates a transport and attaches a permanent `'error'` listener. The
|
|
125
|
+
* framed transport is an EventEmitter; an `'error'` emitted with no listener
|
|
126
|
+
* is thrown by Node and crashes the process. `invoke()` registers a per-call
|
|
127
|
+
* listener, but fire-and-forget `send()` does not — so this guarantees a
|
|
128
|
+
* listener always exists for the transport's lifetime.
|
|
129
|
+
*/
|
|
130
|
+
async newTransport() {
|
|
131
|
+
const transport = await this.createTransport();
|
|
132
|
+
transport.on('error', (error) => {
|
|
133
|
+
log.debug(`CoreDevice transport error: ${error.message}`);
|
|
134
|
+
});
|
|
135
|
+
this.nextMessageId = 1;
|
|
136
|
+
return transport;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Closes any existing connection and opens a fresh one. Used by {@link invoke}
|
|
140
|
+
* because CoreDevice services are one-shot per connection.
|
|
141
|
+
*/
|
|
142
|
+
async refreshTransport() {
|
|
143
|
+
if (this.transport) {
|
|
144
|
+
const previous = this.transport;
|
|
145
|
+
this.transport = null;
|
|
146
|
+
await previous.close().catch(() => undefined);
|
|
147
|
+
}
|
|
148
|
+
this.transport = await this.newTransport();
|
|
149
|
+
return this.transport;
|
|
150
|
+
}
|
|
151
|
+
async invokeInternal(featureIdentifier, input, options) {
|
|
152
|
+
const transport = await this.refreshTransport();
|
|
153
|
+
const request = this.buildEnvelope(featureIdentifier, input, options.actionIdentifier);
|
|
154
|
+
// Register the response listener before sending so a fast reply is not lost.
|
|
155
|
+
const responsePromise = this.waitForResponse(transport, options.timeoutMs ?? DEFAULT_INVOKE_TIMEOUT_MS, featureIdentifier);
|
|
156
|
+
transport.sendDataFrame(encodeMessage({
|
|
157
|
+
flags: XpcConstants.XPC_FLAGS_ALWAYS_SET |
|
|
158
|
+
XpcConstants.XPC_FLAGS_DATA_PRESENT |
|
|
159
|
+
XpcConstants.XPC_FLAGS_WANTING_REPLY,
|
|
160
|
+
id: this.nextMessageId++,
|
|
161
|
+
body: request,
|
|
162
|
+
}), Http2Constants.ROOT_CHANNEL);
|
|
163
|
+
const response = await responsePromise;
|
|
164
|
+
const output = response['CoreDevice.output'];
|
|
165
|
+
if (output === undefined) {
|
|
166
|
+
throw buildInvocationError(featureIdentifier, response);
|
|
167
|
+
}
|
|
168
|
+
return output;
|
|
169
|
+
}
|
|
170
|
+
buildEnvelope(featureIdentifier, input, actionIdentifier) {
|
|
171
|
+
const request = {
|
|
172
|
+
'CoreDevice.CoreDeviceDDIProtocolVersion': CORE_DEVICE_DDI_PROTOCOL_VERSION,
|
|
173
|
+
'CoreDevice.coreDeviceVersion': CORE_DEVICE_VERSION,
|
|
174
|
+
'CoreDevice.deviceIdentifier': randomUUID(),
|
|
175
|
+
'CoreDevice.input': input,
|
|
176
|
+
'CoreDevice.invocationIdentifier': randomUUID(),
|
|
177
|
+
};
|
|
178
|
+
if (featureIdentifier !== undefined) {
|
|
179
|
+
request['CoreDevice.featureIdentifier'] = featureIdentifier;
|
|
180
|
+
request['CoreDevice.action'] = {};
|
|
181
|
+
}
|
|
182
|
+
if (actionIdentifier !== undefined) {
|
|
183
|
+
request['CoreDevice.actionIdentifier'] = actionIdentifier;
|
|
184
|
+
}
|
|
185
|
+
return request;
|
|
186
|
+
}
|
|
187
|
+
waitForResponse(transport, timeoutMs, featureIdentifier) {
|
|
188
|
+
return new Promise((resolve, reject) => {
|
|
189
|
+
let settled = false;
|
|
190
|
+
const cleanup = () => {
|
|
191
|
+
if (settled) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
settled = true;
|
|
195
|
+
clearTimeout(timer);
|
|
196
|
+
transport.off('message', onMessage);
|
|
197
|
+
transport.off('error', onError);
|
|
198
|
+
transport.off('close', onClose);
|
|
199
|
+
};
|
|
200
|
+
const onMessage = (body) => {
|
|
201
|
+
// Skip empty/handshake acks; the real reply carries CoreDevice.* keys.
|
|
202
|
+
if (!body ||
|
|
203
|
+
typeof body !== 'object' ||
|
|
204
|
+
Object.keys(body).length === 0) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
cleanup();
|
|
208
|
+
resolve(body);
|
|
209
|
+
};
|
|
210
|
+
const onError = (error) => {
|
|
211
|
+
cleanup();
|
|
212
|
+
reject(error);
|
|
213
|
+
};
|
|
214
|
+
const onClose = () => {
|
|
215
|
+
cleanup();
|
|
216
|
+
reject(new CoreDeviceError(`CoreDevice connection closed while awaiting '${featureIdentifier ?? '<none>'}'`));
|
|
217
|
+
};
|
|
218
|
+
const timer = setTimeout(() => {
|
|
219
|
+
cleanup();
|
|
220
|
+
reject(new CoreDeviceError(`CoreDevice invocation '${featureIdentifier ?? '<none>'}' timed out after ${timeoutMs}ms`));
|
|
221
|
+
}, timeoutMs);
|
|
222
|
+
transport.on('message', onMessage);
|
|
223
|
+
transport.once('error', onError);
|
|
224
|
+
transport.once('close', onClose);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Builds a descriptive error from a CoreDevice reply that carries no output.
|
|
230
|
+
* The device returns an `NSError`-shaped `CoreDevice.error` whose `userInfo`
|
|
231
|
+
* holds a human-readable reason; surface it so callers can tell *why* (e.g. a
|
|
232
|
+
* missing bundle id or pid) instead of a generic failure.
|
|
233
|
+
*/
|
|
234
|
+
function buildInvocationError(featureIdentifier, response) {
|
|
235
|
+
const feature = featureIdentifier ?? '<none>';
|
|
236
|
+
const deviceError = response['CoreDevice.error'];
|
|
237
|
+
if (!deviceError ||
|
|
238
|
+
typeof deviceError !== 'object' ||
|
|
239
|
+
Array.isArray(deviceError)) {
|
|
240
|
+
return new CoreDeviceError(`CoreDevice invocation '${feature}' returned no output`, response);
|
|
241
|
+
}
|
|
242
|
+
const error = deviceError;
|
|
243
|
+
const userInfo = error.userInfo && typeof error.userInfo === 'object'
|
|
244
|
+
? error.userInfo
|
|
245
|
+
: {};
|
|
246
|
+
const reason = pickString(userInfo.NSLocalizedDescription) ??
|
|
247
|
+
pickString(userInfo.NSLocalizedFailureReason) ??
|
|
248
|
+
pickString(userInfo.NSDebugDescription) ??
|
|
249
|
+
'unknown error';
|
|
250
|
+
const failureReason = pickString(userInfo.NSLocalizedFailureReason);
|
|
251
|
+
const detail = failureReason && failureReason !== reason
|
|
252
|
+
? `${reason} ${failureReason}`
|
|
253
|
+
: reason;
|
|
254
|
+
const domain = pickString(error.domain) ?? 'unknown';
|
|
255
|
+
const code = typeof error.code === 'number' ? ` ${error.code}` : '';
|
|
256
|
+
return new CoreDeviceError(`CoreDevice '${feature}' failed: ${detail} [${domain}${code}]`, response);
|
|
257
|
+
}
|
|
258
|
+
function pickString(value) {
|
|
259
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
260
|
+
}
|
|
261
|
+
export default CoreDeviceService;
|
|
262
|
+
//# sourceMappingURL=core-device-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-device-service.js","sourceRoot":"","sources":["../../../../../src/services/ios/core-device/core-device-service.ts"],"names":[],"mappings":"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"}
|