Commit Graph
45 Commits
Author SHA1 Message Date
tmu 8261759190 📝 Restore and rewrite the README entry point
The README collapsed to API-only prose once the old match/P surface was
dropped. Rebuild its entry-point shape: a quick-start Synopsis walked through
a Contact union, a dedicated Installation section, and a real-world Examples
section (primitive-union dispatch, a fallback, a property union narrowed by
the tagged-union matcher, and the widening variant).

Set the tagline and Description to the library's identity — exhaustive,
type-safe pattern matching for TypeScript — and state the goal Moose-style:
type-safe pattern matching with a lean syntax, accomplished by exhaustive
branches and typed per-branch handler parameters, backed by autocomplete and
a tiny footprint. Drop the F#-style framing and the stale "matches method is
a type guard" and "(not regex)" copy from the README, AGENTS.md and the
package.json description. Every fence is a doc-test, so the examples cannot
drift from the API.
2026-09-24 21:55:51 +00:00
tmu 0bde38d6e4 📝 Summarize the API docs work in the changelog 2026-09-23 22:51:11 +00:00
tmu 5ddbbdc183 ♻️ Keep the public API to the four factories
The builder types are already the inferred return types and travel into
the emitted .d.ts, so exporting them only made them nameable while
pinning the internal Strict/Widening split as API. Revert the type
exports and the public renames, drop the type-surface test, and document
only the factories in README and development/library.md.
2026-09-23 22:19:02 +00:00
tmu f485b1bae2 ✨ Finalize and document the public API surface
Export the matcher builder types from the barrel and rename them off the
internal Strict/Widening suffixes, so the type a factory returns is
nameable: PrimitiveUnionMatcher/W, TaggedUnionMatcher/W, and the
tagged-union factory types. Add TSDoc to every public symbol — the
declarations carry it into the published package — and pin the exported
names in src/index.test.ts.

Document the API in README and record the surface decision (and the
rejected matcher-shared export) in development/library.md.
2026-09-23 21:39:15 +00:00
tmu acf9d06ddd 🚀 Release 0.8.1
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 35s
CI / maintain (push) Successful in 14s
CI / publish (push) Failing after 16s
2026-09-23 21:24:42 +00:00
tmu 52ce655d8e 👷 Gate CI at 100% coverage
test:ci now runs c8 with --all --include "src/**/*.ts" --100, so the
build job fails when any runtime file under src/ is untested. --all is
what makes the gate non-vacuous: without it c8 counts only the files the
suite happened to load, and a new untested module stays invisible.

Add src/index.test.ts to load the public barrel, which was previously
never imported at runtime and so read as 0% under --all. matcher-shared.ts
is types-only (an empty runtime image) and carries a file-level c8 ignore
with the reason.

Why 100% and the rejected alternatives: development/ci.md § Coverage
threshold.
2026-09-23 21:12:24 +00:00
tmu 69231eb9ac 📝 Note the duplicate-tag tests in the changelog 2026-09-23 20:30:44 +00:00
tmu 11d364e187 🚀 Release 0.8.0
CI / release-gate (push) Successful in 3s
CI / build (push) Successful in 34s
CI / maintain (push) Successful in 15s
CI / publish (push) Failing after 19s
2026-09-23 17:42:32 +00:00
tmu 1bdf7f0315 🐛 Reject mixed literal and broad universes
`UnsupportedReason` tested `IsLiteral<PatternKey<T>> extends false`.
`IsLiteral` is `boolean` for a union that mixes a literal with a broad
type (`"a" | \`x-${number}\``), so `boolean extends false` is false and
the mix was accepted. Test `extends true` instead, which rejects it.

