///
///
import EventEmitter from "events";
import type { DeviceModel } from "@ledgerhq/devices";
import { TransportError, StatusCodes, getAltStatusMessage, TransportStatusError } from "@ledgerhq/errors";
import { LocalTracer, TraceContext, LogType } from "@ledgerhq/logs";
export { TransportError, TransportStatusError, StatusCodes, getAltStatusMessage };
/**
*/
export type Subscription = {
unsubscribe: () => void;
};
/**
*/
export type Device = any;
export type DescriptorEventType = "add" | "remove";
/**
* A "descriptor" is a parameter that is specific to the implementation, and can be an ID, file path, or URL.
* type: add or remove event
* descriptor: a parameter that can be passed to open(descriptor)
* deviceModel: device info on the model (is it a nano s, nano x, ...)
* device: transport specific device info
*/
export interface DescriptorEvent {
type: DescriptorEventType;
descriptor: Descriptor;
deviceModel?: DeviceModel | null | undefined;
device?: Device;
}
/**
* Observer generic type, following the Observer pattern
*/
export type Observer = Readonly<{
next: (event: EventType) => unknown;
error: (e: EventError) => unknown;
complete: () => unknown;
}>;
/**
* The Transport class defines a generic interface for communicating with a Ledger hardware wallet.
* There are different kind of transports based on the technology (channels like U2F, HID, Bluetooth, Webusb) and environment (Node, Web,...).
* It is an abstract class that needs to be implemented.
*/
export default class Transport {
exchangeTimeout: number;
unresponsiveTimeout: number;
deviceModel: DeviceModel | null | undefined;
tracer: LocalTracer;
constructor({ context, logType }?: {
context?: TraceContext;
logType?: LogType;
});
/**
* Check if the transport is supported on the current platform/browser.
* @returns {Promise} A promise that resolves with a boolean indicating support.
*/
static readonly isSupported: () => Promise;
/**
* List all available descriptors for the transport.
* For a better granularity, checkout `listen()`.
*
* @returns {Promise>} A promise that resolves with an array of descriptors.
* @example
* TransportFoo.list().then(descriptors => ...)
*/
static readonly list: () => Promise>;
/**
* Listen for device events for the transport. The method takes an observer of DescriptorEvent and returns a Subscription.
* A DescriptorEvent is an object containing a "descriptor" and a "type" field. The "type" field can be "add" or "remove", and the "descriptor" field can be passed to the "open" method.
* The "listen" method will first emit all currently connected devices and then will emit events as they occur, such as when a USB device is plugged in or a Bluetooth device becomes discoverable.
* @param {Observer>} observer - An object with "next", "error", and "complete" functions, following the observer pattern.
* @returns {Subscription} A Subscription object on which you can call ".unsubscribe()" to stop listening to descriptors.
* @example
const sub = TransportFoo.listen({
next: e => {
if (e.type==="add") {
sub.unsubscribe();
const transport = await TransportFoo.open(e.descriptor);
...
}
},
error: error => {},
complete: () => {}
})
*/
static readonly listen: (observer: Observer>) => Subscription;
/**
* Attempt to create a Transport instance with a specific descriptor.
* @param {any} descriptor - The descriptor to open the transport with.
* @param {number} timeout - An optional timeout for the transport connection.
* @param {TraceContext} context Optional tracing/log context
* @returns {Promise} A promise that resolves with a Transport instance.
* @example
TransportFoo.open(descriptor).then(transport => ...)
*/
static readonly open: (descriptor?: any, timeoutMs?: number, context?: TraceContext) => Promise;
/**
* Send data to the device using a low level API.
* It's recommended to use the "send" method for a higher level API.
* @param {Buffer} apdu - The data to send.
* @param {Object} options - Contains optional options for the exchange function
* - abortTimeoutMs: stop the exchange after a given timeout. Another timeout exists
* to detect unresponsive device (see `unresponsiveTimeout`). This timeout aborts the exchange.
* @returns {Promise} A promise that resolves with the response data from the device.
*/
exchange(_apdu: Buffer, { abortTimeoutMs }?: {
abortTimeoutMs?: number;
}): Promise;
/**
* Send apdus in batch to the device using a low level API.
* The default implementation is to call exchange for each apdu.
* @param {Array} apdus - array of apdus to send.
* @param {Observer} observer - an observer that will receive the response of each apdu.
* @returns {Subscription} A Subscription object on which you can call ".unsubscribe()" to stop sending apdus.
*/
exchangeBulk(apdus: Buffer[], observer: Observer): Subscription;
/**
* Set the "scramble key" for the next data exchanges with the device.
* Each app can have a different scramble key and it is set internally during instantiation.
* @param {string} key - The scramble key to set.
* deprecated This method is no longer needed for modern transports and should be migrated away from.
* no @ before deprecated as it breaks documentationjs on version 14.0.2
* https://github.com/documentationjs/documentation/issues/1596
*/
setScrambleKey(_key: string): void;
/**
* Close the connection with the device.
*
* Note: for certain transports (hw-transport-node-hid-singleton for ex), once the promise resolved,
* the transport instance is actually still cached, and the device is disconnected only after a defined timeout.
* But for the consumer of the Transport, this does not matter and it can consider the transport to be closed.
*
* @returns {Promise} A promise that resolves when the transport is closed.
*/
close(): Promise;
_events: EventEmitter;
/**
* Listen for an event on the transport instance.
* Transport implementations may have specific events. Common events include:
* "disconnect" : triggered when the transport is disconnected.
* @param {string} eventName - The name of the event to listen for.
* @param {(...args: Array) => any} cb - The callback function to be invoked when the event occurs.
*/
on(eventName: string, cb: (...args: Array) => any): void;
/**
* Stop listening to an event on an instance of transport.
*/
off(eventName: string, cb: (...args: Array) => any): void;
emit(event: string, ...args: any): void;
/**
* Enable or not logs of the binary exchange
*/
setDebugMode(): void;
/**
* Set a timeout (in milliseconds) for the exchange call. Only some transport might implement it. (e.g. U2F)
*/
setExchangeTimeout(exchangeTimeout: number): void;
/**
* Define the delay before emitting "unresponsive" on an exchange that does not respond
*/
setExchangeUnresponsiveTimeout(unresponsiveTimeout: number): void;
/**
* Send data to the device using the higher level API.
*
* @param {number} cla - The instruction class for the command.
* @param {number} ins - The instruction code for the command.
* @param {number} p1 - The first parameter for the instruction.
* @param {number} p2 - The second parameter for the instruction.
* @param {Buffer} data - The data to be sent. Defaults to an empty buffer.
* @param {Array} statusList - A list of acceptable status codes for the response. Defaults to [StatusCodes.OK].
* @param {Object} options - Contains optional options for the exchange function
* - abortTimeoutMs: stop the send after a given timeout. Another timeout exists
* to detect unresponsive device (see `unresponsiveTimeout`). This timeout aborts the exchange.
* @returns {Promise} A promise that resolves with the response data from the device.
*/
send: (cla: number, ins: number, p1: number, p2: number, data?: Buffer, statusList?: Array, { abortTimeoutMs }?: {
abortTimeoutMs?: number | undefined;
}) => Promise;
/**
* create() allows to open the first descriptor available or
* throw if there is none or if timeout is reached.
* This is a light helper, alternative to using listen() and open() (that you may need for any more advanced usecase)
* @example
TransportFoo.create().then(transport => ...)
*/
static create(openTimeout?: number, listenTimeout?: number): Promise;
exchangeBusyPromise: Promise | null | undefined;
/**
* Wrapper to make an exchange "atomic" (blocking any other exchange)
*
* It also handles "unresponsiveness" by emitting "unresponsive" and "responsive" events.
*
* @param f The exchange job, using the transport to run
* @returns a Promise resolving with the output of the given job
*/
exchangeAtomicImpl