@ledgerhq/device-transport-kit-speculos 0.0.0-wrong-error-when-in-experimental-provider-20251021161219 → 0.0.0-z-intent-queue-7-20251204111630

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,56 +1,60 @@
1
1
  {
2
- "name": "@ledgerhq/device-transport-kit-speculos",
3
- "version": "1.0.1",
4
- "license": "Apache-2.0",
5
- "private": false,
2
+ "dependencies": {
3
+ "@sentry/minimal": "catalog:",
4
+ "axios": "catalog:",
5
+ "purify-ts": "catalog:"
6
+ },
7
+ "devDependencies": {
8
+ "@ledgerhq/device-management-kit": "workspace:^",
9
+ "@ledgerhq/eslint-config-dsdk": "workspace:^",
10
+ "@ledgerhq/ldmk-tool": "workspace:^",
11
+ "@ledgerhq/prettier-config-dsdk": "workspace:^",
12
+ "@ledgerhq/tsconfig-dsdk": "workspace:^",
13
+ "@ledgerhq/vitest-config-dmk": "workspace:^",
14
+ "rxjs": "catalog:",
15
+ "ts-node": "catalog:"
16
+ },
6
17
  "exports": {
7
18
  ".": {
8
- "types": "./lib/types/index.d.ts",
9
19
  "import": "./lib/esm/index.js",
10
- "require": "./lib/cjs/index.js"
20
+ "require": "./lib/cjs/index.js",
21
+ "types": "./lib/types/index.d.ts"
11
22
  },
12
23
  "./*": {
13
- "types": "./lib/types/*",
14
24
  "import": "./lib/esm/*",
15
- "require": "./lib/cjs/*"
25
+ "require": "./lib/cjs/*",
26
+ "types": "./lib/types/*"
16
27
  }
17
28
  },
18
29
  "files": [
19
30
  "./lib"
20
31
  ],
32
+ "license": "Apache-2.0",
33
+ "name": "@ledgerhq/device-transport-kit-speculos",
34
+ "peerDependencies": {
35
+ "@ledgerhq/device-management-kit": "workspace:^",
36
+ "rxjs": "catalog:"
37
+ },
38
+ "private": false,
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/LedgerHQ/device-sdk-ts.git"
42
+ },
21
43
  "scripts": {
22
- "prebuild": "rimraf lib",
23
44
  "build": "pnpm ldmk-tool build --entryPoints index.ts,src/**/*.ts --tsconfig tsconfig.prod.json",
24
45
  "dev": "concurrently \"pnpm watch:builds\" \"pnpm watch:types\"",
25
- "watch:builds": "pnpm ldmk-tool watch --entryPoints index.ts,src/**/*.ts --tsconfig tsconfig.prod.json",
26
- "watch:types": "concurrently \"tsc --watch -p tsconfig.prod.json\" \"tsc-alias --watch -p tsconfig.prod.json\"",
27
46
  "lint": "eslint",
28
47
  "lint:fix": "pnpm lint --fix",
29
48
  "postpack": "find . -name '*.tgz' -exec cp {} ../../../dist/ \\; ",
49
+ "prebuild": "rimraf lib",
30
50
  "prettier": "prettier . --check",
31
51
  "prettier:fix": "prettier . --write",
32
- "typecheck": "tsc --noEmit",
33
52
  "test": "vitest run --passWithNoTests",
53
+ "test:coverage": "vitest run --coverage --passWithNoTests",
34
54
  "test:watch": "vitest --passWithNoTests",
35
- "test:coverage": "vitest run --coverage --passWithNoTests"
36
- },
37
- "dependencies": {
38
- "@sentry/minimal": "catalog:",
39
- "purify-ts": "catalog:",
40
- "axios": "catalog:"
41
- },
42
- "devDependencies": {
43
- "@ledgerhq/device-management-kit": "workspace:*",
44
- "@ledgerhq/eslint-config-dsdk": "workspace:*",
45
- "@ledgerhq/ldmk-tool": "workspace:*",
46
- "@ledgerhq/prettier-config-dsdk": "workspace:*",
47
- "@ledgerhq/tsconfig-dsdk": "workspace:*",
48
- "@ledgerhq/vitest-config-dmk": "workspace:*",
49
- "rxjs": "catalog:",
50
- "ts-node": "catalog:"
55
+ "typecheck": "tsc --noEmit",
56
+ "watch:builds": "pnpm ldmk-tool watch --entryPoints index.ts,src/**/*.ts --tsconfig tsconfig.prod.json",
57
+ "watch:types": "concurrently \"tsc --watch -p tsconfig.prod.json\" \"tsc-alias --watch -p tsconfig.prod.json\""
51
58
  },
52
- "peerDependencies": {
53
- "@ledgerhq/device-management-kit": "workspace:*",
54
- "rxjs": "catalog:"
55
- }
59
+ "version": "0.0.0-z-intent-queue-7-20251204111630"
56
60
  }
@@ -1,2 +1,2 @@
1
- "use strict";var l=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var m=(s,e)=>{for(var t in e)l(s,t,{get:e[t],enumerable:!0})},y=(s,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of S(e))!f.call(s,r)&&r!==t&&l(s,r,{get:()=>e[r],enumerable:!(n=b(e,r))||n.enumerable});return s};var I=s=>y(l({},"__esModule",{value:!0}),s);var E={};m(E,{SpeculosTransport:()=>D,speculosIdentifier:()=>g,speculosTransportFactory:()=>T});module.exports=I(E);var i=require("@ledgerhq/device-management-kit"),c=require("purify-ts"),u=require("rxjs"),v=require("../internal/datasource/HttpSpeculosDatasource");const g="SPECULOS_HTTP_TRANSPORT";class D{logger;identifier=g;_speculosDataSource;connectedDevice=null;disconnectInterval=null;_isE2E;speculosDevice={id:"SpeculosID",deviceModel:{id:i.DeviceModelId.STAX,productName:"Speculos - App Name - version",usbProductId:16,bootloaderUsbProductId:1,getBlockSize(){return 32},blockSize:32,usbOnly:!0,memorySize:320*1024,masks:[823132160]},transport:this.identifier};constructor(e,t,n,r){this._isE2E=r??!1,this.logger=e("SpeculosTransport"),this._speculosDataSource=new v.HttpSpeculosDatasource(n,this._isE2E)}isSupported(){return!0}getIdentifier(){return this.identifier}listenToAvailableDevices(){return(0,u.from)([[this.speculosDevice]])}startDiscovering(){return this.logger.debug("startDiscovering"),(0,u.from)([this.speculosDevice])}stopDiscovering(){this.logger.debug("stopDiscovering")}async connect(e){this.logger.debug("connect");const t=await this._speculosDataSource.postApdu("B0010000");this.logger.debug(`Hex Response: ${t}`);const n=this.createApduResponse(t),r=new i.ApduParser(n);r.extract8BitUInt();const o=r.encodeToString(r.extractFieldLVEncoded()),a=r.encodeToString(r.extractFieldLVEncoded());this.logger.debug(`App Name: ${o} and version ${a}`);const p=e.deviceId;try{const d={sendApdu:h=>this.sendApdu(p,e.deviceId,e.onDisconnect,h),deviceModel:{...this.speculosDevice.deviceModel,productName:`Speculos - ${o} - ${a}`,getBlockSize(){return 32}},transport:this.identifier,id:"SpeculosID",type:"USB"};return this.connectedDevice=d,this._isE2E||this.listenForDisconnect(e.onDisconnect,e.deviceId),(0,c.Right)(d)}catch(d){return(0,c.Left)(new i.OpeningConnectionError(d))}}async disconnect(e){return this.logger.debug("disconnect"),this.connectedDevice=null,Promise.resolve((0,c.Right)(void 0))}async sendApdu(e,t,n,r){try{const o=(0,i.bufferToHexaString)(r).substring(2);this.logger.debug(`send APDU: ${o}`);const a=await this._speculosDataSource.postApdu(o),p=this.createApduResponse(a);return(0,c.Right)(p)}catch(o){return this.connectedDevice&&(this.logger.debug("disconnecting"),n(t),this.disconnect({connectedDevice:this.connectedDevice}),this.disconnectInterval&&clearInterval(this.disconnectInterval)),(0,c.Left)(new i.GeneralDmkError(o))}}createApduResponse(e){return this.logger.debug(`Status code hex: ${e.substring(e.length-4,e.length)}`),this.logger.debug(`data hex: ${e.substring(0,e.length-4)}`),{statusCode:this.fromHexString(e.substring(e.length-4,e.length)),data:this.fromHexString(e.substring(0,e.length-4))}}fromHexString(e){return e?new Uint8Array(e.match(/.{1,2}/g).map(t=>parseInt(t,16))):Uint8Array.from([])}listenForDisconnect(e,t){this.disconnectInterval=setInterval(async()=>{await this._speculosDataSource.isServerAvailable()||(this.logger.info(`Speculos server unavailable, disconnecting device ${t}`),e(t),this.connectedDevice&&await this.disconnect({connectedDevice:this.connectedDevice}),this.disconnectInterval&&clearInterval(this.disconnectInterval))},2e3)}}const T=(s="http://127.0.0.1:5000",e=!1)=>({config:t,loggerServiceFactory:n})=>new D(n,t,s,e);0&&(module.exports={SpeculosTransport,speculosIdentifier,speculosTransportFactory});
1
+ "use strict";var l=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var m=Object.prototype.hasOwnProperty;var y=(s,e)=>{for(var t in e)l(s,t,{get:e[t],enumerable:!0})},b=(s,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of S(e))!m.call(s,r)&&r!==t&&l(s,r,{get:()=>e[r],enumerable:!(n=f(e,r))||n.enumerable});return s};var I=s=>b(l({},"__esModule",{value:!0}),s);var E={};y(E,{SpeculosTransport:()=>D,speculosIdentifier:()=>g,speculosTransportFactory:()=>T});module.exports=I(E);var o=require("@ledgerhq/device-management-kit"),i=require("purify-ts"),u=require("rxjs"),v=require("../internal/datasource/HttpSpeculosDatasource");const g="SPECULOS_HTTP_TRANSPORT";class D{logger;identifier=g;_speculosDataSource;connectedDevice=null;disconnectInterval=null;_isE2E;speculosDevice={id:"SpeculosID",deviceModel:{id:o.DeviceModelId.STAX,productName:"Speculos - App Name - version",usbProductId:16,bootloaderUsbProductId:1,getBlockSize(){return 32},blockSize:32,usbOnly:!0,memorySize:320*1024,masks:[823132160]},transport:this.identifier};constructor(e,t,n,r){this._isE2E=r??!1,this.logger=e("SpeculosTransport"),this._speculosDataSource=new v.HttpSpeculosDatasource(n)}isSupported(){return!0}getIdentifier(){return this.identifier}listenToAvailableDevices(){return(0,u.from)([[this.speculosDevice]])}startDiscovering(){return this.logger.debug("startDiscovering"),(0,u.from)([this.speculosDevice])}stopDiscovering(){this.logger.debug("stopDiscovering")}async connect(e){this.logger.debug("connect");const t=await this._speculosDataSource.postApdu("B0010000");this.logger.debug(`Hex Response: ${t}`);const n=this.createApduResponse(t),r=new o.ApduParser(n);r.extract8BitUInt();const c=r.encodeToString(r.extractFieldLVEncoded()),a=r.encodeToString(r.extractFieldLVEncoded());this.logger.debug(`App Name: ${c} and version ${a}`);const d=e.deviceId;try{const p={sendApdu:h=>this.sendApdu(d,e.deviceId,e.onDisconnect,h),deviceModel:{...this.speculosDevice.deviceModel,productName:`Speculos - ${c} - ${a}`,getBlockSize(){return 32}},transport:this.identifier,id:"SpeculosID",type:"USB"};return this.connectedDevice=p,this._isE2E||this.listenForDisconnect(e.onDisconnect,e.deviceId),(0,i.Right)(p)}catch(p){return(0,i.Left)(new o.OpeningConnectionError(p))}}async disconnect(e){return this.logger.debug("disconnect"),this.connectedDevice=null,Promise.resolve((0,i.Right)(void 0))}async sendApdu(e,t,n,r){try{const c=(0,o.bufferToHexaString)(r).substring(2),a=await this._speculosDataSource.postApdu(c),d=this.createApduResponse(a);return(0,i.Right)(d)}catch(c){return this.connectedDevice&&(this.logger.debug("disconnecting"),n(t),this.disconnect({connectedDevice:this.connectedDevice}),this.disconnectInterval&&clearInterval(this.disconnectInterval)),(0,i.Left)(new o.GeneralDmkError(c))}}createApduResponse(e){return{statusCode:this.fromHexString(e.substring(e.length-4,e.length)),data:this.fromHexString(e.substring(0,e.length-4))}}fromHexString(e){return e?new Uint8Array(e.match(/.{1,2}/g).map(t=>parseInt(t,16))):Uint8Array.from([])}listenForDisconnect(e,t){this.disconnectInterval=setInterval(async()=>{await this._speculosDataSource.isServerAvailable()||(this.logger.info(`Speculos server unavailable, disconnecting device ${t}`),e(t),this.connectedDevice&&await this.disconnect({connectedDevice:this.connectedDevice}),this.disconnectInterval&&clearInterval(this.disconnectInterval))},2e3)}}const T=(s="http://127.0.0.1:5000",e=!1)=>({config:t,loggerServiceFactory:n})=>new D(n,t,s,e);0&&(module.exports={SpeculosTransport,speculosIdentifier,speculosTransportFactory});
2
2
  //# sourceMappingURL=SpeculosTransport.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/api/SpeculosTransport.ts"],
