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