@ledgerhq/device-contacts-kit 0.0.0-develop-20260813001639 → 0.0.0-develop-20260815000734

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 (41) hide show
  1. package/README.md +32 -0
  2. package/lib/cjs/api/index.js +1 -1
  3. package/lib/cjs/api/index.js.map +3 -3
  4. package/lib/cjs/api/model/ContactsVersionRequirements.js +2 -0
  5. package/lib/cjs/api/model/ContactsVersionRequirements.js.map +7 -0
  6. package/lib/cjs/api/model/ContactsVersionRequirements.test.js +2 -0
  7. package/lib/cjs/api/model/ContactsVersionRequirements.test.js.map +7 -0
  8. package/lib/cjs/internal/app-binder/ContactsApplicationResolver.js +2 -0
  9. package/lib/cjs/internal/app-binder/ContactsApplicationResolver.js.map +7 -0
  10. package/lib/cjs/internal/app-binder/isContactsSupportedForSession.js +2 -0
  11. package/lib/cjs/internal/app-binder/isContactsSupportedForSession.js.map +7 -0
  12. package/lib/cjs/internal/app-binder/isContactsSupportedForSession.test.js +2 -0
  13. package/lib/cjs/internal/app-binder/isContactsSupportedForSession.test.js.map +7 -0
  14. package/lib/cjs/package.json +3 -1
  15. package/lib/esm/api/index.js +1 -1
  16. package/lib/esm/api/index.js.map +3 -3
  17. package/lib/esm/api/model/ContactsVersionRequirements.js +2 -0
  18. package/lib/esm/api/model/ContactsVersionRequirements.js.map +7 -0
  19. package/lib/esm/api/model/ContactsVersionRequirements.test.js +2 -0
  20. package/lib/esm/api/model/ContactsVersionRequirements.test.js.map +7 -0
  21. package/lib/esm/internal/app-binder/ContactsApplicationResolver.js +2 -0
  22. package/lib/esm/internal/app-binder/ContactsApplicationResolver.js.map +7 -0
  23. package/lib/esm/internal/app-binder/isContactsSupportedForSession.js +2 -0
  24. package/lib/esm/internal/app-binder/isContactsSupportedForSession.js.map +7 -0
  25. package/lib/esm/internal/app-binder/isContactsSupportedForSession.test.js +2 -0
  26. package/lib/esm/internal/app-binder/isContactsSupportedForSession.test.js.map +7 -0
  27. package/lib/esm/package.json +3 -1
  28. package/lib/types/api/index.d.ts +1 -0
  29. package/lib/types/api/index.d.ts.map +1 -1
  30. package/lib/types/api/model/ContactsVersionRequirements.d.ts +70 -0
  31. package/lib/types/api/model/ContactsVersionRequirements.d.ts.map +1 -0
  32. package/lib/types/api/model/ContactsVersionRequirements.test.d.ts +2 -0
  33. package/lib/types/api/model/ContactsVersionRequirements.test.d.ts.map +1 -0
  34. package/lib/types/internal/app-binder/ContactsApplicationResolver.d.ts +12 -0
  35. package/lib/types/internal/app-binder/ContactsApplicationResolver.d.ts.map +1 -0
  36. package/lib/types/internal/app-binder/isContactsSupportedForSession.d.ts +17 -0
  37. package/lib/types/internal/app-binder/isContactsSupportedForSession.d.ts.map +1 -0
  38. package/lib/types/internal/app-binder/isContactsSupportedForSession.test.d.ts +2 -0
  39. package/lib/types/internal/app-binder/isContactsSupportedForSession.test.d.ts.map +1 -0
  40. package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
  41. package/package.json +8 -6
package/README.md CHANGED
@@ -29,3 +29,35 @@ const contactsManager = new ContactsManagerBuilder({
29
29
 
30
30
  Concrete Contacts operations (Register Identity, Edit Contact Name, Edit Identifier, Edit Scope,
31
31
  Register Ledger Account, Edit Ledger Account) are added by their dedicated implementation tickets.
32
+
33
+ ## Version requirements
34
+
35
+ Contacts APDUs are only supported on some device models, and have minimum embedded-app and device
36
+ OS version requirements. The requirements are exposed as plain, serializable data so hosts can
37
+ consume them without duplicating the values:
38
+
39
+ ```ts
40
+ import {
41
+ CONTACTS_VERSION_REQUIREMENTS,
42
+ isContactsSupported,
43
+ resolveContactsVersionRequirements,
44
+ } from "@ledgerhq/device-contacts-kit";
45
+
46
+ // Look up the minimum OS / app versions for a device model.
47
+ const requirement = resolveContactsVersionRequirements(deviceModelId);
48
+ // { supported: true, minOsVersion, minAppVersion: { Ethereum } } | { supported: false }
49
+
50
+ // Or evaluate support directly.
51
+ const supported = isContactsSupported({
52
+ deviceModelId,
53
+ osVersion,
54
+ appName,
55
+ appVersion,
56
+ });
57
+ ```
58
+
59
+ There are two independent axes: `minOsVersion` gates OS-owned operations (served by the device OS,
60
+ e.g. renaming a contact from the dashboard) and `minAppVersion` gates app-owned operations (served
61
+ by the embedded app, keyed by app name — v1 ships Ethereum only). The `isContactsSupported` helper
62
+ and the raw `CONTACTS_VERSION_REQUIREMENTS` table are pure and dependency-light, so a host such as
63
+ Ledger Wallet can import either to compose its own app-readiness checks.
@@ -1,2 +1,2 @@
1
- "use strict";var o=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var g=Object.prototype.hasOwnProperty;var m=(t,r)=>{for(var e in r)o(t,e,{get:r[e],enumerable:!0})},s=(t,r,e,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of f(r))!g.call(t,a)&&a!==e&&o(t,a,{get:()=>r[a],enumerable:!(n=c(r,a))||n.enumerable});return t};var x=t=>s(o({},"__esModule",{value:!0}),t);var C={};m(C,{ContactsManagerBuilder:()=>p.ContactsManagerBuilder});module.exports=x(C);var p=require("./ContactsManagerBuilder");0&&(module.exports={ContactsManagerBuilder});
1
+ "use strict";var n=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var i=Object.prototype.hasOwnProperty;var u=(o,e)=>{for(var s in e)n(o,s,{get:e[s],enumerable:!0})},E=(o,e,s,p)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of c(e))!i.call(o,r)&&r!==s&&n(o,r,{get:()=>e[r],enumerable:!(p=C(e,r))||p.enumerable});return o};var M=o=>E(n({},"__esModule",{value:!0}),o);var y={};u(y,{CONTACTS_VERSION_REQUIREMENTS:()=>t.CONTACTS_VERSION_REQUIREMENTS,ContactsManagerBuilder:()=>a.ContactsManagerBuilder,ETHEREUM_APP_NAME:()=>t.ETHEREUM_APP_NAME,isContactsSupported:()=>t.isContactsSupported,isVersionAtLeast:()=>t.isVersionAtLeast,resolveContactsVersionRequirements:()=>t.resolveContactsVersionRequirements});module.exports=M(y);var a=require("./ContactsManagerBuilder"),t=require("./model/ContactsVersionRequirements");0&&(module.exports={CONTACTS_VERSION_REQUIREMENTS,ContactsManagerBuilder,ETHEREUM_APP_NAME,isContactsSupported,isVersionAtLeast,resolveContactsVersionRequirements});
2
2
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/api/index.ts"],
4
- "sourcesContent": ["export { type ContactsManager } from \"@api/ContactsManager\";\nexport { ContactsManagerBuilder } from \"@api/ContactsManagerBuilder\";\n"],
5
- "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,sEAAAE,EAAAF,GACA,IAAAG,EAAuC",
6
- "names": ["api_exports", "__export", "__toCommonJS", "import_ContactsManagerBuilder"]
4
+ "sourcesContent": ["export { type ContactsManager } from \"@api/ContactsManager\";\nexport { ContactsManagerBuilder } from \"@api/ContactsManagerBuilder\";\nexport {\n CONTACTS_VERSION_REQUIREMENTS,\n type ContactsModelRequirement,\n type ContactsModelSupport,\n type ContactsModelUnsupported,\n type ContactsSupportQuery,\n type ContactsVersionRequirements,\n ETHEREUM_APP_NAME,\n isContactsSupported,\n isVersionAtLeast,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,oVAAAE,EAAAF,GACA,IAAAG,EAAuC,uCACvCC,EAWO",
6
+ "names": ["api_exports", "__export", "__toCommonJS", "import_ContactsManagerBuilder", "import_ContactsVersionRequirements"]
7
7
  }
