@ledgerhq/device-transport-kit-mockserver 0.0.0-develop-20260622003722 → 0.0.0-develop-20260624002956
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/MockserverTransport.js +1 -1
- package/lib/cjs/MockserverTransport.js.map +3 -3
- package/lib/cjs/MockserverTransport.test.js +2 -0
- package/lib/cjs/MockserverTransport.test.js.map +7 -0
- package/lib/cjs/package.json +1 -1
- package/lib/esm/MockserverTransport.js +1 -1
- package/lib/esm/MockserverTransport.js.map +3 -3
- package/lib/esm/MockserverTransport.test.js +2 -0
- package/lib/esm/MockserverTransport.test.js.map +7 -0
- package/lib/esm/package.json +1 -1
- package/lib/types/MockserverTransport.d.ts +14 -4
- package/lib/types/MockserverTransport.d.ts.map +1 -1
- package/lib/types/MockserverTransport.test.d.ts +2 -0
- package/lib/types/MockserverTransport.test.d.ts.map +1 -0
- package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
- package/package.json +6 -6
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var a=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var m=Object.prototype.hasOwnProperty;var h=(n,e)=>{for(var r in e)a(n,r,{get:e[r],enumerable:!0})},y=(n,e,r,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of D(e))!m.call(n,s)&&s!==r&&a(n,s,{get:()=>e[s],enumerable:!(i=u(e,s))||i.enumerable});return n};var b=n=>y(a({},"__esModule",{value:!0}),n);var I={};h(I,{MockTransport:()=>g,mockserverIdentifier:()=>l,mockserverTransportFactory:()=>E});module.exports=b(I);var o=require("@ledgerhq/device-management-kit"),v=require("@ledgerhq/device-mockserver-client"),c=require("purify-ts"),t=require("rxjs");const l="MOCKSERVER",T=[823132160],f=1e3;class g{logger;mockClient;identifier=l;constructor(e,r,i){this.logger=e("MockTransport"),this.mockClient=new v.MockClient(r,{token:i})}isSupported(){return!0}getIdentifier(){return this.identifier}listenToAvailableDevices(){return this.logger.debug("listenToAvailableDevices"),(0,t.timer)(0,f).pipe((0,t.switchMap)(()=>(0,t.from)(this.mockClient.listDevices())),(0,t.map)(e=>this.mapToDiscoveredDevices(e)),(0,t.catchError)(e=>(this.logger.error("listenToAvailableDevices failed",{data:{error:e}}),(0,t.of)([]))))}startDiscovering(){return this.logger.debug("startDiscovering"),(0,t.from)(this.mockClient.listDevices()).pipe((0,t.map)(e=>this.mapToDiscoveredDevices(e)),(0,t.mergeMap)(e=>(0,t.from)(e)))}stopDiscovering(){this.logger.debug("stopDiscovering")}async connect(e){this.logger.debug("connect");const r=e.deviceId;try{const{device:i}=await this.mockClient.connect(r),s={sendApdu:d=>this.sendApdu(r,e.deviceId,e.onDisconnect,d),deviceModel:this.buildDeviceModel(i),id:e.deviceId,type:i.connectivity_type,transport:this.identifier};return(0,c.Right)(s)}catch(i){return(0,c.Left)(new o.OpeningConnectionError(i))}}async disconnect(e){this.logger.debug("disconnect");const r=e.connectedDevice.id;try{return await this.mockClient.disconnect(r)?(0,c.Right)(void 0):(0,c.Left)(new o.DisconnectError(new Error(`Failed to disconnect ${r}`)))}catch(i){return(0,c.Left)(new o.DisconnectError(i))}}async sendApdu(e,r,i,s){this.logger.debug("send");try{const d=await this.mockClient.sendApdu(e,s);this.logger.debug((0,o.formatApduSentLog)(s));const p={statusCode:(0,o.hexaStringToBuffer)(d.response.slice(-4))??new Uint8Array,data:(0,o.hexaStringToBuffer)(d.response.slice(0,-4))??new Uint8Array};return this.logger.debug((0,o.formatApduReceivedLog)(p)),(0,c.Right)(p)}catch(d){return i(r),(0,c.Left)(new o.NoAccessibleDeviceError(d))}}mapToDiscoveredDevices(e){return e.map(r=>({id:r.id,deviceModel:this.buildDeviceModel(r),transport:this.identifier}))}buildDeviceModel(e){return{id:e.device_type,productName:e.name,usbProductId:16,legacyUsbProductId:1,bootloaderUsbProductId:1,getBlockSize(){return 32},usbOnly:!0,memorySize:320*1024,masks:e.masks??T}}}const E=(n,e)=>({config:r,loggerServiceFactory:i})=>new g(i,n??r.mockUrl,e);0&&(module.exports={MockTransport,mockserverIdentifier,mockserverTransportFactory});
|
|
2
2
|
//# sourceMappingURL=MockserverTransport.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/MockserverTransport.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n type ApduResponse,\n type ConnectError,\n type DeviceId,\n type DeviceModelId,\n DisconnectError,\n type DisconnectHandler,\n type
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,mBAAAE,EAAA,yBAAAC,EAAA,+BAAAC,IAAA,eAAAC,EAAAL,GAAA,IAAAM,EAmBO,2CACPC,
|
|
6
|
-
"names": ["MockserverTransport_exports", "__export", "MockTransport", "mockserverIdentifier", "mockserverTransportFactory", "__toCommonJS", "import_device_management_kit", "import_device_mockserver_client", "import_purify_ts", "import_rxjs", "loggerServiceFactory", "
|
|
4
|
+
"sourcesContent": ["import {\n type ApduResponse,\n type ConnectError,\n type DeviceId,\n type DeviceModelId,\n DisconnectError,\n type DisconnectHandler,\n type DmkError,\n formatApduReceivedLog,\n formatApduSentLog,\n hexaStringToBuffer,\n type LoggerPublisherService,\n NoAccessibleDeviceError,\n OpeningConnectionError,\n type Transport,\n type TransportConnectedDevice,\n type TransportDiscoveredDevice,\n type TransportFactory,\n type TransportIdentifier,\n} from \"@ledgerhq/device-management-kit\";\nimport {\n type CommandResponse,\n type Device,\n MockClient,\n} from \"@ledgerhq/device-mockserver-client\";\nimport { type Either, Left, Right } from \"purify-ts\";\nimport {\n catchError,\n from,\n map,\n mergeMap,\n type Observable,\n of,\n switchMap,\n timer,\n} from \"rxjs\";\n\nexport const mockserverIdentifier: TransportIdentifier = \"MOCKSERVER\";\n\nconst DEFAULT_MASKS = [0x31100000];\n\n/** Interval (ms) at which the mock server is polled for available devices. */\nconst DISCOVERY_POLL_INTERVAL_MS = 1000;\n\nexport class MockTransport implements Transport {\n private logger: LoggerPublisherService;\n private mockClient: MockClient;\n private readonly identifier: TransportIdentifier = mockserverIdentifier;\n\n constructor(\n loggerServiceFactory: (tag: string) => LoggerPublisherService,\n mockUrl: string,\n sessionToken?: string,\n ) {\n this.logger = loggerServiceFactory(\"MockTransport\");\n this.mockClient = new MockClient(mockUrl, { token: sessionToken });\n }\n\n isSupported(): boolean {\n return true;\n }\n\n getIdentifier(): TransportIdentifier {\n return this.identifier;\n }\n\n listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]> {\n this.logger.debug(\"listenToAvailableDevices\");\n return timer(0, DISCOVERY_POLL_INTERVAL_MS).pipe(\n switchMap(() => from(this.mockClient.listDevices())),\n map((devices) => this.mapToDiscoveredDevices(devices)),\n catchError((error) => {\n this.logger.error(\"listenToAvailableDevices failed\", {\n data: { error },\n });\n return of([]);\n }),\n );\n }\n\n startDiscovering(): Observable<TransportDiscoveredDevice> {\n this.logger.debug(\"startDiscovering\");\n return from(this.mockClient.listDevices()).pipe(\n map((devices) => this.mapToDiscoveredDevices(devices)),\n mergeMap((devices) => from(devices)),\n );\n }\n\n stopDiscovering(): void {\n //DO NOTHING HERE\n this.logger.debug(\"stopDiscovering\");\n }\n\n async connect(params: {\n deviceId: DeviceId;\n onDisconnect: DisconnectHandler;\n }): Promise<Either<ConnectError, TransportConnectedDevice>> {\n this.logger.debug(\"connect\");\n const deviceId: string = params.deviceId;\n try {\n const { device } = await this.mockClient.connect(deviceId);\n const connectedDevice = {\n sendApdu: (apdu) =>\n this.sendApdu(deviceId, params.deviceId, params.onDisconnect, apdu),\n deviceModel: this.buildDeviceModel(device),\n id: params.deviceId,\n type: device.connectivity_type,\n transport: this.identifier,\n } as TransportConnectedDevice;\n return Right(connectedDevice);\n } catch (error) {\n return Left(new OpeningConnectionError(error as Error));\n }\n }\n\n async disconnect(params: {\n connectedDevice: TransportConnectedDevice;\n }): Promise<Either<DmkError, void>> {\n this.logger.debug(\"disconnect\");\n const deviceId: string = params.connectedDevice.id;\n try {\n const success: boolean = await this.mockClient.disconnect(deviceId);\n if (!success) {\n return Left(\n new DisconnectError(new Error(`Failed to disconnect ${deviceId}`)),\n );\n }\n return Right(undefined);\n } catch (error) {\n return Left(new DisconnectError(error as Error));\n }\n }\n\n async sendApdu(\n deviceId: string,\n onDisconnectDeviceId: DeviceId,\n onDisconnect: DisconnectHandler,\n apdu: Uint8Array,\n ): Promise<Either<DmkError, ApduResponse>> {\n this.logger.debug(\"send\");\n try {\n const response: CommandResponse = await this.mockClient.sendApdu(\n deviceId,\n apdu,\n );\n this.logger.debug(formatApduSentLog(apdu));\n const apduResponse = {\n statusCode:\n hexaStringToBuffer(response.response.slice(-4)) ?? new Uint8Array(),\n data:\n hexaStringToBuffer(response.response.slice(0, -4)) ??\n new Uint8Array(),\n } as ApduResponse;\n this.logger.debug(formatApduReceivedLog(apduResponse));\n return Right(apduResponse);\n } catch (error) {\n onDisconnect(onDisconnectDeviceId);\n return Left(new NoAccessibleDeviceError(error as Error));\n }\n }\n\n private mapToDiscoveredDevices(\n devices: Device[],\n ): TransportDiscoveredDevice[] {\n return devices.map((device) => ({\n id: device.id,\n deviceModel: this.buildDeviceModel(device),\n transport: this.identifier,\n }));\n }\n\n private buildDeviceModel(device: Device) {\n return {\n id: device.device_type as DeviceModelId,\n productName: device.name,\n usbProductId: 0x10,\n legacyUsbProductId: 0x0001,\n bootloaderUsbProductId: 0x0001,\n getBlockSize() {\n return 32;\n },\n usbOnly: true,\n memorySize: 320 * 1024,\n masks: device.masks ?? DEFAULT_MASKS,\n };\n }\n}\n\n/**\n * Build a mock-server transport factory.\n *\n * @param mockUrl Optional mock server URL. When omitted, `config.mockUrl` from\n * the DMK configuration is used.\n * @param sessionToken Optional bearer token to share an existing session\n * When omitted, the client self-provisions a session via /auth.\n */\nexport const mockserverTransportFactory =\n (mockUrl?: string, sessionToken?: string): TransportFactory =>\n ({ config, loggerServiceFactory }) =>\n new MockTransport(\n loggerServiceFactory,\n mockUrl ?? config.mockUrl,\n sessionToken,\n );\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,mBAAAE,EAAA,yBAAAC,EAAA,+BAAAC,IAAA,eAAAC,EAAAL,GAAA,IAAAM,EAmBO,2CACPC,EAIO,8CACPC,EAAyC,qBACzCC,EASO,gBAEA,MAAMN,EAA4C,aAEnDO,EAAgB,CAAC,SAAU,EAG3BC,EAA6B,IAE5B,MAAMT,CAAmC,CACtC,OACA,WACS,WAAkCC,EAEnD,YACES,EACAC,EACAC,EACA,CACA,KAAK,OAASF,EAAqB,eAAe,EAClD,KAAK,WAAa,IAAI,aAAWC,EAAS,CAAE,MAAOC,CAAa,CAAC,CACnE,CAEA,aAAuB,CACrB,MAAO,EACT,CAEA,eAAqC,CACnC,OAAO,KAAK,UACd,CAEA,0BAAoE,CAClE,YAAK,OAAO,MAAM,0BAA0B,KACrC,SAAM,EAAGH,CAA0B,EAAE,QAC1C,aAAU,OAAM,QAAK,KAAK,WAAW,YAAY,CAAC,CAAC,KACnD,OAAKI,GAAY,KAAK,uBAAuBA,CAAO,CAAC,KACrD,cAAYC,IACV,KAAK,OAAO,MAAM,kCAAmC,CACnD,KAAM,CAAE,MAAAA,CAAM,CAChB,CAAC,KACM,MAAG,CAAC,CAAC,EACb,CACH,CACF,CAEA,kBAA0D,CACxD,YAAK,OAAO,MAAM,kBAAkB,KAC7B,QAAK,KAAK,WAAW,YAAY,CAAC,EAAE,QACzC,OAAKD,GAAY,KAAK,uBAAuBA,CAAO,CAAC,KACrD,YAAUA,MAAY,QAAKA,CAAO,CAAC,CACrC,CACF,CAEA,iBAAwB,CAEtB,KAAK,OAAO,MAAM,iBAAiB,CACrC,CAEA,MAAM,QAAQE,EAG8C,CAC1D,KAAK,OAAO,MAAM,SAAS,EAC3B,MAAMC,EAAmBD,EAAO,SAChC,GAAI,CACF,KAAM,CAAE,OAAAE,CAAO,EAAI,MAAM,KAAK,WAAW,QAAQD,CAAQ,EACnDE,EAAkB,CACtB,SAAWC,GACT,KAAK,SAASH,EAAUD,EAAO,SAAUA,EAAO,aAAcI,CAAI,EACpE,YAAa,KAAK,iBAAiBF,CAAM,EACzC,GAAIF,EAAO,SACX,KAAME,EAAO,kBACb,UAAW,KAAK,UAClB,EACA,SAAO,SAAMC,CAAe,CAC9B,OAASJ,EAAO,CACd,SAAO,QAAK,IAAI,yBAAuBA,CAAc,CAAC,CACxD,CACF,CAEA,MAAM,WAAWC,EAEmB,CAClC,KAAK,OAAO,MAAM,YAAY,EAC9B,MAAMC,EAAmBD,EAAO,gBAAgB,GAChD,GAAI,CAEF,OADyB,MAAM,KAAK,WAAW,WAAWC,CAAQ,KAM3D,SAAM,MAAS,KAJb,QACL,IAAI,kBAAgB,IAAI,MAAM,wBAAwBA,CAAQ,EAAE,CAAC,CACnE,CAGJ,OAASF,EAAO,CACd,SAAO,QAAK,IAAI,kBAAgBA,CAAc,CAAC,CACjD,CACF,CAEA,MAAM,SACJE,EACAI,EACAC,EACAF,EACyC,CACzC,KAAK,OAAO,MAAM,MAAM,EACxB,GAAI,CACF,MAAMG,EAA4B,MAAM,KAAK,WAAW,SACtDN,EACAG,CACF,EACA,KAAK,OAAO,SAAM,qBAAkBA,CAAI,CAAC,EACzC,MAAMI,EAAe,CACnB,cACE,sBAAmBD,EAAS,SAAS,MAAM,EAAE,CAAC,GAAK,IAAI,WACzD,QACE,sBAAmBA,EAAS,SAAS,MAAM,EAAG,EAAE,CAAC,GACjD,IAAI,UACR,EACA,YAAK,OAAO,SAAM,yBAAsBC,CAAY,CAAC,KAC9C,SAAMA,CAAY,CAC3B,OAAST,EAAO,CACd,OAAAO,EAAaD,CAAoB,KAC1B,QAAK,IAAI,0BAAwBN,CAAc,CAAC,CACzD,CACF,CAEQ,uBACND,EAC6B,CAC7B,OAAOA,EAAQ,IAAKI,IAAY,CAC9B,GAAIA,EAAO,GACX,YAAa,KAAK,iBAAiBA,CAAM,EACzC,UAAW,KAAK,UAClB,EAAE,CACJ,CAEQ,iBAAiBA,EAAgB,CACvC,MAAO,CACL,GAAIA,EAAO,YACX,YAAaA,EAAO,KACpB,aAAc,GACd,mBAAoB,EACpB,uBAAwB,EACxB,cAAe,CACb,MAAO,GACT,EACA,QAAS,GACT,WAAY,IAAM,KAClB,MAAOA,EAAO,OAAST,CACzB,CACF,CACF,CAUO,MAAMN,EACX,CAACS,EAAkBC,IACnB,CAAC,CAAE,OAAAY,EAAQ,qBAAAd,CAAqB,IAC9B,IAAIV,EACFU,EACAC,GAAWa,EAAO,QAClBZ,CACF",
|
|
6
|
+
"names": ["MockserverTransport_exports", "__export", "MockTransport", "mockserverIdentifier", "mockserverTransportFactory", "__toCommonJS", "import_device_management_kit", "import_device_mockserver_client", "import_purify_ts", "import_rxjs", "DEFAULT_MASKS", "DISCOVERY_POLL_INTERVAL_MS", "loggerServiceFactory", "mockUrl", "sessionToken", "devices", "error", "params", "deviceId", "device", "connectedDevice", "apdu", "onDisconnectDeviceId", "onDisconnect", "response", "apduResponse", "config"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var u=require("@ledgerhq/device-management-kit"),l=require("@ledgerhq/device-mockserver-client"),r=require("rxjs"),n=require("./MockserverTransport");vi.mock("@ledgerhq/device-mockserver-client",()=>({MockClient:vi.fn()}));const m=()=>({debug:vi.fn(),info:vi.fn(),warn:vi.fn(),error:vi.fn()}),o={config:{mockUrl:"http://localhost:8080"},loggerServiceFactory:m},a=(t={})=>({id:"device-1",name:"Ledger Nano X",device_type:"nanoX",connectivity_type:"USB",...t}),d=t=>{const e=vi.fn(t);return vi.mocked(l.MockClient).mockImplementation(()=>({listDevices:e})),e},i=(t={})=>{const e={listDevices:vi.fn(()=>Promise.resolve([])),connect:vi.fn(),disconnect:vi.fn(),sendApdu:vi.fn(),...t};return vi.mocked(l.MockClient).mockImplementation(()=>e),e};describe("mockserverTransportFactory",()=>{beforeEach(()=>{vi.clearAllMocks(),d(()=>Promise.resolve([]))}),it("builds a supported transport with the mockserver identifier",()=>{const t=(0,n.mockserverTransportFactory)("http://localhost:8080")(o);expect(t.getIdentifier()).toBe(n.mockserverIdentifier),expect(t.isSupported()).toBe(!0)}),it("accepts an injected session token without throwing",()=>{expect(()=>(0,n.mockserverTransportFactory)("http://localhost:8080","tok-123")(o)).not.toThrow()}),describe("listenToAvailableDevices",()=>{it("emits the mock server devices mapped as discovered devices",async()=>{d(()=>Promise.resolve([a(),a({id:"device-2",name:"Stax"})]));const t=(0,n.mockserverTransportFactory)("http://localhost:8080")(o),e=await(0,r.firstValueFrom)(t.listenToAvailableDevices());expect(e).toEqual([expect.objectContaining({id:"device-1",transport:n.mockserverIdentifier}),expect.objectContaining({id:"device-2",transport:n.mockserverIdentifier})])}),it("keeps polling so newly added devices appear",async()=>{d(()=>Promise.resolve([])).mockResolvedValueOnce([]).mockResolvedValueOnce([a()]);const e=(0,n.mockserverTransportFactory)("http://localhost:8080")(o),c=await(0,r.firstValueFrom)(e.listenToAvailableDevices().pipe((0,r.take)(2),(0,r.toArray)()));expect(c[0]).toEqual([]),expect(c[1]).toEqual([expect.objectContaining({id:"device-1"})])}),it("emits an empty list when the mock server is unreachable",async()=>{d(()=>Promise.reject(new Error("offline")));const t=(0,n.mockserverTransportFactory)("http://localhost:8080")(o),e=await(0,r.firstValueFrom)(t.listenToAvailableDevices());expect(e).toEqual([])})}),describe("startDiscovering",()=>{it("emits each device returned by the mock server",async()=>{i({listDevices:vi.fn(()=>Promise.resolve([a(),a({id:"device-2",name:"Stax"})]))});const t=(0,n.mockserverTransportFactory)("http://localhost:8080")(o),e=await(0,r.firstValueFrom)(t.startDiscovering().pipe((0,r.toArray)()));expect(e).toEqual([expect.objectContaining({id:"device-1"}),expect.objectContaining({id:"device-2"})])})}),describe("stopDiscovering",()=>{it("does nothing and does not throw",()=>{i();const t=(0,n.mockserverTransportFactory)("http://localhost:8080")(o);expect(()=>t.stopDiscovering()).not.toThrow()})}),describe("connect",()=>{it("returns a connected device on success",async()=>{i({connect:vi.fn(()=>Promise.resolve({device:a({masks:[305419896]}),connected:!0}))});const t=(0,n.mockserverTransportFactory)("http://localhost:8080")(o),e=vi.fn(),c=await t.connect({deviceId:"device-1",onDisconnect:e});expect(c.isRight()).toBe(!0);const s=c.unsafeCoerce();expect(s.id).toBe("device-1"),expect(s.transport).toBe(n.mockserverIdentifier),expect(s.deviceModel.masks).toEqual([305419896])}),it("falls back to the default masks when the device has none",async()=>{i({connect:vi.fn(()=>Promise.resolve({device:a(),connected:!0}))});const e=await(0,n.mockserverTransportFactory)("http://localhost:8080")(o).connect({deviceId:"device-1",onDisconnect:vi.fn()});expect(e.unsafeCoerce().deviceModel.masks).toEqual([823132160]),expect(e.unsafeCoerce().deviceModel.getBlockSize({firmwareVersion:"1.0.0"})).toBe(32)}),it("returns an OpeningConnectionError when the client throws",async()=>{i({connect:vi.fn(()=>Promise.reject(new Error("nope")))});const e=await(0,n.mockserverTransportFactory)("http://localhost:8080")(o).connect({deviceId:"device-1",onDisconnect:vi.fn()});expect(e.isLeft()).toBe(!0),expect(e.swap().unsafeCoerce()).toBeInstanceOf(u.OpeningConnectionError)})}),describe("disconnect",()=>{const t={id:"device-1"};it("returns Right(undefined) when the client disconnects successfully",async()=>{const e=vi.fn(()=>Promise.resolve(!0));i({disconnect:e});const s=await(0,n.mockserverTransportFactory)("http://localhost:8080")(o).disconnect({connectedDevice:t});expect(e).toHaveBeenCalledWith("device-1"),expect(s.isRight()).toBe(!0)}),it("returns a DisconnectError when the client reports failure",async()=>{i({disconnect:vi.fn(()=>Promise.resolve(!1))});const c=await(0,n.mockserverTransportFactory)("http://localhost:8080")(o).disconnect({connectedDevice:t});expect(c.isLeft()).toBe(!0)}),it("returns a DisconnectError when the client throws",async()=>{i({disconnect:vi.fn(()=>Promise.reject(new Error("boom")))});const c=await(0,n.mockserverTransportFactory)("http://localhost:8080")(o).disconnect({connectedDevice:t});expect(c.isLeft()).toBe(!0)})}),describe("sendApdu",()=>{const t=e=>(i({connect:vi.fn(()=>Promise.resolve({device:a(),connected:!0})),sendApdu:e}),(0,n.mockserverTransportFactory)("http://localhost:8080")(o));it("splits the response into data and status code",async()=>{const e=vi.fn(()=>Promise.resolve({response:"aabb9000"})),c=t(e),s=vi.fn(),p=await(await c.connect({deviceId:"device-1",onDisconnect:s})).unsafeCoerce().sendApdu(Uint8Array.from([224,1,0,0]));expect(p.isRight()).toBe(!0);const v=p.unsafeCoerce();expect(Array.from(v.data)).toEqual([170,187]),expect(Array.from(v.statusCode)).toEqual([144,0]),expect(s).not.toHaveBeenCalled()}),it("calls onDisconnect and returns a NoAccessibleDeviceError on failure",async()=>{const e=vi.fn(()=>Promise.reject(new Error("device lost"))),c=t(e),s=vi.fn(),p=await(await c.connect({deviceId:"device-1",onDisconnect:s})).unsafeCoerce().sendApdu(Uint8Array.from([224,1,0,0]));expect(p.isLeft()).toBe(!0),expect(s).toHaveBeenCalledWith("device-1")})}),describe("mockserverTransportFactory",()=>{it("uses the config mock url when no explicit url is provided",()=>{const t=i();expect(t).toBeDefined(),(0,n.mockserverTransportFactory)()(o),expect(l.MockClient).toHaveBeenCalledWith("http://localhost:8080",expect.anything())}),it("prefers an explicit url over the config mock url",()=>{i(),(0,n.mockserverTransportFactory)("https://explicit:9090")(o),expect(l.MockClient).toHaveBeenCalledWith("https://explicit:9090",expect.anything())})})});
|
|
2
|
+
//# sourceMappingURL=MockserverTransport.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/MockserverTransport.test.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n type LoggerPublisherService,\n OpeningConnectionError,\n type TransportConnectedDevice,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Device, MockClient } from \"@ledgerhq/device-mockserver-client\";\nimport { firstValueFrom, take, toArray } from \"rxjs\";\n\nimport {\n mockserverIdentifier,\n mockserverTransportFactory,\n} from \"./MockserverTransport\";\n\nvi.mock(\"@ledgerhq/device-mockserver-client\", () => ({\n MockClient: vi.fn(),\n}));\n\ntype TransportArgs = Parameters<\n ReturnType<typeof mockserverTransportFactory>\n>[0];\n\nconst loggerServiceFactory = (): LoggerPublisherService =>\n ({\n debug: vi.fn(),\n info: vi.fn(),\n warn: vi.fn(),\n error: vi.fn(),\n }) as unknown as LoggerPublisherService;\n\nconst transportArgs = {\n config: { mockUrl: \"http://localhost:8080\" },\n loggerServiceFactory,\n} as unknown as TransportArgs;\n\nconst aDevice = (overrides: Partial<Device> = {}): Device => ({\n id: \"device-1\",\n name: \"Ledger Nano X\",\n device_type: \"nanoX\",\n connectivity_type: \"USB\",\n ...overrides,\n});\n\nconst mockListDevices = (\n impl: () => Promise<Device[]>,\n): ReturnType<typeof vi.fn> => {\n const listDevices = vi.fn(impl);\n vi.mocked(MockClient).mockImplementation(\n () => ({ listDevices }) as unknown as MockClient,\n );\n return listDevices;\n};\n\nconst mockClientImpl = (overrides: Partial<Record<string, unknown>> = {}) => {\n const client = {\n listDevices: vi.fn(() => Promise.resolve([])),\n connect: vi.fn(),\n disconnect: vi.fn(),\n sendApdu: vi.fn(),\n ...overrides,\n };\n vi.mocked(MockClient).mockImplementation(\n () => client as unknown as MockClient,\n );\n return client;\n};\n\ndescribe(\"mockserverTransportFactory\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n mockListDevices(() => Promise.resolve([]));\n });\n\n it(\"builds a supported transport with the mockserver identifier\", () => {\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n expect(transport.getIdentifier()).toBe(mockserverIdentifier);\n expect(transport.isSupported()).toBe(true);\n });\n\n it(\"accepts an injected session token without throwing\", () => {\n expect(() =>\n mockserverTransportFactory(\n \"http://localhost:8080\",\n \"tok-123\",\n )(transportArgs),\n ).not.toThrow();\n });\n\n describe(\"listenToAvailableDevices\", () => {\n it(\"emits the mock server devices mapped as discovered devices\", async () => {\n mockListDevices(() =>\n Promise.resolve([aDevice(), aDevice({ id: \"device-2\", name: \"Stax\" })]),\n );\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const devices = await firstValueFrom(\n transport.listenToAvailableDevices(),\n );\n\n expect(devices).toEqual([\n expect.objectContaining({\n id: \"device-1\",\n transport: mockserverIdentifier,\n }),\n expect.objectContaining({\n id: \"device-2\",\n transport: mockserverIdentifier,\n }),\n ]);\n });\n\n it(\"keeps polling so newly added devices appear\", async () => {\n const listDevices = mockListDevices(() => Promise.resolve([]));\n listDevices.mockResolvedValueOnce([]).mockResolvedValueOnce([aDevice()]);\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const emissions = await firstValueFrom(\n transport.listenToAvailableDevices().pipe(take(2), toArray()),\n );\n\n expect(emissions[0]).toEqual([]);\n expect(emissions[1]).toEqual([\n expect.objectContaining({ id: \"device-1\" }),\n ]);\n });\n\n it(\"emits an empty list when the mock server is unreachable\", async () => {\n mockListDevices(() => Promise.reject(new Error(\"offline\")));\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const devices = await firstValueFrom(\n transport.listenToAvailableDevices(),\n );\n\n expect(devices).toEqual([]);\n });\n });\n\n describe(\"startDiscovering\", () => {\n it(\"emits each device returned by the mock server\", async () => {\n mockClientImpl({\n listDevices: vi.fn(() =>\n Promise.resolve([\n aDevice(),\n aDevice({ id: \"device-2\", name: \"Stax\" }),\n ]),\n ),\n });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const devices = await firstValueFrom(\n transport.startDiscovering().pipe(toArray()),\n );\n\n expect(devices).toEqual([\n expect.objectContaining({ id: \"device-1\" }),\n expect.objectContaining({ id: \"device-2\" }),\n ]);\n });\n });\n\n describe(\"stopDiscovering\", () => {\n it(\"does nothing and does not throw\", () => {\n mockClientImpl();\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n expect(() => transport.stopDiscovering()).not.toThrow();\n });\n });\n\n describe(\"connect\", () => {\n it(\"returns a connected device on success\", async () => {\n mockClientImpl({\n connect: vi.fn(() =>\n Promise.resolve({\n device: aDevice({ masks: [0x12345678] }),\n connected: true,\n }),\n ),\n });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n const onDisconnect = vi.fn();\n\n const result = await transport.connect({\n deviceId: \"device-1\",\n onDisconnect,\n });\n\n expect(result.isRight()).toBe(true);\n const connected = result.unsafeCoerce();\n expect(connected.id).toBe(\"device-1\");\n expect(connected.transport).toBe(mockserverIdentifier);\n expect(connected.deviceModel.masks).toEqual([0x12345678]);\n });\n\n it(\"falls back to the default masks when the device has none\", async () => {\n mockClientImpl({\n connect: vi.fn(() =>\n Promise.resolve({ device: aDevice(), connected: true }),\n ),\n });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const result = await transport.connect({\n deviceId: \"device-1\",\n onDisconnect: vi.fn(),\n });\n\n expect(result.unsafeCoerce().deviceModel.masks).toEqual([0x31100000]);\n expect(\n result.unsafeCoerce().deviceModel.getBlockSize({\n firmwareVersion: \"1.0.0\",\n }),\n ).toBe(32);\n });\n\n it(\"returns an OpeningConnectionError when the client throws\", async () => {\n mockClientImpl({\n connect: vi.fn(() => Promise.reject(new Error(\"nope\"))),\n });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const result = await transport.connect({\n deviceId: \"device-1\",\n onDisconnect: vi.fn(),\n });\n\n expect(result.isLeft()).toBe(true);\n expect(result.swap().unsafeCoerce()).toBeInstanceOf(\n OpeningConnectionError,\n );\n });\n });\n\n describe(\"disconnect\", () => {\n const connectedDevice = {\n id: \"device-1\",\n } as TransportConnectedDevice;\n\n it(\"returns Right(undefined) when the client disconnects successfully\", async () => {\n const disconnect = vi.fn(() => Promise.resolve(true));\n mockClientImpl({ disconnect });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const result = await transport.disconnect({ connectedDevice });\n\n expect(disconnect).toHaveBeenCalledWith(\"device-1\");\n expect(result.isRight()).toBe(true);\n });\n\n it(\"returns a DisconnectError when the client reports failure\", async () => {\n mockClientImpl({ disconnect: vi.fn(() => Promise.resolve(false)) });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const result = await transport.disconnect({ connectedDevice });\n\n expect(result.isLeft()).toBe(true);\n });\n\n it(\"returns a DisconnectError when the client throws\", async () => {\n mockClientImpl({\n disconnect: vi.fn(() => Promise.reject(new Error(\"boom\"))),\n });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const result = await transport.disconnect({ connectedDevice });\n\n expect(result.isLeft()).toBe(true);\n });\n });\n\n describe(\"sendApdu\", () => {\n const buildConnectedTransport = (sendApdu: ReturnType<typeof vi.fn>) => {\n mockClientImpl({\n connect: vi.fn(() =>\n Promise.resolve({ device: aDevice(), connected: true }),\n ),\n sendApdu,\n });\n return mockserverTransportFactory(\"http://localhost:8080\")(transportArgs);\n };\n\n it(\"splits the response into data and status code\", async () => {\n const sendApdu = vi.fn(() => Promise.resolve({ response: \"aabb9000\" }));\n const transport = buildConnectedTransport(sendApdu);\n const onDisconnect = vi.fn();\n const connected = (\n await transport.connect({ deviceId: \"device-1\", onDisconnect })\n ).unsafeCoerce();\n\n const result = await connected.sendApdu(\n Uint8Array.from([0xe0, 0x01, 0x00, 0x00]),\n );\n\n expect(result.isRight()).toBe(true);\n const apduResponse = result.unsafeCoerce();\n expect(Array.from(apduResponse.data)).toEqual([0xaa, 0xbb]);\n expect(Array.from(apduResponse.statusCode)).toEqual([0x90, 0x00]);\n expect(onDisconnect).not.toHaveBeenCalled();\n });\n\n it(\"calls onDisconnect and returns a NoAccessibleDeviceError on failure\", async () => {\n const sendApdu = vi.fn(() => Promise.reject(new Error(\"device lost\")));\n const transport = buildConnectedTransport(sendApdu);\n const onDisconnect = vi.fn();\n const connected = (\n await transport.connect({ deviceId: \"device-1\", onDisconnect })\n ).unsafeCoerce();\n\n const result = await connected.sendApdu(\n Uint8Array.from([0xe0, 0x01, 0x00, 0x00]),\n );\n\n expect(result.isLeft()).toBe(true);\n expect(onDisconnect).toHaveBeenCalledWith(\"device-1\");\n });\n });\n\n describe(\"mockserverTransportFactory\", () => {\n it(\"uses the config mock url when no explicit url is provided\", () => {\n const client = mockClientImpl();\n expect(client).toBeDefined();\n\n mockserverTransportFactory()(transportArgs);\n\n expect(MockClient).toHaveBeenCalledWith(\n \"http://localhost:8080\",\n expect.anything(),\n );\n });\n\n it(\"prefers an explicit url over the config mock url\", () => {\n mockClientImpl();\n\n mockserverTransportFactory(\"https://explicit:9090\")(transportArgs);\n\n expect(MockClient).toHaveBeenCalledWith(\n \"https://explicit:9090\",\n expect.anything(),\n );\n });\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["import_device_management_kit", "import_device_mockserver_client", "import_rxjs", "import_MockserverTransport", "loggerServiceFactory", "transportArgs", "aDevice", "overrides", "mockListDevices", "impl", "listDevices", "mockClientImpl", "client", "transport", "devices", "emissions", "onDisconnect", "result", "connected", "connectedDevice", "disconnect", "buildConnectedTransport", "sendApdu", "apduResponse"]
|
|
7
|
+
}
|
package/lib/cjs/package.json
CHANGED
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"watch:builds": "pnpm ldmk-tool watch --entryPoints src/index.ts,src/**/*.ts --tsconfig tsconfig.prod.json",
|
|
57
57
|
"watch:types": "concurrently \"tsc --watch -p tsconfig.prod.json\" \"tsc-alias --watch -p tsconfig.prod.json\""
|
|
58
58
|
},
|
|
59
|
-
"version": "0.0.0-develop-
|
|
59
|
+
"version": "0.0.0-develop-20260624002956"
|
|
60
60
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{DisconnectError as p,formatApduReceivedLog as g,formatApduSentLog as u,NoAccessibleDeviceError as
|
|
1
|
+
import{DisconnectError as p,formatApduReceivedLog as g,formatApduSentLog as u,hexaStringToBuffer as v,NoAccessibleDeviceError as D,OpeningConnectionError as m}from"@ledgerhq/device-management-kit";import{MockClient as h}from"@ledgerhq/device-mockserver-client";import{Left as n,Right as s}from"purify-ts";import{catchError as y,from as c,map as l,mergeMap as b,of as T,switchMap as f,timer as E}from"rxjs";const I="MOCKSERVER",k=[823132160],C=1e3;class A{logger;mockClient;identifier=I;constructor(e,r,t){this.logger=e("MockTransport"),this.mockClient=new h(r,{token:t})}isSupported(){return!0}getIdentifier(){return this.identifier}listenToAvailableDevices(){return this.logger.debug("listenToAvailableDevices"),E(0,C).pipe(f(()=>c(this.mockClient.listDevices())),l(e=>this.mapToDiscoveredDevices(e)),y(e=>(this.logger.error("listenToAvailableDevices failed",{data:{error:e}}),T([]))))}startDiscovering(){return this.logger.debug("startDiscovering"),c(this.mockClient.listDevices()).pipe(l(e=>this.mapToDiscoveredDevices(e)),b(e=>c(e)))}stopDiscovering(){this.logger.debug("stopDiscovering")}async connect(e){this.logger.debug("connect");const r=e.deviceId;try{const{device:t}=await this.mockClient.connect(r),o={sendApdu:i=>this.sendApdu(r,e.deviceId,e.onDisconnect,i),deviceModel:this.buildDeviceModel(t),id:e.deviceId,type:t.connectivity_type,transport:this.identifier};return s(o)}catch(t){return n(new m(t))}}async disconnect(e){this.logger.debug("disconnect");const r=e.connectedDevice.id;try{return await this.mockClient.disconnect(r)?s(void 0):n(new p(new Error(`Failed to disconnect ${r}`)))}catch(t){return n(new p(t))}}async sendApdu(e,r,t,o){this.logger.debug("send");try{const i=await this.mockClient.sendApdu(e,o);this.logger.debug(u(o));const a={statusCode:v(i.response.slice(-4))??new Uint8Array,data:v(i.response.slice(0,-4))??new Uint8Array};return this.logger.debug(g(a)),s(a)}catch(i){return t(r),n(new D(i))}}mapToDiscoveredDevices(e){return e.map(r=>({id:r.id,deviceModel:this.buildDeviceModel(r),transport:this.identifier}))}buildDeviceModel(e){return{id:e.device_type,productName:e.name,usbProductId:16,legacyUsbProductId:1,bootloaderUsbProductId:1,getBlockSize(){return 32},usbOnly:!0,memorySize:320*1024,masks:e.masks??k}}}const L=(d,e)=>({config:r,loggerServiceFactory:t})=>new A(t,d??r.mockUrl,e);export{A as MockTransport,I as mockserverIdentifier,L as mockserverTransportFactory};
|
|
2
2
|
//# sourceMappingURL=MockserverTransport.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/MockserverTransport.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n type ApduResponse,\n type ConnectError,\n type DeviceId,\n type DeviceModelId,\n DisconnectError,\n type DisconnectHandler,\n type
|
|
5
|
-
"mappings": "AAAA,OAKE,mBAAAA,
|
|
6
|
-
"names": ["DisconnectError", "formatApduReceivedLog", "formatApduSentLog", "NoAccessibleDeviceError", "OpeningConnectionError", "MockClient", "Left", "Right", "from", "mergeMap", "mockserverIdentifier", "MockTransport", "loggerServiceFactory", "
|
|
4
|
+
"sourcesContent": ["import {\n type ApduResponse,\n type ConnectError,\n type DeviceId,\n type DeviceModelId,\n DisconnectError,\n type DisconnectHandler,\n type DmkError,\n formatApduReceivedLog,\n formatApduSentLog,\n hexaStringToBuffer,\n type LoggerPublisherService,\n NoAccessibleDeviceError,\n OpeningConnectionError,\n type Transport,\n type TransportConnectedDevice,\n type TransportDiscoveredDevice,\n type TransportFactory,\n type TransportIdentifier,\n} from \"@ledgerhq/device-management-kit\";\nimport {\n type CommandResponse,\n type Device,\n MockClient,\n} from \"@ledgerhq/device-mockserver-client\";\nimport { type Either, Left, Right } from \"purify-ts\";\nimport {\n catchError,\n from,\n map,\n mergeMap,\n type Observable,\n of,\n switchMap,\n timer,\n} from \"rxjs\";\n\nexport const mockserverIdentifier: TransportIdentifier = \"MOCKSERVER\";\n\nconst DEFAULT_MASKS = [0x31100000];\n\n/** Interval (ms) at which the mock server is polled for available devices. */\nconst DISCOVERY_POLL_INTERVAL_MS = 1000;\n\nexport class MockTransport implements Transport {\n private logger: LoggerPublisherService;\n private mockClient: MockClient;\n private readonly identifier: TransportIdentifier = mockserverIdentifier;\n\n constructor(\n loggerServiceFactory: (tag: string) => LoggerPublisherService,\n mockUrl: string,\n sessionToken?: string,\n ) {\n this.logger = loggerServiceFactory(\"MockTransport\");\n this.mockClient = new MockClient(mockUrl, { token: sessionToken });\n }\n\n isSupported(): boolean {\n return true;\n }\n\n getIdentifier(): TransportIdentifier {\n return this.identifier;\n }\n\n listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]> {\n this.logger.debug(\"listenToAvailableDevices\");\n return timer(0, DISCOVERY_POLL_INTERVAL_MS).pipe(\n switchMap(() => from(this.mockClient.listDevices())),\n map((devices) => this.mapToDiscoveredDevices(devices)),\n catchError((error) => {\n this.logger.error(\"listenToAvailableDevices failed\", {\n data: { error },\n });\n return of([]);\n }),\n );\n }\n\n startDiscovering(): Observable<TransportDiscoveredDevice> {\n this.logger.debug(\"startDiscovering\");\n return from(this.mockClient.listDevices()).pipe(\n map((devices) => this.mapToDiscoveredDevices(devices)),\n mergeMap((devices) => from(devices)),\n );\n }\n\n stopDiscovering(): void {\n //DO NOTHING HERE\n this.logger.debug(\"stopDiscovering\");\n }\n\n async connect(params: {\n deviceId: DeviceId;\n onDisconnect: DisconnectHandler;\n }): Promise<Either<ConnectError, TransportConnectedDevice>> {\n this.logger.debug(\"connect\");\n const deviceId: string = params.deviceId;\n try {\n const { device } = await this.mockClient.connect(deviceId);\n const connectedDevice = {\n sendApdu: (apdu) =>\n this.sendApdu(deviceId, params.deviceId, params.onDisconnect, apdu),\n deviceModel: this.buildDeviceModel(device),\n id: params.deviceId,\n type: device.connectivity_type,\n transport: this.identifier,\n } as TransportConnectedDevice;\n return Right(connectedDevice);\n } catch (error) {\n return Left(new OpeningConnectionError(error as Error));\n }\n }\n\n async disconnect(params: {\n connectedDevice: TransportConnectedDevice;\n }): Promise<Either<DmkError, void>> {\n this.logger.debug(\"disconnect\");\n const deviceId: string = params.connectedDevice.id;\n try {\n const success: boolean = await this.mockClient.disconnect(deviceId);\n if (!success) {\n return Left(\n new DisconnectError(new Error(`Failed to disconnect ${deviceId}`)),\n );\n }\n return Right(undefined);\n } catch (error) {\n return Left(new DisconnectError(error as Error));\n }\n }\n\n async sendApdu(\n deviceId: string,\n onDisconnectDeviceId: DeviceId,\n onDisconnect: DisconnectHandler,\n apdu: Uint8Array,\n ): Promise<Either<DmkError, ApduResponse>> {\n this.logger.debug(\"send\");\n try {\n const response: CommandResponse = await this.mockClient.sendApdu(\n deviceId,\n apdu,\n );\n this.logger.debug(formatApduSentLog(apdu));\n const apduResponse = {\n statusCode:\n hexaStringToBuffer(response.response.slice(-4)) ?? new Uint8Array(),\n data:\n hexaStringToBuffer(response.response.slice(0, -4)) ??\n new Uint8Array(),\n } as ApduResponse;\n this.logger.debug(formatApduReceivedLog(apduResponse));\n return Right(apduResponse);\n } catch (error) {\n onDisconnect(onDisconnectDeviceId);\n return Left(new NoAccessibleDeviceError(error as Error));\n }\n }\n\n private mapToDiscoveredDevices(\n devices: Device[],\n ): TransportDiscoveredDevice[] {\n return devices.map((device) => ({\n id: device.id,\n deviceModel: this.buildDeviceModel(device),\n transport: this.identifier,\n }));\n }\n\n private buildDeviceModel(device: Device) {\n return {\n id: device.device_type as DeviceModelId,\n productName: device.name,\n usbProductId: 0x10,\n legacyUsbProductId: 0x0001,\n bootloaderUsbProductId: 0x0001,\n getBlockSize() {\n return 32;\n },\n usbOnly: true,\n memorySize: 320 * 1024,\n masks: device.masks ?? DEFAULT_MASKS,\n };\n }\n}\n\n/**\n * Build a mock-server transport factory.\n *\n * @param mockUrl Optional mock server URL. When omitted, `config.mockUrl` from\n * the DMK configuration is used.\n * @param sessionToken Optional bearer token to share an existing session\n * When omitted, the client self-provisions a session via /auth.\n */\nexport const mockserverTransportFactory =\n (mockUrl?: string, sessionToken?: string): TransportFactory =>\n ({ config, loggerServiceFactory }) =>\n new MockTransport(\n loggerServiceFactory,\n mockUrl ?? config.mockUrl,\n sessionToken,\n );\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["DisconnectError", "formatApduReceivedLog", "formatApduSentLog", "hexaStringToBuffer", "NoAccessibleDeviceError", "OpeningConnectionError", "MockClient", "Left", "Right", "catchError", "from", "map", "mergeMap", "of", "switchMap", "timer", "mockserverIdentifier", "DEFAULT_MASKS", "DISCOVERY_POLL_INTERVAL_MS", "MockTransport", "loggerServiceFactory", "mockUrl", "sessionToken", "devices", "error", "params", "deviceId", "device", "connectedDevice", "apdu", "onDisconnectDeviceId", "onDisconnect", "response", "apduResponse", "mockserverTransportFactory", "config"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{OpeningConnectionError as f}from"@ledgerhq/device-management-kit";import{MockClient as v}from"@ledgerhq/device-mockserver-client";import{firstValueFrom as l,take as x,toArray as h}from"rxjs";import{mockserverIdentifier as p,mockserverTransportFactory as n}from"./MockserverTransport";vi.mock("@ledgerhq/device-mockserver-client",()=>({MockClient:vi.fn()}));const k=()=>({debug:vi.fn(),info:vi.fn(),warn:vi.fn(),error:vi.fn()}),o={config:{mockUrl:"http://localhost:8080"},loggerServiceFactory:k},r=(t={})=>({id:"device-1",name:"Ledger Nano X",device_type:"nanoX",connectivity_type:"USB",...t}),d=t=>{const e=vi.fn(t);return vi.mocked(v).mockImplementation(()=>({listDevices:e})),e},i=(t={})=>{const e={listDevices:vi.fn(()=>Promise.resolve([])),connect:vi.fn(),disconnect:vi.fn(),sendApdu:vi.fn(),...t};return vi.mocked(v).mockImplementation(()=>e),e};describe("mockserverTransportFactory",()=>{beforeEach(()=>{vi.clearAllMocks(),d(()=>Promise.resolve([]))}),it("builds a supported transport with the mockserver identifier",()=>{const t=n("http://localhost:8080")(o);expect(t.getIdentifier()).toBe(p),expect(t.isSupported()).toBe(!0)}),it("accepts an injected session token without throwing",()=>{expect(()=>n("http://localhost:8080","tok-123")(o)).not.toThrow()}),describe("listenToAvailableDevices",()=>{it("emits the mock server devices mapped as discovered devices",async()=>{d(()=>Promise.resolve([r(),r({id:"device-2",name:"Stax"})]));const t=n("http://localhost:8080")(o),e=await l(t.listenToAvailableDevices());expect(e).toEqual([expect.objectContaining({id:"device-1",transport:p}),expect.objectContaining({id:"device-2",transport:p})])}),it("keeps polling so newly added devices appear",async()=>{d(()=>Promise.resolve([])).mockResolvedValueOnce([]).mockResolvedValueOnce([r()]);const e=n("http://localhost:8080")(o),c=await l(e.listenToAvailableDevices().pipe(x(2),h()));expect(c[0]).toEqual([]),expect(c[1]).toEqual([expect.objectContaining({id:"device-1"})])}),it("emits an empty list when the mock server is unreachable",async()=>{d(()=>Promise.reject(new Error("offline")));const t=n("http://localhost:8080")(o),e=await l(t.listenToAvailableDevices());expect(e).toEqual([])})}),describe("startDiscovering",()=>{it("emits each device returned by the mock server",async()=>{i({listDevices:vi.fn(()=>Promise.resolve([r(),r({id:"device-2",name:"Stax"})]))});const t=n("http://localhost:8080")(o),e=await l(t.startDiscovering().pipe(h()));expect(e).toEqual([expect.objectContaining({id:"device-1"}),expect.objectContaining({id:"device-2"})])})}),describe("stopDiscovering",()=>{it("does nothing and does not throw",()=>{i();const t=n("http://localhost:8080")(o);expect(()=>t.stopDiscovering()).not.toThrow()})}),describe("connect",()=>{it("returns a connected device on success",async()=>{i({connect:vi.fn(()=>Promise.resolve({device:r({masks:[305419896]}),connected:!0}))});const t=n("http://localhost:8080")(o),e=vi.fn(),c=await t.connect({deviceId:"device-1",onDisconnect:e});expect(c.isRight()).toBe(!0);const s=c.unsafeCoerce();expect(s.id).toBe("device-1"),expect(s.transport).toBe(p),expect(s.deviceModel.masks).toEqual([305419896])}),it("falls back to the default masks when the device has none",async()=>{i({connect:vi.fn(()=>Promise.resolve({device:r(),connected:!0}))});const e=await n("http://localhost:8080")(o).connect({deviceId:"device-1",onDisconnect:vi.fn()});expect(e.unsafeCoerce().deviceModel.masks).toEqual([823132160]),expect(e.unsafeCoerce().deviceModel.getBlockSize({firmwareVersion:"1.0.0"})).toBe(32)}),it("returns an OpeningConnectionError when the client throws",async()=>{i({connect:vi.fn(()=>Promise.reject(new Error("nope")))});const e=await n("http://localhost:8080")(o).connect({deviceId:"device-1",onDisconnect:vi.fn()});expect(e.isLeft()).toBe(!0),expect(e.swap().unsafeCoerce()).toBeInstanceOf(f)})}),describe("disconnect",()=>{const t={id:"device-1"};it("returns Right(undefined) when the client disconnects successfully",async()=>{const e=vi.fn(()=>Promise.resolve(!0));i({disconnect:e});const s=await n("http://localhost:8080")(o).disconnect({connectedDevice:t});expect(e).toHaveBeenCalledWith("device-1"),expect(s.isRight()).toBe(!0)}),it("returns a DisconnectError when the client reports failure",async()=>{i({disconnect:vi.fn(()=>Promise.resolve(!1))});const c=await n("http://localhost:8080")(o).disconnect({connectedDevice:t});expect(c.isLeft()).toBe(!0)}),it("returns a DisconnectError when the client throws",async()=>{i({disconnect:vi.fn(()=>Promise.reject(new Error("boom")))});const c=await n("http://localhost:8080")(o).disconnect({connectedDevice:t});expect(c.isLeft()).toBe(!0)})}),describe("sendApdu",()=>{const t=e=>(i({connect:vi.fn(()=>Promise.resolve({device:r(),connected:!0})),sendApdu:e}),n("http://localhost:8080")(o));it("splits the response into data and status code",async()=>{const e=vi.fn(()=>Promise.resolve({response:"aabb9000"})),c=t(e),s=vi.fn(),a=await(await c.connect({deviceId:"device-1",onDisconnect:s})).unsafeCoerce().sendApdu(Uint8Array.from([224,1,0,0]));expect(a.isRight()).toBe(!0);const u=a.unsafeCoerce();expect(Array.from(u.data)).toEqual([170,187]),expect(Array.from(u.statusCode)).toEqual([144,0]),expect(s).not.toHaveBeenCalled()}),it("calls onDisconnect and returns a NoAccessibleDeviceError on failure",async()=>{const e=vi.fn(()=>Promise.reject(new Error("device lost"))),c=t(e),s=vi.fn(),a=await(await c.connect({deviceId:"device-1",onDisconnect:s})).unsafeCoerce().sendApdu(Uint8Array.from([224,1,0,0]));expect(a.isLeft()).toBe(!0),expect(s).toHaveBeenCalledWith("device-1")})}),describe("mockserverTransportFactory",()=>{it("uses the config mock url when no explicit url is provided",()=>{const t=i();expect(t).toBeDefined(),n()(o),expect(v).toHaveBeenCalledWith("http://localhost:8080",expect.anything())}),it("prefers an explicit url over the config mock url",()=>{i(),n("https://explicit:9090")(o),expect(v).toHaveBeenCalledWith("https://explicit:9090",expect.anything())})})});
|
|
2
|
+
//# sourceMappingURL=MockserverTransport.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/MockserverTransport.test.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n type LoggerPublisherService,\n OpeningConnectionError,\n type TransportConnectedDevice,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Device, MockClient } from \"@ledgerhq/device-mockserver-client\";\nimport { firstValueFrom, take, toArray } from \"rxjs\";\n\nimport {\n mockserverIdentifier,\n mockserverTransportFactory,\n} from \"./MockserverTransport\";\n\nvi.mock(\"@ledgerhq/device-mockserver-client\", () => ({\n MockClient: vi.fn(),\n}));\n\ntype TransportArgs = Parameters<\n ReturnType<typeof mockserverTransportFactory>\n>[0];\n\nconst loggerServiceFactory = (): LoggerPublisherService =>\n ({\n debug: vi.fn(),\n info: vi.fn(),\n warn: vi.fn(),\n error: vi.fn(),\n }) as unknown as LoggerPublisherService;\n\nconst transportArgs = {\n config: { mockUrl: \"http://localhost:8080\" },\n loggerServiceFactory,\n} as unknown as TransportArgs;\n\nconst aDevice = (overrides: Partial<Device> = {}): Device => ({\n id: \"device-1\",\n name: \"Ledger Nano X\",\n device_type: \"nanoX\",\n connectivity_type: \"USB\",\n ...overrides,\n});\n\nconst mockListDevices = (\n impl: () => Promise<Device[]>,\n): ReturnType<typeof vi.fn> => {\n const listDevices = vi.fn(impl);\n vi.mocked(MockClient).mockImplementation(\n () => ({ listDevices }) as unknown as MockClient,\n );\n return listDevices;\n};\n\nconst mockClientImpl = (overrides: Partial<Record<string, unknown>> = {}) => {\n const client = {\n listDevices: vi.fn(() => Promise.resolve([])),\n connect: vi.fn(),\n disconnect: vi.fn(),\n sendApdu: vi.fn(),\n ...overrides,\n };\n vi.mocked(MockClient).mockImplementation(\n () => client as unknown as MockClient,\n );\n return client;\n};\n\ndescribe(\"mockserverTransportFactory\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n mockListDevices(() => Promise.resolve([]));\n });\n\n it(\"builds a supported transport with the mockserver identifier\", () => {\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n expect(transport.getIdentifier()).toBe(mockserverIdentifier);\n expect(transport.isSupported()).toBe(true);\n });\n\n it(\"accepts an injected session token without throwing\", () => {\n expect(() =>\n mockserverTransportFactory(\n \"http://localhost:8080\",\n \"tok-123\",\n )(transportArgs),\n ).not.toThrow();\n });\n\n describe(\"listenToAvailableDevices\", () => {\n it(\"emits the mock server devices mapped as discovered devices\", async () => {\n mockListDevices(() =>\n Promise.resolve([aDevice(), aDevice({ id: \"device-2\", name: \"Stax\" })]),\n );\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const devices = await firstValueFrom(\n transport.listenToAvailableDevices(),\n );\n\n expect(devices).toEqual([\n expect.objectContaining({\n id: \"device-1\",\n transport: mockserverIdentifier,\n }),\n expect.objectContaining({\n id: \"device-2\",\n transport: mockserverIdentifier,\n }),\n ]);\n });\n\n it(\"keeps polling so newly added devices appear\", async () => {\n const listDevices = mockListDevices(() => Promise.resolve([]));\n listDevices.mockResolvedValueOnce([]).mockResolvedValueOnce([aDevice()]);\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const emissions = await firstValueFrom(\n transport.listenToAvailableDevices().pipe(take(2), toArray()),\n );\n\n expect(emissions[0]).toEqual([]);\n expect(emissions[1]).toEqual([\n expect.objectContaining({ id: \"device-1\" }),\n ]);\n });\n\n it(\"emits an empty list when the mock server is unreachable\", async () => {\n mockListDevices(() => Promise.reject(new Error(\"offline\")));\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const devices = await firstValueFrom(\n transport.listenToAvailableDevices(),\n );\n\n expect(devices).toEqual([]);\n });\n });\n\n describe(\"startDiscovering\", () => {\n it(\"emits each device returned by the mock server\", async () => {\n mockClientImpl({\n listDevices: vi.fn(() =>\n Promise.resolve([\n aDevice(),\n aDevice({ id: \"device-2\", name: \"Stax\" }),\n ]),\n ),\n });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const devices = await firstValueFrom(\n transport.startDiscovering().pipe(toArray()),\n );\n\n expect(devices).toEqual([\n expect.objectContaining({ id: \"device-1\" }),\n expect.objectContaining({ id: \"device-2\" }),\n ]);\n });\n });\n\n describe(\"stopDiscovering\", () => {\n it(\"does nothing and does not throw\", () => {\n mockClientImpl();\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n expect(() => transport.stopDiscovering()).not.toThrow();\n });\n });\n\n describe(\"connect\", () => {\n it(\"returns a connected device on success\", async () => {\n mockClientImpl({\n connect: vi.fn(() =>\n Promise.resolve({\n device: aDevice({ masks: [0x12345678] }),\n connected: true,\n }),\n ),\n });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n const onDisconnect = vi.fn();\n\n const result = await transport.connect({\n deviceId: \"device-1\",\n onDisconnect,\n });\n\n expect(result.isRight()).toBe(true);\n const connected = result.unsafeCoerce();\n expect(connected.id).toBe(\"device-1\");\n expect(connected.transport).toBe(mockserverIdentifier);\n expect(connected.deviceModel.masks).toEqual([0x12345678]);\n });\n\n it(\"falls back to the default masks when the device has none\", async () => {\n mockClientImpl({\n connect: vi.fn(() =>\n Promise.resolve({ device: aDevice(), connected: true }),\n ),\n });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const result = await transport.connect({\n deviceId: \"device-1\",\n onDisconnect: vi.fn(),\n });\n\n expect(result.unsafeCoerce().deviceModel.masks).toEqual([0x31100000]);\n expect(\n result.unsafeCoerce().deviceModel.getBlockSize({\n firmwareVersion: \"1.0.0\",\n }),\n ).toBe(32);\n });\n\n it(\"returns an OpeningConnectionError when the client throws\", async () => {\n mockClientImpl({\n connect: vi.fn(() => Promise.reject(new Error(\"nope\"))),\n });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const result = await transport.connect({\n deviceId: \"device-1\",\n onDisconnect: vi.fn(),\n });\n\n expect(result.isLeft()).toBe(true);\n expect(result.swap().unsafeCoerce()).toBeInstanceOf(\n OpeningConnectionError,\n );\n });\n });\n\n describe(\"disconnect\", () => {\n const connectedDevice = {\n id: \"device-1\",\n } as TransportConnectedDevice;\n\n it(\"returns Right(undefined) when the client disconnects successfully\", async () => {\n const disconnect = vi.fn(() => Promise.resolve(true));\n mockClientImpl({ disconnect });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const result = await transport.disconnect({ connectedDevice });\n\n expect(disconnect).toHaveBeenCalledWith(\"device-1\");\n expect(result.isRight()).toBe(true);\n });\n\n it(\"returns a DisconnectError when the client reports failure\", async () => {\n mockClientImpl({ disconnect: vi.fn(() => Promise.resolve(false)) });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const result = await transport.disconnect({ connectedDevice });\n\n expect(result.isLeft()).toBe(true);\n });\n\n it(\"returns a DisconnectError when the client throws\", async () => {\n mockClientImpl({\n disconnect: vi.fn(() => Promise.reject(new Error(\"boom\"))),\n });\n const transport = mockserverTransportFactory(\"http://localhost:8080\")(\n transportArgs,\n );\n\n const result = await transport.disconnect({ connectedDevice });\n\n expect(result.isLeft()).toBe(true);\n });\n });\n\n describe(\"sendApdu\", () => {\n const buildConnectedTransport = (sendApdu: ReturnType<typeof vi.fn>) => {\n mockClientImpl({\n connect: vi.fn(() =>\n Promise.resolve({ device: aDevice(), connected: true }),\n ),\n sendApdu,\n });\n return mockserverTransportFactory(\"http://localhost:8080\")(transportArgs);\n };\n\n it(\"splits the response into data and status code\", async () => {\n const sendApdu = vi.fn(() => Promise.resolve({ response: \"aabb9000\" }));\n const transport = buildConnectedTransport(sendApdu);\n const onDisconnect = vi.fn();\n const connected = (\n await transport.connect({ deviceId: \"device-1\", onDisconnect })\n ).unsafeCoerce();\n\n const result = await connected.sendApdu(\n Uint8Array.from([0xe0, 0x01, 0x00, 0x00]),\n );\n\n expect(result.isRight()).toBe(true);\n const apduResponse = result.unsafeCoerce();\n expect(Array.from(apduResponse.data)).toEqual([0xaa, 0xbb]);\n expect(Array.from(apduResponse.statusCode)).toEqual([0x90, 0x00]);\n expect(onDisconnect).not.toHaveBeenCalled();\n });\n\n it(\"calls onDisconnect and returns a NoAccessibleDeviceError on failure\", async () => {\n const sendApdu = vi.fn(() => Promise.reject(new Error(\"device lost\")));\n const transport = buildConnectedTransport(sendApdu);\n const onDisconnect = vi.fn();\n const connected = (\n await transport.connect({ deviceId: \"device-1\", onDisconnect })\n ).unsafeCoerce();\n\n const result = await connected.sendApdu(\n Uint8Array.from([0xe0, 0x01, 0x00, 0x00]),\n );\n\n expect(result.isLeft()).toBe(true);\n expect(onDisconnect).toHaveBeenCalledWith(\"device-1\");\n });\n });\n\n describe(\"mockserverTransportFactory\", () => {\n it(\"uses the config mock url when no explicit url is provided\", () => {\n const client = mockClientImpl();\n expect(client).toBeDefined();\n\n mockserverTransportFactory()(transportArgs);\n\n expect(MockClient).toHaveBeenCalledWith(\n \"http://localhost:8080\",\n expect.anything(),\n );\n });\n\n it(\"prefers an explicit url over the config mock url\", () => {\n mockClientImpl();\n\n mockserverTransportFactory(\"https://explicit:9090\")(transportArgs);\n\n expect(MockClient).toHaveBeenCalledWith(\n \"https://explicit:9090\",\n expect.anything(),\n );\n });\n });\n});\n"],
|
|
5
|
+
"mappings": "AAAA,OAEE,0BAAAA,MAEK,kCACP,OAAsB,cAAAC,MAAkB,qCACxC,OAAS,kBAAAC,EAAgB,QAAAC,EAAM,WAAAC,MAAe,OAE9C,OACE,wBAAAC,EACA,8BAAAC,MACK,wBAEP,GAAG,KAAK,qCAAsC,KAAO,CACnD,WAAY,GAAG,GAAG,CACpB,EAAE,EAMF,MAAMC,EAAuB,KAC1B,CACC,MAAO,GAAG,GAAG,EACb,KAAM,GAAG,GAAG,EACZ,KAAM,GAAG,GAAG,EACZ,MAAO,GAAG,GAAG,CACf,GAEIC,EAAgB,CACpB,OAAQ,CAAE,QAAS,uBAAwB,EAC3C,qBAAAD,CACF,EAEME,EAAU,CAACC,EAA6B,CAAC,KAAe,CAC5D,GAAI,WACJ,KAAM,gBACN,YAAa,QACb,kBAAmB,MACnB,GAAGA,CACL,GAEMC,EACJC,GAC6B,CAC7B,MAAMC,EAAc,GAAG,GAAGD,CAAI,EAC9B,UAAG,OAAOX,CAAU,EAAE,mBACpB,KAAO,CAAE,YAAAY,CAAY,EACvB,EACOA,CACT,EAEMC,EAAiB,CAACJ,EAA8C,CAAC,IAAM,CAC3E,MAAMK,EAAS,CACb,YAAa,GAAG,GAAG,IAAM,QAAQ,QAAQ,CAAC,CAAC,CAAC,EAC5C,QAAS,GAAG,GAAG,EACf,WAAY,GAAG,GAAG,EAClB,SAAU,GAAG,GAAG,EAChB,GAAGL,CACL,EACA,UAAG,OAAOT,CAAU,EAAE,mBACpB,IAAMc,CACR,EACOA,CACT,EAEA,SAAS,6BAA8B,IAAM,CAC3C,WAAW,IAAM,CACf,GAAG,cAAc,EACjBJ,EAAgB,IAAM,QAAQ,QAAQ,CAAC,CAAC,CAAC,CAC3C,CAAC,EAED,GAAG,8DAA+D,IAAM,CACtE,MAAMK,EAAYV,EAA2B,uBAAuB,EAClEE,CACF,EAEA,OAAOQ,EAAU,cAAc,CAAC,EAAE,KAAKX,CAAoB,EAC3D,OAAOW,EAAU,YAAY,CAAC,EAAE,KAAK,EAAI,CAC3C,CAAC,EAED,GAAG,qDAAsD,IAAM,CAC7D,OAAO,IACLV,EACE,wBACA,SACF,EAAEE,CAAa,CACjB,EAAE,IAAI,QAAQ,CAChB,CAAC,EAED,SAAS,2BAA4B,IAAM,CACzC,GAAG,6DAA8D,SAAY,CAC3EG,EAAgB,IACd,QAAQ,QAAQ,CAACF,EAAQ,EAAGA,EAAQ,CAAE,GAAI,WAAY,KAAM,MAAO,CAAC,CAAC,CAAC,CACxE,EACA,MAAMO,EAAYV,EAA2B,uBAAuB,EAClEE,CACF,EAEMS,EAAU,MAAMf,EACpBc,EAAU,yBAAyB,CACrC,EAEA,OAAOC,CAAO,EAAE,QAAQ,CACtB,OAAO,iBAAiB,CACtB,GAAI,WACJ,UAAWZ,CACb,CAAC,EACD,OAAO,iBAAiB,CACtB,GAAI,WACJ,UAAWA,CACb,CAAC,CACH,CAAC,CACH,CAAC,EAED,GAAG,8CAA+C,SAAY,CACxCM,EAAgB,IAAM,QAAQ,QAAQ,CAAC,CAAC,CAAC,EACjD,sBAAsB,CAAC,CAAC,EAAE,sBAAsB,CAACF,EAAQ,CAAC,CAAC,EACvE,MAAMO,EAAYV,EAA2B,uBAAuB,EAClEE,CACF,EAEMU,EAAY,MAAMhB,EACtBc,EAAU,yBAAyB,EAAE,KAAKb,EAAK,CAAC,EAAGC,EAAQ,CAAC,CAC9D,EAEA,OAAOc,EAAU,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAC/B,OAAOA,EAAU,CAAC,CAAC,EAAE,QAAQ,CAC3B,OAAO,iBAAiB,CAAE,GAAI,UAAW,CAAC,CAC5C,CAAC,CACH,CAAC,EAED,GAAG,0DAA2D,SAAY,CACxEP,EAAgB,IAAM,QAAQ,OAAO,IAAI,MAAM,SAAS,CAAC,CAAC,EAC1D,MAAMK,EAAYV,EAA2B,uBAAuB,EAClEE,CACF,EAEMS,EAAU,MAAMf,EACpBc,EAAU,yBAAyB,CACrC,EAEA,OAAOC,CAAO,EAAE,QAAQ,CAAC,CAAC,CAC5B,CAAC,CACH,CAAC,EAED,SAAS,mBAAoB,IAAM,CACjC,GAAG,gDAAiD,SAAY,CAC9DH,EAAe,CACb,YAAa,GAAG,GAAG,IACjB,QAAQ,QAAQ,CACdL,EAAQ,EACRA,EAAQ,CAAE,GAAI,WAAY,KAAM,MAAO,CAAC,CAC1C,CAAC,CACH,CACF,CAAC,EACD,MAAMO,EAAYV,EAA2B,uBAAuB,EAClEE,CACF,EAEMS,EAAU,MAAMf,EACpBc,EAAU,iBAAiB,EAAE,KAAKZ,EAAQ,CAAC,CAC7C,EAEA,OAAOa,CAAO,EAAE,QAAQ,CACtB,OAAO,iBAAiB,CAAE,GAAI,UAAW,CAAC,EAC1C,OAAO,iBAAiB,CAAE,GAAI,UAAW,CAAC,CAC5C,CAAC,CACH,CAAC,CACH,CAAC,EAED,SAAS,kBAAmB,IAAM,CAChC,GAAG,kCAAmC,IAAM,CAC1CH,EAAe,EACf,MAAME,EAAYV,EAA2B,uBAAuB,EAClEE,CACF,EAEA,OAAO,IAAMQ,EAAU,gBAAgB,CAAC,EAAE,IAAI,QAAQ,CACxD,CAAC,CACH,CAAC,EAED,SAAS,UAAW,IAAM,CACxB,GAAG,wCAAyC,SAAY,CACtDF,EAAe,CACb,QAAS,GAAG,GAAG,IACb,QAAQ,QAAQ,CACd,OAAQL,EAAQ,CAAE,MAAO,CAAC,SAAU,CAAE,CAAC,EACvC,UAAW,EACb,CAAC,CACH,CACF,CAAC,EACD,MAAMO,EAAYV,EAA2B,uBAAuB,EAClEE,CACF,EACMW,EAAe,GAAG,GAAG,EAErBC,EAAS,MAAMJ,EAAU,QAAQ,CACrC,SAAU,WACV,aAAAG,CACF,CAAC,EAED,OAAOC,EAAO,QAAQ,CAAC,EAAE,KAAK,EAAI,EAClC,MAAMC,EAAYD,EAAO,aAAa,EACtC,OAAOC,EAAU,EAAE,EAAE,KAAK,UAAU,EACpC,OAAOA,EAAU,SAAS,EAAE,KAAKhB,CAAoB,EACrD,OAAOgB,EAAU,YAAY,KAAK,EAAE,QAAQ,CAAC,SAAU,CAAC,CAC1D,CAAC,EAED,GAAG,2DAA4D,SAAY,CACzEP,EAAe,CACb,QAAS,GAAG,GAAG,IACb,QAAQ,QAAQ,CAAE,OAAQL,EAAQ,EAAG,UAAW,EAAK,CAAC,CACxD,CACF,CAAC,EAKD,MAAMW,EAAS,MAJGd,EAA2B,uBAAuB,EAClEE,CACF,EAE+B,QAAQ,CACrC,SAAU,WACV,aAAc,GAAG,GAAG,CACtB,CAAC,EAED,OAAOY,EAAO,aAAa,EAAE,YAAY,KAAK,EAAE,QAAQ,CAAC,SAAU,CAAC,EACpE,OACEA,EAAO,aAAa,EAAE,YAAY,aAAa,CAC7C,gBAAiB,OACnB,CAAC,CACH,EAAE,KAAK,EAAE,CACX,CAAC,EAED,GAAG,2DAA4D,SAAY,CACzEN,EAAe,CACb,QAAS,GAAG,GAAG,IAAM,QAAQ,OAAO,IAAI,MAAM,MAAM,CAAC,CAAC,CACxD,CAAC,EAKD,MAAMM,EAAS,MAJGd,EAA2B,uBAAuB,EAClEE,CACF,EAE+B,QAAQ,CACrC,SAAU,WACV,aAAc,GAAG,GAAG,CACtB,CAAC,EAED,OAAOY,EAAO,OAAO,CAAC,EAAE,KAAK,EAAI,EACjC,OAAOA,EAAO,KAAK,EAAE,aAAa,CAAC,EAAE,eACnCpB,CACF,CACF,CAAC,CACH,CAAC,EAED,SAAS,aAAc,IAAM,CAC3B,MAAMsB,EAAkB,CACtB,GAAI,UACN,EAEA,GAAG,oEAAqE,SAAY,CAClF,MAAMC,EAAa,GAAG,GAAG,IAAM,QAAQ,QAAQ,EAAI,CAAC,EACpDT,EAAe,CAAE,WAAAS,CAAW,CAAC,EAK7B,MAAMH,EAAS,MAJGd,EAA2B,uBAAuB,EAClEE,CACF,EAE+B,WAAW,CAAE,gBAAAc,CAAgB,CAAC,EAE7D,OAAOC,CAAU,EAAE,qBAAqB,UAAU,EAClD,OAAOH,EAAO,QAAQ,CAAC,EAAE,KAAK,EAAI,CACpC,CAAC,EAED,GAAG,4DAA6D,SAAY,CAC1EN,EAAe,CAAE,WAAY,GAAG,GAAG,IAAM,QAAQ,QAAQ,EAAK,CAAC,CAAE,CAAC,EAKlE,MAAMM,EAAS,MAJGd,EAA2B,uBAAuB,EAClEE,CACF,EAE+B,WAAW,CAAE,gBAAAc,CAAgB,CAAC,EAE7D,OAAOF,EAAO,OAAO,CAAC,EAAE,KAAK,EAAI,CACnC,CAAC,EAED,GAAG,mDAAoD,SAAY,CACjEN,EAAe,CACb,WAAY,GAAG,GAAG,IAAM,QAAQ,OAAO,IAAI,MAAM,MAAM,CAAC,CAAC,CAC3D,CAAC,EAKD,MAAMM,EAAS,MAJGd,EAA2B,uBAAuB,EAClEE,CACF,EAE+B,WAAW,CAAE,gBAAAc,CAAgB,CAAC,EAE7D,OAAOF,EAAO,OAAO,CAAC,EAAE,KAAK,EAAI,CACnC,CAAC,CACH,CAAC,EAED,SAAS,WAAY,IAAM,CACzB,MAAMI,EAA2BC,IAC/BX,EAAe,CACb,QAAS,GAAG,GAAG,IACb,QAAQ,QAAQ,CAAE,OAAQL,EAAQ,EAAG,UAAW,EAAK,CAAC,CACxD,EACA,SAAAgB,CACF,CAAC,EACMnB,EAA2B,uBAAuB,EAAEE,CAAa,GAG1E,GAAG,gDAAiD,SAAY,CAC9D,MAAMiB,EAAW,GAAG,GAAG,IAAM,QAAQ,QAAQ,CAAE,SAAU,UAAW,CAAC,CAAC,EAChET,EAAYQ,EAAwBC,CAAQ,EAC5CN,EAAe,GAAG,GAAG,EAKrBC,EAAS,MAHb,MAAMJ,EAAU,QAAQ,CAAE,SAAU,WAAY,aAAAG,CAAa,CAAC,GAC9D,aAAa,EAEgB,SAC7B,WAAW,KAAK,CAAC,IAAM,EAAM,EAAM,CAAI,CAAC,CAC1C,EAEA,OAAOC,EAAO,QAAQ,CAAC,EAAE,KAAK,EAAI,EAClC,MAAMM,EAAeN,EAAO,aAAa,EACzC,OAAO,MAAM,KAAKM,EAAa,IAAI,CAAC,EAAE,QAAQ,CAAC,IAAM,GAAI,CAAC,EAC1D,OAAO,MAAM,KAAKA,EAAa,UAAU,CAAC,EAAE,QAAQ,CAAC,IAAM,CAAI,CAAC,EAChE,OAAOP,CAAY,EAAE,IAAI,iBAAiB,CAC5C,CAAC,EAED,GAAG,sEAAuE,SAAY,CACpF,MAAMM,EAAW,GAAG,GAAG,IAAM,QAAQ,OAAO,IAAI,MAAM,aAAa,CAAC,CAAC,EAC/DT,EAAYQ,EAAwBC,CAAQ,EAC5CN,EAAe,GAAG,GAAG,EAKrBC,EAAS,MAHb,MAAMJ,EAAU,QAAQ,CAAE,SAAU,WAAY,aAAAG,CAAa,CAAC,GAC9D,aAAa,EAEgB,SAC7B,WAAW,KAAK,CAAC,IAAM,EAAM,EAAM,CAAI,CAAC,CAC1C,EAEA,OAAOC,EAAO,OAAO,CAAC,EAAE,KAAK,EAAI,EACjC,OAAOD,CAAY,EAAE,qBAAqB,UAAU,CACtD,CAAC,CACH,CAAC,EAED,SAAS,6BAA8B,IAAM,CAC3C,GAAG,4DAA6D,IAAM,CACpE,MAAMJ,EAASD,EAAe,EAC9B,OAAOC,CAAM,EAAE,YAAY,EAE3BT,EAA2B,EAAEE,CAAa,EAE1C,OAAOP,CAAU,EAAE,qBACjB,wBACA,OAAO,SAAS,CAClB,CACF,CAAC,EAED,GAAG,mDAAoD,IAAM,CAC3Da,EAAe,EAEfR,EAA2B,uBAAuB,EAAEE,CAAa,EAEjE,OAAOP,CAAU,EAAE,qBACjB,wBACA,OAAO,SAAS,CAClB,CACF,CAAC,CACH,CAAC,CACH,CAAC",
|
|
6
|
+
"names": ["OpeningConnectionError", "MockClient", "firstValueFrom", "take", "toArray", "mockserverIdentifier", "mockserverTransportFactory", "loggerServiceFactory", "transportArgs", "aDevice", "overrides", "mockListDevices", "impl", "listDevices", "mockClientImpl", "client", "transport", "devices", "emissions", "onDisconnect", "result", "connected", "connectedDevice", "disconnect", "buildConnectedTransport", "sendApdu", "apduResponse"]
|
|
7
|
+
}
|
package/lib/esm/package.json
CHANGED
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"watch:builds": "pnpm ldmk-tool watch --entryPoints src/index.ts,src/**/*.ts --tsconfig tsconfig.prod.json",
|
|
57
57
|
"watch:types": "concurrently \"tsc --watch -p tsconfig.prod.json\" \"tsc-alias --watch -p tsconfig.prod.json\""
|
|
58
58
|
},
|
|
59
|
-
"version": "0.0.0-develop-
|
|
59
|
+
"version": "0.0.0-develop-20260624002956"
|
|
60
60
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ApduResponse, type ConnectError, type DeviceId, type DisconnectHandler, type
|
|
1
|
+
import { type ApduResponse, type ConnectError, type DeviceId, type DisconnectHandler, type DmkError, type LoggerPublisherService, type Transport, type TransportConnectedDevice, type TransportDiscoveredDevice, type TransportFactory, type TransportIdentifier } from "@ledgerhq/device-management-kit";
|
|
2
2
|
import { type Either } from "purify-ts";
|
|
3
3
|
import { type Observable } from "rxjs";
|
|
4
4
|
export declare const mockserverIdentifier: TransportIdentifier;
|
|
@@ -6,7 +6,7 @@ export declare class MockTransport implements Transport {
|
|
|
6
6
|
private logger;
|
|
7
7
|
private mockClient;
|
|
8
8
|
private readonly identifier;
|
|
9
|
-
constructor(loggerServiceFactory: (tag: string) => LoggerPublisherService,
|
|
9
|
+
constructor(loggerServiceFactory: (tag: string) => LoggerPublisherService, mockUrl: string, sessionToken?: string);
|
|
10
10
|
isSupported(): boolean;
|
|
11
11
|
getIdentifier(): TransportIdentifier;
|
|
12
12
|
listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]>;
|
|
@@ -19,7 +19,17 @@ export declare class MockTransport implements Transport {
|
|
|
19
19
|
disconnect(params: {
|
|
20
20
|
connectedDevice: TransportConnectedDevice;
|
|
21
21
|
}): Promise<Either<DmkError, void>>;
|
|
22
|
-
sendApdu(
|
|
22
|
+
sendApdu(deviceId: string, onDisconnectDeviceId: DeviceId, onDisconnect: DisconnectHandler, apdu: Uint8Array): Promise<Either<DmkError, ApduResponse>>;
|
|
23
|
+
private mapToDiscoveredDevices;
|
|
24
|
+
private buildDeviceModel;
|
|
23
25
|
}
|
|
24
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Build a mock-server transport factory.
|
|
28
|
+
*
|
|
29
|
+
* @param mockUrl Optional mock server URL. When omitted, `config.mockUrl` from
|
|
30
|
+
* the DMK configuration is used.
|
|
31
|
+
* @param sessionToken Optional bearer token to share an existing session
|
|
32
|
+
* When omitted, the client self-provisions a session via /auth.
|
|
33
|
+
*/
|
|
34
|
+
export declare const mockserverTransportFactory: (mockUrl?: string, sessionToken?: string) => TransportFactory;
|
|
25
35
|
//# sourceMappingURL=MockserverTransport.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockserverTransport.d.ts","sourceRoot":"","sources":["../../src/MockserverTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,QAAQ,EAGb,KAAK,iBAAiB,EACtB,KAAK,
|
|
1
|
+
{"version":3,"file":"MockserverTransport.d.ts","sourceRoot":"","sources":["../../src/MockserverTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,QAAQ,EAGb,KAAK,iBAAiB,EACtB,KAAK,QAAQ,EAIb,KAAK,sBAAsB,EAG3B,KAAK,SAAS,EACd,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACzB,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EAAE,KAAK,MAAM,EAAe,MAAM,WAAW,CAAC;AACrD,OAAO,EAKL,KAAK,UAAU,EAIhB,MAAM,MAAM,CAAC;AAEd,eAAO,MAAM,oBAAoB,EAAE,mBAAkC,CAAC;AAOtE,qBAAa,aAAc,YAAW,SAAS;IAC7C,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6C;gBAGtE,oBAAoB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,sBAAsB,EAC7D,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,MAAM;IAMvB,WAAW,IAAI,OAAO;IAItB,aAAa,IAAI,mBAAmB;IAIpC,wBAAwB,IAAI,UAAU,CAAC,yBAAyB,EAAE,CAAC;IAcnE,gBAAgB,IAAI,UAAU,CAAC,yBAAyB,CAAC;IAQzD,eAAe,IAAI,IAAI;IAKjB,OAAO,CAAC,MAAM,EAAE;QACpB,QAAQ,EAAE,QAAQ,CAAC;QACnB,YAAY,EAAE,iBAAiB,CAAC;KACjC,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,wBAAwB,CAAC,CAAC;IAmBrD,UAAU,CAAC,MAAM,EAAE;QACvB,eAAe,EAAE,wBAAwB,CAAC;KAC3C,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAgB7B,QAAQ,CACZ,QAAQ,EAAE,MAAM,EAChB,oBAAoB,EAAE,QAAQ,EAC9B,YAAY,EAAE,iBAAiB,EAC/B,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAuB1C,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,gBAAgB;CAezB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,GACpC,UAAU,MAAM,EAAE,eAAe,MAAM,KAAG,gBAMxC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MockserverTransport.test.d.ts","sourceRoot":"","sources":["../../src/MockserverTransport.test.ts"],"names":[],"mappings":""}
|