@ledgerhq/device-transport-kit-web-ble 0.0.0-legacy-speculos-datasource-20250821095840 → 0.0.0-lifi-with-logger-20251208144248

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- 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=15e3,o=400,t=1200,I=3e3,O=500;export{o as ADVERTISING_DELAY,t as ADVERTISING_TIMEOUT,O as RECONNECTION_LOOP_BACKOFF,E as RECONNECT_DEVICE_TIMEOUT,I as REDISCOVER_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 = 15000;\nexport const ADVERTISING_DELAY = 400;\nexport const ADVERTISING_TIMEOUT = 1200;\nexport const REDISCOVER_TIMEOUT = 3000;\nexport const RECONNECTION_LOOP_BACKOFF = 500;\n"],
5
+ "mappings": "AAAO,MAAMA,EAA2B,KAC3BC,EAAoB,IACpBC,EAAsB,KACtBC,EAAqB,IACrBC,EAA4B",
6
+ "names": ["RECONNECT_DEVICE_TIMEOUT", "ADVERTISING_DELAY", "ADVERTISING_TIMEOUT", "REDISCOVER_TIMEOUT", "RECONNECTION_LOOP_BACKOFF"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{DeviceDisconnectedWhileSendingError as n}from"@ledgerhq/device-management-kit";import{Left as o,Maybe as r,Right as d}from"purify-ts";import{BehaviorSubject as p}from"rxjs";class h{_characteristics;_apduSender=r.empty();_apduSenderFactory;_apduReceiver;_logger;_isDeviceReady=new p(!1);_sendResolver=r.empty();_notificationsActive=!1;_apduReceiverFactory;_mtuHandshakeInFlight=!1;static MTU_OP=8;constructor(e,t){this._characteristics={writeCharacteristic:e.writeCharacteristic,notifyCharacteristic:e.notifyCharacteristic},this._apduSenderFactory=e.apduSenderFactory,this._apduReceiverFactory=e.apduReceiverFactory,this._apduReceiver=e.apduReceiverFactory(),this._logger=t("WebBleApduSender")}_failPendingSend(e){this._sendResolver.map(t=>t(o(e))),this._sendResolver=r.empty()}_handleNotify=e=>{const t=e.target;if(!t.value)return;const i=new Uint8Array(t.value.buffer);if(this._isDeviceReady.value)this._onReceiveApdu(i);else{if(!this._mtuHandshakeInFlight){this._logger.debug("Dropping pre-handshake frame",{data:{data:i}});return}if(i.length<6||i[0]!==h.MTU_OP){this._logger.debug("Non-MTU frame during handshake; dropping",{data:{data:i}});return}this._onReceiveSetup(i)}};_onReceiveSetup(e){const t=e[5];if(typeof t!="number"||t<=0)throw new Error("MTU negotiation failed: invalid MTU");this._apduSender=r.of(this._apduSenderFactory({frameSize:t})),this._isDeviceReady.next(!0)}_onReceiveApdu(e){this._apduReceiver.handleFrame(e).map(t=>t.map(i=>{this._logger.debug("Received APDU",{data:{resp:i}}),this._sendResolver.map(a=>a(d(i))),this._sendResolver=r.empty()})).mapLeft(t=>{this._sendResolver.map(i=>i(o(t))),this._sendResolver=r.empty()})}async _write(e){const t=this._characteristics.writeCharacteristic.properties;if(t.write)await this._characteristics.writeCharacteristic.writeValueWithResponse(e);else if(t.writeWithoutResponse)await this._characteristics.writeCharacteristic.writeValueWithoutResponse(e);else try{await this._characteristics.writeCharacteristic.writeValueWithResponse(e)}catch{await this._characteristics.writeCharacteristic.writeValueWithoutResponse(e)}}async setupConnection(){const e=this._characteristics.notifyCharacteristic;this._notificationsActive||(await e.startNotifications(),this._notificationsActive=!0,e.addEventListener("characteristicvaluechanged",this._handleNotify)),this._mtuHandshakeInFlight=!0;const t=new Uint8Array([h.MTU_OP,0,0,0,0]);try{await this._write(t.buffer),await Promise.race([new Promise((i,a)=>{const c=this._isDeviceReady.subscribe({next:s=>{s&&(c.unsubscribe(),i())},error:s=>{c.unsubscribe(),a(s)}})}),new Promise((i,a)=>setTimeout(()=>a(new Error("MTU negotiation timeout")),2e3))])}catch(i){try{e.removeEventListener("characteristicvaluechanged",this._handleNotify),await e.stopNotifications().catch(()=>{})}finally{this._notificationsActive=!1,this._isDeviceReady.next(!1),this._apduSender=r.empty()}throw i}finally{this._mtuHandshakeInFlight=!1}}async sendApdu(e,t,i){if(this._apduSender.isNothing())return o(new n("Link not ready (no MTU / sender)"));const a=new Promise(c=>{this._sendResolver=r.of(c)});for(const c of this._apduSender.map(s=>s.getFrames(e)).orDefault([]))try{await this._write(c.getRawData().buffer)}catch(s){this._logger.error("Frame write failed",{data:{e:s}}),this._failPendingSend(new n("Write failed"));break}return a}closeConnection(){try{this._failPendingSend(new n("Connection closed")),this._notificationsActive&&(this._characteristics.notifyCharacteristic.removeEventListener("characteristicvaluechanged",this._handleNotify),this._characteristics.notifyCharacteristic.stopNotifications().catch(()=>{}),this._notificationsActive=!1),this._characteristics.notifyCharacteristic.service.device.gatt?.disconnect()}catch{this._logger.error("Failed to disconnect from device")}}getDependencies(){return this._characteristics}async setDependencies(e){const t=this._characteristics.notifyCharacteristic;if(this._failPendingSend(new n("Link changed")),this._notificationsActive&&t.service.device.gatt?.connected)try{await t.stopNotifications()}catch{this._logger.warn("Failed to stop notifications on old characteristic")}t.removeEventListener("characteristicvaluechanged",this._handleNotify),this._notificationsActive=!1,this._characteristics=e,this._apduReceiver=this._apduReceiverFactory(),this._isDeviceReady.next(!1),this._apduSender=r.empty(),this._sendResolver=r.empty(),await e.notifyCharacteristic.startNotifications(),this._notificationsActive=!0,e.notifyCharacteristic.addEventListener("characteristicvaluechanged",this._handleNotify)}}export{h as WebBleApduSender};
1
+ import{DeviceDisconnectedWhileSendingError as o,DeviceNotInitializedError as h,SendApduTimeoutError as l}from"@ledgerhq/device-management-kit";import{Left as c,Maybe as n,Right as f}from"purify-ts";import{BehaviorSubject as _}from"rxjs";const u=8;class w{_dependencies;_apduFrameSegmenter=n.empty();_apduSenderFactory;_apduReceiverFactory;_apduFrameReceiver;_logger;_mtuNegotiated$=new _(!1);_notificationsReady=!1;_mtuRequestInProgress=!1;_pendingResponseResolver=n.empty();constructor(t,e){this._dependencies={writeCharacteristic:t.writeCharacteristic,notifyCharacteristic:t.notifyCharacteristic},this._apduSenderFactory=t.apduSenderFactory,this._apduReceiverFactory=t.apduReceiverFactory,this._apduFrameReceiver=t.apduReceiverFactory(),this._logger=e("WebBleApduSender")}async sendApdu(t,e,i){try{const a=Math.max(1800,i??0);await this._waitUntilMtuNegotiated(a)}catch(a){return c(a)}if(!this._isGattConnected())return this._markLinkUnavailable(),c(new o("GATT not connected"));if(this._apduFrameSegmenter.isNothing())return c(new h("Unknown MTU / sender not ready"));let r;const s=new Promise(a=>{this._pendingResponseResolver=n.of(p=>{r&&clearTimeout(r),a(p)})}),d=this._apduFrameSegmenter.map(a=>a.getFrames(t)).orDefault([]);for(const a of d)try{await this._writeToGattCharacteristic(a.getRawData().slice().buffer)}catch(p){const m=e?"Frame write failed during expected drop":"Frame write failed";this._logger[e?"debug":"error"](m,{data:{e:p}}),this._failPendingSend(new o("Write failed"));break}return i&&(r=setTimeout(()=>{this._logger.debug("[sendApdu] Abort timeout triggered"),this._pendingResponseResolver.map(a=>a(c(new l("Abort timeout"))))},i)),s}closeConnection(){try{this._failPendingSend(new o("Connection closed")),this._notificationsReady&&(this._dependencies.notifyCharacteristic.removeEventListener("characteristicvaluechanged",this._handleNotification),this._dependencies.notifyCharacteristic.stopNotifications().catch(()=>{}),this._notificationsReady=!1),this._dependencies.notifyCharacteristic.service.device.gatt?.disconnect()}catch{this._logger.error("Failed to disconnect from device")}finally{this._mtuNegotiated$.next(!1),this._apduFrameSegmenter=n.empty()}}getDependencies(){return this._dependencies}setDependencies(t){this._failPendingSend(new o("Link changed"));try{this._notificationsReady&&(this._dependencies.notifyCharacteristic.removeEventListener("characteristicvaluechanged",this._handleNotification),this._dependencies.notifyCharacteristic.stopNotifications().catch(()=>{}))}catch{}this._notificationsReady=!1,this._mtuNegotiated$.next(!1),this._apduFrameSegmenter=n.empty(),this._pendingResponseResolver=n.empty(),this._dependencies=t,this._apduFrameReceiver=this._apduReceiverFactory()}async setupConnection(){const t=this._dependencies.notifyCharacteristic;this._notificationsReady||(await t.startNotifications(),this._logger.debug("Notify armed",{data:{notifyUuid:this._dependencies.notifyCharacteristic.uuid,writeUuid:this._dependencies.writeCharacteristic.uuid,props:this._dependencies.writeCharacteristic.properties}}),this._notificationsReady=!0,t.addEventListener("characteristicvaluechanged",this._handleNotification)),await this._sleep(120),this._mtuRequestInProgress=!0,this._mtuNegotiated$.next(!1),this._apduFrameSegmenter=n.empty();const e=new Uint8Array([u,0,0,0,0]);try{await this._writeToGattCharacteristic(e.buffer),await Promise.race([new Promise((i,r)=>{const s=setTimeout(()=>r(new Error("MTU negotiation timeout")),2e3),d=this._mtuNegotiated$.subscribe(a=>{a&&(clearTimeout(s),d.unsubscribe(),i())})}),this._sleep(2300).then(()=>{if(!this._isGattConnected())throw new o("Link dropped during MTU")})])}catch(i){try{t.removeEventListener("characteristicvaluechanged",this._handleNotification),await t.stopNotifications().catch(()=>{})}finally{this._notificationsReady=!1,this._mtuNegotiated$.next(!1),this._apduFrameSegmenter=n.empty()}throw i}finally{this._mtuRequestInProgress=!1}}_isGattConnected(){try{return!!this._dependencies.notifyCharacteristic.service.device.gatt?.connected}catch{return!1}}_isGattDisconnectedError(t){const e=t,i=(typeof e=="object"&&e!==null&&"name"in e?e.name??"":"").toString(),r=(typeof e=="object"&&e!==null&&"message"in e?e.message??"":"").toString().toLowerCase();return i==="NetworkError"||r.includes("gatt server is disconnected")||r.includes("not connected")||r.includes("cannot perform gatt operations")}_failPendingSend(t){this._pendingResponseResolver.map(e=>e(c(t))),this._pendingResponseResolver=n.empty()}_markLinkUnavailable(){this._notificationsReady&&(this._dependencies.notifyCharacteristic.removeEventListener("characteristicvaluechanged",this._handleNotification),this._dependencies.notifyCharacteristic.stopNotifications().catch(()=>{}),this._notificationsReady=!1),this._mtuNegotiated$.next(!1),this._apduFrameSegmenter=n.empty(),this._pendingResponseResolver=n.empty()}async _sleep(t){return new Promise(e=>setTimeout(e,t))}_handleNotification=t=>{const e=t.target;if(!e.value)return;const i=new Uint8Array(e.value.buffer);if(!this._mtuNegotiated$.value){if(!this._mtuRequestInProgress){this._logger.debug("Dropping pre-handshake frame",{data:{data:i}});return}if(i.length<6||i[0]!==u){this._logger.debug("Non-MTU frame during handshake; dropping",{data:{data:i}});return}this._handleMtuNegotiationFrame(i);return}this._handleApduFrame(i)};_handleMtuNegotiationFrame(t){const e=t[5];if(e===void 0||!Number.isFinite(e)||e<=0)throw new Error("MTU negotiation failed: invalid MTU");const i=e;this._apduFrameSegmenter=n.of(this._apduSenderFactory({frameSize:i})),this._mtuNegotiated$.next(!0)}_handleApduFrame(t){this._apduFrameReceiver.handleFrame(t).map(e=>e.map(i=>{this._logger.debug("Received APDU",{data:{resp:i}}),this._pendingResponseResolver.map(r=>r(f(i))),this._pendingResponseResolver=n.empty()})).mapLeft(e=>{this._pendingResponseResolver.map(i=>i(c(e))),this._pendingResponseResolver=n.empty()})}async _writeToGattCharacteristic(t){const e=this._dependencies.writeCharacteristic;if(!this._isGattConnected())throw this._markLinkUnavailable(),new o("GATT not connected");const i=typeof e.writeValueWithoutResponse=="function",r=typeof e.writeValueWithResponse=="function";if(e.properties.writeWithoutResponse&&i)try{await e.writeValueWithoutResponse(t);return}catch(s){if(this._isGattDisconnectedError(s)||!this._isGattConnected())throw this._markLinkUnavailable(),new o("Write failed")}if(e.properties.write&&r){await e.writeValueWithResponse(t);return}throw new Error("No supported write method for characteristic")}async _waitUntilMtuNegotiated(t=2e3){if(!(this._notificationsReady&&this._mtuNegotiated$.value&&this._isGattConnected()))return new Promise((e,i)=>{const r=this._mtuNegotiated$.subscribe(d=>{d&&this._notificationsReady&&this._isGattConnected()&&(clearTimeout(s),r.unsubscribe(),e())}),s=setTimeout(()=>{r.unsubscribe(),i(new h("Link not ready"))},t);this._notificationsReady&&this._mtuNegotiated$.value&&this._isGattConnected()&&(clearTimeout(s),r.unsubscribe(),e())})}}export{u as MTU_OP,w as WebBleApduSender};
2
2
  //# sourceMappingURL=WebBleApduSender.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/api/transport/WebBleApduSender.ts"],
4
- "sourcesContent": ["import {\n type ApduReceiverServiceFactory,\n type ApduResponse,\n type ApduSenderServiceFactory,\n type DeviceApduSender,\n DeviceDisconnectedWhileSendingError,\n type DmkError,\n type LoggerPublisherService,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Either, Left, Maybe, Right } from \"purify-ts\";\nimport { BehaviorSubject } from \"rxjs\";\n\nexport type WebBleApduSenderDependencies = {\n writeCharacteristic: BluetoothRemoteGATTCharacteristic;\n notifyCharacteristic: BluetoothRemoteGATTCharacteristic;\n};\n\nexport class WebBleApduSender\n implements DeviceApduSender<WebBleApduSenderDependencies>\n{\n private _characteristics: WebBleApduSenderDependencies;\n private _apduSender: Maybe<ReturnType<ApduSenderServiceFactory>> =\n Maybe.empty();\n private _apduSenderFactory: ApduSenderServiceFactory;\n private _apduReceiver: ReturnType<ApduReceiverServiceFactory>;\n private _logger: LoggerPublisherService;\n private _isDeviceReady = new BehaviorSubject<boolean>(false);\n private _sendResolver: Maybe<\n (result: Either<DmkError, ApduResponse>) => void\n > = Maybe.empty();\n private _notificationsActive = false;\n private _apduReceiverFactory: ApduReceiverServiceFactory;\n private _mtuHandshakeInFlight = false;\n private static readonly MTU_OP = 0x08; // adjust to your proto\n\n constructor(\n deps: WebBleApduSenderDependencies & {\n apduSenderFactory: ApduSenderServiceFactory;\n apduReceiverFactory: ApduReceiverServiceFactory;\n },\n loggerFactory: (tag: string) => LoggerPublisherService,\n ) {\n this._characteristics = {\n writeCharacteristic: deps.writeCharacteristic,\n notifyCharacteristic: deps.notifyCharacteristic,\n };\n this._apduSenderFactory = deps.apduSenderFactory;\n this._apduReceiverFactory = deps.apduReceiverFactory;\n this._apduReceiver = deps.apduReceiverFactory();\n this._logger = loggerFactory(\"WebBleApduSender\");\n }\n\n private _failPendingSend(err: DmkError) {\n this._sendResolver.map((r) => r(Left(err)));\n this._sendResolver = Maybe.empty();\n }\n\n private _handleNotify = (event: Event) => {\n const characteristic = event.target as BluetoothRemoteGATTCharacteristic;\n if (!characteristic.value) return;\n const data = new Uint8Array(characteristic.value.buffer);\n\n if (!this._isDeviceReady.value) {\n if (!this._mtuHandshakeInFlight) {\n this._logger.debug(\"Dropping pre-handshake frame\", { data: { data } });\n return;\n }\n if (data.length < 6 || data[0] !== WebBleApduSender.MTU_OP) {\n this._logger.debug(\"Non-MTU frame during handshake; dropping\", {\n data: { data },\n });\n return;\n }\n this._onReceiveSetup(data);\n } else {\n this._onReceiveApdu(data);\n }\n };\n\n private _onReceiveSetup(mtuResponseBuffer: Uint8Array) {\n const negotiatedMtu = mtuResponseBuffer[5]; // adjust if MTU is 16-bit in your proto\n if (typeof negotiatedMtu !== \"number\" || negotiatedMtu <= 0) {\n throw new Error(\"MTU negotiation failed: invalid MTU\");\n }\n this._apduSender = Maybe.of(\n this._apduSenderFactory({ frameSize: negotiatedMtu }),\n );\n this._isDeviceReady.next(true);\n }\n\n private _onReceiveApdu(incomingFrame: Uint8Array) {\n this._apduReceiver\n .handleFrame(incomingFrame)\n .map((respOpt) =>\n respOpt.map((resp) => {\n this._logger.debug(\"Received APDU\", { data: { resp } });\n this._sendResolver.map((r) => r(Right(resp)));\n this._sendResolver = Maybe.empty();\n }),\n )\n .mapLeft((err) => {\n this._sendResolver.map((r) => r(Left(err)));\n this._sendResolver = Maybe.empty();\n });\n }\n\n private async _write(buf: ArrayBuffer) {\n const props = this._characteristics.writeCharacteristic.properties;\n if (props.write) {\n await this._characteristics.writeCharacteristic.writeValueWithResponse(\n buf,\n );\n } else if (props.writeWithoutResponse) {\n await this._characteristics.writeCharacteristic.writeValueWithoutResponse(\n buf,\n );\n } else {\n // fallback to trying with response, then without\n try {\n await this._characteristics.writeCharacteristic.writeValueWithResponse(\n buf,\n );\n } catch {\n await this._characteristics.writeCharacteristic.writeValueWithoutResponse(\n buf,\n );\n }\n }\n }\n\n public async setupConnection(): Promise<void> {\n const notifyChar = this._characteristics.notifyCharacteristic;\n\n if (!this._notificationsActive) {\n await notifyChar.startNotifications();\n this._notificationsActive = true;\n notifyChar.addEventListener(\n \"characteristicvaluechanged\",\n this._handleNotify,\n );\n }\n\n this._mtuHandshakeInFlight = true;\n const mtuReq = new Uint8Array([WebBleApduSender.MTU_OP, 0, 0, 0, 0]);\n\n try {\n await this._write(mtuReq.buffer);\n\n await Promise.race([\n new Promise<void>((res, rej) => {\n const sub = this._isDeviceReady.subscribe({\n next: (ready) => {\n if (ready) {\n sub.unsubscribe();\n res();\n }\n },\n error: (e) => {\n sub.unsubscribe();\n rej(e);\n },\n });\n }),\n new Promise<void>((_, rej) =>\n setTimeout(() => rej(new Error(\"MTU negotiation timeout\")), 2000),\n ),\n ]);\n } catch (e) {\n try {\n notifyChar.removeEventListener(\n \"characteristicvaluechanged\",\n this._handleNotify,\n );\n await notifyChar.stopNotifications().catch(() => {});\n } finally {\n this._notificationsActive = false;\n this._isDeviceReady.next(false);\n this._apduSender = Maybe.empty();\n }\n throw e;\n } finally {\n this._mtuHandshakeInFlight = false;\n }\n }\n\n public async sendApdu(\n apdu: Uint8Array,\n _triggersDisconnection?: boolean,\n _abortTimeout?: number,\n ): Promise<Either<DmkError, ApduResponse>> {\n if (this._apduSender.isNothing()) {\n return Left(\n new DeviceDisconnectedWhileSendingError(\n \"Link not ready (no MTU / sender)\",\n ) as unknown as DmkError,\n );\n }\n\n const promise = new Promise<Either<DmkError, ApduResponse>>((resolve) => {\n this._sendResolver = Maybe.of(resolve);\n });\n\n for (const frame of this._apduSender\n .map((s) => s.getFrames(apdu))\n .orDefault([])) {\n try {\n await this._write(frame.getRawData().buffer);\n } catch (e) {\n this._logger.error(\"Frame write failed\", { data: { e } });\n this._failPendingSend(\n new DeviceDisconnectedWhileSendingError(\"Write failed\"),\n );\n break;\n }\n }\n\n return promise;\n }\n\n public closeConnection(): void {\n try {\n this._failPendingSend(\n new DeviceDisconnectedWhileSendingError(\"Connection closed\"),\n );\n\n if (this._notificationsActive) {\n this._characteristics.notifyCharacteristic.removeEventListener(\n \"characteristicvaluechanged\",\n this._handleNotify,\n );\n this._characteristics.notifyCharacteristic\n .stopNotifications()\n .catch(() => {});\n this._notificationsActive = false;\n }\n this._characteristics.notifyCharacteristic.service.device.gatt?.disconnect();\n } catch {\n this._logger.error(\"Failed to disconnect from device\");\n }\n }\n\n public getDependencies(): WebBleApduSenderDependencies {\n return this._characteristics;\n }\n\n public async setDependencies(\n deps: WebBleApduSenderDependencies,\n ): Promise<void> {\n const oldNotify = this._characteristics.notifyCharacteristic;\n\n // fail any in-flight APDU before swapping the link\n this._failPendingSend(\n new DeviceDisconnectedWhileSendingError(\"Link changed\"),\n );\n\n if (this._notificationsActive && oldNotify.service.device.gatt?.connected) {\n try {\n await oldNotify.stopNotifications();\n } catch {\n this._logger.warn(\"Failed to stop notifications on old characteristic\");\n }\n }\n oldNotify.removeEventListener(\n \"characteristicvaluechanged\",\n this._handleNotify,\n );\n this._notificationsActive = false;\n\n // Swap in new characteristics\n this._characteristics = deps;\n\n // Reset parsing/sending state across reconnect\n this._apduReceiver = this._apduReceiverFactory(); // fresh deframer\n this._isDeviceReady.next(false);\n this._apduSender = Maybe.empty();\n this._sendResolver = Maybe.empty();\n\n // Bind notifications for the new characteristic\n await deps.notifyCharacteristic.startNotifications();\n this._notificationsActive = true;\n deps.notifyCharacteristic.addEventListener(\n \"characteristicvaluechanged\",\n this._handleNotify,\n );\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["DeviceDisconnectedWhileSendingError", "Left", "Maybe", "Right", "BehaviorSubject", "WebBleApduSender", "deps", "loggerFactory", "err", "r", "event", "characteristic", "data", "mtuResponseBuffer", "negotiatedMtu", "incomingFrame", "respOpt", "resp", "buf", "props", "notifyChar", "mtuReq", "res", "rej", "sub", "ready", "e", "_", "apdu", "_triggersDisconnection", "_abortTimeout", "promise", "resolve", "frame", "oldNotify"]
4
+ "sourcesContent": ["import {\n type ApduReceiverService,\n type ApduReceiverServiceFactory,\n type ApduResponse,\n type ApduSenderService,\n type ApduSenderServiceFactory,\n type DeviceApduSender,\n DeviceDisconnectedWhileSendingError,\n DeviceNotInitializedError,\n type DmkError,\n type LoggerPublisherService,\n SendApduTimeoutError,\n} from \"@ledgerhq/device-management-kit\";\nimport { type Either, Left, Maybe, Right } from \"purify-ts\";\nimport { BehaviorSubject } from \"rxjs\";\n\nexport type WebBleApduSenderDependencies = {\n writeCharacteristic: BluetoothRemoteGATTCharacteristic;\n notifyCharacteristic: BluetoothRemoteGATTCharacteristic;\n};\n\nexport const MTU_OP = 0x08;\n\nexport class WebBleApduSender\n implements DeviceApduSender<WebBleApduSenderDependencies>\n{\n private _dependencies: WebBleApduSenderDependencies;\n private _apduFrameSegmenter: Maybe<ApduSenderService> = Maybe.empty();\n private _apduSenderFactory: ApduSenderServiceFactory;\n private _apduReceiverFactory: ApduReceiverServiceFactory;\n private _apduFrameReceiver: ApduReceiverService;\n private _logger: LoggerPublisherService;\n\n private _mtuNegotiated$ = new BehaviorSubject<boolean>(false);\n private _notificationsReady = false;\n private _mtuRequestInProgress = false;\n\n private _pendingResponseResolver: Maybe<\n (r: Either<DmkError, ApduResponse>) => void\n > = Maybe.empty();\n\n constructor(\n initialDependencies: WebBleApduSenderDependencies & {\n apduSenderFactory: ApduSenderServiceFactory;\n apduReceiverFactory: ApduReceiverServiceFactory;\n },\n loggerFactory: (tag: string) => LoggerPublisherService,\n ) {\n this._dependencies = {\n writeCharacteristic: initialDependencies.writeCharacteristic,\n notifyCharacteristic: initialDependencies.notifyCharacteristic,\n };\n this._apduSenderFactory = initialDependencies.apduSenderFactory;\n this._apduReceiverFactory = initialDependencies.apduReceiverFactory;\n this._apduFrameReceiver = initialDependencies.apduReceiverFactory();\n this._logger = loggerFactory(\"WebBleApduSender\");\n }\n\n public async sendApdu(\n apdu: Uint8Array,\n triggersDisconnection?: boolean,\n abortTimeout?: number,\n ): Promise<Either<DmkError, ApduResponse>> {\n try {\n const waitBudget = Math.max(1800, abortTimeout ?? 0);\n await this._waitUntilMtuNegotiated(waitBudget);\n } catch (e) {\n return Left(e as DmkError);\n }\n\n if (!this._isGattConnected()) {\n this._markLinkUnavailable();\n return Left(\n new DeviceDisconnectedWhileSendingError(\n \"GATT not connected\",\n ) as unknown as DmkError,\n );\n }\n\n if (this._apduFrameSegmenter.isNothing()) {\n return Left(\n new DeviceNotInitializedError(\n \"Unknown MTU / sender not ready\",\n ) as unknown as DmkError,\n );\n }\n\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n const responsePromise = new Promise<Either<DmkError, ApduResponse>>(\n (resolve) => {\n this._pendingResponseResolver = Maybe.of((result) => {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n resolve(result);\n });\n },\n );\n\n const frames = this._apduFrameSegmenter\n .map((segmenter) => segmenter.getFrames(apdu))\n .orDefault([]);\n\n for (const frame of frames) {\n try {\n await this._writeToGattCharacteristic(\n frame.getRawData().slice().buffer,\n );\n } catch (e) {\n const msg = triggersDisconnection\n ? \"Frame write failed during expected drop\"\n : \"Frame write failed\";\n this._logger[triggersDisconnection ? \"debug\" : \"error\"](msg, {\n data: { e },\n });\n this._failPendingSend(\n new DeviceDisconnectedWhileSendingError(\"Write failed\"),\n );\n break;\n }\n }\n\n if (abortTimeout) {\n timeoutHandle = setTimeout(() => {\n this._logger.debug(\"[sendApdu] Abort timeout triggered\");\n this._pendingResponseResolver.map((resolve) =>\n resolve(Left(new SendApduTimeoutError(\"Abort timeout\"))),\n );\n }, abortTimeout);\n }\n\n return responsePromise;\n }\n\n public closeConnection(): void {\n try {\n this._failPendingSend(\n new DeviceDisconnectedWhileSendingError(\"Connection closed\"),\n );\n if (this._notificationsReady) {\n this._dependencies.notifyCharacteristic.removeEventListener(\n \"characteristicvaluechanged\",\n this._handleNotification,\n );\n this._dependencies.notifyCharacteristic\n .stopNotifications()\n .catch(() => {});\n this._notificationsReady = false;\n }\n this._dependencies.notifyCharacteristic.service.device.gatt?.disconnect();\n } catch {\n this._logger.error(\"Failed to disconnect from device\");\n } finally {\n this._mtuNegotiated$.next(false);\n this._apduFrameSegmenter = Maybe.empty();\n }\n }\n\n public getDependencies(): WebBleApduSenderDependencies {\n return this._dependencies;\n }\n\n public setDependencies(deps: WebBleApduSenderDependencies): void {\n this._failPendingSend(\n new DeviceDisconnectedWhileSendingError(\"Link changed\"),\n );\n\n try {\n if (this._notificationsReady) {\n this._dependencies.notifyCharacteristic.removeEventListener(\n \"characteristicvaluechanged\",\n this._handleNotification,\n );\n this._dependencies.notifyCharacteristic\n .stopNotifications()\n .catch(() => {});\n }\n } catch {\n // ignore\n }\n\n this._notificationsReady = false;\n this._mtuNegotiated$.next(false);\n this._apduFrameSegmenter = Maybe.empty();\n this._pendingResponseResolver = Maybe.empty();\n\n this._dependencies = deps;\n this._apduFrameReceiver = this._apduReceiverFactory();\n }\n\n public async setupConnection(): Promise<void> {\n const notifyCharacteristic = this._dependencies.notifyCharacteristic;\n if (!this._notificationsReady) {\n await notifyCharacteristic.startNotifications();\n this._logger.debug(\"Notify armed\", {\n data: {\n notifyUuid: this._dependencies.notifyCharacteristic.uuid,\n writeUuid: this._dependencies.writeCharacteristic.uuid,\n props: this._dependencies.writeCharacteristic.properties,\n },\n });\n this._notificationsReady = true;\n notifyCharacteristic.addEventListener(\n \"characteristicvaluechanged\",\n this._handleNotification,\n );\n }\n\n // Avoids possible drops on the very first notification if we write immediately\n await this._sleep(120);\n\n this._mtuRequestInProgress = true;\n this._mtuNegotiated$.next(false);\n this._apduFrameSegmenter = Maybe.empty();\n\n const mtuRequestFrame = new Uint8Array([MTU_OP, 0, 0, 0, 0]);\n\n try {\n await this._writeToGattCharacteristic(mtuRequestFrame.buffer);\n\n await Promise.race([\n new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(\n () => reject(new Error(\"MTU negotiation timeout\")),\n 2000,\n );\n const sub = this._mtuNegotiated$.subscribe((ready) => {\n if (ready) {\n clearTimeout(timeout);\n sub.unsubscribe();\n resolve();\n }\n });\n }),\n this._sleep(2300).then(() => {\n if (!this._isGattConnected()) {\n throw new DeviceDisconnectedWhileSendingError(\n \"Link dropped during MTU\",\n );\n }\n }),\n ]);\n } catch (e) {\n try {\n notifyCharacteristic.removeEventListener(\n \"characteristicvaluechanged\",\n this._handleNotification,\n );\n await notifyCharacteristic.stopNotifications().catch(() => {});\n } finally {\n this._notificationsReady = false;\n this._mtuNegotiated$.next(false);\n this._apduFrameSegmenter = Maybe.empty();\n }\n throw e;\n } finally {\n this._mtuRequestInProgress = false;\n }\n }\n\n private _isGattConnected(): boolean {\n try {\n return !!this._dependencies.notifyCharacteristic.service.device.gatt\n ?.connected;\n } catch {\n return false;\n }\n }\n\n private _isGattDisconnectedError(e: unknown): boolean {\n const err = e as Error | { name?: string; message?: string };\n const name = (\n typeof err === \"object\" && err !== null && \"name\" in err\n ? (err.name ?? \"\")\n : \"\"\n ).toString();\n const msg = (\n typeof err === \"object\" && err !== null && \"message\" in err\n ? (err.message ?? \"\")\n : \"\"\n )\n .toString()\n .toLowerCase();\n return (\n name === \"NetworkError\" ||\n msg.includes(\"gatt server is disconnected\") ||\n msg.includes(\"not connected\") ||\n msg.includes(\"cannot perform gatt operations\")\n );\n }\n\n private _failPendingSend(err: DmkError) {\n this._pendingResponseResolver.map((resolve) => resolve(Left(err)));\n this._pendingResponseResolver = Maybe.empty();\n }\n\n private _markLinkUnavailable(): void {\n if (this._notificationsReady) {\n this._dependencies.notifyCharacteristic.removeEventListener(\n \"characteristicvaluechanged\",\n this._handleNotification,\n );\n this._dependencies.notifyCharacteristic\n .stopNotifications()\n .catch(() => {});\n this._notificationsReady = false;\n }\n\n this._mtuNegotiated$.next(false);\n this._apduFrameSegmenter = Maybe.empty();\n this._pendingResponseResolver = Maybe.empty();\n }\n\n private async _sleep(ms: number) {\n return new Promise((r) => setTimeout(r, ms));\n }\n\n private _handleNotification = (event: Event) => {\n const characteristic = event.target as BluetoothRemoteGATTCharacteristic;\n if (!characteristic.value) return;\n const data = new Uint8Array(characteristic.value.buffer);\n\n if (!this._mtuNegotiated$.value) {\n if (!this._mtuRequestInProgress) {\n this._logger.debug(\"Dropping pre-handshake frame\", { data: { data } });\n return;\n }\n if (data.length < 6 || data[0] !== MTU_OP) {\n this._logger.debug(\"Non-MTU frame during handshake; dropping\", {\n data: { data },\n });\n return;\n }\n this._handleMtuNegotiationFrame(data);\n return;\n }\n\n this._handleApduFrame(data);\n };\n\n private _handleMtuNegotiationFrame(mtuResponseBuffer: Uint8Array) {\n const ledgerMtu = mtuResponseBuffer[5];\n if (\n ledgerMtu === undefined ||\n !Number.isFinite(ledgerMtu) ||\n ledgerMtu <= 0\n ) {\n throw new Error(\"MTU negotiation failed: invalid MTU\");\n }\n\n const frameSize = ledgerMtu;\n this._apduFrameSegmenter = Maybe.of(this._apduSenderFactory({ frameSize }));\n this._mtuNegotiated$.next(true);\n }\n\n private _handleApduFrame(incomingFrame: Uint8Array) {\n this._apduFrameReceiver\n .handleFrame(incomingFrame)\n .map((maybeResponse) =>\n maybeResponse.map((resp) => {\n this._logger.debug(\"Received APDU\", { data: { resp } });\n this._pendingResponseResolver.map((resolve) => resolve(Right(resp)));\n this._pendingResponseResolver = Maybe.empty();\n }),\n )\n .mapLeft((err) => {\n this._pendingResponseResolver.map((resolve) => resolve(Left(err)));\n this._pendingResponseResolver = Maybe.empty();\n });\n }\n\n private async _writeToGattCharacteristic(buf: ArrayBuffer) {\n const ch = this._dependencies.writeCharacteristic;\n\n if (!this._isGattConnected()) {\n this._markLinkUnavailable();\n throw new DeviceDisconnectedWhileSendingError(\"GATT not connected\");\n }\n\n const hasWnr = typeof ch.writeValueWithoutResponse === \"function\";\n const hasWr = typeof ch.writeValueWithResponse === \"function\";\n\n // Prefer WNR for Ledger throughput\n if (ch.properties.writeWithoutResponse && hasWnr) {\n try {\n await ch.writeValueWithoutResponse(buf);\n return;\n } catch (e) {\n if (this._isGattDisconnectedError(e) || !this._isGattConnected()) {\n this._markLinkUnavailable();\n throw new DeviceDisconnectedWhileSendingError(\"Write failed\");\n }\n // otherwise, try WR\n }\n }\n\n if (ch.properties.write && hasWr) {\n await ch.writeValueWithResponse(buf);\n return;\n }\n\n throw new Error(\"No supported write method for characteristic\");\n }\n\n private async _waitUntilMtuNegotiated(maxMs = 2000): Promise<void> {\n if (\n this._notificationsReady &&\n this._mtuNegotiated$.value &&\n this._isGattConnected()\n )\n return;\n\n return new Promise<void>((resolve, reject) => {\n const subscription = this._mtuNegotiated$.subscribe((ready) => {\n if (!ready) return;\n if (this._notificationsReady && this._isGattConnected()) {\n clearTimeout(timer);\n subscription.unsubscribe();\n resolve();\n }\n });\n\n const timer = setTimeout(() => {\n subscription.unsubscribe();\n reject(new DeviceNotInitializedError(\"Link not ready\"));\n }, maxMs);\n\n if (\n this._notificationsReady &&\n this._mtuNegotiated$.value &&\n this._isGattConnected()\n ) {\n clearTimeout(timer);\n subscription.unsubscribe();\n resolve();\n }\n });\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["DeviceDisconnectedWhileSendingError", "DeviceNotInitializedError", "SendApduTimeoutError", "Left", "Maybe", "Right", "BehaviorSubject", "MTU_OP", "WebBleApduSender", "initialDependencies", "loggerFactory", "apdu", "triggersDisconnection", "abortTimeout", "waitBudget", "e", "timeoutHandle", "responsePromise", "resolve", "result", "frames", "segmenter", "frame", "msg", "deps", "notifyCharacteristic", "mtuRequestFrame", "reject", "timeout", "sub", "ready", "err", "name", "ms", "r", "event", "characteristic", "data", "mtuResponseBuffer", "ledgerMtu", "frameSize", "incomingFrame", "maybeResponse", "resp", "buf", "ch", "hasWnr", "hasWr", "maxMs", "subscription", "timer"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{Maybe as A,Right as R}from"purify-ts";import{afterEach as E,beforeEach as k,describe as B,expect as n,it as o,vi as t}from"vitest";import{WebBleApduSender as V}from"./WebBleApduSender";class L{subscribers=[];tag;constructor(r,i){this.subscribers=r,this.tag=i}debug=t.fn();info=t.fn();warn=t.fn();error=t.fn()}function d(){const e=t.fn(),r=t.fn().mockResolvedValue(void 0);return{service:{device:{gatt:{connected:!0,disconnect:e}}},uuid:"mock-uuid",properties:{broadcast:!1,read:!1,writeWithoutResponse:!1,write:!1,notify:!1,indicate:!1,authenticatedSignedWrites:!1,reliableWrite:!1,writableAuxiliaries:!1},getDescriptor:t.fn().mockResolvedValue(void 0),getDescriptors:t.fn().mockResolvedValue([]),startNotifications:t.fn().mockResolvedValue(void 0),addEventListener:t.fn(),removeEventListener:t.fn(),stopNotifications:r,writeValueWithResponse:t.fn().mockResolvedValue(void 0),writeValueWithoutResponse:t.fn().mockResolvedValue(void 0),readValue:t.fn().mockResolvedValue(new DataView(new ArrayBuffer(0))),writeValue:t.fn().mockResolvedValue(void 0),dispatchEvent:t.fn(),oncharacteristicvaluechanged:null}}let u,a,g,m,y,s;const h=()=>new Promise(e=>setImmediate(e));k(()=>{u=d(),a=d(),g=t.fn().mockReturnValue({getFrames:e=>[{getRawData:()=>new DataView(e.buffer)}]}),m=t.fn().mockReturnValue({handleFrame:t.fn(e=>R(A.of({data:new Uint8Array([144,0])})))}),y=e=>new L([],e),s=new V({writeCharacteristic:u,notifyCharacteristic:a,apduSenderFactory:g,apduReceiverFactory:m},y)});E(()=>{t.restoreAllMocks()});B("WebBleApduSender",()=>{o("getDependencies returns initial chars",()=>{const e=s.getDependencies();n(e.writeCharacteristic).toBe(u),n(e.notifyCharacteristic).toBe(a)}),o("setupConnection negotiates MTU and listens",async()=>{const e=s.setupConnection();n(a.startNotifications).toHaveBeenCalled(),await h();const i=a.addEventListener.mock.calls.filter(([l])=>l==="characteristicvaluechanged")[0];if(!i)throw new Error("No event registered for 'characteristicvaluechanged'");const c=i[1],f=new Uint8Array([0,0,0,0,0,32]).buffer;c({target:{value:{buffer:f}}}),await n(e).resolves.toBeUndefined()}),o("sendApdu writes frames and resolves on notification",async()=>{const e=s.setupConnection();await h();const r=a.addEventListener.mock.calls.find(([v])=>v==="characteristicvaluechanged");if(!r)throw new Error("No event registered for 'characteristicvaluechanged'");const i=r[1];i({target:{value:{buffer:new Uint8Array([0,0,0,0,0,32]).buffer}}}),await e;const c=new Uint8Array([1,2,3]),f=s.sendApdu(c);n(u.writeValueWithResponse).toHaveBeenCalledWith(c.buffer),await h();const l=a.addEventListener.mock.calls.filter(([v])=>v==="characteristicvaluechanged"),p=l[l.length-1];if(!p)throw new Error("No APDU handler registered.");const[,C]=p,b=new Uint8Array([144,0]).buffer;C({target:{value:{buffer:b}}});const w=await f;n(w.isRight()).toBe(!0),n(w.extract().data).toEqual(new Uint8Array([144,0]))}),o("closeConnection calls disconnect",()=>{s.closeConnection(),n(a.service.device.gatt.disconnect).toHaveBeenCalled()}),o("setDependencies swaps characteristics",async()=>{const e=d(),r=d();a.service.device.gatt.connected=!0,await s.setDependencies({writeCharacteristic:r,notifyCharacteristic:e}),n(a.removeEventListener).toHaveBeenCalled(),n(e.startNotifications).toHaveBeenCalled(),n(e.addEventListener).toHaveBeenCalledWith("characteristicvaluechanged",n.any(Function));const i=s.getDependencies();n(i.notifyCharacteristic).toBe(e),n(i.writeCharacteristic).toBe(r)})});
1
+ import{Maybe as B,Right as L}from"purify-ts";import{afterEach as V,beforeEach as x,describe as S,expect as n,it as u,vi as t}from"vitest";import{MTU_OP as h,WebBleApduSender as U}from"./WebBleApduSender";class W{subscribers=[];tag;constructor(r,o){this.subscribers=r,this.tag=o}debug=t.fn();info=t.fn();warn=t.fn();error=t.fn()}function p(){const e=t.fn(),r=t.fn().mockResolvedValue(void 0);return{service:{device:{gatt:{connected:!0,disconnect:e}}},uuid:"mock-uuid",properties:{broadcast:!1,read:!1,writeWithoutResponse:!1,write:!1,notify:!1,indicate:!1,authenticatedSignedWrites:!1,reliableWrite:!1,writableAuxiliaries:!1},getDescriptor:t.fn().mockResolvedValue(void 0),getDescriptors:t.fn().mockResolvedValue([]),startNotifications:t.fn().mockResolvedValue(void 0),addEventListener:t.fn(),removeEventListener:t.fn(),stopNotifications:r,writeValueWithResponse:t.fn().mockResolvedValue(void 0),writeValueWithoutResponse:t.fn().mockResolvedValue(void 0),readValue:t.fn().mockResolvedValue(new DataView(new ArrayBuffer(0))),writeValue:t.fn().mockResolvedValue(void 0),dispatchEvent:t.fn(),oncharacteristicvaluechanged:null}}let d,a,C,b,E,s;const v=()=>new Promise(e=>setImmediate(e)),m=e=>new Promise(r=>setTimeout(r,e));x(()=>{d=p(),a=p(),d.properties.write=!0,C=t.fn().mockReturnValue({getFrames:e=>[{getRawData:()=>e}]}),b=t.fn().mockReturnValue({handleFrame:t.fn(e=>L(B.of({data:new Uint8Array([144,0])})))}),E=e=>new W([],e),s=new U({writeCharacteristic:d,notifyCharacteristic:a,apduSenderFactory:C,apduReceiverFactory:b},E)});V(()=>{t.restoreAllMocks()});S("WebBleApduSender",()=>{u("getDependencies returns initial chars",()=>{const e=s.getDependencies();n(e.writeCharacteristic).toBe(d),n(e.notifyCharacteristic).toBe(a)}),u("setupConnection negotiates MTU and listens",async()=>{const e=s.setupConnection();n(a.startNotifications).toHaveBeenCalled(),await v();const o=a.addEventListener.mock.calls.filter(([l])=>l==="characteristicvaluechanged")[0];if(!o)throw new Error("No event registered for 'characteristicvaluechanged'");const i=o[1];await m(150);const c=new Uint8Array([h,0,0,0,0,32]).buffer;i({target:{value:{buffer:c}}}),await n(e).resolves.toBeUndefined()}),u("sendApdu writes frames and resolves on notification",async()=>{const e=s.setupConnection();await v();const r=a.addEventListener.mock.calls.find(([w])=>w==="characteristicvaluechanged");if(!r)throw new Error("No event registered for 'characteristicvaluechanged'");const o=r[1];await m(150),o({target:{value:{buffer:new Uint8Array([h,0,0,0,0,32]).buffer}}}),await e;const i=new Uint8Array([1,2,3]),c=s.sendApdu(i);await v(),n(d.writeValueWithResponse).toHaveBeenCalled();const l=d.writeValueWithResponse.mock.calls.at(-1)?.[0];n(l).toBeInstanceOf(ArrayBuffer),n(Array.from(new Uint8Array(l))).toEqual(Array.from(i));const f=a.addEventListener.mock.calls.filter(([w])=>w==="characteristicvaluechanged"),g=f[f.length-1];if(!g)throw new Error("No APDU handler registered.");const[,R]=g,k=new Uint8Array([144,0]).buffer;R({target:{value:{buffer:k}}});const y=await c;n(y.isRight()).toBe(!0);const A=y.extract();n(A).toHaveProperty("data"),n(A.data).toEqual(new Uint8Array([144,0]))}),u("closeConnection calls disconnect",()=>{s.closeConnection(),n(a.service.device.gatt.disconnect).toHaveBeenCalled()}),u("setDependencies swaps characteristics and resets link (does not arm)",async()=>{const e=s.setupConnection();await v();const r=a.addEventListener.mock.calls.find(([f])=>f==="characteristicvaluechanged");if(!r)throw new Error("No event registered for 'characteristicvaluechanged'");const o=r[1];await m(150),o({target:{value:{buffer:new Uint8Array([h,0,0,0,0,32]).buffer}}}),await e;const i=p(),c=p();c.properties.write=!0,s.setDependencies({writeCharacteristic:c,notifyCharacteristic:i}),n(a.removeEventListener).toHaveBeenCalledWith("characteristicvaluechanged",n.any(Function)),n(i.startNotifications).not.toHaveBeenCalled(),n(i.addEventListener).not.toHaveBeenCalled();const l=s.getDependencies();n(l.notifyCharacteristic).toBe(i),n(l.writeCharacteristic).toBe(c)})});
2
2
  //# sourceMappingURL=WebBleApduSender.test.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/api/transport/WebBleApduSender.test.ts"],
4
- "sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n type ApduReceiverServiceFactory,\n type ApduResponse,\n type ApduSenderServiceFactory,\n type LoggerPublisherService,\n} from \"@ledgerhq/device-management-kit\";\nimport { Maybe, Right } from \"purify-ts\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\n\nimport { WebBleApduSender } from \"./WebBleApduSender\";\n\ntype EventListener = (event: Event) => void;\nclass LoggerStub implements LoggerPublisherService {\n subscribers: any[] = [];\n tag: string;\n constructor(subs: any[], tag: string) {\n this.subscribers = subs;\n this.tag = tag;\n }\n debug = vi.fn();\n info = vi.fn();\n warn = vi.fn();\n error = vi.fn();\n}\n\nfunction makeCharacteristic() {\n const disconnect = vi.fn();\n const stopNotifications = vi.fn().mockResolvedValue(undefined);\n return {\n service: { device: { gatt: { connected: true, disconnect } } },\n uuid: \"mock-uuid\",\n properties: {\n broadcast: false,\n read: false,\n writeWithoutResponse: false,\n write: false,\n notify: false,\n indicate: false,\n authenticatedSignedWrites: false,\n reliableWrite: false,\n writableAuxiliaries: false,\n },\n getDescriptor: vi.fn().mockResolvedValue(undefined),\n getDescriptors: vi.fn().mockResolvedValue([]),\n startNotifications: vi.fn().mockResolvedValue(undefined),\n addEventListener: vi.fn(),\n removeEventListener: vi.fn(),\n stopNotifications,\n writeValueWithResponse: vi.fn().mockResolvedValue(undefined),\n writeValueWithoutResponse: vi.fn().mockResolvedValue(undefined),\n readValue: vi.fn().mockResolvedValue(new DataView(new ArrayBuffer(0))),\n writeValue: vi.fn().mockResolvedValue(undefined),\n dispatchEvent: vi.fn(),\n oncharacteristicvaluechanged: null,\n };\n}\n\nlet writeChar: any;\nlet notifyChar: any;\nlet apduSenderFactory: ApduSenderServiceFactory;\nlet apduReceiverFactory: ApduReceiverServiceFactory;\nlet loggerFactory: (tag: string) => LoggerPublisherService;\nlet sender: WebBleApduSender;\n\nconst flushPromises = () =>\n new Promise<void>((resolve) => setImmediate(resolve));\n\nbeforeEach(() => {\n writeChar = makeCharacteristic();\n notifyChar = makeCharacteristic();\n\n apduSenderFactory = vi.fn().mockReturnValue({\n getFrames: (apdu: Uint8Array) => [\n { getRawData: () => new DataView(apdu.buffer) },\n ],\n });\n\n apduReceiverFactory = vi.fn().mockReturnValue({\n handleFrame: vi.fn((_frame: Uint8Array) =>\n Right(Maybe.of({ data: new Uint8Array([0x90, 0x00]) } as ApduResponse)),\n ),\n });\n\n loggerFactory = (tag: string) => new LoggerStub([], tag);\n\n sender = new WebBleApduSender(\n {\n writeCharacteristic: writeChar,\n notifyCharacteristic: notifyChar,\n apduSenderFactory,\n apduReceiverFactory,\n },\n loggerFactory,\n );\n});\n\nafterEach(() => {\n vi.restoreAllMocks();\n});\n\ndescribe(\"WebBleApduSender\", () => {\n it(\"getDependencies returns initial chars\", () => {\n // when\n const deps = sender.getDependencies();\n\n // then\n expect(deps.writeCharacteristic).toBe(writeChar);\n expect(deps.notifyCharacteristic).toBe(notifyChar);\n });\n\n it(\"setupConnection negotiates MTU and listens\", async () => {\n // when\n const promise = sender.setupConnection();\n\n // then\n expect(notifyChar.startNotifications).toHaveBeenCalled();\n await flushPromises();\n\n const filteredCalls = (\n notifyChar.addEventListener.mock.calls as [string, EventListener][]\n ).filter(([event]) => event === \"characteristicvaluechanged\");\n\n const firstCall = filteredCalls[0];\n\n if (!firstCall) {\n throw new Error(\"No event registered for 'characteristicvaluechanged'\");\n }\n\n const handler = firstCall[1];\n\n // given\n const mtuBuf = new Uint8Array([0, 0, 0, 0, 0, 0x20]).buffer;\n\n // when\n handler({ target: { value: { buffer: mtuBuf } } } as unknown as Event);\n\n // then\n await expect(promise).resolves.toBeUndefined();\n });\n\n it(\"sendApdu writes frames and resolves on notification\", async () => {\n // given\n const setupPromise = sender.setupConnection();\n await flushPromises();\n\n const mtuCall = (\n notifyChar.addEventListener.mock.calls as [string, EventListener][]\n ).find(([event]) => event === \"characteristicvaluechanged\");\n\n if (!mtuCall)\n throw new Error(\"No event registered for 'characteristicvaluechanged'\");\n\n const mtuHandler = mtuCall[1];\n\n mtuHandler({\n target: {\n value: { buffer: new Uint8Array([0, 0, 0, 0, 0, 0x20]).buffer },\n },\n } as unknown as Event);\n\n await setupPromise;\n\n const apduCmd = new Uint8Array([0x01, 0x02, 0x03]);\n\n // when\n const promise = sender.sendApdu(apduCmd);\n\n // then\n expect(writeChar.writeValueWithResponse).toHaveBeenCalledWith(\n apduCmd.buffer,\n );\n await flushPromises();\n\n const filteredCalls = (\n notifyChar.addEventListener.mock.calls as [string, EventListener][]\n ).filter(([event]) => event === \"characteristicvaluechanged\");\n\n const lastCall = filteredCalls[filteredCalls.length - 1];\n if (!lastCall) throw new Error(\"No APDU handler registered.\");\n\n const [, apduHandler] = lastCall;\n\n // when\n const respBuf = new Uint8Array([0x90, 0x00]).buffer;\n apduHandler({ target: { value: { buffer: respBuf } } } as unknown as Event);\n\n // then\n const result = await promise;\n\n expect(result.isRight()).toBe(true);\n expect((result.extract() as ApduResponse).data).toEqual(\n new Uint8Array([0x90, 0x00]),\n );\n });\n\n it(\"closeConnection calls disconnect\", () => {\n // when\n sender.closeConnection();\n\n // then\n expect(notifyChar.service.device.gatt.disconnect).toHaveBeenCalled();\n });\n\n it(\"setDependencies swaps characteristics\", async () => {\n // given\n const newNotify = makeCharacteristic();\n const newWrite = makeCharacteristic();\n notifyChar.service.device.gatt.connected = true;\n\n // when\n await sender.setDependencies({\n writeCharacteristic:\n newWrite as unknown as BluetoothRemoteGATTCharacteristic,\n notifyCharacteristic:\n newNotify as unknown as BluetoothRemoteGATTCharacteristic,\n });\n\n // then\n expect(notifyChar.removeEventListener).toHaveBeenCalled();\n expect(newNotify.startNotifications).toHaveBeenCalled();\n expect(newNotify.addEventListener).toHaveBeenCalledWith(\n \"characteristicvaluechanged\",\n expect.any(Function),\n );\n\n const deps = sender.getDependencies();\n expect(deps.notifyCharacteristic).toBe(newNotify);\n expect(deps.writeCharacteristic).toBe(newWrite);\n });\n});\n"],
5
- "mappings": "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",
6
- "names": ["Maybe", "Right", "afterEach", "beforeEach", "describe", "expect", "it", "vi", "WebBleApduSender", "LoggerStub", "subs", "tag", "makeCharacteristic", "disconnect", "stopNotifications", "writeChar", "notifyChar", "apduSenderFactory", "apduReceiverFactory", "loggerFactory", "sender", "flushPromises", "resolve", "apdu", "_frame", "deps", "promise", "firstCall", "event", "handler", "mtuBuf", "setupPromise", "mtuCall", "mtuHandler", "apduCmd", "filteredCalls", "lastCall", "apduHandler", "respBuf", "result", "newNotify", "newWrite"]
4
+ "sourcesContent": ["/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n type ApduReceiverServiceFactory,\n type ApduResponse,\n type ApduSenderServiceFactory,\n type LoggerPublisherService,\n} from \"@ledgerhq/device-management-kit\";\nimport { Maybe, Right } from \"purify-ts\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\n\nimport { MTU_OP, WebBleApduSender } from \"./WebBleApduSender\";\n\ntype EventListener = (event: Event) => void;\nclass LoggerStub implements LoggerPublisherService {\n subscribers: any[] = [];\n tag: string;\n constructor(subs: any[], tag: string) {\n this.subscribers = subs;\n this.tag = tag;\n }\n debug = vi.fn();\n info = vi.fn();\n warn = vi.fn();\n error = vi.fn();\n}\n\nfunction makeCharacteristic() {\n const disconnect = vi.fn();\n const stopNotifications = vi.fn().mockResolvedValue(undefined);\n return {\n service: { device: { gatt: { connected: true, disconnect } } },\n uuid: \"mock-uuid\",\n properties: {\n broadcast: false,\n read: false,\n writeWithoutResponse: false,\n write: false,\n notify: false,\n indicate: false,\n authenticatedSignedWrites: false,\n reliableWrite: false,\n writableAuxiliaries: false,\n },\n getDescriptor: vi.fn().mockResolvedValue(undefined),\n getDescriptors: vi.fn().mockResolvedValue([]),\n startNotifications: vi.fn().mockResolvedValue(undefined),\n addEventListener: vi.fn(),\n removeEventListener: vi.fn(),\n stopNotifications,\n writeValueWithResponse: vi.fn().mockResolvedValue(undefined),\n writeValueWithoutResponse: vi.fn().mockResolvedValue(undefined),\n readValue: vi.fn().mockResolvedValue(new DataView(new ArrayBuffer(0))),\n writeValue: vi.fn().mockResolvedValue(undefined),\n dispatchEvent: vi.fn(),\n oncharacteristicvaluechanged: null,\n };\n}\n\nlet writeChar: any;\nlet notifyChar: any;\nlet apduSenderFactory: ApduSenderServiceFactory;\nlet apduReceiverFactory: ApduReceiverServiceFactory;\nlet loggerFactory: (tag: string) => LoggerPublisherService;\nlet sender: WebBleApduSender;\n\nconst flushPromises = () =>\n new Promise<void>((resolve) => setImmediate(resolve));\nconst wait = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\nbeforeEach(() => {\n writeChar = makeCharacteristic();\n notifyChar = makeCharacteristic();\n\n // it requires the characteristic to advertise \"write\" or \"writeWithoutResponse\" to pick a write mode\n writeChar.properties.write = true;\n\n // class calls getRawData().slice().buffer, return a Uint8Array\n apduSenderFactory = vi.fn().mockReturnValue({\n getFrames: (apdu: Uint8Array) => [{ getRawData: () => apdu }],\n });\n\n apduReceiverFactory = vi.fn().mockReturnValue({\n handleFrame: vi.fn((_frame: Uint8Array) =>\n Right(Maybe.of({ data: new Uint8Array([0x90, 0x00]) } as ApduResponse)),\n ),\n });\n\n loggerFactory = (tag: string) => new LoggerStub([], tag);\n\n sender = new WebBleApduSender(\n {\n writeCharacteristic: writeChar,\n notifyCharacteristic: notifyChar,\n apduSenderFactory,\n apduReceiverFactory,\n },\n loggerFactory,\n );\n});\n\nafterEach(() => {\n vi.restoreAllMocks();\n});\n\ndescribe(\"WebBleApduSender\", () => {\n it(\"getDependencies returns initial chars\", () => {\n const deps = sender.getDependencies();\n expect(deps.writeCharacteristic).toBe(writeChar);\n expect(deps.notifyCharacteristic).toBe(notifyChar);\n });\n\n it(\"setupConnection negotiates MTU and listens\", async () => {\n // when\n const promise = sender.setupConnection();\n\n // then\n expect(notifyChar.startNotifications).toHaveBeenCalled();\n await flushPromises();\n\n const filteredCalls = (\n notifyChar.addEventListener.mock.calls as [string, EventListener][]\n ).filter(([event]) => event === \"characteristicvaluechanged\");\n\n const firstCall = filteredCalls[0];\n if (!firstCall) {\n throw new Error(\"No event registered for 'characteristicvaluechanged'\");\n }\n const handler = firstCall[1];\n\n // wait for the internal sleep so the handshake flag is set\n await wait(150);\n\n const mtuBuf = new Uint8Array([MTU_OP, 0, 0, 0, 0, 0x20]).buffer;\n\n handler({ target: { value: { buffer: mtuBuf } } } as unknown as Event);\n\n await expect(promise).resolves.toBeUndefined();\n });\n\n it(\"sendApdu writes frames and resolves on notification\", async () => {\n // given\n const setupPromise = sender.setupConnection();\n await flushPromises();\n\n const mtuCall = (\n notifyChar.addEventListener.mock.calls as [string, EventListener][]\n ).find(([event]) => event === \"characteristicvaluechanged\");\n if (!mtuCall)\n throw new Error(\"No event registered for 'characteristicvaluechanged'\");\n\n const mtuHandler = mtuCall[1];\n\n // wait past the handshake delay, then send the proper MTU frame\n await wait(150);\n mtuHandler({\n target: {\n value: { buffer: new Uint8Array([MTU_OP, 0, 0, 0, 0, 0x20]).buffer },\n },\n } as unknown as Event);\n\n await setupPromise;\n\n const apduCmd = new Uint8Array([0x01, 0x02, 0x03]);\n\n // when\n const promise = sender.sendApdu(apduCmd);\n\n // then check that the last write matches the APDU bytes\n await flushPromises();\n expect(writeChar.writeValueWithResponse).toHaveBeenCalled();\n\n const lastArg = writeChar.writeValueWithResponse.mock.calls.at(\n -1,\n )?.[0] as ArrayBuffer;\n expect(lastArg).toBeInstanceOf(ArrayBuffer);\n expect(Array.from(new Uint8Array(lastArg))).toEqual(Array.from(apduCmd));\n\n const filteredCalls = (\n notifyChar.addEventListener.mock.calls as [string, EventListener][]\n ).filter(([event]) => event === \"characteristicvaluechanged\");\n const lastCall = filteredCalls[filteredCalls.length - 1];\n if (!lastCall) throw new Error(\"No APDU handler registered.\");\n\n const [, apduHandler] = lastCall;\n\n // when\n const respBuf = new Uint8Array([0x90, 0x00]).buffer;\n apduHandler({ target: { value: { buffer: respBuf } } } as unknown as Event);\n\n // then\n const result = await promise;\n expect(result.isRight()).toBe(true);\n const extractedResult = result.extract();\n expect(extractedResult).toHaveProperty(\"data\");\n expect((extractedResult as ApduResponse).data).toEqual(\n new Uint8Array([0x90, 0x00]),\n );\n });\n\n it(\"closeConnection calls disconnect\", () => {\n sender.closeConnection();\n expect(notifyChar.service.device.gatt.disconnect).toHaveBeenCalled();\n });\n\n it(\"setDependencies swaps characteristics and resets link (does not arm)\", async () => {\n // given\n const setupPromise = sender.setupConnection();\n await flushPromises();\n\n const mtuCall = (\n notifyChar.addEventListener.mock.calls as [string, EventListener][]\n ).find(([event]) => event === \"characteristicvaluechanged\");\n if (!mtuCall)\n throw new Error(\"No event registered for 'characteristicvaluechanged'\");\n const mtuHandler = mtuCall[1];\n\n await wait(150);\n mtuHandler({\n target: {\n value: { buffer: new Uint8Array([MTU_OP, 0, 0, 0, 0, 0x20]).buffer },\n },\n } as unknown as Event);\n await setupPromise;\n\n const newNotify = makeCharacteristic();\n const newWrite = makeCharacteristic();\n newWrite.properties.write = true;\n\n // when\n sender.setDependencies({\n writeCharacteristic:\n newWrite as unknown as BluetoothRemoteGATTCharacteristic,\n notifyCharacteristic:\n newNotify as unknown as BluetoothRemoteGATTCharacteristic,\n });\n\n // then\n expect(notifyChar.removeEventListener).toHaveBeenCalledWith(\n \"characteristicvaluechanged\",\n expect.any(Function),\n );\n expect(newNotify.startNotifications).not.toHaveBeenCalled();\n expect(newNotify.addEventListener).not.toHaveBeenCalled();\n\n const deps = sender.getDependencies();\n expect(deps.notifyCharacteristic).toBe(newNotify);\n expect(deps.writeCharacteristic).toBe(newWrite);\n });\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["Maybe", "Right", "afterEach", "beforeEach", "describe", "expect", "it", "vi", "MTU_OP", "WebBleApduSender", "LoggerStub", "subs", "tag", "makeCharacteristic", "disconnect", "stopNotifications", "writeChar", "notifyChar", "apduSenderFactory", "apduReceiverFactory", "loggerFactory", "sender", "flushPromises", "resolve", "wait", "ms", "apdu", "_frame", "deps", "promise", "firstCall", "event", "handler", "mtuBuf", "setupPromise", "mtuCall", "mtuHandler", "apduCmd", "lastArg", "filteredCalls", "lastCall", "apduHandler", "respBuf", "result", "extractedResult", "newNotify", "newWrite"]
7
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 D,DeviceConnectionStateMachine as f,GeneralDmkError as m,OpeningConnectionError as l,TransportConnectedDevice as S,UnknownDeviceError as h}from"@ledgerhq/device-management-kit";import{Left as g,Right as p}from"purify-ts";import{BehaviorSubject as w,from as _}from"rxjs";import{switchMap as T}from"rxjs/operators";import{ADVERTISING_DELAY as I,ADVERTISING_TIMEOUT as C,RECONNECT_DEVICE_TIMEOUT as b,RECONNECTION_LOOP_BACKOFF as E,REDISCOVER_TIMEOUT as B}from"../data/WebBleConfig";import{WebBleApduSender as M}from"./WebBleApduSender";const u="WEB-BLE-RN-STYLE";class R{constructor(e,i,t,n){this._deviceModelDataSource=e;this.loggerFactory=i;this._apduSenderFactory=t;this._apduReceiverFactory=n;this._logger=i("WebBleTransportRnStyle")}_logger;_connectionStateMachinesByDeviceId=new Map;_deviceRegistryById=new Map;_discoveredDevices$=new w([]);isSupported(){return typeof navigator<"u"&&!!navigator.bluetooth}getIdentifier(){return u}startDiscovering(){const e=this._deviceModelDataSource.getBluetoothServices().map(t=>({services:[t]})),i=this._deviceModelDataSource.getBluetoothServices();return _(navigator.bluetooth.requestDevice({filters:e,optionalServices:i})).pipe(T(async t=>{const{serviceUuid:n,ledgerServiceInfo:r}=await this._identifyLedgerGattService(t),c={id:t.id,deviceModel:r.deviceModel,transport:u,name:t.name||void 0};return this._deviceRegistryById.set(t.id,{device:t,serviceUuid:n,ledgerServiceInfo:r,discoveredDevice:c}),this._publishDiscoveredDevices(),c}))}stopDiscovering(){}listenToAvailableDevices(){return this._publishDiscoveredDevices(),this._discoveredDevices$.asObservable()}async connect(e){const i=this._deviceRegistryById.get(e.deviceId);if(!i)return g(new h(`Unknown device ${e.deviceId}`));if(this._connectionStateMachinesByDeviceId.has(e.deviceId))return g(new D(`Device ${e.deviceId} already connected`));try{const t=i.device;if(!t.gatt)throw new l("No GATT server available on device");t.gatt.connected||(await this._withTimeout(t.gatt.connect(),6e3,"GATT connect timed out"),await this._sleep(150));const{service:n,ledgerServiceInfo:r}=await this._getPrimaryLedgerGattService(t),{writeCharacteristic:c,notifyCharacteristic:d}=await this._resolveLedgerServiceCharacteristics(n,r),o=new M({writeCharacteristic:c,notifyCharacteristic:d,apduSenderFactory:this._apduSenderFactory,apduReceiverFactory:this._apduReceiverFactory},this.loggerFactory),v=new f({deviceId:e.deviceId,deviceApduSender:o,timeoutDuration:b,tryToReconnect:()=>{this._tryToReconnect(e.deviceId,e.onReconnect).catch(a=>this._logger.error("tryToReconnect() threw",{data:{e:a}}))},onTerminated:()=>{try{this._connectionStateMachinesByDeviceId.get(e.deviceId)?.closeConnection(),e.onDisconnect(e.deviceId)}finally{this._connectionStateMachinesByDeviceId.delete(e.deviceId);const a=this._deviceRegistryById.get(e.deviceId);a?.gattDisconnectListener&&(a.device.removeEventListener("gattserverdisconnected",a.gattDisconnectListener),a.gattDisconnectListener=void 0),this._publishDiscoveredDevices()}}});await o.setupConnection(),this._connectionStateMachinesByDeviceId.set(e.deviceId,v),i.serviceUuid=n.uuid,i.ledgerServiceInfo=r;const s=a=>this._handleGattServerDisconnected(e.deviceId);return i.gattDisconnectListener=s,t.addEventListener("gattserverdisconnected",s),this._publishDiscoveredDevices(),p(new S({id:e.deviceId,deviceModel:r.deviceModel,type:"BLE",transport:u,sendApdu:(...a)=>v.sendApdu(...a),name:t.name||void 0}))}catch(t){return this._logger.error("connect() error",{data:{e:t}}),g(new l(t))}}async disconnect(e){const i=e.connectedDevice.id,t=this._connectionStateMachinesByDeviceId.get(i),n=this._deviceRegistryById.get(i);if(!t)return g(new h(`Unknown device ${i}`));try{return t.closeConnection(),this._connectionStateMachinesByDeviceId.delete(i),n?.gattDisconnectListener&&(n.device.removeEventListener("gattserverdisconnected",n.gattDisconnectListener),n.gattDisconnectListener=void 0),await this._safelyCancelGattConnection(i),this._publishDiscoveredDevices(),p(void 0)}catch(r){return g(new m({originalError:r}))}}_handleGattServerDisconnected(e){this._logger.debug(`[${e}] gattserverdisconnected`);const i=this._connectionStateMachinesByDeviceId.get(e);i&&i.eventDeviceDisconnected()}async _waitForAdvertisementInRange(e,i={}){const{startTimeoutMs:t=500,advTimeoutMs:n=1500}=i;if(typeof e.watchAdvertisements!="function")return!1;const r=new AbortController,c=setTimeout(()=>r.abort(),t),d=await e.watchAdvertisements({signal:r.signal}).then(()=>!0).catch(()=>!1);if(clearTimeout(c),!d)return!1;const o=await new Promise(v=>{const s=()=>{e.removeEventListener("advertisementreceived",s),v(!0)};e.addEventListener("advertisementreceived",s),setTimeout(()=>{e.removeEventListener("advertisementreceived",s),v(!1)},n)});return r.abort(),o}async _rediscoverPermittedDevice(e){if(typeof navigator.bluetooth.getDevices!="function")return null;try{this._logger.debug(`Attempting to rediscover device ${e}`);const t=(await navigator.bluetooth.getDevices()).find(n=>n.id===e)??null;if(t){const n=await this._waitForAdvertisementInRange(t,{startTimeoutMs:I,advTimeoutMs:C});this._logger.debug(`Rediscovered ${t.id}, inRange=${n}`)}return t}catch{return null}}async _tryToReconnect(e,i){for(await this._safelyCancelGattConnection(e);;){const t=this._deviceRegistryById.get(e),n=this._connectionStateMachinesByDeviceId.get(e);if(!t||!n){this._logger.debug(`[${e}] aborting reconnect: registry or state machine missing`);return}try{const r=await this._withTimeout(this._rediscoverPermittedDevice(e),B,"rediscovery timeout");if(!r)throw new Error("Device not found");if(!r.gatt)throw new Error("No GATT on device");try{await r.gatt.connect()}catch(a){this._logger.error(`[${e}] gatt.connect() failed`,{data:{e:a}}),r.gatt.connected&&r.gatt.disconnect()}const{service:c,ledgerServiceInfo:d}=await this._getPrimaryLedgerGattService(r),{writeCharacteristic:o,notifyCharacteristic:v}=await this._resolveLedgerServiceCharacteristics(c,d);n.setDependencies({writeCharacteristic:o,notifyCharacteristic:v}),await n.setupConnection(),n.eventDeviceConnected(),t.serviceUuid=c.uuid,t.ledgerServiceInfo=d,t.gattDisconnectListener&&r.removeEventListener("gattserverdisconnected",t.gattDisconnectListener);const s=a=>this._handleGattServerDisconnected(e);r.addEventListener("gattserverdisconnected",s),t.gattDisconnectListener=s,await i?.(e),this._publishDiscoveredDevices();return}catch(r){this._logger.error(`[${e}] reconnect attempt failed`,{data:{e:r}}),t?.device.gatt?.connected&&t.device.gatt.disconnect(),await this._sleep(E);continue}}}async _safelyCancelGattConnection(e){const i=this._deviceRegistryById.get(e);i&&(i.device.gatt?.connected&&i.device.gatt.disconnect(),await this._sleep(100))}async _identifyLedgerGattService(e){if(!e.gatt)throw new l("No GATT server available on device");try{await this._withTimeout(e.gatt.connect(),6e3,"connect timeout");const{service:i,ledgerServiceInfo:t}=await this._getPrimaryLedgerGattService(e);return{serviceUuid:i.uuid,ledgerServiceInfo:t}}finally{e.gatt?.disconnect(),await this._sleep(200)}}async _getPrimaryLedgerGattService(e){const i=this._deviceModelDataSource.getBluetoothServices(),t=this._deviceRegistryById.get(e.id)?.serviceUuid,n=t?[t,...i.filter(r=>r!==t)]:i.slice();for(const r of n)try{const c=await e.gatt.getPrimaryService(r),o=this._deviceModelDataSource.getBluetoothServicesInfos()[c.uuid];if(!o)throw new h(e.name||"");return{service:c,ledgerServiceInfo:o}}catch(c){if(c?.name==="SecurityError")throw new l(`Missing Web Bluetooth permission for service ${r}. Add it to optionalServices in requestDevice().`);try{const o=(await e.gatt.getPrimaryServices()).find(v=>v.uuid.toLowerCase()===r.toLowerCase());if(o){const s=this._deviceModelDataSource.getBluetoothServicesInfos()[o.uuid];if(!s)throw new h(e.name||"");return{service:o,ledgerServiceInfo:s}}}catch{this._logger.error("Failed to get primary services",{data:{deviceId:e.id}})}}throw new l("Ledger GATT service not found")}async _resolveLedgerServiceCharacteristics(e,i){const t=await e.getCharacteristic(i.notifyUuid);return{writeCharacteristic:await this._findWritableCharacteristic(e,i),notifyCharacteristic:t}}async _findWritableCharacteristic(e,i){const t=[i.writeCmdUuid,i.writeUuid].filter(Boolean),n=[];for(const r of t)try{const c=await e.getCharacteristic(r);if(n.push(c),c.properties.writeWithoutResponse||c.properties.write)return c}catch{this._logger.error("Failed to get write characteristic",{data:{deviceId:e.device.id}})}throw new l("No write characteristic available")}_publishDiscoveredDevices(){const e=[];for(const[t]of this._connectionStateMachinesByDeviceId){const n=this._deviceRegistryById.get(t);n?.ledgerServiceInfo&&e.push({id:t,deviceModel:n.ledgerServiceInfo.deviceModel,transport:u,name:n.device.name||void 0})}const i=Array.from(this._deviceRegistryById.values()).map(t=>t.discoveredDevice).filter(t=>!this._connectionStateMachinesByDeviceId.has(t.id));this._discoveredDevices$.next([...e,...i])}_withTimeout(e,i,t,n){return new Promise((r,c)=>{const d=setTimeout(()=>{c(new l(t)),n?.()},i);e.then(o=>{clearTimeout(d),r(o)},o=>{this._logger.error("withTimeout() promise rejected",{data:{e:o}}),clearTimeout(d),c(o)})})}_sleep(e){return new Promise(i=>setTimeout(i,e))}}const F=({deviceModelDataSource:y,loggerServiceFactory:e,apduSenderServiceFactory:i,apduReceiverServiceFactory:t})=>new R(y,e,i,t);export{R as WebBleTransport,u as webBleIdentifier,F 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": "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",
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 ConnectError,\n DeviceAlreadyConnectedError,\n DeviceConnectionStateMachine,\n type DeviceModelDataSource,\n type DmkError,\n GeneralDmkError,\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 { type Either, Left, Right } from \"purify-ts\";\nimport { BehaviorSubject, from, type Observable } from \"rxjs\";\nimport { switchMap } from \"rxjs/operators\";\n\nimport {\n ADVERTISING_DELAY,\n ADVERTISING_TIMEOUT,\n RECONNECT_DEVICE_TIMEOUT,\n RECONNECTION_LOOP_BACKOFF,\n REDISCOVER_TIMEOUT,\n} from \"@api/data/WebBleConfig\";\n\nimport {\n WebBleApduSender,\n type WebBleApduSenderDependencies,\n} from \"./WebBleApduSender\";\n\nexport const webBleIdentifier: TransportIdentifier = \"WEB-BLE-RN-STYLE\";\n\ntype DeviceRegistryEntry = {\n device: BluetoothDevice;\n serviceUuid?: string;\n ledgerServiceInfo?: {\n writeCmdUuid: string;\n writeUuid?: string;\n notifyUuid: string;\n deviceModel: TransportDeviceModel;\n };\n discoveredDevice: TransportDiscoveredDevice;\n gattDisconnectListener?: (ev: Event) => void;\n};\n\nexport class WebBleTransport implements Transport {\n private _logger: LoggerPublisherService;\n\n private _connectionStateMachinesByDeviceId = new Map<\n string,\n DeviceConnectionStateMachine<WebBleApduSenderDependencies>\n >();\n private _deviceRegistryById = new Map<string, DeviceRegistryEntry>();\n private _discoveredDevices$ = new BehaviorSubject<\n TransportDiscoveredDevice[]\n >([]);\n\n constructor(\n private readonly _deviceModelDataSource: DeviceModelDataSource,\n private loggerFactory: (tag: string) => LoggerPublisherService,\n private readonly _apduSenderFactory: ApduSenderServiceFactory,\n private readonly _apduReceiverFactory: ApduReceiverServiceFactory,\n ) {\n this._logger = loggerFactory(\"WebBleTransportRnStyle\");\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 bluetoothServiceUuids = this._deviceModelDataSource\n .getBluetoothServices()\n .map((serviceUuid) => ({ services: [serviceUuid] }));\n const allOptionalServiceUuids =\n this._deviceModelDataSource.getBluetoothServices();\n\n return from(\n navigator.bluetooth.requestDevice({\n filters: bluetoothServiceUuids,\n optionalServices: allOptionalServiceUuids,\n }),\n ).pipe(\n switchMap(async (bluetoothDevice) => {\n const { serviceUuid, ledgerServiceInfo } =\n await this._identifyLedgerGattService(bluetoothDevice);\n\n const discoveredDevice: TransportDiscoveredDevice = {\n id: bluetoothDevice.id,\n deviceModel: ledgerServiceInfo.deviceModel,\n transport: webBleIdentifier,\n name: bluetoothDevice.name || undefined,\n };\n\n this._deviceRegistryById.set(bluetoothDevice.id, {\n device: bluetoothDevice,\n serviceUuid,\n ledgerServiceInfo,\n discoveredDevice,\n });\n\n this._publishDiscoveredDevices();\n return discoveredDevice;\n }),\n );\n }\n\n stopDiscovering(): void {\n /* no-op on Web Bluetooth */\n }\n\n listenToAvailableDevices(): Observable<TransportDiscoveredDevice[]> {\n this._publishDiscoveredDevices();\n return this._discoveredDevices$.asObservable();\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 registryEntry = this._deviceRegistryById.get(params.deviceId);\n if (!registryEntry)\n return Left(new UnknownDeviceError(`Unknown device ${params.deviceId}`));\n\n if (this._connectionStateMachinesByDeviceId.has(params.deviceId)) {\n return Left(\n new DeviceAlreadyConnectedError(\n `Device ${params.deviceId} already connected`,\n ),\n );\n }\n\n try {\n const bluetoothDevice = registryEntry.device;\n if (!bluetoothDevice.gatt) {\n throw new OpeningConnectionError(\"No GATT server available on device\");\n }\n\n if (!bluetoothDevice.gatt.connected) {\n await this._withTimeout(\n bluetoothDevice.gatt.connect(),\n 6000,\n \"GATT connect timed out\",\n );\n await this._sleep(150);\n }\n\n const { service: ledgerService, ledgerServiceInfo } =\n await this._getPrimaryLedgerGattService(bluetoothDevice);\n const {\n writeCharacteristic: gattWriteCharacteristic,\n notifyCharacteristic: gattNotifyCharacteristic,\n } = await this._resolveLedgerServiceCharacteristics(\n ledgerService,\n ledgerServiceInfo,\n );\n\n const apduSender = new WebBleApduSender(\n {\n writeCharacteristic: gattWriteCharacteristic,\n notifyCharacteristic: gattNotifyCharacteristic,\n apduSenderFactory: this._apduSenderFactory,\n apduReceiverFactory: this._apduReceiverFactory,\n },\n this.loggerFactory,\n );\n\n const connectionStateMachine =\n new DeviceConnectionStateMachine<WebBleApduSenderDependencies>({\n deviceId: params.deviceId,\n deviceApduSender: apduSender,\n timeoutDuration: RECONNECT_DEVICE_TIMEOUT,\n tryToReconnect: () => {\n this._tryToReconnect(params.deviceId, params.onReconnect).catch(\n (e) =>\n this._logger.error(\"tryToReconnect() threw\", { data: { e } }),\n );\n },\n onTerminated: () => {\n try {\n this._connectionStateMachinesByDeviceId\n .get(params.deviceId)\n ?.closeConnection();\n params.onDisconnect(params.deviceId);\n } finally {\n this._connectionStateMachinesByDeviceId.delete(params.deviceId);\n const entry = this._deviceRegistryById.get(params.deviceId);\n if (entry?.gattDisconnectListener) {\n entry.device.removeEventListener(\n \"gattserverdisconnected\",\n entry.gattDisconnectListener,\n );\n entry.gattDisconnectListener = undefined;\n }\n this._publishDiscoveredDevices();\n }\n },\n });\n\n await apduSender.setupConnection();\n\n this._connectionStateMachinesByDeviceId.set(\n params.deviceId,\n connectionStateMachine,\n );\n registryEntry.serviceUuid = ledgerService.uuid;\n registryEntry.ledgerServiceInfo = ledgerServiceInfo;\n\n const onGattDisconnected = (_ev: Event) =>\n this._handleGattServerDisconnected(params.deviceId);\n registryEntry.gattDisconnectListener = onGattDisconnected;\n bluetoothDevice.addEventListener(\n \"gattserverdisconnected\",\n onGattDisconnected,\n );\n\n this._publishDiscoveredDevices();\n\n return Right(\n new TransportConnectedDevice({\n id: params.deviceId,\n deviceModel: ledgerServiceInfo.deviceModel,\n type: \"BLE\",\n transport: webBleIdentifier,\n sendApdu: (...apduArgs) =>\n connectionStateMachine.sendApdu(...apduArgs),\n name: bluetoothDevice.name || undefined,\n }),\n );\n } catch (e) {\n this._logger.error(\"connect() error\", { data: { e } });\n return Left(new OpeningConnectionError(e));\n }\n }\n\n async disconnect(params: {\n connectedDevice: TransportConnectedDevice;\n }): Promise<Either<DmkError, void>> {\n const deviceId = params.connectedDevice.id;\n const connectionStateMachine =\n this._connectionStateMachinesByDeviceId.get(deviceId);\n const registryEntry = this._deviceRegistryById.get(deviceId);\n if (!connectionStateMachine)\n return Left(new UnknownDeviceError(`Unknown device ${deviceId}`));\n\n try {\n connectionStateMachine.closeConnection();\n this._connectionStateMachinesByDeviceId.delete(deviceId);\n if (registryEntry?.gattDisconnectListener) {\n registryEntry.device.removeEventListener(\n \"gattserverdisconnected\",\n registryEntry.gattDisconnectListener,\n );\n registryEntry.gattDisconnectListener = undefined;\n }\n await this._safelyCancelGattConnection(deviceId);\n this._publishDiscoveredDevices();\n return Right(undefined);\n } catch (e) {\n return Left(new GeneralDmkError({ originalError: e }));\n }\n }\n\n private _handleGattServerDisconnected(deviceId: string) {\n this._logger.debug(`[${deviceId}] gattserverdisconnected`);\n const connectionStateMachine =\n this._connectionStateMachinesByDeviceId.get(deviceId);\n if (!connectionStateMachine) return;\n connectionStateMachine.eventDeviceDisconnected();\n }\n\n private async _waitForAdvertisementInRange(\n device: BluetoothDevice,\n opts: { startTimeoutMs?: number; advTimeoutMs?: number } = {},\n ): Promise<boolean> {\n const { startTimeoutMs = 500, advTimeoutMs = 1500 } = opts;\n if (typeof device.watchAdvertisements !== \"function\") return false;\n\n const abortController = new AbortController();\n const startTimer = setTimeout(\n () => abortController.abort(),\n startTimeoutMs,\n );\n const advertisementsStarted = await device\n .watchAdvertisements({ signal: abortController.signal })\n .then(() => true)\n .catch(() => false);\n clearTimeout(startTimer);\n if (!advertisementsStarted) return false;\n\n const advertisementSeen = await new Promise<boolean>((resolve) => {\n const handleAdvertisement = () => {\n device.removeEventListener(\n \"advertisementreceived\",\n handleAdvertisement,\n );\n resolve(true);\n };\n device.addEventListener(\"advertisementreceived\", handleAdvertisement);\n setTimeout(() => {\n device.removeEventListener(\n \"advertisementreceived\",\n handleAdvertisement,\n );\n resolve(false);\n }, advTimeoutMs);\n });\n\n abortController.abort();\n return advertisementSeen;\n }\n\n private async _rediscoverPermittedDevice(\n targetDeviceId: string,\n ): Promise<BluetoothDevice | null> {\n if (typeof navigator.bluetooth.getDevices !== \"function\") return null;\n try {\n this._logger.debug(`Attempting to rediscover device ${targetDeviceId}`);\n const permittedDevices = await navigator.bluetooth.getDevices();\n const matchingDevice =\n permittedDevices.find((d) => d.id === targetDeviceId) ?? null;\n\n if (matchingDevice) {\n const isInRange = await this._waitForAdvertisementInRange(\n matchingDevice,\n {\n startTimeoutMs: ADVERTISING_DELAY,\n advTimeoutMs: ADVERTISING_TIMEOUT,\n },\n );\n this._logger.debug(\n `Rediscovered ${matchingDevice.id}, inRange=${isInRange}`,\n );\n }\n return matchingDevice;\n } catch {\n return null;\n }\n }\n\n private async _tryToReconnect(\n deviceId: string,\n onReconnect?: (id: string) => Promise<void> | void,\n ) {\n await this._safelyCancelGattConnection(deviceId);\n\n while (true) {\n const registryEntry = this._deviceRegistryById.get(deviceId);\n const connectionStateMachine =\n this._connectionStateMachinesByDeviceId.get(deviceId);\n\n if (!registryEntry || !connectionStateMachine) {\n this._logger.debug(\n `[${deviceId}] aborting reconnect: registry or state machine missing`,\n );\n return;\n }\n\n try {\n const rediscoveredDevice = await this._withTimeout(\n this._rediscoverPermittedDevice(deviceId),\n REDISCOVER_TIMEOUT,\n \"rediscovery timeout\",\n );\n\n if (!rediscoveredDevice) throw new Error(\"Device not found\");\n\n if (!rediscoveredDevice.gatt) throw new Error(\"No GATT on device\");\n\n try {\n await rediscoveredDevice.gatt.connect();\n } catch (e) {\n this._logger.error(`[${deviceId}] gatt.connect() failed`, {\n data: { e },\n });\n if (rediscoveredDevice.gatt.connected)\n rediscoveredDevice.gatt.disconnect();\n }\n\n const { service: ledgerService, ledgerServiceInfo } =\n await this._getPrimaryLedgerGattService(rediscoveredDevice);\n\n const {\n writeCharacteristic: gattWriteCharacteristic,\n notifyCharacteristic: gattNotifyCharacteristic,\n } = await this._resolveLedgerServiceCharacteristics(\n ledgerService,\n ledgerServiceInfo,\n );\n\n connectionStateMachine.setDependencies({\n writeCharacteristic: gattWriteCharacteristic,\n notifyCharacteristic: gattNotifyCharacteristic,\n });\n await connectionStateMachine.setupConnection();\n connectionStateMachine.eventDeviceConnected();\n\n registryEntry.serviceUuid = ledgerService.uuid;\n registryEntry.ledgerServiceInfo = ledgerServiceInfo;\n\n if (registryEntry.gattDisconnectListener) {\n rediscoveredDevice.removeEventListener(\n \"gattserverdisconnected\",\n registryEntry.gattDisconnectListener,\n );\n }\n\n const onDisconnectedCallback = (_: Event) =>\n this._handleGattServerDisconnected(deviceId);\n\n rediscoveredDevice.addEventListener(\n \"gattserverdisconnected\",\n onDisconnectedCallback,\n );\n\n registryEntry.gattDisconnectListener = onDisconnectedCallback;\n\n await onReconnect?.(deviceId);\n\n this._publishDiscoveredDevices();\n\n return;\n } catch (e) {\n this._logger.error(`[${deviceId}] reconnect attempt failed`, {\n data: { e },\n });\n\n if (registryEntry?.device.gatt?.connected)\n registryEntry.device.gatt.disconnect();\n\n await this._sleep(RECONNECTION_LOOP_BACKOFF);\n continue;\n }\n }\n }\n\n private async _safelyCancelGattConnection(deviceId: string) {\n const registryEntry = this._deviceRegistryById.get(deviceId);\n if (!registryEntry) return;\n if (registryEntry.device.gatt?.connected)\n registryEntry.device.gatt.disconnect();\n await this._sleep(100);\n }\n\n private async _identifyLedgerGattService(device: BluetoothDevice): Promise<{\n serviceUuid: string;\n ledgerServiceInfo: NonNullable<DeviceRegistryEntry[\"ledgerServiceInfo\"]>;\n }> {\n if (!device.gatt) {\n throw new OpeningConnectionError(\"No GATT server available on device\");\n }\n try {\n await this._withTimeout(device.gatt.connect(), 6000, \"connect timeout\");\n const { service, ledgerServiceInfo } =\n await this._getPrimaryLedgerGattService(device);\n return { serviceUuid: service.uuid, ledgerServiceInfo };\n } finally {\n device.gatt?.disconnect();\n await this._sleep(200);\n }\n }\n\n private async _getPrimaryLedgerGattService(device: BluetoothDevice): Promise<{\n service: BluetoothRemoteGATTService;\n ledgerServiceInfo: NonNullable<DeviceRegistryEntry[\"ledgerServiceInfo\"]>;\n }> {\n const knownLedgerServiceUuids =\n this._deviceModelDataSource.getBluetoothServices();\n const lastSuccessfulServiceUuid = this._deviceRegistryById.get(\n device.id,\n )?.serviceUuid;\n const preferredSearchOrder = lastSuccessfulServiceUuid\n ? [\n lastSuccessfulServiceUuid,\n ...knownLedgerServiceUuids.filter(\n (u) => u !== lastSuccessfulServiceUuid,\n ),\n ]\n : knownLedgerServiceUuids.slice();\n\n for (const candidateUuid of preferredSearchOrder) {\n try {\n const primaryService =\n await device.gatt!.getPrimaryService(candidateUuid);\n const ledgerServiceInfoMap =\n this._deviceModelDataSource.getBluetoothServicesInfos();\n const ledgerServiceInfo = ledgerServiceInfoMap[primaryService.uuid];\n if (!ledgerServiceInfo) throw new UnknownDeviceError(device.name || \"\");\n return { service: primaryService, ledgerServiceInfo };\n } catch (e: unknown) {\n if ((e as Error)?.name === \"SecurityError\") {\n throw new OpeningConnectionError(\n `Missing Web Bluetooth permission for service ${candidateUuid}. ` +\n `Add it to optionalServices in requestDevice().`,\n );\n }\n try {\n const allPrimaryServices = await device.gatt!.getPrimaryServices();\n const matchedService = allPrimaryServices.find(\n (s) => s.uuid.toLowerCase() === candidateUuid.toLowerCase(),\n );\n if (matchedService) {\n const ledgerServiceInfoMap =\n this._deviceModelDataSource.getBluetoothServicesInfos();\n const ledgerServiceInfo = ledgerServiceInfoMap[matchedService.uuid];\n if (!ledgerServiceInfo)\n throw new UnknownDeviceError(device.name || \"\");\n return { service: matchedService, ledgerServiceInfo };\n }\n } catch {\n this._logger.error(\"Failed to get primary services\", {\n data: { deviceId: device.id },\n });\n }\n }\n }\n throw new OpeningConnectionError(\"Ledger GATT service not found\");\n }\n\n private async _resolveLedgerServiceCharacteristics(\n service: BluetoothRemoteGATTService,\n ledgerServiceInfo: NonNullable<DeviceRegistryEntry[\"ledgerServiceInfo\"]>,\n ): Promise<{\n writeCharacteristic: BluetoothRemoteGATTCharacteristic;\n notifyCharacteristic: BluetoothRemoteGATTCharacteristic;\n }> {\n const notifyCharacteristic = await service.getCharacteristic(\n ledgerServiceInfo.notifyUuid,\n );\n const writeCharacteristic = await this._findWritableCharacteristic(\n service,\n ledgerServiceInfo,\n );\n return { writeCharacteristic, notifyCharacteristic };\n }\n\n private async _findWritableCharacteristic(\n service: BluetoothRemoteGATTService,\n ledgerServiceInfo: NonNullable<DeviceRegistryEntry[\"ledgerServiceInfo\"]>,\n ): Promise<BluetoothRemoteGATTCharacteristic> {\n const preferredCharacteristicUuids = [\n ledgerServiceInfo.writeCmdUuid,\n ledgerServiceInfo.writeUuid,\n ].filter(Boolean) as string[];\n\n const attemptedCharacteristics: BluetoothRemoteGATTCharacteristic[] = [];\n\n for (const uuid of preferredCharacteristicUuids) {\n try {\n const characteristic = await service.getCharacteristic(uuid);\n attemptedCharacteristics.push(characteristic);\n\n if (characteristic.properties.writeWithoutResponse)\n return characteristic;\n if (characteristic.properties.write) return characteristic;\n } catch {\n this._logger.error(\"Failed to get write characteristic\", {\n data: { deviceId: service.device.id },\n });\n }\n }\n\n throw new OpeningConnectionError(\"No write characteristic available\");\n }\n\n private _publishDiscoveredDevices() {\n const connectedSummaries: TransportDiscoveredDevice[] = [];\n for (const [deviceId] of this._connectionStateMachinesByDeviceId) {\n const registryEntry = this._deviceRegistryById.get(deviceId);\n if (registryEntry?.ledgerServiceInfo) {\n connectedSummaries.push({\n id: deviceId,\n deviceModel: registryEntry.ledgerServiceInfo.deviceModel,\n transport: webBleIdentifier,\n name: registryEntry.device.name || undefined,\n });\n }\n }\n const scannedSummaries = Array.from(this._deviceRegistryById.values())\n .map((entry) => entry.discoveredDevice)\n .filter(\n (summary) => !this._connectionStateMachinesByDeviceId.has(summary.id),\n );\n\n this._discoveredDevices$.next([...connectedSummaries, ...scannedSummaries]);\n }\n\n private _withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n timeoutMessage: string,\n onTimeoutCancel?: () => void,\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timeoutHandle = setTimeout(() => {\n reject(new OpeningConnectionError(timeoutMessage));\n onTimeoutCancel?.();\n }, timeoutMs);\n promise.then(\n (value) => {\n clearTimeout(timeoutHandle);\n resolve(value);\n },\n (error) => {\n this._logger.error(\"withTimeout() promise rejected\", {\n data: { e: error },\n });\n clearTimeout(timeoutHandle);\n reject(error);\n },\n );\n });\n }\n\n private _sleep(ms: number) {\n return new Promise((resolve) => setTimeout(resolve, ms));\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", "DeviceConnectionStateMachine", "GeneralDmkError", "OpeningConnectionError", "TransportConnectedDevice", "UnknownDeviceError", "Left", "Right", "BehaviorSubject", "from", "switchMap", "ADVERTISING_DELAY", "ADVERTISING_TIMEOUT", "RECONNECT_DEVICE_TIMEOUT", "RECONNECTION_LOOP_BACKOFF", "REDISCOVER_TIMEOUT", "WebBleApduSender", "webBleIdentifier", "WebBleTransport", "_deviceModelDataSource", "loggerFactory", "_apduSenderFactory", "_apduReceiverFactory", "bluetoothServiceUuids", "serviceUuid", "allOptionalServiceUuids", "bluetoothDevice", "ledgerServiceInfo", "discoveredDevice", "params", "registryEntry", "ledgerService", "gattWriteCharacteristic", "gattNotifyCharacteristic", "apduSender", "connectionStateMachine", "e", "entry", "onGattDisconnected", "_ev", "apduArgs", "deviceId", "device", "opts", "startTimeoutMs", "advTimeoutMs", "abortController", "startTimer", "advertisementsStarted", "advertisementSeen", "resolve", "handleAdvertisement", "targetDeviceId", "matchingDevice", "d", "isInRange", "onReconnect", "rediscoveredDevice", "onDisconnectedCallback", "_", "service", "knownLedgerServiceUuids", "lastSuccessfulServiceUuid", "preferredSearchOrder", "u", "candidateUuid", "primaryService", "matchedService", "s", "notifyCharacteristic", "preferredCharacteristicUuids", "attemptedCharacteristics", "uuid", "characteristic", "connectedSummaries", "scannedSummaries", "summary", "promise", "timeoutMs", "timeoutMessage", "onTimeoutCancel", "reject", "timeoutHandle", "value", "error", "ms", "webBleTransportFactory", "deviceModelDataSource", "loggerServiceFactory", "apduSenderServiceFactory", "apduReceiverServiceFactory"]
7
7
  }