custom-email-otp-template 0.0.0-stage → 1.0.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.
Files changed (62) hide show
  1. package/README.md +154 -3
  2. package/dist/index.d.ts +5 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +25 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/src/index.d.ts +5 -0
  7. package/dist/src/index.d.ts.map +1 -0
  8. package/dist/src/index.js +12 -0
  9. package/dist/src/index.js.map +1 -0
  10. package/dist/src/templates/default.d.ts +3 -0
  11. package/dist/src/templates/default.d.ts.map +1 -0
  12. package/dist/src/templates/default.js +117 -0
  13. package/dist/src/templates/default.js.map +1 -0
  14. package/dist/src/types/email.types.d.ts +11 -0
  15. package/dist/src/types/email.types.d.ts.map +1 -0
  16. package/dist/src/types/email.types.js +3 -0
  17. package/dist/src/types/email.types.js.map +1 -0
  18. package/dist/src/utils/escapeHtml.d.ts +2 -0
  19. package/dist/src/utils/escapeHtml.d.ts.map +1 -0
  20. package/dist/src/utils/escapeHtml.js +16 -0
  21. package/dist/src/utils/escapeHtml.js.map +1 -0
  22. package/dist/src/utils/validate.d.ts +3 -0
  23. package/dist/src/utils/validate.d.ts.map +1 -0
  24. package/dist/src/utils/validate.js +25 -0
  25. package/dist/src/utils/validate.js.map +1 -0
  26. package/dist/templates/dark.d.ts +3 -0
  27. package/dist/templates/dark.d.ts.map +1 -0
  28. package/dist/templates/dark.js +126 -0
  29. package/dist/templates/dark.js.map +1 -0
  30. package/dist/templates/default.d.ts +3 -0
  31. package/dist/templates/default.d.ts.map +1 -0
  32. package/dist/templates/default.js +117 -0
  33. package/dist/templates/default.js.map +1 -0
  34. package/dist/templates/modern.d.ts +3 -0
  35. package/dist/templates/modern.d.ts.map +1 -0
  36. package/dist/templates/modern.js +127 -0
  37. package/dist/templates/modern.js.map +1 -0
  38. package/dist/tests/template.test.d.ts +2 -0
  39. package/dist/tests/template.test.d.ts.map +1 -0
  40. package/dist/tests/template.test.js +57 -0
  41. package/dist/tests/template.test.js.map +1 -0
  42. package/dist/types/email.types.d.ts +11 -0
  43. package/dist/types/email.types.d.ts.map +1 -0
  44. package/dist/types/email.types.js +3 -0
  45. package/dist/types/email.types.js.map +1 -0
  46. package/dist/utils/escapeHtml.d.ts +2 -0
  47. package/dist/utils/escapeHtml.d.ts.map +1 -0
  48. package/dist/utils/escapeHtml.js +16 -0
  49. package/dist/utils/escapeHtml.js.map +1 -0
  50. package/dist/utils/validate.d.ts +3 -0
  51. package/dist/utils/validate.d.ts.map +1 -0
  52. package/dist/utils/validate.js +29 -0
  53. package/dist/utils/validate.js.map +1 -0
  54. package/dist/utils/validateEmail.d.ts +2 -0
  55. package/dist/utils/validateEmail.d.ts.map +1 -0
  56. package/dist/utils/validateEmail.js +12 -0
  57. package/dist/utils/validateEmail.js.map +1 -0
  58. package/dist/utils/validateUrl.d.ts +2 -0
  59. package/dist/utils/validateUrl.d.ts.map +1 -0
  60. package/dist/utils/validateUrl.js +17 -0
  61. package/dist/utils/validateUrl.js.map +1 -0
  62. package/package.json +36 -4
