Skip to content

@corewire/core ​

@corewire/core has a main entry point and five subpaths. It knows no protocol.

@corewire/core ​

ts
import { AppleProtocolError, Logger, reporter } from '@corewire/core';

Errors ​

Every class takes a message: string. The connection errors have a default message.

ExportExtends
AppleProtocolErrorError
ConnectionErrorAppleProtocolError
ConnectionTimeoutErrorConnectionError
ConnectionClosedErrorConnectionError
PairingErrorAppleProtocolError
AuthenticationErrorPairingError
CredentialsErrorPairingError
CommandErrorAppleProtocolError
SetupErrorAppleProtocolError
DiscoveryErrorAppleProtocolError
EncryptionErrorAppleProtocolError
InvalidResponseErrorAppleProtocolError
TimeoutErrorAppleProtocolError
PlaybackErrorAppleProtocolError
DecryptionErrorEncryptionError; the authentication tag of a ChaCha20-Poly1305 frame did not match
TLV8PairingErrorPairingError; constructor(message: string, code?: number), code: number | undefined holds the TLV8 error code of the accessory

Logging ​

ExportDescription
class Loggerconstructor(id: string, sink?: LogSink); debug(), error(), info(), net(), raw(), warn(); traffic(protocol, direction, summary, decoded?, bytes?); id, label
reporter: ReporterThe global reporter
type Reporterall(), none(), enable(group), disable(group), isEnabled(group), accepts(group), printsToConsole(group), setSink(sink, options?), clearSink(), setTrafficSink(sink), clearTrafficSink(), reportTraffic(entry), report(group, deviceId, args), tapsTraffic
type DebugGroup'debug' | 'error' | 'info' | 'net' | 'raw' | 'warn'
type LogSink(group: DebugGroup, args: readonly unknown[]) => void
type ReporterEntry{group: DebugGroup; deviceId: string | null; args: readonly unknown[]; timestamp: number}
type ReporterSink(entry: ReporterEntry) => void
type ReporterSinkOptions{groups?: readonly DebugGroup[]; silenceConsole?: boolean}
type TrafficEntry{deviceId: string | null; protocol: TrafficProtocol; direction: TrafficDirection; summary: string; decoded?: unknown; bytes?: Uint8Array; timestamp: number}
type TrafficSink(entry: TrafficEntry) => void
type TrafficDirection'in' | 'out'
type TrafficProtocolstring

Helpers ​

ExportDescription
waitFor(ms: number): Promise<void>
type EventMapRecord<string, any>

@corewire/core/crypto ​

The subpath exports namespaces and two types. Acronyms stay in capitals: AES, ChaCha20, HKDF.

ExportDescription
ChaCha20encrypt(key, nonce, aad, plaintext): EncryptedData, decrypt(key, nonce, aad, ciphertext, authTag): Buffer (throws DecryptionError from the main entry), padNonce(nonce), CHACHA20_AUTH_TAG_LENGTH (16), CHACHA20_NONCE_LENGTH (12), type EncryptedData ({ciphertext; authTag})
Ed25519generateKeyPair(): KeyPair, sign(message, secretKey), verify(message, signature, publicKey)
Curve25519generateKeyPair(): KeyPair, generateSharedSecKey(priKey, pubKey)
AESencrypt(key, iv, plaintext), decrypt(key, iv, ciphertext); AES-128-CTR
HKDFderive(options: HKDFOptions): Buffer; deriveEncryptionKeys(sharedSecret: Buffer, salt: string, readInfo: string, writeInfo: string): {readKey: Buffer; writeKey: Buffer}
type HKDFOptions{hash; key; length; salt; info}
type KeyPair{publicKey: Uint8Array; secretKey: Uint8Array}

@corewire/core/discovery ​

ExportDescription
class Discoveryconstructor(service: string); find(useCache?), findUntil(id, tries?, timeout?), findByAddress(address, timeout?, wakePorts?); static findServices(services, useCache?), clearCache(), wake(address, ports)
mdnsMulticast(services: string[], timeout = 4): Promise<MdnsService[]>Timeout in seconds
mdnsUnicast(hosts: string[], services: string[], timeout = 4, wakePorts = []): Promise<MdnsService[]>
class MdnsResponderconstructor(logger: Logger, service: AdvertisedService, address?: string); start(), stop(), static localAddress()
type DiscoveryResult{id: string; fqdn: string; address: string; txt: Readonly<Record<string, string>>; service: {port: number; protocol: 'tcp' | 'udp'; type: string}}. The model identifier sits in txt.model or txt.am
type DiscoverOptions{useCache?: boolean}; reuses a scan younger than 30 seconds, default true. The one definition the protocol packages and devices share
type MdnsService{name; type; address; port; properties: Record<string, string>}
type AdvertisedService{instance; type; host; port; txt}

