@ceralive/modem-control 1.0.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 (474) 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 -236
  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/uhubctl-power-hook.test.ts +0 -274
  406. package/src/backend/uhubctl-power-hook.ts +0 -377
  407. package/src/backend/usage/accounting.test.ts +0 -147
  408. package/src/backend/usage/accounting.ts +0 -123
  409. package/src/backend/usage/billing-cycle.test.ts +0 -62
  410. package/src/backend/usage/billing-cycle.ts +0 -45
  411. package/src/backend/usage/index.ts +0 -60
  412. package/src/backend/usage/policy-store.test.ts +0 -164
  413. package/src/backend/usage/policy-store.ts +0 -216
  414. package/src/backend/usage/policy-write.test.ts +0 -198
  415. package/src/backend/usage/policy-write.ts +0 -207
  416. package/src/backend/usage/proc-net-dev.test.ts +0 -56
  417. package/src/backend/usage/sampler.test.ts +0 -327
  418. package/src/backend/usage/sampler.ts +0 -282
  419. package/src/backend/usage/store.test.ts +0 -148
  420. package/src/backend/usage/store.ts +0 -177
  421. package/src/backend/usb-enumerator.test.ts +0 -87
  422. package/src/backend/usb-enumerator.ts +0 -181
  423. package/src/backend/usb-mode-transition.test.ts +0 -323
  424. package/src/backend/usb-mode-transition.ts +0 -253
  425. package/src/domain/brand.ts +0 -29
  426. package/src/domain/errors.ts +0 -77
  427. package/src/domain/guards.test.ts +0 -218
  428. package/src/domain/guards.ts +0 -144
  429. package/src/domain/identity.test.ts +0 -83
  430. package/src/domain/identity.ts +0 -165
  431. package/src/domain/index.ts +0 -12
  432. package/src/domain/snapshot.test.ts +0 -266
  433. package/src/domain/snapshot.ts +0 -120
  434. package/src/index.test.ts +0 -6
  435. package/src/index.ts +0 -15
  436. package/src/ports/README.md +0 -61
  437. package/src/ports/forbidden-surface.test.ts +0 -241
  438. package/src/ports/modem-manager.ts +0 -72
  439. package/src/ports/network-manager.ts +0 -87
  440. package/src/ports/ops.ts +0 -60
  441. package/src/ports/ops.type-test.ts +0 -39
  442. package/src/ports/receipts.test.ts +0 -153
  443. package/src/ports/reconcile.test.ts +0 -152
  444. package/src/ports/reconcile.ts +0 -338
  445. package/src/ports/router.ts +0 -29
  446. package/src/redact.test.ts +0 -82
  447. package/src/redact.ts +0 -73
  448. package/src/transport/README.md +0 -65
  449. package/src/transport/calls.ts +0 -113
  450. package/src/transport/characterization.test.ts +0 -260
  451. package/src/transport/codec.test.ts +0 -118
  452. package/src/transport/codec.ts +0 -240
  453. package/src/transport/conformance-python.test.ts +0 -152
  454. package/src/transport/conformance-same-lib.test.ts +0 -115
  455. package/src/transport/dbus-native-lib.d.ts +0 -19
  456. package/src/transport/dbus-native.ts +0 -85
  457. package/src/transport/index.ts +0 -30
  458. package/src/transport/no-library-leak.test.ts +0 -61
  459. package/src/transport/reliability.test.ts +0 -173
  460. package/src/transport/signals.ts +0 -150
  461. package/src/transport/signature.ts +0 -110
  462. package/src/transport/test-support/fake-service.ts +0 -168
  463. package/src/transport/test-support/independent-producer.py +0 -110
  464. package/src/transport/test-support/private-bus.ts +0 -66
  465. package/src/transport/transport.ts +0 -250
  466. package/src/transport/types.ts +0 -118
  467. package/src/usb-mode/catalog-schema.test.ts +0 -181
  468. package/src/usb-mode/catalog-schema.ts +0 -113
  469. package/src/usb-mode/certified-catalog.json +0 -67
  470. package/src/usb-mode/index.ts +0 -58
  471. package/src/usb-mode/ingestion.test.ts +0 -268
  472. package/src/usb-mode/ingestion.ts +0 -297
  473. package/src/usb-mode/promotion-review.ts +0 -117
  474. package/src/usb-mode/usb-devices-parse.ts +0 -196