4
- "sourcesContent": ["import {\n ApduParser,\n type ApduResponse,\n bufferToHexaString,\n type ConnectError,\n type DeviceId,\n DeviceModelId,\n type DisconnectHandler,\n type DmkConfig,\n type DmkError,\n GeneralDmkError,\n type LoggerPublisherService,\n OpeningConnectionError,\n type Transport,\n type TransportConnectedDevice,\n type TransportDiscoveredDevice,\n type TransportFactory,\n type TransportIdentifier,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Either, Left, Right } from \"purify-ts\";\nimport { from, type Observable } from \"rxjs\";\n\nimport { HttpSpeculosDatasource } from \"@internal/datasource/HttpSpeculosDatasource\";\nimport { type SpeculosDatasource } from \"@internal/datasource/SpeculosDatasource\";\n\nexport const speculosIdentifier: TransportIdentifier =\n \"SPECULOS_HTTP_TRANSPORT\";\n\nexport class SpeculosTransport implements Transport {\n private logger: LoggerPublisherService;\n private readonly identifier: TransportIdentifier = speculosIdentifier;\n private readonly _speculosDataSource: SpeculosDatasource;\n private connectedDevice: TransportConnectedDevice | null = null;\n private disconnectInterval: NodeJS.Timeout | null = null;\n private readonly _isE2E: boolean;\n private readonly speculosDevice: TransportDiscoveredDevice = {\n id: \"SpeculosID\", //TODO make it dynamic at creation\n deviceModel: {\n id: DeviceModelId.STAX,\n productName: \"Speculos - App Name - version\",\n usbProductId: 0x10,\n bootloaderUsbProductId: 0x0001,\n getBlockSize() {\n return 32;\n },\n blockSize: 32,\n usbOnly: true,\n memorySize: 320 * 1024,\n masks: [0x31100000],\n },\n transport: this.identifier,\n };\n\n constructor(\n loggerServiceFactory: (tag: string) => LoggerPublisherService,\n _config: DmkConfig,\n speculosUrl: string,\n isE2E?: boolean,\n ) {\n this._isE2E = isE2E ?? false;\n this.logger = loggerServiceFactory(\"SpeculosTransport\");\n this._speculosDataSource = new HttpSpeculosDatasource(\n speculosUrl,\n this._isE2E,\n ); // See how to pass properly speculos config.\n }\n\n isSupported(): boolean {\n return true;\n }\n\n getIdentifier(): TransportIdentifier {\n return this.identifier;\n }\n\n listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]> {\n return from([[this.speculosDevice]]);\n }\n\n startDiscovering(): Observable<TransportDiscoveredDevice> {\n this.logger.debug(\"startDiscovering\");\n return from([this.speculosDevice]);\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\n const hexResponse = await this._speculosDataSource.postApdu(\"B0010000\");\n this.logger.debug(`Hex Response: ${hexResponse}`);\n const apduResponse = this.createApduResponse(hexResponse);\n const parser = new ApduParser(apduResponse);\n\n //Copy paste from GetAppAndVersionCommand\n parser.extract8BitUInt(); //Need otherwise parser is not in the right position\n const appName = parser.encodeToString(parser.extractFieldLVEncoded());\n const appVersion = parser.encodeToString(parser.extractFieldLVEncoded());\n\n this.logger.debug(`App Name: ${appName} and version ${appVersion}`);\n\n const sessionId: string = params.deviceId;\n try {\n const connectedDevice: TransportConnectedDevice = {\n sendApdu: (apdu) => {\n return this.sendApdu(\n sessionId,\n params.deviceId,\n params.onDisconnect,\n apdu,\n );\n },\n deviceModel: {\n ...this.speculosDevice.deviceModel,\n productName: `Speculos - ${appName} - ${appVersion}`,\n getBlockSize() {\n return 32;\n },\n },\n transport: this.identifier,\n id: \"SpeculosID\", //TODO make it dynamic at creation\n type: \"USB\",\n };\n\n this.connectedDevice = connectedDevice;\n if (!this._isE2E) {\n this.listenForDisconnect(params.onDisconnect, params.deviceId);\n }\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 this.connectedDevice = null;\n return Promise.resolve(Right(undefined));\n }\n\n async sendApdu(\n _sessionId: string,\n deviceId: DeviceId,\n onDisconnect: DisconnectHandler,\n apdu: Uint8Array,\n ): Promise<Either<DmkError, ApduResponse>> {\n try {\n const hexApdu = bufferToHexaString(apdu).substring(2);\n this.logger.debug(`send APDU: ${hexApdu}`);\n const hexResponse: string =\n await this._speculosDataSource.postApdu(hexApdu);\n const apduResponse = this.createApduResponse(hexResponse);\n return Right(apduResponse);\n } catch (error) {\n if (this.connectedDevice) {\n this.logger.debug(\"disconnecting\");\n onDisconnect(deviceId);\n this.disconnect({\n connectedDevice: this.connectedDevice,\n });\n\n if (this.disconnectInterval) {\n clearInterval(this.disconnectInterval);\n }\n }\n return Left(new GeneralDmkError(error));\n }\n }\n\n private createApduResponse(hexApdu: string): ApduResponse {\n this.logger.debug(\n `Status code hex: ${hexApdu.substring(hexApdu.length - 4, hexApdu.length)}`,\n );\n this.logger.debug(`data hex: ${hexApdu.substring(0, hexApdu.length - 4)}`);\n\n const apduResponse = {\n statusCode: this.fromHexString(\n hexApdu.substring(hexApdu.length - 4, hexApdu.length),\n ),\n data: this.fromHexString(hexApdu.substring(0, hexApdu.length - 4)),\n };\n return apduResponse;\n }\n\n //TODO: Move this to a helper\n private fromHexString(hexString: string): Uint8Array {\n if (!hexString) {\n return Uint8Array.from([]);\n }\n return new Uint8Array(\n hexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16)),\n );\n }\n\n private listenForDisconnect(\n onDisconnect: DisconnectHandler,\n deviceId: DeviceId,\n ): void {\n this.disconnectInterval = setInterval(async () => {\n const isServerAvailable =\n await this._speculosDataSource.isServerAvailable();\n\n if (!isServerAvailable) {\n this.logger.info(\n `Speculos server unavailable, disconnecting device ${deviceId}`,\n );\n onDisconnect(deviceId);\n\n if (this.connectedDevice) {\n await this.disconnect({\n connectedDevice: this.connectedDevice,\n });\n }\n\n if (this.disconnectInterval) {\n clearInterval(this.disconnectInterval);\n }\n }\n }, 2000);\n }\n}\n\nexport const speculosTransportFactory: (\n speculosUrl?: string,\n isE2E?: boolean,\n) => TransportFactory =\n (speculosUrl = \"http://127.0.0.1:5000\", isE2E = false) =>\n ({ config, loggerServiceFactory }) =>\n new SpeculosTransport(loggerServiceFactory, config, speculosUrl, isE2E);\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n ApduParser,\n type ApduResponse,\n bufferToHexaString,\n type ConnectError,\n type DeviceId,\n DeviceModelId,\n type DisconnectHandler,\n type DmkConfig,\n type DmkError,\n GeneralDmkError,\n type LoggerPublisherService,\n OpeningConnectionError,\n type Transport,\n type TransportConnectedDevice,\n type TransportDiscoveredDevice,\n type TransportFactory,\n type TransportIdentifier,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Either, Left, Right } from \"purify-ts\";\nimport { from, type Observable } from \"rxjs\";\n\nimport { HttpSpeculosDatasource } from \"@internal/datasource/HttpSpeculosDatasource\";\nimport { type SpeculosDatasource } from \"@internal/datasource/SpeculosDatasource\";\n\nexport const speculosIdentifier: TransportIdentifier =\n \"SPECULOS_HTTP_TRANSPORT\";\n\nexport class SpeculosTransport implements Transport {\n private logger: LoggerPublisherService;\n private readonly identifier: TransportIdentifier = speculosIdentifier;\n private readonly _speculosDataSource: SpeculosDatasource;\n private connectedDevice: TransportConnectedDevice | null = null;\n private disconnectInterval: NodeJS.Timeout | null = null;\n private readonly _isE2E: boolean;\n private readonly speculosDevice: TransportDiscoveredDevice = {\n id: \"SpeculosID\", //TODO make it dynamic at creation\n deviceModel: {\n id: DeviceModelId.STAX,\n productName: \"Speculos - App Name - version\",\n usbProductId: 0x10,\n bootloaderUsbProductId: 0x0001,\n getBlockSize() {\n return 32;\n },\n blockSize: 32,\n usbOnly: true,\n memorySize: 320 * 1024,\n masks: [0x31100000],\n },\n transport: this.identifier,\n };\n\n constructor(\n loggerServiceFactory: (tag: string) => LoggerPublisherService,\n _config: DmkConfig,\n speculosUrl: string,\n isE2E?: boolean,\n ) {\n this._isE2E = isE2E ?? false;\n this.logger = loggerServiceFactory(\"SpeculosTransport\");\n this._speculosDataSource = new HttpSpeculosDatasource(speculosUrl); // See how to pass properly speculos config.\n }\n\n isSupported(): boolean {\n return true;\n }\n\n getIdentifier(): TransportIdentifier {\n return this.identifier;\n }\n\n listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]> {\n return from([[this.speculosDevice]]);\n }\n\n startDiscovering(): Observable<TransportDiscoveredDevice> {\n this.logger.debug(\"startDiscovering\");\n return from([this.speculosDevice]);\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\n const hexResponse = await this._speculosDataSource.postApdu(\"B0010000\");\n this.logger.debug(`Hex Response: ${hexResponse}`);\n const apduResponse = this.createApduResponse(hexResponse);\n const parser = new ApduParser(apduResponse);\n\n //Copy paste from GetAppAndVersionCommand\n parser.extract8BitUInt(); //Need otherwise parser is not in the right position\n const appName = parser.encodeToString(parser.extractFieldLVEncoded());\n const appVersion = parser.encodeToString(parser.extractFieldLVEncoded());\n\n this.logger.debug(`App Name: ${appName} and version ${appVersion}`);\n\n const sessionId: string = params.deviceId;\n try {\n const connectedDevice: TransportConnectedDevice = {\n sendApdu: (apdu) => {\n return this.sendApdu(\n sessionId,\n params.deviceId,\n params.onDisconnect,\n apdu,\n );\n },\n deviceModel: {\n ...this.speculosDevice.deviceModel,\n productName: `Speculos - ${appName} - ${appVersion}`,\n getBlockSize() {\n return 32;\n },\n },\n transport: this.identifier,\n id: \"SpeculosID\", //TODO make it dynamic at creation\n type: \"USB\",\n };\n\n this.connectedDevice = connectedDevice;\n if (!this._isE2E) {\n this.listenForDisconnect(params.onDisconnect, params.deviceId);\n }\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 this.connectedDevice = null;\n return Promise.resolve(Right(undefined));\n }\n\n async sendApdu(\n _sessionId: string,\n deviceId: DeviceId,\n onDisconnect: DisconnectHandler,\n apdu: Uint8Array,\n ): Promise<Either<DmkError, ApduResponse>> {\n try {\n const hexApdu = bufferToHexaString(apdu).substring(2);\n const hexResponse: string =\n await this._speculosDataSource.postApdu(hexApdu);\n const apduResponse = this.createApduResponse(hexResponse);\n return Right(apduResponse);\n } catch (error) {\n if (this.connectedDevice) {\n this.logger.debug(\"disconnecting\");\n onDisconnect(deviceId);\n this.disconnect({\n connectedDevice: this.connectedDevice,\n });\n\n if (this.disconnectInterval) {\n clearInterval(this.disconnectInterval);\n }\n }\n return Left(new GeneralDmkError(error));\n }\n }\n\n private createApduResponse(hexApdu: string): ApduResponse {\n const apduResponse = {\n statusCode: this.fromHexString(\n hexApdu.substring(hexApdu.length - 4, hexApdu.length),\n ),\n data: this.fromHexString(hexApdu.substring(0, hexApdu.length - 4)),\n };\n return apduResponse;\n }\n\n //TODO: Move this to a helper\n private fromHexString(hexString: string): Uint8Array {\n if (!hexString) {\n return Uint8Array.from([]);\n }\n return new Uint8Array(\n hexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16)),\n );\n }\n\n private listenForDisconnect(\n onDisconnect: DisconnectHandler,\n deviceId: DeviceId,\n ): void {\n this.disconnectInterval = setInterval(async () => {\n const isServerAvailable =\n await this._speculosDataSource.isServerAvailable();\n\n if (!isServerAvailable) {\n this.logger.info(\n `Speculos server unavailable, disconnecting device ${deviceId}`,\n );\n onDisconnect(deviceId);\n\n if (this.connectedDevice) {\n await this.disconnect({\n connectedDevice: this.connectedDevice,\n });\n }\n\n if (this.disconnectInterval) {\n clearInterval(this.disconnectInterval);\n }\n }\n }, 2000);\n }\n}\n\nexport const speculosTransportFactory: (\n speculosUrl?: string,\n isE2E?: boolean,\n) => TransportFactory =\n (speculosUrl = \"http://127.0.0.1:5000\", isE2E = false) =>\n ({ config, loggerServiceFactory }) =>\n new SpeculosTransport(loggerServiceFactory, config, speculosUrl, isE2E);\n"],
5
+ "mappings": "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",
6
6
  "names": ["SpeculosTransport_exports", "__export", "SpeculosTransport", "speculosIdentifier", "speculosTransportFactory", "__toCommonJS", "import_device_management_kit", "import_purify_ts", "import_rxjs", "import_HttpSpeculosDatasource", "loggerServiceFactory", "_config", "speculosUrl", "isE2E", "params", "hexResponse", "apduResponse", "parser", "appName", "appVersion", "sessionId", "connectedDevice", "apdu", "error", "_params", "_sessionId", "deviceId", "onDisconnect", "hexApdu", "hexString", "byte", "config"]
