gitoriaLog in with ident

tracker

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commit7d4b293d7d4b293dtracker#40: "Franchises" in the main menu (desktop header after People, phone sidebar) → /franchises, marked on franchise and timeline pages; gates 365/0, 32/0, 53/0, 24/0, check-theme 0mre7d4b293d/plugins/fetch/pending_fetch.hl

3.6 KB

  1. \* A request in flight. Handed back by fetch()/fetchStart() — never `new`ed.
  2. The request is already on the wire when you hold one: it has a thread of
  3. its own, holding one costs a socket, not a fiber. With stream = true the
  4. events below arrive AT this object. *\
  5. \* A decoded piece of the body, token by token (stream mode). *\
  6. emit chunk(
  7. String chunk \\ the decoded bytes as they arrived
  8. )
  9. \* The stream's final frame — body is EMPTY, the chunks were the body. *\
  10. emit done(
  11. Number status \\ the HTTP status
  12. Boolean ok \\ true for 2xx
  13. Hybrid headers \\ response headers
  14. String error \\ '' or the failure text
  15. Boolean aborted \\ true when abort() cut it
  16. )
  17. Hybrid #handle
  18. Number #srcId = 0
  19. // Has the answer arrived? Never waits — a poll for code that wants to do
  20. // something else in the meantime.
  21. done() {
  22. return #handle.done()
  23. }
  24. // THE WAIT. Blocks the calling fiber until the answer is in, then hands back a
  25. // FetchResponse. A failed fetch (refused connection, timeout, TLS verification,
  26. // too many redirects) surfaces here as a located error routed to `on Error`, so
  27. // a caller that wants to survive one writes an `on Error(e)` handler.
  28. response() {
  29. let r = #handle.result()
  30. // An `on Error` handler that ABSORBED the failure leaves the call yielding
  31. // null (the plugin-error contract every `hl:` module follows). Hydrating null
  32. // would be a second, misleading error, so a caught failure answers null —
  33. // exactly what `hl:fs`'s file() does.
  34. if (r == null) {
  35. return null
  36. }
  37. return r > new FetchResponse()
  38. }
  39. // STREAM MODE (the LLM-token flow). `fetch(url, { stream = true })` calls this
  40. // before handing the instance out: the ticket's own event source is registered
  41. // AT this instance and the fan-out below re-emits every frame at itself, so app
  42. // code holds nothing but the object and its events — `on p.chunk`, `on p.done`.
  43. arm() {
  44. #srcId = __native("eventloop.register", #handle.events(), "__fetchEvent")
  45. }
  46. // THE FAN-OUT: a native frame in, a DECLARED event out. The frames are Hybrids
  47. // ({ tag, url, chunk } or { tag, url, status, ok, body, error, headers, aborted });
  48. // `chunk` and `done` above declare TYPED parameters, so each field is handed over
  49. // BY NAME — `emit chunk(ev)` would bind a Hybrid to `String chunk` and fail every
  50. // frame (mission 257: it did).
  51. //
  52. // NAMED, NOT POSITIONAL, and that distinction cost the creator a night (2026-08-30).
  53. // 257 fixed the Hybrid-into-String bind by emitting POSITIONALLY — `emit
  54. // chunk(ev.chunk)` — which type-checks and is still wrong: every handler in this
  55. // language receives a PAYLOAD OBJECT and destructures it. His own working code is
  56. // `on stream.chunk({ chunk })`, exactly as `on server.line({ line, stream })` reads
  57. // for hl:proc and as his `emit ready(port = …)` writes it. Positional emission
  58. // hands `{ chunk }` a String, and the app dies with "Cannot destructure non-object
  59. // value" — a DIFFERENT error from the one 257 fixed, on the same line of his app.
  60. // `name = value` is the form that satisfies the typed declaration AND keeps
  61. // destructuring working. Do not "simplify" it back.
  62. on __fetchEvent(ev) {
  63. if (ev.chunk != null) {
  64. emit chunk(chunk = ev.chunk)
  65. return null
  66. }
  67. // the final frame — retire the source so the loop can end by itself
  68. __native("eventloop.unregister", #srcId)
  69. emit done(status = ev.status, ok = ev.ok, headers = ev.headers, error = ev.error, aborted = ev.aborted)
  70. return null
  71. }
  72. \* THE STOP BUTTON: abort this fetch — the socket is shut down, the peer
  73. sees the disconnect and stops generating, and `done` arrives with
  74. aborted = true. (fetchAbort(tag) cuts a whole tag at once.) *\
  75. abort() {
  76. return #handle.abort()
  77. }

Branches

Latest commits

  • 7d4b293dtracker#40: "Franchises" in the main menu (desktop header after People, phone sidebar) → /franchises, marked on franchise and timeline pages; gates 365/0, 32/0, 53/0, 24/0, check-theme 0mre
  • 93dfb0batracker#40 (mission 034): the curated franchises — data/franchises.json (17 franchises, 31 timelines, 285 TMDB titles, movies + series, in-universe/release order, 12 TMDB collections attached); lib/franchiseseed.hl + jobs.hl franchiseSeedTick (last start job, imports missing titles via details.hl importWithCredits = the search's Add, one per step paced, then one build; franchiseseed.db: editor changes win, the creator's same-name franchise adopted / timeline left alone, 404 remembered, resumable, idempotent); timeline heads 'N titles · in-universe order' (orderKind) and wrap on a phone; series pages show the widget; new gate tests/franchiseseed.mjs (5th in deploy.sh), the others run with TRACKER_FRANCHISE_SEED=0; gates 365/0, 32/0, 52/0, 24/0, check-theme 0; real copy 196 imported, 0 failed, 7 min, restart unchanged=31mre
  • 96ba683adeploy.sh: a gate without a 'passed,' line (check-theme) no longer ends the scriptmre
  • eb3b9205tracker: report 031mre
  • 9b5d2e89tracker mission 031: README (What it does, Test: four gates + the #32 checks, Files: theme/, new pages), STATUS (real copy, A/B load, how to repeat, open points), LOGmre
  • 39950e4ctracker#32 (mission 031): the WorldAPI theme (theme/ vendored verbatim from layouts.worldapi.org 85b5654; styles.hl inherits it: accent green-dark, type colours 1-6; own base/header rules, row lines, genre-pill and inverted-button frames removed, the season foldable keeps its line; check-theme 21 -> 0, 4th deploy gate; main actions class primary) and the #32 header (theme AppHeader/MainMenu/UserMenu/Sidebar/ContentFirst: desktop brand, search, Series|Shows|Movies|Genres|People, user icon with Unwatched..Settings, Logout; signed out the ident selector, phone the iD icon dropdown; phone menu in the sidebar overlay; marked entry by :has); /find -> /search/<q>, /genres, /people(/<letter>), /settings; main { ContentFirst { slot } } works around the hl:web one-line slot bug; gates 365/0, 32/0, 52/0, check-theme 0mre
  • a386dc92tracker: reports 029 + 030mre
  • 71e0fd7dtracker missions 029 + 030: README (What it does, Files, gate count), STATUS (real-copy numbers, how to repeat, open points), LOGmre
  • d36ea6eatracker#34 + #35 (mission 030): Follow directly under the poster, as wide as the poster (show.hl, styles.hl); the status pill next to a series' title — TVmaze's status (new tvmazeStatus, stored by the sync's TVmaze merge) else TMDB's, TVmaze Ended + TMDB Canceled = Canceled, inverted (filled, dark text, no border), green running / yellow pending / red canceled / muted ended (shows.hl statusOf); the daily delta asks TVmaze's status of an unfollowed series TVmaze's change list names (dailysync.hl syncRunStep, sync.hl syncTvmazeStatus); the status backfill after the details repair (backfill.hl, jobs.hl statusTick; resumable, 550 ms per TVmaze request); gates 354/0, 32/0, 52/0mre
  • 7d7d4487tracker#33 (mission 029): reduced titles — every title TMDB's details never went through this app (no detailsAt, no tmdbSync) is incomplete (shows.hl isIncomplete; the old tracker's migrated rows passed #26's test: 5,697 non-adult on the live copy, 691 series without seasons); the repair job does the visibly reduced first (shows.hl missingParts), the page completes one on open; a title TMDB has no poster for (The Remaining) shows the placeholder; tools/count-incomplete.hl; gate fixtures stand for synced titles (tmdbSync), tests/seed-reduced.hl + #33 checks; gates 347/0, 32/0, 52/0mre
  • 661c2592tracker: report 028mre
  • 27c916fatracker mission 028: README ("Code order", the new file map), STATUS (counts before/after, tests, how to repeat, open), LOGmre
  • d924f398tracker mission 028: comments name the new files (sync.hl, dailysync.hl, backfill.hl, credits.hl, jobs.hl, images.hl …); tools/ref-params.py + tools/lambda-audit.py also scan lib/ (they globbed the root only), lambda-audit counts a plain `x = p` alias like `let x = p`mre
  • 2e89b968tracker mission 028 (code order) 5/5 let: `let` only where a variable is reassigned — 667 never-reassigned lets became plain declarations (project.hl, lib/, components/, tools/, tests/); kept: 264 in loop bodies (a plain declaration there is 'Cannot reassign' on the 2nd pass), 234 reassigned, 27 whose name is also a member/outer/free name (a plain write would rebind it); tools/let-audit.py decides and fixes (README 'Code order'); tests/realdata-m028.{sh,mjs} = the page-output diff on a real copy; gates 342/0, 32/0, 52/0, real-copy pages identicalmre
  • 54796ff2tracker mission 028 (code order) 4/5 thin faces + last copies: the show page's check/follow faces call lib/watches.hl toggleWatched / toggleSeasonWatched (seasonAllWatched moved there) and lib/follows.hl toggleFollowed; both logins (header selector face, /login/callback) share lib/users.hl userOfCode; todayStr/listOf copies in components and the export readers copied into tools/migrate.hl + tools/old-short-ids.hl now once (lib/util.hl, lib/export.hl); gates 342/0, 32/0, 52/0; old-short-ids output byte-identical, migrate output identicalmre
  • 06b078e3tracker mission 028 (code order) 3/5 project.hl is the map: config, routes, wiring and a feature → file index (914 → 258 lines); the background jobs (daily sync run, backfills, details repair, credits job, merge, short ids, collection seed) moved unchanged into lib/jobs.hl (a class: their state is reassigned every step, a static cannot be; one instance made after the server), the login callback into lib/users.hl, poster/photo serving into lib/images.hl, the /shows/<slug> rule into lib/shows.hl showsMovedPath; route handlers are thin wrappers; gates 342/0, 32/0, 52/0, real-copy pages identicalmre
  • 94716fd2tracker mission 028 (code order) 2/5 util + topics: lib/util.hl holds envOr, storageDir, postersDir, profilesDir, newId, hexDigits, todayStr, dateOr, textOr, hasId, listOr, firstOf, sortDesc once (were copied into up to 5 files); tmdbsync.hl split into tmdb.hl (TMDB/TVmaze requests), sync.hl (one title's sync), sync-helpers.hl, backfill.hl; details.hl split into details.hl, credits.hl, credits-helpers.hl (isIncomplete to shows.hl); search-helpers.hl (words, query, ranking, slugs); collections.hl (the TMDB collection seed, out of franchises.hl); deltasync.hl renamed dailysync.hl; no behaviour change: gates 342/0, 32/0, 52/0, real-copy pages identicalmre
  • 186079b0tracker mission 028 (code order) 1/5 move: every root .hl except project.hl into lib/ (styles.hl into components/), import paths only; gates 342/0, 32/0, 52/0; real-copy pages identicalmre
  • 4f47f181tracker: report 027mre
  • dc1d4be4tracker mission 027: Hybriel master 06617221 vendored (plugin allocator fixes 3a781359 + 413f60e4); real copy RSS through first-start jobs + 400 loads flat ~2.55 GB (190aa11d 2.3 -> 5.6 GB), page times <= 1.1x; gates 342/0, 32/0, 52/0mre