Pull the data layer out of the watchlist
The first real refactor. Your watchlist fetches quotes; your screener fetches screens. Both go through the proxy, both handle failure, both validate what comes back. Right now that logic lives inside the watchlist, and you are about to need it twice.
Have the checklist open before you start
You are about to accept generated code that moves working software around. Course 1 gave you four questions for a diff; this unit needs them running the whole time, so here they are as a card to keep open — worst consequence first:
- Leaks. Does this touch the key, the proxy, or anything a caller can influence?
- Cost. How many API calls does this path make, and did that number change?
- Lies. Is every number still in the units and currency it claims?
- Failure. What does this do with no data, bad data, or a dead upstream?
Unit 3 turns each of those into a lesson and shows what they catch. You do not have to wait for that to use them — a checklist you run badly beats one you read later.
The rule that decides what moves
Not "move the shared code". That instruction is too vague to act on and produces a utils.ts nobody can navigate.
The useful rule: move what would be identical in a product that has nothing to do with watchlists. Fetching through the proxy, checking res.ok, applying a timeout, narrowing an unknown response to a validated shape — none of that knows what a watchlist is. It moves.
Deciding which columns to show, what "your list" means, how a threshold rule reads — all of that is the watchlist being itself. It stays.
src/lib/
market.ts ← moved out: proxy fetch, ok check, timeout, shape guard
quotes.ts ← watchlist: turns market data into Quote rows
screens.ts ← new: turns market data into screen results
Three files where there was one, and the middle one got smaller. That is what a good extraction looks like: the thing you pulled out is boring, and both callers shrank.
Do it as a refactor, not a rewrite
There is a specific way to ask for this that gets a good result, and a specific way that gets a mess.
Extract the proxy fetch, the
res.okcheck, the timeout and the shape validation fromsrc/lib/quotes.tsinto a newsrc/lib/market.ts. Do not change behaviour.quotes.tskeeps its exported signature exactly as it is, and calls into the new module. Show me the diff before writing.
Three constraints in there, and each one prevents a specific failure. Do not change behaviour stops opportunistic "improvements" riding along in a diff you are reading for structure. Keeps its exported signature means the watchlist page needs no edit, so if the page breaks you know the extraction is wrong. Show me the diff is the loop.
Compare with "refactor the data layer to be reusable", which invites a redesign you did not ask for and cannot review.
Verify by reverting your attention
The whole point of a behaviour-preserving refactor is that you can check it without reading it: load the watchlist and confirm nothing changed. Same rows, same numbers, same errors when you break the key.
If anything looks different, the refactor is wrong, and it is wrong now while the diff is one commit old rather than in three weeks when it is buried. This is the cheapest test you will ever run and the one people skip because the code "obviously" still works.
Commit before you build on it
git add src/lib/market.ts src/lib/quotes.ts
git commit -m "Extract the transport out of quotes - the screener needs the same thing"
One commit that changes structure and nothing else. When the screener turns out to need a change in market.ts next week, you want that diff to sit on top of a clean extraction, not tangled inside it. This is what makes the history useful rather than archaeological.
Try it now
Do the extraction with the prompt above, then run the verification honestly: open the watchlist and compare it against a screenshot you take first. Most people skip the screenshot and then cannot say whether the third column always looked like that. Take the screenshot.