appium-ios-remotexpc 0.13.2 → 0.14.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.
Files changed (55) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/src/index.d.ts +1 -1
  3. package/build/src/index.d.ts.map +1 -1
  4. package/build/src/lib/plist/binary-plist-creator.d.ts.map +1 -1
  5. package/build/src/lib/plist/binary-plist-creator.js +30 -0
  6. package/build/src/lib/plist/index.d.ts +1 -0
  7. package/build/src/lib/plist/index.d.ts.map +1 -1
  8. package/build/src/lib/plist/index.js +1 -0
  9. package/build/src/lib/plist/plist-uid.d.ts +10 -0
  10. package/build/src/lib/plist/plist-uid.d.ts.map +1 -0
  11. package/build/src/lib/plist/plist-uid.js +10 -0
  12. package/build/src/lib/types.d.ts +165 -2
  13. package/build/src/lib/types.d.ts.map +1 -1
  14. package/build/src/services/ios/dvt/channel-fragmenter.d.ts +21 -0
  15. package/build/src/services/ios/dvt/channel-fragmenter.d.ts.map +1 -0
  16. package/build/src/services/ios/dvt/channel-fragmenter.js +37 -0
  17. package/build/src/services/ios/dvt/channel.d.ts +32 -0
  18. package/build/src/services/ios/dvt/channel.d.ts.map +1 -0
  19. package/build/src/services/ios/dvt/channel.js +44 -0
  20. package/build/src/services/ios/dvt/dtx-message.d.ts +88 -0
  21. package/build/src/services/ios/dvt/dtx-message.d.ts.map +1 -0
  22. package/build/src/services/ios/dvt/dtx-message.js +113 -0
  23. package/build/src/services/ios/dvt/index.d.ts +119 -0
  24. package/build/src/services/ios/dvt/index.d.ts.map +1 -0
  25. package/build/src/services/ios/dvt/index.js +552 -0
  26. package/build/src/services/ios/dvt/instruments/condition-inducer.d.ts +37 -0
  27. package/build/src/services/ios/dvt/instruments/condition-inducer.d.ts.map +1 -0
  28. package/build/src/services/ios/dvt/instruments/condition-inducer.js +99 -0
  29. package/build/src/services/ios/dvt/instruments/location-simulation.d.ts +43 -0
  30. package/build/src/services/ios/dvt/instruments/location-simulation.d.ts.map +1 -0
  31. package/build/src/services/ios/dvt/instruments/location-simulation.js +60 -0
  32. package/build/src/services/ios/dvt/nskeyedarchiver-decoder.d.ts +41 -0
  33. package/build/src/services/ios/dvt/nskeyedarchiver-decoder.d.ts.map +1 -0
  34. package/build/src/services/ios/dvt/nskeyedarchiver-decoder.js +190 -0
  35. package/build/src/services/ios/dvt/utils.d.ts +19 -0
  36. package/build/src/services/ios/dvt/utils.d.ts.map +1 -0
  37. package/build/src/services/ios/dvt/utils.js +67 -0
  38. package/build/src/services.d.ts +2 -1
  39. package/build/src/services.d.ts.map +1 -1
  40. package/build/src/services.js +23 -0
  41. package/package.json +4 -1
  42. package/src/index.ts +6 -0
  43. package/src/lib/plist/binary-plist-creator.ts +30 -0
  44. package/src/lib/plist/index.ts +2 -0
  45. package/src/lib/plist/plist-uid.ts +9 -0
  46. package/src/lib/types.ts +179 -1
  47. package/src/services/ios/dvt/channel-fragmenter.ts +42 -0
  48. package/src/services/ios/dvt/channel.ts +58 -0
  49. package/src/services/ios/dvt/dtx-message.ts +162 -0
  50. package/src/services/ios/dvt/index.ts +727 -0
  51. package/src/services/ios/dvt/instruments/condition-inducer.ts +140 -0
  52. package/src/services/ios/dvt/instruments/location-simulation.ts +83 -0
  53. package/src/services/ios/dvt/nskeyedarchiver-decoder.ts +219 -0
  54. package/src/services/ios/dvt/utils.ts +89 -0
  55. package/src/services.ts +33 -0
