@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.
- package/LICENSE +21 -0
- package/README.md +59 -0
- package/dist/app.d.ts +13 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +172 -0
- package/dist/app.js.map +1 -0
- package/dist/config.d.ts +52 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +85 -0
- package/dist/config.js.map +1 -0
- package/dist/delivery.d.ts +26 -0
- package/dist/delivery.d.ts.map +1 -0
- package/dist/delivery.js +83 -0
- package/dist/delivery.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown.d.ts +17 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +173 -0
- package/dist/markdown.js.map +1 -0
- package/dist/mod.d.ts +20 -0
- package/dist/mod.d.ts.map +1 -0
- package/dist/mod.js +57 -0
- package/dist/mod.js.map +1 -0
- package/dist/ops/account.d.ts +4 -0
- package/dist/ops/account.d.ts.map +1 -0
- package/dist/ops/account.js +33 -0
- package/dist/ops/account.js.map +1 -0
- package/dist/ops/send.d.ts +4 -0
- package/dist/ops/send.d.ts.map +1 -0
- package/dist/ops/send.js +57 -0
- package/dist/ops/send.js.map +1 -0
- package/dist/ops/shared.d.ts +8 -0
- package/dist/ops/shared.d.ts.map +1 -0
- package/dist/ops/shared.js +18 -0
- package/dist/ops/shared.js.map +1 -0
- package/dist/service.d.ts +51 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +156 -0
- package/dist/service.js.map +1 -0
- package/dist/settings.d.ts +18 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +122 -0
- package/dist/settings.js.map +1 -0
- package/dist/types.d.ts +133 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +63 -0
- package/dist/types.js.map +1 -0
- package/dist/well-known.d.ts +34 -0
- package/dist/well-known.d.ts.map +1 -0
- package/dist/well-known.js +28 -0
- package/dist/well-known.js.map +1 -0
- package/docs/guides/delivery.md +57 -0
- package/docs/index.md +95 -0
- package/docs/ops/email.account.md +1 -0
- package/docs/ops/email.send.md +1 -0
- 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
|
+
}
|