@ledgerhq/device-contacts-kit 0.0.0-develop-20260827155839 → 0.0.0-develop-20260829001241

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. package/README.md +57 -2
  2. package/lib/cjs/api/ContactsManager.js +1 -1
  3. package/lib/cjs/api/ContactsManager.js.map +1 -1
  4. package/lib/cjs/api/app-binder/RegisterExternalAddressDeviceActionTypes.js +2 -0
  5. package/lib/cjs/api/app-binder/RegisterExternalAddressDeviceActionTypes.js.map +7 -0
  6. package/lib/cjs/api/app-binder/RenameContactDeviceActionTypes.js +2 -0
  7. package/lib/cjs/api/app-binder/RenameContactDeviceActionTypes.js.map +7 -0
  8. package/lib/cjs/api/index.js +1 -1
  9. package/lib/cjs/api/index.js.map +2 -2
  10. package/lib/cjs/api/model/RegisterExternalAddress.js +2 -0
  11. package/lib/cjs/api/model/RegisterExternalAddress.js.map +7 -0
  12. package/lib/cjs/api/model/RenameContact.js +2 -0
  13. package/lib/cjs/api/model/RenameContact.js.map +7 -0
  14. package/lib/cjs/internal/DefaultContactsManager.js +1 -1
  15. package/lib/cjs/internal/DefaultContactsManager.js.map +3 -3
  16. package/lib/cjs/internal/DefaultContactsManager.test.js +2 -0
  17. package/lib/cjs/internal/DefaultContactsManager.test.js.map +7 -0
  18. package/lib/cjs/internal/app-binder/ContactsAppBinder.js +1 -1
  19. package/lib/cjs/internal/app-binder/ContactsAppBinder.js.map +3 -3
  20. package/lib/cjs/internal/app-binder/command/RegisterIdentityCommand.js +2 -0
  21. package/lib/cjs/internal/app-binder/command/RegisterIdentityCommand.js.map +7 -0
  22. package/lib/cjs/internal/app-binder/command/RegisterIdentityCommand.test.js +2 -0
  23. package/lib/cjs/internal/app-binder/command/RegisterIdentityCommand.test.js.map +7 -0
  24. package/lib/cjs/internal/app-binder/command/RenameContactCommand.js +2 -0
  25. package/lib/cjs/internal/app-binder/command/RenameContactCommand.js.map +7 -0
  26. package/lib/cjs/internal/app-binder/command/RenameContactCommand.test.js +2 -0
  27. package/lib/cjs/internal/app-binder/command/RenameContactCommand.test.js.map +7 -0
  28. package/lib/cjs/internal/app-binder/device-action/RegisterExternalAddress/RegisterExternalAddressDeviceAction.js +2 -0
  29. package/lib/cjs/internal/app-binder/device-action/RegisterExternalAddress/RegisterExternalAddressDeviceAction.js.map +7 -0
  30. package/lib/cjs/internal/app-binder/device-action/RegisterExternalAddress/RegisterExternalAddressDeviceAction.test.js +2 -0
  31. package/lib/cjs/internal/app-binder/device-action/RegisterExternalAddress/RegisterExternalAddressDeviceAction.test.js.map +7 -0
  32. package/lib/cjs/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.js +2 -0
  33. package/lib/cjs/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.js.map +7 -0
  34. package/lib/cjs/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.test.js +2 -0
  35. package/lib/cjs/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.test.js.map +7 -0
  36. package/lib/cjs/internal/app-binder/device-action/RenameContact/RenameContactDeviceAction.js +2 -0
  37. package/lib/cjs/internal/app-binder/device-action/RenameContact/RenameContactDeviceAction.js.map +7 -0
  38. package/lib/cjs/internal/app-binder/device-action/RenameContact/RenameContactDeviceAction.test.js +2 -0
  39. package/lib/cjs/internal/app-binder/device-action/RenameContact/RenameContactDeviceAction.test.js.map +7 -0
  40. package/lib/cjs/internal/app-binder/device-action/RenameContact/validateRenameContactInput.js +2 -0
  41. package/lib/cjs/internal/app-binder/device-action/RenameContact/validateRenameContactInput.js.map +7 -0
  42. package/lib/cjs/internal/app-binder/device-action/RenameContact/validateRenameContactInput.test.js +2 -0
  43. package/lib/cjs/internal/app-binder/device-action/RenameContact/validateRenameContactInput.test.js.map +7 -0
  44. package/lib/cjs/internal/app-binder/device-action/__test-utils__/makeInternalApi.js +2 -0
  45. package/lib/cjs/internal/app-binder/device-action/__test-utils__/makeInternalApi.js.map +7 -0
  46. package/lib/cjs/internal/app-binder/device-action/__test-utils__/setupGoToDashboardDAMock.js +2 -0
  47. package/lib/cjs/internal/app-binder/device-action/__test-utils__/setupGoToDashboardDAMock.js.map +7 -0
  48. package/lib/cjs/internal/app-binder/device-action/__test-utils__/setupOpenAppDAMock.js +2 -0
  49. package/lib/cjs/internal/app-binder/device-action/__test-utils__/setupOpenAppDAMock.js.map +7 -0
  50. package/lib/cjs/internal/app-binder/device-action/__test-utils__/setupWaitForAppAndVersionDAMock.js +2 -0
  51. package/lib/cjs/internal/app-binder/device-action/__test-utils__/setupWaitForAppAndVersionDAMock.js.map +7 -0
  52. package/lib/cjs/internal/app-binder/device-action/__test-utils__/testDeviceActionStates.js +2 -0
  53. package/lib/cjs/internal/app-binder/device-action/__test-utils__/testDeviceActionStates.js.map +7 -0
  54. package/lib/cjs/internal/app-binder/isContactsSupportedForSession.js +1 -1
  55. package/lib/cjs/internal/app-binder/isContactsSupportedForSession.js.map +3 -3
  56. package/lib/cjs/internal/app-binder/isContactsSupportedForSession.test.js +1 -1
  57. package/lib/cjs/internal/app-binder/isContactsSupportedForSession.test.js.map +3 -3
  58. package/lib/cjs/internal/app-binder/model/contactsConstants.js +2 -0
  59. package/lib/cjs/internal/app-binder/model/contactsConstants.js.map +7 -0
  60. package/lib/cjs/internal/app-binder/model/contactsErrors.js +2 -0
  61. package/lib/cjs/internal/app-binder/model/contactsErrors.js.map +7 -0
  62. package/lib/cjs/internal/app-binder/model/contactsValidation.js +2 -0
  63. package/lib/cjs/internal/app-binder/model/contactsValidation.js.map +7 -0
  64. package/lib/cjs/internal/app-binder/model/contactsValidation.test.js +2 -0
  65. package/lib/cjs/internal/app-binder/model/contactsValidation.test.js.map +7 -0
  66. package/lib/cjs/internal/app-binder/services/contactsTlvSerializer.js +2 -0
  67. package/lib/cjs/internal/app-binder/services/contactsTlvSerializer.js.map +7 -0
  68. package/lib/cjs/internal/app-binder/services/contactsTlvSerializer.test.js +2 -0
  69. package/lib/cjs/internal/app-binder/services/contactsTlvSerializer.test.js.map +7 -0
  70. package/lib/cjs/internal/app-binder/services/sendFramedContactsPayload.js +2 -0
  71. package/lib/cjs/internal/app-binder/services/sendFramedContactsPayload.js.map +7 -0
  72. package/lib/cjs/internal/app-binder/services/sendFramedContactsPayload.test.js +2 -0
  73. package/lib/cjs/internal/app-binder/services/sendFramedContactsPayload.test.js.map +7 -0
  74. package/lib/cjs/internal/app-binder/task/SendRegisterIdentityTask.js +2 -0
  75. package/lib/cjs/internal/app-binder/task/SendRegisterIdentityTask.js.map +7 -0
  76. package/lib/cjs/internal/app-binder/task/SendRegisterIdentityTask.test.js +2 -0
  77. package/lib/cjs/internal/app-binder/task/SendRegisterIdentityTask.test.js.map +7 -0
  78. package/lib/cjs/internal/app-binder/task/SendRenameContactTask.js +2 -0
  79. package/lib/cjs/internal/app-binder/task/SendRenameContactTask.js.map +7 -0
  80. package/lib/cjs/internal/app-binder/task/SendRenameContactTask.test.js +2 -0
  81. package/lib/cjs/internal/app-binder/task/SendRenameContactTask.test.js.map +7 -0
  82. package/lib/cjs/internal/di.js +1 -1
  83. package/lib/cjs/internal/di.js.map +3 -3
  84. package/lib/cjs/internal/di.test.js +1 -1
  85. package/lib/cjs/internal/di.test.js.map +3 -3
  86. package/lib/cjs/internal/use-cases/RegisterExternalAddressUseCase.js +2 -0
  87. package/lib/cjs/internal/use-cases/RegisterExternalAddressUseCase.js.map +7 -0
  88. package/lib/cjs/internal/use-cases/RegisterExternalAddressUseCase.test.js +2 -0
  89. package/lib/cjs/internal/use-cases/RegisterExternalAddressUseCase.test.js.map +7 -0
  90. package/lib/cjs/internal/use-cases/RenameContactUseCase.js +2 -0
  91. package/lib/cjs/internal/use-cases/RenameContactUseCase.js.map +7 -0
  92. package/lib/cjs/internal/use-cases/RenameContactUseCase.test.js +2 -0
  93. package/lib/cjs/internal/use-cases/RenameContactUseCase.test.js.map +7 -0
  94. package/lib/cjs/internal/use-cases/di/useCaseModule.js +2 -0
  95. package/lib/cjs/internal/use-cases/di/useCaseModule.js.map +7 -0
  96. package/lib/cjs/internal/use-cases/di/useCaseTypes.js +2 -0
  97. package/lib/cjs/internal/use-cases/di/useCaseTypes.js.map +7 -0
  98. package/lib/cjs/package.json +2 -1
  99. package/lib/esm/api/app-binder/RegisterExternalAddressDeviceActionTypes.js +1 -0
  100. package/lib/esm/api/app-binder/RegisterExternalAddressDeviceActionTypes.js.map +7 -0
  101. package/lib/esm/api/app-binder/RenameContactDeviceActionTypes.js +1 -0
  102. package/lib/esm/api/app-binder/RenameContactDeviceActionTypes.js.map +7 -0
  103. package/lib/esm/api/index.js +1 -1
  104. package/lib/esm/api/index.js.map +2 -2
  105. package/lib/esm/api/model/RegisterExternalAddress.js +1 -0
  106. package/lib/esm/api/model/RegisterExternalAddress.js.map +7 -0
  107. package/lib/esm/api/model/RenameContact.js +1 -0
  108. package/lib/esm/api/model/RenameContact.js.map +7 -0
  109. package/lib/esm/internal/DefaultContactsManager.js +1 -1
  110. package/lib/esm/internal/DefaultContactsManager.js.map +3 -3
  111. package/lib/esm/internal/DefaultContactsManager.test.js +2 -0
  112. package/lib/esm/internal/DefaultContactsManager.test.js.map +7 -0
  113. package/lib/esm/internal/app-binder/ContactsAppBinder.js +1 -1
  114. package/lib/esm/internal/app-binder/ContactsAppBinder.js.map +3 -3
  115. package/lib/esm/internal/app-binder/command/RegisterIdentityCommand.js +2 -0
  116. package/lib/esm/internal/app-binder/command/RegisterIdentityCommand.js.map +7 -0
  117. package/lib/esm/internal/app-binder/command/RegisterIdentityCommand.test.js +2 -0
  118. package/lib/esm/internal/app-binder/command/RegisterIdentityCommand.test.js.map +7 -0
  119. package/lib/esm/internal/app-binder/command/RenameContactCommand.js +2 -0
  120. package/lib/esm/internal/app-binder/command/RenameContactCommand.js.map +7 -0
  121. package/lib/esm/internal/app-binder/command/RenameContactCommand.test.js +2 -0
  122. package/lib/esm/internal/app-binder/command/RenameContactCommand.test.js.map +7 -0
  123. package/lib/esm/internal/app-binder/device-action/RegisterExternalAddress/RegisterExternalAddressDeviceAction.js +2 -0
  124. package/lib/esm/internal/app-binder/device-action/RegisterExternalAddress/RegisterExternalAddressDeviceAction.js.map +7 -0
  125. package/lib/esm/internal/app-binder/device-action/RegisterExternalAddress/RegisterExternalAddressDeviceAction.test.js +2 -0
  126. package/lib/esm/internal/app-binder/device-action/RegisterExternalAddress/RegisterExternalAddressDeviceAction.test.js.map +7 -0
  127. package/lib/esm/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.js +2 -0
  128. package/lib/esm/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.js.map +7 -0
  129. package/lib/esm/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.test.js +2 -0
  130. package/lib/esm/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.test.js.map +7 -0
  131. package/lib/esm/internal/app-binder/device-action/RenameContact/RenameContactDeviceAction.js +2 -0
  132. package/lib/esm/internal/app-binder/device-action/RenameContact/RenameContactDeviceAction.js.map +7 -0
  133. package/lib/esm/internal/app-binder/device-action/RenameContact/RenameContactDeviceAction.test.js +2 -0
  134. package/lib/esm/internal/app-binder/device-action/RenameContact/RenameContactDeviceAction.test.js.map +7 -0
  135. package/lib/esm/internal/app-binder/device-action/RenameContact/validateRenameContactInput.js +2 -0
  136. package/lib/esm/internal/app-binder/device-action/RenameContact/validateRenameContactInput.js.map +7 -0
  137. package/lib/esm/internal/app-binder/device-action/RenameContact/validateRenameContactInput.test.js +2 -0
  138. package/lib/esm/internal/app-binder/device-action/RenameContact/validateRenameContactInput.test.js.map +7 -0
  139. package/lib/esm/internal/app-binder/device-action/__test-utils__/makeInternalApi.js +2 -0
  140. package/lib/esm/internal/app-binder/device-action/__test-utils__/makeInternalApi.js.map +7 -0
  141. package/lib/esm/internal/app-binder/device-action/__test-utils__/setupGoToDashboardDAMock.js +2 -0
  142. package/lib/esm/internal/app-binder/device-action/__test-utils__/setupGoToDashboardDAMock.js.map +7 -0
  143. package/lib/esm/internal/app-binder/device-action/__test-utils__/setupOpenAppDAMock.js +2 -0
  144. package/lib/esm/internal/app-binder/device-action/__test-utils__/setupOpenAppDAMock.js.map +7 -0
  145. package/lib/esm/internal/app-binder/device-action/__test-utils__/setupWaitForAppAndVersionDAMock.js +2 -0
  146. package/lib/esm/internal/app-binder/device-action/__test-utils__/setupWaitForAppAndVersionDAMock.js.map +7 -0
  147. package/lib/esm/internal/app-binder/device-action/__test-utils__/testDeviceActionStates.js +2 -0
  148. package/lib/esm/internal/app-binder/device-action/__test-utils__/testDeviceActionStates.js.map +7 -0
  149. package/lib/esm/internal/app-binder/isContactsSupportedForSession.js +1 -1
  150. package/lib/esm/internal/app-binder/isContactsSupportedForSession.js.map +3 -3
  151. package/lib/esm/internal/app-binder/isContactsSupportedForSession.test.js +1 -1
  152. package/lib/esm/internal/app-binder/isContactsSupportedForSession.test.js.map +3 -3
  153. package/lib/esm/internal/app-binder/model/contactsConstants.js +2 -0
  154. package/lib/esm/internal/app-binder/model/contactsConstants.js.map +7 -0
  155. package/lib/esm/internal/app-binder/model/contactsErrors.js +2 -0
  156. package/lib/esm/internal/app-binder/model/contactsErrors.js.map +7 -0
  157. package/lib/esm/internal/app-binder/model/contactsValidation.js +2 -0
  158. package/lib/esm/internal/app-binder/model/contactsValidation.js.map +7 -0
  159. package/lib/esm/internal/app-binder/model/contactsValidation.test.js +2 -0
  160. package/lib/esm/internal/app-binder/model/contactsValidation.test.js.map +7 -0
  161. package/lib/esm/internal/app-binder/services/contactsTlvSerializer.js +2 -0
  162. package/lib/esm/internal/app-binder/services/contactsTlvSerializer.js.map +7 -0
  163. package/lib/esm/internal/app-binder/services/contactsTlvSerializer.test.js +2 -0
  164. package/lib/esm/internal/app-binder/services/contactsTlvSerializer.test.js.map +7 -0
  165. package/lib/esm/internal/app-binder/services/sendFramedContactsPayload.js +2 -0
  166. package/lib/esm/internal/app-binder/services/sendFramedContactsPayload.js.map +7 -0
  167. package/lib/esm/internal/app-binder/services/sendFramedContactsPayload.test.js +2 -0
  168. package/lib/esm/internal/app-binder/services/sendFramedContactsPayload.test.js.map +7 -0
  169. package/lib/esm/internal/app-binder/task/SendRegisterIdentityTask.js +2 -0
  170. package/lib/esm/internal/app-binder/task/SendRegisterIdentityTask.js.map +7 -0
  171. package/lib/esm/internal/app-binder/task/SendRegisterIdentityTask.test.js +2 -0
  172. package/lib/esm/internal/app-binder/task/SendRegisterIdentityTask.test.js.map +7 -0
  173. package/lib/esm/internal/app-binder/task/SendRenameContactTask.js +2 -0
  174. package/lib/esm/internal/app-binder/task/SendRenameContactTask.js.map +7 -0
  175. package/lib/esm/internal/app-binder/task/SendRenameContactTask.test.js +2 -0
  176. package/lib/esm/internal/app-binder/task/SendRenameContactTask.test.js.map +7 -0
  177. package/lib/esm/internal/di.js +1 -1
  178. package/lib/esm/internal/di.js.map +3 -3
  179. package/lib/esm/internal/di.test.js +1 -1
  180. package/lib/esm/internal/di.test.js.map +3 -3
  181. package/lib/esm/internal/use-cases/RegisterExternalAddressUseCase.js +2 -0
  182. package/lib/esm/internal/use-cases/RegisterExternalAddressUseCase.js.map +7 -0
  183. package/lib/esm/internal/use-cases/RegisterExternalAddressUseCase.test.js +2 -0
  184. package/lib/esm/internal/use-cases/RegisterExternalAddressUseCase.test.js.map +7 -0
  185. package/lib/esm/internal/use-cases/RenameContactUseCase.js +2 -0
  186. package/lib/esm/internal/use-cases/RenameContactUseCase.js.map +7 -0
  187. package/lib/esm/internal/use-cases/RenameContactUseCase.test.js +2 -0
  188. package/lib/esm/internal/use-cases/RenameContactUseCase.test.js.map +7 -0
  189. package/lib/esm/internal/use-cases/di/useCaseModule.js +2 -0
  190. package/lib/esm/internal/use-cases/di/useCaseModule.js.map +7 -0
  191. package/lib/esm/internal/use-cases/di/useCaseTypes.js +2 -0
  192. package/lib/esm/internal/use-cases/di/useCaseTypes.js.map +7 -0
  193. package/lib/esm/package.json +2 -1
  194. package/lib/types/api/ContactsManager.d.ts +23 -5
  195. package/lib/types/api/ContactsManager.d.ts.map +1 -1
  196. package/lib/types/api/app-binder/RegisterExternalAddressDeviceActionTypes.d.ts +30 -0
  197. package/lib/types/api/app-binder/RegisterExternalAddressDeviceActionTypes.d.ts.map +1 -0
  198. package/lib/types/api/app-binder/RenameContactDeviceActionTypes.d.ts +25 -0
  199. package/lib/types/api/app-binder/RenameContactDeviceActionTypes.d.ts.map +1 -0
  200. package/lib/types/api/index.d.ts +4 -0
  201. package/lib/types/api/index.d.ts.map +1 -1
  202. package/lib/types/api/model/RegisterExternalAddress.d.ts +50 -0
  203. package/lib/types/api/model/RegisterExternalAddress.d.ts.map +1 -0
  204. package/lib/types/api/model/RenameContact.d.ts +39 -0
  205. package/lib/types/api/model/RenameContact.d.ts.map +1 -0
  206. package/lib/types/internal/DefaultContactsManager.d.ts +8 -2
  207. package/lib/types/internal/DefaultContactsManager.d.ts.map +1 -1
  208. package/lib/types/internal/DefaultContactsManager.test.d.ts +2 -0
  209. package/lib/types/internal/DefaultContactsManager.test.d.ts.map +1 -0
  210. package/lib/types/internal/app-binder/ContactsAppBinder.d.ts +7 -3
  211. package/lib/types/internal/app-binder/ContactsAppBinder.d.ts.map +1 -1
  212. package/lib/types/internal/app-binder/command/RegisterIdentityCommand.d.ts +30 -0
  213. package/lib/types/internal/app-binder/command/RegisterIdentityCommand.d.ts.map +1 -0
  214. package/lib/types/internal/app-binder/command/RegisterIdentityCommand.test.d.ts +2 -0
  215. package/lib/types/internal/app-binder/command/RegisterIdentityCommand.test.d.ts.map +1 -0
  216. package/lib/types/internal/app-binder/command/RenameContactCommand.d.ts +25 -0
  217. package/lib/types/internal/app-binder/command/RenameContactCommand.d.ts.map +1 -0
  218. package/lib/types/internal/app-binder/command/RenameContactCommand.test.d.ts +2 -0
  219. package/lib/types/internal/app-binder/command/RenameContactCommand.test.d.ts.map +1 -0
  220. package/lib/types/internal/app-binder/device-action/RegisterExternalAddress/RegisterExternalAddressDeviceAction.d.ts +14 -0
  221. package/lib/types/internal/app-binder/device-action/RegisterExternalAddress/RegisterExternalAddressDeviceAction.d.ts.map +1 -0
  222. package/lib/types/internal/app-binder/device-action/RegisterExternalAddress/RegisterExternalAddressDeviceAction.test.d.ts +2 -0
  223. package/lib/types/internal/app-binder/device-action/RegisterExternalAddress/RegisterExternalAddressDeviceAction.test.d.ts.map +1 -0
  224. package/lib/types/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.d.ts +12 -0
  225. package/lib/types/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.d.ts.map +1 -0
  226. package/lib/types/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.test.d.ts +2 -0
  227. package/lib/types/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.test.d.ts.map +1 -0
  228. package/lib/types/internal/app-binder/device-action/RenameContact/RenameContactDeviceAction.d.ts +13 -0
  229. package/lib/types/internal/app-binder/device-action/RenameContact/RenameContactDeviceAction.d.ts.map +1 -0
  230. package/lib/types/internal/app-binder/device-action/RenameContact/RenameContactDeviceAction.test.d.ts +2 -0
  231. package/lib/types/internal/app-binder/device-action/RenameContact/RenameContactDeviceAction.test.d.ts.map +1 -0
  232. package/lib/types/internal/app-binder/device-action/RenameContact/validateRenameContactInput.d.ts +12 -0
  233. package/lib/types/internal/app-binder/device-action/RenameContact/validateRenameContactInput.d.ts.map +1 -0
  234. package/lib/types/internal/app-binder/device-action/RenameContact/validateRenameContactInput.test.d.ts +2 -0
  235. package/lib/types/internal/app-binder/device-action/RenameContact/validateRenameContactInput.test.d.ts.map +1 -0
  236. package/lib/types/internal/app-binder/device-action/__test-utils__/makeInternalApi.d.ts +4 -0
  237. package/lib/types/internal/app-binder/device-action/__test-utils__/makeInternalApi.d.ts.map +1 -0
  238. package/lib/types/internal/app-binder/device-action/__test-utils__/setupGoToDashboardDAMock.d.ts +12 -0
  239. package/lib/types/internal/app-binder/device-action/__test-utils__/setupGoToDashboardDAMock.d.ts.map +1 -0
  240. package/lib/types/internal/app-binder/device-action/__test-utils__/setupOpenAppDAMock.d.ts +2 -0
  241. package/lib/types/internal/app-binder/device-action/__test-utils__/setupOpenAppDAMock.d.ts.map +1 -0
  242. package/lib/types/internal/app-binder/device-action/__test-utils__/setupWaitForAppAndVersionDAMock.d.ts +17 -0
  243. package/lib/types/internal/app-binder/device-action/__test-utils__/setupWaitForAppAndVersionDAMock.d.ts.map +1 -0
  244. package/lib/types/internal/app-binder/device-action/__test-utils__/testDeviceActionStates.d.ts +15 -0
  245. package/lib/types/internal/app-binder/device-action/__test-utils__/testDeviceActionStates.d.ts.map +1 -0
  246. package/lib/types/internal/app-binder/isContactsSupportedForSession.d.ts +31 -8
  247. package/lib/types/internal/app-binder/isContactsSupportedForSession.d.ts.map +1 -1
  248. package/lib/types/internal/app-binder/model/contactsConstants.d.ts +39 -0
  249. package/lib/types/internal/app-binder/model/contactsConstants.d.ts.map +1 -0
  250. package/lib/types/internal/app-binder/model/contactsErrors.d.ts +30 -0
  251. package/lib/types/internal/app-binder/model/contactsErrors.d.ts.map +1 -0
  252. package/lib/types/internal/app-binder/model/contactsValidation.d.ts +53 -0
  253. package/lib/types/internal/app-binder/model/contactsValidation.d.ts.map +1 -0
  254. package/lib/types/internal/app-binder/model/contactsValidation.test.d.ts +2 -0
  255. package/lib/types/internal/app-binder/model/contactsValidation.test.d.ts.map +1 -0
  256. package/lib/types/internal/app-binder/services/contactsTlvSerializer.d.ts +33 -0
  257. package/lib/types/internal/app-binder/services/contactsTlvSerializer.d.ts.map +1 -0
  258. package/lib/types/internal/app-binder/services/contactsTlvSerializer.test.d.ts +2 -0
  259. package/lib/types/internal/app-binder/services/contactsTlvSerializer.test.d.ts.map +1 -0
  260. package/lib/types/internal/app-binder/services/sendFramedContactsPayload.d.ts +14 -0
  261. package/lib/types/internal/app-binder/services/sendFramedContactsPayload.d.ts.map +1 -0
  262. package/lib/types/internal/app-binder/services/sendFramedContactsPayload.test.d.ts +2 -0
  263. package/lib/types/internal/app-binder/services/sendFramedContactsPayload.test.d.ts.map +1 -0
  264. package/lib/types/internal/app-binder/task/SendRegisterIdentityTask.d.ts +25 -0
  265. package/lib/types/internal/app-binder/task/SendRegisterIdentityTask.d.ts.map +1 -0
  266. package/lib/types/internal/app-binder/task/SendRegisterIdentityTask.test.d.ts +2 -0
  267. package/lib/types/internal/app-binder/task/SendRegisterIdentityTask.test.d.ts.map +1 -0
  268. package/lib/types/internal/app-binder/task/SendRenameContactTask.d.ts +20 -0
  269. package/lib/types/internal/app-binder/task/SendRenameContactTask.d.ts.map +1 -0
  270. package/lib/types/internal/app-binder/task/SendRenameContactTask.test.d.ts +2 -0
  271. package/lib/types/internal/app-binder/task/SendRenameContactTask.test.d.ts.map +1 -0
  272. package/lib/types/internal/di.d.ts.map +1 -1
  273. package/lib/types/internal/use-cases/RegisterExternalAddressUseCase.d.ts +17 -0
  274. package/lib/types/internal/use-cases/RegisterExternalAddressUseCase.d.ts.map +1 -0
  275. package/lib/types/internal/use-cases/RegisterExternalAddressUseCase.test.d.ts +2 -0
  276. package/lib/types/internal/use-cases/RegisterExternalAddressUseCase.test.d.ts.map +1 -0
  277. package/lib/types/internal/use-cases/RenameContactUseCase.d.ts +19 -0
  278. package/lib/types/internal/use-cases/RenameContactUseCase.d.ts.map +1 -0
  279. package/lib/types/internal/use-cases/RenameContactUseCase.test.d.ts +2 -0
  280. package/lib/types/internal/use-cases/RenameContactUseCase.test.d.ts.map +1 -0
  281. package/lib/types/internal/use-cases/di/useCaseModule.d.ts +3 -0
  282. package/lib/types/internal/use-cases/di/useCaseModule.d.ts.map +1 -0
  283. package/lib/types/internal/use-cases/di/useCaseTypes.d.ts +5 -0
  284. package/lib/types/internal/use-cases/di/useCaseTypes.d.ts.map +1 -0
  285. package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
  286. package/package.json +7 -6
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/internal/app-binder/device-action/RenameContact/validateRenameContactInput.test.ts"],
4
+ "sourcesContent": ["import { type RenameContactInput } from \"@api/model/RenameContact\";\nimport { ContactsValidationError } from \"@internal/app-binder/model/contactsValidation\";\n\nimport { validateRenameContactInput } from \"./validateRenameContactInput\";\n\nconst VALID_INPUT: RenameContactInput = {\n previousContactName: \"Alice\",\n newContactName: \"Bob\",\n groupHandle: new Uint8Array(64).fill(0xcc),\n hmacProof: new Uint8Array(32).fill(0xdd),\n};\n\ndescribe(\"validateRenameContactInput\", () => {\n it(\"returns null for valid input\", () => {\n expect(validateRenameContactInput(VALID_INPUT)).toBeNull();\n });\n\n it(\"returns a ContactsValidationError for a non-printable new name\", () => {\n expect(\n validateRenameContactInput({ ...VALID_INPUT, newContactName: \"Am\u00E9lie\" }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"returns a ContactsValidationError for a non-printable previous name\", () => {\n expect(\n validateRenameContactInput({\n ...VALID_INPUT,\n previousContactName: \"Am\u00E9lie\",\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"returns an error for an empty new name\", () => {\n expect(\n validateRenameContactInput({ ...VALID_INPUT, newContactName: \"\" }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"returns an error for a group handle of the wrong length\", () => {\n expect(\n validateRenameContactInput({\n ...VALID_INPUT,\n groupHandle: new Uint8Array(63),\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"returns an error for an hmac proof of the wrong length\", () => {\n expect(\n validateRenameContactInput({\n ...VALID_INPUT,\n hmacProof: new Uint8Array(31),\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n});\n"],
5
+ "mappings": "AACA,OAAS,2BAAAA,MAA+B,gDAExC,OAAS,8BAAAC,MAAkC,+BAE3C,MAAMC,EAAkC,CACtC,oBAAqB,QACrB,eAAgB,MAChB,YAAa,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,EACzC,UAAW,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,CACzC,EAEA,SAAS,6BAA8B,IAAM,CAC3C,GAAG,+BAAgC,IAAM,CACvC,OAAOD,EAA2BC,CAAW,CAAC,EAAE,SAAS,CAC3D,CAAC,EAED,GAAG,iEAAkE,IAAM,CACzE,OACED,EAA2B,CAAE,GAAGC,EAAa,eAAgB,WAAS,CAAC,CACzE,EAAE,eAAeF,CAAuB,CAC1C,CAAC,EAED,GAAG,sEAAuE,IAAM,CAC9E,OACEC,EAA2B,CACzB,GAAGC,EACH,oBAAqB,WACvB,CAAC,CACH,EAAE,eAAeF,CAAuB,CAC1C,CAAC,EAED,GAAG,yCAA0C,IAAM,CACjD,OACEC,EAA2B,CAAE,GAAGC,EAAa,eAAgB,EAAG,CAAC,CACnE,EAAE,eAAeF,CAAuB,CAC1C,CAAC,EAED,GAAG,0DAA2D,IAAM,CAClE,OACEC,EAA2B,CACzB,GAAGC,EACH,YAAa,IAAI,WAAW,EAAE,CAChC,CAAC,CACH,EAAE,eAAeF,CAAuB,CAC1C,CAAC,EAED,GAAG,yDAA0D,IAAM,CACjE,OACEC,EAA2B,CACzB,GAAGC,EACH,UAAW,IAAI,WAAW,EAAE,CAC9B,CAAC,CACH,EAAE,eAAeF,CAAuB,CAC1C,CAAC,CACH,CAAC",
6
+ "names": ["ContactsValidationError", "validateRenameContactInput", "VALID_INPUT"]
7
+ }
@@ -0,0 +1,2 @@
1
+ function n(){return{sendApdu:vi.fn(),sendCommand:vi.fn().mockResolvedValue(void 0),getDeviceModel:vi.fn(),getDeviceSessionState:vi.fn(),getDeviceSessionStateObservable:vi.fn(),setDeviceSessionState:vi.fn(),getManagerApiService:vi.fn(),getSecureChannelService:vi.fn(),loggerFactory:vi.fn(e=>({debug:vi.fn(),info:vi.fn(),warn:vi.fn(),error:vi.fn(),subscribers:[]}))}}export{n as makeDeviceActionInternalApiMock};
2
+ //# sourceMappingURL=makeInternalApi.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/internal/app-binder/device-action/__test-utils__/makeInternalApi.ts"],
4
+ "sourcesContent": ["import { type InternalApi } from \"@ledgerhq/device-management-kit\";\nimport { type Mocked } from \"vitest\";\n\nexport function makeDeviceActionInternalApiMock(): Mocked<InternalApi> {\n return {\n sendApdu: vi.fn(),\n sendCommand: vi.fn().mockResolvedValue(undefined),\n getDeviceModel: vi.fn(),\n getDeviceSessionState: vi.fn(),\n getDeviceSessionStateObservable: vi.fn(),\n setDeviceSessionState: vi.fn(),\n getManagerApiService: vi.fn(),\n getSecureChannelService: vi.fn(),\n loggerFactory: vi.fn((_tag: string) => ({\n debug: vi.fn(),\n info: vi.fn(),\n warn: vi.fn(),\n error: vi.fn(),\n subscribers: [],\n })),\n };\n}\n"],
5
+ "mappings": "AAGO,SAASA,GAAuD,CACrE,MAAO,CACL,SAAU,GAAG,GAAG,EAChB,YAAa,GAAG,GAAG,EAAE,kBAAkB,MAAS,EAChD,eAAgB,GAAG,GAAG,EACtB,sBAAuB,GAAG,GAAG,EAC7B,gCAAiC,GAAG,GAAG,EACvC,sBAAuB,GAAG,GAAG,EAC7B,qBAAsB,GAAG,GAAG,EAC5B,wBAAyB,GAAG,GAAG,EAC/B,cAAe,GAAG,GAAIC,IAAkB,CACtC,MAAO,GAAG,GAAG,EACb,KAAM,GAAG,GAAG,EACZ,KAAM,GAAG,GAAG,EACZ,MAAO,GAAG,GAAG,EACb,YAAa,CAAC,CAChB,EAAE,CACJ,CACF",
6
+ "names": ["makeDeviceActionInternalApiMock", "_tag"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{GoToDashboardDeviceAction as t,UserInteractionRequired as n}from"@ledgerhq/device-management-kit";import{Left as o,Right as i}from"purify-ts";import{assign as a,createMachine as c}from"xstate";const p=(e={})=>{const r=e.requiredUserInteraction??n.None;t.mockImplementation(()=>({makeStateMachine:vi.fn().mockImplementation(()=>c({initial:"pending",states:{pending:{entry:a({intermediateValue:{requiredUserInteraction:r}}),after:{0:"done"}},done:{type:"final"}},output:()=>e.error?o(e.error):i(void 0)}))}))};export{p as setupGoToDashboardDAMock};
2
+ //# sourceMappingURL=setupGoToDashboardDAMock.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/internal/app-binder/device-action/__test-utils__/setupGoToDashboardDAMock.ts"],
4
+ "sourcesContent": ["import {\n GoToDashboardDeviceAction,\n UserInteractionRequired,\n} from \"@ledgerhq/device-management-kit\";\nimport { Left, Right } from \"purify-ts\";\nimport { type Mock } from \"vitest\";\nimport { assign, createMachine } from \"xstate\";\n\n/**\n * Mock GoToDashboardDeviceAction to resolve immediately with either success\n * (default) or an error. Emits a single `requiredUserInteraction` snapshot\n * (default `None` \u2014 device assumed unlocked and already ready) so the parent\n * action can surface it, mirroring the real nested device action.\n */\nexport const setupGoToDashboardDAMock = (\n options: {\n error?: unknown;\n requiredUserInteraction?: UserInteractionRequired;\n } = {},\n) => {\n const interaction =\n options.requiredUserInteraction ?? UserInteractionRequired.None;\n (GoToDashboardDeviceAction as Mock).mockImplementation(() => ({\n makeStateMachine: vi.fn().mockImplementation(() =>\n createMachine({\n initial: \"pending\",\n states: {\n pending: {\n entry: assign({\n intermediateValue: {\n requiredUserInteraction: interaction,\n },\n }),\n after: {\n 0: \"done\",\n },\n },\n done: {\n type: \"final\",\n },\n },\n output: () => (options.error ? Left(options.error) : Right(undefined)),\n }),\n ),\n }));\n};\n"],
5
+ "mappings": "AAAA,OACE,6BAAAA,EACA,2BAAAC,MACK,kCACP,OAAS,QAAAC,EAAM,SAAAC,MAAa,YAE5B,OAAS,UAAAC,EAAQ,iBAAAC,MAAqB,SAQ/B,MAAMC,EAA2B,CACtCC,EAGI,CAAC,IACF,CACH,MAAMC,EACJD,EAAQ,yBAA2BN,EAAwB,KAC5DD,EAAmC,mBAAmB,KAAO,CAC5D,iBAAkB,GAAG,GAAG,EAAE,mBAAmB,IAC3CK,EAAc,CACZ,QAAS,UACT,OAAQ,CACN,QAAS,CACP,MAAOD,EAAO,CACZ,kBAAmB,CACjB,wBAAyBI,CAC3B,CACF,CAAC,EACD,MAAO,CACL,EAAG,MACL,CACF,EACA,KAAM,CACJ,KAAM,OACR,CACF,EACA,OAAQ,IAAOD,EAAQ,MAAQL,EAAKK,EAAQ,KAAK,EAAIJ,EAAM,MAAS,CACtE,CAAC,CACH,CACF,EAAE,CACJ",
6
+ "names": ["GoToDashboardDeviceAction", "UserInteractionRequired", "Left", "Right", "assign", "createMachine", "setupGoToDashboardDAMock", "options", "interaction"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{OpenAppDeviceAction as n,UserInteractionRequired as t}from"@ledgerhq/device-management-kit";import{Left as i,Right as o}from"purify-ts";import{assign as p,createMachine as r}from"xstate";const f=e=>{n.mockImplementation(()=>({makeStateMachine:vi.fn().mockImplementation(()=>r({initial:"pending",states:{pending:{entry:p({intermediateValue:{requiredUserInteraction:t.ConfirmOpenApp}}),after:{0:"done"}},done:{type:"final"}},output:()=>e?i(e):o(void 0)}))}))};export{f as setupOpenAppDAMock};
2
+ //# sourceMappingURL=setupOpenAppDAMock.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/internal/app-binder/device-action/__test-utils__/setupOpenAppDAMock.ts"],
4
+ "sourcesContent": ["import {\n OpenAppDeviceAction,\n UserInteractionRequired,\n} from \"@ledgerhq/device-management-kit\";\nimport { Left, Right } from \"purify-ts\";\nimport { type Mock } from \"vitest\";\nimport { assign, createMachine } from \"xstate\";\n\nexport const setupOpenAppDAMock = (error?: unknown) => {\n (OpenAppDeviceAction as Mock).mockImplementation(() => ({\n makeStateMachine: vi.fn().mockImplementation(() =>\n createMachine({\n initial: \"pending\",\n states: {\n pending: {\n entry: assign({\n intermediateValue: {\n requiredUserInteraction: UserInteractionRequired.ConfirmOpenApp,\n },\n }),\n after: {\n 0: \"done\",\n },\n },\n done: {\n type: \"final\",\n },\n },\n output: () => (error ? Left(error) : Right(undefined)),\n }),\n ),\n }));\n};\n"],
5
+ "mappings": "AAAA,OACE,uBAAAA,EACA,2BAAAC,MACK,kCACP,OAAS,QAAAC,EAAM,SAAAC,MAAa,YAE5B,OAAS,UAAAC,EAAQ,iBAAAC,MAAqB,SAE/B,MAAMC,EAAsBC,GAAoB,CACpDP,EAA6B,mBAAmB,KAAO,CACtD,iBAAkB,GAAG,GAAG,EAAE,mBAAmB,IAC3CK,EAAc,CACZ,QAAS,UACT,OAAQ,CACN,QAAS,CACP,MAAOD,EAAO,CACZ,kBAAmB,CACjB,wBAAyBH,EAAwB,cACnD,CACF,CAAC,EACD,MAAO,CACL,EAAG,MACL,CACF,EACA,KAAM,CACJ,KAAM,OACR,CACF,EACA,OAAQ,IAAOM,EAAQL,EAAKK,CAAK,EAAIJ,EAAM,MAAS,CACtD,CAAC,CACH,CACF,EAAE,CACJ",
6
+ "names": ["OpenAppDeviceAction", "UserInteractionRequired", "Left", "Right", "assign", "createMachine", "setupOpenAppDAMock", "error"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{UserInteractionRequired as t,WaitForAppAndVersionDeviceAction as o}from"@ledgerhq/device-management-kit";import{Left as r,Right as i}from"purify-ts";import{createMachine as a}from"xstate";const s=(e={name:"Ethereum",version:"1.15.0"},n=t.None)=>{o.mockImplementation(()=>({makeStateMachine:vi.fn().mockImplementation(()=>a({initial:"pending",context:{intermediateValue:{requiredUserInteraction:n}},states:{pending:{after:{0:"done"}},done:{type:"final"}},output:()=>"error"in e?r(e.error):i(e)}))}))};export{s as setupWaitForAppAndVersionDAMock};
2
+ //# sourceMappingURL=setupWaitForAppAndVersionDAMock.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/internal/app-binder/device-action/__test-utils__/setupWaitForAppAndVersionDAMock.ts"],
4
+ "sourcesContent": ["import {\n UserInteractionRequired,\n WaitForAppAndVersionDeviceAction,\n} from \"@ledgerhq/device-management-kit\";\nimport { Left, Right } from \"purify-ts\";\nimport { type Mock } from \"vitest\";\nimport { createMachine } from \"xstate\";\n\n/**\n * Mock WaitForAppAndVersionDeviceAction to resolve immediately with either a\n * fresh `{ name, version }` (default) or an error.\n *\n * The mock machine carries an `intermediateValue` in its context (matching the\n * real DA) so the parent's onSnapshot propagation has a defined value to read.\n * Pass `requiredUserInteraction` to have the child emit an interaction (e.g.\n * UnlockDevice for a locked device) before completing.\n */\nexport const setupWaitForAppAndVersionDAMock = (\n result: { name: string; version: string } | { error: unknown } = {\n name: \"Ethereum\",\n version: \"1.15.0\",\n },\n requiredUserInteraction: UserInteractionRequired = UserInteractionRequired.None,\n) => {\n (WaitForAppAndVersionDeviceAction as Mock).mockImplementation(() => ({\n makeStateMachine: vi.fn().mockImplementation(() =>\n createMachine({\n initial: \"pending\",\n context: {\n intermediateValue: { requiredUserInteraction },\n },\n states: {\n pending: {\n after: {\n 0: \"done\",\n },\n },\n done: {\n type: \"final\",\n },\n },\n output: () => (\"error\" in result ? Left(result.error) : Right(result)),\n }),\n ),\n }));\n};\n"],
5
+ "mappings": "AAAA,OACE,2BAAAA,EACA,oCAAAC,MACK,kCACP,OAAS,QAAAC,EAAM,SAAAC,MAAa,YAE5B,OAAS,iBAAAC,MAAqB,SAWvB,MAAMC,EAAkC,CAC7CC,EAAiE,CAC/D,KAAM,WACN,QAAS,QACX,EACAC,EAAmDP,EAAwB,OACxE,CACFC,EAA0C,mBAAmB,KAAO,CACnE,iBAAkB,GAAG,GAAG,EAAE,mBAAmB,IAC3CG,EAAc,CACZ,QAAS,UACT,QAAS,CACP,kBAAmB,CAAE,wBAAAG,CAAwB,CAC/C,EACA,OAAQ,CACN,QAAS,CACP,MAAO,CACL,EAAG,MACL,CACF,EACA,KAAM,CACJ,KAAM,OACR,CACF,EACA,OAAQ,IAAO,UAAWD,EAASJ,EAAKI,EAAO,KAAK,EAAIH,EAAMG,CAAM,CACtE,CAAC,CACH,CACF,EAAE,CACJ",
6
+ "names": ["UserInteractionRequired", "WaitForAppAndVersionDeviceAction", "Left", "Right", "createMachine", "setupWaitForAppAndVersionDAMock", "result", "requiredUserInteraction"]
7
+ }
@@ -0,0 +1,2 @@
1
+ function p(n,i,c,{onDone:a,onError:t}){const r=[],{observable:o,cancel:u}=n._execute(c);return o.subscribe({next:e=>{r.push(e)},error:e=>{t(e)},complete:()=>{try{expect(r).toEqual(i),a()}catch(e){t(e)}}}),{observable:o,cancel:u}}export{p as testDeviceActionStates};
2
+ //# sourceMappingURL=testDeviceActionStates.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/internal/app-binder/device-action/__test-utils__/testDeviceActionStates.ts"],
4
+ "sourcesContent": ["import {\n type DeviceAction,\n type DeviceActionIntermediateValue,\n type DeviceActionState,\n type DmkError,\n type InternalApi,\n} from \"@ledgerhq/device-management-kit\";\n\n/**\n * Test that the states emitted by a device action match the expected states.\n * @param deviceAction The device action to test.\n * @param expectedStates The expected states.\n * @param callbacks { onDone, onError } The callbacks to call when the test is done or an error occurs.\n */\nexport function testDeviceActionStates<\n Output,\n Input,\n Error extends DmkError,\n IntermediateValue extends DeviceActionIntermediateValue,\n>(\n deviceAction: DeviceAction<Output, Input, Error, IntermediateValue>,\n expectedStates: Array<DeviceActionState<Output, Error, IntermediateValue>>,\n internalApi: InternalApi,\n {\n onDone,\n onError,\n }: {\n onDone: () => void;\n onError: (error: Error) => void;\n },\n) {\n const observedStates: Array<\n DeviceActionState<Output, Error, IntermediateValue>\n > = [];\n\n const { observable, cancel } = deviceAction._execute(internalApi);\n observable.subscribe({\n next: (state) => {\n observedStates.push(state);\n },\n error: (error) => {\n onError(error);\n },\n complete: () => {\n try {\n expect(observedStates).toEqual(expectedStates);\n onDone();\n } catch (e) {\n onError(e as Error);\n }\n },\n });\n return { observable, cancel };\n}\n"],
5
+ "mappings": "AAcO,SAASA,EAMdC,EACAC,EACAC,EACA,CACE,OAAAC,EACA,QAAAC,CACF,EAIA,CACA,MAAMC,EAEF,CAAC,EAEC,CAAE,WAAAC,EAAY,OAAAC,CAAO,EAAIP,EAAa,SAASE,CAAW,EAChE,OAAAI,EAAW,UAAU,CACnB,KAAOE,GAAU,CACfH,EAAe,KAAKG,CAAK,CAC3B,EACA,MAAQC,GAAU,CAChBL,EAAQK,CAAK,CACf,EACA,SAAU,IAAM,CACd,GAAI,CACF,OAAOJ,CAAc,EAAE,QAAQJ,CAAc,EAC7CE,EAAO,CACT,OAAS,EAAG,CACVC,EAAQ,CAAU,CACpB,CACF,CACF,CAAC,EACM,CAAE,WAAAE,EAAY,OAAAC,CAAO,CAC9B",
6
+ "names": ["testDeviceActionStates", "deviceAction", "expectedStates", "internalApi", "onDone", "onError", "observedStates", "observable", "cancel", "state", "error"]
7
+ }
@@ -1,2 +1,2 @@
1
- import{ApplicationChecker as p}from"@ledgerhq/device-management-kit";import{isVersionAtLeast as a,resolveContactsVersionRequirements as f}from"../../api/model/ContactsVersionRequirements";import{ContactsApplicationResolver as c}from"./ContactsApplicationResolver";function A(o,s){const e=o.getDeviceSessionState(),n=f(e.deviceModelId);if(!n.supported)return!1;const i="currentApp"in e?e.currentApp?.name:void 0;if(i===void 0)return!1;const r=n.minAppVersion[i];if(r===void 0||!new p(e,s,new c).withMinVersionInclusive(r).check())return!1;const t="firmwareVersion"in e?e.firmwareVersion?.os:void 0;return t===void 0?!1:a(t,n.minOsVersion)}export{A as isContactsSupportedForSession};
1
+ import{ApplicationChecker as a}from"@ledgerhq/device-management-kit";import{isVersionAtLeast as s,resolveContactsVersionRequirements as p}from"../../api/model/ContactsVersionRequirements";import{ContactsApplicationResolver as c}from"./ContactsApplicationResolver";function m(r,n){const e=r.getDeviceSessionState(),i=p(e.deviceModelId);if(!i.supported)return!1;const o=i.minAppVersion[n.name];if(o===void 0||!new a(e,{version:n.version},new c).withMinVersionInclusive(o).check())return!1;const t="firmwareVersion"in e?e.firmwareVersion?.os:void 0;return t===void 0?!1:s(t,i.minOsVersion)}function V(r){const n=r.getDeviceSessionState(),e=p(n.deviceModelId);if(!e.supported)return!1;const i="firmwareVersion"in n?n.firmwareVersion?.os:void 0;return i===void 0?!1:s(i,e.minOsVersion)}export{V as isContactsOsSupportedForSession,m as isContactsSupportedForSession};
2
2
  //# sourceMappingURL=isContactsSupportedForSession.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/internal/app-binder/isContactsSupportedForSession.ts"],
4
- "sourcesContent": ["import {\n type AppConfig,\n ApplicationChecker,\n type InternalApi,\n} from \"@ledgerhq/device-management-kit\";\n\nimport {\n isVersionAtLeast,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n\nimport { ContactsApplicationResolver } from \"./ContactsApplicationResolver\";\n\n/**\n * Session-aware Contacts support check, for internal consumption by Contacts\n * `DeviceAction`s. Reads the current device session and resolves it against the\n * static requirements table:\n * - the device model must be supported;\n * - the running app must be known to Contacts and meet its minimum version \u2014\n * validated with DMK's {@link ApplicationChecker} so the version comes from\n * the authoritative `GetAppConfiguration` result;\n * - the device OS must meet its minimum version (a dimension `ApplicationChecker`\n * does not cover, so it is checked separately here).\n *\n * @param internalApi - the DeviceAction's internal API for the current session.\n * @param appConfig - the running app's configuration (must include its version).\n */\nexport function isContactsSupportedForSession(\n internalApi: InternalApi,\n appConfig: AppConfig,\n): boolean {\n const deviceState = internalApi.getDeviceSessionState();\n const requirement = resolveContactsVersionRequirements(\n deviceState.deviceModelId,\n );\n if (!requirement.supported) return false;\n\n const appName =\n \"currentApp\" in deviceState ? deviceState.currentApp?.name : undefined;\n if (appName === undefined) return false;\n\n const minAppVersion = requirement.minAppVersion[appName];\n if (minAppVersion === undefined) return false;\n\n const appCompatible = new ApplicationChecker(\n deviceState,\n appConfig,\n new ContactsApplicationResolver(),\n )\n .withMinVersionInclusive(minAppVersion)\n .check();\n if (!appCompatible) return false;\n\n const osVersion =\n \"firmwareVersion\" in deviceState\n ? deviceState.firmwareVersion?.os\n : undefined;\n if (osVersion === undefined) return false;\n\n return isVersionAtLeast(osVersion, requirement.minOsVersion);\n}\n"],
5
- "mappings": "AAAA,OAEE,sBAAAA,MAEK,kCAEP,OACE,oBAAAC,EACA,sCAAAC,MACK,yCAEP,OAAS,+BAAAC,MAAmC,gCAgBrC,SAASC,EACdC,EACAC,EACS,CACT,MAAMC,EAAcF,EAAY,sBAAsB,EAChDG,EAAcN,EAClBK,EAAY,aACd,EACA,GAAI,CAACC,EAAY,UAAW,MAAO,GAEnC,MAAMC,EACJ,eAAgBF,EAAcA,EAAY,YAAY,KAAO,OAC/D,GAAIE,IAAY,OAAW,MAAO,GAElC,MAAMC,EAAgBF,EAAY,cAAcC,CAAO,EAUvD,GATIC,IAAkB,QASlB,CAPkB,IAAIV,EACxBO,EACAD,EACA,IAAIH,CACN,EACG,wBAAwBO,CAAa,EACrC,MAAM,EACW,MAAO,GAE3B,MAAMC,EACJ,oBAAqBJ,EACjBA,EAAY,iBAAiB,GAC7B,OACN,OAAII,IAAc,OAAkB,GAE7BV,EAAiBU,EAAWH,EAAY,YAAY,CAC7D",
6
- "names": ["ApplicationChecker", "isVersionAtLeast", "resolveContactsVersionRequirements", "ContactsApplicationResolver", "isContactsSupportedForSession", "internalApi", "appConfig", "deviceState", "requirement", "appName", "minAppVersion", "osVersion"]
4
+ "sourcesContent": ["import {\n ApplicationChecker,\n type InternalApi,\n} from \"@ledgerhq/device-management-kit\";\n\nimport {\n isVersionAtLeast,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n\nimport { ContactsApplicationResolver } from \"./ContactsApplicationResolver\";\n\n/** The running app's identity, read freshly from the device. */\nexport type RunningApp = {\n readonly name: string;\n readonly version: string;\n};\n\n/**\n * Session-aware Contacts support check, for internal consumption by Contacts\n * `DeviceAction`s. Resolves the given running app against the static\n * requirements table:\n * - the device model must be supported;\n * - the running app must be known to Contacts and meet its minimum version;\n * - the device OS must meet its minimum version (a dimension `ApplicationChecker`\n * does not cover, so it is checked separately here).\n *\n * The app `name` and `version` are supplied by the caller so they can come from\n * a fresh `WaitForAppAndVersion` result rather than the (potentially stale)\n * device session state; the model and OS come from the session state, which\n * does not change under the caller's feet the way the running app can.\n *\n * @param internalApi - the DeviceAction's internal API for the current session.\n * @param app - the running app's fresh name and version.\n */\nexport function isContactsSupportedForSession(\n internalApi: InternalApi,\n app: RunningApp,\n): boolean {\n const deviceState = internalApi.getDeviceSessionState();\n const requirement = resolveContactsVersionRequirements(\n deviceState.deviceModelId,\n );\n if (!requirement.supported) return false;\n\n const minAppVersion = requirement.minAppVersion[app.name];\n if (minAppVersion === undefined) return false;\n\n const appCompatible = new ApplicationChecker(\n deviceState,\n { version: app.version },\n new ContactsApplicationResolver(),\n )\n .withMinVersionInclusive(minAppVersion)\n .check();\n if (!appCompatible) return false;\n\n const osVersion =\n \"firmwareVersion\" in deviceState\n ? deviceState.firmwareVersion?.os\n : undefined;\n if (osVersion === undefined) return false;\n\n return isVersionAtLeast(osVersion, requirement.minOsVersion);\n}\n\n/**\n * OS-only Contacts support check, for internal consumption by dashboard\n * `DeviceAction`s (e.g. Rename Contact, EDIT CONTACT NAME). Dashboard operations\n * are served by the device OS, not the embedded app, so only two dimensions\n * apply:\n * - the device model must be supported;\n * - the device OS must meet its minimum version.\n *\n * The running-app dimension is intentionally skipped: on the dashboard there is\n * no embedded app to gate on (the running \"app\" is BOLOS). The model and OS both\n * come from the session state, which is stable across the operation.\n *\n * @param internalApi - the DeviceAction's internal API for the current session.\n */\nexport function isContactsOsSupportedForSession(\n internalApi: InternalApi,\n): boolean {\n const deviceState = internalApi.getDeviceSessionState();\n const requirement = resolveContactsVersionRequirements(\n deviceState.deviceModelId,\n );\n if (!requirement.supported) return false;\n\n const osVersion =\n \"firmwareVersion\" in deviceState\n ? deviceState.firmwareVersion?.os\n : undefined;\n if (osVersion === undefined) return false;\n\n return isVersionAtLeast(osVersion, requirement.minOsVersion);\n}\n"],
5
+ "mappings": "AAAA,OACE,sBAAAA,MAEK,kCAEP,OACE,oBAAAC,EACA,sCAAAC,MACK,yCAEP,OAAS,+BAAAC,MAAmC,gCAyBrC,SAASC,EACdC,EACAC,EACS,CACT,MAAMC,EAAcF,EAAY,sBAAsB,EAChDG,EAAcN,EAClBK,EAAY,aACd,EACA,GAAI,CAACC,EAAY,UAAW,MAAO,GAEnC,MAAMC,EAAgBD,EAAY,cAAcF,EAAI,IAAI,EAUxD,GATIG,IAAkB,QASlB,CAPkB,IAAIT,EACxBO,EACA,CAAE,QAASD,EAAI,OAAQ,EACvB,IAAIH,CACN,EACG,wBAAwBM,CAAa,EACrC,MAAM,EACW,MAAO,GAE3B,MAAMC,EACJ,oBAAqBH,EACjBA,EAAY,iBAAiB,GAC7B,OACN,OAAIG,IAAc,OAAkB,GAE7BT,EAAiBS,EAAWF,EAAY,YAAY,CAC7D,CAgBO,SAASG,EACdN,EACS,CACT,MAAME,EAAcF,EAAY,sBAAsB,EAChDG,EAAcN,EAClBK,EAAY,aACd,EACA,GAAI,CAACC,EAAY,UAAW,MAAO,GAEnC,MAAME,EACJ,oBAAqBH,EACjBA,EAAY,iBAAiB,GAC7B,OACN,OAAIG,IAAc,OAAkB,GAE7BT,EAAiBS,EAAWF,EAAY,YAAY,CAC7D",
6
+ "names": ["ApplicationChecker", "isVersionAtLeast", "resolveContactsVersionRequirements", "ContactsApplicationResolver", "isContactsSupportedForSession", "internalApi", "app", "deviceState", "requirement", "minAppVersion", "osVersion", "isContactsOsSupportedForSession"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{DeviceModelId as r,DeviceSessionStateType as a,DeviceStatus as c}from"@ledgerhq/device-management-kit";import{ETHEREUM_APP_NAME as u,resolveContactsVersionRequirements as f}from"../../api/model/ContactsVersionRequirements";import{isContactsSupportedForSession as t}from"./isContactsSupportedForSession";const d=(()=>{const e=f(r.FLEX);if(!e.supported)throw new Error("Flex must be supported");return e})(),S=d.minOsVersion,s=(()=>{const e=d.minAppVersion[u];if(e===void 0)throw new Error("Ethereum min version required");return e})(),p="0.0.1";function i({appName:e=u,osVersion:l=S,modelId:m=r.FLEX}={}){return{sessionStateType:a.ReadyWithoutSecureChannel,deviceStatus:c.CONNECTED,installedApps:[],currentApp:{name:e,version:"0.0.0"},deviceModelId:m,firmwareVersion:{mcu:"1.0.0",bootloader:"1.0.0",os:l},isSecureConnectionAllowed:!1}}function n(e){return{getDeviceSessionState:()=>e}}function o(e){return{version:e}}describe("isContactsSupportedForSession",()=>{it("returns true when model, app version and OS version all meet the minimums",()=>{const e=n(i());expect(t(e,o(s))).toBe(!0)}),it("returns false on an unsupported device model",()=>{const e=n(i({modelId:r.NANO_X}));expect(t(e,o(s))).toBe(!1)}),it("returns false when the app version is below the minimum",()=>{const e=n(i());expect(t(e,o(p))).toBe(!1)}),it("returns false when the running app is unknown to Contacts",()=>{const e=n(i({appName:"Bitcoin"}));expect(t(e,o(s))).toBe(!1)}),it("returns false when the OS version is below the minimum",()=>{const e=n(i({osVersion:p}));expect(t(e,o(s))).toBe(!1)}),it("returns false when the device session has no running app",()=>{const e=n({sessionStateType:a.Connected,deviceStatus:c.CONNECTED,deviceModelId:r.FLEX});expect(t(e,o(s))).toBe(!1)})});
1
+ import{DeviceModelId as i,DeviceSessionStateType as c,DeviceStatus as u}from"@ledgerhq/device-management-kit";import{ETHEREUM_APP_NAME as r,resolveContactsVersionRequirements as l}from"../../api/model/ContactsVersionRequirements";import{isContactsSupportedForSession as t}from"./isContactsSupportedForSession";const d=(()=>{const e=l(i.FLEX);if(!e.supported)throw new Error("Flex must be supported");return e})(),m=d.minOsVersion,S=(()=>{const e=d.minAppVersion[r];if(e===void 0)throw new Error("Ethereum min version required");return e})(),a="0.0.1";function s({osVersion:e=m,modelId:p=i.FLEX}={}){return{sessionStateType:c.ReadyWithoutSecureChannel,deviceStatus:u.CONNECTED,installedApps:[],currentApp:{name:r,version:"0.0.0"},deviceModelId:p,firmwareVersion:{mcu:"1.0.0",bootloader:"1.0.0",os:e},isSecureConnectionAllowed:!1}}function n(e){return{getDeviceSessionState:()=>e}}function o(e=r,p=S){return{name:e,version:p}}describe("isContactsSupportedForSession",()=>{it("returns true when model, app version and OS version all meet the minimums",()=>{const e=n(s());expect(t(e,o())).toBe(!0)}),it("returns false on an unsupported device model",()=>{const e=n(s({modelId:i.NANO_X}));expect(t(e,o())).toBe(!1)}),it("returns false when the app version is below the minimum",()=>{const e=n(s());expect(t(e,o(r,a))).toBe(!1)}),it("returns false when the running app is unknown to Contacts",()=>{const e=n(s());expect(t(e,o("Bitcoin"))).toBe(!1)}),it("returns false when the OS version is below the minimum",()=>{const e=n(s({osVersion:a}));expect(t(e,o())).toBe(!1)}),it("returns false when the device session has no running app",()=>{const e=n({sessionStateType:c.Connected,deviceStatus:u.CONNECTED,deviceModelId:i.FLEX});expect(t(e,o())).toBe(!1)})});
2
2
  //# sourceMappingURL=isContactsSupportedForSession.test.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/internal/app-binder/isContactsSupportedForSession.test.ts"],
4
- "sourcesContent": ["import {\n type AppConfig,\n DeviceModelId,\n DeviceSessionStateType,\n DeviceStatus,\n type InternalApi,\n} from \"@ledgerhq/device-management-kit\";\n\nimport {\n ETHEREUM_APP_NAME,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n\nimport { isContactsSupportedForSession } from \"./isContactsSupportedForSession\";\n\nconst flexSupport = (() => {\n const requirement = resolveContactsVersionRequirements(DeviceModelId.FLEX);\n if (!requirement.supported) throw new Error(\"Flex must be supported\");\n return requirement;\n})();\n\nconst MIN_OS_VERSION = flexSupport.minOsVersion;\nconst MIN_APP_VERSION = (() => {\n const version = flexSupport.minAppVersion[ETHEREUM_APP_NAME];\n if (version === undefined) throw new Error(\"Ethereum min version required\");\n return version;\n})();\nconst BELOW_ANY_VERSION = \"0.0.1\";\n\ntype ReadyStateOptions = {\n appName?: string;\n osVersion?: string;\n modelId?: DeviceModelId;\n};\n\nfunction createReadyState({\n appName = ETHEREUM_APP_NAME,\n osVersion = MIN_OS_VERSION,\n modelId = DeviceModelId.FLEX,\n}: ReadyStateOptions = {}) {\n return {\n sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel,\n deviceStatus: DeviceStatus.CONNECTED,\n installedApps: [],\n currentApp: { name: appName, version: \"0.0.0\" },\n deviceModelId: modelId,\n firmwareVersion: { mcu: \"1.0.0\", bootloader: \"1.0.0\", os: osVersion },\n isSecureConnectionAllowed: false,\n };\n}\n\nfunction createInternalApi(deviceState: object): InternalApi {\n return {\n getDeviceSessionState: () => deviceState,\n } as unknown as InternalApi;\n}\n\nfunction appConfig(version: string): AppConfig {\n return { version };\n}\n\ndescribe(\"isContactsSupportedForSession\", () => {\n it(\"returns true when model, app version and OS version all meet the minimums\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n true,\n );\n });\n\n it(\"returns false on an unsupported device model\", () => {\n const api = createInternalApi(\n createReadyState({ modelId: DeviceModelId.NANO_X }),\n );\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n false,\n );\n });\n\n // App-version requirement.\n it(\"returns false when the app version is below the minimum\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsSupportedForSession(api, appConfig(BELOW_ANY_VERSION)),\n ).toBe(false);\n });\n\n it(\"returns false when the running app is unknown to Contacts\", () => {\n const api = createInternalApi(createReadyState({ appName: \"Bitcoin\" }));\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n false,\n );\n });\n\n // OS-version requirement.\n it(\"returns false when the OS version is below the minimum\", () => {\n const api = createInternalApi(\n createReadyState({ osVersion: BELOW_ANY_VERSION }),\n );\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n false,\n );\n });\n\n it(\"returns false when the device session has no running app\", () => {\n const api = createInternalApi({\n sessionStateType: DeviceSessionStateType.Connected,\n deviceStatus: DeviceStatus.CONNECTED,\n deviceModelId: DeviceModelId.FLEX,\n });\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n false,\n );\n });\n});\n"],
5
- "mappings": "AAAA,OAEE,iBAAAA,EACA,0BAAAC,EACA,gBAAAC,MAEK,kCAEP,OACE,qBAAAC,EACA,sCAAAC,MACK,yCAEP,OAAS,iCAAAC,MAAqC,kCAE9C,MAAMC,GAAe,IAAM,CACzB,MAAMC,EAAcH,EAAmCJ,EAAc,IAAI,EACzE,GAAI,CAACO,EAAY,UAAW,MAAM,IAAI,MAAM,wBAAwB,EACpE,OAAOA,CACT,GAAG,EAEGC,EAAiBF,EAAY,aAC7BG,GAAmB,IAAM,CAC7B,MAAMC,EAAUJ,EAAY,cAAcH,CAAiB,EAC3D,GAAIO,IAAY,OAAW,MAAM,IAAI,MAAM,+BAA+B,EAC1E,OAAOA,CACT,GAAG,EACGC,EAAoB,QAQ1B,SAASC,EAAiB,CACxB,QAAAC,EAAUV,EACV,UAAAW,EAAYN,EACZ,QAAAO,EAAUf,EAAc,IAC1B,EAAuB,CAAC,EAAG,CACzB,MAAO,CACL,iBAAkBC,EAAuB,0BACzC,aAAcC,EAAa,UAC3B,cAAe,CAAC,EAChB,WAAY,CAAE,KAAMW,EAAS,QAAS,OAAQ,EAC9C,cAAeE,EACf,gBAAiB,CAAE,IAAK,QAAS,WAAY,QAAS,GAAID,CAAU,EACpE,0BAA2B,EAC7B,CACF,CAEA,SAASE,EAAkBC,EAAkC,CAC3D,MAAO,CACL,sBAAuB,IAAMA,CAC/B,CACF,CAEA,SAASC,EAAUR,EAA4B,CAC7C,MAAO,CAAE,QAAAA,CAAQ,CACnB,CAEA,SAAS,gCAAiC,IAAM,CAC9C,GAAG,4EAA6E,IAAM,CACpF,MAAMS,EAAMH,EAAkBJ,EAAiB,CAAC,EAChD,OAAOP,EAA8Bc,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,EAED,GAAG,+CAAgD,IAAM,CACvD,MAAMU,EAAMH,EACVJ,EAAiB,CAAE,QAASZ,EAAc,MAAO,CAAC,CACpD,EACA,OAAOK,EAA8Bc,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,EAGD,GAAG,0DAA2D,IAAM,CAClE,MAAMU,EAAMH,EAAkBJ,EAAiB,CAAC,EAChD,OACEP,EAA8Bc,EAAKD,EAAUP,CAAiB,CAAC,CACjE,EAAE,KAAK,EAAK,CACd,CAAC,EAED,GAAG,4DAA6D,IAAM,CACpE,MAAMQ,EAAMH,EAAkBJ,EAAiB,CAAE,QAAS,SAAU,CAAC,CAAC,EACtE,OAAOP,EAA8Bc,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,EAGD,GAAG,yDAA0D,IAAM,CACjE,MAAMU,EAAMH,EACVJ,EAAiB,CAAE,UAAWD,CAAkB,CAAC,CACnD,EACA,OAAON,EAA8Bc,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,EAED,GAAG,2DAA4D,IAAM,CACnE,MAAMU,EAAMH,EAAkB,CAC5B,iBAAkBf,EAAuB,UACzC,aAAcC,EAAa,UAC3B,cAAeF,EAAc,IAC/B,CAAC,EACD,OAAOK,EAA8Bc,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,CACH,CAAC",
6
- "names": ["DeviceModelId", "DeviceSessionStateType", "DeviceStatus", "ETHEREUM_APP_NAME", "resolveContactsVersionRequirements", "isContactsSupportedForSession", "flexSupport", "requirement", "MIN_OS_VERSION", "MIN_APP_VERSION", "version", "BELOW_ANY_VERSION", "createReadyState", "appName", "osVersion", "modelId", "createInternalApi", "deviceState", "appConfig", "api"]
4
+ "sourcesContent": ["import {\n DeviceModelId,\n DeviceSessionStateType,\n DeviceStatus,\n type InternalApi,\n} from \"@ledgerhq/device-management-kit\";\n\nimport {\n ETHEREUM_APP_NAME,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n\nimport {\n isContactsSupportedForSession,\n type RunningApp,\n} from \"./isContactsSupportedForSession\";\n\nconst flexSupport = (() => {\n const requirement = resolveContactsVersionRequirements(DeviceModelId.FLEX);\n if (!requirement.supported) throw new Error(\"Flex must be supported\");\n return requirement;\n})();\n\nconst MIN_OS_VERSION = flexSupport.minOsVersion;\nconst MIN_APP_VERSION = (() => {\n const version = flexSupport.minAppVersion[ETHEREUM_APP_NAME];\n if (version === undefined) throw new Error(\"Ethereum min version required\");\n return version;\n})();\nconst BELOW_ANY_VERSION = \"0.0.1\";\n\ntype ReadyStateOptions = {\n osVersion?: string;\n modelId?: DeviceModelId;\n};\n\nfunction createReadyState({\n osVersion = MIN_OS_VERSION,\n modelId = DeviceModelId.FLEX,\n}: ReadyStateOptions = {}) {\n return {\n sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel,\n deviceStatus: DeviceStatus.CONNECTED,\n installedApps: [],\n currentApp: { name: ETHEREUM_APP_NAME, version: \"0.0.0\" },\n deviceModelId: modelId,\n firmwareVersion: { mcu: \"1.0.0\", bootloader: \"1.0.0\", os: osVersion },\n isSecureConnectionAllowed: false,\n };\n}\n\nfunction createInternalApi(deviceState: object): InternalApi {\n return {\n getDeviceSessionState: () => deviceState,\n } as unknown as InternalApi;\n}\n\n// The running app identity now comes from the caller (a fresh\n// WaitForAppAndVersion result), not the device session state.\nfunction runningApp(\n name = ETHEREUM_APP_NAME,\n version = MIN_APP_VERSION,\n): RunningApp {\n return { name, version };\n}\n\ndescribe(\"isContactsSupportedForSession\", () => {\n it(\"returns true when model, app version and OS version all meet the minimums\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsSupportedForSession(api, runningApp())).toBe(true);\n });\n\n it(\"returns false on an unsupported device model\", () => {\n const api = createInternalApi(\n createReadyState({ modelId: DeviceModelId.NANO_X }),\n );\n expect(isContactsSupportedForSession(api, runningApp())).toBe(false);\n });\n\n // App-version requirement (evaluated from the fresh app version).\n it(\"returns false when the app version is below the minimum\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsSupportedForSession(\n api,\n runningApp(ETHEREUM_APP_NAME, BELOW_ANY_VERSION),\n ),\n ).toBe(false);\n });\n\n it(\"returns false when the running app is unknown to Contacts\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsSupportedForSession(api, runningApp(\"Bitcoin\"))).toBe(\n false,\n );\n });\n\n // OS-version requirement.\n it(\"returns false when the OS version is below the minimum\", () => {\n const api = createInternalApi(\n createReadyState({ osVersion: BELOW_ANY_VERSION }),\n );\n expect(isContactsSupportedForSession(api, runningApp())).toBe(false);\n });\n\n it(\"returns false when the device session has no running app\", () => {\n const api = createInternalApi({\n sessionStateType: DeviceSessionStateType.Connected,\n deviceStatus: DeviceStatus.CONNECTED,\n deviceModelId: DeviceModelId.FLEX,\n });\n expect(isContactsSupportedForSession(api, runningApp())).toBe(false);\n });\n});\n"],
5
+ "mappings": "AAAA,OACE,iBAAAA,EACA,0BAAAC,EACA,gBAAAC,MAEK,kCAEP,OACE,qBAAAC,EACA,sCAAAC,MACK,yCAEP,OACE,iCAAAC,MAEK,kCAEP,MAAMC,GAAe,IAAM,CACzB,MAAMC,EAAcH,EAAmCJ,EAAc,IAAI,EACzE,GAAI,CAACO,EAAY,UAAW,MAAM,IAAI,MAAM,wBAAwB,EACpE,OAAOA,CACT,GAAG,EAEGC,EAAiBF,EAAY,aAC7BG,GAAmB,IAAM,CAC7B,MAAMC,EAAUJ,EAAY,cAAcH,CAAiB,EAC3D,GAAIO,IAAY,OAAW,MAAM,IAAI,MAAM,+BAA+B,EAC1E,OAAOA,CACT,GAAG,EACGC,EAAoB,QAO1B,SAASC,EAAiB,CACxB,UAAAC,EAAYL,EACZ,QAAAM,EAAUd,EAAc,IAC1B,EAAuB,CAAC,EAAG,CACzB,MAAO,CACL,iBAAkBC,EAAuB,0BACzC,aAAcC,EAAa,UAC3B,cAAe,CAAC,EAChB,WAAY,CAAE,KAAMC,EAAmB,QAAS,OAAQ,EACxD,cAAeW,EACf,gBAAiB,CAAE,IAAK,QAAS,WAAY,QAAS,GAAID,CAAU,EACpE,0BAA2B,EAC7B,CACF,CAEA,SAASE,EAAkBC,EAAkC,CAC3D,MAAO,CACL,sBAAuB,IAAMA,CAC/B,CACF,CAIA,SAASC,EACPC,EAAOf,EACPO,EAAUD,EACE,CACZ,MAAO,CAAE,KAAAS,EAAM,QAAAR,CAAQ,CACzB,CAEA,SAAS,gCAAiC,IAAM,CAC9C,GAAG,4EAA6E,IAAM,CACpF,MAAMS,EAAMJ,EAAkBH,EAAiB,CAAC,EAChD,OAAOP,EAA8Bc,EAAKF,EAAW,CAAC,CAAC,EAAE,KAAK,EAAI,CACpE,CAAC,EAED,GAAG,+CAAgD,IAAM,CACvD,MAAME,EAAMJ,EACVH,EAAiB,CAAE,QAASZ,EAAc,MAAO,CAAC,CACpD,EACA,OAAOK,EAA8Bc,EAAKF,EAAW,CAAC,CAAC,EAAE,KAAK,EAAK,CACrE,CAAC,EAGD,GAAG,0DAA2D,IAAM,CAClE,MAAME,EAAMJ,EAAkBH,EAAiB,CAAC,EAChD,OACEP,EACEc,EACAF,EAAWd,EAAmBQ,CAAiB,CACjD,CACF,EAAE,KAAK,EAAK,CACd,CAAC,EAED,GAAG,4DAA6D,IAAM,CACpE,MAAMQ,EAAMJ,EAAkBH,EAAiB,CAAC,EAChD,OAAOP,EAA8Bc,EAAKF,EAAW,SAAS,CAAC,CAAC,EAAE,KAChE,EACF,CACF,CAAC,EAGD,GAAG,yDAA0D,IAAM,CACjE,MAAME,EAAMJ,EACVH,EAAiB,CAAE,UAAWD,CAAkB,CAAC,CACnD,EACA,OAAON,EAA8Bc,EAAKF,EAAW,CAAC,CAAC,EAAE,KAAK,EAAK,CACrE,CAAC,EAED,GAAG,2DAA4D,IAAM,CACnE,MAAME,EAAMJ,EAAkB,CAC5B,iBAAkBd,EAAuB,UACzC,aAAcC,EAAa,UAC3B,cAAeF,EAAc,IAC/B,CAAC,EACD,OAAOK,EAA8Bc,EAAKF,EAAW,CAAC,CAAC,EAAE,KAAK,EAAK,CACrE,CAAC,CACH,CAAC",
6
+ "names": ["DeviceModelId", "DeviceSessionStateType", "DeviceStatus", "ETHEREUM_APP_NAME", "resolveContactsVersionRequirements", "isContactsSupportedForSession", "flexSupport", "requirement", "MIN_OS_VERSION", "MIN_APP_VERSION", "version", "BELOW_ANY_VERSION", "createReadyState", "osVersion", "modelId", "createInternalApi", "deviceState", "runningApp", "name", "api"]
7
7
  }
@@ -0,0 +1,2 @@
1
+ const o=176,t=16,_=1,x=224,A=46,e=0,C=64,T=32,n=32,E=32,r={bitcoin:0,ethereum:1,solana:2,polkadot:3,cosmos:4,cardano:5};export{r as BLOCKCHAIN_FAMILY_BY_NAME,o as CONTACTS_APDU_CLA,t as CONTACTS_APDU_INS,C as GROUP_HANDLE_BYTES,E as HMAC_NAME_BYTES,T as HMAC_PROOF_BYTES,n as HMAC_REST_BYTES,x as RENAME_CONTACT_APDU_CLA,A as RENAME_CONTACT_APDU_INS,e as RENAME_CONTACT_P1,_ as SUB_CMD_REGISTER_IDENTITY};
2
+ //# sourceMappingURL=contactsConstants.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/internal/app-binder/model/contactsConstants.ts"],
4
+ "sourcesContent": ["/**\n * Protocol constants shared by the Contacts (Address Book) operations.\n *\n * Wire format and values follow the BOLOS SDK Address Book specification\n * (ledger-secure-sdk/app_features/address_book). Op-specific sub-command bytes\n * and response field sizes live here; TLV tags and struct-type bytes live in\n * {@link ./../services/contactsTlvSerializer}.\n */\n\n/** APDU class/instruction shared by every Address Book sub-command. */\nexport const CONTACTS_APDU_CLA = 0xb0;\nexport const CONTACTS_APDU_INS = 0x10;\n\n/** P1 sub-command selector for REGISTER IDENTITY (register external address). */\nexport const SUB_CMD_REGISTER_IDENTITY = 0x01;\n\n/**\n * EDIT CONTACT NAME (rename) is a blockchain-agnostic OS/dashboard command with\n * its own CLA/INS \u2014 NOT a sub-command of the address-book app APDU (0xB0/0x10).\n * The OS serves it directly, so it must run on the dashboard. P1 is a fixed\n * 0x00 (there is no sub-command selector for this command); P2 still carries the\n * chunk-continuation flag handled by {@link ./../services/sendFramedContactsPayload}.\n */\nexport const RENAME_CONTACT_APDU_CLA = 0xe0;\nexport const RENAME_CONTACT_APDU_INS = 0x2e;\nexport const RENAME_CONTACT_P1 = 0x00;\n\n/** Sizes (bytes) of the fields in the REGISTER IDENTITY final-chunk response. */\nexport const GROUP_HANDLE_BYTES = 64;\nexport const HMAC_PROOF_BYTES = 32;\nexport const HMAC_REST_BYTES = 32;\n\n/**\n * Size (bytes) of the rotated `hmac_name` returned on the EDIT CONTACT NAME\n * final-chunk response \u2014 the replacement group-level name proof.\n */\nexport const HMAC_NAME_BYTES = 32;\n\n/**\n * Blockchain-family byte encoded in the `BLOCKCHAIN_FAMILY` TLV, keyed by a\n * lowercase family name. v1 ships Ethereum only; the remaining families are\n * listed for completeness and will be enabled as their chains are supported.\n */\nexport const BLOCKCHAIN_FAMILY_BY_NAME: Readonly<Record<string, number>> = {\n bitcoin: 0x00,\n ethereum: 0x01,\n solana: 0x02,\n polkadot: 0x03,\n cosmos: 0x04,\n cardano: 0x05,\n};\n"],
5
+ "mappings": "AAUO,MAAMA,EAAoB,IACpBC,EAAoB,GAGpBC,EAA4B,EAS5BC,EAA0B,IAC1BC,EAA0B,GAC1BC,EAAoB,EAGpBC,EAAqB,GACrBC,EAAmB,GACnBC,EAAkB,GAMlBC,EAAkB,GAOlBC,EAA8D,CACzE,QAAS,EACT,SAAU,EACV,OAAQ,EACR,SAAU,EACV,OAAQ,EACR,QAAS,CACX",
6
+ "names": ["CONTACTS_APDU_CLA", "CONTACTS_APDU_INS", "SUB_CMD_REGISTER_IDENTITY", "RENAME_CONTACT_APDU_CLA", "RENAME_CONTACT_APDU_INS", "RENAME_CONTACT_P1", "GROUP_HANDLE_BYTES", "HMAC_PROOF_BYTES", "HMAC_REST_BYTES", "HMAC_NAME_BYTES", "BLOCKCHAIN_FAMILY_BY_NAME"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{DeviceExchangeError as o}from"@ledgerhq/device-management-kit";const t="6982",n={[t]:{message:"This address-book entry was registered with a different seed. Connect the Ledger device that registered it to modify this contact."},6800:{message:"Internal error (Please report)"},6983:{message:"Wrong data length"},6984:{message:"Unsupported address-book operation for this device or app configuration"},6985:{message:"Condition not satisfied"},"6a80":{message:"Invalid contact data, or operation refused on device"},"6a84":{message:"Insufficient memory: the device address book is full"},"6a88":{message:"Contact data not found"},"6af0":{message:"Invalid value for this contact entry"},"6b00":{message:"The app rejected the contact data"},"6f00":{message:"Technical problem (Internal error, please report)"}};class s extends o{constructor(r){super({tag:"ContactsCommandError",...r})}}const d=e=>new s(e);class c{constructor(r="The connected device or app does not meet the Contacts version requirements."){this.message=r}_tag="ContactsVersionRequirementError";originalError}export{n as CONTACTS_APP_ERRORS,t as CONTACT_SEED_MISMATCH_ERROR_CODE,s as ContactsCommandError,c as ContactsVersionRequirementError,d as contactsCommandErrorFactory};
2
+ //# sourceMappingURL=contactsErrors.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/internal/app-binder/model/contactsErrors.ts"],
4
+ "sourcesContent": ["import {\n type CommandErrorArgs,\n type CommandErrors,\n DeviceExchangeError,\n type DmkError,\n} from \"@ledgerhq/device-management-kit\";\n\n/**\n * Status word returned by the app's Address Book *edit* operations when the\n * seed-bound HMAC / group-handle verification fails \u2014 i.e. the entry was\n * registered with a different seed than the one currently on the device.\n *\n * The device runs this check *before* showing any UI and returns 0x6982 *only*\n * for seed-binding failures (user rejection and TLV errors return 0x6a80,\n * address validation returns 0x6af0), so 0x6982 is an unambiguous \"wrong seed\"\n * signal that consumers (e.g. Ledger Wallet) can catch to guide the user.\n */\nexport const CONTACT_SEED_MISMATCH_ERROR_CODE = \"6982\" as const;\n\nexport type ContactsErrorCodes =\n | \"6982\"\n | \"6800\"\n | \"6983\"\n | \"6984\"\n | \"6985\"\n | \"6a80\"\n | \"6a84\"\n | \"6a88\"\n | \"6af0\"\n | \"6b00\"\n | \"6f00\";\n\nexport const CONTACTS_APP_ERRORS: CommandErrors<ContactsErrorCodes> = {\n [CONTACT_SEED_MISMATCH_ERROR_CODE]: {\n message:\n \"This address-book entry was registered with a different seed. Connect the Ledger device that registered it to modify this contact.\",\n },\n \"6800\": { message: \"Internal error (Please report)\" },\n \"6983\": { message: \"Wrong data length\" },\n \"6984\": {\n message:\n \"Unsupported address-book operation for this device or app configuration\",\n },\n \"6985\": { message: \"Condition not satisfied\" },\n \"6a80\": { message: \"Invalid contact data, or operation refused on device\" },\n \"6a84\": { message: \"Insufficient memory: the device address book is full\" },\n \"6a88\": { message: \"Contact data not found\" },\n \"6af0\": { message: \"Invalid value for this contact entry\" },\n \"6b00\": { message: \"The app rejected the contact data\" },\n \"6f00\": { message: \"Technical problem (Internal error, please report)\" },\n};\n\nexport class ContactsCommandError extends DeviceExchangeError<ContactsErrorCodes> {\n constructor(args: CommandErrorArgs<ContactsErrorCodes>) {\n super({ tag: \"ContactsCommandError\", ...args });\n }\n}\n\nexport const contactsCommandErrorFactory = (\n args: CommandErrorArgs<ContactsErrorCodes>,\n) => new ContactsCommandError(args);\n\n/**\n * Raised by a Contacts `DeviceAction` when the connected device model, app\n * version, or OS version does not meet the Contacts requirements \u2014 i.e. the\n * DSDK-1376 support check fails before any APDU is sent.\n */\nexport class ContactsVersionRequirementError implements DmkError {\n readonly _tag = \"ContactsVersionRequirementError\";\n readonly originalError?: unknown;\n constructor(\n readonly message = \"The connected device or app does not meet the Contacts version requirements.\",\n ) {}\n}\n"],
5
+ "mappings": "AAAA,OAGE,uBAAAA,MAEK,kCAYA,MAAMC,EAAmC,OAenCC,EAAyD,CACpE,CAACD,CAAgC,EAAG,CAClC,QACE,oIACJ,EACA,KAAQ,CAAE,QAAS,gCAAiC,EACpD,KAAQ,CAAE,QAAS,mBAAoB,EACvC,KAAQ,CACN,QACE,yEACJ,EACA,KAAQ,CAAE,QAAS,yBAA0B,EAC7C,OAAQ,CAAE,QAAS,sDAAuD,EAC1E,OAAQ,CAAE,QAAS,sDAAuD,EAC1E,OAAQ,CAAE,QAAS,wBAAyB,EAC5C,OAAQ,CAAE,QAAS,sCAAuC,EAC1D,OAAQ,CAAE,QAAS,mCAAoC,EACvD,OAAQ,CAAE,QAAS,mDAAoD,CACzE,EAEO,MAAME,UAA6BH,CAAwC,CAChF,YAAYI,EAA4C,CACtD,MAAM,CAAE,IAAK,uBAAwB,GAAGA,CAAK,CAAC,CAChD,CACF,CAEO,MAAMC,EACXD,GACG,IAAID,EAAqBC,CAAI,EAO3B,MAAME,CAAoD,CAG/D,YACWC,EAAU,+EACnB,CADS,aAAAA,CACR,CAJM,KAAO,kCACP,aAIX",
6
+ "names": ["DeviceExchangeError", "CONTACT_SEED_MISMATCH_ERROR_CODE", "CONTACTS_APP_ERRORS", "ContactsCommandError", "args", "contactsCommandErrorFactory", "ContactsVersionRequirementError", "message"]
7
+ }
@@ -0,0 +1,2 @@
1
+ const w=33,b=33,x=64,l=32,d=10,$=20;class i extends Error{_tag="ContactsValidationError";originalError;constructor(n){super(n),this.name="ContactsValidationError"}}const m=32,p=126,y=new TextEncoder;function E(t,n){const{field:r,bufferLength:e}=n;if(!t)throw new i(`${r} must not be empty (firmware rejects empty strings).`);const o=y.encode(t),s=e-1;if(o.length>s)throw new i(`${r} ${JSON.stringify(t)} is too long: ${o.length} bytes encoded (max ${s}). The device buffer is ${e} bytes including the trailing NUL.`);const f=[];for(let c=0;c<o.length;c++){const a=o[c];(a<m||a>p)&&f.push(c)}if(f.length>0){const a=Array.from(new Set(f.map(g=>o[g]))).sort((g,h)=>g-h).map(g=>`0x${g.toString(16).padStart(2,"0")}`).join(", ");throw new i(`${r} ${JSON.stringify(t)} contains ${f.length} non-ASCII byte(s) at position(s) [${f.join(", ")}]: ${a}. The device buffer is byte-level isprint() \u2014 only 0x20-0x7E are accepted, no UTF-8 / accents / emoji.`)}}function N(t,n){const{field:r,expectedBytes:e}=n;if(t.length!==e)throw new i(`${r} is ${t.length} bytes, expected ${e}.`)}function u(t){if(!t)throw new i(`derivationPath must not be empty (e.g. "m/44'/60'/0'/0/0").`);const n=t.split("/"),r=n[0]==="m"||n[0]==="M"?n.slice(1):n;if(r.length===0)throw new i(`derivationPath ${JSON.stringify(t)} has no segments past 'm'.`);if(r.length>d)throw new i(`derivationPath ${JSON.stringify(t)} has ${r.length} segments (max ${d}). Firmware rejects deeper paths.`);for(const e of r){const o=e.endsWith("'")?e.slice(0,-1):e;if(!o||!/^\d+$/.test(o))throw new i(`derivationPath ${JSON.stringify(t)} segment ${JSON.stringify(e)} is not numeric. Use plain integers, optionally suffixed with ' for hardened.`);const s=Number(o);if(!Number.isSafeInteger(s)||s>=2147483648)throw new i(`derivationPath ${JSON.stringify(t)} segment ${JSON.stringify(e)} >= 2^31. Use the ' suffix for hardening rather than overflowing.`)}}function I(t){const n=typeof t=="number";if(!n&&!(typeof t=="bigint"))throw new i(`chainId must be number or bigint, got ${typeof t}.`);if(n&&!Number.isInteger(t))throw new i(`chainId must be an integer, got ${t}.`);const e=typeof t=="bigint"?t:BigInt(t);if(e<=0n)throw new i(`chainId must be positive, got ${t}.`);const o=(1n<<64n)-1n;if(e>o)throw new i(`chainId ${t} exceeds uint64 range (max ${o}).`)}export{w as CONTACT_NAME_BUFFER_LENGTH,i as ContactsValidationError,$ as ETH_ADDRESS_BYTES,x as GROUP_HANDLE_SIZE,l as HMAC_PROOF_LENGTH,d as MAX_BIP32_DEPTH,b as SCOPE_BUFFER_LENGTH,N as validateByteLength,I as validateChainId,u as validateDerivationPath,E as validatePrintableLabel};
2
+ //# sourceMappingURL=contactsValidation.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/internal/app-binder/model/contactsValidation.ts"],
4
+ "sourcesContent": ["/**\n * Pre-APDU input validators for the Contacts operations.\n *\n * Buffer-length constants mirror the Ledger secure SDK `#define` values\n * *including* the trailing NUL terminator (so a 32-printable-char name lives in\n * a 33-byte buffer). Validators subtract 1 internally so the constants remain\n * pure equality checks against the upstream `#define`s. The byte-level\n * `0x20..0x7E` printable check exactly matches firmware `is_printable_string`.\n */\nimport { type DmkError } from \"@ledgerhq/device-management-kit\";\n\n// ---- SDK-mirrored constants -------------------------------------------------\n// Source of truth: ledger-secure-sdk/app_features/address_book/include/\n\nexport const CONTACT_NAME_BUFFER_LENGTH = 33; // identity.h:49 (incl. NUL)\nexport const SCOPE_BUFFER_LENGTH = 33; // identity.h:50 (incl. NUL)\nexport const GROUP_HANDLE_SIZE = 64; // identity.h:47\nexport const HMAC_PROOF_LENGTH = 32; // CX_SHA256_SIZE\nexport const MAX_BIP32_DEPTH = 10; // bip32.h:15 (MAX_BIP32_PATH)\nexport const ETH_ADDRESS_BYTES = 20; // protocol convention, no header\n\n// ---- error type -------------------------------------------------------------\n\n/**\n * Raised when caller input fails a pre-APDU check. Implements `DmkError` (and\n * stays an `Error` subclass) so a Contacts device action can surface it as a\n * typed terminal error state on the observable rather than throwing out of the\n * public, device-action-returning API.\n */\nexport class ContactsValidationError extends Error implements DmkError {\n readonly _tag = \"ContactsValidationError\";\n readonly originalError?: unknown;\n constructor(message: string) {\n super(message);\n this.name = \"ContactsValidationError\";\n }\n}\n\n// ---- printable-string validation -------------------------------------------\n\nconst PRINTABLE_MIN = 0x20;\nconst PRINTABLE_MAX = 0x7e;\n\nconst utf8Encoder = new TextEncoder();\n\n/**\n * Mirror of firmware `is_printable_string` + length cap. `bufferLength` is the\n * firmware's fixed buffer size *including* the trailing NUL, so the maximum\n * payload byte length is `bufferLength - 1`.\n */\nexport function validatePrintableLabel(\n value: string,\n options: { field: string; bufferLength: number },\n): void {\n const { field, bufferLength } = options;\n if (!value) {\n throw new ContactsValidationError(\n `${field} must not be empty (firmware rejects empty strings).`,\n );\n }\n const encoded = utf8Encoder.encode(value);\n const maxBytes = bufferLength - 1;\n if (encoded.length > maxBytes) {\n throw new ContactsValidationError(\n `${field} ${JSON.stringify(value)} is too long: ${encoded.length} bytes ` +\n `encoded (max ${maxBytes}). The device buffer is ${bufferLength} bytes ` +\n `including the trailing NUL.`,\n );\n }\n const badPositions: number[] = [];\n for (let i = 0; i < encoded.length; i++) {\n const byte = encoded[i]!;\n if (byte < PRINTABLE_MIN || byte > PRINTABLE_MAX) {\n badPositions.push(i);\n }\n }\n if (badPositions.length > 0) {\n const offendingBytes = Array.from(\n new Set(badPositions.map((i) => encoded[i]!)),\n ).sort((a, b) => a - b);\n const offendingHex = offendingBytes\n .map((b) => `0x${b.toString(16).padStart(2, \"0\")}`)\n .join(\", \");\n throw new ContactsValidationError(\n `${field} ${JSON.stringify(value)} contains ${badPositions.length} ` +\n `non-ASCII byte(s) at position(s) [${badPositions.join(\", \")}]: ` +\n `${offendingHex}. The device buffer is byte-level isprint() \u2014 only ` +\n `0x20-0x7E are accepted, no UTF-8 / accents / emoji.`,\n );\n }\n}\n\n// ---- byte-buffer validation ------------------------------------------------\n\n/** Assert a byte buffer is exactly `expectedBytes` long and non-empty. */\nexport function validateByteLength(\n value: Uint8Array,\n options: { field: string; expectedBytes: number },\n): void {\n const { field, expectedBytes } = options;\n if (value.length !== expectedBytes) {\n throw new ContactsValidationError(\n `${field} is ${value.length} bytes, expected ${expectedBytes}.`,\n );\n }\n}\n\n// ---- BIP32 path validation -------------------------------------------------\n\n/**\n * Sanity-check a BIP32 path before serialization. Firmware enforces depth <=\n * MAX_BIP32_DEPTH and rejects malformed segments. Accepts `m/44'/60'/0'/0/0`\n * style; segments may end in `'` (hardened).\n */\nexport function validateDerivationPath(path: string): void {\n if (!path) {\n throw new ContactsValidationError(\n `derivationPath must not be empty (e.g. \"m/44'/60'/0'/0/0\").`,\n );\n }\n const parts = path.split(\"/\");\n const segments =\n parts[0] === \"m\" || parts[0] === \"M\" ? parts.slice(1) : parts;\n if (segments.length === 0) {\n throw new ContactsValidationError(\n `derivationPath ${JSON.stringify(path)} has no segments past 'm'.`,\n );\n }\n if (segments.length > MAX_BIP32_DEPTH) {\n throw new ContactsValidationError(\n `derivationPath ${JSON.stringify(path)} has ${segments.length} segments ` +\n `(max ${MAX_BIP32_DEPTH}). Firmware rejects deeper paths.`,\n );\n }\n for (const segment of segments) {\n const token = segment.endsWith(\"'\") ? segment.slice(0, -1) : segment;\n if (!token || !/^\\d+$/.test(token)) {\n throw new ContactsValidationError(\n `derivationPath ${JSON.stringify(path)} segment ${JSON.stringify(segment)} ` +\n `is not numeric. Use plain integers, optionally suffixed with ' for hardened.`,\n );\n }\n const n = Number(token);\n if (!Number.isSafeInteger(n) || n >= 0x80000000) {\n throw new ContactsValidationError(\n `derivationPath ${JSON.stringify(path)} segment ${JSON.stringify(segment)} ` +\n `>= 2^31. Use the ' suffix for hardening rather than overflowing.`,\n );\n }\n }\n}\n\n// ---- chain id validation ---------------------------------------------------\n\n/**\n * Firmware encodes chain_id as uint64. JS `number` is fine up to 2^53; accept\n * `bigint` for the high range so callers don't silently truncate.\n */\nexport function validateChainId(value: number | bigint): void {\n const isNumber = typeof value === \"number\";\n const isBigInt = typeof value === \"bigint\";\n if (!isNumber && !isBigInt) {\n throw new ContactsValidationError(\n `chainId must be number or bigint, got ${typeof value}.`,\n );\n }\n if (isNumber && !Number.isInteger(value)) {\n throw new ContactsValidationError(\n `chainId must be an integer, got ${value}.`,\n );\n }\n const v = typeof value === \"bigint\" ? value : BigInt(value);\n if (v <= 0n) {\n throw new ContactsValidationError(\n `chainId must be positive, got ${value}.`,\n );\n }\n const UINT64_MAX = (1n << 64n) - 1n;\n if (v > UINT64_MAX) {\n throw new ContactsValidationError(\n `chainId ${value} exceeds uint64 range (max ${UINT64_MAX}).`,\n );\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["CONTACT_NAME_BUFFER_LENGTH", "SCOPE_BUFFER_LENGTH", "GROUP_HANDLE_SIZE", "HMAC_PROOF_LENGTH", "MAX_BIP32_DEPTH", "ETH_ADDRESS_BYTES", "ContactsValidationError", "message", "PRINTABLE_MIN", "PRINTABLE_MAX", "utf8Encoder", "validatePrintableLabel", "value", "options", "field", "bufferLength", "encoded", "maxBytes", "badPositions", "i", "byte", "offendingHex", "a", "b", "validateByteLength", "expectedBytes", "validateDerivationPath", "path", "parts", "segments", "segment", "token", "n", "validateChainId", "isNumber", "v", "UINT64_MAX"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{CONTACT_NAME_BUFFER_LENGTH as n,ContactsValidationError as e,validateByteLength as o,validateChainId as t,validateDerivationPath as r,validatePrintableLabel as a}from"./contactsValidation";const c="44'/60'/0'/0/0";describe("contactsValidation",()=>{describe("validatePrintableLabel",()=>{const i={field:"name",bufferLength:n};it("accepts a printable ASCII label within the buffer",()=>{expect(()=>a("Alice",i)).not.toThrow()}),it("rejects an empty label",()=>{expect(()=>a("",i)).toThrow(e)}),it("rejects a label exceeding bufferLength - 1 bytes",()=>{expect(()=>a("x".repeat(n),i)).toThrow(e)}),it("rejects non-ASCII / accented characters (byte-level isprint)",()=>{expect(()=>a("Am\xE9lie",i)).toThrow(e)})}),describe("validateByteLength",()=>{it("accepts an exact-length buffer",()=>{expect(()=>o(new Uint8Array(20),{field:"identifier",expectedBytes:20})).not.toThrow()}),it("rejects a wrong-length buffer",()=>{expect(()=>o(new Uint8Array(19),{field:"identifier",expectedBytes:20})).toThrow(e)})}),describe("validateChainId",()=>{it("accepts a positive integer and bigint",()=>{expect(()=>t(1)).not.toThrow(),expect(()=>t(56n)).not.toThrow()}),it("rejects zero, negative, and non-integer",()=>{expect(()=>t(0)).toThrow(e),expect(()=>t(-1)).toThrow(e),expect(()=>t(1.5)).toThrow(e)})}),describe("validateDerivationPath",()=>{it("accepts a representative BIP32 path",()=>{expect(()=>r(c)).not.toThrow()}),it("accepts m-prefixed and hardened paths",()=>{expect(()=>r("m/44'/60'/0'/0/0")).not.toThrow()}),it("rejects a non-numeric segment",()=>{expect(()=>r("44'/x/0")).toThrow(e)})})});
2
+ //# sourceMappingURL=contactsValidation.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/internal/app-binder/model/contactsValidation.test.ts"],
4
+ "sourcesContent": ["import {\n CONTACT_NAME_BUFFER_LENGTH,\n ContactsValidationError,\n validateByteLength,\n validateChainId,\n validateDerivationPath,\n validatePrintableLabel,\n} from \"./contactsValidation\";\n\n// A representative BIP32 path (used by Ledger-Account ops, not external\n// addresses). Kept here to exercise the shared validator.\nconst SAMPLE_DERIVATION_PATH = \"44'/60'/0'/0/0\";\n\ndescribe(\"contactsValidation\", () => {\n describe(\"validatePrintableLabel\", () => {\n const opts = { field: \"name\", bufferLength: CONTACT_NAME_BUFFER_LENGTH };\n\n it(\"accepts a printable ASCII label within the buffer\", () => {\n expect(() => validatePrintableLabel(\"Alice\", opts)).not.toThrow();\n });\n\n it(\"rejects an empty label\", () => {\n expect(() => validatePrintableLabel(\"\", opts)).toThrow(\n ContactsValidationError,\n );\n });\n\n it(\"rejects a label exceeding bufferLength - 1 bytes\", () => {\n expect(() =>\n validatePrintableLabel(\"x\".repeat(CONTACT_NAME_BUFFER_LENGTH), opts),\n ).toThrow(ContactsValidationError);\n });\n\n it(\"rejects non-ASCII / accented characters (byte-level isprint)\", () => {\n expect(() => validatePrintableLabel(\"Am\u00E9lie\", opts)).toThrow(\n ContactsValidationError,\n );\n });\n });\n\n describe(\"validateByteLength\", () => {\n it(\"accepts an exact-length buffer\", () => {\n expect(() =>\n validateByteLength(new Uint8Array(20), {\n field: \"identifier\",\n expectedBytes: 20,\n }),\n ).not.toThrow();\n });\n\n it(\"rejects a wrong-length buffer\", () => {\n expect(() =>\n validateByteLength(new Uint8Array(19), {\n field: \"identifier\",\n expectedBytes: 20,\n }),\n ).toThrow(ContactsValidationError);\n });\n });\n\n describe(\"validateChainId\", () => {\n it(\"accepts a positive integer and bigint\", () => {\n expect(() => validateChainId(1)).not.toThrow();\n expect(() => validateChainId(56n)).not.toThrow();\n });\n\n it(\"rejects zero, negative, and non-integer\", () => {\n expect(() => validateChainId(0)).toThrow(ContactsValidationError);\n expect(() => validateChainId(-1)).toThrow(ContactsValidationError);\n expect(() => validateChainId(1.5)).toThrow(ContactsValidationError);\n });\n });\n\n describe(\"validateDerivationPath\", () => {\n it(\"accepts a representative BIP32 path\", () => {\n expect(() =>\n validateDerivationPath(SAMPLE_DERIVATION_PATH),\n ).not.toThrow();\n });\n\n it(\"accepts m-prefixed and hardened paths\", () => {\n expect(() => validateDerivationPath(\"m/44'/60'/0'/0/0\")).not.toThrow();\n });\n\n it(\"rejects a non-numeric segment\", () => {\n expect(() => validateDerivationPath(\"44'/x/0\")).toThrow(\n ContactsValidationError,\n );\n });\n });\n});\n"],
5
+ "mappings": "AAAA,OACE,8BAAAA,EACA,2BAAAC,EACA,sBAAAC,EACA,mBAAAC,EACA,0BAAAC,EACA,0BAAAC,MACK,uBAIP,MAAMC,EAAyB,iBAE/B,SAAS,qBAAsB,IAAM,CACnC,SAAS,yBAA0B,IAAM,CACvC,MAAMC,EAAO,CAAE,MAAO,OAAQ,aAAcP,CAA2B,EAEvE,GAAG,oDAAqD,IAAM,CAC5D,OAAO,IAAMK,EAAuB,QAASE,CAAI,CAAC,EAAE,IAAI,QAAQ,CAClE,CAAC,EAED,GAAG,yBAA0B,IAAM,CACjC,OAAO,IAAMF,EAAuB,GAAIE,CAAI,CAAC,EAAE,QAC7CN,CACF,CACF,CAAC,EAED,GAAG,mDAAoD,IAAM,CAC3D,OAAO,IACLI,EAAuB,IAAI,OAAOL,CAA0B,EAAGO,CAAI,CACrE,EAAE,QAAQN,CAAuB,CACnC,CAAC,EAED,GAAG,+DAAgE,IAAM,CACvE,OAAO,IAAMI,EAAuB,YAAUE,CAAI,CAAC,EAAE,QACnDN,CACF,CACF,CAAC,CACH,CAAC,EAED,SAAS,qBAAsB,IAAM,CACnC,GAAG,iCAAkC,IAAM,CACzC,OAAO,IACLC,EAAmB,IAAI,WAAW,EAAE,EAAG,CACrC,MAAO,aACP,cAAe,EACjB,CAAC,CACH,EAAE,IAAI,QAAQ,CAChB,CAAC,EAED,GAAG,gCAAiC,IAAM,CACxC,OAAO,IACLA,EAAmB,IAAI,WAAW,EAAE,EAAG,CACrC,MAAO,aACP,cAAe,EACjB,CAAC,CACH,EAAE,QAAQD,CAAuB,CACnC,CAAC,CACH,CAAC,EAED,SAAS,kBAAmB,IAAM,CAChC,GAAG,wCAAyC,IAAM,CAChD,OAAO,IAAME,EAAgB,CAAC,CAAC,EAAE,IAAI,QAAQ,EAC7C,OAAO,IAAMA,EAAgB,GAAG,CAAC,EAAE,IAAI,QAAQ,CACjD,CAAC,EAED,GAAG,0CAA2C,IAAM,CAClD,OAAO,IAAMA,EAAgB,CAAC,CAAC,EAAE,QAAQF,CAAuB,EAChE,OAAO,IAAME,EAAgB,EAAE,CAAC,EAAE,QAAQF,CAAuB,EACjE,OAAO,IAAME,EAAgB,GAAG,CAAC,EAAE,QAAQF,CAAuB,CACpE,CAAC,CACH,CAAC,EAED,SAAS,yBAA0B,IAAM,CACvC,GAAG,sCAAuC,IAAM,CAC9C,OAAO,IACLG,EAAuBE,CAAsB,CAC/C,EAAE,IAAI,QAAQ,CAChB,CAAC,EAED,GAAG,wCAAyC,IAAM,CAChD,OAAO,IAAMF,EAAuB,kBAAkB,CAAC,EAAE,IAAI,QAAQ,CACvE,CAAC,EAED,GAAG,gCAAiC,IAAM,CACxC,OAAO,IAAMA,EAAuB,SAAS,CAAC,EAAE,QAC9CH,CACF,CACF,CAAC,CACH,CAAC,CACH,CAAC",
6
+ "names": ["CONTACT_NAME_BUFFER_LENGTH", "ContactsValidationError", "validateByteLength", "validateChainId", "validateDerivationPath", "validatePrintableLabel", "SAMPLE_DERIVATION_PATH", "opts"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{ByteArrayBuilder as f}from"@ledgerhq/device-management-kit";const i=33024,_={STRUCT_TYPE:1,STRUCT_VERSION:2,DERIVATION_PATH:105,CHAIN_ID:35,HMAC_PROOF:41,BLOCKCHAIN_FAMILY:81,CONTACT_NAME:240,SCOPE:241,ACCOUNT_IDENTIFIER:242,PREVIOUS_CONTACT_NAME:243,PREVIOUS_IDENTIFIER:244,PREVIOUS_SCOPE:245,GROUP_HANDLE:246,HMAC_REST:247},I=45,d=46,C=47,A=48,U=49,c=50,s=51,R=52,B=1;function r(t,n){if(n<128)t.add8BitUIntToData(n);else if(n<=255)t.add16BitUIntToData(i+n);else throw new Error(`TLV tag ${n} exceeds supported single-byte range`)}function o(t,n){if(n<128)t.add8BitUIntToData(n);else if(n<=255)t.add8BitUIntToData(129),t.add8BitUIntToData(n);else if(n<=65535)t.add8BitUIntToData(130),t.add16BitUIntToData(n);else throw new Error(`TLV length ${n} exceeds supported range`)}function a(t){const n=typeof t=="bigint"?t:BigInt(t);if(n<=0n)throw new Error(`chainId must be positive, got ${t}`);if(n>0xffffffffffffffffn)throw new Error(`chainId ${t} exceeds uint64 range`);const e=[];let T=n;for(;T>0n;)e.unshift(Number(T&0xffn)),T>>=8n;return Uint8Array.from(e)}function D(t,n,e){r(t,n),t.add8BitUIntToData(1),t.add8BitUIntToData(e)}function u(t,n,e){r(t,n);const T=new TextEncoder().encode(e);o(t,T.length),t.addBufferToData(T)}function E(t,n,e){r(t,n),o(t,e.length),t.addBufferToData(e)}function O(t,n,e){E(t,n,a(e))}function N(t){const n=new f;return n.add8BitUIntToData(t.length),t.forEach(e=>n.add32BitUIntToData(e)),n.build()}export{_ as CONTACTS_TLV_TAG,d as STRUCT_TYPE_EDIT_CONTACT_NAME,U as STRUCT_TYPE_EDIT_IDENTIFIER,A as STRUCT_TYPE_EDIT_LEDGER_ACCOUNT,c as STRUCT_TYPE_EDIT_SCOPE,s as STRUCT_TYPE_PROVIDE_CONTACT,R as STRUCT_TYPE_PROVIDE_LEDGER_ACCOUNT_CONTACT,I as STRUCT_TYPE_REGISTER_IDENTITY,C as STRUCT_TYPE_REGISTER_LEDGER_ACCOUNT,B as STRUCT_VERSION_VALUE,u as encodeTlvAscii,E as encodeTlvBuffer,O as encodeTlvChainId,D as encodeTlvUInt8,N as packDerivationPath};
2
+ //# sourceMappingURL=contactsTlvSerializer.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/internal/app-binder/services/contactsTlvSerializer.ts"],
4
+ "sourcesContent": ["// TLV encoding for the Contacts feature.\n// The SDK's TLV parser (lib_tlv/tlv_library.c, get_der_value_as_uint32) reads\n// tags as DER values: a byte with the high bit set means \"long form, low 7 bits\n// = following byte count\". So tags >= 0x80 (e.g. 0xf0) MUST be sent as the\n// 2-byte form [0x81, tag]; a bare 0xf0 is mis-read as a 112-byte tag. Lengths\n// >= 0x80 likewise use DER long form. (Verified on-device: a raw one-byte tag\n// parses as garbage and the app rejects the payload with 0x6a80.)\nimport { ByteArrayBuilder } from \"@ledgerhq/device-management-kit\";\n\nconst LONG_TAG_PREFIX_HIGH_BYTE = 0x81 << 8;\n\nexport const CONTACTS_TLV_TAG = {\n STRUCT_TYPE: 0x01,\n STRUCT_VERSION: 0x02,\n // Ledger-Account operations only; external-address ops carry no path.\n // TODO(Ledger Account ticket): spec tag registry lists 0x69 but the per-op\n // payload tables list 0x21 \u2014 confirm before the Ledger-Account op ships.\n DERIVATION_PATH: 0x69,\n CHAIN_ID: 0x23,\n HMAC_PROOF: 0x29,\n BLOCKCHAIN_FAMILY: 0x51,\n CONTACT_NAME: 0xf0,\n SCOPE: 0xf1,\n ACCOUNT_IDENTIFIER: 0xf2,\n PREVIOUS_CONTACT_NAME: 0xf3,\n PREVIOUS_IDENTIFIER: 0xf4,\n PREVIOUS_SCOPE: 0xf5,\n GROUP_HANDLE: 0xf6,\n HMAC_REST: 0xf7,\n} as const;\n\nexport const STRUCT_TYPE_REGISTER_IDENTITY = 0x2d;\nexport const STRUCT_TYPE_EDIT_CONTACT_NAME = 0x2e;\nexport const STRUCT_TYPE_REGISTER_LEDGER_ACCOUNT = 0x2f;\nexport const STRUCT_TYPE_EDIT_LEDGER_ACCOUNT = 0x30;\nexport const STRUCT_TYPE_EDIT_IDENTIFIER = 0x31;\nexport const STRUCT_TYPE_EDIT_SCOPE = 0x32;\nexport const STRUCT_TYPE_PROVIDE_CONTACT = 0x33;\nexport const STRUCT_TYPE_PROVIDE_LEDGER_ACCOUNT_CONTACT = 0x34;\nexport const STRUCT_VERSION_VALUE = 0x01;\n\nfunction writeTag(builder: ByteArrayBuilder, tag: number): void {\n if (tag < 0x80) {\n builder.add8BitUIntToData(tag);\n } else if (tag <= 0xff) {\n // DER long-form: 0x81 (1 following byte) then the tag value.\n builder.add16BitUIntToData(LONG_TAG_PREFIX_HIGH_BYTE + tag);\n } else {\n throw new Error(`TLV tag ${tag} exceeds supported single-byte range`);\n }\n}\n\nfunction writeDerLength(builder: ByteArrayBuilder, length: number): void {\n if (length < 0x80) {\n builder.add8BitUIntToData(length);\n } else if (length <= 0xff) {\n builder.add8BitUIntToData(0x81);\n builder.add8BitUIntToData(length);\n } else if (length <= 0xffff) {\n builder.add8BitUIntToData(0x82);\n builder.add16BitUIntToData(length);\n } else {\n throw new Error(`TLV length ${length} exceeds supported range`);\n }\n}\n\nfunction chainIdToMinimalBytes(chainId: number | bigint): Uint8Array {\n const big = typeof chainId === \"bigint\" ? chainId : BigInt(chainId);\n if (big <= 0n) {\n throw new Error(`chainId must be positive, got ${chainId}`);\n }\n if (big > 0xffffffffffffffffn) {\n throw new Error(`chainId ${chainId} exceeds uint64 range`);\n }\n const bytes: number[] = [];\n let n = big;\n while (n > 0n) {\n bytes.unshift(Number(n & 0xffn));\n n >>= 8n;\n }\n return Uint8Array.from(bytes);\n}\n\nexport function encodeTlvUInt8(\n builder: ByteArrayBuilder,\n tag: number,\n value: number,\n): void {\n writeTag(builder, tag);\n builder.add8BitUIntToData(1);\n builder.add8BitUIntToData(value);\n}\n\nexport function encodeTlvAscii(\n builder: ByteArrayBuilder,\n tag: number,\n value: string,\n): void {\n writeTag(builder, tag);\n const bytes = new TextEncoder().encode(value);\n writeDerLength(builder, bytes.length);\n builder.addBufferToData(bytes);\n}\n\nexport function encodeTlvBuffer(\n builder: ByteArrayBuilder,\n tag: number,\n value: Uint8Array,\n): void {\n writeTag(builder, tag);\n writeDerLength(builder, value.length);\n builder.addBufferToData(value);\n}\n\nexport function encodeTlvChainId(\n builder: ByteArrayBuilder,\n tag: number,\n chainId: number | bigint,\n): void {\n encodeTlvBuffer(builder, tag, chainIdToMinimalBytes(chainId));\n}\n\n/** Pack BIP32 segments as `count(1) || segment(4 BE)*count`. */\nexport function packDerivationPath(segments: number[]): Uint8Array {\n const builder = new ByteArrayBuilder();\n builder.add8BitUIntToData(segments.length);\n segments.forEach((segment) => builder.add32BitUIntToData(segment));\n return builder.build();\n}\n"],
5
+ "mappings": "AAOA,OAAS,oBAAAA,MAAwB,kCAEjC,MAAMC,EAA4B,MAErBC,EAAmB,CAC9B,YAAa,EACb,eAAgB,EAIhB,gBAAiB,IACjB,SAAU,GACV,WAAY,GACZ,kBAAmB,GACnB,aAAc,IACd,MAAO,IACP,mBAAoB,IACpB,sBAAuB,IACvB,oBAAqB,IACrB,eAAgB,IAChB,aAAc,IACd,UAAW,GACb,EAEaC,EAAgC,GAChCC,EAAgC,GAChCC,EAAsC,GACtCC,EAAkC,GAClCC,EAA8B,GAC9BC,EAAyB,GACzBC,EAA8B,GAC9BC,EAA6C,GAC7CC,EAAuB,EAEpC,SAASC,EAASC,EAA2BC,EAAmB,CAC9D,GAAIA,EAAM,IACRD,EAAQ,kBAAkBC,CAAG,UACpBA,GAAO,IAEhBD,EAAQ,mBAAmBZ,EAA4Ba,CAAG,MAE1D,OAAM,IAAI,MAAM,WAAWA,CAAG,sCAAsC,CAExE,CAEA,SAASC,EAAeF,EAA2BG,EAAsB,CACvE,GAAIA,EAAS,IACXH,EAAQ,kBAAkBG,CAAM,UACvBA,GAAU,IACnBH,EAAQ,kBAAkB,GAAI,EAC9BA,EAAQ,kBAAkBG,CAAM,UACvBA,GAAU,MACnBH,EAAQ,kBAAkB,GAAI,EAC9BA,EAAQ,mBAAmBG,CAAM,MAEjC,OAAM,IAAI,MAAM,cAAcA,CAAM,0BAA0B,CAElE,CAEA,SAASC,EAAsBC,EAAsC,CACnE,MAAMC,EAAM,OAAOD,GAAY,SAAWA,EAAU,OAAOA,CAAO,EAClE,GAAIC,GAAO,GACT,MAAM,IAAI,MAAM,iCAAiCD,CAAO,EAAE,EAE5D,GAAIC,EAAM,oBACR,MAAM,IAAI,MAAM,WAAWD,CAAO,uBAAuB,EAE3D,MAAME,EAAkB,CAAC,EACzB,IAAIC,EAAIF,EACR,KAAOE,EAAI,IACTD,EAAM,QAAQ,OAAOC,EAAI,KAAK,CAAC,EAC/BA,IAAM,GAER,OAAO,WAAW,KAAKD,CAAK,CAC9B,CAEO,SAASE,EACdT,EACAC,EACAS,EACM,CACNX,EAASC,EAASC,CAAG,EACrBD,EAAQ,kBAAkB,CAAC,EAC3BA,EAAQ,kBAAkBU,CAAK,CACjC,CAEO,SAASC,EACdX,EACAC,EACAS,EACM,CACNX,EAASC,EAASC,CAAG,EACrB,MAAMM,EAAQ,IAAI,YAAY,EAAE,OAAOG,CAAK,EAC5CR,EAAeF,EAASO,EAAM,MAAM,EACpCP,EAAQ,gBAAgBO,CAAK,CAC/B,CAEO,SAASK,EACdZ,EACAC,EACAS,EACM,CACNX,EAASC,EAASC,CAAG,EACrBC,EAAeF,EAASU,EAAM,MAAM,EACpCV,EAAQ,gBAAgBU,CAAK,CAC/B,CAEO,SAASG,EACdb,EACAC,EACAI,EACM,CACNO,EAAgBZ,EAASC,EAAKG,EAAsBC,CAAO,CAAC,CAC9D,CAGO,SAASS,EAAmBC,EAAgC,CACjE,MAAMf,EAAU,IAAIb,EACpB,OAAAa,EAAQ,kBAAkBe,EAAS,MAAM,EACzCA,EAAS,QAASC,GAAYhB,EAAQ,mBAAmBgB,CAAO,CAAC,EAC1DhB,EAAQ,MAAM,CACvB",
6
+ "names": ["ByteArrayBuilder", "LONG_TAG_PREFIX_HIGH_BYTE", "CONTACTS_TLV_TAG", "STRUCT_TYPE_REGISTER_IDENTITY", "STRUCT_TYPE_EDIT_CONTACT_NAME", "STRUCT_TYPE_REGISTER_LEDGER_ACCOUNT", "STRUCT_TYPE_EDIT_LEDGER_ACCOUNT", "STRUCT_TYPE_EDIT_IDENTIFIER", "STRUCT_TYPE_EDIT_SCOPE", "STRUCT_TYPE_PROVIDE_CONTACT", "STRUCT_TYPE_PROVIDE_LEDGER_ACCOUNT_CONTACT", "STRUCT_VERSION_VALUE", "writeTag", "builder", "tag", "writeDerLength", "length", "chainIdToMinimalBytes", "chainId", "big", "bytes", "n", "encodeTlvUInt8", "value", "encodeTlvAscii", "encodeTlvBuffer", "encodeTlvChainId", "packDerivationPath", "segments", "segment"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{ByteArrayBuilder as i}from"@ledgerhq/device-management-kit";import{CONTACTS_TLV_TAG as t,encodeTlvAscii as c,encodeTlvBuffer as o,encodeTlvChainId as x,encodeTlvUInt8 as a,packDerivationPath as l}from"./contactsTlvSerializer";describe("contactsTlvSerializer",()=>{describe("DERIVATION_PATH tag",()=>{it("is 0x69 (not the pre-1.23 value 0x21)",()=>{expect(t.DERIVATION_PATH).toBe(105)}),it("encodes a packed path under tag 0x69",()=>{const e=new i,r=l([2147483692,2147483708,2147483648]);o(e,t.DERIVATION_PATH,r),expect(e.build()).toStrictEqual(Uint8Array.from([105,13,3,128,0,0,44,128,0,0,60,128,0,0,0]))})}),describe("primitive encoders",()=>{it("encodes a single-byte TLV (tag < 0x80, length 1)",()=>{const e=new i;a(e,t.STRUCT_TYPE,45),expect(e.build()).toStrictEqual(Uint8Array.from([1,1,45]))}),it("encodes tags >= 0x80 as the 2-byte DER long form [0x81, tag]",()=>{const e=new i;c(e,t.CONTACT_NAME,"AB"),expect(e.build()).toStrictEqual(Uint8Array.from([129,240,2,65,66]))}),it("encodes chainId as minimal big-endian bytes",()=>{const e=new i;x(e,t.CHAIN_ID,1n),expect(e.build()).toStrictEqual(Uint8Array.from([35,1,1]));const r=new i;x(r,t.CHAIN_ID,56),expect(r.build()).toStrictEqual(Uint8Array.from([35,1,56]))}),it("emits DER long-form length for payloads >= 0x80 bytes",()=>{const e=new i,r=new Uint8Array(200).fill(171);o(e,t.ACCOUNT_IDENTIFIER,r);const n=e.build();expect(Array.from(n.slice(0,4))).toEqual([129,242,129,200]),expect(n.length).toBe(204)}),it("rejects a non-positive chainId",()=>{const e=new i;expect(()=>x(e,t.CHAIN_ID,0)).toThrow()})})});
2
+ //# sourceMappingURL=contactsTlvSerializer.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/internal/app-binder/services/contactsTlvSerializer.test.ts"],
4
+ "sourcesContent": ["import { ByteArrayBuilder } from \"@ledgerhq/device-management-kit\";\n\nimport {\n CONTACTS_TLV_TAG,\n encodeTlvAscii,\n encodeTlvBuffer,\n encodeTlvChainId,\n encodeTlvUInt8,\n packDerivationPath,\n} from \"./contactsTlvSerializer\";\n\ndescribe(\"contactsTlvSerializer\", () => {\n describe(\"DERIVATION_PATH tag\", () => {\n // Pins the ETH-app 1.23 / BOLOS SDK protocol change (commit 963d72b7):\n // the address-book derivation-path tag moved from 0x21 to 0x69. Sending the\n // old tag makes the on-device TLV parser reject with 0x6A80.\n it(\"is 0x69 (not the pre-1.23 value 0x21)\", () => {\n expect(CONTACTS_TLV_TAG.DERIVATION_PATH).toBe(0x69);\n });\n\n it(\"encodes a packed path under tag 0x69\", () => {\n const builder = new ByteArrayBuilder();\n const path = packDerivationPath([0x8000002c, 0x8000003c, 0x80000000]);\n\n encodeTlvBuffer(builder, CONTACTS_TLV_TAG.DERIVATION_PATH, path);\n\n expect(builder.build()).toStrictEqual(\n Uint8Array.from([\n 0x69, // DERIVATION_PATH tag\n 0x0d, // length: 1 count byte + 3 * 4-byte segments\n 0x03, // segment count\n 0x80,\n 0x00,\n 0x00,\n 0x2c, // 44'\n 0x80,\n 0x00,\n 0x00,\n 0x3c, // 60'\n 0x80,\n 0x00,\n 0x00,\n 0x00, // 0'\n ]),\n );\n });\n });\n\n describe(\"primitive encoders\", () => {\n it(\"encodes a single-byte TLV (tag < 0x80, length 1)\", () => {\n const builder = new ByteArrayBuilder();\n encodeTlvUInt8(builder, CONTACTS_TLV_TAG.STRUCT_TYPE, 0x2d);\n expect(builder.build()).toStrictEqual(\n Uint8Array.from([0x01, 0x01, 0x2d]),\n );\n });\n\n it(\"encodes tags >= 0x80 as the 2-byte DER long form [0x81, tag]\", () => {\n const builder = new ByteArrayBuilder();\n encodeTlvAscii(builder, CONTACTS_TLV_TAG.CONTACT_NAME, \"AB\");\n // Tag 0xf0 -> DER long form [0x81, 0xf0], length 2, ASCII \"AB\".\n expect(builder.build()).toStrictEqual(\n Uint8Array.from([0x81, 0xf0, 0x02, 0x41, 0x42]),\n );\n });\n\n it(\"encodes chainId as minimal big-endian bytes\", () => {\n const builder = new ByteArrayBuilder();\n encodeTlvChainId(builder, CONTACTS_TLV_TAG.CHAIN_ID, 1n);\n expect(builder.build()).toStrictEqual(\n Uint8Array.from([0x23, 0x01, 0x01]),\n );\n\n const builder2 = new ByteArrayBuilder();\n encodeTlvChainId(builder2, CONTACTS_TLV_TAG.CHAIN_ID, 56);\n expect(builder2.build()).toStrictEqual(\n Uint8Array.from([0x23, 0x01, 0x38]),\n );\n });\n\n it(\"emits DER long-form length for payloads >= 0x80 bytes\", () => {\n const builder = new ByteArrayBuilder();\n const value = new Uint8Array(200).fill(0xab);\n encodeTlvBuffer(builder, CONTACTS_TLV_TAG.ACCOUNT_IDENTIFIER, value);\n const out = builder.build();\n // tag 0xf2 -> [0x81, 0xf2], then long-form length 0x81 0xc8 (200).\n expect(Array.from(out.slice(0, 4))).toEqual([0x81, 0xf2, 0x81, 0xc8]);\n expect(out.length).toBe(4 + 200);\n });\n\n it(\"rejects a non-positive chainId\", () => {\n const builder = new ByteArrayBuilder();\n expect(() =>\n encodeTlvChainId(builder, CONTACTS_TLV_TAG.CHAIN_ID, 0),\n ).toThrow();\n });\n });\n});\n"],
5
+ "mappings": "AAAA,OAAS,oBAAAA,MAAwB,kCAEjC,OACE,oBAAAC,EACA,kBAAAC,EACA,mBAAAC,EACA,oBAAAC,EACA,kBAAAC,EACA,sBAAAC,MACK,0BAEP,SAAS,wBAAyB,IAAM,CACtC,SAAS,sBAAuB,IAAM,CAIpC,GAAG,wCAAyC,IAAM,CAChD,OAAOL,EAAiB,eAAe,EAAE,KAAK,GAAI,CACpD,CAAC,EAED,GAAG,uCAAwC,IAAM,CAC/C,MAAMM,EAAU,IAAIP,EACdQ,EAAOF,EAAmB,CAAC,WAAY,WAAY,UAAU,CAAC,EAEpEH,EAAgBI,EAASN,EAAiB,gBAAiBO,CAAI,EAE/D,OAAOD,EAAQ,MAAM,CAAC,EAAE,cACtB,WAAW,KAAK,CACd,IACA,GACA,EACA,IACA,EACA,EACA,GACA,IACA,EACA,EACA,GACA,IACA,EACA,EACA,CACF,CAAC,CACH,CACF,CAAC,CACH,CAAC,EAED,SAAS,qBAAsB,IAAM,CACnC,GAAG,mDAAoD,IAAM,CAC3D,MAAMA,EAAU,IAAIP,EACpBK,EAAeE,EAASN,EAAiB,YAAa,EAAI,EAC1D,OAAOM,EAAQ,MAAM,CAAC,EAAE,cACtB,WAAW,KAAK,CAAC,EAAM,EAAM,EAAI,CAAC,CACpC,CACF,CAAC,EAED,GAAG,+DAAgE,IAAM,CACvE,MAAMA,EAAU,IAAIP,EACpBE,EAAeK,EAASN,EAAiB,aAAc,IAAI,EAE3D,OAAOM,EAAQ,MAAM,CAAC,EAAE,cACtB,WAAW,KAAK,CAAC,IAAM,IAAM,EAAM,GAAM,EAAI,CAAC,CAChD,CACF,CAAC,EAED,GAAG,8CAA+C,IAAM,CACtD,MAAMA,EAAU,IAAIP,EACpBI,EAAiBG,EAASN,EAAiB,SAAU,EAAE,EACvD,OAAOM,EAAQ,MAAM,CAAC,EAAE,cACtB,WAAW,KAAK,CAAC,GAAM,EAAM,CAAI,CAAC,CACpC,EAEA,MAAME,EAAW,IAAIT,EACrBI,EAAiBK,EAAUR,EAAiB,SAAU,EAAE,EACxD,OAAOQ,EAAS,MAAM,CAAC,EAAE,cACvB,WAAW,KAAK,CAAC,GAAM,EAAM,EAAI,CAAC,CACpC,CACF,CAAC,EAED,GAAG,wDAAyD,IAAM,CAChE,MAAMF,EAAU,IAAIP,EACdU,EAAQ,IAAI,WAAW,GAAG,EAAE,KAAK,GAAI,EAC3CP,EAAgBI,EAASN,EAAiB,mBAAoBS,CAAK,EACnE,MAAMC,EAAMJ,EAAQ,MAAM,EAE1B,OAAO,MAAM,KAAKI,EAAI,MAAM,EAAG,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAM,IAAM,IAAM,GAAI,CAAC,EACpE,OAAOA,EAAI,MAAM,EAAE,KAAK,GAAO,CACjC,CAAC,EAED,GAAG,iCAAkC,IAAM,CACzC,MAAMJ,EAAU,IAAIP,EACpB,OAAO,IACLI,EAAiBG,EAASN,EAAiB,SAAU,CAAC,CACxD,EAAE,QAAQ,CACZ,CAAC,CACH,CAAC,CACH,CAAC",
6
+ "names": ["ByteArrayBuilder", "CONTACTS_TLV_TAG", "encodeTlvAscii", "encodeTlvBuffer", "encodeTlvChainId", "encodeTlvUInt8", "packDerivationPath", "builder", "path", "builder2", "value", "out"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{isSuccessDmkResult as f}from"@ledgerhq/device-management-kit";const A=255,p=0,y=128;async function S(e,{payload:r,makeCommand:n,logger:t,commandTag:u}){const o=R(r),s=k(o,A),l=u??"framed-contacts";for(let a=0;a<s.length-1;a++){const i=a===0?p:y,g=s[a];C(t,l,a,s.length,i,g.length,o.length);const h=await e.sendCommand(n(g,i));if(!f(h))return h}const c=s.length-1,m=c===0?p:y,d=s[c];return C(t,l,c,s.length,m,d.length,o.length),await e.sendCommand(n(d,m))}function C(e,r,n,t,u,o,s){e&&e.debug(`[chunk ${n+1}/${t}] sending`,{tag:r,data:{chunkIndex:n,chunkCount:t,p2:`0x${u.toString(16).padStart(2,"0")}`,chunkLen:o,framedPayloadLen:s,isFirst:n===0,isLast:n===t-1}})}function R(e){const r=e.length,n=new Uint8Array(r+2);return n[0]=r>>8&255,n[1]=r&255,n.set(e,2),n}function k(e,r){if(e.length===0)return[e];const n=[];for(let t=0;t<e.length;t+=r)n.push(e.slice(t,Math.min(t+r,e.length)));return n}export{S as sendFramedContactsPayload};
2
+ //# sourceMappingURL=sendFramedContactsPayload.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/internal/app-binder/services/sendFramedContactsPayload.ts"],
4
+ "sourcesContent": ["// Chunked-framing transport for the Contacts ops. Since the BOLOS SDK\n// \"use uniform chunked transport for all sub-commands\" change, every\n// address-book sub-command frames its TLV this way.\n//\n// First chunk is dispatched with P2=0x00 and carries the 2-byte BE\n// total-payload-length prefix. Subsequent chunks use P2=0x80. The\n// caller-supplied `makeCommand` builds a Command per chunk; the helper\n// returns the final chunk's CommandResult unchanged so the caller can\n// parse the device's structured response off the last exchange.\n// Protocol: ledger-secure-sdk/app_features/address_book/doc/address_book_spec.md \u00A73\nimport {\n type Command,\n type CommandResult,\n type InternalApi,\n isSuccessDmkResult,\n type LoggerPublisherService,\n} from \"@ledgerhq/device-management-kit\";\n\nconst MAX_CHUNK_BYTES = 255;\nconst P2_FIRST = 0x00;\nconst P2_NEXT = 0x80;\n\nexport type SendFramedContactsPayloadArgs<Response> = {\n /** TLV payload bytes (without any header or length framing). */\n readonly payload: Uint8Array;\n /** Sub-command byte (e.g. 0x01 for REGISTER IDENTITY). */\n readonly p1: number;\n /** Per-chunk Command factory. The helper passes the chunk bytes and the P2 byte. */\n readonly makeCommand: (\n chunk: Uint8Array,\n p2: number,\n ) => Command<Response, unknown, unknown>;\n /** Optional per-chunk diagnostic logging (one `debug` entry per outgoing chunk). */\n readonly logger?: LoggerPublisherService;\n readonly commandTag?: string;\n};\n\nexport async function sendFramedContactsPayload<Response>(\n api: InternalApi,\n {\n payload,\n makeCommand,\n logger,\n commandTag,\n }: SendFramedContactsPayloadArgs<Response>,\n): Promise<CommandResult<Response>> {\n const framed = prependFrameLength(payload);\n const chunks = sliceChunks(framed, MAX_CHUNK_BYTES);\n const tag = commandTag ?? \"framed-contacts\";\n\n // Intermediate chunks: dispatch and short-circuit on error.\n for (let i = 0; i < chunks.length - 1; i++) {\n const p2 = i === 0 ? P2_FIRST : P2_NEXT;\n const chunk = chunks[i]!;\n logChunk(logger, tag, i, chunks.length, p2, chunk.length, framed.length);\n const result = (await api.sendCommand(\n makeCommand(chunk, p2),\n )) as CommandResult<Response>;\n if (!isSuccessDmkResult(result)) {\n return result;\n }\n }\n\n const lastIndex = chunks.length - 1;\n const lastP2 = lastIndex === 0 ? P2_FIRST : P2_NEXT;\n const lastChunk = chunks[lastIndex]!;\n logChunk(\n logger,\n tag,\n lastIndex,\n chunks.length,\n lastP2,\n lastChunk.length,\n framed.length,\n );\n return (await api.sendCommand(\n makeCommand(lastChunk, lastP2),\n )) as CommandResult<Response>;\n}\n\nfunction logChunk(\n logger: LoggerPublisherService | undefined,\n tag: string,\n index: number,\n total: number,\n p2: number,\n chunkLen: number,\n framedLen: number,\n): void {\n if (!logger) return;\n logger.debug(`[chunk ${index + 1}/${total}] sending`, {\n tag,\n data: {\n chunkIndex: index,\n chunkCount: total,\n p2: `0x${p2.toString(16).padStart(2, \"0\")}`,\n chunkLen,\n framedPayloadLen: framedLen,\n isFirst: index === 0,\n isLast: index === total - 1,\n },\n });\n}\n\nfunction prependFrameLength(payload: Uint8Array): Uint8Array {\n const total = payload.length;\n const framed = new Uint8Array(total + 2);\n framed[0] = (total >> 8) & 0xff;\n framed[1] = total & 0xff;\n framed.set(payload, 2);\n return framed;\n}\n\nfunction sliceChunks(buffer: Uint8Array, max: number): Uint8Array[] {\n if (buffer.length === 0) return [buffer];\n const out: Uint8Array[] = [];\n for (let offset = 0; offset < buffer.length; offset += max) {\n out.push(buffer.slice(offset, Math.min(offset + max, buffer.length)));\n }\n return out;\n}\n"],
5
+ "mappings": "AAUA,OAIE,sBAAAA,MAEK,kCAEP,MAAMC,EAAkB,IAClBC,EAAW,EACXC,EAAU,IAiBhB,eAAsBC,EACpBC,EACA,CACE,QAAAC,EACA,YAAAC,EACA,OAAAC,EACA,WAAAC,CACF,EACkC,CAClC,MAAMC,EAASC,EAAmBL,CAAO,EACnCM,EAASC,EAAYH,EAAQT,CAAe,EAC5Ca,EAAML,GAAc,kBAG1B,QAASM,EAAI,EAAGA,EAAIH,EAAO,OAAS,EAAGG,IAAK,CAC1C,MAAMC,EAAKD,IAAM,EAAIb,EAAWC,EAC1Bc,EAAQL,EAAOG,CAAC,EACtBG,EAASV,EAAQM,EAAKC,EAAGH,EAAO,OAAQI,EAAIC,EAAM,OAAQP,EAAO,MAAM,EACvE,MAAMS,EAAU,MAAMd,EAAI,YACxBE,EAAYU,EAAOD,CAAE,CACvB,EACA,GAAI,CAAChB,EAAmBmB,CAAM,EAC5B,OAAOA,CAEX,CAEA,MAAMC,EAAYR,EAAO,OAAS,EAC5BS,EAASD,IAAc,EAAIlB,EAAWC,EACtCmB,EAAYV,EAAOQ,CAAS,EAClC,OAAAF,EACEV,EACAM,EACAM,EACAR,EAAO,OACPS,EACAC,EAAU,OACVZ,EAAO,MACT,EACQ,MAAML,EAAI,YAChBE,EAAYe,EAAWD,CAAM,CAC/B,CACF,CAEA,SAASH,EACPV,EACAM,EACAS,EACAC,EACAR,EACAS,EACAC,EACM,CACDlB,GACLA,EAAO,MAAM,UAAUe,EAAQ,CAAC,IAAIC,CAAK,YAAa,CACpD,IAAAV,EACA,KAAM,CACJ,WAAYS,EACZ,WAAYC,EACZ,GAAI,KAAKR,EAAG,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,GACzC,SAAAS,EACA,iBAAkBC,EAClB,QAASH,IAAU,EACnB,OAAQA,IAAUC,EAAQ,CAC5B,CACF,CAAC,CACH,CAEA,SAASb,EAAmBL,EAAiC,CAC3D,MAAMkB,EAAQlB,EAAQ,OAChBI,EAAS,IAAI,WAAWc,EAAQ,CAAC,EACvC,OAAAd,EAAO,CAAC,EAAKc,GAAS,EAAK,IAC3Bd,EAAO,CAAC,EAAIc,EAAQ,IACpBd,EAAO,IAAIJ,EAAS,CAAC,EACdI,CACT,CAEA,SAASG,EAAYc,EAAoBC,EAA2B,CAClE,GAAID,EAAO,SAAW,EAAG,MAAO,CAACA,CAAM,EACvC,MAAME,EAAoB,CAAC,EAC3B,QAASC,EAAS,EAAGA,EAASH,EAAO,OAAQG,GAAUF,EACrDC,EAAI,KAAKF,EAAO,MAAMG,EAAQ,KAAK,IAAIA,EAASF,EAAKD,EAAO,MAAM,CAAC,CAAC,EAEtE,OAAOE,CACT",
6
+ "names": ["isSuccessDmkResult", "MAX_CHUNK_BYTES", "P2_FIRST", "P2_NEXT", "sendFramedContactsPayload", "api", "payload", "makeCommand", "logger", "commandTag", "framed", "prependFrameLength", "chunks", "sliceChunks", "tag", "i", "p2", "chunk", "logChunk", "result", "lastIndex", "lastP2", "lastChunk", "index", "total", "chunkLen", "framedLen", "buffer", "max", "out", "offset"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{CommandResultFactory as c,InvalidStatusWordError as y}from"@ledgerhq/device-management-kit";import{sendFramedContactsPayload as m}from"./sendFramedContactsPayload";function l(){return{sendCommand:vi.fn()}}function r(e,n=66){return new Uint8Array(e).fill(n)}const d=()=>vi.fn((e,n)=>({name:"test",args:{chunk:e,p2:n},getApdu:vi.fn(),parseResponse:vi.fn()}));describe("sendFramedContactsPayload",()=>{it("single-chunk: prepends BE length, uses P2=0x00, returns command result",async()=>{const e=l(),n=c({data:{ok:!0}});e.sendCommand.mockResolvedValueOnce(n);const t=d(),o=await m(e,{payload:r(100),p1:1,makeCommand:t});expect(t).toHaveBeenCalledTimes(1);const[a,s]=t.mock.calls[0];expect(s).toBe(0),expect(a[0]).toBe(0),expect(a[1]).toBe(100),expect(a.length).toBe(102),expect(o).toBe(n)}),it("multi-chunk: splits into <=255B pieces, P2 0x00 then 0x80, prefix on first only",async()=>{const e=l(),n=c({data:{}}),t=c({data:{ok:!0}});e.sendCommand.mockResolvedValueOnce(n).mockResolvedValueOnce(n).mockResolvedValueOnce(t);const o=d(),a=await m(e,{payload:r(600,85),p1:1,makeCommand:o});expect(o).toHaveBeenCalledTimes(3);const[s,p]=o.mock.calls[0],[i,u]=o.mock.calls[1],[k,x]=o.mock.calls[2];expect([p,u,x]).toEqual([0,128,128]),expect([s.length,i.length,k.length]).toEqual([255,255,92]),expect(s[0]).toBe(2),expect(s[1]).toBe(88),expect(i[0]).toBe(85),expect(a).toBe(t)}),it("short-circuits on intermediate-chunk error without dispatching remaining chunks",async()=>{const e=l(),n=c({error:new y("kaboom")});e.sendCommand.mockResolvedValueOnce(n);const t=d(),o=await m(e,{payload:r(600),p1:1,makeCommand:t});expect(e.sendCommand).toHaveBeenCalledTimes(1),expect(t).toHaveBeenCalledTimes(1),expect(o).toBe(n)})});
2
+ //# sourceMappingURL=sendFramedContactsPayload.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/internal/app-binder/services/sendFramedContactsPayload.test.ts"],
4
+ "sourcesContent": ["import {\n type Command,\n type CommandResult,\n CommandResultFactory,\n type InternalApi,\n InvalidStatusWordError,\n} from \"@ledgerhq/device-management-kit\";\n\nimport { sendFramedContactsPayload } from \"./sendFramedContactsPayload\";\n\nfunction makeApiMock(): InternalApi & {\n sendCommand: ReturnType<typeof vi.fn>;\n} {\n return {\n sendCommand: vi.fn(),\n } as unknown as InternalApi & { sendCommand: ReturnType<typeof vi.fn> };\n}\n\nfunction makePayload(byteCount: number, fillByte = 0x42): Uint8Array {\n return new Uint8Array(byteCount).fill(fillByte);\n}\n\nconst makeCommandFactory = () =>\n vi.fn(\n (chunk: Uint8Array, p2: number) =>\n ({\n name: \"test\",\n args: { chunk, p2 },\n getApdu: vi.fn(),\n parseResponse: vi.fn(),\n }) as unknown as Command<unknown, unknown, unknown>,\n );\n\ndescribe(\"sendFramedContactsPayload\", () => {\n it(\"single-chunk: prepends BE length, uses P2=0x00, returns command result\", async () => {\n const api = makeApiMock();\n const finalResult = CommandResultFactory({ data: { ok: true } });\n api.sendCommand.mockResolvedValueOnce(finalResult);\n const makeCommand = makeCommandFactory();\n\n const result = await sendFramedContactsPayload(api, {\n payload: makePayload(100),\n p1: 0x01,\n makeCommand,\n });\n\n expect(makeCommand).toHaveBeenCalledTimes(1);\n const [chunk, p2] = makeCommand.mock.calls[0]!;\n expect(p2).toBe(0x00);\n // 2-byte BE length prefix = 100 \u2192 0x00, 0x64.\n expect(chunk[0]).toBe(0x00);\n expect(chunk[1]).toBe(0x64);\n expect(chunk.length).toBe(102);\n expect(result).toBe(finalResult);\n });\n\n it(\"multi-chunk: splits into <=255B pieces, P2 0x00 then 0x80, prefix on first only\", async () => {\n const api = makeApiMock();\n const intermediateOk = CommandResultFactory({ data: {} });\n const finalResult = CommandResultFactory({ data: { ok: true } });\n api.sendCommand\n .mockResolvedValueOnce(intermediateOk)\n .mockResolvedValueOnce(intermediateOk)\n .mockResolvedValueOnce(finalResult);\n const makeCommand = makeCommandFactory();\n\n // 600 bytes payload \u2192 602 with frame prefix \u2192 3 chunks (255 + 255 + 92).\n const result = await sendFramedContactsPayload(api, {\n payload: makePayload(600, 0x55),\n p1: 0x01,\n makeCommand,\n });\n\n expect(makeCommand).toHaveBeenCalledTimes(3);\n const [chunk0, p20] = makeCommand.mock.calls[0]!;\n const [chunk1, p21] = makeCommand.mock.calls[1]!;\n const [chunk2, p22] = makeCommand.mock.calls[2]!;\n expect([p20, p21, p22]).toEqual([0x00, 0x80, 0x80]);\n expect([chunk0.length, chunk1.length, chunk2.length]).toEqual([\n 255, 255, 92,\n ]);\n // Frame prefix (600 = 0x0258) only on the first chunk.\n expect(chunk0[0]).toBe(0x02);\n expect(chunk0[1]).toBe(0x58);\n // Continuation chunks begin with raw payload bytes.\n expect(chunk1[0]).toBe(0x55);\n expect(result).toBe(finalResult);\n });\n\n it(\"short-circuits on intermediate-chunk error without dispatching remaining chunks\", async () => {\n const api = makeApiMock();\n const errorResult: CommandResult<unknown> = CommandResultFactory({\n error: new InvalidStatusWordError(\"kaboom\"),\n });\n api.sendCommand.mockResolvedValueOnce(errorResult);\n const makeCommand = makeCommandFactory();\n\n const result = await sendFramedContactsPayload(api, {\n payload: makePayload(600),\n p1: 0x01,\n makeCommand,\n });\n\n expect(api.sendCommand).toHaveBeenCalledTimes(1);\n expect(makeCommand).toHaveBeenCalledTimes(1);\n expect(result).toBe(errorResult);\n });\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["CommandResultFactory", "InvalidStatusWordError", "sendFramedContactsPayload", "makeApiMock", "makePayload", "byteCount", "fillByte", "makeCommandFactory", "chunk", "p2", "api", "finalResult", "makeCommand", "result", "intermediateOk", "chunk0", "p20", "chunk1", "p21", "chunk2", "p22", "errorResult"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{ByteArrayBuilder as m,DmkResultFactory as s,InvalidStatusWordError as p,isSuccessCommandResult as C}from"@ledgerhq/device-management-kit";import{RegisterIdentityCommand as u}from"../../app-binder/command/RegisterIdentityCommand";import{BLOCKCHAIN_FAMILY_BY_NAME as I,SUB_CMD_REGISTER_IDENTITY as T}from"../../app-binder/model/contactsConstants";import{CONTACTS_TLV_TAG as t,encodeTlvAscii as c,encodeTlvBuffer as n,encodeTlvChainId as g,encodeTlvUInt8 as a,packDerivationPath as A,STRUCT_TYPE_REGISTER_IDENTITY as R,STRUCT_VERSION_VALUE as f}from"../../app-binder/services/contactsTlvSerializer";import{sendFramedContactsPayload as E}from"../../app-binder/services/sendFramedContactsPayload";class k{constructor(r,o){this.api=r;this.args=o}async run(){const r=this.buildPayload(this.args),o=await E(this.api,{payload:r,p1:T,makeCommand:(l,y)=>new u({data:l,p2:y}),logger:this.args.logger,commandTag:"SendRegisterIdentityTask"});if(!C(o))return o;const{groupHandle:e,hmacProof:i,hmacRest:d}=o.data;return!e||!i||!d?s({error:new p("RegisterIdentity final-chunk response was incomplete")}):s({data:{groupHandle:e,hmacProof:i,hmacRest:d}})}buildPayload(r){const o=I[r.blockchainFamily.toLowerCase()];if(o===void 0)throw new Error(`Unsupported blockchain family: ${r.blockchainFamily}`);const e=new m;return a(e,t.STRUCT_TYPE,R),a(e,t.STRUCT_VERSION,f),c(e,t.CONTACT_NAME,r.contactName),c(e,t.SCOPE,r.scope),n(e,t.ACCOUNT_IDENTIFIER,r.identifier),n(e,t.DERIVATION_PATH,A([2147483692,2147483708,2147483648,0,0])),r.chainId!==void 0&&g(e,t.CHAIN_ID,r.chainId),a(e,t.BLOCKCHAIN_FAMILY,o),r.existingContactGroup&&(n(e,t.GROUP_HANDLE,r.existingContactGroup.groupHandle),n(e,t.HMAC_PROOF,r.existingContactGroup.hmacProof)),e.build()}}export{k as SendRegisterIdentityTask};
2
+ //# sourceMappingURL=SendRegisterIdentityTask.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/internal/app-binder/task/SendRegisterIdentityTask.ts"],
4
+ "sourcesContent": ["// Builds the TLV payload for op 1 (Register Identity / Register External\n// Address) and dispatches it via the chunked-framing scheme shared with the\n// other address-book ops (2-byte BE total length + <=255B chunks). The device\n// returns the 129-byte register response on the final chunk.\n//\n// Reference: Address Book Final Specifications \u2014 Register Identity. Tag order:\n// STRUCT_TYPE, STRUCT_VERSION, CONTACT_NAME, SCOPE, ACCOUNT_IDENTIFIER,\n// DERIVATION_PATH, CHAIN_ID (Ethereum only), BLOCKCHAIN_FAMILY, then optional\n// GROUP_HANDLE + HMAC_PROOF when extending an existing group.\n//\n// DERIVATION_PATH (tag 0x69, default m/44'/60'/0'/0/0): the Ethereum app\n// requires it on Register Identity and rejects the payload with 0x6a80 when it\n// is absent (verified on-device against the deployed dev app).\nimport {\n ByteArrayBuilder,\n type CommandResult,\n DmkResultFactory,\n type InternalApi,\n InvalidStatusWordError,\n isSuccessCommandResult,\n type LoggerPublisherService,\n} from \"@ledgerhq/device-management-kit\";\n\nimport { type ExistingContactGroup } from \"@api/model/RegisterExternalAddress\";\nimport { RegisterIdentityCommand } from \"@internal/app-binder/command/RegisterIdentityCommand\";\nimport {\n BLOCKCHAIN_FAMILY_BY_NAME,\n SUB_CMD_REGISTER_IDENTITY,\n} from \"@internal/app-binder/model/contactsConstants\";\nimport { type ContactsErrorCodes } from \"@internal/app-binder/model/contactsErrors\";\nimport {\n CONTACTS_TLV_TAG,\n encodeTlvAscii,\n encodeTlvBuffer,\n encodeTlvChainId,\n encodeTlvUInt8,\n packDerivationPath,\n STRUCT_TYPE_REGISTER_IDENTITY,\n STRUCT_VERSION_VALUE,\n} from \"@internal/app-binder/services/contactsTlvSerializer\";\nimport { sendFramedContactsPayload } from \"@internal/app-binder/services/sendFramedContactsPayload\";\n\nexport type RegisterIdentityProofs = {\n readonly groupHandle: Uint8Array;\n readonly hmacProof: Uint8Array;\n readonly hmacRest: Uint8Array;\n};\n\nexport type SendRegisterIdentityTaskArgs = {\n readonly contactName: string;\n readonly scope: string;\n readonly identifier: Uint8Array;\n readonly blockchainFamily: string;\n readonly chainId?: bigint;\n readonly existingContactGroup?: ExistingContactGroup;\n readonly logger?: LoggerPublisherService;\n};\n\nexport class SendRegisterIdentityTask {\n constructor(\n private readonly api: InternalApi,\n private readonly args: SendRegisterIdentityTaskArgs,\n ) {}\n\n async run(): Promise<\n CommandResult<RegisterIdentityProofs, ContactsErrorCodes>\n > {\n const payload = this.buildPayload(this.args);\n\n const result = (await sendFramedContactsPayload(this.api, {\n payload,\n p1: SUB_CMD_REGISTER_IDENTITY,\n makeCommand: (chunk, p2) =>\n new RegisterIdentityCommand({ data: chunk, p2 }),\n logger: this.args.logger,\n commandTag: \"SendRegisterIdentityTask\",\n })) as CommandResult<\n {\n readonly groupHandle?: Uint8Array;\n readonly hmacProof?: Uint8Array;\n readonly hmacRest?: Uint8Array;\n },\n ContactsErrorCodes\n >;\n\n if (!isSuccessCommandResult(result)) {\n return result;\n }\n\n const { groupHandle, hmacProof, hmacRest } = result.data;\n if (!groupHandle || !hmacProof || !hmacRest) {\n return DmkResultFactory({\n error: new InvalidStatusWordError(\n \"RegisterIdentity final-chunk response was incomplete\",\n ),\n });\n }\n return DmkResultFactory({ data: { groupHandle, hmacProof, hmacRest } });\n }\n\n private buildPayload(args: SendRegisterIdentityTaskArgs): Uint8Array {\n const family =\n BLOCKCHAIN_FAMILY_BY_NAME[args.blockchainFamily.toLowerCase()];\n if (family === undefined) {\n throw new Error(\n `Unsupported blockchain family: ${args.blockchainFamily}`,\n );\n }\n\n const builder = new ByteArrayBuilder();\n encodeTlvUInt8(\n builder,\n CONTACTS_TLV_TAG.STRUCT_TYPE,\n STRUCT_TYPE_REGISTER_IDENTITY,\n );\n encodeTlvUInt8(\n builder,\n CONTACTS_TLV_TAG.STRUCT_VERSION,\n STRUCT_VERSION_VALUE,\n );\n encodeTlvAscii(builder, CONTACTS_TLV_TAG.CONTACT_NAME, args.contactName);\n encodeTlvAscii(builder, CONTACTS_TLV_TAG.SCOPE, args.scope);\n encodeTlvBuffer(\n builder,\n CONTACTS_TLV_TAG.ACCOUNT_IDENTIFIER,\n args.identifier,\n );\n // The Ethereum app requires DERIVATION_PATH on Register Identity (0x6a80\n // without it). Default m/44'/60'/0'/0/0.\n encodeTlvBuffer(\n builder,\n CONTACTS_TLV_TAG.DERIVATION_PATH,\n packDerivationPath([0x8000002c, 0x8000003c, 0x80000000, 0, 0]),\n );\n if (args.chainId !== undefined) {\n encodeTlvChainId(builder, CONTACTS_TLV_TAG.CHAIN_ID, args.chainId);\n }\n encodeTlvUInt8(builder, CONTACTS_TLV_TAG.BLOCKCHAIN_FAMILY, family);\n\n if (args.existingContactGroup) {\n encodeTlvBuffer(\n builder,\n CONTACTS_TLV_TAG.GROUP_HANDLE,\n args.existingContactGroup.groupHandle,\n );\n encodeTlvBuffer(\n builder,\n CONTACTS_TLV_TAG.HMAC_PROOF,\n args.existingContactGroup.hmacProof,\n );\n }\n\n return builder.build();\n }\n}\n"],
5
+ "mappings": "AAaA,OACE,oBAAAA,EAEA,oBAAAC,EAEA,0BAAAC,EACA,0BAAAC,MAEK,kCAGP,OAAS,2BAAAC,MAA+B,uDACxC,OACE,6BAAAC,EACA,6BAAAC,MACK,+CAEP,OACE,oBAAAC,EACA,kBAAAC,EACA,mBAAAC,EACA,oBAAAC,EACA,kBAAAC,EACA,sBAAAC,EACA,iCAAAC,EACA,wBAAAC,MACK,sDACP,OAAS,6BAAAC,MAAiC,0DAkBnC,MAAMC,CAAyB,CACpC,YACmBC,EACAC,EACjB,CAFiB,SAAAD,EACA,UAAAC,CAChB,CAEH,MAAM,KAEJ,CACA,MAAMC,EAAU,KAAK,aAAa,KAAK,IAAI,EAErCC,EAAU,MAAML,EAA0B,KAAK,IAAK,CACxD,QAAAI,EACA,GAAIb,EACJ,YAAa,CAACe,EAAOC,IACnB,IAAIlB,EAAwB,CAAE,KAAMiB,EAAO,GAAAC,CAAG,CAAC,EACjD,OAAQ,KAAK,KAAK,OAClB,WAAY,0BACd,CAAC,EASD,GAAI,CAACnB,EAAuBiB,CAAM,EAChC,OAAOA,EAGT,KAAM,CAAE,YAAAG,EAAa,UAAAC,EAAW,SAAAC,CAAS,EAAIL,EAAO,KACpD,MAAI,CAACG,GAAe,CAACC,GAAa,CAACC,EAC1BxB,EAAiB,CACtB,MAAO,IAAIC,EACT,sDACF,CACF,CAAC,EAEID,EAAiB,CAAE,KAAM,CAAE,YAAAsB,EAAa,UAAAC,EAAW,SAAAC,CAAS,CAAE,CAAC,CACxE,CAEQ,aAAaP,EAAgD,CACnE,MAAMQ,EACJrB,EAA0Ba,EAAK,iBAAiB,YAAY,CAAC,EAC/D,GAAIQ,IAAW,OACb,MAAM,IAAI,MACR,kCAAkCR,EAAK,gBAAgB,EACzD,EAGF,MAAMS,EAAU,IAAI3B,EACpB,OAAAW,EACEgB,EACApB,EAAiB,YACjBM,CACF,EACAF,EACEgB,EACApB,EAAiB,eACjBO,CACF,EACAN,EAAemB,EAASpB,EAAiB,aAAcW,EAAK,WAAW,EACvEV,EAAemB,EAASpB,EAAiB,MAAOW,EAAK,KAAK,EAC1DT,EACEkB,EACApB,EAAiB,mBACjBW,EAAK,UACP,EAGAT,EACEkB,EACApB,EAAiB,gBACjBK,EAAmB,CAAC,WAAY,WAAY,WAAY,EAAG,CAAC,CAAC,CAC/D,EACIM,EAAK,UAAY,QACnBR,EAAiBiB,EAASpB,EAAiB,SAAUW,EAAK,OAAO,EAEnEP,EAAegB,EAASpB,EAAiB,kBAAmBmB,CAAM,EAE9DR,EAAK,uBACPT,EACEkB,EACApB,EAAiB,aACjBW,EAAK,qBAAqB,WAC5B,EACAT,EACEkB,EACApB,EAAiB,WACjBW,EAAK,qBAAqB,SAC5B,GAGKS,EAAQ,MAAM,CACvB,CACF",
6
+ "names": ["ByteArrayBuilder", "DmkResultFactory", "InvalidStatusWordError", "isSuccessCommandResult", "RegisterIdentityCommand", "BLOCKCHAIN_FAMILY_BY_NAME", "SUB_CMD_REGISTER_IDENTITY", "CONTACTS_TLV_TAG", "encodeTlvAscii", "encodeTlvBuffer", "encodeTlvChainId", "encodeTlvUInt8", "packDerivationPath", "STRUCT_TYPE_REGISTER_IDENTITY", "STRUCT_VERSION_VALUE", "sendFramedContactsPayload", "SendRegisterIdentityTask", "api", "args", "payload", "result", "chunk", "p2", "groupHandle", "hmacProof", "hmacRest", "family", "builder"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{CommandResultFactory as t,InvalidStatusWordError as r}from"@ledgerhq/device-management-kit";import{RegisterIdentityCommand as s}from"../../app-binder/command/RegisterIdentityCommand";import{SendRegisterIdentityTask as a}from"./SendRegisterIdentityTask";function e(c){const n=new Uint8Array(c.length/2);for(let d=0;d<c.length;d+=2)n[d/2]=parseInt(c.slice(d,d+2),16);return n}const m=e("004d01012d02010181f005416c69636581f108457468206d61696e81f21400000000000000000000000000000000deadbeef6915058000002c8000003c800000000000000000000000230101510101"),l=e("00b301012d02010181f005416c69636581f108417262206d61696e81f21444444444444444444444444444444444444444446915058000002c8000003c8000000000000000000000002302a4b151010181f640cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc2920dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"),i={groupHandle:e("cc".repeat(64)),hmacProof:e("dd".repeat(32)),hmacRest:e("aa".repeat(32))};function o(){return{sendCommand:vi.fn()}}describe("SendRegisterIdentityTask",()=>{it("assembles a fresh-register framed chunk and dispatches RegisterIdentityCommand",async()=>{const c=o();c.sendCommand.mockResolvedValueOnce(t({data:i}));const n=await new a(c,{contactName:"Alice",scope:"Eth main",identifier:e("00000000000000000000000000000000deadbeef"),blockchainFamily:"ethereum",chainId:1n}).run();expect(c.sendCommand.mock.calls).toHaveLength(1),expect(c.sendCommand.mock.calls[0][0]).toStrictEqual(new s({data:m,p2:0})),expect(n).toStrictEqual(t({data:i}))}),it("appends GROUP_HANDLE + HMAC_PROOF on extension and frames the extension payload",async()=>{const c=o();c.sendCommand.mockResolvedValueOnce(t({data:i})),await new a(c,{contactName:"Alice",scope:"Arb main",identifier:e("44".repeat(20)),blockchainFamily:"ethereum",chainId:42161n,existingContactGroup:{groupHandle:e("cc".repeat(64)),hmacProof:e("dd".repeat(32))}}).run(),expect(c.sendCommand.mock.calls).toHaveLength(1),expect(c.sendCommand.mock.calls[0][0]).toStrictEqual(new s({data:l,p2:0}))}),it("omits the CHAIN_ID TLV when chainId is not provided",async()=>{const c=o();c.sendCommand.mockResolvedValueOnce(t({data:i})),await new a(c,{contactName:"Alice",scope:"Eth main",identifier:e("00000000000000000000000000000000deadbeef"),blockchainFamily:"ethereum"}).run();const n=c.sendCommand.mock.calls[0][0],d=Buffer.from(n.args.data).toString("hex");expect(d).not.toContain("230101"),expect(d.endsWith("510101")).toBe(!0)}),it("propagates command-level errors",async()=>{const c=o();c.sendCommand.mockResolvedValueOnce(t({error:new r("user cancelled")}));const n=await new a(c,{contactName:"Alice",scope:"Eth main",identifier:e("00000000000000000000000000000000deadbeef"),blockchainFamily:"ethereum",chainId:1n}).run();expect(n).toStrictEqual(t({error:new r("user cancelled")}))}),it("throws on an unsupported blockchain family",async()=>{const c=o();await expect(new a(c,{contactName:"Alice",scope:"Eth main",identifier:e("00000000000000000000000000000000deadbeef"),blockchainFamily:"dogecoin",chainId:1n}).run()).rejects.toThrow(/Unsupported blockchain family/),expect(c.sendCommand).not.toHaveBeenCalled()})});
2
+ //# sourceMappingURL=SendRegisterIdentityTask.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/internal/app-binder/task/SendRegisterIdentityTask.test.ts"],
4
+ "sourcesContent": ["// Validates that the TLV serializer + tag ordering + chunk framing produce the\n// expected wire bytes for op 1 (Register Identity): tags >= 0x80 use the 2-byte\n// DER form [0x81, tag], and Register Identity carries DERIVATION_PATH (tag 0x69,\n// m/44'/60'/0'/0/0) which the Ethereum app requires. The framed chunk = 2-byte\n// BE total length + TLV.\nimport {\n CommandResultFactory,\n type InternalApi,\n InvalidStatusWordError,\n} from \"@ledgerhq/device-management-kit\";\n\nimport { RegisterIdentityCommand } from \"@internal/app-binder/command/RegisterIdentityCommand\";\n\nimport { SendRegisterIdentityTask } from \"./SendRegisterIdentityTask\";\n\nfunction hexToBytes(hex: string): Uint8Array {\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n out[i / 2] = parseInt(hex.slice(i, i + 2), 16);\n }\n return out;\n}\n\n// Framed chunk = \"00 <Lc>\" (2-byte BE total TLV length) + TLV. Tag order:\n// STRUCT_TYPE, STRUCT_VERSION, CONTACT_NAME, SCOPE, ACCOUNT_IDENTIFIER,\n// DERIVATION_PATH, CHAIN_ID, BLOCKCHAIN_FAMILY, then optional GROUP_HANDLE +\n// HMAC_PROOF. Tags >= 0x80 are encoded as [0x81, tag]; DERIVATION_PATH (0x69)\n// packs m/44'/60'/0'/0/0 as \"05 8000002c 8000003c 80000000 00000000 00000000\".\nconst FRESH_FRAMED_CHUNK = hexToBytes(\n \"004d01012d020101\" +\n \"81f005416c696365\" +\n \"81f108457468206d61696e\" +\n \"81f21400000000000000000000000000000000deadbeef\" +\n \"6915058000002c8000003c800000000000000000000000\" +\n \"230101510101\",\n);\n\nconst EXTENSION_FRAMED_CHUNK = hexToBytes(\n \"00b301012d020101\" +\n \"81f005416c696365\" +\n \"81f108417262206d61696e\" +\n \"81f2144444444444444444444444444444444444444444\" +\n \"6915058000002c8000003c800000000000000000000000\" +\n \"2302a4b1510101\" +\n \"81f640cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\" +\n \"2920dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\",\n);\n\nconst OK_PROOFS = {\n groupHandle: hexToBytes(\"cc\".repeat(64)),\n hmacProof: hexToBytes(\"dd\".repeat(32)),\n hmacRest: hexToBytes(\"aa\".repeat(32)),\n};\n\nfunction makeApiMock(): InternalApi & {\n sendCommand: ReturnType<typeof vi.fn>;\n} {\n return { sendCommand: vi.fn() } as unknown as InternalApi & {\n sendCommand: ReturnType<typeof vi.fn>;\n };\n}\n\ndescribe(\"SendRegisterIdentityTask\", () => {\n it(\"assembles a fresh-register framed chunk and dispatches RegisterIdentityCommand\", async () => {\n const api = makeApiMock();\n api.sendCommand.mockResolvedValueOnce(\n CommandResultFactory({ data: OK_PROOFS }),\n );\n\n const result = await new SendRegisterIdentityTask(api, {\n contactName: \"Alice\",\n scope: \"Eth main\",\n identifier: hexToBytes(\"00000000000000000000000000000000deadbeef\"),\n blockchainFamily: \"ethereum\",\n chainId: 1n,\n }).run();\n\n expect(api.sendCommand.mock.calls).toHaveLength(1);\n expect(api.sendCommand.mock.calls[0]![0]).toStrictEqual(\n new RegisterIdentityCommand({ data: FRESH_FRAMED_CHUNK, p2: 0x00 }),\n );\n expect(result).toStrictEqual(CommandResultFactory({ data: OK_PROOFS }));\n });\n\n it(\"appends GROUP_HANDLE + HMAC_PROOF on extension and frames the extension payload\", async () => {\n const api = makeApiMock();\n api.sendCommand.mockResolvedValueOnce(\n CommandResultFactory({ data: OK_PROOFS }),\n );\n\n await new SendRegisterIdentityTask(api, {\n contactName: \"Alice\",\n scope: \"Arb main\",\n identifier: hexToBytes(\"44\".repeat(20)),\n blockchainFamily: \"ethereum\",\n chainId: 42161n,\n existingContactGroup: {\n groupHandle: hexToBytes(\"cc\".repeat(64)),\n hmacProof: hexToBytes(\"dd\".repeat(32)),\n },\n }).run();\n\n expect(api.sendCommand.mock.calls).toHaveLength(1);\n expect(api.sendCommand.mock.calls[0]![0]).toStrictEqual(\n new RegisterIdentityCommand({ data: EXTENSION_FRAMED_CHUNK, p2: 0x00 }),\n );\n });\n\n it(\"omits the CHAIN_ID TLV when chainId is not provided\", async () => {\n const api = makeApiMock();\n api.sendCommand.mockResolvedValueOnce(\n CommandResultFactory({ data: OK_PROOFS }),\n );\n\n await new SendRegisterIdentityTask(api, {\n contactName: \"Alice\",\n scope: \"Eth main\",\n identifier: hexToBytes(\"00000000000000000000000000000000deadbeef\"),\n blockchainFamily: \"ethereum\",\n }).run();\n\n const command = api.sendCommand.mock\n .calls[0]![0] as RegisterIdentityCommand;\n // No `23 01 ..` CHAIN_ID TLV; BLOCKCHAIN_FAMILY 51 01 01 follows the path.\n const framedHex = Buffer.from(command.args.data).toString(\"hex\");\n expect(framedHex).not.toContain(\"230101\");\n expect(framedHex.endsWith(\"510101\")).toBe(true);\n });\n\n it(\"propagates command-level errors\", async () => {\n const api = makeApiMock();\n api.sendCommand.mockResolvedValueOnce(\n CommandResultFactory({\n error: new InvalidStatusWordError(\"user cancelled\"),\n }),\n );\n\n const result = await new SendRegisterIdentityTask(api, {\n contactName: \"Alice\",\n scope: \"Eth main\",\n identifier: hexToBytes(\"00000000000000000000000000000000deadbeef\"),\n blockchainFamily: \"ethereum\",\n chainId: 1n,\n }).run();\n\n expect(result).toStrictEqual(\n CommandResultFactory({\n error: new InvalidStatusWordError(\"user cancelled\"),\n }),\n );\n });\n\n it(\"throws on an unsupported blockchain family\", async () => {\n const api = makeApiMock();\n\n await expect(\n new SendRegisterIdentityTask(api, {\n contactName: \"Alice\",\n scope: \"Eth main\",\n identifier: hexToBytes(\"00000000000000000000000000000000deadbeef\"),\n blockchainFamily: \"dogecoin\",\n chainId: 1n,\n }).run(),\n ).rejects.toThrow(/Unsupported blockchain family/);\n expect(api.sendCommand).not.toHaveBeenCalled();\n });\n});\n"],
5
+ "mappings": "AAKA,OACE,wBAAAA,EAEA,0BAAAC,MACK,kCAEP,OAAS,2BAAAC,MAA+B,uDAExC,OAAS,4BAAAC,MAAgC,6BAEzC,SAASC,EAAWC,EAAyB,CAC3C,MAAMC,EAAM,IAAI,WAAWD,EAAI,OAAS,CAAC,EACzC,QAASE,EAAI,EAAGA,EAAIF,EAAI,OAAQE,GAAK,EACnCD,EAAIC,EAAI,CAAC,EAAI,SAASF,EAAI,MAAME,EAAGA,EAAI,CAAC,EAAG,EAAE,EAE/C,OAAOD,CACT,CAOA,MAAME,EAAqBJ,EACzB,gKAMF,EAEMK,EAAyBL,EAC7B,4WAQF,EAEMM,EAAY,CAChB,YAAaN,EAAW,KAAK,OAAO,EAAE,CAAC,EACvC,UAAWA,EAAW,KAAK,OAAO,EAAE,CAAC,EACrC,SAAUA,EAAW,KAAK,OAAO,EAAE,CAAC,CACtC,EAEA,SAASO,GAEP,CACA,MAAO,CAAE,YAAa,GAAG,GAAG,CAAE,CAGhC,CAEA,SAAS,2BAA4B,IAAM,CACzC,GAAG,iFAAkF,SAAY,CAC/F,MAAMC,EAAMD,EAAY,EACxBC,EAAI,YAAY,sBACdZ,EAAqB,CAAE,KAAMU,CAAU,CAAC,CAC1C,EAEA,MAAMG,EAAS,MAAM,IAAIV,EAAyBS,EAAK,CACrD,YAAa,QACb,MAAO,WACP,WAAYR,EAAW,0CAA0C,EACjE,iBAAkB,WAClB,QAAS,EACX,CAAC,EAAE,IAAI,EAEP,OAAOQ,EAAI,YAAY,KAAK,KAAK,EAAE,aAAa,CAAC,EACjD,OAAOA,EAAI,YAAY,KAAK,MAAM,CAAC,EAAG,CAAC,CAAC,EAAE,cACxC,IAAIV,EAAwB,CAAE,KAAMM,EAAoB,GAAI,CAAK,CAAC,CACpE,EACA,OAAOK,CAAM,EAAE,cAAcb,EAAqB,CAAE,KAAMU,CAAU,CAAC,CAAC,CACxE,CAAC,EAED,GAAG,kFAAmF,SAAY,CAChG,MAAME,EAAMD,EAAY,EACxBC,EAAI,YAAY,sBACdZ,EAAqB,CAAE,KAAMU,CAAU,CAAC,CAC1C,EAEA,MAAM,IAAIP,EAAyBS,EAAK,CACtC,YAAa,QACb,MAAO,WACP,WAAYR,EAAW,KAAK,OAAO,EAAE,CAAC,EACtC,iBAAkB,WAClB,QAAS,OACT,qBAAsB,CACpB,YAAaA,EAAW,KAAK,OAAO,EAAE,CAAC,EACvC,UAAWA,EAAW,KAAK,OAAO,EAAE,CAAC,CACvC,CACF,CAAC,EAAE,IAAI,EAEP,OAAOQ,EAAI,YAAY,KAAK,KAAK,EAAE,aAAa,CAAC,EACjD,OAAOA,EAAI,YAAY,KAAK,MAAM,CAAC,EAAG,CAAC,CAAC,EAAE,cACxC,IAAIV,EAAwB,CAAE,KAAMO,EAAwB,GAAI,CAAK,CAAC,CACxE,CACF,CAAC,EAED,GAAG,sDAAuD,SAAY,CACpE,MAAMG,EAAMD,EAAY,EACxBC,EAAI,YAAY,sBACdZ,EAAqB,CAAE,KAAMU,CAAU,CAAC,CAC1C,EAEA,MAAM,IAAIP,EAAyBS,EAAK,CACtC,YAAa,QACb,MAAO,WACP,WAAYR,EAAW,0CAA0C,EACjE,iBAAkB,UACpB,CAAC,EAAE,IAAI,EAEP,MAAMU,EAAUF,EAAI,YAAY,KAC7B,MAAM,CAAC,EAAG,CAAC,EAERG,EAAY,OAAO,KAAKD,EAAQ,KAAK,IAAI,EAAE,SAAS,KAAK,EAC/D,OAAOC,CAAS,EAAE,IAAI,UAAU,QAAQ,EACxC,OAAOA,EAAU,SAAS,QAAQ,CAAC,EAAE,KAAK,EAAI,CAChD,CAAC,EAED,GAAG,kCAAmC,SAAY,CAChD,MAAMH,EAAMD,EAAY,EACxBC,EAAI,YAAY,sBACdZ,EAAqB,CACnB,MAAO,IAAIC,EAAuB,gBAAgB,CACpD,CAAC,CACH,EAEA,MAAMY,EAAS,MAAM,IAAIV,EAAyBS,EAAK,CACrD,YAAa,QACb,MAAO,WACP,WAAYR,EAAW,0CAA0C,EACjE,iBAAkB,WAClB,QAAS,EACX,CAAC,EAAE,IAAI,EAEP,OAAOS,CAAM,EAAE,cACbb,EAAqB,CACnB,MAAO,IAAIC,EAAuB,gBAAgB,CACpD,CAAC,CACH,CACF,CAAC,EAED,GAAG,6CAA8C,SAAY,CAC3D,MAAMW,EAAMD,EAAY,EAExB,MAAM,OACJ,IAAIR,EAAyBS,EAAK,CAChC,YAAa,QACb,MAAO,WACP,WAAYR,EAAW,0CAA0C,EACjE,iBAAkB,WAClB,QAAS,EACX,CAAC,EAAE,IAAI,CACT,EAAE,QAAQ,QAAQ,+BAA+B,EACjD,OAAOQ,EAAI,WAAW,EAAE,IAAI,iBAAiB,CAC/C,CAAC,CACH,CAAC",
6
+ "names": ["CommandResultFactory", "InvalidStatusWordError", "RegisterIdentityCommand", "SendRegisterIdentityTask", "hexToBytes", "hex", "out", "i", "FRESH_FRAMED_CHUNK", "EXTENSION_FRAMED_CHUNK", "OK_PROOFS", "makeApiMock", "api", "result", "command", "framedHex"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{ByteArrayBuilder as c,DmkResultFactory as n,InvalidStatusWordError as C,isSuccessCommandResult as l}from"@ledgerhq/device-management-kit";import{RenameContactCommand as T}from"../../app-binder/command/RenameContactCommand";import{RENAME_CONTACT_P1 as p}from"../../app-binder/model/contactsConstants";import{CONTACTS_TLV_TAG as e,encodeTlvAscii as m,encodeTlvBuffer as o,encodeTlvUInt8 as s,packDerivationPath as y,STRUCT_TYPE_EDIT_CONTACT_NAME as u,STRUCT_VERSION_VALUE as A}from"../../app-binder/services/contactsTlvSerializer";import{sendFramedContactsPayload as R}from"../../app-binder/services/sendFramedContactsPayload";class S{constructor(a,r){this.api=a;this.args=r}async run(){const a=this.buildPayload(this.args),r=await R(this.api,{payload:a,p1:p,makeCommand:(d,i)=>new T({data:d,p2:i}),logger:this.args.logger,commandTag:"SendRenameContactTask"});if(!l(r))return r;const{hmacProof:t}=r.data;return t?n({data:{hmacProof:t}}):n({error:new C("RenameContact final-chunk response did not carry hmac_name")})}buildPayload(a){const r=new c;return s(r,e.STRUCT_TYPE,u),s(r,e.STRUCT_VERSION,A),m(r,e.CONTACT_NAME,a.newContactName),m(r,e.PREVIOUS_CONTACT_NAME,a.previousContactName),o(r,e.GROUP_HANDLE,a.groupHandle),o(r,e.DERIVATION_PATH,y([2147483692,2147483708,2147483648,0,0])),o(r,e.HMAC_PROOF,a.hmacProof),r.build()}}export{S as SendRenameContactTask};
2
+ //# sourceMappingURL=SendRenameContactTask.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/internal/app-binder/task/SendRenameContactTask.ts"],
4
+ "sourcesContent": ["// Builds the TLV payload for the Rename Contact (EDIT CONTACT NAME) op and\n// dispatches it via the chunked-framing scheme shared with the other\n// address-book ops (2-byte BE total length + <=255B chunks). Rename is an\n// OS/dashboard command (CLA 0xE0 / INS 0x2E) \u2014 the caller runs this task after\n// navigating to the dashboard (GoToDashboardDeviceAction), never inside an app.\n//\n// The device rotates the contact-level hmac_name and returns the fresh proof on\n// the final chunk; per-entry hmac_rest values are untouched (they bind\n// gid|scope|id|family|chain_id, never the name), so a single APDU regardless of\n// the contact's entry count.\n//\n// Reference: Address Book Final Specifications \u2014 Edit Contact Name. Tag order:\n// STRUCT_TYPE, STRUCT_VERSION, CONTACT_NAME (new), PREVIOUS_CONTACT_NAME\n// (old), GROUP_HANDLE, DERIVATION_PATH, HMAC_PROOF.\nimport {\n ByteArrayBuilder,\n type CommandResult,\n DmkResultFactory,\n type InternalApi,\n InvalidStatusWordError,\n isSuccessCommandResult,\n type LoggerPublisherService,\n} from \"@ledgerhq/device-management-kit\";\n\nimport { RenameContactCommand } from \"@internal/app-binder/command/RenameContactCommand\";\nimport { RENAME_CONTACT_P1 } from \"@internal/app-binder/model/contactsConstants\";\nimport { type ContactsErrorCodes } from \"@internal/app-binder/model/contactsErrors\";\nimport {\n CONTACTS_TLV_TAG,\n encodeTlvAscii,\n encodeTlvBuffer,\n encodeTlvUInt8,\n packDerivationPath,\n STRUCT_TYPE_EDIT_CONTACT_NAME,\n STRUCT_VERSION_VALUE,\n} from \"@internal/app-binder/services/contactsTlvSerializer\";\nimport { sendFramedContactsPayload } from \"@internal/app-binder/services/sendFramedContactsPayload\";\n\nexport type RenameContactProof = {\n readonly hmacProof: Uint8Array;\n};\n\nexport type SendRenameContactTaskArgs = {\n readonly previousContactName: string;\n readonly newContactName: string;\n readonly groupHandle: Uint8Array;\n readonly hmacProof: Uint8Array;\n readonly logger?: LoggerPublisherService;\n};\n\nexport class SendRenameContactTask {\n constructor(\n private readonly api: InternalApi,\n private readonly args: SendRenameContactTaskArgs,\n ) {}\n\n async run(): Promise<CommandResult<RenameContactProof, ContactsErrorCodes>> {\n const payload = this.buildPayload(this.args);\n\n const result = (await sendFramedContactsPayload(this.api, {\n payload,\n p1: RENAME_CONTACT_P1,\n makeCommand: (chunk, p2) => new RenameContactCommand({ data: chunk, p2 }),\n logger: this.args.logger,\n commandTag: \"SendRenameContactTask\",\n })) as CommandResult<\n { readonly hmacProof?: Uint8Array },\n ContactsErrorCodes\n >;\n\n if (!isSuccessCommandResult(result)) {\n return result;\n }\n\n const { hmacProof } = result.data;\n if (!hmacProof) {\n return DmkResultFactory({\n error: new InvalidStatusWordError(\n \"RenameContact final-chunk response did not carry hmac_name\",\n ),\n });\n }\n return DmkResultFactory({ data: { hmacProof } });\n }\n\n private buildPayload(args: SendRenameContactTaskArgs): Uint8Array {\n const builder = new ByteArrayBuilder();\n encodeTlvUInt8(\n builder,\n CONTACTS_TLV_TAG.STRUCT_TYPE,\n STRUCT_TYPE_EDIT_CONTACT_NAME,\n );\n encodeTlvUInt8(\n builder,\n CONTACTS_TLV_TAG.STRUCT_VERSION,\n STRUCT_VERSION_VALUE,\n );\n encodeTlvAscii(builder, CONTACTS_TLV_TAG.CONTACT_NAME, args.newContactName);\n encodeTlvAscii(\n builder,\n CONTACTS_TLV_TAG.PREVIOUS_CONTACT_NAME,\n args.previousContactName,\n );\n encodeTlvBuffer(builder, CONTACTS_TLV_TAG.GROUP_HANDLE, args.groupHandle);\n // The Ethereum app requires DERIVATION_PATH on Edit Contact Name (0x6a80\n // without it), same as Register Identity. Default m/44'/60'/0'/0/0.\n encodeTlvBuffer(\n builder,\n CONTACTS_TLV_TAG.DERIVATION_PATH,\n packDerivationPath([0x8000002c, 0x8000003c, 0x80000000, 0, 0]),\n );\n encodeTlvBuffer(builder, CONTACTS_TLV_TAG.HMAC_PROOF, args.hmacProof);\n\n return builder.build();\n }\n}\n"],
5
+ "mappings": "AAcA,OACE,oBAAAA,EAEA,oBAAAC,EAEA,0BAAAC,EACA,0BAAAC,MAEK,kCAEP,OAAS,wBAAAC,MAA4B,oDACrC,OAAS,qBAAAC,MAAyB,+CAElC,OACE,oBAAAC,EACA,kBAAAC,EACA,mBAAAC,EACA,kBAAAC,EACA,sBAAAC,EACA,iCAAAC,EACA,wBAAAC,MACK,sDACP,OAAS,6BAAAC,MAAiC,0DAcnC,MAAMC,CAAsB,CACjC,YACmBC,EACAC,EACjB,CAFiB,SAAAD,EACA,UAAAC,CAChB,CAEH,MAAM,KAAsE,CAC1E,MAAMC,EAAU,KAAK,aAAa,KAAK,IAAI,EAErCC,EAAU,MAAML,EAA0B,KAAK,IAAK,CACxD,QAAAI,EACA,GAAIZ,EACJ,YAAa,CAACc,EAAOC,IAAO,IAAIhB,EAAqB,CAAE,KAAMe,EAAO,GAAAC,CAAG,CAAC,EACxE,OAAQ,KAAK,KAAK,OAClB,WAAY,uBACd,CAAC,EAKD,GAAI,CAACjB,EAAuBe,CAAM,EAChC,OAAOA,EAGT,KAAM,CAAE,UAAAG,CAAU,EAAIH,EAAO,KAC7B,OAAKG,EAOEpB,EAAiB,CAAE,KAAM,CAAE,UAAAoB,CAAU,CAAE,CAAC,EANtCpB,EAAiB,CACtB,MAAO,IAAIC,EACT,4DACF,CACF,CAAC,CAGL,CAEQ,aAAac,EAA6C,CAChE,MAAMM,EAAU,IAAItB,EACpB,OAAAS,EACEa,EACAhB,EAAiB,YACjBK,CACF,EACAF,EACEa,EACAhB,EAAiB,eACjBM,CACF,EACAL,EAAee,EAAShB,EAAiB,aAAcU,EAAK,cAAc,EAC1ET,EACEe,EACAhB,EAAiB,sBACjBU,EAAK,mBACP,EACAR,EAAgBc,EAAShB,EAAiB,aAAcU,EAAK,WAAW,EAGxER,EACEc,EACAhB,EAAiB,gBACjBI,EAAmB,CAAC,WAAY,WAAY,WAAY,EAAG,CAAC,CAAC,CAC/D,EACAF,EAAgBc,EAAShB,EAAiB,WAAYU,EAAK,SAAS,EAE7DM,EAAQ,MAAM,CACvB,CACF",
6
+ "names": ["ByteArrayBuilder", "DmkResultFactory", "InvalidStatusWordError", "isSuccessCommandResult", "RenameContactCommand", "RENAME_CONTACT_P1", "CONTACTS_TLV_TAG", "encodeTlvAscii", "encodeTlvBuffer", "encodeTlvUInt8", "packDerivationPath", "STRUCT_TYPE_EDIT_CONTACT_NAME", "STRUCT_VERSION_VALUE", "sendFramedContactsPayload", "SendRenameContactTask", "api", "args", "payload", "result", "chunk", "p2", "hmacProof", "builder"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{CommandResultFactory as t,InvalidStatusWordError as d}from"@ledgerhq/device-management-kit";import{RenameContactCommand as p}from"../../app-binder/command/RenameContactCommand";import{SendRenameContactTask as o}from"./SendRenameContactTask";function s(e){const n=new Uint8Array(e.length/2);for(let a=0;a<e.length;a+=2)n[a/2]=parseInt(e.slice(a,a+2),16);return n}const r=s("cc".repeat(64)),c=s("dd".repeat(32)),i=s("ee".repeat(32)),l="6915058000002c8000003c800000000000000000000000",u=s("009001012e02010181f003426f6281f305416c69636581f640"+"cc".repeat(64)+l+"2920"+"dd".repeat(32));function m(){return{sendCommand:vi.fn()}}describe("SendRenameContactTask",()=>{it("assembles the rename framed chunk and dispatches RenameContactCommand",async()=>{const e=m();e.sendCommand.mockResolvedValueOnce(t({data:{hmacProof:i}}));const n=await new o(e,{previousContactName:"Alice",newContactName:"Bob",groupHandle:r,hmacProof:c}).run();expect(e.sendCommand.mock.calls).toHaveLength(1),expect(e.sendCommand.mock.calls[0][0]).toStrictEqual(new p({data:u,p2:0})),expect(n).toStrictEqual(t({data:{hmacProof:i}}))}),it("emits the DERIVATION_PATH tag immediately before HMAC_PROOF",async()=>{const e=m();e.sendCommand.mockResolvedValueOnce(t({data:{hmacProof:i}})),await new o(e,{previousContactName:"Alice",newContactName:"Bob",groupHandle:r,hmacProof:c}).run();const n=e.sendCommand.mock.calls[0][0],a=Buffer.from(n.args.data).toString("hex");expect(a.endsWith(l+"2920"+"dd".repeat(32))).toBe(!0)}),it("propagates command-level errors",async()=>{const e=m();e.sendCommand.mockResolvedValueOnce(t({error:new d("user cancelled")}));const n=await new o(e,{previousContactName:"Alice",newContactName:"Bob",groupHandle:r,hmacProof:c}).run();expect(n).toStrictEqual(t({error:new d("user cancelled")}))}),it("returns InvalidStatusWordError when the final response carries no hmac_name",async()=>{const e=m();e.sendCommand.mockResolvedValueOnce(t({data:{}}));const n=await new o(e,{previousContactName:"Alice",newContactName:"Bob",groupHandle:r,hmacProof:c}).run();expect(n.status).not.toBe(void 0),"error"in n&&expect(n.error).toBeInstanceOf(d)})});
2
+ //# sourceMappingURL=SendRenameContactTask.test.js.map