Found while adding property-union broad-universe coverage; the gate is
shared with the primitive-union matcher.
2026-09-23 16:32:09 +00:00
tmu 55bf6c24a7 🐛 Narrow union-valued and optional discriminants
The tagged-union matcher selected handler parameters with
`Extract<T, Record<K, V>>`, which only keeps a member whose
discriminant is a singleton literal. A property that is itself a
union (`{ color: "red" | "green" | "blue" }`) or optional
(`{ type?: "x" }`) matched no member, so every handler received
`never`, and the fallback saw the whole shape instead of the
unhandled tags.

Replace the selector with `Narrowed`, which distributes over `T`,
drops a member whose `K` cannot take the tag, and narrows `K`. The
`T[K] extends V` fast path keeps an exact member (a discriminated
union's declared interface) untouched. The fallback reuses
`Narrowed` over `Exclude<Tags, HandledTags>`, so a union-valued
property narrows to the unhandled tags rather than the whole shape.

Matching a defined tag on an optional property makes the key
required (`{ type: "x" }`); the `undefined` tag yields
`{ type?: never }` under `exactOptionalPropertyTypes` (absence) or
`{ type?: undefined }` when the property admits an explicit
`undefined`.
2026-09-23 16:04:00 +00:00
tmu 013bfa883b 📝 Prune the backlog; document the LSP shutdown fix
Remove every done/cancelled backlog item; the reasons they recorded are
already in development/ (library.md, testing.md, tooling.md, workflow.md)
and git history is the archive.

The one exception was the TS 7 LSP shutdown workaround, which lived only in
a code comment. Record it under development/testing.md § Autocomplete
(Known issue) before dropping the bug item.
2026-09-23 15:12:54 +00:00
tmu d99d84d387 🚀 Release 0.7.1
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 34s
CI / maintain (push) Successful in 15s
CI / publish (push) Failing after 17s
2026-09-23 14:56:59 +00:00
tmu 2e4262383c ⬆️ Upgrade dependencies
Bump every direct dependency to the latest registry version: cspell
10.3.3, knip 6.38.0, oxfmt 0.70.0, oxlint 1.85.0, oxlint-tsgolint
7.0.2002 and type-fest 5.10.0, plus their transitive updates. oxfmt is
the only range widened (^0.68.0 -> ^0.70.0); the rest moved within
their existing semver ranges.

`npm run maintain:outdated` is now clean and `npm run verify` is green
with no rule or format fallout. `maintain:knip` still reports the
pre-existing `src/index.ts` false positive (dist-only `exports`), which
predates this bump.
2026-09-23 14:50:29 +00:00
tmu a8f1a05db2 🚀 Release 0.7.0
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 34s
CI / maintain (push) Failing after 16s
CI / publish (push) Failing after 17s
2026-09-23 14:34:51 +00:00
tmu ce287b384f 📝 Note the gate in changelog and backlog
Close the broad-universe type-hole task and cancel the widened-literal
test task, since arbitrary strings no longer fall through to a fallback.
2026-09-23 13:42:25 +00:00
tmu 1723419e69 🚀 Release 0.6.0
CI / release-gate (push) Successful in 3s
CI / build (push) Successful in 33s
CI / maintain (push) Failing after 13s
CI / publish (push) Failing after 16s
2026-09-22 11:10:34 +00:00
tmu 02f736507c ♻️ Rename primitive to primitive-union
`getMatcher` / `getMatcherW` become `getPrimitiveUnionMatcher` /
`getPrimitiveUnionMatcherW`; `src/primitive.ts` and its test move to
`src/primitive-union.*`. The `Union` suffix mirrors `getTaggedUnionMatcher`.
2026-09-22 09:46:59 +00:00
tmu 8ab3c6dbc6 📝 Document the tagged-union matcher 2026-09-21 22:14:22 +00:00
tmu a4b4618c9f 🚀 Release 0.5.0
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 30s
CI / maintain (push) Failing after 14s
CI / publish (push) Failing after 17s
2026-09-21 13:11:27 +00:00
tmu 45495fe2a8 ✨ Match boolean, null and undefined
Extend the matcher universe beyond string | number so a union can carry
boolean, null and undefined members. true|false, null and undefined are
not property keys, so handler keys are their stringification while the
callback still receives the real member. Symbols are rejected (a brand
is compile-time only) and bigint is not a property key.
2026-09-21 09:44:44 +00:00
tmu f27966e803 ✨ Reject a fallback for an exhaustive map
A fallback paired with a handler map that already covers `T` was still
accepted, even though its remainder is empty. Fold the guard into
`Handled`'s own (F-bounded) constraint so it is checked after inference;
a conditional in the fallback parameter is evaluated while `Handled` is
still its constraint and rejects every partial map whose handler callbacks
need contextual typing.
2026-09-20 22:05:40 +00:00
tmu 7e7f716424 🚀 Release 0.4.0
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 30s
CI / maintain (push) Failing after 14s
CI / publish (push) Failing after 16s
2026-09-20 21:46:32 +00:00
tmu ff823b3cba 📝 Route every task through the full branching model
Drop the leaf-task exception in AGENTS.md: a task without subtasks was implemented on the current branch, bypassing create:branch / create:finish. Every task now opens and closes a branch, so the clean-tree / current-main / green-baseline preconditions and the post-merge verify always apply.
2026-09-20 21:43:46 +00:00
tmu ae7ffb6fa2 📝 Document the two-argument fallback
Rewrite development/library.md around the new shape: the fallback is a second
argument (so it can receive the remainder), `R` needs an inference site in the
handler map, `Exact` restores the excess-property check, and the overload order
is load-bearing. Record this branch's rejected alternatives (single-object `_`,
currying, `this`/HKT, variance/`const`/`NoInfer`/brands) and the redundant-
fallback known issue. Add the changelog note and check off the backlog task.
2026-09-18 23:52:47 +00:00
tmu cc88c07961 🚀 Release 0.3.0
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 29s
CI / maintain (push) Failing after 15s
CI / publish (push) Failing after 18s
2026-09-18 07:51:16 +00:00
tmu 9da5b5dbf2 📝 Note the test-helper home in the changelog 2026-09-17 13:23:36 +00:00
tmu ee146ce350 🚀 Release 0.2.0
CI / release-gate (push) Successful in 3s
CI / build (push) Successful in 24s
CI / maintain (push) Failing after 15s
CI / publish (push) Failing after 17s
2026-09-16 22:07:16 +00:00
tmu 16092350b8 📝 Check off example-code removal in backlog
Note the finished work under [Unreleased] and mark the task and its
subtasks done; retarget the v1.0 coverage tasks at the surviving
primitive module.
2026-09-16 21:58:14 +00:00
tmu 08513b36a9 📝 Check off straighten-oxc-rules in backlog
Note the finished work under [Unreleased] and mark the task and its
subtasks done.
2026-09-16 21:46:04 +00:00
tmu 1e13917cbe 🚀 Release 0.1.8
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 25s
CI / maintain (push) Successful in 15s
CI / publish (push) Failing after 15s
2026-09-16 09:01:49 +00:00
tmu c15bc0153e 📝 Require concise prose in development/
Actionable sentence in CONTRIBUTING.md § Rules the tools don't enforce;
the why — humans skim, agents imitate the dominant style, so verbosity
compounds — in development/README.md § Decision blocks. Both sides in
one commit per the write-each-fact-once rule.
2026-09-16 08:00:36 +00:00
tmu ae319f6547 🔧 Ignore @types/node in maintain:outdated
DefinitelyTyped keeps the @types/node latest dist-tag on the LTS line,
so check-outdated compared the current-line types against a lower
version: a permanent "reverted" flag, scan exit 1, zero signal. Ignored
by package name; --types filtering and a version-scoped pin rejected
(rationale in development/tooling.md).
2026-09-16 07:55:25 +00:00
tmu 63e8edce79 ⬆️ Upgrade dependencies
@types/node to ^26.6.1 and cspell to ^10.3.2 — the only packages
check-outdated found behind the registry, both bumps within the existing
semver ranges. cspell 10.3.2 drops its transitive
fast-json-stable-stringify. npm run verify is green with no rule or
format fallout. maintain:outdated still flags @types/node as "reverted"
because DefinitelyTyped's latest dist-tag stays on the 22.x LTS series;
known false positive, advisory scan only.
2026-09-16 07:31:49 +00:00
tmu b456a430a7 🚀 Release 0.1.7
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 22s
CI / maintain (push) Failing after 14s
CI / publish (push) Failing after 15s
2026-09-16 07:23:24 +00:00
tmu d646fd1ec4 ♻️ Let create:branch start from an ahead main
`create:finish` leaves the merge local until `create:release` pushes it, so
`main` is routinely ahead of its upstream between a merge and a release. The
branch front door demanded an exact match and refused until it was pushed,
forcing a manual `git push` that the model deliberately keeps out of it.