package/README.md CHANGED
@@ -1,3 +1,154 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # Custom Email OTP Template
2
+
3
+ A lightweight and customizable HTML email OTP template generator built with TypeScript.
4
+
5
+ Generate beautiful, responsive OTP verification emails using ready-made templates and custom branding.
6
+
7
+ ## Features
8
+
9
+ - Multiple email templates: Default, Modern, and Dark.
10
+ - Custom company name and branding.
11
+ - Custom OTP expiration time.
12
+ - Custom brand colors.
13
+ - Custom logo support.
14
+ - Support email configuration.
15
+ - TypeScript support.
16
+ - HTML escaping for dynamic text.
17
+ - Input validation.
18
+ - No email service dependency.
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install custom-email-otp-template
24
+ ```
25
+
26
+ ## Basic Usage
27
+
28
+ ```typescript
29
+ import { generateOtpEmail } from "custom-email-otp-template";
30
+
31
+ const email = generateOtpEmail({
32
+ companyName: "My Company",
33
+ otp: "583921"
34
+ });
35
+
36
+ console.log(email.html);
37
+ ```
38
+
39
+ ## Available Templates
40
+
41
+ ### Default Template
42
+
43
+ ```typescript
44
+ const email = generateOtpEmail({
45
+ companyName: "My Company",
46
+ otp: "583921",
47
+ template: "default"
48
+ });
49
+ ```
50
+
51
+ ### Modern Template
52
+
53
+ ```typescript
54
+ const email = generateOtpEmail({
55
+ companyName: "My Company",
56
+ otp: "583921",
57
+ template: "modern",
58
+ brandColor: "#2563eb"
59
+ });
60
+ ```
61
+
62
+ ### Dark Template
63
+
64
+ ```typescript
65
+ const email = generateOtpEmail({
66
+ companyName: "My Company",
67
+ otp: "583921",
68
+ template: "dark",
69
+ brandColor: "#60a5fa"
70
+ });
71
+ ```
72
+
73
+ ## Custom Branding
74
+
75
+ ```typescript
76
+ const email = generateOtpEmail({
77
+ companyName: "Tech Solutions",
78
+ otp: "583921",
79
+ expiresIn: "5 minutes",
80
+ brandColor: "#2563eb",
81
+ template: "modern",
82
+ logoUrl: "https://example.com/logo.png",
83
+ supportEmail: "support@example.com"
84
+ });
85
+ ```
86
+
87
+ ## Configuration Options
88
+
89
+ | Property | Type | Required | Default |
90
+ |---|---|---|---|
91
+ | companyName | string | Yes | — |
92
+ | otp | string | Yes | — |
93
+ | expiresIn | string | No | 5 minutes |
94
+ | brandColor | string | No | #2563eb |
95
+ | logoUrl | string | No | — |
96
+ | template | string | No | default |
97
+ | supportEmail | string | No | — |
98
+
99
+ ## Using with Nodemailer
100
+
101
+ This package generates HTML only. You can send the generated HTML using Nodemailer.
102
+
103
+ ```bash
104
+ npm install nodemailer
105
+ ```
106
+
107
+ ```typescript
108
+ import nodemailer from "nodemailer";
109
+ import { generateOtpEmail } from "custom-email-otp-template";
110
+
111
+ const email = generateOtpEmail({
112
+ companyName: "My Company",
113
+ otp: "583921",
114
+ template: "modern"
115
+ });
116
+
117
+ const transporter = nodemailer.createTransport({
118
+ host: process.env.SMTP_HOST,
119
+ port: Number(process.env.SMTP_PORT),
120
+ secure: true,
121
+ auth: {
122
+ user: process.env.SMTP_USER,
123
+ pass: process.env.SMTP_PASSWORD
124
+ }
125
+ });
126
+
127
+ await transporter.sendMail({
128
+ from: process.env.SMTP_USER,
129
+ to: "user@example.com",
130
+ subject: "Email Verification",
131
+ html: email.html
132
+ });
133
+ ```
134
+
135
+ ## Important Notes
136
+
137
+ - This package generates email HTML; it does not generate OTPs.
138
+ - This package does not send emails.
139
+ - Generate and securely verify OTPs in your own application.
140
+ - Never log or expose OTP values in production.
141
+ - OTP expiration and verification must be enforced by your application.
142
+ - Use a trusted HTTPS image URL for your logo.
143
+
144
+ ## Development
145
+
146
+ ```bash
147
+ npm install
148
+ npm run build
149
+ npm test
150
+ ```
151
+
152
+ ## License
153
+
154
+ MIT License
@@ -0,0 +1,5 @@
1
+ import type { OtpEmailOptions } from "./types/email.types.js";
2
+ export declare function generateOtpEmail(options: OtpEmailOptions): {
3
+ html: string;
4
+ };
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAQ9D,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe;;EAqBxD"}
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateOtpEmail = generateOtpEmail;
4
+ const default_js_1 = require("./templates/default.js");
5
+ const modern_js_1 = require("./templates/modern.js");
6
+ const dark_js_1 = require("./templates/dark.js");
7
+ const validate_js_1 = require("./utils/validate.js");
8
+ function generateOtpEmail(options) {
9
+ (0, validate_js_1.validateOtpEmail)(options);
10
+ let html;
11
+ switch (options.template ?? "default") {
12
+ case "modern":
13
+ html = (0, modern_js_1.modernTemplate)(options);
14
+ break;
15
+ case "dark":
16
+ html = (0, dark_js_1.darkTemplate)(options);
17
+ break;
18
+ case "default":
19
+ default:
20
+ html = (0, default_js_1.defaultTemplate)(options);
21
+ break;
22
+ }
23
+ return { html };
24
+ }
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAGA,uDAAyD;AACzD,qDAAuD;AACvD,iDAAmD;AAEnD,qDAAuD;AAEvD,0BAAiC,OAAwB;IACrD,IAAA,8BAAgB,EAAC,OAAO,CAAC,CAAC;IAE1B,IAAI,IAAY,CAAC;IAEjB,QAAQ,OAAO,CAAC,QAAQ,IAAI,SAAS,EAAE,CAAC;QACpC,KAAK,QAAQ;YACT,IAAI,GAAG,IAAA,0BAAc,EAAC,OAAO,CAAC,CAAC;YAC/B,MAAM;QAEV,KAAK,MAAM;YACP,IAAI,GAAG,IAAA,sBAAY,EAAC,OAAO,CAAC,CAAC;YAC7B,MAAM;QAEV,KAAK,SAAS,CAAC;QACf;YACI,IAAI,GAAG,IAAA,4BAAe,EAAC,OAAO,CAAC,CAAC;YAChC,MAAM;IACd,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,CAAC;AACpB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { OtpEmailOption } from "./types/email.types.js";
2
+ export declare function generateOtpEmail(options: OtpEmailOption): {
3
+ html: string;
4
+ };
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAIxD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc;IAGpD,IAAI;EAEP"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateOtpEmail = generateOtpEmail;
4
+ const default_js_1 = require("./templates/default.js");
5
+ const validate_js_1 = require("./utils/validate.js");
6
+ function generateOtpEmail(options) {
7
+ (0, validate_js_1.validateOtpEmail)(options);
8
+ return {
9
+ html: (0, default_js_1.defaultTemplate)(options),
10
+ };
11
+ }
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;AACA,uDAAyD;AACzD,qDAAuD;AAEvD,0BAAiC,OAAuB;IACtD,IAAA,8BAAgB,EAAC,OAAO,CAAC,CAAC;IAC1B,OAAO;QACL,IAAI,EAAE,IAAA,4BAAe,EAAC,OAAO,CAAC;KAC/B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { OtpEmailOption } from "../types/email.types.js";
2
+ export declare function defaultTemplate(options: OtpEmailOption): string;
3
+ //# sourceMappingURL=default.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../../src/templates/default.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGzD,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CA6H/D"}
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultTemplate = defaultTemplate;
4
+ const escapeHtml_js_1 = require("../utils/escapeHtml.js");
5
+ function defaultTemplate(options) {
6
+ const { companyName, otp, expiresIn = "5 minutes", brandColor = "#2563eb", logoUrl, supportEmail, } = options;
7
+ const safeCompanyName = (0, escapeHtml_js_1.escapeHtml)(companyName);
8
+ const safeOtp = (0, escapeHtml_js_1.escapeHtml)(otp);
9
+ const safeExpiresIn = (0, escapeHtml_js_1.escapeHtml)(expiresIn);
10
+ const safeSupportEmail = supportEmail
11
+ ? (0, escapeHtml_js_1.escapeHtml)(supportEmail)
12
+ : "";
13
+ const safeLogoUrl = logoUrl
14
+ ? (0, escapeHtml_js_1.escapeHtml)(logoUrl)
15
+ : "";
16
+ return `
17
+ <!DOCTYPE html>
18
+ <html>
19
+ <head>
20
+ <meta charset="UTF-8">
21
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
22
+ <title>Email Verification</title>
23
+ </head>
24
+
25
+ <body style="
26
+ margin: 0;
27
+ padding: 30px 10px;
28
+ background-color: #f3f4f6;
29
+ font-family: Arial, sans-serif;
30
+ ">
31
+
32
+ <div style="
33
+ max-width: 500px;
34
+ margin: auto;
35
+ background: #ffffff;
36
+ padding: 35px 25px;
37
+ border-radius: 12px;
38
+ text-align: center;
39
+ ">
40
+
41
+ ${safeLogoUrl
42
+ ? `
43
+ <img
44
+ src="${safeLogoUrl}"
45
+ alt="${safeCompanyName}"
46
+ width="120"
47
+ style="margin-bottom: 20px;"
48
+ />
49
+ `
50
+ : ""}
51
+
52
+ <h2 style="color: #111827;">
53
+ Verify Your Email
54
+ </h2>
55
+
56
+ <p style="color: #6b7280; line-height: 1.6;">
57
+ Hello,<br>
58
+ Use the verification code below to complete
59
+ your email verification.
60
+ </p>
61
+
62
+ <div style="
63
+ margin: 30px 0;
64
+ padding: 20px;
65
+ background: #f9fafb;
66
+ border-radius: 8px;
67
+ color: ${brandColor};
68
+ font-size: 32px;
69
+ font-weight: bold;
70
+ letter-spacing: 8px;
71
+ ">
72
+ ${safeOtp}
73
+ </div>
74
+
75
+ <p style="color: #6b7280;">
76
+ This code expires in
77
+ <strong>${safeExpiresIn}</strong>.
78
+ </p>
79
+
80
+ <p style="
81
+ color: #9ca3af;
82
+ font-size: 13px;
83
+ line-height: 1.6;
84
+ ">
85
+ If you didn't request this verification code,
86
+ please ignore this email.
87
+ </p>
88
+
89
+ ${safeSupportEmail
90
+ ? `
91
+ <p style="font-size: 13px; color: #6b7280;">
92
+ Support:
93
+ <a href="mailto:${safeSupportEmail}">
94
+ ${safeSupportEmail}
95
+ </a>
96
+ </p>
97
+ `
98
+ : ""}
99
+
100
+ <hr style="
101
+ border: none;
102
+ border-top: 1px solid #e5e7eb;
103
+ margin: 25px 0;
104
+ " />
105
+
106
+ <p style="font-size: 12px; color: #9ca3af;">
107
+ &copy; ${new Date().getFullYear()} ${safeCompanyName}.
108
+ All rights reserved.
109
+ </p>
110
+
111
+ </div>
112
+
113
+ </body>
114
+ </html>
115
+ `;
116
+ }
117
+ //# sourceMappingURL=default.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default.js","sourceRoot":"","sources":["../../../src/templates/default.ts"],"names":[],"mappings":";;;AACA,0DAAoD;AAEpD,yBAAgC,OAAuB;IACrD,MAAM,EACJ,WAAW,EACX,GAAG,EACH,SAAS,GAAG,WAAW,EACvB,UAAU,GAAG,SAAS,EACtB,OAAO,EACP,YAAY,GACb,GAAG,OAAO,CAAC;IAEZ,MAAM,eAAe,GAAG,IAAA,0BAAU,EAAC,WAAW,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAA,0BAAU,EAAC,GAAG,CAAC,CAAC;IAChC,MAAM,aAAa,GAAG,IAAA,0BAAU,EAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,gBAAgB,GAAG,YAAY;QACnC,CAAC,CAAC,IAAA,0BAAU,EAAC,YAAY,CAAC;QAC1B,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,WAAW,GAAG,OAAO;QACzB,CAAC,CAAC,IAAA,0BAAU,EAAC,OAAO,CAAC;QACrB,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;QA0BD,WAAW;QACT,CAAC,CAAC;;mBAEO,WAAW;mBACX,eAAe;;;;WAIvB;QACD,CAAC,CAAC,EACN;;;;;;;;;;;;;;;;;iBAiBW,UAAU;;;;;UAKjB,OAAO;;;;;kBAKC,aAAa;;;;;;;;;;;;QAavB,gBAAgB;QACd,CAAC,CAAC;;;8BAGkB,gBAAgB;gBAC9B,gBAAgB;;;WAGrB;QACD,CAAC,CAAC,EACN;;;;;;;;;iBASW,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,eAAe;;;;;;;;GAQzD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ export type EmailTemplate = "default" | "modern" | "dark";
2
+ export interface OtpEmailOption {
3
+ companyName: string;
4
+ otp: string;
5
+ expiresIn?: string;
6
+ brandColor?: string;
7
+ logoUrl?: string;
8
+ template?: EmailTemplate;
9
+ supportEmail?: string;
10
+ }
11
+ //# sourceMappingURL=email.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.types.d.ts","sourceRoot":"","sources":["../../../src/types/email.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAI,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAE;AAE5D,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAG,MAAM,CAAC;IACb,SAAS,CAAC,EAAC,MAAM,CAAC;IAClB,UAAU,CAAC,EAAC,MAAM,CAAC;IACnB,OAAO,CAAC,EAAC,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAC,aAAa,CAAC;IACxB,YAAY,CAAC,EAAC,MAAM,CAAA;CACrB"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=email.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.types.js","sourceRoot":"","sources":["../../../src/types/email.types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export declare function escapeHtml(value: string): string;
2
+ //# sourceMappingURL=escapeHtml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escapeHtml.d.ts","sourceRoot":"","sources":["../../../src/utils/escapeHtml.ts"],"names":[],"mappings":"AACA,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAYhD"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.escapeHtml = escapeHtml;
4
+ function escapeHtml(value) {
5
+ return value.replace(/[&<>"']/g, (character) => {
6
+ const entities = {
7
+ "&": "&amp;",
8
+ "<": "&lt;",
9
+ ">": "&gt;",
10
+ '"': "&quot;",
11
+ "'": "&#39;"
12
+ };
13
+ return entities[character];
14
+ });
15
+ }
16
+ //# sourceMappingURL=escapeHtml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escapeHtml.js","sourceRoot":"","sources":["../../../src/utils/escapeHtml.ts"],"names":[],"mappings":";;;AACA,oBAA2B,KAAa;IACpC,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,SAAS,EAAE,EAAE;QAC3C,MAAM,QAAQ,GAA2B;YACrC,GAAG,EAAE,OAAO;YACZ,GAAG,EAAE,MAAM;YACX,GAAG,EAAE,MAAM;YACX,GAAG,EAAE,QAAQ;YACb,GAAG,EAAE,OAAO;SACf,CAAC;QAEF,OAAO,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { OtpEmailOption } from "../types/email.types.js";
2
+ export declare function validateOtpEmail(options: OtpEmailOption): void;
3
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/utils/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAsB9D"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateOtpEmail = validateOtpEmail;
4
+ function validateOtpEmail(options) {
5
+ if (!options) {
6
+ throw new Error("OTP email options are required.");
7
+ }
8
+ if (!options.companyName.trim()) {
9
+ throw new Error("Company name is required.");
10
+ }
11
+ if (!options.otp.trim()) {
12
+ throw new Error("OTP is required.");
13
+ }
14
+ if (!/^[a-zA-Z0-9]{4,8}$/.test(options.otp)) {
15
+ throw new Error("OTP must contain 4 to 8 alphanumeric characters.");
16
+ }
17
+ if (options.brandColor && !/^#[0-9A-Fa-f]{6}$/.test(options.brandColor)) {
18
+ throw new Error("Brand color must be a valid HEX color.");
19
+ }
20
+ if (options.template &&
21
+ !["default", "modern", "dark"].includes(options.template)) {
22
+ throw new Error("Invalid email template.");
23
+ }
24
+ }
25
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/utils/validate.ts"],"names":[],"mappings":";;;AAEA,0BAAiC,OAAuB;IACtD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,OAAO,CAAC,UAAU,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IACD,IACE,OAAO,CAAC,QAAQ;QAChB,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,EACzD,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { OtpEmailOptions } from "../types/email.types.js";
2
+ export declare function darkTemplate(options: OtpEmailOptions): string;
3
+ //# sourceMappingURL=dark.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dark.d.ts","sourceRoot":"","sources":["../../src/templates/dark.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG/D,wBAAgB,YAAY,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAqI7D"}
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.darkTemplate = darkTemplate;
4
+ const escapeHtml_js_1 = require("../utils/escapeHtml.js");
5
+ function darkTemplate(options) {
6
+ const { companyName, otp, expiresIn = "5 minutes", brandColor = "#60a5fa", logoUrl, supportEmail } = options;
7
+ const safeCompanyName = (0, escapeHtml_js_1.escapeHtml)(companyName);
8
+ const safeOtp = (0, escapeHtml_js_1.escapeHtml)(otp);
9
+ const safeExpiresIn = (0, escapeHtml_js_1.escapeHtml)(expiresIn);
10
+ const safeLogoUrl = logoUrl ? (0, escapeHtml_js_1.escapeHtml)(logoUrl) : "";
11
+ const safeSupportEmail = supportEmail
12
+ ? (0, escapeHtml_js_1.escapeHtml)(supportEmail)
13
+ : "";
14
+ return `
15
+ <!DOCTYPE html>
16
+ <html>
17
+ <head>
18
+ <meta charset="UTF-8">
19
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
20
+ <title>Email Verification</title>
21
+ </head>
22
+
23
+ <body style="
24
+ margin: 0;
25
+ padding: 30px 12px;
26
+ background-color: #111827;
27
+ font-family: Arial, sans-serif;
28
+ ">
29
+
30
+ <div style="
31
+ max-width: 520px;
32
+ margin: auto;
33
+ background: #1f2937;
34
+ border-radius: 14px;
35
+ overflow: hidden;
36
+ ">
37
+
38
+ <div style="
39
+ padding: 35px 20px;
40
+ text-align: center;
41
+ ">
42
+ ${logoUrl
43
+ ? `<img src="${safeLogoUrl}" alt="${safeCompanyName}" width="110" style="margin-bottom: 15px;">`
44
+ : ""}
45
+
46
+ <h1 style="
47
+ color: #ffffff;
48
+ font-size: 24px;
49
+ ">
50
+ Verify Your Email
51
+ </h1>
52
+
53
+ <p style="
54
+ color: #9ca3af;
55
+ line-height: 1.6;
56
+ ">
57
+ Welcome to ${safeCompanyName}.
58
+ Enter the code below to verify your email.
59
+ </p>
60
+ </div>
61
+
62
+ <div style="
63
+ margin: 0 25px 25px;
64
+ padding: 25px 15px;
65
+ background: #374151;
66
+ border-radius: 12px;
67
+ text-align: center;
68
+ ">
69
+ <p style="
70
+ color: #9ca3af;
71
+ font-size: 13px;
72
+ ">
73
+ VERIFICATION CODE
74
+ </p>
75
+
76
+ <div style="
77
+ color: ${brandColor};
78
+ font-size: 32px;
79
+ font-weight: bold;
80
+ letter-spacing: 8px;
81
+ ">
82
+ ${safeOtp}
83
+ </div>
84
+ </div>
85
+
86
+ <div style="
87
+ padding: 0 25px 30px;
88
+ text-align: center;
89
+ ">
90
+ <p style="color: #d1d5db; font-size: 14px;">
91
+ This code expires in
92
+ <strong>${safeExpiresIn}</strong>.
93
+ </p>
94
+
95
+ <p style="
96
+ color: #9ca3af;
97
+ font-size: 13px;
98
+ line-height: 1.6;
99
+ ">
100
+ If you didn't request this code,
101
+ please ignore this email.
102
+ </p>
103
+
104
+ ${supportEmail
105
+ ? `<p style="font-size: 13px;"><a style="color: ${brandColor};" href="mailto:${safeSupportEmail}">${safeSupportEmail}</a></p>`
106
+ : ""}
107
+ </div>
108
+
109
+ <div style="
110
+ padding: 18px;
111
+ background: #111827;
112
+ text-align: center;
113
+ color: #6b7280;
114
+ font-size: 12px;
115
+ ">
116
+ © ${new Date().getFullYear()} ${safeCompanyName}.
117
+ All rights reserved.
118
+ </div>
119
+
120
+ </div>
121
+
122
+ </body>
123
+ </html>
124
+ `;
125
+ }
126
+ //# sourceMappingURL=dark.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dark.js","sourceRoot":"","sources":["../../src/templates/dark.ts"],"names":[],"mappings":";;;AAEA,0DAAoD;AAEpD,sBAA6B,OAAwB;IACjD,MAAM,EACF,WAAW,EACX,GAAG,EACH,SAAS,GAAG,WAAW,EACvB,UAAU,GAAG,SAAS,EACtB,OAAO,EACP,YAAY,EACf,GAAG,OAAO,CAAC;IAEZ,MAAM,eAAe,GAAG,IAAA,0BAAU,EAAC,WAAW,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAA,0BAAU,EAAC,GAAG,CAAC,CAAC;IAChC,MAAM,aAAa,GAAG,IAAA,0BAAU,EAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,IAAA,0BAAU,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,MAAM,gBAAgB,GAAG,YAAY;QACjC,CAAC,CAAC,IAAA,0BAAU,EAAC,YAAY,CAAC;QAC1B,CAAC,CAAC,EAAE,CAAC;IAET,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA6BS,OAAO;QACH,CAAC,CAAC,aAAa,WAAW,UAAU,eAAe,6CAA6C;QAChG,CAAC,CAAC,EACV;;;;;;;;;;;;;iCAaiB,eAAe;;;;;;;;;;;;;;;;;;;;6BAoBnB,UAAU;;;;;sBAKjB,OAAO;;;;;;;;;;8BAUC,aAAa;;;;;;;;;;;;kBAavB,YAAY;QACR,CAAC,CAAC,gDAAgD,UAAU,mBAAmB,gBAAgB,KAAK,gBAAgB,UAAU;QAC9H,CAAC,CAAC,EACV;;;;;;;;;;oBAUI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,eAAe;;;;;;;;KAQ1D,CAAC;AACN,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { OtpEmailOptions } from "../types/email.types.js";
2
+ export declare function defaultTemplate(options: OtpEmailOptions): string;
3
+ //# sourceMappingURL=default.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../src/templates/default.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG1D,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CA6HhE"}
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultTemplate = defaultTemplate;
4
+ const escapeHtml_js_1 = require("../utils/escapeHtml.js");
5
+ function defaultTemplate(options) {
6
+ const { companyName, otp, expiresIn = "5 minutes", brandColor = "#2563eb", logoUrl, supportEmail, } = options;
7
+ const safeCompanyName = (0, escapeHtml_js_1.escapeHtml)(companyName);
8
+ const safeOtp = (0, escapeHtml_js_1.escapeHtml)(otp);
9
+ const safeExpiresIn = (0, escapeHtml_js_1.escapeHtml)(expiresIn);
10
+ const safeSupportEmail = supportEmail
11
+ ? (0, escapeHtml_js_1.escapeHtml)(supportEmail)
12
+ : "";
13
+ const safeLogoUrl = logoUrl
14
+ ? (0, escapeHtml_js_1.escapeHtml)(logoUrl)
15
+ : "";
16
+ return `
17
+ <!DOCTYPE html>
18
+ <html>
19
+ <head>
20
+ <meta charset="UTF-8">
21
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
22
+ <title>Email Verification</title>
23
+ </head>
24
+
25
+ <body style="
26
+ margin: 0;
27
+ padding: 30px 10px;
28
+ background-color: #f3f4f6;
29
+ font-family: Arial, sans-serif;
30
+ ">
31
+
32
+ <div style="
33
+ max-width: 500px;
34
+ margin: auto;
35
+ background: #ffffff;
36
+ padding: 35px 25px;
37
+ border-radius: 12px;
38
+ text-align: center;
39
+ ">
40
+
41
+ ${safeLogoUrl
42
+ ? `
43
+ <img
44
+ src="${safeLogoUrl}"
45
+ alt="${safeCompanyName}"
46
+ width="120"
47
+ style="margin-bottom: 20px;"
48
+ />
49
+ `
50
+ : ""}
51
+
52
+ <h2 style="color: #111827;">
53
+ Verify Your Email
54
+ </h2>
55
+
56
+ <p style="color: #6b7280; line-height: 1.6;">
57
+ Hello,<br>
58
+ Use the verification code below to complete
59
+ your email verification.
60
+ </p>
61
+
62
+ <div style="
63
+ margin: 30px 0;
64
+ padding: 20px;
65
+ background: #f9fafb;
66
+ border-radius: 8px;
67
+ color: ${brandColor};
68
+ font-size: 32px;
69
+ font-weight: bold;
70
+ letter-spacing: 8px;
71
+ ">
72
+ ${safeOtp}
73
+ </div>
74
+
75
+ <p style="color: #6b7280;">
76
+ This code expires in
77
+ <strong>${safeExpiresIn}</strong>.
78
+ </p>
79
+
80
+ <p style="
81
+ color: #9ca3af;
82
+ font-size: 13px;
83
+ line-height: 1.6;
84
+ ">
85
+ If you didn't request this verification code,
86
+ please ignore this email.
87
+ </p>
88
+
89
+ ${safeSupportEmail
90
+ ? `
91
+ <p style="font-size: 13px; color: #6b7280;">
92
+ Support:
93
+ <a href="mailto:${safeSupportEmail}">
94
+ ${safeSupportEmail}
95
+ </a>
96
+ </p>
97
+ `
98
+ : ""}
99
+
100
+ <hr style="
101
+ border: none;
102
+ border-top: 1px solid #e5e7eb;
103
+ margin: 25px 0;
104
+ " />
105
+
106
+ <p style="font-size: 12px; color: #9ca3af;">
107
+ &copy; ${new Date().getFullYear()} ${safeCompanyName}.
108
+ All rights reserved.
109
+ </p>
110
+
111
+ </div>
112
+
113
+ </body>
114
+ </html>
115
+ `;
116
+ }
117
+ //# sourceMappingURL=default.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default.js","sourceRoot":"","sources":["../../src/templates/default.ts"],"names":[],"mappings":";;;AACA,0DAAoD;AAEpD,yBAAgC,OAAwB;IACtD,MAAM,EACJ,WAAW,EACX,GAAG,EACH,SAAS,GAAG,WAAW,EACvB,UAAU,GAAG,SAAS,EACtB,OAAO,EACP,YAAY,GACb,GAAG,OAAO,CAAC;IAEZ,MAAM,eAAe,GAAG,IAAA,0BAAU,EAAC,WAAW,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAA,0BAAU,EAAC,GAAG,CAAC,CAAC;IAChC,MAAM,aAAa,GAAG,IAAA,0BAAU,EAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,gBAAgB,GAAG,YAAY;QACnC,CAAC,CAAC,IAAA,0BAAU,EAAC,YAAY,CAAC;QAC1B,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,WAAW,GAAG,OAAO;QACzB,CAAC,CAAC,IAAA,0BAAU,EAAC,OAAO,CAAC;QACrB,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;QA0BD,WAAW;QACT,CAAC,CAAC;;mBAEO,WAAW;mBACX,eAAe;;;;WAIvB;QACD,CAAC,CAAC,EACN;;;;;;;;;;;;;;;;;iBAiBW,UAAU;;;;;UAKjB,OAAO;;;;;kBAKC,aAAa;;;;;;;;;;;;QAavB,gBAAgB;QACd,CAAC,CAAC;;;8BAGkB,gBAAgB;gBAC9B,gBAAgB;;;WAGrB;QACD,CAAC,CAAC,EACN;;;;;;;;;iBASW,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,eAAe;;;;;;;;GAQzD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { OtpEmailOptions } from "../types/email.types.js";
2
+ export declare function modernTemplate(options: OtpEmailOptions): string;
3
+ //# sourceMappingURL=modern.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modern.d.ts","sourceRoot":"","sources":["../../src/templates/modern.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG/D,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAsI/D"}
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.modernTemplate = modernTemplate;
4
+ const escapeHtml_js_1 = require("../utils/escapeHtml.js");
5
+ function modernTemplate(options) {
6
+ const { companyName, otp, expiresIn = "5 minutes", brandColor = "#2563eb", logoUrl, supportEmail } = options;
7
+ const safeCompanyName = (0, escapeHtml_js_1.escapeHtml)(companyName);
8
+ const safeOtp = (0, escapeHtml_js_1.escapeHtml)(otp);
9
+ const safeExpiresIn = (0, escapeHtml_js_1.escapeHtml)(expiresIn);
10
+ const safeLogoUrl = logoUrl ? (0, escapeHtml_js_1.escapeHtml)(logoUrl) : "";
11
+ const safeSupportEmail = supportEmail
12
+ ? (0, escapeHtml_js_1.escapeHtml)(supportEmail)
13
+ : "";
14
+ return `
15
+ <!DOCTYPE html>
16
+ <html>
17
+ <head>
18
+ <meta charset="UTF-8">
19
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
20
+ <title>Email Verification</title>
21
+ </head>
22
+
23
+ <body style="
24
+ margin: 0;
25
+ padding: 30px 12px;
26
+ background-color: #eef2ff;
27
+ font-family: Arial, sans-serif;
28
+ ">
29
+
30
+ <div style="
31
+ max-width: 520px;
32
+ margin: auto;
33
+ background: #ffffff;
34
+ border-radius: 16px;
35
+ overflow: hidden;
36
+ ">
37
+
38
+ <div style="
39
+ padding: 30px 20px;
40
+ background: ${brandColor};
41
+ text-align: center;
42
+ ">
43
+ ${logoUrl
44
+ ? `<img src="${safeLogoUrl}" alt="${safeCompanyName}" width="110" style="margin-bottom: 15px;">`
45
+ : ""}
46
+
47
+ <h1 style="
48
+ color: #ffffff;
49
+ font-size: 24px;
50
+ margin: 0;
51
+ ">
52
+ Verify Your Email
53
+ </h1>
54
+ </div>
55
+
56
+ <div style="
57
+ padding: 35px 25px;
58
+ text-align: center;
59
+ ">
60
+
61
+ <p style="color: #4b5563; line-height: 1.7;">
62
+ Welcome to <strong>${safeCompanyName}</strong>!
63
+ Please use the verification code below.
64
+ </p>
65
+
66
+ <div style="
67
+ margin: 30px 0;
68
+ padding: 25px 15px;
69
+ background: #f1f5f9;
70
+ border-radius: 12px;
71
+ ">
72
+ <p style="
73
+ margin: 0 0 12px;
74
+ color: #64748b;
75
+ font-size: 13px;
76
+ ">
77
+ YOUR VERIFICATION CODE
78
+ </p>
79
+
80
+ <div style="
81
+ color: ${brandColor};
82
+ font-size: 32px;
83
+ font-weight: bold;
84
+ letter-spacing: 8px;
85
+ ">
86
+ ${safeOtp}
87
+ </div>
88
+ </div>
89
+
90
+ <p style="color: #64748b; font-size: 14px;">
91
+ This code expires in
92
+ <strong>${safeExpiresIn}</strong>.
93
+ </p>
94
+
95
+ <p style="
96
+ color: #94a3b8;
97
+ font-size: 13px;
98
+ line-height: 1.6;
99
+ ">
100
+ If you didn't request this code, you can safely
101
+ ignore this email.
102
+ </p>
103
+
104
+ ${supportEmail
105
+ ? `<p style="font-size: 13px; color: #64748b;">Need help? <a href="mailto:${safeSupportEmail}">${safeSupportEmail}</a></p>`
106
+ : ""}
107
+
108
+ </div>
109
+
110
+ <div style="
111
+ padding: 18px;
112
+ background: #f8fafc;
113
+ text-align: center;
114
+ color: #94a3b8;
115
+ font-size: 12px;
116
+ ">
117
+ © ${new Date().getFullYear()} ${safeCompanyName}.
118
+ All rights reserved.
119
+ </div>
120
+
121
+ </div>
122
+
123
+ </body>
124
+ </html>
125
+ `;
126
+ }
127
+ //# sourceMappingURL=modern.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modern.js","sourceRoot":"","sources":["../../src/templates/modern.ts"],"names":[],"mappings":";;;AAEA,0DAAoD;AAEpD,wBAA+B,OAAwB;IACnD,MAAM,EACF,WAAW,EACX,GAAG,EACH,SAAS,GAAG,WAAW,EACvB,UAAU,GAAG,SAAS,EACtB,OAAO,EACP,YAAY,EACf,GAAG,OAAO,CAAC;IAEZ,MAAM,eAAe,GAAG,IAAA,0BAAU,EAAC,WAAW,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAA,0BAAU,EAAC,GAAG,CAAC,CAAC;IAChC,MAAM,aAAa,GAAG,IAAA,0BAAU,EAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,IAAA,0BAAU,EAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,MAAM,gBAAgB,GAAG,YAAY;QACjC,CAAC,CAAC,IAAA,0BAAU,EAAC,YAAY,CAAC;QAC1B,CAAC,CAAC,EAAE,CAAC;IAET,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;8BA0BmB,UAAU;;;kBAIpB,OAAO;QACH,CAAC,CAAC,aAAa,WAAW,UAAU,eAAe,6CAA6C;QAChG,CAAC,CAAC,EACV;;;;;;;;;;;;;;;;;yCAiByB,eAAe;;;;;;;;;;;;;;;;;;;iCAmBvB,UAAU;;;;;0BAKjB,OAAO;;;;;;8BAMH,aAAa;;;;;;;;;;;;kBAavB,YAAY;QACR,CAAC,CAAC,0EAA0E,gBAAgB,KAAK,gBAAgB,UAAU;QAC3H,CAAC,CAAC,EACV;;;;;;;;;;;oBAWI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,eAAe;;;;;;;;KAQ1D,CAAC;AACN,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=template.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.test.d.ts","sourceRoot":"","sources":["../../tests/template.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const index_js_1 = require("../src/index.js");
5
+ (0, vitest_1.describe)("Custom Email OTP Template", () => {
6
+ (0, vitest_1.it)("should generate HTML for valid OTP", () => {
7
+ const result = (0, index_js_1.generateOtpEmail)({
8
+ companyName: "My Company",
9
+ otp: "583921"
10
+ });
11
+ (0, vitest_1.expect)(result.html).toBeTypeOf("string");
12
+ (0, vitest_1.expect)(result.html).toContain("583921");
13
+ (0, vitest_1.expect)(result.html).toContain("My Company");
14
+ });
15
+ (0, vitest_1.it)("should throw error when company name is empty", () => {
16
+ (0, vitest_1.expect)(() => {
17
+ (0, index_js_1.generateOtpEmail)({
18
+ companyName: "",
19
+ otp: "583921"
20
+ });
21
+ }).toThrow("Company name is required.");
22
+ });
23
+ (0, vitest_1.it)("should throw error when OTP is empty", () => {
24
+ (0, vitest_1.expect)(() => {
25
+ (0, index_js_1.generateOtpEmail)({
26
+ companyName: "My Company",
27
+ otp: ""
28
+ });
29
+ }).toThrow("OTP is required.");
30
+ });
31
+ (0, vitest_1.it)("should reject invalid OTP", () => {
32
+ (0, vitest_1.expect)(() => {
33
+ (0, index_js_1.generateOtpEmail)({
34
+ companyName: "My Company",
35
+ otp: "12"
36
+ });
37
+ }).toThrow("OTP must contain 4 to 8 alphanumeric characters.");
38
+ });
39
+ (0, vitest_1.it)("should reject invalid brand color", () => {
40
+ (0, vitest_1.expect)(() => {
41
+ (0, index_js_1.generateOtpEmail)({
42
+ companyName: "My Company",
43
+ otp: "583921",
44
+ brandColor: "red"
45
+ });
46
+ }).toThrow("Brand color must be a valid HEX color.");
47
+ });
48
+ (0, vitest_1.it)("should escape HTML characters", () => {
49
+ const result = (0, index_js_1.generateOtpEmail)({
50
+ companyName: "<script>alert('test')</script>",
51
+ otp: "583921"
52
+ });
53
+ (0, vitest_1.expect)(result.html).not.toContain("<script>");
54
+ (0, vitest_1.expect)(result.html).toContain("&lt;script&gt;");
55
+ });
56
+ });
57
+ //# sourceMappingURL=template.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.test.js","sourceRoot":"","sources":["../../tests/template.test.ts"],"names":[],"mappings":";;AACA,mCAA8C;AAC9C,8CAAmD;AAEnD,IAAA,iBAAQ,EAAC,2BAA2B,EAAE,GAAG,EAAE;IAEvC,IAAA,WAAE,EAAC,oCAAoC,EAAE,GAAG,EAAE;QAE1C,MAAM,MAAM,GAAG,IAAA,2BAAgB,EAAC;YAC5B,WAAW,EAAE,YAAY;YACzB,GAAG,EAAE,QAAQ;SAChB,CAAC,CAAC;QAEH,IAAA,eAAM,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAA,eAAM,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAA,eAAM,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,+CAA+C,EAAE,GAAG,EAAE;QAErD,IAAA,eAAM,EAAC,GAAG,EAAE;YACR,IAAA,2BAAgB,EAAC;gBACb,WAAW,EAAE,EAAE;gBACf,GAAG,EAAE,QAAQ;aAChB,CAAC,CAAC;QACP,CAAC,CAAC,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,sCAAsC,EAAE,GAAG,EAAE;QAE5C,IAAA,eAAM,EAAC,GAAG,EAAE;YACR,IAAA,2BAAgB,EAAC;gBACb,WAAW,EAAE,YAAY;gBACzB,GAAG,EAAE,EAAE;aACV,CAAC,CAAC;QACP,CAAC,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,2BAA2B,EAAE,GAAG,EAAE;QAEjC,IAAA,eAAM,EAAC,GAAG,EAAE;YACR,IAAA,2BAAgB,EAAC;gBACb,WAAW,EAAE,YAAY;gBACzB,GAAG,EAAE,IAAI;aACZ,CAAC,CAAC;QACP,CAAC,CAAC,CAAC,OAAO,CAAC,kDAAkD,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,mCAAmC,EAAE,GAAG,EAAE;QAEzC,IAAA,eAAM,EAAC,GAAG,EAAE;YACR,IAAA,2BAAgB,EAAC;gBACb,WAAW,EAAE,YAAY;gBACzB,GAAG,EAAE,QAAQ;gBACb,UAAU,EAAE,KAAK;aACpB,CAAC,CAAC;QACP,CAAC,CAAC,CAAC,OAAO,CAAC,wCAAwC,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,+BAA+B,EAAE,GAAG,EAAE;QAErC,MAAM,MAAM,GAAG,IAAA,2BAAgB,EAAC;YAC5B,WAAW,EAAE,gCAAgC;YAC7C,GAAG,EAAE,QAAQ;SAChB,CAAC,CAAC;QAEH,IAAA,eAAM,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAC9C,IAAA,eAAM,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;AAEP,CAAC,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ export type EmailTemplate = "default" | "modern" | "dark";
2
+ export interface OtpEmailOptions {
3
+ companyName: string;
4
+ otp: string;
5
+ expiresIn?: string;
6
+ brandColor?: string;
7
+ logoUrl?: string;
8
+ template?: EmailTemplate;
9
+ supportEmail?: string;
10
+ }
11
+ //# sourceMappingURL=email.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.types.d.ts","sourceRoot":"","sources":["../../src/types/email.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAI,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAE;AAE5D,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAG,MAAM,CAAC;IACb,SAAS,CAAC,EAAC,MAAM,CAAC;IAClB,UAAU,CAAC,EAAC,MAAM,CAAC;IACnB,OAAO,CAAC,EAAC,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAC,aAAa,CAAC;IACxB,YAAY,CAAC,EAAC,MAAM,CAAA;CACrB"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=email.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.types.js","sourceRoot":"","sources":["../../src/types/email.types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export declare function escapeHtml(value: string): string;
2
+ //# sourceMappingURL=escapeHtml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escapeHtml.d.ts","sourceRoot":"","sources":["../../src/utils/escapeHtml.ts"],"names":[],"mappings":"AACA,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAYhD"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.escapeHtml = escapeHtml;
4
+ function escapeHtml(value) {
5
+ return value.replace(/[&<>"']/g, (character) => {
6
+ const entities = {
7
+ "&": "&amp;",
8
+ "<": "&lt;",
9
+ ">": "&gt;",
10
+ '"': "&quot;",
11
+ "'": "&#39;"
12
+ };
13
+ return entities[character];
14
+ });
15
+ }
16
+ //# sourceMappingURL=escapeHtml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escapeHtml.js","sourceRoot":"","sources":["../../src/utils/escapeHtml.ts"],"names":[],"mappings":";;;AACA,oBAA2B,KAAa;IACpC,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,SAAS,EAAE,EAAE;QAC3C,MAAM,QAAQ,GAA2B;YACrC,GAAG,EAAE,OAAO;YACZ,GAAG,EAAE,MAAM;YACX,GAAG,EAAE,MAAM;YACX,GAAG,EAAE,QAAQ;YACb,GAAG,EAAE,OAAO;SACf,CAAC;QAEF,OAAO,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { OtpEmailOptions } from "../types/email.types.js";
2
+ export declare function validateOtpEmail(options: OtpEmailOptions): void;
3
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI1D,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAwB/D"}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateOtpEmail = validateOtpEmail;
4
+ const validateUrl_js_1 = require("./validateUrl.js");
5
+ const validateEmail_js_1 = require("./validateEmail.js");
6
+ function validateOtpEmail(options) {
7
+ (0, validateUrl_js_1.validateLogoUrl)(options.logoUrl);
8
+ (0, validateEmail_js_1.validateEmail)(options.supportEmail);
9
+ if (!options) {
10
+ throw new Error("OTP email options are required.");
11
+ }
12
+ if (!options.companyName.trim()) {
13
+ throw new Error("Company name is required.");
14
+ }
15
+ if (!options.otp.trim()) {
16
+ throw new Error("OTP is required.");
17
+ }
18
+ if (!/^[a-zA-Z0-9]{4,8}$/.test(options.otp)) {
19
+ throw new Error("OTP must contain 4 to 8 alphanumeric characters.");
20
+ }
21
+ if (options.brandColor && !/^#[0-9A-Fa-f]{6}$/.test(options.brandColor)) {
22
+ throw new Error("Brand color must be a valid HEX color.");
23
+ }
24
+ if (options.template &&
25
+ !["default", "modern", "dark"].includes(options.template)) {
26
+ throw new Error("Invalid email template.");
27
+ }
28
+ }
29
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":";;;AACA,qDAAmD;AACnD,yDAAmD;AAEnD,0BAAiC,OAAwB;IACvD,IAAA,gCAAe,EAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACjC,IAAA,gCAAa,EAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACpC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,OAAO,CAAC,UAAU,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IACD,IACE,OAAO,CAAC,QAAQ;QAChB,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,EACzD,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function validateEmail(email?: string): void;
2
+ //# sourceMappingURL=validateEmail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateEmail.d.ts","sourceRoot":"","sources":["../../src/utils/validateEmail.ts"],"names":[],"mappings":"AACA,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAQlD"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateEmail = validateEmail;
4
+ function validateEmail(email) {
5
+ if (!email)
6
+ return;
7
+ const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
8
+ if (!emailPattern.test(email)) {
9
+ throw new Error("Please provide a valid support email.");
10
+ }
11
+ }
12
+ //# sourceMappingURL=validateEmail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateEmail.js","sourceRoot":"","sources":["../../src/utils/validateEmail.ts"],"names":[],"mappings":";;;AACA,uBAA8B,KAAc;IACxC,IAAI,CAAC,KAAK;QAAE,OAAO;IAEnB,MAAM,YAAY,GAAG,4BAA4B,CAAC;IAElD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC7D,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function validateLogoUrl(url?: string): void;
2
+ //# sourceMappingURL=validateUrl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateUrl.d.ts","sourceRoot":"","sources":["../../src/utils/validateUrl.ts"],"names":[],"mappings":"AACA,wBAAgB,eAAe,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAYlD"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateLogoUrl = validateLogoUrl;
4
+ function validateLogoUrl(url) {
5
+ if (!url)
6
+ return;
7
+ try {
8
+ const parsedUrl = new URL(url);
9
+ if (parsedUrl.protocol !== "https:") {
10
+ throw new Error("Logo URL must use HTTPS.");
11
+ }
12
+ }
13
+ catch {
14
+ throw new Error("Please provide a valid HTTPS logo URL.");
15
+ }
16
+ }
17
+ //# sourceMappingURL=validateUrl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateUrl.js","sourceRoot":"","sources":["../../src/utils/validateUrl.ts"],"names":[],"mappings":";;;AACA,yBAAgC,GAAY;IACxC,IAAI,CAAC,GAAG;QAAE,OAAO;IAEjB,IAAI,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAE/B,IAAI,SAAS,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAChD,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACL,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC9D,CAAC;AACL,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,38 @@
1
1
  {
2
2
  "name": "custom-email-otp-template",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Customizable HTML email OTP templates for Node.js applications",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "require": "./dist/index.js",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest",
21
+ "example": "tsx examples/example.ts"
22
+ },
23
+ "keywords": [
24
+ "email",
25
+ "otp",
26
+ "email-template",
27
+ "typescript",
28
+ "html-email"
29
+ ],
30
+ "author": "",
31
+ "license": "MIT",
32
+ "devDependencies": {
33
+ "@types/node": "^26.6.6",
34
+ "tsx": "^4.23.15",
35
+ "typescript": "^7.0.2",
36
+ "vitest": "^5.1.0"
37
+ }
38
+ }