7
7
  }
@@ -1,2 +1,2 @@
1
- "use strict";var k=Object.create;var l=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var O=Object.getPrototypeOf,R=Object.prototype.hasOwnProperty;var P=(t,e)=>{for(var r in e)l(t,r,{get:e[r],enumerable:!0})},E=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of L(e))!R.call(t,n)&&n!==r&&l(t,n,{get:()=>e[n],enumerable:!(s=I(e,n))||s.enumerable});return t};var v=(t,e,r)=>(r=t!=null?k(O(t)):{},E(e||!t||!t.__esModule?l(r,"default",{value:t,enumerable:!0}):r,t)),U=t=>E(l({},"__esModule",{value:!0}),t);var X={};P(X,{HttpSpeculosDatasource:()=>V,makeKeepAliveAxiosForSSE:()=>A,makeNoKeepAliveAxios:()=>b});module.exports=U(X);var m=v(require("axios")),S=v(require("../../../package.json"));const p=1e4,f=t=>t.replace(/\/+$/,"");function b(t,e,r){return m.default.create({baseURL:f(t),timeout:e,headers:{"X-Ledger-Client-Version":r},transitional:{clarifyTimeoutError:!0}})}function A(t,e){return m.default.create({baseURL:f(t),timeout:0,headers:{"X-Ledger-Client-Version":e},transitional:{clarifyTimeoutError:!0}})}class V{constructor(e,r=!1,s=`ldmk-transport-speculos/${S.default.version}`){this.baseUrl=e;this._isE2E=r;this.apduClient=b(e,p,s),this._isE2E&&(this.sseClient=A(e,s))}apduClient;sseClient=null;async postApdu(e){const{data:r}=await this.apduClient.post("/apdu",{data:e},{timeout:p});return r.data}async isServerAvailable(){try{return await this.apduClient.request({method:"GET",url:"/events",timeout:p}),!0}catch{return!1}}async openEventStream(e,r){if(typeof fetch>"u")throw new Error("global fetch is not available in Node < 18");if(!this.sseClient)throw new Error("SSE not supported unless in E2E mode");const s=this.sseClient.defaults.baseURL??this.baseUrl,n=`${f(s)}/events?stream=true`,c=new AbortController,w={Accept:"text/event-stream","Cache-Control":"no-cache","X-Ledger-Client-Version":this.sseClient.defaults.headers?.common?.["X-Ledger-Client-Version"]??"ldmk-transport-speculos"},u=await fetch(n,{method:"GET",headers:w,signal:c.signal});if(!u.ok)throw c.abort(),new Error(`SSE request failed with status ${u.status}`);const d=u.body;if(!d)throw c.abort(),new Error("SSE response has no body stream.");const C=d.getReader(),x=new TextDecoder("utf-8");let o="",h=!1;const T=()=>{if(!h){h=!0;try{r?.()}catch{}}},y=a=>{if(a.startsWith("data: ")){const i=a.slice(6);try{e(JSON.parse(i))}catch{e({data:i})}}};return(async()=>{try{for(;;){const{value:a,done:i}=await C.read();if(i)break;o+=x.decode(a,{stream:!0});const g=o.split(/\r?\n/);o=g.pop()??"";for(const D of g)y(D)}}catch{}finally{if(o.length){for(const a of o.split(/\r?\n/))y(a);o=""}T()}})(),d}}0&&(module.exports={HttpSpeculosDatasource,makeKeepAliveAxiosForSSE,makeNoKeepAliveAxios});
1
+ "use strict";var T=Object.create;var c=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var k=Object.getPrototypeOf,I=Object.prototype.hasOwnProperty;var L=(t,e)=>{for(var s in e)c(t,s,{get:e[s],enumerable:!0})},h=(t,e,s,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of O(e))!I.call(t,r)&&r!==s&&c(t,r,{get:()=>e[r],enumerable:!(a=D(e,r))||a.enumerable});return t};var y=(t,e,s)=>(s=t!=null?T(k(t)):{},h(e||!t||!t.__esModule?c(s,"default",{value:t,enumerable:!0}):s,t)),P=t=>h(c({},"__esModule",{value:!0}),t);var $={};L($,{HttpSpeculosDatasource:()=>U,axiosClientFactory:()=>v});module.exports=P($);var g=y(require("axios")),A=y(require("../../../package.json"));const R=2e3,b=t=>t.replace(/\/+$/,"");function v(t,e){return g.default.create({baseURL:b(t),timeout:0,headers:{"X-Ledger-Client-Version":e},transitional:{clarifyTimeoutError:!0}})}class U{constructor(e,s=`ldmk-transport-speculos/${A.default.version}`){this.baseUrl=e;this.speculosAxiosClient=v(e,s)}speculosAxiosClient;async postApdu(e){const{data:s}=await this.speculosAxiosClient.post("/apdu",{data:e});return s.data}async isServerAvailable(){try{return await this.speculosAxiosClient.request({method:"GET",url:"/events",timeout:R}),!0}catch{return!1}}async openEventStream(e,s){if(typeof fetch>"u")throw new Error("global fetch is not available in Node < 18");const a=this.speculosAxiosClient.defaults.baseURL??this.baseUrl,r=`${b(a)}/events?stream=true`,l=new AbortController,x={Accept:"text/event-stream","Cache-Control":"no-cache","X-Ledger-Client-Version":this.speculosAxiosClient.defaults.headers?.common?.["X-Ledger-Client-Version"]??"ldmk-transport-speculos"},u=await fetch(r,{method:"GET",headers:x,signal:l.signal});if(!u.ok)throw l.abort(),new Error(`SSE request failed with status ${u.status}`);const p=u.body;if(!p)throw l.abort(),new Error("SSE response has no body stream.");const S=p.getReader(),w=new TextDecoder("utf-8");let o="",d=!1;const C=()=>{if(!d){d=!0;try{s?.()}catch{}}},f=n=>{if(n.startsWith("data: ")){const i=n.slice(6);try{e(JSON.parse(i))}catch{e({data:i})}}};return(async()=>{try{for(;;){const{value:n,done:i}=await S.read();if(i)break;o+=w.decode(n,{stream:!0});const m=o.split(/\r?\n/);o=m.pop()??"";for(const E of m)f(E)}}catch{}finally{if(o.length){for(const n of o.split(/\r?\n/))f(n);o=""}C()}})(),p}}0&&(module.exports={HttpSpeculosDatasource,axiosClientFactory});
2
2
  //# sourceMappingURL=HttpSpeculosDatasource.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/internal/datasource/HttpSpeculosDatasource.ts"],
