@pattern-js/mod-email 0.3.0-main.4a77893

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/app.d.ts +13 -0
  4. package/dist/app.d.ts.map +1 -0
  5. package/dist/app.js +172 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/config.d.ts +52 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +85 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/delivery.d.ts +26 -0
  12. package/dist/delivery.d.ts.map +1 -0
  13. package/dist/delivery.js +83 -0
  14. package/dist/delivery.js.map +1 -0
  15. package/dist/index.d.ts +16 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +15 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/markdown.d.ts +17 -0
  20. package/dist/markdown.d.ts.map +1 -0
  21. package/dist/markdown.js +173 -0
  22. package/dist/markdown.js.map +1 -0
  23. package/dist/mod.d.ts +20 -0
  24. package/dist/mod.d.ts.map +1 -0
  25. package/dist/mod.js +57 -0
  26. package/dist/mod.js.map +1 -0
  27. package/dist/ops/account.d.ts +4 -0
  28. package/dist/ops/account.d.ts.map +1 -0
  29. package/dist/ops/account.js +33 -0
  30. package/dist/ops/account.js.map +1 -0
  31. package/dist/ops/send.d.ts +4 -0
  32. package/dist/ops/send.d.ts.map +1 -0
  33. package/dist/ops/send.js +57 -0
  34. package/dist/ops/send.js.map +1 -0
  35. package/dist/ops/shared.d.ts +8 -0
  36. package/dist/ops/shared.d.ts.map +1 -0
  37. package/dist/ops/shared.js +18 -0
  38. package/dist/ops/shared.js.map +1 -0
  39. package/dist/service.d.ts +51 -0
  40. package/dist/service.d.ts.map +1 -0
  41. package/dist/service.js +156 -0
  42. package/dist/service.js.map +1 -0
  43. package/dist/settings.d.ts +18 -0
  44. package/dist/settings.d.ts.map +1 -0
  45. package/dist/settings.js +122 -0
  46. package/dist/settings.js.map +1 -0
  47. package/dist/types.d.ts +133 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +63 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/well-known.d.ts +34 -0
  52. package/dist/well-known.d.ts.map +1 -0
  53. package/dist/well-known.js +28 -0
  54. package/dist/well-known.js.map +1 -0
  55. package/docs/guides/delivery.md +57 -0
  56. package/docs/index.md +95 -0
  57. package/docs/ops/email.account.md +1 -0
  58. package/docs/ops/email.send.md +1 -0
  59. package/package.json +54 -0