@@ -1,241 +0,0 @@
1
- // Guard: no port interface may ever declare a bearer / connect method.
2
- //
3
- // The single most safety-critical constraint in the package (Must-NOT-Have: "no MM
4
- // Simple.Connect / CreateBearer / Bearer.Connect calls ever"). Interfaces are
5
- // erased at runtime, so the enforcement is a source scan: every port `.ts` file is
6
- // parsed with the TypeScript compiler API and each DECLARED member of an interface,
7
- // type literal, or class is checked for a name that is `connect`, `simpleConnect`,
8
- // or contains "bearer". Adding such a member to any port fails this test — and CI.
9
- //
10
- // The detector walks the real AST rather than the source text, so it catches shapes
11
- // a naive regex cannot: method signatures, property signatures typed as function
12
- // types, get/set accessors, optional members, and quoted or computed string-literal
13
- // member names (`'connect'()`, `['connect']()`) all resolve to their true name. It
14
- // only ever inspects member NAMES on those three declaration kinds — never string
15
- // literal values, value-position object-literal keys, comments, local variables, or
16
- // call expressions — so prose mentions of "bearer" and value keys never trip it.
17
-
18
- import { expect, test } from 'bun:test';
19
- import { readdirSync } from 'node:fs';
20
- import { join } from 'node:path';
21
- import * as ts from 'typescript';
22
-
23
- const portsDir = import.meta.dir;
24
-
25
- function isForbiddenMethodName(name: string): boolean {
26
- const lower = name.toLowerCase();
27
- return lower === 'connect' || lower === 'simpleconnect' || lower.includes('bearer');
28
- }
29
-
30
- /**
31
- * Resolve a declared member's name node to its static string name, or `undefined`
32
- * when the name is not statically knowable (e.g. a computed `[Symbol.iterator]` or a
33
- * computed reference to a non-literal identifier). Plain identifiers, private names,
34
- * string / numeric literals, and computed names wrapping a string / numeric literal
35
- * (`['connect']`) all resolve to their real text.
36
- */
37
- function resolveMemberName(name: ts.Node): string | undefined {
38
- if (ts.isIdentifier(name) || ts.isPrivateIdentifier(name)) {
39
- return name.text;
40
- }
41
- if (ts.isStringLiteralLike(name) || ts.isNumericLiteral(name)) {
42
- return name.text;
43
- }
44
- if (ts.isComputedPropertyName(name)) {
45
- const { expression } = name;
46
- if (ts.isStringLiteralLike(expression) || ts.isNumericLiteral(expression)) {
47
- return expression.text;
48
- }
49
- }
50
- return undefined;
51
- }
52
-
53
- /**
54
- * Every DECLARED member name of an interface, type literal, or class in `source`,
55
- * parsed via the TypeScript compiler API. Value-position object-literal keys, string
56
- * literal values, comments, local variables, and call expressions are all excluded —
57
- * only the port's actual type surface is inspected. Method signatures, property
58
- * signatures (including arrow-typed properties), get/set accessors, optional members,
59
- * and quoted / computed string-literal member names are all resolved to their name.
60
- */
61
- function declaredMemberNames(source: string): string[] {
62
- const sourceFile = ts.createSourceFile(
63
- 'port.ts',
64
- source,
65
- ts.ScriptTarget.Latest,
66
- /* setParentNodes */ true,
67
- );
68
- const names: string[] = [];
69
-
70
- const collectMembers = (members: readonly (ts.TypeElement | ts.ClassElement)[]): void => {
71
- for (const member of members) {
72
- if (member.name === undefined) {
73
- continue;
74
- }
75
- const resolved = resolveMemberName(member.name);
76
- if (resolved !== undefined) {
77
- names.push(resolved);
78
- }
79
- }
80
- };
81
-
82
- const visit = (node: ts.Node): void => {
83
- if (
84
- ts.isInterfaceDeclaration(node) ||
85
- ts.isClassDeclaration(node) ||
86
- ts.isClassExpression(node) ||
87
- ts.isTypeLiteralNode(node)
88
- ) {
89
- collectMembers(node.members);
90
- }
91
- ts.forEachChild(node, visit);
92
- };
93
-
94
- visit(sourceFile);
95
- return names;
96
- }
97
-
98
- // The retired regex detector — kept ONLY so the differential test below can prove,
99
- // by running it, that the AST rebuild is a strict improvement (it caught just one of
100
- // the seven forbidden shapes). Not used by any real-port scan; do not reintroduce.
101
- function stripComments(source: string): string {
102
- return source.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/[^\n]*/g, '');
103
- }
104
-
105
- function legacyRegexMemberNames(source: string): string[] {
106
- const names: string[] = [];
107
- const methodDecl = /(?:^|\n)\s*([a-zA-Z_$][\w$]*)\s*[<(]/g;
108
- let match = methodDecl.exec(source);
109
- while (match !== null) {
110
- const name = match[1];
111
- if (name !== undefined) {
112
- names.push(name);
113
- }
114
- match = methodDecl.exec(source);
115
- }
116
- return names;
117
- }
118
-
119
- function portSourceFiles(): string[] {
120
- return readdirSync(portsDir)
121
- .filter((file) => file.endsWith('.ts'))
122
- .filter((file) => !file.endsWith('.test.ts') && !file.endsWith('.type-test.ts'));
123
- }
124
-
125
- test('no port source declares a bearer / connect method', async () => {
126
- for (const file of portSourceFiles()) {
127
- const source = await Bun.file(join(portsDir, file)).text();
128
- const forbidden = declaredMemberNames(source).filter(isForbiddenMethodName);
129
- expect(forbidden, `forbidden member(s) in ${file}: ${forbidden.join(', ')}`).toEqual([]);
130
- }
131
- });
132
-
133
- test('the ModemManager port source declares the expected non-bearer mutations', async () => {
134
- const source = await Bun.file(join(portsDir, 'modem-manager.ts')).text();
135
- const names = declaredMemberNames(source);
136
- for (const expected of [
137
- 'setRadioModes',
138
- 'setPrimarySimSlot',
139
- 'sendPin',
140
- 'sendPuk',
141
- 'scanNetworks',
142
- 'inhibit',
143
- 'uninhibit',
144
- ]) {
145
- expect(names).toContain(expected);
146
- }
147
- });
148
-
149
- test('the detector catches every forbidden member shape (self-test)', () => {
150
- // Each line is a DISTINCT declaration shape the AST resolver must catch: a plain
151
- // method, a computed string-literal method, a quoted string-literal method, an
152
- // optional method, a get accessor, a set accessor, and an arrow-typed property.
153
- // `setRadioModes` is a legitimate mutation that must pass through unflagged.
154
- const rogue = `
155
- export interface RogueSurface {
156
- connect(): Promise<void>;
157
- ['simpleConnect'](): void;
158
- 'createBearer'(): void;
159
- bearerReset?(): void;
160
- get bearerState(): number;
161
- set bearerTarget(value: number);
162
- bearerHook: () => Promise<void>;
163
- setRadioModes(): Promise<void>;
164
- }
165
- `;
166
- const flagged = declaredMemberNames(rogue).filter(isForbiddenMethodName).sort();
167
- expect(flagged).toEqual([
168
- 'bearerHook',
169
- 'bearerReset',
170
- 'bearerState',
171
- 'bearerTarget',
172
- 'connect',
173
- 'createBearer',
174
- 'simpleConnect',
175
- ]);
176
- // The acceptance bar for the rebuild: at least seven distinct forbidden shapes.
177
- expect(flagged.length).toBeGreaterThanOrEqual(7);
178
- });
179
-
180
- test('the detector ignores string values, value-position keys, and non-forbidden members', () => {
181
- // Three negative fixtures that must NOT trip the detector:
182
- // 1. `reconnectPolicy` — a real interface member whose name merely CONTAINS the
183
- // substring "connect"; the predicate matches "connect" exactly, not as a
184
- // substring, so it must pass (proves the predicate is not overly broad).
185
- // 2. `description: '…bearer…'` — a member whose string-literal VALUE contains
186
- // "bearer"; only member NAMES are inspected, never string content.
187
- // 3. `bearerConnect: true` — a key in a value-position object literal, not a
188
- // type-level member declaration; the AST never walks object-literal keys.
189
- const negative = `
190
- interface LegitPort {
191
- reconnectPolicy: RetryPolicy;
192
- setRadioModes(): Promise<void>;
193
- description: 'manages bearer state internally';
194
- }
195
- const runtimeConfig = {
196
- bearerConnect: true,
197
- description: 'manages bearer state internally',
198
- };
199
- `;
200
- const members = declaredMemberNames(negative);
201
- expect(members).toContain('reconnectPolicy');
202
- expect(members).not.toContain('bearerConnect');
203
- expect(members.filter(isForbiddenMethodName)).toEqual([]);
204
- });
205
-
206
- test('the AST detector catches member shapes the legacy regex missed (differential)', () => {
207
- // The same rogue surface, run through BOTH detectors. The retired regex only ever
208
- // matched an identifier immediately followed by `(` or `<` at a line start, so it
209
- // caught the single plain method and missed the other six shapes. The AST walk
210
- // catches all seven — the strict improvement the rebuild delivers.
211
- const rogue = `
212
- export interface RogueSurface {
213
- connect(): Promise<void>;
214
- ['simpleConnect'](): void;
215
- 'createBearer'(): void;
216
- bearerReset?(): void;
217
- get bearerState(): number;
218
- set bearerTarget(value: number);
219
- bearerHook: () => Promise<void>;
220
- }
221
- `;
222
- const legacyFlagged = legacyRegexMemberNames(stripComments(rogue))
223
- .filter(isForbiddenMethodName)
224
- .sort();
225
- const astFlagged = declaredMemberNames(rogue).filter(isForbiddenMethodName).sort();
226
-
227
- // The regex caught only the plain method.
228
- expect(legacyFlagged).toEqual(['connect']);
229
- // The AST catches every shape the regex missed, and then some.
230
- for (const missed of [
231
- 'bearerHook',
232
- 'bearerReset',
233
- 'bearerState',
234
- 'bearerTarget',
235
- 'createBearer',
236
- 'simpleConnect',
237
- ]) {
238
- expect(legacyFlagged).not.toContain(missed);
239
- expect(astFlagged).toContain(missed);
240
- }
241
- });
@@ -1,72 +0,0 @@
1
- // The ModemManager port — radio, SIM, scan, inhibit, and observation.
2
- //
3
- // CRITICAL INVARIANT (the single most safety-critical constraint in the package):
4
- // this port has NO bearer / connection verb. There is no connect, no createBearer,
5
- // no bearerConnect — MM's Simple.Connect / CreateBearer / Bearer.Connect are NEVER
6
- // callable through here. NetworkManager is the sole owner of bearers and activation
7
- // (see README ownership table). `forbidden-surface.test.ts` fails the build if any
8
- // such method is ever added to this file.
9
-
10
- import type { DesiredRadio, EpochMillis, RuntimePath } from '../domain';
11
- import type { ModemObservationPort } from './observation';
12
- import type { Receipt } from './receipts';
13
-
14
- /** A live handle to one modem — its ModemManager D-Bus object path (per-boot). */
15
- export type ModemRef = RuntimePath;
16
-
17
- /** Outcome of a SIM PIN unlock attempt. */
18
- export interface SimUnlockResult {
19
- readonly outcome: 'unlocked' | 'incorrect-pin' | 'sim-puk-required' | 'unsupported' | 'error';
20
- readonly remainingAttempts?: number;
21
- readonly reason: string;
22
- }
23
-
24
- /** Outcome of a SIM PUK unblock attempt. */
25
- export interface SimPukUnlockResult {
26
- readonly outcome: 'unlocked' | 'incorrect-puk' | 'permanently-blocked' | 'unsupported' | 'error';
27
- readonly remainingAttempts?: number;
28
- readonly reason: string;
29
- }
30
-
31
- /** One operator returned by a network scan. */
32
- export interface ScannedNetwork {
33
- /** MCC+MNC operator code. */
34
- readonly operatorCode: string;
35
- readonly operatorName?: string;
36
- readonly availability: 'available' | 'current' | 'forbidden' | 'unknown';
37
- }
38
-
39
- /** Result of a network scan — discriminated, retaining the reason on failure. */
40
- export type NetworkScanResult =
41
- | { readonly ok: true; readonly networks: readonly ScannedNetwork[] }
42
- | { readonly ok: false; readonly reason: string };
43
-
44
- /** A held inhibition over a modem, released via `uninhibit`. */
45
- export interface InhibitLease {
46
- /** The equipment UID the inhibition is keyed to. */
47
- readonly uid: string;
48
- readonly acquiredAt: EpochMillis;
49
- }
50
-
51
- /**
52
- * The ModemManager port — EXTENDS the read-only observation port with the modem
53
- * mutations MM legitimately owns: radio modes, primary SIM slot, PIN / PUK unlock,
54
- * network scan, and inhibit / uninhibit. It owns NO bearer / connection lifecycle;
55
- * bearers and activation belong to `NetworkManagerPort`.
56
- */
57
- export interface ModemManagerPort extends ModemObservationPort {
58
- /** Set the modem's radio access-technology preference. */
59
- setRadioModes(modem: ModemRef, preference: DesiredRadio): Promise<Receipt>;
60
- /** Select the primary SIM slot (multi-slot modems only). */
61
- setPrimarySimSlot(modem: ModemRef, slotIndex: number): Promise<Receipt>;
62
- /** Submit a SIM PIN (exactly-once; read-before-submit is the adapter's job). */
63
- sendPin(modem: ModemRef, pin: string): Promise<SimUnlockResult>;
64
- /** Submit a SIM PUK plus the new PIN (exactly-once). */
65
- sendPuk(modem: ModemRef, puk: string, newPin: string): Promise<SimPukUnlockResult>;
66
- /** Scan for visible networks (long-running). */
67
- scanNetworks(modem: ModemRef): Promise<NetworkScanResult>;
68
- /** Inhibit MM from managing a device (for a maintenance lease), keyed by UID. */
69
- inhibit(uid: string): Promise<InhibitLease>;
70
- /** Release a previously-taken inhibition. */
71
- uninhibit(lease: InhibitLease): Promise<void>;
72
- }
@@ -1,87 +0,0 @@
1
- // The NetworkManager port — NM is the SOLE owner of bearers, APN, auth, roaming,
2
- // autoconnect, and activation (see README ownership table). This port is the ONLY
3
- // way the controller touches any of those resources; the ModemManager port has no
4
- // bearer / connection verbs at all.
5
-
6
- import type { Brand, EpochMillis } from '../domain';
7
- import { nonEmptyString } from '../domain';
8
- import type { Receipt } from './receipts';
9
-
10
- /** A NetworkManager connection-profile UUID. */
11
- export type ConnectionId = Brand<string, 'ConnectionId'>;
12
-
13
- /** A kernel network-device interface name (e.g. `wwan0`). */
14
- export type DeviceIfname = Brand<string, 'DeviceIfname'>;
15
-
16
- /** Construct a `ConnectionId` from a non-empty NM connection UUID. */
17
- export function connectionId(value: string): ConnectionId {
18
- return nonEmptyString(value, 'connectionId') as ConnectionId;
19
- }
20
-
21
- /** Construct a `DeviceIfname` from a non-empty interface name. */
22
- export function deviceIfname(value: string): DeviceIfname {
23
- return nonEmptyString(value, 'deviceIfname') as DeviceIfname;
24
- }
25
-
26
- /**
27
- * A GSM connection profile — NM's `gsm.*` setting group. `password` is SENSITIVE
28
- * and MUST be redacted in every log / output (see `../redact`). The full nine-field
29
- * nmcli write parity lands with the concrete adapter (A4.1); this is the port
30
- * contract that adapter fulfils.
31
- */
32
- export interface GsmProfileInput {
33
- readonly connectionName: string;
34
- /** Concrete APN, or the empty string when `autoConfig` drives it (A4.1). */
35
- readonly apn: string;
36
- readonly username?: string;
37
- /** SENSITIVE — redact everywhere. */
38
- readonly password?: string;
39
- /** `true` ⇒ `gsm.home-only` (roaming disabled). */
40
- readonly homeOnly: boolean;
41
- /** `true` ⇒ `gsm.auto-config yes` (Auto-APN); mutually exclusive with creds. */
42
- readonly autoConfig: boolean;
43
- /** Manual operator selection (`gsm.network-id`); empty unless roaming-pinned. */
44
- readonly networkId?: string;
45
- }
46
-
47
- /** A persisted GSM profile as read back from NM, keyed by its connection id. */
48
- export interface GsmProfile extends GsmProfileInput {
49
- readonly connectionId: ConnectionId;
50
- }
51
-
52
- /** A partial update to an existing GSM profile. */
53
- export type GsmProfilePatch = Partial<GsmProfileInput>;
54
-
55
- /**
56
- * A quiesce lease — a held guarantee that the connection on THIS exact device stays
57
- * deactivated while a disruptive MM operation runs, then is reactivated on release.
58
- * Bound to BOTH `connectionId` and `deviceIfname` so it can never quiesce the wrong
59
- * device (the two-device isolation invariant, A4.1).
60
- */
61
- export interface QuiesceLease {
62
- readonly connectionId: ConnectionId;
63
- readonly deviceIfname: DeviceIfname;
64
- readonly acquiredAt: EpochMillis;
65
- }
66
-
67
- /**
68
- * The NetworkManager port. Activation and deactivation take BOTH the connection id
69
- * AND the device interface name — never an id alone. This encodes, at the type
70
- * level, that we address a connection on an EXACT device (`nmcli connection up
71
- * <uuid> ifname <dev>` / verify-then-`device disconnect <ifname>`); the id-only
72
- * `nmcli connection down` is structurally impossible to express here (A4.1).
73
- */
74
- export interface NetworkManagerPort {
75
- createGsmProfile(profile: GsmProfileInput): Promise<GsmProfile>;
76
- readGsmProfile(id: ConnectionId): Promise<GsmProfile | undefined>;
77
- updateGsmProfile(id: ConnectionId, patch: GsmProfilePatch): Promise<GsmProfile>;
78
- deleteGsmProfile(id: ConnectionId): Promise<void>;
79
- /** Activate `id` on the EXACT device `ifname`. */
80
- activate(id: ConnectionId, ifname: DeviceIfname): Promise<Receipt>;
81
- /** Deactivate `id` on the EXACT device `ifname`. */
82
- deactivate(id: ConnectionId, ifname: DeviceIfname): Promise<Receipt>;
83
- /** Take a quiesce lease over `id` on the EXACT device `ifname`. */
84
- acquireQuiesceLease(id: ConnectionId, ifname: DeviceIfname): Promise<QuiesceLease>;
85
- /** Release a quiesce lease, reactivating the connection it held down. */
86
- releaseQuiesceLease(lease: QuiesceLease): Promise<void>;
87
- }
package/src/ports/ops.ts DELETED
@@ -1,60 +0,0 @@
1
- // Port-tagged reconcile ops — a planned mutation TAGGED with the port that owns it.
2
- //
3
- // The op kinds are DISJOINT by construction: a radio / SIM op (`MmOp`) shares no
4
- // `kind` with a connection / APN op (`NmOp`). Tagging is therefore type-checked —
5
- // `{ port: 'nm', op: { kind: 'setRadioModes', … } }` does NOT compile, because a
6
- // radio op is not assignable to the NM arm. That mis-tag is a COMPILE-TIME error
7
- // (see `ops.type-test.ts` for the `@ts-expect-error` proofs). This is the ownership
8
- // matrix (README) enforced by the type system, not merely by convention.
9
-
10
- import type { DesiredRadio } from '../domain';
11
- import type {
12
- ConnectionId,
13
- DeviceIfname,
14
- GsmProfileInput,
15
- GsmProfilePatch,
16
- } from './network-manager';
17
-
18
- /** Radio + SIM ops — owned SOLELY by the ModemManager port. */
19
- export type MmOp =
20
- | { readonly kind: 'setRadioModes'; readonly preference: DesiredRadio }
21
- | { readonly kind: 'setPrimarySimSlot'; readonly slotIndex: number };
22
-
23
- /** Connection / APN / activation ops — owned SOLELY by the NetworkManager port. */
24
- export type NmOp =
25
- | { readonly kind: 'createGsmProfile'; readonly profile: GsmProfileInput }
26
- | {
27
- readonly kind: 'updateGsmProfile';
28
- readonly connectionId: ConnectionId;
29
- readonly patch: GsmProfilePatch;
30
- }
31
- | {
32
- readonly kind: 'activate';
33
- readonly connectionId: ConnectionId;
34
- readonly deviceIfname: DeviceIfname;
35
- }
36
- | {
37
- readonly kind: 'deactivate';
38
- readonly connectionId: ConnectionId;
39
- readonly deviceIfname: DeviceIfname;
40
- };
41
-
42
- /**
43
- * A planned op tagged with its owning port. The two arms are disjoint on BOTH the
44
- * `port` discriminant AND the op `kind` space, so the planner cannot emit — and a
45
- * reviewer cannot write — a radio op under the NM port, or a connection op under
46
- * the MM port.
47
- */
48
- export type PortTaggedOp =
49
- | { readonly port: 'mm'; readonly op: MmOp }
50
- | { readonly port: 'nm'; readonly op: NmOp };
51
-
52
- /** Tag an MM op for the ModemManager port. Only an `MmOp` is accepted. */
53
- export function mmOp(op: MmOp): PortTaggedOp {
54
- return { port: 'mm', op };
55
- }
56
-
57
- /** Tag an NM op for the NetworkManager port. Only an `NmOp` is accepted. */
58
- export function nmOp(op: NmOp): PortTaggedOp {
59
- return { port: 'nm', op };
60
- }
@@ -1,39 +0,0 @@
1
- // COMPILE-TIME negative tests for port-tagged op disjointness.
2
- //
3
- // This file is type-checked by `tsc --noEmit` (it is inside the workspace
4
- // `include`) but is NOT a runtime test — it is never imported and every binding is
5
- // a type assertion. Each `@ts-expect-error` asserts the type system REJECTS a
6
- // mis-tagged op; if disjointness ever regresses (e.g. an `NmOp` gains a radio
7
- // `kind`), the directive becomes UNUSED and `tsc` fails the build. That failure IS
8
- // the ownership matrix enforced at the type level.
9
-
10
- import { connectionId, deviceIfname } from './network-manager';
11
- import type { MmOp, NmOp, PortTaggedOp } from './ops';
12
-
13
- const radioOp: MmOp = { kind: 'setRadioModes', preference: { preferenceOrdered: ['5gnr'] } };
14
- const slotOp: MmOp = { kind: 'setPrimarySimSlot', slotIndex: 2 };
15
- const activateOp: NmOp = {
16
- kind: 'activate',
17
- connectionId: connectionId('11111111-1111-1111-1111-111111111111'),
18
- deviceIfname: deviceIfname('wwan0'),
19
- };
20
-
21
- // Positive controls — correctly-tagged ops MUST type-check.
22
- const goodMm: PortTaggedOp = { port: 'mm', op: radioOp };
23
- const goodNm: PortTaggedOp = { port: 'nm', op: activateOp };
24
-
25
- // @ts-expect-error a radio op tagged for the NM port must NOT compile.
26
- const badRadioOnNm: PortTaggedOp = { port: 'nm', op: radioOp };
27
- // @ts-expect-error a SIM op tagged for the NM port must NOT compile.
28
- const badSlotOnNm: PortTaggedOp = { port: 'nm', op: slotOp };
29
- // @ts-expect-error an activate op tagged for the MM port must NOT compile.
30
- const badActivateOnMm: PortTaggedOp = { port: 'mm', op: activateOp };
31
-
32
- // Exported so the bindings are "used" and lint stays quiet; never imported at runtime.
33
- export const PORT_TAGGED_OP_TYPE_CASES: readonly PortTaggedOp[] = [
34
- goodMm,
35
- goodNm,
36
- badRadioOnNm,
37
- badSlotOnNm,
38
- badActivateOnMm,
39
- ];
@@ -1,153 +0,0 @@
1
- // Receipt taxonomy — every status reachable, "prefer 5G on 4G-only" is surfaced as
2
- // `unsupported` (never silently downgraded), and every receipt carries a reason.
3
-
4
- import { expect, test } from 'bun:test';
5
- import {
6
- type DesiredCellularPolicy,
7
- defaultCellularPolicy,
8
- imeiEquipmentId,
9
- type PolicyBindingKey,
10
- policyBindingKey,
11
- runtimePath,
12
- } from '../domain';
13
- import { connectionId, deviceIfname } from './network-manager';
14
- import type { PolicyDimension, Receipt } from './receipts';
15
- import { type AppliedCellularState, type ModemCapabilities, planReconcile } from './reconcile';
16
-
17
- const BOUND_TO: PolicyBindingKey = policyBindingKey({
18
- equipmentId: imeiEquipmentId('490154203237518'),
19
- runtimePath: runtimePath('/org/freedesktop/ModemManager1/Modem/0'),
20
- });
21
-
22
- const FOUR_G_ONLY: ModemCapabilities = {
23
- supportedRats: new Set(['lte', 'umts', 'gsm']),
24
- simSlotCount: 1,
25
- supportsAutoApn: true,
26
- };
27
-
28
- const FIVE_G_DUAL_SLOT: ModemCapabilities = {
29
- supportedRats: new Set(['5gnr', 'lte', 'umts', 'gsm']),
30
- simSlotCount: 2,
31
- supportsAutoApn: true,
32
- };
33
-
34
- const ADDRESSABLE = {
35
- connectionId: connectionId('conn-uuid'),
36
- deviceIfname: deviceIfname('wwan0'),
37
- appliedApn: 'auto',
38
- appliedRoaming: false,
39
- appliedRadioPreference: ['5gnr', 'lte', 'umts', 'gsm'],
40
- } as const;
41
-
42
- function receiptFor(receipts: readonly Receipt[], dimension: PolicyDimension): Receipt {
43
- const found = receipts.find((entry) => entry.dimension === dimension);
44
- if (found === undefined) {
45
- throw new Error(`no receipt for dimension ${dimension}`);
46
- }
47
- return found;
48
- }
49
-
50
- test('prefer 5G on a 4G-only modem is unsupported with a reason, never silently applied', () => {
51
- const policy = defaultCellularPolicy(BOUND_TO);
52
- const applied: AppliedCellularState = { nmActivation: 'disconnected', hasProfile: false };
53
- const { ops, receipts } = planReconcile(applied, policy, FOUR_G_ONLY);
54
-
55
- const radio = receiptFor(receipts, 'radio');
56
- expect(radio.status).toBe('unsupported');
57
- expect(radio.reason).toContain('5gnr');
58
- expect(radio.reason.length).toBeGreaterThan(0);
59
- expect(ops.filter((op) => op.port === 'mm' && op.op.kind === 'setRadioModes')).toEqual([]);
60
- });
61
-
62
- test('all four receipt statuses are reachable', () => {
63
- const policy = defaultCellularPolicy(BOUND_TO);
64
-
65
- const applied: AppliedCellularState = {
66
- nmActivation: 'activated',
67
- hasProfile: true,
68
- ...ADDRESSABLE,
69
- };
70
- expect(
71
- receiptFor(planReconcile(applied, policy, FIVE_G_DUAL_SLOT).receipts, 'enabled').status,
72
- ).toBe('applied');
73
-
74
- const pending: AppliedCellularState = {
75
- nmActivation: 'disconnected',
76
- hasProfile: true,
77
- ...ADDRESSABLE,
78
- };
79
- expect(
80
- receiptFor(planReconcile(pending, policy, FIVE_G_DUAL_SLOT).receipts, 'enabled').status,
81
- ).toBe('pending');
82
-
83
- const unsupported: AppliedCellularState = { nmActivation: 'disconnected', hasProfile: false };
84
- expect(receiptFor(planReconcile(unsupported, policy, FOUR_G_ONLY).receipts, 'radio').status).toBe(
85
- 'unsupported',
86
- );
87
-
88
- const failed: AppliedCellularState = {
89
- nmActivation: 'failed',
90
- hasProfile: true,
91
- activationFailureReason: 'no-signal',
92
- ...ADDRESSABLE,
93
- };
94
- const failedReceipt = receiptFor(
95
- planReconcile(failed, policy, FIVE_G_DUAL_SLOT).receipts,
96
- 'enabled',
97
- );
98
- expect(failedReceipt.status).toBe('failed');
99
- expect(failedReceipt.reason).toContain('no-signal');
100
- });
101
-
102
- test('every dimension yields a receipt and every receipt has a non-empty reason', () => {
103
- const policy = defaultCellularPolicy(BOUND_TO);
104
- const { receipts } = planReconcile(
105
- { nmActivation: 'disconnected', hasProfile: false },
106
- policy,
107
- FOUR_G_ONLY,
108
- );
109
- for (const entry of receipts) {
110
- expect(entry.reason.length).toBeGreaterThan(0);
111
- }
112
- const expectedDimensions: PolicyDimension[] = [
113
- 'connection',
114
- 'enabled',
115
- 'radio',
116
- 'recovery',
117
- 'roaming',
118
- 'simSlot',
119
- 'usage',
120
- ];
121
- expect(receipts.map((entry) => entry.dimension).sort()).toEqual(expectedDimensions.sort());
122
- });
123
-
124
- test('primary SIM slot selection is unsupported on a single-slot modem', () => {
125
- const policy: DesiredCellularPolicy = { ...defaultCellularPolicy(BOUND_TO), simSlot: 2 };
126
- const { receipts } = planReconcile(
127
- { nmActivation: 'disconnected', hasProfile: false },
128
- policy,
129
- FOUR_G_ONLY,
130
- );
131
- expect(receiptFor(receipts, 'simSlot').status).toBe('unsupported');
132
- });
133
-
134
- test('an out-of-range primary SIM slot is a failed receipt', () => {
135
- const policy: DesiredCellularPolicy = { ...defaultCellularPolicy(BOUND_TO), simSlot: 5 };
136
- const { receipts } = planReconcile(
137
- { nmActivation: 'disconnected', hasProfile: false },
138
- policy,
139
- FIVE_G_DUAL_SLOT,
140
- );
141
- expect(receiptFor(receipts, 'simSlot').status).toBe('failed');
142
- });
143
-
144
- test('Auto-APN is unsupported when the stack cannot auto-configure', () => {
145
- const policy = defaultCellularPolicy(BOUND_TO);
146
- const caps: ModemCapabilities = { ...FIVE_G_DUAL_SLOT, supportsAutoApn: false };
147
- const { receipts } = planReconcile(
148
- { nmActivation: 'disconnected', hasProfile: false },
149
- policy,
150
- caps,
151
- );
152
- expect(receiptFor(receipts, 'connection').status).toBe('unsupported');
153
- });