@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,53 @@
1
+ /**
2
+ * Every way a USSD verb can fail to do what the operator asked.
3
+ *
4
+ * unsupported — this modem exposes no USSD interface at all.
5
+ * lte-only-unsupported — the modem is registered PS-only (LTE/5G with no CS
6
+ * domain) and the network refused. NOT a device fault.
7
+ * carrier-rejected — the network refused for some other reason.
8
+ * not-registered — there is no network to carry the session.
9
+ * session-busy — a session is already open (locally or network-side).
10
+ * no-session — respond/cancel with nothing open.
11
+ * invalid-state — the verb is wrong for the state the session is in.
12
+ * timeout — the bounded wait elapsed with no answer.
13
+ * transport-failed — the bus call itself failed; the modem never answered.
14
+ */
15
+ export declare const USSD_REFUSAL_REASONS: readonly ["unsupported", "lte-only-unsupported", "carrier-rejected", "not-registered", "session-busy", "no-session", "invalid-state", "timeout", "transport-failed"];
16
+ export type UssdRefusalReason = (typeof USSD_REFUSAL_REASONS)[number];
17
+ /**
18
+ * What the modem is registered on, as far as anyone has looked.
19
+ *
20
+ * `csDomain` is the load-bearing field: `false` means the modem is attached with
21
+ * NO circuit-switched domain available (LTE/5G-SA without CSFB), which is the
22
+ * registration on which a plain USSD refusal is a carrier-policy statement rather
23
+ * than a device one. `undefined` means nobody read it, and is never treated as
24
+ * `false`.
25
+ */
26
+ export interface UssdRegistrationFacts {
27
+ readonly registered: boolean;
28
+ /** Is a circuit-switched domain available on this registration? */
29
+ readonly csDomain?: boolean;
30
+ /** MM access technologies currently in use, lowercased (`lte`, `5gnr`, …). */
31
+ readonly accessTechnologies?: readonly string[];
32
+ }
33
+ /**
34
+ * True when the modem's registration positively cannot carry a circuit-switched
35
+ * service. Requires evidence in BOTH directions: something must say the CS domain
36
+ * is absent, and every technology in use must be a packet-only one. A modem that
37
+ * reported no technologies at all answers `false` — an empty list is a statement
38
+ * about the read.
39
+ */
40
+ export declare function isPacketSwitchedOnly(facts: UssdRegistrationFacts): boolean;
41
+ /**
42
+ * Classify a failed USSD call. Pure, total, never throws.
43
+ *
44
+ * Order is deliberate: the D-Bus error NAME is the strongest signal and is read
45
+ * first; the message text is consulted only for the names MM overloads; anything
46
+ * unrecognised stays `transport-failed`, which is the honest answer for a failure
47
+ * whose origin cannot be attributed to the network.
48
+ *
49
+ * The PS-only promotion runs LAST and applies to exactly the two reasons that are
50
+ * ambiguous between a device limit and a carrier policy — a `not-registered` or a
51
+ * bus failure is neither, and is left alone.
52
+ */
53
+ export declare function classifyUssdFailure(error: unknown, registration?: UssdRegistrationFacts): UssdRefusalReason;
@@ -0,0 +1,154 @@
1
+ // Why a USSD verb did not succeed — one typed vocabulary, and the classifier that
2
+ // maps ModemManager's own failure surface onto it.
3
+ //
4
+ // The point of typing these is that every member names a DIFFERENT thing the
5
+ // operator can do, and the one this module exists for is the honest reporting of
6
+ // a carrier that will not carry USSD at all on the registration the modem has.
7
+ //
8
+ // THE LTE-ONLY CASE, stated plainly. USSD is a circuit-switched supplementary
9
+ // service. A modem registered PS-only — LTE or 5G-SA with no CS domain and no
10
+ // CSFB — can only carry it if the operator deploys USSI (USSD over IMS, 3GPP TS
11
+ // 24.390); many do not, and the modem/network then answers a generic
12
+ // unsupported/failed error that is indistinguishable, on its face, from "this
13
+ // modem has no USSD interface". Reporting that as a device limitation would send
14
+ // an operator hunting for a firmware fix for a network policy, so the classifier
15
+ // takes the modem's REGISTRATION alongside the error and separates the two.
16
+ //
17
+ // Nothing here guesses: `lte-only-unsupported` is claimed ONLY when the
18
+ // registration is positively known to be PS-only. An unknown registration keeps
19
+ // the generic reason, because "we did not look" is not evidence.
20
+ /**
21
+ * Every way a USSD verb can fail to do what the operator asked.
22
+ *
23
+ * unsupported — this modem exposes no USSD interface at all.
24
+ * lte-only-unsupported — the modem is registered PS-only (LTE/5G with no CS
25
+ * domain) and the network refused. NOT a device fault.
26
+ * carrier-rejected — the network refused for some other reason.
27
+ * not-registered — there is no network to carry the session.
28
+ * session-busy — a session is already open (locally or network-side).
29
+ * no-session — respond/cancel with nothing open.
30
+ * invalid-state — the verb is wrong for the state the session is in.
31
+ * timeout — the bounded wait elapsed with no answer.
32
+ * transport-failed — the bus call itself failed; the modem never answered.
33
+ */
34
+ export const USSD_REFUSAL_REASONS = [
35
+ 'unsupported',
36
+ 'lte-only-unsupported',
37
+ 'carrier-rejected',
38
+ 'not-registered',
39
+ 'session-busy',
40
+ 'no-session',
41
+ 'invalid-state',
42
+ 'timeout',
43
+ 'transport-failed',
44
+ ];
45
+ /** Access technologies that carry no circuit-switched domain of their own. */
46
+ const PACKET_ONLY_RATS = new Set([
47
+ 'lte',
48
+ '5gnr',
49
+ 'lte-cat-m',
50
+ 'lte-nb-iot',
51
+ ]);
52
+ /**
53
+ * True when the modem's registration positively cannot carry a circuit-switched
54
+ * service. Requires evidence in BOTH directions: something must say the CS domain
55
+ * is absent, and every technology in use must be a packet-only one. A modem that
56
+ * reported no technologies at all answers `false` — an empty list is a statement
57
+ * about the read.
58
+ */
59
+ export function isPacketSwitchedOnly(facts) {
60
+ if (!facts.registered) {
61
+ return false;
62
+ }
63
+ if (facts.csDomain !== false) {
64
+ return false;
65
+ }
66
+ const rats = facts.accessTechnologies;
67
+ if (rats === undefined || rats.length === 0) {
68
+ return false;
69
+ }
70
+ return rats.every((rat) => PACKET_ONLY_RATS.has(rat.toLowerCase()));
71
+ }
72
+ /**
73
+ * ModemManager D-Bus error names this classifier recognises, matched on the
74
+ * SUFFIX after the last dot so a future `...Error.Core.Unsupported` regrouping
75
+ * does not silently fall through to `transport-failed`.
76
+ */
77
+ const ERROR_SUFFIX_REASONS = new Map([
78
+ ['Unsupported', 'unsupported'],
79
+ ['NotSupported', 'unsupported'],
80
+ ['InProgress', 'session-busy'],
81
+ ['NoNetwork', 'not-registered'],
82
+ ['NotRegistered', 'not-registered'],
83
+ ['Timeout', 'timeout'],
84
+ ['Aborted', 'carrier-rejected'],
85
+ ['Failed', 'carrier-rejected'],
86
+ ]);
87
+ /**
88
+ * Message fragments that identify a refusal no error NAME distinguishes. MM
89
+ * folds several modem answers into `Core.Failed`, so the text is the only signal
90
+ * separating "the network said no" from "the bus call broke".
91
+ */
92
+ const MESSAGE_REASONS = [
93
+ [/ussd.*(?:not supported|unsupported)/i, 'unsupported'],
94
+ [/(?:session|operation) (?:already )?(?:active|in progress)/i, 'session-busy'],
95
+ [/no (?:active )?ussd session/i, 'no-session'],
96
+ [/not registered|no network/i, 'not-registered'],
97
+ [/timed? ?out/i, 'timeout'],
98
+ [/rejected|refused|denied|network error/i, 'carrier-rejected'],
99
+ ];
100
+ function errorName(error) {
101
+ if (typeof error !== 'object' || error === null) {
102
+ return undefined;
103
+ }
104
+ const name = error.dbusName;
105
+ if (typeof name === 'string' && name.includes('.')) {
106
+ return name;
107
+ }
108
+ const fallback = error.name;
109
+ return typeof fallback === 'string' && fallback.includes('.') ? fallback : undefined;
110
+ }
111
+ function errorMessage(error) {
112
+ if (error instanceof Error) {
113
+ return error.message;
114
+ }
115
+ if (typeof error === 'object' && error !== null) {
116
+ const message = error.message;
117
+ if (typeof message === 'string') {
118
+ return message;
119
+ }
120
+ }
121
+ return String(error);
122
+ }
123
+ /**
124
+ * Classify a failed USSD call. Pure, total, never throws.
125
+ *
126
+ * Order is deliberate: the D-Bus error NAME is the strongest signal and is read
127
+ * first; the message text is consulted only for the names MM overloads; anything
128
+ * unrecognised stays `transport-failed`, which is the honest answer for a failure
129
+ * whose origin cannot be attributed to the network.
130
+ *
131
+ * The PS-only promotion runs LAST and applies to exactly the two reasons that are
132
+ * ambiguous between a device limit and a carrier policy — a `not-registered` or a
133
+ * bus failure is neither, and is left alone.
134
+ */
135
+ export function classifyUssdFailure(error, registration = { registered: false }) {
136
+ const name = errorName(error);
137
+ const suffix = name?.slice(name.lastIndexOf('.') + 1);
138
+ const message = errorMessage(error);
139
+ let reason = suffix === undefined ? undefined : ERROR_SUFFIX_REASONS.get(suffix);
140
+ // `Core.Failed` is MM's catch-all, so its message is worth more than its name.
141
+ if (reason === undefined || reason === 'carrier-rejected') {
142
+ for (const [pattern, mapped] of MESSAGE_REASONS) {
143
+ if (pattern.test(message)) {
144
+ reason = mapped;
145
+ break;
146
+ }
147
+ }
148
+ }
149
+ if (reason === undefined) {
150
+ return 'transport-failed';
151
+ }
152
+ const ambiguous = reason === 'unsupported' || reason === 'carrier-rejected';
153
+ return ambiguous && isPacketSwitchedOnly(registration) ? 'lte-only-unsupported' : reason;
154
+ }
@@ -0,0 +1,20 @@
1
+ import { type DecodedManagedObjects } from '../backend/managed-objects.js';
2
+ import type { DbusTransport } from '../transport/index.js';
3
+ import type { UssdRegistrationFacts } from './refusal.js';
4
+ /** Decode `Modem.AccessTechnologies` into the technology names in use. */
5
+ export declare function decodeAccessTechnologies(mask: number): readonly string[];
6
+ /**
7
+ * Derive the registration facts from an already-fetched managed-objects tree.
8
+ *
9
+ * Pure, so the derivation is testable against a fixture tree without a bus — the
10
+ * `sim-unlock.ts` split between "read the tree" and "decide from the tree".
11
+ */
12
+ export declare function registrationFactsFromTree(tree: DecodedManagedObjects, modemPath: string): UssdRegistrationFacts;
13
+ /** The unread default — every field withheld, so nothing can be claimed from it. */
14
+ export declare const UNKNOWN_REGISTRATION: UssdRegistrationFacts;
15
+ /**
16
+ * Read the registration facts for one modem. Fail-soft: a tree we could not fetch
17
+ * yields {@link UNKNOWN_REGISTRATION}, which can only ever make the refusal LESS
18
+ * specific.
19
+ */
20
+ export declare function readUssdRegistrationFacts(transport: DbusTransport, destination: string, modemPath: string): Promise<UssdRegistrationFacts>;
@@ -0,0 +1,101 @@
1
+ // Reading the registration facts the USSD refusal classifier needs.
2
+ //
3
+ // ModemManager publishes NO "is a circuit-switched domain available" property, so
4
+ // the fact has to be DERIVED — and the derivation is worth stating, because it is
5
+ // the difference between telling an operator their modem cannot do USSD and
6
+ // telling them their carrier will not carry it on this registration:
7
+ //
8
+ // * `Modem.AccessTechnologies` is a bitmask. When every bit in use is a
9
+ // packet-only radio (LTE, 5G-NR, LTE-M, NB-IoT) the modem is not camped on a
10
+ // circuit-switched radio at all.
11
+ // * `Modem3gpp.RegistrationState` still overrides that, because CS FALLBACK is
12
+ // exactly the case where an LTE-camped modem CAN reach the CS domain. MM has
13
+ // two states that say so outright — `HOME_CSFB_NOT_PREFERRED` (9) and
14
+ // `ROAMING_CSFB_NOT_PREFERRED` (10) — and a modem in either of them is
15
+ // reported CS-capable regardless of its radio.
16
+ //
17
+ // Both reads are best-effort and NEVER throw: a fact nobody could read is left
18
+ // `undefined`, which the classifier treats as "we did not look" rather than as a
19
+ // negative. That asymmetry is the whole safety property — an unread registration
20
+ // can only ever produce the generic refusal, never the more specific claim.
21
+ import { MODEM_IFACE, MODEM3GPP_IFACE } from '../backend/constants.js';
22
+ import { fetchManagedObjects, findInterface, numberProp, } from '../backend/managed-objects.js';
23
+ /** `MMModemAccessTechnology` bits this module names. */
24
+ const ACCESS_TECHNOLOGY_BITS = [
25
+ [1 << 1, 'gsm'],
26
+ [1 << 2, 'gsm-compact'],
27
+ [1 << 3, 'gprs'],
28
+ [1 << 4, 'edge'],
29
+ [1 << 5, 'umts'],
30
+ [1 << 6, 'hsdpa'],
31
+ [1 << 7, 'hsupa'],
32
+ [1 << 8, 'hspa'],
33
+ [1 << 9, 'hspa-plus'],
34
+ [1 << 10, '1xrtt'],
35
+ [1 << 11, 'evdo0'],
36
+ [1 << 12, 'evdoa'],
37
+ [1 << 13, 'evdob'],
38
+ [1 << 14, 'lte'],
39
+ [1 << 15, '5gnr'],
40
+ [1 << 16, 'lte-cat-m'],
41
+ [1 << 17, 'lte-nb-iot'],
42
+ ];
43
+ /** `MMModem3gppRegistrationState` values that mean the modem is on a network. */
44
+ const REGISTERED_STATES = new Set([1, 5, 6, 7, 9, 10]);
45
+ /** …and the two that positively advertise a circuit-switched fallback. */
46
+ const CSFB_STATES = new Set([9, 10]);
47
+ /** Decode `Modem.AccessTechnologies` into the technology names in use. */
48
+ export function decodeAccessTechnologies(mask) {
49
+ const names = [];
50
+ for (const [bit, name] of ACCESS_TECHNOLOGY_BITS) {
51
+ if ((mask & bit) !== 0) {
52
+ names.push(name);
53
+ }
54
+ }
55
+ return names;
56
+ }
57
+ /**
58
+ * Derive the registration facts from an already-fetched managed-objects tree.
59
+ *
60
+ * Pure, so the derivation is testable against a fixture tree without a bus — the
61
+ * `sim-unlock.ts` split between "read the tree" and "decide from the tree".
62
+ */
63
+ export function registrationFactsFromTree(tree, modemPath) {
64
+ const modem = findInterface(tree, modemPath, MODEM_IFACE);
65
+ const threeGpp = findInterface(tree, modemPath, MODEM3GPP_IFACE);
66
+ const registrationState = numberProp(threeGpp, 'RegistrationState');
67
+ const accessMask = numberProp(modem, 'AccessTechnologies');
68
+ const registered = registrationState !== undefined && REGISTERED_STATES.has(registrationState);
69
+ const technologies = accessMask === undefined ? undefined : decodeAccessTechnologies(accessMask);
70
+ // A CSFB registration is CS-capable outright. Otherwise the domain is only
71
+ // declared ABSENT when the radios in use were actually read and are all
72
+ // packet-only; an unread mask leaves the field undefined on purpose.
73
+ let csDomain;
74
+ if (registrationState !== undefined && CSFB_STATES.has(registrationState)) {
75
+ csDomain = true;
76
+ }
77
+ else if (technologies !== undefined && technologies.length > 0) {
78
+ csDomain = technologies.some((rat) => rat !== 'lte' && rat !== '5gnr' && rat !== 'lte-cat-m' && rat !== 'lte-nb-iot');
79
+ }
80
+ return {
81
+ registered,
82
+ ...(csDomain === undefined ? {} : { csDomain }),
83
+ ...(technologies === undefined ? {} : { accessTechnologies: technologies }),
84
+ };
85
+ }
86
+ /** The unread default — every field withheld, so nothing can be claimed from it. */
87
+ export const UNKNOWN_REGISTRATION = { registered: false };
88
+ /**
89
+ * Read the registration facts for one modem. Fail-soft: a tree we could not fetch
90
+ * yields {@link UNKNOWN_REGISTRATION}, which can only ever make the refusal LESS
91
+ * specific.
92
+ */
93
+ export async function readUssdRegistrationFacts(transport, destination, modemPath) {
94
+ try {
95
+ const tree = await fetchManagedObjects(transport, destination);
96
+ return registrationFactsFromTree(tree, modemPath);
97
+ }
98
+ catch {
99
+ return UNKNOWN_REGISTRATION;
100
+ }
101
+ }
@@ -0,0 +1,84 @@
1
+ import type { UssdRefusalReason } from './refusal.js';
2
+ /**
3
+ * Session states.
4
+ *
5
+ * Three of them (`idle`, `active`, `awaiting-reply`) mirror MM's own
6
+ * `MMModem3gppUssdSessionState`; the rest are LOCAL in-flight states, because MM
7
+ * has no state for "we dispatched a call and the reply has not landed". Without
8
+ * them a second `initiate` racing the first would be judged against `idle` and
9
+ * allowed through, which is exactly the double-open the network answers busy.
10
+ */
11
+ export declare const USSD_SESSION_STATES: readonly ["idle", "initiating", "active", "awaiting-reply", "responding", "cancelling", "closed"];
12
+ export type UssdSessionState = (typeof USSD_SESSION_STATES)[number];
13
+ /** How a session that reached `closed` got there. */
14
+ export declare const USSD_SESSION_OUTCOMES: readonly ["completed", "cancelled", "timed-out", "failed"];
15
+ export type UssdSessionOutcome = (typeof USSD_SESSION_OUTCOMES)[number];
16
+ /** MM's post-call session state, decoded. */
17
+ export type UssdRepliedState = 'awaiting-reply' | 'active' | 'released';
18
+ export type UssdSessionEvent =
19
+ /** The operator asked to open a session. */
20
+ {
21
+ readonly kind: 'initiate';
22
+ }
23
+ /** The operator answered a network prompt. */
24
+ | {
25
+ readonly kind: 'respond';
26
+ }
27
+ /** The operator asked to close the session. */
28
+ | {
29
+ readonly kind: 'cancel';
30
+ }
31
+ /**
32
+ * The network answered an `Initiate`/`Respond`. `sessionState` is MM's own
33
+ * post-call `Modem3gpp.Ussd.State`, decoded: the network either wants an
34
+ * answer, is holding the session open with nothing pending, or released it.
35
+ */
36
+ | {
37
+ readonly kind: 'replied';
38
+ readonly sessionState: UssdRepliedState;
39
+ }
40
+ /** A `Cancel` was confirmed by the modem. */
41
+ | {
42
+ readonly kind: 'cancelled';
43
+ }
44
+ /** The network released the session without our asking (notification path). */
45
+ | {
46
+ readonly kind: 'network-released';
47
+ }
48
+ /** The bounded wait elapsed with no answer. */
49
+ | {
50
+ readonly kind: 'timeout';
51
+ }
52
+ /** The call failed. The reason is carried onto the terminal state verbatim. */
53
+ | {
54
+ readonly kind: 'failed';
55
+ readonly reason: UssdRefusalReason;
56
+ };
57
+ export interface UssdSessionSnapshot {
58
+ readonly state: UssdSessionState;
59
+ /** Present only at `closed`. */
60
+ readonly outcome?: UssdSessionOutcome;
61
+ /** Present only at `closed` with outcome `failed`. */
62
+ readonly refusal?: UssdRefusalReason;
63
+ }
64
+ export type UssdTransition = {
65
+ readonly ok: true;
66
+ readonly snapshot: UssdSessionSnapshot;
67
+ }
68
+ /** The verb is not legal in this state, and the machine did NOT move. */
69
+ | {
70
+ readonly ok: false;
71
+ readonly refusal: UssdRefusalReason;
72
+ };
73
+ export declare const IDLE_SESSION: UssdSessionSnapshot;
74
+ /**
75
+ * Apply one event. TOTAL: every (state, event) pair has an answer, and an answer
76
+ * is either a new snapshot or a typed refusal that leaves the machine untouched.
77
+ *
78
+ * A `closed` machine accepts NOTHING — not even another `cancel`. Re-opening a
79
+ * terminal session would hide the fact that the previous one ended, and the
80
+ * cost of a fresh machine is one object.
81
+ */
82
+ export declare function reduceUssdSession(snapshot: UssdSessionSnapshot, event: UssdSessionEvent): UssdTransition;
83
+ /** True while the session still holds a network dialogue open. */
84
+ export declare function isUssdSessionOpen(snapshot: UssdSessionSnapshot): boolean;
@@ -0,0 +1,163 @@
1
+ // The USSD session state machine — pure, total, and the only place a session's
2
+ // legality is decided.
3
+ //
4
+ // USSD is a SESSION protocol, not a request/response one: `Initiate` opens a
5
+ // dialogue the network may keep open pending a `Respond`, and a session that is
6
+ // neither responded to nor cancelled stays open on the NETWORK side, consuming a
7
+ // scarce per-subscriber slot and blocking the next `Initiate` with a busy error.
8
+ // So "which verb is legal right now" is a real question with a real wrong answer,
9
+ // and answering it inside the D-Bus adapter would make it untestable without a
10
+ // bus. It lives here instead, as data.
11
+ //
12
+ // Everything the machine can be told is an EVENT and every answer is a
13
+ // TRANSITION — an illegal verb is REFUSED with a typed reason, never thrown and
14
+ // never silently ignored. A refusal at an RPC boundary must name what the caller
15
+ // can do about it; a throw becomes an opaque failure and a silent no-op becomes a
16
+ // UI that spins forever.
17
+ //
18
+ // The machine carries NO carrier text. The reply an operator sees is threaded by
19
+ // the adapter and redacted at every log boundary (`../redact`); keeping it out of
20
+ // the state entirely means a state snapshot can never leak one.
21
+ /**
22
+ * Session states.
23
+ *
24
+ * Three of them (`idle`, `active`, `awaiting-reply`) mirror MM's own
25
+ * `MMModem3gppUssdSessionState`; the rest are LOCAL in-flight states, because MM
26
+ * has no state for "we dispatched a call and the reply has not landed". Without
27
+ * them a second `initiate` racing the first would be judged against `idle` and
28
+ * allowed through, which is exactly the double-open the network answers busy.
29
+ */
30
+ export const USSD_SESSION_STATES = [
31
+ /** No session. MM `IDLE`. */
32
+ 'idle',
33
+ /** `Initiate` dispatched, reply outstanding. Local. */
34
+ 'initiating',
35
+ /** Network answered and the session is open with nothing pending. MM `ACTIVE`. */
36
+ 'active',
37
+ /** Network asked a question; a `Respond` is required. MM `USER_RESPONSE`. */
38
+ 'awaiting-reply',
39
+ /** `Respond` dispatched, reply outstanding. Local. */
40
+ 'responding',
41
+ /** `Cancel` dispatched, confirmation outstanding. Local. */
42
+ 'cancelling',
43
+ /** Terminal for this session object. A new session starts from a new machine. */
44
+ 'closed',
45
+ ];
46
+ /** How a session that reached `closed` got there. */
47
+ export const USSD_SESSION_OUTCOMES = [
48
+ /** The network completed the dialogue and released the session. */
49
+ 'completed',
50
+ /** The operator cancelled it. */
51
+ 'cancelled',
52
+ /** No answer within the bound; the machine closed it locally. */
53
+ 'timed-out',
54
+ /** The network or the modem refused. `refusal` names which. */
55
+ 'failed',
56
+ ];
57
+ export const IDLE_SESSION = { state: 'idle' };
58
+ /** States in which an operator verb may be dispatched at all. */
59
+ const ACCEPTS_INITIATE = new Set(['idle']);
60
+ const ACCEPTS_RESPOND = new Set([
61
+ 'awaiting-reply',
62
+ ]);
63
+ const ACCEPTS_CANCEL = new Set([
64
+ 'initiating',
65
+ 'active',
66
+ 'awaiting-reply',
67
+ 'responding',
68
+ ]);
69
+ /** States with a call in flight — the only ones a network answer may land on. */
70
+ const IN_FLIGHT = new Set([
71
+ 'initiating',
72
+ 'responding',
73
+ ]);
74
+ function open(state) {
75
+ return { ok: true, snapshot: { state } };
76
+ }
77
+ function close(outcome, refusal) {
78
+ return {
79
+ ok: true,
80
+ snapshot: { state: 'closed', outcome, ...(refusal === undefined ? {} : { refusal }) },
81
+ };
82
+ }
83
+ function refuse(refusal) {
84
+ return { ok: false, refusal };
85
+ }
86
+ /**
87
+ * Apply one event. TOTAL: every (state, event) pair has an answer, and an answer
88
+ * is either a new snapshot or a typed refusal that leaves the machine untouched.
89
+ *
90
+ * A `closed` machine accepts NOTHING — not even another `cancel`. Re-opening a
91
+ * terminal session would hide the fact that the previous one ended, and the
92
+ * cost of a fresh machine is one object.
93
+ */
94
+ export function reduceUssdSession(snapshot, event) {
95
+ const state = snapshot.state;
96
+ if (state === 'closed') {
97
+ return refuse('no-session');
98
+ }
99
+ // Every event except `initiate` describes something happening TO a session,
100
+ // and an idle machine has none for them to happen to.
101
+ const sessionOpen = state !== 'idle';
102
+ switch (event.kind) {
103
+ case 'initiate':
104
+ // A session already in flight or open is the busy case the network
105
+ // itself would answer — refused locally so no second dialogue is opened.
106
+ return ACCEPTS_INITIATE.has(state) ? open('initiating') : refuse('session-busy');
107
+ case 'respond':
108
+ // Responding to a session that never asked a question is not a busy
109
+ // device; it is the wrong verb, and `invalid-state` says so.
110
+ return ACCEPTS_RESPOND.has(state) ? open('responding') : refuse('invalid-state');
111
+ case 'cancel':
112
+ // The two refusals are different operator facts: nothing to close, vs a
113
+ // cancel that is already in flight.
114
+ if (ACCEPTS_CANCEL.has(state)) {
115
+ return open('cancelling');
116
+ }
117
+ return refuse(sessionOpen ? 'invalid-state' : 'no-session');
118
+ case 'replied':
119
+ // A network answer that lands on a state with no call in flight is
120
+ // evidence of a lost reply or a duplicate; it is refused rather than
121
+ // used to resurrect a session the machine already moved past.
122
+ if (!IN_FLIGHT.has(state)) {
123
+ return refuse('invalid-state');
124
+ }
125
+ // The three-way answer is the dialogue-vs-one-shot distinction, and
126
+ // collapsing `active` into `released` is what would leave a session MM
127
+ // still considers open dangling on the network side.
128
+ if (event.sessionState === 'released') {
129
+ return close('completed');
130
+ }
131
+ return open(event.sessionState);
132
+ case 'cancelled':
133
+ // Only a machine that asked to cancel may be closed by one, so a stray
134
+ // confirmation cannot tear down a live dialogue.
135
+ return state === 'cancelling' ? close('cancelled') : refuse('invalid-state');
136
+ case 'network-released':
137
+ // The network is authoritative about its own session, so this lands
138
+ // from anywhere the session is still open — including mid-call, where
139
+ // it is the honest end of a dialogue whose reply will not come. During
140
+ // a cancel it is reported as `cancelled`: the operator asked for the
141
+ // session to end and it ended.
142
+ if (!sessionOpen) {
143
+ return refuse('no-session');
144
+ }
145
+ return close(state === 'cancelling' ? 'cancelled' : 'completed');
146
+ case 'timeout':
147
+ // The bound closes rather than reverting: after an unanswered call the
148
+ // network's own view is unknown, and pretending we are back at `idle`
149
+ // would let the next `initiate` walk into a busy error with no
150
+ // explanation.
151
+ return sessionOpen ? close('timed-out') : refuse('no-session');
152
+ case 'failed':
153
+ return sessionOpen ? close('failed', event.reason) : refuse('no-session');
154
+ default: {
155
+ const unreachable = event;
156
+ return unreachable;
157
+ }
158
+ }
159
+ }
160
+ /** True while the session still holds a network dialogue open. */
161
+ export function isUssdSessionOpen(snapshot) {
162
+ return snapshot.state !== 'idle' && snapshot.state !== 'closed';
163
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ceralive/modem-control",
3
- "version": "0.2.0",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "Cellular modem control for CeraLive — ModemManager D-Bus backend, NetworkManager adapter, desired-state reconciler, USB composition-mode model, data-usage sampler.",
6
6
  "license": "AGPL-3.0",
@@ -13,14 +13,48 @@
13
13
  "registry": "https://registry.npmjs.org/",
14
14
  "access": "public"
15
15
  },
