@rx-ted/packages-honest-plugins 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +45 -0
  3. package/api-doc/.turbo/turbo-build.log +1 -0
  4. package/api-doc/.turbo/turbo-test.log +11 -0
  5. package/api-doc/.turbo/turbo-typecheck.log +1 -0
  6. package/api-doc/CHANGELOG.md +22 -0
  7. package/api-doc/README.md +80 -0
  8. package/api-doc/dist/api-doc.plugin.d.ts +53 -0
  9. package/api-doc/dist/api-doc.plugin.js +452 -0
  10. package/api-doc/dist/index.d.ts +5 -0
  11. package/api-doc/dist/index.js +3 -0
  12. package/api-doc/dist/interfaces/renderer.interface.d.ts +7 -0
  13. package/api-doc/dist/interfaces/renderer.interface.js +1 -0
  14. package/api-doc/dist/renderers/scalar.renderer.d.ts +10 -0
  15. package/api-doc/dist/renderers/scalar.renderer.js +76 -0
  16. package/api-doc/dist/renderers/swagger.renderer.d.ts +10 -0
  17. package/api-doc/dist/renderers/swagger.renderer.js +83 -0
  18. package/api-doc/node_modules/.bin/esbuild +22 -0
  19. package/api-doc/node_modules/.bin/jiti +22 -0
  20. package/api-doc/node_modules/.bin/sass +22 -0
  21. package/api-doc/node_modules/.bin/tsx +22 -0
  22. package/api-doc/node_modules/.bin/vite +22 -0
  23. package/api-doc/node_modules/.bin/vitest +22 -0
  24. package/api-doc/node_modules/.bin/yaml +22 -0
  25. package/api-doc/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  26. package/api-doc/package.json +48 -0
  27. package/api-doc/src/api-doc.plugin.ts +554 -0
  28. package/api-doc/src/index.ts +5 -0
  29. package/api-doc/src/interfaces/renderer.interface.ts +5 -0
  30. package/api-doc/src/plugin.test.ts +740 -0
  31. package/api-doc/src/renderers/scalar.renderer.ts +82 -0
  32. package/api-doc/src/renderers/swagger.renderer.ts +89 -0
  33. package/api-doc/tsconfig.json +21 -0
  34. package/api-doc/vitest.config.ts +13 -0
  35. package/cache/.turbo/turbo-build.log +1 -0
  36. package/cache/.turbo/turbo-test.log +16 -0
  37. package/cache/CHANGELOG.md +23 -0
  38. package/cache/README.md +79 -0
  39. package/cache/dist/cache-service.d.ts +8 -0
  40. package/cache/dist/cache-service.js +21 -0
  41. package/cache/dist/cacheable.d.ts +2 -0
  42. package/cache/dist/cacheable.js +10 -0
  43. package/cache/dist/cloudflare/driver.d.ts +23 -0
  44. package/cache/dist/cloudflare/driver.js +102 -0
  45. package/cache/dist/cloudflare/index.d.ts +3 -0
  46. package/cache/dist/cloudflare/index.js +2 -0
  47. package/cache/dist/cloudflare/plugin.d.ts +19 -0
  48. package/cache/dist/cloudflare/plugin.js +58 -0
  49. package/cache/dist/index.d.ts +11 -0
  50. package/cache/dist/index.js +14 -0
  51. package/cache/dist/local/driver.d.ts +2 -0
  52. package/cache/dist/local/driver.js +98 -0
  53. package/cache/dist/local/index.d.ts +2 -0
  54. package/cache/dist/local/index.js +2 -0
  55. package/cache/dist/local/plugin.d.ts +14 -0
  56. package/cache/dist/local/plugin.js +24 -0
  57. package/cache/dist/redis/driver.d.ts +10 -0
  58. package/cache/dist/redis/driver.js +102 -0
  59. package/cache/dist/redis/index.d.ts +4 -0
  60. package/cache/dist/redis/index.js +2 -0
  61. package/cache/dist/redis/plugin.d.ts +25 -0
  62. package/cache/dist/redis/plugin.js +61 -0
  63. package/cache/dist/resolve.d.ts +24 -0
  64. package/cache/dist/resolve.js +114 -0
  65. package/cache/dist/types.d.ts +18 -0
  66. package/cache/dist/types.js +1 -0
  67. package/cache/node_modules/.bin/esbuild +22 -0
  68. package/cache/node_modules/.bin/jiti +22 -0
  69. package/cache/node_modules/.bin/sass +22 -0
  70. package/cache/node_modules/.bin/tsx +22 -0
  71. package/cache/node_modules/.bin/vite +22 -0
  72. package/cache/node_modules/.bin/vitest +22 -0
  73. package/cache/node_modules/.bin/yaml +22 -0
  74. package/cache/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  75. package/cache/package.json +46 -0
  76. package/cache/src/cache-service.test.ts +16 -0
  77. package/cache/src/cache-service.ts +15 -0
  78. package/cache/src/cacheable.test.ts +78 -0
  79. package/cache/src/cacheable.ts +16 -0
  80. package/cache/src/cloudflare/driver.test.ts +188 -0
  81. package/cache/src/cloudflare/driver.ts +118 -0
  82. package/cache/src/cloudflare/index.ts +3 -0
  83. package/cache/src/cloudflare/plugin.ts +71 -0
  84. package/cache/src/index.test.ts +68 -0
  85. package/cache/src/index.ts +20 -0
  86. package/cache/src/local/driver.ts +117 -0
  87. package/cache/src/local/index.ts +2 -0
  88. package/cache/src/local/plugin.ts +34 -0
  89. package/cache/src/redis/driver.test.ts +71 -0
  90. package/cache/src/redis/driver.ts +124 -0
  91. package/cache/src/redis/index.ts +4 -0
  92. package/cache/src/redis/plugin.ts +84 -0
  93. package/cache/src/resolve.test.ts +257 -0
  94. package/cache/src/resolve.ts +140 -0
  95. package/cache/src/types.ts +14 -0
  96. package/cache/tsconfig.json +21 -0
  97. package/cache/vitest.config.ts +13 -0
  98. package/db/.turbo/turbo-build.log +1 -0
  99. package/db/.turbo/turbo-test.log +21 -0
  100. package/db/CHANGELOG.md +26 -0
  101. package/db/README.md +99 -0
  102. package/db/dist/constants.d.ts +1 -0
  103. package/db/dist/constants.js +1 -0
  104. package/db/dist/d1/index.d.ts +3 -0
  105. package/db/dist/d1/index.js +2 -0
  106. package/db/dist/d1/plugin.d.ts +18 -0
  107. package/db/dist/d1/plugin.js +104 -0
  108. package/db/dist/db-service.d.ts +7 -0
  109. package/db/dist/db-service.js +21 -0
  110. package/db/dist/index.d.ts +15 -0
  111. package/db/dist/index.js +20 -0
  112. package/db/dist/mysql/client.d.ts +36 -0
  113. package/db/dist/mysql/client.js +84 -0
  114. package/db/dist/mysql/driver.d.ts +18 -0
  115. package/db/dist/mysql/driver.js +107 -0
  116. package/db/dist/mysql/index.d.ts +11 -0
  117. package/db/dist/mysql/index.js +11 -0
  118. package/db/dist/mysql/plugin.d.ts +51 -0
  119. package/db/dist/mysql/plugin.js +94 -0
  120. package/db/dist/mysql/types.d.ts +38 -0
  121. package/db/dist/mysql/types.js +1 -0
  122. package/db/dist/postgres/index.d.ts +2 -0
  123. package/db/dist/postgres/index.js +3 -0
  124. package/db/dist/repository.d.ts +14 -0
  125. package/db/dist/repository.js +79 -0
  126. package/db/dist/resolve.d.ts +6 -0
  127. package/db/dist/resolve.js +81 -0
  128. package/db/dist/schema-builder/column-builder.d.ts +26 -0
  129. package/db/dist/schema-builder/column-builder.js +53 -0
  130. package/db/dist/schema-builder/compile-d1.d.ts +5 -0
  131. package/db/dist/schema-builder/compile-d1.js +67 -0
  132. package/db/dist/schema-builder/compile-mysql.d.ts +12 -0
  133. package/db/dist/schema-builder/compile-mysql.js +78 -0
  134. package/db/dist/schema-builder/index.d.ts +8 -0
  135. package/db/dist/schema-builder/index.js +22 -0
  136. package/db/dist/schema-builder/table-builder.d.ts +16 -0
  137. package/db/dist/schema-builder/table-builder.js +38 -0
  138. package/db/dist/schema-builder/types.d.ts +26 -0
  139. package/db/dist/schema-builder/types.js +1 -0
  140. package/db/dist/schema-builder/zod-bridge.d.ts +24 -0
  141. package/db/dist/schema-builder/zod-bridge.js +53 -0
  142. package/db/dist/sqlite/index.d.ts +4 -0
  143. package/db/dist/sqlite/index.js +2 -0
  144. package/db/dist/sqlite/plugin.d.ts +17 -0
  145. package/db/dist/sqlite/plugin.js +33 -0
  146. package/db/dist/types.d.ts +42 -0
  147. package/db/dist/types.js +1 -0
  148. package/db/node_modules/.bin/esbuild +22 -0
  149. package/db/node_modules/.bin/jiti +22 -0
  150. package/db/node_modules/.bin/sass +22 -0
  151. package/db/node_modules/.bin/tsx +22 -0
  152. package/db/node_modules/.bin/vite +22 -0
  153. package/db/node_modules/.bin/vitest +22 -0
  154. package/db/node_modules/.bin/yaml +22 -0
  155. package/db/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  156. package/db/package.json +59 -0
  157. package/db/src/constants.test.ts +8 -0
  158. package/db/src/constants.ts +1 -0
  159. package/db/src/d1/index.ts +13 -0
  160. package/db/src/d1/plugin.ts +135 -0
  161. package/db/src/db-service.ts +14 -0
  162. package/db/src/index.test.ts +63 -0
  163. package/db/src/index.ts +57 -0
  164. package/db/src/mysql/client.ts +112 -0
  165. package/db/src/mysql/driver.test.ts +190 -0
  166. package/db/src/mysql/driver.ts +116 -0
  167. package/db/src/mysql/index.ts +51 -0
  168. package/db/src/mysql/plugin.test.ts +228 -0
  169. package/db/src/mysql/plugin.ts +128 -0
  170. package/db/src/mysql/types.ts +32 -0
  171. package/db/src/postgres/index.ts +15 -0
  172. package/db/src/repository.test.ts +195 -0
  173. package/db/src/repository.ts +95 -0
  174. package/db/src/resolve.ts +105 -0
  175. package/db/src/schema-builder/column-builder.test.ts +55 -0
  176. package/db/src/schema-builder/column-builder.ts +87 -0
  177. package/db/src/schema-builder/compile-d1.test.ts +60 -0
  178. package/db/src/schema-builder/compile-d1.ts +70 -0
  179. package/db/src/schema-builder/compile-mysql.test.ts +64 -0
  180. package/db/src/schema-builder/compile-mysql.ts +102 -0
  181. package/db/src/schema-builder/index.test.ts +36 -0
  182. package/db/src/schema-builder/index.ts +41 -0
  183. package/db/src/schema-builder/table-builder.test.ts +55 -0
  184. package/db/src/schema-builder/table-builder.ts +50 -0
  185. package/db/src/schema-builder/types.test.ts +68 -0
  186. package/db/src/schema-builder/types.ts +30 -0
  187. package/db/src/schema-builder/zod-bridge.ts +89 -0
  188. package/db/src/sqlite/index.ts +14 -0
  189. package/db/src/sqlite/plugin.ts +46 -0
  190. package/db/src/types.ts +45 -0
  191. package/db/tsconfig.json +21 -0
  192. package/db/vitest.config.ts +13 -0
  193. package/mail/.turbo/turbo-build.log +1 -0
  194. package/mail/.turbo/turbo-test.log +12 -0
  195. package/mail/.turbo/turbo-typecheck.log +1 -0
  196. package/mail/CHANGELOG.md +25 -0
  197. package/mail/README.md +214 -0
  198. package/mail/dist/index.d.ts +4 -0
  199. package/mail/dist/index.js +2 -0
  200. package/mail/dist/plugin.d.ts +64 -0
  201. package/mail/dist/plugin.js +232 -0
  202. package/mail/dist/providers/brevo.provider.d.ts +15 -0
  203. package/mail/dist/providers/brevo.provider.js +75 -0
  204. package/mail/dist/providers/custom.provider.d.ts +9 -0
  205. package/mail/dist/providers/custom.provider.js +22 -0
  206. package/mail/dist/providers/resend.provider.d.ts +16 -0
  207. package/mail/dist/providers/resend.provider.js +77 -0
  208. package/mail/dist/providers/smtp.provider.d.ts +21 -0
  209. package/mail/dist/providers/smtp.provider.js +86 -0
  210. package/mail/dist/rate-limiter.d.ts +16 -0
  211. package/mail/dist/rate-limiter.js +49 -0
  212. package/mail/dist/types.d.ts +68 -0
  213. package/mail/dist/types.js +6 -0
  214. package/mail/node_modules/.bin/esbuild +22 -0
  215. package/mail/node_modules/.bin/jiti +22 -0
  216. package/mail/node_modules/.bin/sass +22 -0
  217. package/mail/node_modules/.bin/tsx +22 -0
  218. package/mail/node_modules/.bin/vite +22 -0
  219. package/mail/node_modules/.bin/vitest +22 -0
  220. package/mail/node_modules/.bin/yaml +22 -0
  221. package/mail/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  222. package/mail/package.json +52 -0
  223. package/mail/src/index.ts +4 -0
  224. package/mail/src/plugin.test.ts +219 -0
  225. package/mail/src/plugin.ts +313 -0
  226. package/mail/src/providers/brevo.provider.ts +78 -0
  227. package/mail/src/providers/custom.provider.ts +29 -0
  228. package/mail/src/providers/resend.provider.ts +74 -0
  229. package/mail/src/providers/smtp.provider.ts +85 -0
  230. package/mail/src/rate-limiter.test.ts +81 -0
  231. package/mail/src/rate-limiter.ts +54 -0
  232. package/mail/src/types.ts +81 -0
  233. package/mail/tsconfig.json +21 -0
  234. package/mail/vitest.config.ts +13 -0
  235. package/package.json +36 -0
  236. package/s3/.turbo/turbo-build.log +1 -0
  237. package/s3/.turbo/turbo-test.log +11 -0
  238. package/s3/CHANGELOG.md +20 -0
  239. package/s3/README.md +90 -0
  240. package/s3/dist/index.d.ts +6 -0
  241. package/s3/dist/index.js +4 -0
  242. package/s3/dist/s3-service.d.ts +7 -0
  243. package/s3/dist/s3-service.js +21 -0
  244. package/s3/dist/s3.driver.d.ts +6 -0
  245. package/s3/dist/s3.driver.js +221 -0
  246. package/s3/dist/s3.plugin.d.ts +24 -0
  247. package/s3/dist/s3.plugin.js +58 -0
  248. package/s3/dist/types.d.ts +30 -0
  249. package/s3/dist/types.js +1 -0
  250. package/s3/node_modules/.bin/esbuild +22 -0
  251. package/s3/node_modules/.bin/jiti +22 -0
  252. package/s3/node_modules/.bin/sass +22 -0
  253. package/s3/node_modules/.bin/tsx +22 -0
  254. package/s3/node_modules/.bin/vite +22 -0
  255. package/s3/node_modules/.bin/vitest +22 -0
  256. package/s3/node_modules/.bin/yaml +22 -0
  257. package/s3/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  258. package/s3/package.json +47 -0
  259. package/s3/src/index.test.ts +20 -0
  260. package/s3/src/index.ts +13 -0
  261. package/s3/src/s3-service.ts +14 -0
  262. package/s3/src/s3.driver.ts +259 -0
  263. package/s3/src/s3.plugin.ts +80 -0
  264. package/s3/src/types.ts +37 -0
  265. package/s3/tsconfig.json +21 -0
  266. package/s3/vitest.config.ts +13 -0
