Two failures of scripts/build.mjs did not say what to change. `node scripts/build.mjs --outt /some/dir` ignored
the misspelt flag, exited 0 and replaced the tracked dist/. Without devDependencies (npm install --omit=dev in a
clone runs "prepare") the script died with a stack trace, because typescript was resolved at module level,
outside the try block.
The script now takes no arguments or `--out <dir>` and answers anything else with one line and the usage, exit 1.
A missing compiler gives "build: typescript is not installed: run npm install (it is a devDependency)". The body
moved into build(args), so the staging directory and the compiler path are no longer module-level variables.
Two tests on a temporary package: three refused argument lists, and a package without node_modules; both compare
the package before and after. No change to dist/. npm test: 30 pass in 9.5-10.2 s (load average 7 on 4 cores).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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>
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>
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>
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>
"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>
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>