Portfolio Manifest — Updated September 19, 2026

Terrence Daniels

Principal Frontend Engineer

A redesigned rebuild of Directus's admin-panel state layer, package by package: Pinia stores built fresh from the real project's domain, not reproduced from its source — early-stage, and honest about that scale, with the reasoning for each redesign decision written down as it happens.

236commits
121Vitest tests
96%line coverage
0lint errors
0CodeQL findings
3packages + 2 in progress + 1 live app + api/ in progress

Why this project

Redesign is the default, not the exception

Directus is a real, battle-tested admin panel — a legitimate reference point for what a CMS state layer actually needs. Rather than reproducing its source, this repo takes the real project as a domain reference and rebuilds each piece from that understanding: same problem space, independent implementation, judged on its own merits rather than measured against fidelity to the original.

Vue 3 Pinia TypeScript Yarn Berry Vitest GitHub Actions CodeQL

Redesign by default, not by exception

Independent refs that must stay in sync are a bug waiting to happen

The real Directus useAppStore tracks hydration as three separate, independently-mutated refs — hydrated, hydrating, error — nothing stops all three from being set at once into a state that's actually meaningless (hydrated and errored, simultaneously). The same shape of risk showed up independently in this repo's own first pass at session state: accessToken and expiresAt as two separate nullable refs that had to be kept in lockstep by hand.

One discriminated value, not several refs

Both cases were collapsed into a single value with a fixed set of valid shapes — Session | null for auth, a { status } union for hydration. Invalid combinations aren't handled defensively; they're not representable at all.

Applied twice, on purpose

The second instance (useAppStore) wasn't a coincidence — it was deliberately checked for, once the first fix in useAuthStore established the pattern was worth applying consistently rather than treating it as a one-off.

Coverage doesn't catch everything

A completeness audit, prompted by comparing this package's file count against the real source

100% statement/branch coverage was already true before this audit — and it still let four real gaps through, because every line executing isn't the same as the right value ending up in the right place. All four found and fixed in the same pass, once file-count parity with the real source prompted a second look.

01
auth.ts
Session wasn't exported, unlike every other store's public type — a consumer could use the value but never name its type.
consistency commit a381c27
02
package.json
A strict exports map with no ./package.json subpath blocks any tooling that resolves a package's own package.json directly.
tooling commit ec2205c
03
auth.test.ts, user.test.ts
Both only asserted derived values (loggedIn, fullName), never the raw stored state — a bug that swapped stored fields would still pass every existing test.

What prompted it: asked why this package had more files than the real source's — the honest answer was the real packages/stores is one flat store with 7 raw refs and no computed properties or actions at all. That comparison is what triggered auditing every file in this package against itself, not just against the original.

Verifying it actually works

CI on every push, not a one-time local check

Every store change runs through the same five real gates before being called done — format, lint, style lint, build, test — then again on GitHub Actions infrastructure this repo doesn't control. Two real CI failures were hit and fixed during this build, both from the same root cause: pushing a yarn.lock or a config file ahead of the file it actually depends on.

20 tests, 0 lint errors

Across every store added so far, real assertions against real behavior — including regression tests for the two bugs this project found and fixed in itself.

CodeQL, checked, not assumed

0 open findings as of this page's last update — verified against the repository's own Code Scanning API, not asserted from memory.

Judgment calls

Building more than the reference, on purpose

A direct file-count comparison against the real source could have gone the other way: find out the real packages/stores is a single 38-line file and conclude this repo was over-built. Instead, the real source's own code says otherwise — a literal @TODO maybe move to userStore comment sitting on its authenticated field, acknowledging the flat single-store shape was never the intended end state. Building three purpose-specific stores instead of one grab-bag isn't scope creep relative to that; it's finishing a direction the original source already pointed at but hadn't taken yet.

Beyond the stores: a real, deployed app

Now the state layer has something to sit inside

app/ is a real Vue app, not a placeholder: routed (vue-router), styled, with a working login/logout flow against a simulated authClient — try it yourself: demo@directus-main.dev / demo1234, or anything else to see the real InvalidCredentialsError path. packages/errors shipped alongside it: a real DirectusError class hierarchy, redesigned away from the source's factory-function-plus-enum pattern.

A repo-wide drift sweep found a real bug

Adding a test for the router guard's "already logged in" redirect exposed a genuine, previously silent defect: earlier tests' mounted components were never unmounted, and since they all shared one router instance, their stale registrations interfered with later tests. Fixed with enableAutoUnmount, applied everywhere the same risk existed — not just where it had actually failed yet.

The same sweep found this page stale

This page's own numbers were stuck describing an earlier phase until this pass — the manifest above, the issue tracker, the wiki, and the external portfolio hub all got the same audit. Verifiable is only true if it's kept current.

A real backend, one route at a time

Session persistence shipped, then a real server started

Session persistence shipped next: a hard refresh no longer logs you out, and testing it caught a real bug along the way. Then api/ started: a real Express server with a tested POST /auth/login, reusing the same InvalidCredentialsError the app already used against its fake client — the server side isn't simulated anymore, even though app/ hasn't been pointed at it yet.

Pinia's default flush batches, silently

$subscribe's default flush option waits for the next Vue tick — fine for UI updates, wrong for a cookie write that needs to survive a real page unload right after login. A test that set a session and immediately checked the cookie caught it: the write hadn't happened yet. Fixed with an explicit { flush: 'sync' }.

Same demo login, two files, on purpose — for now

authClient.ts's fake client and api/'s real route both hardcode the same demo credentials. Not centralized into a shared file: that duplication is a symptom of the fake client being temporary, and resolves to zero once app/ calls the real endpoint instead of simulating one — only the backend should ever know what a valid login looks like.