@ceralive/modem-control 0.2.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 -221
- 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/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 -37
- package/src/backend/usage/proc-net-dev.test.ts +0 -56
- package/src/backend/usage/sampler.test.ts +0 -219
- package/src/backend/usage/sampler.ts +0 -228
- 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 -27
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// The band-write descriptor — the certification gate, expressed as an operation contract.
|
|
2
|
+
//
|
|
3
|
+
// It ADDS NOTHING to `band/certification.ts`. That module already owns the four-proof
|
|
4
|
+
// per-SKU catalog, the fail-closed lookup, and the `offerableBands` narrowing, and it
|
|
5
|
+
// is the only place a SKU may be declared certified. What was missing was the other
|
|
6
|
+
// end of the wire: a band write's OPERATION DESCRIPTOR did not say that it is
|
|
7
|
+
// disruptive-with-a-certification-requirement, so the gate lived only inside the
|
|
8
|
+
// provider's capability flag and a consumer reading the descriptor could not see it.
|
|
9
|
+
//
|
|
10
|
+
// TWO INDEPENDENT FENCES, deliberately. `availability: refused` is what a consumer
|
|
11
|
+
// reads to decide whether to offer the control at all; the provider's own
|
|
12
|
+
// `bandWrite` capability check is what refuses the call if one is made anyway. A gate
|
|
13
|
+
// that exists only in the descriptor is advisory, and a gate that exists only in the
|
|
14
|
+
// provider is invisible — a band lock can take a working uplink off the air, so it
|
|
15
|
+
// gets both.
|
|
16
|
+
import { offerableBands } from '../band/index.js';
|
|
17
|
+
import { defineOperationDescriptor } from '../domain/index.js';
|
|
18
|
+
export const BAND_WRITE_OPERATION_ID = 'modemmanager.bands';
|
|
19
|
+
/** The refusal reasons a band write may carry. Certification is the first gate. */
|
|
20
|
+
export const BAND_WRITE_REFUSAL = 'band-certification-required';
|
|
21
|
+
export const BAND_READ_REFUSAL = 'band-read-unsupported';
|
|
22
|
+
export const BAND_NONE_OFFERABLE_REFUSAL = 'no-offerable-certified-bands';
|
|
23
|
+
export function describeBandWriteCertification(input) {
|
|
24
|
+
const offerable = offerableBands(input.entry, input.supported);
|
|
25
|
+
return input.entry === undefined
|
|
26
|
+
? { required: true, satisfied: false, reason: BAND_WRITE_REFUSAL, offerable }
|
|
27
|
+
: { required: true, satisfied: true, reason: 'band-certification-proven', offerable };
|
|
28
|
+
}
|
|
29
|
+
function sameBandSet(left, right) {
|
|
30
|
+
if (left.length !== right.length)
|
|
31
|
+
return false;
|
|
32
|
+
const sorted = [...right].sort();
|
|
33
|
+
return [...left].sort().every((band, index) => band === sorted[index]);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Whether a band readback confirms the write.
|
|
37
|
+
*
|
|
38
|
+
* A NARROWING lock must match exactly — a superset is a different lock from the one
|
|
39
|
+
* that was asked for. The reset (`['any']`) is the exception ModemManager forces:
|
|
40
|
+
* releasing a lock is `SetCurrentBands([ANY])`, and a modem afterwards reports either
|
|
41
|
+
* `any` or its whole supported set, both of which mean "no lock is in force".
|
|
42
|
+
*/
|
|
43
|
+
export function bandWriteReadbackMatches(requested, observed) {
|
|
44
|
+
if (requested.length === 1 && requested[0] === 'any') {
|
|
45
|
+
return ((observed.current.length === 1 && observed.current[0] === 'any') ||
|
|
46
|
+
sameBandSet(observed.current, observed.supported));
|
|
47
|
+
}
|
|
48
|
+
return sameBandSet(requested, observed.current);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The live band-write descriptor.
|
|
52
|
+
*
|
|
53
|
+
* `mutationImpact: 'disruptive'` because `SetCurrentBands` re-registers the radio and
|
|
54
|
+
* drops the bearer underneath NetworkManager; `readback` is REQUIRED because an
|
|
55
|
+
* accepted-but-ignored band write is the failure mode the catalog's own `readback`
|
|
56
|
+
* proof exists to catch, and it looks like success from the call site alone.
|
|
57
|
+
*/
|
|
58
|
+
export function buildBandWriteDescriptor(input) {
|
|
59
|
+
const refusal = !input.certification.satisfied
|
|
60
|
+
? BAND_WRITE_REFUSAL
|
|
61
|
+
: input.certification.offerable.length === 0
|
|
62
|
+
? BAND_NONE_OFFERABLE_REFUSAL
|
|
63
|
+
: undefined;
|
|
64
|
+
return defineOperationDescriptor({
|
|
65
|
+
id: BAND_WRITE_OPERATION_ID,
|
|
66
|
+
support: {
|
|
67
|
+
read: input.readSupported
|
|
68
|
+
? { supported: true }
|
|
69
|
+
: { supported: false, reason: BAND_READ_REFUSAL },
|
|
70
|
+
write: input.certification.satisfied
|
|
71
|
+
? { supported: true }
|
|
72
|
+
: { supported: false, reason: BAND_WRITE_REFUSAL },
|
|
73
|
+
},
|
|
74
|
+
authority: 'provider',
|
|
75
|
+
provider: input.provider,
|
|
76
|
+
constraints: { kind: 'allowed-values', values: [input.certification.offerable] },
|
|
77
|
+
livePreconditions: ['modem-present', 'runtime-interface-present', 'band-certification-present'],
|
|
78
|
+
availability: refusal === undefined ? { state: 'available' } : { state: 'refused', reason: refusal },
|
|
79
|
+
mutationImpact: 'disruptive',
|
|
80
|
+
retryClass: 'never',
|
|
81
|
+
readback: {
|
|
82
|
+
required: true,
|
|
83
|
+
reason: 'band-write-readback',
|
|
84
|
+
matches: bandWriteReadbackMatches,
|
|
85
|
+
},
|
|
86
|
+
rollback: { required: false },
|
|
87
|
+
journal: { required: true, reason: 'disruptive-radio-write' },
|
|
88
|
+
admission: { required: true, reason: 'provider-mutation' },
|
|
89
|
+
evidence: { profiles: [input.profile], firmware: [] },
|
|
90
|
+
confidence: 'high',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Radio capability TRUTH: the modem's own mode catalog and band-write gate, verbatim.
|
|
2
|
+
//
|
|
3
|
+
// It sits beside `src/band/` (the band vocabulary and per-SKU certification catalog)
|
|
4
|
+
// rather than inside it, because a mode combination is not a band and the two have
|
|
5
|
+
// different safety models — a mode change is disruptive-but-reversible, a band lock
|
|
6
|
+
// can strand a radio where nothing registers. Reachable through the package ROOT
|
|
7
|
+
// entry, deliberately NOT a new package subpath (the todo 17/18/23 precedent).
|
|
8
|
+
export * from './band-truth.js';
|
|
9
|
+
export * from './mode-combinations.js';
|
|
10
|
+
export * from './mode-truth.js';
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** A mode as every operator-facing surface spells it. Opaque; compare by equality. */
|
|
2
|
+
export type ModeName = string;
|
|
3
|
+
/**
|
|
4
|
+
* `MM_MODEM_MODE_NONE` (0) — the modem expresses no preference within its allowed set.
|
|
5
|
+
* A legal, load-bearing value; never a stand-in for "not reported".
|
|
6
|
+
*/
|
|
7
|
+
export declare const MODE_NONE = "none";
|
|
8
|
+
/** `MM_MODEM_MODE_ANY` (0xFFFFFFFF) — every mode the modem has. */
|
|
9
|
+
export declare const MODE_ANY = "any";
|
|
10
|
+
/** Decode one `MMModemMode` bit. Total: an unnamed bit round-trips. */
|
|
11
|
+
export declare function modeName(bit: number): ModeName;
|
|
12
|
+
/** Encode one mode name. `undefined` for a name this build cannot place. */
|
|
13
|
+
export declare function modeValue(name: ModeName): number | undefined;
|
|
14
|
+
/** True when this build recognises the name (a `mode-bit-<n>` passthrough does not). */
|
|
15
|
+
export declare function isNamedMode(name: ModeName): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Split a mask into its set bits, named where this build can and passed through where
|
|
18
|
+
* it cannot. A zero mask is the EMPTY set — the caller decides whether that reads as
|
|
19
|
+
* `none` (a preference) or as an anomaly (an allowed set nothing can be chosen from).
|
|
20
|
+
*/
|
|
21
|
+
export declare function modeNames(mask: number): readonly ModeName[];
|
|
22
|
+
/** Encode a set of mode names into one mask. Fails closed on an unplaceable name. */
|
|
23
|
+
export declare function encodeModeNames(names: readonly ModeName[]): {
|
|
24
|
+
readonly ok: true;
|
|
25
|
+
readonly mask: number;
|
|
26
|
+
} | {
|
|
27
|
+
readonly ok: false;
|
|
28
|
+
readonly unknown: ModeName;
|
|
29
|
+
};
|
|
30
|
+
/** Why a combination could not be fully placed. Never a reason to hide it. */
|
|
31
|
+
export declare const MODE_COMBINATION_ANOMALIES: readonly ["unnamed-allowed-bit", "unnamed-preferred-bit", "preferred-not-in-allowed", "preferred-not-singular", "empty-allowed"];
|
|
32
|
+
export type ModeCombinationAnomaly = (typeof MODE_COMBINATION_ANOMALIES)[number];
|
|
33
|
+
export type ModeCombinationClassification = 'named' | 'unknown-combination';
|
|
34
|
+
/**
|
|
35
|
+
* One `(allowed, preferred)` pair, decoded without loss.
|
|
36
|
+
*
|
|
37
|
+
* The masks are retained ALONGSIDE the names so a consumer can always reproduce the
|
|
38
|
+
* exact bytes the modem reported, whatever this build managed to name.
|
|
39
|
+
*/
|
|
40
|
+
export type ModeCombination = {
|
|
41
|
+
readonly allowedMask: number;
|
|
42
|
+
readonly allowed: readonly ModeName[];
|
|
43
|
+
readonly preferredMask: number;
|
|
44
|
+
/** `none` when the modem stated no preference — verbatim, never a substitute. */
|
|
45
|
+
readonly preferred: ModeName;
|
|
46
|
+
readonly classification: ModeCombinationClassification;
|
|
47
|
+
readonly anomalies: readonly ModeCombinationAnomaly[];
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* A `SupportedModes` catalog: everything decoded, plus everything that could not be.
|
|
51
|
+
*
|
|
52
|
+
* `undecodable` exists so the no-drop property is structural rather than a promise —
|
|
53
|
+
* `combinations.length + undecodable.length` is the member count the provider sent.
|
|
54
|
+
*/
|
|
55
|
+
export type ModeCombinationSet = {
|
|
56
|
+
readonly combinations: readonly ModeCombination[];
|
|
57
|
+
/** Members that were not a `(uu)` pair, retained exactly as the provider sent them. */
|
|
58
|
+
readonly undecodable: readonly unknown[];
|
|
59
|
+
};
|
|
60
|
+
/** Build a combination from two raw masks, recording every anomaly it carries. */
|
|
61
|
+
export declare function modeCombination(allowedMask: number, preferredMask: number): ModeCombination;
|
|
62
|
+
/** True when the modem stated no preference within this combination's allowed set. */
|
|
63
|
+
export declare function statesNoPreference(combination: ModeCombination): boolean;
|
|
64
|
+
/** Decode one `CurrentModes` `(uu)`. `undefined` only when the value is not a pair. */
|
|
65
|
+
export declare function decodeModeCombination(value: unknown): ModeCombination | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Decode a `SupportedModes` `a(uu)`.
|
|
68
|
+
*
|
|
69
|
+
* Nothing is dropped: a member that is not a pair lands in `undecodable`, and a pair
|
|
70
|
+
* this build cannot fully name lands in `combinations` classified
|
|
71
|
+
* `unknown-combination`. A non-array value is an empty catalog, not an error — a modem
|
|
72
|
+
* that advertises no mode control is a real reading.
|
|
73
|
+
*/
|
|
74
|
+
export declare function decodeSupportedModeCombinations(value: unknown): ModeCombinationSet;
|
|
75
|
+
/** True when any advertised combination carries an anomaly this build could not place. */
|
|
76
|
+
export declare function hasUnknownCombination(set: ModeCombinationSet): boolean;
|
|
77
|
+
/** Whether a mask names a mode this build recognises across every set bit. */
|
|
78
|
+
export declare function isFullyNamedMask(mask: number): boolean;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// `MMModemMode` ↔ mode NAME, and the (allowed, preferred) COMBINATION, verbatim.
|
|
2
|
+
//
|
|
3
|
+
// ModemManager advertises what a radio can do as `Modem.SupportedModes`, an `a(uu)`
|
|
4
|
+
// of (allowed-mask, preferred-mask) pairs, and what it is doing as `Modem.CurrentModes`,
|
|
5
|
+
// one `(uu)`. Everything an operator may legally ask for is in that list, and this
|
|
6
|
+
// module's whole job is to carry it across UNCHANGED.
|
|
7
|
+
//
|
|
8
|
+
// THREE THINGS ARE ROUTINELY LOST BY A DECODER THAT MEANS WELL, and each one is a
|
|
9
|
+
// documented CeraLive case rather than a hypothetical:
|
|
10
|
+
//
|
|
11
|
+
// 1. `preferred: 0` (`MM_MODEM_MODE_NONE`). The bench Fibocom FM350-GL advertises
|
|
12
|
+
// exactly one combination, and its preferred mask is 0 — the modem allows a set
|
|
13
|
+
// of modes and states NO preference within it. That is a real, legal answer, and
|
|
14
|
+
// it is NOT the same as "prefer the highest allowed mode". Substituting a default
|
|
15
|
+
// shows an operator a preference the modem never expressed and cannot be returned
|
|
16
|
+
// to. `preferred` is therefore the name `none`, carried through to the operation
|
|
17
|
+
// descriptor's own allowed-value list.
|
|
18
|
+
// 2. A bit this build does not name. MM's mode enum grows; a mask carrying an
|
|
19
|
+
// unfamiliar bit is still a combination the modem advertised and will accept. It
|
|
20
|
+
// round-trips as `mode-bit-<n>` (the `band-<n>` discipline from `band-names.ts`),
|
|
21
|
+
// the combination is CLASSIFIED `unknown-combination`, and it stays offerable.
|
|
22
|
+
// `unknown` is never coerced to `unsupported` — that is the support-claim
|
|
23
|
+
// taxonomy's first rule, and it applies to a mode catalog exactly as it does to a
|
|
24
|
+
// capability probe.
|
|
25
|
+
// 3. A member that is not a `(uu)` at all. Dropping it silently shortens the catalog,
|
|
26
|
+
// so `decodeSupportedModeCombinations` RETAINS it in a separate `undecodable`
|
|
27
|
+
// list. Decoded + undecodable always equals what the provider sent.
|
|
28
|
+
//
|
|
29
|
+
// This module is pure and total: no transport, no clock, no I/O. The `SetCurrentModes`
|
|
30
|
+
// call itself stays where every other radio mutation lives (`backend/mm-mutations.ts`).
|
|
31
|
+
/**
|
|
32
|
+
* `MM_MODEM_MODE_NONE` (0) — the modem expresses no preference within its allowed set.
|
|
33
|
+
* A legal, load-bearing value; never a stand-in for "not reported".
|
|
34
|
+
*/
|
|
35
|
+
export const MODE_NONE = 'none';
|
|
36
|
+
/** `MM_MODEM_MODE_ANY` (0xFFFFFFFF) — every mode the modem has. */
|
|
37
|
+
export const MODE_ANY = 'any';
|
|
38
|
+
const MODE_ANY_VALUE = 0xffffffff;
|
|
39
|
+
/** The named `MMModemMode` bits, in MM's own bit order. */
|
|
40
|
+
const NAMED_BITS = [
|
|
41
|
+
[1 << 0, 'cs'],
|
|
42
|
+
[1 << 1, '2g'],
|
|
43
|
+
[1 << 2, '3g'],
|
|
44
|
+
[1 << 3, '4g'],
|
|
45
|
+
[1 << 4, '5g'],
|
|
46
|
+
];
|
|
47
|
+
const BIT_TO_NAME = new Map(NAMED_BITS);
|
|
48
|
+
const NAME_TO_BIT = new Map(NAMED_BITS.map(([bit, name]) => [name, bit]));
|
|
49
|
+
/** The passthrough spelling for a mode bit this build does not name. */
|
|
50
|
+
const PASSTHROUGH_RE = /^mode-bit-(\d+)$/;
|
|
51
|
+
const NAMED_MASK = NAMED_BITS.reduce((mask, [bit]) => mask | bit, 0);
|
|
52
|
+
/** Decode one `MMModemMode` bit. Total: an unnamed bit round-trips. */
|
|
53
|
+
export function modeName(bit) {
|
|
54
|
+
return BIT_TO_NAME.get(bit) ?? `mode-bit-${bit}`;
|
|
55
|
+
}
|
|
56
|
+
/** Encode one mode name. `undefined` for a name this build cannot place. */
|
|
57
|
+
export function modeValue(name) {
|
|
58
|
+
if (name === MODE_NONE)
|
|
59
|
+
return 0;
|
|
60
|
+
if (name === MODE_ANY)
|
|
61
|
+
return MODE_ANY_VALUE;
|
|
62
|
+
const known = NAME_TO_BIT.get(name);
|
|
63
|
+
if (known !== undefined)
|
|
64
|
+
return known;
|
|
65
|
+
const passthrough = PASSTHROUGH_RE.exec(name);
|
|
66
|
+
if (passthrough?.[1] === undefined)
|
|
67
|
+
return undefined;
|
|
68
|
+
const parsed = Number(passthrough[1]);
|
|
69
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined;
|
|
70
|
+
}
|
|
71
|
+
/** True when this build recognises the name (a `mode-bit-<n>` passthrough does not). */
|
|
72
|
+
export function isNamedMode(name) {
|
|
73
|
+
return name === MODE_NONE || name === MODE_ANY || NAME_TO_BIT.has(name);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Split a mask into its set bits, named where this build can and passed through where
|
|
77
|
+
* it cannot. A zero mask is the EMPTY set — the caller decides whether that reads as
|
|
78
|
+
* `none` (a preference) or as an anomaly (an allowed set nothing can be chosen from).
|
|
79
|
+
*/
|
|
80
|
+
export function modeNames(mask) {
|
|
81
|
+
if (!Number.isSafeInteger(mask) || mask <= 0)
|
|
82
|
+
return [];
|
|
83
|
+
if (mask === MODE_ANY_VALUE)
|
|
84
|
+
return [MODE_ANY];
|
|
85
|
+
const names = [];
|
|
86
|
+
for (let bit = 1; bit <= mask && bit > 0; bit *= 2) {
|
|
87
|
+
if ((mask & bit) !== 0)
|
|
88
|
+
names.push(modeName(bit));
|
|
89
|
+
}
|
|
90
|
+
return names;
|
|
91
|
+
}
|
|
92
|
+
/** Encode a set of mode names into one mask. Fails closed on an unplaceable name. */
|
|
93
|
+
export function encodeModeNames(names) {
|
|
94
|
+
let mask = 0;
|
|
95
|
+
for (const name of names) {
|
|
96
|
+
const value = modeValue(name);
|
|
97
|
+
if (value === undefined)
|
|
98
|
+
return { ok: false, unknown: name };
|
|
99
|
+
mask |= value;
|
|
100
|
+
}
|
|
101
|
+
return { ok: true, mask: mask >>> 0 };
|
|
102
|
+
}
|
|
103
|
+
/** Why a combination could not be fully placed. Never a reason to hide it. */
|
|
104
|
+
export const MODE_COMBINATION_ANOMALIES = [
|
|
105
|
+
/** The allowed mask carries a bit this build does not name. */
|
|
106
|
+
'unnamed-allowed-bit',
|
|
107
|
+
/** The preferred mask carries a bit this build does not name. */
|
|
108
|
+
'unnamed-preferred-bit',
|
|
109
|
+
/** The preferred mode is not a member of the allowed set. */
|
|
110
|
+
'preferred-not-in-allowed',
|
|
111
|
+
/** The preferred mask names more than one mode; MM's contract is at most one. */
|
|
112
|
+
'preferred-not-singular',
|
|
113
|
+
/** The allowed mask is zero — nothing can be selected from this combination. */
|
|
114
|
+
'empty-allowed',
|
|
115
|
+
];
|
|
116
|
+
function isUnnamed(names) {
|
|
117
|
+
return names.some((name) => !isNamedMode(name));
|
|
118
|
+
}
|
|
119
|
+
/** Build a combination from two raw masks, recording every anomaly it carries. */
|
|
120
|
+
export function modeCombination(allowedMask, preferredMask) {
|
|
121
|
+
const allowed = modeNames(allowedMask);
|
|
122
|
+
const preferredNames = modeNames(preferredMask);
|
|
123
|
+
const anomalies = [];
|
|
124
|
+
if (allowed.length === 0)
|
|
125
|
+
anomalies.push('empty-allowed');
|
|
126
|
+
if (isUnnamed(allowed))
|
|
127
|
+
anomalies.push('unnamed-allowed-bit');
|
|
128
|
+
if (isUnnamed(preferredNames))
|
|
129
|
+
anomalies.push('unnamed-preferred-bit');
|
|
130
|
+
if (preferredNames.length > 1)
|
|
131
|
+
anomalies.push('preferred-not-singular');
|
|
132
|
+
// A zero preferred mask is `none` and is NOT an anomaly: it is MM's own way of
|
|
133
|
+
// saying "no preference within the allowed set", which the FM350 actually reports.
|
|
134
|
+
if (preferredNames.length > 0 &&
|
|
135
|
+
preferredMask !== MODE_ANY_VALUE &&
|
|
136
|
+
(preferredMask & allowedMask) !== preferredMask) {
|
|
137
|
+
anomalies.push('preferred-not-in-allowed');
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
allowedMask,
|
|
141
|
+
allowed,
|
|
142
|
+
preferredMask,
|
|
143
|
+
preferred: preferredNames.length === 0 ? MODE_NONE : preferredNames.join('+'),
|
|
144
|
+
classification: anomalies.length === 0 ? 'named' : 'unknown-combination',
|
|
145
|
+
anomalies,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** True when the modem stated no preference within this combination's allowed set. */
|
|
149
|
+
export function statesNoPreference(combination) {
|
|
150
|
+
return combination.preferredMask === 0;
|
|
151
|
+
}
|
|
152
|
+
function decodePair(value) {
|
|
153
|
+
if (!Array.isArray(value) || value.length !== 2)
|
|
154
|
+
return undefined;
|
|
155
|
+
const [allowed, preferred] = value;
|
|
156
|
+
if (typeof allowed !== 'number' || !Number.isSafeInteger(allowed) || allowed < 0)
|
|
157
|
+
return undefined;
|
|
158
|
+
if (typeof preferred !== 'number' || !Number.isSafeInteger(preferred) || preferred < 0) {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
return [allowed, preferred];
|
|
162
|
+
}
|
|
163
|
+
/** Decode one `CurrentModes` `(uu)`. `undefined` only when the value is not a pair. */
|
|
164
|
+
export function decodeModeCombination(value) {
|
|
165
|
+
const pair = decodePair(value);
|
|
166
|
+
return pair === undefined ? undefined : modeCombination(pair[0], pair[1]);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Decode a `SupportedModes` `a(uu)`.
|
|
170
|
+
*
|
|
171
|
+
* Nothing is dropped: a member that is not a pair lands in `undecodable`, and a pair
|
|
172
|
+
* this build cannot fully name lands in `combinations` classified
|
|
173
|
+
* `unknown-combination`. A non-array value is an empty catalog, not an error — a modem
|
|
174
|
+
* that advertises no mode control is a real reading.
|
|
175
|
+
*/
|
|
176
|
+
export function decodeSupportedModeCombinations(value) {
|
|
177
|
+
if (!Array.isArray(value))
|
|
178
|
+
return { combinations: [], undecodable: [] };
|
|
179
|
+
const combinations = [];
|
|
180
|
+
const undecodable = [];
|
|
181
|
+
for (const member of value) {
|
|
182
|
+
const pair = decodePair(member);
|
|
183
|
+
if (pair === undefined)
|
|
184
|
+
undecodable.push(member);
|
|
185
|
+
else
|
|
186
|
+
combinations.push(modeCombination(pair[0], pair[1]));
|
|
187
|
+
}
|
|
188
|
+
return { combinations, undecodable };
|
|
189
|
+
}
|
|
190
|
+
/** True when any advertised combination carries an anomaly this build could not place. */
|
|
191
|
+
export function hasUnknownCombination(set) {
|
|
192
|
+
return (set.undecodable.length > 0 ||
|
|
193
|
+
set.combinations.some((each) => each.classification === 'unknown-combination'));
|
|
194
|
+
}
|
|
195
|
+
/** Whether a mask names a mode this build recognises across every set bit. */
|
|
196
|
+
export function isFullyNamedMask(mask) {
|
|
197
|
+
return mask === MODE_ANY_VALUE || mask === 0 || (mask & ~NAMED_MASK) === 0;
|
|
198
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type OperationDescriptor } from '../domain/index.js';
|
|
2
|
+
import { type ModeCombination, type ModeCombinationSet, type ModeName } from './mode-combinations.js';
|
|
3
|
+
/**
|
|
4
|
+
* A mode selection as a caller expresses it: the set to allow, and the one to prefer
|
|
5
|
+
* within it. `preferred: 'none'` is a first-class selection, not a missing field.
|
|
6
|
+
*/
|
|
7
|
+
export type ModeSelection = {
|
|
8
|
+
readonly allowed: readonly ModeName[];
|
|
9
|
+
readonly preferred: ModeName;
|
|
10
|
+
};
|
|
11
|
+
/** `CurrentModes` as a reading — "the modem did not report it" is an answer, not a null. */
|
|
12
|
+
export type ModeCombinationReading = {
|
|
13
|
+
readonly state: 'reported';
|
|
14
|
+
readonly combination: ModeCombination;
|
|
15
|
+
} | {
|
|
16
|
+
readonly state: 'not-reported';
|
|
17
|
+
};
|
|
18
|
+
export type RadioModeTruth = {
|
|
19
|
+
readonly current: ModeCombinationReading;
|
|
20
|
+
readonly supported: ModeCombinationSet;
|
|
21
|
+
};
|
|
22
|
+
export declare function readRadioModeTruth(input: {
|
|
23
|
+
readonly currentModes: unknown;
|
|
24
|
+
readonly supportedModes: unknown;
|
|
25
|
+
}): RadioModeTruth;
|
|
26
|
+
/** The selection a combination represents, with `preferred` carried across verbatim. */
|
|
27
|
+
export declare function selectionOf(combination: ModeCombination): ModeSelection;
|
|
28
|
+
/** Two selections are the same when both the allowed set AND the preference agree. */
|
|
29
|
+
export declare function sameSelection(left: ModeSelection, right: ModeSelection): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* The advertised combination a selection names, or `undefined`.
|
|
32
|
+
*
|
|
33
|
+
* `undefined` is the whole answer — never the nearest neighbour. `prefer-4g` silently
|
|
34
|
+
* becoming `prefer-5g` on a marginal cell is the exact substitution
|
|
35
|
+
* `five-g-preference.ts` refuses, and it is refused here for the same reason.
|
|
36
|
+
*/
|
|
37
|
+
export declare function matchAdvertisedCombination(supported: ModeCombinationSet, selection: ModeSelection): ModeCombination | undefined;
|
|
38
|
+
/** The masks `SetCurrentModes` needs, or the name that could not be placed. */
|
|
39
|
+
export declare function encodeModeSelection(selection: ModeSelection): {
|
|
40
|
+
readonly ok: true;
|
|
41
|
+
readonly allowedMask: number;
|
|
42
|
+
readonly preferredMask: number;
|
|
43
|
+
} | {
|
|
44
|
+
readonly ok: false;
|
|
45
|
+
readonly unknown: ModeName;
|
|
46
|
+
};
|
|
47
|
+
export declare const MODE_WRITE_OPERATION_ID = "modemmanager.mode-combination";
|
|
48
|
+
/** Why a mode write is not on offer right now. */
|
|
49
|
+
export type ModeWriteRefusal = 'mode-write-unsupported' | 'no-advertised-mode-combinations';
|
|
50
|
+
export type ModeWriteDescriptorInput = {
|
|
51
|
+
readonly provider: string;
|
|
52
|
+
readonly profile: string;
|
|
53
|
+
readonly truth: RadioModeTruth;
|
|
54
|
+
readonly writeSupported: boolean;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* The live mode-write descriptor.
|
|
58
|
+
*
|
|
59
|
+
* `constraints` is an `allowed-values` list built from the modem's OWN catalog, so a
|
|
60
|
+
* combination carrying `preferred: 'none'` reaches a consumer through the descriptor
|
|
61
|
+
* exactly as the modem stated it. Readback is REQUIRED: `SetCurrentModes` returning
|
|
62
|
+
* without an error only proves the daemon accepted the call, and an
|
|
63
|
+
* accepted-but-ignored mode write is indistinguishable from success at the call site.
|
|
64
|
+
*/
|
|
65
|
+
export declare function buildModeWriteDescriptor(input: ModeWriteDescriptorInput): OperationDescriptor<ModeSelection, RadioModeTruth>;
|
|
66
|
+
/** True when the modem advertises at least one combination stating no preference. */
|
|
67
|
+
export declare function advertisesNoPreference(truth: RadioModeTruth): boolean;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// What the modem says it can do with its modes, and the operation descriptor built
|
|
2
|
+
// from exactly that — nothing added, nothing narrowed.
|
|
3
|
+
//
|
|
4
|
+
// `capability/five-g-preference.ts` maps four named POSTURES onto an (allowed,
|
|
5
|
+
// preferred) pair and refuses to name one the modem never advertised. This module is
|
|
6
|
+
// the layer underneath it: the modem's own catalog, unedited, so a posture selector
|
|
7
|
+
// and a raw combination selector are reading the same truth. It deliberately does not
|
|
8
|
+
// know what a posture is.
|
|
9
|
+
//
|
|
10
|
+
// THE OFFERED SET IS THE ADVERTISED SET. A combination classified
|
|
11
|
+
// `unknown-combination` — an unfamiliar mode bit, a preferred mode outside its own
|
|
12
|
+
// allowed set — is STILL offered, because the modem advertised it and will accept it.
|
|
13
|
+
// Hiding it would be coercing `unknown` into `unsupported`, which is the one thing
|
|
14
|
+
// `support-claim.ts` exists to stop.
|
|
15
|
+
import { defineOperationDescriptor } from '../domain/index.js';
|
|
16
|
+
import { decodeModeCombination, decodeSupportedModeCombinations, encodeModeNames, MODE_NONE, statesNoPreference, } from './mode-combinations.js';
|
|
17
|
+
export function readRadioModeTruth(input) {
|
|
18
|
+
const current = decodeModeCombination(input.currentModes);
|
|
19
|
+
return {
|
|
20
|
+
current: current === undefined
|
|
21
|
+
? { state: 'not-reported' }
|
|
22
|
+
: { state: 'reported', combination: current },
|
|
23
|
+
supported: decodeSupportedModeCombinations(input.supportedModes),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** The selection a combination represents, with `preferred` carried across verbatim. */
|
|
27
|
+
export function selectionOf(combination) {
|
|
28
|
+
return { allowed: combination.allowed, preferred: combination.preferred };
|
|
29
|
+
}
|
|
30
|
+
function sameNameSet(left, right) {
|
|
31
|
+
if (left.length !== right.length)
|
|
32
|
+
return false;
|
|
33
|
+
const sorted = [...right].sort();
|
|
34
|
+
return [...left].sort().every((name, index) => name === sorted[index]);
|
|
35
|
+
}
|
|
36
|
+
/** Two selections are the same when both the allowed set AND the preference agree. */
|
|
37
|
+
export function sameSelection(left, right) {
|
|
38
|
+
return left.preferred === right.preferred && sameNameSet(left.allowed, right.allowed);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The advertised combination a selection names, or `undefined`.
|
|
42
|
+
*
|
|
43
|
+
* `undefined` is the whole answer — never the nearest neighbour. `prefer-4g` silently
|
|
44
|
+
* becoming `prefer-5g` on a marginal cell is the exact substitution
|
|
45
|
+
* `five-g-preference.ts` refuses, and it is refused here for the same reason.
|
|
46
|
+
*/
|
|
47
|
+
export function matchAdvertisedCombination(supported, selection) {
|
|
48
|
+
return supported.combinations.find((each) => sameSelection(selectionOf(each), selection));
|
|
49
|
+
}
|
|
50
|
+
/** The masks `SetCurrentModes` needs, or the name that could not be placed. */
|
|
51
|
+
export function encodeModeSelection(selection) {
|
|
52
|
+
const allowed = encodeModeNames(selection.allowed);
|
|
53
|
+
if (!allowed.ok)
|
|
54
|
+
return allowed;
|
|
55
|
+
const preferred = encodeModeNames(selection.preferred === MODE_NONE ? [] : [selection.preferred]);
|
|
56
|
+
if (!preferred.ok)
|
|
57
|
+
return preferred;
|
|
58
|
+
return { ok: true, allowedMask: allowed.mask, preferredMask: preferred.mask };
|
|
59
|
+
}
|
|
60
|
+
export const MODE_WRITE_OPERATION_ID = 'modemmanager.mode-combination';
|
|
61
|
+
/**
|
|
62
|
+
* The live mode-write descriptor.
|
|
63
|
+
*
|
|
64
|
+
* `constraints` is an `allowed-values` list built from the modem's OWN catalog, so a
|
|
65
|
+
* combination carrying `preferred: 'none'` reaches a consumer through the descriptor
|
|
66
|
+
* exactly as the modem stated it. Readback is REQUIRED: `SetCurrentModes` returning
|
|
67
|
+
* without an error only proves the daemon accepted the call, and an
|
|
68
|
+
* accepted-but-ignored mode write is indistinguishable from success at the call site.
|
|
69
|
+
*/
|
|
70
|
+
export function buildModeWriteDescriptor(input) {
|
|
71
|
+
const values = input.truth.supported.combinations.map(selectionOf);
|
|
72
|
+
const refusal = !input.writeSupported
|
|
73
|
+
? 'mode-write-unsupported'
|
|
74
|
+
: values.length === 0
|
|
75
|
+
? 'no-advertised-mode-combinations'
|
|
76
|
+
: undefined;
|
|
77
|
+
return defineOperationDescriptor({
|
|
78
|
+
id: MODE_WRITE_OPERATION_ID,
|
|
79
|
+
support: {
|
|
80
|
+
read: { supported: true },
|
|
81
|
+
write: input.writeSupported
|
|
82
|
+
? { supported: true }
|
|
83
|
+
: { supported: false, reason: 'mode-write-unsupported' },
|
|
84
|
+
},
|
|
85
|
+
authority: 'provider',
|
|
86
|
+
provider: input.provider,
|
|
87
|
+
constraints: { kind: 'allowed-values', values },
|
|
88
|
+
livePreconditions: [
|
|
89
|
+
'modem-present',
|
|
90
|
+
'runtime-interface-present',
|
|
91
|
+
'mode-combination-advertised',
|
|
92
|
+
],
|
|
93
|
+
availability: refusal === undefined ? { state: 'available' } : { state: 'refused', reason: refusal },
|
|
94
|
+
mutationImpact: 'disruptive',
|
|
95
|
+
retryClass: 'never',
|
|
96
|
+
readback: {
|
|
97
|
+
required: true,
|
|
98
|
+
reason: 'mode-write-readback',
|
|
99
|
+
matches: (selection, observed) => observed.current.state === 'reported' &&
|
|
100
|
+
sameSelection(selectionOf(observed.current.combination), selection),
|
|
101
|
+
},
|
|
102
|
+
rollback: { required: false },
|
|
103
|
+
journal: { required: true, reason: 'disruptive-radio-write' },
|
|
104
|
+
admission: { required: true, reason: 'provider-mutation' },
|
|
105
|
+
evidence: { profiles: [input.profile], firmware: [] },
|
|
106
|
+
confidence: 'high',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/** True when the modem advertises at least one combination stating no preference. */
|
|
110
|
+
export function advertisesNoPreference(truth) {
|
|
111
|
+
return truth.supported.combinations.some(statesNoPreference);
|
|
112
|
+
}
|
package/dist/redact.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** The marker substituted for every redacted value. */
|
|
2
|
+
export declare const REDACTED = "[redacted]";
|
|
3
|
+
/** Whole-key SMS-content test, case- and separator-insensitive. */
|
|
4
|
+
export declare function isSmsSensitiveKey(key: string): boolean;
|
|
5
|
+
/** Whole-key USSD-content test, case- and separator-insensitive. */
|
|
6
|
+
export declare function isUssdSensitiveKey(key: string): boolean;
|
|
7
|
+
/** Whole-key own-number test, case-, separator- and dot-insensitive. */
|
|
8
|
+
export declare function isOwnNumberSensitiveKey(key: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Return a deep copy of `value` with every sensitive field redacted. Plain objects
|
|
11
|
+
* and arrays are walked recursively; all other values (primitives, and opaque
|
|
12
|
+
* objects like `Date` / `Set` / `Map`) are returned unchanged. The input is never
|
|
13
|
+
* mutated.
|
|
14
|
+
*/
|
|
15
|
+
export declare function redact(value: unknown): unknown;
|