refactor(di): keep mock gateways out of production builds (FH-E.6)

21 DI tokens selected their implementation like this:

  factory: () => (environment.useMockData ? inject(XLocal) : inject(XApi))

That reads as a toggle and is not one. Naming both classes in the factory
keeps both reachable, so every mock shipped regardless of the flag - and
`useMockData` is false in both environment files, so none of them were
ever the selected implementation in the first place. Verified: a fixture
string from partner-hierarchy-local.gateway.ts was present in a
production bundle.

Token factories now inject the API gateway unconditionally. Mock
overrides move to src/app/mock-gateway.providers.ts, swapped for a
production copy that imports nothing, via the same fileReplacements
mechanism mock-data.interceptor.production.ts already uses. Dev behaviour
is unchanged - flip useMockData in environment.ts exactly as before.

useExisting rather than useClass: the local gateways are already
providedIn: 'root' singletons, and an app-level provider for the token
wins over its tree-shakable default.

scan-bundle.sh gains two patterns so this cannot come back: any
*LocalGateway class name, and known fixture literals. Verified in both
directions - clean against the real dist, exit 1 against a planted
OfferLocalGateway.

Result: zero LocalGateway classes and zero fixtures in the production
bundle, down from 21 classes and 75 kB of source. Initial bundle is
unchanged at 1.55 MB because these all sat in lazy chunks; the win is
that production can no longer serve seeded fixtures as real data, not
bytes off the critical path.

Not addressed here: MediaRepository is still bound to MockMediaRepository
unconditionally in app.config.ts. That one cannot be deleted - no real
implementation exists yet - so it is a missing API gateway, not dead
weight. Tracked separately.

256 tests pass. Build green, boundaries and cycles green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-08-21 15:59:11 +04:00
parent a3808842c1
commit a35953d90f
26 changed files with 142 additions and 65 deletions

View File

@@ -2,6 +2,13 @@
# Fails the build if a production bundle contains anything that should only
# ever exist server-side.
#
# It also fails on mock gateway code, for the same reason in a different
# register: a production build that can reach a *LocalGateway is a production
# build that can serve seeded fixtures as if they were real data. Those used to
# ship - a fixture string from partner-hierarchy-local.gateway.ts was present in
# a production bundle on 2026-08-21 - because naming both classes in a token
# factory kept both reachable no matter what the flag said.
#
# Why this exists: the storefront used to send provider payment credentials
# from the browser - an `authorization-key` header, a `userid-value` header,
# and a hardcoded partner ID literal compiled into the bundle. That code is
@@ -30,6 +37,8 @@ PATTERNS=(
"private key block|BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY"
"aws access key|AKIA[0-9A-Z]{16}"
"telegram bot token|[0-9]{8,10}:AA[0-9A-Za-z_-]{33}"
"mock gateway class|[A-Za-z]+LocalGateway"
"mock gateway fixture|ptr_local|customer_vk_mock"
)
failed=0
@@ -49,7 +58,9 @@ done
if [[ $failed -ne 0 ]]; then
echo >&2
echo "A credential reached the browser bundle. Move it behind the API." >&2
echo "Something reached the browser bundle that should not have." >&2
echo "Credentials belong behind the API; mock gateways belong in dev-only" >&2
echo "providers swapped out by angular.json fileReplacements." >&2
exit 1
fi