@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,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every way a USSD verb can fail to do what the operator asked.
|
|
3
|
+
*
|
|
4
|
+
* unsupported — this modem exposes no USSD interface at all.
|
|
5
|
+
* lte-only-unsupported — the modem is registered PS-only (LTE/5G with no CS
|
|
6
|
+
* domain) and the network refused. NOT a device fault.
|
|
7
|
+
* carrier-rejected — the network refused for some other reason.
|
|
8
|
+
* not-registered — there is no network to carry the session.
|
|
9
|
+
* session-busy — a session is already open (locally or network-side).
|
|
10
|
+
* no-session — respond/cancel with nothing open.
|
|
11
|
+
* invalid-state — the verb is wrong for the state the session is in.
|
|
12
|
+
* timeout — the bounded wait elapsed with no answer.
|
|
13
|
+
* transport-failed — the bus call itself failed; the modem never answered.
|
|
14
|
+
*/
|
|
15
|
+
export declare const USSD_REFUSAL_REASONS: readonly ["unsupported", "lte-only-unsupported", "carrier-rejected", "not-registered", "session-busy", "no-session", "invalid-state", "timeout", "transport-failed"];
|
|
16
|
+
export type UssdRefusalReason = (typeof USSD_REFUSAL_REASONS)[number];
|
|
17
|
+
/**
|
|
18
|
+
* What the modem is registered on, as far as anyone has looked.
|
|
19
|
+
*
|
|
20
|
+
* `csDomain` is the load-bearing field: `false` means the modem is attached with
|
|
21
|
+
* NO circuit-switched domain available (LTE/5G-SA without CSFB), which is the
|
|
22
|
+
* registration on which a plain USSD refusal is a carrier-policy statement rather
|
|
23
|
+
* than a device one. `undefined` means nobody read it, and is never treated as
|
|
24
|
+
* `false`.
|
|
25
|
+
*/
|
|
26
|
+
export interface UssdRegistrationFacts {
|
|
27
|
+
readonly registered: boolean;
|
|
28
|
+
/** Is a circuit-switched domain available on this registration? */
|
|
29
|
+
readonly csDomain?: boolean;
|
|
30
|
+
/** MM access technologies currently in use, lowercased (`lte`, `5gnr`, …). */
|
|
31
|
+
readonly accessTechnologies?: readonly string[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* True when the modem's registration positively cannot carry a circuit-switched
|
|
35
|
+
* service. Requires evidence in BOTH directions: something must say the CS domain
|
|
36
|
+
* is absent, and every technology in use must be a packet-only one. A modem that
|
|
37
|
+
* reported no technologies at all answers `false` — an empty list is a statement
|
|
38
|
+
* about the read.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isPacketSwitchedOnly(facts: UssdRegistrationFacts): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Classify a failed USSD call. Pure, total, never throws.
|
|
43
|
+
*
|
|
44
|
+
* Order is deliberate: the D-Bus error NAME is the strongest signal and is read
|
|
45
|
+
* first; the message text is consulted only for the names MM overloads; anything
|
|
46
|
+
* unrecognised stays `transport-failed`, which is the honest answer for a failure
|
|
47
|
+
* whose origin cannot be attributed to the network.
|
|
48
|
+
*
|
|
49
|
+
* The PS-only promotion runs LAST and applies to exactly the two reasons that are
|
|
50
|
+
* ambiguous between a device limit and a carrier policy — a `not-registered` or a
|
|
51
|
+
* bus failure is neither, and is left alone.
|
|
52
|
+
*/
|
|
53
|
+
export declare function classifyUssdFailure(error: unknown, registration?: UssdRegistrationFacts): UssdRefusalReason;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Why a USSD verb did not succeed — one typed vocabulary, and the classifier that
|
|
2
|
+
// maps ModemManager's own failure surface onto it.
|
|
3
|
+
//
|
|
4
|
+
// The point of typing these is that every member names a DIFFERENT thing the
|
|
5
|
+
// operator can do, and the one this module exists for is the honest reporting of
|
|
6
|
+
// a carrier that will not carry USSD at all on the registration the modem has.
|
|
7
|
+
//
|
|
8
|
+
// THE LTE-ONLY CASE, stated plainly. USSD is a circuit-switched supplementary
|
|
9
|
+
// service. A modem registered PS-only — LTE or 5G-SA with no CS domain and no
|
|
10
|
+
// CSFB — can only carry it if the operator deploys USSI (USSD over IMS, 3GPP TS
|
|
11
|
+
// 24.390); many do not, and the modem/network then answers a generic
|
|
12
|
+
// unsupported/failed error that is indistinguishable, on its face, from "this
|
|
13
|
+
// modem has no USSD interface". Reporting that as a device limitation would send
|
|
14
|
+
// an operator hunting for a firmware fix for a network policy, so the classifier
|
|
15
|
+
// takes the modem's REGISTRATION alongside the error and separates the two.
|
|
16
|
+
//
|
|
17
|
+
// Nothing here guesses: `lte-only-unsupported` is claimed ONLY when the
|
|
18
|
+
// registration is positively known to be PS-only. An unknown registration keeps
|
|
19
|
+
// the generic reason, because "we did not look" is not evidence.
|
|
20
|
+
/**
|
|
21
|
+
* Every way a USSD verb can fail to do what the operator asked.
|
|
22
|
+
*
|
|
23
|
+
* unsupported — this modem exposes no USSD interface at all.
|
|
24
|
+
* lte-only-unsupported — the modem is registered PS-only (LTE/5G with no CS
|
|
25
|
+
* domain) and the network refused. NOT a device fault.
|
|
26
|
+
* carrier-rejected — the network refused for some other reason.
|
|
27
|
+
* not-registered — there is no network to carry the session.
|
|
28
|
+
* session-busy — a session is already open (locally or network-side).
|
|
29
|
+
* no-session — respond/cancel with nothing open.
|
|
30
|
+
* invalid-state — the verb is wrong for the state the session is in.
|
|
31
|
+
* timeout — the bounded wait elapsed with no answer.
|
|
32
|
+
* transport-failed — the bus call itself failed; the modem never answered.
|
|
33
|
+
*/
|
|
34
|
+
export const USSD_REFUSAL_REASONS = [
|
|
35
|
+
'unsupported',
|
|
36
|
+
'lte-only-unsupported',
|
|
37
|
+
'carrier-rejected',
|
|
38
|
+
'not-registered',
|
|
39
|
+
'session-busy',
|
|
40
|
+
'no-session',
|
|
41
|
+
'invalid-state',
|
|
42
|
+
'timeout',
|
|
43
|
+
'transport-failed',
|
|
44
|
+
];
|
|
45
|
+
/** Access technologies that carry no circuit-switched domain of their own. */
|
|
46
|
+
const PACKET_ONLY_RATS = new Set([
|
|
47
|
+
'lte',
|
|
48
|
+
'5gnr',
|
|
49
|
+
'lte-cat-m',
|
|
50
|
+
'lte-nb-iot',
|
|
51
|
+
]);
|
|
52
|
+
/**
|
|
53
|
+
* True when the modem's registration positively cannot carry a circuit-switched
|
|
54
|
+
* service. Requires evidence in BOTH directions: something must say the CS domain
|
|
55
|
+
* is absent, and every technology in use must be a packet-only one. A modem that
|
|
56
|
+
* reported no technologies at all answers `false` — an empty list is a statement
|
|
57
|
+
* about the read.
|
|
58
|
+
*/
|
|
59
|
+
export function isPacketSwitchedOnly(facts) {
|
|
60
|
+
if (!facts.registered) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (facts.csDomain !== false) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const rats = facts.accessTechnologies;
|
|
67
|
+
if (rats === undefined || rats.length === 0) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
return rats.every((rat) => PACKET_ONLY_RATS.has(rat.toLowerCase()));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* ModemManager D-Bus error names this classifier recognises, matched on the
|
|
74
|
+
* SUFFIX after the last dot so a future `...Error.Core.Unsupported` regrouping
|
|
75
|
+
* does not silently fall through to `transport-failed`.
|
|
76
|
+
*/
|
|
77
|
+
const ERROR_SUFFIX_REASONS = new Map([
|
|
78
|
+
['Unsupported', 'unsupported'],
|
|
79
|
+
['NotSupported', 'unsupported'],
|
|
80
|
+
['InProgress', 'session-busy'],
|
|
81
|
+
['NoNetwork', 'not-registered'],
|
|
82
|
+
['NotRegistered', 'not-registered'],
|
|
83
|
+
['Timeout', 'timeout'],
|
|
84
|
+
['Aborted', 'carrier-rejected'],
|
|
85
|
+
['Failed', 'carrier-rejected'],
|
|
86
|
+
]);
|
|
87
|
+
/**
|
|
88
|
+
* Message fragments that identify a refusal no error NAME distinguishes. MM
|
|
89
|
+
* folds several modem answers into `Core.Failed`, so the text is the only signal
|
|
90
|
+
* separating "the network said no" from "the bus call broke".
|
|
91
|
+
*/
|
|
92
|
+
const MESSAGE_REASONS = [
|
|
93
|
+
[/ussd.*(?:not supported|unsupported)/i, 'unsupported'],
|
|
94
|
+
[/(?:session|operation) (?:already )?(?:active|in progress)/i, 'session-busy'],
|
|
95
|
+
[/no (?:active )?ussd session/i, 'no-session'],
|
|
96
|
+
[/not registered|no network/i, 'not-registered'],
|
|
97
|
+
[/timed? ?out/i, 'timeout'],
|
|
98
|
+
[/rejected|refused|denied|network error/i, 'carrier-rejected'],
|
|
99
|
+
];
|
|
100
|
+
function errorName(error) {
|
|
101
|
+
if (typeof error !== 'object' || error === null) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const name = error.dbusName;
|
|
105
|
+
if (typeof name === 'string' && name.includes('.')) {
|
|
106
|
+
return name;
|
|
107
|
+
}
|
|
108
|
+
const fallback = error.name;
|
|
109
|
+
return typeof fallback === 'string' && fallback.includes('.') ? fallback : undefined;
|
|
110
|
+
}
|
|
111
|
+
function errorMessage(error) {
|
|
112
|
+
if (error instanceof Error) {
|
|
113
|
+
return error.message;
|
|
114
|
+
}
|
|
115
|
+
if (typeof error === 'object' && error !== null) {
|
|
116
|
+
const message = error.message;
|
|
117
|
+
if (typeof message === 'string') {
|
|
118
|
+
return message;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return String(error);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Classify a failed USSD call. Pure, total, never throws.
|
|
125
|
+
*
|
|
126
|
+
* Order is deliberate: the D-Bus error NAME is the strongest signal and is read
|
|
127
|
+
* first; the message text is consulted only for the names MM overloads; anything
|
|
128
|
+
* unrecognised stays `transport-failed`, which is the honest answer for a failure
|
|
129
|
+
* whose origin cannot be attributed to the network.
|
|
130
|
+
*
|
|
131
|
+
* The PS-only promotion runs LAST and applies to exactly the two reasons that are
|
|
132
|
+
* ambiguous between a device limit and a carrier policy — a `not-registered` or a
|
|
133
|
+
* bus failure is neither, and is left alone.
|
|
134
|
+
*/
|
|
135
|
+
export function classifyUssdFailure(error, registration = { registered: false }) {
|
|
136
|
+
const name = errorName(error);
|
|
137
|
+
const suffix = name?.slice(name.lastIndexOf('.') + 1);
|
|
138
|
+
const message = errorMessage(error);
|
|
139
|
+
let reason = suffix === undefined ? undefined : ERROR_SUFFIX_REASONS.get(suffix);
|
|
140
|
+
// `Core.Failed` is MM's catch-all, so its message is worth more than its name.
|
|
141
|
+
if (reason === undefined || reason === 'carrier-rejected') {
|
|
142
|
+
for (const [pattern, mapped] of MESSAGE_REASONS) {
|
|
143
|
+
if (pattern.test(message)) {
|
|
144
|
+
reason = mapped;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (reason === undefined) {
|
|
150
|
+
return 'transport-failed';
|
|
151
|
+
}
|
|
152
|
+
const ambiguous = reason === 'unsupported' || reason === 'carrier-rejected';
|
|
153
|
+
return ambiguous && isPacketSwitchedOnly(registration) ? 'lte-only-unsupported' : reason;
|
|
154
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type DecodedManagedObjects } from '../backend/managed-objects.js';
|
|
2
|
+
import type { DbusTransport } from '../transport/index.js';
|
|
3
|
+
import type { UssdRegistrationFacts } from './refusal.js';
|
|
4
|
+
/** Decode `Modem.AccessTechnologies` into the technology names in use. */
|
|
5
|
+
export declare function decodeAccessTechnologies(mask: number): readonly string[];
|
|
6
|
+
/**
|
|
7
|
+
* Derive the registration facts from an already-fetched managed-objects tree.
|
|
8
|
+
*
|
|
9
|
+
* Pure, so the derivation is testable against a fixture tree without a bus — the
|
|
10
|
+
* `sim-unlock.ts` split between "read the tree" and "decide from the tree".
|
|
11
|
+
*/
|
|
12
|
+
export declare function registrationFactsFromTree(tree: DecodedManagedObjects, modemPath: string): UssdRegistrationFacts;
|
|
13
|
+
/** The unread default — every field withheld, so nothing can be claimed from it. */
|
|
14
|
+
export declare const UNKNOWN_REGISTRATION: UssdRegistrationFacts;
|
|
15
|
+
/**
|
|
16
|
+
* Read the registration facts for one modem. Fail-soft: a tree we could not fetch
|
|
17
|
+
* yields {@link UNKNOWN_REGISTRATION}, which can only ever make the refusal LESS
|
|
18
|
+
* specific.
|
|
19
|
+
*/
|
|
20
|
+
export declare function readUssdRegistrationFacts(transport: DbusTransport, destination: string, modemPath: string): Promise<UssdRegistrationFacts>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Reading the registration facts the USSD refusal classifier needs.
|
|
2
|
+
//
|
|
3
|
+
// ModemManager publishes NO "is a circuit-switched domain available" property, so
|
|
4
|
+
// the fact has to be DERIVED — and the derivation is worth stating, because it is
|
|
5
|
+
// the difference between telling an operator their modem cannot do USSD and
|
|
6
|
+
// telling them their carrier will not carry it on this registration:
|
|
7
|
+
//
|
|
8
|
+
// * `Modem.AccessTechnologies` is a bitmask. When every bit in use is a
|
|
9
|
+
// packet-only radio (LTE, 5G-NR, LTE-M, NB-IoT) the modem is not camped on a
|
|
10
|
+
// circuit-switched radio at all.
|
|
11
|
+
// * `Modem3gpp.RegistrationState` still overrides that, because CS FALLBACK is
|
|
12
|
+
// exactly the case where an LTE-camped modem CAN reach the CS domain. MM has
|
|
13
|
+
// two states that say so outright — `HOME_CSFB_NOT_PREFERRED` (9) and
|
|
14
|
+
// `ROAMING_CSFB_NOT_PREFERRED` (10) — and a modem in either of them is
|
|
15
|
+
// reported CS-capable regardless of its radio.
|
|
16
|
+
//
|
|
17
|
+
// Both reads are best-effort and NEVER throw: a fact nobody could read is left
|
|
18
|
+
// `undefined`, which the classifier treats as "we did not look" rather than as a
|
|
19
|
+
// negative. That asymmetry is the whole safety property — an unread registration
|
|
20
|
+
// can only ever produce the generic refusal, never the more specific claim.
|
|
21
|
+
import { MODEM_IFACE, MODEM3GPP_IFACE } from '../backend/constants.js';
|
|
22
|
+
import { fetchManagedObjects, findInterface, numberProp, } from '../backend/managed-objects.js';
|
|
23
|
+
/** `MMModemAccessTechnology` bits this module names. */
|
|
24
|
+
const ACCESS_TECHNOLOGY_BITS = [
|
|
25
|
+
[1 << 1, 'gsm'],
|
|
26
|
+
[1 << 2, 'gsm-compact'],
|
|
27
|
+
[1 << 3, 'gprs'],
|
|
28
|
+
[1 << 4, 'edge'],
|
|
29
|
+
[1 << 5, 'umts'],
|
|
30
|
+
[1 << 6, 'hsdpa'],
|
|
31
|
+
[1 << 7, 'hsupa'],
|
|
32
|
+
[1 << 8, 'hspa'],
|
|
33
|
+
[1 << 9, 'hspa-plus'],
|
|
34
|
+
[1 << 10, '1xrtt'],
|
|
35
|
+
[1 << 11, 'evdo0'],
|
|
36
|
+
[1 << 12, 'evdoa'],
|
|
37
|
+
[1 << 13, 'evdob'],
|
|
38
|
+
[1 << 14, 'lte'],
|
|
39
|
+
[1 << 15, '5gnr'],
|
|
40
|
+
[1 << 16, 'lte-cat-m'],
|
|
41
|
+
[1 << 17, 'lte-nb-iot'],
|
|
42
|
+
];
|
|
43
|
+
/** `MMModem3gppRegistrationState` values that mean the modem is on a network. */
|
|
44
|
+
const REGISTERED_STATES = new Set([1, 5, 6, 7, 9, 10]);
|
|
45
|
+
/** …and the two that positively advertise a circuit-switched fallback. */
|
|
46
|
+
const CSFB_STATES = new Set([9, 10]);
|
|
47
|
+
/** Decode `Modem.AccessTechnologies` into the technology names in use. */
|
|
48
|
+
export function decodeAccessTechnologies(mask) {
|
|
49
|
+
const names = [];
|
|
50
|
+
for (const [bit, name] of ACCESS_TECHNOLOGY_BITS) {
|
|
51
|
+
if ((mask & bit) !== 0) {
|
|
52
|
+
names.push(name);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return names;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Derive the registration facts from an already-fetched managed-objects tree.
|
|
59
|
+
*
|
|
60
|
+
* Pure, so the derivation is testable against a fixture tree without a bus — the
|
|
61
|
+
* `sim-unlock.ts` split between "read the tree" and "decide from the tree".
|
|
62
|
+
*/
|
|
63
|
+
export function registrationFactsFromTree(tree, modemPath) {
|
|
64
|
+
const modem = findInterface(tree, modemPath, MODEM_IFACE);
|
|
65
|
+
const threeGpp = findInterface(tree, modemPath, MODEM3GPP_IFACE);
|
|
66
|
+
const registrationState = numberProp(threeGpp, 'RegistrationState');
|
|
67
|
+
const accessMask = numberProp(modem, 'AccessTechnologies');
|
|
68
|
+
const registered = registrationState !== undefined && REGISTERED_STATES.has(registrationState);
|
|
69
|
+
const technologies = accessMask === undefined ? undefined : decodeAccessTechnologies(accessMask);
|
|
70
|
+
// A CSFB registration is CS-capable outright. Otherwise the domain is only
|
|
71
|
+
// declared ABSENT when the radios in use were actually read and are all
|
|
72
|
+
// packet-only; an unread mask leaves the field undefined on purpose.
|
|
73
|
+
let csDomain;
|
|
74
|
+
if (registrationState !== undefined && CSFB_STATES.has(registrationState)) {
|
|
75
|
+
csDomain = true;
|
|
76
|
+
}
|
|
77
|
+
else if (technologies !== undefined && technologies.length > 0) {
|
|
78
|
+
csDomain = technologies.some((rat) => rat !== 'lte' && rat !== '5gnr' && rat !== 'lte-cat-m' && rat !== 'lte-nb-iot');
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
registered,
|
|
82
|
+
...(csDomain === undefined ? {} : { csDomain }),
|
|
83
|
+
...(technologies === undefined ? {} : { accessTechnologies: technologies }),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** The unread default — every field withheld, so nothing can be claimed from it. */
|
|
87
|
+
export const UNKNOWN_REGISTRATION = { registered: false };
|
|
88
|
+
/**
|
|
89
|
+
* Read the registration facts for one modem. Fail-soft: a tree we could not fetch
|
|
90
|
+
* yields {@link UNKNOWN_REGISTRATION}, which can only ever make the refusal LESS
|
|
91
|
+
* specific.
|
|
92
|
+
*/
|
|
93
|
+
export async function readUssdRegistrationFacts(transport, destination, modemPath) {
|
|
94
|
+
try {
|
|
95
|
+
const tree = await fetchManagedObjects(transport, destination);
|
|
96
|
+
return registrationFactsFromTree(tree, modemPath);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return UNKNOWN_REGISTRATION;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { UssdRefusalReason } from './refusal.js';
|
|
2
|
+
/**
|
|
3
|
+
* Session states.
|
|
4
|
+
*
|
|
5
|
+
* Three of them (`idle`, `active`, `awaiting-reply`) mirror MM's own
|
|
6
|
+
* `MMModem3gppUssdSessionState`; the rest are LOCAL in-flight states, because MM
|
|
7
|
+
* has no state for "we dispatched a call and the reply has not landed". Without
|
|
8
|
+
* them a second `initiate` racing the first would be judged against `idle` and
|
|
9
|
+
* allowed through, which is exactly the double-open the network answers busy.
|
|
10
|
+
*/
|
|
11
|
+
export declare const USSD_SESSION_STATES: readonly ["idle", "initiating", "active", "awaiting-reply", "responding", "cancelling", "closed"];
|
|
12
|
+
export type UssdSessionState = (typeof USSD_SESSION_STATES)[number];
|
|
13
|
+
/** How a session that reached `closed` got there. */
|
|
14
|
+
export declare const USSD_SESSION_OUTCOMES: readonly ["completed", "cancelled", "timed-out", "failed"];
|
|
15
|
+
export type UssdSessionOutcome = (typeof USSD_SESSION_OUTCOMES)[number];
|
|
16
|
+
/** MM's post-call session state, decoded. */
|
|
17
|
+
export type UssdRepliedState = 'awaiting-reply' | 'active' | 'released';
|
|
18
|
+
export type UssdSessionEvent =
|
|
19
|
+
/** The operator asked to open a session. */
|
|
20
|
+
{
|
|
21
|
+
readonly kind: 'initiate';
|
|
22
|
+
}
|
|
23
|
+
/** The operator answered a network prompt. */
|
|
24
|
+
| {
|
|
25
|
+
readonly kind: 'respond';
|
|
26
|
+
}
|
|
27
|
+
/** The operator asked to close the session. */
|
|
28
|
+
| {
|
|
29
|
+
readonly kind: 'cancel';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The network answered an `Initiate`/`Respond`. `sessionState` is MM's own
|
|
33
|
+
* post-call `Modem3gpp.Ussd.State`, decoded: the network either wants an
|
|
34
|
+
* answer, is holding the session open with nothing pending, or released it.
|
|
35
|
+
*/
|
|
36
|
+
| {
|
|
37
|
+
readonly kind: 'replied';
|
|
38
|
+
readonly sessionState: UssdRepliedState;
|
|
39
|
+
}
|
|
40
|
+
/** A `Cancel` was confirmed by the modem. */
|
|
41
|
+
| {
|
|
42
|
+
readonly kind: 'cancelled';
|
|
43
|
+
}
|
|
44
|
+
/** The network released the session without our asking (notification path). */
|
|
45
|
+
| {
|
|
46
|
+
readonly kind: 'network-released';
|
|
47
|
+
}
|
|
48
|
+
/** The bounded wait elapsed with no answer. */
|
|
49
|
+
| {
|
|
50
|
+
readonly kind: 'timeout';
|
|
51
|
+
}
|
|
52
|
+
/** The call failed. The reason is carried onto the terminal state verbatim. */
|
|
53
|
+
| {
|
|
54
|
+
readonly kind: 'failed';
|
|
55
|
+
readonly reason: UssdRefusalReason;
|
|
56
|
+
};
|
|
57
|
+
export interface UssdSessionSnapshot {
|
|
58
|
+
readonly state: UssdSessionState;
|
|
59
|
+
/** Present only at `closed`. */
|
|
60
|
+
readonly outcome?: UssdSessionOutcome;
|
|
61
|
+
/** Present only at `closed` with outcome `failed`. */
|
|
62
|
+
readonly refusal?: UssdRefusalReason;
|
|
63
|
+
}
|
|
64
|
+
export type UssdTransition = {
|
|
65
|
+
readonly ok: true;
|
|
66
|
+
readonly snapshot: UssdSessionSnapshot;
|
|
67
|
+
}
|
|
68
|
+
/** The verb is not legal in this state, and the machine did NOT move. */
|
|
69
|
+
| {
|
|
70
|
+
readonly ok: false;
|
|
71
|
+
readonly refusal: UssdRefusalReason;
|
|
72
|
+
};
|
|
73
|
+
export declare const IDLE_SESSION: UssdSessionSnapshot;
|
|
74
|
+
/**
|
|
75
|
+
* Apply one event. TOTAL: every (state, event) pair has an answer, and an answer
|
|
76
|
+
* is either a new snapshot or a typed refusal that leaves the machine untouched.
|
|
77
|
+
*
|
|
78
|
+
* A `closed` machine accepts NOTHING — not even another `cancel`. Re-opening a
|
|
79
|
+
* terminal session would hide the fact that the previous one ended, and the
|
|
80
|
+
* cost of a fresh machine is one object.
|
|
81
|
+
*/
|
|
82
|
+
export declare function reduceUssdSession(snapshot: UssdSessionSnapshot, event: UssdSessionEvent): UssdTransition;
|
|
83
|
+
/** True while the session still holds a network dialogue open. */
|
|
84
|
+
export declare function isUssdSessionOpen(snapshot: UssdSessionSnapshot): boolean;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// The USSD session state machine — pure, total, and the only place a session's
|
|
2
|
+
// legality is decided.
|
|
3
|
+
//
|
|
4
|
+
// USSD is a SESSION protocol, not a request/response one: `Initiate` opens a
|
|
5
|
+
// dialogue the network may keep open pending a `Respond`, and a session that is
|
|
6
|
+
// neither responded to nor cancelled stays open on the NETWORK side, consuming a
|
|
7
|
+
// scarce per-subscriber slot and blocking the next `Initiate` with a busy error.
|
|
8
|
+
// So "which verb is legal right now" is a real question with a real wrong answer,
|
|
9
|
+
// and answering it inside the D-Bus adapter would make it untestable without a
|
|
10
|
+
// bus. It lives here instead, as data.
|
|
11
|
+
//
|
|
12
|
+
// Everything the machine can be told is an EVENT and every answer is a
|
|
13
|
+
// TRANSITION — an illegal verb is REFUSED with a typed reason, never thrown and
|
|
14
|
+
// never silently ignored. A refusal at an RPC boundary must name what the caller
|
|
15
|
+
// can do about it; a throw becomes an opaque failure and a silent no-op becomes a
|
|
16
|
+
// UI that spins forever.
|
|
17
|
+
//
|
|
18
|
+
// The machine carries NO carrier text. The reply an operator sees is threaded by
|
|
19
|
+
// the adapter and redacted at every log boundary (`../redact`); keeping it out of
|
|
20
|
+
// the state entirely means a state snapshot can never leak one.
|
|
21
|
+
/**
|
|
22
|
+
* Session states.
|
|
23
|
+
*
|
|
24
|
+
* Three of them (`idle`, `active`, `awaiting-reply`) mirror MM's own
|
|
25
|
+
* `MMModem3gppUssdSessionState`; the rest are LOCAL in-flight states, because MM
|
|
26
|
+
* has no state for "we dispatched a call and the reply has not landed". Without
|
|
27
|
+
* them a second `initiate` racing the first would be judged against `idle` and
|
|
28
|
+
* allowed through, which is exactly the double-open the network answers busy.
|
|
29
|
+
*/
|
|
30
|
+
export const USSD_SESSION_STATES = [
|
|
31
|
+
/** No session. MM `IDLE`. */
|
|
32
|
+
'idle',
|
|
33
|
+
/** `Initiate` dispatched, reply outstanding. Local. */
|
|
34
|
+
'initiating',
|
|
35
|
+
/** Network answered and the session is open with nothing pending. MM `ACTIVE`. */
|
|
36
|
+
'active',
|
|
37
|
+
/** Network asked a question; a `Respond` is required. MM `USER_RESPONSE`. */
|
|
38
|
+
'awaiting-reply',
|
|
39
|
+
/** `Respond` dispatched, reply outstanding. Local. */
|
|
40
|
+
'responding',
|
|
41
|
+
/** `Cancel` dispatched, confirmation outstanding. Local. */
|
|
42
|
+
'cancelling',
|
|
43
|
+
/** Terminal for this session object. A new session starts from a new machine. */
|
|
44
|
+
'closed',
|
|
45
|
+
];
|
|
46
|
+
/** How a session that reached `closed` got there. */
|
|
47
|
+
export const USSD_SESSION_OUTCOMES = [
|
|
48
|
+
/** The network completed the dialogue and released the session. */
|
|
49
|
+
'completed',
|
|
50
|
+
/** The operator cancelled it. */
|
|
51
|
+
'cancelled',
|
|
52
|
+
/** No answer within the bound; the machine closed it locally. */
|
|
53
|
+
'timed-out',
|
|
54
|
+
/** The network or the modem refused. `refusal` names which. */
|
|
55
|
+
'failed',
|
|
56
|
+
];
|
|
57
|
+
export const IDLE_SESSION = { state: 'idle' };
|
|
58
|
+
/** States in which an operator verb may be dispatched at all. */
|
|
59
|
+
const ACCEPTS_INITIATE = new Set(['idle']);
|
|
60
|
+
const ACCEPTS_RESPOND = new Set([
|
|
61
|
+
'awaiting-reply',
|
|
62
|
+
]);
|
|
63
|
+
const ACCEPTS_CANCEL = new Set([
|
|
64
|
+
'initiating',
|
|
65
|
+
'active',
|
|
66
|
+
'awaiting-reply',
|
|
67
|
+
'responding',
|
|
68
|
+
]);
|
|
69
|
+
/** States with a call in flight — the only ones a network answer may land on. */
|
|
70
|
+
const IN_FLIGHT = new Set([
|
|
71
|
+
'initiating',
|
|
72
|
+
'responding',
|
|
73
|
+
]);
|
|
74
|
+
function open(state) {
|
|
75
|
+
return { ok: true, snapshot: { state } };
|
|
76
|
+
}
|
|
77
|
+
function close(outcome, refusal) {
|
|
78
|
+
return {
|
|
79
|
+
ok: true,
|
|
80
|
+
snapshot: { state: 'closed', outcome, ...(refusal === undefined ? {} : { refusal }) },
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function refuse(refusal) {
|
|
84
|
+
return { ok: false, refusal };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Apply one event. TOTAL: every (state, event) pair has an answer, and an answer
|
|
88
|
+
* is either a new snapshot or a typed refusal that leaves the machine untouched.
|
|
89
|
+
*
|
|
90
|
+
* A `closed` machine accepts NOTHING — not even another `cancel`. Re-opening a
|
|
91
|
+
* terminal session would hide the fact that the previous one ended, and the
|
|
92
|
+
* cost of a fresh machine is one object.
|
|
93
|
+
*/
|
|
94
|
+
export function reduceUssdSession(snapshot, event) {
|
|
95
|
+
const state = snapshot.state;
|
|
96
|
+
if (state === 'closed') {
|
|
97
|
+
return refuse('no-session');
|
|
98
|
+
}
|
|
99
|
+
// Every event except `initiate` describes something happening TO a session,
|
|
100
|
+
// and an idle machine has none for them to happen to.
|
|
101
|
+
const sessionOpen = state !== 'idle';
|
|
102
|
+
switch (event.kind) {
|
|
103
|
+
case 'initiate':
|
|
104
|
+
// A session already in flight or open is the busy case the network
|
|
105
|
+
// itself would answer — refused locally so no second dialogue is opened.
|
|
106
|
+
return ACCEPTS_INITIATE.has(state) ? open('initiating') : refuse('session-busy');
|
|
107
|
+
case 'respond':
|
|
108
|
+
// Responding to a session that never asked a question is not a busy
|
|
109
|
+
// device; it is the wrong verb, and `invalid-state` says so.
|
|
110
|
+
return ACCEPTS_RESPOND.has(state) ? open('responding') : refuse('invalid-state');
|
|
111
|
+
case 'cancel':
|
|
112
|
+
// The two refusals are different operator facts: nothing to close, vs a
|
|
113
|
+
// cancel that is already in flight.
|
|
114
|
+
if (ACCEPTS_CANCEL.has(state)) {
|
|
115
|
+
return open('cancelling');
|
|
116
|
+
}
|
|
117
|
+
return refuse(sessionOpen ? 'invalid-state' : 'no-session');
|
|
118
|
+
case 'replied':
|
|
119
|
+
// A network answer that lands on a state with no call in flight is
|
|
120
|
+
// evidence of a lost reply or a duplicate; it is refused rather than
|
|
121
|
+
// used to resurrect a session the machine already moved past.
|
|
122
|
+
if (!IN_FLIGHT.has(state)) {
|
|
123
|
+
return refuse('invalid-state');
|
|
124
|
+
}
|
|
125
|
+
// The three-way answer is the dialogue-vs-one-shot distinction, and
|
|
126
|
+
// collapsing `active` into `released` is what would leave a session MM
|
|
127
|
+
// still considers open dangling on the network side.
|
|
128
|
+
if (event.sessionState === 'released') {
|
|
129
|
+
return close('completed');
|
|
130
|
+
}
|
|
131
|
+
return open(event.sessionState);
|
|
132
|
+
case 'cancelled':
|
|
133
|
+
// Only a machine that asked to cancel may be closed by one, so a stray
|
|
134
|
+
// confirmation cannot tear down a live dialogue.
|
|
135
|
+
return state === 'cancelling' ? close('cancelled') : refuse('invalid-state');
|
|
136
|
+
case 'network-released':
|
|
137
|
+
// The network is authoritative about its own session, so this lands
|
|
138
|
+
// from anywhere the session is still open — including mid-call, where
|
|
139
|
+
// it is the honest end of a dialogue whose reply will not come. During
|
|
140
|
+
// a cancel it is reported as `cancelled`: the operator asked for the
|
|
141
|
+
// session to end and it ended.
|
|
142
|
+
if (!sessionOpen) {
|
|
143
|
+
return refuse('no-session');
|
|
144
|
+
}
|
|
145
|
+
return close(state === 'cancelling' ? 'cancelled' : 'completed');
|
|
146
|
+
case 'timeout':
|
|
147
|
+
// The bound closes rather than reverting: after an unanswered call the
|
|
148
|
+
// network's own view is unknown, and pretending we are back at `idle`
|
|
149
|
+
// would let the next `initiate` walk into a busy error with no
|
|
150
|
+
// explanation.
|
|
151
|
+
return sessionOpen ? close('timed-out') : refuse('no-session');
|
|
152
|
+
case 'failed':
|
|
153
|
+
return sessionOpen ? close('failed', event.reason) : refuse('no-session');
|
|
154
|
+
default: {
|
|
155
|
+
const unreachable = event;
|
|
156
|
+
return unreachable;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/** True while the session still holds a network dialogue open. */
|
|
161
|
+
export function isUssdSessionOpen(snapshot) {
|
|
162
|
+
return snapshot.state !== 'idle' && snapshot.state !== 'closed';
|
|
163
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ceralive/modem-control",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Cellular modem control for CeraLive — ModemManager D-Bus backend, NetworkManager adapter, desired-state reconciler, USB composition-mode model, data-usage sampler.",
|
|
6
6
|
"license": "AGPL-3.0",
|
|
@@ -13,14 +13,48 @@
|
|
|
13
13
|
"registry": "https://registry.npmjs.org/",
|
|
14
14
|
"access": "public"
|
|
15
15
|
},
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
16
19
|
"exports": {
|
|
17
|
-
".":
|
|
18
|
-
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./transport": {
|
|
25
|
+
"types": "./dist/transport/index.d.ts",
|
|
26
|
+
"import": "./dist/transport/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./domain": {
|
|
29
|
+
"types": "./dist/domain/index.d.ts",
|
|
30
|
+
"import": "./dist/domain/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./providers": {
|
|
33
|
+
"types": "./dist/providers/index.d.ts",
|
|
34
|
+
"import": "./dist/providers/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./capabilities": {
|
|
37
|
+
"types": "./dist/capability/index.d.ts",
|
|
38
|
+
"import": "./dist/capability/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./hardware": {
|
|
41
|
+
"types": "./dist/hardware/index.d.ts",
|
|
42
|
+
"import": "./dist/hardware/index.js"
|
|
43
|
+
},
|
|
44
|
+
"./testing": {
|
|
45
|
+
"types": "./dist/testing/index.d.ts",
|
|
46
|
+
"import": "./dist/testing/index.js"
|
|
47
|
+
},
|
|
48
|
+
"./package.json": "./package.json"
|
|
19
49
|
},
|
|
20
50
|
"files": [
|
|
21
|
-
"
|
|
51
|
+
"dist"
|
|
22
52
|
],
|
|
23
53
|
"scripts": {
|
|
54
|
+
"build": "bun run scripts/build.ts",
|
|
55
|
+
"prepack": "bun run build",
|
|
56
|
+
"verify:consumers": "bun run scripts/verify-consumers.ts",
|
|
57
|
+
"verify:tarball": "bun run scripts/assert-tarball-shape.ts",
|
|
24
58
|
"test": "bun test",
|
|
25
59
|
"typecheck": "tsc --noEmit"
|
|
26
60
|
},
|