@neverinfamous/mysql-mcp 2.3.0 → 3.0.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.
Files changed (347) hide show
  1. package/.dockerignore +1 -0
  2. package/.gitattributes +18 -0
  3. package/.github/workflows/codeql.yml +2 -10
  4. package/.github/workflows/docker-publish.yml +15 -13
  5. package/CHANGELOG.md +287 -1
  6. package/DOCKER_README.md +100 -265
  7. package/Dockerfile +5 -0
  8. package/README.md +124 -59
  9. package/VERSION +1 -1
  10. package/dist/__tests__/mocks/adapter.d.ts.map +1 -1
  11. package/dist/__tests__/mocks/adapter.js +2 -0
  12. package/dist/__tests__/mocks/adapter.js.map +1 -1
  13. package/dist/adapters/DatabaseAdapter.d.ts.map +1 -1
  14. package/dist/adapters/DatabaseAdapter.js +50 -9
  15. package/dist/adapters/DatabaseAdapter.js.map +1 -1
  16. package/dist/adapters/mysql/MySQLAdapter.d.ts +6 -0
  17. package/dist/adapters/mysql/MySQLAdapter.d.ts.map +1 -1
  18. package/dist/adapters/mysql/MySQLAdapter.js +8 -0
  19. package/dist/adapters/mysql/MySQLAdapter.js.map +1 -1
  20. package/dist/adapters/mysql/SchemaManager.js +16 -15
  21. package/dist/adapters/mysql/SchemaManager.js.map +1 -1
  22. package/dist/adapters/mysql/prompts/index.js +10 -20
  23. package/dist/adapters/mysql/prompts/index.js.map +1 -1
  24. package/dist/adapters/mysql/prompts/proxysqlSetup.js +1 -1
  25. package/dist/adapters/mysql/resources/docstore.d.ts.map +1 -1
  26. package/dist/adapters/mysql/resources/docstore.js +10 -7
  27. package/dist/adapters/mysql/resources/docstore.js.map +1 -1
  28. package/dist/adapters/mysql/resources/events.js +11 -8
  29. package/dist/adapters/mysql/resources/events.js.map +1 -1
  30. package/dist/adapters/mysql/resources/indexes.d.ts.map +1 -1
  31. package/dist/adapters/mysql/resources/indexes.js +12 -15
  32. package/dist/adapters/mysql/resources/indexes.js.map +1 -1
  33. package/dist/adapters/mysql/resources/innodb.d.ts.map +1 -1
  34. package/dist/adapters/mysql/resources/innodb.js +20 -17
  35. package/dist/adapters/mysql/resources/innodb.js.map +1 -1
  36. package/dist/adapters/mysql/resources/locks.d.ts.map +1 -1
  37. package/dist/adapters/mysql/resources/locks.js +9 -6
  38. package/dist/adapters/mysql/resources/locks.js.map +1 -1
  39. package/dist/adapters/mysql/resources/performance.d.ts.map +1 -1
  40. package/dist/adapters/mysql/resources/performance.js +15 -15
  41. package/dist/adapters/mysql/resources/performance.js.map +1 -1
  42. package/dist/adapters/mysql/resources/spatial.d.ts.map +1 -1
  43. package/dist/adapters/mysql/resources/spatial.js +9 -6
  44. package/dist/adapters/mysql/resources/spatial.js.map +1 -1
  45. package/dist/adapters/mysql/resources/sysschema.d.ts.map +1 -1
  46. package/dist/adapters/mysql/resources/sysschema.js +12 -9
  47. package/dist/adapters/mysql/resources/sysschema.js.map +1 -1
  48. package/dist/adapters/mysql/tools/admin/backup.d.ts.map +1 -1
  49. package/dist/adapters/mysql/tools/admin/backup.js +170 -121
  50. package/dist/adapters/mysql/tools/admin/backup.js.map +1 -1
  51. package/dist/adapters/mysql/tools/admin/maintenance.d.ts.map +1 -1
  52. package/dist/adapters/mysql/tools/admin/maintenance.js +106 -57
  53. package/dist/adapters/mysql/tools/admin/maintenance.js.map +1 -1
  54. package/dist/adapters/mysql/tools/admin/monitoring.d.ts.map +1 -1
  55. package/dist/adapters/mysql/tools/admin/monitoring.js +183 -101
  56. package/dist/adapters/mysql/tools/admin/monitoring.js.map +1 -1
  57. package/dist/adapters/mysql/tools/cluster/group-replication.d.ts.map +1 -1
  58. package/dist/adapters/mysql/tools/cluster/group-replication.js +164 -120
  59. package/dist/adapters/mysql/tools/cluster/group-replication.js.map +1 -1
  60. package/dist/adapters/mysql/tools/cluster/innodb-cluster.d.ts.map +1 -1
  61. package/dist/adapters/mysql/tools/cluster/innodb-cluster.js +212 -145
  62. package/dist/adapters/mysql/tools/cluster/innodb-cluster.js.map +1 -1
  63. package/dist/adapters/mysql/tools/codemode/index.d.ts.map +1 -1
  64. package/dist/adapters/mysql/tools/codemode/index.js +6 -4
  65. package/dist/adapters/mysql/tools/codemode/index.js.map +1 -1
  66. package/dist/adapters/mysql/tools/core.d.ts.map +1 -1
  67. package/dist/adapters/mysql/tools/core.js +152 -29
  68. package/dist/adapters/mysql/tools/core.js.map +1 -1
  69. package/dist/adapters/mysql/tools/docstore.d.ts.map +1 -1
  70. package/dist/adapters/mysql/tools/docstore.js +340 -163
  71. package/dist/adapters/mysql/tools/docstore.js.map +1 -1
  72. package/dist/adapters/mysql/tools/events.d.ts.map +1 -1
  73. package/dist/adapters/mysql/tools/events.js +284 -198
  74. package/dist/adapters/mysql/tools/events.js.map +1 -1
  75. package/dist/adapters/mysql/tools/json/core.d.ts.map +1 -1
  76. package/dist/adapters/mysql/tools/json/core.js +11 -39
  77. package/dist/adapters/mysql/tools/json/core.js.map +1 -1
  78. package/dist/adapters/mysql/tools/json/enhanced.d.ts.map +1 -1
  79. package/dist/adapters/mysql/tools/json/enhanced.js +15 -33
  80. package/dist/adapters/mysql/tools/json/enhanced.js.map +1 -1
  81. package/dist/adapters/mysql/tools/json/helpers.d.ts.map +1 -1
  82. package/dist/adapters/mysql/tools/json/helpers.js +13 -24
  83. package/dist/adapters/mysql/tools/json/helpers.js.map +1 -1
  84. package/dist/adapters/mysql/tools/partitioning.js +3 -0
  85. package/dist/adapters/mysql/tools/partitioning.js.map +1 -1
  86. package/dist/adapters/mysql/tools/performance/analysis.d.ts.map +1 -1
  87. package/dist/adapters/mysql/tools/performance/analysis.js +89 -60
  88. package/dist/adapters/mysql/tools/performance/analysis.js.map +1 -1
  89. package/dist/adapters/mysql/tools/performance/optimization.d.ts.map +1 -1
  90. package/dist/adapters/mysql/tools/performance/optimization.js +151 -127
  91. package/dist/adapters/mysql/tools/performance/optimization.js.map +1 -1
  92. package/dist/adapters/mysql/tools/proxysql.d.ts +1 -1
  93. package/dist/adapters/mysql/tools/proxysql.d.ts.map +1 -1
  94. package/dist/adapters/mysql/tools/proxysql.js +289 -176
  95. package/dist/adapters/mysql/tools/proxysql.js.map +1 -1
  96. package/dist/adapters/mysql/tools/replication.js +75 -49
  97. package/dist/adapters/mysql/tools/replication.js.map +1 -1
  98. package/dist/adapters/mysql/tools/roles.d.ts.map +1 -1
  99. package/dist/adapters/mysql/tools/roles.js +224 -182
  100. package/dist/adapters/mysql/tools/roles.js.map +1 -1
  101. package/dist/adapters/mysql/tools/router.d.ts.map +1 -1
  102. package/dist/adapters/mysql/tools/router.js +168 -67
  103. package/dist/adapters/mysql/tools/router.js.map +1 -1
  104. package/dist/adapters/mysql/tools/schema/constraints.d.ts.map +1 -1
  105. package/dist/adapters/mysql/tools/schema/constraints.js +21 -3
  106. package/dist/adapters/mysql/tools/schema/constraints.js.map +1 -1
  107. package/dist/adapters/mysql/tools/schema/management.d.ts.map +1 -1
  108. package/dist/adapters/mysql/tools/schema/management.js +61 -14
  109. package/dist/adapters/mysql/tools/schema/management.js.map +1 -1
  110. package/dist/adapters/mysql/tools/schema/routines.d.ts.map +1 -1
  111. package/dist/adapters/mysql/tools/schema/routines.js +27 -4
  112. package/dist/adapters/mysql/tools/schema/routines.js.map +1 -1
  113. package/dist/adapters/mysql/tools/schema/scheduled_events.d.ts.map +1 -1
  114. package/dist/adapters/mysql/tools/schema/scheduled_events.js +24 -3
  115. package/dist/adapters/mysql/tools/schema/scheduled_events.js.map +1 -1
  116. package/dist/adapters/mysql/tools/schema/triggers.d.ts.map +1 -1
  117. package/dist/adapters/mysql/tools/schema/triggers.js +23 -2
  118. package/dist/adapters/mysql/tools/schema/triggers.js.map +1 -1
  119. package/dist/adapters/mysql/tools/schema/views.d.ts.map +1 -1
  120. package/dist/adapters/mysql/tools/schema/views.js +47 -7
  121. package/dist/adapters/mysql/tools/schema/views.js.map +1 -1
  122. package/dist/adapters/mysql/tools/security/audit.d.ts.map +1 -1
  123. package/dist/adapters/mysql/tools/security/audit.js +102 -34
  124. package/dist/adapters/mysql/tools/security/audit.js.map +1 -1
  125. package/dist/adapters/mysql/tools/security/data-protection.d.ts.map +1 -1
  126. package/dist/adapters/mysql/tools/security/data-protection.js +264 -205
  127. package/dist/adapters/mysql/tools/security/data-protection.js.map +1 -1
  128. package/dist/adapters/mysql/tools/security/encryption.d.ts.map +1 -1
  129. package/dist/adapters/mysql/tools/security/encryption.js +137 -104
  130. package/dist/adapters/mysql/tools/security/encryption.js.map +1 -1
  131. package/dist/adapters/mysql/tools/shell/backup.d.ts.map +1 -1
  132. package/dist/adapters/mysql/tools/shell/backup.js +71 -59
  133. package/dist/adapters/mysql/tools/shell/backup.js.map +1 -1
  134. package/dist/adapters/mysql/tools/shell/restore.d.ts.map +1 -1
  135. package/dist/adapters/mysql/tools/shell/restore.js +61 -47
  136. package/dist/adapters/mysql/tools/shell/restore.js.map +1 -1
  137. package/dist/adapters/mysql/tools/spatial/geometry.d.ts.map +1 -1
  138. package/dist/adapters/mysql/tools/spatial/geometry.js +19 -5
  139. package/dist/adapters/mysql/tools/spatial/geometry.js.map +1 -1
  140. package/dist/adapters/mysql/tools/spatial/operations.d.ts.map +1 -1
  141. package/dist/adapters/mysql/tools/spatial/operations.js +42 -17
  142. package/dist/adapters/mysql/tools/spatial/operations.js.map +1 -1
  143. package/dist/adapters/mysql/tools/spatial/queries.d.ts.map +1 -1
  144. package/dist/adapters/mysql/tools/spatial/queries.js +109 -57
  145. package/dist/adapters/mysql/tools/spatial/queries.js.map +1 -1
  146. package/dist/adapters/mysql/tools/spatial/setup.d.ts.map +1 -1
  147. package/dist/adapters/mysql/tools/spatial/setup.js +103 -50
  148. package/dist/adapters/mysql/tools/spatial/setup.js.map +1 -1
  149. package/dist/adapters/mysql/tools/stats/comparative.d.ts.map +1 -1
  150. package/dist/adapters/mysql/tools/stats/comparative.js +128 -79
  151. package/dist/adapters/mysql/tools/stats/comparative.js.map +1 -1
  152. package/dist/adapters/mysql/tools/stats/descriptive.d.ts.map +1 -1
  153. package/dist/adapters/mysql/tools/stats/descriptive.js +174 -102
  154. package/dist/adapters/mysql/tools/stats/descriptive.js.map +1 -1
  155. package/dist/adapters/mysql/tools/sysschema/activity.d.ts.map +1 -1
  156. package/dist/adapters/mysql/tools/sysschema/activity.js +50 -25
  157. package/dist/adapters/mysql/tools/sysschema/activity.js.map +1 -1
  158. package/dist/adapters/mysql/tools/sysschema/performance.d.ts.map +1 -1
  159. package/dist/adapters/mysql/tools/sysschema/performance.js +121 -66
  160. package/dist/adapters/mysql/tools/sysschema/performance.js.map +1 -1
  161. package/dist/adapters/mysql/tools/sysschema/resources.d.ts.map +1 -1
  162. package/dist/adapters/mysql/tools/sysschema/resources.js +101 -64
  163. package/dist/adapters/mysql/tools/sysschema/resources.js.map +1 -1
  164. package/dist/adapters/mysql/tools/text/fulltext.d.ts.map +1 -1
  165. package/dist/adapters/mysql/tools/text/fulltext.js +18 -32
  166. package/dist/adapters/mysql/tools/text/fulltext.js.map +1 -1
  167. package/dist/adapters/mysql/tools/transactions.d.ts.map +1 -1
  168. package/dist/adapters/mysql/tools/transactions.js +48 -23
  169. package/dist/adapters/mysql/tools/transactions.js.map +1 -1
  170. package/dist/adapters/mysql/types/proxysql-types.d.ts +15 -0
  171. package/dist/adapters/mysql/types/proxysql-types.d.ts.map +1 -1
  172. package/dist/adapters/mysql/types/proxysql-types.js +33 -1
  173. package/dist/adapters/mysql/types/proxysql-types.js.map +1 -1
  174. package/dist/adapters/mysql/types/router-types.d.ts +1 -1
  175. package/dist/adapters/mysql/types/router-types.js +1 -1
  176. package/dist/adapters/mysql/types/router-types.js.map +1 -1
  177. package/dist/adapters/mysql/types/shell-types.js +2 -2
  178. package/dist/adapters/mysql/types/shell-types.js.map +1 -1
  179. package/dist/adapters/mysql/types.d.ts +485 -21
  180. package/dist/adapters/mysql/types.d.ts.map +1 -1
  181. package/dist/adapters/mysql/types.js +546 -19
  182. package/dist/adapters/mysql/types.js.map +1 -1
  183. package/dist/auth/scopes.js +1 -1
  184. package/dist/auth/scopes.js.map +1 -1
  185. package/dist/codemode/api.d.ts +3 -2
  186. package/dist/codemode/api.d.ts.map +1 -1
  187. package/dist/codemode/api.js +80 -5
  188. package/dist/codemode/api.js.map +1 -1
  189. package/dist/codemode/sandbox-factory.js +1 -1
  190. package/dist/codemode/sandbox-factory.js.map +1 -1
  191. package/dist/codemode/types.d.ts +26 -0
  192. package/dist/codemode/types.d.ts.map +1 -1
  193. package/dist/codemode/types.js +2 -0
  194. package/dist/codemode/types.js.map +1 -1
  195. package/dist/codemode/worker-sandbox.d.ts +4 -2
  196. package/dist/codemode/worker-sandbox.d.ts.map +1 -1
  197. package/dist/codemode/worker-sandbox.js +66 -7
  198. package/dist/codemode/worker-sandbox.js.map +1 -1
  199. package/dist/codemode/worker-script.d.ts +3 -0
  200. package/dist/codemode/worker-script.d.ts.map +1 -1
  201. package/dist/codemode/worker-script.js +128 -75
  202. package/dist/codemode/worker-script.js.map +1 -1
  203. package/dist/constants/ServerInstructions.d.ts +1 -1
  204. package/dist/constants/ServerInstructions.d.ts.map +1 -1
  205. package/dist/constants/ServerInstructions.js +37 -31
  206. package/dist/constants/ServerInstructions.js.map +1 -1
  207. package/dist/filtering/ToolConstants.d.ts +1 -1
  208. package/dist/filtering/ToolConstants.d.ts.map +1 -1
  209. package/dist/filtering/ToolConstants.js +1 -2
  210. package/dist/filtering/ToolConstants.js.map +1 -1
  211. package/dist/pool/ConnectionPool.d.ts.map +1 -1
  212. package/dist/pool/ConnectionPool.js.map +1 -1
  213. package/dist/transports/http.d.ts.map +1 -1
  214. package/dist/transports/http.js +6 -0
  215. package/dist/transports/http.js.map +1 -1
  216. package/dist/utils/validators.d.ts +1 -1
  217. package/dist/utils/validators.d.ts.map +1 -1
  218. package/dist/utils/validators.js.map +1 -1
  219. package/package.json +4 -4
  220. package/releases/v2.3.0-release-notes.md +20 -20
  221. package/releases/v2.3.1-release-notes.md +34 -0
  222. package/releases/v3.0.0-release-notes.md +81 -0
  223. package/src/__tests__/mocks/adapter.ts +3 -0
  224. package/src/__tests__/perf.test.ts +6 -6
  225. package/src/adapters/DatabaseAdapter.ts +58 -9
  226. package/src/adapters/__tests__/DatabaseAdapter.test.ts +89 -8
  227. package/src/adapters/mysql/MySQLAdapter.ts +17 -2
  228. package/src/adapters/mysql/SchemaManager.ts +21 -21
  229. package/src/adapters/mysql/__tests__/MySQLAdapter.test.ts +1 -1
  230. package/src/adapters/mysql/prompts/index.ts +12 -22
  231. package/src/adapters/mysql/prompts/proxysqlSetup.ts +1 -1
  232. package/src/adapters/mysql/resources/docstore.ts +13 -10
  233. package/src/adapters/mysql/resources/events.ts +12 -12
  234. package/src/adapters/mysql/resources/indexes.ts +17 -19
  235. package/src/adapters/mysql/resources/innodb.ts +23 -22
  236. package/src/adapters/mysql/resources/locks.ts +9 -7
  237. package/src/adapters/mysql/resources/performance.ts +23 -18
  238. package/src/adapters/mysql/resources/spatial.ts +9 -7
  239. package/src/adapters/mysql/resources/sysschema.ts +12 -11
  240. package/src/adapters/mysql/tools/__tests__/core.test.ts +126 -55
  241. package/src/adapters/mysql/tools/__tests__/docstore.test.ts +459 -88
  242. package/src/adapters/mysql/tools/__tests__/events.test.ts +281 -103
  243. package/src/adapters/mysql/tools/__tests__/proxysql.test.ts +128 -28
  244. package/src/adapters/mysql/tools/__tests__/replication.test.ts +48 -2
  245. package/src/adapters/mysql/tools/__tests__/roles.test.ts +15 -18
  246. package/src/adapters/mysql/tools/__tests__/router.test.ts +32 -5
  247. package/src/adapters/mysql/tools/__tests__/security.test.ts +126 -2
  248. package/src/adapters/mysql/tools/__tests__/security_injection.test.ts +84 -76
  249. package/src/adapters/mysql/tools/__tests__/security_integration.test.ts +47 -50
  250. package/src/adapters/mysql/tools/__tests__/spatial.test.ts +11 -10
  251. package/src/adapters/mysql/tools/__tests__/spatial_handler.test.ts +54 -38
  252. package/src/adapters/mysql/tools/__tests__/stats.test.ts +285 -152
  253. package/src/adapters/mysql/tools/__tests__/transactions.test.ts +13 -13
  254. package/src/adapters/mysql/tools/admin/__tests__/backup.test.ts +171 -25
  255. package/src/adapters/mysql/tools/admin/__tests__/maintenance.test.ts +240 -4
  256. package/src/adapters/mysql/tools/admin/__tests__/monitoring-summary.test.ts +274 -0
  257. package/src/adapters/mysql/tools/admin/__tests__/monitoring.test.ts +94 -5
  258. package/src/adapters/mysql/tools/admin/backup.ts +193 -143
  259. package/src/adapters/mysql/tools/admin/maintenance.ts +118 -69
  260. package/src/adapters/mysql/tools/admin/monitoring.ts +201 -125
  261. package/src/adapters/mysql/tools/cluster/__tests__/group-replication.test.ts +69 -0
  262. package/src/adapters/mysql/tools/cluster/__tests__/innodb-cluster.test.ts +141 -0
  263. package/src/adapters/mysql/tools/cluster/group-replication.ts +172 -132
  264. package/src/adapters/mysql/tools/cluster/innodb-cluster.ts +231 -157
  265. package/src/adapters/mysql/tools/codemode/__tests__/codemode-tool.test.ts +227 -0
  266. package/src/adapters/mysql/tools/codemode/index.ts +5 -3
  267. package/src/adapters/mysql/tools/core.ts +152 -38
  268. package/src/adapters/mysql/tools/docstore.ts +422 -205
  269. package/src/adapters/mysql/tools/events.ts +334 -233
  270. package/src/adapters/mysql/tools/json/__tests__/core.test.ts +20 -0
  271. package/src/adapters/mysql/tools/json/__tests__/enhanced.test.ts +82 -50
  272. package/src/adapters/mysql/tools/json/__tests__/helpers.test.ts +42 -3
  273. package/src/adapters/mysql/tools/json/core.ts +21 -42
  274. package/src/adapters/mysql/tools/json/enhanced.ts +22 -37
  275. package/src/adapters/mysql/tools/json/helpers.ts +21 -25
  276. package/src/adapters/mysql/tools/partitioning.ts +3 -0
  277. package/src/adapters/mysql/tools/performance/__tests__/analysis.test.ts +98 -5
  278. package/src/adapters/mysql/tools/performance/__tests__/optimization-coverage.test.ts +515 -0
  279. package/src/adapters/mysql/tools/performance/__tests__/optimization.test.ts +187 -0
  280. package/src/adapters/mysql/tools/performance/analysis.ts +95 -69
  281. package/src/adapters/mysql/tools/performance/optimization.ts +182 -153
  282. package/src/adapters/mysql/tools/proxysql.ts +314 -209
  283. package/src/adapters/mysql/tools/replication.ts +84 -57
  284. package/src/adapters/mysql/tools/roles.ts +274 -226
  285. package/src/adapters/mysql/tools/router.ts +181 -85
  286. package/src/adapters/mysql/tools/schema/__tests__/constraints.test.ts +13 -0
  287. package/src/adapters/mysql/tools/schema/__tests__/management.test.ts +60 -25
  288. package/src/adapters/mysql/tools/schema/__tests__/scheduled_events.test.ts +11 -0
  289. package/src/adapters/mysql/tools/schema/__tests__/triggers.test.ts +25 -4
  290. package/src/adapters/mysql/tools/schema/__tests__/views.test.ts +46 -14
  291. package/src/adapters/mysql/tools/schema/constraints.ts +22 -3
  292. package/src/adapters/mysql/tools/schema/management.ts +60 -15
  293. package/src/adapters/mysql/tools/schema/routines.ts +26 -4
  294. package/src/adapters/mysql/tools/schema/scheduled_events.ts +25 -3
  295. package/src/adapters/mysql/tools/schema/triggers.ts +27 -2
  296. package/src/adapters/mysql/tools/schema/views.ts +46 -8
  297. package/src/adapters/mysql/tools/security/__tests__/audit.test.ts +90 -4
  298. package/src/adapters/mysql/tools/security/audit.ts +113 -39
  299. package/src/adapters/mysql/tools/security/data-protection.ts +293 -233
  300. package/src/adapters/mysql/tools/security/encryption.ts +172 -139
  301. package/src/adapters/mysql/tools/shell/__tests__/backup.test.ts +29 -0
  302. package/src/adapters/mysql/tools/shell/backup.ts +90 -73
  303. package/src/adapters/mysql/tools/shell/restore.ts +62 -48
  304. package/src/adapters/mysql/tools/spatial/__tests__/operations.test.ts +22 -14
  305. package/src/adapters/mysql/tools/spatial/__tests__/queries.test.ts +65 -51
  306. package/src/adapters/mysql/tools/spatial/geometry.ts +23 -7
  307. package/src/adapters/mysql/tools/spatial/operations.ts +60 -31
  308. package/src/adapters/mysql/tools/spatial/queries.ts +142 -65
  309. package/src/adapters/mysql/tools/spatial/setup.ts +121 -55
  310. package/src/adapters/mysql/tools/stats/__tests__/comparative.test.ts +12 -10
  311. package/src/adapters/mysql/tools/stats/comparative.ts +150 -98
  312. package/src/adapters/mysql/tools/stats/descriptive.ts +204 -127
  313. package/src/adapters/mysql/tools/sysschema/__tests__/error-paths.test.ts +222 -0
  314. package/src/adapters/mysql/tools/sysschema/__tests__/performance.test.ts +45 -0
  315. package/src/adapters/mysql/tools/sysschema/__tests__/resources.test.ts +6 -3
  316. package/src/adapters/mysql/tools/sysschema/activity.ts +52 -27
  317. package/src/adapters/mysql/tools/sysschema/performance.ts +132 -68
  318. package/src/adapters/mysql/tools/sysschema/resources.ts +105 -67
  319. package/src/adapters/mysql/tools/text/__tests__/fulltext.test.ts +45 -17
  320. package/src/adapters/mysql/tools/text/fulltext.ts +27 -38
  321. package/src/adapters/mysql/tools/transactions.ts +49 -24
  322. package/src/adapters/mysql/types/proxysql-types.ts +38 -1
  323. package/src/adapters/mysql/types/router-types.ts +1 -1
  324. package/src/adapters/mysql/types/shell-types.ts +2 -2
  325. package/src/adapters/mysql/types.ts +632 -19
  326. package/src/auth/__tests__/scopes.test.ts +2 -2
  327. package/src/auth/scopes.ts +1 -1
  328. package/src/codemode/__tests__/api.test.ts +417 -0
  329. package/src/codemode/__tests__/sandbox-factory.test.ts +158 -0
  330. package/src/codemode/__tests__/sandbox.test.ts +301 -0
  331. package/src/codemode/__tests__/security.test.ts +368 -0
  332. package/src/codemode/__tests__/worker-sandbox.test.ts +179 -0
  333. package/src/codemode/__tests__/worker-script.test.ts +226 -0
  334. package/src/codemode/api.ts +89 -5
  335. package/src/codemode/sandbox-factory.ts +1 -1
  336. package/src/codemode/types.ts +34 -0
  337. package/src/codemode/worker-sandbox.ts +74 -7
  338. package/src/codemode/worker-script.ts +157 -86
  339. package/src/constants/ServerInstructions.ts +37 -31
  340. package/src/filtering/ToolConstants.ts +1 -2
  341. package/src/filtering/__tests__/ToolFilter.test.ts +9 -9
  342. package/src/pool/ConnectionPool.ts +4 -1
  343. package/src/transports/__tests__/http.test.ts +15 -3
  344. package/src/transports/http.ts +12 -0
  345. package/src/utils/validators.ts +2 -1
  346. package/vitest.config.ts +3 -1
  347. package/CODE_MODE.md +0 -245
