Commit graph

8 commits

Author SHA1 Message Date
ee311dab01 build: stage beside dist/, not inside it
The staging directory was dist/.build-XXXXXX. A build that is killed cannot remove it, the next build removed
only its own, and "files": ["dist"] put the leftover into the npm tarball. .gitignore hid it from git status and
the freshness test skipped every name starting with ".build-", so nothing reported it.

The staging directory is now .dist-staging beside the output (.<name>-staging for --out <dir>) and is cleared at
the start of each build. The freshness test no longer skips anything: a stray file under dist/ fails it.

Checked in a scratch copy: a build stopped with SIGKILL after 1.5 s leaves .dist-staging/ and an unchanged dist/;
npm pack lists 36 files, none from the staging directory; the next build removes it. New test for the same on a
temporary package. npm test: 28 pass in 8.2-9.5 s.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 14:40:36 +00:00
c340e11f6f test: a failed build leaves dist/ as it was
scripts/build.mjs and its commit message state that a failed build does not touch dist/. That held when checked by
hand, and no test covered it, so a later change to the swap could break it unnoticed.

test/build.test.js runs a copy of the script in a temporary package with one source file and the dist/ of an
earlier build, and compares every file and directory of that package before and after. Two cases: a type error
(the first compiler run fails, TS2322) and "export =" (CommonJS compiles, the ES module run fails with TS1203, so
the staging directory already holds cjs/ and types/).

Checked in a scratch copy: with the cleanup of the staging directory removed both tests fail; with cjs/ and types/
swapped in before the ES module run the second one fails. npm test: 27 pass in 5.7 s.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 14:34:53 +00:00
70072d39e3 build: the ES module build gets its own declarations
An ES module TypeScript consumer was given the CommonJS declarations in dist/types. With those,
`import alex2node from "alex2node"` compiled (module Node16) and then failed when Node loaded it: "The requested
module 'alex2node' does not provide an export named 'default'". The ES module build has named exports only.

tsconfig.esm.json now emits declarations next to dist/esm/*.js, under that directory's {"type": "module"}, and
"exports" selects per condition: import -> dist/esm/index.d.ts, require -> dist/types/index.d.ts. The default
import is now refused with TS1192; named imports are unchanged. "main", "module" and "types" are as before.

test/fixtures/types.mts holds the default import under @ts-expect-error, and a new test requires "types" before
"default" and a declaration for every built file. dist/: 25 files, 132,291 B -> 33 files, 155,647 B; the npm
tarball 25,125 B -> 25,837 B. npm test: 25 pass in 5.6 s.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 14:31:34 +00:00
f74773132a test: read an ErrorResponse off the broker; every answer has its own messageId
Step 2 replaced uuid by crypto.randomUUID() at three call sites (AlexaStatusMessage, AlexaErrorResponse,
Device.sendSceneResponse) and only the first was asserted, in the ES module test. With the messageId dropped or set
to a constant in the other two, the suite still passed 22/22, and no test read an ErrorResponse at all.

New: an ErrorResponse compared whole against the documented shape (header, endpoint, payload, no context), and one
test that sends two directives each to a light, an unreachable light and a scene and requires six different
version 4 UUIDs. The Response, StateReport, scene and change report tests check the messageId too; the pattern
lives in the harness.

Checked in a scratch copy: a missing or constant messageId at each of the three sites now fails the suite (the
missing one in AlexaStatusMessage does not compile). No change to src/ or dist/. npm test: 24 pass in 5.5 s.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 14:26:44 +00:00
1d88c7811a build: dual CJS/ESM output, ES2020 target, Node 18 floor
"exports" gains "import" and "types" next to "require": import "alex2node" failed with
ERR_PACKAGE_PATH_NOT_EXPORTED. scripts/build.mjs compiles src/ to dist/cjs, dist/esm and dist/types in a staging
directory and swaps them in only when both compiler runs passed. "main" and "types" follow for resolvers that do
not read "exports". Target ES6 -> ES2020, "engines": node >= 18, relative imports carry ".js" for Node's ESM loader.
uuid gives way to crypto.randomUUID(); mqtt is the only runtime dependency left.

Tests load the package by name. New: both entry points, the modules the builds import, dist/ equal to a fresh
build, a round trip on the ES module build, and two compiled type fixtures in place of the .d.ts regexes.
22 tests pass in 5.7 s. dist/: 16 files, 79,711 B -> 25 files, 132,291 B; the npm tarball is 25,125 B.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 13:58:17 +00:00
58869d44c1 test: split the 1.5.2 suite into a harness and per-topic files
test/helpers/harness.js holds the aedes broker, the Alexa-side watcher, until() and the cleanups, plus directive(),
connected() and setup(), which the old file spelled out in each test. test/alex2node.test.js (194 lines, 8 tests)
becomes connection, discovery, directives, change-report and typings.

The 50-line round-trip test is cut at its topic boundaries and the duplicate-endpointId test no longer carries the
thermostat discovery check, so a failure names a case: 8 tests become 15, every 1.5.2 assertion kept. Four checks
are stricter: the "directive" event is compared whole, the StateReport is checked for its token and properties, the
Celsius conversion covers lowerSetpoint as well as temperature, and a duplicate endpointId is listed once.

No change to src/ or dist/. npm test: 15 pass in 0.9 s.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 13:45:38 +00:00
275f00f8a7 1.5.2: send() never rejects (status, error, scene, change report: resolves the topic or "" and reports the failure through the bridge's error event when listened to - 1.5.1 rejected, and an un-caught .send() killed the host on any broker hiccup); types ship (declaration: true, dist/*.d.ts tracked; addSupportedModes takes {value, modeResources}, ActionMapping payload optional, addHealthProp accepts EndpointHealth or the string); disconnect()/connect() re-binds devices to the new client; registerDevice returns the existing device on a duplicate endpointId (warning via the log hook, console.warn without one); ThermostatController discovery lists targetSetpoint and drops adaptiveRecoveryStatus; the UNSUPORTED INTERFACE TYPE stderr spam goes through the log hook. Examples: require("alex2node"), an error listener in each, EndpointHealth.OK, neutral endpoint ids, BlindControl reads correlationToken from the header, the thermostat reports Fahrenheit as Fahrenheit, ExamplePowerController is power-only again + new ExamplePowerControllerWithBrightness. readme (install from Forgejo, 1.5.2 changelog, table syntax), LICENSE (MIT); tests for the non-rejecting send, reconnect, duplicate, thermostat discovery and the shipped declaration signatures (8/8 on an in-process broker).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 04:58:39 +00:00
717af632de 1.5.1: broker errors as events that never crash the host (error only when listened to; connect/offline/reconnect/close; connected flag), configurable broker host + mqtt options + log hook, Alexa.ChangeReport via Device.getChangeReport (<root>/changeReport), SceneController discovery + sendSceneResponse, addCapability options (proactivelyReported), unregisterDevice/clearDevices/getDevices/disconnect, discover/directive events, promise-returning quiet send(); tests on an in-process broker (aedes); readme
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 19:29:38 +00:00