@ceralive/modem-control 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (474) hide show
  1. package/README.md +317 -0
  2. package/dist/backend/at-lease.d.ts +57 -0
  3. package/dist/backend/at-lease.js +109 -0
  4. package/dist/backend/cell-info.d.ts +46 -0
  5. package/dist/backend/cell-info.js +124 -0
  6. package/dist/backend/constants.d.ts +24 -0
  7. package/{src/backend/constants.ts → dist/backend/constants.js} +4 -9
  8. package/dist/backend/device-classifier.d.ts +46 -0
  9. package/dist/backend/device-classifier.js +258 -0
  10. package/dist/backend/enrichment.d.ts +28 -0
  11. package/dist/backend/enrichment.js +59 -0
  12. package/dist/backend/features.d.ts +61 -0
  13. package/dist/backend/features.js +109 -0
  14. package/dist/backend/identity-ladder.d.ts +57 -0
  15. package/dist/backend/identity-ladder.js +158 -0
  16. package/dist/backend/identity-registry.d.ts +51 -0
  17. package/dist/backend/identity-registry.js +101 -0
  18. package/dist/backend/index.d.ts +30 -0
  19. package/dist/backend/index.js +35 -0
  20. package/dist/backend/lifecycle-interlock.d.ts +25 -0
  21. package/dist/backend/lifecycle-interlock.js +18 -0
  22. package/dist/backend/managed-objects.d.ts +39 -0
  23. package/dist/backend/managed-objects.js +82 -0
  24. package/dist/backend/mapping.d.ts +11 -0
  25. package/dist/backend/mapping.js +148 -0
  26. package/dist/backend/mm-backend.d.ts +44 -0
  27. package/dist/backend/mm-backend.js +154 -0
  28. package/dist/backend/mm-location.d.ts +21 -0
  29. package/dist/backend/mm-location.js +237 -0
  30. package/dist/backend/mm-mutations.d.ts +42 -0
  31. package/dist/backend/mm-mutations.js +259 -0
  32. package/dist/backend/modem-actor.d.ts +41 -0
  33. package/dist/backend/modem-actor.js +78 -0
  34. package/dist/backend/nm-auto-apn.d.ts +54 -0
  35. package/dist/backend/nm-auto-apn.js +124 -0
  36. package/dist/backend/nm-gsm-fields.d.ts +23 -0
  37. package/dist/backend/nm-gsm-fields.js +107 -0
  38. package/dist/backend/nmcli-nm-port.d.ts +28 -0
  39. package/dist/backend/nmcli-nm-port.js +173 -0
  40. package/dist/backend/nmcli-runner.d.ts +24 -0
  41. package/dist/backend/nmcli-runner.js +35 -0
  42. package/dist/backend/observer.d.ts +37 -0
  43. package/dist/backend/observer.js +219 -0
  44. package/dist/backend/power-contract.d.ts +49 -0
  45. package/dist/backend/power-contract.js +34 -0
  46. package/dist/backend/recovery-attribution.d.ts +32 -0
  47. package/dist/backend/recovery-attribution.js +57 -0
  48. package/dist/backend/recovery-budget.d.ts +45 -0
  49. package/dist/backend/recovery-budget.js +44 -0
  50. package/dist/backend/recovery-ladder.d.ts +94 -0
  51. package/dist/backend/recovery-ladder.js +116 -0
  52. package/dist/backend/router-ethernet.d.ts +19 -0
  53. package/dist/backend/router-ethernet.js +66 -0
  54. package/dist/backend/row-store.d.ts +13 -0
  55. package/dist/backend/row-store.js +82 -0
  56. package/dist/backend/signal-setup.d.ts +30 -0
  57. package/dist/backend/signal-setup.js +92 -0
  58. package/dist/backend/sim-unlock.d.ts +13 -0
  59. package/dist/backend/sim-unlock.js +153 -0
  60. package/dist/backend/transition-preconditions.d.ts +76 -0
  61. package/dist/backend/transition-preconditions.js +64 -0
  62. package/dist/backend/usage/accounting.d.ts +39 -0
  63. package/dist/backend/usage/accounting.js +73 -0
  64. package/dist/backend/usage/billing-cycle.d.ts +11 -0
  65. package/dist/backend/usage/billing-cycle.js +39 -0
  66. package/dist/backend/usage/boot-id.d.ts +6 -0
  67. package/{src/backend/usage/boot-id.ts → dist/backend/usage/boot-id.js} +7 -7
  68. package/dist/backend/usage/index.d.ts +8 -0
  69. package/dist/backend/usage/index.js +11 -0
  70. package/dist/backend/usage/policy-store.d.ts +50 -0
  71. package/dist/backend/usage/policy-store.js +161 -0
  72. package/dist/backend/usage/policy-write.d.ts +65 -0
  73. package/dist/backend/usage/policy-write.js +112 -0
  74. package/dist/backend/usage/proc-net-dev.d.ts +18 -0
  75. package/{src/backend/usage/proc-net-dev.ts → dist/backend/usage/proc-net-dev.js} +39 -47
  76. package/dist/backend/usage/sampler.d.ts +75 -0
  77. package/dist/backend/usage/sampler.js +211 -0
  78. package/dist/backend/usage/store.d.ts +38 -0
  79. package/dist/backend/usage/store.js +126 -0
  80. package/dist/backend/usb-device-snapshot.d.ts +31 -0
  81. package/dist/backend/usb-device-snapshot.js +1 -0
  82. package/dist/backend/usb-enumerator.d.ts +21 -0
  83. package/dist/backend/usb-enumerator.js +153 -0
  84. package/dist/backend/usb-mode-transition.d.ts +29 -0
  85. package/dist/backend/usb-mode-transition.js +196 -0
  86. package/dist/band/band-names.d.ts +42 -0
  87. package/dist/band/band-names.js +150 -0
  88. package/dist/band/certification.d.ts +84 -0
  89. package/dist/band/certification.js +127 -0
  90. package/dist/band/certified-bands.json +4 -0
  91. package/dist/band/index.d.ts +2 -0
  92. package/dist/band/index.js +8 -0
  93. package/dist/capability/detect.d.ts +52 -0
  94. package/dist/capability/detect.js +86 -0
  95. package/dist/capability/five-g-preference.d.ts +104 -0
  96. package/dist/capability/five-g-preference.js +171 -0
  97. package/dist/capability/index.d.ts +3 -0
  98. package/dist/capability/index.js +10 -0
  99. package/dist/capability/support-claim.d.ts +39 -0
  100. package/dist/capability/support-claim.js +81 -0
  101. package/dist/domain/brand.d.ts +10 -0
  102. package/dist/domain/brand.js +21 -0
  103. package/dist/domain/errors.d.ts +32 -0
  104. package/dist/domain/errors.js +48 -0
  105. package/dist/domain/generation.d.ts +8 -0
  106. package/dist/domain/generation.js +12 -0
  107. package/dist/domain/guards.d.ts +6 -0
  108. package/dist/domain/guards.js +127 -0
  109. package/dist/domain/identity.d.ts +109 -0
  110. package/dist/domain/identity.js +86 -0
  111. package/dist/domain/index.d.ts +14 -0
  112. package/dist/domain/index.js +18 -0
  113. package/dist/domain/mm-enums.d.ts +12 -0
  114. package/dist/domain/mm-enums.js +139 -0
  115. package/dist/domain/modem-presentation.d.ts +10 -0
  116. package/dist/domain/modem-presentation.js +39 -0
  117. package/dist/domain/observation.d.ts +39 -0
  118. package/dist/domain/observation.js +4 -0
  119. package/dist/domain/operation.d.ts +118 -0
  120. package/dist/domain/operation.js +85 -0
  121. package/dist/domain/physical-identity.d.ts +43 -0
  122. package/dist/domain/physical-identity.js +113 -0
  123. package/{src/domain/policy.ts → dist/domain/policy.d.ts} +34 -69
  124. package/dist/domain/policy.js +38 -0
  125. package/dist/domain/shadow-divergence.d.ts +27 -0
  126. package/dist/domain/shadow-divergence.js +70 -0
  127. package/dist/domain/snapshot.d.ts +53 -0
  128. package/dist/domain/snapshot.js +75 -0
  129. package/{src/domain/state.ts → dist/domain/state.d.ts} +23 -122
  130. package/dist/domain/state.js +38 -0
  131. package/dist/fcc/coverage.d.ts +63 -0
  132. package/dist/fcc/coverage.js +102 -0
  133. package/dist/fcc/index.d.ts +3 -0
  134. package/dist/fcc/index.js +12 -0
  135. package/dist/fcc/policy-store.d.ts +40 -0
  136. package/dist/fcc/policy-store.js +146 -0
  137. package/dist/fcc/policy-write.d.ts +32 -0
  138. package/dist/fcc/policy-write.js +37 -0
  139. package/dist/hardware/hilink-protocol.d.ts +39 -0
  140. package/dist/hardware/hilink-protocol.js +58 -0
  141. package/dist/hardware/index.d.ts +3 -0
  142. package/dist/hardware/index.js +15 -0
  143. package/dist/hardware/router-parsers.d.ts +89 -0
  144. package/dist/hardware/router-parsers.js +234 -0
  145. package/dist/index.d.ts +19 -0
  146. package/dist/index.js +26 -0
  147. package/dist/journal/codec.d.ts +44 -0
  148. package/dist/journal/codec.js +198 -0
  149. package/dist/journal/engine.d.ts +28 -0
  150. package/dist/journal/engine.js +68 -0
  151. package/dist/journal/entry.d.ts +74 -0
  152. package/dist/journal/entry.js +56 -0
  153. package/dist/journal/index.d.ts +6 -0
  154. package/dist/journal/index.js +6 -0
  155. package/dist/journal/legacy-ceraui.d.ts +73 -0
  156. package/dist/journal/legacy-ceraui.js +227 -0
  157. package/dist/journal/recovery.d.ts +58 -0
  158. package/dist/journal/recovery.js +117 -0
  159. package/dist/journal/store.d.ts +55 -0
  160. package/dist/journal/store.js +150 -0
  161. package/dist/location/fix-state.d.ts +53 -0
  162. package/dist/location/fix-state.js +75 -0
  163. package/dist/location/index.d.ts +2 -0
  164. package/dist/location/index.js +8 -0
  165. package/dist/location/nmea.d.ts +10 -0
  166. package/dist/location/nmea.js +89 -0
  167. package/dist/observations/envelope.d.ts +63 -0
  168. package/dist/observations/envelope.js +77 -0
  169. package/dist/observations/freshness.d.ts +28 -0
  170. package/dist/observations/freshness.js +76 -0
  171. package/dist/observations/index.d.ts +13 -0
  172. package/dist/observations/index.js +24 -0
  173. package/dist/observations/metric.d.ts +47 -0
  174. package/dist/observations/metric.js +73 -0
  175. package/dist/observations/model.d.ts +80 -0
  176. package/dist/observations/model.js +11 -0
  177. package/dist/observations/provenance.d.ts +94 -0
  178. package/dist/observations/provenance.js +67 -0
  179. package/dist/observations/raw.d.ts +42 -0
  180. package/dist/observations/raw.js +146 -0
  181. package/dist/observations/reading.d.ts +51 -0
  182. package/dist/observations/reading.js +65 -0
  183. package/dist/observations/sources/hilink.d.ts +10 -0
  184. package/dist/observations/sources/hilink.js +84 -0
  185. package/dist/observations/sources/modemmanager.d.ts +18 -0
  186. package/dist/observations/sources/modemmanager.js +239 -0
  187. package/dist/observations/sources/router-shared.d.ts +29 -0
  188. package/dist/observations/sources/router-shared.js +68 -0
  189. package/dist/observations/sources/ufi.d.ts +10 -0
  190. package/dist/observations/sources/ufi.js +111 -0
  191. package/dist/observations/sources/zte.d.ts +7 -0
  192. package/dist/observations/sources/zte.js +71 -0
  193. package/dist/observations/state-separation.d.ts +64 -0
  194. package/dist/observations/state-separation.js +52 -0
  195. package/dist/operations/contracts.d.ts +72 -0
  196. package/dist/operations/contracts.js +1 -0
  197. package/dist/operations/index.d.ts +1 -0
  198. package/dist/operations/index.js +1 -0
  199. package/dist/operations/operation-engine.d.ts +16 -0
  200. package/dist/operations/operation-engine.js +194 -0
  201. package/dist/ports/index.d.ts +12 -0
  202. package/{src/ports/index.ts → dist/ports/index.js} +12 -8
  203. package/dist/ports/location.d.ts +87 -0
  204. package/dist/ports/location.js +36 -0
  205. package/dist/ports/modem-manager.d.ts +89 -0
  206. package/dist/ports/modem-manager.js +9 -0
  207. package/dist/ports/mutation-admission.d.ts +27 -0
  208. package/dist/ports/mutation-admission.js +9 -0
  209. package/dist/ports/network-manager.d.ts +68 -0
  210. package/dist/ports/network-manager.js +13 -0
  211. package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
  212. package/dist/ports/observation.js +7 -0
  213. package/dist/ports/ops.d.ts +44 -0
  214. package/dist/ports/ops.js +16 -0
  215. package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
  216. package/dist/ports/receipts.js +9 -0
  217. package/dist/ports/reconcile.d.ts +33 -0
  218. package/dist/ports/reconcile.js +200 -0
  219. package/dist/ports/resource-ownership.d.ts +29 -0
  220. package/dist/ports/resource-ownership.js +1 -0
  221. package/dist/ports/router.d.ts +19 -0
  222. package/dist/ports/router.js +7 -0
  223. package/dist/ports/sms.d.ts +64 -0
  224. package/dist/ports/sms.js +24 -0
  225. package/dist/ports/uhubctl.d.ts +6 -0
  226. package/dist/ports/uhubctl.js +1 -0
  227. package/dist/providers/contracts.d.ts +124 -0
  228. package/dist/providers/contracts.js +10 -0
  229. package/dist/providers/huawei-hilink/index.d.ts +2 -0
  230. package/dist/providers/huawei-hilink/index.js +2 -0
  231. package/dist/providers/huawei-hilink/operations.d.ts +20 -0
  232. package/dist/providers/huawei-hilink/operations.js +56 -0
  233. package/dist/providers/huawei-hilink/provider.d.ts +52 -0
  234. package/dist/providers/huawei-hilink/provider.js +76 -0
  235. package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
  236. package/dist/providers/huawei-hilink/runtime.js +171 -0
  237. package/dist/providers/huawei-hilink/session.d.ts +28 -0
  238. package/dist/providers/huawei-hilink/session.js +120 -0
  239. package/dist/providers/huawei-hilink/transport.d.ts +19 -0
  240. package/dist/providers/huawei-hilink/transport.js +1 -0
  241. package/dist/providers/index.d.ts +8 -0
  242. package/dist/providers/index.js +8 -0
  243. package/dist/providers/matcher.d.ts +3 -0
  244. package/dist/providers/matcher.js +205 -0
  245. package/dist/providers/modem-manager/errors.d.ts +7 -0
  246. package/dist/providers/modem-manager/errors.js +37 -0
  247. package/dist/providers/modem-manager/generic-operations.d.ts +8 -0
  248. package/dist/providers/modem-manager/generic-operations.js +207 -0
  249. package/dist/providers/modem-manager/index.d.ts +3 -0
  250. package/dist/providers/modem-manager/index.js +3 -0
  251. package/dist/providers/modem-manager/module-operations.d.ts +21 -0
  252. package/dist/providers/modem-manager/module-operations.js +118 -0
  253. package/dist/providers/modem-manager/provider.d.ts +39 -0
  254. package/dist/providers/modem-manager/provider.js +152 -0
  255. package/dist/providers/modem-manager/snapshot.d.ts +5 -0
  256. package/dist/providers/modem-manager/snapshot.js +204 -0
  257. package/dist/providers/modem-manager/types.d.ts +135 -0
  258. package/dist/providers/modem-manager/types.js +1 -0
  259. package/dist/providers/network-manager/adapter.d.ts +71 -0
  260. package/dist/providers/network-manager/adapter.js +348 -0
  261. package/dist/providers/network-manager/index.d.ts +2 -0
  262. package/dist/providers/network-manager/index.js +2 -0
  263. package/dist/providers/network-manager/types.d.ts +163 -0
  264. package/dist/providers/network-manager/types.js +77 -0
  265. package/dist/providers/registry.d.ts +13 -0
  266. package/dist/providers/registry.js +33 -0
  267. package/dist/providers/ufi-himi/index.d.ts +6 -0
  268. package/dist/providers/ufi-himi/index.js +6 -0
  269. package/dist/providers/ufi-himi/operations.d.ts +41 -0
  270. package/dist/providers/ufi-himi/operations.js +66 -0
  271. package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
  272. package/dist/providers/ufi-himi/prohibitions.js +88 -0
  273. package/dist/providers/ufi-himi/provider.d.ts +41 -0
  274. package/dist/providers/ufi-himi/provider.js +204 -0
  275. package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
  276. package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
  277. package/dist/providers/ufi-himi/session.d.ts +46 -0
  278. package/dist/providers/ufi-himi/session.js +92 -0
  279. package/dist/providers/ufi-himi/transport.d.ts +29 -0
  280. package/dist/providers/ufi-himi/transport.js +25 -0
  281. package/dist/providers/zte-goform/index.d.ts +2 -0
  282. package/dist/providers/zte-goform/index.js +2 -0
  283. package/dist/providers/zte-goform/provider.d.ts +51 -0
  284. package/dist/providers/zte-goform/provider.js +101 -0
  285. package/dist/providers/zte-goform/session.d.ts +17 -0
  286. package/dist/providers/zte-goform/session.js +133 -0
  287. package/dist/providers/zte-goform/transport.d.ts +16 -0
  288. package/dist/providers/zte-goform/transport.js +1 -0
  289. package/dist/radio/band-truth.d.ts +50 -0
  290. package/dist/radio/band-truth.js +92 -0
  291. package/dist/radio/index.d.ts +3 -0
  292. package/dist/radio/index.js +10 -0
  293. package/dist/radio/mode-combinations.d.ts +78 -0
  294. package/dist/radio/mode-combinations.js +198 -0
  295. package/dist/radio/mode-truth.d.ts +67 -0
  296. package/dist/radio/mode-truth.js +112 -0
  297. package/dist/redact.d.ts +15 -0
  298. package/dist/redact.js +189 -0
  299. package/dist/safety/composition-root.d.ts +28 -0
  300. package/dist/safety/composition-root.js +65 -0
  301. package/dist/safety/flock-resource-ownership.d.ts +11 -0
  302. package/dist/safety/flock-resource-ownership.js +151 -0
  303. package/dist/safety/index.d.ts +2 -0
  304. package/dist/safety/index.js +2 -0
  305. package/dist/sms/dbus-messaging.d.ts +17 -0
  306. package/dist/sms/dbus-messaging.js +185 -0
  307. package/dist/sms/inbox-store.d.ts +10 -0
  308. package/dist/sms/inbox-store.js +82 -0
  309. package/dist/sms/index.d.ts +4 -0
  310. package/dist/sms/index.js +10 -0
  311. package/dist/sms/mmcli-parse.d.ts +54 -0
  312. package/dist/sms/mmcli-parse.js +224 -0
  313. package/dist/sms/normalize.d.ts +42 -0
  314. package/dist/sms/normalize.js +95 -0
  315. package/dist/testing/domain-fakes.d.ts +58 -0
  316. package/dist/testing/domain-fakes.js +98 -0
  317. package/dist/testing/index.d.ts +2 -0
  318. package/dist/testing/index.js +17 -0
  319. package/dist/testing/provider-fakes.d.ts +45 -0
  320. package/dist/testing/provider-fakes.js +64 -0
  321. package/dist/transport/calls.d.ts +9 -0
  322. package/dist/transport/calls.js +88 -0
  323. package/dist/transport/codec.d.ts +3 -0
  324. package/dist/transport/codec.js +207 -0
  325. package/dist/transport/dbus-native.d.ts +57 -0
  326. package/dist/transport/dbus-native.js +17 -0
  327. package/dist/transport/errors.d.ts +21 -0
  328. package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
  329. package/dist/transport/index.d.ts +4 -0
  330. package/dist/transport/index.js +9 -0
  331. package/dist/transport/signals.d.ts +15 -0
  332. package/dist/transport/signals.js +123 -0
  333. package/dist/transport/signature.d.ts +7 -0
  334. package/dist/transport/signature.js +94 -0
  335. package/dist/transport/transport.d.ts +2 -0
  336. package/dist/transport/transport.js +202 -0
  337. package/dist/transport/types.d.ts +61 -0
  338. package/dist/transport/types.js +19 -0
  339. package/dist/usb-mode/catalog-schema.d.ts +139 -0
  340. package/dist/usb-mode/catalog-schema.js +97 -0
  341. package/dist/usb-mode/catalog.d.ts +21 -0
  342. package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
  343. package/dist/usb-mode/certified-catalog.json +67 -0
  344. package/dist/usb-mode/index.d.ts +5 -0
  345. package/dist/usb-mode/index.js +15 -0
  346. package/dist/usb-mode/ingestion.d.ts +111 -0
  347. package/dist/usb-mode/ingestion.js +187 -0
  348. package/dist/usb-mode/promotion-review.d.ts +21 -0
  349. package/dist/usb-mode/promotion-review.js +87 -0
  350. package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
  351. package/dist/usb-mode/usb-devices-parse.js +157 -0
  352. package/dist/ussd/calls.d.ts +32 -0
  353. package/dist/ussd/calls.js +96 -0
  354. package/dist/ussd/index.d.ts +5 -0
  355. package/dist/ussd/index.js +12 -0
  356. package/dist/ussd/mm-ussd.d.ts +37 -0
  357. package/dist/ussd/mm-ussd.js +205 -0
  358. package/dist/ussd/refusal.d.ts +53 -0
  359. package/dist/ussd/refusal.js +154 -0
  360. package/dist/ussd/registration.d.ts +20 -0
  361. package/dist/ussd/registration.js +101 -0
  362. package/dist/ussd/session.d.ts +84 -0
  363. package/dist/ussd/session.js +163 -0
  364. package/package.json +38 -4
  365. package/src/backend/at-lease.test.ts +0 -106
  366. package/src/backend/at-lease.ts +0 -158
  367. package/src/backend/cell-info.test.ts +0 -154
  368. package/src/backend/cell-info.ts +0 -160
  369. package/src/backend/device-classifier.test.ts +0 -168
  370. package/src/backend/device-classifier.ts +0 -248
  371. package/src/backend/enrichment.ts +0 -96
  372. package/src/backend/features.test.ts +0 -162
  373. package/src/backend/features.ts +0 -179
  374. package/src/backend/identity-ladder.test.ts +0 -117
  375. package/src/backend/identity-ladder.ts +0 -221
  376. package/src/backend/identity-registry.test.ts +0 -89
  377. package/src/backend/identity-registry.ts +0 -151
  378. package/src/backend/index.ts +0 -236
  379. package/src/backend/lifecycle-interlock.ts +0 -38
  380. package/src/backend/managed-objects.ts +0 -108
  381. package/src/backend/mapping.ts +0 -160
  382. package/src/backend/mm-backend.ts +0 -191
  383. package/src/backend/mm-mutations.ts +0 -228
  384. package/src/backend/modem-actor.test.ts +0 -95
  385. package/src/backend/modem-actor.ts +0 -112
  386. package/src/backend/nm-auto-apn.ts +0 -161
  387. package/src/backend/nm-gsm-fields.ts +0 -122
  388. package/src/backend/nmcli-nm-port.ts +0 -228
  389. package/src/backend/nmcli-runner.ts +0 -52
  390. package/src/backend/observer.ts +0 -297
  391. package/src/backend/power-contract.test.ts +0 -40
  392. package/src/backend/power-contract.ts +0 -83
  393. package/src/backend/recovery-attribution.test.ts +0 -102
  394. package/src/backend/recovery-attribution.ts +0 -86
  395. package/src/backend/recovery-budget.test.ts +0 -64
  396. package/src/backend/recovery-budget.ts +0 -84
  397. package/src/backend/recovery-ladder.test.ts +0 -257
  398. package/src/backend/recovery-ladder.ts +0 -249
  399. package/src/backend/router-ethernet.test.ts +0 -71
  400. package/src/backend/router-ethernet.ts +0 -90
  401. package/src/backend/row-store.ts +0 -105
  402. package/src/backend/signal-setup.ts +0 -112
  403. package/src/backend/sim-unlock.ts +0 -193
  404. package/src/backend/transition-preconditions.ts +0 -149
  405. package/src/backend/uhubctl-power-hook.test.ts +0 -274
  406. package/src/backend/uhubctl-power-hook.ts +0 -377
  407. package/src/backend/usage/accounting.test.ts +0 -147
  408. package/src/backend/usage/accounting.ts +0 -123
  409. package/src/backend/usage/billing-cycle.test.ts +0 -62
  410. package/src/backend/usage/billing-cycle.ts +0 -45
  411. package/src/backend/usage/index.ts +0 -60
  412. package/src/backend/usage/policy-store.test.ts +0 -164
  413. package/src/backend/usage/policy-store.ts +0 -216
  414. package/src/backend/usage/policy-write.test.ts +0 -198
  415. package/src/backend/usage/policy-write.ts +0 -207
  416. package/src/backend/usage/proc-net-dev.test.ts +0 -56
  417. package/src/backend/usage/sampler.test.ts +0 -327
  418. package/src/backend/usage/sampler.ts +0 -282
  419. package/src/backend/usage/store.test.ts +0 -148
  420. package/src/backend/usage/store.ts +0 -177
  421. package/src/backend/usb-enumerator.test.ts +0 -87
  422. package/src/backend/usb-enumerator.ts +0 -181
  423. package/src/backend/usb-mode-transition.test.ts +0 -323
  424. package/src/backend/usb-mode-transition.ts +0 -253
  425. package/src/domain/brand.ts +0 -29
  426. package/src/domain/errors.ts +0 -77
  427. package/src/domain/guards.test.ts +0 -218
  428. package/src/domain/guards.ts +0 -144
  429. package/src/domain/identity.test.ts +0 -83
  430. package/src/domain/identity.ts +0 -165
  431. package/src/domain/index.ts +0 -12
  432. package/src/domain/snapshot.test.ts +0 -266
  433. package/src/domain/snapshot.ts +0 -120
  434. package/src/index.test.ts +0 -6
  435. package/src/index.ts +0 -15
  436. package/src/ports/README.md +0 -61
  437. package/src/ports/forbidden-surface.test.ts +0 -241
  438. package/src/ports/modem-manager.ts +0 -72
  439. package/src/ports/network-manager.ts +0 -87
  440. package/src/ports/ops.ts +0 -60
  441. package/src/ports/ops.type-test.ts +0 -39
  442. package/src/ports/receipts.test.ts +0 -153
  443. package/src/ports/reconcile.test.ts +0 -152
  444. package/src/ports/reconcile.ts +0 -338
  445. package/src/ports/router.ts +0 -29
  446. package/src/redact.test.ts +0 -82
  447. package/src/redact.ts +0 -73
  448. package/src/transport/README.md +0 -65
  449. package/src/transport/calls.ts +0 -113
  450. package/src/transport/characterization.test.ts +0 -260
  451. package/src/transport/codec.test.ts +0 -118
  452. package/src/transport/codec.ts +0 -240
  453. package/src/transport/conformance-python.test.ts +0 -152
  454. package/src/transport/conformance-same-lib.test.ts +0 -115
  455. package/src/transport/dbus-native-lib.d.ts +0 -19
  456. package/src/transport/dbus-native.ts +0 -85
  457. package/src/transport/index.ts +0 -30
  458. package/src/transport/no-library-leak.test.ts +0 -61
  459. package/src/transport/reliability.test.ts +0 -173
  460. package/src/transport/signals.ts +0 -150
  461. package/src/transport/signature.ts +0 -110
  462. package/src/transport/test-support/fake-service.ts +0 -168
  463. package/src/transport/test-support/independent-producer.py +0 -110
  464. package/src/transport/test-support/private-bus.ts +0 -66
  465. package/src/transport/transport.ts +0 -250
  466. package/src/transport/types.ts +0 -118
  467. package/src/usb-mode/catalog-schema.test.ts +0 -181
  468. package/src/usb-mode/catalog-schema.ts +0 -113
  469. package/src/usb-mode/certified-catalog.json +0 -67
  470. package/src/usb-mode/index.ts +0 -58
  471. package/src/usb-mode/ingestion.test.ts +0 -268
  472. package/src/usb-mode/ingestion.ts +0 -297
  473. package/src/usb-mode/promotion-review.ts +0 -117
  474. package/src/usb-mode/usb-devices-parse.ts +0 -196