@@ -0,0 +1,43 @@
1
+ import type { DVTSecureSocketProxyService } from '../index.js';
2
+ /**
3
+ * Geographic coordinates
4
+ */
5
+ export interface LocationCoordinates {
6
+ latitude: number;
7
+ longitude: number;
8
+ }
9
+ /**
10
+ * Location simulation service for simulating device GPS location
11
+ */
12
+ export declare class LocationSimulation {
13
+ private readonly dvt;
14
+ static readonly IDENTIFIER = "com.apple.instruments.server.services.LocationSimulation";
15
+ private channel;
16
+ constructor(dvt: DVTSecureSocketProxyService);
17
+ /**
18
+ * Initialize the location simulation channel
19
+ */
20
+ initialize(): Promise<void>;
21
+ /**
22
+ * Set the simulated GPS location
23
+ * @param coordinates The location coordinates
24
+ */
25
+ set(coordinates: LocationCoordinates): Promise<void>;
26
+ /**
27
+ * Set the simulated GPS location
28
+ * @param latitude The latitude coordinate
29
+ * @param longitude The longitude coordinate
30
+ */
31
+ setLocation(latitude: number, longitude: number): Promise<void>;
32
+ /**
33
+ * Stop location simulation and restore the actual device location
34
+ *
35
+ * Note: This method is safe to call even if no location simulation is currently active.
36
+ */
37
+ clear(): Promise<void>;
38
+ /**
39
+ * Stop location simulation (alias for clear)
40
+ */
41
+ stop(): Promise<void>;
42
+ }
43
+ //# sourceMappingURL=location-simulation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"location-simulation.d.ts","sourceRoot":"","sources":["../../../../../../src/services/ios/dvt/instruments/location-simulation.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAI/D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,kBAAkB;IAMjB,OAAO,CAAC,QAAQ,CAAC,GAAG;IALhC,MAAM,CAAC,QAAQ,CAAC,UAAU,8DACmC;IAE7D,OAAO,CAAC,OAAO,CAAwB;gBAEV,GAAG,EAAE,2BAA2B;IAE7D;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC;;;OAGG;IACG,GAAG,CAAC,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAe1D;;;;OAIG;IACG,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrE;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAG5B"}
@@ -0,0 +1,60 @@
1
+ import { getLogger } from '../../../../lib/logger.js';
2
+ import { MessageAux } from '../dtx-message.js';
3
+ const log = getLogger('LocationSimulation');
4
+ /**
5
+ * Location simulation service for simulating device GPS location
6
+ */
7
+ export class LocationSimulation {
8
+ dvt;
9
+ static IDENTIFIER = 'com.apple.instruments.server.services.LocationSimulation';
10
+ channel = null;
11
+ constructor(dvt) {
12
+ this.dvt = dvt;
13
+ }
14
+ /**
15
+ * Initialize the location simulation channel
16
+ */
17
+ async initialize() {
18
+ if (this.channel) {
19
+ return;
20
+ }
21
+ this.channel = await this.dvt.makeChannel(LocationSimulation.IDENTIFIER);
22
+ }
23
+ /**
24
+ * Set the simulated GPS location
25
+ * @param coordinates The location coordinates
26
+ */
27
+ async set(coordinates) {
28
+ await this.initialize();
29
+ const args = new MessageAux()
30
+ .appendObj(coordinates.latitude)
31
+ .appendObj(coordinates.longitude);
32
+ await this.channel.call('simulateLocationWithLatitude_longitude_')(args);
33
+ await this.channel.receivePlist();
34
+ log.info(`Location set to: ${coordinates.latitude}, ${coordinates.longitude}`);
35
+ }
36
+ /**
37
+ * Set the simulated GPS location
38
+ * @param latitude The latitude coordinate
39
+ * @param longitude The longitude coordinate
40
+ */
41
+ async setLocation(latitude, longitude) {
42
+ await this.set({ latitude, longitude });
43
+ }
44
+ /**
45
+ * Stop location simulation and restore the actual device location
46
+ *
47
+ * Note: This method is safe to call even if no location simulation is currently active.
48
+ */
49
+ async clear() {
50
+ await this.initialize();
51
+ await this.channel.call('stopLocationSimulation')();
52
+ log.info('Location simulation stopped');
53
+ }
54
+ /**
55
+ * Stop location simulation (alias for clear)
56
+ */
57
+ async stop() {
58
+ await this.clear();
59
+ }
60
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Decode NSKeyedArchiver formatted data into native JavaScript objects
3
+ *
4
+ * NSKeyedArchiver is Apple's serialization format that stores object graphs
5
+ * with references. The format includes:
6
+ * - $version: Archive version (typically 100000)
7
+ * - $archiver: "NSKeyedArchiver"
8
+ * - $top: Root object references
9
+ * - $objects: Array of all objects with cross-references
10
+ */
11
+ export declare class NSKeyedArchiverDecoder {
12
+ private readonly objects;
13
+ private readonly decoded;
14
+ private readonly archive;
15
+ constructor(data: any);
16
+ /**
17
+ * Check if data is in NSKeyedArchiver format
18
+ */
19
+ static isNSKeyedArchive(data: any): boolean;
20
+ /**
21
+ * Decode the entire archive starting from the root
22
+ */
23
+ decode(): any;
24
+ /**
25
+ * Decode an object at a specific index
26
+ */
27
+ private decodeObject;
28
+ /**
29
+ * Decode an NSArray
30
+ */
31
+ private decodeArray;
32
+ /**
33
+ * Decode an NSDictionary
34
+ */
35
+ private decodeDictionary;
36
+ }
37
+ /**
38
+ * Decode NSKeyedArchiver data or return as-is if not archived
39
+ */
40
+ export declare function decodeNSKeyedArchiver(data: any): any;
41
+ //# sourceMappingURL=nskeyedarchiver-decoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nskeyedarchiver-decoder.d.ts","sourceRoot":"","sources":["../../../../../src/services/ios/dvt/nskeyedarchiver-decoder.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAM;gBAElB,IAAI,EAAE,GAAG;IAUrB;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO;IAa3C;;OAEG;IACH,MAAM,IAAI,GAAG;IA8Bb;;OAEG;IACH,OAAO,CAAC,YAAY;IA4EpB;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAkBzB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAiBpD"}
@@ -0,0 +1,190 @@
1
+ import { getLogger } from '../../../lib/logger.js';
2
+ const log = getLogger('NSKeyedArchiverDecoder');
3
+ /**
4
+ * Decode NSKeyedArchiver formatted data into native JavaScript objects
5
+ *
6
+ * NSKeyedArchiver is Apple's serialization format that stores object graphs
7
+ * with references. The format includes:
8
+ * - $version: Archive version (typically 100000)
9
+ * - $archiver: "NSKeyedArchiver"
10
+ * - $top: Root object references
11
+ * - $objects: Array of all objects with cross-references
12
+ */
13
+ export class NSKeyedArchiverDecoder {
14
+ objects;
15
+ decoded;
16
+ archive;
17
+ constructor(data) {
18
+ if (!NSKeyedArchiverDecoder.isNSKeyedArchive(data)) {
19
+ throw new Error('Data is not in NSKeyedArchiver format');
20
+ }
21
+ this.archive = data;
22
+ this.objects = data.$objects || [];
23
+ this.decoded = new Map();
24
+ }
25
+ /**
26
+ * Check if data is in NSKeyedArchiver format
27
+ */
28
+ static isNSKeyedArchive(data) {
29
+ if (!data || typeof data !== 'object') {
30
+ return false;
31
+ }
32
+ return ('$archiver' in data &&
33
+ data.$archiver === 'NSKeyedArchiver' &&
34
+ '$objects' in data &&
35
+ Array.isArray(data.$objects));
36
+ }
37
+ /**
38
+ * Decode the entire archive starting from the root
39
+ */
40
+ decode() {
41
+ if (!this.objects || this.objects.length === 0) {
42
+ return null;
43
+ }
44
+ // Extract root reference from $top
45
+ let rootIndex = null;
46
+ if (this.archive.$top && typeof this.archive.$top === 'object') {
47
+ const top = this.archive.$top;
48
+ if ('root' in top) {
49
+ const root = top.root;
50
+ if (typeof root === 'number') {
51
+ rootIndex = root;
52
+ }
53
+ else if (typeof root === 'object' && root && 'CF$UID' in root) {
54
+ rootIndex = root.CF$UID;
55
+ }
56
+ }
57
+ }
58
+ // If we found the root index, decode it
59
+ if (rootIndex !== null) {
60
+ return this.decodeObject(rootIndex);
61
+ }
62
+ // Fallback: decode first non-null object
63
+ log.warn('Could not find root reference, using fallback');
64
+ return this.decodeObject(1);
65
+ }
66
+ /**
67
+ * Decode an object at a specific index
68
+ */
69
+ decodeObject(index) {
70
+ if (index < 0 || index >= this.objects.length) {
71
+ return null;
72
+ }
73
+ // Check cache
74
+ if (this.decoded.has(index)) {
75
+ return this.decoded.get(index);
76
+ }
77
+ const obj = this.objects[index];
78
+ // Handle null marker
79
+ if (obj === '$null' || obj === null) {
80
+ return null;
81
+ }
82
+ // Handle primitive types
83
+ if (typeof obj !== 'object') {
84
+ return obj;
85
+ }
86
+ // Handle UID references
87
+ if ('CF$UID' in obj) {
88
+ return this.decodeObject(obj.CF$UID);
89
+ }
90
+ // Handle NSDictionary (NS.keys + NS.objects) - check this FIRST before NSArray
91
+ if ('NS.keys' in obj && 'NS.objects' in obj) {
92
+ const result = this.decodeDictionary(obj['NS.keys'], obj['NS.objects']);
93
+ this.decoded.set(index, result);
94
+ return result;
95
+ }
96
+ // Handle NSArray (NS.objects only, without NS.keys)
97
+ if ('NS.objects' in obj) {
98
+ const result = this.decodeArray(obj['NS.objects']);
99
+ this.decoded.set(index, result);
100
+ return result;
101
+ }
102
+ // Handle regular objects - just return as-is but resolve references
103
+ const result = {};
104
+ for (const [key, value] of Object.entries(obj)) {
105
+ if (key === '$class') {
106
+ continue; // Skip class metadata
107
+ }
108
+ if (typeof value === 'number') {
109
+ // Could be a reference or primitive
110
+ const referenced = this.objects[value];
111
+ if (referenced &&
112
+ typeof referenced === 'object' &&
113
+ referenced !== '$null') {
114
+ result[key] = this.decodeObject(value);
115
+ }
116
+ else {
117
+ result[key] = value;
118
+ }
119
+ }
120
+ else if (typeof value === 'object' && value && 'CF$UID' in value) {
121
+ const uid = value.CF$UID;
122
+ if (typeof uid === 'number') {
123
+ result[key] = this.decodeObject(uid);
124
+ }
125
+ else {
126
+ result[key] = value;
127
+ }
128
+ }
129
+ else {
130
+ result[key] = value;
131
+ }
132
+ }
133
+ this.decoded.set(index, result);
134
+ return result;
135
+ }
136
+ /**
137
+ * Decode an NSArray
138
+ */
139
+ decodeArray(refs) {
140
+ if (!Array.isArray(refs)) {
141
+ return [];
142
+ }
143
+ return refs.map((ref) => {
144
+ if (typeof ref === 'number') {
145
+ return this.decodeObject(ref);
146
+ }
147
+ else if (typeof ref === 'object' && ref && 'CF$UID' in ref) {
148
+ return this.decodeObject(ref.CF$UID);
149
+ }
150
+ return ref;
151
+ });
152
+ }
153
+ /**
154
+ * Decode an NSDictionary
155
+ */
156
+ decodeDictionary(keyRefs, valueRefs) {
157
+ if (!Array.isArray(keyRefs) || !Array.isArray(valueRefs)) {
158
+ return {};
159
+ }
160
+ const result = {};
161
+ for (let i = 0; i < keyRefs.length && i < valueRefs.length; i++) {
162
+ const key = this.decodeObject(keyRefs[i]);
163
+ const value = this.decodeObject(valueRefs[i]);
164
+ if (typeof key === 'string') {
165
+ result[key] = value;
166
+ }
167
+ }
168
+ return result;
169
+ }
170
+ }
171
+ /**
172
+ * Decode NSKeyedArchiver data or return as-is if not archived
173
+ */
174
+ export function decodeNSKeyedArchiver(data) {
175
+ if (!data) {
176
+ return data;
177
+ }
178
+ // Check if this is NSKeyedArchiver format
179
+ if (!NSKeyedArchiverDecoder.isNSKeyedArchive(data)) {
180
+ return data;
181
+ }
182
+ try {
183
+ const decoder = new NSKeyedArchiverDecoder(data);
184
+ return decoder.decode();
185
+ }
186
+ catch (error) {
187
+ log.warn('Failed to decode NSKeyedArchiver data:', error);
188
+ return data;
189
+ }
190
+ }
@@ -0,0 +1,19 @@
1
+ import type { PlistDictionary } from '../../../lib/types.js';
2
+ export declare function isPlainObject(value: any): value is Record<string, any>;
3
+ export declare function hasProperties(obj: any, ...props: string[]): boolean;
4
+ export declare function isNSKeyedArchiverFormat(data: any): boolean;
5
+ export declare function isNSDictionaryFormat(obj: any): boolean;
6
+ export declare function hasNSErrorIndicators(obj: any): boolean;
7
+ /**
8
+ * Extract $objects array from NSKeyedArchiver format, returns null if invalid
9
+ */
10
+ export declare function extractNSKeyedArchiverObjects(data: any): any[] | null;
11
+ /**
12
+ * Extract NSDictionary from NSKeyedArchiver objects using key/value references
13
+ */
14
+ export declare function extractNSDictionary(dictObj: any, objects: any[]): PlistDictionary;
15
+ /**
16
+ * Extract strings from NSKeyedArchiver objects array as dictionary keys
17
+ */
18
+ export declare function extractCapabilityStrings(objects: any[]): PlistDictionary;
19
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/services/ios/dvt/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAEtE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAEnE;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAM1D;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAEtD;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAOtD;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,IAAI,CAOrE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,GAAG,EAAE,GACb,eAAe,CAsBjB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,eAAe,CAYxE"}
@@ -0,0 +1,67 @@
1
+ export function isPlainObject(value) {
2
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
3
+ }
4
+ export function hasProperties(obj, ...props) {
5
+ return isPlainObject(obj) ? props.every((prop) => prop in obj) : false;
6
+ }
7
+ export function isNSKeyedArchiverFormat(data) {
8
+ return (hasProperties(data, '$objects') &&
9
+ Array.isArray(data.$objects) &&
10
+ data.$objects.length > 0);
11
+ }
12
+ export function isNSDictionaryFormat(obj) {
13
+ return hasProperties(obj, 'NS.keys', 'NS.objects');
14
+ }
15
+ export function hasNSErrorIndicators(obj) {
16
+ if (!isPlainObject(obj)) {
17
+ return false;
18
+ }
19
+ const errorProps = ['NSCode', 'NSUserInfo', 'NSDomain'];
20
+ return errorProps.some((prop) => prop in obj);
21
+ }
22
+ /**
23
+ * Extract $objects array from NSKeyedArchiver format, returns null if invalid
24
+ */
25
+ export function extractNSKeyedArchiverObjects(data) {
26
+ if (!isNSKeyedArchiverFormat(data)) {
27
+ return null;
28
+ }
29
+ const objects = data.$objects;
30
+ return objects.length > 1 ? objects : null;
31
+ }
32
+ /**
33
+ * Extract NSDictionary from NSKeyedArchiver objects using key/value references
34
+ */
35
+ export function extractNSDictionary(dictObj, objects) {
36
+ if (!isNSDictionaryFormat(dictObj)) {
37
+ return {};
38
+ }
39
+ const keysRef = dictObj['NS.keys'];
40
+ const valuesRef = dictObj['NS.objects'];
41
+ if (!Array.isArray(keysRef) || !Array.isArray(valuesRef)) {
42
+ return {};
43
+ }
44
+ const result = {};
45
+ for (let i = 0; i < keysRef.length; i++) {
46
+ const key = objects[keysRef[i]];
47
+ const value = objects[valuesRef[i]];
48
+ if (typeof key === 'string') {
49
+ result[key] = value;
50
+ }
51
+ }
52
+ return result;
53
+ }
54
+ /**
55
+ * Extract strings from NSKeyedArchiver objects array as dictionary keys
56
+ */
57
+ export function extractCapabilityStrings(objects) {
58
+ const result = {};
59
+ // Start from index 1 because index 0 is always '$null' in NSKeyedArchiver format
60
+ for (let i = 1; i < objects.length; i++) {
61
+ const obj = objects[i];
62
+ if (typeof obj === 'string' && obj !== '$null') {
63
+ result[obj] = true;
64
+ }
65
+ }
66
+ return result;
67
+ }
@@ -1,5 +1,5 @@
1
1
  import { RemoteXpcConnection } from './lib/remote-xpc/remote-xpc-connection.js';
2
- import type { DiagnosticsServiceWithConnection, MisagentServiceWithConnection, MobileConfigServiceWithConnection, MobileImageMounterServiceWithConnection, NotificationProxyServiceWithConnection, PowerAssertionServiceWithConnection, SpringboardServiceWithConnection, SyslogService as SyslogServiceType, WebInspectorServiceWithConnection } from './lib/types.js';
2
+ import type { DVTServiceWithConnection, DiagnosticsServiceWithConnection, MisagentServiceWithConnection, MobileConfigServiceWithConnection, MobileImageMounterServiceWithConnection, NotificationProxyServiceWithConnection, PowerAssertionServiceWithConnection, SpringboardServiceWithConnection, SyslogService as SyslogServiceType, WebInspectorServiceWithConnection } from './lib/types.js';
3
3
  import AfcService from './services/ios/afc/index.js';
4
4
  export declare function startDiagnosticsService(udid: string): Promise<DiagnosticsServiceWithConnection>;
5
5
  export declare function startNotificationProxyService(udid: string): Promise<NotificationProxyServiceWithConnection>;
@@ -15,6 +15,7 @@ export declare function startSyslogService(udid: string): Promise<SyslogServiceT
15
15
  */
16
16
  export declare function startAfcService(udid: string): Promise<AfcService>;
17
17
  export declare function startWebInspectorService(udid: string): Promise<WebInspectorServiceWithConnection>;
18
+ export declare function startDVTService(udid: string): Promise<DVTServiceWithConnection>;
18
19
  export declare function createRemoteXPCConnection(udid: string): Promise<{
19
20
  remoteXPC: RemoteXpcConnection;
20
21
  tunnelConnection: {
@@ -1 +1 @@
1
- {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/services.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAGhF,OAAO,KAAK,EACV,gCAAgC,EAChC,6BAA6B,EAC7B,iCAAiC,EACjC,uCAAuC,EACvC,sCAAsC,EACtC,mCAAmC,EACnC,gCAAgC,EAChC,aAAa,IAAI,iBAAiB,EAClC,iCAAiC,EAClC,MAAM,gBAAgB,CAAC;AACxB,OAAO,UAAU,MAAM,6BAA6B,CAAC;AAcrD,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gCAAgC,CAAC,CAY3C;AAED,wBAAsB,6BAA6B,CACjD,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,sCAAsC,CAAC,CAYjD;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iCAAiC,CAAC,CAY5C;AAED,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,uCAAuC,CAAC,CAYlD;AAED,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gCAAgC,CAAC,CAY3C;AAED,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,6BAA6B,CAAC,CAYxC;AAED,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,mCAAmC,CAAC,CAY9C;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,CAAC,CAG5B;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAOvE;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iCAAiC,CAAC,CAY5C;AAED,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,MAAM;;;;;;;;GAO3D"}
1
+ {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/services.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAGhF,OAAO,KAAK,EACV,wBAAwB,EACxB,gCAAgC,EAChC,6BAA6B,EAC7B,iCAAiC,EACjC,uCAAuC,EACvC,sCAAsC,EACtC,mCAAmC,EACnC,gCAAgC,EAChC,aAAa,IAAI,iBAAiB,EAClC,iCAAiC,EAClC,MAAM,gBAAgB,CAAC;AACxB,OAAO,UAAU,MAAM,6BAA6B,CAAC;AAiBrD,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gCAAgC,CAAC,CAY3C;AAED,wBAAsB,6BAA6B,CACjD,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,sCAAsC,CAAC,CAYjD;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iCAAiC,CAAC,CAY5C;AAED,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,uCAAuC,CAAC,CAYlD;AAED,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gCAAgC,CAAC,CAY3C;AAED,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,6BAA6B,CAAC,CAYxC;AAED,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,mCAAmC,CAAC,CAY9C;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,CAAC,CAG5B;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAOvE;AAED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iCAAiC,CAAC,CAY5C;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,wBAAwB,CAAC,CAyBnC;AAED,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,MAAM;;;;;;;;GAO3D"}
@@ -4,6 +4,9 @@ import { TunnelManager } from './lib/tunnel/index.js';
4
4
  import { TunnelApiClient } from './lib/tunnel/tunnel-api-client.js';
5
5
  import AfcService from './services/ios/afc/index.js';
6
6
  import DiagnosticsService from './services/ios/diagnostic-service/index.js';
7
+ import { DVTSecureSocketProxyService } from './services/ios/dvt/index.js';
8
+ import { ConditionInducer } from './services/ios/dvt/instruments/condition-inducer.js';
9
+ import { LocationSimulation } from './services/ios/dvt/instruments/location-simulation.js';
7
10
  import { MisagentService } from './services/ios/misagent/index.js';
8
11
  import { MobileConfigService } from './services/ios/mobile-config/index.js';
9
12
  import MobileImageMounterService from './services/ios/mobile-image-mounter/index.js';
@@ -118,6 +121,26 @@ export async function startWebInspectorService(udid) {
118
121
  ]),
119
122
  };
120
123
  }
124
+ export async function startDVTService(udid) {
125
+ const { remoteXPC, tunnelConnection } = await createRemoteXPCConnection(udid);
126
+ const dvtServiceDescriptor = remoteXPC.findService(DVTSecureSocketProxyService.RSD_SERVICE_NAME);
127
+ // Create DVT service instance
128
+ const dvtService = new DVTSecureSocketProxyService([
129
+ tunnelConnection.host,
130
+ parseInt(dvtServiceDescriptor.port, 10),
131
+ ]);
132
+ // Connect to DVT service
133
+ await dvtService.connect();
134
+ // Create instrument services
135
+ const locationSimulation = new LocationSimulation(dvtService);
136
+ const conditionInducer = new ConditionInducer(dvtService);
137
+ return {
138
+ remoteXPC: remoteXPC,
139
+ dvtService,
140
+ locationSimulation,
141
+ conditionInducer,
142
+ };
143
+ }
121
144
  export async function createRemoteXPCConnection(udid) {
122
145
  const tunnelConnection = await getTunnelInformation(udid);
123
146
  const remoteXPC = await startService(tunnelConnection.host, tunnelConnection.port);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-ios-remotexpc",
3
- "version": "0.13.2",
3
+ "version": "0.14.0",
4
4
  "main": "build/src/index.js",
5
5
  "types": "build/src/index.d.ts",
6
6
  "type": "module",
@@ -39,6 +39,9 @@
39
39
  "test:misagent": "mocha test/integration/misagent-service-test.ts --exit --timeout 1m",
40
40
  "test:afc": "mocha test/integration/afc-test.ts --exit --timeout 1m",
41
41
  "test:power-assertion": "mocha test/integration/power-assertion-test.ts --exit --timeout 1m",
42
+ "test:dvt": "mocha test/integration/dvt-service-test.ts --exit --timeout 1m",
43
+ "test:location-simulation-dvt": "mocha test/integration/dvt_instruments/location-simulation-test.ts --exit --timeout 1m",
44
+ "test:condition-inducer-dvt": "mocha test/integration/dvt_instruments/condition-inducer-test.ts --exit --timeout 1m",
42
45
  "test:tunnel-creation": "sudo tsx scripts/test-tunnel-creation.ts",
43
46
  "test:tunnel-creation:lsof": "sudo tsx scripts/test-tunnel-creation.ts --keep-open"
44
47
  },
package/src/index.ts CHANGED
@@ -24,6 +24,11 @@ export type {
24
24
  WebInspectorService,
25
25
  MisagentService,
26
26
  SyslogService,
27
+ DVTSecureSocketProxyService,
28
+ LocationSimulationService,
29
+ ConditionInducerService,
30
+ ConditionProfile,
31
+ ConditionGroup,
27
32
  SocketInfo,
28
33
  TunnelResult,
29
34
  TunnelRegistry,
@@ -36,6 +41,7 @@ export type {
36
41
  SpringboardServiceWithConnection,
37
42
  WebInspectorServiceWithConnection,
38
43
  MisagentServiceWithConnection,
44
+ DVTServiceWithConnection,
39
45
  } from './lib/types.js';
40
46
  export { PowerAssertionType } from './lib/types.js';
41
47
  export {
@@ -11,6 +11,7 @@ import {
11
11
  BPLIST_TRAILER_SIZE,
12
12
  BPLIST_TYPE,
13
13
  } from './constants.js';
14
+ import { PlistUID } from './plist-uid.js';
14
15
 
15
16
  /**
16
17
  * Checks if a value is a plain object (not null, not an array, not a Date, not a Buffer)
@@ -220,6 +221,30 @@ class BinaryPlistCreator {
220
221
  return Buffer.from([value ? BPLIST_TYPE.TRUE : BPLIST_TYPE.FALSE]);
221
222
  }
222
223
 
224
+ /**
225
+ * Creates UID data for NSKeyedArchiver references
226
+ * @param value - The UID value to encode
227
+ * @returns Buffer containing the encoded UID
228
+ */
229
+ private _createUIDData(value: number): Buffer {
230
+ // UID format: 0x80 | length_marker, followed by the UID value
231
+ let buffer: Buffer;
232
+ if (value <= 255) {
233
+ buffer = Buffer.alloc(2);
234
+ buffer.writeUInt8(BPLIST_TYPE.UID | 0, 0);
235
+ buffer.writeUInt8(value, 1);
236
+ } else if (value <= 65535) {
237
+ buffer = Buffer.alloc(3);
238
+ buffer.writeUInt8(BPLIST_TYPE.UID | 1, 0);
239
+ buffer.writeUInt16BE(value, 1);
240
+ } else {
241
+ buffer = Buffer.alloc(5);
242
+ buffer.writeUInt8(BPLIST_TYPE.UID | 2, 0);
243
+ buffer.writeUInt32BE(value, 1);
244
+ }
245
+ return buffer;
246
+ }
247
+
223
248
  /**
224
249
  * Creates binary data for an integer value
225
250
  * @param value - The integer value (number or bigint)
@@ -507,6 +532,11 @@ class BinaryPlistCreator {
507
532
  return this._createBufferData(value);
508
533
  }
509
534
 
535
+ // Handle UID (for NSKeyedArchiver format)
536
+ if (value instanceof PlistUID) {
537
+ return this._createUIDData(value.value);
538
+ }
539
+
510
540
  // Handle strings
511
541
  if (typeof value === 'string') {
512
542
  return this._createStringData(value);
@@ -26,6 +26,8 @@ import {
26
26
  trimBeforeXmlDeclaration,
27
27
  } from './utils.js';
28
28
 
29
+ export { PlistUID } from './plist-uid.js';
30
+
29
31
  export {
30
32
  createPlist,
31
33
  createXmlPlist,
@@ -0,0 +1,9 @@
1
+ import type { IPlistUID } from '../types.js';
2
+
3
+ /**
4
+ * UID (Unique Identifier) class for plist references
5
+ * Used in NSKeyedArchiver format
6
+ */
7
+ export class PlistUID implements IPlistUID {
8
+ constructor(public readonly value: number) {}
9
+ }