@shware/workflow 0.4.0 → 2.0.0

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 (162) hide show
  1. package/README.md +90 -49
  2. package/dist/client.d.cts +1 -1
  3. package/dist/client.d.mts +1 -1
  4. package/dist/cloudflare/bindings.cjs.map +1 -1
  5. package/dist/cloudflare/bindings.d.cts +1 -1
  6. package/dist/cloudflare/bindings.d.mts +1 -1
  7. package/dist/cloudflare/bindings.mjs.map +1 -1
  8. package/dist/cloudflare/channels.cjs +13 -18
  9. package/dist/cloudflare/channels.cjs.map +1 -1
  10. package/dist/cloudflare/channels.d.cts +3 -3
  11. package/dist/cloudflare/channels.d.cts.map +1 -1
  12. package/dist/cloudflare/channels.d.mts +3 -3
  13. package/dist/cloudflare/channels.d.mts.map +1 -1
  14. package/dist/cloudflare/channels.mjs +14 -19
  15. package/dist/cloudflare/channels.mjs.map +1 -1
  16. package/dist/cloudflare/index.d.cts +4 -4
  17. package/dist/cloudflare/index.d.mts +4 -4
  18. package/dist/cloudflare/router.d.cts +1 -1
  19. package/dist/cloudflare/router.d.mts +1 -1
  20. package/dist/cloudflare/runner.d.cts +2 -2
  21. package/dist/cloudflare/runner.d.mts +2 -2
  22. package/dist/cloudflare/senders.cjs +15 -8
  23. package/dist/cloudflare/senders.cjs.map +1 -1
  24. package/dist/cloudflare/senders.d.cts +15 -11
  25. package/dist/cloudflare/senders.d.cts.map +1 -1
  26. package/dist/cloudflare/senders.d.mts +15 -11
  27. package/dist/cloudflare/senders.d.mts.map +1 -1
  28. package/dist/cloudflare/senders.mjs +15 -8
  29. package/dist/cloudflare/senders.mjs.map +1 -1
  30. package/dist/cloudflare/worker.cjs +7 -4
  31. package/dist/cloudflare/worker.cjs.map +1 -1
  32. package/dist/cloudflare/worker.d.cts +2 -2
  33. package/dist/cloudflare/worker.d.cts.map +1 -1
  34. package/dist/cloudflare/worker.d.mts +2 -2
  35. package/dist/cloudflare/worker.d.mts.map +1 -1
  36. package/dist/cloudflare/worker.mjs +8 -5
  37. package/dist/cloudflare/worker.mjs.map +1 -1
  38. package/dist/config.cjs +66 -42
  39. package/dist/config.cjs.map +1 -1
  40. package/dist/config.d.cts +26 -45
  41. package/dist/config.d.cts.map +1 -1
  42. package/dist/config.d.mts +26 -45
  43. package/dist/config.d.mts.map +1 -1
  44. package/dist/config.mjs +67 -42
  45. package/dist/config.mjs.map +1 -1
  46. package/dist/dsl/action.cjs +1 -1
  47. package/dist/dsl/action.cjs.map +1 -1
  48. package/dist/dsl/action.d.cts +1 -1
  49. package/dist/dsl/action.d.mts +1 -1
  50. package/dist/dsl/action.mjs +1 -1
  51. package/dist/dsl/action.mjs.map +1 -1
  52. package/dist/dsl/bundle.cjs +3 -2
  53. package/dist/dsl/bundle.cjs.map +1 -1
  54. package/dist/dsl/bundle.d.cts +3 -2
  55. package/dist/dsl/bundle.d.cts.map +1 -1
  56. package/dist/dsl/bundle.d.mts +3 -2
  57. package/dist/dsl/bundle.d.mts.map +1 -1
  58. package/dist/dsl/bundle.mjs +3 -2
  59. package/dist/dsl/bundle.mjs.map +1 -1
  60. package/dist/dsl/condition.cjs +1 -1
  61. package/dist/dsl/condition.cjs.map +1 -1
  62. package/dist/dsl/condition.d.cts +1 -1
  63. package/dist/dsl/condition.d.mts +1 -1
  64. package/dist/dsl/condition.mjs +1 -1
  65. package/dist/dsl/condition.mjs.map +1 -1
  66. package/dist/dsl/flow.cjs +45 -17
  67. package/dist/dsl/flow.cjs.map +1 -1
  68. package/dist/dsl/flow.d.cts +30 -17
  69. package/dist/dsl/flow.d.cts.map +1 -1
  70. package/dist/dsl/flow.d.mts +30 -17
  71. package/dist/dsl/flow.d.mts.map +1 -1
  72. package/dist/dsl/flow.mjs +45 -18
  73. package/dist/dsl/flow.mjs.map +1 -1
  74. package/dist/dsl/index.cjs +0 -1
  75. package/dist/dsl/index.d.cts +2 -2
  76. package/dist/dsl/index.d.mts +2 -2
  77. package/dist/dsl/index.mjs +2 -2
  78. package/dist/dsl/template.cjs +2 -47
  79. package/dist/dsl/template.cjs.map +1 -1
  80. package/dist/dsl/template.d.cts +4 -54
  81. package/dist/dsl/template.d.cts.map +1 -1
  82. package/dist/dsl/template.d.mts +4 -54
  83. package/dist/dsl/template.d.mts.map +1 -1
  84. package/dist/dsl/template.mjs +3 -47
  85. package/dist/dsl/template.mjs.map +1 -1
  86. package/dist/dsl/workflow.cjs +1 -1
  87. package/dist/dsl/workflow.cjs.map +1 -1
  88. package/dist/dsl/workflow.d.cts +1 -1
  89. package/dist/dsl/workflow.d.mts +1 -1
  90. package/dist/dsl/workflow.mjs +1 -1
  91. package/dist/dsl/workflow.mjs.map +1 -1
  92. package/dist/email-context.cjs +29 -0
  93. package/dist/email-context.cjs.map +1 -0
  94. package/dist/email-context.d.cts +11 -0
  95. package/dist/email-context.d.cts.map +1 -0
  96. package/dist/email-context.d.mts +11 -0
  97. package/dist/email-context.d.mts.map +1 -0
  98. package/dist/email-context.mjs +27 -0
  99. package/dist/email-context.mjs.map +1 -0
  100. package/dist/email.cjs +72 -0
  101. package/dist/email.cjs.map +1 -0
  102. package/dist/email.d.cts +63 -0
  103. package/dist/email.d.cts.map +1 -0
  104. package/dist/email.d.mts +63 -0
  105. package/dist/email.d.mts.map +1 -0
  106. package/dist/email.mjs +65 -0
  107. package/dist/email.mjs.map +1 -0
  108. package/dist/engine/index.d.cts +2 -2
  109. package/dist/engine/index.d.mts +2 -2
  110. package/dist/engine/ports.d.cts +7 -2
  111. package/dist/engine/ports.d.cts.map +1 -1
  112. package/dist/engine/ports.d.mts +7 -2
  113. package/dist/engine/ports.d.mts.map +1 -1
  114. package/dist/index.cjs +13 -3
  115. package/dist/index.d.cts +5 -4
  116. package/dist/index.d.mts +5 -4
  117. package/dist/index.mjs +5 -4
  118. package/dist/push.cjs +3 -5
  119. package/dist/push.cjs.map +1 -1
  120. package/dist/push.d.cts +2 -2
  121. package/dist/push.d.cts.map +1 -1
  122. package/dist/push.d.mts +2 -2
  123. package/dist/push.d.mts.map +1 -1
  124. package/dist/push.mjs +4 -6
  125. package/dist/push.mjs.map +1 -1
  126. package/dist/react-email.cjs +16 -8
  127. package/dist/react-email.cjs.map +1 -1
  128. package/dist/react-email.d.cts +2 -1
  129. package/dist/react-email.d.cts.map +1 -1
  130. package/dist/react-email.d.mts +2 -1
  131. package/dist/react-email.d.mts.map +1 -1
  132. package/dist/react-email.mjs +17 -9
  133. package/dist/react-email.mjs.map +1 -1
  134. package/dist/reporting/classify.cjs.map +1 -1
  135. package/dist/reporting/classify.d.cts +1 -1
  136. package/dist/reporting/classify.d.mts +1 -1
  137. package/dist/reporting/classify.mjs.map +1 -1
  138. package/dist/reporting/observer.cjs +5 -4
  139. package/dist/reporting/observer.cjs.map +1 -1
  140. package/dist/reporting/observer.d.cts +3 -2
  141. package/dist/reporting/observer.d.cts.map +1 -1
  142. package/dist/reporting/observer.d.mts +3 -2
  143. package/dist/reporting/observer.d.mts.map +1 -1
  144. package/dist/reporting/observer.mjs +5 -4
  145. package/dist/reporting/observer.mjs.map +1 -1
  146. package/dist/reporting/tracking.cjs +3 -12
  147. package/dist/reporting/tracking.cjs.map +1 -1
  148. package/dist/reporting/tracking.d.cts +18 -7
  149. package/dist/reporting/tracking.d.cts.map +1 -1
  150. package/dist/reporting/tracking.d.mts +18 -7
  151. package/dist/reporting/tracking.d.mts.map +1 -1
  152. package/dist/reporting/tracking.mjs +3 -12
  153. package/dist/reporting/tracking.mjs.map +1 -1
  154. package/dist/templates.cjs +90 -30
  155. package/dist/templates.cjs.map +1 -1
  156. package/dist/templates.d.cts +70 -13
  157. package/dist/templates.d.cts.map +1 -1
  158. package/dist/templates.d.mts +70 -13
  159. package/dist/templates.d.mts.map +1 -1
  160. package/dist/templates.mjs +87 -31
  161. package/dist/templates.mjs.map +1 -1
  162. package/package.json +11 -1