4
- "sourcesContent": ["import axios, { type AxiosInstance } from \"axios\";\n\nimport PACKAGE from \"@root/package.json\";\n\nimport { type SpeculosDatasource } from \"./SpeculosDatasource\";\n\nconst TIMEOUT = 10000; // 10 second timeout for availability check\n\nconst removeTrailingSlashes = (url: string) => url.replace(/\\/+$/, \"\");\n\nexport function makeNoKeepAliveAxios(\n baseUrl: string,\n timeoutMs: number,\n clientHeader: string,\n): AxiosInstance {\n return axios.create({\n baseURL: removeTrailingSlashes(baseUrl),\n timeout: timeoutMs,\n headers: {\n \"X-Ledger-Client-Version\": clientHeader,\n },\n transitional: { clarifyTimeoutError: true },\n });\n}\n\nexport function makeKeepAliveAxiosForSSE(\n baseUrl: string,\n clientHeader: string,\n): AxiosInstance {\n return axios.create({\n baseURL: removeTrailingSlashes(baseUrl),\n timeout: 0, // no timeout for SSE\n headers: {\n \"X-Ledger-Client-Version\": clientHeader,\n },\n transitional: { clarifyTimeoutError: true },\n });\n}\n\nexport class HttpSpeculosDatasource implements SpeculosDatasource {\n private readonly apduClient: AxiosInstance;\n private readonly sseClient: AxiosInstance | null = null;\n\n constructor(\n private readonly baseUrl: string,\n private readonly _isE2E: boolean = false,\n clientHeader: string = `ldmk-transport-speculos/${PACKAGE.version}`,\n ) {\n this.apduClient = makeNoKeepAliveAxios(baseUrl, TIMEOUT, clientHeader);\n if (this._isE2E) {\n this.sseClient = makeKeepAliveAxiosForSSE(baseUrl, clientHeader);\n }\n }\n\n async postApdu(apdu: string): Promise<string> {\n const { data } = await this.apduClient.post<SpeculosApduDTO>(\n \"/apdu\",\n { data: apdu },\n { timeout: TIMEOUT },\n );\n return data.data;\n }\n\n async isServerAvailable(): Promise<boolean> {\n try {\n await this.apduClient.request<SpeculosEventsDTO>({\n method: \"GET\",\n url: \"/events\",\n timeout: TIMEOUT,\n });\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * open an SSE event stream using the fetch API.\n * - calls `onEvent` for each line starting with \"data: \"\n * - calls `onClose` when the stream ends or errors\n * - returns the ReadableStream so callers can `cancel()` it\n */\n async openEventStream(\n onEvent: (json: Record<string, unknown>) => void,\n onClose?: () => void,\n ): Promise<ReadableStream<Uint8Array>> {\n if (typeof fetch === \"undefined\") {\n throw new Error(\"global fetch is not available in Node < 18\");\n }\n if (!this.sseClient) {\n throw new Error(\"SSE not supported unless in E2E mode\");\n }\n\n const urlBase = this.sseClient.defaults.baseURL ?? this.baseUrl;\n const url = `${removeTrailingSlashes(urlBase)}/events?stream=true`;\n\n const controller = new AbortController();\n\n const headers: HeadersInit = {\n Accept: \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"X-Ledger-Client-Version\":\n (this.sseClient.defaults.headers?.common?.[\n \"X-Ledger-Client-Version\"\n ] as string) ?? \"ldmk-transport-speculos\",\n };\n\n const response = await fetch(url, {\n method: \"GET\",\n headers,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n controller.abort();\n throw new Error(`SSE request failed with status ${response.status}`);\n }\n\n const stream = response.body;\n if (!stream) {\n controller.abort();\n throw new Error(\"SSE response has no body stream.\");\n }\n\n const reader = stream.getReader();\n const decoder = new TextDecoder(\"utf-8\");\n let buffer = \"\";\n let closed = false;\n\n const finalize = () => {\n if (!closed) {\n closed = true;\n try {\n onClose?.();\n } catch {\n // swallow listener errors\n }\n }\n };\n\n const emitParsedEvent = (line: string) => {\n if (line.startsWith(\"data: \")) {\n const payload = line.slice(6);\n try {\n onEvent(JSON.parse(payload));\n } catch {\n onEvent({ data: payload });\n }\n }\n };\n\n (async () => {\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n // normalise line breaks and process complete lines\n const lines = buffer.split(/\\r?\\n/);\n buffer = lines.pop() ?? \"\"; // keep last partial line\n\n for (const line of lines) {\n emitParsedEvent(line);\n // other SSE fields ignored to mirror original behavior\n }\n }\n } catch {\n // network/reader error\n } finally {\n // flush any remaining buffered text as lines\n if (buffer.length) {\n for (const line of buffer.split(/\\r?\\n/)) {\n emitParsedEvent(line);\n }\n buffer = \"\";\n }\n finalize();\n }\n })();\n\n // consumers can cancel with: (await openEventStream(...)).cancel()\n return stream;\n }\n}\n\ntype SpeculosApduDTO = {\n data: string;\n};\n\ntype SpeculosEventsDTO = {\n events: Array<{\n text?: string;\n x?: number;\n y?: number;\n }>;\n};\n"],
5
- "mappings": "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",
6
- "names": ["HttpSpeculosDatasource_exports", "__export", "HttpSpeculosDatasource", "makeKeepAliveAxiosForSSE", "makeNoKeepAliveAxios", "__toCommonJS", "import_axios", "import_package", "TIMEOUT", "removeTrailingSlashes", "url", "baseUrl", "timeoutMs", "clientHeader", "axios", "_isE2E", "PACKAGE", "apdu", "data", "onEvent", "onClose", "urlBase", "controller", "headers", "response", "stream", "reader", "decoder", "buffer", "closed", "finalize", "emitParsedEvent", "line", "payload", "value", "done", "lines"]
4
+ "sourcesContent": ["import axios, { type AxiosInstance } from \"axios\";\n\nimport PACKAGE from \"@root/package.json\";\n\nimport { type SpeculosDatasource } from \"./SpeculosDatasource\";\n\nconst TIMEOUT = 2000; // 2 second timeout for availability check\n\nconst removeTrailingSlashes = (url: string) => url.replace(/\\/+$/, \"\");\n\nexport function axiosClientFactory(\n baseUrl: string,\n clientHeader: string,\n): AxiosInstance {\n return axios.create({\n baseURL: removeTrailingSlashes(baseUrl),\n timeout: 0,\n headers: {\n \"X-Ledger-Client-Version\": clientHeader,\n },\n transitional: { clarifyTimeoutError: true },\n });\n}\n\nexport class HttpSpeculosDatasource implements SpeculosDatasource {\n private readonly speculosAxiosClient: AxiosInstance;\n\n constructor(\n private readonly baseUrl: string,\n clientHeader: string = `ldmk-transport-speculos/${PACKAGE.version}`,\n ) {\n this.speculosAxiosClient = axiosClientFactory(baseUrl, clientHeader);\n }\n\n async postApdu(apdu: string): Promise<string> {\n const { data } = await this.speculosAxiosClient.post<SpeculosApduDTO>(\n \"/apdu\",\n {\n data: apdu,\n },\n );\n return data.data;\n }\n\n async isServerAvailable(): Promise<boolean> {\n try {\n await this.speculosAxiosClient.request<SpeculosEventsDTO>({\n method: \"GET\",\n url: \"/events\",\n timeout: TIMEOUT,\n });\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * open an SSE event stream using the fetch API.\n * - calls `onEvent` for each line starting with \"data: \"\n * - calls `onClose` when the stream ends or errors\n * - returns the ReadableStream so callers can `cancel()` it\n */\n async openEventStream(\n onEvent: (json: Record<string, unknown>) => void,\n onClose?: () => void,\n ): Promise<ReadableStream<Uint8Array>> {\n if (typeof fetch === \"undefined\") {\n throw new Error(\"global fetch is not available in Node < 18\");\n }\n\n const urlBase = this.speculosAxiosClient.defaults.baseURL ?? this.baseUrl;\n const url = `${removeTrailingSlashes(urlBase)}/events?stream=true`;\n\n const controller = new AbortController();\n\n const headers: HeadersInit = {\n Accept: \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"X-Ledger-Client-Version\":\n (this.speculosAxiosClient.defaults.headers?.common?.[\n \"X-Ledger-Client-Version\"\n ] as string) ?? \"ldmk-transport-speculos\",\n };\n\n const response = await fetch(url, {\n method: \"GET\",\n headers,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n controller.abort();\n throw new Error(`SSE request failed with status ${response.status}`);\n }\n\n const stream = response.body;\n if (!stream) {\n controller.abort();\n throw new Error(\"SSE response has no body stream.\");\n }\n\n const reader = stream.getReader();\n const decoder = new TextDecoder(\"utf-8\");\n let buffer = \"\";\n let closed = false;\n\n const finalize = () => {\n if (!closed) {\n closed = true;\n try {\n onClose?.();\n } catch {\n // swallow listener errors\n }\n }\n };\n\n const emitParsedEvent = (line: string) => {\n if (line.startsWith(\"data: \")) {\n const payload = line.slice(6);\n try {\n onEvent(JSON.parse(payload));\n } catch {\n onEvent({ data: payload });\n }\n }\n };\n\n (async () => {\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n // normalise line breaks and process complete lines\n const lines = buffer.split(/\\r?\\n/);\n buffer = lines.pop() ?? \"\"; // keep last partial line\n\n for (const line of lines) {\n emitParsedEvent(line);\n // other SSE fields ignored to mirror original behavior\n }\n }\n } catch {\n // network/reader error\n } finally {\n // flush any remaining buffered text as lines\n if (buffer.length) {\n for (const line of buffer.split(/\\r?\\n/)) {\n emitParsedEvent(line);\n }\n buffer = \"\";\n }\n finalize();\n }\n })();\n\n // consumers can cancel with: (await openEventStream(...)).cancel()\n return stream;\n }\n}\n\ntype SpeculosApduDTO = {\n data: string;\n};\n\ntype SpeculosEventsDTO = {\n events: Array<{\n text?: string;\n x?: number;\n y?: number;\n }>;\n};\n"],
5
+ "mappings": "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",
6
+ "names": ["HttpSpeculosDatasource_exports", "__export", "HttpSpeculosDatasource", "axiosClientFactory", "__toCommonJS", "import_axios", "import_package", "TIMEOUT", "removeTrailingSlashes", "url", "baseUrl", "clientHeader", "axios", "PACKAGE", "apdu", "data", "onEvent", "onClose", "urlBase", "controller", "headers", "response", "stream", "reader", "decoder", "buffer", "closed", "finalize", "emitParsedEvent", "line", "payload", "value", "done", "lines"]
7
7
  }
@@ -1,56 +1,60 @@
1
1
  {
2
- "name": "@ledgerhq/device-transport-kit-speculos",
3
- "version": "1.0.1",
4
- "license": "Apache-2.0",
5
- "private": false,
2
+ "dependencies": {
3
+ "@sentry/minimal": "catalog:",
4
+ "axios": "catalog:",
5
+ "purify-ts": "catalog:"
6
+ },
7
+ "devDependencies": {
8
+ "@ledgerhq/device-management-kit": "workspace:^",
9
+ "@ledgerhq/eslint-config-dsdk": "workspace:^",
10
+ "@ledgerhq/ldmk-tool": "workspace:^",
11
+ "@ledgerhq/prettier-config-dsdk": "workspace:^",
12
+ "@ledgerhq/tsconfig-dsdk": "workspace:^",
13
+ "@ledgerhq/vitest-config-dmk": "workspace:^",
14
+ "rxjs": "catalog:",
15
+ "ts-node": "catalog:"
16
+ },
6
17
  "exports": {
7
18
  ".": {
8
- "types": "./lib/types/index.d.ts",
9
19
  "import": "./lib/esm/index.js",
10
- "require": "./lib/cjs/index.js"
20
+ "require": "./lib/cjs/index.js",
21
+ "types": "./lib/types/index.d.ts"
11
22
  },
12
23
  "./*": {
13
- "types": "./lib/types/*",
14
24
  "import": "./lib/esm/*",
15
- "require": "./lib/cjs/*"
25
+ "require": "./lib/cjs/*",
26
+ "types": "./lib/types/*"
16
27
  }
17
28
  },
18
29
  "files": [
19
30
  "./lib"
20
31
  ],
32
+ "license": "Apache-2.0",
33
+ "name": "@ledgerhq/device-transport-kit-speculos",
34
+ "peerDependencies": {
35
+ "@ledgerhq/device-management-kit": "workspace:^",
36
+ "rxjs": "catalog:"
37
+ },
38
+ "private": false,
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/LedgerHQ/device-sdk-ts.git"
42
+ },
21
43
  "scripts": {
22
- "prebuild": "rimraf lib",
23
44
  "build": "pnpm ldmk-tool build --entryPoints index.ts,src/**/*.ts --tsconfig tsconfig.prod.json",
24
45
  "dev": "concurrently \"pnpm watch:builds\" \"pnpm watch:types\"",
25
- "watch:builds": "pnpm ldmk-tool watch --entryPoints index.ts,src/**/*.ts --tsconfig tsconfig.prod.json",
26
- "watch:types": "concurrently \"tsc --watch -p tsconfig.prod.json\" \"tsc-alias --watch -p tsconfig.prod.json\"",
27
46
  "lint": "eslint",
28
47
  "lint:fix": "pnpm lint --fix",
29
48
  "postpack": "find . -name '*.tgz' -exec cp {} ../../../dist/ \\; ",
49
+ "prebuild": "rimraf lib",
30
50
  "prettier": "prettier . --check",
31
51
  "prettier:fix": "prettier . --write",
32
- "typecheck": "tsc --noEmit",
33
52
  "test": "vitest run --passWithNoTests",
53
+ "test:coverage": "vitest run --coverage --passWithNoTests",
34
54
  "test:watch": "vitest --passWithNoTests",
35
- "test:coverage": "vitest run --coverage --passWithNoTests"
36
- },
37
- "dependencies": {
38
- "@sentry/minimal": "catalog:",
39
- "purify-ts": "catalog:",
40
- "axios": "catalog:"
41
- },
42
- "devDependencies": {
43
- "@ledgerhq/device-management-kit": "workspace:*",
44
- "@ledgerhq/eslint-config-dsdk": "workspace:*",
45
- "@ledgerhq/ldmk-tool": "workspace:*",
46
- "@ledgerhq/prettier-config-dsdk": "workspace:*",
47
- "@ledgerhq/tsconfig-dsdk": "workspace:*",
48
- "@ledgerhq/vitest-config-dmk": "workspace:*",
49
- "rxjs": "catalog:",
50
- "ts-node": "catalog:"
55
+ "typecheck": "tsc --noEmit",
56
+ "watch:builds": "pnpm ldmk-tool watch --entryPoints index.ts,src/**/*.ts --tsconfig tsconfig.prod.json",
57
+ "watch:types": "concurrently \"tsc --watch -p tsconfig.prod.json\" \"tsc-alias --watch -p tsconfig.prod.json\""
51
58
  },
52
- "peerDependencies": {
53
- "@ledgerhq/device-management-kit": "workspace:*",
54
- "rxjs": "catalog:"
55
- }
59
+ "version": "0.0.0-z-intent-queue-7-20251204111630"
56
60
  }
