@ledgerhq/device-transport-kit-web-ble 0.0.0-legacy-speculos-datasource-20250821095840 → 0.0.0-multisig-20250822145545

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.
Files changed (28) hide show
  1. package/lib/esm/api/data/WebBleConfig.js +1 -1
  2. package/lib/esm/api/data/WebBleConfig.js.map +3 -3
  3. package/lib/esm/api/transport/BleDeviceConnection.js +2 -0
  4. package/lib/esm/api/transport/BleDeviceConnection.js.map +7 -0
  5. package/lib/esm/api/transport/BleDeviceConnection.test.js +2 -0
  6. package/lib/esm/api/transport/BleDeviceConnection.test.js.map +7 -0
  7. package/lib/esm/api/transport/WebBleTransport.js +1 -1
  8. package/lib/esm/api/transport/WebBleTransport.js.map +3 -3
  9. package/lib/esm/api/transport/WebBleTransport.test.js +1 -1
  10. package/lib/esm/api/transport/WebBleTransport.test.js.map +3 -3
  11. package/lib/types/api/data/WebBleConfig.d.ts +1 -3
  12. package/lib/types/api/data/WebBleConfig.d.ts.map +1 -1
  13. package/lib/types/api/transport/BleDeviceConnection.d.ts +98 -0
  14. package/lib/types/api/transport/BleDeviceConnection.d.ts.map +1 -0
  15. package/lib/types/api/transport/BleDeviceConnection.test.d.ts +2 -0
  16. package/lib/types/api/transport/BleDeviceConnection.test.d.ts.map +1 -0
  17. package/lib/types/api/transport/WebBleTransport.d.ts +76 -21
  18. package/lib/types/api/transport/WebBleTransport.d.ts.map +1 -1
  19. package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
  20. package/package.json +7 -7
  21. package/lib/esm/api/transport/WebBleApduSender.js +0 -2
  22. package/lib/esm/api/transport/WebBleApduSender.js.map +0 -7
  23. package/lib/esm/api/transport/WebBleApduSender.test.js +0 -2
  24. package/lib/esm/api/transport/WebBleApduSender.test.js.map +0 -7
  25. package/lib/types/api/transport/WebBleApduSender.d.ts +0 -34
  26. package/lib/types/api/transport/WebBleApduSender.d.ts.map +0 -1
  27. package/lib/types/api/transport/WebBleApduSender.test.d.ts +0 -2
  28. package/lib/types/api/transport/WebBleApduSender.test.d.ts.map +0 -1
@@ -1,2 +1,2 @@
1
- const E=15e3,N=2e3,T=5;export{T as RECONNECTION_RETRY_COUNT,E as RECONNECT_DEVICE_TIMEOUT,N as SINGLE_RECONNECTION_TIMEOUT};
1
+ const E=6e3;export{E as RECONNECT_DEVICE_TIMEOUT};
2
2
  //# sourceMappingURL=WebBleConfig.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/api/data/WebBleConfig.ts"],
4
- "sourcesContent": ["export const RECONNECT_DEVICE_TIMEOUT = 15000;\nexport const SINGLE_RECONNECTION_TIMEOUT = 2000;\nexport const RECONNECTION_RETRY_COUNT = 5;\n"],
5
- "mappings": "AAAO,MAAMA,EAA2B,KAC3BC,EAA8B,IAC9BC,EAA2B",
6
- "names": ["RECONNECT_DEVICE_TIMEOUT", "SINGLE_RECONNECTION_TIMEOUT", "RECONNECTION_RETRY_COUNT"]
4
+ "sourcesContent": ["export const RECONNECT_DEVICE_TIMEOUT = 6000;\n"],
5
+ "mappings": "AAAO,MAAMA,EAA2B",
6
+ "names": ["RECONNECT_DEVICE_TIMEOUT"]
7
7
  }
