@ultimat3/mail 1.0.0 → 1.2.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 (69) hide show
  1. package/package.json +6 -6
  2. package/src/blocks.d.ts +0 -47
  3. package/src/blocks.d.ts.map +0 -1
  4. package/src/blocks.js +0 -23
  5. package/src/blocks.js.map +0 -1
  6. package/src/catalog.d.ts +0 -6
  7. package/src/catalog.d.ts.map +0 -1
  8. package/src/catalog.js +0 -78
  9. package/src/catalog.js.map +0 -1
  10. package/src/driver.d.ts +0 -79
  11. package/src/driver.d.ts.map +0 -1
  12. package/src/driver.js +0 -115
  13. package/src/driver.js.map +0 -1
  14. package/src/errors.d.ts +0 -23
  15. package/src/errors.d.ts.map +0 -1
  16. package/src/errors.js +0 -82
  17. package/src/errors.js.map +0 -1
  18. package/src/html.d.ts +0 -10
  19. package/src/html.d.ts.map +0 -1
  20. package/src/html.js +0 -34
  21. package/src/html.js.map +0 -1
  22. package/src/index.d.ts +0 -17
  23. package/src/index.d.ts.map +0 -1
  24. package/src/index.js +0 -13
  25. package/src/index.js.map +0 -1
  26. package/src/job.d.ts +0 -17
  27. package/src/job.d.ts.map +0 -1
  28. package/src/job.js +0 -79
  29. package/src/job.js.map +0 -1
  30. package/src/layout.d.ts +0 -41
  31. package/src/layout.d.ts.map +0 -1
  32. package/src/layout.js +0 -160
  33. package/src/layout.js.map +0 -1
  34. package/src/mail.d.ts +0 -67
  35. package/src/mail.d.ts.map +0 -1
  36. package/src/mail.js +0 -119
  37. package/src/mail.js.map +0 -1
  38. package/src/render.d.ts +0 -26
  39. package/src/render.d.ts.map +0 -1
  40. package/src/render.js +0 -149
  41. package/src/render.js.map +0 -1
  42. package/src/templates/index.d.ts +0 -9
  43. package/src/templates/index.d.ts.map +0 -1
  44. package/src/templates/index.js +0 -23
  45. package/src/templates/index.js.map +0 -1
  46. package/src/templates/invite.d.ts +0 -15
  47. package/src/templates/invite.d.ts.map +0 -1
  48. package/src/templates/invite.js +0 -25
  49. package/src/templates/invite.js.map +0 -1
  50. package/src/templates/mfa-enrolled.d.ts +0 -14
  51. package/src/templates/mfa-enrolled.d.ts.map +0 -1
  52. package/src/templates/mfa-enrolled.js +0 -26
  53. package/src/templates/mfa-enrolled.js.map +0 -1
  54. package/src/templates/reset-password.d.ts +0 -13
  55. package/src/templates/reset-password.d.ts.map +0 -1
  56. package/src/templates/reset-password.js +0 -23
  57. package/src/templates/reset-password.js.map +0 -1
  58. package/src/templates/security-alert.d.ts +0 -16
  59. package/src/templates/security-alert.d.ts.map +0 -1
  60. package/src/templates/security-alert.js +0 -29
  61. package/src/templates/security-alert.js.map +0 -1
  62. package/src/templates/verify-email.d.ts +0 -13
  63. package/src/templates/verify-email.d.ts.map +0 -1
  64. package/src/templates/verify-email.js +0 -23
  65. package/src/templates/verify-email.js.map +0 -1
  66. package/src/templates/welcome.d.ts +0 -13
  67. package/src/templates/welcome.d.ts.map +0 -1
  68. package/src/templates/welcome.js +0 -20
  69. package/src/templates/welcome.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/mail",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Transactional email as data: one template renders HTML and text, sent through a job.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -30,10 +30,10 @@
30
30
  "test": "bun test"
31
31
  },
32
32
  "dependencies": {
33
- "@ultimat3/core": "1.0.0",
34
- "@ultimat3/i18n": "1.0.0",
35
- "@ultimat3/jobs": "1.0.0",
36
- "@ultimat3/schema": "1.0.0",
37
- "@ultimat3/time": "1.0.0"
33
+ "@ultimat3/core": "1.2.0",
34
+ "@ultimat3/i18n": "1.2.0",
35
+ "@ultimat3/jobs": "1.2.0",
36
+ "@ultimat3/schema": "1.2.0",
37
+ "@ultimat3/time": "1.2.0"
38
38
  }
39
39
  }