@@ -0,0 +1,2 @@
1
+ "use strict";var d=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var R=(e,o)=>{for(var n in o)d(e,n,{get:o[n],enumerable:!0})},S=(e,o,n,s)=>{if(o&&typeof o=="object"||typeof o=="function")for(let r of M(o))!N.call(e,r)&&r!==n&&d(e,r,{get:()=>o[r],enumerable:!(s=E(o,r))||s.enumerable});return e};var _=e=>S(d({},"__esModule",{value:!0}),e);var C={};R(C,{CONTACTS_VERSION_REQUIREMENTS:()=>V,ETHEREUM_APP_NAME:()=>i,isContactsSupported:()=>f,isVersionAtLeast:()=>c,resolveContactsVersionRequirements:()=>m});module.exports=_(C);var p=require("@ledgerhq/device-management-kit"),t=require("semver");const i="Ethereum",u={supported:!1},A="1.5.0",O="1.2.0",y="0.9.0",a="1.15.0",V={[p.DeviceModelId.NANO_S]:u,[p.DeviceModelId.NANO_SP]:u,[p.DeviceModelId.NANO_X]:u,[p.DeviceModelId.STAX]:{supported:!0,minOsVersion:A,minAppVersion:{[i]:a}},[p.DeviceModelId.FLEX]:{supported:!0,minOsVersion:O,minAppVersion:{[i]:a}},[p.DeviceModelId.APEX]:{supported:!0,minOsVersion:y,minAppVersion:{[i]:a}}};function m(e){return V[e]}function c(e,o){const n=(0,t.valid)(e)??(0,t.valid)((0,t.coerce)(e)),s=(0,t.valid)(o)??(0,t.valid)((0,t.coerce)(o));return n===null||s===null?!1:(0,t.gte)(n,s)}function f({deviceModelId:e,osVersion:o,appName:n,appVersion:s}){const r=m(e);if(!r.supported||!c(o,r.minOsVersion))return!1;const l=r.minAppVersion[n];return l===void 0?!1:c(s,l)}0&&(module.exports={CONTACTS_VERSION_REQUIREMENTS,ETHEREUM_APP_NAME,isContactsSupported,isVersionAtLeast,resolveContactsVersionRequirements});
2
+ //# sourceMappingURL=ContactsVersionRequirements.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/api/model/ContactsVersionRequirements.ts"],
4
+ "sourcesContent": ["import { DeviceModelId } from \"@ledgerhq/device-management-kit\";\nimport { coerce, gte, valid } from \"semver\";\n\n/**\n * Version requirements for a device model on which Contacts (Address Book)\n * operations are supported.\n *\n * Contacts has two independent version axes:\n * - `minOsVersion` gates OS-owned operations (e.g. renaming a contact from the\n * device dashboard), which are served by the device OS.\n * - `minAppVersion` gates app-owned operations (registering / editing external\n * addresses and Ledger accounts), which are served by the embedded app. It is\n * keyed by app name because the required version differs per app; v1 ships\n * Ethereum only.\n */\nexport type ContactsModelSupport = {\n readonly supported: true;\n readonly minOsVersion: string;\n readonly minAppVersion: Readonly<Record<string, string>>;\n};\n\n/** Marks a device model on which Contacts is not supported at all. */\nexport type ContactsModelUnsupported = {\n readonly supported: false;\n};\n\nexport type ContactsModelRequirement =\n | ContactsModelSupport\n | ContactsModelUnsupported;\n\n/**\n * The full, static Contacts version-requirement table, keyed by device model.\n * Serializable plain data with no runtime dependencies, so hosts (e.g. Ledger\n * Wallet) can import and consume it directly when composing their own\n * app-readiness checks, without duplicating the values.\n */\nexport type ContactsVersionRequirements = Readonly<\n Record<DeviceModelId, ContactsModelRequirement>\n>;\n\n/** The name of the Ethereum embedded app (the only app supported in v1). */\nexport const ETHEREUM_APP_NAME = \"Ethereum\";\n\nconst UNSUPPORTED: ContactsModelUnsupported = { supported: false };\n\n// TODO(DSDK-1376): replace these placeholder versions with the real minimums\n// confirmed by the firmware and Ethereum-app owners. The shape and consumers\n// are final; only the version strings below are provisional.\nconst MIN_OS_VERSION_STAX = \"1.5.0\";\nconst MIN_OS_VERSION_FLEX = \"1.2.0\";\nconst MIN_OS_VERSION_APEX = \"0.9.0\";\nconst MIN_ETHEREUM_APP_VERSION = \"1.15.0\";\n\n/**\n * Contacts APDUs are supported only on the touchscreen device models (Stax,\n * Flex, Apex). The Nano models do not support Contacts at all.\n */\nexport const CONTACTS_VERSION_REQUIREMENTS: ContactsVersionRequirements = {\n [DeviceModelId.NANO_S]: UNSUPPORTED,\n [DeviceModelId.NANO_SP]: UNSUPPORTED,\n [DeviceModelId.NANO_X]: UNSUPPORTED,\n [DeviceModelId.STAX]: {\n supported: true,\n minOsVersion: MIN_OS_VERSION_STAX,\n minAppVersion: { [ETHEREUM_APP_NAME]: MIN_ETHEREUM_APP_VERSION },\n },\n [DeviceModelId.FLEX]: {\n supported: true,\n minOsVersion: MIN_OS_VERSION_FLEX,\n minAppVersion: { [ETHEREUM_APP_NAME]: MIN_ETHEREUM_APP_VERSION },\n },\n [DeviceModelId.APEX]: {\n supported: true,\n minOsVersion: MIN_OS_VERSION_APEX,\n minAppVersion: { [ETHEREUM_APP_NAME]: MIN_ETHEREUM_APP_VERSION },\n },\n};\n\n/**\n * Resolve the Contacts version requirements for a device model \u2014 i.e. the\n * minimum supported OS and app versions, or that the model is unsupported.\n */\nexport function resolveContactsVersionRequirements(\n deviceModelId: DeviceModelId,\n): ContactsModelRequirement {\n return CONTACTS_VERSION_REQUIREMENTS[deviceModelId];\n}\n\n/**\n * Whether `actual` is greater than or equal to `minimum`, comparing as semver.\n * Tolerant of non-strict version strings (coerces where possible) and returns\n * `false` (i.e. requirement not met) when either version cannot be parsed, so\n * an unknown version never passes a check.\n */\nexport function isVersionAtLeast(actual: string, minimum: string): boolean {\n const a = valid(actual) ?? valid(coerce(actual));\n const b = valid(minimum) ?? valid(coerce(minimum));\n if (a === null || b === null) return false;\n return gte(a, b);\n}\n\n/** Inputs for {@link isContactsSupported}. */\nexport type ContactsSupportQuery = {\n readonly deviceModelId: DeviceModelId;\n /** The device OS (firmware) version. */\n readonly osVersion: string;\n /** The name of the currently running embedded app. */\n readonly appName: string;\n /** The version of the currently running embedded app. */\n readonly appVersion: string;\n};\n\n/**\n * Whether Contacts app-owned operations are supported for the given device\n * model, OS version, and running app \u2014 checking the model is supported, the OS\n * meets its minimum, the app is known to Contacts, and the app meets its\n * minimum.\n *\n * Pure and dependency-light: hosts can call this directly, or read\n * {@link CONTACTS_VERSION_REQUIREMENTS} to build their own checks.\n */\nexport function isContactsSupported({\n deviceModelId,\n osVersion,\n appName,\n appVersion,\n}: ContactsSupportQuery): boolean {\n const requirement = resolveContactsVersionRequirements(deviceModelId);\n if (!requirement.supported) return false;\n if (!isVersionAtLeast(osVersion, requirement.minOsVersion)) return false;\n\n const minAppVersion = requirement.minAppVersion[appName];\n if (minAppVersion === undefined) return false;\n return isVersionAtLeast(appVersion, minAppVersion);\n}\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,mCAAAE,EAAA,sBAAAC,EAAA,wBAAAC,EAAA,qBAAAC,EAAA,uCAAAC,IAAA,eAAAC,EAAAP,GAAA,IAAAQ,EAA8B,2CAC9BC,EAAmC,kBAwC5B,MAAMN,EAAoB,WAE3BO,EAAwC,CAAE,UAAW,EAAM,EAK3DC,EAAsB,QACtBC,EAAsB,QACtBC,EAAsB,QACtBC,EAA2B,SAMpBZ,EAA6D,CACxE,CAAC,gBAAc,MAAM,EAAGQ,EACxB,CAAC,gBAAc,OAAO,EAAGA,EACzB,CAAC,gBAAc,MAAM,EAAGA,EACxB,CAAC,gBAAc,IAAI,EAAG,CACpB,UAAW,GACX,aAAcC,EACd,cAAe,CAAE,CAACR,CAAiB,EAAGW,CAAyB,CACjE,EACA,CAAC,gBAAc,IAAI,EAAG,CACpB,UAAW,GACX,aAAcF,EACd,cAAe,CAAE,CAACT,CAAiB,EAAGW,CAAyB,CACjE,EACA,CAAC,gBAAc,IAAI,EAAG,CACpB,UAAW,GACX,aAAcD,EACd,cAAe,CAAE,CAACV,CAAiB,EAAGW,CAAyB,CACjE,CACF,EAMO,SAASR,EACdS,EAC0B,CAC1B,OAAOb,EAA8Ba,CAAa,CACpD,CAQO,SAASV,EAAiBW,EAAgBC,EAA0B,CACzE,MAAMC,KAAI,SAAMF,CAAM,MAAK,YAAM,UAAOA,CAAM,CAAC,EACzCG,KAAI,SAAMF,CAAO,MAAK,YAAM,UAAOA,CAAO,CAAC,EACjD,OAAIC,IAAM,MAAQC,IAAM,KAAa,MAC9B,OAAID,EAAGC,CAAC,CACjB,CAsBO,SAASf,EAAoB,CAClC,cAAAW,EACA,UAAAK,EACA,QAAAC,EACA,WAAAC,CACF,EAAkC,CAChC,MAAMC,EAAcjB,EAAmCS,CAAa,EAEpE,GADI,CAACQ,EAAY,WACb,CAAClB,EAAiBe,EAAWG,EAAY,YAAY,EAAG,MAAO,GAEnE,MAAMC,EAAgBD,EAAY,cAAcF,CAAO,EACvD,OAAIG,IAAkB,OAAkB,GACjCnB,EAAiBiB,EAAYE,CAAa,CACnD",
6
+ "names": ["ContactsVersionRequirements_exports", "__export", "CONTACTS_VERSION_REQUIREMENTS", "ETHEREUM_APP_NAME", "isContactsSupported", "isVersionAtLeast", "resolveContactsVersionRequirements", "__toCommonJS", "import_device_management_kit", "import_semver", "UNSUPPORTED", "MIN_OS_VERSION_STAX", "MIN_OS_VERSION_FLEX", "MIN_OS_VERSION_APEX", "MIN_ETHEREUM_APP_VERSION", "deviceModelId", "actual", "minimum", "a", "b", "osVersion", "appName", "appVersion", "requirement", "minAppVersion"]
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";var o=require("@ledgerhq/device-management-kit"),e=require("./ContactsVersionRequirements");const a="0.0.1",p="999.0.0";function n(t){const s=(0,e.resolveContactsVersionRequirements)(t);if(!s.supported)throw new Error(`expected ${t} to be supported in the test fixture`);return s}function r(t){const s=t.minAppVersion[e.ETHEREUM_APP_NAME];if(s===void 0)throw new Error("expected an Ethereum minimum app version in the fixture");return s}describe("ContactsVersionRequirements",()=>{describe("resolveContactsVersionRequirements",()=>{it("marks every device model, supported or not",()=>{for(const t of Object.values(o.DeviceModelId))expect(e.CONTACTS_VERSION_REQUIREMENTS[t]).toBeDefined()}),it("reports Nano models as unsupported",()=>{expect((0,e.resolveContactsVersionRequirements)(o.DeviceModelId.NANO_S)).toEqual({supported:!1}),expect((0,e.resolveContactsVersionRequirements)(o.DeviceModelId.NANO_X)).toEqual({supported:!1})}),it("reports touchscreen models as supported with OS and app minimums",()=>{const t=n(o.DeviceModelId.FLEX);expect(t.minOsVersion).toEqual(expect.any(String)),expect(r(t)).toEqual(expect.any(String))})}),describe("isContactsSupported",()=>{it("returns false for an unsupported device model",()=>{const t=n(o.DeviceModelId.FLEX);expect((0,e.isContactsSupported)({deviceModelId:o.DeviceModelId.NANO_S,osVersion:p,appName:e.ETHEREUM_APP_NAME,appVersion:r(t)})).toBe(!1)}),it("gates on the minimum app version",()=>{const t=n(o.DeviceModelId.FLEX),s=r(t),i={deviceModelId:o.DeviceModelId.FLEX,osVersion:t.minOsVersion,appName:e.ETHEREUM_APP_NAME};expect((0,e.isContactsSupported)({...i,appVersion:a})).toBe(!1),expect((0,e.isContactsSupported)({...i,appVersion:s})).toBe(!0),expect((0,e.isContactsSupported)({...i,appVersion:p})).toBe(!0)}),it("returns false for an app that is unknown to Contacts",()=>{const t=n(o.DeviceModelId.FLEX);expect((0,e.isContactsSupported)({deviceModelId:o.DeviceModelId.FLEX,osVersion:t.minOsVersion,appName:"Bitcoin",appVersion:p})).toBe(!1)}),it("gates on the minimum OS version",()=>{const t=n(o.DeviceModelId.FLEX),s={deviceModelId:o.DeviceModelId.FLEX,appName:e.ETHEREUM_APP_NAME,appVersion:r(t)};expect((0,e.isContactsSupported)({...s,osVersion:a})).toBe(!1),expect((0,e.isContactsSupported)({...s,osVersion:t.minOsVersion})).toBe(!0)})}),describe("isVersionAtLeast",()=>{it("compares versions as semver, inclusive of equality",()=>{expect((0,e.isVersionAtLeast)("1.2.0","1.2.0")).toBe(!0),expect((0,e.isVersionAtLeast)("1.3.0","1.2.0")).toBe(!0),expect((0,e.isVersionAtLeast)("1.1.9","1.2.0")).toBe(!1)}),it("coerces non-strict version strings",()=>{expect((0,e.isVersionAtLeast)("1.2","1.2.0")).toBe(!0)}),it("returns false when a version cannot be parsed",()=>{expect((0,e.isVersionAtLeast)("not-a-version","1.2.0")).toBe(!1),expect((0,e.isVersionAtLeast)("1.2.0","not-a-version")).toBe(!1)})})});
2
+ //# sourceMappingURL=ContactsVersionRequirements.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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 type ContactsModelSupport,\n ETHEREUM_APP_NAME,\n isContactsSupported,\n isVersionAtLeast,\n resolveContactsVersionRequirements,\n} from \"./ContactsVersionRequirements\";\n\nconst BELOW_ANY_VERSION = \"0.0.1\";\nconst ABOVE_ANY_VERSION = \"999.0.0\";\n\nfunction supportOf(modelId: DeviceModelId): ContactsModelSupport {\n const requirement = resolveContactsVersionRequirements(modelId);\n if (!requirement.supported) {\n throw new Error(`expected ${modelId} to be supported in the test fixture`);\n }\n return requirement;\n}\n\nfunction ethAppVersionOf(support: ContactsModelSupport): string {\n const version = support.minAppVersion[ETHEREUM_APP_NAME];\n if (version === undefined) {\n throw new Error(\"expected an Ethereum minimum app version in the fixture\");\n }\n return version;\n}\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 models as unsupported\", () => {\n expect(resolveContactsVersionRequirements(DeviceModelId.NANO_S)).toEqual({\n supported: false,\n });\n expect(resolveContactsVersionRequirements(DeviceModelId.NANO_X)).toEqual({\n supported: false,\n });\n });\n\n it(\"reports touchscreen models as supported with OS and app minimums\", () => {\n const flex = supportOf(DeviceModelId.FLEX);\n expect(flex.minOsVersion).toEqual(expect.any(String));\n expect(ethAppVersionOf(flex)).toEqual(expect.any(String));\n });\n });\n\n describe(\"isContactsSupported\", () => {\n it(\"returns false for an unsupported device model\", () => {\n const flex = supportOf(DeviceModelId.FLEX);\n expect(\n isContactsSupported({\n deviceModelId: DeviceModelId.NANO_S,\n osVersion: ABOVE_ANY_VERSION,\n appName: ETHEREUM_APP_NAME,\n appVersion: ethAppVersionOf(flex),\n }),\n ).toBe(false);\n });\n\n // App-version requirement.\n it(\"gates on the minimum app version\", () => {\n const flex = supportOf(DeviceModelId.FLEX);\n const minAppVersion = ethAppVersionOf(flex);\n const base = {\n deviceModelId: DeviceModelId.FLEX,\n osVersion: flex.minOsVersion,\n appName: ETHEREUM_APP_NAME,\n };\n\n expect(\n isContactsSupported({ ...base, appVersion: BELOW_ANY_VERSION }),\n ).toBe(false);\n expect(isContactsSupported({ ...base, appVersion: minAppVersion })).toBe(\n true,\n );\n expect(\n isContactsSupported({ ...base, appVersion: ABOVE_ANY_VERSION }),\n ).toBe(true);\n });\n\n it(\"returns false for an app that is unknown to Contacts\", () => {\n const flex = supportOf(DeviceModelId.FLEX);\n expect(\n isContactsSupported({\n deviceModelId: DeviceModelId.FLEX,\n osVersion: flex.minOsVersion,\n appName: \"Bitcoin\",\n appVersion: ABOVE_ANY_VERSION,\n }),\n ).toBe(false);\n });\n\n // OS-version requirement.\n it(\"gates on the minimum OS version\", () => {\n const flex = supportOf(DeviceModelId.FLEX);\n const base = {\n deviceModelId: DeviceModelId.FLEX,\n appName: ETHEREUM_APP_NAME,\n appVersion: ethAppVersionOf(flex),\n };\n\n expect(\n isContactsSupported({ ...base, osVersion: BELOW_ANY_VERSION }),\n ).toBe(false);\n expect(\n isContactsSupported({ ...base, osVersion: flex.minOsVersion }),\n ).toBe(true);\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});\n"],
5
+ "mappings": "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",
6
+ "names": ["import_device_management_kit", "import_ContactsVersionRequirements", "BELOW_ANY_VERSION", "ABOVE_ANY_VERSION", "supportOf", "modelId", "requirement", "ethAppVersionOf", "support", "version", "flex", "minAppVersion", "base"]
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";var i=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var l=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var c=(p,e)=>{for(var s in e)i(p,s,{get:e[s],enumerable:!0})},m=(p,e,s,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of l(e))!v.call(p,o)&&o!==s&&i(p,o,{get:()=>e[o],enumerable:!(t=a(e,o))||t.enumerable});return p};var A=p=>m(i({},"__esModule",{value:!0}),p);var C={};c(C,{ContactsApplicationResolver:()=>f});module.exports=A(C);var r=require("@ledgerhq/device-management-kit");const n="0.0.0";class f{resolve(e,s){return e.sessionStateType===r.DeviceSessionStateType.Connected?{isCompatible:!1,version:n}:e.currentApp?.name?{isCompatible:!0,version:s.version}:{isCompatible:!1,version:n}}}0&&(module.exports={ContactsApplicationResolver});
2
+ //# sourceMappingURL=ContactsApplicationResolver.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/internal/app-binder/ContactsApplicationResolver.ts"],
4
+ "sourcesContent": ["import {\n type AppConfig,\n type ApplicationResolver,\n type DeviceSessionState,\n DeviceSessionStateType,\n type ResolvedApp,\n} from \"@ledgerhq/device-management-kit\";\n\nconst DEFAULT_VERSION = \"0.0.0\";\n\n/**\n * Resolves the running-app version for Contacts, for use with DMK's\n * `ApplicationChecker`. Which app names and versions are actually required is\n * decided by the static Contacts requirements table, so this resolver only\n * surfaces the authoritative app version (from `GetAppConfiguration`) and\n * rejects sessions where no app is running.\n */\nexport class ContactsApplicationResolver implements ApplicationResolver {\n resolve(deviceState: DeviceSessionState, appConfig: AppConfig): ResolvedApp {\n if (deviceState.sessionStateType === DeviceSessionStateType.Connected) {\n return { isCompatible: false, version: DEFAULT_VERSION };\n }\n\n const appName = deviceState.currentApp?.name;\n if (!appName) {\n return { isCompatible: false, version: DEFAULT_VERSION };\n }\n\n return { isCompatible: true, version: appConfig.version };\n }\n}\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,iCAAAE,IAAA,eAAAC,EAAAH,GAAA,IAAAI,EAMO,2CAEP,MAAMC,EAAkB,QASjB,MAAMH,CAA2D,CACtE,QAAQI,EAAiCC,EAAmC,CAC1E,OAAID,EAAY,mBAAqB,yBAAuB,UACnD,CAAE,aAAc,GAAO,QAASD,CAAgB,EAGzCC,EAAY,YAAY,KAKjC,CAAE,aAAc,GAAM,QAASC,EAAU,OAAQ,EAH/C,CAAE,aAAc,GAAO,QAASF,CAAgB,CAI3D,CACF",
6
+ "names": ["ContactsApplicationResolver_exports", "__export", "ContactsApplicationResolver", "__toCommonJS", "import_device_management_kit", "DEFAULT_VERSION", "deviceState", "appConfig"]
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";var s=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var m=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var l=(i,e)=>{for(var n in e)s(i,n,{get:e[n],enumerable:!0})},A=(i,e,n,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of m(e))!d.call(i,r)&&r!==n&&s(i,r,{get:()=>e[r],enumerable:!(t=u(e,r))||t.enumerable});return i};var V=i=>A(s({},"__esModule",{value:!0}),i);var v={};l(v,{isContactsSupportedForSession:()=>C});module.exports=V(v);var f=require("@ledgerhq/device-management-kit"),o=require("../../api/model/ContactsVersionRequirements"),c=require("./ContactsApplicationResolver");function C(i,e){const n=i.getDeviceSessionState(),t=(0,o.resolveContactsVersionRequirements)(n.deviceModelId);if(!t.supported)return!1;const r="currentApp"in n?n.currentApp?.name:void 0;if(r===void 0)return!1;const p=t.minAppVersion[r];if(p===void 0||!new f.ApplicationChecker(n,e,new c.ContactsApplicationResolver).withMinVersionInclusive(p).check())return!1;const a="firmwareVersion"in n?n.firmwareVersion?.os:void 0;return a===void 0?!1:(0,o.isVersionAtLeast)(a,t.minOsVersion)}0&&(module.exports={isContactsSupportedForSession});
2
+ //# sourceMappingURL=isContactsSupportedForSession.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/internal/app-binder/isContactsSupportedForSession.ts"],
4
+ "sourcesContent": ["import {\n type AppConfig,\n ApplicationChecker,\n type InternalApi,\n} from \"@ledgerhq/device-management-kit\";\n\nimport {\n isVersionAtLeast,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n\nimport { ContactsApplicationResolver } from \"./ContactsApplicationResolver\";\n\n/**\n * Session-aware Contacts support check, for internal consumption by Contacts\n * `DeviceAction`s. Reads the current device session and resolves it against the\n * static requirements table:\n * - the device model must be supported;\n * - the running app must be known to Contacts and meet its minimum version \u2014\n * validated with DMK's {@link ApplicationChecker} so the version comes from\n * the authoritative `GetAppConfiguration` result;\n * - the device OS must meet its minimum version (a dimension `ApplicationChecker`\n * does not cover, so it is checked separately here).\n *\n * @param internalApi - the DeviceAction's internal API for the current session.\n * @param appConfig - the running app's configuration (must include its version).\n */\nexport function isContactsSupportedForSession(\n internalApi: InternalApi,\n appConfig: AppConfig,\n): boolean {\n const deviceState = internalApi.getDeviceSessionState();\n const requirement = resolveContactsVersionRequirements(\n deviceState.deviceModelId,\n );\n if (!requirement.supported) return false;\n\n const appName =\n \"currentApp\" in deviceState ? deviceState.currentApp?.name : undefined;\n if (appName === undefined) return false;\n\n const minAppVersion = requirement.minAppVersion[appName];\n if (minAppVersion === undefined) return false;\n\n const appCompatible = new ApplicationChecker(\n deviceState,\n appConfig,\n new ContactsApplicationResolver(),\n )\n .withMinVersionInclusive(minAppVersion)\n .check();\n if (!appCompatible) return false;\n\n const osVersion =\n \"firmwareVersion\" in deviceState\n ? deviceState.firmwareVersion?.os\n : undefined;\n if (osVersion === undefined) return false;\n\n return isVersionAtLeast(osVersion, requirement.minOsVersion);\n}\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,mCAAAE,IAAA,eAAAC,EAAAH,GAAA,IAAAI,EAIO,2CAEPC,EAGO,kDAEPC,EAA4C,yCAgBrC,SAASJ,EACdK,EACAC,EACS,CACT,MAAMC,EAAcF,EAAY,sBAAsB,EAChDG,KAAc,sCAClBD,EAAY,aACd,EACA,GAAI,CAACC,EAAY,UAAW,MAAO,GAEnC,MAAMC,EACJ,eAAgBF,EAAcA,EAAY,YAAY,KAAO,OAC/D,GAAIE,IAAY,OAAW,MAAO,GAElC,MAAMC,EAAgBF,EAAY,cAAcC,CAAO,EAUvD,GATIC,IAAkB,QASlB,CAPkB,IAAI,qBACxBH,EACAD,EACA,IAAI,6BACN,EACG,wBAAwBI,CAAa,EACrC,MAAM,EACW,MAAO,GAE3B,MAAMC,EACJ,oBAAqBJ,EACjBA,EAAY,iBAAiB,GAC7B,OACN,OAAII,IAAc,OAAkB,MAE7B,oBAAiBA,EAAWH,EAAY,YAAY,CAC7D",
6
+ "names": ["isContactsSupportedForSession_exports", "__export", "isContactsSupportedForSession", "__toCommonJS", "import_device_management_kit", "import_ContactsVersionRequirements", "import_ContactsApplicationResolver", "internalApi", "appConfig", "deviceState", "requirement", "appName", "minAppVersion", "osVersion"]
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";var t=require("@ledgerhq/device-management-kit"),p=require("../../api/model/ContactsVersionRequirements"),n=require("./isContactsSupportedForSession");const c=(()=>{const e=(0,p.resolveContactsVersionRequirements)(t.DeviceModelId.FLEX);if(!e.supported)throw new Error("Flex must be supported");return e})(),l=c.minOsVersion,i=(()=>{const e=c.minAppVersion[p.ETHEREUM_APP_NAME];if(e===void 0)throw new Error("Ethereum min version required");return e})(),a="0.0.1";function r({appName:e=p.ETHEREUM_APP_NAME,osVersion:u=l,modelId:d=t.DeviceModelId.FLEX}={}){return{sessionStateType:t.DeviceSessionStateType.ReadyWithoutSecureChannel,deviceStatus:t.DeviceStatus.CONNECTED,installedApps:[],currentApp:{name:e,version:"0.0.0"},deviceModelId:d,firmwareVersion:{mcu:"1.0.0",bootloader:"1.0.0",os:u},isSecureConnectionAllowed:!1}}function o(e){return{getDeviceSessionState:()=>e}}function s(e){return{version:e}}describe("isContactsSupportedForSession",()=>{it("returns true when model, app version and OS version all meet the minimums",()=>{const e=o(r());expect((0,n.isContactsSupportedForSession)(e,s(i))).toBe(!0)}),it("returns false on an unsupported device model",()=>{const e=o(r({modelId:t.DeviceModelId.NANO_X}));expect((0,n.isContactsSupportedForSession)(e,s(i))).toBe(!1)}),it("returns false when the app version is below the minimum",()=>{const e=o(r());expect((0,n.isContactsSupportedForSession)(e,s(a))).toBe(!1)}),it("returns false when the running app is unknown to Contacts",()=>{const e=o(r({appName:"Bitcoin"}));expect((0,n.isContactsSupportedForSession)(e,s(i))).toBe(!1)}),it("returns false when the OS version is below the minimum",()=>{const e=o(r({osVersion:a}));expect((0,n.isContactsSupportedForSession)(e,s(i))).toBe(!1)}),it("returns false when the device session has no running app",()=>{const e=o({sessionStateType:t.DeviceSessionStateType.Connected,deviceStatus:t.DeviceStatus.CONNECTED,deviceModelId:t.DeviceModelId.FLEX});expect((0,n.isContactsSupportedForSession)(e,s(i))).toBe(!1)})});
2
+ //# sourceMappingURL=isContactsSupportedForSession.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/internal/app-binder/isContactsSupportedForSession.test.ts"],
4
+ "sourcesContent": ["import {\n type AppConfig,\n DeviceModelId,\n DeviceSessionStateType,\n DeviceStatus,\n type InternalApi,\n} from \"@ledgerhq/device-management-kit\";\n\nimport {\n ETHEREUM_APP_NAME,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n\nimport { isContactsSupportedForSession } from \"./isContactsSupportedForSession\";\n\nconst flexSupport = (() => {\n const requirement = resolveContactsVersionRequirements(DeviceModelId.FLEX);\n if (!requirement.supported) throw new Error(\"Flex must be supported\");\n return requirement;\n})();\n\nconst MIN_OS_VERSION = flexSupport.minOsVersion;\nconst MIN_APP_VERSION = (() => {\n const version = flexSupport.minAppVersion[ETHEREUM_APP_NAME];\n if (version === undefined) throw new Error(\"Ethereum min version required\");\n return version;\n})();\nconst BELOW_ANY_VERSION = \"0.0.1\";\n\ntype ReadyStateOptions = {\n appName?: string;\n osVersion?: string;\n modelId?: DeviceModelId;\n};\n\nfunction createReadyState({\n appName = ETHEREUM_APP_NAME,\n osVersion = MIN_OS_VERSION,\n modelId = DeviceModelId.FLEX,\n}: ReadyStateOptions = {}) {\n return {\n sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel,\n deviceStatus: DeviceStatus.CONNECTED,\n installedApps: [],\n currentApp: { name: appName, version: \"0.0.0\" },\n deviceModelId: modelId,\n firmwareVersion: { mcu: \"1.0.0\", bootloader: \"1.0.0\", os: osVersion },\n isSecureConnectionAllowed: false,\n };\n}\n\nfunction createInternalApi(deviceState: object): InternalApi {\n return {\n getDeviceSessionState: () => deviceState,\n } as unknown as InternalApi;\n}\n\nfunction appConfig(version: string): AppConfig {\n return { version };\n}\n\ndescribe(\"isContactsSupportedForSession\", () => {\n it(\"returns true when model, app version and OS version all meet the minimums\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n true,\n );\n });\n\n it(\"returns false on an unsupported device model\", () => {\n const api = createInternalApi(\n createReadyState({ modelId: DeviceModelId.NANO_X }),\n );\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n false,\n );\n });\n\n // App-version requirement.\n it(\"returns false when the app version is below the minimum\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsSupportedForSession(api, appConfig(BELOW_ANY_VERSION)),\n ).toBe(false);\n });\n\n it(\"returns false when the running app is unknown to Contacts\", () => {\n const api = createInternalApi(createReadyState({ appName: \"Bitcoin\" }));\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n false,\n );\n });\n\n // OS-version requirement.\n it(\"returns false when the OS version is below the minimum\", () => {\n const api = createInternalApi(\n createReadyState({ osVersion: BELOW_ANY_VERSION }),\n );\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n false,\n );\n });\n\n it(\"returns false when the device session has no running app\", () => {\n const api = createInternalApi({\n sessionStateType: DeviceSessionStateType.Connected,\n deviceStatus: DeviceStatus.CONNECTED,\n deviceModelId: DeviceModelId.FLEX,\n });\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n false,\n );\n });\n});\n"],
5
+ "mappings": "aAAA,IAAAA,EAMO,2CAEPC,EAGO,kDAEPC,EAA8C,2CAE9C,MAAMC,GAAe,IAAM,CACzB,MAAMC,KAAc,sCAAmC,gBAAc,IAAI,EACzE,GAAI,CAACA,EAAY,UAAW,MAAM,IAAI,MAAM,wBAAwB,EACpE,OAAOA,CACT,GAAG,EAEGC,EAAiBF,EAAY,aAC7BG,GAAmB,IAAM,CAC7B,MAAMC,EAAUJ,EAAY,cAAc,mBAAiB,EAC3D,GAAII,IAAY,OAAW,MAAM,IAAI,MAAM,+BAA+B,EAC1E,OAAOA,CACT,GAAG,EACGC,EAAoB,QAQ1B,SAASC,EAAiB,CACxB,QAAAC,EAAU,oBACV,UAAAC,EAAYN,EACZ,QAAAO,EAAU,gBAAc,IAC1B,EAAuB,CAAC,EAAG,CACzB,MAAO,CACL,iBAAkB,yBAAuB,0BACzC,aAAc,eAAa,UAC3B,cAAe,CAAC,EAChB,WAAY,CAAE,KAAMF,EAAS,QAAS,OAAQ,EAC9C,cAAeE,EACf,gBAAiB,CAAE,IAAK,QAAS,WAAY,QAAS,GAAID,CAAU,EACpE,0BAA2B,EAC7B,CACF,CAEA,SAASE,EAAkBC,EAAkC,CAC3D,MAAO,CACL,sBAAuB,IAAMA,CAC/B,CACF,CAEA,SAASC,EAAUR,EAA4B,CAC7C,MAAO,CAAE,QAAAA,CAAQ,CACnB,CAEA,SAAS,gCAAiC,IAAM,CAC9C,GAAG,4EAA6E,IAAM,CACpF,MAAMS,EAAMH,EAAkBJ,EAAiB,CAAC,EAChD,UAAO,iCAA8BO,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,EAED,GAAG,+CAAgD,IAAM,CACvD,MAAMU,EAAMH,EACVJ,EAAiB,CAAE,QAAS,gBAAc,MAAO,CAAC,CACpD,EACA,UAAO,iCAA8BO,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,EAGD,GAAG,0DAA2D,IAAM,CAClE,MAAMU,EAAMH,EAAkBJ,EAAiB,CAAC,EAChD,UACE,iCAA8BO,EAAKD,EAAUP,CAAiB,CAAC,CACjE,EAAE,KAAK,EAAK,CACd,CAAC,EAED,GAAG,4DAA6D,IAAM,CACpE,MAAMQ,EAAMH,EAAkBJ,EAAiB,CAAE,QAAS,SAAU,CAAC,CAAC,EACtE,UAAO,iCAA8BO,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,EAGD,GAAG,yDAA0D,IAAM,CACjE,MAAMU,EAAMH,EACVJ,EAAiB,CAAE,UAAWD,CAAkB,CAAC,CACnD,EACA,UAAO,iCAA8BQ,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,EAED,GAAG,2DAA4D,IAAM,CACnE,MAAMU,EAAMH,EAAkB,CAC5B,iBAAkB,yBAAuB,UACzC,aAAc,eAAa,UAC3B,cAAe,gBAAc,IAC/B,CAAC,EACD,UAAO,iCAA8BG,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,CACH,CAAC",
6
+ "names": ["import_device_management_kit", "import_ContactsVersionRequirements", "import_isContactsSupportedForSession", "flexSupport", "requirement", "MIN_OS_VERSION", "MIN_APP_VERSION", "version", "BELOW_ANY_VERSION", "createReadyState", "appName", "osVersion", "modelId", "createInternalApi", "deviceState", "appConfig", "api"]
7
+ }
@@ -3,6 +3,7 @@
3
3
  "inversify": "catalog:",
4
4
  "purify-ts": "catalog:",
5
5
  "reflect-metadata": "catalog:",
6
+ "semver": "catalog:",
6
7
  "xstate": "catalog:"
7
8
  },
8
9
  "devDependencies": {
@@ -12,6 +13,7 @@
12
13
  "@ledgerhq/prettier-config-dsdk": "workspace:*",
13
14
  "@ledgerhq/tsconfig-dsdk": "workspace:*",
14
15
  "@ledgerhq/vitest-config-dmk": "workspace:*",
16
+ "@types/semver": "catalog:",
15
17
  "rxjs": "catalog:",
16
18
  "ts-node": "catalog:"
17
19
  },
@@ -55,5 +57,5 @@
55
57
  "watch:builds": "pnpm ldmk-tool watch --entryPoints src/index.ts,src/**/*.ts --tsconfig tsconfig.prod.json",
56
58
  "watch:types": "concurrently \"tsc --watch -p tsconfig.prod.json\" \"tsc-alias --watch -p tsconfig.prod.json\""
57
59
  },
