@techstream/quark-core 1.2.0 → 1.4.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.
package/src/email.test.js CHANGED
@@ -1,133 +1,146 @@
1
1
  import assert from "node:assert";
2
- import { mock, test } from "node:test";
2
+ import { test } from "node:test";
3
3
  import { createEmailService } from "./email.js";
4
4
 
5
5
  test("Email Service", async (t) => {
6
- await t.test("createEmailService returns object with sendEmail method", () => {
7
- const service = createEmailService();
8
- assert.strictEqual(typeof service, "object");
9
- assert.strictEqual(typeof service.sendEmail, "function");
10
- });
11
-
12
- await t.test("SMTP provider: uses Mailhog defaults when no SMTP_HOST set", async () => {
13
- // Clear any explicit SMTP env vars
14
- const origHost = process.env.SMTP_HOST;
15
- const origPort = process.env.SMTP_PORT;
16
- delete process.env.SMTP_HOST;
17
- delete process.env.SMTP_PORT;
18
-
19
- try {
20
- const service = createEmailService({ provider: "smtp" });
21
-
22
- // Mock nodemailer at the transport level
23
- let capturedConfig = null;
24
- const mockTransport = {
25
- sendMail: async (opts) => {
26
- return { messageId: "test-id-123", ...opts };
27
- },
28
- };
29
-
30
- // We test that sendEmail resolves without error
31
- // The transport is lazily created, so sendEmail triggers creation
32
- // We can't easily intercept the dynamic import, but we can verify
33
- // the service was created correctly
34
- assert.strictEqual(typeof service.sendEmail, "function");
35
- } finally {
36
- if (origHost !== undefined) process.env.SMTP_HOST = origHost;
37
- else delete process.env.SMTP_HOST;
38
- if (origPort !== undefined) process.env.SMTP_PORT = origPort;
39
- else delete process.env.SMTP_PORT;
40
- }
41
- });
42
-
43
- await t.test("SMTP provider: uses explicit SMTP_HOST/SMTP_PORT when set", () => {
44
- const origHost = process.env.SMTP_HOST;
45
- const origPort = process.env.SMTP_PORT;
46
- process.env.SMTP_HOST = "mail.example.com";
47
- process.env.SMTP_PORT = "587";
48
-
49
- try {
50
- const service = createEmailService({ provider: "smtp" });
6
+ await t.test(
7
+ "createEmailService returns object with sendEmail method",
8
+ () => {
9
+ const service = createEmailService();
10
+ assert.strictEqual(typeof service, "object");
51
11
  assert.strictEqual(typeof service.sendEmail, "function");
52
- } finally {
53
- if (origHost !== undefined) process.env.SMTP_HOST = origHost;
54
- else delete process.env.SMTP_HOST;
55
- if (origPort !== undefined) process.env.SMTP_PORT = origPort;
56
- else delete process.env.SMTP_PORT;
57
- }
58
- });
59
-
60
- await t.test("Resend provider: throws if RESEND_API_KEY missing", async () => {
61
- const origKey = process.env.RESEND_API_KEY;
62
- delete process.env.RESEND_API_KEY;
63
-
64
- try {
65
- const service = createEmailService({ provider: "resend" });
66
-
67
- await assert.rejects(
68
- () => service.sendEmail("test@example.com", "Subject", "<p>body</p>"),
69
- (err) =>
70
- err instanceof Error &&
71
- /RESEND_API_KEY/.test(err.message),
72
- );
73
- } finally {
74
- if (origKey !== undefined) process.env.RESEND_API_KEY = origKey;
75
- else delete process.env.RESEND_API_KEY;
76
- }
77
- });
78
-
79
- await t.test("Resend provider: calls fetch with correct URL, headers, and body", async () => {
80
- const origKey = process.env.RESEND_API_KEY;
81
- process.env.RESEND_API_KEY = "re_test_key_123";
82
-
83
- // Mock global fetch
84
- const originalFetch = globalThis.fetch;
85
- let capturedUrl = null;
86
- let capturedOptions = null;
87
-
88
- globalThis.fetch = async (url, opts) => {
89
- capturedUrl = url;
90
- capturedOptions = opts;
91
- return {
92
- ok: true,
93
- json: async () => ({ id: "resend-msg-id" }),
12
+ },
13
+ );
14
+
15
+ await t.test(
16
+ "SMTP provider: uses Mailhog defaults when no SMTP_HOST set",
17
+ async () => {
18
+ // Clear any explicit SMTP env vars
19
+ const origHost = process.env.SMTP_HOST;
20
+ const origPort = process.env.SMTP_PORT;
21
+ delete process.env.SMTP_HOST;
22
+ delete process.env.SMTP_PORT;
23
+
24
+ try {
25
+ const service = createEmailService({ provider: "smtp" });
26
+
27
+ // Mock nodemailer at the transport level
28
+ const _capturedConfig = null;
29
+ const _mockTransport = {
30
+ sendMail: async (opts) => {
31
+ return { messageId: "test-id-123", ...opts };
32
+ },
33
+ };
34
+
35
+ // We test that sendEmail resolves without error
36
+ // The transport is lazily created, so sendEmail triggers creation
37
+ // We can't easily intercept the dynamic import, but we can verify
38
+ // the service was created correctly
39
+ assert.strictEqual(typeof service.sendEmail, "function");
40
+ } finally {
41
+ if (origHost !== undefined) process.env.SMTP_HOST = origHost;
42
+ else delete process.env.SMTP_HOST;
43
+ if (origPort !== undefined) process.env.SMTP_PORT = origPort;
44
+ else delete process.env.SMTP_PORT;
45
+ }
46
+ },
47
+ );
48
+
49
+ await t.test(
50
+ "SMTP provider: uses explicit SMTP_HOST/SMTP_PORT when set",
51
+ () => {
52
+ const origHost = process.env.SMTP_HOST;
53
+ const origPort = process.env.SMTP_PORT;
54
+ process.env.SMTP_HOST = "mail.example.com";
55
+ process.env.SMTP_PORT = "587";
56
+
57
+ try {
58
+ const service = createEmailService({ provider: "smtp" });
59
+ assert.strictEqual(typeof service.sendEmail, "function");
60
+ } finally {
61
+ if (origHost !== undefined) process.env.SMTP_HOST = origHost;
62
+ else delete process.env.SMTP_HOST;
63
+ if (origPort !== undefined) process.env.SMTP_PORT = origPort;
64
+ else delete process.env.SMTP_PORT;
65
+ }
66
+ },
67
+ );
68
+
69
+ await t.test(
70
+ "Resend provider: throws if RESEND_API_KEY missing",
71
+ async () => {
72
+ const origKey = process.env.RESEND_API_KEY;
73
+ delete process.env.RESEND_API_KEY;
74
+
75
+ try {
76
+ const service = createEmailService({ provider: "resend" });
77
+
78
+ await assert.rejects(
79
+ () => service.sendEmail("test@example.com", "Subject", "<p>body</p>"),
80
+ (err) => err instanceof Error && /RESEND_API_KEY/.test(err.message),
81
+ );
82
+ } finally {
83
+ if (origKey !== undefined) process.env.RESEND_API_KEY = origKey;
84
+ else delete process.env.RESEND_API_KEY;
85
+ }
86
+ },
87
+ );
88
+
89
+ await t.test(
90
+ "Resend provider: calls fetch with correct URL, headers, and body",
91
+ async () => {
92
+ const origKey = process.env.RESEND_API_KEY;
93
+ process.env.RESEND_API_KEY = "re_test_key_123";
94
+
95
+ // Mock global fetch
96
+ const originalFetch = globalThis.fetch;
97
+ let capturedUrl = null;
98
+ let capturedOptions = null;
99
+
100
+ globalThis.fetch = async (url, opts) => {
101
+ capturedUrl = url;
102
+ capturedOptions = opts;
103
+ return {
104
+ ok: true,
105
+ json: async () => ({ id: "resend-msg-id" }),
106
+ };
94
107
  };
95
- };
96
-
97
- try {
98
- const service = createEmailService({
99
- provider: "resend",
100
- from: "Test <test@example.com>",
101
- });
102
-
103
- const result = await service.sendEmail(
104
- "recipient@example.com",
105
- "Test Subject",
106
- "<p>Hello</p>",
107
- "Hello",
108
- );
109
-
110
- assert.strictEqual(capturedUrl, "https://api.resend.com/emails");
111
- assert.strictEqual(capturedOptions.method, "POST");
112
-
113
- const headers = capturedOptions.headers;
114
- assert.strictEqual(headers.Authorization, "Bearer re_test_key_123");
115
- assert.strictEqual(headers["Content-Type"], "application/json");
116
108
 
117
- const body = JSON.parse(capturedOptions.body);
118
- assert.strictEqual(body.from, "Test <test@example.com>");
119
- assert.deepStrictEqual(body.to, ["recipient@example.com"]);
120
- assert.strictEqual(body.subject, "Test Subject");
121
- assert.strictEqual(body.html, "<p>Hello</p>");
122
- assert.strictEqual(body.text, "Hello");
123
-
124
- assert.strictEqual(result.id, "resend-msg-id");
125
- } finally {
126
- globalThis.fetch = originalFetch;
127
- if (origKey !== undefined) process.env.RESEND_API_KEY = origKey;
128
- else delete process.env.RESEND_API_KEY;
129
- }
130
- });
109
+ try {
110
+ const service = createEmailService({
111
+ provider: "resend",
112
+ from: "Test <test@example.com>",
113
+ });
114
+
115
+ const result = await service.sendEmail(
116
+ "recipient@example.com",
117
+ "Test Subject",
118
+ "<p>Hello</p>",
119
+ "Hello",
120
+ );
121
+
122
+ assert.strictEqual(capturedUrl, "https://api.resend.com/emails");
123
+ assert.strictEqual(capturedOptions.method, "POST");
124
+
125
+ const headers = capturedOptions.headers;
126
+ assert.strictEqual(headers.Authorization, "Bearer re_test_key_123");
127
+ assert.strictEqual(headers["Content-Type"], "application/json");
128
+
129
+ const body = JSON.parse(capturedOptions.body);
130
+ assert.strictEqual(body.from, "Test <test@example.com>");
131
+ assert.deepStrictEqual(body.to, ["recipient@example.com"]);
132
+ assert.strictEqual(body.subject, "Test Subject");
133
+ assert.strictEqual(body.html, "<p>Hello</p>");
134
+ assert.strictEqual(body.text, "Hello");
135
+
136
+ assert.strictEqual(result.id, "resend-msg-id");
137
+ } finally {
138
+ globalThis.fetch = originalFetch;
139
+ if (origKey !== undefined) process.env.RESEND_API_KEY = origKey;
140
+ else delete process.env.RESEND_API_KEY;
141
+ }
142
+ },
143
+ );
131
144
 
132
145
  await t.test("Resend provider: throws on non-ok response", async () => {
133
146
  const origKey = process.env.RESEND_API_KEY;
@@ -196,8 +209,7 @@ test("Email Service", async (t) => {
196
209
  // The service should be created without error (defaults to smtp)
197
210
  assert.strictEqual(typeof service.sendEmail, "function");
198
211
  } finally {
199
- if (origProvider !== undefined)
200
- process.env.EMAIL_PROVIDER = origProvider;
212
+ if (origProvider !== undefined) process.env.EMAIL_PROVIDER = origProvider;
201
213
  else delete process.env.EMAIL_PROVIDER;
202
214
  }
203
215
  });
@@ -209,7 +221,7 @@ test("Email Service", async (t) => {
209
221
  const originalFetch = globalThis.fetch;
210
222
  let capturedBody = null;
211
223
 
212
- globalThis.fetch = async (url, opts) => {
224
+ globalThis.fetch = async (_url, opts) => {
213
225
  capturedBody = JSON.parse(opts.body);
214
226
  return { ok: true, json: async () => ({ id: "msg-1" }) };
215
227
  };
package/src/index.js CHANGED
@@ -7,6 +7,10 @@ export * from "./authorization.js";
7
7
  export * from "./cache.js";
8
8
  // CSRF protection exports
9
9
  export * from "./csrf.js";
10
+ // Email service exports
11
+ export * from "./email.js";
12
+ // Email template exports
13
+ export * from "./email-templates.js";
10
14
  // Error Reporter exports
11
15
  export * from "./error-reporter.js";
12
16
  // Error exports
@@ -15,8 +19,6 @@ export * from "./errors.js";
15
19
  export * from "./logger.js";
16
20
  // Mailhog exports
17
21
  export * from "./mailhog.js";
18
- // Email service exports
19
- export * from "./email.js";
20
22
  // Queue exports
21
23
  export * from "./queue/index.js";
22
24
  // Rate limiting exports
package/src/mailhog.js CHANGED
@@ -30,8 +30,8 @@ function getMailhogUiUrl() {
30
30
  export const getMailhogSmtpConfig = () => {
31
31
  const url = getMailhogSmtpUrl();
32
32
  const match = url.match(/smtp:\/\/([^:]+):(\d+)/);
33
- const host = match ? match[1] : (process.env.MAILHOG_HOST || "localhost");
34
- const port = match ? match[2] : (process.env.MAILHOG_SMTP_PORT || "1025");
33
+ const host = match ? match[1] : process.env.MAILHOG_HOST || "localhost";
34
+ const port = match ? match[2] : process.env.MAILHOG_SMTP_PORT || "1025";
35
35
 
36
36
  return {
37
37
  host,
@@ -143,7 +143,7 @@ class RedisRateLimiter {
143
143
  remaining: maxRequests - currentCount,
144
144
  resetTime,
145
145
  };
146
- } catch (error) {
146
+ } catch (_error) {
147
147
  // Configurable fail-open / fail-closed behaviour
148
148
  if (this.failOpen) {
149
149
  return {
@@ -1,7 +0,0 @@
1
-
2
- 
3
- > @quark/core@1.0.0 lint /Users/david/Projects/quark/packages/core
4
- > biome format --write && biome check --write
5
-
6
- Formatted 17 files in 40ms. No fixes applied.
7
- Checked 17 files in 64ms. No fixes applied.