@@ -0,0 +1,301 @@
1
+ /**
2
+ * mysql-mcp - Code Mode Sandbox Unit Tests
3
+ *
4
+ * Tests for CodeModeSandbox (vm-based) and SandboxPool.
5
+ * Uses real Node.js vm module for sandbox execution.
6
+ */
7
+
8
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
9
+ import { CodeModeSandbox, SandboxPool } from "../sandbox.js";
10
+
11
+ // Suppress logger
12
+ vi.mock("../../utils/logger.js", () => ({
13
+ logger: { info: vi.fn(), warning: vi.fn(), error: vi.fn() },
14
+ }));
15
+
16
+ describe("CodeModeSandbox", () => {
17
+ let sandbox: CodeModeSandbox;
18
+
19
+ beforeEach(() => {
20
+ sandbox = CodeModeSandbox.create({
21
+ memoryLimitMb: 64,
22
+ timeoutMs: 5000,
23
+ cpuLimitMs: 5000,
24
+ });
25
+ });
26
+
27
+ afterEach(() => {
28
+ sandbox.dispose();
29
+ });
30
+
31
+ // ===========================================================================
32
+ // Creation
33
+ // ===========================================================================
34
+ describe("create", () => {
35
+ it("should create a sandbox with default options", () => {
36
+ const s = CodeModeSandbox.create();
37
+ expect(s).toBeDefined();
38
+ expect(s.isHealthy()).toBe(true);
39
+ s.dispose();
40
+ });
41
+
42
+ it("should create a sandbox with custom options", () => {
43
+ expect(sandbox).toBeDefined();
44
+ expect(sandbox.isHealthy()).toBe(true);
45
+ });
46
+ });
47
+
48
+ // ===========================================================================
49
+ // Execution
50
+ // ===========================================================================
51
+ describe("execute", () => {
52
+ it("should execute simple code and return result", async () => {
53
+ const result = await sandbox.execute("return 42", {});
54
+ expect(result.success).toBe(true);
55
+ expect(result.result).toBe(42);
56
+ expect(result.metrics.wallTimeMs).toBeGreaterThanOrEqual(0);
57
+ });
58
+
59
+ it("should provide access to api bindings", async () => {
60
+ const mockApi = {
61
+ core: {
62
+ readQuery: vi
63
+ .fn()
64
+ .mockResolvedValue({ rows: [{ id: 1 }], rowsAffected: 0 }),
65
+ },
66
+ };
67
+ const result = await sandbox.execute(
68
+ "return await mysql.core.readQuery('SELECT 1')",
69
+ mockApi,
70
+ );
71
+ expect(result.success).toBe(true);
72
+ expect(mockApi.core.readQuery).toHaveBeenCalledWith("SELECT 1");
73
+ });
74
+
75
+ it("should capture console output", async () => {
76
+ await sandbox.execute(
77
+ 'console.log("hello"); console.warn("warning")',
78
+ {},
79
+ );
80
+ const output = sandbox.getConsoleOutput();
81
+ expect(output.some((line: string) => line.includes("hello"))).toBe(true);
82
+ expect(output.some((line: string) => line.includes("warning"))).toBe(
83
+ true,
84
+ );
85
+ });
86
+
87
+ it("should handle code errors gracefully", async () => {
88
+ const result = await sandbox.execute("throw new Error('test error')", {});
89
+ expect(result.success).toBe(false);
90
+ expect(result.error).toContain("test error");
91
+ });
92
+
93
+ it("should handle syntax errors", async () => {
94
+ const result = await sandbox.execute("const x = {{{", {});
95
+ expect(result.success).toBe(false);
96
+ expect(result.error).toBeTruthy();
97
+ });
98
+
99
+ it("should handle async code", async () => {
100
+ const result = await sandbox.execute(
101
+ "const val = await Promise.resolve(99); return val",
102
+ {},
103
+ );
104
+ expect(result.success).toBe(true);
105
+ expect(result.result).toBe(99);
106
+ });
107
+
108
+ it("should fail on disposed sandbox", async () => {
109
+ sandbox.dispose();
110
+ const result = await sandbox.execute("return 1", {});
111
+ expect(result.success).toBe(false);
112
+ expect(result.error).toContain("disposed");
113
+ });
114
+
115
+ it("should include execution metrics", async () => {
116
+ const result = await sandbox.execute("return 1", {});
117
+ expect(result.metrics).toBeDefined();
118
+ expect(result.metrics.wallTimeMs).toBeGreaterThanOrEqual(0);
119
+ expect(result.metrics.memoryUsedMb).toBeGreaterThanOrEqual(0);
120
+ });
121
+
122
+ it("should handle console.error and console.info", async () => {
123
+ await sandbox.execute('console.error("err"); console.info("inf")', {});
124
+ const output = sandbox.getConsoleOutput();
125
+ expect(output.some((line: string) => line.includes("err"))).toBe(true);
126
+ expect(output.some((line: string) => line.includes("inf"))).toBe(true);
127
+ });
128
+ });
129
+
130
+ // ===========================================================================
131
+ // calculateMetrics
132
+ // ===========================================================================
133
+ describe("calculateMetrics", () => {
134
+ it("should calculate correct metrics", () => {
135
+ const metrics = sandbox.calculateMetrics(0, 100, 1000, 2000);
136
+ expect(metrics.wallTimeMs).toBe(100);
137
+ expect(metrics.memoryUsedMb).toBeGreaterThanOrEqual(0);
138
+ });
139
+ });
140
+
141
+ // ===========================================================================
142
+ // Console output management
143
+ // ===========================================================================
144
+ describe("getConsoleOutput / clearConsoleOutput", () => {
145
+ it("should return empty array initially", () => {
146
+ expect(sandbox.getConsoleOutput()).toHaveLength(0);
147
+ });
148
+
149
+ it("should clear console output", async () => {
150
+ await sandbox.execute('console.log("test")', {});
151
+ expect(sandbox.getConsoleOutput().length).toBeGreaterThan(0);
152
+ sandbox.clearConsoleOutput();
153
+ expect(sandbox.getConsoleOutput()).toHaveLength(0);
154
+ });
155
+ });
156
+
157
+ // ===========================================================================
158
+ // Health and disposal
159
+ // ===========================================================================
160
+ describe("isHealthy / dispose", () => {
161
+ it("should be healthy after creation", () => {
162
+ expect(sandbox.isHealthy()).toBe(true);
163
+ });
164
+
165
+ it("should be unhealthy after dispose", () => {
166
+ sandbox.dispose();
167
+ expect(sandbox.isHealthy()).toBe(false);
168
+ });
169
+
170
+ it("should handle multiple dispose calls", () => {
171
+ sandbox.dispose();
172
+ sandbox.dispose(); // Should not throw
173
+ expect(sandbox.isHealthy()).toBe(false);
174
+ });
175
+ });
176
+ });
177
+
178
+ // =============================================================================
179
+ // SandboxPool
180
+ // =============================================================================
181
+ describe("SandboxPool", () => {
182
+ let pool: SandboxPool;
183
+
184
+ beforeEach(() => {
185
+ pool = new SandboxPool(
186
+ { minInstances: 0, maxInstances: 3, idleTimeoutMs: 1000 },
187
+ { memoryLimitMb: 64, timeoutMs: 5000, cpuLimitMs: 5000 },
188
+ );
189
+ });
190
+
191
+ afterEach(() => {
192
+ pool.dispose();
193
+ });
194
+
195
+ describe("initialize", () => {
196
+ it("should initialize without error", () => {
197
+ pool.initialize();
198
+ const stats = pool.getStats();
199
+ expect(stats.max).toBe(3);
200
+ });
201
+
202
+ it("should pre-warm minimum instances", () => {
203
+ const warmPool = new SandboxPool(
204
+ { minInstances: 2, maxInstances: 5, idleTimeoutMs: 1000 },
205
+ { memoryLimitMb: 64, timeoutMs: 5000, cpuLimitMs: 5000 },
206
+ );
207
+ warmPool.initialize();
208
+ expect(warmPool.getStats().available).toBe(2);
209
+ warmPool.dispose();
210
+ });
211
+ });
212
+
213
+ describe("acquire / release", () => {
214
+ it("should acquire and release sandboxes", () => {
215
+ pool.initialize();
216
+ const s = pool.acquire();
217
+ expect(s).toBeDefined();
218
+ expect(s.isHealthy()).toBe(true);
219
+ pool.release(s);
220
+ });
221
+
222
+ it("should track in-use count", () => {
223
+ pool.initialize();
224
+ const s = pool.acquire();
225
+ expect(pool.getStats().inUse).toBe(1);
226
+ pool.release(s);
227
+ expect(pool.getStats().inUse).toBe(0);
228
+ });
229
+
230
+ it("should throw when pool is exhausted", () => {
231
+ pool.initialize();
232
+ const acquired: CodeModeSandbox[] = [];
233
+ for (let i = 0; i < 3; i++) {
234
+ acquired.push(pool.acquire());
235
+ }
236
+ expect(() => pool.acquire()).toThrow();
237
+ for (const s of acquired) pool.release(s);
238
+ });
239
+
240
+ it("should dispose unhealthy sandbox on release", () => {
241
+ pool.initialize();
242
+ const s = pool.acquire();
243
+ s.dispose(); // Make it unhealthy
244
+ pool.release(s);
245
+ expect(pool.getStats().available).toBe(0);
246
+ });
247
+ });
248
+
249
+ describe("execute", () => {
250
+ it("should execute code using pooled sandbox", async () => {
251
+ pool.initialize();
252
+ const result = await pool.execute("return 42", {});
253
+ expect(result.success).toBe(true);
254
+ expect(result.result).toBe(42);
255
+ });
256
+
257
+ it("should handle execution errors", async () => {
258
+ pool.initialize();
259
+ const result = await pool.execute("throw new Error('pool error')", {});
260
+ expect(result.success).toBe(false);
261
+ expect(result.error).toContain("pool error");
262
+ });
263
+ });
264
+
265
+ describe("cleanup", () => {
266
+ it("should clean up idle sandboxes", () => {
267
+ pool.initialize();
268
+ // Acquire and release to create idle sandbox
269
+ const s = pool.acquire();
270
+ pool.release(s);
271
+ expect(pool.getStats().available).toBeGreaterThan(0);
272
+
273
+ // Advance time past idle timeout
274
+ vi.useFakeTimers();
275
+ vi.advanceTimersByTime(2000);
276
+ pool.cleanup();
277
+ vi.useRealTimers();
278
+ });
279
+ });
280
+
281
+ describe("getStats", () => {
282
+ it("should return correct stats", () => {
283
+ pool.initialize();
284
+ const stats = pool.getStats();
285
+ expect(stats).toHaveProperty("available");
286
+ expect(stats).toHaveProperty("inUse");
287
+ expect(stats).toHaveProperty("max");
288
+ expect(stats.max).toBe(3);
289
+ });
290
+ });
291
+
292
+ describe("dispose", () => {
293
+ it("should dispose all sandboxes", () => {
294
+ pool.initialize();
295
+ const s = pool.acquire();
296
+ pool.release(s);
297
+ pool.dispose();
298
+ expect(pool.getStats().available).toBe(0);
299
+ });
300
+ });
301
+ });
@@ -0,0 +1,368 @@
1
+ /**
2
+ * mysql-mcp - Code Mode Security Unit Tests
3
+ *
4
+ * Tests for CodeModeSecurityManager: validation, rate limiting,
5
+ * result sanitization, audit logging, and cleanup.
6
+ */
7
+
8
+ import { describe, it, expect, vi, beforeEach } from "vitest";
9
+ import { CodeModeSecurityManager } from "../security.js";
10
+ import type { SandboxResult } from "../types.js";
11
+
12
+ // Suppress logger output
13
+ vi.mock("../../utils/logger.js", () => ({
14
+ logger: {
15
+ info: vi.fn(),
16
+ warning: vi.fn(),
17
+ error: vi.fn(),
18
+ },
19
+ }));
20
+
21
+ describe("CodeModeSecurityManager", () => {
22
+ let manager: CodeModeSecurityManager;
23
+
24
+ beforeEach(() => {
25
+ vi.clearAllMocks();
26
+ manager = new CodeModeSecurityManager();
27
+ });
28
+
29
+ // ===========================================================================
30
+ // validateCode
31
+ // ===========================================================================
32
+ describe("validateCode", () => {
33
+ it("should accept valid code", () => {
34
+ const result = manager.validateCode(
35
+ 'const x = await mysql.core.readQuery("SELECT 1")',
36
+ );
37
+ expect(result.valid).toBe(true);
38
+ expect(result.errors).toHaveLength(0);
39
+ });
40
+
41
+ it("should reject empty string", () => {
42
+ const result = manager.validateCode("");
43
+ expect(result.valid).toBe(false);
44
+ expect(result.errors[0]).toContain("non-empty string");
45
+ });
46
+
47
+ it("should reject non-string input", () => {
48
+ const result = manager.validateCode(null as unknown as string);
49
+ expect(result.valid).toBe(false);
50
+ expect(result.errors[0]).toContain("non-empty string");
51
+ });
52
+
53
+ it("should reject code exceeding max length", () => {
54
+ const longCode = "x".repeat(100_000);
55
+ const result = manager.validateCode(longCode);
56
+ expect(result.valid).toBe(false);
57
+ expect(result.errors[0]).toContain("maximum length");
58
+ });
59
+
60
+ it("should detect require() pattern", () => {
61
+ const result = manager.validateCode('const fs = require("fs")');
62
+ expect(result.valid).toBe(false);
63
+ expect(result.errors.length).toBeGreaterThan(0);
64
+ });
65
+
66
+ it("should detect dynamic import()", () => {
67
+ const result = manager.validateCode('const m = import("child_process")');
68
+ expect(result.valid).toBe(false);
69
+ });
70
+
71
+ it("should detect process access", () => {
72
+ const result = manager.validateCode("process.exit(1)");
73
+ expect(result.valid).toBe(false);
74
+ });
75
+
76
+ it("should detect eval()", () => {
77
+ const result = manager.validateCode('eval("malicious")');
78
+ expect(result.valid).toBe(false);
79
+ });
80
+
81
+ it("should detect Function constructor", () => {
82
+ const result = manager.validateCode('new Function("return 1")');
83
+ expect(result.valid).toBe(false);
84
+ });
85
+
86
+ it("should detect __proto__ access", () => {
87
+ const result = manager.validateCode("obj.__proto__.polluted = true");
88
+ expect(result.valid).toBe(false);
89
+ });
90
+
91
+ it("should detect constructor.constructor chaining", () => {
92
+ const result = manager.validateCode(
93
+ '"".constructor.constructor("return this")()',
94
+ );
95
+ expect(result.valid).toBe(false);
96
+ });
97
+
98
+ it("should detect globalThis access", () => {
99
+ const result = manager.validateCode("globalThis.foo = 1");
100
+ expect(result.valid).toBe(false);
101
+ });
102
+
103
+ it("should detect child_process", () => {
104
+ const result = manager.validateCode("child_process.exec('ls')");
105
+ expect(result.valid).toBe(false);
106
+ });
107
+
108
+ it("should detect fs access", () => {
109
+ const result = manager.validateCode("fs.readFileSync('/etc/passwd')");
110
+ expect(result.valid).toBe(false);
111
+ });
112
+
113
+ it("should detect net access", () => {
114
+ const result = manager.validateCode("net.connect(80)");
115
+ expect(result.valid).toBe(false);
116
+ });
117
+
118
+ it("should detect http access", () => {
119
+ const result = manager.validateCode("http.get('http://evil.test')");
120
+ expect(result.valid).toBe(false);
121
+ });
122
+
123
+ it("should detect https access", () => {
124
+ const result = manager.validateCode("https.get('https://evil.test')");
125
+ expect(result.valid).toBe(false);
126
+ });
127
+
128
+ it("should detect bracket-notation constructor access", () => {
129
+ const result = manager.validateCode('x["constructor"]');
130
+ expect(result.valid).toBe(false);
131
+ });
132
+
133
+ it("should detect Reflect.construct", () => {
134
+ const result = manager.validateCode("Reflect.construct(Array, [])");
135
+ expect(result.valid).toBe(false);
136
+ });
137
+
138
+ it("should accumulate multiple blocked pattern errors", () => {
139
+ const result = manager.validateCode(
140
+ 'require("fs"); process.exit(1); eval("x")',
141
+ );
142
+ expect(result.valid).toBe(false);
143
+ expect(result.errors.length).toBeGreaterThanOrEqual(3);
144
+ });
145
+
146
+ it("should respect custom maxCodeLength", () => {
147
+ const small = new CodeModeSecurityManager({ maxCodeLength: 10 });
148
+ const result = small.validateCode("a".repeat(11));
149
+ expect(result.valid).toBe(false);
150
+ });
151
+ });
152
+
153
+ // ===========================================================================
154
+ // checkRateLimit
155
+ // ===========================================================================
156
+ describe("checkRateLimit", () => {
157
+ it("should allow first request", () => {
158
+ expect(manager.checkRateLimit("client-1")).toBe(true);
159
+ });
160
+
161
+ it("should allow requests within limit", () => {
162
+ for (let i = 0; i < 59; i++) {
163
+ expect(manager.checkRateLimit("client-2")).toBe(true);
164
+ }
165
+ });
166
+
167
+ it("should block when limit exceeded", () => {
168
+ const smallLimit = new CodeModeSecurityManager({
169
+ maxExecutionsPerMinute: 3,
170
+ });
171
+ expect(smallLimit.checkRateLimit("c")).toBe(true);
172
+ expect(smallLimit.checkRateLimit("c")).toBe(true);
173
+ expect(smallLimit.checkRateLimit("c")).toBe(true);
174
+ expect(smallLimit.checkRateLimit("c")).toBe(false);
175
+ });
176
+
177
+ it("should reset after window expires", () => {
178
+ const smallLimit = new CodeModeSecurityManager({
179
+ maxExecutionsPerMinute: 1,
180
+ });
181
+ expect(smallLimit.checkRateLimit("c")).toBe(true);
182
+ expect(smallLimit.checkRateLimit("c")).toBe(false);
183
+
184
+ // Simulate time passage by manipulating the internal map
185
+ vi.useFakeTimers();
186
+ vi.advanceTimersByTime(61_000);
187
+ expect(smallLimit.checkRateLimit("c")).toBe(true);
188
+ vi.useRealTimers();
189
+ });
190
+
191
+ it("should track clients independently", () => {
192
+ const smallLimit = new CodeModeSecurityManager({
193
+ maxExecutionsPerMinute: 1,
194
+ });
195
+ expect(smallLimit.checkRateLimit("a")).toBe(true);
196
+ expect(smallLimit.checkRateLimit("a")).toBe(false);
197
+ expect(smallLimit.checkRateLimit("b")).toBe(true);
198
+ });
199
+ });
200
+
201
+ // ===========================================================================
202
+ // getRateLimitRemaining
203
+ // ===========================================================================
204
+ describe("getRateLimitRemaining", () => {
205
+ it("should return full limit for unknown client", () => {
206
+ expect(manager.getRateLimitRemaining("unknown")).toBe(60);
207
+ });
208
+
209
+ it("should decrease after requests", () => {
210
+ manager.checkRateLimit("client");
211
+ expect(manager.getRateLimitRemaining("client")).toBe(59);
212
+ });
213
+
214
+ it("should return 0 when exhausted", () => {
215
+ const small = new CodeModeSecurityManager({
216
+ maxExecutionsPerMinute: 2,
217
+ });
218
+ small.checkRateLimit("c");
219
+ small.checkRateLimit("c");
220
+ expect(small.getRateLimitRemaining("c")).toBe(0);
221
+ });
222
+
223
+ it("should return full limit after window expires", () => {
224
+ manager.checkRateLimit("client");
225
+ vi.useFakeTimers();
226
+ vi.advanceTimersByTime(61_000);
227
+ expect(manager.getRateLimitRemaining("client")).toBe(60);
228
+ vi.useRealTimers();
229
+ });
230
+ });
231
+
232
+ // ===========================================================================
233
+ // sanitizeResult
234
+ // ===========================================================================
235
+ describe("sanitizeResult", () => {
236
+ it("should pass through small results", () => {
237
+ const input = { foo: "bar" };
238
+ expect(manager.sanitizeResult(input)).toEqual(input);
239
+ });
240
+
241
+ it("should truncate oversized results", () => {
242
+ const small = new CodeModeSecurityManager({ maxResultSize: 10 });
243
+ const result = small.sanitizeResult({ data: "a".repeat(100) }) as {
244
+ _truncated: boolean;
245
+ };
246
+ expect(result._truncated).toBe(true);
247
+ });
248
+
249
+ it("should handle non-serializable results", () => {
250
+ const circular: Record<string, unknown> = {};
251
+ circular.self = circular;
252
+ const result = manager.sanitizeResult(circular) as {
253
+ _error: string;
254
+ };
255
+ expect(result._error).toContain("could not be serialized");
256
+ });
257
+ });
258
+
259
+ // ===========================================================================
260
+ // auditLog
261
+ // ===========================================================================
262
+ describe("auditLog", () => {
263
+ const makeResult = (success: boolean): SandboxResult => ({
264
+ success,
265
+ result: success ? "ok" : undefined,
266
+ error: success ? undefined : "test error",
267
+ stack: success ? undefined : "Error: test error\n at line 1",
268
+ metrics: { wallTimeMs: 10, cpuTimeMs: 5, memoryUsedMb: 1 },
269
+ });
270
+
271
+ it("should log successful executions", async () => {
272
+ const { logger } = await import("../../utils/logger.js");
273
+ const record = manager.createExecutionRecord(
274
+ "const x = 1",
275
+ makeResult(true),
276
+ false,
277
+ "client-1",
278
+ );
279
+ manager.auditLog(record);
280
+ expect(logger.info).toHaveBeenCalled();
281
+ });
282
+
283
+ it("should log failed executions with warning", async () => {
284
+ const { logger } = await import("../../utils/logger.js");
285
+ const record = manager.createExecutionRecord(
286
+ "bad code",
287
+ makeResult(false),
288
+ true,
289
+ );
290
+ manager.auditLog(record);
291
+ expect(logger.warning).toHaveBeenCalled();
292
+ });
293
+ });
294
+
295
+ // ===========================================================================
296
+ // createExecutionRecord
297
+ // ===========================================================================
298
+ describe("createExecutionRecord", () => {
299
+ it("should create record with correct fields", () => {
300
+ const result: SandboxResult = {
301
+ success: true,
302
+ result: 42,
303
+ metrics: { wallTimeMs: 10, cpuTimeMs: 5, memoryUsedMb: 1 },
304
+ };
305
+ const record = manager.createExecutionRecord(
306
+ "const x = 1",
307
+ result,
308
+ false,
309
+ "client-1",
310
+ );
311
+
312
+ expect(record.id).toBeTruthy();
313
+ expect(record.clientId).toBe("client-1");
314
+ expect(record.timestamp).toBeInstanceOf(Date);
315
+ expect(record.codePreview).toBe("const x = 1");
316
+ expect(record.result).toEqual(result);
317
+ expect(record.readonly).toBe(false);
318
+ });
319
+
320
+ it("should truncate long code in preview", () => {
321
+ const longCode = "x".repeat(300);
322
+ const result: SandboxResult = {
323
+ success: true,
324
+ metrics: { wallTimeMs: 0, cpuTimeMs: 0, memoryUsedMb: 0 },
325
+ };
326
+ const record = manager.createExecutionRecord(longCode, result, true);
327
+
328
+ expect(record.codePreview.length).toBeLessThan(longCode.length);
329
+ expect(record.codePreview).toContain("...");
330
+ });
331
+
332
+ it("should handle undefined clientId", () => {
333
+ const result: SandboxResult = {
334
+ success: true,
335
+ metrics: { wallTimeMs: 0, cpuTimeMs: 0, memoryUsedMb: 0 },
336
+ };
337
+ const record = manager.createExecutionRecord("x", result, false);
338
+ expect(record.clientId).toBeUndefined();
339
+ });
340
+ });
341
+
342
+ // ===========================================================================
343
+ // cleanupRateLimits
344
+ // ===========================================================================
345
+ describe("cleanupRateLimits", () => {
346
+ it("should remove expired entries", () => {
347
+ const small = new CodeModeSecurityManager({
348
+ maxExecutionsPerMinute: 1,
349
+ });
350
+ small.checkRateLimit("client-old");
351
+ small.checkRateLimit("client-old"); // blocked
352
+
353
+ vi.useFakeTimers();
354
+ vi.advanceTimersByTime(61_000);
355
+ small.cleanupRateLimits();
356
+
357
+ // After cleanup, should have full limit again
358
+ expect(small.getRateLimitRemaining("client-old")).toBe(1);
359
+ vi.useRealTimers();
360
+ });
361
+
362
+ it("should keep active entries", () => {
363
+ manager.checkRateLimit("active");
364
+ manager.cleanupRateLimits();
365
+ expect(manager.getRateLimitRemaining("active")).toBe(59);
366
+ });
367
+ });
368
+ });