@joystick.js/db-canary 0.0.0-canary.2209

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 (354) hide show
  1. package/.build/getFilesToBuild.js +26 -0
  2. package/.build/getPlatformSafeFilePath.js +6 -0
  3. package/.build/getPlatformSafePath.js +6 -0
  4. package/.build/index.js +88 -0
  5. package/.build/isWindows.js +3 -0
  6. package/API_KEY +1 -0
  7. package/README.md +1821 -0
  8. package/data/data.mdb +0 -0
  9. package/data/lock.mdb +0 -0
  10. package/dist/client/database.js +1 -0
  11. package/dist/client/index.js +1 -0
  12. package/dist/server/cluster/index.js +1 -0
  13. package/dist/server/cluster/master.js +20 -0
  14. package/dist/server/cluster/worker.js +1 -0
  15. package/dist/server/index.js +1 -0
  16. package/dist/server/lib/api_key_manager.js +9 -0
  17. package/dist/server/lib/auth_manager.js +1 -0
  18. package/dist/server/lib/auto_index_manager.js +1 -0
  19. package/dist/server/lib/backup_manager.js +1 -0
  20. package/dist/server/lib/connection_manager.js +1 -0
  21. package/dist/server/lib/disk_utils.js +2 -0
  22. package/dist/server/lib/http_server.js +405 -0
  23. package/dist/server/lib/index_manager.js +1 -0
  24. package/dist/server/lib/load_settings.js +1 -0
  25. package/dist/server/lib/logger.js +1 -0
  26. package/dist/server/lib/op_types.js +1 -0
  27. package/dist/server/lib/operation_dispatcher.js +1 -0
  28. package/dist/server/lib/operations/admin.js +1 -0
  29. package/dist/server/lib/operations/bulk_write.js +1 -0
  30. package/dist/server/lib/operations/create_index.js +1 -0
  31. package/dist/server/lib/operations/delete_one.js +1 -0
  32. package/dist/server/lib/operations/drop_index.js +1 -0
  33. package/dist/server/lib/operations/find.js +1 -0
  34. package/dist/server/lib/operations/find_one.js +1 -0
  35. package/dist/server/lib/operations/get_indexes.js +1 -0
  36. package/dist/server/lib/operations/insert_one.js +1 -0
  37. package/dist/server/lib/operations/update_one.js +1 -0
  38. package/dist/server/lib/performance_monitor.js +1 -0
  39. package/dist/server/lib/query_engine.js +1 -0
  40. package/dist/server/lib/recovery_manager.js +1 -0
  41. package/dist/server/lib/replication_manager.js +1 -0
  42. package/dist/server/lib/safe_json_parse.js +1 -0
  43. package/dist/server/lib/send_response.js +1 -0
  44. package/dist/server/lib/tcp_protocol.js +1 -0
  45. package/dist/server/lib/write_forwarder.js +1 -0
  46. package/dist/server/lib/write_queue.js +1 -0
  47. package/increment_version.js +3 -0
  48. package/logs/.013e15b54597d05db4b4b53ecc37b10c92a72927-audit.json +20 -0
  49. package/logs/.02de550a67ea0f5961faa2dfd458a4d06f59ebd1-audit.json +20 -0
  50. package/logs/.03494ba24eb3c72214b4068a77d54b8993bee651-audit.json +20 -0
  51. package/logs/.06309ec60b339be1259a7993dd09c732f8907fbc-audit.json +20 -0
  52. package/logs/.0663a04dcfa17285661e5e1b8cfa51f41523b210-audit.json +20 -0
  53. package/logs/.0f06e6c4c9b824622729e13927587479e5060391-audit.json +20 -0
  54. package/logs/.16ccf58682ecb22b3e3ec63f0da1b7fe9be56528-audit.json +20 -0
  55. package/logs/.1fa1a5d02f496474b1ab473524c65c984146a9ad-audit.json +20 -0
  56. package/logs/.2223c0ae3bea6f0d62c62b1d319cc8634856abb7-audit.json +20 -0
  57. package/logs/.23dc79ffda3e083665e6f5993f59397adcbf4a46-audit.json +20 -0
  58. package/logs/.28104f49b03906b189eefd1cd462cb46c3c0af22-audit.json +20 -0
  59. package/logs/.29cdbf13808abe6a0ce70ee2f2efdd680ce3fd8e-audit.json +20 -0
  60. package/logs/.2a9889afd071f77f41f5170d08703a0afca866b7-audit.json +20 -0
  61. package/logs/.2acec3d1940a2bbed487528b703ee5948959a599-audit.json +20 -0
  62. package/logs/.2fb60ff326338c02bfedbcd0e936444e4a216750-audit.json +20 -0
  63. package/logs/.318fc7a19530d76a345f030f7cad00dda15300e7-audit.json +20 -0
  64. package/logs/.3cf27043e19085f908cedc7701e6d013463208ee-audit.json +25 -0
  65. package/logs/.3d90d785415817fc443402843b7c95f8371adc9b-audit.json +20 -0
  66. package/logs/.4074bca620375f72966fc52dfd439577727671e5-audit.json +20 -0
  67. package/logs/.40eecf018417ea80a70ea8ec7a3cc9406bc6334b-audit.json +20 -0
  68. package/logs/.50e974f1ef7c365fca6a1251b2e2c2252914cb5e-audit.json +20 -0
  69. package/logs/.52cb7d9e4223cf26ba36006ac26b949a97c7923c-audit.json +20 -0
  70. package/logs/.54befcdb84c15aad980705a31bcc9f555c3577ab-audit.json +20 -0
  71. package/logs/.57dfb70e22eddb84db2e3c0ceeefac5c0b9baffa-audit.json +20 -0
  72. package/logs/.5f0b24705a1eaad4eca4968f2d86f91b3f9be683-audit.json +20 -0
  73. package/logs/.61ba98fdda7db58576b382fee07904e5db1169d6-audit.json +20 -0
  74. package/logs/.6235017727ef6b199d569a99d6aa8c8e80a1b475-audit.json +20 -0
  75. package/logs/.63db16193699219489d218a1ddea5dde3750cae4-audit.json +20 -0
  76. package/logs/.64fb67dfe14149c9eef728d79bf30a54da809c60-audit.json +20 -0
  77. package/logs/.669137453368987c1f311b5345342527afb54e50-audit.json +20 -0
  78. package/logs/.7a71f8c89ea28ae266d356aeff6306e876a30fbb-audit.json +20 -0
  79. package/logs/.7afbaa90fe9dc3a7d682676f9bb79f9a1b1fd9a6-audit.json +20 -0
  80. package/logs/.7ca29e322cd05327035de850099e7610864f2347-audit.json +20 -0
  81. package/logs/.83335ab3347e449dae03455a110aaf7f120d4802-audit.json +20 -0
  82. package/logs/.8c2487b5fd445d2c8e5c483c80b9fa99bbf1ca58-audit.json +20 -0
  83. package/logs/.8c8b9dc386922c9f3b4c13251af7052aac1d24c0-audit.json +20 -0
  84. package/logs/.8d6155d94640c4863301ae0fee5e4e7372a21446-audit.json +20 -0
  85. package/logs/.944a3119a243deea7c8270d5d9e582bb1d0eaa10-audit.json +20 -0
  86. package/logs/.9816a845c30fb2909f3b26a23eeb3538ebcad5db-audit.json +20 -0
  87. package/logs/.9dc08784e38b865488177c26d4af5934555e0323-audit.json +20 -0
  88. package/logs/.9dd27d2e0e454ac0a37600206d1cac5493b0d7ee-audit.json +20 -0
  89. package/logs/.a3d486feeac7654c59b547de96600e8849a06d4f-audit.json +20 -0
  90. package/logs/.a5b811f4def22250f86cc18870d7c4573625df22-audit.json +20 -0
  91. package/logs/.a61648eb5f830e0b6f508ac35e4f8f629d2ad4c7-audit.json +20 -0
  92. package/logs/.a89016d507045771b4b5a65656944a9c0f1e528b-audit.json +20 -0
  93. package/logs/.a99bee160a1c590be959af46bacc02724803f691-audit.json +20 -0
  94. package/logs/.ada7906d6243fd7da802f03d86c4ae5dd9df6236-audit.json +20 -0
  95. package/logs/.b518339ee942143b6af983af167f5bbb6983b4de-audit.json +20 -0
  96. package/logs/.b51b124b166d53c9519017856ea610d61d65fabe-audit.json +20 -0
  97. package/logs/.b7a6aee19f58e55633d5e4a3709041c47dfff975-audit.json +20 -0
  98. package/logs/.bd7a8a6ba9c55d557a4867ab53f02e3ec2e1553d-audit.json +20 -0
  99. package/logs/.c1435dafe453b169d6392b25065f3cf4ab6fbb21-audit.json +20 -0
  100. package/logs/.c17e1ce043109f77dc2f0e2aa290a9d1ed842c03-audit.json +20 -0
  101. package/logs/.ca62637ce9540e5a38a2fbedb2115febb6ad308a-audit.json +15 -0
  102. package/logs/.ccee67b9c176967f8977071409a41f5cb5cd6ad4-audit.json +20 -0
  103. package/logs/.db24043417ea79a6f14cd947476399e53930b48d-audit.json +20 -0
  104. package/logs/.e0f12acccb57829f5f33712bb2e2607ecd808147-audit.json +20 -0
  105. package/logs/.e9b6cc33d0bbd2e644c4e2bf44d177f850016557-audit.json +20 -0
  106. package/logs/.f15291d434808e3bdca7963ccd2e73893be027e6-audit.json +20 -0
  107. package/logs/.f4bdf9e21ef84f8a3fae3ffb32bbc39275991351-audit.json +15 -0
  108. package/logs/.fbac3aefac1e81b4230df5aa50667cb90d51024f-audit.json +20 -0
  109. package/logs/.fcfd495c0a9169db243f4a4f21878ee02b76413c-audit.json +20 -0
  110. package/logs/admin-2025-09-12.log +580 -0
  111. package/logs/admin-2025-09-15.log +283 -0
  112. package/logs/admin-error-2025-09-12.log +22 -0
  113. package/logs/admin-error-2025-09-15.log +10 -0
  114. package/logs/api_key_manager-2025-09-12.log +658 -0
  115. package/logs/api_key_manager-2025-09-15.log +295 -0
  116. package/logs/api_key_manager-error-2025-09-12.log +0 -0
  117. package/logs/api_key_manager-error-2025-09-15.log +0 -0
  118. package/logs/auth_manager-2025-09-12.log +4432 -0
  119. package/logs/auth_manager-2025-09-15.log +2000 -0
  120. package/logs/auth_manager-error-2025-09-12.log +11 -0
  121. package/logs/auth_manager-error-2025-09-15.log +5 -0
  122. package/logs/auto_index_manager-2025-09-12.log +84 -0
  123. package/logs/auto_index_manager-2025-09-15.log +45 -0
  124. package/logs/auto_index_manager-error-2025-09-12.log +6 -0
  125. package/logs/auto_index_manager-error-2025-09-15.log +0 -0
  126. package/logs/backup_manager-2025-09-12.log +198 -0
  127. package/logs/backup_manager-2025-09-15.log +90 -0
  128. package/logs/backup_manager-error-2025-09-12.log +198 -0
  129. package/logs/backup_manager-error-2025-09-15.log +90 -0
  130. package/logs/bulk_write-2025-09-12.log +66 -0
  131. package/logs/bulk_write-2025-09-15.log +38 -0
  132. package/logs/bulk_write-error-2025-09-12.log +0 -0
  133. package/logs/bulk_write-error-2025-09-15.log +0 -0
  134. package/logs/connection_manager-2025-09-12.log +2412 -0
  135. package/logs/connection_manager-2025-09-15.log +1132 -0
  136. package/logs/connection_manager-error-2025-09-12.log +0 -0
  137. package/logs/connection_manager-error-2025-09-15.log +0 -0
  138. package/logs/create_index-2025-09-12.log +302 -0
  139. package/logs/create_index-2025-09-15.log +158 -0
  140. package/logs/create_index-error-2025-09-12.log +30 -0
  141. package/logs/create_index-error-2025-09-15.log +13 -0
  142. package/logs/delete_one-2025-09-12.log +73 -0
  143. package/logs/delete_one-2025-09-15.log +43 -0
  144. package/logs/delete_one-error-2025-09-12.log +0 -0
  145. package/logs/delete_one-error-2025-09-15.log +0 -0
  146. package/logs/disk_utils-2025-09-12.log +4954 -0
  147. package/logs/disk_utils-2025-09-15.log +2446 -0
  148. package/logs/disk_utils-error-2025-09-12.log +0 -0
  149. package/logs/disk_utils-error-2025-09-15.log +0 -0
  150. package/logs/drop_index-2025-09-12.log +41 -0
  151. package/logs/drop_index-2025-09-15.log +23 -0
  152. package/logs/drop_index-error-2025-09-12.log +11 -0
  153. package/logs/drop_index-error-2025-09-15.log +5 -0
  154. package/logs/find-2025-09-12.log +1050 -0
  155. package/logs/find-2025-09-15.log +592 -0
  156. package/logs/find-error-2025-09-12.log +1 -0
  157. package/logs/find-error-2025-09-15.log +0 -0
  158. package/logs/find_one-2025-09-12.log +425 -0
  159. package/logs/find_one-2025-09-15.log +264 -0
  160. package/logs/find_one-error-2025-09-12.log +5 -0
  161. package/logs/find_one-error-2025-09-15.log +0 -0
  162. package/logs/get_indexes-2025-09-12.log +84 -0
  163. package/logs/get_indexes-2025-09-15.log +56 -0
  164. package/logs/get_indexes-error-2025-09-12.log +6 -0
  165. package/logs/get_indexes-error-2025-09-15.log +0 -0
  166. package/logs/http_server-2025-09-12.log +2772 -0
  167. package/logs/http_server-2025-09-15.log +1276 -0
  168. package/logs/http_server-error-2025-09-12.log +212 -0
  169. package/logs/http_server-error-2025-09-15.log +44 -0
  170. package/logs/index_manager-2025-09-12.log +5031 -0
  171. package/logs/index_manager-2025-09-15.log +2909 -0
  172. package/logs/index_manager-error-2025-09-12.log +80 -0
  173. package/logs/index_manager-error-2025-09-15.log +38 -0
  174. package/logs/insert_one-2025-09-12.log +2181 -0
  175. package/logs/insert_one-2025-09-15.log +1293 -0
  176. package/logs/insert_one-error-2025-09-12.log +0 -0
  177. package/logs/insert_one-error-2025-09-15.log +0 -0
  178. package/logs/master-2025-09-12.log +1882 -0
  179. package/logs/master-2025-09-15.log +910 -0
  180. package/logs/master-error-2025-09-12.log +80 -0
  181. package/logs/master-error-2025-09-15.log +0 -0
  182. package/logs/operation_dispatcher-2025-09-12.log +751 -0
  183. package/logs/operation_dispatcher-2025-09-15.log +359 -0
  184. package/logs/operation_dispatcher-error-2025-09-12.log +33 -0
  185. package/logs/operation_dispatcher-error-2025-09-15.log +11 -0
  186. package/logs/performance_monitor-2025-09-12.log +14889 -0
  187. package/logs/performance_monitor-2025-09-15.log +6803 -0
  188. package/logs/performance_monitor-error-2025-09-12.log +0 -0
  189. package/logs/performance_monitor-error-2025-09-15.log +0 -0
  190. package/logs/query_engine-2025-09-12.log +5310 -0
  191. package/logs/query_engine-2025-09-15.log +2639 -0
  192. package/logs/query_engine-error-2025-09-12.log +0 -0
  193. package/logs/query_engine-error-2025-09-15.log +0 -0
  194. package/logs/recovery_manager-2025-09-12.log +462 -0
  195. package/logs/recovery_manager-2025-09-15.log +210 -0
  196. package/logs/recovery_manager-error-2025-09-12.log +22 -0
  197. package/logs/recovery_manager-error-2025-09-15.log +10 -0
  198. package/logs/replication-2025-09-12.log +1923 -0
  199. package/logs/replication-2025-09-15.log +917 -0
  200. package/logs/replication-error-2025-09-12.log +33 -0
  201. package/logs/replication-error-2025-09-15.log +15 -0
  202. package/logs/server-2025-09-12.log +2601 -0
  203. package/logs/server-2025-09-15.log +1191 -0
  204. package/logs/server-error-2025-09-12.log +0 -0
  205. package/logs/server-error-2025-09-15.log +0 -0
  206. package/logs/tcp_protocol-2025-09-12.log +22 -0
  207. package/logs/tcp_protocol-2025-09-15.log +10 -0
  208. package/logs/tcp_protocol-error-2025-09-12.log +22 -0
  209. package/logs/tcp_protocol-error-2025-09-15.log +10 -0
  210. package/logs/test-2025-09-12.log +0 -0
  211. package/logs/test-2025-09-15.log +0 -0
  212. package/logs/test-error-2025-09-12.log +0 -0
  213. package/logs/test-error-2025-09-15.log +0 -0
  214. package/logs/update_one-2025-09-12.log +173 -0
  215. package/logs/update_one-2025-09-15.log +118 -0
  216. package/logs/update_one-error-2025-09-12.log +0 -0
  217. package/logs/update_one-error-2025-09-15.log +0 -0
  218. package/logs/worker-2025-09-12.log +1457 -0
  219. package/logs/worker-2025-09-15.log +695 -0
  220. package/logs/worker-error-2025-09-12.log +0 -0
  221. package/logs/worker-error-2025-09-15.log +0 -0
  222. package/logs/write_forwarder-2025-09-12.log +1956 -0
  223. package/logs/write_forwarder-2025-09-15.log +932 -0
  224. package/logs/write_forwarder-error-2025-09-12.log +66 -0
  225. package/logs/write_forwarder-error-2025-09-15.log +30 -0
  226. package/logs/write_queue-2025-09-12.log +612 -0
  227. package/logs/write_queue-2025-09-15.log +301 -0
  228. package/logs/write_queue-error-2025-09-12.log +184 -0
  229. package/logs/write_queue-error-2025-09-15.log +83 -0
  230. package/package.json +48 -0
  231. package/prompts/01-core-infrastructure.md +56 -0
  232. package/prompts/02-secondary-indexing.md +65 -0
  233. package/prompts/03-write-serialization.md +63 -0
  234. package/prompts/04-enhanced-authentication.md +75 -0
  235. package/prompts/05-comprehensive-admin-operations.md +75 -0
  236. package/prompts/06-backup-and-restore-system.md +106 -0
  237. package/prompts/07-production-safety-features.md +107 -0
  238. package/prompts/08-tcp-client-library.md +121 -0
  239. package/prompts/09-api-method-chaining.md +134 -0
  240. package/prompts/10-automatic-index-creation.md +223 -0
  241. package/prompts/11-operation-naming-consistency.md +268 -0
  242. package/prompts/12-tcp-replication-system.md +333 -0
  243. package/prompts/13-master-read-write-operations.md +57 -0
  244. package/prompts/14-index-upsert-operations.md +68 -0
  245. package/prompts/15-client-api-return-types.md +81 -0
  246. package/prompts/16-server-setup-ui.md +97 -0
  247. package/prompts/17-emergency-password-change.md +108 -0
  248. package/prompts/18-joystick-framework-integration.md +116 -0
  249. package/prompts/19-api-key-authentication-system.md +137 -0
  250. package/prompts/20-configurable-server-port.md +105 -0
  251. package/prompts/21-multi-database-support.md +161 -0
  252. package/prompts/FULL_TEXT_SEARCH.md +293 -0
  253. package/prompts/PROMPTS.md +158 -0
  254. package/prompts/README.md +221 -0
  255. package/prompts/TYPESCRIPT_GENERATION.md +179 -0
  256. package/src/client/database.js +166 -0
  257. package/src/client/index.js +752 -0
  258. package/src/server/cluster/index.js +53 -0
  259. package/src/server/cluster/master.js +774 -0
  260. package/src/server/cluster/worker.js +537 -0
  261. package/src/server/index.js +540 -0
  262. package/src/server/lib/api_key_manager.js +473 -0
  263. package/src/server/lib/auth_manager.js +375 -0
  264. package/src/server/lib/auto_index_manager.js +681 -0
  265. package/src/server/lib/backup_manager.js +650 -0
  266. package/src/server/lib/connection_manager.js +218 -0
  267. package/src/server/lib/disk_utils.js +118 -0
  268. package/src/server/lib/http_server.js +1165 -0
  269. package/src/server/lib/index_manager.js +756 -0
  270. package/src/server/lib/load_settings.js +143 -0
  271. package/src/server/lib/logger.js +135 -0
  272. package/src/server/lib/op_types.js +29 -0
  273. package/src/server/lib/operation_dispatcher.js +268 -0
  274. package/src/server/lib/operations/admin.js +808 -0
  275. package/src/server/lib/operations/bulk_write.js +367 -0
  276. package/src/server/lib/operations/create_index.js +68 -0
  277. package/src/server/lib/operations/delete_one.js +114 -0
  278. package/src/server/lib/operations/drop_index.js +58 -0
  279. package/src/server/lib/operations/find.js +340 -0
  280. package/src/server/lib/operations/find_one.js +319 -0
  281. package/src/server/lib/operations/get_indexes.js +52 -0
  282. package/src/server/lib/operations/insert_one.js +113 -0
  283. package/src/server/lib/operations/update_one.js +225 -0
  284. package/src/server/lib/performance_monitor.js +313 -0
  285. package/src/server/lib/query_engine.js +243 -0
  286. package/src/server/lib/recovery_manager.js +388 -0
  287. package/src/server/lib/replication_manager.js +727 -0
  288. package/src/server/lib/safe_json_parse.js +21 -0
  289. package/src/server/lib/send_response.js +47 -0
  290. package/src/server/lib/tcp_protocol.js +130 -0
  291. package/src/server/lib/write_forwarder.js +636 -0
  292. package/src/server/lib/write_queue.js +335 -0
  293. package/test_data/data.mdb +0 -0
  294. package/test_data/lock.mdb +0 -0
  295. package/tests/client/index.test.js +1232 -0
  296. package/tests/server/cluster/cluster.test.js +248 -0
  297. package/tests/server/cluster/master_read_write_operations.test.js +577 -0
  298. package/tests/server/index.test.js +651 -0
  299. package/tests/server/integration/authentication_integration.test.js +294 -0
  300. package/tests/server/integration/auto_indexing_integration.test.js +268 -0
  301. package/tests/server/integration/backup_integration.test.js +513 -0
  302. package/tests/server/integration/indexing_integration.test.js +126 -0
  303. package/tests/server/integration/production_safety_integration.test.js +358 -0
  304. package/tests/server/integration/replication_integration.test.js +227 -0
  305. package/tests/server/integration/write_serialization_integration.test.js +246 -0
  306. package/tests/server/lib/api_key_manager.test.js +516 -0
  307. package/tests/server/lib/auth_manager.test.js +317 -0
  308. package/tests/server/lib/auto_index_manager.test.js +275 -0
  309. package/tests/server/lib/backup_manager.test.js +238 -0
  310. package/tests/server/lib/connection_manager.test.js +221 -0
  311. package/tests/server/lib/disk_utils.test.js +63 -0
  312. package/tests/server/lib/http_server.test.js +389 -0
  313. package/tests/server/lib/index_manager.test.js +301 -0
  314. package/tests/server/lib/load_settings.test.js +107 -0
  315. package/tests/server/lib/load_settings_port_config.test.js +243 -0
  316. package/tests/server/lib/logger.test.js +282 -0
  317. package/tests/server/lib/operations/admin.test.js +638 -0
  318. package/tests/server/lib/operations/bulk_write.test.js +128 -0
  319. package/tests/server/lib/operations/create_index.test.js +138 -0
  320. package/tests/server/lib/operations/delete_one.test.js +52 -0
  321. package/tests/server/lib/operations/drop_index.test.js +72 -0
  322. package/tests/server/lib/operations/find.test.js +93 -0
  323. package/tests/server/lib/operations/find_one.test.js +91 -0
  324. package/tests/server/lib/operations/get_indexes.test.js +87 -0
  325. package/tests/server/lib/operations/insert_one.test.js +42 -0
  326. package/tests/server/lib/operations/update_one.test.js +89 -0
  327. package/tests/server/lib/performance_monitor.test.js +185 -0
  328. package/tests/server/lib/query_engine.test.js +46 -0
  329. package/tests/server/lib/recovery_manager.test.js +414 -0
  330. package/tests/server/lib/replication_manager.test.js +202 -0
  331. package/tests/server/lib/safe_json_parse.test.js +45 -0
  332. package/tests/server/lib/send_response.test.js +155 -0
  333. package/tests/server/lib/tcp_protocol.test.js +169 -0
  334. package/tests/server/lib/write_forwarder.test.js +258 -0
  335. package/tests/server/lib/write_queue.test.js +255 -0
  336. package/tsconfig.json +30 -0
  337. package/types/client/index.d.ts +447 -0
  338. package/types/server/cluster/index.d.ts +28 -0
  339. package/types/server/cluster/master.d.ts +115 -0
  340. package/types/server/cluster/worker.d.ts +1 -0
  341. package/types/server/lib/auth_manager.d.ts +13 -0
  342. package/types/server/lib/backup_manager.d.ts +43 -0
  343. package/types/server/lib/connection_manager.d.ts +15 -0
  344. package/types/server/lib/disk_utils.d.ts +3 -0
  345. package/types/server/lib/index_manager.d.ts +24 -0
  346. package/types/server/lib/load_settings.d.ts +4 -0
  347. package/types/server/lib/logger.d.ts +44 -0
  348. package/types/server/lib/op_types.d.ts +6 -0
  349. package/types/server/lib/performance_monitor.d.ts +68 -0
  350. package/types/server/lib/query_engine.d.ts +10 -0
  351. package/types/server/lib/safe_json_parse.d.ts +7 -0
  352. package/types/server/lib/send_response.d.ts +3 -0
  353. package/types/server/lib/tcp_protocol.d.ts +12 -0
  354. package/types/server/lib/write_queue.d.ts +2 -0