@@ -1,218 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { type ImpossibleStateCode, ImpossibleStateError } from './errors';
3
- import { imeiEquipmentId, type ModemIdentity, runtimePath } from './identity';
4
- import type { CellularSnapshot } from './snapshot';
5
- import { createSnapshot, revision } from './snapshot';
6
- import { epochMillis } from './state';
7
-
8
- const IDENTITY: ModemIdentity = {
9
- equipmentId: imeiEquipmentId('490154203237518'),
10
- runtimePath: runtimePath('/org/freedesktop/ModemManager1/Modem/0'),
11
- };
12
-
13
- /** A fully valid registered + connected + NM-activated snapshot to mutate from. */
14
- function connectedBase(): CellularSnapshot {
15
- return {
16
- identity: IDENTITY,
17
- presence: 'present',
18
- sourceHealth: 'live',
19
- simSlots: [{ index: 1, occupied: true, active: true, lock: 'none' }],
20
- radioPower: 'on',
21
- mmState: 'connected',
22
- registration: { status: 'home', activeRats: new Set(['lte']) },
23
- nmActivation: 'activated',
24
- dataInterface: { present: true, name: 'wwan0' },
25
- reconcileStatus: 'converged',
26
- recoveryState: { stage: 'idle', attempts: 0 },
27
- revision: revision(5),
28
- };
29
- }
30
-
31
- function expectImpossible(fields: CellularSnapshot, code: ImpossibleStateCode): void {
32
- expect(() => createSnapshot(fields)).toThrow(ImpossibleStateError);
33
- try {
34
- createSnapshot(fields);
35
- } catch (error) {
36
- expect(error).toBeInstanceOf(ImpossibleStateError);
37
- expect((error as ImpossibleStateError).code).toBe(code);
38
- return;
39
- }
40
- throw new Error(`expected ${code} to throw`);
41
- }
42
-
43
- describe('valid snapshots pass', () => {
44
- test('a coherent connected snapshot constructs', () => {
45
- expect(() => createSnapshot(connectedBase())).not.toThrow();
46
- });
47
-
48
- test('mmState locked WITH an occupied pin-locked slot is valid', () => {
49
- const s: CellularSnapshot = {
50
- ...connectedBase(),
51
- mmState: 'locked',
52
- nmActivation: 'disconnected',
53
- registration: { status: 'idle', activeRats: new Set() },
54
- radioPower: 'on',
55
- dataInterface: { present: false },
56
- simSlots: [{ index: 1, occupied: true, active: true, lock: 'sim-pin' }],
57
- };
58
- expect(() => createSnapshot(s)).not.toThrow();
59
- });
60
-
61
- test('a cooldown recovery state with a deadline is valid', () => {
62
- const s: CellularSnapshot = {
63
- ...connectedBase(),
64
- recoveryState: {
65
- stage: 'cooldown',
66
- attempts: 2,
67
- cooldownUntil: epochMillis(1_700_000_000_000),
68
- },
69
- };
70
- expect(() => createSnapshot(s)).not.toThrow();
71
- });
72
- });
73
-
74
- describe('impossible combinations throw typed errors', () => {
75
- test('registered while absent (the named guard)', () => {
76
- expectImpossible({ ...connectedBase(), presence: 'absent' }, 'registered-while-absent');
77
- });
78
-
79
- test('registered with the radio off', () => {
80
- expectImpossible({ ...connectedBase(), radioPower: 'off' }, 'registered-radio-off');
81
- });
82
-
83
- test('registered with an empty RAT set', () => {
84
- expectImpossible(
85
- { ...connectedBase(), registration: { status: 'home', activeRats: new Set() } },
86
- 'registered-empty-rat-set',
87
- );
88
- });
89
-
90
- test('an active MM state while absent', () => {
91
- expectImpossible(
92
- {
93
- ...connectedBase(),
94
- presence: 'absent',
95
- mmState: 'enabled',
96
- registration: { status: 'idle', activeRats: new Set() },
97
- nmActivation: 'disconnected',
98
- dataInterface: { present: false },
99
- },
100
- 'active-state-while-absent',
101
- );
102
- });
103
-
104
- test('the radio off while searching', () => {
105
- expectImpossible(
106
- {
107
- ...connectedBase(),
108
- radioPower: 'off',
109
- mmState: 'searching',
110
- registration: { status: 'searching', activeRats: new Set() },
111
- nmActivation: 'disconnected',
112
- dataInterface: { present: false },
113
- },
114
- 'radio-off-while-active',
115
- );
116
- });
117
-
118
- test('NM activated while absent', () => {
119
- expectImpossible(
120
- {
121
- ...connectedBase(),
122
- presence: 'absent',
123
- mmState: 'disabled',
124
- registration: { status: 'idle', activeRats: new Set() },
125
- dataInterface: { present: false },
126
- },
127
- 'nm-activated-while-absent',
128
- );
129
- });
130
-
131
- test('NM activated without a data interface', () => {
132
- expectImpossible(
133
- { ...connectedBase(), dataInterface: { present: false } },
134
- 'nm-activated-without-interface',
135
- );
136
- });
137
-
138
- test('NM activated while MM is not connected', () => {
139
- expectImpossible(
140
- { ...connectedBase(), mmState: 'registered' },
141
- 'nm-activated-without-mm-connected',
142
- );
143
- });
144
-
145
- test('more than one active SIM slot', () => {
146
- expectImpossible(
147
- {
148
- ...connectedBase(),
149
- simSlots: [
150
- { index: 1, occupied: true, active: true, lock: 'none' },
151
- { index: 2, occupied: true, active: true, lock: 'none' },
152
- ],
153
- },
154
- 'multiple-active-sim-slots',
155
- );
156
- });
157
-
158
- test('a locked SIM in an empty slot', () => {
159
- expectImpossible(
160
- {
161
- ...connectedBase(),
162
- simSlots: [{ index: 1, occupied: false, active: false, lock: 'sim-pin' }],
163
- },
164
- 'locked-sim-in-empty-slot',
165
- );
166
- });
167
-
168
- test('MM locked without any locked SIM', () => {
169
- expectImpossible(
170
- {
171
- ...connectedBase(),
172
- mmState: 'locked',
173
- nmActivation: 'disconnected',
174
- registration: { status: 'idle', activeRats: new Set() },
175
- dataInterface: { present: false },
176
- simSlots: [{ index: 1, occupied: true, active: true, lock: 'none' }],
177
- },
178
- 'mm-locked-without-sim-lock',
179
- );
180
- });
181
-
182
- test('a data-interface name without presence', () => {
183
- expectImpossible(
184
- {
185
- ...connectedBase(),
186
- mmState: 'disabled',
187
- nmActivation: 'disconnected',
188
- registration: { status: 'idle', activeRats: new Set() },
189
- dataInterface: { present: false, name: 'wwan0' },
190
- },
191
- 'data-interface-name-without-presence',
192
- );
193
- });
194
-
195
- test('a negative recovery attempt count', () => {
196
- expectImpossible(
197
- { ...connectedBase(), recoveryState: { stage: 'nm-cycle', attempts: -1 } },
198
- 'recovery-attempts-negative',
199
- );
200
- });
201
-
202
- test('a cooldown deadline outside the cooldown stage', () => {
203
- expectImpossible(
204
- {
205
- ...connectedBase(),
206
- recoveryState: { stage: 'nm-cycle', attempts: 1, cooldownUntil: epochMillis(123) },
207
- },
208
- 'recovery-cooldown-stage-mismatch',
209
- );
210
- });
211
-
212
- test('idle recovery carrying attempts', () => {
213
- expectImpossible(
214
- { ...connectedBase(), recoveryState: { stage: 'idle', attempts: 3 } },
215
- 'recovery-idle-with-attempts',
216
- );
217
- });
218
- });
@@ -1,144 +0,0 @@
1
- // Impossible-combination guards.
2
- //
3
- // The orthogonal dimensions are independent, but not EVERY combination is
4
- // physically real. A modem cannot be `registered` while `absent`; the radio
5
- // cannot be `off` while the modem is `connected`. Each rule below rejects one
6
- // such impossible cross-dimension combination with a distinct code, so a bad
7
- // construction names exactly what it violated instead of silently persisting an
8
- // incoherent snapshot. `checkSnapshot` returns the first violation (or null);
9
- // `assertSnapshot` throws `ImpossibleStateError`.
10
-
11
- import type { ImpossibleStateCode } from './errors';
12
- import { ImpossibleStateError } from './errors';
13
- import type { CellularSnapshot } from './snapshot';
14
- import { isRegistered, MM_STATES_REQUIRING_RADIO, SIM_LOCK_REQUIRES_CARD } from './state';
15
-
16
- type Violation = { readonly code: ImpossibleStateCode; readonly detail: string } | null;
17
-
18
- function checkRegistration(s: CellularSnapshot): Violation {
19
- if (!isRegistered(s.registration.status)) {
20
- return null;
21
- }
22
- if (s.presence === 'absent') {
23
- return { code: 'registered-while-absent', detail: `status=${s.registration.status}` };
24
- }
25
- if (s.radioPower === 'off') {
26
- return { code: 'registered-radio-off', detail: `status=${s.registration.status}` };
27
- }
28
- if (s.registration.activeRats.size === 0) {
29
- return { code: 'registered-empty-rat-set', detail: `status=${s.registration.status}` };
30
- }
31
- return null;
32
- }
33
-
34
- function radioIsOnTheAir(s: CellularSnapshot): boolean {
35
- return (
36
- MM_STATES_REQUIRING_RADIO.has(s.mmState) ||
37
- s.registration.status === 'searching' ||
38
- s.nmActivation === 'activating' ||
39
- s.nmActivation === 'activated'
40
- );
41
- }
42
-
43
- function checkRadioAndPresence(s: CellularSnapshot): Violation {
44
- if (MM_STATES_REQUIRING_RADIO.has(s.mmState) && s.presence === 'absent') {
45
- return { code: 'active-state-while-absent', detail: `mmState=${s.mmState}` };
46
- }
47
- if (s.radioPower === 'off' && radioIsOnTheAir(s)) {
48
- return { code: 'radio-off-while-active', detail: `mmState=${s.mmState}` };
49
- }
50
- return null;
51
- }
52
-
53
- function checkNmActivation(s: CellularSnapshot): Violation {
54
- if (s.nmActivation !== 'activated') {
55
- return null;
56
- }
57
- if (s.presence === 'absent') {
58
- return { code: 'nm-activated-while-absent', detail: 'nmActivation=activated' };
59
- }
60
- if (!s.dataInterface.present) {
61
- return { code: 'nm-activated-without-interface', detail: 'nmActivation=activated' };
62
- }
63
- if (s.mmState !== 'connected') {
64
- return { code: 'nm-activated-without-mm-connected', detail: `mmState=${s.mmState}` };
65
- }
66
- return null;
67
- }
68
-
69
- function checkSimSlots(s: CellularSnapshot): Violation {
70
- let activeCount = 0;
71
- let hasLockedCard = false;
72
- for (const slot of s.simSlots) {
73
- if (slot.active) {
74
- activeCount += 1;
75
- }
76
- if (!slot.occupied && SIM_LOCK_REQUIRES_CARD.has(slot.lock)) {
77
- return { code: 'locked-sim-in-empty-slot', detail: `slot=${slot.index} lock=${slot.lock}` };
78
- }
79
- if (slot.occupied && SIM_LOCK_REQUIRES_CARD.has(slot.lock)) {
80
- hasLockedCard = true;
81
- }
82
- }
83
- if (activeCount > 1) {
84
- return { code: 'multiple-active-sim-slots', detail: `active=${activeCount}` };
85
- }
86
- if (s.mmState === 'locked' && !hasLockedCard) {
87
- return { code: 'mm-locked-without-sim-lock', detail: 'mmState=locked' };
88
- }
89
- return null;
90
- }
91
-
92
- function checkDataInterface(s: CellularSnapshot): Violation {
93
- if (!s.dataInterface.present && s.dataInterface.name !== undefined) {
94
- return {
95
- code: 'data-interface-name-without-presence',
96
- detail: `name=${s.dataInterface.name}`,
97
- };
98
- }
99
- return null;
100
- }
101
-
102
- function checkRecovery(s: CellularSnapshot): Violation {
103
- const { stage, attempts, cooldownUntil } = s.recoveryState;
104
- if (!Number.isSafeInteger(attempts) || attempts < 0) {
105
- return { code: 'recovery-attempts-negative', detail: `attempts=${attempts}` };
106
- }
107
- if ((cooldownUntil !== undefined) !== (stage === 'cooldown')) {
108
- return { code: 'recovery-cooldown-stage-mismatch', detail: `stage=${stage}` };
109
- }
110
- if (stage === 'idle' && attempts !== 0) {
111
- return { code: 'recovery-idle-with-attempts', detail: `attempts=${attempts}` };
112
- }
113
- return null;
114
- }
115
-
116
- const CHECKS: ReadonlyArray<(s: CellularSnapshot) => Violation> = [
117
- checkRegistration,
118
- checkRadioAndPresence,
119
- checkNmActivation,
120
- checkSimSlots,
121
- checkDataInterface,
122
- checkRecovery,
123
- ];
124
-
125
- /** Return the first impossible-combination code the snapshot violates, or null. */
126
- export function checkSnapshot(snapshot: CellularSnapshot): ImpossibleStateCode | null {
127
- for (const check of CHECKS) {
128
- const violation = check(snapshot);
129
- if (violation !== null) {
130
- return violation.code;
131
- }
132
- }
133
- return null;
134
- }
135
-
136
- /** Throw `ImpossibleStateError` if the snapshot holds an impossible combination. */
137
- export function assertSnapshot(snapshot: CellularSnapshot): void {
138
- for (const check of CHECKS) {
139
- const violation = check(snapshot);
140
- if (violation !== null) {
141
- throw new ImpossibleStateError(violation.code, violation.detail);
142
- }
143
- }
144
- }
@@ -1,83 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { PolicyBindingRefusedError } from './errors';
3
- import {
4
- canBindPolicy,
5
- demoteToLowConfidence,
6
- imeiEquipmentId,
7
- type ModemIdentity,
8
- NO_EQUIPMENT_ID,
9
- runtimePath,
10
- serialEquipmentId,
11
- } from './identity';
12
- import { policyBindingKey } from './policy';
13
-
14
- const PATH = runtimePath('/org/freedesktop/ModemManager1/Modem/0');
15
-
16
- function identityWith(equipmentId: ModemIdentity['equipmentId']): ModemIdentity {
17
- return { equipmentId, runtimePath: PATH };
18
- }
19
-
20
- describe('equipment id confidence grading', () => {
21
- test('a canonical 15-digit IMEI grades high', () => {
22
- const id = imeiEquipmentId('490154203237518');
23
- expect(id).toEqual({ provenance: 'imei', value: '490154203237518', confidence: 'high' });
24
- });
25
-
26
- test('an all-zeros IMEI grades low (ambiguous placeholder)', () => {
27
- expect(imeiEquipmentId('000000000000000').confidence).toBe('low');
28
- });
29
-
30
- test('a blank IMEI grades low', () => {
31
- expect(imeiEquipmentId(' ').confidence).toBe('low');
32
- });
33
-
34
- test('a non-standard IMEI shape grades medium', () => {
35
- expect(imeiEquipmentId('49015420').confidence).toBe('medium');
36
- });
37
-
38
- test('a serial fallback caps at medium', () => {
39
- expect(serialEquipmentId('SN-ABC-123').confidence).toBe('medium');
40
- });
41
-
42
- test('the none provenance carries no value and is always low', () => {
43
- expect(NO_EQUIPMENT_ID).toEqual({ provenance: 'none', confidence: 'low' });
44
- });
45
- });
46
-
47
- describe('duplicate demotion', () => {
48
- test('a duplicate IMEI is demoted to low confidence but keeps its value', () => {
49
- const original = imeiEquipmentId('490154203237518');
50
- const demoted = demoteToLowConfidence(original);
51
- expect(demoted).toEqual({ provenance: 'imei', value: '490154203237518', confidence: 'low' });
52
- });
53
-
54
- test('demoting a none id is a no-op', () => {
55
- expect(demoteToLowConfidence(NO_EQUIPMENT_ID)).toBe(NO_EQUIPMENT_ID);
56
- });
57
- });
58
-
59
- describe('durable policy binding gate', () => {
60
- test('a high-confidence identity may bind policy and yields a key', () => {
61
- const identity = identityWith(imeiEquipmentId('490154203237518'));
62
- expect(canBindPolicy(identity)).toBe(true);
63
- expect(policyBindingKey(identity)).toEqual({ equipmentId: identity.equipmentId });
64
- });
65
-
66
- test('a low-confidence (zero IMEI) identity is refused', () => {
67
- const identity = identityWith(imeiEquipmentId('000000000000000'));
68
- expect(canBindPolicy(identity)).toBe(false);
69
- expect(() => policyBindingKey(identity)).toThrow(PolicyBindingRefusedError);
70
- });
71
-
72
- test('a duplicate-demoted identity is refused', () => {
73
- const identity = identityWith(demoteToLowConfidence(imeiEquipmentId('490154203237518')));
74
- expect(canBindPolicy(identity)).toBe(false);
75
- expect(() => policyBindingKey(identity)).toThrow(PolicyBindingRefusedError);
76
- });
77
-
78
- test('a none-provenance identity is refused (never binds durable policy)', () => {
79
- const identity = identityWith(NO_EQUIPMENT_ID);
80
- expect(canBindPolicy(identity)).toBe(false);
81
- expect(() => policyBindingKey(identity)).toThrow(PolicyBindingRefusedError);
82
- });
83
- });
@@ -1,165 +0,0 @@
1
- // Modem identity — the four ids that pin a physical modem across its lifecycle.
2
- //
3
- // Design (draft §Oracle round-1): a modem is identified by FOUR distinct ids, not
4
- // one, because each answers a different question and each has a different lifetime:
5
- //
6
- // - logicalSlotId — the stable physical-slot handle. Durable policy and routing
7
- // bind to THIS. Optional: absent until the identity ladder
8
- // (A3.2) resolves a slot from udev/Physdev/sysfs.
9
- // - equipmentId — IMEI (or serial fallback) with provenance + confidence.
10
- // - subscriptionId — ICCID/EID. SENSITIVE (see marker below). Optional: no SIM.
11
- // - runtimePath — the live ModemManager D-Bus object path. NEVER PERSISTED.
12
-
13
- import type { Brand } from './brand';
14
- import { nonEmptyString } from './brand';
15
- import { PolicyBindingRefusedError } from './errors';
16
-
17
- /**
18
- * Stable physical-slot handle (e.g. `slot-usb-1-2`). The ONLY id durable policy
19
- * and routing are allowed to bind to — it survives equipment swaps in the slot.
20
- */
21
- export type LogicalSlotId = Brand<string, 'LogicalSlotId'>;
22
-
23
- /**
24
- * SIM subscription id (ICCID or EID).
25
- *
26
- * SENSITIVE — this value identifies a subscriber. It MUST be redacted in every
27
- * log, telemetry, and error surface (the redaction module lands in A2.2). Treat
28
- * it as PII: never print it raw, never include it in a policy binding key.
29
- */
30
- export type SubscriptionId = Brand<string, 'SubscriptionId'>;
31
-
32
- /**
33
- * The live ModemManager D-Bus object path (e.g. `/org/freedesktop/ModemManager1/Modem/3`).
34
- *
35
- * NEVER PERSISTED — this is a per-boot runtime handle. ModemManager reassigns it
36
- * across daemon restarts and device replug, so it is meaningless in durable
37
- * storage. Durable keys derive from `logicalSlotId`/`equipmentId` only; storage
38
- * code in later waves must never write this field. The type name and this note
39
- * make that intent unambiguous at the domain layer.
40
- */
41
- export type RuntimePath = Brand<string, 'RuntimePath'>;
42
-
43
- /** How the equipment id was obtained. Discriminated union over the source. */
44
- export type EquipmentProvenance = 'imei' | 'serial' | 'none';
45
-
46
- /**
47
- * Trust in the equipment id as a durable key.
48
- *
49
- * - high — a well-formed, unique IMEI.
50
- * - medium — a serial fallback, or an IMEI of non-standard shape.
51
- * - low — AMBIGUOUS: a zero/blank IMEI, or a value seen on more than one
52
- * modem (duplicate). A low-confidence identity must NEVER bind
53
- * durable policy — see `policyBindingKey` / `canBindPolicy`.
54
- */
55
- export type IdentityConfidence = 'high' | 'medium' | 'low';
56
-
57
- /**
58
- * The equipment id, discriminated on `provenance`.
59
- *
60
- * `imei` and `serial` carry a `value`; `none` carries none — we genuinely have no
61
- * equipment identifier, so `value` would be a lie. `none` is always low confidence.
62
- */
63
- export type EquipmentId =
64
- | { readonly provenance: 'imei'; readonly value: string; readonly confidence: IdentityConfidence }
65
- | {
66
- readonly provenance: 'serial';
67
- readonly value: string;
68
- readonly confidence: IdentityConfidence;
69
- }
70
- | { readonly provenance: 'none'; readonly confidence: 'low' };
71
-
72
- /** The four-part modem identity. */
73
- export interface ModemIdentity {
74
- /** Absent until the identity ladder resolves a physical slot. */
75
- readonly logicalSlotId?: LogicalSlotId;
76
- readonly equipmentId: EquipmentId;
77
- /** Absent with no SIM. SENSITIVE — redact everywhere (A2.2 redaction module). */
78
- readonly subscriptionId?: SubscriptionId;
79
- /** NEVER PERSISTED — per-boot runtime handle only. */
80
- readonly runtimePath: RuntimePath;
81
- }
82
-
83
- // --- constructors ----------------------------------------------------------
84
-
85
- /** Construct a `LogicalSlotId` from a non-empty string. */
86
- export function logicalSlotId(value: string): LogicalSlotId {
87
- return nonEmptyString(value, 'logicalSlotId') as LogicalSlotId;
88
- }
89
-
90
- /** Construct a `SubscriptionId` from a non-empty string. */
91
- export function subscriptionId(value: string): SubscriptionId {
92
- return nonEmptyString(value, 'subscriptionId') as SubscriptionId;
93
- }
94
-
95
- /** Construct a `RuntimePath` from a non-empty string. */
96
- export function runtimePath(value: string): RuntimePath {
97
- return nonEmptyString(value, 'runtimePath') as RuntimePath;
98
- }
99
-
100
- const ALL_ZEROS = /^0+$/;
101
-
102
- /**
103
- * Grade an IMEI's confidence from its own shape alone.
104
- *
105
- * A blank or all-zeros IMEI is a well-known ambiguous placeholder → `low`. A
106
- * canonical 15/16-digit IMEI → `high`. Anything else → `medium`. DUPLICATE
107
- * detection is cross-modem and cannot be done from a single value here; A3.2
108
- * demotes duplicates to `low` via `demoteToLowConfidence`.
109
- */
110
- function gradeImei(value: string): IdentityConfidence {
111
- const trimmed = value.trim();
112
- if (trimmed.length === 0 || ALL_ZEROS.test(trimmed)) {
113
- return 'low';
114
- }
115
- return /^\d{15,16}$/.test(trimmed) ? 'high' : 'medium';
116
- }
117
-
118
- /** The absence of any equipment id — always low confidence. */
119
- export const NO_EQUIPMENT_ID: EquipmentId = { provenance: 'none', confidence: 'low' };
120
-
121
- /** Build an IMEI-provenance equipment id, grading confidence from the value. */
122
- export function imeiEquipmentId(value: string): EquipmentId {
123
- return { provenance: 'imei', value, confidence: gradeImei(value) };
124
- }
125
-
126
- /**
127
- * Build a serial-fallback equipment id. A serial is never as trustworthy as a
128
- * clean IMEI, so it caps at `medium` (blank → `low`).
129
- */
130
- export function serialEquipmentId(value: string): EquipmentId {
131
- const confidence: IdentityConfidence = value.trim().length === 0 ? 'low' : 'medium';
132
- return { provenance: 'serial', value, confidence };
133
- }
134
-
135
- /**
136
- * Demote an equipment id to `low` confidence — used by the identity ladder when a
137
- * value turns out to be shared across modems (duplicate IMEI). Idempotent; `none`
138
- * is already low.
139
- */
140
- export function demoteToLowConfidence(id: EquipmentId): EquipmentId {
141
- if (id.provenance === 'none') {
142
- return id;
143
- }
144
- return { provenance: id.provenance, value: id.value, confidence: 'low' };
145
- }
146
-
147
- // --- policy-binding gate ----------------------------------------------------
148
-
149
- /**
150
- * Whether this identity may bind DURABLE policy. False for ambiguous
151
- * (low-confidence) equipment identities — a duplicate or zero IMEI must never
152
- * become the durable key that a saved policy attaches to.
153
- */
154
- export function canBindPolicy(identity: ModemIdentity): boolean {
155
- return identity.equipmentId.confidence !== 'low';
156
- }
157
-
158
- /** Throw `PolicyBindingRefusedError` if the identity may not bind durable policy. */
159
- export function assertCanBindPolicy(identity: ModemIdentity): void {
160
- if (!canBindPolicy(identity)) {
161
- throw new PolicyBindingRefusedError(
162
- `equipment id (provenance=${identity.equipmentId.provenance}) is low-confidence`,
163
- );
164
- }
165
- }
@@ -1,12 +0,0 @@
1
- // Domain layer — pure types + reducers for modem identity and orthogonal state.
2
- //
3
- // No I/O, no D-Bus, no NetworkManager: this module is data and pure functions
4
- // only. Later waves (A2.2 ports, A3.x D-Bus backend) build on these exact shapes.
5
-
6
- export * from './brand';
7
- export * from './errors';
8
- export * from './guards';
9
- export * from './identity';
10
- export * from './policy';
11
- export * from './snapshot';
12
- export * from './state';