@basedest/mailer 0.0.0-development

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,11 @@
1
+ /**
2
+ * Configuration for the mailer. Provided by the host application.
3
+ */
4
+ export interface MailerConfig {
5
+ provider: 'log' | 'resend';
6
+ from: string;
7
+ resendApiKey?: string;
8
+ retryEnabled?: boolean;
9
+ rateLimitPerMinute?: number;
10
+ }
11
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB,QAAQ,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC/B"}
package/dist/config.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":""}
@@ -0,0 +1,8 @@
1
+ import type { Mailer } from './index';
2
+ import type { MailerConfig } from './config';
3
+ import type { MailerLogger } from './logger';
4
+ export interface CreateMailerOptions {
5
+ logger?: MailerLogger;
6
+ }
7
+ export declare function createMailer(config: MailerConfig, options?: CreateMailerOptions): Mailer;
8
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAM7C,MAAM,WAAW,mBAAmB;IAChC,MAAM,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,MAAM,CAwCxF"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMailer = createMailer;
4
+ const log_1 = require("./log");
5
+ const resend_1 = require("./resend");
6
+ const with_rate_limit_1 = require("./with-rate-limit");
7
+ const with_retry_1 = require("./with-retry");
8
+ function createMailer(config, options) {
9
+ const logger = options?.logger;
10
+ let mailer;
11
+ switch (config.provider) {
12
+ case 'resend':
13
+ mailer = new resend_1.ResendMailer({
14
+ apiKey: config.resendApiKey,
15
+ from: config.from,
16
+ });
17
+ break;
18
+ case 'log':
19
+ default:
20
+ mailer = new log_1.LogMailer({ logger });
21
+ break;
22
+ }
23
+ if (config.retryEnabled) {
24
+ mailer = (0, with_retry_1.withRetry)(mailer, {
25
+ maxAttempts: 3,
26
+ delayMs: 1000,
27
+ onDeadLetter(params, error) {
28
+ logger?.log({
29
+ level: 'error',
30
+ message: 'Mail dead letter',
31
+ extra: {
32
+ to: params.to,
33
+ subject: params.subject,
34
+ error: error.message,
35
+ },
36
+ });
37
+ },
38
+ });
39
+ }
40
+ if (config.rateLimitPerMinute != null && config.rateLimitPerMinute > 0) {
41
+ mailer = (0, with_rate_limit_1.withRateLimit)(mailer, { maxPerMinute: config.rateLimitPerMinute });
42
+ }
43
+ return mailer;
44
+ }
45
+ //# sourceMappingURL=factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":";;AAYA,oCAwCC;AAjDD,+BAAkC;AAClC,qCAAwC;AACxC,uDAAkD;AAClD,6CAAyC;AAMzC,SAAgB,YAAY,CAAC,MAAoB,EAAE,OAA6B;IAC5E,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC;IAC/B,IAAI,MAAc,CAAC;IAEnB,QAAQ,MAAM,CAAC,QAAQ,EAAE,CAAC;QACtB,KAAK,QAAQ;YACT,MAAM,GAAG,IAAI,qBAAY,CAAC;gBACtB,MAAM,EAAE,MAAM,CAAC,YAAa;gBAC5B,IAAI,EAAE,MAAM,CAAC,IAAI;aACpB,CAAC,CAAC;YACH,MAAM;QACV,KAAK,KAAK,CAAC;QACX;YACI,MAAM,GAAG,IAAI,eAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;YACnC,MAAM;IACd,CAAC;IAED,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACtB,MAAM,GAAG,IAAA,sBAAS,EAAC,MAAM,EAAE;YACvB,WAAW,EAAE,CAAC;YACd,OAAO,EAAE,IAAI;YACb,YAAY,CAAC,MAAM,EAAE,KAAK;gBACtB,MAAM,EAAE,GAAG,CAAC;oBACR,KAAK,EAAE,OAAO;oBACd,OAAO,EAAE,kBAAkB;oBAC3B,KAAK,EAAE;wBACH,EAAE,EAAE,MAAM,CAAC,EAAE;wBACb,OAAO,EAAE,MAAM,CAAC,OAAO;wBACvB,KAAK,EAAE,KAAK,CAAC,OAAO;qBACvB;iBACJ,CAAC,CAAC;YACP,CAAC;SACJ,CAAC,CAAC;IACP,CAAC;IAED,IAAI,MAAM,CAAC,kBAAkB,IAAI,IAAI,IAAI,MAAM,CAAC,kBAAkB,GAAG,CAAC,EAAE,CAAC;QACrE,MAAM,GAAG,IAAA,+BAAa,EAAC,MAAM,EAAE,EAAE,YAAY,EAAE,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface SendParams {
3
+ to: string | string[];
4
+ subject: string;
5
+ /** HTML string or React node (e.g. React Email template). */
6
+ body: string | ReactNode;
7
+ /** Optional locale for future i18n; no translation in this layer. */
8
+ locale?: string;
9
+ from?: string;
10
+ replyTo?: string;
11
+ /** Optional idempotency key for retry/dedup. */
12
+ idempotencyKey?: string;
13
+ }
14
+ export type SendResult = {
15
+ success: true;
16
+ id?: string;
17
+ } | {
18
+ success: false;
19
+ error: Error;
20
+ };
21
+ export interface Mailer {
22
+ send(params: SendParams): Promise<SendResult>;
23
+ }
24
+ export * from './config';
25
+ export * from './factory';
26
+ export * from './logger';
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,UAAU,GAAG;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAE3F,MAAM,WAAW,MAAM;IACnB,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACjD;AAED,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./config"), exports);
18
+ __exportStar(require("./factory"), exports);
19
+ __exportStar(require("./logger"), exports);
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAqBA,2CAAyB;AACzB,4CAA0B;AAC1B,2CAAyB"}
package/dist/log.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { Mailer, SendParams, SendResult } from './index';
2
+ import type { MailerLogger } from './logger';
3
+ export interface LogMailerOptions {
4
+ logger?: MailerLogger;
5
+ }
6
+ export declare class LogMailer implements Mailer {
7
+ private logger;
8
+ constructor(options?: LogMailerOptions);
9
+ send(params: SendParams): Promise<SendResult>;
10
+ }
11
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAe7C,MAAM,WAAW,gBAAgB;IAC7B,MAAM,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,qBAAa,SAAU,YAAW,MAAM;IACpC,OAAO,CAAC,MAAM,CAAe;gBAEjB,OAAO,CAAC,EAAE,gBAAgB;IAIhC,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;CAatD"}
package/dist/log.js ADDED
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LogMailer = void 0;
4
+ const BODY_PREVIEW_MAX_LEN = 200;
5
+ function bodyPreview(body) {
6
+ if (typeof body === 'string') {
7
+ return body.length <= BODY_PREVIEW_MAX_LEN ? body : `${body.slice(0, BODY_PREVIEW_MAX_LEN)}...`;
8
+ }
9
+ return '[React template]';
10
+ }
11
+ const noopLogger = {
12
+ log: () => { },
13
+ };
14
+ class LogMailer {
15
+ constructor(options) {
16
+ this.logger = options?.logger ?? noopLogger;
17
+ }
18
+ async send(params) {
19
+ const to = Array.isArray(params.to) ? params.to : [params.to];
20
+ this.logger.log({
21
+ message: 'Mail (log provider)',
22
+ extra: {
23
+ to,
24
+ subject: params.subject,
25
+ bodyPreview: bodyPreview(params.body),
26
+ locale: params.locale,
27
+ },
28
+ });
29
+ return { success: true };
30
+ }
31
+ }
32
+ exports.LogMailer = LogMailer;
33
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":";;;AAGA,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAEjC,SAAS,WAAW,CAAC,IAAwB;IACzC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC,MAAM,IAAI,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,KAAK,CAAC;IACpG,CAAC;IACD,OAAO,kBAAkB,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,GAAiB;IAC7B,GAAG,EAAE,GAAG,EAAE,GAAE,CAAC;CAChB,CAAC;AAMF,MAAa,SAAS;IAGlB,YAAY,OAA0B;QAClC,IAAI,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,UAAU,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAkB;QACzB,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC9D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;YACZ,OAAO,EAAE,qBAAqB;YAC9B,KAAK,EAAE;gBACH,EAAE;gBACF,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC;gBACrC,MAAM,EAAE,MAAM,CAAC,MAAM;aACxB;SACJ,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;CACJ;AApBD,8BAoBC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Minimal logger interface for mailer. The host application provides
3
+ */
4
+ export interface MailerLogger {
5
+ log(opts: {
6
+ level?: 'debug' | 'info' | 'warn' | 'error' | 'fatal';
7
+ message: string;
8
+ extra?: Record<string, unknown>;
9
+ }): void;
10
+ }
11
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB,GAAG,CAAC,IAAI,EAAE;QACN,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;QACtD,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACnC,GAAG,IAAI,CAAC;CACZ"}
package/dist/logger.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import type { Mailer, SendParams, SendResult } from './index';
2
+ export interface ResendMailerOptions {
3
+ apiKey: string;
4
+ from: string;
5
+ }
6
+ export declare class ResendMailer implements Mailer {
7
+ private client;
8
+ private defaultFrom;
9
+ constructor(options: ResendMailerOptions);
10
+ send(params: SendParams): Promise<SendResult>;
11
+ }
12
+ //# sourceMappingURL=resend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resend.d.ts","sourceRoot":"","sources":["../src/resend.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE9D,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,YAAa,YAAW,MAAM;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,mBAAmB;IAKlC,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;CA6BtD"}
package/dist/resend.js ADDED
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ResendMailer = void 0;
4
+ const resend_1 = require("resend");
5
+ class ResendMailer {
6
+ constructor(options) {
7
+ this.client = new resend_1.Resend(options.apiKey);
8
+ this.defaultFrom = options.from;
9
+ }
10
+ async send(params) {
11
+ const to = Array.isArray(params.to) ? params.to : [params.to];
12
+ const from = params.from ?? this.defaultFrom;
13
+ const payload = typeof params.body === 'string' ? { html: params.body } : { react: params.body };
14
+ const { data, error } = await this.client.emails.send({
15
+ from,
16
+ to,
17
+ subject: params.subject,
18
+ replyTo: params.replyTo,
19
+ ...payload,
20
+ }, params.idempotencyKey ? { idempotencyKey: params.idempotencyKey } : undefined);
21
+ if (error) {
22
+ const err = error instanceof Error
23
+ ? error
24
+ : new Error(typeof error.message === 'string'
25
+ ? error.message
26
+ : String(error));
27
+ return { success: false, error: err };
28
+ }
29
+ return { success: true, id: data?.id ?? undefined };
30
+ }
31
+ }
32
+ exports.ResendMailer = ResendMailer;
33
+ //# sourceMappingURL=resend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resend.js","sourceRoot":"","sources":["../src/resend.ts"],"names":[],"mappings":";;;AAAA,mCAAgC;AAQhC,MAAa,YAAY;IAIrB,YAAY,OAA4B;QACpC,IAAI,CAAC,MAAM,GAAG,IAAI,eAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAkB;QACzB,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC9D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,CAAC;QAC7C,MAAM,OAAO,GAAG,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;QAEjG,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CACjD;YACI,IAAI;YACJ,EAAE;YACF,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,GAAG,OAAO;SACb,EACD,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS,CAChF,CAAC;QAEF,IAAI,KAAK,EAAE,CAAC;YACR,MAAM,GAAG,GACL,KAAK,YAAY,KAAK;gBAClB,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,IAAI,KAAK,CACL,OAAQ,KAA8B,CAAC,OAAO,KAAK,QAAQ;oBACvD,CAAC,CAAE,KAA6B,CAAC,OAAO;oBACxC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACtB,CAAC;YACZ,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;QAC1C,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,SAAS,EAAE,CAAC;IACxD,CAAC;CACJ;AAtCD,oCAsCC"}
@@ -0,0 +1,11 @@
1
+ import type { Mailer } from './index';
2
+ export interface RateLimitOptions {
3
+ /** Max number of sends per minute. When exceeded, send() returns failure. */
4
+ maxPerMinute: number;
5
+ }
6
+ /**
7
+ * In-memory sliding-window rate limiter. When over limit, returns
8
+ * { success: false, error: Error('Rate limit exceeded') } without calling the inner mailer.
9
+ */
10
+ export declare function withRateLimit(mailer: Mailer, options: RateLimitOptions): Mailer;
11
+ //# sourceMappingURL=with-rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-rate-limit.d.ts","sourceRoot":"","sources":["../src/with-rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAA0B,MAAM,SAAS,CAAC;AAE9D,MAAM,WAAW,gBAAgB;IAC7B,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAyB/E"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withRateLimit = withRateLimit;
4
+ /**
5
+ * In-memory sliding-window rate limiter. When over limit, returns
6
+ * { success: false, error: Error('Rate limit exceeded') } without calling the inner mailer.
7
+ */
8
+ function withRateLimit(mailer, options) {
9
+ const { maxPerMinute } = options;
10
+ const timestamps = [];
11
+ function pruneOlderThanOneMinute(now) {
12
+ const cutoff = now - 60000;
13
+ while (timestamps.length > 0 && timestamps[0] < cutoff) {
14
+ timestamps.shift();
15
+ }
16
+ }
17
+ return {
18
+ async send(params) {
19
+ const now = Date.now();
20
+ pruneOlderThanOneMinute(now);
21
+ if (timestamps.length >= maxPerMinute) {
22
+ return {
23
+ success: false,
24
+ error: new Error('Mailer rate limit exceeded'),
25
+ };
26
+ }
27
+ timestamps.push(now);
28
+ return mailer.send(params);
29
+ },
30
+ };
31
+ }
32
+ //# sourceMappingURL=with-rate-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-rate-limit.js","sourceRoot":"","sources":["../src/with-rate-limit.ts"],"names":[],"mappings":";;AAWA,sCAyBC;AA7BD;;;GAGG;AACH,SAAgB,aAAa,CAAC,MAAc,EAAE,OAAyB;IACnE,MAAM,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;IACjC,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,SAAS,uBAAuB,CAAC,GAAW;QACxC,MAAM,MAAM,GAAG,GAAG,GAAG,KAAM,CAAC;QAC5B,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,CAAC,CAAE,GAAG,MAAM,EAAE,CAAC;YACtD,UAAU,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;IACL,CAAC;IAED,OAAO;QACH,KAAK,CAAC,IAAI,CAAC,MAAkB;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,uBAAuB,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,UAAU,CAAC,MAAM,IAAI,YAAY,EAAE,CAAC;gBACpC,OAAO;oBACH,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,IAAI,KAAK,CAAC,4BAA4B,CAAC;iBACjD,CAAC;YACN,CAAC;YACD,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACrB,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/B,CAAC;KACJ,CAAC;AACN,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Mailer, SendParams } from './index';
2
+ export interface RetryOptions {
3
+ /** Max send attempts including the first. Default 3. */
4
+ maxAttempts?: number;
5
+ /** Delay in ms before each retry. Default 1000. */
6
+ delayMs?: number;
7
+ /** Called after final failure; no persistence in this layer. */
8
+ onDeadLetter?(params: SendParams, error: Error): void | Promise<void>;
9
+ }
10
+ export declare function withRetry(mailer: Mailer, options?: RetryOptions): Mailer;
11
+ //# sourceMappingURL=with-retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-retry.d.ts","sourceRoot":"","sources":["../src/with-retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAc,MAAM,SAAS,CAAC;AAE9D,MAAM,WAAW,YAAY;IACzB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,YAAY,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzE;AAMD,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,CAoBxE"}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withRetry = withRetry;
4
+ function sleep(ms) {
5
+ return new Promise((resolve) => setTimeout(resolve, ms));
6
+ }
7
+ function withRetry(mailer, options) {
8
+ const maxAttempts = options?.maxAttempts ?? 3;
9
+ const delayMs = options?.delayMs ?? 1000;
10
+ const onDeadLetter = options?.onDeadLetter;
11
+ return {
12
+ async send(params) {
13
+ let lastResult = null;
14
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
15
+ lastResult = await mailer.send(params);
16
+ if (lastResult.success)
17
+ return lastResult;
18
+ if (attempt < maxAttempts)
19
+ await sleep(delayMs);
20
+ }
21
+ const result = lastResult;
22
+ if (onDeadLetter) {
23
+ await Promise.resolve(onDeadLetter(params, result.error));
24
+ }
25
+ return result;
26
+ },
27
+ };
28
+ }
29
+ //# sourceMappingURL=with-retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-retry.js","sourceRoot":"","sources":["../src/with-retry.ts"],"names":[],"mappings":";;AAeA,8BAoBC;AAxBD,SAAS,KAAK,CAAC,EAAU;IACrB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,SAAgB,SAAS,CAAC,MAAc,EAAE,OAAsB;IAC5D,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,IAAI,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC;IACzC,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,CAAC;IAE3C,OAAO;QACH,KAAK,CAAC,IAAI,CAAC,MAAkB;YACzB,IAAI,UAAU,GAAsB,IAAI,CAAC;YACzC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;gBACtD,UAAU,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACvC,IAAI,UAAU,CAAC,OAAO;oBAAE,OAAO,UAAU,CAAC;gBAC1C,IAAI,OAAO,GAAG,WAAW;oBAAE,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;YACpD,CAAC;YACD,MAAM,MAAM,GAAG,UAA+C,CAAC;YAC/D,IAAI,YAAY,EAAE,CAAC;gBACf,MAAM,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAC9D,CAAC;YACD,OAAO,MAAM,CAAC;QAClB,CAAC;KACJ,CAAC;AACN,CAAC"}
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@basedest/mailer",
3
+ "version": "0.0.0-development",
4
+ "description": "Mailer abstraction with Resend, retry, and rate-limiting support",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/basedest/mailer.git"
19
+ },
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "type-check": "tsc --noEmit",
23
+ "lint": "eslint .",
24
+ "lint:fix": "eslint . --fix",
25
+ "prepare": "husky",
26
+ "semantic-release": "semantic-release"
27
+ },
28
+ "lint-staged": {
29
+ "*.{json,md}": [
30
+ "prettier --write"
31
+ ],
32
+ "*.ts": [
33
+ "eslint --fix"
34
+ ]
35
+ },
36
+ "keywords": [],
37
+ "author": "basedest <basedest@icloud.com>",
38
+ "license": "ISC",
39
+ "packageManager": "pnpm@10.28.2",
40
+ "pnpm": {
41
+ "overrides": {
42
+ "minimatch": ">=10.2.1"
43
+ }
44
+ },
45
+ "publishConfig": {
46
+ "access": "public",
47
+ "provenance": true
48
+ },
49
+ "dependencies": {
50
+ "resend": "^6.9.1"
51
+ },
52
+ "peerDependencies": {
53
+ "react": ">=18.0.0"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "react": {
57
+ "optional": true
58
+ }
59
+ },
60
+ "devDependencies": {
61
+ "@commitlint/cli": "^20.4.0",
62
+ "@commitlint/config-conventional": "^20.4.0",
63
+ "@semantic-release/commit-analyzer": "^13.0.1",
64
+ "@semantic-release/git": "^10.0.1",
65
+ "@semantic-release/github": "^12.0.3",
66
+ "@semantic-release/npm": "^12.0.1",
67
+ "@semantic-release/release-notes-generator": "^14.1.0",
68
+ "@eslint/js": "^9.39.2",
69
+ "@types/node": "^22.0.0",
70
+ "@types/react": "^19.0.0",
71
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
72
+ "@typescript-eslint/parser": "^8.54.0",
73
+ "eslint": "^9.39.2",
74
+ "eslint-config-prettier": "^10.1.8",
75
+ "eslint-plugin-prettier": "^5.5.0",
76
+ "husky": "^9.0.0",
77
+ "lint-staged": "^16.2.7",
78
+ "prettier": "^3.8.1",
79
+ "semantic-release": "^25.0.3",
80
+ "typescript": "^5.9.3"
81
+ }
82
+ }