@@ -0,0 +1,63 @@
1
+ import { EmailTemplate } from "./templates.mjs";
2
+ //#region src/email.d.ts
3
+ /**
4
+ * Email metadata: who a template's email comes from, where replies go, and the unsubscribe link
5
+ * its layout carries. Each `emailTemplate` declares its own `from` (and `replyTo`, if replies should
6
+ * go elsewhere); the unsubscribe link is the template's `unsubscribeUrl`, else the config's
7
+ * `email.unsubscribeUrl`, else none. `resolveEmail` is that rule, and the sender, the renderer,
8
+ * the Worker's preview and the studio all go through it. No React and no I/O here, so a browser
9
+ * can import it.
10
+ */
11
+ /** An address as the transports take it: bare, or with a display name. */
12
+ export type EmailAddress = string | {
13
+ email: string;
14
+ name?: string;
15
+ };
16
+ /** `Name <email>`, or the bare address. */
17
+ export declare function formatEmailAddress(address: EmailAddress): string;
18
+ /** The config's `email`: what every email template shares. */
19
+ export interface EmailConfig {
20
+ /**
21
+ * The unsubscribe link of every email template that names none of its own. The layout reads it
22
+ * with `useEmailContext()`; sent, it is tracked like every link, but following it shows a
23
+ * confirmation page, and only the confirmation (a POST no link scanner sends) unsubscribes.
24
+ */
25
+ unsubscribeUrl?: string;
26
+ /** How the Worker tells a person's opens and clicks from a machine's (see the reporting classifier). */
27
+ tracking?: {
28
+ /**
29
+ * An open or click this many milliseconds after delivery is a weak machine signal — machine
30
+ * only together with another (a second link within seconds); 1000 by default. A person
31
+ * tapping a mail notification can click within a few seconds, so keep it short.
32
+ */
33
+ quickClickMs?: number;
34
+ };
35
+ }
36
+ /** One template's email metadata, resolved against the config. */
37
+ export interface ResolvedEmail {
38
+ from: EmailAddress;
39
+ /** Absent when the template names none: the email goes out without a Reply-To. */
40
+ replyTo?: EmailAddress;
41
+ /** Absent when neither the template nor the config names one: the email has no unsubscribe link. */
42
+ unsubscribeUrl?: string;
43
+ }
44
+ /** The template's metadata, its `unsubscribeUrl` falling back to the config's. Leaves both untouched. */
45
+ export declare function resolveEmail(template: Pick<EmailTemplate, 'from' | 'replyTo' | 'unsubscribeUrl'>, config?: EmailConfig): ResolvedEmail;
46
+ /** Throws naming `where` when `address` is neither `user@host` nor `{ email: 'user@host', name? }`. */
47
+ export declare function assertEmailAddress(address: unknown, where: string): void;
48
+ /** Throws naming `where` unless `url` is an absolute http(s) URL; '' and null are not a way to leave it out. */
49
+ export declare function assertUnsubscribeUrl(url: unknown, where: string): void;
50
+ /** The config's `email` and every registered template's metadata; throws on the first invalid value. */
51
+ export declare function assertEmailConfig(config: {
52
+ email?: EmailConfig;
53
+ }, templates: Iterable<EmailTemplate>): void;
54
+ /** A template's own `from` / `replyTo` / `unsubscribeUrl`, as `emailTemplate` checks them. */
55
+ export declare function assertEmailMetadata(template: {
56
+ from?: unknown;
57
+ replyTo?: unknown;
58
+ unsubscribeUrl?: unknown;
59
+ }, where: string): void;
60
+ /** The unsubscribe link may carry its own query (a token, a list): the path names it. */
61
+ export declare function isUnsubscribeLink(url: string, unsubscribeUrl: string | undefined): boolean;
62
+ //#endregion
63
+ //# sourceMappingURL=email.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.d.mts","names":[],"sources":["../src/email.ts"],"mappings":";;;;;;;;;;;YAYY;EAA0B;EAAe;;;wBAGrC,mBAAmB,SAAS;;iBAM3B;;;;;;EAMf;;EAEA;;;;;;IAME;;;;iBAKa;EACf,MAAM;;EAEN,UAAU;;EAEV;;;wBAIc,aACd,UAAU,KAAK,uDACf,SAAS,cACR;;wBAYa,mBAAmB,kBAAkB;;wBAkBrC,qBAAqB,cAAc;;wBAanC,kBACd;EAAU,QAAQ;GAClB,WAAW,SAAS;;wBA0BN,oBACd;EAAY;EAAgB;EAAmB;GAC/C;;wBAUc,kBAAkB,aAAa"}
package/dist/email.mjs ADDED
@@ -0,0 +1,65 @@
1
+ //#region src/email.ts
2
+ /** `Name <email>`, or the bare address. */
3
+ function formatEmailAddress(address) {
4
+ if (typeof address === "string") return address;
5
+ return address.name === void 0 ? address.email : `${address.name} <${address.email}>`;
6
+ }
7
+ /** The template's metadata, its `unsubscribeUrl` falling back to the config's. Leaves both untouched. */
8
+ function resolveEmail(template, config) {
9
+ const unsubscribeUrl = template.unsubscribeUrl ?? config?.unsubscribeUrl;
10
+ return {
11
+ from: template.from,
12
+ ...template.replyTo === void 0 ? {} : { replyTo: template.replyTo },
13
+ ...unsubscribeUrl === void 0 ? {} : { unsubscribeUrl }
14
+ };
15
+ }
16
+ const ADDRESS = /^[^\s@<>]+@[^\s@<>]+$/;
17
+ /** Throws naming `where` when `address` is neither `user@host` nor `{ email: 'user@host', name? }`. */
18
+ function assertEmailAddress(address, where) {
19
+ const email = typeof address === "object" && address !== null ? address.email : address;
20
+ const name = typeof address === "object" && address !== null ? address.name : void 0;
21
+ if (typeof email !== "string" || !ADDRESS.test(email)) throw new TypeError(`${where} must be an email address or { email, name? }`);
22
+ if (name !== void 0 && typeof name !== "string") throw new TypeError(`${where}.name must be a string`);
23
+ }
24
+ /** Throws naming `where` unless `url` is an absolute http(s) URL; '' and null are not a way to leave it out. */
25
+ function assertUnsubscribeUrl(url, where) {
26
+ let parsed;
27
+ try {
28
+ parsed = typeof url === "string" ? new URL(url) : void 0;
29
+ } catch {
30
+ parsed = void 0;
31
+ }
32
+ if (parsed === void 0 || parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new TypeError(`${where} must be an http(s) URL (leave it out for none)`);
33
+ }
34
+ /** The config's `email` and every registered template's metadata; throws on the first invalid value. */
35
+ function assertEmailConfig(config, templates) {
36
+ if (Object.hasOwn(config, "emails")) throw new TypeError("the config's `emails` is gone: `from` and `replyTo` are declared on each emailTemplate, and the rest moved to `email: { unsubscribeUrl, tracking }`");
37
+ const email = config.email;
38
+ if (email !== void 0) {
39
+ if (email.unsubscribeUrl !== void 0) assertUnsubscribeUrl(email.unsubscribeUrl, "email.unsubscribeUrl");
40
+ const quick = email.tracking?.quickClickMs;
41
+ if (quick !== void 0 && !(typeof quick === "number" && Number.isFinite(quick) && quick >= 0)) throw new TypeError("email.tracking.quickClickMs must be a non-negative number");
42
+ }
43
+ for (const template of templates) assertEmailMetadata(template, `email template '${template.id}'`);
44
+ }
45
+ /** A template's own `from` / `replyTo` / `unsubscribeUrl`, as `emailTemplate` checks them. */
46
+ function assertEmailMetadata(template, where) {
47
+ assertEmailAddress(template.from, `${where}: from`);
48
+ if (template.replyTo !== void 0) assertEmailAddress(template.replyTo, `${where}: replyTo`);
49
+ if (template.unsubscribeUrl !== void 0) assertUnsubscribeUrl(template.unsubscribeUrl, `${where}: unsubscribeUrl`);
50
+ }
51
+ /** The unsubscribe link may carry its own query (a token, a list): the path names it. */
52
+ function isUnsubscribeLink(url, unsubscribeUrl) {
53
+ if (unsubscribeUrl === void 0) return false;
54
+ try {
55
+ const a = new URL(url);
56
+ const b = new URL(unsubscribeUrl);
57
+ return a.origin === b.origin && a.pathname === b.pathname;
58
+ } catch {
59
+ return false;
60
+ }
61
+ }
62
+ //#endregion
63
+ export { assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, resolveEmail };
64
+
65
+ //# sourceMappingURL=email.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.mjs","names":[],"sources":["../src/email.ts"],"sourcesContent":["import type { EmailTemplate } from './templates';\n\n/**\n * Email metadata: who a template's email comes from, where replies go, and the unsubscribe link\n * its layout carries. Each `emailTemplate` declares its own `from` (and `replyTo`, if replies should\n * go elsewhere); the unsubscribe link is the template's `unsubscribeUrl`, else the config's\n * `email.unsubscribeUrl`, else none. `resolveEmail` is that rule, and the sender, the renderer,\n * the Worker's preview and the studio all go through it. No React and no I/O here, so a browser\n * can import it.\n */\n\n/** An address as the transports take it: bare, or with a display name. */\nexport type EmailAddress = string | { email: string; name?: string };\n\n/** `Name <email>`, or the bare address. */\nexport function formatEmailAddress(address: EmailAddress): string {\n if (typeof address === 'string') return address;\n return address.name === undefined ? address.email : `${address.name} <${address.email}>`;\n}\n\n/** The config's `email`: what every email template shares. */\nexport interface EmailConfig {\n /**\n * The unsubscribe link of every email template that names none of its own. The layout reads it\n * with `useEmailContext()`; sent, it is tracked like every link, but following it shows a\n * confirmation page, and only the confirmation (a POST no link scanner sends) unsubscribes.\n */\n unsubscribeUrl?: string;\n /** How the Worker tells a person's opens and clicks from a machine's (see the reporting classifier). */\n tracking?: {\n /**\n * An open or click this many milliseconds after delivery is a weak machine signal — machine\n * only together with another (a second link within seconds); 1000 by default. A person\n * tapping a mail notification can click within a few seconds, so keep it short.\n */\n quickClickMs?: number;\n };\n}\n\n/** One template's email metadata, resolved against the config. */\nexport interface ResolvedEmail {\n from: EmailAddress;\n /** Absent when the template names none: the email goes out without a Reply-To. */\n replyTo?: EmailAddress;\n /** Absent when neither the template nor the config names one: the email has no unsubscribe link. */\n unsubscribeUrl?: string;\n}\n\n/** The template's metadata, its `unsubscribeUrl` falling back to the config's. Leaves both untouched. */\nexport function resolveEmail(\n template: Pick<EmailTemplate, 'from' | 'replyTo' | 'unsubscribeUrl'>,\n config?: EmailConfig\n): ResolvedEmail {\n const unsubscribeUrl = template.unsubscribeUrl ?? config?.unsubscribeUrl;\n return {\n from: template.from,\n ...(template.replyTo === undefined ? {} : { replyTo: template.replyTo }),\n ...(unsubscribeUrl === undefined ? {} : { unsubscribeUrl }),\n };\n}\n\nconst ADDRESS = /^[^\\s@<>]+@[^\\s@<>]+$/;\n\n/** Throws naming `where` when `address` is neither `user@host` nor `{ email: 'user@host', name? }`. */\nexport function assertEmailAddress(address: unknown, where: string): void {\n const email =\n typeof address === 'object' && address !== null\n ? (address as { email?: unknown }).email\n : address;\n const name =\n typeof address === 'object' && address !== null\n ? (address as { name?: unknown }).name\n : undefined;\n if (typeof email !== 'string' || !ADDRESS.test(email)) {\n throw new TypeError(`${where} must be an email address or { email, name? }`);\n }\n if (name !== undefined && typeof name !== 'string') {\n throw new TypeError(`${where}.name must be a string`);\n }\n}\n\n/** Throws naming `where` unless `url` is an absolute http(s) URL; '' and null are not a way to leave it out. */\nexport function assertUnsubscribeUrl(url: unknown, where: string): void {\n let parsed: URL | undefined;\n try {\n parsed = typeof url === 'string' ? new URL(url) : undefined;\n } catch {\n parsed = undefined;\n }\n if (parsed === undefined || (parsed.protocol !== 'https:' && parsed.protocol !== 'http:')) {\n throw new TypeError(`${where} must be an http(s) URL (leave it out for none)`);\n }\n}\n\n/** The config's `email` and every registered template's metadata; throws on the first invalid value. */\nexport function assertEmailConfig(\n config: { email?: EmailConfig },\n templates: Iterable<EmailTemplate>\n): void {\n if (Object.hasOwn(config, 'emails')) {\n throw new TypeError(\n \"the config's `emails` is gone: `from` and `replyTo` are declared on each emailTemplate, \" +\n 'and the rest moved to `email: { unsubscribeUrl, tracking }`'\n );\n }\n const email = config.email;\n if (email !== undefined) {\n if (email.unsubscribeUrl !== undefined) {\n assertUnsubscribeUrl(email.unsubscribeUrl, 'email.unsubscribeUrl');\n }\n const quick = email.tracking?.quickClickMs;\n if (\n quick !== undefined &&\n !(typeof quick === 'number' && Number.isFinite(quick) && quick >= 0)\n ) {\n throw new TypeError('email.tracking.quickClickMs must be a non-negative number');\n }\n }\n for (const template of templates)\n assertEmailMetadata(template, `email template '${template.id}'`);\n}\n\n/** A template's own `from` / `replyTo` / `unsubscribeUrl`, as `emailTemplate` checks them. */\nexport function assertEmailMetadata(\n template: { from?: unknown; replyTo?: unknown; unsubscribeUrl?: unknown },\n where: string\n): void {\n assertEmailAddress(template.from, `${where}: from`);\n if (template.replyTo !== undefined) assertEmailAddress(template.replyTo, `${where}: replyTo`);\n if (template.unsubscribeUrl !== undefined) {\n assertUnsubscribeUrl(template.unsubscribeUrl, `${where}: unsubscribeUrl`);\n }\n}\n\n/** The unsubscribe link may carry its own query (a token, a list): the path names it. */\nexport function isUnsubscribeLink(url: string, unsubscribeUrl: string | undefined): boolean {\n if (unsubscribeUrl === undefined) return false;\n try {\n const a = new URL(url);\n const b = new URL(unsubscribeUrl);\n return a.origin === b.origin && a.pathname === b.pathname;\n } catch {\n return false;\n }\n}\n"],"mappings":";;AAeA,SAAgB,mBAAmB,SAA+B;CAChE,IAAI,OAAO,YAAY,UAAU,OAAO;CACxC,OAAO,QAAQ,SAAS,KAAA,IAAY,QAAQ,QAAQ,GAAG,QAAQ,KAAK,IAAI,QAAQ,MAAM;AACxF;;AA+BA,SAAgB,aACd,UACA,QACe;CACf,MAAM,iBAAiB,SAAS,kBAAkB,QAAQ;CAC1D,OAAO;EACL,MAAM,SAAS;EACf,GAAI,SAAS,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS,SAAS,QAAQ;EACtE,GAAI,mBAAmB,KAAA,IAAY,CAAC,IAAI,EAAE,eAAe;CAC3D;AACF;AAEA,MAAM,UAAU;;AAGhB,SAAgB,mBAAmB,SAAkB,OAAqB;CACxE,MAAM,QACJ,OAAO,YAAY,YAAY,YAAY,OACtC,QAAgC,QACjC;CACN,MAAM,OACJ,OAAO,YAAY,YAAY,YAAY,OACtC,QAA+B,OAChC,KAAA;CACN,IAAI,OAAO,UAAU,YAAY,CAAC,QAAQ,KAAK,KAAK,GAClD,MAAM,IAAI,UAAU,GAAG,MAAM,8CAA8C;CAE7E,IAAI,SAAS,KAAA,KAAa,OAAO,SAAS,UACxC,MAAM,IAAI,UAAU,GAAG,MAAM,uBAAuB;AAExD;;AAGA,SAAgB,qBAAqB,KAAc,OAAqB;CACtE,IAAI;CACJ,IAAI;EACF,SAAS,OAAO,QAAQ,WAAW,IAAI,IAAI,GAAG,IAAI,KAAA;CACpD,QAAQ;EACN,SAAS,KAAA;CACX;CACA,IAAI,WAAW,KAAA,KAAc,OAAO,aAAa,YAAY,OAAO,aAAa,SAC/E,MAAM,IAAI,UAAU,GAAG,MAAM,gDAAgD;AAEjF;;AAGA,SAAgB,kBACd,QACA,WACM;CACN,IAAI,OAAO,OAAO,QAAQ,QAAQ,GAChC,MAAM,IAAI,UACR,qJAEF;CAEF,MAAM,QAAQ,OAAO;CACrB,IAAI,UAAU,KAAA,GAAW;EACvB,IAAI,MAAM,mBAAmB,KAAA,GAC3B,qBAAqB,MAAM,gBAAgB,sBAAsB;EAEnE,MAAM,QAAQ,MAAM,UAAU;EAC9B,IACE,UAAU,KAAA,KACV,EAAE,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,SAAS,IAElE,MAAM,IAAI,UAAU,2DAA2D;CAEnF;CACA,KAAK,MAAM,YAAY,WACrB,oBAAoB,UAAU,mBAAmB,SAAS,GAAG,EAAE;AACnE;;AAGA,SAAgB,oBACd,UACA,OACM;CACN,mBAAmB,SAAS,MAAM,GAAG,MAAM,OAAO;CAClD,IAAI,SAAS,YAAY,KAAA,GAAW,mBAAmB,SAAS,SAAS,GAAG,MAAM,UAAU;CAC5F,IAAI,SAAS,mBAAmB,KAAA,GAC9B,qBAAqB,SAAS,gBAAgB,GAAG,MAAM,iBAAiB;AAE5E;;AAGA,SAAgB,kBAAkB,KAAa,gBAA6C;CAC1F,IAAI,mBAAmB,KAAA,GAAW,OAAO;CACzC,IAAI;EACF,MAAM,IAAI,IAAI,IAAI,GAAG;EACrB,MAAM,IAAI,IAAI,IAAI,cAAc;EAChC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE;CACnD,QAAQ;EACN,OAAO;CACT;AACF"}
@@ -1,8 +1,8 @@
1
- import { ActionInvocation, ActionInvoker, EngineStep, EventSink, FactSource, JourneyContext, JourneyObserver, JourneyOutcome, MessageSender, MessageTracking, OutboundMessage, PushDevice, SendResult } from "./ports.cjs";
1
+ import { ActionInvocation, ActionInvoker, EngineStep, EventSink, FactSource, JourneyContext, JourneyObserver, JourneyOutcome, LinkPurpose, MessageSender, MessageTracking, OutboundMessage, PushDevice, SendResult } from "./ports.cjs";
2
2
  import { ActionVersionPolicy, RegisteredAction, RegistryActionInvoker } from "./actions.cjs";
3
3
  import { hashToBucket, murmur3 } from "./bucket.cjs";
4
4
  import { DEVICES_UPDATED_EVENT, EvaluateOptions, PROFILE_UPDATED_EVENT, evaluateCondition, matchesWhere, relevantEvents } from "./condition.cjs";
5
5
  import { runJourney } from "./interpreter.cjs";
6
6
  import { scaleDurations, scaleSegmentWindows } from "./time-scale.cjs";
7
7
  import { nextWindowStart, resolveTimeZone } from "./time-window.cjs";
8
- export { type ActionInvocation, type ActionInvoker, type ActionVersionPolicy, DEVICES_UPDATED_EVENT, type EngineStep, type EvaluateOptions, type EventSink, type FactSource, type JourneyContext, type JourneyObserver, type JourneyOutcome, type MessageSender, type MessageTracking, type OutboundMessage, PROFILE_UPDATED_EVENT, type PushDevice, type RegisteredAction, RegistryActionInvoker, type SendResult, evaluateCondition, hashToBucket, matchesWhere, murmur3, nextWindowStart, relevantEvents, resolveTimeZone, runJourney, scaleDurations, scaleSegmentWindows };
8
+ export { type ActionInvocation, type ActionInvoker, type ActionVersionPolicy, DEVICES_UPDATED_EVENT, type EngineStep, type EvaluateOptions, type EventSink, type FactSource, type JourneyContext, type JourneyObserver, type JourneyOutcome, type LinkPurpose, type MessageSender, type MessageTracking, type OutboundMessage, PROFILE_UPDATED_EVENT, type PushDevice, type RegisteredAction, RegistryActionInvoker, type SendResult, evaluateCondition, hashToBucket, matchesWhere, murmur3, nextWindowStart, relevantEvents, resolveTimeZone, runJourney, scaleDurations, scaleSegmentWindows };
@@ -1,8 +1,8 @@
1
- import { ActionInvocation, ActionInvoker, EngineStep, EventSink, FactSource, JourneyContext, JourneyObserver, JourneyOutcome, MessageSender, MessageTracking, OutboundMessage, PushDevice, SendResult } from "./ports.mjs";
1
+ import { ActionInvocation, ActionInvoker, EngineStep, EventSink, FactSource, JourneyContext, JourneyObserver, JourneyOutcome, LinkPurpose, MessageSender, MessageTracking, OutboundMessage, PushDevice, SendResult } from "./ports.mjs";
2
2
  import { ActionVersionPolicy, RegisteredAction, RegistryActionInvoker } from "./actions.mjs";
3
3
  import { hashToBucket, murmur3 } from "./bucket.mjs";
4
4
  import { DEVICES_UPDATED_EVENT, EvaluateOptions, PROFILE_UPDATED_EVENT, evaluateCondition, matchesWhere, relevantEvents } from "./condition.mjs";
5
5
  import { runJourney } from "./interpreter.mjs";
6
6
  import { scaleDurations, scaleSegmentWindows } from "./time-scale.mjs";
7
7
  import { nextWindowStart, resolveTimeZone } from "./time-window.mjs";
8
- export { type ActionInvocation, type ActionInvoker, type ActionVersionPolicy, DEVICES_UPDATED_EVENT, type EngineStep, type EvaluateOptions, type EventSink, type FactSource, type JourneyContext, type JourneyObserver, type JourneyOutcome, type MessageSender, type MessageTracking, type OutboundMessage, PROFILE_UPDATED_EVENT, type PushDevice, type RegisteredAction, RegistryActionInvoker, type SendResult, evaluateCondition, hashToBucket, matchesWhere, murmur3, nextWindowStart, relevantEvents, resolveTimeZone, runJourney, scaleDurations, scaleSegmentWindows };
8
+ export { type ActionInvocation, type ActionInvoker, type ActionVersionPolicy, DEVICES_UPDATED_EVENT, type EngineStep, type EvaluateOptions, type EventSink, type FactSource, type JourneyContext, type JourneyObserver, type JourneyOutcome, type LinkPurpose, type MessageSender, type MessageTracking, type OutboundMessage, PROFILE_UPDATED_EVENT, type PushDevice, type RegisteredAction, RegistryActionInvoker, type SendResult, evaluateCondition, hashToBucket, matchesWhere, murmur3, nextWindowStart, relevantEvents, resolveTimeZone, runJourney, scaleDurations, scaleSegmentWindows };
@@ -93,14 +93,19 @@ export interface OutboundMessage {
93
93
  /** What the host's observer asked the sender to add for measuring this copy (see `MessageTracking`). */
94
94
  tracking?: MessageTracking | undefined;
95
95
  }
96
+ /** What following a tracked email link does: redirect to it, or confirm an unsubscribe. */
97
+ export type LinkPurpose = 'link' | 'unsubscribe';
96
98
  /**
97
99
  * Measuring a copy, as the built-in senders apply it: an email's links are routed through
98
100
  * `link` and `pixel` is appended as a 1×1 image; a push carries `data` in its payload.
99
101
  * Each is optional, and a sender ignores what its channel has no use for.
100
102
  */
101
103
  export interface MessageTracking {
102
- /** The original `http(s)` URL in, the URL that records the click and redirects out. */
103
- link?: (url: string) => string;
104
+ /**
105
+ * The original `http(s)` URL in, the URL that records the click and redirects out — or, for the
106
+ * email's unsubscribe link, asks the recipient to confirm.
107
+ */
108
+ link?: (url: string, purpose: LinkPurpose) => string;
104
109
  /** Loaded when the email is opened. */
105
110
  pixel?: string;
106
111
  /** Extra string keys in the push payload. */
@@ -1 +1 @@
1
- {"version":3,"file":"ports.d.cts","names":[],"sources":["../../src/engine/ports.ts"],"mappings":";;;;;;;;;;;;;;;;iBAiBiB;;EAEf,GAAG,GAAG,cAAc,UAAU,QAAQ,KAAK,QAAQ;;EAGnD,MAAM,cAAc,aAAa;;EAGjC,WAAW,cAAc,sBAAsB;;;;;;;;;;;;EAa/C,UAAU,cAAc,4BAA4B;;EAGpD,YAAY,eAAe;;;;;EAM3B,YAAY,cAAc,oBAAoB;;;iBAI/B;EACf;EACA;;;iBAIe;;;;;;EAMf,YACE,eACA;IAAS;IAAkB;IAAkB,QAAQ;MACpD;EACH,YAAY,eAAe,QAAQ;;EAEnC,oBAAoB,eAAe,QAAQ;;EAE3C,eAAe,QAAQ;;EAEvB,cAAc,SAAS,YAAY;;iBAGpB;;EAEf;EACA;EACA;EACA,SAAS;EACT;;EAEA,OAAO,eAAe;EACtB;;;;;;EAMA;;EAEA,SAAS;;;;;;EAMT;;EAEA,WAAW;;;;;;;iBAQI;;EAEf,QAAQ;;EAER;;EAEA,OAAO;;;;;;EAMP,eAAe;;;;;;;iBAQA;EACf;EACA;;iBAGe;EACf,KAAK,SAAS,kBAAkB,eAAe;;;;;;;;;iBAUhC;;EAEf,SAAS;IACP;IACA;IACA;IACA;MACE;;EAEJ,aAAa;IACX;IACA;IACA;IACA;IACA;MACE;;EAEJ,iBAAiB,SAAS,kBAAkB,QAAQ;;EAEpD,aAAa,SAAS,iBAAiB;IAAU;MAAoB,aAAa;;EAElF,UAAU;IACR;IACA;IACA;IACA,QAAQ;IACR;;IAEA;MACE;;;;;;;;;iBAUW;EACf,SAAS;IACP,SAAS;IACT;IACA,OAAO,eAAe;MACpB,eAAe;;;iBAIJ;;;;;;;;EAQf,KAAK,eAAe,SAAS,eAAe,uBAAuB,aAAa;;;iBAIjE;;EAEf;;EAEA;;EAEA,MAAM,eAAe;EACrB;;EAEA;;;;;;;iBAQe;EACf,OAAO,YAAY,mBAAmB;;iBAGvB;EACf;;EAEA;;;;;;;EAOA;EACA,MAAM;EACN,OAAO;EACP,UAAU;EACV,QAAQ;;EAER,UAAU;;EAEV,YAAY;;EAEZ,WAAW;;;;;;;YAQD;EACN;;EACA;EAAkB;;EAClB;;EACA;;EACA"}
1
+ {"version":3,"file":"ports.d.cts","names":[],"sources":["../../src/engine/ports.ts"],"mappings":";;;;;;;;;;;;;;;;iBAiBiB;;EAEf,GAAG,GAAG,cAAc,UAAU,QAAQ,KAAK,QAAQ;;EAGnD,MAAM,cAAc,aAAa;;EAGjC,WAAW,cAAc,sBAAsB;;;;;;;;;;;;EAa/C,UAAU,cAAc,4BAA4B;;EAGpD,YAAY,eAAe;;;;;EAM3B,YAAY,cAAc,oBAAoB;;;iBAI/B;EACf;EACA;;;iBAIe;;;;;;EAMf,YACE,eACA;IAAS;IAAkB;IAAkB,QAAQ;MACpD;EACH,YAAY,eAAe,QAAQ;;EAEnC,oBAAoB,eAAe,QAAQ;;EAE3C,eAAe,QAAQ;;EAEvB,cAAc,SAAS,YAAY;;iBAGpB;;EAEf;EACA;EACA;EACA,SAAS;EACT;;EAEA,OAAO,eAAe;EACtB;;;;;;EAMA;;EAEA,SAAS;;;;;;EAMT;;EAEA,WAAW;;;YAID;;;;;;iBAOK;;;;;EAKf,QAAQ,aAAa,SAAS;;EAE9B;;EAEA,OAAO;;;;;;EAMP,eAAe;;;;;;;iBAQA;EACf;EACA;;iBAGe;EACf,KAAK,SAAS,kBAAkB,eAAe;;;;;;;;;iBAUhC;;EAEf,SAAS;IACP;IACA;IACA;IACA;MACE;;EAEJ,aAAa;IACX;IACA;IACA;IACA;IACA;MACE;;EAEJ,iBAAiB,SAAS,kBAAkB,QAAQ;;EAEpD,aAAa,SAAS,iBAAiB;IAAU;MAAoB,aAAa;;EAElF,UAAU;IACR;IACA;IACA;IACA,QAAQ;IACR;;IAEA;MACE;;;;;;;;;iBAUW;EACf,SAAS;IACP,SAAS;IACT;IACA,OAAO,eAAe;MACpB,eAAe;;;iBAIJ;;;;;;;;EAQf,KAAK,eAAe,SAAS,eAAe,uBAAuB,aAAa;;;iBAIjE;;EAEf;;EAEA;;EAEA,MAAM,eAAe;EACrB;;EAEA;;;;;;;iBAQe;EACf,OAAO,YAAY,mBAAmB;;iBAGvB;EACf;;EAEA;;;;;;;EAOA;EACA,MAAM;EACN,OAAO;EACP,UAAU;EACV,QAAQ;;EAER,UAAU;;EAEV,YAAY;;EAEZ,WAAW;;;;;;;YAQD;EACN;;EACA;EAAkB;;EAClB;;EACA;;EACA"}
@@ -93,14 +93,19 @@ export interface OutboundMessage {
93
93
  /** What the host's observer asked the sender to add for measuring this copy (see `MessageTracking`). */
94
94
  tracking?: MessageTracking | undefined;
95
95
  }
96
+ /** What following a tracked email link does: redirect to it, or confirm an unsubscribe. */
97
+ export type LinkPurpose = 'link' | 'unsubscribe';
96
98
  /**
97
99
  * Measuring a copy, as the built-in senders apply it: an email's links are routed through
98
100
  * `link` and `pixel` is appended as a 1×1 image; a push carries `data` in its payload.
99
101
  * Each is optional, and a sender ignores what its channel has no use for.
100
102
  */
101
103
  export interface MessageTracking {
102
- /** The original `http(s)` URL in, the URL that records the click and redirects out. */
103
- link?: (url: string) => string;
104
+ /**
105
+ * The original `http(s)` URL in, the URL that records the click and redirects out — or, for the
106
+ * email's unsubscribe link, asks the recipient to confirm.
107
+ */
108
+ link?: (url: string, purpose: LinkPurpose) => string;
104
109
  /** Loaded when the email is opened. */
105
110
  pixel?: string;
106
111
  /** Extra string keys in the push payload. */
@@ -1 +1 @@
1
- {"version":3,"file":"ports.d.mts","names":[],"sources":["../../src/engine/ports.ts"],"mappings":";;;;;;;;;;;;;;;;iBAiBiB;;EAEf,GAAG,GAAG,cAAc,UAAU,QAAQ,KAAK,QAAQ;;EAGnD,MAAM,cAAc,aAAa;;EAGjC,WAAW,cAAc,sBAAsB;;;;;;;;;;;;EAa/C,UAAU,cAAc,4BAA4B;;EAGpD,YAAY,eAAe;;;;;EAM3B,YAAY,cAAc,oBAAoB;;;iBAI/B;EACf;EACA;;;iBAIe;;;;;;EAMf,YACE,eACA;IAAS;IAAkB;IAAkB,QAAQ;MACpD;EACH,YAAY,eAAe,QAAQ;;EAEnC,oBAAoB,eAAe,QAAQ;;EAE3C,eAAe,QAAQ;;EAEvB,cAAc,SAAS,YAAY;;iBAGpB;;EAEf;EACA;EACA;EACA,SAAS;EACT;;EAEA,OAAO,eAAe;EACtB;;;;;;EAMA;;EAEA,SAAS;;;;;;EAMT;;EAEA,WAAW;;;;;;;iBAQI;;EAEf,QAAQ;;EAER;;EAEA,OAAO;;;;;;EAMP,eAAe;;;;;;;iBAQA;EACf;EACA;;iBAGe;EACf,KAAK,SAAS,kBAAkB,eAAe;;;;;;;;;iBAUhC;;EAEf,SAAS;IACP;IACA;IACA;IACA;MACE;;EAEJ,aAAa;IACX;IACA;IACA;IACA;IACA;MACE;;EAEJ,iBAAiB,SAAS,kBAAkB,QAAQ;;EAEpD,aAAa,SAAS,iBAAiB;IAAU;MAAoB,aAAa;;EAElF,UAAU;IACR;IACA;IACA;IACA,QAAQ;IACR;;IAEA;MACE;;;;;;;;;iBAUW;EACf,SAAS;IACP,SAAS;IACT;IACA,OAAO,eAAe;MACpB,eAAe;;;iBAIJ;;;;;;;;EAQf,KAAK,eAAe,SAAS,eAAe,uBAAuB,aAAa;;;iBAIjE;;EAEf;;EAEA;;EAEA,MAAM,eAAe;EACrB;;EAEA;;;;;;;iBAQe;EACf,OAAO,YAAY,mBAAmB;;iBAGvB;EACf;;EAEA;;;;;;;EAOA;EACA,MAAM;EACN,OAAO;EACP,UAAU;EACV,QAAQ;;EAER,UAAU;;EAEV,YAAY;;EAEZ,WAAW;;;;;;;YAQD;EACN;;EACA;EAAkB;;EAClB;;EACA;;EACA"}
1
+ {"version":3,"file":"ports.d.mts","names":[],"sources":["../../src/engine/ports.ts"],"mappings":";;;;;;;;;;;;;;;;iBAiBiB;;EAEf,GAAG,GAAG,cAAc,UAAU,QAAQ,KAAK,QAAQ;;EAGnD,MAAM,cAAc,aAAa;;EAGjC,WAAW,cAAc,sBAAsB;;;;;;;;;;;;EAa/C,UAAU,cAAc,4BAA4B;;EAGpD,YAAY,eAAe;;;;;EAM3B,YAAY,cAAc,oBAAoB;;;iBAI/B;EACf;EACA;;;iBAIe;;;;;;EAMf,YACE,eACA;IAAS;IAAkB;IAAkB,QAAQ;MACpD;EACH,YAAY,eAAe,QAAQ;;EAEnC,oBAAoB,eAAe,QAAQ;;EAE3C,eAAe,QAAQ;;EAEvB,cAAc,SAAS,YAAY;;iBAGpB;;EAEf;EACA;EACA;EACA,SAAS;EACT;;EAEA,OAAO,eAAe;EACtB;;;;;;EAMA;;EAEA,SAAS;;;;;;EAMT;;EAEA,WAAW;;;YAID;;;;;;iBAOK;;;;;EAKf,QAAQ,aAAa,SAAS;;EAE9B;;EAEA,OAAO;;;;;;EAMP,eAAe;;;;;;;iBAQA;EACf;EACA;;iBAGe;EACf,KAAK,SAAS,kBAAkB,eAAe;;;;;;;;;iBAUhC;;EAEf,SAAS;IACP;IACA;IACA;IACA;MACE;;EAEJ,aAAa;IACX;IACA;IACA;IACA;IACA;MACE;;EAEJ,iBAAiB,SAAS,kBAAkB,QAAQ;;EAEpD,aAAa,SAAS,iBAAiB;IAAU;MAAoB,aAAa;;EAElF,UAAU;IACR;IACA;IACA;IACA,QAAQ;IACR;;IAEA;MACE;;;;;;;;;iBAUW;EACf,SAAS;IACP,SAAS;IACT;IACA,OAAO,eAAe;MACpB,eAAe;;;iBAIJ;;;;;;;;EAQf,KAAK,eAAe,SAAS,eAAe,uBAAuB,aAAa;;;iBAIjE;;EAEf;;EAEA;;EAEA,MAAM,eAAe;EACrB;;EAEA;;;;;;;iBAQe;EACf,OAAO,YAAY,mBAAmB;;iBAGvB;EACf;;EAEA;;;;;;;EAOA;EACA,MAAM;EACN,OAAO;EACP,UAAU;EACV,QAAQ;;EAER,UAAU;;EAEV,YAAY;;EAEZ,WAAW;;;;;;;YAQD;EACN;;EACA;EAAkB;;EAClB;;EACA;;EACA"}
package/dist/index.cjs CHANGED
@@ -2,9 +2,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_hash = require("./hash.cjs");
3
3
  const require_dsl_action = require("./dsl/action.cjs");
4
4
  const require_dsl_refs = require("./dsl/refs.cjs");
5
- const require_templates = require("./templates.cjs");
6
5
  const require_dsl_template = require("./dsl/template.cjs");
7
6
  const require_dsl_condition = require("./dsl/condition.cjs");
7
+ const require_email = require("./email.cjs");
8
+ const require_templates = require("./templates.cjs");
8
9
  const require_dsl_flow = require("./dsl/flow.cjs");
9
10
  const require_dsl_trigger = require("./dsl/trigger.cjs");
10
11
  const require_ir = require("./ir.cjs");
@@ -17,6 +18,7 @@ const require_plan = require("./plan.cjs");
17
18
  const require_runtime = require("./runtime.cjs");
18
19
  exports.ActionIR = require_ir.ActionIR;
19
20
  exports.BundleIR = require_ir.BundleIR;
21
+ exports.CHANNEL_OF = require_templates.CHANNEL_OF;
20
22
  exports.ChannelIR = require_ir.ChannelIR;
21
23
  exports.ConditionIR = require_ir.ConditionIR;
22
24
  exports.ConfigCompileError = require_config.ConfigCompileError;
@@ -41,7 +43,11 @@ exports.WorkflowIR = require_ir.WorkflowIR;
41
43
  exports.action = require_dsl_action.action;
42
44
  exports.and = require_dsl_condition.and;
43
45
  exports.apnsEnvironment = require_config.apnsEnvironment;
46
+ exports.assertEmailAddress = require_email.assertEmailAddress;
47
+ exports.assertEmailConfig = require_email.assertEmailConfig;
48
+ exports.assertEmailMetadata = require_email.assertEmailMetadata;
44
49
  exports.assertSupportedSchema = require_templates.assertSupportedSchema;
50
+ exports.assertUnsubscribeUrl = require_email.assertUnsubscribeUrl;
45
51
  exports.between = require_dsl_condition.between;
46
52
  exports.canonicalJSON = require_hash.canonicalJSON;
47
53
  exports.checkEvent = require_contract.checkEvent;
@@ -56,11 +62,12 @@ exports.eq = require_dsl_condition.eq;
56
62
  exports.event = require_dsl_refs.event;
57
63
  exports.exists = require_dsl_condition.exists;
58
64
  exports.flow = require_dsl_flow.flow;
59
- exports.formatEmailAddress = require_config.formatEmailAddress;
65
+ exports.formatEmailAddress = require_email.formatEmailAddress;
60
66
  exports.fullHash = require_hash.fullHash;
61
67
  exports.gt = require_dsl_condition.gt;
62
68
  exports.gte = require_dsl_condition.gte;
63
69
  exports.inArray = require_dsl_condition.inArray;
70
+ exports.indexTemplates = require_templates.indexTemplates;
64
71
  exports.isConfig = require_config.isConfig;
65
72
  exports.isContract = require_contract.isContract;
66
73
  exports.isDiscordTemplate = require_templates.isDiscordTemplate;
@@ -68,6 +75,9 @@ exports.isEmailTemplate = require_templates.isEmailTemplate;
68
75
  exports.isPushTemplate = require_templates.isPushTemplate;
69
76
  exports.isSemanticChange = require_plan.isSemanticChange;
70
77
  exports.isSlackTemplate = require_templates.isSlackTemplate;
78
+ exports.isTemplate = require_templates.isTemplate;
79
+ exports.isUnsubscribeLink = require_email.isUnsubscribeLink;
80
+ exports.lookupTemplate = require_templates.lookupTemplate;
71
81
  exports.lt = require_dsl_condition.lt;
72
82
  exports.lte = require_dsl_condition.lte;
73
83
  exports.ne = require_dsl_condition.ne;
@@ -86,6 +96,7 @@ exports.registryValidator = require_templates.registryValidator;
86
96
  exports.renderDiscordContent = require_templates.renderDiscordContent;
87
97
  exports.renderPushContent = require_templates.renderPushContent;
88
98
  exports.renderSlackContent = require_templates.renderSlackContent;
99
+ exports.resolveEmail = require_email.resolveEmail;
89
100
  exports.resolveRuntime = require_runtime.resolveRuntime;
90
101
  exports.segment = require_dsl_condition.segment;
91
102
  exports.semanticHash = require_hash.semanticHash;
@@ -94,7 +105,6 @@ exports.slackTemplate = require_templates.slackTemplate;
94
105
  exports.stripMeta = require_hash.stripMeta;
95
106
  exports.stripProvenance = require_hash.stripProvenance;
96
107
  exports.template = require_dsl_template.template;
97
- exports.templates = require_dsl_template.templates;
98
108
  exports.trigger = require_dsl_trigger.trigger;
99
109
  exports.user = require_dsl_refs.user;
100
110
  exports.workflow = require_dsl_workflow.workflow;
package/dist/index.d.cts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR } from "./ir.cjs";
2
- import { AnyTemplate, DiscordContent, DiscordTemplate, EmailTemplate, EmailTemplateDefinition, PropsSchema, PushContent, PushTemplate, PushTemplateDefinition, ResolvedProps, SlackContent, SlackTemplate, TemplateChannel, TemplateIssue, TemplateValidationError, WebhookTemplateDefinition, assertSupportedSchema, discordTemplate, emailTemplate, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate } from "./templates.cjs";
2
+ import { EmailAddress, EmailConfig, ResolvedEmail, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, resolveEmail } from "./email.cjs";
3
+ import { AnyTemplate, CHANNEL_OF, ChannelTemplate, DiscordContent, DiscordTemplate, EmailMetadata, EmailTemplate, EmailTemplateDefinition, PropsSchema, PushContent, PushTemplate, PushTemplateDefinition, ResolvedProps, SlackContent, SlackTemplate, TemplateChannel, TemplateIssue, TemplateRegistry, TemplateValidationError, WebhookTemplateDefinition, assertSupportedSchema, discordTemplate, emailTemplate, indexTemplates, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, isTemplate, lookupTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate } from "./templates.cjs";
3
4
  import { ActionContext, ActionHandler, ActionRef, action } from "./dsl/action.cjs";
4
5
  import { Duration, EventMap, TimeOfDay, UserPropertyBase, Weekday } from "./dsl/base.cjs";
5
6
  import { EventRef, EventRefs, MessageArgs, PayloadRef, PayloadRefNode, PayloadRefs, PropInput, PropsInput, UserPropertyRef, UserRefs, event, user } from "./dsl/refs.cjs";
6
- import { Channel, EmptyProps, PropsOf, TemplateFactory, TemplateRef, TemplateRefTable, TemplateRefs, TemplateRegistry, template, templates } from "./dsl/template.cjs";
7
+ import { Channel, EmptyProps, PropsOf, TemplateFactory, TemplateRef, template } from "./dsl/template.cjs";
7
8
  import { Condition, PayloadWhere, PerformedOptions, SegmentDefinition, SegmentRef, and, between, contains, eq, exists, gt, gte, inArray, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, performed, reachable, segment } from "./dsl/condition.cjs";
8
9
  import { BranchArm, BranchCase, EmitArgs, EmitPayload, Flow, FlowBuilder, PreferOptions, PushOptions, SubFlow, flow } from "./dsl/flow.cjs";
9
10
  import { DateTime, EventTriggerOptions, TriggerFactory, TriggerRef, trigger } from "./dsl/trigger.cjs";
@@ -12,7 +13,7 @@ import { compileBundle } from "./dsl/bundle.cjs";
12
13
  import "./dsl/index.cjs";
13
14
  import { Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DeclaredOutput, EventPayload, ProfilePatch, checkEvent, defineContract, isContract } from "./contract.cjs";
14
15
  import { JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, resolveRuntime } from "./runtime.cjs";
15
- import { ApnsConfig, ConfigCompileError, DatabaseConfig, EmailAddress, EmailConfig, FcmConfig, JourneyConfig, JourneyConfigDefinition, WebPushConfig, apnsEnvironment, compileConfig, defineConfig, formatEmailAddress, isConfig } from "./config.cjs";
16
+ import { ApnsConfig, ConfigCompileError, DatabaseConfig, FcmConfig, JourneyConfig, JourneyConfigDefinition, WebPushConfig, apnsEnvironment, compileConfig, defineConfig, isConfig } from "./config.cjs";
16
17
  import { canonicalJSON, fullHash, semanticHash, sha256Hex, stripMeta, stripProvenance } from "./hash.cjs";
17
18
  import { BundlePlan, ChangeStatus, NodeChange, ResourceChange, WorkflowChange, isSemanticChange, plan } from "./plan.cjs";
18
- export { type ActionContext, type ActionHandler, ActionIR, type ActionRef, AnyTemplate, ApnsConfig, type BranchArm, type BranchCase, BundleIR, BundlePlan, ChangeStatus, type Channel, ChannelIR, type Condition, ConditionIR, ConfigCompileError, Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DatabaseConfig, type DateTime, DeclaredOutput, DiscordContent, DiscordTemplate, type Duration, DurationIR, EmailAddress, EmailConfig, EmailTemplate, EmailTemplateDefinition, type EmitArgs, type EmitPayload, type EmptyProps, type EventMap, EventPayload, type EventRef, type EventRefs, type EventTriggerOptions, FcmConfig, type Flow, type FlowBuilder, GoalIR, type GoalOptions, IR_VERSION, JourneyConfig, JourneyConfigDefinition, JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, type MessageArgs, MetaIR, NodeChange, NodeIR, NodeMetaIR, type PayloadRef, type PayloadRefNode, type PayloadRefs, type PayloadWhere, type PerformedOptions, type PreferOptions, ProfilePatch, type PropInput, PropValueIR, PropertyOperatorIR, type PropsInput, type PropsOf, PropsSchema, PushContent, type PushOptions, PushTemplate, PushTemplateDefinition, ResolvedProps, ResourceChange, ScalarIR, type SegmentDefinition, SegmentIR, type SegmentRef, SlackContent, SlackTemplate, SourceLocIR, type SubFlow, TemplateChannel, type TemplateFactory, TemplateIR, TemplateIssue, type TemplateRef, type TemplateRefTable, type TemplateRefs, type TemplateRegistry, TemplateValidationError, type TimeOfDay, type TriggerFactory, TriggerIR, type TriggerRef, type UserPropertyBase, type UserPropertyRef, UserPropertyRefIR, type UserRefs, WebPushConfig, WebhookTemplateDefinition, type Weekday, WeekdayIR, type WorkflowBuilder, WorkflowChange, type WorkflowDefinition, WorkflowIR, action, and, apnsEnvironment, assertSupportedSchema, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, templates, trigger, user, workflow };
19
+ export { type ActionContext, type ActionHandler, ActionIR, type ActionRef, AnyTemplate, ApnsConfig, type BranchArm, type BranchCase, BundleIR, BundlePlan, CHANNEL_OF, ChangeStatus, type Channel, ChannelIR, ChannelTemplate, type Condition, ConditionIR, ConfigCompileError, Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DatabaseConfig, type DateTime, DeclaredOutput, DiscordContent, DiscordTemplate, type Duration, DurationIR, EmailAddress, EmailConfig, EmailMetadata, EmailTemplate, EmailTemplateDefinition, type EmitArgs, type EmitPayload, type EmptyProps, type EventMap, EventPayload, type EventRef, type EventRefs, type EventTriggerOptions, FcmConfig, type Flow, type FlowBuilder, GoalIR, type GoalOptions, IR_VERSION, JourneyConfig, JourneyConfigDefinition, JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, type MessageArgs, MetaIR, NodeChange, NodeIR, NodeMetaIR, type PayloadRef, type PayloadRefNode, type PayloadRefs, type PayloadWhere, type PerformedOptions, type PreferOptions, ProfilePatch, type PropInput, PropValueIR, PropertyOperatorIR, type PropsInput, type PropsOf, PropsSchema, PushContent, type PushOptions, PushTemplate, PushTemplateDefinition, ResolvedEmail, ResolvedProps, ResourceChange, ScalarIR, type SegmentDefinition, SegmentIR, type SegmentRef, SlackContent, SlackTemplate, SourceLocIR, type SubFlow, TemplateChannel, type TemplateFactory, TemplateIR, TemplateIssue, type TemplateRef, TemplateRegistry, TemplateValidationError, type TimeOfDay, type TriggerFactory, TriggerIR, type TriggerRef, type UserPropertyBase, type UserPropertyRef, UserPropertyRefIR, type UserRefs, WebPushConfig, WebhookTemplateDefinition, type Weekday, WeekdayIR, type WorkflowBuilder, WorkflowChange, type WorkflowDefinition, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertSupportedSchema, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, trigger, user, workflow };
package/dist/index.d.mts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR } from "./ir.mjs";
2
- import { AnyTemplate, DiscordContent, DiscordTemplate, EmailTemplate, EmailTemplateDefinition, PropsSchema, PushContent, PushTemplate, PushTemplateDefinition, ResolvedProps, SlackContent, SlackTemplate, TemplateChannel, TemplateIssue, TemplateValidationError, WebhookTemplateDefinition, assertSupportedSchema, discordTemplate, emailTemplate, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate } from "./templates.mjs";
2
+ import { EmailAddress, EmailConfig, ResolvedEmail, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, resolveEmail } from "./email.mjs";
3
+ import { AnyTemplate, CHANNEL_OF, ChannelTemplate, DiscordContent, DiscordTemplate, EmailMetadata, EmailTemplate, EmailTemplateDefinition, PropsSchema, PushContent, PushTemplate, PushTemplateDefinition, ResolvedProps, SlackContent, SlackTemplate, TemplateChannel, TemplateIssue, TemplateRegistry, TemplateValidationError, WebhookTemplateDefinition, assertSupportedSchema, discordTemplate, emailTemplate, indexTemplates, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, isTemplate, lookupTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate } from "./templates.mjs";
3
4
  import { ActionContext, ActionHandler, ActionRef, action } from "./dsl/action.mjs";
4
5
  import { Duration, EventMap, TimeOfDay, UserPropertyBase, Weekday } from "./dsl/base.mjs";
5
6
  import { EventRef, EventRefs, MessageArgs, PayloadRef, PayloadRefNode, PayloadRefs, PropInput, PropsInput, UserPropertyRef, UserRefs, event, user } from "./dsl/refs.mjs";
6
- import { Channel, EmptyProps, PropsOf, TemplateFactory, TemplateRef, TemplateRefTable, TemplateRefs, TemplateRegistry, template, templates } from "./dsl/template.mjs";
7
+ import { Channel, EmptyProps, PropsOf, TemplateFactory, TemplateRef, template } from "./dsl/template.mjs";
7
8
  import { Condition, PayloadWhere, PerformedOptions, SegmentDefinition, SegmentRef, and, between, contains, eq, exists, gt, gte, inArray, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, performed, reachable, segment } from "./dsl/condition.mjs";
8
9
  import { BranchArm, BranchCase, EmitArgs, EmitPayload, Flow, FlowBuilder, PreferOptions, PushOptions, SubFlow, flow } from "./dsl/flow.mjs";
9
10
  import { DateTime, EventTriggerOptions, TriggerFactory, TriggerRef, trigger } from "./dsl/trigger.mjs";
@@ -12,7 +13,7 @@ import { compileBundle } from "./dsl/bundle.mjs";
12
13
  import "./dsl/index.mjs";
13
14
  import { Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DeclaredOutput, EventPayload, ProfilePatch, checkEvent, defineContract, isContract } from "./contract.mjs";
14
15
  import { JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, resolveRuntime } from "./runtime.mjs";
15
- import { ApnsConfig, ConfigCompileError, DatabaseConfig, EmailAddress, EmailConfig, FcmConfig, JourneyConfig, JourneyConfigDefinition, WebPushConfig, apnsEnvironment, compileConfig, defineConfig, formatEmailAddress, isConfig } from "./config.mjs";
16
+ import { ApnsConfig, ConfigCompileError, DatabaseConfig, FcmConfig, JourneyConfig, JourneyConfigDefinition, WebPushConfig, apnsEnvironment, compileConfig, defineConfig, isConfig } from "./config.mjs";
16
17
  import { canonicalJSON, fullHash, semanticHash, sha256Hex, stripMeta, stripProvenance } from "./hash.mjs";
17
18
  import { BundlePlan, ChangeStatus, NodeChange, ResourceChange, WorkflowChange, isSemanticChange, plan } from "./plan.mjs";
18
- export { type ActionContext, type ActionHandler, ActionIR, type ActionRef, AnyTemplate, ApnsConfig, type BranchArm, type BranchCase, BundleIR, BundlePlan, ChangeStatus, type Channel, ChannelIR, type Condition, ConditionIR, ConfigCompileError, Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DatabaseConfig, type DateTime, DeclaredOutput, DiscordContent, DiscordTemplate, type Duration, DurationIR, EmailAddress, EmailConfig, EmailTemplate, EmailTemplateDefinition, type EmitArgs, type EmitPayload, type EmptyProps, type EventMap, EventPayload, type EventRef, type EventRefs, type EventTriggerOptions, FcmConfig, type Flow, type FlowBuilder, GoalIR, type GoalOptions, IR_VERSION, JourneyConfig, JourneyConfigDefinition, JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, type MessageArgs, MetaIR, NodeChange, NodeIR, NodeMetaIR, type PayloadRef, type PayloadRefNode, type PayloadRefs, type PayloadWhere, type PerformedOptions, type PreferOptions, ProfilePatch, type PropInput, PropValueIR, PropertyOperatorIR, type PropsInput, type PropsOf, PropsSchema, PushContent, type PushOptions, PushTemplate, PushTemplateDefinition, ResolvedProps, ResourceChange, ScalarIR, type SegmentDefinition, SegmentIR, type SegmentRef, SlackContent, SlackTemplate, SourceLocIR, type SubFlow, TemplateChannel, type TemplateFactory, TemplateIR, TemplateIssue, type TemplateRef, type TemplateRefTable, type TemplateRefs, type TemplateRegistry, TemplateValidationError, type TimeOfDay, type TriggerFactory, TriggerIR, type TriggerRef, type UserPropertyBase, type UserPropertyRef, UserPropertyRefIR, type UserRefs, WebPushConfig, WebhookTemplateDefinition, type Weekday, WeekdayIR, type WorkflowBuilder, WorkflowChange, type WorkflowDefinition, WorkflowIR, action, and, apnsEnvironment, assertSupportedSchema, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, templates, trigger, user, workflow };
19
+ export { type ActionContext, type ActionHandler, ActionIR, type ActionRef, AnyTemplate, ApnsConfig, type BranchArm, type BranchCase, BundleIR, BundlePlan, CHANNEL_OF, ChangeStatus, type Channel, ChannelIR, ChannelTemplate, type Condition, ConditionIR, ConfigCompileError, Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DatabaseConfig, type DateTime, DeclaredOutput, DiscordContent, DiscordTemplate, type Duration, DurationIR, EmailAddress, EmailConfig, EmailMetadata, EmailTemplate, EmailTemplateDefinition, type EmitArgs, type EmitPayload, type EmptyProps, type EventMap, EventPayload, type EventRef, type EventRefs, type EventTriggerOptions, FcmConfig, type Flow, type FlowBuilder, GoalIR, type GoalOptions, IR_VERSION, JourneyConfig, JourneyConfigDefinition, JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, type MessageArgs, MetaIR, NodeChange, NodeIR, NodeMetaIR, type PayloadRef, type PayloadRefNode, type PayloadRefs, type PayloadWhere, type PerformedOptions, type PreferOptions, ProfilePatch, type PropInput, PropValueIR, PropertyOperatorIR, type PropsInput, type PropsOf, PropsSchema, PushContent, type PushOptions, PushTemplate, PushTemplateDefinition, ResolvedEmail, ResolvedProps, ResourceChange, ScalarIR, type SegmentDefinition, SegmentIR, type SegmentRef, SlackContent, SlackTemplate, SourceLocIR, type SubFlow, TemplateChannel, type TemplateFactory, TemplateIR, TemplateIssue, type TemplateRef, TemplateRegistry, TemplateValidationError, type TimeOfDay, type TriggerFactory, TriggerIR, type TriggerRef, type UserPropertyBase, type UserPropertyRef, UserPropertyRefIR, type UserRefs, WebPushConfig, WebhookTemplateDefinition, type Weekday, WeekdayIR, type WorkflowBuilder, WorkflowChange, type WorkflowDefinition, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertSupportedSchema, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, trigger, user, workflow };
package/dist/index.mjs CHANGED
@@ -1,17 +1,18 @@
1
1
  import { canonicalJSON, fullHash, semanticHash, sha256Hex, stripMeta, stripProvenance } from "./hash.mjs";
2
2
  import { action } from "./dsl/action.mjs";
3
3
  import { event, user } from "./dsl/refs.mjs";
4
- import { TemplateValidationError, assertSupportedSchema, discordTemplate, emailTemplate, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate } from "./templates.mjs";
5
- import { template, templates } from "./dsl/template.mjs";
4
+ import { template } from "./dsl/template.mjs";
6
5
  import { and, between, contains, eq, exists, gt, gte, inArray, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, performed, reachable, segment } from "./dsl/condition.mjs";
6
+ import { assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, resolveEmail } from "./email.mjs";
7
+ import { CHANNEL_OF, TemplateValidationError, assertSupportedSchema, discordTemplate, emailTemplate, indexTemplates, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, isTemplate, lookupTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate } from "./templates.mjs";
7
8
  import { flow } from "./dsl/flow.mjs";
8
9
  import { trigger } from "./dsl/trigger.mjs";
9
10
  import { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR } from "./ir.mjs";
10
11
  import { workflow } from "./dsl/workflow.mjs";
11
12
  import { compileBundle } from "./dsl/bundle.mjs";
12
13
  import "./dsl/index.mjs";
13
- import { ConfigCompileError, apnsEnvironment, compileConfig, defineConfig, formatEmailAddress, isConfig } from "./config.mjs";
14
+ import { ConfigCompileError, apnsEnvironment, compileConfig, defineConfig, isConfig } from "./config.mjs";
14
15
  import { ContractValidationError, checkEvent, defineContract, isContract } from "./contract.mjs";
15
16
  import { isSemanticChange, plan } from "./plan.mjs";
16
17
  import { resolveRuntime } from "./runtime.mjs";
17
- export { ActionIR, BundleIR, ChannelIR, ConditionIR, ConfigCompileError, ContractValidationError, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TemplateValidationError, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR, action, and, apnsEnvironment, assertSupportedSchema, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, templates, trigger, user, workflow };
18
+ export { ActionIR, BundleIR, CHANNEL_OF, ChannelIR, ConditionIR, ConfigCompileError, ContractValidationError, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TemplateValidationError, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertSupportedSchema, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, trigger, user, workflow };
package/dist/push.cjs CHANGED
@@ -1,12 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_templates = require("./templates.cjs");
3
3
  //#region src/push.ts
4
- /** A renderer over the registry; a TemplateValidationError means nothing may be sent. */
4
+ /** A renderer over the registry, by template id; a TemplateValidationError means nothing may be sent. */
5
5
  function registryPushRenderer(pushes) {
6
- return async (key, props) => {
7
- if (!Object.hasOwn(pushes, key)) throw new Error(`unknown push template: ${key}`);
8
- return require_templates.renderPushContent(pushes[key], key, props);
9
- };
6
+ const index = require_templates.indexTemplates("push", pushes);
7
+ return async (id, props) => require_templates.renderPushContent(require_templates.lookupTemplate(index, "push", id), props);
10
8
  }
11
9
  //#endregion
12
10
  exports.registryPushRenderer = registryPushRenderer;
package/dist/push.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"push.cjs","names":["renderPushContent"],"sources":["../src/push.ts"],"sourcesContent":["import type { ScalarIR } from './ir';\nimport { type PushContent, type PushTemplate, renderPushContent } from './templates';\n\n/**\n * `@shware/workflow/push` — push-notification content. A push is data, not a\n * component: a title and a body the OS renders in its own chrome, built by a\n * `pushTemplate`'s `content(parsedProps)` — final text, never placeholder-\n * expanded, so braces in user data survive. Transport is a separate concern\n * (`@shware/workflow/apns`, `@shware/workflow/fcm`).\n */\n\nexport type RenderedPush = PushContent;\n\nexport type PushRenderer = (\n template: string,\n props: Record<string, ScalarIR | undefined>\n) => Promise<RenderedPush>;\n\n/** A renderer over the registry; a TemplateValidationError means nothing may be sent. */\nexport function registryPushRenderer(pushes: Record<string, PushTemplate>): PushRenderer {\n return async (key, props) => {\n if (!Object.hasOwn(pushes, key)) throw new Error(`unknown push template: ${key}`);\n return renderPushContent(pushes[key], key, props);\n };\n}\n"],"mappings":";;;;AAmBA,SAAgB,qBAAqB,QAAoD;CACvF,OAAO,OAAO,KAAK,UAAU;EAC3B,IAAI,CAAC,OAAO,OAAO,QAAQ,GAAG,GAAG,MAAM,IAAI,MAAM,0BAA0B,KAAK;EAChF,OAAOA,kBAAAA,kBAAkB,OAAO,MAAM,KAAK,KAAK;CAClD;AACF"}
1
+ {"version":3,"file":"push.cjs","names":["indexTemplates","renderPushContent","lookupTemplate"],"sources":["../src/push.ts"],"sourcesContent":["import type { ScalarIR } from './ir';\nimport {\n type PushContent,\n type PushTemplate,\n indexTemplates,\n lookupTemplate,\n renderPushContent,\n} from './templates';\n\n/**\n * `@shware/workflow/push` — push-notification content. A push is data, not a\n * component: a title and a body the OS renders in its own chrome, built by a\n * `pushTemplate`'s `content(parsedProps)` — final text, never placeholder-\n * expanded, so braces in user data survive. Transport is a separate concern\n * (`@shware/workflow/apns`, `@shware/workflow/fcm`).\n */\n\nexport type RenderedPush = PushContent;\n\nexport type PushRenderer = (\n template: string,\n props: Record<string, ScalarIR | undefined>\n) => Promise<RenderedPush>;\n\n/** A renderer over the registry, by template id; a TemplateValidationError means nothing may be sent. */\nexport function registryPushRenderer(pushes: Readonly<Record<string, PushTemplate>>): PushRenderer {\n const index = indexTemplates('push', pushes);\n return async (id, props) => renderPushContent(lookupTemplate(index, 'push', id), props);\n}\n"],"mappings":";;;;AAyBA,SAAgB,qBAAqB,QAA8D;CACjG,MAAM,QAAQA,kBAAAA,eAAe,QAAQ,MAAM;CAC3C,OAAO,OAAO,IAAI,UAAUC,kBAAAA,kBAAkBC,kBAAAA,eAAe,OAAO,QAAQ,EAAE,GAAG,KAAK;AACxF"}
package/dist/push.d.cts CHANGED
@@ -10,7 +10,7 @@ import { PushContent, PushTemplate } from "./templates.cjs";
10
10
  */
