@pallasoft/ps-utilities 2.3.0 → 2.4.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.
@@ -0,0 +1,26 @@
1
+ export interface EmailPreviewInput {
2
+ from: string;
3
+ html: string | null;
4
+ receivedAt: string;
5
+ subject: string | null;
6
+ text: string | null;
7
+ }
8
+ export declare class EmailPreviewService {
9
+ static readonly fileName = "email.html";
10
+ static readonly contentType = "text/html";
11
+ static readonly imageFileName = "email.png";
12
+ static readonly imageContentType = "image/png";
13
+ private static readonly page;
14
+ static escape(value: string): string;
15
+ static hasContent(input: EmailPreviewInput): boolean;
16
+ static subjectOf(input: EmailPreviewInput): string;
17
+ static toText(input: EmailPreviewInput): string;
18
+ static toDocument(input: EmailPreviewInput): string;
19
+ static toImageMarkup(input: EmailPreviewInput): string;
20
+ private static clip;
21
+ private static wrap;
22
+ private static push;
23
+ private static fromHtml;
24
+ private static decode;
25
+ private static body;
26
+ }
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EmailPreviewService = void 0;
4
+ var EmailPreviewService = /** @class */ (function () {
5
+ function EmailPreviewService() {
6
+ }
7
+ EmailPreviewService.escape = function (value) {
8
+ return value
9
+ .replace(/&/g, '&')
10
+ .replace(/</g, '&lt;')
11
+ .replace(/>/g, '&gt;')
12
+ .replace(/"/g, '&quot;')
13
+ .replace(/'/g, '&#39;');
14
+ };
15
+ EmailPreviewService.hasContent = function (input) {
16
+ return ((input.html !== null && input.html.trim() !== '') ||
17
+ (input.text !== null && input.text.trim() !== ''));
18
+ };
19
+ EmailPreviewService.subjectOf = function (input) {
20
+ return input.subject === null || input.subject.trim() === '' ? 'Email' : input.subject.trim();
21
+ };
22
+ EmailPreviewService.toText = function (input) {
23
+ if (input.text !== null && input.text.trim() !== '')
24
+ return input.text.trim();
25
+ if (input.html === null)
26
+ return '';
27
+ return EmailPreviewService.fromHtml(input.html);
28
+ };
29
+ EmailPreviewService.toDocument = function (input) {
30
+ var subject = EmailPreviewService.subjectOf(input);
31
+ var body = input.html !== null && input.html.trim() !== ''
32
+ ? EmailPreviewService.body(input.html)
33
+ : "<pre style=\"white-space:pre-wrap;font-family:inherit;margin:0;\">".concat(EmailPreviewService.escape(input.text === null ? '' : input.text), "</pre>");
34
+ return "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t<title>".concat(EmailPreviewService.escape(subject), "</title>\n\t</head>\n\t<body style=\"margin:0;padding:24px;background:#f5f7fa;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;color:#101828;\">\n\t\t<div style=\"max-width:760px;margin:0 auto;background:#ffffff;border:1px solid #e4e8ee;border-radius:12px;overflow:hidden;\">\n\t\t\t<div style=\"padding:16px 20px;border-bottom:1px solid #e4e8ee;background:#f9fafb;\">\n\t\t\t\t<div style=\"font-size:15px;font-weight:700;\">").concat(EmailPreviewService.escape(subject), "</div>\n\t\t\t\t<div style=\"margin-top:4px;font-size:12px;color:#667085;\">From ").concat(EmailPreviewService.escape(input.from), " \u00B7 ").concat(EmailPreviewService.escape(input.receivedAt), "</div>\n\t\t\t</div>\n\t\t\t<div style=\"padding:20px;\">").concat(body, "</div>\n\t\t</div>\n\t</body>\n</html>");
35
+ };
36
+ EmailPreviewService.toImageMarkup = function (input) {
37
+ var page = EmailPreviewService.page;
38
+ var subject = EmailPreviewService.subjectOf(input);
39
+ var meta = "From ".concat(input.from, " \u00B7 ").concat(input.receivedAt);
40
+ var width = page.width - page.padding * 2;
41
+ var lines = EmailPreviewService.wrap(EmailPreviewService.toText(input), Math.floor(width / page.charWidth)).slice(0, page.maxLines);
42
+ var bodyTop = page.padding + page.subjectSize + 10 + page.metaSize + page.headerGap;
43
+ var height = Math.max(page.minHeight, bodyTop + lines.length * page.lineHeight + page.padding);
44
+ var body = lines
45
+ .map(function (line, index) {
46
+ return "<text x=\"".concat(page.padding, "\" y=\"").concat(bodyTop + index * page.lineHeight, "\" font-size=\"").concat(page.bodySize, "\" fill=\"#344054\">").concat(EmailPreviewService.escape(line), "</text>");
47
+ })
48
+ .join('');
49
+ return "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"".concat(page.width, "\" height=\"").concat(height, "\" viewBox=\"0 0 ").concat(page.width, " ").concat(height, "\" font-family=\"").concat(page.fontFamily, "\">\n\t<rect width=\"").concat(page.width, "\" height=\"").concat(height, "\" fill=\"#ffffff\" />\n\t<rect x=\"0\" y=\"0\" width=\"").concat(page.width, "\" height=\"6\" fill=\"#0f9d58\" />\n\t<text x=\"").concat(page.padding, "\" y=\"").concat(page.padding + page.subjectSize, "\" font-size=\"").concat(page.subjectSize, "\" font-weight=\"700\" fill=\"#101828\">").concat(EmailPreviewService.escape(EmailPreviewService.clip(subject, Math.floor(width / (page.charWidth * 1.45)))), "</text>\n\t<text x=\"").concat(page.padding, "\" y=\"").concat(page.padding + page.subjectSize + 10 + page.metaSize, "\" font-size=\"").concat(page.metaSize, "\" fill=\"#667085\">").concat(EmailPreviewService.escape(EmailPreviewService.clip(meta, Math.floor(width / (page.charWidth * 0.9)))), "</text>\n\t<line x1=\"").concat(page.padding, "\" y1=\"").concat(bodyTop - page.headerGap + 8, "\" x2=\"").concat(page.width - page.padding, "\" y2=\"").concat(bodyTop - page.headerGap + 8, "\" stroke=\"#e4e8ee\" stroke-width=\"1\" />\n\t").concat(body, "\n</svg>");
50
+ };
51
+ EmailPreviewService.clip = function (value, limit) {
52
+ return value.length <= limit ? value : "".concat(value.slice(0, Math.max(limit - 1, 1)), "\u2026");
53
+ };
54
+ EmailPreviewService.wrap = function (text, limit) {
55
+ var lines = [];
56
+ for (var _i = 0, _a = text.split('\n'); _i < _a.length; _i++) {
57
+ var paragraph = _a[_i];
58
+ if (paragraph.trim() === '') {
59
+ lines.push('');
60
+ continue;
61
+ }
62
+ var current = '';
63
+ for (var _b = 0, _c = paragraph.trim().split(/\s+/); _b < _c.length; _b++) {
64
+ var word = _c[_b];
65
+ var candidate = current === '' ? word : "".concat(current, " ").concat(word);
66
+ if (candidate.length <= limit) {
67
+ current = candidate;
68
+ continue;
69
+ }
70
+ if (current !== '')
71
+ lines.push(current);
72
+ current = word.length <= limit ? word : EmailPreviewService.push(lines, word, limit);
73
+ }
74
+ lines.push(current);
75
+ }
76
+ return lines;
77
+ };
78
+ EmailPreviewService.push = function (lines, word, limit) {
79
+ var rest = word;
80
+ while (rest.length > limit) {
81
+ lines.push(rest.slice(0, limit));
82
+ rest = rest.slice(limit);
83
+ }
84
+ return rest;
85
+ };
86
+ EmailPreviewService.fromHtml = function (html) {
87
+ return EmailPreviewService.decode(html
88
+ .replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, '')
89
+ .replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, '')
90
+ .replace(/<head\b[^>]*>[\s\S]*?<\/head>/gi, '')
91
+ .replace(/<br\s*\/?>/gi, '\n')
92
+ .replace(/<\/(p|div|tr|li|h[1-6]|table|section|header|footer)>/gi, '\n')
93
+ .replace(/<[^>]+>/g, ' '))
94
+ .replace(/[^\S\n]+/g, ' ')
95
+ .replace(/ *\n */g, '\n')
96
+ .replace(/\n{3,}/g, '\n\n')
97
+ .trim();
98
+ };
99
+ EmailPreviewService.decode = function (value) {
100
+ return value
101
+ .replace(/&nbsp;/gi, ' ')
102
+ .replace(/&amp;/gi, '&')
103
+ .replace(/&lt;/gi, '<')
104
+ .replace(/&gt;/gi, '>')
105
+ .replace(/&quot;/gi, '"')
106
+ .replace(/&#0?39;|&#x27;|&apos;/gi, "'")
107
+ .replace(/&#8217;|&rsquo;/gi, '’')
108
+ .replace(/&#8212;|&mdash;/gi, '—')
109
+ .replace(/&#8211;|&ndash;/gi, '–');
110
+ };
111
+ EmailPreviewService.body = function (html) {
112
+ var withoutScripts = html
113
+ .replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, '')
114
+ .replace(/<iframe\b[^>]*>[\s\S]*?<\/iframe>/gi, '')
115
+ .replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, '')
116
+ .replace(/\son[a-z]+\s*=\s*'[^']*'/gi, '');
117
+ var inner = withoutScripts.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
118
+ return inner === null ? withoutScripts : inner[1];
119
+ };
120
+ EmailPreviewService.fileName = 'email.html';
121
+ EmailPreviewService.contentType = 'text/html';
122
+ EmailPreviewService.imageFileName = 'email.png';
123
+ EmailPreviewService.imageContentType = 'image/png';
124
+ EmailPreviewService.page = {
125
+ width: 860,
126
+ padding: 48,
127
+ headerGap: 28,
128
+ subjectSize: 22,
129
+ metaSize: 13,
130
+ bodySize: 15,
131
+ lineHeight: 24,
132
+ maxLines: 140,
133
+ minHeight: 520,
134
+ charWidth: 7.4,
135
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, 'DejaVu Sans', 'Liberation Sans', sans-serif",
136
+ };
137
+ return EmailPreviewService;
138
+ }());
139
+ exports.EmailPreviewService = EmailPreviewService;
@@ -1,5 +1,6 @@
1
1
  export * from './color-service';
2
2
  export * from './crypto-service';
3
+ export * from './email-preview-service';
3
4
  export * from './encrypted-fields-service';
4
5
  export * from './envelope-crypto-service';
5
6
  export * from './date-service';
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./color-service"), exports);
18
18
  __exportStar(require("./crypto-service"), exports);
19
+ __exportStar(require("./email-preview-service"), exports);
19
20
  __exportStar(require("./encrypted-fields-service"), exports);
20
21
  __exportStar(require("./envelope-crypto-service"), exports);
21
22
  __exportStar(require("./date-service"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pallasoft/ps-utilities",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -41,27 +41,27 @@
41
41
  "@commitlint/config-conventional": "^20.0.0",
42
42
  "@eslint/compat": "^2.0.0",
43
43
  "@ianvs/prettier-plugin-sort-imports": "^4.7.0",
44
+ "@semantic-release/changelog": "^6.0.3",
45
+ "@semantic-release/commit-analyzer": "^13.0.0",
46
+ "@semantic-release/git": "^10.0.1",
47
+ "@semantic-release/github": "^11.0.1",
48
+ "@semantic-release/npm": "^12.0.0",
49
+ "@semantic-release/release-notes-generator": "^14.0.1",
44
50
  "@typescript-eslint/eslint-plugin": "^8.48.0",
45
51
  "@typescript-eslint/parser": "^8.48.0",
46
52
  "commitlint": "^20.1.0",
53
+ "conventional-changelog-conventionalcommits": "^7.0.2",
47
54
  "eslint": "^9.39.1",
48
55
  "eslint-config-prettier": "^10.1.8",
49
56
  "eslint-plugin-import": "^2.32.0",
50
57
  "eslint-plugin-prettier": "^5.5.4",
58
+ "hono": "^4.12.18",
51
59
  "husky": "^9.1.7",
52
60
  "lint-staged": "^16.2.7",
53
61
  "prettier": "^3.6.2",
54
- "hono": "^4.12.18",
62
+ "semantic-release": "^24.2.0",
55
63
  "ts-node": "^10.9.2",
56
- "typescript": "^5.9.3",
57
- "@semantic-release/changelog": "^6.0.3",
58
- "@semantic-release/commit-analyzer": "^13.0.0",
59
- "@semantic-release/git": "^10.0.1",
60
- "@semantic-release/github": "^11.0.1",
61
- "@semantic-release/npm": "^12.0.0",
62
- "@semantic-release/release-notes-generator": "^14.0.1",
63
- "conventional-changelog-conventionalcommits": "^7.0.2",
64
- "semantic-release": "^24.2.0"
64
+ "typescript": "^5.9.3"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "hono": "^4.0.0"