Portfolio Manifest — Prepared 2026-08-20

Terrence Daniels

Full-Stack Node.js/Vue Engineer

An independently maintained port of Directus's own monorepo — a real-time API/App dashboard for managing SQL database content — migrated from pnpm to Yarn and rebuilt one package at a time . All 40 workspace manifests are done; real source is genuinely early, three packages in, but every migration decision along the way has been verified against a real install or a real compiler run, and every gap found — five fixed, one tracked openly — is disclosed in this repo's own build log, not smoothed over.

40/40 manifests migrated
3 of 40 packages source started
56 of 56 types package files (complete)
5 real gaps found & fixed
326 entry Yarn catalog rebuilt
5 public surfaces kept in sync

Why this project

A real package-manager migration, verified rather than trusted

Moving a large monorepo off pnpm is a genuinely common, genuinely underrated engineering task — the kind of work that shows up in real migrations far more often than greenfield builds do. Directus's own monorepo (40 workspace packages, a 331-entry shared version catalog, 17 CI workflows, two Dockerfiles) made a useful subject precisely because pnpm has mechanisms Yarn has no exact equivalent for: each one needed a real decision, tested against a real install, not a find-and-replace. Two of those first-pass decisions turned out to be wrong and got corrected properly rather than left standing.

Yarn 4.18.0 Node.js 26 TypeScript 5.9.3 Vue 3 Express / Knex GitHub Actions CodeQL Docker

Manifests first, then real source, one file at a time

All 40 package.json files done · directus/ source complete, not yet runnable · types/ complete (56/56) · schema/ started (3/16)

Migration order is deliberate: every workspace's package.json was individually inspected and transformed before any real source landed — yarn install has resolved the complete dependency graph since that phase finished. Real source is additive from there. directus/ (the CLI wrapper, 6 files) has a complete source tree, though not yet a runnable one — cli.js imports two still-manifest-only packages ( @directus/update-check , @directus/api/cli/run.js ), the same "manifests first" gap packages/types already discloses for itself; packages/types is now complete — 56 of 56 planned files, added one file per commit, each one diffed byte-for-byte against Directus's real upstream source before landing, not reconstructed from memory — though it doesn't fully type-check yet: cross-package dependencies on @directus/constants and @directus/schema touch several files each, plus one more on @directus/ai . packages/schema is newly started (3 of 16 files), directly chipping away at two of those gaps. The other 37 packages, sdk , api , and app are still manifests waiting on their source trees; see todo.md for the honest current state rather than assuming more is done.

Verification over assumption

Every ported file gets a real diff -u against upstream before it's committed. Every import is checked against what's actually landed in the fork, not what's expected to be there. The isolated-vm fix below was confirmed by reading real g++ compiler output, not by guessing at a Node compatibility table.

Gaps disclosed, not hidden

A permissive enableScripts: true default (Yarn has no scoped install-script allowlist like pnpm's) and a Docker build-cache regression (no equivalent to pnpm fetch 's layer-caching trick) are both accepted trade-offs — written down as real regressions in todo.md , not silently absorbed.

Five real gaps, found and fixed

Found by verifying against a real install or a real compiler run, not by assuming the first pass was right

Two turned up in this fork's own first-pass migration work and got corrected before anything was left standing on a wrong assumption. Two turned up in Directus's own upstream source or its published tooling — genuine gaps in a widely-used project, not typos. One turned up in a VS Code extension's own stale bundled data, misdiagnosing this repo's real config as broken.

