@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,94 @@
|
|
|
1
|
+
// D-Bus signature parsing and support checks.
|
|
2
|
+
//
|
|
3
|
+
// We keep our own tiny recursive-descent parser rather than reaching into the
|
|
4
|
+
// underlying library's internal `lib/signature.js`: the parsed tree drives our
|
|
5
|
+
// signature-aware encode/decode (below) and the tree shape is part of this seam's
|
|
6
|
+
// contract, not the library's. The node shape intentionally mirrors the library's
|
|
7
|
+
// (`{ type, child }`) so a tree the library hands us inside a decoded variant can be
|
|
8
|
+
// re-serialised with `signatureFromNode` without a translation step.
|
|
9
|
+
import { UnsupportedSignatureError } from './errors.js';
|
|
10
|
+
const CONTAINER_CLOSE = {
|
|
11
|
+
'{': '}',
|
|
12
|
+
'(': ')',
|
|
13
|
+
};
|
|
14
|
+
// Every basic and container type char D-Bus defines. `h` (UNIX_FD) is deliberately
|
|
15
|
+
// listed as *known* so the parser accepts it structurally — we reject it explicitly in
|
|
16
|
+
// `assertSupportedSignature` with a typed error rather than as a vague parse failure.
|
|
17
|
+
const KNOWN_TYPES = new Set('ybnqiuxtdsogarvhe{}()'.split(''));
|
|
18
|
+
// UNIX_FD: file-descriptor passing. Unsupported everywhere in this transport.
|
|
19
|
+
const UNSUPPORTED_TYPE = 'h';
|
|
20
|
+
// Throws UnsupportedSignatureError if the signature contains `h` anywhere (including
|
|
21
|
+
// nested inside arrays, structs, dict entries, or variants). Signatures are pure type
|
|
22
|
+
// strings, so a plain character scan is exact.
|
|
23
|
+
export function assertSupportedSignature(signature) {
|
|
24
|
+
const index = signature.indexOf(UNSUPPORTED_TYPE);
|
|
25
|
+
if (index !== -1) {
|
|
26
|
+
throw new UnsupportedSignatureError(signature, UNSUPPORTED_TYPE);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Parse a full signature (which may contain several top-level complete types) into a
|
|
30
|
+
// flat list of nodes.
|
|
31
|
+
export function parseSignature(signature) {
|
|
32
|
+
let index = 0;
|
|
33
|
+
function next() {
|
|
34
|
+
if (index < signature.length) {
|
|
35
|
+
const char = signature[index];
|
|
36
|
+
index += 1;
|
|
37
|
+
return char;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
function parseOne(char) {
|
|
42
|
+
if (!KNOWN_TYPES.has(char)) {
|
|
43
|
+
throw new Error(`Unknown D-Bus type "${char}" in signature "${signature}"`);
|
|
44
|
+
}
|
|
45
|
+
const children = [];
|
|
46
|
+
switch (char) {
|
|
47
|
+
case 'a': {
|
|
48
|
+
const element = next();
|
|
49
|
+
if (element === null) {
|
|
50
|
+
throw new Error(`Bad signature "${signature}": array with no element type`);
|
|
51
|
+
}
|
|
52
|
+
children.push(parseOne(element));
|
|
53
|
+
return { type: 'a', child: children };
|
|
54
|
+
}
|
|
55
|
+
case '{':
|
|
56
|
+
case '(': {
|
|
57
|
+
const close = CONTAINER_CLOSE[char];
|
|
58
|
+
let element = next();
|
|
59
|
+
while (element !== null && element !== close) {
|
|
60
|
+
children.push(parseOne(element));
|
|
61
|
+
element = next();
|
|
62
|
+
}
|
|
63
|
+
if (element === null) {
|
|
64
|
+
throw new Error(`Bad signature "${signature}": unterminated "${char}"`);
|
|
65
|
+
}
|
|
66
|
+
return { type: char, child: children };
|
|
67
|
+
}
|
|
68
|
+
default:
|
|
69
|
+
return { type: char, child: children };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const nodes = [];
|
|
73
|
+
let char = next();
|
|
74
|
+
while (char !== null) {
|
|
75
|
+
nodes.push(parseOne(char));
|
|
76
|
+
char = next();
|
|
77
|
+
}
|
|
78
|
+
return nodes;
|
|
79
|
+
}
|
|
80
|
+
// Re-serialise a single parsed node back to its signature string. Used to recover the
|
|
81
|
+
// inner signature of a decoded variant, whose contained type the library hands us as a
|
|
82
|
+
// parse tree rather than a string.
|
|
83
|
+
export function signatureFromNode(node) {
|
|
84
|
+
switch (node.type) {
|
|
85
|
+
case 'a':
|
|
86
|
+
return `a${signatureFromNode(node.child[0])}`;
|
|
87
|
+
case '(':
|
|
88
|
+
return `(${node.child.map(signatureFromNode).join('')})`;
|
|
89
|
+
case '{':
|
|
90
|
+
return `{${node.child.map(signatureFromNode).join('')}}`;
|
|
91
|
+
default:
|
|
92
|
+
return node.type;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// The D-Bus transport seam implementation.
|
|
2
|
+
//
|
|
3
|
+
// Wraps `@httptoolkit/dbus-native` behind the `DbusTransport` interface. This module owns the
|
|
4
|
+
// connection lifecycle — handshake, a reconnect loop that re-issues every match rule after a
|
|
5
|
+
// bus restart, and teardown — and delegates method-call dispatch to `./calls` and signal
|
|
6
|
+
// subscription/match-rule tracking to `./signals`. Its single `message` listener fans out to
|
|
7
|
+
// the signal registry, so subscribing never grows the listener count (the 100-cycle leak check).
|
|
8
|
+
import { EventEmitter } from 'node:events';
|
|
9
|
+
import { CallDispatcher, DEFAULT_CALL_TIMEOUT_MS } from './calls.js';
|
|
10
|
+
import { createClient, } from './dbus-native.js';
|
|
11
|
+
import { DisconnectedError, TransportError } from './errors.js';
|
|
12
|
+
import { SignalRegistry } from './signals.js';
|
|
13
|
+
// Bound a single connect/auth attempt so a stalled handshake cannot freeze the reconnect
|
|
14
|
+
// loop. A local unix-socket D-Bus connect completes in milliseconds; 2s is ample headroom
|
|
15
|
+
// while keeping reconnect responsive after a bus restart.
|
|
16
|
+
const CONNECT_TIMEOUT_MS = 2_000;
|
|
17
|
+
const DEFAULT_RECONNECT = {
|
|
18
|
+
enabled: true,
|
|
19
|
+
initialDelayMs: 50,
|
|
20
|
+
maxDelayMs: 2_000,
|
|
21
|
+
maxAttempts: 0,
|
|
22
|
+
};
|
|
23
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
24
|
+
class DbusTransportImpl {
|
|
25
|
+
#options;
|
|
26
|
+
#reconnect;
|
|
27
|
+
#emitter = new EventEmitter();
|
|
28
|
+
#calls;
|
|
29
|
+
#signals;
|
|
30
|
+
#bus = null;
|
|
31
|
+
#state = 'idle';
|
|
32
|
+
#closing = false;
|
|
33
|
+
// Bound once so the same references can be detached from a dead connection.
|
|
34
|
+
#onMessage = (message) => this.#signals.dispatch(message);
|
|
35
|
+
#onConnectionError = (cause) => this.#handleDrop(cause instanceof Error ? cause : new DisconnectedError(String(cause)));
|
|
36
|
+
#onConnectionEnd = () => this.#handleDrop(new DisconnectedError('bus connection ended'));
|
|
37
|
+
constructor(options) {
|
|
38
|
+
this.#options = options;
|
|
39
|
+
this.#calls = new CallDispatcher(options.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS);
|
|
40
|
+
this.#signals = new SignalRegistry({
|
|
41
|
+
currentBus: () => this.#bus,
|
|
42
|
+
isConnected: () => this.#state === 'connected',
|
|
43
|
+
emitError: (error) => this.#emitter.emit('error', error),
|
|
44
|
+
});
|
|
45
|
+
this.#reconnect = {
|
|
46
|
+
enabled: options.reconnect?.enabled ?? DEFAULT_RECONNECT.enabled,
|
|
47
|
+
initialDelayMs: options.reconnect?.initialDelayMs ?? DEFAULT_RECONNECT.initialDelayMs,
|
|
48
|
+
maxDelayMs: options.reconnect?.maxDelayMs ?? DEFAULT_RECONNECT.maxDelayMs,
|
|
49
|
+
maxAttempts: options.reconnect?.maxAttempts ?? DEFAULT_RECONNECT.maxAttempts,
|
|
50
|
+
};
|
|
51
|
+
// Avoid MaxListeners warnings when many consumers observe transport events.
|
|
52
|
+
this.#emitter.setMaxListeners(0);
|
|
53
|
+
}
|
|
54
|
+
async connect() {
|
|
55
|
+
if (this.#state === 'connected') {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (this.#closing) {
|
|
59
|
+
throw new TransportError('Transport is closed');
|
|
60
|
+
}
|
|
61
|
+
this.#state = 'connecting';
|
|
62
|
+
await this.#establish();
|
|
63
|
+
this.#emitter.emit('connected');
|
|
64
|
+
}
|
|
65
|
+
isConnected() {
|
|
66
|
+
return this.#state === 'connected';
|
|
67
|
+
}
|
|
68
|
+
async disconnect() {
|
|
69
|
+
this.#closing = true;
|
|
70
|
+
this.#state = 'closed';
|
|
71
|
+
const bus = this.#bus;
|
|
72
|
+
this.#bus = null;
|
|
73
|
+
this.#calls.rejectAll(new DisconnectedError('transport closed'));
|
|
74
|
+
if (bus) {
|
|
75
|
+
this.#quiesce(bus);
|
|
76
|
+
await bus.disconnect().catch(() => undefined);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
callMethod(call) {
|
|
80
|
+
return this.#calls.call(this.#bus, this.#state === 'connected', call);
|
|
81
|
+
}
|
|
82
|
+
subscribeSignal(spec, listener) {
|
|
83
|
+
return this.#signals.subscribe(spec, listener);
|
|
84
|
+
}
|
|
85
|
+
subscriptionCount() {
|
|
86
|
+
return this.#signals.count();
|
|
87
|
+
}
|
|
88
|
+
on(event, handler) {
|
|
89
|
+
this.#emitter.on(event, handler);
|
|
90
|
+
}
|
|
91
|
+
off(event, handler) {
|
|
92
|
+
this.#emitter.off(event, handler);
|
|
93
|
+
}
|
|
94
|
+
// ── internals ──────────────────────────────────────────────────────────────────
|
|
95
|
+
async #establish() {
|
|
96
|
+
const options = { ReturnLongjs: true };
|
|
97
|
+
if (this.#options.socket !== undefined) {
|
|
98
|
+
options.socket = this.#options.socket;
|
|
99
|
+
}
|
|
100
|
+
else if (this.#options.busAddress !== undefined) {
|
|
101
|
+
options.busAddress = this.#options.busAddress;
|
|
102
|
+
}
|
|
103
|
+
const bus = createClient(options);
|
|
104
|
+
try {
|
|
105
|
+
await new Promise((resolve, reject) => {
|
|
106
|
+
const onConnect = () => {
|
|
107
|
+
cleanup();
|
|
108
|
+
resolve();
|
|
109
|
+
};
|
|
110
|
+
const onError = (error) => {
|
|
111
|
+
cleanup();
|
|
112
|
+
reject(error instanceof Error ? error : new TransportError(String(error)));
|
|
113
|
+
};
|
|
114
|
+
const timer = setTimeout(() => {
|
|
115
|
+
cleanup();
|
|
116
|
+
reject(new TransportError(`bus connect timed out after ${CONNECT_TIMEOUT_MS}ms`));
|
|
117
|
+
}, CONNECT_TIMEOUT_MS);
|
|
118
|
+
const cleanup = () => {
|
|
119
|
+
clearTimeout(timer);
|
|
120
|
+
bus.connection.removeListener('connect', onConnect);
|
|
121
|
+
bus.connection.removeListener('error', onError);
|
|
122
|
+
};
|
|
123
|
+
bus.connection.once('connect', onConnect);
|
|
124
|
+
bus.connection.once('error', onError);
|
|
125
|
+
});
|
|
126
|
+
bus.connection.on('message', this.#onMessage);
|
|
127
|
+
bus.connection.on('error', this.#onConnectionError);
|
|
128
|
+
bus.connection.on('end', this.#onConnectionEnd);
|
|
129
|
+
// Re-issue every live match rule so a reconnect resubscribes transparently.
|
|
130
|
+
await this.#signals.reissueRules(bus);
|
|
131
|
+
this.#bus = bus;
|
|
132
|
+
this.#state = 'connected';
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
this.#detachHandlers(bus);
|
|
136
|
+
bus.connection.removeAllListeners();
|
|
137
|
+
bus.connection.on('error', () => undefined);
|
|
138
|
+
try {
|
|
139
|
+
bus.connection.end();
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// The half-open connection is already dead; nothing to close.
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
#detachHandlers(bus) {
|
|
148
|
+
bus.connection.removeListener('message', this.#onMessage);
|
|
149
|
+
bus.connection.removeListener('error', this.#onConnectionError);
|
|
150
|
+
bus.connection.removeListener('end', this.#onConnectionEnd);
|
|
151
|
+
}
|
|
152
|
+
// Detach our handlers from a dead connection, then swallow any late socket error it
|
|
153
|
+
// still emits — without a listener, Node re-throws an EventEmitter 'error' and crashes
|
|
154
|
+
// the process mid-reconnect.
|
|
155
|
+
#quiesce(bus) {
|
|
156
|
+
this.#detachHandlers(bus);
|
|
157
|
+
bus.connection.on('error', () => undefined);
|
|
158
|
+
}
|
|
159
|
+
#handleDrop(cause) {
|
|
160
|
+
if (this.#closing) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (this.#state === 'disconnected' || this.#state === 'reconnecting') {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
this.#state = 'disconnected';
|
|
167
|
+
if (this.#bus) {
|
|
168
|
+
this.#quiesce(this.#bus);
|
|
169
|
+
}
|
|
170
|
+
this.#bus = null;
|
|
171
|
+
this.#calls.rejectAll(cause);
|
|
172
|
+
this.#emitter.emit('disconnected', cause);
|
|
173
|
+
if (this.#reconnect.enabled) {
|
|
174
|
+
void this.#reconnectLoop();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async #reconnectLoop() {
|
|
178
|
+
this.#state = 'reconnecting';
|
|
179
|
+
let delay = this.#reconnect.initialDelayMs;
|
|
180
|
+
let attempt = 0;
|
|
181
|
+
while (!this.#closing) {
|
|
182
|
+
attempt += 1;
|
|
183
|
+
try {
|
|
184
|
+
await this.#establish();
|
|
185
|
+
this.#emitter.emit('reconnected');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
if (this.#reconnect.maxAttempts > 0 && attempt >= this.#reconnect.maxAttempts) {
|
|
190
|
+
this.#state = 'disconnected';
|
|
191
|
+
this.#emitter.emit('error', error);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
await sleep(delay);
|
|
195
|
+
delay = Math.min(delay * 2, this.#reconnect.maxDelayMs);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
export function createDbusTransport(options = {}) {
|
|
201
|
+
return new DbusTransportImpl(options);
|
|
202
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export type DbusValue = string | number | boolean | bigint | Uint8Array | DbusVariant | DbusValue[];
|
|
2
|
+
export interface DbusVariant {
|
|
3
|
+
readonly signature: string;
|
|
4
|
+
readonly value: DbusValue;
|
|
5
|
+
}
|
|
6
|
+
export declare function variant(signature: string, value: DbusValue): DbusVariant;
|
|
7
|
+
export declare function isVariant(value: DbusValue): value is DbusVariant;
|
|
8
|
+
export interface MethodCall {
|
|
9
|
+
readonly destination: string;
|
|
10
|
+
readonly path: string;
|
|
11
|
+
readonly interface: string;
|
|
12
|
+
readonly member: string;
|
|
13
|
+
readonly signature?: string;
|
|
14
|
+
readonly args?: readonly DbusValue[];
|
|
15
|
+
readonly timeoutMs?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface MethodReply {
|
|
18
|
+
readonly signature: string;
|
|
19
|
+
readonly body: DbusValue[];
|
|
20
|
+
}
|
|
21
|
+
export interface SignalSpec {
|
|
22
|
+
readonly interface: string;
|
|
23
|
+
readonly member: string;
|
|
24
|
+
readonly path?: string;
|
|
25
|
+
readonly sender?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface SignalEvent {
|
|
28
|
+
readonly path: string;
|
|
29
|
+
readonly interface: string;
|
|
30
|
+
readonly member: string;
|
|
31
|
+
readonly sender: string | undefined;
|
|
32
|
+
readonly signature: string;
|
|
33
|
+
readonly body: DbusValue[];
|
|
34
|
+
}
|
|
35
|
+
export type SignalListener = (event: SignalEvent) => void;
|
|
36
|
+
export interface Subscription {
|
|
37
|
+
unsubscribe(): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export type TransportEvent = 'connected' | 'reconnected' | 'disconnected' | 'error';
|
|
40
|
+
export interface DbusTransportOptions {
|
|
41
|
+
readonly busAddress?: string;
|
|
42
|
+
readonly socket?: string;
|
|
43
|
+
readonly callTimeoutMs?: number;
|
|
44
|
+
readonly reconnect?: ReconnectOptions;
|
|
45
|
+
}
|
|
46
|
+
export interface ReconnectOptions {
|
|
47
|
+
readonly enabled?: boolean;
|
|
48
|
+
readonly initialDelayMs?: number;
|
|
49
|
+
readonly maxDelayMs?: number;
|
|
50
|
+
readonly maxAttempts?: number;
|
|
51
|
+
}
|
|
52
|
+
export interface DbusTransport {
|
|
53
|
+
connect(): Promise<void>;
|
|
54
|
+
disconnect(): Promise<void>;
|
|
55
|
+
isConnected(): boolean;
|
|
56
|
+
callMethod(call: MethodCall): Promise<MethodReply>;
|
|
57
|
+
subscribeSignal(spec: SignalSpec, listener: SignalListener): Promise<Subscription>;
|
|
58
|
+
on(event: TransportEvent, handler: (payload?: unknown) => void): void;
|
|
59
|
+
off(event: TransportEvent, handler: (payload?: unknown) => void): void;
|
|
60
|
+
subscriptionCount(): number;
|
|
61
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Public value and message types for the D-Bus transport seam.
|
|
2
|
+
//
|
|
3
|
+
// These types are the transport's own vocabulary. None of the underlying
|
|
4
|
+
// `@httptoolkit/dbus-native` types appear here or in `./index.ts`, so the package's
|
|
5
|
+
// public surface never leaks the library — swapping the implementation (see README:
|
|
6
|
+
// documented fallback `@particle/dbus-next`) would not change a single caller type.
|
|
7
|
+
// Construct a variant for encoding, e.g. `variant('u', 42)` or `variant('t', 5n)`.
|
|
8
|
+
export function variant(signature, value) {
|
|
9
|
+
return { signature, value };
|
|
10
|
+
}
|
|
11
|
+
// Narrowing guard for a decoded variant.
|
|
12
|
+
export function isVariant(value) {
|
|
13
|
+
return (typeof value === 'object' &&
|
|
14
|
+
value !== null &&
|
|
15
|
+
!Array.isArray(value) &&
|
|
16
|
+
!(value instanceof Uint8Array) &&
|
|
17
|
+
typeof value.signature === 'string' &&
|
|
18
|
+
'value' in value);
|
|
19
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* The full canonical USB composition-mode vocabulary. A SKU's `canonicalMode` may be
|
|
4
|
+
* any of these; only the first three are ModemManager-manageable. (Structurally
|
|
5
|
+
* identical to `PreferredUsbMode` in the power contract — A4.2 owns this vocabulary.)
|
|
6
|
+
*/
|
|
7
|
+
export declare const CANONICAL_USB_MODES: readonly ['qmi', 'mbim', 'ecm-ncm', 'rndis', 'router-ethernet'];
|
|
8
|
+
export type CanonicalUsbMode = (typeof CANONICAL_USB_MODES)[number];
|
|
9
|
+
/**
|
|
10
|
+
* The ModemManager-manageable modes — the ONLY modes a transition may move between.
|
|
11
|
+
* `rndis` and `router-ethernet` are deliberately absent: a device in either is not
|
|
12
|
+
* MM-managed, so switching to/from them crosses the MM↔router line the schema forbids.
|
|
13
|
+
*/
|
|
14
|
+
export declare const MM_USB_MODES: readonly ['qmi', 'mbim', 'ecm-ncm'];
|
|
15
|
+
export type MmUsbMode = (typeof MM_USB_MODES)[number];
|
|
16
|
+
/** The USB descriptors a device must present AFTER a transition — the postcondition. */
|
|
17
|
+
export declare const expectedDescriptorsSchema: z.ZodObject<{
|
|
18
|
+
deviceClass: z.ZodNumber;
|
|
19
|
+
interfaces: z.ZodArray<z.ZodObject<{
|
|
20
|
+
interfaceClass: z.ZodNumber;
|
|
21
|
+
interfaceSubClass: z.ZodNumber;
|
|
22
|
+
interfaceProtocol: z.ZodNumber;
|
|
23
|
+
}, z.core.$strict>>;
|
|
24
|
+
}, z.core.$strict>;
|
|
25
|
+
export type ExpectedDescriptors = z.infer<typeof expectedDescriptorsSchema>;
|
|
26
|
+
/** One permitted, certified transition between two MM modes. */
|
|
27
|
+
export declare const permittedTransitionSchema: z.ZodObject<{
|
|
28
|
+
from: z.ZodEnum<{
|
|
29
|
+
"ecm-ncm": "ecm-ncm";
|
|
30
|
+
mbim: "mbim";
|
|
31
|
+
qmi: "qmi";
|
|
32
|
+
}>;
|
|
33
|
+
to: z.ZodEnum<{
|
|
34
|
+
"ecm-ncm": "ecm-ncm";
|
|
35
|
+
mbim: "mbim";
|
|
36
|
+
qmi: "qmi";
|
|
37
|
+
}>;
|
|
38
|
+
atCommand: z.ZodString;
|
|
39
|
+
applyCommand: z.ZodOptional<z.ZodString>;
|
|
40
|
+
expectedResponse: z.ZodString;
|
|
41
|
+
expectsPortDrop: z.ZodBoolean;
|
|
42
|
+
expectedDescriptors: z.ZodObject<{
|
|
43
|
+
deviceClass: z.ZodNumber;
|
|
44
|
+
interfaces: z.ZodArray<z.ZodObject<{
|
|
45
|
+
interfaceClass: z.ZodNumber;
|
|
46
|
+
interfaceSubClass: z.ZodNumber;
|
|
47
|
+
interfaceProtocol: z.ZodNumber;
|
|
48
|
+
}, z.core.$strict>>;
|
|
49
|
+
}, z.core.$strict>;
|
|
50
|
+
evidenceBundleSha256: z.ZodOptional<z.ZodString>;
|
|
51
|
+
}, z.core.$strict>;
|
|
52
|
+
export type PermittedTransition = z.infer<typeof permittedTransitionSchema>;
|
|
53
|
+
/** One certified SKU: its discriminator, canonical mode, and permitted transitions. */
|
|
54
|
+
export declare const catalogEntrySchema: z.ZodObject<{
|
|
55
|
+
vidPid: z.ZodString;
|
|
56
|
+
model: z.ZodString;
|
|
57
|
+
firmwarePrefix: z.ZodString;
|
|
58
|
+
canonicalMode: z.ZodEnum<{
|
|
59
|
+
"ecm-ncm": "ecm-ncm";
|
|
60
|
+
mbim: "mbim";
|
|
61
|
+
qmi: "qmi";
|
|
62
|
+
rndis: "rndis";
|
|
63
|
+
"router-ethernet": "router-ethernet";
|
|
64
|
+
}>;
|
|
65
|
+
permittedTransitions: z.ZodArray<z.ZodObject<{
|
|
66
|
+
from: z.ZodEnum<{
|
|
67
|
+
"ecm-ncm": "ecm-ncm";
|
|
68
|
+
mbim: "mbim";
|
|
69
|
+
qmi: "qmi";
|
|
70
|
+
}>;
|
|
71
|
+
to: z.ZodEnum<{
|
|
72
|
+
"ecm-ncm": "ecm-ncm";
|
|
73
|
+
mbim: "mbim";
|
|
74
|
+
qmi: "qmi";
|
|
75
|
+
}>;
|
|
76
|
+
atCommand: z.ZodString;
|
|
77
|
+
applyCommand: z.ZodOptional<z.ZodString>;
|
|
78
|
+
expectedResponse: z.ZodString;
|
|
79
|
+
expectsPortDrop: z.ZodBoolean;
|
|
80
|
+
expectedDescriptors: z.ZodObject<{
|
|
81
|
+
deviceClass: z.ZodNumber;
|
|
82
|
+
interfaces: z.ZodArray<z.ZodObject<{
|
|
83
|
+
interfaceClass: z.ZodNumber;
|
|
84
|
+
interfaceSubClass: z.ZodNumber;
|
|
85
|
+
interfaceProtocol: z.ZodNumber;
|
|
86
|
+
}, z.core.$strict>>;
|
|
87
|
+
}, z.core.$strict>;
|
|
88
|
+
evidenceBundleSha256: z.ZodOptional<z.ZodString>;
|
|
89
|
+
}, z.core.$strict>>;
|
|
90
|
+
}, z.core.$strict>;
|
|
91
|
+
export type CatalogEntry = z.infer<typeof catalogEntrySchema>;
|
|
92
|
+
/** The whole certified catalog file. */
|
|
93
|
+
export declare const certifiedCatalogSchema: z.ZodObject<{
|
|
94
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
95
|
+
entries: z.ZodArray<z.ZodObject<{
|
|
96
|
+
vidPid: z.ZodString;
|
|
97
|
+
model: z.ZodString;
|
|
98
|
+
firmwarePrefix: z.ZodString;
|
|
99
|
+
canonicalMode: z.ZodEnum<{
|
|
100
|
+
"ecm-ncm": "ecm-ncm";
|
|
101
|
+
mbim: "mbim";
|
|
102
|
+
qmi: "qmi";
|
|
103
|
+
rndis: "rndis";
|
|
104
|
+
"router-ethernet": "router-ethernet";
|
|
105
|
+
}>;
|
|
106
|
+
permittedTransitions: z.ZodArray<z.ZodObject<{
|
|
107
|
+
from: z.ZodEnum<{
|
|
108
|
+
"ecm-ncm": "ecm-ncm";
|
|
109
|
+
mbim: "mbim";
|
|
110
|
+
qmi: "qmi";
|
|
111
|
+
}>;
|
|
112
|
+
to: z.ZodEnum<{
|
|
113
|
+
"ecm-ncm": "ecm-ncm";
|
|
114
|
+
mbim: "mbim";
|
|
115
|
+
qmi: "qmi";
|
|
116
|
+
}>;
|
|
117
|
+
atCommand: z.ZodString;
|
|
118
|
+
applyCommand: z.ZodOptional<z.ZodString>;
|
|
119
|
+
expectedResponse: z.ZodString;
|
|
120
|
+
expectsPortDrop: z.ZodBoolean;
|
|
121
|
+
expectedDescriptors: z.ZodObject<{
|
|
122
|
+
deviceClass: z.ZodNumber;
|
|
123
|
+
interfaces: z.ZodArray<z.ZodObject<{
|
|
124
|
+
interfaceClass: z.ZodNumber;
|
|
125
|
+
interfaceSubClass: z.ZodNumber;
|
|
126
|
+
interfaceProtocol: z.ZodNumber;
|
|
127
|
+
}, z.core.$strict>>;
|
|
128
|
+
}, z.core.$strict>;
|
|
129
|
+
evidenceBundleSha256: z.ZodOptional<z.ZodString>;
|
|
130
|
+
}, z.core.$strict>>;
|
|
131
|
+
}, z.core.$strict>>;
|
|
132
|
+
}, z.core.$strict>;
|
|
133
|
+
export type CertifiedCatalog = z.infer<typeof certifiedCatalogSchema>;
|
|
134
|
+
/** A live device's SKU discriminator — matched against the catalog. */
|
|
135
|
+
export interface SkuDiscriminator {
|
|
136
|
+
readonly vidPid: string;
|
|
137
|
+
readonly model: string;
|
|
138
|
+
readonly firmwarePrefix: string;
|
|
139
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// The certified USB-mode catalog schema — the contract that gates a mode switch.
|
|
2
|
+
//
|
|
3
|
+
// A catalog ENTRY is one server-derived SKU discriminator (VID:PID + model +
|
|
4
|
+
// firmware prefix) plus everything a certified transition needs: the canonical mode
|
|
5
|
+
// the SKU speaks, the AT commands that switch it, the expected AT response and
|
|
6
|
+
// port-drop behaviour, the USB descriptors it should present AFTER the switch, and a
|
|
7
|
+
// forward-reference to the evidence bundle that certified it. Certification is a
|
|
8
|
+
// human-reviewed commit that adds an entry — evidence bundles are inputs to that
|
|
9
|
+
// review, never read at runtime.
|
|
10
|
+
//
|
|
11
|
+
// TWO safety invariants are enforced by the SCHEMA ITSELF, not by convention:
|
|
12
|
+
// 1. Permitted transitions are WITHIN-ModemManager only — `qmi ↔ mbim ↔ ecm-ncm`.
|
|
13
|
+
// A transition's `from`/`to` are typed to the MM-mode enum, so any attempt to
|
|
14
|
+
// declare an `MM ↔ router` (or `rndis`) transition FAILS to parse.
|
|
15
|
+
// 2. `.strict()` everywhere — an entry carrying an unknown field is rejected, so a
|
|
16
|
+
// typo or a smuggled extra field can never slip past review as valid data.
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
/**
|
|
19
|
+
* The full canonical USB composition-mode vocabulary. A SKU's `canonicalMode` may be
|
|
20
|
+
* any of these; only the first three are ModemManager-manageable. (Structurally
|
|
21
|
+
* identical to `PreferredUsbMode` in the power contract — A4.2 owns this vocabulary.)
|
|
22
|
+
*/
|
|
23
|
+
export const CANONICAL_USB_MODES = ['qmi', 'mbim', 'ecm-ncm', 'rndis', 'router-ethernet'];
|
|
24
|
+
/**
|
|
25
|
+
* The ModemManager-manageable modes — the ONLY modes a transition may move between.
|
|
26
|
+
* `rndis` and `router-ethernet` are deliberately absent: a device in either is not
|
|
27
|
+
* MM-managed, so switching to/from them crosses the MM↔router line the schema forbids.
|
|
28
|
+
*/
|
|
29
|
+
export const MM_USB_MODES = ['qmi', 'mbim', 'ecm-ncm'];
|
|
30
|
+
const canonicalMode = z.enum(CANONICAL_USB_MODES);
|
|
31
|
+
const mmMode = z.enum(MM_USB_MODES);
|
|
32
|
+
/** A `bInterfaceClass`/`bInterfaceSubClass`/`bInterfaceProtocol` byte triple. */
|
|
33
|
+
const usbByte = z.number().int().min(0).max(255);
|
|
34
|
+
/** The USB descriptors a device must present AFTER a transition — the postcondition. */
|
|
35
|
+
export const expectedDescriptorsSchema = z.strictObject({
|
|
36
|
+
deviceClass: usbByte,
|
|
37
|
+
interfaces: z
|
|
38
|
+
.array(z.strictObject({
|
|
39
|
+
interfaceClass: usbByte,
|
|
40
|
+
interfaceSubClass: usbByte,
|
|
41
|
+
interfaceProtocol: usbByte,
|
|
42
|
+
}))
|
|
43
|
+
.min(1),
|
|
44
|
+
});
|
|
45
|
+
/** One permitted, certified transition between two MM modes. */
|
|
46
|
+
export const permittedTransitionSchema = z
|
|
47
|
+
.strictObject({
|
|
48
|
+
from: mmMode,
|
|
49
|
+
to: mmMode,
|
|
50
|
+
/** The EXACT AT command that performs the switch (allowlisted at send time). */
|
|
51
|
+
atCommand: z.string().min(1),
|
|
52
|
+
/**
|
|
53
|
+
* The EXACT AT command that COMMITS the switch on a SKU whose `atCommand` only
|
|
54
|
+
* writes non-volatile configuration; omitted for a SKU that re-enumerates on
|
|
55
|
+
* `atCommand` alone. Sent through the SAME allowlisted lease.
|
|
56
|
+
*
|
|
57
|
+
* Declaring it is a per-SKU hardware FACT, not a retry. Measured on the bench
|
|
58
|
+
* RM530N-GL: `AT+QCFG="usbnet",<n>` answers `OK`, reads back the new value, and
|
|
59
|
+
* leaves the device in the OLD composition for the whole port-drop budget — having
|
|
60
|
+
* already committed to NV. Undeclared, such a SKU fails on a timeout AFTER it was
|
|
61
|
+
* silently changed, then lands in the new composition at the next unrelated reboot.
|
|
62
|
+
*/
|
|
63
|
+
applyCommand: z.string().min(1).optional(),
|
|
64
|
+
/** The AT response expected on success (e.g. `OK`) — never proof on its own. */
|
|
65
|
+
expectedResponse: z.string().min(1),
|
|
66
|
+
/** Whether the control port is expected to drop after the command is written. */
|
|
67
|
+
expectsPortDrop: z.boolean(),
|
|
68
|
+
/** The descriptors the device must present after re-enumerating — the postcondition. */
|
|
69
|
+
expectedDescriptors: expectedDescriptorsSchema,
|
|
70
|
+
/** sha256 of the evidence bundle that certified this transition (A6.2 fills it). */
|
|
71
|
+
evidenceBundleSha256: z
|
|
72
|
+
.string()
|
|
73
|
+
.regex(/^[0-9a-f]{64}$/)
|
|
74
|
+
.optional(),
|
|
75
|
+
})
|
|
76
|
+
.refine((t) => t.from !== t.to, { message: 'a transition must change mode (from !== to)' });
|
|
77
|
+
/** One certified SKU: its discriminator, canonical mode, and permitted transitions. */
|
|
78
|
+
export const catalogEntrySchema = z
|
|
79
|
+
.strictObject({
|
|
80
|
+
/** VID:PID, lowercase hex, `xxxx:xxxx`. */
|
|
81
|
+
vidPid: z.string().regex(/^[0-9a-f]{4}:[0-9a-f]{4}$/),
|
|
82
|
+
/** The server-derived model string (a discriminator, not free text). */
|
|
83
|
+
model: z.string().min(1),
|
|
84
|
+
/** The firmware-revision prefix that discriminates this personality. */
|
|
85
|
+
firmwarePrefix: z.string().min(1),
|
|
86
|
+
/** The mode this SKU speaks as certified. */
|
|
87
|
+
canonicalMode,
|
|
88
|
+
/** The certified transitions — empty for a non-MM (router/rndis) SKU. */
|
|
89
|
+
permittedTransitions: z.array(permittedTransitionSchema),
|
|
90
|
+
})
|
|
91
|
+
.refine((e) => e.permittedTransitions.length === 0 ||
|
|
92
|
+
MM_USB_MODES.includes(e.canonicalMode), { message: 'only an MM-mode SKU (qmi/mbim/ecm-ncm) may declare permitted transitions' });
|
|
93
|
+
/** The whole certified catalog file. */
|
|
94
|
+
export const certifiedCatalogSchema = z.strictObject({
|
|
95
|
+
schemaVersion: z.literal(1),
|
|
96
|
+
entries: z.array(catalogEntrySchema),
|
|
97
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CatalogEntry, type CertifiedCatalog, type MmUsbMode, type PermittedTransition, type SkuDiscriminator } from './catalog-schema.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parse and validate an untrusted value as a certified catalog. Throws a `ZodError`
|
|
4
|
+
* with a precise path if the value violates the schema (unknown field, an MM↔router
|
|
5
|
+
* transition, a bad VID:PID, etc.).
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadCertifiedCatalog(value: unknown): CertifiedCatalog;
|
|
8
|
+
/** The catalog shipped in this package, validated at module load. */
|
|
9
|
+
export declare const CERTIFIED_CATALOG: CertifiedCatalog;
|
|
10
|
+
/**
|
|
11
|
+
* Find the catalog entry matching a live device's SKU discriminator. All three
|
|
12
|
+
* discriminators (VID:PID, model, firmware prefix) must match — a partial match is
|
|
13
|
+
* NOT a certified device. Returns `undefined` for an uncertified SKU (never a guess).
|
|
14
|
+
*/
|
|
15
|
+
export declare function findCatalogEntry(catalog: CertifiedCatalog, sku: SkuDiscriminator): CatalogEntry | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Find the permitted transition `from → to` in a catalog entry. Returns `undefined`
|
|
18
|
+
* when the entry declares no such transition — the caller MUST treat that as "not
|
|
19
|
+
* permitted", never as "permitted with no command".
|
|
20
|
+
*/
|
|
21
|
+
export declare function findPermittedTransition(entry: CatalogEntry, from: MmUsbMode, to: MmUsbMode): PermittedTransition | undefined;
|