Skip to content

Build and supply chain

The stack

ConcernToolWhy
CompilerTypeScript 7.0.2Go-native. Pinned to ~7.0 — see below.
Bundlertsdown (rolldown)Native .d.ts, dual ESM+CJS, same vendor as oxc
Lintoxlint + tsgolintType-aware via the real compiler, 12–18× ESLint
FormatoxfmtPrettier-compatible, one binary
TestsVitest + fast-checkRuntime, type-level and property tests in one runner
ReleasesChangesets + OIDCTrusted publishing, no long-lived npm token

No ESLint, no Prettier — the Rust toolchain does both jobs in a fraction of the time and a fraction of the node_modules.

TypeScript 7 has no JS API

This is the single most consequential fact about building on TS 7 today. Its package exports maps "." to a three-line version.cjs. There is no ts.createProgram.

Anything that does import ts from 'typescript' simply does not work: TypeDoc, ts-morph, api-extractor, dts-bundle-generator, tsd, typescript-eslint. The API is expected to return, in a different form, in TypeScript 7.1.

Two consequences visible in this repo:

  • The API reference is generated by a small in-repo script rather than TypeDoc. Its completeness is guaranteed by reading the export inventory from the built package at runtime, so an undocumented export is a build failure.
  • TypeScript is pinned to ~7.0.2, not ^7.0.2. rolldown-plugin-dts gates its tsgo path on an exact versionMajorMinor === "7.0" comparison and falls back to a package that is not in the dependency graph. A single unguarded minor bump breaks declaration emit and desynchronises the linter from the compiler.

The declaration pipeline has no exit code

tsdown drives rolldown-plugin-dts, which spawns the compiler with --noCheck and resolves on process close without reading the exit status.

Declaration-emit-only diagnostics — TS4023 ("cannot be named"), TS2742 ("inferred type cannot be named without a reference to X") — are exactly what deeply inferred curried generics provoke, and they are suppressed there. The plugin throws only if an entry's .d.ts is missing entirely; a type degraded to any would ship silently.

So pnpm check:dts-emit runs the real compiler with declaration emit and a real exit code, as a required CI job, plus a grep asserting no exported declaration contains : any. It is not hygiene — it is the only thing between this pipeline and shipping a broken product.

Zero ambient dependencies

tsconfig.json sets types: [], so the published .d.ts cannot depend on @types/* from devDependencies. That is why there is no Task.delay: setTimeout is a host API, and including it would have meant widening lib.

tsdown builds with platform: 'neutral' on the same premise.

Supply chain

Every GitHub Action is pinned to a full commit SHA with a version comment, and Renovate updates those pins — a pinned SHA nothing updates is a stale SHA.

ControlNotes
gitleaksMIT CLI, pinned + checksum-verified. Deliberately not gitleaks-action, which has been proprietary since v2.0.0 and is free only under a personal account.
OSV-ScannerReads pnpm-lock.yaml natively; no account, no token.
CodeQLjavascript-typescript. Found a real arity defect during development.
OpenSSF ScorecardPublished to the security tab.
Dependency reviewBlocks copyleft licences that would contaminate MIT.
npm provenanceSLSA attestation via OIDC trusted publishing.

Snyk was removed

It was wired in, given a token, and removed once it proved unable to say anything true here. Three failures compound:

  1. Snyk cannot parse pnpm 11's lockfile — pnpm writes a multi-document YAML file and Snyk's parser expects one document.
  2. npm install --package-lock-only crashes on this dependency tree (@npmcli/arborist throws), so no substitute lockfile can be generated.
  3. Without a lockfile, snyk test --file=package.json exits 0 with "no vulnerable paths found" while --print-deps shows the scanned tree as smullyan @ 0.0.0 — zero dependencies. continue-on-error: true hid the hard parse failure for a full run and reported the job green.

A scan that reports clean because it scanned nothing is worse than no scan: it trains you to trust a signal carrying no information. OSV-Scanner covers the same advisory databases, correctly, on the real lockfile.

Releasing

The first publish had to be manual — trusted publishing cannot create a package, and since 2026-07-31 bypass-2FA tokens can no longer modify trusted-publishing configuration either. Everything after that is automated, with one deliberate manual step documented in Releasing.

MIT licensed.