package/src/blocks.d.ts DELETED
@@ -1,47 +0,0 @@
1
- import type { TranslateVars, Translator } from '@ultimat3/i18n';
2
- export type CalloutTone = 'info' | 'danger';
3
- export type MailBlock = {
4
- readonly kind: 'heading';
5
- readonly key: string;
6
- readonly vars?: TranslateVars | undefined;
7
- } | {
8
- readonly kind: 'paragraph';
9
- readonly key: string;
10
- readonly vars?: TranslateVars | undefined;
11
- } | {
12
- readonly kind: 'button';
13
- readonly key: string;
14
- readonly href: string;
15
- readonly vars?: TranslateVars | undefined;
16
- } | {
17
- readonly kind: 'callout';
18
- readonly key: string;
19
- readonly tone: CalloutTone;
20
- readonly vars?: TranslateVars | undefined;
21
- }
22
- /** A label key plus an already-localised value (a date, an IP, a method name). */
23
- | {
24
- readonly kind: 'detail';
25
- readonly key: string;
26
- readonly value: string;
27
- } | {
28
- readonly kind: 'divider';
29
- };
30
- /** Block constructors, so a template never hand-writes an object literal or a colour. */
31
- export declare const blocks: Readonly<{
32
- heading: (key: string, vars?: TranslateVars) => MailBlock;
33
- paragraph: (key: string, vars?: TranslateVars) => MailBlock;
34
- button: (key: string, href: string, vars?: TranslateVars) => MailBlock;
35
- callout: (key: string, tone: CalloutTone, vars?: TranslateVars) => MailBlock;
36
- detail: (key: string, value: string) => MailBlock;
37
- divider: () => MailBlock;
38
- }>;
39
- export interface TemplateArgs<I> {
40
- readonly data: I;
41
- readonly t: Translator;
42
- readonly locale: string;
43
- /** IANA zone. Every date in a mail is formatted with it — never with the server's zone. */
44
- readonly tz: string;
45
- }
46
- export type MailTemplate<I> = (args: TemplateArgs<I>) => readonly MailBlock[];
47
- //# sourceMappingURL=blocks.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["blocks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEhE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE5C,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,SAAS,CAAA;CAAE,GAC7F;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,SAAS,CAAA;CAAE,GAC/F;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;CAC3C,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;CAC3C;AACH,kFAAkF;GAChF;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACzE;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAEjC,yFAAyF;AACzF,eAAO,MAAM,MAAM;mBACF,MAAM,SAAS,aAAa,KAAG,SAAS;qBACtC,MAAM,SAAS,aAAa,KAAG,SAAS;kBAC3C,MAAM,QAAQ,MAAM,SAAS,aAAa,KAAG,SAAS;mBAMrD,MAAM,QAAQ,WAAW,SAAS,aAAa,KAAG,SAAS;kBAM5D,MAAM,SAAS,MAAM,KAAG,SAAS;mBAClC,SAAS;EACtB,CAAC;AAEH,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,CAAC,EAAE,UAAU,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,SAAS,SAAS,EAAE,CAAC"}
package/src/blocks.js DELETED
@@ -1,23 +0,0 @@
1
- // Single responsibility: the vocabulary a template writes in. A mail body is a list of blocks
2
- // carrying i18n KEYS, never markup and never a colour — that is what makes one template able
3
- // to produce an HTML part and a text part, and what keeps translation out of the renderer.
4
- /** Block constructors, so a template never hand-writes an object literal or a colour. */
5
- export const blocks = Object.freeze({
6
- heading: (key, vars) => ({ kind: 'heading', key, vars }),
7
- paragraph: (key, vars) => ({ kind: 'paragraph', key, vars }),
8
- button: (key, href, vars) => ({
9
- kind: 'button',
10
- key,
11
- href,
12
- vars,
13
- }),
14
- callout: (key, tone, vars) => ({
15
- kind: 'callout',
16
- key,
17
- tone,
18
- vars,
19
- }),
20
- detail: (key, value) => ({ kind: 'detail', key, value }),
21
- divider: () => ({ kind: 'divider' }),
22
- });
23
- //# sourceMappingURL=blocks.js.map
package/src/blocks.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"blocks.js","sourceRoot":"","sources":["blocks.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,6FAA6F;AAC7F,2FAA2F;AAyB3F,yFAAyF;AACzF,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAClC,OAAO,EAAE,CAAC,GAAW,EAAE,IAAoB,EAAa,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IAC3F,SAAS,EAAE,CAAC,GAAW,EAAE,IAAoB,EAAa,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IAC/F,MAAM,EAAE,CAAC,GAAW,EAAE,IAAY,EAAE,IAAoB,EAAa,EAAE,CAAC,CAAC;QACvE,IAAI,EAAE,QAAQ;QACd,GAAG;QACH,IAAI;QACJ,IAAI;KACL,CAAC;IACF,OAAO,EAAE,CAAC,GAAW,EAAE,IAAiB,EAAE,IAAoB,EAAa,EAAE,CAAC,CAAC;QAC7E,IAAI,EAAE,SAAS;QACf,GAAG;QACH,IAAI;QACJ,IAAI;KACL,CAAC;IACF,MAAM,EAAE,CAAC,GAAW,EAAE,KAAa,EAAa,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IACnF,OAAO,EAAE,GAAc,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;CAChD,CAAC,CAAC"}
package/src/catalog.d.ts DELETED
@@ -1,6 +0,0 @@
1
- import { type Catalog, type Locale, type NestedCatalog } from '@ultimat3/i18n';
2
- export declare const MAIL_CATALOG_SOURCE: NestedCatalog;
3
- export declare const MAIL_CATALOG: Catalog;
4
- /** Called at boot next to `registerFrameworkCatalog()`. Idempotent. */
5
- export declare function registerMailCatalog(locale?: Locale): void;
6
- //# sourceMappingURL=catalog.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["catalog.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,OAAO,EAEZ,KAAK,MAAM,EAEX,KAAK,aAAa,EAEnB,MAAM,gBAAgB,CAAC;AAExB,eAAO,MAAM,mBAAmB,EAAE,aAkEjC,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,OAA0C,CAAC;AAEtE,uEAAuE;AACvE,wBAAgB,mBAAmB,CAAC,MAAM,GAAE,MAAuB,GAAG,IAAI,CAEzE"}
package/src/catalog.js DELETED
@@ -1,78 +0,0 @@
1
- // Single responsibility: the English source strings for the keys this package's templates
2
- // emit. Templates carry keys only; the words live here (and in an app's own catalogs, which
3
- // are registered after this one and therefore win). Translating a framework mail means
4
- // shipping `mail.*` in an app catalog — never editing a template.
5
- import { DEFAULT_LOCALE, loadCatalog, registerCatalog, } from '@ultimat3/i18n';
6
- export const MAIL_CATALOG_SOURCE = {
7
- mail: {
8
- footer: {
9
- legal: 'You received this message because of activity on your account.',
10
- help: 'Questions? Reply to this email and a human will answer.',
11
- unsubscribe: 'Unsubscribe',
12
- },
13
- welcome: {
14
- subject: 'Welcome to {appName}',
15
- preheader: 'Your {appName} account is ready.',
16
- heading: 'Welcome, {name}',
17
- body: 'Your {appName} account is ready. Pick up where you left off any time.',
18
- cta: 'Open {appName}',
19
- },
20
- 'verify-email': {
21
- subject: 'Verify your email address',
22
- preheader: 'One click and this address is confirmed.',
23
- heading: 'Verify your email, {name}',
24
- body: 'Confirm this address so we can secure your account and reach you when it matters.',
25
- cta: 'Verify email address',
26
- expiry_one: 'This link expires in {count} minute.',
27
- expiry_other: 'This link expires in {count} minutes.',
28
- ignore: 'If you did not create this account, ignore this message.',
29
- },
30
- 'reset-password': {
31
- subject: 'Reset your password',
32
- preheader: 'A link to choose a new password.',
33
- heading: 'Reset your password, {name}',
34
- body: 'Choose a new password. Existing sessions stay signed in until you sign them out.',
35
- cta: 'Choose a new password',
36
- expiry_one: 'This link expires in {count} minute.',
37
- expiry_other: 'This link expires in {count} minutes.',
38
- ignore: 'If you did not ask for this, no action is needed — your password is unchanged.',
39
- },
40
- invite: {
41
- subject: '{inviterName} invited you to {orgName}',
42
- preheader: 'Your invitation to {orgName}.',
43
- heading: 'Join {orgName}',
44
- body: '{inviterName} invited you to collaborate in {orgName}.',
45
- cta: 'Accept invitation',
46
- expiry_one: 'This invitation expires in {count} hour.',
47
- expiry_other: 'This invitation expires in {count} hours.',
48
- },
49
- 'mfa-enrolled': {
50
- subject: 'Two-factor authentication is on',
51
- preheader: 'A new second factor now protects your account.',
52
- heading: 'Two-factor is on, {name}',
53
- body: 'Every sign-in now needs a second factor as well as your password.',
54
- 'method-label': 'Method',
55
- 'method-totp': 'Authenticator app',
56
- 'method-webauthn': 'Passkey or security key',
57
- 'method-sms': 'Text message',
58
- 'at-label': 'Enabled',
59
- help: 'If this was not you, remove the factor and change your password immediately.',
60
- },
61
- 'security-alert': {
62
- subject: 'Security alert on your account',
63
- preheader: 'Confirm recent activity on your account.',
64
- heading: 'Security alert, {name}',
65
- alert: 'We noticed activity on your account that we want you to confirm.',
66
- 'event-label': 'Event',
67
- 'ip-label': 'IP address',
68
- 'at-label': 'When',
69
- help: 'If this was not you, change your password and sign out every other session.',
70
- },
71
- },
72
- };
73
- export const MAIL_CATALOG = loadCatalog(MAIL_CATALOG_SOURCE);
74
- /** Called at boot next to `registerFrameworkCatalog()`. Idempotent. */
75
- export function registerMailCatalog(locale = DEFAULT_LOCALE) {
76
- registerCatalog(locale, MAIL_CATALOG);
77
- }
78
- //# sourceMappingURL=catalog.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"catalog.js","sourceRoot":"","sources":["catalog.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,4FAA4F;AAC5F,uFAAuF;AACvF,kEAAkE;AAElE,OAAO,EAEL,cAAc,EAEd,WAAW,EAEX,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAExB,MAAM,CAAC,MAAM,mBAAmB,GAAkB;IAChD,IAAI,EAAE;QACJ,MAAM,EAAE;YACN,KAAK,EAAE,gEAAgE;YACvE,IAAI,EAAE,yDAAyD;YAC/D,WAAW,EAAE,aAAa;SAC3B;QACD,OAAO,EAAE;YACP,OAAO,EAAE,sBAAsB;YAC/B,SAAS,EAAE,kCAAkC;YAC7C,OAAO,EAAE,iBAAiB;YAC1B,IAAI,EAAE,uEAAuE;YAC7E,GAAG,EAAE,gBAAgB;SACtB;QACD,cAAc,EAAE;YACd,OAAO,EAAE,2BAA2B;YACpC,SAAS,EAAE,0CAA0C;YACrD,OAAO,EAAE,2BAA2B;YACpC,IAAI,EAAE,mFAAmF;YACzF,GAAG,EAAE,sBAAsB;YAC3B,UAAU,EAAE,sCAAsC;YAClD,YAAY,EAAE,uCAAuC;YACrD,MAAM,EAAE,0DAA0D;SACnE;QACD,gBAAgB,EAAE;YAChB,OAAO,EAAE,qBAAqB;YAC9B,SAAS,EAAE,kCAAkC;YAC7C,OAAO,EAAE,6BAA6B;YACtC,IAAI,EAAE,kFAAkF;YACxF,GAAG,EAAE,uBAAuB;YAC5B,UAAU,EAAE,sCAAsC;YAClD,YAAY,EAAE,uCAAuC;YACrD,MAAM,EAAE,gFAAgF;SACzF;QACD,MAAM,EAAE;YACN,OAAO,EAAE,wCAAwC;YACjD,SAAS,EAAE,+BAA+B;YAC1C,OAAO,EAAE,gBAAgB;YACzB,IAAI,EAAE,wDAAwD;YAC9D,GAAG,EAAE,mBAAmB;YACxB,UAAU,EAAE,0CAA0C;YACtD,YAAY,EAAE,2CAA2C;SAC1D;QACD,cAAc,EAAE;YACd,OAAO,EAAE,iCAAiC;YAC1C,SAAS,EAAE,gDAAgD;YAC3D,OAAO,EAAE,0BAA0B;YACnC,IAAI,EAAE,mEAAmE;YACzE,cAAc,EAAE,QAAQ;YACxB,aAAa,EAAE,mBAAmB;YAClC,iBAAiB,EAAE,yBAAyB;YAC5C,YAAY,EAAE,cAAc;YAC5B,UAAU,EAAE,SAAS;YACrB,IAAI,EAAE,8EAA8E;SACrF;QACD,gBAAgB,EAAE;YAChB,OAAO,EAAE,gCAAgC;YACzC,SAAS,EAAE,0CAA0C;YACrD,OAAO,EAAE,wBAAwB;YACjC,KAAK,EAAE,kEAAkE;YACzE,aAAa,EAAE,OAAO;YACtB,UAAU,EAAE,YAAY;YACxB,UAAU,EAAE,MAAM;YAClB,IAAI,EAAE,6EAA6E;SACpF;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAY,WAAW,CAAC,mBAAmB,CAAC,CAAC;AAEtE,uEAAuE;AACvE,MAAM,UAAU,mBAAmB,CAAC,MAAM,GAAW,cAAc;IACjE,eAAe,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AACxC,CAAC"}
package/src/driver.d.ts DELETED
@@ -1,79 +0,0 @@
1
- /** The rendered envelope. Everything a transport needs; nothing it does not. */
2
- export interface MailMessage {
3
- readonly mailId: string;
4
- readonly to: readonly string[];
5
- readonly subject: string;
6
- readonly html: string;
7
- readonly text: string;
8
- readonly locale: string;
9
- readonly tz: string;
10
- readonly replyTo?: string | undefined;
11
- readonly cc?: readonly string[] | undefined;
12
- readonly bcc?: readonly string[] | undefined;
13
- readonly unsubscribeUrl?: string | undefined;
14
- readonly idempotencyKey?: string | undefined;
15
- }
16
- export interface SendResult {
17
- /** Provider message id, or a local id for the memory/log drivers. */
18
- readonly id: string;
19
- readonly driver: string;
20
- readonly accepted: readonly string[];
21
- /** True when the message was handed to the queue instead of a transport. */
22
- readonly queued: boolean;
23
- readonly idempotencyKey: string;
24
- }
25
- export interface MailDriver {
26
- readonly name: string;
27
- send(message: MailMessage): Promise<SendResult>;
28
- }
29
- /**
30
- * RFC 8058 one-click unsubscribe. Gmail and Yahoo require it for bulk senders and
31
- * reward it for transactional ones, so it is computed here rather than per driver.
32
- */
33
- export declare function messageHeaders(message: MailMessage): Readonly<Record<string, string>>;
34
- export interface SentMail {
35
- readonly at: Date;
36
- readonly message: MailMessage;
37
- readonly result: SendResult;
38
- }
39
- /**
40
- * Dev + test driver. Retains every message it accepts and exposes them, which is what the
41
- * `/_x` mail panel reads to show the last mails rendered by the running app — a real inbox
42
- * is not part of the local loop.
43
- */
44
- export interface MemoryMailDriver extends MailDriver {
45
- readonly name: 'memory';
46
- readonly sent: readonly SentMail[];
47
- /** Newest first, so the panel and assertions do not index backwards. */
48
- outbox(): readonly SentMail[];
49
- lastTo(address: string): SentMail | undefined;
50
- clear(): void;
51
- }
52
- export declare function createMemoryDriver(): MemoryMailDriver;
53
- /**
54
- * Structured log line per message through core's `logger` — the default for a worker that
55
- * has no credentials yet. Bodies are never logged; a mail body is user data.
56
- */
57
- export declare function createLogDriver(logger?: import("@ultimat3/core").Logger): MailDriver;
58
- export interface SmtpDriverOptions {
59
- /** `smtps://user:pass@host:465`. Read from `SMTP_URL`, never hardcoded. */
60
- readonly url: string;
61
- readonly from: string;
62
- readonly poolSize?: number | undefined;
63
- }
64
- /** Interface-complete; the wire protocol is not in this build. */
65
- export declare function createSmtpDriver(options: SmtpDriverOptions): MailDriver;
66
- export interface ResendDriverOptions {
67
- /** Read from `RESEND_API_KEY`. */
68
- readonly apiKey: string;
69
- readonly from: string;
70
- }
71
- /** Interface-complete; the HTTP call is not in this build. */
72
- export declare function createResendDriver(options: ResendDriverOptions): MailDriver;
73
- /** Set once at boot from `app.config.ts`. One driver per process, like the job driver. */
74
- export declare function setMailDriver(driver: MailDriver): void;
75
- export declare function tryMailDriver(): MailDriver | undefined;
76
- /** Throws `X_MAIL_DRIVER_UNAVAILABLE` rather than silently dropping mail on the floor. */
77
- export declare function mailDriver(): MailDriver;
78
- export declare function resetMailDriver(): void;
79
- //# sourceMappingURL=driver.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["driver.ts"],"names":[],"mappings":"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"}
package/src/driver.js DELETED
@@ -1,115 +0,0 @@
1
- // Single responsibility: the transport seam. One `MailDriver` interface, four implementations,
2
- // and a module-level ambient driver so `send()` never knows which one is installed. Swapping
3
- // SMTP for Resend is an `app.config.ts` line and zero template changes.
4
- import { nanoid, logger as rootLogger } from '@ultimat3/core';
5
- import { driverUnavailable, transportNotImplemented } from './errors';
6
- /**
7
- * RFC 8058 one-click unsubscribe. Gmail and Yahoo require it for bulk senders and
8
- * reward it for transactional ones, so it is computed here rather than per driver.
9
- */
10
- export function messageHeaders(message) {
11
- const headers = { 'Auto-Submitted': 'auto-generated' };
12
- if (message.replyTo !== undefined)
13
- headers['Reply-To'] = message.replyTo;
14
- if (message.unsubscribeUrl !== undefined) {
15
- headers['List-Unsubscribe'] = `<${message.unsubscribeUrl}>`;
16
- headers['List-Unsubscribe-Post'] = 'List-Unsubscribe=One-Click';
17
- }
18
- return headers;
19
- }
20
- function accepted(message) {
21
- return [...message.to, ...(message.cc ?? []), ...(message.bcc ?? [])];
22
- }
23
- function resultFor(driver, message, id) {
24
- return {
25
- id,
26
- driver,
27
- accepted: accepted(message),
28
- queued: false,
29
- idempotencyKey: message.idempotencyKey ?? id,
30
- };
31
- }
32
- export function createMemoryDriver() {
33
- const sent = [];
34
- return {
35
- name: 'memory',
36
- sent,
37
- send(message) {
38
- const result = resultFor('memory', message, `mem_${nanoid(12)}`);
39
- sent.push({ at: new Date(), message, result });
40
- return Promise.resolve(result);
41
- },
42
- outbox: () => [...sent].reverse(),
43
- lastTo: (address) => [...sent].reverse().find((entry) => entry.message.to.includes(address)),
44
- clear: () => {
45
- sent.length = 0;
46
- },
47
- };
48
- }
49
- /**
50
- * Structured log line per message through core's `logger` — the default for a worker that
51
- * has no credentials yet. Bodies are never logged; a mail body is user data.
52
- */
53
- export function createLogDriver(logger = rootLogger) {
54
- return {
55
- name: 'log',
56
- send(message) {
57
- const result = resultFor('log', message, `log_${nanoid(12)}`);
58
- logger.info('mail.send', {
59
- mailId: message.mailId,
60
- to: message.to.length,
61
- subject: message.subject,
62
- locale: message.locale,
63
- idempotencyKey: result.idempotencyKey,
64
- });
65
- return Promise.resolve(result);
66
- },
67
- };
68
- }
69
- /** Interface-complete; the wire protocol is not in this build. */
70
- export function createSmtpDriver(options) {
71
- return {
72
- name: 'smtp',
73
- send(_message) {
74
- throw transportNotImplemented('smtp', `set SMTP_URL (currently "${redactUrl(options.url)}") and run: x mail doctor --json`);
75
- },
76
- };
77
- }
78
- /** Interface-complete; the HTTP call is not in this build. */
79
- export function createResendDriver(options) {
80
- return {
81
- name: 'resend',
82
- send(_message) {
83
- const state = options.apiKey === '' ? 'empty' : 'set';
84
- throw transportNotImplemented('resend', `set RESEND_API_KEY (currently ${state}), then run: x mail doctor --json`);
85
- },
86
- };
87
- }
88
- function redactUrl(url) {
89
- try {
90
- const parsed = new URL(url);
91
- parsed.password = '';
92
- return parsed.href;
93
- }
94
- catch {
95
- return 'not a URL';
96
- }
97
- }
98
- let ambient;
99
- /** Set once at boot from `app.config.ts`. One driver per process, like the job driver. */
100
- export function setMailDriver(driver) {
101
- ambient = driver;
102
- }
103
- export function tryMailDriver() {
104
- return ambient;
105
- }
106
- /** Throws `X_MAIL_DRIVER_UNAVAILABLE` rather than silently dropping mail on the floor. */
107
- export function mailDriver() {
108
- if (ambient === undefined)
109
- throw driverUnavailable('setMailDriver() was never called');
110
- return ambient;
111
- }
112
- export function resetMailDriver() {
113
- ambient = undefined;
114
- }
115
- //# sourceMappingURL=driver.js.map
package/src/driver.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"driver.js","sourceRoot":"","sources":["driver.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,6FAA6F;AAC7F,wEAAwE;AAExE,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAiCtE;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,OAAoB;IACjD,MAAM,OAAO,GAA2B,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;IAC/E,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IACzE,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;QACzC,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI,OAAO,CAAC,cAAc,GAAG,CAAC;QAC5D,OAAO,CAAC,uBAAuB,CAAC,GAAG,4BAA4B,CAAC;IAClE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,QAAQ,CAAC,OAAoB;IACpC,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,SAAS,SAAS,CAAC,MAAc,EAAE,OAAoB,EAAE,EAAU;IACjE,OAAO;QACL,EAAE;QACF,MAAM;QACN,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC;QAC3B,MAAM,EAAE,KAAK;QACb,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,EAAE;KAC7C,CAAC;AACJ,CAAC;AAsBD,MAAM,UAAU,kBAAkB;IAChC,MAAM,IAAI,GAAe,EAAE,CAAC;IAC5B,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,IAAI;QACJ,IAAI,CAAC,OAAoB;YACvB,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACjE,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YAC/C,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC;QACD,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,EAAE;QACjC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC5F,KAAK,EAAE,GAAG,EAAE;YACV,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAAM,GAAG,UAAU;IACjD,OAAO;QACL,IAAI,EAAE,KAAK;QACX,IAAI,CAAC,OAAoB;YACvB,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9D,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;gBACvB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,MAAM;gBACrB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,cAAc,EAAE,MAAM,CAAC,cAAc;aACtC,CAAC,CAAC;YACH,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC;KACF,CAAC;AACJ,CAAC;AASD,kEAAkE;AAClE,MAAM,UAAU,gBAAgB,CAAC,OAA0B;IACzD,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,IAAI,CAAC,QAAqB;YACxB,MAAM,uBAAuB,CAC3B,MAAM,EACN,4BAA4B,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,kCAAkC,CACrF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAQD,8DAA8D;AAC9D,MAAM,UAAU,kBAAkB,CAAC,OAA4B;IAC7D,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,IAAI,CAAC,QAAqB;YACxB,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YACtD,MAAM,uBAAuB,CAC3B,QAAQ,EACR,iCAAiC,KAAK,mCAAmC,CAC1E,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC;QACrB,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,WAAW,CAAC;IACrB,CAAC;AACH,CAAC;AAED,IAAI,OAA+B,CAAC;AAEpC,0FAA0F;AAC1F,MAAM,UAAU,aAAa,CAAC,MAAkB;IAC9C,OAAO,GAAG,MAAM,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,UAAU;IACxB,IAAI,OAAO,KAAK,SAAS;QAAE,MAAM,iBAAiB,CAAC,kCAAkC,CAAC,CAAC;IACvF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,GAAG,SAAS,CAAC;AACtB,CAAC"}
package/src/errors.d.ts DELETED
@@ -1,23 +0,0 @@
1
- import { UltimateError } from '@ultimat3/core';
2
- export declare const MAIL_ERROR_CODES: readonly ['X_MAIL_LOCALE_MISSING', 'X_MAIL_TEMPLATE_UNKNOWN', 'X_MAIL_DUPLICATE', 'X_MAIL_TEXT_MISSING', 'X_MAIL_DRIVER_UNAVAILABLE', 'X_NOT_IMPLEMENTED'];
3
- export type MailErrorCode = (typeof MAIL_ERROR_CODES)[number];
4
- export declare const MAIL_ERROR_TITLES: Readonly<Record<MailErrorCode, string>>;
5
- export interface MailErrorInit {
6
- readonly code: MailErrorCode;
7
- readonly cause: string;
8
- readonly fix: string;
9
- readonly meta?: Readonly<Record<string, unknown>> | undefined;
10
- }
11
- export declare class MailError extends UltimateError {
12
- readonly name = "MailError";
13
- constructor(init: MailErrorInit);
14
- }
15
- export declare const localeMissing: (mailId: string) => MailError;
16
- export declare const templateUnknown: (mailId: string, known: readonly string[]) => MailError;
17
- export declare const layoutUnknown: (mailId: string, layout: string, known: readonly string[]) => MailError;
18
- export declare const mailDuplicate: (mailId: string) => MailError;
19
- export declare const textMissing: (mailId: string) => MailError;
20
- export declare const driverUnavailable: (what: string) => MailError;
21
- /** The blessed shape for a transport that exists as an interface but has no wire yet. */
22
- export declare const transportNotImplemented: (driver: string, fix: string) => MailError;
23
- //# sourceMappingURL=errors.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["errors.ts"],"names":[],"mappings":"AAIA,OAAO,EAAoC,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEjF,eAAO,MAAM,gBAAgB,YAC3B,uBAAuB,EACvB,yBAAyB,EACzB,kBAAkB,EAClB,qBAAqB,EACrB,2BAA2B,EAC3B,mBAAmB,CACX,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9D,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAOrE,CAAC;AAQF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;CAC/D;AAED,qBAAa,SAAU,SAAQ,aAAa;IAC1C,SAAkB,IAAI,eAAe;IAErC,YAAY,IAAI,EAAE,aAAa,EAQ9B;CACF;AAED,eAAO,MAAM,aAAa,WAAY,MAAM,KAAG,SAM3C,CAAC;AAEL,eAAO,MAAM,eAAe,WAAY,MAAM,SAAS,SAAS,MAAM,EAAE,KAAG,SAMvE,CAAC;AAEL,eAAO,MAAM,aAAa,WAChB,MAAM,UACN,MAAM,SACP,SAAS,MAAM,EAAE,KACvB,SAMC,CAAC;AAEL,eAAO,MAAM,aAAa,WAAY,MAAM,KAAG,SAM3C,CAAC;AAEL,eAAO,MAAM,WAAW,WAAY,MAAM,KAAG,SAMzC,CAAC;AAEL,eAAO,MAAM,iBAAiB,SAAU,MAAM,KAAG,SAM7C,CAAC;AAEL,yFAAyF;AACzF,eAAO,MAAM,uBAAuB,WAAY,MAAM,OAAO,MAAM,KAAG,SAMlE,CAAC"}
package/src/errors.js DELETED
@@ -1,82 +0,0 @@
1
- // Single responsibility: the mail package's stable error codes and their `fix:` lines.
2
- // Mail fails in production, not in tests — a wrong locale, an empty text part or an
3
- // unconfigured driver must name the exact call site edit that repairs it.
4
- import { hasErrorCode, registerErrorCodes, UltimateError } from '@ultimat3/core';
5
- export const MAIL_ERROR_CODES = [
6
- 'X_MAIL_LOCALE_MISSING',
7
- 'X_MAIL_TEMPLATE_UNKNOWN',
8
- 'X_MAIL_DUPLICATE',
9
- 'X_MAIL_TEXT_MISSING',
10
- 'X_MAIL_DRIVER_UNAVAILABLE',
11
- 'X_NOT_IMPLEMENTED',
12
- ];
13
- export const MAIL_ERROR_TITLES = {
14
- X_MAIL_LOCALE_MISSING: 'send() was called without a locale',
15
- X_MAIL_TEMPLATE_UNKNOWN: 'no mail is registered under that id',
16
- X_MAIL_DUPLICATE: 'two mails claim the same id',
17
- X_MAIL_TEXT_MISSING: 'the rendered mail has no plain-text part',
18
- X_MAIL_DRIVER_UNAVAILABLE: 'no mail driver is configured',
19
- X_NOT_IMPLEMENTED: 'this driver has no transport yet',
20
- };
21
- // Titles must be registered for `format()` to render the contract's first line, but
22
- // `X_NOT_IMPLEMENTED` belongs to core — registering a code twice throws X_ERROR_CODE_DUPLICATE.
23
- for (const [code, title] of Object.entries(MAIL_ERROR_TITLES)) {
24
- if (!hasErrorCode(code))
25
- registerErrorCodes({ [code]: { title } });
26
- }
27
- export class MailError extends UltimateError {
28
- name = 'MailError';
29
- constructor(init) {
30
- super({
31
- code: init.code,
32
- cause: init.cause,
33
- fix: init.fix,
34
- docs: `https://ultimate.dev/errors/${init.code}`,
35
- meta: init.meta,
36
- });
37
- }
38
- }
39
- export const localeMissing = (mailId) => new MailError({
40
- code: 'X_MAIL_LOCALE_MISSING',
41
- cause: `send(${mailId}, data, options) got no locale — the recipient's language is unknown`,
42
- fix: `pass it: send(${mailId}, data, { to, locale: ctx.locale })`,
43
- meta: { mailId },
44
- });
45
- export const templateUnknown = (mailId, known) => new MailError({
46
- code: 'X_MAIL_TEMPLATE_UNKNOWN',
47
- cause: `no mail with id "${mailId}" is registered (have: ${known.join(', ') || 'none'})`,
48
- fix: `x mail list --json # then export defineMail({ id: '${mailId}', ... }) and import it`,
49
- meta: { mailId, known },
50
- });
51
- export const layoutUnknown = (mailId, layout, known) => new MailError({
52
- code: 'X_MAIL_TEMPLATE_UNKNOWN',
53
- cause: `mail "${mailId}" wants layout "${layout}" (registered: ${known.join(', ')})`,
54
- fix: `registerLayout('${layout}', myLayout) at boot, or set layout: 'base' on that mail`,
55
- meta: { mailId, layout, known },
56
- });
57
- export const mailDuplicate = (mailId) => new MailError({
58
- code: 'X_MAIL_DUPLICATE',
59
- cause: `mail id "${mailId}" is already registered by another defineMail() call`,
60
- fix: `x mail list --json # then rename one of the two defineMail({ id }) declarations`,
61
- meta: { mailId },
62
- });
63
- export const textMissing = (mailId) => new MailError({
64
- code: 'X_MAIL_TEXT_MISSING',
65
- cause: `mail "${mailId}" rendered an empty text part (HTML-only mail scores as spam)`,
66
- fix: `add a text block to the "${mailId}" template: blocks.paragraph('mail.${mailId}.body')`,
67
- meta: { mailId },
68
- });
69
- export const driverUnavailable = (what) => new MailError({
70
- code: 'X_MAIL_DRIVER_UNAVAILABLE',
71
- cause: `${what} — nothing can deliver the message`,
72
- fix: 'setMailDriver(createMemoryDriver()) in dev, createSmtpDriver({ url: env.SMTP_URL }) live',
73
- meta: { what },
74
- });
75
- /** The blessed shape for a transport that exists as an interface but has no wire yet. */
76
- export const transportNotImplemented = (driver, fix) => new MailError({
77
- code: 'X_NOT_IMPLEMENTED',
78
- cause: `the ${driver} mail driver has no transport in this build`,
79
- fix,
80
- meta: { driver },
81
- });
82
- //# sourceMappingURL=errors.js.map
package/src/errors.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["errors.ts"],"names":[],"mappings":"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"}
package/src/html.d.ts DELETED
@@ -1,10 +0,0 @@
1
- /** Escape text content AND attribute values — the same set covers both in mail. */
2
- export declare function escapeHtml(value: string): string;
3
- /**
4
- * `javascript:` and `data:` hrefs are stripped rather than escaped: some clients still
5
- * follow them, and a link the recipient cannot trust is worse than a dead one.
6
- */
7
- export declare function safeUrl(value: string): string;
8
- /** `style="a:b;c:d"` from declarations, so no call site hand-builds a style attribute. */
9
- export declare function styleAttr(declarations: readonly string[]): string;
10
- //# sourceMappingURL=html.d.ts.map
package/src/html.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["html.ts"],"names":[],"mappings":"AAYA,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAID;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ7C;AAED,0FAA0F;AAC1F,wBAAgB,SAAS,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAEjE"}
package/src/html.js DELETED
@@ -1,34 +0,0 @@
1
- // Single responsibility: turning untrusted values into inert HTML. Mail bodies interpolate
2
- // user-supplied names, org names and URLs; an unescaped one is a phishing vector that also
3
- // renders in the recipient's client forever, so escaping lives in exactly one place.
4
- const ESCAPES = {
5
- '&': '&amp;',
6
- '<': '&lt;',
7
- '>': '&gt;',
8
- '"': '&quot;',
9
- "'": '&#39;',
10
- };
11
- /** Escape text content AND attribute values — the same set covers both in mail. */
12
- export function escapeHtml(value) {
13
- return value.replace(/[&<>"']/g, (char) => ESCAPES[char] ?? char);
14
- }
15
- const SAFE_PROTOCOLS = ['http:', 'https:', 'mailto:'];
16
- /**
17
- * `javascript:` and `data:` hrefs are stripped rather than escaped: some clients still
18
- * follow them, and a link the recipient cannot trust is worse than a dead one.
19
- */
20
- export function safeUrl(value) {
21
- let parsed;
22
- try {
23
- parsed = new URL(value);
24
- }
25
- catch {
26
- return '#';
27
- }
28
- return SAFE_PROTOCOLS.includes(parsed.protocol) ? parsed.href : '#';
29
- }
30
- /** `style="a:b;c:d"` from declarations, so no call site hand-builds a style attribute. */
31
- export function styleAttr(declarations) {
32
- return `style="${escapeHtml(declarations.join(';'))}"`;
33
- }
34
- //# sourceMappingURL=html.js.map
package/src/html.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"html.js","sourceRoot":"","sources":["html.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,2FAA2F;AAC3F,qFAAqF;AAErF,MAAM,OAAO,GAAqC;IAChD,GAAG,EAAE,OAAO;IACZ,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,OAAO;CACb,CAAC;AAEF,mFAAmF;AACnF,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,cAAc,GAAsB,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;AAEzE;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;AACtE,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,SAAS,CAAC,YAA+B;IACvD,OAAO,UAAU,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;AACzD,CAAC"}
package/src/index.d.ts DELETED
@@ -1,17 +0,0 @@
1
- export type { CalloutTone, MailBlock, MailTemplate, TemplateArgs } from './blocks';
2
- export { blocks } from './blocks';
3
- export { MAIL_CATALOG, MAIL_CATALOG_SOURCE, registerMailCatalog } from './catalog';
4
- export type { MailDriver, MailMessage, MemoryMailDriver, ResendDriverOptions, SendResult, SentMail, SmtpDriverOptions, } from './driver';
5
- export { createLogDriver, createMemoryDriver, createResendDriver, createSmtpDriver, mailDriver, messageHeaders, resetMailDriver, setMailDriver, tryMailDriver, } from './driver';
6
- export type { MailErrorCode, MailErrorInit } from './errors';
7
- export { driverUnavailable, layoutUnknown, localeMissing, MAIL_ERROR_CODES, MAIL_ERROR_TITLES, MailError, mailDuplicate, templateUnknown, textMissing, transportNotImplemented, } from './errors';
8
- export { escapeHtml, safeUrl } from './html';
9
- export { mailIdempotencyKey, mailMessageSchema, sendMailJob } from './job';
10
- export type { ColorScheme, DarkRule, LayoutInput, MailLayout, MailToken, UnsubscribeSlot, } from './layout';
11
- export { BASE_LAYOUT, baseLayout, DARK_RULES, darkModeCss, layoutFor, MAIL_FONT_STACK, MAIL_TOKENS, MAIL_WIDTH_PX, registeredLayouts, registerLayout, token, } from './layout';
12
- export type { AnyMailDefinition, MailDefinition, MailInit, SendOptions } from './mail';
13
- export { defineMail, mailFor, registeredMailIds, registeredMails, renderMessage, resetMails, send, sendById, } from './mail';
14
- export type { RenderableMail, RenderedMail, RenderOptions } from './render';
15
- export { FOOTER_KEYS, renderMail, textOf, UNSUBSCRIBE_KEY } from './render';
16
- export { FRAMEWORK_MAILS, type InviteInput, inviteInput, inviteMail, MFA_METHODS, type MfaEnrolledInput, mfaEnrolledInput, mfaEnrolledMail, type ResetPasswordInput, resetPasswordInput, resetPasswordMail, type SecurityAlertInput, securityAlertInput, securityAlertMail, type VerifyEmailInput, verifyEmailInput, verifyEmailMail, type WelcomeInput, welcomeInput, welcomeMail, } from './templates';
17
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACnF,YAAY,EACV,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,mBAAmB,EACnB,UAAU,EACV,QAAQ,EACR,iBAAiB,GAClB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,eAAe,EACf,aAAa,EACb,aAAa,GACd,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EACT,aAAa,EACb,eAAe,EACf,WAAW,EACX,uBAAuB,GACxB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAE7C,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAC3E,YAAY,EACV,WAAW,EACX,QAAQ,EACR,WAAW,EACX,UAAU,EACV,SAAS,EACT,eAAe,GAChB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,WAAW,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,SAAS,EACT,eAAe,EACf,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,KAAK,GACN,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACvF,OAAO,EACL,UAAU,EACV,OAAO,EACP,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,UAAU,EACV,IAAI,EACJ,QAAQ,GACT,MAAM,QAAQ,CAAC;AAChB,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE5E,OAAO,EACL,eAAe,EACf,KAAK,WAAW,EAChB,WAAW,EACX,UAAU,EACV,WAAW,EACX,KAAK,gBAAgB,EACrB,gBAAgB,EAChB,eAAe,EACf,KAAK,kBAAkB,EACvB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,gBAAgB,EAChB,eAAe,EACf,KAAK,YAAY,EACjB,YAAY,EACZ,WAAW,GACZ,MAAM,aAAa,CAAC"}
package/src/index.js DELETED
@@ -1,13 +0,0 @@
1
- // Single responsibility: the public API of @ultimat3/mail. Explicit named exports only —
2
- // other packages call `defineMail`, `send` and the driver seam, and nothing else.
3
- export { blocks } from './blocks';
4
- export { MAIL_CATALOG, MAIL_CATALOG_SOURCE, registerMailCatalog } from './catalog';
5
- export { createLogDriver, createMemoryDriver, createResendDriver, createSmtpDriver, mailDriver, messageHeaders, resetMailDriver, setMailDriver, tryMailDriver, } from './driver';
6
- export { driverUnavailable, layoutUnknown, localeMissing, MAIL_ERROR_CODES, MAIL_ERROR_TITLES, MailError, mailDuplicate, templateUnknown, textMissing, transportNotImplemented, } from './errors';
7
- export { escapeHtml, safeUrl } from './html';
8
- export { mailIdempotencyKey, mailMessageSchema, sendMailJob } from './job';
9
- export { BASE_LAYOUT, baseLayout, DARK_RULES, darkModeCss, layoutFor, MAIL_FONT_STACK, MAIL_TOKENS, MAIL_WIDTH_PX, registeredLayouts, registerLayout, token, } from './layout';
10
- export { defineMail, mailFor, registeredMailIds, registeredMails, renderMessage, resetMails, send, sendById, } from './mail';
11
- export { FOOTER_KEYS, renderMail, textOf, UNSUBSCRIBE_KEY } from './render';
12
- export { FRAMEWORK_MAILS, inviteInput, inviteMail, MFA_METHODS, mfaEnrolledInput, mfaEnrolledMail, resetPasswordInput, resetPasswordMail, securityAlertInput, securityAlertMail, verifyEmailInput, verifyEmailMail, welcomeInput, welcomeMail, } from './templates';
13
- //# sourceMappingURL=index.js.map
package/src/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,kFAAkF;AAGlF,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAUnF,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,eAAe,EACf,aAAa,EACb,aAAa,GACd,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EACT,aAAa,EACb,eAAe,EACf,WAAW,EACX,uBAAuB,GACxB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAE7C,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAS3E,OAAO,EACL,WAAW,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,SAAS,EACT,eAAe,EACf,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,KAAK,GACN,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,UAAU,EACV,OAAO,EACP,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,UAAU,EACV,IAAI,EACJ,QAAQ,GACT,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE5E,OAAO,EACL,eAAe,EAEf,WAAW,EACX,UAAU,EACV,WAAW,EAEX,gBAAgB,EAChB,eAAe,EAEf,kBAAkB,EAClB,iBAAiB,EAEjB,kBAAkB,EAClB,iBAAiB,EAEjB,gBAAgB,EAChB,eAAe,EAEf,YAAY,EACZ,WAAW,GACZ,MAAM,aAAa,CAAC"}