@ceralive/modem-control 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +341 -0
- package/dist/backend/at-lease.d.ts +59 -0
- package/dist/backend/at-lease.js +118 -0
- package/dist/backend/cell-info.d.ts +46 -0
- package/dist/backend/cell-info.js +124 -0
- package/dist/backend/constants.d.ts +24 -0
- package/{src/backend/constants.ts → dist/backend/constants.js} +4 -9
- package/dist/backend/device-classifier.d.ts +46 -0
- package/dist/backend/device-classifier.js +258 -0
- package/dist/backend/enrichment.d.ts +28 -0
- package/dist/backend/enrichment.js +59 -0
- package/dist/backend/features.d.ts +61 -0
- package/dist/backend/features.js +109 -0
- package/dist/backend/identity-ladder.d.ts +57 -0
- package/dist/backend/identity-ladder.js +158 -0
- package/dist/backend/identity-registry.d.ts +51 -0
- package/dist/backend/identity-registry.js +101 -0
- package/dist/backend/index.d.ts +30 -0
- package/dist/backend/index.js +35 -0
- package/dist/backend/lifecycle-interlock.d.ts +25 -0
- package/dist/backend/lifecycle-interlock.js +18 -0
- package/dist/backend/managed-objects.d.ts +39 -0
- package/dist/backend/managed-objects.js +82 -0
- package/dist/backend/mapping.d.ts +11 -0
- package/dist/backend/mapping.js +148 -0
- package/dist/backend/mm-backend.d.ts +44 -0
- package/dist/backend/mm-backend.js +154 -0
- package/dist/backend/mm-location.d.ts +21 -0
- package/dist/backend/mm-location.js +237 -0
- package/dist/backend/mm-mutations.d.ts +42 -0
- package/dist/backend/mm-mutations.js +259 -0
- package/dist/backend/modem-actor.d.ts +41 -0
- package/dist/backend/modem-actor.js +78 -0
- package/dist/backend/nm-auto-apn.d.ts +54 -0
- package/dist/backend/nm-auto-apn.js +124 -0
- package/dist/backend/nm-gsm-fields.d.ts +23 -0
- package/dist/backend/nm-gsm-fields.js +107 -0
- package/dist/backend/nmcli-nm-port.d.ts +28 -0
- package/dist/backend/nmcli-nm-port.js +173 -0
- package/dist/backend/nmcli-runner.d.ts +24 -0
- package/dist/backend/nmcli-runner.js +35 -0
- package/dist/backend/observer.d.ts +37 -0
- package/dist/backend/observer.js +219 -0
- package/dist/backend/power-contract.d.ts +49 -0
- package/dist/backend/power-contract.js +34 -0
- package/dist/backend/recovery-attribution.d.ts +32 -0
- package/dist/backend/recovery-attribution.js +57 -0
- package/dist/backend/recovery-budget.d.ts +45 -0
- package/dist/backend/recovery-budget.js +44 -0
- package/dist/backend/recovery-ladder.d.ts +94 -0
- package/dist/backend/recovery-ladder.js +116 -0
- package/dist/backend/router-ethernet.d.ts +19 -0
- package/dist/backend/router-ethernet.js +66 -0
- package/dist/backend/row-store.d.ts +13 -0
- package/dist/backend/row-store.js +82 -0
- package/dist/backend/signal-setup.d.ts +30 -0
- package/dist/backend/signal-setup.js +92 -0
- package/dist/backend/sim-unlock.d.ts +13 -0
- package/dist/backend/sim-unlock.js +153 -0
- package/dist/backend/transition-preconditions.d.ts +101 -0
- package/dist/backend/transition-preconditions.js +132 -0
- package/dist/backend/usage/accounting.d.ts +39 -0
- package/dist/backend/usage/accounting.js +73 -0
- package/dist/backend/usage/billing-cycle.d.ts +11 -0
- package/dist/backend/usage/billing-cycle.js +39 -0
- package/dist/backend/usage/boot-id.d.ts +6 -0
- package/{src/backend/usage/boot-id.ts → dist/backend/usage/boot-id.js} +7 -7
- package/dist/backend/usage/index.d.ts +8 -0
- package/dist/backend/usage/index.js +11 -0
- package/dist/backend/usage/policy-store.d.ts +50 -0
- package/dist/backend/usage/policy-store.js +161 -0
- package/dist/backend/usage/policy-write.d.ts +65 -0
- package/dist/backend/usage/policy-write.js +112 -0
- package/dist/backend/usage/proc-net-dev.d.ts +18 -0
- package/{src/backend/usage/proc-net-dev.ts → dist/backend/usage/proc-net-dev.js} +39 -47
- package/dist/backend/usage/sampler.d.ts +75 -0
- package/dist/backend/usage/sampler.js +211 -0
- package/dist/backend/usage/store.d.ts +38 -0
- package/dist/backend/usage/store.js +126 -0
- package/dist/backend/usb-device-snapshot.d.ts +31 -0
- package/dist/backend/usb-device-snapshot.js +1 -0
- package/dist/backend/usb-enumerator.d.ts +21 -0
- package/dist/backend/usb-enumerator.js +153 -0
- package/dist/backend/usb-mode-transition.d.ts +29 -0
- package/dist/backend/usb-mode-transition.js +216 -0
- package/dist/band/band-names.d.ts +42 -0
- package/dist/band/band-names.js +150 -0
- package/dist/band/certification.d.ts +84 -0
- package/dist/band/certification.js +127 -0
- package/dist/band/certified-bands.json +4 -0
- package/dist/band/index.d.ts +2 -0
- package/dist/band/index.js +8 -0
- package/dist/capability/detect.d.ts +52 -0
- package/dist/capability/detect.js +86 -0
- package/dist/capability/five-g-preference.d.ts +104 -0
- package/dist/capability/five-g-preference.js +171 -0
- package/dist/capability/index.d.ts +3 -0
- package/dist/capability/index.js +10 -0
- package/dist/capability/support-claim.d.ts +39 -0
- package/dist/capability/support-claim.js +81 -0
- package/dist/domain/brand.d.ts +10 -0
- package/dist/domain/brand.js +21 -0
- package/dist/domain/errors.d.ts +32 -0
- package/dist/domain/errors.js +48 -0
- package/dist/domain/generation.d.ts +8 -0
- package/dist/domain/generation.js +12 -0
- package/dist/domain/guards.d.ts +6 -0
- package/dist/domain/guards.js +127 -0
- package/dist/domain/identity.d.ts +109 -0
- package/dist/domain/identity.js +86 -0
- package/dist/domain/index.d.ts +14 -0
- package/dist/domain/index.js +18 -0
- package/dist/domain/mm-enums.d.ts +12 -0
- package/dist/domain/mm-enums.js +139 -0
- package/dist/domain/modem-presentation.d.ts +10 -0
- package/dist/domain/modem-presentation.js +39 -0
- package/dist/domain/observation.d.ts +39 -0
- package/dist/domain/observation.js +4 -0
- package/dist/domain/operation.d.ts +118 -0
- package/dist/domain/operation.js +85 -0
- package/dist/domain/physical-identity.d.ts +43 -0
- package/dist/domain/physical-identity.js +113 -0
- package/{src/domain/policy.ts → dist/domain/policy.d.ts} +34 -69
- package/dist/domain/policy.js +38 -0
- package/dist/domain/shadow-divergence.d.ts +27 -0
- package/dist/domain/shadow-divergence.js +70 -0
- package/dist/domain/snapshot.d.ts +53 -0
- package/dist/domain/snapshot.js +75 -0
- package/{src/domain/state.ts → dist/domain/state.d.ts} +23 -122
- package/dist/domain/state.js +38 -0
- package/dist/fcc/coverage.d.ts +63 -0
- package/dist/fcc/coverage.js +102 -0
- package/dist/fcc/index.d.ts +3 -0
- package/dist/fcc/index.js +12 -0
- package/dist/fcc/policy-store.d.ts +40 -0
- package/dist/fcc/policy-store.js +146 -0
- package/dist/fcc/policy-write.d.ts +32 -0
- package/dist/fcc/policy-write.js +37 -0
- package/dist/hardware/hilink-protocol.d.ts +39 -0
- package/dist/hardware/hilink-protocol.js +58 -0
- package/dist/hardware/index.d.ts +3 -0
- package/dist/hardware/index.js +15 -0
- package/dist/hardware/router-parsers.d.ts +89 -0
- package/dist/hardware/router-parsers.js +234 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +27 -0
- package/dist/journal/codec.d.ts +44 -0
- package/dist/journal/codec.js +198 -0
- package/dist/journal/engine.d.ts +28 -0
- package/dist/journal/engine.js +68 -0
- package/dist/journal/entry.d.ts +74 -0
- package/dist/journal/entry.js +56 -0
- package/dist/journal/index.d.ts +6 -0
- package/dist/journal/index.js +6 -0
- package/dist/journal/legacy-ceraui.d.ts +73 -0
- package/dist/journal/legacy-ceraui.js +227 -0
- package/dist/journal/recovery.d.ts +58 -0
- package/dist/journal/recovery.js +117 -0
- package/dist/journal/store.d.ts +55 -0
- package/dist/journal/store.js +150 -0
- package/dist/location/fix-state.d.ts +53 -0
- package/dist/location/fix-state.js +75 -0
- package/dist/location/index.d.ts +2 -0
- package/dist/location/index.js +8 -0
- package/dist/location/nmea.d.ts +10 -0
- package/dist/location/nmea.js +89 -0
- package/dist/observations/envelope.d.ts +63 -0
- package/dist/observations/envelope.js +77 -0
- package/dist/observations/freshness.d.ts +28 -0
- package/dist/observations/freshness.js +76 -0
- package/dist/observations/index.d.ts +13 -0
- package/dist/observations/index.js +24 -0
- package/dist/observations/metric.d.ts +61 -0
- package/dist/observations/metric.js +73 -0
- package/dist/observations/model.d.ts +80 -0
- package/dist/observations/model.js +11 -0
- package/dist/observations/provenance.d.ts +94 -0
- package/dist/observations/provenance.js +67 -0
- package/dist/observations/raw.d.ts +42 -0
- package/dist/observations/raw.js +146 -0
- package/dist/observations/reading.d.ts +51 -0
- package/dist/observations/reading.js +65 -0
- package/dist/observations/sources/hilink.d.ts +10 -0
- package/dist/observations/sources/hilink.js +84 -0
- package/dist/observations/sources/modemmanager.d.ts +18 -0
- package/dist/observations/sources/modemmanager.js +239 -0
- package/dist/observations/sources/router-shared.d.ts +29 -0
- package/dist/observations/sources/router-shared.js +68 -0
- package/dist/observations/sources/ufi.d.ts +10 -0
- package/dist/observations/sources/ufi.js +111 -0
- package/dist/observations/sources/zte.d.ts +7 -0
- package/dist/observations/sources/zte.js +71 -0
- package/dist/observations/state-separation.d.ts +64 -0
- package/dist/observations/state-separation.js +52 -0
- package/dist/operation-ids.d.ts +2 -0
- package/dist/operation-ids.js +26 -0
- package/dist/operations/contracts.d.ts +72 -0
- package/dist/operations/contracts.js +1 -0
- package/dist/operations/index.d.ts +1 -0
- package/dist/operations/index.js +1 -0
- package/dist/operations/operation-engine.d.ts +16 -0
- package/dist/operations/operation-engine.js +194 -0
- package/dist/ports/index.d.ts +12 -0
- package/{src/ports/index.ts → dist/ports/index.js} +12 -8
- package/dist/ports/location.d.ts +87 -0
- package/dist/ports/location.js +36 -0
- package/dist/ports/modem-manager.d.ts +89 -0
- package/dist/ports/modem-manager.js +9 -0
- package/dist/ports/mutation-admission.d.ts +27 -0
- package/dist/ports/mutation-admission.js +9 -0
- package/dist/ports/network-manager.d.ts +68 -0
- package/dist/ports/network-manager.js +13 -0
- package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
- package/dist/ports/observation.js +7 -0
- package/dist/ports/ops.d.ts +44 -0
- package/dist/ports/ops.js +16 -0
- package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
- package/dist/ports/receipts.js +9 -0
- package/dist/ports/reconcile.d.ts +33 -0
- package/dist/ports/reconcile.js +200 -0
- package/dist/ports/resource-ownership.d.ts +29 -0
- package/dist/ports/resource-ownership.js +1 -0
- package/dist/ports/router.d.ts +19 -0
- package/dist/ports/router.js +7 -0
- package/dist/ports/sms.d.ts +64 -0
- package/dist/ports/sms.js +24 -0
- package/dist/ports/uhubctl.d.ts +6 -0
- package/dist/ports/uhubctl.js +1 -0
- package/dist/providers/contracts.d.ts +124 -0
- package/dist/providers/contracts.js +10 -0
- package/dist/providers/huawei-hilink/index.d.ts +2 -0
- package/dist/providers/huawei-hilink/index.js +2 -0
- package/dist/providers/huawei-hilink/operations.d.ts +20 -0
- package/dist/providers/huawei-hilink/operations.js +56 -0
- package/dist/providers/huawei-hilink/provider.d.ts +52 -0
- package/dist/providers/huawei-hilink/provider.js +76 -0
- package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
- package/dist/providers/huawei-hilink/runtime.js +171 -0
- package/dist/providers/huawei-hilink/session.d.ts +28 -0
- package/dist/providers/huawei-hilink/session.js +120 -0
- package/dist/providers/huawei-hilink/transport.d.ts +19 -0
- package/dist/providers/huawei-hilink/transport.js +1 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/matcher.d.ts +3 -0
- package/dist/providers/matcher.js +205 -0
- package/dist/providers/modem-manager/errors.d.ts +7 -0
- package/dist/providers/modem-manager/errors.js +37 -0
- package/dist/providers/modem-manager/generic-operations.d.ts +10 -0
- package/dist/providers/modem-manager/generic-operations.js +209 -0
- package/dist/providers/modem-manager/index.d.ts +4 -0
- package/dist/providers/modem-manager/index.js +4 -0
- package/dist/providers/modem-manager/module-operations.d.ts +21 -0
- package/dist/providers/modem-manager/module-operations.js +118 -0
- package/dist/providers/modem-manager/provider.d.ts +41 -0
- package/dist/providers/modem-manager/provider.js +155 -0
- package/dist/providers/modem-manager/runtime-composition-operation.d.ts +32 -0
- package/dist/providers/modem-manager/runtime-composition-operation.js +151 -0
- package/dist/providers/modem-manager/snapshot.d.ts +5 -0
- package/dist/providers/modem-manager/snapshot.js +204 -0
- package/dist/providers/modem-manager/types.d.ts +137 -0
- package/dist/providers/modem-manager/types.js +1 -0
- package/dist/providers/network-manager/adapter.d.ts +71 -0
- package/dist/providers/network-manager/adapter.js +348 -0
- package/dist/providers/network-manager/index.d.ts +2 -0
- package/dist/providers/network-manager/index.js +2 -0
- package/dist/providers/network-manager/types.d.ts +171 -0
- package/dist/providers/network-manager/types.js +77 -0
- package/dist/providers/registry.d.ts +13 -0
- package/dist/providers/registry.js +33 -0
- package/dist/providers/ufi-himi/index.d.ts +6 -0
- package/dist/providers/ufi-himi/index.js +6 -0
- package/dist/providers/ufi-himi/operations.d.ts +41 -0
- package/dist/providers/ufi-himi/operations.js +66 -0
- package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
- package/dist/providers/ufi-himi/prohibitions.js +88 -0
- package/dist/providers/ufi-himi/provider.d.ts +41 -0
- package/dist/providers/ufi-himi/provider.js +204 -0
- package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
- package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
- package/dist/providers/ufi-himi/session.d.ts +46 -0
- package/dist/providers/ufi-himi/session.js +92 -0
- package/dist/providers/ufi-himi/transport.d.ts +29 -0
- package/dist/providers/ufi-himi/transport.js +25 -0
- package/dist/providers/zte-goform/index.d.ts +2 -0
- package/dist/providers/zte-goform/index.js +2 -0
- package/dist/providers/zte-goform/provider.d.ts +56 -0
- package/dist/providers/zte-goform/provider.js +101 -0
- package/dist/providers/zte-goform/session.d.ts +23 -0
- package/dist/providers/zte-goform/session.js +197 -0
- package/dist/providers/zte-goform/transport.d.ts +16 -0
- package/dist/providers/zte-goform/transport.js +1 -0
- package/dist/radio/band-truth.d.ts +50 -0
- package/dist/radio/band-truth.js +92 -0
- package/dist/radio/index.d.ts +3 -0
- package/dist/radio/index.js +10 -0
- package/dist/radio/mode-combinations.d.ts +88 -0
- package/dist/radio/mode-combinations.js +198 -0
- package/dist/radio/mode-truth.d.ts +67 -0
- package/dist/radio/mode-truth.js +112 -0
- package/dist/redact.d.ts +15 -0
- package/dist/redact.js +189 -0
- package/dist/safety/composition-root.d.ts +28 -0
- package/dist/safety/composition-root.js +65 -0
- package/dist/safety/flock-resource-ownership.d.ts +11 -0
- package/dist/safety/flock-resource-ownership.js +138 -0
- package/dist/safety/index.d.ts +2 -0
- package/dist/safety/index.js +2 -0
- package/dist/sms/dbus-messaging.d.ts +17 -0
- package/dist/sms/dbus-messaging.js +185 -0
- package/dist/sms/inbox-store.d.ts +10 -0
- package/dist/sms/inbox-store.js +82 -0
- package/dist/sms/index.d.ts +4 -0
- package/dist/sms/index.js +10 -0
- package/dist/sms/mmcli-parse.d.ts +54 -0
- package/dist/sms/mmcli-parse.js +224 -0
- package/dist/sms/normalize.d.ts +42 -0
- package/dist/sms/normalize.js +95 -0
- package/dist/testing/domain-fakes.d.ts +58 -0
- package/dist/testing/domain-fakes.js +98 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +17 -0
- package/dist/testing/provider-fakes.d.ts +45 -0
- package/dist/testing/provider-fakes.js +64 -0
- package/dist/transport/calls.d.ts +9 -0
- package/dist/transport/calls.js +88 -0
- package/dist/transport/codec.d.ts +3 -0
- package/dist/transport/codec.js +207 -0
- package/dist/transport/dbus-native.d.ts +57 -0
- package/dist/transport/dbus-native.js +17 -0
- package/dist/transport/errors.d.ts +21 -0
- package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
- package/dist/transport/index.d.ts +4 -0
- package/dist/transport/index.js +9 -0
- package/dist/transport/signals.d.ts +15 -0
- package/dist/transport/signals.js +123 -0
- package/dist/transport/signature.d.ts +7 -0
- package/dist/transport/signature.js +94 -0
- package/dist/transport/transport.d.ts +2 -0
- package/dist/transport/transport.js +202 -0
- package/dist/transport/types.d.ts +61 -0
- package/dist/transport/types.js +19 -0
- package/dist/usb-mode/catalog-schema.d.ts +139 -0
- package/dist/usb-mode/catalog-schema.js +97 -0
- package/dist/usb-mode/catalog.d.ts +21 -0
- package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
- package/dist/usb-mode/certified-catalog.json +67 -0
- package/dist/usb-mode/index.d.ts +6 -0
- package/dist/usb-mode/index.js +16 -0
- package/dist/usb-mode/ingestion.d.ts +111 -0
- package/dist/usb-mode/ingestion.js +187 -0
- package/dist/usb-mode/promotion-review.d.ts +21 -0
- package/dist/usb-mode/promotion-review.js +87 -0
- package/dist/usb-mode/runtime-capability.d.ts +59 -0
- package/dist/usb-mode/runtime-capability.js +157 -0
- package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
- package/dist/usb-mode/usb-devices-parse.js +157 -0
- package/dist/ussd/calls.d.ts +32 -0
- package/dist/ussd/calls.js +96 -0
- package/dist/ussd/index.d.ts +5 -0
- package/dist/ussd/index.js +12 -0
- package/dist/ussd/mm-ussd.d.ts +37 -0
- package/dist/ussd/mm-ussd.js +205 -0
- package/dist/ussd/refusal.d.ts +53 -0
- package/dist/ussd/refusal.js +154 -0
- package/dist/ussd/registration.d.ts +20 -0
- package/dist/ussd/registration.js +101 -0
- package/dist/ussd/session.d.ts +106 -0
- package/dist/ussd/session.js +163 -0
- package/package.json +38 -4
- package/src/backend/at-lease.test.ts +0 -106
- package/src/backend/at-lease.ts +0 -158
- package/src/backend/cell-info.test.ts +0 -154
- package/src/backend/cell-info.ts +0 -160
- package/src/backend/device-classifier.test.ts +0 -168
- package/src/backend/device-classifier.ts +0 -248
- package/src/backend/enrichment.ts +0 -96
- package/src/backend/features.test.ts +0 -162
- package/src/backend/features.ts +0 -179
- package/src/backend/identity-ladder.test.ts +0 -117
- package/src/backend/identity-ladder.ts +0 -221
- package/src/backend/identity-registry.test.ts +0 -89
- package/src/backend/identity-registry.ts +0 -151
- package/src/backend/index.ts +0 -236
- package/src/backend/lifecycle-interlock.ts +0 -38
- package/src/backend/managed-objects.ts +0 -108
- package/src/backend/mapping.ts +0 -160
- package/src/backend/mm-backend.ts +0 -191
- package/src/backend/mm-mutations.ts +0 -228
- package/src/backend/modem-actor.test.ts +0 -95
- package/src/backend/modem-actor.ts +0 -112
- package/src/backend/nm-auto-apn.ts +0 -161
- package/src/backend/nm-gsm-fields.ts +0 -122
- package/src/backend/nmcli-nm-port.ts +0 -228
- package/src/backend/nmcli-runner.ts +0 -52
- package/src/backend/observer.ts +0 -297
- package/src/backend/power-contract.test.ts +0 -40
- package/src/backend/power-contract.ts +0 -83
- package/src/backend/recovery-attribution.test.ts +0 -102
- package/src/backend/recovery-attribution.ts +0 -86
- package/src/backend/recovery-budget.test.ts +0 -64
- package/src/backend/recovery-budget.ts +0 -84
- package/src/backend/recovery-ladder.test.ts +0 -257
- package/src/backend/recovery-ladder.ts +0 -249
- package/src/backend/router-ethernet.test.ts +0 -71
- package/src/backend/router-ethernet.ts +0 -90
- package/src/backend/row-store.ts +0 -105
- package/src/backend/signal-setup.ts +0 -112
- package/src/backend/sim-unlock.ts +0 -193
- package/src/backend/transition-preconditions.ts +0 -149
- package/src/backend/uhubctl-power-hook.test.ts +0 -274
- package/src/backend/uhubctl-power-hook.ts +0 -377
- package/src/backend/usage/accounting.test.ts +0 -147
- package/src/backend/usage/accounting.ts +0 -123
- package/src/backend/usage/billing-cycle.test.ts +0 -62
- package/src/backend/usage/billing-cycle.ts +0 -45
- package/src/backend/usage/index.ts +0 -60
- package/src/backend/usage/policy-store.test.ts +0 -164
- package/src/backend/usage/policy-store.ts +0 -216
- package/src/backend/usage/policy-write.test.ts +0 -198
- package/src/backend/usage/policy-write.ts +0 -207
- package/src/backend/usage/proc-net-dev.test.ts +0 -56
- package/src/backend/usage/sampler.test.ts +0 -327
- package/src/backend/usage/sampler.ts +0 -282
- package/src/backend/usage/store.test.ts +0 -148
- package/src/backend/usage/store.ts +0 -177
- package/src/backend/usb-enumerator.test.ts +0 -87
- package/src/backend/usb-enumerator.ts +0 -181
- package/src/backend/usb-mode-transition.test.ts +0 -323
- package/src/backend/usb-mode-transition.ts +0 -253
- package/src/domain/brand.ts +0 -29
- package/src/domain/errors.ts +0 -77
- package/src/domain/guards.test.ts +0 -218
- package/src/domain/guards.ts +0 -144
- package/src/domain/identity.test.ts +0 -83
- package/src/domain/identity.ts +0 -165
- package/src/domain/index.ts +0 -12
- package/src/domain/snapshot.test.ts +0 -266
- package/src/domain/snapshot.ts +0 -120
- package/src/index.test.ts +0 -6
- package/src/index.ts +0 -15
- package/src/ports/README.md +0 -61
- package/src/ports/forbidden-surface.test.ts +0 -241
- package/src/ports/modem-manager.ts +0 -72
- package/src/ports/network-manager.ts +0 -87
- package/src/ports/ops.ts +0 -60
- package/src/ports/ops.type-test.ts +0 -39
- package/src/ports/receipts.test.ts +0 -153
- package/src/ports/reconcile.test.ts +0 -152
- package/src/ports/reconcile.ts +0 -338
- package/src/ports/router.ts +0 -29
- package/src/redact.test.ts +0 -82
- package/src/redact.ts +0 -73
- package/src/transport/README.md +0 -65
- package/src/transport/calls.ts +0 -113
- package/src/transport/characterization.test.ts +0 -260
- package/src/transport/codec.test.ts +0 -118
- package/src/transport/codec.ts +0 -240
- package/src/transport/conformance-python.test.ts +0 -152
- package/src/transport/conformance-same-lib.test.ts +0 -115
- package/src/transport/dbus-native-lib.d.ts +0 -19
- package/src/transport/dbus-native.ts +0 -85
- package/src/transport/index.ts +0 -30
- package/src/transport/no-library-leak.test.ts +0 -61
- package/src/transport/reliability.test.ts +0 -173
- package/src/transport/signals.ts +0 -150
- package/src/transport/signature.ts +0 -110
- package/src/transport/test-support/fake-service.ts +0 -168
- package/src/transport/test-support/independent-producer.py +0 -110
- package/src/transport/test-support/private-bus.ts +0 -66
- package/src/transport/transport.ts +0 -250
- package/src/transport/types.ts +0 -118
- package/src/usb-mode/catalog-schema.test.ts +0 -181
- package/src/usb-mode/catalog-schema.ts +0 -113
- package/src/usb-mode/certified-catalog.json +0 -67
- package/src/usb-mode/index.ts +0 -58
- package/src/usb-mode/ingestion.test.ts +0 -268
- package/src/usb-mode/ingestion.ts +0 -297
- package/src/usb-mode/promotion-review.ts +0 -117
- package/src/usb-mode/usb-devices-parse.ts +0 -196
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ResourceOwnershipPort } from '../ports/index.js';
|
|
2
|
+
export type FlockResourceOwnershipOptions = {
|
|
3
|
+
readonly lockPath: string;
|
|
4
|
+
readonly flockBinary?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare class FlockResourceOwnershipError extends Error {
|
|
7
|
+
readonly detail: string;
|
|
8
|
+
readonly name = "FlockResourceOwnershipError";
|
|
9
|
+
constructor(detail: string);
|
|
10
|
+
}
|
|
11
|
+
export declare function createFlockResourceOwnershipPort(options: FlockResourceOwnershipOptions): ResourceOwnershipPort;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
export class FlockResourceOwnershipError extends Error {
|
|
4
|
+
detail;
|
|
5
|
+
name = 'FlockResourceOwnershipError';
|
|
6
|
+
constructor(detail) {
|
|
7
|
+
super(`flock ownership failed: ${detail}`);
|
|
8
|
+
this.detail = detail;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function createFlockResourceOwnershipPort(options) {
|
|
12
|
+
return {
|
|
13
|
+
async acquire() {
|
|
14
|
+
const child = spawn(options.flockBinary ?? 'flock', ['--exclusive', '--nonblock', '--no-fork', options.lockPath, '/bin/cat'], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
15
|
+
if (child.pid === undefined) {
|
|
16
|
+
throw new FlockResourceOwnershipError('helper started without a process id');
|
|
17
|
+
}
|
|
18
|
+
const holder = { pid: child.pid, startedAtEpochMs: Date.now() };
|
|
19
|
+
const closed = childClosed(child);
|
|
20
|
+
const started = await childStarted(child, options.lockPath, holder);
|
|
21
|
+
if (started.status === 'closed') {
|
|
22
|
+
if (started.code === 1) {
|
|
23
|
+
const holder = await readHolder(options.lockPath);
|
|
24
|
+
return {
|
|
25
|
+
status: 'refused',
|
|
26
|
+
reason: 'already-owned',
|
|
27
|
+
...(holder !== undefined && pidIsAlive(holder.pid) ? { holder } : {}),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
throw new FlockResourceOwnershipError(`helper exited ${started.code ?? 'by signal'}${started.stderr ? `: ${started.stderr}` : ''}`);
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
status: 'acquired',
|
|
34
|
+
lease: createLease(child, closed, started.holder),
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function createLease(child, closed, holder) {
|
|
40
|
+
let released = false;
|
|
41
|
+
return {
|
|
42
|
+
holder,
|
|
43
|
+
lost: closed.then(() => ({ reason: 'holder-exited' })),
|
|
44
|
+
async release() {
|
|
45
|
+
if (released)
|
|
46
|
+
return;
|
|
47
|
+
released = true;
|
|
48
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
49
|
+
child.stdin.end();
|
|
50
|
+
}
|
|
51
|
+
await closed;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function childClosed(child) {
|
|
56
|
+
return new Promise((resolve) => child.once('close', () => resolve()));
|
|
57
|
+
}
|
|
58
|
+
function childStarted(child, lockPath, holder) {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
let stdout = '';
|
|
61
|
+
let stderr = '';
|
|
62
|
+
let settled = false;
|
|
63
|
+
const finish = (result) => {
|
|
64
|
+
if (settled)
|
|
65
|
+
return;
|
|
66
|
+
settled = true;
|
|
67
|
+
resolve(result);
|
|
68
|
+
};
|
|
69
|
+
child.stderr.on('data', (chunk) => {
|
|
70
|
+
stderr += chunk.toString('utf8');
|
|
71
|
+
});
|
|
72
|
+
child.stdout.on('data', (chunk) => {
|
|
73
|
+
stdout += chunk.toString('utf8');
|
|
74
|
+
const newline = stdout.indexOf('\n');
|
|
75
|
+
if (newline < 0)
|
|
76
|
+
return;
|
|
77
|
+
const acquired = parseAcquiredLine(stdout.slice(0, newline));
|
|
78
|
+
if (acquired === undefined || settled)
|
|
79
|
+
return;
|
|
80
|
+
settled = true;
|
|
81
|
+
void writeFile(lockPath, `${JSON.stringify(holder)}\n`, { mode: 0o600 }).then(() => resolve({ status: 'acquired', holder }), (error) => {
|
|
82
|
+
child.stdin.end();
|
|
83
|
+
reject(error);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
child.once('close', (code) => finish({ status: 'closed', code, stderr: stderr.trim() }));
|
|
87
|
+
child.stdin.write(`${JSON.stringify({ type: 'acquired', holder })}\n`);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function parseAcquiredLine(line) {
|
|
91
|
+
try {
|
|
92
|
+
const parsed = JSON.parse(line);
|
|
93
|
+
if (typeof parsed !== 'object' || parsed === null || !('holder' in parsed))
|
|
94
|
+
return undefined;
|
|
95
|
+
return parseHolder(parsed.holder);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error instanceof SyntaxError)
|
|
99
|
+
return undefined;
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function readHolder(lockPath) {
|
|
104
|
+
try {
|
|
105
|
+
return parseHolder(JSON.parse(await readFile(lockPath, 'utf8')));
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT')
|
|
109
|
+
return undefined;
|
|
110
|
+
if (error instanceof SyntaxError)
|
|
111
|
+
return undefined;
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function parseHolder(value) {
|
|
116
|
+
if (typeof value !== 'object' || value === null)
|
|
117
|
+
return undefined;
|
|
118
|
+
if (!('pid' in value) || !('startedAtEpochMs' in value))
|
|
119
|
+
return undefined;
|
|
120
|
+
if (typeof value.pid !== 'number' || !Number.isSafeInteger(value.pid) || value.pid <= 0) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
if (typeof value.startedAtEpochMs !== 'number' || !Number.isFinite(value.startedAtEpochMs)) {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
return { pid: value.pid, startedAtEpochMs: value.startedAtEpochMs };
|
|
127
|
+
}
|
|
128
|
+
function pidIsAlive(pid) {
|
|
129
|
+
try {
|
|
130
|
+
process.kill(pid, 0);
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
if (error instanceof Error && 'code' in error && error.code === 'ESRCH')
|
|
135
|
+
return false;
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type DecodedProps } from '../backend/managed-objects.js';
|
|
2
|
+
import type { SmsMessage, SmsObservationPort } from '../ports/sms.js';
|
|
3
|
+
import type { DbusTransport } from '../transport/index.js';
|
|
4
|
+
/** `org.freedesktop.ModemManager1.Sms` — one message object. */
|
|
5
|
+
export declare const SMS_IFACE = "org.freedesktop.ModemManager1.Sms";
|
|
6
|
+
/** Build one message from an SMS object's decoded `Sms` properties. */
|
|
7
|
+
export declare function smsFromProps(path: string, props: DecodedProps | undefined): SmsMessage;
|
|
8
|
+
export interface DbusSmsPortOptions {
|
|
9
|
+
readonly transport: DbusTransport;
|
|
10
|
+
/** The modem's ModemManager object path. */
|
|
11
|
+
readonly modemPath: string;
|
|
12
|
+
readonly destination?: string;
|
|
13
|
+
readonly cap?: number;
|
|
14
|
+
/** Called when a fold-time error must be surfaced without failing a read. */
|
|
15
|
+
readonly onError?: (error: unknown) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function createDbusSmsPort(options: DbusSmsPortOptions): SmsObservationPort;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// The ModemManager Messaging adapter — LIST ONCE, then follow Added / Deleted.
|
|
2
|
+
//
|
|
3
|
+
// Three verbs, all reads: `Messaging.List` for the object paths,
|
|
4
|
+
// `Properties.GetAll` per message, and a subscription to `Added` / `Deleted`.
|
|
5
|
+
// There is no `Create`, no `Send`, no `Delete`, and none may be added — see
|
|
6
|
+
// `../ports/sms.ts` for why that is permanent rather than staged, and
|
|
7
|
+
// `readonly-gate.test.ts` for the lock.
|
|
8
|
+
//
|
|
9
|
+
// NEVER RE-LIST ON A TICK. MM tells us about every change, so a poll would cost
|
|
10
|
+
// one method call per stored message per tick and still could not report an
|
|
11
|
+
// arrival sooner than the tick it happened to land on. The ONLY re-list is on a
|
|
12
|
+
// source RECONNECT, where the events that occurred while the bus was down were
|
|
13
|
+
// never delivered and folding is therefore impossible; that re-list is published
|
|
14
|
+
// as `resynced` so the consumer replaces its rows instead of merging (see
|
|
15
|
+
// `inbox-store.ts`).
|
|
16
|
+
//
|
|
17
|
+
// A MESSAGE THAT VANISHES BETWEEN THE LIST AND ITS READ IS ORDINARY. The modem's
|
|
18
|
+
// own storage rotates, so a failed per-message read SKIPS that message rather
|
|
19
|
+
// than failing the whole inbox. A failed LIST is different — nothing was read at
|
|
20
|
+
// all — and answers a typed refusal.
|
|
21
|
+
import { MM_BUS_NAME, PROPERTIES_IFACE } from '../backend/constants.js';
|
|
22
|
+
import { numberProp, stringProp } from '../backend/managed-objects.js';
|
|
23
|
+
// The one definition of MM's Messaging interface name lives with the capability
|
|
24
|
+
// probe that also keys on it; duplicating it here is how the two drift apart.
|
|
25
|
+
import { MESSAGING_IFACE } from '../capability/detect.js';
|
|
26
|
+
import { classifySmsFailure } from './mmcli-parse.js';
|
|
27
|
+
import { normalizeSmsState, SMS_INBOX_CAP, selectReadablePaths, sortAndCapSms } from './normalize.js';
|
|
28
|
+
/** `org.freedesktop.ModemManager1.Sms` — one message object. */
|
|
29
|
+
export const SMS_IFACE = 'org.freedesktop.ModemManager1.Sms';
|
|
30
|
+
/**
|
|
31
|
+
* `MMSmsState` (ModemManager `mm-enums.h`) as the port's own vocabulary. The
|
|
32
|
+
* index IS the enum value; an out-of-range value folds to `unknown` through
|
|
33
|
+
* {@link normalizeSmsState}, so a future MM state can never surface as a raw
|
|
34
|
+
* number an operator cannot act on.
|
|
35
|
+
*/
|
|
36
|
+
const SMS_STATE_BY_ORDINAL = [
|
|
37
|
+
'unknown',
|
|
38
|
+
'stored',
|
|
39
|
+
'receiving',
|
|
40
|
+
'received',
|
|
41
|
+
'sending',
|
|
42
|
+
'sent',
|
|
43
|
+
];
|
|
44
|
+
function stateFromOrdinal(ordinal) {
|
|
45
|
+
if (ordinal === undefined) {
|
|
46
|
+
return 'unknown';
|
|
47
|
+
}
|
|
48
|
+
return normalizeSmsState(SMS_STATE_BY_ORDINAL[ordinal]);
|
|
49
|
+
}
|
|
50
|
+
/** Build one message from an SMS object's decoded `Sms` properties. */
|
|
51
|
+
export function smsFromProps(path, props) {
|
|
52
|
+
const from = stringProp(props, 'Number');
|
|
53
|
+
const timestamp = stringProp(props, 'Timestamp');
|
|
54
|
+
const index = path.slice(path.lastIndexOf('/') + 1);
|
|
55
|
+
return {
|
|
56
|
+
id: index,
|
|
57
|
+
...(from !== undefined && from !== '' ? { from } : {}),
|
|
58
|
+
...(timestamp !== undefined && timestamp !== '' ? { timestamp } : {}),
|
|
59
|
+
// A data-only (WAP/PDU) message carries no text at all; `''` says so.
|
|
60
|
+
text: stringProp(props, 'Text') ?? '',
|
|
61
|
+
state: stateFromOrdinal(numberProp(props, 'State')),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function describe(error) {
|
|
65
|
+
return error instanceof Error ? error.message : String(error);
|
|
66
|
+
}
|
|
67
|
+
export function createDbusSmsPort(options) {
|
|
68
|
+
const { transport, modemPath } = options;
|
|
69
|
+
const destination = options.destination ?? MM_BUS_NAME;
|
|
70
|
+
const cap = options.cap ?? SMS_INBOX_CAP;
|
|
71
|
+
const listeners = new Set();
|
|
72
|
+
const subscriptions = [];
|
|
73
|
+
let wiring;
|
|
74
|
+
let stopped = false;
|
|
75
|
+
const emit = (event) => {
|
|
76
|
+
for (const listener of listeners) {
|
|
77
|
+
try {
|
|
78
|
+
listener(event);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
options.onError?.(error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const readMessage = async (path) => {
|
|
86
|
+
try {
|
|
87
|
+
const reply = await transport.callMethod({
|
|
88
|
+
destination,
|
|
89
|
+
path,
|
|
90
|
+
interface: PROPERTIES_IFACE,
|
|
91
|
+
member: 'GetAll',
|
|
92
|
+
signature: 's',
|
|
93
|
+
args: [SMS_IFACE],
|
|
94
|
+
});
|
|
95
|
+
return smsFromProps(path, reply.body[0]);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Vanished between the list and this read — ordinary storage rotation.
|
|
99
|
+
// Nothing is logged: the error text is the only place a body could hide.
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const listInbox = async () => {
|
|
104
|
+
let paths;
|
|
105
|
+
try {
|
|
106
|
+
const reply = await transport.callMethod({
|
|
107
|
+
destination,
|
|
108
|
+
path: modemPath,
|
|
109
|
+
interface: MESSAGING_IFACE,
|
|
110
|
+
member: 'List',
|
|
111
|
+
});
|
|
112
|
+
paths = Array.isArray(reply.body[0]) ? reply.body[0] : [];
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
return { ok: false, reason: classifySmsFailure(describe(error)) };
|
|
116
|
+
}
|
|
117
|
+
const candidates = selectReadablePaths(paths.filter((value) => typeof value === 'string'), cap);
|
|
118
|
+
const messages = [];
|
|
119
|
+
for (const path of candidates) {
|
|
120
|
+
const message = await readMessage(path);
|
|
121
|
+
if (message !== undefined) {
|
|
122
|
+
messages.push(message);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { ok: true, messages: sortAndCapSms(messages, cap) };
|
|
126
|
+
};
|
|
127
|
+
const resync = async () => {
|
|
128
|
+
const result = await listInbox();
|
|
129
|
+
if (result.ok) {
|
|
130
|
+
emit({ kind: 'resynced', messages: result.messages });
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const onAdded = async (body) => {
|
|
134
|
+
const path = body[0];
|
|
135
|
+
if (typeof path !== 'string') {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const message = await readMessage(path);
|
|
139
|
+
if (message !== undefined) {
|
|
140
|
+
emit({ kind: 'added', message });
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const wire = async () => {
|
|
144
|
+
const added = await transport.subscribeSignal({ interface: MESSAGING_IFACE, member: 'Added', path: modemPath }, (event) => {
|
|
145
|
+
void onAdded(event.body).catch((error) => options.onError?.(error));
|
|
146
|
+
});
|
|
147
|
+
const deleted = await transport.subscribeSignal({ interface: MESSAGING_IFACE, member: 'Deleted', path: modemPath }, (event) => {
|
|
148
|
+
const path = event.body[0];
|
|
149
|
+
if (typeof path === 'string') {
|
|
150
|
+
emit({ kind: 'deleted', id: path.slice(path.lastIndexOf('/') + 1) });
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
subscriptions.push(added, deleted);
|
|
154
|
+
transport.on('reconnected', reconnected);
|
|
155
|
+
// A subscription established AFTER the caller's own `list()` can have
|
|
156
|
+
// missed an arrival in between, so the wiring ends with one resync.
|
|
157
|
+
await resync();
|
|
158
|
+
};
|
|
159
|
+
const reconnected = () => {
|
|
160
|
+
void resync().catch((error) => options.onError?.(error));
|
|
161
|
+
};
|
|
162
|
+
return {
|
|
163
|
+
list: listInbox,
|
|
164
|
+
observe(listener) {
|
|
165
|
+
listeners.add(listener);
|
|
166
|
+
if (wiring === undefined && !stopped) {
|
|
167
|
+
wiring = wire().catch((error) => {
|
|
168
|
+
options.onError?.(error);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return () => {
|
|
172
|
+
listeners.delete(listener);
|
|
173
|
+
};
|
|
174
|
+
},
|
|
175
|
+
async stop() {
|
|
176
|
+
stopped = true;
|
|
177
|
+
listeners.clear();
|
|
178
|
+
transport.off('reconnected', reconnected);
|
|
179
|
+
await wiring?.catch(() => undefined);
|
|
180
|
+
wiring = undefined;
|
|
181
|
+
const held = subscriptions.splice(0, subscriptions.length);
|
|
182
|
+
await Promise.all(held.map((subscription) => subscription.unsubscribe()));
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SmsInboxEvent, SmsMessage } from '../ports/sms.js';
|
|
2
|
+
export interface SmsInboxStore {
|
|
3
|
+
/** Apply one event. Answers whether the visible inbox actually changed. */
|
|
4
|
+
apply(event: SmsInboxEvent): boolean;
|
|
5
|
+
/** The current inbox, newest-first and capped. */
|
|
6
|
+
snapshot(): SmsMessage[];
|
|
7
|
+
/** Number of retained rows, before the cap is applied. */
|
|
8
|
+
size(): number;
|
|
9
|
+
}
|
|
10
|
+
export declare function createSmsInboxStore(cap?: number): SmsInboxStore;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// The inbox row store — folds `Added` / `Deleted` / resync events into one
|
|
2
|
+
// newest-first, capped list.
|
|
3
|
+
//
|
|
4
|
+
// TWO PROPERTIES ARE THE WHOLE POINT OF THIS FILE, and both are failures a
|
|
5
|
+
// signal-driven inbox produces on its own unless something stops them.
|
|
6
|
+
//
|
|
7
|
+
// DUPLICATE SUPPRESSION. ModemManager emits `Added` for a message it is
|
|
8
|
+
// receiving AND again once it is stored, a re-subscribe after a bus hiccup can
|
|
9
|
+
// replay one, and a consumer that also re-listed would hold the same message
|
|
10
|
+
// twice. Rows are therefore keyed by object index and an `Added` carrying
|
|
11
|
+
// nothing new is a NO-OP — `apply` answers `false` and no consumer is woken.
|
|
12
|
+
//
|
|
13
|
+
// RESTART RECOVERY. A source restart cannot be folded, because the events that
|
|
14
|
+
// happened while it was down were never delivered. `resynced` therefore REPLACES
|
|
15
|
+
// the whole row set rather than merging into it: merging would keep a message
|
|
16
|
+
// deleted during the outage forever, and re-adding each row of a fresh list one
|
|
17
|
+
// `added` at a time is exactly how a restart comes to duplicate the inbox it
|
|
18
|
+
// already had.
|
|
19
|
+
import { SMS_INBOX_CAP, sortAndCapSms } from './normalize.js';
|
|
20
|
+
/** Two rows are the same message when every rendered field matches. */
|
|
21
|
+
function sameMessage(a, b) {
|
|
22
|
+
return (a.id === b.id &&
|
|
23
|
+
a.text === b.text &&
|
|
24
|
+
a.state === b.state &&
|
|
25
|
+
a.from === b.from &&
|
|
26
|
+
a.timestamp === b.timestamp);
|
|
27
|
+
}
|
|
28
|
+
export function createSmsInboxStore(cap = SMS_INBOX_CAP) {
|
|
29
|
+
const rows = new Map();
|
|
30
|
+
const applyAdded = (message) => {
|
|
31
|
+
const held = rows.get(message.id);
|
|
32
|
+
// A repeated Added for a row we already hold VERBATIM is the duplicate
|
|
33
|
+
// this store exists to swallow. An Added that genuinely differs is a
|
|
34
|
+
// state transition (receiving -> received) and does update the row.
|
|
35
|
+
if (held !== undefined && sameMessage(held, message)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
rows.set(message.id, message);
|
|
39
|
+
return true;
|
|
40
|
+
};
|
|
41
|
+
const applyResynced = (messages) => {
|
|
42
|
+
const next = new Map();
|
|
43
|
+
for (const message of messages) {
|
|
44
|
+
next.set(message.id, message);
|
|
45
|
+
}
|
|
46
|
+
// Report "unchanged" when the authoritative list matches what is already
|
|
47
|
+
// held, so a reconnect that found nothing new broadcasts nothing.
|
|
48
|
+
let changed = next.size !== rows.size;
|
|
49
|
+
if (!changed) {
|
|
50
|
+
for (const [id, message] of next) {
|
|
51
|
+
const held = rows.get(id);
|
|
52
|
+
if (held === undefined || !sameMessage(held, message)) {
|
|
53
|
+
changed = true;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
rows.clear();
|
|
59
|
+
for (const [id, message] of next) {
|
|
60
|
+
rows.set(id, message);
|
|
61
|
+
}
|
|
62
|
+
return changed;
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
apply(event) {
|
|
66
|
+
switch (event.kind) {
|
|
67
|
+
case 'added':
|
|
68
|
+
return applyAdded(event.message);
|
|
69
|
+
case 'deleted':
|
|
70
|
+
return rows.delete(event.id);
|
|
71
|
+
case 'resynced':
|
|
72
|
+
return applyResynced(event.messages);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
snapshot() {
|
|
76
|
+
return sortAndCapSms([...rows.values()], cap);
|
|
77
|
+
},
|
|
78
|
+
size() {
|
|
79
|
+
return rows.size;
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// The read-only SMS surface: normalization, the `mmcli -K` grammar, the
|
|
2
|
+
// Added/Deleted fold, and the ModemManager Messaging adapter.
|
|
3
|
+
//
|
|
4
|
+
// LIST / READ and observation ONLY. Nothing exported here composes, stores,
|
|
5
|
+
// sends, or deletes a message; `readonly-gate.test.ts` fails the build if that
|
|
6
|
+
// ever stops being true.
|
|
7
|
+
export * from './dbus-messaging.js';
|
|
8
|
+
export * from './inbox-store.js';
|
|
9
|
+
export * from './mmcli-parse.js';
|
|
10
|
+
export * from './normalize.js';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { SmsMessage, SmsReadRefusal } from '../ports/sms.js';
|
|
2
|
+
/** A parse outcome. `detail` NEVER carries message content — key names only. */
|
|
3
|
+
export type SmsParseResult<T> = {
|
|
4
|
+
readonly ok: true;
|
|
5
|
+
readonly value: T;
|
|
6
|
+
} | {
|
|
7
|
+
readonly ok: false;
|
|
8
|
+
readonly reason: string;
|
|
9
|
+
readonly detail: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Undo `g_strescape()` on one `-K` value.
|
|
13
|
+
*
|
|
14
|
+
* mmcli does not print non-ASCII text — it prints the LITERAL ASCII characters
|
|
15
|
+
* of the octal escape. A Spanish message arrives on stdout as the eight
|
|
16
|
+
* characters `\`,`3`,`0`,`2`,`\`,`2`,`4`,`1` where the wire carried the two
|
|
17
|
+
* UTF-8 bytes `0xC2 0xA1` (confirmed on the bench board with `od -c`). The
|
|
18
|
+
* escapes are therefore per-BYTE, not per-character, and the only correct
|
|
19
|
+
* decode is to rebuild the byte sequence and read it back as UTF-8 — decoding
|
|
20
|
+
* each escape with `String.fromCharCode` instead turns `¡` into `¡`.
|
|
21
|
+
*
|
|
22
|
+
* Total and silent: it never throws and never logs, because the value it holds
|
|
23
|
+
* is message content. An escape outside the grammar is copied VERBATIM rather
|
|
24
|
+
* than dropped — mmcli cannot emit one, so meeting one means the value was
|
|
25
|
+
* never escaped and copying it is the only lossless answer.
|
|
26
|
+
*/
|
|
27
|
+
export declare function unescapeMmcliValue(value: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Extract SMS object paths from `mmcli -K -m <id> --messaging-list-sms`.
|
|
30
|
+
*
|
|
31
|
+
* An inbox with no messages prints as `modem.messaging.sms: --`, whose `--`
|
|
32
|
+
* carries no entry — so an ABSENT key is a legitimate EMPTY INBOX, exactly as it
|
|
33
|
+
* is for `--3gpp-scan`, not drift. What IS drift, and what fails loud here, is
|
|
34
|
+
* output that never mentions the key at all (a renamed field, an error body
|
|
35
|
+
* reaching this parser): answering "no messages" to a read that never ran would
|
|
36
|
+
* be the worst possible lie about an inbox.
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseSmsListOutput(raw: string): SmsParseResult<string[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Parse one `mmcli -K -s <path>` record.
|
|
41
|
+
*
|
|
42
|
+
* A malformed record reports the KEY NAMES it found and nothing else — see the
|
|
43
|
+
* content-free note at the top of this file.
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseSmsRecordOutput(raw: string): SmsParseResult<SmsMessage>;
|
|
46
|
+
/**
|
|
47
|
+
* Classify a source failure into a refusal the operator can act on.
|
|
48
|
+
*
|
|
49
|
+
* The three recognised strings are ModemManager 1.24's own, confirmed on the
|
|
50
|
+
* bench board: a modem with no Messaging interface, a radio that has not come up
|
|
51
|
+
* yet, and a selector nothing answers to. Anything else stays `read_failed`
|
|
52
|
+
* rather than being guessed at.
|
|
53
|
+
*/
|
|
54
|
+
export declare function classifySmsFailure(description: string): SmsReadRefusal;
|