@ledgerhq/device-transport-kit-speculos 0.0.0-test-recursive-stack-20251002122259 → 0.0.0-test-attest-npmjs-1-20251120163531
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/package.json +1 -1
- package/lib/cjs/src/api/SpeculosTransport.js +1 -1
- package/lib/cjs/src/api/SpeculosTransport.js.map +3 -3
- package/lib/cjs/src/index.js +1 -1
- package/lib/cjs/src/index.js.map +2 -2
- package/lib/cjs/src/internal/datasource/HttpSpeculosDatasource.js +1 -1
- package/lib/cjs/src/internal/datasource/HttpSpeculosDatasource.js.map +3 -3
- package/lib/cjs/src/internal/datasource/SpeculosDatasource.js +1 -1
- package/lib/cjs/src/internal/datasource/SpeculosDatasource.js.map +1 -1
- package/lib/esm/package.json +1 -1
- package/lib/esm/src/api/SpeculosTransport.js +1 -1
- package/lib/esm/src/api/SpeculosTransport.js.map +3 -3
- package/lib/esm/src/index.js +1 -1
- package/lib/esm/src/index.js.map +2 -2
- package/lib/esm/src/internal/datasource/HttpSpeculosDatasource.js +1 -1
- package/lib/esm/src/internal/datasource/HttpSpeculosDatasource.js.map +3 -3
- package/lib/types/src/api/SpeculosTransport.d.ts +3 -2
- package/lib/types/src/api/SpeculosTransport.d.ts.map +1 -1
- package/lib/types/src/index.d.ts +1 -0
- package/lib/types/src/index.d.ts.map +1 -1
- package/lib/types/src/internal/datasource/HttpSpeculosDatasource.d.ts +12 -2
- package/lib/types/src/internal/datasource/HttpSpeculosDatasource.d.ts.map +1 -1
- package/lib/types/src/internal/datasource/SpeculosDatasource.d.ts +2 -1
- package/lib/types/src/internal/datasource/SpeculosDatasource.d.ts.map +1 -1
- package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
- package/package.json +5 -5
package/lib/cjs/package.json
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var l=Object.defineProperty;var
|
|
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 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 ) {\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.
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,uBAAAE,EAAA,uBAAAC,EAAA,6BAAAC,IAAA,eAAAC,EAAAL,GAAA,IAAAM,EAkBO,2CACPC,EAAyC,qBACzCC,EAAsC,gBAEtCC,EAAuC,uDAGhC,MAAMN,EACX,0BAEK,MAAMD,CAAuC,CAC1C,OACS,WAAkCC,EAClC,oBACT,gBAAmD,KACnD,mBAA4C,KACnC,eAA4C,CAC3D,GAAI,aACJ,YAAa,CACX,GAAI,gBAAc,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,YACEO,EACAC,EACAC,EACA,CACA,KAAK,
|
|
6
|
-
"names": ["SpeculosTransport_exports", "__export", "SpeculosTransport", "speculosIdentifier", "speculosTransportFactory", "__toCommonJS", "import_device_management_kit", "import_purify_ts", "import_rxjs", "import_HttpSpeculosDatasource", "loggerServiceFactory", "_config", "speculosUrl", "params", "hexResponse", "apduResponse", "parser", "appName", "appVersion", "sessionId", "connectedDevice", "apdu", "error", "_params", "_sessionId", "deviceId", "onDisconnect", "hexApdu", "hexString", "byte", "config"]
|
|
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
|
+
"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
|
}
|
package/lib/cjs/src/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var a=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var
|
|
1
|
+
"use strict";var a=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var t=(f,o,p,x)=>{if(o&&typeof o=="object"||typeof o=="function")for(let m of c(o))!d.call(f,m)&&m!==p&&a(f,m,{get:()=>o[m],enumerable:!(x=b(o,m))||x.enumerable});return f},e=(f,o,p)=>(t(f,o,"default"),p&&t(p,o,"default"));var g=f=>t(a({},"__esModule",{value:!0}),f);var r={};module.exports=g(r);e(r,require("./api/SpeculosTransport"),module.exports);e(r,require("./internal/datasource/HttpSpeculosDatasource"),module.exports);0&&(module.exports={...require("./api/SpeculosTransport"),...require("./internal/datasource/HttpSpeculosDatasource")});
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/lib/cjs/src/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export * from \"./api/SpeculosTransport\";\n"],
|
|
5
|
-
"mappings": "iaAAA,IAAAA,EAAA,kBAAAC,EAAAD,GAAAE,EAAAF,EAAc,mCAAd",
|
|
4
|
+
"sourcesContent": ["export * from \"./api/SpeculosTransport\";\nexport * from \"./internal/datasource/HttpSpeculosDatasource\";\n"],
|
|
5
|
+
"mappings": "iaAAA,IAAAA,EAAA,kBAAAC,EAAAD,GAAAE,EAAAF,EAAc,mCAAd,gBACAE,EAAAF,EAAc,wDADd",
|
|
6
6
|
"names": ["src_exports", "__toCommonJS", "__reExport"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
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 from \"axios\";\n\nimport PACKAGE from \"@root/package.json\";\n\nimport { type SpeculosDatasource } from \"./SpeculosDatasource\";\n\nconst TIMEOUT =
|
|
5
|
-
"mappings": "0jBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,4BAAAE,IAAA,eAAAC,
|
|
6
|
-
"names": ["HttpSpeculosDatasource_exports", "__export", "HttpSpeculosDatasource", "__toCommonJS", "import_axios", "import_package", "TIMEOUT", "
|
|
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,2 +1,2 @@
|
|
|
1
|
-
"use strict";var a=Object.defineProperty;var
|
|
1
|
+
"use strict";var a=Object.defineProperty;var s=Object.getOwnPropertyDescriptor;var t=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var l=(o,e,i,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of t(e))!d.call(o,r)&&r!==i&&a(o,r,{get:()=>e[r],enumerable:!(n=s(e,r))||n.enumerable});return o};var p=o=>l(a({},"__esModule",{value:!0}),o);var v={};module.exports=p(v);
|
|
2
2
|
//# sourceMappingURL=SpeculosDatasource.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/internal/datasource/SpeculosDatasource.ts"],
|
|
4
|
-
"sourcesContent": ["export interface SpeculosDatasource {\n
|
|
4
|
+
"sourcesContent": ["export interface SpeculosDatasource {\n postApdu(apdu: string): Promise<string>;\n isServerAvailable(): Promise<boolean>;\n openEventStream(\n onEvent: (json: Record<string, unknown>) => void,\n onClose?: () => void,\n ): Promise<ReadableStream<Uint8Array>>;\n}\n"],
|
|
5
5
|
"mappings": "+WAAA,IAAAA,EAAA,kBAAAC,EAAAD",
|
|
6
6
|
"names": ["SpeculosDatasource_exports", "__toCommonJS"]
|
|
7
7
|
}
|
package/lib/esm/package.json
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ApduParser as v,bufferToHexaString as g,DeviceModelId as D,GeneralDmkError as h,OpeningConnectionError as
|
|
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 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 ) {\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.
|
|
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,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,EACA,CACA,KAAK,
|
|
6
|
-
"names": ["ApduParser", "bufferToHexaString", "DeviceModelId", "GeneralDmkError", "OpeningConnectionError", "Left", "Right", "from", "HttpSpeculosDatasource", "speculosIdentifier", "SpeculosTransport", "loggerServiceFactory", "_config", "speculosUrl", "params", "hexResponse", "apduResponse", "parser", "appName", "appVersion", "sessionId", "connectedDevice", "apdu", "error", "_params", "_sessionId", "deviceId", "onDisconnect", "hexApdu", "hexString", "byte", "speculosTransportFactory", "config"]
|
|
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
|
+
"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
|
}
|
package/lib/esm/src/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export*from"./api/SpeculosTransport";
|
|
1
|
+
export*from"./api/SpeculosTransport";export*from"./internal/datasource/HttpSpeculosDatasource";
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/lib/esm/src/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export * from \"./api/SpeculosTransport\";\n"],
|
|
5
|
-
"mappings": "AAAA,WAAc",
|
|
4
|
+
"sourcesContent": ["export * from \"./api/SpeculosTransport\";\nexport * from \"./internal/datasource/HttpSpeculosDatasource\";\n"],
|
|
5
|
+
"mappings": "AAAA,WAAc,0BACd,WAAc",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
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 from \"axios\";\n\nimport PACKAGE from \"@root/package.json\";\n\nimport { type SpeculosDatasource } from \"./SpeculosDatasource\";\n\nconst TIMEOUT =
|
|
5
|
-
"mappings": "AAAA,OAAOA,
|
|
6
|
-
"names": ["axios", "PACKAGE", "TIMEOUT", "
|
|
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
|
}
|
|
@@ -8,8 +8,9 @@ export declare class SpeculosTransport implements Transport {
|
|
|
8
8
|
private readonly _speculosDataSource;
|
|
9
9
|
private connectedDevice;
|
|
10
10
|
private disconnectInterval;
|
|
11
|
+
private readonly _isE2E;
|
|
11
12
|
private readonly speculosDevice;
|
|
12
|
-
constructor(loggerServiceFactory: (tag: string) => LoggerPublisherService, _config: DmkConfig, speculosUrl: string);
|
|
13
|
+
constructor(loggerServiceFactory: (tag: string) => LoggerPublisherService, _config: DmkConfig, speculosUrl: string, isE2E?: boolean);
|
|
13
14
|
isSupported(): boolean;
|
|
14
15
|
getIdentifier(): TransportIdentifier;
|
|
15
16
|
listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]>;
|
|
@@ -27,5 +28,5 @@ export declare class SpeculosTransport implements Transport {
|
|
|
27
28
|
private fromHexString;
|
|
28
29
|
private listenForDisconnect;
|
|
29
30
|
}
|
|
30
|
-
export declare const speculosTransportFactory: (speculosUrl?: string) => TransportFactory;
|
|
31
|
+
export declare const speculosTransportFactory: (speculosUrl?: string, isE2E?: boolean) => TransportFactory;
|
|
31
32
|
//# sourceMappingURL=SpeculosTransport.d.ts.map
|
|
@@ -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,cAAc,CAgB7B;gBAGA,oBAAoB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,sBAAsB,EAC7D,OAAO,EAAE,SAAS,EAClB,WAAW,EAAE,MAAM;
|
|
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"}
|
package/lib/types/src/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,8CAA8C,CAAC"}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
+
import { type AxiosInstance } from "axios";
|
|
1
2
|
import { type SpeculosDatasource } from "./SpeculosDatasource";
|
|
3
|
+
export declare function axiosClientFactory(baseUrl: string, clientHeader: string): AxiosInstance;
|
|
2
4
|
export declare class HttpSpeculosDatasource implements SpeculosDatasource {
|
|
3
5
|
private readonly baseUrl;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
private readonly speculosAxiosClient;
|
|
7
|
+
constructor(baseUrl: string, clientHeader?: string);
|
|
8
|
+
postApdu(apdu: string): Promise<string>;
|
|
6
9
|
isServerAvailable(): Promise<boolean>;
|
|
10
|
+
/**
|
|
11
|
+
* open an SSE event stream using the fetch API.
|
|
12
|
+
* - calls `onEvent` for each line starting with "data: "
|
|
13
|
+
* - calls `onClose` when the stream ends or errors
|
|
14
|
+
* - returns the ReadableStream so callers can `cancel()` it
|
|
15
|
+
*/
|
|
16
|
+
openEventStream(onEvent: (json: Record<string, unknown>) => void, onClose?: () => void): Promise<ReadableStream<Uint8Array>>;
|
|
7
17
|
}
|
|
8
18
|
//# sourceMappingURL=HttpSpeculosDatasource.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HttpSpeculosDatasource.d.ts","sourceRoot":"","sources":["../../../../../src/internal/datasource/HttpSpeculosDatasource.ts"],"names":[],"mappings":"
|
|
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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export interface SpeculosDatasource {
|
|
2
|
-
|
|
2
|
+
postApdu(apdu: string): Promise<string>;
|
|
3
3
|
isServerAvailable(): Promise<boolean>;
|
|
4
|
+
openEventStream(onEvent: (json: Record<string, unknown>) => void, onClose?: () => void): Promise<ReadableStream<Uint8Array>>;
|
|
4
5
|
}
|
|
5
6
|
//# sourceMappingURL=SpeculosDatasource.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpeculosDatasource.d.ts","sourceRoot":"","sources":["../../../../../src/internal/datasource/SpeculosDatasource.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"SpeculosDatasource.d.ts","sourceRoot":"","sources":["../../../../../src/internal/datasource/SpeculosDatasource.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,eAAe,CACb,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,CAAC;CACxC"}
|