@geekmidas/emailkit 0.0.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.
@@ -0,0 +1,182 @@
1
+ import type React from 'react';
2
+ import type { CSSProperties, ReactElement } from 'react';
3
+
4
+ // Common email styling utilities
5
+ export const emailStyles = {
6
+ container: {
7
+ fontFamily:
8
+ '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
9
+ lineHeight: 1.6,
10
+ color: '#333',
11
+ maxWidth: '600px',
12
+ margin: '0 auto',
13
+ padding: '20px',
14
+ } as CSSProperties,
15
+
16
+ heading: {
17
+ color: '#2c3e50',
18
+ marginBottom: '20px',
19
+ } as CSSProperties,
20
+
21
+ button: {
22
+ display: 'inline-block',
23
+ padding: '12px 24px',
24
+ backgroundColor: '#3498db',
25
+ color: '#ffffff',
26
+ textDecoration: 'none',
27
+ borderRadius: '4px',
28
+ fontWeight: 'bold',
29
+ } as CSSProperties,
30
+
31
+ footer: {
32
+ marginTop: '40px',
33
+ paddingTop: '20px',
34
+ borderTop: '1px solid #eee',
35
+ fontSize: '14px',
36
+ color: '#666',
37
+ } as CSSProperties,
38
+ };
39
+
40
+ // Base layout component for emails
41
+ export interface EmailLayoutProps {
42
+ children: React.ReactNode;
43
+ preheader?: string;
44
+ title?: string;
45
+ }
46
+
47
+ export function EmailLayout({
48
+ children,
49
+ preheader,
50
+ title,
51
+ }: EmailLayoutProps): ReactElement {
52
+ return (
53
+ <html>
54
+ <head>
55
+ <meta charSet="utf-8" />
56
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
57
+ {title && <title>{title}</title>}
58
+ {preheader && (
59
+ <div style={{ display: 'none', maxHeight: 0, overflow: 'hidden' }}>
60
+ {preheader}
61
+ </div>
62
+ )}
63
+ </head>
64
+ <body style={{ margin: 0, padding: 0, backgroundColor: '#f4f4f4' }}>
65
+ <div style={emailStyles.container}>{children}</div>
66
+ </body>
67
+ </html>
68
+ );
69
+ }
70
+
71
+ // Example welcome email template
72
+ export interface WelcomeEmailProps {
73
+ name: string;
74
+ confirmationUrl?: string;
75
+ }
76
+
77
+ export function WelcomeEmail({
78
+ name,
79
+ confirmationUrl,
80
+ }: WelcomeEmailProps): ReactElement {
81
+ return (
82
+ <EmailLayout
83
+ title="Welcome!"
84
+ preheader={`Welcome to our service, ${name}!`}
85
+ >
86
+ <h1 style={emailStyles.heading}>Welcome, {name}!</h1>
87
+ <p>
88
+ We're excited to have you on board. Your account has been successfully
89
+ created.
90
+ </p>
91
+ {confirmationUrl && (
92
+ <>
93
+ <p>Please confirm your email address by clicking the button below:</p>
94
+ <p style={{ textAlign: 'center', margin: '30px 0' }}>
95
+ <a href={confirmationUrl} style={emailStyles.button}>
96
+ Confirm Email
97
+ </a>
98
+ </p>
99
+ </>
100
+ )}
101
+ <div style={emailStyles.footer}>
102
+ <p>If you have any questions, feel free to reply to this email.</p>
103
+ <p>
104
+ Best regards,
105
+ <br />
106
+ The Team
107
+ </p>
108
+ </div>
109
+ </EmailLayout>
110
+ );
111
+ }
112
+
113
+ // Example password reset template
114
+ export interface PasswordResetEmailProps {
115
+ name: string;
116
+ resetUrl: string;
117
+ expiresIn: string;
118
+ }
119
+
120
+ export function PasswordResetEmail({
121
+ name,
122
+ resetUrl,
123
+ expiresIn,
124
+ }: PasswordResetEmailProps): ReactElement {
125
+ return (
126
+ <EmailLayout title="Password Reset Request" preheader="Reset your password">
127
+ <h1 style={emailStyles.heading}>Password Reset Request</h1>
128
+ <p>Hi {name},</p>
129
+ <p>
130
+ We received a request to reset your password. Click the button below to
131
+ create a new password:
132
+ </p>
133
+ <p style={{ textAlign: 'center', margin: '30px 0' }}>
134
+ <a href={resetUrl} style={emailStyles.button}>
135
+ Reset Password
136
+ </a>
137
+ </p>
138
+ <p>
139
+ This link will expire in {expiresIn}. If you didn't request a password
140
+ reset, you can safely ignore this email.
141
+ </p>
142
+ <div style={emailStyles.footer}>
143
+ <p>For security reasons, this link can only be used once.</p>
144
+ </div>
145
+ </EmailLayout>
146
+ );
147
+ }
148
+
149
+ // Example notification template
150
+ export interface NotificationEmailProps {
151
+ name: string;
152
+ title: string;
153
+ message: string;
154
+ actionUrl?: string;
155
+ actionText?: string;
156
+ }
157
+
158
+ export function NotificationEmail({
159
+ name,
160
+ title,
161
+ message,
162
+ actionUrl,
163
+ actionText = 'View Details',
164
+ }: NotificationEmailProps): ReactElement {
165
+ return (
166
+ <EmailLayout title={title} preheader={message}>
167
+ <h1 style={emailStyles.heading}>{title}</h1>
168
+ <p>Hi {name},</p>
169
+ <p>{message}</p>
170
+ {actionUrl && (
171
+ <p style={{ textAlign: 'center', margin: '30px 0' }}>
172
+ <a href={actionUrl} style={emailStyles.button}>
173
+ {actionText}
174
+ </a>
175
+ </p>
176
+ )}
177
+ <div style={emailStyles.footer}>
178
+ <p>This is an automated notification from our system.</p>
179
+ </div>
180
+ </EmailLayout>
181
+ );
182
+ }
package/src/types.ts ADDED
@@ -0,0 +1,94 @@
1
+ import type { Address } from 'nodemailer/lib/mailer';
2
+ import type { ReactElement } from 'react';
3
+
4
+ export interface EmailOptions {
5
+ from: string | Address;
6
+ to: string | string[] | Address[];
7
+ cc?: string | string[] | Address[];
8
+ bcc?: string | string[] | Address[];
9
+ subject: string;
10
+ replyTo?: string | Address;
11
+ attachments?: Attachment[];
12
+ }
13
+
14
+ export interface Attachment {
15
+ filename: string;
16
+ content?: string | Buffer;
17
+ path?: string;
18
+ contentType?: string;
19
+ cid?: string;
20
+ }
21
+
22
+ export interface PlainEmailOptions extends EmailOptions {
23
+ text?: string;
24
+ html?: string;
25
+ }
26
+
27
+
28
+ export interface SMTPConfig {
29
+ host: string;
30
+ port: number;
31
+ secure?: boolean;
32
+ auth?: {
33
+ user: string;
34
+ pass: string;
35
+ };
36
+ tls?: {
37
+ rejectUnauthorized?: boolean;
38
+ servername?: string;
39
+ };
40
+ pool?: boolean;
41
+ maxConnections?: number;
42
+ maxMessages?: number;
43
+ rateLimit?: number;
44
+ logger?: boolean;
45
+ debug?: boolean;
46
+ }
47
+
48
+
49
+ export interface SendResult {
50
+ messageId: string;
51
+ accepted: string[];
52
+ rejected: string[];
53
+ response: string;
54
+ }
55
+
56
+ export interface EmailTemplate<T = any> {
57
+ (props: T): ReactElement;
58
+ }
59
+
60
+ // Extract props type from a template function
61
+ export type TemplateProps<T> = T extends EmailTemplate<infer P> ? P : never;
62
+
63
+ // Template record type for type safety
64
+ export type TemplateRecord = Record<string, EmailTemplate<any>>;
65
+
66
+ // Extract template names from template record
67
+ export type TemplateNames<T extends TemplateRecord> = keyof T;
68
+
69
+ // Extract props for a specific template
70
+ export type TemplatePropsFor<
71
+ T extends TemplateRecord,
72
+ K extends TemplateNames<T>,
73
+ > = TemplateProps<T[K]>;
74
+
75
+ export interface EmailClientConfig<T extends TemplateRecord = TemplateRecord> {
76
+ smtp: SMTPConfig;
77
+ templates: T;
78
+ defaults?: {
79
+ from?: string | Address;
80
+ replyTo?: string | Address;
81
+ };
82
+ }
83
+
84
+ export interface EmailClient<T extends TemplateRecord = TemplateRecord> {
85
+ send(options: PlainEmailOptions): Promise<SendResult>;
86
+ sendTemplate<K extends TemplateNames<T>>(
87
+ template: K,
88
+ options: Omit<EmailOptions, 'template'> & {
89
+ props: TemplatePropsFor<T, K>;
90
+ },
91
+ ): Promise<SendResult>;
92
+ verify(): Promise<boolean>;
93
+ close(): Promise<void>;
94
+ }