@@ -0,0 +1,57 @@
1
+ # Wiring sign-in email delivery
2
+
3
+ The zero-config path: install mod-email + a driver, create one account, done.
4
+
5
+ ## 1. Install the mods
6
+
7
+ ```jsonc
8
+ // pattern.config.json — after the identity mods
9
+ { "mods": [
10
+ "@pattern-js/mod-identity",
11
+ "@pattern-js/mod-auth-magic-link",
12
+ "@pattern-js/mod-email",
13
+ "@pattern-js/mod-email-resend", // or @pattern-js/mod-email-smtp
14
+ ...
15
+ ] }
16
+ ```
17
+
18
+ (Order doesn't actually matter — drivers find the email service through the
19
+ two-phase install.) `create-pattern` wires all of this when you pick Resend or
20
+ SMTP at the "Sign-in link delivery?" question.
21
+
22
+ ## 2. Create the `default` account
23
+
24
+ Admin → **System → Email** → new account named `default`:
25
+
26
+ - **Resend**: the API key, from the vault (System → Secrets) or an env var
27
+ such as `RESEND_API_KEY`. The From address must be on a domain you verified
28
+ with Resend.
29
+ - **SMTP**: host (+ port/TLS/username as options) and the password as the one
30
+ secret. A local catcher like Mailpit (`host: localhost`, `port: 1025`, no
31
+ auth) is great in dev.
32
+
33
+ Press **Test** — it sends a real email to an address you type, which proves
34
+ credentials, From address and inbox placement in one go.
35
+
36
+ ## 3. There is no step 3
37
+
38
+ The packaged **`email.deliver-token`** workflow (visible in the admin catalog)
39
+ is already subscribed to identity's `identity.deliverToken` hook. It probes
40
+ for the `default` account on every delivery:
41
+
42
+ - no account → the payload passes through untouched and the link prints to
43
+ the server console, exactly as before;
44
+ - account exists → the link goes by email (subject "Your login link", a
45
+ button, a plain-text alternative) and the console stays silent;
46
+ - the send throws (bad key, provider down) → identity logs a warning and the
47
+ console fallback still prints the link — you are never locked out.
48
+
49
+ ## Customizing the email
50
+
51
+ Open `email.deliver-token` in the admin editor. The wording lives in two
52
+ `core.string.template` nodes (`{{purpose}}`, `{{url}}`, `{{email}}` from the
53
+ hook payload); the sender is the `email.account` node (point it at another
54
+ account name if you keep `default` for something else). For a bigger rewrite,
55
+ fork the workflow, subscribe your fork to the same hook (a `boundary.hook`
56
+ trigger with `hook: "identity.deliverToken"`), and set a lower `priority` so
57
+ it runs first — return `delivered: true` and the chain stops falling through.
package/docs/index.md ADDED
@@ -0,0 +1,95 @@
1
+ # Email
2
+
3
+ `@pattern-js/mod-email` is the **email contract**: it owns the accounts, the
4
+ `email.send` / `email.account` ops, the admin page, and a packaged workflow
5
+ that delivers identity sign-in links. It sends nothing by itself — a **driver
6
+ mod** does the wire work: `@pattern-js/mod-email-resend` (the Resend API) or
7
+ `@pattern-js/mod-email-smtp` (any SMTP relay, via nodemailer). Several drivers
8
+ can live in one runtime side by side.
9
+
10
+ ```jsonc
11
+ { "mods": ["@pattern-js/mod-email", "@pattern-js/mod-email-resend"] }
12
+ ```
13
+
14
+ ## Accounts
15
+
16
+ The one persisted concept is the **account** — a memorable name bound to a
17
+ driver, a From address, sourced secrets and driver options. Two accounts of
18
+ the same driver with different credentials are just two records (prod + a
19
+ staging relay, a marketing sender + an alerts sender). Manage them in admin →
20
+ **System → Email**; the form generates itself from whatever fields the
21
+ registered drivers declare, and the **Test** button sends a real email so you
22
+ verify end-to-end delivery, not a wiring check.
23
+
24
+ The `"default"` account is the convention everything falls back to: an
25
+ `email.send` node with nothing wired uses it, and the sign-in delivery
26
+ workflow probes for it.
27
+
28
+ Credentials never sit in workflow values or in the accounts file — an account
29
+ stores *references* (`{ source: "vault" | "env", key }`), resolved at send
30
+ time. The value flowing on edges is an `EmailAccountRef` (the account NAME),
31
+ so re-pointing an account in admin instantly re-targets every workflow using
32
+ it — the same move as mod-ai's model aliases.
33
+
34
+ ## Sending from a workflow
35
+
36
+ `email.send` takes `to` + `subject` + a body. Write the body once in
37
+ **`markdown`** — it renders to inline-styled HTML plus a plain-text
38
+ alternative, and a paragraph that is exactly one link becomes a button — or
39
+ pass explicit `html`/`text`, which win per part. Attachments accept in-memory
40
+ media (`{ bytes, mime }`, what `ai.image.generate` outputs), blob references
41
+ (`{ blobId }`, what `store.blob.put` returns), or literal files
42
+ (`{ filename, content }`). Pick a non-default sender by wiring an
43
+ `email.account` node into its `account` input.
44
+
45
+ ## Sign-in links, delivered
46
+
47
+ mod-identity delivers login/invite links through its `identity.deliverToken`
48
+ hook, with a console fallback. This mod ships a visible workflow —
49
+ **`email.deliver-token`** — subscribed to that hook: while no `default`
50
+ account exists it passes the payload through untouched (links keep printing to
51
+ the console, nothing to configure), and the moment you create the account,
52
+ sign-in links go by email. A broken account never locks you out: the send
53
+ fails, identity logs a warning, and the console fallback prints the link
54
+ anyway. Open the workflow in the admin editor to reword the email (two
55
+ `core.string.template` nodes) or fork it entirely — see the
56
+ [delivery guide](guides/delivery.md).
57
+
58
+ ## Writing a driver
59
+
60
+ A driver mod registers an `EmailDriverSpec` on the email service in `ready()`:
61
+
62
+ ```ts
63
+ import { EMAIL_SERVICE, type EmailService } from "@pattern-js/mod-email";
64
+
65
+ export default defineMod({
66
+ name: "my-email-driver",
67
+ ready: (engine) => {
68
+ engine.service<EmailService>(EMAIL_SERVICE)?.registerDriver({
69
+ id: "acme",
70
+ label: "Acme Mail",
71
+ secrets: [{ field: "apiKey", label: "API key", required: true }],
72
+ options: [{ field: "region", label: "Region", placeholder: "us-east-1" }],
73
+ send: async (message, creds, options) => {
74
+ // message is fully normalized: from/to[]/subject/html/text/attachments (bytes)
75
+ return { messageId: "..." };
76
+ },
77
+ });
78
+ },
79
+ });
80
+ ```
81
+
82
+ The declared `secrets`/`options` fields drive the account form in admin — a
83
+ driver mod ships zero UI. `mod-email-resend` is the worked example to copy
84
+ (one fetch, ~60 lines).
85
+
86
+ ## Config
87
+
88
+ Defaults work from the bare config entry. The accounts file lives at
89
+ `.pattern-data/email-config.json`; to move it, export a local wrapper mod:
90
+
91
+ ```js
92
+ // mods/email.mjs
93
+ import { emailMod } from "@pattern-js/mod-email";
94
+ export default emailMod({ configPath: "./.pattern-data/email-config.json" });
95
+ ```
@@ -0,0 +1 @@
1
+ Resolve an account name (configured in admin → System → Email) to an account reference for `email.send` — the ref carries the NAME, never the credentials, so re-pointing the account in admin instantly re-targets every workflow using it. Defaults to `"default"`. Two modes: `required: true` (the default) throws with an admin hint when the account is missing — the right failure for a workflow that must send; `required: false` probes instead, outputting `account: null` + `configured: false` so a `core.flow.branch` can take a fallback path — exactly how the packaged `email.deliver-token` workflow decides between emailing a sign-in link and leaving the console fallback alone.
@@ -0,0 +1 @@
1
+ Send one email through a configured account. Write the body once in `markdown` — it renders to inline-styled HTML plus a plain-text alternative, and a paragraph that is exactly one link becomes a button (the "click to sign in" line costs nothing to author) — or pass explicit `html`/`text`, which win per part. Attachments accept in-memory media (`{bytes,mime}`, what the AI generation ops output), blob references (`{blobId}`, what `store.blob.put` returns — mod-store resolves the bytes), or literal files (`{filename,content}`). With nothing wired to `account` it uses the `"default"` account; wire an `email.account` node (or a bare name string) to pick another. `from` defaults to the account's From and can be overridden per message. Credentials resolve at send time from the vault or env — they never ride the graph.
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@pattern-js/mod-email",
3
+ "version": "0.3.0-main.4a77893",
4
+ "description": "Transactional email for Pattern — the contract mod. Admin-configured accounts (provider + sourced secrets + from), driver mods plug in (Resend, SMTP, …), one email.send op, and a packaged workflow that delivers identity sign-in links the moment a default account exists.",
5
+ "keywords": [
6
+ "pattern",
7
+ "mod",
8
+ "email",
9
+ "transactional-email",
10
+ "smtp",
11
+ "resend"
12
+ ],
13
+ "type": "module",
14
+ "license": "MIT",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ }
20
+ },
21
+ "main": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "files": [
24
+ "dist",
25
+ "docs",
26
+ "README.md"
27
+ ],
28
+ "dependencies": {
29
+ "zod": "^4.0.0",
30
+ "@pattern-js/core": "0.3.0-main.4a77893",
31
+ "@pattern-js/runtime-node": "0.3.0-main.4a77893"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^22.0.0",
35
+ "typescript": "^5.6.0",
36
+ "@pattern-js/mod-identity": "0.3.0-main.4a77893",
37
+ "@pattern-js/mod-auth-magic-link": "0.3.0-main.4a77893"
38
+ },
39
+ "engines": {
40
+ "node": ">=22.5"
41
+ },
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/makkr-studio/pattern.git",
45
+ "directory": "packages/mod-email"
46
+ },
47
+ "homepage": "https://github.com/makkr-studio/pattern#readme",
48
+ "bugs": "https://github.com/makkr-studio/pattern/issues",
49
+ "author": "Makkr Studio (https://github.com/makkr-studio)",
50
+ "scripts": {
51
+ "build": "tsc -p tsconfig.json",
52
+ "typecheck": "tsc -p tsconfig.json --noEmit"
53
+ }
54
+ }