@ceralive/modem-control 0.2.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +317 -0
- package/dist/backend/at-lease.d.ts +57 -0
- package/dist/backend/at-lease.js +109 -0
- package/dist/backend/cell-info.d.ts +46 -0
- package/dist/backend/cell-info.js +124 -0
- package/dist/backend/constants.d.ts +24 -0
- package/{src/backend/constants.ts → dist/backend/constants.js} +4 -9
- package/dist/backend/device-classifier.d.ts +46 -0
- package/dist/backend/device-classifier.js +258 -0
- package/dist/backend/enrichment.d.ts +28 -0
- package/dist/backend/enrichment.js +59 -0
- package/dist/backend/features.d.ts +61 -0
- package/dist/backend/features.js +109 -0
- package/dist/backend/identity-ladder.d.ts +57 -0
- package/dist/backend/identity-ladder.js +158 -0
- package/dist/backend/identity-registry.d.ts +51 -0
- package/dist/backend/identity-registry.js +101 -0
- package/dist/backend/index.d.ts +30 -0
- package/dist/backend/index.js +35 -0
- package/dist/backend/lifecycle-interlock.d.ts +25 -0
- package/dist/backend/lifecycle-interlock.js +18 -0
- package/dist/backend/managed-objects.d.ts +39 -0
- package/dist/backend/managed-objects.js +82 -0
- package/dist/backend/mapping.d.ts +11 -0
- package/dist/backend/mapping.js +148 -0
- package/dist/backend/mm-backend.d.ts +44 -0
- package/dist/backend/mm-backend.js +154 -0
- package/dist/backend/mm-location.d.ts +21 -0
- package/dist/backend/mm-location.js +237 -0
- package/dist/backend/mm-mutations.d.ts +42 -0
- package/dist/backend/mm-mutations.js +259 -0
- package/dist/backend/modem-actor.d.ts +41 -0
- package/dist/backend/modem-actor.js +78 -0
- package/dist/backend/nm-auto-apn.d.ts +54 -0
- package/dist/backend/nm-auto-apn.js +124 -0
- package/dist/backend/nm-gsm-fields.d.ts +23 -0
- package/dist/backend/nm-gsm-fields.js +107 -0
- package/dist/backend/nmcli-nm-port.d.ts +28 -0
- package/dist/backend/nmcli-nm-port.js +173 -0
- package/dist/backend/nmcli-runner.d.ts +24 -0
- package/dist/backend/nmcli-runner.js +35 -0
- package/dist/backend/observer.d.ts +37 -0
- package/dist/backend/observer.js +219 -0
- package/dist/backend/power-contract.d.ts +49 -0
- package/dist/backend/power-contract.js +34 -0
- package/dist/backend/recovery-attribution.d.ts +32 -0
- package/dist/backend/recovery-attribution.js +57 -0
- package/dist/backend/recovery-budget.d.ts +45 -0
- package/dist/backend/recovery-budget.js +44 -0
- package/dist/backend/recovery-ladder.d.ts +94 -0
- package/dist/backend/recovery-ladder.js +116 -0
- package/dist/backend/router-ethernet.d.ts +19 -0
- package/dist/backend/router-ethernet.js +66 -0
- package/dist/backend/row-store.d.ts +13 -0
- package/dist/backend/row-store.js +82 -0
- package/dist/backend/signal-setup.d.ts +30 -0
- package/dist/backend/signal-setup.js +92 -0
- package/dist/backend/sim-unlock.d.ts +13 -0
- package/dist/backend/sim-unlock.js +153 -0
- package/dist/backend/transition-preconditions.d.ts +76 -0
- package/dist/backend/transition-preconditions.js +64 -0
- package/dist/backend/usage/accounting.d.ts +39 -0
- package/dist/backend/usage/accounting.js +73 -0
- package/dist/backend/usage/billing-cycle.d.ts +11 -0
- package/dist/backend/usage/billing-cycle.js +39 -0
- package/dist/backend/usage/boot-id.d.ts +6 -0
- package/{src/backend/usage/boot-id.ts → dist/backend/usage/boot-id.js} +7 -7
- package/dist/backend/usage/index.d.ts +8 -0
- package/dist/backend/usage/index.js +11 -0
- package/dist/backend/usage/policy-store.d.ts +50 -0
- package/dist/backend/usage/policy-store.js +161 -0
- package/dist/backend/usage/policy-write.d.ts +65 -0
- package/dist/backend/usage/policy-write.js +112 -0
- package/dist/backend/usage/proc-net-dev.d.ts +18 -0
- package/{src/backend/usage/proc-net-dev.ts → dist/backend/usage/proc-net-dev.js} +39 -47
- package/dist/backend/usage/sampler.d.ts +75 -0
- package/dist/backend/usage/sampler.js +211 -0
- package/dist/backend/usage/store.d.ts +38 -0
- package/dist/backend/usage/store.js +126 -0
- package/dist/backend/usb-device-snapshot.d.ts +31 -0
- package/dist/backend/usb-device-snapshot.js +1 -0
- package/dist/backend/usb-enumerator.d.ts +21 -0
- package/dist/backend/usb-enumerator.js +153 -0
- package/dist/backend/usb-mode-transition.d.ts +29 -0
- package/dist/backend/usb-mode-transition.js +196 -0
- package/dist/band/band-names.d.ts +42 -0
- package/dist/band/band-names.js +150 -0
- package/dist/band/certification.d.ts +84 -0
- package/dist/band/certification.js +127 -0
- package/dist/band/certified-bands.json +4 -0
- package/dist/band/index.d.ts +2 -0
- package/dist/band/index.js +8 -0
- package/dist/capability/detect.d.ts +52 -0
- package/dist/capability/detect.js +86 -0
- package/dist/capability/five-g-preference.d.ts +104 -0
- package/dist/capability/five-g-preference.js +171 -0
- package/dist/capability/index.d.ts +3 -0
- package/dist/capability/index.js +10 -0
- package/dist/capability/support-claim.d.ts +39 -0
- package/dist/capability/support-claim.js +81 -0
- package/dist/domain/brand.d.ts +10 -0
- package/dist/domain/brand.js +21 -0
- package/dist/domain/errors.d.ts +32 -0
- package/dist/domain/errors.js +48 -0
- package/dist/domain/generation.d.ts +8 -0
- package/dist/domain/generation.js +12 -0
- package/dist/domain/guards.d.ts +6 -0
- package/dist/domain/guards.js +127 -0
- package/dist/domain/identity.d.ts +109 -0
- package/dist/domain/identity.js +86 -0
- package/dist/domain/index.d.ts +14 -0
- package/dist/domain/index.js +18 -0
- package/dist/domain/mm-enums.d.ts +12 -0
- package/dist/domain/mm-enums.js +139 -0
- package/dist/domain/modem-presentation.d.ts +10 -0
- package/dist/domain/modem-presentation.js +39 -0
- package/dist/domain/observation.d.ts +39 -0
- package/dist/domain/observation.js +4 -0
- package/dist/domain/operation.d.ts +118 -0
- package/dist/domain/operation.js +85 -0
- package/dist/domain/physical-identity.d.ts +43 -0
- package/dist/domain/physical-identity.js +113 -0
- package/{src/domain/policy.ts → dist/domain/policy.d.ts} +34 -69
- package/dist/domain/policy.js +38 -0
- package/dist/domain/shadow-divergence.d.ts +27 -0
- package/dist/domain/shadow-divergence.js +70 -0
- package/dist/domain/snapshot.d.ts +53 -0
- package/dist/domain/snapshot.js +75 -0
- package/{src/domain/state.ts → dist/domain/state.d.ts} +23 -122
- package/dist/domain/state.js +38 -0
- package/dist/fcc/coverage.d.ts +63 -0
- package/dist/fcc/coverage.js +102 -0
- package/dist/fcc/index.d.ts +3 -0
- package/dist/fcc/index.js +12 -0
- package/dist/fcc/policy-store.d.ts +40 -0
- package/dist/fcc/policy-store.js +146 -0
- package/dist/fcc/policy-write.d.ts +32 -0
- package/dist/fcc/policy-write.js +37 -0
- package/dist/hardware/hilink-protocol.d.ts +39 -0
- package/dist/hardware/hilink-protocol.js +58 -0
- package/dist/hardware/index.d.ts +3 -0
- package/dist/hardware/index.js +15 -0
- package/dist/hardware/router-parsers.d.ts +89 -0
- package/dist/hardware/router-parsers.js +234 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +26 -0
- package/dist/journal/codec.d.ts +44 -0
- package/dist/journal/codec.js +198 -0
- package/dist/journal/engine.d.ts +28 -0
- package/dist/journal/engine.js +68 -0
- package/dist/journal/entry.d.ts +74 -0
- package/dist/journal/entry.js +56 -0
- package/dist/journal/index.d.ts +6 -0
- package/dist/journal/index.js +6 -0
- package/dist/journal/legacy-ceraui.d.ts +73 -0
- package/dist/journal/legacy-ceraui.js +227 -0
- package/dist/journal/recovery.d.ts +58 -0
- package/dist/journal/recovery.js +117 -0
- package/dist/journal/store.d.ts +55 -0
- package/dist/journal/store.js +150 -0
- package/dist/location/fix-state.d.ts +53 -0
- package/dist/location/fix-state.js +75 -0
- package/dist/location/index.d.ts +2 -0
- package/dist/location/index.js +8 -0
- package/dist/location/nmea.d.ts +10 -0
- package/dist/location/nmea.js +89 -0
- package/dist/observations/envelope.d.ts +63 -0
- package/dist/observations/envelope.js +77 -0
- package/dist/observations/freshness.d.ts +28 -0
- package/dist/observations/freshness.js +76 -0
- package/dist/observations/index.d.ts +13 -0
- package/dist/observations/index.js +24 -0
- package/dist/observations/metric.d.ts +47 -0
- package/dist/observations/metric.js +73 -0
- package/dist/observations/model.d.ts +80 -0
- package/dist/observations/model.js +11 -0
- package/dist/observations/provenance.d.ts +94 -0
- package/dist/observations/provenance.js +67 -0
- package/dist/observations/raw.d.ts +42 -0
- package/dist/observations/raw.js +146 -0
- package/dist/observations/reading.d.ts +51 -0
- package/dist/observations/reading.js +65 -0
- package/dist/observations/sources/hilink.d.ts +10 -0
- package/dist/observations/sources/hilink.js +84 -0
- package/dist/observations/sources/modemmanager.d.ts +18 -0
- package/dist/observations/sources/modemmanager.js +239 -0
- package/dist/observations/sources/router-shared.d.ts +29 -0
- package/dist/observations/sources/router-shared.js +68 -0
- package/dist/observations/sources/ufi.d.ts +10 -0
- package/dist/observations/sources/ufi.js +111 -0
- package/dist/observations/sources/zte.d.ts +7 -0
- package/dist/observations/sources/zte.js +71 -0
- package/dist/observations/state-separation.d.ts +64 -0
- package/dist/observations/state-separation.js +52 -0
- package/dist/operations/contracts.d.ts +72 -0
- package/dist/operations/contracts.js +1 -0
- package/dist/operations/index.d.ts +1 -0
- package/dist/operations/index.js +1 -0
- package/dist/operations/operation-engine.d.ts +16 -0
- package/dist/operations/operation-engine.js +194 -0
- package/dist/ports/index.d.ts +12 -0
- package/{src/ports/index.ts → dist/ports/index.js} +12 -8
- package/dist/ports/location.d.ts +87 -0
- package/dist/ports/location.js +36 -0
- package/dist/ports/modem-manager.d.ts +89 -0
- package/dist/ports/modem-manager.js +9 -0
- package/dist/ports/mutation-admission.d.ts +27 -0
- package/dist/ports/mutation-admission.js +9 -0
- package/dist/ports/network-manager.d.ts +68 -0
- package/dist/ports/network-manager.js +13 -0
- package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
- package/dist/ports/observation.js +7 -0
- package/dist/ports/ops.d.ts +44 -0
- package/dist/ports/ops.js +16 -0
- package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
- package/dist/ports/receipts.js +9 -0
- package/dist/ports/reconcile.d.ts +33 -0
- package/dist/ports/reconcile.js +200 -0
- package/dist/ports/resource-ownership.d.ts +29 -0
- package/dist/ports/resource-ownership.js +1 -0
- package/dist/ports/router.d.ts +19 -0
- package/dist/ports/router.js +7 -0
- package/dist/ports/sms.d.ts +64 -0
- package/dist/ports/sms.js +24 -0
- package/dist/ports/uhubctl.d.ts +6 -0
- package/dist/ports/uhubctl.js +1 -0
- package/dist/providers/contracts.d.ts +124 -0
- package/dist/providers/contracts.js +10 -0
- package/dist/providers/huawei-hilink/index.d.ts +2 -0
- package/dist/providers/huawei-hilink/index.js +2 -0
- package/dist/providers/huawei-hilink/operations.d.ts +20 -0
- package/dist/providers/huawei-hilink/operations.js +56 -0
- package/dist/providers/huawei-hilink/provider.d.ts +52 -0
- package/dist/providers/huawei-hilink/provider.js +76 -0
- package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
- package/dist/providers/huawei-hilink/runtime.js +171 -0
- package/dist/providers/huawei-hilink/session.d.ts +28 -0
- package/dist/providers/huawei-hilink/session.js +120 -0
- package/dist/providers/huawei-hilink/transport.d.ts +19 -0
- package/dist/providers/huawei-hilink/transport.js +1 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/matcher.d.ts +3 -0
- package/dist/providers/matcher.js +205 -0
- package/dist/providers/modem-manager/errors.d.ts +7 -0
- package/dist/providers/modem-manager/errors.js +37 -0
- package/dist/providers/modem-manager/generic-operations.d.ts +8 -0
- package/dist/providers/modem-manager/generic-operations.js +207 -0
- package/dist/providers/modem-manager/index.d.ts +3 -0
- package/dist/providers/modem-manager/index.js +3 -0
- package/dist/providers/modem-manager/module-operations.d.ts +21 -0
- package/dist/providers/modem-manager/module-operations.js +118 -0
- package/dist/providers/modem-manager/provider.d.ts +39 -0
- package/dist/providers/modem-manager/provider.js +152 -0
- package/dist/providers/modem-manager/snapshot.d.ts +5 -0
- package/dist/providers/modem-manager/snapshot.js +204 -0
- package/dist/providers/modem-manager/types.d.ts +135 -0
- package/dist/providers/modem-manager/types.js +1 -0
- package/dist/providers/network-manager/adapter.d.ts +71 -0
- package/dist/providers/network-manager/adapter.js +348 -0
- package/dist/providers/network-manager/index.d.ts +2 -0
- package/dist/providers/network-manager/index.js +2 -0
- package/dist/providers/network-manager/types.d.ts +163 -0
- package/dist/providers/network-manager/types.js +77 -0
- package/dist/providers/registry.d.ts +13 -0
- package/dist/providers/registry.js +33 -0
- package/dist/providers/ufi-himi/index.d.ts +6 -0
- package/dist/providers/ufi-himi/index.js +6 -0
- package/dist/providers/ufi-himi/operations.d.ts +41 -0
- package/dist/providers/ufi-himi/operations.js +66 -0
- package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
- package/dist/providers/ufi-himi/prohibitions.js +88 -0
- package/dist/providers/ufi-himi/provider.d.ts +41 -0
- package/dist/providers/ufi-himi/provider.js +204 -0
- package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
- package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
- package/dist/providers/ufi-himi/session.d.ts +46 -0
- package/dist/providers/ufi-himi/session.js +92 -0
- package/dist/providers/ufi-himi/transport.d.ts +29 -0
- package/dist/providers/ufi-himi/transport.js +25 -0
- package/dist/providers/zte-goform/index.d.ts +2 -0
- package/dist/providers/zte-goform/index.js +2 -0
- package/dist/providers/zte-goform/provider.d.ts +51 -0
- package/dist/providers/zte-goform/provider.js +101 -0
- package/dist/providers/zte-goform/session.d.ts +17 -0
- package/dist/providers/zte-goform/session.js +133 -0
- package/dist/providers/zte-goform/transport.d.ts +16 -0
- package/dist/providers/zte-goform/transport.js +1 -0
- package/dist/radio/band-truth.d.ts +50 -0
- package/dist/radio/band-truth.js +92 -0
- package/dist/radio/index.d.ts +3 -0
- package/dist/radio/index.js +10 -0
- package/dist/radio/mode-combinations.d.ts +78 -0
- package/dist/radio/mode-combinations.js +198 -0
- package/dist/radio/mode-truth.d.ts +67 -0
- package/dist/radio/mode-truth.js +112 -0
- package/dist/redact.d.ts +15 -0
- package/dist/redact.js +189 -0
- package/dist/safety/composition-root.d.ts +28 -0
- package/dist/safety/composition-root.js +65 -0
- package/dist/safety/flock-resource-ownership.d.ts +11 -0
- package/dist/safety/flock-resource-ownership.js +151 -0
- package/dist/safety/index.d.ts +2 -0
- package/dist/safety/index.js +2 -0
- package/dist/sms/dbus-messaging.d.ts +17 -0
- package/dist/sms/dbus-messaging.js +185 -0
- package/dist/sms/inbox-store.d.ts +10 -0
- package/dist/sms/inbox-store.js +82 -0
- package/dist/sms/index.d.ts +4 -0
- package/dist/sms/index.js +10 -0
- package/dist/sms/mmcli-parse.d.ts +54 -0
- package/dist/sms/mmcli-parse.js +224 -0
- package/dist/sms/normalize.d.ts +42 -0
- package/dist/sms/normalize.js +95 -0
- package/dist/testing/domain-fakes.d.ts +58 -0
- package/dist/testing/domain-fakes.js +98 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +17 -0
- package/dist/testing/provider-fakes.d.ts +45 -0
- package/dist/testing/provider-fakes.js +64 -0
- package/dist/transport/calls.d.ts +9 -0
- package/dist/transport/calls.js +88 -0
- package/dist/transport/codec.d.ts +3 -0
- package/dist/transport/codec.js +207 -0
- package/dist/transport/dbus-native.d.ts +57 -0
- package/dist/transport/dbus-native.js +17 -0
- package/dist/transport/errors.d.ts +21 -0
- package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
- package/dist/transport/index.d.ts +4 -0
- package/dist/transport/index.js +9 -0
- package/dist/transport/signals.d.ts +15 -0
- package/dist/transport/signals.js +123 -0
- package/dist/transport/signature.d.ts +7 -0
- package/dist/transport/signature.js +94 -0
- package/dist/transport/transport.d.ts +2 -0
- package/dist/transport/transport.js +202 -0
- package/dist/transport/types.d.ts +61 -0
- package/dist/transport/types.js +19 -0
- package/dist/usb-mode/catalog-schema.d.ts +139 -0
- package/dist/usb-mode/catalog-schema.js +97 -0
- package/dist/usb-mode/catalog.d.ts +21 -0
- package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
- package/dist/usb-mode/certified-catalog.json +67 -0
- package/dist/usb-mode/index.d.ts +5 -0
- package/dist/usb-mode/index.js +15 -0
- package/dist/usb-mode/ingestion.d.ts +111 -0
- package/dist/usb-mode/ingestion.js +187 -0
- package/dist/usb-mode/promotion-review.d.ts +21 -0
- package/dist/usb-mode/promotion-review.js +87 -0
- package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
- package/dist/usb-mode/usb-devices-parse.js +157 -0
- package/dist/ussd/calls.d.ts +32 -0
- package/dist/ussd/calls.js +96 -0
- package/dist/ussd/index.d.ts +5 -0
- package/dist/ussd/index.js +12 -0
- package/dist/ussd/mm-ussd.d.ts +37 -0
- package/dist/ussd/mm-ussd.js +205 -0
- package/dist/ussd/refusal.d.ts +53 -0
- package/dist/ussd/refusal.js +154 -0
- package/dist/ussd/registration.d.ts +20 -0
- package/dist/ussd/registration.js +101 -0
- package/dist/ussd/session.d.ts +84 -0
- package/dist/ussd/session.js +163 -0
- package/package.json +38 -4
- package/src/backend/at-lease.test.ts +0 -106
- package/src/backend/at-lease.ts +0 -158
- package/src/backend/cell-info.test.ts +0 -154
- package/src/backend/cell-info.ts +0 -160
- package/src/backend/device-classifier.test.ts +0 -168
- package/src/backend/device-classifier.ts +0 -248
- package/src/backend/enrichment.ts +0 -96
- package/src/backend/features.test.ts +0 -162
- package/src/backend/features.ts +0 -179
- package/src/backend/identity-ladder.test.ts +0 -117
- package/src/backend/identity-ladder.ts +0 -221
- package/src/backend/identity-registry.test.ts +0 -89
- package/src/backend/identity-registry.ts +0 -151
- package/src/backend/index.ts +0 -221
- package/src/backend/lifecycle-interlock.ts +0 -38
- package/src/backend/managed-objects.ts +0 -108
- package/src/backend/mapping.ts +0 -160
- package/src/backend/mm-backend.ts +0 -191
- package/src/backend/mm-mutations.ts +0 -228
- package/src/backend/modem-actor.test.ts +0 -95
- package/src/backend/modem-actor.ts +0 -112
- package/src/backend/nm-auto-apn.ts +0 -161
- package/src/backend/nm-gsm-fields.ts +0 -122
- package/src/backend/nmcli-nm-port.ts +0 -228
- package/src/backend/nmcli-runner.ts +0 -52
- package/src/backend/observer.ts +0 -297
- package/src/backend/power-contract.test.ts +0 -40
- package/src/backend/power-contract.ts +0 -83
- package/src/backend/recovery-attribution.test.ts +0 -102
- package/src/backend/recovery-attribution.ts +0 -86
- package/src/backend/recovery-budget.test.ts +0 -64
- package/src/backend/recovery-budget.ts +0 -84
- package/src/backend/recovery-ladder.test.ts +0 -257
- package/src/backend/recovery-ladder.ts +0 -249
- package/src/backend/router-ethernet.test.ts +0 -71
- package/src/backend/router-ethernet.ts +0 -90
- package/src/backend/row-store.ts +0 -105
- package/src/backend/signal-setup.ts +0 -112
- package/src/backend/sim-unlock.ts +0 -193
- package/src/backend/transition-preconditions.ts +0 -149
- package/src/backend/usage/accounting.test.ts +0 -147
- package/src/backend/usage/accounting.ts +0 -123
- package/src/backend/usage/billing-cycle.test.ts +0 -62
- package/src/backend/usage/billing-cycle.ts +0 -45
- package/src/backend/usage/index.ts +0 -37
- package/src/backend/usage/proc-net-dev.test.ts +0 -56
- package/src/backend/usage/sampler.test.ts +0 -219
- package/src/backend/usage/sampler.ts +0 -228
- package/src/backend/usage/store.test.ts +0 -148
- package/src/backend/usage/store.ts +0 -177
- package/src/backend/usb-enumerator.test.ts +0 -87
- package/src/backend/usb-enumerator.ts +0 -181
- package/src/backend/usb-mode-transition.test.ts +0 -323
- package/src/backend/usb-mode-transition.ts +0 -253
- package/src/domain/brand.ts +0 -29
- package/src/domain/errors.ts +0 -77
- package/src/domain/guards.test.ts +0 -218
- package/src/domain/guards.ts +0 -144
- package/src/domain/identity.test.ts +0 -83
- package/src/domain/identity.ts +0 -165
- package/src/domain/index.ts +0 -12
- package/src/domain/snapshot.test.ts +0 -266
- package/src/domain/snapshot.ts +0 -120
- package/src/index.test.ts +0 -6
- package/src/index.ts +0 -15
- package/src/ports/README.md +0 -61
- package/src/ports/forbidden-surface.test.ts +0 -241
- package/src/ports/modem-manager.ts +0 -72
- package/src/ports/network-manager.ts +0 -87
- package/src/ports/ops.ts +0 -60
- package/src/ports/ops.type-test.ts +0 -39
- package/src/ports/receipts.test.ts +0 -153
- package/src/ports/reconcile.test.ts +0 -152
- package/src/ports/reconcile.ts +0 -338
- package/src/ports/router.ts +0 -29
- package/src/redact.test.ts +0 -82
- package/src/redact.ts +0 -73
- package/src/transport/README.md +0 -65
- package/src/transport/calls.ts +0 -113
- package/src/transport/characterization.test.ts +0 -260
- package/src/transport/codec.test.ts +0 -118
- package/src/transport/codec.ts +0 -240
- package/src/transport/conformance-python.test.ts +0 -152
- package/src/transport/conformance-same-lib.test.ts +0 -115
- package/src/transport/dbus-native-lib.d.ts +0 -19
- package/src/transport/dbus-native.ts +0 -85
- package/src/transport/index.ts +0 -30
- package/src/transport/no-library-leak.test.ts +0 -61
- package/src/transport/reliability.test.ts +0 -173
- package/src/transport/signals.ts +0 -150
- package/src/transport/signature.ts +0 -110
- package/src/transport/test-support/fake-service.ts +0 -168
- package/src/transport/test-support/independent-producer.py +0 -110
- package/src/transport/test-support/private-bus.ts +0 -66
- package/src/transport/transport.ts +0 -250
- package/src/transport/types.ts +0 -118
- package/src/usb-mode/catalog-schema.test.ts +0 -181
- package/src/usb-mode/catalog-schema.ts +0 -113
- package/src/usb-mode/certified-catalog.json +0 -67
- package/src/usb-mode/index.ts +0 -27
package/src/transport/README.md
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# D-Bus transport seam
|
|
2
|
-
|
|
3
|
-
A minimal internal transport interface over [`@httptoolkit/dbus-native`](https://www.npmjs.com/package/@httptoolkit/dbus-native).
|
|
4
|
-
Everything the rest of `@ceralive/modem-control` needs to talk to ModemManager over
|
|
5
|
-
D-Bus goes through the `DbusTransport` interface exported by [`index.ts`](./index.ts) —
|
|
6
|
-
method calls, signal subscriptions, and reconnect. The A3.x D-Bus backend builds
|
|
7
|
-
directly on this shape.
|
|
8
|
-
|
|
9
|
-
## Why this library
|
|
10
|
-
|
|
11
|
-
`@httptoolkit/dbus-native` is a pure-JavaScript D-Bus client (no libdbus / native
|
|
12
|
-
addon), which is the deciding factor: it imports and runs under **Bun 1.3.14** with
|
|
13
|
-
**EXTERNAL** auth on a session bus — session-verified during A2.4, and pinned exactly at
|
|
14
|
-
`0.1.5`. A native-addon client (anything binding libdbus) is a portability and
|
|
15
|
-
cross-compile liability for the arm64 + amd64 device image; a pure-JS client is not.
|
|
16
|
-
|
|
17
|
-
## The fallback
|
|
18
|
-
|
|
19
|
-
If `@httptoolkit/dbus-native` proves inadequate — an un-fixable marshalling bug, an
|
|
20
|
-
unmaintained upstream, or a Bun incompatibility introduced by a future runtime bump —
|
|
21
|
-
the documented fallback is [`@particle/dbus-next`](https://www.npmjs.com/package/@particle/dbus-next),
|
|
22
|
-
which is also pure-JS and was verified importable under Bun during planning (draft
|
|
23
|
-
ledger §dbus-native). It is **not** implemented here; this note records the escape hatch.
|
|
24
|
-
Because the entire library surface is quarantined behind this seam (see below), swapping
|
|
25
|
-
to it would touch only [`dbus-native.ts`](./dbus-native.ts), [`transport.ts`](./transport.ts),
|
|
26
|
-
and [`codec.ts`](./codec.ts) — never a caller.
|
|
27
|
-
|
|
28
|
-
## The seam contract
|
|
29
|
-
|
|
30
|
-
* **No library types leak.** `index.ts` re-exports only the transport's own types
|
|
31
|
-
(`DbusValue`, `DbusVariant`, `MethodCall`, `SignalEvent`, …). The raw library types
|
|
32
|
-
live in [`dbus-native.ts`](./dbus-native.ts) and go no further. A guard test asserts
|
|
33
|
-
the package entry (`../index.ts`) never re-exports the library.
|
|
34
|
-
* **Lossless 64-bit.** D-Bus `x` (INT64) and `t` (UINT64) are `bigint` end-to-end, never
|
|
35
|
-
a JS `number`. On decode the library runs with `ReturnLongjs: true` and we convert its
|
|
36
|
-
Long.js objects to `bigint` via their exact decimal string; on encode we require a
|
|
37
|
-
`bigint` and hand the library a decimal string (its only lossless 64-bit input).
|
|
38
|
-
Passing a `number` for a 64-bit field throws `BigIntRequiredError`.
|
|
39
|
-
* **`h` is unsupported.** UNIX_FD / file-descriptor passing is rejected up front with a
|
|
40
|
-
typed `UnsupportedSignatureError` — in an outgoing signature, a reply/signal signature,
|
|
41
|
-
or nested in a variant — never silently dropped or coerced.
|
|
42
|
-
* **Variants round-trip.** A decoded variant keeps its inner signature
|
|
43
|
-
(`DbusVariant { signature, value }`), so encode/decode is symmetric.
|
|
44
|
-
|
|
45
|
-
## Conformance
|
|
46
|
-
|
|
47
|
-
Tests run under `dbus-run-session -- bun test control/src/transport` and prove the seam
|
|
48
|
-
two independent ways:
|
|
49
|
-
|
|
50
|
-
1. **Same library** ([`conformance-same-lib.test.ts`](./conformance-same-lib.test.ts)) —
|
|
51
|
-
round-trips representative signatures against a minimal fake service built on the same
|
|
52
|
-
`@httptoolkit/dbus-native`.
|
|
53
|
-
2. **Independent producer** ([`conformance-python.test.ts`](./conformance-python.test.ts)) —
|
|
54
|
-
round-trips against a `python3-dbus` (`dbus-python`) service in a subprocess,
|
|
55
|
-
exercising `a{oa{sa{sv}}}`, `x`/`t` above 2^53, variants, and `PropertiesChanged`
|
|
56
|
-
invalidations. A different implementation on the wire is the real proof our codec is
|
|
57
|
-
correct, not merely self-consistent.
|
|
58
|
-
|
|
59
|
-
Plus [`reliability.test.ts`](./reliability.test.ts): reconnect after a bus restart
|
|
60
|
-
(against a dedicated private `dbus-daemon`), a ≥5000-event signal stream, late replies,
|
|
61
|
-
and a 100-cycle subscribe/unsubscribe listener-leak check.
|
|
62
|
-
|
|
63
|
-
The `test-support/` fakes here are intentionally minimal — just enough to round-trip the
|
|
64
|
-
signatures under test. The MM-faithful fake service (root ObjectManager, `Modem` /
|
|
65
|
-
`Modem3gpp` interfaces, SIM objects, bearer tripwires) is a separate, later task (A2.3).
|
package/src/transport/calls.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
// Method-call dispatch and reply correlation for the D-Bus transport seam.
|
|
2
|
-
//
|
|
3
|
-
// A call marshals its body through the codec, registers a pending record so a bus drop can
|
|
4
|
-
// reject it, and installs a per-call reply callback guarded by a `done` flag — so a reply
|
|
5
|
-
// that arrives after the call already timed out or the connection dropped is silently
|
|
6
|
-
// ignored rather than mis-correlated onto a stale promise. `rejectAll` mass-rejects every
|
|
7
|
-
// in-flight call when the connection drops.
|
|
8
|
-
|
|
9
|
-
import { decodeBody, encodeBody } from './codec';
|
|
10
|
-
import { messageType, type RawBus, type RawMessage, type ReplyContext } from './dbus-native';
|
|
11
|
-
import { DisconnectedError, TransportError } from './errors';
|
|
12
|
-
import type { DbusValue, MethodCall, MethodReply } from './types';
|
|
13
|
-
|
|
14
|
-
export const DEFAULT_CALL_TIMEOUT_MS = 30_000;
|
|
15
|
-
|
|
16
|
-
interface PendingCall {
|
|
17
|
-
settle(): void;
|
|
18
|
-
reject(error: unknown): void;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class CallDispatcher {
|
|
22
|
-
readonly #pending = new Set<PendingCall>();
|
|
23
|
-
readonly #callTimeoutMs: number;
|
|
24
|
-
|
|
25
|
-
constructor(callTimeoutMs: number) {
|
|
26
|
-
this.#callTimeoutMs = callTimeoutMs;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async call(bus: RawBus | null, connected: boolean, call: MethodCall): Promise<MethodReply> {
|
|
30
|
-
if (!connected || bus === null) {
|
|
31
|
-
throw new DisconnectedError('cannot call method: transport not connected');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const signature = call.signature ?? '';
|
|
35
|
-
const args = call.args ?? [];
|
|
36
|
-
const message: RawMessage = {
|
|
37
|
-
type: messageType.methodCall,
|
|
38
|
-
destination: call.destination,
|
|
39
|
-
path: call.path,
|
|
40
|
-
interface: call.interface,
|
|
41
|
-
member: call.member,
|
|
42
|
-
};
|
|
43
|
-
if (signature.length > 0) {
|
|
44
|
-
// Throws UnsupportedSignatureError / BigIntRequiredError before anything hits
|
|
45
|
-
// the wire.
|
|
46
|
-
message.signature = signature;
|
|
47
|
-
message.body = encodeBody(signature, args);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const timeoutMs = call.timeoutMs ?? this.#callTimeoutMs;
|
|
51
|
-
const pendingSet = this.#pending;
|
|
52
|
-
return new Promise<MethodReply>((resolve, reject) => {
|
|
53
|
-
let done = false;
|
|
54
|
-
const pending: PendingCall = {
|
|
55
|
-
settle: finish,
|
|
56
|
-
reject: (error) => {
|
|
57
|
-
finish();
|
|
58
|
-
reject(error);
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
function finish(): void {
|
|
63
|
-
if (done) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
done = true;
|
|
67
|
-
clearTimeout(timer);
|
|
68
|
-
pendingSet.delete(pending);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const timer = setTimeout(() => {
|
|
72
|
-
finish();
|
|
73
|
-
reject(
|
|
74
|
-
new TransportError(
|
|
75
|
-
`Method call ${call.interface}.${call.member} timed out after ${timeoutMs}ms`,
|
|
76
|
-
),
|
|
77
|
-
);
|
|
78
|
-
}, timeoutMs);
|
|
79
|
-
|
|
80
|
-
pendingSet.add(pending);
|
|
81
|
-
|
|
82
|
-
bus.invoke(
|
|
83
|
-
message,
|
|
84
|
-
function reply(this: ReplyContext, error: unknown, ...body: unknown[]): void {
|
|
85
|
-
if (done) {
|
|
86
|
-
// Reply arrived after timeout/disconnect already settled the promise — ignore.
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
finish();
|
|
90
|
-
if (error) {
|
|
91
|
-
reject(error instanceof Error ? error : new TransportError(String(error)));
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
try {
|
|
95
|
-
const replySignature = this.signature ?? '';
|
|
96
|
-
const decoded: DbusValue[] =
|
|
97
|
-
replySignature.length > 0 ? decodeBody(replySignature, body) : [];
|
|
98
|
-
resolve({ signature: replySignature, body: decoded });
|
|
99
|
-
} catch (decodeError) {
|
|
100
|
-
reject(decodeError);
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
);
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
rejectAll(cause: unknown): void {
|
|
108
|
-
for (const pending of this.#pending) {
|
|
109
|
-
pending.reject(cause);
|
|
110
|
-
}
|
|
111
|
-
this.#pending.clear();
|
|
112
|
-
}
|
|
113
|
-
}
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
// Characterization tests for the transport seam's reconnect / call-correlation / ordering
|
|
2
|
-
// edges — the behaviour that a later refactor (splitting transport.ts into lifecycle,
|
|
3
|
-
// call-dispatch, and signal modules) MUST preserve byte-for-byte at the observable level.
|
|
4
|
-
//
|
|
5
|
-
// These pin CURRENT behaviour, not aspirational behaviour: each assertion records what the
|
|
6
|
-
// unsplit transport.ts actually does today. If the split changes any of these observable
|
|
7
|
-
// facts, one of these tests goes red — which is the whole point.
|
|
8
|
-
//
|
|
9
|
-
// Like reliability.test.ts, these run against dedicated private `dbus-daemon` instances
|
|
10
|
-
// (each test owns one) so a destructive kill/restart is safe. That also makes the file
|
|
11
|
-
// self-contained — it needs no outer `dbus-run-session`.
|
|
12
|
-
|
|
13
|
-
import { describe, expect, test } from 'bun:test';
|
|
14
|
-
import { createDbusTransport } from './index';
|
|
15
|
-
import { FAKE_IFACE, FAKE_PATH, startFakeService, TICK_MEMBER } from './test-support/fake-service';
|
|
16
|
-
import { PrivateBus } from './test-support/private-bus';
|
|
17
|
-
|
|
18
|
-
const HAS_DBUS_DAEMON = Bun.which('dbus-daemon') !== null;
|
|
19
|
-
|
|
20
|
-
const sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));
|
|
21
|
-
|
|
22
|
-
async function waitFor(predicate: () => boolean, timeoutMs: number, label: string): Promise<void> {
|
|
23
|
-
const deadline = Date.now() + timeoutMs;
|
|
24
|
-
while (Date.now() < deadline) {
|
|
25
|
-
if (predicate()) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
await sleep(10);
|
|
29
|
-
}
|
|
30
|
-
throw new Error(`timed out after ${timeoutMs}ms waiting for ${label}`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const tickSpec = { interface: FAKE_IFACE, member: TICK_MEMBER, path: FAKE_PATH };
|
|
34
|
-
|
|
35
|
-
describe.skipIf(!HAS_DBUS_DAEMON)('transport characterization', () => {
|
|
36
|
-
// (i) A call that times out, then the real reply lands AFTER the timeout already
|
|
37
|
-
// settled the promise. The late reply must be silently ignored (each `bus.invoke`
|
|
38
|
-
// owns its own reply closure, guarded by a `done` flag) — no crash, and no
|
|
39
|
-
// mis-correlation onto a later, unrelated call.
|
|
40
|
-
test('a reply arriving after the call already timed out is silently ignored', async () => {
|
|
41
|
-
const bus = new PrivateBus();
|
|
42
|
-
await bus.start();
|
|
43
|
-
const fake = await startFakeService({ socket: bus.socket });
|
|
44
|
-
const transport = createDbusTransport({ socket: bus.socket, reconnect: { enabled: false } });
|
|
45
|
-
let errorEvents = 0;
|
|
46
|
-
transport.on('error', () => {
|
|
47
|
-
errorEvents += 1;
|
|
48
|
-
});
|
|
49
|
-
await transport.connect();
|
|
50
|
-
|
|
51
|
-
// SlowPing replies after 400ms; the call's own timeout is 100ms, so it times out
|
|
52
|
-
// first and the reply becomes a "late" one 300ms later.
|
|
53
|
-
await expect(
|
|
54
|
-
transport.callMethod({
|
|
55
|
-
destination: fake.busName,
|
|
56
|
-
path: FAKE_PATH,
|
|
57
|
-
interface: FAKE_IFACE,
|
|
58
|
-
member: 'SlowPing',
|
|
59
|
-
signature: 'u',
|
|
60
|
-
args: [400],
|
|
61
|
-
timeoutMs: 100,
|
|
62
|
-
}),
|
|
63
|
-
).rejects.toThrow('timed out after 100ms');
|
|
64
|
-
|
|
65
|
-
// Wait well past the 400ms reply so the ignored late reply has actually been
|
|
66
|
-
// delivered to (and dropped by) the settled callback.
|
|
67
|
-
await sleep(500);
|
|
68
|
-
|
|
69
|
-
// No crash, no error event, still connected — the late reply disturbed nothing.
|
|
70
|
-
expect(errorEvents).toBe(0);
|
|
71
|
-
expect(transport.isConnected()).toBe(true);
|
|
72
|
-
|
|
73
|
-
// And the pending machinery is intact: a fresh call correlates to its OWN reply,
|
|
74
|
-
// proving the late reply was not mis-delivered to a later promise.
|
|
75
|
-
const reply = await transport.callMethod({
|
|
76
|
-
destination: fake.busName,
|
|
77
|
-
path: FAKE_PATH,
|
|
78
|
-
interface: FAKE_IFACE,
|
|
79
|
-
member: 'Ping',
|
|
80
|
-
});
|
|
81
|
-
expect(reply.body[0]).toBe('pong');
|
|
82
|
-
|
|
83
|
-
await transport.disconnect();
|
|
84
|
-
await fake.stop();
|
|
85
|
-
await bus.stop();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// (ii) A bus drop while a call is in flight. The in-flight call must reject, and the
|
|
89
|
-
// observed ordering is pinned: the `disconnected` event is delivered BEFORE the call
|
|
90
|
-
// rejection is observed. (In `#handleDrop`, pending calls are rejected and then
|
|
91
|
-
// `disconnected` is emitted synchronously — but a promise rejection is observed on a
|
|
92
|
-
// microtask, so the synchronous event listener runs first.)
|
|
93
|
-
test('a mid-call bus drop rejects the in-flight call after emitting disconnected', async () => {
|
|
94
|
-
const bus = new PrivateBus();
|
|
95
|
-
await bus.start();
|
|
96
|
-
const fake = await startFakeService({ socket: bus.socket });
|
|
97
|
-
const transport = createDbusTransport({ socket: bus.socket, reconnect: { enabled: false } });
|
|
98
|
-
|
|
99
|
-
const order: string[] = [];
|
|
100
|
-
let rejection: unknown = null;
|
|
101
|
-
transport.on('disconnected', () => order.push('disconnected'));
|
|
102
|
-
await transport.connect();
|
|
103
|
-
|
|
104
|
-
// A call that will never get a reply — the bus dies under it.
|
|
105
|
-
const call = transport
|
|
106
|
-
.callMethod({
|
|
107
|
-
destination: fake.busName,
|
|
108
|
-
path: FAKE_PATH,
|
|
109
|
-
interface: FAKE_IFACE,
|
|
110
|
-
member: 'SlowPing',
|
|
111
|
-
signature: 'u',
|
|
112
|
-
args: [5000],
|
|
113
|
-
})
|
|
114
|
-
.catch((error: unknown) => {
|
|
115
|
-
order.push('call-rejected');
|
|
116
|
-
rejection = error;
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// Give the call time to reach the wire, then drop the bus under it.
|
|
120
|
-
await sleep(30);
|
|
121
|
-
bus.kill();
|
|
122
|
-
|
|
123
|
-
await waitFor(
|
|
124
|
-
() => order.includes('disconnected') && order.includes('call-rejected'),
|
|
125
|
-
5000,
|
|
126
|
-
'disconnected + call rejection',
|
|
127
|
-
);
|
|
128
|
-
await call;
|
|
129
|
-
|
|
130
|
-
// Pinned ordering: the event precedes the observed rejection.
|
|
131
|
-
expect(order).toEqual(['disconnected', 'call-rejected']);
|
|
132
|
-
// Pinned rejection type: a DisconnectedError (the connection-end drop cause).
|
|
133
|
-
expect(rejection).toBeInstanceOf(Error);
|
|
134
|
-
expect((rejection as Error).name).toBe('DisconnectedError');
|
|
135
|
-
|
|
136
|
-
await transport.disconnect();
|
|
137
|
-
// The fake died with its bus; stopping it would write to a closed stream. Leave it.
|
|
138
|
-
await bus.stop();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
// (iii) A drop that arrives while a reconnect is already running. The reconnect loop is
|
|
142
|
-
// idempotent: `#handleDrop` early-returns whenever the state is already `disconnected`
|
|
143
|
-
// or `reconnecting`, so repeated low-level drop signals never spawn a second reconnect
|
|
144
|
-
// loop. The transport converges to a single connected state — exactly one `disconnected`
|
|
145
|
-
// and one `reconnected`, no error, and it is not wedged.
|
|
146
|
-
test('a drop during an in-flight reconnect does not double-schedule or wedge', async () => {
|
|
147
|
-
const bus = new PrivateBus();
|
|
148
|
-
await bus.start();
|
|
149
|
-
let fake = await startFakeService({ socket: bus.socket });
|
|
150
|
-
const transport = createDbusTransport({
|
|
151
|
-
socket: bus.socket,
|
|
152
|
-
reconnect: { initialDelayMs: 25, maxDelayMs: 100 },
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
const events: string[] = [];
|
|
156
|
-
let errorEvents = 0;
|
|
157
|
-
transport.on('disconnected', () => events.push('disconnected'));
|
|
158
|
-
transport.on('reconnected', () => events.push('reconnected'));
|
|
159
|
-
transport.on('error', () => {
|
|
160
|
-
errorEvents += 1;
|
|
161
|
-
});
|
|
162
|
-
await transport.connect();
|
|
163
|
-
|
|
164
|
-
const ticks: bigint[] = [];
|
|
165
|
-
const subscription = await transport.subscribeSignal(tickSpec, (event) => {
|
|
166
|
-
ticks.push(event.body[0] as bigint);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
fake.emitTick(11n);
|
|
170
|
-
await waitFor(() => ticks.includes(11n), 3000, 'pre-drop tick');
|
|
171
|
-
|
|
172
|
-
// Drop the bus and leave it down long enough for the reconnect loop to spin through
|
|
173
|
-
// several failed establish attempts before the bus returns.
|
|
174
|
-
bus.kill();
|
|
175
|
-
await waitFor(() => events.includes('disconnected'), 5000, 'disconnected');
|
|
176
|
-
await sleep(150);
|
|
177
|
-
await bus.start();
|
|
178
|
-
await waitFor(() => events.includes('reconnected'), 15000, 'reconnected');
|
|
179
|
-
|
|
180
|
-
// A settle window to catch any spurious extra event from a double-scheduled loop.
|
|
181
|
-
await sleep(300);
|
|
182
|
-
|
|
183
|
-
expect(events.filter((event) => event === 'disconnected')).toHaveLength(1);
|
|
184
|
-
expect(events.filter((event) => event === 'reconnected')).toHaveLength(1);
|
|
185
|
-
expect(errorEvents).toBe(0);
|
|
186
|
-
|
|
187
|
-
// Not wedged: a fresh producer's signal flows through the auto-resubscribed rule.
|
|
188
|
-
fake = await startFakeService({ socket: bus.socket });
|
|
189
|
-
fake.emitTick(22n);
|
|
190
|
-
await waitFor(() => ticks.includes(22n), 8000, 'post-reconnect tick');
|
|
191
|
-
expect(transport.subscriptionCount()).toBe(1);
|
|
192
|
-
|
|
193
|
-
await subscription.unsubscribe();
|
|
194
|
-
await transport.disconnect();
|
|
195
|
-
await fake.stop();
|
|
196
|
-
await bus.stop();
|
|
197
|
-
}, 30000);
|
|
198
|
-
|
|
199
|
-
// (iv) A subscription added AND one removed while a reconnect is in progress. Because
|
|
200
|
-
// mutating a subscription while disconnected only touches the in-memory match-rule
|
|
201
|
-
// refcount (the bus call is skipped when not connected), and `#establish()` re-issues
|
|
202
|
-
// every live rule on reconnect, the refcounting must end up correct: the added
|
|
203
|
-
// subscription is registered (receives signals) and the removed one is not.
|
|
204
|
-
test('subscriptions mutated during reconnect end up correctly (un)registered', async () => {
|
|
205
|
-
const bus = new PrivateBus();
|
|
206
|
-
await bus.start();
|
|
207
|
-
const transport = createDbusTransport({
|
|
208
|
-
socket: bus.socket,
|
|
209
|
-
reconnect: { initialDelayMs: 25, maxDelayMs: 100 },
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
const events: string[] = [];
|
|
213
|
-
transport.on('disconnected', () => events.push('disconnected'));
|
|
214
|
-
transport.on('reconnected', () => events.push('reconnected'));
|
|
215
|
-
await transport.connect();
|
|
216
|
-
|
|
217
|
-
// `removed` is a path-filtered rule; `added` is a distinct (no-path) rule that still
|
|
218
|
-
// matches the same emitted Tick — so their match-rule strings differ and are tracked
|
|
219
|
-
// independently.
|
|
220
|
-
const removedTicks: bigint[] = [];
|
|
221
|
-
const addedTicks: bigint[] = [];
|
|
222
|
-
const removed = await transport.subscribeSignal(tickSpec, (event) => {
|
|
223
|
-
removedTicks.push(event.body[0] as bigint);
|
|
224
|
-
});
|
|
225
|
-
expect(transport.subscriptionCount()).toBe(1);
|
|
226
|
-
|
|
227
|
-
// Drop the bus; while the reconnect loop is running, mutate the subscription set.
|
|
228
|
-
bus.kill();
|
|
229
|
-
await waitFor(() => events.includes('disconnected'), 5000, 'disconnected');
|
|
230
|
-
|
|
231
|
-
await removed.unsubscribe();
|
|
232
|
-
const added = await transport.subscribeSignal(
|
|
233
|
-
{ interface: FAKE_IFACE, member: TICK_MEMBER },
|
|
234
|
-
(event) => {
|
|
235
|
-
addedTicks.push(event.body[0] as bigint);
|
|
236
|
-
},
|
|
237
|
-
);
|
|
238
|
-
expect(transport.subscriptionCount()).toBe(1);
|
|
239
|
-
|
|
240
|
-
// Bring the bus back; `#establish()` re-issues exactly the rules still in the
|
|
241
|
-
// refcount map — the `added` one, not the `removed` one.
|
|
242
|
-
await bus.start();
|
|
243
|
-
await waitFor(() => events.includes('reconnected'), 15000, 'reconnected');
|
|
244
|
-
|
|
245
|
-
const fake = await startFakeService({ socket: bus.socket });
|
|
246
|
-
fake.emitTick(33n);
|
|
247
|
-
await waitFor(() => addedTicks.includes(33n), 8000, 'added-subscription tick');
|
|
248
|
-
|
|
249
|
-
// Grace to prove the removed subscription genuinely receives nothing.
|
|
250
|
-
await sleep(200);
|
|
251
|
-
expect(addedTicks).toEqual([33n]);
|
|
252
|
-
expect(removedTicks).toEqual([]);
|
|
253
|
-
expect(transport.subscriptionCount()).toBe(1);
|
|
254
|
-
|
|
255
|
-
await added.unsubscribe();
|
|
256
|
-
await transport.disconnect();
|
|
257
|
-
await fake.stop();
|
|
258
|
-
await bus.stop();
|
|
259
|
-
}, 30000);
|
|
260
|
-
});
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
// Pure, in-process encode/decode round-trip: no bus, fully deterministic. Marshals with
|
|
2
|
-
// the library's own marshaller and reads it back with its DBusBuffer under
|
|
3
|
-
// `ReturnLongjs: true` — exactly the wire path a real call takes — so a green run here is
|
|
4
|
-
// exact proof the codec is lossless, independent of any daemon timing.
|
|
5
|
-
|
|
6
|
-
import { expect, test } from 'bun:test';
|
|
7
|
-
import DBusBuffer from '@httptoolkit/dbus-native/lib/dbus-buffer';
|
|
8
|
-
import marshall from '@httptoolkit/dbus-native/lib/marshall';
|
|
9
|
-
import { decodeBody, encodeBody } from './codec';
|
|
10
|
-
import { BigIntRequiredError, SixtyFourBitRangeError, UnsupportedSignatureError } from './errors';
|
|
11
|
-
import { type DbusValue, variant } from './types';
|
|
12
|
-
|
|
13
|
-
const INT64_MAX = 2n ** 63n - 1n;
|
|
14
|
-
const INT64_MIN = -(2n ** 63n);
|
|
15
|
-
const UINT64_MAX = 2n ** 64n - 1n;
|
|
16
|
-
|
|
17
|
-
function roundTrip(signature: string, values: readonly DbusValue[]): DbusValue[] {
|
|
18
|
-
const encoded = encodeBody(signature, values);
|
|
19
|
-
const buffer = marshall(signature, encoded);
|
|
20
|
-
const reader = new DBusBuffer(buffer, 0, { ayBuffer: true, ReturnLongjs: true });
|
|
21
|
-
const raw = reader.read(signature) as unknown[];
|
|
22
|
-
return decodeBody(signature, raw);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
test('2^63-1 round-trips through an INT64 exactly (bigint equality)', () => {
|
|
26
|
-
const [value] = roundTrip('x', [INT64_MAX]);
|
|
27
|
-
expect(value).toBe(INT64_MAX);
|
|
28
|
-
expect(typeof value).toBe('bigint');
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test('the full UINT64 range (2^64-1) round-trips exactly', () => {
|
|
32
|
-
const [value] = roundTrip('t', [UINT64_MAX]);
|
|
33
|
-
expect(value).toBe(UINT64_MAX);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('INT64 minimum round-trips exactly', () => {
|
|
37
|
-
const [value] = roundTrip('x', [INT64_MIN]);
|
|
38
|
-
expect(value).toBe(INT64_MIN);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test('a value just above 2^53 survives (no silent Number coercion)', () => {
|
|
42
|
-
const beyondSafe = 2n ** 53n + 1n;
|
|
43
|
-
const [asInt, asUint] = roundTrip('xt', [beyondSafe, beyondSafe]);
|
|
44
|
-
expect(asInt).toBe(beyondSafe);
|
|
45
|
-
expect(asUint).toBe(beyondSafe);
|
|
46
|
-
// The precision-losing path would have produced 9007199254740992n (== 2^53).
|
|
47
|
-
expect(asInt).not.toBe(2n ** 53n);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('the MM GetManagedObjects shape a{oa{sa{sv}}} round-trips with a 64-bit variant', () => {
|
|
51
|
-
const managed: DbusValue = [
|
|
52
|
-
[
|
|
53
|
-
'/org/freedesktop/ModemManager1/Modem/0',
|
|
54
|
-
[
|
|
55
|
-
[
|
|
56
|
-
'org.freedesktop.ModemManager1.Modem',
|
|
57
|
-
[
|
|
58
|
-
['SupportedCapabilities', variant('t', UINT64_MAX)],
|
|
59
|
-
['SignalQuality', variant('u', 87)],
|
|
60
|
-
['DeviceIdentifier', variant('s', 'dev-0')],
|
|
61
|
-
],
|
|
62
|
-
],
|
|
63
|
-
],
|
|
64
|
-
],
|
|
65
|
-
];
|
|
66
|
-
const [decoded] = roundTrip('a{oa{sa{sv}}}', [managed]);
|
|
67
|
-
expect(decoded).toEqual(managed);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test('a variant preserves its inner signature and 64-bit value across a round-trip', () => {
|
|
71
|
-
const [decoded] = roundTrip('v', [variant('t', UINT64_MAX)]);
|
|
72
|
-
expect(decoded).toEqual({ signature: 't', value: UINT64_MAX });
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test('a byte array (ay) round-trips as a Uint8Array', () => {
|
|
76
|
-
const bytes = new Uint8Array([0, 1, 2, 254, 255]);
|
|
77
|
-
const [decoded] = roundTrip('ay', [bytes]);
|
|
78
|
-
expect(decoded).toBeInstanceOf(Uint8Array);
|
|
79
|
-
expect(Array.from(decoded as Uint8Array)).toEqual([0, 1, 2, 254, 255]);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test('a struct with mixed 64-bit fields round-trips', () => {
|
|
83
|
-
const [decoded] = roundTrip('(xtu)', [[INT64_MIN, UINT64_MAX, 7]]);
|
|
84
|
-
expect(decoded).toEqual([INT64_MIN, UINT64_MAX, 7]);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('signature h (UNIX_FD) is rejected on encode with a typed error', () => {
|
|
88
|
-
expect(() => encodeBody('h', [0])).toThrow(UnsupportedSignatureError);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test('signature h nested inside a container is rejected on encode', () => {
|
|
92
|
-
expect(() => encodeBody('a{sh}', [[]])).toThrow(UnsupportedSignatureError);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('signature h is rejected on decode with a typed error', () => {
|
|
96
|
-
expect(() => decodeBody('h', [0])).toThrow(UnsupportedSignatureError);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test('the UnsupportedSignatureError carries the offending type', () => {
|
|
100
|
-
try {
|
|
101
|
-
encodeBody('h', [0]);
|
|
102
|
-
throw new Error('expected throw');
|
|
103
|
-
} catch (error) {
|
|
104
|
-
expect(error).toBeInstanceOf(UnsupportedSignatureError);
|
|
105
|
-
expect((error as UnsupportedSignatureError).unsupportedType).toBe('h');
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test('passing a JS number for a 64-bit field throws BigIntRequiredError', () => {
|
|
110
|
-
expect(() => encodeBody('t', [123])).toThrow(BigIntRequiredError);
|
|
111
|
-
expect(() => encodeBody('x', [123])).toThrow(BigIntRequiredError);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test('an out-of-range 64-bit bigint throws SixtyFourBitRangeError', () => {
|
|
115
|
-
expect(() => encodeBody('t', [-1n])).toThrow(SixtyFourBitRangeError);
|
|
116
|
-
expect(() => encodeBody('x', [2n ** 63n])).toThrow(SixtyFourBitRangeError);
|
|
117
|
-
expect(() => encodeBody('t', [2n ** 64n])).toThrow(SixtyFourBitRangeError);
|
|
118
|
-
});
|