11
11
  export type RenderedPush = PushContent;
12
12
  export type PushRenderer = (template: string, props: Record<string, ScalarIR | undefined>) => Promise<RenderedPush>;
13
- /** A renderer over the registry; a TemplateValidationError means nothing may be sent. */
14
- export declare function registryPushRenderer(pushes: Record<string, PushTemplate>): PushRenderer;
13
+ /** A renderer over the registry, by template id; a TemplateValidationError means nothing may be sent. */
14
+ export declare function registryPushRenderer(pushes: Readonly<Record<string, PushTemplate>>): PushRenderer;
15
15
  //#endregion
16
16
  //# sourceMappingURL=push.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"push.d.cts","names":[],"sources":["../src/push.ts"],"mappings":";;;;;;;;;;YAWY,eAAe;YAEf,gBACV,kBACA,OAAO,eAAe,0BACnB,QAAQ;;wBAGG,qBAAqB,QAAQ,eAAe,gBAAgB"}
1
+ {"version":3,"file":"push.d.cts","names":[],"sources":["../src/push.ts"],"mappings":";;;;;;;;;;YAiBY,eAAe;YAEf,gBACV,kBACA,OAAO,eAAe,0BACnB,QAAQ;;wBAGG,qBAAqB,QAAQ,SAAS,eAAe,iBAAiB"}
package/dist/push.d.mts CHANGED
@@ -10,7 +10,7 @@ import { PushContent, PushTemplate } from "./templates.mjs";
10
10
  */
