@ledgerhq/device-transport-kit-node-hid 1.0.0 → 1.0.1

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.
@@ -1,2 +1,2 @@
1
- "use strict";var u=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var N=(v,e)=>{for(var i in e)u(v,i,{get:e[i],enumerable:!0})},S=(v,e,i,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of C(e))!A.call(v,o)&&o!==i&&u(v,o,{get:()=>e[o],enumerable:!(t=H(e,o))||t.enumerable});return v};var T=v=>S(u({},"__esModule",{value:!0}),v);var w={};N(w,{NodeHidTransport:()=>_,nodeHidIdentifier:()=>m,nodeHidTransportFactory:()=>b});module.exports=T(w);var c=require("@ledgerhq/device-management-kit"),h=require("node-hid"),d=require("purify-ts"),p=require("rxjs"),D=require("usb"),l=require("uuid"),y=require("../data/NodeHidConfig"),f=require("../model/Errors"),I=require("../transport/NodeHidApduSender");const E={devicesAsync:h.devicesAsync,HIDAsync:h.HIDAsync},m="NODE-HID";class _{constructor(e,i,t,o,r=a=>new c.DeviceConnectionStateMachine(a),s=a=>new I.NodeHidApduSender(a)){this._deviceModelDataSource=e;this._loggerServiceFactory=i;this._apduSenderFactory=t;this._apduReceiverFactory=o;this._deviceConnectionStateMachineFactory=r;this._deviceApduSenderFactory=s;this._logger=i("NodeHidTransport"),this.startListeningToConnectionEvents()}_transportDiscoveredDevices=new p.BehaviorSubject([]);_deviceConnectionsByHidDevice=new Map;_deviceConnectionsPendingReconnection=new Set;_connectionListenersAbortController=new AbortController;_logger;connectionType="USB";identifier=m;get hidApi(){return this.isSupported()?(0,d.Right)(E):(0,d.Left)(new f.NodeHidTransportNotSupportedError("NodeHID not supported"))}isSupported(){try{return this._logger.debug("isSupported: true"),!0}catch(e){return this._logger.error("isSupported: error",{data:{error:e}}),!1}}getIdentifier(){return this.identifier}async getDevices(){return d.EitherAsync.liftEither(this.hidApi).map(async e=>{try{const t=(await e.devicesAsync()).filter(r=>r.vendorId===c.LEDGER_VENDOR_ID);return Array.from(new Map(t.map(r=>[`${r.vendorId}:${r.productId}`,r])).values())}catch(i){const t=new c.NoAccessibleDeviceError(i);throw this._logger.error("getDevices: error getting devices",{data:{error:i}}),t}})}mapHIDDeviceToTransportDiscoveredDevice(e){const i=this._transportDiscoveredDevices.getValue().find(r=>r.hidDevice.vendorId===e.vendorId&&r.hidDevice.productId===e.productId);if(i)return i;const t=this._deviceConnectionsByHidDevice.get(e);return this.getDeviceModel(e).caseOf({Just:r=>{const s=t?.getDeviceId()??(0,l.v4)(),a={id:s,deviceModel:r,hidDevice:e,transport:this.identifier};return this._logger.debug(`Discovered device ${s} ${a.deviceModel.productName}`),a},Nothing:()=>{throw this._logger.warn(`Device not recognized: hidDevice.productId: 0x${e.productId.toString(16)}`),new c.DeviceNotRecognizedError(`Device not recognized: hidDevice.productId: 0x${e.productId.toString(16)}`)}})}listenToAvailableDevices(){return this.updateTransportDiscoveredDevices(),this._transportDiscoveredDevices.pipe((0,p.map)(e=>e.map(({hidDevice:i,...t})=>t)))}async updateTransportDiscoveredDevices(){(await this.getDevices()).caseOf({Left:i=>{this._logger.error("Error while getting accessible device",{data:{error:i}})},Right:i=>{this._transportDiscoveredDevices.next(i.map(t=>this.mapHIDDeviceToTransportDiscoveredDevice(t)))}})}async promptDeviceAccess(){return d.EitherAsync.liftEither(this.hidApi).map(async e=>{let i=[];try{i=(await e.devicesAsync()).filter(r=>r.vendorId===c.LEDGER_VENDOR_ID),await this.updateTransportDiscoveredDevices()}catch(o){const r=new c.NoAccessibleDeviceError(o);throw this._logger.error("promptDeviceAccess: error requesting device",{data:{error:o}}),r}if(this._logger.debug(`promptDeviceAccess: hidDevices len ${i.length}`),i.length===0)throw this._logger.warn("No device was selected"),new c.NoAccessibleDeviceError("No selected device");const t=[];for(const o of i)t.push(o),this._logger.debug("promptDeviceAccess: selected device",{data:{hidDevice:o}});return t}).run()}startDiscovering(){return this._logger.debug("startDiscovering"),(0,p.from)(this.promptDeviceAccess()).pipe((0,p.switchMap)(e=>e.caseOf({Left:i=>{throw this._logger.error("Error while getting accessible device",{data:{error:i}}),i},Right:i=>{this._logger.info(`Got access to ${i.length} HID devices`);const t=i.map(o=>this.mapHIDDeviceToTransportDiscoveredDevice(o));return(0,p.from)(t)}})))}stopDiscovering(){}startListeningToConnectionEvents(){this._logger.debug("startListeningToConnectionEvents"),D.usb.on("attach",e=>{this.handleDeviceConnection(e)}),D.usb.on("detach",e=>{this.handleDeviceDisconnection(e)}),process.on("exit",()=>{D.usb.unrefHotplugEvents(),D.usb.removeAllListeners()})}stopListeningToConnectionEvents(){this._logger.debug("stopListeningToConnectionEvents"),this._connectionListenersAbortController.abort(),D.usb.removeAllListeners()}async connect({deviceId:e,onDisconnect:i}){this._logger.debug("connect",{data:{deviceId:e}});const t=this._transportDiscoveredDevices.getValue().find(n=>n.id===e);if(!t)return this._logger.error(`Unknown device ${e}`),(0,d.Left)(new c.UnknownDeviceError(`Unknown device ${e}`));const o=this._deviceConnectionsByHidDevice.get(t.hidDevice);if(o)return(0,d.Right)(new c.TransportConnectedDevice({id:e,deviceModel:t.deviceModel,type:this.connectionType,sendApdu:(...n)=>o.sendApdu(...n),transport:this.identifier}));const r=this._deviceApduSenderFactory({dependencies:{device:t.hidDevice},apduSenderFactory:this._apduSenderFactory,apduReceiverFactory:this._apduReceiverFactory,loggerFactory:this._loggerServiceFactory}),s=this._deviceConnectionStateMachineFactory({deviceId:e,deviceApduSender:r,timeoutDuration:y.RECONNECT_DEVICE_TIMEOUT,tryToReconnect:()=>{this._deviceConnectionsByHidDevice.forEach((n,g)=>{n.getDeviceId()===e&&(this._deviceConnectionsPendingReconnection.add(n),this._deviceConnectionsByHidDevice.delete(g))})},onTerminated:()=>{this._deviceConnectionsPendingReconnection.forEach(n=>{n.getDeviceId()===e&&(this._deviceConnectionsPendingReconnection.delete(n),i(n.getDeviceId()))}),this._deviceConnectionsByHidDevice.forEach((n,g)=>{n.getDeviceId()===e&&(this._deviceConnectionsByHidDevice.delete(g),i(n.getDeviceId()))})}});try{await s.setupConnection()}catch(n){return this._logger.error("Error while setting up device connection",{data:{error:n}}),(0,d.Left)(new c.OpeningConnectionError(n))}this._deviceConnectionsByHidDevice.set(t.hidDevice,s);const a=new c.TransportConnectedDevice({sendApdu:(...n)=>s.sendApdu(...n),deviceModel:t.deviceModel,id:e,type:this.connectionType,transport:this.identifier});return(0,d.Right)(a)}getDeviceModel(e){const{productId:i}=e,t=this._deviceModelDataSource.getAllDeviceModels().find(o=>o.usbProductId===i>>8||o.bootloaderUsbProductId===i);return t?d.Maybe.of(t):d.Maybe.zero()}getHidUsbProductId(e){return this.getDeviceModel(e).caseOf({Just:i=>i.usbProductId,Nothing:()=>e.productId>>8})}async disconnect(e){this._logger.debug("disconnect",{data:{connectedDevice:e}});const i=Array.from(this._deviceConnectionsByHidDevice.values()).find(t=>t.getDeviceId()===e.connectedDevice.id);return i?(i.closeConnection(),Promise.resolve((0,d.Right)(void 0))):(this._logger.error("No matching device connection found",{data:{connectedDevice:e}}),Promise.resolve((0,d.Left)(new c.UnknownDeviceError(`Unknown device ${e.connectedDevice.id}`))))}async handleDeviceDisconnection(e){const{idVendor:i,idProduct:t}=e.deviceDescriptor;if(i!==c.LEDGER_VENDOR_ID)return;this._logger.info("[handleDeviceDisconnectionEvent] Device disconnected",{data:{vendorId:i,productId:t}}),this.updateTransportDiscoveredDevices(),(()=>{for(const[r,s]of this._deviceConnectionsByHidDevice.entries())if(r.vendorId===i&&(r.productId>>8===t||r.productId===t))return(0,d.Just)(s);return d.Nothing})().caseOf({Just:r=>{try{r.eventDeviceDisconnected()}catch(s){this._logger.error("Error while handling device disconnection",{data:{error:s}})}},Nothing:()=>{this._logger.error("No matching device connection found",{data:{vendorId:i,productId:t}})}})}async handleDeviceReconnection(e,i){try{this._deviceConnectionsPendingReconnection.delete(e),e.setDependencies({device:i}),await e.setupConnection(),this._deviceConnectionsByHidDevice.set(i,e),e.eventDeviceConnected()}catch(t){this._logger.error("Error while reconnecting to device",{data:{error:t}}),e.closeConnection()}}async handleDeviceConnection(e){const{idVendor:i,idProduct:t}=e.deviceDescriptor;if(i!==c.LEDGER_VENDOR_ID)return;this._logger.info("[handleDeviceConnection] New device connected",{data:{vendorId:i,productId:t}}),(await this.getDevices()).caseOf({Left:r=>{this._logger.error("Error while getting HID devices for reconnection",{data:{error:r}})},Right:async r=>{const s=r.find(n=>n.vendorId===i&&(n.productId>>8===t||n.productId===t));if(!s){this._logger.debug("[handleDeviceConnection] No matching HID device found",{data:{vendorId:i,productId:t}});return}const a=Array.from(this._deviceConnectionsPendingReconnection).find(n=>this.getHidUsbProductId(n.getDependencies().device)===this.getHidUsbProductId(s));a&&await this.handleDeviceReconnection(a,s),await this.updateTransportDiscoveredDevices()}})}destroy(){this.stopListeningToConnectionEvents(),this._deviceConnectionsByHidDevice.forEach(e=>{e.closeConnection()}),this._deviceConnectionsPendingReconnection.clear()}}const b=({deviceModelDataSource:v,loggerServiceFactory:e,apduSenderServiceFactory:i,apduReceiverServiceFactory:t})=>new _(v,e,i,t);0&&(module.exports={NodeHidTransport,nodeHidIdentifier,nodeHidTransportFactory});
1
+ "use strict";var u=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var N=(a,e)=>{for(var i in e)u(a,i,{get:e[i],enumerable:!0})},S=(a,e,i,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of C(e))!E.call(a,r)&&r!==i&&u(a,r,{get:()=>e[r],enumerable:!(t=A(e,r))||t.enumerable});return a};var T=a=>S(u({},"__esModule",{value:!0}),a);var M={};N(M,{NodeHidTransport:()=>H,nodeHidIdentifier:()=>m,nodeHidTransportFactory:()=>P});module.exports=T(M);var d=require("@ledgerhq/device-management-kit"),g=require("node-hid"),c=require("purify-ts"),p=require("rxjs"),D=require("usb"),y=require("uuid"),f=require("../data/NodeHidConfig"),I=require("../model/Errors"),_=require("../transport/NodeHidApduSender");const w={devicesAsync:g.devicesAsync,HIDAsync:g.HIDAsync},b=65440,l=a=>a.vendorId!==d.LEDGER_VENDOR_ID?!1:process.platform==="darwin"||process.platform==="win32"?a.usagePage===b:!0,m="NODE-HID";class H{constructor(e,i,t,r,n=v=>new d.DeviceConnectionStateMachine(v),s=v=>new _.NodeHidApduSender(v)){this._deviceModelDataSource=e;this._loggerServiceFactory=i;this._apduSenderFactory=t;this._apduReceiverFactory=r;this._deviceConnectionStateMachineFactory=n;this._deviceApduSenderFactory=s;this._logger=i("NodeHidTransport"),this.startListeningToConnectionEvents()}_transportDiscoveredDevices=new p.BehaviorSubject([]);_deviceConnectionsByHidDevice=new Map;_deviceConnectionsPendingReconnection=new Set;_connectionListenersAbortController=new AbortController;_logger;connectionType="USB";identifier=m;get hidApi(){return this.isSupported()?(0,c.Right)(w):(0,c.Left)(new I.NodeHidTransportNotSupportedError("NodeHID not supported"))}isSupported(){try{return this._logger.debug("isSupported: true"),!0}catch(e){return this._logger.error("isSupported: error",{data:{error:e}}),!1}}getIdentifier(){return this.identifier}async getDevices(){return c.EitherAsync.liftEither(this.hidApi).map(async e=>{try{const t=(await e.devicesAsync()).filter(l);return Array.from(new Map(t.map(n=>[`${n.vendorId}:${n.productId}`,n])).values())}catch(i){const t=new d.NoAccessibleDeviceError(i);throw this._logger.error("getDevices: error getting devices",{data:{error:i}}),t}})}mapHIDDeviceToTransportDiscoveredDevice(e){const i=this._transportDiscoveredDevices.getValue().find(n=>n.hidDevice.vendorId===e.vendorId&&n.hidDevice.productId===e.productId);if(i)return i;const t=this._deviceConnectionsByHidDevice.get(e);return this.getDeviceModel(e).caseOf({Just:n=>{const s=t?.getDeviceId()??(0,y.v4)(),v={id:s,deviceModel:n,hidDevice:e,transport:this.identifier};return this._logger.debug(`Discovered device ${s} ${v.deviceModel.productName}`),v},Nothing:()=>{throw this._logger.warn(`Device not recognized: hidDevice.productId: 0x${e.productId.toString(16)}`),new d.DeviceNotRecognizedError(`Device not recognized: hidDevice.productId: 0x${e.productId.toString(16)}`)}})}listenToAvailableDevices(){return this.updateTransportDiscoveredDevices(),this._transportDiscoveredDevices.pipe((0,p.map)(e=>e.map(({hidDevice:i,...t})=>t)))}async updateTransportDiscoveredDevices(){(await this.getDevices()).caseOf({Left:i=>{this._logger.error("Error while getting accessible device",{data:{error:i}})},Right:i=>{this._transportDiscoveredDevices.next(i.map(t=>this.mapHIDDeviceToTransportDiscoveredDevice(t)))}})}async promptDeviceAccess(){return c.EitherAsync.liftEither(this.hidApi).map(async e=>{let i=[];try{i=(await e.devicesAsync()).filter(l),await this.updateTransportDiscoveredDevices()}catch(r){const n=new d.NoAccessibleDeviceError(r);throw this._logger.error("promptDeviceAccess: error requesting device",{data:{error:r}}),n}if(this._logger.debug(`promptDeviceAccess: hidDevices len ${i.length}`),i.length===0)throw this._logger.warn("No device was selected"),new d.NoAccessibleDeviceError("No selected device");const t=[];for(const r of i)t.push(r),this._logger.debug("promptDeviceAccess: selected device",{data:{hidDevice:r}});return t}).run()}startDiscovering(){return this._logger.debug("startDiscovering"),(0,p.from)(this.promptDeviceAccess()).pipe((0,p.switchMap)(e=>e.caseOf({Left:i=>{throw this._logger.error("Error while getting accessible device",{data:{error:i}}),i},Right:i=>{this._logger.info(`Got access to ${i.length} HID devices`);const t=i.map(r=>this.mapHIDDeviceToTransportDiscoveredDevice(r));return(0,p.from)(t)}})))}stopDiscovering(){}startListeningToConnectionEvents(){this._logger.debug("startListeningToConnectionEvents"),D.usb.on("attach",e=>{this.handleDeviceConnection(e)}),D.usb.on("detach",e=>{this.handleDeviceDisconnection(e)}),process.on("exit",()=>{D.usb.unrefHotplugEvents(),D.usb.removeAllListeners()})}stopListeningToConnectionEvents(){this._logger.debug("stopListeningToConnectionEvents"),this._connectionListenersAbortController.abort(),D.usb.removeAllListeners()}async connect({deviceId:e,onDisconnect:i}){this._logger.debug("connect",{data:{deviceId:e}});const t=this._transportDiscoveredDevices.getValue().find(o=>o.id===e);if(!t)return this._logger.error(`Unknown device ${e}`),(0,c.Left)(new d.UnknownDeviceError(`Unknown device ${e}`));const r=this._deviceConnectionsByHidDevice.get(t.hidDevice);if(r)return(0,c.Right)(new d.TransportConnectedDevice({id:e,deviceModel:t.deviceModel,type:this.connectionType,sendApdu:(...o)=>r.sendApdu(...o),transport:this.identifier}));const n=this._deviceApduSenderFactory({dependencies:{device:t.hidDevice},apduSenderFactory:this._apduSenderFactory,apduReceiverFactory:this._apduReceiverFactory,loggerFactory:this._loggerServiceFactory}),s=this._deviceConnectionStateMachineFactory({deviceId:e,deviceApduSender:n,timeoutDuration:f.RECONNECT_DEVICE_TIMEOUT,tryToReconnect:()=>{this._deviceConnectionsByHidDevice.forEach((o,h)=>{o.getDeviceId()===e&&(this._deviceConnectionsPendingReconnection.add(o),this._deviceConnectionsByHidDevice.delete(h))})},onTerminated:()=>{this._deviceConnectionsPendingReconnection.forEach(o=>{o.getDeviceId()===e&&(this._deviceConnectionsPendingReconnection.delete(o),i(o.getDeviceId()))}),this._deviceConnectionsByHidDevice.forEach((o,h)=>{o.getDeviceId()===e&&(this._deviceConnectionsByHidDevice.delete(h),i(o.getDeviceId()))})}});try{await s.setupConnection()}catch(o){return this._logger.error("Error while setting up device connection",{data:{error:o}}),(0,c.Left)(new d.OpeningConnectionError(o))}this._deviceConnectionsByHidDevice.set(t.hidDevice,s);const v=new d.TransportConnectedDevice({sendApdu:(...o)=>s.sendApdu(...o),deviceModel:t.deviceModel,id:e,type:this.connectionType,transport:this.identifier});return(0,c.Right)(v)}getDeviceModel(e){const{productId:i}=e,t=this._deviceModelDataSource.getAllDeviceModels().find(r=>r.usbProductId===i>>8||r.bootloaderUsbProductId===i);return t?c.Maybe.of(t):c.Maybe.zero()}getHidUsbProductId(e){return this.getDeviceModel(e).caseOf({Just:i=>i.usbProductId,Nothing:()=>e.productId>>8})}async disconnect(e){this._logger.debug("disconnect",{data:{connectedDevice:e}});const i=Array.from(this._deviceConnectionsByHidDevice.values()).find(t=>t.getDeviceId()===e.connectedDevice.id);return i?(i.closeConnection(),Promise.resolve((0,c.Right)(void 0))):(this._logger.error("No matching device connection found",{data:{connectedDevice:e}}),Promise.resolve((0,c.Left)(new d.UnknownDeviceError(`Unknown device ${e.connectedDevice.id}`))))}async handleDeviceDisconnection(e){const{idVendor:i,idProduct:t}=e.deviceDescriptor;if(i!==d.LEDGER_VENDOR_ID)return;this._logger.info("[handleDeviceDisconnectionEvent] Device disconnected",{data:{vendorId:i,productId:t}}),this.updateTransportDiscoveredDevices(),(()=>{for(const[n,s]of this._deviceConnectionsByHidDevice.entries())if(n.vendorId===i&&(n.productId>>8===t||n.productId===t))return(0,c.Just)(s);return c.Nothing})().caseOf({Just:n=>{try{n.eventDeviceDisconnected()}catch(s){this._logger.error("Error while handling device disconnection",{data:{error:s}})}},Nothing:()=>{this._logger.error("No matching device connection found",{data:{vendorId:i,productId:t}})}})}async handleDeviceReconnection(e,i){try{this._deviceConnectionsPendingReconnection.delete(e),e.setDependencies({device:i}),await e.setupConnection(),this._deviceConnectionsByHidDevice.set(i,e),e.eventDeviceConnected()}catch(t){this._logger.error("Error while reconnecting to device",{data:{error:t}}),e.closeConnection()}}async handleDeviceConnection(e){const{idVendor:i,idProduct:t}=e.deviceDescriptor;if(i!==d.LEDGER_VENDOR_ID)return;this._logger.info("[handleDeviceConnection] New device connected",{data:{vendorId:i,productId:t}}),(await this.getDevices()).caseOf({Left:n=>{this._logger.error("Error while getting HID devices for reconnection",{data:{error:n}})},Right:async n=>{const s=n.find(o=>o.vendorId===i&&(o.productId>>8===t||o.productId===t));if(!s){this._logger.debug("[handleDeviceConnection] No matching HID device found",{data:{vendorId:i,productId:t}});return}const v=Array.from(this._deviceConnectionsPendingReconnection).find(o=>this.getHidUsbProductId(o.getDependencies().device)===this.getHidUsbProductId(s));v&&await this.handleDeviceReconnection(v,s),await this.updateTransportDiscoveredDevices()}})}destroy(){this.stopListeningToConnectionEvents(),this._deviceConnectionsByHidDevice.forEach(e=>{e.closeConnection()}),this._deviceConnectionsPendingReconnection.clear()}}const P=({deviceModelDataSource:a,loggerServiceFactory:e,apduSenderServiceFactory:i,apduReceiverServiceFactory:t})=>new H(a,e,i,t);0&&(module.exports={NodeHidTransport,nodeHidIdentifier,nodeHidTransportFactory});
2
2
  //# sourceMappingURL=NodeHidTransport.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/api/transport/NodeHidTransport.ts"],