@@ -0,0 +1,774 @@
1
+ import cluster from 'cluster';
2
+ import os from 'os';
3
+ import { EventEmitter } from 'events';
4
+ import { writeFileSync } from 'fs';
5
+ import { load_settings, get_port_configuration } from '../lib/load_settings.js';
6
+ import {
7
+ restore_backup,
8
+ start_backup_schedule,
9
+ stop_backup_schedule
10
+ } from '../lib/backup_manager.js';
11
+ import create_logger from '../lib/logger.js';
12
+ import { initialize_database, check_and_grow_map_size, cleanup_database } from '../lib/query_engine.js';
13
+ import {
14
+ setup_authentication,
15
+ verify_password,
16
+ initialize_auth_manager
17
+ } from '../lib/auth_manager.js';
18
+ import insert_one from '../lib/operations/insert_one.js';
19
+ import update_one from '../lib/operations/update_one.js';
20
+ import delete_one from '../lib/operations/delete_one.js';
21
+ import bulk_write from '../lib/operations/bulk_write.js';
22
+ import find_one from '../lib/operations/find_one.js';
23
+ import find from '../lib/operations/find.js';
24
+ import create_index_operation from '../lib/operations/create_index.js';
25
+ import drop_index_operation from '../lib/operations/drop_index.js';
26
+ import get_indexes_operation from '../lib/operations/get_indexes.js';
27
+ import { start_http_server, stop_http_server, is_setup_required } from '../lib/http_server.js';
28
+
29
+ /**
30
+ * @typedef {Object} ClusterMasterOptions
31
+ * @property {number} [worker_count] - Number of worker processes to spawn
32
+ * @property {number} [port=1983] - Server port
33
+ * @property {string} [settings_file='settings.db.json'] - Settings file path
34
+ */
35
+
36
+ /**
37
+ * Cluster master process that manages worker processes and coordinates database writes.
38
+ * @extends EventEmitter
39
+ * @fires ClusterMaster#writes_completed
40
+ */
41
+ class ClusterMaster extends EventEmitter {
42
+ /**
43
+ * Creates a new cluster master instance.
44
+ * @param {ClusterMasterOptions} [options={}] - Master configuration options
45
+ */
46
+ constructor(options = {}) {
47
+ super();
48
+
49
+ this.workers = new Map();
50
+ this.write_queue = [];
51
+ this.processing_writes = false;
52
+ this.authenticated_sessions = new Map();
53
+ this.worker_count = options.worker_count || os.cpus().length;
54
+ this.port = options.port || 1983;
55
+ this.settings_file = options.settings_file || 'settings.db.json';
56
+ this.settings = null;
57
+ this.pending_writes = new Map();
58
+ this.write_id_counter = 0;
59
+ this.shutting_down = false;
60
+ this.master_id = `master_${Date.now()}_${Math.random()}`;
61
+
62
+ const { create_context_logger } = create_logger('master');
63
+ this.log = create_context_logger({ port: this.port, worker_count: this.worker_count, master_id: this.master_id });
64
+
65
+ this.setup_master();
66
+ }
67
+
68
+ /**
69
+ * Sets up the cluster master configuration and event handlers.
70
+ */
71
+ setup_master() {
72
+ cluster.setupPrimary({
73
+ exec: new URL('./index.js', import.meta.url).pathname,
74
+ args: [],
75
+ silent: false
76
+ });
77
+
78
+ cluster.on('exit', (worker, code, signal) => {
79
+ this.log.warn('Worker died', {
80
+ worker_pid: worker.process.pid,
81
+ exit_code: code,
82
+ signal: signal
83
+ });
84
+ this.handle_worker_death(worker);
85
+ });
86
+
87
+ cluster.on('message', (worker, message) => {
88
+ this.handle_worker_message(worker, message);
89
+ });
90
+ }
91
+
92
+ /**
93
+ * Starts the cluster master and spawns worker processes.
94
+ * @throws {Error} When startup fails
95
+ */
96
+ async start() {
97
+ const start_time = Date.now();
98
+
99
+ try {
100
+ this.settings = load_settings(this.settings_file);
101
+ this.log.info('Settings loaded successfully', { settings_file: this.settings_file });
102
+
103
+ // NOTE: Initialize database and auth manager.
104
+ initialize_database();
105
+ initialize_auth_manager();
106
+ this.log.info('Database and auth manager initialized');
107
+
108
+ // NOTE: Handle startup restore if configured.
109
+ if (this.settings?.restore_from) {
110
+ try {
111
+ this.log.info('Startup restore requested', { backup_filename: this.settings.restore_from });
112
+
113
+ const restore_result = await restore_backup(this.settings.restore_from);
114
+
115
+ this.log.info('Startup restore completed', {
116
+ backup_filename: this.settings.restore_from,
117
+ duration_ms: restore_result.duration_ms
118
+ });
119
+
120
+ // NOTE: Remove restore_from from settings after successful restore.
121
+ const updated_settings = { ...this.settings };
122
+ delete updated_settings.restore_from;
123
+
124
+ writeFileSync(this.settings_file, JSON.stringify(updated_settings, null, 2));
125
+
126
+ // NOTE: Reload settings to reflect the change.
127
+ this.settings = load_settings(this.settings_file);
128
+
129
+ this.log.info('Removed restore_from from settings after successful restore');
130
+ } catch (restore_error) {
131
+ this.log.error('Startup restore failed', {
132
+ backup_filename: this.settings.restore_from,
133
+ error: restore_error.message
134
+ });
135
+
136
+ // NOTE: Continue with existing database if restore fails.
137
+ this.log.info('Continuing with existing database after restore failure');
138
+ }
139
+ }
140
+
141
+ // NOTE: Start backup scheduling if S3 is configured.
142
+ if (this.settings?.s3) {
143
+ try {
144
+ start_backup_schedule();
145
+ this.log.info('Backup scheduling started');
146
+ } catch (backup_schedule_error) {
147
+ this.log.warn('Failed to start backup scheduling', { error: backup_schedule_error.message });
148
+ }
149
+ }
150
+
151
+ // NOTE: Start HTTP server for setup if authentication is not configured.
152
+ if (is_setup_required()) {
153
+ try {
154
+ const { http_port } = get_port_configuration();
155
+ const http_server_instance = await start_http_server(http_port);
156
+ if (http_server_instance) {
157
+ this.log.info('HTTP setup server started', { http_port });
158
+ }
159
+ } catch (http_error) {
160
+ this.log.warn('Failed to start HTTP setup server', { error: http_error.message });
161
+ }
162
+ }
163
+
164
+ for (let i = 0; i < this.worker_count; i++) {
165
+ this.spawn_worker();
166
+ }
167
+
168
+ const duration_ms = Date.now() - start_time;
169
+ this.log.info('Master process started successfully', {
170
+ workers_spawned: this.worker_count,
171
+ startup_duration_ms: duration_ms
172
+ });
173
+
174
+ } catch (error) {
175
+ this.log.error('Failed to start master process', { error: error.message });
176
+ process.exit(1);
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Spawns a new worker process.
182
+ * @returns {cluster.Worker} The spawned worker instance
183
+ */
184
+ spawn_worker() {
185
+ const spawn_start = Date.now();
186
+ this.log.info('Spawning worker');
187
+
188
+ const worker = cluster.fork({
189
+ WORKER_PORT: this.port,
190
+ WORKER_SETTINGS: JSON.stringify(this.settings)
191
+ });
192
+
193
+ this.workers.set(worker.id, {
194
+ worker,
195
+ connections: 0,
196
+ last_heartbeat: Date.now(),
197
+ status: 'starting'
198
+ });
199
+
200
+ const spawn_duration = Date.now() - spawn_start;
201
+ this.log.info('Worker spawned successfully', {
202
+ worker_id: worker.id,
203
+ worker_pid: worker.process.pid,
204
+ spawn_duration_ms: spawn_duration
205
+ });
206
+
207
+ return worker;
208
+ }
209
+
210
+ /**
211
+ * Handles worker process death and respawning.
212
+ * @param {cluster.Worker} dead_worker - The worker that died
213
+ */
214
+ handle_worker_death(dead_worker) {
215
+ this.workers.delete(dead_worker.id);
216
+
217
+ if (!this.shutting_down) {
218
+ this.log.info('Respawning worker after death', {
219
+ dead_worker_id: dead_worker.id,
220
+ respawn_delay_ms: 1000
221
+ });
222
+ setTimeout(() => {
223
+ this.spawn_worker();
224
+ }, 1000);
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Handles messages from worker processes.
230
+ * @param {cluster.Worker} worker - Worker that sent the message
231
+ * @param {Object} message - Message from worker
232
+ */
233
+ handle_worker_message(worker, message) {
234
+ switch (message.type) {
235
+ case 'worker_ready':
236
+ this.handle_worker_ready_for_config(worker, message);
237
+ break;
238
+
239
+ case 'server_ready':
240
+ this.handle_worker_server_ready(worker, message);
241
+ break;
242
+
243
+ case 'write_request':
244
+ this.handle_write_request(worker, message);
245
+ break;
246
+
247
+ case 'auth_request':
248
+ this.handle_auth_request(worker, message);
249
+ break;
250
+
251
+ case 'setup_request':
252
+ this.handle_setup_request(worker, message);
253
+ break;
254
+
255
+ case 'connection_count':
256
+ this.update_worker_connections(worker, message);
257
+ break;
258
+
259
+ case 'heartbeat':
260
+ this.handle_worker_heartbeat(worker, message);
261
+ break;
262
+
263
+ default:
264
+ this.log.warn('Unknown message type received', {
265
+ message_type: message.type,
266
+ worker_id: worker.id
267
+ });
268
+ }
269
+ }
270
+
271
+ handle_worker_ready_for_config(worker, message) {
272
+ this.log.info('Worker ready for config, sending configuration', {
273
+ worker_id: worker.id,
274
+ worker_pid: worker.process.pid,
275
+ master_id: this.master_id
276
+ });
277
+ worker.send({
278
+ type: 'config',
279
+ data: {
280
+ port: this.port,
281
+ settings: this.settings,
282
+ master_id: this.master_id
283
+ }
284
+ });
285
+ }
286
+
287
+ handle_worker_server_ready(worker, message) {
288
+ const worker_info = this.workers.get(worker.id);
289
+ if (worker_info) {
290
+ worker_info.status = 'ready';
291
+ this.log.info('Worker server ready', {
292
+ worker_id: worker.id,
293
+ worker_pid: worker.process.pid
294
+ });
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Handles write requests from workers, queuing them for serialized execution.
300
+ * @param {cluster.Worker} worker - Worker requesting the write
301
+ * @param {Object} message - Write request message
302
+ */
303
+ async handle_write_request(worker, message) {
304
+ if (this.shutting_down) {
305
+ worker.send({
306
+ type: 'write_response',
307
+ data: {
308
+ write_id: message.data.write_id,
309
+ success: false,
310
+ error: 'Server is shutting down'
311
+ }
312
+ });
313
+ return;
314
+ }
315
+
316
+ const { write_id, op_type, data, socket_id } = message.data;
317
+
318
+ try {
319
+ const write_operation = {
320
+ write_id,
321
+ worker_id: worker.id,
322
+ op_type,
323
+ data,
324
+ socket_id,
325
+ timestamp: Date.now()
326
+ };
327
+
328
+ this.write_queue.push(write_operation);
329
+ this.process_write_queue();
330
+
331
+ } catch (error) {
332
+ worker.send({
333
+ type: 'write_response',
334
+ data: {
335
+ write_id,
336
+ success: false,
337
+ error: error.message
338
+ }
339
+ });
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Processes the write queue, executing operations serially.
345
+ */
346
+ async process_write_queue() {
347
+ if (this.processing_writes || this.write_queue.length === 0) {
348
+ return;
349
+ }
350
+
351
+ this.processing_writes = true;
352
+
353
+ while (this.write_queue.length > 0) {
354
+ const write_op = this.write_queue.shift();
355
+ await this.execute_write_operation(write_op);
356
+ }
357
+
358
+ this.processing_writes = false;
359
+
360
+ if (this.shutting_down && this.write_queue.length === 0) {
361
+ this.emit('writes_completed');
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Executes a single write operation and sends response to worker.
367
+ * @param {Object} write_op - Write operation to execute
368
+ */
369
+ async execute_write_operation(write_op) {
370
+ const { write_id, worker_id, op_type, data, socket_id } = write_op;
371
+ const worker_info = this.workers.get(worker_id);
372
+
373
+ if (!worker_info) {
374
+ this.log.error('Worker not found for write operation', { worker_id });
375
+ return;
376
+ }
377
+
378
+ try {
379
+ const result = await this.perform_database_operation(op_type, data);
380
+
381
+ worker_info.worker.send({
382
+ type: 'write_response',
383
+ data: {
384
+ write_id,
385
+ success: true,
386
+ result
387
+ }
388
+ });
389
+
390
+ this.broadcast_write_notification(op_type, data, worker_id);
391
+
392
+ } catch (error) {
393
+ this.log.error('Write operation failed', {
394
+ write_id,
395
+ op_type,
396
+ worker_id,
397
+ error_message: error.message
398
+ });
399
+
400
+ worker_info.worker.send({
401
+ type: 'write_response',
402
+ data: {
403
+ write_id,
404
+ success: false,
405
+ error: error.message
406
+ }
407
+ });
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Performs database operations (both read and write operations).
413
+ * @param {string} op_type - Type of database operation
414
+ * @param {Object} data - Operation data
415
+ * @returns {Promise<Object>} Operation result
416
+ * @throws {Error} When operation fails or is unsupported
417
+ */
418
+ async perform_database_operation(op_type, data) {
419
+ const operation_start = Date.now();
420
+ this.log.info('Executing database operation', { op_type });
421
+
422
+ try {
423
+ let result;
424
+ const database_name = data.database || 'default';
425
+
426
+ switch (op_type) {
427
+ case 'insert_one':
428
+ result = await insert_one(database_name, data.collection, data.document, data.options);
429
+ break;
430
+
431
+ case 'update_one':
432
+ result = await update_one(database_name, data.collection, data.filter, data.update, data.options);
433
+ break;
434
+
435
+ case 'delete_one':
436
+ result = await delete_one(database_name, data.collection, data.filter, data.options);
437
+ break;
438
+
439
+ case 'bulk_write':
440
+ result = await bulk_write(database_name, data.collection, data.operations, data.options);
441
+ break;
442
+
443
+ case 'find_one':
444
+ result = await find_one(database_name, data.collection, data.filter, data.options);
445
+ break;
446
+
447
+ case 'find':
448
+ result = await find(database_name, data.collection, data.filter, data.options);
449
+ break;
450
+
451
+ case 'create_index':
452
+ result = await create_index_operation(database_name, data.collection, data.field, data.options);
453
+ break;
454
+
455
+ case 'drop_index':
456
+ result = await drop_index_operation(database_name, data.collection, data.field);
457
+ break;
458
+
459
+ case 'get_indexes':
460
+ result = await get_indexes_operation(database_name, data.collection);
461
+ break;
462
+
463
+ default:
464
+ throw new Error(`Unsupported database operation: ${op_type}`);
465
+ }
466
+
467
+ const duration_ms = Date.now() - operation_start;
468
+ this.log.log_operation(op_type, duration_ms, { result });
469
+
470
+ // NOTE: Trigger map size check after write operations (not read operations).
471
+ const read_operations = ['find_one', 'find', 'get_indexes'];
472
+ if (!read_operations.includes(op_type)) {
473
+ setImmediate(() => check_and_grow_map_size());
474
+ }
475
+
476
+ return result;
477
+ } catch (error) {
478
+ const duration_ms = Date.now() - operation_start;
479
+ this.log.error('Database operation failed', {
480
+ op_type,
481
+ duration_ms,
482
+ error_message: error.message
483
+ });
484
+ throw error;
485
+ }
486
+ }
487
+
488
+ /**
489
+ * Broadcasts write notifications to all workers except the originating one.
490
+ * @param {string} op_type - Type of write operation
491
+ * @param {Object} data - Operation data
492
+ * @param {number} exclude_worker_id - Worker ID to exclude from broadcast
493
+ */
494
+ broadcast_write_notification(op_type, data, exclude_worker_id) {
495
+ const notification = {
496
+ type: 'write_notification',
497
+ data: {
498
+ op_type,
499
+ data,
500
+ timestamp: Date.now()
501
+ }
502
+ };
503
+
504
+ for (const [worker_id, worker_info] of this.workers) {
505
+ if (worker_id !== exclude_worker_id && worker_info.status === 'ready') {
506
+ worker_info.worker.send(notification);
507
+ }
508
+ }
509
+ }
510
+
511
+ async handle_auth_request(worker, message) {
512
+ const { auth_id, socket_id, password } = message.data;
513
+
514
+ try {
515
+ const is_authenticated = await verify_password(password, 'cluster_client');
516
+
517
+ if (is_authenticated) {
518
+ this.authenticated_sessions.set(socket_id, {
519
+ authenticated_at: Date.now(),
520
+ worker_id: worker.id
521
+ });
522
+ }
523
+
524
+ worker.send({
525
+ type: 'auth_response',
526
+ data: {
527
+ auth_id,
528
+ success: is_authenticated,
529
+ message: is_authenticated ? 'Authentication successful' : 'Authentication failed'
530
+ }
531
+ });
532
+ } catch (error) {
533
+ worker.send({
534
+ type: 'auth_response',
535
+ data: {
536
+ auth_id,
537
+ success: false,
538
+ message: `Authentication error: ${error.message}`
539
+ }
540
+ });
541
+ }
542
+ }
543
+
544
+ handle_setup_request(worker, message) {
545
+ const { setup_id, socket_id } = message.data;
546
+
547
+ try {
548
+ const password = setup_authentication();
549
+
550
+ const instructions = `===
551
+ JoystickDB Setup
552
+
553
+ Your database has been setup. Follow the instructions below carefully to avoid issues.
554
+
555
+ Password:
556
+ ${password}
557
+
558
+ Store this password in your environment settings or another secure location. When connecting a client, make sure to provide this password via the client's options object like this:
559
+
560
+ import joystickdb from '@joystickdb/client';
561
+
562
+ const client = joystickdb.client({
563
+ host: 'localhost',
564
+ port: 1983,
565
+ password: '${password}'
566
+ });
567
+
568
+ await client.ping();
569
+ ===`;
570
+
571
+ worker.send({
572
+ type: 'setup_response',
573
+ data: {
574
+ setup_id,
575
+ success: true,
576
+ password,
577
+ instructions,
578
+ message: 'Authentication setup completed successfully'
579
+ }
580
+ });
581
+ } catch (error) {
582
+ worker.send({
583
+ type: 'setup_response',
584
+ data: {
585
+ setup_id,
586
+ success: false,
587
+ error: error.message
588
+ }
589
+ });
590
+ }
591
+ }
592
+
593
+ update_worker_connections(worker, message) {
594
+ const worker_info = this.workers.get(worker.id);
595
+ if (worker_info) {
596
+ worker_info.connections = message.data.count;
597
+ }
598
+ }
599
+
600
+ handle_worker_heartbeat(worker, message) {
601
+ const worker_info = this.workers.get(worker.id);
602
+ if (worker_info) {
603
+ worker_info.last_heartbeat = Date.now();
604
+ }
605
+ }
606
+
607
+ /**
608
+ * Gets comprehensive cluster statistics.
609
+ * @returns {Object} Cluster statistics including worker info and connection counts
610
+ */
611
+ get_cluster_stats() {
612
+ const stats = {
613
+ master_pid: process.pid,
614
+ worker_count: this.workers.size,
615
+ total_connections: 0,
616
+ write_queue_length: this.write_queue.length,
617
+ authenticated_sessions: this.authenticated_sessions.size,
618
+ workers: []
619
+ };
620
+
621
+ for (const [worker_id, worker_info] of this.workers) {
622
+ stats.total_connections += worker_info.connections;
623
+ stats.workers.push({
624
+ id: worker_id,
625
+ pid: worker_info.worker.process.pid,
626
+ connections: worker_info.connections,
627
+ status: worker_info.status,
628
+ last_heartbeat: worker_info.last_heartbeat
629
+ });
630
+ }
631
+
632
+ return stats;
633
+ }
634
+
635
+ /**
636
+ * Performs graceful shutdown of the cluster master and all workers.
637
+ */
638
+ async shutdown() {
639
+ const shutdown_start = Date.now();
640
+ this.log.info('Initiating graceful shutdown');
641
+ this.shutting_down = true;
642
+
643
+ // NOTE: Stop HTTP server.
644
+ try {
645
+ await stop_http_server();
646
+ this.log.info('HTTP server stopped');
647
+ } catch (http_stop_error) {
648
+ this.log.warn('Failed to stop HTTP server', { error: http_stop_error.message });
649
+ }
650
+
651
+ // NOTE: Stop backup scheduling.
652
+ try {
653
+ stop_backup_schedule();
654
+ this.log.info('Backup scheduling stopped');
655
+ } catch (backup_stop_error) {
656
+ this.log.warn('Failed to stop backup scheduling', { error: backup_stop_error.message });
657
+ }
658
+
659
+ // NOTE: Send shutdown signal to all workers.
660
+ for (const [worker_id, worker_info] of this.workers) {
661
+ try {
662
+ worker_info.worker.send({ type: 'shutdown' });
663
+ } catch (error) {
664
+ this.log.warn('Error sending shutdown signal to worker', { worker_id, error: error.message });
665
+ }
666
+ }
667
+
668
+ // NOTE: Wait for pending writes to complete.
669
+ if (this.write_queue.length > 0) {
670
+ this.log.info('Waiting for pending writes to complete', {
671
+ pending_writes: this.write_queue.length
672
+ });
673
+ await new Promise((resolve) => {
674
+ const timeout = setTimeout(() => {
675
+ this.log.warn('Timeout waiting for writes to complete, proceeding with shutdown');
676
+ resolve();
677
+ }, process.env.NODE_ENV === 'test' ? 1000 : 5000);
678
+
679
+ this.once('writes_completed', () => {
680
+ clearTimeout(timeout);
681
+ resolve();
682
+ });
683
+ });
684
+ }
685
+
686
+ this.log.info('All writes completed, disconnecting workers');
687
+
688
+ // NOTE: Disconnect all workers.
689
+ for (const [worker_id, worker_info] of this.workers) {
690
+ try {
691
+ worker_info.worker.disconnect();
692
+ } catch (error) {
693
+ this.log.warn('Error disconnecting worker', { worker_id, error: error.message });
694
+ }
695
+ }
696
+
697
+ // NOTE: Wait for workers to exit gracefully, but with a timeout for forced termination.
698
+ const graceful_timeout = process.env.NODE_ENV === 'test' ? 500 : 3000;
699
+
700
+ await new Promise((resolve) => {
701
+ const timeout = setTimeout(() => {
702
+ // NOTE: Force kill any remaining workers.
703
+ for (const [worker_id, worker_info] of this.workers) {
704
+ this.log.warn('Force killing worker after timeout', { worker_id });
705
+ try {
706
+ worker_info.worker.kill('SIGKILL');
707
+ } catch (error) {
708
+ this.log.warn('Error force killing worker', { worker_id, error: error.message });
709
+ }
710
+ }
711
+ this.workers.clear();
712
+ resolve();
713
+ }, graceful_timeout);
714
+
715
+ const check_workers = () => {
716
+ if (this.workers.size === 0) {
717
+ clearTimeout(timeout);
718
+ resolve();
719
+ } else {
720
+ setTimeout(check_workers, 50);
721
+ }
722
+ };
723
+ check_workers();
724
+ });
725
+
726
+ // NOTE: Clean up database connections in master process.
727
+ try {
728
+ cleanup_database();
729
+ this.log.info('Database cleanup completed');
730
+ } catch (cleanup_error) {
731
+ this.log.warn('Error during database cleanup', { error: cleanup_error.message });
732
+ }
733
+
734
+ // NOTE: Clear all internal state.
735
+ this.authenticated_sessions.clear();
736
+ this.write_queue.length = 0;
737
+ this.pending_writes.clear();
738
+
739
+ // NOTE: In test environment, aggressively clean up cluster state to prevent worker reuse.
740
+ if (process.env.NODE_ENV === 'test') {
741
+ try {
742
+ // Force kill any remaining workers in the global cluster state
743
+ for (const worker_id in cluster.workers) {
744
+ const worker = cluster.workers[worker_id];
745
+ if (worker && !worker.isDead()) {
746
+ this.log.info('Force killing remaining cluster worker', { worker_id, worker_pid: worker.process.pid });
747
+ try {
748
+ worker.kill('SIGKILL');
749
+ } catch (kill_error) {
750
+ this.log.warn('Error force killing remaining worker', { worker_id, error: kill_error.message });
751
+ }
752
+ }
753
+ }
754
+
755
+ // Clear the global cluster workers registry
756
+ for (const worker_id in cluster.workers) {
757
+ delete cluster.workers[worker_id];
758
+ }
759
+
760
+ // Remove all cluster event listeners to prevent memory leaks
761
+ cluster.removeAllListeners();
762
+
763
+ this.log.info('Aggressive cluster cleanup completed for test environment');
764
+ } catch (cleanup_error) {
765
+ this.log.warn('Error during aggressive cluster cleanup', { error: cleanup_error.message });
766
+ }
767
+ }
768
+
769
+ const shutdown_duration = Date.now() - shutdown_start;
770
+ this.log.info('Shutdown complete', { shutdown_duration_ms: shutdown_duration });
771
+ }
772
+ }
773
+
774
+ export default ClusterMaster;