11
11
  export type RenderedPush = PushContent;
12
12
  export type PushRenderer = (template: string, props: Record<string, ScalarIR | undefined>) => Promise<RenderedPush>;
13
- /** A renderer over the registry; a TemplateValidationError means nothing may be sent. */
14
- export declare function registryPushRenderer(pushes: Record<string, PushTemplate>): PushRenderer;
13
+ /** A renderer over the registry, by template id; a TemplateValidationError means nothing may be sent. */
14
+ export declare function registryPushRenderer(pushes: Readonly<Record<string, PushTemplate>>): PushRenderer;
15
15
  //#endregion
16
16
  //# sourceMappingURL=push.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"push.d.mts","names":[],"sources":["../src/push.ts"],"mappings":";;;;;;;;;;YAWY,eAAe;YAEf,gBACV,kBACA,OAAO,eAAe,0BACnB,QAAQ;;wBAGG,qBAAqB,QAAQ,eAAe,gBAAgB"}
1
+ {"version":3,"file":"push.d.mts","names":[],"sources":["../src/push.ts"],"mappings":";;;;;;;;;;YAiBY,eAAe;YAEf,gBACV,kBACA,OAAO,eAAe,0BACnB,QAAQ;;wBAGG,qBAAqB,QAAQ,SAAS,eAAe,iBAAiB"}
package/dist/push.mjs CHANGED
@@ -1,11 +1,9 @@
1
- import { renderPushContent } from "./templates.mjs";
1
+ import { indexTemplates, lookupTemplate, renderPushContent } from "./templates.mjs";
2
2
  //#region src/push.ts