4
- "sourcesContent": ["import {\n type ApduReceiverServiceFactory,\n type ApduSenderServiceFactory,\n type ConnectError,\n type ConnectionType,\n DeviceConnectionStateMachine,\n type DeviceConnectionStateMachineParams,\n type DeviceId,\n type DeviceModelDataSource,\n DeviceNotRecognizedError,\n type DisconnectHandler,\n type DmkError,\n LEDGER_VENDOR_ID,\n type LoggerPublisherService,\n NoAccessibleDeviceError,\n OpeningConnectionError,\n type Transport,\n TransportConnectedDevice,\n type TransportDeviceModel,\n type TransportDiscoveredDevice,\n type TransportFactory,\n type TransportIdentifier,\n UnknownDeviceError,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Device as NodeHIDDevice, devicesAsync, HIDAsync } from \"node-hid\";\nimport {\n type Either,\n EitherAsync,\n Just,\n Left,\n Maybe,\n Nothing,\n Right,\n} from \"purify-ts\";\nimport { BehaviorSubject, from, map, type Observable, switchMap } from \"rxjs\";\nimport { type Device, usb } from \"usb\";\nimport { v4 as uuid } from \"uuid\";\n\nimport { RECONNECT_DEVICE_TIMEOUT } from \"@api/data/NodeHidConfig\";\nimport { NodeHidTransportNotSupportedError } from \"@api/model/Errors\";\nimport {\n NodeHidApduSender,\n type NodeHidApduSenderConstructorArgs,\n type NodeHidApduSenderDependencies,\n} from \"@api/transport/NodeHidApduSender\";\n\ntype NodeHIDAPI = typeof NodeHIDAPI;\nconst NodeHIDAPI = { devicesAsync, HIDAsync } as const;\n\ntype PromptDeviceAccessError =\n | NoAccessibleDeviceError\n | NodeHidTransportNotSupportedError;\n\ntype NodeHidTransportDiscoveredDevice = TransportDiscoveredDevice & {\n hidDevice: NodeHIDDevice;\n};\n\nexport const nodeHidIdentifier: TransportIdentifier = \"NODE-HID\";\n\nexport class NodeHidTransport implements Transport {\n /** List of HID devices that have been discovered */\n private _transportDiscoveredDevices: BehaviorSubject<\n Array<NodeHidTransportDiscoveredDevice>\n > = new BehaviorSubject<Array<NodeHidTransportDiscoveredDevice>>([]);\n\n /** Map of *connected* HIDDevice to their NodeHidDeviceConnection */\n private _deviceConnectionsByHidDevice: Map<\n NodeHIDDevice,\n DeviceConnectionStateMachine<NodeHidApduSenderDependencies>\n > = new Map();\n\n /**\n * Set of NodeHidDeviceConnection for which the HIDDevice has been\n * disconnected, so they are waiting for a reconnection\n */\n private _deviceConnectionsPendingReconnection: Set<\n DeviceConnectionStateMachine<NodeHidApduSenderDependencies>\n > = new Set();\n\n /** AbortController to stop listening to HID connection events */\n private _connectionListenersAbortController: AbortController =\n new AbortController();\n private _logger: LoggerPublisherService;\n private readonly connectionType: ConnectionType = \"USB\";\n private readonly identifier: TransportIdentifier = nodeHidIdentifier;\n\n constructor(\n private readonly _deviceModelDataSource: DeviceModelDataSource,\n private readonly _loggerServiceFactory: (\n tag: string,\n ) => LoggerPublisherService,\n private readonly _apduSenderFactory: ApduSenderServiceFactory,\n private readonly _apduReceiverFactory: ApduReceiverServiceFactory,\n private readonly _deviceConnectionStateMachineFactory: (\n args: DeviceConnectionStateMachineParams<NodeHidApduSenderDependencies>,\n ) => DeviceConnectionStateMachine<NodeHidApduSenderDependencies> = (args) =>\n new DeviceConnectionStateMachine(args),\n private readonly _deviceApduSenderFactory: (\n args: NodeHidApduSenderConstructorArgs,\n ) => NodeHidApduSender = (args) => new NodeHidApduSender(args),\n ) {\n this._logger = _loggerServiceFactory(\"NodeHidTransport\");\n\n this.startListeningToConnectionEvents();\n }\n\n /**\n * Get the NodeHID API if supported or error\n * @returns `Either<NodeHidTransportNotSupportedError, HID>`\n */\n private get hidApi(): Either<NodeHidTransportNotSupportedError, NodeHIDAPI> {\n if (this.isSupported()) {\n return Right(NodeHIDAPI);\n }\n\n return Left(new NodeHidTransportNotSupportedError(\"NodeHID not supported\"));\n }\n\n isSupported() {\n try {\n const result = true; // Node hid should be supported !\n this._logger.debug(`isSupported: ${result}`);\n return result;\n } catch (error) {\n this._logger.error(`isSupported: error`, { data: { error } });\n return false;\n }\n }\n\n getIdentifier(): TransportIdentifier {\n return this.identifier;\n }\n\n private async getDevices(): Promise<Either<DmkError, NodeHIDDevice[]>> {\n return EitherAsync.liftEither(this.hidApi).map(async (hidApi) => {\n try {\n const allDevices = await hidApi.devicesAsync();\n\n const ledgerDevices = allDevices.filter(\n (hidDevice) => hidDevice.vendorId === LEDGER_VENDOR_ID,\n );\n\n // Remove duplicates from same device with different interfaces by keeping only one device per vendorId:productId combination\n const uniqueDevices = Array.from(\n new Map(\n ledgerDevices.map((device) => [\n `${device.vendorId}:${device.productId}`,\n device,\n ]),\n ).values(),\n );\n\n return uniqueDevices;\n } catch (error) {\n const deviceError = new NoAccessibleDeviceError(error);\n this._logger.error(`getDevices: error getting devices`, {\n data: { error },\n });\n throw deviceError;\n }\n });\n }\n\n /**\n * Map a HIDDevice to an TransportDiscoveredDevice, either by creating a new one or returning an existing one\n */\n private mapHIDDeviceToTransportDiscoveredDevice(\n hidDevice: NodeHIDDevice,\n ): NodeHidTransportDiscoveredDevice {\n const existingDiscoveredDevice = this._transportDiscoveredDevices\n .getValue()\n .find(\n (internalDevice) =>\n internalDevice.hidDevice.vendorId === hidDevice.vendorId &&\n internalDevice.hidDevice.productId === hidDevice.productId,\n );\n\n if (existingDiscoveredDevice) {\n return existingDiscoveredDevice;\n }\n\n const existingDeviceConnection =\n this._deviceConnectionsByHidDevice.get(hidDevice);\n\n const maybeDeviceModel = this.getDeviceModel(hidDevice);\n return maybeDeviceModel.caseOf({\n Just: (deviceModel) => {\n const id = existingDeviceConnection?.getDeviceId() ?? uuid();\n\n const discoveredDevice = {\n id,\n deviceModel,\n hidDevice,\n transport: this.identifier,\n };\n\n this._logger.debug(\n `Discovered device ${id} ${discoveredDevice.deviceModel.productName}`,\n );\n\n return discoveredDevice;\n },\n Nothing: () => {\n // [ASK] Or we just ignore the not recognized device ? And log them\n this._logger.warn(\n `Device not recognized: hidDevice.productId: 0x${hidDevice.productId.toString(16)}`,\n );\n throw new DeviceNotRecognizedError(\n `Device not recognized: hidDevice.productId: 0x${hidDevice.productId.toString(16)}`,\n );\n },\n });\n }\n\n /**\n * Listen to known devices (devices to which the user has granted access)\n */\n public listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]> {\n this.updateTransportDiscoveredDevices();\n return this._transportDiscoveredDevices.pipe(\n map((devices) => devices.map(({ hidDevice, ...device }) => device)),\n );\n }\n\n private async updateTransportDiscoveredDevices(): Promise<void> {\n const eitherDevices = await this.getDevices();\n\n eitherDevices.caseOf({\n Left: (error) => {\n this._logger.error(\"Error while getting accessible device\", {\n data: { error },\n });\n },\n Right: (hidDevices) => {\n this._transportDiscoveredDevices.next(\n hidDevices.map((hidDevice) =>\n this.mapHIDDeviceToTransportDiscoveredDevice(hidDevice),\n ),\n );\n },\n });\n }\n\n private async promptDeviceAccess(): Promise<\n Either<PromptDeviceAccessError, NodeHIDDevice[]>\n > {\n return EitherAsync.liftEither(this.hidApi)\n .map(async (hidApi) => {\n let hidDevices: NodeHIDDevice[] = [];\n\n try {\n const allDevices = await hidApi.devicesAsync();\n\n hidDevices = allDevices.filter(\n (d) => d.vendorId === LEDGER_VENDOR_ID,\n );\n await this.updateTransportDiscoveredDevices();\n } catch (error) {\n const deviceError = new NoAccessibleDeviceError(error);\n this._logger.error(`promptDeviceAccess: error requesting device`, {\n data: { error },\n });\n throw deviceError;\n }\n\n this._logger.debug(\n `promptDeviceAccess: hidDevices len ${hidDevices.length}`,\n );\n\n // Granted access to 0 device (by clicking on cancel for ex) results in an error\n if (hidDevices.length === 0) {\n this._logger.warn(\"No device was selected\");\n throw new NoAccessibleDeviceError(\"No selected device\");\n }\n\n const discoveredHidDevices: NodeHIDDevice[] = [];\n\n for (const hidDevice of hidDevices) {\n discoveredHidDevices.push(hidDevice);\n\n this._logger.debug(`promptDeviceAccess: selected device`, {\n data: { hidDevice },\n });\n }\n\n return discoveredHidDevices;\n })\n .run();\n }\n\n startDiscovering(): Observable<TransportDiscoveredDevice> {\n this._logger.debug(\"startDiscovering\");\n\n return from(this.promptDeviceAccess()).pipe(\n switchMap((either) => {\n return either.caseOf({\n Left: (error) => {\n this._logger.error(\"Error while getting accessible device\", {\n data: { error },\n });\n throw error;\n },\n Right: (hidDevices) => {\n this._logger.info(`Got access to ${hidDevices.length} HID devices`);\n\n const discoveredDevices = hidDevices.map((hidDevice) => {\n return this.mapHIDDeviceToTransportDiscoveredDevice(hidDevice);\n });\n\n return from(discoveredDevices);\n },\n });\n }),\n );\n }\n\n stopDiscovering(): void {\n /**\n * This does nothing because the startDiscovering method is just a\n * promise wrapped into an observable. So there is no need to stop it.\n */\n }\n\n private startListeningToConnectionEvents(): void {\n this._logger.debug(\"startListeningToConnectionEvents\");\n\n usb.on(\"attach\", (device) => {\n this.handleDeviceConnection(device);\n });\n\n usb.on(\"detach\", (device) => {\n this.handleDeviceDisconnection(device);\n });\n\n process.on(\"exit\", () => {\n usb.unrefHotplugEvents();\n usb.removeAllListeners();\n });\n }\n\n private stopListeningToConnectionEvents(): void {\n this._logger.debug(\"stopListeningToConnectionEvents\");\n this._connectionListenersAbortController.abort();\n usb.removeAllListeners();\n }\n\n /**\n * Connect to a HID USB device and update the internal state of the associated device\n */\n async connect({\n deviceId,\n onDisconnect,\n }: {\n deviceId: DeviceId;\n onDisconnect: DisconnectHandler;\n }): Promise<Either<ConnectError, TransportConnectedDevice>> {\n this._logger.debug(\"connect\", { data: { deviceId } });\n\n const matchingInternalDevice = this._transportDiscoveredDevices\n .getValue()\n .find((internalDevice) => internalDevice.id === deviceId);\n\n if (!matchingInternalDevice) {\n this._logger.error(`Unknown device ${deviceId}`);\n return Left(new UnknownDeviceError(`Unknown device ${deviceId}`));\n }\n\n const alreadyExistingDeviceConnection =\n this._deviceConnectionsByHidDevice.get(matchingInternalDevice.hidDevice);\n if (alreadyExistingDeviceConnection) {\n return Right(\n new TransportConnectedDevice({\n id: deviceId,\n deviceModel: matchingInternalDevice.deviceModel,\n type: this.connectionType,\n sendApdu: (...args) =>\n alreadyExistingDeviceConnection.sendApdu(...args),\n transport: this.identifier,\n }),\n );\n }\n\n const nodeHidApduSender = this._deviceApduSenderFactory({\n dependencies: { device: matchingInternalDevice.hidDevice },\n apduSenderFactory: this._apduSenderFactory,\n apduReceiverFactory: this._apduReceiverFactory,\n loggerFactory: this._loggerServiceFactory,\n });\n\n const deviceConnection = this._deviceConnectionStateMachineFactory({\n deviceId,\n deviceApduSender: nodeHidApduSender,\n timeoutDuration: RECONNECT_DEVICE_TIMEOUT,\n tryToReconnect: () => {\n this._deviceConnectionsByHidDevice.forEach(\n (deviceConnection, hidDevice) => {\n if (deviceConnection.getDeviceId() === deviceId) {\n this._deviceConnectionsPendingReconnection.add(deviceConnection);\n this._deviceConnectionsByHidDevice.delete(hidDevice);\n }\n },\n );\n },\n onTerminated: () => {\n this._deviceConnectionsPendingReconnection.forEach(\n (deviceConnection) => {\n if (deviceConnection.getDeviceId() === deviceId) {\n this._deviceConnectionsPendingReconnection.delete(\n deviceConnection,\n );\n onDisconnect(deviceConnection.getDeviceId());\n }\n },\n );\n this._deviceConnectionsByHidDevice.forEach(\n (deviceConnection, hidDevice) => {\n if (deviceConnection.getDeviceId() === deviceId) {\n this._deviceConnectionsByHidDevice.delete(hidDevice);\n onDisconnect(deviceConnection.getDeviceId());\n }\n },\n );\n },\n });\n\n try {\n await deviceConnection.setupConnection();\n } catch (error) {\n this._logger.error(\"Error while setting up device connection\", {\n data: { error },\n });\n\n return Left(new OpeningConnectionError(error));\n }\n\n this._deviceConnectionsByHidDevice.set(\n matchingInternalDevice.hidDevice,\n deviceConnection,\n );\n\n const connectedDevice = new TransportConnectedDevice({\n sendApdu: (...args) => deviceConnection.sendApdu(...args),\n deviceModel: matchingInternalDevice.deviceModel,\n id: deviceId,\n type: this.connectionType,\n transport: this.identifier,\n });\n\n return Right(connectedDevice);\n }\n\n private getDeviceModel(\n hidDevice: NodeHIDDevice,\n ): Maybe<TransportDeviceModel> {\n const { productId } = hidDevice;\n const matchingModel = this._deviceModelDataSource.getAllDeviceModels().find(\n (deviceModel) =>\n // outside of bootloader mode, the value that we need to identify a device model is the first byte of the actual hidDevice.productId\n deviceModel.usbProductId === productId >> 8 ||\n deviceModel.bootloaderUsbProductId === productId,\n );\n return matchingModel ? Maybe.of(matchingModel) : Maybe.zero();\n }\n\n private getHidUsbProductId(hidDevice: NodeHIDDevice): number {\n return this.getDeviceModel(hidDevice).caseOf({\n Just: (deviceModel) => deviceModel.usbProductId,\n Nothing: () => hidDevice.productId >> 8,\n });\n }\n\n /**\n * Disconnect from a HID USB device\n */\n async disconnect(params: {\n connectedDevice: TransportConnectedDevice;\n }): Promise<Either<DmkError, void>> {\n this._logger.debug(\"disconnect\", { data: { connectedDevice: params } });\n\n const matchingDeviceConnection = Array.from(\n this._deviceConnectionsByHidDevice.values(),\n ).find(\n (deviceConnection) =>\n deviceConnection.getDeviceId() === params.connectedDevice.id,\n );\n\n if (!matchingDeviceConnection) {\n this._logger.error(\"No matching device connection found\", {\n data: { connectedDevice: params },\n });\n return Promise.resolve(\n Left(\n new UnknownDeviceError(`Unknown device ${params.connectedDevice.id}`),\n ),\n );\n }\n\n matchingDeviceConnection.closeConnection();\n return Promise.resolve(Right(undefined));\n }\n\n /**\n * Handle the disconnection event of a HID device\n * @param device USB device that was detached\n */\n private async handleDeviceDisconnection(device: Device): Promise<void> {\n const { idVendor, idProduct } = device.deviceDescriptor;\n\n if (idVendor !== LEDGER_VENDOR_ID) {\n return;\n }\n\n this._logger.info(\"[handleDeviceDisconnectionEvent] Device disconnected\", {\n data: { vendorId: idVendor, productId: idProduct },\n });\n\n this.updateTransportDiscoveredDevices();\n\n const matchingDeviceConnection = (): Maybe<\n DeviceConnectionStateMachine<NodeHidApduSenderDependencies>\n > => {\n for (const [\n hidDevice,\n deviceConnection,\n ] of this._deviceConnectionsByHidDevice.entries()) {\n if (\n hidDevice.vendorId === idVendor &&\n (hidDevice.productId >> 8 === idProduct ||\n hidDevice.productId === idProduct)\n ) {\n return Just(deviceConnection);\n }\n }\n return Nothing;\n };\n\n matchingDeviceConnection().caseOf({\n Just: (deviceConnection) => {\n try {\n deviceConnection.eventDeviceDisconnected();\n } catch (error) {\n this._logger.error(\"Error while handling device disconnection\", {\n data: { error },\n });\n }\n },\n Nothing: () => {\n this._logger.error(\"No matching device connection found\", {\n data: { vendorId: idVendor, productId: idProduct },\n });\n },\n });\n }\n\n private async handleDeviceReconnection(\n deviceConnection: DeviceConnectionStateMachine<NodeHidApduSenderDependencies>,\n hidDevice: NodeHIDDevice,\n ) {\n try {\n this._deviceConnectionsPendingReconnection.delete(deviceConnection);\n deviceConnection.setDependencies({ device: hidDevice });\n await deviceConnection.setupConnection();\n this._deviceConnectionsByHidDevice.set(hidDevice, deviceConnection);\n deviceConnection.eventDeviceConnected();\n } catch (error) {\n this._logger.error(\"Error while reconnecting to device\", {\n data: { error },\n });\n deviceConnection.closeConnection();\n }\n }\n\n /**\n * Handle the connection event of a HID device\n * @param device USB device that was attached\n */\n private async handleDeviceConnection(device: Device): Promise<void> {\n const { idVendor, idProduct } = device.deviceDescriptor;\n\n if (idVendor !== LEDGER_VENDOR_ID) {\n return;\n }\n\n this._logger.info(\"[handleDeviceConnection] New device connected\", {\n data: { vendorId: idVendor, productId: idProduct },\n });\n\n // Find the corresponding HID device by matching vendor and product IDs\n const eitherDevices = await this.getDevices();\n eitherDevices.caseOf({\n Left: (error) => {\n this._logger.error(\"Error while getting HID devices for reconnection\", {\n data: { error },\n });\n },\n Right: async (hidDevices) => {\n const matchingHidDevice = hidDevices.find(\n (hidDevice) =>\n hidDevice.vendorId === idVendor &&\n (hidDevice.productId >> 8 === idProduct ||\n hidDevice.productId === idProduct),\n );\n\n if (!matchingHidDevice) {\n this._logger.debug(\n \"[handleDeviceConnection] No matching HID device found\",\n {\n data: { vendorId: idVendor, productId: idProduct },\n },\n );\n\n return;\n }\n\n // Check if there's a pending reconnection for a device with matching product ID\n const matchingDeviceConnection = Array.from(\n this._deviceConnectionsPendingReconnection,\n ).find(\n (deviceConnection) =>\n this.getHidUsbProductId(\n deviceConnection.getDependencies().device,\n ) === this.getHidUsbProductId(matchingHidDevice),\n );\n\n if (matchingDeviceConnection) {\n await this.handleDeviceReconnection(\n matchingDeviceConnection,\n matchingHidDevice,\n );\n }\n\n /**\n * Note: we do this after handling the reconnection to allow the newly\n * discovered device to keep the same DeviceId as the previous one in case\n * of a reconnection.\n */\n await this.updateTransportDiscoveredDevices();\n },\n });\n }\n\n public destroy() {\n this.stopListeningToConnectionEvents();\n this._deviceConnectionsByHidDevice.forEach((connection) => {\n connection.closeConnection();\n });\n this._deviceConnectionsPendingReconnection.clear();\n }\n}\n\nexport const nodeHidTransportFactory: TransportFactory = ({\n deviceModelDataSource,\n loggerServiceFactory,\n apduSenderServiceFactory,\n apduReceiverServiceFactory,\n}) =>\n new NodeHidTransport(\n deviceModelDataSource,\n loggerServiceFactory,\n apduSenderServiceFactory,\n apduReceiverServiceFactory,\n );\n"],
5
- "mappings": "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",
6
- "names": ["NodeHidTransport_exports", "__export", "NodeHidTransport", "nodeHidIdentifier", "nodeHidTransportFactory", "__toCommonJS", "import_device_management_kit", "import_node_hid", "import_purify_ts", "import_rxjs", "import_usb", "import_uuid", "import_NodeHidConfig", "import_Errors", "import_NodeHidApduSender", "NodeHIDAPI", "_deviceModelDataSource", "_loggerServiceFactory", "_apduSenderFactory", "_apduReceiverFactory", "_deviceConnectionStateMachineFactory", "args", "_deviceApduSenderFactory", "error", "hidApi", "ledgerDevices", "hidDevice", "device", "deviceError", "existingDiscoveredDevice", "internalDevice", "existingDeviceConnection", "deviceModel", "id", "uuid", "discoveredDevice", "devices", "hidDevices", "d", "discoveredHidDevices", "either", "discoveredDevices", "deviceId", "onDisconnect", "matchingInternalDevice", "alreadyExistingDeviceConnection", "nodeHidApduSender", "deviceConnection", "connectedDevice", "productId", "matchingModel", "params", "matchingDeviceConnection", "idVendor", "idProduct", "matchingHidDevice", "connection", "deviceModelDataSource", "loggerServiceFactory", "apduSenderServiceFactory", "apduReceiverServiceFactory"]
4
+ "sourcesContent": ["import {\n type ApduReceiverServiceFactory,\n type ApduSenderServiceFactory,\n type ConnectError,\n type ConnectionType,\n DeviceConnectionStateMachine,\n type DeviceConnectionStateMachineParams,\n type DeviceId,\n type DeviceModelDataSource,\n DeviceNotRecognizedError,\n type DisconnectHandler,\n type DmkError,\n LEDGER_VENDOR_ID,\n type LoggerPublisherService,\n NoAccessibleDeviceError,\n OpeningConnectionError,\n type Transport,\n TransportConnectedDevice,\n type TransportDeviceModel,\n type TransportDiscoveredDevice,\n type TransportFactory,\n type TransportIdentifier,\n UnknownDeviceError,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Device as NodeHIDDevice, devicesAsync, HIDAsync } from \"node-hid\";\nimport {\n type Either,\n EitherAsync,\n Just,\n Left,\n Maybe,\n Nothing,\n Right,\n} from \"purify-ts\";\nimport { BehaviorSubject, from, map, type Observable, switchMap } from \"rxjs\";\nimport { type Device, usb } from \"usb\";\nimport { v4 as uuid } from \"uuid\";\n\nimport { RECONNECT_DEVICE_TIMEOUT } from \"@api/data/NodeHidConfig\";\nimport { NodeHidTransportNotSupportedError } from \"@api/model/Errors\";\nimport {\n NodeHidApduSender,\n type NodeHidApduSenderConstructorArgs,\n type NodeHidApduSenderDependencies,\n} from \"@api/transport/NodeHidApduSender\";\n\ntype NodeHIDAPI = typeof NodeHIDAPI;\nconst NodeHIDAPI = { devicesAsync, HIDAsync } as const;\n\n// HID usage page used by Ledger devices for the APDU channel on macOS and Windows.\nconst LEDGER_APDU_USAGE_PAGE = 0xffa0;\n\nconst isLedgerApduInterface = (hidDevice: NodeHIDDevice) => {\n if (hidDevice.vendorId !== LEDGER_VENDOR_ID) {\n return false;\n }\n\n if (process.platform === \"darwin\" || process.platform === \"win32\") {\n return hidDevice.usagePage === LEDGER_APDU_USAGE_PAGE;\n }\n\n return true;\n};\n\ntype PromptDeviceAccessError =\n | NoAccessibleDeviceError\n | NodeHidTransportNotSupportedError;\n\ntype NodeHidTransportDiscoveredDevice = TransportDiscoveredDevice & {\n hidDevice: NodeHIDDevice;\n};\n\nexport const nodeHidIdentifier: TransportIdentifier = \"NODE-HID\";\n\nexport class NodeHidTransport implements Transport {\n /** List of HID devices that have been discovered */\n private _transportDiscoveredDevices: BehaviorSubject<\n Array<NodeHidTransportDiscoveredDevice>\n > = new BehaviorSubject<Array<NodeHidTransportDiscoveredDevice>>([]);\n\n /** Map of *connected* HIDDevice to their NodeHidDeviceConnection */\n private _deviceConnectionsByHidDevice: Map<\n NodeHIDDevice,\n DeviceConnectionStateMachine<NodeHidApduSenderDependencies>\n > = new Map();\n\n /**\n * Set of NodeHidDeviceConnection for which the HIDDevice has been\n * disconnected, so they are waiting for a reconnection\n */\n private _deviceConnectionsPendingReconnection: Set<\n DeviceConnectionStateMachine<NodeHidApduSenderDependencies>\n > = new Set();\n\n /** AbortController to stop listening to HID connection events */\n private _connectionListenersAbortController: AbortController =\n new AbortController();\n private _logger: LoggerPublisherService;\n private readonly connectionType: ConnectionType = \"USB\";\n private readonly identifier: TransportIdentifier = nodeHidIdentifier;\n\n constructor(\n private readonly _deviceModelDataSource: DeviceModelDataSource,\n private readonly _loggerServiceFactory: (\n tag: string,\n ) => LoggerPublisherService,\n private readonly _apduSenderFactory: ApduSenderServiceFactory,\n private readonly _apduReceiverFactory: ApduReceiverServiceFactory,\n private readonly _deviceConnectionStateMachineFactory: (\n args: DeviceConnectionStateMachineParams<NodeHidApduSenderDependencies>,\n ) => DeviceConnectionStateMachine<NodeHidApduSenderDependencies> = (args) =>\n new DeviceConnectionStateMachine(args),\n private readonly _deviceApduSenderFactory: (\n args: NodeHidApduSenderConstructorArgs,\n ) => NodeHidApduSender = (args) => new NodeHidApduSender(args),\n ) {\n this._logger = _loggerServiceFactory(\"NodeHidTransport\");\n\n this.startListeningToConnectionEvents();\n }\n\n /**\n * Get the NodeHID API if supported or error\n * @returns `Either<NodeHidTransportNotSupportedError, HID>`\n */\n private get hidApi(): Either<NodeHidTransportNotSupportedError, NodeHIDAPI> {\n if (this.isSupported()) {\n return Right(NodeHIDAPI);\n }\n\n return Left(new NodeHidTransportNotSupportedError(\"NodeHID not supported\"));\n }\n\n isSupported() {\n try {\n const result = true; // Node hid should be supported !\n this._logger.debug(`isSupported: ${result}`);\n return result;\n } catch (error) {\n this._logger.error(`isSupported: error`, { data: { error } });\n return false;\n }\n }\n\n getIdentifier(): TransportIdentifier {\n return this.identifier;\n }\n\n private async getDevices(): Promise<Either<DmkError, NodeHIDDevice[]>> {\n return EitherAsync.liftEither(this.hidApi).map(async (hidApi) => {\n try {\n const allDevices = await hidApi.devicesAsync();\n\n const ledgerDevices = allDevices.filter(isLedgerApduInterface);\n\n // Remove duplicates from same device with different interfaces by keeping only one device per vendorId:productId combination\n const uniqueDevices = Array.from(\n new Map(\n ledgerDevices.map((device) => [\n `${device.vendorId}:${device.productId}`,\n device,\n ]),\n ).values(),\n );\n\n return uniqueDevices;\n } catch (error) {\n const deviceError = new NoAccessibleDeviceError(error);\n this._logger.error(`getDevices: error getting devices`, {\n data: { error },\n });\n throw deviceError;\n }\n });\n }\n\n /**\n * Map a HIDDevice to an TransportDiscoveredDevice, either by creating a new one or returning an existing one\n */\n private mapHIDDeviceToTransportDiscoveredDevice(\n hidDevice: NodeHIDDevice,\n ): NodeHidTransportDiscoveredDevice {\n const existingDiscoveredDevice = this._transportDiscoveredDevices\n .getValue()\n .find(\n (internalDevice) =>\n internalDevice.hidDevice.vendorId === hidDevice.vendorId &&\n internalDevice.hidDevice.productId === hidDevice.productId,\n );\n\n if (existingDiscoveredDevice) {\n return existingDiscoveredDevice;\n }\n\n const existingDeviceConnection =\n this._deviceConnectionsByHidDevice.get(hidDevice);\n\n const maybeDeviceModel = this.getDeviceModel(hidDevice);\n return maybeDeviceModel.caseOf({\n Just: (deviceModel) => {\n const id = existingDeviceConnection?.getDeviceId() ?? uuid();\n\n const discoveredDevice = {\n id,\n deviceModel,\n hidDevice,\n transport: this.identifier,\n };\n\n this._logger.debug(\n `Discovered device ${id} ${discoveredDevice.deviceModel.productName}`,\n );\n\n return discoveredDevice;\n },\n Nothing: () => {\n // [ASK] Or we just ignore the not recognized device ? And log them\n this._logger.warn(\n `Device not recognized: hidDevice.productId: 0x${hidDevice.productId.toString(16)}`,\n );\n throw new DeviceNotRecognizedError(\n `Device not recognized: hidDevice.productId: 0x${hidDevice.productId.toString(16)}`,\n );\n },\n });\n }\n\n /**\n * Listen to known devices (devices to which the user has granted access)\n */\n public listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]> {\n this.updateTransportDiscoveredDevices();\n return this._transportDiscoveredDevices.pipe(\n map((devices) => devices.map(({ hidDevice, ...device }) => device)),\n );\n }\n\n private async updateTransportDiscoveredDevices(): Promise<void> {\n const eitherDevices = await this.getDevices();\n\n eitherDevices.caseOf({\n Left: (error) => {\n this._logger.error(\"Error while getting accessible device\", {\n data: { error },\n });\n },\n Right: (hidDevices) => {\n this._transportDiscoveredDevices.next(\n hidDevices.map((hidDevice) =>\n this.mapHIDDeviceToTransportDiscoveredDevice(hidDevice),\n ),\n );\n },\n });\n }\n\n private async promptDeviceAccess(): Promise<\n Either<PromptDeviceAccessError, NodeHIDDevice[]>\n > {\n return EitherAsync.liftEither(this.hidApi)\n .map(async (hidApi) => {\n let hidDevices: NodeHIDDevice[] = [];\n\n try {\n const allDevices = await hidApi.devicesAsync();\n\n hidDevices = allDevices.filter(isLedgerApduInterface);\n await this.updateTransportDiscoveredDevices();\n } catch (error) {\n const deviceError = new NoAccessibleDeviceError(error);\n this._logger.error(`promptDeviceAccess: error requesting device`, {\n data: { error },\n });\n throw deviceError;\n }\n\n this._logger.debug(\n `promptDeviceAccess: hidDevices len ${hidDevices.length}`,\n );\n\n // Granted access to 0 device (by clicking on cancel for ex) results in an error\n if (hidDevices.length === 0) {\n this._logger.warn(\"No device was selected\");\n throw new NoAccessibleDeviceError(\"No selected device\");\n }\n\n const discoveredHidDevices: NodeHIDDevice[] = [];\n\n for (const hidDevice of hidDevices) {\n discoveredHidDevices.push(hidDevice);\n\n this._logger.debug(`promptDeviceAccess: selected device`, {\n data: { hidDevice },\n });\n }\n\n return discoveredHidDevices;\n })\n .run();\n }\n\n startDiscovering(): Observable<TransportDiscoveredDevice> {\n this._logger.debug(\"startDiscovering\");\n\n return from(this.promptDeviceAccess()).pipe(\n switchMap((either) => {\n return either.caseOf({\n Left: (error) => {\n this._logger.error(\"Error while getting accessible device\", {\n data: { error },\n });\n throw error;\n },\n Right: (hidDevices) => {\n this._logger.info(`Got access to ${hidDevices.length} HID devices`);\n\n const discoveredDevices = hidDevices.map((hidDevice) => {\n return this.mapHIDDeviceToTransportDiscoveredDevice(hidDevice);\n });\n\n return from(discoveredDevices);\n },\n });\n }),\n );\n }\n\n stopDiscovering(): void {\n /**\n * This does nothing because the startDiscovering method is just a\n * promise wrapped into an observable. So there is no need to stop it.\n */\n }\n\n private startListeningToConnectionEvents(): void {\n this._logger.debug(\"startListeningToConnectionEvents\");\n\n usb.on(\"attach\", (device) => {\n this.handleDeviceConnection(device);\n });\n\n usb.on(\"detach\", (device) => {\n this.handleDeviceDisconnection(device);\n });\n\n process.on(\"exit\", () => {\n usb.unrefHotplugEvents();\n usb.removeAllListeners();\n });\n }\n\n private stopListeningToConnectionEvents(): void {\n this._logger.debug(\"stopListeningToConnectionEvents\");\n this._connectionListenersAbortController.abort();\n usb.removeAllListeners();\n }\n\n /**\n * Connect to a HID USB device and update the internal state of the associated device\n */\n async connect({\n deviceId,\n onDisconnect,\n }: {\n deviceId: DeviceId;\n onDisconnect: DisconnectHandler;\n }): Promise<Either<ConnectError, TransportConnectedDevice>> {\n this._logger.debug(\"connect\", { data: { deviceId } });\n\n const matchingInternalDevice = this._transportDiscoveredDevices\n .getValue()\n .find((internalDevice) => internalDevice.id === deviceId);\n\n if (!matchingInternalDevice) {\n this._logger.error(`Unknown device ${deviceId}`);\n return Left(new UnknownDeviceError(`Unknown device ${deviceId}`));\n }\n\n const alreadyExistingDeviceConnection =\n this._deviceConnectionsByHidDevice.get(matchingInternalDevice.hidDevice);\n if (alreadyExistingDeviceConnection) {\n return Right(\n new TransportConnectedDevice({\n id: deviceId,\n deviceModel: matchingInternalDevice.deviceModel,\n type: this.connectionType,\n sendApdu: (...args) =>\n alreadyExistingDeviceConnection.sendApdu(...args),\n transport: this.identifier,\n }),\n );\n }\n\n const nodeHidApduSender = this._deviceApduSenderFactory({\n dependencies: { device: matchingInternalDevice.hidDevice },\n apduSenderFactory: this._apduSenderFactory,\n apduReceiverFactory: this._apduReceiverFactory,\n loggerFactory: this._loggerServiceFactory,\n });\n\n const deviceConnection = this._deviceConnectionStateMachineFactory({\n deviceId,\n deviceApduSender: nodeHidApduSender,\n timeoutDuration: RECONNECT_DEVICE_TIMEOUT,\n tryToReconnect: () => {\n this._deviceConnectionsByHidDevice.forEach(\n (deviceConnection, hidDevice) => {\n if (deviceConnection.getDeviceId() === deviceId) {\n this._deviceConnectionsPendingReconnection.add(deviceConnection);\n this._deviceConnectionsByHidDevice.delete(hidDevice);\n }\n },\n );\n },\n onTerminated: () => {\n this._deviceConnectionsPendingReconnection.forEach(\n (deviceConnection) => {\n if (deviceConnection.getDeviceId() === deviceId) {\n this._deviceConnectionsPendingReconnection.delete(\n deviceConnection,\n );\n onDisconnect(deviceConnection.getDeviceId());\n }\n },\n );\n this._deviceConnectionsByHidDevice.forEach(\n (deviceConnection, hidDevice) => {\n if (deviceConnection.getDeviceId() === deviceId) {\n this._deviceConnectionsByHidDevice.delete(hidDevice);\n onDisconnect(deviceConnection.getDeviceId());\n }\n },\n );\n },\n });\n\n try {\n await deviceConnection.setupConnection();\n } catch (error) {\n this._logger.error(\"Error while setting up device connection\", {\n data: { error },\n });\n\n return Left(new OpeningConnectionError(error));\n }\n\n this._deviceConnectionsByHidDevice.set(\n matchingInternalDevice.hidDevice,\n deviceConnection,\n );\n\n const connectedDevice = new TransportConnectedDevice({\n sendApdu: (...args) => deviceConnection.sendApdu(...args),\n deviceModel: matchingInternalDevice.deviceModel,\n id: deviceId,\n type: this.connectionType,\n transport: this.identifier,\n });\n\n return Right(connectedDevice);\n }\n\n private getDeviceModel(\n hidDevice: NodeHIDDevice,\n ): Maybe<TransportDeviceModel> {\n const { productId } = hidDevice;\n const matchingModel = this._deviceModelDataSource.getAllDeviceModels().find(\n (deviceModel) =>\n // outside of bootloader mode, the value that we need to identify a device model is the first byte of the actual hidDevice.productId\n deviceModel.usbProductId === productId >> 8 ||\n deviceModel.bootloaderUsbProductId === productId,\n );\n return matchingModel ? Maybe.of(matchingModel) : Maybe.zero();\n }\n\n private getHidUsbProductId(hidDevice: NodeHIDDevice): number {\n return this.getDeviceModel(hidDevice).caseOf({\n Just: (deviceModel) => deviceModel.usbProductId,\n Nothing: () => hidDevice.productId >> 8,\n });\n }\n\n /**\n * Disconnect from a HID USB device\n */\n async disconnect(params: {\n connectedDevice: TransportConnectedDevice;\n }): Promise<Either<DmkError, void>> {\n this._logger.debug(\"disconnect\", { data: { connectedDevice: params } });\n\n const matchingDeviceConnection = Array.from(\n this._deviceConnectionsByHidDevice.values(),\n ).find(\n (deviceConnection) =>\n deviceConnection.getDeviceId() === params.connectedDevice.id,\n );\n\n if (!matchingDeviceConnection) {\n this._logger.error(\"No matching device connection found\", {\n data: { connectedDevice: params },\n });\n return Promise.resolve(\n Left(\n new UnknownDeviceError(`Unknown device ${params.connectedDevice.id}`),\n ),\n );\n }\n\n matchingDeviceConnection.closeConnection();\n return Promise.resolve(Right(undefined));\n }\n\n /**\n * Handle the disconnection event of a HID device\n * @param device USB device that was detached\n */\n private async handleDeviceDisconnection(device: Device): Promise<void> {\n const { idVendor, idProduct } = device.deviceDescriptor;\n\n if (idVendor !== LEDGER_VENDOR_ID) {\n return;\n }\n\n this._logger.info(\"[handleDeviceDisconnectionEvent] Device disconnected\", {\n data: { vendorId: idVendor, productId: idProduct },\n });\n\n this.updateTransportDiscoveredDevices();\n\n const matchingDeviceConnection = (): Maybe<\n DeviceConnectionStateMachine<NodeHidApduSenderDependencies>\n > => {\n for (const [\n hidDevice,\n deviceConnection,\n ] of this._deviceConnectionsByHidDevice.entries()) {\n if (\n hidDevice.vendorId === idVendor &&\n (hidDevice.productId >> 8 === idProduct ||\n hidDevice.productId === idProduct)\n ) {\n return Just(deviceConnection);\n }\n }\n return Nothing;\n };\n\n matchingDeviceConnection().caseOf({\n Just: (deviceConnection) => {\n try {\n deviceConnection.eventDeviceDisconnected();\n } catch (error) {\n this._logger.error(\"Error while handling device disconnection\", {\n data: { error },\n });\n }\n },\n Nothing: () => {\n this._logger.error(\"No matching device connection found\", {\n data: { vendorId: idVendor, productId: idProduct },\n });\n },\n });\n }\n\n private async handleDeviceReconnection(\n deviceConnection: DeviceConnectionStateMachine<NodeHidApduSenderDependencies>,\n hidDevice: NodeHIDDevice,\n ) {\n try {\n this._deviceConnectionsPendingReconnection.delete(deviceConnection);\n deviceConnection.setDependencies({ device: hidDevice });\n await deviceConnection.setupConnection();\n this._deviceConnectionsByHidDevice.set(hidDevice, deviceConnection);\n deviceConnection.eventDeviceConnected();\n } catch (error) {\n this._logger.error(\"Error while reconnecting to device\", {\n data: { error },\n });\n deviceConnection.closeConnection();\n }\n }\n\n /**\n * Handle the connection event of a HID device\n * @param device USB device that was attached\n */\n private async handleDeviceConnection(device: Device): Promise<void> {\n const { idVendor, idProduct } = device.deviceDescriptor;\n\n if (idVendor !== LEDGER_VENDOR_ID) {\n return;\n }\n\n this._logger.info(\"[handleDeviceConnection] New device connected\", {\n data: { vendorId: idVendor, productId: idProduct },\n });\n\n // Find the corresponding HID device by matching vendor and product IDs\n const eitherDevices = await this.getDevices();\n eitherDevices.caseOf({\n Left: (error) => {\n this._logger.error(\"Error while getting HID devices for reconnection\", {\n data: { error },\n });\n },\n Right: async (hidDevices) => {\n const matchingHidDevice = hidDevices.find(\n (hidDevice) =>\n hidDevice.vendorId === idVendor &&\n (hidDevice.productId >> 8 === idProduct ||\n hidDevice.productId === idProduct),\n );\n\n if (!matchingHidDevice) {\n this._logger.debug(\n \"[handleDeviceConnection] No matching HID device found\",\n {\n data: { vendorId: idVendor, productId: idProduct },\n },\n );\n\n return;\n }\n\n // Check if there's a pending reconnection for a device with matching product ID\n const matchingDeviceConnection = Array.from(\n this._deviceConnectionsPendingReconnection,\n ).find(\n (deviceConnection) =>\n this.getHidUsbProductId(\n deviceConnection.getDependencies().device,\n ) === this.getHidUsbProductId(matchingHidDevice),\n );\n\n if (matchingDeviceConnection) {\n await this.handleDeviceReconnection(\n matchingDeviceConnection,\n matchingHidDevice,\n );\n }\n\n /**\n * Note: we do this after handling the reconnection to allow the newly\n * discovered device to keep the same DeviceId as the previous one in case\n * of a reconnection.\n */\n await this.updateTransportDiscoveredDevices();\n },\n });\n }\n\n public destroy() {\n this.stopListeningToConnectionEvents();\n this._deviceConnectionsByHidDevice.forEach((connection) => {\n connection.closeConnection();\n });\n this._deviceConnectionsPendingReconnection.clear();\n }\n}\n\nexport const nodeHidTransportFactory: TransportFactory = ({\n deviceModelDataSource,\n loggerServiceFactory,\n apduSenderServiceFactory,\n apduReceiverServiceFactory,\n}) =>\n new NodeHidTransport(\n deviceModelDataSource,\n loggerServiceFactory,\n apduSenderServiceFactory,\n apduReceiverServiceFactory,\n );\n"],
5
+ "mappings": "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",
6
+ "names": ["NodeHidTransport_exports", "__export", "NodeHidTransport", "nodeHidIdentifier", "nodeHidTransportFactory", "__toCommonJS", "import_device_management_kit", "import_node_hid", "import_purify_ts", "import_rxjs", "import_usb", "import_uuid", "import_NodeHidConfig", "import_Errors", "import_NodeHidApduSender", "NodeHIDAPI", "LEDGER_APDU_USAGE_PAGE", "isLedgerApduInterface", "hidDevice", "_deviceModelDataSource", "_loggerServiceFactory", "_apduSenderFactory", "_apduReceiverFactory", "_deviceConnectionStateMachineFactory", "args", "_deviceApduSenderFactory", "error", "hidApi", "ledgerDevices", "device", "deviceError", "existingDiscoveredDevice", "internalDevice", "existingDeviceConnection", "deviceModel", "id", "uuid", "discoveredDevice", "devices", "hidDevices", "discoveredHidDevices", "either", "discoveredDevices", "deviceId", "onDisconnect", "matchingInternalDevice", "alreadyExistingDeviceConnection", "nodeHidApduSender", "deviceConnection", "connectedDevice", "productId", "matchingModel", "params", "matchingDeviceConnection", "idVendor", "idProduct", "matchingHidDevice", "connection", "deviceModelDataSource", "loggerServiceFactory", "apduSenderServiceFactory", "apduReceiverServiceFactory"]
7
7
  }
