Skip to content

Restructure src around what needs the extension and what does not - #249

Merged
droplister merged 1 commit into
mainfrom
refactor/structure
Aug 4, 2026
Merged

Restructure src around what needs the extension and what does not#249
droplister merged 1 commit into
mainfrom
refactor/structure

Conversation

@droplister

Copy link
Copy Markdown
Member

Squashes #244#248 into one change. The tree is byte-identical to the stack tip — verified with git diff --quiet against src/.

utils/ was 139 files and 32,332 lines — 42% of the codebase, holding every security-critical module. The name said "small helpers"; the contents were the domain.

The split is measured, not stylistic

The argument is a security property made enforceable: code that derives keys, builds transactions and verifies them should be unable to depend on browser or session state. That's what makes it auditable in isolation and testable without a browser.

Every file was checked for whether it — or anything it transitively imports — touches chrome.*, browser.*, wxt or webext-bridge:

coupled total
blockchain/ 0 77
validation/ 0 14
encryption/, qr-code/, hardware/ 0 9
storage/ 9 10
auth/ 2 2

100 of 139 files were already portable, and exactly one crossed the linewalletManager, which reaches for session storage. Moving that single file took core's dependencies on platform from three to zero.

  • core/ — cannot reach the extension runtime
  • platform/ — is the extension

core/__tests__/layering.test.ts asserts the direction rather than documenting it. An auto-import would otherwise undo it silently.

Regrouping inside core

  • api/ — client, status and cache existed as three loose files serving each other
  • blockchain/ removed — it held bitcoin, counterparty and counterwallet and nothing else, prefixing 77 files while discriminating nothing. Longest path: 73 → 62 chars
  • Flattening collided two errors.ts, so each is now named for what it is: rpcErrors.ts (JSON-RPC codes) and errors.ts (blockchain hierarchy)
  • security/ removed — two unrelated files under a word describing everything in a wallet. That's how utils/ started

components had the same drift

ui/ is meant to be widgets that don't know what an asset is. domain/ held 4 files; ui/ held 27 referencing assets, dispensers, balances and wallets 8–41 times each (asset-select-input: 41). Those move to domain/, placed by subject since that's how domain/ is already organised. ui/ now has none.

Deletions — 2,312 lines

Three modules reachable only from their own tests: qrCode.ts (sanitised scanned QR input; the wallet has no scanner), RequestManager.ts (superseded by approvalService), address-card.tsx (no reference at all). The earlier sweep missed them because it counted any importer, and a test counts.

Naming and wiring

Filenames now agree with their area — core is camelCase, components is kebab-case — and platform/wallet/, a folder holding one file, is flattened.

The two validators the deletion pass deliberately spared are now wired up. parseCSV replaces the MPMA form's regex-over-split-lines, adding a row cap, real quoted-field parsing and formula-injection rejection. The CSV upload had no size limit at all, so validateFile bounds it at 2 MB.

Neither is wired into the inscription upload, deliberately. detectMaliciousContent rejects any text file containing <script or <iframe — and an HTML inscription carrying a script tag is exactly what that feature publishes. It would have broken inscriptions, which only started working in 0.7.0.

Verification

  • tsc --noEmit clean; biome check src clean (672 files)
  • vitest run src3965 passed, 49 skipped
  • wxt build succeeds
  • playwright test e2e/pages/compose/send — 26 passed
  • Squashed tree confirmed identical to the reviewed stack

269+ of the changed files are pure renames. No logic changed except the CSV parsing swap, which has tests for both new protections.

Note

#232 (Trezor Connect 10, draft) touches utils/hardware/, now core/hardware/. It needs rebasing whenever v10 stabilises.

utils/ was 139 files and 32k lines - 42% of the codebase and every
security-critical module in it. The name said "small helpers"; the contents
were the domain. That mis-naming is not cosmetic: ranking files by directory
or import count put an icon barrel at the top and buried the modules
everything depends on.

Split by measurement, not taste. Every file was checked for whether it or
anything it transitively imports touches chrome.*, browser.*, wxt or
webext-bridge. blockchain (77 files), validation, encryption and qr-code
came back at 0% coupling; auth at 100%, storage at 90%. 100 of 139 files
were already portable, and exactly one crossed the line - walletManager,
which reaches for session storage. Moving it alone took core's dependencies
on platform from three to zero.

  core/      cannot reach the extension runtime: packing and unpacking
             messages, signing, key derivation, encryption, validation,
             formatting, the API client and its cache
  platform/  is the extension: chrome storage, the session keychain, the
             dapp bridge, analytics, popup and proxy plumbing

core/__tests__/layering.test.ts asserts the direction rather than
documenting it: platform may call into core, core may not reach back, and
nothing under core may touch chrome.* or import webext-bridge, #imports or
wxt. An auto-import would otherwise undo this silently.

Inside core: api/ groups the client, its health check and its cache, which
existed as three loose files serving each other. blockchain/ is gone - it
held bitcoin, counterparty and counterwallet and nothing else, so it
prefixed 77 files while discriminating nothing; the longest path drops from
73 characters to 62. Flattening collided two modules both named errors.ts,
so each is now named for what it is: rpcErrors.ts for the JSON-RPC codes,
errors.ts for the blockchain hierarchy. security/ is gone too - it held two
unrelated files under a word that describes everything in a wallet, which is
how utils/ started.

components/ had the same drift one level down. ui/ is meant to be widgets
that do not know what an asset is, and domain/ is where the wallet-aware
ones go; domain/ held 4 files while ui/ held 27 referencing assets,
dispensers, balances and wallets 8 to 41 times each. Those move to domain/,
placed by subject because that is how domain/ is already organised. ui/ now
has none.

Deletes three modules reachable only from their own tests: qrCode.ts
sanitised scanned QR input and the wallet has no scanner, RequestManager is
superseded by approvalService, address-card has no reference at all. The
earlier dead-code sweep missed them because it counted any importer and a
test counts.

Filenames now agree with their area - core is camelCase, components is
kebab-case - and platform/wallet, a folder holding one file, is flattened.

Finally wires up the two validators the deletion pass left alone. The MPMA
form parsed CSV with a regex over split lines; parseCSV replaces it and adds
a row cap, real quoted-field parsing and rejection of spreadsheet formula
injection. The CSV upload had no size limit at all, so validateFile bounds
it. Neither is wired into the inscription upload: detectMaliciousContent
rejects any text file containing <script or <iframe, and an HTML inscription
carrying a script tag is exactly what that feature publishes.

Claude-Session: https://claude.ai/code/session_01CcjnCrgosSeshymXLBxdGj
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