3
- /** A renderer over the registry; a TemplateValidationError means nothing may be sent. */
3
+ /** A renderer over the registry, by template id; a TemplateValidationError means nothing may be sent. */
4
4
  function registryPushRenderer(pushes) {
5
- return async (key, props) => {
6
- if (!Object.hasOwn(pushes, key)) throw new Error(`unknown push template: ${key}`);
7
- return renderPushContent(pushes[key], key, props);
8
- };
5
+ const index = indexTemplates("push", pushes);
6
+ return async (id, props) => renderPushContent(lookupTemplate(index, "push", id), props);
9
7
  }
10
8
  //#endregion
11
9
  export { registryPushRenderer };
package/dist/push.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"push.mjs","names":[],"sources":["../src/push.ts"],"sourcesContent":["import type { ScalarIR } from './ir';\nimport { type PushContent, type PushTemplate, renderPushContent } from './templates';\n\n/**\n * `@shware/workflow/push` — push-notification content. A push is data, not a\n * component: a title and a body the OS renders in its own chrome, built by a\n * `pushTemplate`'s `content(parsedProps)` — final text, never placeholder-\n * expanded, so braces in user data survive. Transport is a separate concern\n * (`@shware/workflow/apns`, `@shware/workflow/fcm`).\n */\n\nexport type RenderedPush = PushContent;\n\nexport type PushRenderer = (\n template: string,\n props: Record<string, ScalarIR | undefined>\n) => Promise<RenderedPush>;\n\n/** A renderer over the registry; a TemplateValidationError means nothing may be sent. */\nexport function registryPushRenderer(pushes: Record<string, PushTemplate>): PushRenderer {\n return async (key, props) => {\n if (!Object.hasOwn(pushes, key)) throw new Error(`unknown push template: ${key}`);\n return renderPushContent(pushes[key], key, props);\n };\n}\n"],"mappings":";;;AAmBA,SAAgB,qBAAqB,QAAoD;CACvF,OAAO,OAAO,KAAK,UAAU;EAC3B,IAAI,CAAC,OAAO,OAAO,QAAQ,GAAG,GAAG,MAAM,IAAI,MAAM,0BAA0B,KAAK;EAChF,OAAO,kBAAkB,OAAO,MAAM,KAAK,KAAK;CAClD;AACF"}
1
+ {"version":3,"file":"push.mjs","names":[],"sources":["../src/push.ts"],"sourcesContent":["import type { ScalarIR } from './ir';\nimport {\n type PushContent,\n type PushTemplate,\n indexTemplates,\n lookupTemplate,\n renderPushContent,\n} from './templates';\n\n/**\n * `@shware/workflow/push` — push-notification content. A push is data, not a\n * component: a title and a body the OS renders in its own chrome, built by a\n * `pushTemplate`'s `content(parsedProps)` — final text, never placeholder-\n * expanded, so braces in user data survive. Transport is a separate concern\n * (`@shware/workflow/apns`, `@shware/workflow/fcm`).\n */\n\nexport type RenderedPush = PushContent;\n\nexport type PushRenderer = (\n template: string,\n props: Record<string, ScalarIR | undefined>\n) => Promise<RenderedPush>;\n\n/** A renderer over the registry, by template id; a TemplateValidationError means nothing may be sent. */\nexport function registryPushRenderer(pushes: Readonly<Record<string, PushTemplate>>): PushRenderer {\n const index = indexTemplates('push', pushes);\n return async (id, props) => renderPushContent(lookupTemplate(index, 'push', id), props);\n}\n"],"mappings":";;;AAyBA,SAAgB,qBAAqB,QAA8D;CACjG,MAAM,QAAQ,eAAe,QAAQ,MAAM;CAC3C,OAAO,OAAO,IAAI,UAAU,kBAAkB,eAAe,OAAO,QAAQ,EAAE,GAAG,KAAK;AACxF"}
@@ -1,5 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_email = require("./email.cjs");
2
3
  const require_templates = require("./templates.cjs");
