preludes / email.lisp
Email automation rules. Who gets an email, when, and how fast. Closed lanes, one decision per contact, and typed delivery recovery.
The TypeScript engine checks rules and generates Effect TypeScript. Scheduling, rate enforcement, and dry runs are not implemented.
From the source
Purpose: Typed email decisions, predicates, and automation-rule declarations. Requires: compiler.lisp; the email linker supplies each rule's EmailLane vocabulary. Used by: The optional TypeScript email linker; scheduling and rate enforcement belong to hosts.
Try it
Forms 1
Keywords this prelude adds. Each is a form descriptor: its slots, their types, and the IR it produces.
(rule name lanes clause ...)email.lisp:48
Declares an email rule with lane configuration and decision clauses. The email linker checks predicates and decisions and lowers the rule to Effect code.
| Slot | Type |
|---|---|
| name | String |
| lanes | Syntax |
| clause | (List Syntax) |
Produces EmailRuleIR · has a TypeScript :emit hook
Example
(rule "welcome" (lanes (lane "default" :per-hour 100))
(when (tag? "customer") (skip "Already a customer"))
(send "default"))Functions 6
Ordinary typed definitions that programs and elaboration hooks can call.
sendemail.lisp:19
Returns a Send decision; delivery happens in the host's checked operation.
(: send (-> EmailLane EmailDecision))
skipemail.lisp:27
Returns a Skip decision retaining the author's reason.
(: skip (-> String EmailDecision))
tag?email.lisp:31
Predicates take the contact first; rules omit it.
(: tag? (-> EmailContact String Bool))
domain?email.lisp:35
Tests the recipient's domain for exact string equality.
(: domain? (-> EmailContact String Bool))
due?email.lisp:40
Tests whether a known journey is due within the supplied hours; an absent journey returns false.
(: due? (-> EmailContact String Int Bool))
Types 3
Payload contracts and IR shapes, shown as authored.
EmailContactemail.lisp:10
Host-supplied recipient facts; due-in maps journey names to hours until their next scheduled email.
(type EmailContact
{:email String :domain String :tags (List String) :due-in (Map String Int)})EmailDecisionemail.lisp:14
Every rule answers each contact with exactly one decision.
(type EmailDecision
(Tagged (Send {:lane EmailLane}) (Defer {:hours Int}) (Skip {:reason String})))EmailRuleIRemail.lisp:45
(type EmailRuleIR
{:kind "EmailRule" :name String :lanes RuntimeExpr :clauses (List RuntimeExpr)})