tracker
All repositories: gitoria
2.6 KB
// tests/browser/ports.mjs — the two port questions the CDP driver has to ask,// and the reasons they are asked this way. They lived in the hl:web browser// tier's `fixtures.mjs` until that tier moved to `archive/hlweb-v1/`// (2026-09-10); the driver survives it, so its dependencies do too.//// Every process a caller starts is its own to kill BY PID — never `pkill -f`,// which on this machine matches sibling sessions' runners.//// `portOpen` (a plain connect) is NOT here: the driver keeps its own, because// "something answers" is a different question from "this port is free", and// only the second one needs the reasons written down.import { createServer as netServer } from 'node:net';import { execFileSync } from 'node:child_process';/*** A BIND IS THE QUESTION, SO A BIND IS THE TEST (mission 285). A listener on a* LAN address is invisible to a loopback connect and still takes the port from* a server that binds 0.0.0.0 — which is how a browser-tier red once looked* like a hung server: the app's own log said `bind() failed on port N` while* every "is it open?" probe said no.** Nothing is left behind: a listening socket with no accepted connections* leaves no TIME_WAIT when it closes.*/export function portBindable(port) {return bindProbe(port).then((r) => r.ok);}/*** The same probe with the kernel's own word for WHY. `EADDRINUSE` is a* collision and is what this file is about; anything else (EACCES, EMFILE …)* is a different problem and must not be reported as one, because an error* that names the wrong cause costs the next reader a whole night.*/function bindProbe(port) {return new Promise((res) => {const s = netServer();s.once('error', (e) => res({ ok: false, code: (e && e.code) || String(e) }));s.listen(port, '0.0.0.0', () => s.close(() => res({ ok: true, code: null })));});}/*** Every socket on `port`, in any state, with its owner — `ss -tanp`. LISTEN is* the state that actually blocks a bind (measured: TIME_WAIT and ESTABLISHED do* not, on loopback or on the LAN address), so a report showing only listeners* and finding none says "nothing", which is the least useful thing a diagnostic* can say. Show the whole port.*/export function whoHolds(port) {try {const out = execFileSync('ss', ['-tanp'], { encoding: 'utf-8' });const hits = out.split('\n').filter((l) => new RegExp(`:${port}(\\s|$)`).test(l));return hits.length? hits.map((l) => l.trim().replace(/\s+/g, ' ')).join('\n '): '(no socket on that port at all by the time `ss` ran — the holder let go between the failed bind and this line)';} catch { return '(`ss` is not runnable here)'; }}
Branches
- mainmain branch
Latest commits
- dcc5eecaMerge branch 't14-search'mre
- 03edc783Merge branch 't15-tvmaze'mre
- 71b46345tracker#15: numbering check by date or title, placeholder titles in other languages, docs + real-data proofmre
- 6bb2daf1tracker#13: homepage (tiles, intro, latest movies/shows), /shows, /movies/page/N, /my/movies; lists cached in memorymre
- b8bd1157tracker#14: README + STATUS (search, real-data numbers, gate, merge notes)mre
- 65c694a8tracker#14: search — header magnifier, /search/<text> (in-memory word-prefix index over titles + people), Fetch from web (TMDB search/multi, ours left out), Add = import via syncShow; gate +25 checks, real-data scriptmre
- 34f2c15btracker#15: TVmaze merge in the sync (gaps only: new episodes/seasons, empty titles/air dates; numbering check), fake TVmaze episodes + gatemre
- cbdc4ea7tracker#12: link icons TMDB/IMDb/TVDB/TVmaze; sync fills missing ids (TVmaze lookup); movies fetched via /movie/mre
- b105bcd8tracker#11: Hybriel master ff51cf46 (checks no longer vanish), mobile-first styles, carets, follow button, sign-in modal, inverted check, orange castmre
- 31b758aatracker#10: installable app (manifest, service worker, offline shell), own icon + faviconmre
- 2fa9d997tracker#9: TMDB sync (followed shows: seasons, episodes, posters), tools/sync-tmdb.hl + daily run 04:00 UTC, fake TMDB in gatemre
- 49e1f61edeploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
- 54070a4etracker#8: /my/unwatched + /my/schedule (301 from old), S01E01, title (year), 1 episode, watched-set lookup (unwatched 15s -> 1s)mre
- 3251488atracker#7: /my/shows (followed shows, newest follow first, poster, title, last watched SxxEyy); gate can take screenshots (TRACKER_GATE_SHOTS)mre
- 44b7d9f9tracker#6: /schedule — upcoming episodes of followed shows, soonest firstmre
- 91c9fc8ctracker#5: /unwatched — unwatched released episodes of followed shows, newest firstmre
- a97c0295tracker#4: show page /shows/:slug (header, seasons, episodes, watch checks) + tools/relink-episode-seasons.hlmre
- 05f407c5tracker: no border on any button except inverted ones (Log out, ident status and identities too); header brand weight 100mre
- 17375427tracker#2: tools/migrate.hl + tools/verify.hl — old MongoDB data into mpackdb with new idsmre
- 31aac936tracker#3: no border on the header and on filled buttons; inverted buttons keep theirsmre