@nxgt/janus-mail 0.8.0 → 0.9.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.
- package/README.md +65 -13
- package/dist/generated/mail.d.ts +8 -0
- package/dist/generated/mail.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +62 -23
- package/dist/index.js.map +7 -6
- package/dist/janus-mail.d.ts.map +1 -1
- package/dist/link-types.d.ts +51 -0
- package/dist/link-types.d.ts.map +1 -0
- package/dist/new-sign-in-mail.d.ts +16 -0
- package/dist/new-sign-in-mail.d.ts.map +1 -0
- package/dist/options.d.ts.map +1 -1
- package/dist/template-types.d.ts +15 -2
- package/dist/template-types.d.ts.map +1 -1
- package/dist/templates.d.ts +1 -1
- package/dist/templates.d.ts.map +1 -1
- package/dist/types.d.ts +20 -46
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +3 -3
- package/docs/guide/building.md +1 -1
- package/docs/guide/locales.md +7 -4
- package/docs/guide/sending.md +48 -1
- package/docs/guide/templates.md +20 -11
- package/docs/roadmap.md +12 -23
- package/docs/troubleshooting.md +44 -9
- package/mails/en/new-sign-in.html +60 -0
- package/mails/en/new-sign-in.txt +30 -0
- package/mails/fr/new-sign-in.html +58 -0
- package/mails/fr/new-sign-in.txt +30 -0
- package/mails/mail-manifest.json +27 -0
- package/package.json +4 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-types.d.ts","sourceRoot":"","sources":["../src/link-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc;IAC9B,6HAA6H;IAC7H,QAAQ,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAChD,mEAAmE;IACnE,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAClD;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,MAAM,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,MAAM,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,MAAM,CAAC;IACtC;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CAC/C"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `newSignIn`: the notice of a sign-in from a device the user had not signed
|
|
3
|
+
* in from — which device, when, and where when the sender knows — linking to
|
|
4
|
+
* where the account is secured.
|
|
5
|
+
*
|
|
6
|
+
* Apart from the other notices because of its optional `location`: Janus
|
|
7
|
+
* sees no IP, so the sender may know none, and the e-mail then shows `—`,
|
|
8
|
+
* which reads the same in every locale.
|
|
9
|
+
*/
|
|
10
|
+
import type { ResolvedOptions } from './options';
|
|
11
|
+
import type { JanusMail } from './types';
|
|
12
|
+
/** What the e-mail shows where the sender knew no location. */
|
|
13
|
+
export declare const NO_LOCATION = "\u2014";
|
|
14
|
+
/** The new sign-in notice, over the resolved options. */
|
|
15
|
+
export declare function newSignInMail(options: ResolvedOptions): Pick<JanusMail<string>, 'newSignIn'>;
|
|
16
|
+
//# sourceMappingURL=new-sign-in-mail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new-sign-in-mail.d.ts","sourceRoot":"","sources":["../src/new-sign-in-mail.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAIzC,+DAA+D;AAC/D,eAAO,MAAM,WAAW,WAAM,CAAC;AAY/B,yDAAyD;AACzD,wBAAgB,aAAa,CAC5B,OAAO,EAAE,eAAe,GACtB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAgBtC"}
|
package/dist/options.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAKlD,OAAO,KAAK,EACX,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAClB,MAAM,SAAS,CAAC;AAEjB,+DAA+D;AAC/D,eAAO,MAAM,cAAc,EAAE,SAAS,qBAAqB,
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAKlD,OAAO,KAAK,EACX,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAClB,MAAM,SAAS,CAAC;AAEjB,+DAA+D;AAC/D,eAAO,MAAM,cAAc,EAAE,SAAS,qBAAqB,EAa1D,CAAC;AAEF,yFAAyF;AACzF,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC/C,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACtB;AAiJD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe,CAoBhE"}
|
package/dist/template-types.d.ts
CHANGED
|
@@ -84,6 +84,19 @@ export interface JanusMailVariables {
|
|
|
84
84
|
readonly recoveryCodesLeft: string;
|
|
85
85
|
readonly link: string;
|
|
86
86
|
};
|
|
87
|
+
/**
|
|
88
|
+
* `device`, `time` and `location` are the sender's text, already in the
|
|
89
|
+
* recipient's locale; `location` is `—` when the sender knew none. `link`
|
|
90
|
+
* is where the account is secured.
|
|
91
|
+
*/
|
|
92
|
+
readonly newSignIn: {
|
|
93
|
+
readonly brand: string;
|
|
94
|
+
readonly name: string;
|
|
95
|
+
readonly device: string;
|
|
96
|
+
readonly location: string;
|
|
97
|
+
readonly time: string;
|
|
98
|
+
readonly link: string;
|
|
99
|
+
};
|
|
87
100
|
/** `name` is in the subject too: "Welcome, Ada". */
|
|
88
101
|
readonly welcome: {
|
|
89
102
|
readonly brand: string;
|
|
@@ -91,7 +104,7 @@ export interface JanusMailVariables {
|
|
|
91
104
|
readonly link: string;
|
|
92
105
|
};
|
|
93
106
|
}
|
|
94
|
-
/** The name of one of the
|
|
107
|
+
/** The name of one of the twelve templates: `verifyEmail`, `resetPassword`, … */
|
|
95
108
|
export type JanusMailTemplateName = keyof JanusMailVariables;
|
|
96
109
|
/**
|
|
97
110
|
* One e-mail: its variables and the locale picked for the recipient, in; its
|
|
@@ -102,7 +115,7 @@ export type JanusMailTemplateName = keyof JanusMailVariables;
|
|
|
102
115
|
export type JanusMailTemplate<V, L extends string = JanusMailLocale> = (variables: V & {
|
|
103
116
|
readonly locale: L;
|
|
104
117
|
}) => Rendered | PromiseLike<Rendered>;
|
|
105
|
-
/** The
|
|
118
|
+
/** The twelve templates, each for the locales `L`. */
|
|
106
119
|
export type JanusMailTemplates<L extends string = JanusMailLocale> = {
|
|
107
120
|
readonly [K in JanusMailTemplateName]: JanusMailTemplate<JanusMailVariables[K], L>;
|
|
108
121
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-types.d.ts","sourceRoot":"","sources":["../src/template-types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"template-types.d.ts","sourceRoot":"","sources":["../src/template-types.ts"],"names":[],"mappings":"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"}
|
package/dist/templates.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { JanusMailTemplates } from './types';
|
|
2
2
|
/**
|
|
3
|
-
* The
|
|
3
|
+
* The twelve default templates, in the locales they are built in. The
|
|
4
4
|
* renderer behind them is created on the first render, not here.
|
|
5
5
|
*
|
|
6
6
|
* ```ts
|
package/dist/templates.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAgClD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,IAAI,kBAAkB,CA6DnD"}
|
package/dist/types.d.ts
CHANGED
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
import type { Clock, IssuedCode, IssuedToken, StepUpByEmail } from '@nxgt/janus';
|
|
7
7
|
import type { Address, Mailer, SentMail, WantedLocales } from '@nxgt/mail';
|
|
8
8
|
import type { JanusMailLocale } from './generated/locales';
|
|
9
|
+
import type { JanusMailLinks } from './link-types';
|
|
9
10
|
import type { JanusMailTemplates } from './template-types';
|
|
10
11
|
export type { JanusMailLocale } from './generated/locales';
|
|
12
|
+
export type { JanusMailLinks } from './link-types';
|
|
11
13
|
export type { JanusMailTemplate, JanusMailTemplateName, JanusMailTemplates, JanusMailVariables, } from './template-types';
|
|
12
14
|
/**
|
|
13
15
|
* Who an e-mail is for, besides the address: the name it greets, and the
|
|
@@ -19,52 +21,6 @@ export interface Recipient {
|
|
|
19
21
|
readonly name: string;
|
|
20
22
|
readonly locale?: WantedLocales;
|
|
21
23
|
}
|
|
22
|
-
/**
|
|
23
|
-
* The links of the e-mails, each an absolute `http(s)` URL of your
|
|
24
|
-
* application. Each is called at send time and must answer a string
|
|
25
|
-
* synchronously: an `async` function or a `URL` object is a compile error,
|
|
26
|
-
* and from JavaScript, or through a cast, the send throws a `TypeError`
|
|
27
|
-
* naming the call. A
|
|
28
|
-
* `mailto:` URL is accepted too — `secureAccount: () => 'mailto:security@acme.example'`
|
|
29
|
-
* sends the user to your support desk; any other scheme is refused by the
|
|
30
|
-
* renderer with a `MailRefused`. The functions are copied when `janusMail()`
|
|
31
|
-
* is called: replacing one afterwards changes nothing.
|
|
32
|
-
*/
|
|
33
|
-
export interface JanusMailLinks {
|
|
34
|
-
/** The page that confirms an e-mail, given the one-time token: `(token) => \`https://app.example/verify?token=${token}\`` */
|
|
35
|
-
readonly verifyEmail: (token: string) => string;
|
|
36
|
-
/** The page that sets a new password, given the one-time token. */
|
|
37
|
-
readonly resetPassword: (token: string) => string;
|
|
38
|
-
/**
|
|
39
|
-
* Where a user who did not make a change secures their account — their
|
|
40
|
-
* security settings. `passwordChanged`, `emailChanged`, `twoFactorEnabled`
|
|
41
|
-
* and `twoFactorDisabled` link to it, and so does `stepUp`, for a user who
|
|
42
|
-
* asked for no code.
|
|
43
|
-
*/
|
|
44
|
-
readonly secureAccount: () => string;
|
|
45
|
-
/**
|
|
46
|
-
* Where a new user starts — your application's home, or its sign-in page
|
|
47
|
-
* for an account someone else created. `welcome` links to it, from its
|
|
48
|
-
* "Get started" button.
|
|
49
|
-
*/
|
|
50
|
-
readonly getStarted: () => string;
|
|
51
|
-
/**
|
|
52
|
-
* Where a user regenerates their recovery codes — `recoveryCodeUsed`
|
|
53
|
-
* links to it, from its "Secure my account" button. **Optional**: without
|
|
54
|
-
* it, that e-mail links to `secureAccount`, the security settings where
|
|
55
|
-
* the codes usually are.
|
|
56
|
-
*/
|
|
57
|
-
readonly recoveryCodes?: () => string;
|
|
58
|
-
/**
|
|
59
|
-
* The page a sign-in link opens, given its one-time token —
|
|
60
|
-
* `magicLink` links to it. **Optional**, for an application that sends no
|
|
61
|
-
* sign-in link; without it, `magicLink` throws a `TypeError` before
|
|
62
|
-
* anything is rendered. That page spends nothing: its button posts the
|
|
63
|
-
* token to the route that calls `auth.magicLink.confirm`, so a mail
|
|
64
|
-
* scanner that opens the link does not sign in for the user.
|
|
65
|
-
*/
|
|
66
|
-
readonly magicLink?: (token: string) => string;
|
|
67
|
-
}
|
|
68
24
|
/** The options every locale set shares. */
|
|
69
25
|
interface JanusMailBaseOptions<L extends string> {
|
|
70
26
|
/** The transport — `@nxgt/mail-smtp`, `@nxgt/mail-resend`, or `createMemoryMailer()` in tests. */
|
|
@@ -200,6 +156,24 @@ export interface JanusMail<L extends string = JanusMailLocale> {
|
|
|
200
156
|
readonly when: string;
|
|
201
157
|
readonly recoveryCodesLeft: number | string;
|
|
202
158
|
}): Promise<SentMail>;
|
|
159
|
+
/**
|
|
160
|
+
* Tells `to.email` their account was signed in from a device they had
|
|
161
|
+
* not signed in from, `time`, on `device` — and `location`, when you know
|
|
162
|
+
* it — with a link to secure the account: once a sign-in answered
|
|
163
|
+
* `newDevice: true`, or on the `user.newDeviceSignedIn` event.
|
|
164
|
+
*
|
|
165
|
+
* Every value is your text, in the recipient's locale: `device` described
|
|
166
|
+
* from the request (`'Firefox on macOS'`), `time` formatted in their time
|
|
167
|
+
* zone. **Janus sees no IP**: `location` is yours to work out, and an
|
|
168
|
+
* e-mail without one shows `—` in its place.
|
|
169
|
+
*/
|
|
170
|
+
newSignIn(to: Recipient & {
|
|
171
|
+
readonly email: string;
|
|
172
|
+
}, signIn: {
|
|
173
|
+
readonly device: string;
|
|
174
|
+
readonly time: string;
|
|
175
|
+
readonly location?: string;
|
|
176
|
+
}): Promise<SentMail>;
|
|
203
177
|
/** The templates in use: the defaults, with `templates` over them. */
|
|
204
178
|
readonly templates: JanusMailTemplates<L>;
|
|
205
179
|
/** The locales sent in. */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EACX,KAAK,EACL,UAAU,EACV,WAAW,EACX,aAAa,EACb,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,YAAY,EACX,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,GAClB,MAAM,kBAAkB,CAAC;AAE1B;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;CAChC;AAED
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/docs/README.md
CHANGED
|
@@ -5,7 +5,7 @@ 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 step-up's code, the welcome on `user.created`, the recovery code notice on `user.recoveryCodeUsed
|
|
8
|
+
| [Sending](guide/sending.md) | You are wiring `janusMail()` into your routes: the options, `clock` among them; each method, the step-up's code, the welcome on `user.created`, the recovery code notice on `user.recoveryCodeUsed`, the change notices on `user.passwordChanged` and `user.emailChanged` and the new sign-in notice 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 |
|
|
@@ -23,13 +23,13 @@ They mean the same here. These pages add:
|
|
|
23
23
|
|
|
24
24
|
| Word | Means | Not |
|
|
25
25
|
| --- | --- | --- |
|
|
26
|
-
| **e-mail** | One of the
|
|
26
|
+
| **e-mail** | One of the twelve this package sends: `verify-email`, `reset-password`, `sign-in-code`, `magic-link`, `confirm-action`, `password-changed`, `email-changed`, `two-factor-enabled`, `two-factor-disabled`, `recovery-code-used`, `new-sign-in`, `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`, `resetPassword.request` or `magicLink.request`, an `IssuedCode` from `signInCode.request`, a `StepUpByEmail` from `stepUp.request` once narrowed to `via: 'email'` | "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
|
-
| **notice** | An e-mail that tells, and asks for nothing: `passwordChanged`, `emailChanged`, `twoFactorEnabled`, `twoFactorDisabled`, `recoveryCodeUsed`. It links to `links.secureAccount()` — `recoveryCodeUsed` to `links.recoveryCodes()` when given | "alert", "notification" |
|
|
32
|
+
| **notice** | An e-mail that tells, and asks for nothing: `passwordChanged`, `emailChanged`, `twoFactorEnabled`, `twoFactorDisabled`, `recoveryCodeUsed`, `newSignIn`. It links to `links.secureAccount()` — `recoveryCodeUsed` to `links.recoveryCodes()` when given | "alert", "notification" |
|
|
33
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" |
|
|
34
34
|
| **codes left** | How many recovery codes the user still holds, as the recovery code notice says it: `recoveryCodesLeft`, a sentence written from the count with the preset's plural in the recipient's locale — "You have 1 recovery code left." — or passed as text | "remaining codes" — the count itself is `@nxgt/janus`'s `recoveryCodesLeft` |
|
|
35
35
|
| **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` |
|
package/docs/guide/building.md
CHANGED
|
@@ -55,7 +55,7 @@ packages/janus-mail/
|
|
|
55
55
|
`src/generated/mail.ts` (`rendererTypes`), from the build.
|
|
56
56
|
3. `scripts/build-mail.ts` reads the manifest and **fails unless its
|
|
57
57
|
format is one every `@nxgt/mail` the peer admits reads** — see below —
|
|
58
|
-
and **unless exactly the
|
|
58
|
+
and **unless exactly the twelve e-mails were built**: a thirteenth from a new
|
|
59
59
|
`@nxgt/mail-presets`, or one missing, stops the build.
|
|
60
60
|
4. It writes `src/generated/locales.ts` from the manifest's locales, and
|
|
61
61
|
`src/generated/codes-left.ts` — below — each only when its content
|
package/docs/guide/locales.md
CHANGED
|
@@ -89,6 +89,8 @@ const templates: JanusMailTemplates<Locale> = {
|
|
|
89
89
|
twoFactorDisabled: ({ name, link, locale }) => render('two-factor-disabled', locale, { name, link }),
|
|
90
90
|
recoveryCodeUsed: ({ name, when, recoveryCodesLeft, link, locale }) =>
|
|
91
91
|
render('recovery-code-used', locale, { name, when, recoveryCodesLeft, link }),
|
|
92
|
+
newSignIn: ({ name, device, location, time, link, locale }) =>
|
|
93
|
+
render('new-sign-in', locale, { name, device, location, time, link }),
|
|
92
94
|
welcome: ({ name, link, locale }) => render('welcome', locale, { name, link }),
|
|
93
95
|
};
|
|
94
96
|
|
|
@@ -103,7 +105,8 @@ plural of the codes left in `en` and `fr` only, so a German recipient's
|
|
|
103
105
|
`mail.recoveryCodeUsed` takes the sentence as text — "Sie haben noch 3
|
|
104
106
|
Wiederherstellungscodes." — and a count is a `TypeError` there.
|
|
105
107
|
`recoveryCodeUsed` is the ninth template, since 0.5.0, `magicLink` the
|
|
106
|
-
tenth, since 0.7.0,
|
|
108
|
+
tenth, since 0.7.0, `stepUp` the eleventh, since 0.8.0, and `newSignIn` the
|
|
109
|
+
twelfth, since 0.9.0: a `templates`
|
|
107
110
|
written for an earlier version no longer
|
|
108
111
|
compiles with a locale beyond `en` and `fr` — add the one it lacks. A
|
|
109
112
|
renderer over a Maizzle build of your own, with
|
|
@@ -112,13 +115,13 @@ With only some templates, the call does not compile — the error lands on
|
|
|
112
115
|
`templates`:
|
|
113
116
|
|
|
114
117
|
```text
|
|
115
|
-
TS2740: Type '{ verifyEmail: () => Rendered; }' is missing the following properties from type 'JanusMailTemplates<"en" | "fr" | "de">': welcome, resetPassword, signInCode, magicLink, and
|
|
118
|
+
TS2740: Type '{ verifyEmail: () => Rendered; }' is missing the following properties from type 'JanusMailTemplates<"en" | "fr" | "de">': welcome, resetPassword, signInCode, magicLink, and 7 more.
|
|
116
119
|
```
|
|
117
120
|
|
|
118
121
|
and in JavaScript it throws when `janusMail()` is called:
|
|
119
122
|
|
|
120
123
|
```text
|
|
121
|
-
TypeError: janusMail: the default templates are built in en and fr only — with another locale in locales, pass every template in templates; resetPassword, signInCode, magicLink, stepUp, passwordChanged, emailChanged, twoFactorEnabled, twoFactorDisabled, recoveryCodeUsed, welcome missing
|
|
124
|
+
TypeError: janusMail: the default templates are built in en and fr only — with another locale in locales, pass every template in templates; resetPassword, signInCode, magicLink, stepUp, passwordChanged, emailChanged, twoFactorEnabled, twoFactorDisabled, recoveryCodeUsed, newSignIn, welcome missing
|
|
122
125
|
```
|
|
123
126
|
|
|
124
127
|
A default template is typed for `'en' | 'fr'`, so reusing one in a wider set
|
|
@@ -131,4 +134,4 @@ Built-in locales for more languages are on the [roadmap](../roadmap.md).
|
|
|
131
134
|
|
|
132
135
|
- `@nxgt/mail`'s [locales guide](https://github.com/softistx/nxgt-mail/blob/develop/packages/mail/docs/guide/locales.md)
|
|
133
136
|
— `pickLocale` and `parseAcceptLanguage` in full.
|
|
134
|
-
- [Templates](templates.md) — writing the
|
|
137
|
+
- [Templates](templates.md) — writing the twelve for a new locale.
|
package/docs/guide/sending.md
CHANGED
|
@@ -46,7 +46,7 @@ time one is sent, and kept.
|
|
|
46
46
|
| `brand` | `string` | required | The name the e-mails show, in the header, the body and the footer. Text: escaped in the HTML |
|
|
47
47
|
| `links.verifyEmail` | `(token) => string` | required | The page that confirms an address, given the one-time token |
|
|
48
48
|
| `links.resetPassword` | `(token) => string` | required | The page that sets a new password, given the one-time token |
|
|
49
|
-
| `links.secureAccount` | `() => string` | required | Where a user who made no change secures their account: the notices link to it
|
|
49
|
+
| `links.secureAccount` | `() => string` | required | Where a user who made no change secures their account: the notices link to it — the new sign-in's included — and so does the step-up's code, for a user who asked for none |
|
|
50
50
|
| `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 |
|
|
51
51
|
| `links.magicLink` | `(token) => string` | none: `magicLink` is a `TypeError` without it | The page a sign-in link opens, given the one-time token. That page spends nothing: its button posts the token to the route that calls `auth.magicLink.confirm` |
|
|
52
52
|
| `links.recoveryCodes` | `() => string` | `links.secureAccount` | Where a user regenerates their recovery codes: the recovery code notice links to it, from its **Secure my account** button |
|
|
@@ -349,6 +349,52 @@ when opening the session then fails.
|
|
|
349
349
|
codes; without it, `links.secureAccount()`. `recoveryCodes` is optional,
|
|
350
350
|
so a `links` written for 0.4 still compiles.
|
|
351
351
|
|
|
352
|
+
### `newSignIn(to, { device, time, location? })`
|
|
353
|
+
|
|
354
|
+
```ts
|
|
355
|
+
// From the sign-in's answer, with @nxgt/janus given devices: { keys } and the device cookie:
|
|
356
|
+
const signedIn = await auth.signIn({ email, password }, { device: cookie ?? null });
|
|
357
|
+
if (signedIn.newDevice) {
|
|
358
|
+
const { name, email, locale } = signedIn.user;
|
|
359
|
+
await mail.newSignIn(
|
|
360
|
+
{ name, email, locale },
|
|
361
|
+
{
|
|
362
|
+
device: describeDevice(request.headers.get('user-agent')), // yours: 'Firefox on macOS'
|
|
363
|
+
time: new Intl.DateTimeFormat(locale ?? 'en', {
|
|
364
|
+
dateStyle: 'long',
|
|
365
|
+
timeStyle: 'short',
|
|
366
|
+
timeZone: 'Europe/Paris', // the user's, when you keep it
|
|
367
|
+
}).format(signedIn.session.createdAt),
|
|
368
|
+
location: 'Lyon, France', // optional: from a geo-IP of yours
|
|
369
|
+
},
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
A notice for the account at `to.email`: it was signed in from a device the
|
|
375
|
+
user had not signed in from — which device, where and when — with a
|
|
376
|
+
**Secure my account** button to `links.secureAccount()`. Send it when a
|
|
377
|
+
`@nxgt/janus` 0.17 sign-in answers `newDevice: true`, or on the
|
|
378
|
+
`user.newDeviceSignedIn` event it sends then — the event names the user by
|
|
379
|
+
id alone, so read the name, the locale and the address from the user, and
|
|
380
|
+
it carries no request to describe the device from.
|
|
381
|
+
|
|
382
|
+
- **Every value is your text**, already in the recipient's locale: `device`
|
|
383
|
+
as you describe the request — "Firefox on macOS", "Firefox sur macOS" —
|
|
384
|
+
and `time` formatted in their time zone: "29 septembre 2026 à 09:12".
|
|
385
|
+
The session's `createdAt`, or the event's `occurredAt`, is when it
|
|
386
|
+
happened.
|
|
387
|
+
- **`location` is optional, and yours.** `@nxgt/janus` sees no IP: a
|
|
388
|
+
location comes from a geo-IP of your own, if you have one. Left out, the
|
|
389
|
+
e-mail shows `—` — an em dash, which reads the same in English and
|
|
390
|
+
French — since every line of the summary is always there. `null` is a
|
|
391
|
+
compile error, and in JavaScript `janusMail.newSignIn: location must be a
|
|
392
|
+
string`: spread it in only when you have one,
|
|
393
|
+
`...(city === null ? {} : { location: city })`.
|
|
394
|
+
- **The text part** gives each line of the summary as its label, then its
|
|
395
|
+
value on the next line — `Device`, then `Firefox on macOS` — where every
|
|
396
|
+
other paragraph is one line.
|
|
397
|
+
|
|
352
398
|
## The expiry
|
|
353
399
|
|
|
354
400
|
`verifyEmail`, `resetPassword`, `signInCode`, `magicLink` and `stepUp` say
|
|
@@ -406,6 +452,7 @@ a `TypeError`, and nothing reaches the mailer: revive the date with
|
|
|
406
452
|
| `emailChanged` | `to.formerEmail` | `to.newEmail` |
|
|
407
453
|
| `twoFactorEnabled`, `twoFactorDisabled` | `to.email` | — |
|
|
408
454
|
| `recoveryCodeUsed` | `to.email` | — |
|
|
455
|
+
| `newSignIn` | `to.email` | — |
|
|
409
456
|
| `welcome` | `to.email` | — |
|
|
410
457
|
|
|
411
458
|
`issued.email` is the address the user's record holds, as they registered
|
package/docs/guide/templates.md
CHANGED
|
@@ -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
|
|
4
|
+
is given and answers, replacing some of the twelve, 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
|
|
25
|
+
The sign-in code e-mail is now yours; the eleven others stay the defaults.
|
|
26
26
|
|
|
27
27
|
## What a template is
|
|
28
28
|
|
|
@@ -69,14 +69,16 @@ e-mail.
|
|
|
69
69
|
| `twoFactorEnabled` | `brand`, `name`, `link` | `mail.twoFactorEnabled(to)` |
|
|
70
70
|
| `twoFactorDisabled` | `brand`, `name`, `link` | `mail.twoFactorDisabled(to)` |
|
|
71
71
|
| `recoveryCodeUsed` | `brand`, `name`, `when`, `recoveryCodesLeft`, `link` | `mail.recoveryCodeUsed(to, { when, recoveryCodesLeft })` |
|
|
72
|
+
| `newSignIn` | `brand`, `name`, `device`, `location`, `time`, `link` | `mail.newSignIn(to, { device, time, location? })` |
|
|
72
73
|
| `welcome` | `brand`, `name`, `link` | `mail.welcome(to)` |
|
|
73
74
|
|
|
74
75
|
Every one also gets `locale`, one of `locales`. Every value is a string:
|
|
75
76
|
`brand` from the options, `name` from the recipient, `link` from `links` —
|
|
76
|
-
`links.secureAccount()` for the
|
|
77
|
+
`links.secureAccount()` for the notices other than `recoveryCodeUsed` and
|
|
78
|
+
for `stepUp`, `links.recoveryCodes()` (else
|
|
77
79
|
`links.secureAccount()`) for `recoveryCodeUsed`, `links.getStarted()` for
|
|
78
|
-
`welcome` — `code`, `newEmail` and `
|
|
79
|
-
call, and `expiresIn` derived from the flow's `expiresAt` in the locale, or
|
|
80
|
+
`welcome` — `code`, `newEmail`, `when`, `device` and `time` from the
|
|
81
|
+
call, `location` from the call or `—` without one, and `expiresIn` derived from the flow's `expiresAt` in the locale, or
|
|
80
82
|
the send's own `expiresIn` ([Sending](sending.md#the-expiry)).
|
|
81
83
|
|
|
82
84
|
`recoveryCodesLeft` is a **sentence**, not the count: "You have 9 recovery
|
|
@@ -126,11 +128,12 @@ methods live on its prototype and are refused — see
|
|
|
126
128
|
[troubleshooting](../troubleshooting.md#janusmail-templatesname-is-not-an-own-enumerable-property--pass-a-plain-object-as--name-variables--rendered-).
|
|
127
129
|
|
|
128
130
|
`templates` is `Partial` while `locales` stays within `en` and `fr`: give
|
|
129
|
-
any of the
|
|
130
|
-
another locale, it takes **all
|
|
131
|
+
any of the twelve, and the defaults render the rest. Once `locales` holds
|
|
132
|
+
another locale, it takes **all twelve** — `stepUp` included, since 0.8, and
|
|
133
|
+
`newSignIn`, since 0.9 — see
|
|
131
134
|
[Adding a locale](locales.md#adding-a-locale).
|
|
132
135
|
|
|
133
|
-
`mail.templates` answers the
|
|
136
|
+
`mail.templates` answers the twelve in use — yours, and the defaults for the
|
|
134
137
|
rest — frozen:
|
|
135
138
|
|
|
136
139
|
```ts
|
|
@@ -152,7 +155,7 @@ const { subject, html, text } = await defaults.resetPassword({
|
|
|
152
155
|
});
|
|
153
156
|
```
|
|
154
157
|
|
|
155
|
-
It answers the
|
|
158
|
+
It answers the twelve defaults, typed for `'en' | 'fr'`, without a mailer:
|
|
156
159
|
to preview an e-mail, to wrap one — add a line to the default text, say — or
|
|
157
160
|
to send one through something other than `janusMail()`. A default reused for
|
|
158
161
|
a wider set of locales is a compile error: it could not render the one it
|
|
@@ -192,18 +195,24 @@ The defaults are [`@nxgt/mail-presets`](https://www.npmjs.com/package/@nxgt/mail
|
|
|
192
195
|
| `twoFactorEnabled` | `two-factor-enabled` | Two-factor authentication was turned on | L'authentification à deux facteurs a été activée |
|
|
193
196
|
| `twoFactorDisabled` | `two-factor-disabled` | Two-factor authentication was turned off | L'authentification à deux facteurs a été désactivée |
|
|
194
197
|
| `recoveryCodeUsed` | `recovery-code-used` | A recovery code was used on your account | Un code de récupération a été utilisé sur votre compte |
|
|
198
|
+
| `newSignIn` | `new-sign-in` | New sign-in to your account | Nouvelle connexion à votre compte |
|
|
195
199
|
| `welcome` | `welcome` | Welcome, `Ada` | Bienvenue, `Ada` |
|
|
196
200
|
|
|
197
201
|
Each has a header and a footer with the brand, a heading, a greeting by name
|
|
198
202
|
(but `signInCode` and `magicLink`), a button with its link and the link again in text for a
|
|
199
|
-
client that shows no button, and a text part. The
|
|
203
|
+
client that shows no button, and a text part. The notices other than `recoveryCodeUsed` add a warning:
|
|
200
204
|
*if this was not you, secure your account now*. `stepUp` shows its code as
|
|
201
205
|
the sign-in code does, names no action — *someone, we hope you, asked to do
|
|
202
206
|
something sensitive on your account* — and warns not to share the code if
|
|
203
207
|
it was not the user, its button saying **Secure my account**. `recoveryCodeUsed` says it
|
|
204
208
|
too, after a warning banner — *a recovery code was used on your account at
|
|
205
209
|
`when`* — the codes left, and a line on generating new ones; its button says
|
|
206
|
-
**Secure my account**. `
|
|
210
|
+
**Secure my account**. `newSignIn` says the account *was signed in from a
|
|
211
|
+
device we had not seen*, then a summary of three lines — **Device**,
|
|
212
|
+
**Location** and **Time**, *Appareil*, *Lieu* and *Heure* in French — then
|
|
213
|
+
*if this was you, there is nothing to do; if this was not you, secure your
|
|
214
|
+
account now*, its button saying **Secure my account**; in the text part each
|
|
215
|
+
line of the summary is its label, then its value on the next line. `welcome` has the brand in
|
|
207
216
|
its heading — "Welcome to Acme" — and the recipient's name in its subject,
|
|
208
217
|
filled at send time like the body; its button says **Get started**.
|
|
209
218
|
|
package/docs/roadmap.md
CHANGED
|
@@ -9,9 +9,8 @@ Nothing between releases.
|
|
|
9
9
|
|
|
10
10
|
## Next
|
|
11
11
|
|
|
12
|
-
- **The
|
|
13
|
-
`@nxgt/
|
|
14
|
-
sign-in from a new device, an invitation to a user type.
|
|
12
|
+
- **The invitation** — `invitation` from `@nxgt/mail-presets`, once
|
|
13
|
+
`@nxgt/janus` has a flow that sends it: an invitation to a user type.
|
|
15
14
|
- **The other presets of `@nxgt/mail-presets` 0.4.0** — `account-deleted`,
|
|
16
15
|
after a user is deleted, once `@nxgt/janus` has a deletion a link can
|
|
17
16
|
undo; and `invitation-accepted`, once it has invitations.
|
|
@@ -47,6 +46,16 @@ Nothing between releases.
|
|
|
47
46
|
|
|
48
47
|
Newest first; from the first release on, the package's CHANGELOG holds every one.
|
|
49
48
|
|
|
49
|
+
- **The new sign-in notice, v0.9.0.** `newSignIn(to, { device, time,
|
|
50
|
+
location? })`, built from `@nxgt/mail-presets` 1.1.0's `new-sign-in` in
|
|
51
|
+
English and French — "New sign-in to your account", which device, where
|
|
52
|
+
and when, and a **Secure my account** button to `links.secureAccount()`:
|
|
53
|
+
send it when an `@nxgt/janus` 0.17 sign-in answers `newDevice: true`, or
|
|
54
|
+
on `user.newDeviceSignedIn`. Every value is your text in the recipient's
|
|
55
|
+
locale; `@nxgt/janus` sees no IP, so `location` is yours, and `—` without
|
|
56
|
+
one. Twelve e-mails now, and twelve templates to pass for a locale beyond
|
|
57
|
+
`en` and `fr`.
|
|
58
|
+
|
|
50
59
|
- **The step-up's code, v0.8.0.** `stepUp(issued, to, options?)`, built
|
|
51
60
|
from `@nxgt/mail-presets` 1.1.0's `confirm-action` in English and French:
|
|
52
61
|
send what `@nxgt/janus`'s `auth.stepUp.request(user)` answered, once
|
|
@@ -121,23 +130,3 @@ Newest first; from the first release on, the package's CHANGELOG holds every one
|
|
|
121
130
|
linking to `links.secureAccount()`: send them on `@nxgt/janus` 0.9's
|
|
122
131
|
`user.secondFactorEnabled` and `user.secondFactorDisabled` events. Seven
|
|
123
132
|
e-mails now, and seven templates to pass for a locale beyond `en` and `fr`.
|
|
124
|
-
- **Dark mode, v0.3.0.** The e-mails follow the reader's dark mode in
|
|
125
|
-
every client that supports it, Gmail excepted: a dark page, a dark card and
|
|
126
|
-
light text. Built from `@nxgt/mail-ui` 0.4.0, `@nxgt/mail-presets` 0.4.0
|
|
127
|
-
and `@nxgt/mail-i18n` 0.5.0; the text parts, subjects and variables are
|
|
128
|
-
unchanged, the manifest is still format 1, and the `@nxgt/mail` peer stays
|
|
129
|
-
`>=0.1.0 <1`.
|
|
130
|
-
|
|
131
|
-
- **The expiry in the e-mail, v0.2.0.** The verification, reset and sign-in
|
|
132
|
-
code e-mails say how long the link or code lasts — "1 hour", "1 heure" —
|
|
133
|
-
derived from the flow's `expiresAt` in the recipient's locale, measured
|
|
134
|
-
against `janusMail({ clock })` (the clock given to `janus()`), or given per
|
|
135
|
-
send as `{ expiresIn }`. Two breaks: a template called directly takes
|
|
136
|
-
`expiresIn`, and `signInCode` reads `issued.expiresAt`. Built from
|
|
137
|
-
`@nxgt/mail-presets` 0.2.0, whose text parts keep each paragraph on one
|
|
138
|
-
line.
|
|
139
|
-
|
|
140
|
-
- **The first release, v0.1.0.** The five e-mails of `@nxgt/janus`'s flows,
|
|
141
|
-
in English and French, over any `@nxgt/mail` transport: `janusMail()` and
|
|
142
|
-
`janusTemplates()`. Built with Maizzle when the package is built, shipped
|
|
143
|
-
in `mails/`, and only filled in at send time, every value escaped.
|