You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
npm run test:demo loads the demo page's own module URL and checks it against the source. Every other gate in this repo reads this repo, and the defect below was not in this repo, so nothing could see it. This one reads the page, pulls the import URL, the <option> roster, and the preset buttons straight out of docs/index.html, fetches whatever that URL resolves to, and calls every op the page offers on every day the page ships. The oracle is index.js itself, so a published build that merely throws is caught and so is one that quietly answers something else. The module graph is mirrored to disk one URL at a time rather than concatenated, which matters more than it sounds: a CDN handing out two copies of one package produces two files here too, so the bug reproduces instead of being bundled away. Three guards, each proved to fire alone by planting one defect apiece: pointing it at the old jsDelivr URL turns it red with the real error on every call, adding an <option> the gate cannot call fails by name, and removing one the gate does call fails by name too. It is deliberately not part of npm test or the ci workflow, on two grounds: it needs the network and a live CDN, so it must never be able to block a merge, and it measures what is PUBLISHED, so a pull request that changes behaviour would fail it until release. It runs weekly, on demand, and whenever the page or the gate changes on master. Run it by hand after a release.
Fixed
The GitHub Pages demo threw on every date, and had done since the release carrying 4a6e564. Name that release with git tag --contains 4a6e564 | head -1. The page reported daymath: invalid date "2026-08-06" on load, for a day that is plainly valid, and the real error underneath it was TypeError: Invalid calling context out of temporal-polyfill. Nothing in daymath's own code is wrong, which is why every gate stayed green. The page loaded daymath from jsDelivr's +esm endpoint, and jsDelivr builds each subpath of a dependency as its own bundle, carrying its own private copy of that package's internals. daymath imports temporal-polyfill/fns/PlainDate and temporal-polyfill/fns/Calendar, so fromString came out of one copy while the getAny resolver passed to it came out of another, and the first copy does not recognise a calendar the second one built. No import spelling avoids that split: temporal-polyfill/fns is in the export map but its file is an empty stub (wc -c node_modules/temporal-polyfill/fns/index.js), so the separate subpaths are the only fns API on offer. The page now loads from https://esm.sh/daymath?bundle, which inlines a single copy, verified against every function the page exposes and every preset it ships. Anyone loading daymath through jsDelivr +esm hits the same wall, and that is a packaging question for temporal-polyfill rather than something daymath can answer from inside its own tarball.
A broken Temporal now says so, instead of being relabelled as a bad date.toPlainDate wrapped the parse in a catch-all and rewrote every throw as daymath: invalid <label> <input>, so an implementation fault arrived wearing the one message guaranteed to send the reader off after their own input. That is precisely what happened above, and it is why the demo sat broken across three releases with nobody suspecting the CDN. Only a RangeError is relabelled now, and anything else surfaces unchanged with its own message. Temporal reports every genuine input fault as a RangeError, and an impossible day, a month out of range, a year past the Temporal bounds, and an unknown calendar were each checked, so nothing that used to be caught stops being caught. The cause was always attached and the demo page was throwing it away; the page prints the whole chain now, so the next fault of this shape is readable on screen instead of only in a debugger.