Reject only a `main` that is behind its upstream — matching `create:finish`,
which already tolerates being ahead — and record why pushing from `finish` was
rejected instead. The push stays with `create:release`, so the merge remains
reviewable locally, and the known issue about the deadlock is gone.

Resolves: backlog task "Resolve the finish/push tension".
2026-09-15 22:14:51 +00:00
tmu f6f820a648 📝 Require a changelog note before finishing a branch
A merged branch must carry a final commit adding a short summary of the
work under [Unreleased] in CHANGELOG.md. create:release derives the bump
heuristic from that body, so notes have to exist before release day;
rationale in development/workflow.md.
2026-09-15 22:08:08 +00:00
tmu cf73857931 🚀 Release 0.1.6
CI / release-gate (push) Successful in 3s
CI / build (push) Successful in 23s
CI / maintain (push) Failing after 14s
CI / publish (push) Failing after 15s
2026-09-15 21:53:19 +00:00
tmu 1465926783 📝 Check off the docs restructure and note it in the changelog 2026-09-15 21:51:21 +00:00
tmu c0bba0775c 🚀 Release 0.1.5
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 24s
CI / maintain (push) Failing after 15s
CI / publish (push) Failing after 17s
2026-09-15 09:33:17 +00:00
tmu 9c472c88c2 🚀 Release 0.1.4
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 22s
CI / maintain (push) Successful in 14s
CI / publish (push) Failing after 3s
2026-09-14 16:20:46 +00:00
tmu 1dfb979ebb 🚀 Release 0.1.3
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 43s
CI / maintain (push) Failing after 33s
CI / publish (push) Failing after 3s
2026-09-14 15:45:11 +00:00
tmu 5292455dbc 🚀 Release 0.1.2
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 36s
CI / maintain (push) Successful in 27s
CI / publish (push) Failing after 3s
2026-09-14 12:03:32 +00:00
tmu b5bfe83140 🚀 Release 0.1.1
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 34s
CI / maintain (push) Failing after 27s
CI / publish (push) Failing after 4s
2026-09-14 11:58:51 +00:00
tmu 67e3e13b5e 🚀 Release 0.1.0
CI / release-gate (push) Successful in 2s
CI / build (push) Successful in 37s
CI / maintain (push) Failing after 30s
CI / publish (push) Failing after 4s
2026-09-14 11:48:55 +00:00
tmu 09c5b129bb ✨ Add pubv-based release.sh and npm run release
Release front-door: pubv runs preflight + the interactive semver
heuristic and graduates CHANGELOG.md [Unreleased] -> dated; npm version
syncs package.json + lockfile; git commit --amend folds them into
pubv's single release commit; the tag is created AFTER the amend so it
marks the exact commit on main that gets published; then push. Tags are
v-less (--tag-prefix=none) to match the changelog compare refs.

@runwisp/pubv added as a devDependency. The short "how we got here /
what was rejected" note lives as a comment atop the script; the docs/
exploration was removed as too much to read.
2026-09-07 18:17:28 +02:00