v0.7.3
Added
-
npm run test:splitbuilds a broken Temporal on disk and checks that daymath still tells the truth. 0.7.2 fixed one catch-all and shippedtest:demoto watch for the next one, but that gate needs the network, reads only the ops the demo page offers, and can only see a build that is already published. This one copiestemporal-polyfillout ofnode_modulesonce per subpath, rewrites the bare specifiers inindex.jsto point each import at its own copy, and imports the result. That is exactly what jsDelivr's+esmproduces, so the brand check between the copies fails the same way, and it runs offline against the working tree instead of against npmjs. The law it checks is one sentence, and it holds for any export: a broken implementation must never make daymath answer wrongly or blame the caller. Every call in the roster either agrees with../index.jsor throws aTypeError. ARangeErroris the failure it exists to catch, because that is daymath saying the input was bad when the input was fine, and a wrong answer is worse still. Proved to block by revertingindex.jsto 0.7.2 and running it: the run names each mislabel and each wrong answer, with the source answer beside it, and exits 1. It runs on every matrix version inci, because the shim lane and the native lane word their faults differently, andprepublishOnlynow runs it too.A second phase raises the same fault from one import at a time, and that is the half that reaches every catch. The CDN shape cannot reach them all, and it never will:
InstantFns.fromStringandZonedFns.fromFieldstake no calendar resolver, so no record crosses between copies and a split leaves both alone however the bundles fall. Their narrowing would then be code no gate runs, which is exactly how a catch-all comes back in a later edit. So the second phase stops modelling one CDN and models the rule — any import can fault, and daymath owes the same answer either way. Nothing is hand-listed: the import roster is read out ofindex.js, so an import added later joins the phase with no edit here, and a namespace member counts only where the source actually calls it. Each phase was proved to block on its own by planting the defect it exists for. Deleting the narrowing at those two calls turns the run red and names both,day("2026-01-01T00:00:00Z")reportingneither a moment nor a time zoneandday(0, "America/New_York")reporting an unknown time zone for one it knows. An import that refuses to load is a legal outcome, because it is loud and answers nothing, and the run reports how many loaded. Three things stop that legality becoming a quiet pass, and a review round added all three. The stub is loaded on its own first, so a probe the gate failed to BUILD is a gate defect rather than a silent skip — an aliased import wrote a stub that did not parse, and the run would have gone quiet on the one import it was watching. A{ getAny as resolve }import now yields the export name rather than the local one. And which imports were checked is recorded inscripts/split-copy.baseline.json, so coverage cannot fall quietly — a count can say that something stopped being checked and never say what, which is the lessonexamples.baseline.jsonalready carries.declinedis a real state in that roster rather than a failure:index.jscallsgetAnyat module scope, so a faulting stub stops the whole module loading and no export can be judged. Drift fails in both directions and names the import.npm run test:split:writere-records. Every one of these was proved to block by planting it. -
The cross-runtime battery never passed a third argument, and never passed a
weekStartsOnit refuses.isSameWeekandareIntervalsOverlappingare the only exports that take three, so their whole options path was unprobed, andscripts/battery.mjsoffered only 0, 1 and 7 — all accepted. That is whynpm run test:runtimespassed straight through theisSameWeekmessage fix below. An export could change WHICH error it reports for a bad option and no hash would move. The battery now carries aWEEK_STARTSlist holding both halves, passes it in the third slot as well as the second, and exercisesinclusiveonareIntervalsOverlapping. Proved by recording the baseline against 0.7.2 and running it against this branch: one export differs, and it isisSameWeek. That is also the independent evidence that this release is a patch. The baseline is re-recorded, and it agrees on Node, Deno and Bun, so the widened probes read the same on native Temporal and on the polyfill. The same gap asJUNKin that file, which exists because no probe ever passed a bad string.
Fixed
- A broken Temporal was still being relabelled everywhere
toPlainDateis not. 0.7.2 narrowed one catch and called the job done. It was one instance of a class, and a sweep of the perimeter found the rest, each proved reachable by loading a broken build and calling it.isValidwas the worst of them: it answeredfalsefor a perfectly valid day and raised nothing at all, so a reader learned nothing and had nowhere to look.calendarRulereportedunknownfor a calendar the runtime names happily.guardRangerelabelled every throw under it, across every add, sub,setYear,startOfand interval export. The zoned branch ofday()said it could not read a zone that it had read correctly. All of them now go through one function,assertCallerFault, which re-throws anything that is not aRangeError— find them withgrep -n 'assertCallerFault' index.js. The discriminant is a measurement, not a guess: a bad string, a bad field, an out-of-range result and an unknown calendar each report aRangeErroron the shim lane and on the native lane, checked through parse,withFields,addDays,addYears,fromFieldsandwithCalendar.isValidneeded its own shape check first, becausetoPlainDatereports a non-string with aTypeErrorandisValidstill owesfalsefor one. isSameWeekblamed the dates for a badweekStartsOn. It read the option inside its own range guard, soisSameWeek(a, b, { weekStartsOn: 8 })answereddaymath: isSameWeek could not produce a valid dateand never named the option.startOfWeekandendOfWeekalready read it before the guard and sayweekStartsOn must be an integer 0…7.isSameWeeknow does the same, which is the same defect as the entry above wearing different clothes: a guard that catches more than it was written for.