gitoriaLog in with ident

ident

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commit7423581574235815ident mission 009 (4/4): docs (README files map + same-output test, STATUS, LOG), report, tests/letcount.py + realdata-baseline/comparemre74235815/plugins/smtp/Mailer.hl

3.4 KB

  1. \* hl:smtp — the SMTP submission client.
  2. import { Mailer } from 'hl:smtp'
  3. m = new Mailer('mail.example.com', 587, {
  4. user = '[email protected]'
  5. pass = '…'
  6. from = '[email protected]'
  7. })
  8. m.send({ to = '[email protected]' subject = 'Hello' text = 'Hi' })
  9. Non-blocking: send() answers a job id, the OUTCOME arrives later — drain
  10. it with `for (r of m.results())`, or deliver() it as the declared events
  11. below while the program keeps running. *\
  12. /* The first three declared properties ARE the positional signature (SPEC.md
  13. "The file root is the constructor"), so `new Mailer(host, port, options)`
  14. reads as written. */
  15. \* One send was ACCEPTED by the server. r = { id, ok, code, stage, message,
  16. secure, to }. *\
  17. emit sent(
  18. Hybrid r \\ the result record for one message
  19. )
  20. \* One send FAILED — refused, TLS problem, timeout. Same record shape;
  21. `stage` names where it died. *\
  22. emit failed(
  23. Hybrid r \\ the result record for one message
  24. )
  25. String host = "127.0.0.1" \\ the submission server
  26. Number port = 587 \\ 587 STARTTLS, 465 implicit TLS
  27. Hybrid options = null \\ { user, pass, from, … } — credentials and defaults
  28. \* The plugin's handle for this mailer — set by the run body; a send() before
  29. construction finished is impossible, the root IS the constructor. *\
  30. Number handle = 0
  31. \* SEND — non-blocking. Validated and rendered HERE, on this thread, so a
  32. refusal (a line break in to/subject, a missing recipient, an address
  33. without an @) is a located error at YOUR line, through `on Error` like any
  34. plugin failure. Answers the job id; the outcome arrives on the result
  35. source. msg: { to (String or list), subject, text, html, from }. *\
  36. send(
  37. Hybrid msg \\ the message — to/subject/text/html/from
  38. ) {
  39. return __native("smtp.send", handle, msg)
  40. }
  41. \* THE RESULT SOURCE, drained: `for (r of m.results())` yields one result per
  42. accepted send and ENDS when every send is answered — which is what lets a
  43. script that only sends mail terminate. *\
  44. results() {
  45. return __native("smtp.results", handle)
  46. }
  47. \* THE RESULT SOURCE, delivered: results arrive as the declared sent/failed
  48. events while the program goes on — a server keeps serving with mail in
  49. flight. Use this OR results(), never both: they take from one queue. *\
  50. deliver() {
  51. __native("smtp.mark_registered", handle)
  52. __native("eventloop.register", __native("smtp.results", handle), "mail")
  53. }
  54. \* How many sends have not been answered yet. *\
  55. pending() {
  56. return __native("smtp.pending", handle)
  57. }
  58. \* Stop the sender thread. A job already in conversation finishes. *\
  59. close() {
  60. __native("smtp.close", handle)
  61. handle = 0
  62. }
  63. // The split every caller of deliver() wants: one event per outcome. `mail`
  64. // is delivered at THIS instance by the loop, and the re-emit dispatches at
  65. // it too, so an instance-scoped `on m.sent(r)` in the constructing program
  66. // hears it.
  67. on mail(r) {
  68. if (r.ok == true) {
  69. emit sent(r)
  70. } else {
  71. emit failed(r)
  72. }
  73. }
  74. // Open the mailer and start its sender thread. A METHOD, called from the run
  75. // body — never a root assignment: a bare `handle = …` at the root would be a
  76. // property INITIALIZER, and initializers run BEFORE positional args bind
  77. // (construction order 1 vs 4) — it would open a mailer for the DEFAULT host.
  78. // Same shape as hl:http1's listen().
  79. open() {
  80. handle = __native("smtp.open", host, port, options)
  81. }
  82. // Run body — the constructor's last act.
  83. open()

Branches

Latest commits

  • 74235815ident mission 009 (4/4): docs (README files map + same-output test, STATUS, LOG), report, tests/letcount.py + realdata-baseline/comparemre
  • fe183516ident mission 009 (3/4): let only where reassigned — 293 never-reassigned lets are plain declarations; kept: reassigned, loop bodies, names of a file member, a name declared twice in one function; same outputmre
  • d2e7f91bident mission 009 (2/4): one lib file per topic (login, accounts, identities, apps, invites, selector, notify + helpers, util), function routes as thin wrappers in lib/api.hl, project.hl = the map; same outputmre
  • 91017164ident mission 009 (1/4): file moves — the root .hl files into lib/ (api.hl → lib/api-helpers.hl), styles.hl → components/styles.hl; imports adjusted, no other changemre
  • f8bdcbc2ident: Hybriel master 06617221 (plugin allocators 3a781359 + 413f60e4, mpackdb 2cb7ae5e, http1 773de63e); all gates greenmre
  • ff78726cident: Hybriel master 190aa11d (fc838894 GC correctness, #127, #126 closure scopes); gates all greenmre
  • a3a7d21aident: Hybriel master 8efba065 (#126 GC by bytes, #48 lambda params copy); session-writing lambdas take &sessionmre
  • 98226b41antcolony#40: mission references point to the moved missionsmre
  • ff805b9aantcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
  • 51a7bcdfident: Hybriel master 73267707 (#122); /code uses the new page() signature; pending address passed as parameter; once-checksmre
  • 836f644fident#24: installable app (manifest, service worker, data-free offline /start), own iconmre
  • 8bebbbf2deploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
  • cc063ea2deploy.sh: never send .git or .gitignore to Byrodinmre
  • 81b15b7bState of 2026-09-27, before the move to gitoriamre