@ceralive/modem-control 0.2.0 → 1.1.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 (464) hide show
  1. package/README.md +317 -0
  2. package/dist/backend/at-lease.d.ts +57 -0
  3. package/dist/backend/at-lease.js +109 -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 +76 -0
  61. package/dist/backend/transition-preconditions.js +64 -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 +196 -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 +19 -0
  146. package/dist/index.js +26 -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 +47 -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/operations/contracts.d.ts +72 -0
  196. package/dist/operations/contracts.js +1 -0
  197. package/dist/operations/index.d.ts +1 -0
  198. package/dist/operations/index.js +1 -0
  199. package/dist/operations/operation-engine.d.ts +16 -0
  200. package/dist/operations/operation-engine.js +194 -0
  201. package/dist/ports/index.d.ts +12 -0
  202. package/{src/ports/index.ts → dist/ports/index.js} +12 -8
  203. package/dist/ports/location.d.ts +87 -0
  204. package/dist/ports/location.js +36 -0
  205. package/dist/ports/modem-manager.d.ts +89 -0
  206. package/dist/ports/modem-manager.js +9 -0
  207. package/dist/ports/mutation-admission.d.ts +27 -0
  208. package/dist/ports/mutation-admission.js +9 -0
  209. package/dist/ports/network-manager.d.ts +68 -0
  210. package/dist/ports/network-manager.js +13 -0
  211. package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
  212. package/dist/ports/observation.js +7 -0
  213. package/dist/ports/ops.d.ts +44 -0
  214. package/dist/ports/ops.js +16 -0
  215. package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
  216. package/dist/ports/receipts.js +9 -0
  217. package/dist/ports/reconcile.d.ts +33 -0
  218. package/dist/ports/reconcile.js +200 -0
  219. package/dist/ports/resource-ownership.d.ts +29 -0
  220. package/dist/ports/resource-ownership.js +1 -0
  221. package/dist/ports/router.d.ts +19 -0
  222. package/dist/ports/router.js +7 -0
  223. package/dist/ports/sms.d.ts +64 -0
  224. package/dist/ports/sms.js +24 -0
  225. package/dist/ports/uhubctl.d.ts +6 -0
  226. package/dist/ports/uhubctl.js +1 -0
  227. package/dist/providers/contracts.d.ts +124 -0
  228. package/dist/providers/contracts.js +10 -0
  229. package/dist/providers/huawei-hilink/index.d.ts +2 -0
  230. package/dist/providers/huawei-hilink/index.js +2 -0
  231. package/dist/providers/huawei-hilink/operations.d.ts +20 -0
  232. package/dist/providers/huawei-hilink/operations.js +56 -0
  233. package/dist/providers/huawei-hilink/provider.d.ts +52 -0
  234. package/dist/providers/huawei-hilink/provider.js +76 -0
  235. package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
  236. package/dist/providers/huawei-hilink/runtime.js +171 -0
  237. package/dist/providers/huawei-hilink/session.d.ts +28 -0
  238. package/dist/providers/huawei-hilink/session.js +120 -0
  239. package/dist/providers/huawei-hilink/transport.d.ts +19 -0
  240. package/dist/providers/huawei-hilink/transport.js +1 -0
  241. package/dist/providers/index.d.ts +8 -0
  242. package/dist/providers/index.js +8 -0
  243. package/dist/providers/matcher.d.ts +3 -0
  244. package/dist/providers/matcher.js +205 -0
  245. package/dist/providers/modem-manager/errors.d.ts +7 -0
  246. package/dist/providers/modem-manager/errors.js +37 -0
  247. package/dist/providers/modem-manager/generic-operations.d.ts +8 -0
  248. package/dist/providers/modem-manager/generic-operations.js +207 -0
  249. package/dist/providers/modem-manager/index.d.ts +3 -0
  250. package/dist/providers/modem-manager/index.js +3 -0
  251. package/dist/providers/modem-manager/module-operations.d.ts +21 -0
  252. package/dist/providers/modem-manager/module-operations.js +118 -0
  253. package/dist/providers/modem-manager/provider.d.ts +39 -0
  254. package/dist/providers/modem-manager/provider.js +152 -0
  255. package/dist/providers/modem-manager/snapshot.d.ts +5 -0
  256. package/dist/providers/modem-manager/snapshot.js +204 -0
  257. package/dist/providers/modem-manager/types.d.ts +135 -0
  258. package/dist/providers/modem-manager/types.js +1 -0
  259. package/dist/providers/network-manager/adapter.d.ts +71 -0
  260. package/dist/providers/network-manager/adapter.js +348 -0
  261. package/dist/providers/network-manager/index.d.ts +2 -0
  262. package/dist/providers/network-manager/index.js +2 -0
  263. package/dist/providers/network-manager/types.d.ts +163 -0
  264. package/dist/providers/network-manager/types.js +77 -0
  265. package/dist/providers/registry.d.ts +13 -0
  266. package/dist/providers/registry.js +33 -0
  267. package/dist/providers/ufi-himi/index.d.ts +6 -0
  268. package/dist/providers/ufi-himi/index.js +6 -0
  269. package/dist/providers/ufi-himi/operations.d.ts +41 -0
  270. package/dist/providers/ufi-himi/operations.js +66 -0
  271. package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
  272. package/dist/providers/ufi-himi/prohibitions.js +88 -0
  273. package/dist/providers/ufi-himi/provider.d.ts +41 -0
  274. package/dist/providers/ufi-himi/provider.js +204 -0
  275. package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
  276. package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
  277. package/dist/providers/ufi-himi/session.d.ts +46 -0
  278. package/dist/providers/ufi-himi/session.js +92 -0
  279. package/dist/providers/ufi-himi/transport.d.ts +29 -0
  280. package/dist/providers/ufi-himi/transport.js +25 -0
  281. package/dist/providers/zte-goform/index.d.ts +2 -0
  282. package/dist/providers/zte-goform/index.js +2 -0
  283. package/dist/providers/zte-goform/provider.d.ts +51 -0
  284. package/dist/providers/zte-goform/provider.js +101 -0
  285. package/dist/providers/zte-goform/session.d.ts +17 -0
  286. package/dist/providers/zte-goform/session.js +133 -0
  287. package/dist/providers/zte-goform/transport.d.ts +16 -0
  288. package/dist/providers/zte-goform/transport.js +1 -0
  289. package/dist/radio/band-truth.d.ts +50 -0
  290. package/dist/radio/band-truth.js +92 -0
  291. package/dist/radio/index.d.ts +3 -0
  292. package/dist/radio/index.js +10 -0
  293. package/dist/radio/mode-combinations.d.ts +78 -0
  294. package/dist/radio/mode-combinations.js +198 -0
  295. package/dist/radio/mode-truth.d.ts +67 -0
  296. package/dist/radio/mode-truth.js +112 -0
  297. package/dist/redact.d.ts +15 -0
  298. package/dist/redact.js +189 -0
  299. package/dist/safety/composition-root.d.ts +28 -0
  300. package/dist/safety/composition-root.js +65 -0
  301. package/dist/safety/flock-resource-ownership.d.ts +11 -0
  302. package/dist/safety/flock-resource-ownership.js +151 -0
  303. package/dist/safety/index.d.ts +2 -0
  304. package/dist/safety/index.js +2 -0
  305. package/dist/sms/dbus-messaging.d.ts +17 -0
  306. package/dist/sms/dbus-messaging.js +185 -0
  307. package/dist/sms/inbox-store.d.ts +10 -0
  308. package/dist/sms/inbox-store.js +82 -0
  309. package/dist/sms/index.d.ts +4 -0
  310. package/dist/sms/index.js +10 -0
  311. package/dist/sms/mmcli-parse.d.ts +54 -0
  312. package/dist/sms/mmcli-parse.js +224 -0
  313. package/dist/sms/normalize.d.ts +42 -0
  314. package/dist/sms/normalize.js +95 -0
  315. package/dist/testing/domain-fakes.d.ts +58 -0
  316. package/dist/testing/domain-fakes.js +98 -0
  317. package/dist/testing/index.d.ts +2 -0
  318. package/dist/testing/index.js +17 -0
  319. package/dist/testing/provider-fakes.d.ts +45 -0
  320. package/dist/testing/provider-fakes.js +64 -0
  321. package/dist/transport/calls.d.ts +9 -0
  322. package/dist/transport/calls.js +88 -0
  323. package/dist/transport/codec.d.ts +3 -0
  324. package/dist/transport/codec.js +207 -0
  325. package/dist/transport/dbus-native.d.ts +57 -0
  326. package/dist/transport/dbus-native.js +17 -0
  327. package/dist/transport/errors.d.ts +21 -0
  328. package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
  329. package/dist/transport/index.d.ts +4 -0
  330. package/dist/transport/index.js +9 -0
  331. package/dist/transport/signals.d.ts +15 -0
  332. package/dist/transport/signals.js +123 -0
  333. package/dist/transport/signature.d.ts +7 -0
  334. package/dist/transport/signature.js +94 -0
  335. package/dist/transport/transport.d.ts +2 -0
  336. package/dist/transport/transport.js +202 -0
  337. package/dist/transport/types.d.ts +61 -0
  338. package/dist/transport/types.js +19 -0
  339. package/dist/usb-mode/catalog-schema.d.ts +139 -0
  340. package/dist/usb-mode/catalog-schema.js +97 -0
  341. package/dist/usb-mode/catalog.d.ts +21 -0
  342. package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
  343. package/dist/usb-mode/certified-catalog.json +67 -0
  344. package/dist/usb-mode/index.d.ts +5 -0
  345. package/dist/usb-mode/index.js +15 -0
  346. package/dist/usb-mode/ingestion.d.ts +111 -0
  347. package/dist/usb-mode/ingestion.js +187 -0
  348. package/dist/usb-mode/promotion-review.d.ts +21 -0
  349. package/dist/usb-mode/promotion-review.js +87 -0
  350. package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
  351. package/dist/usb-mode/usb-devices-parse.js +157 -0
  352. package/dist/ussd/calls.d.ts +32 -0
  353. package/dist/ussd/calls.js +96 -0
  354. package/dist/ussd/index.d.ts +5 -0
  355. package/dist/ussd/index.js +12 -0
  356. package/dist/ussd/mm-ussd.d.ts +37 -0
  357. package/dist/ussd/mm-ussd.js +205 -0
  358. package/dist/ussd/refusal.d.ts +53 -0
  359. package/dist/ussd/refusal.js +154 -0
  360. package/dist/ussd/registration.d.ts +20 -0
  361. package/dist/ussd/registration.js +101 -0
  362. package/dist/ussd/session.d.ts +84 -0
  363. package/dist/ussd/session.js +163 -0
  364. package/package.json +38 -4
  365. package/src/backend/at-lease.test.ts +0 -106
  366. package/src/backend/at-lease.ts +0 -158
  367. package/src/backend/cell-info.test.ts +0 -154
  368. package/src/backend/cell-info.ts +0 -160
  369. package/src/backend/device-classifier.test.ts +0 -168
  370. package/src/backend/device-classifier.ts +0 -248
  371. package/src/backend/enrichment.ts +0 -96
  372. package/src/backend/features.test.ts +0 -162
  373. package/src/backend/features.ts +0 -179
  374. package/src/backend/identity-ladder.test.ts +0 -117
  375. package/src/backend/identity-ladder.ts +0 -221
  376. package/src/backend/identity-registry.test.ts +0 -89
  377. package/src/backend/identity-registry.ts +0 -151
  378. package/src/backend/index.ts +0 -221
  379. package/src/backend/lifecycle-interlock.ts +0 -38
  380. package/src/backend/managed-objects.ts +0 -108
  381. package/src/backend/mapping.ts +0 -160
  382. package/src/backend/mm-backend.ts +0 -191
  383. package/src/backend/mm-mutations.ts +0 -228
  384. package/src/backend/modem-actor.test.ts +0 -95
  385. package/src/backend/modem-actor.ts +0 -112
  386. package/src/backend/nm-auto-apn.ts +0 -161
  387. package/src/backend/nm-gsm-fields.ts +0 -122
  388. package/src/backend/nmcli-nm-port.ts +0 -228
  389. package/src/backend/nmcli-runner.ts +0 -52
  390. package/src/backend/observer.ts +0 -297
  391. package/src/backend/power-contract.test.ts +0 -40
  392. package/src/backend/power-contract.ts +0 -83
  393. package/src/backend/recovery-attribution.test.ts +0 -102
  394. package/src/backend/recovery-attribution.ts +0 -86
  395. package/src/backend/recovery-budget.test.ts +0 -64
  396. package/src/backend/recovery-budget.ts +0 -84
  397. package/src/backend/recovery-ladder.test.ts +0 -257
  398. package/src/backend/recovery-ladder.ts +0 -249
  399. package/src/backend/router-ethernet.test.ts +0 -71
  400. package/src/backend/router-ethernet.ts +0 -90
  401. package/src/backend/row-store.ts +0 -105
  402. package/src/backend/signal-setup.ts +0 -112
  403. package/src/backend/sim-unlock.ts +0 -193
  404. package/src/backend/transition-preconditions.ts +0 -149
  405. package/src/backend/usage/accounting.test.ts +0 -147
  406. package/src/backend/usage/accounting.ts +0 -123
  407. package/src/backend/usage/billing-cycle.test.ts +0 -62
  408. package/src/backend/usage/billing-cycle.ts +0 -45
  409. package/src/backend/usage/index.ts +0 -37
  410. package/src/backend/usage/proc-net-dev.test.ts +0 -56
  411. package/src/backend/usage/sampler.test.ts +0 -219
  412. package/src/backend/usage/sampler.ts +0 -228
  413. package/src/backend/usage/store.test.ts +0 -148
  414. package/src/backend/usage/store.ts +0 -177
  415. package/src/backend/usb-enumerator.test.ts +0 -87
  416. package/src/backend/usb-enumerator.ts +0 -181
  417. package/src/backend/usb-mode-transition.test.ts +0 -323
  418. package/src/backend/usb-mode-transition.ts +0 -253
  419. package/src/domain/brand.ts +0 -29
  420. package/src/domain/errors.ts +0 -77
  421. package/src/domain/guards.test.ts +0 -218
  422. package/src/domain/guards.ts +0 -144
  423. package/src/domain/identity.test.ts +0 -83
  424. package/src/domain/identity.ts +0 -165
  425. package/src/domain/index.ts +0 -12
  426. package/src/domain/snapshot.test.ts +0 -266
  427. package/src/domain/snapshot.ts +0 -120
  428. package/src/index.test.ts +0 -6
  429. package/src/index.ts +0 -15
  430. package/src/ports/README.md +0 -61
  431. package/src/ports/forbidden-surface.test.ts +0 -241
  432. package/src/ports/modem-manager.ts +0 -72
  433. package/src/ports/network-manager.ts +0 -87
  434. package/src/ports/ops.ts +0 -60
  435. package/src/ports/ops.type-test.ts +0 -39
  436. package/src/ports/receipts.test.ts +0 -153
  437. package/src/ports/reconcile.test.ts +0 -152
  438. package/src/ports/reconcile.ts +0 -338
  439. package/src/ports/router.ts +0 -29
  440. package/src/redact.test.ts +0 -82
  441. package/src/redact.ts +0 -73
  442. package/src/transport/README.md +0 -65
  443. package/src/transport/calls.ts +0 -113
  444. package/src/transport/characterization.test.ts +0 -260
  445. package/src/transport/codec.test.ts +0 -118
  446. package/src/transport/codec.ts +0 -240
  447. package/src/transport/conformance-python.test.ts +0 -152
  448. package/src/transport/conformance-same-lib.test.ts +0 -115
  449. package/src/transport/dbus-native-lib.d.ts +0 -19
  450. package/src/transport/dbus-native.ts +0 -85
  451. package/src/transport/index.ts +0 -30
  452. package/src/transport/no-library-leak.test.ts +0 -61
  453. package/src/transport/reliability.test.ts +0 -173
  454. package/src/transport/signals.ts +0 -150
  455. package/src/transport/signature.ts +0 -110
  456. package/src/transport/test-support/fake-service.ts +0 -168
  457. package/src/transport/test-support/independent-producer.py +0 -110
  458. package/src/transport/test-support/private-bus.ts +0 -66
  459. package/src/transport/transport.ts +0 -250
  460. package/src/transport/types.ts +0 -118
  461. package/src/usb-mode/catalog-schema.test.ts +0 -181
  462. package/src/usb-mode/catalog-schema.ts +0 -113
  463. package/src/usb-mode/certified-catalog.json +0 -67
  464. package/src/usb-mode/index.ts +0 -27
