@ledgerhq/device-contacts-kit 0.0.0-develop-20261008001410 → 0.0.0-develop-20261010001520

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 (83) hide show
  1. package/README.md +14 -2
  2. package/lib/cjs/api/index.js +1 -1
  3. package/lib/cjs/api/index.js.map +2 -2
  4. package/lib/cjs/api/model/ContactsVersionRequirements.js +1 -1
  5. package/lib/cjs/api/model/ContactsVersionRequirements.js.map +3 -3
  6. package/lib/cjs/api/model/ContactsVersionRequirements.test.js +1 -1
  7. package/lib/cjs/api/model/ContactsVersionRequirements.test.js.map +2 -2
  8. package/lib/cjs/api/model/EditExternalAddressIdentifier.js.map +1 -1
  9. package/lib/cjs/api/model/EditExternalAddressScope.js.map +1 -1
  10. package/lib/cjs/api/model/ProvideContact.js.map +1 -1
  11. package/lib/cjs/api/model/RegisterExternalAddress.js.map +1 -1
  12. package/lib/cjs/internal/app-binder/contactsVersionGuards.test.js +1 -1
  13. package/lib/cjs/internal/app-binder/contactsVersionGuards.test.js.map +3 -3
  14. package/lib/cjs/internal/app-binder/device-action/EditExternalAddressIdentifier/validateEditExternalAddressIdentifierInput.js +1 -1
  15. package/lib/cjs/internal/app-binder/device-action/EditExternalAddressIdentifier/validateEditExternalAddressIdentifierInput.js.map +3 -3
  16. package/lib/cjs/internal/app-binder/device-action/EditExternalAddressIdentifier/validateEditExternalAddressIdentifierInput.test.js +1 -1
  17. package/lib/cjs/internal/app-binder/device-action/EditExternalAddressIdentifier/validateEditExternalAddressIdentifierInput.test.js.map +3 -3
  18. package/lib/cjs/internal/app-binder/device-action/EditExternalAddressScope/validateEditExternalAddressScopeInput.js +1 -1
  19. package/lib/cjs/internal/app-binder/device-action/EditExternalAddressScope/validateEditExternalAddressScopeInput.js.map +3 -3
  20. package/lib/cjs/internal/app-binder/device-action/EditExternalAddressScope/validateEditExternalAddressScopeInput.test.js +1 -1
  21. package/lib/cjs/internal/app-binder/device-action/EditExternalAddressScope/validateEditExternalAddressScopeInput.test.js.map +3 -3
  22. package/lib/cjs/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.js +1 -1
  23. package/lib/cjs/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.js.map +3 -3
  24. package/lib/cjs/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.test.js +1 -1
  25. package/lib/cjs/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.test.js.map +3 -3
  26. package/lib/cjs/internal/app-binder/model/contactsValidation.js +1 -1
  27. package/lib/cjs/internal/app-binder/model/contactsValidation.js.map +3 -3
  28. package/lib/cjs/internal/app-binder/model/contactsValidation.test.js +1 -1
  29. package/lib/cjs/internal/app-binder/model/contactsValidation.test.js.map +3 -3
  30. package/lib/cjs/internal/app-binder/services/provideContact.js +1 -1
  31. package/lib/cjs/internal/app-binder/services/provideContact.js.map +3 -3
  32. package/lib/cjs/internal/app-binder/services/provideContact.test.js +1 -1
  33. package/lib/cjs/internal/app-binder/services/provideContact.test.js.map +2 -2
  34. package/lib/cjs/package.json +1 -1
  35. package/lib/esm/api/index.js +1 -1
  36. package/lib/esm/api/index.js.map +3 -3
  37. package/lib/esm/api/model/ContactsVersionRequirements.js +1 -1
  38. package/lib/esm/api/model/ContactsVersionRequirements.js.map +3 -3
  39. package/lib/esm/api/model/ContactsVersionRequirements.test.js +1 -1
  40. package/lib/esm/api/model/ContactsVersionRequirements.test.js.map +3 -3
  41. package/lib/esm/internal/app-binder/contactsVersionGuards.test.js +1 -1
  42. package/lib/esm/internal/app-binder/contactsVersionGuards.test.js.map +3 -3
  43. package/lib/esm/internal/app-binder/device-action/EditExternalAddressIdentifier/validateEditExternalAddressIdentifierInput.js +1 -1
  44. package/lib/esm/internal/app-binder/device-action/EditExternalAddressIdentifier/validateEditExternalAddressIdentifierInput.js.map +3 -3
  45. package/lib/esm/internal/app-binder/device-action/EditExternalAddressIdentifier/validateEditExternalAddressIdentifierInput.test.js +1 -1
  46. package/lib/esm/internal/app-binder/device-action/EditExternalAddressIdentifier/validateEditExternalAddressIdentifierInput.test.js.map +3 -3
  47. package/lib/esm/internal/app-binder/device-action/EditExternalAddressScope/validateEditExternalAddressScopeInput.js +1 -1
  48. package/lib/esm/internal/app-binder/device-action/EditExternalAddressScope/validateEditExternalAddressScopeInput.js.map +3 -3
  49. package/lib/esm/internal/app-binder/device-action/EditExternalAddressScope/validateEditExternalAddressScopeInput.test.js +1 -1
  50. package/lib/esm/internal/app-binder/device-action/EditExternalAddressScope/validateEditExternalAddressScopeInput.test.js.map +3 -3
  51. package/lib/esm/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.js +1 -1
  52. package/lib/esm/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.js.map +3 -3
  53. package/lib/esm/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.test.js +1 -1
  54. package/lib/esm/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.test.js.map +3 -3
  55. package/lib/esm/internal/app-binder/model/contactsValidation.js +1 -1
  56. package/lib/esm/internal/app-binder/model/contactsValidation.js.map +3 -3
  57. package/lib/esm/internal/app-binder/model/contactsValidation.test.js +1 -1
  58. package/lib/esm/internal/app-binder/model/contactsValidation.test.js.map +3 -3
  59. package/lib/esm/internal/app-binder/services/provideContact.js +1 -1
  60. package/lib/esm/internal/app-binder/services/provideContact.js.map +3 -3
  61. package/lib/esm/internal/app-binder/services/provideContact.test.js +1 -1
  62. package/lib/esm/internal/app-binder/services/provideContact.test.js.map +2 -2
  63. package/lib/esm/package.json +1 -1
  64. package/lib/types/api/index.d.ts +1 -1
  65. package/lib/types/api/index.d.ts.map +1 -1
  66. package/lib/types/api/model/ContactsVersionRequirements.d.ts +1 -0
  67. package/lib/types/api/model/ContactsVersionRequirements.d.ts.map +1 -1
  68. package/lib/types/api/model/EditExternalAddressIdentifier.d.ts +2 -2
  69. package/lib/types/api/model/EditExternalAddressIdentifier.d.ts.map +1 -1
  70. package/lib/types/api/model/EditExternalAddressScope.d.ts +2 -2
  71. package/lib/types/api/model/EditExternalAddressScope.d.ts.map +1 -1
  72. package/lib/types/api/model/ProvideContact.d.ts +1 -1
  73. package/lib/types/api/model/ProvideContact.d.ts.map +1 -1
  74. package/lib/types/api/model/RegisterExternalAddress.d.ts +2 -2
  75. package/lib/types/api/model/RegisterExternalAddress.d.ts.map +1 -1
  76. package/lib/types/internal/app-binder/device-action/EditExternalAddressIdentifier/validateEditExternalAddressIdentifierInput.d.ts.map +1 -1
  77. package/lib/types/internal/app-binder/device-action/EditExternalAddressScope/validateEditExternalAddressScopeInput.d.ts.map +1 -1
  78. package/lib/types/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.d.ts.map +1 -1
  79. package/lib/types/internal/app-binder/model/contactsValidation.d.ts +23 -0
  80. package/lib/types/internal/app-binder/model/contactsValidation.d.ts.map +1 -1
  81. package/lib/types/internal/app-binder/services/provideContact.d.ts.map +1 -1
  82. package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
  83. package/package.json +7 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/api/model/ContactsVersionRequirements.test.ts"],
