1 | /// <reference types="node" />
|
2 | /// <reference types="node" />
|
3 | import EventEmitter from "events";
|
4 | import type { DeviceModel } from "@ledgerhq/devices";
|
5 | import { TransportError, StatusCodes, getAltStatusMessage, TransportStatusError } from "@ledgerhq/errors";
|
6 | import { LocalTracer, TraceContext, LogType } from "@ledgerhq/logs";
|
7 | export { TransportError, TransportStatusError, StatusCodes, getAltStatusMessage };
|
8 | /**
|
9 | */
|
10 | export type Subscription = {
|
11 | unsubscribe: () => void;
|
12 | };
|
13 | /**
|
14 | */
|
15 | export type Device = any;
|
16 | export type DescriptorEventType = "add" | "remove";
|
17 | /**
|
18 | * A "descriptor" is a parameter that is specific to the implementation, and can be an ID, file path, or URL.
|
19 | * type: add or remove event
|
20 | * descriptor: a parameter that can be passed to open(descriptor)
|
21 | * deviceModel: device info on the model (is it a nano s, nano x, ...)
|
22 | * device: transport specific device info
|
23 | */
|
24 | export interface DescriptorEvent<Descriptor> {
|
25 | type: DescriptorEventType;
|
26 | descriptor: Descriptor;
|
27 | deviceModel?: DeviceModel | null | undefined;
|
28 | device?: Device;
|
29 | }
|
30 | /**
|
31 | * Observer generic type, following the Observer pattern
|
32 | */
|
33 | export type Observer<EventType, EventError = unknown> = Readonly<{
|
34 | next: (event: EventType) => unknown;
|
35 | error: (e: EventError) => unknown;
|
36 | complete: () => unknown;
|
37 | }>;
|
38 | /**
|
39 | * The Transport class defines a generic interface for communicating with a Ledger hardware wallet.
|
40 | * There are different kind of transports based on the technology (channels like U2F, HID, Bluetooth, Webusb) and environment (Node, Web,...).
|
41 | * It is an abstract class that needs to be implemented.
|
42 | */
|
43 | export default class Transport {
|
44 | exchangeTimeout: number;
|
45 | unresponsiveTimeout: number;
|
46 | deviceModel: DeviceModel | null | undefined;
|
47 | tracer: LocalTracer;
|
48 | constructor({ context, logType }?: {
|
49 | context?: TraceContext;
|
50 | logType?: LogType;
|
51 | });
|
52 | /**
|
53 | * Check if the transport is supported on the current platform/browser.
|
54 | * @returns {Promise<boolean>} A promise that resolves with a boolean indicating support.
|
55 | */
|
56 | static readonly isSupported: () => Promise<boolean>;
|
57 | /**
|
58 | * List all available descriptors for the transport.
|
59 | * For a better granularity, checkout `listen()`.
|
60 | *
|
61 | * @returns {Promise<Array<any>>} A promise that resolves with an array of descriptors.
|
62 | * @example
|
63 | * TransportFoo.list().then(descriptors => ...)
|
64 | */
|
65 | static readonly list: () => Promise<Array<any>>;
|
66 | /**
|
67 | * Listen for device events for the transport. The method takes an observer of DescriptorEvent and returns a Subscription.
|
68 | * 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.
|
69 | * 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.
|
70 | * @param {Observer<DescriptorEvent<any>>} observer - An object with "next", "error", and "complete" functions, following the observer pattern.
|
71 | * @returns {Subscription} A Subscription object on which you can call ".unsubscribe()" to stop listening to descriptors.
|
72 | * @example
|
73 | const sub = TransportFoo.listen({
|
74 | next: e => {
|
75 | if (e.type==="add") {
|
76 | sub.unsubscribe();
|
77 | const transport = await TransportFoo.open(e.descriptor);
|
78 | ...
|
79 | }
|
80 | },
|
81 | error: error => {},
|
82 | complete: () => {}
|
83 | })
|
84 | */
|
85 | static readonly listen: (observer: Observer<DescriptorEvent<any>>) => Subscription;
|
86 | /**
|
87 | * Attempt to create a Transport instance with a specific descriptor.
|
88 | * @param {any} descriptor - The descriptor to open the transport with.
|
89 | * @param {number} timeout - An optional timeout for the transport connection.
|
90 | * @param {TraceContext} context Optional tracing/log context
|
91 | * @returns {Promise<Transport>} A promise that resolves with a Transport instance.
|
92 | * @example
|
93 | TransportFoo.open(descriptor).then(transport => ...)
|
94 | */
|
95 | static readonly open: (descriptor?: any, timeoutMs?: number, context?: TraceContext) => Promise<Transport>;
|
96 | /**
|
97 | * Send data to the device using a low level API.
|
98 | * It's recommended to use the "send" method for a higher level API.
|
99 | * @param {Buffer} apdu - The data to send.
|
100 | * @param {Object} options - Contains optional options for the exchange function
|
101 | * - abortTimeoutMs: stop the exchange after a given timeout. Another timeout exists
|
102 | * to detect unresponsive device (see `unresponsiveTimeout`). This timeout aborts the exchange.
|
103 | * @returns {Promise<Buffer>} A promise that resolves with the response data from the device.
|
104 | */
|
105 | exchange(_apdu: Buffer, { abortTimeoutMs }?: {
|
106 | abortTimeoutMs?: number;
|
107 | }): Promise<Buffer>;
|
108 | /**
|
109 | * Send apdus in batch to the device using a low level API.
|
110 | * The default implementation is to call exchange for each apdu.
|
111 | * @param {Array<Buffer>} apdus - array of apdus to send.
|
112 | * @param {Observer<Buffer>} observer - an observer that will receive the response of each apdu.
|
113 | * @returns {Subscription} A Subscription object on which you can call ".unsubscribe()" to stop sending apdus.
|
114 | */
|
115 | exchangeBulk(apdus: Buffer[], observer: Observer<Buffer>): Subscription;
|
116 | /**
|
117 | * Set the "scramble key" for the next data exchanges with the device.
|
118 | * Each app can have a different scramble key and it is set internally during instantiation.
|
119 | * @param {string} key - The scramble key to set.
|
120 | * deprecated This method is no longer needed for modern transports and should be migrated away from.
|
121 | * no @ before deprecated as it breaks documentationjs on version 14.0.2
|
122 | * https://github.com/documentationjs/documentation/issues/1596
|
123 | */
|
124 | setScrambleKey(_key: string): void;
|
125 | /**
|
126 | * Close the connection with the device.
|
127 | *
|
128 | * Note: for certain transports (hw-transport-node-hid-singleton for ex), once the promise resolved,
|
129 | * the transport instance is actually still cached, and the device is disconnected only after a defined timeout.
|
130 | * But for the consumer of the Transport, this does not matter and it can consider the transport to be closed.
|
131 | *
|
132 | * @returns {Promise<void>} A promise that resolves when the transport is closed.
|
133 | */
|
134 | close(): Promise<void>;
|
135 | _events: EventEmitter;
|
136 | /**
|
137 | * Listen for an event on the transport instance.
|
138 | * Transport implementations may have specific events. Common events include:
|
139 | * "disconnect" : triggered when the transport is disconnected.
|
140 | * @param {string} eventName - The name of the event to listen for.
|
141 | * @param {(...args: Array<any>) => any} cb - The callback function to be invoked when the event occurs.
|
142 | */
|
143 | on(eventName: string, cb: (...args: Array<any>) => any): void;
|
144 | /**
|
145 | * Stop listening to an event on an instance of transport.
|
146 | */
|
147 | off(eventName: string, cb: (...args: Array<any>) => any): void;
|
148 | emit(event: string, ...args: any): void;
|
149 | /**
|
150 | * Enable or not logs of the binary exchange
|
151 | */
|
152 | setDebugMode(): void;
|
153 | /**
|
154 | * Set a timeout (in milliseconds) for the exchange call. Only some transport might implement it. (e.g. U2F)
|
155 | */
|
156 | setExchangeTimeout(exchangeTimeout: number): void;
|
157 | /**
|
158 | * Define the delay before emitting "unresponsive" on an exchange that does not respond
|
159 | */
|
160 | setExchangeUnresponsiveTimeout(unresponsiveTimeout: number): void;
|
161 | /**
|
162 | * Send data to the device using the higher level API.
|
163 | *
|
164 | * @param {number} cla - The instruction class for the command.
|
165 | * @param {number} ins - The instruction code for the command.
|
166 | * @param {number} p1 - The first parameter for the instruction.
|
167 | * @param {number} p2 - The second parameter for the instruction.
|
168 | * @param {Buffer} data - The data to be sent. Defaults to an empty buffer.
|
169 | * @param {Array<number>} statusList - A list of acceptable status codes for the response. Defaults to [StatusCodes.OK].
|
170 | * @param {Object} options - Contains optional options for the exchange function
|
171 | * - abortTimeoutMs: stop the send after a given timeout. Another timeout exists
|
172 | * to detect unresponsive device (see `unresponsiveTimeout`). This timeout aborts the exchange.
|
173 | * @returns {Promise<Buffer>} A promise that resolves with the response data from the device.
|
174 | */
|
175 | send: (cla: number, ins: number, p1: number, p2: number, data?: Buffer, statusList?: Array<number>, { abortTimeoutMs }?: {
|
176 | abortTimeoutMs?: number | undefined;
|
177 | }) => Promise<Buffer>;
|
178 | /**
|
179 | * create() allows to open the first descriptor available or
|
180 | * throw if there is none or if timeout is reached.
|
181 | * This is a light helper, alternative to using listen() and open() (that you may need for any more advanced usecase)
|
182 | * @example
|
183 | TransportFoo.create().then(transport => ...)
|
184 | */
|
185 | static create(openTimeout?: number, listenTimeout?: number): Promise<Transport>;
|
186 | exchangeBusyPromise: Promise<void> | null | undefined;
|
187 | /**
|
188 | * Wrapper to make an exchange "atomic" (blocking any other exchange)
|
189 | *
|
190 | * It also handles "unresponsiveness" by emitting "unresponsive" and "responsive" events.
|
191 | *
|
192 | * @param f The exchange job, using the transport to run
|
193 | * @returns a Promise resolving with the output of the given job
|
194 | */
|
195 | exchangeAtomicImpl<Output>(f: () => Promise<Output>): Promise<Output>;
|
196 | decorateAppAPIMethods(self: Record<string, any>, methods: Array<string>, scrambleKey: string): void;
|
197 | _appAPIlock: string | null;
|
198 | decorateAppAPIMethod<R, A extends any[]>(methodName: string, f: (...args: A) => Promise<R>, ctx: any, scrambleKey: string): (...args: A) => Promise<R>;
|
199 | /**
|
200 | * Sets the context used by the logging/tracing mechanism
|
201 | *
|
202 | * Useful when re-using (cached) the same Transport instance,
|
203 | * but with a new tracing context.
|
204 | *
|
205 | * @param context A TraceContext, that can undefined to reset the context
|
206 | */
|
207 | setTraceContext(context?: TraceContext): void;
|
208 | /**
|
209 | * Updates the context used by the logging/tracing mechanism
|
210 | *
|
211 | * The update only overrides the key-value that are already defined in the current context.
|
212 | *
|
213 | * @param contextToAdd A TraceContext that will be added to the current context
|
214 | */
|
215 | updateTraceContext(contextToAdd: TraceContext): void;
|
216 | /**
|
217 | * Gets the tracing context of the transport instance
|
218 | */
|
219 | getTraceContext(): TraceContext | undefined;
|
220 | static ErrorMessage_ListenTimeout: string;
|
221 | static ErrorMessage_NoDeviceFound: string;
|
222 | }
|
223 | //# sourceMappingURL=Transport.d.ts.map |
\ | No newline at end of file |