messages: pure builders for every event the bridge sends

src/messages builds Response, StateReport, DeferredResponse, ErrorResponse, ChangeReport and the scene, doorbell
and simple events from plain values; messageId and times are parameters, so a test compares whole objects.
StateBuilder collects properties checked by the descriptors, AlexaError and AlexaErrors carry the payload fields
of an error type. AlexaStatusMessage and AlexaErrorResponse move to src/compat and are written on the builders.
On the wire, against 1.5.2: a DeferredResponse has no context key, a ChangeReport has no correlationToken, the
namespace of an ErrorResponse follows its type, and a ChangeReport without a changed property is not published:
send() resolves "" and the bridge reports an error that names the endpoint.
143 tests pass (108 before).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
David 2026-09-28 19:28:05 +00:00
parent 2558e21787
commit 46fa06728c
68 changed files with 3410 additions and 1208 deletions

View file

@ -4,6 +4,7 @@
import {
ActionMapping, AlexaActions, AlexaInterfaceType, Assets, BrightnessController, DeclarationError, DisplayCategory, EndpointHealth,
ModeController, PowerController, PowerState, RangeController, TemperatureSensor, ToggleController, asset, registry, semantics, text,
AlexaErrors, StateBuilder, messages,
} from "alex2node";
import type {
Alex2MQTT, Alex2MQTTOptions, AlexaInterface, AlexaStatusMessage, AssetId, Capability, CapabilityJson, ChangeCause, Device,
@ -136,3 +137,23 @@ export {
options, added, chained, sent, on, namespace, reachable, announced, endpoint, problems, names, lower, position, current, version,
refused, opening, percent, vacuum, misspelt, notAUnit, measured, rankine,
};
// StateBuilder.set(): the properties of the interface and the type of each value
const state = new StateBuilder()
.set(PowerController, "powerState", "ON")
.set(TemperatureSensor, "temperature", { value: 70, scale: "FAHRENHEIT" })
.set(RangeController, "rangeValue", 3, { instance: "Blind.Lift", timeOfSample: new Date() })
.health("UNREACHABLE", "WIFI_AP_NOT_FOUND");
// @ts-expect-error a power state is ON or OFF
state.set(PowerController, "powerState", "on");
// @ts-expect-error not a property of the interface
state.set(PowerController, "power", "ON");
// The builders take what the builder of the state collected; a message is typed down to its payload
const report = messages.changeReport({ endpointId: "lamp-1", cause: "APP_INTERACTION", changed: state.change, context: state.context });
const changedNames: string[] = report.event.payload.change.properties.map((property) => property.name);
// @ts-expect-error a DeferredResponse has no context
messages.deferredResponse({ endpointId: "lamp-1", correlationToken: "ct" }).context;
// @ts-expect-error the range is two numbers
AlexaErrors.valueOutOfRange("too high", { minimumValue: 1 });
const errorNamespace: string = AlexaErrors.setpointsTooClose("too close", { value: 2, scale: "CELSIUS" }).namespace;