4
- "sourcesContent": ["import { DeviceModelId } from \"@ledgerhq/device-management-kit\";\n\nimport {\n CONTACTS_VERSION_REQUIREMENTS,\n ETHEREUM_APP_NAME,\n isVersionAtLeast,\n isVersionBelow,\n renameRequiresDerivationPath,\n resolveContactsVersionRequirements,\n TRON_APP_NAME,\n} from \"./ContactsVersionRequirements\";\n\ndescribe(\"ContactsVersionRequirements\", () => {\n describe(\"resolveContactsVersionRequirements\", () => {\n it(\"marks every device model, supported or not\", () => {\n for (const modelId of Object.values(DeviceModelId)) {\n expect(CONTACTS_VERSION_REQUIREMENTS[modelId]).toBeDefined();\n }\n });\n\n it(\"reports Nano S as unsupported\", () => {\n expect(resolveContactsVersionRequirements(DeviceModelId.NANO_S)).toEqual({\n supported: false,\n });\n });\n\n // Pins the minimums themselves, not just their shape: each OS version is\n // the release whose changelog introduces the Address Book feature, so a\n // change here has to be a deliberate one, reviewed against that release.\n it(\"pins the minimum OS and app versions of every supported model\", () => {\n const appVersions = {\n [ETHEREUM_APP_NAME]: \"1.23.0\",\n [TRON_APP_NAME]: \"0.8.0\",\n };\n\n expect(CONTACTS_VERSION_REQUIREMENTS).toEqual({\n [DeviceModelId.NANO_S]: { supported: false },\n [DeviceModelId.NANO_SP]: {\n supported: true,\n minOsVersion: \"1.7.0\",\n minAppVersion: appVersions,\n renameDerivationPathRequiredBelowOsVersion: \"1.7.0-rc3\",\n },\n [DeviceModelId.NANO_X]: {\n supported: true,\n minOsVersion: \"2.8.0\",\n minAppVersion: appVersions,\n renameDerivationPathRequiredBelowOsVersion: \"2.8.0-rc3\",\n },\n [DeviceModelId.STAX]: {\n supported: true,\n minOsVersion: \"1.11.0\",\n minAppVersion: appVersions,\n renameDerivationPathRequiredBelowOsVersion: \"1.11.0-rc3\",\n },\n [DeviceModelId.FLEX]: {\n supported: true,\n minOsVersion: \"1.7.0\",\n minAppVersion: appVersions,\n renameDerivationPathRequiredBelowOsVersion: \"1.7.0-rc3\",\n },\n [DeviceModelId.APEX]: {\n supported: true,\n minOsVersion: \"1.2.0\",\n minAppVersion: appVersions,\n renameDerivationPathRequiredBelowOsVersion: \"1.2.0-rc3\",\n },\n });\n });\n });\n\n describe(\"isVersionAtLeast\", () => {\n it(\"compares versions as semver, inclusive of equality\", () => {\n expect(isVersionAtLeast(\"1.2.0\", \"1.2.0\")).toBe(true);\n expect(isVersionAtLeast(\"1.3.0\", \"1.2.0\")).toBe(true);\n expect(isVersionAtLeast(\"1.1.9\", \"1.2.0\")).toBe(false);\n });\n\n it(\"coerces non-strict version strings\", () => {\n expect(isVersionAtLeast(\"1.2\", \"1.2.0\")).toBe(true);\n });\n\n it(\"returns false when a version cannot be parsed\", () => {\n expect(isVersionAtLeast(\"not-a-version\", \"1.2.0\")).toBe(false);\n expect(isVersionAtLeast(\"1.2.0\", \"not-a-version\")).toBe(false);\n });\n\n it(\"ignores prerelease and build tags when comparing to the minimum\", () => {\n expect(isVersionAtLeast(\"1.7.0-rc2\", \"1.7.0\")).toBe(true);\n expect(isVersionAtLeast(\"1.23.0-dev\", \"1.23.0\")).toBe(true);\n });\n\n it(\"still fails a minimum strictly above the tagged version's core\", () => {\n expect(isVersionAtLeast(\"1.6.9-rc5\", \"1.7.0\")).toBe(false);\n });\n });\n\n describe(\"isVersionBelow\", () => {\n it(\"orders release candidates of the same core (unlike isVersionAtLeast)\", () => {\n // The whole reason this helper exists: rc2 must sort below rc3.\n expect(isVersionBelow(\"1.7.0-rc2\", \"1.7.0-rc3\")).toBe(true);\n expect(isVersionBelow(\"1.7.0-rc3\", \"1.7.0-rc3\")).toBe(false);\n expect(isVersionBelow(\"1.7.0-rc4\", \"1.7.0-rc3\")).toBe(false);\n });\n\n it(\"treats the final release as newer than any of its prereleases\", () => {\n expect(isVersionBelow(\"1.7.0\", \"1.7.0-rc3\")).toBe(false);\n });\n\n it(\"compares the release core when it differs\", () => {\n expect(isVersionBelow(\"1.6.9\", \"1.7.0-rc3\")).toBe(true);\n expect(isVersionBelow(\"1.8.0\", \"1.7.0-rc3\")).toBe(false);\n });\n\n it(\"does not coerce, so a non-strict version is unparseable and returns false\", () => {\n expect(isVersionBelow(\"1.7\", \"1.7.0-rc3\")).toBe(false);\n expect(isVersionBelow(\"not-a-version\", \"1.7.0-rc3\")).toBe(false);\n expect(isVersionBelow(\"1.7.0-rc2\", \"not-a-version\")).toBe(false);\n });\n });\n\n describe(\"renameRequiresDerivationPath\", () => {\n it(\"requires the path below the model's cutoff (rc2) and not at/after it\", () => {\n expect(\n renameRequiresDerivationPath(DeviceModelId.FLEX, \"1.7.0-rc2\"),\n ).toBe(true);\n expect(\n renameRequiresDerivationPath(DeviceModelId.FLEX, \"1.7.0-rc3\"),\n ).toBe(false);\n // GA and later never want the path.\n expect(renameRequiresDerivationPath(DeviceModelId.FLEX, \"1.7.0\")).toBe(\n false,\n );\n expect(renameRequiresDerivationPath(DeviceModelId.FLEX, \"1.8.0\")).toBe(\n false,\n );\n });\n\n it(\"applies the same rc2/rc3 split to the other supported models\", () => {\n expect(\n renameRequiresDerivationPath(DeviceModelId.STAX, \"1.11.0-rc2\"),\n ).toBe(true);\n expect(\n renameRequiresDerivationPath(DeviceModelId.STAX, \"1.11.0-rc3\"),\n ).toBe(false);\n expect(\n renameRequiresDerivationPath(DeviceModelId.NANO_SP, \"1.7.0-rc2\"),\n ).toBe(true);\n });\n\n it(\"returns false for an unsupported model\", () => {\n expect(\n renameRequiresDerivationPath(DeviceModelId.NANO_S, \"1.7.0-rc2\"),\n ).toBe(false);\n });\n\n it(\"returns false (no path) when the OS version is unparseable\", () => {\n expect(renameRequiresDerivationPath(DeviceModelId.FLEX, \"\")).toBe(false);\n });\n });\n});\n"],
5
- "mappings": "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",
6
- "names": ["DeviceModelId", "CONTACTS_VERSION_REQUIREMENTS", "ETHEREUM_APP_NAME", "isVersionAtLeast", "isVersionBelow", "renameRequiresDerivationPath", "resolveContactsVersionRequirements", "TRON_APP_NAME", "modelId", "appVersions"]
4
+ "sourcesContent": ["import { DeviceModelId } from \"@ledgerhq/device-management-kit\";\n\nimport {\n CONTACTS_VERSION_REQUIREMENTS,\n ETHEREUM_APP_NAME,\n isVersionAtLeast,\n isVersionBelow,\n renameRequiresDerivationPath,\n resolveContactsVersionRequirements,\n SOLANA_APP_NAME,\n TRON_APP_NAME,\n} from \"./ContactsVersionRequirements\";\n\ndescribe(\"ContactsVersionRequirements\", () => {\n describe(\"resolveContactsVersionRequirements\", () => {\n it(\"marks every device model, supported or not\", () => {\n for (const modelId of Object.values(DeviceModelId)) {\n expect(CONTACTS_VERSION_REQUIREMENTS[modelId]).toBeDefined();\n }\n });\n\n it(\"reports Nano S as unsupported\", () => {\n expect(resolveContactsVersionRequirements(DeviceModelId.NANO_S)).toEqual({\n supported: false,\n });\n });\n\n // Pins the minimums themselves, not just their shape: each OS version is\n // the release whose changelog introduces the Address Book feature, so a\n // change here has to be a deliberate one, reviewed against that release.\n it(\"pins the minimum OS and app versions of every supported model\", () => {\n const appVersions = {\n [ETHEREUM_APP_NAME]: \"1.23.0\",\n [TRON_APP_NAME]: \"0.8.0\",\n [SOLANA_APP_NAME]: \"10.0.0\",\n };\n\n expect(CONTACTS_VERSION_REQUIREMENTS).toEqual({\n [DeviceModelId.NANO_S]: { supported: false },\n [DeviceModelId.NANO_SP]: {\n supported: true,\n minOsVersion: \"1.7.0\",\n minAppVersion: appVersions,\n renameDerivationPathRequiredBelowOsVersion: \"1.7.0-rc3\",\n },\n [DeviceModelId.NANO_X]: {\n supported: true,\n minOsVersion: \"2.8.0\",\n minAppVersion: appVersions,\n renameDerivationPathRequiredBelowOsVersion: \"2.8.0-rc3\",\n },\n [DeviceModelId.STAX]: {\n supported: true,\n minOsVersion: \"1.11.0\",\n minAppVersion: appVersions,\n renameDerivationPathRequiredBelowOsVersion: \"1.11.0-rc3\",\n },\n [DeviceModelId.FLEX]: {\n supported: true,\n minOsVersion: \"1.7.0\",\n minAppVersion: appVersions,\n renameDerivationPathRequiredBelowOsVersion: \"1.7.0-rc3\",\n },\n [DeviceModelId.APEX]: {\n supported: true,\n minOsVersion: \"1.2.0\",\n minAppVersion: appVersions,\n renameDerivationPathRequiredBelowOsVersion: \"1.2.0-rc3\",\n },\n });\n });\n });\n\n describe(\"isVersionAtLeast\", () => {\n it(\"compares versions as semver, inclusive of equality\", () => {\n expect(isVersionAtLeast(\"1.2.0\", \"1.2.0\")).toBe(true);\n expect(isVersionAtLeast(\"1.3.0\", \"1.2.0\")).toBe(true);\n expect(isVersionAtLeast(\"1.1.9\", \"1.2.0\")).toBe(false);\n });\n\n it(\"coerces non-strict version strings\", () => {\n expect(isVersionAtLeast(\"1.2\", \"1.2.0\")).toBe(true);\n });\n\n it(\"returns false when a version cannot be parsed\", () => {\n expect(isVersionAtLeast(\"not-a-version\", \"1.2.0\")).toBe(false);\n expect(isVersionAtLeast(\"1.2.0\", \"not-a-version\")).toBe(false);\n });\n\n it(\"ignores prerelease and build tags when comparing to the minimum\", () => {\n expect(isVersionAtLeast(\"1.7.0-rc2\", \"1.7.0\")).toBe(true);\n expect(isVersionAtLeast(\"1.23.0-dev\", \"1.23.0\")).toBe(true);\n });\n\n it(\"still fails a minimum strictly above the tagged version's core\", () => {\n expect(isVersionAtLeast(\"1.6.9-rc5\", \"1.7.0\")).toBe(false);\n });\n });\n\n describe(\"isVersionBelow\", () => {\n it(\"orders release candidates of the same core (unlike isVersionAtLeast)\", () => {\n // The whole reason this helper exists: rc2 must sort below rc3.\n expect(isVersionBelow(\"1.7.0-rc2\", \"1.7.0-rc3\")).toBe(true);\n expect(isVersionBelow(\"1.7.0-rc3\", \"1.7.0-rc3\")).toBe(false);\n expect(isVersionBelow(\"1.7.0-rc4\", \"1.7.0-rc3\")).toBe(false);\n });\n\n it(\"treats the final release as newer than any of its prereleases\", () => {\n expect(isVersionBelow(\"1.7.0\", \"1.7.0-rc3\")).toBe(false);\n });\n\n it(\"compares the release core when it differs\", () => {\n expect(isVersionBelow(\"1.6.9\", \"1.7.0-rc3\")).toBe(true);\n expect(isVersionBelow(\"1.8.0\", \"1.7.0-rc3\")).toBe(false);\n });\n\n it(\"does not coerce, so a non-strict version is unparseable and returns false\", () => {\n expect(isVersionBelow(\"1.7\", \"1.7.0-rc3\")).toBe(false);\n expect(isVersionBelow(\"not-a-version\", \"1.7.0-rc3\")).toBe(false);\n expect(isVersionBelow(\"1.7.0-rc2\", \"not-a-version\")).toBe(false);\n });\n });\n\n describe(\"renameRequiresDerivationPath\", () => {\n it(\"requires the path below the model's cutoff (rc2) and not at/after it\", () => {\n expect(\n renameRequiresDerivationPath(DeviceModelId.FLEX, \"1.7.0-rc2\"),\n ).toBe(true);\n expect(\n renameRequiresDerivationPath(DeviceModelId.FLEX, \"1.7.0-rc3\"),\n ).toBe(false);\n // GA and later never want the path.\n expect(renameRequiresDerivationPath(DeviceModelId.FLEX, \"1.7.0\")).toBe(\n false,\n );\n expect(renameRequiresDerivationPath(DeviceModelId.FLEX, \"1.8.0\")).toBe(\n false,\n );\n });\n\n it(\"applies the same rc2/rc3 split to the other supported models\", () => {\n expect(\n renameRequiresDerivationPath(DeviceModelId.STAX, \"1.11.0-rc2\"),\n ).toBe(true);\n expect(\n renameRequiresDerivationPath(DeviceModelId.STAX, \"1.11.0-rc3\"),\n ).toBe(false);\n expect(\n renameRequiresDerivationPath(DeviceModelId.NANO_SP, \"1.7.0-rc2\"),\n ).toBe(true);\n });\n\n it(\"returns false for an unsupported model\", () => {\n expect(\n renameRequiresDerivationPath(DeviceModelId.NANO_S, \"1.7.0-rc2\"),\n ).toBe(false);\n });\n\n it(\"returns false (no path) when the OS version is unparseable\", () => {\n expect(renameRequiresDerivationPath(DeviceModelId.FLEX, \"\")).toBe(false);\n });\n });\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["DeviceModelId", "CONTACTS_VERSION_REQUIREMENTS", "ETHEREUM_APP_NAME", "isVersionAtLeast", "isVersionBelow", "renameRequiresDerivationPath", "resolveContactsVersionRequirements", "SOLANA_APP_NAME", "TRON_APP_NAME", "modelId", "appVersions"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{DeviceModelId as i,DeviceSessionStateType as d,DeviceStatus as l}from"@ledgerhq/device-management-kit";import{ETHEREUM_APP_NAME as c,resolveContactsVersionRequirements as f}from"../../api/model/ContactsVersionRequirements";import{isContactsAppVersionSupportedForSession as s,isContactsOsVersionSupportedForSession as o}from"./contactsVersionGuards";const m=(()=>{const e=f(i.FLEX);if(!e.supported)throw new Error("Flex must be supported");return e})(),a=m.minOsVersion,S=(()=>{const e=m.minAppVersion[c];if(e===void 0)throw new Error("Ethereum min version required");return e})(),p="0.0.1";function n({osVersion:e=a,modelId:u=i.FLEX}={}){return{sessionStateType:d.ReadyWithoutSecureChannel,deviceStatus:l.CONNECTED,installedApps:[],currentApp:{name:c,version:"0.0.0"},deviceModelId:u,firmwareVersion:{mcu:"1.0.0",bootloader:"1.0.0",os:e},isSecureConnectionAllowed:!1}}function t(e){return{getDeviceSessionState:()=>e}}function r(e=c,u=S){return{name:e,version:u}}describe("isContactsAppVersionSupportedForSession",()=>{it("returns true when the model and app version meet the minimums",()=>{const e=t(n());expect(s(e,r())).toBe(!0)}),it("returns true regardless of OS version, since app-owned operations are not gated by it",()=>{const e=t(n({osVersion:p}));expect(s(e,r())).toBe(!0)}),it("returns false on an unsupported device model",()=>{const e=t(n({modelId:i.NANO_S}));expect(s(e,r())).toBe(!1)}),it("returns false when the app version is below the minimum",()=>{const e=t(n());expect(s(e,r(c,p))).toBe(!1)}),it("returns false when the running app is unknown to Contacts",()=>{const e=t(n());expect(s(e,r("Bitcoin"))).toBe(!1)}),it("returns true for a prerelease build of the minimum app version",()=>{const e=t(n());expect(s(e,r(c,`${S}-rc2`))).toBe(!0)}),it("returns false when the device session has no running app",()=>{const e=t({sessionStateType:d.Connected,deviceStatus:l.CONNECTED,deviceModelId:i.FLEX});expect(s(e,r())).toBe(!1)})});describe("isContactsOsVersionSupportedForSession",()=>{it("returns true when the model and the caller's OS version meet the minimums",()=>{const e=t(n());expect(o(e,a)).toBe(!0)}),it("returns false on an unsupported device model",()=>{const e=t(n({modelId:i.NANO_S}));expect(o(e,a)).toBe(!1)}),it("returns false when the caller's OS version is below the minimum",()=>{const e=t(n());expect(o(e,p)).toBe(!1)}),it("returns true for a release candidate of the minimum OS version",()=>{const e=t(n());expect(o(e,`${a}-rc2`)).toBe(!0)}),it("returns false for a release candidate below the minimum OS version",()=>{const e=t(n());expect(o(e,`${p}-rc2`)).toBe(!1)}),it("never reads the OS version from the device session state",()=>{const e=t({sessionStateType:d.Connected,deviceStatus:l.CONNECTED,deviceModelId:i.FLEX});expect(o(e,a)).toBe(!0),expect(o(e,p)).toBe(!1)})});
1
+ import{DeviceModelId as a,DeviceSessionStateType as d,DeviceStatus as l}from"@ledgerhq/device-management-kit";import{ETHEREUM_APP_NAME as u,resolveContactsVersionRequirements as h,SOLANA_APP_NAME as f,TRON_APP_NAME as A}from"../../api/model/ContactsVersionRequirements";import{isContactsAppVersionSupportedForSession as s,isContactsOsVersionSupportedForSession as i}from"./contactsVersionGuards";const m=(()=>{const e=h(a.FLEX);if(!e.supported)throw new Error("Flex must be supported");return e})(),p=m.minOsVersion,S=(()=>{const e=m.minAppVersion[u];if(e===void 0)throw new Error("Ethereum min version required");return e})(),c="0.0.1";function n({osVersion:e=p,modelId:r=a.FLEX}={}){return{sessionStateType:d.ReadyWithoutSecureChannel,deviceStatus:l.CONNECTED,installedApps:[],currentApp:{name:u,version:"0.0.0"},deviceModelId:r,firmwareVersion:{mcu:"1.0.0",bootloader:"1.0.0",os:e},isSecureConnectionAllowed:!1}}function t(e){return{getDeviceSessionState:()=>e}}function o(e=u,r=S){return{name:e,version:r}}describe("isContactsAppVersionSupportedForSession",()=>{it("returns true when the model and app version meet the minimums",()=>{const e=t(n());expect(s(e,o())).toBe(!0)}),it("returns true regardless of OS version, since app-owned operations are not gated by it",()=>{const e=t(n({osVersion:c}));expect(s(e,o())).toBe(!0)}),it("returns false on an unsupported device model",()=>{const e=t(n({modelId:a.NANO_S}));expect(s(e,o())).toBe(!1)}),it("returns false when the app version is below the minimum",()=>{const e=t(n());expect(s(e,o(u,c))).toBe(!1)}),it("returns false when the running app is unknown to Contacts",()=>{const e=t(n());expect(s(e,o("Bitcoin"))).toBe(!1)}),it.each([A,f])("returns true for the %s app at its minimum version",e=>{const r=m.minAppVersion[e];if(r===void 0)throw new Error(`${e} min version`);const v=t(n());expect(s(v,o(e,r))).toBe(!0)}),it("returns false for a Solana app below its minimum version",()=>{const e=t(n());expect(s(e,o(f,"1.16.0"))).toBe(!1)}),it("returns true for a prerelease build of the minimum app version",()=>{const e=t(n());expect(s(e,o(u,`${S}-rc2`))).toBe(!0)}),it("returns false when the device session has no running app",()=>{const e=t({sessionStateType:d.Connected,deviceStatus:l.CONNECTED,deviceModelId:a.FLEX});expect(s(e,o())).toBe(!1)})});describe("isContactsOsVersionSupportedForSession",()=>{it("returns true when the model and the caller's OS version meet the minimums",()=>{const e=t(n());expect(i(e,p)).toBe(!0)}),it("returns false on an unsupported device model",()=>{const e=t(n({modelId:a.NANO_S}));expect(i(e,p)).toBe(!1)}),it("returns false when the caller's OS version is below the minimum",()=>{const e=t(n());expect(i(e,c)).toBe(!1)}),it("returns true for a release candidate of the minimum OS version",()=>{const e=t(n());expect(i(e,`${p}-rc2`)).toBe(!0)}),it("returns false for a release candidate below the minimum OS version",()=>{const e=t(n());expect(i(e,`${c}-rc2`)).toBe(!1)}),it("never reads the OS version from the device session state",()=>{const e=t({sessionStateType:d.Connected,deviceStatus:l.CONNECTED,deviceModelId:a.FLEX});expect(i(e,p)).toBe(!0),expect(i(e,c)).toBe(!1)})});
2
2
  //# sourceMappingURL=contactsVersionGuards.test.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/internal/app-binder/contactsVersionGuards.test.ts"],
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 isContactsAppVersionSupportedForSession,\n isContactsOsVersionSupportedForSession,\n type RunningApp,\n} from \"./contactsVersionGuards\";\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(\"isContactsAppVersionSupportedForSession\", () => {\n it(\"returns true when the model and app version meet the minimums\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n true,\n );\n });\n\n it(\"returns true regardless of OS version, since app-owned operations are not gated by it\", () => {\n const api = createInternalApi(\n createReadyState({ osVersion: BELOW_ANY_VERSION }),\n );\n expect(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n true,\n );\n });\n\n it(\"returns false on an unsupported device model\", () => {\n const api = createInternalApi(\n createReadyState({ modelId: DeviceModelId.NANO_S }),\n );\n expect(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n false,\n );\n });\n\n it(\"returns false when the app version is below the minimum\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsAppVersionSupportedForSession(\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(\n isContactsAppVersionSupportedForSession(api, runningApp(\"Bitcoin\")),\n ).toBe(false);\n });\n\n it(\"returns true for a prerelease build of the minimum app version\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsAppVersionSupportedForSession(\n api,\n runningApp(ETHEREUM_APP_NAME, `${MIN_APP_VERSION}-rc2`),\n ),\n ).toBe(true);\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(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n false,\n );\n });\n});\n\ndescribe(\"isContactsOsVersionSupportedForSession\", () => {\n it(\"returns true when the model and the caller's OS version meet the minimums\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsOsVersionSupportedForSession(api, MIN_OS_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_S }),\n );\n expect(isContactsOsVersionSupportedForSession(api, MIN_OS_VERSION)).toBe(\n false,\n );\n });\n\n it(\"returns false when the caller's OS version is below the minimum\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsOsVersionSupportedForSession(api, BELOW_ANY_VERSION)).toBe(\n false,\n );\n });\n\n it(\"returns true for a release candidate of the minimum OS version\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsOsVersionSupportedForSession(api, `${MIN_OS_VERSION}-rc2`),\n ).toBe(true);\n });\n\n it(\"returns false for a release candidate below the minimum OS version\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsOsVersionSupportedForSession(api, `${BELOW_ANY_VERSION}-rc2`),\n ).toBe(false);\n });\n\n it(\"never reads the OS version from the device session state\", () => {\n // A session with no firmwareVersion at all: the guard must rely solely on\n // the caller-supplied version and still resolve support.\n const api = createInternalApi({\n sessionStateType: DeviceSessionStateType.Connected,\n deviceStatus: DeviceStatus.CONNECTED,\n deviceModelId: DeviceModelId.FLEX,\n });\n expect(isContactsOsVersionSupportedForSession(api, MIN_OS_VERSION)).toBe(\n true,\n );\n expect(isContactsOsVersionSupportedForSession(api, BELOW_ANY_VERSION)).toBe(\n false,\n );\n });\n});\n"],
5
- "mappings": "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",
6
- "names": ["DeviceModelId", "DeviceSessionStateType", "DeviceStatus", "ETHEREUM_APP_NAME", "resolveContactsVersionRequirements", "isContactsAppVersionSupportedForSession", "isContactsOsVersionSupportedForSession", "flexSupport", "requirement", "MIN_OS_VERSION", "MIN_APP_VERSION", "version", "BELOW_ANY_VERSION", "createReadyState", "osVersion", "modelId", "createInternalApi", "deviceState", "runningApp", "name", "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 SOLANA_APP_NAME,\n TRON_APP_NAME,\n} from \"@api/model/ContactsVersionRequirements\";\n\nimport {\n isContactsAppVersionSupportedForSession,\n isContactsOsVersionSupportedForSession,\n type RunningApp,\n} from \"./contactsVersionGuards\";\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(\"isContactsAppVersionSupportedForSession\", () => {\n it(\"returns true when the model and app version meet the minimums\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n true,\n );\n });\n\n it(\"returns true regardless of OS version, since app-owned operations are not gated by it\", () => {\n const api = createInternalApi(\n createReadyState({ osVersion: BELOW_ANY_VERSION }),\n );\n expect(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n true,\n );\n });\n\n it(\"returns false on an unsupported device model\", () => {\n const api = createInternalApi(\n createReadyState({ modelId: DeviceModelId.NANO_S }),\n );\n expect(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n false,\n );\n });\n\n it(\"returns false when the app version is below the minimum\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsAppVersionSupportedForSession(\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(\n isContactsAppVersionSupportedForSession(api, runningApp(\"Bitcoin\")),\n ).toBe(false);\n });\n\n it.each([TRON_APP_NAME, SOLANA_APP_NAME])(\n \"returns true for the %s app at its minimum version\",\n (appName) => {\n const version = flexSupport.minAppVersion[appName];\n if (version === undefined) throw new Error(`${appName} min version`);\n const api = createInternalApi(createReadyState());\n expect(\n isContactsAppVersionSupportedForSession(\n api,\n runningApp(appName, version),\n ),\n ).toBe(true);\n },\n );\n\n it(\"returns false for a Solana app below its minimum version\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsAppVersionSupportedForSession(\n api,\n runningApp(SOLANA_APP_NAME, \"1.16.0\"),\n ),\n ).toBe(false);\n });\n\n it(\"returns true for a prerelease build of the minimum app version\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsAppVersionSupportedForSession(\n api,\n runningApp(ETHEREUM_APP_NAME, `${MIN_APP_VERSION}-rc2`),\n ),\n ).toBe(true);\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(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n false,\n );\n });\n});\n\ndescribe(\"isContactsOsVersionSupportedForSession\", () => {\n it(\"returns true when the model and the caller's OS version meet the minimums\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsOsVersionSupportedForSession(api, MIN_OS_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_S }),\n );\n expect(isContactsOsVersionSupportedForSession(api, MIN_OS_VERSION)).toBe(\n false,\n );\n });\n\n it(\"returns false when the caller's OS version is below the minimum\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsOsVersionSupportedForSession(api, BELOW_ANY_VERSION)).toBe(\n false,\n );\n });\n\n it(\"returns true for a release candidate of the minimum OS version\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsOsVersionSupportedForSession(api, `${MIN_OS_VERSION}-rc2`),\n ).toBe(true);\n });\n\n it(\"returns false for a release candidate below the minimum OS version\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsOsVersionSupportedForSession(api, `${BELOW_ANY_VERSION}-rc2`),\n ).toBe(false);\n });\n\n it(\"never reads the OS version from the device session state\", () => {\n // A session with no firmwareVersion at all: the guard must rely solely on\n // the caller-supplied version and still resolve support.\n const api = createInternalApi({\n sessionStateType: DeviceSessionStateType.Connected,\n deviceStatus: DeviceStatus.CONNECTED,\n deviceModelId: DeviceModelId.FLEX,\n });\n expect(isContactsOsVersionSupportedForSession(api, MIN_OS_VERSION)).toBe(\n true,\n );\n expect(isContactsOsVersionSupportedForSession(api, BELOW_ANY_VERSION)).toBe(\n false,\n );\n });\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["DeviceModelId", "DeviceSessionStateType", "DeviceStatus", "ETHEREUM_APP_NAME", "resolveContactsVersionRequirements", "SOLANA_APP_NAME", "TRON_APP_NAME", "isContactsAppVersionSupportedForSession", "isContactsOsVersionSupportedForSession", "flexSupport", "requirement", "MIN_OS_VERSION", "MIN_APP_VERSION", "version", "BELOW_ANY_VERSION", "createReadyState", "osVersion", "modelId", "createInternalApi", "deviceState", "runningApp", "name", "api", "appName"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{BLOCKCHAIN_FAMILY_BY_NAME as a}from"../../../app-binder/model/contactsConstants";import{CONTACT_NAME_BUFFER_LENGTH as f,ContactsValidationError as i,ETH_ADDRESS_BYTES as n,GROUP_HANDLE_SIZE as l,HMAC_PROOF_LENGTH as o,SCOPE_BUFFER_LENGTH as c,validateByteLength as r,validateChainId as s,validatePrintableLabel as d}from"../../../app-binder/model/contactsValidation";const h=o;function I(e){try{d(e.contactName,{field:"contactName",bufferLength:f}),d(e.scope,{field:"scope",bufferLength:c});const t=e.blockchainFamily.toLowerCase();if(!(t in a))throw new i(`Unsupported blockchain family: ${e.blockchainFamily}`);if(t==="ethereum"){if(r(e.previousIdentifier,{field:"previousIdentifier",expectedBytes:n}),r(e.newIdentifier,{field:"newIdentifier",expectedBytes:n}),e.chainId===void 0)throw new i("chainId is required for the Ethereum blockchain family.")}else{if(e.previousIdentifier.length===0)throw new i("previousIdentifier must not be empty.");if(e.newIdentifier.length===0)throw new i("newIdentifier must not be empty.")}return e.chainId!==void 0&&s(e.chainId),r(e.groupHandle,{field:"groupHandle",expectedBytes:l}),r(e.hmacProof,{field:"hmacProof",expectedBytes:o}),r(e.hmacRest,{field:"hmacRest",expectedBytes:h}),null}catch(t){if(t instanceof i)return t;throw t}}export{I as validateEditExternalAddressIdentifierInput};
1
+ import{CONTACT_NAME_BUFFER_LENGTH as d,ContactsValidationError as n,GROUP_HANDLE_SIZE as l,HMAC_PROOF_LENGTH as a,SCOPE_BUFFER_LENGTH as o,validateByteLength as i,validateFamilyIdentifiers as f,validatePrintableLabel as r}from"../../../app-binder/model/contactsValidation";const c=a;function E(e){try{return r(e.contactName,{field:"contactName",bufferLength:d}),r(e.scope,{field:"scope",bufferLength:o}),f({blockchainFamily:e.blockchainFamily,identifiers:[{field:"previousIdentifier",value:e.previousIdentifier},{field:"newIdentifier",value:e.newIdentifier}],chainId:e.chainId}),i(e.groupHandle,{field:"groupHandle",expectedBytes:l}),i(e.hmacProof,{field:"hmacProof",expectedBytes:a}),i(e.hmacRest,{field:"hmacRest",expectedBytes:c}),null}catch(t){if(t instanceof n)return t;throw t}}export{E as validateEditExternalAddressIdentifierInput};
2
2
  //# sourceMappingURL=validateEditExternalAddressIdentifierInput.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/internal/app-binder/device-action/EditExternalAddressIdentifier/validateEditExternalAddressIdentifierInput.ts"],
4
- "sourcesContent": ["import { type EditExternalAddressIdentifierInput } from \"@api/model/EditExternalAddressIdentifier\";\nimport { BLOCKCHAIN_FAMILY_BY_NAME } from \"@internal/app-binder/model/contactsConstants\";\nimport {\n CONTACT_NAME_BUFFER_LENGTH,\n ContactsValidationError,\n ETH_ADDRESS_BYTES,\n GROUP_HANDLE_SIZE,\n HMAC_PROOF_LENGTH,\n SCOPE_BUFFER_LENGTH,\n validateByteLength,\n validateChainId,\n validatePrintableLabel,\n} from \"@internal/app-binder/model/contactsValidation\";\n\n// The address-level proof (`hmac_rest`) shares the 32-byte HMAC width.\nconst HMAC_REST_LENGTH = HMAC_PROOF_LENGTH;\n\n/**\n * Validate the caller input for Edit External Address Identifier. Returns the\n * first `ContactsValidationError`, or `null` when valid. Non-throwing so the\n * device action can surface it as a typed terminal error state.\n */\nexport function validateEditExternalAddressIdentifierInput(\n input: EditExternalAddressIdentifierInput,\n): ContactsValidationError | null {\n try {\n validatePrintableLabel(input.contactName, {\n field: \"contactName\",\n bufferLength: CONTACT_NAME_BUFFER_LENGTH,\n });\n validatePrintableLabel(input.scope, {\n field: \"scope\",\n bufferLength: SCOPE_BUFFER_LENGTH,\n });\n\n const family = input.blockchainFamily.toLowerCase();\n if (!(family in BLOCKCHAIN_FAMILY_BY_NAME)) {\n throw new ContactsValidationError(\n `Unsupported blockchain family: ${input.blockchainFamily}`,\n );\n }\n if (family === \"ethereum\") {\n validateByteLength(input.previousIdentifier, {\n field: \"previousIdentifier\",\n expectedBytes: ETH_ADDRESS_BYTES,\n });\n validateByteLength(input.newIdentifier, {\n field: \"newIdentifier\",\n expectedBytes: ETH_ADDRESS_BYTES,\n });\n // CHAIN_ID is mandatory for the Ethereum family (multiple networks share\n // the same address format).\n if (input.chainId === undefined) {\n throw new ContactsValidationError(\n \"chainId is required for the Ethereum blockchain family.\",\n );\n }\n } else {\n if (input.previousIdentifier.length === 0) {\n throw new ContactsValidationError(\n \"previousIdentifier must not be empty.\",\n );\n }\n if (input.newIdentifier.length === 0) {\n throw new ContactsValidationError(\"newIdentifier must not be empty.\");\n }\n }\n\n if (input.chainId !== undefined) {\n validateChainId(input.chainId);\n }\n\n validateByteLength(input.groupHandle, {\n field: \"groupHandle\",\n expectedBytes: GROUP_HANDLE_SIZE,\n });\n validateByteLength(input.hmacProof, {\n field: \"hmacProof\",\n expectedBytes: HMAC_PROOF_LENGTH,\n });\n validateByteLength(input.hmacRest, {\n field: \"hmacRest\",\n expectedBytes: HMAC_REST_LENGTH,\n });\n\n return null;\n } catch (error) {\n if (error instanceof ContactsValidationError) {\n return error;\n }\n throw error;\n }\n}\n"],
5
- "mappings": "AACA,OAAS,6BAAAA,MAAiC,+CAC1C,OACE,8BAAAC,EACA,2BAAAC,EACA,qBAAAC,EACA,qBAAAC,EACA,qBAAAC,EACA,uBAAAC,EACA,sBAAAC,EACA,mBAAAC,EACA,0BAAAC,MACK,gDAGP,MAAMC,EAAmBL,EAOlB,SAASM,EACdC,EACgC,CAChC,GAAI,CACFH,EAAuBG,EAAM,YAAa,CACxC,MAAO,cACP,aAAcX,CAChB,CAAC,EACDQ,EAAuBG,EAAM,MAAO,CAClC,MAAO,QACP,aAAcN,CAChB,CAAC,EAED,MAAMO,EAASD,EAAM,iBAAiB,YAAY,EAClD,GAAI,EAAEC,KAAUb,GACd,MAAM,IAAIE,EACR,kCAAkCU,EAAM,gBAAgB,EAC1D,EAEF,GAAIC,IAAW,YAWb,GAVAN,EAAmBK,EAAM,mBAAoB,CAC3C,MAAO,qBACP,cAAeT,CACjB,CAAC,EACDI,EAAmBK,EAAM,cAAe,CACtC,MAAO,gBACP,cAAeT,CACjB,CAAC,EAGGS,EAAM,UAAY,OACpB,MAAM,IAAIV,EACR,yDACF,MAEG,CACL,GAAIU,EAAM,mBAAmB,SAAW,EACtC,MAAM,IAAIV,EACR,uCACF,EAEF,GAAIU,EAAM,cAAc,SAAW,EACjC,MAAM,IAAIV,EAAwB,kCAAkC,CAExE,CAEA,OAAIU,EAAM,UAAY,QACpBJ,EAAgBI,EAAM,OAAO,EAG/BL,EAAmBK,EAAM,YAAa,CACpC,MAAO,cACP,cAAeR,CACjB,CAAC,EACDG,EAAmBK,EAAM,UAAW,CAClC,MAAO,YACP,cAAeP,CACjB,CAAC,EACDE,EAAmBK,EAAM,SAAU,CACjC,MAAO,WACP,cAAeF,CACjB,CAAC,EAEM,IACT,OAASI,EAAO,CACd,GAAIA,aAAiBZ,EACnB,OAAOY,EAET,MAAMA,CACR,CACF",
6
- "names": ["BLOCKCHAIN_FAMILY_BY_NAME", "CONTACT_NAME_BUFFER_LENGTH", "ContactsValidationError", "ETH_ADDRESS_BYTES", "GROUP_HANDLE_SIZE", "HMAC_PROOF_LENGTH", "SCOPE_BUFFER_LENGTH", "validateByteLength", "validateChainId", "validatePrintableLabel", "HMAC_REST_LENGTH", "validateEditExternalAddressIdentifierInput", "input", "family", "error"]
4
+ "sourcesContent": ["import { type EditExternalAddressIdentifierInput } from \"@api/model/EditExternalAddressIdentifier\";\nimport {\n CONTACT_NAME_BUFFER_LENGTH,\n ContactsValidationError,\n GROUP_HANDLE_SIZE,\n HMAC_PROOF_LENGTH,\n SCOPE_BUFFER_LENGTH,\n validateByteLength,\n validateFamilyIdentifiers,\n validatePrintableLabel,\n} from \"@internal/app-binder/model/contactsValidation\";\n\n// The address-level proof (`hmac_rest`) shares the 32-byte HMAC width.\nconst HMAC_REST_LENGTH = HMAC_PROOF_LENGTH;\n\n/**\n * Validate the caller input for Edit External Address Identifier. Returns the\n * first `ContactsValidationError`, or `null` when valid. Non-throwing so the\n * device action can surface it as a typed terminal error state.\n */\nexport function validateEditExternalAddressIdentifierInput(\n input: EditExternalAddressIdentifierInput,\n): ContactsValidationError | null {\n try {\n validatePrintableLabel(input.contactName, {\n field: \"contactName\",\n bufferLength: CONTACT_NAME_BUFFER_LENGTH,\n });\n validatePrintableLabel(input.scope, {\n field: \"scope\",\n bufferLength: SCOPE_BUFFER_LENGTH,\n });\n\n validateFamilyIdentifiers({\n blockchainFamily: input.blockchainFamily,\n identifiers: [\n { field: \"previousIdentifier\", value: input.previousIdentifier },\n { field: \"newIdentifier\", value: input.newIdentifier },\n ],\n chainId: input.chainId,\n });\n\n validateByteLength(input.groupHandle, {\n field: \"groupHandle\",\n expectedBytes: GROUP_HANDLE_SIZE,\n });\n validateByteLength(input.hmacProof, {\n field: \"hmacProof\",\n expectedBytes: HMAC_PROOF_LENGTH,\n });\n validateByteLength(input.hmacRest, {\n field: \"hmacRest\",\n expectedBytes: HMAC_REST_LENGTH,\n });\n\n return null;\n } catch (error) {\n if (error instanceof ContactsValidationError) {\n return error;\n }\n throw error;\n }\n}\n"],
5
+ "mappings": "AACA,OACE,8BAAAA,EACA,2BAAAC,EACA,qBAAAC,EACA,qBAAAC,EACA,uBAAAC,EACA,sBAAAC,EACA,6BAAAC,EACA,0BAAAC,MACK,gDAGP,MAAMC,EAAmBL,EAOlB,SAASM,EACdC,EACgC,CAChC,GAAI,CACF,OAAAH,EAAuBG,EAAM,YAAa,CACxC,MAAO,cACP,aAAcV,CAChB,CAAC,EACDO,EAAuBG,EAAM,MAAO,CAClC,MAAO,QACP,aAAcN,CAChB,CAAC,EAEDE,EAA0B,CACxB,iBAAkBI,EAAM,iBACxB,YAAa,CACX,CAAE,MAAO,qBAAsB,MAAOA,EAAM,kBAAmB,EAC/D,CAAE,MAAO,gBAAiB,MAAOA,EAAM,aAAc,CACvD,EACA,QAASA,EAAM,OACjB,CAAC,EAEDL,EAAmBK,EAAM,YAAa,CACpC,MAAO,cACP,cAAeR,CACjB,CAAC,EACDG,EAAmBK,EAAM,UAAW,CAClC,MAAO,YACP,cAAeP,CACjB,CAAC,EACDE,EAAmBK,EAAM,SAAU,CACjC,MAAO,WACP,cAAeF,CACjB,CAAC,EAEM,IACT,OAASG,EAAO,CACd,GAAIA,aAAiBV,EACnB,OAAOU,EAET,MAAMA,CACR,CACF",
6
+ "names": ["CONTACT_NAME_BUFFER_LENGTH", "ContactsValidationError", "GROUP_HANDLE_SIZE", "HMAC_PROOF_LENGTH", "SCOPE_BUFFER_LENGTH", "validateByteLength", "validateFamilyIdentifiers", "validatePrintableLabel", "HMAC_REST_LENGTH", "validateEditExternalAddressIdentifierInput", "input", "error"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{ContactsValidationError as n}from"../../../app-binder/model/contactsValidation";import{validateEditExternalAddressIdentifierInput as t}from"./validateEditExternalAddressIdentifierInput";const r={contactName:"Alice",scope:"Eth main",previousIdentifier:new Uint8Array(20).fill(17),newIdentifier:new Uint8Array(20).fill(34),blockchainFamily:"ethereum",chainId:1n,groupHandle:new Uint8Array(64).fill(204),hmacProof:new Uint8Array(32).fill(221),hmacRest:new Uint8Array(32).fill(170)};describe("validateEditExternalAddressIdentifierInput",()=>{it("returns null for a valid input",()=>{expect(t(r)).toBeNull()}),it("rejects an empty contactName",()=>{const e=t({...r,contactName:""});expect(e).toBeInstanceOf(n)}),it("rejects a non-printable scope",()=>{const e=t({...r,scope:"caf\xE9"});expect(e).toBeInstanceOf(n)}),it("rejects an unsupported blockchain family",()=>{const e=t({...r,blockchainFamily:"dogecoin"});expect(e).toBeInstanceOf(n)}),it("rejects a previousIdentifier of the wrong length for Ethereum",()=>{const e=t({...r,previousIdentifier:new Uint8Array(19).fill(17)});expect(e).toBeInstanceOf(n)}),it("rejects a newIdentifier of the wrong length for Ethereum",()=>{const e=t({...r,newIdentifier:new Uint8Array(21).fill(34)});expect(e).toBeInstanceOf(n)}),it("requires chainId for the Ethereum family",()=>{const e=t({...r,chainId:void 0});expect(e).toBeInstanceOf(n)}),it("rejects a groupHandle of the wrong length",()=>{const e=t({...r,groupHandle:new Uint8Array(63).fill(204)});expect(e).toBeInstanceOf(n)}),it("rejects an hmacProof of the wrong length",()=>{const e=t({...r,hmacProof:new Uint8Array(31).fill(221)});expect(e).toBeInstanceOf(n)}),it("rejects an hmacRest of the wrong length",()=>{const e=t({...r,hmacRest:new Uint8Array(33).fill(170)});expect(e).toBeInstanceOf(n)})});
1
+ import{ContactsValidationError as n}from"../../../app-binder/model/contactsValidation";import{validateEditExternalAddressIdentifierInput as t}from"./validateEditExternalAddressIdentifierInput";const r={contactName:"Alice",scope:"Eth main",previousIdentifier:new Uint8Array(20).fill(17),newIdentifier:new Uint8Array(20).fill(34),blockchainFamily:"ethereum",chainId:1n,groupHandle:new Uint8Array(64).fill(204),hmacProof:new Uint8Array(32).fill(221),hmacRest:new Uint8Array(32).fill(170)};describe("validateEditExternalAddressIdentifierInput",()=>{it("returns null for a valid input",()=>{expect(t(r)).toBeNull()}),it("rejects an empty contactName",()=>{const e=t({...r,contactName:""});expect(e).toBeInstanceOf(n)}),it("rejects a non-printable scope",()=>{const e=t({...r,scope:"caf\xE9"});expect(e).toBeInstanceOf(n)}),it("rejects an unsupported blockchain family",()=>{const e=t({...r,blockchainFamily:"dogecoin"});expect(e).toBeInstanceOf(n)}),it("rejects a previousIdentifier of the wrong length for Ethereum",()=>{const e=t({...r,previousIdentifier:new Uint8Array(19).fill(17)});expect(e).toBeInstanceOf(n)}),it("rejects a newIdentifier of the wrong length for Ethereum",()=>{const e=t({...r,newIdentifier:new Uint8Array(21).fill(34)});expect(e).toBeInstanceOf(n)}),it("requires chainId for the Ethereum family",()=>{const e=t({...r,chainId:void 0});expect(e).toBeInstanceOf(n)}),describe("Solana",()=>{const e={...r,previousIdentifier:new Uint8Array(32).fill(17),newIdentifier:new Uint8Array(32).fill(34),blockchainFamily:"solana",chainId:void 0};it("accepts 32-byte public keys without chainId",()=>{expect(t(e)).toBeNull()}),it.each(["previousIdentifier","newIdentifier"])("rejects a %s that is not 32 bytes",i=>{const a=t({...e,[i]:new Uint8Array(20)});expect(a).toBeInstanceOf(n),expect(a?.message).toContain(i)}),it("rejects a chainId",()=>{expect(t({...e,chainId:1n})).toBeInstanceOf(n)})}),it("rejects a groupHandle of the wrong length",()=>{const e=t({...r,groupHandle:new Uint8Array(63).fill(204)});expect(e).toBeInstanceOf(n)}),it("rejects an hmacProof of the wrong length",()=>{const e=t({...r,hmacProof:new Uint8Array(31).fill(221)});expect(e).toBeInstanceOf(n)}),it("rejects an hmacRest of the wrong length",()=>{const e=t({...r,hmacRest:new Uint8Array(33).fill(170)});expect(e).toBeInstanceOf(n)})});
2
2
  //# sourceMappingURL=validateEditExternalAddressIdentifierInput.test.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/internal/app-binder/device-action/EditExternalAddressIdentifier/validateEditExternalAddressIdentifierInput.test.ts"],
4
- "sourcesContent": ["import { type EditExternalAddressIdentifierInput } from \"@api/model/EditExternalAddressIdentifier\";\nimport { ContactsValidationError } from \"@internal/app-binder/model/contactsValidation\";\n\nimport { validateEditExternalAddressIdentifierInput } from \"./validateEditExternalAddressIdentifierInput\";\n\nconst VALID_INPUT: EditExternalAddressIdentifierInput = {\n contactName: \"Alice\",\n scope: \"Eth main\",\n previousIdentifier: new Uint8Array(20).fill(0x11),\n newIdentifier: new Uint8Array(20).fill(0x22),\n blockchainFamily: \"ethereum\",\n chainId: 1n,\n groupHandle: new Uint8Array(64).fill(0xcc),\n hmacProof: new Uint8Array(32).fill(0xdd),\n hmacRest: new Uint8Array(32).fill(0xaa),\n};\n\ndescribe(\"validateEditExternalAddressIdentifierInput\", () => {\n it(\"returns null for a valid input\", () => {\n expect(validateEditExternalAddressIdentifierInput(VALID_INPUT)).toBeNull();\n });\n\n it(\"rejects an empty contactName\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n contactName: \"\",\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects a non-printable scope\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n scope: \"caf\u00E9\", // 0xC3 0xA9 is non-ASCII\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an unsupported blockchain family\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n blockchainFamily: \"dogecoin\",\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects a previousIdentifier of the wrong length for Ethereum\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n previousIdentifier: new Uint8Array(19).fill(0x11),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects a newIdentifier of the wrong length for Ethereum\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n newIdentifier: new Uint8Array(21).fill(0x22),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"requires chainId for the Ethereum family\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n chainId: undefined,\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects a groupHandle of the wrong length\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n groupHandle: new Uint8Array(63).fill(0xcc),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an hmacProof of the wrong length\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n hmacProof: new Uint8Array(31).fill(0xdd),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an hmacRest of the wrong length\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n hmacRest: new Uint8Array(33).fill(0xaa),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n});\n"],
5
- "mappings": "AACA,OAAS,2BAAAA,MAA+B,gDAExC,OAAS,8CAAAC,MAAkD,+CAE3D,MAAMC,EAAkD,CACtD,YAAa,QACb,MAAO,WACP,mBAAoB,IAAI,WAAW,EAAE,EAAE,KAAK,EAAI,EAChD,cAAe,IAAI,WAAW,EAAE,EAAE,KAAK,EAAI,EAC3C,iBAAkB,WAClB,QAAS,GACT,YAAa,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,EACzC,UAAW,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,EACvC,SAAU,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,CACxC,EAEA,SAAS,6CAA8C,IAAM,CAC3D,GAAG,iCAAkC,IAAM,CACzC,OAAOD,EAA2CC,CAAW,CAAC,EAAE,SAAS,CAC3E,CAAC,EAED,GAAG,+BAAgC,IAAM,CACvC,MAAMC,EAAQF,EAA2C,CACvD,GAAGC,EACH,YAAa,EACf,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,gCAAiC,IAAM,CACxC,MAAMG,EAAQF,EAA2C,CACvD,GAAGC,EACH,MAAO,SACT,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,2CAA4C,IAAM,CACnD,MAAMG,EAAQF,EAA2C,CACvD,GAAGC,EACH,iBAAkB,UACpB,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,gEAAiE,IAAM,CACxE,MAAMG,EAAQF,EAA2C,CACvD,GAAGC,EACH,mBAAoB,IAAI,WAAW,EAAE,EAAE,KAAK,EAAI,CAClD,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,2DAA4D,IAAM,CACnE,MAAMG,EAAQF,EAA2C,CACvD,GAAGC,EACH,cAAe,IAAI,WAAW,EAAE,EAAE,KAAK,EAAI,CAC7C,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,2CAA4C,IAAM,CACnD,MAAMG,EAAQF,EAA2C,CACvD,GAAGC,EACH,QAAS,MACX,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,4CAA6C,IAAM,CACpD,MAAMG,EAAQF,EAA2C,CACvD,GAAGC,EACH,YAAa,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,CAC3C,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,2CAA4C,IAAM,CACnD,MAAMG,EAAQF,EAA2C,CACvD,GAAGC,EACH,UAAW,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,CACzC,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,0CAA2C,IAAM,CAClD,MAAMG,EAAQF,EAA2C,CACvD,GAAGC,EACH,SAAU,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,CACxC,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,CACH,CAAC",
6
- "names": ["ContactsValidationError", "validateEditExternalAddressIdentifierInput", "VALID_INPUT", "error"]
4
+ "sourcesContent": ["import { type EditExternalAddressIdentifierInput } from \"@api/model/EditExternalAddressIdentifier\";\nimport { ContactsValidationError } from \"@internal/app-binder/model/contactsValidation\";\n\nimport { validateEditExternalAddressIdentifierInput } from \"./validateEditExternalAddressIdentifierInput\";\n\nconst VALID_INPUT: EditExternalAddressIdentifierInput = {\n contactName: \"Alice\",\n scope: \"Eth main\",\n previousIdentifier: new Uint8Array(20).fill(0x11),\n newIdentifier: new Uint8Array(20).fill(0x22),\n blockchainFamily: \"ethereum\",\n chainId: 1n,\n groupHandle: new Uint8Array(64).fill(0xcc),\n hmacProof: new Uint8Array(32).fill(0xdd),\n hmacRest: new Uint8Array(32).fill(0xaa),\n};\n\ndescribe(\"validateEditExternalAddressIdentifierInput\", () => {\n it(\"returns null for a valid input\", () => {\n expect(validateEditExternalAddressIdentifierInput(VALID_INPUT)).toBeNull();\n });\n\n it(\"rejects an empty contactName\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n contactName: \"\",\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects a non-printable scope\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n scope: \"caf\u00E9\", // 0xC3 0xA9 is non-ASCII\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an unsupported blockchain family\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n blockchainFamily: \"dogecoin\",\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects a previousIdentifier of the wrong length for Ethereum\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n previousIdentifier: new Uint8Array(19).fill(0x11),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects a newIdentifier of the wrong length for Ethereum\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n newIdentifier: new Uint8Array(21).fill(0x22),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"requires chainId for the Ethereum family\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n chainId: undefined,\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n describe(\"Solana\", () => {\n const SOLANA_INPUT: EditExternalAddressIdentifierInput = {\n ...VALID_INPUT,\n previousIdentifier: new Uint8Array(32).fill(0x11),\n newIdentifier: new Uint8Array(32).fill(0x22),\n blockchainFamily: \"solana\",\n chainId: undefined,\n };\n\n it(\"accepts 32-byte public keys without chainId\", () => {\n expect(\n validateEditExternalAddressIdentifierInput(SOLANA_INPUT),\n ).toBeNull();\n });\n\n it.each([\"previousIdentifier\", \"newIdentifier\"] as const)(\n \"rejects a %s that is not 32 bytes\",\n (field) => {\n const error = validateEditExternalAddressIdentifierInput({\n ...SOLANA_INPUT,\n [field]: new Uint8Array(20),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n expect(error?.message).toContain(field);\n },\n );\n\n it(\"rejects a chainId\", () => {\n expect(\n validateEditExternalAddressIdentifierInput({\n ...SOLANA_INPUT,\n chainId: 1n,\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n });\n\n it(\"rejects a groupHandle of the wrong length\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n groupHandle: new Uint8Array(63).fill(0xcc),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an hmacProof of the wrong length\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n hmacProof: new Uint8Array(31).fill(0xdd),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an hmacRest of the wrong length\", () => {\n const error = validateEditExternalAddressIdentifierInput({\n ...VALID_INPUT,\n hmacRest: new Uint8Array(33).fill(0xaa),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["ContactsValidationError", "validateEditExternalAddressIdentifierInput", "VALID_INPUT", "error", "SOLANA_INPUT", "field"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{BLOCKCHAIN_FAMILY_BY_NAME as n}from"../../../app-binder/model/contactsConstants";import{CONTACT_NAME_BUFFER_LENGTH as c,ContactsValidationError as r,ETH_ADDRESS_BYTES as f,GROUP_HANDLE_SIZE as l,HMAC_PROOF_LENGTH as d,SCOPE_BUFFER_LENGTH as a,validateByteLength as i,validateChainId as h,validatePrintableLabel as o}from"../../../app-binder/model/contactsValidation";const s=d;function _(e){try{o(e.contactName,{field:"contactName",bufferLength:c}),o(e.previousScope,{field:"previousScope",bufferLength:a}),o(e.newScope,{field:"newScope",bufferLength:a});const t=e.blockchainFamily.toLowerCase();if(!(t in n))throw new r(`Unsupported blockchain family: ${e.blockchainFamily}`);if(t==="ethereum"){if(i(e.identifier,{field:"identifier",expectedBytes:f}),e.chainId===void 0)throw new r("chainId is required for the Ethereum blockchain family.")}else if(e.identifier.length===0)throw new r("identifier must not be empty.");return e.chainId!==void 0&&h(e.chainId),i(e.groupHandle,{field:"groupHandle",expectedBytes:l}),i(e.hmacProof,{field:"hmacProof",expectedBytes:d}),i(e.hmacRest,{field:"hmacRest",expectedBytes:s}),null}catch(t){if(t instanceof r)return t;throw t}}export{_ as validateEditExternalAddressScopeInput};
1
+ import{CONTACT_NAME_BUFFER_LENGTH as d,ContactsValidationError as c,GROUP_HANDLE_SIZE as l,HMAC_PROOF_LENGTH as o,SCOPE_BUFFER_LENGTH as i,validateByteLength as r,validateFamilyIdentifiers as n,validatePrintableLabel as a}from"../../../app-binder/model/contactsValidation";const f=o;function E(e){try{return a(e.contactName,{field:"contactName",bufferLength:d}),a(e.previousScope,{field:"previousScope",bufferLength:i}),a(e.newScope,{field:"newScope",bufferLength:i}),n({blockchainFamily:e.blockchainFamily,identifiers:[{field:"identifier",value:e.identifier}],chainId:e.chainId}),r(e.groupHandle,{field:"groupHandle",expectedBytes:l}),r(e.hmacProof,{field:"hmacProof",expectedBytes:o}),r(e.hmacRest,{field:"hmacRest",expectedBytes:f}),null}catch(t){if(t instanceof c)return t;throw t}}export{E as validateEditExternalAddressScopeInput};
2
2
  //# sourceMappingURL=validateEditExternalAddressScopeInput.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/internal/app-binder/device-action/EditExternalAddressScope/validateEditExternalAddressScopeInput.ts"],
4
- "sourcesContent": ["import { type EditExternalAddressScopeInput } from \"@api/model/EditExternalAddressScope\";\nimport { BLOCKCHAIN_FAMILY_BY_NAME } from \"@internal/app-binder/model/contactsConstants\";\nimport {\n CONTACT_NAME_BUFFER_LENGTH,\n ContactsValidationError,\n ETH_ADDRESS_BYTES,\n GROUP_HANDLE_SIZE,\n HMAC_PROOF_LENGTH,\n SCOPE_BUFFER_LENGTH,\n validateByteLength,\n validateChainId,\n validatePrintableLabel,\n} from \"@internal/app-binder/model/contactsValidation\";\n\n// The address-level proof (`hmac_rest`) shares the 32-byte HMAC width.\nconst HMAC_REST_LENGTH = HMAC_PROOF_LENGTH;\n\n/**\n * Validate the caller input for Edit External Address Scope. Returns the first\n * `ContactsValidationError`, or `null` when valid. Non-throwing so the device\n * action can surface it as a typed terminal error state.\n */\nexport function validateEditExternalAddressScopeInput(\n input: EditExternalAddressScopeInput,\n): ContactsValidationError | null {\n try {\n validatePrintableLabel(input.contactName, {\n field: \"contactName\",\n bufferLength: CONTACT_NAME_BUFFER_LENGTH,\n });\n validatePrintableLabel(input.previousScope, {\n field: \"previousScope\",\n bufferLength: SCOPE_BUFFER_LENGTH,\n });\n validatePrintableLabel(input.newScope, {\n field: \"newScope\",\n bufferLength: SCOPE_BUFFER_LENGTH,\n });\n\n const family = input.blockchainFamily.toLowerCase();\n if (!(family in BLOCKCHAIN_FAMILY_BY_NAME)) {\n throw new ContactsValidationError(\n `Unsupported blockchain family: ${input.blockchainFamily}`,\n );\n }\n if (family === \"ethereum\") {\n validateByteLength(input.identifier, {\n field: \"identifier\",\n expectedBytes: ETH_ADDRESS_BYTES,\n });\n // CHAIN_ID is mandatory for the Ethereum family (multiple networks share\n // the same address format).\n if (input.chainId === undefined) {\n throw new ContactsValidationError(\n \"chainId is required for the Ethereum blockchain family.\",\n );\n }\n } else {\n if (input.identifier.length === 0) {\n throw new ContactsValidationError(\"identifier must not be empty.\");\n }\n }\n\n if (input.chainId !== undefined) {\n validateChainId(input.chainId);\n }\n\n validateByteLength(input.groupHandle, {\n field: \"groupHandle\",\n expectedBytes: GROUP_HANDLE_SIZE,\n });\n validateByteLength(input.hmacProof, {\n field: \"hmacProof\",\n expectedBytes: HMAC_PROOF_LENGTH,\n });\n validateByteLength(input.hmacRest, {\n field: \"hmacRest\",\n expectedBytes: HMAC_REST_LENGTH,\n });\n\n return null;\n } catch (error) {\n if (error instanceof ContactsValidationError) {\n return error;\n }\n throw error;\n }\n}\n"],
5
- "mappings": "AACA,OAAS,6BAAAA,MAAiC,+CAC1C,OACE,8BAAAC,EACA,2BAAAC,EACA,qBAAAC,EACA,qBAAAC,EACA,qBAAAC,EACA,uBAAAC,EACA,sBAAAC,EACA,mBAAAC,EACA,0BAAAC,MACK,gDAGP,MAAMC,EAAmBL,EAOlB,SAASM,EACdC,EACgC,CAChC,GAAI,CACFH,EAAuBG,EAAM,YAAa,CACxC,MAAO,cACP,aAAcX,CAChB,CAAC,EACDQ,EAAuBG,EAAM,cAAe,CAC1C,MAAO,gBACP,aAAcN,CAChB,CAAC,EACDG,EAAuBG,EAAM,SAAU,CACrC,MAAO,WACP,aAAcN,CAChB,CAAC,EAED,MAAMO,EAASD,EAAM,iBAAiB,YAAY,EAClD,GAAI,EAAEC,KAAUb,GACd,MAAM,IAAIE,EACR,kCAAkCU,EAAM,gBAAgB,EAC1D,EAEF,GAAIC,IAAW,YAOb,GANAN,EAAmBK,EAAM,WAAY,CACnC,MAAO,aACP,cAAeT,CACjB,CAAC,EAGGS,EAAM,UAAY,OACpB,MAAM,IAAIV,EACR,yDACF,UAGEU,EAAM,WAAW,SAAW,EAC9B,MAAM,IAAIV,EAAwB,+BAA+B,EAIrE,OAAIU,EAAM,UAAY,QACpBJ,EAAgBI,EAAM,OAAO,EAG/BL,EAAmBK,EAAM,YAAa,CACpC,MAAO,cACP,cAAeR,CACjB,CAAC,EACDG,EAAmBK,EAAM,UAAW,CAClC,MAAO,YACP,cAAeP,CACjB,CAAC,EACDE,EAAmBK,EAAM,SAAU,CACjC,MAAO,WACP,cAAeF,CACjB,CAAC,EAEM,IACT,OAASI,EAAO,CACd,GAAIA,aAAiBZ,EACnB,OAAOY,EAET,MAAMA,CACR,CACF",
6
- "names": ["BLOCKCHAIN_FAMILY_BY_NAME", "CONTACT_NAME_BUFFER_LENGTH", "ContactsValidationError", "ETH_ADDRESS_BYTES", "GROUP_HANDLE_SIZE", "HMAC_PROOF_LENGTH", "SCOPE_BUFFER_LENGTH", "validateByteLength", "validateChainId", "validatePrintableLabel", "HMAC_REST_LENGTH", "validateEditExternalAddressScopeInput", "input", "family", "error"]
4
+ "sourcesContent": ["import { type EditExternalAddressScopeInput } from \"@api/model/EditExternalAddressScope\";\nimport {\n CONTACT_NAME_BUFFER_LENGTH,\n ContactsValidationError,\n GROUP_HANDLE_SIZE,\n HMAC_PROOF_LENGTH,\n SCOPE_BUFFER_LENGTH,\n validateByteLength,\n validateFamilyIdentifiers,\n validatePrintableLabel,\n} from \"@internal/app-binder/model/contactsValidation\";\n\n// The address-level proof (`hmac_rest`) shares the 32-byte HMAC width.\nconst HMAC_REST_LENGTH = HMAC_PROOF_LENGTH;\n\n/**\n * Validate the caller input for Edit External Address Scope. Returns the first\n * `ContactsValidationError`, or `null` when valid. Non-throwing so the device\n * action can surface it as a typed terminal error state.\n */\nexport function validateEditExternalAddressScopeInput(\n input: EditExternalAddressScopeInput,\n): ContactsValidationError | null {\n try {\n validatePrintableLabel(input.contactName, {\n field: \"contactName\",\n bufferLength: CONTACT_NAME_BUFFER_LENGTH,\n });\n validatePrintableLabel(input.previousScope, {\n field: \"previousScope\",\n bufferLength: SCOPE_BUFFER_LENGTH,\n });\n validatePrintableLabel(input.newScope, {\n field: \"newScope\",\n bufferLength: SCOPE_BUFFER_LENGTH,\n });\n\n validateFamilyIdentifiers({\n blockchainFamily: input.blockchainFamily,\n identifiers: [{ field: \"identifier\", value: input.identifier }],\n chainId: input.chainId,\n });\n\n validateByteLength(input.groupHandle, {\n field: \"groupHandle\",\n expectedBytes: GROUP_HANDLE_SIZE,\n });\n validateByteLength(input.hmacProof, {\n field: \"hmacProof\",\n expectedBytes: HMAC_PROOF_LENGTH,\n });\n validateByteLength(input.hmacRest, {\n field: \"hmacRest\",\n expectedBytes: HMAC_REST_LENGTH,\n });\n\n return null;\n } catch (error) {\n if (error instanceof ContactsValidationError) {\n return error;\n }\n throw error;\n }\n}\n"],
5
+ "mappings": "AACA,OACE,8BAAAA,EACA,2BAAAC,EACA,qBAAAC,EACA,qBAAAC,EACA,uBAAAC,EACA,sBAAAC,EACA,6BAAAC,EACA,0BAAAC,MACK,gDAGP,MAAMC,EAAmBL,EAOlB,SAASM,EACdC,EACgC,CAChC,GAAI,CACF,OAAAH,EAAuBG,EAAM,YAAa,CACxC,MAAO,cACP,aAAcV,CAChB,CAAC,EACDO,EAAuBG,EAAM,cAAe,CAC1C,MAAO,gBACP,aAAcN,CAChB,CAAC,EACDG,EAAuBG,EAAM,SAAU,CACrC,MAAO,WACP,aAAcN,CAChB,CAAC,EAEDE,EAA0B,CACxB,iBAAkBI,EAAM,iBACxB,YAAa,CAAC,CAAE,MAAO,aAAc,MAAOA,EAAM,UAAW,CAAC,EAC9D,QAASA,EAAM,OACjB,CAAC,EAEDL,EAAmBK,EAAM,YAAa,CACpC,MAAO,cACP,cAAeR,CACjB,CAAC,EACDG,EAAmBK,EAAM,UAAW,CAClC,MAAO,YACP,cAAeP,CACjB,CAAC,EACDE,EAAmBK,EAAM,SAAU,CACjC,MAAO,WACP,cAAeF,CACjB,CAAC,EAEM,IACT,OAASG,EAAO,CACd,GAAIA,aAAiBV,EACnB,OAAOU,EAET,MAAMA,CACR,CACF",
6
+ "names": ["CONTACT_NAME_BUFFER_LENGTH", "ContactsValidationError", "GROUP_HANDLE_SIZE", "HMAC_PROOF_LENGTH", "SCOPE_BUFFER_LENGTH", "validateByteLength", "validateFamilyIdentifiers", "validatePrintableLabel", "HMAC_REST_LENGTH", "validateEditExternalAddressScopeInput", "input", "error"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{ContactsValidationError as r}from"../../../app-binder/model/contactsValidation";import{validateEditExternalAddressScopeInput as t}from"./validateEditExternalAddressScopeInput";const n={contactName:"Alice",previousScope:"Eth main",newScope:"Eth cold",identifier:new Uint8Array(20).fill(17),blockchainFamily:"ethereum",chainId:1n,groupHandle:new Uint8Array(64).fill(204),hmacProof:new Uint8Array(32).fill(221),hmacRest:new Uint8Array(32).fill(170)};describe("validateEditExternalAddressScopeInput",()=>{it("returns null for a valid input",()=>{expect(t(n)).toBeNull()}),it("rejects an empty contactName",()=>{const e=t({...n,contactName:""});expect(e).toBeInstanceOf(r)}),it("rejects a non-printable previousScope",()=>{const e=t({...n,previousScope:"caf\xE9"});expect(e).toBeInstanceOf(r)}),it("rejects a non-printable newScope",()=>{const e=t({...n,newScope:"caf\xE9"});expect(e).toBeInstanceOf(r)}),it("rejects an unsupported blockchain family",()=>{const e=t({...n,blockchainFamily:"dogecoin"});expect(e).toBeInstanceOf(r)}),it("rejects an identifier of the wrong length for Ethereum",()=>{const e=t({...n,identifier:new Uint8Array(19).fill(17)});expect(e).toBeInstanceOf(r)}),it("requires chainId for the Ethereum family",()=>{const e=t({...n,chainId:void 0});expect(e).toBeInstanceOf(r)}),it("rejects a groupHandle of the wrong length",()=>{const e=t({...n,groupHandle:new Uint8Array(63).fill(204)});expect(e).toBeInstanceOf(r)}),it("rejects an hmacProof of the wrong length",()=>{const e=t({...n,hmacProof:new Uint8Array(31).fill(221)});expect(e).toBeInstanceOf(r)}),it("rejects an hmacRest of the wrong length",()=>{const e=t({...n,hmacRest:new Uint8Array(33).fill(170)});expect(e).toBeInstanceOf(r)})});
1
+ import{ContactsValidationError as n}from"../../../app-binder/model/contactsValidation";import{validateEditExternalAddressScopeInput as t}from"./validateEditExternalAddressScopeInput";const r={contactName:"Alice",previousScope:"Eth main",newScope:"Eth cold",identifier:new Uint8Array(20).fill(17),blockchainFamily:"ethereum",chainId:1n,groupHandle:new Uint8Array(64).fill(204),hmacProof:new Uint8Array(32).fill(221),hmacRest:new Uint8Array(32).fill(170)};describe("validateEditExternalAddressScopeInput",()=>{it("returns null for a valid input",()=>{expect(t(r)).toBeNull()}),it("rejects an empty contactName",()=>{const e=t({...r,contactName:""});expect(e).toBeInstanceOf(n)}),it("rejects a non-printable previousScope",()=>{const e=t({...r,previousScope:"caf\xE9"});expect(e).toBeInstanceOf(n)}),it("rejects a non-printable newScope",()=>{const e=t({...r,newScope:"caf\xE9"});expect(e).toBeInstanceOf(n)}),it("rejects an unsupported blockchain family",()=>{const e=t({...r,blockchainFamily:"dogecoin"});expect(e).toBeInstanceOf(n)}),it("rejects an identifier of the wrong length for Ethereum",()=>{const e=t({...r,identifier:new Uint8Array(19).fill(17)});expect(e).toBeInstanceOf(n)}),it("requires chainId for the Ethereum family",()=>{const e=t({...r,chainId:void 0});expect(e).toBeInstanceOf(n)}),describe("Solana",()=>{const e={...r,identifier:new Uint8Array(32).fill(17),blockchainFamily:"solana",chainId:void 0};it("accepts a 32-byte public key without chainId",()=>{expect(t(e)).toBeNull()}),it("rejects an identifier that is not 32 bytes",()=>{expect(t({...e,identifier:new Uint8Array(20)})).toBeInstanceOf(n)}),it("rejects a chainId",()=>{expect(t({...e,chainId:1n})).toBeInstanceOf(n)})}),it("rejects a groupHandle of the wrong length",()=>{const e=t({...r,groupHandle:new Uint8Array(63).fill(204)});expect(e).toBeInstanceOf(n)}),it("rejects an hmacProof of the wrong length",()=>{const e=t({...r,hmacProof:new Uint8Array(31).fill(221)});expect(e).toBeInstanceOf(n)}),it("rejects an hmacRest of the wrong length",()=>{const e=t({...r,hmacRest:new Uint8Array(33).fill(170)});expect(e).toBeInstanceOf(n)})});
2
2
  //# sourceMappingURL=validateEditExternalAddressScopeInput.test.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/internal/app-binder/device-action/EditExternalAddressScope/validateEditExternalAddressScopeInput.test.ts"],
4
- "sourcesContent": ["import { type EditExternalAddressScopeInput } from \"@api/model/EditExternalAddressScope\";\nimport { ContactsValidationError } from \"@internal/app-binder/model/contactsValidation\";\n\nimport { validateEditExternalAddressScopeInput } from \"./validateEditExternalAddressScopeInput\";\n\nconst VALID_INPUT: EditExternalAddressScopeInput = {\n contactName: \"Alice\",\n previousScope: \"Eth main\",\n newScope: \"Eth cold\",\n identifier: new Uint8Array(20).fill(0x11),\n blockchainFamily: \"ethereum\",\n chainId: 1n,\n groupHandle: new Uint8Array(64).fill(0xcc),\n hmacProof: new Uint8Array(32).fill(0xdd),\n hmacRest: new Uint8Array(32).fill(0xaa),\n};\n\ndescribe(\"validateEditExternalAddressScopeInput\", () => {\n it(\"returns null for a valid input\", () => {\n expect(validateEditExternalAddressScopeInput(VALID_INPUT)).toBeNull();\n });\n\n it(\"rejects an empty contactName\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n contactName: \"\",\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects a non-printable previousScope\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n previousScope: \"caf\u00E9\", // 0xC3 0xA9 is non-ASCII\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects a non-printable newScope\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n newScope: \"caf\u00E9\",\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an unsupported blockchain family\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n blockchainFamily: \"dogecoin\",\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an identifier of the wrong length for Ethereum\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n identifier: new Uint8Array(19).fill(0x11),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"requires chainId for the Ethereum family\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n chainId: undefined,\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects a groupHandle of the wrong length\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n groupHandle: new Uint8Array(63).fill(0xcc),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an hmacProof of the wrong length\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n hmacProof: new Uint8Array(31).fill(0xdd),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an hmacRest of the wrong length\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n hmacRest: new Uint8Array(33).fill(0xaa),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n});\n"],
5
- "mappings": "AACA,OAAS,2BAAAA,MAA+B,gDAExC,OAAS,yCAAAC,MAA6C,0CAEtD,MAAMC,EAA6C,CACjD,YAAa,QACb,cAAe,WACf,SAAU,WACV,WAAY,IAAI,WAAW,EAAE,EAAE,KAAK,EAAI,EACxC,iBAAkB,WAClB,QAAS,GACT,YAAa,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,EACzC,UAAW,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,EACvC,SAAU,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,CACxC,EAEA,SAAS,wCAAyC,IAAM,CACtD,GAAG,iCAAkC,IAAM,CACzC,OAAOD,EAAsCC,CAAW,CAAC,EAAE,SAAS,CACtE,CAAC,EAED,GAAG,+BAAgC,IAAM,CACvC,MAAMC,EAAQF,EAAsC,CAClD,GAAGC,EACH,YAAa,EACf,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,wCAAyC,IAAM,CAChD,MAAMG,EAAQF,EAAsC,CAClD,GAAGC,EACH,cAAe,SACjB,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,mCAAoC,IAAM,CAC3C,MAAMG,EAAQF,EAAsC,CAClD,GAAGC,EACH,SAAU,SACZ,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,2CAA4C,IAAM,CACnD,MAAMG,EAAQF,EAAsC,CAClD,GAAGC,EACH,iBAAkB,UACpB,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,yDAA0D,IAAM,CACjE,MAAMG,EAAQF,EAAsC,CAClD,GAAGC,EACH,WAAY,IAAI,WAAW,EAAE,EAAE,KAAK,EAAI,CAC1C,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,2CAA4C,IAAM,CACnD,MAAMG,EAAQF,EAAsC,CAClD,GAAGC,EACH,QAAS,MACX,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,4CAA6C,IAAM,CACpD,MAAMG,EAAQF,EAAsC,CAClD,GAAGC,EACH,YAAa,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,CAC3C,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,2CAA4C,IAAM,CACnD,MAAMG,EAAQF,EAAsC,CAClD,GAAGC,EACH,UAAW,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,CACzC,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,EAED,GAAG,0CAA2C,IAAM,CAClD,MAAMG,EAAQF,EAAsC,CAClD,GAAGC,EACH,SAAU,IAAI,WAAW,EAAE,EAAE,KAAK,GAAI,CACxC,CAAC,EACD,OAAOC,CAAK,EAAE,eAAeH,CAAuB,CACtD,CAAC,CACH,CAAC",
6
- "names": ["ContactsValidationError", "validateEditExternalAddressScopeInput", "VALID_INPUT", "error"]
4
+ "sourcesContent": ["import { type EditExternalAddressScopeInput } from \"@api/model/EditExternalAddressScope\";\nimport { ContactsValidationError } from \"@internal/app-binder/model/contactsValidation\";\n\nimport { validateEditExternalAddressScopeInput } from \"./validateEditExternalAddressScopeInput\";\n\nconst VALID_INPUT: EditExternalAddressScopeInput = {\n contactName: \"Alice\",\n previousScope: \"Eth main\",\n newScope: \"Eth cold\",\n identifier: new Uint8Array(20).fill(0x11),\n blockchainFamily: \"ethereum\",\n chainId: 1n,\n groupHandle: new Uint8Array(64).fill(0xcc),\n hmacProof: new Uint8Array(32).fill(0xdd),\n hmacRest: new Uint8Array(32).fill(0xaa),\n};\n\ndescribe(\"validateEditExternalAddressScopeInput\", () => {\n it(\"returns null for a valid input\", () => {\n expect(validateEditExternalAddressScopeInput(VALID_INPUT)).toBeNull();\n });\n\n it(\"rejects an empty contactName\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n contactName: \"\",\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects a non-printable previousScope\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n previousScope: \"caf\u00E9\", // 0xC3 0xA9 is non-ASCII\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects a non-printable newScope\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n newScope: \"caf\u00E9\",\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an unsupported blockchain family\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n blockchainFamily: \"dogecoin\",\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an identifier of the wrong length for Ethereum\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n identifier: new Uint8Array(19).fill(0x11),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"requires chainId for the Ethereum family\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n chainId: undefined,\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n describe(\"Solana\", () => {\n const SOLANA_INPUT: EditExternalAddressScopeInput = {\n ...VALID_INPUT,\n identifier: new Uint8Array(32).fill(0x11),\n blockchainFamily: \"solana\",\n chainId: undefined,\n };\n\n it(\"accepts a 32-byte public key without chainId\", () => {\n expect(validateEditExternalAddressScopeInput(SOLANA_INPUT)).toBeNull();\n });\n\n it(\"rejects an identifier that is not 32 bytes\", () => {\n expect(\n validateEditExternalAddressScopeInput({\n ...SOLANA_INPUT,\n identifier: new Uint8Array(20),\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects a chainId\", () => {\n expect(\n validateEditExternalAddressScopeInput({ ...SOLANA_INPUT, chainId: 1n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n });\n\n it(\"rejects a groupHandle of the wrong length\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n groupHandle: new Uint8Array(63).fill(0xcc),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an hmacProof of the wrong length\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n hmacProof: new Uint8Array(31).fill(0xdd),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"rejects an hmacRest of the wrong length\", () => {\n const error = validateEditExternalAddressScopeInput({\n ...VALID_INPUT,\n hmacRest: new Uint8Array(33).fill(0xaa),\n });\n expect(error).toBeInstanceOf(ContactsValidationError);\n });\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["ContactsValidationError", "validateEditExternalAddressScopeInput", "VALID_INPUT", "error", "SOLANA_INPUT"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{BLOCKCHAIN_FAMILY_BY_NAME as o}from"../../../app-binder/model/contactsConstants";import{CONTACT_NAME_BUFFER_LENGTH as a,ContactsValidationError as i,ETH_ADDRESS_BYTES as d,GROUP_HANDLE_SIZE as c,HMAC_PROOF_LENGTH as f,SCOPE_BUFFER_LENGTH as l,validateByteLength as r,validateChainId as s,validatePrintableLabel as n}from"../../../app-binder/model/contactsValidation";function E(e){try{n(e.contactName,{field:"contactName",bufferLength:a}),n(e.scope,{field:"scope",bufferLength:l});const t=e.blockchainFamily.toLowerCase();if(!(t in o))throw new i(`Unsupported blockchain family: ${e.blockchainFamily}`);if(t==="ethereum"){if(r(e.identifier,{field:"identifier",expectedBytes:d}),e.chainId===void 0)throw new i("chainId is required for the Ethereum blockchain family.")}else if(e.identifier.length===0)throw new i("identifier must not be empty.");return e.chainId!==void 0&&s(e.chainId),e.existingContactGroup&&(r(e.existingContactGroup.groupHandle,{field:"existingContactGroup.groupHandle",expectedBytes:c}),r(e.existingContactGroup.hmacProof,{field:"existingContactGroup.hmacProof",expectedBytes:f})),null}catch(t){if(t instanceof i)return t;throw t}}export{E as validateRegisterExternalAddressInput};
1
+ import{CONTACT_NAME_BUFFER_LENGTH as a,ContactsValidationError as n,GROUP_HANDLE_SIZE as o,HMAC_PROOF_LENGTH as l,SCOPE_BUFFER_LENGTH as c,validateByteLength as r,validateFamilyIdentifiers as d,validatePrintableLabel as i}from"../../../app-binder/model/contactsValidation";function f(e){try{return i(e.contactName,{field:"contactName",bufferLength:a}),i(e.scope,{field:"scope",bufferLength:c}),d({blockchainFamily:e.blockchainFamily,identifiers:[{field:"identifier",value:e.identifier}],chainId:e.chainId}),e.existingContactGroup&&(r(e.existingContactGroup.groupHandle,{field:"existingContactGroup.groupHandle",expectedBytes:o}),r(e.existingContactGroup.hmacProof,{field:"existingContactGroup.hmacProof",expectedBytes:l})),null}catch(t){if(t instanceof n)return t;throw t}}export{f as validateRegisterExternalAddressInput};
2
2
  //# sourceMappingURL=validateRegisterExternalAddressInput.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.ts"],
4
- "sourcesContent": ["import { type RegisterExternalAddressInput } from \"@api/model/RegisterExternalAddress\";\nimport { BLOCKCHAIN_FAMILY_BY_NAME } from \"@internal/app-binder/model/contactsConstants\";\nimport {\n CONTACT_NAME_BUFFER_LENGTH,\n ContactsValidationError,\n ETH_ADDRESS_BYTES,\n GROUP_HANDLE_SIZE,\n HMAC_PROOF_LENGTH,\n SCOPE_BUFFER_LENGTH,\n validateByteLength,\n validateChainId,\n validatePrintableLabel,\n} from \"@internal/app-binder/model/contactsValidation\";\n\n/**\n * Validate the caller input for Register External Address. Returns the first\n * `ContactsValidationError` found, or `null` when the input is valid.\n *\n * Non-throwing by design: the device action calls this so an invalid input is\n * surfaced as a typed terminal error state on the observable, keeping the\n * public `ContactsManager.registerExternalAddress` free of synchronous throws.\n */\nexport function validateRegisterExternalAddressInput(\n input: RegisterExternalAddressInput,\n): ContactsValidationError | null {\n try {\n validatePrintableLabel(input.contactName, {\n field: \"contactName\",\n bufferLength: CONTACT_NAME_BUFFER_LENGTH,\n });\n validatePrintableLabel(input.scope, {\n field: \"scope\",\n bufferLength: SCOPE_BUFFER_LENGTH,\n });\n\n const family = input.blockchainFamily.toLowerCase();\n if (!(family in BLOCKCHAIN_FAMILY_BY_NAME)) {\n throw new ContactsValidationError(\n `Unsupported blockchain family: ${input.blockchainFamily}`,\n );\n }\n if (family === \"ethereum\") {\n validateByteLength(input.identifier, {\n field: \"identifier\",\n expectedBytes: ETH_ADDRESS_BYTES,\n });\n // CHAIN_ID is mandatory for the Ethereum family (multiple networks share\n // the same address format).\n if (input.chainId === undefined) {\n throw new ContactsValidationError(\n \"chainId is required for the Ethereum blockchain family.\",\n );\n }\n } else if (input.identifier.length === 0) {\n throw new ContactsValidationError(\"identifier must not be empty.\");\n }\n\n if (input.chainId !== undefined) {\n validateChainId(input.chainId);\n }\n\n if (input.existingContactGroup) {\n validateByteLength(input.existingContactGroup.groupHandle, {\n field: \"existingContactGroup.groupHandle\",\n expectedBytes: GROUP_HANDLE_SIZE,\n });\n validateByteLength(input.existingContactGroup.hmacProof, {\n field: \"existingContactGroup.hmacProof\",\n expectedBytes: HMAC_PROOF_LENGTH,\n });\n }\n\n return null;\n } catch (error) {\n if (error instanceof ContactsValidationError) {\n return error;\n }\n throw error;\n }\n}\n"],
5
- "mappings": "AACA,OAAS,6BAAAA,MAAiC,+CAC1C,OACE,8BAAAC,EACA,2BAAAC,EACA,qBAAAC,EACA,qBAAAC,EACA,qBAAAC,EACA,uBAAAC,EACA,sBAAAC,EACA,mBAAAC,EACA,0BAAAC,MACK,gDAUA,SAASC,EACdC,EACgC,CAChC,GAAI,CACFF,EAAuBE,EAAM,YAAa,CACxC,MAAO,cACP,aAAcV,CAChB,CAAC,EACDQ,EAAuBE,EAAM,MAAO,CAClC,MAAO,QACP,aAAcL,CAChB,CAAC,EAED,MAAMM,EAASD,EAAM,iBAAiB,YAAY,EAClD,GAAI,EAAEC,KAAUZ,GACd,MAAM,IAAIE,EACR,kCAAkCS,EAAM,gBAAgB,EAC1D,EAEF,GAAIC,IAAW,YAOb,GANAL,EAAmBI,EAAM,WAAY,CACnC,MAAO,aACP,cAAeR,CACjB,CAAC,EAGGQ,EAAM,UAAY,OACpB,MAAM,IAAIT,EACR,yDACF,UAEOS,EAAM,WAAW,SAAW,EACrC,MAAM,IAAIT,EAAwB,+BAA+B,EAGnE,OAAIS,EAAM,UAAY,QACpBH,EAAgBG,EAAM,OAAO,EAG3BA,EAAM,uBACRJ,EAAmBI,EAAM,qBAAqB,YAAa,CACzD,MAAO,mCACP,cAAeP,CACjB,CAAC,EACDG,EAAmBI,EAAM,qBAAqB,UAAW,CACvD,MAAO,iCACP,cAAeN,CACjB,CAAC,GAGI,IACT,OAASQ,EAAO,CACd,GAAIA,aAAiBX,EACnB,OAAOW,EAET,MAAMA,CACR,CACF",
6
- "names": ["BLOCKCHAIN_FAMILY_BY_NAME", "CONTACT_NAME_BUFFER_LENGTH", "ContactsValidationError", "ETH_ADDRESS_BYTES", "GROUP_HANDLE_SIZE", "HMAC_PROOF_LENGTH", "SCOPE_BUFFER_LENGTH", "validateByteLength", "validateChainId", "validatePrintableLabel", "validateRegisterExternalAddressInput", "input", "family", "error"]
4
+ "sourcesContent": ["import { type RegisterExternalAddressInput } from \"@api/model/RegisterExternalAddress\";\nimport {\n CONTACT_NAME_BUFFER_LENGTH,\n ContactsValidationError,\n GROUP_HANDLE_SIZE,\n HMAC_PROOF_LENGTH,\n SCOPE_BUFFER_LENGTH,\n validateByteLength,\n validateFamilyIdentifiers,\n validatePrintableLabel,\n} from \"@internal/app-binder/model/contactsValidation\";\n\n/**\n * Validate the caller input for Register External Address. Returns the first\n * `ContactsValidationError` found, or `null` when the input is valid.\n *\n * Non-throwing by design: the device action calls this so an invalid input is\n * surfaced as a typed terminal error state on the observable, keeping the\n * public `ContactsManager.registerExternalAddress` free of synchronous throws.\n */\nexport function validateRegisterExternalAddressInput(\n input: RegisterExternalAddressInput,\n): ContactsValidationError | null {\n try {\n validatePrintableLabel(input.contactName, {\n field: \"contactName\",\n bufferLength: CONTACT_NAME_BUFFER_LENGTH,\n });\n validatePrintableLabel(input.scope, {\n field: \"scope\",\n bufferLength: SCOPE_BUFFER_LENGTH,\n });\n\n validateFamilyIdentifiers({\n blockchainFamily: input.blockchainFamily,\n identifiers: [{ field: \"identifier\", value: input.identifier }],\n chainId: input.chainId,\n });\n\n if (input.existingContactGroup) {\n validateByteLength(input.existingContactGroup.groupHandle, {\n field: \"existingContactGroup.groupHandle\",\n expectedBytes: GROUP_HANDLE_SIZE,\n });\n validateByteLength(input.existingContactGroup.hmacProof, {\n field: \"existingContactGroup.hmacProof\",\n expectedBytes: HMAC_PROOF_LENGTH,\n });\n }\n\n return null;\n } catch (error) {\n if (error instanceof ContactsValidationError) {\n return error;\n }\n throw error;\n }\n}\n"],
5
+ "mappings": "AACA,OACE,8BAAAA,EACA,2BAAAC,EACA,qBAAAC,EACA,qBAAAC,EACA,uBAAAC,EACA,sBAAAC,EACA,6BAAAC,EACA,0BAAAC,MACK,gDAUA,SAASC,EACdC,EACgC,CAChC,GAAI,CACF,OAAAF,EAAuBE,EAAM,YAAa,CACxC,MAAO,cACP,aAAcT,CAChB,CAAC,EACDO,EAAuBE,EAAM,MAAO,CAClC,MAAO,QACP,aAAcL,CAChB,CAAC,EAEDE,EAA0B,CACxB,iBAAkBG,EAAM,iBACxB,YAAa,CAAC,CAAE,MAAO,aAAc,MAAOA,EAAM,UAAW,CAAC,EAC9D,QAASA,EAAM,OACjB,CAAC,EAEGA,EAAM,uBACRJ,EAAmBI,EAAM,qBAAqB,YAAa,CACzD,MAAO,mCACP,cAAeP,CACjB,CAAC,EACDG,EAAmBI,EAAM,qBAAqB,UAAW,CACvD,MAAO,iCACP,cAAeN,CACjB,CAAC,GAGI,IACT,OAASO,EAAO,CACd,GAAIA,aAAiBT,EACnB,OAAOS,EAET,MAAMA,CACR,CACF",
6
+ "names": ["CONTACT_NAME_BUFFER_LENGTH", "ContactsValidationError", "GROUP_HANDLE_SIZE", "HMAC_PROOF_LENGTH", "SCOPE_BUFFER_LENGTH", "validateByteLength", "validateFamilyIdentifiers", "validatePrintableLabel", "validateRegisterExternalAddressInput", "input", "error"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{ContactsValidationError as n}from"../../../app-binder/model/contactsValidation";import{validateRegisterExternalAddressInput as e}from"./validateRegisterExternalAddressInput";const t={contactName:"Alice",scope:"Eth main",identifier:new Uint8Array(20).fill(17),blockchainFamily:"ethereum",chainId:1n};describe("validateRegisterExternalAddressInput",()=>{it("returns null for valid input",()=>{expect(e(t)).toBeNull()}),it("returns null for a valid existing-contact-group input",()=>{expect(e({...t,existingContactGroup:{groupHandle:new Uint8Array(64),hmacProof:new Uint8Array(32)}})).toBeNull()}),it("returns a ContactsValidationError for a non-printable contact name",()=>{expect(e({...t,contactName:"Am\xE9lie"})).toBeInstanceOf(n)}),it("returns an error for an Ethereum identifier of the wrong length",()=>{expect(e({...t,identifier:new Uint8Array(19)})).toBeInstanceOf(n)}),it("returns an error for an unsupported blockchain family",()=>{expect(e({...t,blockchainFamily:"dogecoin"})).toBeInstanceOf(n)}),it("returns an error when chainId is missing for the Ethereum family",()=>{expect(e({...t,chainId:void 0})).toBeInstanceOf(n)}),it("returns an error for a wrong-sized existing group handle",()=>{expect(e({...t,existingContactGroup:{groupHandle:new Uint8Array(32),hmacProof:new Uint8Array(32)}})).toBeInstanceOf(n)})});
1
+ import{ContactsValidationError as t}from"../../../app-binder/model/contactsValidation";import{validateRegisterExternalAddressInput as e}from"./validateRegisterExternalAddressInput";const n={contactName:"Alice",scope:"Eth main",identifier:new Uint8Array(20).fill(17),blockchainFamily:"ethereum",chainId:1n};describe("validateRegisterExternalAddressInput",()=>{it("returns null for valid input",()=>{expect(e(n)).toBeNull()}),it("returns null for a valid existing-contact-group input",()=>{expect(e({...n,existingContactGroup:{groupHandle:new Uint8Array(64),hmacProof:new Uint8Array(32)}})).toBeNull()}),it("returns a ContactsValidationError for a non-printable contact name",()=>{expect(e({...n,contactName:"Am\xE9lie"})).toBeInstanceOf(t)}),it("returns an error for an Ethereum identifier of the wrong length",()=>{expect(e({...n,identifier:new Uint8Array(19)})).toBeInstanceOf(t)}),it("returns an error for an unsupported blockchain family",()=>{expect(e({...n,blockchainFamily:"dogecoin"})).toBeInstanceOf(t)}),it("returns an error when chainId is missing for the Ethereum family",()=>{expect(e({...n,chainId:void 0})).toBeInstanceOf(t)}),describe("Solana",()=>{const r={...n,identifier:new Uint8Array(32).fill(17),blockchainFamily:"solana",chainId:void 0};it("returns null for a 32-byte public key without chainId",()=>{expect(e(r)).toBeNull()}),it.each([31,33])("returns an error for a %i-byte identifier",i=>{expect(e({...r,identifier:new Uint8Array(i)})).toBeInstanceOf(t)}),it("returns an error when a chainId is given",()=>{expect(e({...r,chainId:1n})).toBeInstanceOf(t)})}),it("returns an error for a wrong-sized existing group handle",()=>{expect(e({...n,existingContactGroup:{groupHandle:new Uint8Array(32),hmacProof:new Uint8Array(32)}})).toBeInstanceOf(t)})});
2
2
  //# sourceMappingURL=validateRegisterExternalAddressInput.test.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/internal/app-binder/device-action/RegisterExternalAddress/validateRegisterExternalAddressInput.test.ts"],
4
- "sourcesContent": ["import { type RegisterExternalAddressInput } from \"@api/model/RegisterExternalAddress\";\nimport { ContactsValidationError } from \"@internal/app-binder/model/contactsValidation\";\n\nimport { validateRegisterExternalAddressInput } from \"./validateRegisterExternalAddressInput\";\n\nconst VALID_INPUT: RegisterExternalAddressInput = {\n contactName: \"Alice\",\n scope: \"Eth main\",\n identifier: new Uint8Array(20).fill(0x11),\n blockchainFamily: \"ethereum\",\n chainId: 1n,\n};\n\ndescribe(\"validateRegisterExternalAddressInput\", () => {\n it(\"returns null for valid input\", () => {\n expect(validateRegisterExternalAddressInput(VALID_INPUT)).toBeNull();\n });\n\n it(\"returns null for a valid existing-contact-group input\", () => {\n expect(\n validateRegisterExternalAddressInput({\n ...VALID_INPUT,\n existingContactGroup: {\n groupHandle: new Uint8Array(64),\n hmacProof: new Uint8Array(32),\n },\n }),\n ).toBeNull();\n });\n\n it(\"returns a ContactsValidationError for a non-printable contact name\", () => {\n expect(\n validateRegisterExternalAddressInput({\n ...VALID_INPUT,\n contactName: \"Am\u00E9lie\",\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"returns an error for an Ethereum identifier of the wrong length\", () => {\n expect(\n validateRegisterExternalAddressInput({\n ...VALID_INPUT,\n identifier: new Uint8Array(19),\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"returns an error for an unsupported blockchain family\", () => {\n expect(\n validateRegisterExternalAddressInput({\n ...VALID_INPUT,\n blockchainFamily: \"dogecoin\",\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"returns an error when chainId is missing for the Ethereum family\", () => {\n expect(\n validateRegisterExternalAddressInput({\n ...VALID_INPUT,\n chainId: undefined,\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"returns an error for a wrong-sized existing group handle\", () => {\n expect(\n validateRegisterExternalAddressInput({\n ...VALID_INPUT,\n existingContactGroup: {\n groupHandle: new Uint8Array(32),\n hmacProof: new Uint8Array(32),\n },\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n});\n"],
5
- "mappings": "AACA,OAAS,2BAAAA,MAA+B,gDAExC,OAAS,wCAAAC,MAA4C,yCAErD,MAAMC,EAA4C,CAChD,YAAa,QACb,MAAO,WACP,WAAY,IAAI,WAAW,EAAE,EAAE,KAAK,EAAI,EACxC,iBAAkB,WAClB,QAAS,EACX,EAEA,SAAS,uCAAwC,IAAM,CACrD,GAAG,+BAAgC,IAAM,CACvC,OAAOD,EAAqCC,CAAW,CAAC,EAAE,SAAS,CACrE,CAAC,EAED,GAAG,wDAAyD,IAAM,CAChE,OACED,EAAqC,CACnC,GAAGC,EACH,qBAAsB,CACpB,YAAa,IAAI,WAAW,EAAE,EAC9B,UAAW,IAAI,WAAW,EAAE,CAC9B,CACF,CAAC,CACH,EAAE,SAAS,CACb,CAAC,EAED,GAAG,qEAAsE,IAAM,CAC7E,OACED,EAAqC,CACnC,GAAGC,EACH,YAAa,WACf,CAAC,CACH,EAAE,eAAeF,CAAuB,CAC1C,CAAC,EAED,GAAG,kEAAmE,IAAM,CAC1E,OACEC,EAAqC,CACnC,GAAGC,EACH,WAAY,IAAI,WAAW,EAAE,CAC/B,CAAC,CACH,EAAE,eAAeF,CAAuB,CAC1C,CAAC,EAED,GAAG,wDAAyD,IAAM,CAChE,OACEC,EAAqC,CACnC,GAAGC,EACH,iBAAkB,UACpB,CAAC,CACH,EAAE,eAAeF,CAAuB,CAC1C,CAAC,EAED,GAAG,mEAAoE,IAAM,CAC3E,OACEC,EAAqC,CACnC,GAAGC,EACH,QAAS,MACX,CAAC,CACH,EAAE,eAAeF,CAAuB,CAC1C,CAAC,EAED,GAAG,2DAA4D,IAAM,CACnE,OACEC,EAAqC,CACnC,GAAGC,EACH,qBAAsB,CACpB,YAAa,IAAI,WAAW,EAAE,EAC9B,UAAW,IAAI,WAAW,EAAE,CAC9B,CACF,CAAC,CACH,EAAE,eAAeF,CAAuB,CAC1C,CAAC,CACH,CAAC",
6
- "names": ["ContactsValidationError", "validateRegisterExternalAddressInput", "VALID_INPUT"]
4
+ "sourcesContent": ["import { type RegisterExternalAddressInput } from \"@api/model/RegisterExternalAddress\";\nimport { ContactsValidationError } from \"@internal/app-binder/model/contactsValidation\";\n\nimport { validateRegisterExternalAddressInput } from \"./validateRegisterExternalAddressInput\";\n\nconst VALID_INPUT: RegisterExternalAddressInput = {\n contactName: \"Alice\",\n scope: \"Eth main\",\n identifier: new Uint8Array(20).fill(0x11),\n blockchainFamily: \"ethereum\",\n chainId: 1n,\n};\n\ndescribe(\"validateRegisterExternalAddressInput\", () => {\n it(\"returns null for valid input\", () => {\n expect(validateRegisterExternalAddressInput(VALID_INPUT)).toBeNull();\n });\n\n it(\"returns null for a valid existing-contact-group input\", () => {\n expect(\n validateRegisterExternalAddressInput({\n ...VALID_INPUT,\n existingContactGroup: {\n groupHandle: new Uint8Array(64),\n hmacProof: new Uint8Array(32),\n },\n }),\n ).toBeNull();\n });\n\n it(\"returns a ContactsValidationError for a non-printable contact name\", () => {\n expect(\n validateRegisterExternalAddressInput({\n ...VALID_INPUT,\n contactName: \"Am\u00E9lie\",\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"returns an error for an Ethereum identifier of the wrong length\", () => {\n expect(\n validateRegisterExternalAddressInput({\n ...VALID_INPUT,\n identifier: new Uint8Array(19),\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"returns an error for an unsupported blockchain family\", () => {\n expect(\n validateRegisterExternalAddressInput({\n ...VALID_INPUT,\n blockchainFamily: \"dogecoin\",\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"returns an error when chainId is missing for the Ethereum family\", () => {\n expect(\n validateRegisterExternalAddressInput({\n ...VALID_INPUT,\n chainId: undefined,\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n describe(\"Solana\", () => {\n const SOLANA_INPUT: RegisterExternalAddressInput = {\n ...VALID_INPUT,\n identifier: new Uint8Array(32).fill(0x11),\n blockchainFamily: \"solana\",\n chainId: undefined,\n };\n\n it(\"returns null for a 32-byte public key without chainId\", () => {\n expect(validateRegisterExternalAddressInput(SOLANA_INPUT)).toBeNull();\n });\n\n it.each([31, 33])(\"returns an error for a %i-byte identifier\", (length) => {\n expect(\n validateRegisterExternalAddressInput({\n ...SOLANA_INPUT,\n identifier: new Uint8Array(length),\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n\n it(\"returns an error when a chainId is given\", () => {\n expect(\n validateRegisterExternalAddressInput({ ...SOLANA_INPUT, chainId: 1n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n });\n\n it(\"returns an error for a wrong-sized existing group handle\", () => {\n expect(\n validateRegisterExternalAddressInput({\n ...VALID_INPUT,\n existingContactGroup: {\n groupHandle: new Uint8Array(32),\n hmacProof: new Uint8Array(32),\n },\n }),\n ).toBeInstanceOf(ContactsValidationError);\n });\n});\n"],
5
+ "mappings": "AACA,OAAS,2BAAAA,MAA+B,gDAExC,OAAS,wCAAAC,MAA4C,yCAErD,MAAMC,EAA4C,CAChD,YAAa,QACb,MAAO,WACP,WAAY,IAAI,WAAW,EAAE,EAAE,KAAK,EAAI,EACxC,iBAAkB,WAClB,QAAS,EACX,EAEA,SAAS,uCAAwC,IAAM,CACrD,GAAG,+BAAgC,IAAM,CACvC,OAAOD,EAAqCC,CAAW,CAAC,EAAE,SAAS,CACrE,CAAC,EAED,GAAG,wDAAyD,IAAM,CAChE,OACED,EAAqC,CACnC,GAAGC,EACH,qBAAsB,CACpB,YAAa,IAAI,WAAW,EAAE,EAC9B,UAAW,IAAI,WAAW,EAAE,CAC9B,CACF,CAAC,CACH,EAAE,SAAS,CACb,CAAC,EAED,GAAG,qEAAsE,IAAM,CAC7E,OACED,EAAqC,CACnC,GAAGC,EACH,YAAa,WACf,CAAC,CACH,EAAE,eAAeF,CAAuB,CAC1C,CAAC,EAED,GAAG,kEAAmE,IAAM,CAC1E,OACEC,EAAqC,CACnC,GAAGC,EACH,WAAY,IAAI,WAAW,EAAE,CAC/B,CAAC,CACH,EAAE,eAAeF,CAAuB,CAC1C,CAAC,EAED,GAAG,wDAAyD,IAAM,CAChE,OACEC,EAAqC,CACnC,GAAGC,EACH,iBAAkB,UACpB,CAAC,CACH,EAAE,eAAeF,CAAuB,CAC1C,CAAC,EAED,GAAG,mEAAoE,IAAM,CAC3E,OACEC,EAAqC,CACnC,GAAGC,EACH,QAAS,MACX,CAAC,CACH,EAAE,eAAeF,CAAuB,CAC1C,CAAC,EAED,SAAS,SAAU,IAAM,CACvB,MAAMG,EAA6C,CACjD,GAAGD,EACH,WAAY,IAAI,WAAW,EAAE,EAAE,KAAK,EAAI,EACxC,iBAAkB,SAClB,QAAS,MACX,EAEA,GAAG,wDAAyD,IAAM,CAChE,OAAOD,EAAqCE,CAAY,CAAC,EAAE,SAAS,CACtE,CAAC,EAED,GAAG,KAAK,CAAC,GAAI,EAAE,CAAC,EAAE,4CAA8CC,GAAW,CACzE,OACEH,EAAqC,CACnC,GAAGE,EACH,WAAY,IAAI,WAAWC,CAAM,CACnC,CAAC,CACH,EAAE,eAAeJ,CAAuB,CAC1C,CAAC,EAED,GAAG,2CAA4C,IAAM,CACnD,OACEC,EAAqC,CAAE,GAAGE,EAAc,QAAS,EAAG,CAAC,CACvE,EAAE,eAAeH,CAAuB,CAC1C,CAAC,CACH,CAAC,EAED,GAAG,2DAA4D,IAAM,CACnE,OACEC,EAAqC,CACnC,GAAGC,EACH,qBAAsB,CACpB,YAAa,IAAI,WAAW,EAAE,EAC9B,UAAW,IAAI,WAAW,EAAE,CAC9B,CACF,CAAC,CACH,EAAE,eAAeF,CAAuB,CAC1C,CAAC,CACH,CAAC",
6
+ "names": ["ContactsValidationError", "validateRegisterExternalAddressInput", "VALID_INPUT", "SOLANA_INPUT", "length"]
7
7
  }
@@ -1,2 +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};
1
+ import{BLOCKCHAIN_FAMILY_BY_NAME as m}from"../../app-binder/model/contactsConstants";const E=33,I=33,N=64,S=32,h=10,y=20,p=32;class n extends Error{_tag="ContactsValidationError";originalError;constructor(i){super(i),this.name="ContactsValidationError"}}const w=32,b=126,u=new TextEncoder;function _(e,i){const{field:r,bufferLength:t}=i;if(!e)throw new n(`${r} must not be empty (firmware rejects empty strings).`);const o=u.encode(e),a=t-1;if(o.length>a)throw new n(`${r} ${JSON.stringify(e)} is too long: ${o.length} bytes encoded (max ${a}). The device buffer is ${t} bytes including the trailing NUL.`);const s=[];for(let f=0;f<o.length;f++){const c=o[f];(c<w||c>b)&&s.push(f)}if(s.length>0){const c=Array.from(new Set(s.map(d=>o[d]))).sort((d,l)=>d-l).map(d=>`0x${d.toString(16).padStart(2,"0")}`).join(", ");throw new n(`${r} ${JSON.stringify(e)} contains ${s.length} non-ASCII byte(s) at position(s) [${s.join(", ")}]: ${c}. The device buffer is byte-level isprint() \u2014 only 0x20-0x7E are accepted, no UTF-8 / accents / emoji.`)}}function g(e,i){const{field:r,expectedBytes:t}=i;if(e.length!==t)throw new n(`${r} is ${e.length} bytes, expected ${t}.`)}function B(e){if(!e)throw new n(`derivationPath must not be empty (e.g. "m/44'/60'/0'/0/0").`);const i=e.split("/"),r=i[0]==="m"||i[0]==="M"?i.slice(1):i;if(r.length===0)throw new n(`derivationPath ${JSON.stringify(e)} has no segments past 'm'.`);if(r.length>h)throw new n(`derivationPath ${JSON.stringify(e)} has ${r.length} segments (max ${h}). Firmware rejects deeper paths.`);for(const t of r){const o=t.endsWith("'")?t.slice(0,-1):t;if(!o||!/^\d+$/.test(o))throw new n(`derivationPath ${JSON.stringify(e)} segment ${JSON.stringify(t)} is not numeric. Use plain integers, optionally suffixed with ' for hardened.`);const a=Number(o);if(!Number.isSafeInteger(a)||a>=2147483648)throw new n(`derivationPath ${JSON.stringify(e)} segment ${JSON.stringify(t)} >= 2^31. Use the ' suffix for hardening rather than overflowing.`)}}function x(e){const i=typeof e=="number";if(!i&&!(typeof e=="bigint"))throw new n(`chainId must be number or bigint, got ${typeof e}.`);if(i&&!Number.isInteger(e))throw new n(`chainId must be an integer, got ${e}.`);const t=typeof e=="bigint"?e:BigInt(e);if(t<=0n)throw new n(`chainId must be positive, got ${e}.`);const o=(1n<<64n)-1n;if(t>o)throw new n(`chainId ${e} exceeds uint64 range (max ${o}).`)}function A(e){const{blockchainFamily:i,identifiers:r,chainId:t}=e,o=i.toLowerCase(),a=m[o];if(a===void 0)throw new n(`Unsupported blockchain family: ${i}`);if(o==="ethereum"){for(const{field:s,value:f}of r)g(f,{field:s,expectedBytes:y});if(t===void 0)throw new n("chainId is required for the Ethereum blockchain family.")}else if(o==="solana"){for(const{field:s,value:f}of r)g(f,{field:s,expectedBytes:p});if(t!==void 0)throw new n("chainId is not allowed for the Solana blockchain family.")}else for(const{field:s,value:f}of r)if(f.length===0)throw new n(`${s} must not be empty.`);return t!==void 0&&x(t),a}export{E as CONTACT_NAME_BUFFER_LENGTH,n as ContactsValidationError,y as ETH_ADDRESS_BYTES,N as GROUP_HANDLE_SIZE,S as HMAC_PROOF_LENGTH,h as MAX_BIP32_DEPTH,I as SCOPE_BUFFER_LENGTH,p as SOLANA_ADDRESS_BYTES,g as validateByteLength,x as validateChainId,B as validateDerivationPath,A as validateFamilyIdentifiers,_ as validatePrintableLabel};
2
2
  //# sourceMappingURL=contactsValidation.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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"]
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\nimport { BLOCKCHAIN_FAMILY_BY_NAME } from \"@internal/app-binder/model/contactsConstants\";\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\nexport const SOLANA_ADDRESS_BYTES = 32; // ed25519 public key, 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\n// ---- blockchain family validation ------------------------------------------\n\n/** An address identifier to check, named after its input field. */\ntype IdentifierField = {\n readonly field: string;\n readonly value: Uint8Array;\n};\n\n/**\n * Check a blockchain family, its address identifiers and its chain id:\n * - Ethereum: 20-byte addresses, and CHAIN_ID is mandatory (multiple networks\n * share the same address format).\n * - Solana: 32-byte public keys, and no CHAIN_ID (the firmware specification\n * only defines one for Ethereum).\n * - Any other known family: non-empty identifiers.\n *\n * A chain id, when present, is range-checked. Returns the family byte encoded\n * in the `BLOCKCHAIN_FAMILY` TLV.\n */\nexport function validateFamilyIdentifiers(options: {\n blockchainFamily: string;\n identifiers: readonly IdentifierField[];\n chainId?: bigint;\n}): number {\n const { blockchainFamily, identifiers, chainId } = options;\n const family = blockchainFamily.toLowerCase();\n const familyByte = BLOCKCHAIN_FAMILY_BY_NAME[family];\n if (familyByte === undefined) {\n throw new ContactsValidationError(\n `Unsupported blockchain family: ${blockchainFamily}`,\n );\n }\n\n if (family === \"ethereum\") {\n for (const { field, value } of identifiers) {\n validateByteLength(value, { field, expectedBytes: ETH_ADDRESS_BYTES });\n }\n if (chainId === undefined) {\n throw new ContactsValidationError(\n \"chainId is required for the Ethereum blockchain family.\",\n );\n }\n } else if (family === \"solana\") {\n for (const { field, value } of identifiers) {\n validateByteLength(value, { field, expectedBytes: SOLANA_ADDRESS_BYTES });\n }\n if (chainId !== undefined) {\n throw new ContactsValidationError(\n \"chainId is not allowed for the Solana blockchain family.\",\n );\n }\n } else {\n for (const { field, value } of identifiers) {\n if (value.length === 0) {\n throw new ContactsValidationError(`${field} must not be empty.`);\n }\n }\n }\n\n if (chainId !== undefined) {\n validateChainId(chainId);\n }\n\n return familyByte;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["BLOCKCHAIN_FAMILY_BY_NAME", "CONTACT_NAME_BUFFER_LENGTH", "SCOPE_BUFFER_LENGTH", "GROUP_HANDLE_SIZE", "HMAC_PROOF_LENGTH", "MAX_BIP32_DEPTH", "ETH_ADDRESS_BYTES", "SOLANA_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", "validateFamilyIdentifiers", "blockchainFamily", "identifiers", "chainId", "family", "familyByte"]
7
7
  }
@@ -1,2 +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)})})});
1
+ import{CONTACT_NAME_BUFFER_LENGTH as o,ContactsValidationError as i,validateByteLength as c,validateChainId as n,validateDerivationPath as r,validateFamilyIdentifiers as t,validatePrintableLabel as a}from"./contactsValidation";const d="44'/60'/0'/0/0";describe("contactsValidation",()=>{describe("validatePrintableLabel",()=>{const e={field:"name",bufferLength:o};it("accepts a printable ASCII label within the buffer",()=>{expect(()=>a("Alice",e)).not.toThrow()}),it("rejects an empty label",()=>{expect(()=>a("",e)).toThrow(i)}),it("rejects a label exceeding bufferLength - 1 bytes",()=>{expect(()=>a("x".repeat(o),e)).toThrow(i)}),it("rejects non-ASCII / accented characters (byte-level isprint)",()=>{expect(()=>a("Am\xE9lie",e)).toThrow(i)})}),describe("validateByteLength",()=>{it("accepts an exact-length buffer",()=>{expect(()=>c(new Uint8Array(20),{field:"identifier",expectedBytes:20})).not.toThrow()}),it("rejects a wrong-length buffer",()=>{expect(()=>c(new Uint8Array(19),{field:"identifier",expectedBytes:20})).toThrow(i)})}),describe("validateChainId",()=>{it("accepts a positive integer and bigint",()=>{expect(()=>n(1)).not.toThrow(),expect(()=>n(56n)).not.toThrow()}),it("rejects zero, negative, and non-integer",()=>{expect(()=>n(0)).toThrow(i),expect(()=>n(-1)).toThrow(i),expect(()=>n(1.5)).toThrow(i)})}),describe("validateDerivationPath",()=>{it("accepts a representative BIP32 path",()=>{expect(()=>r(d)).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(i)})}),describe("validateFamilyIdentifiers",()=>{const e=l=>[{field:"identifier",value:new Uint8Array(l).fill(17)}];it("returns the BLOCKCHAIN_FAMILY byte, case-insensitively",()=>{expect(t({blockchainFamily:"Ethereum",identifiers:e(20),chainId:1n})).toBe(1),expect(t({blockchainFamily:"solana",identifiers:e(32)})).toBe(2),expect(t({blockchainFamily:"tron",identifiers:e(21)})).toBe(6)}),it("rejects an unknown family",()=>{expect(()=>t({blockchainFamily:"dogecoin",identifiers:e(20)})).toThrow("Unsupported blockchain family: dogecoin")}),it("requires 20-byte identifiers and a chainId for Ethereum",()=>{expect(()=>t({blockchainFamily:"ethereum",identifiers:e(32),chainId:1n})).toThrow("identifier is 32 bytes, expected 20."),expect(()=>t({blockchainFamily:"ethereum",identifiers:e(20)})).toThrow("chainId is required for the Ethereum blockchain family.")}),it("requires 32-byte identifiers and no chainId for Solana",()=>{expect(()=>t({blockchainFamily:"solana",identifiers:e(20)})).toThrow("identifier is 20 bytes, expected 32."),expect(()=>t({blockchainFamily:"solana",identifiers:e(32),chainId:101n})).toThrow("chainId is not allowed for the Solana blockchain family.")}),it("only requires non-empty identifiers for other families",()=>{expect(()=>t({blockchainFamily:"tron",identifiers:e(0)})).toThrow("identifier must not be empty.")}),it("range-checks a chainId",()=>{expect(()=>t({blockchainFamily:"ethereum",identifiers:e(20),chainId:0n})).toThrow(i)})})});
2
2
  //# sourceMappingURL=contactsValidation.test.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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"]
