@alis-kit/mailer 0.1.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 (38) hide show
  1. package/README.md +284 -0
  2. package/dist/core/MailerKit.d.ts +90 -0
  3. package/dist/core/MailerKit.d.ts.map +1 -0
  4. package/dist/core/MailerKit.js +179 -0
  5. package/dist/core/MailerKit.js.map +1 -0
  6. package/dist/decorators/EmailTemplate.d.ts +39 -0
  7. package/dist/decorators/EmailTemplate.d.ts.map +1 -0
  8. package/dist/decorators/EmailTemplate.js +58 -0
  9. package/dist/decorators/EmailTemplate.js.map +1 -0
  10. package/dist/index.d.ts +4 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +4 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/providers/BaseProvider.d.ts +14 -0
  15. package/dist/providers/BaseProvider.d.ts.map +1 -0
  16. package/dist/providers/BaseProvider.js +3 -0
  17. package/dist/providers/BaseProvider.js.map +1 -0
  18. package/dist/providers/NodemailerProvider.d.ts +7 -0
  19. package/dist/providers/NodemailerProvider.d.ts.map +1 -0
  20. package/dist/providers/NodemailerProvider.js +26 -0
  21. package/dist/providers/NodemailerProvider.js.map +1 -0
  22. package/dist/template/TemplateEngine.d.ts +8 -0
  23. package/dist/template/TemplateEngine.d.ts.map +1 -0
  24. package/dist/template/TemplateEngine.js +33 -0
  25. package/dist/template/TemplateEngine.js.map +1 -0
  26. package/dist/template/TemplatePath.d.ts +2 -0
  27. package/dist/template/TemplatePath.d.ts.map +1 -0
  28. package/dist/template/TemplatePath.js +9 -0
  29. package/dist/template/TemplatePath.js.map +1 -0
  30. package/dist/types/index.d.ts +51 -0
  31. package/dist/types/index.d.ts.map +1 -0
  32. package/dist/types/index.js +2 -0
  33. package/dist/types/index.js.map +1 -0
  34. package/dist/vitest.config.d.ts +3 -0
  35. package/dist/vitest.config.d.ts.map +1 -0
  36. package/dist/vitest.config.js +13 -0
  37. package/dist/vitest.config.js.map +1 -0
  38. package/package.json +38 -0