01
isolated-vm@5.0.3 — incompatible with Node 26's V8
The version inherited from Directus's own catalog failed to compile identically on Windows and the GitHub Actions Ubuntu runner. Real compiler output ( gh run view --log-failed ) showed the native addon calling V8 APIs Node 26's bundled V8 has since removed or changed — Template::SetAccessor , GetPrototype , internal-field accessor signatures. Every other native module in the ~2,150-package graph built cleanly, ruling out a missing-toolchain theory first. Fixed by bumping to 7.0.1 , verified with a real yarn install .
high · build-breaking
02
"Yarn has no shared-catalog feature" — wrong, corrected
Stood documented as fact — in this repo's own docs, its wiki, and several commit messages — until yarn config -v 's full settings dump (not yarn help , which doesn't mention it) surfaced real catalog / catalogs config keys. Verified in a throwaway scratch project before touching this repo. Fixed properly, not just reverted: a 326-entry catalog rebuilt from the original 331 (5 confirmed-dead entries dropped), and 671 of 673 literal-pin conversions restored to catalog: across 40 files, each diff reviewed before committing.
high · process correction
03
deploy-production.mjs — pruned the live repo's own node_modules
pnpm's deploy --legacy --prod has no Yarn equivalent; the first replacement script pruned this repo's own root node_modules in place — a real regression from pnpm's separately-built deploy output. Reconsidered: yarn workspaces focus --production only needs a checkout at the right commit, not the live working tree, so the script now runs inside a disposable git worktree instead. Verified end-to-end — a working dist/ with 724 node_modules entries came out the other side.
high · destructive-action correction
04
@directus/tsconfig — neither base nor node22 config sets rootDir
A real gap in Directus's own published @directus/tsconfig npm package (v4.0.0): with outDir set but no explicit rootDir , TypeScript falls back to inferring it from the "common source directory" of the include set. Real tsc 5.9.3 builds identically either way — confirmed by running it directly and diffing the output before and after — but newer/preview TS tooling errors on the implicit inference. Fixed with an explicit "rootDir": "./src" in packages/types/tsconfig.json , the same value already being inferred.
low · tooling gap
05
Webhint — stale bundled schema flagged a valid tsconfig lib value
The VS Code Webhint extension's typescript-config/is-valid hint flagged packages/types/tsconfig.json 's inherited lib: ["ES2023"] as invalid. It isn't — TypeScript 5.9.3 (the version actually installed) has supported ES2023 as a lib value since 5.2; the hint's bundled schema is just out of date. Confirmed against the real installed TypeScript version before suppressing, and scoped the suppression to that one hint only rather than disabling Webhint's defaults wholesale.
low · false positive
Honest gap, tracked not solved: release-notes-generator 's package.json no longer lists @pnpm/workspace.find-packages , @pnpm/workspace.pkgs-graph , or @pnpm/logger — but the actual source file that imports them, src/utils/process-packages.ts , hasn't been rewritten yet. Caught and tracked rather than assumed fixed just because the manifest looked clean. Planned replacement: read each workspace's package.json directly and build the dependency graph from workspace:* references — no package-manager-specific API needed at all.

Keeping the record honest across five surfaces

todo.md, architect.md, the profile README, the portfolio site, and the wiki — checked, not assumed in sync

Progress claims that live in more than one place drift, quietly, unless someone actually checks. A full sweep of every surface that cross-references this repo's status — done after the twelfth file landed in packages/types — found three wiki pages still claiming only one workspace package had real source, stale since the first file after directus/ landed. Fixed the same day, verified live afterward rather than assumed fixed on commit.

Per-file discipline

Every file added to packages/types gets its own commit, followed by a second commit updating todo.md and architect.md 's file counts — not batched at the end of a session where drift is easy to lose track of.

Occasional, not per-file, elsewhere

The wiki , scrum board , and the family profile/portfolio pages carry coarser claims — swept periodically instead, since updating them per-file would be noise for a five-page wiki tracking a 54-file package.

Judgment calls

Where the real trade-offs got made, and written down

Not every pnpm mechanism has a clean Yarn replacement, and pretending otherwise would just move the gap somewhere less visible. onlyBuiltDependencies — pnpm's default-deny list for which packages may run install/postinstall scripts — has no Yarn Berry equivalent; enableScripts: true is Yarn's permissive default, and that's a real security-posture downgrade, accepted and documented rather than quietly inherited. Similarly, pnpm fetch 's layer-caching trick (populate the store from just the lockfile before copying in source) has no equivalent under Yarn workspaces, since every workspace's package.json needs to be present to resolve at all — so both Dockerfiles now do one COPY . . then yarn install , meaning any source change invalidates the install layer too. Both trade-offs are recorded in todo.md as accepted regressions, not oversights discovered later.