4
+ const require_email_context = require("./email-context.cjs");
3
5
  let react = require("react");
4
6
  let react_dom_server = require("react-dom/server");
5
7
  //#region src/react-email.ts
@@ -8,10 +10,14 @@ let react_dom_server = require("react-dom/server");
8
10
  * registry (`react-dom` is an optional peer dependency; import this subpath
9
11
  * only when you use it).
10
12
  *
11
- * The registry maps keys to `emailTemplate({...})` definitions. Props are
13
+ * The registry holds `emailTemplate({...})` definitions, looked up by their `id`. Props are
12
14
  * parsed against the template's schema before the component renders — a
13
15
  * failure throws TemplateValidationError and nothing renders — and the
14
- * component renders as an element, so hooks and context work. Rendering
16
+ * component renders as an element inside an `EmailProvider` carrying the
17
+ * template's resolved metadata (`resolveEmail` against the config's `email`),
18
+ * so hooks and context work and a layout's `useEmailContext()` reads the
19
+ * unsubscribe link the email is sent with; the render returns that metadata
20
+ * with the subject and HTML, for the sender. Rendering
15
21
  * streams and waits for every Suspense boundary (`allReady`) — react-email's
16
22
  * `<Tailwind>` suspends while it compiles the classes, which synchronous
17
23
  * static markup cannot wait for — then prepends the XHTML doctype react-email