58
- "version": "0.0.0-develop-20260813001639"
60
+ "version": "0.0.0-develop-20260815000734"
59
61
  }
@@ -1,2 +1,2 @@
1
- import{ContactsManagerBuilder as a}from"./ContactsManagerBuilder";export{a as ContactsManagerBuilder};
1
+ import{ContactsManagerBuilder as o}from"./ContactsManagerBuilder";import{CONTACTS_VERSION_REQUIREMENTS as s,ETHEREUM_APP_NAME as n,isContactsSupported as p,isVersionAtLeast as a,resolveContactsVersionRequirements as C}from"./model/ContactsVersionRequirements";export{s as CONTACTS_VERSION_REQUIREMENTS,o as ContactsManagerBuilder,n as ETHEREUM_APP_NAME,p as isContactsSupported,a as isVersionAtLeast,C as resolveContactsVersionRequirements};
2
2
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/api/index.ts"],
4
- "sourcesContent": ["export { type ContactsManager } from \"@api/ContactsManager\";\nexport { ContactsManagerBuilder } from \"@api/ContactsManagerBuilder\";\n"],
5
- "mappings": "AACA,OAAS,0BAAAA,MAA8B",
6
- "names": ["ContactsManagerBuilder"]
4
+ "sourcesContent": ["export { type ContactsManager } from \"@api/ContactsManager\";\nexport { ContactsManagerBuilder } from \"@api/ContactsManagerBuilder\";\nexport {\n CONTACTS_VERSION_REQUIREMENTS,\n type ContactsModelRequirement,\n type ContactsModelSupport,\n type ContactsModelUnsupported,\n type ContactsSupportQuery,\n type ContactsVersionRequirements,\n ETHEREUM_APP_NAME,\n isContactsSupported,\n isVersionAtLeast,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n"],
5
+ "mappings": "AACA,OAAS,0BAAAA,MAA8B,8BACvC,OACE,iCAAAC,EAMA,qBAAAC,EACA,uBAAAC,EACA,oBAAAC,EACA,sCAAAC,MACK",
6
+ "names": ["ContactsManagerBuilder", "CONTACTS_VERSION_REQUIREMENTS", "ETHEREUM_APP_NAME", "isContactsSupported", "isVersionAtLeast", "resolveContactsVersionRequirements"]
7
7
  }