@@ -1,2 +1,2 @@
1
- import{ApduParser as v,bufferToHexaString as g,DeviceModelId as D,GeneralDmkError as h,OpeningConnectionError as b}from"@ledgerhq/device-management-kit";import{Left as p,Right as a}from"purify-ts";import{from as l}from"rxjs";import{HttpSpeculosDatasource as S}from"../internal/datasource/HttpSpeculosDatasource";const f="SPECULOS_HTTP_TRANSPORT";class m{logger;identifier=f;_speculosDataSource;connectedDevice=null;disconnectInterval=null;_isE2E;speculosDevice={id:"SpeculosID",deviceModel:{id:D.STAX,productName:"Speculos - App Name - version",usbProductId:16,bootloaderUsbProductId:1,getBlockSize(){return 32},blockSize:32,usbOnly:!0,memorySize:320*1024,masks:[823132160]},transport:this.identifier};constructor(e,t,n,r){this._isE2E=r??!1,this.logger=e("SpeculosTransport"),this._speculosDataSource=new S(n,this._isE2E)}isSupported(){return!0}getIdentifier(){return this.identifier}listenToAvailableDevices(){return l([[this.speculosDevice]])}startDiscovering(){return this.logger.debug("startDiscovering"),l([this.speculosDevice])}stopDiscovering(){this.logger.debug("stopDiscovering")}async connect(e){this.logger.debug("connect");const t=await this._speculosDataSource.postApdu("B0010000");this.logger.debug(`Hex Response: ${t}`);const n=this.createApduResponse(t),r=new v(n);r.extract8BitUInt();const s=r.encodeToString(r.extractFieldLVEncoded()),i=r.encodeToString(r.extractFieldLVEncoded());this.logger.debug(`App Name: ${s} and version ${i}`);const c=e.deviceId;try{const o={sendApdu:u=>this.sendApdu(c,e.deviceId,e.onDisconnect,u),deviceModel:{...this.speculosDevice.deviceModel,productName:`Speculos - ${s} - ${i}`,getBlockSize(){return 32}},transport:this.identifier,id:"SpeculosID",type:"USB"};return this.connectedDevice=o,this._isE2E||this.listenForDisconnect(e.onDisconnect,e.deviceId),a(o)}catch(o){return p(new b(o))}}async disconnect(e){return this.logger.debug("disconnect"),this.connectedDevice=null,Promise.resolve(a(void 0))}async sendApdu(e,t,n,r){try{const s=g(r).substring(2);this.logger.debug(`send APDU: ${s}`);const i=await this._speculosDataSource.postApdu(s),c=this.createApduResponse(i);return a(c)}catch(s){return this.connectedDevice&&(this.logger.debug("disconnecting"),n(t),this.disconnect({connectedDevice:this.connectedDevice}),this.disconnectInterval&&clearInterval(this.disconnectInterval)),p(new h(s))}}createApduResponse(e){return this.logger.debug(`Status code hex: ${e.substring(e.length-4,e.length)}`),this.logger.debug(`data hex: ${e.substring(0,e.length-4)}`),{statusCode:this.fromHexString(e.substring(e.length-4,e.length)),data:this.fromHexString(e.substring(0,e.length-4))}}fromHexString(e){return e?new Uint8Array(e.match(/.{1,2}/g).map(t=>parseInt(t,16))):Uint8Array.from([])}listenForDisconnect(e,t){this.disconnectInterval=setInterval(async()=>{await this._speculosDataSource.isServerAvailable()||(this.logger.info(`Speculos server unavailable, disconnecting device ${t}`),e(t),this.connectedDevice&&await this.disconnect({connectedDevice:this.connectedDevice}),this.disconnectInterval&&clearInterval(this.disconnectInterval))},2e3)}}const A=(d="http://127.0.0.1:5000",e=!1)=>({config:t,loggerServiceFactory:n})=>new m(n,t,d,e);export{m as SpeculosTransport,f as speculosIdentifier,A as speculosTransportFactory};
1
+ import{ApduParser as v,bufferToHexaString as g,DeviceModelId as D,GeneralDmkError as h,OpeningConnectionError as f}from"@ledgerhq/device-management-kit";import{Left as d,Right as a}from"purify-ts";import{from as l}from"rxjs";import{HttpSpeculosDatasource as S}from"../internal/datasource/HttpSpeculosDatasource";const m="SPECULOS_HTTP_TRANSPORT";class y{logger;identifier=m;_speculosDataSource;connectedDevice=null;disconnectInterval=null;_isE2E;speculosDevice={id:"SpeculosID",deviceModel:{id:D.STAX,productName:"Speculos - App Name - version",usbProductId:16,bootloaderUsbProductId:1,getBlockSize(){return 32},blockSize:32,usbOnly:!0,memorySize:320*1024,masks:[823132160]},transport:this.identifier};constructor(e,t,n,r){this._isE2E=r??!1,this.logger=e("SpeculosTransport"),this._speculosDataSource=new S(n)}isSupported(){return!0}getIdentifier(){return this.identifier}listenToAvailableDevices(){return l([[this.speculosDevice]])}startDiscovering(){return this.logger.debug("startDiscovering"),l([this.speculosDevice])}stopDiscovering(){this.logger.debug("stopDiscovering")}async connect(e){this.logger.debug("connect");const t=await this._speculosDataSource.postApdu("B0010000");this.logger.debug(`Hex Response: ${t}`);const n=this.createApduResponse(t),r=new v(n);r.extract8BitUInt();const s=r.encodeToString(r.extractFieldLVEncoded()),o=r.encodeToString(r.extractFieldLVEncoded());this.logger.debug(`App Name: ${s} and version ${o}`);const c=e.deviceId;try{const i={sendApdu:u=>this.sendApdu(c,e.deviceId,e.onDisconnect,u),deviceModel:{...this.speculosDevice.deviceModel,productName:`Speculos - ${s} - ${o}`,getBlockSize(){return 32}},transport:this.identifier,id:"SpeculosID",type:"USB"};return this.connectedDevice=i,this._isE2E||this.listenForDisconnect(e.onDisconnect,e.deviceId),a(i)}catch(i){return d(new f(i))}}async disconnect(e){return this.logger.debug("disconnect"),this.connectedDevice=null,Promise.resolve(a(void 0))}async sendApdu(e,t,n,r){try{const s=g(r).substring(2),o=await this._speculosDataSource.postApdu(s),c=this.createApduResponse(o);return a(c)}catch(s){return this.connectedDevice&&(this.logger.debug("disconnecting"),n(t),this.disconnect({connectedDevice:this.connectedDevice}),this.disconnectInterval&&clearInterval(this.disconnectInterval)),d(new h(s))}}createApduResponse(e){return{statusCode:this.fromHexString(e.substring(e.length-4,e.length)),data:this.fromHexString(e.substring(0,e.length-4))}}fromHexString(e){return e?new Uint8Array(e.match(/.{1,2}/g).map(t=>parseInt(t,16))):Uint8Array.from([])}listenForDisconnect(e,t){this.disconnectInterval=setInterval(async()=>{await this._speculosDataSource.isServerAvailable()||(this.logger.info(`Speculos server unavailable, disconnecting device ${t}`),e(t),this.connectedDevice&&await this.disconnect({connectedDevice:this.connectedDevice}),this.disconnectInterval&&clearInterval(this.disconnectInterval))},2e3)}}const A=(p="http://127.0.0.1:5000",e=!1)=>({config:t,loggerServiceFactory:n})=>new y(n,t,p,e);export{y as SpeculosTransport,m as speculosIdentifier,A as speculosTransportFactory};
2
2
  //# sourceMappingURL=SpeculosTransport.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/api/SpeculosTransport.ts"],
