@ceralive/modem-control 1.0.0 → 1.2.0

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 (480) hide show
  1. package/README.md +341 -0
  2. package/dist/backend/at-lease.d.ts +59 -0
  3. package/dist/backend/at-lease.js +118 -0
  4. package/dist/backend/cell-info.d.ts +46 -0
  5. package/dist/backend/cell-info.js +124 -0
  6. package/dist/backend/constants.d.ts +24 -0
  7. package/{src/backend/constants.ts → dist/backend/constants.js} +4 -9
  8. package/dist/backend/device-classifier.d.ts +46 -0
  9. package/dist/backend/device-classifier.js +258 -0
  10. package/dist/backend/enrichment.d.ts +28 -0
  11. package/dist/backend/enrichment.js +59 -0
  12. package/dist/backend/features.d.ts +61 -0
  13. package/dist/backend/features.js +109 -0
  14. package/dist/backend/identity-ladder.d.ts +57 -0
  15. package/dist/backend/identity-ladder.js +158 -0
  16. package/dist/backend/identity-registry.d.ts +51 -0
  17. package/dist/backend/identity-registry.js +101 -0
  18. package/dist/backend/index.d.ts +30 -0
  19. package/dist/backend/index.js +35 -0
  20. package/dist/backend/lifecycle-interlock.d.ts +25 -0
  21. package/dist/backend/lifecycle-interlock.js +18 -0
  22. package/dist/backend/managed-objects.d.ts +39 -0
  23. package/dist/backend/managed-objects.js +82 -0
  24. package/dist/backend/mapping.d.ts +11 -0
  25. package/dist/backend/mapping.js +148 -0
  26. package/dist/backend/mm-backend.d.ts +44 -0
  27. package/dist/backend/mm-backend.js +154 -0
  28. package/dist/backend/mm-location.d.ts +21 -0
  29. package/dist/backend/mm-location.js +237 -0
  30. package/dist/backend/mm-mutations.d.ts +42 -0
  31. package/dist/backend/mm-mutations.js +259 -0
  32. package/dist/backend/modem-actor.d.ts +41 -0
  33. package/dist/backend/modem-actor.js +78 -0
  34. package/dist/backend/nm-auto-apn.d.ts +54 -0
  35. package/dist/backend/nm-auto-apn.js +124 -0
  36. package/dist/backend/nm-gsm-fields.d.ts +23 -0
  37. package/dist/backend/nm-gsm-fields.js +107 -0
  38. package/dist/backend/nmcli-nm-port.d.ts +28 -0
  39. package/dist/backend/nmcli-nm-port.js +173 -0
  40. package/dist/backend/nmcli-runner.d.ts +24 -0
  41. package/dist/backend/nmcli-runner.js +35 -0
  42. package/dist/backend/observer.d.ts +37 -0
  43. package/dist/backend/observer.js +219 -0
  44. package/dist/backend/power-contract.d.ts +49 -0
  45. package/dist/backend/power-contract.js +34 -0
  46. package/dist/backend/recovery-attribution.d.ts +32 -0
  47. package/dist/backend/recovery-attribution.js +57 -0
  48. package/dist/backend/recovery-budget.d.ts +45 -0
  49. package/dist/backend/recovery-budget.js +44 -0
  50. package/dist/backend/recovery-ladder.d.ts +94 -0
  51. package/dist/backend/recovery-ladder.js +116 -0
  52. package/dist/backend/router-ethernet.d.ts +19 -0
  53. package/dist/backend/router-ethernet.js +66 -0
  54. package/dist/backend/row-store.d.ts +13 -0
  55. package/dist/backend/row-store.js +82 -0
  56. package/dist/backend/signal-setup.d.ts +30 -0
  57. package/dist/backend/signal-setup.js +92 -0
  58. package/dist/backend/sim-unlock.d.ts +13 -0
  59. package/dist/backend/sim-unlock.js +153 -0
  60. package/dist/backend/transition-preconditions.d.ts +101 -0
  61. package/dist/backend/transition-preconditions.js +132 -0
  62. package/dist/backend/usage/accounting.d.ts +39 -0
  63. package/dist/backend/usage/accounting.js +73 -0
  64. package/dist/backend/usage/billing-cycle.d.ts +11 -0
  65. package/dist/backend/usage/billing-cycle.js +39 -0
  66. package/dist/backend/usage/boot-id.d.ts +6 -0
  67. package/{src/backend/usage/boot-id.ts → dist/backend/usage/boot-id.js} +7 -7
  68. package/dist/backend/usage/index.d.ts +8 -0
  69. package/dist/backend/usage/index.js +11 -0
  70. package/dist/backend/usage/policy-store.d.ts +50 -0
  71. package/dist/backend/usage/policy-store.js +161 -0
  72. package/dist/backend/usage/policy-write.d.ts +65 -0
  73. package/dist/backend/usage/policy-write.js +112 -0
  74. package/dist/backend/usage/proc-net-dev.d.ts +18 -0
  75. package/{src/backend/usage/proc-net-dev.ts → dist/backend/usage/proc-net-dev.js} +39 -47
  76. package/dist/backend/usage/sampler.d.ts +75 -0
  77. package/dist/backend/usage/sampler.js +211 -0
  78. package/dist/backend/usage/store.d.ts +38 -0
  79. package/dist/backend/usage/store.js +126 -0
  80. package/dist/backend/usb-device-snapshot.d.ts +31 -0
  81. package/dist/backend/usb-device-snapshot.js +1 -0
  82. package/dist/backend/usb-enumerator.d.ts +21 -0
  83. package/dist/backend/usb-enumerator.js +153 -0
  84. package/dist/backend/usb-mode-transition.d.ts +29 -0
  85. package/dist/backend/usb-mode-transition.js +216 -0
  86. package/dist/band/band-names.d.ts +42 -0
  87. package/dist/band/band-names.js +150 -0
  88. package/dist/band/certification.d.ts +84 -0
  89. package/dist/band/certification.js +127 -0
  90. package/dist/band/certified-bands.json +4 -0
  91. package/dist/band/index.d.ts +2 -0
  92. package/dist/band/index.js +8 -0
  93. package/dist/capability/detect.d.ts +52 -0
  94. package/dist/capability/detect.js +86 -0
  95. package/dist/capability/five-g-preference.d.ts +104 -0
  96. package/dist/capability/five-g-preference.js +171 -0
  97. package/dist/capability/index.d.ts +3 -0
  98. package/dist/capability/index.js +10 -0
  99. package/dist/capability/support-claim.d.ts +39 -0
  100. package/dist/capability/support-claim.js +81 -0
  101. package/dist/domain/brand.d.ts +10 -0
  102. package/dist/domain/brand.js +21 -0
  103. package/dist/domain/errors.d.ts +32 -0
  104. package/dist/domain/errors.js +48 -0
  105. package/dist/domain/generation.d.ts +8 -0
  106. package/dist/domain/generation.js +12 -0
  107. package/dist/domain/guards.d.ts +6 -0
  108. package/dist/domain/guards.js +127 -0
  109. package/dist/domain/identity.d.ts +109 -0
  110. package/dist/domain/identity.js +86 -0
  111. package/dist/domain/index.d.ts +14 -0
  112. package/dist/domain/index.js +18 -0
  113. package/dist/domain/mm-enums.d.ts +12 -0
  114. package/dist/domain/mm-enums.js +139 -0
  115. package/dist/domain/modem-presentation.d.ts +10 -0
  116. package/dist/domain/modem-presentation.js +39 -0
  117. package/dist/domain/observation.d.ts +39 -0
  118. package/dist/domain/observation.js +4 -0
  119. package/dist/domain/operation.d.ts +118 -0
  120. package/dist/domain/operation.js +85 -0
  121. package/dist/domain/physical-identity.d.ts +43 -0
  122. package/dist/domain/physical-identity.js +113 -0
  123. package/{src/domain/policy.ts → dist/domain/policy.d.ts} +34 -69
  124. package/dist/domain/policy.js +38 -0
  125. package/dist/domain/shadow-divergence.d.ts +27 -0
  126. package/dist/domain/shadow-divergence.js +70 -0
  127. package/dist/domain/snapshot.d.ts +53 -0
  128. package/dist/domain/snapshot.js +75 -0
  129. package/{src/domain/state.ts → dist/domain/state.d.ts} +23 -122
  130. package/dist/domain/state.js +38 -0
  131. package/dist/fcc/coverage.d.ts +63 -0
  132. package/dist/fcc/coverage.js +102 -0
  133. package/dist/fcc/index.d.ts +3 -0
  134. package/dist/fcc/index.js +12 -0
  135. package/dist/fcc/policy-store.d.ts +40 -0
  136. package/dist/fcc/policy-store.js +146 -0
  137. package/dist/fcc/policy-write.d.ts +32 -0
  138. package/dist/fcc/policy-write.js +37 -0
  139. package/dist/hardware/hilink-protocol.d.ts +39 -0
  140. package/dist/hardware/hilink-protocol.js +58 -0
  141. package/dist/hardware/index.d.ts +3 -0
  142. package/dist/hardware/index.js +15 -0
  143. package/dist/hardware/router-parsers.d.ts +89 -0
  144. package/dist/hardware/router-parsers.js +234 -0
  145. package/dist/index.d.ts +20 -0
  146. package/dist/index.js +27 -0
  147. package/dist/journal/codec.d.ts +44 -0
  148. package/dist/journal/codec.js +198 -0
  149. package/dist/journal/engine.d.ts +28 -0
  150. package/dist/journal/engine.js +68 -0
  151. package/dist/journal/entry.d.ts +74 -0
  152. package/dist/journal/entry.js +56 -0
  153. package/dist/journal/index.d.ts +6 -0
  154. package/dist/journal/index.js +6 -0
  155. package/dist/journal/legacy-ceraui.d.ts +73 -0
  156. package/dist/journal/legacy-ceraui.js +227 -0
  157. package/dist/journal/recovery.d.ts +58 -0
  158. package/dist/journal/recovery.js +117 -0
  159. package/dist/journal/store.d.ts +55 -0
  160. package/dist/journal/store.js +150 -0
  161. package/dist/location/fix-state.d.ts +53 -0
  162. package/dist/location/fix-state.js +75 -0
  163. package/dist/location/index.d.ts +2 -0
  164. package/dist/location/index.js +8 -0
  165. package/dist/location/nmea.d.ts +10 -0
  166. package/dist/location/nmea.js +89 -0
  167. package/dist/observations/envelope.d.ts +63 -0
  168. package/dist/observations/envelope.js +77 -0
  169. package/dist/observations/freshness.d.ts +28 -0
  170. package/dist/observations/freshness.js +76 -0
  171. package/dist/observations/index.d.ts +13 -0
  172. package/dist/observations/index.js +24 -0
  173. package/dist/observations/metric.d.ts +61 -0
  174. package/dist/observations/metric.js +73 -0
  175. package/dist/observations/model.d.ts +80 -0
  176. package/dist/observations/model.js +11 -0
  177. package/dist/observations/provenance.d.ts +94 -0
  178. package/dist/observations/provenance.js +67 -0
  179. package/dist/observations/raw.d.ts +42 -0
  180. package/dist/observations/raw.js +146 -0
  181. package/dist/observations/reading.d.ts +51 -0
  182. package/dist/observations/reading.js +65 -0
  183. package/dist/observations/sources/hilink.d.ts +10 -0
  184. package/dist/observations/sources/hilink.js +84 -0
  185. package/dist/observations/sources/modemmanager.d.ts +18 -0
  186. package/dist/observations/sources/modemmanager.js +239 -0
  187. package/dist/observations/sources/router-shared.d.ts +29 -0
  188. package/dist/observations/sources/router-shared.js +68 -0
  189. package/dist/observations/sources/ufi.d.ts +10 -0
  190. package/dist/observations/sources/ufi.js +111 -0
  191. package/dist/observations/sources/zte.d.ts +7 -0
  192. package/dist/observations/sources/zte.js +71 -0
  193. package/dist/observations/state-separation.d.ts +64 -0
  194. package/dist/observations/state-separation.js +52 -0
  195. package/dist/operation-ids.d.ts +2 -0
  196. package/dist/operation-ids.js +26 -0
  197. package/dist/operations/contracts.d.ts +72 -0
  198. package/dist/operations/contracts.js +1 -0
  199. package/dist/operations/index.d.ts +1 -0
  200. package/dist/operations/index.js +1 -0
  201. package/dist/operations/operation-engine.d.ts +16 -0
  202. package/dist/operations/operation-engine.js +194 -0
  203. package/dist/ports/index.d.ts +12 -0
  204. package/{src/ports/index.ts → dist/ports/index.js} +12 -8
  205. package/dist/ports/location.d.ts +87 -0
  206. package/dist/ports/location.js +36 -0
  207. package/dist/ports/modem-manager.d.ts +89 -0
  208. package/dist/ports/modem-manager.js +9 -0
  209. package/dist/ports/mutation-admission.d.ts +27 -0
  210. package/dist/ports/mutation-admission.js +9 -0
  211. package/dist/ports/network-manager.d.ts +68 -0
  212. package/dist/ports/network-manager.js +13 -0
  213. package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
  214. package/dist/ports/observation.js +7 -0
  215. package/dist/ports/ops.d.ts +44 -0
  216. package/dist/ports/ops.js +16 -0
  217. package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
  218. package/dist/ports/receipts.js +9 -0
  219. package/dist/ports/reconcile.d.ts +33 -0
  220. package/dist/ports/reconcile.js +200 -0
  221. package/dist/ports/resource-ownership.d.ts +29 -0
  222. package/dist/ports/resource-ownership.js +1 -0
  223. package/dist/ports/router.d.ts +19 -0
  224. package/dist/ports/router.js +7 -0
  225. package/dist/ports/sms.d.ts +64 -0
  226. package/dist/ports/sms.js +24 -0
  227. package/dist/ports/uhubctl.d.ts +6 -0
  228. package/dist/ports/uhubctl.js +1 -0
  229. package/dist/providers/contracts.d.ts +124 -0
  230. package/dist/providers/contracts.js +10 -0
  231. package/dist/providers/huawei-hilink/index.d.ts +2 -0
  232. package/dist/providers/huawei-hilink/index.js +2 -0
  233. package/dist/providers/huawei-hilink/operations.d.ts +20 -0
  234. package/dist/providers/huawei-hilink/operations.js +56 -0
  235. package/dist/providers/huawei-hilink/provider.d.ts +52 -0
  236. package/dist/providers/huawei-hilink/provider.js +76 -0
  237. package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
  238. package/dist/providers/huawei-hilink/runtime.js +171 -0
  239. package/dist/providers/huawei-hilink/session.d.ts +28 -0
  240. package/dist/providers/huawei-hilink/session.js +120 -0
  241. package/dist/providers/huawei-hilink/transport.d.ts +19 -0
  242. package/dist/providers/huawei-hilink/transport.js +1 -0
  243. package/dist/providers/index.d.ts +8 -0
  244. package/dist/providers/index.js +8 -0
  245. package/dist/providers/matcher.d.ts +3 -0
  246. package/dist/providers/matcher.js +205 -0
  247. package/dist/providers/modem-manager/errors.d.ts +7 -0
  248. package/dist/providers/modem-manager/errors.js +37 -0
  249. package/dist/providers/modem-manager/generic-operations.d.ts +10 -0
  250. package/dist/providers/modem-manager/generic-operations.js +209 -0
  251. package/dist/providers/modem-manager/index.d.ts +4 -0
  252. package/dist/providers/modem-manager/index.js +4 -0
  253. package/dist/providers/modem-manager/module-operations.d.ts +21 -0
  254. package/dist/providers/modem-manager/module-operations.js +118 -0
  255. package/dist/providers/modem-manager/provider.d.ts +41 -0
  256. package/dist/providers/modem-manager/provider.js +155 -0
  257. package/dist/providers/modem-manager/runtime-composition-operation.d.ts +32 -0
  258. package/dist/providers/modem-manager/runtime-composition-operation.js +151 -0
  259. package/dist/providers/modem-manager/snapshot.d.ts +5 -0
  260. package/dist/providers/modem-manager/snapshot.js +204 -0
  261. package/dist/providers/modem-manager/types.d.ts +137 -0
  262. package/dist/providers/modem-manager/types.js +1 -0
  263. package/dist/providers/network-manager/adapter.d.ts +71 -0
  264. package/dist/providers/network-manager/adapter.js +348 -0
  265. package/dist/providers/network-manager/index.d.ts +2 -0
  266. package/dist/providers/network-manager/index.js +2 -0
  267. package/dist/providers/network-manager/types.d.ts +171 -0
  268. package/dist/providers/network-manager/types.js +77 -0
  269. package/dist/providers/registry.d.ts +13 -0
  270. package/dist/providers/registry.js +33 -0
  271. package/dist/providers/ufi-himi/index.d.ts +6 -0
  272. package/dist/providers/ufi-himi/index.js +6 -0
  273. package/dist/providers/ufi-himi/operations.d.ts +41 -0
  274. package/dist/providers/ufi-himi/operations.js +66 -0
  275. package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
  276. package/dist/providers/ufi-himi/prohibitions.js +88 -0
  277. package/dist/providers/ufi-himi/provider.d.ts +41 -0
  278. package/dist/providers/ufi-himi/provider.js +204 -0
  279. package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
  280. package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
  281. package/dist/providers/ufi-himi/session.d.ts +46 -0
  282. package/dist/providers/ufi-himi/session.js +92 -0
  283. package/dist/providers/ufi-himi/transport.d.ts +29 -0
  284. package/dist/providers/ufi-himi/transport.js +25 -0
  285. package/dist/providers/zte-goform/index.d.ts +2 -0
  286. package/dist/providers/zte-goform/index.js +2 -0
  287. package/dist/providers/zte-goform/provider.d.ts +56 -0
  288. package/dist/providers/zte-goform/provider.js +101 -0
  289. package/dist/providers/zte-goform/session.d.ts +23 -0
  290. package/dist/providers/zte-goform/session.js +197 -0
  291. package/dist/providers/zte-goform/transport.d.ts +16 -0
  292. package/dist/providers/zte-goform/transport.js +1 -0
  293. package/dist/radio/band-truth.d.ts +50 -0
  294. package/dist/radio/band-truth.js +92 -0
  295. package/dist/radio/index.d.ts +3 -0
  296. package/dist/radio/index.js +10 -0
  297. package/dist/radio/mode-combinations.d.ts +88 -0
  298. package/dist/radio/mode-combinations.js +198 -0
  299. package/dist/radio/mode-truth.d.ts +67 -0
  300. package/dist/radio/mode-truth.js +112 -0
  301. package/dist/redact.d.ts +15 -0
  302. package/dist/redact.js +189 -0
  303. package/dist/safety/composition-root.d.ts +28 -0
  304. package/dist/safety/composition-root.js +65 -0
  305. package/dist/safety/flock-resource-ownership.d.ts +11 -0
  306. package/dist/safety/flock-resource-ownership.js +138 -0
  307. package/dist/safety/index.d.ts +2 -0
  308. package/dist/safety/index.js +2 -0
  309. package/dist/sms/dbus-messaging.d.ts +17 -0
  310. package/dist/sms/dbus-messaging.js +185 -0
  311. package/dist/sms/inbox-store.d.ts +10 -0
  312. package/dist/sms/inbox-store.js +82 -0
  313. package/dist/sms/index.d.ts +4 -0
  314. package/dist/sms/index.js +10 -0
  315. package/dist/sms/mmcli-parse.d.ts +54 -0
  316. package/dist/sms/mmcli-parse.js +224 -0
  317. package/dist/sms/normalize.d.ts +42 -0
  318. package/dist/sms/normalize.js +95 -0
  319. package/dist/testing/domain-fakes.d.ts +58 -0
  320. package/dist/testing/domain-fakes.js +98 -0
  321. package/dist/testing/index.d.ts +2 -0
  322. package/dist/testing/index.js +17 -0
  323. package/dist/testing/provider-fakes.d.ts +45 -0
  324. package/dist/testing/provider-fakes.js +64 -0
  325. package/dist/transport/calls.d.ts +9 -0
  326. package/dist/transport/calls.js +88 -0
  327. package/dist/transport/codec.d.ts +3 -0
  328. package/dist/transport/codec.js +207 -0
  329. package/dist/transport/dbus-native.d.ts +57 -0
  330. package/dist/transport/dbus-native.js +17 -0
  331. package/dist/transport/errors.d.ts +21 -0
  332. package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
  333. package/dist/transport/index.d.ts +4 -0
  334. package/dist/transport/index.js +9 -0
  335. package/dist/transport/signals.d.ts +15 -0
  336. package/dist/transport/signals.js +123 -0
  337. package/dist/transport/signature.d.ts +7 -0
  338. package/dist/transport/signature.js +94 -0
  339. package/dist/transport/transport.d.ts +2 -0
  340. package/dist/transport/transport.js +202 -0
  341. package/dist/transport/types.d.ts +61 -0
  342. package/dist/transport/types.js +19 -0
  343. package/dist/usb-mode/catalog-schema.d.ts +139 -0
  344. package/dist/usb-mode/catalog-schema.js +97 -0
  345. package/dist/usb-mode/catalog.d.ts +21 -0
  346. package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
  347. package/dist/usb-mode/certified-catalog.json +67 -0
  348. package/dist/usb-mode/index.d.ts +6 -0
  349. package/dist/usb-mode/index.js +16 -0
  350. package/dist/usb-mode/ingestion.d.ts +111 -0
  351. package/dist/usb-mode/ingestion.js +187 -0
  352. package/dist/usb-mode/promotion-review.d.ts +21 -0
  353. package/dist/usb-mode/promotion-review.js +87 -0
  354. package/dist/usb-mode/runtime-capability.d.ts +59 -0
  355. package/dist/usb-mode/runtime-capability.js +157 -0
  356. package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
  357. package/dist/usb-mode/usb-devices-parse.js +157 -0
  358. package/dist/ussd/calls.d.ts +32 -0
  359. package/dist/ussd/calls.js +96 -0
  360. package/dist/ussd/index.d.ts +5 -0
  361. package/dist/ussd/index.js +12 -0
  362. package/dist/ussd/mm-ussd.d.ts +37 -0
  363. package/dist/ussd/mm-ussd.js +205 -0
  364. package/dist/ussd/refusal.d.ts +53 -0
  365. package/dist/ussd/refusal.js +154 -0
  366. package/dist/ussd/registration.d.ts +20 -0
  367. package/dist/ussd/registration.js +101 -0
  368. package/dist/ussd/session.d.ts +106 -0
  369. package/dist/ussd/session.js +163 -0
  370. package/package.json +38 -4
  371. package/src/backend/at-lease.test.ts +0 -106
  372. package/src/backend/at-lease.ts +0 -158
  373. package/src/backend/cell-info.test.ts +0 -154
  374. package/src/backend/cell-info.ts +0 -160
  375. package/src/backend/device-classifier.test.ts +0 -168
  376. package/src/backend/device-classifier.ts +0 -248
  377. package/src/backend/enrichment.ts +0 -96
  378. package/src/backend/features.test.ts +0 -162
  379. package/src/backend/features.ts +0 -179
  380. package/src/backend/identity-ladder.test.ts +0 -117
  381. package/src/backend/identity-ladder.ts +0 -221
  382. package/src/backend/identity-registry.test.ts +0 -89
  383. package/src/backend/identity-registry.ts +0 -151
  384. package/src/backend/index.ts +0 -236
  385. package/src/backend/lifecycle-interlock.ts +0 -38
  386. package/src/backend/managed-objects.ts +0 -108
  387. package/src/backend/mapping.ts +0 -160
  388. package/src/backend/mm-backend.ts +0 -191
  389. package/src/backend/mm-mutations.ts +0 -228
  390. package/src/backend/modem-actor.test.ts +0 -95
  391. package/src/backend/modem-actor.ts +0 -112
  392. package/src/backend/nm-auto-apn.ts +0 -161
  393. package/src/backend/nm-gsm-fields.ts +0 -122
  394. package/src/backend/nmcli-nm-port.ts +0 -228
  395. package/src/backend/nmcli-runner.ts +0 -52
  396. package/src/backend/observer.ts +0 -297
  397. package/src/backend/power-contract.test.ts +0 -40
  398. package/src/backend/power-contract.ts +0 -83
  399. package/src/backend/recovery-attribution.test.ts +0 -102
  400. package/src/backend/recovery-attribution.ts +0 -86
  401. package/src/backend/recovery-budget.test.ts +0 -64
  402. package/src/backend/recovery-budget.ts +0 -84
  403. package/src/backend/recovery-ladder.test.ts +0 -257
  404. package/src/backend/recovery-ladder.ts +0 -249
  405. package/src/backend/router-ethernet.test.ts +0 -71
  406. package/src/backend/router-ethernet.ts +0 -90
  407. package/src/backend/row-store.ts +0 -105
  408. package/src/backend/signal-setup.ts +0 -112
  409. package/src/backend/sim-unlock.ts +0 -193
  410. package/src/backend/transition-preconditions.ts +0 -149
  411. package/src/backend/uhubctl-power-hook.test.ts +0 -274
  412. package/src/backend/uhubctl-power-hook.ts +0 -377
  413. package/src/backend/usage/accounting.test.ts +0 -147
  414. package/src/backend/usage/accounting.ts +0 -123
  415. package/src/backend/usage/billing-cycle.test.ts +0 -62
  416. package/src/backend/usage/billing-cycle.ts +0 -45
  417. package/src/backend/usage/index.ts +0 -60
  418. package/src/backend/usage/policy-store.test.ts +0 -164
  419. package/src/backend/usage/policy-store.ts +0 -216
  420. package/src/backend/usage/policy-write.test.ts +0 -198
  421. package/src/backend/usage/policy-write.ts +0 -207
  422. package/src/backend/usage/proc-net-dev.test.ts +0 -56
  423. package/src/backend/usage/sampler.test.ts +0 -327
  424. package/src/backend/usage/sampler.ts +0 -282
  425. package/src/backend/usage/store.test.ts +0 -148
  426. package/src/backend/usage/store.ts +0 -177
  427. package/src/backend/usb-enumerator.test.ts +0 -87
  428. package/src/backend/usb-enumerator.ts +0 -181
  429. package/src/backend/usb-mode-transition.test.ts +0 -323
  430. package/src/backend/usb-mode-transition.ts +0 -253
  431. package/src/domain/brand.ts +0 -29
  432. package/src/domain/errors.ts +0 -77
  433. package/src/domain/guards.test.ts +0 -218
  434. package/src/domain/guards.ts +0 -144
  435. package/src/domain/identity.test.ts +0 -83
  436. package/src/domain/identity.ts +0 -165
  437. package/src/domain/index.ts +0 -12
  438. package/src/domain/snapshot.test.ts +0 -266
  439. package/src/domain/snapshot.ts +0 -120
  440. package/src/index.test.ts +0 -6
  441. package/src/index.ts +0 -15
  442. package/src/ports/README.md +0 -61
  443. package/src/ports/forbidden-surface.test.ts +0 -241
  444. package/src/ports/modem-manager.ts +0 -72
  445. package/src/ports/network-manager.ts +0 -87
  446. package/src/ports/ops.ts +0 -60
  447. package/src/ports/ops.type-test.ts +0 -39
  448. package/src/ports/receipts.test.ts +0 -153
  449. package/src/ports/reconcile.test.ts +0 -152
  450. package/src/ports/reconcile.ts +0 -338
  451. package/src/ports/router.ts +0 -29
  452. package/src/redact.test.ts +0 -82
  453. package/src/redact.ts +0 -73
  454. package/src/transport/README.md +0 -65
  455. package/src/transport/calls.ts +0 -113
  456. package/src/transport/characterization.test.ts +0 -260
  457. package/src/transport/codec.test.ts +0 -118
  458. package/src/transport/codec.ts +0 -240
  459. package/src/transport/conformance-python.test.ts +0 -152
  460. package/src/transport/conformance-same-lib.test.ts +0 -115
  461. package/src/transport/dbus-native-lib.d.ts +0 -19
  462. package/src/transport/dbus-native.ts +0 -85
  463. package/src/transport/index.ts +0 -30
  464. package/src/transport/no-library-leak.test.ts +0 -61
  465. package/src/transport/reliability.test.ts +0 -173
  466. package/src/transport/signals.ts +0 -150
  467. package/src/transport/signature.ts +0 -110
  468. package/src/transport/test-support/fake-service.ts +0 -168
  469. package/src/transport/test-support/independent-producer.py +0 -110
  470. package/src/transport/test-support/private-bus.ts +0 -66
  471. package/src/transport/transport.ts +0 -250
  472. package/src/transport/types.ts +0 -118
  473. package/src/usb-mode/catalog-schema.test.ts +0 -181
  474. package/src/usb-mode/catalog-schema.ts +0 -113
  475. package/src/usb-mode/certified-catalog.json +0 -67
  476. package/src/usb-mode/index.ts +0 -58
  477. package/src/usb-mode/ingestion.test.ts +0 -268
  478. package/src/usb-mode/ingestion.ts +0 -297
  479. package/src/usb-mode/promotion-review.ts +0 -117
  480. package/src/usb-mode/usb-devices-parse.ts +0 -196
