Repository navigation
0.9.4
temporal-fmt v0.9.4
A mods/ folder now exists — drop a .mjs file in it and the CLI
picks it up on startup and registers it against the library, no
publishing or build step required. Modeled on the Minecraft mods-
folder convention: the host looks for files and runs them, rather
than depending on a package.
Added
- A mod default-exports
{ name, register(ctx, config) }, where
ctxis the same registration API (registerLocale,
registerLocaleVocab,registerRelativeGrammar,createFormatter,
createHolidayCalendar) already documented under Locales, Business
calendars, and Extending with custom tokens. .tfmodpackaging for mods bigger than one file: a gzipped tar
containingmod.json(name/version/main/requires/priority/
temporalFmtVersion/config) plusmain.mjsand an optionaldata/
directory. The manifest is readable without executing any code,
which is what makes dependency resolution happen beforemain.mjs
is ever imported. Extraction shells out to the systemtarbinary
rather than adding a parsing dependency.requires/priorityfields for controlling load order across
mods, resolved as a real dependency graph (Kahn's algorithm) rather
than just sorting by filename. Missing dependencies and circular
dependencies each fail only the mods involved; everything else
still loads.- Conflict reporting: if two mods register the same locale tag,
relative-time grammar, or custom token name, the load report says
which mod won (last-loaded wins, same as callingregisterLocale
twice already did — this just makes it visible). ctx.overrideFormat/ctx.overrideParse, plus the same
overrideXxx()shape for 79 more functions that have no internal
call sites in this library — the full list is in the README under
"Which functions are overridable." Unlike the additive registration
functions, only one mod may hold a given override point; a second
attempt fails immediately and names the mod that already owns it.temporalFmtVersioninmod.json(exact version or^caret
range, same semantics as npm) — checked beforemain.mjsis
imported, so a mod built against a newer override surface fails
cleanly instead of throwing from insideregister().- Per-mod user settings: a
configarray inmod.json(string/
number/boolean/enum, with optional min/max/choices) resolved
against an optionalconfig/<mod-name>.jsonfile kept as a sibling
ofmods/, not inside it, so re-downloading a.tfmodnever
touches a user's settings. Unknown keys and out-of-range values are
reported and fall back to the schema default rather than failing
the whole mod. loadMods()andformatModLoadReport()inscripts/loadMods.mjs
(CLI-only, not published — needsfs/path) for anyone embedding
temporal-fmtoutside the CLI who wants the same loader; the
published, Node-agnostic pieces it depends on (buildModContext(),
isMod()) are exported from the library itself.
Changed
src/index.ts's exports forformat,parse, and 79 other
previously-direct re-exports now route through a small override
registry (src/runtime.ts) instead of re-exporting straight from
their source modules. This is what makes a mod's override reach
every internal caller —formatRange()'s own use offormat(),
for instance — not just whoever importsformatfrom the package
root.mergeWithConfig()'s return type no longer asserts the merged
object back into the genericTit was called with; it now returns
its own actual shape (Tplus the five config fields, each
optional). The oldas T & {...}cast wasn't a guarantee a spread
could honestly make for an arbitrary caller-suppliedT, andtsc
caught it once this generic got exercised through a real call site
(the override wrapper inmodApi.ts).