@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
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
// Guard: the `@httptoolkit/dbus-native` library must never surface in a public export.
|
|
2
|
-
//
|
|
3
|
-
// The package entry (`control/src/index.ts`) and the transport seam's own public entry
|
|
4
|
-
// (`./index.ts`) may reference the library only through the quarantined facade
|
|
5
|
-
// (`./dbus-native.ts`) — never re-export it. Swapping the underlying library (documented
|
|
6
|
-
// fallback `@particle/dbus-next`) must stay invisible to every caller.
|
|
7
|
-
|
|
8
|
-
import { expect, test } from 'bun:test';
|
|
9
|
-
import { readdirSync } from 'node:fs';
|
|
10
|
-
import { join } from 'node:path';
|
|
11
|
-
import * as transportPublic from './index';
|
|
12
|
-
|
|
13
|
-
// A single-quoted module specifier — the shape of a real import/export, distinct from a
|
|
14
|
-
// prose mention of the library in a comment (those use backticks).
|
|
15
|
-
const LIBRARY_IMPORT = "'@httptoolkit/dbus-native'";
|
|
16
|
-
const transportDir = import.meta.dir;
|
|
17
|
-
const controlSrcDir = join(transportDir, '..');
|
|
18
|
-
|
|
19
|
-
test('the package entry does not import or re-export the D-Bus library', async () => {
|
|
20
|
-
const source = await Bun.file(join(controlSrcDir, 'index.ts')).text();
|
|
21
|
-
expect(source).not.toContain(LIBRARY_IMPORT);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test('the transport public entry does not import or re-export the D-Bus library', async () => {
|
|
25
|
-
const source = await Bun.file(join(transportDir, 'index.ts')).text();
|
|
26
|
-
expect(source).not.toContain(LIBRARY_IMPORT);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test('only the quarantined facade imports the D-Bus library from production modules', async () => {
|
|
30
|
-
// Enumerate every non-test production module in transport/ dynamically, so a NEW module
|
|
31
|
-
// (e.g. a future transport split) that imports the library directly is caught — the old
|
|
32
|
-
// fixed list never knew about files it did not name. The importer set must be EXACTLY the
|
|
33
|
-
// sanctioned facade, `dbus-native.ts`.
|
|
34
|
-
const productionModules = readdirSync(transportDir).filter(
|
|
35
|
-
(name) => name.endsWith('.ts') && !name.endsWith('.test.ts'),
|
|
36
|
-
);
|
|
37
|
-
const importers: string[] = [];
|
|
38
|
-
for (const moduleName of productionModules) {
|
|
39
|
-
const source = await Bun.file(join(transportDir, moduleName)).text();
|
|
40
|
-
if (source.includes(LIBRARY_IMPORT)) {
|
|
41
|
-
importers.push(moduleName);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
expect(importers.sort()).toEqual(['dbus-native.ts']);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test('the transport public surface exposes only the seam\u2019s own values', () => {
|
|
48
|
-
const exported = Object.keys(transportPublic).sort();
|
|
49
|
-
expect(exported).toEqual(
|
|
50
|
-
[
|
|
51
|
-
'BigIntRequiredError',
|
|
52
|
-
'DisconnectedError',
|
|
53
|
-
'SixtyFourBitRangeError',
|
|
54
|
-
'TransportError',
|
|
55
|
-
'UnsupportedSignatureError',
|
|
56
|
-
'createDbusTransport',
|
|
57
|
-
'isVariant',
|
|
58
|
-
'variant',
|
|
59
|
-
].sort(),
|
|
60
|
-
);
|
|
61
|
-
});
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
// Reliability tests for the transport seam: reconnect after a bus restart, a ≥5000-event
|
|
2
|
-
// signal stream, late replies, and a 100-cycle subscribe/unsubscribe leak check.
|
|
3
|
-
//
|
|
4
|
-
// These run against dedicated private `dbus-daemon` instances (not the outer session
|
|
5
|
-
// bus): the reconnect test must kill and respawn its bus, which is only safe on a bus we
|
|
6
|
-
// own. That also makes this file self-contained — it needs no `dbus-run-session`.
|
|
7
|
-
|
|
8
|
-
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
9
|
-
import type { DbusTransport } from './index';
|
|
10
|
-
import { createDbusTransport } from './index';
|
|
11
|
-
import {
|
|
12
|
-
FAKE_IFACE,
|
|
13
|
-
FAKE_PATH,
|
|
14
|
-
type FakeService,
|
|
15
|
-
startFakeService,
|
|
16
|
-
TICK_MEMBER,
|
|
17
|
-
} from './test-support/fake-service';
|
|
18
|
-
import { PrivateBus } from './test-support/private-bus';
|
|
19
|
-
|
|
20
|
-
const HAS_DBUS_DAEMON = Bun.which('dbus-daemon') !== null;
|
|
21
|
-
|
|
22
|
-
const sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));
|
|
23
|
-
|
|
24
|
-
async function waitFor(predicate: () => boolean, timeoutMs: number, label: string): Promise<void> {
|
|
25
|
-
const deadline = Date.now() + timeoutMs;
|
|
26
|
-
while (Date.now() < deadline) {
|
|
27
|
-
if (predicate()) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
await sleep(10);
|
|
31
|
-
}
|
|
32
|
-
throw new Error(`timed out after ${timeoutMs}ms waiting for ${label}`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const tickSpec = { interface: FAKE_IFACE, member: TICK_MEMBER, path: FAKE_PATH };
|
|
36
|
-
|
|
37
|
-
describe.skipIf(!HAS_DBUS_DAEMON)('transport reliability (shared private bus)', () => {
|
|
38
|
-
let bus: PrivateBus;
|
|
39
|
-
let fake: FakeService;
|
|
40
|
-
let transport: DbusTransport;
|
|
41
|
-
|
|
42
|
-
beforeAll(async () => {
|
|
43
|
-
bus = new PrivateBus();
|
|
44
|
-
await bus.start();
|
|
45
|
-
fake = await startFakeService({ socket: bus.socket });
|
|
46
|
-
transport = createDbusTransport({ socket: bus.socket });
|
|
47
|
-
await transport.connect();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
afterAll(async () => {
|
|
51
|
-
await transport.disconnect();
|
|
52
|
-
await fake.stop();
|
|
53
|
-
await bus.stop();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test('a late reply (reply delayed 600ms) still resolves the call correctly', async () => {
|
|
57
|
-
const reply = await transport.callMethod({
|
|
58
|
-
destination: fake.busName,
|
|
59
|
-
path: FAKE_PATH,
|
|
60
|
-
interface: FAKE_IFACE,
|
|
61
|
-
member: 'SlowPing',
|
|
62
|
-
signature: 'u',
|
|
63
|
-
args: [600],
|
|
64
|
-
});
|
|
65
|
-
expect(reply.body[0]).toBe('pong');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('a ≥5000-event signal stream is delivered completely, in order, as exact bigints', async () => {
|
|
69
|
-
const total = 5000;
|
|
70
|
-
const received: bigint[] = [];
|
|
71
|
-
const subscription = await transport.subscribeSignal(tickSpec, (event) => {
|
|
72
|
-
received.push(event.body[0] as bigint);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
for (let seq = 0; seq < total; seq += 1) {
|
|
76
|
-
fake.emitTick(BigInt(seq));
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
await waitFor(() => received.length >= total, 20_000, `${total} tick signals`);
|
|
80
|
-
expect(received.length).toBe(total);
|
|
81
|
-
expect(received[0]).toBe(0n);
|
|
82
|
-
expect(received[total - 1]).toBe(BigInt(total - 1));
|
|
83
|
-
// Order and exactness across the whole stream.
|
|
84
|
-
let ordered = true;
|
|
85
|
-
for (let i = 0; i < total; i += 1) {
|
|
86
|
-
if (received[i] !== BigInt(i)) {
|
|
87
|
-
ordered = false;
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
expect(ordered).toBe(true);
|
|
92
|
-
|
|
93
|
-
await subscription.unsubscribe();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test('100 subscribe/unsubscribe cycles leave no leaked listener', async () => {
|
|
97
|
-
const baseline = transport.subscriptionCount();
|
|
98
|
-
for (let cycle = 0; cycle < 100; cycle += 1) {
|
|
99
|
-
const subscription = await transport.subscribeSignal(tickSpec, () => undefined);
|
|
100
|
-
await subscription.unsubscribe();
|
|
101
|
-
}
|
|
102
|
-
expect(transport.subscriptionCount()).toBe(baseline);
|
|
103
|
-
|
|
104
|
-
// Behavioural proof: after all those cycles a fresh subscriber receives each signal
|
|
105
|
-
// exactly once — a leaked listener from an earlier cycle would double-deliver.
|
|
106
|
-
let deliveries = 0;
|
|
107
|
-
const subscription = await transport.subscribeSignal(tickSpec, () => {
|
|
108
|
-
deliveries += 1;
|
|
109
|
-
});
|
|
110
|
-
fake.emitTick(1n);
|
|
111
|
-
await waitFor(() => deliveries >= 1, 3_000, 'one tick after leak cycles');
|
|
112
|
-
await sleep(100);
|
|
113
|
-
expect(deliveries).toBe(1);
|
|
114
|
-
await subscription.unsubscribe();
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test.skipIf(!HAS_DBUS_DAEMON)(
|
|
119
|
-
'transport reconnects and resubscribes after a bus restart without a consumer-facing crash',
|
|
120
|
-
async () => {
|
|
121
|
-
const bus = new PrivateBus();
|
|
122
|
-
await bus.start();
|
|
123
|
-
let fake = await startFakeService({ socket: bus.socket });
|
|
124
|
-
const transport = createDbusTransport({
|
|
125
|
-
socket: bus.socket,
|
|
126
|
-
reconnect: { initialDelayMs: 25, maxDelayMs: 200 },
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
const events: string[] = [];
|
|
130
|
-
let consumerError: unknown = null;
|
|
131
|
-
transport.on('disconnected', () => events.push('disconnected'));
|
|
132
|
-
transport.on('reconnected', () => events.push('reconnected'));
|
|
133
|
-
transport.on('error', (error) => {
|
|
134
|
-
consumerError = error;
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
await transport.connect();
|
|
138
|
-
|
|
139
|
-
const ticks: bigint[] = [];
|
|
140
|
-
const subscription = await transport.subscribeSignal(tickSpec, (event) => {
|
|
141
|
-
ticks.push(event.body[0] as bigint);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
fake.emitTick(11n);
|
|
145
|
-
await waitFor(() => ticks.includes(11n), 3_000, 'pre-restart tick');
|
|
146
|
-
|
|
147
|
-
// Kill the bus mid-flight; the old fake dies with it.
|
|
148
|
-
bus.kill();
|
|
149
|
-
await waitFor(() => events.includes('disconnected'), 5_000, 'disconnected event');
|
|
150
|
-
|
|
151
|
-
// Bring the bus back at the same socket; the transport must reconnect on its own.
|
|
152
|
-
await bus.start();
|
|
153
|
-
await waitFor(() => events.includes('reconnected'), 15_000, 'reconnected event');
|
|
154
|
-
|
|
155
|
-
// A fresh producer on the restored bus; the transport auto-resubscribed, so its
|
|
156
|
-
// signal must arrive without the caller re-subscribing.
|
|
157
|
-
fake = await startFakeService({ socket: bus.socket });
|
|
158
|
-
fake.emitTick(22n);
|
|
159
|
-
await waitFor(() => ticks.includes(22n), 8_000, 'post-reconnect tick (resubscribe)');
|
|
160
|
-
|
|
161
|
-
expect(events).toContain('disconnected');
|
|
162
|
-
expect(events).toContain('reconnected');
|
|
163
|
-
expect(ticks).toContain(11n);
|
|
164
|
-
expect(ticks).toContain(22n);
|
|
165
|
-
expect(consumerError).toBeNull();
|
|
166
|
-
|
|
167
|
-
await subscription.unsubscribe();
|
|
168
|
-
await transport.disconnect();
|
|
169
|
-
await fake.stop();
|
|
170
|
-
await bus.stop();
|
|
171
|
-
},
|
|
172
|
-
30_000,
|
|
173
|
-
);
|
package/src/transport/signals.ts
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
// Signal subscription and match-rule tracking for the D-Bus transport seam.
|
|
2
|
-
//
|
|
3
|
-
// The transport's single persistent `message` listener fans out here: `dispatch` walks the
|
|
4
|
-
// live subscription registry and delivers each decoded signal to every matching listener.
|
|
5
|
-
// Match rules are refcounted so N subscriptions sharing a rule add/remove it on the bus
|
|
6
|
-
// exactly once, and `reissueRules` re-adds every live rule after a reconnect — so
|
|
7
|
-
// subscribing/unsubscribing never grows the connection's listener count (the 100-cycle
|
|
8
|
-
// leak check depends on this).
|
|
9
|
-
|
|
10
|
-
import { decodeBody } from './codec';
|
|
11
|
-
import { messageType, type RawBus, type RawMessage } from './dbus-native';
|
|
12
|
-
import type { DbusValue, SignalEvent, SignalListener, SignalSpec, Subscription } from './types';
|
|
13
|
-
|
|
14
|
-
// The live connection context the registry reads through. The transport supplies these so
|
|
15
|
-
// the registry always sees the current bus/connected state (which change across reconnects)
|
|
16
|
-
// rather than capturing a stale reference, and routes decode/listener failures to the
|
|
17
|
-
// transport's `error` event.
|
|
18
|
-
export interface SignalHost {
|
|
19
|
-
currentBus(): RawBus | null;
|
|
20
|
-
isConnected(): boolean;
|
|
21
|
-
emitError(error: unknown): void;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface SubscriptionRecord {
|
|
25
|
-
readonly id: number;
|
|
26
|
-
readonly spec: SignalSpec;
|
|
27
|
-
readonly listener: SignalListener;
|
|
28
|
-
readonly rule: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function buildMatchRule(spec: SignalSpec): string {
|
|
32
|
-
const parts = [`type='signal'`, `interface='${spec.interface}'`, `member='${spec.member}'`];
|
|
33
|
-
if (spec.path !== undefined) {
|
|
34
|
-
parts.push(`path='${spec.path}'`);
|
|
35
|
-
}
|
|
36
|
-
if (spec.sender !== undefined) {
|
|
37
|
-
parts.push(`sender='${spec.sender}'`);
|
|
38
|
-
}
|
|
39
|
-
return parts.join(',');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function signalMatches(spec: SignalSpec, message: RawMessage): boolean {
|
|
43
|
-
if (message.interface !== spec.interface || message.member !== spec.member) {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
if (spec.path !== undefined && message.path !== spec.path) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
if (spec.sender !== undefined && message.sender !== spec.sender) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export class SignalRegistry {
|
|
56
|
-
readonly #host: SignalHost;
|
|
57
|
-
readonly #subscriptions = new Map<number, SubscriptionRecord>();
|
|
58
|
-
readonly #matchRuleRefcount = new Map<string, number>();
|
|
59
|
-
#nextSubId = 1;
|
|
60
|
-
|
|
61
|
-
constructor(host: SignalHost) {
|
|
62
|
-
this.#host = host;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async subscribe(spec: SignalSpec, listener: SignalListener): Promise<Subscription> {
|
|
66
|
-
const rule = buildMatchRule(spec);
|
|
67
|
-
const id = this.#nextSubId++;
|
|
68
|
-
this.#subscriptions.set(id, { id, spec, listener, rule });
|
|
69
|
-
await this.#addMatchRule(rule);
|
|
70
|
-
|
|
71
|
-
let removed = false;
|
|
72
|
-
return {
|
|
73
|
-
unsubscribe: async (): Promise<void> => {
|
|
74
|
-
if (removed) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
removed = true;
|
|
78
|
-
this.#subscriptions.delete(id);
|
|
79
|
-
await this.#removeMatchRule(rule);
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
count(): number {
|
|
85
|
-
return this.#subscriptions.size;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Re-issue every live match rule against a freshly established bus so a reconnect
|
|
89
|
-
// resubscribes transparently. Called by `#establish` before it swaps in the new bus, so
|
|
90
|
-
// the fresh bus is passed in explicitly rather than read from the host.
|
|
91
|
-
async reissueRules(bus: RawBus): Promise<void> {
|
|
92
|
-
for (const rule of this.#matchRuleRefcount.keys()) {
|
|
93
|
-
await bus.addMatch(rule);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
dispatch(message: RawMessage): void {
|
|
98
|
-
if (message.type !== messageType.signal) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
for (const record of this.#subscriptions.values()) {
|
|
102
|
-
if (!signalMatches(record.spec, message)) {
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
let body: DbusValue[];
|
|
106
|
-
try {
|
|
107
|
-
const signature = message.signature ?? '';
|
|
108
|
-
body = signature.length > 0 ? decodeBody(signature, message.body ?? []) : [];
|
|
109
|
-
} catch (error) {
|
|
110
|
-
this.#host.emitError(error);
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
const event: SignalEvent = {
|
|
114
|
-
path: message.path ?? '',
|
|
115
|
-
interface: message.interface ?? '',
|
|
116
|
-
member: message.member ?? '',
|
|
117
|
-
sender: message.sender,
|
|
118
|
-
signature: message.signature ?? '',
|
|
119
|
-
body,
|
|
120
|
-
};
|
|
121
|
-
try {
|
|
122
|
-
record.listener(event);
|
|
123
|
-
} catch (error) {
|
|
124
|
-
this.#host.emitError(error);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
async #addMatchRule(rule: string): Promise<void> {
|
|
130
|
-
const current = this.#matchRuleRefcount.get(rule) ?? 0;
|
|
131
|
-
this.#matchRuleRefcount.set(rule, current + 1);
|
|
132
|
-
const bus = this.#host.currentBus();
|
|
133
|
-
if (current === 0 && this.#host.isConnected() && bus) {
|
|
134
|
-
await bus.addMatch(rule);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
async #removeMatchRule(rule: string): Promise<void> {
|
|
139
|
-
const current = this.#matchRuleRefcount.get(rule) ?? 0;
|
|
140
|
-
if (current <= 1) {
|
|
141
|
-
this.#matchRuleRefcount.delete(rule);
|
|
142
|
-
const bus = this.#host.currentBus();
|
|
143
|
-
if (current === 1 && this.#host.isConnected() && bus) {
|
|
144
|
-
await bus.removeMatch(rule).catch(() => undefined);
|
|
145
|
-
}
|
|
146
|
-
} else {
|
|
147
|
-
this.#matchRuleRefcount.set(rule, current - 1);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
@@ -1,110 +0,0 @@
|
|
|
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
|
-
|
|
10
|
-
import { UnsupportedSignatureError } from './errors';
|
|
11
|
-
|
|
12
|
-
export interface SignatureNode {
|
|
13
|
-
readonly type: string;
|
|
14
|
-
readonly child: readonly SignatureNode[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const CONTAINER_CLOSE: Record<string, string> = {
|
|
18
|
-
'{': '}',
|
|
19
|
-
'(': ')',
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
// Every basic and container type char D-Bus defines. `h` (UNIX_FD) is deliberately
|
|
23
|
-
// listed as *known* so the parser accepts it structurally — we reject it explicitly in
|
|
24
|
-
// `assertSupportedSignature` with a typed error rather than as a vague parse failure.
|
|
25
|
-
const KNOWN_TYPES = new Set('ybnqiuxtdsogarvhe{}()'.split(''));
|
|
26
|
-
|
|
27
|
-
// UNIX_FD: file-descriptor passing. Unsupported everywhere in this transport.
|
|
28
|
-
const UNSUPPORTED_TYPE = 'h';
|
|
29
|
-
|
|
30
|
-
// Throws UnsupportedSignatureError if the signature contains `h` anywhere (including
|
|
31
|
-
// nested inside arrays, structs, dict entries, or variants). Signatures are pure type
|
|
32
|
-
// strings, so a plain character scan is exact.
|
|
33
|
-
export function assertSupportedSignature(signature: string): void {
|
|
34
|
-
const index = signature.indexOf(UNSUPPORTED_TYPE);
|
|
35
|
-
if (index !== -1) {
|
|
36
|
-
throw new UnsupportedSignatureError(signature, UNSUPPORTED_TYPE);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Parse a full signature (which may contain several top-level complete types) into a
|
|
41
|
-
// flat list of nodes.
|
|
42
|
-
export function parseSignature(signature: string): SignatureNode[] {
|
|
43
|
-
let index = 0;
|
|
44
|
-
|
|
45
|
-
function next(): string | null {
|
|
46
|
-
if (index < signature.length) {
|
|
47
|
-
const char = signature[index] as string;
|
|
48
|
-
index += 1;
|
|
49
|
-
return char;
|
|
50
|
-
}
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function parseOne(char: string): SignatureNode {
|
|
55
|
-
if (!KNOWN_TYPES.has(char)) {
|
|
56
|
-
throw new Error(`Unknown D-Bus type "${char}" in signature "${signature}"`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const children: SignatureNode[] = [];
|
|
60
|
-
switch (char) {
|
|
61
|
-
case 'a': {
|
|
62
|
-
const element = next();
|
|
63
|
-
if (element === null) {
|
|
64
|
-
throw new Error(`Bad signature "${signature}": array with no element type`);
|
|
65
|
-
}
|
|
66
|
-
children.push(parseOne(element));
|
|
67
|
-
return { type: 'a', child: children };
|
|
68
|
-
}
|
|
69
|
-
case '{':
|
|
70
|
-
case '(': {
|
|
71
|
-
const close = CONTAINER_CLOSE[char];
|
|
72
|
-
let element = next();
|
|
73
|
-
while (element !== null && element !== close) {
|
|
74
|
-
children.push(parseOne(element));
|
|
75
|
-
element = next();
|
|
76
|
-
}
|
|
77
|
-
if (element === null) {
|
|
78
|
-
throw new Error(`Bad signature "${signature}": unterminated "${char}"`);
|
|
79
|
-
}
|
|
80
|
-
return { type: char, child: children };
|
|
81
|
-
}
|
|
82
|
-
default:
|
|
83
|
-
return { type: char, child: children };
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const nodes: SignatureNode[] = [];
|
|
88
|
-
let char = next();
|
|
89
|
-
while (char !== null) {
|
|
90
|
-
nodes.push(parseOne(char));
|
|
91
|
-
char = next();
|
|
92
|
-
}
|
|
93
|
-
return nodes;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Re-serialise a single parsed node back to its signature string. Used to recover the
|
|
97
|
-
// inner signature of a decoded variant, whose contained type the library hands us as a
|
|
98
|
-
// parse tree rather than a string.
|
|
99
|
-
export function signatureFromNode(node: SignatureNode): string {
|
|
100
|
-
switch (node.type) {
|
|
101
|
-
case 'a':
|
|
102
|
-
return `a${signatureFromNode(node.child[0] as SignatureNode)}`;
|
|
103
|
-
case '(':
|
|
104
|
-
return `(${node.child.map(signatureFromNode).join('')})`;
|
|
105
|
-
case '{':
|
|
106
|
-
return `{${node.child.map(signatureFromNode).join('')}}`;
|
|
107
|
-
default:
|
|
108
|
-
return node.type;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
// Minimal fake D-Bus service built on the SAME `@httptoolkit/dbus-native` library, for
|
|
2
|
-
// the half-(a) conformance tests. It is deliberately tiny — a handful of echo/describe
|
|
3
|
-
// methods and one signal — NOT the MM-faithful object model (root ObjectManager, Modem /
|
|
4
|
-
// Modem3gpp interfaces, SIM objects, bearer tripwires); that richer fake is task A2.3.
|
|
5
|
-
//
|
|
6
|
-
// The service runs with `ReturnLongjs: true` so 64-bit values it receives survive as
|
|
7
|
-
// Long objects and re-marshal losslessly on echo. Decode-direction methods return fixed,
|
|
8
|
-
// hand-built values in the library's native encode shape; encode-direction methods report
|
|
9
|
-
// back a canonical string of what they received, so the tests never depend on this
|
|
10
|
-
// transport's own codec to judge it.
|
|
11
|
-
|
|
12
|
-
import * as dbusNativeModule from '@httptoolkit/dbus-native';
|
|
13
|
-
|
|
14
|
-
export const FAKE_BUS_NAME = 'tv.ceralive.ModemStackFake';
|
|
15
|
-
export const FAKE_PATH = '/tv/ceralive/fake';
|
|
16
|
-
export const FAKE_IFACE = 'tv.ceralive.ModemStackFake.Conformance';
|
|
17
|
-
export const TICK_MEMBER = 'Tick';
|
|
18
|
-
|
|
19
|
-
export const INT64_MAX = 2n ** 63n - 1n;
|
|
20
|
-
export const UINT64_MAX = 2n ** 64n - 1n;
|
|
21
|
-
|
|
22
|
-
type MethodImpl = (...args: unknown[]) => unknown;
|
|
23
|
-
|
|
24
|
-
interface FakeBus {
|
|
25
|
-
connection: {
|
|
26
|
-
on(event: string, handler: (...args: unknown[]) => void): void;
|
|
27
|
-
once(event: string, handler: (...args: unknown[]) => void): void;
|
|
28
|
-
removeListener(event: string, handler: (...args: unknown[]) => void): void;
|
|
29
|
-
};
|
|
30
|
-
requestName(name: string, flags: number): Promise<unknown>;
|
|
31
|
-
setMethodCallHandler(
|
|
32
|
-
path: string,
|
|
33
|
-
iface: string,
|
|
34
|
-
member: string,
|
|
35
|
-
handler: [MethodImpl, string],
|
|
36
|
-
): void;
|
|
37
|
-
sendSignal(path: string, iface: string, member: string, signature: string, args: unknown[]): void;
|
|
38
|
-
disconnect(): Promise<void>;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
interface FakeModule {
|
|
42
|
-
createClient(options: { busAddress?: string; socket?: string; ReturnLongjs?: boolean }): FakeBus;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const fakeModule = ((dbusNativeModule as { default?: unknown }).default ??
|
|
46
|
-
dbusNativeModule) as unknown as FakeModule;
|
|
47
|
-
|
|
48
|
-
// Fixed GetManagedObjects reply in library-native encode shape: object path → interface
|
|
49
|
-
// name → property name → variant [signature, value]. Exercises the real MM
|
|
50
|
-
// `a{oa{sa{sv}}}` nesting plus a 64-bit variant (t = 2^64-1) and a string variant.
|
|
51
|
-
function managedObjectsValue(): unknown {
|
|
52
|
-
return [
|
|
53
|
-
[
|
|
54
|
-
'/org/freedesktop/ModemManager1/Modem/0',
|
|
55
|
-
[
|
|
56
|
-
[
|
|
57
|
-
'org.freedesktop.ModemManager1.Modem',
|
|
58
|
-
[
|
|
59
|
-
['SupportedCapabilities', ['t', UINT64_MAX.toString()]],
|
|
60
|
-
['SignalQuality', ['u', 87]],
|
|
61
|
-
['DeviceIdentifier', ['s', 'fake-device-0']],
|
|
62
|
-
],
|
|
63
|
-
],
|
|
64
|
-
],
|
|
65
|
-
],
|
|
66
|
-
];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface FakeService {
|
|
70
|
-
readonly busName: string;
|
|
71
|
-
emitTick(seq: bigint): void;
|
|
72
|
-
stop(): Promise<void>;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export interface FakeServiceOptions {
|
|
76
|
-
readonly busAddress?: string;
|
|
77
|
-
readonly socket?: string;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export async function startFakeService(options: FakeServiceOptions): Promise<FakeService> {
|
|
81
|
-
const clientOptions: { busAddress?: string; socket?: string; ReturnLongjs?: boolean } = {
|
|
82
|
-
ReturnLongjs: true,
|
|
83
|
-
};
|
|
84
|
-
if (options.socket !== undefined) {
|
|
85
|
-
clientOptions.socket = options.socket;
|
|
86
|
-
} else if (options.busAddress !== undefined) {
|
|
87
|
-
clientOptions.busAddress = options.busAddress;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const bus = fakeModule.createClient(clientOptions);
|
|
91
|
-
|
|
92
|
-
await new Promise<void>((resolve, reject) => {
|
|
93
|
-
const onConnect = (): void => {
|
|
94
|
-
bus.connection.removeListener('error', onError);
|
|
95
|
-
resolve();
|
|
96
|
-
};
|
|
97
|
-
const onError = (error: unknown): void => {
|
|
98
|
-
bus.connection.removeListener('connect', onConnect);
|
|
99
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
100
|
-
};
|
|
101
|
-
bus.connection.once('connect', onConnect);
|
|
102
|
-
bus.connection.once('error', onError);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
// After connect, swallow late socket errors so a killed bus (reconnect test) does not
|
|
106
|
-
// surface an unhandled EventEmitter 'error' from this helper's dead connection.
|
|
107
|
-
bus.connection.on('error', () => undefined);
|
|
108
|
-
|
|
109
|
-
// A reconnect/drop test kills the bus and ABANDONS this fake (stopping it would itself
|
|
110
|
-
// write to the closed stream). Any reply the library still owes — e.g. a SlowPing whose
|
|
111
|
-
// delay has not elapsed — would then be written to that dead stream when its timer
|
|
112
|
-
// fires, throwing "Can't write a message to a closed stream" ASYNCHRONOUSLY, seconds
|
|
113
|
-
// later, inside whatever unrelated test happens to be running. Track the reply timers
|
|
114
|
-
// and cancel them the instant the connection stream ends, so a dead fake never writes.
|
|
115
|
-
const pendingReplyTimers = new Set<ReturnType<typeof setTimeout>>();
|
|
116
|
-
const clearPendingReplies = (): void => {
|
|
117
|
-
for (const timer of pendingReplyTimers) {
|
|
118
|
-
clearTimeout(timer);
|
|
119
|
-
}
|
|
120
|
-
pendingReplyTimers.clear();
|
|
121
|
-
};
|
|
122
|
-
bus.connection.on('end', clearPendingReplies);
|
|
123
|
-
|
|
124
|
-
const define = (member: string, impl: MethodImpl, resultSignature: string): void => {
|
|
125
|
-
bus.setMethodCallHandler(FAKE_PATH, FAKE_IFACE, member, [impl, resultSignature]);
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
define('Ping', () => 'pong', 's');
|
|
129
|
-
// The library awaits a Promise returned by a handler, so this replies after a delay —
|
|
130
|
-
// used to prove a late reply still resolves the caller's method call. The timer is
|
|
131
|
-
// tracked so a bus drop before it fires cancels the owed reply instead of writing it
|
|
132
|
-
// to a closed stream.
|
|
133
|
-
define(
|
|
134
|
-
'SlowPing',
|
|
135
|
-
(delayMs) =>
|
|
136
|
-
new Promise((resolve) => {
|
|
137
|
-
const timer = setTimeout(() => {
|
|
138
|
-
pendingReplyTimers.delete(timer);
|
|
139
|
-
resolve('pong');
|
|
140
|
-
}, Number(delayMs));
|
|
141
|
-
pendingReplyTimers.add(timer);
|
|
142
|
-
}),
|
|
143
|
-
's',
|
|
144
|
-
);
|
|
145
|
-
define('GetManagedObjects', () => managedObjectsValue(), 'a{oa{sa{sv}}}');
|
|
146
|
-
define('GetInt64', () => INT64_MAX.toString(), 'x');
|
|
147
|
-
define('GetUint64', () => UINT64_MAX.toString(), 't');
|
|
148
|
-
// Echo re-marshals the received Long — an exact 64-bit round-trip through the daemon.
|
|
149
|
-
define('EchoInt64', (value) => value, 'x');
|
|
150
|
-
define('EchoUint64', (value) => value, 't');
|
|
151
|
-
// Describe reports the received value's exact decimal string, judging the transport's
|
|
152
|
-
// encode without re-encoding anything.
|
|
153
|
-
define('DescribeInt64', (value) => String(value), 's');
|
|
154
|
-
define('DescribeUint64', (value) => String(value), 's');
|
|
155
|
-
|
|
156
|
-
await bus.requestName(FAKE_BUS_NAME, 0);
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
busName: FAKE_BUS_NAME,
|
|
160
|
-
emitTick(seq: bigint): void {
|
|
161
|
-
bus.sendSignal(FAKE_PATH, FAKE_IFACE, TICK_MEMBER, 't', [seq.toString()]);
|
|
162
|
-
},
|
|
163
|
-
async stop(): Promise<void> {
|
|
164
|
-
clearPendingReplies();
|
|
165
|
-
await bus.disconnect().catch(() => undefined);
|
|
166
|
-
},
|
|
167
|
-
};
|
|
168
|
-
}
|