@corewire/core/encoding ​

ExportDescription
TLV8encode(entries), decode(buffer): Map<number, Buffer> (one value per type, the first run wins), decodeEntries(buffer): [number, Buffer][] (every entry in order, adjacent fragments joined), bail(data) (throws TLV8PairingError from the main entry), Value, State, Method, Flags, ErrorCode
OPackencode(data), decode(data), int(value: number | bigint), float(value), sizedInteger(value: number | bigint, size) with a size of 1, 2, 4, 8 or 16 bytes. Integers are signed; a sized integer outside the safe number range holds a bigint. Dates encode and decode as Date. encode throws a TypeError beyond 32 nested containers and decode throws a TypeError on a truncated buffer, a bad reference, deeper nesting or an unsupported tag
Plistserialize(value), parse(buffer), types PlistValue, PlistDictionary
DAAPencodeTag(), encodeContainer(), encodeTagWithSize(), encodePlaybackStatus(), encodeTrackMetadata(), decode(), decodeTag(), decodeToObject(), decodeTrackMetadata(), ContentCode, TagType and their types
NTPnow(): bigint, parts(ntp: bigint): [number, number], encode(fields), decode(buffer), type PacketFields
NSKeyedArchiverdecode(archive), decodeAsArray(archive)
RTIdecodeSession(data), encodeOperation(sessionUUID, text, clear), type TextInputSession

@corewire/core/pairing ​

ExportDescription
class PairSetupconstructor(context: Context, transport: PairingTransport, options?: PairingOptions); start(), pin(askPin): Promise<AccessoryCredentials>, transient(): Promise<AccessoryKeys>, m1() to m6()
class PairVerifyconstructor(context, transport, options?); start(credentials: AccessoryCredentials): Promise<AccessoryKeys>
class PairSetupServerconstructor(context, identity: AccessoryIdentity, pin: string, options?); handle(request: Buffer): Promise<Buffer>, controller, sharedSecret
class PairVerifyServerconstructor(context, identity, resolveController: ControllerResolver, options?); handle(request), pairingId, sharedSecret
generateAccessoryIdentity(): AccessoryIdentityA random identifier and a new Ed25519 key pair
deriveControlKeys(sharedSecret: Buffer): {accessoryToControllerKey: Buffer; controllerToAccessoryKey: Buffer}
TRANSIENT_PAIRING_PIN'3939'
type AccessoryCredentials{accessoryIdentifier: string; accessoryLongTermPublicKey: Buffer; pairingId: Buffer; publicKey: Buffer; secretKey: Buffer}
type AccessoryKeys{pairingId; sharedSecret; accessoryToControllerKey; controllerToAccessoryKey}, all Buffer
type AccessoryIdentity{identifier: string; publicKey: Buffer; secretKey: Buffer}
type PairedController{identifier: string; longTermPublicKey: Buffer}
type ControllerResolver(pairingId: string) => Buffer | undefined
type PairingOptions{useAes?: boolean}
type PairingStep'm1' | 'm3' | 'm5'
type PairingTransport(payload: Buffer, step: PairingStep) => Promise<Buffer>

@corewire/core/transport ​

ExportDescription
class Contextconstructor(deviceId: string, identity: DeviceIdentity = {}, sink?: LogSink); deviceId, identity (only the overrides you passed), logger
DEFAULT_CLIENT_NAME'CoreWire': the name a client presents when identity.name is not set
class Connection<TEventMap>constructor(context, address, port); connect(), disconnect(), destroy(error?), write(data), debug(enabled); address, port, localAddress, isConnected, state, signal, context; events connect, data, error, closed
class EncryptionAwareConnection<TEventMap>Connection plus enableEncryption(readKey, writeKey), isEncrypted, and the protected getter encryption: EncryptionState | undefined for subclasses
class EncryptionStateconstructor(readKey, writeKey); readKey, readCount, writeKey, writeCount
buildNonce(counter: number, format: NonceFormat): BufferA 12-byte nonce
type NonceFormat'counter-at-0' | 'counter-at-4'
type DeviceIdentity{name?; model?; osName?; osVersion?; osBuildVersion?; sourceVersion?; applicationBundleIdentifier?; applicationBundleVersion?}, all strings. A field you leave out, or set to undefined, keeps the default of the protocol package
type CloseReason'local' | 'remote' | 'timeout' | 'error'
type ConnectionState'closing' | 'connected' | 'connecting' | 'disconnected' | 'failed'

CoreWire is released under the MIT license.