16
+ "sideEffects": false,
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
16
19
  "exports": {
17
- ".": "./src/index.ts",
18
- "./transport": "./src/transport/index.ts"
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ },
24
+ "./transport": {
25
+ "types": "./dist/transport/index.d.ts",
26
+ "import": "./dist/transport/index.js"
27
+ },
28
+ "./domain": {
29
+ "types": "./dist/domain/index.d.ts",
30
+ "import": "./dist/domain/index.js"
31
+ },
32
+ "./providers": {
33
+ "types": "./dist/providers/index.d.ts",
34
+ "import": "./dist/providers/index.js"
35
+ },
36
+ "./capabilities": {
37
+ "types": "./dist/capability/index.d.ts",
38
+ "import": "./dist/capability/index.js"
39
+ },
40
+ "./hardware": {
41
+ "types": "./dist/hardware/index.d.ts",
42
+ "import": "./dist/hardware/index.js"
43
+ },
44
+ "./testing": {
45
+ "types": "./dist/testing/index.d.ts",
46
+ "import": "./dist/testing/index.js"
47
+ },
48
+ "./package.json": "./package.json"
19
49
  },
20
50
  "files": [
21
- "src"
51
+ "dist"
22
52
  ],
23
53
  "scripts": {
54
+ "build": "bun run scripts/build.ts",
55
+ "prepack": "bun run build",
56
+ "verify:consumers": "bun run scripts/verify-consumers.ts",
57
+ "verify:tarball": "bun run scripts/assert-tarball-shape.ts",
24
58
  "test": "bun test",
25
59
  "typecheck": "tsc --noEmit"
26
60
  },