@fluojs/email 1.0.0-beta.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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.ko.md +325 -0
  3. package/README.md +325 -0
  4. package/dist/channel.d.ts +24 -0
  5. package/dist/channel.d.ts.map +1 -0
  6. package/dist/channel.js +64 -0
  7. package/dist/constants.d.ts +6 -0
  8. package/dist/constants.d.ts.map +1 -0
  9. package/dist/constants.js +17 -0
  10. package/dist/errors.d.ts +13 -0
  11. package/dist/errors.d.ts.map +1 -0
  12. package/dist/errors.js +19 -0
  13. package/dist/index.d.ts +9 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +6 -0
  16. package/dist/module.d.ts +45 -0
  17. package/dist/module.d.ts.map +1 -0
  18. package/dist/module.js +151 -0
  19. package/dist/node/node.d.ts +2 -0
  20. package/dist/node/node.d.ts.map +1 -0
  21. package/dist/node/node.js +1 -0
  22. package/dist/node/nodemailer.d.ts +104 -0
  23. package/dist/node/nodemailer.d.ts.map +1 -0
  24. package/dist/node/nodemailer.js +166 -0
  25. package/dist/node.d.ts +2 -0
  26. package/dist/node.d.ts.map +1 -0
  27. package/dist/node.js +1 -0
  28. package/dist/queue-entry.d.ts +4 -0
  29. package/dist/queue-entry.d.ts.map +1 -0
  30. package/dist/queue-entry.js +2 -0
  31. package/dist/queue.d.ts +38 -0
  32. package/dist/queue.d.ts.map +1 -0
  33. package/dist/queue.js +66 -0
  34. package/dist/service.d.ts +81 -0
  35. package/dist/service.d.ts.map +1 -0
  36. package/dist/service.js +275 -0
  37. package/dist/status.d.ts +28 -0
  38. package/dist/status.d.ts.map +1 -0
  39. package/dist/status.js +83 -0
  40. package/dist/tokens.d.ts +10 -0
  41. package/dist/tokens.d.ts.map +1 -0
  42. package/dist/tokens.js +6 -0
  43. package/dist/types.d.ts +242 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +1 -0
  46. package/package.json +84 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-entry.d.ts","sourceRoot":"","sources":["../src/queue-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kCAAkC,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,oCAAoC,EAAE,MAAM,YAAY,CAAC;AAClE,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { DEFAULT_EMAIL_QUEUE_WORKER_OPTIONS } from './constants.js';
