fix(env): reconcile env schema with reads and fail fast at boot - #18
Merged
ibrahimmosouf-png merged 1 commit intoAug 23, 2026
Merged
Conversation
9 tasks
9 tasks
Close the drift between lib/env.ts, .env.example, and the process.env reads scattered across app/, components/, hooks/, lib/, store/, features/, utils/, and middleware.ts. Declare every read in RULES, sync .env.example (REDIS_URL, Cloudinary, base URL, error tracking), align the API URL fallback with the documented localhost:3001, and wire assertEnv into server boot via instrumentation.ts so missing required variables fail fast with the variable name. Add a Node test that statically scans the repo and fails when a read is undeclared or the template drifts from the schema. Also fix pre-existing type errors (adminStore, draftStore, campaignDeployer) that blocked the required type-check and build gates.
naobadiah01
force-pushed
the
fix/issue-9-env-contract
branch
from
August 23, 2026 16:39
ed37f55 to
13f55de
Compare
Degentle12
added a commit
to Degentle12/OrbitChain-Web
that referenced
this pull request
Aug 23, 2026
The wallet store fetched balances from a hardcoded Horizon testnet URL, so mainnet wallets always displayed a zero balance. Resolve the Horizon base URL from the configured network (NEXT_PUBLIC_STELLAR_NETWORK with a NEXT_PUBLIC_STELLAR_HORIZON_URL override) via a new resolveHorizonUrl() in lib/stellar/config.ts, and move the fetch into a pure lib/stellar/balance.ts module that distinguishes Horizon 404 (a valid zero balance for a new account) from real lookup failures, which now surface as a balanceError instead of a silently wrong zero. Balance refreshes on connect and every 15s; the duplicate hardcoded fetch in Header.tsx is removed and the wallet store owns the refresh lifecycle. Adds unit tests with mocked fetch covering native balance, no native asset, 404, network error, non-404 HTTP error, and mainnet vs testnet URL selection. Also fixes pre-existing type errors in adminStore, draftStore, and campaignDeployer that blocked the required type-check and build gates (identical to PR OrbitChainLabs#18).
nasalehj
added a commit
to nasalehj/OrbitChain-Web
that referenced
this pull request
Aug 23, 2026
The repository had no test infrastructure and no CI: package.json had no test script, no test files existed, and .github/workflows did not exist, so pure logic (Stellar formatting/validation/error mapping, the cache manager, the auth and UI stores, image uploads) had zero regression protection. Introduce Vitest as the runner (TS-native, zero-config unit tests, built-in jsdom for browser-API tests, and a path to React Testing Library for future component tests), add a test script, and write 76 unit tests across the seven modules the issue lists. Add a GitHub Actions workflow that runs type-check, lint, test, and build on push and pull request with no required secrets, and document npm test in the README. Also fixes pre-existing type errors in adminStore, draftStore, and campaignDeployer that blocked the required type-check and build gates (identical to PRs OrbitChainLabs#18 and OrbitChainLabs#19).
nasalehj
added a commit
to nasalehj/OrbitChain-Web
that referenced
this pull request
Aug 23, 2026
The repository had no test infrastructure and no CI: package.json had no test script, no test files existed, and .github/workflows did not exist, so pure logic (Stellar formatting/validation/error mapping, the cache manager, the auth and UI stores, image uploads) had zero regression protection. Introduce Vitest as the runner (TS-native, zero-config unit tests, built-in jsdom for browser-API tests, and a path to React Testing Library for future component tests), add a test script, and write 76 unit tests across the seven modules the issue lists. Add a GitHub Actions workflow that runs type-check, lint, test, and build on push and pull request with no required secrets, and document npm test in the README. Also fixes pre-existing type errors in adminStore, draftStore, and campaignDeployer that blocked the required type-check and build gates (identical to PRs OrbitChainLabs#18 and OrbitChainLabs#19).
nasalehj
added a commit
to nasalehj/OrbitChain-Web
that referenced
this pull request
Aug 23, 2026
The repository had no test infrastructure and no CI: package.json had no test script, no test files existed, and .github/workflows did not exist, so pure logic (Stellar formatting/validation/error mapping, the cache manager, the auth and UI stores, image uploads) had zero regression protection. Introduce Vitest as the runner (TS-native, zero-config unit tests, built-in jsdom for browser-API tests, and a path to React Testing Library for future component tests), add a test script, and write 76 unit tests across the seven modules the issue lists. Add a GitHub Actions workflow that runs type-check, lint, test, and build on push and pull request with no required secrets, and document npm test in the README. Also fixes pre-existing type errors in adminStore, draftStore, and campaignDeployer that blocked the required type-check and build gates (identical to PRs OrbitChainLabs#18 and OrbitChainLabs#19).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #9
Reconciles the environment contract across all three surfaces that had drifted apart: the schema in
lib/env.ts(RULES),.env.example, and everyprocess.env.*read in the codebase. Every read is now declared in the schema,.env.examplematches the schema, the API URL fallback agrees with the documented example (http://localhost:3001), andassertEnvis wired into server boot throughinstrumentation.tsso a missing required variable fails fast with its name instead of surfacing as a cryptic runtime error.The most important design decision:
assertEnvruns at server boot (next dev/next start) rather than insidenext build, because in Next.js 14.2 the instrumentation hook (still experimental, enabled vianext.config.js) is invoked at server startup and respects the schema's required/optional split — optional keys only warn, required keys throw. A static-scan regression test (Node's built-in test runner, zero new dependencies) closes the drift permanently.Why
Before this change,
lib/env.tswas documented as the single source of truth for environment variables but nothing enforced it:assertEnvwas defined and never called, so misconfiguration failed at runtime, not at boot.NEXT_PUBLIC_BASE_URL(sitemap/robots),NEXT_PUBLIC_ERROR_*(error tracking),NEXT_PUBLIC_STELLAR_EXPLORER_URL,CLOUDINARY_API_KEY/CLOUDINARY_API_SECRET(server-side delete route),NEXTAUTH_SECRET/OAuth ids, and the legacyDB_*/APP_NAMEscaffolding were absent from the schema..env.exampleomittedREDIS_URL, the Cloudinary server secrets,NEXT_PUBLIC_BASE_URL, and the error-tracking variables.http://localhost:5000/apiin four places, plus two strayhttp://localhost:3000) disagreed with the documentedhttp://localhost:3001, so a fresh clone without.env.localtalked to the wrong port.Additionally,
npm run type-checkandnpm run buildwere already failing onmainfrom pre-existing type errors inlib/server/adminStore.ts,lib/server/draftStore.ts, andlib/server/campaignDeployer.ts(the latter was written against the removedSorobanRpc/scvalAPI of@stellar/stellar-sdkv11, while the repo installs v14.5.0). Since the acceptance criteria requiretype-checkandbuildto pass, those were fixed minimally and are documented below.What was built
lib/env.tsRULES) now declares everyprocess.env.*read in the scanned directories, includingNEXT_PUBLIC_BASE_URL,NEXT_PUBLIC_APP_URL, error-tracking variables,NEXT_PUBLIC_STELLAR_EXPLORER_URL,CLOUDINARY_API_KEY/CLOUDINARY_API_SECRET,NEXTAUTH_SECRET, OAuth ids/secrets, and the legacyAPP_NAME/DB_*group.RULESis exported so the regression test can read it directly. New variables are optional (the code already degrades gracefully without them); only the pre-existing required set stays required, so existing builds don't break.NODE_ENVis deliberately excluded and handled by the test's build-time allowlist..env.exampleREDIS_URL,CLOUDINARY_API_KEY/CLOUDINARY_API_SECRET,NEXT_PUBLIC_BASE_URL,NEXT_PUBLIC_STELLAR_EXPLORER_URL,NEXT_PUBLIC_CAMPAIGN_CONTRACT_ID, and the error-tracking variables added; optional variables documented as commented entries; API URL stayshttp://localhost:3001.instrumentation.tsassertEnv()at server boot so missing required variables fail fast with the variable name.next.config.jsexperimental.instrumentationHook(required for instrumentation to run in Next.js 14.2).tests/env-contract.test.tsapp/,components/,hooks/,lib/,store/,features/,utils/, andmiddleware.ts, asserting everyprocess.env.Xread is declared inRULES(allowlisting the framework-managedNODE_ENV); (2) asserts everyRULESkey is present in.env.example. Runs on Node's built-in test runner with zero new dependencies.package.json"test": "node --test \"tests/**/*.test.ts\"".tsconfig.json"target": "ES2020"(was unset, defaulting to ES3 — this is what made BigInt literals incampaignDeployer.tsfail) and"allowImportingTsExtensions": true(required so the test can importlib/env.tsunder Node's native TypeScript support; safe withnoEmit).README.mdnpm testscript in the scripts table and the Testing section.The regression test is the enforcement mechanism for the reconciliation: it reads
RULESdirectly fromlib/env.tsand scans real source files, so neither the schema nor the template can drift again without a failing test.Integration changes outside
lib/lib/server/adminStore.ts— pre-existingnoUncheckedIndexedAccesstype errors (7) fixed:users[index] = { ...users[index], ... }patterns now captureconst current = users[index]; if (!current) return undefined;before spreading. Behavior is unchanged.lib/server/draftStore.ts— same pre-existing pattern fixed insaveDraft(1 error), with an explicit consistency guard.lib/server/campaignDeployer.ts— pre-existing errors (3) from being written against@stellar/stellar-sdkv11 APIs that no longer exist in v14.5.0:SorobanRpc.Server→rpc.Server,SorobanRpc.isSimulationError→rpc.Api.isSimulationError,SorobanRpc.assembleTransaction(...).sign(...)→rpc.assembleTransaction(...).build()thensign()(v14signmutates in place and returns void),scval.*arg building →nativeToScVal/xdr.ScVal.scvMapwith a smallscMaphelper that preserves symbol map keys (what#[derive(Serialize)]contract structs expect) and sorts keys like the SDK does,sendResult.status === 'FAILED'→'ERROR'only (v14 status union),errorResult?.resultXdr→errorResult?.toXDR('base64'). Also itsNEXT_PUBLIC_API_URLfallback was aligned tohttp://localhost:3001.lib/api/client.ts,lib/auth/verifyToken.ts,app/(main)/projects/[id]/page.tsx,components/donations/DonationModal.tsx,components/donations/DonationChart.tsx,components/donations/RecentDonations.tsx— API URL fallback aligned tohttp://localhost:3001so the code default and.env.exampleagree.These are the only pre-existing failures that blocked the required
type-check/buildgates; they are mechanical fixes, not refactors.Acceptance criteria coverage
process.env.*read inapp/,components/,hooks/,lib/,store/,features/,utils/, andmiddleware.tsis declared inRULESinlib/env.ts. (tests/env-contract.test.ts— "every process.env.* read is declared in lib/env.ts RULES").env.examplematches the schema:REDIS_URL,CLOUDINARY_API_KEY/CLOUDINARY_API_SECRET,NEXT_PUBLIC_BASE_URL, and the error-tracking variables are present, and the API URL default and example agree. (.env.examplerewrite;tests/env-contract.test.ts— "every RULES key is present in .env.example"; API fallbacks nowhttp://localhost:3001)assertEnvruns at build or server boot and throws with the variable name on a missing required value. (instrumentation.ts+next.config.js; verified:next startwithout env throws[env] ❌ Build failed — required environment variables are missing or invalid: NEXT_PUBLIC_API_URL, NEXT_PUBLIC_STELLAR_NETWORK, NEXT_PUBLIC_STELLAR_HORIZON_URL, NEXT_PUBLIC_SOROBAN_RPC_URL, NEXT_PUBLIC_STELLAR_NETWORK_PASSPHRASE, NEXT_PUBLIC_WALLET_APP_NAME, NEXT_PUBLIC_WALLET_APP_URL, AUTH_SECRET, DATABASE_URLand exits)process.env.Xread is added without a schema entry. (Verified by probe: addingprocess.env.NEXT_PUBLIC_UNDECLARED_TEST_VARtoutils/made the test fail, naming the variable and file; probe removed)npm run type-check,npm run lint, andnpm run buildpass. (See Test plan)Test plan
npm test— 2/2 passing (2 new tests)npm run type-check— no errorsnpm run lint— no warnings or errorsnpm run build— succeeds (verified with a local, gitignored.env.localcontaining valid required values)next startwithout env fails fast listing every missing required variable; with valid env it boots and serves.Env vars / Notes
No new environment variables were added beyond what the code already reads, and no existing variable changed meaning. All newly declared variables are optional (the code already falls back or degrades gracefully); only the schema's pre-existing required set (
NEXT_PUBLIC_API_URL,NEXT_PUBLIC_STELLAR_*,NEXT_PUBLIC_WALLET_APP_NAME,NEXT_PUBLIC_WALLET_APP_URL,AUTH_SECRET,DATABASE_URL) throws at boot when missing — this respects the issue's requirement that optional keys never breaknext build.Operational notes:
next dev/next startwithout required variables exit with a clear error naming each missing variable.next buildis unaffected (the instrumentation hook runs at server startup in Next 14.2, not during build), so CI builds can still complete and the failure surfaces at runtime boot.NEXT_PUBLIC_API_URLcode fallback changed fromhttp://localhost:5000/api(and two strayhttp://localhost:3000) tohttp://localhost:3001, matching.env.example.NODE_ENVis framework-managed and always present; it is allowlisted in the test rather than declared in the schema, per the issue's requirement that the test tolerate build-time variables.components/n18n/is unused scaffolding (excluded fromtsconfig); itsAPP_NAME/DB_*reads are declared in the schema (optional) to keep the contract closed, and.env.exampledocuments them under a "Legacy (unused)" section.