4
+ "sourcesContent": ["import {\n CONTACT_NAME_BUFFER_LENGTH,\n ContactsValidationError,\n validateByteLength,\n validateChainId,\n validateDerivationPath,\n validateFamilyIdentifiers,\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 describe(\"validateFamilyIdentifiers\", () => {\n const identifier = (length: number) => [\n { field: \"identifier\", value: new Uint8Array(length).fill(0x11) },\n ];\n\n it(\"returns the BLOCKCHAIN_FAMILY byte, case-insensitively\", () => {\n expect(\n validateFamilyIdentifiers({\n blockchainFamily: \"Ethereum\",\n identifiers: identifier(20),\n chainId: 1n,\n }),\n ).toBe(0x01);\n expect(\n validateFamilyIdentifiers({\n blockchainFamily: \"solana\",\n identifiers: identifier(32),\n }),\n ).toBe(0x02);\n expect(\n validateFamilyIdentifiers({\n blockchainFamily: \"tron\",\n identifiers: identifier(21),\n }),\n ).toBe(0x06);\n });\n\n it(\"rejects an unknown family\", () => {\n expect(() =>\n validateFamilyIdentifiers({\n blockchainFamily: \"dogecoin\",\n identifiers: identifier(20),\n }),\n ).toThrow(\"Unsupported blockchain family: dogecoin\");\n });\n\n it(\"requires 20-byte identifiers and a chainId for Ethereum\", () => {\n expect(() =>\n validateFamilyIdentifiers({\n blockchainFamily: \"ethereum\",\n identifiers: identifier(32),\n chainId: 1n,\n }),\n ).toThrow(\"identifier is 32 bytes, expected 20.\");\n expect(() =>\n validateFamilyIdentifiers({\n blockchainFamily: \"ethereum\",\n identifiers: identifier(20),\n }),\n ).toThrow(\"chainId is required for the Ethereum blockchain family.\");\n });\n\n it(\"requires 32-byte identifiers and no chainId for Solana\", () => {\n expect(() =>\n validateFamilyIdentifiers({\n blockchainFamily: \"solana\",\n identifiers: identifier(20),\n }),\n ).toThrow(\"identifier is 20 bytes, expected 32.\");\n expect(() =>\n validateFamilyIdentifiers({\n blockchainFamily: \"solana\",\n identifiers: identifier(32),\n chainId: 101n,\n }),\n ).toThrow(\"chainId is not allowed for the Solana blockchain family.\");\n });\n\n it(\"only requires non-empty identifiers for other families\", () => {\n expect(() =>\n validateFamilyIdentifiers({\n blockchainFamily: \"tron\",\n identifiers: identifier(0),\n }),\n ).toThrow(\"identifier must not be empty.\");\n });\n\n it(\"range-checks a chainId\", () => {\n expect(() =>\n validateFamilyIdentifiers({\n blockchainFamily: \"ethereum\",\n identifiers: identifier(20),\n chainId: 0n,\n }),\n ).toThrow(ContactsValidationError);\n });\n });\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["CONTACT_NAME_BUFFER_LENGTH", "ContactsValidationError", "validateByteLength", "validateChainId", "validateDerivationPath", "validateFamilyIdentifiers", "validatePrintableLabel", "SAMPLE_DERIVATION_PATH", "opts", "identifier", "length"]
7
7
  }