@@ -0,0 +1,2 @@
1
+ import{CommandUtils as u,DeviceNotInitializedError as p,ReconnectionFailedError as d}from"@ledgerhq/device-management-kit";import{Left as n,Maybe as o,Nothing as v,Right as h}from"purify-ts";class y{_writeCharacteristic;_notifyCharacteristic;_logger;_apduSender;_apduSenderFactory;_apduReceiver;_isDeviceReady;_sendApduPromiseResolver;_settleReconnectionPromiseResolvers;constructor({writeCharacteristic:e,notifyCharacteristic:t,apduSenderFactory:i,apduReceiverFactory:s},a){this._apduSenderFactory=i,this._apduSender=v,this._apduReceiver=s(),this._logger=a("BleDeviceConnection"),this._writeCharacteristic=e,this._notifyCharacteristic=t,this._notifyCharacteristic.oncharacteristicvaluechanged=this.onNotifyCharacteristicValueChanged,this._isDeviceReady=!1,this._sendApduPromiseResolver=o.zero(),this._settleReconnectionPromiseResolvers=o.zero()}set notifyCharacteristic(e){this._notifyCharacteristic=e,this._notifyCharacteristic.oncharacteristicvaluechanged=this.onNotifyCharacteristicValueChanged}set writeCharacteristic(e){this._writeCharacteristic=e}onReceiveSetupApduResponse(e){const t=new Uint8Array(e),[i]=t.slice(5);i&&(this._apduSender=o.of(this._apduSenderFactory({frameSize:i})),this._settleReconnectionPromiseResolvers.ifJust(s=>{s.resolve(),this._settleReconnectionPromiseResolvers=o.zero()}),this._isDeviceReady=!0)}onNotifyCharacteristicValueChanged=e=>{if(!this.isDataViewEvent(e))return;const{target:{value:{buffer:t}}}=e;t instanceof ArrayBuffer&&(this._isDeviceReady?this.receiveApdu(t):this.onReceiveSetupApduResponse(t))};async setup(){const e=Uint8Array.from([8,0,0,0,0]);await this._notifyCharacteristic.startNotifications(),await this._writeCharacteristic.writeValueWithoutResponse(e)}receiveApdu(e){this._apduReceiver.handleFrame(new Uint8Array(e)).map(i=>{i.map(s=>{this._logger.debug("Received APDU Response",{data:{response:s}}),this._sendApduPromiseResolver.map(({resolve:a})=>a(h(s)))})}).mapLeft(i=>{this._sendApduPromiseResolver.map(({resolve:s})=>s(n(i)))})}async sendApdu(e,t){if(!this._isDeviceReady)return Promise.resolve(n(new p("Unknown MTU")));const i=new Promise(r=>{this._sendApduPromiseResolver=o.of({resolve:r})}),s=this._apduSender.mapOrDefault(r=>r.getFrames(e),[]);for(const r of s)try{const c=r.getRawData();this._logger.debug("Sending Frame",{data:{frame:c}}),await this._writeCharacteristic.writeValueWithoutResponse(new Uint8Array(c))}catch(c){this._logger.error("Error sending frame",{data:{error:c}})}const a=await i;return this._sendApduPromiseResolver=o.zero(),a.caseOf({Right:async r=>t&&u.isSuccessResponse(r)?(await this.setupWaitForReconnection()).map(()=>r):h(r),Left:async r=>Promise.resolve(n(r))})}isDataViewEvent(e){return e.target!==null&&"value"in e.target&&e.target.value instanceof DataView}setupWaitForReconnection(){return new Promise(e=>{this._settleReconnectionPromiseResolvers=o.of({resolve:()=>e(h(void 0)),reject:t=>e(n(t))})})}async reconnect(e,t){this._isDeviceReady=!1,this.notifyCharacteristic=t,this.writeCharacteristic=e,await this.setup()}disconnect(){this._settleReconnectionPromiseResolvers.ifJust(e=>{e.reject(new d),this._settleReconnectionPromiseResolvers=o.zero()}),this._isDeviceReady=!1}}export{y as BleDeviceConnection};
2
+ //# sourceMappingURL=BleDeviceConnection.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/api/transport/BleDeviceConnection.ts"],
4
+ "sourcesContent": ["import {\n type ApduReceiverService,\n type ApduReceiverServiceFactory,\n type ApduResponse,\n type ApduSenderService,\n type ApduSenderServiceFactory,\n CommandUtils,\n type DeviceConnection,\n DeviceNotInitializedError,\n type DmkError,\n type LoggerPublisherService,\n ReconnectionFailedError,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Either, Left, Maybe, Nothing, Right } from \"purify-ts\";\n\ntype BleDeviceConnectionConstructorArgs = {\n writeCharacteristic: BluetoothRemoteGATTCharacteristic;\n notifyCharacteristic: BluetoothRemoteGATTCharacteristic;\n apduSenderFactory: ApduSenderServiceFactory;\n apduReceiverFactory: ApduReceiverServiceFactory;\n};\n\nexport type DataViewEvent = Event & {\n target: {\n value: DataView;\n };\n};\n\nexport class BleDeviceConnection implements DeviceConnection {\n private _writeCharacteristic: BluetoothRemoteGATTCharacteristic;\n private _notifyCharacteristic: BluetoothRemoteGATTCharacteristic;\n private readonly _logger: LoggerPublisherService;\n private _apduSender: Maybe<ApduSenderService>;\n private readonly _apduSenderFactory: ApduSenderServiceFactory;\n private readonly _apduReceiver: ApduReceiverService;\n private _isDeviceReady: boolean;\n private _sendApduPromiseResolver: Maybe<{\n resolve(value: Either<DmkError, ApduResponse>): void;\n }>;\n private _settleReconnectionPromiseResolvers: Maybe<{\n resolve(): void;\n reject(err: DmkError): void;\n }>;\n\n constructor(\n {\n writeCharacteristic,\n notifyCharacteristic,\n apduSenderFactory,\n apduReceiverFactory,\n }: BleDeviceConnectionConstructorArgs,\n loggerServiceFactory: (tag: string) => LoggerPublisherService,\n ) {\n this._apduSenderFactory = apduSenderFactory;\n this._apduSender = Nothing;\n this._apduReceiver = apduReceiverFactory();\n this._logger = loggerServiceFactory(\"BleDeviceConnection\");\n this._writeCharacteristic = writeCharacteristic;\n this._notifyCharacteristic = notifyCharacteristic;\n this._notifyCharacteristic.oncharacteristicvaluechanged =\n this.onNotifyCharacteristicValueChanged;\n this._isDeviceReady = false;\n this._sendApduPromiseResolver = Maybe.zero();\n this._settleReconnectionPromiseResolvers = Maybe.zero();\n }\n\n /**\n * NotifyCharacteristic setter\n * Register a listener on characteristic value change\n * @param notifyCharacteristic\n * @private\n */\n private set notifyCharacteristic(\n notifyCharacteristic: BluetoothRemoteGATTCharacteristic,\n ) {\n this._notifyCharacteristic = notifyCharacteristic;\n this._notifyCharacteristic.oncharacteristicvaluechanged =\n this.onNotifyCharacteristicValueChanged;\n }\n\n /**\n * WriteCharacteristic setter\n * @param writeCharacteristic\n * @private\n */\n private set writeCharacteristic(\n writeCharacteristic: BluetoothRemoteGATTCharacteristic,\n ) {\n this._writeCharacteristic = writeCharacteristic;\n }\n\n /**\n * Event handler to setup the mtu size in response of 0x0800000000 APDU\n * @param value\n * @private\n */\n private onReceiveSetupApduResponse(value: ArrayBuffer) {\n const mtuResponse = new Uint8Array(value);\n // the mtu is the 5th byte of the response\n const [frameSize] = mtuResponse.slice(5);\n if (frameSize) {\n this._apduSender = Maybe.of(this._apduSenderFactory({ frameSize }));\n this._settleReconnectionPromiseResolvers.ifJust((promise) => {\n promise.resolve();\n this._settleReconnectionPromiseResolvers = Maybe.zero();\n });\n this._isDeviceReady = true;\n }\n }\n\n /**\n * Main event handler for BLE notify characteristic\n * Call _onReceiveSetupApduResponse if device mtu is not set\n * Call receiveApdu otherwise\n * @param event\n */\n private onNotifyCharacteristicValueChanged = (event: Event) => {\n if (!this.isDataViewEvent(event)) {\n return;\n }\n const {\n target: {\n value: { buffer },\n },\n } = event;\n if (buffer instanceof ArrayBuffer) {\n if (!this._isDeviceReady) {\n this.onReceiveSetupApduResponse(buffer);\n } else {\n this.receiveApdu(buffer);\n }\n }\n };\n\n /**\n * Setup BleDeviceConnection\n *\n * The device is considered as ready once the mtu had been set\n * APDU 0x0800000000 is used to get this mtu size\n */\n public async setup() {\n const requestMtuApdu = Uint8Array.from([0x08, 0x00, 0x00, 0x00, 0x00]);\n\n await this._notifyCharacteristic.startNotifications();\n await this._writeCharacteristic.writeValueWithoutResponse(requestMtuApdu);\n }\n\n /**\n * Receive APDU response\n * Resolve sendApdu promise once the framer receives all the frames of the response\n * @param data\n */\n receiveApdu(data: ArrayBuffer) {\n const response = this._apduReceiver.handleFrame(new Uint8Array(data));\n\n response\n .map((maybeApduResponse) => {\n maybeApduResponse.map((apduResponse) => {\n this._logger.debug(\"Received APDU Response\", {\n data: { response: apduResponse },\n });\n this._sendApduPromiseResolver.map(({ resolve }) =>\n resolve(Right(apduResponse)),\n );\n });\n })\n .mapLeft((error) => {\n this._sendApduPromiseResolver.map(({ resolve }) =>\n resolve(Left(error)),\n );\n });\n }\n\n /**\n * Send apdu if the mtu had been set\n *\n * Get all frames for a given APDU\n * Save a promise that would be completed once the response had been received\n * @param apdu\n * @param triggersDisconnection\n */\n async sendApdu(\n apdu: Uint8Array,\n triggersDisconnection?: boolean,\n ): Promise<Either<DmkError, ApduResponse>> {\n if (!this._isDeviceReady) {\n return Promise.resolve(\n Left(new DeviceNotInitializedError(\"Unknown MTU\")),\n );\n }\n // Create a promise that would be resolved once the response had been received\n const resultPromise = new Promise<Either<DmkError, ApduResponse>>(\n (resolve) => {\n this._sendApduPromiseResolver = Maybe.of({\n resolve,\n });\n },\n );\n const frames = this._apduSender.mapOrDefault(\n (apduSender) => apduSender.getFrames(apdu),\n [],\n );\n for (const frame of frames) {\n try {\n const frameData = frame.getRawData();\n this._logger.debug(\"Sending Frame\", {\n data: { frame: frameData },\n });\n await this._writeCharacteristic.writeValueWithoutResponse(\n new Uint8Array(frameData),\n );\n } catch (error) {\n this._logger.error(\"Error sending frame\", { data: { error } });\n }\n }\n const response = await resultPromise;\n this._sendApduPromiseResolver = Maybe.zero();\n return response.caseOf({\n Right: async (apduResponse) => {\n if (\n triggersDisconnection &&\n CommandUtils.isSuccessResponse(apduResponse)\n ) {\n const reconnectionRes = await this.setupWaitForReconnection();\n return reconnectionRes.map(() => apduResponse);\n } else {\n return Right(apduResponse);\n }\n },\n Left: async (error) => Promise.resolve(Left(error)),\n });\n }\n\n /**\n * Typeguard to check if an event contains target value of type DataView\n *\n * @param event\n * @private\n */\n private isDataViewEvent(event: Event): event is DataViewEvent {\n return (\n event.target !== null &&\n \"value\" in event.target &&\n event.target.value instanceof DataView\n );\n }\n\n /**\n * Setup a promise that would be resolved once the device is reconnected\n *\n * @private\n */\n private setupWaitForReconnection(): Promise<Either<DmkError, void>> {\n return new Promise<Either<DmkError, void>>((resolve) => {\n this._settleReconnectionPromiseResolvers = Maybe.of({\n resolve: () => resolve(Right(undefined)),\n reject: (error: DmkError) => resolve(Left(error)),\n });\n });\n }\n\n /**\n * Reconnect to the device by resetting new ble characteristics\n * @param writeCharacteristic\n * @param notifyCharacteristic\n */\n public async reconnect(\n writeCharacteristic: BluetoothRemoteGATTCharacteristic,\n notifyCharacteristic: BluetoothRemoteGATTCharacteristic,\n ) {\n this._isDeviceReady = false;\n this.notifyCharacteristic = notifyCharacteristic;\n this.writeCharacteristic = writeCharacteristic;\n await this.setup();\n }\n\n /**\n * Disconnect from the device\n */\n public disconnect() {\n // if a reconnection promise is pending, reject it\n this._settleReconnectionPromiseResolvers.ifJust((promise) => {\n promise.reject(new ReconnectionFailedError());\n this._settleReconnectionPromiseResolvers = Maybe.zero();\n });\n this._isDeviceReady = false;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["CommandUtils", "DeviceNotInitializedError", "ReconnectionFailedError", "Left", "Maybe", "Nothing", "Right", "BleDeviceConnection", "writeCharacteristic", "notifyCharacteristic", "apduSenderFactory", "apduReceiverFactory", "loggerServiceFactory", "value", "mtuResponse", "frameSize", "promise", "event", "buffer", "requestMtuApdu", "data", "maybeApduResponse", "apduResponse", "resolve", "error", "apdu", "triggersDisconnection", "resultPromise", "frames", "apduSender", "frame", "frameData", "response"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{ApduResponse as p,defaultApduReceiverServiceStubBuilder as l,defaultApduSenderServiceStubBuilder as v,DeviceNotInitializedError as S}from"@ledgerhq/device-management-kit";import{Left as f,Right as h}from"purify-ts";import{bleCharacteristicStubBuilder as u}from"../model/BleDevice.stub";import{BleDeviceConnection as s}from"./BleDeviceConnection";const x=new Uint8Array([8,0,0,0,0]),d=new Uint8Array([0,0,0,0,0,66]),A=Uint8Array.from([0,0,0,0,0,0,0,0]);class w{subscribers=[];tag;constructor(i,r){this.subscribers=i,this.tag=r}error=vi.fn();warn=vi.fn();info=vi.fn();debug=vi.fn()}describe("BleDeviceConnection",()=>{let t,i,r,o;const n=e=>new w([],e);beforeEach(()=>{t=u(),i=u(),r=vi.fn(()=>v(void 0,n)),o=vi.fn(()=>l(void 0,n))});function a(e,c=Uint8Array.from([])){e.onNotifyCharacteristicValueChanged({target:{value:new DataView(c.buffer)}})}describe("sendApdu",()=>{it("should return an error if the device isn't setup",async()=>{const c=await new s({writeCharacteristic:t,notifyCharacteristic:i,apduSenderFactory:r,apduReceiverFactory:o},n).sendApdu(Uint8Array.from([66]));expect(c).toStrictEqual(f(new S("Unknown MTU")))}),it("should send apdu without error if device is setup",async()=>{const e=new s({writeCharacteristic:t,notifyCharacteristic:i,apduSenderFactory:r,apduReceiverFactory:o},n);a(e,d);const c=e.sendApdu(new Uint8Array([66]));a(e,A),expect(t.writeValueWithoutResponse).toHaveBeenCalledTimes(1),expect(await c).toStrictEqual(h(new p({statusCode:Uint8Array.from([]),data:Uint8Array.from([])})))})}),describe("setup",()=>{it("should send the apdu 0x0800000000 to get mtu size",async()=>{await new s({writeCharacteristic:t,notifyCharacteristic:i,apduSenderFactory:r,apduReceiverFactory:o},n).setup(),expect(t.writeValueWithoutResponse).toHaveBeenCalledWith(new Uint8Array(x))}),it("should setup apduSender with the correct mtu size",()=>{const e=new s({writeCharacteristic:t,notifyCharacteristic:i,apduSenderFactory:r,apduReceiverFactory:o},n);a(e,d),expect(r).toHaveBeenCalledWith({frameSize:66})})})});
2
+ //# sourceMappingURL=BleDeviceConnection.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/api/transport/BleDeviceConnection.test.ts"],
4
+ "sourcesContent": ["import {\n type ApduReceiverService,\n ApduResponse,\n type ApduSenderService,\n defaultApduReceiverServiceStubBuilder,\n defaultApduSenderServiceStubBuilder,\n DeviceNotInitializedError,\n type LoggerPublisherService,\n type LoggerSubscriberService,\n} from \"@ledgerhq/device-management-kit\";\nimport { Left, Right } from \"purify-ts\";\n\nimport { bleCharacteristicStubBuilder } from \"@api/model/BleDevice.stub\";\n\nimport { BleDeviceConnection, type DataViewEvent } from \"./BleDeviceConnection\";\n\nconst GET_MTU_APDU = new Uint8Array([0x08, 0x00, 0x00, 0x00, 0x00]);\nconst GET_MTU_APDU_RESPONSE = new Uint8Array([\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x42,\n]);\nconst EMPTY_APDU_RESPONSE = Uint8Array.from([\n 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n]);\n\nclass LoggerPublisherServiceStub implements LoggerPublisherService {\n subscribers: LoggerSubscriberService[] = [];\n tag: string;\n constructor(subscribers: LoggerSubscriberService[], tag: string) {\n this.subscribers = subscribers;\n this.tag = tag;\n }\n error = vi.fn();\n warn = vi.fn();\n info = vi.fn();\n debug = vi.fn();\n}\n\ndescribe(\"BleDeviceConnection\", () => {\n let writeCharacteristic: BluetoothRemoteGATTCharacteristic;\n let notifyCharacteristic: BluetoothRemoteGATTCharacteristic;\n let apduSenderFactory: () => ApduSenderService;\n let apduReceiverFactory: () => ApduReceiverService;\n const logger = (tag: string) => new LoggerPublisherServiceStub([], tag);\n\n beforeEach(() => {\n writeCharacteristic = bleCharacteristicStubBuilder();\n notifyCharacteristic = bleCharacteristicStubBuilder();\n apduSenderFactory = vi.fn(() =>\n defaultApduSenderServiceStubBuilder(undefined, logger),\n );\n apduReceiverFactory = vi.fn(() =>\n defaultApduReceiverServiceStubBuilder(undefined, logger),\n );\n });\n\n function receiveApdu(\n connection: BleDeviceConnection,\n buffer: Uint8Array = Uint8Array.from([]),\n ) {\n // @ts-expect-error private function call to mock web ble response\n connection.onNotifyCharacteristicValueChanged({\n target: {\n value: new DataView(buffer.buffer),\n },\n } as DataViewEvent);\n }\n\n describe(\"sendApdu\", () => {\n it(\"should return an error if the device isn't setup\", async () => {\n // given\n const connection = new BleDeviceConnection(\n {\n writeCharacteristic,\n notifyCharacteristic,\n apduSenderFactory,\n apduReceiverFactory,\n },\n logger,\n );\n // when\n const errorOrApduResponse = await connection.sendApdu(\n Uint8Array.from([0x42]),\n );\n // then\n expect(errorOrApduResponse).toStrictEqual(\n Left(new DeviceNotInitializedError(\"Unknown MTU\")),\n );\n });\n\n it(\"should send apdu without error if device is setup\", async () => {\n // given\n const connection = new BleDeviceConnection(\n {\n writeCharacteristic,\n notifyCharacteristic,\n apduSenderFactory,\n apduReceiverFactory,\n },\n logger,\n );\n // when\n receiveApdu(connection, GET_MTU_APDU_RESPONSE);\n const response = connection.sendApdu(new Uint8Array([0x42]));\n receiveApdu(connection, EMPTY_APDU_RESPONSE);\n // then\n expect(\n writeCharacteristic.writeValueWithoutResponse,\n ).toHaveBeenCalledTimes(1);\n expect(await response).toStrictEqual(\n Right(\n new ApduResponse({\n statusCode: Uint8Array.from([]),\n data: Uint8Array.from([]),\n }),\n ),\n );\n });\n });\n describe(\"setup\", () => {\n it(\"should send the apdu 0x0800000000 to get mtu size\", async () => {\n // given\n const connection = new BleDeviceConnection(\n {\n writeCharacteristic,\n notifyCharacteristic,\n apduSenderFactory,\n apduReceiverFactory,\n },\n logger,\n );\n // when\n await connection.setup();\n // then\n expect(\n writeCharacteristic.writeValueWithoutResponse,\n ).toHaveBeenCalledWith(new Uint8Array(GET_MTU_APDU));\n });\n it(\"should setup apduSender with the correct mtu size\", () => {\n // given\n const connection = new BleDeviceConnection(\n {\n writeCharacteristic,\n notifyCharacteristic,\n apduSenderFactory,\n apduReceiverFactory,\n },\n logger,\n );\n // when\n receiveApdu(connection, GET_MTU_APDU_RESPONSE);\n // then\n expect(apduSenderFactory).toHaveBeenCalledWith({ frameSize: 0x42 });\n });\n });\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["ApduResponse", "defaultApduReceiverServiceStubBuilder", "defaultApduSenderServiceStubBuilder", "DeviceNotInitializedError", "Left", "Right", "bleCharacteristicStubBuilder", "BleDeviceConnection", "GET_MTU_APDU", "GET_MTU_APDU_RESPONSE", "EMPTY_APDU_RESPONSE", "LoggerPublisherServiceStub", "subscribers", "tag", "writeCharacteristic", "notifyCharacteristic", "apduSenderFactory", "apduReceiverFactory", "logger", "receiveApdu", "connection", "buffer", "errorOrApduResponse", "response"]
7
+ }
@@ -1,2 +1,2 @@
1
- import{DeviceAlreadyConnectedError as C,DeviceDisconnectedWhileSendingError as M,OpeningConnectionError as h,TransportConnectedDevice as E,UnknownDeviceError as l}from"@ledgerhq/device-management-kit";import{DeviceConnectionStateMachine as R}from"@ledgerhq/device-management-kit";import{Left as v,Right as u}from"purify-ts";import{defer as A,from as p,Subject as P,throwError as B}from"rxjs";import{concatMap as T,retryWhen as F,scan as U,tap as L,timeout as O}from"rxjs/operators";import{switchMap as W}from"rxjs/operators";import{RECONNECT_DEVICE_TIMEOUT as S,RECONNECTION_RETRY_COUNT as N,SINGLE_RECONNECTION_TIMEOUT as w}from"../data/WebBleConfig";import{WebBleApduSender as $}from"./WebBleApduSender";const _="WEB-BLE";class x{constructor(e,t,i,n){this.loggerFactory=t;this._deviceModelDataSource=e,this._apduSenderFactory=i,this._apduReceiverFactory=n,this._logger=t("WebBleTransport")}_logger;_deviceModelDataSource;_apduSenderFactory;_apduReceiverFactory;_deviceRegistry=new Map;_connectionMachines=new Map;_discoveredDevicesSubject=new P;_reconnectionPromises=new Map;_reconnectionTimers=new Map;isSupported(){return typeof navigator<"u"&&!!navigator.bluetooth}getIdentifier(){return _}startDiscovering(){const e=this._deviceModelDataSource.getBluetoothServices().map(t=>({services:[t]}));return p(navigator.bluetooth.requestDevice({filters:e})).pipe(W(async t=>{if(!t.gatt)throw new h("No GATT server available on device");const i=await t.gatt.connect(),n=this._deviceModelDataSource.getBluetoothServices();let r=null;for(const o of n)try{r=await i.getPrimaryService(o);break}catch{this._logger.warn(`Service ${o} not found on device ${t.name}`)}if(!r)throw new h("Ledger GATT service not found");const a=this._deviceModelDataSource.getBluetoothServicesInfos()[r.uuid];if(!a)throw new l(t.name||"");const d=t.id,s={id:d,deviceModel:a.deviceModel,transport:_};return this._deviceRegistry.set(d,{device:t,service:r,infos:a,discovered:s}),s}))}listenToAvailableDevices(){return this.startDiscovering().subscribe({next:e=>this._discoveredDevicesSubject.next([e]),error:e=>this._logger.error("Scan error",{data:{e}})}),this._discoveredDevicesSubject.asObservable()}stopDiscovering(){}async connect(e){const t=this._deviceRegistry.get(e.deviceId);if(this._connectionMachines.has(e.deviceId))return v(new C("Device already connected"));if(!t)return v(new l(`Unknown device ${e.deviceId}`));try{const{device:i,service:n,infos:r,discovered:a}=t,d=new $({writeCharacteristic:await n.getCharacteristic(r.writeCmdUuid),notifyCharacteristic:await n.getCharacteristic(r.notifyUuid),apduSenderFactory:this._apduSenderFactory,apduReceiverFactory:this._apduReceiverFactory},this.loggerFactory),s=this.handleDisconnect(e.deviceId,d),o=new R({deviceId:e.deviceId,deviceApduSender:d,timeoutDuration:S,tryToReconnect:()=>{s()},onTerminated:()=>{try{e.onDisconnect(e.deviceId),this._connectionMachines.delete(e.deviceId);const c=this._reconnectionTimers.get(e.deviceId);c&&(clearTimeout(c),this._reconnectionTimers.delete(e.deviceId))}catch(c){this._logger.error("Error during onTerminated cleanup",{data:{error:c}})}}});return await o.setupConnection(),t.onDisconnect=()=>e.onDisconnect(e.deviceId),t.onReconnect=e.onReconnect&&(()=>e.onReconnect(e.deviceId)),this._connectionMachines.set(e.deviceId,o),t.listener=s,i.addEventListener("gattserverdisconnected",t.listener),u(new E({id:e.deviceId,deviceModel:a.deviceModel,type:"BLE",transport:_,sendApdu:async(c,g,b)=>{const f=this._reconnectionPromises.get(e.deviceId);if(f)try{await f.promise}catch{this._logger.error("Reconnection failed")}const m=this._connectionMachines.get(e.deviceId);return m?(await m.sendApdu(c,g,b)).chainLeft(D=>D instanceof M&&g?u({statusCode:new Uint8Array([144,0]),data:new Uint8Array(0)}):v(D)):v(new l(`Unknown device ${e.deviceId}`))}}))}catch(i){return this._deviceRegistry.delete(e.deviceId),this._logger.error("Connection error",{data:{error:i}}),v(new h(i))}}handleDisconnect(e,t){return()=>{const i=this._connectionMachines.get(e),n=this._deviceRegistry.get(e);if(!i||!n)return;if(this._reconnectionPromises.has(e)){this._logger.debug("reconnection already in flight, ignoring");return}const r={};r.promise=new Promise(o=>r.resolve=o),this._reconnectionPromises.set(e,r),i.eventDeviceDisconnected(),this._reconnectionTimers.has(e)&&clearTimeout(this._reconnectionTimers.get(e));const a=setTimeout(()=>{n.onDisconnect?.(),this._cleanupConnection(e)},S);this._reconnectionTimers.set(e,a);const s=A(()=>(this._logger.debug("attempt gatt.connect()"),n.device.gatt?.connected&&n.device.gatt.disconnect(),p(n.device.gatt.connect()))).pipe(O({first:w,with:()=>B(()=>new h(`connect timed out after ${w} ms`))}),T(async()=>{try{return await n.device.gatt.getPrimaryService(n.service.uuid)}catch{for(const o of this._deviceModelDataSource.getBluetoothServices())try{return await n.device.gatt.getPrimaryService(o)}catch{this._logger.warn(`Service ${o} not found on device ${n.device.name}`)}throw new h("Ledger GATT service not found on reconnect")}}),T(o=>p(this._rebindCharacteristics(e,o,t,i))),F(o=>o.pipe(L(c=>this._logger.warn(`retrying #${e} (${c instanceof Error?c.message:c})`)),U(c=>{if(c+1>=N)throw new Error("max retries reached");return c+1},0)))).subscribe({next:async()=>{this._logger.debug(`reconnect SUCCESS for ${e}`);const o=this._reconnectionTimers.get(e);o&&(clearTimeout(o),this._reconnectionTimers.delete(e)),this._connectionMachines.get(e)?.eventDeviceConnected();try{n?.onReconnect&&await n.onReconnect()}catch(g){this._logger.error("onReconnect callback threw",{data:{error:g}})}this._reconnectionPromises.get(e)?.resolve(),this._reconnectionPromises.delete(e),s.unsubscribe()},error:()=>{const o=this._reconnectionTimers.get(e);o&&(clearTimeout(o),this._reconnectionTimers.delete(e)),this._cleanupDevice(e),s.unsubscribe()}})}}_cleanupDevice(e){try{const t=this._connectionMachines.get(e),i=this._deviceRegistry.get(e),n=this._reconnectionPromises.get(e);if(t)try{t.eventDeviceDisconnected(),t.closeConnection()}catch(r){this._logger.error("Error closing state machine",{data:{error:r}})}i&&(i.listener&&i.device.removeEventListener("gattserverdisconnected",i.listener),i.device.gatt?.connected&&i.device.gatt.disconnect()),this._connectionMachines.delete(e),this._deviceRegistry.delete(e),n&&(n.resolve(),this._reconnectionPromises.delete(e)),this._reconnectionTimers.has(e)&&(clearTimeout(this._reconnectionTimers.get(e)),this._reconnectionTimers.delete(e))}catch(t){this._logger.error("Unexpected error during device cleanup",{data:{error:t}})}}_cleanupConnection(e){const t=this._connectionMachines.get(e);if(t){try{t.closeConnection()}catch(n){this._logger.error("Error closing state machine",{data:{error:n}})}this._connectionMachines.delete(e)}const i=this._reconnectionPromises.get(e);i&&(i.resolve(),this._reconnectionPromises.delete(e)),this._reconnectionTimers.has(e)&&(clearTimeout(this._reconnectionTimers.get(e)),this._reconnectionTimers.delete(e))}async _rebindCharacteristics(e,t,i,n){const r=this._deviceRegistry.get(e);r.listener&&r.device.removeEventListener("gattserverdisconnected",r.listener),r.listener=this.handleDisconnect(e,i),r.device.addEventListener("gattserverdisconnected",r.listener),r.service=t,await i.setDependencies({writeCharacteristic:await t.getCharacteristic(r.infos.writeCmdUuid),notifyCharacteristic:await t.getCharacteristic(r.infos.notifyUuid)}),await n.setupConnection()}async disconnect(e){const t=e.connectedDevice.id;if(!this._connectionMachines.get(t))return v(new l(`Unknown device ${t}`));const n=this._deviceRegistry.get(t);if(n&&n.onDisconnect)try{n.onDisconnect()}catch(r){this._logger.error("Error in onDisconnect callback",{data:{error:r}})}return this._cleanupDevice(t),Promise.resolve(u(void 0))}}const J=({deviceModelDataSource:y,loggerServiceFactory:e,apduSenderServiceFactory:t,apduReceiverServiceFactory:i})=>new x(y,e,t,i);export{x as WebBleTransport,_ as webBleIdentifier,J as webBleTransportFactory};
1
+ import{DeviceAlreadyConnectedError as B,DeviceNotRecognizedError as b,NoAccessibleDeviceError as m,OpeningConnectionError as p,TransportConnectedDevice as I,UnknownDeviceError as h}from"@ledgerhq/device-management-kit";import{EitherAsync as u,Left as c,Maybe as d,Right as a}from"purify-ts";import{from as g,switchMap as _,timer as w}from"rxjs";import{v4 as T}from"uuid";import{RECONNECT_DEVICE_TIMEOUT as S}from"../data/WebBleConfig";import{BleDeviceGattServerError as v,BleTransportNotSupportedError as E}from"../model/Errors";import{BleDeviceConnection as C}from"../transport/BleDeviceConnection";const A="WEB-BLE";class F{constructor(e,i,t,r){this._deviceModelDataSource=e;this._loggerServiceFactory=i;this._apduSenderFactory=t;this._apduReceiverFactory=r;this._connectedDevices=[],this._internalDevicesById=new Map,this._deviceConnectionById=new Map,this._disconnectionHandlersById=new Map,this._logger=i("WebBleTransport")}_connectedDevices;_internalDevicesById;_deviceConnectionById;_disconnectionHandlersById;_logger;connectionType="BLE";identifier=A;getBluetoothApi(){return this.isSupported()?a(navigator.bluetooth):c(new E("WebBle not supported"))}isSupported(){try{return!!navigator?.bluetooth}catch{return!1}}getIdentifier(){return this.identifier}listenToAvailableDevices(){return g([])}async getBleGattService(e){if(!e.gatt)return c(new v("Device gatt not found"));try{const[i]=await e.gatt.getPrimaryServices();return i?a(i):c(new v("bluetooth service not found"))}catch(i){return c(new v(i))}}getBleDeviceInfos(e){const t=this._deviceModelDataSource.getBluetoothServicesInfos()[e.uuid];return t?a(t):(this._logger.error(`Device not recognized: ${e.device.name}`),c(new b(`Device not recognized: ${e.device.name}`)))}promptDeviceAccess(){return u(async({liftEither:e,throwE:i})=>{const t=await e(this.getBluetoothApi());let r;try{r=await t.requestDevice({filters:this._deviceModelDataSource.getBluetoothServices().map(n=>({services:[n]}))})}catch(n){return i(new m(n))}return r})}getDiscoveredDeviceFrom(e){return{id:T(),deviceModel:e.deviceModel,transport:this.identifier}}setInternalDeviceFrom(e,i,t,r){const n={id:e.id,bleDevice:i,bleGattService:r,bleDeviceInfos:t,discoveredDevice:e};this._logger.debug(`Discovered device ${n.id} ${e.deviceModel.productName}`),this._internalDevicesById.set(n.id,n)}startDiscovering(){return this._logger.debug("startDiscovering"),g(this.promptDeviceAccess()).pipe(_(async e=>u(async({liftEither:i,fromPromise:t})=>{const r=await i(e);if(r.gatt)try{await r.gatt.connect()}catch(n){throw new p(n)}try{const n=await t(this.getBleGattService(r)),o=await i(this.getBleDeviceInfos(n)),s=this.getDiscoveredDeviceFrom(o);return this.setInternalDeviceFrom(s,r,o,n),s}catch(n){throw this._logger.error("Error while discovering device",{data:{error:n,bleDevice:r}}),r.forget&&await r.forget(),n}}).caseOf({Right:i=>i,Left:i=>{throw this._logger.error("Error while getting accessible device",{data:{error:i}}),i}})))}stopDiscovering(){this._logger.debug("stopDiscovering")}async connect({deviceId:e,onDisconnect:i}){const t=this._internalDevicesById.get(e);if(!t)return this._logger.error(`Unknown device ${e}`,{data:{internalDevices:this._internalDevicesById}}),this._logger.debug("Available devices",{data:{devices:this._internalDevicesById}}),c(new h(`Unknown device ${e}`));if(this._connectedDevices.includes(t.bleDevice))return this._internalDevicesById.delete(e),c(new B("Device already connected"));const{discoveredDevice:{deviceModel:r}}=t;try{const[n,o]=await Promise.all([t.bleGattService.getCharacteristic(t.bleDeviceInfos.writeCmdUuid),t.bleGattService.getCharacteristic(t.bleDeviceInfos.notifyUuid)]),s=new C({writeCharacteristic:n,notifyCharacteristic:o,apduReceiverFactory:this._apduReceiverFactory,apduSenderFactory:this._apduSenderFactory},this._loggerServiceFactory);await s.setup();const D=new I({sendApdu:(y,f)=>s.sendApdu(y,f),deviceModel:r,id:e,type:this.connectionType,transport:this.identifier});return t.bleDevice.ongattserverdisconnected=this._getDeviceDisconnectedHandler(t,s),this._deviceConnectionById.set(t.id,s),this._disconnectionHandlersById.set(t.id,()=>{this.disconnect({connectedDevice:D}).then(()=>i(e))}),this._connectedDevices.push(t.bleDevice),a(D)}catch(n){return t.bleDevice.forget&&await t.bleDevice.forget(),this._internalDevicesById.delete(e),this._logger.error("Error while getting characteristics",{data:{error:n}}),c(new p(n))}}_getDeviceDisconnectedHandler(e,i){return async()=>{const t=w(S).subscribe(()=>{this._logger.debug("disconnection timer over"),d.fromNullable(this._disconnectionHandlersById.get(e.id)).map(o=>o())});await e.bleDevice.gatt?.connect(),t.unsubscribe();const r=await this.getBleGattService(e.bleDevice);if(r.isRight()){const[n,o]=await Promise.all([r.extract().getCharacteristic(e.bleDeviceInfos.writeCmdUuid),r.extract().getCharacteristic(e.bleDeviceInfos.notifyUuid)]);await i.reconnect(n,o)}}}async disconnect(e){const i=d.fromNullable(this._internalDevicesById.get(e.connectedDevice.id));return this._logger.debug("disconnect device",{data:{connectedDevice:e.connectedDevice}}),i.isNothing()?(this._logger.error(`Unknown device ${e.connectedDevice.id}`),Promise.resolve(c(new h(`Unknown device ${e.connectedDevice.id}`)))):(i.map(t=>{const{bleDevice:r}=t;d.fromNullable(this._deviceConnectionById.get(t.id)).map(o=>o.disconnect()),r.gatt?.connected&&r.gatt.disconnect(),this._internalDevicesById.delete(t.id),this._deviceConnectionById.delete(t.id),this._disconnectionHandlersById.delete(t.id),this._connectedDevices.includes(r)&&delete this._connectedDevices[this._connectedDevices.indexOf(r)]}),Promise.resolve(a(void 0)))}}const O=({deviceModelDataSource:l,loggerServiceFactory:e,apduSenderServiceFactory:i,apduReceiverServiceFactory:t})=>new F(l,e,i,t);export{F as WebBleTransport,A as webBleIdentifier,O as webBleTransportFactory};
2
2
  //# sourceMappingURL=WebBleTransport.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/api/transport/WebBleTransport.ts"],
4
- "sourcesContent": ["import {\n type ApduReceiverServiceFactory,\n type ApduSenderServiceFactory,\n type ConnectError,\n DeviceAlreadyConnectedError,\n DeviceDisconnectedWhileSendingError,\n type DeviceModelDataSource,\n type LoggerPublisherService,\n OpeningConnectionError,\n type Transport,\n TransportConnectedDevice,\n type TransportDeviceModel,\n type TransportDiscoveredDevice,\n type TransportFactory,\n type TransportIdentifier,\n UnknownDeviceError,\n} from \"@ledgerhq/device-management-kit\";\nimport { DeviceConnectionStateMachine } from \"@ledgerhq/device-management-kit\";\nimport { type Either, Left, Right } from \"purify-ts\";\nimport { defer, from, type Observable, Subject, throwError } from \"rxjs\";\nimport { concatMap, retryWhen, scan, tap, timeout } from \"rxjs/operators\";\nimport { switchMap } from \"rxjs/operators\";\n\nimport {\n RECONNECT_DEVICE_TIMEOUT,\n RECONNECTION_RETRY_COUNT,\n SINGLE_RECONNECTION_TIMEOUT,\n} from \"@api/data/WebBleConfig\";\n\nimport {\n WebBleApduSender,\n type WebBleApduSenderDependencies,\n} from \"./WebBleApduSender\";\n\nexport const webBleIdentifier: TransportIdentifier = \"WEB-BLE\";\n\nexport class WebBleTransport implements Transport {\n private _logger: LoggerPublisherService;\n private _deviceModelDataSource: DeviceModelDataSource;\n private _apduSenderFactory: ApduSenderServiceFactory;\n private _apduReceiverFactory: ApduReceiverServiceFactory;\n\n private _deviceRegistry = new Map<\n string,\n {\n device: BluetoothDevice;\n service: BluetoothRemoteGATTService;\n infos: {\n writeCmdUuid: string;\n notifyUuid: string;\n deviceModel: TransportDeviceModel;\n };\n discovered: TransportDiscoveredDevice;\n onDisconnect?: () => void;\n onReconnect?: () => Promise<void> | void;\n listener?: (ev: Event) => void;\n }\n >();\n\n private _connectionMachines = new Map<\n string,\n DeviceConnectionStateMachine<WebBleApduSenderDependencies>\n >();\n private _discoveredDevicesSubject = new Subject<\n TransportDiscoveredDevice[]\n >();\n private _reconnectionPromises = new Map<\n string,\n { promise: Promise<void>; resolve: () => void }\n >();\n private _reconnectionTimers = new Map<\n string,\n ReturnType<typeof setTimeout>\n >();\n\n constructor(\n deviceModelDataSource: DeviceModelDataSource,\n private loggerFactory: (tag: string) => LoggerPublisherService,\n apduSenderFactory: ApduSenderServiceFactory,\n apduReceiverFactory: ApduReceiverServiceFactory,\n ) {\n this._deviceModelDataSource = deviceModelDataSource;\n this._apduSenderFactory = apduSenderFactory;\n this._apduReceiverFactory = apduReceiverFactory;\n this._logger = loggerFactory(\"WebBleTransport\");\n }\n\n isSupported(): boolean {\n return typeof navigator !== \"undefined\" && !!navigator.bluetooth;\n }\n\n getIdentifier(): TransportIdentifier {\n return webBleIdentifier;\n }\n\n startDiscovering(): Observable<TransportDiscoveredDevice> {\n const filters = this._deviceModelDataSource\n .getBluetoothServices()\n .map((s) => ({ services: [s] }));\n\n return from(navigator.bluetooth.requestDevice({ filters })).pipe(\n switchMap(async (device) => {\n if (!device.gatt)\n throw new OpeningConnectionError(\n \"No GATT server available on device\",\n );\n\n const server = await device.gatt.connect();\n const knownUuids = this._deviceModelDataSource.getBluetoothServices();\n let service: BluetoothRemoteGATTService | null = null;\n\n for (const uuid of knownUuids) {\n try {\n service = await server.getPrimaryService(uuid);\n break;\n } catch {\n this._logger.warn(\n `Service ${uuid} not found on device ${device.name}`,\n );\n }\n }\n if (!service)\n throw new OpeningConnectionError(\"Ledger GATT service not found\");\n\n const infos =\n this._deviceModelDataSource.getBluetoothServicesInfos()[service.uuid];\n if (!infos) throw new UnknownDeviceError(device.name || \"\");\n\n const id = device.id;\n const discovered: TransportDiscoveredDevice = {\n id,\n deviceModel: infos.deviceModel,\n transport: webBleIdentifier,\n };\n\n this._deviceRegistry.set(id, { device, service, infos, discovered });\n return discovered;\n }),\n );\n }\n\n listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]> {\n this.startDiscovering().subscribe({\n next: (d) => this._discoveredDevicesSubject.next([d]),\n error: (e) => this._logger.error(\"Scan error\", { data: { e } }),\n });\n return this._discoveredDevicesSubject.asObservable();\n }\n\n stopDiscovering(): void {\n // browser prompt cannot be cancelled\n }\n\n async connect(params: {\n deviceId: string;\n onDisconnect: (deviceId: string) => void;\n onReconnect?: (deviceId: string) => Promise<void> | void;\n }): Promise<Either<ConnectError, TransportConnectedDevice>> {\n const deviceEntry = this._deviceRegistry.get(params.deviceId);\n\n if (this._connectionMachines.has(params.deviceId)) {\n return Left(new DeviceAlreadyConnectedError(\"Device already connected\"));\n }\n if (!deviceEntry) {\n return Left(new UnknownDeviceError(`Unknown device ${params.deviceId}`));\n }\n\n try {\n const { device, service, infos, discovered } = deviceEntry;\n\n const apduSender = new WebBleApduSender(\n {\n writeCharacteristic: await service.getCharacteristic(\n infos.writeCmdUuid,\n ),\n notifyCharacteristic: await service.getCharacteristic(\n infos.notifyUuid,\n ),\n apduSenderFactory: this._apduSenderFactory,\n apduReceiverFactory: this._apduReceiverFactory,\n },\n this.loggerFactory,\n );\n\n const disconnectHandler = this.handleDisconnect(\n params.deviceId,\n apduSender,\n );\n\n const connectionMachine =\n new DeviceConnectionStateMachine<WebBleApduSenderDependencies>({\n deviceId: params.deviceId,\n deviceApduSender: apduSender,\n timeoutDuration: RECONNECT_DEVICE_TIMEOUT,\n tryToReconnect: () => {\n disconnectHandler();\n },\n onTerminated: () => {\n try {\n params.onDisconnect(params.deviceId);\n this._connectionMachines.delete(params.deviceId);\n const t = this._reconnectionTimers.get(params.deviceId);\n if (t) {\n clearTimeout(t);\n this._reconnectionTimers.delete(params.deviceId);\n }\n } catch (e) {\n this._logger.error(\"Error during onTerminated cleanup\", {\n data: { error: e },\n });\n }\n },\n });\n\n await connectionMachine.setupConnection();\n\n deviceEntry.onDisconnect = () => params.onDisconnect(params.deviceId);\n deviceEntry.onReconnect =\n params.onReconnect && (() => params.onReconnect!(params.deviceId));\n this._connectionMachines.set(params.deviceId, connectionMachine);\n\n deviceEntry.listener = disconnectHandler;\n device.addEventListener(\"gattserverdisconnected\", deviceEntry.listener);\n\n return Right(\n new TransportConnectedDevice({\n id: params.deviceId,\n deviceModel: discovered.deviceModel,\n type: \"BLE\",\n transport: webBleIdentifier,\n sendApdu: async (apdu, triggersDisconnection, abortTimeout) => {\n const pending = this._reconnectionPromises.get(params.deviceId);\n if (pending) {\n try {\n await pending.promise;\n } catch {\n this._logger.error(\"Reconnection failed\");\n }\n }\n\n const machine = this._connectionMachines.get(params.deviceId);\n if (!machine) {\n return Left(\n new UnknownDeviceError(`Unknown device ${params.deviceId}`),\n );\n }\n\n const res = await machine.sendApdu(\n apdu,\n triggersDisconnection,\n abortTimeout,\n );\n return res.chainLeft((err) => {\n if (\n err instanceof DeviceDisconnectedWhileSendingError &&\n triggersDisconnection\n ) {\n return Right({\n statusCode: new Uint8Array([0x90, 0x00]),\n data: new Uint8Array(0),\n });\n }\n return Left(err);\n });\n },\n }),\n );\n } catch (e) {\n this._deviceRegistry.delete(params.deviceId);\n this._logger.error(\"Connection error\", { data: { error: e } });\n return Left(new OpeningConnectionError(e));\n }\n }\n\n private handleDisconnect(\n deviceId: string,\n apduSender: WebBleApduSender,\n ): (ev?: Event) => void {\n return () => {\n const machine = this._connectionMachines.get(deviceId);\n const entry = this._deviceRegistry.get(deviceId);\n if (!machine || !entry) return;\n if (this._reconnectionPromises.has(deviceId)) {\n this._logger.debug(\"reconnection already in flight, ignoring\");\n return;\n }\n\n const pending = {} as { promise: Promise<void>; resolve: () => void };\n pending.promise = new Promise<void>((res) => (pending.resolve = res));\n this._reconnectionPromises.set(deviceId, pending);\n\n machine.eventDeviceDisconnected();\n\n if (this._reconnectionTimers.has(deviceId))\n clearTimeout(this._reconnectionTimers.get(deviceId)!);\n\n const disconnectTimer = setTimeout(() => {\n entry.onDisconnect?.();\n this._cleanupConnection(deviceId);\n }, RECONNECT_DEVICE_TIMEOUT);\n this._reconnectionTimers.set(deviceId, disconnectTimer);\n\n const reconnectionSubject = defer(() => {\n this._logger.debug(\"attempt gatt.connect()\");\n if (entry.device.gatt?.connected) entry.device.gatt.disconnect();\n return from(entry.device.gatt!.connect());\n }).pipe(\n timeout({\n first: SINGLE_RECONNECTION_TIMEOUT,\n with: () =>\n throwError(\n () =>\n new OpeningConnectionError(\n `connect timed out after ${SINGLE_RECONNECTION_TIMEOUT} ms`,\n ),\n ),\n }),\n concatMap(async () => {\n try {\n return await entry.device.gatt!.getPrimaryService(\n entry.service.uuid,\n );\n } catch {\n for (const uuid of this._deviceModelDataSource.getBluetoothServices()) {\n try {\n return await entry.device.gatt!.getPrimaryService(uuid);\n } catch {\n this._logger.warn(\n `Service ${uuid} not found on device ${entry.device.name}`,\n );\n }\n }\n throw new OpeningConnectionError(\n \"Ledger GATT service not found on reconnect\",\n );\n }\n }),\n concatMap((svc) =>\n from(this._rebindCharacteristics(deviceId, svc, apduSender, machine)),\n ),\n retryWhen((err$) =>\n err$.pipe(\n tap((err) =>\n this._logger.warn(\n `retrying #${deviceId} (${\n err instanceof Error ? err.message : err\n })`,\n ),\n ),\n scan((count) => {\n if (count + 1 >= RECONNECTION_RETRY_COUNT) {\n throw new Error(\"max retries reached\");\n }\n return count + 1;\n }, 0),\n ),\n ),\n );\n\n const reconnectionSub = reconnectionSubject.subscribe({\n next: async () => {\n this._logger.debug(`reconnect SUCCESS for ${deviceId}`);\n\n const t = this._reconnectionTimers.get(deviceId);\n if (t) {\n clearTimeout(t);\n this._reconnectionTimers.delete(deviceId);\n }\n\n this._connectionMachines.get(deviceId)?.eventDeviceConnected();\n\n try {\n if (entry?.onReconnect) {\n await entry.onReconnect(); // wait for SCP reset\n }\n } catch (e) {\n this._logger.error(\"onReconnect callback threw\", {\n data: { error: e },\n });\n }\n\n // Unblock pending sends *after* SCP reset\n const pendingSenders = this._reconnectionPromises.get(deviceId);\n pendingSenders?.resolve();\n this._reconnectionPromises.delete(deviceId);\n\n reconnectionSub.unsubscribe();\n },\n error: () => {\n const t = this._reconnectionTimers.get(deviceId);\n if (t) {\n clearTimeout(t);\n this._reconnectionTimers.delete(deviceId);\n }\n this._cleanupDevice(deviceId);\n reconnectionSub.unsubscribe();\n },\n });\n };\n }\n\n private _cleanupDevice(deviceId: string): void {\n try {\n const machine = this._connectionMachines.get(deviceId);\n const entry = this._deviceRegistry.get(deviceId);\n const pending = this._reconnectionPromises.get(deviceId);\n\n if (machine) {\n try {\n machine.eventDeviceDisconnected();\n machine.closeConnection();\n } catch (e) {\n this._logger.error(\"Error closing state machine\", {\n data: { error: e },\n });\n }\n }\n\n if (entry) {\n if (entry.listener) {\n entry.device.removeEventListener(\n \"gattserverdisconnected\",\n entry.listener,\n );\n }\n if (entry.device.gatt?.connected) {\n entry.device.gatt.disconnect();\n }\n }\n\n this._connectionMachines.delete(deviceId);\n this._deviceRegistry.delete(deviceId);\n\n if (pending) {\n pending.resolve();\n this._reconnectionPromises.delete(deviceId);\n }\n\n if (this._reconnectionTimers.has(deviceId)) {\n clearTimeout(this._reconnectionTimers.get(deviceId)!);\n this._reconnectionTimers.delete(deviceId);\n }\n } catch (e) {\n this._logger.error(\"Unexpected error during device cleanup\", {\n data: { error: e },\n });\n }\n }\n\n private _cleanupConnection(deviceId: string): void {\n const machine = this._connectionMachines.get(deviceId);\n if (machine) {\n try {\n machine.closeConnection();\n } catch (e) {\n this._logger.error(\"Error closing state machine\", {\n data: { error: e },\n });\n }\n this._connectionMachines.delete(deviceId);\n }\n\n const pending = this._reconnectionPromises.get(deviceId);\n if (pending) {\n pending.resolve();\n this._reconnectionPromises.delete(deviceId);\n }\n\n if (this._reconnectionTimers.has(deviceId)) {\n clearTimeout(this._reconnectionTimers.get(deviceId)!);\n this._reconnectionTimers.delete(deviceId);\n }\n }\n\n private async _rebindCharacteristics(\n deviceId: string,\n freshSvc: BluetoothRemoteGATTService,\n apduSender: WebBleApduSender,\n connectionMachine: DeviceConnectionStateMachine<WebBleApduSenderDependencies>,\n ) {\n const deviceEntry = this._deviceRegistry.get(deviceId)!;\n\n if (deviceEntry.listener)\n deviceEntry.device.removeEventListener(\n \"gattserverdisconnected\",\n deviceEntry.listener,\n );\n\n deviceEntry.listener = this.handleDisconnect(deviceId, apduSender);\n deviceEntry.device.addEventListener(\n \"gattserverdisconnected\",\n deviceEntry.listener,\n );\n\n deviceEntry.service = freshSvc;\n\n await apduSender.setDependencies({\n writeCharacteristic: await freshSvc.getCharacteristic(\n deviceEntry.infos.writeCmdUuid,\n ),\n notifyCharacteristic: await freshSvc.getCharacteristic(\n deviceEntry.infos.notifyUuid,\n ),\n });\n\n await connectionMachine.setupConnection();\n }\n\n public async disconnect(params: {\n connectedDevice: TransportConnectedDevice;\n }) {\n const id = params.connectedDevice.id;\n const connectionMachine = this._connectionMachines.get(id);\n if (!connectionMachine)\n return Left(new UnknownDeviceError(`Unknown device ${id}`));\n\n const entry = this._deviceRegistry.get(id);\n\n if (entry && entry.onDisconnect) {\n try {\n entry.onDisconnect();\n } catch (e) {\n this._logger.error(\"Error in onDisconnect callback\", {\n data: { error: e },\n });\n }\n }\n\n this._cleanupDevice(id);\n return Promise.resolve(Right(undefined));\n }\n}\n\nexport const webBleTransportFactory: TransportFactory = ({\n deviceModelDataSource,\n loggerServiceFactory,\n apduSenderServiceFactory,\n apduReceiverServiceFactory,\n}) =>\n new WebBleTransport(\n deviceModelDataSource,\n loggerServiceFactory,\n apduSenderServiceFactory,\n apduReceiverServiceFactory,\n );\n"],
5
- "mappings": "AAAA,OAIE,+BAAAA,EACA,uCAAAC,EAGA,0BAAAC,EAEA,4BAAAC,EAKA,sBAAAC,MACK,kCACP,OAAS,gCAAAC,MAAoC,kCAC7C,OAAsB,QAAAC,EAAM,SAAAC,MAAa,YACzC,OAAS,SAAAC,EAAO,QAAAC,EAAuB,WAAAC,EAAS,cAAAC,MAAkB,OAClE,OAAS,aAAAC,EAAW,aAAAC,EAAW,QAAAC,EAAM,OAAAC,EAAK,WAAAC,MAAe,iBACzD,OAAS,aAAAC,MAAiB,iBAE1B,OACE,4BAAAC,EACA,4BAAAC,EACA,+BAAAC,MACK,yBAEP,OACE,oBAAAC,MAEK,qBAEA,MAAMC,EAAwC,UAE9C,MAAMC,CAAqC,CAuChD,YACEC,EACQC,EACRC,EACAC,EACA,CAHQ,mBAAAF,EAIR,KAAK,uBAAyBD,EAC9B,KAAK,mBAAqBE,EAC1B,KAAK,qBAAuBC,EAC5B,KAAK,QAAUF,EAAc,iBAAiB,CAChD,CAhDQ,QACA,uBACA,mBACA,qBAEA,gBAAkB,IAAI,IAiBtB,oBAAsB,IAAI,IAI1B,0BAA4B,IAAIf,EAGhC,sBAAwB,IAAI,IAI5B,oBAAsB,IAAI,IAiBlC,aAAuB,CACrB,OAAO,OAAO,UAAc,KAAe,CAAC,CAAC,UAAU,SACzD,CAEA,eAAqC,CACnC,OAAOY,CACT,CAEA,kBAA0D,CACxD,MAAMM,EAAU,KAAK,uBAClB,qBAAqB,EACrB,IAAKC,IAAO,CAAE,SAAU,CAACA,CAAC,CAAE,EAAE,EAEjC,OAAOpB,EAAK,UAAU,UAAU,cAAc,CAAE,QAAAmB,CAAQ,CAAC,CAAC,EAAE,KAC1DX,EAAU,MAAOa,GAAW,CAC1B,GAAI,CAACA,EAAO,KACV,MAAM,IAAI5B,EACR,oCACF,EAEF,MAAM6B,EAAS,MAAMD,EAAO,KAAK,QAAQ,EACnCE,EAAa,KAAK,uBAAuB,qBAAqB,EACpE,IAAIC,EAA6C,KAEjD,UAAWC,KAAQF,EACjB,GAAI,CACFC,EAAU,MAAMF,EAAO,kBAAkBG,CAAI,EAC7C,KACF,MAAQ,CACN,KAAK,QAAQ,KACX,WAAWA,CAAI,wBAAwBJ,EAAO,IAAI,EACpD,CACF,CAEF,GAAI,CAACG,EACH,MAAM,IAAI/B,EAAuB,+BAA+B,EAElE,MAAMiC,EACJ,KAAK,uBAAuB,0BAA0B,EAAEF,EAAQ,IAAI,EACtE,GAAI,CAACE,EAAO,MAAM,IAAI/B,EAAmB0B,EAAO,MAAQ,EAAE,EAE1D,MAAMM,EAAKN,EAAO,GACZO,EAAwC,CAC5C,GAAAD,EACA,YAAaD,EAAM,YACnB,UAAWb,CACb,EAEA,YAAK,gBAAgB,IAAIc,EAAI,CAAE,OAAAN,EAAQ,QAAAG,EAAS,MAAAE,EAAO,WAAAE,CAAW,CAAC,EAC5DA,CACT,CAAC,CACH,CACF,CAEA,0BAAoE,CAClE,YAAK,iBAAiB,EAAE,UAAU,CAChC,KAAOC,GAAM,KAAK,0BAA0B,KAAK,CAACA,CAAC,CAAC,EACpD,MAAQ,GAAM,KAAK,QAAQ,MAAM,aAAc,CAAE,KAAM,CAAE,CAAE,CAAE,CAAC,CAChE,CAAC,EACM,KAAK,0BAA0B,aAAa,CACrD,CAEA,iBAAwB,CAExB,CAEA,MAAM,QAAQC,EAI8C,CAC1D,MAAMC,EAAc,KAAK,gBAAgB,IAAID,EAAO,QAAQ,EAE5D,GAAI,KAAK,oBAAoB,IAAIA,EAAO,QAAQ,EAC9C,OAAOjC,EAAK,IAAIN,EAA4B,0BAA0B,CAAC,EAEzE,GAAI,CAACwC,EACH,OAAOlC,EAAK,IAAIF,EAAmB,kBAAkBmC,EAAO,QAAQ,EAAE,CAAC,EAGzE,GAAI,CACF,KAAM,CAAE,OAAAT,EAAQ,QAAAG,EAAS,MAAAE,EAAO,WAAAE,CAAW,EAAIG,EAEzCC,EAAa,IAAIpB,EACrB,CACE,oBAAqB,MAAMY,EAAQ,kBACjCE,EAAM,YACR,EACA,qBAAsB,MAAMF,EAAQ,kBAClCE,EAAM,UACR,EACA,kBAAmB,KAAK,mBACxB,oBAAqB,KAAK,oBAC5B,EACA,KAAK,aACP,EAEMO,EAAoB,KAAK,iBAC7BH,EAAO,SACPE,CACF,EAEME,EACJ,IAAItC,EAA2D,CAC7D,SAAUkC,EAAO,SACjB,iBAAkBE,EAClB,gBAAiBvB,EACjB,eAAgB,IAAM,CACpBwB,EAAkB,CACpB,EACA,aAAc,IAAM,CAClB,GAAI,CACFH,EAAO,aAAaA,EAAO,QAAQ,EACnC,KAAK,oBAAoB,OAAOA,EAAO,QAAQ,EAC/C,MAAMK,EAAI,KAAK,oBAAoB,IAAIL,EAAO,QAAQ,EAClDK,IACF,aAAaA,CAAC,EACd,KAAK,oBAAoB,OAAOL,EAAO,QAAQ,EAEnD,OAASM,EAAG,CACV,KAAK,QAAQ,MAAM,oCAAqC,CACtD,KAAM,CAAE,MAAOA,CAAE,CACnB,CAAC,CACH,CACF,CACF,CAAC,EAEH,aAAMF,EAAkB,gBAAgB,EAExCH,EAAY,aAAe,IAAMD,EAAO,aAAaA,EAAO,QAAQ,EACpEC,EAAY,YACVD,EAAO,cAAgB,IAAMA,EAAO,YAAaA,EAAO,QAAQ,GAClE,KAAK,oBAAoB,IAAIA,EAAO,SAAUI,CAAiB,EAE/DH,EAAY,SAAWE,EACvBZ,EAAO,iBAAiB,yBAA0BU,EAAY,QAAQ,EAE/DjC,EACL,IAAIJ,EAAyB,CAC3B,GAAIoC,EAAO,SACX,YAAaF,EAAW,YACxB,KAAM,MACN,UAAWf,EACX,SAAU,MAAOwB,EAAMC,EAAuBC,IAAiB,CAC7D,MAAMC,EAAU,KAAK,sBAAsB,IAAIV,EAAO,QAAQ,EAC9D,GAAIU,EACF,GAAI,CACF,MAAMA,EAAQ,OAChB,MAAQ,CACN,KAAK,QAAQ,MAAM,qBAAqB,CAC1C,CAGF,MAAMC,EAAU,KAAK,oBAAoB,IAAIX,EAAO,QAAQ,EAC5D,OAAKW,GAMO,MAAMA,EAAQ,SACxBJ,EACAC,EACAC,CACF,GACW,UAAWG,GAElBA,aAAelD,GACf8C,EAEOxC,EAAM,CACX,WAAY,IAAI,WAAW,CAAC,IAAM,CAAI,CAAC,EACvC,KAAM,IAAI,WAAW,CAAC,CACxB,CAAC,EAEID,EAAK6C,CAAG,CAChB,EArBQ7C,EACL,IAAIF,EAAmB,kBAAkBmC,EAAO,QAAQ,EAAE,CAC5D,CAoBJ,CACF,CAAC,CACH,CACF,OAASM,EAAG,CACV,YAAK,gBAAgB,OAAON,EAAO,QAAQ,EAC3C,KAAK,QAAQ,MAAM,mBAAoB,CAAE,KAAM,CAAE,MAAOM,CAAE,CAAE,CAAC,EACtDvC,EAAK,IAAIJ,EAAuB2C,CAAC,CAAC,CAC3C,CACF,CAEQ,iBACNO,EACAX,EACsB,CACtB,MAAO,IAAM,CACX,MAAMS,EAAU,KAAK,oBAAoB,IAAIE,CAAQ,EAC/CC,EAAQ,KAAK,gBAAgB,IAAID,CAAQ,EAC/C,GAAI,CAACF,GAAW,CAACG,EAAO,OACxB,GAAI,KAAK,sBAAsB,IAAID,CAAQ,EAAG,CAC5C,KAAK,QAAQ,MAAM,0CAA0C,EAC7D,MACF,CAEA,MAAMH,EAAU,CAAC,EACjBA,EAAQ,QAAU,IAAI,QAAeK,GAASL,EAAQ,QAAUK,CAAI,EACpE,KAAK,sBAAsB,IAAIF,EAAUH,CAAO,EAEhDC,EAAQ,wBAAwB,EAE5B,KAAK,oBAAoB,IAAIE,CAAQ,GACvC,aAAa,KAAK,oBAAoB,IAAIA,CAAQ,CAAE,EAEtD,MAAMG,EAAkB,WAAW,IAAM,CACvCF,EAAM,eAAe,EACrB,KAAK,mBAAmBD,CAAQ,CAClC,EAAGlC,CAAwB,EAC3B,KAAK,oBAAoB,IAAIkC,EAAUG,CAAe,EA2DtD,MAAMC,EAzDsBhD,EAAM,KAChC,KAAK,QAAQ,MAAM,wBAAwB,EACvC6C,EAAM,OAAO,MAAM,WAAWA,EAAM,OAAO,KAAK,WAAW,EACxD5C,EAAK4C,EAAM,OAAO,KAAM,QAAQ,CAAC,EACzC,EAAE,KACDrC,EAAQ,CACN,MAAOI,EACP,KAAM,IACJT,EACE,IACE,IAAIT,EACF,2BAA2BkB,CAA2B,KACxD,CACJ,CACJ,CAAC,EACDR,EAAU,SAAY,CACpB,GAAI,CACF,OAAO,MAAMyC,EAAM,OAAO,KAAM,kBAC9BA,EAAM,QAAQ,IAChB,CACF,MAAQ,CACN,UAAWnB,KAAQ,KAAK,uBAAuB,qBAAqB,EAClE,GAAI,CACF,OAAO,MAAMmB,EAAM,OAAO,KAAM,kBAAkBnB,CAAI,CACxD,MAAQ,CACN,KAAK,QAAQ,KACX,WAAWA,CAAI,wBAAwBmB,EAAM,OAAO,IAAI,EAC1D,CACF,CAEF,MAAM,IAAInD,EACR,4CACF,CACF,CACF,CAAC,EACDU,EAAW6C,GACThD,EAAK,KAAK,uBAAuB2C,EAAUK,EAAKhB,EAAYS,CAAO,CAAC,CACtE,EACArC,EAAW6C,GACTA,EAAK,KACH3C,EAAKoC,GACH,KAAK,QAAQ,KACX,aAAaC,CAAQ,KACnBD,aAAe,MAAQA,EAAI,QAAUA,CACvC,GACF,CACF,EACArC,EAAM6C,GAAU,CACd,GAAIA,EAAQ,GAAKxC,EACf,MAAM,IAAI,MAAM,qBAAqB,EAEvC,OAAOwC,EAAQ,CACjB,EAAG,CAAC,CACN,CACF,CACF,EAE4C,UAAU,CACpD,KAAM,SAAY,CAChB,KAAK,QAAQ,MAAM,yBAAyBP,CAAQ,EAAE,EAEtD,MAAMR,EAAI,KAAK,oBAAoB,IAAIQ,CAAQ,EAC3CR,IACF,aAAaA,CAAC,EACd,KAAK,oBAAoB,OAAOQ,CAAQ,GAG1C,KAAK,oBAAoB,IAAIA,CAAQ,GAAG,qBAAqB,EAE7D,GAAI,CACEC,GAAO,aACT,MAAMA,EAAM,YAAY,CAE5B,OAASR,EAAG,CACV,KAAK,QAAQ,MAAM,6BAA8B,CAC/C,KAAM,CAAE,MAAOA,CAAE,CACnB,CAAC,CACH,CAGuB,KAAK,sBAAsB,IAAIO,CAAQ,GAC9C,QAAQ,EACxB,KAAK,sBAAsB,OAAOA,CAAQ,EAE1CI,EAAgB,YAAY,CAC9B,EACA,MAAO,IAAM,CACX,MAAMZ,EAAI,KAAK,oBAAoB,IAAIQ,CAAQ,EAC3CR,IACF,aAAaA,CAAC,EACd,KAAK,oBAAoB,OAAOQ,CAAQ,GAE1C,KAAK,eAAeA,CAAQ,EAC5BI,EAAgB,YAAY,CAC9B,CACF,CAAC,CACH,CACF,CAEQ,eAAeJ,EAAwB,CAC7C,GAAI,CACF,MAAMF,EAAU,KAAK,oBAAoB,IAAIE,CAAQ,EAC/CC,EAAQ,KAAK,gBAAgB,IAAID,CAAQ,EACzCH,EAAU,KAAK,sBAAsB,IAAIG,CAAQ,EAEvD,GAAIF,EACF,GAAI,CACFA,EAAQ,wBAAwB,EAChCA,EAAQ,gBAAgB,CAC1B,OAASL,EAAG,CACV,KAAK,QAAQ,MAAM,8BAA+B,CAChD,KAAM,CAAE,MAAOA,CAAE,CACnB,CAAC,CACH,CAGEQ,IACEA,EAAM,UACRA,EAAM,OAAO,oBACX,yBACAA,EAAM,QACR,EAEEA,EAAM,OAAO,MAAM,WACrBA,EAAM,OAAO,KAAK,WAAW,GAIjC,KAAK,oBAAoB,OAAOD,CAAQ,EACxC,KAAK,gBAAgB,OAAOA,CAAQ,EAEhCH,IACFA,EAAQ,QAAQ,EAChB,KAAK,sBAAsB,OAAOG,CAAQ,GAGxC,KAAK,oBAAoB,IAAIA,CAAQ,IACvC,aAAa,KAAK,oBAAoB,IAAIA,CAAQ,CAAE,EACpD,KAAK,oBAAoB,OAAOA,CAAQ,EAE5C,OAASP,EAAG,CACV,KAAK,QAAQ,MAAM,yCAA0C,CAC3D,KAAM,CAAE,MAAOA,CAAE,CACnB,CAAC,CACH,CACF,CAEQ,mBAAmBO,EAAwB,CACjD,MAAMF,EAAU,KAAK,oBAAoB,IAAIE,CAAQ,EACrD,GAAIF,EAAS,CACX,GAAI,CACFA,EAAQ,gBAAgB,CAC1B,OAASL,EAAG,CACV,KAAK,QAAQ,MAAM,8BAA+B,CAChD,KAAM,CAAE,MAAOA,CAAE,CACnB,CAAC,CACH,CACA,KAAK,oBAAoB,OAAOO,CAAQ,CAC1C,CAEA,MAAMH,EAAU,KAAK,sBAAsB,IAAIG,CAAQ,EACnDH,IACFA,EAAQ,QAAQ,EAChB,KAAK,sBAAsB,OAAOG,CAAQ,GAGxC,KAAK,oBAAoB,IAAIA,CAAQ,IACvC,aAAa,KAAK,oBAAoB,IAAIA,CAAQ,CAAE,EACpD,KAAK,oBAAoB,OAAOA,CAAQ,EAE5C,CAEA,MAAc,uBACZA,EACAQ,EACAnB,EACAE,EACA,CACA,MAAMH,EAAc,KAAK,gBAAgB,IAAIY,CAAQ,EAEjDZ,EAAY,UACdA,EAAY,OAAO,oBACjB,yBACAA,EAAY,QACd,EAEFA,EAAY,SAAW,KAAK,iBAAiBY,EAAUX,CAAU,EACjED,EAAY,OAAO,iBACjB,yBACAA,EAAY,QACd,EAEAA,EAAY,QAAUoB,EAEtB,MAAMnB,EAAW,gBAAgB,CAC/B,oBAAqB,MAAMmB,EAAS,kBAClCpB,EAAY,MAAM,YACpB,EACA,qBAAsB,MAAMoB,EAAS,kBACnCpB,EAAY,MAAM,UACpB,CACF,CAAC,EAED,MAAMG,EAAkB,gBAAgB,CAC1C,CAEA,MAAa,WAAWJ,EAErB,CACD,MAAMH,EAAKG,EAAO,gBAAgB,GAElC,GAAI,CADsB,KAAK,oBAAoB,IAAIH,CAAE,EAEvD,OAAO9B,EAAK,IAAIF,EAAmB,kBAAkBgC,CAAE,EAAE,CAAC,EAE5D,MAAMiB,EAAQ,KAAK,gBAAgB,IAAIjB,CAAE,EAEzC,GAAIiB,GAASA,EAAM,aACjB,GAAI,CACFA,EAAM,aAAa,CACrB,OAASR,EAAG,CACV,KAAK,QAAQ,MAAM,iCAAkC,CACnD,KAAM,CAAE,MAAOA,CAAE,CACnB,CAAC,CACH,CAGF,YAAK,eAAeT,CAAE,EACf,QAAQ,QAAQ7B,EAAM,MAAS,CAAC,CACzC,CACF,CAEO,MAAMsD,EAA2C,CAAC,CACvD,sBAAArC,EACA,qBAAAsC,EACA,yBAAAC,EACA,2BAAAC,CACF,IACE,IAAIzC,EACFC,EACAsC,EACAC,EACAC,CACF",
6
- "names": ["DeviceAlreadyConnectedError", "DeviceDisconnectedWhileSendingError", "OpeningConnectionError", "TransportConnectedDevice", "UnknownDeviceError", "DeviceConnectionStateMachine", "Left", "Right", "defer", "from", "Subject", "throwError", "concatMap", "retryWhen", "scan", "tap", "timeout", "switchMap", "RECONNECT_DEVICE_TIMEOUT", "RECONNECTION_RETRY_COUNT", "SINGLE_RECONNECTION_TIMEOUT", "WebBleApduSender", "webBleIdentifier", "WebBleTransport", "deviceModelDataSource", "loggerFactory", "apduSenderFactory", "apduReceiverFactory", "filters", "s", "device", "server", "knownUuids", "service", "uuid", "infos", "id", "discovered", "d", "params", "deviceEntry", "apduSender", "disconnectHandler", "connectionMachine", "t", "e", "apdu", "triggersDisconnection", "abortTimeout", "pending", "machine", "err", "deviceId", "entry", "res", "disconnectTimer", "reconnectionSub", "svc", "err$", "count", "freshSvc", "webBleTransportFactory", "loggerServiceFactory", "apduSenderServiceFactory", "apduReceiverServiceFactory"]
4
+ "sourcesContent": ["import {\n type ApduReceiverServiceFactory,\n type ApduSenderServiceFactory,\n type BleDeviceInfos,\n type ConnectError,\n type ConnectionType,\n DeviceAlreadyConnectedError,\n type DeviceId,\n type DeviceModelDataSource,\n DeviceNotRecognizedError,\n type DisconnectHandler,\n type DmkError,\n type LoggerPublisherService,\n NoAccessibleDeviceError,\n OpeningConnectionError,\n type Transport,\n TransportConnectedDevice,\n type TransportDiscoveredDevice,\n type TransportFactory,\n type TransportIdentifier,\n UnknownDeviceError,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Either, EitherAsync, Left, Maybe, Right } from \"purify-ts\";\nimport { from, type Observable, switchMap, timer } from \"rxjs\";\nimport { v4 as uuid } from \"uuid\";\n\nimport { RECONNECT_DEVICE_TIMEOUT } from \"@api/data/WebBleConfig\";\nimport {\n BleDeviceGattServerError,\n BleTransportNotSupportedError,\n} from \"@api/model/Errors\";\nimport { BleDeviceConnection } from \"@api/transport/BleDeviceConnection\";\n\ntype PromptDeviceAccessError =\n | NoAccessibleDeviceError\n | BleTransportNotSupportedError;\n\n// An attempt to manage the state of several devices with one transport. Not final.\ntype WebBleInternalDevice = {\n id: DeviceId;\n bleDevice: BluetoothDevice;\n bleDeviceInfos: BleDeviceInfos;\n bleGattService: BluetoothRemoteGATTService;\n discoveredDevice: TransportDiscoveredDevice;\n};\n\nexport const webBleIdentifier: TransportIdentifier = \"WEB-BLE\";\n\nexport class WebBleTransport implements Transport {\n private readonly _connectedDevices: Array<BluetoothDevice>;\n private readonly _internalDevicesById: Map<DeviceId, WebBleInternalDevice>;\n private _deviceConnectionById: Map<DeviceId, BleDeviceConnection>;\n private _disconnectionHandlersById: Map<DeviceId, () => void>;\n private _logger: LoggerPublisherService;\n private readonly connectionType: ConnectionType = \"BLE\";\n private readonly identifier: TransportIdentifier = webBleIdentifier;\n\n constructor(\n private readonly _deviceModelDataSource: DeviceModelDataSource,\n private readonly _loggerServiceFactory: (\n tag: string,\n ) => LoggerPublisherService,\n private readonly _apduSenderFactory: ApduSenderServiceFactory,\n private readonly _apduReceiverFactory: ApduReceiverServiceFactory,\n ) {\n this._connectedDevices = [];\n this._internalDevicesById = new Map();\n this._deviceConnectionById = new Map();\n this._disconnectionHandlersById = new Map();\n this._logger = _loggerServiceFactory(\"WebBleTransport\");\n }\n\n /**\n * Get the Bluetooth API if supported or error\n * @returns `Either<BleTransportNotSupportedError, Bluetooth>`\n */\n private getBluetoothApi(): Either<BleTransportNotSupportedError, Bluetooth> {\n if (this.isSupported()) {\n return Right(navigator.bluetooth);\n }\n\n return Left(new BleTransportNotSupportedError(\"WebBle not supported\"));\n }\n\n isSupported(): boolean {\n try {\n const result = !!navigator?.bluetooth;\n return result;\n } catch {\n return false;\n }\n }\n\n getIdentifier(): TransportIdentifier {\n return this.identifier;\n }\n\n listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]> {\n return from([]);\n }\n\n /**\n * Get Bluetooth GATT Primary service that is used to get writeCharacteristic and notifyCharacteristic\n * @param bleDevice\n * @private\n */\n private async getBleGattService(\n bleDevice: BluetoothDevice,\n ): Promise<Either<BleDeviceGattServerError, BluetoothRemoteGATTService>> {\n if (!bleDevice.gatt) {\n return Left(new BleDeviceGattServerError(\"Device gatt not found\"));\n }\n try {\n const [bleGattService] = await bleDevice.gatt.getPrimaryServices();\n if (!bleGattService) {\n return Left(\n new BleDeviceGattServerError(\"bluetooth service not found\"),\n );\n }\n return Right(bleGattService);\n } catch (e) {\n return Left(new BleDeviceGattServerError(e));\n }\n }\n\n /**\n * BleDeviceInfos to map primary service uuid to device model & characteristics uuid\n * @param bleGattService\n * @private\n */\n private getBleDeviceInfos(\n bleGattService: BluetoothRemoteGATTService,\n ): Either<DeviceNotRecognizedError, BleDeviceInfos> {\n const serviceToBleInfos =\n this._deviceModelDataSource.getBluetoothServicesInfos();\n const bleDeviceInfos = serviceToBleInfos[bleGattService.uuid];\n\n if (!bleDeviceInfos) {\n this._logger.error(\n `Device not recognized: ${bleGattService.device.name}`,\n );\n return Left(\n new DeviceNotRecognizedError(\n `Device not recognized: ${bleGattService.device.name}`,\n ),\n );\n }\n return Right(bleDeviceInfos);\n }\n\n /**\n * Prompt device selection in navigator\n *\n * @private\n */\n private promptDeviceAccess(): EitherAsync<\n PromptDeviceAccessError,\n BluetoothDevice\n > {\n return EitherAsync(async ({ liftEither, throwE }) => {\n const bluetoothApi = await liftEither(this.getBluetoothApi());\n let bleDevice: BluetoothDevice;\n\n try {\n bleDevice = await bluetoothApi.requestDevice({\n filters: this._deviceModelDataSource\n .getBluetoothServices()\n .map((serviceUuid) => ({\n services: [serviceUuid],\n })),\n });\n } catch (error) {\n return throwE(new NoAccessibleDeviceError(error));\n }\n\n return bleDevice;\n });\n }\n\n /**\n * Generate a discovered device from BluetoothDevice, BleGATT primary service and BLE device infos\n * @param bleDeviceInfos\n * @private\n */\n private getDiscoveredDeviceFrom(\n bleDeviceInfos: BleDeviceInfos,\n ): TransportDiscoveredDevice {\n return {\n id: uuid(),\n deviceModel: bleDeviceInfos.deviceModel,\n transport: this.identifier,\n };\n }\n\n /**\n * Generate an InternalDevice from a unique id, a BluetoothDevice, BleGATT primary service and BLE device infos\n * @param discoveredDevice\n * @param bleDevice\n * @param bleDeviceInfos\n * @param bleGattService\n * @private\n */\n private setInternalDeviceFrom(\n discoveredDevice: TransportDiscoveredDevice,\n bleDevice: BluetoothDevice,\n bleDeviceInfos: BleDeviceInfos,\n bleGattService: BluetoothRemoteGATTService,\n ) {\n const internalDevice: WebBleInternalDevice = {\n id: discoveredDevice.id,\n bleDevice,\n bleGattService,\n bleDeviceInfos,\n discoveredDevice,\n };\n\n this._logger.debug(\n `Discovered device ${internalDevice.id} ${discoveredDevice.deviceModel.productName}`,\n );\n this._internalDevicesById.set(internalDevice.id, internalDevice);\n }\n\n /**\n * Main method to get a device from a button click handler\n * The GATT connection is done here in order to populate TransportDiscoveredDevice with deviceModel\n */\n startDiscovering(): Observable<TransportDiscoveredDevice> {\n this._logger.debug(\"startDiscovering\");\n\n return from(this.promptDeviceAccess()).pipe(\n switchMap(async (errorOrBleDevice) =>\n EitherAsync(async ({ liftEither, fromPromise }) => {\n const bleDevice = await liftEither(errorOrBleDevice);\n if (bleDevice.gatt) {\n try {\n await bleDevice.gatt.connect();\n } catch (error) {\n throw new OpeningConnectionError(error);\n }\n }\n try {\n const bleGattService = await fromPromise(\n this.getBleGattService(bleDevice),\n );\n const bleDeviceInfos = await liftEither(\n this.getBleDeviceInfos(bleGattService),\n );\n const discoveredDevice =\n this.getDiscoveredDeviceFrom(bleDeviceInfos);\n this.setInternalDeviceFrom(\n discoveredDevice,\n bleDevice,\n bleDeviceInfos,\n bleGattService,\n );\n return discoveredDevice;\n } catch (error) {\n this._logger.error(\"Error while discovering device\", {\n data: { error, bleDevice },\n });\n if (bleDevice.forget) {\n await bleDevice.forget();\n }\n throw error;\n }\n }).caseOf({\n Right: (discoveredDevice) => discoveredDevice,\n Left: (error) => {\n this._logger.error(\"Error while getting accessible device\", {\n data: { error },\n });\n throw error;\n },\n }),\n ),\n );\n }\n\n stopDiscovering(): void {\n this._logger.debug(\"stopDiscovering\");\n }\n\n /**\n * Connect to a BLE device and update the internal state of the associated device\n * Handle ondisconnect event on the device in order to try a reconnection\n */\n async connect({\n deviceId,\n onDisconnect,\n }: {\n deviceId: DeviceId;\n onDisconnect: DisconnectHandler;\n }): Promise<Either<ConnectError, TransportConnectedDevice>> {\n const internalDevice = this._internalDevicesById.get(deviceId);\n\n if (!internalDevice) {\n this._logger.error(`Unknown device ${deviceId}`, {\n data: { internalDevices: this._internalDevicesById },\n });\n this._logger.debug(\"Available devices\", {\n data: { devices: this._internalDevicesById },\n });\n return Left(new UnknownDeviceError(`Unknown device ${deviceId}`));\n }\n // if device already connected, remove device id from internal state and remove error\n if (this._connectedDevices.includes(internalDevice.bleDevice)) {\n this._internalDevicesById.delete(deviceId);\n return Left(new DeviceAlreadyConnectedError(\"Device already connected\"));\n }\n\n const {\n discoveredDevice: { deviceModel },\n } = internalDevice;\n\n try {\n const [writeCharacteristic, notifyCharacteristic] = await Promise.all([\n internalDevice.bleGattService.getCharacteristic(\n internalDevice.bleDeviceInfos.writeCmdUuid,\n ),\n internalDevice.bleGattService.getCharacteristic(\n internalDevice.bleDeviceInfos.notifyUuid,\n ),\n ]);\n\n const deviceConnection = new BleDeviceConnection(\n {\n writeCharacteristic,\n notifyCharacteristic,\n apduReceiverFactory: this._apduReceiverFactory,\n apduSenderFactory: this._apduSenderFactory,\n },\n this._loggerServiceFactory,\n );\n\n await deviceConnection.setup();\n\n const connectedDevice = new TransportConnectedDevice({\n sendApdu: (apdu, triggersDisconnection) =>\n deviceConnection.sendApdu(apdu, triggersDisconnection),\n deviceModel,\n id: deviceId,\n type: this.connectionType,\n transport: this.identifier,\n });\n\n internalDevice.bleDevice.ongattserverdisconnected =\n this._getDeviceDisconnectedHandler(internalDevice, deviceConnection);\n\n this._deviceConnectionById.set(internalDevice.id, deviceConnection);\n this._disconnectionHandlersById.set(internalDevice.id, () => {\n this.disconnect({ connectedDevice }).then(() => onDisconnect(deviceId));\n });\n\n this._connectedDevices.push(internalDevice.bleDevice);\n\n return Right(connectedDevice);\n } catch (error) {\n if (internalDevice.bleDevice.forget) {\n await internalDevice.bleDevice.forget();\n }\n\n this._internalDevicesById.delete(deviceId);\n\n this._logger.error(\"Error while getting characteristics\", {\n data: { error },\n });\n\n return Left(new OpeningConnectionError(error));\n }\n }\n\n /**\n * Get the device disconnected handler\n * @param internalDevice WebBleInternalDevice\n * @param deviceConnection BleDeviceConnection\n * @returns async () => void\n * @private\n */\n private _getDeviceDisconnectedHandler(\n internalDevice: WebBleInternalDevice,\n deviceConnection: BleDeviceConnection,\n ) {\n return async () => {\n // start a timer to disconnect the device if it does not reconnect\n const disconnectObserver = timer(RECONNECT_DEVICE_TIMEOUT).subscribe(\n () => {\n this._logger.debug(\"disconnection timer over\");\n // retrieve the disconnect handler and call it\n const disconnectHandler = Maybe.fromNullable(\n this._disconnectionHandlersById.get(internalDevice.id),\n );\n disconnectHandler.map((handler) => handler());\n },\n );\n\n // connect to the navigator device\n await internalDevice.bleDevice.gatt?.connect();\n\n // cancel disconnection timeout\n disconnectObserver.unsubscribe();\n\n // retrieve new ble characteristics\n const service = await this.getBleGattService(internalDevice.bleDevice);\n\n if (service.isRight()) {\n const [writeC, notifyC] = await Promise.all([\n service\n .extract()\n .getCharacteristic(internalDevice.bleDeviceInfos.writeCmdUuid),\n service\n .extract()\n .getCharacteristic(internalDevice.bleDeviceInfos.notifyUuid),\n ]);\n\n // reconnect device connection\n await deviceConnection.reconnect(writeC, notifyC);\n }\n };\n }\n\n /**\n * Disconnect from a BLE device and delete its handlers\n *\n * @param params { connectedDevice: TransportConnectedDevice }\n */\n async disconnect(params: {\n connectedDevice: TransportConnectedDevice;\n }): Promise<Either<DmkError, void>> {\n // retrieve internal device\n const maybeInternalDevice = Maybe.fromNullable(\n this._internalDevicesById.get(params.connectedDevice.id),\n );\n\n this._logger.debug(\"disconnect device\", {\n data: { connectedDevice: params.connectedDevice },\n });\n\n if (maybeInternalDevice.isNothing()) {\n this._logger.error(`Unknown device ${params.connectedDevice.id}`);\n\n return Promise.resolve(\n Left(\n new UnknownDeviceError(`Unknown device ${params.connectedDevice.id}`),\n ),\n );\n }\n\n maybeInternalDevice.map((device) => {\n const { bleDevice } = device;\n\n // retrieve device connection and disconnect it\n const maybeDeviceConnection = Maybe.fromNullable(\n this._deviceConnectionById.get(device.id),\n );\n\n maybeDeviceConnection.map((dConnection) => dConnection.disconnect());\n\n // disconnect device gatt server\n if (bleDevice.gatt?.connected) {\n bleDevice.gatt.disconnect();\n }\n // clean up objects\n this._internalDevicesById.delete(device.id);\n this._deviceConnectionById.delete(device.id);\n this._disconnectionHandlersById.delete(device.id);\n\n if (this._connectedDevices.includes(bleDevice)) {\n delete this._connectedDevices[\n this._connectedDevices.indexOf(bleDevice)\n ];\n }\n });\n\n return Promise.resolve(Right(undefined));\n }\n}\n\nexport const webBleTransportFactory: TransportFactory = ({\n deviceModelDataSource,\n loggerServiceFactory,\n apduSenderServiceFactory,\n apduReceiverServiceFactory,\n}) =>\n new WebBleTransport(\n deviceModelDataSource,\n loggerServiceFactory,\n apduSenderServiceFactory,\n apduReceiverServiceFactory,\n );\n"],
5
+ "mappings": "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",
6
+ "names": ["DeviceAlreadyConnectedError", "DeviceNotRecognizedError", "NoAccessibleDeviceError", "OpeningConnectionError", "TransportConnectedDevice", "UnknownDeviceError", "EitherAsync", "Left", "Maybe", "Right", "from", "switchMap", "timer", "uuid", "RECONNECT_DEVICE_TIMEOUT", "BleDeviceGattServerError", "BleTransportNotSupportedError", "BleDeviceConnection", "webBleIdentifier", "WebBleTransport", "_deviceModelDataSource", "_loggerServiceFactory", "_apduSenderFactory", "_apduReceiverFactory", "bleDevice", "bleGattService", "e", "bleDeviceInfos", "liftEither", "throwE", "bluetoothApi", "serviceUuid", "error", "discoveredDevice", "internalDevice", "errorOrBleDevice", "fromPromise", "deviceId", "onDisconnect", "deviceModel", "writeCharacteristic", "notifyCharacteristic", "deviceConnection", "connectedDevice", "apdu", "triggersDisconnection", "disconnectObserver", "handler", "service", "writeC", "notifyC", "params", "maybeInternalDevice", "device", "dConnection", "webBleTransportFactory", "deviceModelDataSource", "loggerServiceFactory", "apduSenderServiceFactory", "apduReceiverServiceFactory"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{DeviceAlreadyConnectedError as m,OpeningConnectionError as l,StaticDeviceModelDataSource as y,UnknownDeviceError as g}from"@ledgerhq/device-management-kit";import{Left as p,Right as w}from"purify-ts";import{firstValueFrom as i}from"rxjs";import{bleDeviceStubBuilder as v}from"../model/BleDevice.stub";import{WebBleTransport as h}from"./WebBleTransport";vi.mock("./WebBleApduSender",async()=>({WebBleApduSender:class{constructor(n,r){}setDependencies=vi.fn().mockResolvedValue(void 0)}}));vi.mock("@ledgerhq/device-management-kit",async()=>({...await vi.importActual("@ledgerhq/device-management-kit"),DeviceConnectionStateMachine:class{constructor(r){}setupConnection=vi.fn().mockResolvedValue(void 0);sendApdu=vi.fn();closeConnection=vi.fn();eventDeviceDetached=vi.fn();eventDeviceAttached=vi.fn()}}));class S{subscribers=[];tag;constructor(r,o){this.subscribers=r,this.tag=o}error=vi.fn();warn=vi.fn();info=vi.fn();debug=vi.fn()}const u=new y,D=new S([],"WebBleTransport");let t,f,b;beforeEach(()=>{vi.useFakeTimers(),f=vi.fn(),b=vi.fn(),t=new h(u,()=>D,b,f)});afterEach(()=>{vi.restoreAllMocks()});describe("WebBleTransport",()=>{describe("isSupported",()=>{it("returns false when navigator.bluetooth is undefined",()=>{delete globalThis.navigator;const n=t.isSupported();expect(n).toBe(!1)}),it("returns true when navigator.bluetooth exists",()=>{globalThis.navigator={bluetooth:{}};const n=t.isSupported();expect(n).toBe(!0)})}),describe("startDiscovering",()=>{let n,r,o,s;beforeEach(()=>{const e=u.getBluetoothServices()[0];r=v(),o={uuid:e,getCharacteristic:vi.fn().mockResolvedValue({})},s={getPrimaryServices:()=>Promise.resolve([o])},Object.defineProperty(r,"gatt",{value:{connect:()=>Promise.resolve(s)},writable:!0,configurable:!0}),n=vi.fn(),globalThis.navigator={bluetooth:{requestDevice:n}}}),it("emits a discovered device when a known device is returned",async()=>{n.mockResolvedValueOnce(r);const e=await i(t.startDiscovering());expect(e).toEqual(expect.objectContaining({deviceModel:expect.objectContaining({id:"stax"}),transport:t.getIdentifier()}))}),it("emits UnknownDeviceError when deviceInfo is missing",async()=>{const e=v();Object.defineProperty(e,"gatt",{value:{connect:()=>Promise.resolve({getPrimaryServices:()=>Promise.resolve([{uuid:"invalid"}])})},writable:!0,configurable:!0}),n.mockResolvedValueOnce(e),await expect(i(t.startDiscovering())).rejects.toBeInstanceOf(g)}),it("throws OpeningConnectionError when device has no GATT server",async()=>{const e=v();Object.defineProperty(e,"gatt",{value:void 0,writable:!0,configurable:!0}),n.mockResolvedValueOnce(e),await expect(i(t.startDiscovering())).rejects.toBeInstanceOf(l)}),it("throws OpeningConnectionError when no GATT services are found",async()=>{const e=v(),c={getPrimaryServices:()=>Promise.resolve([])};Object.defineProperty(e,"gatt",{value:{connect:()=>Promise.resolve(c)},writable:!0,configurable:!0}),n.mockResolvedValueOnce(e),await expect(i(t.startDiscovering())).rejects.toBeInstanceOf(l)})}),describe("connect/disconnect flow",()=>{let n,r,o,s;beforeEach(()=>{const e=u.getBluetoothServices()[0];r=v(),o={uuid:e,getCharacteristic:vi.fn().mockResolvedValue({})},s={getPrimaryServices:()=>Promise.resolve([o])},Object.defineProperty(r,"gatt",{value:{connect:()=>Promise.resolve(s)},writable:!0,configurable:!0}),n=vi.fn().mockResolvedValue(r),globalThis.navigator={bluetooth:{requestDevice:n}}}),it("returns UnknownDeviceError when connecting an unknown deviceId",async()=>{const e="nonexistent",c=await t.connect({deviceId:e,onDisconnect:vi.fn()});expect(c).toEqual(p(new g(`Unknown device ${e}`)))}),it("connects and returns a connected device on success",async()=>{const e=await i(t.startDiscovering()),c=await t.connect({deviceId:e.id,onDisconnect:vi.fn()});expect(c.isRight()).toBe(!0);const a=c.extract();expect(a.id).toBe(e.id)}),it("does not allow connecting twice to the same device",async()=>{const e=await i(t.startDiscovering());await t.connect({deviceId:e.id,onDisconnect:vi.fn()});const c=await t.connect({deviceId:e.id,onDisconnect:vi.fn()});expect(c).toEqual(p(new m("Device already connected")))}),it("returns OpeningConnectionError when characteristic retrieval fails",async()=>{const e={uuid:u.getBluetoothServices()[0],getCharacteristic:vi.fn().mockRejectedValue(new Error("boom"))},c={getPrimaryServices:()=>Promise.resolve([e])};Object.defineProperty(r,"gatt",{value:{connect:()=>Promise.resolve(c)},writable:!0,configurable:!0});const a=await i(t.startDiscovering()),d=await t.connect({deviceId:a.id,onDisconnect:vi.fn()});expect(d.isLeft()).toBe(!0),expect(d.extract()).toBeInstanceOf(l)}),it("disconnects successfully",async()=>{const e=await i(t.startDiscovering()),a=(await t.connect({deviceId:e.id,onDisconnect:vi.fn()})).extract(),d=await t.disconnect({connectedDevice:a});expect(d).toEqual(w(void 0))})})});
1
+ import{NoAccessibleDeviceError as p,OpeningConnectionError as b,StaticDeviceModelDataSource as w,UnknownDeviceError as h}from"@ledgerhq/device-management-kit";import{Left as f,Right as S}from"purify-ts";import{RECONNECT_DEVICE_TIMEOUT as m}from"../data/WebBleConfig";import{bleDeviceStubBuilder as E}from"../model/BleDevice.stub";import{BleTransportNotSupportedError as D}from"../model/Errors";import{BleDeviceGattServerError as g}from"../model/Errors";import{WebBleTransport as y}from"./WebBleTransport";class k{subscribers=[];tag;constructor(d,v){this.subscribers=d,this.tag=v}error=vi.fn();warn=vi.fn();info=vi.fn();debug=vi.fn()}const x=new w,O=new k([],"web-ble"),c=E();describe("WebBleTransport",()=>{let i,d,v;beforeEach(()=>{d=vi.fn(),v=vi.fn(),i=new y(x,()=>O,v,d),vi.useFakeTimers()}),afterEach(()=>{vi.restoreAllMocks()});const s=(r,t)=>{i.startDiscovering().subscribe({next:r,error:t})};describe("When Web bluetooth API is not supported",()=>{it("should not support the transport",()=>{expect(i.isSupported()).toBe(!1)}),it("should emit a startDiscovering error",()=>new Promise((r,t)=>{s(()=>{t("Should not emit any value")},e=>{expect(e).toBeInstanceOf(D),r()})}))}),describe("When Web Bluetooth API is supported",()=>{const r=vi.fn();beforeAll(()=>{global.navigator={bluetooth:{requestDevice:r}}}),afterAll(()=>{vi.restoreAllMocks(),global.navigator=void 0}),it("should support the transport",()=>{expect(i.isSupported()).toBe(!0)}),describe("startDiscovering",()=>{it("should emit device if one new grant access",()=>new Promise((t,e)=>{r.mockResolvedValueOnce(c),s(o=>{try{expect(o).toEqual(expect.objectContaining({deviceModel:expect.objectContaining({id:"nanoX",productName:"Ledger Nano X"})})),t()}catch(n){e(n)}},o=>{e(o)})})),it("should throw DeviceNotRecognizedError if the device is not recognized",()=>new Promise((t,e)=>{r.mockResolvedValueOnce({...c,gatt:{...c.gatt,getPrimaryServices:vi.fn(()=>Promise.resolve([]))},productId:16962}),s(()=>{e("should not return a device")},o=>{expect(o).toBeInstanceOf(g),t()})})),it("should emit an error if the request device is in error",()=>new Promise((t,e)=>{const o="request device error";r.mockImplementationOnce(()=>{throw new Error(o)}),s(()=>{e("should not return a device")},n=>{expect(n).toBeInstanceOf(p),expect(n).toStrictEqual(new p(new Error(o))),t()})})),it("should emit an error if the user did not grant us access to a device (clicking on cancel on the browser popup for ex)",()=>new Promise((t,e)=>{r.mockResolvedValueOnce({forget:vi.fn()}),s(o=>{e(`Should not emit any value, but emitted ${JSON.stringify(o)}`)},o=>{try{expect(o).toBeInstanceOf(g),t()}catch(n){e(n)}})}))}),describe("connect",()=>{it("should throw UnknownDeviceError if no internal device",async()=>{const t={deviceId:"fake",onDisconnect:vi.fn()},e=await i.connect(t);expect(e).toStrictEqual(f(new h("Unknown device fake")))}),it("should throw OpeningConnectionError if the device is already opened",async()=>{const t={deviceId:"fake",onDisconnect:vi.fn()},e=await i.connect(t);expect(e).toStrictEqual(f(new h("Unknown device fake")))}),it("should throw OpeningConnectionError if the device cannot be opened",()=>new Promise((t,e)=>{const o="cannot be opened";r.mockResolvedValueOnce({...c,gatt:{connect:()=>{throw new Error(o)}}}),s(()=>{e("Should not emit any value")},n=>{expect(n).toBeInstanceOf(b),t()})})),it("should return the opened device",()=>new Promise((t,e)=>{r.mockResolvedValueOnce({...c,gatt:{...c.gatt,connected:!0}}),s(o=>{i.connect({deviceId:o.id,onDisconnect:vi.fn()}).then(n=>{n.ifRight(a=>{expect(a).toEqual(expect.objectContaining({id:o.id})),t()}).ifLeft(()=>{e(n)})}).catch(n=>{e(n)})},o=>{e(o)})})),it("should return a device if available",()=>new Promise((t,e)=>{r.mockResolvedValueOnce(c),s(o=>{i.connect({deviceId:o.id,onDisconnect:vi.fn()}).then(n=>{n.ifRight(a=>{expect(a).toEqual(expect.objectContaining({id:o.id})),t()}).ifLeft(()=>{e(n)})}).catch(n=>{e(n)})},o=>{e(o)})}))}),describe("disconnect",()=>{it("should disconnect the device",()=>new Promise((t,e)=>{r.mockResolvedValueOnce(c);const o=vi.fn();s(n=>{i.connect({deviceId:n.id,onDisconnect:o}).then(a=>{a.ifRight(l=>{i.disconnect({connectedDevice:l}).then(u=>{expect(u).toStrictEqual(S(void 0)),t()}).catch(u=>{e(u)})})})},n=>{e(n)})})),it("should call disconnect handler if device is hardware disconnected",()=>new Promise((t,e)=>{const o=vi.fn(),n=vi.spyOn(i,"disconnect");r.mockResolvedValueOnce(c),s(a=>{i.connect({deviceId:a.id,onDisconnect:o}).then(()=>{c.ongattserverdisconnected(new Event("")),vi.advanceTimersByTime(m),expect(n).toHaveBeenCalled(),t()})},a=>{e(a)})}))}),describe("reconnect",()=>{it("should not call disconnection if reconnection happen",()=>new Promise((t,e)=>{const o=vi.fn(),n=vi.spyOn(i,"disconnect");r.mockResolvedValueOnce(c),s(a=>{i.connect({deviceId:a.id,onDisconnect:o}).then(()=>{c.ongattserverdisconnected(new Event("")),vi.advanceTimersByTime(m/3),expect(n).toHaveBeenCalledTimes(0),t()}).catch(l=>{e(l)})})}))})})});
2
2
  //# sourceMappingURL=WebBleTransport.test.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/api/transport/WebBleTransport.test.ts"],
4
- "sourcesContent": ["/* eslint-disable @typescript-eslint/no-unsafe-return */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nvi.mock(\"./WebBleApduSender\", async () => ({\n WebBleApduSender: class {\n constructor(_deps: any, _loggerFactory: any) {}\n setDependencies = vi.fn().mockResolvedValue(undefined);\n },\n}));\n\nvi.mock(\"@ledgerhq/device-management-kit\", async () => {\n const actual = (await vi.importActual(\n \"@ledgerhq/device-management-kit\",\n )) as any;\n return {\n ...actual,\n DeviceConnectionStateMachine: class {\n constructor(_opts: any) {}\n setupConnection = vi.fn().mockResolvedValue(undefined);\n sendApdu = vi.fn();\n closeConnection = vi.fn();\n eventDeviceDetached = vi.fn();\n eventDeviceAttached = vi.fn();\n },\n };\n});\n\nimport {\n type ApduReceiverServiceFactory,\n type ApduSenderServiceFactory,\n DeviceAlreadyConnectedError,\n type LoggerPublisherService,\n type LoggerSubscriberService,\n OpeningConnectionError,\n StaticDeviceModelDataSource,\n type TransportConnectedDevice,\n UnknownDeviceError,\n} from \"@ledgerhq/device-management-kit\";\nimport { Left, Right } from \"purify-ts\";\nimport { firstValueFrom } from \"rxjs\";\n\nimport { bleDeviceStubBuilder } from \"@api/model/BleDevice.stub\";\n\nimport { WebBleTransport } from \"./WebBleTransport\";\n\nclass LoggerPublisherServiceStub implements LoggerPublisherService {\n subscribers: LoggerSubscriberService[] = [];\n tag: string;\n constructor(subscribers: LoggerSubscriberService[], tag: string) {\n this.subscribers = subscribers;\n this.tag = tag;\n }\n error = vi.fn();\n warn = vi.fn();\n info = vi.fn();\n debug = vi.fn();\n}\n\nconst bleDeviceModelDataSource = new StaticDeviceModelDataSource();\nconst logger = new LoggerPublisherServiceStub([], \"WebBleTransport\");\n\nlet transport: WebBleTransport;\nlet apduReceiverFactory: ApduReceiverServiceFactory;\nlet apduSenderFactory: ApduSenderServiceFactory;\n\nbeforeEach(() => {\n vi.useFakeTimers();\n apduReceiverFactory = vi.fn();\n apduSenderFactory = vi.fn();\n transport = new WebBleTransport(\n bleDeviceModelDataSource,\n () => logger,\n apduSenderFactory,\n apduReceiverFactory,\n );\n});\n\nafterEach(() => {\n vi.restoreAllMocks();\n});\n\ndescribe(\"WebBleTransport\", () => {\n describe(\"isSupported\", () => {\n it(\"returns false when navigator.bluetooth is undefined\", () => {\n // given\n delete (globalThis as any).navigator;\n\n // when\n const result = transport.isSupported();\n\n // then\n expect(result).toBe(false);\n });\n\n it(\"returns true when navigator.bluetooth exists\", () => {\n // given\n (globalThis as any).navigator = { bluetooth: {} };\n\n // when\n const result = transport.isSupported();\n\n // then\n expect(result).toBe(true);\n });\n });\n\n describe(\"startDiscovering\", () => {\n let requestDevice: ReturnType<typeof vi.fn>;\n let stubDevice: BluetoothDevice;\n let mockService: any;\n let mockServer: any;\n\n beforeEach(() => {\n const serviceUuid = bleDeviceModelDataSource.getBluetoothServices()[0];\n stubDevice = bleDeviceStubBuilder();\n mockService = {\n uuid: serviceUuid,\n getCharacteristic: vi.fn().mockResolvedValue({}),\n };\n mockServer = {\n getPrimaryServices: () => Promise.resolve([mockService]),\n };\n Object.defineProperty(stubDevice, \"gatt\", {\n value: { connect: () => Promise.resolve(mockServer) },\n writable: true,\n configurable: true,\n });\n\n requestDevice = vi.fn();\n (globalThis as any).navigator = {\n bluetooth: { requestDevice },\n };\n });\n\n it(\"emits a discovered device when a known device is returned\", async () => {\n // given\n requestDevice.mockResolvedValueOnce(stubDevice);\n\n // when\n const device = await firstValueFrom(transport.startDiscovering());\n\n // then\n expect(device).toEqual(\n expect.objectContaining({\n deviceModel: expect.objectContaining({ id: \"stax\" }),\n transport: transport.getIdentifier(),\n }),\n );\n });\n\n it(\"emits UnknownDeviceError when deviceInfo is missing\", async () => {\n // given\n const unknownStub = bleDeviceStubBuilder();\n Object.defineProperty(unknownStub, \"gatt\", {\n value: {\n connect: () =>\n Promise.resolve({\n getPrimaryServices: () => Promise.resolve([{ uuid: \"invalid\" }]),\n } as any),\n },\n writable: true,\n configurable: true,\n });\n requestDevice.mockResolvedValueOnce(unknownStub);\n\n // when / then\n await expect(\n firstValueFrom(transport.startDiscovering()),\n ).rejects.toBeInstanceOf(UnknownDeviceError);\n });\n\n it(\"throws OpeningConnectionError when device has no GATT server\", async () => {\n // given\n const noGattStub = bleDeviceStubBuilder();\n Object.defineProperty(noGattStub, \"gatt\", {\n value: undefined,\n writable: true,\n configurable: true,\n });\n requestDevice.mockResolvedValueOnce(noGattStub);\n\n // when / then\n await expect(\n firstValueFrom(transport.startDiscovering()),\n ).rejects.toBeInstanceOf(OpeningConnectionError);\n });\n\n it(\"throws OpeningConnectionError when no GATT services are found\", async () => {\n // given\n const noServiceStub = bleDeviceStubBuilder();\n const emptyServer = {\n getPrimaryServices: () => Promise.resolve([]),\n } as any;\n Object.defineProperty(noServiceStub, \"gatt\", {\n value: { connect: () => Promise.resolve(emptyServer) },\n writable: true,\n configurable: true,\n });\n requestDevice.mockResolvedValueOnce(noServiceStub);\n\n // when / then\n await expect(\n firstValueFrom(transport.startDiscovering()),\n ).rejects.toBeInstanceOf(OpeningConnectionError);\n });\n });\n\n describe(\"connect/disconnect flow\", () => {\n let requestDevice: ReturnType<typeof vi.fn>;\n let stubDevice: BluetoothDevice;\n let mockService: any;\n let mockServer: any;\n\n beforeEach(() => {\n const serviceUuid = bleDeviceModelDataSource.getBluetoothServices()[0];\n stubDevice = bleDeviceStubBuilder();\n mockService = {\n uuid: serviceUuid,\n getCharacteristic: vi.fn().mockResolvedValue({}),\n };\n mockServer = {\n getPrimaryServices: () => Promise.resolve([mockService]),\n };\n Object.defineProperty(stubDevice, \"gatt\", {\n value: { connect: () => Promise.resolve(mockServer) },\n writable: true,\n configurable: true,\n });\n\n requestDevice = vi.fn().mockResolvedValue(stubDevice);\n (globalThis as any).navigator = {\n bluetooth: { requestDevice },\n };\n });\n\n it(\"returns UnknownDeviceError when connecting an unknown deviceId\", async () => {\n // given\n const deviceId = \"nonexistent\";\n\n // when\n const result = await transport.connect({\n deviceId,\n onDisconnect: vi.fn(),\n });\n\n // then\n expect(result).toEqual(\n Left(new UnknownDeviceError(`Unknown device ${deviceId}`)),\n );\n });\n\n it(\"connects and returns a connected device on success\", async () => {\n // given\n const discovered = await firstValueFrom(transport.startDiscovering());\n\n // when\n const result = await transport.connect({\n deviceId: discovered.id,\n onDisconnect: vi.fn(),\n });\n\n // then\n expect(result.isRight()).toBe(true);\n const conn = result.extract() as TransportConnectedDevice;\n expect(conn.id).toBe(discovered.id);\n });\n\n it(\"does not allow connecting twice to the same device\", async () => {\n // given\n const discovered = await firstValueFrom(transport.startDiscovering());\n await transport.connect({\n deviceId: discovered.id,\n onDisconnect: vi.fn(),\n });\n\n // when\n const secondAttempt = await transport.connect({\n deviceId: discovered.id,\n onDisconnect: vi.fn(),\n });\n\n // then\n expect(secondAttempt).toEqual(\n Left(new DeviceAlreadyConnectedError(\"Device already connected\")),\n );\n });\n\n it(\"returns OpeningConnectionError when characteristic retrieval fails\", async () => {\n // given\n const badCharacteristicService = {\n uuid: bleDeviceModelDataSource.getBluetoothServices()[0],\n getCharacteristic: vi.fn().mockRejectedValue(new Error(\"boom\")),\n };\n const badServer = {\n getPrimaryServices: () => Promise.resolve([badCharacteristicService]),\n };\n Object.defineProperty(stubDevice, \"gatt\", {\n value: { connect: () => Promise.resolve(badServer) },\n writable: true,\n configurable: true,\n });\n\n const discovered = await firstValueFrom(transport.startDiscovering());\n\n // when\n const result = await transport.connect({\n deviceId: discovered.id,\n onDisconnect: vi.fn(),\n });\n\n // then\n expect(result.isLeft()).toBe(true);\n expect(result.extract()).toBeInstanceOf(OpeningConnectionError);\n });\n\n it(\"disconnects successfully\", async () => {\n // given\n const discovered = await firstValueFrom(transport.startDiscovering());\n const result = await transport.connect({\n deviceId: discovered.id,\n onDisconnect: vi.fn(),\n });\n const conn = result.extract() as TransportConnectedDevice;\n\n // when\n const disc = await transport.disconnect({ connectedDevice: conn });\n\n // then\n expect(disc).toEqual(Right(undefined));\n });\n });\n});\n"],
5
- "mappings": "AA2BA,OAGE,+BAAAA,EAGA,0BAAAC,EACA,+BAAAC,EAEA,sBAAAC,MACK,kCACP,OAAS,QAAAC,EAAM,SAAAC,MAAa,YAC5B,OAAS,kBAAAC,MAAsB,OAE/B,OAAS,wBAAAC,MAA4B,4BAErC,OAAS,mBAAAC,MAAuB,oBAxChC,GAAG,KAAK,qBAAsB,UAAa,CACzC,iBAAkB,KAAM,CACtB,YAAYC,EAAYC,EAAqB,CAAC,CAC9C,gBAAkB,GAAG,GAAG,EAAE,kBAAkB,MAAS,CACvD,CACF,EAAE,EAEF,GAAG,KAAK,kCAAmC,UAIlC,CACL,GAJc,MAAM,GAAG,aACvB,iCACF,EAGE,6BAA8B,KAAM,CAClC,YAAYC,EAAY,CAAC,CACzB,gBAAkB,GAAG,GAAG,EAAE,kBAAkB,MAAS,EACrD,SAAW,GAAG,GAAG,EACjB,gBAAkB,GAAG,GAAG,EACxB,oBAAsB,GAAG,GAAG,EAC5B,oBAAsB,GAAG,GAAG,CAC9B,CACF,EACD,EAoBD,MAAMC,CAA6D,CACjE,YAAyC,CAAC,EAC1C,IACA,YAAYC,EAAwCC,EAAa,CAC/D,KAAK,YAAcD,EACnB,KAAK,IAAMC,CACb,CACA,MAAQ,GAAG,GAAG,EACd,KAAO,GAAG,GAAG,EACb,KAAO,GAAG,GAAG,EACb,MAAQ,GAAG,GAAG,CAChB,CAEA,MAAMC,EAA2B,IAAIb,EAC/Bc,EAAS,IAAIJ,EAA2B,CAAC,EAAG,iBAAiB,EAEnE,IAAIK,EACAC,EACAC,EAEJ,WAAW,IAAM,CACf,GAAG,cAAc,EACjBD,EAAsB,GAAG,GAAG,EAC5BC,EAAoB,GAAG,GAAG,EAC1BF,EAAY,IAAIT,EACdO,EACA,IAAMC,EACNG,EACAD,CACF,CACF,CAAC,EAED,UAAU,IAAM,CACd,GAAG,gBAAgB,CACrB,CAAC,EAED,SAAS,kBAAmB,IAAM,CAChC,SAAS,cAAe,IAAM,CAC5B,GAAG,sDAAuD,IAAM,CAE9D,OAAQ,WAAmB,UAG3B,MAAME,EAASH,EAAU,YAAY,EAGrC,OAAOG,CAAM,EAAE,KAAK,EAAK,CAC3B,CAAC,EAED,GAAG,+CAAgD,IAAM,CAEtD,WAAmB,UAAY,CAAE,UAAW,CAAC,CAAE,EAGhD,MAAMA,EAASH,EAAU,YAAY,EAGrC,OAAOG,CAAM,EAAE,KAAK,EAAI,CAC1B,CAAC,CACH,CAAC,EAED,SAAS,mBAAoB,IAAM,CACjC,IAAIC,EACAC,EACAC,EACAC,EAEJ,WAAW,IAAM,CACf,MAAMC,EAAcV,EAAyB,qBAAqB,EAAE,CAAC,EACrEO,EAAaf,EAAqB,EAClCgB,EAAc,CACZ,KAAME,EACN,kBAAmB,GAAG,GAAG,EAAE,kBAAkB,CAAC,CAAC,CACjD,EACAD,EAAa,CACX,mBAAoB,IAAM,QAAQ,QAAQ,CAACD,CAAW,CAAC,CACzD,EACA,OAAO,eAAeD,EAAY,OAAQ,CACxC,MAAO,CAAE,QAAS,IAAM,QAAQ,QAAQE,CAAU,CAAE,EACpD,SAAU,GACV,aAAc,EAChB,CAAC,EAEDH,EAAgB,GAAG,GAAG,EACrB,WAAmB,UAAY,CAC9B,UAAW,CAAE,cAAAA,CAAc,CAC7B,CACF,CAAC,EAED,GAAG,4DAA6D,SAAY,CAE1EA,EAAc,sBAAsBC,CAAU,EAG9C,MAAMI,EAAS,MAAMpB,EAAeW,EAAU,iBAAiB,CAAC,EAGhE,OAAOS,CAAM,EAAE,QACb,OAAO,iBAAiB,CACtB,YAAa,OAAO,iBAAiB,CAAE,GAAI,MAAO,CAAC,EACnD,UAAWT,EAAU,cAAc,CACrC,CAAC,CACH,CACF,CAAC,EAED,GAAG,sDAAuD,SAAY,CAEpE,MAAMU,EAAcpB,EAAqB,EACzC,OAAO,eAAeoB,EAAa,OAAQ,CACzC,MAAO,CACL,QAAS,IACP,QAAQ,QAAQ,CACd,mBAAoB,IAAM,QAAQ,QAAQ,CAAC,CAAE,KAAM,SAAU,CAAC,CAAC,CACjE,CAAQ,CACZ,EACA,SAAU,GACV,aAAc,EAChB,CAAC,EACDN,EAAc,sBAAsBM,CAAW,EAG/C,MAAM,OACJrB,EAAeW,EAAU,iBAAiB,CAAC,CAC7C,EAAE,QAAQ,eAAed,CAAkB,CAC7C,CAAC,EAED,GAAG,+DAAgE,SAAY,CAE7E,MAAMyB,EAAarB,EAAqB,EACxC,OAAO,eAAeqB,EAAY,OAAQ,CACxC,MAAO,OACP,SAAU,GACV,aAAc,EAChB,CAAC,EACDP,EAAc,sBAAsBO,CAAU,EAG9C,MAAM,OACJtB,EAAeW,EAAU,iBAAiB,CAAC,CAC7C,EAAE,QAAQ,eAAehB,CAAsB,CACjD,CAAC,EAED,GAAG,gEAAiE,SAAY,CAE9E,MAAM4B,EAAgBtB,EAAqB,EACrCuB,EAAc,CAClB,mBAAoB,IAAM,QAAQ,QAAQ,CAAC,CAAC,CAC9C,EACA,OAAO,eAAeD,EAAe,OAAQ,CAC3C,MAAO,CAAE,QAAS,IAAM,QAAQ,QAAQC,CAAW,CAAE,EACrD,SAAU,GACV,aAAc,EAChB,CAAC,EACDT,EAAc,sBAAsBQ,CAAa,EAGjD,MAAM,OACJvB,EAAeW,EAAU,iBAAiB,CAAC,CAC7C,EAAE,QAAQ,eAAehB,CAAsB,CACjD,CAAC,CACH,CAAC,EAED,SAAS,0BAA2B,IAAM,CACxC,IAAIoB,EACAC,EACAC,EACAC,EAEJ,WAAW,IAAM,CACf,MAAMC,EAAcV,EAAyB,qBAAqB,EAAE,CAAC,EACrEO,EAAaf,EAAqB,EAClCgB,EAAc,CACZ,KAAME,EACN,kBAAmB,GAAG,GAAG,EAAE,kBAAkB,CAAC,CAAC,CACjD,EACAD,EAAa,CACX,mBAAoB,IAAM,QAAQ,QAAQ,CAACD,CAAW,CAAC,CACzD,EACA,OAAO,eAAeD,EAAY,OAAQ,CACxC,MAAO,CAAE,QAAS,IAAM,QAAQ,QAAQE,CAAU,CAAE,EACpD,SAAU,GACV,aAAc,EAChB,CAAC,EAEDH,EAAgB,GAAG,GAAG,EAAE,kBAAkBC,CAAU,EACnD,WAAmB,UAAY,CAC9B,UAAW,CAAE,cAAAD,CAAc,CAC7B,CACF,CAAC,EAED,GAAG,iEAAkE,SAAY,CAE/E,MAAMU,EAAW,cAGXX,EAAS,MAAMH,EAAU,QAAQ,CACrC,SAAAc,EACA,aAAc,GAAG,GAAG,CACtB,CAAC,EAGD,OAAOX,CAAM,EAAE,QACbhB,EAAK,IAAID,EAAmB,kBAAkB4B,CAAQ,EAAE,CAAC,CAC3D,CACF,CAAC,EAED,GAAG,qDAAsD,SAAY,CAEnE,MAAMC,EAAa,MAAM1B,EAAeW,EAAU,iBAAiB,CAAC,EAG9DG,EAAS,MAAMH,EAAU,QAAQ,CACrC,SAAUe,EAAW,GACrB,aAAc,GAAG,GAAG,CACtB,CAAC,EAGD,OAAOZ,EAAO,QAAQ,CAAC,EAAE,KAAK,EAAI,EAClC,MAAMa,EAAOb,EAAO,QAAQ,EAC5B,OAAOa,EAAK,EAAE,EAAE,KAAKD,EAAW,EAAE,CACpC,CAAC,EAED,GAAG,qDAAsD,SAAY,CAEnE,MAAMA,EAAa,MAAM1B,EAAeW,EAAU,iBAAiB,CAAC,EACpE,MAAMA,EAAU,QAAQ,CACtB,SAAUe,EAAW,GACrB,aAAc,GAAG,GAAG,CACtB,CAAC,EAGD,MAAME,EAAgB,MAAMjB,EAAU,QAAQ,CAC5C,SAAUe,EAAW,GACrB,aAAc,GAAG,GAAG,CACtB,CAAC,EAGD,OAAOE,CAAa,EAAE,QACpB9B,EAAK,IAAIJ,EAA4B,0BAA0B,CAAC,CAClE,CACF,CAAC,EAED,GAAG,qEAAsE,SAAY,CAEnF,MAAMmC,EAA2B,CAC/B,KAAMpB,EAAyB,qBAAqB,EAAE,CAAC,EACvD,kBAAmB,GAAG,GAAG,EAAE,kBAAkB,IAAI,MAAM,MAAM,CAAC,CAChE,EACMqB,EAAY,CAChB,mBAAoB,IAAM,QAAQ,QAAQ,CAACD,CAAwB,CAAC,CACtE,EACA,OAAO,eAAeb,EAAY,OAAQ,CACxC,MAAO,CAAE,QAAS,IAAM,QAAQ,QAAQc,CAAS,CAAE,EACnD,SAAU,GACV,aAAc,EAChB,CAAC,EAED,MAAMJ,EAAa,MAAM1B,EAAeW,EAAU,iBAAiB,CAAC,EAG9DG,EAAS,MAAMH,EAAU,QAAQ,CACrC,SAAUe,EAAW,GACrB,aAAc,GAAG,GAAG,CACtB,CAAC,EAGD,OAAOZ,EAAO,OAAO,CAAC,EAAE,KAAK,EAAI,EACjC,OAAOA,EAAO,QAAQ,CAAC,EAAE,eAAenB,CAAsB,CAChE,CAAC,EAED,GAAG,2BAA4B,SAAY,CAEzC,MAAM+B,EAAa,MAAM1B,EAAeW,EAAU,iBAAiB,CAAC,EAK9DgB,GAJS,MAAMhB,EAAU,QAAQ,CACrC,SAAUe,EAAW,GACrB,aAAc,GAAG,GAAG,CACtB,CAAC,GACmB,QAAQ,EAGtBK,EAAO,MAAMpB,EAAU,WAAW,CAAE,gBAAiBgB,CAAK,CAAC,EAGjE,OAAOI,CAAI,EAAE,QAAQhC,EAAM,MAAS,CAAC,CACvC,CAAC,CACH,CAAC,CACH,CAAC",
6
- "names": ["DeviceAlreadyConnectedError", "OpeningConnectionError", "StaticDeviceModelDataSource", "UnknownDeviceError", "Left", "Right", "firstValueFrom", "bleDeviceStubBuilder", "WebBleTransport", "_deps", "_loggerFactory", "_opts", "LoggerPublisherServiceStub", "subscribers", "tag", "bleDeviceModelDataSource", "logger", "transport", "apduReceiverFactory", "apduSenderFactory", "result", "requestDevice", "stubDevice", "mockService", "mockServer", "serviceUuid", "device", "unknownStub", "noGattStub", "noServiceStub", "emptyServer", "deviceId", "discovered", "conn", "secondAttempt", "badCharacteristicService", "badServer", "disc"]
4
+ "sourcesContent": ["import {\n type ApduReceiverServiceFactory,\n type ApduSenderServiceFactory,\n type DeviceModel,\n type LoggerPublisherService,\n type LoggerSubscriberService,\n NoAccessibleDeviceError,\n OpeningConnectionError,\n StaticDeviceModelDataSource,\n type TransportDiscoveredDevice,\n UnknownDeviceError,\n} from \"@ledgerhq/device-management-kit\";\nimport { Left, Right } from \"purify-ts\";\n\nimport { RECONNECT_DEVICE_TIMEOUT } from \"@api/data/WebBleConfig\";\nimport { bleDeviceStubBuilder } from \"@api/model/BleDevice.stub\";\nimport { BleTransportNotSupportedError } from \"@api/model/Errors\";\nimport { BleDeviceGattServerError } from \"@api/model/Errors\";\n\nimport { WebBleTransport } from \"./WebBleTransport\";\n\nclass LoggerPublisherServiceStub implements LoggerPublisherService {\n subscribers: LoggerSubscriberService[] = [];\n tag: string;\n constructor(subscribers: LoggerSubscriberService[], tag: string) {\n this.subscribers = subscribers;\n this.tag = tag;\n }\n error = vi.fn();\n warn = vi.fn();\n info = vi.fn();\n debug = vi.fn();\n}\n\n// Our StaticDeviceModelDataSource can directly be used in our unit tests\nconst bleDeviceModelDataSource = new StaticDeviceModelDataSource();\nconst logger = new LoggerPublisherServiceStub([], \"web-ble\");\n\nconst stubDevice: BluetoothDevice = bleDeviceStubBuilder();\n\ndescribe(\"WebBleTransport\", () => {\n let transport: WebBleTransport;\n let apduReceiverServiceFactoryStub: ApduReceiverServiceFactory;\n let apduSenderServiceFactoryStub: ApduSenderServiceFactory;\n\n beforeEach(() => {\n apduReceiverServiceFactoryStub = vi.fn();\n apduSenderServiceFactoryStub = vi.fn();\n transport = new WebBleTransport(\n bleDeviceModelDataSource,\n () => logger,\n apduSenderServiceFactoryStub,\n apduReceiverServiceFactoryStub,\n );\n vi.useFakeTimers();\n });\n\n afterEach(() => {\n vi.restoreAllMocks();\n });\n\n const discoverDevice = (\n onSuccess: (discoveredDevice: TransportDiscoveredDevice) => void,\n onError?: (error: unknown) => void,\n ) => {\n transport.startDiscovering().subscribe({\n next: onSuccess,\n error: onError,\n });\n };\n\n describe(\"When Web bluetooth API is not supported\", () => {\n it(\"should not support the transport\", () => {\n expect(transport.isSupported()).toBe(false);\n });\n\n it(\"should emit a startDiscovering error\", () =>\n new Promise<void>((resolve, reject) => {\n discoverDevice(\n () => {\n reject(\"Should not emit any value\");\n },\n (error) => {\n expect(error).toBeInstanceOf(BleTransportNotSupportedError);\n resolve();\n },\n );\n }));\n });\n\n describe(\"When Web Bluetooth API is supported\", () => {\n const mockedRequestDevice = vi.fn();\n\n beforeAll(() => {\n global.navigator = {\n bluetooth: {\n requestDevice: mockedRequestDevice,\n },\n } as unknown as Navigator;\n });\n\n afterAll(() => {\n vi.restoreAllMocks();\n global.navigator = undefined as unknown as Navigator;\n });\n\n it(\"should support the transport\", () => {\n expect(transport.isSupported()).toBe(true);\n });\n\n describe(\"startDiscovering\", () => {\n it(\"should emit device if one new grant access\", () =>\n new Promise<void>((resolve, reject) => {\n mockedRequestDevice.mockResolvedValueOnce(stubDevice);\n\n discoverDevice(\n (discoveredDevice) => {\n try {\n expect(discoveredDevice).toEqual(\n expect.objectContaining({\n deviceModel: expect.objectContaining({\n id: \"nanoX\",\n productName: \"Ledger Nano X\",\n }) as DeviceModel,\n }),\n );\n\n resolve();\n } catch (expectError) {\n reject(expectError as Error);\n }\n },\n (error) => {\n reject(error as Error);\n },\n );\n }));\n\n it(\"should throw DeviceNotRecognizedError if the device is not recognized\", () =>\n new Promise<void>((resolve, reject) => {\n mockedRequestDevice.mockResolvedValueOnce({\n ...stubDevice,\n gatt: {\n ...stubDevice.gatt,\n getPrimaryServices: vi.fn(() => Promise.resolve([])),\n },\n productId: 0x4242,\n });\n\n discoverDevice(\n () => {\n reject(\"should not return a device\");\n },\n (error) => {\n expect(error).toBeInstanceOf(BleDeviceGattServerError);\n resolve();\n },\n );\n }));\n\n it(\"should emit an error if the request device is in error\", () =>\n new Promise<void>((resolve, reject) => {\n const message = \"request device error\";\n mockedRequestDevice.mockImplementationOnce(() => {\n throw new Error(message);\n });\n\n discoverDevice(\n () => {\n reject(\"should not return a device\");\n },\n (error) => {\n expect(error).toBeInstanceOf(NoAccessibleDeviceError);\n expect(error).toStrictEqual(\n new NoAccessibleDeviceError(new Error(message)),\n );\n resolve();\n },\n );\n }));\n\n it(\"should emit an error if the user did not grant us access to a device (clicking on cancel on the browser popup for ex)\", () =>\n new Promise<void>((resolve, reject) => {\n mockedRequestDevice.mockResolvedValueOnce({ forget: vi.fn() });\n\n discoverDevice(\n (discoveredDevice) => {\n reject(\n `Should not emit any value, but emitted ${JSON.stringify(\n discoveredDevice,\n )}`,\n );\n },\n (error) => {\n try {\n expect(error).toBeInstanceOf(BleDeviceGattServerError);\n resolve();\n } catch (expectError) {\n reject(expectError as Error);\n }\n },\n );\n }));\n });\n\n describe(\"connect\", () => {\n it(\"should throw UnknownDeviceError if no internal device\", async () => {\n const connectParams = {\n deviceId: \"fake\",\n onDisconnect: vi.fn(),\n };\n\n const connect = await transport.connect(connectParams);\n\n expect(connect).toStrictEqual(\n Left(new UnknownDeviceError(\"Unknown device fake\")),\n );\n });\n\n it(\"should throw OpeningConnectionError if the device is already opened\", async () => {\n const device = {\n deviceId: \"fake\",\n onDisconnect: vi.fn(),\n };\n\n const connect = await transport.connect(device);\n\n expect(connect).toStrictEqual(\n Left(new UnknownDeviceError(\"Unknown device fake\")),\n );\n });\n\n it(\"should throw OpeningConnectionError if the device cannot be opened\", () =>\n new Promise<void>((resolve, reject) => {\n const message = \"cannot be opened\";\n mockedRequestDevice.mockResolvedValueOnce({\n ...stubDevice,\n gatt: {\n connect: () => {\n throw new Error(message);\n },\n },\n });\n\n discoverDevice(\n () => {\n reject(\"Should not emit any value\");\n },\n (error) => {\n expect(error).toBeInstanceOf(OpeningConnectionError);\n resolve();\n },\n );\n }));\n\n it(\"should return the opened device\", () =>\n new Promise<void>((resolve, reject) => {\n mockedRequestDevice.mockResolvedValueOnce({\n ...stubDevice,\n gatt: {\n ...stubDevice.gatt,\n connected: true,\n },\n });\n\n discoverDevice(\n (discoveredDevice) => {\n transport\n .connect({\n deviceId: discoveredDevice.id,\n onDisconnect: vi.fn(),\n })\n .then((connectedDevice) => {\n connectedDevice\n .ifRight((device) => {\n expect(device).toEqual(\n expect.objectContaining({ id: discoveredDevice.id }),\n );\n resolve();\n })\n .ifLeft(() => {\n reject(connectedDevice);\n });\n })\n .catch((error) => {\n reject(error);\n });\n },\n (error) => {\n reject(error as Error);\n },\n );\n }));\n\n it(\"should return a device if available\", () =>\n new Promise<void>((resolve, reject) => {\n mockedRequestDevice.mockResolvedValueOnce(stubDevice);\n\n discoverDevice(\n (discoveredDevice) => {\n transport\n .connect({\n deviceId: discoveredDevice.id,\n onDisconnect: vi.fn(),\n })\n .then((connectedDevice) => {\n connectedDevice\n .ifRight((device) => {\n expect(device).toEqual(\n expect.objectContaining({ id: discoveredDevice.id }),\n );\n resolve();\n })\n .ifLeft(() => {\n reject(connectedDevice);\n });\n })\n .catch((error) => {\n reject(error);\n });\n },\n (error) => {\n reject(error as Error);\n },\n );\n }));\n });\n\n describe(\"disconnect\", () => {\n it(\"should disconnect the device\", () =>\n new Promise<void>((resolve, reject) => {\n mockedRequestDevice.mockResolvedValueOnce(stubDevice);\n\n const onDisconnect = vi.fn();\n\n discoverDevice(\n (discoveredDevice) => {\n transport\n .connect({\n deviceId: discoveredDevice.id,\n onDisconnect,\n })\n .then((connectedDevice) => {\n connectedDevice.ifRight((device) => {\n transport\n .disconnect({ connectedDevice: device })\n .then((value) => {\n expect(value).toStrictEqual(Right(undefined));\n resolve();\n })\n .catch((error) => {\n reject(error);\n });\n });\n });\n },\n (error) => {\n reject(error as Error);\n },\n );\n }));\n\n it(\"should call disconnect handler if device is hardware disconnected\", () =>\n new Promise<void>((resolve, reject) => {\n const onDisconnect = vi.fn();\n const disconnectSpy = vi.spyOn(transport, \"disconnect\");\n mockedRequestDevice.mockResolvedValueOnce(stubDevice);\n\n discoverDevice(\n (discoveredDevice) => {\n transport\n .connect({\n deviceId: discoveredDevice.id,\n onDisconnect,\n })\n .then(() => {\n stubDevice.ongattserverdisconnected(new Event(\"\"));\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT);\n expect(disconnectSpy).toHaveBeenCalled();\n resolve();\n });\n },\n (error) => {\n reject(error as Error);\n },\n );\n }));\n });\n\n describe(\"reconnect\", () => {\n it(\"should not call disconnection if reconnection happen\", () =>\n new Promise<void>((resolve, reject) => {\n // given\n const onDisconnect = vi.fn();\n const disconnectSpy = vi.spyOn(transport, \"disconnect\");\n mockedRequestDevice.mockResolvedValueOnce(stubDevice);\n\n // when\n discoverDevice((discoveredDevice) => {\n transport\n .connect({\n deviceId: discoveredDevice.id,\n onDisconnect,\n })\n .then(() => {\n stubDevice.ongattserverdisconnected(new Event(\"\"));\n\n vi.advanceTimersByTime(RECONNECT_DEVICE_TIMEOUT / 3);\n\n // then\n expect(disconnectSpy).toHaveBeenCalledTimes(0);\n resolve();\n })\n .catch((error) => {\n reject(error);\n });\n });\n }));\n });\n });\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["NoAccessibleDeviceError", "OpeningConnectionError", "StaticDeviceModelDataSource", "UnknownDeviceError", "Left", "Right", "RECONNECT_DEVICE_TIMEOUT", "bleDeviceStubBuilder", "BleTransportNotSupportedError", "BleDeviceGattServerError", "WebBleTransport", "LoggerPublisherServiceStub", "subscribers", "tag", "bleDeviceModelDataSource", "logger", "stubDevice", "transport", "apduReceiverServiceFactoryStub", "apduSenderServiceFactoryStub", "discoverDevice", "onSuccess", "onError", "resolve", "reject", "error", "mockedRequestDevice", "discoveredDevice", "expectError", "message", "connectParams", "connect", "device", "connectedDevice", "onDisconnect", "value", "disconnectSpy"]
7
7
  }
@@ -1,4 +1,2 @@
1
- export declare const RECONNECT_DEVICE_TIMEOUT = 15000;
2
- export declare const SINGLE_RECONNECTION_TIMEOUT = 2000;
3
- export declare const RECONNECTION_RETRY_COUNT = 5;
1
+ export declare const RECONNECT_DEVICE_TIMEOUT = 6000;
4
2
  //# sourceMappingURL=WebBleConfig.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WebBleConfig.d.ts","sourceRoot":"","sources":["../../../../src/api/data/WebBleConfig.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB,QAAQ,CAAC;AAC9C,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,wBAAwB,IAAI,CAAC"}
1
+ {"version":3,"file":"WebBleConfig.d.ts","sourceRoot":"","sources":["../../../../src/api/data/WebBleConfig.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB,OAAO,CAAC"}