@basmilius/apple-common 0.9.18 → 0.9.19
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/dist/index.d.mts +97 -1
- package/dist/index.mjs +301 -15
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -437,6 +437,30 @@ declare const RAOP_SERVICE = "_raop._tcp.local";
|
|
|
437
437
|
//#region src/symbols.d.ts
|
|
438
438
|
declare const ENCRYPTION: unique symbol;
|
|
439
439
|
//#endregion
|
|
440
|
+
//#region src/recovery.d.ts
|
|
441
|
+
type EventMap$1 = {
|
|
442
|
+
recovering: [attempt: number];
|
|
443
|
+
recovered: [];
|
|
444
|
+
failed: [errors: Error[]];
|
|
445
|
+
};
|
|
446
|
+
type ConnectionRecoveryOptions = {
|
|
447
|
+
maxAttempts?: number;
|
|
448
|
+
baseDelay?: number;
|
|
449
|
+
maxDelay?: number;
|
|
450
|
+
useExponentialBackoff?: boolean;
|
|
451
|
+
reconnectInterval?: number;
|
|
452
|
+
onReconnect: () => Promise<void>;
|
|
453
|
+
};
|
|
454
|
+
declare class ConnectionRecovery extends EventEmitter<EventMap$1> {
|
|
455
|
+
#private;
|
|
456
|
+
constructor(options: ConnectionRecoveryOptions);
|
|
457
|
+
get isRecovering(): boolean;
|
|
458
|
+
get attempt(): number;
|
|
459
|
+
handleDisconnect(unexpected: boolean): void;
|
|
460
|
+
reset(): void;
|
|
461
|
+
dispose(): void;
|
|
462
|
+
}
|
|
463
|
+
//#endregion
|
|
440
464
|
//#region src/timing.d.ts
|
|
441
465
|
declare class TimingServer {
|
|
442
466
|
#private;
|
|
@@ -457,6 +481,78 @@ declare function randomInt64(): bigint;
|
|
|
457
481
|
declare function uint16ToBE(value: number): Buffer;
|
|
458
482
|
declare function uint53ToLE(value: number): Buffer;
|
|
459
483
|
//#endregion
|
|
484
|
+
//#region src/deviceModel.d.ts
|
|
485
|
+
declare enum DeviceModel {
|
|
486
|
+
Unknown = 0,
|
|
487
|
+
AppleTVGen2 = 1,
|
|
488
|
+
AppleTVGen3 = 2,
|
|
489
|
+
AppleTVHD = 3,
|
|
490
|
+
AppleTV4K = 4,
|
|
491
|
+
AppleTV4KGen2 = 5,
|
|
492
|
+
AppleTV4KGen3 = 6,
|
|
493
|
+
HomePod = 10,
|
|
494
|
+
HomePodMini = 11,
|
|
495
|
+
HomePodGen2 = 12,
|
|
496
|
+
AirPortExpress = 20,
|
|
497
|
+
AirPortExpressGen2 = 21
|
|
498
|
+
}
|
|
499
|
+
declare enum DeviceType {
|
|
500
|
+
Unknown = 0,
|
|
501
|
+
AppleTV = 1,
|
|
502
|
+
HomePod = 2,
|
|
503
|
+
AirPort = 3
|
|
504
|
+
}
|
|
505
|
+
declare const lookupDeviceModel: (identifier: string) => DeviceModel;
|
|
506
|
+
declare const getDeviceModelName: (model: DeviceModel) => string;
|
|
507
|
+
declare const getDeviceType: (model: DeviceModel) => DeviceType;
|
|
508
|
+
declare const isAppleTV: (model: DeviceModel) => boolean;
|
|
509
|
+
declare const isHomePod: (model: DeviceModel) => boolean;
|
|
510
|
+
declare const isAirPort: (model: DeviceModel) => boolean;
|
|
511
|
+
//#endregion
|
|
512
|
+
//#region src/errors.d.ts
|
|
513
|
+
declare class AppleProtocolError extends Error {
|
|
514
|
+
constructor(message: string);
|
|
515
|
+
}
|
|
516
|
+
declare class ConnectionError extends AppleProtocolError {
|
|
517
|
+
constructor(message: string);
|
|
518
|
+
}
|
|
519
|
+
declare class ConnectionTimeoutError extends ConnectionError {
|
|
520
|
+
constructor(message?: string);
|
|
521
|
+
}
|
|
522
|
+
declare class ConnectionClosedError extends ConnectionError {
|
|
523
|
+
constructor(message?: string);
|
|
524
|
+
}
|
|
525
|
+
declare class PairingError extends AppleProtocolError {
|
|
526
|
+
constructor(message: string);
|
|
527
|
+
}
|
|
528
|
+
declare class AuthenticationError extends PairingError {
|
|
529
|
+
constructor(message: string);
|
|
530
|
+
}
|
|
531
|
+
declare class CredentialsError extends PairingError {
|
|
532
|
+
constructor(message: string);
|
|
533
|
+
}
|
|
534
|
+
declare class CommandError extends AppleProtocolError {
|
|
535
|
+
constructor(message: string);
|
|
536
|
+
}
|
|
537
|
+
declare class SetupError extends AppleProtocolError {
|
|
538
|
+
constructor(message: string);
|
|
539
|
+
}
|
|
540
|
+
declare class DiscoveryError extends AppleProtocolError {
|
|
541
|
+
constructor(message: string);
|
|
542
|
+
}
|
|
543
|
+
declare class EncryptionError extends AppleProtocolError {
|
|
544
|
+
constructor(message: string);
|
|
545
|
+
}
|
|
546
|
+
declare class InvalidResponseError extends AppleProtocolError {
|
|
547
|
+
constructor(message: string);
|
|
548
|
+
}
|
|
549
|
+
declare class TimeoutError extends AppleProtocolError {
|
|
550
|
+
constructor(message: string);
|
|
551
|
+
}
|
|
552
|
+
declare class PlaybackError extends AppleProtocolError {
|
|
553
|
+
constructor(message: string);
|
|
554
|
+
}
|
|
555
|
+
//#endregion
|
|
460
556
|
//#region src/audioSource.d.ts
|
|
461
557
|
interface AudioSource {
|
|
462
558
|
get duration(): number;
|
|
@@ -466,4 +562,4 @@ interface AudioSource {
|
|
|
466
562
|
stop(): Promise<void>;
|
|
467
563
|
}
|
|
468
564
|
//#endregion
|
|
469
|
-
export { AIRPLAY_SERVICE, AIRPLAY_TRANSIENT_PIN, type AccessoryCredentials, type AccessoryKeys, AccessoryPair, AccessoryVerify, type AirPlayFeatureFlagName, AirPlayFeatureFlags, type AudioSource, COMPANION_LINK_SERVICE, type CombinedDiscoveryResult, Connection, type ConnectionState, Context, Discovery, type DiscoveryResult, ENCRYPTION, EncryptionAwareConnection, EncryptionState, type EventMap, HTTP_TIMEOUT, JsonStorage, type Logger, type MdnsService, MemoryStorage, type PairingRequirement, type ProtocolType, RAOP_SERVICE, type Reporter, Storage, type StorageData, type StoredDevice, TimingServer, describeFlags, generateActiveRemoteId, generateDacpId, generateSessionId, getLocalIP, getMacAddress, getPairingRequirement, getProtocolVersion, hasFeatureFlag, isPasswordRequired, isRemoteControlSupported, multicast as mdnsMulticast, unicast as mdnsUnicast, parseFeatures, prompt, randomInt32, randomInt64, reporter, uint16ToBE, uint53ToLE, v4 as uuid, waitFor };
|
|
565
|
+
export { AIRPLAY_SERVICE, AIRPLAY_TRANSIENT_PIN, type AccessoryCredentials, type AccessoryKeys, AccessoryPair, AccessoryVerify, type AirPlayFeatureFlagName, AirPlayFeatureFlags, AppleProtocolError, type AudioSource, AuthenticationError, COMPANION_LINK_SERVICE, type CombinedDiscoveryResult, CommandError, Connection, ConnectionClosedError, ConnectionError, ConnectionRecovery, type ConnectionRecoveryOptions, type ConnectionState, ConnectionTimeoutError, Context, CredentialsError, DeviceModel, DeviceType, Discovery, DiscoveryError, type DiscoveryResult, ENCRYPTION, EncryptionAwareConnection, EncryptionError, EncryptionState, type EventMap, HTTP_TIMEOUT, InvalidResponseError, JsonStorage, type Logger, type MdnsService, MemoryStorage, PairingError, type PairingRequirement, PlaybackError, type ProtocolType, RAOP_SERVICE, type Reporter, SetupError, Storage, type StorageData, type StoredDevice, TimeoutError, TimingServer, describeFlags, generateActiveRemoteId, generateDacpId, generateSessionId, getDeviceModelName, getDeviceType, getLocalIP, getMacAddress, getPairingRequirement, getProtocolVersion, hasFeatureFlag, isAirPort, isAppleTV, isHomePod, isPasswordRequired, isRemoteControlSupported, lookupDeviceModel, multicast as mdnsMulticast, unicast as mdnsUnicast, parseFeatures, prompt, randomInt32, randomInt64, reporter, uint16ToBE, uint53ToLE, v4 as uuid, waitFor };
|
package/dist/index.mjs
CHANGED
|
@@ -257,6 +257,93 @@ const AIRPLAY_SERVICE = "_airplay._tcp.local";
|
|
|
257
257
|
const COMPANION_LINK_SERVICE = "_companion-link._tcp.local";
|
|
258
258
|
const RAOP_SERVICE = "_raop._tcp.local";
|
|
259
259
|
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region src/errors.ts
|
|
262
|
+
var AppleProtocolError = class extends Error {
|
|
263
|
+
constructor(message) {
|
|
264
|
+
super(message);
|
|
265
|
+
this.name = "AppleProtocolError";
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
var ConnectionError = class extends AppleProtocolError {
|
|
269
|
+
constructor(message) {
|
|
270
|
+
super(message);
|
|
271
|
+
this.name = "ConnectionError";
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
var ConnectionTimeoutError = class extends ConnectionError {
|
|
275
|
+
constructor(message = "Connection timed out.") {
|
|
276
|
+
super(message);
|
|
277
|
+
this.name = "ConnectionTimeoutError";
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
var ConnectionClosedError = class extends ConnectionError {
|
|
281
|
+
constructor(message = "Connection closed unexpectedly.") {
|
|
282
|
+
super(message);
|
|
283
|
+
this.name = "ConnectionClosedError";
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
var PairingError = class extends AppleProtocolError {
|
|
287
|
+
constructor(message) {
|
|
288
|
+
super(message);
|
|
289
|
+
this.name = "PairingError";
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
var AuthenticationError = class extends PairingError {
|
|
293
|
+
constructor(message) {
|
|
294
|
+
super(message);
|
|
295
|
+
this.name = "AuthenticationError";
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
var CredentialsError = class extends PairingError {
|
|
299
|
+
constructor(message) {
|
|
300
|
+
super(message);
|
|
301
|
+
this.name = "CredentialsError";
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
var CommandError = class extends AppleProtocolError {
|
|
305
|
+
constructor(message) {
|
|
306
|
+
super(message);
|
|
307
|
+
this.name = "CommandError";
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
var SetupError = class extends AppleProtocolError {
|
|
311
|
+
constructor(message) {
|
|
312
|
+
super(message);
|
|
313
|
+
this.name = "SetupError";
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
var DiscoveryError = class extends AppleProtocolError {
|
|
317
|
+
constructor(message) {
|
|
318
|
+
super(message);
|
|
319
|
+
this.name = "DiscoveryError";
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
var EncryptionError = class extends AppleProtocolError {
|
|
323
|
+
constructor(message) {
|
|
324
|
+
super(message);
|
|
325
|
+
this.name = "EncryptionError";
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
var InvalidResponseError = class extends AppleProtocolError {
|
|
329
|
+
constructor(message) {
|
|
330
|
+
super(message);
|
|
331
|
+
this.name = "InvalidResponseError";
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
var TimeoutError = class extends AppleProtocolError {
|
|
335
|
+
constructor(message) {
|
|
336
|
+
super(message);
|
|
337
|
+
this.name = "TimeoutError";
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
var PlaybackError = class extends AppleProtocolError {
|
|
341
|
+
constructor(message) {
|
|
342
|
+
super(message);
|
|
343
|
+
this.name = "PlaybackError";
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
|
|
260
347
|
//#endregion
|
|
261
348
|
//#region src/mdns.ts
|
|
262
349
|
const MDNS_ADDRESS = "224.0.0.251";
|
|
@@ -321,10 +408,12 @@ function createQueryPackets(services, qtype = QueryType.PTR, unicastResponse = f
|
|
|
321
408
|
}
|
|
322
409
|
return packets;
|
|
323
410
|
}
|
|
411
|
+
const MAX_POINTER_JUMPS = 128;
|
|
324
412
|
const decodeQName = (buf, offset) => {
|
|
325
413
|
const labels = [];
|
|
326
414
|
let currentOffset = offset;
|
|
327
415
|
let jumped = false;
|
|
416
|
+
let jumps = 0;
|
|
328
417
|
let returnOffset = offset;
|
|
329
418
|
while (currentOffset < buf.byteLength) {
|
|
330
419
|
const length = buf[currentOffset];
|
|
@@ -333,6 +422,7 @@ const decodeQName = (buf, offset) => {
|
|
|
333
422
|
break;
|
|
334
423
|
}
|
|
335
424
|
if ((length & 192) === 192) {
|
|
425
|
+
if (++jumps > MAX_POINTER_JUMPS) break;
|
|
336
426
|
const pointer = (length & 63) << 8 | buf[currentOffset + 1];
|
|
337
427
|
if (!jumped) returnOffset = currentOffset + 2;
|
|
338
428
|
currentOffset = pointer;
|
|
@@ -713,7 +803,7 @@ var Discovery = class Discovery {
|
|
|
713
803
|
tries--;
|
|
714
804
|
await waitFor(timeout);
|
|
715
805
|
}
|
|
716
|
-
throw new
|
|
806
|
+
throw new DiscoveryError(`Device '${id}' not found after several tries, aborting.`);
|
|
717
807
|
}
|
|
718
808
|
static clearCache() {
|
|
719
809
|
Discovery.#cache.clear();
|
|
@@ -826,7 +916,7 @@ var Connection = class extends EventEmitter {
|
|
|
826
916
|
}
|
|
827
917
|
async connect() {
|
|
828
918
|
if (this.#state === "connected") return;
|
|
829
|
-
if (this.#state === "connecting") throw new
|
|
919
|
+
if (this.#state === "connecting") throw new ConnectionError("A connection is already being established.");
|
|
830
920
|
this.#retryEnabled = true;
|
|
831
921
|
this.#retryAttempt = 0;
|
|
832
922
|
return this.#attemptConnect();
|
|
@@ -861,7 +951,7 @@ var Connection = class extends EventEmitter {
|
|
|
861
951
|
}
|
|
862
952
|
write(data) {
|
|
863
953
|
if (!this.#socket || this.state !== "connected" || !this.#socket.writable) {
|
|
864
|
-
this.#emitInternal("error",
|
|
954
|
+
this.#emitInternal("error", new ConnectionClosedError("Cannot write to a disconnected connection."));
|
|
865
955
|
return;
|
|
866
956
|
}
|
|
867
957
|
this.#socket.write(data, (err) => {
|
|
@@ -942,7 +1032,7 @@ var Connection = class extends EventEmitter {
|
|
|
942
1032
|
this.#context.logger.net(`Connection closed (${hadError ? "with error" : "normally"}).`);
|
|
943
1033
|
}
|
|
944
1034
|
this.#emitInternal("close", hadError);
|
|
945
|
-
if (wasConnected && this.#retryEnabled && hadError) this.#scheduleRetry(
|
|
1035
|
+
if (wasConnected && this.#retryEnabled && hadError) this.#scheduleRetry(new ConnectionClosedError());
|
|
946
1036
|
}
|
|
947
1037
|
#onConnect() {
|
|
948
1038
|
this.#state = "connected";
|
|
@@ -974,7 +1064,7 @@ var Connection = class extends EventEmitter {
|
|
|
974
1064
|
}
|
|
975
1065
|
#onTimeout() {
|
|
976
1066
|
this.#context.logger.error("Connection timed out.");
|
|
977
|
-
const err =
|
|
1067
|
+
const err = new ConnectionTimeoutError();
|
|
978
1068
|
this.#emitInternal("timeout");
|
|
979
1069
|
if (this.#state === "connecting") this.#scheduleRetry(err);
|
|
980
1070
|
else {
|
|
@@ -3176,7 +3266,7 @@ var AccessoryPair = class extends BasePairing {
|
|
|
3176
3266
|
const m4 = await this.m4(m3);
|
|
3177
3267
|
const m5 = await this.m5(m4);
|
|
3178
3268
|
const m6 = await this.m6(m4, m5);
|
|
3179
|
-
if (!m6) throw new
|
|
3269
|
+
if (!m6) throw new PairingError("Pairing failed, could not get accessory keys.");
|
|
3180
3270
|
return m6;
|
|
3181
3271
|
}
|
|
3182
3272
|
async transient() {
|
|
@@ -3337,7 +3427,7 @@ var AccessoryPair = class extends BasePairing {
|
|
|
3337
3427
|
accessoryIdentifier,
|
|
3338
3428
|
accessoryLongTermPublicKey
|
|
3339
3429
|
]);
|
|
3340
|
-
if (!Ed25519.verify(accessoryInfo, accessorySignature, accessoryLongTermPublicKey)) throw new
|
|
3430
|
+
if (!Ed25519.verify(accessoryInfo, accessorySignature, accessoryLongTermPublicKey)) throw new AuthenticationError("Invalid accessory signature.");
|
|
3341
3431
|
return {
|
|
3342
3432
|
accessoryIdentifier: accessoryIdentifier.toString(),
|
|
3343
3433
|
accessoryLongTermPublicKey,
|
|
@@ -3410,13 +3500,13 @@ var AccessoryVerify = class extends BasePairing {
|
|
|
3410
3500
|
const tlv = TLV8.decode(data);
|
|
3411
3501
|
const accessoryIdentifier = tlv.get(TLV8.Value.Identifier);
|
|
3412
3502
|
const accessorySignature = tlv.get(TLV8.Value.Signature);
|
|
3413
|
-
if (accessoryIdentifier.toString() !== localAccessoryIdentifier) throw new
|
|
3503
|
+
if (accessoryIdentifier.toString() !== localAccessoryIdentifier) throw new AuthenticationError(`Invalid accessory identifier. Expected ${accessoryIdentifier.toString()} to be ${localAccessoryIdentifier}.`);
|
|
3414
3504
|
const accessoryInfo = Buffer.concat([
|
|
3415
3505
|
m1.serverPublicKey,
|
|
3416
3506
|
accessoryIdentifier,
|
|
3417
3507
|
this.#ephemeralKeyPair.publicKey
|
|
3418
3508
|
]);
|
|
3419
|
-
if (!Ed25519.verify(accessoryInfo, accessorySignature, longTermPublicKey)) throw new
|
|
3509
|
+
if (!Ed25519.verify(accessoryInfo, accessorySignature, longTermPublicKey)) throw new AuthenticationError("Invalid accessory signature.");
|
|
3420
3510
|
return {
|
|
3421
3511
|
serverEphemeralPublicKey: m1.serverPublicKey,
|
|
3422
3512
|
sessionKey,
|
|
@@ -3463,6 +3553,112 @@ var AccessoryVerify = class extends BasePairing {
|
|
|
3463
3553
|
//#region src/symbols.ts
|
|
3464
3554
|
const ENCRYPTION = Symbol();
|
|
3465
3555
|
|
|
3556
|
+
//#endregion
|
|
3557
|
+
//#region src/recovery.ts
|
|
3558
|
+
var ConnectionRecovery = class extends EventEmitter {
|
|
3559
|
+
#options;
|
|
3560
|
+
#attempt = 0;
|
|
3561
|
+
#errors = [];
|
|
3562
|
+
#isRecovering = false;
|
|
3563
|
+
#isScheduledReconnecting = false;
|
|
3564
|
+
#retryTimeout;
|
|
3565
|
+
#reconnectInterval;
|
|
3566
|
+
#disposed = false;
|
|
3567
|
+
constructor(options) {
|
|
3568
|
+
super();
|
|
3569
|
+
this.#options = {
|
|
3570
|
+
maxAttempts: 3,
|
|
3571
|
+
baseDelay: 1e3,
|
|
3572
|
+
maxDelay: 3e4,
|
|
3573
|
+
useExponentialBackoff: true,
|
|
3574
|
+
reconnectInterval: 0,
|
|
3575
|
+
...options
|
|
3576
|
+
};
|
|
3577
|
+
if (this.#options.reconnectInterval > 0) this.#startReconnectInterval();
|
|
3578
|
+
}
|
|
3579
|
+
get isRecovering() {
|
|
3580
|
+
return this.#isRecovering;
|
|
3581
|
+
}
|
|
3582
|
+
get attempt() {
|
|
3583
|
+
return this.#attempt;
|
|
3584
|
+
}
|
|
3585
|
+
handleDisconnect(unexpected) {
|
|
3586
|
+
if (this.#disposed || !unexpected) return;
|
|
3587
|
+
this.#stopReconnectInterval();
|
|
3588
|
+
this.#recover();
|
|
3589
|
+
}
|
|
3590
|
+
reset() {
|
|
3591
|
+
this.#attempt = 0;
|
|
3592
|
+
this.#errors = [];
|
|
3593
|
+
this.#isRecovering = false;
|
|
3594
|
+
if (this.#retryTimeout) {
|
|
3595
|
+
clearTimeout(this.#retryTimeout);
|
|
3596
|
+
this.#retryTimeout = void 0;
|
|
3597
|
+
}
|
|
3598
|
+
if (this.#options.reconnectInterval > 0) this.#startReconnectInterval();
|
|
3599
|
+
}
|
|
3600
|
+
dispose() {
|
|
3601
|
+
this.#disposed = true;
|
|
3602
|
+
this.#isRecovering = false;
|
|
3603
|
+
this.#isScheduledReconnecting = false;
|
|
3604
|
+
if (this.#retryTimeout) {
|
|
3605
|
+
clearTimeout(this.#retryTimeout);
|
|
3606
|
+
this.#retryTimeout = void 0;
|
|
3607
|
+
}
|
|
3608
|
+
this.#stopReconnectInterval();
|
|
3609
|
+
this.removeAllListeners();
|
|
3610
|
+
}
|
|
3611
|
+
#recover() {
|
|
3612
|
+
if (this.#isRecovering || this.#disposed) return;
|
|
3613
|
+
if (this.#attempt >= this.#options.maxAttempts) {
|
|
3614
|
+
this.emit("failed", this.#errors);
|
|
3615
|
+
return;
|
|
3616
|
+
}
|
|
3617
|
+
this.#isRecovering = true;
|
|
3618
|
+
this.#attempt++;
|
|
3619
|
+
this.emit("recovering", this.#attempt);
|
|
3620
|
+
const delay = this.#calculateDelay();
|
|
3621
|
+
this.#retryTimeout = setTimeout(async () => {
|
|
3622
|
+
this.#retryTimeout = void 0;
|
|
3623
|
+
try {
|
|
3624
|
+
await this.#options.onReconnect();
|
|
3625
|
+
this.#isRecovering = false;
|
|
3626
|
+
this.#attempt = 0;
|
|
3627
|
+
this.#errors = [];
|
|
3628
|
+
this.emit("recovered");
|
|
3629
|
+
if (this.#options.reconnectInterval > 0) this.#startReconnectInterval();
|
|
3630
|
+
} catch (err) {
|
|
3631
|
+
this.#isRecovering = false;
|
|
3632
|
+
this.#errors.push(err instanceof Error ? err : new Error(String(err)));
|
|
3633
|
+
this.#recover();
|
|
3634
|
+
}
|
|
3635
|
+
}, delay);
|
|
3636
|
+
}
|
|
3637
|
+
#calculateDelay() {
|
|
3638
|
+
if (!this.#options.useExponentialBackoff) return this.#options.baseDelay;
|
|
3639
|
+
const delay = this.#options.baseDelay * Math.pow(2, this.#attempt - 1);
|
|
3640
|
+
return Math.min(delay, this.#options.maxDelay);
|
|
3641
|
+
}
|
|
3642
|
+
#startReconnectInterval() {
|
|
3643
|
+
this.#stopReconnectInterval();
|
|
3644
|
+
this.#reconnectInterval = setInterval(async () => {
|
|
3645
|
+
if (this.#isRecovering || this.#disposed || this.#isScheduledReconnecting) return;
|
|
3646
|
+
this.#isScheduledReconnecting = true;
|
|
3647
|
+
try {
|
|
3648
|
+
await this.#options.onReconnect();
|
|
3649
|
+
} catch (_) {} finally {
|
|
3650
|
+
this.#isScheduledReconnecting = false;
|
|
3651
|
+
}
|
|
3652
|
+
}, this.#options.reconnectInterval);
|
|
3653
|
+
}
|
|
3654
|
+
#stopReconnectInterval() {
|
|
3655
|
+
if (this.#reconnectInterval) {
|
|
3656
|
+
clearInterval(this.#reconnectInterval);
|
|
3657
|
+
this.#reconnectInterval = void 0;
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
};
|
|
3661
|
+
|
|
3466
3662
|
//#endregion
|
|
3467
3663
|
//#region src/timing.ts
|
|
3468
3664
|
var TimingServer = class {
|
|
@@ -3485,13 +3681,18 @@ var TimingServer = class {
|
|
|
3485
3681
|
}
|
|
3486
3682
|
listen() {
|
|
3487
3683
|
return new Promise((resolve, reject) => {
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3684
|
+
const onError = (err) => {
|
|
3685
|
+
this.#socket.removeListener("listening", onListening);
|
|
3686
|
+
reject(err);
|
|
3687
|
+
};
|
|
3688
|
+
const onListening = () => {
|
|
3689
|
+
this.#socket.removeListener("error", onError);
|
|
3491
3690
|
this.#onListening();
|
|
3492
3691
|
resolve();
|
|
3493
|
-
}
|
|
3494
|
-
this.#socket.
|
|
3692
|
+
};
|
|
3693
|
+
this.#socket.once("error", onError);
|
|
3694
|
+
this.#socket.once("listening", onListening);
|
|
3695
|
+
this.#socket.bind(0);
|
|
3495
3696
|
});
|
|
3496
3697
|
}
|
|
3497
3698
|
#onConnect() {
|
|
@@ -3596,4 +3797,89 @@ function splitUInt53(number) {
|
|
|
3596
3797
|
}
|
|
3597
3798
|
|
|
3598
3799
|
//#endregion
|
|
3599
|
-
|
|
3800
|
+
//#region src/deviceModel.ts
|
|
3801
|
+
let DeviceModel = /* @__PURE__ */ function(DeviceModel) {
|
|
3802
|
+
DeviceModel[DeviceModel["Unknown"] = 0] = "Unknown";
|
|
3803
|
+
DeviceModel[DeviceModel["AppleTVGen2"] = 1] = "AppleTVGen2";
|
|
3804
|
+
DeviceModel[DeviceModel["AppleTVGen3"] = 2] = "AppleTVGen3";
|
|
3805
|
+
DeviceModel[DeviceModel["AppleTVHD"] = 3] = "AppleTVHD";
|
|
3806
|
+
DeviceModel[DeviceModel["AppleTV4K"] = 4] = "AppleTV4K";
|
|
3807
|
+
DeviceModel[DeviceModel["AppleTV4KGen2"] = 5] = "AppleTV4KGen2";
|
|
3808
|
+
DeviceModel[DeviceModel["AppleTV4KGen3"] = 6] = "AppleTV4KGen3";
|
|
3809
|
+
DeviceModel[DeviceModel["HomePod"] = 10] = "HomePod";
|
|
3810
|
+
DeviceModel[DeviceModel["HomePodMini"] = 11] = "HomePodMini";
|
|
3811
|
+
DeviceModel[DeviceModel["HomePodGen2"] = 12] = "HomePodGen2";
|
|
3812
|
+
DeviceModel[DeviceModel["AirPortExpress"] = 20] = "AirPortExpress";
|
|
3813
|
+
DeviceModel[DeviceModel["AirPortExpressGen2"] = 21] = "AirPortExpressGen2";
|
|
3814
|
+
return DeviceModel;
|
|
3815
|
+
}({});
|
|
3816
|
+
let DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
3817
|
+
DeviceType[DeviceType["Unknown"] = 0] = "Unknown";
|
|
3818
|
+
DeviceType[DeviceType["AppleTV"] = 1] = "AppleTV";
|
|
3819
|
+
DeviceType[DeviceType["HomePod"] = 2] = "HomePod";
|
|
3820
|
+
DeviceType[DeviceType["AirPort"] = 3] = "AirPort";
|
|
3821
|
+
return DeviceType;
|
|
3822
|
+
}({});
|
|
3823
|
+
const MODEL_IDENTIFIERS = {
|
|
3824
|
+
"AppleTV2,1": DeviceModel.AppleTVGen2,
|
|
3825
|
+
"AppleTV3,1": DeviceModel.AppleTVGen3,
|
|
3826
|
+
"AppleTV3,2": DeviceModel.AppleTVGen3,
|
|
3827
|
+
"AppleTV5,3": DeviceModel.AppleTVHD,
|
|
3828
|
+
"AppleTV6,2": DeviceModel.AppleTV4K,
|
|
3829
|
+
"AppleTV11,1": DeviceModel.AppleTV4KGen2,
|
|
3830
|
+
"AppleTV14,1": DeviceModel.AppleTV4KGen3,
|
|
3831
|
+
"AudioAccessory1,1": DeviceModel.HomePod,
|
|
3832
|
+
"AudioAccessory1,2": DeviceModel.HomePod,
|
|
3833
|
+
"AudioAccessory5,1": DeviceModel.HomePodMini,
|
|
3834
|
+
"AudioAccessorySingle5,1": DeviceModel.HomePodMini,
|
|
3835
|
+
"AudioAccessory6,1": DeviceModel.HomePodGen2,
|
|
3836
|
+
"AirPort4,107": DeviceModel.AirPortExpress,
|
|
3837
|
+
"AirPort10,115": DeviceModel.AirPortExpressGen2
|
|
3838
|
+
};
|
|
3839
|
+
const INTERNAL_NAMES = {
|
|
3840
|
+
"K66AP": DeviceModel.AppleTVGen2,
|
|
3841
|
+
"J33AP": DeviceModel.AppleTVGen3,
|
|
3842
|
+
"J33IAP": DeviceModel.AppleTVGen3,
|
|
3843
|
+
"J42dAP": DeviceModel.AppleTVHD,
|
|
3844
|
+
"J105aAP": DeviceModel.AppleTV4K,
|
|
3845
|
+
"J305AP": DeviceModel.AppleTV4KGen2,
|
|
3846
|
+
"J255AP": DeviceModel.AppleTV4KGen3,
|
|
3847
|
+
"B520AP": DeviceModel.HomePodMini
|
|
3848
|
+
};
|
|
3849
|
+
const MODEL_NAMES = {
|
|
3850
|
+
[DeviceModel.Unknown]: "Unknown",
|
|
3851
|
+
[DeviceModel.AppleTVGen2]: "Apple TV (2nd generation)",
|
|
3852
|
+
[DeviceModel.AppleTVGen3]: "Apple TV (3rd generation)",
|
|
3853
|
+
[DeviceModel.AppleTVHD]: "Apple TV HD",
|
|
3854
|
+
[DeviceModel.AppleTV4K]: "Apple TV 4K (1st generation)",
|
|
3855
|
+
[DeviceModel.AppleTV4KGen2]: "Apple TV 4K (2nd generation)",
|
|
3856
|
+
[DeviceModel.AppleTV4KGen3]: "Apple TV 4K (3rd generation)",
|
|
3857
|
+
[DeviceModel.HomePod]: "HomePod",
|
|
3858
|
+
[DeviceModel.HomePodMini]: "HomePod mini",
|
|
3859
|
+
[DeviceModel.HomePodGen2]: "HomePod (2nd generation)",
|
|
3860
|
+
[DeviceModel.AirPortExpress]: "AirPort Express",
|
|
3861
|
+
[DeviceModel.AirPortExpressGen2]: "AirPort Express (2nd generation)"
|
|
3862
|
+
};
|
|
3863
|
+
const MODEL_TYPES = {
|
|
3864
|
+
[DeviceModel.Unknown]: DeviceType.Unknown,
|
|
3865
|
+
[DeviceModel.AppleTVGen2]: DeviceType.AppleTV,
|
|
3866
|
+
[DeviceModel.AppleTVGen3]: DeviceType.AppleTV,
|
|
3867
|
+
[DeviceModel.AppleTVHD]: DeviceType.AppleTV,
|
|
3868
|
+
[DeviceModel.AppleTV4K]: DeviceType.AppleTV,
|
|
3869
|
+
[DeviceModel.AppleTV4KGen2]: DeviceType.AppleTV,
|
|
3870
|
+
[DeviceModel.AppleTV4KGen3]: DeviceType.AppleTV,
|
|
3871
|
+
[DeviceModel.HomePod]: DeviceType.HomePod,
|
|
3872
|
+
[DeviceModel.HomePodMini]: DeviceType.HomePod,
|
|
3873
|
+
[DeviceModel.HomePodGen2]: DeviceType.HomePod,
|
|
3874
|
+
[DeviceModel.AirPortExpress]: DeviceType.AirPort,
|
|
3875
|
+
[DeviceModel.AirPortExpressGen2]: DeviceType.AirPort
|
|
3876
|
+
};
|
|
3877
|
+
const lookupDeviceModel = (identifier) => MODEL_IDENTIFIERS[identifier] ?? INTERNAL_NAMES[identifier] ?? DeviceModel.Unknown;
|
|
3878
|
+
const getDeviceModelName = (model) => MODEL_NAMES[model] ?? "Unknown";
|
|
3879
|
+
const getDeviceType = (model) => MODEL_TYPES[model] ?? DeviceType.Unknown;
|
|
3880
|
+
const isAppleTV = (model) => getDeviceType(model) === DeviceType.AppleTV;
|
|
3881
|
+
const isHomePod = (model) => getDeviceType(model) === DeviceType.HomePod;
|
|
3882
|
+
const isAirPort = (model) => getDeviceType(model) === DeviceType.AirPort;
|
|
3883
|
+
|
|
3884
|
+
//#endregion
|
|
3885
|
+
export { AIRPLAY_SERVICE, AIRPLAY_TRANSIENT_PIN, AccessoryPair, AccessoryVerify, AirPlayFeatureFlags, AppleProtocolError, AuthenticationError, COMPANION_LINK_SERVICE, CommandError, Connection, ConnectionClosedError, ConnectionError, ConnectionRecovery, ConnectionTimeoutError, Context, CredentialsError, DeviceModel, DeviceType, Discovery, DiscoveryError, ENCRYPTION, EncryptionAwareConnection, EncryptionError, EncryptionState, HTTP_TIMEOUT, InvalidResponseError, JsonStorage, MemoryStorage, PairingError, PlaybackError, RAOP_SERVICE, SetupError, Storage, TimeoutError, TimingServer, describeFlags, generateActiveRemoteId, generateDacpId, generateSessionId, getDeviceModelName, getDeviceType, getLocalIP, getMacAddress, getPairingRequirement, getProtocolVersion, hasFeatureFlag, isAirPort, isAppleTV, isHomePod, isPasswordRequired, isRemoteControlSupported, lookupDeviceModel, multicast as mdnsMulticast, unicast as mdnsUnicast, parseFeatures, prompt, randomInt32, randomInt64, reporter, uint16ToBE, uint53ToLE, v4 as uuid, waitFor };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basmilius/apple-common",
|
|
3
3
|
"description": "Common features shared across various apple protocol packages.",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.19",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": {
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@basmilius/apple-encoding": "0.9.
|
|
49
|
-
"@basmilius/apple-encryption": "0.9.
|
|
48
|
+
"@basmilius/apple-encoding": "0.9.19",
|
|
49
|
+
"@basmilius/apple-encryption": "0.9.19"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/bun": "^1.3.11",
|