4
- "sourcesContent": ["import {\n ApduParser,\n type ApduResponse,\n bufferToHexaString,\n type ConnectError,\n type DeviceId,\n DeviceModelId,\n type DisconnectHandler,\n type DmkConfig,\n type DmkError,\n GeneralDmkError,\n type LoggerPublisherService,\n OpeningConnectionError,\n type Transport,\n type TransportConnectedDevice,\n type TransportDiscoveredDevice,\n type TransportFactory,\n type TransportIdentifier,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Either, Left, Right } from \"purify-ts\";\nimport { from, type Observable } from \"rxjs\";\n\nimport { HttpSpeculosDatasource } from \"@internal/datasource/HttpSpeculosDatasource\";\nimport { type SpeculosDatasource } from \"@internal/datasource/SpeculosDatasource\";\n\nexport const speculosIdentifier: TransportIdentifier =\n \"SPECULOS_HTTP_TRANSPORT\";\n\nexport class SpeculosTransport implements Transport {\n private logger: LoggerPublisherService;\n private readonly identifier: TransportIdentifier = speculosIdentifier;\n private readonly _speculosDataSource: SpeculosDatasource;\n private connectedDevice: TransportConnectedDevice | null = null;\n private disconnectInterval: NodeJS.Timeout | null = null;\n private readonly _isE2E: boolean;\n private readonly speculosDevice: TransportDiscoveredDevice = {\n id: \"SpeculosID\", //TODO make it dynamic at creation\n deviceModel: {\n id: DeviceModelId.STAX,\n productName: \"Speculos - App Name - version\",\n usbProductId: 0x10,\n bootloaderUsbProductId: 0x0001,\n getBlockSize() {\n return 32;\n },\n blockSize: 32,\n usbOnly: true,\n memorySize: 320 * 1024,\n masks: [0x31100000],\n },\n transport: this.identifier,\n };\n\n constructor(\n loggerServiceFactory: (tag: string) => LoggerPublisherService,\n _config: DmkConfig,\n speculosUrl: string,\n isE2E?: boolean,\n ) {\n this._isE2E = isE2E ?? false;\n this.logger = loggerServiceFactory(\"SpeculosTransport\");\n this._speculosDataSource = new HttpSpeculosDatasource(\n speculosUrl,\n this._isE2E,\n ); // See how to pass properly speculos config.\n }\n\n isSupported(): boolean {\n return true;\n }\n\n getIdentifier(): TransportIdentifier {\n return this.identifier;\n }\n\n listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]> {\n return from([[this.speculosDevice]]);\n }\n\n startDiscovering(): Observable<TransportDiscoveredDevice> {\n this.logger.debug(\"startDiscovering\");\n return from([this.speculosDevice]);\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\n const hexResponse = await this._speculosDataSource.postApdu(\"B0010000\");\n this.logger.debug(`Hex Response: ${hexResponse}`);\n const apduResponse = this.createApduResponse(hexResponse);\n const parser = new ApduParser(apduResponse);\n\n //Copy paste from GetAppAndVersionCommand\n parser.extract8BitUInt(); //Need otherwise parser is not in the right position\n const appName = parser.encodeToString(parser.extractFieldLVEncoded());\n const appVersion = parser.encodeToString(parser.extractFieldLVEncoded());\n\n this.logger.debug(`App Name: ${appName} and version ${appVersion}`);\n\n const sessionId: string = params.deviceId;\n try {\n const connectedDevice: TransportConnectedDevice = {\n sendApdu: (apdu) => {\n return this.sendApdu(\n sessionId,\n params.deviceId,\n params.onDisconnect,\n apdu,\n );\n },\n deviceModel: {\n ...this.speculosDevice.deviceModel,\n productName: `Speculos - ${appName} - ${appVersion}`,\n getBlockSize() {\n return 32;\n },\n },\n transport: this.identifier,\n id: \"SpeculosID\", //TODO make it dynamic at creation\n type: \"USB\",\n };\n\n this.connectedDevice = connectedDevice;\n if (!this._isE2E) {\n this.listenForDisconnect(params.onDisconnect, params.deviceId);\n }\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 this.connectedDevice = null;\n return Promise.resolve(Right(undefined));\n }\n\n async sendApdu(\n _sessionId: string,\n deviceId: DeviceId,\n onDisconnect: DisconnectHandler,\n apdu: Uint8Array,\n ): Promise<Either<DmkError, ApduResponse>> {\n try {\n const hexApdu = bufferToHexaString(apdu).substring(2);\n this.logger.debug(`send APDU: ${hexApdu}`);\n const hexResponse: string =\n await this._speculosDataSource.postApdu(hexApdu);\n const apduResponse = this.createApduResponse(hexResponse);\n return Right(apduResponse);\n } catch (error) {\n if (this.connectedDevice) {\n this.logger.debug(\"disconnecting\");\n onDisconnect(deviceId);\n this.disconnect({\n connectedDevice: this.connectedDevice,\n });\n\n if (this.disconnectInterval) {\n clearInterval(this.disconnectInterval);\n }\n }\n return Left(new GeneralDmkError(error));\n }\n }\n\n private createApduResponse(hexApdu: string): ApduResponse {\n this.logger.debug(\n `Status code hex: ${hexApdu.substring(hexApdu.length - 4, hexApdu.length)}`,\n );\n this.logger.debug(`data hex: ${hexApdu.substring(0, hexApdu.length - 4)}`);\n\n const apduResponse = {\n statusCode: this.fromHexString(\n hexApdu.substring(hexApdu.length - 4, hexApdu.length),\n ),\n data: this.fromHexString(hexApdu.substring(0, hexApdu.length - 4)),\n };\n return apduResponse;\n }\n\n //TODO: Move this to a helper\n private fromHexString(hexString: string): Uint8Array {\n if (!hexString) {\n return Uint8Array.from([]);\n }\n return new Uint8Array(\n hexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16)),\n );\n }\n\n private listenForDisconnect(\n onDisconnect: DisconnectHandler,\n deviceId: DeviceId,\n ): void {\n this.disconnectInterval = setInterval(async () => {\n const isServerAvailable =\n await this._speculosDataSource.isServerAvailable();\n\n if (!isServerAvailable) {\n this.logger.info(\n `Speculos server unavailable, disconnecting device ${deviceId}`,\n );\n onDisconnect(deviceId);\n\n if (this.connectedDevice) {\n await this.disconnect({\n connectedDevice: this.connectedDevice,\n });\n }\n\n if (this.disconnectInterval) {\n clearInterval(this.disconnectInterval);\n }\n }\n }, 2000);\n }\n}\n\nexport const speculosTransportFactory: (\n speculosUrl?: string,\n isE2E?: boolean,\n) => TransportFactory =\n (speculosUrl = \"http://127.0.0.1:5000\", isE2E = false) =>\n ({ config, loggerServiceFactory }) =>\n new SpeculosTransport(loggerServiceFactory, config, speculosUrl, isE2E);\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n ApduParser,\n type ApduResponse,\n bufferToHexaString,\n type ConnectError,\n type DeviceId,\n DeviceModelId,\n type DisconnectHandler,\n type DmkConfig,\n type DmkError,\n GeneralDmkError,\n type LoggerPublisherService,\n OpeningConnectionError,\n type Transport,\n type TransportConnectedDevice,\n type TransportDiscoveredDevice,\n type TransportFactory,\n type TransportIdentifier,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Either, Left, Right } from \"purify-ts\";\nimport { from, type Observable } from \"rxjs\";\n\nimport { HttpSpeculosDatasource } from \"@internal/datasource/HttpSpeculosDatasource\";\nimport { type SpeculosDatasource } from \"@internal/datasource/SpeculosDatasource\";\n\nexport const speculosIdentifier: TransportIdentifier =\n \"SPECULOS_HTTP_TRANSPORT\";\n\nexport class SpeculosTransport implements Transport {\n private logger: LoggerPublisherService;\n private readonly identifier: TransportIdentifier = speculosIdentifier;\n private readonly _speculosDataSource: SpeculosDatasource;\n private connectedDevice: TransportConnectedDevice | null = null;\n private disconnectInterval: NodeJS.Timeout | null = null;\n private readonly _isE2E: boolean;\n private readonly speculosDevice: TransportDiscoveredDevice = {\n id: \"SpeculosID\", //TODO make it dynamic at creation\n deviceModel: {\n id: DeviceModelId.STAX,\n productName: \"Speculos - App Name - version\",\n usbProductId: 0x10,\n bootloaderUsbProductId: 0x0001,\n getBlockSize() {\n return 32;\n },\n blockSize: 32,\n usbOnly: true,\n memorySize: 320 * 1024,\n masks: [0x31100000],\n },\n transport: this.identifier,\n };\n\n constructor(\n loggerServiceFactory: (tag: string) => LoggerPublisherService,\n _config: DmkConfig,\n speculosUrl: string,\n isE2E?: boolean,\n ) {\n this._isE2E = isE2E ?? false;\n this.logger = loggerServiceFactory(\"SpeculosTransport\");\n this._speculosDataSource = new HttpSpeculosDatasource(speculosUrl); // See how to pass properly speculos config.\n }\n\n isSupported(): boolean {\n return true;\n }\n\n getIdentifier(): TransportIdentifier {\n return this.identifier;\n }\n\n listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]> {\n return from([[this.speculosDevice]]);\n }\n\n startDiscovering(): Observable<TransportDiscoveredDevice> {\n this.logger.debug(\"startDiscovering\");\n return from([this.speculosDevice]);\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\n const hexResponse = await this._speculosDataSource.postApdu(\"B0010000\");\n this.logger.debug(`Hex Response: ${hexResponse}`);\n const apduResponse = this.createApduResponse(hexResponse);\n const parser = new ApduParser(apduResponse);\n\n //Copy paste from GetAppAndVersionCommand\n parser.extract8BitUInt(); //Need otherwise parser is not in the right position\n const appName = parser.encodeToString(parser.extractFieldLVEncoded());\n const appVersion = parser.encodeToString(parser.extractFieldLVEncoded());\n\n this.logger.debug(`App Name: ${appName} and version ${appVersion}`);\n\n const sessionId: string = params.deviceId;\n try {\n const connectedDevice: TransportConnectedDevice = {\n sendApdu: (apdu) => {\n return this.sendApdu(\n sessionId,\n params.deviceId,\n params.onDisconnect,\n apdu,\n );\n },\n deviceModel: {\n ...this.speculosDevice.deviceModel,\n productName: `Speculos - ${appName} - ${appVersion}`,\n getBlockSize() {\n return 32;\n },\n },\n transport: this.identifier,\n id: \"SpeculosID\", //TODO make it dynamic at creation\n type: \"USB\",\n };\n\n this.connectedDevice = connectedDevice;\n if (!this._isE2E) {\n this.listenForDisconnect(params.onDisconnect, params.deviceId);\n }\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 this.connectedDevice = null;\n return Promise.resolve(Right(undefined));\n }\n\n async sendApdu(\n _sessionId: string,\n deviceId: DeviceId,\n onDisconnect: DisconnectHandler,\n apdu: Uint8Array,\n ): Promise<Either<DmkError, ApduResponse>> {\n try {\n const hexApdu = bufferToHexaString(apdu).substring(2);\n const hexResponse: string =\n await this._speculosDataSource.postApdu(hexApdu);\n const apduResponse = this.createApduResponse(hexResponse);\n return Right(apduResponse);\n } catch (error) {\n if (this.connectedDevice) {\n this.logger.debug(\"disconnecting\");\n onDisconnect(deviceId);\n this.disconnect({\n connectedDevice: this.connectedDevice,\n });\n\n if (this.disconnectInterval) {\n clearInterval(this.disconnectInterval);\n }\n }\n return Left(new GeneralDmkError(error));\n }\n }\n\n private createApduResponse(hexApdu: string): ApduResponse {\n const apduResponse = {\n statusCode: this.fromHexString(\n hexApdu.substring(hexApdu.length - 4, hexApdu.length),\n ),\n data: this.fromHexString(hexApdu.substring(0, hexApdu.length - 4)),\n };\n return apduResponse;\n }\n\n //TODO: Move this to a helper\n private fromHexString(hexString: string): Uint8Array {\n if (!hexString) {\n return Uint8Array.from([]);\n }\n return new Uint8Array(\n hexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16)),\n );\n }\n\n private listenForDisconnect(\n onDisconnect: DisconnectHandler,\n deviceId: DeviceId,\n ): void {\n this.disconnectInterval = setInterval(async () => {\n const isServerAvailable =\n await this._speculosDataSource.isServerAvailable();\n\n if (!isServerAvailable) {\n this.logger.info(\n `Speculos server unavailable, disconnecting device ${deviceId}`,\n );\n onDisconnect(deviceId);\n\n if (this.connectedDevice) {\n await this.disconnect({\n connectedDevice: this.connectedDevice,\n });\n }\n\n if (this.disconnectInterval) {\n clearInterval(this.disconnectInterval);\n }\n }\n }, 2000);\n }\n}\n\nexport const speculosTransportFactory: (\n speculosUrl?: string,\n isE2E?: boolean,\n) => TransportFactory =\n (speculosUrl = \"http://127.0.0.1:5000\", isE2E = false) =>\n ({ config, loggerServiceFactory }) =>\n new SpeculosTransport(loggerServiceFactory, config, speculosUrl, isE2E);\n"],
5
+ "mappings": "AAAA,OACE,cAAAA,EAEA,sBAAAC,EAGA,iBAAAC,EAIA,mBAAAC,EAEA,0BAAAC,MAMK,kCACP,OAAsB,QAAAC,EAAM,SAAAC,MAAa,YACzC,OAAS,QAAAC,MAA6B,OAEtC,OAAS,0BAAAC,MAA8B,8CAGhC,MAAMC,EACX,0BAEK,MAAMC,CAAuC,CAC1C,OACS,WAAkCD,EAClC,oBACT,gBAAmD,KACnD,mBAA4C,KACnC,OACA,eAA4C,CAC3D,GAAI,aACJ,YAAa,CACX,GAAIP,EAAc,KAClB,YAAa,gCACb,aAAc,GACd,uBAAwB,EACxB,cAAe,CACb,MAAO,GACT,EACA,UAAW,GACX,QAAS,GACT,WAAY,IAAM,KAClB,MAAO,CAAC,SAAU,CACpB,EACA,UAAW,KAAK,UAClB,EAEA,YACES,EACAC,EACAC,EACAC,EACA,CACA,KAAK,OAASA,GAAS,GACvB,KAAK,OAASH,EAAqB,mBAAmB,EACtD,KAAK,oBAAsB,IAAIH,EAAuBK,CAAW,CACnE,CAEA,aAAuB,CACrB,MAAO,EACT,CAEA,eAAqC,CACnC,OAAO,KAAK,UACd,CAEA,0BAAoE,CAClE,OAAON,EAAK,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,CACrC,CAEA,kBAA0D,CACxD,YAAK,OAAO,MAAM,kBAAkB,EAC7BA,EAAK,CAAC,KAAK,cAAc,CAAC,CACnC,CAEA,iBAAwB,CAEtB,KAAK,OAAO,MAAM,iBAAiB,CACrC,CAEA,MAAM,QAAQQ,EAG8C,CAC1D,KAAK,OAAO,MAAM,SAAS,EAE3B,MAAMC,EAAc,MAAM,KAAK,oBAAoB,SAAS,UAAU,EACtE,KAAK,OAAO,MAAM,iBAAiBA,CAAW,EAAE,EAChD,MAAMC,EAAe,KAAK,mBAAmBD,CAAW,EAClDE,EAAS,IAAIlB,EAAWiB,CAAY,EAG1CC,EAAO,gBAAgB,EACvB,MAAMC,EAAUD,EAAO,eAAeA,EAAO,sBAAsB,CAAC,EAC9DE,EAAaF,EAAO,eAAeA,EAAO,sBAAsB,CAAC,EAEvE,KAAK,OAAO,MAAM,aAAaC,CAAO,gBAAgBC,CAAU,EAAE,EAElE,MAAMC,EAAoBN,EAAO,SACjC,GAAI,CACF,MAAMO,EAA4C,CAChD,SAAWC,GACF,KAAK,SACVF,EACAN,EAAO,SACPA,EAAO,aACPQ,CACF,EAEF,YAAa,CACX,GAAG,KAAK,eAAe,YACvB,YAAa,cAAcJ,CAAO,MAAMC,CAAU,GAClD,cAAe,CACb,MAAO,GACT,CACF,EACA,UAAW,KAAK,WAChB,GAAI,aACJ,KAAM,KACR,EAEA,YAAK,gBAAkBE,EAClB,KAAK,QACR,KAAK,oBAAoBP,EAAO,aAAcA,EAAO,QAAQ,EAExDT,EAAMgB,CAAe,CAC9B,OAASE,EAAO,CACd,OAAOnB,EAAK,IAAID,EAAuBoB,CAAc,CAAC,CACxD,CACF,CAEA,MAAM,WAAWC,EAEmB,CAClC,YAAK,OAAO,MAAM,YAAY,EAC9B,KAAK,gBAAkB,KAChB,QAAQ,QAAQnB,EAAM,MAAS,CAAC,CACzC,CAEA,MAAM,SACJoB,EACAC,EACAC,EACAL,EACyC,CACzC,GAAI,CACF,MAAMM,EAAU5B,EAAmBsB,CAAI,EAAE,UAAU,CAAC,EAC9CP,EACJ,MAAM,KAAK,oBAAoB,SAASa,CAAO,EAC3CZ,EAAe,KAAK,mBAAmBD,CAAW,EACxD,OAAOV,EAAMW,CAAY,CAC3B,OAASO,EAAO,CACd,OAAI,KAAK,kBACP,KAAK,OAAO,MAAM,eAAe,EACjCI,EAAaD,CAAQ,EACrB,KAAK,WAAW,CACd,gBAAiB,KAAK,eACxB,CAAC,EAEG,KAAK,oBACP,cAAc,KAAK,kBAAkB,GAGlCtB,EAAK,IAAIF,EAAgBqB,CAAK,CAAC,CACxC,CACF,CAEQ,mBAAmBK,EAA+B,CAOxD,MANqB,CACnB,WAAY,KAAK,cACfA,EAAQ,UAAUA,EAAQ,OAAS,EAAGA,EAAQ,MAAM,CACtD,EACA,KAAM,KAAK,cAAcA,EAAQ,UAAU,EAAGA,EAAQ,OAAS,CAAC,CAAC,CACnE,CAEF,CAGQ,cAAcC,EAA+B,CACnD,OAAKA,EAGE,IAAI,WACTA,EAAU,MAAM,SAAS,EAAG,IAAKC,GAAS,SAASA,EAAM,EAAE,CAAC,CAC9D,EAJS,WAAW,KAAK,CAAC,CAAC,CAK7B,CAEQ,oBACNH,EACAD,EACM,CACN,KAAK,mBAAqB,YAAY,SAAY,CAE9C,MAAM,KAAK,oBAAoB,kBAAkB,IAGjD,KAAK,OAAO,KACV,qDAAqDA,CAAQ,EAC/D,EACAC,EAAaD,CAAQ,EAEjB,KAAK,iBACP,MAAM,KAAK,WAAW,CACpB,gBAAiB,KAAK,eACxB,CAAC,EAGC,KAAK,oBACP,cAAc,KAAK,kBAAkB,EAG3C,EAAG,GAAI,CACT,CACF,CAEO,MAAMK,EAIX,CAACnB,EAAc,wBAAyBC,EAAQ,KAChD,CAAC,CAAE,OAAAmB,EAAQ,qBAAAtB,CAAqB,IAC9B,IAAID,EAAkBC,EAAsBsB,EAAQpB,EAAaC,CAAK",
6
6
  "names": ["ApduParser", "bufferToHexaString", "DeviceModelId", "GeneralDmkError", "OpeningConnectionError", "Left", "Right", "from", "HttpSpeculosDatasource", "speculosIdentifier", "SpeculosTransport", "loggerServiceFactory", "_config", "speculosUrl", "isE2E", "params", "hexResponse", "apduResponse", "parser", "appName", "appVersion", "sessionId", "connectedDevice", "apdu", "error", "_params", "_sessionId", "deviceId", "onDisconnect", "hexApdu", "hexString", "byte", "speculosTransportFactory", "config"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import h from"axios";import A from"../../../package.json";const u=1e4,d=t=>t.replace(/\/+$/,"");function w(t,e,r){return h.create({baseURL:d(t),timeout:e,headers:{"X-Ledger-Client-Version":r},transitional:{clarifyTimeoutError:!0}})}function C(t,e){return h.create({baseURL:d(t),timeout:0,headers:{"X-Ledger-Client-Version":e},transitional:{clarifyTimeoutError:!0}})}class D{constructor(e,r=!1,o=`ldmk-transport-speculos/${A.version}`){this.baseUrl=e;this._isE2E=r;this.apduClient=w(e,u,o),this._isE2E&&(this.sseClient=C(e,o))}apduClient;sseClient=null;async postApdu(e){const{data:r}=await this.apduClient.post("/apdu",{data:e},{timeout:u});return r.data}async isServerAvailable(){try{return await this.apduClient.request({method:"GET",url:"/events",timeout:u}),!0}catch{return!1}}async openEventStream(e,r){if(typeof fetch>"u")throw new Error("global fetch is not available in Node < 18");if(!this.sseClient)throw new Error("SSE not supported unless in E2E mode");const o=this.sseClient.defaults.baseURL??this.baseUrl,y=`${d(o)}/events?stream=true`,i=new AbortController,g={Accept:"text/event-stream","Cache-Control":"no-cache","X-Ledger-Client-Version":this.sseClient.defaults.headers?.common?.["X-Ledger-Client-Version"]??"ldmk-transport-speculos"},l=await fetch(y,{method:"GET",headers:g,signal:i.signal});if(!l.ok)throw i.abort(),new Error(`SSE request failed with status ${l.status}`);const c=l.body;if(!c)throw i.abort(),new Error("SSE response has no body stream.");const E=c.getReader(),v=new TextDecoder("utf-8");let s="",p=!1;const S=()=>{if(!p){p=!0;try{r?.()}catch{}}},m=n=>{if(n.startsWith("data: ")){const a=n.slice(6);try{e(JSON.parse(a))}catch{e({data:a})}}};return(async()=>{try{for(;;){const{value:n,done:a}=await E.read();if(a)break;s+=v.decode(n,{stream:!0});const f=s.split(/\r?\n/);s=f.pop()??"";for(const b of f)m(b)}}catch{}finally{if(s.length){for(const n of s.split(/\r?\n/))m(n);s=""}S()}})(),c}}export{D as HttpSpeculosDatasource,C as makeKeepAliveAxiosForSSE,w as makeNoKeepAliveAxios};
1
+ import v from"axios";import x from"../../../package.json";const S=2e3,d=o=>o.replace(/\/+$/,"");function w(o,e){return v.create({baseURL:d(o),timeout:0,headers:{"X-Ledger-Client-Version":e},transitional:{clarifyTimeoutError:!0}})}class T{constructor(e,r=`ldmk-transport-speculos/${x.version}`){this.baseUrl=e;this.speculosAxiosClient=w(e,r)}speculosAxiosClient;async postApdu(e){const{data:r}=await this.speculosAxiosClient.post("/apdu",{data:e});return r.data}async isServerAvailable(){try{return await this.speculosAxiosClient.request({method:"GET",url:"/events",timeout:S}),!0}catch{return!1}}async openEventStream(e,r){if(typeof fetch>"u")throw new Error("global fetch is not available in Node < 18");const f=this.speculosAxiosClient.defaults.baseURL??this.baseUrl,m=`${d(f)}/events?stream=true`,a=new AbortController,h={Accept:"text/event-stream","Cache-Control":"no-cache","X-Ledger-Client-Version":this.speculosAxiosClient.defaults.headers?.common?.["X-Ledger-Client-Version"]??"ldmk-transport-speculos"},i=await fetch(m,{method:"GET",headers:h,signal:a.signal});if(!i.ok)throw a.abort(),new Error(`SSE request failed with status ${i.status}`);const c=i.body;if(!c)throw a.abort(),new Error("SSE response has no body stream.");const y=c.getReader(),g=new TextDecoder("utf-8");let t="",l=!1;const A=()=>{if(!l){l=!0;try{r?.()}catch{}}},u=s=>{if(s.startsWith("data: ")){const n=s.slice(6);try{e(JSON.parse(n))}catch{e({data:n})}}};return(async()=>{try{for(;;){const{value:s,done:n}=await y.read();if(n)break;t+=g.decode(s,{stream:!0});const p=t.split(/\r?\n/);t=p.pop()??"";for(const b of p)u(b)}}catch{}finally{if(t.length){for(const s of t.split(/\r?\n/))u(s);t=""}A()}})(),c}}export{T as HttpSpeculosDatasource,w as axiosClientFactory};
2
2
  //# sourceMappingURL=HttpSpeculosDatasource.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/internal/datasource/HttpSpeculosDatasource.ts"],
4
- "sourcesContent": ["import axios, { type AxiosInstance } from \"axios\";\n\nimport PACKAGE from \"@root/package.json\";\n\nimport { type SpeculosDatasource } from \"./SpeculosDatasource\";\n\nconst TIMEOUT = 10000; // 10 second timeout for availability check\n\nconst removeTrailingSlashes = (url: string) => url.replace(/\\/+$/, \"\");\n\nexport function makeNoKeepAliveAxios(\n baseUrl: string,\n timeoutMs: number,\n clientHeader: string,\n): AxiosInstance {\n return axios.create({\n baseURL: removeTrailingSlashes(baseUrl),\n timeout: timeoutMs,\n headers: {\n \"X-Ledger-Client-Version\": clientHeader,\n },\n transitional: { clarifyTimeoutError: true },\n });\n}\n\nexport function makeKeepAliveAxiosForSSE(\n baseUrl: string,\n clientHeader: string,\n): AxiosInstance {\n return axios.create({\n baseURL: removeTrailingSlashes(baseUrl),\n timeout: 0, // no timeout for SSE\n headers: {\n \"X-Ledger-Client-Version\": clientHeader,\n },\n transitional: { clarifyTimeoutError: true },\n });\n}\n\nexport class HttpSpeculosDatasource implements SpeculosDatasource {\n private readonly apduClient: AxiosInstance;\n private readonly sseClient: AxiosInstance | null = null;\n\n constructor(\n private readonly baseUrl: string,\n private readonly _isE2E: boolean = false,\n clientHeader: string = `ldmk-transport-speculos/${PACKAGE.version}`,\n ) {\n this.apduClient = makeNoKeepAliveAxios(baseUrl, TIMEOUT, clientHeader);\n if (this._isE2E) {\n this.sseClient = makeKeepAliveAxiosForSSE(baseUrl, clientHeader);\n }\n }\n\n async postApdu(apdu: string): Promise<string> {\n const { data } = await this.apduClient.post<SpeculosApduDTO>(\n \"/apdu\",\n { data: apdu },\n { timeout: TIMEOUT },\n );\n return data.data;\n }\n\n async isServerAvailable(): Promise<boolean> {\n try {\n await this.apduClient.request<SpeculosEventsDTO>({\n method: \"GET\",\n url: \"/events\",\n timeout: TIMEOUT,\n });\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * open an SSE event stream using the fetch API.\n * - calls `onEvent` for each line starting with \"data: \"\n * - calls `onClose` when the stream ends or errors\n * - returns the ReadableStream so callers can `cancel()` it\n */\n async openEventStream(\n onEvent: (json: Record<string, unknown>) => void,\n onClose?: () => void,\n ): Promise<ReadableStream<Uint8Array>> {\n if (typeof fetch === \"undefined\") {\n throw new Error(\"global fetch is not available in Node < 18\");\n }\n if (!this.sseClient) {\n throw new Error(\"SSE not supported unless in E2E mode\");\n }\n\n const urlBase = this.sseClient.defaults.baseURL ?? this.baseUrl;\n const url = `${removeTrailingSlashes(urlBase)}/events?stream=true`;\n\n const controller = new AbortController();\n\n const headers: HeadersInit = {\n Accept: \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"X-Ledger-Client-Version\":\n (this.sseClient.defaults.headers?.common?.[\n \"X-Ledger-Client-Version\"\n ] as string) ?? \"ldmk-transport-speculos\",\n };\n\n const response = await fetch(url, {\n method: \"GET\",\n headers,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n controller.abort();\n throw new Error(`SSE request failed with status ${response.status}`);\n }\n\n const stream = response.body;\n if (!stream) {\n controller.abort();\n throw new Error(\"SSE response has no body stream.\");\n }\n\n const reader = stream.getReader();\n const decoder = new TextDecoder(\"utf-8\");\n let buffer = \"\";\n let closed = false;\n\n const finalize = () => {\n if (!closed) {\n closed = true;\n try {\n onClose?.();\n } catch {\n // swallow listener errors\n }\n }\n };\n\n const emitParsedEvent = (line: string) => {\n if (line.startsWith(\"data: \")) {\n const payload = line.slice(6);\n try {\n onEvent(JSON.parse(payload));\n } catch {\n onEvent({ data: payload });\n }\n }\n };\n\n (async () => {\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n // normalise line breaks and process complete lines\n const lines = buffer.split(/\\r?\\n/);\n buffer = lines.pop() ?? \"\"; // keep last partial line\n\n for (const line of lines) {\n emitParsedEvent(line);\n // other SSE fields ignored to mirror original behavior\n }\n }\n } catch {\n // network/reader error\n } finally {\n // flush any remaining buffered text as lines\n if (buffer.length) {\n for (const line of buffer.split(/\\r?\\n/)) {\n emitParsedEvent(line);\n }\n buffer = \"\";\n }\n finalize();\n }\n })();\n\n // consumers can cancel with: (await openEventStream(...)).cancel()\n return stream;\n }\n}\n\ntype SpeculosApduDTO = {\n data: string;\n};\n\ntype SpeculosEventsDTO = {\n events: Array<{\n text?: string;\n x?: number;\n y?: number;\n }>;\n};\n"],
5
- "mappings": "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",
6
- "names": ["axios", "PACKAGE", "TIMEOUT", "removeTrailingSlashes", "url", "makeNoKeepAliveAxios", "baseUrl", "timeoutMs", "clientHeader", "makeKeepAliveAxiosForSSE", "HttpSpeculosDatasource", "_isE2E", "apdu", "data", "onEvent", "onClose", "urlBase", "controller", "headers", "response", "stream", "reader", "decoder", "buffer", "closed", "finalize", "emitParsedEvent", "line", "payload", "value", "done", "lines"]
4
+ "sourcesContent": ["import axios, { type AxiosInstance } from \"axios\";\n\nimport PACKAGE from \"@root/package.json\";\n\nimport { type SpeculosDatasource } from \"./SpeculosDatasource\";\n\nconst TIMEOUT = 2000; // 2 second timeout for availability check\n\nconst removeTrailingSlashes = (url: string) => url.replace(/\\/+$/, \"\");\n\nexport function axiosClientFactory(\n baseUrl: string,\n clientHeader: string,\n): AxiosInstance {\n return axios.create({\n baseURL: removeTrailingSlashes(baseUrl),\n timeout: 0,\n headers: {\n \"X-Ledger-Client-Version\": clientHeader,\n },\n transitional: { clarifyTimeoutError: true },\n });\n}\n\nexport class HttpSpeculosDatasource implements SpeculosDatasource {\n private readonly speculosAxiosClient: AxiosInstance;\n\n constructor(\n private readonly baseUrl: string,\n clientHeader: string = `ldmk-transport-speculos/${PACKAGE.version}`,\n ) {\n this.speculosAxiosClient = axiosClientFactory(baseUrl, clientHeader);\n }\n\n async postApdu(apdu: string): Promise<string> {\n const { data } = await this.speculosAxiosClient.post<SpeculosApduDTO>(\n \"/apdu\",\n {\n data: apdu,\n },\n );\n return data.data;\n }\n\n async isServerAvailable(): Promise<boolean> {\n try {\n await this.speculosAxiosClient.request<SpeculosEventsDTO>({\n method: \"GET\",\n url: \"/events\",\n timeout: TIMEOUT,\n });\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * open an SSE event stream using the fetch API.\n * - calls `onEvent` for each line starting with \"data: \"\n * - calls `onClose` when the stream ends or errors\n * - returns the ReadableStream so callers can `cancel()` it\n */\n async openEventStream(\n onEvent: (json: Record<string, unknown>) => void,\n onClose?: () => void,\n ): Promise<ReadableStream<Uint8Array>> {\n if (typeof fetch === \"undefined\") {\n throw new Error(\"global fetch is not available in Node < 18\");\n }\n\n const urlBase = this.speculosAxiosClient.defaults.baseURL ?? this.baseUrl;\n const url = `${removeTrailingSlashes(urlBase)}/events?stream=true`;\n\n const controller = new AbortController();\n\n const headers: HeadersInit = {\n Accept: \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"X-Ledger-Client-Version\":\n (this.speculosAxiosClient.defaults.headers?.common?.[\n \"X-Ledger-Client-Version\"\n ] as string) ?? \"ldmk-transport-speculos\",\n };\n\n const response = await fetch(url, {\n method: \"GET\",\n headers,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n controller.abort();\n throw new Error(`SSE request failed with status ${response.status}`);\n }\n\n const stream = response.body;\n if (!stream) {\n controller.abort();\n throw new Error(\"SSE response has no body stream.\");\n }\n\n const reader = stream.getReader();\n const decoder = new TextDecoder(\"utf-8\");\n let buffer = \"\";\n let closed = false;\n\n const finalize = () => {\n if (!closed) {\n closed = true;\n try {\n onClose?.();\n } catch {\n // swallow listener errors\n }\n }\n };\n\n const emitParsedEvent = (line: string) => {\n if (line.startsWith(\"data: \")) {\n const payload = line.slice(6);\n try {\n onEvent(JSON.parse(payload));\n } catch {\n onEvent({ data: payload });\n }\n }\n };\n\n (async () => {\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n // normalise line breaks and process complete lines\n const lines = buffer.split(/\\r?\\n/);\n buffer = lines.pop() ?? \"\"; // keep last partial line\n\n for (const line of lines) {\n emitParsedEvent(line);\n // other SSE fields ignored to mirror original behavior\n }\n }\n } catch {\n // network/reader error\n } finally {\n // flush any remaining buffered text as lines\n if (buffer.length) {\n for (const line of buffer.split(/\\r?\\n/)) {\n emitParsedEvent(line);\n }\n buffer = \"\";\n }\n finalize();\n }\n })();\n\n // consumers can cancel with: (await openEventStream(...)).cancel()\n return stream;\n }\n}\n\ntype SpeculosApduDTO = {\n data: string;\n};\n\ntype SpeculosEventsDTO = {\n events: Array<{\n text?: string;\n x?: number;\n y?: number;\n }>;\n};\n"],
5
+ "mappings": "AAAA,OAAOA,MAAmC,QAE1C,OAAOC,MAAa,qBAIpB,MAAMC,EAAU,IAEVC,EAAyBC,GAAgBA,EAAI,QAAQ,OAAQ,EAAE,EAE9D,SAASC,EACdC,EACAC,EACe,CACf,OAAOP,EAAM,OAAO,CAClB,QAASG,EAAsBG,CAAO,EACtC,QAAS,EACT,QAAS,CACP,0BAA2BC,CAC7B,EACA,aAAc,CAAE,oBAAqB,EAAK,CAC5C,CAAC,CACH,CAEO,MAAMC,CAAqD,CAGhE,YACmBF,EACjBC,EAAuB,2BAA2BN,EAAQ,OAAO,GACjE,CAFiB,aAAAK,EAGjB,KAAK,oBAAsBD,EAAmBC,EAASC,CAAY,CACrE,CAPiB,oBASjB,MAAM,SAASE,EAA+B,CAC5C,KAAM,CAAE,KAAAC,CAAK,EAAI,MAAM,KAAK,oBAAoB,KAC9C,QACA,CACE,KAAMD,CACR,CACF,EACA,OAAOC,EAAK,IACd,CAEA,MAAM,mBAAsC,CAC1C,GAAI,CACF,aAAM,KAAK,oBAAoB,QAA2B,CACxD,OAAQ,MACR,IAAK,UACL,QAASR,CACX,CAAC,EACM,EACT,MAAQ,CACN,MAAO,EACT,CACF,CAQA,MAAM,gBACJS,EACAC,EACqC,CACrC,GAAI,OAAO,MAAU,IACnB,MAAM,IAAI,MAAM,4CAA4C,EAG9D,MAAMC,EAAU,KAAK,oBAAoB,SAAS,SAAW,KAAK,QAC5DT,EAAM,GAAGD,EAAsBU,CAAO,CAAC,sBAEvCC,EAAa,IAAI,gBAEjBC,EAAuB,CAC3B,OAAQ,oBACR,gBAAiB,WACjB,0BACG,KAAK,oBAAoB,SAAS,SAAS,SAC1C,yBACF,GAAgB,yBACpB,EAEMC,EAAW,MAAM,MAAMZ,EAAK,CAChC,OAAQ,MACR,QAAAW,EACA,OAAQD,EAAW,MACrB,CAAC,EAED,GAAI,CAACE,EAAS,GACZ,MAAAF,EAAW,MAAM,EACX,IAAI,MAAM,kCAAkCE,EAAS,MAAM,EAAE,EAGrE,MAAMC,EAASD,EAAS,KACxB,GAAI,CAACC,EACH,MAAAH,EAAW,MAAM,EACX,IAAI,MAAM,kCAAkC,EAGpD,MAAMI,EAASD,EAAO,UAAU,EAC1BE,EAAU,IAAI,YAAY,OAAO,EACvC,IAAIC,EAAS,GACTC,EAAS,GAEb,MAAMC,EAAW,IAAM,CACrB,GAAI,CAACD,EAAQ,CACXA,EAAS,GACT,GAAI,CACFT,IAAU,CACZ,MAAQ,CAER,CACF,CACF,EAEMW,EAAmBC,GAAiB,CACxC,GAAIA,EAAK,WAAW,QAAQ,EAAG,CAC7B,MAAMC,EAAUD,EAAK,MAAM,CAAC,EAC5B,GAAI,CACFb,EAAQ,KAAK,MAAMc,CAAO,CAAC,CAC7B,MAAQ,CACNd,EAAQ,CAAE,KAAMc,CAAQ,CAAC,CAC3B,CACF,CACF,EAEA,OAAC,SAAY,CACX,GAAI,CACF,OAAa,CACX,KAAM,CAAE,MAAAC,EAAO,KAAAC,CAAK,EAAI,MAAMT,EAAO,KAAK,EAC1C,GAAIS,EAAM,MACVP,GAAUD,EAAQ,OAAOO,EAAO,CAAE,OAAQ,EAAK,CAAC,EAGhD,MAAME,EAAQR,EAAO,MAAM,OAAO,EAClCA,EAASQ,EAAM,IAAI,GAAK,GAExB,UAAWJ,KAAQI,EACjBL,EAAgBC,CAAI,CAGxB,CACF,MAAQ,CAER,QAAE,CAEA,GAAIJ,EAAO,OAAQ,CACjB,UAAWI,KAAQJ,EAAO,MAAM,OAAO,EACrCG,EAAgBC,CAAI,EAEtBJ,EAAS,EACX,CACAE,EAAS,CACX,CACF,GAAG,EAGIL,CACT,CACF",
6
+ "names": ["axios", "PACKAGE", "TIMEOUT", "removeTrailingSlashes", "url", "axiosClientFactory", "baseUrl", "clientHeader", "HttpSpeculosDatasource", "apdu", "data", "onEvent", "onClose", "urlBase", "controller", "headers", "response", "stream", "reader", "decoder", "buffer", "closed", "finalize", "emitParsedEvent", "line", "payload", "value", "done", "lines"]
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SpeculosTransport.d.ts","sourceRoot":"","sources":["../../../../src/api/SpeculosTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EAEjB,KAAK,YAAY,EACjB,KAAK,QAAQ,EAEb,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,KAAK,QAAQ,EAEb,KAAK,sBAAsB,EAE3B,KAAK,SAAS,EACd,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,KAAK,MAAM,EAAe,MAAM,WAAW,CAAC;AACrD,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AAK7C,eAAO,MAAM,kBAAkB,EAAE,mBACN,CAAC;AAE5B,qBAAa,iBAAkB,YAAW,SAAS;IACjD,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2C;IACtE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,kBAAkB,CAA+B;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;IACjC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAgB7B;gBAGA,oBAAoB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,sBAAsB,EAC7D,OAAO,EAAE,SAAS,EAClB,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,OAAO;IAUjB,WAAW,IAAI,OAAO;IAItB,aAAa,IAAI,mBAAmB;IAIpC,wBAAwB,IAAI,UAAU,CAAC,yBAAyB,EAAE,CAAC;IAInE,gBAAgB,IAAI,UAAU,CAAC,yBAAyB,CAAC;IAKzD,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;IAgDrD,UAAU,CAAC,OAAO,EAAE;QACxB,eAAe,EAAE,wBAAwB,CAAC;KAC3C,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAM7B,QAAQ,CACZ,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,iBAAiB,EAC/B,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAwB1C,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,mBAAmB;CA0B5B;AAED,eAAO,MAAM,wBAAwB,EAAE,CACrC,WAAW,CAAC,EAAE,MAAM,EACpB,KAAK,CAAC,EAAE,OAAO,KACZ,gBAGsE,CAAC"}
1
+ {"version":3,"file":"SpeculosTransport.d.ts","sourceRoot":"","sources":["../../../../src/api/SpeculosTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EAEjB,KAAK,YAAY,EACjB,KAAK,QAAQ,EAEb,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,KAAK,QAAQ,EAEb,KAAK,sBAAsB,EAE3B,KAAK,SAAS,EACd,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,KAAK,MAAM,EAAe,MAAM,WAAW,CAAC;AACrD,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AAK7C,eAAO,MAAM,kBAAkB,EAAE,mBACN,CAAC;AAE5B,qBAAa,iBAAkB,YAAW,SAAS;IACjD,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2C;IACtE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,kBAAkB,CAA+B;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;IACjC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAgB7B;gBAGA,oBAAoB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,sBAAsB,EAC7D,OAAO,EAAE,SAAS,EAClB,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,OAAO;IAOjB,WAAW,IAAI,OAAO;IAItB,aAAa,IAAI,mBAAmB;IAIpC,wBAAwB,IAAI,UAAU,CAAC,yBAAyB,EAAE,CAAC;IAInE,gBAAgB,IAAI,UAAU,CAAC,yBAAyB,CAAC;IAKzD,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;IAgDrD,UAAU,CAAC,OAAO,EAAE;QACxB,eAAe,EAAE,wBAAwB,CAAC;KAC3C,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAM7B,QAAQ,CACZ,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,iBAAiB,EAC/B,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAuB1C,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,mBAAmB;CA0B5B;AAED,eAAO,MAAM,wBAAwB,EAAE,CACrC,WAAW,CAAC,EAAE,MAAM,EACpB,KAAK,CAAC,EAAE,OAAO,KACZ,gBAGsE,CAAC"}
@@ -1,13 +1,10 @@
1
1
  import { type AxiosInstance } from "axios";
2
2
  import { type SpeculosDatasource } from "./SpeculosDatasource";
3
- export declare function makeNoKeepAliveAxios(baseUrl: string, timeoutMs: number, clientHeader: string): AxiosInstance;
4
- export declare function makeKeepAliveAxiosForSSE(baseUrl: string, clientHeader: string): AxiosInstance;
3
+ export declare function axiosClientFactory(baseUrl: string, clientHeader: string): AxiosInstance;
5
4
  export declare class HttpSpeculosDatasource implements SpeculosDatasource {
6
5
  private readonly baseUrl;
7
- private readonly _isE2E;
8
- private readonly apduClient;
9
- private readonly sseClient;
10
- constructor(baseUrl: string, _isE2E?: boolean, clientHeader?: string);
6
+ private readonly speculosAxiosClient;
7
+ constructor(baseUrl: string, clientHeader?: string);
11
8
  postApdu(apdu: string): Promise<string>;
12
9
  isServerAvailable(): Promise<boolean>;
13
10
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"HttpSpeculosDatasource.d.ts","sourceRoot":"","sources":["../../../../../src/internal/datasource/HttpSpeculosDatasource.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAIlD,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAM/D,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,aAAa,CASf;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,aAAa,CASf;AAED,qBAAa,sBAAuB,YAAW,kBAAkB;IAK7D,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IALzB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8B;gBAGrC,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,OAAe,EACxC,YAAY,GAAE,MAAqD;IAQ/D,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IASvC,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC;IAa3C;;;;;OAKG;IACG,eAAe,CACnB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,EAChD,OAAO,CAAC,EAAE,MAAM,IAAI,GACnB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;CAmGvC"}
1
+ {"version":3,"file":"HttpSpeculosDatasource.d.ts","sourceRoot":"","sources":["../../../../../src/internal/datasource/HttpSpeculosDatasource.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAIlD,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAM/D,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,aAAa,CASf;AAED,qBAAa,sBAAuB,YAAW,kBAAkB;IAI7D,OAAO,CAAC,QAAQ,CAAC,OAAO;IAH1B,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgB;gBAGjC,OAAO,EAAE,MAAM,EAChC,YAAY,GAAE,MAAqD;IAK/D,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUvC,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC;IAa3C;;;;;OAKG;IACG,eAAe,CACnB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,EAChD,OAAO,CAAC,EAAE,MAAM,IAAI,GACnB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;CAgGvC"}