@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.
- package/README.md +341 -0
- package/dist/backend/at-lease.d.ts +59 -0
- package/dist/backend/at-lease.js +118 -0
- package/dist/backend/cell-info.d.ts +46 -0
- package/dist/backend/cell-info.js +124 -0
- package/dist/backend/constants.d.ts +24 -0
- package/{src/backend/constants.ts → dist/backend/constants.js} +4 -9
- package/dist/backend/device-classifier.d.ts +46 -0
- package/dist/backend/device-classifier.js +258 -0
- package/dist/backend/enrichment.d.ts +28 -0
- package/dist/backend/enrichment.js +59 -0
- package/dist/backend/features.d.ts +61 -0
- package/dist/backend/features.js +109 -0
- package/dist/backend/identity-ladder.d.ts +57 -0
- package/dist/backend/identity-ladder.js +158 -0
- package/dist/backend/identity-registry.d.ts +51 -0
- package/dist/backend/identity-registry.js +101 -0
- package/dist/backend/index.d.ts +30 -0
- package/dist/backend/index.js +35 -0
- package/dist/backend/lifecycle-interlock.d.ts +25 -0
- package/dist/backend/lifecycle-interlock.js +18 -0
- package/dist/backend/managed-objects.d.ts +39 -0
- package/dist/backend/managed-objects.js +82 -0
- package/dist/backend/mapping.d.ts +11 -0
- package/dist/backend/mapping.js +148 -0
- package/dist/backend/mm-backend.d.ts +44 -0
- package/dist/backend/mm-backend.js +154 -0
- package/dist/backend/mm-location.d.ts +21 -0
- package/dist/backend/mm-location.js +237 -0
- package/dist/backend/mm-mutations.d.ts +42 -0
- package/dist/backend/mm-mutations.js +259 -0
- package/dist/backend/modem-actor.d.ts +41 -0
- package/dist/backend/modem-actor.js +78 -0
- package/dist/backend/nm-auto-apn.d.ts +54 -0
- package/dist/backend/nm-auto-apn.js +124 -0
- package/dist/backend/nm-gsm-fields.d.ts +23 -0
- package/dist/backend/nm-gsm-fields.js +107 -0
- package/dist/backend/nmcli-nm-port.d.ts +28 -0
- package/dist/backend/nmcli-nm-port.js +173 -0
- package/dist/backend/nmcli-runner.d.ts +24 -0
- package/dist/backend/nmcli-runner.js +35 -0
- package/dist/backend/observer.d.ts +37 -0
- package/dist/backend/observer.js +219 -0
- package/dist/backend/power-contract.d.ts +49 -0
- package/dist/backend/power-contract.js +34 -0
- package/dist/backend/recovery-attribution.d.ts +32 -0
- package/dist/backend/recovery-attribution.js +57 -0
- package/dist/backend/recovery-budget.d.ts +45 -0
- package/dist/backend/recovery-budget.js +44 -0
- package/dist/backend/recovery-ladder.d.ts +94 -0
- package/dist/backend/recovery-ladder.js +116 -0
- package/dist/backend/router-ethernet.d.ts +19 -0
- package/dist/backend/router-ethernet.js +66 -0
- package/dist/backend/row-store.d.ts +13 -0
- package/dist/backend/row-store.js +82 -0
- package/dist/backend/signal-setup.d.ts +30 -0
- package/dist/backend/signal-setup.js +92 -0
- package/dist/backend/sim-unlock.d.ts +13 -0
- package/dist/backend/sim-unlock.js +153 -0
- package/dist/backend/transition-preconditions.d.ts +101 -0
- package/dist/backend/transition-preconditions.js +132 -0
- package/dist/backend/usage/accounting.d.ts +39 -0
- package/dist/backend/usage/accounting.js +73 -0
- package/dist/backend/usage/billing-cycle.d.ts +11 -0
- package/dist/backend/usage/billing-cycle.js +39 -0
- package/dist/backend/usage/boot-id.d.ts +6 -0
- package/{src/backend/usage/boot-id.ts → dist/backend/usage/boot-id.js} +7 -7
- package/dist/backend/usage/index.d.ts +8 -0
- package/dist/backend/usage/index.js +11 -0
- package/dist/backend/usage/policy-store.d.ts +50 -0
- package/dist/backend/usage/policy-store.js +161 -0
- package/dist/backend/usage/policy-write.d.ts +65 -0
- package/dist/backend/usage/policy-write.js +112 -0
- package/dist/backend/usage/proc-net-dev.d.ts +18 -0
- package/{src/backend/usage/proc-net-dev.ts → dist/backend/usage/proc-net-dev.js} +39 -47
- package/dist/backend/usage/sampler.d.ts +75 -0
- package/dist/backend/usage/sampler.js +211 -0
- package/dist/backend/usage/store.d.ts +38 -0
- package/dist/backend/usage/store.js +126 -0
- package/dist/backend/usb-device-snapshot.d.ts +31 -0
- package/dist/backend/usb-device-snapshot.js +1 -0
- package/dist/backend/usb-enumerator.d.ts +21 -0
- package/dist/backend/usb-enumerator.js +153 -0
- package/dist/backend/usb-mode-transition.d.ts +29 -0
- package/dist/backend/usb-mode-transition.js +216 -0
- package/dist/band/band-names.d.ts +42 -0
- package/dist/band/band-names.js +150 -0
- package/dist/band/certification.d.ts +84 -0
- package/dist/band/certification.js +127 -0
- package/dist/band/certified-bands.json +4 -0
- package/dist/band/index.d.ts +2 -0
- package/dist/band/index.js +8 -0
- package/dist/capability/detect.d.ts +52 -0
- package/dist/capability/detect.js +86 -0
- package/dist/capability/five-g-preference.d.ts +104 -0
- package/dist/capability/five-g-preference.js +171 -0
- package/dist/capability/index.d.ts +3 -0
- package/dist/capability/index.js +10 -0
- package/dist/capability/support-claim.d.ts +39 -0
- package/dist/capability/support-claim.js +81 -0
- package/dist/domain/brand.d.ts +10 -0
- package/dist/domain/brand.js +21 -0
- package/dist/domain/errors.d.ts +32 -0
- package/dist/domain/errors.js +48 -0
- package/dist/domain/generation.d.ts +8 -0
- package/dist/domain/generation.js +12 -0
- package/dist/domain/guards.d.ts +6 -0
- package/dist/domain/guards.js +127 -0
- package/dist/domain/identity.d.ts +109 -0
- package/dist/domain/identity.js +86 -0
- package/dist/domain/index.d.ts +14 -0
- package/dist/domain/index.js +18 -0
- package/dist/domain/mm-enums.d.ts +12 -0
- package/dist/domain/mm-enums.js +139 -0
- package/dist/domain/modem-presentation.d.ts +10 -0
- package/dist/domain/modem-presentation.js +39 -0
- package/dist/domain/observation.d.ts +39 -0
- package/dist/domain/observation.js +4 -0
- package/dist/domain/operation.d.ts +118 -0
- package/dist/domain/operation.js +85 -0
- package/dist/domain/physical-identity.d.ts +43 -0
- package/dist/domain/physical-identity.js +113 -0
- package/{src/domain/policy.ts → dist/domain/policy.d.ts} +34 -69
- package/dist/domain/policy.js +38 -0
- package/dist/domain/shadow-divergence.d.ts +27 -0
- package/dist/domain/shadow-divergence.js +70 -0
- package/dist/domain/snapshot.d.ts +53 -0
- package/dist/domain/snapshot.js +75 -0
- package/{src/domain/state.ts → dist/domain/state.d.ts} +23 -122
- package/dist/domain/state.js +38 -0
- package/dist/fcc/coverage.d.ts +63 -0
- package/dist/fcc/coverage.js +102 -0
- package/dist/fcc/index.d.ts +3 -0
- package/dist/fcc/index.js +12 -0
- package/dist/fcc/policy-store.d.ts +40 -0
- package/dist/fcc/policy-store.js +146 -0
- package/dist/fcc/policy-write.d.ts +32 -0
- package/dist/fcc/policy-write.js +37 -0
- package/dist/hardware/hilink-protocol.d.ts +39 -0
- package/dist/hardware/hilink-protocol.js +58 -0
- package/dist/hardware/index.d.ts +3 -0
- package/dist/hardware/index.js +15 -0
- package/dist/hardware/router-parsers.d.ts +89 -0
- package/dist/hardware/router-parsers.js +234 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +27 -0
- package/dist/journal/codec.d.ts +44 -0
- package/dist/journal/codec.js +198 -0
- package/dist/journal/engine.d.ts +28 -0
- package/dist/journal/engine.js +68 -0
- package/dist/journal/entry.d.ts +74 -0
- package/dist/journal/entry.js +56 -0
- package/dist/journal/index.d.ts +6 -0
- package/dist/journal/index.js +6 -0
- package/dist/journal/legacy-ceraui.d.ts +73 -0
- package/dist/journal/legacy-ceraui.js +227 -0
- package/dist/journal/recovery.d.ts +58 -0
- package/dist/journal/recovery.js +117 -0
- package/dist/journal/store.d.ts +55 -0
- package/dist/journal/store.js +150 -0
- package/dist/location/fix-state.d.ts +53 -0
- package/dist/location/fix-state.js +75 -0
- package/dist/location/index.d.ts +2 -0
- package/dist/location/index.js +8 -0
- package/dist/location/nmea.d.ts +10 -0
- package/dist/location/nmea.js +89 -0
- package/dist/observations/envelope.d.ts +63 -0
- package/dist/observations/envelope.js +77 -0
- package/dist/observations/freshness.d.ts +28 -0
- package/dist/observations/freshness.js +76 -0
- package/dist/observations/index.d.ts +13 -0
- package/dist/observations/index.js +24 -0
- package/dist/observations/metric.d.ts +61 -0
- package/dist/observations/metric.js +73 -0
- package/dist/observations/model.d.ts +80 -0
- package/dist/observations/model.js +11 -0
- package/dist/observations/provenance.d.ts +94 -0
- package/dist/observations/provenance.js +67 -0
- package/dist/observations/raw.d.ts +42 -0
- package/dist/observations/raw.js +146 -0
- package/dist/observations/reading.d.ts +51 -0
- package/dist/observations/reading.js +65 -0
- package/dist/observations/sources/hilink.d.ts +10 -0
- package/dist/observations/sources/hilink.js +84 -0
- package/dist/observations/sources/modemmanager.d.ts +18 -0
- package/dist/observations/sources/modemmanager.js +239 -0
- package/dist/observations/sources/router-shared.d.ts +29 -0
- package/dist/observations/sources/router-shared.js +68 -0
- package/dist/observations/sources/ufi.d.ts +10 -0
- package/dist/observations/sources/ufi.js +111 -0
- package/dist/observations/sources/zte.d.ts +7 -0
- package/dist/observations/sources/zte.js +71 -0
- package/dist/observations/state-separation.d.ts +64 -0
- package/dist/observations/state-separation.js +52 -0
- package/dist/operation-ids.d.ts +2 -0
- package/dist/operation-ids.js +26 -0
- package/dist/operations/contracts.d.ts +72 -0
- package/dist/operations/contracts.js +1 -0
- package/dist/operations/index.d.ts +1 -0
- package/dist/operations/index.js +1 -0
- package/dist/operations/operation-engine.d.ts +16 -0
- package/dist/operations/operation-engine.js +194 -0
- package/dist/ports/index.d.ts +12 -0
- package/{src/ports/index.ts → dist/ports/index.js} +12 -8
- package/dist/ports/location.d.ts +87 -0
- package/dist/ports/location.js +36 -0
- package/dist/ports/modem-manager.d.ts +89 -0
- package/dist/ports/modem-manager.js +9 -0
- package/dist/ports/mutation-admission.d.ts +27 -0
- package/dist/ports/mutation-admission.js +9 -0
- package/dist/ports/network-manager.d.ts +68 -0
- package/dist/ports/network-manager.js +13 -0
- package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
- package/dist/ports/observation.js +7 -0
- package/dist/ports/ops.d.ts +44 -0
- package/dist/ports/ops.js +16 -0
- package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
- package/dist/ports/receipts.js +9 -0
- package/dist/ports/reconcile.d.ts +33 -0
- package/dist/ports/reconcile.js +200 -0
- package/dist/ports/resource-ownership.d.ts +29 -0
- package/dist/ports/resource-ownership.js +1 -0
- package/dist/ports/router.d.ts +19 -0
- package/dist/ports/router.js +7 -0
- package/dist/ports/sms.d.ts +64 -0
- package/dist/ports/sms.js +24 -0
- package/dist/ports/uhubctl.d.ts +6 -0
- package/dist/ports/uhubctl.js +1 -0
- package/dist/providers/contracts.d.ts +124 -0
- package/dist/providers/contracts.js +10 -0
- package/dist/providers/huawei-hilink/index.d.ts +2 -0
- package/dist/providers/huawei-hilink/index.js +2 -0
- package/dist/providers/huawei-hilink/operations.d.ts +20 -0
- package/dist/providers/huawei-hilink/operations.js +56 -0
- package/dist/providers/huawei-hilink/provider.d.ts +52 -0
- package/dist/providers/huawei-hilink/provider.js +76 -0
- package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
- package/dist/providers/huawei-hilink/runtime.js +171 -0
- package/dist/providers/huawei-hilink/session.d.ts +28 -0
- package/dist/providers/huawei-hilink/session.js +120 -0
- package/dist/providers/huawei-hilink/transport.d.ts +19 -0
- package/dist/providers/huawei-hilink/transport.js +1 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/matcher.d.ts +3 -0
- package/dist/providers/matcher.js +205 -0
- package/dist/providers/modem-manager/errors.d.ts +7 -0
- package/dist/providers/modem-manager/errors.js +37 -0
- package/dist/providers/modem-manager/generic-operations.d.ts +10 -0
- package/dist/providers/modem-manager/generic-operations.js +209 -0
- package/dist/providers/modem-manager/index.d.ts +4 -0
- package/dist/providers/modem-manager/index.js +4 -0
- package/dist/providers/modem-manager/module-operations.d.ts +21 -0
- package/dist/providers/modem-manager/module-operations.js +118 -0
- package/dist/providers/modem-manager/provider.d.ts +41 -0
- package/dist/providers/modem-manager/provider.js +155 -0
- package/dist/providers/modem-manager/runtime-composition-operation.d.ts +32 -0
- package/dist/providers/modem-manager/runtime-composition-operation.js +151 -0
- package/dist/providers/modem-manager/snapshot.d.ts +5 -0
- package/dist/providers/modem-manager/snapshot.js +204 -0
- package/dist/providers/modem-manager/types.d.ts +137 -0
- package/dist/providers/modem-manager/types.js +1 -0
- package/dist/providers/network-manager/adapter.d.ts +71 -0
- package/dist/providers/network-manager/adapter.js +348 -0
- package/dist/providers/network-manager/index.d.ts +2 -0
- package/dist/providers/network-manager/index.js +2 -0
- package/dist/providers/network-manager/types.d.ts +171 -0
- package/dist/providers/network-manager/types.js +77 -0
- package/dist/providers/registry.d.ts +13 -0
- package/dist/providers/registry.js +33 -0
- package/dist/providers/ufi-himi/index.d.ts +6 -0
- package/dist/providers/ufi-himi/index.js +6 -0
- package/dist/providers/ufi-himi/operations.d.ts +41 -0
- package/dist/providers/ufi-himi/operations.js +66 -0
- package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
- package/dist/providers/ufi-himi/prohibitions.js +88 -0
- package/dist/providers/ufi-himi/provider.d.ts +41 -0
- package/dist/providers/ufi-himi/provider.js +204 -0
- package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
- package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
- package/dist/providers/ufi-himi/session.d.ts +46 -0
- package/dist/providers/ufi-himi/session.js +92 -0
- package/dist/providers/ufi-himi/transport.d.ts +29 -0
- package/dist/providers/ufi-himi/transport.js +25 -0
- package/dist/providers/zte-goform/index.d.ts +2 -0
- package/dist/providers/zte-goform/index.js +2 -0
- package/dist/providers/zte-goform/provider.d.ts +56 -0
- package/dist/providers/zte-goform/provider.js +101 -0
- package/dist/providers/zte-goform/session.d.ts +23 -0
- package/dist/providers/zte-goform/session.js +197 -0
- package/dist/providers/zte-goform/transport.d.ts +16 -0
- package/dist/providers/zte-goform/transport.js +1 -0
- package/dist/radio/band-truth.d.ts +50 -0
- package/dist/radio/band-truth.js +92 -0
- package/dist/radio/index.d.ts +3 -0
- package/dist/radio/index.js +10 -0
- package/dist/radio/mode-combinations.d.ts +88 -0
- package/dist/radio/mode-combinations.js +198 -0
- package/dist/radio/mode-truth.d.ts +67 -0
- package/dist/radio/mode-truth.js +112 -0
- package/dist/redact.d.ts +15 -0
- package/dist/redact.js +189 -0
- package/dist/safety/composition-root.d.ts +28 -0
- package/dist/safety/composition-root.js +65 -0
- package/dist/safety/flock-resource-ownership.d.ts +11 -0
- package/dist/safety/flock-resource-ownership.js +138 -0
- package/dist/safety/index.d.ts +2 -0
- package/dist/safety/index.js +2 -0
- package/dist/sms/dbus-messaging.d.ts +17 -0
- package/dist/sms/dbus-messaging.js +185 -0
- package/dist/sms/inbox-store.d.ts +10 -0
- package/dist/sms/inbox-store.js +82 -0
- package/dist/sms/index.d.ts +4 -0
- package/dist/sms/index.js +10 -0
- package/dist/sms/mmcli-parse.d.ts +54 -0
- package/dist/sms/mmcli-parse.js +224 -0
- package/dist/sms/normalize.d.ts +42 -0
- package/dist/sms/normalize.js +95 -0
- package/dist/testing/domain-fakes.d.ts +58 -0
- package/dist/testing/domain-fakes.js +98 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +17 -0
- package/dist/testing/provider-fakes.d.ts +45 -0
- package/dist/testing/provider-fakes.js +64 -0
- package/dist/transport/calls.d.ts +9 -0
- package/dist/transport/calls.js +88 -0
- package/dist/transport/codec.d.ts +3 -0
- package/dist/transport/codec.js +207 -0
- package/dist/transport/dbus-native.d.ts +57 -0
- package/dist/transport/dbus-native.js +17 -0
- package/dist/transport/errors.d.ts +21 -0
- package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
- package/dist/transport/index.d.ts +4 -0
- package/dist/transport/index.js +9 -0
- package/dist/transport/signals.d.ts +15 -0
- package/dist/transport/signals.js +123 -0
- package/dist/transport/signature.d.ts +7 -0
- package/dist/transport/signature.js +94 -0
- package/dist/transport/transport.d.ts +2 -0
- package/dist/transport/transport.js +202 -0
- package/dist/transport/types.d.ts +61 -0
- package/dist/transport/types.js +19 -0
- package/dist/usb-mode/catalog-schema.d.ts +139 -0
- package/dist/usb-mode/catalog-schema.js +97 -0
- package/dist/usb-mode/catalog.d.ts +21 -0
- package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
- package/dist/usb-mode/certified-catalog.json +67 -0
- package/dist/usb-mode/index.d.ts +6 -0
- package/dist/usb-mode/index.js +16 -0
- package/dist/usb-mode/ingestion.d.ts +111 -0
- package/dist/usb-mode/ingestion.js +187 -0
- package/dist/usb-mode/promotion-review.d.ts +21 -0
- package/dist/usb-mode/promotion-review.js +87 -0
- package/dist/usb-mode/runtime-capability.d.ts +59 -0
- package/dist/usb-mode/runtime-capability.js +157 -0
- package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
- package/dist/usb-mode/usb-devices-parse.js +157 -0
- package/dist/ussd/calls.d.ts +32 -0
- package/dist/ussd/calls.js +96 -0
- package/dist/ussd/index.d.ts +5 -0
- package/dist/ussd/index.js +12 -0
- package/dist/ussd/mm-ussd.d.ts +37 -0
- package/dist/ussd/mm-ussd.js +205 -0
- package/dist/ussd/refusal.d.ts +53 -0
- package/dist/ussd/refusal.js +154 -0
- package/dist/ussd/registration.d.ts +20 -0
- package/dist/ussd/registration.js +101 -0
- package/dist/ussd/session.d.ts +106 -0
- package/dist/ussd/session.js +163 -0
- package/package.json +38 -4
- package/src/backend/at-lease.test.ts +0 -106
- package/src/backend/at-lease.ts +0 -158
- package/src/backend/cell-info.test.ts +0 -154
- package/src/backend/cell-info.ts +0 -160
- package/src/backend/device-classifier.test.ts +0 -168
- package/src/backend/device-classifier.ts +0 -248
- package/src/backend/enrichment.ts +0 -96
- package/src/backend/features.test.ts +0 -162
- package/src/backend/features.ts +0 -179
- package/src/backend/identity-ladder.test.ts +0 -117
- package/src/backend/identity-ladder.ts +0 -221
- package/src/backend/identity-registry.test.ts +0 -89
- package/src/backend/identity-registry.ts +0 -151
- package/src/backend/index.ts +0 -236
- package/src/backend/lifecycle-interlock.ts +0 -38
- package/src/backend/managed-objects.ts +0 -108
- package/src/backend/mapping.ts +0 -160
- package/src/backend/mm-backend.ts +0 -191
- package/src/backend/mm-mutations.ts +0 -228
- package/src/backend/modem-actor.test.ts +0 -95
- package/src/backend/modem-actor.ts +0 -112
- package/src/backend/nm-auto-apn.ts +0 -161
- package/src/backend/nm-gsm-fields.ts +0 -122
- package/src/backend/nmcli-nm-port.ts +0 -228
- package/src/backend/nmcli-runner.ts +0 -52
- package/src/backend/observer.ts +0 -297
- package/src/backend/power-contract.test.ts +0 -40
- package/src/backend/power-contract.ts +0 -83
- package/src/backend/recovery-attribution.test.ts +0 -102
- package/src/backend/recovery-attribution.ts +0 -86
- package/src/backend/recovery-budget.test.ts +0 -64
- package/src/backend/recovery-budget.ts +0 -84
- package/src/backend/recovery-ladder.test.ts +0 -257
- package/src/backend/recovery-ladder.ts +0 -249
- package/src/backend/router-ethernet.test.ts +0 -71
- package/src/backend/router-ethernet.ts +0 -90
- package/src/backend/row-store.ts +0 -105
- package/src/backend/signal-setup.ts +0 -112
- package/src/backend/sim-unlock.ts +0 -193
- package/src/backend/transition-preconditions.ts +0 -149
- package/src/backend/uhubctl-power-hook.test.ts +0 -274
- package/src/backend/uhubctl-power-hook.ts +0 -377
- package/src/backend/usage/accounting.test.ts +0 -147
- package/src/backend/usage/accounting.ts +0 -123
- package/src/backend/usage/billing-cycle.test.ts +0 -62
- package/src/backend/usage/billing-cycle.ts +0 -45
- package/src/backend/usage/index.ts +0 -60
- package/src/backend/usage/policy-store.test.ts +0 -164
- package/src/backend/usage/policy-store.ts +0 -216
- package/src/backend/usage/policy-write.test.ts +0 -198
- package/src/backend/usage/policy-write.ts +0 -207
- package/src/backend/usage/proc-net-dev.test.ts +0 -56
- package/src/backend/usage/sampler.test.ts +0 -327
- package/src/backend/usage/sampler.ts +0 -282
- package/src/backend/usage/store.test.ts +0 -148
- package/src/backend/usage/store.ts +0 -177
- package/src/backend/usb-enumerator.test.ts +0 -87
- package/src/backend/usb-enumerator.ts +0 -181
- package/src/backend/usb-mode-transition.test.ts +0 -323
- package/src/backend/usb-mode-transition.ts +0 -253
- package/src/domain/brand.ts +0 -29
- package/src/domain/errors.ts +0 -77
- package/src/domain/guards.test.ts +0 -218
- package/src/domain/guards.ts +0 -144
- package/src/domain/identity.test.ts +0 -83
- package/src/domain/identity.ts +0 -165
- package/src/domain/index.ts +0 -12
- package/src/domain/snapshot.test.ts +0 -266
- package/src/domain/snapshot.ts +0 -120
- package/src/index.test.ts +0 -6
- package/src/index.ts +0 -15
- package/src/ports/README.md +0 -61
- package/src/ports/forbidden-surface.test.ts +0 -241
- package/src/ports/modem-manager.ts +0 -72
- package/src/ports/network-manager.ts +0 -87
- package/src/ports/ops.ts +0 -60
- package/src/ports/ops.type-test.ts +0 -39
- package/src/ports/receipts.test.ts +0 -153
- package/src/ports/reconcile.test.ts +0 -152
- package/src/ports/reconcile.ts +0 -338
- package/src/ports/router.ts +0 -29
- package/src/redact.test.ts +0 -82
- package/src/redact.ts +0 -73
- package/src/transport/README.md +0 -65
- package/src/transport/calls.ts +0 -113
- package/src/transport/characterization.test.ts +0 -260
- package/src/transport/codec.test.ts +0 -118
- package/src/transport/codec.ts +0 -240
- package/src/transport/conformance-python.test.ts +0 -152
- package/src/transport/conformance-same-lib.test.ts +0 -115
- package/src/transport/dbus-native-lib.d.ts +0 -19
- package/src/transport/dbus-native.ts +0 -85
- package/src/transport/index.ts +0 -30
- package/src/transport/no-library-leak.test.ts +0 -61
- package/src/transport/reliability.test.ts +0 -173
- package/src/transport/signals.ts +0 -150
- package/src/transport/signature.ts +0 -110
- package/src/transport/test-support/fake-service.ts +0 -168
- package/src/transport/test-support/independent-producer.py +0 -110
- package/src/transport/test-support/private-bus.ts +0 -66
- package/src/transport/transport.ts +0 -250
- package/src/transport/types.ts +0 -118
- package/src/usb-mode/catalog-schema.test.ts +0 -181
- package/src/usb-mode/catalog-schema.ts +0 -113
- package/src/usb-mode/certified-catalog.json +0 -67
- package/src/usb-mode/index.ts +0 -58
- package/src/usb-mode/ingestion.test.ts +0 -268
- package/src/usb-mode/ingestion.ts +0 -297
- package/src/usb-mode/promotion-review.ts +0 -117
- package/src/usb-mode/usb-devices-parse.ts +0 -196
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Typed errors for the domain layer.
|
|
2
|
+
//
|
|
3
|
+
// The domain never fails silently: an impossible state combination, a refused
|
|
4
|
+
// policy binding, or a non-monotonic revision each raise a distinct, catchable
|
|
5
|
+
// error class carrying a machine-readable reason. QA and callers discriminate on
|
|
6
|
+
// the class and the `code`, never on a message string.
|
|
7
|
+
/** Base class for every domain-layer error. Callers can catch this to trap all. */
|
|
8
|
+
export class DomainError extends Error {
|
|
9
|
+
name = 'DomainError';
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
// Restore the prototype chain across the ES5 target transpile so
|
|
13
|
+
// `instanceof` works on subclasses (standard TS extends-Error guard).
|
|
14
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** A snapshot was constructed (or transitioned into) a physically impossible state. */
|
|
18
|
+
export class ImpossibleStateError extends DomainError {
|
|
19
|
+
name = 'ImpossibleStateError';
|
|
20
|
+
code;
|
|
21
|
+
constructor(code, detail) {
|
|
22
|
+
super(`impossible cellular state [${code}]: ${detail}`);
|
|
23
|
+
this.code = code;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A durable policy binding was attempted against an identity that is not allowed
|
|
28
|
+
* to carry one — today only low-confidence (ambiguous) equipment identities.
|
|
29
|
+
*/
|
|
30
|
+
export class PolicyBindingRefusedError extends DomainError {
|
|
31
|
+
name = 'PolicyBindingRefusedError';
|
|
32
|
+
reason;
|
|
33
|
+
constructor(detail) {
|
|
34
|
+
super(`durable policy binding refused: ${detail}`);
|
|
35
|
+
this.reason = 'ambiguous-identity';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** A snapshot transition tried to keep or lower the monotonic revision. */
|
|
39
|
+
export class RevisionMonotonicityError extends DomainError {
|
|
40
|
+
name = 'RevisionMonotonicityError';
|
|
41
|
+
previous;
|
|
42
|
+
next;
|
|
43
|
+
constructor(previous, next) {
|
|
44
|
+
super(`revision must strictly increase: ${previous} -> ${next}`);
|
|
45
|
+
this.previous = previous;
|
|
46
|
+
this.next = next;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Brand } from './brand.js';
|
|
2
|
+
/** Monotonic lifetime of one physical enumeration/provider selection. */
|
|
3
|
+
export type DeviceGeneration = Brand<number, 'DeviceGeneration'>;
|
|
4
|
+
export declare function deviceGeneration(value: number): DeviceGeneration;
|
|
5
|
+
/** Re-enumeration or provider replacement starts the next fenced lifetime. */
|
|
6
|
+
export declare function nextDeviceGeneration(current: DeviceGeneration): DeviceGeneration;
|
|
7
|
+
/** Whether an async completion still belongs to the current device lifetime. */
|
|
8
|
+
export declare function isCurrentGeneration(completion: DeviceGeneration, current: DeviceGeneration): boolean;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { nonNegativeInteger } from './brand.js';
|
|
2
|
+
export function deviceGeneration(value) {
|
|
3
|
+
return nonNegativeInteger(value, 'deviceGeneration');
|
|
4
|
+
}
|
|
5
|
+
/** Re-enumeration or provider replacement starts the next fenced lifetime. */
|
|
6
|
+
export function nextDeviceGeneration(current) {
|
|
7
|
+
return deviceGeneration(current + 1);
|
|
8
|
+
}
|
|
9
|
+
/** Whether an async completion still belongs to the current device lifetime. */
|
|
10
|
+
export function isCurrentGeneration(completion, current) {
|
|
11
|
+
return completion === current;
|
|
12
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ImpossibleStateCode } from './errors.js';
|
|
2
|
+
import type { CellularSnapshot } from './snapshot.js';
|
|
3
|
+
/** Return the first impossible-combination code the snapshot violates, or null. */
|
|
4
|
+
export declare function checkSnapshot(snapshot: CellularSnapshot): ImpossibleStateCode | null;
|
|
5
|
+
/** Throw `ImpossibleStateError` if the snapshot holds an impossible combination. */
|
|
6
|
+
export declare function assertSnapshot(snapshot: CellularSnapshot): void;
|
|
@@ -0,0 +1,127 @@
|
|
|
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
|
+
import { ImpossibleStateError } from './errors.js';
|
|
11
|
+
import { isRegistered, MM_STATES_REQUIRING_RADIO, SIM_LOCK_REQUIRES_CARD } from './state.js';
|
|
12
|
+
function checkRegistration(s) {
|
|
13
|
+
if (!isRegistered(s.registration.status)) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
if (s.presence === 'absent') {
|
|
17
|
+
return { code: 'registered-while-absent', detail: `status=${s.registration.status}` };
|
|
18
|
+
}
|
|
19
|
+
if (s.radioPower === 'off') {
|
|
20
|
+
return { code: 'registered-radio-off', detail: `status=${s.registration.status}` };
|
|
21
|
+
}
|
|
22
|
+
if (s.registration.activeRats.size === 0) {
|
|
23
|
+
return { code: 'registered-empty-rat-set', detail: `status=${s.registration.status}` };
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
function radioIsOnTheAir(s) {
|
|
28
|
+
return (MM_STATES_REQUIRING_RADIO.has(s.mmState) ||
|
|
29
|
+
s.registration.status === 'searching' ||
|
|
30
|
+
s.nmActivation === 'activating' ||
|
|
31
|
+
s.nmActivation === 'activated');
|
|
32
|
+
}
|
|
33
|
+
function checkRadioAndPresence(s) {
|
|
34
|
+
if (MM_STATES_REQUIRING_RADIO.has(s.mmState) && s.presence === 'absent') {
|
|
35
|
+
return { code: 'active-state-while-absent', detail: `mmState=${s.mmState}` };
|
|
36
|
+
}
|
|
37
|
+
if (s.radioPower === 'off' && radioIsOnTheAir(s)) {
|
|
38
|
+
return { code: 'radio-off-while-active', detail: `mmState=${s.mmState}` };
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
function checkNmActivation(s) {
|
|
43
|
+
if (s.nmActivation !== 'activated') {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
if (s.presence === 'absent') {
|
|
47
|
+
return { code: 'nm-activated-while-absent', detail: 'nmActivation=activated' };
|
|
48
|
+
}
|
|
49
|
+
if (!s.dataInterface.present) {
|
|
50
|
+
return { code: 'nm-activated-without-interface', detail: 'nmActivation=activated' };
|
|
51
|
+
}
|
|
52
|
+
if (s.mmState !== 'connected') {
|
|
53
|
+
return { code: 'nm-activated-without-mm-connected', detail: `mmState=${s.mmState}` };
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
function checkSimSlots(s) {
|
|
58
|
+
let activeCount = 0;
|
|
59
|
+
let hasLockedCard = false;
|
|
60
|
+
for (const slot of s.simSlots) {
|
|
61
|
+
if (slot.active) {
|
|
62
|
+
activeCount += 1;
|
|
63
|
+
}
|
|
64
|
+
if (!slot.occupied && SIM_LOCK_REQUIRES_CARD.has(slot.lock)) {
|
|
65
|
+
return { code: 'locked-sim-in-empty-slot', detail: `slot=${slot.index} lock=${slot.lock}` };
|
|
66
|
+
}
|
|
67
|
+
if (slot.occupied && SIM_LOCK_REQUIRES_CARD.has(slot.lock)) {
|
|
68
|
+
hasLockedCard = true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (activeCount > 1) {
|
|
72
|
+
return { code: 'multiple-active-sim-slots', detail: `active=${activeCount}` };
|
|
73
|
+
}
|
|
74
|
+
if (s.mmState === 'locked' && !hasLockedCard) {
|
|
75
|
+
return { code: 'mm-locked-without-sim-lock', detail: 'mmState=locked' };
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function checkDataInterface(s) {
|
|
80
|
+
if (!s.dataInterface.present && s.dataInterface.name !== undefined) {
|
|
81
|
+
return {
|
|
82
|
+
code: 'data-interface-name-without-presence',
|
|
83
|
+
detail: `name=${s.dataInterface.name}`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
function checkRecovery(s) {
|
|
89
|
+
const { stage, attempts, cooldownUntil } = s.recoveryState;
|
|
90
|
+
if (!Number.isSafeInteger(attempts) || attempts < 0) {
|
|
91
|
+
return { code: 'recovery-attempts-negative', detail: `attempts=${attempts}` };
|
|
92
|
+
}
|
|
93
|
+
if ((cooldownUntil !== undefined) !== (stage === 'cooldown')) {
|
|
94
|
+
return { code: 'recovery-cooldown-stage-mismatch', detail: `stage=${stage}` };
|
|
95
|
+
}
|
|
96
|
+
if (stage === 'idle' && attempts !== 0) {
|
|
97
|
+
return { code: 'recovery-idle-with-attempts', detail: `attempts=${attempts}` };
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const CHECKS = [
|
|
102
|
+
checkRegistration,
|
|
103
|
+
checkRadioAndPresence,
|
|
104
|
+
checkNmActivation,
|
|
105
|
+
checkSimSlots,
|
|
106
|
+
checkDataInterface,
|
|
107
|
+
checkRecovery,
|
|
108
|
+
];
|
|
109
|
+
/** Return the first impossible-combination code the snapshot violates, or null. */
|
|
110
|
+
export function checkSnapshot(snapshot) {
|
|
111
|
+
for (const check of CHECKS) {
|
|
112
|
+
const violation = check(snapshot);
|
|
113
|
+
if (violation !== null) {
|
|
114
|
+
return violation.code;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
/** Throw `ImpossibleStateError` if the snapshot holds an impossible combination. */
|
|
120
|
+
export function assertSnapshot(snapshot) {
|
|
121
|
+
for (const check of CHECKS) {
|
|
122
|
+
const violation = check(snapshot);
|
|
123
|
+
if (violation !== null) {
|
|
124
|
+
throw new ImpossibleStateError(violation.code, violation.detail);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { Brand } from './brand.js';
|
|
2
|
+
/**
|
|
3
|
+
* Stable physical-slot handle (e.g. `slot-usb-1-2`). The ONLY id durable policy
|
|
4
|
+
* and routing are allowed to bind to — it survives equipment swaps in the slot.
|
|
5
|
+
*/
|
|
6
|
+
export type LogicalSlotId = Brand<string, 'LogicalSlotId'>;
|
|
7
|
+
/**
|
|
8
|
+
* SIM subscription id (ICCID or EID).
|
|
9
|
+
*
|
|
10
|
+
* SENSITIVE — this value identifies a subscriber. It MUST be redacted in every
|
|
11
|
+
* log, telemetry, and error surface (the redaction module lands in A2.2). Treat
|
|
12
|
+
* it as PII: never print it raw, never include it in a policy binding key.
|
|
13
|
+
*/
|
|
14
|
+
export type SubscriptionId = Brand<string, 'SubscriptionId'>;
|
|
15
|
+
/**
|
|
16
|
+
* The SIM's OWN number (MSISDN), as ModemManager's `Modem.OwnNumbers` reports it.
|
|
17
|
+
*
|
|
18
|
+
* SENSITIVE — it is the subscriber's telephone number, so it belongs to the same
|
|
19
|
+
* class as `subscriptionId`: never printed raw, never a policy binding key. It is
|
|
20
|
+
* DISPLAYED to the operator on an explicit reveal; that is a rendering decision
|
|
21
|
+
* and does not make it loggable.
|
|
22
|
+
*/
|
|
23
|
+
export type SubscriberNumber = Brand<string, 'SubscriberNumber'>;
|
|
24
|
+
/**
|
|
25
|
+
* The live ModemManager D-Bus object path (e.g. `/org/freedesktop/ModemManager1/Modem/3`).
|
|
26
|
+
*
|
|
27
|
+
* NEVER PERSISTED — this is a per-boot runtime handle. ModemManager reassigns it
|
|
28
|
+
* across daemon restarts and device replug, so it is meaningless in durable
|
|
29
|
+
* storage. Durable keys derive from `logicalSlotId`/`equipmentId` only; storage
|
|
30
|
+
* code in later waves must never write this field. The type name and this note
|
|
31
|
+
* make that intent unambiguous at the domain layer.
|
|
32
|
+
*/
|
|
33
|
+
export type RuntimePath = Brand<string, 'RuntimePath'>;
|
|
34
|
+
/** How the equipment id was obtained. Discriminated union over the source. */
|
|
35
|
+
export type EquipmentProvenance = 'imei' | 'serial' | 'none';
|
|
36
|
+
/**
|
|
37
|
+
* Trust in the equipment id as a durable key.
|
|
38
|
+
*
|
|
39
|
+
* - high — a well-formed, unique IMEI.
|
|
40
|
+
* - medium — a serial fallback, or an IMEI of non-standard shape.
|
|
41
|
+
* - low — AMBIGUOUS: a zero/blank IMEI, or a value seen on more than one
|
|
42
|
+
* modem (duplicate). A low-confidence identity must NEVER bind
|
|
43
|
+
* durable policy — see `policyBindingKey` / `canBindPolicy`.
|
|
44
|
+
*/
|
|
45
|
+
export type IdentityConfidence = 'high' | 'medium' | 'low';
|
|
46
|
+
/**
|
|
47
|
+
* The equipment id, discriminated on `provenance`.
|
|
48
|
+
*
|
|
49
|
+
* `imei` and `serial` carry a `value`; `none` carries none — we genuinely have no
|
|
50
|
+
* equipment identifier, so `value` would be a lie. `none` is always low confidence.
|
|
51
|
+
*/
|
|
52
|
+
export type EquipmentId = {
|
|
53
|
+
readonly provenance: 'imei';
|
|
54
|
+
readonly value: string;
|
|
55
|
+
readonly confidence: IdentityConfidence;
|
|
56
|
+
} | {
|
|
57
|
+
readonly provenance: 'serial';
|
|
58
|
+
readonly value: string;
|
|
59
|
+
readonly confidence: IdentityConfidence;
|
|
60
|
+
} | {
|
|
61
|
+
readonly provenance: 'none';
|
|
62
|
+
readonly confidence: 'low';
|
|
63
|
+
};
|
|
64
|
+
/** The four-part modem identity. */
|
|
65
|
+
export interface ModemIdentity {
|
|
66
|
+
/** Absent until the identity ladder resolves a physical slot. */
|
|
67
|
+
readonly logicalSlotId?: LogicalSlotId;
|
|
68
|
+
readonly equipmentId: EquipmentId;
|
|
69
|
+
/** Absent with no SIM. SENSITIVE — redact everywhere (A2.2 redaction module). */
|
|
70
|
+
readonly subscriptionId?: SubscriptionId;
|
|
71
|
+
/**
|
|
72
|
+
* The SIM's own number(s). ABSENT when the carrier/SIM published none — most
|
|
73
|
+
* SIMs do not, so absence is the common case and never an error. SENSITIVE.
|
|
74
|
+
*/
|
|
75
|
+
readonly ownNumbers?: readonly SubscriberNumber[];
|
|
76
|
+
/** NEVER PERSISTED — per-boot runtime handle only. */
|
|
77
|
+
readonly runtimePath: RuntimePath;
|
|
78
|
+
}
|
|
79
|
+
/** Construct a `LogicalSlotId` from a non-empty string. */
|
|
80
|
+
export declare function logicalSlotId(value: string): LogicalSlotId;
|
|
81
|
+
/** Construct a `SubscriptionId` from a non-empty string. */
|
|
82
|
+
export declare function subscriptionId(value: string): SubscriptionId;
|
|
83
|
+
/** Construct a `SubscriberNumber` from a non-empty string. */
|
|
84
|
+
export declare function subscriberNumber(value: string): SubscriberNumber;
|
|
85
|
+
/** Construct a `RuntimePath` from a non-empty string. */
|
|
86
|
+
export declare function runtimePath(value: string): RuntimePath;
|
|
87
|
+
/** The absence of any equipment id — always low confidence. */
|
|
88
|
+
export declare const NO_EQUIPMENT_ID: EquipmentId;
|
|
89
|
+
/** Build an IMEI-provenance equipment id, grading confidence from the value. */
|
|
90
|
+
export declare function imeiEquipmentId(value: string): EquipmentId;
|
|
91
|
+
/**
|
|
92
|
+
* Build a serial-fallback equipment id. A serial is never as trustworthy as a
|
|
93
|
+
* clean IMEI, so it caps at `medium` (blank → `low`).
|
|
94
|
+
*/
|
|
95
|
+
export declare function serialEquipmentId(value: string): EquipmentId;
|
|
96
|
+
/**
|
|
97
|
+
* Demote an equipment id to `low` confidence — used by the identity ladder when a
|
|
98
|
+
* value turns out to be shared across modems (duplicate IMEI). Idempotent; `none`
|
|
99
|
+
* is already low.
|
|
100
|
+
*/
|
|
101
|
+
export declare function demoteToLowConfidence(id: EquipmentId): EquipmentId;
|
|
102
|
+
/**
|
|
103
|
+
* Whether this identity may bind DURABLE policy. False for ambiguous
|
|
104
|
+
* (low-confidence) equipment identities — a duplicate or zero IMEI must never
|
|
105
|
+
* become the durable key that a saved policy attaches to.
|
|
106
|
+
*/
|
|
107
|
+
export declare function canBindPolicy(identity: ModemIdentity): boolean;
|
|
108
|
+
/** Throw `PolicyBindingRefusedError` if the identity may not bind durable policy. */
|
|
109
|
+
export declare function assertCanBindPolicy(identity: ModemIdentity): void;
|
|
@@ -0,0 +1,86 @@
|
|
|
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
|
+
import { nonEmptyString } from './brand.js';
|
|
13
|
+
import { PolicyBindingRefusedError } from './errors.js';
|
|
14
|
+
// --- constructors ----------------------------------------------------------
|
|
15
|
+
/** Construct a `LogicalSlotId` from a non-empty string. */
|
|
16
|
+
export function logicalSlotId(value) {
|
|
17
|
+
return nonEmptyString(value, 'logicalSlotId');
|
|
18
|
+
}
|
|
19
|
+
/** Construct a `SubscriptionId` from a non-empty string. */
|
|
20
|
+
export function subscriptionId(value) {
|
|
21
|
+
return nonEmptyString(value, 'subscriptionId');
|
|
22
|
+
}
|
|
23
|
+
/** Construct a `SubscriberNumber` from a non-empty string. */
|
|
24
|
+
export function subscriberNumber(value) {
|
|
25
|
+
return nonEmptyString(value, 'subscriberNumber');
|
|
26
|
+
}
|
|
27
|
+
/** Construct a `RuntimePath` from a non-empty string. */
|
|
28
|
+
export function runtimePath(value) {
|
|
29
|
+
return nonEmptyString(value, 'runtimePath');
|
|
30
|
+
}
|
|
31
|
+
const ALL_ZEROS = /^0+$/;
|
|
32
|
+
/**
|
|
33
|
+
* Grade an IMEI's confidence from its own shape alone.
|
|
34
|
+
*
|
|
35
|
+
* A blank or all-zeros IMEI is a well-known ambiguous placeholder → `low`. A
|
|
36
|
+
* canonical 15/16-digit IMEI → `high`. Anything else → `medium`. DUPLICATE
|
|
37
|
+
* detection is cross-modem and cannot be done from a single value here; A3.2
|
|
38
|
+
* demotes duplicates to `low` via `demoteToLowConfidence`.
|
|
39
|
+
*/
|
|
40
|
+
function gradeImei(value) {
|
|
41
|
+
const trimmed = value.trim();
|
|
42
|
+
if (trimmed.length === 0 || ALL_ZEROS.test(trimmed)) {
|
|
43
|
+
return 'low';
|
|
44
|
+
}
|
|
45
|
+
return /^\d{15,16}$/.test(trimmed) ? 'high' : 'medium';
|
|
46
|
+
}
|
|
47
|
+
/** The absence of any equipment id — always low confidence. */
|
|
48
|
+
export const NO_EQUIPMENT_ID = { provenance: 'none', confidence: 'low' };
|
|
49
|
+
/** Build an IMEI-provenance equipment id, grading confidence from the value. */
|
|
50
|
+
export function imeiEquipmentId(value) {
|
|
51
|
+
return { provenance: 'imei', value, confidence: gradeImei(value) };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build a serial-fallback equipment id. A serial is never as trustworthy as a
|
|
55
|
+
* clean IMEI, so it caps at `medium` (blank → `low`).
|
|
56
|
+
*/
|
|
57
|
+
export function serialEquipmentId(value) {
|
|
58
|
+
const confidence = value.trim().length === 0 ? 'low' : 'medium';
|
|
59
|
+
return { provenance: 'serial', value, confidence };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Demote an equipment id to `low` confidence — used by the identity ladder when a
|
|
63
|
+
* value turns out to be shared across modems (duplicate IMEI). Idempotent; `none`
|
|
64
|
+
* is already low.
|
|
65
|
+
*/
|
|
66
|
+
export function demoteToLowConfidence(id) {
|
|
67
|
+
if (id.provenance === 'none') {
|
|
68
|
+
return id;
|
|
69
|
+
}
|
|
70
|
+
return { provenance: id.provenance, value: id.value, confidence: 'low' };
|
|
71
|
+
}
|
|
72
|
+
// --- policy-binding gate ----------------------------------------------------
|
|
73
|
+
/**
|
|
74
|
+
* Whether this identity may bind DURABLE policy. False for ambiguous
|
|
75
|
+
* (low-confidence) equipment identities — a duplicate or zero IMEI must never
|
|
76
|
+
* become the durable key that a saved policy attaches to.
|
|
77
|
+
*/
|
|
78
|
+
export function canBindPolicy(identity) {
|
|
79
|
+
return identity.equipmentId.confidence !== 'low';
|
|
80
|
+
}
|
|
81
|
+
/** Throw `PolicyBindingRefusedError` if the identity may not bind durable policy. */
|
|
82
|
+
export function assertCanBindPolicy(identity) {
|
|
83
|
+
if (!canBindPolicy(identity)) {
|
|
84
|
+
throw new PolicyBindingRefusedError(`equipment id (provenance=${identity.equipmentId.provenance}) is low-confidence`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from './brand.js';
|
|
2
|
+
export * from './errors.js';
|
|
3
|
+
export * from './generation.js';
|
|
4
|
+
export * from './guards.js';
|
|
5
|
+
export * from './identity.js';
|
|
6
|
+
export * from './mm-enums.js';
|
|
7
|
+
export * from './modem-presentation.js';
|
|
8
|
+
export * from './observation.js';
|
|
9
|
+
export * from './operation.js';
|
|
10
|
+
export * from './physical-identity.js';
|
|
11
|
+
export * from './policy.js';
|
|
12
|
+
export * from './shadow-divergence.js';
|
|
13
|
+
export * from './snapshot.js';
|
|
14
|
+
export * from './state.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
export * from './brand.js';
|
|
6
|
+
export * from './errors.js';
|
|
7
|
+
export * from './generation.js';
|
|
8
|
+
export * from './guards.js';
|
|
9
|
+
export * from './identity.js';
|
|
10
|
+
export * from './mm-enums.js';
|
|
11
|
+
export * from './modem-presentation.js';
|
|
12
|
+
export * from './observation.js';
|
|
13
|
+
export * from './operation.js';
|
|
14
|
+
export * from './physical-identity.js';
|
|
15
|
+
export * from './policy.js';
|
|
16
|
+
export * from './shadow-divergence.js';
|
|
17
|
+
export * from './snapshot.js';
|
|
18
|
+
export * from './state.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RadioAccessTechnology } from './state.js';
|
|
2
|
+
export declare function decodeMmAccessTechnologies(mask: number | undefined): Set<RadioAccessTechnology>;
|
|
3
|
+
export declare function modeMaskToLabel(mask: number | undefined): string | undefined;
|
|
4
|
+
export declare function decodeMmState(state: number | undefined): string;
|
|
5
|
+
export declare function decodeRegistrationState(state: number | undefined): string;
|
|
6
|
+
export declare function decodeStateFailedReason(value: number | undefined): string | undefined;
|
|
7
|
+
export declare function decodeUnlockRequired(lock: number | undefined): string | undefined;
|
|
8
|
+
export declare function decodeSimType(value: number | undefined): 'physical' | 'esim' | undefined;
|
|
9
|
+
export declare function decodeEsimStatus(value: number | undefined): 'no-profiles' | 'with-profiles' | undefined;
|
|
10
|
+
export declare function decodePacketServiceState(value: number | undefined): 'detached' | 'attached' | undefined;
|
|
11
|
+
export declare function decodeNetworkRejectionError(value: number | undefined): string | undefined;
|
|
12
|
+
export declare function runtimeIdFromPath(path: string): number | undefined;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
const ACCESS_TECH_BITS = [
|
|
2
|
+
[1 << 1, 'gsm'],
|
|
3
|
+
[1 << 2, 'gsm'],
|
|
4
|
+
[1 << 3, 'gsm'],
|
|
5
|
+
[1 << 4, 'gsm'],
|
|
6
|
+
[1 << 5, 'umts'],
|
|
7
|
+
[1 << 6, 'umts'],
|
|
8
|
+
[1 << 7, 'umts'],
|
|
9
|
+
[1 << 8, 'umts'],
|
|
10
|
+
[1 << 9, 'umts'],
|
|
11
|
+
[1 << 14, 'lte'],
|
|
12
|
+
[1 << 15, '5gnr'],
|
|
13
|
+
[1 << 16, 'lte'],
|
|
14
|
+
[1 << 17, 'lte'],
|
|
15
|
+
];
|
|
16
|
+
export function decodeMmAccessTechnologies(mask) {
|
|
17
|
+
const result = new Set();
|
|
18
|
+
if (mask === undefined || mask <= 0)
|
|
19
|
+
return result;
|
|
20
|
+
for (const [bit, rat] of ACCESS_TECH_BITS)
|
|
21
|
+
if ((mask & bit) !== 0)
|
|
22
|
+
result.add(rat);
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
export function modeMaskToLabel(mask) {
|
|
26
|
+
if (mask === undefined || mask <= 0)
|
|
27
|
+
return undefined;
|
|
28
|
+
const tokens = [
|
|
29
|
+
[1 << 1, '2g'],
|
|
30
|
+
[1 << 2, '3g'],
|
|
31
|
+
[1 << 3, '4g'],
|
|
32
|
+
[1 << 4, '5g'],
|
|
33
|
+
];
|
|
34
|
+
const named = tokens.filter(([bit]) => (mask & bit) !== 0).map(([, token]) => token);
|
|
35
|
+
return named.length === 0 ? undefined : named.reverse().join('');
|
|
36
|
+
}
|
|
37
|
+
const MM_STATES = new Map([
|
|
38
|
+
[-1, 'failed'],
|
|
39
|
+
[1, 'initializing'],
|
|
40
|
+
[2, 'locked'],
|
|
41
|
+
[3, 'disabled'],
|
|
42
|
+
[4, 'disabling'],
|
|
43
|
+
[5, 'enabling'],
|
|
44
|
+
[6, 'enabled'],
|
|
45
|
+
[7, 'searching'],
|
|
46
|
+
[8, 'registered'],
|
|
47
|
+
[9, 'disconnecting'],
|
|
48
|
+
[10, 'connecting'],
|
|
49
|
+
[11, 'connected'],
|
|
50
|
+
]);
|
|
51
|
+
export function decodeMmState(state) {
|
|
52
|
+
return state === undefined ? 'unknown' : (MM_STATES.get(state) ?? 'unknown');
|
|
53
|
+
}
|
|
54
|
+
export function decodeRegistrationState(state) {
|
|
55
|
+
if (state === 0)
|
|
56
|
+
return 'idle';
|
|
57
|
+
if (state === 1 || state === 6 || state === 7)
|
|
58
|
+
return 'home';
|
|
59
|
+
if (state === 2)
|
|
60
|
+
return 'searching';
|
|
61
|
+
if (state === 3)
|
|
62
|
+
return 'denied';
|
|
63
|
+
if (state === 5 || state === 8 || state === 9)
|
|
64
|
+
return 'roaming';
|
|
65
|
+
return state === 11 ? 'emergency-only' : 'unknown';
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* `MMModemStateFailedReason` → its mmcli spelling.
|
|
69
|
+
*
|
|
70
|
+
* This decoder is what makes "there is no SIM" an EXPLICIT reading rather than an
|
|
71
|
+
* inference. `Modem.StateFailedReason` is a `u` on D-Bus, so a provider that copies
|
|
72
|
+
* the property verbatim holds a NUMBER, while the migrated SIM-presence rule
|
|
73
|
+
* (`deriveSimPresence`) matches the mmcli STRING `sim-missing` that CeraUI has always
|
|
74
|
+
* read. Without this decoder the D-Bus path can never produce the one fact that
|
|
75
|
+
* proves absence, and a modem with no SIM reads `unknown` forever — or, worse,
|
|
76
|
+
* invites a consumer to infer absence from the blank `Sim` object path, which is
|
|
77
|
+
* exactly the guess this package refuses to make.
|
|
78
|
+
*
|
|
79
|
+
* An unrecognized value answers `undefined`: a reason this build cannot place says
|
|
80
|
+
* nothing, and must not be laundered into one that does.
|
|
81
|
+
*/
|
|
82
|
+
const STATE_FAILED_REASONS = new Map([
|
|
83
|
+
[0, 'none'],
|
|
84
|
+
// MM's own `UNKNOWN` member. Spelled distinctly so it is never confused with the
|
|
85
|
+
// `'unknown'` sentinel the label decoders use to mean "this build could not place it".
|
|
86
|
+
[1, 'unknown-reason'],
|
|
87
|
+
[2, 'sim-missing'],
|
|
88
|
+
[3, 'sim-error'],
|
|
89
|
+
[4, 'unknown-capabilities'],
|
|
90
|
+
[5, 'esim-without-profiles'],
|
|
91
|
+
]);
|
|
92
|
+
export function decodeStateFailedReason(value) {
|
|
93
|
+
return value === undefined ? undefined : STATE_FAILED_REASONS.get(value);
|
|
94
|
+
}
|
|
95
|
+
export function decodeUnlockRequired(lock) {
|
|
96
|
+
return lock === 1
|
|
97
|
+
? 'none'
|
|
98
|
+
: lock === 2
|
|
99
|
+
? 'sim-pin'
|
|
100
|
+
: lock === 3
|
|
101
|
+
? 'sim-pin2'
|
|
102
|
+
: lock === 4
|
|
103
|
+
? 'sim-puk'
|
|
104
|
+
: lock === 5
|
|
105
|
+
? 'sim-puk2'
|
|
106
|
+
: undefined;
|
|
107
|
+
}
|
|
108
|
+
export function decodeSimType(value) {
|
|
109
|
+
return value === 1 ? 'physical' : value === 2 ? 'esim' : undefined;
|
|
110
|
+
}
|
|
111
|
+
export function decodeEsimStatus(value) {
|
|
112
|
+
return value === 1 ? 'no-profiles' : value === 2 ? 'with-profiles' : undefined;
|
|
113
|
+
}
|
|
114
|
+
export function decodePacketServiceState(value) {
|
|
115
|
+
return value === 1 ? 'detached' : value === 2 ? 'attached' : undefined;
|
|
116
|
+
}
|
|
117
|
+
const NETWORK_REJECTIONS = new Map([
|
|
118
|
+
[2, 'imsi-unknown-in-hlr'],
|
|
119
|
+
[3, 'illegal-ms'],
|
|
120
|
+
[4, 'imsi-unknown-in-vlr'],
|
|
121
|
+
[5, 'imei-not-accepted'],
|
|
122
|
+
[6, 'illegal-me'],
|
|
123
|
+
[7, 'gprs-not-allowed'],
|
|
124
|
+
[8, 'gprs-and-non-gprs-not-allowed'],
|
|
125
|
+
[11, 'plmn-not-allowed'],
|
|
126
|
+
[12, 'location-area-not-allowed'],
|
|
127
|
+
[13, 'roaming-not-allowed-in-location-area'],
|
|
128
|
+
[14, 'gprs-not-allowed-in-plmn'],
|
|
129
|
+
[15, 'no-cells-in-location-area'],
|
|
130
|
+
[17, 'network-failure'],
|
|
131
|
+
[22, 'congestion'],
|
|
132
|
+
]);
|
|
133
|
+
export function decodeNetworkRejectionError(value) {
|
|
134
|
+
return value === undefined ? undefined : NETWORK_REJECTIONS.get(value);
|
|
135
|
+
}
|
|
136
|
+
export function runtimeIdFromPath(path) {
|
|
137
|
+
const value = /\/(\d+)$/.exec(path)?.[1];
|
|
138
|
+
return value === undefined ? undefined : Number.parseInt(value, 10);
|
|
139
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface ModemHardwareIdentity {
|
|
2
|
+
readonly model?: string;
|
|
3
|
+
readonly manufacturer?: string;
|
|
4
|
+
readonly firmwareRevision?: string;
|
|
5
|
+
readonly equipmentId?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function isUninformativeIdentity(value: string | undefined): boolean;
|
|
8
|
+
export declare function firmwareIdentityLabel(revision: string | undefined): string | undefined;
|
|
9
|
+
export declare function modemHardwareLabel(identity: ModemHardwareIdentity): string;
|
|
10
|
+
export declare function modemHardwareName(identity: ModemHardwareIdentity): string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const BARE_NUMERAL_RE = /^\d{1,4}$/;
|
|
2
|
+
const MMCLI_EMPTY = '--';
|
|
3
|
+
export function isUninformativeIdentity(value) {
|
|
4
|
+
const trimmed = value?.trim();
|
|
5
|
+
return (trimmed === undefined ||
|
|
6
|
+
trimmed === '' ||
|
|
7
|
+
trimmed === MMCLI_EMPTY ||
|
|
8
|
+
BARE_NUMERAL_RE.test(trimmed));
|
|
9
|
+
}
|
|
10
|
+
export function firmwareIdentityLabel(revision) {
|
|
11
|
+
const trimmed = revision?.trim();
|
|
12
|
+
if (!trimmed || trimmed === MMCLI_EMPTY)
|
|
13
|
+
return undefined;
|
|
14
|
+
const head = trimmed
|
|
15
|
+
.replace(/\s*\[[^\]]*]\s*$/, '')
|
|
16
|
+
.trim()
|
|
17
|
+
.split(/\s{2,}/)[0]
|
|
18
|
+
?.trim() ?? '';
|
|
19
|
+
return head.length >= 3 && /[A-Za-z]/.test(head) ? head : undefined;
|
|
20
|
+
}
|
|
21
|
+
export function modemHardwareLabel(identity) {
|
|
22
|
+
const model = identity.model?.trim();
|
|
23
|
+
if (model !== undefined && !isUninformativeIdentity(model))
|
|
24
|
+
return model;
|
|
25
|
+
const firmware = firmwareIdentityLabel(identity.firmwareRevision);
|
|
26
|
+
if (firmware !== undefined)
|
|
27
|
+
return firmware;
|
|
28
|
+
const manufacturer = identity.manufacturer?.trim();
|
|
29
|
+
return manufacturer !== undefined && !isUninformativeIdentity(manufacturer)
|
|
30
|
+
? manufacturer
|
|
31
|
+
: 'Cellular modem';
|
|
32
|
+
}
|
|
33
|
+
export function modemHardwareName(identity) {
|
|
34
|
+
const label = modemHardwareLabel(identity);
|
|
35
|
+
const equipmentId = identity.equipmentId?.trim();
|
|
36
|
+
return !equipmentId || equipmentId === MMCLI_EMPTY
|
|
37
|
+
? label
|
|
38
|
+
: `${label} - ${equipmentId.slice(-5)}`;
|
|
39
|
+
}
|