@nxgt/janus-mail 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -223,7 +223,8 @@ without the same `clock` here, a clock set in the past makes every send an
223
223
 
224
224
  **Locales are BCP 47 tags: `fr-CA`, never `fr_CA`.** The expiry is
225
225
  formatted by `Intl` in the locale picked, so a locale `Intl` refuses is a
226
- `TypeError` from `janusMail()`.
226
+ `TypeError` from `janusMail()`:
227
+ [`janusMail: locales must be BCP 47 language tags, as 'fr-CA'`](docs/troubleshooting.md#janusmail-locales-must-be-bcp-47-language-tags-as-fr-ca).
227
228
 
228
229
  **The expiry is formatted by the runtime's `Intl`.** A locale the runtime has
229
230
  no data for is formatted in its default language, and French puts a no-break
@@ -259,9 +260,13 @@ The symptoms and fixes are in [troubleshooting](docs/troubleshooting.md).
259
260
  ## Type safety, counted
260
261
 
261
262
  **Twenty-five plausible mistakes, twenty-five refused at compile time.**
262
- [`test/types/refusals.ts`](test/types/refusals.ts) holds one
263
- `@ts-expect-error` per mistake, beside the calls that must keep compiling —
264
- among them adding a language with every template — and
263
+ Three files hold one `@ts-expect-error` per mistake, beside the calls that
264
+ must keep compiling:
265
+ [`test/types/send-refusals.ts`](test/types/send-refusals.ts) the eight of a
266
+ send, 1 to 8;
267
+ [`test/types/option-refusals.ts`](test/types/option-refusals.ts) the twelve
268
+ of `janusMail()`'s options, 9 to 20, beside adding a language with every
269
+ template; and
265
270
  [`test/types/expiry-refusals.ts`](test/types/expiry-refusals.ts) the five of
266
271
  the expiry and the clock, 21 to 25:
267
272
 
package/dist/index.js CHANGED
@@ -215,7 +215,7 @@ function resolveLocales(options) {
215
215
  }
216
216
  const locales = Object.freeze([...wanted]);
217
217
  if (!locales.every(isLanguageTag)) {
218
- refuse("locales must be BCP 47 language tags, with hyphens, as 'fr-CA'");
218
+ refuse("locales must be BCP 47 language tags, as 'fr-CA'");
219
219
  }
220
220
  if (new Set(locales).size !== locales.length) {
221
221
  refuse("locales holds the same locale twice");
@@ -298,5 +298,5 @@ export {
298
298
  janusTemplates
299
299
  };
300
300
 
301
- //# debugId=941D325BF4DF9C0164756E2164756E21
301
+ //# debugId=B823BF1AC1618A7C64756E2164756E21
302
302
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -9,10 +9,10 @@
9
9
  "// Generated by scripts/build-mail.ts from mails/mail-manifest.json, after\n// each build of the default e-mails. Never edited; committed.\n\n/** The locales the default e-mails are built in, the fallback locale first. */\nexport const LOCALES = ['en', 'fr'] as const;\n\n/** A locale the default e-mails are built in. */\nexport type JanusMailLocale = (typeof LOCALES)[number];\n",
10
10
  "/**\n * Where the default e-mails are, and the one renderer that reads them.\n *\n * The package ships the built e-mails in `mails/`, beside `dist/` — not in\n * it, because the root `build.ts` removes from `dist/` every file it did not\n * write itself.\n */\nimport { fileURLToPath } from 'node:url';\nimport { createMailRenderer, type MailRenderer } from '@nxgt/mail/renderer';\nimport type { MailEmails } from './generated/mail';\n\n/**\n * The folder of the built e-mails, resolved from this module rather than from\n * the working directory, which is the consumer's.\n *\n * **This file must stay directly under `src/`.** `../mails/` is right from\n * `src/mails.ts` in the specs, and from `dist/index.js` in the tarball,\n * because the build bundles this module into the single entry\n * `dist/index.js`, one level under the package as `src/` is. Moved into a\n * subfolder, or bundled into `dist/chunks/` by a second entry point, it would\n * resolve a folder that does not exist — which the artifact spec, rendering\n * from `../dist`, would catch.\n */\nexport const MAILS_DIR = fileURLToPath(new URL('../mails/', import.meta.url));\n\nlet renderer: MailRenderer<MailEmails> | undefined;\n\n/**\n * The renderer of the default e-mails, created on first use and shared by\n * every `janusMail()` and `janusTemplates()` after it.\n *\n * Lazy, so importing the package reads nothing: an application that\n * overrides every template never opens `mails/`. The first default template\n * rendered reads the whole build once — a missing `mails/` throws there, with\n * `createMailRenderer`'s own message.\n */\nexport function defaultRenderer(): MailRenderer<MailEmails> {\n\trenderer ??= createMailRenderer<MailEmails>({ dir: MAILS_DIR });\n\treturn renderer;\n}\n",
11
11
  "/**\n * The default templates: the prebuilt e-mails, filled by `@nxgt/mail`'s\n * renderer. Each picks from its variables exactly what its e-mail takes — the\n * renderer refuses a variable too many — so the `locale` beside them goes to\n * the renderer's options, and nowhere else.\n */\nimport type { Rendered } from '@nxgt/mail';\nimport { defaultRenderer } from './mails';\nimport type { JanusMailTemplates } from './types';\n\n/**\n * The five default templates, in the locales they are built in. The\n * renderer behind them is created on the first render, not here.\n *\n * ```ts\n * const defaults = janusTemplates();\n * const { subject } = await defaults.verifyEmail({ brand: 'Acme', name: 'Ada', link, locale: 'fr' });\n * ```\n *\n * A URL that is not `http:`, `https:` or `mailto:` throws `MailRefused`; a\n * missing `mails/` folder throws when the first one renders.\n */\nexport function janusTemplates(): JanusMailTemplates {\n\treturn Object.freeze({\n\t\tverifyEmail: ({ brand, name, link, expiresIn, locale }): Rendered =>\n\t\t\tdefaultRenderer().render(\n\t\t\t\t'verify-email',\n\t\t\t\t{ brand, name, link, expiresIn },\n\t\t\t\t{ locale },\n\t\t\t),\n\t\tresetPassword: ({ brand, name, link, expiresIn, locale }): Rendered =>\n\t\t\tdefaultRenderer().render(\n\t\t\t\t'reset-password',\n\t\t\t\t{ brand, name, link, expiresIn },\n\t\t\t\t{ locale },\n\t\t\t),\n\t\tsignInCode: ({ brand, code, expiresIn, locale }): Rendered =>\n\t\t\tdefaultRenderer().render(\n\t\t\t\t'sign-in-code',\n\t\t\t\t{ brand, code, expiresIn },\n\t\t\t\t{ locale },\n\t\t\t),\n\t\tpasswordChanged: ({ brand, name, link, locale }): Rendered =>\n\t\t\tdefaultRenderer().render(\n\t\t\t\t'password-changed',\n\t\t\t\t{ brand, name, link },\n\t\t\t\t{ locale },\n\t\t\t),\n\t\temailChanged: ({ brand, name, link, newEmail, locale }): Rendered =>\n\t\t\tdefaultRenderer().render(\n\t\t\t\t'email-changed',\n\t\t\t\t{ brand, name, link, newEmail },\n\t\t\t\t{ locale },\n\t\t\t),\n\t});\n}\n",
12
- "/**\n * `janusMail()`'s options, checked once when it is called. A mistake is a\n * wiring mistake, so a bare `TypeError` naming the option — never its value —\n * thrown before anything is sent.\n */\nimport type { Clock } from '@nxgt/janus';\nimport type { Address, Mailer } from '@nxgt/mail';\nimport { LOCALES } from './generated/locales';\nimport { janusTemplates } from './templates';\nimport type {\n\tJanusMailLinks,\n\tJanusMailTemplateName,\n\tJanusMailTemplates,\n} from './types';\n\n/** The templates in the order the documentation lists them. */\nexport const TEMPLATE_NAMES: readonly JanusMailTemplateName[] = [\n\t'verifyEmail',\n\t'resetPassword',\n\t'signInCode',\n\t'passwordChanged',\n\t'emailChanged',\n];\n\nconst LINK_NAMES = ['verifyEmail', 'resetPassword', 'secureAccount'] as const;\n\n/** The options once checked, with `string` for the locales: the degenericised mirror. */\nexport interface ResolvedOptions {\n\treadonly mailer: Mailer;\n\treadonly from: Address;\n\treadonly replyTo: Address | undefined;\n\treadonly brand: string;\n\treadonly links: JanusMailLinks;\n\treadonly locales: readonly string[];\n\treadonly fallbackLocale: string;\n\treadonly templates: JanusMailTemplates<string>;\n\treadonly clock: Clock;\n}\n\n/** The system clock, written here: `@nxgt/janus` is a peer for its types only. */\nconst SYSTEM_CLOCK: Clock = Object.freeze({ now: () => new Date() });\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isAddress(value: unknown): value is Address {\n\tif (typeof value === 'string') return value.trim() !== '';\n\treturn (\n\t\tisObject(value) &&\n\t\ttypeof value.name === 'string' &&\n\t\ttypeof value.address === 'string' &&\n\t\tvalue.address.trim() !== ''\n\t);\n}\n\nfunction refuse(message: string): never {\n\tthrow new TypeError(`janusMail: ${message}`);\n}\n\n/** `mailer`, `from`, `replyTo`, `brand` and `links`. */\nfunction checkSending(options: Record<string, unknown>): void {\n\tconst { mailer, from, replyTo, brand, links } = options;\n\tif (!isObject(mailer) || typeof mailer.send !== 'function') {\n\t\trefuse('mailer must be a Mailer — an object with a send function');\n\t}\n\tif (!isAddress(from)) {\n\t\trefuse(\n\t\t\t\"from must be an address, as 'noreply@example.com' or { name, address }\",\n\t\t);\n\t}\n\tif (replyTo !== undefined && !isAddress(replyTo)) {\n\t\trefuse(\n\t\t\t\"replyTo must be an address, as 'support@example.com' or { name, address }\",\n\t\t);\n\t}\n\tif (typeof brand !== 'string' || brand.trim() === '') {\n\t\trefuse(\"brand must be the name the e-mails show, as 'Acme'\");\n\t}\n\tif (!isObject(links)) {\n\t\trefuse(\n\t\t\t'links must be an object, as { verifyEmail, resetPassword, secureAccount }',\n\t\t);\n\t}\n\tfor (const name of LINK_NAMES) {\n\t\tif (typeof links[name] !== 'function') {\n\t\t\trefuse(`links.${name} must be a function`);\n\t\t}\n\t}\n}\n\n/** `clock`, bound to the caller's object, or the system clock. */\nfunction resolveClock(clock: unknown): Clock {\n\tif (clock === undefined) return SYSTEM_CLOCK;\n\tif (!isObject(clock) || typeof clock.now !== 'function') {\n\t\trefuse('clock must be a Clock — an object with a now function');\n\t}\n\treturn Object.freeze({ now: (clock.now as () => Date).bind(clock) });\n}\n\n/** Whether `Intl` takes the locale — the expiry is formatted in it — as `fr-CA`, not `fr_CA`. */\nfunction isLanguageTag(locale: string): boolean {\n\ttry {\n\t\tIntl.getCanonicalLocales(locale);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/** `locales` and `fallbackLocale`, with their defaults. */\nfunction resolveLocales(options: Record<string, unknown>): {\n\tlocales: readonly string[];\n\tfallbackLocale: string;\n} {\n\tconst wanted = options.locales ?? LOCALES;\n\tif (\n\t\t!Array.isArray(wanted) ||\n\t\twanted.length === 0 ||\n\t\t!wanted.every((locale) => typeof locale === 'string' && locale !== '')\n\t) {\n\t\trefuse(\"locales must list at least one locale, as ['en', 'fr']\");\n\t}\n\tconst locales: readonly string[] = Object.freeze([...wanted]);\n\tif (!locales.every(isLanguageTag)) {\n\t\trefuse(\"locales must be BCP 47 language tags, with hyphens, as 'fr-CA'\");\n\t}\n\tif (new Set(locales).size !== locales.length) {\n\t\trefuse('locales holds the same locale twice');\n\t}\n\tconst fallbackLocale =\n\t\toptions.fallbackLocale ??\n\t\t(locales.includes('en') ? 'en' : (locales[0] as string));\n\tif (typeof fallbackLocale !== 'string' || !locales.includes(fallbackLocale)) {\n\t\trefuse('fallbackLocale must be one of locales');\n\t}\n\treturn { locales, fallbackLocale };\n}\n\n/** `templates` over the defaults — the defaults only while every locale is built. */\nfunction resolveTemplates(\n\toptions: Record<string, unknown>,\n\tlocales: readonly string[],\n): JanusMailTemplates<string> {\n\tconst given = options.templates ?? {};\n\tif (!isObject(given)) {\n\t\trefuse(\n\t\t\t'templates must be an object of functions, as { verifyEmail: (variables) => rendered }',\n\t\t);\n\t}\n\tfor (const [name, template] of Object.entries(given)) {\n\t\tif (!(TEMPLATE_NAMES as readonly string[]).includes(name)) {\n\t\t\trefuse(\n\t\t\t\t`templates has no template ${name} — name one of ${TEMPLATE_NAMES.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (typeof template !== 'function') {\n\t\t\trefuse(`templates.${name} must be a function`);\n\t\t}\n\t}\n\t// Own enumerable keys, the ones the spread below copies. A template on a\n\t// prototype — a class instance's method — or an own property that is not\n\t// enumerable would be dropped without a word, the default sent in its\n\t// place: `in` sees it and `passed` does not, so it is refused instead.\n\tconst passed = Object.keys(given);\n\tfor (const name of TEMPLATE_NAMES) {\n\t\tif (name in given && !passed.includes(name)) {\n\t\t\trefuse(\n\t\t\t\t`templates.${name} is not an own enumerable property — pass a plain object, as { ${name}: (variables) => rendered }`,\n\t\t\t);\n\t\t}\n\t}\n\tconst built = locales.every((locale) =>\n\t\t(LOCALES as readonly string[]).includes(locale),\n\t);\n\tconst missing = TEMPLATE_NAMES.filter((name) => !passed.includes(name));\n\tif (!built && missing.length > 0) {\n\t\trefuse(\n\t\t\t`the default templates are built in ${LOCALES.join(' and ')} only — with another ` +\n\t\t\t\t`locale in locales, pass every template in templates; ${missing.join(', ')} missing`,\n\t\t);\n\t}\n\t// The one cast: a default template takes a built locale only, and the check\n\t// above proves every locale is one whenever a default is kept.\n\tconst defaults = janusTemplates() as unknown as JanusMailTemplates<string>;\n\treturn Object.freeze({ ...defaults, ...given }) as JanusMailTemplates<string>;\n}\n\n/** A frozen copy of a checked address: a later change to the caller's object is not seen. */\nfunction frozenAddress(address: Address): Address {\n\treturn typeof address === 'string'\n\t\t? address\n\t\t: Object.freeze({ name: address.name, address: address.address });\n}\n\n/**\n * A frozen copy of the checked links, each bound to the caller's object — a\n * class instance's methods keep their `this` — so replacing a link after\n * `janusMail()` cannot undo the check.\n */\nfunction frozenLinks(links: JanusMailLinks): JanusMailLinks {\n\treturn Object.freeze({\n\t\tverifyEmail: links.verifyEmail.bind(links),\n\t\tresetPassword: links.resetPassword.bind(links),\n\t\tsecureAccount: links.secureAccount.bind(links),\n\t});\n}\n\n/**\n * Checks `janusMail()`'s options and answers them resolved, or throws a\n * `TypeError`. What was checked is copied and frozen, so the guarantee made\n * here holds at every send.\n */\nexport function resolveOptions(options: unknown): ResolvedOptions {\n\tif (!isObject(options)) {\n\t\trefuse('options must be an object, as { mailer, from, brand, links }');\n\t}\n\tcheckSending(options);\n\tconst { locales, fallbackLocale } = resolveLocales(options);\n\treturn Object.freeze({\n\t\tmailer: options.mailer as Mailer,\n\t\tfrom: frozenAddress(options.from as Address),\n\t\treplyTo:\n\t\t\toptions.replyTo === undefined\n\t\t\t\t? undefined\n\t\t\t\t: frozenAddress(options.replyTo as Address),\n\t\tbrand: options.brand as string,\n\t\tlinks: frozenLinks(options.links as unknown as JanusMailLinks),\n\t\tlocales,\n\t\tfallbackLocale,\n\t\ttemplates: resolveTemplates(options, locales),\n\t\tclock: resolveClock(options.clock),\n\t});\n}\n",
12
+ "/**\n * `janusMail()`'s options, checked once when it is called. A mistake is a\n * wiring mistake, so a bare `TypeError` naming the option — never its value —\n * thrown before anything is sent.\n */\nimport type { Clock } from '@nxgt/janus';\nimport type { Address, Mailer } from '@nxgt/mail';\nimport { LOCALES } from './generated/locales';\nimport { janusTemplates } from './templates';\nimport type {\n\tJanusMailLinks,\n\tJanusMailTemplateName,\n\tJanusMailTemplates,\n} from './types';\n\n/** The templates in the order the documentation lists them. */\nexport const TEMPLATE_NAMES: readonly JanusMailTemplateName[] = [\n\t'verifyEmail',\n\t'resetPassword',\n\t'signInCode',\n\t'passwordChanged',\n\t'emailChanged',\n];\n\nconst LINK_NAMES = ['verifyEmail', 'resetPassword', 'secureAccount'] as const;\n\n/** The options once checked, with `string` for the locales: the degenericised mirror. */\nexport interface ResolvedOptions {\n\treadonly mailer: Mailer;\n\treadonly from: Address;\n\treadonly replyTo: Address | undefined;\n\treadonly brand: string;\n\treadonly links: JanusMailLinks;\n\treadonly locales: readonly string[];\n\treadonly fallbackLocale: string;\n\treadonly templates: JanusMailTemplates<string>;\n\treadonly clock: Clock;\n}\n\n/** The system clock, written here: `@nxgt/janus` is a peer for its types only. */\nconst SYSTEM_CLOCK: Clock = Object.freeze({ now: () => new Date() });\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isAddress(value: unknown): value is Address {\n\tif (typeof value === 'string') return value.trim() !== '';\n\treturn (\n\t\tisObject(value) &&\n\t\ttypeof value.name === 'string' &&\n\t\ttypeof value.address === 'string' &&\n\t\tvalue.address.trim() !== ''\n\t);\n}\n\nfunction refuse(message: string): never {\n\tthrow new TypeError(`janusMail: ${message}`);\n}\n\n/** `mailer`, `from`, `replyTo`, `brand` and `links`. */\nfunction checkSending(options: Record<string, unknown>): void {\n\tconst { mailer, from, replyTo, brand, links } = options;\n\tif (!isObject(mailer) || typeof mailer.send !== 'function') {\n\t\trefuse('mailer must be a Mailer — an object with a send function');\n\t}\n\tif (!isAddress(from)) {\n\t\trefuse(\n\t\t\t\"from must be an address, as 'noreply@example.com' or { name, address }\",\n\t\t);\n\t}\n\tif (replyTo !== undefined && !isAddress(replyTo)) {\n\t\trefuse(\n\t\t\t\"replyTo must be an address, as 'support@example.com' or { name, address }\",\n\t\t);\n\t}\n\tif (typeof brand !== 'string' || brand.trim() === '') {\n\t\trefuse(\"brand must be the name the e-mails show, as 'Acme'\");\n\t}\n\tif (!isObject(links)) {\n\t\trefuse(\n\t\t\t'links must be an object, as { verifyEmail, resetPassword, secureAccount }',\n\t\t);\n\t}\n\tfor (const name of LINK_NAMES) {\n\t\tif (typeof links[name] !== 'function') {\n\t\t\trefuse(`links.${name} must be a function`);\n\t\t}\n\t}\n}\n\n/** `clock`, bound to the caller's object, or the system clock. */\nfunction resolveClock(clock: unknown): Clock {\n\tif (clock === undefined) return SYSTEM_CLOCK;\n\tif (!isObject(clock) || typeof clock.now !== 'function') {\n\t\trefuse('clock must be a Clock — an object with a now function');\n\t}\n\treturn Object.freeze({ now: (clock.now as () => Date).bind(clock) });\n}\n\n/** Whether `Intl` takes the locale — the expiry is formatted in it — as `fr-CA`, not `fr_CA`. */\nfunction isLanguageTag(locale: string): boolean {\n\ttry {\n\t\tIntl.getCanonicalLocales(locale);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/** `locales` and `fallbackLocale`, with their defaults. */\nfunction resolveLocales(options: Record<string, unknown>): {\n\tlocales: readonly string[];\n\tfallbackLocale: string;\n} {\n\tconst wanted = options.locales ?? LOCALES;\n\tif (\n\t\t!Array.isArray(wanted) ||\n\t\twanted.length === 0 ||\n\t\t!wanted.every((locale) => typeof locale === 'string' && locale !== '')\n\t) {\n\t\trefuse(\"locales must list at least one locale, as ['en', 'fr']\");\n\t}\n\tconst locales: readonly string[] = Object.freeze([...wanted]);\n\tif (!locales.every(isLanguageTag)) {\n\t\trefuse(\"locales must be BCP 47 language tags, as 'fr-CA'\");\n\t}\n\tif (new Set(locales).size !== locales.length) {\n\t\trefuse('locales holds the same locale twice');\n\t}\n\tconst fallbackLocale =\n\t\toptions.fallbackLocale ??\n\t\t(locales.includes('en') ? 'en' : (locales[0] as string));\n\tif (typeof fallbackLocale !== 'string' || !locales.includes(fallbackLocale)) {\n\t\trefuse('fallbackLocale must be one of locales');\n\t}\n\treturn { locales, fallbackLocale };\n}\n\n/** `templates` over the defaults — the defaults only while every locale is built. */\nfunction resolveTemplates(\n\toptions: Record<string, unknown>,\n\tlocales: readonly string[],\n): JanusMailTemplates<string> {\n\tconst given = options.templates ?? {};\n\tif (!isObject(given)) {\n\t\trefuse(\n\t\t\t'templates must be an object of functions, as { verifyEmail: (variables) => rendered }',\n\t\t);\n\t}\n\tfor (const [name, template] of Object.entries(given)) {\n\t\tif (!(TEMPLATE_NAMES as readonly string[]).includes(name)) {\n\t\t\trefuse(\n\t\t\t\t`templates has no template ${name} — name one of ${TEMPLATE_NAMES.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (typeof template !== 'function') {\n\t\t\trefuse(`templates.${name} must be a function`);\n\t\t}\n\t}\n\t// Own enumerable keys, the ones the spread below copies. A template on a\n\t// prototype — a class instance's method — or an own property that is not\n\t// enumerable would be dropped without a word, the default sent in its\n\t// place: `in` sees it and `passed` does not, so it is refused instead.\n\tconst passed = Object.keys(given);\n\tfor (const name of TEMPLATE_NAMES) {\n\t\tif (name in given && !passed.includes(name)) {\n\t\t\trefuse(\n\t\t\t\t`templates.${name} is not an own enumerable property — pass a plain object, as { ${name}: (variables) => rendered }`,\n\t\t\t);\n\t\t}\n\t}\n\tconst built = locales.every((locale) =>\n\t\t(LOCALES as readonly string[]).includes(locale),\n\t);\n\tconst missing = TEMPLATE_NAMES.filter((name) => !passed.includes(name));\n\tif (!built && missing.length > 0) {\n\t\trefuse(\n\t\t\t`the default templates are built in ${LOCALES.join(' and ')} only — with another ` +\n\t\t\t\t`locale in locales, pass every template in templates; ${missing.join(', ')} missing`,\n\t\t);\n\t}\n\t// The one cast: a default template takes a built locale only, and the check\n\t// above proves every locale is one whenever a default is kept.\n\tconst defaults = janusTemplates() as unknown as JanusMailTemplates<string>;\n\treturn Object.freeze({ ...defaults, ...given }) as JanusMailTemplates<string>;\n}\n\n/** A frozen copy of a checked address: a later change to the caller's object is not seen. */\nfunction frozenAddress(address: Address): Address {\n\treturn typeof address === 'string'\n\t\t? address\n\t\t: Object.freeze({ name: address.name, address: address.address });\n}\n\n/**\n * A frozen copy of the checked links, each bound to the caller's object — a\n * class instance's methods keep their `this` — so replacing a link after\n * `janusMail()` cannot undo the check.\n */\nfunction frozenLinks(links: JanusMailLinks): JanusMailLinks {\n\treturn Object.freeze({\n\t\tverifyEmail: links.verifyEmail.bind(links),\n\t\tresetPassword: links.resetPassword.bind(links),\n\t\tsecureAccount: links.secureAccount.bind(links),\n\t});\n}\n\n/**\n * Checks `janusMail()`'s options and answers them resolved, or throws a\n * `TypeError`. What was checked is copied and frozen, so the guarantee made\n * here holds at every send.\n */\nexport function resolveOptions(options: unknown): ResolvedOptions {\n\tif (!isObject(options)) {\n\t\trefuse('options must be an object, as { mailer, from, brand, links }');\n\t}\n\tcheckSending(options);\n\tconst { locales, fallbackLocale } = resolveLocales(options);\n\treturn Object.freeze({\n\t\tmailer: options.mailer as Mailer,\n\t\tfrom: frozenAddress(options.from as Address),\n\t\treplyTo:\n\t\t\toptions.replyTo === undefined\n\t\t\t\t? undefined\n\t\t\t\t: frozenAddress(options.replyTo as Address),\n\t\tbrand: options.brand as string,\n\t\tlinks: frozenLinks(options.links as unknown as JanusMailLinks),\n\t\tlocales,\n\t\tfallbackLocale,\n\t\ttemplates: resolveTemplates(options, locales),\n\t\tclock: resolveClock(options.clock),\n\t});\n}\n",
13
13
  "/**\n * `janusMail()`: the e-mails of Janus's flows, rendered by the templates and\n * handed to the mailer. It sends what a flow answered, to the address the\n * flow answered, and nothing else: no retry, no queue, no error of its own —\n * the mailer's `MailFailure` and `MailRefused` reach the caller untouched.\n */\n\nimport { expiringMails } from './expiring-mails';\nimport { noticeMails } from './notice-mails';\nimport { resolveOptions } from './options';\nimport type { JanusMail, JanusMailLocale, JanusMailOptions } from './types';\n\n/**\n * The e-mails of Janus's flows, over your mailer:\n *\n * ```ts\n * const mail = janusMail({\n * \tmailer,\n * \tfrom: 'noreply@acme.example',\n * \tbrand: 'Acme',\n * \tlinks: {\n * \t\tverifyEmail: (token) => `https://acme.example/verify?token=${token}`,\n * \t\tresetPassword: (token) => `https://acme.example/reset?token=${token}`,\n * \t\tsecureAccount: () => 'https://acme.example/account/security',\n * \t},\n * });\n *\n * await mail.verifyEmail(await auth.verifyEmail.send(user), { name: user.name, locale: user.locale });\n * ```\n *\n * Reads nothing when called: the default e-mails are read on the first one\n * sent. A wrong option is a bare `TypeError`, thrown here.\n */\nexport function janusMail<const L extends string = JanusMailLocale>(\n\toptions: JanusMailOptions<L>,\n): JanusMail<L> {\n\tconst resolved = resolveOptions(options);\n\tconst mail: JanusMail<string> = Object.freeze({\n\t\t...expiringMails(resolved),\n\t\t...noticeMails(resolved),\n\t\ttemplates: resolved.templates,\n\t\tlocales: resolved.locales,\n\t});\n\t// The one cast at the boundary: the mirror above works on `string` locales.\n\treturn mail as unknown as JanusMail<L>;\n}\n"
14
14
  ],
15
- "mappings": "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",
16
- "debugId": "941D325BF4DF9C0164756E2164756E21",
15
+ "mappings": "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",
16
+ "debugId": "B823BF1AC1618A7C64756E2164756E21",
17
17
  "names": []
18
18
  }
@@ -95,6 +95,28 @@ the build fails until it holds every message the presets use in `en` (see
95
95
  `@nxgt/mail-presets`'
96
96
  [Another locale](https://github.com/softistx/nxgt-mail/blob/develop/packages/mail-presets/docs/guide/presets.md#another-locale)).
97
97
 
98
+ An override in a catalogue uses the presets' own key, in kebab-case since
99
+ `@nxgt/mail-presets` 0.3.0, and nested, never dotted — `presets.link-expires`
100
+ is written:
101
+
102
+ ```json
103
+ { "presets": { "link-expires": "…" } }
104
+ ```
105
+
106
+ `@nxgt/mail-i18n` accepts a camelCase key too, so an override under a key from
107
+ before presets 0.3.0, such as `presets.linkExpires`, is a key of its own that
108
+ no preset reads. In `en`, the fallback locale, it is ignored without an
109
+ error, and the preset's own text goes out instead — in `fr` too, when `fr`
110
+ carries the same old key. In `fr` alone, the build refuses it:
111
+
112
+ ```text
113
+ Error: i18n: fr: presets.linkExpires is not a key of en, the fallback locale
114
+ ```
115
+
116
+ The presets'
117
+ [Upgrading](https://github.com/softistx/nxgt-mail/blob/develop/packages/mail-presets/docs/troubleshooting.md#upgrading)
118
+ entry lists every old and new key.
119
+
98
120
  ## Why `mails/` is not in `dist/`
99
121
 
100
122
  The root `build.ts` removes from `dist/` every file it did not write itself,
@@ -48,6 +48,10 @@ override can branch on it.
48
48
  | `locales` | `['en', 'fr']`, the built ones | The locales an e-mail can be sent in. Its type is inferred as written: `['en']` makes `L` `'en'` |
49
49
  | `fallbackLocale` | `'en'` when in `locales`, else the first of them | Sent when the recipient wants none of `locales`. Must be one of them: another is a compile error, and a `TypeError` |
50
50
 
51
+ Each locale must be a BCP 47 tag — `fr-CA`, never `fr_CA` — and `janusMail()`
52
+ refuses anything else with a `TypeError`:
53
+ [`janusMail: locales must be BCP 47 language tags, as 'fr-CA'`](../troubleshooting.md#janusmail-locales-must-be-bcp-47-language-tags-as-fr-ca).
54
+
51
55
  **Fewer locales.** `locales: ['en']` sends English to everyone, a French
52
56
  speaker included; the defaults still fit, and `templates` stays partial.
53
57
 
@@ -153,7 +153,7 @@ them renders: that is when the package's `mails/` folder is read, once.
153
153
  ## What the defaults say
154
154
 
155
155
  The defaults are [`@nxgt/mail-presets`](https://www.npmjs.com/package/@nxgt/mail-presets)
156
- 0.2.0, built with a neutral grey theme and no logo:
156
+ 0.3.0, built with a neutral grey theme and no logo:
157
157
 
158
158
  | Template | Built from | Subject (`en`) | Subject (`fr`) |
159
159
  | --- | --- | --- | --- |
@@ -28,13 +28,13 @@ How the messages are shaped:
28
28
  - [`janusMail: links must be an object, as { verifyEmail, resetPassword, secureAccount }`](#janusmail-links-must-be-an-object-as--verifyemail-resetpassword-secureaccount-)
29
29
  - [`janusMail: links.<name> must be a function`](#janusmail-linksname-must-be-a-function)
30
30
  - [`janusMail: locales must list at least one locale, as ['en', 'fr']`](#janusmail-locales-must-list-at-least-one-locale-as-en-fr)
31
+ - [`janusMail: locales must be BCP 47 language tags, as 'fr-CA'`](#janusmail-locales-must-be-bcp-47-language-tags-as-fr-ca)
31
32
  - [`janusMail: locales holds the same locale twice`](#janusmail-locales-holds-the-same-locale-twice)
32
33
  - [`janusMail: fallbackLocale must be one of locales`](#janusmail-fallbacklocale-must-be-one-of-locales)
33
34
  - [`janusMail: templates must be an object of functions, as { verifyEmail: (variables) => rendered }`](#janusmail-templates-must-be-an-object-of-functions-as--verifyemail-variables--rendered-)
34
35
  - [`janusMail: templates has no template <name> — name one of verifyEmail, resetPassword, signInCode, passwordChanged, emailChanged`](#janusmail-templates-has-no-template-name--name-one-of-verifyemail-resetpassword-signincode-passwordchanged-emailchanged)
35
36
  - [`janusMail: templates.<name> must be a function`](#janusmail-templatesname-must-be-a-function)
36
37
  - [`janusMail: templates.<name> is not an own enumerable property — pass a plain object, as { <name>: (variables) => rendered }`](#janusmail-templatesname-is-not-an-own-enumerable-property--pass-a-plain-object-as--name-variables--rendered-)
37
- - [`janusMail: locales must be BCP 47 language tags, with hyphens, as 'fr-CA'`](#janusmail-locales-must-be-bcp-47-language-tags-with-hyphens-as-fr-ca)
38
38
  - [`janusMail: clock must be a Clock — an object with a now function`](#janusmail-clock-must-be-a-clock--an-object-with-a-now-function)
39
39
  - [`janusMail: the default templates are built in en and fr only — with another locale in locales, pass every template in templates; <names> missing`](#janusmail-the-default-templates-are-built-in-en-and-fr-only--with-another-locale-in-locales-pass-every-template-in-templates-names-missing)
40
40
 
@@ -122,6 +122,19 @@ links: {
122
122
  `locales: []`, `locales: 'en'`, or a list holding something that is not a
123
123
  non-empty string. Leave it out for `['en', 'fr']`.
124
124
 
125
+ ### `janusMail: locales must be BCP 47 language tags, as 'fr-CA'`
126
+
127
+ A `TypeError` when `janusMail()` is called: a locale in `locales` is one
128
+ `Intl` refuses — `de_DE` with an underscore, a grandfathered `i-klingon`, a
129
+ bare private-use `x-…`. The expiry is formatted by `Intl` in the locale
130
+ picked, so a locale it cannot parse would fail at the first send; it is
131
+ refused here instead. Write a tag `Intl` takes — `de-DE`, not `de_DE`; `tlh`,
132
+ not `i-klingon`:
133
+
134
+ ```ts
135
+ janusMail({ mailer, from, brand: 'Acme', links, locales: ['en', 'fr', 'de-DE'], templates });
136
+ ```
137
+
125
138
  ### `janusMail: locales holds the same locale twice`
126
139
 
127
140
  `['en', 'en']`. List each once.
@@ -176,18 +189,6 @@ out: the defaults could not render them in that locale. Pass all five — see
176
189
  [Adding a locale](guide/locales.md#adding-a-locale) — or drop the locale.
177
190
  The message lists the templates missing.
178
191
 
179
- ### `janusMail: locales must be BCP 47 language tags, with hyphens, as 'fr-CA'`
180
-
181
- A `TypeError` when `janusMail()` is called: a locale in `locales` is one
182
- `Intl` refuses — `de_DE` with an underscore, a grandfathered `i-klingon`, a
183
- bare private-use `x-…`. The expiry is formatted by `Intl` in the locale
184
- picked, so a locale it cannot parse would fail at the first send; it is
185
- refused here instead. Write the tag with hyphens:
186
-
187
- ```ts
188
- janusMail({ mailer, from, brand: 'Acme', links, locales: ['en', 'fr', 'de-DE'], templates });
189
- ```
190
-
191
192
  ### `janusMail: clock must be a Clock — an object with a now function`
192
193
 
193
194
  `clock` is `@nxgt/janus`'s `Clock`, `{ now(): Date }` — pass the one given to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/janus-mail",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "The e-mails of @nxgt/janus's flows — e-mail verification, password reset, sign-in code, password changed, e-mail changed — rendered in English and French from a prebuilt Maizzle build, with your brand, sent through any @nxgt/mail transport",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -56,9 +56,9 @@
56
56
  "@nxgt/janus": "^0.8.8",
57
57
  "@nxgt/mail": "0.6.0",
58
58
  "@nxgt/mail-config": "0.2.1",
59
- "@nxgt/mail-i18n": "0.3.1",
60
- "@nxgt/mail-presets": "0.2.0",
61
- "@nxgt/mail-ui": "0.2.0",
59
+ "@nxgt/mail-i18n": "0.4.0",
60
+ "@nxgt/mail-presets": "0.3.0",
61
+ "@nxgt/mail-ui": "0.3.0",
62
62
  "@types/bun": "^1.4.2",
63
63
  "vue": "3.5.43",
64
64
  "vue-tsc": "^3.3.11"