@@ -1,2 +1,2 @@
1
- "use strict";var s=require("@ledgerhq/device-management-kit"),w=require("purify-ts"),m=require("rxjs"),b=require("../data/NodeHidConfig"),a=require("../model/HIDDevice.stub"),V=require("./NodeHidTransport");const d=vi.fn();vi.mock("node-hid",()=>({devicesAsync:(...n)=>d(...n),HIDAsync:{open:vi.fn()}}));const I=vi.fn(),T=[],N=[];vi.mock("usb",()=>({usb:{on:(n,l)=>{I(n,l),n==="attach"?T.push(l):n==="detach"&&N.push(l)},removeAllListeners:vi.fn(),unrefHotplugEvents:vi.fn()},Device:class{}}));class M{constructor(l,f){this.subscribers=l,this.tag=f}subscribers=[];tag="";error=vi.fn();warn=vi.fn();info=vi.fn();debug=vi.fn()}const R=new s.StaticDeviceModelDataSource,P=new M([],"node-hid"),D=(0,a.nodeHidDeviceStubBuilder)(),v=async()=>{const n=await vi.importActual("timers");return new Promise(n.setImmediate)},H=(n,l)=>({deviceDescriptor:{idVendor:n,idProduct:l}}),k=(n,l)=>{const f=H(n,l);T.forEach(h=>h(f))},y=(n,l)=>{const f=H(n,l);N.forEach(h=>h(f))};describe("NodeHidTransport",()=>{let n,l,f,h=vi.fn();const C=vi.fn(),E=vi.fn(),O={sendApdu:vi.fn().mockResolvedValue((0,w.Right)({data:new Uint8Array,statusCode:new Uint8Array([144,0])})),getDependencies:vi.fn().mockReturnValue({device:D}),setDependencies:vi.fn(),closeConnection:vi.fn(),setupConnection:vi.fn()},g={getDependencies:vi.fn().mockReturnValue({device:D}),setDependencies:vi.fn(),getDeviceId:vi.fn(),sendApdu:vi.fn().mockResolvedValue((0,w.Right)({data:new Uint8Array,statusCode:new Uint8Array([144,0])})),setupConnection:vi.fn(),eventDeviceConnected:C,eventDeviceDisconnected:E,closeConnection:vi.fn()};function B(){T.length=0,N.length=0,l=vi.fn(),f=vi.fn(),h=vi.fn(e=>({...g,getDeviceId:vi.fn().mockReturnValue(e.deviceId),getDependencies:e.deviceApduSender.getDependencies,setDependencies:e.deviceApduSender.setDependencies}));const c=vi.fn(e=>({...O,getDependencies:()=>e.dependencies,setDependencies:i=>e.dependencies=i}));n=new V.NodeHidTransport(R,()=>P,f,l,h,c)}beforeEach(()=>{B(),vi.useFakeTimers()}),afterEach(()=>{vi.restoreAllMocks(),vi.clearAllMocks(),vi.useRealTimers()});const x=(c,e)=>{n.startDiscovering().subscribe({next:c,error:e})};describe("isSupported",()=>{it("should always support the transport",()=>{expect(n.isSupported()).toBe(!0)})}),describe("getIdentifier",()=>{it("should return NODE-HID identifier",()=>{expect(n.getIdentifier()).toBe("NODE-HID")})}),describe("startDiscovering",()=>{R.getAllDeviceModels().flatMap(e=>[{testTitle:`should emit device when discovered (${e.productName})`,hidDevice:(0,a.nodeHidDeviceStubBuilder)({productId:e.usbProductId<<8,product:e.productName}),expectedDeviceModel:e},{testTitle:`should emit device when discovered (${e.productName}, bootloader)`,hidDevice:(0,a.nodeHidDeviceStubBuilder)({productId:e.bootloaderUsbProductId,product:e.productName}),expectedDeviceModel:e}]).forEach(e=>{it(e.testTitle,()=>new Promise((i,t)=>{d.mockResolvedValueOnce([e.hidDevice]),x(o=>{try{expect(o).toEqual(expect.objectContaining({deviceModel:e.expectedDeviceModel})),i()}catch(r){t(r)}},o=>{t(o)})}))}),it("should emit multiple devices if several are connected",()=>new Promise((e,i)=>{d.mockResolvedValueOnce([D,(0,a.nodeHidDeviceStubBuilder)({productId:20497,product:"Ledger Nano S Plus",path:"/dev/hidraw1"})]);let t=0;x(o=>{try{switch(t){case 0:expect(o).toEqual(expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X,productName:"Ledger Nano X",usbProductId:64})}));break;case 1:expect(o).toEqual(expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_SP,productName:"Ledger Nano S Plus",usbProductId:80})})),e();break}t++}catch(r){i(r)}},o=>{i(o)})})),it("should throw DeviceNotRecognizedError if the device is not recognized",()=>new Promise((e,i)=>{d.mockResolvedValueOnce([(0,a.nodeHidDeviceStubBuilder)({productId:16962})]),x(()=>{i("should not return a device")},t=>{expect(t).toBeInstanceOf(s.DeviceNotRecognizedError),e()})})),it("should emit an error if devicesAsync throws",()=>new Promise((e,i)=>{const t="devices async error";d.mockImplementationOnce(()=>{throw new Error(t)}),x(()=>{i("should not return a device")},o=>{expect(o).toBeInstanceOf(s.NoAccessibleDeviceError),expect(o).toStrictEqual(new s.NoAccessibleDeviceError(new Error(t))),e()})})),it("should emit an error if no devices are found",()=>new Promise((e,i)=>{d.mockResolvedValueOnce([]),x(t=>{i(`Should not emit any value, but emitted ${JSON.stringify(t)}`)},t=>{try{expect(t).toBeInstanceOf(s.NoAccessibleDeviceError),e()}catch(o){i(o)}})})),it("should emit the same discoveredDevice object if discovered twice in a row",async()=>{d.mockResolvedValue([D]);const e=await new Promise((t,o)=>{x(()=>t(),r=>o(r))}),i=await new Promise((t,o)=>{x(()=>t(),r=>o(r))});expect(i).toBe(e)})}),describe("destroy",()=>{it("should stop monitoring connections when destroyed",()=>{const c=vi.spyOn(AbortController.prototype,"abort");n.destroy(),expect(c).toHaveBeenCalled()})}),describe("connect",()=>{it("should throw UnknownDeviceError if no internal device",async()=>{const c={deviceId:"fake",onDisconnect:vi.fn()},e=await n.connect(c);expect(e).toStrictEqual((0,w.Left)(new s.UnknownDeviceError("Unknown device fake")))}),it("should throw OpeningConnectionError if the device cannot be opened",async()=>{const c="cannot be opened";g.setupConnection.mockRejectedValueOnce(new Error(c)),d.mockResolvedValueOnce([D]),d.mockResolvedValue([D]);const e=await(0,m.lastValueFrom)(n.startDiscovering()),i=await n.connect({deviceId:e.id,onDisconnect:vi.fn()});expect(i.isLeft()).toBe(!0),expect(i.extract()).toBeInstanceOf(s.OpeningConnectionError)}),it("should return a device if available",async()=>{d.mockResolvedValueOnce([D]),d.mockResolvedValue([D]);const c=await(0,m.lastValueFrom)(n.startDiscovering()),e=await n.connect({deviceId:c.id,onDisconnect:vi.fn()});expect(e.isRight()).toStrictEqual(!0),expect(e.extract()).toEqual(expect.objectContaining({id:c.id}))}),it("should return an existing connected device",async()=>{d.mockResolvedValueOnce([D]),d.mockResolvedValue([D]);const c=await(0,m.lastValueFrom)(n.startDiscovering());await n.connect({deviceId:c.id,onDisconnect:vi.fn()});const e=await n.connect({deviceId:c.id,onDisconnect:vi.fn()});expect(e.isRight()).toStrictEqual(!0),expect(e.extract()).toEqual(expect.objectContaining({id:c.id}))})}),describe("disconnect",()=>{it("should throw an error if the device is not connected",async()=>{const c=(0,s.connectedDeviceStubBuilder)(),e=await n.disconnect({connectedDevice:c});expect(e).toStrictEqual((0,w.Left)(new s.UnknownDeviceError(`Unknown device ${c.id}`)))}),it("should disconnect if the device is connected",async()=>{d.mockResolvedValueOnce([D]),d.mockResolvedValue([D]);const c=await(0,m.lastValueFrom)(n.startDiscovering()),e=await n.connect({deviceId:c.id,onDisconnect:vi.fn()});expect(e.isRight()).toStrictEqual(!0);const i=await n.disconnect({connectedDevice:e.unsafeCoerce()});expect(i).toStrictEqual((0,w.Right)(void 0))}),it("should call disconnect handler if a connected device is unplugged",async()=>{let c=vi.fn();h.mockImplementationOnce(S=>(c=S.onTerminated,{...g,getDeviceId:vi.fn().mockReturnValue(S.deviceId)}));const e=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw0"}),i=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1",productId:20497});d.mockResolvedValueOnce([e,i]),d.mockResolvedValue([e,i]);const t=await(0,m.lastValueFrom)(n.startDiscovering().pipe((0,m.toArray)()));expect(t.length).toStrictEqual(2);const o=vi.fn(),r=await n.connect({deviceId:t[0].id,onDisconnect:o}),u=vi.fn(),p=await n.connect({deviceId:t[1].id,onDisconnect:u});expect(r.isRight()).toStrictEqual(!0),expect(p.isRight()).toStrictEqual(!0),c(),expect(o).toHaveBeenCalled(),expect(u).not.toHaveBeenCalled()}),it("should call disconnect handler if a connected device is unplugged while reconnecting",async()=>{let c=vi.fn(),e=vi.fn();h.mockImplementationOnce(A=>(c=A.onTerminated,e=A.tryToReconnect,{...g,getDeviceId:vi.fn().mockReturnValue(A.deviceId)}));const i=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw0"}),t=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1",productId:20497});d.mockResolvedValueOnce([i,t]),d.mockResolvedValue([i,t]);const o=await(0,m.lastValueFrom)(n.startDiscovering().pipe((0,m.toArray)()));expect(o.length).toStrictEqual(2);const r=vi.fn(),u=await n.connect({deviceId:o[0].id,onDisconnect:r}),p=vi.fn(),S=await n.connect({deviceId:o[1].id,onDisconnect:p});expect(u.isRight()).toStrictEqual(!0),expect(S.isRight()).toStrictEqual(!0),e(),c(),expect(r).toHaveBeenCalled(),expect(p).not.toHaveBeenCalled()})}),describe("reconnect",()=>{it("should stop disconnection if reconnection happens",()=>new Promise((c,e)=>{const i=vi.fn();let t=vi.fn();const o=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw0"}),r=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1"});d.mockResolvedValueOnce([o]),d.mockResolvedValue([o,r]),h.mockImplementationOnce(u=>(t=u.tryToReconnect,{...g,getDeviceId:vi.fn().mockReturnValue(u.deviceId),getDependencies:u.deviceApduSender.getDependencies,setDependencies:u.deviceApduSender.setDependencies})),x(async u=>{try{await n.connect({deviceId:u.id,onDisconnect:i}),y(11415,64),await v(),expect(E).toHaveBeenCalled(),t(),vi.advanceTimersByTime(b.RECONNECT_DEVICE_TIMEOUT/3),k(11415,64),await v(),expect(C).toHaveBeenCalled(),vi.advanceTimersByTime(b.RECONNECT_DEVICE_TIMEOUT),expect(i).not.toHaveBeenCalled(),c()}catch(p){e(p)}})})),it("should be able to reconnect twice in a row if the device is unplugged and replugged twice",()=>new Promise((c,e)=>{const i=vi.fn();let t=vi.fn();const o=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw0"}),r=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1"}),u=(0,a.nodeHidDeviceStubBuilder)({path:"/dev/hidraw2"});d.mockResolvedValueOnce([o]),d.mockResolvedValue([o,r,u]),h.mockImplementationOnce(p=>(t=p.tryToReconnect,{...g,getDeviceId:vi.fn().mockReturnValue(p.deviceId),getDependencies:p.deviceApduSender.getDependencies,setDependencies:p.deviceApduSender.setDependencies})),x(async p=>{await n.connect({deviceId:p.id,onDisconnect:i});try{y(11415,64),await v(),expect(E).toHaveBeenCalled(),t(),vi.advanceTimersByTime(b.RECONNECT_DEVICE_TIMEOUT/3),k(11415,64),await v(),expect(C).toHaveBeenCalled(),vi.advanceTimersByTime(b.RECONNECT_DEVICE_TIMEOUT),expect(i).not.toHaveBeenCalled(),y(11415,64),await v(),expect(E).toHaveBeenCalled(),t(),vi.advanceTimersByTime(b.RECONNECT_DEVICE_TIMEOUT/3),k(11415,64),await v(),expect(C).toHaveBeenCalled(),vi.advanceTimersByTime(b.RECONNECT_DEVICE_TIMEOUT),expect(i).not.toHaveBeenCalled(),c()}catch(S){e(S)}})}))}),describe("listenToAvailableDevices",()=>{it("should emit the devices already connected before listening",async()=>{const c=(0,a.nodeHidDeviceStubBuilder)();d.mockResolvedValue([c]);const e=vi.fn(),i=vi.fn();let t=[];n.listenToAvailableDevices().subscribe({next:o=>{t=o},complete:e,error:i}),await v(),expect(t).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X})})]),expect(e).not.toHaveBeenCalled(),expect(i).not.toHaveBeenCalled()}),it("should emit the new list of devices after connection and disconnection events",async()=>{B();const c=(0,a.nodeHidDeviceStubBuilder)({productId:R.getDeviceModel({id:s.DeviceModelId.NANO_X}).usbProductId<<8,path:"/dev/hidraw0"}),e=(0,a.nodeHidDeviceStubBuilder)({productId:R.getDeviceModel({id:s.DeviceModelId.STAX}).usbProductId<<8,path:"/dev/hidraw1"});d.mockResolvedValue([c]);const i=vi.fn(),t=vi.fn();let o=[];n.listenToAvailableDevices().subscribe({next:r=>{o=r},complete:i,error:t}),await v(),expect(o).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X})})]),d.mockResolvedValue([c,e]),k(11415,96),await v(),expect(o).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X})}),expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.STAX})})]),d.mockResolvedValue([e]),y(11415,64),await v(),expect(o).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.STAX})})]),expect(i).not.toHaveBeenCalled(),expect(t).not.toHaveBeenCalled()}),it("should preserve DeviceId in case the device has been disconnected and reconnected before the timeout",async()=>{const c=(0,a.nodeHidDeviceStubBuilder)();d.mockResolvedValue([c]);const e=vi.fn(),i=vi.fn();let t=[];n.listenToAvailableDevices().subscribe({next:r=>{t=r},complete:e,error:i}),await v();const o=t[0]?.id;expect(o).toBeTruthy(),expect(t[0]?.deviceModel?.id).toBe(s.DeviceModelId.NANO_X),await n.connect({deviceId:t[0].id,onDisconnect:vi.fn()}),await v(),d.mockResolvedValue([]),y(11415,64),await v(),expect(t).toEqual([]),d.mockResolvedValue([c]),k(11415,64),await v(),expect(t).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X})})]),expect(t[0]?.id).toBeTruthy(),expect(t[0]?.id).toBe(o)})}),describe("USB connection events",()=>{it("should register usb attach and detach listeners on construction",()=>{expect(I).toHaveBeenCalledWith("attach",expect.any(Function)),expect(I).toHaveBeenCalledWith("detach",expect.any(Function))}),it("should ignore non-Ledger USB devices",async()=>{d.mockResolvedValue([D]);let c=[];n.listenToAvailableDevices().subscribe({next:e=>{c=e}}),await v(),expect(c.length).toBe(1),k(4660,22136),await v(),expect(c.length).toBe(1)})})});
1
+ "use strict";var s=require("@ledgerhq/device-management-kit"),g=require("purify-ts"),u=require("rxjs"),x=require("../data/NodeHidConfig"),r=require("../model/HIDDevice.stub"),O=require("./NodeHidTransport");const d=vi.fn();vi.mock("node-hid",()=>({devicesAsync:(...v)=>d(...v),HIDAsync:{open:vi.fn()}}));const N=vi.fn(),B=[],V=[];vi.mock("usb",()=>({usb:{on:(v,c)=>{N(v,c),v==="attach"?B.push(c):v==="detach"&&V.push(c)},removeAllListeners:vi.fn(),unrefHotplugEvents:vi.fn()},Device:class{}}));class q{constructor(c,f){this.subscribers=c,this.tag=f}subscribers=[];tag="";error=vi.fn();warn=vi.fn();info=vi.fn();debug=vi.fn()}const E=new s.StaticDeviceModelDataSource,U=new q([],"node-hid"),h=(0,r.nodeHidDeviceStubBuilder)(),l=async()=>{const v=await vi.importActual("timers");return new Promise(v.setImmediate)},T=v=>{Object.defineProperty(process,"platform",{value:v,configurable:!0})},P=(v,c)=>({deviceDescriptor:{idVendor:v,idProduct:c}}),y=(v,c)=>{const f=P(v,c);B.forEach(w=>w(f))},C=(v,c)=>{const f=P(v,c);V.forEach(w=>w(f))};describe("NodeHidTransport",()=>{const v=process.platform;let c,f,w,b=vi.fn();const R=vi.fn(),I=vi.fn(),M={sendApdu:vi.fn().mockResolvedValue((0,g.Right)({data:new Uint8Array,statusCode:new Uint8Array([144,0])})),getDependencies:vi.fn().mockReturnValue({device:h}),setDependencies:vi.fn(),closeConnection:vi.fn(),setupConnection:vi.fn()},k={getDependencies:vi.fn().mockReturnValue({device:h}),setDependencies:vi.fn(),getDeviceId:vi.fn(),sendApdu:vi.fn().mockResolvedValue((0,g.Right)({data:new Uint8Array,statusCode:new Uint8Array([144,0])})),setupConnection:vi.fn(),eventDeviceConnected:R,eventDeviceDisconnected:I,closeConnection:vi.fn()};function H(){B.length=0,V.length=0,f=vi.fn(),w=vi.fn(),b=vi.fn(e=>({...k,getDeviceId:vi.fn().mockReturnValue(e.deviceId),getDependencies:e.deviceApduSender.getDependencies,setDependencies:e.deviceApduSender.setDependencies}));const i=vi.fn(e=>({...M,getDependencies:()=>e.dependencies,setDependencies:n=>e.dependencies=n}));c=new O.NodeHidTransport(E,()=>U,w,f,b,i)}beforeEach(()=>{H(),vi.useFakeTimers()}),afterEach(()=>{T(v),vi.restoreAllMocks(),vi.clearAllMocks(),vi.useRealTimers()});const m=(i,e)=>{c.startDiscovering().subscribe({next:i,error:e})};describe("isSupported",()=>{it("should always support the transport",()=>{expect(c.isSupported()).toBe(!0)})}),describe("getIdentifier",()=>{it("should return NODE-HID identifier",()=>{expect(c.getIdentifier()).toBe("NODE-HID")})}),describe("startDiscovering",()=>{E.getAllDeviceModels().flatMap(e=>[{testTitle:`should emit device when discovered (${e.productName})`,hidDevice:(0,r.nodeHidDeviceStubBuilder)({productId:e.usbProductId<<8,product:e.productName}),expectedDeviceModel:e},{testTitle:`should emit device when discovered (${e.productName}, bootloader)`,hidDevice:(0,r.nodeHidDeviceStubBuilder)({productId:e.bootloaderUsbProductId,product:e.productName}),expectedDeviceModel:e}]).forEach(e=>{it(e.testTitle,()=>new Promise((n,t)=>{d.mockResolvedValueOnce([e.hidDevice]),m(o=>{try{expect(o).toEqual(expect.objectContaining({deviceModel:e.expectedDeviceModel})),n()}catch(a){t(a)}},o=>{t(o)})}))}),it("should emit multiple devices if several are connected",()=>new Promise((e,n)=>{d.mockResolvedValueOnce([h,(0,r.nodeHidDeviceStubBuilder)({productId:20497,product:"Ledger Nano S Plus",path:"/dev/hidraw1"})]);let t=0;m(o=>{try{switch(t){case 0:expect(o).toEqual(expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X,productName:"Ledger Nano X",usbProductId:64})}));break;case 1:expect(o).toEqual(expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_SP,productName:"Ledger Nano S Plus",usbProductId:80})})),e();break}t++}catch(a){n(a)}},o=>{n(o)})})),it("should ignore non-APDU ledger interfaces on darwin",async()=>{T("darwin");const e=(0,r.nodeHidDeviceStubBuilder)({path:"/dev/hidraw0",interface:2,usagePage:61904}),n=(0,r.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1",interface:0,usagePage:65440});d.mockResolvedValueOnce([e,n]),d.mockResolvedValue([e,n]);const t=await(0,u.lastValueFrom)(c.startDiscovering().pipe((0,u.toArray)()));expect(t).toHaveLength(1),expect(t[0].hidDevice.path).toBe("/dev/hidraw1")}),it("should ignore non-APDU ledger interfaces on win32",async()=>{T("win32");const e=(0,r.nodeHidDeviceStubBuilder)({path:"\\\\?\\hid#vid_2c97&pid_0001&mi_01#generic",interface:2,usagePage:61904}),n=(0,r.nodeHidDeviceStubBuilder)({path:"\\\\?\\hid#vid_2c97&pid_0001&mi_00#apdu",interface:0,usagePage:65440});d.mockResolvedValueOnce([e,n]),d.mockResolvedValue([e,n]);const t=await(0,u.lastValueFrom)(c.startDiscovering().pipe((0,u.toArray)()));expect(t).toHaveLength(1),expect(t[0].hidDevice.path).toBe("\\\\?\\hid#vid_2c97&pid_0001&mi_00#apdu")}),it("should throw DeviceNotRecognizedError if the device is not recognized",()=>new Promise((e,n)=>{d.mockResolvedValueOnce([(0,r.nodeHidDeviceStubBuilder)({productId:16962})]),m(()=>{n("should not return a device")},t=>{expect(t).toBeInstanceOf(s.DeviceNotRecognizedError),e()})})),it("should emit an error if devicesAsync throws",()=>new Promise((e,n)=>{const t="devices async error";d.mockImplementationOnce(()=>{throw new Error(t)}),m(()=>{n("should not return a device")},o=>{expect(o).toBeInstanceOf(s.NoAccessibleDeviceError),expect(o).toStrictEqual(new s.NoAccessibleDeviceError(new Error(t))),e()})})),it("should emit an error if no devices are found",()=>new Promise((e,n)=>{d.mockResolvedValueOnce([]),m(t=>{n(`Should not emit any value, but emitted ${JSON.stringify(t)}`)},t=>{try{expect(t).toBeInstanceOf(s.NoAccessibleDeviceError),e()}catch(o){n(o)}})})),it("should emit the same discoveredDevice object if discovered twice in a row",async()=>{d.mockResolvedValue([h]);const e=await new Promise((t,o)=>{m(()=>t(),a=>o(a))}),n=await new Promise((t,o)=>{m(()=>t(),a=>o(a))});expect(n).toBe(e)})}),describe("destroy",()=>{it("should stop monitoring connections when destroyed",()=>{const i=vi.spyOn(AbortController.prototype,"abort");c.destroy(),expect(i).toHaveBeenCalled()})}),describe("connect",()=>{it("should throw UnknownDeviceError if no internal device",async()=>{const i={deviceId:"fake",onDisconnect:vi.fn()},e=await c.connect(i);expect(e).toStrictEqual((0,g.Left)(new s.UnknownDeviceError("Unknown device fake")))}),it("should throw OpeningConnectionError if the device cannot be opened",async()=>{const i="cannot be opened";k.setupConnection.mockRejectedValueOnce(new Error(i)),d.mockResolvedValueOnce([h]),d.mockResolvedValue([h]);const e=await(0,u.lastValueFrom)(c.startDiscovering()),n=await c.connect({deviceId:e.id,onDisconnect:vi.fn()});expect(n.isLeft()).toBe(!0),expect(n.extract()).toBeInstanceOf(s.OpeningConnectionError)}),it("should return a device if available",async()=>{d.mockResolvedValueOnce([h]),d.mockResolvedValue([h]);const i=await(0,u.lastValueFrom)(c.startDiscovering()),e=await c.connect({deviceId:i.id,onDisconnect:vi.fn()});expect(e.isRight()).toStrictEqual(!0),expect(e.extract()).toEqual(expect.objectContaining({id:i.id}))}),it("should return an existing connected device",async()=>{d.mockResolvedValueOnce([h]),d.mockResolvedValue([h]);const i=await(0,u.lastValueFrom)(c.startDiscovering());await c.connect({deviceId:i.id,onDisconnect:vi.fn()});const e=await c.connect({deviceId:i.id,onDisconnect:vi.fn()});expect(e.isRight()).toStrictEqual(!0),expect(e.extract()).toEqual(expect.objectContaining({id:i.id}))})}),describe("disconnect",()=>{it("should throw an error if the device is not connected",async()=>{const i=(0,s.connectedDeviceStubBuilder)(),e=await c.disconnect({connectedDevice:i});expect(e).toStrictEqual((0,g.Left)(new s.UnknownDeviceError(`Unknown device ${i.id}`)))}),it("should disconnect if the device is connected",async()=>{d.mockResolvedValueOnce([h]),d.mockResolvedValue([h]);const i=await(0,u.lastValueFrom)(c.startDiscovering()),e=await c.connect({deviceId:i.id,onDisconnect:vi.fn()});expect(e.isRight()).toStrictEqual(!0);const n=await c.disconnect({connectedDevice:e.unsafeCoerce()});expect(n).toStrictEqual((0,g.Right)(void 0))}),it("should call disconnect handler if a connected device is unplugged",async()=>{let i=vi.fn();b.mockImplementationOnce(S=>(i=S.onTerminated,{...k,getDeviceId:vi.fn().mockReturnValue(S.deviceId)}));const e=(0,r.nodeHidDeviceStubBuilder)({path:"/dev/hidraw0"}),n=(0,r.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1",productId:20497});d.mockResolvedValueOnce([e,n]),d.mockResolvedValue([e,n]);const t=await(0,u.lastValueFrom)(c.startDiscovering().pipe((0,u.toArray)()));expect(t.length).toStrictEqual(2);const o=vi.fn(),a=await c.connect({deviceId:t[0].id,onDisconnect:o}),p=vi.fn(),D=await c.connect({deviceId:t[1].id,onDisconnect:p});expect(a.isRight()).toStrictEqual(!0),expect(D.isRight()).toStrictEqual(!0),i(),expect(o).toHaveBeenCalled(),expect(p).not.toHaveBeenCalled()}),it("should call disconnect handler if a connected device is unplugged while reconnecting",async()=>{let i=vi.fn(),e=vi.fn();b.mockImplementationOnce(A=>(i=A.onTerminated,e=A.tryToReconnect,{...k,getDeviceId:vi.fn().mockReturnValue(A.deviceId)}));const n=(0,r.nodeHidDeviceStubBuilder)({path:"/dev/hidraw0"}),t=(0,r.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1",productId:20497});d.mockResolvedValueOnce([n,t]),d.mockResolvedValue([n,t]);const o=await(0,u.lastValueFrom)(c.startDiscovering().pipe((0,u.toArray)()));expect(o.length).toStrictEqual(2);const a=vi.fn(),p=await c.connect({deviceId:o[0].id,onDisconnect:a}),D=vi.fn(),S=await c.connect({deviceId:o[1].id,onDisconnect:D});expect(p.isRight()).toStrictEqual(!0),expect(S.isRight()).toStrictEqual(!0),e(),i(),expect(a).toHaveBeenCalled(),expect(D).not.toHaveBeenCalled()})}),describe("reconnect",()=>{it("should stop disconnection if reconnection happens",()=>new Promise((i,e)=>{const n=vi.fn();let t=vi.fn();const o=(0,r.nodeHidDeviceStubBuilder)({path:"/dev/hidraw0"}),a=(0,r.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1"});d.mockResolvedValueOnce([o]),d.mockResolvedValue([o,a]),b.mockImplementationOnce(p=>(t=p.tryToReconnect,{...k,getDeviceId:vi.fn().mockReturnValue(p.deviceId),getDependencies:p.deviceApduSender.getDependencies,setDependencies:p.deviceApduSender.setDependencies})),m(async p=>{try{await c.connect({deviceId:p.id,onDisconnect:n}),C(11415,64),await l(),expect(I).toHaveBeenCalled(),t(),vi.advanceTimersByTime(x.RECONNECT_DEVICE_TIMEOUT/3),y(11415,64),await l(),expect(R).toHaveBeenCalled(),vi.advanceTimersByTime(x.RECONNECT_DEVICE_TIMEOUT),expect(n).not.toHaveBeenCalled(),i()}catch(D){e(D)}})})),it("should be able to reconnect twice in a row if the device is unplugged and replugged twice",()=>new Promise((i,e)=>{const n=vi.fn();let t=vi.fn();const o=(0,r.nodeHidDeviceStubBuilder)({path:"/dev/hidraw0"}),a=(0,r.nodeHidDeviceStubBuilder)({path:"/dev/hidraw1"}),p=(0,r.nodeHidDeviceStubBuilder)({path:"/dev/hidraw2"});d.mockResolvedValueOnce([o]),d.mockResolvedValue([o,a,p]),b.mockImplementationOnce(D=>(t=D.tryToReconnect,{...k,getDeviceId:vi.fn().mockReturnValue(D.deviceId),getDependencies:D.deviceApduSender.getDependencies,setDependencies:D.deviceApduSender.setDependencies})),m(async D=>{await c.connect({deviceId:D.id,onDisconnect:n});try{C(11415,64),await l(),expect(I).toHaveBeenCalled(),t(),vi.advanceTimersByTime(x.RECONNECT_DEVICE_TIMEOUT/3),y(11415,64),await l(),expect(R).toHaveBeenCalled(),vi.advanceTimersByTime(x.RECONNECT_DEVICE_TIMEOUT),expect(n).not.toHaveBeenCalled(),C(11415,64),await l(),expect(I).toHaveBeenCalled(),t(),vi.advanceTimersByTime(x.RECONNECT_DEVICE_TIMEOUT/3),y(11415,64),await l(),expect(R).toHaveBeenCalled(),vi.advanceTimersByTime(x.RECONNECT_DEVICE_TIMEOUT),expect(n).not.toHaveBeenCalled(),i()}catch(S){e(S)}})}))}),describe("listenToAvailableDevices",()=>{it("should emit the devices already connected before listening",async()=>{const i=(0,r.nodeHidDeviceStubBuilder)();d.mockResolvedValue([i]);const e=vi.fn(),n=vi.fn();let t=[];c.listenToAvailableDevices().subscribe({next:o=>{t=o},complete:e,error:n}),await l(),expect(t).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X})})]),expect(e).not.toHaveBeenCalled(),expect(n).not.toHaveBeenCalled()}),it("should emit the new list of devices after connection and disconnection events",async()=>{H();const i=(0,r.nodeHidDeviceStubBuilder)({productId:E.getDeviceModel({id:s.DeviceModelId.NANO_X}).usbProductId<<8,path:"/dev/hidraw0"}),e=(0,r.nodeHidDeviceStubBuilder)({productId:E.getDeviceModel({id:s.DeviceModelId.STAX}).usbProductId<<8,path:"/dev/hidraw1"});d.mockResolvedValue([i]);const n=vi.fn(),t=vi.fn();let o=[];c.listenToAvailableDevices().subscribe({next:a=>{o=a},complete:n,error:t}),await l(),expect(o).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X})})]),d.mockResolvedValue([i,e]),y(11415,96),await l(),expect(o).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X})}),expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.STAX})})]),d.mockResolvedValue([e]),C(11415,64),await l(),expect(o).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.STAX})})]),expect(n).not.toHaveBeenCalled(),expect(t).not.toHaveBeenCalled()}),it("should preserve DeviceId in case the device has been disconnected and reconnected before the timeout",async()=>{const i=(0,r.nodeHidDeviceStubBuilder)();d.mockResolvedValue([i]);const e=vi.fn(),n=vi.fn();let t=[];c.listenToAvailableDevices().subscribe({next:a=>{t=a},complete:e,error:n}),await l();const o=t[0]?.id;expect(o).toBeTruthy(),expect(t[0]?.deviceModel?.id).toBe(s.DeviceModelId.NANO_X),await c.connect({deviceId:t[0].id,onDisconnect:vi.fn()}),await l(),d.mockResolvedValue([]),C(11415,64),await l(),expect(t).toEqual([]),d.mockResolvedValue([i]),y(11415,64),await l(),expect(t).toEqual([expect.objectContaining({deviceModel:expect.objectContaining({id:s.DeviceModelId.NANO_X})})]),expect(t[0]?.id).toBeTruthy(),expect(t[0]?.id).toBe(o)})}),describe("USB connection events",()=>{it("should register usb attach and detach listeners on construction",()=>{expect(N).toHaveBeenCalledWith("attach",expect.any(Function)),expect(N).toHaveBeenCalledWith("detach",expect.any(Function))}),it("should ignore non-Ledger USB devices",async()=>{d.mockResolvedValue([h]);let i=[];c.listenToAvailableDevices().subscribe({next:e=>{i=e}}),await l(),expect(i.length).toBe(1),y(4660,22136),await l(),expect(i.length).toBe(1)})})});
2
2
  //# sourceMappingURL=NodeHidTransport.test.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/api/transport/NodeHidTransport.test.ts"],
4
- "sourcesContent": ["/* eslint @typescript-eslint/consistent-type-imports: off */\nimport {\n type ApduReceiverServiceFactory,\n type ApduResponse,\n type ApduSenderServiceFactory,\n connectedDeviceStubBuilder,\n DeviceConnectionStateMachine,\n type DeviceConnectionStateMachineParams,\n type DeviceModel,\n DeviceModelId,\n DeviceNotRecognizedError,\n type LoggerPublisherService,\n type LoggerSubscriberService,\n NoAccessibleDeviceError,\n OpeningConnectionError,\n StaticDeviceModelDataSource,\n type TransportDeviceModel,\n type TransportDiscoveredDevice,\n UnknownDeviceError,\n} from \"@ledgerhq/device-management-kit\";\nimport type { Device as NodeHIDDevice } from \"node-hid\";\nimport { Left, Right } from \"purify-ts\";\nimport { lastValueFrom, toArray } from \"rxjs\";\n\nimport { RECONNECT_DEVICE_TIMEOUT } from \"@api/data/NodeHidConfig\";\nimport { nodeHidDeviceStubBuilder } from \"@api/model/HIDDevice.stub\";\n\nimport {\n NodeHidApduSender,\n type NodeHidApduSenderDependencies,\n} from \"./NodeHidApduSender\";\nimport { NodeHidTransport } from \"./NodeHidTransport\";\n\n// Mock node-hid module\nconst mockDevicesAsync = vi.fn();\nvi.mock(\"node-hid\", () => ({\n devicesAsync: (...args: unknown[]) => mockDevicesAsync(...args),\n HIDAsync: {\n open: vi.fn(),\n },\n}));\n\n// Mock usb module\nconst mockUsbOn = vi.fn();\nconst mockUsbAttachCallbacks: ((device: unknown) => void)[] = [];\nconst mockUsbDetachCallbacks: ((device: unknown) => void)[] = [];\n\nvi.mock(\"usb\", () => ({\n usb: {\n on: (event: string, callback: (device: unknown) => void) => {\n mockUsbOn(event, callback);\n if (event === \"attach\") {\n mockUsbAttachCallbacks.push(callback);\n } else if (event === \"detach\") {\n mockUsbDetachCallbacks.push(callback);\n }\n },\n removeAllListeners: vi.fn(),\n unrefHotplugEvents: vi.fn(),\n },\n Device: class {},\n}));\n\nclass LoggerPublisherServiceStub implements LoggerPublisherService {\n constructor(subscribers: LoggerSubscriberService[], tag: string) {\n this.subscribers = subscribers;\n this.tag = tag;\n }\n subscribers: LoggerSubscriberService[] = [];\n tag: string = \"\";\n error = vi.fn();\n warn = vi.fn();\n info = vi.fn();\n debug = vi.fn();\n}\n\n// Our StaticDeviceModelDataSource can directly be used in our unit tests\nconst usbDeviceModelDataSource = new StaticDeviceModelDataSource();\nconst logger = new LoggerPublisherServiceStub([], \"node-hid\");\n\nconst stubDevice: NodeHIDDevice = nodeHidDeviceStubBuilder();\n\n/**\n * Flushes all pending promises\n */\nconst flushPromises = async () => {\n const timers = await vi.importActual<typeof import(\"timers\")>(\"timers\");\n return new Promise(timers.setImmediate);\n};\n\n/**\n * Helper to create a USB device event object matching the usb module's Device type\n */\nconst createUsbDevice = (vendorId: number, productId: number) => ({\n deviceDescriptor: {\n idVendor: vendorId,\n idProduct: productId,\n },\n});\n\n/**\n * Emit a USB attach event\n */\nconst emitUsbAttachEvent = (vendorId: number, productId: number) => {\n const device = createUsbDevice(vendorId, productId);\n mockUsbAttachCallbacks.forEach((callback) => callback(device));\n};\n\n/**\n * Emit a USB detach event\n */\nconst emitUsbDetachEvent = (vendorId: number, productId: number) => {\n const device = createUsbDevice(vendorId, productId);\n mockUsbDetachCallbacks.forEach((callback) => callback(device));\n};\n\ndescribe(\"NodeHidTransport\", () => {\n let transport: NodeHidTransport;\n let apduReceiverServiceFactoryStub: ApduReceiverServiceFactory;\n let apduSenderServiceFactoryStub: ApduSenderServiceFactory;\n\n let mockDeviceConnectionStateMachineFactory = vi.fn();\n const mockEventDeviceConnected = vi.fn();\n const mockEventDeviceDisconnected = vi.fn();\n\n const mockDeviceApduSender = {\n sendApdu: vi.fn().mockResolvedValue(\n Right({\n data: new Uint8Array(),\n statusCode: new Uint8Array([0x90, 0x00]),\n } as ApduResponse),\n ),\n getDependencies: vi.fn().mockReturnValue({ device: stubDevice }),\n setDependencies: vi.fn(),\n closeConnection: vi.fn(),\n setupConnection: vi.fn(),\n };\n\n const mockDeviceConnectionStateMachine = {\n getDependencies: vi.fn().mockReturnValue({ device: stubDevice }),\n setDependencies: vi.fn(),\n getDeviceId: vi.fn(),\n sendApdu: vi.fn().mockResolvedValue(\n Right({\n data: new Uint8Array(),\n statusCode: new Uint8Array([0x90, 0x00]),\n } as ApduResponse),\n ),\n setupConnection: vi.fn(),\n eventDeviceConnected: mockEventDeviceConnected,\n eventDeviceDisconnected: mockEventDeviceDisconnected,\n closeConnection: vi.fn(),\n };\n\n function initializeTransport() {\n // Clear callbacks before reinitializing\n mockUsbAttachCallbacks.length = 0;\n mockUsbDetachCallbacks.length = 0;\n\n apduReceiverServiceFactoryStub = vi.fn();\n apduSenderServiceFactoryStub = vi.fn();\n mockDeviceConnectionStateMachineFactory = vi.fn(\n (\n params: DeviceConnectionStateMachineParams<NodeHidApduSenderDependencies>,\n ) => {\n return {\n ...mockDeviceConnectionStateMachine,\n getDeviceId: vi.fn().mockReturnValue(params.deviceId),\n getDependencies: params.deviceApduSender.getDependencies,\n setDependencies: params.deviceApduSender.setDependencies,\n } as unknown as DeviceConnectionStateMachine<NodeHidApduSenderDependencies>;\n },\n );\n const mockDeviceApduSenderFactory = vi.fn((params) => {\n return {\n ...mockDeviceApduSender,\n getDependencies: () => params.dependencies,\n setDependencies: (dependencies: NodeHidApduSenderDependencies) =>\n (params.dependencies = dependencies),\n } as unknown as NodeHidApduSender;\n });\n transport = new NodeHidTransport(\n usbDeviceModelDataSource,\n () => logger,\n apduSenderServiceFactoryStub,\n apduReceiverServiceFactoryStub,\n mockDeviceConnectionStateMachineFactory,\n mockDeviceApduSenderFactory,\n );\n }\n\n beforeEach(() => {\n initializeTransport();\n vi.useFakeTimers();\n });\n\n afterEach(() => {\n vi.restoreAllMocks();\n vi.clearAllMocks();\n vi.useRealTimers();\n });\n\n const discoverDevice = (\n onSuccess: (discoveredDevice: TransportDiscoveredDevice) => void,\n onError?: (error: unknown) => void,\n ) => {\n transport.startDiscovering().subscribe({\n next: onSuccess,\n error: onError,\n });\n };\n\n describe(\"isSupported\", () => {\n it(\"should always support the transport\", () => {\n expect(transport.isSupported()).toBe(true);\n });\n });\n\n describe(\"getIdentifier\", () => {\n it(\"should return NODE-HID identifier\", () => {\n expect(transport.getIdentifier()).toBe(\"NODE-HID\");\n });\n });\n\n describe(\"startDiscovering\", () => {\n const testCases = usbDeviceModelDataSource\n .getAllDeviceModels()\n .flatMap((deviceModel) => {\n return [\n {\n testTitle: `should emit device when discovered (${deviceModel.productName})`,\n hidDevice: nodeHidDeviceStubBuilder({\n productId: deviceModel.usbProductId << 8,\n product: deviceModel.productName,\n }),\n expectedDeviceModel: deviceModel,\n },\n {\n testTitle: `should emit device when discovered (${deviceModel.productName}, bootloader)`,\n hidDevice: nodeHidDeviceStubBuilder({\n productId: deviceModel.bootloaderUsbProductId,\n product: deviceModel.productName,\n }),\n expectedDeviceModel: deviceModel,\n },\n ];\n });\n\n testCases.forEach((testCase) => {\n it(\n testCase.testTitle,\n () =>\n new Promise<void>((resolve, reject) => {\n mockDevicesAsync.mockResolvedValueOnce([testCase.hidDevice]);\n\n discoverDevice(\n (discoveredDevice) => {\n try {\n expect(discoveredDevice).toEqual(\n expect.objectContaining({\n deviceModel: testCase.expectedDeviceModel,\n }),\n );\n\n resolve();\n } catch (expectError) {\n reject(expectError as Error);\n }\n },\n (error) => {\n reject(error as Error);\n },\n );\n }),\n );\n });\n\n it(\"should emit multiple devices if several are connected\", () =>\n new Promise<void>((resolve, reject) => {\n mockDevicesAsync.mockResolvedValueOnce([\n stubDevice,\n nodeHidDeviceStubBuilder({\n productId: 0x5011,\n product: \"Ledger Nano S Plus\",\n path: \"/dev/hidraw1\",\n }),\n ]);\n\n let count = 0;\n discoverDevice(\n (discoveredDevice) => {\n try {\n switch (count) {\n case 0:\n expect(discoveredDevice).toEqual(\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.NANO_X,\n productName: \"Ledger Nano X\",\n usbProductId: 0x40,\n }) as DeviceModel,\n }),\n );\n break;\n case 1:\n expect(discoveredDevice).toEqual(\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.NANO_SP,\n productName: \"Ledger Nano S Plus\",\n usbProductId: 0x50,\n }) as DeviceModel,\n }),\n );\n\n resolve();\n break;\n }\n\n count++;\n } catch (expectError) {\n reject(expectError as Error);\n }\n },\n (error) => {\n reject(error as Error);\n },\n );\n }));\n\n it(\"should throw DeviceNotRecognizedError if the device is not recognized\", () =>\n new Promise<void>((resolve, reject) => {\n mockDevicesAsync.mockResolvedValueOnce([\n nodeHidDeviceStubBuilder({\n productId: 0x4242,\n }),\n ]);\n\n discoverDevice(\n () => {\n reject(\"should not return a device\");\n },\n (error) => {\n expect(error).toBeInstanceOf(DeviceNotRecognizedError);\n resolve();\n },\n );\n }));\n\n it(\"should emit an error if devicesAsync throws\", () =>\n new Promise<void>((resolve, reject) => {\n const message = \"devices async error\";\n mockDevicesAsync.mockImplementationOnce(() => {\n throw new Error(message);\n });\n\n discoverDevice(\n () => {\n reject(\"should not return a device\");\n },\n (error) => {\n expect(error).toBeInstanceOf(NoAccessibleDeviceError);\n expect(error).toStrictEqual(\n new NoAccessibleDeviceError(new Error(message)),\n );\n resolve();\n },\n );\n }));\n\n it(\"should emit an error if no devices are found\", () =>\n new Promise<void>((resolve, reject) => {\n mockDevicesAsync.mockResolvedValueOnce([]);\n\n discoverDevice(\n (discoveredDevice) => {\n reject(\n `Should not emit any value, but emitted ${JSON.stringify(\n discoveredDevice,\n )}`,\n );\n },\n (error) => {\n try {\n expect(error).toBeInstanceOf(NoAccessibleDeviceError);\n resolve();\n } catch (expectError) {\n reject(expectError as Error);\n }\n },\n );\n }));\n\n it(\"should emit the same discoveredDevice object if discovered twice in a row\", async () => {\n mockDevicesAsync.mockResolvedValue([stubDevice]);\n\n const firstDiscoveredDevice = await new Promise<void>(\n (resolve, reject) => {\n discoverDevice(\n () => resolve(),\n (err) => reject(err),\n );\n },\n );\n const secondDiscoveredDevice = await new Promise<void>(\n (resolve, reject) => {\n discoverDevice(\n () => resolve(),\n (err) => reject(err),\n );\n },\n );\n expect(secondDiscoveredDevice).toBe(firstDiscoveredDevice);\n });\n });\n\n describe(\"destroy\", () => {\n it(\"should stop monitoring connections when destroyed\", () => {\n const abortSpy = vi.spyOn(AbortController.prototype, \"abort\");\n\n transport.destroy();\n\n expect(abortSpy).toHaveBeenCalled();\n });\n });\n\n describe(\"connect\", () => {\n it(\"should throw UnknownDeviceError if no internal device\", async () => {\n const connectParams = {\n deviceId: \"fake\",\n onDisconnect: vi.fn(),\n };\n\n const connect = await transport.connect(connectParams);\n\n expect(connect).toStrictEqual(\n Left(new UnknownDeviceError(\"Unknown device fake\")),\n );\n });\n\n it(\"should throw OpeningConnectionError if the device cannot be opened\", async () => {\n const message = \"cannot be opened\";\n // Mock the DeviceConnectionStateMachine's setupConnection to reject\n mockDeviceConnectionStateMachine.setupConnection.mockRejectedValueOnce(\n new Error(message),\n );\n mockDevicesAsync.mockResolvedValueOnce([stubDevice]);\n mockDevicesAsync.mockResolvedValue([stubDevice]);\n\n const discoveredDevice = await lastValueFrom(\n transport.startDiscovering(),\n );\n const connected = await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect: vi.fn(),\n });\n expect(connected.isLeft()).toBe(true);\n expect(connected.extract()).toBeInstanceOf(OpeningConnectionError);\n });\n\n it(\"should return a device if available\", async () => {\n mockDevicesAsync.mockResolvedValueOnce([stubDevice]);\n mockDevicesAsync.mockResolvedValue([stubDevice]);\n\n const discoveredDevice = await lastValueFrom(\n transport.startDiscovering(),\n );\n const connected = await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect: vi.fn(),\n });\n expect(connected.isRight()).toStrictEqual(true);\n expect(connected.extract()).toEqual(\n expect.objectContaining({ id: discoveredDevice.id }),\n );\n });\n\n it(\"should return an existing connected device\", async () => {\n mockDevicesAsync.mockResolvedValueOnce([stubDevice]);\n mockDevicesAsync.mockResolvedValue([stubDevice]);\n\n const discoveredDevice = await lastValueFrom(\n transport.startDiscovering(),\n );\n await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect: vi.fn(),\n });\n const connected = await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect: vi.fn(),\n });\n expect(connected.isRight()).toStrictEqual(true);\n expect(connected.extract()).toEqual(\n expect.objectContaining({ id: discoveredDevice.id }),\n );\n });\n });\n\n describe(\"disconnect\", () => {\n it(\"should throw an error if the device is not connected\", async () => {\n // given\n const connectedDevice = connectedDeviceStubBuilder();\n\n // when\n const disconnect = await transport.disconnect({\n connectedDevice,\n });\n\n expect(disconnect).toStrictEqual(\n Left(new UnknownDeviceError(`Unknown device ${connectedDevice.id}`)),\n );\n });\n\n it(\"should disconnect if the device is connected\", async () => {\n mockDevicesAsync.mockResolvedValueOnce([stubDevice]);\n mockDevicesAsync.mockResolvedValue([stubDevice]);\n\n const discoveredDevice = await lastValueFrom(\n transport.startDiscovering(),\n );\n const connected = await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect: vi.fn(),\n });\n expect(connected.isRight()).toStrictEqual(true);\n const result = await transport.disconnect({\n connectedDevice: connected.unsafeCoerce(),\n });\n expect(result).toStrictEqual(Right(undefined));\n });\n\n it(\"should call disconnect handler if a connected device is unplugged\", async () => {\n // Get onTerminated for the first connection only\n let onTerminated1 = vi.fn();\n mockDeviceConnectionStateMachineFactory.mockImplementationOnce(\n (params) => {\n onTerminated1 = params.onTerminated;\n return {\n ...mockDeviceConnectionStateMachine,\n getDeviceId: vi.fn().mockReturnValue(params.deviceId),\n } as unknown as DeviceConnectionStateMachine<NodeHidApduSenderDependencies>;\n },\n );\n\n // Add 2 discoverable devices\n const hidDevice1 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw0\" });\n const hidDevice2 = nodeHidDeviceStubBuilder({\n path: \"/dev/hidraw1\",\n productId: 0x5011,\n });\n mockDevicesAsync.mockResolvedValueOnce([hidDevice1, hidDevice2]);\n mockDevicesAsync.mockResolvedValue([hidDevice1, hidDevice2]);\n\n // Connect the 2 devices\n const discoveredDevices = await lastValueFrom(\n transport.startDiscovering().pipe(toArray()),\n );\n expect(discoveredDevices.length).toStrictEqual(2);\n const onDisconnect1 = vi.fn();\n const connected1 = await transport.connect({\n deviceId: discoveredDevices[0]!.id,\n onDisconnect: onDisconnect1,\n });\n const onDisconnect2 = vi.fn();\n const connected2 = await transport.connect({\n deviceId: discoveredDevices[1]!.id,\n onDisconnect: onDisconnect2,\n });\n expect(connected1.isRight()).toStrictEqual(true);\n expect(connected2.isRight()).toStrictEqual(true);\n\n // unplug the first device\n onTerminated1();\n expect(onDisconnect1).toHaveBeenCalled();\n expect(onDisconnect2).not.toHaveBeenCalled();\n });\n\n it(\"should call disconnect handler if a connected device is unplugged while reconnecting\", async () => {\n // Get onTerminated for the first connection only\n let onTerminated1 = vi.fn();\n let tryToReconnect1 = vi.fn();\n mockDeviceConnectionStateMachineFactory.mockImplementationOnce(\n (params) => {\n onTerminated1 = params.onTerminated;\n tryToReconnect1 = params.tryToReconnect;\n return {\n ...mockDeviceConnectionStateMachine,\n getDeviceId: vi.fn().mockReturnValue(params.deviceId),\n } as unknown as DeviceConnectionStateMachine<NodeHidApduSenderDependencies>;\n },\n );\n\n // Add 2 discoverable devices\n const hidDevice1 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw0\" });\n const hidDevice2 = nodeHidDeviceStubBuilder({\n path: \"/dev/hidraw1\",\n productId: 0x5011,\n });\n mockDevicesAsync.mockResolvedValueOnce([hidDevice1, hidDevice2]);\n mockDevicesAsync.mockResolvedValue([hidDevice1, hidDevice2]);\n\n // Connect the 2 devices\n const discoveredDevices = await lastValueFrom(\n transport.startDiscovering().pipe(toArray()),\n );\n expect(discoveredDevices.length).toStrictEqual(2);\n const onDisconnect1 = vi.fn();\n const connected1 = await transport.connect({\n deviceId: discoveredDevices[0]!.id,\n onDisconnect: onDisconnect1,\n });\n const onDisconnect2 = vi.fn();\n const connected2 = await transport.connect({\n deviceId: discoveredDevices[1]!.id,\n onDisconnect: onDisconnect2,\n });\n expect(connected1.isRight()).toStrictEqual(true);\n expect(connected2.isRight()).toStrictEqual(true);\n\n // Try to reconnect the first device\n tryToReconnect1();\n\n // unplug the first device\n onTerminated1();\n expect(onDisconnect1).toHaveBeenCalled();\n expect(onDisconnect2).not.toHaveBeenCalled();\n });\n });\n\n describe(\"reconnect\", () => {\n it(\"should stop disconnection if reconnection happens\", () =>\n new Promise<void>((resolve, reject) => {\n // given\n const onDisconnect = vi.fn();\n let tryToReconnect = vi.fn();\n\n const hidDevice1 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw0\" });\n const hidDevice2 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw1\" });\n\n mockDevicesAsync.mockResolvedValueOnce([hidDevice1]);\n mockDevicesAsync.mockResolvedValue([hidDevice1, hidDevice2]);\n mockDeviceConnectionStateMachineFactory.mockImplementationOnce(\n (params) => {\n tryToReconnect = params.tryToReconnect;\n return {\n ...mockDeviceConnectionStateMachine,\n getDeviceId: vi.fn().mockReturnValue(params.deviceId),\n getDependencies: params.deviceApduSender.getDependencies,\n setDependencies: params.deviceApduSender.setDependencies,\n } as unknown as DeviceConnectionStateMachine<NodeHidApduSenderDependencies>;\n },\n );\n\n discoverDevice(async (discoveredDevice) => {\n try {\n await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect,\n });\n\n /* Disconnection */\n emitUsbDetachEvent(0x2c97, 0x40);\n await flushPromises();\n expect(mockEventDeviceDisconnected).toHaveBeenCalled();\n\n tryToReconnect();\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT / 3);\n\n /* Reconnection */\n emitUsbAttachEvent(0x2c97, 0x40);\n\n await flushPromises();\n expect(mockEventDeviceConnected).toHaveBeenCalled();\n\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT);\n expect(onDisconnect).not.toHaveBeenCalled();\n resolve();\n } catch (error) {\n reject(error as Error);\n }\n });\n }));\n\n it(\"should be able to reconnect twice in a row if the device is unplugged and replugged twice\", () =>\n new Promise<void>((resolve, reject) => {\n // given\n const onDisconnect = vi.fn();\n let tryToReconnect = vi.fn();\n\n const hidDevice1 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw0\" });\n const hidDevice2 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw1\" });\n const hidDevice3 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw2\" });\n\n mockDevicesAsync.mockResolvedValueOnce([hidDevice1]);\n mockDevicesAsync.mockResolvedValue([\n hidDevice1,\n hidDevice2,\n hidDevice3,\n ]);\n mockDeviceConnectionStateMachineFactory.mockImplementationOnce(\n (params) => {\n tryToReconnect = params.tryToReconnect;\n return {\n ...mockDeviceConnectionStateMachine,\n getDeviceId: vi.fn().mockReturnValue(params.deviceId),\n getDependencies: params.deviceApduSender.getDependencies,\n setDependencies: params.deviceApduSender.setDependencies,\n } as unknown as DeviceConnectionStateMachine<NodeHidApduSenderDependencies>;\n },\n );\n\n // when\n discoverDevice(async (discoveredDevice) => {\n await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect,\n });\n try {\n /* First disconnection */\n emitUsbDetachEvent(0x2c97, 0x40);\n await flushPromises();\n expect(mockEventDeviceDisconnected).toHaveBeenCalled();\n tryToReconnect();\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT / 3);\n\n /* First reconnection */\n emitUsbAttachEvent(0x2c97, 0x40);\n\n await flushPromises();\n expect(mockEventDeviceConnected).toHaveBeenCalled();\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT);\n expect(onDisconnect).not.toHaveBeenCalled();\n\n /* Second disconnection */\n emitUsbDetachEvent(0x2c97, 0x40);\n await flushPromises();\n expect(mockEventDeviceDisconnected).toHaveBeenCalled();\n tryToReconnect();\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT / 3);\n\n /* Second reconnection */\n emitUsbAttachEvent(0x2c97, 0x40);\n\n await flushPromises();\n expect(mockEventDeviceConnected).toHaveBeenCalled();\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT);\n expect(onDisconnect).not.toHaveBeenCalled();\n\n resolve();\n } catch (error) {\n reject(error as Error);\n }\n });\n }));\n });\n\n describe(\"listenToAvailableDevices\", () => {\n it(\"should emit the devices already connected before listening\", async () => {\n // given\n const hidDevice = nodeHidDeviceStubBuilder();\n mockDevicesAsync.mockResolvedValue([hidDevice]);\n\n const onComplete = vi.fn();\n const onError = vi.fn();\n\n let observedDevices: TransportDiscoveredDevice[] = [];\n // when\n transport.listenToAvailableDevices().subscribe({\n next: (knownDevices) => {\n observedDevices = knownDevices;\n },\n complete: onComplete,\n error: onError,\n });\n\n await flushPromises();\n\n expect(observedDevices).toEqual([\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.NANO_X,\n }) as TransportDeviceModel,\n }),\n ]);\n expect(onComplete).not.toHaveBeenCalled();\n expect(onError).not.toHaveBeenCalled();\n });\n\n it(\"should emit the new list of devices after connection and disconnection events\", async () => {\n initializeTransport();\n // given\n const hidDevice1 = nodeHidDeviceStubBuilder({\n productId:\n usbDeviceModelDataSource.getDeviceModel({\n id: DeviceModelId.NANO_X,\n }).usbProductId << 8,\n path: \"/dev/hidraw0\",\n });\n const hidDevice2 = nodeHidDeviceStubBuilder({\n productId:\n usbDeviceModelDataSource.getDeviceModel({ id: DeviceModelId.STAX })\n .usbProductId << 8,\n path: \"/dev/hidraw1\",\n });\n mockDevicesAsync.mockResolvedValue([hidDevice1]);\n\n const onComplete = vi.fn();\n const onError = vi.fn();\n\n let observedDevices: TransportDiscoveredDevice[] = [];\n // when\n transport.listenToAvailableDevices().subscribe({\n next: (knownDevices) => {\n observedDevices = knownDevices;\n },\n complete: onComplete,\n error: onError,\n });\n\n await flushPromises();\n\n expect(observedDevices).toEqual([\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.NANO_X,\n }) as TransportDeviceModel,\n }),\n ]);\n\n // When a new device is connected\n mockDevicesAsync.mockResolvedValue([hidDevice1, hidDevice2]);\n emitUsbAttachEvent(0x2c97, 0x60);\n await flushPromises();\n\n expect(observedDevices).toEqual([\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.NANO_X,\n }) as TransportDeviceModel,\n }),\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.STAX,\n }) as TransportDeviceModel,\n }),\n ]);\n\n // When a device is disconnected\n mockDevicesAsync.mockResolvedValue([hidDevice2]);\n emitUsbDetachEvent(0x2c97, 0x40);\n await flushPromises();\n\n expect(observedDevices).toEqual([\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.STAX,\n }) as TransportDeviceModel,\n }),\n ]);\n\n expect(onComplete).not.toHaveBeenCalled();\n expect(onError).not.toHaveBeenCalled();\n });\n\n it(\"should preserve DeviceId in case the device has been disconnected and reconnected before the timeout\", async () => {\n // given\n const hidDevice = nodeHidDeviceStubBuilder();\n\n mockDevicesAsync.mockResolvedValue([hidDevice]);\n\n const onComplete = vi.fn();\n const onError = vi.fn();\n let observedDevices: TransportDiscoveredDevice[] = [];\n // when\n transport.listenToAvailableDevices().subscribe({\n next: (knownDevices) => {\n observedDevices = knownDevices;\n },\n complete: onComplete,\n error: onError,\n });\n\n await flushPromises();\n\n const firstObservedDeviceId = observedDevices[0]?.id;\n expect(firstObservedDeviceId).toBeTruthy();\n expect(observedDevices[0]?.deviceModel?.id).toBe(DeviceModelId.NANO_X);\n\n // Start a connection with the device\n await transport.connect({\n deviceId: observedDevices[0]!.id,\n onDisconnect: vi.fn(),\n });\n await flushPromises();\n\n // When the device is disconnected\n mockDevicesAsync.mockResolvedValue([]);\n emitUsbDetachEvent(0x2c97, 0x40);\n await flushPromises();\n\n expect(observedDevices).toEqual([]);\n\n // When the device is reconnected\n mockDevicesAsync.mockResolvedValue([hidDevice]);\n emitUsbAttachEvent(0x2c97, 0x40);\n await flushPromises();\n\n expect(observedDevices).toEqual([\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.NANO_X,\n }) as TransportDeviceModel,\n }),\n ]);\n\n expect(observedDevices[0]?.id).toBeTruthy();\n expect(observedDevices[0]?.id).toBe(firstObservedDeviceId);\n });\n });\n\n describe(\"USB connection events\", () => {\n it(\"should register usb attach and detach listeners on construction\", () => {\n expect(mockUsbOn).toHaveBeenCalledWith(\"attach\", expect.any(Function));\n expect(mockUsbOn).toHaveBeenCalledWith(\"detach\", expect.any(Function));\n });\n\n it(\"should ignore non-Ledger USB devices\", async () => {\n mockDevicesAsync.mockResolvedValue([stubDevice]);\n\n let observedDevices: TransportDiscoveredDevice[] = [];\n transport.listenToAvailableDevices().subscribe({\n next: (knownDevices) => {\n observedDevices = knownDevices;\n },\n });\n\n await flushPromises();\n expect(observedDevices.length).toBe(1);\n\n // Emit attach event for non-Ledger device\n emitUsbAttachEvent(0x1234, 0x5678);\n await flushPromises();\n\n // Should still be 1 device\n expect(observedDevices.length).toBe(1);\n });\n });\n});\n"],
5
- "mappings": "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",
6
- "names": ["import_device_management_kit", "import_purify_ts", "import_rxjs", "import_NodeHidConfig", "import_HIDDevice", "import_NodeHidTransport", "mockDevicesAsync", "args", "mockUsbOn", "mockUsbAttachCallbacks", "mockUsbDetachCallbacks", "event", "callback", "LoggerPublisherServiceStub", "subscribers", "tag", "usbDeviceModelDataSource", "logger", "stubDevice", "flushPromises", "timers", "createUsbDevice", "vendorId", "productId", "emitUsbAttachEvent", "device", "emitUsbDetachEvent", "transport", "apduReceiverServiceFactoryStub", "apduSenderServiceFactoryStub", "mockDeviceConnectionStateMachineFactory", "mockEventDeviceConnected", "mockEventDeviceDisconnected", "mockDeviceApduSender", "mockDeviceConnectionStateMachine", "initializeTransport", "params", "mockDeviceApduSenderFactory", "dependencies", "discoverDevice", "onSuccess", "onError", "deviceModel", "testCase", "resolve", "reject", "discoveredDevice", "expectError", "error", "count", "message", "firstDiscoveredDevice", "err", "secondDiscoveredDevice", "abortSpy", "connectParams", "connect", "connected", "connectedDevice", "disconnect", "result", "onTerminated1", "hidDevice1", "hidDevice2", "discoveredDevices", "onDisconnect1", "connected1", "onDisconnect2", "connected2", "tryToReconnect1", "onDisconnect", "tryToReconnect", "hidDevice3", "hidDevice", "onComplete", "observedDevices", "knownDevices", "firstObservedDeviceId"]
4
+ "sourcesContent": ["/* eslint @typescript-eslint/consistent-type-imports: off */\nimport {\n type ApduReceiverServiceFactory,\n type ApduResponse,\n type ApduSenderServiceFactory,\n connectedDeviceStubBuilder,\n DeviceConnectionStateMachine,\n type DeviceConnectionStateMachineParams,\n type DeviceModel,\n DeviceModelId,\n DeviceNotRecognizedError,\n type LoggerPublisherService,\n type LoggerSubscriberService,\n NoAccessibleDeviceError,\n OpeningConnectionError,\n StaticDeviceModelDataSource,\n type TransportDeviceModel,\n type TransportDiscoveredDevice,\n UnknownDeviceError,\n} from \"@ledgerhq/device-management-kit\";\nimport type { Device as NodeHIDDevice } from \"node-hid\";\nimport { Left, Right } from \"purify-ts\";\nimport { lastValueFrom, toArray } from \"rxjs\";\n\nimport { RECONNECT_DEVICE_TIMEOUT } from \"@api/data/NodeHidConfig\";\nimport { nodeHidDeviceStubBuilder } from \"@api/model/HIDDevice.stub\";\n\nimport {\n NodeHidApduSender,\n type NodeHidApduSenderDependencies,\n} from \"./NodeHidApduSender\";\nimport { NodeHidTransport } from \"./NodeHidTransport\";\n\n// Mock node-hid module\nconst mockDevicesAsync = vi.fn();\nvi.mock(\"node-hid\", () => ({\n devicesAsync: (...args: unknown[]) => mockDevicesAsync(...args),\n HIDAsync: {\n open: vi.fn(),\n },\n}));\n\n// Mock usb module\nconst mockUsbOn = vi.fn();\nconst mockUsbAttachCallbacks: ((device: unknown) => void)[] = [];\nconst mockUsbDetachCallbacks: ((device: unknown) => void)[] = [];\n\nvi.mock(\"usb\", () => ({\n usb: {\n on: (event: string, callback: (device: unknown) => void) => {\n mockUsbOn(event, callback);\n if (event === \"attach\") {\n mockUsbAttachCallbacks.push(callback);\n } else if (event === \"detach\") {\n mockUsbDetachCallbacks.push(callback);\n }\n },\n removeAllListeners: vi.fn(),\n unrefHotplugEvents: vi.fn(),\n },\n Device: class {},\n}));\n\nclass LoggerPublisherServiceStub implements LoggerPublisherService {\n constructor(subscribers: LoggerSubscriberService[], tag: string) {\n this.subscribers = subscribers;\n this.tag = tag;\n }\n subscribers: LoggerSubscriberService[] = [];\n tag: string = \"\";\n error = vi.fn();\n warn = vi.fn();\n info = vi.fn();\n debug = vi.fn();\n}\n\n// Our StaticDeviceModelDataSource can directly be used in our unit tests\nconst usbDeviceModelDataSource = new StaticDeviceModelDataSource();\nconst logger = new LoggerPublisherServiceStub([], \"node-hid\");\n\nconst stubDevice: NodeHIDDevice = nodeHidDeviceStubBuilder();\n\n/**\n * Flushes all pending promises\n */\nconst flushPromises = async () => {\n const timers = await vi.importActual<typeof import(\"timers\")>(\"timers\");\n return new Promise(timers.setImmediate);\n};\n\nconst setProcessPlatform = (platform: NodeJS.Platform) => {\n Object.defineProperty(process, \"platform\", {\n value: platform,\n configurable: true,\n });\n};\n\n/**\n * Helper to create a USB device event object matching the usb module's Device type\n */\nconst createUsbDevice = (vendorId: number, productId: number) => ({\n deviceDescriptor: {\n idVendor: vendorId,\n idProduct: productId,\n },\n});\n\n/**\n * Emit a USB attach event\n */\nconst emitUsbAttachEvent = (vendorId: number, productId: number) => {\n const device = createUsbDevice(vendorId, productId);\n mockUsbAttachCallbacks.forEach((callback) => callback(device));\n};\n\n/**\n * Emit a USB detach event\n */\nconst emitUsbDetachEvent = (vendorId: number, productId: number) => {\n const device = createUsbDevice(vendorId, productId);\n mockUsbDetachCallbacks.forEach((callback) => callback(device));\n};\n\ndescribe(\"NodeHidTransport\", () => {\n const originalPlatform = process.platform;\n let transport: NodeHidTransport;\n let apduReceiverServiceFactoryStub: ApduReceiverServiceFactory;\n let apduSenderServiceFactoryStub: ApduSenderServiceFactory;\n\n let mockDeviceConnectionStateMachineFactory = vi.fn();\n const mockEventDeviceConnected = vi.fn();\n const mockEventDeviceDisconnected = vi.fn();\n\n const mockDeviceApduSender = {\n sendApdu: vi.fn().mockResolvedValue(\n Right({\n data: new Uint8Array(),\n statusCode: new Uint8Array([0x90, 0x00]),\n } as ApduResponse),\n ),\n getDependencies: vi.fn().mockReturnValue({ device: stubDevice }),\n setDependencies: vi.fn(),\n closeConnection: vi.fn(),\n setupConnection: vi.fn(),\n };\n\n const mockDeviceConnectionStateMachine = {\n getDependencies: vi.fn().mockReturnValue({ device: stubDevice }),\n setDependencies: vi.fn(),\n getDeviceId: vi.fn(),\n sendApdu: vi.fn().mockResolvedValue(\n Right({\n data: new Uint8Array(),\n statusCode: new Uint8Array([0x90, 0x00]),\n } as ApduResponse),\n ),\n setupConnection: vi.fn(),\n eventDeviceConnected: mockEventDeviceConnected,\n eventDeviceDisconnected: mockEventDeviceDisconnected,\n closeConnection: vi.fn(),\n };\n\n function initializeTransport() {\n // Clear callbacks before reinitializing\n mockUsbAttachCallbacks.length = 0;\n mockUsbDetachCallbacks.length = 0;\n\n apduReceiverServiceFactoryStub = vi.fn();\n apduSenderServiceFactoryStub = vi.fn();\n mockDeviceConnectionStateMachineFactory = vi.fn(\n (\n params: DeviceConnectionStateMachineParams<NodeHidApduSenderDependencies>,\n ) => {\n return {\n ...mockDeviceConnectionStateMachine,\n getDeviceId: vi.fn().mockReturnValue(params.deviceId),\n getDependencies: params.deviceApduSender.getDependencies,\n setDependencies: params.deviceApduSender.setDependencies,\n } as unknown as DeviceConnectionStateMachine<NodeHidApduSenderDependencies>;\n },\n );\n const mockDeviceApduSenderFactory = vi.fn((params) => {\n return {\n ...mockDeviceApduSender,\n getDependencies: () => params.dependencies,\n setDependencies: (dependencies: NodeHidApduSenderDependencies) =>\n (params.dependencies = dependencies),\n } as unknown as NodeHidApduSender;\n });\n transport = new NodeHidTransport(\n usbDeviceModelDataSource,\n () => logger,\n apduSenderServiceFactoryStub,\n apduReceiverServiceFactoryStub,\n mockDeviceConnectionStateMachineFactory,\n mockDeviceApduSenderFactory,\n );\n }\n\n beforeEach(() => {\n initializeTransport();\n vi.useFakeTimers();\n });\n\n afterEach(() => {\n setProcessPlatform(originalPlatform);\n vi.restoreAllMocks();\n vi.clearAllMocks();\n vi.useRealTimers();\n });\n\n const discoverDevice = (\n onSuccess: (discoveredDevice: TransportDiscoveredDevice) => void,\n onError?: (error: unknown) => void,\n ) => {\n transport.startDiscovering().subscribe({\n next: onSuccess,\n error: onError,\n });\n };\n\n describe(\"isSupported\", () => {\n it(\"should always support the transport\", () => {\n expect(transport.isSupported()).toBe(true);\n });\n });\n\n describe(\"getIdentifier\", () => {\n it(\"should return NODE-HID identifier\", () => {\n expect(transport.getIdentifier()).toBe(\"NODE-HID\");\n });\n });\n\n describe(\"startDiscovering\", () => {\n const testCases = usbDeviceModelDataSource\n .getAllDeviceModels()\n .flatMap((deviceModel) => {\n return [\n {\n testTitle: `should emit device when discovered (${deviceModel.productName})`,\n hidDevice: nodeHidDeviceStubBuilder({\n productId: deviceModel.usbProductId << 8,\n product: deviceModel.productName,\n }),\n expectedDeviceModel: deviceModel,\n },\n {\n testTitle: `should emit device when discovered (${deviceModel.productName}, bootloader)`,\n hidDevice: nodeHidDeviceStubBuilder({\n productId: deviceModel.bootloaderUsbProductId,\n product: deviceModel.productName,\n }),\n expectedDeviceModel: deviceModel,\n },\n ];\n });\n\n testCases.forEach((testCase) => {\n it(\n testCase.testTitle,\n () =>\n new Promise<void>((resolve, reject) => {\n mockDevicesAsync.mockResolvedValueOnce([testCase.hidDevice]);\n\n discoverDevice(\n (discoveredDevice) => {\n try {\n expect(discoveredDevice).toEqual(\n expect.objectContaining({\n deviceModel: testCase.expectedDeviceModel,\n }),\n );\n\n resolve();\n } catch (expectError) {\n reject(expectError as Error);\n }\n },\n (error) => {\n reject(error as Error);\n },\n );\n }),\n );\n });\n\n it(\"should emit multiple devices if several are connected\", () =>\n new Promise<void>((resolve, reject) => {\n mockDevicesAsync.mockResolvedValueOnce([\n stubDevice,\n nodeHidDeviceStubBuilder({\n productId: 0x5011,\n product: \"Ledger Nano S Plus\",\n path: \"/dev/hidraw1\",\n }),\n ]);\n\n let count = 0;\n discoverDevice(\n (discoveredDevice) => {\n try {\n switch (count) {\n case 0:\n expect(discoveredDevice).toEqual(\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.NANO_X,\n productName: \"Ledger Nano X\",\n usbProductId: 0x40,\n }) as DeviceModel,\n }),\n );\n break;\n case 1:\n expect(discoveredDevice).toEqual(\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.NANO_SP,\n productName: \"Ledger Nano S Plus\",\n usbProductId: 0x50,\n }) as DeviceModel,\n }),\n );\n\n resolve();\n break;\n }\n\n count++;\n } catch (expectError) {\n reject(expectError as Error);\n }\n },\n (error) => {\n reject(error as Error);\n },\n );\n }));\n\n it(\"should ignore non-APDU ledger interfaces on darwin\", async () => {\n setProcessPlatform(\"darwin\");\n\n const genericInterface = nodeHidDeviceStubBuilder({\n path: \"/dev/hidraw0\",\n interface: 2,\n usagePage: 0xf1d0,\n });\n const apduInterface = nodeHidDeviceStubBuilder({\n path: \"/dev/hidraw1\",\n interface: 0,\n usagePage: 0xffa0,\n });\n\n mockDevicesAsync.mockResolvedValueOnce([genericInterface, apduInterface]);\n mockDevicesAsync.mockResolvedValue([genericInterface, apduInterface]);\n\n const discoveredDevices = await lastValueFrom(\n transport.startDiscovering().pipe(toArray()),\n );\n\n expect(discoveredDevices).toHaveLength(1);\n expect(\n (\n discoveredDevices[0] as TransportDiscoveredDevice & {\n hidDevice: NodeHIDDevice;\n }\n ).hidDevice.path,\n ).toBe(\"/dev/hidraw1\");\n });\n\n it(\"should ignore non-APDU ledger interfaces on win32\", async () => {\n setProcessPlatform(\"win32\");\n\n const genericInterface = nodeHidDeviceStubBuilder({\n path: \"\\\\\\\\?\\\\hid#vid_2c97&pid_0001&mi_01#generic\",\n interface: 2,\n usagePage: 0xf1d0,\n });\n const apduInterface = nodeHidDeviceStubBuilder({\n path: \"\\\\\\\\?\\\\hid#vid_2c97&pid_0001&mi_00#apdu\",\n interface: 0,\n usagePage: 0xffa0,\n });\n\n mockDevicesAsync.mockResolvedValueOnce([genericInterface, apduInterface]);\n mockDevicesAsync.mockResolvedValue([genericInterface, apduInterface]);\n\n const discoveredDevices = await lastValueFrom(\n transport.startDiscovering().pipe(toArray()),\n );\n\n expect(discoveredDevices).toHaveLength(1);\n expect(\n (\n discoveredDevices[0] as TransportDiscoveredDevice & {\n hidDevice: NodeHIDDevice;\n }\n ).hidDevice.path,\n ).toBe(\"\\\\\\\\?\\\\hid#vid_2c97&pid_0001&mi_00#apdu\");\n });\n\n it(\"should throw DeviceNotRecognizedError if the device is not recognized\", () =>\n new Promise<void>((resolve, reject) => {\n mockDevicesAsync.mockResolvedValueOnce([\n nodeHidDeviceStubBuilder({\n productId: 0x4242,\n }),\n ]);\n\n discoverDevice(\n () => {\n reject(\"should not return a device\");\n },\n (error) => {\n expect(error).toBeInstanceOf(DeviceNotRecognizedError);\n resolve();\n },\n );\n }));\n\n it(\"should emit an error if devicesAsync throws\", () =>\n new Promise<void>((resolve, reject) => {\n const message = \"devices async error\";\n mockDevicesAsync.mockImplementationOnce(() => {\n throw new Error(message);\n });\n\n discoverDevice(\n () => {\n reject(\"should not return a device\");\n },\n (error) => {\n expect(error).toBeInstanceOf(NoAccessibleDeviceError);\n expect(error).toStrictEqual(\n new NoAccessibleDeviceError(new Error(message)),\n );\n resolve();\n },\n );\n }));\n\n it(\"should emit an error if no devices are found\", () =>\n new Promise<void>((resolve, reject) => {\n mockDevicesAsync.mockResolvedValueOnce([]);\n\n discoverDevice(\n (discoveredDevice) => {\n reject(\n `Should not emit any value, but emitted ${JSON.stringify(\n discoveredDevice,\n )}`,\n );\n },\n (error) => {\n try {\n expect(error).toBeInstanceOf(NoAccessibleDeviceError);\n resolve();\n } catch (expectError) {\n reject(expectError as Error);\n }\n },\n );\n }));\n\n it(\"should emit the same discoveredDevice object if discovered twice in a row\", async () => {\n mockDevicesAsync.mockResolvedValue([stubDevice]);\n\n const firstDiscoveredDevice = await new Promise<void>(\n (resolve, reject) => {\n discoverDevice(\n () => resolve(),\n (err) => reject(err),\n );\n },\n );\n const secondDiscoveredDevice = await new Promise<void>(\n (resolve, reject) => {\n discoverDevice(\n () => resolve(),\n (err) => reject(err),\n );\n },\n );\n expect(secondDiscoveredDevice).toBe(firstDiscoveredDevice);\n });\n });\n\n describe(\"destroy\", () => {\n it(\"should stop monitoring connections when destroyed\", () => {\n const abortSpy = vi.spyOn(AbortController.prototype, \"abort\");\n\n transport.destroy();\n\n expect(abortSpy).toHaveBeenCalled();\n });\n });\n\n describe(\"connect\", () => {\n it(\"should throw UnknownDeviceError if no internal device\", async () => {\n const connectParams = {\n deviceId: \"fake\",\n onDisconnect: vi.fn(),\n };\n\n const connect = await transport.connect(connectParams);\n\n expect(connect).toStrictEqual(\n Left(new UnknownDeviceError(\"Unknown device fake\")),\n );\n });\n\n it(\"should throw OpeningConnectionError if the device cannot be opened\", async () => {\n const message = \"cannot be opened\";\n // Mock the DeviceConnectionStateMachine's setupConnection to reject\n mockDeviceConnectionStateMachine.setupConnection.mockRejectedValueOnce(\n new Error(message),\n );\n mockDevicesAsync.mockResolvedValueOnce([stubDevice]);\n mockDevicesAsync.mockResolvedValue([stubDevice]);\n\n const discoveredDevice = await lastValueFrom(\n transport.startDiscovering(),\n );\n const connected = await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect: vi.fn(),\n });\n expect(connected.isLeft()).toBe(true);\n expect(connected.extract()).toBeInstanceOf(OpeningConnectionError);\n });\n\n it(\"should return a device if available\", async () => {\n mockDevicesAsync.mockResolvedValueOnce([stubDevice]);\n mockDevicesAsync.mockResolvedValue([stubDevice]);\n\n const discoveredDevice = await lastValueFrom(\n transport.startDiscovering(),\n );\n const connected = await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect: vi.fn(),\n });\n expect(connected.isRight()).toStrictEqual(true);\n expect(connected.extract()).toEqual(\n expect.objectContaining({ id: discoveredDevice.id }),\n );\n });\n\n it(\"should return an existing connected device\", async () => {\n mockDevicesAsync.mockResolvedValueOnce([stubDevice]);\n mockDevicesAsync.mockResolvedValue([stubDevice]);\n\n const discoveredDevice = await lastValueFrom(\n transport.startDiscovering(),\n );\n await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect: vi.fn(),\n });\n const connected = await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect: vi.fn(),\n });\n expect(connected.isRight()).toStrictEqual(true);\n expect(connected.extract()).toEqual(\n expect.objectContaining({ id: discoveredDevice.id }),\n );\n });\n });\n\n describe(\"disconnect\", () => {\n it(\"should throw an error if the device is not connected\", async () => {\n // given\n const connectedDevice = connectedDeviceStubBuilder();\n\n // when\n const disconnect = await transport.disconnect({\n connectedDevice,\n });\n\n expect(disconnect).toStrictEqual(\n Left(new UnknownDeviceError(`Unknown device ${connectedDevice.id}`)),\n );\n });\n\n it(\"should disconnect if the device is connected\", async () => {\n mockDevicesAsync.mockResolvedValueOnce([stubDevice]);\n mockDevicesAsync.mockResolvedValue([stubDevice]);\n\n const discoveredDevice = await lastValueFrom(\n transport.startDiscovering(),\n );\n const connected = await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect: vi.fn(),\n });\n expect(connected.isRight()).toStrictEqual(true);\n const result = await transport.disconnect({\n connectedDevice: connected.unsafeCoerce(),\n });\n expect(result).toStrictEqual(Right(undefined));\n });\n\n it(\"should call disconnect handler if a connected device is unplugged\", async () => {\n // Get onTerminated for the first connection only\n let onTerminated1 = vi.fn();\n mockDeviceConnectionStateMachineFactory.mockImplementationOnce(\n (params) => {\n onTerminated1 = params.onTerminated;\n return {\n ...mockDeviceConnectionStateMachine,\n getDeviceId: vi.fn().mockReturnValue(params.deviceId),\n } as unknown as DeviceConnectionStateMachine<NodeHidApduSenderDependencies>;\n },\n );\n\n // Add 2 discoverable devices\n const hidDevice1 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw0\" });\n const hidDevice2 = nodeHidDeviceStubBuilder({\n path: \"/dev/hidraw1\",\n productId: 0x5011,\n });\n mockDevicesAsync.mockResolvedValueOnce([hidDevice1, hidDevice2]);\n mockDevicesAsync.mockResolvedValue([hidDevice1, hidDevice2]);\n\n // Connect the 2 devices\n const discoveredDevices = await lastValueFrom(\n transport.startDiscovering().pipe(toArray()),\n );\n expect(discoveredDevices.length).toStrictEqual(2);\n const onDisconnect1 = vi.fn();\n const connected1 = await transport.connect({\n deviceId: discoveredDevices[0]!.id,\n onDisconnect: onDisconnect1,\n });\n const onDisconnect2 = vi.fn();\n const connected2 = await transport.connect({\n deviceId: discoveredDevices[1]!.id,\n onDisconnect: onDisconnect2,\n });\n expect(connected1.isRight()).toStrictEqual(true);\n expect(connected2.isRight()).toStrictEqual(true);\n\n // unplug the first device\n onTerminated1();\n expect(onDisconnect1).toHaveBeenCalled();\n expect(onDisconnect2).not.toHaveBeenCalled();\n });\n\n it(\"should call disconnect handler if a connected device is unplugged while reconnecting\", async () => {\n // Get onTerminated for the first connection only\n let onTerminated1 = vi.fn();\n let tryToReconnect1 = vi.fn();\n mockDeviceConnectionStateMachineFactory.mockImplementationOnce(\n (params) => {\n onTerminated1 = params.onTerminated;\n tryToReconnect1 = params.tryToReconnect;\n return {\n ...mockDeviceConnectionStateMachine,\n getDeviceId: vi.fn().mockReturnValue(params.deviceId),\n } as unknown as DeviceConnectionStateMachine<NodeHidApduSenderDependencies>;\n },\n );\n\n // Add 2 discoverable devices\n const hidDevice1 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw0\" });\n const hidDevice2 = nodeHidDeviceStubBuilder({\n path: \"/dev/hidraw1\",\n productId: 0x5011,\n });\n mockDevicesAsync.mockResolvedValueOnce([hidDevice1, hidDevice2]);\n mockDevicesAsync.mockResolvedValue([hidDevice1, hidDevice2]);\n\n // Connect the 2 devices\n const discoveredDevices = await lastValueFrom(\n transport.startDiscovering().pipe(toArray()),\n );\n expect(discoveredDevices.length).toStrictEqual(2);\n const onDisconnect1 = vi.fn();\n const connected1 = await transport.connect({\n deviceId: discoveredDevices[0]!.id,\n onDisconnect: onDisconnect1,\n });\n const onDisconnect2 = vi.fn();\n const connected2 = await transport.connect({\n deviceId: discoveredDevices[1]!.id,\n onDisconnect: onDisconnect2,\n });\n expect(connected1.isRight()).toStrictEqual(true);\n expect(connected2.isRight()).toStrictEqual(true);\n\n // Try to reconnect the first device\n tryToReconnect1();\n\n // unplug the first device\n onTerminated1();\n expect(onDisconnect1).toHaveBeenCalled();\n expect(onDisconnect2).not.toHaveBeenCalled();\n });\n });\n\n describe(\"reconnect\", () => {\n it(\"should stop disconnection if reconnection happens\", () =>\n new Promise<void>((resolve, reject) => {\n // given\n const onDisconnect = vi.fn();\n let tryToReconnect = vi.fn();\n\n const hidDevice1 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw0\" });\n const hidDevice2 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw1\" });\n\n mockDevicesAsync.mockResolvedValueOnce([hidDevice1]);\n mockDevicesAsync.mockResolvedValue([hidDevice1, hidDevice2]);\n mockDeviceConnectionStateMachineFactory.mockImplementationOnce(\n (params) => {\n tryToReconnect = params.tryToReconnect;\n return {\n ...mockDeviceConnectionStateMachine,\n getDeviceId: vi.fn().mockReturnValue(params.deviceId),\n getDependencies: params.deviceApduSender.getDependencies,\n setDependencies: params.deviceApduSender.setDependencies,\n } as unknown as DeviceConnectionStateMachine<NodeHidApduSenderDependencies>;\n },\n );\n\n discoverDevice(async (discoveredDevice) => {\n try {\n await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect,\n });\n\n /* Disconnection */\n emitUsbDetachEvent(0x2c97, 0x40);\n await flushPromises();\n expect(mockEventDeviceDisconnected).toHaveBeenCalled();\n\n tryToReconnect();\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT / 3);\n\n /* Reconnection */\n emitUsbAttachEvent(0x2c97, 0x40);\n\n await flushPromises();\n expect(mockEventDeviceConnected).toHaveBeenCalled();\n\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT);\n expect(onDisconnect).not.toHaveBeenCalled();\n resolve();\n } catch (error) {\n reject(error as Error);\n }\n });\n }));\n\n it(\"should be able to reconnect twice in a row if the device is unplugged and replugged twice\", () =>\n new Promise<void>((resolve, reject) => {\n // given\n const onDisconnect = vi.fn();\n let tryToReconnect = vi.fn();\n\n const hidDevice1 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw0\" });\n const hidDevice2 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw1\" });\n const hidDevice3 = nodeHidDeviceStubBuilder({ path: \"/dev/hidraw2\" });\n\n mockDevicesAsync.mockResolvedValueOnce([hidDevice1]);\n mockDevicesAsync.mockResolvedValue([\n hidDevice1,\n hidDevice2,\n hidDevice3,\n ]);\n mockDeviceConnectionStateMachineFactory.mockImplementationOnce(\n (params) => {\n tryToReconnect = params.tryToReconnect;\n return {\n ...mockDeviceConnectionStateMachine,\n getDeviceId: vi.fn().mockReturnValue(params.deviceId),\n getDependencies: params.deviceApduSender.getDependencies,\n setDependencies: params.deviceApduSender.setDependencies,\n } as unknown as DeviceConnectionStateMachine<NodeHidApduSenderDependencies>;\n },\n );\n\n // when\n discoverDevice(async (discoveredDevice) => {\n await transport.connect({\n deviceId: discoveredDevice.id,\n onDisconnect,\n });\n try {\n /* First disconnection */\n emitUsbDetachEvent(0x2c97, 0x40);\n await flushPromises();\n expect(mockEventDeviceDisconnected).toHaveBeenCalled();\n tryToReconnect();\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT / 3);\n\n /* First reconnection */\n emitUsbAttachEvent(0x2c97, 0x40);\n\n await flushPromises();\n expect(mockEventDeviceConnected).toHaveBeenCalled();\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT);\n expect(onDisconnect).not.toHaveBeenCalled();\n\n /* Second disconnection */\n emitUsbDetachEvent(0x2c97, 0x40);\n await flushPromises();\n expect(mockEventDeviceDisconnected).toHaveBeenCalled();\n tryToReconnect();\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT / 3);\n\n /* Second reconnection */\n emitUsbAttachEvent(0x2c97, 0x40);\n\n await flushPromises();\n expect(mockEventDeviceConnected).toHaveBeenCalled();\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT);\n expect(onDisconnect).not.toHaveBeenCalled();\n\n resolve();\n } catch (error) {\n reject(error as Error);\n }\n });\n }));\n });\n\n describe(\"listenToAvailableDevices\", () => {\n it(\"should emit the devices already connected before listening\", async () => {\n // given\n const hidDevice = nodeHidDeviceStubBuilder();\n mockDevicesAsync.mockResolvedValue([hidDevice]);\n\n const onComplete = vi.fn();\n const onError = vi.fn();\n\n let observedDevices: TransportDiscoveredDevice[] = [];\n // when\n transport.listenToAvailableDevices().subscribe({\n next: (knownDevices) => {\n observedDevices = knownDevices;\n },\n complete: onComplete,\n error: onError,\n });\n\n await flushPromises();\n\n expect(observedDevices).toEqual([\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.NANO_X,\n }) as TransportDeviceModel,\n }),\n ]);\n expect(onComplete).not.toHaveBeenCalled();\n expect(onError).not.toHaveBeenCalled();\n });\n\n it(\"should emit the new list of devices after connection and disconnection events\", async () => {\n initializeTransport();\n // given\n const hidDevice1 = nodeHidDeviceStubBuilder({\n productId:\n usbDeviceModelDataSource.getDeviceModel({\n id: DeviceModelId.NANO_X,\n }).usbProductId << 8,\n path: \"/dev/hidraw0\",\n });\n const hidDevice2 = nodeHidDeviceStubBuilder({\n productId:\n usbDeviceModelDataSource.getDeviceModel({ id: DeviceModelId.STAX })\n .usbProductId << 8,\n path: \"/dev/hidraw1\",\n });\n mockDevicesAsync.mockResolvedValue([hidDevice1]);\n\n const onComplete = vi.fn();\n const onError = vi.fn();\n\n let observedDevices: TransportDiscoveredDevice[] = [];\n // when\n transport.listenToAvailableDevices().subscribe({\n next: (knownDevices) => {\n observedDevices = knownDevices;\n },\n complete: onComplete,\n error: onError,\n });\n\n await flushPromises();\n\n expect(observedDevices).toEqual([\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.NANO_X,\n }) as TransportDeviceModel,\n }),\n ]);\n\n // When a new device is connected\n mockDevicesAsync.mockResolvedValue([hidDevice1, hidDevice2]);\n emitUsbAttachEvent(0x2c97, 0x60);\n await flushPromises();\n\n expect(observedDevices).toEqual([\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.NANO_X,\n }) as TransportDeviceModel,\n }),\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.STAX,\n }) as TransportDeviceModel,\n }),\n ]);\n\n // When a device is disconnected\n mockDevicesAsync.mockResolvedValue([hidDevice2]);\n emitUsbDetachEvent(0x2c97, 0x40);\n await flushPromises();\n\n expect(observedDevices).toEqual([\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.STAX,\n }) as TransportDeviceModel,\n }),\n ]);\n\n expect(onComplete).not.toHaveBeenCalled();\n expect(onError).not.toHaveBeenCalled();\n });\n\n it(\"should preserve DeviceId in case the device has been disconnected and reconnected before the timeout\", async () => {\n // given\n const hidDevice = nodeHidDeviceStubBuilder();\n\n mockDevicesAsync.mockResolvedValue([hidDevice]);\n\n const onComplete = vi.fn();\n const onError = vi.fn();\n let observedDevices: TransportDiscoveredDevice[] = [];\n // when\n transport.listenToAvailableDevices().subscribe({\n next: (knownDevices) => {\n observedDevices = knownDevices;\n },\n complete: onComplete,\n error: onError,\n });\n\n await flushPromises();\n\n const firstObservedDeviceId = observedDevices[0]?.id;\n expect(firstObservedDeviceId).toBeTruthy();\n expect(observedDevices[0]?.deviceModel?.id).toBe(DeviceModelId.NANO_X);\n\n // Start a connection with the device\n await transport.connect({\n deviceId: observedDevices[0]!.id,\n onDisconnect: vi.fn(),\n });\n await flushPromises();\n\n // When the device is disconnected\n mockDevicesAsync.mockResolvedValue([]);\n emitUsbDetachEvent(0x2c97, 0x40);\n await flushPromises();\n\n expect(observedDevices).toEqual([]);\n\n // When the device is reconnected\n mockDevicesAsync.mockResolvedValue([hidDevice]);\n emitUsbAttachEvent(0x2c97, 0x40);\n await flushPromises();\n\n expect(observedDevices).toEqual([\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: DeviceModelId.NANO_X,\n }) as TransportDeviceModel,\n }),\n ]);\n\n expect(observedDevices[0]?.id).toBeTruthy();\n expect(observedDevices[0]?.id).toBe(firstObservedDeviceId);\n });\n });\n\n describe(\"USB connection events\", () => {\n it(\"should register usb attach and detach listeners on construction\", () => {\n expect(mockUsbOn).toHaveBeenCalledWith(\"attach\", expect.any(Function));\n expect(mockUsbOn).toHaveBeenCalledWith(\"detach\", expect.any(Function));\n });\n\n it(\"should ignore non-Ledger USB devices\", async () => {\n mockDevicesAsync.mockResolvedValue([stubDevice]);\n\n let observedDevices: TransportDiscoveredDevice[] = [];\n transport.listenToAvailableDevices().subscribe({\n next: (knownDevices) => {\n observedDevices = knownDevices;\n },\n });\n\n await flushPromises();\n expect(observedDevices.length).toBe(1);\n\n // Emit attach event for non-Ledger device\n emitUsbAttachEvent(0x1234, 0x5678);\n await flushPromises();\n\n // Should still be 1 device\n expect(observedDevices.length).toBe(1);\n });\n });\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["import_device_management_kit", "import_purify_ts", "import_rxjs", "import_NodeHidConfig", "import_HIDDevice", "import_NodeHidTransport", "mockDevicesAsync", "args", "mockUsbOn", "mockUsbAttachCallbacks", "mockUsbDetachCallbacks", "event", "callback", "LoggerPublisherServiceStub", "subscribers", "tag", "usbDeviceModelDataSource", "logger", "stubDevice", "flushPromises", "timers", "setProcessPlatform", "platform", "createUsbDevice", "vendorId", "productId", "emitUsbAttachEvent", "device", "emitUsbDetachEvent", "originalPlatform", "transport", "apduReceiverServiceFactoryStub", "apduSenderServiceFactoryStub", "mockDeviceConnectionStateMachineFactory", "mockEventDeviceConnected", "mockEventDeviceDisconnected", "mockDeviceApduSender", "mockDeviceConnectionStateMachine", "initializeTransport", "params", "mockDeviceApduSenderFactory", "dependencies", "discoverDevice", "onSuccess", "onError", "deviceModel", "testCase", "resolve", "reject", "discoveredDevice", "expectError", "error", "count", "genericInterface", "apduInterface", "discoveredDevices", "message", "firstDiscoveredDevice", "err", "secondDiscoveredDevice", "abortSpy", "connectParams", "connect", "connected", "connectedDevice", "disconnect", "result", "onTerminated1", "hidDevice1", "hidDevice2", "onDisconnect1", "connected1", "onDisconnect2", "connected2", "tryToReconnect1", "onDisconnect", "tryToReconnect", "hidDevice3", "hidDevice", "onComplete", "observedDevices", "knownDevices", "firstObservedDeviceId"]
7
7
  }
@@ -31,7 +31,7 @@
31
31
  "license": "Apache-2.0",
32
32
  "name": "@ledgerhq/device-transport-kit-node-hid",
33
33
  "peerDependencies": {
34
- "@ledgerhq/device-management-kit": "workspace:*",
34
+ "@ledgerhq/device-management-kit": "^1.2.0",
35
35
  "rxjs": "catalog:"
36
36
  },
37
37
  "private": false,
@@ -55,5 +55,5 @@
55
55
  "watch:builds": "pnpm ldmk-tool watch --entryPoints src/index.ts,src/**/*.ts --tsconfig tsconfig.prod.json --platform node",
56
56
  "watch:types": "concurrently \"tsc --watch -p tsconfig.prod.json\" \"tsc-alias --watch -p tsconfig.prod.json\""
57
57
  },
58
- "version": "1.0.0"
58
+ "version": "1.0.1"
59
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"NodeHidTransport.d.ts","sourceRoot":"","sources":["../../../../src/api/transport/NodeHidTransport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"NodeHidTransport.d.ts","sourceRoot":"","sources":["../../../../src/api/transport/NodeHidTransport.ts"],"names":[],"mappings":"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"}