@@ -0,0 +1,16 @@
1
+ import type { MailProvider, SendMailOptions, SendMailResult } from '../types';
2
+ export declare class ResendMailProvider implements MailProvider {
3
+ private config;
4
+ private fromEmail;
5
+ private fromName?;
6
+ readonly name: string;
7
+ private client;
8
+ private initPromise;
9
+ constructor(name: string, config: {
10
+ apiKey: string;
11
+ }, fromEmail: string, fromName?: string | undefined);
12
+ private ensureInit;
13
+ send(options: SendMailOptions, _providerName?: string): Promise<SendMailResult>;
14
+ healthCheck(): Promise<boolean>;
15
+ close(): Promise<void>;
16
+ }
@@ -0,0 +1,77 @@
1
+ export class ResendMailProvider {
2
+ config;
3
+ fromEmail;
4
+ fromName;
5
+ name;
6
+ client = null;
7
+ initPromise = null;
8
+ constructor(name, config, fromEmail, fromName) {
9
+ this.config = config;
10
+ this.fromEmail = fromEmail;
11
+ this.fromName = fromName;
12
+ this.name = name;
13
+ }
14
+ async ensureInit() {
15
+ if (this.client)
16
+ return;
17
+ if (this.initPromise)
18
+ return this.initPromise;
19
+ this.initPromise = (async () => {
20
+ const { Resend } = await import('resend');
21
+ this.client = new Resend(this.config.apiKey);
22
+ })();
23
+ return this.initPromise;
24
+ }
25
+ async send(options, _providerName) {
26
+ await this.ensureInit();
27
+ const payload = {
28
+ from: this.fromName
29
+ ? `${this.fromName} <${options.from || this.fromEmail}>`
30
+ : options.from || this.fromEmail,
31
+ to: Array.isArray(options.to) ? options.to : [options.to],
32
+ subject: options.subject,
33
+ };
34
+ if (options.cc)
35
+ payload.cc = Array.isArray(options.cc) ? options.cc : [options.cc];
36
+ if (options.bcc)
37
+ payload.bcc = Array.isArray(options.bcc) ? options.bcc : [options.bcc];
38
+ if (options.html)
39
+ payload.html = options.html;
40
+ if (options.text)
41
+ payload.text = options.text;
42
+ if (options.attachments?.length) {
43
+ payload.attachments = options.attachments.map((a) => ({
44
+ filename: a.filename,
45
+ content: a.content,
46
+ path: a.path,
47
+ content_type: a.contentType,
48
+ }));
49
+ }
50
+ if (options.headers)
51
+ payload.headers = options.headers;
52
+ if (options.tags)
53
+ payload.tags = Object.entries(options.tags).map(([key, value]) => ({ name: key, value }));
54
+ const { data, error } = await this.client.emails.send(payload);
55
+ if (error)
56
+ throw new Error(`Resend send failed: ${error.message}`);
57
+ return { id: data?.id ?? 'unknown', provider: this.name };
58
+ }
59
+ async healthCheck() {
60
+ try {
61
+ await this.ensureInit();
62
+ await this.client.emails.send({
63
+ from: this.fromName ? `${this.fromName} <${this.fromEmail}>` : this.fromEmail,
64
+ to: [this.fromEmail],
65
+ subject: 'Health check',
66
+ text: 'This is a health check email from Resend provider.',
67
+ });
68
+ return true;
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ }
74
+ async close() {
75
+ this.client = null;
76
+ }
77
+ }
@@ -0,0 +1,21 @@
1
+ import type { MailProvider, SendMailOptions, SendMailResult } from '../types';
2
+ export declare class SmtpMailProvider implements MailProvider {
3
+ private config;
4
+ private fromEmail;
5
+ private fromName?;
6
+ readonly name: string;
7
+ private mailer;
8
+ private initPromise;
9
+ constructor(name: string, config: {
10
+ host: string;
11
+ port: number;
12
+ user: string;
13
+ pass: string;
14
+ secure?: boolean;
15
+ }, fromEmail: string, fromName?: string | undefined);
16
+ private ensureInit;
17
+ send(options: SendMailOptions, _providerName?: string): Promise<SendMailResult>;
18
+ warmUp(): Promise<void>;
19
+ healthCheck(): Promise<boolean>;
20
+ close(): Promise<void>;
21
+ }
@@ -0,0 +1,86 @@
1
+ export class SmtpMailProvider {
2
+ config;
3
+ fromEmail;
4
+ fromName;
5
+ name;
6
+ mailer = null;
7
+ initPromise = null;
8
+ constructor(name, config, fromEmail, fromName) {
9
+ this.config = config;
10
+ this.fromEmail = fromEmail;
11
+ this.fromName = fromName;
12
+ this.name = name;
13
+ }
14
+ async ensureInit() {
15
+ if (this.mailer)
16
+ return;
17
+ if (this.initPromise)
18
+ return this.initPromise;
19
+ this.initPromise = (async () => {
20
+ const { createSMTPMailer } = await import('sently/smtp');
21
+ this.mailer = await createSMTPMailer({
22
+ host: this.config.host,
23
+ port: this.config.port,
24
+ secure: this.config.secure,
25
+ auth: {
26
+ user: this.config.user,
27
+ pass: this.config.pass,
28
+ },
29
+ });
30
+ })();
31
+ return this.initPromise;
32
+ }
33
+ async send(options, _providerName) {
34
+ await this.ensureInit();
35
+ const email = options.from || this.fromEmail;
36
+ const payload = {
37
+ from: this.fromName ? { name: this.fromName, address: email } : email,
38
+ to: Array.isArray(options.to) ? options.to.join(', ') : options.to,
39
+ subject: options.subject,
40
+ };
41
+ if (options.html)
42
+ payload.html = options.html;
43
+ if (options.text)
44
+ payload.text = options.text;
45
+ if (options.cc)
46
+ payload.cc = Array.isArray(options.cc) ? options.cc.join(', ') : options.cc;
47
+ if (options.bcc)
48
+ payload.bcc = Array.isArray(options.bcc) ? options.bcc.join(', ') : options.bcc;
49
+ if (options.replyTo)
50
+ payload.replyTo = options.replyTo;
51
+ if (options.attachments?.length) {
52
+ payload.attachments = options.attachments.map((a) => ({
53
+ filename: a.filename,
54
+ content: a.content,
55
+ path: a.path,
56
+ contentType: a.contentType,
57
+ }));
58
+ }
59
+ const info = await this.mailer.send(payload);
60
+ return { id: info.messageId ?? 'unknown', provider: this.name };
61
+ }
62
+ async warmUp() {
63
+ await this.ensureInit();
64
+ await this.mailer.verify();
65
+ }
66
+ async healthCheck() {
67
+ try {
68
+ await this.warmUp();
69
+ return true;
70
+ }
71
+ catch {
72
+ return false;
73
+ }
74
+ }
75
+ async close() {
76
+ if (this.mailer) {
77
+ try {
78
+ await this.mailer.close();
79
+ }
80
+ catch {
81
+ /* ignore */
82
+ }
83
+ this.mailer = null;
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,16 @@
1
+ import type { ILogger } from '@rx-ted/packages-core';
2
+ export interface MailCacheDriver {
3
+ incr(key: string): Promise<number>;
4
+ expire(key: string, ttl: number): Promise<boolean>;
5
+ get?(key: string): Promise<unknown>;
6
+ }
7
+ export declare class RateLimiter {
8
+ private quota;
9
+ private providerName;
10
+ private cache?;
11
+ private logger?;
12
+ constructor(quota: number, providerName: string, cache?: (MailCacheDriver | null) | undefined, logger?: ILogger | undefined);
13
+ checkAndIncrement(): Promise<void>;
14
+ markHealthCheckDone(): Promise<void>;
15
+ wasHealthCheckDoneToday(): Promise<boolean>;
16
+ }
@@ -0,0 +1,49 @@
1
+ export class RateLimiter {
2
+ quota;
3
+ providerName;
4
+ cache;
5
+ logger;
6
+ constructor(quota, providerName, cache, logger) {
7
+ this.quota = quota;
8
+ this.providerName = providerName;
9
+ this.cache = cache;
10
+ this.logger = logger;
11
+ }
12
+ async checkAndIncrement() {
13
+ if (!this.cache)
14
+ return;
15
+ const key = `mail:quota:${this.providerName}:${today()}`;
16
+ const count = await this.cache.incr(key);
17
+ if (count === 1) {
18
+ await this.cache.expire(key, 86400);
19
+ }
20
+ if (count > this.quota) {
21
+ this.logger?.warn?.('Daily quota exceeded', {
22
+ provider: this.providerName,
23
+ quota: this.quota,
24
+ });
25
+ const { MailQuotaExceededError } = await import('./types');
26
+ throw new MailQuotaExceededError(this.providerName, this.quota);
27
+ }
28
+ }
29
+ async markHealthCheckDone() {
30
+ if (!this.cache)
31
+ return;
32
+ const key = `mail:healthcheck:${this.providerName}:${today()}`;
33
+ await this.cache.incr(key);
34
+ await this.cache.expire(key, 86400);
35
+ }
36
+ async wasHealthCheckDoneToday() {
37
+ if (!this.cache)
38
+ return false;
39
+ const key = `mail:healthcheck:${this.providerName}:${today()}`;
40
+ if (typeof this.cache.get === 'function') {
41
+ const val = await this.cache.get(key);
42
+ return val !== null && val !== undefined;
43
+ }
44
+ return false;
45
+ }
46
+ }
47
+ function today() {
48
+ return new Date().toISOString().slice(0, 10);
49
+ }
@@ -0,0 +1,68 @@
1
+ export interface Attachment {
2
+ filename: string;
3
+ content?: string | Uint8Array;
4
+ path?: string;
5
+ contentType?: string;
6
+ }
7
+ export interface SendMailOptions {
8
+ from?: string;
9
+ to: string | string[];
10
+ cc?: string | string[];
11
+ bcc?: string | string[];
12
+ subject: string;
13
+ text?: string;
14
+ html?: string;
15
+ replyTo?: string;
16
+ attachments?: Attachment[];
17
+ headers?: Record<string, string>;
18
+ tags?: Record<string, string>;
19
+ }
20
+ export interface SendMailResult {
21
+ id: string;
22
+ provider: string;
23
+ }
24
+ export interface MailProvider {
25
+ readonly name: string;
26
+ send(options: SendMailOptions, providerName?: string): Promise<SendMailResult>;
27
+ healthCheck(): Promise<boolean>;
28
+ close(): Promise<void>;
29
+ warmUp?(): Promise<void>;
30
+ }
31
+ export interface ResendProviderConfig {
32
+ apiKey: string;
33
+ }
34
+ export interface BrevoProviderConfig {
35
+ apiKey: string;
36
+ }
37
+ export interface SmtpProviderConfig {
38
+ host: string;
39
+ port: number;
40
+ user: string;
41
+ pass: string;
42
+ secure?: boolean;
43
+ }
44
+ export type ProviderType = 'resend' | 'brevo' | 'smtp' | 'custom';
45
+ export interface MailProviderConfigEntry {
46
+ name: string;
47
+ provider: ProviderType;
48
+ fromEmail: string;
49
+ fromName?: string;
50
+ limits?: {
51
+ dailyQuota?: number;
52
+ };
53
+ resend?: ResendProviderConfig;
54
+ brevo?: BrevoProviderConfig;
55
+ smtp?: SmtpProviderConfig;
56
+ custom?: MailProvider;
57
+ }
58
+ export interface MailPluginOptions {
59
+ providers: MailProviderConfigEntry[];
60
+ healthCheck?: {
61
+ intervalMs?: number;
62
+ maxRetries?: number;
63
+ retryDelayMs?: number;
64
+ };
65
+ }
66
+ export declare class MailQuotaExceededError extends Error {
67
+ constructor(providerName: string, quota: number);
68
+ }
@@ -0,0 +1,6 @@
1
+ export class MailQuotaExceededError extends Error {
2
+ constructor(providerName, quota) {
3
+ super(`Mail quota exceeded for provider "${providerName}": ${quota}/day`);
4
+ this.name = 'MailQuotaExceededError';
5
+ }
6
+ }
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/esbuild@0.28.1/node_modules/esbuild/node_modules:/Users/ben/projects/app/node_modules/.pnpm/esbuild@0.28.1/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/esbuild@0.28.1/node_modules/esbuild/node_modules:/Users/ben/projects/app/node_modules/.pnpm/esbuild@0.28.1/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../../../../../node_modules/.pnpm/esbuild@0.28.1/node_modules/esbuild/bin/esbuild" "$@"
19
+ else
20
+ exec node "$basedir/../../../../../node_modules/.pnpm/esbuild@0.28.1/node_modules/esbuild/bin/esbuild" "$@"
21
+ fi
22
+ # cmd-shim-target=/Users/ben/projects/app/node_modules/.pnpm/esbuild@0.28.1/node_modules/esbuild/bin/esbuild
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/node_modules:/Users/ben/projects/app/node_modules/.pnpm/jiti@2.6.1/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/node_modules:/Users/ben/projects/app/node_modules/.pnpm/jiti@2.6.1/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../../../../../node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/lib/jiti-cli.mjs" "$@"
19
+ else
20
+ exec node "$basedir/../../../../../node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/lib/jiti-cli.mjs" "$@"
21
+ fi
22
+ # cmd-shim-target=/Users/ben/projects/app/node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/lib/jiti-cli.mjs
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/sass@1.100.0/node_modules/sass/node_modules:/Users/ben/projects/app/node_modules/.pnpm/sass@1.100.0/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/sass@1.100.0/node_modules/sass/node_modules:/Users/ben/projects/app/node_modules/.pnpm/sass@1.100.0/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../../../../../node_modules/.pnpm/sass@1.100.0/node_modules/sass/sass.js" "$@"
19
+ else
20
+ exec node "$basedir/../../../../../node_modules/.pnpm/sass@1.100.0/node_modules/sass/sass.js" "$@"
21
+ fi
22
+ # cmd-shim-target=/Users/ben/projects/app/node_modules/.pnpm/sass@1.100.0/node_modules/sass/sass.js
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/tsx@4.23.0/node_modules/tsx/node_modules:/Users/ben/projects/app/node_modules/.pnpm/tsx@4.23.0/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/tsx@4.23.0/node_modules/tsx/node_modules:/Users/ben/projects/app/node_modules/.pnpm/tsx@4.23.0/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../../../../../node_modules/.pnpm/tsx@4.23.0/node_modules/tsx/dist/cli.mjs" "$@"
19
+ else
20
+ exec node "$basedir/../../../../../node_modules/.pnpm/tsx@4.23.0/node_modules/tsx/dist/cli.mjs" "$@"
21
+ fi
22
+ # cmd-shim-target=/Users/ben/projects/app/node_modules/.pnpm/tsx@4.23.0/node_modules/tsx/dist/cli.mjs
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/vite@8.0.16_@types+node@25.9.2_esbuild@0.28.1_jiti@2.6.1_sass@1.100.0_tsx@4.23.0_yaml@2.9.0/node_modules/vite/node_modules:/Users/ben/projects/app/node_modules/.pnpm/vite@8.0.16_@types+node@25.9.2_esbuild@0.28.1_jiti@2.6.1_sass@1.100.0_tsx@4.23.0_yaml@2.9.0/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/vite@8.0.16_@types+node@25.9.2_esbuild@0.28.1_jiti@2.6.1_sass@1.100.0_tsx@4.23.0_yaml@2.9.0/node_modules/vite/node_modules:/Users/ben/projects/app/node_modules/.pnpm/vite@8.0.16_@types+node@25.9.2_esbuild@0.28.1_jiti@2.6.1_sass@1.100.0_tsx@4.23.0_yaml@2.9.0/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../../../../../node_modules/.pnpm/vite@8.0.16_@types+node@25.9.2_esbuild@0.28.1_jiti@2.6.1_sass@1.100.0_tsx@4.23.0_yaml@2.9.0/node_modules/vite/bin/vite.js" "$@"
19
+ else
20
+ exec node "$basedir/../../../../../node_modules/.pnpm/vite@8.0.16_@types+node@25.9.2_esbuild@0.28.1_jiti@2.6.1_sass@1.100.0_tsx@4.23.0_yaml@2.9.0/node_modules/vite/bin/vite.js" "$@"
21
+ fi
22
+ # cmd-shim-target=/Users/ben/projects/app/node_modules/.pnpm/vite@8.0.16_@types+node@25.9.2_esbuild@0.28.1_jiti@2.6.1_sass@1.100.0_tsx@4.23.0_yaml@2.9.0/node_modules/vite/bin/vite.js
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.2_@vitest+coverage-v8@4.1.7_jsdom@29.1.1_@noble+hashes@1._7bccdcabe1f1ec543f47b71e54a2d105/node_modules/vitest/node_modules:/Users/ben/projects/app/node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.2_@vitest+coverage-v8@4.1.7_jsdom@29.1.1_@noble+hashes@1._7bccdcabe1f1ec543f47b71e54a2d105/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.2_@vitest+coverage-v8@4.1.7_jsdom@29.1.1_@noble+hashes@1._7bccdcabe1f1ec543f47b71e54a2d105/node_modules/vitest/node_modules:/Users/ben/projects/app/node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.2_@vitest+coverage-v8@4.1.7_jsdom@29.1.1_@noble+hashes@1._7bccdcabe1f1ec543f47b71e54a2d105/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../vitest/vitest.mjs" "$@"
19
+ else
20
+ exec node "$basedir/../vitest/vitest.mjs" "$@"
21
+ fi
22
+ # cmd-shim-target=/Users/ben/projects/app/packages/honest-plugins/mail/node_modules/vitest/vitest.mjs
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/node_modules:/Users/ben/projects/app/node_modules/.pnpm/yaml@2.9.0/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/ben/projects/app/node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/node_modules:/Users/ben/projects/app/node_modules/.pnpm/yaml@2.9.0/node_modules:/Users/ben/projects/app/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../../../../../node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/bin.mjs" "$@"
19
+ else
20
+ exec node "$basedir/../../../../../node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/bin.mjs" "$@"
21
+ fi
22
+ # cmd-shim-target=/Users/ben/projects/app/node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/bin.mjs
@@ -0,0 +1 @@
1
+ {"version":"4.1.8","results":[[":src/rate-limiter.test.ts",{"duration":6.678083000000001,"failed":false}],[":src/plugin.test.ts",{"duration":4.98658300000001,"failed":false}]]}
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@rx-ted/packages-honest-plugins-mail",
3
+ "version": "1.0.1",
4
+ "private": true,
5
+ "description": "Mail plugin for @rx-ted/packages-honest",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "vitest": "./src/index.ts",
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "sideEffects": [
17
+ "./dist/index.js"
18
+ ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/rx-ted/honest.git"
22
+ },
23
+ "author": "rx-ted",
24
+ "license": "MIT",
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "scripts": {
29
+ "build": "tsc -p tsconfig.json",
30
+ "typecheck": "tsc -p tsconfig.json --noEmit",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest",
33
+ "test:coverage": "vitest run --coverage",
34
+ "demo": "bun run demo/send.ts"
35
+ },
36
+ "dependencies": {
37
+ "@rx-ted/packages-core": "workspace:^"
38
+ },
39
+ "peerDependencies": {
40
+ "@rx-ted/packages-honest": "workspace:^",
41
+ "hono": "^4.12.18"
42
+ },
43
+ "optionalDependencies": {
44
+ "sently": "^0.8.0",
45
+ "resend": "^6.14.0",
46
+ "@getbrevo/brevo": "^5.0.4"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^25.9.1",
50
+ "vitest": "^4.1.7"
51
+ }
52
+ }
@@ -0,0 +1,4 @@
1
+ export { MailPlugin, MAIL_GLOBAL_KEY, MAIL_DISABLED_MSG } from './plugin';
2
+ export type { MailPluginOptions } from './plugin';
3
+ export type { MailProvider, SendMailOptions, SendMailResult, Attachment } from './types';
4
+ export { MailQuotaExceededError } from './types';