@@ -0,0 +1,52 @@
1
+ import type { MmPropertyProbe } from '../backend/features.js';
2
+ import type { RadioAccessTechnology } from '../domain/index.js';
3
+ import type { CapabilityEvidence, CapabilityModule } from './support-claim.js';
4
+ /** MM's Messaging interface — SMS list/read lives here. */
5
+ export declare const MESSAGING_IFACE = "org.freedesktop.ModemManager1.Modem.Messaging";
6
+ /** MM's Location interface — GNSS sources are advertised here. */
7
+ export declare const LOCATION_IFACE = "org.freedesktop.ModemManager1.Modem.Location";
8
+ /** MM's 3GPP USSD interface — the same name the USSD adapter dials. */
9
+ export declare const USSD_IFACE = "org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd";
10
+ /**
11
+ * What A3.2 observed, plus the two signals the static object tree cannot carry.
12
+ *
13
+ * `interfaces` is the set of interface NAMES exported on the modem object, which
14
+ * is how SMS/USSD/Location are advertised — they are separate interfaces, not
15
+ * properties, so `MmPropertyProbe` alone cannot see them.
16
+ */
17
+ export interface ModuleCapabilityProbe extends MmPropertyProbe {
18
+ readonly interfaces?: ReadonlySet<string>;
19
+ /**
20
+ * `Location.Capabilities` decoded to source names (`gps-raw`, `gps-nmea`, …).
21
+ * An EMPTY set is a real answer — the interface exists and offers no GNSS.
22
+ */
23
+ readonly locationSources?: ReadonlySet<string>;
24
+ /**
25
+ * `SupportedModes` decoded to the RAT families it actually names.
26
+ *
27
+ * OPTIONAL, and its absence is the pre-existing behaviour verbatim: a caller
28
+ * that decoded only property NAMES answers exactly as before. Supplying it
29
+ * strictly NARROWS `five-g-pref`, because the property's mere presence is
30
+ * equally true of a 4G-only modem — see `detectCapabilityModules`.
31
+ */
32
+ readonly supportedRats?: ReadonlySet<RadioAccessTechnology>;
33
+ }
34
+ /**
35
+ * Detect every module's capability for one modem. Pure, total, never throws.
36
+ *
37
+ * `fcc-auto-unlock` is deliberately always `unknown`: FCC unlock is carried out
38
+ * by a ModemManager PLUGIN keyed on the device, and nothing on the modem's own
39
+ * D-Bus surface advertises whether one applies. Reporting `absent` would hide the
40
+ * module on hardware that supports it, and `present` would promise a plugin that
41
+ * may not be installed — so the honest answer is that this probe cannot tell, and
42
+ * evidence for it has to come from the catalog instead.
43
+ *
44
+ * `five-g-pref` is the one module whose property NAME is not the question. Every
45
+ * ModemManager modem exports `SupportedModes`, including a 4G-only one, so the
46
+ * name alone would resolve `present` on hardware with no 5G at all and offer a 5G
47
+ * posture nothing could honour. When the caller decoded the property's VALUE the
48
+ * verdict narrows to whether the catalog actually names 5GNR; when it did not,
49
+ * the property-name answer stands, so this is a strict narrowing and never a new
50
+ * way to claim a capability.
51
+ */
52
+ export declare function detectCapabilityModules(probe: ModuleCapabilityProbe): Record<CapabilityModule, CapabilityEvidence>;
@@ -0,0 +1,86 @@
1
+ // Per-module capability detection — what ONE modem can actually be asked to do.
2
+ //
3
+ // It follows `backend/features.ts` exactly, because the constraints are the same:
4
+ // detection must PROBE the observed surface rather than match a version
5
+ // whitelist, it must never throw, and an unseen future ModemManager must degrade
6
+ // gracefully instead of resolving to a confident wrong answer.
7
+ //
8
+ // The one rule this module adds is that `unknown` is a first-class result. A
9
+ // property set we never observed says nothing about the device, and the ladder in
10
+ // `support-claim.ts` stops at `enabled` for it — surfaced by nothing, mutated by
11
+ // nothing. Answering `absent` there would hide a working capability; answering
12
+ // `present` would offer a control the modem cannot honour.
13
+ import { MODEM_IFACE, MODEM3GPP_USSD_IFACE } from '../backend/constants.js';
14
+ import { fiveGPreferenceEvidence } from './five-g-preference.js';
15
+ /** MM's Messaging interface — SMS list/read lives here. */
16
+ export const MESSAGING_IFACE = `${MODEM_IFACE}.Messaging`;
17
+ /** MM's Location interface — GNSS sources are advertised here. */
18
+ export const LOCATION_IFACE = `${MODEM_IFACE}.Location`;
19
+ /** MM's 3GPP USSD interface — the same name the USSD adapter dials. */
20
+ export const USSD_IFACE = MODEM3GPP_USSD_IFACE;
21
+ const GNSS_SOURCES = ['gps-raw', 'gps-nmea', 'gps-unmanaged', 'agps-msa', 'agps-msb'];
22
+ /**
23
+ * A property we did not see is only evidence of ABSENCE when we saw the object at
24
+ * all. An empty property set means the read never landed.
25
+ */
26
+ function fromProperty(probe, name) {
27
+ if (probe.properties.size === 0) {
28
+ return 'unknown';
29
+ }
30
+ return probe.properties.has(name) ? 'present' : 'absent';
31
+ }
32
+ function fromAnyProperty(probe, names) {
33
+ if (probe.properties.size === 0) {
34
+ return 'unknown';
35
+ }
36
+ return names.some((name) => probe.properties.has(name)) ? 'present' : 'absent';
37
+ }
38
+ function fromInterface(probe, iface) {
39
+ if (probe.interfaces === undefined) {
40
+ return 'unknown';
41
+ }
42
+ return probe.interfaces.has(iface) ? 'present' : 'absent';
43
+ }
44
+ function detectGnss(probe) {
45
+ const iface = fromInterface(probe, LOCATION_IFACE);
46
+ if (iface !== 'present') {
47
+ return iface;
48
+ }
49
+ // The interface being exported is not the same claim as the modem offering a
50
+ // GNSS source — MM exports Location for 3GPP-LAC/CID-only devices too.
51
+ if (probe.locationSources === undefined) {
52
+ return 'unknown';
53
+ }
54
+ return GNSS_SOURCES.some((source) => probe.locationSources?.has(source)) ? 'present' : 'absent';
55
+ }
56
+ /**
57
+ * Detect every module's capability for one modem. Pure, total, never throws.
58
+ *
59
+ * `fcc-auto-unlock` is deliberately always `unknown`: FCC unlock is carried out
60
+ * by a ModemManager PLUGIN keyed on the device, and nothing on the modem's own
61
+ * D-Bus surface advertises whether one applies. Reporting `absent` would hide the
62
+ * module on hardware that supports it, and `present` would promise a plugin that
63
+ * may not be installed — so the honest answer is that this probe cannot tell, and
64
+ * evidence for it has to come from the catalog instead.
65
+ *
66
+ * `five-g-pref` is the one module whose property NAME is not the question. Every
67
+ * ModemManager modem exports `SupportedModes`, including a 4G-only one, so the
68
+ * name alone would resolve `present` on hardware with no 5G at all and offer a 5G
69
+ * posture nothing could honour. When the caller decoded the property's VALUE the
70
+ * verdict narrows to whether the catalog actually names 5GNR; when it did not,
71
+ * the property-name answer stands, so this is a strict narrowing and never a new
72
+ * way to claim a capability.
73
+ */
74
+ export function detectCapabilityModules(probe) {
75
+ return {
76
+ 'band-lock': fromProperty(probe, 'SupportedBands'),
77
+ sms: fromInterface(probe, MESSAGING_IFACE),
78
+ 'five-g-pref': probe.supportedRats === undefined
79
+ ? fromProperty(probe, 'SupportedModes')
80
+ : fiveGPreferenceEvidence(probe.supportedRats),
81
+ 'fcc-auto-unlock': 'unknown',
82
+ gps: detectGnss(probe),
83
+ ussd: fromInterface(probe, USSD_IFACE),
84
+ esim: fromAnyProperty(probe, ['SimType', 'EsimStatus']),
85
+ };
86
+ }
@@ -0,0 +1,104 @@
1
+ import type { RadioAccessTechnology } from '../domain/index.js';
2
+ import type { CapabilityEvidence } from './support-claim.js';
3
+ /**
4
+ * The postures this module offers. Deliberately four NAMED intents rather than a
5
+ * free (allowed, preferred) pair: an arbitrary pair is expressible on the wire but
6
+ * not answerable by an operator, and most pairs are postures nobody wants.
7
+ *
8
+ * 5g-only — allow ONLY 5G. No fallback: out of 5G coverage the modem stops
9
+ * registering, which is why it is offered but never a default.
10
+ * prefer-5g — allow 5G and everything below it, rank 5G first.
11
+ * prefer-4g — the SAME allowed set, ranked LTE first. This is the posture a
12
+ * marginal 5G cell calls for, and the one the coarse allowed-set
13
+ * selector structurally cannot express.
14
+ * 5g-off — allow everything the modem supports EXCEPT 5G.
15
+ */
16
+ export declare const FIVE_G_PREFERENCES: readonly ['5g-only', 'prefer-5g', 'prefer-4g', '5g-off'];
17
+ export type FiveGPreference = (typeof FIVE_G_PREFERENCES)[number];
18
+ /** One `(allowed, preferred)` pair, the shape MM's `CurrentModes` really carries. */
19
+ export interface RadioModeSet {
20
+ readonly allowed: ReadonlySet<RadioAccessTechnology>;
21
+ /** MM's `preferred` is a SINGLE mode, and `none` is a legal answer. */
22
+ readonly preferred?: RadioAccessTechnology | undefined;
23
+ }
24
+ /** What a stated preference resolves to, in `MmMutations.setRadioModes`'s shape. */
25
+ export interface FiveGRadioTarget {
26
+ readonly preferenceOrdered: readonly RadioAccessTechnology[];
27
+ readonly allowedSet: ReadonlySet<RadioAccessTechnology>;
28
+ }
29
+ /**
30
+ * SA vs NSA — and the honest answer, which is that ModemManager does not expose
31
+ * the choice at all.
32
+ *
33
+ * Checked against MM 1.24.2's own D-Bus surface rather than recalled: the only
34
+ * NR-specific member on a modem object is
35
+ * `Modem.Modem3gpp.SetNr5gRegistrationSettings`, whose keys are `mico-mode` and
36
+ * `drx-cycle` — power-saving registration parameters, not a standalone-vs
37
+ * non-standalone selector. Vendors expose the selector through their own AT
38
+ * commands (Quectel `AT+QNWPREFCFG="mode_pref"`, and one per vendor after that),
39
+ * which is a per-SKU control surface this module deliberately does not open: an
40
+ * uncertified AT write that can cost registration is exactly what the evidence
41
+ * gate exists to keep out.
42
+ *
43
+ * So the axis is REPORTED as unsupported rather than omitted. A missing field
44
+ * reads as "nobody asked"; a stated `not-exposed-by-modemmanager` tells an
45
+ * operator looking for an SA toggle why there is none.
46
+ */
47
+ export declare const NR_MODE_UNSUPPORTED_REASON: 'not-exposed-by-modemmanager';
48
+ export interface NrModeSelection {
49
+ readonly supported: false;
50
+ readonly reason: typeof NR_MODE_UNSUPPORTED_REASON;
51
+ }
52
+ /** The SA/NSA verdict. Constant today, and a function so a future SKU-certified
53
+ * path replaces one call site rather than every consumer. */
54
+ export declare function nrModeSelection(): NrModeSelection;
55
+ /**
56
+ * Does this modem's advertised mode catalog contain 5G?
57
+ *
58
+ * An EMPTY catalog is `unknown`, never `absent` — an unobserved read says nothing
59
+ * about the device, and reporting `absent` there would hide the module on a modem
60
+ * that supports it. This is the same first-class-`unknown` rule `detect.ts`
61
+ * follows, applied one level deeper: `detect.ts` can only see that a
62
+ * `SupportedModes` property EXISTS, which is equally true of a 4G-only modem.
63
+ */
64
+ export declare function fiveGPreferenceEvidence(supportedRats: ReadonlySet<RadioAccessTechnology> | undefined): CapabilityEvidence;
65
+ /**
66
+ * Which postures this modem can actually be asked for.
67
+ *
68
+ * A modem with no 5G is offered NOTHING — not `5g-off` either, because "turn 5G
69
+ * off" is a control that would change nothing on a radio that has no 5G, and a
70
+ * control that cannot change anything is the defect this repo's evidence gate
71
+ * exists to keep off an operator's screen.
72
+ *
73
+ * `prefer-5g` / `prefer-4g` / `5g-off` each additionally require a SUB-5G mode to
74
+ * fall back to: on a 5G-only radio they would all collapse onto the same allowed
75
+ * set as `5g-only`, i.e. three labels for one posture.
76
+ */
77
+ export declare function offeredFiveGPreferences(supportedRats: ReadonlySet<RadioAccessTechnology> | undefined): readonly FiveGPreference[];
78
+ /**
79
+ * Resolve a stated preference into the `(allowed, preferred)` pair to write.
80
+ *
81
+ * `undefined` means this modem cannot express the posture — the caller must
82
+ * REFUSE rather than substitute a neighbouring one. Substituting is how an
83
+ * operator asks for "prefer 4G" on a marginal cell and silently gets 5G-first.
84
+ */
85
+ export declare function fiveGPreferenceToRadio(preference: FiveGPreference, supportedRats: ReadonlySet<RadioAccessTechnology> | undefined): FiveGRadioTarget | undefined;
86
+ /**
87
+ * Which posture a modem's CURRENT modes name — or `undefined` for a pair this
88
+ * model does not name.
89
+ *
90
+ * `undefined` is a first-class answer and must not be rounded to the nearest
91
+ * posture: a radio parked on `allowed: 3g,4g; preferred: 3g` is in a state no 5G
92
+ * preference describes, and reporting one would show an operator a selection they
93
+ * never made and cannot get back to.
94
+ */
95
+ export declare function readFiveGPreference(current: RadioModeSet | undefined): FiveGPreference | undefined;
96
+ /**
97
+ * Did a readback land on the requested posture?
98
+ *
99
+ * The RESULT of the write is not the write's own acknowledgement — MM answering
100
+ * the method call says the request was accepted, not that the radio took it, and
101
+ * a modem is entitled to clamp a mode set it cannot honour. So a confirmation is
102
+ * a re-read compared against the request, and this is that comparison.
103
+ */
104
+ export declare function fiveGPreferenceConfirmed(requested: FiveGPreference, readback: RadioModeSet | undefined): boolean;
@@ -0,0 +1,171 @@
1
+ // The `five-g-pref` capability module's MODEL — which 5G postures a modem can be
2
+ // asked for, how each maps onto ModemManager's `SetCurrentModes`, and which one
3
+ // the radio is on right now.
4
+ //
5
+ // It is pure and total. Nothing here talks to a bus: `MmMutations.setRadioModes`
6
+ // already owns the D-Bus call, the per-modem serialization and the quiesce, so
7
+ // this module's whole job is to decide WHICH `DesiredRadio` a stated preference
8
+ // means and to refuse to name one the modem never advertised.
9
+ //
10
+ // WHY THE MODULE EXISTS AT ALL, given the 3G/4G/5G selector already writes
11
+ // `SetCurrentModes`: that selector's vocabulary is the ALLOWED SET. Two genuinely
12
+ // different postures — "allow 4G and 5G, prefer 5G" and "allow 4G and 5G, prefer
13
+ // 4G" — share one allowed set and are distinguishable only by the PREFERRED mode,
14
+ // which the coarse selector folds away. An operator on a marginal 5G cell wants
15
+ // exactly that distinction, and it is the one thing they could not previously ask
16
+ // for.
17
+ /**
18
+ * The postures this module offers. Deliberately four NAMED intents rather than a
19
+ * free (allowed, preferred) pair: an arbitrary pair is expressible on the wire but
20
+ * not answerable by an operator, and most pairs are postures nobody wants.
21
+ *
22
+ * 5g-only — allow ONLY 5G. No fallback: out of 5G coverage the modem stops
23
+ * registering, which is why it is offered but never a default.
24
+ * prefer-5g — allow 5G and everything below it, rank 5G first.
25
+ * prefer-4g — the SAME allowed set, ranked LTE first. This is the posture a
26
+ * marginal 5G cell calls for, and the one the coarse allowed-set
27
+ * selector structurally cannot express.
28
+ * 5g-off — allow everything the modem supports EXCEPT 5G.
29
+ */
30
+ export const FIVE_G_PREFERENCES = ['5g-only', 'prefer-5g', 'prefer-4g', '5g-off'];
31
+ /**
32
+ * SA vs NSA — and the honest answer, which is that ModemManager does not expose
33
+ * the choice at all.
34
+ *
35
+ * Checked against MM 1.24.2's own D-Bus surface rather than recalled: the only
36
+ * NR-specific member on a modem object is
37
+ * `Modem.Modem3gpp.SetNr5gRegistrationSettings`, whose keys are `mico-mode` and
38
+ * `drx-cycle` — power-saving registration parameters, not a standalone-vs
39
+ * non-standalone selector. Vendors expose the selector through their own AT
40
+ * commands (Quectel `AT+QNWPREFCFG="mode_pref"`, and one per vendor after that),
41
+ * which is a per-SKU control surface this module deliberately does not open: an
42
+ * uncertified AT write that can cost registration is exactly what the evidence
43
+ * gate exists to keep out.
44
+ *
45
+ * So the axis is REPORTED as unsupported rather than omitted. A missing field
46
+ * reads as "nobody asked"; a stated `not-exposed-by-modemmanager` tells an
47
+ * operator looking for an SA toggle why there is none.
48
+ */
49
+ export const NR_MODE_UNSUPPORTED_REASON = 'not-exposed-by-modemmanager';
50
+ /** The SA/NSA verdict. Constant today, and a function so a future SKU-certified
51
+ * path replaces one call site rather than every consumer. */
52
+ export function nrModeSelection() {
53
+ return { supported: false, reason: NR_MODE_UNSUPPORTED_REASON };
54
+ }
55
+ /** RAT ranking, highest generation first. The ONE ordering this module uses. */
56
+ const RAT_ORDER = ['5gnr', 'lte', 'umts', 'gsm'];
57
+ function ranked(rats) {
58
+ return RAT_ORDER.filter((rat) => rats.has(rat));
59
+ }
60
+ /**
61
+ * Does this modem's advertised mode catalog contain 5G?
62
+ *
63
+ * An EMPTY catalog is `unknown`, never `absent` — an unobserved read says nothing
64
+ * about the device, and reporting `absent` there would hide the module on a modem
65
+ * that supports it. This is the same first-class-`unknown` rule `detect.ts`
66
+ * follows, applied one level deeper: `detect.ts` can only see that a
67
+ * `SupportedModes` property EXISTS, which is equally true of a 4G-only modem.
68
+ */
69
+ export function fiveGPreferenceEvidence(supportedRats) {
70
+ if (supportedRats === undefined || supportedRats.size === 0) {
71
+ return 'unknown';
72
+ }
73
+ return supportedRats.has('5gnr') ? 'present' : 'absent';
74
+ }
75
+ /**
76
+ * Which postures this modem can actually be asked for.
77
+ *
78
+ * A modem with no 5G is offered NOTHING — not `5g-off` either, because "turn 5G
79
+ * off" is a control that would change nothing on a radio that has no 5G, and a
80
+ * control that cannot change anything is the defect this repo's evidence gate
81
+ * exists to keep off an operator's screen.
82
+ *
83
+ * `prefer-5g` / `prefer-4g` / `5g-off` each additionally require a SUB-5G mode to
84
+ * fall back to: on a 5G-only radio they would all collapse onto the same allowed
85
+ * set as `5g-only`, i.e. three labels for one posture.
86
+ */
87
+ export function offeredFiveGPreferences(supportedRats) {
88
+ if (supportedRats === undefined || !supportedRats.has('5gnr')) {
89
+ return [];
90
+ }
91
+ const lower = ranked(supportedRats).filter((rat) => rat !== '5gnr');
92
+ if (lower.length === 0) {
93
+ return ['5g-only'];
94
+ }
95
+ // `prefer-4g` names LTE specifically, so it is offered only on a modem that
96
+ // has LTE. A 5G+UMTS-only radio is not a real fleet device, but naming a mode
97
+ // the modem never advertised is the one thing this module must not do.
98
+ return FIVE_G_PREFERENCES.filter((preference) => preference !== 'prefer-4g' || supportedRats.has('lte'));
99
+ }
100
+ /**
101
+ * Resolve a stated preference into the `(allowed, preferred)` pair to write.
102
+ *
103
+ * `undefined` means this modem cannot express the posture — the caller must
104
+ * REFUSE rather than substitute a neighbouring one. Substituting is how an
105
+ * operator asks for "prefer 4G" on a marginal cell and silently gets 5G-first.
106
+ */
107
+ export function fiveGPreferenceToRadio(preference, supportedRats) {
108
+ if (supportedRats === undefined || !offeredFiveGPreferences(supportedRats).includes(preference)) {
109
+ return undefined;
110
+ }
111
+ const all = ranked(supportedRats);
112
+ const withoutNr = all.filter((rat) => rat !== '5gnr');
113
+ switch (preference) {
114
+ case '5g-only':
115
+ return { allowedSet: new Set(['5gnr']), preferenceOrdered: ['5gnr'] };
116
+ case 'prefer-5g':
117
+ return { allowedSet: new Set(all), preferenceOrdered: all };
118
+ case 'prefer-4g':
119
+ // The allowed set is IDENTICAL to `prefer-5g`'s — only the ranking moves.
120
+ // That is the whole point of the posture, and it is why a consumer must
121
+ // never diff allowed sets to decide whether a write is needed.
122
+ return {
123
+ allowedSet: new Set(all),
124
+ preferenceOrdered: ['lte', ...all.filter((rat) => rat !== 'lte')],
125
+ };
126
+ case '5g-off':
127
+ return { allowedSet: new Set(withoutNr), preferenceOrdered: withoutNr };
128
+ }
129
+ }
130
+ /**
131
+ * Which posture a modem's CURRENT modes name — or `undefined` for a pair this
132
+ * model does not name.
133
+ *
134
+ * `undefined` is a first-class answer and must not be rounded to the nearest
135
+ * posture: a radio parked on `allowed: 3g,4g; preferred: 3g` is in a state no 5G
136
+ * preference describes, and reporting one would show an operator a selection they
137
+ * never made and cannot get back to.
138
+ */
139
+ export function readFiveGPreference(current) {
140
+ if (current === undefined || current.allowed.size === 0) {
141
+ return undefined;
142
+ }
143
+ const has5g = current.allowed.has('5gnr');
144
+ const others = ranked(current.allowed).filter((rat) => rat !== '5gnr');
145
+ if (!has5g) {
146
+ // A sub-5G allowed set is `5g-off` whatever it ranks first: the posture is
147
+ // about 5G, and this model has no opinion on how 4G and 3G are ordered.
148
+ return '5g-off';
149
+ }
150
+ if (others.length === 0) {
151
+ return '5g-only';
152
+ }
153
+ if (current.preferred === '5gnr') {
154
+ return 'prefer-5g';
155
+ }
156
+ if (current.preferred === 'lte') {
157
+ return 'prefer-4g';
158
+ }
159
+ return undefined;
160
+ }
161
+ /**
162
+ * Did a readback land on the requested posture?
163
+ *
164
+ * The RESULT of the write is not the write's own acknowledgement — MM answering
165
+ * the method call says the request was accepted, not that the radio took it, and
166
+ * a modem is entitled to clamp a mode set it cannot honour. So a confirmation is
167
+ * a re-read compared against the request, and this is that comparison.
168
+ */
169
+ export function fiveGPreferenceConfirmed(requested, readback) {
170
+ return readFiveGPreference(readback) === requested;
171
+ }
@@ -0,0 +1,3 @@
1
+ export * from './detect.js';
2
+ export * from './five-g-preference.js';
3
+ export * from './support-claim.js';
@@ -0,0 +1,10 @@
1
+ // Capability-module feature-gate framework — the support-claim taxonomy, the
2
+ // per-modem capability detection the seven gated modules resolve against, and the
3
+ // modules that have landed their own probe + evidence.
4
+ //
5
+ // Implemented so far: `five-g-pref` (`five-g-preference.ts`). The remaining six
6
+ // are framework-only and may not be surfaced or claimed until their own change
7
+ // lands.
8
+ export * from './detect.js';
9
+ export * from './five-g-preference.js';
10
+ export * from './support-claim.js';
@@ -0,0 +1,39 @@
1
+ /** The seven gated capability modules. */
2
+ export declare const CAPABILITY_MODULES: readonly ['band-lock', 'sms', 'five-g-pref', 'fcc-auto-unlock', 'gps', 'ussd', 'esim'];
3
+ export type CapabilityModule = (typeof CAPABILITY_MODULES)[number];
4
+ /**
5
+ * The ladder, lowest rung first. `resolveSupportClaim` answers with the highest
6
+ * rung reached:
7
+ *
8
+ * unavailable — not shipped in this build, OR the modem positively lacks it.
9
+ * implemented — shipped, gate OFF. The default for every module, everywhere.
10
+ * enabled — gate ON, capability UNKNOWN. "Not asked" is not "absent".
11
+ * capable — gate ON, modem advertises it. The floor for offering a control.
12
+ * certified — capable AND proven on this exact model+firmware. The ONLY rung
13
+ * a support matrix or a doc may claim.
14
+ */
15
+ export declare const SUPPORT_CLAIM_STATES: readonly ['unavailable', 'implemented', 'enabled', 'capable', 'certified'];
16
+ export type SupportClaimState = (typeof SUPPORT_CLAIM_STATES)[number];
17
+ /** What a probe found. `unknown` is an answer about the READ, not the device. */
18
+ export type CapabilityEvidence = 'present' | 'absent' | 'unknown';
19
+ export interface SupportClaimInput {
20
+ readonly implemented: boolean;
21
+ readonly gateEnabled: boolean;
22
+ readonly capability: CapabilityEvidence;
23
+ readonly certified: boolean;
24
+ }
25
+ export declare function resolveSupportClaim(input: SupportClaimInput): SupportClaimState;
26
+ export declare const SURFACEABLE_SUPPORT_STATES: readonly SupportClaimState[];
27
+ export declare function mayRenderModule(state: SupportClaimState): boolean;
28
+ export declare function mayClaimSupport(state: SupportClaimState): boolean;
29
+ export type CapabilityModuleClaims = Readonly<Record<CapabilityModule, SupportClaimState>>;
30
+ export interface CapabilityMatrixInput {
31
+ readonly implemented: readonly CapabilityModule[];
32
+ readonly gates: Partial<Record<CapabilityModule, boolean>>;
33
+ readonly capability: Partial<Record<CapabilityModule, CapabilityEvidence>>;
34
+ readonly certified?: Partial<Record<CapabilityModule, boolean>>;
35
+ }
36
+ /** Total by construction: every module gets an explicit state. */
37
+ export declare function resolveCapabilityMatrix(input: CapabilityMatrixInput): CapabilityModuleClaims;
38
+ export declare function surfaceableModules(claims: CapabilityModuleClaims): CapabilityModule[];
39
+ export declare function claimableModules(claims: CapabilityModuleClaims): CapabilityModule[];
@@ -0,0 +1,81 @@
1
+ // The support-claim taxonomy — the vocabulary this stack uses to say how much is
2
+ // actually known about a capability module on a given modem.
3
+ //
4
+ // It exists because "supported" was one word doing four jobs: the code exists,
5
+ // an operator turned it on, the modem advertises it, and somebody proved it on
6
+ // this firmware. Those are four different facts with four different consequences
7
+ // — the third gates what a UI may offer, the fourth gates what a support matrix
8
+ // may claim — and collapsing them is how a doc comes to promise a combination
9
+ // nobody ever ran.
10
+ //
11
+ // This module is PURE and mirrors the same ladder CeraUI's `@ceraui/rpc`
12
+ // `capability-modules.schema.ts` carries. It is a mirror rather than a shared
13
+ // import by Rule D: this repo builds standalone, so the two halves are kept
14
+ // honest by their tests, not by a path.
15
+ /** The seven gated capability modules. */
16
+ export const CAPABILITY_MODULES = [
17
+ 'band-lock',
18
+ 'sms',
19
+ 'five-g-pref',
20
+ 'fcc-auto-unlock',
21
+ 'gps',
22
+ 'ussd',
23
+ 'esim',
24
+ ];
25
+ /**
26
+ * The ladder, lowest rung first. `resolveSupportClaim` answers with the highest
27
+ * rung reached:
28
+ *
29
+ * unavailable — not shipped in this build, OR the modem positively lacks it.
30
+ * implemented — shipped, gate OFF. The default for every module, everywhere.
31
+ * enabled — gate ON, capability UNKNOWN. "Not asked" is not "absent".
32
+ * capable — gate ON, modem advertises it. The floor for offering a control.
33
+ * certified — capable AND proven on this exact model+firmware. The ONLY rung
34
+ * a support matrix or a doc may claim.
35
+ */
36
+ export const SUPPORT_CLAIM_STATES = [
37
+ 'unavailable',
38
+ 'implemented',
39
+ 'enabled',
40
+ 'capable',
41
+ 'certified',
42
+ ];
43
+ export function resolveSupportClaim(input) {
44
+ if (!input.implemented || input.capability === 'absent') {
45
+ return 'unavailable';
46
+ }
47
+ if (!input.gateEnabled) {
48
+ return 'implemented';
49
+ }
50
+ if (input.capability === 'unknown') {
51
+ return 'enabled';
52
+ }
53
+ return input.certified ? 'certified' : 'capable';
54
+ }
55
+ export const SURFACEABLE_SUPPORT_STATES = ['capable', 'certified'];
56
+ export function mayRenderModule(state) {
57
+ return SURFACEABLE_SUPPORT_STATES.includes(state);
58
+ }
59
+ export function mayClaimSupport(state) {
60
+ return state === 'certified';
61
+ }
62
+ /** Total by construction: every module gets an explicit state. */
63
+ export function resolveCapabilityMatrix(input) {
64
+ const implemented = new Set(input.implemented);
65
+ const claims = {};
66
+ for (const module of CAPABILITY_MODULES) {
67
+ claims[module] = resolveSupportClaim({
68
+ implemented: implemented.has(module),
69
+ gateEnabled: input.gates[module] === true,
70
+ capability: input.capability[module] ?? 'unknown',
71
+ certified: input.certified?.[module] === true,
72
+ });
73
+ }
74
+ return claims;
75
+ }
76
+ export function surfaceableModules(claims) {
77
+ return CAPABILITY_MODULES.filter((module) => mayRenderModule(claims[module]));
78
+ }
79
+ export function claimableModules(claims) {
80
+ return CAPABILITY_MODULES.filter((module) => mayClaimSupport(claims[module]));
81
+ }
@@ -0,0 +1,10 @@
1
+ declare const brand: unique symbol;
2
+ /** A primitive `T` tagged with a compile-time-only brand `B`. Erased at runtime. */
3
+ export type Brand<T, B extends string> = T & {
4
+ readonly [brand]: B;
5
+ };
6
+ /** Assert a value is a non-empty string, throwing a typed error otherwise. */
7
+ export declare function nonEmptyString(value: string, label: string): string;
8
+ /** Assert a value is a non-negative safe integer, throwing a typed error otherwise. */
9
+ export declare function nonNegativeInteger(value: number, label: string): number;
10
+ export {};
@@ -0,0 +1,21 @@
1
+ // Nominal (branded) primitive types for the domain layer.
2
+ //
3
+ // Branding stops the four identity strings — and the two counter numbers — from
4
+ // being interchangeable at the type level: a `LogicalSlotId` can never be passed
5
+ // where a `SubscriptionId` is expected, even though both are strings at runtime.
6
+ // Downstream waves (A2.2 ports, A3.x D-Bus backend) depend on this distinction.
7
+ import { DomainError } from './errors.js';
8
+ /** Assert a value is a non-empty string, throwing a typed error otherwise. */
9
+ export function nonEmptyString(value, label) {
10
+ if (value.length === 0) {
11
+ throw new DomainError(`${label} must be a non-empty string`);
12
+ }
13
+ return value;
14
+ }
15
+ /** Assert a value is a non-negative safe integer, throwing a typed error otherwise. */
16
+ export function nonNegativeInteger(value, label) {
17
+ if (!Number.isSafeInteger(value) || value < 0) {
18
+ throw new DomainError(`${label} must be a non-negative safe integer, got ${value}`);
19
+ }
20
+ return value;
21
+ }
@@ -0,0 +1,32 @@
1
+ /** Base class for every domain-layer error. Callers can catch this to trap all. */
2
+ export declare class DomainError extends Error {
3
+ readonly name: string;
4
+ constructor(message: string);
5
+ }
6
+ /**
7
+ * Every distinct impossible cross-dimension combination the guards reject.
8
+ * One code per rule so a failing construction names exactly what it violated.
9
+ */
10
+ export type ImpossibleStateCode = 'registered-while-absent' | 'registered-radio-off' | 'registered-empty-rat-set' | 'active-state-while-absent' | 'radio-off-while-active' | 'nm-activated-while-absent' | 'nm-activated-without-interface' | 'nm-activated-without-mm-connected' | 'multiple-active-sim-slots' | 'locked-sim-in-empty-slot' | 'mm-locked-without-sim-lock' | 'data-interface-name-without-presence' | 'recovery-attempts-negative' | 'recovery-cooldown-stage-mismatch' | 'recovery-idle-with-attempts';
11
+ /** A snapshot was constructed (or transitioned into) a physically impossible state. */
12
+ export declare class ImpossibleStateError extends DomainError {
13
+ readonly name = "ImpossibleStateError";
14
+ readonly code: ImpossibleStateCode;
15
+ constructor(code: ImpossibleStateCode, detail: string);
16
+ }
17
+ /**
18
+ * A durable policy binding was attempted against an identity that is not allowed
19
+ * to carry one — today only low-confidence (ambiguous) equipment identities.
20
+ */
21
+ export declare class PolicyBindingRefusedError extends DomainError {
22
+ readonly name = "PolicyBindingRefusedError";
23
+ readonly reason: 'ambiguous-identity';
24
+ constructor(detail: string);
25
+ }
26
+ /** A snapshot transition tried to keep or lower the monotonic revision. */
27
+ export declare class RevisionMonotonicityError extends DomainError {
28
+ readonly name = "RevisionMonotonicityError";
29
+ readonly previous: number;
30
+ readonly next: number;
31
+ constructor(previous: number, next: number);
32
+ }