gitoriaLog in with ident

antcolony

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Main branchmain3a4d0324antcolony#37: a too-long report gets up to 3 fix tries, finished work is never thrown away for lengthmremain/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

  • 3a4d0324antcolony#37: a too-long report gets up to 3 fix tries, finished work is never thrown away for lengthmre
  • a6af7883tracker: worker box sees calendar.worldapi.org (login to copy)mre
  • c613d26btemplates: bridges to external components (login.js for ident's selector) are allowed (creator 2026-09-27)mre
  • 9062978ctracker: worker box sees /media/STORAGE/projects/old-tracker read-only (tracker#2 source data)mre
  • 7f9660eeState of 2026-09-27, before the move to gitoriamre