@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,17 @@
1
+ /**
2
+ * @pattern-js/mod-email — markdown → email HTML + plain-text alternative.
3
+ *
4
+ * Email clients ignore stylesheets, so every style is inline and the subset is
5
+ * deliberately small: headings, paragraphs, fenced code, lists, bold/italic/
6
+ * code spans, links (+ bare-URL autolink). One email-specific rule: a paragraph
7
+ * that is EXACTLY one link renders as a button — the "click the button" line in
8
+ * a sign-in email costs nothing to author. Only http/https/mailto hrefs survive
9
+ * (a `javascript:` link renders as plain text). Zero dependencies on purpose:
10
+ * this is the provider-independent part of sending decent email, and it belongs
11
+ * to the contract mod.
12
+ */
13
+ export declare function renderEmailMarkdown(md: string): {
14
+ html: string;
15
+ text: string;
16
+ };
17
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA8IH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA4C9E"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * @pattern-js/mod-email — markdown → email HTML + plain-text alternative.
3
+ *
4
+ * Email clients ignore stylesheets, so every style is inline and the subset is
5
+ * deliberately small: headings, paragraphs, fenced code, lists, bold/italic/
6
+ * code spans, links (+ bare-URL autolink). One email-specific rule: a paragraph
7
+ * that is EXACTLY one link renders as a button — the "click the button" line in
8
+ * a sign-in email costs nothing to author. Only http/https/mailto hrefs survive
9
+ * (a `javascript:` link renders as plain text). Zero dependencies on purpose:
10
+ * this is the provider-independent part of sending decent email, and it belongs
11
+ * to the contract mod.
12
+ */
13
+ const FONT = "-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif";
14
+ const MONO = "ui-monospace,SFMono-Regular,Menlo,Consolas,monospace";
15
+ const S = {
16
+ wrap: `max-width:560px;margin:0 auto;padding:24px;font-family:${FONT};font-size:15px;line-height:1.6;color:#1a1a1a;`,
17
+ h1: `margin:0 0 12px;font-size:22px;line-height:1.3;font-weight:700;`,
18
+ h2: `margin:20px 0 8px;font-size:18px;line-height:1.35;font-weight:700;`,
19
+ h3: `margin:16px 0 6px;font-size:15px;line-height:1.4;font-weight:700;`,
20
+ p: `margin:0 0 12px;`,
21
+ a: `color:#2563eb;text-decoration:underline;`,
22
+ button: `display:inline-block;padding:12px 20px;border-radius:8px;background:#111111;color:#ffffff;text-decoration:none;font-weight:600;`,
23
+ code: `font-family:${MONO};font-size:13px;background:#f4f4f5;border-radius:4px;padding:1px 5px;`,
24
+ pre: `margin:0 0 12px;padding:12px 14px;background:#f4f4f5;border-radius:8px;font-family:${MONO};font-size:13px;line-height:1.5;overflow:auto;white-space:pre-wrap;`,
25
+ list: `margin:0 0 12px;padding-left:24px;`,
26
+ };
27
+ const SAFE_HREF = /^(https?:|mailto:)/i;
28
+ // After HTML-escaping, `&` in a URL reads `&` — fine inside an attribute.
29
+ const URL_RE = /https?:\/\/[^\s<>"')]+/g;
30
+ // Placeholder delimiter for protected inline spans; stripped from input first.
31
+ const NUL = "\u0000";
32
+ function escapeHtml(s) {
33
+ return s
34
+ .replace(/&/g, "&amp;")
35
+ .replace(/</g, "&lt;")
36
+ .replace(/>/g, "&gt;")
37
+ .replace(/"/g, "&quot;")
38
+ .replace(/'/g, "&#39;");
39
+ }
40
+ function parseBlocks(md) {
41
+ const blocks = [];
42
+ const lines = md.replace(/\r\n/g, "\n").split("\n");
43
+ let i = 0;
44
+ while (i < lines.length) {
45
+ const line = lines[i];
46
+ if (!line.trim()) {
47
+ i++;
48
+ continue;
49
+ }
50
+ // Fenced code — verbatim until the closing fence.
51
+ if (/^```/.test(line)) {
52
+ const body = [];
53
+ i++;
54
+ while (i < lines.length && !/^```/.test(lines[i])) {
55
+ body.push(lines[i]);
56
+ i++;
57
+ }
58
+ i++; // closing fence (or EOF)
59
+ blocks.push({ kind: "code", body: body.join("\n") });
60
+ continue;
61
+ }
62
+ const heading = /^(#{1,3})\s+(.*)$/.exec(line);
63
+ if (heading) {
64
+ blocks.push({ kind: "heading", depth: heading[1].length, text: heading[2] });
65
+ i++;
66
+ continue;
67
+ }
68
+ const bullet = /^\s*[-*]\s+(.*)$/.exec(line);
69
+ const numbered = /^\s*\d+[.)]\s+(.*)$/.exec(line);
70
+ if (bullet || numbered) {
71
+ const ordered = Boolean(numbered);
72
+ const items = [];
73
+ const re = ordered ? /^\s*\d+[.)]\s+(.*)$/ : /^\s*[-*]\s+(.*)$/;
74
+ while (i < lines.length) {
75
+ const m = re.exec(lines[i]);
76
+ if (!m)
77
+ break;
78
+ items.push(m[1]);
79
+ i++;
80
+ }
81
+ blocks.push({ kind: "list", ordered, items });
82
+ continue;
83
+ }
84
+ // Paragraph: consecutive non-blank, non-structural lines join with spaces.
85
+ const para = [line.trim()];
86
+ i++;
87
+ while (i < lines.length && lines[i].trim() && !/^(#{1,3}\s|```|\s*[-*]\s|\s*\d+[.)]\s)/.test(lines[i])) {
88
+ para.push(lines[i].trim());
89
+ i++;
90
+ }
91
+ blocks.push({ kind: "para", text: para.join(" ") });
92
+ }
93
+ return blocks;
94
+ }
95
+ /* ── inline rendering (on escaped text) ────────────────────────────────── */
96
+ function renderInline(escaped) {
97
+ // Protect code spans (and built anchors) from further formatting with
98
+ // NUL-delimited placeholders — NUL can't survive in the input (stripped).
99
+ const stash = [];
100
+ const keep = (html) => `${NUL}${stash.push(html) - 1}${NUL}`;
101
+ let s = escaped.replace(/`([^`]+)`/g, (_, code) => keep(`<code style="${S.code}">${code}</code>`));
102
+ // [text](url) — unsafe schemes render as their text, not a link.
103
+ s = s.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (_, text, href) => SAFE_HREF.test(href) ? keep(`<a href="${href}" style="${S.a}">${text}</a>`) : text);
104
+ // Bare URLs autolink.
105
+ s = s.replace(URL_RE, (url) => keep(`<a href="${url}" style="${S.a}">${url}</a>`));
106
+ s = s.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
107
+ s = s.replace(/(^|\W)\*([^*\s][^*]*)\*/g, "$1<em>$2</em>");
108
+ s = s.replace(/(^|\W)_([^_\s][^_]*)_/g, "$1<em>$2</em>");
109
+ return s.replace(/\u0000(\d+)\u0000/g, (_, n) => stash[Number(n)]);
110
+ }
111
+ /** The button rule: a paragraph that is exactly one link (or one bare URL). */
112
+ function soleLink(text) {
113
+ const t = text.trim();
114
+ const linked = /^\[([^\]]+)\]\(([^)\s]+)\)$/.exec(t);
115
+ if (linked && SAFE_HREF.test(linked[2]))
116
+ return { href: linked[2], label: linked[1] };
117
+ if (/^https?:\/\/\S+$/.test(t))
118
+ return { href: t, label: t };
119
+ return undefined;
120
+ }
121
+ /* ── text alternative ──────────────────────────────────────────────────── */
122
+ function inlineText(text) {
123
+ return text
124
+ .replace(/`([^`]+)`/g, "$1")
125
+ .replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (_, label, href) => (label === href ? href : `${label} (${href})`))
126
+ .replace(/\*\*([^*]+)\*\*/g, "$1")
127
+ .replace(/(^|\W)\*([^*\s][^*]*)\*/g, "$1$2")
128
+ .replace(/(^|\W)_([^_\s][^_]*)_/g, "$1$2");
129
+ }
130
+ /* ── public API ────────────────────────────────────────────────────────── */
131
+ export function renderEmailMarkdown(md) {
132
+ const blocks = parseBlocks(md.replaceAll(NUL, ""));
133
+ const html = blocks
134
+ .map((b) => {
135
+ switch (b.kind) {
136
+ case "heading": {
137
+ const tag = `h${b.depth}`;
138
+ return `<${tag} style="${S[tag]}">${renderInline(escapeHtml(b.text))}</${tag}>`;
139
+ }
140
+ case "code":
141
+ return `<pre style="${S.pre}">${escapeHtml(b.body)}</pre>`;
142
+ case "list": {
143
+ const tag = b.ordered ? "ol" : "ul";
144
+ const items = b.items.map((it) => `<li>${renderInline(escapeHtml(it))}</li>`).join("");
145
+ return `<${tag} style="${S.list}">${items}</${tag}>`;
146
+ }
147
+ case "para": {
148
+ const button = soleLink(b.text);
149
+ if (button) {
150
+ return `<p style="${S.p}"><a href="${escapeHtml(button.href)}" style="${S.button}">${escapeHtml(button.label)}</a></p>`;
151
+ }
152
+ return `<p style="${S.p}">${renderInline(escapeHtml(b.text))}</p>`;
153
+ }
154
+ }
155
+ })
156
+ .join("\n");
157
+ const text = blocks
158
+ .map((b) => {
159
+ switch (b.kind) {
160
+ case "heading":
161
+ return inlineText(b.text);
162
+ case "code":
163
+ return b.body;
164
+ case "list":
165
+ return b.items.map((it, i) => (b.ordered ? `${i + 1}. ${inlineText(it)}` : `- ${inlineText(it)}`)).join("\n");
166
+ case "para":
167
+ return inlineText(b.text);
168
+ }
169
+ })
170
+ .join("\n\n");
171
+ return { html: `<div style="${S.wrap}">\n${html}\n</div>`, text };
172
+ }
173
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"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"}
package/dist/mod.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @pattern-js/mod-email — mod assembly.
3
+ *
4
+ * The CONTRACT mod: it owns the account settings, the `email.send` /
5
+ * `email.account` ops, the admin page, and the packaged sign-in delivery
6
+ * workflow. It sends nothing by itself — driver mods (mod-email-resend,
7
+ * mod-email-smtp, …) register an `EmailDriverSpec` on the service in their
8
+ * `ready()`. Both services are provided in `setup`, so drivers can rely on
9
+ * them regardless of listing order in pattern.config.json (two-phase install).
10
+ */
11
+ import { type PatternMod } from "@pattern-js/core";
12
+ export interface EmailModOptions {
13
+ /** Where accounts persist. Default ".pattern-data/email-config.json". */
14
+ configPath?: string;
15
+ }
16
+ export declare function emailMod(options?: EmailModOptions): PatternMod;
17
+ /** Ready-to-use with defaults (for `loadMods`/`engine.use`). */
18
+ declare const _default: PatternMod;
19
+ export default _default;
20
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAS3E,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAYD,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,UAAU,CAqBlE;AAED,gEAAgE;;AAChE,wBAA0B"}
package/dist/mod.js ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @pattern-js/mod-email — mod assembly.
3
+ *
4
+ * The CONTRACT mod: it owns the account settings, the `email.send` /
5
+ * `email.account` ops, the admin page, and the packaged sign-in delivery
6
+ * workflow. It sends nothing by itself — driver mods (mod-email-resend,
7
+ * mod-email-smtp, …) register an `EmailDriverSpec` on the service in their
8
+ * `ready()`. Both services are provided in `setup`, so drivers can rely on
9
+ * them regardless of listing order in pattern.config.json (two-phase install).
10
+ */
11
+ import { existsSync } from "node:fs";
12
+ import { fileURLToPath } from "node:url";
13
+ import { localFs, provideFilesystem } from "@pattern-js/runtime-node";
14
+ import { defineMod } from "@pattern-js/core";
15
+ import { EmailConfigService } from "./config.js";
16
+ import { DefaultEmailService } from "./service.js";
17
+ import { EMAIL_CONFIG_SERVICE, EMAIL_SERVICE } from "./well-known.js";
18
+ import { accountOp } from "./ops/account.js";
19
+ import { sendOp } from "./ops/send.js";
20
+ import { deliverTokenWorkflow } from "./delivery.js";
21
+ import { emailAdminRoutes, emailFrontend, settingsOps } from "./settings.js";
22
+ /** The packaged docs/ chapter (the `docs` contribution points at "email-docs"). */
23
+ function packagedDocs(engine) {
24
+ try {
25
+ const dir = fileURLToPath(new URL("../docs", import.meta.url));
26
+ if (existsSync(dir))
27
+ provideFilesystem(engine, "email-docs", localFs(dir));
28
+ }
29
+ catch {
30
+ /* packaged without docs — the contribution is simply skipped */
31
+ }
32
+ }
33
+ export function emailMod(options = {}) {
34
+ const config = new EmailConfigService(options.configPath);
35
+ const service = new DefaultEmailService(config);
36
+ return defineMod({
37
+ name: "@pattern-js/mod-email",
38
+ docs: { filesystem: "email-docs", title: "Email", order: 43 },
39
+ ops: [sendOp, accountOp, ...settingsOps],
40
+ // The delivery workflow registers unconditionally: without mod-identity its
41
+ // hook is auto-declared and never invoked (inert); with it, delivery starts
42
+ // the moment a "default" account exists.
43
+ workflows: [deliverTokenWorkflow(), ...emailAdminRoutes()],
44
+ frontend: emailFrontend(),
45
+ setup: (engine) => {
46
+ packagedDocs(engine);
47
+ engine.provideService(EMAIL_CONFIG_SERVICE, config);
48
+ engine.provideService(EMAIL_SERVICE, service);
49
+ },
50
+ ready: async () => {
51
+ await config.load();
52
+ },
53
+ });
54
+ }
55
+ /** Ready-to-use with defaults (for `loadMods`/`engine.use`). */
56
+ export default emailMod();
57
+ //# sourceMappingURL=mod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.js","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,SAAS,EAAgC,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAO7E,mFAAmF;AACnF,SAAS,YAAY,CAAC,MAAc;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,IAAI,UAAU,CAAC,GAAG,CAAC;YAAE,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7E,CAAC;IAAC,MAAM,CAAC;QACP,gEAAgE;IAClE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,UAA2B,EAAE;IACpD,MAAM,MAAM,GAAG,IAAI,kBAAkB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAChD,OAAO,SAAS,CAAC;QACf,IAAI,EAAE,uBAAuB;QAC7B,IAAI,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;QAC7D,GAAG,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,WAAW,CAAC;QACxC,4EAA4E;QAC5E,4EAA4E;QAC5E,yCAAyC;QACzC,SAAS,EAAE,CAAC,oBAAoB,EAAE,EAAE,GAAG,gBAAgB,EAAE,CAAC;QAC1D,QAAQ,EAAE,aAAa,EAAE;QACzB,KAAK,EAAE,CAAC,MAAc,EAAE,EAAE;YACxB,YAAY,CAAC,MAAM,CAAC,CAAC;YACrB,MAAM,CAAC,cAAc,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;YACpD,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC;QACD,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACtB,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,gEAAgE;AAChE,eAAe,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ /** @pattern-js/mod-email — email.account: resolve a named account to a ref value (like ai.alias). */
2
+ import { type OpDefinition } from "@pattern-js/core";
3
+ export declare const accountOp: OpDefinition;
4
+ //# sourceMappingURL=account.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/ops/account.ts"],"names":[],"mappings":"AAAA,qGAAqG;AAErG,OAAO,EAAY,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAK/D,eAAO,MAAM,SAAS,EAAE,YA2BvB,CAAC"}
@@ -0,0 +1,33 @@
1
+ /** @pattern-js/mod-email — email.account: resolve a named account to a ref value (like ai.alias). */
2
+ import { value, z } from "@pattern-js/core";
3
+ import { accountRefSchema } from "../types.js";
4
+ import { DEFAULT_ACCOUNT } from "../config.js";
5
+ import { emailConfig, maybe } from "./shared.js";
6
+ export const accountOp = {
7
+ type: "email.account",
8
+ title: "email.account",
9
+ description: "Resolve a named email account (configured in admin → System → Email) to an account reference. " +
10
+ "Re-pointing the account in admin instantly re-targets every workflow using it. Defaults to " +
11
+ '"default". With required=false it probes instead of throwing: `configured` reports whether the ' +
12
+ "account exists — the packaged delivery workflow branches on it.",
13
+ config: z.object({
14
+ account: z.string().min(1).default(DEFAULT_ACCOUNT),
15
+ required: z.boolean().default(true),
16
+ }),
17
+ configInputs: { account: value(z.string()) },
18
+ inputs: {},
19
+ outputs: {
20
+ account: value(accountRefSchema.nullable()),
21
+ configured: value(z.boolean()),
22
+ },
23
+ execute: async (ctx) => {
24
+ const cfg = ctx.config;
25
+ const name = (await maybe(ctx, "account")) ?? cfg.account;
26
+ const ref = emailConfig(ctx).resolveAccount(name);
27
+ if (!ref && cfg.required) {
28
+ throw new Error(`email.account: no account "${name}" is configured — add it in admin → System → Email.`);
29
+ }
30
+ return { account: ref ?? null, configured: Boolean(ref) };
31
+ },
32
+ };
33
+ //# sourceMappingURL=account.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.js","sourceRoot":"","sources":["../../src/ops/account.ts"],"names":[],"mappings":"AAAA,qGAAqG;AAErG,OAAO,EAAE,KAAK,EAAE,CAAC,EAAqB,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,CAAC,MAAM,SAAS,GAAiB;IACrC,IAAI,EAAE,eAAe;IACrB,KAAK,EAAE,eAAe;IACtB,WAAW,EACT,gGAAgG;QAChG,6FAA6F;QAC7F,iGAAiG;QACjG,iEAAiE;IACnE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;QACnD,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;KACpC,CAAC;IACF,YAAY,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;IAC5C,MAAM,EAAE,EAAE;IACV,OAAO,EAAE;QACP,OAAO,EAAE,KAAK,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC;QAC3C,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;KAC/B;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACrB,MAAM,GAAG,GAAG,GAAG,CAAC,MAAgD,CAAC;QACjE,MAAM,IAAI,GAAG,CAAC,MAAM,KAAK,CAAS,GAAG,EAAE,SAAS,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC;QAClE,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,qDAAqD,CAAC,CAAC;QAC3G,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;IAC5D,CAAC;CACF,CAAC"}
@@ -0,0 +1,4 @@
1
+ /** @pattern-js/mod-email — email.send: the operational node every driver serves. */
2
+ import { type OpDefinition } from "@pattern-js/core";
3
+ export declare const sendOp: OpDefinition;
4
+ //# sourceMappingURL=send.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../src/ops/send.ts"],"names":[],"mappings":"AAAA,oFAAoF;AAEpF,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAMzE,eAAO,MAAM,MAAM,EAAE,YAwDpB,CAAC"}
@@ -0,0 +1,57 @@
1
+ /** @pattern-js/mod-email — email.send: the operational node every driver serves. */
2
+ import { required, value, z } from "@pattern-js/core";
3
+ import { accountRefSchema, attachmentInputSchema } from "../types.js";
4
+ import { emailService, maybe } from "./shared.js";
5
+ const recipients = z.union([z.string(), z.array(z.string())]);
6
+ export const sendOp = {
7
+ type: "email.send",
8
+ title: "email.send",
9
+ description: "Send an email through a configured account (drivers: Resend, SMTP, …). Write the body once in " +
10
+ "`markdown` — it renders to inline-styled HTML plus a plain-text alternative (a paragraph that is " +
11
+ "exactly one link becomes a button) — or pass explicit `html`/`text`, which win per part. " +
12
+ "Attachments accept in-memory media ({bytes,mime}), blob references ({blobId}), or literal files " +
13
+ '({filename,content}). Without an `account` input it uses the "default" account.',
14
+ config: z.object({
15
+ /** Static account name; an `account` input (ref or name) wins over it. */
16
+ account: z.string().optional(),
17
+ }),
18
+ inputs: {
19
+ account: value(z.union([accountRefSchema, z.string()])),
20
+ to: required(recipients),
21
+ subject: required(z.string()),
22
+ markdown: value(z.string()),
23
+ html: value(z.string()),
24
+ text: value(z.string()),
25
+ from: value(z.string()),
26
+ cc: value(recipients),
27
+ bcc: value(recipients),
28
+ replyTo: value(z.string()),
29
+ attachments: value(z.array(attachmentInputSchema)),
30
+ },
31
+ outputs: {
32
+ result: value(z.object({
33
+ messageId: z.string().optional(),
34
+ provider: z.string(),
35
+ account: z.string(),
36
+ })),
37
+ },
38
+ execute: async (ctx) => {
39
+ const cfg = ctx.config;
40
+ const [account, to, subject, markdown, html, text, from, cc, bcc, replyTo, attachments] = await Promise.all([
41
+ maybe(ctx, "account"),
42
+ ctx.input.value("to"),
43
+ ctx.input.value("subject"),
44
+ maybe(ctx, "markdown"),
45
+ maybe(ctx, "html"),
46
+ maybe(ctx, "text"),
47
+ maybe(ctx, "from"),
48
+ maybe(ctx, "cc"),
49
+ maybe(ctx, "bcc"),
50
+ maybe(ctx, "replyTo"),
51
+ maybe(ctx, "attachments"),
52
+ ]);
53
+ const result = await emailService(ctx).send({ account: account ?? cfg.account, to, cc, bcc, replyTo, from, subject, markdown, html, text, attachments }, ctx);
54
+ return { result };
55
+ },
56
+ };
57
+ //# sourceMappingURL=send.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/ops/send.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /** @pattern-js/mod-email — shared op helpers. */
2
+ import type { OpContext } from "@pattern-js/core";
3
+ import type { EmailService } from "../service.js";
4
+ import type { EmailConfigService } from "../config.js";
5
+ export declare function maybe<T>(ctx: OpContext, port: string): Promise<T | undefined>;
6
+ export declare function emailService(ctx: OpContext): EmailService;
7
+ export declare function emailConfig(ctx: OpContext): EmailConfigService;
8
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/ops/shared.ts"],"names":[],"mappings":"AAAA,iDAAiD;AAEjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGvD,wBAAsB,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAEnF;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,SAAS,GAAG,YAAY,CAIzD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,SAAS,GAAG,kBAAkB,CAI9D"}
@@ -0,0 +1,18 @@
1
+ /** @pattern-js/mod-email — shared op helpers. */
2
+ import { EMAIL_CONFIG_SERVICE, EMAIL_SERVICE } from "../well-known.js";
3
+ export async function maybe(ctx, port) {
4
+ return ctx.input.has(port) ? (await ctx.input.value(port)) : undefined;
5
+ }
6
+ export function emailService(ctx) {
7
+ const svc = ctx.services[EMAIL_SERVICE];
8
+ if (!svc)
9
+ throw new Error("mod-email: email service missing — install @pattern-js/mod-email.");
10
+ return svc;
11
+ }
12
+ export function emailConfig(ctx) {
13
+ const svc = ctx.services[EMAIL_CONFIG_SERVICE];
14
+ if (!svc)
15
+ throw new Error("mod-email: email config missing — install @pattern-js/mod-email.");
16
+ return svc;
17
+ }
18
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/ops/shared.ts"],"names":[],"mappings":"AAAA,iDAAiD;AAKjD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEvE,MAAM,CAAC,KAAK,UAAU,KAAK,CAAI,GAAc,EAAE,IAAY;IACzD,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAc;IACzC,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,aAAa,CAA6B,CAAC;IACpE,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;IAC/F,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAc;IACxC,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,oBAAoB,CAAmC,CAAC;IACjF,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;IAC9F,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @pattern-js/mod-email — the contract surface.
3
+ *
4
+ * Driver mods (mod-email-resend, mod-email-smtp, …) register an
5
+ * `EmailDriverSpec` here in their `ready()` (all setups run before any ready,
6
+ * so the service always exists by then — mirroring how login methods register
7
+ * on mod-identity). `send` resolves the account by NAME at send time: sourced
8
+ * secrets (vault/env), markdown → html+text, attachments → bytes, then the
9
+ * driver. Secret VALUES never sit in workflow values or persisted config.
10
+ */
11
+ import type { OpContext } from "@pattern-js/core";
12
+ import { type EmailConfigService } from "./config.js";
13
+ import type { EmailAccount, EmailDriverInfo, EmailDriverSpec, SendInput } from "./types.js";
14
+ export interface SendResult {
15
+ messageId?: string;
16
+ provider: string;
17
+ account: string;
18
+ }
19
+ export interface TestResult {
20
+ ok: boolean;
21
+ detail?: string;
22
+ messageId?: string;
23
+ }
24
+ export interface EmailService {
25
+ /** Driver mods call this in their `ready()`; same-id re-registration replaces. */
26
+ registerDriver(spec: EmailDriverSpec): void;
27
+ drivers(): EmailDriverInfo[];
28
+ driver(id: string): EmailDriverSpec | undefined;
29
+ /** Resolve account + secrets + bodies + attachments, then delegate to the driver. */
30
+ send(input: SendInput, ctx: OpContext): Promise<SendResult>;
31
+ /** REAL test send of a canned message to `to`, from an (unsaved) draft account. */
32
+ testAccount(draft: EmailAccount, to: string, ctx: OpContext): Promise<TestResult>;
33
+ }
34
+ export declare class DefaultEmailService implements EmailService {
35
+ private readonly config;
36
+ private readonly registry;
37
+ constructor(config: EmailConfigService);
38
+ registerDriver(spec: EmailDriverSpec): void;
39
+ drivers(): EmailDriverInfo[];
40
+ driver(id: string): EmailDriverSpec | undefined;
41
+ send(input: SendInput, ctx: OpContext): Promise<SendResult>;
42
+ testAccount(draft: EmailAccount, to: string, ctx: OpContext): Promise<TestResult>;
43
+ private sendVia;
44
+ /** Resolve every sourced secret the account carries; enforce the driver's required fields. */
45
+ private resolveSecrets;
46
+ private resolveSourced;
47
+ /** to/cc/bcc listed, bodies final (explicit html/text win over markdown per part), attachments as bytes. */
48
+ private normalize;
49
+ private resolveAttachment;
50
+ }
51
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAmB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEvE,OAAO,KAAK,EAEV,YAAY,EAEZ,eAAe,EACf,eAAe,EAGf,SAAS,EACV,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,kFAAkF;IAClF,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IAC5C,OAAO,IAAI,eAAe,EAAE,CAAC;IAC7B,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IAChD,qFAAqF;IACrF,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC5D,mFAAmF;IACnF,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACnF;AAED,qBAAa,mBAAoB,YAAW,YAAY;IAG1C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;gBAElC,MAAM,EAAE,kBAAkB;IAEvD,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI;IAI3C,OAAO,IAAI,eAAe,EAAE;IAI5B,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAIzC,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IAS3D,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;YAqBzE,OAAO;IAcrB,8FAA8F;YAChF,cAAc;YAmBd,cAAc;IAW5B,4GAA4G;YAC9F,SAAS;YAmCT,iBAAiB;CAqBhC"}