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.