Specification: Utility Extraction
Implementation specification for Part 2 of Component Preparation. Requirements: requirements.md. Verification: verification.md. Detailed code changes: implementation-changes.md.
Pre-conditions
Section titled “Pre-conditions”- Part 1 (Legacy Cleanup) is complete and committed
- All checks pass on the current branch (baseline from Part 1)
Implementation Order
Section titled “Implementation Order”Groups are independent but the recommended order minimizes risk by starting with the highest-impact, lowest-risk extractions:
- Group E — Error handling (1 function, simplest extraction)
- Group G — Environment constants (foundation for debug logging)
- Group F — Formatters (pure functions, no dependencies)
- Group D — Safe serialization (pure functions, browser API only)
- Group A — API route infrastructure (highest impact, touches 43 files)
- Group B — Lookup parsing (refactor within single file)
- Group C — JWT consolidation (touches auth-critical code, highest care)
Each group is one commit. All checks must pass after each commit before proceeding to the next group.
Group E — Error Handling
Section titled “Group E — Error Handling”Files: src/lib/errors.ts (new), src/lib/errors.test.ts (new)
- Create
src/lib/errors.tswithextractErrorMessage(error: unknown): string - Create
src/lib/errors.test.tswith cases per REQ-UE-E01 - Replace inline error narrowing patterns in API routes and auth handlers
with imports from
errors.ts - Run all checks
See implementation-changes.md § Group E for file-level details.
Group G — Environment Constants
Section titled “Group G — Environment Constants”Files: src/lib/env-spa.ts (new), src/lib/env-spa.test.ts (new),
src/lib/utils.ts (modify)
- Create
src/lib/env-spa.tsexportingIS_PRODUCTION,IS_STAGE,IS_MOCK_MODE - Create
src/lib/env-spa.test.tsper REQ-UE-G03 - Update
debugLog/debugError/debugWarninsrc/lib/utils.tsto importIS_STAGEfromenv-spa.ts - Replace scattered
process.env.NEXT_PUBLIC_*comparisons in consumer files with the exported constants - Run all checks
See implementation-changes.md § Group G for file-level details.
Group F — Formatters
Section titled “Group F — Formatters”1
Files: src/lib/formatters.ts (new), src/lib/formatters.test.ts (new),
src/components/table/columnPresets.tsx (modify),
src/lib/ardaClient.ts (modify)
- Create
src/lib/formatters.tswithformatDate,formatDateTime,formatCurrency,formatQuantity - Create
src/lib/formatters.test.tsper REQ-UE-F05 - Update
columnPresets.tsxto import formatters fromformatters.ts(remove inline definitions) - Update
ardaClient.tsformatQuantityto import fromformatters.ts - Run all checks
See implementation-changes.md § Group F for file-level details.
Group D — Safe Serialization
Section titled “Group D — Safe Serialization”Files: src/lib/storage.ts (new), src/lib/storage.test.ts (new)
- Create
src/lib/storage.tswithsafeJsonParse,getStorageItem,setStorageItem - Create
src/lib/storage.test.tsper REQ-UE-D04 - Replace inline
localStorageJSON patterns in consumer files with imports fromstorage.ts - Run all checks
See implementation-changes.md § Group D for file-level details.
Group A — API Route Infrastructure
Section titled “Group A — API Route Infrastructure”Files: src/lib/api-route-utils.ts (new),
src/lib/api-route-utils.test.ts (new), 43 route files (modify)
- Create
src/lib/api-route-utils.tswithgenerateRequestId,parseUpstreamResponse,forwardAsNextResponse - Create
src/lib/api-route-utils.test.tsper REQ-UE-A05 - Update each API route handler to import and use the shared functions, removing inline implementations
- Run all checks
STOP: This group touches 43 files. After updating all routes, run the full test suite and verify no route handler behavior has changed. Spot-check at least 3 routes across different domains (items, kanban, tenant) to confirm correct request IDs, content-type parsing, and status forwarding.
See implementation-changes.md § Group A for file-level details.
Group B — Lookup Response Parsing
Section titled “Group B — Lookup Response Parsing”Files: src/lib/ardaClient.ts (modify)
- Create the
lookupFieldhelper function (exported for testing) - Refactor all 9 lookup functions to delegate to
lookupField - Add test cases for
lookupFieldin the appropriateardaClient.*.test.tsfile - Run all checks
See implementation-changes.md § Group B for file-level details.
Group C — JWT / Token Consolidation
Section titled “Group C — JWT / Token Consolidation”Files: src/lib/jwt.ts (modify if needed),
src/store/thunks/authThunks.ts (modify),
src/lib/tokenRefresh.ts (modify),
src/store/components/AuthInit.tsx (modify),
src/contexts/AuthContext.tsx (modify),
src/lib/ardaClient.ts (modify)
- Ensure
decodeJWTPayloadinjwt.tsis client-safe (noNextRequestdependency in the function itself — the import is already separate) - Replace all inline
atob(token.split('.')[1])patterns withdecodeJWTPayloadimports - Replace all inline
token.split('.').length !== 3checks with a sharedisValidJWTFormatfunction fromjwt.ts - Run all checks
STOP: This group modifies auth-critical code paths. After replacement, verify that all auth-related tests pass. Pay special attention to token refresh flows and the AuthInit bootstrap sequence.
See implementation-changes.md § Group C for file-level details.
Final Steps
Section titled “Final Steps”CHANGELOG Update
Section titled “CHANGELOG Update”After all groups are committed, add a single CHANGELOG.md entry under
[Unreleased]:
### Changed
- Extracted shared API route utilities (`generateRequestId`, `parseUpstreamResponse`, `forwardAsNextResponse`) to `src/lib/api-route-utils.ts`, replacing inline implementations across 43 route handlers- Consolidated 9 lookup functions in `ardaClient.ts` into parameterized `lookupField` helper- Replaced inline JWT decode/validation with shared `jwt.ts` functions- Extracted `localStorage` JSON utilities to `src/lib/storage.ts`- Extracted error message narrowing to `src/lib/errors.ts`- Extracted date/currency/quantity formatters to `src/lib/formatters.ts`- Extracted client-side environment constants to `src/lib/env-spa.ts`Final Verification
Section titled “Final Verification”Run all checks one final time after the CHANGELOG commit:
npm run lintnpx tsc --noEmitnpx jest --no-coverage --watchAll=false --forceExitnpm run buildGroup Commit Gate
Section titled “Group Commit Gate”Each group follows this sequence before moving to the next:
- Implement the extraction and its unit tests
- Run all local checks:
Terminal window npm run lintnpx tsc --noEmitnpx jest --no-coverage --watchAll=false --forceExitnpm run build - All checks must pass — diagnose and fix any failures before proceeding
- Commit the group as a single atomic commit
- Only then begin the next group
Do not batch multiple groups into a single commit. Do not begin the next group with uncommitted changes from the previous one.
Quality Guidelines
Section titled “Quality Guidelines”- Do not modify any file not listed in the group’s scope.
- Do not reformat or restructure surrounding code.
- Preserve all public API signatures — consumers must compile without edits beyond import paths.
Acceptance Checklist
Section titled “Acceptance Checklist”-
src/lib/errors.ts+ tests created; inline patterns replaced -
src/lib/env-spa.ts+ tests created;utils.tsupdated -
src/lib/formatters.ts+ tests created;columnPresets.tsxandardaClient.tsupdated -
src/lib/storage.ts+ tests created; inline patterns replaced -
src/lib/api-route-utils.ts+ tests created; 43 routes updated -
lookupFieldextracted inardaClient.ts; tests added - Inline JWT decode/validation replaced with
jwt.tsimports - CHANGELOG updated
- All checks pass after every group commit
Open Questions and Decisions
Section titled “Open Questions and Decisions”| # | Question | Options | Recommendation | Decision |
|---|---|---|---|---|
| 1 | Should lookupField be exported (testable directly) or kept private (tested through public functions only)? | a) Export for direct testing, b) Keep private, test via public wrappers | Export — enables focused unit tests without complex mock setup for 9 wrappers | pending |
| 2 | Should isValidJWTFormat be a new named export from jwt.ts or inlined in decodeJWTPayload? | a) Separate named export, b) Internal to decodeJWTPayload | Separate export — ardaClient.ts uses format validation independently of decode | pending |
| 3 | Should Group A CHANGELOG entry use Changed or Added category? | a) Changed (refactoring existing behavior), b) Added (new utility module) | Changed — behavior is preserved, only the code organization changes | pending |
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved