@cosmicdrift/kumiko-renderer-mail-html 0.371.1 → 0.372.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/dist/document-kit.js +5 -57
- package/package.json +7 -7
package/dist/document-kit.js
CHANGED
|
@@ -5,69 +5,17 @@ import { selectMany } from "@cosmicdrift/kumiko-framework/db";
|
|
|
5
5
|
import { isUuid, SYSTEM_TENANT_ID, TENANT_CURRENCY_CONFIG_KEY, } from "@cosmicdrift/kumiko-framework/engine";
|
|
6
6
|
import { fileRefsTable } from "@cosmicdrift/kumiko-framework/files";
|
|
7
7
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
8
|
-
import {
|
|
9
|
-
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
8
|
+
import { DOCUMENT_FALLBACK_CURRENCY, formatDocumentDate, formatDocumentMoney, formatDocumentPercent, } from "@cosmicdrift/kumiko-headless";
|
|
10
9
|
import Handlebars from "handlebars";
|
|
11
10
|
// Handlebars instance, template resolution and resource lookup shared by the
|
|
12
11
|
// mail and PDF renderers, so both format money/dates and find images the same way.
|
|
13
12
|
const documentKitLogger = createFallbackLogger("renderer-mail-html");
|
|
14
|
-
const FALLBACK_CURRENCY = "EUR";
|
|
15
13
|
const SAFE_LINK_PROTOCOL = /^(https?:|mailto:)/i;
|
|
16
|
-
const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
17
|
-
const DATE_TIME_WITH_OFFSET = /^\d{4}-\d{2}-\d{2}T.*[+-]\d{2}(:?\d{2})?$/;
|
|
18
|
-
function isPlainRecord(value) {
|
|
19
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20
|
-
}
|
|
21
14
|
export function createDocumentHandlebars(format) {
|
|
22
15
|
const hbs = Handlebars.create();
|
|
23
|
-
hbs.registerHelper("money", (value) =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
if (isPlainRecord(value)) {
|
|
28
|
-
const { amount, currency } = value;
|
|
29
|
-
if (typeof amount === "number" && Number.isFinite(amount) && typeof currency === "string") {
|
|
30
|
-
return formatMoney(amount, currency, format.locale);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return value === undefined || value === null ? "" : String(value);
|
|
34
|
-
});
|
|
35
|
-
hbs.registerHelper("date", (value) => {
|
|
36
|
-
if (typeof value !== "string" && typeof value !== "number") {
|
|
37
|
-
return value === undefined || value === null ? "" : String(value);
|
|
38
|
-
}
|
|
39
|
-
const locale = resolveSafeMoneyLocale(format.locale);
|
|
40
|
-
try {
|
|
41
|
-
// A date-only string has no zone; going through an instant would show the
|
|
42
|
-
// previous day west of UTC.
|
|
43
|
-
if (typeof value === "string" && DATE_ONLY.test(value)) {
|
|
44
|
-
return Temporal.PlainDate.from(value).toLocaleString(locale, { dateStyle: "long" });
|
|
45
|
-
}
|
|
46
|
-
// An explicit offset names the sender's zone: the calendar date is the wall-clock
|
|
47
|
-
// date of the string, not the date at UTC.
|
|
48
|
-
if (typeof value === "string" && DATE_TIME_WITH_OFFSET.test(value)) {
|
|
49
|
-
return Temporal.PlainDateTime.from(value)
|
|
50
|
-
.toPlainDate()
|
|
51
|
-
.toLocaleString(locale, { dateStyle: "long" });
|
|
52
|
-
}
|
|
53
|
-
const instant = typeof value === "number"
|
|
54
|
-
? Temporal.Instant.fromEpochMilliseconds(value)
|
|
55
|
-
: Temporal.Instant.from(value);
|
|
56
|
-
return instant.toLocaleString(locale, { dateStyle: "long", timeZone: "UTC" });
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
return String(value);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
hbs.registerHelper("percent", (value) => {
|
|
63
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
64
|
-
return new Intl.NumberFormat(resolveSafeMoneyLocale(format.locale), {
|
|
65
|
-
style: "percent",
|
|
66
|
-
maximumFractionDigits: 2,
|
|
67
|
-
}).format(value / 100);
|
|
68
|
-
}
|
|
69
|
-
return value === undefined || value === null ? "" : String(value);
|
|
70
|
-
});
|
|
16
|
+
hbs.registerHelper("money", (value) => formatDocumentMoney(value, format.currency, format.locale));
|
|
17
|
+
hbs.registerHelper("date", (value) => formatDocumentDate(value, format.locale));
|
|
18
|
+
hbs.registerHelper("percent", (value) => formatDocumentPercent(value, format.locale));
|
|
71
19
|
hbs.registerHelper("richText", (value, options) => {
|
|
72
20
|
if (typeof value !== "string")
|
|
73
21
|
return "";
|
|
@@ -131,7 +79,7 @@ async function readConfigText(ctx, key) {
|
|
|
131
79
|
// "Oct 1, 2026". The currency is a tenant fact and still comes from its settings.
|
|
132
80
|
export async function readDocumentFormat(ctx, templateLocale) {
|
|
133
81
|
const currency = await readConfigText(ctx, TENANT_CURRENCY_CONFIG_KEY);
|
|
134
|
-
return { locale: templateLocale, currency: currency ??
|
|
82
|
+
return { locale: templateLocale, currency: currency ?? DOCUMENT_FALLBACK_CURRENCY };
|
|
135
83
|
}
|
|
136
84
|
export async function readTenantName(ctx) {
|
|
137
85
|
return (await readConfigText(ctx, BRANDING_QN.title)) ?? "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-renderer-mail-html",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.372.0",
|
|
4
4
|
"description": "Renderer-Foundation plugin for branded HTML emails: mail-kind templates (document HTML + subject meta) rendered with isolated Handlebars into an MJML layout, plus plain text via html-to-text. Templates resolved from template-resolver.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -19,17 +19,17 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@cosmicdrift/kumiko-bundled-features": "0.
|
|
23
|
-
"@cosmicdrift/kumiko-framework": "0.
|
|
24
|
-
"@cosmicdrift/kumiko-headless": "0.
|
|
25
|
-
"@cosmicdrift/kumiko-types": "0.
|
|
22
|
+
"@cosmicdrift/kumiko-bundled-features": "0.372.0",
|
|
23
|
+
"@cosmicdrift/kumiko-framework": "0.372.0",
|
|
24
|
+
"@cosmicdrift/kumiko-headless": "0.372.0",
|
|
25
|
+
"@cosmicdrift/kumiko-types": "0.372.0",
|
|
26
26
|
"handlebars": "^4.7.8",
|
|
27
27
|
"html-to-text": "^9.0.5",
|
|
28
28
|
"mjml": "^5.2.2"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@cosmicdrift/kumiko-locale-de": "0.
|
|
32
|
-
"@cosmicdrift/kumiko-testing": "0.
|
|
31
|
+
"@cosmicdrift/kumiko-locale-de": "0.372.0",
|
|
32
|
+
"@cosmicdrift/kumiko-testing": "0.372.0",
|
|
33
33
|
"@types/html-to-text": "^9.0.4",
|
|
34
34
|
"@types/mjml": "^5.0.0"
|
|
35
35
|
},
|