@@ -19,13 +25,15 @@ let react_dom_server = require("react-dom/server");
19
25
  * statically imports prettier and html-to-text for options never used here.
20
26
  */
21
27
  const DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
22
- function registryRenderer(emails) {
23
- return async (key, props) => {
24
- if (!Object.hasOwn(emails, key)) throw new Error(`unknown email template: ${key}`);
25
- const template = emails[key];
26
- const parsed = require_templates.parseTemplateProps(template, key, props);
27
- const element = (0, react.createElement)(template.component, parsed);
28
+ function registryRenderer(emails, config) {
29
+ const index = require_templates.indexTemplates("email", emails);
30
+ return async (id, props) => {
31
+ const template = require_templates.lookupTemplate(index, "email", id);
32
+ const parsed = require_templates.parseTemplateProps(template, props);
33
+ const email = require_email.resolveEmail(template, config);
34
+ const element = (0, react.createElement)(require_email_context.EmailProvider, { email }, (0, react.createElement)(template.component, parsed));
28
35
  return {
36
+ ...email,
29
37
  subject: template.subject,
30
38
  html: DOCTYPE + await renderToHtml(element)
31
39
  };
@@ -1 +1 @@
1
- {"version":3,"file":"react-email.cjs","names":["parseTemplateProps","createElement","renderToReadableStream"],"sources":["../src/react-email.ts"],"sourcesContent":["import { type ComponentType, type ReactElement, createElement } from 'react';\nimport { renderToReadableStream } from 'react-dom/server';\nimport type { EmailRenderer } from './cloudflare';\nimport { type EmailTemplate, parseTemplateProps } from './templates';\n\n/**\n * `@shware/workflow/react-email` — an EmailRenderer over the project's email\n * registry (`react-dom` is an optional peer dependency; import this subpath\n * only when you use it).\n *\n * The registry maps keys to `emailTemplate({...})` definitions. Props are\n * parsed against the template's schema before the component renders — a\n * failure throws TemplateValidationError and nothing renders — and the\n * component renders as an element, so hooks and context work. Rendering\n * streams and waits for every Suspense boundary (`allReady`) — react-email's\n * `<Tailwind>` suspends while it compiles the classes, which synchronous\n * static markup cannot wait for — then prepends the XHTML doctype react-email\n * uses. @react-email/render itself is avoided because its Workers build\n * statically imports prettier and html-to-text for options never used here.\n */\n\nconst DOCTYPE =\n '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">';\n\nexport function registryRenderer(emails: Record<string, EmailTemplate>): EmailRenderer {\n return async (key, props) => {\n if (!Object.hasOwn(emails, key)) throw new Error(`unknown email template: ${key}`);\n const template = emails[key];\n const parsed = parseTemplateProps(template, key, props);\n const element = createElement(template.component as ComponentType<object>, parsed);\n return { subject: template.subject, html: DOCTYPE + (await renderToHtml(element)) };\n };\n}\n\n/** Render to a string once every Suspense boundary has resolved; a rendering error rejects. */\nasync function renderToHtml(element: ReactElement): Promise<string> {\n let failure: unknown;\n const stream = await renderToReadableStream(element, {\n onError: (error) => {\n failure = error;\n },\n });\n await stream.allReady;\n const html = await new Response(stream).text();\n if (failure !== undefined) {\n throw failure instanceof Error ? failure : new Error('email render failed', { cause: failure });\n }\n // Streaming a document adds what static markup did not: an HTML5 doctype (the caller prepends\n // the XHTML one email clients expect) and React 19's image preload hints, which mean nothing in\n // an email. Both are dropped, as @react-email/render does.\n return html\n .replace(/^<!DOCTYPE html>/i, '')\n .replace(/<link rel=\"preload\" as=\"image\"[^>]*\\/>/g, '');\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,MAAM,UACJ;AAEF,SAAgB,iBAAiB,QAAsD;CACrF,OAAO,OAAO,KAAK,UAAU;EAC3B,IAAI,CAAC,OAAO,OAAO,QAAQ,GAAG,GAAG,MAAM,IAAI,MAAM,2BAA2B,KAAK;EACjF,MAAM,WAAW,OAAO;EACxB,MAAM,SAASA,kBAAAA,mBAAmB,UAAU,KAAK,KAAK;EACtD,MAAM,WAAA,GAAUC,MAAAA,cAAAA,CAAc,SAAS,WAAoC,MAAM;EACjF,OAAO;GAAE,SAAS,SAAS;GAAS,MAAM,UAAW,MAAM,aAAa,OAAO;EAAG;CACpF;AACF;;AAGA,eAAe,aAAa,SAAwC;CAClE,IAAI;CACJ,MAAM,SAAS,OAAA,GAAMC,iBAAAA,uBAAAA,CAAuB,SAAS,EACnD,UAAU,UAAU;EAClB,UAAU;CACZ,EACF,CAAC;CACD,MAAM,OAAO;CACb,MAAM,OAAO,MAAM,IAAI,SAAS,MAAM,CAAC,CAAC,KAAK;CAC7C,IAAI,YAAY,KAAA,GACd,MAAM,mBAAmB,QAAQ,UAAU,IAAI,MAAM,uBAAuB,EAAE,OAAO,QAAQ,CAAC;CAKhG,OAAO,KACJ,QAAQ,qBAAqB,EAAE,CAAC,CAChC,QAAQ,2CAA2C,EAAE;AAC1D"}
1
+ {"version":3,"file":"react-email.cjs","names":["indexTemplates","lookupTemplate","parseTemplateProps","resolveEmail","createElement","EmailProvider","renderToReadableStream"],"sources":["../src/react-email.ts"],"sourcesContent":["import { type ComponentType, type ReactElement, createElement } from 'react';\nimport { renderToReadableStream } from 'react-dom/server';\nimport type { EmailRenderer } from './cloudflare';\nimport { type EmailConfig, resolveEmail } from './email';\nimport { EmailProvider } from './email-context';\nimport {\n type EmailTemplate,\n indexTemplates,\n lookupTemplate,\n parseTemplateProps,\n} from './templates';\n\n/**\n * `@shware/workflow/react-email` — an EmailRenderer over the project's email\n * registry (`react-dom` is an optional peer dependency; import this subpath\n * only when you use it).\n *\n * The registry holds `emailTemplate({...})` definitions, looked up by their `id`. Props are\n * parsed against the template's schema before the component renders — a\n * failure throws TemplateValidationError and nothing renders — and the\n * component renders as an element inside an `EmailProvider` carrying the\n * template's resolved metadata (`resolveEmail` against the config's `email`),\n * so hooks and context work and a layout's `useEmailContext()` reads the\n * unsubscribe link the email is sent with; the render returns that metadata\n * with the subject and HTML, for the sender. Rendering\n * streams and waits for every Suspense boundary (`allReady`) — react-email's\n * `<Tailwind>` suspends while it compiles the classes, which synchronous\n * static markup cannot wait for — then prepends the XHTML doctype react-email\n * uses. @react-email/render itself is avoided because its Workers build\n * statically imports prettier and html-to-text for options never used here.\n */\n\nconst DOCTYPE =\n '<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">';\n\nexport function registryRenderer(\n emails: Readonly<Record<string, EmailTemplate>>,\n config?: EmailConfig\n): EmailRenderer {\n const index = indexTemplates('email', emails);\n return async (id, props) => {\n const template = lookupTemplate(index, 'email', id);\n const parsed = parseTemplateProps(template, props);\n const email = resolveEmail(template, config);\n const element = createElement(\n EmailProvider,\n { email },\n createElement(template.component as ComponentType<object>, parsed)\n );\n return { ...email, subject: template.subject, html: DOCTYPE + (await renderToHtml(element)) };\n };\n}\n\n/** Render to a string once every Suspense boundary has resolved; a rendering error rejects. */\nasync function renderToHtml(element: ReactElement): Promise<string> {\n let failure: unknown;\n const stream = await renderToReadableStream(element, {\n onError: (error) => {\n failure = error;\n },\n });\n await stream.allReady;\n const html = await new Response(stream).text();\n if (failure !== undefined) {\n throw failure instanceof Error ? failure : new Error('email render failed', { cause: failure });\n }\n // Streaming a document adds what static markup did not: an HTML5 doctype (the caller prepends\n // the XHTML one email clients expect) and React 19's image preload hints, which mean nothing in\n // an email. Both are dropped, as @react-email/render does.\n return html\n .replace(/^<!DOCTYPE html>/i, '')\n .replace(/<link rel=\"preload\" as=\"image\"[^>]*\\/>/g, '');\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAM,UACJ;AAEF,SAAgB,iBACd,QACA,QACe;CACf,MAAM,QAAQA,kBAAAA,eAAe,SAAS,MAAM;CAC5C,OAAO,OAAO,IAAI,UAAU;EAC1B,MAAM,WAAWC,kBAAAA,eAAe,OAAO,SAAS,EAAE;EAClD,MAAM,SAASC,kBAAAA,mBAAmB,UAAU,KAAK;EACjD,MAAM,QAAQC,cAAAA,aAAa,UAAU,MAAM;EAC3C,MAAM,WAAA,GAAUC,MAAAA,cAAAA,CACdC,sBAAAA,eACA,EAAE,MAAM,IAAA,GACRD,MAAAA,cAAAA,CAAc,SAAS,WAAoC,MAAM,CACnE;EACA,OAAO;GAAE,GAAG;GAAO,SAAS,SAAS;GAAS,MAAM,UAAW,MAAM,aAAa,OAAO;EAAG;CAC9F;AACF;;AAGA,eAAe,aAAa,SAAwC;CAClE,IAAI;CACJ,MAAM,SAAS,OAAA,GAAME,iBAAAA,uBAAAA,CAAuB,SAAS,EACnD,UAAU,UAAU;EAClB,UAAU;CACZ,EACF,CAAC;CACD,MAAM,OAAO;CACb,MAAM,OAAO,MAAM,IAAI,SAAS,MAAM,CAAC,CAAC,KAAK;CAC7C,IAAI,YAAY,KAAA,GACd,MAAM,mBAAmB,QAAQ,UAAU,IAAI,MAAM,uBAAuB,EAAE,OAAO,QAAQ,CAAC;CAKhG,OAAO,KACJ,QAAQ,qBAAqB,EAAE,CAAC,CAChC,QAAQ,2CAA2C,EAAE;AAC1D"}
@@ -1,7 +1,8 @@
1
+ import { EmailConfig } from "./email.cjs";
1
2
  import { EmailTemplate } from "./templates.cjs";
2
3
  import { EmailRenderer } from "./cloudflare/senders.cjs";
3
4
  import "./cloudflare/index.cjs";
4
5
  //#region src/react-email.d.ts
5
- export declare function registryRenderer(emails: Record<string, EmailTemplate>): EmailRenderer;
6
+ export declare function registryRenderer(emails: Readonly<Record<string, EmailTemplate>>, config?: EmailConfig): EmailRenderer;
6
7
  //#endregion
7
8
  //# sourceMappingURL=react-email.d.cts.map