@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,116 @@
1
+ import type { Pool } from 'mysql2/promise';
2
+ import type { Logger } from '@rx-ted/packages-core';
3
+ import type { DatabaseClient, DatabaseConnection, DatabaseDriver, QueryResult } from '../types';
4
+ import { createMysqlClient, MysqlDatabaseClient } from './client';
5
+
6
+ export async function createMysqlDriver(options: {
7
+ host: string;
8
+ port: number;
9
+ user: string;
10
+ password?: string;
11
+ database: string;
12
+ ssl?: boolean | { rejectUnauthorized?: boolean; ca?: string; cert?: string; key?: string };
13
+ logger?: Logger;
14
+ pool?: Pool;
15
+ }): Promise<DatabaseDriver> {
16
+ const logger = options.logger;
17
+ if (logger)
18
+ logger.debug(
19
+ { host: options.host, port: options.port, database: options.database },
20
+ 'MySQL: connecting',
21
+ );
22
+ const client = options.pool
23
+ ? new MysqlDatabaseClient(options.pool)
24
+ : await createMysqlClient(options);
25
+ if (logger) logger.info('MySQL: connected');
26
+ return new MysqlDriver(client, logger);
27
+ }
28
+
29
+ class MysqlDriver implements DatabaseDriver {
30
+ constructor(
31
+ private client: DatabaseClient,
32
+ private logger?: Logger,
33
+ ) {}
34
+ async query<T = unknown>(sql: string, params?: unknown[]): Promise<T[]> {
35
+ if (this.logger) this.logger.debug({ sql: sql.substring(0, 100), params }, 'MySQL: query');
36
+ const conn = await this.client.getConnection();
37
+ const result = await conn.query<T>(sql, params);
38
+ conn.release();
39
+ if (this.logger) this.logger.debug({ rows: result.length }, 'MySQL: result');
40
+ return result;
41
+ }
42
+ async execute(sql: string, params?: unknown[]): Promise<QueryResult> {
43
+ if (this.logger) this.logger.debug({ sql: sql.substring(0, 100), params }, 'MySQL: execute');
44
+ const conn = await this.client.getConnection();
45
+ const stmt = conn.prepare(sql);
46
+ const result = await stmt.execute(params);
47
+ stmt.release();
48
+ conn.release();
49
+ if (this.logger)
50
+ this.logger.debug(
51
+ { affectedRows: result.affectedRows, insertId: result.insertId },
52
+ 'MySQL: executed',
53
+ );
54
+ return {
55
+ affectedRows: result.affectedRows,
56
+ insertId: result.insertId ? Number(result.insertId) : undefined,
57
+ };
58
+ }
59
+ async transaction<T>(fn: (driver: DatabaseDriver) => Promise<T>): Promise<T> {
60
+ const conn = await this.client.getConnection();
61
+ await conn.query('SET autocommit = 0');
62
+ await conn.query('START TRANSACTION');
63
+ const txDriver = new TransactionalMysqlDriver(conn, this.logger);
64
+ try {
65
+ const result = await fn(txDriver);
66
+ await conn.query('COMMIT');
67
+ await conn.query('SET autocommit = 1');
68
+ if (this.logger) this.logger.debug('MySQL: transaction committed');
69
+ return result;
70
+ } catch (error) {
71
+ await conn.query('ROLLBACK');
72
+ await conn.query('SET autocommit = 1');
73
+ if (this.logger) this.logger.error({ error }, 'MySQL: transaction rolled back');
74
+ throw error;
75
+ } finally {
76
+ conn.release();
77
+ }
78
+ }
79
+ async close(): Promise<void> {
80
+ await this.client.close();
81
+ if (this.logger) this.logger.info('MySQL: closed');
82
+ }
83
+ async healthCheck(): Promise<boolean> {
84
+ try {
85
+ const conn = await this.client.getConnection();
86
+ await conn.query('SELECT 1');
87
+ conn.release();
88
+ return true;
89
+ } catch {
90
+ return false;
91
+ }
92
+ }
93
+ }
94
+
95
+ class TransactionalMysqlDriver implements DatabaseDriver {
96
+ constructor(
97
+ private connection: DatabaseConnection,
98
+ _logger?: Logger,
99
+ ) {}
100
+ async query<T = unknown>(sql: string, params?: unknown[]): Promise<T[]> {
101
+ return this.connection.query<T>(sql, params);
102
+ }
103
+ async execute(sql: string, params?: unknown[]): Promise<QueryResult> {
104
+ const stmt = this.connection.prepare(sql);
105
+ const result = await stmt.execute(params);
106
+ stmt.release();
107
+ return result;
108
+ }
109
+ async transaction<T>(_fn: (driver: DatabaseDriver) => Promise<T>): Promise<T> {
110
+ throw new Error('Nested transactions are not supported');
111
+ }
112
+ async close(): Promise<void> {}
113
+ async healthCheck(): Promise<boolean> {
114
+ return true;
115
+ }
116
+ }
@@ -0,0 +1,51 @@
1
+ // Plugin
2
+ export {
3
+ MysqlPlugin,
4
+ DRIZZLE_GLOBAL_KEY,
5
+ DB_CONTEXT_KEY,
6
+ DRIZZLE_CONTEXT_KEY,
7
+ MYSQL_CONTEXT_KEY,
8
+ POOL_CONTEXT_KEY,
9
+ } from './plugin';
10
+ export type { MysqlPluginOptions } from './plugin';
11
+
12
+ // Global key
13
+ export { DB_GLOBAL_KEY } from '../constants';
14
+
15
+ // Raw driver
16
+ export { createMysqlDriver } from './driver';
17
+ export { createMysqlPool, createMysqlClient, MysqlDatabaseClient } from './client';
18
+ export type { MysqlClientOptions } from './types';
19
+
20
+ // MySQL-specific types
21
+ export type {
22
+ DatabaseOptions,
23
+ SslOptions,
24
+ DriverOptions,
25
+ DatabaseDriverOptions,
26
+ } from './types';
27
+
28
+ // ── Drizzle ORM re-exports ──
29
+ export {
30
+ mysqlTable,
31
+ varchar,
32
+ int,
33
+ bigint,
34
+ char,
35
+ text,
36
+ longtext,
37
+ date,
38
+ datetime,
39
+ timestamp,
40
+ boolean,
41
+ json,
42
+ mysqlEnum,
43
+ uniqueIndex,
44
+ index,
45
+ primaryKey,
46
+ foreignKey,
47
+ } from 'drizzle-orm/mysql-core';
48
+
49
+ export type { MySql2Database } from 'drizzle-orm/mysql2';
50
+ export { drizzle } from 'drizzle-orm/mysql2';
51
+ export * as drizzleORM from 'drizzle-orm';
@@ -0,0 +1,228 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { MysqlPlugin } from './plugin';
3
+ import type { Application } from '@rx-ted/packages-honest';
4
+
5
+ vi.mock('@rx-ted/packages-honest', () => ({
6
+ ComponentManager: {
7
+ registerPlugin: vi.fn(),
8
+ getPlugin: vi.fn().mockReturnValue({ platform: 'node' }),
9
+ hasPlugin: vi.fn(),
10
+ },
11
+ resolvePluginLogger: vi.fn().mockReturnValue({
12
+ child: vi.fn().mockReturnThis(),
13
+ info: vi.fn(),
14
+ error: vi.fn(),
15
+ warn: vi.fn(),
16
+ debug: vi.fn(),
17
+ }),
18
+ LOGGER_SYMBOL: Symbol('logger'),
19
+ }));
20
+
21
+ vi.mock('./client', () => ({
22
+ createMysqlPool: vi.fn().mockReturnValue({ end: vi.fn() }),
23
+ createMysqlClient: vi.fn(),
24
+ MysqlDatabaseClient: class {
25
+ constructor(public pool: any) {}
26
+ prepare = vi.fn();
27
+ getConnection = vi.fn();
28
+ close = vi.fn();
29
+ },
30
+ }));
31
+
32
+ vi.mock('./driver', () => ({
33
+ createMysqlDriver: vi.fn().mockResolvedValue({
34
+ query: vi.fn(),
35
+ execute: vi.fn(),
36
+ transaction: vi.fn(),
37
+ close: vi.fn(),
38
+ healthCheck: vi.fn().mockResolvedValue(true),
39
+ }),
40
+ }));
41
+
42
+ vi.mock('drizzle-orm/mysql2', () => ({
43
+ drizzle: vi.fn().mockReturnValue({ _: 'drizzle-instance' }),
44
+ }));
45
+
46
+ import { ComponentManager } from '@rx-ted/packages-honest';
47
+ import { createMysqlPool } from './client';
48
+ import { createMysqlDriver } from './driver';
49
+ import { drizzle } from 'drizzle-orm/mysql2';
50
+ import { DB_GLOBAL_KEY } from '../constants';
51
+
52
+ function createMockApp(): Application {
53
+ const ctx = new Map<string, any>();
54
+ return {
55
+ getContext: vi.fn().mockReturnValue({
56
+ set: vi.fn((key: string, value: any) => {
57
+ ctx.set(key, value);
58
+ }),
59
+ get: vi.fn((key: string) => ctx.get(key)),
60
+ }),
61
+ } as unknown as Application;
62
+ }
63
+
64
+ describe('MysqlPlugin', () => {
65
+ let plugin: MysqlPlugin;
66
+ let mockApp: Application;
67
+
68
+ const defaultOptions = {
69
+ connection: {
70
+ host: 'localhost',
71
+ port: 3306,
72
+ user: 'root',
73
+ password: 'secret',
74
+ database: 'test',
75
+ },
76
+ };
77
+
78
+ beforeEach(() => {
79
+ vi.clearAllMocks();
80
+ plugin = new MysqlPlugin(defaultOptions);
81
+ mockApp = createMockApp();
82
+ });
83
+
84
+ describe('construction', () => {
85
+ it('sets name and version', () => {
86
+ expect(plugin.name).toBe('mysql-plugin');
87
+ expect(plugin.version).toBe('1.0.0');
88
+ });
89
+
90
+ it('has static globalKey', () => {
91
+ expect(MysqlPlugin.globalKey).toBe(DB_GLOBAL_KEY);
92
+ });
93
+
94
+ it('implements IPlugin', () => {
95
+ expect(typeof plugin.beforeModulesRegistered).toBe('function');
96
+ expect(typeof plugin.afterModulesRegistered).toBe('function');
97
+ expect(typeof plugin.close).toBe('function');
98
+ });
99
+ });
100
+
101
+ describe('beforeModulesRegistered', () => {
102
+ it('creates pool, driver, and drizzle instance', async () => {
103
+ await plugin.beforeModulesRegistered(mockApp, {} as any);
104
+
105
+ expect(createMysqlPool).toHaveBeenCalledWith({
106
+ host: 'localhost',
107
+ port: 3306,
108
+ user: 'root',
109
+ password: 'secret',
110
+ database: 'test',
111
+ });
112
+ expect(createMysqlDriver).toHaveBeenCalled();
113
+ expect(drizzle).toHaveBeenCalled();
114
+ });
115
+
116
+ it('registers the drizzle instance in ComponentManager', async () => {
117
+ await plugin.beforeModulesRegistered(mockApp, {} as any);
118
+
119
+ expect(ComponentManager.registerPlugin).toHaveBeenCalledWith(
120
+ DB_GLOBAL_KEY,
121
+ expect.objectContaining({ _: 'drizzle-instance' }),
122
+ );
123
+ });
124
+
125
+ it('stores pool, driver and drizzle in app context', async () => {
126
+ const ctx = { set: vi.fn() };
127
+ (mockApp.getContext as any).mockReturnValue(ctx);
128
+
129
+ await plugin.beforeModulesRegistered(mockApp, {} as any);
130
+
131
+ expect(ctx.set).toHaveBeenCalledWith('honest:mysql:pool', expect.any(Object));
132
+ expect(ctx.set).toHaveBeenCalledWith('honest:mysql', expect.any(Object));
133
+ expect(ctx.set).toHaveBeenCalledWith('context:db', expect.any(Object));
134
+ });
135
+
136
+ it('uses custom contextKey if provided', async () => {
137
+ const ctx = { set: vi.fn() };
138
+ (mockApp.getContext as any).mockReturnValue(ctx);
139
+ plugin = new MysqlPlugin({
140
+ ...defaultOptions,
141
+ contextKey: 'custom:ctx',
142
+ });
143
+
144
+ await plugin.beforeModulesRegistered(mockApp, {} as any);
145
+
146
+ expect(ctx.set).toHaveBeenCalledWith('custom:ctx', expect.any(Object));
147
+ });
148
+ });
149
+
150
+ describe('afterModulesRegistered', () => {
151
+ it('runs health check', async () => {
152
+ await plugin.beforeModulesRegistered(mockApp, {} as any);
153
+ const healthCheck = vi.fn().mockResolvedValue(true);
154
+
155
+ (createMysqlDriver as any).mockResolvedValue({
156
+ query: vi.fn(),
157
+ execute: vi.fn(),
158
+ transaction: vi.fn(),
159
+ close: vi.fn(),
160
+ healthCheck,
161
+ });
162
+ await plugin.beforeModulesRegistered(mockApp, {} as any);
163
+
164
+ await plugin.afterModulesRegistered(mockApp, {} as any);
165
+
166
+ expect(healthCheck).toHaveBeenCalled();
167
+ });
168
+ });
169
+
170
+ describe('getters', () => {
171
+ it('getClient returns driver after init', async () => {
172
+ await plugin.beforeModulesRegistered(mockApp, {} as any);
173
+
174
+ expect(plugin.getClient()).toBeDefined();
175
+ });
176
+
177
+ it('getClient throws before init', () => {
178
+ expect(() => plugin.getClient()).toThrow('MySQL driver not initialized');
179
+ });
180
+
181
+ it('getDrizzle returns instance after init', async () => {
182
+ await plugin.beforeModulesRegistered(mockApp, {} as any);
183
+
184
+ expect(plugin.getDrizzle()).toBeDefined();
185
+ });
186
+
187
+ it('getDrizzle throws before init', () => {
188
+ expect(() => plugin.getDrizzle()).toThrow('Drizzle not initialized');
189
+ });
190
+
191
+ it('getPool returns pool after init', async () => {
192
+ await plugin.beforeModulesRegistered(mockApp, {} as any);
193
+
194
+ expect(plugin.getPool()).toBeDefined();
195
+ });
196
+
197
+ it('getPool throws before init', () => {
198
+ expect(() => plugin.getPool()).toThrow('MySQL pool not initialized');
199
+ });
200
+ });
201
+
202
+ describe('close', () => {
203
+ it('calls close on driver and pool end', async () => {
204
+ await plugin.beforeModulesRegistered(mockApp, {} as any);
205
+
206
+ const driverClose = vi.fn().mockResolvedValue(undefined);
207
+ (createMysqlDriver as any).mockResolvedValue({
208
+ query: vi.fn(),
209
+ execute: vi.fn(),
210
+ transaction: vi.fn(),
211
+ close: driverClose,
212
+ healthCheck: vi.fn().mockResolvedValue(true),
213
+ });
214
+ await plugin.beforeModulesRegistered(mockApp, {} as any);
215
+
216
+ await plugin.close();
217
+
218
+ expect(driverClose).toHaveBeenCalled();
219
+ });
220
+
221
+ it('resets internal references after close', async () => {
222
+ await plugin.beforeModulesRegistered(mockApp, {} as any);
223
+ await plugin.close();
224
+
225
+ expect(() => plugin.getClient()).toThrow('MySQL driver not initialized');
226
+ });
227
+ });
228
+ });
@@ -0,0 +1,128 @@
1
+ import type { Hono } from 'hono';
2
+ import type mysql from 'mysql2/promise';
3
+ import { drizzle } from 'drizzle-orm/mysql2';
4
+ import { ENV_SYMBOL, type Env, type ILogger } from '@rx-ted/packages-core';
5
+ import { ComponentManager, resolvePluginLogger } from '@rx-ted/packages-honest';
6
+ import type { Application, IPlugin } from '@rx-ted/packages-honest';
7
+ import { createMysqlPool } from './client';
8
+ import { createMysqlDriver } from './driver';
9
+ import type { DatabaseDriver } from '../types';
10
+ import { DB_GLOBAL_KEY } from '../constants';
11
+ import { assertRuntimeSupport, type AppRuntime } from '../resolve';
12
+
13
+ export const DB_CONTEXT_KEY = 'context:db';
14
+ export const POOL_CONTEXT_KEY = 'honest:mysql:pool';
15
+
16
+ // Backward-compat aliases
17
+ export const DRIZZLE_GLOBAL_KEY = DB_GLOBAL_KEY;
18
+ export const DRIZZLE_CONTEXT_KEY = DB_CONTEXT_KEY;
19
+ export const MYSQL_CONTEXT_KEY = 'honest:mysql';
20
+
21
+ export interface MysqlPluginOptions {
22
+ connection: {
23
+ host: string;
24
+ port: number;
25
+ user: string;
26
+ password?: string;
27
+ database: string;
28
+ ssl?: boolean | { rejectUnauthorized?: boolean; ca?: string; cert?: string; key?: string };
29
+ connectionLimit?: number;
30
+ };
31
+ contextKey?: string;
32
+ schema?: Record<string, unknown>;
33
+ logger?: boolean | ((log: string) => void);
34
+ plugins?: Array<{ name: string; install: (db: any) => void }>;
35
+ }
36
+
37
+ export class MysqlPlugin implements IPlugin {
38
+ static readonly globalKey = DB_GLOBAL_KEY;
39
+ readonly name = 'mysql-plugin';
40
+ readonly version = '1.0.0';
41
+ logger?: ILogger;
42
+ private driver: DatabaseDriver | null = null;
43
+ private drizzleInstance: any = null;
44
+ private pool: mysql.Pool | null = null;
45
+ private readonly options: MysqlPluginOptions;
46
+ private readonly contextKey: string;
47
+
48
+ constructor(options: MysqlPluginOptions) {
49
+ this.options = options;
50
+ this.contextKey = options.contextKey ?? MYSQL_CONTEXT_KEY;
51
+ }
52
+
53
+ getClient(): DatabaseDriver {
54
+ if (!this.driver) throw new Error('MySQL driver not initialized');
55
+ return this.driver;
56
+ }
57
+
58
+ getDrizzle(): any {
59
+ if (!this.drizzleInstance) throw new Error('Drizzle not initialized');
60
+ return this.drizzleInstance;
61
+ }
62
+
63
+ getPool(): mysql.Pool {
64
+ if (!this.pool) throw new Error('MySQL pool not initialized');
65
+ return this.pool;
66
+ }
67
+
68
+ async beforeModulesRegistered(app: Application, _hono: Hono): Promise<void> {
69
+ this.logger ??= resolvePluginLogger(this.name);
70
+
71
+ const env = ComponentManager.getPlugin<Env>(ENV_SYMBOL);
72
+ assertRuntimeSupport(env.platform as AppRuntime, 'mysql');
73
+
74
+ this.logger.info(
75
+ {
76
+ host: this.options.connection.host,
77
+ database: this.options.connection.database,
78
+ },
79
+ 'MySQL: initializing',
80
+ );
81
+
82
+ this.pool = createMysqlPool({ ...this.options.connection });
83
+ this.driver = await createMysqlDriver({
84
+ ...this.options.connection,
85
+ pool: this.pool,
86
+ logger: this.logger as any,
87
+ });
88
+
89
+ const { schema } = this.options;
90
+ this.drizzleInstance = drizzle(this.pool, {
91
+ schema,
92
+ mode: 'default',
93
+ logger:
94
+ this.options.logger === true
95
+ ? true
96
+ : typeof this.options.logger === 'function'
97
+ ? {
98
+ logQuery: (query: string, params: unknown[]) =>
99
+ (this.options.logger as Function)(query),
100
+ }
101
+ : this.options.logger,
102
+ } as any);
103
+
104
+ ComponentManager.registerPlugin(DB_GLOBAL_KEY, this.drizzleInstance);
105
+ app.getContext().set(POOL_CONTEXT_KEY, this.pool);
106
+ app.getContext().set(this.contextKey, this.driver);
107
+ app.getContext().set(DB_CONTEXT_KEY, this.drizzleInstance);
108
+ }
109
+
110
+ async afterModulesRegistered(_app: Application, _hono: Hono): Promise<void> {
111
+ if (!this.logger) return;
112
+ const healthy = await this.driver?.healthCheck();
113
+ if (healthy) {
114
+ this.logger.info('MySQL: health check passed');
115
+ } else {
116
+ this.logger.error('MySQL: health check failed');
117
+ }
118
+ }
119
+
120
+ async close(): Promise<void> {
121
+ this.logger?.info('MySQL: shutting down');
122
+ await this.driver?.close();
123
+ if (this.pool) await this.pool.end();
124
+ this.driver = null;
125
+ this.drizzleInstance = null;
126
+ this.pool = null;
127
+ }
128
+ }
@@ -0,0 +1,32 @@
1
+ import type { Logger } from '@rx-ted/packages-core';
2
+
3
+ export interface DatabaseOptions {
4
+ host: string;
5
+ port: number;
6
+ user: string;
7
+ password?: string;
8
+ database: string;
9
+ ssl?: boolean | { rejectUnauthorized?: boolean; ca?: string; cert?: string; key?: string };
10
+ }
11
+
12
+ export interface SslOptions {
13
+ rejectUnauthorized?: boolean;
14
+ ca?: string;
15
+ cert?: string;
16
+ key?: string;
17
+ }
18
+
19
+ export interface DriverOptions {
20
+ logger?: Logger;
21
+ }
22
+
23
+ export interface DatabaseDriverOptions extends DatabaseOptions, DriverOptions {}
24
+
25
+ export interface MysqlClientOptions {
26
+ host: string;
27
+ port: number;
28
+ user: string;
29
+ password?: string;
30
+ database: string;
31
+ ssl?: boolean | { rejectUnauthorized?: boolean; ca?: string; cert?: string; key?: string };
32
+ }
@@ -0,0 +1,15 @@
1
+ // PostgreSQL dialect — placeholder
2
+ // Re-export Drizzle pg-core types for consumers to use in schema definitions
3
+ export {
4
+ pgTable,
5
+ text,
6
+ integer,
7
+ boolean,
8
+ timestamp,
9
+ pgEnum,
10
+ uniqueIndex,
11
+ index,
12
+ primaryKey,
13
+ foreignKey,
14
+ } from 'drizzle-orm/pg-core';
15
+ export type { PgDatabase } from 'drizzle-orm/pg-core';