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.
- package/CHANGELOG.md +6 -0
- package/build/src/index.d.ts +1 -1
- package/build/src/index.d.ts.map +1 -1
- package/build/src/lib/plist/binary-plist-creator.d.ts.map +1 -1
- package/build/src/lib/plist/binary-plist-creator.js +30 -0
- package/build/src/lib/plist/index.d.ts +1 -0
- package/build/src/lib/plist/index.d.ts.map +1 -1
- package/build/src/lib/plist/index.js +1 -0
- package/build/src/lib/plist/plist-uid.d.ts +10 -0
- package/build/src/lib/plist/plist-uid.d.ts.map +1 -0
- package/build/src/lib/plist/plist-uid.js +10 -0
- package/build/src/lib/types.d.ts +165 -2
- package/build/src/lib/types.d.ts.map +1 -1
- package/build/src/services/ios/dvt/channel-fragmenter.d.ts +21 -0
- package/build/src/services/ios/dvt/channel-fragmenter.d.ts.map +1 -0
- package/build/src/services/ios/dvt/channel-fragmenter.js +37 -0
- package/build/src/services/ios/dvt/channel.d.ts +32 -0
- package/build/src/services/ios/dvt/channel.d.ts.map +1 -0
- package/build/src/services/ios/dvt/channel.js +44 -0
- package/build/src/services/ios/dvt/dtx-message.d.ts +88 -0
- package/build/src/services/ios/dvt/dtx-message.d.ts.map +1 -0
- package/build/src/services/ios/dvt/dtx-message.js +113 -0
- package/build/src/services/ios/dvt/index.d.ts +119 -0
- package/build/src/services/ios/dvt/index.d.ts.map +1 -0
- package/build/src/services/ios/dvt/index.js +552 -0
- package/build/src/services/ios/dvt/instruments/condition-inducer.d.ts +37 -0
- package/build/src/services/ios/dvt/instruments/condition-inducer.d.ts.map +1 -0
- package/build/src/services/ios/dvt/instruments/condition-inducer.js +99 -0
- package/build/src/services/ios/dvt/instruments/location-simulation.d.ts +43 -0
- package/build/src/services/ios/dvt/instruments/location-simulation.d.ts.map +1 -0
- package/build/src/services/ios/dvt/instruments/location-simulation.js +60 -0
- package/build/src/services/ios/dvt/nskeyedarchiver-decoder.d.ts +41 -0
- package/build/src/services/ios/dvt/nskeyedarchiver-decoder.d.ts.map +1 -0
- package/build/src/services/ios/dvt/nskeyedarchiver-decoder.js +190 -0
- package/build/src/services/ios/dvt/utils.d.ts +19 -0
- package/build/src/services/ios/dvt/utils.d.ts.map +1 -0
- package/build/src/services/ios/dvt/utils.js +67 -0
- package/build/src/services.d.ts +2 -1
- package/build/src/services.d.ts.map +1 -1
- package/build/src/services.js +23 -0
- package/package.json +4 -1
- package/src/index.ts +6 -0
- package/src/lib/plist/binary-plist-creator.ts +30 -0
- package/src/lib/plist/index.ts +2 -0
- package/src/lib/plist/plist-uid.ts +9 -0
- package/src/lib/types.ts +179 -1
- package/src/services/ios/dvt/channel-fragmenter.ts +42 -0
- package/src/services/ios/dvt/channel.ts +58 -0
- package/src/services/ios/dvt/dtx-message.ts +162 -0
- package/src/services/ios/dvt/index.ts +727 -0
- package/src/services/ios/dvt/instruments/condition-inducer.ts +140 -0
- package/src/services/ios/dvt/instruments/location-simulation.ts +83 -0
- package/src/services/ios/dvt/nskeyedarchiver-decoder.ts +219 -0
- package/src/services/ios/dvt/utils.ts +89 -0
- package/src/services.ts +33 -0
package/src/lib/types.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { EventEmitter } from 'events';
|
|
|
6
6
|
|
|
7
7
|
import type { ServiceConnection } from '../service-connection.js';
|
|
8
8
|
import type { BaseService, Service } from '../services/ios/base-service.js';
|
|
9
|
+
import type { LocationCoordinates } from '../services/ios/dvt/instruments/location-simulation.js';
|
|
9
10
|
import { ProvisioningProfile } from '../services/ios/misagent/provisioning-profile.js';
|
|
10
11
|
import type { PowerAssertionOptions } from '../services/ios/power-assertion/index.js';
|
|
11
12
|
import { PowerAssertionType } from '../services/ios/power-assertion/index.js';
|
|
@@ -17,8 +18,13 @@ export type { PowerAssertionOptions };
|
|
|
17
18
|
export { PowerAssertionType };
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
|
-
*
|
|
21
|
+
* UID (Unique Identifier) interface for plist references
|
|
22
|
+
* Used in NSKeyedArchiver format
|
|
21
23
|
*/
|
|
24
|
+
export interface IPlistUID {
|
|
25
|
+
value: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
22
28
|
export type PlistValue =
|
|
23
29
|
| string
|
|
24
30
|
| number
|
|
@@ -26,6 +32,7 @@ export type PlistValue =
|
|
|
26
32
|
| boolean
|
|
27
33
|
| Date
|
|
28
34
|
| Buffer
|
|
35
|
+
| IPlistUID
|
|
29
36
|
| PlistArray
|
|
30
37
|
| PlistDictionary
|
|
31
38
|
| null;
|
|
@@ -347,6 +354,177 @@ export interface PowerAssertionServiceWithConnection {
|
|
|
347
354
|
remoteXPC: RemoteXpcConnection;
|
|
348
355
|
}
|
|
349
356
|
|
|
357
|
+
/**
|
|
358
|
+
* DVT (Developer Tools) service interface
|
|
359
|
+
*/
|
|
360
|
+
export interface DVTSecureSocketProxyService extends BaseService {
|
|
361
|
+
/**
|
|
362
|
+
* Connect to the DVT service
|
|
363
|
+
*/
|
|
364
|
+
connect(): Promise<void>;
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Get supported identifiers (capabilities)
|
|
368
|
+
* @example
|
|
369
|
+
* const capabilities = dvtService.getSupportedIdentifiers();
|
|
370
|
+
* // Example output:
|
|
371
|
+
* // {
|
|
372
|
+
* // "com.apple.instruments.server.services.processcontrol.capability.memorylimits": 1,
|
|
373
|
+
* // "com.apple.instruments.server.services.coreml.perfrunner": 4,
|
|
374
|
+
* // "com.apple.instruments.server.services.processcontrolbydictionary": 4,
|
|
375
|
+
* // "com.apple.instruments.server.services.graphics.coreanimation.immediate": 1,
|
|
376
|
+
* // // ... more identifiers
|
|
377
|
+
* // }
|
|
378
|
+
*/
|
|
379
|
+
getSupportedIdentifiers(): PlistDictionary;
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Create a channel for a specific identifier
|
|
383
|
+
* @param identifier The channel identifier
|
|
384
|
+
* @returns The created channel
|
|
385
|
+
*/
|
|
386
|
+
makeChannel(identifier: string): Promise<any>;
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Close the DVT service connection
|
|
390
|
+
*/
|
|
391
|
+
close(): Promise<void>;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Location simulation service interface
|
|
396
|
+
*/
|
|
397
|
+
export interface LocationSimulationService {
|
|
398
|
+
/**
|
|
399
|
+
* Set the simulated location
|
|
400
|
+
* @param latitude The latitude
|
|
401
|
+
* @param longitude The longitude
|
|
402
|
+
*/
|
|
403
|
+
setLocation(latitude: number, longitude: number): Promise<void>;
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Set the simulated location using the LocationCoordinates type.
|
|
407
|
+
* @param coordinates The location coordinates
|
|
408
|
+
*/
|
|
409
|
+
set(coordinates: LocationCoordinates): Promise<void>;
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Clear/stop location simulation
|
|
413
|
+
*
|
|
414
|
+
* Note: This method is safe to call even if no location simulation is currently active.
|
|
415
|
+
*/
|
|
416
|
+
clear(): Promise<void>;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Condition profile information
|
|
421
|
+
*/
|
|
422
|
+
export interface ConditionProfile {
|
|
423
|
+
identifier: string;
|
|
424
|
+
description?: string;
|
|
425
|
+
[key: string]: any;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Condition group information
|
|
430
|
+
*/
|
|
431
|
+
export interface ConditionGroup {
|
|
432
|
+
identifier: string;
|
|
433
|
+
profiles: ConditionProfile[];
|
|
434
|
+
[key: string]: any;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Condition inducer service interface
|
|
439
|
+
*/
|
|
440
|
+
export interface ConditionInducerService {
|
|
441
|
+
/**
|
|
442
|
+
* List all available condition inducers and their profiles
|
|
443
|
+
*
|
|
444
|
+
* Each group in the response contains information about whether a condition
|
|
445
|
+
* is currently active via the `isActive` field and which profile is active
|
|
446
|
+
* via the `activeProfile` field.
|
|
447
|
+
*
|
|
448
|
+
* @returns Array of condition groups with their available profiles
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
* ```typescript
|
|
452
|
+
* const groups = await conditionInducer.list();
|
|
453
|
+
* // Example response:
|
|
454
|
+
* // [
|
|
455
|
+
* // {
|
|
456
|
+
* // "profiles": [
|
|
457
|
+
* // {
|
|
458
|
+
* // "name": "100% packet loss",
|
|
459
|
+
* // "identifier": "SlowNetwork100PctLoss",
|
|
460
|
+
* // "description": "Name: 100% Loss Scenario\nDownlink Bandwidth: 0 Mbps\nDownlink Latency: 0 ms\nDownlink Packet Loss Ratio: 100%\nUplink Bandwidth: 0 Mbps\nUplink Latency: 0 ms\nUplink Packet Loss Ratio: 100%"
|
|
461
|
+
* // },
|
|
462
|
+
* // // ... more profiles
|
|
463
|
+
* // ],
|
|
464
|
+
* // "profilesSorted": true,
|
|
465
|
+
* // "identifier": "SlowNetworkCondition",
|
|
466
|
+
* // "isDestructive": false,
|
|
467
|
+
* // "isInternal": false,
|
|
468
|
+
* // "activeProfile": "",
|
|
469
|
+
* // "name": "Network Link",
|
|
470
|
+
* // "isActive": false
|
|
471
|
+
* // },
|
|
472
|
+
* // // ... more groups
|
|
473
|
+
* // ]
|
|
474
|
+
* ```
|
|
475
|
+
*/
|
|
476
|
+
list(): Promise<ConditionGroup[]>;
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Set a specific condition profile
|
|
480
|
+
*
|
|
481
|
+
* Note: If a condition is already active, attempting to set a new one will
|
|
482
|
+
* throw an error: {'NSLocalizedDescription': 'A condition is already active.'}
|
|
483
|
+
* You must call disable() first before setting a different condition.
|
|
484
|
+
*
|
|
485
|
+
* Available profile identifiers include (but may vary by iOS version):
|
|
486
|
+
* - Network profiles: SlowNetwork100PctLoss, SlowNetworkVeryBadNetwork,
|
|
487
|
+
* SlowNetworkEdgeBad, SlowNetworkEdgeAverage, SlowNetworkEdgeGood,
|
|
488
|
+
* SlowNetworkEdge, SlowNetwork2GRural, SlowNetwork2GUrban,
|
|
489
|
+
* SlowNetwork3GAverage, SlowNetwork3GGood, SlowNetwork3G,
|
|
490
|
+
* SlowNetworkLTE, SlowNetworkWiFi, SlowNetworkWiFi80211AC,
|
|
491
|
+
* SlowNetworkDSL, SlowNetworkHighLatencyDNS
|
|
492
|
+
* - Thermal profiles: ThermalFair, ThermalSerious, ThermalCritical
|
|
493
|
+
* - GPU profiles: GPUPerformanceStateMin, GPUPerformanceStateMid, GPUPerformanceStateMax
|
|
494
|
+
* - And others depending on device capabilities
|
|
495
|
+
*
|
|
496
|
+
* Use list() to see all available profiles for your device.
|
|
497
|
+
*
|
|
498
|
+
* @param profileIdentifier The identifier of the profile to enable
|
|
499
|
+
* @throws Error if the profile identifier is not found
|
|
500
|
+
* @throws Error if a condition is already active
|
|
501
|
+
*/
|
|
502
|
+
set(profileIdentifier: string): Promise<void>;
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Disable the currently active condition
|
|
506
|
+
*
|
|
507
|
+
* Note: This method is idempotent - calling it when no condition is active
|
|
508
|
+
* will not throw an error.
|
|
509
|
+
*/
|
|
510
|
+
disable(): Promise<void>;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* DVT service with connection
|
|
515
|
+
* This allows callers to properly manage the connection lifecycle
|
|
516
|
+
*/
|
|
517
|
+
export interface DVTServiceWithConnection {
|
|
518
|
+
/** The DVTSecureSocketProxyService instance */
|
|
519
|
+
dvtService: DVTSecureSocketProxyService;
|
|
520
|
+
/** The LocationSimulation service instance */
|
|
521
|
+
locationSimulation: LocationSimulationService;
|
|
522
|
+
/** The ConditionInducer service instance */
|
|
523
|
+
conditionInducer: ConditionInducerService;
|
|
524
|
+
/** The RemoteXPC connection that can be used to close the connection */
|
|
525
|
+
remoteXPC: RemoteXpcConnection;
|
|
526
|
+
}
|
|
527
|
+
|
|
350
528
|
/**
|
|
351
529
|
* Represents the WebInspectorService
|
|
352
530
|
*/
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { DTXMessageHeader } from './dtx-message.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handles message fragmentation for DTX channels
|
|
5
|
+
* Assembles fragmented messages and queues complete messages for retrieval
|
|
6
|
+
*/
|
|
7
|
+
export class ChannelFragmenter {
|
|
8
|
+
private readonly messages: Buffer[] = [];
|
|
9
|
+
private packetData: Buffer = Buffer.alloc(0);
|
|
10
|
+
private streamPacketData: Buffer = Buffer.alloc(0);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get the next complete message from the queue
|
|
14
|
+
*/
|
|
15
|
+
get(): Buffer | null {
|
|
16
|
+
return this.messages.shift() || null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Add a message fragment and assemble if complete
|
|
21
|
+
* @param header The message header
|
|
22
|
+
* @param chunk The message data chunk
|
|
23
|
+
*/
|
|
24
|
+
addFragment(header: DTXMessageHeader, chunk: Buffer): void {
|
|
25
|
+
// Handle positive vs negative channel codes (regular vs stream data)
|
|
26
|
+
if (header.channelCode >= 0) {
|
|
27
|
+
this.packetData = Buffer.concat([this.packetData, chunk]);
|
|
28
|
+
|
|
29
|
+
if (header.fragmentId === header.fragmentCount - 1) {
|
|
30
|
+
this.messages.push(this.packetData);
|
|
31
|
+
this.packetData = Buffer.alloc(0);
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
this.streamPacketData = Buffer.concat([this.streamPacketData, chunk]);
|
|
35
|
+
|
|
36
|
+
if (header.fragmentId === header.fragmentCount - 1) {
|
|
37
|
+
this.messages.push(this.streamPacketData);
|
|
38
|
+
this.streamPacketData = Buffer.alloc(0);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { MessageAux } from './dtx-message.js';
|
|
2
|
+
import type { DVTSecureSocketProxyService } from './index.js';
|
|
3
|
+
|
|
4
|
+
export type ChannelMethodCall = (
|
|
5
|
+
args?: MessageAux,
|
|
6
|
+
expectsReply?: boolean,
|
|
7
|
+
) => Promise<void>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Represents a DTX communication channel for a specific instrument service
|
|
11
|
+
*/
|
|
12
|
+
export class Channel {
|
|
13
|
+
constructor(
|
|
14
|
+
private readonly channelCode: number,
|
|
15
|
+
private readonly service: DVTSecureSocketProxyService,
|
|
16
|
+
) {}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Receive a plist response from the channel
|
|
20
|
+
*/
|
|
21
|
+
async receivePlist(): Promise<any> {
|
|
22
|
+
const [data] = await this.service.recvPlist(this.channelCode);
|
|
23
|
+
return data;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Call a method on this channel with automatic ObjectiveC selector conversion
|
|
28
|
+
*
|
|
29
|
+
* Converts method names to ObjectiveC selector format:
|
|
30
|
+
* - 'methodName' -> 'methodName'
|
|
31
|
+
* - 'method_name' -> 'method:name:'
|
|
32
|
+
* - '_method_name' -> '_method:name:'
|
|
33
|
+
*
|
|
34
|
+
* @param methodName The method name
|
|
35
|
+
* @returns A function that sends the message with optional arguments
|
|
36
|
+
*/
|
|
37
|
+
call(methodName: string): ChannelMethodCall {
|
|
38
|
+
const selector = this.convertToSelector(methodName);
|
|
39
|
+
return (async (args, expectsReply = true) => {
|
|
40
|
+
await this.service.sendMessage(
|
|
41
|
+
this.channelCode,
|
|
42
|
+
selector,
|
|
43
|
+
args,
|
|
44
|
+
expectsReply,
|
|
45
|
+
);
|
|
46
|
+
}) as ChannelMethodCall;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Convert method name to ObjectiveC selector format
|
|
51
|
+
*/
|
|
52
|
+
private convertToSelector(name: string): string {
|
|
53
|
+
if (name.startsWith('_')) {
|
|
54
|
+
return '_' + name.substring(1).replace(/_/g, ':');
|
|
55
|
+
}
|
|
56
|
+
return name.replace(/_/g, ':');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DTX Message Header structure
|
|
3
|
+
*/
|
|
4
|
+
export interface DTXMessageHeader {
|
|
5
|
+
magic: number;
|
|
6
|
+
cb: number;
|
|
7
|
+
fragmentId: number;
|
|
8
|
+
fragmentCount: number;
|
|
9
|
+
length: number;
|
|
10
|
+
identifier: number;
|
|
11
|
+
conversationIndex: number;
|
|
12
|
+
channelCode: number;
|
|
13
|
+
expectsReply: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* DTX Message Payload Header structure
|
|
18
|
+
*/
|
|
19
|
+
export interface DTXMessagePayloadHeader {
|
|
20
|
+
flags: number;
|
|
21
|
+
auxiliaryLength: number;
|
|
22
|
+
totalLength: bigint;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Message auxiliary value structure
|
|
27
|
+
*/
|
|
28
|
+
export interface MessageAuxValue {
|
|
29
|
+
type: number;
|
|
30
|
+
value: any;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* DTX Protocol constants
|
|
35
|
+
*/
|
|
36
|
+
export const DTX_CONSTANTS = {
|
|
37
|
+
MESSAGE_HEADER_MAGIC: 0x1f3d5b79,
|
|
38
|
+
MESSAGE_HEADER_SIZE: 32,
|
|
39
|
+
PAYLOAD_HEADER_SIZE: 16,
|
|
40
|
+
MESSAGE_AUX_MAGIC: 0x1f0,
|
|
41
|
+
EMPTY_DICTIONARY: 0xa,
|
|
42
|
+
|
|
43
|
+
// Message types
|
|
44
|
+
INSTRUMENTS_MESSAGE_TYPE: 2,
|
|
45
|
+
EXPECTS_REPLY_MASK: 0x1000,
|
|
46
|
+
|
|
47
|
+
// Auxiliary value types
|
|
48
|
+
AUX_TYPE_OBJECT: 2,
|
|
49
|
+
AUX_TYPE_INT32: 3,
|
|
50
|
+
AUX_TYPE_INT64: 6,
|
|
51
|
+
} as const;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* DTX Message utilities for encoding and decoding protocol messages
|
|
55
|
+
*/
|
|
56
|
+
export class DTXMessage {
|
|
57
|
+
/**
|
|
58
|
+
* Parse DTX message header from buffer
|
|
59
|
+
*/
|
|
60
|
+
static parseMessageHeader(buffer: Buffer): DTXMessageHeader {
|
|
61
|
+
if (buffer.length < DTX_CONSTANTS.MESSAGE_HEADER_SIZE) {
|
|
62
|
+
throw new Error('Buffer too small for DTX message header');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
magic: buffer.readUInt32LE(0),
|
|
67
|
+
cb: buffer.readUInt32LE(4),
|
|
68
|
+
fragmentId: buffer.readUInt16LE(8),
|
|
69
|
+
fragmentCount: buffer.readUInt16LE(10),
|
|
70
|
+
length: buffer.readUInt32LE(12),
|
|
71
|
+
identifier: buffer.readUInt32LE(16),
|
|
72
|
+
conversationIndex: buffer.readUInt32LE(20),
|
|
73
|
+
channelCode: buffer.readInt32LE(24),
|
|
74
|
+
expectsReply: buffer.readUInt32LE(28),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Build DTX message header buffer
|
|
80
|
+
*/
|
|
81
|
+
static buildMessageHeader(header: DTXMessageHeader): Buffer {
|
|
82
|
+
const buffer = Buffer.alloc(DTX_CONSTANTS.MESSAGE_HEADER_SIZE);
|
|
83
|
+
|
|
84
|
+
buffer.writeUInt32LE(header.magic, 0);
|
|
85
|
+
buffer.writeUInt32LE(header.cb, 4);
|
|
86
|
+
buffer.writeUInt16LE(header.fragmentId, 8);
|
|
87
|
+
buffer.writeUInt16LE(header.fragmentCount, 10);
|
|
88
|
+
buffer.writeUInt32LE(header.length, 12);
|
|
89
|
+
buffer.writeUInt32LE(header.identifier, 16);
|
|
90
|
+
buffer.writeUInt32LE(header.conversationIndex, 20);
|
|
91
|
+
buffer.writeInt32LE(header.channelCode, 24);
|
|
92
|
+
buffer.writeUInt32LE(header.expectsReply, 28);
|
|
93
|
+
|
|
94
|
+
return buffer;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Parse DTX payload header from buffer
|
|
99
|
+
*/
|
|
100
|
+
static parsePayloadHeader(buffer: Buffer): DTXMessagePayloadHeader {
|
|
101
|
+
if (buffer.length < DTX_CONSTANTS.PAYLOAD_HEADER_SIZE) {
|
|
102
|
+
throw new Error('Buffer too small for DTX payload header');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
flags: buffer.readUInt32LE(0),
|
|
107
|
+
auxiliaryLength: buffer.readUInt32LE(4),
|
|
108
|
+
totalLength: buffer.readBigUInt64LE(8),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Build DTX payload header buffer
|
|
114
|
+
*/
|
|
115
|
+
static buildPayloadHeader(header: DTXMessagePayloadHeader): Buffer {
|
|
116
|
+
const buffer = Buffer.alloc(DTX_CONSTANTS.PAYLOAD_HEADER_SIZE);
|
|
117
|
+
|
|
118
|
+
buffer.writeUInt32LE(header.flags, 0);
|
|
119
|
+
buffer.writeUInt32LE(header.auxiliaryLength, 4);
|
|
120
|
+
buffer.writeBigUInt64LE(header.totalLength, 8);
|
|
121
|
+
|
|
122
|
+
return buffer;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Message auxiliary builder for DTX protocol parameters
|
|
128
|
+
*/
|
|
129
|
+
export class MessageAux {
|
|
130
|
+
private readonly values: MessageAuxValue[] = [];
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Append a 32-bit integer
|
|
134
|
+
*/
|
|
135
|
+
appendInt(value: number): MessageAux {
|
|
136
|
+
this.values.push({ type: DTX_CONSTANTS.AUX_TYPE_INT32, value });
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Append a 64-bit integer (bigint)
|
|
142
|
+
*/
|
|
143
|
+
appendLong(value: bigint): MessageAux {
|
|
144
|
+
this.values.push({ type: DTX_CONSTANTS.AUX_TYPE_INT64, value });
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Append an object (will be archived as NSKeyedArchiver plist)
|
|
150
|
+
*/
|
|
151
|
+
appendObj(value: any): MessageAux {
|
|
152
|
+
this.values.push({ type: DTX_CONSTANTS.AUX_TYPE_OBJECT, value });
|
|
153
|
+
return this;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get raw values for encoding
|
|
158
|
+
*/
|
|
159
|
+
getValues(): MessageAuxValue[] {
|
|
160
|
+
return this.values;
|
|
161
|
+
}
|
|
162
|
+
}
|