2
+ export { createEmailNotificationsQueueAdapter } from './queue.js';
@@ -0,0 +1,38 @@
1
+ import type { NotificationsQueueAdapter } from '@fluojs/notifications';
2
+ import { QueueLifecycleService } from '@fluojs/queue';
3
+ import { EmailService } from './service.js';
4
+ import type { EmailNotificationDispatchRequest } from './types.js';
5
+ /** Serialized queue payload used by the built-in notifications queue adapter. */
6
+ export declare class EmailNotificationQueueJob {
7
+ readonly notification: EmailNotificationDispatchRequest;
8
+ readonly queuedAt: string;
9
+ constructor(notification: EmailNotificationDispatchRequest, queuedAt: string);
10
+ }
11
+ /**
12
+ * Creates a notifications queue adapter backed by {@link QueueLifecycleService}.
13
+ *
14
+ * @param queue Queue lifecycle service used to enqueue email notification jobs.
15
+ * @returns A queue adapter compatible with `NotificationsModule.forRoot(...)` queue wiring.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * NotificationsModule.forRootAsync({
20
+ * inject: [EMAIL_CHANNEL, QueueLifecycleService],
21
+ * useFactory: (channel, queue) => ({
22
+ * channels: [channel],
23
+ * queue: {
24
+ * adapter: createEmailNotificationsQueueAdapter(queue),
25
+ * bulkThreshold: 25,
26
+ * },
27
+ * }),
28
+ * });
29
+ * ```
30
+ */
31
+ export declare function createEmailNotificationsQueueAdapter(queue: QueueLifecycleService): NotificationsQueueAdapter;
32
+ /** Internal queue worker that converts queued notification jobs back into email delivery. */
33
+ export declare class EmailNotificationsQueueWorker {
34
+ private readonly email;
35
+ constructor(email: EmailService);
36
+ handle(job: EmailNotificationQueueJob): Promise<void>;
37
+ }
38
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAyB,MAAM,uBAAuB,CAAC;AAC9F,OAAO,EAAE,qBAAqB,EAAe,MAAM,eAAe,CAAC;AAGnE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,YAAY,CAAC;AAEnE,iFAAiF;AACjF,qBAAa,yBAAyB;aAElB,YAAY,EAAE,gCAAgC;aAC9C,QAAQ,EAAE,MAAM;gBADhB,YAAY,EAAE,gCAAgC,EAC9C,QAAQ,EAAE,MAAM;CAEnC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oCAAoC,CAAC,KAAK,EAAE,qBAAqB,GAAG,yBAAyB,CAW5G;AAED,6FAA6F;AAC7F,qBAEa,6BAA6B;IAC5B,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,YAAY;IAE1C,MAAM,CAAC,GAAG,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;CAG5D"}
package/dist/queue.js ADDED
@@ -0,0 +1,66 @@
1
+ let _initClass;
2
+ function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
3
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
4
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
5
+ function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
6
+ function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
7
+ import { Inject } from '@fluojs/core';
8
+ import { QueueWorker } from '@fluojs/queue';
9
+ import { DEFAULT_EMAIL_QUEUE_WORKER_OPTIONS } from './constants.js';
10
+ import { EmailService } from './service.js';
11
+ /** Serialized queue payload used by the built-in notifications queue adapter. */
12
+ export class EmailNotificationQueueJob {
13
+ constructor(notification, queuedAt) {
14
+ this.notification = notification;
15
+ this.queuedAt = queuedAt;
16
+ }
17
+ }
18
+
19
+ /**
20
+ * Creates a notifications queue adapter backed by {@link QueueLifecycleService}.
21
+ *
22
+ * @param queue Queue lifecycle service used to enqueue email notification jobs.
23
+ * @returns A queue adapter compatible with `NotificationsModule.forRoot(...)` queue wiring.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * NotificationsModule.forRootAsync({
28
+ * inject: [EMAIL_CHANNEL, QueueLifecycleService],
29
+ * useFactory: (channel, queue) => ({
30
+ * channels: [channel],
31
+ * queue: {
32
+ * adapter: createEmailNotificationsQueueAdapter(queue),
33
+ * bulkThreshold: 25,
34
+ * },
35
+ * }),
36
+ * });
37
+ * ```
38
+ */
39
+ export function createEmailNotificationsQueueAdapter(queue) {
40
+ return {
41
+ enqueue(job) {
42
+ return queue.enqueue(new EmailNotificationQueueJob(job.notification, job.queuedAt));
43
+ },
44
+ enqueueMany(jobs) {
45
+ return Promise.all(jobs.map(job => queue.enqueue(new EmailNotificationQueueJob(job.notification, job.queuedAt))));
46
+ }
47
+ };
48
+ }
49
+
50
+ /** Internal queue worker that converts queued notification jobs back into email delivery. */
51
+ let _EmailNotificationsQu;
52
+ class EmailNotificationsQueueWorker {
53
+ static {
54
+ [_EmailNotificationsQu, _initClass] = _applyDecs(this, [QueueWorker(EmailNotificationQueueJob, DEFAULT_EMAIL_QUEUE_WORKER_OPTIONS), Inject(EmailService)], []).c;
55
+ }
56
+ constructor(email) {
57
+ this.email = email;
58
+ }
59
+ async handle(job) {
60
+ await this.email.sendNotification(job.notification);
61
+ }
62
+ static {
63
+ _initClass();
64
+ }
65
+ }
66
+ export { _EmailNotificationsQu as EmailNotificationsQueueWorker };
@@ -0,0 +1,81 @@
1
+ import type { OnApplicationShutdown, OnModuleInit } from '@fluojs/runtime';
2
+ import type { Email, EmailMessage, EmailNotificationDispatchRequest, EmailSendBatchResult, EmailSendManyOptions, EmailSendOptions, EmailSendResult, NormalizedEmailModuleOptions } from './types.js';
3
+ /**
4
+ * Injectable email delivery service for standalone and notifications-backed usage.
5
+ *
6
+ * @remarks
7
+ * The service stays transport-agnostic at the shared package boundary, consumes only
8
+ * explicitly injected {@link EmailTransport} contracts, and translates
9
+ * `@fluojs/notifications` envelopes into concrete email messages.
10
+ */
11
+ export declare class EmailService implements Email, OnModuleInit, OnApplicationShutdown {
12
+ private readonly options;
13
+ private lifecycleState;
14
+ private resolvedTransport;
15
+ private transportPromise;
16
+ constructor(options: NormalizedEmailModuleOptions);
17
+ onApplicationShutdown(): Promise<void>;
18
+ onModuleInit(): Promise<void>;
19
+ /**
20
+ * Creates a platform status snapshot for the active email transport wiring.
21
+ *
22
+ * @returns A structured snapshot describing lifecycle state, resource ownership, and notifications integration details.
23
+ */
24
+ createPlatformStatusSnapshot(): import("./status.js").EmailPlatformStatusSnapshot;
25
+ /**
26
+ * Sends one email message directly through the configured transport.
27
+ *
28
+ * @param message Caller-supplied email message with addresses, subject, and body content.
29
+ * @param options Optional abort signal propagated to the transport.
30
+ * @returns A normalized delivery receipt describing accepted/rejected recipients and the message id.
31
+ * @throws {EmailMessageValidationError} When the resolved message is missing required sender, recipient, or content fields.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * await email.send({
36
+ * from: 'noreply@example.com',
37
+ * to: ['user@example.com'],
38
+ * subject: 'Welcome',
39
+ * text: 'Hello from Fluo',
40
+ * });
41
+ * ```
42
+ */
43
+ send(message: EmailMessage, options?: EmailSendOptions): Promise<EmailSendResult>;
44
+ /**
45
+ * Sends multiple email messages in input order with optional tolerant failure handling.
46
+ *
47
+ * @param messages Ordered message list to deliver through the configured transport.
48
+ * @param options Optional tolerant batch controls such as `continueOnError`.
49
+ * @returns A batch summary containing successes and any captured failures.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * const result = await email.sendMany(messages, { continueOnError: true });
54
+ * console.log(result.succeeded, result.failed);
55
+ * ```
56
+ */
57
+ sendMany(messages: readonly EmailMessage[], options?: EmailSendManyOptions): Promise<EmailSendBatchResult>;
58
+ /**
59
+ * Converts one notifications foundation request into a concrete email delivery.
60
+ *
61
+ * @param notification Shared notification envelope interpreted by the email package.
62
+ * @param options Optional abort signal propagated to rendering and transport work.
63
+ * @returns A normalized delivery receipt for the resulting email message.
64
+ * @throws {EmailMessageValidationError} When the notification cannot resolve recipients or content.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * await email.sendNotification({
69
+ * channel: 'email',
70
+ * recipients: ['user@example.com'],
71
+ * subject: 'Digest',
72
+ * payload: { text: 'Your weekly update' },
73
+ * });
74
+ * ```
75
+ */
76
+ sendNotification(notification: EmailNotificationDispatchRequest, options?: EmailSendOptions): Promise<EmailSendResult>;
77
+ private ensureTransport;
78
+ private normalizeMessage;
79
+ private renderNotification;
80
+ }
81
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAM3E,OAAO,KAAK,EACV,KAAK,EAGL,YAAY,EACZ,gCAAgC,EAC9B,oBAAoB,EAEpB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EAKf,4BAA4B,EAC7B,MAAM,YAAY,CAAC;AA2CtB;;;;;;;GAOG;AACH,qBACa,YAAa,YAAW,KAAK,EAAE,YAAY,EAAE,qBAAqB;IAKjE,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,OAAO,CAAC,cAAc,CAAmF;IACzG,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,gBAAgB,CAAsC;gBAEjC,OAAO,EAAE,4BAA4B;IAE5D,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAetC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBnC;;;;OAIG;IACH,4BAA4B;IAY5B;;;;;;;;;;;;;;;;;OAiBG;IACG,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC;IAoB3F;;;;;;;;;;;;OAYG;IACG,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA6BpH;;;;;;;;;;;;;;;;;OAiBG;IACG,gBAAgB,CACpB,YAAY,EAAE,gCAAgC,EAC9C,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC;YA0Bb,eAAe;IAe7B,OAAO,CAAC,gBAAgB;YAuBV,kBAAkB;CAejC"}
@@ -0,0 +1,275 @@
1
+ let _initClass;
2
+ function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
3
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
4
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
5
+ function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
6
+ function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
7
+ import { Inject } from '@fluojs/core';
8
+ import { DEFAULT_EMAIL_QUEUE_WORKER_OPTIONS } from './constants.js';
9
+ import { EmailMessageValidationError } from './errors.js';
10
+ import { createEmailPlatformStatusSnapshot } from './status.js';
11
+ import { EMAIL_OPTIONS } from './tokens.js';
12
+ function normalizeAddress(address) {
13
+ if (typeof address === 'string') {
14
+ const trimmed = address.trim();
15
+ return {
16
+ address: trimmed
17
+ };
18
+ }
19
+ return {
20
+ address: address.address.trim(),
21
+ ...(address.name ? {
22
+ name: address.name
23
+ } : {})
24
+ };
25
+ }
26
+ function normalizeAddressList(value) {
27
+ if (!value) {
28
+ return [];
29
+ }
30
+ const list = Array.isArray(value) ? value : [value];
31
+ return list.map(entry => normalizeAddress(entry));
32
+ }
33
+ function createAbortError() {
34
+ const error = new Error('Email delivery was aborted.');
35
+ error.name = 'AbortError';
36
+ return error;
37
+ }
38
+ function assertMessageContent(message) {
39
+ if (message.to.length === 0) {
40
+ throw new EmailMessageValidationError('Email messages require at least one recipient in `to`.');
41
+ }
42
+ if (!message.from.address) {
43
+ throw new EmailMessageValidationError('Email messages require a resolved `from` address.');
44
+ }
45
+ if (!message.html && !message.text) {
46
+ throw new EmailMessageValidationError('Email messages require `html` or `text` content.');
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Injectable email delivery service for standalone and notifications-backed usage.
52
+ *
53
+ * @remarks
54
+ * The service stays transport-agnostic at the shared package boundary, consumes only
55
+ * explicitly injected {@link EmailTransport} contracts, and translates
56
+ * `@fluojs/notifications` envelopes into concrete email messages.
57
+ */
58
+ let _EmailService;
59
+ class EmailService {
60
+ static {
61
+ [_EmailService, _initClass] = _applyDecs(this, [Inject(EMAIL_OPTIONS)], []).c;
62
+ }
63
+ lifecycleState = 'created';
64
+ resolvedTransport;
65
+ transportPromise;
66
+ constructor(options) {
67
+ this.options = options;
68
+ }
69
+ async onApplicationShutdown() {
70
+ this.lifecycleState = 'stopping';
71
+ try {
72
+ if (this.resolvedTransport && this.options.transport.ownsResources && this.resolvedTransport.close) {
73
+ await this.resolvedTransport.close();
74
+ }
75
+ this.lifecycleState = 'stopped';
76
+ } catch {
77
+ this.lifecycleState = 'failed';
78
+ throw new Error('Email transport failed to close cleanly.');
79
+ }
80
+ }
81
+ async onModuleInit() {
82
+ this.lifecycleState = 'starting';
83
+ try {
84
+ const transport = await this.ensureTransport();
85
+ if (this.options.verifyOnModuleInit && transport.verify) {
86
+ await transport.verify();
87
+ }
88
+ this.lifecycleState = 'ready';
89
+ } catch {
90
+ this.lifecycleState = 'failed';
91
+ throw new Error('Email transport failed to initialize.');
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Creates a platform status snapshot for the active email transport wiring.
97
+ *
98
+ * @returns A structured snapshot describing lifecycle state, resource ownership, and notifications integration details.
99
+ */
100
+ createPlatformStatusSnapshot() {
101
+ return createEmailPlatformStatusSnapshot({
102
+ channelName: this.options.notifications.channel,
103
+ defaultFromConfigured: this.options.defaultFrom !== undefined,
104
+ lifecycleState: this.lifecycleState,
105
+ ownsTransportResources: this.options.transport.ownsResources,
106
+ queueWorkerJobName: DEFAULT_EMAIL_QUEUE_WORKER_OPTIONS.jobName,
107
+ transportKind: this.options.transport.kind,
108
+ verifiedOnModuleInit: this.options.verifyOnModuleInit
109
+ });
110
+ }
111
+
112
+ /**
113
+ * Sends one email message directly through the configured transport.
114
+ *
115
+ * @param message Caller-supplied email message with addresses, subject, and body content.
116
+ * @param options Optional abort signal propagated to the transport.
117
+ * @returns A normalized delivery receipt describing accepted/rejected recipients and the message id.
118
+ * @throws {EmailMessageValidationError} When the resolved message is missing required sender, recipient, or content fields.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * await email.send({
123
+ * from: 'noreply@example.com',
124
+ * to: ['user@example.com'],
125
+ * subject: 'Welcome',
126
+ * text: 'Hello from Fluo',
127
+ * });
128
+ * ```
129
+ */
130
+ async send(message, options = {}) {
131
+ if (options.signal?.aborted) {
132
+ throw createAbortError();
133
+ }
134
+ const transport = await this.ensureTransport();
135
+ const normalized = this.normalizeMessage(message);
136
+ assertMessageContent(normalized);
137
+ const result = await transport.send(normalized, options);
138
+ return {
139
+ accepted: result.accepted ?? [],
140
+ messageId: result.messageId ?? '',
141
+ metadata: result.metadata,
142
+ pending: result.pending ?? [],
143
+ rejected: result.rejected ?? [],
144
+ response: result.response
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Sends multiple email messages in input order with optional tolerant failure handling.
150
+ *
151
+ * @param messages Ordered message list to deliver through the configured transport.
152
+ * @param options Optional tolerant batch controls such as `continueOnError`.
153
+ * @returns A batch summary containing successes and any captured failures.
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * const result = await email.sendMany(messages, { continueOnError: true });
158
+ * console.log(result.succeeded, result.failed);
159
+ * ```
160
+ */
161
+ async sendMany(messages, options = {}) {
162
+ const results = [];
163
+ const failures = [];
164
+ for (const message of messages) {
165
+ try {
166
+ results.push(await this.send(message, options));
167
+ } catch (error) {
168
+ const failure = {
169
+ error: error instanceof Error ? error : new Error('Email delivery failed.'),
170
+ message
171
+ };
172
+ if (!(options.continueOnError ?? false)) {
173
+ throw failure.error;
174
+ }
175
+ failures.push(failure);
176
+ }
177
+ }
178
+ return {
179
+ failed: failures.length,
180
+ failures,
181
+ results,
182
+ succeeded: results.length
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Converts one notifications foundation request into a concrete email delivery.
188
+ *
189
+ * @param notification Shared notification envelope interpreted by the email package.
190
+ * @param options Optional abort signal propagated to rendering and transport work.
191
+ * @returns A normalized delivery receipt for the resulting email message.
192
+ * @throws {EmailMessageValidationError} When the notification cannot resolve recipients or content.
193
+ *
194
+ * @example
195
+ * ```ts
196
+ * await email.sendNotification({
197
+ * channel: 'email',
198
+ * recipients: ['user@example.com'],
199
+ * subject: 'Digest',
200
+ * payload: { text: 'Your weekly update' },
201
+ * });
202
+ * ```
203
+ */
204
+ async sendNotification(notification, options = {}) {
205
+ const payload = notification.payload;
206
+ const rendered = await this.renderNotification(notification);
207
+ return this.send({
208
+ attachments: payload.attachments,
209
+ bcc: payload.bcc,
210
+ cc: payload.cc,
211
+ from: payload.from,
212
+ headers: payload.headers,
213
+ html: payload.html ?? rendered?.html,
214
+ metadata: {
215
+ ...(payload.metadata ?? {}),
216
+ ...(notification.metadata ?? {}),
217
+ ...(notification.template ? {
218
+ template: notification.template
219
+ } : {})
220
+ },
221
+ replyTo: payload.replyTo,
222
+ subject: notification.subject ?? rendered?.subject,
223
+ text: payload.text ?? rendered?.text,
224
+ to: payload.to ?? notification.recipients ?? []
225
+ }, options);
226
+ }
227
+ async ensureTransport() {
228
+ if (this.resolvedTransport) {
229
+ return this.resolvedTransport;
230
+ }
231
+ if (!this.transportPromise) {
232
+ this.transportPromise = this.options.transport.create().then(transport => {
233
+ this.resolvedTransport = transport;
234
+ return transport;
235
+ });
236
+ }
237
+ return this.transportPromise;
238
+ }
239
+ normalizeMessage(message) {
240
+ const from = message.from ? normalizeAddress(message.from) : this.options.defaultFrom;
241
+ const replyTo = normalizeAddressList(message.replyTo);
242
+ if (!from) {
243
+ throw new EmailMessageValidationError('Email messages require `from` or `defaultFrom` to be configured.');
244
+ }
245
+ return {
246
+ attachments: message.attachments,
247
+ bcc: normalizeAddressList(message.bcc),
248
+ cc: normalizeAddressList(message.cc),
249
+ from,
250
+ headers: message.headers,
251
+ html: message.html,
252
+ metadata: message.metadata,
253
+ replyTo: replyTo.length > 0 ? replyTo : this.options.defaultReplyTo,
254
+ subject: message.subject,
255
+ text: message.text,
256
+ to: normalizeAddressList(message.to)
257
+ };
258
+ }
259
+ async renderNotification(notification) {
260
+ if (!notification.template || !this.options.renderer) {
261
+ return undefined;
262
+ }
263
+ return this.options.renderer.render({
264
+ locale: notification.locale,
265
+ metadata: notification.metadata,
266
+ payload: notification.payload,
267
+ subject: notification.subject,
268
+ template: notification.template
269
+ });
270
+ }
271
+ static {
272
+ _initClass();
273
+ }
274
+ }
275
+ export { _EmailService as EmailService };
@@ -0,0 +1,28 @@
1
+ import type { PlatformHealthReport, PlatformReadinessReport, PlatformSnapshot } from '@fluojs/runtime';
2
+ /** Resolved email lifecycle state used for diagnostics and health checks. */
3
+ export type EmailLifecycleState = 'created' | 'starting' | 'ready' | 'stopping' | 'stopped' | 'failed';
4
+ /** Input required to describe the package health/readiness contract. */
5
+ export interface EmailStatusAdapterInput {
6
+ channelName: string;
7
+ defaultFromConfigured: boolean;
8
+ lifecycleState: EmailLifecycleState;
9
+ ownsTransportResources: boolean;
10
+ queueWorkerJobName: string;
11
+ transportKind: string;
12
+ verifiedOnModuleInit: boolean;
13
+ }
14
+ /** Structured snapshot returned by {@link createEmailPlatformStatusSnapshot}. */
15
+ export interface EmailPlatformStatusSnapshot {
16
+ details: Record<string, unknown>;
17
+ health: PlatformHealthReport;
18
+ ownership: PlatformSnapshot['ownership'];
19
+ readiness: PlatformReadinessReport;
20
+ }
21
+ /**
22
+ * Creates a health/readiness snapshot for the email delivery layer.
23
+ *
24
+ * @param input Lifecycle and ownership details derived from the active email module wiring.
25
+ * @returns A structured snapshot suitable for status endpoints and operational diagnostics.
26
+ */
27
+ export declare function createEmailPlatformStatusSnapshot(input: EmailStatusAdapterInput): EmailPlatformStatusSnapshot;
28
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEvG,6EAA6E;AAC7E,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEvG,wEAAwE;AACxE,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,cAAc,EAAE,mBAAmB,CAAC;IACpC,sBAAsB,EAAE,OAAO,CAAC;IAChC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,OAAO,CAAC;CAC/B;AAED,iFAAiF;AACjF,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACzC,SAAS,EAAE,uBAAuB,CAAC;CACpC;AA6DD;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,uBAAuB,GAAG,2BAA2B,CAkB7G"}
package/dist/status.js ADDED
@@ -0,0 +1,83 @@
1
+ /** Resolved email lifecycle state used for diagnostics and health checks. */
2
+
3
+ /** Input required to describe the package health/readiness contract. */
4
+
5
+ /** Structured snapshot returned by {@link createEmailPlatformStatusSnapshot}. */
6
+
7
+ function createReadiness(input) {
8
+ if (input.lifecycleState === 'ready') {
9
+ return {
10
+ critical: true,
11
+ status: 'ready'
12
+ };
13
+ }
14
+ if (input.lifecycleState === 'starting') {
15
+ return {
16
+ critical: true,
17
+ reason: 'Email transport is still starting.',
18
+ status: 'degraded'
19
+ };
20
+ }
21
+ if (input.lifecycleState === 'stopping' || input.lifecycleState === 'stopped') {
22
+ return {
23
+ critical: true,
24
+ reason: 'Email transport is shutting down or already stopped.',
25
+ status: 'not-ready'
26
+ };
27
+ }
28
+ if (input.lifecycleState === 'failed') {
29
+ return {
30
+ critical: true,
31
+ reason: 'Email transport failed to initialize.',
32
+ status: 'not-ready'
33
+ };
34
+ }
35
+ return {
36
+ critical: true,
37
+ reason: 'Email transport has not started yet.',
38
+ status: 'not-ready'
39
+ };
40
+ }
41
+ function createHealth(input) {
42
+ if (input.lifecycleState === 'failed' || input.lifecycleState === 'stopped') {
43
+ return {
44
+ reason: 'Email transport is unavailable.',
45
+ status: 'unhealthy'
46
+ };
47
+ }
48
+ if (input.lifecycleState === 'created' || input.lifecycleState === 'starting' || input.lifecycleState === 'stopping') {
49
+ return {
50
+ reason: 'Email transport is transitioning lifecycle state.',
51
+ status: 'degraded'
52
+ };
53
+ }
54
+ return {
55
+ status: 'healthy'
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Creates a health/readiness snapshot for the email delivery layer.
61
+ *
62
+ * @param input Lifecycle and ownership details derived from the active email module wiring.
63
+ * @returns A structured snapshot suitable for status endpoints and operational diagnostics.
64
+ */
65
+ export function createEmailPlatformStatusSnapshot(input) {
66
+ return {
67
+ details: {
68
+ channelName: input.channelName,
69
+ defaultFromConfigured: input.defaultFromConfigured,
70
+ dependencies: ['notifications.channel', 'email.transport'],
71
+ lifecycleState: input.lifecycleState,
72
+ queueWorkerJobName: input.queueWorkerJobName,
73
+ transportKind: input.transportKind,
74
+ verifiedOnModuleInit: input.verifiedOnModuleInit
75
+ },
76
+ health: createHealth(input),
77
+ ownership: {
78
+ externallyManaged: !input.ownsTransportResources,
79
+ ownsResources: input.ownsTransportResources
80
+ },
81
+ readiness: createReadiness(input)
82
+ };
83
+ }
@@ -0,0 +1,10 @@
1
+ import type { Token } from '@fluojs/core';
2
+ import type { NotificationChannel } from '@fluojs/notifications';
3
+ import type { Email, EmailNotificationDispatchRequest, NormalizedEmailModuleOptions } from './types.js';
4
+ /** Compatibility token for the facade returned by {@link EmailModule.forRoot}. */
5
+ export declare const EMAIL: Token<Email>;
6
+ /** Injection token for the channel implementation consumed by `@fluojs/notifications`. */
7
+ export declare const EMAIL_CHANNEL: Token<NotificationChannel<EmailNotificationDispatchRequest>>;
8
+ /** Injection token for normalized email module options consumed internally by providers. */
9
+ export declare const EMAIL_OPTIONS: Token<NormalizedEmailModuleOptions>;
10
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,OAAO,KAAK,EAAE,KAAK,EAAE,gCAAgC,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAExG,kFAAkF;AAClF,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK,CAA4B,CAAC;AAC5D,0FAA0F;AAC1F,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,mBAAmB,CAAC,gCAAgC,CAAC,CAAoC,CAAC;AAC5H,4FAA4F;AAC5F,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,4BAA4B,CAAoC,CAAC"}
package/dist/tokens.js ADDED
@@ -0,0 +1,6 @@
1
+ /** Compatibility token for the facade returned by {@link EmailModule.forRoot}. */
2
+ export const EMAIL = Symbol.for('fluo.email');
3
+ /** Injection token for the channel implementation consumed by `@fluojs/notifications`. */
4
+ export const EMAIL_CHANNEL = Symbol.for('fluo.email.channel');
5
+ /** Injection token for normalized email module options consumed internally by providers. */
6
+ export const EMAIL_OPTIONS = Symbol.for('fluo.email.options');