package/README.md ADDED
@@ -0,0 +1,284 @@
1
+ # Mailer Kit
2
+
3
+ Email delivery abstraction with a template engine and TC39 native decorators (Stage 3).
4
+
5
+ ## Features
6
+
7
+ - **TC39 Native Decorators** — Uses stage 3 decorators with `Symbol.metadata`, no `reflect-metadata` needed
8
+ - **Template Engine** — Dynamic `${variable}` and `${nested.variable}` interpolation
9
+ - **Provider Agnostic** — Switch between Nodemailer, Resend, or custom providers
10
+ - **Async Queues** — Send emails synchronously or queue them for background delivery
11
+ - **Type Safe** — Full TypeScript support with strict mode
12
+
13
+ ## Requirements
14
+
15
+ - **Node.js** >= 18.0.0
16
+ - **TypeScript** >= 5.5.0
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @alis-kit/mailer
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ### 1. Setup
27
+
28
+ ```typescript
29
+ import { MailerKit } from "@alis-kit/mailer"
30
+
31
+ MailerKit.setup({
32
+ provider: "nodemailer",
33
+ config: {
34
+ host: "smtp.gmail.com",
35
+ port: 587,
36
+ auth: {
37
+ user: process.env.SMTP_USER,
38
+ pass: process.env.SMTP_PASS
39
+ }
40
+ },
41
+ from: "noreply@example.com"
42
+ })
43
+ ```
44
+
45
+ ### 2. Define a Template
46
+
47
+ ```typescript
48
+ import { EmailTemplate } from "@alis-kit/mailer"
49
+
50
+ /**
51
+ * Welcome email sent to new users after registration.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * await MailerKit.send(WelcomeEmail, {
56
+ * to: "user@example.com",
57
+ * name: "John"
58
+ * })
59
+ * ```
60
+ */
61
+ @EmailTemplate("welcome", {
62
+ template: "./templates/welcome.html",
63
+ subject: "Welcome to Our Platform!"
64
+ })
65
+ export class WelcomeEmail {
66
+ to!: string
67
+ name!: string
68
+ }
69
+ ```
70
+
71
+ ### 3. Create the HTML Template
72
+
73
+ ```html
74
+ <!-- templates/welcome.html -->
75
+ <h1>Welcome, ${name}!</h1>
76
+ <p>We're excited to have you on board.</p>
77
+ ```
78
+
79
+ ### 4. Send
80
+
81
+ ```typescript
82
+ await MailerKit.send(WelcomeEmail, {
83
+ to: "user@example.com",
84
+ name: "John"
85
+ })
86
+ ```
87
+
88
+ ## API Reference
89
+
90
+ ### `MailerKit.setup(config)`
91
+
92
+ Initialize the mailer service. Must be called before sending.
93
+
94
+ ```typescript
95
+ MailerKit.setup({
96
+ provider: "nodemailer",
97
+ config: {
98
+ host: "smtp.example.com",
99
+ port: 587,
100
+ auth: { user: "...", pass: "..." }
101
+ },
102
+ from: "sender@example.com",
103
+ queue: {
104
+ engine: "memory" // or "bullmq"
105
+ }
106
+ })
107
+ ```
108
+
109
+ | Option | Type | Required | Description |
110
+ |--------|------|----------|-------------|
111
+ | `provider` | `"nodemailer"` | Yes | Email provider to use |
112
+ | `config` | `object` | Yes | Provider-specific configuration |
113
+ | `from` | `string` | Yes | Default sender email address |
114
+ | `queue.engine` | `"memory" \| "bullmq"` | No | Queue engine for async delivery |
115
+
116
+ ---
117
+
118
+ ### `@EmailTemplate(name, options)`
119
+
120
+ Class decorator that links a class to an HTML template file. Uses TC39 stage 3 native decorators.
121
+
122
+ ```typescript
123
+ @EmailTemplate("notification", {
124
+ template: "./templates/notification.html",
125
+ subject: "You have a new notification"
126
+ })
127
+ export class NotificationEmail {
128
+ to!: string
129
+ title!: string
130
+ message!: string
131
+ }
132
+ ```
133
+
134
+ | Parameter | Type | Required | Description |
135
+ |-----------|------|----------|-------------|
136
+ | `name` | `string` | Yes | Unique identifier for the template |
137
+ | `options.template` | `string` | Yes | Path to the HTML template file |
138
+ | `options.subject` | `string` | No | Default subject line |
139
+
140
+ ---
141
+
142
+ ### `MailerKit.send(TemplateClass, data)`
143
+
144
+ Send an email immediately.
145
+
146
+ ```typescript
147
+ await MailerKit.send(NotificationEmail, {
148
+ to: "user@example.com",
149
+ title: "New Message",
150
+ message: "You have a new notification!"
151
+ })
152
+ ```
153
+
154
+ | Parameter | Type | Description |
155
+ |-----------|------|-------------|
156
+ | `TemplateClass` | `Class` | A class decorated with `@EmailTemplate` |
157
+ | `data` | `object` | Email data including `to`, template variables, and optional overrides |
158
+
159
+ **Data properties:**
160
+
161
+ | Property | Type | Description |
162
+ |----------|------|-------------|
163
+ | `to` | `string \| string[]` | Recipient(s) — **required** |
164
+ | `subject` | `string` | Override default subject |
165
+ | `cc` | `string \| string[]` | CC recipients |
166
+ | `bcc` | `string \| string[]` | BCC recipients |
167
+ | `replyTo` | `string` | Reply-to address |
168
+ | `attachments` | `Attachment[]` | File attachments (pdf, doc, docx, xls, xlsx, jpg, jpeg, png, zip) |
169
+
170
+ ---
171
+
172
+ ### `MailerKit.queue(TemplateClass, data, options?)`
173
+
174
+ Queue an email for asynchronous delivery.
175
+
176
+ ```typescript
177
+ await MailerKit.queue(WelcomeEmail, {
178
+ to: "user@example.com",
179
+ name: "John"
180
+ }, { delay: "5s" })
181
+ ```
182
+
183
+ | Option | Type | Description |
184
+ |--------|------|-------------|
185
+ | `delay` | `string` | Delay before sending (e.g., `"5s"`, `"2m"`, `"1h"`) |
186
+ | `priority` | `number` | Priority level (for BullMQ) |
187
+
188
+ ---
189
+
190
+ ### `TemplateEngine.render(templatePath, variables)`
191
+
192
+ Render an HTML template with variable interpolation.
193
+
194
+ ```typescript
195
+ import { TemplateEngine } from "@alis-kit/mailer"
196
+
197
+ const html = await TemplateEngine.render("./template.html", {
198
+ name: "John",
199
+ user: { email: "john@example.com" }
200
+ })
201
+ ```
202
+
203
+ **Supported syntax:**
204
+
205
+ | Syntax | Example | Description |
206
+ |--------|---------|-------------|
207
+ | `${var}` | `${name}` | Simple variable |
208
+ | `${nested}` | `${user.email}` | Dot-notation for nested objects |
209
+ | `${array}` | `${items}` | Arrays are JSON-stringified |
210
+
211
+ ---
212
+
213
+ ### `MailerKit.getTemplateMetadata(TemplateClass)`
214
+
215
+ Retrieve the metadata attached to a decorated class.
216
+
217
+ ```typescript
218
+ const metadata = MailerKit.getTemplateMetadata(WelcomeEmail)
219
+ // { name: "welcome", template: "./templates/welcome.html", subject: "Welcome!" }
220
+ ```
221
+
222
+ ## Template Variables
223
+
224
+ Properties defined in your decorated class act as template variables:
225
+
226
+ ```typescript
227
+ @EmailTemplate("invoice", {
228
+ template: "./templates/invoice.html",
229
+ subject: "Your Invoice"
230
+ })
231
+ export class InvoiceEmail {
232
+ to!: string
233
+ invoiceNumber!: string
234
+ total!: number
235
+ items!: Array<{ name: string; price: number }>
236
+ }
237
+ ```
238
+
239
+ ```html
240
+ <!-- templates/invoice.html -->
241
+ <h1>Invoice #${invoiceNumber}</h1>
242
+ <p>Total: $${total}</p>
243
+ <ul>
244
+ ${items}
245
+ </ul>
246
+ ```
247
+
248
+ ## Error Handling
249
+
250
+ | Error | Cause |
251
+ |-------|-------|
252
+ | `MailerKit not initialized. Call setup() first.` | `send()` or `queue()` called before `setup()` |
253
+ | `Unsupported mail provider: <name>` | Invalid provider in `setup()` config |
254
+ | `Class <Name> is not a valid @EmailTemplate` | Class not decorated with `@EmailTemplate` |
255
+ | `Class <Name> is already decorated with @EmailTemplate` | Duplicate `@EmailTemplate` on same class |
256
+ | `Unsupported attachment type: .<ext>` | Attachment file extension not in allowed list |
257
+ | `Template file not found at: <path>` | HTML template file does not exist |
258
+ | `Queue not configured` | `queue()` called without `queue` in config |
259
+ | `BullMQ integration not implemented` | Using `"bullmq"` engine (not yet supported) |
260
+
261
+ ## Testing
262
+
263
+ ```bash
264
+ # Run all tests
265
+ npm test
266
+
267
+ # Run tests in watch mode
268
+ npm run test:watch
269
+ ```
270
+
271
+ ## Architecture
272
+
273
+ This project follows the **Functional Core + Decorator Sugar** pattern:
274
+
275
+ - **`core/`** — Pure functions and classes with all business logic
276
+ - **`decorators/`** — Thin wrappers that store metadata via `Symbol.metadata`
277
+ - **`providers/`** — Email provider adapters (Nodemailer, Resend, etc.)
278
+ - **`template/`** — Template engine and path resolution
279
+
280
+ Decorators never contain business logic — they only attach metadata that `core/` reads.
281
+
282
+ ## License
283
+
284
+ ISC
@@ -0,0 +1,90 @@
1
+ import { MailerKitConfig, SendData, QueueOptions } from "../types/index.js";
2
+ /**
3
+ * Core mailer service that orchestrates email sending and queuing.
4
+ *
5
+ * Reads template metadata from `@EmailTemplate` decorated classes via WeakMap
6
+ * storage — no `reflect-metadata` or `Symbol.metadata` runtime required.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * MailerKit.setup({
11
+ * provider: "nodemailer",
12
+ * config: { host: "smtp.gmail.com", port: 587, auth: { user: "...", pass: "..." } },
13
+ * from: "noreply@example.com"
14
+ * })
15
+ *
16
+ * await MailerKit.send(WelcomeEmail, { to: "user@example.com", name: "John" })
17
+ * ```
18
+ */
19
+ export declare class MailerKit {
20
+ private static config;
21
+ private static provider;
22
+ /**
23
+ * Initialize MailerKit with a provider configuration.
24
+ *
25
+ * @param config - MailerKit configuration including provider and credentials.
26
+ * @throws {Error} If the specified provider is not supported.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * MailerKit.setup({
31
+ * provider: "nodemailer",
32
+ * config: { host: "smtp.example.com", port: 587, auth: { user: "u", pass: "p" } },
33
+ * from: "sender@example.com"
34
+ * })
35
+ * ```
36
+ */
37
+ static setup(config: MailerKitConfig): void;
38
+ /**
39
+ * Retrieve the template metadata attached to a decorated class.
40
+ *
41
+ * Uses WeakMap storage (compatible with all transpilers) — no `reflect-metadata` needed.
42
+ *
43
+ * @param TemplateClass - The class constructor to inspect.
44
+ * @returns The template metadata object with `name`, `template`, and optional `subject`.
45
+ * @throws {Error} If the class does not have `@EmailTemplate` metadata.
46
+ */
47
+ static getTemplateMetadata<T>(TemplateClass: new () => T): {
48
+ name: string;
49
+ template: string;
50
+ subject?: string;
51
+ };
52
+ /**
53
+ * Send an email immediately using a template class.
54
+ *
55
+ * @param TemplateClass - A class decorated with `@EmailTemplate`.
56
+ * @param data - Email data including recipient(s), template variables, and optional overrides.
57
+ * @throws {Error} If MailerKit is not initialized or the class lacks `@EmailTemplate` metadata.
58
+ * @throws {Error} If an attachment has an unsupported file extension.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * await MailerKit.send(WelcomeEmail, {
63
+ * to: "user@example.com",
64
+ * name: "John"
65
+ * })
66
+ * ```
67
+ */
68
+ static send<T>(TemplateClass: new () => T, data: SendData<T>): Promise<void>;
69
+ /**
70
+ * Queue an email for asynchronous delivery.
71
+ *
72
+ * Currently supports the `"memory"` engine with configurable delay.
73
+ * BullMQ support is planned for a future release.
74
+ *
75
+ * @param TemplateClass - A class decorated with `@EmailTemplate`.
76
+ * @param data - Email data including recipient(s) and template variables.
77
+ * @param options - Optional queue options (delay, priority).
78
+ * @throws {Error} If queue is not configured or uses an unsupported engine.
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * await MailerKit.queue(WelcomeEmail, {
83
+ * to: "user@example.com",
84
+ * name: "John"
85
+ * }, { delay: "5s" })
86
+ * ```
87
+ */
88
+ static queue<T>(TemplateClass: new () => T, data: SendData<T>, options?: QueueOptions): Promise<void>;
89
+ }
90
+ //# sourceMappingURL=MailerKit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MailerKit.d.ts","sourceRoot":"","sources":["../../core/MailerKit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAW3E;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAiB;IACtC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAc;IAErC;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,eAAe;IAYpC;;;;;;;;OAQG;IACH,MAAM,CAAC,mBAAmB,CAAC,CAAC,EAC1B,aAAa,EAAE,UAAU,CAAC,GACzB;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;IAWvD;;;;;;;;;;;;;;;OAeG;WACU,IAAI,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBlF;;;;;;;;;;;;;;;;;;OAkBG;WACU,KAAK,CAAC,CAAC,EAClB,aAAa,EAAE,UAAU,CAAC,EAC1B,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EACjB,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,IAAI,CAAC;CAcjB"}
@@ -0,0 +1,179 @@
1
+ import { getEmailTemplateMetadata } from "../decorators/EmailTemplate.js";
2
+ import { TemplateEngine } from "../template/TemplateEngine.js";
3
+ import { TemplatePath } from "../template/TemplatePath.js";
4
+ import { NodemailerProvider } from "../providers/NodemailerProvider.js";
5
+ const ALLOWED_ATTACHMENT_EXTENSIONS = [
6
+ "pdf", "doc", "docx", "xls", "xlsx", "jpg", "jpeg", "png", "zip"
7
+ ];
8
+ /**
9
+ * Core mailer service that orchestrates email sending and queuing.
10
+ *
11
+ * Reads template metadata from `@EmailTemplate` decorated classes via WeakMap
12
+ * storage — no `reflect-metadata` or `Symbol.metadata` runtime required.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * MailerKit.setup({
17
+ * provider: "nodemailer",
18
+ * config: { host: "smtp.gmail.com", port: 587, auth: { user: "...", pass: "..." } },
19
+ * from: "noreply@example.com"
20
+ * })
21
+ *
22
+ * await MailerKit.send(WelcomeEmail, { to: "user@example.com", name: "John" })
23
+ * ```
24
+ */
25
+ export class MailerKit {
26
+ static config;
27
+ static provider;
28
+ /**
29
+ * Initialize MailerKit with a provider configuration.
30
+ *
31
+ * @param config - MailerKit configuration including provider and credentials.
32
+ * @throws {Error} If the specified provider is not supported.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * MailerKit.setup({
37
+ * provider: "nodemailer",
38
+ * config: { host: "smtp.example.com", port: 587, auth: { user: "u", pass: "p" } },
39
+ * from: "sender@example.com"
40
+ * })
41
+ * ```
42
+ */
43
+ static setup(config) {
44
+ this.config = config;
45
+ switch (config.provider) {
46
+ case "nodemailer":
47
+ this.provider = new NodemailerProvider(config.config);
48
+ break;
49
+ default:
50
+ throw new Error(`Unsupported mail provider: ${config.provider}`);
51
+ }
52
+ }
53
+ /**
54
+ * Retrieve the template metadata attached to a decorated class.
55
+ *
56
+ * Uses WeakMap storage (compatible with all transpilers) — no `reflect-metadata` needed.
57
+ *
58
+ * @param TemplateClass - The class constructor to inspect.
59
+ * @returns The template metadata object with `name`, `template`, and optional `subject`.
60
+ * @throws {Error} If the class does not have `@EmailTemplate` metadata.
61
+ */
62
+ static getTemplateMetadata(TemplateClass) {
63
+ const metadata = getEmailTemplateMetadata(TemplateClass);
64
+ if (!metadata) {
65
+ throw new Error(`Class ${TemplateClass.name} is not a valid @EmailTemplate. ` +
66
+ `Ensure the class is decorated with @EmailTemplate("name", { template: "..." }).`);
67
+ }
68
+ return metadata;
69
+ }
70
+ /**
71
+ * Send an email immediately using a template class.
72
+ *
73
+ * @param TemplateClass - A class decorated with `@EmailTemplate`.
74
+ * @param data - Email data including recipient(s), template variables, and optional overrides.
75
+ * @throws {Error} If MailerKit is not initialized or the class lacks `@EmailTemplate` metadata.
76
+ * @throws {Error} If an attachment has an unsupported file extension.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * await MailerKit.send(WelcomeEmail, {
81
+ * to: "user@example.com",
82
+ * name: "John"
83
+ * })
84
+ * ```
85
+ */
86
+ static async send(TemplateClass, data) {
87
+ if (!this.provider) {
88
+ throw new Error("MailerKit not initialized. Call setup() first.");
89
+ }
90
+ const metadata = this.getTemplateMetadata(TemplateClass);
91
+ const html = await TemplateEngine.render(TemplatePath(metadata.template), data);
92
+ const subject = data.subject || metadata.subject || "No Subject";
93
+ if (data.attachments) {
94
+ validateAttachments(data.attachments);
95
+ }
96
+ await this.provider.send({
97
+ from: this.config.from,
98
+ to: data.to,
99
+ subject,
100
+ html,
101
+ cc: data.cc,
102
+ bcc: data.bcc,
103
+ replyTo: data.replyTo,
104
+ attachments: data.attachments
105
+ });
106
+ }
107
+ /**
108
+ * Queue an email for asynchronous delivery.
109
+ *
110
+ * Currently supports the `"memory"` engine with configurable delay.
111
+ * BullMQ support is planned for a future release.
112
+ *
113
+ * @param TemplateClass - A class decorated with `@EmailTemplate`.
114
+ * @param data - Email data including recipient(s) and template variables.
115
+ * @param options - Optional queue options (delay, priority).
116
+ * @throws {Error} If queue is not configured or uses an unsupported engine.
117
+ *
118
+ * @example
119
+ * ```ts
120
+ * await MailerKit.queue(WelcomeEmail, {
121
+ * to: "user@example.com",
122
+ * name: "John"
123
+ * }, { delay: "5s" })
124
+ * ```
125
+ */
126
+ static async queue(TemplateClass, data, options) {
127
+ if (!this.config.queue) {
128
+ throw new Error("Queue not configured");
129
+ }
130
+ if (this.config.queue.engine === "memory") {
131
+ const delayMs = parseDelay(options?.delay || "0s");
132
+ setTimeout(() => {
133
+ this.send(TemplateClass, data).catch(console.error);
134
+ }, delayMs);
135
+ }
136
+ else if (this.config.queue.engine === "bullmq") {
137
+ throw new Error("BullMQ integration not implemented in this version");
138
+ }
139
+ }
140
+ }
141
+ /**
142
+ * Validate that all attachments have allowed file extensions.
143
+ *
144
+ * @param attachments - Array of attachment objects to validate.
145
+ * @throws {Error} If any attachment has an unsupported file extension.
146
+ */
147
+ function validateAttachments(attachments) {
148
+ for (const att of attachments) {
149
+ const ext = att.path.split(".").pop()?.toLowerCase();
150
+ if (!ext || !ALLOWED_ATTACHMENT_EXTENSIONS.includes(ext)) {
151
+ throw new Error(`Unsupported attachment type: .${ext}`);
152
+ }
153
+ }
154
+ }
155
+ /**
156
+ * Parse a human-readable delay string into milliseconds.
157
+ *
158
+ * @param delay - Delay string like `"5s"`, `"2m"`, `"1h"`, `"1d"`.
159
+ * @returns The delay in milliseconds.
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * parseDelay("5s") // 5000
164
+ * parseDelay("2m") // 120000
165
+ * parseDelay("1h") // 3600000
166
+ * ```
167
+ */
168
+ function parseDelay(delay) {
169
+ const unit = delay.slice(-1);
170
+ const value = parseInt(delay.slice(0, -1));
171
+ switch (unit) {
172
+ case "s": return value * 1000;
173
+ case "m": return value * 60 * 1000;
174
+ case "h": return value * 60 * 60 * 1000;
175
+ case "d": return value * 24 * 60 * 60 * 1000;
176
+ default: return value;
177
+ }
178
+ }
179
+ //# sourceMappingURL=MailerKit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MailerKit.js","sourceRoot":"","sources":["../../core/MailerKit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { EmailTemplateOptions } from "../types/index.js";
2
+ export declare const EMAIL_TEMPLATE_METADATA = "mailer:template";
3
+ /**
4
+ * Class decorator that links a class to an email template file.
5
+ *
6
+ * Stores template metadata using WeakMap (compatible with all transpilers)
7
+ * and optionally on `Symbol.metadata` when the runtime supports it.
8
+ *
9
+ * @param name - Unique identifier for the template.
10
+ * @param options - Configuration object for the template.
11
+ * @param options.template - Path to the HTML template file.
12
+ * @param options.subject - Default subject line for the email.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * @EmailTemplate("welcome", {
17
+ * template: "./templates/welcome.html",
18
+ * subject: "Welcome to our platform!"
19
+ * })
20
+ * export class WelcomeEmail {
21
+ * to!: string
22
+ * name!: string
23
+ * }
24
+ * ```
25
+ */
26
+ export declare function EmailTemplate(name: string, options: EmailTemplateOptions): (target: unknown, context: ClassDecoratorContext) => void;
27
+ /**
28
+ * Retrieve the template metadata attached to a decorated class.
29
+ *
30
+ * Reads from WeakMap storage first (always available),
31
+ * then falls back to `Symbol.metadata` if present.
32
+ *
33
+ * @param TemplateClass - The class constructor to inspect.
34
+ * @returns The template metadata object, or `undefined` if not decorated.
35
+ */
36
+ export declare function getEmailTemplateMetadata(TemplateClass: object): (EmailTemplateOptions & {
37
+ name: string;
38
+ }) | undefined;
39
+ //# sourceMappingURL=EmailTemplate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailTemplate.d.ts","sourceRoot":"","sources":["../../decorators/EmailTemplate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAExD,eAAO,MAAM,uBAAuB,oBAAoB,CAAA;AAWxD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,IAC/D,QAAQ,OAAO,EAAE,SAAS,qBAAqB,UAgBxD;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,MAAM,GACpB,CAAC,oBAAoB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAEvD"}
@@ -0,0 +1,58 @@
1
+ export const EMAIL_TEMPLATE_METADATA = "mailer:template";
2
+ /**
3
+ * WeakMap storage for template metadata, keyed by class constructor.
4
+ *
5
+ * TC39 decorators (stage 3) with `context.metadata` require full runtime support.
6
+ * Since esbuild/Vite doesn't fully implement `context.metadata` yet,
7
+ * we use WeakMap as the primary storage mechanism.
8
+ */
9
+ const templateMetadataStore = new WeakMap();
10
+ /**
11
+ * Class decorator that links a class to an email template file.
12
+ *
13
+ * Stores template metadata using WeakMap (compatible with all transpilers)
14
+ * and optionally on `Symbol.metadata` when the runtime supports it.
15
+ *
16
+ * @param name - Unique identifier for the template.
17
+ * @param options - Configuration object for the template.
18
+ * @param options.template - Path to the HTML template file.
19
+ * @param options.subject - Default subject line for the email.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * @EmailTemplate("welcome", {
24
+ * template: "./templates/welcome.html",
25
+ * subject: "Welcome to our platform!"
26
+ * })
27
+ * export class WelcomeEmail {
28
+ * to!: string
29
+ * name!: string
30
+ * }
31
+ * ```
32
+ */
33
+ export function EmailTemplate(name, options) {
34
+ return (target, context) => {
35
+ const key = target;
36
+ if (templateMetadataStore.has(key)) {
37
+ throw new Error(`Class ${String(context.name)} is already decorated with @EmailTemplate`);
38
+ }
39
+ const metadata = { name, ...options };
40
+ templateMetadataStore.set(key, metadata);
41
+ if (context.metadata) {
42
+ context.metadata[EMAIL_TEMPLATE_METADATA] = metadata;
43
+ }
44
+ };
45
+ }
46
+ /**
47
+ * Retrieve the template metadata attached to a decorated class.
48
+ *
49
+ * Reads from WeakMap storage first (always available),
50
+ * then falls back to `Symbol.metadata` if present.
51
+ *
52
+ * @param TemplateClass - The class constructor to inspect.
53
+ * @returns The template metadata object, or `undefined` if not decorated.
54
+ */
55
+ export function getEmailTemplateMetadata(TemplateClass) {
56
+ return templateMetadataStore.get(TemplateClass);
57
+ }
58
+ //# sourceMappingURL=EmailTemplate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailTemplate.js","sourceRoot":"","sources":["../../decorators/EmailTemplate.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,uBAAuB,GAAG,iBAAiB,CAAA;AAExD;;;;;;GAMG;AACH,MAAM,qBAAqB,GAAG,IAAI,OAAO,EAAmD,CAAA;AAE5F;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,OAA6B;IACvE,OAAO,CAAC,MAAe,EAAE,OAA8B,EAAE,EAAE;QACzD,MAAM,GAAG,GAAG,MAAgB,CAAA;QAE5B,IAAI,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CACb,SAAS,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,2CAA2C,CACzE,CAAA;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAA;QACrC,qBAAqB,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;QAExC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACrB,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAC,GAAG,QAAQ,CAAA;QACtD,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,wBAAwB,CACtC,aAAqB;IAErB,OAAO,qBAAqB,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;AACjD,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from "./decorators/EmailTemplate.js";
2
+ export * from "./core/MailerKit.js";
3
+ export * from "./types/index.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAA;AAC7C,cAAc,qBAAqB,CAAA;AACnC,cAAc,kBAAkB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./decorators/EmailTemplate.js";
2
+ export * from "./core/MailerKit.js";
3
+ export * from "./types/index.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAA;AAC7C,cAAc,qBAAqB,CAAA;AACnC,cAAc,kBAAkB,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { Attachment } from "../types/index.js";
2
+ export declare abstract class BaseProvider {
3
+ abstract send(options: {
4
+ from: string;
5
+ to: string | string[];
6
+ subject: string;
7
+ html: string;
8
+ cc?: string | string[];
9
+ bcc?: string | string[];
10
+ replyTo?: string;
11
+ attachments?: Attachment[];
12
+ }): Promise<void>;
13
+ }
14
+ //# sourceMappingURL=BaseProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseProvider.d.ts","sourceRoot":"","sources":["../../providers/BaseProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C,8BAAsB,YAAY;IAChC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE;QACrB,IAAI,EAAE,MAAM,CAAA;QACZ,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;QACrB,OAAO,EAAE,MAAM,CAAA;QACf,IAAI,EAAE,MAAM,CAAA;QACZ,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;QACtB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;QACvB,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAA;KAC3B,GAAG,OAAO,CAAC,IAAI,CAAC;CAClB"}
@@ -0,0 +1,3 @@
1
+ export class BaseProvider {
2
+ }
3
+ //# sourceMappingURL=BaseProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseProvider.js","sourceRoot":"","sources":["../../providers/BaseProvider.ts"],"names":[],"mappings":"AAGA,MAAM,OAAgB,YAAY;CAWjC"}
@@ -0,0 +1,7 @@
1
+ import { BaseProvider } from "./BaseProvider.js";
2
+ export declare class NodemailerProvider extends BaseProvider {
3
+ private transporter;
4
+ constructor(config: any);
5
+ send(options: any): Promise<void>;
6
+ }
7
+ //# sourceMappingURL=NodemailerProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NodemailerProvider.d.ts","sourceRoot":"","sources":["../../providers/NodemailerProvider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGhD,qBAAa,kBAAmB,SAAQ,YAAY;IAClD,OAAO,CAAC,WAAW,CAAK;gBAEZ,MAAM,EAAE,GAAG;IAKjB,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;CAexC"}
@@ -0,0 +1,26 @@
1
+ // MONAGE-PATCH: add .js extensions to relative imports + replace require() with ESM import for NodeNext compatibility
2
+ import nodemailer from "nodemailer";
3
+ import { BaseProvider } from "./BaseProvider.js";
4
+ export class NodemailerProvider extends BaseProvider {
5
+ transporter;
6
+ constructor(config) {
7
+ super();
8
+ this.transporter = nodemailer.createTransport(config);
9
+ }
10
+ async send(options) {
11
+ await this.transporter.sendMail({
12
+ from: options.from,
13
+ to: options.to,
14
+ subject: options.subject,
15
+ html: options.html,
16
+ cc: options.cc,
17
+ bcc: options.bcc,
18
+ replyTo: options.replyTo,
19
+ attachments: options.attachments?.map((a) => ({
20
+ filename: a.filename,
21
+ path: a.path
22
+ }))
23
+ });
24
+ }
25
+ }
26
+ //# sourceMappingURL=NodemailerProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NodemailerProvider.js","sourceRoot":"","sources":["../../providers/NodemailerProvider.ts"],"names":[],"mappings":"AAAA,sHAAsH;AACtH,OAAO,UAAU,MAAM,YAAY,CAAA;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGhD,MAAM,OAAO,kBAAmB,SAAQ,YAAY;IAC1C,WAAW,CAAK;IAExB,YAAY,MAAW;QACrB,KAAK,EAAE,CAAA;QACP,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC,eAAe,CAAC,MAAM,CAAC,CAAA;IACvD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAY;QACrB,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;YAC9B,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAa,EAAE,EAAE,CAAC,CAAC;gBACxD,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,IAAI,EAAE,CAAC,CAAC,IAAI;aACb,CAAC,CAAC;SACJ,CAAC,CAAA;IACJ,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ export declare class TemplateEngine {
2
+ /**
3
+ * Render a template file with variables.
4
+ */
5
+ static render(templatePath: string, variables: Record<string, any>): Promise<string>;
6
+ private static getNestedValue;
7
+ }
8
+ //# sourceMappingURL=TemplateEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TemplateEngine.d.ts","sourceRoot":"","sources":["../../template/TemplateEngine.ts"],"names":[],"mappings":"AAGA,qBAAa,cAAc;IACzB;;OAEG;WACU,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAoB1F,OAAO,CAAC,MAAM,CAAC,cAAc;CAG9B"}
@@ -0,0 +1,33 @@
1
+ import * as fs from "fs/promises";
2
+ import * as path from "path";
3
+ export class TemplateEngine {
4
+ /**
5
+ * Render a template file with variables.
6
+ */
7
+ static async render(templatePath, variables) {
8
+ let content;
9
+ try {
10
+ const fullPath = path.isAbsolute(templatePath)
11
+ ? templatePath
12
+ : path.join(process.cwd(), templatePath);
13
+ content = await fs.readFile(fullPath, "utf-8");
14
+ }
15
+ catch (error) {
16
+ throw new Error(`Template file not found at: ${templatePath}`);
17
+ }
18
+ return content.replace(/\$\{([^}]+)\}/g, (_, key) => {
19
+ const value = this.getNestedValue(variables, key.trim());
20
+ if (value === undefined || value === null)
21
+ return "";
22
+ if (Array.isArray(value))
23
+ return JSON.stringify(value);
24
+ if (typeof value === "object")
25
+ return JSON.stringify(value);
26
+ return String(value);
27
+ });
28
+ }
29
+ static getNestedValue(obj, path) {
30
+ return path.split(".").reduce((acc, part) => acc && acc[part], obj);
31
+ }
32
+ }
33
+ //# sourceMappingURL=TemplateEngine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TemplateEngine.js","sourceRoot":"","sources":["../../template/TemplateEngine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAE5B,MAAM,OAAO,cAAc;IACzB;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,YAAoB,EAAE,SAA8B;QACtE,IAAI,OAAe,CAAA;QACnB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;gBAC5C,CAAC,CAAC,YAAY;gBACd,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,YAAY,CAAC,CAAA;YAC1C,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QAChD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,+BAA+B,YAAY,EAAE,CAAC,CAAA;QAChE,CAAC;QAED,OAAO,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE;YAClD,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;YACxD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;gBAAE,OAAO,EAAE,CAAA;YACpD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;YACtD,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;YAC3D,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;QACtB,CAAC,CAAC,CAAA;IACJ,CAAC;IAEO,MAAM,CAAC,cAAc,CAAC,GAAQ,EAAE,IAAY;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAA;IACrE,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export declare function TemplatePath(templateName: string): string;
2
+ //# sourceMappingURL=TemplatePath.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TemplatePath.d.ts","sourceRoot":"","sources":["../../template/TemplatePath.ts"],"names":[],"mappings":"AAGA,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAMzD"}
@@ -0,0 +1,9 @@
1
+ import { fileURLToPath } from "url";
2
+ import { dirname, join } from "path";
3
+ export function TemplatePath(templateName) {
4
+ const __filename = fileURLToPath(import.meta.url);
5
+ const __dirname = dirname(__filename);
6
+ // Sesuaikan path relatif ke lokasi file utils ini
7
+ return join(__dirname, "../../../", templateName);
8
+ }
9
+ //# sourceMappingURL=TemplatePath.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TemplatePath.js","sourceRoot":"","sources":["../../template/TemplatePath.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAErC,MAAM,UAAU,YAAY,CAAC,YAAoB;IAC7C,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEtC,kDAAkD;IAClD,OAAO,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,51 @@
1
+ export type ProviderType = "nodemailer" | "resend" | "sendgrid";
2
+ export interface EmailTemplateOptions {
3
+ /**
4
+ * Path to the HTML template file.
5
+ */
6
+ template: string;
7
+ /**
8
+ * Default subject for the email.
9
+ */
10
+ subject?: string;
11
+ }
12
+ export type AllowedAttachmentExt = "pdf" | "doc" | "docx" | "xls" | "xlsx" | "jpg" | "jpeg" | "png" | "zip";
13
+ export interface Attachment {
14
+ filename: string;
15
+ path: string;
16
+ }
17
+ export type SendData<T> = Omit<T, never> & {
18
+ to: string | string[];
19
+ subject?: string;
20
+ cc?: string | string[];
21
+ bcc?: string | string[];
22
+ replyTo?: string;
23
+ attachments?: Attachment[];
24
+ };
25
+ export interface QueueOptions {
26
+ delay?: string;
27
+ priority?: number;
28
+ }
29
+ export interface MailerKitConfig {
30
+ provider: ProviderType;
31
+ config: any;
32
+ from: string;
33
+ queue?: {
34
+ engine: "bullmq" | "memory";
35
+ engineConfig?: {
36
+ redis?: {
37
+ host: string;
38
+ port: number;
39
+ };
40
+ };
41
+ rateLimit?: {
42
+ max: number;
43
+ per: "second" | "minute" | "hour";
44
+ };
45
+ retry?: {
46
+ attempts: number;
47
+ backoff: "exponential" | "fixed";
48
+ };
49
+ };
50
+ }
51
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../types/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,QAAQ,GAAG,UAAU,CAAA;AAE/D,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAA;AAE3G,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG;IACzC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACtB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAA;CAC3B,CAAA;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,YAAY,CAAA;IACtB,MAAM,EAAE,GAAG,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE;QACN,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAA;QAC3B,YAAY,CAAC,EAAE;YACb,KAAK,CAAC,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAA;aAAE,CAAA;SACvC,CAAA;QACD,SAAS,CAAC,EAAE;YACV,GAAG,EAAE,MAAM,CAAA;YACX,GAAG,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAA;SAClC,CAAA;QACD,KAAK,CAAC,EAAE;YACN,QAAQ,EAAE,MAAM,CAAA;YAChB,OAAO,EAAE,aAAa,GAAG,OAAO,CAAA;SACjC,CAAA;KACF,CAAA;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../types/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ declare const _default: import("vite").UserConfig;
2
+ export default _default;
3
+ //# sourceMappingURL=vitest.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.config.d.ts","sourceRoot":"","sources":["../vitest.config.ts"],"names":[],"mappings":";AAEA,wBAUE"}
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from "vitest/config";
2
+ export default defineConfig({
3
+ test: {
4
+ globals: true,
5
+ environment: "node",
6
+ include: ["**/*.test.ts"],
7
+ coverage: {
8
+ provider: "v8",
9
+ include: ["core/**/*.ts", "decorators/**/*.ts", "template/**/*.ts"]
10
+ }
11
+ }
12
+ });
13
+ //# sourceMappingURL=vitest.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.config.js","sourceRoot":"","sources":["../vitest.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAE5C,eAAe,YAAY,CAAC;IAC1B,IAAI,EAAE;QACJ,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,MAAM;QACnB,OAAO,EAAE,CAAC,cAAc,CAAC;QACzB,QAAQ,EAAE;YACR,QAAQ,EAAE,IAAI;YACd,OAAO,EAAE,CAAC,cAAc,EAAE,oBAAoB,EAAE,kBAAkB,CAAC;SACpE;KACF;CACF,CAAC,CAAA"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@alis-kit/mailer",
3
+ "version": "0.1.0",
4
+ "description": "Email delivery abstraction with template engine and TC39 native decorators",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "engines": {
18
+ "node": ">=18.0.0"
19
+ },
20
+ "dependencies": {
21
+ "nodemailer": "^6.9.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^20.0.0",
25
+ "@types/nodemailer": "^6.4.0",
26
+ "typescript": "^5.5.0",
27
+ "vitest": "^3.0.0"
28
+ },
29
+ "peerDependencies": {
30
+ "typescript": ">=5.5.0"
31
+ },
32
+ "scripts": {
33
+ "build": "tsc",
34
+ "test": "vitest run",
35
+ "test:watch": "vitest",
36
+ "lint": "tsc --noEmit"
37
+ }
38
+ }