@@ -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
+ }
@@ -0,0 +1,48 @@
1
+ // Typed errors for the domain layer.
2
+ //
3
+ // The domain never fails silently: an impossible state combination, a refused
4
+ // policy binding, or a non-monotonic revision each raise a distinct, catchable
5
+ // error class carrying a machine-readable reason. QA and callers discriminate on
6
+ // the class and the `code`, never on a message string.
7
+ /** Base class for every domain-layer error. Callers can catch this to trap all. */
8
+ export class DomainError extends Error {
9
+ name = 'DomainError';
10
+ constructor(message) {
11
+ super(message);
12
+ // Restore the prototype chain across the ES5 target transpile so
13
+ // `instanceof` works on subclasses (standard TS extends-Error guard).
14
+ Object.setPrototypeOf(this, new.target.prototype);
15
+ }
16
+ }
17
+ /** A snapshot was constructed (or transitioned into) a physically impossible state. */
18
+ export class ImpossibleStateError extends DomainError {
19
+ name = 'ImpossibleStateError';
20
+ code;
21
+ constructor(code, detail) {
22
+ super(`impossible cellular state [${code}]: ${detail}`);
23
+ this.code = code;
24
+ }
25
+ }
26
+ /**
27
+ * A durable policy binding was attempted against an identity that is not allowed
28
+ * to carry one — today only low-confidence (ambiguous) equipment identities.
29
+ */
30
+ export class PolicyBindingRefusedError extends DomainError {
31
+ name = 'PolicyBindingRefusedError';
32
+ reason;
33
+ constructor(detail) {
34
+ super(`durable policy binding refused: ${detail}`);
35
+ this.reason = 'ambiguous-identity';
36
+ }
37
+ }
38
+ /** A snapshot transition tried to keep or lower the monotonic revision. */
39
+ export class RevisionMonotonicityError extends DomainError {
40
+ name = 'RevisionMonotonicityError';
41
+ previous;
42
+ next;
43
+ constructor(previous, next) {
44
+ super(`revision must strictly increase: ${previous} -> ${next}`);
45
+ this.previous = previous;
46
+ this.next = next;
47
+ }
48
+ }
@@ -0,0 +1,8 @@
1
+ import type { Brand } from './brand.js';
2
+ /** Monotonic lifetime of one physical enumeration/provider selection. */
3
+ export type DeviceGeneration = Brand<number, 'DeviceGeneration'>;
4
+ export declare function deviceGeneration(value: number): DeviceGeneration;
5
+ /** Re-enumeration or provider replacement starts the next fenced lifetime. */
6
+ export declare function nextDeviceGeneration(current: DeviceGeneration): DeviceGeneration;
7
+ /** Whether an async completion still belongs to the current device lifetime. */
8
+ export declare function isCurrentGeneration(completion: DeviceGeneration, current: DeviceGeneration): boolean;
@@ -0,0 +1,12 @@
1
+ import { nonNegativeInteger } from './brand.js';
2
+ export function deviceGeneration(value) {
3
+ return nonNegativeInteger(value, 'deviceGeneration');
4
+ }
5
+ /** Re-enumeration or provider replacement starts the next fenced lifetime. */
6
+ export function nextDeviceGeneration(current) {
7
+ return deviceGeneration(current + 1);
8
+ }
9
+ /** Whether an async completion still belongs to the current device lifetime. */
10
+ export function isCurrentGeneration(completion, current) {
11
+ return completion === current;
12
+ }
@@ -0,0 +1,6 @@
1
+ import type { ImpossibleStateCode } from './errors.js';
2
+ import type { CellularSnapshot } from './snapshot.js';
3
+ /** Return the first impossible-combination code the snapshot violates, or null. */
4
+ export declare function checkSnapshot(snapshot: CellularSnapshot): ImpossibleStateCode | null;
5
+ /** Throw `ImpossibleStateError` if the snapshot holds an impossible combination. */
6
+ export declare function assertSnapshot(snapshot: CellularSnapshot): void;