@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
@@ -0,0 +1,18 @@
1
+ import { type ObservationEnvelope } from '../../domain/index.js';
2
+ import { type NormalizationContext } from '../envelope.js';
3
+ import type { NormalizedModemObservation } from '../model.js';
4
+ import { type RawFieldValue } from '../provenance.js';
5
+ /**
6
+ * The decoded ModemManager property records, one per D-Bus interface.
7
+ *
8
+ * A body left out is a body nobody read, and reads as `not-observed` rather than as a
9
+ * modem that reports nothing — that difference decides whether a consumer retries or
10
+ * hides a control.
11
+ */
12
+ export type ModemManagerObservationInput = {
13
+ readonly modem?: Readonly<Record<string, RawFieldValue>>;
14
+ readonly modem3gpp?: Readonly<Record<string, RawFieldValue>>;
15
+ readonly sim?: Readonly<Record<string, RawFieldValue>>;
16
+ readonly signal?: Readonly<Record<string, RawFieldValue>>;
17
+ };
18
+ export declare function normalizeModemManagerObservation(input: ModemManagerObservationInput, context: NormalizationContext): ObservationEnvelope<NormalizedModemObservation>;
@@ -0,0 +1,239 @@
1
+ // ModemManager → normalized observation.
2
+ //
3
+ // Every decode here is the MIGRATED logic (`domain/mm-enums.ts`,
4
+ // `domain/modem-presentation.ts`, `hardware/router-parsers.ts` for SIM presence) —
5
+ // this module adds provenance and reason-carrying unknowns, and decodes nothing
6
+ // itself. Where a decoder answers `'unknown'` or `undefined`, the distinction it
7
+ // cannot make is made here from the RAW record: a field that was never in the payload
8
+ // is `not-reported`, an interface nobody read is `not-observed`, and a field that WAS
9
+ // present but decoded to nothing is `malformed` with a diagnostic note. Collapsing
10
+ // those three into one absent value is precisely the loss this layer exists to stop.
11
+ import { decodeEsimStatus, decodeMmAccessTechnologies, decodeMmState, decodeRegistrationState, decodeSimType, decodeStateFailedReason, decodeUnlockRequired, modeMaskToLabel, modemHardwareLabel, modemHardwareName, } from '../../domain/index.js';
12
+ import { readSimPresence } from '../../hardware/router-parsers.js';
13
+ import { freshObservation, metricProvenance } from '../envelope.js';
14
+ import { knownMetric, unknownMetric } from '../metric.js';
15
+ import { createObservationDiagnostics, } from '../provenance.js';
16
+ import { hasRawField, mergeRawRecords, prefixRawRecord, rawBooleanAt, rawKey, rawNumber, rawNumberAt, rawString, rawStringArray, } from '../raw.js';
17
+ const SOURCE = 'modemmanager';
18
+ const MODEM = 'Modem';
19
+ const MODEM3GPP = 'Modem3gpp';
20
+ const SIM = 'Sim';
21
+ const SIGNAL = 'Signal';
22
+ export function normalizeModemManagerObservation(input, context) {
23
+ const raw = mergeRawRecords(prefixRawRecord(MODEM, input.modem), prefixRawRecord(MODEM3GPP, input.modem3gpp), prefixRawRecord(SIM, input.sim), prefixRawRecord(SIGNAL, input.signal));
24
+ const notes = [];
25
+ const consumed = [];
26
+ const provenance = (...fields) => {
27
+ consumed.push(...fields);
28
+ return metricProvenance(SOURCE, context, fields);
29
+ };
30
+ const hardware = normalizeHardware(raw, provenance);
31
+ const radio = normalizeRadio(raw, provenance, notes);
32
+ const signal = normalizeSignal(raw, input.signal !== undefined, provenance);
33
+ const sim = normalizeSim(raw, input.sim !== undefined, provenance, notes);
34
+ return freshObservation(SOURCE, context, {
35
+ source: SOURCE,
36
+ hardware,
37
+ radio,
38
+ signal,
39
+ sim,
40
+ diagnostics: createObservationDiagnostics({ source: SOURCE, raw, consumed, notes }),
41
+ });
42
+ }
43
+ const MODEL = rawKey(MODEM, 'Model');
44
+ const MANUFACTURER = rawKey(MODEM, 'Manufacturer');
45
+ const REVISION = rawKey(MODEM, 'Revision');
46
+ const EQUIPMENT_ID = rawKey(MODEM, 'EquipmentIdentifier');
47
+ const STATE = rawKey(MODEM, 'State');
48
+ const CURRENT_MODES = rawKey(MODEM, 'CurrentModes');
49
+ const ACCESS_TECHNOLOGIES = rawKey(MODEM, 'AccessTechnologies');
50
+ const SIGNAL_QUALITY = rawKey(MODEM, 'SignalQuality');
51
+ const SIM_PATH = rawKey(MODEM, 'Sim');
52
+ const SIM_SLOTS = rawKey(MODEM, 'SimSlots');
53
+ const FAILED_REASON = rawKey(MODEM, 'StateFailedReason');
54
+ const UNLOCK_REQUIRED = rawKey(MODEM, 'UnlockRequired');
55
+ const REGISTRATION_STATE = rawKey(MODEM3GPP, 'RegistrationState');
56
+ const SIM_TYPE = rawKey(SIM, 'SimType');
57
+ const ESIM_STATUS = rawKey(SIM, 'EsimStatus');
58
+ const RSSI = rawKey(SIGNAL, 'rssi');
59
+ const RSRP = rawKey(SIGNAL, 'rsrp');
60
+ const RSRQ = rawKey(SIGNAL, 'rsrq');
61
+ const SNR = rawKey(SIGNAL, 'snr');
62
+ function hardwareIdentity(raw) {
63
+ const model = rawString(raw, MODEL);
64
+ const manufacturer = rawString(raw, MANUFACTURER);
65
+ const firmwareRevision = rawString(raw, REVISION);
66
+ const equipmentId = rawString(raw, EQUIPMENT_ID);
67
+ return {
68
+ ...(model === undefined ? {} : { model }),
69
+ ...(manufacturer === undefined ? {} : { manufacturer }),
70
+ ...(firmwareRevision === undefined ? {} : { firmwareRevision }),
71
+ ...(equipmentId === undefined ? {} : { equipmentId }),
72
+ };
73
+ }
74
+ function normalizeHardware(raw, provenance) {
75
+ const identity = hardwareIdentity(raw);
76
+ const named = identity.model !== undefined ||
77
+ identity.manufacturer !== undefined ||
78
+ identity.firmwareRevision !== undefined;
79
+ const labelProvenance = provenance(MODEL, MANUFACTURER, REVISION);
80
+ const nameProvenance = provenance(MODEL, MANUFACTURER, REVISION, EQUIPMENT_ID);
81
+ return {
82
+ label: named
83
+ ? knownMetric(modemHardwareLabel(identity), labelProvenance)
84
+ : unknownMetric('not-reported', labelProvenance),
85
+ displayName: named
86
+ ? knownMetric(modemHardwareName(identity), nameProvenance)
87
+ : unknownMetric('not-reported', nameProvenance),
88
+ };
89
+ }
90
+ function normalizeRadio(raw, provenance, notes) {
91
+ return {
92
+ modemState: decodedLabel(raw, STATE, decodeMmState, provenance, notes),
93
+ registration: decodedLabel(raw, REGISTRATION_STATE, decodeRegistrationState, provenance, notes),
94
+ accessTechnologies: normalizeAccessTechnologies(raw, provenance, notes),
95
+ modeLabel: normalizeModeLabel(raw, provenance, notes),
96
+ };
97
+ }
98
+ /** A decoder whose "I could not place this" answer is the literal string `unknown`. */
99
+ function decodedLabel(raw, key, decode, provenance, notes) {
100
+ const source = provenance(key);
101
+ if (!hasRawField(raw, key)) {
102
+ return unknownMetric('not-reported', source);
103
+ }
104
+ const decoded = decode(rawNumber(raw, key));
105
+ if (decoded === 'unknown') {
106
+ notes.push({ code: 'field-shape-unrecognized', field: key });
107
+ return unknownMetric('malformed', source);
108
+ }
109
+ return knownMetric(decoded, source);
110
+ }
111
+ function normalizeAccessTechnologies(raw, provenance, notes) {
112
+ const source = provenance(ACCESS_TECHNOLOGIES);
113
+ if (!hasRawField(raw, ACCESS_TECHNOLOGIES)) {
114
+ return unknownMetric('not-reported', source);
115
+ }
116
+ const mask = rawNumber(raw, ACCESS_TECHNOLOGIES);
117
+ // MM's 0 is `UNKNOWN` — the modem did not say, which is not a malformed answer.
118
+ if (mask === undefined || mask <= 0) {
119
+ return unknownMetric('not-reported', source);
120
+ }
121
+ const decoded = [...decodeMmAccessTechnologies(mask)].sort();
122
+ if (decoded.length === 0) {
123
+ notes.push({ code: 'field-shape-unrecognized', field: ACCESS_TECHNOLOGIES });
124
+ return unknownMetric('malformed', source);
125
+ }
126
+ return knownMetric(decoded, source);
127
+ }
128
+ function normalizeModeLabel(raw, provenance, notes) {
129
+ const source = provenance(CURRENT_MODES);
130
+ if (!hasRawField(raw, CURRENT_MODES)) {
131
+ return unknownMetric('not-reported', source);
132
+ }
133
+ // `CurrentModes` is a `(uu)`; the ALLOWED mask is member 0. A flattened scalar
134
+ // (mmcli's shape) decodes at the same index, so both retentions work here.
135
+ const mask = rawNumberAt(raw, CURRENT_MODES, 0);
136
+ if (mask === undefined || mask <= 0) {
137
+ return unknownMetric('not-reported', source);
138
+ }
139
+ const label = modeMaskToLabel(mask);
140
+ if (label === undefined) {
141
+ notes.push({ code: 'field-shape-unrecognized', field: CURRENT_MODES });
142
+ return unknownMetric('malformed', source);
143
+ }
144
+ return knownMetric(label, source);
145
+ }
146
+ function normalizeSignal(raw, signalRead, provenance) {
147
+ const missing = signalRead ? 'not-reported' : 'not-observed';
148
+ const extended = (key) => {
149
+ const source = provenance(key);
150
+ const value = rawNumber(raw, key);
151
+ return value === undefined
152
+ ? unknownMetric(hasRawField(raw, key) ? 'malformed' : missing, source)
153
+ : knownMetric(value, source);
154
+ };
155
+ // `SignalQuality` is a `(ub)`: percentage at 0, "measured recently" at 1. Both are
156
+ // claimed; a source that retained only the percentage answers `not-reported` for the
157
+ // flag, which is a claim about the READ, never about ModemManager's capability.
158
+ const quality = rawNumberAt(raw, SIGNAL_QUALITY, 0);
159
+ const recent = rawBooleanAt(raw, SIGNAL_QUALITY, 1);
160
+ const qualityProvenance = provenance(SIGNAL_QUALITY);
161
+ return {
162
+ quality: quality === undefined
163
+ ? unknownMetric('not-reported', qualityProvenance)
164
+ : knownMetric(quality, qualityProvenance),
165
+ qualityRecent: recent === undefined
166
+ ? unknownMetric('not-reported', provenance(SIGNAL_QUALITY))
167
+ : knownMetric(recent, provenance(SIGNAL_QUALITY)),
168
+ // ModemManager reports a percentage and no bar scale. Deriving bars from the
169
+ // percentage would be inventing a reading, so this is a capability claim.
170
+ bars: unknownMetric('unsupported', metricProvenanceEmpty(provenance)),
171
+ maxBars: unknownMetric('unsupported', metricProvenanceEmpty(provenance)),
172
+ dbm: extended(RSSI),
173
+ rsrp: extended(RSRP),
174
+ rsrq: extended(RSRQ),
175
+ snr: extended(SNR),
176
+ // `Modem.Signal` exposes rssi/rsrp/rsrq/snr/ecio/io/rscp — there is no SINR member.
177
+ sinr: unknownMetric('unsupported', metricProvenanceEmpty(provenance)),
178
+ };
179
+ }
180
+ /** Provenance for a metric no raw field backs, which is what `unsupported` means. */
181
+ function metricProvenanceEmpty(provenance) {
182
+ return provenance();
183
+ }
184
+ /**
185
+ * `Modem.StateFailedReason` in the spelling the presence rule matches.
186
+ *
187
+ * D-Bus types it `u`, so a provider retaining the property verbatim holds a NUMBER,
188
+ * while the migrated rule matches the mmcli STRING. Both are accepted here — the
189
+ * number through the enum decoder, the string as it stands — because the two transports
190
+ * for the same fact must not disagree about whether a modem has a SIM. An
191
+ * unrecognized number decodes to `undefined` and simply proves nothing.
192
+ */
193
+ function failedReasonOf(raw) {
194
+ const numeric = rawNumber(raw, FAILED_REASON);
195
+ if (numeric !== undefined)
196
+ return decodeStateFailedReason(numeric);
197
+ return rawString(raw, FAILED_REASON);
198
+ }
199
+ function simPresenceFacts(raw) {
200
+ const sim = rawString(raw, SIM_PATH);
201
+ const simSlots = rawStringArray(raw, SIM_SLOTS);
202
+ const failedReason = failedReasonOf(raw);
203
+ return {
204
+ ...(sim === undefined ? {} : { sim }),
205
+ ...(simSlots === undefined ? {} : { simSlots }),
206
+ ...(failedReason === undefined ? {} : { failedReason }),
207
+ };
208
+ }
209
+ function normalizeSim(raw, simRead, provenance, notes) {
210
+ const presenceProvenance = provenance(SIM_PATH, SIM_SLOTS, FAILED_REASON);
211
+ // `absent` here comes from `state-failed-reason` evidence and from nothing else.
212
+ // A blank `Sim` object path with no failure reason stays `unknown`: ModemManager
213
+ // reports `/` while a modem is initializing and while a slot switch is in flight,
214
+ // so reading it as "no SIM" would report an absent SIM on a modem holding one.
215
+ const { presence, evidence } = readSimPresence(simPresenceFacts(raw));
216
+ const missing = simRead ? 'not-reported' : 'not-observed';
217
+ return {
218
+ presence: presence === 'unknown'
219
+ ? unknownMetric('not-reported', presenceProvenance)
220
+ : knownMetric(presence, presenceProvenance),
221
+ presenceEvidence: evidence,
222
+ lockRequired: decodedOptional(raw, UNLOCK_REQUIRED, decodeUnlockRequired, provenance, notes),
223
+ kind: decodedOptional(raw, SIM_TYPE, decodeSimType, provenance, notes, missing),
224
+ esimStatus: decodedOptional(raw, ESIM_STATUS, decodeEsimStatus, provenance, notes, missing),
225
+ };
226
+ }
227
+ /** A decoder whose "I could not place this" answer is `undefined`. */
228
+ function decodedOptional(raw, key, decode, provenance, notes, absentReason = 'not-reported') {
229
+ const source = provenance(key);
230
+ if (!hasRawField(raw, key)) {
231
+ return unknownMetric(absentReason, source);
232
+ }
233
+ const decoded = decode(rawNumber(raw, key));
234
+ if (decoded === undefined) {
235
+ notes.push({ code: 'field-shape-unrecognized', field: key });
236
+ return unknownMetric('malformed', source);
237
+ }
238
+ return knownMetric(decoded, source);
239
+ }
@@ -0,0 +1,29 @@
1
+ import { type ObservationAuthority } from '../../domain/index.js';
2
+ import { type NormalizedMetric } from '../metric.js';
3
+ import type { NormalizedHardware, NormalizedSim } from '../model.js';
4
+ import type { MetricProvenance } from '../provenance.js';
5
+ export type RouterProvenance = (fields: readonly string[], authority?: ObservationAuthority) => MetricProvenance;
6
+ export declare function unsupportedRadioMetric(provenance: RouterProvenance): NormalizedMetric<string>;
7
+ /**
8
+ * The SIM block every router source produces.
9
+ *
10
+ * `vendorPresenceField` NAMES the vendor's own presence code when the payload carried
11
+ * one, so the evidence records that a code exists and was deliberately not decoded —
12
+ * distinct from `no-evidence`, which says the payload offered nothing at all. Neither
13
+ * one is ever `absent`: claiming absence from a vendor code whose semantics no
14
+ * migrated decoder covers is precisely the invented reading this layer refuses.
15
+ */
16
+ export declare function routerSim(provenance: RouterProvenance, vendorPresenceField?: string): NormalizedSim;
17
+ /** Router admin APIs report no measurement-recency flag; that is a source capability claim. */
18
+ export declare function unsupportedQualityRecent(provenance: RouterProvenance): NormalizedMetric<boolean>;
19
+ /**
20
+ * A router's product name, screened by the migrated presentation rules.
21
+ *
22
+ * `modemHardwareLabel` falls back to a generic label when its input says nothing, and
23
+ * publishing that as a KNOWN value would be reporting a name no device gave. So an
24
+ * uninformative product string is refused up front and reads `not-reported` instead.
25
+ */
26
+ export declare function routerHardware(provenance: RouterProvenance, product: {
27
+ readonly name: string;
28
+ readonly field: string;
29
+ } | undefined): NormalizedHardware;
@@ -0,0 +1,68 @@
1
+ // The claims every router admin API makes identically, in one place.
2
+ //
3
+ // HiLink, goform and HIMI are three vendor HTTP surfaces over a modem, and they share
4
+ // a capability boundary: none of them exposes ModemManager's modem state, its 3GPP
5
+ // registration enum, its lock enum or its SIM-type enum. Those read `unsupported`,
6
+ // which is a durable claim about the SOURCE rather than about one poll — and stating
7
+ // it once here is what stops the three normalizers from drifting into three different
8
+ // answers for the same structural fact.
9
+ //
10
+ // SIM PRESENCE is deliberately NOT claimed. Each vendor reports a presence code of
11
+ // its own (`SimStatus`, `simcard_state`, `simstate`) with vendor semantics, and no
12
+ // migrated decoder covers them; guessing one would be exactly the invented reading
13
+ // this layer exists to prevent. It reads `not-reported` and the vendor code stays
14
+ // verbatim in the diagnostics block for the per-vendor providers to claim later.
15
+ import { isUninformativeIdentity, modemHardwareLabel, modemHardwareName, } from '../../domain/index.js';
16
+ import { knownMetric, unknownMetric } from '../metric.js';
17
+ export function unsupportedRadioMetric(provenance) {
18
+ return unknownMetric('unsupported', provenance([]));
19
+ }
20
+ /**
21
+ * The SIM block every router source produces.
22
+ *
23
+ * `vendorPresenceField` NAMES the vendor's own presence code when the payload carried
24
+ * one, so the evidence records that a code exists and was deliberately not decoded —
25
+ * distinct from `no-evidence`, which says the payload offered nothing at all. Neither
26
+ * one is ever `absent`: claiming absence from a vendor code whose semantics no
27
+ * migrated decoder covers is precisely the invented reading this layer refuses.
28
+ */
29
+ export function routerSim(provenance, vendorPresenceField) {
30
+ // The vendor field is NAMED in the evidence but is NOT consumed: no metric claims its
31
+ // value, so it must stay in the diagnostics block's `unmapped` set, verbatim, for the
32
+ // per-vendor provider that will one day decode it with evidence.
33
+ return {
34
+ presence: unknownMetric('not-reported', provenance([])),
35
+ presenceEvidence: vendorPresenceField === undefined
36
+ ? { kind: 'no-evidence', inspected: [] }
37
+ : { kind: 'vendor-code-unclaimed', field: vendorPresenceField },
38
+ lockRequired: unknownMetric('unsupported', provenance([])),
39
+ kind: unknownMetric('unsupported', provenance([])),
40
+ esimStatus: unknownMetric('unsupported', provenance([])),
41
+ };
42
+ }
43
+ /** Router admin APIs report no measurement-recency flag; that is a source capability claim. */
44
+ export function unsupportedQualityRecent(provenance) {
45
+ return unknownMetric('unsupported', provenance([]));
46
+ }
47
+ /**
48
+ * A router's product name, screened by the migrated presentation rules.
49
+ *
50
+ * `modemHardwareLabel` falls back to a generic label when its input says nothing, and
51
+ * publishing that as a KNOWN value would be reporting a name no device gave. So an
52
+ * uninformative product string is refused up front and reads `not-reported` instead.
53
+ */
54
+ export function routerHardware(provenance, product) {
55
+ if (product === undefined || isUninformativeIdentity(product.name)) {
56
+ const source = provenance(product === undefined ? [] : [product.field]);
57
+ return {
58
+ label: unknownMetric('not-reported', source),
59
+ displayName: unknownMetric('not-reported', source),
60
+ };
61
+ }
62
+ const source = provenance([product.field]);
63
+ const identity = { model: product.name };
64
+ return {
65
+ label: knownMetric(modemHardwareLabel(identity), source),
66
+ displayName: knownMetric(modemHardwareName(identity), source),
67
+ };
68
+ }
@@ -0,0 +1,10 @@
1
+ import type { ObservationEnvelope } from '../../domain/index.js';
2
+ import { type NormalizationContext } from '../envelope.js';
3
+ import type { NormalizedModemObservation } from '../model.js';
4
+ export type UfiObservationInput = {
5
+ readonly sysinfo: string;
6
+ readonly overview: string;
7
+ readonly status: string;
8
+ readonly produceInfo?: string;
9
+ };
10
+ export declare function normalizeUfiObservation(input: UfiObservationInput, context: NormalizationContext): ObservationEnvelope<NormalizedModemObservation>;
@@ -0,0 +1,111 @@
1
+ // Qualcomm UFI / HIMI → normalized observation.
2
+ //
3
+ // UFI answers three endpoints per read, each an envelope of its own (`{reply, params}`),
4
+ // and the migrated `parseUfiSignal` already folds them in the vendor's own precedence
5
+ // order. This module keeps all three bodies verbatim — including the `reply` field,
6
+ // which is how a `SessionOut` refusal is told apart from a body that simply lacked the
7
+ // key — and reports the ONE signal reading the source actually provides.
8
+ //
9
+ // Its overview endpoint returns an IMSI and an ICCID. They are retained, because
10
+ // normalization does not get to decide what a diagnostician may need, and they are
11
+ // masked by the shared key-based redactor on the way to anything that renders. That
12
+ // split — retain here, redact at the boundary — is why `raw` is documented as a
13
+ // redaction-class surface rather than as safe-by-default data.
14
+ import { parseUfiDetails, parseUfiSignal } from '../../hardware/router-parsers.js';
15
+ import { freshObservation, metricProvenance } from '../envelope.js';
16
+ import { metricFromRouterSignal, unknownMetric } from '../metric.js';
17
+ import { createObservationDiagnostics, } from '../provenance.js';
18
+ import { hasRawField, mergeRawRecords, normalizeRawValue, parseJsonObject, prefixRawRecord, rawKey, } from '../raw.js';
19
+ import { routerHardware, routerSim, unsupportedQualityRecent, unsupportedRadioMetric, } from './router-shared.js';
20
+ const SOURCE = 'ufi-himiapi';
21
+ const SYSINFO = 'sysinfo';
22
+ const OVERVIEW = 'overview';
23
+ const STATUS = 'status';
24
+ const PRODUCE_INFO = 'produce-info';
25
+ const SESSION_REFUSAL = 'SessionOut';
26
+ /** HIMI's own SIM presence code. Named as evidence, never decoded into a presence. */
27
+ const SIM_STATE = rawKey(SYSINFO, 'simstate');
28
+ export function normalizeUfiObservation(input, context) {
29
+ const bodies = [
30
+ [SYSINFO, input.sysinfo],
31
+ [OVERVIEW, input.overview],
32
+ [STATUS, input.status],
33
+ [PRODUCE_INFO, input.produceInfo ?? ''],
34
+ ];
35
+ const notes = [];
36
+ const records = bodies.map(([name, text]) => {
37
+ const parsed = parseJsonObject(text);
38
+ if (parsed === undefined && text.trim() !== '') {
39
+ notes.push({ code: 'unparseable-body', field: name });
40
+ }
41
+ if (parsed?.reply === SESSION_REFUSAL) {
42
+ notes.push({ code: 'auth-expired', field: rawKey(name, 'reply') });
43
+ }
44
+ return prefixRawRecord(name, flattenUfiEnvelope(parsed));
45
+ });
46
+ const raw = mergeRawRecords(...records);
47
+ const consumed = [];
48
+ const provenance = (fields, authority) => {
49
+ consumed.push(...fields);
50
+ return authority === undefined
51
+ ? metricProvenance(SOURCE, context, fields)
52
+ : metricProvenance(SOURCE, context, fields, authority);
53
+ };
54
+ const signalModel = parseUfiSignal({
55
+ sysinfo: input.sysinfo,
56
+ overview: input.overview,
57
+ status: input.status,
58
+ });
59
+ const details = parseUfiDetails({
60
+ overview: input.overview,
61
+ sysinfo: input.sysinfo,
62
+ ...(input.produceInfo === undefined ? {} : { produceInfo: input.produceInfo }),
63
+ });
64
+ const product = details?.product;
65
+ return freshObservation(SOURCE, context, {
66
+ source: SOURCE,
67
+ hardware: routerHardware(provenance, product === undefined
68
+ ? undefined
69
+ : { name: product, field: rawKey(PRODUCE_INFO, 'productname') }),
70
+ radio: {
71
+ modemState: unsupportedRadioMetric(provenance),
72
+ registration: unsupportedRadioMetric(provenance),
73
+ accessTechnologies: unknownMetric('unsupported', provenance([])),
74
+ modeLabel: unknownMetric('not-reported', provenance([])),
75
+ },
76
+ signal: {
77
+ quality: unknownMetric('unsupported', provenance([])),
78
+ qualityRecent: unsupportedQualityRecent(provenance),
79
+ bars: metricFromRouterSignal(signalModel.bars, provenance([])),
80
+ maxBars: metricFromRouterSignal(signalModel.max_bars, provenance([])),
81
+ dbm: metricFromRouterSignal(signalModel.dbm, provenance([
82
+ rawKey(SYSINFO, 'SIGNAL'),
83
+ rawKey(OVERVIEW, 'SIGNAL'),
84
+ rawKey(STATUS, 'signalStrength'),
85
+ ])),
86
+ rsrp: metricFromRouterSignal(signalModel.rsrp, provenance([])),
87
+ rsrq: metricFromRouterSignal(signalModel.rsrq, provenance([])),
88
+ snr: metricFromRouterSignal(signalModel.snr, provenance([])),
89
+ sinr: metricFromRouterSignal(signalModel.sinr, provenance([])),
90
+ },
91
+ sim: routerSim(provenance, hasRawField(raw, SIM_STATE) ? SIM_STATE : undefined),
92
+ diagnostics: createObservationDiagnostics({ source: SOURCE, raw, consumed, notes }),
93
+ });
94
+ }
95
+ /** `{reply, params}` flattened to one level — `params` members keep their own names. */
96
+ function flattenUfiEnvelope(parsed) {
97
+ const out = {};
98
+ if (parsed === undefined) {
99
+ return out;
100
+ }
101
+ for (const [key, value] of Object.entries(parsed)) {
102
+ if (key === 'params' && typeof value === 'object' && value !== null && !Array.isArray(value)) {
103
+ for (const [field, param] of Object.entries(value)) {
104
+ out[field] = normalizeRawValue(param);
105
+ }
106
+ continue;
107
+ }
108
+ out[key] = normalizeRawValue(value);
109
+ }
110
+ return out;
111
+ }
@@ -0,0 +1,7 @@
1
+ import type { ObservationEnvelope } from '../../domain/index.js';
2
+ import { type NormalizationContext } from '../envelope.js';
3
+ import type { NormalizedModemObservation } from '../model.js';
4
+ export type ZteObservationInput = {
5
+ readonly body: string;
6
+ };
7
+ export declare function normalizeZteObservation(input: ZteObservationInput, context: NormalizationContext): ObservationEnvelope<NormalizedModemObservation>;
@@ -0,0 +1,71 @@
1
+ // ZTE goform → normalized observation.
2
+ //
3
+ // One goform response answers many keys at once, so the migrated `parseZteSignal` and
4
+ // `parseZteDetails` are both driven from the SAME body here — and everything neither
5
+ // of them claims (`wan_lte_ca`, `rmcc`, `lte_pci`, the cell id, the roaming flag) is
6
+ // retained verbatim in the diagnostics block. That retention is the point: a
7
+ // carrier-aggregation flag no normalized field has a slot for is still the difference
8
+ // between a diagnosable report and a shrug.
9
+ //
10
+ // A body that will not parse is an OBSERVATION whose metrics are `malformed`, not an
11
+ // unavailable one. The bytes arrived; what failed was decoding them, and an
12
+ // unavailable envelope would have to discard them to say so.
13
+ import { parseZteDetails, parseZteSignal } from '../../hardware/router-parsers.js';
14
+ import { freshObservation, metricProvenance } from '../envelope.js';
15
+ import { knownMetric, metricFromRouterSignal, unknownMetric, } from '../metric.js';
16
+ import { createObservationDiagnostics } from '../provenance.js';
17
+ import { hasRawField, parseJsonRecord, prefixRawRecord, rawKey } from '../raw.js';
18
+ import { routerHardware, routerSim, unsupportedQualityRecent, unsupportedRadioMetric, } from './router-shared.js';
19
+ const SOURCE = 'zte-goform';
20
+ const BODY = 'goform';
21
+ /** ZTE's own SIM presence code. Named as evidence, never decoded into a presence. */
22
+ const SIM_CARD_STATE = rawKey(BODY, 'simcard_state');
23
+ export function normalizeZteObservation(input, context) {
24
+ const parsed = parseJsonRecord(input.body);
25
+ const raw = prefixRawRecord(BODY, parsed);
26
+ const notes = [];
27
+ if (parsed === undefined) {
28
+ notes.push({ code: input.body.trim() === '' ? 'empty-body' : 'unparseable-body', field: BODY });
29
+ }
30
+ const consumed = [];
31
+ const provenance = (fields, authority) => {
32
+ consumed.push(...fields);
33
+ return authority === undefined
34
+ ? metricProvenance(SOURCE, context, fields)
35
+ : metricProvenance(SOURCE, context, fields, authority);
36
+ };
37
+ const signalModel = parseZteSignal(input.body);
38
+ const details = parseZteDetails(input.body);
39
+ return freshObservation(SOURCE, context, {
40
+ source: SOURCE,
41
+ hardware: routerHardware(provenance, undefined),
42
+ radio: {
43
+ modemState: unsupportedRadioMetric(provenance),
44
+ registration: unsupportedRadioMetric(provenance),
45
+ accessTechnologies: unknownMetric('unsupported', provenance([])),
46
+ modeLabel: normalizeModeLabel(details?.network_type, parsed !== undefined, provenance),
47
+ },
48
+ signal: {
49
+ quality: unknownMetric('unsupported', provenance([])),
50
+ qualityRecent: unsupportedQualityRecent(provenance),
51
+ bars: metricFromRouterSignal(signalModel.bars, provenance([rawKey(BODY, 'signalbar')], 'derived')),
52
+ // The goform payload carries no maximum; the migrated parser states the
53
+ // vendor's fixed five-bar scale, which is a derivation and says so.
54
+ maxBars: metricFromRouterSignal(signalModel.max_bars, provenance([], 'derived')),
55
+ dbm: metricFromRouterSignal(signalModel.dbm, provenance([rawKey(BODY, 'rssi')])),
56
+ rsrp: metricFromRouterSignal(signalModel.rsrp, provenance([rawKey(BODY, 'lte_rsrp')])),
57
+ rsrq: metricFromRouterSignal(signalModel.rsrq, provenance([rawKey(BODY, 'lte_rsrq')])),
58
+ snr: metricFromRouterSignal(signalModel.snr, provenance([rawKey(BODY, 'lte_snr')])),
59
+ sinr: metricFromRouterSignal(signalModel.sinr, provenance([])),
60
+ },
61
+ sim: routerSim(provenance, hasRawField(raw, SIM_CARD_STATE) ? SIM_CARD_STATE : undefined),
62
+ diagnostics: createObservationDiagnostics({ source: SOURCE, raw, consumed, notes }),
63
+ });
64
+ }
65
+ function normalizeModeLabel(networkType, parsed, provenance) {
66
+ const source = provenance([rawKey(BODY, 'network_type')]);
67
+ if (networkType !== undefined) {
68
+ return knownMetric(networkType, source);
69
+ }
70
+ return unknownMetric(parsed ? 'not-reported' : 'malformed', source);
71
+ }
@@ -0,0 +1,64 @@
1
+ import type { DeviceGeneration, EpochMillis, ObservationEnvelope } from '../domain/index.js';
2
+ /** What an operator asked for. Not a claim that anything acted on it. */
3
+ export type DesiredProfile<T> = {
4
+ readonly kind: 'desired';
5
+ readonly profile: T;
6
+ readonly requestedAt: EpochMillis;
7
+ /** Opaque origin label (an RPC caller, a policy engine). Never a credential. */
8
+ readonly requestedBy: string;
9
+ };
10
+ /** What was actually put into force, and by which operation, in which generation. */
11
+ export type AppliedConfiguration<T> = {
12
+ readonly kind: 'applied';
13
+ readonly configuration: T;
14
+ readonly appliedAt: EpochMillis;
15
+ readonly generation: DeviceGeneration;
16
+ readonly operationId: string;
17
+ };
18
+ /** What the device itself reports — always through an envelope, never bare. */
19
+ export type ObservedState<T> = {
20
+ readonly kind: 'observed';
21
+ readonly observation: ObservationEnvelope<T>;
22
+ };
23
+ export declare function desiredProfile<T>(profile: T, requestedAt: EpochMillis, requestedBy: string): DesiredProfile<T>;
24
+ export declare function appliedConfiguration<T>(input: {
25
+ readonly configuration: T;
26
+ readonly appliedAt: EpochMillis;
27
+ readonly generation: DeviceGeneration;
28
+ readonly operationId: string;
29
+ }): AppliedConfiguration<T>;
30
+ export declare function observedState<T>(observation: ObservationEnvelope<T>): ObservedState<T>;
31
+ /**
32
+ * The three-slot view. There is deliberately no fourth field holding a merged
33
+ * "effective" value: an effective value is a RENDERING decision, and computing one
34
+ * here would bake a policy every consumer would then have to work around.
35
+ */
36
+ export type ModemStateView<TDesired, TApplied, TObserved> = {
37
+ readonly desired: DesiredProfile<TDesired> | null;
38
+ readonly applied: AppliedConfiguration<TApplied> | null;
39
+ readonly observed: ObservedState<TObserved>;
40
+ };
41
+ export declare const STATE_VIEW_SLOTS: readonly ["desired", "applied", "observed"];
42
+ export type StateViewSlot = (typeof STATE_VIEW_SLOTS)[number];
43
+ export type SlotComparison = {
44
+ readonly status: 'aligned';
45
+ } | {
46
+ readonly status: 'diverged';
47
+ }
48
+ /** One side has nothing to compare — NOT the same as agreeing. */
49
+ | {
50
+ readonly status: 'indeterminate';
51
+ readonly missing: StateViewSlot;
52
+ };
53
+ /**
54
+ * Two independent comparisons, never one verdict.
55
+ *
56
+ * `desiredVsApplied` answers "did our write happen"; `appliedVsObserved` answers "did
57
+ * it stick". A single boolean cannot distinguish a request that was never carried out
58
+ * from one the network undid a second later, and those need opposite responses.
59
+ */
60
+ export type StateDivergence = {
61
+ readonly desiredVsApplied: SlotComparison;
62
+ readonly appliedVsObserved: SlotComparison;
63
+ };
64
+ export declare function describeStateDivergence<T>(view: ModemStateView<T, T, T>, equals: (left: T, right: T) => boolean): StateDivergence;