@@ -0,0 +1,2 @@
1
+ import{DeviceModelId as o}from"@ledgerhq/device-management-kit";import{coerce as c,gte as V,valid as s}from"semver";const i="Ethereum",d={supported:!1},m="1.5.0",E="1.2.0",M="0.9.0",u="1.15.0",N={[o.NANO_S]:d,[o.NANO_SP]:d,[o.NANO_X]:d,[o.STAX]:{supported:!0,minOsVersion:m,minAppVersion:{[i]:u}},[o.FLEX]:{supported:!0,minOsVersion:E,minAppVersion:{[i]:u}},[o.APEX]:{supported:!0,minOsVersion:M,minAppVersion:{[i]:u}}};function R(e){return N[e]}function l(e,t){const n=s(e)??s(c(e)),r=s(t)??s(c(t));return n===null||r===null?!1:V(n,r)}function A({deviceModelId:e,osVersion:t,appName:n,appVersion:r}){const p=R(e);if(!p.supported||!l(t,p.minOsVersion))return!1;const a=p.minAppVersion[n];return a===void 0?!1:l(r,a)}export{N as CONTACTS_VERSION_REQUIREMENTS,i as ETHEREUM_APP_NAME,A as isContactsSupported,l as isVersionAtLeast,R as resolveContactsVersionRequirements};
2
+ //# sourceMappingURL=ContactsVersionRequirements.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/api/model/ContactsVersionRequirements.ts"],
4
+ "sourcesContent": ["import { DeviceModelId } from \"@ledgerhq/device-management-kit\";\nimport { coerce, gte, valid } from \"semver\";\n\n/**\n * Version requirements for a device model on which Contacts (Address Book)\n * operations are supported.\n *\n * Contacts has two independent version axes:\n * - `minOsVersion` gates OS-owned operations (e.g. renaming a contact from the\n * device dashboard), which are served by the device OS.\n * - `minAppVersion` gates app-owned operations (registering / editing external\n * addresses and Ledger accounts), which are served by the embedded app. It is\n * keyed by app name because the required version differs per app; v1 ships\n * Ethereum only.\n */\nexport type ContactsModelSupport = {\n readonly supported: true;\n readonly minOsVersion: string;\n readonly minAppVersion: Readonly<Record<string, string>>;\n};\n\n/** Marks a device model on which Contacts is not supported at all. */\nexport type ContactsModelUnsupported = {\n readonly supported: false;\n};\n\nexport type ContactsModelRequirement =\n | ContactsModelSupport\n | ContactsModelUnsupported;\n\n/**\n * The full, static Contacts version-requirement table, keyed by device model.\n * Serializable plain data with no runtime dependencies, so hosts (e.g. Ledger\n * Wallet) can import and consume it directly when composing their own\n * app-readiness checks, without duplicating the values.\n */\nexport type ContactsVersionRequirements = Readonly<\n Record<DeviceModelId, ContactsModelRequirement>\n>;\n\n/** The name of the Ethereum embedded app (the only app supported in v1). */\nexport const ETHEREUM_APP_NAME = \"Ethereum\";\n\nconst UNSUPPORTED: ContactsModelUnsupported = { supported: false };\n\n// TODO(DSDK-1376): replace these placeholder versions with the real minimums\n// confirmed by the firmware and Ethereum-app owners. The shape and consumers\n// are final; only the version strings below are provisional.\nconst MIN_OS_VERSION_STAX = \"1.5.0\";\nconst MIN_OS_VERSION_FLEX = \"1.2.0\";\nconst MIN_OS_VERSION_APEX = \"0.9.0\";\nconst MIN_ETHEREUM_APP_VERSION = \"1.15.0\";\n\n/**\n * Contacts APDUs are supported only on the touchscreen device models (Stax,\n * Flex, Apex). The Nano models do not support Contacts at all.\n */\nexport const CONTACTS_VERSION_REQUIREMENTS: ContactsVersionRequirements = {\n [DeviceModelId.NANO_S]: UNSUPPORTED,\n [DeviceModelId.NANO_SP]: UNSUPPORTED,\n [DeviceModelId.NANO_X]: UNSUPPORTED,\n [DeviceModelId.STAX]: {\n supported: true,\n minOsVersion: MIN_OS_VERSION_STAX,\n minAppVersion: { [ETHEREUM_APP_NAME]: MIN_ETHEREUM_APP_VERSION },\n },\n [DeviceModelId.FLEX]: {\n supported: true,\n minOsVersion: MIN_OS_VERSION_FLEX,\n minAppVersion: { [ETHEREUM_APP_NAME]: MIN_ETHEREUM_APP_VERSION },\n },\n [DeviceModelId.APEX]: {\n supported: true,\n minOsVersion: MIN_OS_VERSION_APEX,\n minAppVersion: { [ETHEREUM_APP_NAME]: MIN_ETHEREUM_APP_VERSION },\n },\n};\n\n/**\n * Resolve the Contacts version requirements for a device model \u2014 i.e. the\n * minimum supported OS and app versions, or that the model is unsupported.\n */\nexport function resolveContactsVersionRequirements(\n deviceModelId: DeviceModelId,\n): ContactsModelRequirement {\n return CONTACTS_VERSION_REQUIREMENTS[deviceModelId];\n}\n\n/**\n * Whether `actual` is greater than or equal to `minimum`, comparing as semver.\n * Tolerant of non-strict version strings (coerces where possible) and returns\n * `false` (i.e. requirement not met) when either version cannot be parsed, so\n * an unknown version never passes a check.\n */\nexport function isVersionAtLeast(actual: string, minimum: string): boolean {\n const a = valid(actual) ?? valid(coerce(actual));\n const b = valid(minimum) ?? valid(coerce(minimum));\n if (a === null || b === null) return false;\n return gte(a, b);\n}\n\n/** Inputs for {@link isContactsSupported}. */\nexport type ContactsSupportQuery = {\n readonly deviceModelId: DeviceModelId;\n /** The device OS (firmware) version. */\n readonly osVersion: string;\n /** The name of the currently running embedded app. */\n readonly appName: string;\n /** The version of the currently running embedded app. */\n readonly appVersion: string;\n};\n\n/**\n * Whether Contacts app-owned operations are supported for the given device\n * model, OS version, and running app \u2014 checking the model is supported, the OS\n * meets its minimum, the app is known to Contacts, and the app meets its\n * minimum.\n *\n * Pure and dependency-light: hosts can call this directly, or read\n * {@link CONTACTS_VERSION_REQUIREMENTS} to build their own checks.\n */\nexport function isContactsSupported({\n deviceModelId,\n osVersion,\n appName,\n appVersion,\n}: ContactsSupportQuery): boolean {\n const requirement = resolveContactsVersionRequirements(deviceModelId);\n if (!requirement.supported) return false;\n if (!isVersionAtLeast(osVersion, requirement.minOsVersion)) return false;\n\n const minAppVersion = requirement.minAppVersion[appName];\n if (minAppVersion === undefined) return false;\n return isVersionAtLeast(appVersion, minAppVersion);\n}\n"],
5
+ "mappings": "AAAA,OAAS,iBAAAA,MAAqB,kCAC9B,OAAS,UAAAC,EAAQ,OAAAC,EAAK,SAAAC,MAAa,SAwC5B,MAAMC,EAAoB,WAE3BC,EAAwC,CAAE,UAAW,EAAM,EAK3DC,EAAsB,QACtBC,EAAsB,QACtBC,EAAsB,QACtBC,EAA2B,SAMpBC,EAA6D,CACxE,CAACV,EAAc,MAAM,EAAGK,EACxB,CAACL,EAAc,OAAO,EAAGK,EACzB,CAACL,EAAc,MAAM,EAAGK,EACxB,CAACL,EAAc,IAAI,EAAG,CACpB,UAAW,GACX,aAAcM,EACd,cAAe,CAAE,CAACF,CAAiB,EAAGK,CAAyB,CACjE,EACA,CAACT,EAAc,IAAI,EAAG,CACpB,UAAW,GACX,aAAcO,EACd,cAAe,CAAE,CAACH,CAAiB,EAAGK,CAAyB,CACjE,EACA,CAACT,EAAc,IAAI,EAAG,CACpB,UAAW,GACX,aAAcQ,EACd,cAAe,CAAE,CAACJ,CAAiB,EAAGK,CAAyB,CACjE,CACF,EAMO,SAASE,EACdC,EAC0B,CAC1B,OAAOF,EAA8BE,CAAa,CACpD,CAQO,SAASC,EAAiBC,EAAgBC,EAA0B,CACzE,MAAMC,EAAIb,EAAMW,CAAM,GAAKX,EAAMF,EAAOa,CAAM,CAAC,EACzCG,EAAId,EAAMY,CAAO,GAAKZ,EAAMF,EAAOc,CAAO,CAAC,EACjD,OAAIC,IAAM,MAAQC,IAAM,KAAa,GAC9Bf,EAAIc,EAAGC,CAAC,CACjB,CAsBO,SAASC,EAAoB,CAClC,cAAAN,EACA,UAAAO,EACA,QAAAC,EACA,WAAAC,CACF,EAAkC,CAChC,MAAMC,EAAcX,EAAmCC,CAAa,EAEpE,GADI,CAACU,EAAY,WACb,CAACT,EAAiBM,EAAWG,EAAY,YAAY,EAAG,MAAO,GAEnE,MAAMC,EAAgBD,EAAY,cAAcF,CAAO,EACvD,OAAIG,IAAkB,OAAkB,GACjCV,EAAiBQ,EAAYE,CAAa,CACnD",
6
+ "names": ["DeviceModelId", "coerce", "gte", "valid", "ETHEREUM_APP_NAME", "UNSUPPORTED", "MIN_OS_VERSION_STAX", "MIN_OS_VERSION_FLEX", "MIN_OS_VERSION_APEX", "MIN_ETHEREUM_APP_VERSION", "CONTACTS_VERSION_REQUIREMENTS", "resolveContactsVersionRequirements", "deviceModelId", "isVersionAtLeast", "actual", "minimum", "a", "b", "isContactsSupported", "osVersion", "appName", "appVersion", "requirement", "minAppVersion"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{DeviceModelId as t}from"@ledgerhq/device-management-kit";import{CONTACTS_VERSION_REQUIREMENTS as l,ETHEREUM_APP_NAME as p,isContactsSupported as s,isVersionAtLeast as n,resolveContactsVersionRequirements as u}from"./ContactsVersionRequirements";const d="0.0.1",c="999.0.0";function r(e){const o=u(e);if(!o.supported)throw new Error(`expected ${e} to be supported in the test fixture`);return o}function i(e){const o=e.minAppVersion[p];if(o===void 0)throw new Error("expected an Ethereum minimum app version in the fixture");return o}describe("ContactsVersionRequirements",()=>{describe("resolveContactsVersionRequirements",()=>{it("marks every device model, supported or not",()=>{for(const e of Object.values(t))expect(l[e]).toBeDefined()}),it("reports Nano models as unsupported",()=>{expect(u(t.NANO_S)).toEqual({supported:!1}),expect(u(t.NANO_X)).toEqual({supported:!1})}),it("reports touchscreen models as supported with OS and app minimums",()=>{const e=r(t.FLEX);expect(e.minOsVersion).toEqual(expect.any(String)),expect(i(e)).toEqual(expect.any(String))})}),describe("isContactsSupported",()=>{it("returns false for an unsupported device model",()=>{const e=r(t.FLEX);expect(s({deviceModelId:t.NANO_S,osVersion:c,appName:p,appVersion:i(e)})).toBe(!1)}),it("gates on the minimum app version",()=>{const e=r(t.FLEX),o=i(e),a={deviceModelId:t.FLEX,osVersion:e.minOsVersion,appName:p};expect(s({...a,appVersion:d})).toBe(!1),expect(s({...a,appVersion:o})).toBe(!0),expect(s({...a,appVersion:c})).toBe(!0)}),it("returns false for an app that is unknown to Contacts",()=>{const e=r(t.FLEX);expect(s({deviceModelId:t.FLEX,osVersion:e.minOsVersion,appName:"Bitcoin",appVersion:c})).toBe(!1)}),it("gates on the minimum OS version",()=>{const e=r(t.FLEX),o={deviceModelId:t.FLEX,appName:p,appVersion:i(e)};expect(s({...o,osVersion:d})).toBe(!1),expect(s({...o,osVersion:e.minOsVersion})).toBe(!0)})}),describe("isVersionAtLeast",()=>{it("compares versions as semver, inclusive of equality",()=>{expect(n("1.2.0","1.2.0")).toBe(!0),expect(n("1.3.0","1.2.0")).toBe(!0),expect(n("1.1.9","1.2.0")).toBe(!1)}),it("coerces non-strict version strings",()=>{expect(n("1.2","1.2.0")).toBe(!0)}),it("returns false when a version cannot be parsed",()=>{expect(n("not-a-version","1.2.0")).toBe(!1),expect(n("1.2.0","not-a-version")).toBe(!1)})})});
2
+ //# sourceMappingURL=ContactsVersionRequirements.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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 type ContactsModelSupport,\n ETHEREUM_APP_NAME,\n isContactsSupported,\n isVersionAtLeast,\n resolveContactsVersionRequirements,\n} from \"./ContactsVersionRequirements\";\n\nconst BELOW_ANY_VERSION = \"0.0.1\";\nconst ABOVE_ANY_VERSION = \"999.0.0\";\n\nfunction supportOf(modelId: DeviceModelId): ContactsModelSupport {\n const requirement = resolveContactsVersionRequirements(modelId);\n if (!requirement.supported) {\n throw new Error(`expected ${modelId} to be supported in the test fixture`);\n }\n return requirement;\n}\n\nfunction ethAppVersionOf(support: ContactsModelSupport): string {\n const version = support.minAppVersion[ETHEREUM_APP_NAME];\n if (version === undefined) {\n throw new Error(\"expected an Ethereum minimum app version in the fixture\");\n }\n return version;\n}\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 models as unsupported\", () => {\n expect(resolveContactsVersionRequirements(DeviceModelId.NANO_S)).toEqual({\n supported: false,\n });\n expect(resolveContactsVersionRequirements(DeviceModelId.NANO_X)).toEqual({\n supported: false,\n });\n });\n\n it(\"reports touchscreen models as supported with OS and app minimums\", () => {\n const flex = supportOf(DeviceModelId.FLEX);\n expect(flex.minOsVersion).toEqual(expect.any(String));\n expect(ethAppVersionOf(flex)).toEqual(expect.any(String));\n });\n });\n\n describe(\"isContactsSupported\", () => {\n it(\"returns false for an unsupported device model\", () => {\n const flex = supportOf(DeviceModelId.FLEX);\n expect(\n isContactsSupported({\n deviceModelId: DeviceModelId.NANO_S,\n osVersion: ABOVE_ANY_VERSION,\n appName: ETHEREUM_APP_NAME,\n appVersion: ethAppVersionOf(flex),\n }),\n ).toBe(false);\n });\n\n // App-version requirement.\n it(\"gates on the minimum app version\", () => {\n const flex = supportOf(DeviceModelId.FLEX);\n const minAppVersion = ethAppVersionOf(flex);\n const base = {\n deviceModelId: DeviceModelId.FLEX,\n osVersion: flex.minOsVersion,\n appName: ETHEREUM_APP_NAME,\n };\n\n expect(\n isContactsSupported({ ...base, appVersion: BELOW_ANY_VERSION }),\n ).toBe(false);\n expect(isContactsSupported({ ...base, appVersion: minAppVersion })).toBe(\n true,\n );\n expect(\n isContactsSupported({ ...base, appVersion: ABOVE_ANY_VERSION }),\n ).toBe(true);\n });\n\n it(\"returns false for an app that is unknown to Contacts\", () => {\n const flex = supportOf(DeviceModelId.FLEX);\n expect(\n isContactsSupported({\n deviceModelId: DeviceModelId.FLEX,\n osVersion: flex.minOsVersion,\n appName: \"Bitcoin\",\n appVersion: ABOVE_ANY_VERSION,\n }),\n ).toBe(false);\n });\n\n // OS-version requirement.\n it(\"gates on the minimum OS version\", () => {\n const flex = supportOf(DeviceModelId.FLEX);\n const base = {\n deviceModelId: DeviceModelId.FLEX,\n appName: ETHEREUM_APP_NAME,\n appVersion: ethAppVersionOf(flex),\n };\n\n expect(\n isContactsSupported({ ...base, osVersion: BELOW_ANY_VERSION }),\n ).toBe(false);\n expect(\n isContactsSupported({ ...base, osVersion: flex.minOsVersion }),\n ).toBe(true);\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});\n"],
5
+ "mappings": "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",
6
+ "names": ["DeviceModelId", "CONTACTS_VERSION_REQUIREMENTS", "ETHEREUM_APP_NAME", "isContactsSupported", "isVersionAtLeast", "resolveContactsVersionRequirements", "BELOW_ANY_VERSION", "ABOVE_ANY_VERSION", "supportOf", "modelId", "requirement", "ethAppVersionOf", "support", "version", "flex", "minAppVersion", "base"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{DeviceSessionStateType as o}from"@ledgerhq/device-management-kit";const p="0.0.0";class r{resolve(e,s){return e.sessionStateType===o.Connected?{isCompatible:!1,version:p}:e.currentApp?.name?{isCompatible:!0,version:s.version}:{isCompatible:!1,version:p}}}export{r as ContactsApplicationResolver};
2
+ //# sourceMappingURL=ContactsApplicationResolver.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/internal/app-binder/ContactsApplicationResolver.ts"],
4
+ "sourcesContent": ["import {\n type AppConfig,\n type ApplicationResolver,\n type DeviceSessionState,\n DeviceSessionStateType,\n type ResolvedApp,\n} from \"@ledgerhq/device-management-kit\";\n\nconst DEFAULT_VERSION = \"0.0.0\";\n\n/**\n * Resolves the running-app version for Contacts, for use with DMK's\n * `ApplicationChecker`. Which app names and versions are actually required is\n * decided by the static Contacts requirements table, so this resolver only\n * surfaces the authoritative app version (from `GetAppConfiguration`) and\n * rejects sessions where no app is running.\n */\nexport class ContactsApplicationResolver implements ApplicationResolver {\n resolve(deviceState: DeviceSessionState, appConfig: AppConfig): ResolvedApp {\n if (deviceState.sessionStateType === DeviceSessionStateType.Connected) {\n return { isCompatible: false, version: DEFAULT_VERSION };\n }\n\n const appName = deviceState.currentApp?.name;\n if (!appName) {\n return { isCompatible: false, version: DEFAULT_VERSION };\n }\n\n return { isCompatible: true, version: appConfig.version };\n }\n}\n"],
5
+ "mappings": "AAAA,OAIE,0BAAAA,MAEK,kCAEP,MAAMC,EAAkB,QASjB,MAAMC,CAA2D,CACtE,QAAQC,EAAiCC,EAAmC,CAC1E,OAAID,EAAY,mBAAqBH,EAAuB,UACnD,CAAE,aAAc,GAAO,QAASC,CAAgB,EAGzCE,EAAY,YAAY,KAKjC,CAAE,aAAc,GAAM,QAASC,EAAU,OAAQ,EAH/C,CAAE,aAAc,GAAO,QAASH,CAAgB,CAI3D,CACF",
6
+ "names": ["DeviceSessionStateType", "DEFAULT_VERSION", "ContactsApplicationResolver", "deviceState", "appConfig"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{ApplicationChecker as p}from"@ledgerhq/device-management-kit";import{isVersionAtLeast as a,resolveContactsVersionRequirements as f}from"../../api/model/ContactsVersionRequirements";import{ContactsApplicationResolver as c}from"./ContactsApplicationResolver";function A(o,s){const e=o.getDeviceSessionState(),n=f(e.deviceModelId);if(!n.supported)return!1;const i="currentApp"in e?e.currentApp?.name:void 0;if(i===void 0)return!1;const r=n.minAppVersion[i];if(r===void 0||!new p(e,s,new c).withMinVersionInclusive(r).check())return!1;const t="firmwareVersion"in e?e.firmwareVersion?.os:void 0;return t===void 0?!1:a(t,n.minOsVersion)}export{A as isContactsSupportedForSession};
2
+ //# sourceMappingURL=isContactsSupportedForSession.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/internal/app-binder/isContactsSupportedForSession.ts"],
4
+ "sourcesContent": ["import {\n type AppConfig,\n ApplicationChecker,\n type InternalApi,\n} from \"@ledgerhq/device-management-kit\";\n\nimport {\n isVersionAtLeast,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n\nimport { ContactsApplicationResolver } from \"./ContactsApplicationResolver\";\n\n/**\n * Session-aware Contacts support check, for internal consumption by Contacts\n * `DeviceAction`s. Reads the current device session and resolves it against the\n * static requirements table:\n * - the device model must be supported;\n * - the running app must be known to Contacts and meet its minimum version \u2014\n * validated with DMK's {@link ApplicationChecker} so the version comes from\n * the authoritative `GetAppConfiguration` result;\n * - the device OS must meet its minimum version (a dimension `ApplicationChecker`\n * does not cover, so it is checked separately here).\n *\n * @param internalApi - the DeviceAction's internal API for the current session.\n * @param appConfig - the running app's configuration (must include its version).\n */\nexport function isContactsSupportedForSession(\n internalApi: InternalApi,\n appConfig: AppConfig,\n): boolean {\n const deviceState = internalApi.getDeviceSessionState();\n const requirement = resolveContactsVersionRequirements(\n deviceState.deviceModelId,\n );\n if (!requirement.supported) return false;\n\n const appName =\n \"currentApp\" in deviceState ? deviceState.currentApp?.name : undefined;\n if (appName === undefined) return false;\n\n const minAppVersion = requirement.minAppVersion[appName];\n if (minAppVersion === undefined) return false;\n\n const appCompatible = new ApplicationChecker(\n deviceState,\n appConfig,\n new ContactsApplicationResolver(),\n )\n .withMinVersionInclusive(minAppVersion)\n .check();\n if (!appCompatible) return false;\n\n const osVersion =\n \"firmwareVersion\" in deviceState\n ? deviceState.firmwareVersion?.os\n : undefined;\n if (osVersion === undefined) return false;\n\n return isVersionAtLeast(osVersion, requirement.minOsVersion);\n}\n"],
5
+ "mappings": "AAAA,OAEE,sBAAAA,MAEK,kCAEP,OACE,oBAAAC,EACA,sCAAAC,MACK,yCAEP,OAAS,+BAAAC,MAAmC,gCAgBrC,SAASC,EACdC,EACAC,EACS,CACT,MAAMC,EAAcF,EAAY,sBAAsB,EAChDG,EAAcN,EAClBK,EAAY,aACd,EACA,GAAI,CAACC,EAAY,UAAW,MAAO,GAEnC,MAAMC,EACJ,eAAgBF,EAAcA,EAAY,YAAY,KAAO,OAC/D,GAAIE,IAAY,OAAW,MAAO,GAElC,MAAMC,EAAgBF,EAAY,cAAcC,CAAO,EAUvD,GATIC,IAAkB,QASlB,CAPkB,IAAIV,EACxBO,EACAD,EACA,IAAIH,CACN,EACG,wBAAwBO,CAAa,EACrC,MAAM,EACW,MAAO,GAE3B,MAAMC,EACJ,oBAAqBJ,EACjBA,EAAY,iBAAiB,GAC7B,OACN,OAAII,IAAc,OAAkB,GAE7BV,EAAiBU,EAAWH,EAAY,YAAY,CAC7D",
6
+ "names": ["ApplicationChecker", "isVersionAtLeast", "resolveContactsVersionRequirements", "ContactsApplicationResolver", "isContactsSupportedForSession", "internalApi", "appConfig", "deviceState", "requirement", "appName", "minAppVersion", "osVersion"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{DeviceModelId as r,DeviceSessionStateType as a,DeviceStatus as c}from"@ledgerhq/device-management-kit";import{ETHEREUM_APP_NAME as u,resolveContactsVersionRequirements as f}from"../../api/model/ContactsVersionRequirements";import{isContactsSupportedForSession as t}from"./isContactsSupportedForSession";const d=(()=>{const e=f(r.FLEX);if(!e.supported)throw new Error("Flex must be supported");return e})(),S=d.minOsVersion,s=(()=>{const e=d.minAppVersion[u];if(e===void 0)throw new Error("Ethereum min version required");return e})(),p="0.0.1";function i({appName:e=u,osVersion:l=S,modelId:m=r.FLEX}={}){return{sessionStateType:a.ReadyWithoutSecureChannel,deviceStatus:c.CONNECTED,installedApps:[],currentApp:{name:e,version:"0.0.0"},deviceModelId:m,firmwareVersion:{mcu:"1.0.0",bootloader:"1.0.0",os:l},isSecureConnectionAllowed:!1}}function n(e){return{getDeviceSessionState:()=>e}}function o(e){return{version:e}}describe("isContactsSupportedForSession",()=>{it("returns true when model, app version and OS version all meet the minimums",()=>{const e=n(i());expect(t(e,o(s))).toBe(!0)}),it("returns false on an unsupported device model",()=>{const e=n(i({modelId:r.NANO_X}));expect(t(e,o(s))).toBe(!1)}),it("returns false when the app version is below the minimum",()=>{const e=n(i());expect(t(e,o(p))).toBe(!1)}),it("returns false when the running app is unknown to Contacts",()=>{const e=n(i({appName:"Bitcoin"}));expect(t(e,o(s))).toBe(!1)}),it("returns false when the OS version is below the minimum",()=>{const e=n(i({osVersion:p}));expect(t(e,o(s))).toBe(!1)}),it("returns false when the device session has no running app",()=>{const e=n({sessionStateType:a.Connected,deviceStatus:c.CONNECTED,deviceModelId:r.FLEX});expect(t(e,o(s))).toBe(!1)})});
2
+ //# sourceMappingURL=isContactsSupportedForSession.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/internal/app-binder/isContactsSupportedForSession.test.ts"],
4
+ "sourcesContent": ["import {\n type AppConfig,\n DeviceModelId,\n DeviceSessionStateType,\n DeviceStatus,\n type InternalApi,\n} from \"@ledgerhq/device-management-kit\";\n\nimport {\n ETHEREUM_APP_NAME,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n\nimport { isContactsSupportedForSession } from \"./isContactsSupportedForSession\";\n\nconst flexSupport = (() => {\n const requirement = resolveContactsVersionRequirements(DeviceModelId.FLEX);\n if (!requirement.supported) throw new Error(\"Flex must be supported\");\n return requirement;\n})();\n\nconst MIN_OS_VERSION = flexSupport.minOsVersion;\nconst MIN_APP_VERSION = (() => {\n const version = flexSupport.minAppVersion[ETHEREUM_APP_NAME];\n if (version === undefined) throw new Error(\"Ethereum min version required\");\n return version;\n})();\nconst BELOW_ANY_VERSION = \"0.0.1\";\n\ntype ReadyStateOptions = {\n appName?: string;\n osVersion?: string;\n modelId?: DeviceModelId;\n};\n\nfunction createReadyState({\n appName = ETHEREUM_APP_NAME,\n osVersion = MIN_OS_VERSION,\n modelId = DeviceModelId.FLEX,\n}: ReadyStateOptions = {}) {\n return {\n sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel,\n deviceStatus: DeviceStatus.CONNECTED,\n installedApps: [],\n currentApp: { name: appName, version: \"0.0.0\" },\n deviceModelId: modelId,\n firmwareVersion: { mcu: \"1.0.0\", bootloader: \"1.0.0\", os: osVersion },\n isSecureConnectionAllowed: false,\n };\n}\n\nfunction createInternalApi(deviceState: object): InternalApi {\n return {\n getDeviceSessionState: () => deviceState,\n } as unknown as InternalApi;\n}\n\nfunction appConfig(version: string): AppConfig {\n return { version };\n}\n\ndescribe(\"isContactsSupportedForSession\", () => {\n it(\"returns true when model, app version and OS version all meet the minimums\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n true,\n );\n });\n\n it(\"returns false on an unsupported device model\", () => {\n const api = createInternalApi(\n createReadyState({ modelId: DeviceModelId.NANO_X }),\n );\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n false,\n );\n });\n\n // App-version requirement.\n it(\"returns false when the app version is below the minimum\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsSupportedForSession(api, appConfig(BELOW_ANY_VERSION)),\n ).toBe(false);\n });\n\n it(\"returns false when the running app is unknown to Contacts\", () => {\n const api = createInternalApi(createReadyState({ appName: \"Bitcoin\" }));\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n false,\n );\n });\n\n // OS-version requirement.\n it(\"returns false when the OS version is below the minimum\", () => {\n const api = createInternalApi(\n createReadyState({ osVersion: BELOW_ANY_VERSION }),\n );\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n false,\n );\n });\n\n it(\"returns false when the device session has no running app\", () => {\n const api = createInternalApi({\n sessionStateType: DeviceSessionStateType.Connected,\n deviceStatus: DeviceStatus.CONNECTED,\n deviceModelId: DeviceModelId.FLEX,\n });\n expect(isContactsSupportedForSession(api, appConfig(MIN_APP_VERSION))).toBe(\n false,\n );\n });\n});\n"],
5
+ "mappings": "AAAA,OAEE,iBAAAA,EACA,0BAAAC,EACA,gBAAAC,MAEK,kCAEP,OACE,qBAAAC,EACA,sCAAAC,MACK,yCAEP,OAAS,iCAAAC,MAAqC,kCAE9C,MAAMC,GAAe,IAAM,CACzB,MAAMC,EAAcH,EAAmCJ,EAAc,IAAI,EACzE,GAAI,CAACO,EAAY,UAAW,MAAM,IAAI,MAAM,wBAAwB,EACpE,OAAOA,CACT,GAAG,EAEGC,EAAiBF,EAAY,aAC7BG,GAAmB,IAAM,CAC7B,MAAMC,EAAUJ,EAAY,cAAcH,CAAiB,EAC3D,GAAIO,IAAY,OAAW,MAAM,IAAI,MAAM,+BAA+B,EAC1E,OAAOA,CACT,GAAG,EACGC,EAAoB,QAQ1B,SAASC,EAAiB,CACxB,QAAAC,EAAUV,EACV,UAAAW,EAAYN,EACZ,QAAAO,EAAUf,EAAc,IAC1B,EAAuB,CAAC,EAAG,CACzB,MAAO,CACL,iBAAkBC,EAAuB,0BACzC,aAAcC,EAAa,UAC3B,cAAe,CAAC,EAChB,WAAY,CAAE,KAAMW,EAAS,QAAS,OAAQ,EAC9C,cAAeE,EACf,gBAAiB,CAAE,IAAK,QAAS,WAAY,QAAS,GAAID,CAAU,EACpE,0BAA2B,EAC7B,CACF,CAEA,SAASE,EAAkBC,EAAkC,CAC3D,MAAO,CACL,sBAAuB,IAAMA,CAC/B,CACF,CAEA,SAASC,EAAUR,EAA4B,CAC7C,MAAO,CAAE,QAAAA,CAAQ,CACnB,CAEA,SAAS,gCAAiC,IAAM,CAC9C,GAAG,4EAA6E,IAAM,CACpF,MAAMS,EAAMH,EAAkBJ,EAAiB,CAAC,EAChD,OAAOP,EAA8Bc,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,EAED,GAAG,+CAAgD,IAAM,CACvD,MAAMU,EAAMH,EACVJ,EAAiB,CAAE,QAASZ,EAAc,MAAO,CAAC,CACpD,EACA,OAAOK,EAA8Bc,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,EAGD,GAAG,0DAA2D,IAAM,CAClE,MAAMU,EAAMH,EAAkBJ,EAAiB,CAAC,EAChD,OACEP,EAA8Bc,EAAKD,EAAUP,CAAiB,CAAC,CACjE,EAAE,KAAK,EAAK,CACd,CAAC,EAED,GAAG,4DAA6D,IAAM,CACpE,MAAMQ,EAAMH,EAAkBJ,EAAiB,CAAE,QAAS,SAAU,CAAC,CAAC,EACtE,OAAOP,EAA8Bc,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,EAGD,GAAG,yDAA0D,IAAM,CACjE,MAAMU,EAAMH,EACVJ,EAAiB,CAAE,UAAWD,CAAkB,CAAC,CACnD,EACA,OAAON,EAA8Bc,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,EAED,GAAG,2DAA4D,IAAM,CACnE,MAAMU,EAAMH,EAAkB,CAC5B,iBAAkBf,EAAuB,UACzC,aAAcC,EAAa,UAC3B,cAAeF,EAAc,IAC/B,CAAC,EACD,OAAOK,EAA8Bc,EAAKD,EAAUT,CAAe,CAAC,CAAC,EAAE,KACrE,EACF,CACF,CAAC,CACH,CAAC",
6
+ "names": ["DeviceModelId", "DeviceSessionStateType", "DeviceStatus", "ETHEREUM_APP_NAME", "resolveContactsVersionRequirements", "isContactsSupportedForSession", "flexSupport", "requirement", "MIN_OS_VERSION", "MIN_APP_VERSION", "version", "BELOW_ANY_VERSION", "createReadyState", "appName", "osVersion", "modelId", "createInternalApi", "deviceState", "appConfig", "api"]
7
+ }
@@ -3,6 +3,7 @@
3
3
  "inversify": "catalog:",
4
4
  "purify-ts": "catalog:",
5
5
  "reflect-metadata": "catalog:",
6
+ "semver": "catalog:",
6
7
  "xstate": "catalog:"
7
8
  },
8
9
  "devDependencies": {
@@ -12,6 +13,7 @@
12
13
  "@ledgerhq/prettier-config-dsdk": "workspace:*",
13
14
  "@ledgerhq/tsconfig-dsdk": "workspace:*",
14
15
  "@ledgerhq/vitest-config-dmk": "workspace:*",
16
+ "@types/semver": "catalog:",
15
17
  "rxjs": "catalog:",
16
18
  "ts-node": "catalog:"
17
19
  },
@@ -55,5 +57,5 @@
55
57
  "watch:builds": "pnpm ldmk-tool watch --entryPoints src/index.ts,src/**/*.ts --tsconfig tsconfig.prod.json",
56
58
  "watch:types": "concurrently \"tsc --watch -p tsconfig.prod.json\" \"tsc-alias --watch -p tsconfig.prod.json\""
57
59
  },
58
- "version": "0.0.0-develop-20260813001639"
60
+ "version": "0.0.0-develop-20260815000734"
59
61
  }
