Skip to content

build: modernize the toolchain, adopt TypeScript 7, and pick up the republished upstreams - #350

Merged
unional merged 2 commits into
mainfrom
chore/modernize-toolchain
Sep 1, 2026
Merged

build: modernize the toolchain, adopt TypeScript 7, and pick up the republished upstreams#350
unional merged 2 commits into
mainfrom
chore/modernize-toolchain

Conversation

@unional

@unional unional commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Brings assertron onto the current stack in one pass: toolchain swap, the five republished runtime upstreams, TypeScript 7, a repaired supply-chain soak, and one updater feeding one merge mechanism.

Toolchain

Concern Was Now
Build three tsc -p passes + ncp tsdown
Lint/format eslint 8 + plugins + prettier biome via @repobuddy/biome
Tests jest + ts-jest + 4 jest-watch plugins vitest
Tasks npm-run-all chain turbo
Dead-code depcheck knip
TypeScript 5.9.3 7.0.2

Eleven devDependencies were deleted rather than upgraded, which is why five open Renovate majors (jest 30, eslint 10, @typescript-eslint 8, size-limit 12) are closed by this rather than merged.

Published paths are preserved. esm/index.js, cjs/index.js, cjs/package.json and the declarations beside each keep their names; LICENSE and README.md still ship; no spec file ships. The emitted output does change — the CJS target moves ES5 → ES2015 (rolldown's floor), a small _virtual/ helper set appears next to the entry, and per-module .d.ts files unreachable through the exports map are no longer emitted — so this carries a patch changeset, not a feat.

tsdown does not typecheck. typecheck is now an explicit script inside verify; proven real by appending a deliberate type error and confirming a non-zero exit.

Coverage is a gate, not a report. Thresholds are set to the level the repo already meets (95%).

TypeScript 7 — adopted

The sweep-wide "TS7 is blocked" note does not apply here. Both old blockers (ts-jest's <6 peer, ts-api-utils via eslint) leave with jest and eslint; tsdown@0.22 accepts a ^7 peer; and the one real remaining blocker — typedoc@0.28.20 crashing at import under TS7 — is not present, because this package publishes no typedoc docs. The full verify runs green on 7.0.2.

TS7 stops auto-including @types/*, so types is now named explicitly (["node", "vitest/globals"]).

Runtime upstreams

Minimums raised to the versions this is built and tested against, all republished by the same sweep and all carrying npm provenance:

Package Resolved before Now
iso-error 6.0.3 6.0.5
path-equal 1.2.5 1.2.7
satisfier 5.4.2 5.4.4
tersify 4.0.1 4.0.6
type-plus 7.6.2 7.6.2 (minimum raised)

type-plus deliberately stays on 7.x — its 8.x line is still a changesets prerelease on the beta tag.

Supply-chain soak — a control repaired, not loosened

The 24h window moves out of .npmrc (where npm warned minimumreleaseage was not a valid key) into pnpm-workspace.yaml, and gains minimumReleaseAgeStrict: true.

This matters because pnpm 11's default mode is loose and self-weakening: on meeting a package younger than the window it does not fail — it silently appends that package to minimumReleaseAgeExclude and carries on. Under the default, the soak enforces nothing and the exclusion list grows by itself.

Under strict mode the install fails with ERR_PNPM_NO_MATURE_MATCHING_VERSION instead, so every exclusion has to be deliberate. Seven versions tripped it here, and each exclusion below is an exact-version pin, never a weakened policy:

iso-error@6.0.5, path-equal@1.2.7, satisfier@5.4.4, tersify@4.0.6, unpartial@1.0.7

unpartial@1.0.7 is the point worth noting: the soak is evaluated per resolved version including transitives, so pinning the five direct dependencies was not enough — tersify@4.0.6 pulls it. knip was held at ~6.33.0 rather than excluded, because a range constraint is cheaper than a pin.

Audit: minimumReleaseAgeExclude was empty before this change and, after a full pnpm nuke + pnpm install --frozen-lockfile, contains only the five reviewed entries above. Nothing was auto-appended.

Pre-existing bugs surfaced, not regressions introduced

  • .husky/commit-msg was committed 100644, so git had been skipping it — commitlint had never once run on this repo. Now 100755, and verified to reject a non-conventional message.
  • knip found ts/assert-order/internalInterfaces.ts and three test helpers that nothing referenced. Removed.
  • perf_hooks keeps its bare specifier deliberately (with a scoped biome ignore): package.json's browser field maps that name for bundlers, and the mapping does not apply to the node: prefixed form.

Dependency automation

Nothing was merging bot PRs here, through three independent faults at once:

  1. .github/mergify.yml was extends: .github, and cyberuni/.github carries no Mergify config at all — it resolved to nothing.
  2. dependabot-automerge.yml armed gh pr merge --auto --squash, and this repo disallows squash merges (and there is no .github/dependabot.yml to feed it).
  3. The two-line Renovate preset carries no automerge policy.

Both dead mechanisms are removed. Renovate with platformAutomerge is now the single updater and the single merge mechanism, and it enqueues natively through the merge queue rather than merging around it. Majors stay manual.

Proof

  • pnpm nuke && pnpm install --frozen-lockfile && pnpm verify green from a cold tree, --force (no turbo cache).
  • Typecheck proven to actually check (deliberate error → non-zero exit).
  • pnpm pack tarball diffed against the published assertron@11.5.3: LICENSE, README.md, cjs/index.{js,d.ts}, cjs/package.json, esm/index.{js,d.ts} all present; zero spec files.
  • Tarball installed into a scratch project: ESM import and CJS require both exercised (truthy, falsyAssertionError, satisfies, AssertOrder), and tsc --module nodenext --strict resolves the types from both an ESM and a CJS consumer.

Settings applied outside this PR

Merge queue rule added to the main ruleset (with strict_required_status_checks_policy: false); Actions default workflow permissions tightened writeread (every workflow that needs more declares its own permissions:); Dependabot security updates disabled in favour of Renovate; and the four stale secrets — NPM_TOKEN, CI_GITHUB_TOKEN, CODACY_PROJECT_TOKEN, CODECOV_TOKEN — deleted. Releases have run on OIDC trusted publishing since 11.5.3, which carries provenance.

…nd turbo

Swaps the whole build/lint/test toolchain in one pass rather than upgrading tools the
swap deletes:

- tsdown replaces the three `tsc -p` build passes and the `ncp package.cjs.json` step.
  `esm/index.js`, `cjs/index.js` and the declarations beside them keep their paths; the
  CJS target moves ES5 -> ES2015 (rolldown's floor). The `tslib` output is dropped: it
  was built on every release and never appeared in the tarball.
- vitest replaces jest, ts-jest and the four jest-watch-* plugins. `globals: true` keeps
  every spec file byte-identical. Coverage is now a gate, not a report: thresholds are
  set to the level the repo already meets.
- biome (via @repobuddy/biome) replaces eslint 8, its plugins and prettier. The reformat
  lands here. `perf_hooks` deliberately keeps its bare specifier — package.json's
  `browser` field maps that name, and the mapping does not apply to `node:perf_hooks`.
- turbo runs the verify graph; knip replaces depcheck and found two genuinely dead
  pieces (`ts/assert-order/internalInterfaces.ts` and three unused test helpers).
- tsdown does not typecheck, so `typecheck` is an explicit script in `verify`. Proven by
  appending a deliberate type error and confirming a non-zero exit.

TypeScript moves to 7.0.2. The old blockers went with jest and eslint (`ts-jest`'s `<6`
peer, `ts-api-utils` via eslint), tsdown accepts a `^7` peer, and this package publishes
no typedoc docs — the one remaining TS7 blocker — so the full verify runs green on it.
TS7 stops auto-including `@types/*`, so `types` is now named explicitly.

Runtime dependency minimums are raised to the versions this is built and tested against:
iso-error@6.0.5, path-equal@1.2.7, satisfier@5.4.4, tersify@4.0.6, type-plus@7.6.2.
type-plus stays on the 7.x line deliberately; its 8.x is still a changesets prerelease.

Supply-chain soak: the 24h window moves from `.npmrc` (where npm warned it was not a
valid key) into pnpm-workspace.yaml, and gains `minimumReleaseAgeStrict: true`. pnpm 11's
default mode is loose — on meeting a too-young package it silently appends it to
`minimumReleaseAgeExclude` and continues, so the soak enforces nothing. Strict mode fails
the install instead, and every exclusion below it is a deliberate exact-version pin. The
list was empty before this change and contains only reviewed entries after it; the
transitive `unpartial@1.0.7` is pinned too, because the soak evaluates per resolved
version and pinning the direct deps alone was not enough.

`.husky/commit-msg` was committed 100644, so git had been skipping it — commitlint had
never actually run. Now 100755 and verified to reject a non-conventional message.
Nothing here was merging bot PRs, through three independent faults at once:

- `.github/mergify.yml` was `extends: .github`, and `cyberuni/.github` carries no Mergify
  config at all — so it resolved to nothing.
- `dependabot-automerge.yml` armed `gh pr merge --auto --squash`, and this repo disallows
  squash merges. It also had no `.github/dependabot.yml` to feed it.
- The two-line Renovate preset carries no automerge policy.

Renovate is now the single updater and the single merge mechanism, with
`platformAutomerge` so it enqueues natively through the merge queue rather than merging
around it. Majors stay manual. `minimumReleaseAge` mirrors the pnpm-side soak.
@changeset-bot

changeset-bot Bot commented Sep 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: af3732f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
assertron Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@unional
unional added this pull request to the merge queue Sep 1, 2026
@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.85714% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.18%. Comparing base (e7bff65) to head (af3732f).

Files with missing lines Patch % Lines
ts/assert-order/StateMachine.ts 33.33% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #350      +/-   ##
==========================================
- Coverage   96.35%   95.18%   -1.18%     
==========================================
  Files          24       21       -3     
  Lines         357      187     -170     
  Branches       60       47      -13     
==========================================
- Hits          344      178     -166     
+ Misses         13        8       -5     
- Partials        0        1       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Merged via the queue into main with commit 2434b2c Sep 1, 2026
5 of 7 checks passed
@unional
unional deleted the chore/modernize-toolchain branch September 1, 2026 18:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant