@nxgt/janus-mail 0.3.2 → 0.4.1

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 (44) hide show
  1. package/README.md +63 -15
  2. package/dist/generated/mail.d.ts +5 -0
  3. package/dist/generated/mail.d.ts.map +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +19 -15
  6. package/dist/index.js.map +6 -6
  7. package/dist/janus-mail.d.ts +1 -0
  8. package/dist/janus-mail.d.ts.map +1 -1
  9. package/dist/notice-mails.d.ts +7 -5
  10. package/dist/notice-mails.d.ts.map +1 -1
  11. package/dist/options.d.ts.map +1 -1
  12. package/dist/template-types.d.ts +79 -0
  13. package/dist/template-types.d.ts.map +1 -0
  14. package/dist/templates.d.ts +1 -1
  15. package/dist/templates.d.ts.map +1 -1
  16. package/dist/types.d.ts +17 -67
  17. package/dist/types.d.ts.map +1 -1
  18. package/docs/README.md +4 -3
  19. package/docs/guide/building.md +7 -5
  20. package/docs/guide/locales.md +4 -3
  21. package/docs/guide/sending.md +80 -38
  22. package/docs/guide/templates.md +26 -14
  23. package/docs/roadmap.md +19 -4
  24. package/docs/troubleshooting.md +79 -22
  25. package/mails/en/email-changed.html +9 -9
  26. package/mails/en/password-changed.html +9 -9
  27. package/mails/en/reset-password.html +9 -9
  28. package/mails/en/sign-in-code.html +9 -9
  29. package/mails/en/two-factor-disabled.html +9 -9
  30. package/mails/en/two-factor-enabled.html +9 -9
  31. package/mails/en/verify-email.html +9 -9
  32. package/mails/en/welcome.html +50 -0
  33. package/mails/en/welcome.txt +19 -0
  34. package/mails/fr/email-changed.html +9 -9
  35. package/mails/fr/password-changed.html +9 -9
  36. package/mails/fr/reset-password.html +9 -9
  37. package/mails/fr/sign-in-code.html +9 -9
  38. package/mails/fr/two-factor-disabled.html +9 -9
  39. package/mails/fr/two-factor-enabled.html +9 -9
  40. package/mails/fr/verify-email.html +9 -9
  41. package/mails/fr/welcome.html +50 -0
  42. package/mails/fr/welcome.txt +19 -0
  43. package/mails/mail-manifest.json +24 -0
  44. package/package.json +7 -6
package/dist/types.d.ts CHANGED
@@ -4,75 +4,11 @@
4
4
  * once, in `janusMail()`.
5
5
  */
6
6
  import type { Clock, IssuedCode, IssuedToken } from '@nxgt/janus';
7
- import type { Address, Mailer, Rendered, SentMail, WantedLocales } from '@nxgt/mail';
7
+ import type { Address, Mailer, SentMail, WantedLocales } from '@nxgt/mail';
8
8
  import type { JanusMailLocale } from './generated/locales';
9
+ import type { JanusMailTemplates } from './template-types';
9
10
  export type { JanusMailLocale } from './generated/locales';
10
- /**
11
- * What each e-mail's template is given, by template name. Every value is
12
- * text: `brand` is the name `janusMail({ brand })` was given, `name` the
13
- * recipient's, `link` an absolute URL from `links`, and `expiresIn` how long
14
- * the link or the code stays valid, in the recipient's locale — "1 hour",
15
- * "1 heure" — or the send's own `expiresIn`.
16
- *
17
- * Held equal to the build's variables by `test/types/variables.ts`: an e-mail
18
- * of the build that gains or loses a variable fails the typecheck there.
19
- */
20
- export interface JanusMailVariables {
21
- readonly verifyEmail: {
22
- readonly brand: string;
23
- readonly name: string;
24
- readonly link: string;
25
- readonly expiresIn: string;
26
- };
27
- readonly resetPassword: {
28
- readonly brand: string;
29
- readonly name: string;
30
- readonly link: string;
31
- readonly expiresIn: string;
32
- };
33
- /** No `challenge`, ever: it is the visitor's secret, and never goes in an e-mail. */
34
- readonly signInCode: {
35
- readonly brand: string;
36
- readonly code: string;
37
- readonly expiresIn: string;
38
- };
39
- readonly passwordChanged: {
40
- readonly brand: string;
41
- readonly name: string;
42
- readonly link: string;
43
- };
44
- readonly emailChanged: {
45
- readonly brand: string;
46
- readonly name: string;
47
- readonly link: string;
48
- readonly newEmail: string;
49
- };
50
- readonly twoFactorEnabled: {
51
- readonly brand: string;
52
- readonly name: string;
53
- readonly link: string;
54
- };
55
- readonly twoFactorDisabled: {
56
- readonly brand: string;
57
- readonly name: string;
58
- readonly link: string;
59
- };
60
- }
61
- /** The name of one of the seven templates: `verifyEmail`, `resetPassword`, … */
62
- export type JanusMailTemplateName = keyof JanusMailVariables;
63
- /**
64
- * One e-mail: its variables and the locale picked for the recipient, in; its
65
- * `subject`, `html` and `text`, out — escaping is then the function's job. A
66
- * default, from the prebuilt e-mails, or yours: React Email, a string, or
67
- * another Maizzle build.
68
- */
69
- export type JanusMailTemplate<V, L extends string = JanusMailLocale> = (variables: V & {
70
- readonly locale: L;
71
- }) => Rendered | PromiseLike<Rendered>;
72
- /** The seven templates, each for the locales `L`. */
73
- export type JanusMailTemplates<L extends string = JanusMailLocale> = {
74
- readonly [K in JanusMailTemplateName]: JanusMailTemplate<JanusMailVariables[K], L>;
75
- };
11
+ export type { JanusMailTemplate, JanusMailTemplateName, JanusMailTemplates, JanusMailVariables, } from './template-types';
76
12
  /**
77
13
  * Who an e-mail is for, besides the address: the name it greets, and the
78
14
  * locales they want, most wanted first — their stored locale, then their
@@ -105,6 +41,12 @@ export interface JanusMailLinks {
105
41
  * and `twoFactorDisabled` link to it.
106
42
  */
107
43
  readonly secureAccount: () => string;
44
+ /**
45
+ * Where a new user starts — your application's home, or its sign-in page
46
+ * for an account someone else created. `welcome` links to it, from its
47
+ * "Get started" button.
48
+ */
49
+ readonly getStarted: () => string;
108
50
  }
109
51
  /** The options every locale set shares. */
110
52
  interface JanusMailBaseOptions<L extends string> {
@@ -196,6 +138,14 @@ export interface JanusMail<L extends string = JanusMailLocale> {
196
138
  twoFactorDisabled(to: Recipient & {
197
139
  readonly email: string;
198
140
  }): Promise<SentMail>;
141
+ /**
142
+ * Welcomes `to.email` to the brand, with a link to get started — on the
143
+ * `user.created` event, which `auth.create` and `auth.signUp` send once
144
+ * the user is inserted.
145
+ */
146
+ welcome(to: Recipient & {
147
+ readonly email: string;
148
+ }): Promise<SentMail>;
199
149
  /** The templates in use: the defaults, with `templates` over them. */
200
150
  readonly templates: JanusMailTemplates<L>;
201
151
  /** The locales sent in. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/docs/README.md CHANGED
@@ -5,11 +5,11 @@ the detail.
5
5
 
6
6
  | Page | Read it when |
7
7
  | --- | --- |
8
- | [Sending](guide/sending.md) | You are wiring `janusMail()` into your routes: the options — `clock` among them — each method, the expiry an e-mail states and how to set it, where each e-mail goes, what a failure looks like, retrying and tracing the mailer, and a test |
8
+ | [Sending](guide/sending.md) | You are wiring `janusMail()` into your routes: the options, `clock` among them; each method, the welcome on `user.created` among them; the expiry an e-mail states and how to set it, where each e-mail goes, what a failure looks like, retrying and tracing the mailer, and a test |
9
9
  | [Templates](guide/templates.md) | You want an e-mail of your own: what a template takes and answers, a partial override, `janusTemplates()`, and what the defaults say |
10
10
  | [Locales](guide/locales.md) | You send in more than one language: how the locale is picked, `fallbackLocale`, fewer locales, and adding one |
11
11
  | [Building](guide/building.md) | You work on this package: the Maizzle project in `mail/`, `mails/`, the generated files, and why none of it reaches a consumer's build |
12
- | [Troubleshooting](troubleshooting.md) | A `TypeError` at wiring or at a call, a `MailRefused`, a `MailFailure`, or a compile error |
12
+ | [Troubleshooting](troubleshooting.md) | A `TypeError` at wiring or at a call, a `MailRefused`, a `MailFailure`, a compile error, or an e-mail that reads wrong in dark or light mode |
13
13
  | [Roadmap](roadmap.md) | You want to know what is coming, and what is deliberately not planned |
14
14
 
15
15
  ## Words
@@ -23,13 +23,14 @@ They mean the same here. These pages add:
23
23
 
24
24
  | Word | Means | Not |
25
25
  | --- | --- | --- |
26
- | **e-mail** | One of the seven this package sends: `verify-email`, `reset-password`, `sign-in-code`, `password-changed`, `email-changed`, `two-factor-enabled`, `two-factor-disabled` — by the name of its built file | "mail", "message" — a *message* is what a mailer sends, addressed |
26
+ | **e-mail** | One of the eight this package sends: `verify-email`, `reset-password`, `sign-in-code`, `password-changed`, `email-changed`, `two-factor-enabled`, `two-factor-disabled`, `welcome` — by the name of its built file | "mail", "message" — a *message* is what a mailer sends, addressed |
27
27
  | **template** | The function that renders one e-mail: `(variables & { locale }) => Rendered`. Named in camelCase, after the method that sends it: `verifyEmail`, `signInCode` | "renderer" — the renderer is `@nxgt/mail`'s, and reads the build |
28
28
  | **default template** | A template of `janusTemplates()`: the prebuilt e-mail, filled by the renderer | "preset" — a preset is `@nxgt/mail-presets`' source, which the build compiles |
29
29
  | **override** | A template of yours, passed in `templates`, used instead of the default | "custom template" |
30
30
  | **issued** | What a flow answered: an `IssuedToken` from `verifyEmail.send` or `resetPassword.request`, an `IssuedCode` from `signInCode.request` | "result", "payload" |
31
31
  | **recipient** | Who an e-mail is for besides the address: a `Recipient`, `{ name, locale? }` | "user" — a user is a record of `@nxgt/janus`; "to" is the address |
32
32
  | **notice** | An e-mail that tells, and asks for nothing: `passwordChanged`, `emailChanged`, `twoFactorEnabled`, `twoFactorDisabled`. It links to `links.secureAccount()` | "alert", "notification" |
33
+ | **welcome** | The e-mail sent once a user was created, on `@nxgt/janus`'s `user.created`: `welcome`. It links to `links.getStarted()` — not a notice, since it warns of nothing | "onboarding", "greeting" |
33
34
  | **expiry** | How long a link or a code lasts, as the e-mail says it: `expiresIn`, derived from the flow's `expiresAt` in the recipient's locale — "1 hour", "1 heure" — or passed per send | "TTL", "lifetime" — the `tokens` TTL is `@nxgt/janus`'s setting, which sets `expiresAt` |
34
35
  | **brand** | The name the e-mails show, `janusMail({ brand })`: text, filled at send time | "logo", "company" |
35
36
  | **the build** | The e-mails as `scripts/build-mail.ts` writes them to `mails/`: HTML and text per locale, and the manifest | "dist" — `mails/` is not `dist/` |
@@ -43,7 +43,7 @@ packages/janus-mail/
43
43
  `src/generated/mail.ts` (`rendererTypes`), from the build.
44
44
  3. `scripts/build-mail.ts` reads the manifest and **fails unless its
45
45
  format is one every `@nxgt/mail` the peer admits reads** — see below —
46
- and **unless exactly the seven e-mails were built**: an eighth from a new
46
+ and **unless exactly the eight e-mails were built**: a ninth from a new
47
47
  `@nxgt/mail-presets`, or one missing, stops the build.
48
48
  4. It writes `src/generated/locales.ts` from the manifest's locales, only
49
49
  when its content changed.
@@ -53,10 +53,12 @@ Then `../../build.ts` builds `dist/`, as for every package.
53
53
  ## The manifest's format keeps the peer honest
54
54
 
55
55
  The package peers `@nxgt/mail` at `>=0.1.0 <1`, so the build it ships must
56
- be readable by `@nxgt/mail` 0.1.0. The manifest says which format it is in —
57
- `formatVersion`, its first key, `MANIFEST_FORMAT` of the `@nxgt/mail-i18n`
58
- that built it — and within 0.x a renderer reads every format up to its own.
59
- `@nxgt/mail` 0.1.0 through 0.8.0 read format 1, and a manifest without the
56
+ be readable by `@nxgt/mail` 0.1.0 — the peer's floor, which the package's
57
+ specs and its typecheck run on in the Floors job, on every CI run. The
58
+ manifest says which format it is in — `formatVersion`, its first key,
59
+ `MANIFEST_FORMAT` of the `@nxgt/mail-i18n` that built it — and within 0.x a
60
+ renderer reads every format up to its own.
61
+ `@nxgt/mail` 0.1.0 through 0.9.0 read format 1, and a manifest without the
60
62
  field is format 1.
61
63
 
62
64
  `formatProblem` in `scripts/build-mail.ts` (spec'd beside it) fails the build
@@ -85,6 +85,7 @@ const templates: JanusMailTemplates<Locale> = {
85
85
  emailChanged: ({ name, link, newEmail, locale }) => render('email-changed', locale, { name, link, newEmail }),
86
86
  twoFactorEnabled: ({ name, link, locale }) => render('two-factor-enabled', locale, { name, link }),
87
87
  twoFactorDisabled: ({ name, link, locale }) => render('two-factor-disabled', locale, { name, link }),
88
+ welcome: ({ name, link, locale }) => render('welcome', locale, { name, link }),
88
89
  };
89
90
 
90
91
  const mail = janusMail({ mailer, from, brand: 'Acme', links, locales: ['en', 'fr', 'de'], templates });
@@ -99,13 +100,13 @@ With only some templates, the call does not compile — the error lands on
99
100
  `templates`:
100
101
 
101
102
  ```text
102
- TS2739: Type '{ verifyEmail: () => Rendered; }' is missing the following properties from type 'JanusMailTemplates<"en" | "fr" | "de">': resetPassword, signInCode, passwordChanged, emailChanged, twoFactorEnabled, twoFactorDisabled
103
+ TS2739: Type '{ verifyEmail: () => Rendered; }' is missing the following properties from type 'JanusMailTemplates<"en" | "fr" | "de">': resetPassword, signInCode, passwordChanged, emailChanged, twoFactorEnabled, twoFactorDisabled, welcome
103
104
  ```
104
105
 
105
106
  and in JavaScript it throws when `janusMail()` is called:
106
107
 
107
108
  ```text
108
- TypeError: janusMail: the default templates are built in en and fr only — with another locale in locales, pass every template in templates; resetPassword, signInCode, passwordChanged, emailChanged, twoFactorEnabled, twoFactorDisabled missing
109
+ TypeError: janusMail: the default templates are built in en and fr only — with another locale in locales, pass every template in templates; resetPassword, signInCode, passwordChanged, emailChanged, twoFactorEnabled, twoFactorDisabled, welcome missing
109
110
  ```
110
111
 
111
112
  A default template is typed for `'en' | 'fr'`, so reusing one in a wider set
@@ -118,4 +119,4 @@ Built-in locales for more languages are on the [roadmap](../roadmap.md).
118
119
 
119
120
  - `@nxgt/mail`'s [locales guide](https://github.com/softistx/nxgt-mail/blob/develop/packages/mail/docs/guide/locales.md)
120
121
  — `pickLocale` and `parseAcceptLanguage` in full.
121
- - [Templates](templates.md) — writing the seven for a new locale.
122
+ - [Templates](templates.md) — writing the eight for a new locale.
@@ -26,6 +26,7 @@ export const mail = janusMail({
26
26
  verifyEmail: (token) => `https://acme.example/verify?token=${encodeURIComponent(token)}`,
27
27
  resetPassword: (token) => `https://acme.example/reset?token=${encodeURIComponent(token)}`,
28
28
  secureAccount: () => 'https://acme.example/account/security',
29
+ getStarted: () => 'https://acme.example/',
29
30
  },
30
31
  });
31
32
  ```
@@ -45,6 +46,7 @@ time one is sent, and kept.
45
46
  | `links.verifyEmail` | `(token) => string` | required | The page that confirms an address, given the one-time token |
46
47
  | `links.resetPassword` | `(token) => string` | required | The page that sets a new password, given the one-time token |
47
48
  | `links.secureAccount` | `() => string` | required | Where a user who made no change secures their account: the notices link to it |
49
+ | `links.getStarted` | `() => string` | required | Where a new user starts — your home page, or your sign-in page for an account someone else created: the welcome's **Get started** button links to it |
48
50
  | `locales` | `readonly L[]` | `['en', 'fr']` | The locales sent in — see [Locales](locales.md) |
49
51
  | `fallbackLocale` | one of `locales` | `'en'`, else the first of `locales` | The locale when the recipient wants none of `locales` |
50
52
  | `templates` | `Partial<JanusMailTemplates<L>>` | none | Your own templates, over the defaults — see [Templates](templates.md) |
@@ -134,6 +136,68 @@ since whoever changed it already controls the new one. It names `newEmail`,
134
136
  and links to `links.secureAccount()`. Read the former address before the
135
137
  update: the user `update` answers already holds the new one.
136
138
 
139
+ ### `twoFactorEnabled(to)` and `twoFactorDisabled(to)`
140
+
141
+ ```ts
142
+ // On the user events @nxgt/janus sends once the write landed:
143
+ const auth = janus({
144
+ ...config,
145
+ async events(event) {
146
+ if (event.type !== 'user.secondFactorEnabled' && event.type !== 'user.secondFactorDisabled') return;
147
+ const user = await auth.get(event.userId);
148
+ const to = { name: user.name, locale: user.locale, email: user.email };
149
+ await (event.type === 'user.secondFactorEnabled' ? mail.twoFactorEnabled(to) : mail.twoFactorDisabled(to));
150
+ },
151
+ });
152
+ ```
153
+
154
+ Two notices: two-factor authentication was turned on, or off, for the
155
+ account at `to.email`, each with a link to `links.secureAccount()` — the
156
+ user's security settings — for a user who did not make the change. Send
157
+ `twoFactorEnabled` on the `user.secondFactorEnabled` event, which
158
+ `auth.secondFactor.activate` sends once the factor is active, and
159
+ `twoFactorDisabled` on `user.secondFactorDisabled`, which
160
+ `auth.secondFactor.disable` sends only when it removed an active factor: a
161
+ `disable` on a user who had none tells nobody anything. The event names the
162
+ user by id alone, so read the name, the locale and the address from the
163
+ user. Calling them right after `activate` or `disable` answered works as
164
+ well — without the events, check `hasSecondFactor` before the `disable`.
165
+
166
+ ### `welcome(to)`
167
+
168
+ ```ts
169
+ // On the user event @nxgt/janus sends once the user is inserted:
170
+ const auth = janus({
171
+ ...config,
172
+ async events(event) {
173
+ if (event.type !== 'user.created') return;
174
+ const user = await auth.get(event.userId);
175
+ await mail.welcome({ name: user.name, locale: user.locale, email: user.email });
176
+ },
177
+ });
178
+ ```
179
+
180
+ Welcomes the new user at `to.email` — "Welcome, Ada", "Bienvenue, Ada", the
181
+ name in the subject too — with a **Get started** button that links to
182
+ `links.getStarted()`: your home page, or your sign-in page when the account
183
+ was created for the user rather than by them. Send it on the `user.created`
184
+ event, which `auth.signUp` and `auth.create` send once the user is inserted —
185
+ even when `signUp`'s session then fails to open — and never for a sign-up
186
+ refused. The event names the user by id alone, so read the name, the locale
187
+ and the address from the user. With several user types, check
188
+ `event.userType` first: a type with no e-mail has nobody to welcome.
189
+
190
+ - **It goes out before the address is verified.** A sign-up with a mistyped
191
+ address welcomes whoever holds it, and with open sign-up anyone can make
192
+ your brand send "Welcome, <any name>" to any address. To welcome proven addresses only, send
193
+ it on `user.emailVerified` instead, sent once the address is proven —
194
+ and again after an address changed and was proven anew, so welcome only
195
+ a user you have not welcomed before.
196
+ - **A failed welcome fails no sign-up.** `janus()` awaits the listener, but a
197
+ listener that throws is a `JANUS_EVENT_FAILED` warning: the user exists,
198
+ and `signUp` answers as it would have. For a welcome that must arrive, put
199
+ the event in a queue that retries a `MAIL_FAILED`, and send from there.
200
+
137
201
  ## The expiry
138
202
 
139
203
  `verifyEmail`, `resetPassword` and `signInCode` say how long the link or the
@@ -178,33 +242,6 @@ through JSON on its way to a queue holds a string — or is already past, is
178
242
  a `TypeError`, and nothing reaches the mailer: revive the date with
179
243
  `new Date(issued.expiresAt)` when the job runs, or pass `expiresIn`.
180
244
 
181
- ### `twoFactorEnabled(to)` and `twoFactorDisabled(to)`
182
-
183
- ```ts
184
- // On the user events @nxgt/janus sends once the write landed:
185
- const auth = janus({
186
- ...config,
187
- async events(event) {
188
- if (event.type !== 'user.secondFactorEnabled' && event.type !== 'user.secondFactorDisabled') return;
189
- const user = await auth.get(event.userId);
190
- const to = { name: user.name, locale: user.locale, email: user.email };
191
- await (event.type === 'user.secondFactorEnabled' ? mail.twoFactorEnabled(to) : mail.twoFactorDisabled(to));
192
- },
193
- });
194
- ```
195
-
196
- Two notices: two-factor authentication was turned on, or off, for the
197
- account at `to.email`, each with a link to `links.secureAccount()` — the
198
- user's security settings — for a user who did not make the change. Send
199
- `twoFactorEnabled` on the `user.secondFactorEnabled` event, which
200
- `auth.secondFactor.activate` sends once the factor is active, and
201
- `twoFactorDisabled` on `user.secondFactorDisabled`, which
202
- `auth.secondFactor.disable` sends only when it removed an active factor: a
203
- `disable` on a user who had none tells nobody anything. The event names the
204
- user by id alone, so read the name, the locale and the address from the
205
- user. Calling them right after `activate` or `disable` answered works as
206
- well — without the events, check `hasSecondFactor` before the `disable`.
207
-
208
245
  ## Where each e-mail goes
209
246
 
210
247
  | Method | To | Never to |
@@ -215,6 +252,7 @@ well — without the events, check `hasSecondFactor` before the `disable`.
215
252
  | `passwordChanged` | `to.email` | — |
216
253
  | `emailChanged` | `to.formerEmail` | `to.newEmail` |
217
254
  | `twoFactorEnabled`, `twoFactorDisabled` | `to.email` | — |
255
+ | `welcome` | `to.email` | — |
218
256
 
219
257
  `issued.email` is the address the user's record holds, as they registered
220
258
  it; what the visitor typed may differ in case or spacing. The recipient is a
@@ -268,10 +306,11 @@ retries a `MAIL_FAILED`, rather than to a promise nobody awaits.
268
306
 
269
307
  ### Retrying and tracing the mailer
270
308
 
271
- From `@nxgt/mail` 0.8, wrap the mailer before you hand it to `janusMail()`:
272
- `withRetry` retries a `MailFailure` with backoff, reusing one idempotency
273
- key for every attempt, and never retries a `MailRefused`; `withTelemetry`
274
- from `@nxgt/mail/telemetry` opens a `mail.send` span per send. That entry
309
+ Wrap the mailer before you hand it to `janusMail()`: `withRetry`, from
310
+ `@nxgt/mail` 0.8, retries a `MailFailure` with backoff, reusing one
311
+ idempotency key for every attempt, and never retries a `MailRefused`;
312
+ `withMailTelemetry`, from `@nxgt/mail/telemetry` 0.9 (`withTelemetry` on
313
+ 0.8), opens a `mail.send` span per send. That entry
275
314
  needs `@opentelemetry/api` installed (an optional peer, loaded by it alone);
276
315
  with no OpenTelemetry SDK registered, its spans are no-ops. Put telemetry on the outside, so one
277
316
  send is one span:
@@ -279,7 +318,7 @@ send is one span:
279
318
  ```ts
280
319
  import { janusMail } from '@nxgt/janus-mail';
281
320
  import { withRetry } from '@nxgt/mail';
282
- import { withTelemetry as withMailTelemetry } from '@nxgt/mail/telemetry';
321
+ import { withMailTelemetry } from '@nxgt/mail/telemetry';
283
322
  import { createSmtpMailer } from '@nxgt/mail-smtp';
284
323
 
285
324
  export const mail = janusMail({
@@ -290,12 +329,14 @@ export const mail = janusMail({
290
329
  });
291
330
  ```
292
331
 
293
- **Alias it.** `@nxgt/telemetry` — which `@nxgt/janus-telemetry` peers — also
294
- exports a `withTelemetry`, `withTelemetry(telemetry, fn)`, which scopes a
295
- telemetry to a block. The two are unrelated: `mail.send` is recorded
296
- through OpenTelemetry, not `@nxgt/telemetry`, so it does not nest under
297
- `@nxgt/janus-telemetry`'s spans. Import `@nxgt/mail`'s under another name,
298
- as above, when a module uses both.
332
+ **Two telemetries, unrelated.** `@nxgt/telemetry` — which
333
+ `@nxgt/janus-telemetry` peers — exports `withTelemetry(telemetry, fn)`,
334
+ which scopes a telemetry to a block. `mail.send` is recorded through
335
+ OpenTelemetry, not `@nxgt/telemetry`, so it does not nest under
336
+ `@nxgt/janus-telemetry`'s spans. `@nxgt/mail` 0.9 renamed its decorator
337
+ `withMailTelemetry` so the two no longer share a name; on 0.8 it is
338
+ `withTelemetry`, still exported by 0.9 as a deprecated alias until 1.0 —
339
+ import it under another name there when a module uses both.
299
340
 
300
341
  **SMTP ignores the idempotency key**, so a retry after an ambiguous SMTP
301
342
  timeout can deliver an e-mail twice. If a duplicate sign-in code or reset
@@ -322,6 +363,7 @@ const links = {
322
363
  verifyEmail: (token: string) => `https://acme.example/verify?token=${token}`,
323
364
  resetPassword: (token: string) => `https://acme.example/reset?token=${token}`,
324
365
  secureAccount: () => 'https://acme.example/account/security',
366
+ getStarted: () => 'https://acme.example/',
325
367
  };
326
368
 
327
369
  it('sends the code, and never the challenge', async () => {
@@ -1,7 +1,7 @@
1
1
  # Templates
2
2
 
3
3
  This page is for changing what an e-mail says or looks like: what a template
4
- is given and answers, replacing some of the seven, using the defaults on their
4
+ is given and answers, replacing some of the eight, using the defaults on their
5
5
  own, and what the defaults say.
6
6
 
7
7
  ```ts
@@ -22,7 +22,7 @@ const mail = janusMail({
22
22
  });
23
23
  ```
24
24
 
25
- The sign-in code e-mail is now yours; the six others stay the defaults.
25
+ The sign-in code e-mail is now yours; the seven others stay the defaults.
26
26
 
27
27
  ## What a template is
28
28
 
@@ -66,10 +66,12 @@ e-mail.
66
66
  | `emailChanged` | `brand`, `name`, `link`, `newEmail` | `mail.emailChanged(to)` |
67
67
  | `twoFactorEnabled` | `brand`, `name`, `link` | `mail.twoFactorEnabled(to)` |
68
68
  | `twoFactorDisabled` | `brand`, `name`, `link` | `mail.twoFactorDisabled(to)` |
69
+ | `welcome` | `brand`, `name`, `link` | `mail.welcome(to)` |
69
70
 
70
71
  Every one also gets `locale`, one of `locales`. Every value is a string:
71
72
  `brand` from the options, `name` from the recipient, `link` from `links` —
72
- `links.secureAccount()` for the four notices — `code` and `newEmail` from the
73
+ `links.secureAccount()` for the four notices, `links.getStarted()` for
74
+ `welcome` — `code` and `newEmail` from the
73
75
  call, and `expiresIn` derived from the flow's `expiresAt` in the locale, or
74
76
  the send's own `expiresIn` ([Sending](sending.md#the-expiry)).
75
77
 
@@ -103,11 +105,11 @@ methods live on its prototype and are refused — see
103
105
  [troubleshooting](../troubleshooting.md#janusmail-templatesname-is-not-an-own-enumerable-property--pass-a-plain-object-as--name-variables--rendered-).
104
106
 
105
107
  `templates` is `Partial` while `locales` stays within `en` and `fr`: give
106
- any of the seven, and the defaults render the rest. Once `locales` holds
107
- another locale, it takes **all seven** — see
108
+ any of the eight, and the defaults render the rest. Once `locales` holds
109
+ another locale, it takes **all eight** — see
108
110
  [Adding a locale](locales.md#adding-a-locale).
109
111
 
110
- `mail.templates` answers the seven in use — yours, and the defaults for the
112
+ `mail.templates` answers the eight in use — yours, and the defaults for the
111
113
  rest — frozen:
112
114
 
113
115
  ```ts
@@ -129,7 +131,7 @@ const { subject, html, text } = await defaults.resetPassword({
129
131
  });
130
132
  ```
131
133
 
132
- It answers the seven defaults, typed for `'en' | 'fr'`, without a mailer:
134
+ It answers the eight defaults, typed for `'en' | 'fr'`, without a mailer:
133
135
  to preview an e-mail, to wrap one — add a line to the default text, say — or
134
136
  to send one through something other than `janusMail()`. A default reused for
135
137
  a wider set of locales is a compile error: it could not render the one it
@@ -155,7 +157,7 @@ them renders: that is when the package's `mails/` folder is read, once.
155
157
  ## What the defaults say
156
158
 
157
159
  The defaults are [`@nxgt/mail-presets`](https://www.npmjs.com/package/@nxgt/mail-presets)
158
- 0.4.2, built with a neutral grey theme and no logo:
160
+ 0.4.3, built with a neutral grey theme and no logo:
159
161
 
160
162
  | Template | Built from | Subject (`en`) | Subject (`fr`) |
161
163
  | --- | --- | --- | --- |
@@ -166,11 +168,14 @@ The defaults are [`@nxgt/mail-presets`](https://www.npmjs.com/package/@nxgt/mail
166
168
  | `emailChanged` | `email-changed` | Your e-mail address was changed | Votre adresse e-mail a été modifiée |
167
169
  | `twoFactorEnabled` | `two-factor-enabled` | Two-factor authentication was turned on | L'authentification à deux facteurs a été activée |
168
170
  | `twoFactorDisabled` | `two-factor-disabled` | Two-factor authentication was turned off | L'authentification à deux facteurs a été désactivée |
171
+ | `welcome` | `welcome` | Welcome, `Ada` | Bienvenue, `Ada` |
169
172
 
170
173
  Each has a header and a footer with the brand, a heading, a greeting by name
171
174
  (but `signInCode`), a button with its link and the link again in text for a
172
175
  client that shows no button, and a text part. The four notices add a warning:
173
- *if this was not you, secure your account now*.
176
+ *if this was not you, secure your account now*. `welcome` has the brand in
177
+ its heading — "Welcome to Acme" — and the recipient's name in its subject,
178
+ filled at send time like the body; its button says **Get started**.
174
179
 
175
180
  `verifyEmail`, `resetPassword` and `signInCode` also say how long the link
176
181
  or the code lasts: "This link expires in 1 hour.", "Ce code expire dans
@@ -186,11 +191,18 @@ is near-black (`#27272a`) with light text in light mode, and near-white
186
191
  dark card, where the light primary would be 1.2:1. The page behind the card
187
192
  is 5% of the light primary over the dark background, a shade darker than the
188
193
  card, which its border outlines. The sign-in code's box is a light grey
189
- (`#f1f5f9`) in light mode and a mid slate (`#94a3b8`) in dark mode, 6.95:1
190
- against the dark card; the code on it stays near-black (`#020918`) in both,
191
- 7.76:1 over the dark box. The footer's muted text keeps its grey (`#62748e`)
192
- in both modes. Nothing is passed for any of it, the text part has no
193
- colours, and a template of your own gets none of it. `@nxgt/mail-ui`'s
194
+ (`#f1f5f9`) with near-black code (`#020918`) in light mode, and a slate a
195
+ step above the dark card (`#1e293b`) with light code (`#cbd5e1`) in dark
196
+ mode, 9.85:1. The muted text — the footer, and the closing *if you did not
197
+ ask for this* — is a slate grey (`#5f718a`) in light mode, 4.53:1 on the
198
+ page, and turns `#cbd5e1` in dark mode, 12.01:1 on the dark card and
199
+ 13.31:1 on the page. The notices' alerts keep their light ground and grey
200
+ text in both modes, 4.56:1 at the least. Every
201
+ text of the defaults reads at 4.5:1 or more in both modes, but the link
202
+ under the button, which `@nxgt/mail-ui` colours with its info blue in
203
+ both: 6.78:1 on the dark card, 2.63:1 on the light one, and no theme token
204
+ moves it without breaking the other mode. Nothing is passed for any of it,
205
+ the text part has no colours, and a template of your own gets none of it. `@nxgt/mail-ui`'s
194
206
  [Dark mode](https://github.com/softistx/nxgt-mail/blob/develop/packages/mail-ui/docs/guide/dark-mode.md)
195
207
  guide has the technique, client by client.
196
208
 
package/docs/roadmap.md CHANGED
@@ -9,10 +9,9 @@ Nothing between releases.
9
9
 
10
10
  ## Next
11
11
 
12
- - **The other presets** — `magic-link`, `new-sign-in`, `welcome` and
13
- `invitation` from `@nxgt/mail-presets`, each once `@nxgt/janus` has a flow
14
- that sends it: a sign-in link, a sign-in from a new device, a user created,
15
- an invitation to a user type.
12
+ - **The other presets** — `magic-link`, `new-sign-in` and `invitation` from
13
+ `@nxgt/mail-presets`, each once `@nxgt/janus` has a flow that sends it: a
14
+ sign-in link, a sign-in from a new device, an invitation to a user type.
16
15
  - **The other presets of `@nxgt/mail-presets` 0.4.0** — `account-deleted`,
17
16
  after a user is deleted, once `@nxgt/janus` has a deletion a link can
18
17
  undo; and `invitation-accepted`, once it has invitations.
@@ -48,6 +47,22 @@ Nothing between releases.
48
47
 
49
48
  Newest first; from the first release on, the package's CHANGELOG holds every one.
50
49
 
50
+ - **Muted text at 4.5:1 or more in both modes, v0.4.1.** Under dark mode the
51
+ sign-in code's box is a slate a step above the dark card with light code on
52
+ it, 9.85:1, and the muted text turns light, 13.31:1 on the page and
53
+ 12.01:1 on the card; in light mode
54
+ the muted grey is a shade darker, 4.53:1 on the page and 4.56:1 on the
55
+ notices' alerts at the least. Built from `@nxgt/mail-ui` 0.7.0 and
56
+ `@nxgt/mail-presets` 0.4.3; the text parts, subjects and variables are
57
+ unchanged, the manifest is still format 1, and the `@nxgt/mail` peer stays
58
+ `>=0.1.0 <1`.
59
+ - **The welcome e-mail, v0.4.0.** `welcome(to)`, built from
60
+ `@nxgt/mail-presets` 0.4.2's `welcome` in English and French — "Welcome,
61
+ Ada" — with a **Get started** button linking to the new
62
+ `links.getStarted()`: send it on `@nxgt/janus`'s `user.created` event.
63
+ `links` now requires `getStarted`, and a locale beyond `en` and `fr` takes
64
+ eight templates.
65
+
51
66
  - **A sign-in code box that is not a light slab in dark mode, v0.3.2.** Under
52
67
  dark mode the code's box is a mid slate, 6.95:1 against the dark card, with
53
68
  the near-black code at 7.76:1 on it, and the page behind the card is darker