phase-2: extract shared utils

This commit is contained in:
Sai Kiran Vella
2026-06-04 11:06:30 -04:00
parent d87f0b8427
commit dc7ee22135
49 changed files with 299 additions and 120 deletions
+24 -18
View File
@@ -32,7 +32,7 @@ npm start -- AAPL MSFT VOO # CLI: specific tickers
npm run finance # CLI: portfolio advice + SimpleFIN → finance-report.html
npm test # run all unit tests (node:test, zero external deps)
npm run test:watch # watch mode — uses verbose spec reporter
npm run format # format all src/bin/tests with Prettier
npm run format # format all server/bin/tests with Prettier
npm run format:check # check formatting without writing (used in CI/pre-commit)
npm run ui:install # install UI dependencies (ui/ subdirectory)
```
@@ -56,7 +56,7 @@ scripts/
prompts/
catalyst-analysis.md ← daily catalyst analysis playbook (LLM prompt + workflow)
src/
server/
config/
ScoringConfig.js ← CREDIT_RATING_SCALE + ScoringRules (single source of truth)
constants.js ← SIGNAL, ASSET_TYPE, SECTOR, SCORE_MODE, REGIME, SIGNAL_ORDER
@@ -199,7 +199,7 @@ CORS is configured for `CLIENT_ORIGIN` env var (default `http://localhost:5173`)
## ScoringConfig Key Values
`src/config/ScoringConfig.js` — single source of truth for all gates, weights, thresholds.
`server/config/ScoringConfig.js` — single source of truth for all gates, weights, thresholds.
**STOCK base gates (Fundamental mode):**
- `maxPERatio: 15` — Graham's actual rule (trailing P/E)
@@ -234,7 +234,7 @@ CORS is configured for `CLIENT_ORIGIN` env var (default `http://localhost:5173`)
## MarketRegime (INFLATED overrides)
`src/market/MarketRegime.js` derives gate overrides from live benchmarks and current rate regime:
`server/market/MarketRegime.js` derives gate overrides from live benchmarks and current rate regime:
| Gate | Formula (NORMAL rates) | Formula (HIGH rates) |
|---|---|---|
@@ -365,10 +365,10 @@ Test output: silent on pass, shows only failures + one summary line (`scripts/su
- Asset `type` (uppercased) is the routing key across DataMapper, asset classes, `SCORERS` map, and ScoringRules.
- Prefer adjusting `ScoringConfig` or `MarketRegime` over hardcoding numbers in scorers.
- BenchmarkProvider caches for 1 hour — restart the server to force a fresh fetch.
- All entry points live in `bin/`. Do not add logic to entry points — they call into `src/`.
- `bin/server.js` starts Fastify; `src/server/` contains all route logic.
- **Never** call `process.exit()` inside `src/` — only `bin/` may do that.
- Class instances don't survive `JSON.stringify`. Call `getDisplayMetrics()` server-side before returning from API routes (see `src/server/routes/screener.js` `serializeAssets()`).
- All entry points live in `bin/`. Do not add logic to entry points — they call into `server/`.
- `bin/server.js` starts Fastify; `server/server/` contains all route logic.
- **Never** call `process.exit()` inside `server/` — only `bin/` may do that.
- Class instances don't survive `JSON.stringify`. Call `getDisplayMetrics()` server-side before returning from API routes (see `server/server/routes/screener.js` `serializeAssets()`).
---
@@ -381,7 +381,7 @@ All items completed. Additional features delivered alongside cleanup:
**Cleanup done:**
- Deleted root-level `finance.js`, `import-portfolio.js`, `markdown.md`
- Deleted `src/server/routes/analyze.js` (orphaned route file)
- Deleted `server/server/routes/analyze.js` (orphaned route file)
- Removed dead `analysis` state, `analysisOpen` state, and "🤖 AI Market Analysis" panel from `+page.svelte`
- Fixed `.gitignore``portfolio.json`, `market-calls.json`, `.env` are now excluded from git
@@ -398,13 +398,19 @@ All items completed. Additional features delivered alongside cleanup:
**Pending (deferred to later):**
- LLM Analysis button on portfolio page (analyse holdings against current news)
### Phase 2 — Extract Shared Utilities
- Create `ui/src/lib/utils.ts` with all pure functions currently duplicated across pages: `sigOrd`, `sorted`, `verdictShort`, `vClass`, `fmtPE`, `fmt`, `fmtShort`, `glClass`
- Create `src/server/utils/logger.js` with shared `noopLogger` constant (currently copy-pasted in `screener.js` and `app.js`)
### Phase 2 — Extract Shared Utilities ✅ COMPLETE
### Phase 3 — Rename `src/` → `server/`
- Rename the directory and update all import paths in `bin/`, internal routes, and `CLAUDE.md`
- Makes the API layer unambiguous — `src/` conventionally implies "all project source"
**Done:**
- Created `ui/src/lib/utils.ts` — typed shared pure functions: `sigOrd`, `sorted`, `verdictShort`, `vClass`, `fmtPE`, `fmt`, `fmtShort`, `glClass`, `advClass`. Exports `Signal` type.
- Created `server/server/utils/logger.js` — shared `noopLogger` constant, imported by `screener.js`, `app.js`, `finance.js`, and `calls.js`
- Added TypeScript support to `ui/``tsconfig.json` extending SvelteKit's generated config, `typescript` and `svelte-check` added as dev dependencies
- All three pages (`+page.svelte`, `safe-buys/+page.svelte`, `portfolio/+page.svelte`) now import from `$lib/utils.js` instead of duplicating logic
### Phase 3 — Rename `src/` → `server/` ✅ COMPLETE
**Done:**
- Renamed `src/` to `server/``src/server/` is now `server/server/`
- Updated all import paths in `bin/`, `tests/`, and `CLAUDE.md`
### Phase 4 — SCSS Migration
Replace per-component `<style>` blocks with a shared token system in `ui/src/styles/`:
@@ -455,10 +461,10 @@ SvelteKit supports TypeScript natively — components just need `<script lang="t
## Adding a New Asset Type
1. Create a subclass of `Asset` in `src/screener/assets/` with a flat `metrics` object and `getDisplayMetrics()`.
1. Create a subclass of `Asset` in `server/screener/assets/` with a flat `metrics` object and `getDisplayMetrics()`.
2. Add a per-type entry (`gates` / `weights` / `thresholds`) to `ScoringRules` in `ScoringConfig.js`.
3. Add inflated overrides in `MarketRegime.getInflatedOverrides()`.
4. Create a Scorer in `src/screener/scorers/` exposing `score(metrics, rules, marketContext)`.
4. Create a Scorer in `server/screener/scorers/` exposing `score(metrics, rules, marketContext)`.
5. Add a mapper in `DataMapper.js`.
6. Wire into `ScreenerEngine`: add `case` in `_buildAsset`, entry in `SCORERS` map.
7. Add the new type to `serializeAssets()` handling in `src/server/routes/screener.js`.
7. Add the new type to `serializeAssets()` handling in `server/server/routes/screener.js`.