@ceralive/modem-control 1.0.0 → 1.2.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 (480) hide show
  1. package/README.md +341 -0
  2. package/dist/backend/at-lease.d.ts +59 -0
  3. package/dist/backend/at-lease.js +118 -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 +101 -0
  61. package/dist/backend/transition-preconditions.js +132 -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 +216 -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 +20 -0
  146. package/dist/index.js +27 -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 +61 -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/operation-ids.d.ts +2 -0
  196. package/dist/operation-ids.js +26 -0
  197. package/dist/operations/contracts.d.ts +72 -0
  198. package/dist/operations/contracts.js +1 -0
  199. package/dist/operations/index.d.ts +1 -0
  200. package/dist/operations/index.js +1 -0
  201. package/dist/operations/operation-engine.d.ts +16 -0
  202. package/dist/operations/operation-engine.js +194 -0
  203. package/dist/ports/index.d.ts +12 -0
  204. package/{src/ports/index.ts → dist/ports/index.js} +12 -8
  205. package/dist/ports/location.d.ts +87 -0
  206. package/dist/ports/location.js +36 -0
  207. package/dist/ports/modem-manager.d.ts +89 -0
  208. package/dist/ports/modem-manager.js +9 -0
  209. package/dist/ports/mutation-admission.d.ts +27 -0
  210. package/dist/ports/mutation-admission.js +9 -0
  211. package/dist/ports/network-manager.d.ts +68 -0
  212. package/dist/ports/network-manager.js +13 -0
  213. package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
  214. package/dist/ports/observation.js +7 -0
  215. package/dist/ports/ops.d.ts +44 -0
  216. package/dist/ports/ops.js +16 -0
  217. package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
  218. package/dist/ports/receipts.js +9 -0
  219. package/dist/ports/reconcile.d.ts +33 -0
  220. package/dist/ports/reconcile.js +200 -0
  221. package/dist/ports/resource-ownership.d.ts +29 -0
  222. package/dist/ports/resource-ownership.js +1 -0
  223. package/dist/ports/router.d.ts +19 -0
  224. package/dist/ports/router.js +7 -0
  225. package/dist/ports/sms.d.ts +64 -0
  226. package/dist/ports/sms.js +24 -0
  227. package/dist/ports/uhubctl.d.ts +6 -0
  228. package/dist/ports/uhubctl.js +1 -0
  229. package/dist/providers/contracts.d.ts +124 -0
  230. package/dist/providers/contracts.js +10 -0
  231. package/dist/providers/huawei-hilink/index.d.ts +2 -0
  232. package/dist/providers/huawei-hilink/index.js +2 -0
  233. package/dist/providers/huawei-hilink/operations.d.ts +20 -0
  234. package/dist/providers/huawei-hilink/operations.js +56 -0
  235. package/dist/providers/huawei-hilink/provider.d.ts +52 -0
  236. package/dist/providers/huawei-hilink/provider.js +76 -0
  237. package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
  238. package/dist/providers/huawei-hilink/runtime.js +171 -0
  239. package/dist/providers/huawei-hilink/session.d.ts +28 -0
  240. package/dist/providers/huawei-hilink/session.js +120 -0
  241. package/dist/providers/huawei-hilink/transport.d.ts +19 -0
  242. package/dist/providers/huawei-hilink/transport.js +1 -0
  243. package/dist/providers/index.d.ts +8 -0
  244. package/dist/providers/index.js +8 -0
  245. package/dist/providers/matcher.d.ts +3 -0
  246. package/dist/providers/matcher.js +205 -0
  247. package/dist/providers/modem-manager/errors.d.ts +7 -0
  248. package/dist/providers/modem-manager/errors.js +37 -0
  249. package/dist/providers/modem-manager/generic-operations.d.ts +10 -0
  250. package/dist/providers/modem-manager/generic-operations.js +209 -0
  251. package/dist/providers/modem-manager/index.d.ts +4 -0
  252. package/dist/providers/modem-manager/index.js +4 -0
  253. package/dist/providers/modem-manager/module-operations.d.ts +21 -0
  254. package/dist/providers/modem-manager/module-operations.js +118 -0
  255. package/dist/providers/modem-manager/provider.d.ts +41 -0
  256. package/dist/providers/modem-manager/provider.js +155 -0
  257. package/dist/providers/modem-manager/runtime-composition-operation.d.ts +32 -0
  258. package/dist/providers/modem-manager/runtime-composition-operation.js +151 -0
  259. package/dist/providers/modem-manager/snapshot.d.ts +5 -0
  260. package/dist/providers/modem-manager/snapshot.js +204 -0
  261. package/dist/providers/modem-manager/types.d.ts +137 -0
  262. package/dist/providers/modem-manager/types.js +1 -0
  263. package/dist/providers/network-manager/adapter.d.ts +71 -0
  264. package/dist/providers/network-manager/adapter.js +348 -0
  265. package/dist/providers/network-manager/index.d.ts +2 -0
  266. package/dist/providers/network-manager/index.js +2 -0
  267. package/dist/providers/network-manager/types.d.ts +171 -0
  268. package/dist/providers/network-manager/types.js +77 -0
  269. package/dist/providers/registry.d.ts +13 -0
  270. package/dist/providers/registry.js +33 -0
  271. package/dist/providers/ufi-himi/index.d.ts +6 -0
  272. package/dist/providers/ufi-himi/index.js +6 -0
  273. package/dist/providers/ufi-himi/operations.d.ts +41 -0
  274. package/dist/providers/ufi-himi/operations.js +66 -0
  275. package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
  276. package/dist/providers/ufi-himi/prohibitions.js +88 -0
  277. package/dist/providers/ufi-himi/provider.d.ts +41 -0
  278. package/dist/providers/ufi-himi/provider.js +204 -0
  279. package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
  280. package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
  281. package/dist/providers/ufi-himi/session.d.ts +46 -0
  282. package/dist/providers/ufi-himi/session.js +92 -0
  283. package/dist/providers/ufi-himi/transport.d.ts +29 -0
  284. package/dist/providers/ufi-himi/transport.js +25 -0
  285. package/dist/providers/zte-goform/index.d.ts +2 -0
  286. package/dist/providers/zte-goform/index.js +2 -0
  287. package/dist/providers/zte-goform/provider.d.ts +56 -0
  288. package/dist/providers/zte-goform/provider.js +101 -0
  289. package/dist/providers/zte-goform/session.d.ts +23 -0
  290. package/dist/providers/zte-goform/session.js +197 -0
  291. package/dist/providers/zte-goform/transport.d.ts +16 -0
  292. package/dist/providers/zte-goform/transport.js +1 -0
  293. package/dist/radio/band-truth.d.ts +50 -0
  294. package/dist/radio/band-truth.js +92 -0
  295. package/dist/radio/index.d.ts +3 -0
  296. package/dist/radio/index.js +10 -0
  297. package/dist/radio/mode-combinations.d.ts +88 -0
  298. package/dist/radio/mode-combinations.js +198 -0
  299. package/dist/radio/mode-truth.d.ts +67 -0
  300. package/dist/radio/mode-truth.js +112 -0
  301. package/dist/redact.d.ts +15 -0
  302. package/dist/redact.js +189 -0
  303. package/dist/safety/composition-root.d.ts +28 -0
  304. package/dist/safety/composition-root.js +65 -0
  305. package/dist/safety/flock-resource-ownership.d.ts +11 -0
  306. package/dist/safety/flock-resource-ownership.js +138 -0
  307. package/dist/safety/index.d.ts +2 -0
  308. package/dist/safety/index.js +2 -0
  309. package/dist/sms/dbus-messaging.d.ts +17 -0
  310. package/dist/sms/dbus-messaging.js +185 -0
  311. package/dist/sms/inbox-store.d.ts +10 -0
  312. package/dist/sms/inbox-store.js +82 -0
  313. package/dist/sms/index.d.ts +4 -0
  314. package/dist/sms/index.js +10 -0
  315. package/dist/sms/mmcli-parse.d.ts +54 -0
  316. package/dist/sms/mmcli-parse.js +224 -0
  317. package/dist/sms/normalize.d.ts +42 -0
  318. package/dist/sms/normalize.js +95 -0
  319. package/dist/testing/domain-fakes.d.ts +58 -0
  320. package/dist/testing/domain-fakes.js +98 -0
  321. package/dist/testing/index.d.ts +2 -0
  322. package/dist/testing/index.js +17 -0
  323. package/dist/testing/provider-fakes.d.ts +45 -0
  324. package/dist/testing/provider-fakes.js +64 -0
  325. package/dist/transport/calls.d.ts +9 -0
  326. package/dist/transport/calls.js +88 -0
  327. package/dist/transport/codec.d.ts +3 -0
  328. package/dist/transport/codec.js +207 -0
  329. package/dist/transport/dbus-native.d.ts +57 -0
  330. package/dist/transport/dbus-native.js +17 -0
  331. package/dist/transport/errors.d.ts +21 -0
  332. package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
  333. package/dist/transport/index.d.ts +4 -0
  334. package/dist/transport/index.js +9 -0
  335. package/dist/transport/signals.d.ts +15 -0
  336. package/dist/transport/signals.js +123 -0
  337. package/dist/transport/signature.d.ts +7 -0
  338. package/dist/transport/signature.js +94 -0
  339. package/dist/transport/transport.d.ts +2 -0
  340. package/dist/transport/transport.js +202 -0
  341. package/dist/transport/types.d.ts +61 -0
  342. package/dist/transport/types.js +19 -0
  343. package/dist/usb-mode/catalog-schema.d.ts +139 -0
  344. package/dist/usb-mode/catalog-schema.js +97 -0
  345. package/dist/usb-mode/catalog.d.ts +21 -0
  346. package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
  347. package/dist/usb-mode/certified-catalog.json +67 -0
  348. package/dist/usb-mode/index.d.ts +6 -0
  349. package/dist/usb-mode/index.js +16 -0
  350. package/dist/usb-mode/ingestion.d.ts +111 -0
  351. package/dist/usb-mode/ingestion.js +187 -0
  352. package/dist/usb-mode/promotion-review.d.ts +21 -0
  353. package/dist/usb-mode/promotion-review.js +87 -0
  354. package/dist/usb-mode/runtime-capability.d.ts +59 -0
  355. package/dist/usb-mode/runtime-capability.js +157 -0
  356. package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
  357. package/dist/usb-mode/usb-devices-parse.js +157 -0
  358. package/dist/ussd/calls.d.ts +32 -0
  359. package/dist/ussd/calls.js +96 -0
  360. package/dist/ussd/index.d.ts +5 -0
  361. package/dist/ussd/index.js +12 -0
  362. package/dist/ussd/mm-ussd.d.ts +37 -0
  363. package/dist/ussd/mm-ussd.js +205 -0
  364. package/dist/ussd/refusal.d.ts +53 -0
  365. package/dist/ussd/refusal.js +154 -0
  366. package/dist/ussd/registration.d.ts +20 -0
  367. package/dist/ussd/registration.js +101 -0
  368. package/dist/ussd/session.d.ts +106 -0
  369. package/dist/ussd/session.js +163 -0
  370. package/package.json +38 -4
  371. package/src/backend/at-lease.test.ts +0 -106
  372. package/src/backend/at-lease.ts +0 -158
  373. package/src/backend/cell-info.test.ts +0 -154
  374. package/src/backend/cell-info.ts +0 -160
  375. package/src/backend/device-classifier.test.ts +0 -168
  376. package/src/backend/device-classifier.ts +0 -248
  377. package/src/backend/enrichment.ts +0 -96
  378. package/src/backend/features.test.ts +0 -162
  379. package/src/backend/features.ts +0 -179
  380. package/src/backend/identity-ladder.test.ts +0 -117
  381. package/src/backend/identity-ladder.ts +0 -221
  382. package/src/backend/identity-registry.test.ts +0 -89
  383. package/src/backend/identity-registry.ts +0 -151
  384. package/src/backend/index.ts +0 -236
  385. package/src/backend/lifecycle-interlock.ts +0 -38
  386. package/src/backend/managed-objects.ts +0 -108
  387. package/src/backend/mapping.ts +0 -160
  388. package/src/backend/mm-backend.ts +0 -191
  389. package/src/backend/mm-mutations.ts +0 -228
  390. package/src/backend/modem-actor.test.ts +0 -95
  391. package/src/backend/modem-actor.ts +0 -112
  392. package/src/backend/nm-auto-apn.ts +0 -161
  393. package/src/backend/nm-gsm-fields.ts +0 -122
  394. package/src/backend/nmcli-nm-port.ts +0 -228
  395. package/src/backend/nmcli-runner.ts +0 -52
  396. package/src/backend/observer.ts +0 -297
  397. package/src/backend/power-contract.test.ts +0 -40
  398. package/src/backend/power-contract.ts +0 -83
  399. package/src/backend/recovery-attribution.test.ts +0 -102
  400. package/src/backend/recovery-attribution.ts +0 -86
  401. package/src/backend/recovery-budget.test.ts +0 -64
  402. package/src/backend/recovery-budget.ts +0 -84
  403. package/src/backend/recovery-ladder.test.ts +0 -257
  404. package/src/backend/recovery-ladder.ts +0 -249
  405. package/src/backend/router-ethernet.test.ts +0 -71
  406. package/src/backend/router-ethernet.ts +0 -90
  407. package/src/backend/row-store.ts +0 -105
  408. package/src/backend/signal-setup.ts +0 -112
  409. package/src/backend/sim-unlock.ts +0 -193
  410. package/src/backend/transition-preconditions.ts +0 -149
  411. package/src/backend/uhubctl-power-hook.test.ts +0 -274
  412. package/src/backend/uhubctl-power-hook.ts +0 -377
  413. package/src/backend/usage/accounting.test.ts +0 -147
  414. package/src/backend/usage/accounting.ts +0 -123
  415. package/src/backend/usage/billing-cycle.test.ts +0 -62
  416. package/src/backend/usage/billing-cycle.ts +0 -45
  417. package/src/backend/usage/index.ts +0 -60
  418. package/src/backend/usage/policy-store.test.ts +0 -164
  419. package/src/backend/usage/policy-store.ts +0 -216
  420. package/src/backend/usage/policy-write.test.ts +0 -198
  421. package/src/backend/usage/policy-write.ts +0 -207
  422. package/src/backend/usage/proc-net-dev.test.ts +0 -56
  423. package/src/backend/usage/sampler.test.ts +0 -327
  424. package/src/backend/usage/sampler.ts +0 -282
  425. package/src/backend/usage/store.test.ts +0 -148
  426. package/src/backend/usage/store.ts +0 -177
  427. package/src/backend/usb-enumerator.test.ts +0 -87
  428. package/src/backend/usb-enumerator.ts +0 -181
  429. package/src/backend/usb-mode-transition.test.ts +0 -323
  430. package/src/backend/usb-mode-transition.ts +0 -253
  431. package/src/domain/brand.ts +0 -29
  432. package/src/domain/errors.ts +0 -77
  433. package/src/domain/guards.test.ts +0 -218
  434. package/src/domain/guards.ts +0 -144
  435. package/src/domain/identity.test.ts +0 -83
  436. package/src/domain/identity.ts +0 -165
  437. package/src/domain/index.ts +0 -12
  438. package/src/domain/snapshot.test.ts +0 -266
  439. package/src/domain/snapshot.ts +0 -120
  440. package/src/index.test.ts +0 -6
  441. package/src/index.ts +0 -15
  442. package/src/ports/README.md +0 -61
  443. package/src/ports/forbidden-surface.test.ts +0 -241
  444. package/src/ports/modem-manager.ts +0 -72
  445. package/src/ports/network-manager.ts +0 -87
  446. package/src/ports/ops.ts +0 -60
  447. package/src/ports/ops.type-test.ts +0 -39
  448. package/src/ports/receipts.test.ts +0 -153
  449. package/src/ports/reconcile.test.ts +0 -152
  450. package/src/ports/reconcile.ts +0 -338
  451. package/src/ports/router.ts +0 -29
  452. package/src/redact.test.ts +0 -82
  453. package/src/redact.ts +0 -73
  454. package/src/transport/README.md +0 -65
  455. package/src/transport/calls.ts +0 -113
  456. package/src/transport/characterization.test.ts +0 -260
  457. package/src/transport/codec.test.ts +0 -118
  458. package/src/transport/codec.ts +0 -240
  459. package/src/transport/conformance-python.test.ts +0 -152
  460. package/src/transport/conformance-same-lib.test.ts +0 -115
  461. package/src/transport/dbus-native-lib.d.ts +0 -19
  462. package/src/transport/dbus-native.ts +0 -85
  463. package/src/transport/index.ts +0 -30
  464. package/src/transport/no-library-leak.test.ts +0 -61
  465. package/src/transport/reliability.test.ts +0 -173
  466. package/src/transport/signals.ts +0 -150
  467. package/src/transport/signature.ts +0 -110
  468. package/src/transport/test-support/fake-service.ts +0 -168
  469. package/src/transport/test-support/independent-producer.py +0 -110
  470. package/src/transport/test-support/private-bus.ts +0 -66
  471. package/src/transport/transport.ts +0 -250
  472. package/src/transport/types.ts +0 -118
  473. package/src/usb-mode/catalog-schema.test.ts +0 -181
  474. package/src/usb-mode/catalog-schema.ts +0 -113
  475. package/src/usb-mode/certified-catalog.json +0 -67
  476. package/src/usb-mode/index.ts +0 -58
  477. package/src/usb-mode/ingestion.test.ts +0 -268
  478. package/src/usb-mode/ingestion.ts +0 -297
  479. package/src/usb-mode/promotion-review.ts +0 -117
  480. package/src/usb-mode/usb-devices-parse.ts +0 -196