@@ -1,3 +1,4 @@
1
1
  export { type ContactsManager } from "./ContactsManager";
2
2
  export { ContactsManagerBuilder } from "./ContactsManagerBuilder";
3
+ export { CONTACTS_VERSION_REQUIREMENTS, type ContactsModelRequirement, type ContactsModelSupport, type ContactsModelUnsupported, type ContactsSupportQuery, type ContactsVersionRequirements, ETHEREUM_APP_NAME, isContactsSupported, isVersionAtLeast, resolveContactsVersionRequirements, } from "./model/ContactsVersionRequirements";
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EACL,6BAA6B,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,kCAAkC,GACnC,MAAM,wCAAwC,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { DeviceModelId } from "@ledgerhq/device-management-kit";
2
+ /**
3
+ * Version requirements for a device model on which Contacts (Address Book)
4
+ * operations are supported.
5
+ *
6
+ * Contacts has two independent version axes:
7
+ * - `minOsVersion` gates OS-owned operations (e.g. renaming a contact from the
8
+ * device dashboard), which are served by the device OS.
9
+ * - `minAppVersion` gates app-owned operations (registering / editing external
10
+ * addresses and Ledger accounts), which are served by the embedded app. It is
11
+ * keyed by app name because the required version differs per app; v1 ships
12
+ * Ethereum only.
13
+ */
14
+ export type ContactsModelSupport = {
15
+ readonly supported: true;
16
+ readonly minOsVersion: string;
17
+ readonly minAppVersion: Readonly<Record<string, string>>;
18
+ };
19
+ /** Marks a device model on which Contacts is not supported at all. */
20
+ export type ContactsModelUnsupported = {
21
+ readonly supported: false;
22
+ };
23
+ export type ContactsModelRequirement = ContactsModelSupport | ContactsModelUnsupported;
24
+ /**
25
+ * The full, static Contacts version-requirement table, keyed by device model.
26
+ * Serializable plain data with no runtime dependencies, so hosts (e.g. Ledger
27
+ * Wallet) can import and consume it directly when composing their own
28
+ * app-readiness checks, without duplicating the values.
29
+ */
30
+ export type ContactsVersionRequirements = Readonly<Record<DeviceModelId, ContactsModelRequirement>>;
31
+ /** The name of the Ethereum embedded app (the only app supported in v1). */
32
+ export declare const ETHEREUM_APP_NAME = "Ethereum";
33
+ /**
34
+ * Contacts APDUs are supported only on the touchscreen device models (Stax,
35
+ * Flex, Apex). The Nano models do not support Contacts at all.
36
+ */
37
+ export declare const CONTACTS_VERSION_REQUIREMENTS: ContactsVersionRequirements;
38
+ /**
39
+ * Resolve the Contacts version requirements for a device model — i.e. the
40
+ * minimum supported OS and app versions, or that the model is unsupported.
41
+ */
42
+ export declare function resolveContactsVersionRequirements(deviceModelId: DeviceModelId): ContactsModelRequirement;
43
+ /**
44
+ * Whether `actual` is greater than or equal to `minimum`, comparing as semver.
45
+ * Tolerant of non-strict version strings (coerces where possible) and returns
46
+ * `false` (i.e. requirement not met) when either version cannot be parsed, so
47
+ * an unknown version never passes a check.
48
+ */
49
+ export declare function isVersionAtLeast(actual: string, minimum: string): boolean;
50
+ /** Inputs for {@link isContactsSupported}. */
51
+ export type ContactsSupportQuery = {
52
+ readonly deviceModelId: DeviceModelId;
53
+ /** The device OS (firmware) version. */
54
+ readonly osVersion: string;
55
+ /** The name of the currently running embedded app. */
56
+ readonly appName: string;
57
+ /** The version of the currently running embedded app. */
58
+ readonly appVersion: string;
59
+ };
60
+ /**
61
+ * Whether Contacts app-owned operations are supported for the given device
62
+ * model, OS version, and running app — checking the model is supported, the OS
63
+ * meets its minimum, the app is known to Contacts, and the app meets its
64
+ * minimum.
65
+ *
66
+ * Pure and dependency-light: hosts can call this directly, or read
67
+ * {@link CONTACTS_VERSION_REQUIREMENTS} to build their own checks.
68
+ */
69
+ export declare function isContactsSupported({ deviceModelId, osVersion, appName, appVersion, }: ContactsSupportQuery): boolean;
70
+ //# sourceMappingURL=ContactsVersionRequirements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContactsVersionRequirements.d.ts","sourceRoot":"","sources":["../../../../src/api/model/ContactsVersionRequirements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGhE;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC1D,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAChC,oBAAoB,GACpB,wBAAwB,CAAC;AAE7B;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAChD,MAAM,CAAC,aAAa,EAAE,wBAAwB,CAAC,CAChD,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,iBAAiB,aAAa,CAAC;AAY5C;;;GAGG;AACH,eAAO,MAAM,6BAA6B,EAAE,2BAmB3C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,kCAAkC,CAChD,aAAa,EAAE,aAAa,GAC3B,wBAAwB,CAE1B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAKzE;AAED,8CAA8C;AAC9C,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,wCAAwC;IACxC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,aAAa,EACb,SAAS,EACT,OAAO,EACP,UAAU,GACX,EAAE,oBAAoB,GAAG,OAAO,CAQhC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ContactsVersionRequirements.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContactsVersionRequirements.test.d.ts","sourceRoot":"","sources":["../../../../src/api/model/ContactsVersionRequirements.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import { type AppConfig, type ApplicationResolver, type DeviceSessionState, type ResolvedApp } from "@ledgerhq/device-management-kit";
2
+ /**
3
+ * Resolves the running-app version for Contacts, for use with DMK's
4
+ * `ApplicationChecker`. Which app names and versions are actually required is
5
+ * decided by the static Contacts requirements table, so this resolver only
6
+ * surfaces the authoritative app version (from `GetAppConfiguration`) and
7
+ * rejects sessions where no app is running.
8
+ */
9
+ export declare class ContactsApplicationResolver implements ApplicationResolver {
10
+ resolve(deviceState: DeviceSessionState, appConfig: AppConfig): ResolvedApp;
11
+ }
12
+ //# sourceMappingURL=ContactsApplicationResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContactsApplicationResolver.d.ts","sourceRoot":"","sources":["../../../../src/internal/app-binder/ContactsApplicationResolver.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAEvB,KAAK,WAAW,EACjB,MAAM,iCAAiC,CAAC;AAIzC;;;;;;GAMG;AACH,qBAAa,2BAA4B,YAAW,mBAAmB;IACrE,OAAO,CAAC,WAAW,EAAE,kBAAkB,EAAE,SAAS,EAAE,SAAS,GAAG,WAAW;CAY5E"}
@@ -0,0 +1,17 @@
1
+ import { type AppConfig, type InternalApi } from "@ledgerhq/device-management-kit";
2
+ /**
3
+ * Session-aware Contacts support check, for internal consumption by Contacts
4
+ * `DeviceAction`s. Reads the current device session and resolves it against the
5
+ * static requirements table:
6
+ * - the device model must be supported;
7
+ * - the running app must be known to Contacts and meet its minimum version —
8
+ * validated with DMK's {@link ApplicationChecker} so the version comes from
9
+ * the authoritative `GetAppConfiguration` result;
10
+ * - the device OS must meet its minimum version (a dimension `ApplicationChecker`
11
+ * does not cover, so it is checked separately here).
12
+ *
13
+ * @param internalApi - the DeviceAction's internal API for the current session.
14
+ * @param appConfig - the running app's configuration (must include its version).
15
+ */
16
+ export declare function isContactsSupportedForSession(internalApi: InternalApi, appConfig: AppConfig): boolean;
17
+ //# sourceMappingURL=isContactsSupportedForSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isContactsSupportedForSession.d.ts","sourceRoot":"","sources":["../../../../src/internal/app-binder/isContactsSupportedForSession.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EAEd,KAAK,WAAW,EACjB,MAAM,iCAAiC,CAAC;AASzC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,SAAS,GACnB,OAAO,CA8BT"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=isContactsSupportedForSession.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isContactsSupportedForSession.test.d.ts","sourceRoot":"","sources":["../../../../src/internal/app-binder/isContactsSupportedForSession.test.ts"],"names":[],"mappings":""}