@@ -1,147 +0,0 @@
1
- // The adversarial accounting matrix — pure, deterministic proofs that per-slot usage
2
- // is attributed correctly across remaps, swaps, reuse, resets, pauses, and rollovers.
3
-
4
- import { describe, expect, test } from 'bun:test';
5
- import { type IdentityConfidence, logicalSlotId } from '../../domain';
6
- import { applySample, type BaselineKey, type SlotAccount } from './accounting';
7
-
8
- const SLOT_A = logicalSlotId('slot-a');
9
- const SLOT_B = logicalSlotId('slot-b');
10
- const SLOT_C = logicalSlotId('slot-c');
11
- const BOOT = 'boot-uuid-1';
12
-
13
- interface Step {
14
- readonly slot: string;
15
- readonly gen: number;
16
- readonly ifname: string;
17
- readonly current: number;
18
- readonly confidence?: IdentityConfidence;
19
- readonly cycleStartMs?: number;
20
- }
21
-
22
- function key(slot: string, gen: number, ifname: string): BaselineKey {
23
- return { logicalSlotId: slot, mappingGeneration: gen, ifname, bootId: BOOT };
24
- }
25
-
26
- function step(prior: SlotAccount | undefined, s: Step): SlotAccount {
27
- return applySample(prior, {
28
- key: key(s.slot, s.gen, s.ifname),
29
- current: s.current,
30
- confidence: s.confidence ?? 'high',
31
- cycleStartMs: s.cycleStartMs ?? 1000,
32
- });
33
- }
34
-
35
- describe('applySample — two modems tracked independently', () => {
36
- test('separate slots never cross-contaminate', () => {
37
- let a = step(undefined, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 100 });
38
- let b = step(undefined, { slot: SLOT_B, gen: 0, ifname: 'wwan1', current: 5000 });
39
- a = step(a, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 300 });
40
- b = step(b, { slot: SLOT_B, gen: 0, ifname: 'wwan1', current: 5001 });
41
- expect(a.cycleBytes).toBe(200);
42
- expect(b.cycleBytes).toBe(1);
43
- });
44
- });
45
-
46
- describe('applySample — A/B ifname swap follows the slot, not the interface', () => {
47
- test('each slot keeps its own total across a swap and re-baselines zero-delta', () => {
48
- // Prime: A on wwan0, B on wwan1.
49
- let a = step(undefined, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 100 });
50
- let b = step(undefined, { slot: SLOT_B, gen: 0, ifname: 'wwan1', current: 1000 });
51
- a = step(a, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 200 });
52
- b = step(b, { slot: SLOT_B, gen: 0, ifname: 'wwan1', current: 1100 });
53
- expect(a.cycleBytes).toBe(100);
54
- expect(b.cycleBytes).toBe(100);
55
-
56
- // Swap: A now maps to wwan1, B to wwan0 (mapping generation bumps for both).
57
- // First post-swap sample must be zero-delta despite the large counter change.
58
- a = step(a, { slot: SLOT_A, gen: 1, ifname: 'wwan1', current: 1100 });
59
- b = step(b, { slot: SLOT_B, gen: 1, ifname: 'wwan0', current: 200 });
60
- expect(a.cycleBytes).toBe(100);
61
- expect(b.cycleBytes).toBe(100);
62
-
63
- // Subsequent growth attributes to the correct slot on its new interface.
64
- a = step(a, { slot: SLOT_A, gen: 1, ifname: 'wwan1', current: 1150 });
65
- b = step(b, { slot: SLOT_B, gen: 1, ifname: 'wwan0', current: 260 });
66
- expect(a.cycleBytes).toBe(150);
67
- expect(b.cycleBytes).toBe(160);
68
- });
69
- });
70
-
71
- describe('applySample — old ifname reused by a different slot (no bleed)', () => {
72
- test('a new slot inheriting an old interface starts from zero', () => {
73
- let a = step(undefined, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 100 });
74
- a = step(a, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 500 });
75
- expect(a.cycleBytes).toBe(400);
76
-
77
- // Slot C now occupies wwan0. Its own account starts fresh (zero-delta baseline).
78
- let c = step(undefined, { slot: SLOT_C, gen: 0, ifname: 'wwan0', current: 500 });
79
- c = step(c, { slot: SLOT_C, gen: 0, ifname: 'wwan0', current: 700 });
80
- expect(c.cycleBytes).toBe(200);
81
- // A's total is untouched — no bleed either direction.
82
- expect(a.cycleBytes).toBe(400);
83
- });
84
- });
85
-
86
- describe('applySample — rename-first-sample is zero-delta', () => {
87
- test('the first sample after a remap never reports a spurious jump', () => {
88
- let s = step(undefined, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 100 });
89
- s = step(s, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 200 });
90
- expect(s.cycleBytes).toBe(100);
91
- // Rename wwan0 → wwan5 with a huge counter: must NOT attribute 9M.
92
- s = step(s, { slot: SLOT_A, gen: 1, ifname: 'wwan5', current: 9_000_000 });
93
- expect(s.cycleBytes).toBe(100);
94
- });
95
- });
96
-
97
- describe('applySample — reset-then-positive', () => {
98
- test('a counter reset clamps to zero, then normal increases resume', () => {
99
- let s = step(undefined, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 1000 });
100
- s = step(s, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 1500 });
101
- expect(s.cycleBytes).toBe(500);
102
- // Interface recreated: counter drops to 50 (decrease) → clamp + rebase.
103
- s = step(s, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 50 });
104
- expect(s.cycleBytes).toBe(500);
105
- // Growth from the rebased baseline is tracked again.
106
- s = step(s, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 250 });
107
- expect(s.cycleBytes).toBe(700);
108
- });
109
-
110
- test('a decrease never produces a negative delta', () => {
111
- let s = step(undefined, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 800 });
112
- s = step(s, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 300 });
113
- expect(s.cycleBytes).toBe(0);
114
- expect(s.cycleBytes).toBeGreaterThanOrEqual(0);
115
- });
116
- });
117
-
118
- describe('applySample — ambiguous identity pauses sampling', () => {
119
- test('low confidence attributes nothing and re-baselines zero-delta on resume', () => {
120
- let s = step(undefined, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 100 });
121
- s = step(s, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 300 });
122
- expect(s.cycleBytes).toBe(200);
123
- // Identity turns ambiguous → paused; the counter keeps climbing unattributed.
124
- s = step(s, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 9999, confidence: 'low' });
125
- expect(s.paused).toBe(true);
126
- expect(s.cycleBytes).toBe(200);
127
- // Confidence returns: resume is zero-delta, so the paused-window bytes are NOT
128
- // back-attributed; only growth after resume counts.
129
- s = step(s, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 9999 });
130
- expect(s.paused).toBe(false);
131
- expect(s.cycleBytes).toBe(200);
132
- s = step(s, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 10_099 });
133
- expect(s.cycleBytes).toBe(300);
134
- });
135
- });
136
-
137
- describe('applySample — cycle rollover resets the per-cycle total', () => {
138
- test('crossing into a newer cycle zeroes cycleBytes but keeps the baseline', () => {
139
- let s = step(undefined, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 100 });
140
- s = step(s, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 600 });
141
- expect(s.cycleBytes).toBe(500);
142
- // New cycle boundary — cycleBytes resets, counter continuity preserved.
143
- s = step(s, { slot: SLOT_A, gen: 0, ifname: 'wwan0', current: 650, cycleStartMs: 2000 });
144
- expect(s.cycleStartMs).toBe(2000);
145
- expect(s.cycleBytes).toBe(50);
146
- });
147
- });
@@ -1,123 +0,0 @@
1
- // The pure usage-accounting reducer — the heart of the sampler.
2
- //
3
- // For each logical slot we keep a per-cycle byte total plus a BASELINE: the last
4
- // observed cumulative counter value under a specific key. The key is the composite
5
- // `{logicalSlotId, mappingGeneration, ifname, bootId}`. The rules, all encoded here
6
- // with zero I/O so they can be exhaustively unit-tested:
7
- //
8
- // - REMAP (any key field changes — e.g. the ifname-to-slot mapping bumped
9
- // `mappingGeneration`, or a reboot changed `bootId`): rebaseline with a
10
- // ZERO-DELTA sample. We start counting fresh from the current cumulative value
11
- // and attribute nothing for that first sample — never a spurious jump.
12
- // - SAME-KEY DECREASE (the cumulative counter went DOWN — interface re-created /
13
- // counter reset without a reported remap): CLAMP the negative delta to zero and
14
- // REBASE the baseline to the new lower value. Never report negative usage.
15
- // - LOW CONFIDENCE (ambiguous identity, A3.2 ladder): sampling is PAUSED. We drop
16
- // the baseline and attribute nothing; on resume the next sample is zero-delta so
17
- // bytes moved during the ambiguous window are never mis-attributed.
18
- // - CYCLE ROLLOVER (`now` crossed into a new billing cycle): the per-cycle total
19
- // resets to zero. The baseline is kept — the kernel counter is continuous across
20
- // a billing boundary; only OUR accounting window resets.
21
-
22
- import type { IdentityConfidence, LogicalSlotId } from '../../domain';
23
-
24
- /** The composite baseline key. A change in ANY field is a remap (zero-delta rebase). */
25
- export interface BaselineKey {
26
- readonly logicalSlotId: string;
27
- readonly mappingGeneration: number;
28
- readonly ifname: string;
29
- readonly bootId: string;
30
- }
31
-
32
- /** Per-slot accounting state. `key`/`lastObserved` are unset before the first sample. */
33
- export interface SlotAccount {
34
- /** Bytes attributed to this slot within the current cycle. */
35
- readonly cycleBytes: number;
36
- /** UTC start of the cycle `cycleBytes` is accruing into. */
37
- readonly cycleStartMs: number;
38
- /** True when the last observation was low-confidence and sampling is paused. */
39
- readonly paused: boolean;
40
- /** The key the baseline was captured under (undefined = no baseline yet). */
41
- readonly key?: BaselineKey;
42
- /** The cumulative counter value at the last accepted sample. */
43
- readonly lastObserved?: number;
44
- }
45
-
46
- /** One observation fed to the reducer for a single slot in one sampling pass. */
47
- export interface SampleInput {
48
- readonly key: BaselineKey;
49
- /** The current cumulative rx+tx counter for the slot's interface. */
50
- readonly current: number;
51
- readonly confidence: IdentityConfidence;
52
- /** The UTC start of the slot's active cycle (from `cycleStart`). */
53
- readonly cycleStartMs: number;
54
- }
55
-
56
- /** A fresh account for a slot first seen in `cycleStartMs`. */
57
- export function initialAccount(cycleStartMs: number): SlotAccount {
58
- return { cycleBytes: 0, cycleStartMs, paused: false };
59
- }
60
-
61
- function sameKey(a: BaselineKey, b: BaselineKey): boolean {
62
- return (
63
- a.logicalSlotId === b.logicalSlotId &&
64
- a.mappingGeneration === b.mappingGeneration &&
65
- a.ifname === b.ifname &&
66
- a.bootId === b.bootId
67
- );
68
- }
69
-
70
- /** Apply a cycle rollover: if we crossed into a newer cycle, zero the per-cycle total. */
71
- function rollCycle(account: SlotAccount, cycleStartMs: number): SlotAccount {
72
- if (cycleStartMs > account.cycleStartMs) {
73
- return { ...account, cycleBytes: 0, cycleStartMs };
74
- }
75
- return account;
76
- }
77
-
78
- /**
79
- * Fold one observation into a slot's account, returning the NEXT account. Pure —
80
- * no clock, no I/O; the caller supplies `current`, `confidence` and `cycleStartMs`.
81
- */
82
- export function applySample(prior: SlotAccount | undefined, input: SampleInput): SlotAccount {
83
- const base = rollCycle(prior ?? initialAccount(input.cycleStartMs), input.cycleStartMs);
84
-
85
- // Ambiguous identity → pause: attribute nothing and drop the baseline so the
86
- // next confident sample re-baselines zero-delta (no back-attribution).
87
- if (input.confidence === 'low') {
88
- return { cycleBytes: base.cycleBytes, cycleStartMs: base.cycleStartMs, paused: true };
89
- }
90
-
91
- // Remap, first-ever sample, or resuming from a pause → zero-delta rebaseline.
92
- if (base.paused || base.key === undefined || base.lastObserved === undefined) {
93
- return { ...base, paused: false, key: input.key, lastObserved: input.current };
94
- }
95
- if (!sameKey(base.key, input.key)) {
96
- return { ...base, paused: false, key: input.key, lastObserved: input.current };
97
- }
98
-
99
- // Same key: a decrease is a counter reset → clamp the negative delta and rebase.
100
- if (input.current < base.lastObserved) {
101
- return { ...base, paused: false, key: input.key, lastObserved: input.current };
102
- }
103
-
104
- // Normal case: attribute the non-negative delta and advance the baseline.
105
- const delta = input.current - base.lastObserved;
106
- return {
107
- ...base,
108
- paused: false,
109
- key: input.key,
110
- lastObserved: input.current,
111
- cycleBytes: base.cycleBytes + delta,
112
- };
113
- }
114
-
115
- /** Convenience constructor for a `BaselineKey` from its parts. */
116
- export function baselineKey(
117
- logicalSlotId: LogicalSlotId,
118
- mappingGeneration: number,
119
- ifname: string,
120
- bootId: string,
121
- ): BaselineKey {
122
- return { logicalSlotId, mappingGeneration, ifname, bootId };
123
- }
@@ -1,62 +0,0 @@
1
- // UTC billing-cycle math — month-length clamping (cycleDay 31 → Feb 28/29) and the
2
- // before/after-boundary cycle-start selection.
3
-
4
- import { describe, expect, test } from 'bun:test';
5
- import { type EpochMillis, epochMillis } from '../../domain';
6
- import { clampCycleDay, cycleStart, daysInMonth } from './billing-cycle';
7
-
8
- const utc = (y: number, m: number, d: number, h = 0): EpochMillis =>
9
- epochMillis(Date.UTC(y, m, d, h));
10
-
11
- describe('daysInMonth', () => {
12
- test('February is 28 days in a common year, 29 in a leap year', () => {
13
- expect(daysInMonth(2023, 1)).toBe(28);
14
- expect(daysInMonth(2024, 1)).toBe(29); // 2024 is a leap year
15
- expect(daysInMonth(2100, 1)).toBe(28); // century non-leap
16
- expect(daysInMonth(2000, 1)).toBe(29); // 400-divisible leap
17
- });
18
-
19
- test('30- and 31-day months', () => {
20
- expect(daysInMonth(2024, 3)).toBe(30); // April
21
- expect(daysInMonth(2024, 0)).toBe(31); // January
22
- });
23
- });
24
-
25
- describe('clampCycleDay', () => {
26
- test('cycleDay 31 clamps to the last day of a short month', () => {
27
- expect(clampCycleDay(31, 2023, 1)).toBe(28); // Feb non-leap
28
- expect(clampCycleDay(31, 2024, 1)).toBe(29); // Feb leap
29
- expect(clampCycleDay(31, 2024, 3)).toBe(30); // April
30
- expect(clampCycleDay(31, 2024, 0)).toBe(31); // January — no clamp
31
- });
32
-
33
- test('a valid in-range day is unchanged; a floor of 1 is enforced', () => {
34
- expect(clampCycleDay(15, 2024, 5)).toBe(15);
35
- expect(clampCycleDay(0, 2024, 5)).toBe(1);
36
- });
37
- });
38
-
39
- describe('cycleStart — month-length clamp with before/after selection', () => {
40
- test('cycleDay 31 in February resolves to the clamped Feb boundary', () => {
41
- // 2023-02-15 with cycleDay 31 → cycle started 2023-01-31 (Feb boundary Feb 28
42
- // is still ahead of the 15th).
43
- expect(cycleStart(epochMillis(utc(2023, 1, 15)), 31)).toBe(utc(2023, 0, 31));
44
- // 2023-03-01 with cycleDay 31 → the active cycle began at clamped Feb 28.
45
- expect(cycleStart(epochMillis(utc(2023, 2, 1)), 31)).toBe(utc(2023, 1, 28));
46
- // Leap year: 2024-03-01 → Feb 29.
47
- expect(cycleStart(epochMillis(utc(2024, 2, 1)), 31)).toBe(utc(2024, 1, 29));
48
- });
49
-
50
- test('now exactly on the boundary starts a new cycle', () => {
51
- expect(cycleStart(epochMillis(utc(2024, 5, 10)), 10)).toBe(utc(2024, 5, 10));
52
- });
53
-
54
- test('now before this month boundary rolls back to the previous month', () => {
55
- // cycleDay 20, now is the 5th → cycle began on the 20th of the prior month.
56
- expect(cycleStart(epochMillis(utc(2024, 6, 5)), 20)).toBe(utc(2024, 5, 20));
57
- });
58
-
59
- test('January rollback wraps to December of the prior year', () => {
60
- expect(cycleStart(epochMillis(utc(2024, 0, 5)), 20)).toBe(utc(2023, 11, 20));
61
- });
62
- });
@@ -1,45 +0,0 @@
1
- // UTC billing-cycle computation with month-length clamping.
2
- //
3
- // A usage cycle resets on a configured day of the month (`cycleDay`, 1–31) in UTC.
4
- // Months are not all the same length, so a `cycleDay` past the end of a short month
5
- // must clamp to that month's LAST day rather than rolling into the next month or
6
- // throwing: `cycleDay: 31` resolves to Feb 28 (or Feb 29 in a leap year), Apr 30,
7
- // etc. All arithmetic is UTC — the device clock's local zone never shifts a cycle.
8
-
9
- import type { EpochMillis } from '../../domain';
10
-
11
- /** Days in a given UTC month. `month` is 0-based (0 = January). Handles leap Feb. */
12
- export function daysInMonth(year: number, month: number): number {
13
- // Day 0 of the next month is the last day of this month; UTC avoids DST drift.
14
- return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
15
- }
16
-
17
- /** Clamp a requested cycle day to the last valid day of the given month. */
18
- export function clampCycleDay(cycleDay: number, year: number, month: number): number {
19
- const floor = Math.max(1, Math.trunc(cycleDay));
20
- return Math.min(floor, daysInMonth(year, month));
21
- }
22
-
23
- /** The UTC epoch-ms of the cycle boundary in a specific month (clamped). */
24
- function boundaryFor(cycleDay: number, year: number, month: number): number {
25
- return Date.UTC(year, month, clampCycleDay(cycleDay, year, month));
26
- }
27
-
28
- /**
29
- * The UTC start (`EpochMillis`) of the cycle that `now` falls in for a given
30
- * `cycleDay`. If `now` is before this month's (clamped) boundary the active cycle
31
- * began on the previous month's boundary; otherwise it began on this month's.
32
- */
33
- export function cycleStart(now: EpochMillis, cycleDay: number): EpochMillis {
34
- const at = new Date(now);
35
- const year = at.getUTCFullYear();
36
- const month = at.getUTCMonth();
37
- const thisBoundary = boundaryFor(cycleDay, year, month);
38
- if (now >= thisBoundary) {
39
- return thisBoundary as EpochMillis;
40
- }
41
- // Roll back one month (December wraps to the prior year), re-clamping there.
42
- const prevMonth = month === 0 ? 11 : month - 1;
43
- const prevYear = month === 0 ? year - 1 : year;
44
- return boundaryFor(cycleDay, prevYear, prevMonth) as EpochMillis;
45
- }
@@ -1,60 +0,0 @@
1
- // Data-usage sampler — internal all-interface sampler over `/proc/net/dev`
2
- // CUMULATIVE counters (A4.3). Public surface consumed by the A6.1 bench CLI `usage`
3
- // command (via `UsageSnapshot`) and the composition root that wires the sampler.
4
-
5
- export {
6
- applySample,
7
- type BaselineKey,
8
- baselineKey,
9
- initialAccount,
10
- type SampleInput,
11
- type SlotAccount,
12
- } from './accounting';
13
- export { clampCycleDay, cycleStart, daysInMonth } from './billing-cycle';
14
- export { readBootId } from './boot-id';
15
- export {
16
- createUsagePolicyFileStore,
17
- isValidCycleDay,
18
- isValidThresholdBytes,
19
- type PersistedUsagePolicy,
20
- type PersistedUsagePolicySlot,
21
- selectUsagePolicy,
22
- USAGE_POLICY_SCHEMA_VERSION,
23
- type UsagePolicyFileStoreOptions,
24
- type UsagePolicyLogEvent,
25
- type UsagePolicyLogger,
26
- type UsagePolicyStore,
27
- } from './policy-store';
28
- export {
29
- getUsagePolicy,
30
- type SetUsagePolicyDeps,
31
- type SetUsagePolicyRejection,
32
- type SetUsagePolicyRequest,
33
- type SetUsagePolicyResult,
34
- setUsagePolicy,
35
- type UsagePolicyApplication,
36
- type UsagePolicyTarget,
37
- } from './policy-write';
38
- export {
39
- type CounterSource,
40
- parseProcNetDev,
41
- procNetDevCounterSource,
42
- } from './proc-net-dev';
43
- export {
44
- createUsageSampler,
45
- type SlotUsageSnapshot,
46
- type UsageObservation,
47
- UsageSampler,
48
- type UsageSamplerOptions,
49
- type UsageSnapshot,
50
- } from './sampler';
51
- export {
52
- createUsageFileStore,
53
- type PersistedSlot,
54
- type PersistedUsage,
55
- USAGE_SCHEMA_VERSION,
56
- type UsageFileStoreOptions,
57
- type UsageLogEvent,
58
- type UsageLogger,
59
- type UsageStore,
60
- } from './store';
@@ -1,164 +0,0 @@
1
- // Usage-policy persistence contract — mode 0600 (real fs.stat), fail-soft
2
- // corruption recovery with METADATA-ONLY logging, and the per-slot selector.
3
-
4
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
5
- import { mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises';
6
- import { tmpdir } from 'node:os';
7
- import { join } from 'node:path';
8
- import {
9
- createUsagePolicyFileStore,
10
- isValidCycleDay,
11
- isValidThresholdBytes,
12
- type PersistedUsagePolicy,
13
- selectUsagePolicy,
14
- USAGE_POLICY_SCHEMA_VERSION,
15
- type UsagePolicyLogEvent,
16
- } from './policy-store';
17
-
18
- let dir: string;
19
- let path: string;
20
-
21
- beforeEach(async () => {
22
- dir = await mkdtemp(join(tmpdir(), 'usage-policy-'));
23
- path = join(dir, 'policy.json');
24
- });
25
-
26
- afterEach(async () => {
27
- await rm(dir, { recursive: true, force: true });
28
- });
29
-
30
- const sample: PersistedUsagePolicy = {
31
- schemaVersion: USAGE_POLICY_SCHEMA_VERSION,
32
- savedAtMs: 1_700_000_000_000,
33
- slots: [
34
- { logicalSlotId: 'slot-a', cycleDay: 15, thresholdBytes: 5_000_000_000 },
35
- { logicalSlotId: 'slot-b', cycleDay: 1 },
36
- { logicalSlotId: 'slot-c', thresholdBytes: 0 },
37
- ],
38
- };
39
-
40
- describe('UsagePolicyStore — versioned round-trip', () => {
41
- test('save then load returns identical, schema-versioned state', async () => {
42
- const store = createUsagePolicyFileStore({ path });
43
- await store.save(sample);
44
- expect(await store.load(1)).toEqual(sample);
45
- });
46
-
47
- test('an absent file loads as a fresh empty document and writes nothing', async () => {
48
- const store = createUsagePolicyFileStore({ path });
49
- const loaded = await store.load(4242);
50
- expect(loaded).toEqual({
51
- schemaVersion: USAGE_POLICY_SCHEMA_VERSION,
52
- savedAtMs: 4242,
53
- slots: [],
54
- });
55
- expect(readFile(path, 'utf8')).rejects.toThrow();
56
- });
57
-
58
- test('the written file is mode 0600 regardless of umask', async () => {
59
- const store = createUsagePolicyFileStore({ path });
60
- await store.save(sample);
61
- expect((await stat(path)).mode & 0o777).toBe(0o600);
62
- });
63
- });
64
-
65
- describe('UsagePolicyStore — fail-soft corruption', () => {
66
- test('invalid JSON is replaced by a fresh 0600 file and logged as METADATA ONLY', async () => {
67
- const events: UsagePolicyLogEvent[] = [];
68
- await writeFile(path, '{"schemaVersion":1,"slots":');
69
- const store = createUsagePolicyFileStore({ path, logger: (e) => events.push(e) });
70
-
71
- const loaded = await store.load(7);
72
-
73
- expect(loaded.slots).toEqual([]);
74
- expect(events).toHaveLength(1);
75
- expect(events[0]?.kind).toBe('corrupt-policy');
76
- expect(events[0]?.reason).toContain('invalid-json');
77
- expect((await stat(path)).mode & 0o777).toBe(0o600);
78
- });
79
-
80
- test.each([
81
- [
82
- 'schemaVersion',
83
- JSON.stringify({ ...sample, schemaVersion: 99 }),
84
- 'schema-mismatch: schemaVersion',
85
- ],
86
- ['savedAtMs', JSON.stringify({ ...sample, savedAtMs: 'soon' }), 'schema-mismatch: savedAtMs'],
87
- ['slots', JSON.stringify({ ...sample, slots: {} }), 'schema-mismatch: slots'],
88
- [
89
- 'logicalSlotId',
90
- JSON.stringify({ ...sample, slots: [{ cycleDay: 3 }] }),
91
- 'schema-mismatch: logicalSlotId',
92
- ],
93
- [
94
- 'cycleDay',
95
- JSON.stringify({ ...sample, slots: [{ logicalSlotId: 'a', cycleDay: 32 }] }),
96
- 'schema-mismatch: cycleDay',
97
- ],
98
- [
99
- 'thresholdBytes',
100
- JSON.stringify({ ...sample, slots: [{ logicalSlotId: 'a', thresholdBytes: -1 }] }),
101
- 'schema-mismatch: thresholdBytes',
102
- ],
103
- ])('a bad %s is rejected by name and never throws', async (_field, text, reason) => {
104
- const events: UsagePolicyLogEvent[] = [];
105
- await writeFile(path, text);
106
- const store = createUsagePolicyFileStore({ path, logger: (e) => events.push(e) });
107
-
108
- expect((await store.load(9)).slots).toEqual([]);
109
- expect(events[0]?.reason).toBe(reason);
110
- });
111
-
112
- test('the corruption log carries a byte count and NEVER the file content', async () => {
113
- const secretish = JSON.stringify({ schemaVersion: 1, slots: 'iccid-8991101200003204514' });
114
- const events: UsagePolicyLogEvent[] = [];
115
- await writeFile(path, secretish);
116
- const store = createUsagePolicyFileStore({ path, logger: (e) => events.push(e) });
117
-
118
- await store.load(11);
119
-
120
- expect(events[0]?.bytes).toBe(Buffer.byteLength(secretish, 'utf8'));
121
- expect(JSON.stringify(events[0])).not.toContain('8991101200003204514');
122
- });
123
- });
124
-
125
- describe('selectUsagePolicy', () => {
126
- test('returns the slot policy, omitting fields the slot never set', () => {
127
- expect(selectUsagePolicy(sample, 'slot-a')).toEqual({
128
- cycleDay: 15,
129
- thresholdBytes: 5_000_000_000,
130
- });
131
- expect(selectUsagePolicy(sample, 'slot-b')).toEqual({ cycleDay: 1 });
132
- expect(selectUsagePolicy(sample, 'slot-c')).toEqual({ thresholdBytes: 0 });
133
- });
134
-
135
- test('an unknown slot answers "no policy set", not a default', () => {
136
- expect(selectUsagePolicy(sample, 'nope')).toEqual({});
137
- });
138
- });
139
-
140
- describe('validators', () => {
141
- test.each([
142
- [1, true],
143
- [31, true],
144
- [15, true],
145
- [0, false],
146
- [32, false],
147
- [1.5, false],
148
- [Number.NaN, false],
149
- ['3', false],
150
- ])('isValidCycleDay(%p) === %p', (value, expected) => {
151
- expect(isValidCycleDay(value)).toBe(expected);
152
- });
153
-
154
- test.each([
155
- [0, true],
156
- [5_000_000_000, true],
157
- [-1, false],
158
- [1.5, false],
159
- [Number.POSITIVE_INFINITY, false],
160
- ['5', false],
161
- ])('isValidThresholdBytes(%p) === %p', (value, expected) => {
162
- expect(isValidThresholdBytes(value)).toBe(expected);
163
- });
164
- });